aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.gitignore1
-rw-r--r--HOWTO/INSTALL-WIN32.md2
-rw-r--r--HOWTO/INSTALL.md4
-rw-r--r--OTP_VERSION2
-rw-r--r--bootstrap/bin/start.bootbin5391 -> 5393 bytes
-rw-r--r--bootstrap/bin/start_clean.bootbin5391 -> 5393 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/beam_asm.beambin11428 -> 11428 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/beam_block.beambin9612 -> 9484 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/beam_bool.beambin15720 -> 15720 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/beam_bsm.beambin12632 -> 12632 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/beam_clean.beambin8948 -> 8948 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/beam_dead.beambin13092 -> 12992 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/beam_validator.beambin30824 -> 30828 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/cerl.beambin32080 -> 32080 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/compile.beambin39028 -> 39112 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/compiler.app2
-rw-r--r--bootstrap/lib/compiler/ebin/core_lint.beambin13572 -> 13572 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/core_parse.beambin57124 -> 57160 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/core_scan.beambin6636 -> 6636 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/sys_core_fold.beambin52400 -> 52400 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/v3_core.beambin54484 -> 54484 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/v3_kernel.beambin46676 -> 46680 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/v3_life.beambin19044 -> 19044 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/application_controller.beambin31932 -> 31932 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/application_master.beambin6632 -> 6636 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/auth.beambin6516 -> 6520 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/code_server.beambin24808 -> 24840 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/disk_log.beambin35832 -> 35836 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/disk_log_1.beambin24884 -> 24884 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/disk_log_server.beambin6624 -> 6624 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/dist_ac.beambin26328 -> 26336 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/dist_util.beambin10672 -> 11220 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/erl_epmd.beambin7180 -> 7244 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/global.beambin32244 -> 32244 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/global_group.beambin17548 -> 17548 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/group.beambin14032 -> 14032 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/heart.beambin5540 -> 5544 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/hipe_unified_loader.beambin13792 -> 13796 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/inet.beambin24156 -> 24156 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/inet6_tcp_dist.beambin768 -> 872 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/inet_res.beambin14868 -> 14868 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/inet_tcp_dist.beambin7212 -> 7616 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/kernel_config.beambin2764 -> 2764 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/net_kernel.beambin22968 -> 24856 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/user.beambin11572 -> 11576 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/user_drv.beambin11408 -> 11420 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/wrap_log_reader.beambin3320 -> 3320 bytes
-rw-r--r--bootstrap/lib/kernel/include/dist_util.hrl8
-rw-r--r--bootstrap/lib/stdlib/ebin/binary.beambin2828 -> 2828 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/dets.beambin53676 -> 53688 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/dets_server.beambin6984 -> 6992 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/dets_utils.beambin28748 -> 28748 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/dets_v8.beambin27488 -> 27488 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/epp.beambin29276 -> 29280 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/erl_lint.beambin90264 -> 90276 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/erl_parse.beambin82652 -> 82660 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/erl_pp.beambin26856 -> 26856 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/file_sorter.beambin30400 -> 30400 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/gen_fsm.beambin9480 -> 9492 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/gen_server.beambin12448 -> 12452 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/log_mf_h.beambin2636 -> 2636 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/pool.beambin3836 -> 3840 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/qlc.beambin70112 -> 70124 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/qlc_pt.beambin76228 -> 76228 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/shell.beambin30232 -> 30300 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/slave.beambin4852 -> 4856 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/sofs.beambin40624 -> 40624 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/stdlib.app2
-rw-r--r--bootstrap/lib/stdlib/ebin/stdlib.appup2
-rw-r--r--bootstrap/lib/stdlib/ebin/supervisor.beambin23616 -> 23616 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/supervisor_bridge.beambin2072 -> 2072 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/zip.beambin26632 -> 26632 bytes
-rw-r--r--configure.in51
-rw-r--r--erts/aclocal.m469
-rw-r--r--erts/configure.in84
-rw-r--r--erts/doc/src/absform.xml18
-rw-r--r--erts/doc/src/alt_dist.xml10
-rw-r--r--erts/doc/src/communication.xml4
-rw-r--r--erts/doc/src/crash_dump.xml10
-rw-r--r--erts/doc/src/driver_entry.xml10
-rw-r--r--erts/doc/src/epmd.xml11
-rw-r--r--erts/doc/src/erl.xml92
-rw-r--r--erts/doc/src/erl_dist_protocol.xml10
-rw-r--r--erts/doc/src/erl_driver.xml20
-rw-r--r--erts/doc/src/erl_ext_dist.xml10
-rw-r--r--erts/doc/src/erl_nif.xml18
-rw-r--r--erts/doc/src/erl_prim_loader.xml14
-rw-r--r--erts/doc/src/erl_tracer.xml2
-rw-r--r--erts/doc/src/erlang.xml192
-rw-r--r--erts/doc/src/erlc.xml6
-rw-r--r--erts/doc/src/erlsrv.xml2
-rw-r--r--erts/doc/src/erts_alloc.xml20
-rw-r--r--erts/doc/src/escript.xml6
-rw-r--r--erts/doc/src/inet_cfg.xml30
-rw-r--r--erts/doc/src/init.xml12
-rw-r--r--erts/doc/src/introduction.xml2
-rw-r--r--erts/doc/src/match_spec.xml14
-rw-r--r--erts/doc/src/notes.xml372
-rw-r--r--erts/doc/src/run_erl.xml13
-rw-r--r--erts/doc/src/start_erl.xml2
-rw-r--r--erts/doc/src/time_correction.xml10
-rw-r--r--erts/emulator/Makefile.in31
-rw-r--r--erts/emulator/beam/atom.names13
-rw-r--r--erts/emulator/beam/beam_bif_load.c1002
-rw-r--r--erts/emulator/beam/beam_emu.c114
-rw-r--r--erts/emulator/beam/beam_load.c184
-rw-r--r--erts/emulator/beam/beam_load.h10
-rw-r--r--erts/emulator/beam/bif.tab8
-rw-r--r--erts/emulator/beam/erl_alloc.types4
-rw-r--r--erts/emulator/beam/erl_bif_info.c28
-rw-r--r--erts/emulator/beam/erl_db.c4
-rw-r--r--erts/emulator/beam/erl_db_hash.c44
-rw-r--r--erts/emulator/beam/erl_db_util.h1
-rw-r--r--erts/emulator/beam/erl_fun.c53
-rw-r--r--erts/emulator/beam/erl_fun.h6
-rw-r--r--erts/emulator/beam/erl_gc.c175
-rw-r--r--erts/emulator/beam/erl_hl_timer.c5
-rw-r--r--erts/emulator/beam/erl_init.c43
-rw-r--r--erts/emulator/beam/erl_lock_check.c4
-rw-r--r--erts/emulator/beam/erl_message.c17
-rw-r--r--erts/emulator/beam/erl_message.h11
-rw-r--r--erts/emulator/beam/erl_nif.c231
-rw-r--r--erts/emulator/beam/erl_process.c357
-rw-r--r--erts/emulator/beam/erl_process.h40
-rw-r--r--erts/emulator/beam/erl_thr_progress.c9
-rw-r--r--erts/emulator/beam/erl_time_sup.c6
-rw-r--r--erts/emulator/beam/erl_trace.c36
-rw-r--r--erts/emulator/beam/global.h48
-rw-r--r--erts/emulator/beam/hash.c21
-rw-r--r--erts/emulator/beam/io.c26
-rw-r--r--erts/emulator/beam/module.c3
-rw-r--r--erts/emulator/beam/module.h1
-rw-r--r--erts/emulator/beam/utils.c160
-rw-r--r--erts/emulator/drivers/common/inet_drv.c49
-rw-r--r--erts/emulator/hipe/hipe_gc.c119
-rw-r--r--erts/emulator/hipe/hipe_risc_gc.h13
-rw-r--r--erts/emulator/hipe/hipe_stack.h3
-rw-r--r--erts/emulator/hipe/hipe_x86_gc.h17
-rw-r--r--erts/emulator/internal_doc/Tracing.md2
-rw-r--r--erts/emulator/sys/common/erl_poll.c14
-rw-r--r--erts/emulator/sys/unix/erl_child_setup.c14
-rw-r--r--erts/emulator/sys/unix/sys.c12
-rw-r--r--erts/emulator/test/Makefile1
-rw-r--r--erts/emulator/test/after_SUITE.erl4
-rw-r--r--erts/emulator/test/alloc_SUITE.erl2
-rw-r--r--erts/emulator/test/busy_port_SUITE.erl6
-rw-r--r--erts/emulator/test/code_SUITE.erl223
-rw-r--r--erts/emulator/test/code_SUITE_data/my_code_test.erl2
-rw-r--r--erts/emulator/test/code_SUITE_data/my_code_test2.erl32
-rw-r--r--erts/emulator/test/dirty_nif_SUITE.erl117
-rw-r--r--erts/emulator/test/dirty_nif_SUITE_data/dirty_nif_SUITE.c27
-rw-r--r--erts/emulator/test/distribution_SUITE.erl111
-rw-r--r--erts/emulator/test/driver_SUITE.erl18
-rw-r--r--erts/emulator/test/driver_SUITE_data/timer_drv.c44
-rw-r--r--erts/emulator/test/erl_link_SUITE.erl2
-rw-r--r--erts/emulator/test/estone_SUITE.erl2
-rw-r--r--erts/emulator/test/float_SUITE.erl2
-rw-r--r--erts/emulator/test/hipe_SUITE.erl67
-rw-r--r--erts/emulator/test/hipe_SUITE_data/literals.erl26
-rw-r--r--erts/emulator/test/hipe_SUITE_data/ref_cell.erl64
-rw-r--r--erts/emulator/test/long_timers_test.erl188
-rw-r--r--erts/emulator/test/map_SUITE.erl35
-rw-r--r--erts/emulator/test/message_queue_data_SUITE.erl2
-rw-r--r--erts/emulator/test/monitor_SUITE.erl2
-rw-r--r--erts/emulator/test/mtx_SUITE.erl2
-rw-r--r--erts/emulator/test/nif_SUITE.erl211
-rw-r--r--erts/emulator/test/nif_SUITE_data/nif_SUITE.c48
-rw-r--r--erts/emulator/test/nif_SUITE_data/nif_mod.erl10
-rw-r--r--erts/emulator/test/node_container_SUITE.erl2
-rw-r--r--erts/emulator/test/old_scheduler_SUITE.erl4
-rw-r--r--erts/emulator/test/port_SUITE.erl31
-rw-r--r--erts/emulator/test/process_SUITE.erl6
-rw-r--r--erts/emulator/test/scheduler_SUITE.erl73
-rw-r--r--erts/emulator/test/signal_SUITE.erl2
-rw-r--r--erts/emulator/test/smoke_test_SUITE.erl2
-rw-r--r--erts/emulator/test/statistics_SUITE.erl17
-rw-r--r--erts/emulator/test/system_info_SUITE.erl2
-rw-r--r--erts/emulator/test/time_SUITE.erl12
-rw-r--r--erts/emulator/test/timer_bif_SUITE.erl20
-rw-r--r--erts/emulator/test/trace_bif_SUITE.erl4
-rw-r--r--erts/emulator/test/trace_call_time_SUITE.erl6
-rw-r--r--erts/emulator/test/z_SUITE.erl8
-rw-r--r--erts/etc/unix/cerl.src18
-rw-r--r--erts/etc/unix/format_man_pages2
-rw-r--r--erts/etc/unix/run_erl.c29
-rw-r--r--erts/example/time_compat.erl4
-rw-r--r--erts/include/internal/erl_misc_utils.h29
-rw-r--r--erts/lib_src/pthread/ethr_event.c74
-rw-r--r--erts/preloaded/ebin/erlang.beambin104816 -> 105100 bytes
-rw-r--r--erts/preloaded/ebin/erts_code_purger.beambin8696 -> 12068 bytes
-rw-r--r--erts/preloaded/ebin/erts_dirty_process_code_checker.beambin0 -> 2144 bytes
-rw-r--r--erts/preloaded/ebin/erts_internal.beambin10536 -> 11104 bytes
-rw-r--r--erts/preloaded/ebin/erts_literal_area_collector.beambin0 -> 3304 bytes
-rw-r--r--erts/preloaded/ebin/init.beambin50048 -> 50032 bytes
-rw-r--r--erts/preloaded/ebin/prim_inet.beambin76236 -> 76544 bytes
-rw-r--r--erts/preloaded/src/Makefile4
-rw-r--r--erts/preloaded/src/erlang.erl31
-rw-r--r--erts/preloaded/src/erts.app.src2
-rw-r--r--erts/preloaded/src/erts_code_purger.erl170
-rw-r--r--erts/preloaded/src/erts_dirty_process_code_checker.erl82
-rw-r--r--erts/preloaded/src/erts_internal.erl52
-rw-r--r--erts/preloaded/src/erts_literal_area_collector.erl113
-rw-r--r--erts/preloaded/src/init.erl9
-rw-r--r--erts/preloaded/src/prim_inet.erl21
-rw-r--r--erts/start_scripts/Makefile5
-rw-r--r--erts/test/run_erl_SUITE.erl19
-rw-r--r--erts/test/z_SUITE.erl14
-rw-r--r--erts/vsn.mk2
-rw-r--r--lib/asn1/doc/src/asn1_getting_started.xml2
-rw-r--r--lib/asn1/doc/src/asn1_introduction.xml2
-rw-r--r--lib/asn1/doc/src/notes.xml20
-rw-r--r--lib/asn1/src/asn1_db.erl4
-rw-r--r--lib/asn1/src/asn1ct.erl3
-rw-r--r--lib/asn1/test/asn1_test_lib.erl53
-rw-r--r--lib/asn1/vsn.mk2
-rw-r--r--lib/common_test/doc/src/common_test_app.xml2
-rw-r--r--lib/common_test/doc/src/ct.xml36
-rw-r--r--lib/common_test/doc/src/ct_hooks.xml2
-rw-r--r--lib/common_test/doc/src/ct_hooks_chapter.xml8
-rw-r--r--lib/common_test/doc/src/ct_ssh.xml106
-rw-r--r--lib/common_test/doc/src/ct_telnet.xml4
-rw-r--r--lib/common_test/doc/src/event_handler_chapter.xml6
-rw-r--r--lib/common_test/doc/src/introduction.xml2
-rw-r--r--lib/common_test/doc/src/notes.xml36
-rw-r--r--lib/common_test/doc/src/run_test_chapter.xml6
-rw-r--r--lib/common_test/doc/src/write_test_chapter.xml14
-rw-r--r--lib/common_test/src/ct.erl19
-rw-r--r--lib/common_test/src/ct_gen_conn.erl10
-rw-r--r--lib/common_test/src/ct_logs.erl17
-rw-r--r--lib/common_test/src/ct_make.erl2
-rw-r--r--lib/common_test/src/ct_telnet.erl88
-rw-r--r--lib/common_test/src/erl2html2.erl2
-rw-r--r--lib/common_test/test/ct_auto_compile_SUITE.erl1
-rw-r--r--lib/common_test/test_server/ts_run.erl2
-rw-r--r--lib/common_test/vsn.mk2
-rw-r--r--lib/compiler/doc/src/compile.xml6
-rw-r--r--lib/compiler/doc/src/notes.xml58
-rw-r--r--lib/compiler/doc/src/ref_man.xml2
-rw-r--r--lib/compiler/src/beam_block.erl98
-rw-r--r--lib/compiler/src/beam_dead.erl7
-rw-r--r--lib/compiler/src/beam_jump.erl22
-rw-r--r--lib/compiler/src/beam_validator.erl4
-rw-r--r--lib/compiler/src/sys_core_fold.erl3
-rw-r--r--lib/compiler/test/beam_block_SUITE.erl27
-rw-r--r--lib/compiler/test/beam_jump_SUITE.erl19
-rw-r--r--lib/compiler/test/beam_validator_SUITE.erl25
-rw-r--r--lib/compiler/test/core_fold_SUITE.erl16
-rw-r--r--lib/compiler/test/match_SUITE.erl13
-rw-r--r--lib/compiler/vsn.mk2
-rw-r--r--lib/crypto/c_src/crypto.c76
-rw-r--r--lib/crypto/doc/src/crypto.xml4
-rw-r--r--lib/crypto/doc/src/notes.xml28
-rw-r--r--lib/crypto/src/crypto.erl14
-rw-r--r--lib/crypto/test/crypto_SUITE.erl25
-rw-r--r--lib/crypto/test/old_crypto_SUITE.erl47
-rw-r--r--lib/crypto/vsn.mk2
-rw-r--r--lib/debugger/doc/src/Makefile2
-rw-r--r--lib/debugger/doc/src/i.xml4
-rw-r--r--lib/debugger/doc/src/notes.xml16
-rw-r--r--lib/debugger/src/dbg_iserver.erl40
-rw-r--r--lib/debugger/src/dbg_wx_trace.erl13
-rw-r--r--lib/debugger/src/dbg_wx_view.erl12
-rw-r--r--lib/debugger/vsn.mk2
-rw-r--r--lib/dialyzer/doc/src/notes.xml23
-rw-r--r--lib/dialyzer/src/dialyzer.erl2
-rw-r--r--lib/dialyzer/src/dialyzer_contracts.erl12
-rw-r--r--lib/dialyzer/test/behaviour_SUITE_data/dialyzer_options1
-rw-r--r--lib/dialyzer/test/map_SUITE_data/src/opaque_bif.erl13
-rw-r--r--lib/dialyzer/test/opaque_SUITE_data/dialyzer_options1
-rw-r--r--lib/dialyzer/vsn.mk2
-rw-r--r--lib/diameter/doc/src/diameter.xml155
-rw-r--r--lib/diameter/doc/src/diameter_app.xml39
-rw-r--r--lib/diameter/doc/src/diameter_codec.xml56
-rw-r--r--lib/diameter/doc/src/diameter_dict.xml69
-rw-r--r--lib/diameter/doc/src/diameter_examples.xml1
-rw-r--r--lib/diameter/doc/src/diameter_tcp.xml2
-rw-r--r--lib/diameter/doc/src/diameter_transport.xml9
-rw-r--r--lib/diameter/doc/src/diameterc.xml6
-rw-r--r--lib/diameter/doc/src/notes.xml25
-rw-r--r--lib/diameter/examples/code/relay.erl2
-rw-r--r--lib/diameter/src/Makefile2
-rw-r--r--lib/diameter/src/base/diameter_lib.erl24
-rw-r--r--lib/diameter/src/base/diameter_traffic.erl88
-rw-r--r--lib/diameter/src/compiler/diameter_codegen.erl15
-rw-r--r--lib/diameter/src/diameter.appup.src12
-rw-r--r--lib/diameter/src/transport/diameter_sctp.erl76
-rw-r--r--lib/diameter/src/transport/diameter_tcp.erl104
-rw-r--r--lib/diameter/test/diameter_traffic_SUITE.erl7
-rw-r--r--lib/diameter/test/diameter_util.erl22
-rw-r--r--lib/diameter/vsn.mk2
-rw-r--r--lib/edoc/doc/overview.edoc10
-rw-r--r--lib/edoc/doc/src/notes.xml15
-rw-r--r--lib/edoc/vsn.mk2
-rw-r--r--lib/erl_docgen/doc/src/notes.xml17
-rw-r--r--lib/erl_docgen/vsn.mk2
-rw-r--r--lib/erl_interface/doc/src/notes.xml17
-rw-r--r--lib/erl_interface/src/connect/ei_connect.c40
-rw-r--r--lib/erl_interface/src/misc/ei_portio.h2
-rw-r--r--lib/erl_interface/vsn.mk2
-rw-r--r--lib/eunit/doc/src/notes.xml17
-rw-r--r--lib/eunit/include/eunit.hrl4
-rw-r--r--lib/eunit/vsn.mk2
-rw-r--r--lib/gs/doc/src/Makefile2
-rw-r--r--lib/gs/doc/src/notes.xml18
-rw-r--r--lib/gs/vsn.mk2
-rw-r--r--lib/hipe/amd64/hipe_amd64_ra_sse2_postconditions.erl16
-rw-r--r--lib/hipe/arm/hipe_arm_registers.erl8
-rw-r--r--lib/hipe/arm/hipe_rtl_to_arm.erl9
-rw-r--r--lib/hipe/cerl/erl_bif_types.erl6
-rw-r--r--lib/hipe/cerl/erl_types.erl33
-rw-r--r--lib/hipe/doc/src/notes.xml40
-rw-r--r--lib/hipe/ppc/hipe_ppc.erl12
-rw-r--r--lib/hipe/ppc/hipe_ppc_assemble.erl7
-rw-r--r--lib/hipe/sparc/hipe_rtl_to_sparc.erl40
-rw-r--r--lib/hipe/sparc/hipe_sparc_registers.erl8
-rw-r--r--lib/hipe/test/hipe_testsuite_driver.erl2
-rw-r--r--lib/hipe/vsn.mk2
-rw-r--r--lib/hipe/x86/hipe_x86_postpass.erl3
-rw-r--r--lib/ic/doc/src/Makefile2
-rw-r--r--lib/ic/doc/src/notes.xml24
-rw-r--r--lib/ic/src/ic_codegen.erl8
-rw-r--r--lib/ic/vsn.mk2
-rw-r--r--lib/inets/doc/src/http_server.xml36
-rw-r--r--lib/inets/doc/src/httpc.xml4
-rw-r--r--lib/inets/doc/src/httpd.xml18
-rw-r--r--lib/inets/doc/src/mod_auth.xml2
-rw-r--r--lib/inets/doc/src/mod_esi.xml2
-rw-r--r--lib/inets/doc/src/notes.xml34
-rw-r--r--lib/inets/src/http_client/httpc_response.erl35
-rw-r--r--lib/inets/src/http_server/httpd_conf.erl12
-rw-r--r--lib/inets/src/http_server/httpd_example.erl11
-rw-r--r--lib/inets/src/http_server/mod_esi.erl66
-rw-r--r--lib/inets/test/httpc_SUITE.erl35
-rw-r--r--lib/inets/test/httpd_SUITE.erl60
-rw-r--r--lib/inets/test/inets_SUITE.erl14
-rw-r--r--lib/inets/test/inets_sup_SUITE.erl38
-rw-r--r--lib/inets/vsn.mk2
-rw-r--r--lib/jinterface/doc/src/Makefile2
-rw-r--r--lib/jinterface/doc/src/notes.xml16
-rw-r--r--lib/jinterface/vsn.mk2
-rw-r--r--lib/kernel/doc/src/app.xml6
-rw-r--r--lib/kernel/doc/src/application.xml6
-rw-r--r--lib/kernel/doc/src/auth.xml4
-rw-r--r--lib/kernel/doc/src/code.xml33
-rw-r--r--lib/kernel/doc/src/erl_boot_server.xml4
-rw-r--r--lib/kernel/doc/src/erl_ddll.xml6
-rw-r--r--lib/kernel/doc/src/error_logger.xml30
-rw-r--r--lib/kernel/doc/src/file.xml28
-rw-r--r--lib/kernel/doc/src/gen_tcp.xml6
-rw-r--r--lib/kernel/doc/src/heart.xml2
-rw-r--r--lib/kernel/doc/src/inet.xml40
-rw-r--r--lib/kernel/doc/src/init_stub.xml2
-rw-r--r--lib/kernel/doc/src/kernel_app.xml22
-rw-r--r--lib/kernel/doc/src/net_kernel.xml48
-rw-r--r--lib/kernel/doc/src/notes.xml105
-rw-r--r--lib/kernel/doc/src/rpc.xml4
-rw-r--r--lib/kernel/doc/src/zlib_stub.xml2
-rw-r--r--lib/kernel/include/dist_util.hrl8
-rw-r--r--lib/kernel/src/code_server.erl41
-rw-r--r--lib/kernel/src/dist_util.erl71
-rw-r--r--lib/kernel/src/erl_epmd.erl6
-rw-r--r--lib/kernel/src/inet.erl11
-rw-r--r--lib/kernel/src/inet6_tcp_dist.erl7
-rw-r--r--lib/kernel/src/inet_int.hrl1
-rw-r--r--lib/kernel/src/inet_tcp_dist.erl29
-rw-r--r--lib/kernel/src/kernel.appup.src4
-rw-r--r--lib/kernel/src/net_kernel.erl128
-rw-r--r--lib/kernel/src/os.erl76
-rw-r--r--lib/kernel/test/code_SUITE.erl94
-rw-r--r--lib/kernel/test/erl_distribution_SUITE.erl163
-rw-r--r--lib/kernel/test/file_SUITE.erl54
-rw-r--r--lib/kernel/test/gen_sctp_SUITE.erl148
-rw-r--r--lib/kernel/test/gen_tcp_api_SUITE.erl21
-rw-r--r--lib/kernel/test/init_SUITE.erl61
-rw-r--r--lib/kernel/test/os_SUITE.erl51
-rw-r--r--lib/kernel/test/os_SUITE_data/Makefile.src8
-rw-r--r--lib/kernel/test/os_SUITE_data/my_fds.c9
-rw-r--r--lib/kernel/vsn.mk2
-rw-r--r--lib/mnesia/doc/src/Mnesia_chap1.xml28
-rw-r--r--lib/mnesia/doc/src/Mnesia_chap8.xml8
-rw-r--r--lib/mnesia/doc/src/Mnesia_overview.xml42
-rw-r--r--lib/mnesia/doc/src/mnesia.xml336
-rw-r--r--lib/mnesia/doc/src/mnesia_frag_hash.xml12
-rw-r--r--lib/mnesia/doc/src/mnesia_registry.xml12
-rw-r--r--lib/mnesia/doc/src/notes.xml16
-rw-r--r--lib/mnesia/doc/src/part.xml2
-rw-r--r--lib/mnesia/doc/src/ref_man.xml2
-rw-r--r--lib/mnesia/vsn.mk2
-rw-r--r--lib/observer/doc/src/notes.xml16
-rw-r--r--lib/observer/doc/src/ttb.xml18
-rw-r--r--lib/observer/src/observer_tv_wx.erl22
-rw-r--r--lib/observer/src/observer_wx.erl8
-rw-r--r--lib/observer/src/ttb.erl9
-rw-r--r--lib/observer/vsn.mk2
-rw-r--r--lib/odbc/configure.in2
-rw-r--r--lib/odbc/doc/src/notes.xml20
-rw-r--r--lib/odbc/vsn.mk2
-rw-r--r--lib/parsetools/doc/src/notes.xml16
-rw-r--r--lib/parsetools/doc/src/yecc.xml2
-rw-r--r--lib/parsetools/src/leex.erl4
-rw-r--r--lib/parsetools/src/yecc.erl3
-rw-r--r--lib/parsetools/test/leex_SUITE.erl8
-rw-r--r--lib/parsetools/test/yecc_SUITE.erl8
-rw-r--r--lib/parsetools/vsn.mk2
-rw-r--r--lib/public_key/doc/src/public_key_app.xml4
-rw-r--r--lib/public_key/test/pbe_SUITE.erl7
-rw-r--r--lib/reltool/doc/src/notes.xml37
-rw-r--r--lib/reltool/doc/src/part.xml2
-rw-r--r--lib/reltool/doc/src/ref_man.xml2
-rw-r--r--lib/reltool/doc/src/reltool.xml8
-rw-r--r--lib/reltool/doc/src/reltool_intro.xml4
-rw-r--r--lib/reltool/src/reltool.hrl26
-rw-r--r--lib/reltool/vsn.mk2
-rw-r--r--lib/runtime_tools/doc/src/dbg.xml4
-rw-r--r--lib/runtime_tools/doc/src/notes.xml16
-rw-r--r--lib/runtime_tools/src/dbg.erl2
-rw-r--r--lib/runtime_tools/vsn.mk2
-rw-r--r--lib/sasl/doc/src/alarm_handler.xml4
-rw-r--r--lib/sasl/doc/src/appup.xml10
-rw-r--r--lib/sasl/doc/src/error_logging.xml18
-rw-r--r--lib/sasl/doc/src/notes.xml55
-rw-r--r--lib/sasl/doc/src/part.xml2
-rw-r--r--lib/sasl/doc/src/rb.xml6
-rw-r--r--lib/sasl/doc/src/ref_man.xml2
-rw-r--r--lib/sasl/doc/src/rel.xml6
-rw-r--r--lib/sasl/doc/src/release_handler.xml20
-rw-r--r--lib/sasl/doc/src/sasl_app.xml24
-rw-r--r--lib/sasl/doc/src/sasl_intro.xml2
-rw-r--r--lib/sasl/doc/src/script.xml2
-rw-r--r--lib/sasl/doc/src/systools.xml6
-rw-r--r--lib/sasl/src/sasl.app.src2
-rw-r--r--lib/sasl/src/systools_make.erl5
-rw-r--r--lib/sasl/vsn.mk2
-rw-r--r--lib/snmp/doc/src/notes.xml30
-rw-r--r--lib/snmp/src/app/snmp.app.src12
-rw-r--r--lib/snmp/src/compile/snmpc.erl11
-rw-r--r--lib/snmp/src/compile/snmpc_misc.erl20
-rw-r--r--lib/snmp/test/snmp_agent_test.erl26
-rw-r--r--lib/snmp/test/snmp_app_test.erl381
-rw-r--r--lib/snmp/test/snmp_manager_test.erl26
-rw-r--r--lib/snmp/test/snmp_to_snmpnet_SUITE.erl10
-rw-r--r--lib/snmp/vsn.mk2
-rw-r--r--lib/ssh/doc/src/notes.xml62
-rw-r--r--lib/ssh/doc/src/ssh.xml6
-rw-r--r--lib/ssh/doc/src/ssh_app.xml4
-rw-r--r--lib/ssh/doc/src/ssh_channel.xml2
-rw-r--r--lib/ssh/src/ssh.app.src2
-rw-r--r--lib/ssh/src/ssh.erl123
-rw-r--r--lib/ssh/src/ssh.hrl2
-rw-r--r--lib/ssh/src/ssh_auth.erl124
-rw-r--r--lib/ssh/src/ssh_cli.erl11
-rw-r--r--lib/ssh/src/ssh_connection_handler.erl127
-rw-r--r--lib/ssh/src/ssh_transport.erl7
-rw-r--r--lib/ssh/test/ssh_algorithms_SUITE.erl18
-rw-r--r--lib/ssh/test/ssh_basic_SUITE.erl33
-rw-r--r--lib/ssh/vsn.mk2
-rw-r--r--lib/ssl/doc/src/notes.xml84
-rw-r--r--lib/ssl/doc/src/ssl_app.xml10
-rw-r--r--lib/ssl/doc/src/ssl_distribution.xml4
-rw-r--r--lib/ssl/src/dtls_connection.erl191
-rw-r--r--lib/ssl/src/dtls_handshake.erl20
-rw-r--r--lib/ssl/src/dtls_record.erl209
-rw-r--r--lib/ssl/src/ssl.app.src2
-rw-r--r--lib/ssl/src/ssl.appup.src17
-rw-r--r--lib/ssl/src/ssl.erl7
-rw-r--r--lib/ssl/src/ssl_alert.erl4
-rw-r--r--lib/ssl/src/ssl_certificate.erl34
-rw-r--r--lib/ssl/src/ssl_cipher.erl33
-rw-r--r--lib/ssl/src/ssl_connection.erl634
-rw-r--r--lib/ssl/src/ssl_connection.hrl4
-rw-r--r--lib/ssl/src/ssl_crl.erl28
-rw-r--r--lib/ssl/src/ssl_handshake.erl128
-rw-r--r--lib/ssl/src/ssl_manager.erl43
-rw-r--r--lib/ssl/src/ssl_pkix_db.erl67
-rw-r--r--lib/ssl/src/ssl_record.erl438
-rw-r--r--lib/ssl/src/ssl_record.hrl42
-rw-r--r--lib/ssl/src/ssl_sup.erl11
-rw-r--r--lib/ssl/src/ssl_tls_dist_proxy.erl2
-rw-r--r--lib/ssl/src/tls_connection.erl523
-rw-r--r--lib/ssl/src/tls_handshake.erl52
-rw-r--r--lib/ssl/src/tls_record.erl131
-rw-r--r--lib/ssl/test/ssl.spec3
-rw-r--r--lib/ssl/test/ssl_ECC_SUITE.erl2
-rw-r--r--lib/ssl/test/ssl_alpn_handshake_SUITE.erl2
-rw-r--r--lib/ssl/test/ssl_basic_SUITE.erl33
-rw-r--r--lib/ssl/test/ssl_bench_SUITE.erl102
-rw-r--r--lib/ssl/test/ssl_certificate_verify_SUITE.erl2
-rw-r--r--lib/ssl/test/ssl_crl_SUITE.erl6
-rw-r--r--lib/ssl/test/ssl_handshake_SUITE.erl2
-rw-r--r--lib/ssl/test/ssl_npn_handshake_SUITE.erl2
-rw-r--r--lib/ssl/test/ssl_npn_hello_SUITE.erl22
-rw-r--r--lib/ssl/test/ssl_packet_SUITE.erl62
-rw-r--r--lib/ssl/test/ssl_payload_SUITE.erl11
-rw-r--r--lib/ssl/test/ssl_pem_cache_SUITE.erl5
-rw-r--r--lib/ssl/test/ssl_session_cache_SUITE.erl2
-rw-r--r--lib/ssl/test/ssl_sni_SUITE.erl2
-rw-r--r--lib/ssl/test/ssl_test_lib.erl59
-rw-r--r--lib/ssl/test/ssl_to_openssl_SUITE.erl60
-rw-r--r--lib/ssl/test/ssl_upgrade_SUITE.erl20
-rw-r--r--lib/ssl/vsn.mk2
-rw-r--r--lib/stdlib/doc/src/dets.xml2
-rw-r--r--lib/stdlib/doc/src/ets.xml8
-rw-r--r--lib/stdlib/doc/src/gen_statem.xml249
-rw-r--r--lib/stdlib/doc/src/notes.xml79
-rw-r--r--lib/stdlib/doc/src/proc_lib.xml4
-rw-r--r--lib/stdlib/doc/src/queue.xml2
-rw-r--r--lib/stdlib/doc/src/stdlib_app.xml8
-rw-r--r--lib/stdlib/doc/src/timer.xml2
-rw-r--r--lib/stdlib/doc/src/unicode_usage.xml8
-rw-r--r--lib/stdlib/doc/src/zip.xml6
-rw-r--r--lib/stdlib/src/beam_lib.erl2
-rw-r--r--lib/stdlib/src/edlin_expand.erl2
-rw-r--r--lib/stdlib/src/gen_statem.erl261
-rw-r--r--lib/stdlib/src/shell.erl4
-rw-r--r--lib/stdlib/src/stdlib.appup.src4
-rw-r--r--lib/stdlib/src/supervisor.erl4
-rw-r--r--lib/stdlib/src/zip.erl82
-rw-r--r--lib/stdlib/test/edlin_expand_SUITE.erl11
-rw-r--r--lib/stdlib/test/ets_SUITE.erl13
-rw-r--r--lib/stdlib/test/gen_statem_SUITE.erl121
-rw-r--r--lib/stdlib/test/shell_SUITE.erl17
-rw-r--r--lib/stdlib/test/zip_SUITE.erl50
-rw-r--r--lib/stdlib/test/zip_SUITE_data/exploit.zipbin0 -> 797 bytes
-rw-r--r--lib/stdlib/vsn.mk2
-rw-r--r--lib/syntax_tools/doc/src/notes.xml34
-rw-r--r--lib/syntax_tools/src/erl_tidy.erl4
-rw-r--r--lib/syntax_tools/src/merl_tests.erl15
-rw-r--r--lib/syntax_tools/src/merl_transform.erl13
-rw-r--r--lib/syntax_tools/test/merl_SUITE.erl21
-rw-r--r--lib/syntax_tools/vsn.mk2
-rw-r--r--lib/tools/doc/src/erlang_mode.xml9
-rw-r--r--lib/tools/doc/src/notes.xml32
-rw-r--r--lib/tools/doc/src/xref_chapter.xml6
-rw-r--r--lib/tools/emacs/erlang-skels.el153
-rw-r--r--lib/tools/emacs/erlang-test.el106
-rw-r--r--lib/tools/emacs/erlang.el112
-rw-r--r--lib/tools/src/fprof.erl11
-rw-r--r--lib/tools/src/make.erl2
-rw-r--r--lib/tools/src/tools.app.src2
-rw-r--r--lib/tools/test/cover_SUITE.erl33
-rw-r--r--lib/tools/test/cover_SUITE_data/otp_6115/f1.erl15
-rw-r--r--lib/tools/test/cover_SUITE_data/otp_6115/f2.erl14
-rw-r--r--lib/tools/test/make_SUITE_data/test1.erl2
-rw-r--r--lib/tools/vsn.mk2
-rw-r--r--lib/wx/api_gen/wx_extra/wxListCtrl.erl62
-rw-r--r--lib/wx/api_gen/wx_extra/wxXmlResource.erl6
-rw-r--r--lib/wx/c_src/wxe_main.cpp6
-rw-r--r--lib/wx/doc/src/notes.xml17
-rw-r--r--lib/wx/include/wx.hrl434
-rw-r--r--lib/wx/src/gen/wxListCtrl.erl62
-rw-r--r--lib/wx/src/gen/wxXmlResource.erl6
-rw-r--r--lib/wx/src/wx_object.erl84
-rw-r--r--lib/wx/vsn.mk2
-rw-r--r--lib/xmerl/doc/src/notes.xml15
-rw-r--r--lib/xmerl/src/xmerl_eventp.erl84
-rw-r--r--lib/xmerl/src/xmerl_scan.erl5
-rw-r--r--lib/xmerl/src/xmerl_xpath.erl30
-rw-r--r--lib/xmerl/src/xmerl_xs.erl7
-rw-r--r--lib/xmerl/src/xmerl_xsd.erl13
-rw-r--r--lib/xmerl/vsn.mk2
-rwxr-xr-x[-rw-r--r--]make/emd2exml.in2
-rwxr-xr-xmake/make_emakefile.in (renamed from make/make_emakefile)2
-rw-r--r--make/otp.mk.in1
-rw-r--r--otp_versions.table11
-rw-r--r--system/README78
-rw-r--r--system/doc/design_principles/statem.xml88
-rw-r--r--system/doc/efficiency_guide/advanced.xml4
-rw-r--r--system/doc/efficiency_guide/profiling.xml8
-rw-r--r--system/doc/embedded/embedded_nt.xml4
-rw-r--r--system/doc/embedded/embedded_solaris.xml14
-rw-r--r--system/doc/embedded/starting.xml8
-rw-r--r--system/doc/oam/oam_intro.xml10
-rw-r--r--system/doc/programming_examples/records.xml2
-rw-r--r--system/doc/reference_manual/typespec.xml2
-rw-r--r--system/doc/top/Makefile2
-rw-r--r--system/doc/tutorial/c_port.xmlsrc4
576 files changed, 12263 insertions, 5487 deletions
diff --git a/.gitignore b/.gitignore
index 3fc95170aa..71350ca1b9 100644
--- a/.gitignore
+++ b/.gitignore
@@ -143,6 +143,7 @@ JAVADOC-GENERATED
/make/output.mk
/make/emd2exml
+/make/make_emakefile
# Created by "out_build update_primary"
/bootstrap/primary_compiler/
diff --git a/HOWTO/INSTALL-WIN32.md b/HOWTO/INSTALL-WIN32.md
index d7be255e9f..c74107d749 100644
--- a/HOWTO/INSTALL-WIN32.md
+++ b/HOWTO/INSTALL-WIN32.md
@@ -392,7 +392,7 @@ tools:
LIB="$WIN_VISUAL_STUDIO_ROOT\\VC\\lib\\;$WIN_SDK\\lib\\winv6.3\\um\\x86"
- INCLUDE="$WIN_VISUAL_STUDIO_ROOT\\VC\\include\\;$WIN_SDK\\include\\shared\\;
+ INCLUDE="$WIN_VISUAL_STUDIO_ROOT\\VC\\include\\;$WIN_SDK\\include\\shared\\;\
$WIN_SDK\\include\\um;$WIN_SDK\\include\\winrt\\;$WIN_SDK\\include\\um\\gl"
export CYGWIN PATH LIBPATH LIB INCLUDE
diff --git a/HOWTO/INSTALL.md b/HOWTO/INSTALL.md
index 8632f46264..551ecdd224 100644
--- a/HOWTO/INSTALL.md
+++ b/HOWTO/INSTALL.md
@@ -429,6 +429,10 @@ Some of the available `configure` options are:
and **not supported**. This functionality **will** be subject to backward
incompatible changes. Note that you should **not** enable the dirty scheduler
functionality on production systems. It is only provided for testing.
+ This switch also imply `--enable-new-purge-strategy` (see below).
+* `--enable-new-purge-strategy` - Enable the purge strategy that will be
+ introduced in ERTS version 9.0 (OTP 20). Note that this switch will be
+ removed in OTP 20.
If you or your system has special requirements please read the `Makefile` for
additional configuration information.
diff --git a/OTP_VERSION b/OTP_VERSION
index 1870facefc..d1c39686d0 100644
--- a/OTP_VERSION
+++ b/OTP_VERSION
@@ -1 +1 @@
-19.0.1
+19.1.1
diff --git a/bootstrap/bin/start.boot b/bootstrap/bin/start.boot
index ef962190a4..4a1f656c3c 100644
--- a/bootstrap/bin/start.boot
+++ b/bootstrap/bin/start.boot
Binary files differ
diff --git a/bootstrap/bin/start_clean.boot b/bootstrap/bin/start_clean.boot
index ef962190a4..4a1f656c3c 100644
--- a/bootstrap/bin/start_clean.boot
+++ b/bootstrap/bin/start_clean.boot
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/beam_asm.beam b/bootstrap/lib/compiler/ebin/beam_asm.beam
index c8bc82022c..481d43c967 100644
--- a/bootstrap/lib/compiler/ebin/beam_asm.beam
+++ b/bootstrap/lib/compiler/ebin/beam_asm.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/beam_block.beam b/bootstrap/lib/compiler/ebin/beam_block.beam
index ea91a62b56..b6a4b6f10f 100644
--- a/bootstrap/lib/compiler/ebin/beam_block.beam
+++ b/bootstrap/lib/compiler/ebin/beam_block.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/beam_bool.beam b/bootstrap/lib/compiler/ebin/beam_bool.beam
index 1673c4aadb..37f368190e 100644
--- a/bootstrap/lib/compiler/ebin/beam_bool.beam
+++ b/bootstrap/lib/compiler/ebin/beam_bool.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/beam_bsm.beam b/bootstrap/lib/compiler/ebin/beam_bsm.beam
index 51abd68b00..48e8f80f79 100644
--- a/bootstrap/lib/compiler/ebin/beam_bsm.beam
+++ b/bootstrap/lib/compiler/ebin/beam_bsm.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/beam_clean.beam b/bootstrap/lib/compiler/ebin/beam_clean.beam
index 0f24e040e3..ed0827953c 100644
--- a/bootstrap/lib/compiler/ebin/beam_clean.beam
+++ b/bootstrap/lib/compiler/ebin/beam_clean.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/beam_dead.beam b/bootstrap/lib/compiler/ebin/beam_dead.beam
index d2dc2f7688..c473003687 100644
--- a/bootstrap/lib/compiler/ebin/beam_dead.beam
+++ b/bootstrap/lib/compiler/ebin/beam_dead.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/beam_validator.beam b/bootstrap/lib/compiler/ebin/beam_validator.beam
index fcb2202d2a..9d952f0a64 100644
--- a/bootstrap/lib/compiler/ebin/beam_validator.beam
+++ b/bootstrap/lib/compiler/ebin/beam_validator.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/cerl.beam b/bootstrap/lib/compiler/ebin/cerl.beam
index dea6cb4ee2..eecdd4e2dd 100644
--- a/bootstrap/lib/compiler/ebin/cerl.beam
+++ b/bootstrap/lib/compiler/ebin/cerl.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/compile.beam b/bootstrap/lib/compiler/ebin/compile.beam
index 8003d590c9..044ed03d93 100644
--- a/bootstrap/lib/compiler/ebin/compile.beam
+++ b/bootstrap/lib/compiler/ebin/compile.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/compiler.app b/bootstrap/lib/compiler/ebin/compiler.app
index 53891c7260..ebd5205219 100644
--- a/bootstrap/lib/compiler/ebin/compiler.app
+++ b/bootstrap/lib/compiler/ebin/compiler.app
@@ -19,7 +19,7 @@
{application, compiler,
[{description, "ERTS CXC 138 10"},
- {vsn, "6.0.3"},
+ {vsn, "7.0.1"},
{modules, [
beam_a,
beam_asm,
diff --git a/bootstrap/lib/compiler/ebin/core_lint.beam b/bootstrap/lib/compiler/ebin/core_lint.beam
index 0e47e00b15..47f9f53546 100644
--- a/bootstrap/lib/compiler/ebin/core_lint.beam
+++ b/bootstrap/lib/compiler/ebin/core_lint.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/core_parse.beam b/bootstrap/lib/compiler/ebin/core_parse.beam
index 920dbc922f..2599138a8c 100644
--- a/bootstrap/lib/compiler/ebin/core_parse.beam
+++ b/bootstrap/lib/compiler/ebin/core_parse.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/core_scan.beam b/bootstrap/lib/compiler/ebin/core_scan.beam
index 5f7b22ba8b..3017a4e026 100644
--- a/bootstrap/lib/compiler/ebin/core_scan.beam
+++ b/bootstrap/lib/compiler/ebin/core_scan.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/sys_core_fold.beam b/bootstrap/lib/compiler/ebin/sys_core_fold.beam
index 68cc4e3ff3..75a6570461 100644
--- a/bootstrap/lib/compiler/ebin/sys_core_fold.beam
+++ b/bootstrap/lib/compiler/ebin/sys_core_fold.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/v3_core.beam b/bootstrap/lib/compiler/ebin/v3_core.beam
index e8e120e145..54f507218f 100644
--- a/bootstrap/lib/compiler/ebin/v3_core.beam
+++ b/bootstrap/lib/compiler/ebin/v3_core.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/v3_kernel.beam b/bootstrap/lib/compiler/ebin/v3_kernel.beam
index 47eaf3b5e8..60cc6503eb 100644
--- a/bootstrap/lib/compiler/ebin/v3_kernel.beam
+++ b/bootstrap/lib/compiler/ebin/v3_kernel.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/v3_life.beam b/bootstrap/lib/compiler/ebin/v3_life.beam
index b220e475ee..d4b3f2a5e6 100644
--- a/bootstrap/lib/compiler/ebin/v3_life.beam
+++ b/bootstrap/lib/compiler/ebin/v3_life.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/application_controller.beam b/bootstrap/lib/kernel/ebin/application_controller.beam
index 2b3c8cf454..d46de4527a 100644
--- a/bootstrap/lib/kernel/ebin/application_controller.beam
+++ b/bootstrap/lib/kernel/ebin/application_controller.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/application_master.beam b/bootstrap/lib/kernel/ebin/application_master.beam
index 24492a6771..da770c5a84 100644
--- a/bootstrap/lib/kernel/ebin/application_master.beam
+++ b/bootstrap/lib/kernel/ebin/application_master.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/auth.beam b/bootstrap/lib/kernel/ebin/auth.beam
index cbe477830b..1ccad1a5a3 100644
--- a/bootstrap/lib/kernel/ebin/auth.beam
+++ b/bootstrap/lib/kernel/ebin/auth.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/code_server.beam b/bootstrap/lib/kernel/ebin/code_server.beam
index 6f99487ca7..7f073fe932 100644
--- a/bootstrap/lib/kernel/ebin/code_server.beam
+++ b/bootstrap/lib/kernel/ebin/code_server.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/disk_log.beam b/bootstrap/lib/kernel/ebin/disk_log.beam
index 5aa8bf7dc1..31fb2d609d 100644
--- a/bootstrap/lib/kernel/ebin/disk_log.beam
+++ b/bootstrap/lib/kernel/ebin/disk_log.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/disk_log_1.beam b/bootstrap/lib/kernel/ebin/disk_log_1.beam
index cb627c10a5..6b10d652de 100644
--- a/bootstrap/lib/kernel/ebin/disk_log_1.beam
+++ b/bootstrap/lib/kernel/ebin/disk_log_1.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/disk_log_server.beam b/bootstrap/lib/kernel/ebin/disk_log_server.beam
index 50154045be..67cda715c4 100644
--- a/bootstrap/lib/kernel/ebin/disk_log_server.beam
+++ b/bootstrap/lib/kernel/ebin/disk_log_server.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/dist_ac.beam b/bootstrap/lib/kernel/ebin/dist_ac.beam
index d2c186f03a..56868dfe6d 100644
--- a/bootstrap/lib/kernel/ebin/dist_ac.beam
+++ b/bootstrap/lib/kernel/ebin/dist_ac.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/dist_util.beam b/bootstrap/lib/kernel/ebin/dist_util.beam
index eb09a06785..bf3572fe23 100644
--- a/bootstrap/lib/kernel/ebin/dist_util.beam
+++ b/bootstrap/lib/kernel/ebin/dist_util.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/erl_epmd.beam b/bootstrap/lib/kernel/ebin/erl_epmd.beam
index 8e8460514c..19be6babd1 100644
--- a/bootstrap/lib/kernel/ebin/erl_epmd.beam
+++ b/bootstrap/lib/kernel/ebin/erl_epmd.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/global.beam b/bootstrap/lib/kernel/ebin/global.beam
index 465dab3a52..601cb9176b 100644
--- a/bootstrap/lib/kernel/ebin/global.beam
+++ b/bootstrap/lib/kernel/ebin/global.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/global_group.beam b/bootstrap/lib/kernel/ebin/global_group.beam
index 03202f68eb..8bd51c2424 100644
--- a/bootstrap/lib/kernel/ebin/global_group.beam
+++ b/bootstrap/lib/kernel/ebin/global_group.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/group.beam b/bootstrap/lib/kernel/ebin/group.beam
index 38eb0e7a6c..c2c66f77ec 100644
--- a/bootstrap/lib/kernel/ebin/group.beam
+++ b/bootstrap/lib/kernel/ebin/group.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/heart.beam b/bootstrap/lib/kernel/ebin/heart.beam
index 86044153af..2ab7ef9d9f 100644
--- a/bootstrap/lib/kernel/ebin/heart.beam
+++ b/bootstrap/lib/kernel/ebin/heart.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/hipe_unified_loader.beam b/bootstrap/lib/kernel/ebin/hipe_unified_loader.beam
index 5a2f294951..f6715adbfe 100644
--- a/bootstrap/lib/kernel/ebin/hipe_unified_loader.beam
+++ b/bootstrap/lib/kernel/ebin/hipe_unified_loader.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/inet.beam b/bootstrap/lib/kernel/ebin/inet.beam
index 5ae4090a61..ff7f0ba7e6 100644
--- a/bootstrap/lib/kernel/ebin/inet.beam
+++ b/bootstrap/lib/kernel/ebin/inet.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/inet6_tcp_dist.beam b/bootstrap/lib/kernel/ebin/inet6_tcp_dist.beam
index 1be1dc1c57..98f70db13c 100644
--- a/bootstrap/lib/kernel/ebin/inet6_tcp_dist.beam
+++ b/bootstrap/lib/kernel/ebin/inet6_tcp_dist.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/inet_res.beam b/bootstrap/lib/kernel/ebin/inet_res.beam
index 2713c7fef6..b1bbf771a3 100644
--- a/bootstrap/lib/kernel/ebin/inet_res.beam
+++ b/bootstrap/lib/kernel/ebin/inet_res.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/inet_tcp_dist.beam b/bootstrap/lib/kernel/ebin/inet_tcp_dist.beam
index d06bdf709a..087274e89d 100644
--- a/bootstrap/lib/kernel/ebin/inet_tcp_dist.beam
+++ b/bootstrap/lib/kernel/ebin/inet_tcp_dist.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/kernel_config.beam b/bootstrap/lib/kernel/ebin/kernel_config.beam
index 8379f77e94..5e59fc2f82 100644
--- a/bootstrap/lib/kernel/ebin/kernel_config.beam
+++ b/bootstrap/lib/kernel/ebin/kernel_config.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/net_kernel.beam b/bootstrap/lib/kernel/ebin/net_kernel.beam
index 8afe1c0388..1197b4f681 100644
--- a/bootstrap/lib/kernel/ebin/net_kernel.beam
+++ b/bootstrap/lib/kernel/ebin/net_kernel.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/user.beam b/bootstrap/lib/kernel/ebin/user.beam
index c2b2808555..cded4b87be 100644
--- a/bootstrap/lib/kernel/ebin/user.beam
+++ b/bootstrap/lib/kernel/ebin/user.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/user_drv.beam b/bootstrap/lib/kernel/ebin/user_drv.beam
index 1d72927105..bd9b1a3bce 100644
--- a/bootstrap/lib/kernel/ebin/user_drv.beam
+++ b/bootstrap/lib/kernel/ebin/user_drv.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/wrap_log_reader.beam b/bootstrap/lib/kernel/ebin/wrap_log_reader.beam
index 91ef523b99..93f1f1ba72 100644
--- a/bootstrap/lib/kernel/ebin/wrap_log_reader.beam
+++ b/bootstrap/lib/kernel/ebin/wrap_log_reader.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/include/dist_util.hrl b/bootstrap/lib/kernel/include/dist_util.hrl
index 43e50d4325..320e916c04 100644
--- a/bootstrap/lib/kernel/include/dist_util.hrl
+++ b/bootstrap/lib/kernel/include/dist_util.hrl
@@ -63,7 +63,7 @@
f_getll, %% Get low level port or pid.
f_address, %% The address of the "socket",
%% generated from Socket,Node
- %% These two are used in the tick loop,
+ %% These three are used in the tick loop,
%% so they are not fun's to avoid holding old code.
mf_tick, %% Takes the socket as parameters and
%% sends a tick, this is no fun, it
@@ -74,7 +74,11 @@
%% {ok, RecvCnt, SendCnt, SendPend} for
%% a given socket. This is a {M,F},
%% returning {error, Reason on failure}
- request_type = normal
+ request_type = normal,
+
+ %% New in kernel-5.1 (OTP 19.1):
+ mf_setopts, %% netkernel:setopts on active connection
+ mf_getopts %% netkernel:getopts on active connection
}).
diff --git a/bootstrap/lib/stdlib/ebin/binary.beam b/bootstrap/lib/stdlib/ebin/binary.beam
index 538d169565..ff9022388e 100644
--- a/bootstrap/lib/stdlib/ebin/binary.beam
+++ b/bootstrap/lib/stdlib/ebin/binary.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/dets.beam b/bootstrap/lib/stdlib/ebin/dets.beam
index 0dfaca7a48..8b9049c855 100644
--- a/bootstrap/lib/stdlib/ebin/dets.beam
+++ b/bootstrap/lib/stdlib/ebin/dets.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/dets_server.beam b/bootstrap/lib/stdlib/ebin/dets_server.beam
index 502eb98318..0b0783bf9d 100644
--- a/bootstrap/lib/stdlib/ebin/dets_server.beam
+++ b/bootstrap/lib/stdlib/ebin/dets_server.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/dets_utils.beam b/bootstrap/lib/stdlib/ebin/dets_utils.beam
index 37247c7176..2a3b22b7e0 100644
--- a/bootstrap/lib/stdlib/ebin/dets_utils.beam
+++ b/bootstrap/lib/stdlib/ebin/dets_utils.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/dets_v8.beam b/bootstrap/lib/stdlib/ebin/dets_v8.beam
index 9a1416a316..39ddeebf85 100644
--- a/bootstrap/lib/stdlib/ebin/dets_v8.beam
+++ b/bootstrap/lib/stdlib/ebin/dets_v8.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/epp.beam b/bootstrap/lib/stdlib/ebin/epp.beam
index 5bcc7cf8d9..b1a75a54eb 100644
--- a/bootstrap/lib/stdlib/ebin/epp.beam
+++ b/bootstrap/lib/stdlib/ebin/epp.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/erl_lint.beam b/bootstrap/lib/stdlib/ebin/erl_lint.beam
index ebe80a32a8..6653cb9f49 100644
--- a/bootstrap/lib/stdlib/ebin/erl_lint.beam
+++ b/bootstrap/lib/stdlib/ebin/erl_lint.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/erl_parse.beam b/bootstrap/lib/stdlib/ebin/erl_parse.beam
index 33a0753965..d75b490162 100644
--- a/bootstrap/lib/stdlib/ebin/erl_parse.beam
+++ b/bootstrap/lib/stdlib/ebin/erl_parse.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/erl_pp.beam b/bootstrap/lib/stdlib/ebin/erl_pp.beam
index e845392e4b..c8798a6094 100644
--- a/bootstrap/lib/stdlib/ebin/erl_pp.beam
+++ b/bootstrap/lib/stdlib/ebin/erl_pp.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/file_sorter.beam b/bootstrap/lib/stdlib/ebin/file_sorter.beam
index 03d9020bb5..693c0b523d 100644
--- a/bootstrap/lib/stdlib/ebin/file_sorter.beam
+++ b/bootstrap/lib/stdlib/ebin/file_sorter.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/gen_fsm.beam b/bootstrap/lib/stdlib/ebin/gen_fsm.beam
index 595574585b..0370d430b2 100644
--- a/bootstrap/lib/stdlib/ebin/gen_fsm.beam
+++ b/bootstrap/lib/stdlib/ebin/gen_fsm.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/gen_server.beam b/bootstrap/lib/stdlib/ebin/gen_server.beam
index 3097207512..aa22d6aae4 100644
--- a/bootstrap/lib/stdlib/ebin/gen_server.beam
+++ b/bootstrap/lib/stdlib/ebin/gen_server.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/log_mf_h.beam b/bootstrap/lib/stdlib/ebin/log_mf_h.beam
index 0c4b22c3df..d0e18955c7 100644
--- a/bootstrap/lib/stdlib/ebin/log_mf_h.beam
+++ b/bootstrap/lib/stdlib/ebin/log_mf_h.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/pool.beam b/bootstrap/lib/stdlib/ebin/pool.beam
index a06ae1717a..cc6e536ccd 100644
--- a/bootstrap/lib/stdlib/ebin/pool.beam
+++ b/bootstrap/lib/stdlib/ebin/pool.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/qlc.beam b/bootstrap/lib/stdlib/ebin/qlc.beam
index fa3240810d..a39ca1391b 100644
--- a/bootstrap/lib/stdlib/ebin/qlc.beam
+++ b/bootstrap/lib/stdlib/ebin/qlc.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/qlc_pt.beam b/bootstrap/lib/stdlib/ebin/qlc_pt.beam
index 4a065ec57f..e9c760ba45 100644
--- a/bootstrap/lib/stdlib/ebin/qlc_pt.beam
+++ b/bootstrap/lib/stdlib/ebin/qlc_pt.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/shell.beam b/bootstrap/lib/stdlib/ebin/shell.beam
index b0c98f4b98..3d7b53387b 100644
--- a/bootstrap/lib/stdlib/ebin/shell.beam
+++ b/bootstrap/lib/stdlib/ebin/shell.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/slave.beam b/bootstrap/lib/stdlib/ebin/slave.beam
index 373d65f198..fa0fa8c960 100644
--- a/bootstrap/lib/stdlib/ebin/slave.beam
+++ b/bootstrap/lib/stdlib/ebin/slave.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/sofs.beam b/bootstrap/lib/stdlib/ebin/sofs.beam
index 5099d4ecc0..7b8ab95876 100644
--- a/bootstrap/lib/stdlib/ebin/sofs.beam
+++ b/bootstrap/lib/stdlib/ebin/sofs.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/stdlib.app b/bootstrap/lib/stdlib/ebin/stdlib.app
index ceb55c66fc..81b30526ab 100644
--- a/bootstrap/lib/stdlib/ebin/stdlib.app
+++ b/bootstrap/lib/stdlib/ebin/stdlib.app
@@ -20,7 +20,7 @@
%%
{application, stdlib,
[{description, "ERTS CXC 138 10"},
- {vsn, "3.0"},
+ {vsn, "3.0.1"},
{modules, [array,
base64,
beam_lib,
diff --git a/bootstrap/lib/stdlib/ebin/stdlib.appup b/bootstrap/lib/stdlib/ebin/stdlib.appup
index 737486d3c5..322a329931 100644
--- a/bootstrap/lib/stdlib/ebin/stdlib.appup
+++ b/bootstrap/lib/stdlib/ebin/stdlib.appup
@@ -16,7 +16,7 @@
%% limitations under the License.
%%
%% %CopyrightEnd%
-{"3.0",
+{"3.0.1",
%% Up from - max one major revision back
[{<<"3\\.0(\\.[0-9]+)*">>,[restart_new_emulator]}, % OTP-19.*
{<<"2\\.[5-8](\\.[0-9]+)*">>,[restart_new_emulator]}], % OTP-18.*
diff --git a/bootstrap/lib/stdlib/ebin/supervisor.beam b/bootstrap/lib/stdlib/ebin/supervisor.beam
index 3ae911ab29..c7534b5438 100644
--- a/bootstrap/lib/stdlib/ebin/supervisor.beam
+++ b/bootstrap/lib/stdlib/ebin/supervisor.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/supervisor_bridge.beam b/bootstrap/lib/stdlib/ebin/supervisor_bridge.beam
index d2bfe6eea8..f52d5c407b 100644
--- a/bootstrap/lib/stdlib/ebin/supervisor_bridge.beam
+++ b/bootstrap/lib/stdlib/ebin/supervisor_bridge.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/zip.beam b/bootstrap/lib/stdlib/ebin/zip.beam
index 6ef3732965..919166fe89 100644
--- a/bootstrap/lib/stdlib/ebin/zip.beam
+++ b/bootstrap/lib/stdlib/ebin/zip.beam
Binary files differ
diff --git a/configure.in b/configure.in
index 8a7f372a50..6db83124be 100644
--- a/configure.in
+++ b/configure.in
@@ -105,7 +105,6 @@ else
fi
AC_SUBST(CROSS_COMPILING)
-
AC_ARG_ENABLE(bootstrap-only,
AS_HELP_STRING([--enable-bootstrap-only],
[enable bootstrap only configuration]),
@@ -129,6 +128,13 @@ AC_PROG_CC
AC_PROG_CXX
AC_CHECK_TOOL(LD, [ld])
+_search_path=/bin:/usr/bin:/usr/local/bin:$PATH
+
+AC_PATH_PROG(ENV, [env], false, $_search_path)
+if test "$ac_cv_path_ENV" = false; then
+ AC_MSG_ERROR([No 'env' command found])
+fi
+
#
# We need GNU make, complain if we can't find it
#
@@ -361,6 +367,49 @@ if test X${enable_native_libs} = Xyes -a X${enable_hipe} != Xno; then
fi
AC_SUBST(NATIVE_LIBS_ENABLED)
+if test $CROSS_COMPILING = no; then
+ case $host_os in
+ darwin*)
+ macosx_version=`sw_vers -productVersion`
+ test $? -eq 0 || {
+ AC_MSG_ERROR([Failed to execute 'sw_vers'; please provide it in PATH])
+ }
+ [case "$macosx_version" in
+ [1-9][0-9].[0-9])
+ int_macosx_version=`echo $macosx_version | sed 's|\([^\.]*\)\.\([^\.]*\)|\1\2|'`;;
+ [1-9][0-9].[0-9].[0-9])
+ int_macosx_version=`echo $macosx_version | sed 's|\([^\.]*\)\.\([^\.]*\)\.\([^\.]*\)|\1\2\3|'`;;
+ [1-9][0-9].[1-9][0-9])
+ int_macosx_version=`echo $macosx_version | sed 's|\([^\.]*\)\.\([^\.]*\)|\1\200|'`;;
+ [1-9][0-9].[1-9][0-9].[0-9])
+ int_macosx_version=`echo $macosx_version | sed 's|\([^\.]*\)\.\([^\.]*\)\.\([^\.]*\)|\1\20\3|'`;;
+ [1-9][0-9].[1-9][0-9].[1-9][0-9])
+ int_macosx_version=`echo $macosx_version | sed 's|\([^\.]*\)\.\([^\.]*\)\.\([^\.]*\)|\1\2\3|'`;;
+ *)
+ int_macosx_version=unexpected;;
+ esac]
+ test $int_macosx_version != unexpected || {
+ AC_MSG_ERROR([Unexpected MacOSX version ($macosx_version) returned by 'sw_vers -productVersion'; this configure script probably needs to be updated])
+ }
+ AC_TRY_COMPILE([
+#if __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ > $int_macosx_version
+#error Compiling for a newer MacOSX version...
+#endif
+ ], [;],
+ [],
+ [AC_MSG_ERROR([
+
+ You are natively building Erlang/OTP for a later version of MacOSX
+ than current version ($macosx_version). You either need to
+ cross-build Erlang/OTP, or set the environment variable
+ MACOSX_DEPLOYMENT_TARGET to $macosx_version (or a lower version).
+
+])])
+ ;;
+ *)
+ ;;
+ esac
+fi
rm -f $ERL_TOP/lib/SKIP-APPLICATIONS
for app in `cd lib && ls -d *`; do
diff --git a/erts/aclocal.m4 b/erts/aclocal.m4
index 013bfe5652..6c0544da31 100644
--- a/erts/aclocal.m4
+++ b/erts/aclocal.m4
@@ -736,10 +736,23 @@ AC_DEFUN(ERL_MONOTONIC_CLOCK,
;;
esac
+ clock_gettime_lib=""
+ AC_CHECK_LIB(rt, clock_gettime, [clock_gettime_lib="-lrt"])
+
+ save_LIBS="$LIBS"
+ LIBS="$LIBS $clock_gettime_lib"
+
+ if test "$LD_MAY_BE_WEAK" != "no"; then
+ trust_test="#error May not be there due to weak linking"
+ else
+ trust_test=""
+ fi
+
AC_CACHE_CHECK([for clock_gettime(CLOCK_MONOTONIC_RAW, _)], erl_cv_clock_gettime_monotonic_raw,
[
- AC_TRY_COMPILE([
+ AC_TRY_LINK([
#include <time.h>
+$trust_test
],
[
struct timespec ts;
@@ -755,8 +768,9 @@ AC_DEFUN(ERL_MONOTONIC_CLOCK,
AC_CACHE_CHECK([for clock_gettime() with ${check_msg}monotonic clock type], erl_cv_clock_gettime_monotonic_$1,
[
for clock_type in $prefer_resolution_clock_gettime_monotonic $default_resolution_clock_gettime_monotonic $high_resolution_clock_gettime_monotonic $low_resolution_clock_gettime_monotonic; do
- AC_TRY_COMPILE([
+ AC_TRY_LINK([
#include <time.h>
+$trust_test
],
[
struct timespec ts;
@@ -771,7 +785,15 @@ AC_DEFUN(ERL_MONOTONIC_CLOCK,
done
])
- AC_CHECK_FUNCS([clock_getres clock_get_attributes gethrtime])
+ LIBS="$save_LIBS"
+
+ if test "$LD_MAY_BE_WEAK" != "no"; then
+ check_for_clock_getres=
+ else
+ check_for_clock_getres=clock_getres
+ fi
+
+ AC_CHECK_FUNCS([$check_for_clock_getres clock_get_attributes gethrtime])
AC_CACHE_CHECK([for mach clock_get_time() with monotonic clock type], erl_cv_mach_clock_get_time_monotonic,
[
@@ -840,7 +862,7 @@ AC_DEFUN(ERL_MONOTONIC_CLOCK,
break
fi
done
- AC_CHECK_LIB(rt, clock_gettime, [erl_monotonic_clock_lib="-lrt"])
+ erl_monotonic_clock_lib=$clock_gettime_lib
;;
mach_clock_get_time)
erl_monotonic_clock_id=SYSTEM_CLOCK
@@ -879,11 +901,24 @@ AC_DEFUN(ERL_WALL_CLOCK,
;;
esac
+ clock_gettime_lib=""
+ AC_CHECK_LIB(rt, clock_gettime, [clock_gettime_lib="-lrt"])
+
+ save_LIBS="$LIBS"
+ LIBS="$LIBS $clock_gettime_lib"
+
+ if test "$LD_MAY_BE_WEAK" != "no"; then
+ trust_test="#error May not be there due to weak linking"
+ else
+ trust_test=""
+ fi
+
AC_CACHE_CHECK([for clock_gettime() with ${check_msg}wall clock type], erl_cv_clock_gettime_wall_$1,
[
for clock_type in $prefer_resolution_clock_gettime_wall $default_resolution_clock_gettime_wall $high_resolution_clock_gettime_wall $low_resolution_clock_gettime_wall; do
- AC_TRY_COMPILE([
+ AC_TRY_LINK([
#include <time.h>
+$trust_test
],
[
struct timespec ts;
@@ -898,7 +933,15 @@ AC_DEFUN(ERL_WALL_CLOCK,
done
])
- AC_CHECK_FUNCS([clock_getres clock_get_attributes gettimeofday])
+ LIBS="$save_LIBS"
+
+ if test "$LD_MAY_BE_WEAK" != "no"; then
+ check_for_clock_getres=
+ else
+ check_for_clock_getres=clock_getres
+ fi
+
+ AC_CHECK_FUNCS([$check_for_clock_getres clock_get_attributes gettimeofday])
AC_CACHE_CHECK([for mach clock_get_time() with wall clock type], erl_cv_mach_clock_get_time_wall,
[
@@ -919,6 +962,7 @@ AC_DEFUN(ERL_WALL_CLOCK,
erl_cv_mach_clock_get_time_wall=no)
])
+ erl_wall_clock_lib=
erl_wall_clock_low_resolution=no
erl_wall_clock_id=
case $1-$erl_cv_clock_gettime_wall_$1-$erl_cv_mach_clock_get_time_wall-$ac_cv_func_gettimeofday-$host_os in
@@ -932,6 +976,7 @@ AC_DEFUN(ERL_WALL_CLOCK,
;;
*-CLOCK_*-*-*-*)
erl_wall_clock_func=clock_gettime
+ erl_wall_clock_lib=$clock_gettime_lib
erl_wall_clock_id=$erl_cv_clock_gettime_wall_$1
for low_res_id in $low_resolution_clock_gettime_wall; do
if test $erl_wall_clock_id = $low_res_id; then
@@ -1680,7 +1725,7 @@ case "$THR_LIB_NAME" in
AC_DEFINE(ETHR_HAVE_SCHED_YIELD, 1, [Define if you have the sched_yield() function.])
AC_MSG_CHECKING([whether sched_yield() returns an int])
sched_yield_ret_int=no
- AC_TRY_COMPILE([
+ AC_TRY_LINK([
#ifdef ETHR_HAVE_SCHED_H
#include <sched.h>
#endif
@@ -1699,7 +1744,7 @@ case "$THR_LIB_NAME" in
AC_DEFINE(ETHR_HAVE_PTHREAD_YIELD, 1, [Define if you have the pthread_yield() function.])
AC_MSG_CHECKING([whether pthread_yield() returns an int])
pthread_yield_ret_int=no
- AC_TRY_COMPILE([
+ AC_TRY_LINK([
#if defined(ETHR_NEED_NPTL_PTHREAD_H)
#include <nptl/pthread.h>
#elif defined(ETHR_HAVE_MIT_PTHREAD_H)
@@ -2436,7 +2481,13 @@ if test $erl_monotonic_clock_low_resolution = yes; then
AC_DEFINE(ERTS_HAVE_LOW_RESOLUTION_OS_MONOTONIC_LOW, [1], [Define if you have a low resolution OS monotonic clock])
fi
-xrtlib="$erl_monotonic_clock_lib"
+xrtlib=
+if test "$erl_monotonic_clock_lib" != ""; then
+ xrtlib="$erl_monotonic_clock_lib"
+fi
+if test "$erl_wall_clock_lib" != ""; then
+ xrtlib="$erl_wall_clock_lib"
+fi
if test "x$erl_monotonic_clock_id" != "x"; then
AC_DEFINE_UNQUOTED(MONOTONIC_CLOCK_ID_STR, ["$erl_monotonic_clock_id"], [Define as a string of monotonic clock id to use])
AC_DEFINE_UNQUOTED(MONOTONIC_CLOCK_ID, [$erl_monotonic_clock_id], [Define to monotonic clock id to use])
diff --git a/erts/configure.in b/erts/configure.in
index c5b08547cb..69b9af3c8c 100644
--- a/erts/configure.in
+++ b/erts/configure.in
@@ -121,6 +121,7 @@ AS_HELP_STRING([--enable-bootstrap-only],
enable_hipe=no
enable_sctp=no
enable_dirty_schedulers=no
+ enable_new_purge=no
fi
])
@@ -139,6 +140,13 @@ AS_HELP_STRING([--enable-dirty-schedulers], [enable dirty scheduler support]),
*) enable_dirty_schedulers=yes ;;
esac ], enable_dirty_schedulers=default)
+AC_ARG_ENABLE(new-purge-strategy,
+AS_HELP_STRING([--enable-new-purge-strategy], [enable new code purge strategy]),
+[ case "$enableval" in
+ no) enable_new_purge=no ;;
+ *) enable_new_purge=yes ;;
+ esac ], enable_new_purge=default)
+
AC_ARG_ENABLE(smp-support,
AS_HELP_STRING([--enable-smp-support], [enable smp support])
AS_HELP_STRING([--disable-smp-support], [disable smp support]),
@@ -738,6 +746,11 @@ if test "$ac_cv_path_MKDIR" = false; then
AC_MSG_ERROR([No 'mkdir' command found])
fi
+AC_PATH_PROG(CP, cp, false, $_search_path)
+if test "$ac_cv_path_CP" = false; then
+ AC_MSG_ERROR([No 'cp' command found])
+fi
+
_search_path=
@@ -862,6 +875,22 @@ dnl for now that is the way we do it.
USER_LD=$LD
USER_LDFLAGS="$LDFLAGS"
LD='$(CC)'
+case $host_os in
+ darwin*)
+ saved_LDFLAGS="$LDFLAGS"
+ LDFLAGS="$LDFLAGS -Wl,-no_weak_imports"
+ AC_TRY_LINK([],[],
+ [
+ LD_MAY_BE_WEAK=no
+ ],
+ [
+ LD_MAY_BE_WEAK=yes
+ LDFLAGS="$saved_LDFLAGS"
+ ]);;
+ *)
+ LD_MAY_BE_WEAK=no;;
+esac
+
AC_SUBST(LD)
LDFLAG_RUNTIME_LIBRARY_PATH="$CFLAG_RUNTIME_LIBRARY_PATH"
@@ -1023,6 +1052,27 @@ esac
AC_MSG_RESULT($DIRTY_SCHEDULER_SUPPORT)
AC_SUBST(DIRTY_SCHEDULER_SUPPORT)
+AC_MSG_CHECKING(whether the new code purge strategy should be enabled)
+case $enable_new_purge-$enable_dirty_schedulers in
+ yes-*)
+ AC_MSG_RESULT(yes)
+ enable_new_purge=yes;;
+ default-yes)
+ AC_MSG_RESULT(yes; forced by dirty scheduler support)
+ enable_new_purge=yes;;
+ no-yes)
+ AC_MSG_ERROR([Dirty schedulers enabled, but new code purge strategy disabled]);;
+ *)
+ AC_MSG_RESULT(no)
+ enable_new_purge=no;;
+esac
+
+if test $enable_new_purge = yes; then
+ AC_DEFINE(ERTS_NEW_PURGE_STRATEGY, 1, [Define if you want to use the new code purge strategy])
+fi
+NEW_PURGE_STRATEGY=$enable_new_purge
+AC_SUBST(NEW_PURGE_STRATEGY)
+
if test $ERTS_BUILD_SMP_EMU = yes; then
if test $found_threads = no; then
@@ -1930,7 +1980,7 @@ getaddrinfo("","",NULL,NULL);
if test $have_getaddrinfo = yes; then
AC_MSG_RESULT([yes])
AC_MSG_CHECKING([whether getaddrinfo accepts enough flags])
- AC_TRY_COMPILE([
+ AC_TRY_LINK([
#include <stdlib.h>
#include <string.h>
#ifdef HAVE_WINSOCK2_H
@@ -2045,7 +2095,7 @@ int main(void) {
fi]);;
esac
-if test $have_posix_memalign = yes; then
+if test "$have_posix_memalign" = "yes"; then
AC_DEFINE(HAVE_POSIX_MEMALIGN,[1],
[Define to 1 if you have the `posix_memalign' function.])
fi
@@ -2187,7 +2237,7 @@ dnl Checks for features/quirks in the system that affects Erlang.
dnl ----------------------------------------------------------------------
AC_MSG_CHECKING([for sched_getaffinity/sched_setaffinity])
-AC_TRY_COMPILE([#include <sched.h>],
+AC_TRY_LINK([#include <sched.h>],
[
#ifndef CPU_SETSIZE
#error no CPU_SETSIZE
@@ -2210,7 +2260,7 @@ fi
AC_MSG_CHECKING([for pset functionality])
-AC_TRY_COMPILE([#include <sys/pset.h>],
+AC_TRY_LINK([#include <sys/pset.h>],
[
int res;
psetid_t id = PS_MYID;
@@ -2228,7 +2278,7 @@ if test $pset_functionality = yes; then
fi
AC_MSG_CHECKING([for processor_bind functionality])
-AC_TRY_COMPILE([
+AC_TRY_LINK([
#include <sys/types.h>
#include <sys/processor.h>
#include <sys/procset.h>
@@ -2244,7 +2294,7 @@ if test $processor_bind_functionality = yes; then
fi
AC_MSG_CHECKING([for cpuset_getaffinity/cpuset_setaffinity])
-AC_TRY_COMPILE([
+AC_TRY_LINK([
#include <sys/param.h>
#include <sys/cpuset.h>
],
@@ -2443,7 +2493,7 @@ if test "x$ac_cv_func_sbrk" = "xyes"; then
for rtype in $ret_types; do
for atype in $arg_types; do
IFS=$save_ifs
- AC_TRY_COMPILE([#include <sys/types.h>
+ AC_TRY_LINK([#include <sys/types.h>
#include <unistd.h>],
[$rtype sbrk($atype incr);],
[erts_cv_sbrk_ret_arg_types="$rtype,$atype"])
@@ -2480,7 +2530,7 @@ if test $ac_cv_func_brk = yes; then
for rtype in $ret_types; do
for atype in $arg_types; do
IFS=$save_ifs
- AC_TRY_COMPILE([#include <sys/types.h>
+ AC_TRY_LINK([#include <sys/types.h>
#include <unistd.h>],
[$rtype brk($atype endds);],
[erts_cv_brk_ret_arg_types="$rtype,$atype"])
@@ -2764,7 +2814,7 @@ if test X${enable_fp_exceptions} != Xyes ; then
FPE=unreliable
else
- AC_MSG_CHECKING([for unreliable floating point execptions])
+ AC_MSG_CHECKING([for unreliable floating point exceptions])
AC_TRY_RUN([
@@ -3502,7 +3552,7 @@ fi
#
if test $have_kernel_poll = epoll; then
AC_MSG_CHECKING([whether epoll is level triggered])
- AC_TRY_COMPILE([#include <sys/epoll.h>],[
+ AC_TRY_LINK([#include <sys/epoll.h>],[
#ifdef EPOLLET
/* Edge triggered option exist, assume level triggered
is default */
@@ -4817,8 +4867,7 @@ dnl Output the result.
dnl ----------------------------------------------------------------------
dnl Note that the output files are relative to $srcdir
-
-AC_OUTPUT(
+AC_CONFIG_FILES([
emulator/$host/Makefile:emulator/Makefile.in
epmd/src/$host/Makefile:epmd/src/Makefile.in
etc/common/$host/Makefile:etc/common/Makefile.in
@@ -4828,15 +4877,22 @@ AC_OUTPUT(
Makefile:Makefile.in
../make/$host/otp.mk:../make/otp.mk.in
../make/$host/otp_ded.mk:../make/otp_ded.mk.in
+])
+
+AC_CONFIG_FILES([../make/make_emakefile:../make/make_emakefile.in],
+ [chmod +x ../make/make_emakefile])
+
dnl
dnl The ones below should be moved to their respective lib
dnl
+dnl ../lib/ssl/c_src/$host/Makefile:../lib/ssl/c_src/Makefile.in
+AC_CONFIG_FILES([
../lib/ic/c_src/$host/Makefile:../lib/ic/c_src/Makefile.in
../lib/os_mon/c_src/$host/Makefile:../lib/os_mon/c_src/Makefile.in
-dnl ../lib/ssl/c_src/$host/Makefile:../lib/ssl/c_src/Makefile.in
../lib/crypto/c_src/$host/Makefile:../lib/crypto/c_src/Makefile.in
../lib/orber/c_src/$host/Makefile:../lib/orber/c_src/Makefile.in
../lib/runtime_tools/c_src/$host/Makefile:../lib/runtime_tools/c_src/Makefile.in
../lib/tools/c_src/$host/Makefile:../lib/tools/c_src/Makefile.in
- )
+ ])
+AC_OUTPUT
diff --git a/erts/doc/src/absform.xml b/erts/doc/src/absform.xml
index 174ad9c640..ab00d47425 100644
--- a/erts/doc/src/absform.xml
+++ b/erts/doc/src/absform.xml
@@ -41,21 +41,21 @@
<list type="bulleted">
<item><seealso marker="stdlib:epp">
- <c>stdlib:epp(3)</c></seealso></item>
+ <c>epp(3)</c></seealso></item>
<item><seealso marker="stdlib:erl_eval">
- <c>stdlib:erl_eval(3)</c></seealso></item>
+ <c>erl_eval(3)</c></seealso></item>
<item><seealso marker="stdlib:erl_lint">
- <c>stdlib:erl_lint(3)</c></seealso></item>
+ <c>erl_lint(3)</c></seealso></item>
<item><seealso marker="stdlib:erl_parse">
- <c>sdlib:erl_parse(3)</c></seealso></item>
+ <c>erl_parse(3)</c></seealso></item>
<item><seealso marker="stdlib:erl_pp">
- <c>stdlib:erl_pp(3)</c></seealso></item>
+ <c>erl_pp(3)</c></seealso></item>
<item><seealso marker="stdlib:io">
- <c>stdlib:io(3)</c></seealso></item>
+ <c>io(3)</c></seealso></item>
</list>
<p>The functions are also used as input and output for parse transforms, see
- the <seealso marker="compiler:compile"><c>compiler:compile(3)</c></seealso>
+ the <seealso marker="compiler:compile"><c>compile(3)</c></seealso>
module.</p>
<p>We use the function <c>Rep</c> to denote the mapping from an Erlang source
@@ -177,8 +177,8 @@
<title>Representation of Parse Errors and End-of-File</title>
<p>In addition to the representations of forms, the list that represents
a module declaration (as returned by functions in
- <seealso marker="stdlib:epp"><c>stdlib:epp(3)</c></seealso> and
- <seealso marker="stdlib:erl_parse"><c>sdlib:erl_parse(3)</c></seealso>)
+ <seealso marker="stdlib:epp"><c>epp(3)</c></seealso> and
+ <seealso marker="stdlib:erl_parse"><c>erl_parse(3)</c></seealso>)
can contain the following:</p>
<list type="bulleted">
diff --git a/erts/doc/src/alt_dist.xml b/erts/doc/src/alt_dist.xml
index f3e3260230..be969a8267 100644
--- a/erts/doc/src/alt_dist.xml
+++ b/erts/doc/src/alt_dist.xml
@@ -40,7 +40,7 @@
<p>The section is a step-by-step explanation of the
<c><![CDATA[uds_dist]]></c> example application (in the
- <c>Kernel</c> application <c><![CDATA[examples]]></c> directory). The
+ Kernel application <c><![CDATA[examples]]></c> directory). The
<c><![CDATA[uds_dist]]></c> application implements distribution over Unix
domain sockets and is written for the Sun Solaris 2 operating environment.
The mechanisms are however general and apply to any operating system Erlang
@@ -379,15 +379,15 @@
(line 40) can be used to implement an interface similar to
Unix <c><![CDATA[writev]]></c> for output. The Erlang runtime
system could previously not use <c>outputv</c> for the
- distribution, but it can as from <c>ERTS</c> 5.7.2.
- As this driver was written before <c>ERTS</c> 5.7.2 it does
+ distribution, but it can as from ERTS 5.7.2.
+ As this driver was written before ERTS 5.7.2 it does
not use the <c>outputv</c> callback. Using the <c>outputv</c>
callback is preferred, as it reduces copying of data. (We
will however use scatter/gather I/O internally in the driver.)</p>
- <p>As from <c>ERTS</c> 5.5.3 the driver interface was extended with
+ <p>As from ERTS 5.5.3 the driver interface was extended with
version control and the possibility to pass capability information.
- Capability flags are present on line 48. As from <c>ERTS</c> 5.7.4 flag
+ Capability flags are present on line 48. As from ERTS 5.7.4 flag
<seealso marker="driver_entry#driver_flags">
<c>ERL_DRV_FLAG_SOFT_BUSY</c></seealso> is required for drivers that
are to be used by the distribution. The soft busy flag implies that the
diff --git a/erts/doc/src/communication.xml b/erts/doc/src/communication.xml
index 632ca6c212..7e18a73aa8 100644
--- a/erts/doc/src/communication.xml
+++ b/erts/doc/src/communication.xml
@@ -89,9 +89,9 @@
<p>Examples of major implementation changes:</p>
<list type="bulleted">
- <item>As from <c>ERTS</c> 5.5.2 exit signals to processes are truly
+ <item>As from ERTS 5.5.2 exit signals to processes are truly
asynchronously delivered.</item>
- <item>As from <c>ERTS</c> 5.10 all signals from processes to ports
+ <item>As from ERTS 5.10 all signals from processes to ports
are truly asynchronously delivered.</item>
</list>
</section>
diff --git a/erts/doc/src/crash_dump.xml b/erts/doc/src/crash_dump.xml
index d55ab222a7..a9aeb1888c 100644
--- a/erts/doc/src/crash_dump.xml
+++ b/erts/doc/src/crash_dump.xml
@@ -39,7 +39,7 @@
<p>The Erlang crash dump had a major facelift in Erlang/OTP R9C. The
information in this section is therefore not directly applicable for
older dumps. However, if you use <seealso marker="observer:crashdump_viewer">
- <c>observer:crashdump_viewer(3)</c></seealso> on older dumps,
+ <c>crashdump_viewer(3)</c></seealso> on older dumps,
the crash dumps are translated into a format similar to this.</p>
</note>
@@ -57,7 +57,7 @@
cause is external limitations, such as running out of memory. A
crash dump caused by an internal error can be caused by the system
reaching limits in the emulator itself (like the number of atoms
- in the system, or too many simultaneous <c>ets</c> tables). Usually the
+ in the system, or too many simultaneous ETS tables). Usually the
emulator or the operating system can be reconfigured to avoid the
crash, which is why interpreting the crash dump correctly is
important.</p>
@@ -133,7 +133,7 @@ Slogan: &lt;reason&gt;</pre>
&lt;Name&gt;:&lt;Name&gt;/1 <c><![CDATA[|]]></c> No function
&lt;Name&gt;:start/2</em></tag>
<item>
- <p>The <c>Kernel</c>/<c>STDLIB</c> applications are
+ <p>The Kernel/STDLIB applications are
damaged or the start script is damaged.</p>
</item>
<tag><em>Driver_select called with too large file descriptor
@@ -199,7 +199,7 @@ Slogan: &lt;reason&gt;</pre>
are still connected to an application failure. There is much
more information available, so a thorough reading of the
crash dump can reveal the crash reason. The size of processes,
- the number of <c>ets</c> tables, and the Erlang data on each process
+ the number of ETS tables, and the Erlang data on each process
stack can be useful to find the problem.</p>
</section>
@@ -481,7 +481,7 @@ Slogan: &lt;reason&gt;</pre>
<section>
<marker id="ets_tables"></marker>
<title>ETS Tables</title>
- <p>This section contains information about all the <c>ets</c> tables in
+ <p>This section contains information about all the ETS tables in
the system. The following fields are of interest for each table:</p>
<taglist>
diff --git a/erts/doc/src/driver_entry.xml b/erts/doc/src/driver_entry.xml
index dea003f091..2421e0a8d9 100644
--- a/erts/doc/src/driver_entry.xml
+++ b/erts/doc/src/driver_entry.xml
@@ -4,7 +4,7 @@
<cref>
<header>
<copyright>
- <year>2001</year><year>2015</year>
+ <year>2001</year><year>2016</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -67,7 +67,7 @@
</list>
</warning>
- <p>As from <c>ERTS</c> 5.9 (Erlang/OTP R15B) the driver interface
+ <p>As from ERTS 5.9 (Erlang/OTP R15B) the driver interface
has been changed with larger types for the callbacks
<seealso marker="#output"><c>output</c></seealso>,
<seealso marker="#control"><c>control</c></seealso>, and
@@ -78,7 +78,7 @@
<note>
<p>Old drivers (compiled with an <c>erl_driver.h</c> from an
- <c>ERTS</c> version earlier than 5.9) must be updated and have
+ ERTS version earlier than 5.9) must be updated and have
to use the extended interface (with
<seealso marker="erl_driver#version_management">version management
</seealso>).</p>
@@ -481,7 +481,7 @@ typedef struct erl_drv_entry {
although a driver instance has marked itself as busy (see
<seealso marker="erl_driver#set_busy_port">
<c>erl_driver:set_busy_port</c></seealso>).
- As from <c>ERTS</c> 5.7.4 this flag is required for drivers used
+ As from ERTS 5.7.4 this flag is required for drivers used
by the Erlang distribution (the behavior has always been
required by drivers used by the distribution).</p>
</item>
@@ -558,7 +558,7 @@ typedef struct erl_drv_entry {
<title>See Also</title>
<p><seealso marker="erl_driver"><c>erl_driver(3)</c></seealso>,
<seealso marker="erlang"><c>erlang(3)</c></seealso>,
- <seealso marker="kernel:erl_ddll"><c>kernel:erl_ddll(3)</c></seealso></p>
+ <seealso marker="kernel:erl_ddll"><c>erl_ddll(3)</c></seealso></p>
</section>
</cref>
diff --git a/erts/doc/src/epmd.xml b/erts/doc/src/epmd.xml
index 120ffb6860..311483022d 100644
--- a/erts/doc/src/epmd.xml
+++ b/erts/doc/src/epmd.xml
@@ -34,24 +34,23 @@
</header>
<com>epmd</com>
- <comsummary>
- <p>Erlang Port Mapper Daemon</p>
+ <comsummary>Erlang Port Mapper Daemon</comsummary>
+
+ <description>
<taglist>
<tag><c><![CDATA[epmd [-d|-debug] [DbgExtra...] [-address Addresses]
- [-port No] [-daemon] [-relaxed_command_check]]]></c></tag>
+ [-port No] [-daemon] [-relaxed_command_check]]]></c></tag>
<item>
<p>Starts the port mapper daemon.</p>
</item>
<tag><c><![CDATA[epmd [-d|-debug] [-port No]
- [-names|-kill|-stop Name]]]></c></tag>
+ [-names|-kill|-stop Name]]]></c></tag>
<item>
<p>Communicates with a running port mapper daemon.</p>
</item>
</taglist>
- </comsummary>
- <description>
<p>This daemon acts as a name server on all hosts involved in
distributed Erlang computations. When an Erlang node starts,
the node has a name and it obtains an address from the host
diff --git a/erts/doc/src/erl.xml b/erts/doc/src/erl.xml
index 1578d40ef8..eb1d24cf12 100644
--- a/erts/doc/src/erl.xml
+++ b/erts/doc/src/erl.xml
@@ -46,7 +46,7 @@
you want to redirect standard input or output.</p>
<note>
- <p>As from <c>ERTS</c> 5.9 (Erlang/OTP R15B) the runtime system does by
+ <p>As from ERTS 5.9 (Erlang/OTP R15B) the runtime system does by
default <em>not</em> bind schedulers to logical processors.
For more information, see system flag
<seealso marker="#+sbt"><c>+sbt</c></seealso>.</p>
@@ -103,7 +103,7 @@
emulator flags. <c><![CDATA[-s my_init]]></c> is an init flag,
interpreted by <c><![CDATA[init]]></c>.
<c><![CDATA[-sname arnie]]></c> is a user flag, stored by
- <c><![CDATA[init]]></c>. It is read by <c>Kernel</c> and causes the
+ <c><![CDATA[init]]></c>. It is read by Kernel and causes the
Erlang runtime system to become distributed. Finally, everything after
<c><![CDATA[-extra]]></c> (that is, <c><![CDATA[+bertie]]></c>) is
considered as plain arguments.</p>
@@ -143,9 +143,9 @@
<p>Sets the application configuration parameter <c><![CDATA[Par]]></c>
to the value <c><![CDATA[Val]]></c> for the application
<c><![CDATA[Application]]></c>; see
- <seealso marker="kernel:app"><c>kernel:app(4)</c></seealso> and
+ <seealso marker="kernel:app"><c>app(4)</c></seealso> and
<seealso marker="kernel:application">
- <c>kernel:application(3)</c></seealso>.</p>
+ <c>application(3)</c></seealso>.</p>
</item>
<tag><marker id="args_file"/><c><![CDATA[-args_file FileName]]></c></tag>
<item>
@@ -192,12 +192,12 @@
<c><![CDATA[Dir]]></c>. Used when applications are installed in
another directory than <c><![CDATA[$ROOT/lib]]></c>; see
<seealso marker="sasl:systools#make_script/1">
- <c>systools:make_script/1,2</c></seealso> in <c>SASL</c>.</p>
+ <c>systools:make_script/1,2</c></seealso> in SASL.</p>
</item>
<tag><c><![CDATA[-code_path_cache]]></c></tag>
<item>
<p>Enables the code path cache of the code server; see
- <seealso marker="kernel:code"><c>kernel:code(3)</c></seealso>.</p>
+ <seealso marker="kernel:code"><c>code(3)</c></seealso>.</p>
</item>
<tag><c><![CDATA[-compile Mod1 Mod2 ...]]></c></tag>
<item>
@@ -212,16 +212,16 @@
<p>Specifies the name of a configuration file,
<c><![CDATA[Config.config]]></c>, which is used to configure
applications; see
- <seealso marker="kernel:app"><c>kernel:app(4)</c></seealso> and
+ <seealso marker="kernel:app"><c>app(4)</c></seealso> and
<seealso marker="kernel:application">
- <c>kernel:application(3)</c></seealso>.</p>
+ <c>application(3)</c></seealso>.</p>
</item>
<tag><marker id="connect_all"/><c><![CDATA[-connect_all false]]></c></tag>
<item>
<p>If this flag is present, <c><![CDATA[global]]></c> does not maintain
a fully connected network of distributed Erlang nodes, and then
global name registration cannot be used; see
- <seealso marker="kernel:global"><c>kernel:global(3)</c></seealso>.</p>
+ <seealso marker="kernel:global"><c>global(3)</c></seealso>.</p>
</item>
<tag><c><![CDATA[-cookie Cookie]]></c></tag>
<item>
@@ -266,7 +266,7 @@
<item>
<p>Starts heartbeat monitoring of the Erlang runtime system;
see <seealso marker="kernel:heart">
- <c>kernel:heart(3)</c></seealso>.</p>
+ <c>heart(3)</c></seealso>.</p>
</item>
<tag><c><![CDATA[-hidden]]></c></tag>
<item>
@@ -278,13 +278,13 @@
nodes are part of the result from <c><![CDATA[nodes/0]]></c> on the
other node. See also hidden global groups;
<seealso marker="kernel:global_group">
- <c>kernel:global_group(3)</c></seealso>.</p>
+ <c>global_group(3)</c></seealso>.</p>
</item>
<tag><c><![CDATA[-hosts Hosts]]></c></tag>
<item>
<p>Specifies the IP addresses for the hosts on which Erlang boot servers
are running, see <seealso marker="kernel:erl_boot_server">
- <c>kernel:erl_boot_server(3)</c></seealso>. This flag
+ <c>erl_boot_server(3)</c></seealso>. This flag
is mandatory if flag <c><![CDATA[-loader inet]]></c> is present.</p>
<p>The IP addresses must be specified in the standard form (four
decimal numbers separated by periods, for example,
@@ -338,7 +338,7 @@
<item>
<p>Makes the Erlang runtime system invoke <c><![CDATA[make:all()]]></c>
in the current working directory and then terminate; see
- <seealso marker="tools:make"><c>tools:make(3)</c></seealso>. Implies
+ <seealso marker="tools:make"><c>make(3)</c></seealso>. Implies
<c><![CDATA[-noinput]]></c>.</p>
</item>
<tag><c><![CDATA[-man Module]]></c></tag>
@@ -351,7 +351,7 @@
<p>Indicates if the system is to load code dynamically
(<c><![CDATA[interactive]]></c>), or if all code is to be loaded
during system initialization (<c><![CDATA[embedded]]></c>); see
- <seealso marker="kernel:code"><c>kernel:code(3)</c></seealso>.
+ <seealso marker="kernel:code"><c>code(3)</c></seealso>.
Defaults to <c><![CDATA[interactive]]></c>.</p>
</item>
<tag><c><![CDATA[-name Name]]></c></tag>
@@ -359,7 +359,7 @@
<p>Makes the Erlang runtime system into a distributed node.
This flag invokes all network servers necessary for a node to
become distributed; see <seealso marker="kernel:net_kernel">
- <c>kernel:net_kernel(3)</c></seealso>. It is also ensured that
+ <c>net_kernel(3)</c></seealso>. It is also ensured that
<c><![CDATA[epmd]]></c> runs on the current host before Erlang is
started; see <seealso marker="epmd"><c>epmd(1)</c></seealso>.and the
<seealso marker="#start_epmd"><c>-start_epmd</c></seealso> option.</p>
@@ -383,7 +383,7 @@
<item>
<p>Disables the sticky directory facility of the Erlang code
server; see
- <seealso marker="kernel:code"><c>kernel:code(3)</c></seealso>.</p>
+ <seealso marker="kernel:code"><c>code(3)</c></seealso>.</p>
</item>
<tag><c><![CDATA[-oldshell]]></c></tag>
<item>
@@ -393,24 +393,26 @@
<tag><c><![CDATA[-pa Dir1 Dir2 ...]]></c></tag>
<item>
<p>Adds the specified directories to the beginning of the code
- path, similar to <c><![CDATA[code:add_pathsa/1]]></c>; see
- <seealso marker="kernel:code"><c>kernel:code(3)</c></seealso>.
- As an alternative to <c>-pa</c>, if several directories are
+ path, similar to <seealso marker="kernel:code#add_pathsa/1">
+ <c><![CDATA[code:add_pathsa/1]]></c></seealso>. Note that the
+ order of the given directories will be reversed in the
+ resulting path.</p>
+ <p>As an alternative to <c>-pa</c>, if several directories are
to be prepended to the code path and the directories have a
common parent directory, that parent directory can be
specified in environment variable <c>ERL_LIBS</c>; see
- <seealso marker="kernel:code"><c>kernel:code(3)</c></seealso>.</p>
+ <seealso marker="kernel:code"><c>code(3)</c></seealso>.</p>
</item>
<tag><c><![CDATA[-pz Dir1 Dir2 ...]]></c></tag>
<item>
<p>Adds the specified directories to the end of the code path,
similar to <c><![CDATA[code:add_pathsz/1]]></c>; see
- <seealso marker="kernel:code"><c>kernel:code(3)</c></seealso>.</p>
+ <seealso marker="kernel:code"><c>code(3)</c></seealso>.</p>
</item>
<tag><c><![CDATA[-path Dir1 Dir2 ...]]></c></tag>
<item>
<p>Replaces the path specified in the boot script; see
- <seealso marker="sasl:script"><c>sasl:script(4)</c></seealso>.</p>
+ <seealso marker="sasl:script"><c>script(4)</c></seealso>.</p>
</item>
<tag><c><![CDATA[-proto_dist Proto]]></c></tag>
<item>
@@ -436,7 +438,7 @@
<item>
<p>Specifies an alternative to <c><![CDATA[rsh]]></c> for starting a
slave node on a remote host; see
- <seealso marker="stdlib:slave"><c>stdlib:slave(3)</c></seealso>.</p>
+ <seealso marker="stdlib:slave"><c>slave(3)</c></seealso>.</p>
</item>
<tag><c><![CDATA[-run Mod [Func [Arg1, Arg2, ...]]]]></c> (init
flag)</tag>
@@ -612,11 +614,11 @@
</item>
<tag><marker id="+e"/><c><![CDATA[+e Number]]></c></tag>
<item>
- <p>Sets the maximum number of <c>ets</c> tables.</p>
+ <p>Sets the maximum number of ETS tables.</p>
</item>
<tag><c><![CDATA[+ec]]></c></tag>
<item>
- <p>Forces option <c>compressed</c> on all <c>ets</c> tables.
+ <p>Forces option <c>compressed</c> on all ETS tables.
Only intended for test and evaluation.</p>
</item>
<tag><marker id="file_name_encoding"></marker>
@@ -627,11 +629,11 @@
code points &gt; 255.</p>
<p>For more information about Unicode filenames, see section
<seealso marker="stdlib:unicode_usage#unicode_file_names">Unicode
- Filenames</seealso> in the <c>STDLIB</c> User's Guide. Notice that
+ Filenames</seealso> in the STDLIB User's Guide. Notice that
this value also applies to command-line parameters and environment
variables (see section <seealso
marker="stdlib:unicode_usage#unicode_in_environment_and_parameters">
- Unicode in Enviroment and Parameters</seealso> in the <c>STDLIB</c>
+ Unicode in Enviroment and Parameters</seealso> in the STDLIB
User's Guide).</p>
</item>
<tag><c><![CDATA[+fnu[{w|i|e}]]]></c></tag>
@@ -663,11 +665,11 @@
points to an invalid filename.</p>
<p>For more information about Unicode filenames, see section
<seealso marker="stdlib:unicode_usage#unicode_file_names">Unicode
- Filenames</seealso> in the <c>STDLIB</c> User's Guide. Notice that
+ Filenames</seealso> in the STDLIB User's Guide. Notice that
this value also applies to command-line parameters and environment
variables (see section <seealso
marker="stdlib:unicode_usage#unicode_in_environment_and_parameters">
- Unicode in Enviroment and Parameters</seealso> in the <c>STDLIB</c>
+ Unicode in Enviroment and Parameters</seealso> in the STDLIB
User's Guide).</p>
</item>
<tag><c><![CDATA[+fna[{w|i|e}]]]></c></tag>
@@ -684,11 +686,11 @@
selected, then <c>w</c>, <c>i</c>, or <c>e</c> have no effect.</p>
<p>For more information about Unicode filenames, see section
<seealso marker="stdlib:unicode_usage#unicode_file_names">Unicode
- Filenames</seealso> in the <c>STDLIB</c> User's Guide. Notice that
+ Filenames</seealso> in the STDLIB User's Guide. Notice that
this value also applies to command-line parameters and environment
variables (see section <seealso
marker="stdlib:unicode_usage#unicode_in_environment_and_parameters">
- Unicode in Enviroment and Parameters</seealso> in the <c>STDLIB</c>
+ Unicode in Enviroment and Parameters</seealso> in the STDLIB
User's Guide).</p>
</item>
<tag><c><![CDATA[+hms Size]]></c></tag>
@@ -783,7 +785,7 @@
example, your font does not cover all Unicode characters.</item>
</taglist>
<p>See also <seealso marker="stdlib:io#printable_range/0">
- <c>io:printable_range/0</c></seealso> in <c>STDLIB</c>.</p>
+ <c>io:printable_range/0</c></seealso> in STDLIB.</p>
</item>
<tag><marker id="+P"/><marker id="max_processes"/><c><![CDATA[+P Number]]></c></tag>
<item>
@@ -838,7 +840,7 @@
</item>
<tag><c><![CDATA[+r]]></c></tag>
<item>
- <p>Forces <c>ets</c> memory block to be moved on realloc.</p>
+ <p>Forces ETS memory block to be moved on realloc.</p>
</item>
<tag><marker id="+rg"/><c><![CDATA[+rg ReaderGroupsLimit]]></c></tag>
<item>
@@ -1268,7 +1270,7 @@
<item>
<p>Enables or disables eager check I/O scheduling. Defaults
to <c>true</c>. The default was changed from <c>false</c>
- as from <c>ERTS</c> 7.0. The behavior before this
+ as from ERTS 7.0. The behavior before this
flag was introduced corresponds to <c>+secio false</c>.</p>
<p>The flag effects when schedulers will check for I/O
operations possible to execute, and when such I/O operations
@@ -1365,7 +1367,7 @@
<tag><marker id="+sws"/><c>+sws default|legacy</c></tag>
<item>
<p>Sets scheduler wakeup strategy. Default strategy changed in
- <c>ERTS</c> 5.10 (Erlang/OTP R16A). This strategy was known as
+ ERTS 5.10 (Erlang/OTP R16A). This strategy was known as
<c>proposal</c> in Erlang/OTP R15. The <c>legacy</c> strategy
was used as default from R13 up to and including R15.</p>
<note>
@@ -1446,7 +1448,7 @@
The current mapping can be retrieved using
<c><![CDATA[error_logger:warning_map/0]]></c>. For more information,
see <seealso marker="kernel:error_logger#warning_map/0">
- <c>error_logger:warning_map/0</c></seealso> in <c>Kernel</c>.</p>
+ <c>error_logger:warning_map/0</c></seealso> in Kernel.</p>
</item>
<tag><c><![CDATA[+zFlag Value]]></c></tag>
<item>
@@ -1515,7 +1517,7 @@
<c><![CDATA[ERL_CRASH_DUMP_SECONDS=-1]]></c>, the runtime system
waits indefinitely for the crash dump file to be written.</p>
<p>This variable is used with <seealso marker="kernel:heart">
- <c>kernel:heart(3)</c></seealso> if <c>heart</c> is running:</p>
+ <c>heart(3)</c></seealso> if <c>heart</c> is running:</p>
<taglist>
<tag><c><![CDATA[ERL_CRASH_DUMP_SECONDS=0]]></c></tag>
<item>Suppresses the writing a crash dump file entirely, thus
@@ -1558,7 +1560,7 @@
<item>
<p>Contains a list of additional library directories that the code
server searches for applications and adds to the code path; see
- <seealso marker="kernel:code"><c>kernel:code(3)</c></seealso>.</p>
+ <seealso marker="kernel:code"><c>code(3)</c></seealso>.</p>
</item>
<tag><c><![CDATA[ERL_EPMD_ADDRESS]]></c></tag>
<item>
@@ -1631,14 +1633,14 @@ code:load_abs("..../user_default"). ]]></code>
<seealso marker="erts_alloc"><c>erts_alloc(3)</c></seealso>,
<seealso marker="init"><c>init(3)</c></seealso>,
<seealso marker="kernel:application">
- <c>kernel:application(3)</c></seealso>,
- <seealso marker="kernel:auth"><c>kernel:auth(3)</c></seealso>,
- <seealso marker="kernel:code"><c>kernel:code(3)</c></seealso>,
+ <c>application(3)</c></seealso>,
+ <seealso marker="kernel:auth"><c>auth(3)</c></seealso>,
+ <seealso marker="kernel:code"><c>code(3)</c></seealso>,
<seealso marker="kernel:erl_boot_server">
- <c>kernel:erl_boot_server(3)</c></seealso>,
- <seealso marker="kernel:heart"><c>kernel:heart(3)</c></seealso>,
- <seealso marker="kernel:net_kernel"><c>kernel:net_kernel(3)</c></seealso>,
- <seealso marker="tools:make"><c>tools:make(3)</c></seealso></p>
+ <c>erl_boot_server(3)</c></seealso>,
+ <seealso marker="kernel:heart"><c>heart(3)</c></seealso>,
+ <seealso marker="kernel:net_kernel"><c>net_kernel(3)</c></seealso>,
+ <seealso marker="tools:make"><c>make(3)</c></seealso></p>
</section>
</comref>
diff --git a/erts/doc/src/erl_dist_protocol.xml b/erts/doc/src/erl_dist_protocol.xml
index a5949ce15f..ee74983730 100644
--- a/erts/doc/src/erl_dist_protocol.xml
+++ b/erts/doc/src/erl_dist_protocol.xml
@@ -5,7 +5,7 @@
<header>
<copyright>
<year>2007</year>
- <year>2015</year>
+ <year>2016</year>
<holder>Ericsson AB, All Rights Reserved</holder>
</copyright>
<legalnotice>
@@ -48,7 +48,7 @@
</item>
<item>
<p>Authentication (done by <seealso marker="kernel:net_kernel">
- <c>kernel:net_kernel(3)</c></seealso>) (3)</p>
+ <c>net_kernel(3)</c></seealso>) (3)</p>
</item>
<item>
<p>Connected (4)</p>
@@ -573,7 +573,7 @@ io:format("old/unused name ~ts at port ~p, fd = ~p ~n",
<p>Every message in the handshake starts with a 16-bit big-endian integer,
which contains the message length (not counting the two initial bytes).
In Erlang this corresponds to option <c>{packet, 2}</c> in
- <seealso marker="kernel:gen_tcp"><c>kernel:gen_tcp(3)</c></seealso>.
+ <seealso marker="kernel:gen_tcp"><c>gen_tcp(3)</c></seealso>.
Notice that after the handshake, the distribution switches to 4 byte
packet headers.</p>
</section>
@@ -825,7 +825,7 @@ DiB == gen_digest(ChA, ICA)?
<section>
<marker id="connected_nodes"/>
<title>Protocol between Connected Nodes</title>
- <p>As from <c>ERTS</c> 5.7.2 the runtime system passes a distribution flag
+ <p>As from ERTS 5.7.2 the runtime system passes a distribution flag
in the handshake stage that enables the use of a
<seealso marker="erl_ext_dist#distribution_header">distribution header
</seealso> on all messages passed. Messages passed between nodes have in
@@ -869,7 +869,7 @@ DiB == gen_digest(ChA, ICA)?
number is omitted from the terms that follow a distribution header
</seealso>.</p>
- <p>Nodes with an <c>ERTS</c> version earlier than 5.7.2 does not pass the
+ <p>Nodes with an ERTS version earlier than 5.7.2 does not pass the
distribution flag that enables the distribution header. Messages passed
between nodes have in this case the following format:</p>
diff --git a/erts/doc/src/erl_driver.xml b/erts/doc/src/erl_driver.xml
index 8e83b74986..836a58a676 100644
--- a/erts/doc/src/erl_driver.xml
+++ b/erts/doc/src/erl_driver.xml
@@ -71,7 +71,7 @@
</list>
</warning>
- <p>As from <c>ERTS</c> 5.5.3 the driver interface has been extended
+ <p>As from ERTS 5.5.3 the driver interface has been extended
(see <seealso marker="driver_entry#extended_marker">
<c>extended marker</c></seealso>). The extended interface introduces
<seealso marker="#version_management">version management</seealso>,
@@ -81,7 +81,7 @@
initialization, and some new driver API functions.</p>
<note>
- <p>As from <c>ERTS</c> 5.9 old drivers must be recompiled
+ <p>As from ERTS 5.9 old drivers must be recompiled
and use the extended interface. They must also be adjusted to the
<seealso marker="#rewrites_for_64_bits">
64-bit capable driver interface</seealso>.</p>
@@ -406,7 +406,7 @@
<section>
<marker id="rewrites_for_64_bits"/>
<title>Rewrites for 64-Bit Driver Interface</title>
- <p><c>ERTS</c> 5.9 introduced two new integer types,
+ <p>ERTS 5.9 introduced two new integer types,
<seealso marker="#ErlDrvSizeT"><c>ErlDrvSizeT</c></seealso> and
<seealso marker="#ErlDrvSSizeT"><c>ErlDrvSSizeT</c></seealso>,
which can hold 64-bit sizes if necessary.</p>
@@ -423,7 +423,7 @@
to get better warnings. Try to find a similar flag if you use
another compiler.</p>
- <p>The following is a checklist for rewriting a pre <c>ERTS</c> 5.9 driver,
+ <p>The following is a checklist for rewriting a pre ERTS 5.9 driver,
most important first:</p>
<taglist>
@@ -717,7 +717,7 @@ typedef struct ErlDrvBinary {
<p>Notice that as a driver binary is shared by the driver and
the emulator. A binary received from the emulator or sent to
the emulator must not be changed by the driver.</p>
- <p>Since <c>ERTS</c> 5.5 (Erlang/OTP R11B), <c>orig_bytes</c> is
+ <p>Since ERTS 5.5 (Erlang/OTP R11B), <c>orig_bytes</c> is
guaranteed to be properly aligned for storage of an array of
doubles (usually 8-byte aligned).</p>
</item>
@@ -1058,7 +1058,7 @@ r = driver_async(myPort, &myKey, myData, myFunc); ]]></code>
<p>The return value is <c>-1</c> if the <c>driver_async</c> call
fails.</p>
<note>
- <p>As from <c>ERTS</c> 5.5.4.3 the default stack size for
+ <p>As from ERTS 5.5.4.3 the default stack size for
threads in the async-thread pool is 16 kilowords,
that is, 64 kilobyte on 32-bit architectures.
This small default size has been chosen because the
@@ -2534,11 +2534,11 @@ ERL_DRV_MAP int sz</pre>
<p>The unsigned integer data type <c>ErlDrvUInt</c> and the
signed integer data type <c>ErlDrvSInt</c> are 64 bits wide
on a 64-bit runtime system and 32 bits wide on a 32-bit
- runtime system. They were introduced in <c>ERTS</c> 5.6
+ runtime system. They were introduced in ERTS 5.6
and replaced some of the <c>int</c> arguments in the list above.</p>
<p>The unsigned integer data type <c>ErlDrvUInt64</c> and the
signed integer data type <c>ErlDrvSInt64</c> are always 64 bits
- wide. They were introduced in <c>ERTS</c> 5.7.4.</p>
+ wide. They were introduced in ERTS 5.7.4.</p>
<p>To build the tuple <c>{tcp, Port, [100 | Binary]}</c>, the
following call can be made.</p>
<code type="none"><![CDATA[
@@ -2630,7 +2630,7 @@ erl_drv_output_term(driver_mk_port(drvport), spec, sizeof(spec) / sizeof(spec[0]
<c>ERL_DRV_BINARY</c> and the <c>ErlDrvBinary</c> in question.</p>
<p>The <c>ERL_DRV_UINT</c>, <c>ERL_DRV_BUF2BINARY</c>, and
<c>ERL_DRV_EXT2TERM</c> term types were introduced in
- <c>ERTS</c> 5.6.</p>
+ ERTS 5.6.</p>
<p>This function is only thread-safe when the emulator with SMP
support is used.</p>
</desc>
@@ -3213,7 +3213,7 @@ erl_drv_output_term(driver_mk_port(drvport), spec, sizeof(spec) / sizeof(spec[0]
<title>See Also</title>
<p><seealso marker="driver_entry"><c>driver_entry(3)</c></seealso>,
<seealso marker="erlang"><c>erlang(3)</c></seealso>,
- <seealso marker="kernel:erl_ddll"><c>kernel:erl_ddll(3)</c></seealso>,
+ <seealso marker="kernel:erl_ddll"><c>erl_ddll(3)</c></seealso>,
section <seealso marker="alt_dist">How to Implement an Alternative
Carrier for the Erlang Distribution></seealso> in the User's Guide</p>
</section>
diff --git a/erts/doc/src/erl_ext_dist.xml b/erts/doc/src/erl_ext_dist.xml
index fed41098f8..4f799f8f34 100644
--- a/erts/doc/src/erl_ext_dist.xml
+++ b/erts/doc/src/erl_ext_dist.xml
@@ -5,7 +5,7 @@
<header>
<copyright>
<year>2007</year>
- <year>2015</year>
+ <year>2016</year>
<holder>Ericsson AB, All Rights Reserved</holder>
</copyright>
<legalnotice>
@@ -119,7 +119,7 @@
<tcaption>Compressed Data Format when Expanded</tcaption></table>
<marker id="utf8_atoms"/>
<note>
- <p>As from <c>ERTS</c> 5.10 (OTP R16) support
+ <p>As from ERTS 5.10 (OTP R16) support
for UTF-8 encoded atoms has been introduced in the external format.
However, only characters that can be encoded using Latin-1 (ISO-8859-1)
are currently supported in atoms. The support for UTF-8 encoded atoms
@@ -149,9 +149,9 @@
<title>Distribution Header</title>
<p>
<marker id="distribution_header"/>
- As from <c>ERTS</c> 5.7.2 the old atom cache protocol was
+ As from ERTS 5.7.2 the old atom cache protocol was
dropped and a new one was introduced. This protocol
- introduced the distribution header. Nodes with an <c>ERTS</c> version
+ introduced the distribution header. Nodes with an ERTS version
earlier than 5.7.2 can still communicate with new nodes,
but no distribution header and no atom cache are used.</p>
<p>
@@ -799,7 +799,7 @@
</p>
<note>
<p>
- <c>SMALL_ATOM_EXT</c> was introduced in <c>ERTS</c> 5.7.2 and
+ <c>SMALL_ATOM_EXT</c> was introduced in ERTS 5.7.2 and
require an exchange of distribution flag
<seealso marker="erl_dist_protocol#dflags">
<c>DFLAG_SMALL_ATOM_TAGS</c></seealso> in the
diff --git a/erts/doc/src/erl_nif.xml b/erts/doc/src/erl_nif.xml
index 2b627281fe..b5dc9037c4 100644
--- a/erts/doc/src/erl_nif.xml
+++ b/erts/doc/src/erl_nif.xml
@@ -474,7 +474,7 @@ return term;</code>
register/unregister its name, and so on.</p>
<p>Termination of a process executing a dirty NIF can only be
completed up to a certain point while it executes the dirty NIF.
- All Erlang resources, such as its registered name and its <c>ets</c>
+ All Erlang resources, such as its registered name and its ETS
tables, are released. All links and monitors are triggered. The
execution of the NIF is, however, <em>not</em> stopped. The NIF
can safely continue execution, allocate heap memory, and so on,
@@ -488,18 +488,6 @@ return term;</code>
sending process is not alive. Deallocation of certain internal
resources, such as process heap and process control block, is
delayed until the dirty NIF has completed.</p>
- <p>Known issue that are planned to be fixed:</p>
- <list type="bulleted">
- <item>
- <p>As purging of a module might need to garbage
- collect a process to determine if it has
- references to the module, a process executing a dirty
- NIF can delay purging for a very long time. Delaying
- a purge operation implies delaying <em>all</em> code
- loading operations, which can cause severe problems for
- the system as a whole.</p>
- </item>
- </list>
</item>
</taglist>
</item>
@@ -1585,7 +1573,7 @@ typedef enum {
<seealso marker="#enif_raise_exception">
<c>enif_raise_exception</c></seealso>.</p>
<note>
- <p>Before <c>ERTS</c> 7.0 (Erlang/OTP 18), the return value
+ <p>Before ERTS 7.0 (Erlang/OTP 18), the return value
from <c>enif_make_badarg</c> had to be returned from the NIF. This
requirement is now lifted as the return value from the NIF is
ignored if <c>enif_make_badarg</c> has been invoked.</p>
@@ -2573,7 +2561,7 @@ enif_map_iterator_destroy(env, &amp;iter);</code>
thread.</p>
<note>
<p>Passing <c>msg_env</c> as <c>NULL</c> is only supported as from
- <c>ERTS</c> 8.0 (Erlang/OTP 19).</p>
+ ERTS 8.0 (Erlang/OTP 19).</p>
</note>
</desc>
</func>
diff --git a/erts/doc/src/erl_prim_loader.xml b/erts/doc/src/erl_prim_loader.xml
index 86a3b98eda..286bac6c93 100644
--- a/erts/doc/src/erl_prim_loader.xml
+++ b/erts/doc/src/erl_prim_loader.xml
@@ -60,7 +60,7 @@
for example,
<c>$OTPROOT/lib/</c><c>mnesia-4.4.7.ez/mnesia-4.4.7/ebin/</c><c>mnesia.beam</c>.
For information about archive files, see
- <seealso marker="kernel:code"><c>kernel:code(3)</c></seealso>.</p>
+ <seealso marker="kernel:code"><c>code(3)</c></seealso>.</p>
</desc>
</func>
@@ -87,7 +87,7 @@
for example,
<c>$OTPROOT/lib/</c><c>mnesia-4.4.7.ez/mnesia-4.4.7/ebin</c>.
For information about archive files, see
- <seealso marker="kernel:code"><c>kernel:code(3)</c></seealso>.</p>
+ <seealso marker="kernel:code"><c>code(3)</c></seealso>.</p>
</desc>
</func>
@@ -98,18 +98,18 @@
<p>Retrieves information about a file. Returns
<c>{ok, <anno>FileInfo</anno>}</c> if successful, otherwise
<c>error</c>. <c><anno>FileInfo</anno></c> is a record
- <c>file_info</c>, defined in the <c>Kernel</c> include file
+ <c>file_info</c>, defined in the Kernel include file
<c>file.hrl</c>. Include the following directive in the module
from which the function is called:</p>
<code type="none">
-include_lib("kernel/include/file.hrl").</code>
<p>For more information about the record <c>file_info</c>, see
- <seealso marker="kernel:file"><c>kernel:file(3)</c></seealso>.</p>
+ <seealso marker="kernel:file"><c>file(3)</c></seealso>.</p>
<p><c><anno>Filename</anno></c> can also be a file in an archive,
for example,
<c>$OTPROOT/lib/</c><c>mnesia-4.4.7.ez/mnesia-4.4.7/ebin/</c><c>mnesia</c>.
For information about archive files, see
- <seealso marker="kernel:code"><c>kernel:code(3)</c></seealso>.</p>
+ <seealso marker="kernel:code"><c>code(3)</c></seealso>.</p>
</desc>
</func>
@@ -167,7 +167,7 @@
can use. This flag is mandatory if flag <c>-loader inet</c>
is present. On each host, there must be on Erlang node
with the <seealso marker="kernel:erl_boot_server">
- <c>kernel:erl_boot_server(3)</c></seealso>,
+ <c>erl_boot_server(3)</c></seealso>,
which handles the load requests.
<c>Hosts</c> is a list of IP addresses (hostnames
are not acceptable).</p>
@@ -184,7 +184,7 @@
<title>See Also</title>
<p><seealso marker="init"><c>init(3)</c></seealso>,
<seealso marker="kernel:erl_boot_server">
- <c>kernel:erl_boot_server(3)</c></seealso></p>
+ <c>erl_boot_server(3)</c></seealso></p>
</section>
</erlref>
diff --git a/erts/doc/src/erl_tracer.xml b/erts/doc/src/erl_tracer.xml
index 131157eef8..83eef374ca 100644
--- a/erts/doc/src/erl_tracer.xml
+++ b/erts/doc/src/erl_tracer.xml
@@ -434,7 +434,7 @@
specified.</p>
<p>For more information on what <c>Label</c> and <c>SeqTraceInfo</c>
can be, see <seealso marker="kernel:seq_trace">
- <c>kernel:seq_trace(3)</c></seealso>.</p>
+ <c>seq_trace(3)</c></seealso>.</p>
</desc>
</func>
diff --git a/erts/doc/src/erlang.xml b/erts/doc/src/erlang.xml
index 2b7a3b85e5..b0d25389fd 100644
--- a/erts/doc/src/erlang.xml
+++ b/erts/doc/src/erlang.xml
@@ -84,22 +84,22 @@
<p>Time unit expressed in parts per second. That is,
the time unit equals <c>1/PartsPerSecond</c> second.</p>
</item>
- <tag><c>seconds</c></tag>
+ <tag><c>second</c></tag>
<item>
<p>Symbolic representation of the time unit
represented by the integer <c>1</c>.</p>
</item>
- <tag><c>milli_seconds</c></tag>
+ <tag><c>millisecond</c></tag>
<item>
<p>Symbolic representation of the time unit
represented by the integer <c>1000</c>.</p>
</item>
- <tag><c>micro_seconds</c></tag>
+ <tag><c>microsecond</c></tag>
<item>
<p>Symbolic representation of the time unit
represented by the integer <c>1000000</c>.</p>
</item>
- <tag><c>nano_seconds</c></tag>
+ <tag><c>nanosecond</c></tag>
<item>
<p>Symbolic representation of the time unit
represented by the integer <c>1000000000</c>.</p>
@@ -119,7 +119,7 @@
<p>One can get an approximation of the <c>native</c>
time unit by calling
<seealso marker="erlang:convert_time_unit/3">
- <c>erlang:convert_time_unit(1, seconds, native)</c></seealso>.
+ <c>erlang:convert_time_unit(1, second, native)</c></seealso>.
The result equals the number
of whole <c>native</c> time units per second. If
the number of <c>native</c> time units per second does not
@@ -149,6 +149,10 @@
<seealso marker="kernel:os#perf_counter/0">
<c>os:perf_counter/0</c></seealso>.</p>
</item>
+ <tag><seealso marker="#type_deprecated_time_unit"><c>deprecated_time_unit()</c></seealso></tag>
+ <item><p>
+ Deprecated symbolic representations kept for backwards-compatibility.
+ </p></item>
</taglist>
<p>The <c>time_unit/0</c> type can be extended.
To convert time values between time units, use
@@ -156,6 +160,27 @@
<c>erlang:convert_time_unit/3</c></seealso>.</p>
</desc>
</datatype>
+ <datatype>
+ <name name="deprecated_time_unit"></name>
+ <desc><marker id="type_deprecated_time_unit"/>
+ <p>The <seealso marker="#type_time_unit"><c>time_unit()</c></seealso>
+ type also consist of the following <em>deprecated</em> symbolic
+ time units:</p>
+ <taglist>
+ <tag><c>seconds</c></tag>
+ <item><p>Same as <seealso marker="#type_time_unit"><c>second</c></seealso>.</p></item>
+
+ <tag><c>milli_seconds</c></tag>
+ <item><p>Same as <seealso marker="#type_time_unit"><c>millisecond</c></seealso>.</p></item>
+
+ <tag><c>micro_seconds</c></tag>
+ <item><p>Same as <seealso marker="#type_time_unit"><c>microsecond</c></seealso>.</p></item>
+
+ <tag><c>nano_seconds</c></tag>
+ <item><p>Same as <seealso marker="#type_time_unit"><c>nanosecond</c></seealso>.</p></item>
+ </taglist>
+ </desc>
+ </datatype>
</datatypes>
<funcs>
@@ -352,7 +377,7 @@ Z = erlang:adler32_combine(X,Y,iolist_size(Data2)).</code>
2> binary_part(Bin,{0,2}).
&lt;&lt;1,2&gt;&gt;</code>
<p>For details about the <c><anno>PosLen</anno></c> semantics, see
- <seealso marker="stdlib:binary"><c>stdlib:binary(3)</c></seealso>.</p>
+ <seealso marker="stdlib:binary"><c>binary(3)</c></seealso>.</p>
<p>Allowed in guard tests.</p>
</desc>
</func>
@@ -479,7 +504,7 @@ Z = erlang:adler32_combine(X,Y,iolist_size(Data2)).</code>
this function is deprecated.</em> New code is to use
<seealso marker="stdlib:binary#bin_to_list/3">
<c>binary:bin_to_list/3</c></seealso>
- in <c>STDLIB</c> instead. All functions in module
+ in STDLIB instead. All functions in module
<c>binary</c> consistently use zero-based indexing.</p>
</note>
</desc>
@@ -491,7 +516,14 @@ Z = erlang:adler32_combine(X,Y,iolist_size(Data2)).</code>
<desc>
<p>Returns an Erlang term that is the result of decoding
binary object <c><anno>Binary</anno></c>, which must be encoded
- according to the Erlang external term format.</p>
+ according to the <seealso marker="erts:erl_ext_dist">
+ Erlang external term format</seealso>.</p>
+ <pre>
+> <input>Bin = term_to_binary(hello).</input>
+&lt;&lt;131,100,0,5,104,101,108,108,111>>
+> <input>hello = binary_to_term(Bin).</input>
+hello
+</pre>
<warning>
<p>When decoding binaries from untrusted sources,
consider using <c>binary_to_term/2</c> to prevent Denial
@@ -530,6 +562,14 @@ Z = erlang:adler32_combine(X,Y,iolist_size(Data2)).</code>
</taglist>
<p>Failure: <c>badarg</c> if <c>safe</c> is specified and unsafe
data is decoded.</p>
+ <pre>
+> <input>binary_to_term(&lt;&lt;131,100,0,5,104,101,108,108,111>>, [safe]).</input>
+** exception error: bad argument
+> <input>hello.</input>
+hello
+> <input>binary_to_term(&lt;&lt;131,100,0,5,104,101,108,108,111>>, [safe]).</input>
+hello
+</pre>
<p>See also
<seealso marker="#term_to_binary/1"><c>term_to_binary/1</c></seealso>,
<seealso marker="#binary_to_term/1">
@@ -699,7 +739,7 @@ Z = erlang:adler32_combine(X,Y,iolist_size(Data2)).</code>
<p>Returns <c>true</c> if <c><anno>Module</anno></c> has old code,
otherwise <c>false</c>.</p>
<p>See also <seealso marker="kernel:code">
- <c>kernel:code(3)</c></seealso>.</p>
+ <c>code(3)</c></seealso>.</p>
</desc>
</func>
@@ -776,8 +816,31 @@ Z = erlang:adler32_combine(X,Y,iolist_size(Data2)).</code>
by passing option <c>{allow_gc, false}</c>.</p>
</item>
</taglist>
+ <note>
+ <p>
+ Up until ERTS version 8.*, the check process code operation
+ checks for all types of references to the old code. That is,
+ direct references (e.g. return addresses on the process
+ stack), indirect references (<c>fun</c>s in process
+ context), and references to literals in the code.
+ </p>
+ <p>
+ As of ERTS version 9.0, the check process code operation
+ only checks for direct references to the code. Indirect
+ references via <c>fun</c>s will be ignored. If such
+ <c>fun</c>s exist and are used after a purge of the old
+ code, an exception will be raised upon usage (same as
+ the case when the <c>fun</c> is received by the process
+ after the purge). Literals will be taken care of (copied)
+ at a later stage. This behavior can as of ERTS version
+ 8.1 be enabled when
+ <seealso marker="doc/installation_guide:INSTALL#Advanced-configuration-and-build-of-ErlangOTP_Configuring">building OTP</seealso>,
+ and will automatically be enabled if dirty scheduler
+ support is enabled.
+ </p>
+ </note>
<p>See also <seealso marker="kernel:code">
- <c>kernel:code(3)</c></seealso>.</p>
+ <c>code(3)</c></seealso>.</p>
<p>Failures:</p>
<taglist>
<tag><c>badarg</c></tag>
@@ -1003,7 +1066,7 @@ Z = erlang:crc32_combine(X,Y,iolist_size(Data2)).</code>
otherwise <c>true</c>.</p>
<warning>
<p>This BIF is intended for the code server (see
- <seealso marker="kernel:code"><c>kernel:code(3)</c></seealso>)
+ <seealso marker="kernel:code"><c>code(3)</c></seealso>)
and is not to be used elsewhere.</p>
</warning>
<p>Failure: <c>badarg</c> if there already is an old version of
@@ -2471,7 +2534,7 @@ os_prompt%</pre>
</taglist>
<warning>
<p>This BIF is intended for the code server (see
- <seealso marker="kernel:code"><c>kernel:code(3)</c></seealso>)
+ <seealso marker="kernel:code"><c>code(3)</c></seealso>)
and is not to be used elsewhere.</p>
</warning>
</desc>
@@ -2536,7 +2599,7 @@ os_prompt%</pre>
<p>Returns a list of all loaded Erlang modules (current and
old code), including preloaded modules.</p>
<p>See also <seealso marker="kernel:code">
- <c>kernel:code(3)</c></seealso>.</p>
+ <c>code(3)</c></seealso>.</p>
</desc>
</func>
@@ -2641,7 +2704,7 @@ os_prompt%</pre>
to the last occurrence is used. Example:</p>
<pre>
> <input>erlang:make_tuple(5, [], [{2,ignored},{5,zz},{2,aa}]).</input>
-{{[],aa,[],[],zz}</pre>
+{[],aa,[],[],zz}</pre>
</desc>
</func>
@@ -2816,7 +2879,7 @@ os_prompt%</pre>
</item>
<tag><c>ets</c></tag>
<item>
- <p>The total amount of memory currently allocated for <c>ets</c>
+ <p>The total amount of memory currently allocated for ETS
tables. This memory is part of the memory presented as
<c>system</c> memory.</p>
</item>
@@ -2837,7 +2900,7 @@ os_prompt%</pre>
<p>For information on how to run the emulator with
instrumentation, see
<seealso marker="tools:instrument">
- <c>tools:instrument(3)</c></seealso>
+ <c>instrument(3)</c></seealso>
and/or <seealso marker="erl"><c>erl(1)</c></seealso>.</p>
</item>
</taglist>
@@ -2885,7 +2948,7 @@ RealSystem = system + MissedSystem</code>
memory blocks.</p>
</note>
<note>
- <p>As from <c>ERTS</c> 5.6.4, <c>erlang:memory/0</c> requires that
+ <p>As from ERTS 5.6.4, <c>erlang:memory/0</c> requires that
all <seealso marker="erts:erts_alloc"><c>erts_alloc(3)</c></seealso>
allocators are enabled (default behavior).</p>
</note>
@@ -2906,7 +2969,7 @@ RealSystem = system + MissedSystem</code>
of <c>memory_type()</c> atoms, in which case a corresponding list of
<c>{memory_type(), Size :: integer >= 0}</c> tuples is returned.</p>
<note>
- <p>As from <c>ERTS</c> 5.6.4,
+ <p>As from ERTS 5.6.4,
<c>erlang:memory/1</c> requires that
all <seealso marker="erts_alloc"><c>erts_alloc(3)</c></seealso>
allocators are enabled (default behavior).</p>
@@ -2954,7 +3017,7 @@ RealSystem = system + MissedSystem</code>
the module.</p>
<warning>
<p>This BIF is intended for the code server (see
- <seealso marker="kernel:code"><c>kernel:code(3)</c></seealso>)
+ <seealso marker="kernel:code"><c>code(3)</c></seealso>)
and is not to be used elsewhere.</p>
</warning>
</desc>
@@ -3177,13 +3240,13 @@ RealSystem = system + MissedSystem</code>
time-out for the <em>monitored node</em> to connect itself,
even if it cannot be actively connected from this node
(that is, it is blocked). The state where this can be useful
- can only be achieved by using the <c>Kernel</c> option
+ can only be achieved by using the Kernel option
<c>dist_auto_connect once</c>. If that option is not
used, option <c>allow_passive_connect</c> has no effect.</p>
<note>
<p>Option <c>allow_passive_connect</c> is used
internally and is seldom needed in applications where the
- network topology and the <c>Kernel</c> options in effect
+ network topology and the Kernel options in effect
are known in advance.</p>
</note>
<p>Failure: <c>badarg</c> if the local node is not alive or the
@@ -3249,9 +3312,9 @@ RealSystem = system + MissedSystem</code>
<desc>
<p>Works exactly like
<seealso marker="#error/1"><c>error/1</c></seealso>, but
- <c>Dialyzer</c> thinks that this BIF will return an arbitrary
+ Dialyzer thinks that this BIF will return an arbitrary
term. When used in a stub function for a NIF to generate an
- exception when the NIF library is not loaded, <c>Dialyzer</c>
+ exception when the NIF library is not loaded, Dialyzer
does not generate false warnings.</p>
</desc>
</func>
@@ -3262,9 +3325,9 @@ RealSystem = system + MissedSystem</code>
<desc>
<p>Works exactly like
<seealso marker="#error/2"><c>error/2</c></seealso>, but
- <c>Dialyzer</c> thinks that this BIF will return an arbitrary
+ Dialyzer thinks that this BIF will return an arbitrary
term. When used in a stub function for a NIF to generate an
- exception when the NIF library is not loaded, <c>Dialyzer</c>
+ exception when the NIF library is not loaded, Dialyzer
does not generate false warnings.</p>
</desc>
</func>
@@ -3387,9 +3450,9 @@ RealSystem = system + MissedSystem</code>
translation or to force, for example UTF-8, supply the executable
and/or arguments as a binary in the correct
encoding. For details, see the module
- <seealso marker="kernel:file"><c>kernel:file(3)</c></seealso>, the
+ <seealso marker="kernel:file"><c>file(3)</c></seealso>, the
function <seealso marker="kernel:file#native_name_encoding/0">
- <c>file:native_name_encoding/0</c></seealso> in <c>Kernel</c>, and
+ <c>file:native_name_encoding/0</c></seealso> in Kernel, and
the <seealso marker="stdlib:unicode_usage">
<c>Using Unicode in Erlang</c></seealso> User's Guide.</p>
<note>
@@ -3701,7 +3764,7 @@ RealSystem = system + MissedSystem</code>
the owning process using signals of the form
<c>{'EXIT', Port, PosixCode}</c>. For the possible values of
<c>PosixCode</c>, see
- <seealso marker="kernel:file"><c>kernel:file(3)</c></seealso>.</p>
+ <seealso marker="kernel:file"><c>file(3)</c></seealso>.</p>
<p>The maximum number of ports that can be open at the same
time can be configured by passing command-line flag
<seealso marker="erl#max_ports"><c>+Q</c></seealso> to
@@ -3716,7 +3779,7 @@ RealSystem = system + MissedSystem</code>
<desc>
<p>Portable hash function that gives the same hash for
the same Erlang term regardless of machine architecture and
- <c>ERTS</c> version (the BIF was introduced in <c>ERTS</c> 4.9.1.1).
+ ERTS version (the BIF was introduced in ERTS 4.9.1.1).
The function returns a hash value for
<c><anno>Term</anno></c> within the range
<c>1..<anno>Range</anno></c>. The maximum value for
@@ -3736,7 +3799,7 @@ RealSystem = system + MissedSystem</code>
<desc>
<p>Portable hash function that gives the same hash for
the same Erlang term regardless of machine architecture and
- <c>ERTS</c> version (the BIF was introduced in <c>ERTS</c> 5.2).
+ ERTS version (the BIF was introduced in ERTS 5.2).
The function returns a hash value for
<c><anno>Term</anno></c> within the range
<c>0..<anno>Range</anno>-1</c>. The maximum value for
@@ -4299,7 +4362,7 @@ RealSystem = system + MissedSystem</code>
<fsummary>Information about the queue size of a port.</fsummary>
<desc>
<p><c><anno>Bytes</anno></c> is the total number
- of bytes queued by the port using the <c>ERTS</c> driver queue
+ of bytes queued by the port using the ERTS driver queue
implementation.</p>
<p>If the port identified by <c><anno>Port</anno></c> is not open,
<c>undefined</c> is returned. If the port is closed and the
@@ -4407,12 +4470,12 @@ RealSystem = system + MissedSystem</code>
</desc>
</func>
- <marker id="process_flag_min_heap_size"/>
<func>
<name name="process_flag" arity="2" clause_i="3"/>
<fsummary>Set process flag min_heap_size for the calling process.
</fsummary>
<desc>
+ <marker id="process_flag_min_heap_size"/>
<p>Changes the minimum heap size for the calling process.</p>
<p>Returns the old value of the flag.</p>
</desc>
@@ -4429,13 +4492,13 @@ RealSystem = system + MissedSystem</code>
</desc>
</func>
- <marker id="process_flag_max_heap_size"/>
<func>
<name name="process_flag" arity="2" clause_i="5"/>
<fsummary>Set process flag max_heap_size for the calling process.
</fsummary>
<type name="max_heap_size"/>
<desc>
+ <marker id="process_flag_max_heap_size"/>
<p>This flag sets the maximum heap size for the calling process.
If <c><anno>MaxHeapSize</anno></c> is an integer, the system default
values for <c>kill</c> and <c>error_logger</c> are used.
@@ -4503,13 +4566,13 @@ RealSystem = system + MissedSystem</code>
</desc>
</func>
- <marker id="process_flag_message_queue_data"/>
<func>
<name name="process_flag" arity="2" clause_i="6"/>
<fsummary>Set process flag message_queue_data for the calling process.
</fsummary>
<type name="message_queue_data"/>
<desc>
+ <marker id="process_flag_message_queue_data"/>
<p>This flag determines how messages in the message queue
are stored, as follows:</p>
<taglist>
@@ -4525,7 +4588,7 @@ RealSystem = system + MissedSystem</code>
<p>All messages in the message queue will eventually be
placed on heap. They can however temporarily be stored
off heap. This is how messages always have been stored
- up until <c>ERTS</c> 8.0.</p>
+ up until ERTS 8.0.</p>
</item>
</taglist>
<p>The default <c>message_queue_data</c> process flag is determined
@@ -5105,11 +5168,11 @@ RealSystem = system + MissedSystem</code>
that no processes execute old code in the module.</p>
<warning>
<p>This BIF is intended for the code server (see
- <seealso marker="kernel:code"><c>kernel:code(3)</c></seealso>)
+ <seealso marker="kernel:code"><c>code(3)</c></seealso>)
and is not to be used elsewhere.</p>
</warning>
<note>
- <p>As from <c>ERTS</c> 8.0 (Erlang/OTP 19), any lingering processes
+ <p>As from ERTS 8.0 (Erlang/OTP 19), any lingering processes
that still execute the old code is killed by this function.
In earlier versions, such incorrect use could cause much
more fatal failures, like emulator crash.</p>
@@ -6008,7 +6071,7 @@ true</pre>
<p>If <c><anno>Dest</anno></c> is a <c>pid()</c>, the timer is
automatically canceled if the process referred to by the
<c>pid()</c> is not alive, or if the process exits. This
- feature was introduced in <c>ERTS</c> 5.4.11. Notice that
+ feature was introduced in ERTS 5.4.11. Notice that
timers are not automatically canceled when
<c><anno>Dest</anno></c> is an <c>atom()</c>.</p>
<p>See also
@@ -6109,7 +6172,7 @@ true</pre>
system flag <seealso marker="#system_flag_microstate_accounting">
<c>microstate_accounting</c></seealso>.</p>
<p><c>statistics(microstate_accounting)</c> returns a list of maps
- representing some of the OS threads within <c>ERTS</c>. Each map
+ representing some of the OS threads within ERTS. Each map
contains <c>type</c> and <c>id</c> fields that can be used to
identify what
thread it is, and also a counters field that contains data about how
@@ -6201,7 +6264,7 @@ lists:map(
<c>scheduler_wall_time</c> fraction. Without extra states this
time is part of the <c>other</c> state.</item>
<tag><c>ets</c></tag>
- <item>Time spent executing <c>ets</c> BIFs. Without extra states
+ <item>Time spent executing ETS BIFs. Without extra states
this time is part of the <c>emulator</c> state.</item>
<tag><c>gc_full</c></tag>
<item>Time spent doing fullsweep garbage collection. Without extra
@@ -6217,7 +6280,7 @@ lists:map(
part of the <c>other</c> state.</item>
</taglist>
<p>The utility module
- <seealso marker="runtime_tools:msacc"><c>runtime_tools:msacc(3)</c></seealso>
+ <seealso marker="runtime_tools:msacc"><c>msacc(3)</c></seealso>
can be used to more easily analyse these statistics.</p>
<p>Returns <c>undefined</c> if system flag
<seealso marker="#system_flag_microstate_accounting">
@@ -6240,7 +6303,7 @@ lists:map(
<pre>
> <input>statistics(reductions).</input>
{2046,11}</pre>
- <note><p>As from <c>ERTS</c> 5.5 (Erlang/OTP R11B),
+ <note><p>As from ERTS 5.5 (Erlang/OTP R11B),
this value does not include reductions performed in current
time slices of currently scheduled processes. If an
exact value is wanted, use
@@ -6697,12 +6760,12 @@ ok
</desc>
</func>
- <marker id="system_flag_max_heap_size"></marker>
<func>
<name name="system_flag" arity="2" clause_i="8"/>
<fsummary>Set system flag max_heap_size.</fsummary>
<type name="max_heap_size"/>
<desc>
+ <marker id="system_flag_max_heap_size"></marker>
<p>
Sets the default maximum heap size settings for processes.
The size is specified in words. The new <c>max_heap_size</c>
@@ -7059,7 +7122,7 @@ ok
<item>
<marker id="system_info_alloc_util_allocators"></marker>
<p>Returns a list of the names of all allocators using
- the <c>ERTS</c> internal <c>alloc_util</c> framework
+ the ERTS internal <c>alloc_util</c> framework
as atoms. For more information, see section
<seealso marker="erts:erts_alloc#alloc_util">The
alloc_util framework</seealso>
@@ -7069,7 +7132,7 @@ ok
<item>
<marker id="system_info_allocator_tuple"></marker>
<p>Returns information about the specified allocator.
- As from <c>ERTS</c> 5.6.1, the return value is a list
+ As from ERTS 5.6.1, the return value is a list
of <c>{instance, InstanceNo, InstanceInfo}</c> tuples,
where <c>InstanceInfo</c> contains information about
a specific instance of the allocator.
@@ -7086,8 +7149,8 @@ ok
<p>The recognized allocators are listed in
<seealso marker="erts:erts_alloc"><c>erts_alloc(3)</c></seealso>.
Information about super carriers can be obtained from
- <c>ERTS</c> 8.0 with <c>{allocator, erts_mmap}</c> or from
- <c>ERTS</c> 5.10.4; the returned list when calling with
+ ERTS 8.0 with <c>{allocator, erts_mmap}</c> or from
+ ERTS 5.10.4; the returned list when calling with
<c>{allocator, mseg_alloc}</c> also includes an
<c>{erts_mmap, _}</c> tuple as one element in the list.</p>
<p>After reading the <c>erts_alloc(3)</c> documentation,
@@ -7571,7 +7634,7 @@ ok
(for example, <c>./configure --with-dynamic-trace=dtrace</c>).
For more information about dynamic tracing, see
<seealso marker="runtime_tools:dyntrace">
- <c>runtime_tools:dyntrace(3)</c></seealso> manual page and the
+ <c>dyntrace(3)</c></seealso> manual page and the
<c>README.dtrace</c>/<c>README.systemtap</c> files in the
Erlang source code top directory.</p>
</item>
@@ -7611,7 +7674,7 @@ ok
</item>
<tag><c>ets_limit</c></tag>
<item>
- <p>Returns the maximum number of <c>ets</c> tables allowed. This
+ <p>Returns the maximum number of ETS tables allowed. This
limit can be increased at startup by passing
command-line flag
<seealso marker="erts:erl#+e"><c>+e</c></seealso> to
@@ -7799,7 +7862,7 @@ ok
<item>
<marker id="system_info_otp_release"></marker>
<p>Returns a string containing the OTP release number of the
- OTP release that the currently executing <c>ERTS</c> application
+ OTP release that the currently executing ERTS application
is part of.</p>
<p>As from Erlang/OTP 17, the OTP release number corresponds to
the major OTP version number. No
@@ -8174,7 +8237,7 @@ ok
<tag><c>tolerant_timeofday</c></tag>
<item>
<marker id="system_info_tolerant_timeofday"></marker>
- <p>Returns whether a pre <c>ERTS</c> 7.0 backwards compatible
+ <p>Returns whether a pre ERTS 7.0 backwards compatible
compensation for sudden changes of system time is <c>enabled</c>
or <c>disabled</c>. Such compensation is <c>enabled</c> when the
<seealso marker="#system_info_time_offset">time offset</seealso>
@@ -8242,8 +8305,8 @@ ok
<p>Argument <c>scheduler</c> has changed name to
<c>scheduler_id</c> to avoid mix up with argument
<c>schedulers</c>. Argument <c>scheduler</c> was
- introduced in <c>ERTS</c> 5.5 and renamed in
- <c>ERTS</c> 5.5.1.</p>
+ introduced in ERTS 5.5 and renamed in
+ ERTS 5.5.1.</p>
</note>
</desc>
</func>
@@ -8451,7 +8514,7 @@ ok
<seealso marker="time_correction#Erlang_Monotonic_Time">Erlang
monotonic time</seealso>. The time stamp (Ts) has the same
format and value as produced by
- <c>erlang:monotonic_time(nano_seconds)</c>.</p>
+ <c>erlang:monotonic_time(nanosecond)</c>.</p>
</item>
<tag><c>runnable_procs</c></tag>
<item>
@@ -8479,7 +8542,7 @@ ok
<seealso marker="time_correction#Erlang_Monotonic_Time">Erlang
monotonic time</seealso> and a monotonically increasing
integer. The time stamp (Ts) has the same format and value
- as produced by <c>{erlang:monotonic_time(nano_seconds),
+ as produced by <c>{erlang:monotonic_time(nanosecond),
erlang:unique_integer([monotonic])}</c>.</p>
</item>
<tag><c>timestamp</c></tag>
@@ -8551,12 +8614,19 @@ ok
</fsummary>
<desc>
<p>Returns a binary data object that is the result of encoding
- <c><anno>Term</anno></c> according to the Erlang external
- term format.</p>
+ <c><anno>Term</anno></c> according to the
+ <seealso marker="erts:erl_ext_dist">Erlang external
+ term format.</seealso></p>
<p>This can be used for various purposes, for example,
writing a term to a file in an efficient way, or sending an
Erlang term to some type of communications channel not
supported by distributed Erlang.</p>
+ <pre>
+> <input>Bin = term_to_binary(hello).</input>
+&lt;&lt;131,100,0,5,104,101,108,108,111>>
+> <input>hello = binary_to_term(Bin).</input>
+hello
+</pre>
<p>See also <seealso marker="#binary_to_term/1">
<c>binary_to_term/1</c></seealso>.</p>
</desc>
@@ -8709,7 +8779,7 @@ ok
<p>The <c>erlang:timestamp()</c> BIF is equivalent to:</p>
<code type="none">
timestamp() ->
- ErlangSystemTime = erlang:system_time(micro_seconds),
+ ErlangSystemTime = erlang:system_time(microsecond),
MegaSecs = ErlangSystemTime div 1000000000000,
Secs = ErlangSystemTime div 1000000 - MegaSecs*1000000,
MicroSecs = ErlangSystemTime rem 1000000,
@@ -8990,7 +9060,7 @@ timestamp() ->
monotonic time</seealso> time stamp in all trace messages. The
time stamp (Ts) has the same format and value as produced by
<seealso marker="#monotonic_time-1">
- <c>erlang:monotonic_time(nano_seconds)</c></seealso>.
+ <c>erlang:monotonic_time(nanosecond)</c></seealso>.
This flag overrides flag <c>cpu_timestamp</c>.</p>
</item>
<tag><c>strict_monotonic_timestamp</c></tag>
@@ -9001,7 +9071,7 @@ timestamp() ->
integer in all trace messages. The time stamp (Ts) has the
same format and value as produced by <c>{</c>
<seealso marker="#monotonic_time-1">
- <c>erlang:monotonic_time(nano_seconds)</c></seealso><c>,</c>
+ <c>erlang:monotonic_time(nanosecond)</c></seealso><c>,</c>
<seealso marker="#unique_integer-1">
<c>erlang:unique_integer([monotonic])</c></seealso><c>}</c>.
This flag overrides flag <c>cpu_timestamp</c>.</p>
@@ -9403,7 +9473,7 @@ timestamp() ->
<desc>
<p>The delivery of trace messages (generated by
<seealso marker="#trace/3"><c>erlang:trace/3</c></seealso>,
- <seealso marker="kernel:seq_trace"><c>kernel:seq_trace(3)</c></seealso>,
+ <seealso marker="kernel:seq_trace"><c>seq_trace(3)</c></seealso>,
or <seealso marker="#system_profile/2">
<c>erlang:system_profile/2</c></seealso>)
is dislocated on the time-line
diff --git a/erts/doc/src/erlc.xml b/erts/doc/src/erlc.xml
index ca3412b72c..7355be488b 100644
--- a/erts/doc/src/erlc.xml
+++ b/erts/doc/src/erlc.xml
@@ -303,9 +303,9 @@ erlc +export_all file.erl</pre>
<section>
<title>See Also</title>
<p><seealso marker="erl"><c>erl(1)</c></seealso>,
- <seealso marker="compiler:compile"><c>compiler:compile(3)</c></seealso>,
- <seealso marker="parsetools:yecc"><c>parsetools:yecc(3)</c></seealso>,
- <seealso marker="snmp:snmp"><c>snmp:snmp(3)</c></seealso></p>
+ <seealso marker="compiler:compile"><c>compile(3)</c></seealso>,
+ <seealso marker="parsetools:yecc"><c>yecc(3)</c></seealso>,
+ <seealso marker="snmp:snmp"><c>snmp(3)</c></seealso></p>
</section>
</comref>
diff --git a/erts/doc/src/erlsrv.xml b/erts/doc/src/erlsrv.xml
index 53583b625d..6c08b25220 100644
--- a/erts/doc/src/erlsrv.xml
+++ b/erts/doc/src/erlsrv.xml
@@ -527,7 +527,7 @@ void initialize_handler(void){
<title>See Also</title>
<p><seealso marker="start_erl"><c>start_erl(1)</c></seealso>,
<seealso marker="sasl:release_handler">
- <c>sasl:release_handler(3)</c></seealso></p>
+ <c>release_handler(3)</c></seealso></p>
</section>
</comref>
diff --git a/erts/doc/src/erts_alloc.xml b/erts/doc/src/erts_alloc.xml
index a43f51b10a..8ab35851c1 100644
--- a/erts/doc/src/erts_alloc.xml
+++ b/erts/doc/src/erts_alloc.xml
@@ -246,7 +246,7 @@
If it satisfies the request, it is used, otherwise a new
carrier is created. The implementation has a time
complexity that is constant.</p>
- <p>As from <c>ERTS</c> 5.6.1 the emulator refuses to
+ <p>As from ERTS 5.6.1 the emulator refuses to
use this strategy on other allocators than <c>temp_alloc</c>.
This because it only causes problems for other allocators.</p>
</item>
@@ -454,7 +454,7 @@
enabled) is passed instead of a <c><![CDATA[<utilization>]]></c>,
a recomended non-zero utilization value is used. The value
chosen depends on the allocator type and can be changed between
- <c>ERTS</c> versions. Defaults to <c>de</c>, but this
+ ERTS versions. Defaults to <c>de</c>, but this
can be changed in the future.</p>
<p>Carriers are abandoned when
memory utilization in the allocator instance falls below the
@@ -617,7 +617,7 @@
SMP support is <c>NoSchedulers+1</c> instances. Each scheduler
uses a lock-free instance of its own and other threads use
a common instance.</p>
- <p>Before <c>ERTS</c> 5.9 it was possible to configure
+ <p>Before ERTS 5.9 it was possible to configure
a smaller number of thread-specific instances than schedulers.
This is, however, not possible anymore.</p>
</item>
@@ -688,7 +688,7 @@
<p>A map over current allocations is kept by the emulator.
The allocation map can be retrieved through module
<seealso marker="tools:instrument">
- <c>tools:instrument(3)</c></seealso>. <c>+Mim true</c>
+ <c>instrument(3)</c></seealso>. <c>+Mim true</c>
implies <c>+Mis true</c>. <c>+Mim true</c> is the same as flag
<seealso marker="erl#instr"><c>-instr</c></seealso> in
<c>erl(1)</c>.</p>
@@ -698,7 +698,7 @@
<p>Status over allocated memory is kept by the emulator.
The allocation status can be retrieved through module
<seealso marker="tools:instrument">
- <c>tools:instrument(3)</c></seealso>.</p>
+ <c>instrument(3)</c></seealso>.</p>
</item>
<tag><marker id="Mit"/><c>+Mit X</c></tag>
<item>
@@ -737,10 +737,10 @@
<p>Disables features that cannot be enabled while creating an
allocator configuration with
<seealso marker="runtime_tools:erts_alloc_config">
- <c>runtime_tools:erts_alloc_config(3)</c></seealso>.</p>
+ <c>erts_alloc_config(3)</c></seealso>.</p>
<note>
<p>This option is to be used only while running
- <c>runtime_tools:erts_alloc_config(3)</c>, <em>not</em> when
+ <c>erts_alloc_config(3)</c>, <em>not</em> when
using the created configuration.</p>
</note>
</item>
@@ -780,7 +780,7 @@
</note>
<p>The <seealso marker="runtime_tools:erts_alloc_config">
- <c>runtime_tools:erts_alloc_config(3)</c></seealso>
+ <c>erts_alloc_config(3)</c></seealso>
tool can be used to aid creation of an
<c>erts_alloc</c> configuration that is suitable for a limited
number of runtime scenarios.</p>
@@ -791,9 +791,9 @@
<p><seealso marker="erl"><c>erl(1)</c></seealso>,
<seealso marker="erlang"><c>erlang(3)</c></seealso>,
<seealso marker="runtime_tools:erts_alloc_config">
- <c>runtime_tools:erts_alloc_config(3)</c></seealso>,
+ <c>erts_alloc_config(3)</c></seealso>,
<seealso marker="tools:instrument">
- <c>tools:instrument(3)</c></seealso></p>
+ <c>instrument(3)</c></seealso></p>
</section>
</cref>
diff --git a/erts/doc/src/escript.xml b/erts/doc/src/escript.xml
index 602b88f6ed..1d5d280338 100644
--- a/erts/doc/src/escript.xml
+++ b/erts/doc/src/escript.xml
@@ -4,7 +4,7 @@
<comref>
<header>
<copyright>
- <year>2007</year><year>2015</year>
+ <year>2007</year><year>2016</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -101,7 +101,7 @@ io:setopts([{encoding, unicode}])</code>
is <c>latin1</c>, as the script runs in a non-interactive terminal
(see section
<seealso marker="stdlib:unicode_usage#unicode_options_summary">
- Summary of Options</seealso>) in the <c>STDLIB</c> User's Guide.</p>
+ Summary of Options</seealso>) in the STDLIB User's Guide.</p>
</note>
<p>On the third line (or second line depending on the presence
of the Emacs directive), arguments can be specified to
@@ -193,7 +193,7 @@ halt(1).</pre>
as one of the emulator flags. <c>Module</c> must be the
name of a module that has an exported <c>main/1</c>
function. For more information about archives and code loading, see
- <seealso marker="kernel:code"><c>kernel:code(3)</c></seealso>.</p>
+ <seealso marker="kernel:code"><c>code(3)</c></seealso>.</p>
<p>It is often very convenient to have a header in
the escript, especially on Unix platforms. However, the header
is optional, so you directly can "execute"
diff --git a/erts/doc/src/inet_cfg.xml b/erts/doc/src/inet_cfg.xml
index 3b104a0032..0cfcc7905d 100644
--- a/erts/doc/src/inet_cfg.xml
+++ b/erts/doc/src/inet_cfg.xml
@@ -40,7 +40,7 @@
for specific settings for Erlang to function properly on a correctly
IP-configured platform.</p>
- <p>When Erlang starts up it reads the <c>Kernel</c> variable
+ <p>When Erlang starts up it reads the Kernel variable
<c><![CDATA[inetrc]]></c>, which, if defined, is to specify the location
and name of a user configuration file. Example:</p>
@@ -57,7 +57,7 @@
<code type="none"><![CDATA[
% export ERL_INETRC=./cfg_files/erl_inetrc]]></code>
- <p>Notice that the <c>Kernel</c> variable <c><![CDATA[inetrc]]></c>
+ <p>Notice that the Kernel variable <c><![CDATA[inetrc]]></c>
overrides this environment variable.</p>
<p>If no user configuration file is specified and Erlang is started
@@ -68,7 +68,7 @@
<c>/etc/host.conf</c> and <c>/etc/nsswitch.conf</c>) in these modes,
except for <c>/etc/resolv.conf</c> and <c>/etc/hosts</c> that is read and
monitored for changes on Unix platforms for the internal DNS client
- <seealso marker="kernel:inet_res"><c>kernel:inet_res(3)</c></seealso>.</p>
+ <seealso marker="kernel:inet_res"><c>inet_res(3)</c></seealso>.</p>
<p>If Erlang is started in long name distributed mode, it needs to
get the domain name from somewhere and reads system <c>inet</c>
@@ -138,7 +138,7 @@
<p><c><![CDATA[File = string()]]></c></p>
<p>Specify a system file that Erlang is to read resolver
configuration from for the internal DNS client
- <seealso marker="kernel:inet_res"><c>kernel:inet_res(3)</c></seealso>,
+ <seealso marker="kernel:inet_res"><c>inet_res(3)</c></seealso>,
and monitor for changes, even if it does not exist.
The path must be absolute.</p>
<p>This can override the configuration parameters
@@ -200,7 +200,7 @@
<p><c><![CDATA[Port = integer()]]></c></p>
<p>Add address (and port, if other than default) of the primary
nameserver to use for
- <seealso marker="kernel:inet_res"><c>kernel:inet_res(3)</c></seealso>.
+ <seealso marker="kernel:inet_res"><c>inet_res(3)</c></seealso>.
</p>
</item>
<tag><c><![CDATA[{alt_nameserver, IP [,Port]}.]]></c></tag>
@@ -209,14 +209,14 @@
<p><c><![CDATA[Port = integer()]]></c></p>
<p>Add address (and port, if other than default) of the secondary
nameserver for
- <seealso marker="kernel:inet_res"><c>kernel:inet_res(3)</c></seealso>.
+ <seealso marker="kernel:inet_res"><c>inet_res(3)</c></seealso>.
</p>
</item>
<tag><c><![CDATA[{search, Domains}.]]></c></tag>
<item>
<p><c><![CDATA[Domains = [string()]]]></c></p>
<p>Add search domains for
- <seealso marker="kernel:inet_res"><c>kernel:inet_res(3)</c></seealso>.
+ <seealso marker="kernel:inet_res"><c>inet_res(3)</c></seealso>.
</p>
</item>
<tag><c><![CDATA[{lookup, Methods}.]]></c></tag>
@@ -229,7 +229,7 @@
<item><c><![CDATA[file]]></c> (use host data retrieved from system
configuration files and/or the user configuration file)</item>
<item><c><![CDATA[dns]]></c> (use the Erlang DNS client
- <seealso marker="kernel:inet_res"><c>kernel:inet_res(3)</c></seealso>
+ <seealso marker="kernel:inet_res"><c>inet_res(3)</c></seealso>
for nameserver queries)</item>
</list>
<p>The lookup method <c><![CDATA[string]]></c> tries to
@@ -249,7 +249,7 @@
<item>
<p><c><![CDATA[Time = integer()]]></c></p>
<p>Set how often (in milliseconds) the resolver cache for
- <seealso marker="kernel:inet_res"><c>kernel:inet_res(3)</c></seealso>
+ <seealso marker="kernel:inet_res"><c>inet_res(3)</c></seealso>
is refreshed (that is, expired DNS records are deleted).
Defaults to 1 hour.</p>
</item>
@@ -258,28 +258,28 @@
<p><c><![CDATA[Time = integer()]]></c></p>
<p>Set the time to wait until retry (in milliseconds) for DNS queries
made by
- <seealso marker="kernel:inet_res"><c>kernel:inet_res(3)</c></seealso>.
+ <seealso marker="kernel:inet_res"><c>inet_res(3)</c></seealso>.
Defaults to 2 seconds.</p>
</item>
<tag><c><![CDATA[{retry, N}.]]></c></tag>
<item>
<p><c><![CDATA[N = integer()]]></c></p>
<p>Set the number of DNS queries
- <seealso marker="kernel:inet_res"><c>kernel:inet_res(3)</c></seealso>
+ <seealso marker="kernel:inet_res"><c>inet_res(3)</c></seealso>
will try before giving up. Defaults to 3.</p>
</item>
<tag><c><![CDATA[{inet6, Bool}.]]></c></tag>
<item>
<p><c><![CDATA[Bool = true | false]]></c></p>
<p>Tells the DNS client
- <seealso marker="kernel:inet_res"><c>kernel:inet_res(3)</c></seealso>
+ <seealso marker="kernel:inet_res"><c>inet_res(3)</c></seealso>
to look up IPv6 addresses. Defaults to <c>false</c>.</p>
</item>
<tag><c><![CDATA[{usevc, Bool}.]]></c></tag>
<item>
<p><c><![CDATA[Bool = true | false]]></c></p>
<p>Tells the DNS client
- <seealso marker="kernel:inet_res"><c>kernel:inet_res(3)</c></seealso>
+ <seealso marker="kernel:inet_res"><c>inet_res(3)</c></seealso>
to use TCP (Virtual Circuit) instead of UDP. Defaults to
<c>false</c>.</p>
</item>
@@ -287,7 +287,7 @@
<item>
<p><c><![CDATA[Version = false | 0]]></c></p>
<p>Sets the EDNS version that
- <seealso marker="kernel:inet_res"><c>kernel:inet_res(3)</c></seealso>
+ <seealso marker="kernel:inet_res"><c>inet_res(3)</c></seealso>
will use. The only allowed version is zero. Defaults to <c>false</c>,
which means not to use EDNS.</p>
</item>
@@ -295,7 +295,7 @@
<item>
<p><c><![CDATA[N = integer()]]></c></p>
<p>Sets the allowed UDP payload size
- <seealso marker="kernel:inet_res"><c>kernel:inet_res(3)</c></seealso>
+ <seealso marker="kernel:inet_res"><c>inet_res(3)</c></seealso>
will advertise in EDNS queries. Also sets the limit
when the DNS query will be deemed too large for UDP
forcing a TCP query instead; this is not entirely
diff --git a/erts/doc/src/init.xml b/erts/doc/src/init.xml
index 54507c6572..c14f0a558d 100644
--- a/erts/doc/src/init.xml
+++ b/erts/doc/src/init.xml
@@ -42,7 +42,7 @@
<p><c>init</c> reads the boot script, which contains instructions on
how to initiate the system. For more information about boot scripts, see
- <seealso marker="sasl:script"><c>sasl:script(4)</c></seealso>.</p>
+ <seealso marker="sasl:script"><c>script(4)</c></seealso>.</p>
<p><c>init</c> also contains functions to restart, reboot, and stop
the system.</p>
@@ -154,7 +154,7 @@
terminates. If command-line flag <c>-heart</c> was specified,
the <c>heart</c> program tries to reboot the system. For more
information, see
- <seealso marker="kernel:heart"><c>kernel:heart(3)</c></seealso>.</p>
+ <seealso marker="kernel:heart"><c>heart(3)</c></seealso>.</p>
<p>To limit the shutdown time, the time <c>init</c> is allowed
to spend taking down applications, command-line flag
<c>-shutdown_time</c> is to be used.</p>
@@ -207,7 +207,7 @@
command-line flag <c>-heart</c> was specified, the <c>heart</c>
program is terminated before the Erlang node terminates.
For more information, see
- <seealso marker="kernel:heart"><c>kernel:heart(3)</c></seealso>.</p>
+ <seealso marker="kernel:heart"><c>heart(3)</c></seealso>.</p>
<p>To limit the shutdown time, the time <c>init</c> is allowed
to spend taking down applications, command-line flag
<c>-shutdown_time</c> is to be used.</p>
@@ -254,9 +254,9 @@
useful when you want to elaborate with code loading from
archives without editing the <c>boot script</c>. For more
information about interpretation of boot scripts, see
- <seealso marker="sasl:script"><c>sasl:script(4)</c></seealso>.
+ <seealso marker="sasl:script"><c>script(4)</c></seealso>.
The flag has also a similar effect on how the code server works; see
- <seealso marker="kernel:code"><c>kernel:code(3)</c></seealso>.</p>
+ <seealso marker="kernel:code"><c>code(3)</c></seealso>.</p>
</item>
<tag><c>-epmd_module Module</c></tag>
<item>
@@ -361,7 +361,7 @@ error</pre>
<section>
<title>See Also</title>
<p><seealso marker="erl_prim_loader"><c>erl_prim_loader(3)</c></seealso>,
- <seealso marker="kernel:heart"><c>kernel:heart(3)</c></seealso></p>
+ <seealso marker="kernel:heart"><c>heart(3)</c></seealso></p>
</section>
</erlref>
diff --git a/erts/doc/src/introduction.xml b/erts/doc/src/introduction.xml
index 97a0d5999e..790e24f9f3 100644
--- a/erts/doc/src/introduction.xml
+++ b/erts/doc/src/introduction.xml
@@ -34,7 +34,7 @@
</header>
<section>
<title>Scope</title>
- <p>The Erlang Runtime System Application, <c>ERTS</c>, contains
+ <p>The Erlang Runtime System Application, ERTS, contains
functionality necessary to run the Erlang system.</p>
<note>
<p>By default, <c><![CDATA[ERTS]]></c> is only guaranteed to be
diff --git a/erts/doc/src/match_spec.xml b/erts/doc/src/match_spec.xml
index 1b297c5d29..2a14f1e47b 100644
--- a/erts/doc/src/match_spec.xml
+++ b/erts/doc/src/match_spec.xml
@@ -142,7 +142,7 @@
</list>
<p>A match specification used in
- <seealso marker="stdlib:ets"><c>stdlib:ets(3)</c></seealso>
+ <seealso marker="stdlib:ets"><c>ets(3)</c></seealso>
can be described in the following <em>informal</em> grammar:</p>
<list type="bulleted">
@@ -678,7 +678,7 @@
</item>
<item>
<p>If the match specification is executed when selecting objects
- from an <c>ets</c> table:</p>
+ from an ETS table:</p>
<p>Evaluate the expressions in order and return the value of
the last expression (typically there is only one expression
in this context).</p>
@@ -691,11 +691,11 @@
<section>
<marker id="differences_ets_tracing"/>
<title>Differences between Match Specifications in ETS and Tracing</title>
- <p><c>ets</c> match specifications produce a return value.
+ <p>ETS match specifications produce a return value.
Usually the <c><![CDATA[MatchBody]]></c> contains one single
<c><![CDATA[ConditionExpression]]></c> that defines the return value
without any side effects. Calls with side effects are not allowed in
- the <c>ets</c> context.</p>
+ the ETS context.</p>
<p>When tracing there is no return value to produce, the
match specification either matches or does not. The effect when the
@@ -824,7 +824,7 @@
<section>
<title>ETS Examples</title>
- <p>Match all objects in an <c>ets</c> table, where the first element is
+ <p>Match all objects in an ETS table, where the first element is
the atom <c>'strider'</c> and the tuple arity is 3, and return the whole
object:</p>
@@ -834,7 +834,7 @@
['$_']}]
]]></code>
- <p>Match all objects in an <c>ets</c> table with arity &gt; 1 and the first
+ <p>Match all objects in an ETS table with arity &gt; 1 and the first
element is 'gandalf', and return element 2:</p>
<code type="none"><![CDATA[
@@ -863,7 +863,7 @@
]]></code>
<p>Function <seealso marker="stdlib:ets#test_ms/2"><c>ets:test_ms/2></c></seealso>
- can be useful for testing complicated <c>ets</c> matches.</p>
+ can be useful for testing complicated ETS matches.</p>
</section>
</chapter>
diff --git a/erts/doc/src/notes.xml b/erts/doc/src/notes.xml
index b3f5e1073b..412675fd2b 100644
--- a/erts/doc/src/notes.xml
+++ b/erts/doc/src/notes.xml
@@ -32,6 +32,370 @@
<p>This document describes the changes made to the ERTS application.</p>
+<section><title>Erts 8.1</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Fix bug for calls from hipe code to BIFs that disable GC
+ while yielding. Has been causing Dialyzer crashes on ARM
+ (and presumably all other non-intel platforms).</p>
+ <p>
+ Own Id: OTP-13724 Aux Id: PR-1116 </p>
+ </item>
+ <item>
+ <p>
+ Fix a bug where changing the current working directory of
+ the VM would not change the current working directory of
+ programs spawned using <c>erlang:open_port({spawn,""},
+ ...)</c>.</p>
+ <p>
+ Own Id: OTP-13733 Aux Id: ERL-175 </p>
+ </item>
+ <item>
+ <p>
+ Fix a bug where disabling tracing from a process that had
+ return_to tracing enabled and was tracing on
+ <c>erlang:trace/3</c> would cause a segmentation fault.</p>
+ <p>
+ Own Id: OTP-13734</p>
+ </item>
+ <item>
+ <p>
+ Update all erts documentation to use simpler English, use
+ consistent terminology and be easier to navigate.</p>
+ <p>
+ Own Id: OTP-13740</p>
+ </item>
+ <item>
+ <p>
+ Add dirty schedulers to the microstate accounting
+ statistics.</p>
+ <p>
+ Own Id: OTP-13744</p>
+ </item>
+ <item>
+ <p>
+ Fixed dirty scheduler build support on 32-bit windows.</p>
+ <p>
+ Own Id: OTP-13759</p>
+ </item>
+ <item>
+ <p>
+ inet:getstat(Socket) on an SCTP socket returned 0 for
+ send stats. This bug has now been corrected. Reported by
+ systra as issue ERL-102 on bugs.erlang.org.</p>
+ <p>
+ Own Id: OTP-13773 Aux Id: ERL-102 </p>
+ </item>
+ <item>
+ <p>
+ AF_UNSPEC and unknown address families were misread by
+ UDP receive in prim_inet resulting in an exception. This
+ bug has now been corrected.</p>
+ <p>
+ Own Id: OTP-13775</p>
+ </item>
+ <item>
+ <p>
+ Sweep HiPE stack for literals during code purge.</p>
+ <p>
+ Own Id: OTP-13777 Aux Id: PR-1122 </p>
+ </item>
+ <item>
+ <p>
+ Fix bug in run_erl for OpenBSD that could cause it on
+ rare occations to exit without starting the program (erl)
+ at all.</p>
+ <p>
+ Own Id: OTP-13795</p>
+ </item>
+ <item>
+ <p>
+ Update build scripts to not make assumtions about where
+ env, cp and perl are located.</p>
+ <p>
+ Own Id: OTP-13800</p>
+ </item>
+ <item>
+ <p>
+ Fixed a bug where init:stop could deadlock if a process
+ with infinite shutdown timeout (e.g. a supervisor)
+ attempted to load code while terminating.</p>
+ <p>
+ Own Id: OTP-13802</p>
+ </item>
+ <item>
+ <p>
+ Fixed a segmentation fault on sparc CPUs when free'ing a
+ tracer module's state.</p>
+ <p>
+ Own Id: OTP-13803</p>
+ </item>
+ <item>
+ <p>
+ <c>fun</c>s was not properly handled during purge of a
+ module. This could cause a crash of the VM after a purge
+ of a module.</p>
+ <p>
+ Own Id: OTP-13809</p>
+ </item>
+ <item>
+ <p>
+ Fixed a memory leak when the process monitoring a port
+ crashed.</p>
+ <p>
+ Own Id: OTP-13818</p>
+ </item>
+ <item>
+ <p>
+ Fixed multiple dirty scheduler related tracing bugs.</p>
+ <p>
+ Own Id: OTP-13822</p>
+ </item>
+ <item>
+ <p>
+ Fix error handling in beam code runtime loader for a
+ number of cases when index and size fields got corrupted
+ (negative) values.</p>
+ <p>
+ Own Id: OTP-13848 Aux Id: ERL-216 </p>
+ </item>
+ <item>
+ <p>
+ Minor fix of dirty scheduler implementation.</p>
+ <p>
+ Own Id: OTP-13852</p>
+ </item>
+ <item>
+ <p>
+ Calls to <c>erl_drv_send_term()</c> or
+ <c>erl_drv_output_term()</c> from a non-scheduler thread
+ while the corresponding port was invalid caused the
+ emulator to enter an inconsistent state which eventually
+ caused an emulator crash.</p>
+ <p>
+ Own Id: OTP-13866</p>
+ </item>
+ <item>
+ <p>
+ Fix a rare race condition in <c>erlang:open_port({spawn,
+ ""}, ...)</c> that would result in the erl_child_setup
+ program aborting and cause the emulator to exit.</p>
+ <p>
+ Own Id: OTP-13868</p>
+ </item>
+ <item>
+ <p>Driver and NIF operations accessing processes or ports
+ could cause an emulator crash when used from
+ non-scheduler threads. Those operations are:</p> <list>
+ <item><c>erl_drv_send_term()</c></item>
+ <item><c>driver_send_term()</c></item>
+ <item><c>erl_drv_output_term()</c></item>
+ <item><c>driver_output_term()</c></item>
+ <item><c>enif_send()</c></item>
+ <item><c>enif_port_command()</c></item> </list>
+ <p>
+ Own Id: OTP-13869</p>
+ </item>
+ <item>
+ <p>
+ Fix start scripts generation dependency in Makefile</p>
+ <p>
+ Own Id: OTP-13871 Aux Id: ERL-241 </p>
+ </item>
+ <item>
+ <p>
+ The VM could crash if <c>erlang:get_stacktrace()</c> was
+ called after a rescheduled NIF had thrown an exception.</p>
+ <p>
+ Own Id: OTP-13877</p>
+ </item>
+ <item>
+ <p>Calling <c>code:delete/1</c> before a loading a module
+ with an on_load function, the old code would be
+ overwritten, causing a memory or a crash if NIFs were
+ involved. (Thanks to vans163 for reporting this bug.)</p>
+ <p>
+ Own Id: OTP-13893 Aux Id: ERL-240 </p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Improved accuracy of timeouts on MacOS X. This by setting
+ premature timeouts followed by a short actual timeout
+ during scheduler wait.</p>
+ <p>
+ Own Id: OTP-13698</p>
+ </item>
+ <item>
+ <p>Added the following symbolic time unit representations
+ to the <seealso
+ marker="erlang#type-time_unit"><c>erlang:time_unit()</c></seealso>
+ type:</p> <list> <item><c>second</c></item>
+ <item><c>millisecond</c></item>
+ <item><c>microsecond</c></item>
+ <item><c>nanosecond</c></item> </list> <p>The following
+ symbolic time unit representations are now
+ <em>deprecated</em>, but still part of the
+ <c>erlang:time_unit()</c> type:</p> <list>
+ <item><c>seconds</c></item>
+ <item><c>milli_seconds</c></item>
+ <item><c>micro_seconds</c></item>
+ <item><c>nano_seconds</c></item> </list>
+ <p>
+ Own Id: OTP-13735</p>
+ </item>
+ <item>
+ <p>
+ Fix maps hashing entropy of maps with maps keys.</p>
+ <p>
+ Own Id: OTP-13763 Aux Id: ERL-199 </p>
+ </item>
+ <item>
+ <p>
+ Improved dirty scheduler support. A purge of a module can
+ now be performed without having to wait for completion of
+ all ongoing dirty NIF calls.</p>
+ <p>
+ Note that when enabling support for dirty schedulers, a
+ new purge strategy will as of ERTS version 8.1 be
+ enabled. This new strategy is not fully backwards
+ compatible with the strategy used by default. For more
+ information see the documentation of <seealso
+ marker="erts:erlang#check_process_code/3"><c>erlang:check_process_code/3</c></seealso>.</p>
+ <p>
+ Own Id: OTP-13808 Aux Id: OTP-13833 </p>
+ </item>
+ <item>
+ <p>
+ A new purge strategy has been introduced. The new
+ strategy will by default be disabled during the OTP 19
+ release, but will be the only strategy available as of
+ the OTP 20 release.</p>
+ <p>
+ The new strategy is slightly incompatible with the
+ strategy being used by default in OTP 19. Using the
+ default strategy, processes holding <c>fun</c>s that
+ refer to the module being purged either fail a soft
+ purge, or will be killed during a hard purge. The new
+ strategy completely ignores <c>fun</c>s. If <c>fun</c>s
+ referring to the code being purged exist, and are used
+ after a purge, an exception will be raised upon usage.
+ That is, the behavior will be exactly the same as the
+ case when a <c>fun</c> is received by a process after the
+ purge.</p>
+ <p>
+ The new strategy can optionally be enabled when building
+ OTP during OTP 19, and will automatically be enabled if
+ the runtime system is built with support for dirty
+ schedulers.</p>
+ <p>
+ For more information see the documentation of <seealso
+ marker="erts:erlang#check_process_code/3"><c>erlang:check_process_code/3</c></seealso>.</p>
+ <p>
+ Own Id: OTP-13833</p>
+ </item>
+ <item>
+ <p>
+ Fixed unnecessary overestimation of heap size need during
+ garbage collection.</p>
+ <p>
+ Own Id: OTP-13851</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Erts 8.0.5</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Fixed a VM crash that occured in a garbage collection of
+ a process when it had received binaries. This bug was
+ introduced in ERTS version 8.0 (OTP 19.0).</p>
+ <p>
+ Own Id: OTP-13890</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Erts 8.0.4</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Fixed a VM crash that occured in garbage collection of a
+ process when it had received maps over the distribution.
+ This bug was introduced in ERTS version 8.0 (OTP 19.0).</p>
+ <p>
+ Own Id: OTP-13889</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Erts 8.0.3</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Fixed a race that could cause a lost wakeup of a process
+ that timed out in a <c>receive ... after</c>. This bug
+ was introduced in ERTS version 7.0.</p>
+ <p>
+ Own Id: OTP-13798 Aux Id: OTP-11997 </p>
+ </item>
+ <item>
+ <p>
+ Fixed segfault after writing an erl crash dump.</p>
+ <p>
+ Own Id: OTP-13799</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Erts 8.0.2</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Fix scheduler deadlock bug in <c>ets:update_counter/4</c>
+ when key is not found and inserting the default object
+ causes the table to grow.</p>
+ <p>
+ Own Id: OTP-13731 Aux Id: ERL-188 </p>
+ </item>
+ <item>
+ <p>
+ Fix VM abort "Overrun stack and heap" in garbage
+ collection triggered by a <c>bsl</c> operation and some
+ very specific heap conditions.</p>
+ <p>
+ Own Id: OTP-13732 Aux Id: seq13142 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Erts 8.0.1</title>
<section><title>Fixed Bugs and Malfunctions</title>
@@ -9332,9 +9696,9 @@
dynamically linking against <c>libssl.so</c> and
<c>libcrypto.so</c>. The runtime library search path has
also been extended. </item><item> The <c>configure</c>
- scripts of <c>erl_interface</c> and <c>odbc</c> now
+ scripts of Erl_interface and ODBC now
search for thread libraries and thread library quirks the
- same way as <c>erts</c> do. </item><item> The
+ same way as ERTS do. </item><item> The
<c>configure</c> script of the <c>odbc</c> application
now also looks for odbc libraries in <c>lib64</c> and
<c>lib/64</c> directories when building on a 64-bit
@@ -10450,7 +10814,7 @@
</item>
<item>
<p>
- A corrected bug in <c>ets</c> for <c>bag</c> and
+ A corrected bug in ETS for <c>bag</c> and
<c>duplicate_bag</c>. A <c>delete/2</c> or
<c>lookup_element/3</c> could miss objects in a fixed
table if one or more objects with the same key had
@@ -10892,7 +11256,7 @@
<list>
<item>
<p>
- A corrected bug in <c>ets</c> for <c>bag</c> and
+ A corrected bug in ETS for <c>bag</c> and
<c>duplicate_bag</c>. A <c>delete/2</c> or
<c>lookup_element/3</c> could miss objects in a fixed
table if one or more objects with the same key had
diff --git a/erts/doc/src/run_erl.xml b/erts/doc/src/run_erl.xml
index 4780c00ea4..a9b6a7e2c6 100644
--- a/erts/doc/src/run_erl.xml
+++ b/erts/doc/src/run_erl.xml
@@ -49,7 +49,7 @@
<funcs>
<func>
<name>run_erl [-daemon] pipe_dir/ log_dir "exec command
- [command_arguments]"</name>
+ arg1 arg2 ..."</name>
<fsummary>Start the Erlang emulator without attached terminal.</fsummary>
<desc>
<p>Arguments:</p>
@@ -92,11 +92,10 @@
</item>
</list>
</item>
- <tag><c>"exec command [command_arguments]"</c></tag>
+ <tag><c>"exec command arg1 arg2 ..."</c></tag>
<item>
- <p>In the third argument, <c><![CDATA[command]]></c> is the
- executable to execute where everything written to <c>stdin</c>
- and <c>stdout</c> is logged to <c><![CDATA[log_dir]]></c>.</p>
+ <p>A space-separated string specifying the program to be executed.
+ The second field is typically a command name such as <c>erl</c>.</p>
</item>
</taglist>
</desc>
@@ -105,7 +104,7 @@
<section>
<title>Notes concerning the Log Files</title>
- <p>While running, <c>run_erl</c> (as stated earlier) sends all output,
+ <p>While running, <c>run_erl</c> sends all output,
uninterpreted, to a log file. The file is named
<c><![CDATA[erlang.log.N]]></c>, where <c>N</c> is an integer. When the
log is "full" (default log size is 100 KB), <c>run_erl</c> starts to log
@@ -174,7 +173,7 @@
without Daylight Saving Time), rather than in local time.
This does not affect data coming from Erlang,
only the logs output directly by <c>run_erl</c>. Application
- <c>SASL</c> can be modified accordingly by setting the Erlang
+ SASL can be modified accordingly by setting the Erlang
application variable <c><![CDATA[utc_log]]></c> to
<c><![CDATA[true]]></c>.</p>
</item>
diff --git a/erts/doc/src/start_erl.xml b/erts/doc/src/start_erl.xml
index 50f8c58a48..4887d4606e 100644
--- a/erts/doc/src/start_erl.xml
+++ b/erts/doc/src/start_erl.xml
@@ -163,7 +163,7 @@
<title>See Also</title>
<p><seealso marker="erlsrv"><c>erlsrv(1)</c></seealso>,
<seealso marker="sasl:release_handler">
- <c>sasl:release_handler(3)</c></seealso></p>
+ <c>release_handler(3)</c></seealso></p>
</section>
</comref>
diff --git a/erts/doc/src/time_correction.xml b/erts/doc/src/time_correction.xml
index 8042ad3b99..77e7a40529 100644
--- a/erts/doc/src/time_correction.xml
+++ b/erts/doc/src/time_correction.xml
@@ -4,7 +4,7 @@
<chapter>
<header>
<copyright>
- <year>1999</year><year>2015</year>
+ <year>1999</year><year>2016</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -35,7 +35,7 @@
<section>
<title>New Extended Time Functionality</title>
- <note><p>As from Erlang/OTP 18 (<c>ERTS</c> 7.0) the time functionality
+ <note><p>As from Erlang/OTP 18 (ERTS 7.0) the time functionality
has been extended. This includes a
<seealso marker="#The_New_Time_API">new API</seealso>
for time and
@@ -247,7 +247,7 @@
everything that has anything to do with time. All timers,
regardless of it is a <c>receive ... after</c> timer, BIF timer,
or a timer in the
- <seealso marker="stdlib:timer"><c>stdlib:timer(3)</c></seealso>
+ <seealso marker="stdlib:timer"><c>timer(3)</c></seealso>
module, are triggered relative Erlang monotonic time. Even
<seealso marker="#Erlang_System_Time">Erlang system
time</seealso> is based on Erlang monotonic time.
@@ -408,7 +408,7 @@
and does not change later. This is the default behavior, but
not because it is the best mode (which it is not). It is
default <em>only</em> because this is how the runtime system
- behaved until <c>ERTS</c> 7.0.
+ behaved until ERTS 7.0.
Ensure that your Erlang code that can execute during a time
warp is <seealso marker="#Time_Warp_Safe_Code">time warp
safe</seealso> before enabling other modes.</p>
@@ -663,7 +663,7 @@
<marker id="The_New_Erlang_Monotonic_Time"/>
<section>
<title>New Erlang Monotonic Time</title>
- <p>Erlang monotonic time as such is new as from <c>ERTS</c> 7.0.
+ <p>Erlang monotonic time as such is new as from ERTS 7.0.
It is introduced to detach time measurements, such as elapsed
time from calendar time. In many use cases there is a need to
measure elapsed time or specify a time relative to another point
diff --git a/erts/emulator/Makefile.in b/erts/emulator/Makefile.in
index 6c4b30020a..e0260205e3 100644
--- a/erts/emulator/Makefile.in
+++ b/erts/emulator/Makefile.in
@@ -51,6 +51,7 @@ ARFLAGS=rc
OMIT_OMIT_FP=no
DIRTY_SCHEDULER_SUPPORT=@DIRTY_SCHEDULER_SUPPORT@
+NEW_PURGE_STRATEGY=@NEW_PURGE_STRATEGY@
ifeq ($(TYPE),debug)
PURIFY =
@@ -591,11 +592,8 @@ GENERATE += $(TTF_DIR)/driver_tab.c
#
# This list must be consistent with PRE_LOADED_MODULES in
# erts/preloaded/src/Makefile.
-ifeq ($(TARGET),win32)
-# On windows the preloaded objects are in a resource object.
-PRELOAD_OBJ = $(OBJDIR)/beams.$(RES_EXT)
-PRELOAD_SRC = $(TARGET)/beams.rc
-$(PRELOAD_SRC): $(ERL_TOP)/erts/preloaded/ebin/otp_ring0.beam \
+
+PRELOAD_BEAM = $(ERL_TOP)/erts/preloaded/ebin/otp_ring0.beam \
$(ERL_TOP)/erts/preloaded/ebin/erts_code_purger.beam \
$(ERL_TOP)/erts/preloaded/ebin/init.beam \
$(ERL_TOP)/erts/preloaded/ebin/prim_eval.beam \
@@ -606,23 +604,20 @@ $(PRELOAD_SRC): $(ERL_TOP)/erts/preloaded/ebin/otp_ring0.beam \
$(ERL_TOP)/erts/preloaded/ebin/erl_prim_loader.beam \
$(ERL_TOP)/erts/preloaded/ebin/erlang.beam \
$(ERL_TOP)/erts/preloaded/ebin/erts_internal.beam \
- $(ERL_TOP)/erts/preloaded/ebin/erl_tracer.beam
+ $(ERL_TOP)/erts/preloaded/ebin/erl_tracer.beam \
+ $(ERL_TOP)/erts/preloaded/ebin/erts_literal_area_collector.beam \
+ $(ERL_TOP)/erts/preloaded/ebin/erts_dirty_process_code_checker.beam
+
+ifeq ($(TARGET),win32)
+# On windows the preloaded objects are in a resource object.
+PRELOAD_OBJ = $(OBJDIR)/beams.$(RES_EXT)
+PRELOAD_SRC = $(TARGET)/beams.rc
+$(PRELOAD_SRC): $(PRELOAD_BEAM)
$(gen_verbose)LANG=C $(PERL) utils/make_preload $(MAKE_PRELOAD_EXTRA) -rc $^ > $@
else
PRELOAD_OBJ = $(OBJDIR)/preload.o
PRELOAD_SRC = $(TARGET)/preload.c
-$(PRELOAD_SRC): $(ERL_TOP)/erts/preloaded/ebin/otp_ring0.beam \
- $(ERL_TOP)/erts/preloaded/ebin/erts_code_purger.beam \
- $(ERL_TOP)/erts/preloaded/ebin/init.beam \
- $(ERL_TOP)/erts/preloaded/ebin/prim_eval.beam \
- $(ERL_TOP)/erts/preloaded/ebin/prim_inet.beam \
- $(ERL_TOP)/erts/preloaded/ebin/prim_file.beam \
- $(ERL_TOP)/erts/preloaded/ebin/zlib.beam \
- $(ERL_TOP)/erts/preloaded/ebin/prim_zip.beam \
- $(ERL_TOP)/erts/preloaded/ebin/erl_prim_loader.beam \
- $(ERL_TOP)/erts/preloaded/ebin/erlang.beam \
- $(ERL_TOP)/erts/preloaded/ebin/erts_internal.beam \
- $(ERL_TOP)/erts/preloaded/ebin/erl_tracer.beam
+$(PRELOAD_SRC): $(PRELOAD_BEAM)
$(gen_verbose)LANG=C $(PERL) utils/make_preload -old $^ > $@
endif
diff --git a/erts/emulator/beam/atom.names b/erts/emulator/beam/atom.names
index badd69856e..2b66bf6f4e 100644
--- a/erts/emulator/beam/atom.names
+++ b/erts/emulator/beam/atom.names
@@ -65,6 +65,7 @@ atom undefined_lambda
atom DOWN='DOWN'
atom UP='UP'
atom EXIT='EXIT'
+atom abort
atom aborted
atom abs_path
atom absoluteURI
@@ -165,6 +166,7 @@ atom commandv
atom compact
atom compat_rel
atom compile
+atom complete
atom compressed
atom config_h
atom convert_time_unit
@@ -176,6 +178,7 @@ atom const
atom context_switches
atom control
atom copy
+atom copy_literals
atom counters
atom cpu
atom cpu_timestamp
@@ -195,6 +198,7 @@ atom dgroup_leader
atom dictionary
atom dirty_cpu
atom dirty_cpu_schedulers_online
+atom dirty_execution
atom dirty_io
atom disable_trace
atom disabled
@@ -235,6 +239,7 @@ atom erlang
atom ERROR='ERROR'
atom error_handler
atom error_logger
+atom erts_code_purger
atom erts_internal
atom ets
atom ETS_TRANSFER='ETS-TRANSFER'
@@ -384,8 +389,10 @@ atom merge_trap
atom meta
atom meta_match_spec
atom micro_seconds
+atom microsecond
atom microstate_accounting
atom milli_seconds
+atom millisecond
atom min_heap_size
atom min_bin_vheap_size
atom minor_version
@@ -402,11 +409,13 @@ atom more
atom multi_scheduling
atom multiline
atom nano_seconds
+atom nanosecond
atom name
atom named_table
atom namelist
atom native
atom native_addresses
+atom need_gc
atom Neq='=/='
atom Neqeq='/='
atom net_kernel
@@ -485,6 +494,7 @@ atom pause
atom pending
atom pending_driver
atom pending_process
+atom pending_purge_lambda
atom pending_reload
atom permanent
atom pid
@@ -494,6 +504,8 @@ atom port_count
atom port_limit
atom port_op
atom positive
+atom prepare
+atom prepare_on_load
atom print
atom priority
atom private
@@ -554,6 +566,7 @@ atom schedulers_online
atom scheme
atom scientific
atom scope
+atom second
atom seconds
atom send_to_non_existing_process
atom sensitive
diff --git a/erts/emulator/beam/beam_bif_load.c b/erts/emulator/beam/beam_bif_load.c
index 15e878ba65..5969197168 100644
--- a/erts/emulator/beam/beam_bif_load.c
+++ b/erts/emulator/beam/beam_bif_load.c
@@ -37,13 +37,93 @@
#include "erl_bits.h"
#include "erl_thr_progress.h"
+#ifdef HIPE
+# include "hipe_stack.h"
+#endif
+
+static struct {
+ Eterm module;
+ erts_smp_mtx_t mtx;
+ Export *pending_purge_lambda;
+ Eterm *sprocs;
+ Eterm def_sprocs[10];
+ Uint sp_size;
+ Uint sp_ix;
+ ErlFunEntry **funs;
+ ErlFunEntry *def_funs[10];
+ Uint fe_size;
+ Uint fe_ix;
+ struct erl_module_instance saved_old;
+} purge_state;
+
+Process *erts_code_purger = NULL;
+
+#ifdef ERTS_DIRTY_SCHEDULERS
+Process *erts_dirty_process_code_checker;
+#endif
+erts_smp_atomic_t erts_copy_literal_area__;
+#define ERTS_SET_COPY_LITERAL_AREA(LA) \
+ erts_smp_atomic_set_nob(&erts_copy_literal_area__, \
+ (erts_aint_t) (LA))
+#ifdef ERTS_NEW_PURGE_STRATEGY
+Process *erts_literal_area_collector = NULL;
+
+typedef struct ErtsLiteralAreaRef_ ErtsLiteralAreaRef;
+struct ErtsLiteralAreaRef_ {
+ ErtsLiteralAreaRef *next;
+ ErtsLiteralArea *literal_area;
+};
+
+struct {
+ erts_smp_mtx_t mtx;
+ ErtsLiteralAreaRef *first;
+ ErtsLiteralAreaRef *last;
+} release_literal_areas;
+#endif
+
static void set_default_trace_pattern(Eterm module);
static Eterm check_process_code(Process* rp, Module* modp, Uint flags, int *redsp, int fcalls);
static void delete_code(Module* modp);
-static void decrement_refc(BeamCodeHeader*);
static int any_heap_ref_ptrs(Eterm* start, Eterm* end, char* mod_start, Uint mod_size);
static int any_heap_refs(Eterm* start, Eterm* end, char* mod_start, Uint mod_size);
+static void
+init_purge_state(void)
+{
+ purge_state.module = THE_NON_VALUE;
+
+ erts_smp_mtx_init(&purge_state.mtx, "purge_state");
+
+ purge_state.pending_purge_lambda =
+ erts_export_put(am_erts_code_purger, am_pending_purge_lambda, 3);
+
+ purge_state.sprocs = &purge_state.def_sprocs[0];
+ purge_state.sp_size = sizeof(purge_state.def_sprocs);
+ purge_state.sp_size /= sizeof(purge_state.def_sprocs[0]);
+ purge_state.sp_ix = 0;
+
+ purge_state.funs = &purge_state.def_funs[0];
+ purge_state.fe_size = sizeof(purge_state.def_funs);
+ purge_state.fe_size /= sizeof(purge_state.def_funs[0]);
+ purge_state.fe_ix = 0;
+
+ purge_state.saved_old.code_hdr = 0;
+}
+
+void
+erts_beam_bif_load_init(void)
+{
+#ifdef ERTS_NEW_PURGE_STRATEGY
+ erts_smp_mtx_init(&release_literal_areas.mtx, "release_literal_areas");
+ release_literal_areas.first = NULL;
+ release_literal_areas.last = NULL;
+#endif
+ erts_smp_atomic_init_nob(&erts_copy_literal_area__,
+ (erts_aint_t) NULL);
+
+ init_purge_state();
+}
+
BIF_RETTYPE code_is_module_native_1(BIF_ALIST_1)
{
Module* modp;
@@ -516,6 +596,43 @@ badarg:
BIF_ERROR(BIF_P, BADARG);
}
+BIF_RETTYPE erts_internal_check_dirty_process_code_2(BIF_ALIST_2)
+{
+#if !defined(ERTS_DIRTY_SCHEDULERS)
+ BIF_ERROR(BIF_P, EXC_NOTSUP);
+#else
+ Process *rp;
+ int reds = 0;
+ Eterm res;
+
+ if (BIF_P != erts_dirty_process_code_checker)
+ BIF_ERROR(BIF_P, EXC_NOTSUP);
+
+ if (is_not_internal_pid(BIF_ARG_1))
+ BIF_ERROR(BIF_P, BADARG);
+
+ if (is_not_atom(BIF_ARG_2))
+ BIF_ERROR(BIF_P, BADARG);
+
+ rp = erts_pid2proc_not_running(BIF_P, ERTS_PROC_LOCK_MAIN,
+ BIF_ARG_1, ERTS_PROC_LOCK_MAIN);
+ if (rp == ERTS_PROC_LOCK_BUSY)
+ ERTS_BIF_YIELD2(bif_export[BIF_erts_internal_check_dirty_process_code_2],
+ BIF_P, BIF_ARG_1, BIF_ARG_2);
+ if (!rp)
+ BIF_RET(am_false);
+
+ res = erts_check_process_code(rp, BIF_ARG_2, 0, &reds, BIF_P->fcalls);
+
+ if (BIF_P != rp)
+ erts_smp_proc_unlock(rp, ERTS_PROC_LOCK_MAIN);
+
+ ASSERT(is_value(res));
+
+ BIF_RET2(res, reds);
+#endif
+}
+
BIF_RETTYPE delete_module_1(BIF_ALIST_1)
{
ErtsCodeIndex code_ix;
@@ -625,10 +742,13 @@ BIF_RETTYPE call_on_load_function_1(BIF_ALIST_1)
{
Module* modp = erts_get_module(BIF_ARG_1, erts_active_code_ix());
- if (modp && modp->old.code_hdr) {
- BIF_TRAP_CODE_PTR_0(BIF_P, modp->old.code_hdr->on_load_function_ptr);
+ if (!modp || !modp->on_load) {
+ BIF_ERROR(BIF_P, BADARG);
}
- else {
+ if (modp->on_load->code_hdr) {
+ BIF_TRAP_CODE_PTR_0(BIF_P,
+ modp->on_load->code_hdr->on_load_function_ptr);
+ } else {
BIF_ERROR(BIF_P, BADARG);
}
}
@@ -651,14 +771,14 @@ BIF_RETTYPE finish_after_on_load_2(BIF_ALIST_2)
code_ix = erts_active_code_ix();
modp = erts_get_module(BIF_ARG_1, code_ix);
- if (!modp || !modp->old.code_hdr) {
+ if (!modp || !modp->on_load || !modp->on_load->code_hdr) {
error:
erts_smp_thr_progress_unblock();
erts_smp_proc_lock(BIF_P, ERTS_PROC_LOCK_MAIN);
erts_release_code_write_permission();
BIF_ERROR(BIF_P, BADARG);
}
- if (modp->old.code_hdr->on_load_function_ptr == NULL) {
+ if (modp->on_load->code_hdr->on_load_function_ptr == NULL) {
goto error;
}
if (BIF_ARG_2 != am_false && BIF_ARG_2 != am_true) {
@@ -667,14 +787,17 @@ BIF_RETTYPE finish_after_on_load_2(BIF_ALIST_2)
if (BIF_ARG_2 == am_true) {
int i;
- struct erl_module_instance t;
/*
- * Swap old and new code.
+ * Make the code with the on_load function current.
*/
- t = modp->curr;
- modp->curr = modp->old;
- modp->old = t;
+
+ if (modp->curr.code_hdr) {
+ modp->old = modp->curr;
+ }
+ modp->curr = *modp->on_load;
+ erts_free(ERTS_ALC_T_PREPARED_CODE, modp->on_load);
+ modp->on_load = 0;
/*
* The on_load function succeded. Fix up export entries.
@@ -748,6 +871,8 @@ set_default_trace_pattern(Eterm module)
}
}
+#ifndef ERTS_NEW_PURGE_STRATEGY
+
static ERTS_INLINE int
check_mod_funs(Process *p, ErlOffHeap *off_heap, char *area, size_t area_size)
{
@@ -762,12 +887,249 @@ check_mod_funs(Process *p, ErlOffHeap *off_heap, char *area, size_t area_size)
return 0;
}
+#endif
+
static Uint hfrag_literal_size(Eterm* start, Eterm* end,
char* lit_start, Uint lit_size);
static void hfrag_literal_copy(Eterm **hpp, ErlOffHeap *ohp,
Eterm *start, Eterm *end,
char *lit_start, Uint lit_size);
+#ifdef ERTS_NEW_PURGE_STRATEGY
+
+Eterm
+erts_proc_copy_literal_area(Process *c_p, int *redsp, int fcalls, int gc_allowed)
+{
+ ErtsLiteralArea *la;
+ ErtsMessage *msgp;
+ struct erl_off_heap_header* oh;
+ char *literals;
+ Uint lit_bsize;
+ ErlHeapFragment *hfrag;
+
+ la = ERTS_COPY_LITERAL_AREA();
+ if (!la)
+ return am_ok;
+
+ oh = la->off_heap;
+ literals = (char *) &la->start[0];
+ lit_bsize = (char *) la->end - literals;
+
+ /*
+ * If a literal is in the message queue we make an explicit copy of
+ * it and attach it to the heap fragment. Each message needs to be
+ * self contained, we cannot save the literal in the old_heap or
+ * any other heap than the message it self.
+ */
+
+ erts_smp_proc_lock(c_p, ERTS_PROC_LOCK_MSGQ);
+ ERTS_SMP_MSGQ_MV_INQ2PRIVQ(c_p);
+ erts_smp_proc_unlock(c_p, ERTS_PROC_LOCK_MSGQ);
+
+ for (msgp = c_p->msg.first; msgp; msgp = msgp->next) {
+ ErlHeapFragment *hf;
+ Uint lit_sz = 0;
+
+ *redsp += 1;
+
+ if (msgp->data.attached == ERTS_MSG_COMBINED_HFRAG)
+ hfrag = &msgp->hfrag;
+ else if (is_value(ERL_MESSAGE_TERM(msgp)) && msgp->data.heap_frag)
+ hfrag = msgp->data.heap_frag;
+ else
+ continue; /* Content on heap or in external term format... */
+
+ for (hf = hfrag; hf; hf = hf->next) {
+ lit_sz += hfrag_literal_size(&hf->mem[0], &hf->mem[hf->used_size],
+ literals, lit_bsize);
+ *redsp += 1;
+ }
+
+ *redsp += lit_sz / 16; /* Better value needed... */
+ if (lit_sz > 0) {
+ ErlHeapFragment *bp = new_message_buffer(lit_sz);
+ Eterm *hp = bp->mem;
+
+ for (hf = hfrag; hf; hf = hf->next) {
+ hfrag_literal_copy(&hp, &bp->off_heap,
+ &hf->mem[0], &hf->mem[hf->used_size],
+ literals, lit_bsize);
+ hfrag = hf;
+ }
+
+ /* link new hfrag last */
+ ASSERT(hfrag->next == NULL);
+ hfrag->next = bp;
+ bp->next = NULL;
+ }
+ }
+
+ if (gc_allowed) {
+ /*
+ * Current implementation first tests without
+ * allowing GC, and then restarts the operation
+ * allowing GC if it is needed. It is therfore
+ * very likely that we will need the GC (although
+ * this is not completely certain). We go for
+ * the GC directly instead of scanning everything
+ * one more time...
+ */
+ goto literal_gc;
+ }
+
+ *redsp += 2;
+ if (any_heap_ref_ptrs(&c_p->fvalue, &c_p->fvalue+1, literals, lit_bsize)) {
+ c_p->freason = EXC_NULL;
+ c_p->fvalue = NIL;
+ c_p->ftrace = NIL;
+ }
+
+ if (any_heap_ref_ptrs(c_p->stop, c_p->hend, literals, lit_bsize))
+ goto literal_gc;
+ *redsp += 1;
+#ifdef HIPE
+ if (nstack_any_heap_ref_ptrs(c_p, literals, lit_bsize))
+ goto literal_gc;
+ *redsp += 1;
+#endif
+ if (any_heap_refs(c_p->heap, c_p->htop, literals, lit_bsize))
+ goto literal_gc;
+ *redsp += 1;
+ if (any_heap_refs(c_p->old_heap, c_p->old_htop, literals, lit_bsize))
+ goto literal_gc;
+
+ /* Check dictionary */
+ *redsp += 1;
+ if (c_p->dictionary) {
+ Eterm* start = ERTS_PD_START(c_p->dictionary);
+ Eterm* end = start + ERTS_PD_SIZE(c_p->dictionary);
+
+ if (any_heap_ref_ptrs(start, end, literals, lit_bsize))
+ goto literal_gc;
+ }
+
+ /* Check heap fragments */
+ for (hfrag = c_p->mbuf; hfrag; hfrag = hfrag->next) {
+ Eterm *hp, *hp_end;
+
+ *redsp += 1;
+
+ hp = &hfrag->mem[0];
+ hp_end = &hfrag->mem[hfrag->used_size];
+ if (any_heap_refs(hp, hp_end, literals, lit_bsize))
+ goto literal_gc;
+ }
+
+ /*
+ * Message buffer fragments (matched messages)
+ * - off heap lists should already have been moved into
+ * process off heap structure.
+ * - Check for literals
+ */
+ for (msgp = c_p->msg_frag; msgp; msgp = msgp->next) {
+ hfrag = erts_message_to_heap_frag(msgp);
+ for (; hfrag; hfrag = hfrag->next) {
+ Eterm *hp, *hp_end;
+
+ *redsp += 1;
+
+ hp = &hfrag->mem[0];
+ hp_end = &hfrag->mem[hfrag->used_size];
+
+ if (any_heap_refs(hp, hp_end, literals, lit_bsize))
+ goto literal_gc;
+ }
+ }
+
+ return am_ok;
+
+literal_gc:
+
+ if (!gc_allowed)
+ return am_need_gc;
+
+ if (c_p->flags & F_DISABLE_GC)
+ return THE_NON_VALUE;
+
+ FLAGS(c_p) |= F_NEED_FULLSWEEP;
+
+ *redsp += erts_garbage_collect_nobump(c_p, 0, c_p->arg_reg, c_p->arity, fcalls);
+
+ erts_garbage_collect_literals(c_p, (Eterm *) literals, lit_bsize, oh);
+
+ *redsp += lit_bsize / 64; /* Need, better value... */
+
+ return am_ok;
+}
+
+static Eterm
+check_process_code(Process* rp, Module* modp, Uint flags, int *redsp, int fcalls)
+{
+ BeamInstr* start;
+ char* mod_start;
+ Uint mod_size;
+ Eterm* sp;
+
+ *redsp += 1;
+
+ /*
+ * Pick up limits for the module.
+ */
+ start = (BeamInstr*) modp->old.code_hdr;
+ mod_start = (char *) start;
+ mod_size = modp->old.code_length;
+
+ /*
+ * Check if current instruction or continuation pointer points into module.
+ */
+ if (ErtsInArea(rp->i, mod_start, mod_size)
+ || ErtsInArea(rp->cp, mod_start, mod_size)) {
+ return am_true;
+ }
+
+ *redsp += (STACK_START(rp) - rp->stop) / 32;
+
+ /*
+ * Check all continuation pointers stored on the stack.
+ */
+ for (sp = rp->stop; sp < STACK_START(rp); sp++) {
+ if (is_CP(*sp) && ErtsInArea(cp_val(*sp), mod_start, mod_size)) {
+ return am_true;
+ }
+ }
+
+ /*
+ * Check all continuation pointers stored in stackdump
+ * and clear exception stackdump if there is a pointer
+ * to the module.
+ */
+ if (rp->ftrace != NIL) {
+ struct StackTrace *s;
+ ASSERT(is_list(rp->ftrace));
+ s = (struct StackTrace *) big_val(CDR(list_val(rp->ftrace)));
+ if ((s->pc && ErtsInArea(s->pc, mod_start, mod_size)) ||
+ (s->current && ErtsInArea(s->current, mod_start, mod_size))) {
+ rp->freason = EXC_NULL;
+ rp->fvalue = NIL;
+ rp->ftrace = NIL;
+ } else {
+ int i;
+ for (i = 0; i < s->depth; i++) {
+ if (ErtsInArea(s->trace[i], mod_start, mod_size)) {
+ rp->freason = EXC_NULL;
+ rp->fvalue = NIL;
+ rp->ftrace = NIL;
+ break;
+ }
+ }
+ }
+ }
+
+ return am_false;
+}
+
+#else /* !ERTS_NEW_PURGE_STRATEGY, i.e, old style purge... */
+
static Eterm
check_process_code(Process* rp, Module* modp, Uint flags, int *redsp, int fcalls)
{
@@ -861,8 +1223,14 @@ check_process_code(Process* rp, Module* modp, Uint flags, int *redsp, int fcalls
ERTS_SMP_MSGQ_MV_INQ2PRIVQ(rp);
erts_smp_proc_unlock(rp, ERTS_PROC_LOCK_MSGQ);
- literals = (char*) modp->old.code_hdr->literals_start;
- lit_bsize = (char*) modp->old.code_hdr->literals_end - literals;
+ if (modp->old.code_hdr->literal_area) {
+ literals = (char*) modp->old.code_hdr->literal_area->start;
+ lit_bsize = (char*) modp->old.code_hdr->literal_area->end - literals;
+ }
+ else {
+ literals = NULL;
+ lit_bsize = 0;
+ }
for (msgp = rp->msg.first; msgp; msgp = msgp->next) {
if (msgp->data.attached == ERTS_MSG_COMBINED_HFRAG)
@@ -903,12 +1271,6 @@ check_process_code(Process* rp, Module* modp, Uint flags, int *redsp, int fcalls
/* Check heap, stack etc... */
if (check_mod_funs(rp, &rp->off_heap, mod_start, mod_size))
goto try_gc;
- if (!(flags & ERTS_CPC_COPY_LITERALS)) {
- /* Process ok. May contain old literals but we will be called
- * again before module is purged.
- */
- return am_false;
- }
if (any_heap_ref_ptrs(&rp->fvalue, &rp->fvalue+1, literals, lit_bsize)) {
rp->freason = EXC_NULL;
rp->fvalue = NIL;
@@ -916,6 +1278,10 @@ check_process_code(Process* rp, Module* modp, Uint flags, int *redsp, int fcalls
}
if (any_heap_ref_ptrs(rp->stop, rp->hend, literals, lit_bsize))
goto try_literal_gc;
+#ifdef HIPE
+ if (nstack_any_heap_ref_ptrs(rp, literals, lit_bsize))
+ goto try_literal_gc;
+#endif
if (any_heap_refs(rp->heap, rp->htop, literals, lit_bsize))
goto try_literal_gc;
if (any_heap_refs(rp->old_heap, rp->old_htop, literals, lit_bsize))
@@ -994,7 +1360,7 @@ check_process_code(Process* rp, Module* modp, Uint flags, int *redsp, int fcalls
}
if (need_gc & ERTS_LITERAL_GC__) {
struct erl_off_heap_header* oh;
- oh = modp->old.code_hdr->literals_off_heap;
+ oh = modp->old.code_hdr->literal_area->off_heap;
*redsp += lit_bsize / 64; /* Need, better value... */
erts_garbage_collect_literals(rp, (Eterm*)literals, lit_bsize, oh);
done_gc |= ERTS_LITERAL_GC__;
@@ -1007,6 +1373,8 @@ check_process_code(Process* rp, Module* modp, Uint flags, int *redsp, int fcalls
}
+#endif /* !ERTS_NEW_PURGE_STRATEGY */
+
static int
any_heap_ref_ptrs(Eterm* start, Eterm* end, char* mod_start, Uint mod_size)
{
@@ -1135,200 +1503,502 @@ hfrag_literal_copy(Eterm **hpp, ErlOffHeap *ohp,
}
}
-#undef in_area
+#ifdef ERTS_NEW_PURGE_STRATEGY
#ifdef ERTS_SMP
-static void copy_literals_commit(void*);
-#endif
-copy_literals_t erts_clrange = {NULL, 0, THE_NON_VALUE};
+ErtsThrPrgrLaterOp later_literal_area_switch;
-/* copy literals
- *
- * copy_literals.ptr = LitPtr
- * copy_literals.sz = LitSz
- * ------ THR PROG COMMIT -----
- *
- * - check process code
- * - check process code
- * ...
- * copy_literals.ptr = NULL
- * copy_literals.sz = 0
- * ------ THR PROG COMMIT -----
- * ...
- */
+typedef struct {
+ ErtsThrPrgrLaterOp lop;
+ ErtsLiteralArea *la;
+} ErtsLaterReleasLiteralArea;
+
+static void
+later_release_literal_area(void *vlrlap)
+{
+ ErtsLaterReleasLiteralArea *lrlap;
+ lrlap = (ErtsLaterReleasLiteralArea *) vlrlap;
+ erts_release_literal_area(lrlap->la);
+ erts_free(ERTS_ALC_T_RELEASE_LAREA, vlrlap);
+}
+static void
+complete_literal_area_switch(void *literal_area)
+{
+ Process *p = erts_literal_area_collector;
+ erts_smp_proc_lock(p, ERTS_PROC_LOCK_STATUS);
+ erts_resume(p, ERTS_PROC_LOCK_STATUS);
+ erts_smp_proc_unlock(p, ERTS_PROC_LOCK_STATUS);
+ if (literal_area)
+ erts_release_literal_area((ErtsLiteralArea *) literal_area);
+}
+#endif
-BIF_RETTYPE erts_internal_copy_literals_2(BIF_ALIST_2)
+#endif /* ERTS_NEW_PURGE_STRATEGY */
+
+BIF_RETTYPE erts_internal_release_literal_area_switch_0(BIF_ALIST_0)
{
- ErtsCodeIndex code_ix;
- Eterm res = am_true;
+#ifndef ERTS_NEW_PURGE_STRATEGY
+ BIF_ERROR(BIF_P, EXC_NOTSUP);
+#else
+ ErtsLiteralArea *unused_la;
+ ErtsLiteralAreaRef *la_ref;
- if (is_not_atom(BIF_ARG_1) || (am_true != BIF_ARG_2 && am_false != BIF_ARG_2)) {
- BIF_ERROR(BIF_P, BADARG);
- }
+ if (BIF_P != erts_literal_area_collector)
+ BIF_ERROR(BIF_P, EXC_NOTSUP);
- if (!erts_try_seize_code_write_permission(BIF_P)) {
- ERTS_BIF_YIELD2(bif_export[BIF_erts_internal_copy_literals_2],
- BIF_P, BIF_ARG_1, BIF_ARG_2);
+ erts_smp_mtx_lock(&release_literal_areas.mtx);
+
+ la_ref = release_literal_areas.first;
+ if (la_ref) {
+ release_literal_areas.first = la_ref->next;
+ if (!release_literal_areas.first)
+ release_literal_areas.last = NULL;
}
- code_ix = erts_active_code_ix();
+ erts_smp_mtx_unlock(&release_literal_areas.mtx);
- if (BIF_ARG_2 == am_true) {
- Module* modp = erts_get_module(BIF_ARG_1, code_ix);
- if (!modp || !modp->old.code_hdr) {
- res = am_false;
- goto done;
- }
- if (erts_clrange.ptr != NULL
- && !(BIF_P->static_flags & ERTS_STC_FLG_SYSTEM_PROC)) {
- res = am_aborted;
- goto done;
- }
- erts_clrange.ptr = modp->old.code_hdr->literals_start;
- erts_clrange.sz = modp->old.code_hdr->literals_end - erts_clrange.ptr;
- erts_clrange.pid = BIF_P->common.id;
- } else if (BIF_ARG_2 == am_false) {
- if (erts_clrange.pid != BIF_P->common.id) {
- res = am_false;
- goto done;
- }
- erts_clrange.ptr = NULL;
- erts_clrange.sz = 0;
- erts_clrange.pid = THE_NON_VALUE;
+ unused_la = ERTS_COPY_LITERAL_AREA();
+
+ if (!la_ref) {
+ ERTS_SET_COPY_LITERAL_AREA(NULL);
+ if (unused_la) {
+#ifdef ERTS_SMP
+ ErtsLaterReleasLiteralArea *lrlap;
+ lrlap = erts_alloc(ERTS_ALC_T_RELEASE_LAREA,
+ sizeof(ErtsLaterReleasLiteralArea));
+ lrlap->la = unused_la;
+ erts_schedule_thr_prgr_later_cleanup_op(
+ later_release_literal_area,
+ (void *) lrlap,
+ &lrlap->lop,
+ (sizeof(ErtsLaterReleasLiteralArea)
+ + sizeof(ErtsLiteralArea)
+ + ((unused_la->end
+ - &unused_la->start[0])
+ - 1)*(sizeof(Eterm))));
+#else
+ erts_release_literal_area(unused_la);
+#endif
+ }
+ BIF_RET(am_false);
}
+ ERTS_SET_COPY_LITERAL_AREA(la_ref->literal_area);
+
+ erts_free(ERTS_ALC_T_LITERAL_REF, la_ref);
+
#ifdef ERTS_SMP
- ASSERT(committer_state.stager == NULL);
- committer_state.stager = BIF_P;
- erts_schedule_thr_prgr_later_op(copy_literals_commit, NULL, &committer_state.lop);
- erts_proc_inc_refc(BIF_P);
+ erts_schedule_thr_prgr_later_op(complete_literal_area_switch,
+ unused_la,
+ &later_literal_area_switch);
erts_suspend(BIF_P, ERTS_PROC_LOCK_MAIN, NULL);
ERTS_BIF_YIELD_RETURN(BIF_P, am_true);
+#else
+ erts_release_literal_area(unused_la);
+ BIF_RET(am_true);
#endif
-done:
- erts_release_code_write_permission();
- BIF_RET(res);
+
+#endif /* ERTS_NEW_PURGE_STRATEGY */
+}
+
+void
+erts_purge_state_add_fun(ErlFunEntry *fe)
+{
+ ASSERT(is_value(purge_state.module));
+ if (purge_state.fe_ix >= purge_state.fe_size) {
+ ErlFunEntry **funs;
+ purge_state.fe_size += 100;
+ funs = erts_alloc(ERTS_ALC_T_PURGE_DATA,
+ sizeof(ErlFunEntry *)*purge_state.fe_size);
+ sys_memcpy((void *) funs,
+ (void *) purge_state.funs,
+ purge_state.fe_ix*sizeof(ErlFunEntry *));
+ if (purge_state.funs != &purge_state.def_funs[0])
+ erts_free(ERTS_ALC_T_PURGE_DATA, purge_state.funs);
+ purge_state.funs = funs;
+ }
+ purge_state.funs[purge_state.fe_ix++] = fe;
+}
+
+Export *
+erts_suspend_process_on_pending_purge_lambda(Process *c_p)
+{
+ erts_smp_mtx_lock(&purge_state.mtx);
+ if (is_value(purge_state.module)) {
+ /*
+ * The process c_p is about to call a fun in the code
+ * that we are trying to purge. Suspend it and call
+ * erts_code_purger:pending_purge_lambda/3. The process
+ * will be resumed when the purge completes or aborts,
+ * and will then try to do the call again.
+ */
+ if (purge_state.sp_ix >= purge_state.sp_size) {
+ Eterm *sprocs;
+ purge_state.sp_size += 100;
+ sprocs = erts_alloc(ERTS_ALC_T_PURGE_DATA,
+ (sizeof(ErlFunEntry *)
+ * purge_state.sp_size));
+ sys_memcpy((void *) sprocs,
+ (void *) purge_state.sprocs,
+ purge_state.sp_ix*sizeof(ErlFunEntry *));
+ if (purge_state.sprocs != &purge_state.def_sprocs[0])
+ erts_free(ERTS_ALC_T_PURGE_DATA, purge_state.sprocs);
+ purge_state.sprocs = sprocs;
+ }
+ purge_state.sprocs[purge_state.sp_ix++] = c_p->common.id;
+ erts_suspend(c_p, ERTS_PROC_LOCK_MAIN, NULL);
+ ERTS_VBUMP_ALL_REDS(c_p);
+ }
+ erts_smp_mtx_unlock(&purge_state.mtx);
+ return purge_state.pending_purge_lambda;
+}
+
+static void
+finalize_purge_operation(Process *c_p, int succeded)
+{
+ Uint ix;
+
+ if (c_p)
+ erts_smp_proc_unlock(c_p, ERTS_PROC_LOCK_MAIN);
+
+ erts_smp_mtx_lock(&purge_state.mtx);
+
+ ASSERT(purge_state.module != THE_NON_VALUE);
+
+ purge_state.module = THE_NON_VALUE;
+
+ /*
+ * Resume all processes that have tried to call
+ * funs in this code.
+ */
+ for (ix = 0; ix < purge_state.sp_ix; ix++) {
+ Process *rp = erts_pid2proc(NULL, 0,
+ purge_state.sprocs[ix],
+ ERTS_PROC_LOCK_STATUS);
+ if (rp) {
+ erts_resume(rp, ERTS_PROC_LOCK_STATUS);
+ erts_smp_proc_unlock(rp, ERTS_PROC_LOCK_STATUS);
+ }
+ }
+
+ erts_smp_mtx_unlock(&purge_state.mtx);
+
+ if (c_p)
+ erts_smp_proc_lock(c_p, ERTS_PROC_LOCK_MAIN);
+
+ if (purge_state.sprocs != &purge_state.def_sprocs[0]) {
+ erts_free(ERTS_ALC_T_PURGE_DATA, purge_state.sprocs);
+ purge_state.sprocs = &purge_state.def_sprocs[0];
+ purge_state.sp_size = sizeof(purge_state.def_sprocs);
+ purge_state.sp_size /= sizeof(purge_state.def_sprocs[0]);
+ }
+ purge_state.sp_ix = 0;
+
+ if (purge_state.funs != &purge_state.def_funs[0]) {
+ erts_free(ERTS_ALC_T_PURGE_DATA, purge_state.funs);
+ purge_state.funs = &purge_state.def_funs[0];
+ purge_state.fe_size = sizeof(purge_state.def_funs);
+ purge_state.fe_size /= sizeof(purge_state.def_funs[0]);
+ }
+ purge_state.fe_ix = 0;
}
#ifdef ERTS_SMP
-static void copy_literals_commit(void* null) {
- Process* p = committer_state.stager;
-#ifdef DEBUG
- committer_state.stager = NULL;
-#endif
- erts_release_code_write_permission();
+
+static ErtsThrPrgrLaterOp purger_lop_data;
+
+static void
+resume_purger(void *unused)
+{
+ Process *p = erts_code_purger;
erts_smp_proc_lock(p, ERTS_PROC_LOCK_STATUS);
- if (!ERTS_PROC_IS_EXITING(p)) {
- erts_resume(p, ERTS_PROC_LOCK_STATUS);
- }
+ erts_resume(p, ERTS_PROC_LOCK_STATUS);
erts_smp_proc_unlock(p, ERTS_PROC_LOCK_STATUS);
- erts_proc_dec_refc(p);
}
-#endif /* ERTS_SMP */
+static void
+finalize_purge_abort(void *unused)
+{
+ erts_fun_purge_abort_finalize(purge_state.funs, purge_state.fe_ix);
+
+ finalize_purge_operation(NULL, 0);
-/* Do the actualy module purging and return:
- * true for success
- * false if no such old module
- * BADARG if not an atom
- */
-BIF_RETTYPE erts_internal_purge_module_1(BIF_ALIST_1)
+ resume_purger(NULL);
+}
+
+#endif /* ERTS_SMP */
+
+BIF_RETTYPE erts_internal_purge_module_2(BIF_ALIST_2)
{
- ErtsCodeIndex code_ix;
- BeamInstr* code;
- BeamInstr* end;
- Module* modp;
- int is_blocking = 0;
- Eterm ret;
+ if (BIF_P != erts_code_purger)
+ BIF_ERROR(BIF_P, EXC_NOTSUP);
- if (is_not_atom(BIF_ARG_1)) {
+ if (is_not_atom(BIF_ARG_1))
BIF_ERROR(BIF_P, BADARG);
- }
- if (!erts_try_seize_code_write_permission(BIF_P)) {
- ERTS_BIF_YIELD1(bif_export[BIF_erts_internal_purge_module_1],
- BIF_P, BIF_ARG_1);
- }
+ switch (BIF_ARG_2) {
- code_ix = erts_active_code_ix();
+ case am_prepare:
+ case am_prepare_on_load: {
+ /*
+ * Prepare for purge by marking all fun
+ * entries referring to the code to purge
+ * with "pending purge" markers.
+ */
+ ErtsCodeIndex code_ix;
+ Module* modp;
+ Eterm res;
- /*
- * Correct module?
- */
+ if (is_value(purge_state.module))
+ BIF_ERROR(BIF_P, BADARG);
- if ((modp = erts_get_module(BIF_ARG_1, code_ix)) == NULL) {
- ERTS_BIF_PREP_RET(ret, am_false);
- }
- else {
- erts_rwlock_old_code(code_ix);
+ code_ix = erts_active_code_ix();
/*
- * Any code to purge?
+ * Correct module?
*/
- if (!modp->old.code_hdr) {
- ERTS_BIF_PREP_RET(ret, am_false);
- }
+ modp = erts_get_module(BIF_ARG_1, code_ix);
+ if (!modp)
+ res = am_false;
else {
/*
- * Unload any NIF library
+ * Any code to purge?
*/
- if (modp->old.nif != NULL) {
- /* ToDo: Do unload nif without blocking */
- erts_rwunlock_old_code(code_ix);
- erts_smp_proc_unlock(BIF_P, ERTS_PROC_LOCK_MAIN);
- erts_smp_thr_progress_block();
- is_blocking = 1;
- erts_rwlock_old_code(code_ix);
- erts_unload_nif(modp->old.nif);
- modp->old.nif = NULL;
+
+ if (BIF_ARG_2 == am_prepare_on_load) {
+ erts_rwlock_old_code(code_ix);
+ } else {
+ erts_rlock_old_code(code_ix);
+ }
+
+ if (BIF_ARG_2 == am_prepare_on_load) {
+ ASSERT(modp->on_load);
+ ASSERT(modp->on_load->code_hdr);
+ purge_state.saved_old = modp->old;
+ modp->old = *modp->on_load;
+ erts_free(ERTS_ALC_T_PREPARED_CODE, (void *) modp->on_load);
+ modp->on_load = 0;
}
+ if (!modp->old.code_hdr)
+ res = am_false;
+ else {
+ BeamInstr* code;
+ BeamInstr* end;
+ erts_smp_mtx_lock(&purge_state.mtx);
+ purge_state.module = BIF_ARG_1;
+ erts_smp_mtx_unlock(&purge_state.mtx);
+ res = am_true;
+ code = (BeamInstr*) modp->old.code_hdr;
+ end = (BeamInstr *)((char *)code + modp->old.code_length);
+ erts_fun_purge_prepare(code, end);
+#if !defined(ERTS_NEW_PURGE_STRATEGY)
+ ASSERT(!ERTS_COPY_LITERAL_AREA());
+ ERTS_SET_COPY_LITERAL_AREA(modp->old.code_hdr->literal_area);
+#endif
+ }
+
+ if (BIF_ARG_2 == am_prepare_on_load) {
+ erts_rwunlock_old_code(code_ix);
+ } else {
+ erts_runlock_old_code(code_ix);
+ }
+ }
+
+#ifndef ERTS_SMP
+ BIF_RET(res);
+#else
+ if (res != am_true)
+ BIF_RET(res);
+ else {
/*
- * Remove the old code.
+ * We'll be resumed when all schedulers are guaranteed
+ * to see the "pending purge" markers that we've made on
+ * all fun entries of the code that we are about to purge.
+ * Processes trying to call these funs will be suspended
+ * before calling the funs. That is we are guaranteed not
+ * to get any more direct references into the code while
+ * checking for such references...
*/
- ASSERT(erts_total_code_size >= modp->old.code_length);
- erts_total_code_size -= modp->old.code_length;
- code = (BeamInstr*) modp->old.code_hdr;
- end = (BeamInstr *)((char *)code + modp->old.code_length);
- erts_cleanup_funs_on_purge(code, end);
- beam_catches_delmod(modp->old.catches, code, modp->old.code_length,
- code_ix);
- decrement_refc(modp->old.code_hdr);
- if (modp->old.code_hdr->literals_start) {
- erts_free(ERTS_ALC_T_LITERAL, modp->old.code_hdr->literals_start);
- }
- erts_free(ERTS_ALC_T_CODE, (void *) code);
- modp->old.code_hdr = NULL;
- modp->old.code_length = 0;
- modp->old.catches = BEAM_CATCHES_NIL;
- erts_remove_from_ranges(code);
- ERTS_BIF_PREP_RET(ret, am_true);
+ erts_schedule_thr_prgr_later_op(resume_purger,
+ NULL,
+ &purger_lop_data);
+ erts_suspend(BIF_P, ERTS_PROC_LOCK_MAIN, NULL);
+ ERTS_BIF_YIELD_RETURN(BIF_P, am_true);
}
- erts_rwunlock_old_code(code_ix);
+#endif
}
- if (is_blocking) {
- erts_smp_thr_progress_unblock();
- erts_smp_proc_lock(BIF_P, ERTS_PROC_LOCK_MAIN);
+
+ case am_abort: {
+ /*
+ * Soft purge that detected direct references into the code
+ * we set out to purge. Abort the purge.
+ */
+
+ if (purge_state.module != BIF_ARG_1)
+ BIF_ERROR(BIF_P, BADARG);
+
+ erts_fun_purge_abort_prepare(purge_state.funs, purge_state.fe_ix);
+
+#if !defined(ERTS_NEW_PURGE_STRATEGY)
+ ASSERT(ERTS_COPY_LITERAL_AREA());
+ ERTS_SET_COPY_LITERAL_AREA(NULL);
+#endif
+#ifndef ERTS_SMP
+ erts_fun_purge_abort_finalize(purge_state.funs, purge_state.fe_ix);
+ finalize_purge_operation(BIF_P, 0);
+ BIF_RET(am_false);
+#else
+ /*
+ * We need to restore the code addresses of the funs in
+ * two stages in order to ensure that we do not get any
+ * stale suspended processes due to the purge abort.
+ * Restore address pointer (erts_fun_purge_abort_prepare);
+ * wait for thread progress; clear pending purge address
+ * pointer (erts_fun_purge_abort_finalize), and then
+ * resume processes that got suspended
+ * (finalize_purge_operation).
+ */
+ erts_schedule_thr_prgr_later_op(finalize_purge_abort,
+ NULL,
+ &purger_lop_data);
+ erts_suspend(BIF_P, ERTS_PROC_LOCK_MAIN, NULL);
+ ERTS_BIF_YIELD_RETURN(BIF_P, am_false);
+#endif
}
- erts_release_code_write_permission();
- return ret;
-}
-static void
-decrement_refc(BeamCodeHeader* code_hdr)
-{
- struct erl_off_heap_header* oh = code_hdr->literals_off_heap;
-
- while (oh) {
- Binary* bptr;
- ASSERT(thing_subtag(oh->thing_word) == REFC_BINARY_SUBTAG);
- bptr = ((ProcBin*)oh)->val;
- if (erts_refc_dectest(&bptr->refc, 0) == 0) {
- erts_bin_free(bptr);
+ case am_complete: {
+ ErtsCodeIndex code_ix;
+ BeamInstr* code;
+ Module* modp;
+ int is_blocking = 0;
+ Eterm ret;
+ ErtsLiteralArea *literals = NULL;
+
+
+ /*
+ * We have no direct references into the code.
+ * Complete to purge.
+ */
+
+ if (purge_state.module != BIF_ARG_1)
+ BIF_ERROR(BIF_P, BADARG);
+
+ if (!erts_try_seize_code_write_permission(BIF_P)) {
+ ERTS_BIF_YIELD2(bif_export[BIF_erts_internal_purge_module_2],
+ BIF_P, BIF_ARG_1, BIF_ARG_2);
+ }
+
+ code_ix = erts_active_code_ix();
+
+ /*
+ * Correct module?
+ */
+
+ if ((modp = erts_get_module(BIF_ARG_1, code_ix)) == NULL) {
+ ERTS_BIF_PREP_RET(ret, am_false);
+ }
+ else {
+
+ erts_rwlock_old_code(code_ix);
+
+ /*
+ * Any code to purge?
+ */
+ if (!modp->old.code_hdr) {
+ ERTS_BIF_PREP_RET(ret, am_false);
+ }
+ else {
+ /*
+ * Unload any NIF library
+ */
+ if (modp->old.nif != NULL) {
+ /* ToDo: Do unload nif without blocking */
+ erts_rwunlock_old_code(code_ix);
+ erts_smp_proc_unlock(BIF_P, ERTS_PROC_LOCK_MAIN);
+ erts_smp_thr_progress_block();
+ is_blocking = 1;
+ erts_rwlock_old_code(code_ix);
+ erts_unload_nif(modp->old.nif);
+ modp->old.nif = NULL;
+ }
+
+ /*
+ * Remove the old code.
+ */
+ ASSERT(erts_total_code_size >= modp->old.code_length);
+ erts_total_code_size -= modp->old.code_length;
+ code = (BeamInstr*) modp->old.code_hdr;
+ erts_fun_purge_complete(purge_state.funs, purge_state.fe_ix);
+ beam_catches_delmod(modp->old.catches, code, modp->old.code_length,
+ code_ix);
+ literals = modp->old.code_hdr->literal_area;
+ modp->old.code_hdr->literal_area = NULL;
+ erts_free(ERTS_ALC_T_CODE, (void *) code);
+ modp->old.code_hdr = NULL;
+ modp->old.code_length = 0;
+ modp->old.catches = BEAM_CATCHES_NIL;
+ erts_remove_from_ranges(code);
+
+ ERTS_BIF_PREP_RET(ret, am_true);
+ }
+
+ if (purge_state.saved_old.code_hdr) {
+ modp->old = purge_state.saved_old;
+ purge_state.saved_old.code_hdr = 0;
+ }
+ erts_rwunlock_old_code(code_ix);
+ }
+ if (is_blocking) {
+ erts_smp_thr_progress_unblock();
+ erts_smp_proc_lock(BIF_P, ERTS_PROC_LOCK_MAIN);
}
- oh = oh->next;
+
+ erts_release_code_write_permission();
+
+ finalize_purge_operation(BIF_P, ret == am_true);
+
+#if !defined(ERTS_NEW_PURGE_STRATEGY)
+
+ ASSERT(ERTS_COPY_LITERAL_AREA() == literals);
+ ERTS_SET_COPY_LITERAL_AREA(NULL);
+ erts_release_literal_area(literals);
+
+#else /* ERTS_NEW_PURGE_STRATEGY */
+
+ if (literals) {
+ ErtsLiteralAreaRef *ref;
+ ref = erts_alloc(ERTS_ALC_T_LITERAL_REF,
+ sizeof(ErtsLiteralAreaRef));
+ ref->literal_area = literals;
+ ref->next = NULL;
+ erts_smp_mtx_lock(&release_literal_areas.mtx);
+ if (release_literal_areas.last) {
+ release_literal_areas.last->next = ref;
+ release_literal_areas.last = ref;
+ }
+ else {
+ release_literal_areas.first = ref;
+ release_literal_areas.last = ref;
+ }
+ erts_smp_mtx_unlock(&release_literal_areas.mtx);
+ erts_queue_message(erts_literal_area_collector,
+ 0,
+ erts_alloc_message(0, NULL),
+ am_copy_literals,
+ BIF_P->common.id);
+ }
+
+#endif /* ERTS_NEW_PURGE_STRATEGY */
+
+ return ret;
+ }
+
+ default:
+ BIF_ERROR(BIF_P, BADARG);
+
}
}
diff --git a/erts/emulator/beam/beam_emu.c b/erts/emulator/beam/beam_emu.c
index 7419a62058..ef4cdf9d5a 100644
--- a/erts/emulator/beam/beam_emu.c
+++ b/erts/emulator/beam/beam_emu.c
@@ -1960,6 +1960,8 @@ void process_main(void)
ErtsMessage* msgp;
PROCESS_MAIN_CHK_LOCKS(c_p);
+ ERTS_CHK_MBUF_SZ(c_p);
+
PreFetch(0, next);
msgp = PEEK_MESSAGE(c_p);
@@ -2047,6 +2049,7 @@ void process_main(void)
}
ERTS_DBG_CHK_REDS(c_p, FCALLS);
+ ERTS_CHK_MBUF_SZ(c_p);
ERTS_VERIFY_UNUSED_TEMP_ALLOC(c_p);
PROCESS_MAIN_CHK_LOCKS(c_p);
@@ -2161,7 +2164,10 @@ void process_main(void)
c_p->i = (BeamInstr *) Arg(0); /* L1 */
SWAPOUT;
c_p->arity = 0;
- erts_smp_atomic32_read_band_relb(&c_p->state, ~ERTS_PSFLG_ACTIVE);
+
+ if (!ERTS_PTMR_IS_TIMED_OUT(c_p))
+ erts_smp_atomic32_read_band_relb(&c_p->state,
+ ~ERTS_PSFLG_ACTIVE);
ASSERT(!ERTS_PROC_IS_EXITING(c_p));
erts_smp_proc_unlock(c_p, ERTS_PROC_LOCKS_MSG_RECEIVE);
c_p->current = NULL;
@@ -2580,7 +2586,9 @@ do { \
c_p->fcalls = FCALLS;
PROCESS_MAIN_CHK_LOCKS(c_p);
ASSERT(!ERTS_PROC_IS_EXITING(c_p));
+ ERTS_CHK_MBUF_SZ(c_p);
result = (*bf)(c_p, tmp_reg);
+ ERTS_CHK_MBUF_SZ(c_p);
ASSERT(!ERTS_PROC_IS_EXITING(c_p) || is_non_value(result));
ERTS_VERIFY_UNUSED_TEMP_ALLOC(c_p);
PROCESS_MAIN_CHK_LOCKS(c_p);
@@ -2611,7 +2619,9 @@ do { \
c_p->fcalls = FCALLS;
PROCESS_MAIN_CHK_LOCKS(c_p);
ASSERT(!ERTS_PROC_IS_EXITING(c_p));
+ ERTS_CHK_MBUF_SZ(c_p);
result = (*bf)(c_p, tmp_reg);
+ ERTS_CHK_MBUF_SZ(c_p);
ASSERT(!ERTS_PROC_IS_EXITING(c_p) || is_non_value(result));
ERTS_VERIFY_UNUSED_TEMP_ALLOC(c_p);
PROCESS_MAIN_CHK_LOCKS(c_p);
@@ -2641,7 +2651,9 @@ do { \
SWAPOUT;
PROCESS_MAIN_CHK_LOCKS(c_p);
ERTS_SMP_UNREQ_PROC_MAIN_LOCK(c_p);
+ ERTS_CHK_MBUF_SZ(c_p);
result = (*bf)(c_p, reg, live);
+ ERTS_CHK_MBUF_SZ(c_p);
ERTS_VERIFY_UNUSED_TEMP_ALLOC(c_p);
ERTS_SMP_REQ_PROC_MAIN_LOCK(c_p);
PROCESS_MAIN_CHK_LOCKS(c_p);
@@ -2682,7 +2694,9 @@ do { \
SWAPOUT;
PROCESS_MAIN_CHK_LOCKS(c_p);
ERTS_SMP_UNREQ_PROC_MAIN_LOCK(c_p);
+ ERTS_CHK_MBUF_SZ(c_p);
result = (*bf)(c_p, reg, live);
+ ERTS_CHK_MBUF_SZ(c_p);
ERTS_VERIFY_UNUSED_TEMP_ALLOC(c_p);
ERTS_SMP_REQ_PROC_MAIN_LOCK(c_p);
PROCESS_MAIN_CHK_LOCKS(c_p);
@@ -2725,7 +2739,9 @@ do { \
SWAPOUT;
PROCESS_MAIN_CHK_LOCKS(c_p);
ERTS_SMP_UNREQ_PROC_MAIN_LOCK(c_p);
+ ERTS_CHK_MBUF_SZ(c_p);
result = (*bf)(c_p, reg, live);
+ ERTS_CHK_MBUF_SZ(c_p);
ERTS_VERIFY_UNUSED_TEMP_ALLOC(c_p);
ERTS_SMP_REQ_PROC_MAIN_LOCK(c_p);
PROCESS_MAIN_CHK_LOCKS(c_p);
@@ -2763,7 +2779,9 @@ do { \
c_p->fcalls = FCALLS;
PROCESS_MAIN_CHK_LOCKS(c_p);
ASSERT(!ERTS_PROC_IS_EXITING(c_p));
+ ERTS_CHK_MBUF_SZ(c_p);
result = (*bf)(c_p, tmp_reg);
+ ERTS_CHK_MBUF_SZ(c_p);
ASSERT(!ERTS_PROC_IS_EXITING(c_p) || is_non_value(result));
ERTS_VERIFY_UNUSED_TEMP_ALLOC(c_p);
PROCESS_MAIN_CHK_LOCKS(c_p);
@@ -2790,7 +2808,9 @@ do { \
bf = (BifFunction) Arg(0);
PROCESS_MAIN_CHK_LOCKS(c_p);
ASSERT(!ERTS_PROC_IS_EXITING(c_p));
+ ERTS_CHK_MBUF_SZ(c_p);
result = (*bf)(c_p, tmp_reg);
+ ERTS_CHK_MBUF_SZ(c_p);
ASSERT(!ERTS_PROC_IS_EXITING(c_p) || is_non_value(result));
ERTS_VERIFY_UNUSED_TEMP_ALLOC(c_p);
PROCESS_MAIN_CHK_LOCKS(c_p);
@@ -2840,7 +2860,9 @@ do { \
ASSERT(!ERTS_PROC_IS_EXITING(c_p));
ERTS_VERIFY_UNUSED_TEMP_ALLOC(c_p);
live_hf_end = c_p->mbuf;
+ ERTS_CHK_MBUF_SZ(c_p);
result = (*bf)(c_p, reg, I);
+ ERTS_CHK_MBUF_SZ(c_p);
ASSERT(!ERTS_PROC_IS_EXITING(c_p) || is_non_value(result));
ERTS_VERIFY_UNUSED_TEMP_ALLOC(c_p);
ERTS_HOLE_CHECK(c_p);
@@ -3026,6 +3048,7 @@ do { \
if (i == 0) {
StoreBifResult(4, Op1);
}
+ ires = big_size(Op1);
goto big_shift;
}
} else if (is_big(Op2)) {
@@ -3041,7 +3064,6 @@ do { \
OpCase(i_bsl_jIssd):
GetArg2(2, Op1, Op2);
-
do_bsl:
if (is_small(Op2)) {
i = signed_val(Op2);
@@ -3067,16 +3089,12 @@ do { \
StoreBifResult(4, Op1);
}
}
- Op1 = small_to_big(ires, tmp_big);
-#ifdef TAG_LITERAL_PTR
- Op1 |= TAG_LITERAL_PTR;
-#endif
+ ires = 1; /* big_size(small_to_big(Op1)) */
big_shift:
if (i > 0) { /* Left shift. */
- ires = big_size(Op1) + (i / D_EXP);
+ ires += (i / D_EXP);
} else { /* Right shift. */
- ires = big_size(Op1);
if (ires <= (-i / D_EXP))
ires = 3; /* ??? */
else
@@ -3095,6 +3113,9 @@ do { \
goto lb_Cl_error;
}
TestHeapPreserve(ires+1, Arg(1), Op1);
+ if (is_small(Op1)) {
+ Op1 = small_to_big(signed_val(Op1), tmp_big);
+ }
bigp = HTOP;
Op1 = big_lshift(Op1, i, bigp);
if (is_big(Op1)) {
@@ -3117,6 +3138,7 @@ do { \
if (i == 0) {
StoreBifResult(4, Op1);
}
+ ires = big_size(Op1);
goto big_shift;
}
} else if (is_big(Op2)) {
@@ -3553,11 +3575,13 @@ do { \
ASSERT(c_p->scheduler_data);
#endif
live_hf_end = c_p->mbuf;
+ ERTS_CHK_MBUF_SZ(c_p);
erts_pre_nif(&env, c_p, (struct erl_module_nif*)I[2], NULL);
nif_bif_result = (*fp)(&env, bif_nif_arity, reg);
if (env.exception_thrown)
nif_bif_result = THE_NON_VALUE;
erts_post_nif(&env);
+ ERTS_CHK_MBUF_SZ(c_p);
PROCESS_MAIN_CHK_LOCKS(c_p);
ERTS_VERIFY_UNUSED_TEMP_ALLOC(c_p);
@@ -3609,7 +3633,9 @@ do { \
Eterm (*bf)(Process*, Eterm*, BeamInstr*) = vbf;
ASSERT(!ERTS_PROC_IS_EXITING(c_p));
live_hf_end = c_p->mbuf;
+ ERTS_CHK_MBUF_SZ(c_p);
nif_bif_result = (*bf)(c_p, reg, I);
+ ERTS_CHK_MBUF_SZ(c_p);
ASSERT(!ERTS_PROC_IS_EXITING(c_p) ||
is_non_value(nif_bif_result));
ERTS_VERIFY_UNUSED_TEMP_ALLOC(c_p);
@@ -5249,19 +5275,14 @@ void erts_dirty_process_main(ErtsSchedulerData *esdp)
else {
/*
* Dirty CPU scheduler:
- * Currently two reductions consumed per
- * micro second spent in the dirty NIF.
+ * Reductions based on time consumed by
+ * the dirty NIF.
*/
- ErtsMonotonicTime time;
- time = erts_get_monotonic_time(esdp);
- time -= start_time;
- time = ERTS_MONOTONIC_TO_USEC(time);
- time *= (CONTEXT_REDS-1)/1000 + 1;
- ASSERT(time >= 0);
- if (time == 0)
- time = 1; /* At least one reduction */
- time += esdp->virtual_reds;
- reds_used = time > INT_MAX ? INT_MAX : (int) time;
+ Sint64 treds;
+ treds = erts_time2reds(start_time,
+ erts_get_monotonic_time(esdp));
+ treds += esdp->virtual_reds;
+ reds_used = treds > INT_MAX ? INT_MAX : (int) treds;
}
PROCESS_MAIN_CHK_LOCKS(c_p);
@@ -5379,7 +5400,7 @@ void erts_dirty_process_main(ErtsSchedulerData *esdp)
ASSERT(!c_p->scheduler_data);
erts_pre_dirty_nif(esdp, &env, c_p,
- (struct erl_module_nif*)I[2], NULL);
+ (struct erl_module_nif*)I[2]);
#ifdef DEBUG
result =
@@ -5388,7 +5409,7 @@ void erts_dirty_process_main(ErtsSchedulerData *esdp)
#endif
(*fp)(&env, arity, reg);
- erts_post_nif(&env);
+ erts_post_dirty_nif(&env);
ASSERT(!is_value(result));
ASSERT(c_p->freason == TRAP);
@@ -6523,34 +6544,49 @@ call_fun(Process* p, /* Current process. */
* representation (the module has never been loaded),
* or the module defining the fun has been unloaded.
*/
+
module = fe->module;
- if ((modp = erts_get_module(module, code_ix)) != NULL
- && modp->curr.code_hdr != NULL) {
+
+ ERTS_SMP_READ_MEMORY_BARRIER;
+ if (fe->pend_purge_address) {
/*
- * There is a module loaded, but obviously the fun is not
- * defined in it. We must not call the error_handler
- * (or we will get into an infinite loop).
+ * The system is currently trying to purge the
+ * module containing this fun. Suspend the process
+ * and let it try again when the purge operation is
+ * done (may succeed or not).
*/
- goto badfun;
+ ep = erts_suspend_process_on_pending_purge_lambda(p);
+ ASSERT(ep);
}
+ else {
+ if ((modp = erts_get_module(module, code_ix)) != NULL
+ && modp->curr.code_hdr != NULL) {
+ /*
+ * There is a module loaded, but obviously the fun is not
+ * defined in it. We must not call the error_handler
+ * (or we will get into an infinite loop).
+ */
+ goto badfun;
+ }
- /*
- * No current code for this module. Call the error_handler module
- * to attempt loading the module.
- */
+ /*
+ * No current code for this module. Call the error_handler module
+ * to attempt loading the module.
+ */
- ep = erts_find_function(erts_proc_get_error_handler(p),
- am_undefined_lambda, 3, code_ix);
- if (ep == NULL) { /* No error handler */
- p->current = NULL;
- p->freason = EXC_UNDEF;
- return NULL;
+ ep = erts_find_function(erts_proc_get_error_handler(p),
+ am_undefined_lambda, 3, code_ix);
+ if (ep == NULL) { /* No error handler */
+ p->current = NULL;
+ p->freason = EXC_UNDEF;
+ return NULL;
+ }
}
reg[0] = module;
reg[1] = fun;
reg[2] = args;
reg[3] = NIL;
- return ep->addressv[erts_active_code_ix()];
+ return ep->addressv[code_ix];
}
}
} else if (is_export_header(hdr)) {
diff --git a/erts/emulator/beam/beam_load.c b/erts/emulator/beam/beam_load.c
index 0c2743beb2..0afdedf6c2 100644
--- a/erts/emulator/beam/beam_load.c
+++ b/erts/emulator/beam/beam_load.c
@@ -105,7 +105,7 @@ typedef struct {
*/
typedef struct genop {
- int op; /* Opcode. */
+ unsigned int op; /* Opcode. */
int arity; /* Number of arguments. */
GenOpArg def_args[MAX_OPARGS]; /* Default buffer for arguments. */
GenOpArg* a; /* The arguments. */
@@ -283,8 +283,8 @@ typedef struct LoaderState {
byte* code_start; /* Start of code file. */
unsigned code_size; /* Size of code file. */
int specific_op; /* Specific opcode (-1 if not found). */
- int num_functions; /* Number of functions in module. */
- int num_labels; /* Number of labels. */
+ unsigned int num_functions; /* Number of functions in module. */
+ unsigned int num_labels; /* Number of labels. */
BeamCodeHeader* hdr; /* Loaded code header */
BeamInstr* codev; /* Loaded code buffer */
int codev_size; /* Size of code buffer in words. */
@@ -303,13 +303,13 @@ typedef struct LoaderState {
* Atom table.
*/
- int num_atoms; /* Number of atoms in atom table. */
+ unsigned int num_atoms; /* Number of atoms in atom table. */
Eterm* atom; /* Atom table. */
- int num_exps; /* Number of exports. */
+ unsigned int num_exps; /* Number of exports. */
ExportEntry* export; /* Pointer to export table. */
- int num_imports; /* Number of imports. */
+ unsigned int num_imports; /* Number of imports. */
ImportEntry* import; /* Import entry (translated information). */
/*
@@ -323,8 +323,8 @@ typedef struct LoaderState {
* Lambda table.
*/
- int num_lambdas; /* Number of lambdas in table. */
- int lambdas_allocated; /* Size of allocated lambda table. */
+ unsigned int num_lambdas; /* Number of lambdas in table. */
+ unsigned int lambdas_allocated; /* Size of allocated lambda table. */
Lambda* lambdas; /* Pointer to lambdas. */
Lambda def_lambdas[16]; /* Default storage for lambda table. */
char* lambda_error; /* Delayed missing 'FunT' error. */
@@ -333,8 +333,8 @@ typedef struct LoaderState {
* Literals (constant pool).
*/
- int num_literals; /* Number of literals in table. */
- int allocated_literals; /* Number of literal entries allocated. */
+ unsigned int num_literals; /* Number of literals in table. */
+ unsigned int allocated_literals; /* Number of literal entries allocated. */
Literal* literals; /* Array of literals. */
LiteralPatch* literal_patches; /* Operands that need to be patched. */
Uint total_literal_size; /* Total heap size for all literals. */
@@ -343,13 +343,13 @@ typedef struct LoaderState {
* Line table.
*/
BeamInstr* line_item; /* Line items from the BEAM file. */
- int num_line_items; /* Number of line items. */
+ unsigned int num_line_items;/* Number of line items. */
LineInstr* line_instr; /* Line instructions */
- int num_line_instrs; /* Maximum number of line instructions */
- int current_li; /* Current line instruction */
- int* func_line; /* Mapping from function to first line instr */
+ unsigned int num_line_instrs; /* Maximum number of line instructions */
+ unsigned int current_li; /* Current line instruction */
+ unsigned int* func_line; /* Mapping from function to first line instr */
Eterm* fname; /* List of file names */
- int num_fnames; /* Number of filenames in fname table */
+ unsigned int num_fnames; /* Number of filenames in fname table */
int loc_size; /* Size of location info in bytes (2/4) */
} LoaderState;
@@ -663,7 +663,7 @@ erts_prepare_loading(Binary* magic, Process *c_p, Eterm group_leader,
stp->hdr->compile_ptr = NULL;
stp->hdr->compile_size = 0;
stp->hdr->compile_size_on_heap = 0;
- stp->hdr->literals_start = NULL;
+ stp->hdr->literal_area = NULL;
stp->hdr->md5_ptr = NULL;
/*
@@ -833,11 +833,20 @@ erts_finish_loading(Binary* magic, Process* c_p,
size = stp->loaded_size;
erts_total_code_size += size;
- if (stp->on_load) {
- inst_p = &mod_tab_p->old;
- } else {
+
+ if (!stp->on_load) {
inst_p = &mod_tab_p->curr;
+ } else {
+ mod_tab_p->on_load =
+ (struct erl_module_instance *)
+ erts_alloc(ERTS_ALC_T_PREPARED_CODE,
+ sizeof(struct erl_module_instance));
+ inst_p = mod_tab_p->on_load;
+ inst_p->nif = 0;
+ inst_p->num_breakpoints = 0;
+ inst_p->num_traced_exports = 0;
}
+
inst_p->code_hdr = stp->hdr;
inst_p->code_length = size;
@@ -1005,8 +1014,9 @@ loader_state_dtor(Binary* magic)
stp->bin = 0;
}
if (stp->hdr != 0) {
- if (stp->hdr->literals_start) {
- erts_free(ERTS_ALC_T_LITERAL, stp->hdr->literals_start);
+ if (stp->hdr->literal_area) {
+ erts_release_literal_area(stp->hdr->literal_area);
+ stp->hdr->literal_area = NULL;
}
erts_free(ERTS_ALC_T_CODE, stp->hdr);
stp->hdr = 0;
@@ -1330,7 +1340,7 @@ verify_chunks(LoaderState* stp)
static int
load_atom_table(LoaderState* stp)
{
- int i;
+ unsigned int i;
GetInt(stp, 4, stp->num_atoms);
stp->num_atoms++;
@@ -1375,13 +1385,13 @@ load_atom_table(LoaderState* stp)
static int
load_import_table(LoaderState* stp)
{
- int i;
+ unsigned int i;
GetInt(stp, 4, stp->num_imports);
stp->import = erts_alloc(ERTS_ALC_T_PREPARED_CODE,
stp->num_imports * sizeof(ImportEntry));
for (i = 0; i < stp->num_imports; i++) {
- int n;
+ unsigned int n;
Eterm mod;
Eterm func;
Uint arity;
@@ -1389,17 +1399,17 @@ load_import_table(LoaderState* stp)
GetInt(stp, 4, n);
if (n >= stp->num_atoms) {
- LoadError2(stp, "import entry %d: invalid atom number %d", i, n);
+ LoadError2(stp, "import entry %u: invalid atom number %u", i, n);
}
mod = stp->import[i].module = stp->atom[n];
GetInt(stp, 4, n);
if (n >= stp->num_atoms) {
- LoadError2(stp, "import entry %d: invalid atom number %d", i, n);
+ LoadError2(stp, "import entry %u: invalid atom number %u", i, n);
}
func = stp->import[i].function = stp->atom[n];
GetInt(stp, 4, arity);
if (arity > MAX_REG) {
- LoadError2(stp, "import entry %d: invalid arity %d", i, arity);
+ LoadError2(stp, "import entry %u: invalid arity %d", i, arity);
}
stp->import[i].arity = arity;
stp->import[i].patches = 0;
@@ -1427,12 +1437,12 @@ load_import_table(LoaderState* stp)
static int
read_export_table(LoaderState* stp)
{
- int i;
+ unsigned int i;
BeamInstr* address;
GetInt(stp, 4, stp->num_exps);
if (stp->num_exps > stp->num_functions) {
- LoadError2(stp, "%d functions exported; only %d functions defined",
+ LoadError2(stp, "%u functions exported; only %u functions defined",
stp->num_exps, stp->num_functions);
}
stp->export
@@ -1450,16 +1460,16 @@ read_export_table(LoaderState* stp)
stp->export[i].function = func;
GetInt(stp, 4, arity);
if (arity > MAX_REG) {
- LoadError2(stp, "export table entry %d: absurdly high arity %d", i, arity);
+ LoadError2(stp, "export table entry %u: absurdly high arity %u", i, arity);
}
stp->export[i].arity = arity;
GetInt(stp, 4, n);
if (n >= stp->num_labels) {
- LoadError3(stp, "export table entry %d: invalid label %d (highest defined label is %d)", i, n, stp->num_labels);
+ LoadError3(stp, "export table entry %u: invalid label %u (highest defined label is %u)", i, n, stp->num_labels);
}
value = stp->labels[n].value;
if (value == 0) {
- LoadError2(stp, "export table entry %d: label %d not resolved", i, n);
+ LoadError2(stp, "export table entry %u: label %u not resolved", i, n);
}
stp->export[i].address = address = stp->codev + value;
@@ -1520,7 +1530,7 @@ is_bif(Eterm mod, Eterm func, unsigned arity)
static int
read_lambda_table(LoaderState* stp)
{
- int i;
+ unsigned int i;
GetInt(stp, 4, stp->num_lambdas);
if (stp->num_lambdas > stp->lambdas_allocated) {
@@ -1540,12 +1550,12 @@ read_lambda_table(LoaderState* stp)
GetAtom(stp, n, stp->lambdas[i].function);
GetInt(stp, 4, arity);
if (arity > MAX_REG) {
- LoadError2(stp, "lambda entry %d: absurdly high arity %d", i, arity);
+ LoadError2(stp, "lambda entry %u: absurdly high arity %u", i, arity);
}
stp->lambdas[i].arity = arity;
GetInt(stp, 4, n);
if (n >= stp->num_labels) {
- LoadError3(stp, "lambda entry %d: invalid label %d (highest defined label is %d)",
+ LoadError3(stp, "lambda entry %u: invalid label %u (highest defined label is %u)",
i, n, stp->num_labels);
}
stp->lambdas[i].label = n;
@@ -1566,7 +1576,7 @@ read_lambda_table(LoaderState* stp)
static int
read_literal_table(LoaderState* stp)
{
- int i;
+ unsigned int i;
uLongf uncompressed_sz;
byte* uncompressed = 0;
@@ -1588,7 +1598,7 @@ read_literal_table(LoaderState* stp)
}
for (i = 0; i < stp->num_literals; i++) {
- int sz;
+ Uint sz;
Sint heap_size;
byte* p;
Eterm val;
@@ -1597,7 +1607,7 @@ read_literal_table(LoaderState* stp)
GetInt(stp, 4, sz); /* Size of external term format. */
GetString(stp, p, sz);
if ((heap_size = erts_decode_ext_size(p, sz)) < 0) {
- LoadError1(stp, "literal %d: bad external format", i);
+ LoadError1(stp, "literal %u: bad external format", i);
}
if (heap_size > 0) {
@@ -1607,7 +1617,7 @@ read_literal_table(LoaderState* stp)
val = erts_decode_ext(&factory, &p, 0);
if (is_non_value(val)) {
- LoadError1(stp, "literal %d: bad external format", i);
+ LoadError1(stp, "literal %u: bad external format", i);
}
erts_factory_close(&factory);
stp->literals[i].heap_frags = factory.heap_frags;
@@ -1617,7 +1627,7 @@ read_literal_table(LoaderState* stp)
erts_factory_dummy_init(&factory);
val = erts_decode_ext(&factory, &p, 0);
if (is_non_value(val)) {
- LoadError1(stp, "literal %d: bad external format", i);
+ LoadError1(stp, "literal %u: bad external format", i);
}
ASSERT(is_immed(val));
stp->literals[i].heap_frags = NULL;
@@ -1640,9 +1650,9 @@ read_line_table(LoaderState* stp)
{
unsigned version;
ERTS_DECLARE_DUMMY(unsigned flags);
- int num_line_items;
+ unsigned int num_line_items;
BeamInstr* lp;
- int i;
+ unsigned int i;
BeamInstr fname_index;
BeamInstr tag;
@@ -1721,7 +1731,7 @@ read_line_table(LoaderState* stp)
}
} else if (tag == TAG_a) {
if (val > stp->num_fnames) {
- LoadError2(stp, "file index overflow (%d/%d)",
+ LoadError2(stp, "file index overflow (%u/%u)",
val, stp->num_fnames);
}
fname_index = val;
@@ -1757,9 +1767,9 @@ read_line_table(LoaderState* stp)
stp->num_line_instrs *
sizeof(LineInstr));
stp->current_li = 0;
- stp->func_line = (int *) erts_alloc(ERTS_ALC_T_PREPARED_CODE,
- stp->num_functions *
- sizeof(int));
+ stp->func_line = (unsigned int *) erts_alloc(ERTS_ALC_T_PREPARED_CODE,
+ stp->num_functions *
+ sizeof(int));
return 1;
@@ -1783,6 +1793,10 @@ read_code_header(LoaderState* stp)
*/
GetInt(stp, 4, head_size);
+ if (head_size > stp->file_left) {
+ LoadError2(stp, "invalid code header size %u; bytes left %u",
+ head_size, stp->file_left);
+ }
stp->code_start = stp->file_p + head_size;
stp->code_size = stp->file_left - head_size;
stp->file_left = head_size;
@@ -1887,7 +1901,7 @@ load_code(LoaderState* stp)
ci = stp->ci;
for (;;) {
- int new_op;
+ unsigned int new_op;
GenOp* tmp_op;
ASSERT(ci <= codev_size);
@@ -1895,10 +1909,10 @@ load_code(LoaderState* stp)
get_next_instr:
GetByte(stp, new_op);
if (new_op >= NUM_GENERIC_OPS) {
- LoadError1(stp, "invalid opcode %d", new_op);
+ LoadError1(stp, "invalid opcode %u", new_op);
}
if (gen_opc[new_op].name[0] == '\0') {
- LoadError1(stp, "invalid opcode %d", new_op);
+ LoadError1(stp, "invalid opcode %u", new_op);
}
@@ -2368,7 +2382,7 @@ load_code(LoaderState* stp)
VerifyTag(stp, tag, TAG_u);
last_label = tmp_op->a[arg].val;
if (!(0 < last_label && last_label < stp->num_labels)) {
- LoadError2(stp, "invalid label num %d (0 < label < %d)",
+ LoadError2(stp, "invalid label num %u (0 < label < %u)",
tmp_op->a[arg].val, stp->num_labels);
}
if (stp->labels[last_label].value != 0) {
@@ -2512,7 +2526,7 @@ load_code(LoaderState* stp)
{
Sint offset;
if (function_number >= stp->num_functions) {
- LoadError1(stp, "too many functions in module (header said %d)",
+ LoadError1(stp, "too many functions in module (header said %u)",
stp->num_functions);
}
@@ -2591,14 +2605,14 @@ load_code(LoaderState* stp)
if (stp->line_item) {
BeamInstr item = code[ci-1];
BeamInstr loc;
- int li;
+ unsigned int li;
if (item >= stp->num_line_items) {
- LoadError2(stp, "line instruction index overflow (%d/%d)",
+ LoadError2(stp, "line instruction index overflow (%u/%u)",
item, stp->num_line_items);
}
li = stp->current_li;
if (li >= stp->num_line_instrs) {
- LoadError2(stp, "line instruction table overflow (%d/%d)",
+ LoadError2(stp, "line instruction table overflow (%u/%u)",
li, stp->num_line_instrs);
}
loc = stp->line_item[item];
@@ -2630,6 +2644,10 @@ load_code(LoaderState* stp)
* End of code found.
*/
case op_int_code_end:
+ if (function_number != stp->num_functions) {
+ LoadError2(stp, "too few functions (%u) in module (header said %u)",
+ function_number, stp->num_functions);
+ }
stp->codev_size = codev_size;
stp->ci = ci;
stp->function = THE_NON_VALUE;
@@ -4560,13 +4578,16 @@ freeze_code(LoaderState* stp)
Eterm* ptr;
LiteralPatch* lp;
ErlOffHeap code_off_heap;
+ ErtsLiteralArea *literal_area;
+ Uint lit_asize;
ERTS_INIT_OFF_HEAP(&code_off_heap);
- ptr = (Eterm*)erts_alloc(ERTS_ALC_T_LITERAL,
- stp->total_literal_size*sizeof(Eterm));
- code_hdr->literals_start = ptr;
- code_hdr->literals_end = ptr + stp->total_literal_size;
+ lit_asize = ERTS_LITERAL_AREA_ALLOC_SIZE(stp->total_literal_size);
+ literal_area = erts_alloc(ERTS_ALC_T_LITERAL, lit_asize);
+ ptr = &literal_area->start[0];
+ literal_area->end = ptr + stp->total_literal_size;
+
for (i = 0; i < stp->num_literals; i++) {
if (is_not_immed(stp->literals[i].term)) {
erts_move_multi_frags(&ptr, &code_off_heap,
@@ -4576,7 +4597,7 @@ freeze_code(LoaderState* stp)
ptr_val(stp->literals[i].term)));
}
}
- code_hdr->literals_off_heap = code_off_heap.first;
+ literal_area->off_heap = code_off_heap.first;
lp = stp->literal_patches;
while (lp != 0) {
BeamInstr* op_ptr;
@@ -4587,6 +4608,7 @@ freeze_code(LoaderState* stp)
op_ptr[0] = lit->term;
lp = lp->next;
}
+ code_hdr->literal_area = literal_area;
}
CHKBLK(ERTS_ALC_T_CODE,code);
@@ -4598,8 +4620,8 @@ freeze_code(LoaderState* stp)
str_table = (byte *) (codev + stp->ci);
} else {
BeamCodeLineTab* const line_tab = (BeamCodeLineTab *) (codev+stp->ci);
- const int ftab_size = stp->num_functions;
- const int num_instrs = stp->current_li;
+ const unsigned int ftab_size = stp->num_functions;
+ const unsigned int num_instrs = stp->current_li;
const BeamInstr** const line_items =
(const BeamInstr**) &line_tab->func_tab[ftab_size + 1];
@@ -4759,7 +4781,7 @@ freeze_code(LoaderState* stp)
static void
final_touch(LoaderState* stp, struct erl_module_instance* inst_p)
{
- int i;
+ unsigned int i;
int on_load = stp->on_load;
unsigned catches;
Uint index;
@@ -5431,7 +5453,7 @@ new_genop(LoaderState* stp)
static int
new_label(LoaderState* stp)
{
- int num = stp->num_labels;
+ unsigned int num = stp->num_labels;
stp->num_labels++;
stp->labels = (Label *) erts_realloc(ERTS_ALC_T_PREPARED_CODE,
@@ -5642,6 +5664,28 @@ has_native(BeamCodeHeader *code_hdr)
return result;
}
+void
+erts_release_literal_area(ErtsLiteralArea* literal_area)
+{
+ struct erl_off_heap_header* oh;
+
+ if (!literal_area)
+ return;
+
+ oh = literal_area->off_heap;
+
+ while (oh) {
+ Binary* bptr;
+ ASSERT(thing_subtag(oh->thing_word) == REFC_BINARY_SUBTAG);
+ bptr = ((ProcBin*)oh)->val;
+ if (erts_refc_dectest(&bptr->refc, 0) == 0) {
+ erts_bin_free(bptr);
+ }
+ oh = oh->next;
+ }
+ erts_free(ERTS_ALC_T_LITERAL, literal_area);
+}
+
int
erts_is_module_native(BeamCodeHeader* code_hdr)
{
@@ -6029,11 +6073,11 @@ stub_copy_info(LoaderState* stp,
static int
stub_read_export_table(LoaderState* stp)
{
- int i;
+ unsigned int i;
GetInt(stp, 4, stp->num_exps);
if (stp->num_exps > stp->num_functions) {
- LoadError2(stp, "%d functions exported; only %d functions defined",
+ LoadError2(stp, "%u functions exported; only %u functions defined",
stp->num_exps, stp->num_functions);
}
stp->export
@@ -6047,7 +6091,7 @@ stub_read_export_table(LoaderState* stp)
GetAtom(stp, n, stp->export[i].function);
GetInt(stp, 4, n);
if (n > MAX_REG) {
- LoadError2(stp, "export table entry %d: absurdly high arity %d", i, n);
+ LoadError2(stp, "export table entry %u: absurdly high arity %u", i, n);
}
stp->export[i].arity = n;
GetInt(stp, 4, n); /* Ignore label */
@@ -6061,8 +6105,8 @@ stub_read_export_table(LoaderState* stp)
static void
stub_final_touch(LoaderState* stp, BeamInstr* fp)
{
- int i;
- int n = stp->num_exps;
+ unsigned int i;
+ unsigned int n = stp->num_exps;
Eterm mod = fp[2];
Eterm function = fp[3];
int arity = fp[4];
@@ -6373,9 +6417,7 @@ erts_make_stub_module(Process* p, Eterm Mod, Eterm Beam, Eterm Info)
code_hdr->compile_ptr = NULL;
code_hdr->compile_size = 0;
code_hdr->compile_size_on_heap = 0;
- code_hdr->literals_start = NULL;
- code_hdr->literals_end = NULL;
- code_hdr->literals_off_heap = 0;
+ code_hdr->literal_area = NULL;
code_hdr->on_load_function_ptr = NULL;
code_hdr->line_table = NULL;
code_hdr->md5_ptr = NULL;
diff --git a/erts/emulator/beam/beam_load.h b/erts/emulator/beam/beam_load.h
index fd2dd97fee..1200bb9c6f 100644
--- a/erts/emulator/beam/beam_load.h
+++ b/erts/emulator/beam/beam_load.h
@@ -50,6 +50,7 @@ extern BeamInstr* em_call_error_handler;
extern BeamInstr* em_apply_bif;
extern BeamInstr* em_call_nif;
+struct ErtsLiteralArea_;
/*
* The following variables keep a sorted list of address ranges for
@@ -89,9 +90,7 @@ typedef struct beam_code_header {
/*
* Literal area (constant pool).
*/
- Eterm* literals_start;
- Eterm* literals_end;
- struct erl_off_heap_header* literals_off_heap;
+ struct ErtsLiteralArea_ *literal_area;
/*
* Pointer to the on_load function (or NULL if none).
@@ -120,7 +119,12 @@ typedef struct beam_code_header {
}BeamCodeHeader;
+void erts_release_literal_area(struct ErtsLiteralArea_* literal_area);
int erts_is_module_native(BeamCodeHeader* code);
+void erts_beam_bif_load_init(void);
+struct erl_fun_entry;
+void erts_purge_state_add_fun(struct erl_fun_entry *fe);
+Export *erts_suspend_process_on_pending_purge_lambda(Process *c_p);
/*
* Layout of the line table.
diff --git a/erts/emulator/beam/bif.tab b/erts/emulator/beam/bif.tab
index 065018514a..80db4eb6ff 100644
--- a/erts/emulator/beam/bif.tab
+++ b/erts/emulator/beam/bif.tab
@@ -161,6 +161,7 @@ bif erts_internal:port_close/1
bif erts_internal:port_connect/2
bif erts_internal:request_system_task/3
+bif erts_internal:request_system_task/4
bif erts_internal:check_process_code/2
bif erts_internal:map_to_tuple_keys/1
@@ -174,6 +175,8 @@ bif erts_internal:is_system_process/1
bif erts_internal:system_check/1
+bif erts_internal:release_literal_area_switch/0
+
# inet_db support
bif erlang:port_set_data/2
bif erlang:port_get_data/1
@@ -642,8 +645,9 @@ bif erts_debug:map_info/1
# New in 19.0
#
-bif erts_internal:copy_literals/2
-bif erts_internal:purge_module/1
+bif erts_internal:is_process_executing_dirty/1
+bif erts_internal:check_dirty_process_code/2
+bif erts_internal:purge_module/2
bif binary:split/2
bif binary:split/3
bif erts_debug:size_shared/1
diff --git a/erts/emulator/beam/erl_alloc.types b/erts/emulator/beam/erl_alloc.types
index 227fedfb69..6e8710eb8a 100644
--- a/erts/emulator/beam/erl_alloc.types
+++ b/erts/emulator/beam/erl_alloc.types
@@ -279,6 +279,7 @@ type TRACER_NIF LONG_LIVED SYSTEM tracer_nif
type TRACE_MSG_QUEUE SHORT_LIVED SYSTEM trace_message_queue
type SCHED_ASYNC_JOB SHORT_LIVED SYSTEM async_calls
type DIRTY_START STANDARD PROCESSES dirty_start
+type DIRTY_SL SHORT_LIVED SYSTEM dirty_short_lived
+if threads_no_smp
# Need thread safe allocs, but std_alloc and fix_alloc are not;
@@ -313,6 +314,7 @@ type RUNQ_BLNS LONG_LIVED SYSTEM run_queue_balancing
type THR_PRGR_IDATA LONG_LIVED SYSTEM thr_prgr_internal_data
type THR_PRGR_DATA LONG_LIVED SYSTEM thr_prgr_data
type T_THR_PRGR_DATA SHORT_LIVED SYSTEM temp_thr_prgr_data
+type RELEASE_LAREA SHORT_LIVED SYSTEM release_literal_area
+endif
#
@@ -367,6 +369,8 @@ type MONITOR_LH STANDARD PROCESSES monitor_lh
type NLINK_LH STANDARD PROCESSES nlink_lh
type CODE LONG_LIVED CODE code
type LITERAL LITERAL CODE literal
+type LITERAL_REF SHORT_LIVED CODE literal_area_ref
+type PURGE_DATA SHORT_LIVED CODE purge_data
type DB_HEIR_DATA STANDARD ETS db_heir_data
type DB_MS_PSDO_PROC LONG_LIVED ETS db_match_pseudo_proc
type SCHDLR_DATA LONG_LIVED SYSTEM scheduler_data
diff --git a/erts/emulator/beam/erl_bif_info.c b/erts/emulator/beam/erl_bif_info.c
index 3fb866733c..29ba12dfdb 100644
--- a/erts/emulator/beam/erl_bif_info.c
+++ b/erts/emulator/beam/erl_bif_info.c
@@ -2284,9 +2284,7 @@ BIF_RETTYPE system_info_1(BIF_ALIST_1)
} else if (ERTS_IS_ATOM_STR("dist_ctrl", BIF_ARG_1)) {
DistEntry *dep;
i = 0;
- /* Need to be the only thread running... */
- erts_smp_proc_unlock(BIF_P, ERTS_PROC_LOCK_MAIN);
- erts_smp_thr_progress_block();
+ erts_smp_rwmtx_rlock(&erts_dist_table_rwmtx);
for (dep = erts_visible_dist_entries; dep; dep = dep->next)
++i;
for (dep = erts_hidden_dist_entries; dep; dep = dep->next)
@@ -2309,8 +2307,7 @@ BIF_RETTYPE system_info_1(BIF_ALIST_1)
res = CONS(hp, tpl, res);
hp += 2;
}
- erts_smp_thr_progress_unblock();
- erts_smp_proc_lock(BIF_P, ERTS_PROC_LOCK_MAIN);
+ erts_smp_rwmtx_runlock(&erts_dist_table_rwmtx);
BIF_RET(res);
} else if (BIF_ARG_1 == am_system_version) {
erts_dsprintf_buf_t *dsbufp = erts_create_tmp_dsbuf(0);
@@ -2886,6 +2883,27 @@ BIF_RETTYPE system_info_1(BIF_ALIST_1)
BIF_RET(AM_tag);
#endif
}
+ else if (ERTS_IS_ATOM_STR("check_process_code",BIF_ARG_1)) {
+ Eterm terms[3];
+ Sint length = 1;
+ Uint sz = 0;
+ Eterm *hp, res;
+ DECL_AM(direct_references);
+
+ terms[0] = AM_direct_references;
+#if !defined(ERTS_NEW_PURGE_STRATEGY)
+ {
+ DECL_AM(indirect_references);
+ terms[1] = AM_indirect_references;
+ terms[2] = am_copy_literals;
+ length = 3;
+ }
+#endif
+ erts_bld_list(NULL, &sz, length, terms);
+ hp = HAlloc(BIF_P, sz);
+ res = erts_bld_list(&hp, NULL, length, terms);
+ BIF_RET(res);
+ }
BIF_ERROR(BIF_P, BADARG);
}
diff --git a/erts/emulator/beam/erl_db.c b/erts/emulator/beam/erl_db.c
index bad34211a5..128a7b3865 100644
--- a/erts/emulator/beam/erl_db.c
+++ b/erts/emulator/beam/erl_db.c
@@ -1094,7 +1094,7 @@ BIF_RETTYPE ets_insert_2(BIF_ALIST_2)
CHECK_TABLES();
- /* Write lock table if more than one object to keep atomicy */
+ /* Write lock table if more than one object to keep atomicity */
kind = ((is_list(BIF_ARG_2) && CDR(list_val(BIF_ARG_2)) != NIL)
? LCK_WRITE : LCK_WRITE_REC);
@@ -1164,7 +1164,7 @@ BIF_RETTYPE ets_insert_new_2(BIF_ALIST_2)
Eterm lookup_ret;
DbTableMethod* meth;
- /* More than one object, use LCK_WRITE to keep atomicy */
+ /* More than one object, use LCK_WRITE to keep atomicity */
kind = LCK_WRITE;
tb = db_get_table(BIF_P, BIF_ARG_1, DB_WRITE, kind);
if (tb == NULL) {
diff --git a/erts/emulator/beam/erl_db_hash.c b/erts/emulator/beam/erl_db_hash.c
index 74979f984a..5e6fe4f460 100644
--- a/erts/emulator/beam/erl_db_hash.c
+++ b/erts/emulator/beam/erl_db_hash.c
@@ -40,7 +40,7 @@
** DB_FINE_LOCKED set. The table variable is_thread_safe will then indicate
** if operations need to obtain fine grained locks or not. Some operations
** will for example always use exclusive table lock to guarantee
-** a higher level of atomicy.
+** a higher level of atomicity.
*/
/* FIXATION:
@@ -2866,15 +2866,7 @@ db_lookup_dbterm_hash(Process *p, DbTable *tbl, Eterm key, Eterm obj,
q->hvalue = hval;
q->next = NULL;
*bp = b = q;
-
- {
- int nitems = erts_smp_atomic_inc_read_nob(&tb->common.nitems);
- int nactive = NACTIVE(tb);
-
- if (nitems > nactive * (CHAIN_LEN + 1) && !IS_FIXED(tb)) {
- grow(tb, nactive);
- }
- }
+ flags |= DB_INC_TRY_GROW;
} else {
HashDbTerm *q, *next = b->next;
@@ -2910,6 +2902,7 @@ db_finalize_dbterm_hash(int cret, DbUpdateHandle* handle)
HashDbTerm **bp = (HashDbTerm **) handle->bp;
HashDbTerm *b = *bp;
erts_smp_rwmtx_t* lck = (erts_smp_rwmtx_t*) handle->lck;
+ HashDbTerm* free_me = NULL;
ERTS_SMP_LC_ASSERT(IS_HASH_WLOCKED(tb, lck)); /* locked by db_lookup_dbterm_hash */
@@ -2921,21 +2914,34 @@ db_finalize_dbterm_hash(int cret, DbUpdateHandle* handle)
b->hvalue = INVALID_HASH;
} else {
*bp = b->next;
- free_term(tb, b);
+ free_me = b;
}
WUNLOCK_HASH(lck);
erts_smp_atomic_dec_nob(&tb->common.nitems);
try_shrink(tb);
- } else if (handle->flags & DB_MUST_RESIZE) {
- db_finalize_resize(handle, offsetof(HashDbTerm,dbterm));
- WUNLOCK_HASH(lck);
-
- free_term(tb, b);
- }
- else {
- WUNLOCK_HASH(lck);
+ } else {
+ if (handle->flags & DB_MUST_RESIZE) {
+ db_finalize_resize(handle, offsetof(HashDbTerm,dbterm));
+ free_me = b;
+ }
+ if (handle->flags & DB_INC_TRY_GROW) {
+ int nactive;
+ int nitems = erts_smp_atomic_inc_read_nob(&tb->common.nitems);
+ WUNLOCK_HASH(lck);
+ nactive = NACTIVE(tb);
+
+ if (nitems > nactive * (CHAIN_LEN + 1) && !IS_FIXED(tb)) {
+ grow(tb, nactive);
+ }
+ } else {
+ WUNLOCK_HASH(lck);
+ }
}
+
+ if (free_me)
+ free_term(tb, free_me);
+
#ifdef DEBUG
handle->dbterm = 0;
#endif
diff --git a/erts/emulator/beam/erl_db_util.h b/erts/emulator/beam/erl_db_util.h
index 60f7067d70..4acedbfed0 100644
--- a/erts/emulator/beam/erl_db_util.h
+++ b/erts/emulator/beam/erl_db_util.h
@@ -79,6 +79,7 @@ typedef union db_table DbTable;
#define DB_MUST_RESIZE 1
#define DB_NEW_OBJECT 2
+#define DB_INC_TRY_GROW 4
/* Info about a database entry while it's being updated
* (by update_counter or update_element)
diff --git a/erts/emulator/beam/erl_fun.c b/erts/emulator/beam/erl_fun.c
index 6ce1376c81..c639ba623f 100644
--- a/erts/emulator/beam/erl_fun.c
+++ b/erts/emulator/beam/erl_fun.c
@@ -199,14 +199,13 @@ erts_erase_fun_entry(ErlFunEntry* fe)
}
void
-erts_cleanup_funs_on_purge(BeamInstr* start, BeamInstr* end)
+erts_fun_purge_prepare(BeamInstr* start, BeamInstr* end)
{
int limit;
HashBucket** bucket;
- ErlFunEntry* to_delete = NULL;
int i;
- erts_fun_write_lock();
+ erts_fun_read_lock();
limit = erts_fun_table.size;
bucket = erts_fun_table.bucket;
for (i = 0; i < limit; i++) {
@@ -217,22 +216,51 @@ erts_cleanup_funs_on_purge(BeamInstr* start, BeamInstr* end)
BeamInstr* addr = fe->address;
if (start <= addr && addr < end) {
+ fe->pend_purge_address = addr;
+ ERTS_SMP_WRITE_MEMORY_BARRIER;
fe->address = unloaded_fun;
- if (erts_refc_dectest(&fe->refc, 0) == 0) {
- fe->address = (void *) to_delete;
- to_delete = fe;
- }
+ erts_purge_state_add_fun(fe);
}
b = b->next;
}
}
+ erts_fun_read_unlock();
+}
+
+void
+erts_fun_purge_abort_prepare(ErlFunEntry **funs, Uint no)
+{
+ Uint ix;
- while (to_delete != NULL) {
- ErlFunEntry* next = (ErlFunEntry *) to_delete->address;
- erts_erase_fun_entry_unlocked(to_delete);
- to_delete = next;
+ for (ix = 0; ix < no; ix++) {
+ ErlFunEntry *fe = funs[ix];
+ if (fe->address == unloaded_fun)
+ fe->address = fe->pend_purge_address;
+ fe->pend_purge_address = NULL;
}
- erts_fun_write_unlock();
+}
+
+void
+erts_fun_purge_abort_finalize(ErlFunEntry **funs, Uint no)
+{
+ Uint ix;
+
+ for (ix = 0; ix < no; ix++)
+ funs[ix]->pend_purge_address = NULL;
+}
+
+void
+erts_fun_purge_complete(ErlFunEntry **funs, Uint no)
+{
+ Uint ix;
+
+ for (ix = 0; ix < no; ix++) {
+ ErlFunEntry *fe = funs[ix];
+ fe->pend_purge_address = NULL;
+ if (erts_refc_dectest(&fe->refc, 0) == 0)
+ erts_erase_fun_entry(fe);
+ }
+ ERTS_SMP_WRITE_MEMORY_BARRIER;
}
void
@@ -294,6 +322,7 @@ fun_alloc(ErlFunEntry* template)
obj->module = template->module;
erts_refc_init(&obj->refc, -1);
obj->address = unloaded_fun;
+ obj->pend_purge_address = NULL;
#ifdef HIPE
obj->native_address = NULL;
#endif
diff --git a/erts/emulator/beam/erl_fun.h b/erts/emulator/beam/erl_fun.h
index 8c4deea7a0..73c3e19c1c 100644
--- a/erts/emulator/beam/erl_fun.h
+++ b/erts/emulator/beam/erl_fun.h
@@ -44,6 +44,7 @@ typedef struct erl_fun_entry {
Eterm module; /* Tagged atom for module. */
erts_refc_t refc; /* Reference count: One for code + one for each
fun object in each process. */
+ BeamInstr *pend_purge_address; /* address stored during a pending purge */
} ErlFunEntry;
/*
@@ -81,7 +82,10 @@ ErlFunEntry* erts_put_fun_entry2(Eterm mod, int old_uniq, int old_index,
void erts_erase_fun_entry(ErlFunEntry* fe);
void erts_cleanup_funs(ErlFunThing* funp);
-void erts_cleanup_funs_on_purge(BeamInstr* start, BeamInstr* end);
+void erts_fun_purge_prepare(BeamInstr* start, BeamInstr* end);
+void erts_fun_purge_abort_prepare(ErlFunEntry **funs, Uint no);
+void erts_fun_purge_abort_finalize(ErlFunEntry **funs, Uint no);
+void erts_fun_purge_complete(ErlFunEntry **funs, Uint no);
void erts_dump_fun_entries(int, void *);
#endif
diff --git a/erts/emulator/beam/erl_gc.c b/erts/emulator/beam/erl_gc.c
index a224383493..6f641a1ea7 100644
--- a/erts/emulator/beam/erl_gc.c
+++ b/erts/emulator/beam/erl_gc.c
@@ -150,6 +150,7 @@ static void move_msgq_to_heap(Process *p);
static int reached_max_heap_size(Process *p, Uint total_heap_size,
Uint extra_heap_size, Uint extra_old_heap_size);
static void init_gc_info(ErtsGCInfo *gcip);
+static Uint64 next_vheap_size(Process* p, Uint64 vheap, Uint64 vheap_sz);
#ifdef HARDDEBUG
static void disallow_heap_frag_ref_in_heap(Process* p);
@@ -387,6 +388,11 @@ erts_gc_after_bif_call_lhf(Process* p, ErlHeapFragment *live_hf_end,
return result;
}
+ if (!p->mbuf) {
+ /* Must have GC:d in BIF call... invalidate live_hf_end */
+ live_hf_end = ERTS_INVALID_HFRAG_PTR;
+ }
+
if (is_non_value(result)) {
if (p->freason == TRAP) {
#if HIPE
@@ -517,6 +523,8 @@ delay_garbage_collection(Process *p, ErlHeapFragment *live_hf_end, int need, int
erts_proc_sched_data((p))->virtual_reds += vreds;
}
+ ERTS_CHK_MBUF_SZ(p);
+
ASSERT(CONTEXT_REDS >= erts_proc_sched_data(p)->virtual_reds);
return reds_left;
}
@@ -527,13 +535,25 @@ young_gen_usage(Process *p)
Uint hsz;
Eterm *aheap;
+ ERTS_CHK_MBUF_SZ(p);
+
hsz = p->mbuf_sz;
if (p->flags & F_ON_HEAP_MSGQ) {
ErtsMessage *mp;
- for (mp = p->msg.first; mp; mp = mp->next)
+ for (mp = p->msg.first; mp; mp = mp->next) {
+ /*
+ * We leave not yet decoded distribution messages
+ * as they are in the queue since it is not
+ * possible to determine a maximum size until
+ * actual decoding. However, we use their estimated
+ * size when calculating need, and by this making
+ * it more likely that they will fit on the heap
+ * when actually decoded.
+ */
if (mp->data.attached)
hsz += erts_msg_attached_data_size(mp);
+ }
}
aheap = p->abandoned_heap;
@@ -590,6 +610,8 @@ garbage_collect(Process* p, ErlHeapFragment *live_hf_end,
DTRACE_CHARBUF(pidbuf, DTRACE_TERM_BUF_SIZE);
#endif
+ ERTS_CHK_MBUF_SZ(p);
+
ASSERT(CONTEXT_REDS - ERTS_REDS_LEFT(p, fcalls)
>= erts_proc_sched_data(p)->virtual_reds);
@@ -741,6 +763,9 @@ do_major_collection:
p->last_old_htop = p->old_htop;
#endif
+ ASSERT(!p->mbuf);
+ ASSERT(!ERTS_IS_GC_DESIRED(p));
+
return reds;
}
@@ -885,6 +910,58 @@ erts_garbage_collect_hibernate(Process* p)
}
+/*
+ * HiPE native code stack scanning procedures:
+ * - fullsweep_nstack()
+ * - gensweep_nstack()
+ * - offset_nstack()
+ * - sweep_literals_nstack()
+ */
+#if defined(HIPE)
+
+#define GENSWEEP_NSTACK(p,old_htop,n_htop) \
+ do { \
+ Eterm *tmp_old_htop = old_htop; \
+ Eterm *tmp_n_htop = n_htop; \
+ gensweep_nstack((p), &tmp_old_htop, &tmp_n_htop); \
+ old_htop = tmp_old_htop; \
+ n_htop = tmp_n_htop; \
+ } while(0)
+
+/*
+ * offset_nstack() can ignore the descriptor-based traversal the other
+ * nstack procedures use and simply call offset_heap_ptr() instead.
+ * This relies on two facts:
+ * 1. The only live non-Erlang terms on an nstack are return addresses,
+ * and they will be skipped thanks to the low/high range check.
+ * 2. Dead values, even if mistaken for pointers into the low/high area,
+ * can be offset safely since they won't be dereferenced.
+ *
+ * XXX: WARNING: If HiPE starts storing other non-Erlang values on the
+ * nstack, such as floats, then this will have to be changed.
+ */
+static ERTS_INLINE void offset_nstack(Process* p, Sint offs,
+ char* area, Uint area_size)
+{
+ if (p->hipe.nstack) {
+ ASSERT(p->hipe.nsp && p->hipe.nstend);
+ offset_heap_ptr(hipe_nstack_start(p), hipe_nstack_used(p),
+ offs, area, area_size);
+ }
+ else {
+ ASSERT(!p->hipe.nsp && !p->hipe.nstend);
+ }
+}
+
+#else /* !HIPE */
+
+#define fullsweep_nstack(p,n_htop) (n_htop)
+#define GENSWEEP_NSTACK(p,old_htop,n_htop) do{}while(0)
+#define offset_nstack(p,offs,area,area_size) do{}while(0)
+#define sweep_literals_nstack(p,old_htop,area,area_size) (old_htop)
+
+#endif /* HIPE */
+
void
erts_garbage_collect_literals(Process* p, Eterm* literals,
Uint byte_lit_size,
@@ -947,7 +1024,7 @@ erts_garbage_collect_literals(Process* p, Eterm* literals,
area_size = byte_lit_size;
n = setup_rootset(p, p->arg_reg, p->arity, &rootset);
roots = rootset.roots;
- old_htop = p->old_htop;
+ old_htop = sweep_literals_nstack(p, p->old_htop, area, area_size);
while (n--) {
Eterm* g_ptr = roots->v;
Uint g_sz = roots->sz;
@@ -1214,56 +1291,6 @@ minor_collection(Process* p, ErlHeapFragment *live_hf_end,
return -1;
}
-/*
- * HiPE native code stack scanning procedures:
- * - fullsweep_nstack()
- * - gensweep_nstack()
- * - offset_nstack()
- */
-#if defined(HIPE)
-
-#define GENSWEEP_NSTACK(p,old_htop,n_htop) \
- do { \
- Eterm *tmp_old_htop = old_htop; \
- Eterm *tmp_n_htop = n_htop; \
- gensweep_nstack((p), &tmp_old_htop, &tmp_n_htop); \
- old_htop = tmp_old_htop; \
- n_htop = tmp_n_htop; \
- } while(0)
-
-/*
- * offset_nstack() can ignore the descriptor-based traversal the other
- * nstack procedures use and simply call offset_heap_ptr() instead.
- * This relies on two facts:
- * 1. The only live non-Erlang terms on an nstack are return addresses,
- * and they will be skipped thanks to the low/high range check.
- * 2. Dead values, even if mistaken for pointers into the low/high area,
- * can be offset safely since they won't be dereferenced.
- *
- * XXX: WARNING: If HiPE starts storing other non-Erlang values on the
- * nstack, such as floats, then this will have to be changed.
- */
-static ERTS_INLINE void offset_nstack(Process* p, Sint offs,
- char* area, Uint area_size)
-{
- if (p->hipe.nstack) {
- ASSERT(p->hipe.nsp && p->hipe.nstend);
- offset_heap_ptr(hipe_nstack_start(p), hipe_nstack_used(p),
- offs, area, area_size);
- }
- else {
- ASSERT(!p->hipe.nsp && !p->hipe.nstend);
- }
-}
-
-#else /* !HIPE */
-
-#define fullsweep_nstack(p,n_htop) (n_htop)
-#define GENSWEEP_NSTACK(p,old_htop,n_htop) do{}while(0)
-#define offset_nstack(p,offs,area,area_size) do{}while(0)
-
-#endif /* HIPE */
-
static void
do_minor(Process *p, ErlHeapFragment *live_hf_end,
char *mature, Uint mature_size,
@@ -2243,44 +2270,31 @@ copy_one_frag(Eterm** hpp, ErlOffHeap* off_heap,
static void
move_msgq_to_heap(Process *p)
{
+
ErtsMessage **mpp = &p->msg.first;
+ Uint64 pre_oh = MSO(p).overhead;
while (*mpp) {
ErtsMessage *mp = *mpp;
if (mp->data.attached) {
ErlHeapFragment *bp;
- ErtsHeapFactory factory;
-
- erts_factory_proc_prealloc_init(&factory, p,
- erts_msg_attached_data_size(mp));
-
- if (is_non_value(ERL_MESSAGE_TERM(mp))) {
- if (mp->data.dist_ext) {
- ASSERT(mp->data.dist_ext->heap_size >= 0);
- if (is_not_nil(ERL_MESSAGE_TOKEN(mp))) {
- bp = erts_dist_ext_trailer(mp->data.dist_ext);
- ERL_MESSAGE_TOKEN(mp) = copy_struct(ERL_MESSAGE_TOKEN(mp),
- bp->used_size,
- &factory.hp,
- factory.off_heap);
- erts_cleanup_offheap(&bp->off_heap);
- }
- ERL_MESSAGE_TERM(mp) = erts_decode_dist_ext(&factory,
- mp->data.dist_ext);
- erts_free_dist_ext_copy(mp->data.dist_ext);
- mp->data.dist_ext = NULL;
- }
- }
- else {
+
+ /*
+ * We leave not yet decoded distribution messages
+ * as they are in the queue since it is not
+ * possible to determine a maximum size until
+ * actual decoding...
+ */
+ if (is_value(ERL_MESSAGE_TERM(mp))) {
bp = erts_message_to_heap_frag(mp);
if (bp->next)
- erts_move_multi_frags(&factory.hp, factory.off_heap, bp,
+ erts_move_multi_frags(&p->htop, &p->off_heap, bp,
mp->m, ERL_MESSAGE_REF_ARRAY_SZ, 0);
else
- copy_one_frag(&factory.hp, factory.off_heap, bp,
+ copy_one_frag(&p->htop, &p->off_heap, bp,
mp->m, ERL_MESSAGE_REF_ARRAY_SZ);
if (mp->data.attached != ERTS_MSG_COMBINED_HFRAG) {
@@ -2297,12 +2311,15 @@ move_msgq_to_heap(Process *p)
mp = new_mp;
}
}
-
- erts_factory_close(&factory);
}
mpp = &(*mpp)->next;
}
+
+ if (pre_oh != MSO(p).overhead) {
+ /* Got new binaries; update vheap size... */
+ BIN_VHEAP_SZ(p) = next_vheap_size(p, MSO(p).overhead, BIN_VHEAP_SZ(p));
+ }
}
static Uint
diff --git a/erts/emulator/beam/erl_hl_timer.c b/erts/emulator/beam/erl_hl_timer.c
index ebeff51aac..f1bef28186 100644
--- a/erts/emulator/beam/erl_hl_timer.c
+++ b/erts/emulator/beam/erl_hl_timer.c
@@ -735,7 +735,10 @@ proc_timeout_common(Process *proc, void *tmr)
if (tmr == (void *) erts_smp_atomic_cmpxchg_mb(&proc->common.timer,
ERTS_PTMR_TIMEDOUT,
(erts_aint_t) tmr)) {
- erts_aint32_t state = erts_smp_atomic32_read_acqb(&proc->state);
+ erts_aint32_t state;
+ erts_smp_proc_lock(proc, ERTS_PROC_LOCKS_MSG_RECEIVE);
+ state = erts_smp_atomic32_read_acqb(&proc->state);
+ erts_smp_proc_unlock(proc, ERTS_PROC_LOCKS_MSG_RECEIVE);
if (!(state & (ERTS_PSFLG_ACTIVE|ERTS_PSFLG_EXITING)))
erts_schedule_process(proc, state, 0);
return 1;
diff --git a/erts/emulator/beam/erl_init.c b/erts/emulator/beam/erl_init.c
index 2239b6f045..781bf024dd 100644
--- a/erts/emulator/beam/erl_init.c
+++ b/erts/emulator/beam/erl_init.c
@@ -382,6 +382,7 @@ erl_init(int ncpu,
erts_init_unicode(); /* after RE to get access to PCRE unicode */
erts_init_external();
erts_init_map();
+ erts_beam_bif_load_init();
erts_delay_trap = erts_export_put(am_erlang, am_delay_trap, 2);
erts_late_init_process();
#if HAVE_ERTS_MSEG
@@ -2248,7 +2249,42 @@ erl_start(int argc, char **argv)
otp_ring0_pid = erl_first_process_otp("otp_ring0", NULL, 0,
boot_argc, boot_argv);
- (void) erl_system_process_otp(otp_ring0_pid, "erts_code_purger");
+ {
+ /*
+ * The erts_code_purger and the erts_literal_area_collector
+ * system processes are *always* alive. If they terminate
+ * they bring the whole VM down.
+ */
+ Eterm pid;
+
+ pid = erl_system_process_otp(otp_ring0_pid, "erts_code_purger");
+ erts_code_purger
+ = (Process *) erts_ptab_pix2intptr_ddrb(&erts_proc,
+ internal_pid_index(pid));
+ ASSERT(erts_code_purger && erts_code_purger->common.id == pid);
+ erts_proc_inc_refc(erts_code_purger);
+
+#ifdef ERTS_NEW_PURGE_STRATEGY
+ pid = erl_system_process_otp(otp_ring0_pid, "erts_literal_area_collector");
+ erts_literal_area_collector
+ = (Process *) erts_ptab_pix2intptr_ddrb(&erts_proc,
+ internal_pid_index(pid));
+ ASSERT(erts_literal_area_collector
+ && erts_literal_area_collector->common.id == pid);
+ erts_proc_inc_refc(erts_literal_area_collector);
+#endif
+
+#ifdef ERTS_DIRTY_SCHEDULERS
+ pid = erl_system_process_otp(otp_ring0_pid, "erts_dirty_process_code_checker");
+ erts_dirty_process_code_checker
+ = (Process *) erts_ptab_pix2intptr_ddrb(&erts_proc,
+ internal_pid_index(pid));
+ ASSERT(erts_dirty_process_code_checker
+ && erts_dirty_process_code_checker->common.id == pid);
+ erts_proc_inc_refc(erts_dirty_process_code_checker);
+#endif
+
+ }
#ifdef ERTS_SMP
erts_start_schedulers();
@@ -2346,14 +2382,15 @@ erts_exit_vv(int n, int flush_async, char *fmt, va_list args1, va_list args2)
if (erts_mtrace_enabled)
erts_mtrace_exit((Uint32) n);
+ if (fmt != NULL && *fmt != '\0')
+ erl_error(fmt, args2); /* Print error message. */
+
/* Produce an Erlang core dump if error */
if (((n == ERTS_ERROR_EXIT && erts_no_crash_dump == 0) || n == ERTS_DUMP_EXIT)
&& erts_initialized) {
erl_crash_dump_v((char*) NULL, 0, fmt, args1);
}
- if (fmt != NULL && *fmt != '\0')
- erl_error(fmt, args2); /* Print error message. */
sys_tty_reset(n);
if (n == ERTS_INTR_EXIT)
diff --git a/erts/emulator/beam/erl_lock_check.c b/erts/emulator/beam/erl_lock_check.c
index 39c0617143..06266363b5 100644
--- a/erts/emulator/beam/erl_lock_check.c
+++ b/erts/emulator/beam/erl_lock_check.c
@@ -96,6 +96,7 @@ static erts_lc_lock_order_t erts_lock_order[] = {
{ "dist_entry", "address" },
{ "dist_entry_links", "address" },
{ "code_write_permission", NULL },
+ { "purge_state", NULL },
{ "proc_status", "pid" },
{ "proc_trace", "pid" },
{ "ports_snapshot", NULL },
@@ -112,6 +113,9 @@ static erts_lc_lock_order_t erts_lock_order[] = {
{ "export_tab", NULL },
{ "fun_tab", NULL },
{ "environ", NULL },
+#ifdef ERTS_NEW_PURGE_STRATEGY
+ { "release_literal_areas", NULL },
+#endif
#endif
{ "efile_drv", "address" },
{ "drv_ev_state_grow", NULL, },
diff --git a/erts/emulator/beam/erl_message.c b/erts/emulator/beam/erl_message.c
index 91e06cde2d..e4c696ae3b 100644
--- a/erts/emulator/beam/erl_message.c
+++ b/erts/emulator/beam/erl_message.c
@@ -1842,3 +1842,20 @@ void erts_factory_undo(ErtsHeapFactory* factory)
factory->heap_frags = NULL;
#endif
}
+
+Uint
+erts_mbuf_size(Process *p)
+{
+ Uint sz = 0;
+ ErlHeapFragment* bp;
+ ErtsMessage* mp;
+
+ for (bp = p->mbuf; bp; bp = bp->next)
+ sz += bp->used_size;
+
+ for (mp = p->msg_frag; mp; mp = mp->next)
+ for (bp = erts_message_to_heap_frag(mp); bp; bp = bp->next)
+ sz += bp->used_size;
+
+ return sz;
+}
diff --git a/erts/emulator/beam/erl_message.h b/erts/emulator/beam/erl_message.h
index 6df969367b..42ed14e69c 100644
--- a/erts/emulator/beam/erl_message.h
+++ b/erts/emulator/beam/erl_message.h
@@ -499,4 +499,15 @@ erts_msgq_replace_msg_ref(ErlMessageQueue *msgq, ErtsMessage *newp, ErtsMessage
#endif
+Uint erts_mbuf_size(Process *p);
+#if defined(DEBUG) || 0
+# define ERTS_CHK_MBUF_SZ(P) \
+ do { \
+ Uint actual_mbuf_sz__ = erts_mbuf_size((P)); \
+ ERTS_ASSERT((P)->mbuf_sz >= actual_mbuf_sz__); \
+ } while (0)
+#else
+# define ERTS_CHK_MBUF_SZ(P) ((void) 1)
+#endif
+
#endif
diff --git a/erts/emulator/beam/erl_nif.c b/erts/emulator/beam/erl_nif.c
index c6127a4967..3a547982da 100644
--- a/erts/emulator/beam/erl_nif.c
+++ b/erts/emulator/beam/erl_nif.c
@@ -120,8 +120,10 @@ execution_state(ErlNifEnv *env, Process **c_pp, int *schedp)
else {
Process *c_p = env->proc;
- if (!(c_p->static_flags & ERTS_STC_FLG_SHADOW_PROC))
- ASSERT(is_scheduler() > 0);
+ if (!(c_p->static_flags & ERTS_STC_FLG_SHADOW_PROC)) {
+ ERTS_SMP_LC_ASSERT(erts_proc_lc_my_proc_locks(c_p)
+ & ERTS_PROC_LOCK_MAIN);
+ }
else {
c_p = env->proc->next;
ASSERT(is_scheduler() < 0);
@@ -154,7 +156,9 @@ static Eterm* alloc_heap_heavy(ErlNifEnv* env, size_t need, Eterm* hp)
HEAP_TOP(env->proc) = env->hp;
}
else {
- env->heap_frag->used_size = hp - env->heap_frag->mem;
+ Uint usz = env->hp - env->heap_frag->mem;
+ env->proc->mbuf_sz += usz - env->heap_frag->used_size;
+ env->heap_frag->used_size = usz;
ASSERT(env->heap_frag->used_size <= env->heap_frag->alloc_size);
}
hp = erts_heap_alloc(env->proc, need, MIN_HEAP_FRAG_SZ);
@@ -208,11 +212,16 @@ void erts_pre_nif(ErlNifEnv* env, Process* p, struct erl_module_nif* mod_nif,
#endif
}
+static void full_cache_env(ErlNifEnv *env);
+static void cache_env(ErlNifEnv* env);
+static void full_flush_env(ErlNifEnv *env);
+static void flush_env(ErlNifEnv* env);
+
+#ifdef ERTS_DIRTY_SCHEDULERS
void erts_pre_dirty_nif(ErtsSchedulerData *esdp,
- ErlNifEnv* env, Process* p, struct erl_module_nif* mod_nif,
- Process* tracee)
+ ErlNifEnv* env, Process* p,
+ struct erl_module_nif* mod_nif)
{
-#ifdef ERTS_DIRTY_SCHEDULERS
Process *sproc;
#ifdef DEBUG
erts_aint32_t state = erts_smp_atomic32_read_nob(&p->state);
@@ -223,7 +232,7 @@ void erts_pre_dirty_nif(ErtsSchedulerData *esdp,
ASSERT(esdp);
#endif
- erts_pre_nif(env, p, mod_nif, tracee);
+ erts_pre_nif(env, p, mod_nif, NULL);
sproc = esdp->dirty_shadow_process;
ASSERT(sproc);
@@ -235,22 +244,10 @@ void erts_pre_dirty_nif(ErtsSchedulerData *esdp,
sproc->next = p;
sproc->common.id = p->common.id;
- sproc->htop = p->htop;
- sproc->stop = p->stop;
- sproc->hend = p->hend;
- sproc->heap = p->heap;
- sproc->abandoned_heap = p->abandoned_heap;
- sproc->heap_sz = p->heap_sz;
- sproc->high_water = p->high_water;
- sproc->old_hend = p->old_hend;
- sproc->old_htop = p->old_htop;
- sproc->old_heap = p->old_heap;
- sproc->mbuf = NULL;
- sproc->mbuf_sz = 0;
- ERTS_INIT_OFF_HEAP(&sproc->off_heap);
env->proc = sproc;
-#endif
+ full_cache_env(env);
}
+#endif
/* Temporary object header, auto-deallocated when NIF returns
* or when independent environment is cleared.
@@ -274,32 +271,37 @@ static ERTS_INLINE void free_tmp_objs(ErlNifEnv* env)
void erts_post_nif(ErlNifEnv* env)
{
erts_unblock_fpe(env->fpe_was_unmasked);
+ full_flush_env(env);
+ free_tmp_objs(env);
+ env->exiting = ERTS_PROC_IS_EXITING(env->proc);
+}
#ifdef ERTS_DIRTY_SCHEDULERS
- if (!(env->proc->static_flags & ERTS_STC_FLG_SHADOW_PROC))
+void erts_post_dirty_nif(ErlNifEnv* env)
+{
+ Process *c_p;
+ ASSERT(env->proc->static_flags & ERTS_STC_FLG_SHADOW_PROC);
+ ASSERT(env->proc->next);
+ erts_unblock_fpe(env->fpe_was_unmasked);
+ full_flush_env(env);
+ free_tmp_objs(env);
+ c_p = env->proc->next;
+ env->exiting = ERTS_PROC_IS_EXITING(c_p);
+ ERTS_VBUMP_ALL_REDS(c_p);
+}
#endif
- {
- ASSERT(is_scheduler() > 0);
- if (env->heap_frag == NULL) {
- ASSERT(env->hp_end == HEAP_LIMIT(env->proc));
- ASSERT(env->hp >= HEAP_TOP(env->proc));
- ASSERT(env->hp <= HEAP_LIMIT(env->proc));
- HEAP_TOP(env->proc) = env->hp;
- }
- else {
- ASSERT(env->hp_end != HEAP_LIMIT(env->proc));
- ASSERT(env->hp_end - env->hp <= env->heap_frag->alloc_size);
- env->heap_frag->used_size = env->hp - env->heap_frag->mem;
- ASSERT(env->heap_frag->used_size <= env->heap_frag->alloc_size);
- }
- env->exiting = ERTS_PROC_IS_EXITING(env->proc);
- }
+
+static void full_flush_env(ErlNifEnv* env)
+{
#ifdef ERTS_DIRTY_SCHEDULERS
- else { /* Dirty nif call using shadow process struct */
+ if (env->proc->static_flags & ERTS_STC_FLG_SHADOW_PROC) {
+ /* Dirty nif call using shadow process struct */
Process *c_p = env->proc->next;
ASSERT(is_scheduler() < 0);
ASSERT(env->proc->common.id == c_p->common.id);
+ ERTS_SMP_LC_ASSERT(erts_proc_lc_my_proc_locks(c_p)
+ & ERTS_PROC_LOCK_MAIN);
if (!env->heap_frag) {
ASSERT(env->hp_end == HEAP_LIMIT(c_p));
@@ -308,11 +310,14 @@ void erts_post_nif(ErlNifEnv* env)
HEAP_TOP(c_p) = env->hp;
}
else {
+ Uint usz;
ASSERT(env->hp_end != HEAP_LIMIT(c_p));
ASSERT(env->hp_end - env->hp <= env->heap_frag->alloc_size);
HEAP_TOP(c_p) = HEAP_TOP(env->proc);
- env->heap_frag->used_size = env->hp - env->heap_frag->mem;
+ usz = env->hp - env->heap_frag->mem;
+ env->proc->mbuf_sz += usz - env->heap_frag->used_size;
+ env->heap_frag->used_size = usz;
ASSERT(env->heap_frag->used_size <= env->heap_frag->alloc_size);
@@ -339,11 +344,48 @@ void erts_post_nif(ErlNifEnv* env)
}
c_p->off_heap.overhead += env->proc->off_heap.overhead;
- env->exiting = ERTS_PROC_IS_EXITING(c_p);
- BUMP_ALL_REDS(c_p);
+ return;
}
#endif
- free_tmp_objs(env);
+
+ flush_env(env);
+}
+
+static void full_cache_env(ErlNifEnv* env)
+{
+#ifdef ERTS_DIRTY_SCHEDULERS
+ if (env->proc->static_flags & ERTS_STC_FLG_SHADOW_PROC) {
+ /* Dirty nif call using shadow process struct */
+ Process *sproc = env->proc;
+ Process *c_p = sproc->next;
+ ASSERT(c_p);
+ ASSERT(is_scheduler() < 0);
+ ASSERT(env->proc->common.id == c_p->common.id);
+ ERTS_SMP_LC_ASSERT(erts_proc_lc_my_proc_locks(c_p)
+ & ERTS_PROC_LOCK_MAIN);
+
+ sproc->htop = c_p->htop;
+ sproc->stop = c_p->stop;
+ sproc->hend = c_p->hend;
+ sproc->heap = c_p->heap;
+ sproc->abandoned_heap = c_p->abandoned_heap;
+ sproc->heap_sz = c_p->heap_sz;
+ sproc->high_water = c_p->high_water;
+ sproc->old_hend = c_p->old_hend;
+ sproc->old_htop = c_p->old_htop;
+ sproc->old_heap = c_p->old_heap;
+ sproc->mbuf = NULL;
+ sproc->mbuf_sz = 0;
+ ERTS_INIT_OFF_HEAP(&sproc->off_heap);
+
+ env->hp_end = HEAP_LIMIT(c_p);
+ env->hp = HEAP_TOP(c_p);
+ env->heap_frag = NULL;
+ return;
+ }
+#endif
+
+ cache_env(env);
}
/* Flush out our cached heap pointers to allow an ordinary HAlloc
@@ -357,9 +399,12 @@ static void flush_env(ErlNifEnv* env)
HEAP_TOP(env->proc) = env->hp;
}
else {
+ Uint usz;
ASSERT(env->hp_end != HEAP_LIMIT(env->proc));
ASSERT(env->hp_end - env->hp <= env->heap_frag->alloc_size);
- env->heap_frag->used_size = env->hp - env->heap_frag->mem;
+ usz = env->hp - env->heap_frag->mem;
+ env->proc->mbuf_sz += usz - env->heap_frag->used_size;
+ env->heap_frag->used_size = usz;
ASSERT(env->heap_frag->used_size <= env->heap_frag->alloc_size);
}
}
@@ -600,17 +645,32 @@ int enif_send(ErlNifEnv* env, const ErlNifPid* to_pid,
if (scheduler > 0) { /* Normal scheduler */
rp = erts_proc_lookup(receiver);
- if (c_p == rp)
- rp_locks = ERTS_PROC_LOCK_MAIN;
+ if (!rp)
+ return 0;
}
else {
- if (c_p && ERTS_PROC_IS_EXITING(c_p))
- return 0;
- rp = erts_pid2proc_opt(c_p, 0, receiver, rp_locks,
+ if (c_p) {
+ ASSERT(scheduler < 0); /* Dirty scheduler */
+ if (ERTS_PROC_IS_EXITING(c_p))
+ return 0;
+
+ if (env->proc->static_flags & ERTS_STC_FLG_SHADOW_PROC) {
+ erts_smp_proc_lock(c_p, ERTS_PROC_LOCK_MAIN);
+ }
+ }
+
+ rp = erts_pid2proc_opt(c_p, ERTS_PROC_LOCK_MAIN,
+ receiver, rp_locks,
ERTS_P2P_FLG_INC_REFC);
+ if (!rp) {
+ if (c_p && (env->proc->static_flags & ERTS_STC_FLG_SHADOW_PROC))
+ erts_smp_proc_unlock(c_p, ERTS_PROC_LOCK_MAIN);
+ return 0;
+ }
}
- if (rp == NULL)
- return 0;
+
+ if (c_p == rp)
+ rp_locks = ERTS_PROC_LOCK_MAIN;
if (menv) {
flush_env(msg_env);
@@ -631,9 +691,9 @@ int enif_send(ErlNifEnv* env, const ErlNifPid* to_pid,
ErlOffHeap *ohp;
Eterm *hp;
if (env && !env->tracee) {
- flush_env(env);
+ full_flush_env(env);
mp = erts_alloc_message_heap(rp, &rp_locks, sz, &hp, &ohp);
- cache_env(env);
+ full_cache_env(env);
}
else {
erts_aint_t state = erts_smp_atomic32_read_nob(&rp->state);
@@ -656,8 +716,11 @@ int enif_send(ErlNifEnv* env, const ErlNifPid* to_pid,
if (!env || !env->tracee) {
- if (c_p && IS_TRACED_FL(c_p, F_TRACE_SEND))
+ if (c_p && IS_TRACED_FL(c_p, F_TRACE_SEND)) {
+ full_flush_env(env);
trace_send(c_p, receiver, msg);
+ full_cache_env(env);
+ }
}
#ifdef ERTS_SMP
else {
@@ -690,10 +753,6 @@ int enif_send(ErlNifEnv* env, const ErlNifPid* to_pid,
erts_smp_proc_trylock(rp, ERTS_PROC_LOCK_MSGQ) == EBUSY) {
if (!msgq) {
-#ifdef ERTS_SMP
- ErtsThrPrgrDelayHandle dhndl;
-#endif
-
msgq = erts_alloc(ERTS_ALC_T_TRACE_MSG_QUEUE,
sizeof(ErlTraceMessageQueue));
msgq->receiver = receiver;
@@ -707,15 +766,7 @@ int enif_send(ErlNifEnv* env, const ErlNifPid* to_pid,
erts_smp_proc_unlock(t_p, ERTS_PROC_LOCK_TRACE);
-#ifdef ERTS_SMP
- if (!scheduler)
- dhndl = erts_thr_progress_unmanaged_delay();
-#endif
- erts_schedule_flush_trace_messages(t_p->common.id);
-#ifdef ERTS_SMP
- if (!scheduler)
- erts_thr_progress_unmanaged_continue(dhndl);
-#endif
+ erts_schedule_flush_trace_messages(t_p, 0);
} else {
msgq->len++;
*msgq->last = mp;
@@ -740,6 +791,8 @@ done:
rp_locks &= ~ERTS_PROC_LOCK_MAIN;
if (rp_locks & ~lc_locks)
erts_smp_proc_unlock(rp, rp_locks & ~lc_locks);
+ if (c_p && (env->proc->static_flags & ERTS_STC_FLG_SHADOW_PROC))
+ erts_smp_proc_unlock(c_p, ERTS_PROC_LOCK_MAIN);
#endif
if (scheduler <= 0)
erts_proc_dec_refc(rp);
@@ -2215,7 +2268,7 @@ int enif_consume_timeslice(ErlNifEnv* env, int percent)
* NIF exports need a few more items than the Export struct provides,
* including the erl_module_nif* and a NIF function pointer, so the
* NifExport below adds those. The Export member must be first in the
- * struct. The saved_mfa, exception_thrown, saved_argc, rootset_extra, and
+ * struct. The saved_current, exception_thrown, saved_argc, rootset_extra, and
* rootset members are used to track the MFA, any pending exception, and
* arguments of the top NIF in case a chain of one or more
* enif_schedule_nif() calls results in an exception, since in that case
@@ -2229,7 +2282,7 @@ typedef struct {
Export exp;
struct erl_module_nif* m;
NativeFunPtr fp;
- Eterm saved_mfa[3];
+ BeamInstr *saved_current;
int exception_thrown;
int saved_argc;
int rootset_extra;
@@ -2340,9 +2393,8 @@ init_nif_sched_data(ErlNifEnv* env, NativeFunPtr direct_fp, NativeFunPtr indirec
reg[i] = (Eterm) argv[i];
}
if (need_save) {
- ep->saved_mfa[0] = proc->current[0];
- ep->saved_mfa[1] = proc->current[1];
- ep->saved_mfa[2] = proc->current[2];
+ ASSERT(proc->current);
+ ep->saved_current = proc->current;
ep->saved_argc = argc;
}
proc->i = (BeamInstr*) ep->exp.addressv[0];
@@ -2365,21 +2417,21 @@ static void
restore_nif_mfa(Process* proc, NifExport* ep, int exception)
{
int i;
- Eterm* reg = erts_proc_sched_data(proc)->x_reg_array;
ERTS_SMP_LC_ASSERT(!(proc->static_flags
& ERTS_STC_FLG_SHADOW_PROC));
ERTS_SMP_LC_ASSERT(erts_proc_lc_my_proc_locks(proc)
& ERTS_PROC_LOCK_MAIN);
- proc->current[0] = ep->saved_mfa[0];
- proc->current[1] = ep->saved_mfa[1];
- proc->current[2] = ep->saved_mfa[2];
- if (exception)
+ ASSERT(ep->saved_current != &ep->exp.code[0]);
+ proc->current = ep->saved_current;
+ ep->saved_current = NULL;
+ if (exception) {
+ Eterm* reg = erts_proc_sched_data(proc)->x_reg_array;
for (i = 0; i < ep->saved_argc; i++)
reg[i] = ep->rootset[i+1];
+ }
ep->saved_argc = 0;
- ep->saved_mfa[0] = THE_NON_VALUE;
}
#ifdef ERTS_DIRTY_SCHEDULERS
@@ -2454,6 +2506,7 @@ execute_dirty_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
*/
ep = (NifExport*) ERTS_PROC_GET_NIF_TRAP_EXPORT(proc);
ASSERT(ep && fp);
+
ep->fp = NULL;
erts_smp_atomic32_read_band_mb(&proc->state, ~(ERTS_PSFLG_DIRTY_CPU_PROC
| ERTS_PSFLG_DIRTY_IO_PROC));
@@ -2538,7 +2591,7 @@ schedule_dirty_nif(ErlNifEnv* env, int flags, int argc, const ERL_NIF_TERM argv[
}
ep = (NifExport*) ERTS_PROC_GET_NIF_TRAP_EXPORT(proc);
- need_save = (ep == NULL || is_non_value(ep->saved_mfa[0]));
+ need_save = (ep == NULL || !ep->saved_current);
result = init_nif_sched_data(env, execute_dirty_nif, fp, need_save, argc, argv);
if (scheduler <= 0)
erts_smp_proc_unlock(proc, ERTS_PROC_LOCK_MAIN);
@@ -2616,7 +2669,7 @@ enif_schedule_nif(ErlNifEnv* env, const char* fun_name, int flags,
}
ep = (NifExport*) ERTS_PROC_GET_NIF_TRAP_EXPORT(proc);
- need_save = (ep == NULL || is_non_value(ep->saved_mfa[0]));
+ need_save = (ep == NULL || !ep->saved_current);
if (flags) {
#ifdef ERTS_DIRTY_SCHEDULERS
@@ -3150,18 +3203,20 @@ BIF_RETTYPE load_nif_2(BIF_ALIST_2)
if (init_func != NULL)
handle = init_func;
+ this_mi = &module_p->curr;
+ prev_mi = &module_p->old;
if (in_area(caller, module_p->old.code_hdr, module_p->old.code_length)) {
- if (module_p->old.code_hdr->on_load_function_ptr) {
- this_mi = &module_p->old;
+ ret = load_nif_error(BIF_P, "old_code", "Calling load_nif from old "
+ "module '%T' not allowed", mod_atom);
+ goto error;
+ } else if (module_p->on_load) {
+ ASSERT(module_p->on_load->code_hdr->on_load_function_ptr);
+ if (module_p->curr.code_hdr) {
prev_mi = &module_p->curr;
} else {
- ret = load_nif_error(BIF_P, "old_code", "Calling load_nif from old "
- "module '%T' not allowed", mod_atom);
- goto error;
+ prev_mi = &module_p->old;
}
- } else {
- this_mi = &module_p->curr;
- prev_mi = &module_p->old;
+ this_mi = module_p->on_load;
}
if (init_func == NULL &&
diff --git a/erts/emulator/beam/erl_process.c b/erts/emulator/beam/erl_process.c
index b5d8c5bc75..bc59147c6c 100644
--- a/erts/emulator/beam/erl_process.c
+++ b/erts/emulator/beam/erl_process.c
@@ -448,6 +448,9 @@ int erts_system_profile_ts_type = ERTS_TRACE_FLG_NOW_TIMESTAMP;
typedef enum {
ERTS_PSTT_GC, /* Garbage Collect */
ERTS_PSTT_CPC, /* Check Process Code */
+#ifdef ERTS_NEW_PURGE_STRATEGY
+ ERTS_PSTT_CLA, /* Copy Literal Area */
+#endif
ERTS_PSTT_COHMQ, /* Change off heap message queue */
ERTS_PSTT_FTMQ /* Flush trace msg queue */
} ErtsProcSysTaskType;
@@ -6671,14 +6674,24 @@ erts_schedule_process(Process *p, erts_aint32_t state, ErtsProcLocks locks)
}
static int
-schedule_process_sys_task(Process *p, erts_aint32_t prio, ErtsProcSysTask *st)
+schedule_process_sys_task(Process *p, erts_aint32_t prio, ErtsProcSysTask *st,
+ erts_aint32_t *fail_state_p)
{
int res;
int locked;
ErtsProcSysTaskQs *stqs, *free_stqs;
- erts_aint32_t state, a, n, enq_prio;
+ erts_aint32_t fail_state, state, a, n, enq_prio;
int enqueue; /* < 0 -> use proxy */
unsigned int prof_runnable_procs;
+ int strict_fail_state;
+
+ fail_state = *fail_state_p;
+ /*
+ * If fail state something other than just exiting process,
+ * ensure that the task wont be scheduled when the
+ * receiver is in the failure state.
+ */
+ strict_fail_state = fail_state != ERTS_PSFLG_EXITING;
res = 1; /* prepare for success */
st->next = st->prev = st; /* Prep for empty prio queue */
@@ -6705,7 +6718,8 @@ schedule_process_sys_task(Process *p, erts_aint32_t prio, ErtsProcSysTask *st)
erts_smp_proc_lock(p, ERTS_PROC_LOCK_STATUS);
state = erts_smp_atomic32_read_nob(&p->state);
- if (state & ERTS_PSFLG_EXITING) {
+ if (state & fail_state) {
+ *fail_state_p = (state & fail_state);
free_stqs = stqs;
res = 0;
goto cleanup;
@@ -6759,7 +6773,7 @@ schedule_process_sys_task(Process *p, erts_aint32_t prio, ErtsProcSysTask *st)
/* Status lock prevents out of order "runnable proc" trace msgs */
ERTS_SMP_LC_ASSERT(ERTS_PROC_LOCK_STATUS & erts_proc_lc_my_proc_locks(p));
- if (!prof_runnable_procs) {
+ if (!prof_runnable_procs && !strict_fail_state) {
erts_smp_proc_unlock(p, ERTS_PROC_LOCK_STATUS);
locked = 0;
}
@@ -6770,6 +6784,11 @@ schedule_process_sys_task(Process *p, erts_aint32_t prio, ErtsProcSysTask *st)
erts_aint32_t e;
n = e = a;
+ if (strict_fail_state && (a & fail_state)) {
+ *fail_state_p = (a & fail_state);
+ goto cleanup;
+ }
+
if (a & ERTS_PSFLG_FREE)
goto cleanup; /* We don't want to schedule free processes... */
@@ -9092,6 +9111,27 @@ resume_process_1(BIF_ALIST_1)
BIF_ERROR(BIF_P, BADARG);
}
+BIF_RETTYPE
+erts_internal_is_process_executing_dirty_1(BIF_ALIST_1)
+{
+ if (is_not_internal_pid(BIF_ARG_1))
+ BIF_ERROR(BIF_P, BADARG);
+#ifdef ERTS_DIRTY_SCHEDULERS
+ else {
+ Process *rp = erts_proc_lookup(BIF_ARG_1);
+ if (rp) {
+ erts_aint32_t state = erts_smp_atomic32_read_nob(&rp->state);
+ if (state & (ERTS_PSFLG_DIRTY_RUNNING
+ |ERTS_PSFLG_DIRTY_RUNNING_SYS)) {
+ BIF_RET(am_true);
+ }
+ }
+ }
+#endif
+ BIF_RET(am_false);
+}
+
+
Uint
erts_run_queues_len(Uint *qlen, int atomic_queues_read, int incl_active_sched)
{
@@ -9368,6 +9408,14 @@ erts_set_process_priority(Process *p, Eterm value)
}
}
+#ifdef __WIN32__
+Sint64
+erts_time2reds(ErtsMonotonicTime start, ErtsMonotonicTime end)
+{
+ return ERTS_TIME2REDS_IMPL__(start, end);
+}
+#endif
+
static int
scheduler_gc_proc(Process *c_p, int reds_left)
{
@@ -9498,15 +9546,6 @@ Process *erts_schedule(ErtsSchedulerData *esdp, Process *p, int calls)
p->reds += actual_reds;
-#ifdef ERTS_SMP
- erts_smp_proc_lock(p, ERTS_PROC_LOCK_TRACE);
- if (p->trace_msg_q) {
- erts_smp_proc_unlock(p, ERTS_PROC_LOCK_TRACE);
- erts_schedule_flush_trace_messages(p->common.id);
- } else
- erts_smp_proc_unlock(p, ERTS_PROC_LOCK_TRACE);
-#endif
-
state = erts_smp_atomic32_read_nob(&p->state);
if (IS_TRACED(p)) {
@@ -9526,7 +9565,15 @@ Process *erts_schedule(ErtsSchedulerData *esdp, Process *p, int calls)
}
}
- erts_smp_proc_lock(p, ERTS_PROC_LOCK_STATUS);
+ erts_smp_proc_lock(p, ERTS_PROC_LOCK_STATUS|ERTS_PROC_LOCK_TRACE);
+
+#ifdef ERTS_SMP
+ if (p->trace_msg_q) {
+ erts_smp_proc_unlock(p, ERTS_PROC_LOCK_STATUS|ERTS_PROC_LOCK_TRACE);
+ erts_schedule_flush_trace_messages(p, 1);
+ erts_smp_proc_lock(p, ERTS_PROC_LOCK_STATUS|ERTS_PROC_LOCK_TRACE);
+ }
+#endif
/* have to re-read state after taking lock */
state = erts_smp_atomic32_read_nob(&p->state);
@@ -9534,9 +9581,11 @@ Process *erts_schedule(ErtsSchedulerData *esdp, Process *p, int calls)
#ifdef ERTS_SMP
if (is_normal_sched && (state & ERTS_PSFLG_PENDING_EXIT))
erts_handle_pending_exit(p, (ERTS_PROC_LOCK_MAIN
+ | ERTS_PROC_LOCK_TRACE
| ERTS_PROC_LOCK_STATUS));
if (p->pending_suspenders)
handle_pending_suspend(p, (ERTS_PROC_LOCK_MAIN
+ | ERTS_PROC_LOCK_TRACE
| ERTS_PROC_LOCK_STATUS));
#endif
@@ -9556,7 +9605,9 @@ Process *erts_schedule(ErtsSchedulerData *esdp, Process *p, int calls)
p->scheduler_data = NULL;
#endif
- erts_smp_proc_unlock(p, ERTS_PROC_LOCK_MAIN|ERTS_PROC_LOCK_STATUS);
+ erts_smp_proc_unlock(p, (ERTS_PROC_LOCK_MAIN
+ | ERTS_PROC_LOCK_STATUS
+ | ERTS_PROC_LOCK_TRACE));
ERTS_MSACC_SET_STATE_CACHED_M(ERTS_MSACC_STATE_OTHER);
@@ -9984,6 +10035,11 @@ Process *erts_schedule(ErtsSchedulerData *esdp, Process *p, int calls)
if (state & (ERTS_PSFLG_ACTIVE_SYS
| ERTS_PSFLG_PENDING_EXIT
| ERTS_PSFLG_EXITING)) {
+ /*
+ * IMPORTANT! We need to take care of
+ * scheduled check-process-code requests
+ * before continuing with dirty execution!
+ */
/* Migrate to normal scheduler... */
goto sunlock_sched_out_proc;
}
@@ -10393,6 +10449,27 @@ execute_sys_tasks(Process *c_p, erts_aint32_t *statep, int in_reds)
}
break;
}
+#ifdef ERTS_NEW_PURGE_STRATEGY
+ case ERTS_PSTT_CLA: {
+ int fcalls;
+ int cla_reds = 0;
+ if (!ERTS_PROC_GET_SAVED_CALLS_BUF(c_p))
+ fcalls = reds;
+ else
+ fcalls = reds - CONTEXT_REDS;
+ st_res = erts_proc_copy_literal_area(c_p,
+ &cla_reds,
+ fcalls,
+ st->arg[0] == am_true);
+ reds -= cla_reds;
+ if (is_non_value(st_res)) {
+ /* Needed gc, but gc was disabled */
+ save_gc_task(c_p, st, st_prio);
+ st = NULL;
+ }
+ break;
+ }
+#endif
case ERTS_PSTT_COHMQ:
reds -= erts_complete_off_heap_message_queue_change(c_p);
st_res = am_true;
@@ -10443,14 +10520,15 @@ cleanup_sys_tasks(Process *c_p, erts_aint32_t in_state, int in_reds)
switch (st->type) {
case ERTS_PSTT_GC:
- st_res = am_false;
- break;
case ERTS_PSTT_CPC:
- st_res = am_false;
- break;
case ERTS_PSTT_COHMQ:
st_res = am_false;
break;
+#ifdef ERTS_NEW_PURGE_STRATEGY
+ case ERTS_PSTT_CLA:
+ st_res = am_ok;
+ break;
+#endif
#ifdef ERTS_SMP
case ERTS_PSTT_FTMQ:
reds -= erts_flush_trace_messages(c_p, ERTS_PROC_LOCK_MAIN);
@@ -10471,22 +10549,83 @@ cleanup_sys_tasks(Process *c_p, erts_aint32_t in_state, int in_reds)
return reds;
}
-BIF_RETTYPE
-erts_internal_request_system_task_3(BIF_ALIST_3)
+#ifdef ERTS_DIRTY_SCHEDULERS
+
+static BIF_RETTYPE
+dispatch_system_task(Process *c_p, erts_aint_t fail_state,
+ ErtsProcSysTask *st, Eterm target,
+ Eterm prio, Eterm operation)
+{
+ Process *rp;
+ ErtsProcLocks rp_locks = 0;
+ ErlOffHeap *ohp;
+ ErtsMessage *mp;
+ Eterm *hp, msg;
+ Uint hsz, osz;
+ BIF_RETTYPE ret;
+
+ switch (st->type) {
+ case ERTS_PSTT_CPC:
+ rp = erts_dirty_process_code_checker;
+ ASSERT(fail_state & (ERTS_PSFLG_DIRTY_RUNNING
+ | ERTS_PSFLG_DIRTY_RUNNING_SYS));
+ if (c_p == rp) {
+ ERTS_BIF_PREP_RET(ret, am_dirty_execution);
+ return ret;
+ }
+ break;
+ default:
+ rp = NULL;
+ ERTS_INTERNAL_ERROR("Non-dispatchable system task");
+ break;
+ }
+
+ ERTS_BIF_PREP_RET(ret, am_ok);
+
+ /*
+ * Send message on the form: {Requester, Target, Operation}
+ */
+
+ ASSERT(is_immed(st->requester));
+ ASSERT(is_immed(target));
+ ASSERT(is_immed(prio));
+
+ osz = size_object(operation);
+ hsz = 5 /* 4-tuple */ + osz;
+
+ mp = erts_alloc_message_heap(rp, &rp_locks, hsz, &hp, &ohp);
+
+ msg = copy_struct(operation, osz, &hp, ohp);
+ msg = TUPLE4(hp, st->requester, target, prio, msg);
+
+ erts_queue_message(rp, rp_locks, mp, msg, st->requester);
+
+ if (rp_locks)
+ erts_smp_proc_unlock(rp, rp_locks);
+
+ return ret;
+}
+
+#endif
+
+static BIF_RETTYPE
+request_system_task(Process *c_p, Eterm requester, Eterm target,
+ Eterm priority, Eterm operation)
{
- Process *rp = erts_proc_lookup(BIF_ARG_1);
+ BIF_RETTYPE ret;
+ Process *rp = erts_proc_lookup(target);
ErtsProcSysTask *st = NULL;
- erts_aint32_t prio;
+ erts_aint32_t prio, fail_state = ERTS_PSFLG_EXITING;
Eterm noproc_res, req_type;
- if (!rp && !is_internal_pid(BIF_ARG_1)) {
- if (!is_external_pid(BIF_ARG_1))
+ if (!rp && !is_internal_pid(target)) {
+ if (!is_external_pid(target))
goto badarg;
- if (external_pid_dist_entry(BIF_ARG_1) != erts_this_dist_entry)
+ if (external_pid_dist_entry(target) != erts_this_dist_entry)
goto badarg;
}
- switch (BIF_ARG_2) {
+ switch (priority) {
case am_max: prio = PRIORITY_MAX; break;
case am_high: prio = PRIORITY_HIGH; break;
case am_normal: prio = PRIORITY_NORMAL; break;
@@ -10494,11 +10633,11 @@ erts_internal_request_system_task_3(BIF_ALIST_3)
default: goto badarg;
}
- if (is_not_tuple(BIF_ARG_3))
+ if (is_not_tuple(operation))
goto badarg;
else {
int i;
- Eterm *tp = tuple_val(BIF_ARG_3);
+ Eterm *tp = tuple_val(operation);
Uint arity = arityval(*tp);
Eterm req_id;
Uint req_id_sz;
@@ -10536,7 +10675,7 @@ erts_internal_request_system_task_3(BIF_ALIST_3)
ERTS_INIT_OFF_HEAP(&st->off_heap);
hp = &st->heap[0];
- st->requester = BIF_P->common.id;
+ st->requester = requester;
st->reply_tag = req_type;
st->req_id_sz = req_id_sz;
st->req_id = req_id_sz == 0 ? req_id : copy_struct(req_id,
@@ -10570,26 +10709,85 @@ erts_internal_request_system_task_3(BIF_ALIST_3)
st->type = ERTS_PSTT_CPC;
if (!rp)
goto noproc;
+#ifdef ERTS_DIRTY_SCHEDULERS
+ /*
+ * If the process should start executing dirty
+ * code it is important that this task is
+ * aborted. Therefore this strict fail state...
+ */
+ fail_state |= (ERTS_PSFLG_DIRTY_RUNNING
+ | ERTS_PSFLG_DIRTY_RUNNING_SYS);
+#endif
break;
+#ifdef ERTS_NEW_PURGE_STRATEGY
+ case am_copy_literals:
+ if (st->arg[0] != am_true && st->arg[0] != am_false)
+ goto badarg;
+ st->type = ERTS_PSTT_CLA;
+ noproc_res = am_ok;
+ if (!rp)
+ goto noproc;
+ break;
+#endif
+
default:
goto badarg;
}
- if (!schedule_process_sys_task(rp, prio, st)) {
- noproc:
- notify_sys_task_executed(BIF_P, st, noproc_res);
+ if (!schedule_process_sys_task(rp, prio, st, &fail_state)) {
+ Eterm failure;
+ if (fail_state & ERTS_PSFLG_EXITING) {
+ noproc:
+ failure = noproc_res;
+ }
+#ifdef ERTS_DIRTY_SCHEDULERS
+ else if (fail_state & (ERTS_PSFLG_DIRTY_RUNNING
+ | ERTS_PSFLG_DIRTY_RUNNING_SYS)) {
+ ret = dispatch_system_task(c_p, fail_state, st,
+ target, priority, operation);
+ goto cleanup_return;
+ }
+#endif
+ else {
+ ERTS_INTERNAL_ERROR("Unknown failure schedule_process_sys_task()");
+ failure = am_internal_error;
+ }
+ notify_sys_task_executed(c_p, st, failure);
}
- BIF_RET(am_ok);
+ ERTS_BIF_PREP_RET(ret, am_ok);
+
+ return ret;
badarg:
+ ERTS_BIF_PREP_ERROR(ret, c_p, BADARG);
+
+#ifdef ERTS_DIRTY_SCHEDULERS
+cleanup_return:
+#endif
+
if (st) {
erts_cleanup_offheap(&st->off_heap);
erts_free(ERTS_ALC_T_PROC_SYS_TSK, st);
}
- BIF_ERROR(BIF_P, BADARG);
+
+ return ret;
+}
+
+BIF_RETTYPE
+erts_internal_request_system_task_3(BIF_ALIST_3)
+{
+ return request_system_task(BIF_P, BIF_P->common.id,
+ BIF_ARG_1, BIF_ARG_2, BIF_ARG_3);
+}
+
+BIF_RETTYPE
+erts_internal_request_system_task_4(BIF_ALIST_4)
+{
+ return request_system_task(BIF_P, BIF_ARG_1,
+ BIF_ARG_2, BIF_ARG_3, BIF_ARG_4);
}
static void
@@ -10598,7 +10796,7 @@ erts_schedule_generic_sys_task(Eterm pid, ErtsProcSysTaskType type)
Process *rp = erts_proc_lookup(pid);
if (rp) {
ErtsProcSysTask *st;
- erts_aint32_t state;
+ erts_aint32_t state, fail_state;
int i;
st = erts_alloc(ERTS_ALC_T_PROC_SYS_TSK,
@@ -10613,21 +10811,103 @@ erts_schedule_generic_sys_task(Eterm pid, ErtsProcSysTaskType type)
ERTS_INIT_OFF_HEAP(&st->off_heap);
state = erts_smp_atomic32_read_nob(&rp->state);
- if (!schedule_process_sys_task(rp, ERTS_PSFLGS_GET_USR_PRIO(state), st))
+ fail_state = ERTS_PSFLG_EXITING;
+
+ if (!schedule_process_sys_task(rp, ERTS_PSFLGS_GET_USR_PRIO(state),
+ st, &fail_state))
erts_free(ERTS_ALC_T_PROC_SYS_TSK, st);
}
}
+
void
erts_schedule_complete_off_heap_message_queue_change(Eterm pid)
{
erts_schedule_generic_sys_task(pid, ERTS_PSTT_COHMQ);
}
+#ifdef ERTS_DIRTY_SCHEDULERS
+
+static void
+flush_dirty_trace_messages(void *vpid)
+{
+ Process *proc;
+ Eterm pid;
+#ifdef ARCH_64
+ pid = (Eterm) vpid;
+#else
+ pid = *((Eterm *) vpid);
+ erts_free(ERTS_ALC_T_DIRTY_SL, vpid);
+#endif
+
+ proc = erts_proc_lookup(pid);
+ if (proc)
+ (void) erts_flush_trace_messages(proc, 0);
+}
+
+#endif /* ERTS_DIRTY_SCHEDULERS */
+
void
-erts_schedule_flush_trace_messages(Eterm pid)
+erts_schedule_flush_trace_messages(Process *proc, int force_on_proc)
{
+#ifdef ERTS_SMP
+ ErtsThrPrgrDelayHandle dhndl;
+#endif
+ Eterm pid = proc->common.id;
+
+#ifdef ERTS_DIRTY_SCHEDULERS
+ erts_aint32_t state;
+
+ if (!force_on_proc) {
+ state = erts_smp_atomic32_read_nob(&proc->state);
+ if (state & (ERTS_PSFLG_DIRTY_RUNNING
+ | ERTS_PSFLG_DIRTY_RUNNING_SYS)) {
+ goto sched_flush_dirty;
+ }
+ }
+#endif
+
+#ifdef ERTS_SMP
+ dhndl = erts_thr_progress_unmanaged_delay();
+#endif
+
erts_schedule_generic_sys_task(pid, ERTS_PSTT_FTMQ);
+
+#ifdef ERTS_SMP
+ erts_thr_progress_unmanaged_continue(dhndl);
+#endif
+
+#ifdef ERTS_DIRTY_SCHEDULERS
+ if (!force_on_proc) {
+ state = erts_smp_atomic32_read_mb(&proc->state);
+ if (state & (ERTS_PSFLG_DIRTY_RUNNING
+ | ERTS_PSFLG_DIRTY_RUNNING_SYS)) {
+ void *vargp;
+
+ sched_flush_dirty:
+ /*
+ * We traced 'proc' from another thread than
+ * it is executing on, and it is executing
+ * on a dirty scheduler. It might take a
+ * significant amount of time before it is
+ * scheduled out (where it gets opportunity
+ * to flush messages). We therefore schedule
+ * the flush on the first ordinary scheduler.
+ */
+
+#ifdef ARCH_64
+ vargp = (void *) pid;
+#else
+ {
+ Eterm *argp = erts_alloc(ERTS_ALC_T_DIRTY_SL, sizeof(Eterm));
+ *argp = pid;
+ vargp = (void *) argp;
+ }
+#endif
+ erts_schedule_misc_aux_work(1, flush_dirty_trace_messages, vargp);
+ }
+ }
+#endif
}
static void
@@ -12276,7 +12556,6 @@ static void doit_exit_monitor(ErtsMonitor *mon, void *vpcontext)
erts_port_demonitor(pcontext->p,
ERTS_PORT_DEMONITOR_ORIGIN_ON_DEATHBED,
prt, mon->ref, NULL);
- return; /* let erts_port_demonitor do the deletion */
} else { /* remote by pid */
ASSERT(is_external_pid(mon->pid));
dep = external_pid_dist_entry(mon->pid);
diff --git a/erts/emulator/beam/erl_process.h b/erts/emulator/beam/erl_process.h
index 714e63e986..3347a7a60e 100644
--- a/erts/emulator/beam/erl_process.h
+++ b/erts/emulator/beam/erl_process.h
@@ -1328,10 +1328,13 @@ ERTS_GLB_INLINE void erts_heap_frag_shrink(Process* p, Eterm* hp);
ERTS_GLB_INLINE void erts_heap_frag_shrink(Process* p, Eterm* hp)
{
ErlHeapFragment* hf = MBUF(p);
+ Uint sz;
ASSERT(hf!=NULL && (hp - hf->mem < hf->alloc_size));
- hf->used_size = hp - hf->mem;
+ sz = hp - hf->mem;
+ p->mbuf_sz -= hf->used_size - sz;
+ hf->used_size = sz;
}
#endif /* inline */
@@ -1549,9 +1552,9 @@ extern int erts_system_profile_ts_type;
#define ERTS_SCHEDULER_IS_DIRTY(ESDP) \
((ESDP)->dirty_no.s.num != 0)
#define ERTS_SCHEDULER_IS_DIRTY_CPU(ESDP) \
- ((ESDP)->dirty_no.s.type == 0)
+ (ERTS_SCHEDULER_IS_DIRTY((ESDP)) & ((ESDP)->dirty_no.s.type == 0))
#define ERTS_SCHEDULER_IS_DIRTY_IO(ESDP) \
- ((ESDP)->dirty_no.s.type == 1)
+ (ERTS_SCHEDULER_IS_DIRTY((ESDP)) & ((ESDP)->dirty_no.s.type == 1))
#else
#define ERTS_SCHEDULER_IS_DIRTY(ESDP) 0
#define ERTS_SCHEDULER_IS_DIRTY_CPU(ESDP) 0
@@ -1767,7 +1770,7 @@ void erts_schedule_thr_prgr_later_cleanup_op(void (*)(void *),
ErtsThrPrgrLaterOp *,
UWord);
void erts_schedule_complete_off_heap_message_queue_change(Eterm pid);
-void erts_schedule_flush_trace_messages(Eterm pid);
+void erts_schedule_flush_trace_messages(Process *proc, int force_on_proc);
int erts_flush_trace_messages(Process *c_p, ErtsProcLocks locks);
#if defined(ERTS_SMP) && defined(ERTS_ENABLE_LOCK_CHECK)
@@ -2473,6 +2476,35 @@ erts_get_atom_cache_map(Process *c_p)
}
#endif
+#ifdef __WIN32__
+/*
+ * Don't want erts_time2reds() inlined in beam_emu.c on windows since
+ * it is compiled with gcc which fails on it. Implementation is in
+ * erl_process.c on windows.
+ */
+# define ERTS_TIME2REDS_IMPL__ erts_time2reds__
+#else
+# define ERTS_TIME2REDS_IMPL__ erts_time2reds
+#endif
+
+ERTS_GLB_INLINE Sint64 ERTS_TIME2REDS_IMPL__(ErtsMonotonicTime start,
+ ErtsMonotonicTime end);
+
+#if ERTS_GLB_INLINE_INCL_FUNC_DEF
+ERTS_GLB_INLINE Sint64
+ERTS_TIME2REDS_IMPL__(ErtsMonotonicTime start, ErtsMonotonicTime end)
+{
+ ErtsMonotonicTime time = end - start;
+ ASSERT(time >= 0);
+ time = ERTS_MONOTONIC_TO_USEC(time);
+ if (time == 0)
+ return (Sint64) 1; /* At least one reduction */
+ /* Currently two reductions per micro second */
+ time *= (CONTEXT_REDS-1)/1000 + 1;
+ return (Sint64) time;
+}
+#endif
+
Process *erts_pid2proc_suspend(Process *,
ErtsProcLocks,
Eterm,
diff --git a/erts/emulator/beam/erl_thr_progress.c b/erts/emulator/beam/erl_thr_progress.c
index 21938e7684..700ed90def 100644
--- a/erts/emulator/beam/erl_thr_progress.c
+++ b/erts/emulator/beam/erl_thr_progress.c
@@ -700,6 +700,7 @@ leader_update(ErtsThrPrgrData *tpd)
tpd->leader_state.chk_next_ix = no_managed;
erts_atomic32_set_nob(&intrnl->misc.data.umrefc_ix.current,
(erts_aint32_t) new_umrefc_ix);
+ tpd->leader_state.umrefc_ix.current = new_umrefc_ix;
ETHR_MEMBAR(ETHR_StoreLoad);
refc = erts_atomic_read_nob(&intrnl->umrefc[umrefc_ix].refc);
ASSERT(refc >= 0);
@@ -969,8 +970,10 @@ erts_thr_progress_unmanaged_continue__(ErtsThrPrgrDelayHandle handle)
#ifdef ERTS_ENABLE_LOCK_CHECK
ErtsThrPrgrData *tpd = perhaps_thr_prgr_data(NULL);
ERTS_LC_ASSERT(tpd && tpd->is_delaying);
- tpd->is_delaying = 0;
- return_tmp_thr_prgr_data(tpd);
+ tpd->is_delaying--;
+ ASSERT(tpd->is_delaying >= 0);
+ if (!tpd->is_delaying)
+ return_tmp_thr_prgr_data(tpd);
#endif
ASSERT(!erts_thr_progress_is_managed_thread());
@@ -995,7 +998,7 @@ erts_thr_progress_unmanaged_delay__(void)
#ifdef ERTS_ENABLE_LOCK_CHECK
{
ErtsThrPrgrData *tpd = tmp_thr_prgr_data(NULL);
- tpd->is_delaying = 1;
+ tpd->is_delaying++;
}
#endif
return (ErtsThrPrgrDelayHandle) umrefc_ix;
diff --git a/erts/emulator/beam/erl_time_sup.c b/erts/emulator/beam/erl_time_sup.c
index 9e37106b88..6aa2a7500f 100644
--- a/erts/emulator/beam/erl_time_sup.c
+++ b/erts/emulator/beam/erl_time_sup.c
@@ -2133,22 +2133,26 @@ time_unit_conversion(Process *c_p, Eterm term, ErtsMonotonicTime val, ErtsMonoto
/* Convert to common user specified time units */
switch (term) {
+ case am_second:
case am_seconds:
case make_small(1):
result = ERTS_MONOTONIC_TO_SEC(val) + muloff*ERTS_MONOTONIC_OFFSET_SEC;
ERTS_BIF_PREP_RET(ret, make_time_val(c_p, result));
break;
+ case am_millisecond:
case am_milli_seconds:
case make_small(1000):
result = ERTS_MONOTONIC_TO_MSEC(val) + muloff*ERTS_MONOTONIC_OFFSET_MSEC;
ERTS_BIF_PREP_RET(ret, make_time_val(c_p, result));
break;
+ case am_microsecond:
case am_micro_seconds:
case make_small(1000*1000):
result = ERTS_MONOTONIC_TO_USEC(val) + muloff*ERTS_MONOTONIC_OFFSET_USEC;
ERTS_BIF_PREP_RET(ret, make_time_val(c_p, result));
break;
#ifdef ARCH_64
+ case am_nanosecond:
case am_nano_seconds:
case make_small(1000*1000*1000):
result = ERTS_MONOTONIC_TO_NSEC(val) + muloff*ERTS_MONOTONIC_OFFSET_NSEC;
@@ -2159,7 +2163,7 @@ time_unit_conversion(Process *c_p, Eterm term, ErtsMonotonicTime val, ErtsMonoto
Eterm value, native_res;
#ifndef ARCH_64
Sint user_res;
- if (term == am_nano_seconds)
+ if (term == am_nanosecond || term == am_nano_seconds)
goto to_nano_seconds;
if (term_to_Sint(term, &user_res)) {
if (user_res == 1000*1000*1000) {
diff --git a/erts/emulator/beam/erl_trace.c b/erts/emulator/beam/erl_trace.c
index d14d237ca6..8c84303997 100644
--- a/erts/emulator/beam/erl_trace.c
+++ b/erts/emulator/beam/erl_trace.c
@@ -813,6 +813,9 @@ trace_send(Process *p, Eterm to, Eterm msg)
ErtsTracerNif *tnif = NULL;
ErtsTracingEvent* te;
Eterm pam_result;
+#ifdef ERTS_SMP
+ ErtsThrPrgrDelayHandle dhndl;
+#endif
ASSERT(ARE_TRACE_FLAGS_ON(p, F_TRACE_SEND));
@@ -837,6 +840,10 @@ trace_send(Process *p, Eterm to, Eterm msg)
} else
pam_result = am_true;
+#ifdef ERTS_SMP
+ dhndl = erts_thr_progress_unmanaged_delay();
+#endif
+
if (is_internal_pid(to)) {
if (!erts_proc_lookup(to))
goto send_to_non_existing_process;
@@ -852,6 +859,11 @@ trace_send(Process *p, Eterm to, Eterm msg)
send_to_tracer_nif(p, &p->common, p->common.id, tnif, TRACE_FUN_T_SEND,
operation, msg, to, pam_result);
}
+
+#ifdef ERTS_SMP
+ erts_thr_progress_unmanaged_continue(dhndl);
+#endif
+
erts_match_set_release_result_trace(p, pam_result);
}
@@ -1167,6 +1179,8 @@ erts_call_trace(Process* p, BeamInstr mfa[3], Binary *match_spec,
Eterm transformed_args[MAX_ARG];
ErtsTracer pre_ms_tracer = erts_tracer_nil;
+ ERTS_SMP_LC_ASSERT(erts_proc_lc_my_proc_locks(p) & ERTS_PROC_LOCK_MAIN);
+
ASSERT(tracer);
if (ERTS_TRACER_COMPARE(*tracer, erts_tracer_true)) {
/* Breakpoint trace enabled without specifying tracer =>
@@ -3108,6 +3122,7 @@ erts_tracer_update(ErtsTracer *tracer, const ErtsTracer new_tracer)
if (is_not_nil(*tracer)) {
Uint offs = 2;
UWord size = 2 * sizeof(Eterm) + sizeof(ErtsThrPrgrLaterOp);
+ ErtsThrPrgrLaterOp *lop;
ASSERT(is_list(*tracer));
if (is_not_immed(ERTS_TRACER_STATE(*tracer))) {
hf = (void*)(((char*)(ptr_val(*tracer)) - offsetof(ErlHeapFragment, mem)));
@@ -3115,6 +3130,16 @@ erts_tracer_update(ErtsTracer *tracer, const ErtsTracer new_tracer)
size = hf->alloc_size * sizeof(Eterm) + sizeof(ErlHeapFragment);
ASSERT(offs == size_object(*tracer));
}
+
+ /* sparc assumes that all structs are double word aligned, so we
+ have to align the ErtsThrPrgrLaterOp struct otherwise it may
+ segfault.*/
+ if ((UWord)(ptr_val(*tracer) + offs) % (sizeof(UWord)*2) == sizeof(UWord))
+ offs += 1;
+
+ lop = (ErtsThrPrgrLaterOp*)(ptr_val(*tracer) + offs);
+ ASSERT((UWord)lop % (sizeof(UWord)*2) == 0);
+
/* We schedule the free:ing of the tracer until after a thread progress
has been made so that we know that no schedulers have any references
to it. Because we do this, it is possible to release all locks of a
@@ -3122,9 +3147,7 @@ erts_tracer_update(ErtsTracer *tracer, const ErtsTracer new_tracer)
without having to worry if it is free'd.
*/
erts_schedule_thr_prgr_later_cleanup_op(
- free_tracer, (void*)(*tracer),
- (ErtsThrPrgrLaterOp*)(ptr_val(*tracer) + offs),
- size);
+ free_tracer, (void*)(*tracer), lop, size);
}
if (is_nil(new_tracer)) {
@@ -3134,16 +3157,17 @@ erts_tracer_update(ErtsTracer *tracer, const ErtsTracer new_tracer)
Not sure if it is worth it, we save 4 words (sizeof(ErlHeapFragment))
per tracer. */
Eterm *hp = erts_alloc(ERTS_ALC_T_HEAP_FRAG,
- 2*sizeof(Eterm) + sizeof(ErtsThrPrgrLaterOp));
+ 3*sizeof(Eterm) + sizeof(ErtsThrPrgrLaterOp));
*tracer = CONS(hp, ERTS_TRACER_MODULE(new_tracer),
ERTS_TRACER_STATE(new_tracer));
} else {
Eterm *hp, tracer_state = ERTS_TRACER_STATE(new_tracer),
tracer_module = ERTS_TRACER_MODULE(new_tracer);
Uint sz = size_object(tracer_state);
- hf = new_message_buffer(sz + 2 /* cons cell */ + (sizeof(ErtsThrPrgrLaterOp)+sizeof(Eterm)-1)/sizeof(Eterm));
+ hf = new_message_buffer(sz + 2 /* cons cell */ +
+ (sizeof(ErtsThrPrgrLaterOp)+sizeof(Eterm)-1)/sizeof(Eterm) + 1);
hp = hf->mem + 2;
- hf->used_size -= (sizeof(ErtsThrPrgrLaterOp)+sizeof(Eterm)-1)/sizeof(Eterm);
+ hf->used_size -= (sizeof(ErtsThrPrgrLaterOp)+sizeof(Eterm)-1)/sizeof(Eterm) + 1;
*tracer = copy_struct(tracer_state, sz, &hp, &hf->off_heap);
*tracer = CONS(hf->mem, tracer_module, *tracer);
ASSERT((void*)(((char*)(ptr_val(*tracer)) - offsetof(ErlHeapFragment, mem))) == hf);
diff --git a/erts/emulator/beam/global.h b/erts/emulator/beam/global.h
index 8933bcad16..b2c76aa605 100644
--- a/erts/emulator/beam/global.h
+++ b/erts/emulator/beam/global.h
@@ -61,9 +61,12 @@ struct enif_environment_t /* ErlNifEnv */
extern void erts_pre_nif(struct enif_environment_t*, Process*,
struct erl_module_nif*, Process* tracee);
extern void erts_post_nif(struct enif_environment_t* env);
+#ifdef ERTS_DIRTY_SCHEDULERS
extern void erts_pre_dirty_nif(ErtsSchedulerData *,
struct enif_environment_t*, Process*,
- struct erl_module_nif*, Process* tracee);
+ struct erl_module_nif*);
+extern void erts_post_dirty_nif(struct enif_environment_t* env);
+#endif
extern Eterm erts_nif_taints(Process* p);
extern void erts_print_nif_taints(int to, void* to_arg);
void erts_unload_nif(struct erl_module_nif* nif);
@@ -998,17 +1001,33 @@ Eterm erl_is_function(Process* p, Eterm arg1, Eterm arg2);
/* beam_bif_load.c */
#define ERTS_CPC_ALLOW_GC (1 << 0)
-#define ERTS_CPC_COPY_LITERALS (1 << 1)
-#define ERTS_CPC_ALL (ERTS_CPC_ALLOW_GC | ERTS_CPC_COPY_LITERALS)
+#define ERTS_CPC_ALL ERTS_CPC_ALLOW_GC
Eterm erts_check_process_code(Process *c_p, Eterm module, Uint flags, int *redsp, int fcalls);
+#ifdef ERTS_NEW_PURGE_STRATEGY
+Eterm erts_proc_copy_literal_area(Process *c_p, int *redsp, int fcalls, int gc_allowed);
+#endif
-typedef struct {
- Eterm *ptr;
- Uint sz;
- Eterm pid;
-} copy_literals_t;
+typedef struct ErtsLiteralArea_ {
+ struct erl_off_heap_header *off_heap;
+ Eterm *end;
+ Eterm start[1]; /* beginning of area */
+} ErtsLiteralArea;
+
+#define ERTS_LITERAL_AREA_ALLOC_SIZE(N) \
+ (sizeof(ErtsLiteralArea) + sizeof(Eterm)*((N) - 1))
+
+extern erts_smp_atomic_t erts_copy_literal_area__;
+#define ERTS_COPY_LITERAL_AREA() \
+ ((ErtsLiteralArea *) erts_smp_atomic_read_nob(&erts_copy_literal_area__))
-extern copy_literals_t erts_clrange;
+#ifdef ERTS_NEW_PURGE_STRATEGY
+extern Process *erts_literal_area_collector;
+#endif
+#ifdef ERTS_DIRTY_SCHEDULERS
+extern Process *erts_dirty_process_code_checker;
+#endif
+
+extern Process *erts_code_purger;
/* beam_load.c */
typedef struct {
@@ -1091,12 +1110,19 @@ typedef struct {
#define INITIALIZE_SHCOPY(info) \
do { \
+ ErtsLiteralArea *larea__ = ERTS_COPY_LITERAL_AREA();\
info.queue_start = info.queue_default; \
info.bitstore_start = info.bitstore_default; \
info.shtable_start = info.shtable_default; \
info.literal_size = 0; \
- info.range_ptr = erts_clrange.ptr; \
- info.range_sz = erts_clrange.sz; \
+ if (larea__) { \
+ info.range_ptr = &larea__->start[0]; \
+ info.range_sz = larea__->end - info.range_ptr; \
+ } \
+ else { \
+ info.range_ptr = NULL; \
+ info.range_sz = 0; \
+ } \
} while(0)
#define DESTROY_SHCOPY(info) \
diff --git a/erts/emulator/beam/hash.c b/erts/emulator/beam/hash.c
index e255b961f1..cd038d100b 100644
--- a/erts/emulator/beam/hash.c
+++ b/erts/emulator/beam/hash.c
@@ -35,9 +35,9 @@
static const int h_size_table[] = {
2, 5, 11, 23, 47, 97, 197, 397, 797, /* double upto here */
1201, 1597,
- 2411, 3203,
+ 2411, 3203,
4813, 6421,
- 9643, 12853,
+ 9643, 12853,
19289, 25717,
51437,
102877,
@@ -49,8 +49,8 @@ static const int h_size_table[] = {
6584983,
13169977,
26339969,
- 52679969,
- -1
+ 52679969,
+ -1
};
/*
@@ -69,7 +69,7 @@ void hash_get_info(HashInfo *hi, Hash *h)
for (i = 0; i < size; i++) {
int depth = 0;
HashBucket* b = h->bucket[i];
-
+
while (b != (HashBucket*) 0) {
objects++;
depth++;
@@ -112,7 +112,7 @@ void hash_info(int to, void *arg, Hash* h)
/*
* Returns size of table in bytes. Stored objects not included.
*/
-int
+int
hash_table_sz(Hash *h)
{
int i;
@@ -190,7 +190,7 @@ void hash_delete(Hash* h)
HashBucket* b = h->bucket[i];
while (b != (HashBucket*) 0) {
HashBucket* b_next = b->next;
-
+
h->fun.free((void*) b);
b = b_next;
}
@@ -250,7 +250,7 @@ void* hash_get(Hash* h, void* tmpl)
HashValue hval = h->fun.hash(tmpl);
int ix = hval % h->size;
HashBucket* b = h->bucket[ix];
-
+
while(b != (HashBucket*) 0) {
if ((b->hvalue == hval) && (h->fun.cmp(tmpl, (void*)b) == 0))
return (void*) b;
@@ -294,7 +294,7 @@ void* hash_erase(Hash* h, void* tmpl)
int ix = hval % h->size;
HashBucket* b = h->bucket[ix];
HashBucket* prev = 0;
-
+
while(b != 0) {
if ((b->hvalue == hval) && (h->fun.cmp(tmpl, (void*)b) == 0)) {
if (prev != 0)
@@ -326,7 +326,7 @@ hash_remove(Hash *h, void *tmpl)
int ix = hval % h->size;
HashBucket *b = h->bucket[ix];
HashBucket *prev = NULL;
-
+
while (b) {
if ((b->hvalue == hval) && (h->fun.cmp(tmpl, (void*)b) == 0)) {
if (prev)
@@ -355,4 +355,3 @@ void hash_foreach(Hash* h, void (*func)(void *, void *), void *func_arg2)
}
}
}
-
diff --git a/erts/emulator/beam/io.c b/erts/emulator/beam/io.c
index cb8792dffa..77dbe92241 100644
--- a/erts/emulator/beam/io.c
+++ b/erts/emulator/beam/io.c
@@ -6591,16 +6591,20 @@ deliver_term_check_port(ErlDrvTermData port_id, Eterm *connected_p,
ErtsThrPrgrDelayHandle dhndl = erts_thr_progress_unmanaged_delay();
#endif
erts_aint32_t state;
+ int res = 1;
Port *prt = erts_port_lookup_raw((Eterm) port_id);
- if (!prt)
- return -1;
+ if (!prt) {
+ res = -1;
+ goto done;
+ }
state = erts_atomic32_read_nob(&prt->state);
if (state & (ERTS_PORT_SFLGS_INVALID_DRIVER_LOOKUP
| ERTS_PORT_SFLG_CLOSING)) {
if (state & ERTS_PORT_SFLGS_INVALID_DRIVER_LOOKUP)
- return -1;
+ res = -1;
else
- return 0;
+ res = 0;
+ goto done;
}
if (connected_p) {
#ifdef ERTS_SMP
@@ -6609,25 +6613,27 @@ deliver_term_check_port(ErlDrvTermData port_id, Eterm *connected_p,
#endif
*connected_p = ERTS_PORT_GET_CONNECTED(prt);
}
+
+done:
+
#ifdef ERTS_SMP
if (dhndl != ERTS_THR_PRGR_DHANDLE_MANAGED) {
+ ERTS_SMP_LC_ASSERT(!prt || !erts_lc_is_port_locked(prt));
erts_thr_progress_unmanaged_continue(dhndl);
ETHR_MEMBAR(ETHR_LoadLoad|ETHR_LoadStore);
} else
#endif
- {
+ if (res == 1) {
+ ERTS_SMP_LC_ASSERT(erts_lc_is_port_locked(prt));
*trace_prt = prt;
}
- ERTS_SMP_LC_ASSERT(dhndl == ERTS_THR_PRGR_DHANDLE_MANAGED
- ? erts_lc_is_port_locked(prt)
- : !erts_lc_is_port_locked(prt));
- return 1;
+ return res;
}
int erl_drv_output_term(ErlDrvTermData port_id, ErlDrvTermData* data, int len)
{
/* May be called from arbitrary thread */
- Eterm connected;
+ Eterm connected = NIL; /* Shut up faulty warning... */
Port *prt = NULL;
int res = deliver_term_check_port(port_id, &connected, &prt);
if (res <= 0)
diff --git a/erts/emulator/beam/module.c b/erts/emulator/beam/module.c
index 3eb11f1693..4f36377450 100644
--- a/erts/emulator/beam/module.c
+++ b/erts/emulator/beam/module.c
@@ -85,6 +85,7 @@ static Module* module_alloc(Module* tmpl)
obj->old.num_breakpoints = 0;
obj->curr.num_traced_exports = 0;
obj->old.num_traced_exports = 0;
+ obj->on_load = 0;
return obj;
}
@@ -201,6 +202,7 @@ void module_start_staging(void)
dst_mod->curr = src_mod->curr;
dst_mod->old = src_mod->old;
+ dst_mod->on_load = src_mod->on_load;
}
/*
@@ -214,6 +216,7 @@ void module_start_staging(void)
dst_mod->curr = src_mod->curr;
dst_mod->old = src_mod->old;
+ dst_mod->on_load = src_mod->on_load;
}
newsz = index_table_sz(dst);
erts_smp_atomic_add_nob(&tot_module_bytes, (newsz - oldsz));
diff --git a/erts/emulator/beam/module.h b/erts/emulator/beam/module.h
index 5a60bc90d9..1c1afc8461 100644
--- a/erts/emulator/beam/module.h
+++ b/erts/emulator/beam/module.h
@@ -39,6 +39,7 @@ typedef struct erl_module {
struct erl_module_instance curr;
struct erl_module_instance old; /* protected by "old_code" rwlock */
+ struct erl_module_instance* on_load;
} Module;
Module* erts_get_module(Eterm mod, ErtsCodeIndex code_ix);
diff --git a/erts/emulator/beam/utils.c b/erts/emulator/beam/utils.c
index 675fafa726..6786657faf 100644
--- a/erts/emulator/beam/utils.c
+++ b/erts/emulator/beam/utils.c
@@ -85,7 +85,7 @@ erts_heap_alloc(Process* p, Uint need, Uint xtra)
&& HEAP_TOP(p) >= p->space_verified_from
&& HEAP_TOP(p) + need <= p->space_verified_from + p->space_verified
&& HEAP_LIMIT(p) - HEAP_TOP(p) >= need) {
-
+
Uint consumed = need + (HEAP_TOP(p) - p->space_verified_from);
ASSERT(consumed <= p->space_verified);
p->space_verified -= consumed;
@@ -102,6 +102,7 @@ erts_heap_alloc(Process* p, Uint need, Uint xtra)
if (bp != NULL && need <= (bp->alloc_size - bp->used_size)) {
Eterm* ret = bp->mem + bp->used_size;
bp->used_size += need;
+ p->mbuf_sz += need;
return ret;
}
#ifdef DEBUG
@@ -124,7 +125,7 @@ erts_heap_alloc(Process* p, Uint need, Uint xtra)
MBUF(p) = bp;
bp->alloc_size = n;
bp->used_size = need;
- MBUF_SIZE(p) += n;
+ MBUF_SIZE(p) += need;
bp->off_heap.first = NULL;
bp->off_heap.overhead = 0;
return bp->mem;
@@ -638,7 +639,7 @@ erts_bld_atom_uword_2tup_list(Uint **hpp, Uint *szp,
ui = uint_to_big(uints[i], *hpp);
*hpp += BIG_UINT_HEAP_SIZE;
}
-
+
res = CONS(*hpp+3, TUPLE2(*hpp, atoms[i], ui), res);
*hpp += 5;
}
@@ -676,14 +677,14 @@ erts_bld_atom_2uint_3tup_list(Uint **hpp, Uint *szp, Sint length,
ui1 = uint_to_big(uints1[i], *hpp);
*hpp += BIG_UINT_HEAP_SIZE;
}
-
+
if (IS_USMALL(0, uints2[i]))
ui2 = make_small(uints2[i]);
else {
ui2 = uint_to_big(uints2[i], *hpp);
*hpp += BIG_UINT_HEAP_SIZE;
}
-
+
res = CONS(*hpp+4, TUPLE3(*hpp, atoms[i], ui1, ui2), res);
*hpp += 6;
}
@@ -794,7 +795,7 @@ hash_binary_bytes(Eterm bin, Uint sz, Uint32 hash)
Uint b;
Uint lshift = bitoffs;
Uint rshift = 8 - lshift;
-
+
while (sz--) {
b = (previous << lshift) & 0xFF;
previous = *ptr++;
@@ -805,7 +806,7 @@ hash_binary_bytes(Eterm bin, Uint sz, Uint32 hash)
b = (previous << lshift) & 0xFF;
previous = *ptr++;
b |= previous >> rshift;
-
+
b >>= 8 - bitsize;
hash = (hash*FUNNY_NUMBER1 + b) * FUNNY_NUMBER12 + bitsize;
}
@@ -835,21 +836,21 @@ Uint32 make_hash(Eterm term_arg)
do { \
Uint32 x = (Uint32) (Expr); \
hash = \
- (((((hash)*(Prime1) + (x & 0xFF)) * (Prime1) + \
- ((x >> 8) & 0xFF)) * (Prime1) + \
- ((x >> 16) & 0xFF)) * (Prime1) + \
+ (((((hash)*(Prime1) + (x & 0xFF)) * (Prime1) + \
+ ((x >> 8) & 0xFF)) * (Prime1) + \
+ ((x >> 16) & 0xFF)) * (Prime1) + \
(x >> 24)); \
} while(0)
-#define UINT32_HASH_RET(Expr, Prime1, Prime2) \
+#define UINT32_HASH_RET(Expr, Prime1, Prime2) \
UINT32_HASH_STEP(Expr, Prime1); \
hash = hash * (Prime2); \
- break
-
-
+ break
+
+
/*
* Significant additions needed for real 64 bit port with larger fixnums.
- */
+ */
/*
* Note, for the simple 64bit port, not utilizing the
@@ -864,7 +865,7 @@ tail_recur:
hash = hash*FUNNY_NUMBER3 + 1;
break;
case ATOM_DEF:
- hash = hash*FUNNY_NUMBER1 +
+ hash = hash*FUNNY_NUMBER1 +
(atom_tab(atom_val(term))->slot.bucket.hvalue);
break;
case SMALL_DEF:
@@ -893,9 +894,9 @@ tail_recur:
Export* ep = *((Export **) (export_val(term) + 1));
hash = hash * FUNNY_NUMBER11 + ep->code[2];
- hash = hash*FUNNY_NUMBER1 +
+ hash = hash*FUNNY_NUMBER1 +
(atom_tab(atom_val(ep->code[0]))->slot.bucket.hvalue);
- hash = hash*FUNNY_NUMBER1 +
+ hash = hash*FUNNY_NUMBER1 +
(atom_tab(atom_val(ep->code[1]))->slot.bucket.hvalue);
break;
}
@@ -906,7 +907,7 @@ tail_recur:
Uint num_free = funp->num_free;
hash = hash * FUNNY_NUMBER10 + num_free;
- hash = hash*FUNNY_NUMBER1 +
+ hash = hash*FUNNY_NUMBER1 +
(atom_tab(atom_val(funp->fe->module))->slot.bucket.hvalue);
hash = hash*FUNNY_NUMBER2 + funp->fe->old_index;
hash = hash*FUNNY_NUMBER2 + funp->fe->old_uniq;
@@ -931,7 +932,7 @@ tail_recur:
UINT32_HASH_RET(internal_ref_numbers(term)[0],FUNNY_NUMBER9,FUNNY_NUMBER10);
case EXTERNAL_REF_DEF:
UINT32_HASH_RET(external_ref_numbers(term)[0],FUNNY_NUMBER9,FUNNY_NUMBER10);
- case FLOAT_DEF:
+ case FLOAT_DEF:
{
FloatDef ff;
GET_DOUBLE(term, ff);
@@ -958,12 +959,12 @@ tail_recur:
** as multiplications on a Sparc is so slow.
*/
hash = hash*FUNNY_NUMBER2 + unsigned_val(*list);
-
+
if (is_not_list(CDR(list))) {
WSTACK_PUSH(stack, MAKE_HASH_CDR_POST_OP);
term = CDR(list);
goto tail_recur;
- }
+ }
list = list_val(CDR(list));
}
WSTACK_PUSH2(stack, CDR(list), MAKE_HASH_CDR_PRE_OP);
@@ -1004,17 +1005,17 @@ tail_recur:
}
hash *= is_neg ? FUNNY_NUMBER4 : FUNNY_NUMBER3;
break;
- }
+ }
case MAP_DEF:
hash = hash*FUNNY_NUMBER13 + FUNNY_NUMBER14 + make_hash2(term);
break;
- case TUPLE_DEF:
+ case TUPLE_DEF:
{
Eterm* ptr = tuple_val(term);
Uint arity = arityval(*ptr);
WSTACK_PUSH3(stack, (UWord) arity, (UWord)(ptr+1), (UWord) arity);
- op = MAKE_HASH_TUPLE_OP;
+ op = MAKE_HASH_TUPLE_OP;
}/*fall through*/
case MAKE_HASH_TUPLE_OP:
case MAKE_HASH_TERM_ARRAY_OP:
@@ -1031,8 +1032,8 @@ tail_recur:
hash = hash*FUNNY_NUMBER9 + arity;
}
break;
- }
-
+ }
+
default:
erts_exit(ERTS_ERROR_EXIT, "Invalid tag in make_hash(0x%X,0x%X)\n", term, op);
return 0;
@@ -1159,8 +1160,8 @@ make_hash2(Eterm term)
if (y < 0) { \
UINT32_HASH(-y, AConst); \
/* Negative numbers are unnecessarily mixed twice. */ \
- } \
- UINT32_HASH(y, AConst); \
+ } \
+ UINT32_HASH(y, AConst); \
} while(0)
#define IS_SSMALL28(x) (((Uint) (((x) >> (28-1)) + 1)) < 2)
@@ -1242,7 +1243,7 @@ make_hash2(Eterm term)
int arity = header_arity(hdr);
Eterm* elem = tuple_val(term);
UINT32_HASH(arity, HCONST_9);
- if (arity == 0) /* Empty tuple */
+ if (arity == 0) /* Empty tuple */
goto hash2_common;
for (i = arity; ; i--) {
term = elem[i];
@@ -1329,7 +1330,7 @@ make_hash2(Eterm term)
{
Export* ep = *((Export **) (export_val(term) + 1));
UINT32_HASH_2
- (ep->code[2],
+ (ep->code[2],
atom_tab(atom_val(ep->code[0]))->slot.bucket.hvalue,
HCONST);
UINT32_HASH
@@ -1343,7 +1344,7 @@ make_hash2(Eterm term)
ErlFunThing* funp = (ErlFunThing *) fun_val(term);
Uint num_free = funp->num_free;
UINT32_HASH_2
- (num_free,
+ (num_free,
atom_tab(atom_val(funp->fe->module))->slot.bucket.hvalue,
HCONST);
UINT32_HASH_2
@@ -1468,7 +1469,7 @@ make_hash2(Eterm term)
goto hash2_common;
}
break;
-
+
default:
erts_exit(ERTS_ERROR_EXIT, "Invalid tag in make_hash2(0x%X)\n", term);
}
@@ -1541,7 +1542,7 @@ make_hash2(Eterm term)
}
case HASH_MAP_PAIR:
hash_xor_pairs ^= hash;
- hash = 0;
+ hash = 0;
goto hash2_common;
default:
break;
@@ -1678,17 +1679,22 @@ make_internal_hash(Eterm term)
* the order in which keys and values are encountered.
* We therefore calculate context independent hashes for all .
* key-value pairs and then xor them together.
+ *
+ * We *do* need to use an initial seed for each pair, i.e. the
+ * hash value, so the hash value is reset for each pair with 'hash'.
+ * If we don't, no additional entropy is given to the system and the
+ * hash collision resolution in maps:from_list/1 would fail.
*/
ESTACK_PUSH(s, hash_xor_pairs);
ESTACK_PUSH(s, hash);
ESTACK_PUSH(s, HASH_MAP_TAIL);
- hash = 0;
- hash_xor_pairs = 0;
for (i = size - 1; i >= 0; i--) {
+ ESTACK_PUSH(s, hash); /* initial seed for all pairs */
ESTACK_PUSH(s, HASH_MAP_PAIR);
ESTACK_PUSH(s, vs[i]);
ESTACK_PUSH(s, ks[i]);
}
+ hash_xor_pairs = 0;
goto pop_next;
}
case HAMT_SUBTAG_HEAD_ARRAY:
@@ -1700,7 +1706,6 @@ make_internal_hash(Eterm term)
ESTACK_PUSH(s, hash_xor_pairs);
ESTACK_PUSH(s, hash);
ESTACK_PUSH(s, HASH_MAP_TAIL);
- hash = 0;
hash_xor_pairs = 0;
}
switch (hdr & _HEADER_MAP_SUBTAG_MASK) {
@@ -1717,6 +1722,7 @@ make_internal_hash(Eterm term)
while (i) {
if (is_list(*ptr)) {
Eterm* cons = list_val(*ptr);
+ ESTACK_PUSH(s, hash); /* initial seed for all pairs */
ESTACK_PUSH(s, HASH_MAP_PAIR);
ESTACK_PUSH(s, CDR(cons));
ESTACK_PUSH(s, CAR(cons));
@@ -1906,6 +1912,7 @@ make_internal_hash(Eterm term)
pop_next:
if (ESTACK_ISEMPTY(s)) {
DESTROY_ESTACK(s);
+
return hash;
}
@@ -1920,7 +1927,7 @@ make_internal_hash(Eterm term)
}
case HASH_MAP_PAIR:
hash_xor_pairs ^= hash;
- hash = 0;
+ hash = (Uint32) ESTACK_POP(s); /* initial seed for all pairs */
goto pop_next;
case HASH_CDR:
@@ -1953,8 +1960,8 @@ Uint32 make_broken_hash(Eterm term)
DECLARE_WSTACK(stack);
unsigned op;
tail_recur:
- op = tag_val_def(term);
- for (;;) {
+ op = tag_val_def(term);
+ for (;;) {
switch (op) {
case NIL_DEF:
hash = hash*FUNNY_NUMBER3 + 1;
@@ -1976,8 +1983,7 @@ tail_recur:
{ /* like a bignum */
Uint32 y4 = (Uint32) y2;
hash = hash*FUNNY_NUMBER2 + ((y4 << 16) | (y4 >> 16));
- if (y3)
- {
+ if (y3) {
hash = hash*FUNNY_NUMBER2 + ((y3 << 16) | (y3 >> 16));
arity++;
}
@@ -2020,9 +2026,9 @@ tail_recur:
Export* ep = *((Export **) (export_val(term) + 1));
hash = hash * FUNNY_NUMBER11 + ep->code[2];
- hash = hash*FUNNY_NUMBER1 +
+ hash = hash*FUNNY_NUMBER1 +
(atom_tab(atom_val(ep->code[0]))->slot.bucket.hvalue);
- hash = hash*FUNNY_NUMBER1 +
+ hash = hash*FUNNY_NUMBER1 +
(atom_tab(atom_val(ep->code[1]))->slot.bucket.hvalue);
break;
}
@@ -2033,7 +2039,7 @@ tail_recur:
Uint num_free = funp->num_free;
hash = hash * FUNNY_NUMBER10 + num_free;
- hash = hash*FUNNY_NUMBER1 +
+ hash = hash*FUNNY_NUMBER1 +
(atom_tab(atom_val(funp->fe->module))->slot.bucket.hvalue);
hash = hash*FUNNY_NUMBER2 + funp->fe->old_index;
hash = hash*FUNNY_NUMBER2 + funp->fe->old_uniq;
@@ -2065,7 +2071,7 @@ tail_recur:
case EXTERNAL_REF_DEF:
hash = hash*FUNNY_NUMBER9 + external_ref_numbers(term)[0];
break;
- case FLOAT_DEF:
+ case FLOAT_DEF:
{
FloatDef ff;
GET_DOUBLE(term, ff);
@@ -2149,7 +2155,7 @@ tail_recur:
}
#else
-#error "unsupported D_EXP size"
+#error "unsupported D_EXP size"
#endif
hash = hash * (is_neg ? FUNNY_NUMBER3 : FUNNY_NUMBER2) + arity;
}
@@ -2158,14 +2164,14 @@ tail_recur:
case MAP_DEF:
hash = hash*FUNNY_NUMBER13 + FUNNY_NUMBER14 + make_hash2(term);
break;
- case TUPLE_DEF:
+ case TUPLE_DEF:
{
Eterm* ptr = tuple_val(term);
Uint arity = arityval(*ptr);
WSTACK_PUSH3(stack, (UWord) arity, (UWord) (ptr+1), (UWord) arity);
op = MAKE_HASH_TUPLE_OP;
- }/*fall through*/
+ }/*fall through*/
case MAKE_HASH_TUPLE_OP:
case MAKE_HASH_TERM_ARRAY_OP:
{
@@ -2193,7 +2199,7 @@ tail_recur:
DESTROY_WSTACK(stack);
return hash;
-
+
#undef MAKE_HASH_TUPLE_OP
#undef MAKE_HASH_TERM_ARRAY_OP
#undef MAKE_HASH_CDR_PRE_OP
@@ -2353,13 +2359,13 @@ static int do_send_term_to_logger(Eterm tag, Eterm gleader,
}
static ERTS_INLINE int
-send_info_to_logger(Eterm gleader, char *buf, int len)
+send_info_to_logger(Eterm gleader, char *buf, int len)
{
return do_send_to_logger(am_info_msg, gleader, buf, len);
}
static ERTS_INLINE int
-send_warning_to_logger(Eterm gleader, char *buf, int len)
+send_warning_to_logger(Eterm gleader, char *buf, int len)
{
Eterm tag;
switch (erts_error_logger_warnings) {
@@ -2371,7 +2377,7 @@ send_warning_to_logger(Eterm gleader, char *buf, int len)
}
static ERTS_INLINE int
-send_error_to_logger(Eterm gleader, char *buf, int len)
+send_error_to_logger(Eterm gleader, char *buf, int len)
{
return do_send_to_logger(am_error, gleader, buf, len);
}
@@ -2613,7 +2619,7 @@ tailrecur_ne:
break; /* not equal */
case TAG_PRIMARY_BOXED:
- {
+ {
Eterm hdr = *boxed_val(a);
switch (hdr & _TAG_HEADER_MASK) {
case ARITYVAL_SUBTAG:
@@ -2639,7 +2645,7 @@ tailrecur_ne:
Uint b_bitsize;
Uint a_bitoffs;
Uint b_bitoffs;
-
+
if (!is_binary(b)) {
goto not_equal;
}
@@ -2671,7 +2677,7 @@ tailrecur_ne:
{
ErlFunThing* f1;
ErlFunThing* f2;
-
+
if (!is_fun(b))
goto not_equal;
f1 = (ErlFunThing *) fun_val(a);
@@ -2702,7 +2708,7 @@ tailrecur_ne:
if(ap->header == bp->header && ap->node == bp->node) {
ASSERT(1 == external_data_words(a));
ASSERT(1 == external_data_words(b));
-
+
if (ap->data.ui[0] == bp->data.ui[0]) goto pop_next;
}
break; /* not equal */
@@ -2759,7 +2765,7 @@ tailrecur_ne:
if (alen == 3 && blen == 3) {
/* Most refs are of length 3 */
if (anum[1] == bnum[1] && anum[2] == bnum[2]) {
- goto pop_next;
+ goto pop_next;
} else {
goto not_equal;
}
@@ -2784,7 +2790,7 @@ tailrecur_ne:
for (i = common_len; i < blen; i++)
if (bnum[i] != 0)
goto not_equal;
- }
+ }
}
goto pop_next;
}
@@ -2792,7 +2798,7 @@ tailrecur_ne:
case NEG_BIG_SUBTAG:
{
int i;
-
+
if (!is_big(b))
goto not_equal;
aa = big_val(a);
@@ -2810,7 +2816,7 @@ tailrecur_ne:
{
FloatDef af;
FloatDef bf;
-
+
if (is_float(b)) {
GET_DOUBLE(a, af);
GET_DOUBLE(b, bf);
@@ -2889,7 +2895,7 @@ term_array: /* arrays in 'aa' and 'bb', length in 'sz' */
}
goto tailrecur_ne;
}
-
+
pop_next:
if (!WSTACK_ISEMPTY(stack)) {
UWord something = WSTACK_POP(stack);
@@ -3093,7 +3099,7 @@ tailrecur_ne:
}
anode = erts_this_node;
adata = internal_port_data(a);
-
+
port_common:
CMP_NODES(anode, bnode);
ON_CMP_GOTO((Sint)(adata - bdata));
@@ -3111,7 +3117,7 @@ tailrecur_ne:
}
anode = erts_this_node;
adata = internal_pid_data(a);
-
+
pid_common:
if (adata != bdata) {
RETURN_NEQ(adata < bdata ? -1 : 1);
@@ -3336,7 +3342,7 @@ tailrecur_ne:
diff = f1->num_free - f2->num_free;
if (diff != 0) {
RETURN_NEQ(diff);
- }
+ }
i = f1->num_free;
if (i == 0) goto pop_next;
aa = f1->env;
@@ -3397,10 +3403,10 @@ tailrecur_ne:
anum = internal_thing_ref_numbers(athing);
alen = internal_thing_ref_no_of_numbers(athing);
}
-
+
ref_common:
CMP_NODES(anode, bnode);
-
+
ASSERT(alen > 0 && blen > 0);
if (alen != blen) {
if (alen > blen) {
@@ -3418,7 +3424,7 @@ tailrecur_ne:
} while (alen < blen);
}
}
-
+
ASSERT(alen == blen);
for (i = (Sint) alen - 1; i >= 0; i--)
if (anum[i] != bnum[i])
@@ -3633,8 +3639,8 @@ term_array: /* arrays in 'aa' and 'bb', length in 'i' */
}
a = *aa;
b = *bb;
- goto tailrecur;
-
+ goto tailrecur;
+
pop_next:
if (!WSTACK_ISEMPTY(stack)) {
UWord something = WSTACK_POP(stack);
@@ -3897,19 +3903,19 @@ intlist_to_buf(Eterm list, char *buf, Sint len)
Eterm* listptr;
Sint sz = 0;
- if (is_nil(list))
+ if (is_nil(list))
return 0;
if (is_not_list(list))
return -1;
listptr = list_val(list);
while (sz < len) {
- if (!is_byte(*listptr))
+ if (!is_byte(*listptr))
return -1;
buf[sz++] = unsigned_val(*listptr);
if (is_nil(*(listptr + 1)))
return(sz);
- if (is_not_list(*(listptr + 1)))
+ if (is_not_list(*(listptr + 1)))
return -1;
listptr = list_val(*(listptr + 1));
}
@@ -4157,10 +4163,10 @@ do { \
} else if (yield_support && --yield_count <= 0)
goto L_yield;
}
-
+
res = len;
- L_return:
+ L_return:
DESTROY_ESTACK(s);
@@ -5053,7 +5059,7 @@ Process *p;
if(p)
print_process_info(ERTS_PRINT_STDERR, NULL, p);
}
-
+
void ppi(Eterm pid)
{
pp(erts_proc_lookup(pid));
@@ -5079,5 +5085,3 @@ ps(Process* p, Eterm* stop)
}
}
#endif
-
-
diff --git a/erts/emulator/drivers/common/inet_drv.c b/erts/emulator/drivers/common/inet_drv.c
index 5ce0e1de9e..1885338ce5 100644
--- a/erts/emulator/drivers/common/inet_drv.c
+++ b/erts/emulator/drivers/common/inet_drv.c
@@ -777,6 +777,7 @@ static int is_nonzero(const char *s, size_t n)
#define INET_LOPT_NETNS 38 /* Network namespace pathname */
#define INET_LOPT_TCP_SHOW_ECONNRESET 39 /* tell user about incoming RST */
#define INET_LOPT_LINE_DELIM 40 /* Line delimiting char */
+#define INET_OPT_TCLASS 41 /* IPv6 transport class */
/* SCTP options: a separate range, from 100: */
#define SCTP_OPT_RTOINFO 100
#define SCTP_OPT_ASSOCINFO 101
@@ -1330,6 +1331,7 @@ static ErlDrvTermData am_reuseaddr;
static ErlDrvTermData am_dontroute;
static ErlDrvTermData am_priority;
static ErlDrvTermData am_tos;
+static ErlDrvTermData am_tclass;
static ErlDrvTermData am_ipv6_v6only;
static ErlDrvTermData am_netns;
#endif
@@ -3720,6 +3722,7 @@ static void inet_init_sctp(void) {
INIT_ATOM(dontroute);
INIT_ATOM(priority);
INIT_ATOM(tos);
+ INIT_ATOM(tclass);
INIT_ATOM(ipv6_v6only);
INIT_ATOM(netns);
@@ -6228,6 +6231,15 @@ static int inet_set_opts(inet_descriptor* desc, char* ptr, int len)
#else
continue;
#endif
+#if defined(IPV6_TCLASS) && defined(SOL_IPV6)
+ case INET_OPT_TCLASS:
+ proto = SOL_IPV6;
+ type = IPV6_TCLASS;
+ propagate = 1;
+ DEBUGF(("inet_set_opts(%ld): s=%d, IPV6_TCLASS=%d\r\n",
+ (long)desc->port, desc->s, ival));
+ break;
+#endif
case TCP_OPT_NODELAY:
proto = IPPROTO_TCP;
@@ -6661,6 +6673,19 @@ static int sctp_set_opts(inet_descriptor* desc, char* ptr, int len)
continue; /* Option not supported -- ignore it */
# endif
+# if defined(IPV6_TCLASS) && defined(SOL_IPV6)
+ case INET_OPT_TCLASS:
+ {
+ arg.ival= get_int32 (curr); curr += 4;
+ proto = SOL_IPV6;
+ type = IPV6_TCLASS;
+ arg_ptr = (char*) (&arg.ival);
+ arg_sz = sizeof ( arg.ival);
+ break;
+ }
+# endif
+
+
case INET_OPT_IPV6_V6ONLY:
# if HAVE_DECL_IPV6_V6ONLY
{
@@ -7162,6 +7187,15 @@ static ErlDrvSSizeT inet_fill_opts(inet_descriptor* desc,
put_int32(0, ptr);
continue;
#endif
+ case INET_OPT_TCLASS:
+#if defined(IPV6_TCLASS) && defined(SOL_IPV6)
+ proto = SOL_IPV6;
+ type = IPV6_TCLASS;
+ break;
+#else
+ TRUNCATE_TO(0,ptr);
+ continue;
+#endif
case INET_OPT_REUSEADDR:
type = SO_REUSEADDR;
break;
@@ -7556,6 +7590,7 @@ static ErlDrvSSizeT sctp_fill_opts(inet_descriptor* desc,
case INET_OPT_DONTROUTE:
case INET_OPT_PRIORITY :
case INET_OPT_TOS :
+ case INET_OPT_TCLASS :
case INET_OPT_IPV6_V6ONLY:
case SCTP_OPT_AUTOCLOSE:
case SCTP_OPT_MAXSEG :
@@ -7629,6 +7664,19 @@ static ErlDrvSSizeT sctp_fill_opts(inet_descriptor* desc,
continue;
# endif
}
+ case INET_OPT_TCLASS:
+ {
+# if defined(IPV6_TCLASS) && defined(SOL_IPV6)
+ proto = SOL_IPV6;
+ type = IPV6_TCLASS;
+ is_int = 1;
+ tag = am_tclass;
+ break;
+# else
+ /* Not supported -- ignore */
+ continue;
+# endif
+ }
case INET_OPT_IPV6_V6ONLY:
# if HAVE_DECL_IPV6_V6ONLY
{
@@ -11407,6 +11455,7 @@ static void packet_inet_command(ErlDrvData e, char* buf, ErlDrvSizeT len)
VALGRIND_MAKE_MEM_DEFINED(mhdr.msg_control, mhdr.msg_controllen); /*suppress "uninitialised bytes"*/
mhdr.msg_flags = 0; /* Not used with "sendmsg" */
+ inet_output_count(desc, data_len);
/* Now do the actual sending. NB: "flags" in "sendmsg" itself are NOT
used: */
code = sock_sendmsg(desc->s, &mhdr, 0);
diff --git a/erts/emulator/hipe/hipe_gc.c b/erts/emulator/hipe/hipe_gc.c
index 566c65882e..68c65dea27 100644
--- a/erts/emulator/hipe/hipe_gc.c
+++ b/erts/emulator/hipe/hipe_gc.c
@@ -237,3 +237,122 @@ void gensweep_nstack(Process *p, Eterm **ptr_old_htop, Eterm **ptr_n_htop)
}
abort();
}
+
+Eterm *sweep_literals_nstack(Process *p, Eterm *old_htop, char *area,
+ Uint area_size)
+{
+ /* known nstack walk state */
+ Eterm *nsp;
+ Eterm *nsp_end;
+ const struct sdesc *sdesc;
+ /* arch-specific nstack walk state */
+ struct nstack_walk_state walk_state;
+
+ ASSERT(!p->hipe.gc_is_unsafe);
+
+ if (!p->hipe.nstack) {
+ ASSERT(!p->hipe.nsp && !p->hipe.nstend);
+ return old_htop;
+ }
+ if (!nstack_walk_init_check(p))
+ return old_htop;
+
+ ASSERT(p->hipe.nsp && p->hipe.nstend);
+ nsp = nstack_walk_nsp_begin(p);
+ nsp_end = nstack_walk_nsp_end(p);
+ sdesc = nstack_walk_init_sdesc_ignore_trap(p, &walk_state);
+
+ while (!nstack_walk_nsp_reached_end(nsp, nsp_end)) {
+ unsigned long ra;
+ unsigned sdesc_size = nstack_walk_frame_size(sdesc);
+ unsigned i = 0;
+ unsigned mask = sdesc->livebits[0];
+ for (;;) {
+ if (mask & 1) {
+ Eterm *nsp_i = nstack_walk_frame_index(nsp, i);
+ Eterm gval = *nsp_i;
+ if (is_boxed(gval)) {
+ Eterm *ptr = boxed_val(gval);
+ Eterm val = *ptr;
+ if (IS_MOVED_BOXED(val)) {
+ ASSERT(is_boxed(val));
+ *nsp_i = val;
+ } else if (ErtsInArea(ptr, area, area_size)) {
+ MOVE_BOXED(ptr, val, old_htop, nsp_i);
+ }
+ } else if (is_list(gval)) {
+ Eterm *ptr = list_val(gval);
+ Eterm val = *ptr;
+ if (IS_MOVED_CONS(val)) {
+ *nsp_i = ptr[1];
+ } else if (ErtsInArea(ptr, area, area_size)) {
+ MOVE_CONS(ptr, val, old_htop, nsp_i);
+ }
+ }
+ }
+ if (++i >= sdesc_size)
+ break;
+ if (i & 31)
+ mask >>= 1;
+ else
+ mask = sdesc->livebits[i >> 5];
+ }
+ ra = nstack_walk_frame_ra(nsp, sdesc);
+ if (ra == (unsigned long)nbif_stack_trap_ra)
+ ra = (unsigned long)p->hipe.ngra;
+ sdesc = hipe_find_sdesc(ra);
+ nsp = nstack_walk_next_frame(nsp, sdesc_size);
+ }
+ return old_htop;
+}
+
+int
+nstack_any_heap_ref_ptrs(Process *rp, char* mod_start, Uint mod_size)
+{
+ Eterm *nsp;
+ Eterm *nsp_end;
+ const struct sdesc *sdesc;
+ /* arch-specific nstack walk state */
+ struct nstack_walk_state walk_state;
+
+ ASSERT(!rp->hipe.gc_is_unsafe);
+
+ if (!rp->hipe.nstack || !nstack_walk_init_check(rp)) return 0;
+ ASSERT(rp->hipe.nsp && rp->hipe.nstend);
+ nsp = nstack_walk_nsp_begin(rp);
+ nsp_end = nstack_walk_nsp_end(rp);
+ sdesc = nstack_walk_init_sdesc_ignore_trap(rp, &walk_state);
+
+ while (!nstack_walk_nsp_reached_end(nsp, nsp_end)) {
+ unsigned long ra;
+ unsigned sdesc_size = nstack_walk_frame_size(sdesc);
+ unsigned i = 0;
+ unsigned mask = sdesc->livebits[0];
+ for (;;) {
+ if (mask & 1) {
+ Eterm *nsp_i = nstack_walk_frame_index(nsp, i);
+ Eterm val = *nsp_i;
+ switch (primary_tag(val)) {
+ case TAG_PRIMARY_BOXED:
+ case TAG_PRIMARY_LIST:
+ if (ErtsInArea(val, mod_start, mod_size)) {
+ return 1;
+ }
+ break;
+ }
+ }
+ if (++i >= sdesc_size)
+ break;
+ if (i & 31)
+ mask >>= 1;
+ else
+ mask = sdesc->livebits[i >> 5];
+ }
+ ra = nstack_walk_frame_ra(nsp, sdesc);
+ if (ra == (unsigned long)nbif_stack_trap_ra)
+ ra = (unsigned long)rp->hipe.ngra;
+ sdesc = hipe_find_sdesc(ra);
+ nsp = nstack_walk_next_frame(nsp, sdesc_size);
+ }
+ return 0;
+}
diff --git a/erts/emulator/hipe/hipe_risc_gc.h b/erts/emulator/hipe/hipe_risc_gc.h
index 315f8e7f9f..09568c140e 100644
--- a/erts/emulator/hipe/hipe_risc_gc.h
+++ b/erts/emulator/hipe/hipe_risc_gc.h
@@ -51,6 +51,19 @@ nstack_walk_init_sdesc(const Process *p, struct nstack_walk_state *state)
return sdesc;
}
+static inline const struct sdesc*
+nstack_walk_init_sdesc_ignore_trap(const Process *p,
+ struct nstack_walk_state *state)
+{
+ unsigned long ra = (unsigned long)p->hipe.nra;
+ const struct sdesc *sdesc;
+ if (ra == (unsigned long)&nbif_stack_trap_ra)
+ ra = (unsigned long)p->hipe.ngra;
+ sdesc = hipe_find_sdesc(ra);
+ state->sdesc0 = sdesc;
+ return sdesc;
+}
+
static inline void nstack_walk_update_trap(Process *p, const struct sdesc *sdesc0)
{
Eterm *nsp = p->hipe.nsp;
diff --git a/erts/emulator/hipe/hipe_stack.h b/erts/emulator/hipe/hipe_stack.h
index 4ea7d5c031..afa0ed4256 100644
--- a/erts/emulator/hipe/hipe_stack.h
+++ b/erts/emulator/hipe/hipe_stack.h
@@ -131,5 +131,8 @@ static __inline__ void hipe_check_nstack(Process *p, unsigned nwords)
*/
extern Eterm *fullsweep_nstack(Process *p, Eterm *n_htop);
extern void gensweep_nstack(Process *p, Eterm **ptr_old_htop, Eterm **ptr_n_htop);
+extern Eterm *sweep_literals_nstack(Process *p, Eterm *n_htop, char *area,
+ Uint area_size);
+extern int nstack_any_heap_ref_ptrs(Process *, char* mod_start, Uint mod_size);
#endif /* HIPE_STACK_H */
diff --git a/erts/emulator/hipe/hipe_x86_gc.h b/erts/emulator/hipe/hipe_x86_gc.h
index c22b28c2d5..00fe03d8f9 100644
--- a/erts/emulator/hipe/hipe_x86_gc.h
+++ b/erts/emulator/hipe/hipe_x86_gc.h
@@ -81,6 +81,23 @@ nstack_walk_init_sdesc(const Process *p, struct nstack_walk_state *state)
#endif
}
+static inline const struct sdesc*
+nstack_walk_init_sdesc_ignore_trap(const Process *p,
+ struct nstack_walk_state *state)
+{
+#ifdef SKIP_YOUNGEST_FRAME
+ unsigned long ra = p->hipe.nsp[0];
+ const struct sdesc *sdesc;
+ if (ra == (unsigned long)nbif_stack_trap_ra)
+ ra = (unsigned long)p->hipe.ngra;
+ sdesc = hipe_find_sdesc(ra);
+ state->sdesc0 = sdesc;
+ return sdesc;
+#else
+ return nstack_walk_init_sdesc(p, state);
+#endif
+}
+
static inline void nstack_walk_update_trap(Process *p, const struct sdesc *sdesc0)
{
#ifdef SKIP_YOUNGEST_FRAME
diff --git a/erts/emulator/internal_doc/Tracing.md b/erts/emulator/internal_doc/Tracing.md
index 30bc5327a7..728f315263 100644
--- a/erts/emulator/internal_doc/Tracing.md
+++ b/erts/emulator/internal_doc/Tracing.md
@@ -57,7 +57,7 @@ generations of breakpoints are kept and indentified by index of 0 and
1. The global atomic variables `erts_active_bp_index` will determine
which generation of breakpoints running code will use.
-### Atomicy Without Atomic Operations
+### Atomicity Without Atomic Operations
Not using the code loading generations (or any other code duplication)
means that `trace_pattern` must at some point write to the active beam
diff --git a/erts/emulator/sys/common/erl_poll.c b/erts/emulator/sys/common/erl_poll.c
index e394d84f73..b8a28bcc18 100644
--- a/erts/emulator/sys/common/erl_poll.c
+++ b/erts/emulator/sys/common/erl_poll.c
@@ -75,6 +75,7 @@
#include "erl_driver.h"
#include "erl_alloc.h"
#include "erl_msacc.h"
+#include "erl_misc_utils.h"
#if !defined(ERTS_POLL_USE_EPOLL) \
&& !defined(ERTS_POLL_USE_DEVPOLL) \
@@ -2132,16 +2133,19 @@ get_timeout(ErtsPollSet ps,
if (timeout > (ErtsMonotonicTime) INT_MAX)
timeout = (ErtsMonotonicTime) INT_MAX;
save_timeout_time += ERTS_MSEC_TO_MONOTONIC(timeout);
+ timeout -= ERTS_PREMATURE_TIMEOUT(timeout, 1000);
break;
case 1000000:
/* Round up to nearest even micro second */
timeout = ERTS_MONOTONIC_TO_USEC(diff_time - 1) + 1;
save_timeout_time += ERTS_USEC_TO_MONOTONIC(timeout);
+ timeout -= ERTS_PREMATURE_TIMEOUT(timeout, 1000*1000);
break;
case 1000000000:
/* Round up to nearest even nano second */
timeout = ERTS_MONOTONIC_TO_NSEC(diff_time - 1) + 1;
save_timeout_time += ERTS_NSEC_TO_MONOTONIC(timeout);
+ timeout -= ERTS_PREMATURE_TIMEOUT(timeout, 1000*1000*1000);
break;
default:
ERTS_INTERNAL_ERROR("Invalid resolution");
@@ -2452,7 +2456,15 @@ ERTS_POLL_EXPORT(erts_poll_wait)(ErtsPollSet ps,
}
#endif
- res = check_fd_events(ps, to, no_fds);
+ while (1) {
+ res = check_fd_events(ps, to, no_fds);
+ if (res != 0)
+ break;
+ if (to == ERTS_POLL_NO_TIMEOUT)
+ break;
+ if (erts_get_monotonic_time(NULL) >= timeout_time)
+ break;
+ }
woke_up(ps);
diff --git a/erts/emulator/sys/unix/erl_child_setup.c b/erts/emulator/sys/unix/erl_child_setup.c
index 6b9ddd8da4..69fc6c2879 100644
--- a/erts/emulator/sys/unix/erl_child_setup.c
+++ b/erts/emulator/sys/unix/erl_child_setup.c
@@ -348,7 +348,7 @@ child_error:
* for posterity. */
static void handle_sigchld(int sig) {
- int buff[2], res;
+ int buff[2], res, __preverrno = errno;
sys_sigblock(SIGCHLD);
@@ -362,6 +362,16 @@ static void handle_sigchld(int sig) {
}
sys_sigrelease(SIGCHLD);
+
+ /* We save and restore the original errno as otherwise
+ the thread we are running in may end up with an
+ unexpected errno. An example of when this happened
+ was when the select in main had gotten an EINTR but
+ before the errno was checked the signal handler
+ was called and set errno to ECHILD from waitpid
+ which caused erl_child_setup to abort as it does
+ not expect ECHILD to be set after select */
+ errno = __preverrno;
}
#if defined(__ANDROID__)
@@ -423,7 +433,7 @@ main(int argc, char *argv[])
sigemptyset(&sa.sa_mask);
sa.sa_flags = SA_RESTART | SA_NOCLDSTOP;
if (sigaction(SIGCHLD, &sa, 0) == -1) {
- perror(0);
+ perror(NULL);
exit(1);
}
diff --git a/erts/emulator/sys/unix/sys.c b/erts/emulator/sys/unix/sys.c
index 6fb86f6dda..b0e623a5b9 100644
--- a/erts/emulator/sys/unix/sys.c
+++ b/erts/emulator/sys/unix/sys.c
@@ -715,11 +715,13 @@ static RETSIGTYPE suspend_signal(void)
static RETSIGTYPE suspend_signal(int signum)
#endif
{
- int res;
- int buf[1];
- do {
- res = read(sig_suspend_fds[0], buf, sizeof(int));
- } while (res < 0 && errno == EINTR);
+ int res, buf[1], tmp_errno = errno;
+ do {
+ res = read(sig_suspend_fds[0], buf, sizeof(int));
+ } while (res < 0 && errno == EINTR);
+
+ /* restore previous errno in case read changed it */
+ errno = tmp_errno;
}
#endif /* #ifdef ERTS_SYS_SUSPEND_SIGNAL */
diff --git a/erts/emulator/test/Makefile b/erts/emulator/test/Makefile
index b580211eff..2e48c475d5 100644
--- a/erts/emulator/test/Makefile
+++ b/erts/emulator/test/Makefile
@@ -70,6 +70,7 @@ MODULES= \
guard_SUITE \
hash_SUITE \
hibernate_SUITE \
+ hipe_SUITE \
list_bif_SUITE \
lttng_SUITE \
map_SUITE \
diff --git a/erts/emulator/test/after_SUITE.erl b/erts/emulator/test/after_SUITE.erl
index 4f20ad3656..b1f7e06bf5 100644
--- a/erts/emulator/test/after_SUITE.erl
+++ b/erts/emulator/test/after_SUITE.erl
@@ -223,7 +223,7 @@ recv_after_32bit(_, _) ->
blaster() ->
receive
{go, TimeoutTime} ->
- Tmo = TimeoutTime - erlang:monotonic_time(milli_seconds),
+ Tmo = TimeoutTime - erlang:monotonic_time(millisecond),
receive after Tmo -> ok end
end.
@@ -234,7 +234,7 @@ spawn_blasters(N) ->
receive_after_blast(Config) when is_list(Config) ->
PMs = spawn_blasters(10000),
- TimeoutTime = erlang:monotonic_time(milli_seconds) + 5000,
+ TimeoutTime = erlang:monotonic_time(millisecond) + 5000,
lists:foreach(fun ({P, _}) -> P ! {go, TimeoutTime} end, PMs),
lists:foreach(fun ({P, M}) ->
receive
diff --git a/erts/emulator/test/alloc_SUITE.erl b/erts/emulator/test/alloc_SUITE.erl
index 84cf4921d3..3a721095e2 100644
--- a/erts/emulator/test/alloc_SUITE.erl
+++ b/erts/emulator/test/alloc_SUITE.erl
@@ -342,7 +342,7 @@ start_node_1(Config, Opts) ->
++ "-"
++ atom_to_list(proplists:get_value(testcase, Config))
++ "-"
- ++ integer_to_list(erlang:system_time(seconds))
+ ++ integer_to_list(erlang:system_time(second))
++ "-"
++ integer_to_list(erlang:unique_integer([positive]))),
test_server:start_node(Name, slave, [{args, Opts++" -pa "++Pa}]).
diff --git a/erts/emulator/test/busy_port_SUITE.erl b/erts/emulator/test/busy_port_SUITE.erl
index bb0632ae08..7094cee992 100644
--- a/erts/emulator/test/busy_port_SUITE.erl
+++ b/erts/emulator/test/busy_port_SUITE.erl
@@ -469,12 +469,12 @@ hs_busy_pcmd(Prt, Opts, StartFun, EndFun) ->
P = spawn_link(fun () ->
erlang:yield(),
Tester ! {self(), doing_port_command},
- Start = erlang:monotonic_time(micro_seconds),
+ Start = erlang:monotonic_time(microsecond),
Res = try {return,
port_command(Prt, [], Opts)}
catch Exception:Error -> {Exception, Error}
end,
- End = erlang:monotonic_time(micro_seconds),
+ End = erlang:monotonic_time(microsecond),
Time = round((End - Start)/1000),
Tester ! {self(), port_command_result, Res, Time}
end),
@@ -717,7 +717,7 @@ run_command(_M,spawn,{Args,Opts}) ->
run_command(M,spawn,Args) ->
run_command(M,spawn,{Args,[]});
run_command(Mod,Func,Args) ->
- erlang:display({{Mod,Func,Args}, erlang:system_time(micro_seconds)}),
+ erlang:display({{Mod,Func,Args}, erlang:system_time(microsecond)}),
apply(Mod,Func,Args).
validate_scenario(Data,[{print,Var}|T]) ->
diff --git a/erts/emulator/test/code_SUITE.erl b/erts/emulator/test/code_SUITE.erl
index 2347a3d4ef..34515efa3d 100644
--- a/erts/emulator/test/code_SUITE.erl
+++ b/erts/emulator/test/code_SUITE.erl
@@ -20,7 +20,8 @@
-module(code_SUITE).
-export([all/0, suite/0, init_per_suite/1, end_per_suite/1,
- versions/1,new_binary_types/1,
+ versions/1,new_binary_types/1, call_purged_fun_code_gone/1,
+ call_purged_fun_code_reload/1, call_purged_fun_code_there/1,
t_check_process_code/1,t_check_old_code/1,
t_check_process_code_ets/1,
external_fun/1,get_chunk/1,module_md5/1,make_stub/1,
@@ -34,7 +35,8 @@
suite() -> [{ct_hooks,[ts_install_cth]}].
all() ->
- [versions, new_binary_types, t_check_process_code,
+ [versions, new_binary_types, call_purged_fun_code_gone,
+ call_purged_fun_code_reload, call_purged_fun_code_there, t_check_process_code,
t_check_process_code_ets, t_check_old_code, external_fun, get_chunk,
module_md5, make_stub, make_stub_many_funs,
constant_pools, constant_refc_binaries, false_dependency,
@@ -127,12 +129,169 @@ new_binary_types(Config) when is_list(Config) ->
bit_sized_binary(Bin))),
ok.
+call_purged_fun_code_gone(Config) when is_list(Config) ->
+ Priv = proplists:get_value(priv_dir, Config),
+ Data = proplists:get_value(data_dir, Config),
+ call_purged_fun_test(Priv, Data, code_gone),
+ ok.
+
+call_purged_fun_code_reload(Config) when is_list(Config) ->
+ Priv = proplists:get_value(priv_dir, Config),
+ Data = proplists:get_value(data_dir, Config),
+ Path = code:get_path(),
+ true = code:add_path(Priv),
+ try
+ call_purged_fun_test(Priv, Data, code_reload)
+ after
+ code:set_path(Path)
+ end,
+ ok.
+
+call_purged_fun_code_there(Config) when is_list(Config) ->
+ Priv = proplists:get_value(priv_dir, Config),
+ Data = proplists:get_value(data_dir, Config),
+ call_purged_fun_test(Priv, Data, code_there),
+ ok.
+
+call_purged_fun_test(Priv, Data, Type) ->
+ File = filename:join(Data, "my_code_test2"),
+ Code = filename:join(Priv, "my_code_test2"),
+
+ catch erlang:purge_module(my_code_test2),
+ catch erlang:delete_module(my_code_test2),
+ catch erlang:purge_module(my_code_test2),
+
+ {ok,my_code_test2} = c:c(File, [{outdir,Priv}]),
+
+ T = ets:new(my_code_test2_fun_table, []),
+ ets:insert(T, {my_fun,my_code_test2:make_fun(4711)}),
+ ets:insert(T, {my_fun2,my_code_test2:make_fun2()}),
+
+ spawn(fun () ->
+ [{my_fun2,F2}] = ets:lookup(T, my_fun2),
+ F2(fun () ->
+ receive after infinity -> ok end
+ end,
+ fun () -> ok end),
+ exit(completed)
+ end),
+
+ PurgeType = case Type of
+ code_gone ->
+ ok = file:delete(Code++".beam"),
+ true;
+ code_reload ->
+ true;
+ code_there ->
+ false
+ end,
+
+ true = erlang:delete_module(my_code_test2),
+
+ Purge = start_purge(my_code_test2, PurgeType),
+
+ {P0, M0} = spawn_monitor(fun () ->
+ [{my_fun,F}] = ets:lookup(T, my_fun),
+ 4712 = F(1),
+ exit(completed)
+ end),
+
+ wait_until(fun () ->
+ {status, suspended}
+ == process_info(P0, status)
+ end),
+
+ ok = continue_purge(Purge),
+
+ {P1, M1} = spawn_monitor(fun () ->
+ [{my_fun,F}] = ets:lookup(T, my_fun),
+ 4713 = F(2),
+ exit(completed)
+ end),
+ {P2, M2} = spawn_monitor(fun () ->
+ [{my_fun,F}] = ets:lookup(T, my_fun),
+ 4714 = F(3),
+ exit(completed)
+ end),
+
+ wait_until(fun () ->
+ {status, suspended}
+ == process_info(P1, status)
+ end),
+ wait_until(fun () ->
+ {status, suspended}
+ == process_info(P2, status)
+ end),
+
+ {current_function,
+ {erts_code_purger,
+ pending_purge_lambda,
+ 3}} = process_info(P0, current_function),
+ {current_function,
+ {erts_code_purger,
+ pending_purge_lambda,
+ 3}} = process_info(P1, current_function),
+ {current_function,
+ {erts_code_purger,
+ pending_purge_lambda,
+ 3}} = process_info(P2, current_function),
+
+ case Type of
+ code_there ->
+ false = complete_purge(Purge);
+ _ ->
+ {true, true} = complete_purge(Purge)
+ end,
+
+ case Type of
+ code_gone ->
+ receive
+ {'DOWN', M0, process, P0, Reason0} ->
+ {undef, _} = Reason0
+ end,
+ receive
+ {'DOWN', M1, process, P1, Reason1} ->
+ {undef, _} = Reason1
+ end,
+ receive
+ {'DOWN', M2, process, P2, Reason2} ->
+ {undef, _} = Reason2
+ end;
+ _ ->
+ receive
+ {'DOWN', M0, process, P0, Reason0} ->
+ completed = Reason0
+ end,
+ receive
+ {'DOWN', M1, process, P1, Reason1} ->
+ completed = Reason1
+ end,
+ receive
+ {'DOWN', M2, process, P2, Reason2} ->
+ completed = Reason2
+ end,
+ catch erlang:purge_module(my_code_test2),
+ catch erlang:delete_module(my_code_test2),
+ catch erlang:purge_module(my_code_test2)
+ end,
+ ok.
+
t_check_process_code(Config) when is_list(Config) ->
+ case check_process_code_handle(indirect_references) of
+ false -> {skipped, "check_process_code() ignores funs"};
+ true -> t_check_process_code_test(Config)
+ end.
+
+t_check_process_code_test(Config) ->
Priv = proplists:get_value(priv_dir, Config),
Data = proplists:get_value(data_dir, Config),
File = filename:join(Data, "my_code_test"),
Code = filename:join(Priv, "my_code_test"),
+ catch erlang:purge_module(my_code_test),
+ catch erlang:delete_module(my_code_test),
+ catch erlang:purge_module(my_code_test),
+
{ok,my_code_test} = c:c(File, [{outdir,Priv}]),
MyFun = fun(X, Y) -> X + Y end, %Confuse things.
@@ -231,11 +390,16 @@ gc1() -> ok.
%% Test check_process_code/2 in combination with a fun obtained from an ets table.
t_check_process_code_ets(Config) when is_list(Config) ->
- case test_server:is_native(?MODULE) of
- true ->
- {skip,"Native code"};
- false ->
- do_check_process_code_ets(Config)
+ case check_process_code_handle(indirect_references) of
+ false ->
+ {skipped, "check_process_code() ignores funs"};
+ true ->
+ case test_server:is_native(?MODULE) of
+ true ->
+ {skip,"Native code"};
+ false ->
+ do_check_process_code_ets(Config)
+ end
end.
do_check_process_code_ets(Config) ->
@@ -243,8 +407,9 @@ do_check_process_code_ets(Config) ->
Data = proplists:get_value(data_dir, Config),
File = filename:join(Data, "my_code_test"),
- erlang:purge_module(my_code_test),
- erlang:delete_module(my_code_test),
+ catch erlang:purge_module(my_code_test),
+ catch erlang:delete_module(my_code_test),
+ catch erlang:purge_module(my_code_test),
{ok,my_code_test} = c:c(File, [{outdir,Priv}]),
T = ets:new(my_code_test, []),
@@ -295,8 +460,8 @@ t_check_old_code(Config) when is_list(Config) ->
Data = proplists:get_value(data_dir, Config),
File = filename:join(Data, "my_code_test"),
- erlang:purge_module(my_code_test),
- erlang:delete_module(my_code_test),
+ catch erlang:purge_module(my_code_test),
+ catch erlang:delete_module(my_code_test),
catch erlang:purge_module(my_code_test),
false = erlang:check_old_code(my_code_test),
@@ -971,3 +1136,39 @@ flush() ->
receive _ -> flush() after 0 -> ok end.
id(I) -> I.
+
+check_process_code_handle(What) ->
+ lists:member(What, erlang:system_info(check_process_code)).
+
+wait_until(Fun) ->
+ case Fun() of
+ true ->
+ ok;
+ false ->
+ receive after 100 -> ok end,
+ wait_until(Fun)
+ end.
+
+start_purge(Mod, Type) when is_atom(Mod)
+ andalso ((Type == true)
+ orelse (Type == false)) ->
+ Ref = make_ref(),
+ erts_code_purger ! {test_purge, Mod, self(), Type, Ref},
+ receive
+ {started, Ref} ->
+ Ref
+ end.
+
+continue_purge(Ref) when is_reference(Ref) ->
+ erts_code_purger ! {continue, Ref},
+ receive
+ {continued, Ref} ->
+ ok
+ end.
+
+complete_purge(Ref) when is_reference(Ref) ->
+ erts_code_purger ! {complete, Ref},
+ receive
+ {test_purge, Res, Ref} ->
+ Res
+ end.
diff --git a/erts/emulator/test/code_SUITE_data/my_code_test.erl b/erts/emulator/test/code_SUITE_data/my_code_test.erl
index d2386157d6..9d12aa9897 100644
--- a/erts/emulator/test/code_SUITE_data/my_code_test.erl
+++ b/erts/emulator/test/code_SUITE_data/my_code_test.erl
@@ -24,5 +24,3 @@
make_fun(A) ->
fun(X) -> A + X end.
-
-
diff --git a/erts/emulator/test/code_SUITE_data/my_code_test2.erl b/erts/emulator/test/code_SUITE_data/my_code_test2.erl
new file mode 100644
index 0000000000..57973535d4
--- /dev/null
+++ b/erts/emulator/test/code_SUITE_data/my_code_test2.erl
@@ -0,0 +1,32 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 1999-2016. All Rights Reserved.
+%%
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+-module(my_code_test2).
+
+-export([make_fun/1, make_fun2/0]).
+
+make_fun(A) ->
+ fun(X) -> A + X end.
+
+make_fun2() ->
+ fun (F1,F2) ->
+ F1(),
+ F2()
+ end.
diff --git a/erts/emulator/test/dirty_nif_SUITE.erl b/erts/emulator/test/dirty_nif_SUITE.erl
index 83b098a704..658bdc41b6 100644
--- a/erts/emulator/test/dirty_nif_SUITE.erl
+++ b/erts/emulator/test/dirty_nif_SUITE.erl
@@ -33,7 +33,8 @@
dirty_nif_exception/1, call_dirty_nif_exception/1,
dirty_scheduler_exit/1, dirty_call_while_terminated/1,
dirty_heap_access/1, dirty_process_info/1,
- dirty_process_register/1, dirty_process_trace/1]).
+ dirty_process_register/1, dirty_process_trace/1,
+ code_purge/1, dirty_nif_send_traced/1]).
-define(nif_stub,nif_stub_error(?LINE)).
@@ -48,7 +49,9 @@ all() ->
dirty_heap_access,
dirty_process_info,
dirty_process_register,
- dirty_process_trace].
+ dirty_process_trace,
+ code_purge,
+ dirty_nif_send_traced].
init_per_suite(Config) ->
try erlang:system_info(dirty_cpu_schedulers) of
@@ -145,9 +148,9 @@ dirty_scheduler_exit(Config) when is_list(Config) ->
[ok] = mcall(Node,
[fun() ->
ok = erlang:load_nif(NifLib, []),
- Start = erlang:monotonic_time(milli_seconds),
+ Start = erlang:monotonic_time(millisecond),
ok = test_dirty_scheduler_exit(),
- End = erlang:monotonic_time(milli_seconds),
+ End = erlang:monotonic_time(millisecond),
io:format("Time=~p ms~n", [End-Start]),
ok
end]),
@@ -230,7 +233,11 @@ dirty_call_while_terminated(Config) when is_list(Config) ->
process_info(self(),
binary))),
process_flag(trap_exit, OT),
- ok.
+ try
+ blipp:blupp(Bin)
+ catch
+ _ : _ -> ok
+ end.
dirty_heap_access(Config) when is_list(Config) ->
{ok, Node} = start_node(Config),
@@ -349,6 +356,103 @@ dirty_process_trace(Config) when is_list(Config) ->
ok
end).
+dirty_code_test_code() ->
+ "
+-module(dirty_code_test).
+
+-export([func/1]).
+
+func(Fun) ->
+ Fun(),
+ blipp:blapp().
+
+".
+
+code_purge(Config) when is_list(Config) ->
+ Path = ?config(data_dir, Config),
+ File = filename:join(Path, "dirty_code_test.erl"),
+ ok = file:write_file(File, dirty_code_test_code()),
+ {ok, dirty_code_test, Bin} = compile:file(File, [binary]),
+ {module, dirty_code_test} = erlang:load_module(dirty_code_test, Bin),
+ Start = erlang:monotonic_time(),
+ {Pid1, Mon1} = spawn_monitor(fun () ->
+ dirty_code_test:func(fun () ->
+ %% Sleep for 6 seconds
+ %% in dirty nif...
+ dirty_sleeper()
+ end)
+ end),
+ {module, dirty_code_test} = erlang:load_module(dirty_code_test, Bin),
+ {Pid2, Mon2} = spawn_monitor(fun () ->
+ dirty_code_test:func(fun () ->
+ %% Sleep for 6 seconds
+ %% in dirty nif...
+ dirty_sleeper()
+ end)
+ end),
+ receive
+ {'DOWN', Mon1, process, Pid1, _} ->
+ ct:fail(premature_death)
+ after 100 ->
+ ok
+ end,
+ true = erlang:purge_module(dirty_code_test),
+ receive
+ {'DOWN', Mon1, process, Pid1, Reason1} ->
+ killed = Reason1
+ end,
+ receive
+ {'DOWN', Mon2, process, Pid2, _} ->
+ ct:fail(premature_death)
+ after 100 ->
+ ok
+ end,
+ true = erlang:delete_module(dirty_code_test),
+ receive
+ {'DOWN', Mon2, process, Pid2, _} ->
+ ct:fail(premature_death)
+ after 100 ->
+ ok
+ end,
+ true = erlang:purge_module(dirty_code_test),
+ receive
+ {'DOWN', Mon2, process, Pid2, Reason2} ->
+ killed = Reason2
+ end,
+ End = erlang:monotonic_time(),
+ Time = erlang:convert_time_unit(End-Start, native, milli_seconds),
+ io:format("Time=~p~n", [Time]),
+ true = Time =< 1000,
+ ok.
+
+dirty_nif_send_traced(Config) when is_list(Config) ->
+ Parent = self(),
+ Rcvr = spawn_link(fun() ->
+ Self = self(),
+ receive {ok, Self} -> ok end,
+ Parent ! {Self, received}
+ end),
+ Sndr = spawn_link(fun () ->
+ receive {Parent, go} -> ok end,
+ {ok, Rcvr} = send_wait_from_dirty_nif(Rcvr),
+ Parent ! {self(), sent}
+ end),
+ 1 = erlang:trace(Sndr, true, [send]),
+ Start = erlang:monotonic_time(),
+ Sndr ! {self(), go},
+ receive {trace, Sndr, send, {ok, Rcvr}, Rcvr} -> ok end,
+ receive {Rcvr, received} -> ok end,
+ End1 = erlang:monotonic_time(),
+ Time1 = erlang:convert_time_unit(End1-Start, native, 1000),
+ io:format("Time1: ~p milliseconds~n", [Time1]),
+ true = Time1 < 500,
+ receive {Sndr, sent} -> ok end,
+ End2 = erlang:monotonic_time(),
+ Time2 = erlang:convert_time_unit(End2-Start, native, 1000),
+ io:format("Time2: ~p milliseconds~n", [Time2]),
+ true = Time2 >= 1900,
+ ok.
+
%%
%% Internal...
%%
@@ -400,7 +504,7 @@ start_node(Config, Args) when is_list(Config) ->
++ "-"
++ atom_to_list(proplists:get_value(testcase, Config))
++ "-"
- ++ integer_to_list(erlang:system_time(seconds))
+ ++ integer_to_list(erlang:system_time(second))
++ "-"
++ integer_to_list(erlang:unique_integer([positive]))),
test_server:start_node(Name, slave, [{args, "-pa "++Pa++" "++Args}]).
@@ -431,6 +535,7 @@ mcall(Node, Funs) ->
lib_loaded() -> false.
call_dirty_nif(_,_,_) -> ?nif_stub.
send_from_dirty_nif(_) -> ?nif_stub.
+send_wait_from_dirty_nif(_) -> ?nif_stub.
call_dirty_nif_exception(_) -> ?nif_stub.
call_dirty_nif_zero_args() -> ?nif_stub.
dirty_call_while_terminated_nif(_) -> ?nif_stub.
diff --git a/erts/emulator/test/dirty_nif_SUITE_data/dirty_nif_SUITE.c b/erts/emulator/test/dirty_nif_SUITE_data/dirty_nif_SUITE.c
index d92933a096..a0019e5d95 100644
--- a/erts/emulator/test/dirty_nif_SUITE_data/dirty_nif_SUITE.c
+++ b/erts/emulator/test/dirty_nif_SUITE_data/dirty_nif_SUITE.c
@@ -100,6 +100,32 @@ static ERL_NIF_TERM send_from_dirty_nif(ErlNifEnv* env, int argc, const ERL_NIF_
return result;
}
+static ERL_NIF_TERM send_wait_from_dirty_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
+{
+ ERL_NIF_TERM result;
+ ErlNifPid pid;
+ ErlNifEnv* menv;
+ int res;
+
+ if (!enif_get_local_pid(env, argv[0], &pid))
+ return enif_make_badarg(env);
+ result = enif_make_tuple2(env, enif_make_atom(env, "ok"), enif_make_pid(env, &pid));
+ menv = enif_alloc_env();
+ res = enif_send(env, &pid, menv, result);
+ enif_free_env(menv);
+
+#ifdef __WIN32__
+ Sleep(2000);
+#else
+ sleep(2);
+#endif
+
+ if (!res)
+ return enif_make_badarg(env);
+ else
+ return result;
+}
+
static ERL_NIF_TERM call_dirty_nif_exception(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
switch (argc) {
@@ -237,6 +263,7 @@ static ErlNifFunc nif_funcs[] =
{"lib_loaded", 0, lib_loaded},
{"call_dirty_nif", 3, call_dirty_nif},
{"send_from_dirty_nif", 1, send_from_dirty_nif, ERL_NIF_DIRTY_JOB_CPU_BOUND},
+ {"send_wait_from_dirty_nif", 1, send_wait_from_dirty_nif, ERL_NIF_DIRTY_JOB_IO_BOUND},
{"call_dirty_nif_exception", 1, call_dirty_nif_exception, ERL_NIF_DIRTY_JOB_IO_BOUND},
{"call_dirty_nif_zero_args", 0, call_dirty_nif_zero_args, ERL_NIF_DIRTY_JOB_CPU_BOUND},
{"dirty_sleeper", 0, dirty_sleeper, ERL_NIF_DIRTY_JOB_IO_BOUND},
diff --git a/erts/emulator/test/distribution_SUITE.erl b/erts/emulator/test/distribution_SUITE.erl
index c6939a695d..6994bfef83 100644
--- a/erts/emulator/test/distribution_SUITE.erl
+++ b/erts/emulator/test/distribution_SUITE.erl
@@ -43,7 +43,7 @@
lost_exit/1, link_to_dead/1, link_to_dead_new_node/1,
applied_monitor_node/1, ref_port_roundtrip/1, nil_roundtrip/1,
trap_bif_1/1, trap_bif_2/1, trap_bif_3/1,
- stop_dist/1,
+ stop_dist/1,
dist_auto_connect_never/1, dist_auto_connect_once/1,
dist_parallel_send/1,
atom_roundtrip/1,
@@ -66,13 +66,13 @@
sendersender/4, sendersender2/4]).
%% epmd_module exports
--export([start_link/0, register_node/2, port_please/2]).
+-export([start_link/0, register_node/2, register_node/3, port_please/2]).
suite() ->
[{ct_hooks,[ts_install_cth]},
{timetrap, {minutes, 4}}].
-all() ->
+all() ->
[ping, {group, bulk_send}, {group, local_send},
link_to_busy, exit_to_busy, lost_exit, link_to_dead,
link_to_dead_new_node, applied_monitor_node,
@@ -83,7 +83,7 @@ all() ->
bad_dist_structure, {group, bad_dist_ext},
start_epmd_false, epmd_module].
-groups() ->
+groups() ->
[{bulk_send, [], [bulk_send_small, bulk_send_big, bulk_send_bigbig]},
{local_send, [],
[local_send_small, local_send_big, local_send_legal]},
@@ -844,59 +844,50 @@ dist_auto_connect_once(Config) when is_list(Config) ->
%% Result is sent here through relay node.
dist_auto_connect_never(Config) when is_list(Config) ->
Self = self(),
- {ok, RelayNode} =
- start_node(dist_auto_connect_relay),
- spawn(RelayNode,
+ {ok, RelayNode} = start_node(dist_auto_connect_relay),
+ spawn(RelayNode,
fun() ->
register(dist_auto_connect_relay, self()),
- dist_auto_connect_relay(Self)
+ dist_auto_connect_relay(Self)
end),
{ok, Handle} = dist_auto_connect_start(dist_auto_connect, never),
- Result =
- receive
- {do_dist_auto_connect, ok} ->
- ok;
- {do_dist_auto_connect, Error} ->
- {error, Error};
- Other ->
- {error, Other}
- after 32000 ->
- timeout
- end,
+ Result = receive
+ {do_dist_auto_connect, ok} ->
+ ok;
+ {do_dist_auto_connect, Error} ->
+ {error, Error};
+ Other ->
+ {error, Other}
+ after 32000 ->
+ timeout
+ end,
stop_node(RelayNode),
- Stopped = dist_auto_connect_stop(Handle),
- Junk =
- receive
- {do_dist_auto_connect, _} = J ->
- J
- after 0 ->
- ok
- end,
+ Stopped = dist_auto_connect_stop(Handle),
+ Junk = receive
+ {do_dist_auto_connect, _} = J -> J
+ after 0 -> ok
+ end,
{ok, ok, ok} = {Result, Stopped, Junk},
ok.
do_dist_auto_connect([never]) ->
Node = list_to_atom("dist_auto_connect_relay@" ++ hostname()),
- io:format("~p:do_dist_auto_connect([false]) Node=~p~n",
- [?MODULE, Node]),
+ io:format("~p:do_dist_auto_connect([false]) Node=~p~n", [?MODULE, Node]),
Ping = net_adm:ping(Node),
- io:format("~p:do_dist_auto_connect([false]) Ping=~p~n",
- [?MODULE, Ping]),
+ io:format("~p:do_dist_auto_connect([false]) Ping=~p~n", [?MODULE, Ping]),
Result = case Ping of
pang -> ok;
_ -> {error, Ping}
end,
- io:format("~p:do_dist_auto_connect([false]) Result=~p~n",
- [?MODULE, Result]),
+ io:format("~p:do_dist_auto_connect([false]) Result=~p~n", [?MODULE, Result]),
net_kernel:connect_node(Node),
catch {dist_auto_connect_relay, Node} ! {do_dist_auto_connect, Result};
% receive after 1000 -> ok end,
% halt();
do_dist_auto_connect(Arg) ->
- io:format("~p:do_dist_auto_connect(~p)~n",
- [?MODULE, Arg]),
+ io:format("~p:do_dist_auto_connect(~p)~n", [?MODULE, Arg]),
receive after 10000 -> ok end,
halt().
@@ -912,11 +903,11 @@ dist_auto_connect_start(Name, Value) when is_list(Name), is_atom(Value) ->
[%"xterm -e ",
atom_to_list(lib:progname()),
% " -noinput ",
- " -detached ",
+ " -detached ",
long_or_short(), " ", Name,
" -setcookie ", Cookie,
" -pa ", ModuleDir,
- " -s ", atom_to_list(?MODULE),
+ " -s ", atom_to_list(?MODULE),
" do_dist_auto_connect ", ValueStr,
" -kernel dist_auto_connect ", ValueStr]),
io:format("~p:dist_auto_connect_start() cmd: ~p~n", [?MODULE, Cmd]),
@@ -947,7 +938,7 @@ dist_auto_connect_stop(Port, Node, Pid, N) when is_integer(N) ->
end.
-dist_auto_connect_relay(Parent) ->
+dist_auto_connect_relay(Parent) ->
receive X ->
catch Parent ! X
end,
@@ -1321,7 +1312,7 @@ get_conflicting_unicode_atoms(CIX, N) ->
start_monitor(Offender,P) ->
Parent = self(),
Q = spawn(Offender,
- fun () ->
+ fun () ->
Ref = erlang:monitor(process,P),
Parent ! {self(),ref,Ref},
receive
@@ -1458,8 +1449,8 @@ bad_dist_structure(Config) when is_list(Config) ->
pong = rpc:call(Victim, net_adm, ping, [Offender]),
P ! two,
P ! check_msgs,
- receive
- {P, messages_checked} -> ok
+ receive
+ {P, messages_checked} -> ok
after 5000 ->
exit(victim_is_dead)
end,
@@ -1765,7 +1756,7 @@ send_bad_structure(Offender,Victim,Bad,WhereToPutSelf,PayLoad) ->
pong = net_adm:ping(Node),
DPrt = dport(Node),
Bad1 = case WhereToPutSelf of
- 0 ->
+ 0 ->
Bad;
N when N > 0 ->
setelement(N,Bad,self())
@@ -1779,8 +1770,8 @@ send_bad_structure(Offender,Victim,Bad,WhereToPutSelf,PayLoad) ->
port_command(DPrt, DData),
Parent ! {DData,Done}
end),
- receive
- {WhatSent,Done} ->
+ receive
+ {WhatSent,Done} ->
io:format("Offender sent ~p~n",[WhatSent]),
ok
after 5000 ->
@@ -1887,7 +1878,7 @@ dmsg_fake_hdr2() ->
1, size(A2), A2,
2, size(A3), A3].
-dmsg_ext(Term) ->
+dmsg_ext(Term) ->
<<131, Res/binary>> = term_to_binary(Term),
Res.
@@ -1934,7 +1925,9 @@ epmd_module(Config) when is_list(Config) ->
start_link() ->
ignore.
-register_node(_Name, Port) ->
+register_node(Name, Port) ->
+ register_node(Name, Port, inet_tcp).
+register_node(_Name, Port, _Driver) ->
%% Save the port number we're listening on.
application:set_env(kernel, dist_listen_port, Port),
Creation = rand:uniform(3),
@@ -1957,7 +1950,7 @@ port_please(_Name, _Ip) ->
%%% Utilities
timestamp() ->
- erlang:monotonic_time(milli_seconds).
+ erlang:monotonic_time(millisecond).
start_node(X) ->
start_node(X, [], []).
@@ -1972,7 +1965,7 @@ start_node(Name, Args, Rel) when is_atom(Name), is_list(Rel) ->
[] -> [];
_ -> [{erl,[{release,Rel}]}]
end,
- test_server:start_node(Name, slave,
+ test_server:start_node(Name, slave,
[{args,
Args++" -setcookie "++Cookie++" -pa \""++Pa++"\""}
| RelArg]);
@@ -1981,7 +1974,7 @@ start_node(Config, Args, Rel) when is_list(Config), is_list(Rel) ->
++ "-"
++ atom_to_list(proplists:get_value(testcase, Config))
++ "-"
- ++ integer_to_list(erlang:system_time(seconds))
+ ++ integer_to_list(erlang:system_time(second))
++ "-"
++ integer_to_list(erlang:unique_integer([positive])))),
start_node(Name, Args, Rel).
@@ -2040,17 +2033,15 @@ inet_rpc_server_loop(Sock) ->
start_relay_node(Node, Args) ->
Pa = filename:dirname(code:which(?MODULE)),
Cookie = "NOT"++atom_to_list(erlang:get_cookie()),
- {ok, LSock} = gen_tcp:listen(0, [binary, {packet, 4},
- {active, false}]),
+ {ok, LSock} = gen_tcp:listen(0, [binary, {packet, 4}, {active, false}]),
{ok, Port} = inet:port(LSock),
{ok, Host} = inet:gethostname(),
RunArg = "-run " ++ atom_to_list(?MODULE) ++ " inet_rpc_server " ++
Host ++ " " ++ integer_to_list(Port),
- {ok, NN} =
- test_server:start_node(Node, peer,
- [{args, Args ++
- " -setcookie "++Cookie++" -pa "++Pa++" "++
- RunArg}]),
+ {ok, NN} = test_server:start_node(Node, peer,
+ [{args, Args ++
+ " -setcookie "++Cookie++" -pa "++Pa++" "++
+ RunArg}]),
[N,H] = string:tokens(atom_to_list(NN),"@"),
{ok, Sock} = gen_tcp:accept(LSock),
pang = net_adm:ping(NN),
@@ -2066,7 +2057,7 @@ wait_dead(N,H,0) ->
wait_dead(N,H,X) ->
case erl_epmd:port_please(N,H) of
{port,_,_} ->
- receive
+ receive
after 1000 ->
ok
end,
@@ -2100,7 +2091,7 @@ node_monitor(Master) ->
Master ! {nodeup, node(), Node}
end,
Nodes0),
- io:format("~p ~p: ~p~n", [node(), erlang:system_time(micro_seconds), Nodes0]),
+ io:format("~p ~p: ~p~n", [node(), erlang:system_time(microsecond), Nodes0]),
node_monitor_loop(Master);
false ->
net_kernel:monitor_nodes(false, Opts),
@@ -2121,7 +2112,7 @@ node_monitor_loop(Master) ->
receive
{nodeup, Node, _InfoList} = Msg ->
Master ! {nodeup, node(), Node},
- io:format("~p ~p: ~p~n", [node(), erlang:system_time(micro_seconds), Msg]),
+ io:format("~p ~p: ~p~n", [node(), erlang:system_time(microsecond), Msg]),
node_monitor_loop(Master);
{nodedown, Node, InfoList} = Msg ->
Reason = case lists:keysearch(nodedown_reason, 1, InfoList) of
@@ -2129,7 +2120,7 @@ node_monitor_loop(Master) ->
_ -> undefined
end,
Master ! {nodedown, node(), Node, Reason},
- io:format("~p ~p: ~p~n", [node(), erlang:system_time(micro_seconds), Msg]),
+ io:format("~p ~p: ~p~n", [node(), erlang:system_time(microsecond), Msg]),
node_monitor_loop(Master)
end.
diff --git a/erts/emulator/test/driver_SUITE.erl b/erts/emulator/test/driver_SUITE.erl
index f134a197aa..2fbf6eae61 100644
--- a/erts/emulator/test/driver_SUITE.erl
+++ b/erts/emulator/test/driver_SUITE.erl
@@ -114,7 +114,7 @@
-define(MAX_DATA_SIZE, 16384).
% This is the allowed delay when testing the driver timer functionality
--define(delay, 100).
+-define(delay, 400).
-define(heap_binary_size, 64).
@@ -401,7 +401,7 @@ try_timeouts(Port, Timeout) ->
true ->
try_timeouts(Port, Timeout div 2)
end
- after Timeout + ?delay ->
+ after Timeout + 100*?delay ->
ct:fail("driver failed to timeout")
end.
@@ -437,7 +437,7 @@ try_cancel(Port, Timeout) ->
Timeout == 0 -> ok;
true -> try_cancel(Port, Timeout div 2)
end
- after ?delay ->
+ after 100*?delay ->
ct:fail("No message from driver")
end
end.
@@ -452,11 +452,7 @@ timer_delay(Config) when is_list(Config) ->
TimeBefore = erlang:monotonic_time(),
Timeout0 = 350,
erlang:port_command(Port, <<?DELAY_START_TIMER,Timeout0:32>>),
- Timeout = Timeout0 +
- case os:type() of
- {win32,_} -> 0; %Driver doesn't sleep on Windows.
- _ -> 1000
- end,
+ Timeout = Timeout0 + 1000,
receive
{Port,{data,[?TIMER]}} ->
Elapsed = erl_millisecs() - erl_millisecs(TimeBefore),
@@ -505,7 +501,7 @@ try_change_timer(Port, Timeout) ->
true ->
try_timeouts(Port, Timeout div 2)
end
- after Timeout + ?delay ->
+ after Timeout + 100*?delay ->
ct:fail("driver failed to timeout")
end.
@@ -2427,7 +2423,7 @@ erl_millisecs() ->
erl_millisecs(erlang:monotonic_time()).
erl_millisecs(MonotonicTime) ->
- (1000*MonotonicTime)/erlang:convert_time_unit(1,seconds,native).
+ (1000*MonotonicTime)/erlang:convert_time_unit(1,second,native).
%% Start/stop drivers.
start_driver(Config, Name, Binary) ->
@@ -2481,7 +2477,7 @@ start_node(Config) when is_list(Config) ->
++ "-"
++ atom_to_list(proplists:get_value(testcase, Config))
++ "-"
- ++ integer_to_list(erlang:system_time(seconds))
+ ++ integer_to_list(erlang:system_time(second))
++ "-"
++ integer_to_list(erlang:unique_integer([positive]))),
test_server:start_node(Name, slave, [{args, "-pa "++Pa}]).
diff --git a/erts/emulator/test/driver_SUITE_data/timer_drv.c b/erts/emulator/test/driver_SUITE_data/timer_drv.c
index 57538e0d57..c3ce3b6e49 100644
--- a/erts/emulator/test/driver_SUITE_data/timer_drv.c
+++ b/erts/emulator/test/driver_SUITE_data/timer_drv.c
@@ -1,5 +1,13 @@
#include <stdio.h>
#include "erl_driver.h"
+#ifdef __WIN32__
+# include <windows.h>
+#else
+# include <sys/time.h>
+# include <sys/types.h>
+# include <sys/select.h>
+# include <unistd.h>
+#endif
#define get_int32(s) ((((unsigned char*) (s))[0] << 24) | \
(((unsigned char*) (s))[1] << 16) | \
@@ -17,6 +25,7 @@ static ErlDrvData timer_start(ErlDrvPort, char*);
static void timer_stop(ErlDrvData);
static void timer_read(ErlDrvData, char*, ErlDrvSizeT);
static void timer(ErlDrvData);
+static void ms_sleep(int ms);
static ErlDrvEntry timer_driver_entry =
{
@@ -75,9 +84,7 @@ static void timer_read(ErlDrvData p, char *buf, ErlDrvSizeT len)
reply[0] = CANCELLED;
driver_output(port, reply, 1);
} else if (buf[0] == DELAY_START_TIMER) {
-#ifndef __WIN32__
- sleep(1);
-#endif
+ ms_sleep(1000);
driver_set_timer(port, get_int32(buf + 1));
}
}
@@ -95,3 +102,34 @@ static void timer(ErlDrvData port)
reply[0] = TIMER;
driver_output((ErlDrvPort)port, reply, 1);
}
+
+static void
+ms_sleep(int ms)
+{
+ /* Important that we do not return too early... */
+ ErlDrvTime time, timeout_time;
+
+ time = erl_drv_monotonic_time(ERL_DRV_USEC);
+
+ timeout_time = time + ((ErlDrvTime) ms)*1000;
+
+ while (time < timeout_time) {
+ ErlDrvTime timeout = timeout_time - time;
+
+#ifdef __WIN32__
+ Sleep((DWORD) (timeout / 1000));
+#else
+ {
+ struct timeval tv;
+
+ tv.tv_sec = (long) timeout / (1000*1000);
+ tv.tv_usec = (long) timeout % (1000*1000);
+
+ select(0, NULL, NULL, NULL, &tv);
+ }
+#endif
+
+ time = erl_drv_monotonic_time(ERL_DRV_USEC);
+ }
+
+}
diff --git a/erts/emulator/test/erl_link_SUITE.erl b/erts/emulator/test/erl_link_SUITE.erl
index 93d2065ba3..89e1aefb50 100644
--- a/erts/emulator/test/erl_link_SUITE.erl
+++ b/erts/emulator/test/erl_link_SUITE.erl
@@ -1011,7 +1011,7 @@ get_names(N, T, Acc) ->
++ "-"
++ atom_to_list(T)
++ "-"
- ++ integer_to_list(erlang:system_time(seconds))
+ ++ integer_to_list(erlang:system_time(second))
++ "-"
++ integer_to_list(erlang:unique_integer([positive]))) | Acc]).
diff --git a/erts/emulator/test/estone_SUITE.erl b/erts/emulator/test/estone_SUITE.erl
index 1180a45585..3ce849b88e 100644
--- a/erts/emulator/test/estone_SUITE.erl
+++ b/erts/emulator/test/estone_SUITE.erl
@@ -364,7 +364,7 @@ monotonic_time() ->
try erlang:monotonic_time() catch error:undef -> erlang:now() end.
subtr(Before, After) when is_integer(Before), is_integer(After) ->
- erlang:convert_time_unit(After-Before, native, micro_seconds);
+ erlang:convert_time_unit(After-Before, native, microsecond);
subtr({_,_,_}=Before, {_,_,_}=After) ->
timer:now_diff(After, Before).
diff --git a/erts/emulator/test/float_SUITE.erl b/erts/emulator/test/float_SUITE.erl
index e85addae3a..36b1f9179f 100644
--- a/erts/emulator/test/float_SUITE.erl
+++ b/erts/emulator/test/float_SUITE.erl
@@ -276,7 +276,7 @@ start_node(Config) when is_list(Config) ->
++ "-"
++ atom_to_list(proplists:get_value(testcase, Config))
++ "-"
- ++ integer_to_list(erlang:system_time(seconds))
+ ++ integer_to_list(erlang:system_time(second))
++ "-"
++ integer_to_list(erlang:unique_integer([positive]))),
test_server:start_node(Name, slave, [{args, "-pa "++Pa}]).
diff --git a/erts/emulator/test/hipe_SUITE.erl b/erts/emulator/test/hipe_SUITE.erl
new file mode 100644
index 0000000000..a556b4ddc0
--- /dev/null
+++ b/erts/emulator/test/hipe_SUITE.erl
@@ -0,0 +1,67 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2016. All Rights Reserved.
+%%
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+-module(hipe_SUITE).
+-export([all/0, t_copy_literals/1]).
+
+all() ->
+ case erlang:system_info(hipe_architecture) of
+ undefined -> {skip, "HiPE is disabled"};
+ _ -> [t_copy_literals]
+ end.
+
+t_copy_literals(doc) ->
+ "Check that BEAM literals referenced from HiPE stack are copied by"
+ " check_process_code";
+t_copy_literals(Config) when is_list(Config) ->
+ %% Compile the the ref_cell and literals modules.
+ Data = proplists:get_value(data_dir, Config),
+ Priv = proplists:get_value(priv_dir, Config),
+ RefFile = filename:join(Data, "ref_cell"),
+ {ok,ref_cell} = c:c(RefFile, [{outdir,Priv},native]),
+ true = code:is_module_native(ref_cell),
+ LitFile = filename:join(Data, "literals"),
+ {ok,literals} = c:c(LitFile, [{outdir,Priv}]),
+
+ %% store references to literals on HiPE stacks
+ PA = ref_cell:start_link(),
+ ref_cell:call(PA, {put_res_of, fun literals:a/0}),
+ PB = ref_cell:start_link_deep(),
+ ref_cell:call(PB, {put_res_of, fun literals:b/0}),
+
+ %% purge the literals
+ _ = (catch erlang:purge_module(literals)),
+ true = erlang:delete_module(literals),
+ true = erlang:purge_module(literals),
+
+ %% Give the literal collector some time to work...
+ receive after 2000 -> ok end,
+
+ %% check that the ex-literals are ok
+ [a,b,c] = ref_cell:call(PA, get),
+ {a,b,c} = ref_cell:call(PB, get),
+
+ %% cleanup
+ ref_cell:call(PA, done),
+ ref_cell:call(PB, done),
+ _ = (catch erlang:purge_module(ref_cell)),
+ true = erlang:delete_module(ref_cell),
+ true = erlang:purge_module(ref_cell),
+ ok.
diff --git a/erts/emulator/test/hipe_SUITE_data/literals.erl b/erts/emulator/test/hipe_SUITE_data/literals.erl
new file mode 100644
index 0000000000..31e443970f
--- /dev/null
+++ b/erts/emulator/test/hipe_SUITE_data/literals.erl
@@ -0,0 +1,26 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2016. All Rights Reserved.
+%%
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+-module(literals).
+
+-export([a/0, b/0]).
+
+a() -> [a,b,c].
+b() -> {a,b,c}.
diff --git a/erts/emulator/test/hipe_SUITE_data/ref_cell.erl b/erts/emulator/test/hipe_SUITE_data/ref_cell.erl
new file mode 100644
index 0000000000..2654e4077b
--- /dev/null
+++ b/erts/emulator/test/hipe_SUITE_data/ref_cell.erl
@@ -0,0 +1,64 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2016. All Rights Reserved.
+%%
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+-module(ref_cell).
+
+-export([start_link/0, start_link_deep/0, call/2]).
+
+-compile(native).
+
+-define(DEPTH, 100).
+-define(ALLOCS, 500).
+
+start_link() ->
+ spawn_link(fun() -> loop(undefined) end).
+
+start_link_deep() ->
+ spawn_link(fun() -> go_deep(?DEPTH) end).
+
+%% Create a stack large enough to get a graylimit trap placed next time there's
+%% a minor gc.
+go_deep(0) ->
+ alloc_some(?ALLOCS),
+ loop(undefined),
+ 0;
+go_deep(Depth) ->
+ go_deep(Depth-1)+1.
+
+%% Do some allocation to trigger a minor gc
+alloc_some(Amount) ->
+ Check = (Amount * (Amount + 1)) div 2,
+ Check = lists:sum(lists:seq(1, Amount)).
+
+call(Pid, Call) ->
+ Pid ! {Call, self()},
+ receive {Pid, Res} -> Res end.
+
+loop(Thing) ->
+ receive
+ {done, Pid} -> Pid ! {self(), done};
+ {{put_res_of, Fun}, Pid} ->
+ NewThing = Fun(),
+ Pid ! {self(), put},
+ loop(NewThing);
+ {get, Pid} ->
+ Pid ! {self(), Thing},
+ loop(Thing)
+ end.
diff --git a/erts/emulator/test/long_timers_test.erl b/erts/emulator/test/long_timers_test.erl
index 7c055a31f9..c9a380a229 100644
--- a/erts/emulator/test/long_timers_test.erl
+++ b/erts/emulator/test/long_timers_test.erl
@@ -27,11 +27,16 @@
%%% Created : 21 Aug 2006 by Rickard Green <[email protected]>
%%%-------------------------------------------------------------------
+-define(HIGH_CPU_INFO, "Ignored due to high CPU utilization.").
+-define(MISSING_CPU_INFO, "Ignored due to missing CPU utilization information.").
-define(MAX_TIMEOUT, 60). % Minutes
--define(MAX_LATE_MS, 15*1000). % Milliseconds
+-define(MAX_LATE_MS, 1000). % Milliseconds
-define(REG_NAME, '___LONG___TIMERS___TEST___SERVER___').
+-define(HIGH_UTIL, 96.0).
+-define(UTIL_INTERVAL, 10000).
+
-define(DRV_NAME, timer_driver).
% First byte in communication with the timer driver
@@ -72,52 +77,149 @@ check_result() ->
receive
{'DOWN', Mon, process, _, Reason} ->
{?REG_NAME, 'DOWN', Reason};
- {result, ?REG_NAME, TORs, Start, End} ->
+ {result, ?REG_NAME, TORs, Start, End, UtilData} ->
erlang:demonitor(Mon),
receive {'DOWN', Mon, _, _, _} -> ok after 0 -> ok end,
stop_node(Node),
- check(TORs, ms((End - Start) - max_late()), ok)
+ Res = check(TORs, Start, End, UtilData, ms((End - Start) - max_late()), ok),
+ io:format("Start = ~p~n End = ~p~n UtilData = ~p~n", [Start, End, UtilData]),
+ Res
end.
+res(New, Old) when New == failed; Old == failed ->
+ failed;
+res(New, Old) when New == missing_cpu_info; Old == missing_cpu_info ->
+ missing_cpu_info;
+res(New, Old) when New == high_cpu; Old == high_cpu ->
+ high_cpu;
+res(New, _Old) ->
+ New.
+
check([#timeout_rec{timeout = Timeout,
type = Type,
timeout_diff = undefined} | TORs],
+ Start,
+ End,
+ UtilData,
NeedRes,
- _Ok) when Timeout < NeedRes ->
- io:format("~p timeout = ~p ms failed! No timeout.~n",
- [Type, Timeout]),
- check(TORs, NeedRes, failed);
+ Ok) when Timeout < NeedRes ->
+ {NewOk, HCPU} = case had_high_cpu_util(Start,
+ Timeout,
+ End - Timeout*1000,
+ UtilData) of
+ yes -> {res(high_cpu, Ok), ?HIGH_CPU_INFO};
+ no -> {res(failed, Ok), ""};
+ missing -> {res(missing_cpu_info, Ok), "FAILED", ?MISSING_CPU_INFO}
+ end,
+ io:format("~p timeout = ~p ms FAILED! No timeout. ~s~n",
+ [Type, Timeout, HCPU]),
+ check(TORs, Start, End, UtilData, NeedRes, NewOk);
check([#timeout_rec{timeout_diff = undefined} | TORs],
+ Start,
+ End,
+ UtilData,
NeedRes,
Ok) ->
- check(TORs, NeedRes, Ok);
+ check(TORs, Start, End, UtilData, NeedRes, Ok);
check([#timeout_rec{timeout = Timeout,
type = Type,
timeout_diff = {error, Reason}} | TORs],
+ Start,
+ End,
+ UtilData,
NeedRes,
_Ok) ->
- io:format("~p timeout = ~p ms failed! exit reason ~p~n",
+ io:format("~p timeout = ~p ms FAILED! exit reason ~p~n",
[Type, Timeout, Reason]),
- check(TORs, NeedRes, failed);
+ check(TORs, Start, End, UtilData, NeedRes, failed);
check([#timeout_rec{timeout = Timeout,
type = Type,
timeout_diff = TimeoutDiff} | TORs],
+ Start,
+ End,
+ UtilData,
NeedRes,
Ok) ->
- {NewOk, SuccessStr} = case ((0 =< TimeoutDiff)
- andalso (TimeoutDiff =< max_late())) of
- true -> {Ok, "succeeded"};
- false -> {failed, "FAILED"}
+ {NewOk, SuccessStr, HCPU} = case {(0 =< TimeoutDiff),
+ (TimeoutDiff =< max_late())} of
+ {true, true} ->
+ {res(ok, Ok), "succeeded", ""};
+ {false, _} ->
+ {res(failed, Ok), "FAILED", ""};
+ _ ->
+ case had_high_cpu_util(Start,
+ Timeout,
+ TimeoutDiff,
+ UtilData) of
+ yes -> {res(high_cpu, Ok), "FAILED", ?HIGH_CPU_INFO};
+ no -> {res(failed, Ok), "FAILED", ""};
+ missing -> {res(missing_cpu_info, Ok), "FAILED", ?MISSING_CPU_INFO}
+ end
end,
- io:format("~s timeout = ~s ms ~s! timeout diff = ~s.~n",
+ io:format("~s timeout = ~s ms ~s! timeout diff = ~s. ~s~n",
[type_str(Type),
time_str(Timeout),
SuccessStr,
- time_str(TimeoutDiff, erlang:convert_time_unit(1, seconds, native))]),
- check(TORs, NeedRes, NewOk);
-check([], _NeedRes, Ok) ->
+ time_str(TimeoutDiff, 1000000),
+ HCPU]),
+ check(TORs, Start, End, UtilData, NeedRes, NewOk);
+check([],_Start,_End,_UtilData,_NeedRes, Ok) ->
Ok.
+% TargetTimeout in ms, other in us.
+had_high_cpu_util(StartTime,
+ TargetTimeout,
+ TimeoutDiff,
+ UtilData) ->
+ TargetTo = StartTime + TargetTimeout*1000,
+ ActTo = TargetTo + TimeoutDiff,
+ hcpu(ActTo, TargetTo, UtilData).
+
+hcpu(_ActTo, _TargetTo, [{UT, 0} | _] = UD) ->
+ missing; %% Util is the integer zero when not supported...
+%% UT2 =:= UT1
+hcpu(ActTo, TargetTo, [{UT, _}, {UT, _} | _] = UD) ->
+ hcpu(ActTo, TargetTo, tl(UD));
+%% UT2 > UT1 > ActTo > TargetTo
+hcpu(ActTo, TargetTo, [{_UT2, _}, {UT1, _} | _] = UD) when UT1 > ActTo ->
+ hcpu(ActTo, TargetTo, tl(UD));
+%% UT2 >= ActTo > TargetTo >= UT1
+hcpu(ActTo, TargetTo,
+ [{UT2, U}, {UT1, _} | _]) when UT2 >= ActTo,
+ TargetTo >= UT1 ->
+ case U >= (((ActTo - TargetTo) / (UT2 - UT1))
+ * (?HIGH_UTIL/100.0)) of
+ true -> yes;
+ false -> no
+ end;
+%% UT2 >= ActTo >= UT1 > TargetTo
+hcpu(ActTo, TargetTo,
+ [{UT2, U}, {UT1, _} | _] = UD) when UT2 >= ActTo,
+ ActTo >= UT1,
+ UT1 > TargetTo ->
+ case U >= (((ActTo - UT1) / (UT2 - UT1))
+ * (?HIGH_UTIL/100.0)) of
+ true -> hcpu(ActTo, TargetTo, tl(UD));
+ false -> no
+ end;
+%% ActTo > UT2 >= TargetTo >= UT1
+hcpu(ActTo, TargetTo,
+ [{UT2, U}, {UT1, _} | _]) when ActTo > UT2,
+ TargetTo >= UT1 ->
+ case U >= (((UT2 - TargetTo) / (UT2 - UT1))
+ * (?HIGH_UTIL/100.0)) of
+ true -> yes;
+ false -> no
+ end;
+%% ActTo > UT2 > UT1 > TargetTo
+hcpu(ActTo, TargetTo,
+ [{UT2, U}, {UT1, _} | _] = UD) when ActTo > UT2,
+ UT1 > TargetTo ->
+ case U >= ?HIGH_UTIL of
+ true -> hcpu(ActTo, TargetTo, tl(UD));
+ false -> no
+ end.
+
type_str(receive_after) -> "receive ... after";
type_str(bif_timer) -> "BIF timer";
type_str(driver) -> "driver".
@@ -142,24 +244,24 @@ unit_str(Res) -> Res.
to_diff(Timeout, Start, Stop) ->
%% 'Timeout' in milli seconds
- %% 'Start', 'Stop', and result in native unit
- (Stop - Start) - erlang:convert_time_unit(Timeout, milli_seconds, native).
+ %% 'Start', 'Stop', and result in micro seconds
+ (Stop - Start) - Timeout*1000.
ms(Time) ->
- erlang:convert_time_unit(Time, native, milli_seconds).
+ erlang:convert_time_unit(Time, microsecond, millisecond).
max_late() ->
- erlang:convert_time_unit(?MAX_LATE_MS, milli_seconds, native).
+ erlang:convert_time_unit(?MAX_LATE_MS, millisecond, microsecond).
receive_after(Timeout) ->
- Start = erlang:monotonic_time(),
+ Start = erlang:monotonic_time(microsecond),
receive
{get_result, ?REG_NAME} ->
?REG_NAME ! #timeout_rec{pid = self(),
type = receive_after,
timeout = Timeout}
after Timeout ->
- Stop = erlang:monotonic_time(),
+ Stop = erlang:monotonic_time(microsecond),
receive
{get_result, ?REG_NAME} ->
?REG_NAME ! #timeout_rec{pid = self(),
@@ -174,7 +276,7 @@ receive_after(Timeout) ->
driver(Timeout) ->
Port = open_port({spawn, ?DRV_NAME},[]),
link(Port),
- Start = erlang:monotonic_time(),
+ Start = erlang:monotonic_time(microsecond),
erlang:port_command(Port, <<?START_TIMER, Timeout:32>>),
receive
{get_result, ?REG_NAME} ->
@@ -182,7 +284,7 @@ driver(Timeout) ->
type = driver,
timeout = Timeout};
{Port,{data,[?TIMER]}} ->
- Stop = erlang:monotonic_time(),
+ Stop = erlang:monotonic_time(microsecond),
unlink(Port),
true = erlang:port_close(Port),
receive
@@ -197,7 +299,7 @@ driver(Timeout) ->
end.
bif_timer(Timeout) ->
- Start = erlang:monotonic_time(),
+ Start = erlang:monotonic_time(microsecond),
Tmr = erlang:start_timer(Timeout, self(), ok),
receive
{get_result, ?REG_NAME} ->
@@ -205,7 +307,7 @@ bif_timer(Timeout) ->
type = bif_timer,
timeout = Timeout};
{timeout, Tmr, ok} ->
- Stop = erlang:monotonic_time(),
+ Stop = erlang:monotonic_time(microsecond),
receive
{get_result, ?REG_NAME} ->
?REG_NAME ! #timeout_rec{pid = self(),
@@ -218,13 +320,22 @@ bif_timer(Timeout) ->
end.
test(Starter, DrvDir, StartDone) ->
+ process_flag(priority, high),
erl_ddll:start(),
ok = load_driver(DrvDir, ?DRV_NAME),
process_flag(trap_exit, true),
register(?REG_NAME, self()),
{group_leader, GL} = process_info(whereis(net_kernel),group_leader),
group_leader(GL, self()),
- Start = erlang:monotonic_time(),
+ try
+ application:start(sasl),
+ application:start(os_mon)
+ catch
+ _ : _ ->
+ ok
+ end,
+ UtilData = new_util(),
+ Start = erlang:monotonic_time(microsecond),
TORs = lists:map(fun (Min) ->
TO = Min*60*1000,
[#timeout_rec{pid = spawn_opt(
@@ -252,16 +363,27 @@ test(Starter, DrvDir, StartDone) ->
lists:seq(1, ?MAX_TIMEOUT)),
FlatTORs = lists:flatten(TORs),
Starter ! StartDone,
- test_loop(FlatTORs, Start).
+ test_loop(FlatTORs, Start, UtilData).
+
+new_util() ->
+ new_util([]).
+
+new_util(UtilData) ->
+ Util = cpu_sup:util(),
+ Time = erlang:monotonic_time(microsecond),
+ [{Time, Util} | UtilData].
-test_loop(TORs, Start) ->
+test_loop(TORs, Start, UtilData) ->
receive
{get_result, ?REG_NAME, Pid} ->
- End = erlang:monotonic_time(),
- Pid ! {result, ?REG_NAME, get_test_results(TORs), Start, End},
+ End = erlang:monotonic_time(microsecond),
+ EndUtilData = new_util(UtilData),
+ Pid ! {result, ?REG_NAME, get_test_results(TORs), Start, End, EndUtilData},
erl_ddll:unload_driver(?DRV_NAME),
erl_ddll:stop(),
exit(bye)
+ after ?UTIL_INTERVAL ->
+ test_loop(TORs, Start, new_util(UtilData))
end.
get_test_results(TORs) ->
diff --git a/erts/emulator/test/map_SUITE.erl b/erts/emulator/test/map_SUITE.erl
index b3870f0313..5af676c409 100644
--- a/erts/emulator/test/map_SUITE.erl
+++ b/erts/emulator/test/map_SUITE.erl
@@ -77,6 +77,7 @@
t_ets/1,
t_dets/1,
t_tracing/1,
+ t_hash_entropy/1,
%% instruction-level tests
t_has_map_fields/1,
@@ -140,6 +141,7 @@ all() -> [t_build_and_match_literals, t_build_and_match_literals_large,
t_pdict,
t_ets,
t_tracing,
+ t_hash_entropy,
%% instruction-level tests
t_has_map_fields,
@@ -3020,6 +3022,39 @@ do_badmap_17(Config) ->
id(I) -> I.
+%% OTP-13763
+t_hash_entropy(Config) when is_list(Config) ->
+ %% entropy bug in 18.3, 19.0
+ M1 = maps:from_list([{#{"id" => I}, ok}||I <- lists:seq(1,50000)]),
+
+ #{ #{"id" => 100} := ok,
+ #{"id" => 200} := ok,
+ #{"id" => 300} := ok,
+ #{"id" => 400} := ok,
+ #{"id" => 500} := ok,
+ #{"id" => 600} := ok,
+ #{"id" => 700} := ok,
+ #{"id" => 800} := ok,
+ #{"id" => 900} := ok,
+ #{"id" => 25061} := ok,
+ #{"id" => 39766} := ok } = M1,
+
+ M0 = maps:from_list([{I,ok}||I <- lists:seq(1,33)]),
+ M2 = maps:from_list([{M0#{"id" => I}, ok}||I <- lists:seq(1,50000)]),
+
+ ok = maps:get(M0#{"id" => 100}, M2),
+ ok = maps:get(M0#{"id" => 200}, M2),
+ ok = maps:get(M0#{"id" => 300}, M2),
+ ok = maps:get(M0#{"id" => 400}, M2),
+ ok = maps:get(M0#{"id" => 500}, M2),
+ ok = maps:get(M0#{"id" => 600}, M2),
+ ok = maps:get(M0#{"id" => 700}, M2),
+ ok = maps:get(M0#{"id" => 800}, M2),
+ ok = maps:get(M0#{"id" => 900}, M2),
+ ok = maps:get(M0#{"id" => 25061}, M2),
+ ok = maps:get(M0#{"id" => 39766}, M2),
+ ok.
+
%% OTP-13146
%% Provoke major GC with a lot of "fat" maps on external format in msg queue
%% causing heap fragments to be allocated.
diff --git a/erts/emulator/test/message_queue_data_SUITE.erl b/erts/emulator/test/message_queue_data_SUITE.erl
index 44e77dfad0..e084b9482d 100644
--- a/erts/emulator/test/message_queue_data_SUITE.erl
+++ b/erts/emulator/test/message_queue_data_SUITE.erl
@@ -200,7 +200,7 @@ start_node(Config, Opts) when is_list(Config), is_list(Opts) ->
++ "-"
++ atom_to_list(proplists:get_value(testcase, Config))
++ "-"
- ++ integer_to_list(erlang:system_time(seconds))
+ ++ integer_to_list(erlang:system_time(second))
++ "-"
++ integer_to_list(erlang:unique_integer([positive]))),
test_server:start_node(Name, slave, [{args, Opts++" -pa "++Pa}]).
diff --git a/erts/emulator/test/monitor_SUITE.erl b/erts/emulator/test/monitor_SUITE.erl
index 90d2bd8c5d..827ed817cc 100644
--- a/erts/emulator/test/monitor_SUITE.erl
+++ b/erts/emulator/test/monitor_SUITE.erl
@@ -697,7 +697,7 @@ mixer(Config) when is_list(Config) ->
named_down(Config) when is_list(Config) ->
Name = list_to_atom(atom_to_list(?MODULE)
++ "-named_down-"
- ++ integer_to_list(erlang:system_time(seconds))
+ ++ integer_to_list(erlang:system_time(second))
++ "-" ++ integer_to_list(erlang:unique_integer([positive]))),
Prio = process_flag(priority,high),
%% Spawn a bunch of high prio cpu bound processes to prevent
diff --git a/erts/emulator/test/mtx_SUITE.erl b/erts/emulator/test/mtx_SUITE.erl
index 1493e52655..12928ed6d8 100644
--- a/erts/emulator/test/mtx_SUITE.erl
+++ b/erts/emulator/test/mtx_SUITE.erl
@@ -443,7 +443,7 @@ hammer_ets_rwlock_test(XOpts, UW, C, N, NP, SC) ->
{'DOWN', M, process, P, _} -> ok
end
end, Ps),
- Res = (Stop-Start)/erlang:convert_time_unit(1,seconds,native),
+ Res = (Stop-Start)/erlang:convert_time_unit(1,second,native),
Caller ! {?MODULE, self(), Res}
end,
TP = spawn_link(T),
diff --git a/erts/emulator/test/nif_SUITE.erl b/erts/emulator/test/nif_SUITE.erl
index a0e9f1bad6..9c1694fa8a 100644
--- a/erts/emulator/test/nif_SUITE.erl
+++ b/erts/emulator/test/nif_SUITE.erl
@@ -29,6 +29,7 @@
-export([all/0, suite/0,
init_per_testcase/2, end_per_testcase/2,
basic/1, reload/1, upgrade/1, heap_frag/1,
+ t_on_load/1,
types/1, many_args/1, binaries/1, get_string/1, get_atom/1,
maps/1,
api_macros/1,
@@ -68,6 +69,7 @@ suite() -> [{ct_hooks,[ts_install_cth]}].
all() ->
[basic, reload, upgrade, heap_frag, types, many_args,
+ t_on_load,
binaries, get_string, get_atom, maps, api_macros, from_array,
iolist_as_binary, resource, resource_binary,
resource_takeover, threading, send, send2, send3,
@@ -83,10 +85,19 @@ all() ->
nif_port_command,
nif_snprintf].
+init_per_testcase(t_on_load, Config) ->
+ ets:new(nif_SUITE, [named_table]),
+ Config;
init_per_testcase(_Case, Config) ->
Config.
+end_per_testcase(t_on_load, _Config) ->
+ ets:delete(nif_SUITE),
+ testcase_cleanup();
end_per_testcase(_Func, _Config) ->
+ testcase_cleanup().
+
+testcase_cleanup() ->
P1 = code:purge(nif_mod),
Del = code:delete(nif_mod),
P2 = code:purge(nif_mod),
@@ -174,11 +185,33 @@ upgrade(Config) when is_list(Config) ->
true = erlang:delete_module(nif_mod),
[] = nif_mod_call_history(),
+ %% Repeat upgrade again but from old (deleted) instance
+ {module,nif_mod} = erlang:load_module(nif_mod,Bin),
+ undefined = nif_mod:lib_version(),
+ 1 = call(Pid,lib_version),
+ [{lib_version,1,9,109}] = nif_mod_call_history(),
+
+ ok = nif_mod:load_nif_lib(Config, 1),
+ 1 = nif_mod:lib_version(),
+ [{upgrade,1,10,110},{lib_version,1,11,111}] = nif_mod_call_history(),
+
+ upgraded = call(Pid,upgrade),
+ false = check_process_code(Pid, nif_mod),
+ true = erlang:purge_module(nif_mod),
+ [{unload,1,12,112}] = nif_mod_call_history(),
+
+ 1 = nif_mod:lib_version(),
+ [{lib_version,1,13,113}] = nif_mod_call_history(),
+
+ true = erlang:delete_module(nif_mod),
+ [] = nif_mod_call_history(),
+
+
Pid ! die,
{'DOWN', MRef, process, Pid, normal} = receive_any(),
false = check_process_code(Pid, nif_mod),
true = erlang:purge_module(nif_mod),
- [{unload,1,9,109}] = nif_mod_call_history(),
+ [{unload,1,14,114}] = nif_mod_call_history(),
%% Module upgrade with different lib version
{module,nif_mod} = erlang:load_module(nif_mod,Bin),
@@ -215,17 +248,170 @@ upgrade(Config) when is_list(Config) ->
true = erlang:delete_module(nif_mod),
[] = nif_mod_call_history(),
+
+ %% Reverse upgrade but from old (deleted) instance
+ {module,nif_mod} = erlang:load_module(nif_mod,Bin),
+ undefined = nif_mod:lib_version(),
+ [] = nif_mod_call_history(),
+ 2 = call(Pid2,lib_version),
+ [{lib_version,2,4,204}] = nif_mod_call_history(),
+
+ ok = nif_mod:load_nif_lib(Config, 1),
+ 1 = nif_mod:lib_version(),
+ [{upgrade,1,1,101},{lib_version,1,2,102}] = nif_mod_call_history(),
+
+ 2 = call(Pid2,lib_version),
+ [{lib_version,2,5,205}] = nif_mod_call_history(),
+
+ upgraded = call(Pid2,upgrade),
+ false = check_process_code(Pid2, nif_mod),
+ true = erlang:purge_module(nif_mod),
+ [{unload,2,6,206}] = nif_mod_call_history(),
+
+ 1 = nif_mod:lib_version(),
+ [{lib_version,1,3,103}] = nif_mod_call_history(),
+
+ true = erlang:delete_module(nif_mod),
+ [] = nif_mod_call_history(),
+
+
Pid2 ! die,
{'DOWN', MRef2, process, Pid2, normal} = receive_any(),
false= check_process_code(Pid2, nif_mod),
true = erlang:purge_module(nif_mod),
- [{unload,2,4,204}] = nif_mod_call_history(),
+ [{unload,1,4,104}] = nif_mod_call_history(),
true = lists:member(?MODULE, erlang:system_info(taints)),
true = lists:member(nif_mod, erlang:system_info(taints)),
verify_tmpmem(TmpMem),
ok.
+%% Test loading/upgrade in on_load
+t_on_load(Config) when is_list(Config) ->
+ TmpMem = tmpmem(),
+ ensure_lib_loaded(Config),
+
+ Data = proplists:get_value(data_dir, Config),
+ File = filename:join(Data, "nif_mod"),
+ {ok,nif_mod,Bin} = compile:file(File, [binary,return_errors,
+ {d,'USE_ON_LOAD'}]),
+
+ %% Use ETS to tell nif_mod:on_load what to do
+ ets:insert(nif_SUITE, {data_dir, Data}),
+ ets:insert(nif_SUITE, {lib_version, 1}),
+ {module,nif_mod} = code:load_binary(nif_mod,File,Bin),
+ hold_nif_mod_priv_data(nif_mod:get_priv_data_ptr()),
+ [{load,1,1,101},{get_priv_data_ptr,1,2,102}] = nif_mod_call_history(),
+
+ {Pid,MRef} = nif_mod:start(),
+ 1 = call(Pid,lib_version),
+ [{lib_version,1,3,103}] = nif_mod_call_history(),
+
+ %% Module upgrade with same lib-version
+ {module,nif_mod} = code:load_binary(nif_mod,File,Bin),
+ 1 = nif_mod:lib_version(),
+ 1 = call(Pid,lib_version),
+ [{upgrade,1,4,104},{lib_version,1,5,105},{lib_version,1,6,106}] = nif_mod_call_history(),
+
+ upgraded = call(Pid,upgrade),
+ false = check_process_code(Pid, nif_mod),
+ true = code:soft_purge(nif_mod),
+ [{unload,1,7,107}] = nif_mod_call_history(),
+
+ 1 = nif_mod:lib_version(),
+ [{lib_version,1,8,108}] = nif_mod_call_history(),
+
+ true = code:delete(nif_mod),
+ [] = nif_mod_call_history(),
+
+ %% Repeat upgrade again but from old (deleted) instance
+ {module,nif_mod} = code:load_binary(nif_mod,File,Bin),
+ [{upgrade,1,9,109}] = nif_mod_call_history(),
+ 1 = nif_mod:lib_version(),
+ 1 = call(Pid,lib_version),
+ [{lib_version,1,10,110},{lib_version,1,11,111}] = nif_mod_call_history(),
+
+ upgraded = call(Pid,upgrade),
+ false = check_process_code(Pid, nif_mod),
+ true = code:soft_purge(nif_mod),
+ [{unload,1,12,112}] = nif_mod_call_history(),
+
+ 1 = nif_mod:lib_version(),
+ [{lib_version,1,13,113}] = nif_mod_call_history(),
+
+ true = code:delete(nif_mod),
+ [] = nif_mod_call_history(),
+
+
+ Pid ! die,
+ {'DOWN', MRef, process, Pid, normal} = receive_any(),
+ false = check_process_code(Pid, nif_mod),
+ true = code:soft_purge(nif_mod),
+ [{unload,1,14,114}] = nif_mod_call_history(),
+
+ %% Module upgrade with different lib version
+ {module,nif_mod} = code:load_binary(nif_mod,File,Bin),
+ hold_nif_mod_priv_data(nif_mod:get_priv_data_ptr()),
+ [{load,1,1,101},{get_priv_data_ptr,1,2,102}] = nif_mod_call_history(),
+
+ 1 = nif_mod:lib_version(),
+ {Pid2,MRef2} = nif_mod:start(),
+ 1 = call(Pid2,lib_version),
+ [{lib_version,1,3,103},{lib_version,1,4,104}] = nif_mod_call_history(),
+
+ true = ets:insert(nif_SUITE,{lib_version,2}),
+ {module,nif_mod} = code:load_binary(nif_mod,File,Bin),
+ [{upgrade,2,1,201}] = nif_mod_call_history(),
+
+ 2 = nif_mod:lib_version(),
+ 1 = call(Pid2,lib_version),
+ [{lib_version,2,2,202},{lib_version,1,5,105}] = nif_mod_call_history(),
+
+ upgraded = call(Pid2,upgrade),
+ false = check_process_code(Pid2, nif_mod),
+ true = code:soft_purge(nif_mod),
+ [{unload,1,6,106}] = nif_mod_call_history(),
+
+ 2 = nif_mod:lib_version(),
+ 2 = call(Pid2,lib_version),
+ [{lib_version,2,3,203},{lib_version,2,4,204}] = nif_mod_call_history(),
+
+ true = code:delete(nif_mod),
+ [] = nif_mod_call_history(),
+
+ %% Reverse upgrade but from old (deleted) instance
+ ets:insert(nif_SUITE,{lib_version,1}),
+ {module,nif_mod} = code:load_binary(nif_mod,File,Bin),
+ [{upgrade,1,1,101}] = nif_mod_call_history(),
+
+ 1 = nif_mod:lib_version(),
+ 2 = call(Pid2,lib_version),
+ [{lib_version,1,2,102},{lib_version,2,5,205}] = nif_mod_call_history(),
+
+ upgraded = call(Pid2,upgrade),
+ false = check_process_code(Pid2, nif_mod),
+ true = code:soft_purge(nif_mod),
+ [{unload,2,6,206}] = nif_mod_call_history(),
+
+ 1 = nif_mod:lib_version(),
+ [{lib_version,1,3,103}] = nif_mod_call_history(),
+
+ true = code:delete(nif_mod),
+ [] = nif_mod_call_history(),
+
+
+ Pid2 ! die,
+ {'DOWN', MRef2, process, Pid2, normal} = receive_any(),
+ false= check_process_code(Pid2, nif_mod),
+ true = code:soft_purge(nif_mod),
+ [{unload,1,4,104}] = nif_mod_call_history(),
+
+ true = lists:member(?MODULE, erlang:system_info(taints)),
+ true = lists:member(nif_mod, erlang:system_info(taints)),
+ verify_tmpmem(TmpMem),
+ ok.
+
+
%% Test NIF building heap fragments
heap_frag(Config) when is_list(Config) ->
TmpMem = tmpmem(),
@@ -1443,6 +1629,17 @@ otp_9828_loop(Bin, Val) ->
consume_timeslice(Config) when is_list(Config) ->
+ case {erlang:system_info(debug_compiled),
+ erlang:system_info(lock_checking)} of
+ {false, false} ->
+ consume_timeslice_test(Config);
+ {false, true} ->
+ {skipped, "Lock checking enabled"};
+ _ ->
+ {skipped, "Debug compiled"}
+ end.
+
+consume_timeslice_test(Config) when is_list(Config) ->
CONTEXT_REDS = 2000,
Me = self(),
Go = make_ref(),
@@ -1692,7 +1889,7 @@ nif_raise_exceptions(NifFunc) ->
end, ok, ExcTerms).
-define(ERL_NIF_TIME_ERROR, -9223372036854775808).
--define(TIME_UNITS, [seconds, milli_seconds, micro_seconds, nano_seconds]).
+-define(TIME_UNITS, [second, millisecond, microsecond, nanosecond]).
nif_monotonic_time(Config) ->
?ERL_NIF_TIME_ERROR = monotonic_time(invalid_time_unit),
@@ -1758,8 +1955,8 @@ chk_toffs([TU|TUs]) ->
chk_toffs(TUs).
nif_convert_time_unit(Config) ->
- ?ERL_NIF_TIME_ERROR = convert_time_unit(0, seconds, invalid_time_unit),
- ?ERL_NIF_TIME_ERROR = convert_time_unit(0, invalid_time_unit, seconds),
+ ?ERL_NIF_TIME_ERROR = convert_time_unit(0, second, invalid_time_unit),
+ ?ERL_NIF_TIME_ERROR = convert_time_unit(0, invalid_time_unit, second),
?ERL_NIF_TIME_ERROR = convert_time_unit(0, invalid_time_unit, invalid_time_unit),
lists:foreach(fun (Offset) ->
lists:foreach(fun (Diff) ->
@@ -1808,7 +2005,7 @@ nif_convert_time_unit(Config) ->
ctu_loop(0) ->
ok;
ctu_loop(N) ->
- chk_ctu(erlang:monotonic_time(nano_seconds)),
+ chk_ctu(erlang:monotonic_time(nanosecond)),
ctu_loop(N-1).
chk_ctu(Time) ->
@@ -1823,7 +2020,7 @@ chk_ctu(Time, [FromTU|FromTUs]) ->
chk_ctu(_Time, _FromTU, []) ->
ok;
chk_ctu(Time, FromTU, [ToTU|ToTUs]) ->
- T = erlang:convert_time_unit(Time, nano_seconds, FromTU),
+ T = erlang:convert_time_unit(Time, nanosecond, FromTU),
TE = erlang:convert_time_unit(T, FromTU, ToTU),
TN = convert_time_unit(T, FromTU, ToTU),
case TE =:= TN of
diff --git a/erts/emulator/test/nif_SUITE_data/nif_SUITE.c b/erts/emulator/test/nif_SUITE_data/nif_SUITE.c
index 13846244d4..f2b1ef9d24 100644
--- a/erts/emulator/test/nif_SUITE_data/nif_SUITE.c
+++ b/erts/emulator/test/nif_SUITE_data/nif_SUITE.c
@@ -38,10 +38,10 @@ static ERL_NIF_TERM atom_self;
static ERL_NIF_TERM atom_ok;
static ERL_NIF_TERM atom_join;
static ERL_NIF_TERM atom_binary_resource_type;
-static ERL_NIF_TERM atom_seconds;
-static ERL_NIF_TERM atom_milli_seconds;
-static ERL_NIF_TERM atom_micro_seconds;
-static ERL_NIF_TERM atom_nano_seconds;
+static ERL_NIF_TERM atom_second;
+static ERL_NIF_TERM atom_millisecond;
+static ERL_NIF_TERM atom_microsecond;
+static ERL_NIF_TERM atom_nanosecond;
typedef struct
@@ -147,10 +147,10 @@ static int load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info)
atom_ok = enif_make_atom(env,"ok");
atom_join = enif_make_atom(env,"join");
atom_binary_resource_type = enif_make_atom(env,"binary_resource_type");
- atom_seconds = enif_make_atom(env,"seconds");
- atom_milli_seconds = enif_make_atom(env,"milli_seconds");
- atom_micro_seconds = enif_make_atom(env,"micro_seconds");
- atom_nano_seconds = enif_make_atom(env,"nano_seconds");
+ atom_second = enif_make_atom(env,"second");
+ atom_millisecond = enif_make_atom(env,"millisecond");
+ atom_microsecond = enif_make_atom(env,"microsecond");
+ atom_nanosecond = enif_make_atom(env,"nanosecond");
*priv_data = data;
return 0;
@@ -1808,13 +1808,13 @@ static ERL_NIF_TERM monotonic_time(ErlNifEnv* env, int argc, const ERL_NIF_TERM
if (argc != 1)
return atom_false;
- if (enif_compare(argv[0], atom_seconds) == 0)
+ if (enif_compare(argv[0], atom_second) == 0)
time_unit = ERL_NIF_SEC;
- else if (enif_compare(argv[0], atom_milli_seconds) == 0)
+ else if (enif_compare(argv[0], atom_millisecond) == 0)
time_unit = ERL_NIF_MSEC;
- else if (enif_compare(argv[0], atom_micro_seconds) == 0)
+ else if (enif_compare(argv[0], atom_microsecond) == 0)
time_unit = ERL_NIF_USEC;
- else if (enif_compare(argv[0], atom_nano_seconds) == 0)
+ else if (enif_compare(argv[0], atom_nanosecond) == 0)
time_unit = ERL_NIF_NSEC;
else
time_unit = 4711; /* invalid time unit */
@@ -1829,13 +1829,13 @@ static ERL_NIF_TERM time_offset(ErlNifEnv* env, int argc, const ERL_NIF_TERM arg
if (argc != 1)
return atom_false;
- if (enif_compare(argv[0], atom_seconds) == 0)
+ if (enif_compare(argv[0], atom_second) == 0)
time_unit = ERL_NIF_SEC;
- else if (enif_compare(argv[0], atom_milli_seconds) == 0)
+ else if (enif_compare(argv[0], atom_millisecond) == 0)
time_unit = ERL_NIF_MSEC;
- else if (enif_compare(argv[0], atom_micro_seconds) == 0)
+ else if (enif_compare(argv[0], atom_microsecond) == 0)
time_unit = ERL_NIF_USEC;
- else if (enif_compare(argv[0], atom_nano_seconds) == 0)
+ else if (enif_compare(argv[0], atom_nanosecond) == 0)
time_unit = ERL_NIF_NSEC;
else
time_unit = 4711; /* invalid time unit */
@@ -1856,24 +1856,24 @@ static ERL_NIF_TERM convert_time_unit(ErlNifEnv* env, int argc, const ERL_NIF_TE
val = (ErlNifTime) i64;
- if (enif_compare(argv[1], atom_seconds) == 0)
+ if (enif_compare(argv[1], atom_second) == 0)
from = ERL_NIF_SEC;
- else if (enif_compare(argv[1], atom_milli_seconds) == 0)
+ else if (enif_compare(argv[1], atom_millisecond) == 0)
from = ERL_NIF_MSEC;
- else if (enif_compare(argv[1], atom_micro_seconds) == 0)
+ else if (enif_compare(argv[1], atom_microsecond) == 0)
from = ERL_NIF_USEC;
- else if (enif_compare(argv[1], atom_nano_seconds) == 0)
+ else if (enif_compare(argv[1], atom_nanosecond) == 0)
from = ERL_NIF_NSEC;
else
from = 4711; /* invalid time unit */
- if (enif_compare(argv[2], atom_seconds) == 0)
+ if (enif_compare(argv[2], atom_second) == 0)
to = ERL_NIF_SEC;
- else if (enif_compare(argv[2], atom_milli_seconds) == 0)
+ else if (enif_compare(argv[2], atom_millisecond) == 0)
to = ERL_NIF_MSEC;
- else if (enif_compare(argv[2], atom_micro_seconds) == 0)
+ else if (enif_compare(argv[2], atom_microsecond) == 0)
to = ERL_NIF_USEC;
- else if (enif_compare(argv[2], atom_nano_seconds) == 0)
+ else if (enif_compare(argv[2], atom_nanosecond) == 0)
to = ERL_NIF_NSEC;
else
to = 4711; /* invalid time unit */
diff --git a/erts/emulator/test/nif_SUITE_data/nif_mod.erl b/erts/emulator/test/nif_SUITE_data/nif_mod.erl
index eec1bb8858..1fcc33faa4 100644
--- a/erts/emulator/test/nif_SUITE_data/nif_mod.erl
+++ b/erts/emulator/test/nif_SUITE_data/nif_mod.erl
@@ -29,6 +29,16 @@
-define(nif_stub,nif_stub_error(?LINE)).
+-ifdef(USE_ON_LOAD).
+-on_load(on_load/0).
+
+on_load() ->
+ [{data_dir, Path}] = ets:lookup(nif_SUITE, data_dir),
+ [{lib_version, Ver}] = ets:lookup(nif_SUITE, lib_version),
+ erlang:load_nif(filename:join(Path,libname(Ver)), []).
+
+-endif.
+
load_nif_lib(Config, Ver) ->
load_nif_lib(Config, Ver, []).
diff --git a/erts/emulator/test/node_container_SUITE.erl b/erts/emulator/test/node_container_SUITE.erl
index 536c91d4ae..af18545bff 100644
--- a/erts/emulator/test/node_container_SUITE.erl
+++ b/erts/emulator/test/node_container_SUITE.erl
@@ -1092,7 +1092,7 @@ wait_until(Pred) ->
get_nodefirstname_string() ->
atom_to_list(?MODULE)
++ "-"
- ++ integer_to_list(erlang:system_time(seconds))
+ ++ integer_to_list(erlang:system_time(second))
++ "-"
++ integer_to_list(erlang:unique_integer([positive])).
diff --git a/erts/emulator/test/old_scheduler_SUITE.erl b/erts/emulator/test/old_scheduler_SUITE.erl
index f91d84beea..ffe7d40139 100644
--- a/erts/emulator/test/old_scheduler_SUITE.erl
+++ b/erts/emulator/test/old_scheduler_SUITE.erl
@@ -309,13 +309,13 @@ receiver(T0, TimeSec, Main, {P1,P1N}, {P2,P2N}) ->
%% uncomment lines below to get life sign (debug)
receiver(T0, Time, Main, P1,P1N,P1Rs, P2,P2N,P2Rs, 0) ->
- % T = erlang:convert_time_unit(erlang:monotonic_time() - T0, native, milli_seconds),
+ % T = erlang:convert_time_unit(erlang:monotonic_time() - T0, native, millisecond),
% erlang:display({round(T/1000),P1Rs,P2Rs}),
receiver(T0, Time, Main, P1,P1N,P1Rs, P2,P2N,P2Rs, 100000);
receiver(T0, Time, Main, P1,P1N,P1Rs, P2,P2N,P2Rs, C) ->
Remain = Time - erlang:convert_time_unit(erlang:monotonic_time() - T0,
- native, milli_seconds), % test time remaining
+ native, millisecond), % test time remaining
Remain1 = if Remain < 0 ->
0;
true ->
diff --git a/erts/emulator/test/port_SUITE.erl b/erts/emulator/test/port_SUITE.erl
index 5c0bfdffd4..4323849465 100644
--- a/erts/emulator/test/port_SUITE.erl
+++ b/erts/emulator/test/port_SUITE.erl
@@ -104,6 +104,7 @@
mon_port_name_demonitor/1,
mon_port_named/1,
mon_port_origin_dies/1,
+ mon_port_owner_dies/1,
mon_port_pid_demonitor/1,
mon_port_remote_on_remote/1,
mon_port_driver_die/1,
@@ -173,6 +174,7 @@ all() ->
mon_port_remote_on_remote,
mon_port_bad_remote_on_local,
mon_port_origin_dies,
+ mon_port_owner_dies,
mon_port_named,
mon_port_bad_named,
mon_port_pid_demonitor,
@@ -1984,7 +1986,7 @@ exit_status_msb_test(Config, SleepSecs) when is_list(Config) ->
Parent = self(),
io:format("SleepSecs = ~p~n", [SleepSecs]),
PortProg = "sleep " ++ integer_to_list(SleepSecs),
- Start = erlang:monotonic_time(micro_seconds),
+ Start = erlang:monotonic_time(microsecond),
NoProcs = case NoSchedsOnln of
NProcs when NProcs < ?EXIT_STATUS_MSB_MAX_PROCS ->
NProcs;
@@ -2058,12 +2060,12 @@ exit_status_msb_test(Config, SleepSecs) when is_list(Config) ->
receive {P, started, SIds} -> SIds end
end,
Procs),
- StartedTime = (erlang:monotonic_time(micro_seconds) - Start)/1000000,
+ StartedTime = (erlang:monotonic_time(microsecond) - Start)/1000000,
io:format("StartedTime = ~p~n", [StartedTime]),
true = StartedTime < SleepSecs,
erlang:system_flag(multi_scheduling, block),
lists:foreach(fun (P) -> receive {P, done} -> ok end end, Procs),
- DoneTime = (erlang:monotonic_time(micro_seconds) - Start)/1000000,
+ DoneTime = (erlang:monotonic_time(microsecond) - Start)/1000000,
io:format("DoneTime = ~p~n", [DoneTime]),
true = DoneTime > SleepSecs,
ok = verify_multi_scheduling_blocked(),
@@ -2637,6 +2639,29 @@ mon_port_origin_dies(Config) ->
Port5 ! {self(), {command, <<"1">>}}, % make port quit
ok.
+%% Port and Monitor owner dies before port is closed
+%% This testcase checks for a regression memory leak in erts
+%% when the controlling and monitoring process is the same process
+%% and the process dies
+mon_port_owner_dies(Config) ->
+ Self = self(),
+ Proc = spawn(fun() ->
+ Port = create_port(Config, ["-h1", "-q"]),
+ Self ! {test_started, Port},
+ erlang:monitor(port, Port),
+ receive stop -> ok end
+ end),
+ erlang:monitor(process, Proc), % we want to sync with its death
+ Port = receive {test_started,P} -> P
+ after 1000 -> ?assert(false) end,
+ ?assertMatch({proc_monitors, true, port_monitored_by, true},
+ port_is_monitored(Proc, Port)),
+ Proc ! stop,
+ %% receive from monitor
+ receive ExitP5 -> ?assertMatch({'DOWN', _, process, Proc, _}, ExitP5)
+ after 1000 -> ?assert(false) end,
+ ok.
+
%% Monitor a named port
mon_port_named(Config) ->
Name6 = test_port6,
diff --git a/erts/emulator/test/process_SUITE.erl b/erts/emulator/test/process_SUITE.erl
index dae8990f56..0f999e0efe 100644
--- a/erts/emulator/test/process_SUITE.erl
+++ b/erts/emulator/test/process_SUITE.erl
@@ -372,7 +372,7 @@ eat_high(Low) ->
process_flag(priority, high),
receive after 1000 -> ok end,
exit(Low, {you, are, dead}),
- loop(erlang:monotonic_time() + erlang:convert_time_unit(5,seconds,native)).
+ loop(erlang:monotonic_time() + erlang:convert_time_unit(5,second,native)).
%% Busy loop for 5 seconds.
@@ -2376,7 +2376,7 @@ no_priority_inversion2(Config) when is_list(Config) ->
[{priority, max}, monitor, link])
end,
lists:seq(1, 2*erlang:system_info(schedulers))),
- receive after 500 -> ok end,
+ receive after 2000 -> ok end,
{PL, ML} = spawn_opt(fun () ->
tok_loop()
end,
@@ -2567,7 +2567,7 @@ start_node(Config, Args) when is_list(Config) ->
++ "-"
++ atom_to_list(proplists:get_value(testcase, Config))
++ "-"
- ++ integer_to_list(erlang:system_time(seconds))
+ ++ integer_to_list(erlang:system_time(second))
++ "-"
++ integer_to_list(erlang:unique_integer([positive]))),
test_server:start_node(Name, slave, [{args, "-pa "++Pa++" "++Args}]).
diff --git a/erts/emulator/test/scheduler_SUITE.erl b/erts/emulator/test/scheduler_SUITE.erl
index f18d79d770..3aee15a8fc 100644
--- a/erts/emulator/test/scheduler_SUITE.erl
+++ b/erts/emulator/test/scheduler_SUITE.erl
@@ -793,13 +793,13 @@ update_cpu_info(Config) when is_list(Config) ->
io:format("START - Affinity mask: ~p - Schedulers online: ~p - Scheduler bindings: ~p~n",
[OldAff, OldOnline, erlang:system_info(scheduler_bindings)]),
case {erlang:system_info(logical_processors_available), OldAff} of
- {Avail, _} when Avail == unknown; OldAff == unknown ->
+ {Avail, _} when Avail == unknown; OldAff == unknown; OldAff == 1 ->
%% Nothing much to test; just a smoke test
case erlang:system_info(update_cpu_info) of
unchanged -> ok;
changed -> ok
end;
- _ ->
+ {Avail, _} ->
try
adjust_schedulers_online(),
case erlang:system_info(schedulers_online) of
@@ -810,7 +810,7 @@ update_cpu_info(Config) when is_list(Config) ->
%% unset least significant bit
Aff = (OldAff band (OldAff - 1)),
set_affinity_mask(Aff),
- Onln1 = Onln0 - 1,
+ Onln1 = Avail - 1,
case adjust_schedulers_online() of
{Onln0, Onln1} ->
Onln1 = erlang:system_info(schedulers_online),
@@ -1072,20 +1072,27 @@ scheduler_threads(Config) when is_list(Config) ->
{Sched, HalfSchedOnln, _} = get_sstate(Config, "+SP:50"),
%% Configure 2x scheduler threads only
{TwiceSched, SchedOnln, _} = get_sstate(Config, "+SP 200"),
- %% Test resetting the scheduler counts
- ResetCmd = "+S "++FourSched++":"++FourSchedOnln++" +S 0:0",
- {Sched, SchedOnln, _} = get_sstate(Config, ResetCmd),
- %% Test negative +S settings, but only for SMP-enabled emulators
- case SmpSupport of
- false -> ok;
- true ->
- SchedMinus1 = Sched-1,
- SchedOnlnMinus1 = SchedOnln-1,
- {SchedMinus1, SchedOnlnMinus1, _} = get_sstate(Config, "+S -1"),
- {Sched, SchedOnlnMinus1, _} = get_sstate(Config, "+S :-1"),
- {SchedMinus1, SchedOnlnMinus1, _} = get_sstate(Config, "+S -1:-1")
- end,
- ok.
+ case {erlang:system_info(logical_processors),
+ erlang:system_info(logical_processors_available)} of
+ {LProc, LProcAvail} when is_integer(LProc), is_integer(LProcAvail) ->
+ %% Test resetting the scheduler counts
+ ResetCmd = "+S "++FourSched++":"++FourSchedOnln++" +S 0:0",
+ {LProc, LProcAvail, _} = get_sstate(Config, ResetCmd),
+ %% Test negative +S settings, but only for SMP-enabled emulators
+ case {SmpSupport, LProc > 1, LProcAvail > 1} of
+ {true, true, true} ->
+ SchedMinus1 = LProc-1,
+ SchedOnlnMinus1 = LProcAvail-1,
+ {SchedMinus1, SchedOnlnMinus1, _} = get_sstate(Config, "+S -1"),
+ {LProc, SchedOnlnMinus1, _} = get_sstate(Config, "+S :-1"),
+ {SchedMinus1, SchedOnlnMinus1, _} = get_sstate(Config, "+S -1:-1"),
+ ok;
+ _ ->
+ {comment, "Skipped reduced amount of schedulers test due to too few logical processors"}
+ end;
+ _ -> %% Skipped when missing info about logical processors...
+ {comment, "Skipped reset amount of schedulers test, and reduced amount of schedulers test due to too unknown amount of logical processors"}
+ end.
dirty_scheduler_threads(Config) when is_list(Config) ->
SmpSupport = erlang:system_info(smp_support),
@@ -1312,11 +1319,33 @@ scheduler_suspend_test(Config, Schedulers) ->
true ->
ok
end,
- erlang:system_info(schedulers_state)
+ until(fun () ->
+ {_A, B, C} = erlang:system_info(
+ schedulers_state),
+ B == C
+ end,
+ erlang:monotonic_time()
+ + erlang:convert_time_unit(1,
+ seconds,
+ native)),
+ erlang:system_info(schedulers_state)
end]),
stop_node(Node),
ok.
-
+
+until(Pred, MaxTime) ->
+ case Pred() of
+ true ->
+ true;
+ false ->
+ case erlang:monotonic_time() > MaxTime of
+ true ->
+ false;
+ false ->
+ receive after 100 -> ok end,
+ until(Pred, MaxTime)
+ end
+ end.
sst0_loop(0) ->
ok;
@@ -1978,10 +2007,10 @@ do_it(Tracer, Low, Normal, High, Max) ->
do_it(Tracer, Low, Normal, High, Max, RedsPerSchedLimit) ->
OldPrio = process_flag(priority, max),
go_work(Low, Normal, High, Max),
- StartWait = erlang:monotonic_time(milli_seconds),
+ StartWait = erlang:monotonic_time(millisecond),
%% Give the emulator a chance to balance the load...
wait_balance(5),
- EndWait = erlang:monotonic_time(milli_seconds),
+ EndWait = erlang:monotonic_time(millisecond),
BalanceWait = EndWait-StartWait,
erlang:display({balance_wait, BalanceWait}),
Timeout = (15 - 4)*60*1000 - BalanceWait,
@@ -2181,7 +2210,7 @@ start_node(Config, Args) when is_list(Config) ->
++ "-"
++ atom_to_list(proplists:get_value(testcase, Config))
++ "-"
- ++ integer_to_list(erlang:system_time(seconds))
+ ++ integer_to_list(erlang:system_time(second))
++ "-"
++ integer_to_list(erlang:unique_integer([positive]))),
test_server:start_node(Name, slave, [{args, "-pa "++Pa++" "++Args}]).
diff --git a/erts/emulator/test/signal_SUITE.erl b/erts/emulator/test/signal_SUITE.erl
index 0b11fa13f5..7e516176f7 100644
--- a/erts/emulator/test/signal_SUITE.erl
+++ b/erts/emulator/test/signal_SUITE.erl
@@ -484,7 +484,7 @@ repeat(Fun, N) when is_integer(N) ->
start_node(Config) ->
Name = list_to_atom(atom_to_list(?MODULE)
++ "-" ++ atom_to_list(proplists:get_value(testcase, Config))
- ++ "-" ++ integer_to_list(erlang:system_time(seconds))
+ ++ "-" ++ integer_to_list(erlang:system_time(second))
++ "-" ++ integer_to_list(erlang:unique_integer([positive]))),
Pa = filename:dirname(code:which(?MODULE)),
test_server:start_node(Name, slave, [{args, "-pa " ++ Pa}]).
diff --git a/erts/emulator/test/smoke_test_SUITE.erl b/erts/emulator/test/smoke_test_SUITE.erl
index 042c7225d5..5eccdc562b 100644
--- a/erts/emulator/test/smoke_test_SUITE.erl
+++ b/erts/emulator/test/smoke_test_SUITE.erl
@@ -152,7 +152,7 @@ start_node(Config, Args) when is_list(Config) ->
++ "-"
++ atom_to_list(proplists:get_value(testcase, Config))
++ "-"
- ++ integer_to_list(erlang:system_time(seconds))
+ ++ integer_to_list(erlang:system_time(second))
++ "-"
++ integer_to_list(erlang:unique_integer([positive]))),
Opts = [{args, "-pa "++Pa++" "++Args}],
diff --git a/erts/emulator/test/statistics_SUITE.erl b/erts/emulator/test/statistics_SUITE.erl
index 71ef003b25..a1f12ba93c 100644
--- a/erts/emulator/test/statistics_SUITE.erl
+++ b/erts/emulator/test/statistics_SUITE.erl
@@ -129,11 +129,15 @@ do_runtime_update(0) ->
{comment,"Never close enough"};
do_runtime_update(N) ->
{T1,Diff0} = statistics(runtime),
- spawn_link(fun cpu_heavy/0),
+ {CPUHog, CPUHogMon} = spawn_opt(fun cpu_heavy/0,[link,monitor]),
receive after 1000 -> ok end,
{T2,Diff} = statistics(runtime),
+ unlink(CPUHog),
+ exit(CPUHog, kill),
+
true = is_integer(T1+T2+Diff0+Diff),
io:format("T1 = ~p, T2 = ~p, Diff = ~p, T2-T1 = ~p", [T1,T2,Diff,T2-T1]),
+ receive {'DOWN',CPUHogMon,process,CPUHog,_} -> ok end,
if
T2 - T1 =:= Diff, 900 =< Diff, Diff =< 1500 -> ok;
true -> do_runtime_update(N-1)
@@ -311,8 +315,17 @@ scheduler_wall_time(Config) when is_list(Config) ->
true -> exit({fullload, FullLoad})
end,
- [exit(Pid, kill) || Pid <- [P1|HalfHogs++LastHogs]],
+ KillHog = fun (HP) ->
+ HPM = erlang:monitor(process, HP),
+ exit(HP, kill),
+ receive
+ {'DOWN', HPM, process, HP, killed} ->
+ ok
+ end
+ end,
+ [KillHog(Pid) || Pid <- [P1|HalfHogs++LastHogs]],
AfterLoad = get_load(),
+ io:format("AfterLoad=~p~n", [AfterLoad]),
{false,_} = {lists:any(fun(Load) -> Load > 25 end, AfterLoad),AfterLoad},
true = erlang:system_flag(scheduler_wall_time, false)
after
diff --git a/erts/emulator/test/system_info_SUITE.erl b/erts/emulator/test/system_info_SUITE.erl
index f31d474c20..a4aedb31f6 100644
--- a/erts/emulator/test/system_info_SUITE.erl
+++ b/erts/emulator/test/system_info_SUITE.erl
@@ -508,7 +508,7 @@ start_node(Config, Envs) when is_list(Config) ->
++ "-"
++ atom_to_list(proplists:get_value(testcase, Config))
++ "-"
- ++ integer_to_list(erlang:system_time(seconds))
+ ++ integer_to_list(erlang:system_time(second))
++ "-"
++ integer_to_list(erlang:unique_integer([positive]))),
test_server:start_node(Name, peer, [{args, "-pa "++Pa}, {env, Envs}]).
diff --git a/erts/emulator/test/time_SUITE.erl b/erts/emulator/test/time_SUITE.erl
index 87b8c62cfa..9501569814 100644
--- a/erts/emulator/test/time_SUITE.erl
+++ b/erts/emulator/test/time_SUITE.erl
@@ -295,7 +295,7 @@ timestamp(Config) when is_list(Config) ->
os_system_time_offset() ->
erlang:convert_time_unit(os:system_time() - erlang:monotonic_time(),
- native, micro_seconds).
+ native, microsecond).
had_time_warp(Secs) ->
had_time_warp(os_system_time_offset(), Secs).
@@ -488,12 +488,12 @@ check_time_warp_mode(Config, TimeCorrection, TimeWarpMode) ->
MonotonicTimeUnit = rpc:call(Node,
erlang,
convert_time_unit,
- [1, seconds, native]),
+ [1, second, native]),
UpMilliSeconds = erlang:convert_time_unit(MonotonicTime - StartTime,
MonotonicTimeUnit,
- milli_seconds),
+ millisecond),
io:format("UpMilliSeconds=~p~n", [UpMilliSeconds]),
- End = erlang:monotonic_time(milli_seconds),
+ End = erlang:monotonic_time(millisecond),
stop_node(Node),
try
true = (UpMilliSeconds > (98*MonotonicityTimeout) div 100),
@@ -810,10 +810,10 @@ do_check_erlang_timestamp(Done, Mon, TO) ->
MaxMon = erlang:monotonic_time(),
TsMin = erlang:convert_time_unit(MinMon+TO,
native,
- micro_seconds),
+ microsecond),
TsMax = erlang:convert_time_unit(MaxMon+TO,
native,
- micro_seconds),
+ microsecond),
TsTime = (MegaSec*1000000+Sec)*1000000+MicroSec,
case (TsMin =< TsTime) andalso (TsTime =< TsMax) of
true ->
diff --git a/erts/emulator/test/timer_bif_SUITE.erl b/erts/emulator/test/timer_bif_SUITE.erl
index a5f11bd959..7cbd93a0f3 100644
--- a/erts/emulator/test/timer_bif_SUITE.erl
+++ b/erts/emulator/test/timer_bif_SUITE.erl
@@ -74,7 +74,7 @@ all() ->
%% Basic start_timer/3 functionality
start_timer_1(Config) when is_list(Config) ->
Ref1 = erlang:start_timer(1000, self(), plopp),
- ok = get(1100, {timeout, Ref1, plopp}),
+ ok = get(1400, {timeout, Ref1, plopp}),
false = erlang:read_timer(Ref1),
false = erlang:cancel_timer(Ref1),
@@ -83,12 +83,12 @@ start_timer_1(Config) when is_list(Config) ->
Ref2 = erlang:start_timer(1000, self(), plapp),
Left2 = erlang:cancel_timer(Ref2),
UpperLimit = 1000,
- true = (Left2 > 900) and (Left2 =< UpperLimit),
+ true = (Left2 > 600) and (Left2 =< UpperLimit),
empty = get_msg(),
false = erlang:cancel_timer(Ref2),
Ref3 = erlang:start_timer(1000, self(), plopp),
- no_message = get(900, {timeout, Ref3, plopp}),
+ no_message = get(600, {timeout, Ref3, plopp}),
ok.
%% Basic send_after/3 functionality
@@ -489,7 +489,7 @@ registered_process(Config) when is_list(Config) ->
same_time_yielding(Config) when is_list(Config) ->
Mem = mem(),
SchdlrsOnln = erlang:system_info(schedulers_online),
- Tmo = erlang:monotonic_time(milli_seconds) + 3000,
+ Tmo = erlang:monotonic_time(millisecond) + 3000,
Tmrs = lists:map(fun (I) ->
process_flag(scheduler, (I rem SchdlrsOnln) + 1),
erlang:start_timer(Tmo, self(), hej, [{abs, true}])
@@ -497,7 +497,7 @@ same_time_yielding(Config) when is_list(Config) ->
lists:seq(1, (?TIMEOUT_YIELD_LIMIT*3+1)*SchdlrsOnln)),
true = mem_larger_than(Mem),
lists:foreach(fun (Tmr) -> receive {timeout, Tmr, hej} -> ok end end, Tmrs),
- Done = erlang:monotonic_time(milli_seconds),
+ Done = erlang:monotonic_time(millisecond),
true = Done >= Tmo,
case erlang:system_info(build_type) of
opt -> true = Done < Tmo + 200;
@@ -517,10 +517,10 @@ same_time_yielding_with_cancel_other(Config) when is_list(Config) ->
do_cancel_tmrs(Tmo, Tmrs, Tester) ->
BeginCancel = erlang:convert_time_unit(Tmo,
- milli_seconds,
- micro_seconds) - 100,
+ millisecond,
+ microsecond) - 100,
busy_wait_until(fun () ->
- erlang:monotonic_time(micro_seconds) >= BeginCancel
+ erlang:monotonic_time(microsecond) >= BeginCancel
end),
lists:foreach(fun (Tmr) ->
erlang:cancel_timer(Tmr,
@@ -535,7 +535,7 @@ do_cancel_tmrs(Tmo, Tmrs, Tester) ->
same_time_yielding_with_cancel_test(Other, Accessor) ->
Mem = mem(),
SchdlrsOnln = erlang:system_info(schedulers_online),
- Tmo = erlang:monotonic_time(milli_seconds) + 3000,
+ Tmo = erlang:monotonic_time(millisecond) + 3000,
Tester = self(),
Cancelor = case Other of
false ->
@@ -656,7 +656,7 @@ get_msg() ->
start_slave() ->
Pa = filename:dirname(code:which(?MODULE)),
Name = atom_to_list(?MODULE)
- ++ "-" ++ integer_to_list(erlang:system_time(seconds))
+ ++ "-" ++ integer_to_list(erlang:system_time(second))
++ "-" ++ integer_to_list(erlang:unique_integer([positive])),
{ok, Node} = test_server:start_node(Name, slave, [{args, "-pa " ++ Pa}]),
Node.
diff --git a/erts/emulator/test/trace_bif_SUITE.erl b/erts/emulator/test/trace_bif_SUITE.erl
index 491b37ae46..f60c777ba1 100644
--- a/erts/emulator/test/trace_bif_SUITE.erl
+++ b/erts/emulator/test/trace_bif_SUITE.erl
@@ -289,9 +289,9 @@ receive_trace_msg_ts_return_to({trace_ts, Pid, return_to, {M,F,A}}, PrevTs, TsTy
make_ts(timestamp) ->
erlang:now();
make_ts(monotonic_timestamp) ->
- erlang:monotonic_time(nano_seconds);
+ erlang:monotonic_time(nanosecond);
make_ts(strict_monotonic_timestamp) ->
- MT = erlang:monotonic_time(nano_seconds),
+ MT = erlang:monotonic_time(nanosecond),
UMI = erlang:unique_integer([monotonic]),
{MT, UMI}.
diff --git a/erts/emulator/test/trace_call_time_SUITE.erl b/erts/emulator/test/trace_call_time_SUITE.erl
index 6582ad134b..26f96a1766 100644
--- a/erts/emulator/test/trace_call_time_SUITE.erl
+++ b/erts/emulator/test/trace_call_time_SUITE.erl
@@ -294,7 +294,7 @@ combo(Config) when is_list(Config) ->
T0 = erlang:monotonic_time(),
with_bif(Nbc),
T1 = erlang:monotonic_time(),
- TimeB = erlang:convert_time_unit(T1-T0, native, micro_seconds),
+ TimeB = erlang:convert_time_unit(T1-T0, native, microsecond),
%%
List = collect(100),
@@ -655,13 +655,13 @@ execute(Pids, Mfa) when is_list(Pids) ->
[P ! {self(), execute, Mfa} || P <- Pids],
As = [receive {P, answer, Answer} -> Answer end || P <- Pids],
T1 = erlang:monotonic_time(),
- {As, erlang:convert_time_unit(T1-T0, native, micro_seconds)};
+ {As, erlang:convert_time_unit(T1-T0, native, microsecond)};
execute(P, Mfa) ->
T0 = erlang:monotonic_time(),
P ! {self(), execute, Mfa},
A = receive {P, answer, Answer} -> Answer end,
T1 = erlang:monotonic_time(),
- {A, erlang:convert_time_unit(T1-T0, native, micro_seconds)}.
+ {A, erlang:convert_time_unit(T1-T0, native, microsecond)}.
diff --git a/erts/emulator/test/z_SUITE.erl b/erts/emulator/test/z_SUITE.erl
index d1085c1958..ab56018373 100644
--- a/erts/emulator/test/z_SUITE.erl
+++ b/erts/emulator/test/z_SUITE.erl
@@ -191,7 +191,13 @@ node_container_refc_check(Config) when is_list(Config) ->
ok.
long_timers(Config) when is_list(Config) ->
- ok = long_timers_test:check_result().
+ case long_timers_test:check_result() of
+ ok -> ok;
+ high_cpu -> {comment, "Ignored failures due to high CPU utilization"};
+ missing_cpu_info -> {comment, "Ignored failures due to missing CPU utilization information"};
+ Fail -> ct:fail(Fail)
+ end.
+
pollset_size(Config) when is_list(Config) ->
Name = pollset_size_testcase_initial_state_holder,
diff --git a/erts/etc/unix/cerl.src b/erts/etc/unix/cerl.src
index c5422ab2ed..30f2d831b5 100644
--- a/erts/etc/unix/cerl.src
+++ b/erts/etc/unix/cerl.src
@@ -399,19 +399,29 @@ elif [ "x$GDB" = "xdump" ]; then
cmdfile="/tmp/.cerlgdb.$$"
case "x$core" in
x/*)
- gdbcmd="$EMU_NAME ${core}"
;;
*)
dir=`pwd`
- gdbcmd="$EMU_NAME ${dir}/${core}"
+ core="${dir}/${core}"
;;
esac
- echo "set width 0
+ case `uname` in
+ Darwin)
+ echo "
+thread backtrace all
+quit
+" > $cmdfile
+ exec lldb -s $cmdfile -c ${core} $EMU_NAME
+ ;;
+ *)
+ echo "set width 0
set height 0
set verbose off
source $ROOTDIR/erts/etc/unix/etp-commands
thread apply all bt
" > $cmdfile
- exec gdb --batch --command=$cmdfile $gdbcmd
+ exec gdb --batch --command=$cmdfile $EMU_NAME $core
+ ;;
+ esac
fi
diff --git a/erts/etc/unix/format_man_pages b/erts/etc/unix/format_man_pages
index 54f2d90c28..0afff13571 100644
--- a/erts/etc/unix/format_man_pages
+++ b/erts/etc/unix/format_man_pages
@@ -107,7 +107,7 @@ case :"$TARGET" in
if [ -f $file ]; then
name=`echo $file | sed 's/\.[^.]*$//'`
sec=`echo $file | sed 's/.*\.//'`
- /usr/bin/groff -Tascii -mandoc $ERL_ROOT/man/man$sec/$file \
+ /usr/bin/groff -Tutf8 -mandoc $ERL_ROOT/man/man$sec/$file \
> $ERL_ROOT/man/cat$sec/$file
fi
done
diff --git a/erts/etc/unix/run_erl.c b/erts/etc/unix/run_erl.c
index 30210ac172..6a92e18648 100644
--- a/erts/etc/unix/run_erl.c
+++ b/erts/etc/unix/run_erl.c
@@ -240,6 +240,7 @@ int main(int argc, char **argv)
int off_argv;
int calculated_pipename = 0;
int highest_pipe_num = 0;
+ int sleepy_child = 0;
program_name = argv[0];
@@ -250,6 +251,11 @@ int main(int argc, char **argv)
init_outbuf();
+ if (!strcmp(argv[1],"-sleepy-child")) { /* For test purpose only */
+ sleepy_child = 1;
+ ++i;
+ }
+
if (!strcmp(argv[1],"-daemon")) {
daemon_init();
++i;
@@ -392,6 +398,9 @@ int main(int argc, char **argv)
exit(1);
}
if (childpid == 0) {
+ if (sleepy_child)
+ sleep(1);
+
/* Child */
sf_close(mfd);
/* disassociate from control terminal */
@@ -904,20 +913,32 @@ static int create_fifo(char *name, int perm)
* Find a master device, open and return fd and slave device name.
*/
+#ifdef HAVE_WORKING_POSIX_OPENPT
+ /*
+ * Use openpty() on OpenBSD even if we have posix_openpt()
+ * as there is a race when read from master pty returns 0
+ * if child has not yet opened slave pty.
+ * (maybe other BSD's have the same problem?)
+ */
+# if !(defined(__OpenBSD__) && defined(HAVE_OPENPTY))
+# define TRY_POSIX_OPENPT
+# endif
+#endif
+
static int open_pty_master(char **ptyslave, int *sfdp)
{
int mfd;
/* Use the posix_openpt if working, as this guarantees creation of the
slave device properly. */
-#if defined(HAVE_WORKING_POSIX_OPENPT) || (defined(__sun) && defined(__SVR4))
-# ifdef HAVE_WORKING_POSIX_OPENPT
- if ((mfd = posix_openpt(O_RDWR)) >= 0) {
+#if defined(TRY_POSIX_OPENPT) || (defined(__sun) && defined(__SVR4))
+# ifdef TRY_POSIX_OPENPT
+ mfd = posix_openpt(O_RDWR);
# elif defined(__sun) && defined(__SVR4)
mfd = sf_open("/dev/ptmx", O_RDWR, 0);
+# endif
if (mfd >= 0) {
-# endif
if ((*ptyslave = ptsname(mfd)) != NULL &&
grantpt(mfd) == 0 &&
unlockpt(mfd) == 0) {
diff --git a/erts/example/time_compat.erl b/erts/example/time_compat.erl
index b87c6cc550..589781c8e8 100644
--- a/erts/example/time_compat.erl
+++ b/erts/example/time_compat.erl
@@ -272,6 +272,10 @@ system_flag(Flag, Value) ->
%%
integer_time_unit(native) -> 1000*1000;
+integer_time_unit(nanosecond) -> 1000*1000*1000;
+integer_time_unit(microsecond) -> 1000*1000;
+integer_time_unit(millisecond) -> 1000;
+integer_time_unit(second) -> 1;
integer_time_unit(nano_seconds) -> 1000*1000*1000;
integer_time_unit(micro_seconds) -> 1000*1000;
integer_time_unit(milli_seconds) -> 1000;
diff --git a/erts/include/internal/erl_misc_utils.h b/erts/include/internal/erl_misc_utils.h
index a4a5d1d510..55566ddf74 100644
--- a/erts/include/internal/erl_misc_utils.h
+++ b/erts/include/internal/erl_misc_utils.h
@@ -56,4 +56,33 @@ int erts_map_win_error_to_errno(DWORD win_error);
int erts_get_last_win_errno(void);
#endif
+#if defined(__APPLE__) && defined(__MACH__) && !defined(__DARWIN__)
+#define __DARWIN__ 1
+#endif
+
+/*
+ * ERTS_PREMATURE_TIMEOUT() expects time units
+ * 1000 (millisec), 1000000 (microsec), or
+ * 1000000000 (nanosec). Might not work properly
+ * otherwise.
+ */
+#undef ERTS_USE_PREMATURE_TIMEOUT
+#undef ERTS_PREMATURE_TIMEOUT
+
+#if defined(__DARWIN__)
+#define ERTS_USE_PREMATURE_TIMEOUT 1
+#define ERTS_PREMATURE_TIMEOUT(TMO, TU) \
+ ((TMO) >= 1 * ((TU) / 1000) \
+ ? ((TMO) >= 20 * ((TU) / 1000) \
+ ? 15 * ((TU) / 1000) \
+ : ((TMO) >= 5 * ((TU) / 1000) \
+ ? 3 * ((TU) / 1000) \
+ : 5 * ((TU) / 10000))) \
+ : 0)
+
+#else
+#define ERTS_USE_PREMATURE_TIMEOUT 0
+#define ERTS_PREMATURE_TIMEOUT(TMO, TU) (0)
+#endif
+
#endif /* #ifndef ERL_MISC_UTILS_H_ */
diff --git a/erts/lib_src/pthread/ethr_event.c b/erts/lib_src/pthread/ethr_event.c
index eef88d5002..464875570a 100644
--- a/erts/lib_src/pthread/ethr_event.c
+++ b/erts/lib_src/pthread/ethr_event.c
@@ -184,6 +184,8 @@ return_event_on:
#include <errno.h>
#include <string.h>
+#include "erl_misc_utils.h"
+
#ifdef __DARWIN__
struct ethr_event_fdsets___ {
@@ -346,12 +348,9 @@ wait__(ethr_event *e, int spincount, ethr_sint64_t timeout)
ethr_sint32_t val;
int res, ulres;
int until_yield = ETHR_YIELD_AFTER_BUSY_LOOPS;
- ethr_sint64_t time = 0; /* SHUT UP annoying faulty warning... */
+ ethr_sint64_t time = 0;
#ifdef ETHR_HAVE_ETHR_GET_MONOTONIC_TIME
- ethr_sint64_t start = 0; /* SHUT UP annoying faulty warning... */
-#endif
-#ifdef ETHR_HAVE_PTHREAD_COND_TIMEDWAIT_MONOTONIC
- struct timespec cond_timeout;
+ ethr_sint64_t timeout_time = 0; /* SHUT UP annoying faulty warning... */
#endif
val = ethr_atomic32_read(&e->state);
@@ -365,30 +364,27 @@ wait__(ethr_event *e, int spincount, ethr_sint64_t timeout)
if (timeout == 0)
return ETIMEDOUT;
else {
- time = timeout;
- switch (e->fd[0]) {
- case ETHR_EVENT_INVALID_FD__:
#ifdef ETHR_HAVE_ETHR_GET_MONOTONIC_TIME
- start = ethr_get_monotonic_time();
+ timeout_time = ethr_get_monotonic_time();
+ timeout_time += timeout;
#endif
+ switch (e->fd[0]) {
+ case ETHR_EVENT_INVALID_FD__:
+ time = timeout;
setup_nonblocking_pipe(e);
break;
#ifdef ETHR_HAVE_PTHREAD_COND_TIMEDWAIT_MONOTONIC
case ETHR_EVENT_COND_TIMEDWAIT__:
- time += ethr_get_monotonic_time();
- cond_timeout.tv_sec = time / (1000*1000*1000);
- cond_timeout.tv_nsec = time % (1000*1000*1000);
+ time = -1;
if (spincount == 0)
goto set_event_off_waiter;
break;
#endif
default:
+ time = timeout;
/* Already initialized pipe... */
if (spincount == 0)
goto set_select_timeout;
-#ifdef ETHR_HAVE_ETHR_GET_MONOTONIC_TIME
- start = ethr_get_monotonic_time();
-#endif
break;
}
}
@@ -418,6 +414,9 @@ wait__(ethr_event *e, int spincount, ethr_sint64_t timeout)
|| e->fd[0] == ETHR_EVENT_COND_TIMEDWAIT__
#endif
) {
+#ifdef ETHR_HAVE_PTHREAD_COND_TIMEDWAIT_MONOTONIC
+ struct timespec cond_timeout;
+#endif
set_event_off_waiter:
@@ -446,9 +445,35 @@ wait__(ethr_event *e, int spincount, ethr_sint64_t timeout)
#ifdef ETHR_HAVE_PTHREAD_COND_TIMEDWAIT_MONOTONIC
if (timeout > 0) {
+ if (time != timeout_time) {
+ time = timeout_time;
+
+#if ERTS_USE_PREMATURE_TIMEOUT
+ {
+ ethr_sint64_t rtmo;
+
+ rtmo = timeout_time - ethr_get_monotonic_time();
+ if (rtmo <= 0) {
+ res = ETIMEDOUT;
+ break;
+ }
+ time = timeout_time;
+ time -= ERTS_PREMATURE_TIMEOUT(rtmo, 1000*1000*1000);
+ }
+#endif
+
+ cond_timeout.tv_sec = time / (1000*1000*1000);
+ cond_timeout.tv_nsec = time % (1000*1000*1000);
+ }
res = pthread_cond_timedwait(&e->cnd, &e->mtx, &cond_timeout);
- if (res == EINTR || res == ETIMEDOUT)
+ if (res == EINTR
+ || (res == ETIMEDOUT
+#if ERTS_USE_PREMATURE_TIMEOUT
+ && time == timeout_time
+#endif
+ )) {
break;
+ }
}
else
#endif
@@ -477,7 +502,11 @@ wait__(ethr_event *e, int spincount, ethr_sint64_t timeout)
struct timeval select_timeout;
#ifdef ETHR_HAVE_ETHR_GET_MONOTONIC_TIME
- time -= ethr_get_monotonic_time() - start;
+#if ERTS_USE_PREMATURE_TIMEOUT
+ restart_select:
+#endif
+
+ time = timeout_time - ethr_get_monotonic_time();
if (time <= 0)
return ETIMEDOUT;
#endif
@@ -492,6 +521,11 @@ wait__(ethr_event *e, int spincount, ethr_sint64_t timeout)
*/
time = ((time - 1) / 1000) + 1;
+#if defined(ETHR_HAVE_ETHR_GET_MONOTONIC_TIME) \
+ && ERTS_USE_PREMATURE_TIMEOUT
+ time -= ERTS_PREMATURE_TIMEOUT(time, 1000*1000);
+#endif
+
select_timeout.tv_sec = time / (1000*1000);
select_timeout.tv_usec = time % (1000*1000);
@@ -559,7 +593,11 @@ wait__(ethr_event *e, int spincount, ethr_sint64_t timeout)
val = ethr_atomic32_read(&e->state);
if (val == ETHR_EVENT_ON__)
goto return_event_on;
-
+#if defined(ETHR_HAVE_ETHR_GET_MONOTONIC_TIME) \
+ && ERTS_USE_PREMATURE_TIMEOUT
+ if (res == ETIMEDOUT)
+ goto restart_select; /* Verify timeout */
+#endif
}
return res;
diff --git a/erts/preloaded/ebin/erlang.beam b/erts/preloaded/ebin/erlang.beam
index b62da04bfd..c68debeabc 100644
--- a/erts/preloaded/ebin/erlang.beam
+++ b/erts/preloaded/ebin/erlang.beam
Binary files differ
diff --git a/erts/preloaded/ebin/erts_code_purger.beam b/erts/preloaded/ebin/erts_code_purger.beam
index a0da864824..6956eee740 100644
--- a/erts/preloaded/ebin/erts_code_purger.beam
+++ b/erts/preloaded/ebin/erts_code_purger.beam
Binary files differ
diff --git a/erts/preloaded/ebin/erts_dirty_process_code_checker.beam b/erts/preloaded/ebin/erts_dirty_process_code_checker.beam
new file mode 100644
index 0000000000..a7ac116c05
--- /dev/null
+++ b/erts/preloaded/ebin/erts_dirty_process_code_checker.beam
Binary files differ
diff --git a/erts/preloaded/ebin/erts_internal.beam b/erts/preloaded/ebin/erts_internal.beam
index d897c8e92f..227b62b7d3 100644
--- a/erts/preloaded/ebin/erts_internal.beam
+++ b/erts/preloaded/ebin/erts_internal.beam
Binary files differ
diff --git a/erts/preloaded/ebin/erts_literal_area_collector.beam b/erts/preloaded/ebin/erts_literal_area_collector.beam
new file mode 100644
index 0000000000..71f3c2ec8c
--- /dev/null
+++ b/erts/preloaded/ebin/erts_literal_area_collector.beam
Binary files differ
diff --git a/erts/preloaded/ebin/init.beam b/erts/preloaded/ebin/init.beam
index b856bff4fe..849273f746 100644
--- a/erts/preloaded/ebin/init.beam
+++ b/erts/preloaded/ebin/init.beam
Binary files differ
diff --git a/erts/preloaded/ebin/prim_inet.beam b/erts/preloaded/ebin/prim_inet.beam
index fba03d52bd..1a573ce297 100644
--- a/erts/preloaded/ebin/prim_inet.beam
+++ b/erts/preloaded/ebin/prim_inet.beam
Binary files differ
diff --git a/erts/preloaded/src/Makefile b/erts/preloaded/src/Makefile
index 4a447d3a09..2ab9edaf5e 100644
--- a/erts/preloaded/src/Makefile
+++ b/erts/preloaded/src/Makefile
@@ -44,7 +44,9 @@ PRE_LOADED_ERL_MODULES = \
erts_code_purger \
erlang \
erts_internal \
- erl_tracer
+ erl_tracer \
+ erts_literal_area_collector \
+ erts_dirty_process_code_checker
PRE_LOADED_BEAM_MODULES = \
prim_eval
diff --git a/erts/preloaded/src/erlang.erl b/erts/preloaded/src/erlang.erl
index edf79b8f75..652a954807 100644
--- a/erts/preloaded/src/erlang.erl
+++ b/erts/preloaded/src/erlang.erl
@@ -59,6 +59,7 @@
-export_type([timestamp/0]).
-export_type([time_unit/0]).
+-export_type([deprecated_time_unit/0]).
-type ext_binary() :: binary().
-type timestamp() :: {MegaSecs :: non_neg_integer(),
@@ -67,12 +68,20 @@
-type time_unit() ::
pos_integer()
- | 'seconds'
+ | 'second'
+ | 'millisecond'
+ | 'microsecond'
+ | 'nanosecond'
+ | 'native'
+ | 'perf_counter'
+ | deprecated_time_unit().
+
+%% Deprecated symbolic units...
+-type deprecated_time_unit() ::
+ 'seconds'
| 'milli_seconds'
| 'micro_seconds'
- | 'nano_seconds'
- | 'native'
- | 'perf_counter'.
+ | 'nano_seconds'.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Native code BIF stubs and their types
@@ -1365,19 +1374,33 @@ convert_time_unit(Time, FromUnit, ToUnit) ->
FU = case FromUnit of
native -> erts_internal:time_unit();
perf_counter -> erts_internal:perf_counter_unit();
+ nanosecond -> 1000*1000*1000;
+ microsecond -> 1000*1000;
+ millisecond -> 1000;
+ second -> 1;
+
+ %% Deprecated symbolic units...
nano_seconds -> 1000*1000*1000;
micro_seconds -> 1000*1000;
milli_seconds -> 1000;
seconds -> 1;
+
_ when FromUnit > 0 -> FromUnit
end,
TU = case ToUnit of
native -> erts_internal:time_unit();
perf_counter -> erts_internal:perf_counter_unit();
+ nanosecond -> 1000*1000*1000;
+ microsecond -> 1000*1000;
+ millisecond -> 1000;
+ second -> 1;
+
+ %% Deprecated symbolic units...
nano_seconds -> 1000*1000*1000;
micro_seconds -> 1000*1000;
milli_seconds -> 1000;
seconds -> 1;
+
_ when ToUnit > 0 -> ToUnit
end,
case Time < 0 of
diff --git a/erts/preloaded/src/erts.app.src b/erts/preloaded/src/erts.app.src
index e18da28905..7ab06164b4 100644
--- a/erts/preloaded/src/erts.app.src
+++ b/erts/preloaded/src/erts.app.src
@@ -37,7 +37,7 @@
{registered, []},
{applications, []},
{env, []},
- {runtime_dependencies, ["stdlib-3.0", "kernel-5.0", "sasl-3.0"]}
+ {runtime_dependencies, ["stdlib-3.0", "kernel-5.0", "sasl-3.0.1"]}
]}.
%% vim: ft=erlang
diff --git a/erts/preloaded/src/erts_code_purger.erl b/erts/preloaded/src/erts_code_purger.erl
index d1e64342e0..28d71fd07e 100644
--- a/erts/preloaded/src/erts_code_purger.erl
+++ b/erts/preloaded/src/erts_code_purger.erl
@@ -22,7 +22,8 @@
%% Purpose : Implement system process erts_code_purger
%% to handle code module purging.
--export([start/0, purge/1, soft_purge/1]).
+-export([start/0, purge/1, soft_purge/1, pending_purge_lambda/3,
+ finish_after_on_load/2]).
-spec start() -> term().
start() ->
@@ -40,10 +41,48 @@ loop() ->
Res = do_soft_purge(Mod),
From ! {reply, soft_purge, Res, Ref};
+ {finish_after_on_load,{Mod,Keep},From,Ref}
+ when is_atom(Mod), is_pid(From) ->
+ Res = do_finish_after_on_load(Mod, Keep),
+ From ! {reply, finish_after_on_load, Res, Ref};
+
+ {test_purge, Mod, From, Type, Ref} when is_atom(Mod), is_pid(From) ->
+ do_test_purge(Mod, From, Type, Ref);
+
_Other -> ignore
end,
loop().
+%%
+%% Processes that tries to call a fun that belongs to
+%% a module that currently is being purged will end
+%% up here (pending_purge_lambda) in a suspended state.
+%% When the purge operation completes or aborts (soft
+%% purge that failed) these processes will be resumed.
+%%
+pending_purge_lambda(_Module, Fun, Args) ->
+ %%
+ %% When the process is resumed, the following
+ %% scenarios exist:
+ %% * The code that the fun refers to is still
+ %% there due to a failed soft purge. The
+ %% call to the fun will succeed via apply/2.
+ %% * The code was purged, and a current version
+ %% of the module is loaded which does not
+ %% contain this fun. The call will result
+ %% in an exception being raised.
+ %% * The code was purged, and no current
+ %% version of the module is loaded. An attempt
+ %% to load the module (via the error_handler)
+ %% will be made. This may or may not succeed.
+ %% If the module is loaded, it may or may
+ %% not contain the fun. The call will
+ %% succeed if the error_handler was able
+ %% to load the module and loaded module
+ %% contains this fun; otherwise, an exception
+ %% will be raised.
+ %%
+ apply(Fun, Args).
%% purge(Module)
%% Kill all processes running code from *old* Module, and then purge the
@@ -60,16 +99,14 @@ purge(Mod) when is_atom(Mod) ->
Result
end.
-
do_purge(Mod) ->
- case erts_internal:copy_literals(Mod, true) of
- false ->
- {false, false};
- true ->
- DidKill = check_proc_code(erlang:processes(), Mod, true),
- true = erts_internal:copy_literals(Mod, false),
- WasPurged = erts_internal:purge_module(Mod),
- {WasPurged, DidKill}
+ case erts_internal:purge_module(Mod, prepare) of
+ false ->
+ {false, false};
+ true ->
+ DidKill = check_proc_code(erlang:processes(), Mod, true),
+ true = erts_internal:purge_module(Mod, complete),
+ {true, DidKill}
end.
%% soft_purge(Module)
@@ -85,23 +122,48 @@ soft_purge(Mod) ->
Result
end.
-
do_soft_purge(Mod) ->
- case erts_internal:copy_literals(Mod, true) of
+ case erts_internal:purge_module(Mod, prepare) of
false ->
true;
true ->
- DoPurge = check_proc_code(erlang:processes(), Mod, false),
- true = erts_internal:copy_literals(Mod, false),
- case DoPurge of
+ Res = check_proc_code(erlang:processes(), Mod, false),
+ erts_internal:purge_module(Mod,
+ case Res of
+ false -> abort;
+ true -> complete
+ end)
+ end.
+
+%% finish_after_on_load(Module, Keep)
+%% Finish after running on_load function. If Keep is false,
+%% purge the code for the on_load function.
+
+finish_after_on_load(Mod, Keep) ->
+ Ref = make_ref(),
+ erts_code_purger ! {finish_after_on_load, {Mod,Keep}, self(), Ref},
+ receive
+ {reply, finish_after_on_load, Result, Ref} ->
+ Result
+ end.
+
+do_finish_after_on_load(Mod, Keep) ->
+ erlang:finish_after_on_load(Mod, Keep),
+ case Keep of
+ true ->
+ ok;
+ false ->
+ case erts_internal:purge_module(Mod, prepare_on_load) of
false ->
- false;
+ true;
true ->
- erts_internal:purge_module(Mod),
- true
+ _ = check_proc_code(erlang:processes(), Mod, true),
+ true = erts_internal:purge_module(Mod, complete)
end
end.
+
+
%%
%% check_proc_code(Pids, Mod, Hard) - Send asynchronous
%% requests to all processes to perform a check_process_code
@@ -283,8 +345,7 @@ cpc_sched_kill(Pid,
cpc_request(#cpc_static{tag = Tag, module = Mod}, Pid, AllowGc) ->
erts_internal:check_process_code(Pid, Mod, [{async, {Tag, Pid, AllowGc}},
- {allow_gc, AllowGc},
- {copy_literals, true}]).
+ {allow_gc, AllowGc}]).
cpc_request_gc(CpcS, [Pid|Pids]) ->
cpc_request(CpcS, Pid, true),
@@ -297,3 +358,72 @@ cpc_init(CpcS, [Pid|Pids], NoReqs) ->
cpc_init(CpcS, Pids, NoReqs+1).
% end of check_proc_code() implementation.
+
+%%
+%% FOR TESTING ONLY
+%%
+%% do_test_purge() is for testing only. The purge is done
+%% as usual, but the tester can control when to enter the
+%% specific phases.
+%%
+do_test_purge(Mod, From, Type, Ref) when Type == true; Type == false ->
+ Mon = erlang:monitor(process, From),
+ Res = case Type of
+ true -> do_test_hard_purge(Mod, From, Ref, Mon);
+ false -> do_test_soft_purge(Mod, From, Ref, Mon)
+ end,
+ From ! {test_purge, Res, Ref},
+ erlang:demonitor(Mon, [flush]),
+ ok;
+do_test_purge(_, _, _, _) ->
+ ok.
+
+do_test_soft_purge(Mod, From, Ref, Mon) ->
+ PrepRes = erts_internal:purge_module(Mod, prepare),
+ TestRes = test_progress(started, From, Mon, Ref, ok),
+ case PrepRes of
+ false ->
+ _ = test_progress(continued, From, Mon, Ref, TestRes),
+ true;
+ true ->
+ Res = check_proc_code(erlang:processes(), Mod, false),
+ _ = test_progress(continued, From, Mon, Ref, TestRes),
+ erts_internal:purge_module(Mod,
+ case Res of
+ false -> abort;
+ true -> complete
+ end)
+ end.
+
+do_test_hard_purge(Mod, From, Ref, Mon) ->
+ PrepRes = erts_internal:purge_module(Mod, prepare),
+ TestRes = test_progress(started, From, Mon, Ref, ok),
+ case PrepRes of
+ false ->
+ _ = test_progress(continued, From, Mon, Ref, TestRes),
+ {false, false};
+ true ->
+ DidKill = check_proc_code(erlang:processes(), Mod, true),
+ _ = test_progress(continued, From, Mon, Ref, TestRes),
+ true = erts_internal:purge_module(Mod, complete),
+ {true, DidKill}
+ end.
+
+test_progress(_State, _From, _Mon, _Ref, died) ->
+ %% Test process died; continue so we wont
+ %% leave the system in an inconsistent
+ %% state...
+ died;
+test_progress(started, From, Mon, Ref, ok) ->
+ From ! {started, Ref},
+ receive
+ {'DOWN', Mon, process, From, _} -> died;
+ {continue, Ref} -> ok
+ end;
+test_progress(continued, From, Mon, Ref, ok) ->
+ From ! {continued, Ref},
+ receive
+ {'DOWN', Mon, process, From, _} -> died;
+ {complete, Ref} -> ok
+ end.
+
diff --git a/erts/preloaded/src/erts_dirty_process_code_checker.erl b/erts/preloaded/src/erts_dirty_process_code_checker.erl
new file mode 100644
index 0000000000..911642082c
--- /dev/null
+++ b/erts/preloaded/src/erts_dirty_process_code_checker.erl
@@ -0,0 +1,82 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2016. All Rights Reserved.
+%%
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+-module(erts_dirty_process_code_checker).
+
+-export([start/0]).
+
+%%
+%% The erts_dirty_process_code_checker is started at
+%% VM boot by the VM. It is a spawned as a system
+%% process, i.e, the whole VM will terminate if
+%% this process terminates.
+%%
+start() ->
+ process_flag(trap_exit, true),
+ msg_loop().
+
+msg_loop() ->
+ _ = receive
+ Request ->
+ handle_request(Request)
+ end,
+ msg_loop().
+
+check_process(Requester, Target, ReqId, Module) ->
+ Result = erts_internal:check_dirty_process_code(Target, Module),
+ Requester ! {check_process_code, ReqId, Result}.
+
+handle_request({Requester,
+ Target,
+ Prio,
+ {check_process_code,
+ ReqId,
+ Module,
+ _Flags} = Op}) ->
+ %%
+ %% Target may have stopped executing dirty since the
+ %% initial request was made. Check its current state
+ %% and try to send the request if possible; otherwise,
+ %% check the dirty executing process and send the result...
+ %%
+ try
+ case erts_internal:is_process_executing_dirty(Target) of
+ true ->
+ check_process(Requester, Target, ReqId, Module);
+ false ->
+ case erts_internal:request_system_task(Requester,
+ Target,
+ Prio,
+ Op) of
+ ok ->
+ ok;
+ dirty_execution ->
+ check_process(Requester, Target, ReqId, Module)
+ end
+ end
+ catch
+ _ : _ ->
+ ok %% Ignore all failures; someone passed us garbage...
+ end;
+handle_request(_Garbage) ->
+ ignore.
+
+
+
diff --git a/erts/preloaded/src/erts_internal.erl b/erts/preloaded/src/erts_internal.erl
index 2459ea2a2c..6aae5ba38c 100644
--- a/erts/preloaded/src/erts_internal.erl
+++ b/erts/preloaded/src/erts_internal.erl
@@ -38,11 +38,13 @@
-export([system_check/1,
gather_system_check_result/1]).
--export([request_system_task/3]).
+-export([request_system_task/3, request_system_task/4]).
-export([check_process_code/3]).
--export([copy_literals/2]).
--export([purge_module/1]).
+-export([check_dirty_process_code/2]).
+-export([is_process_executing_dirty/1]).
+-export([release_literal_area_switch/0]).
+-export([purge_module/2]).
-export([flush_monitor_messages/3]).
@@ -204,14 +206,25 @@ port_info(_Result, _Item) ->
-spec request_system_task(Pid, Prio, Request) -> 'ok' when
Prio :: 'max' | 'high' | 'normal' | 'low',
Request :: {'garbage_collect', term()}
- | {'check_process_code', term(), module(), non_neg_integer()},
+ | {'check_process_code', term(), module(), non_neg_integer()}
+ | {'copy_literals', term(), boolean()},
Pid :: pid().
request_system_task(_Pid, _Prio, _Request) ->
erlang:nif_error(undefined).
+-spec request_system_task(RequesterPid, TargetPid, Prio, Request) -> 'ok' | 'dirty_execution' when
+ Prio :: 'max' | 'high' | 'normal' | 'low',
+ Request :: {'garbage_collect', term()}
+ | {'check_process_code', term(), module(), non_neg_integer()}
+ | {'copy_literals', term(), boolean()},
+ RequesterPid :: pid(),
+ TargetPid :: pid().
+
+request_system_task(_RequesterPid, _TargetPid, _Prio, _Request) ->
+ erlang:nif_error(undefined).
+
-define(ERTS_CPC_ALLOW_GC, (1 bsl 0)).
--define(ERTS_CPC_COPY_LITERALS, (1 bsl 1)).
-spec check_process_code(Module, Flags) -> boolean() when
Module :: module(),
@@ -223,7 +236,7 @@ check_process_code(_Module, _Flags) ->
Pid :: pid(),
Module :: module(),
RequestId :: term(),
- Option :: {async, RequestId} | {allow_gc, boolean()} | {copy_literals, boolean()},
+ Option :: {async, RequestId} | {allow_gc, boolean()},
OptionList :: [Option],
CheckResult :: boolean() | aborted.
check_process_code(Pid, Module, OptionList) ->
@@ -265,8 +278,6 @@ get_cpc_opts([{async, _ReqId} = AsyncTuple | Options], _OldAsync, Flags) ->
get_cpc_opts(Options, AsyncTuple, Flags);
get_cpc_opts([{allow_gc, AllowGC} | Options], Async, Flags) ->
get_cpc_opts(Options, Async, cpc_flags(Flags, ?ERTS_CPC_ALLOW_GC, AllowGC));
-get_cpc_opts([{copy_literals, CopyLit} | Options], Async, Flags) ->
- get_cpc_opts(Options, Async, cpc_flags(Flags, ?ERTS_CPC_COPY_LITERALS, CopyLit));
get_cpc_opts([], Async, Flags) ->
{Async, Flags}.
@@ -275,15 +286,26 @@ cpc_flags(OldFlags, Bit, true) ->
cpc_flags(OldFlags, Bit, false) ->
OldFlags band (bnot Bit).
--spec copy_literals(Module,Bool) -> 'true' | 'false' | 'aborted' when
- Module :: module(),
- Bool :: boolean().
-copy_literals(_Mod, _Bool) ->
+-spec check_dirty_process_code(Pid,Module) -> 'true' | 'false' when
+ Pid :: pid(),
+ Module :: module().
+check_dirty_process_code(_Pid,_Module) ->
erlang:nif_error(undefined).
--spec purge_module(Module) -> boolean() when
- Module :: module().
-purge_module(_Module) ->
+-spec is_process_executing_dirty(Pid) -> 'true' | 'false' when
+ Pid :: pid().
+is_process_executing_dirty(_Pid) ->
+ erlang:nif_error(undefined).
+
+-spec release_literal_area_switch() -> 'true' | 'false'.
+
+release_literal_area_switch() ->
+ erlang:nif_error(undefined).
+
+-spec purge_module(Module, Op) -> boolean() when
+ Module :: module(),
+ Op :: 'prepare' | 'prepare_on_load' | 'abort' | 'complete'.
+purge_module(_Module, _Op) ->
erlang:nif_error(undefined).
-spec system_check(Type) -> 'ok' when
diff --git a/erts/preloaded/src/erts_literal_area_collector.erl b/erts/preloaded/src/erts_literal_area_collector.erl
new file mode 100644
index 0000000000..3befad8dfb
--- /dev/null
+++ b/erts/preloaded/src/erts_literal_area_collector.erl
@@ -0,0 +1,113 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2016. All Rights Reserved.
+%%
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
+%%
+%% %CopyrightEnd%
+%%
+-module(erts_literal_area_collector).
+
+-export([start/0]).
+
+%% Currently we only allow two outstanding literal
+%% copying jobs that garbage collect in order to
+%% copy the literals. Maybe we could allow more
+%% than two outstanding processes, but for now we
+%% play it safe...
+-define(MAX_GC_OUTSTND, 2).
+
+%%
+%% The erts_literal_area_collector is started at
+%% VM boot by the VM. It is a spawned as a system
+%% process, i.e, the whole VM will terminate if
+%% this process terminates.
+%%
+start() ->
+ process_flag(trap_exit, true),
+ msg_loop(undefined, 0, 0, []).
+
+%%
+%% The VM will send us a 'copy_literals' message
+%% when it has a new literal area that needs to
+%% be handled is added. We will also be informed
+%% about more areas when we call
+%% erts_internal:release_literal_area_switch().
+%%
+msg_loop(Area, Outstnd, GcOutstnd, NeedGC) ->
+ receive
+
+ %% A new area to handle has arrived...
+ copy_literals when Outstnd == 0 ->
+ switch_area();
+
+ %% Process (_Pid) has completed the request...
+ {copy_literals, {Area, _GcAllowed, _Pid}, ok} when Outstnd == 1 ->
+ switch_area(); %% Last process completed...
+ {copy_literals, {Area, false, _Pid}, ok} ->
+ msg_loop(Area, Outstnd-1, GcOutstnd, NeedGC);
+ {copy_literals, {Area, true, _Pid}, ok} when NeedGC == [] ->
+ msg_loop(Area, Outstnd-1, GcOutstnd-1, []);
+ {copy_literals, {Area, true, _Pid}, ok} ->
+ send_copy_req(hd(NeedGC), Area, true),
+ msg_loop(Area, Outstnd-1, GcOutstnd, tl(NeedGC));
+
+ %% Process (Pid) failed to complete the request
+ %% since it needs to garbage collect in order to
+ %% complete the request...
+ {copy_literals, {Area, false, Pid}, need_gc} when GcOutstnd < ?MAX_GC_OUTSTND ->
+ send_copy_req(Pid, Area, true),
+ msg_loop(Area, Outstnd, GcOutstnd+1, NeedGC);
+ {copy_literals, {Area, false, Pid}, need_gc} ->
+ msg_loop(Area, Outstnd, GcOutstnd, [Pid|NeedGC]);
+
+ %% Not handled message regarding the area that we
+ %% currently are working with. Crash the VM so
+ %% we notice this bug...
+ {copy_literals, {Area, _, _}, _} = Msg when erlang:is_reference(Area) ->
+ exit({not_handled_message, Msg});
+
+ %% Unexpected garbage message. Get rid of it...
+ _Ignore ->
+ msg_loop(Area, Outstnd, GcOutstnd, NeedGC)
+
+ end.
+
+switch_area() ->
+ Res = erts_internal:release_literal_area_switch(),
+ erlang:garbage_collect(), %% Almost no live data now...
+ case Res of
+ false ->
+ %% No more areas to handle...
+ msg_loop(undefined, 0, 0, []);
+ true ->
+ %% Send requests to all processes to copy
+ %% all live data they have referring to the
+ %% literal area that is to be released...
+ Area = make_ref(),
+ Outstnd = send_copy_reqs(erlang:processes(), Area, false),
+ msg_loop(Area, Outstnd, 0, [])
+ end.
+
+send_copy_reqs(Ps, Area, GC) ->
+ send_copy_reqs(Ps, Area, GC, 0).
+
+send_copy_reqs([], _Area, _GC, N) ->
+ N;
+send_copy_reqs([P|Ps], Area, GC, N) ->
+ send_copy_req(P, Area, GC),
+ send_copy_reqs(Ps, Area, GC, N+1).
+
+send_copy_req(P, Area, GC) ->
+ erts_internal:request_system_task(P, normal, {copy_literals, {Area, GC, P}, GC}).
diff --git a/erts/preloaded/src/init.erl b/erts/preloaded/src/init.erl
index 45468b3b9c..962528f7ab 100644
--- a/erts/preloaded/src/init.erl
+++ b/erts/preloaded/src/init.erl
@@ -423,9 +423,6 @@ loop(State) ->
Loaded = State#state.loaded, %% boot_loop but is handled here
From ! {init,Loaded}, %% anyway.
loop(State);
- {From, {ensure_loaded, _}} ->
- From ! {init, not_allowed},
- loop(State);
Msg ->
loop(handle_msg(Msg,State))
end.
@@ -465,6 +462,8 @@ do_handle_msg(Msg,State) ->
From ! {init,ok},
{new_state,State#state{subscribed = [Pid|Subscribed]}}
end;
+ {From, {ensure_loaded, _}} ->
+ From ! {init, not_allowed};
X ->
case whereis(user) of
undefined ->
@@ -670,9 +669,9 @@ unload(_) ->
do_unload(sub([heart|erlang:pre_loaded()],erlang:loaded())).
do_unload([M|Mods]) ->
- catch erts_internal:purge_module(M),
+ catch erlang:purge_module(M),
catch erlang:delete_module(M),
- catch erts_internal:purge_module(M),
+ catch erlang:purge_module(M),
do_unload(Mods);
do_unload([]) ->
purge_all_hipe_refs(),
diff --git a/erts/preloaded/src/prim_inet.erl b/erts/preloaded/src/prim_inet.erl
index 560810d222..61f727e8a4 100644
--- a/erts/preloaded/src/prim_inet.erl
+++ b/erts/preloaded/src/prim_inet.erl
@@ -347,7 +347,17 @@ accept_opts(L, S) ->
case getopts(L, [active, nodelay, keepalive, delay_send, priority, tos]) of
{ok, Opts} ->
case setopts(S, Opts) of
- ok -> {ok, S};
+ ok ->
+ case getopts(L, [tclass]) of
+ {ok, []} ->
+ {ok, S};
+ {ok, TClassOpts} ->
+ case setopts(S, TClassOpts) of
+ ok ->
+ {ok, S};
+ Error -> close(S), Error
+ end
+ end;
Error -> close(S), Error
end;
Error ->
@@ -1196,6 +1206,7 @@ enc_opt(sndbuf) -> ?INET_OPT_SNDBUF;
enc_opt(recbuf) -> ?INET_OPT_RCVBUF;
enc_opt(priority) -> ?INET_OPT_PRIORITY;
enc_opt(tos) -> ?INET_OPT_TOS;
+enc_opt(tclass) -> ?INET_OPT_TCLASS;
enc_opt(nodelay) -> ?TCP_OPT_NODELAY;
enc_opt(multicast_if) -> ?UDP_OPT_MULTICAST_IF;
enc_opt(multicast_ttl) -> ?UDP_OPT_MULTICAST_TTL;
@@ -1255,6 +1266,7 @@ dec_opt(?INET_OPT_SNDBUF) -> sndbuf;
dec_opt(?INET_OPT_RCVBUF) -> recbuf;
dec_opt(?INET_OPT_PRIORITY) -> priority;
dec_opt(?INET_OPT_TOS) -> tos;
+dec_opt(?INET_OPT_TCLASS) -> tclass;
dec_opt(?TCP_OPT_NODELAY) -> nodelay;
dec_opt(?UDP_OPT_MULTICAST_IF) -> multicast_if;
dec_opt(?UDP_OPT_MULTICAST_TTL) -> multicast_ttl;
@@ -1329,6 +1341,7 @@ type_opt_1(sndbuf) -> int;
type_opt_1(recbuf) -> int;
type_opt_1(priority) -> int;
type_opt_1(tos) -> int;
+type_opt_1(tclass) -> int;
type_opt_1(nodelay) -> bool;
type_opt_1(ipv6_v6only) -> bool;
%% multicast
@@ -2401,13 +2414,13 @@ get_addrs([F|Addrs]) ->
{Addr,Rest} = get_addr(F, Addrs),
[Addr|get_addrs(Rest)].
-get_addr(?INET_AF_LOCAL, [0]) ->
- {{local,<<>>},[]};
get_addr(?INET_AF_LOCAL, [N|Addr]) ->
{A,Rest} = lists:split(N, Addr),
{{local,iolist_to_binary(A)},Rest};
+get_addr(?INET_AF_UNSPEC, Rest) ->
+ {{unspec,<<>>},Rest};
get_addr(?INET_AF_UNDEFINED, Rest) ->
- {{undefined,0},Rest};
+ {{undefined,<<>>},Rest};
get_addr(Family, [P1,P0|Addr]) ->
{IP,Rest} = get_ip(Family, Addr),
{{IP,?u16(P1, P0)},Rest}.
diff --git a/erts/start_scripts/Makefile b/erts/start_scripts/Makefile
index dfd8153f32..ae2521474e 100644
--- a/erts/start_scripts/Makefile
+++ b/erts/start_scripts/Makefile
@@ -17,6 +17,9 @@
#
# %CopyrightEnd%
#
+
+.NOTPARALLEL:
+
include $(ERL_TOP)/make/target.mk
include $(ERL_TOP)/make/$(TARGET)/otp.mk
@@ -69,7 +72,7 @@ debug opt script: rel $(INSTALL_SCRIPTS) $(RELEASES_SRC)
rel: $(REL_SCRIPTS)
-RELEASES.src:
+RELEASES.src: $(SS_ROOT)/start_sasl.rel
$(gen_verbose)$(INSTALL_DIR) $(SS_TMP)
$(V_at)( cd $(SS_TMP) && \
$(ERL) -noinput +B -eval 'release_handler:create_RELEASES("%ERL_ROOT%", "$(SS_ROOT)", "$(SS_ROOT)/start_sasl.rel", []), halt()')
diff --git a/erts/test/run_erl_SUITE.erl b/erts/test/run_erl_SUITE.erl
index 47d38bde7c..fe1ccba1e2 100644
--- a/erts/test/run_erl_SUITE.erl
+++ b/erts/test/run_erl_SUITE.erl
@@ -22,6 +22,7 @@
-export([all/0, suite/0]).
-export([basic/1,heavy/1,heavier/1,defunct/1]).
+-export([sleepy_child/1]).
-export([ping_me_back/1]).
-include_lib("common_test/include/ct.hrl").
@@ -31,17 +32,17 @@ suite() ->
{timetrap, {minutes, 2}}].
all() ->
- [basic, heavy, heavier, defunct].
+ [basic, sleepy_child, heavy, heavier, defunct].
basic(Config) when is_list(Config) ->
case os:type() of
- {unix,_} -> basic_1(Config);
+ {unix,_} -> basic_1(Config, "basic", "");
_ -> {skip,"Not Unix"}
end.
-basic_1(Config) ->
- {Node,Pipe} = do_run_erl(Config, "basic"),
+basic_1(Config, Case, Opt) ->
+ {Node,Pipe} = do_run_erl(Config, Case, Opt),
ToErl = open_port({spawn,"to_erl "++Pipe}, []),
erlang:port_command(ToErl, "halt().\r\n"),
@@ -55,6 +56,12 @@ basic_1(Config) ->
ok.
+sleepy_child(Config) when is_list(Config) ->
+ case os:type() of
+ {unix,_} -> basic_1(Config, "sleepy_child", "-sleepy-child ");
+ _ -> {skip,"Not Unix"}
+ end.
+
heavy(Config) when is_list(Config) ->
case os:type() of
{unix,_} -> heavy_1(Config);
@@ -233,12 +240,14 @@ defunct_2(Config, Perl) ->
%%% Utilities.
do_run_erl(Config, Case) ->
+ do_run_erl(Config, Case, "").
+do_run_erl(Config, Case, Opt) ->
Priv = proplists:get_value(priv_dir, Config),
LogDir = filename:join(Priv, Case),
ok = file:make_dir(LogDir),
Pipe = LogDir ++ "/",
NodeName = "run_erl_node_" ++ Case,
- Cmd = "run_erl "++Pipe++" "++LogDir++" \"erl -sname " ++ NodeName ++
+ Cmd = "run_erl "++Opt++Pipe++" "++LogDir++" \"erl -sname " ++ NodeName ++
" -pa " ++ filename:dirname(code:which(?MODULE)) ++
" -s " ++ ?MODULE_STRING ++ " ping_me_back " ++
atom_to_list(node()) ++ "\"",
diff --git a/erts/test/z_SUITE.erl b/erts/test/z_SUITE.erl
index 281a47134f..d474c71c4f 100644
--- a/erts/test/z_SUITE.erl
+++ b/erts/test/z_SUITE.erl
@@ -214,7 +214,19 @@ dump_core(#core_search_conf{ cerl = Cerl }, Core) ->
format_core(Conf, {ignore, Core}) ->
format_core(Conf, Core, "[ignored] ");
format_core(Conf, Core) ->
- format_core(Conf, Core, "").
+ format_core(Conf, Core, ""),
+
+ %% Try print (log dir) name of offending application
+ CoreDir = filename:dirname(Core),
+ lists:foreach(fun(TestDir) ->
+ case filelib:is_dir(filename:join(CoreDir,TestDir)) of
+ true ->
+ io:format(" from ~s~n", [TestDir]);
+ false ->
+ no
+ end
+ end,
+ filelib:wildcard("*.logs", CoreDir)).
format_core(#core_search_conf{file = false}, Core, Ignore) ->
io:format(" ~s~s " ++ time_fstr() ++ "~s~n",
diff --git a/erts/vsn.mk b/erts/vsn.mk
index 924558dab1..ad9148f61f 100644
--- a/erts/vsn.mk
+++ b/erts/vsn.mk
@@ -18,7 +18,7 @@
# %CopyrightEnd%
#
-VSN = 8.0.1
+VSN = 8.1
# Port number 4365 in 4.2
# Port number 4366 in 4.3
diff --git a/lib/asn1/doc/src/asn1_getting_started.xml b/lib/asn1/doc/src/asn1_getting_started.xml
index 3c8ec24723..d40b294c39 100644
--- a/lib/asn1/doc/src/asn1_getting_started.xml
+++ b/lib/asn1/doc/src/asn1_getting_started.xml
@@ -678,7 +678,7 @@ ok
1081,32,1043,1085,1086,1084]</pre>
<p>For details, see the <seealso marker="stdlib:unicode">unicode</seealso>
- module in <c>stdlib</c>.</p>
+ module in STDLIB.</p>
<p>In the following example, this ASN.1 specification is used:</p>
<pre>
diff --git a/lib/asn1/doc/src/asn1_introduction.xml b/lib/asn1/doc/src/asn1_introduction.xml
index d8b81aa467..e4f406364d 100644
--- a/lib/asn1/doc/src/asn1_introduction.xml
+++ b/lib/asn1/doc/src/asn1_introduction.xml
@@ -30,7 +30,7 @@
<file>asn1_introduction.xml</file>
</header>
- <p>The <c>ASN.1</c> application provides the following:</p>
+ <p>The ASN.1 application provides the following:</p>
<list type="bulleted">
<item>An ASN.1 compiler for Erlang, which generates encode and
diff --git a/lib/asn1/doc/src/notes.xml b/lib/asn1/doc/src/notes.xml
index 68d335f451..499a7e40c3 100644
--- a/lib/asn1/doc/src/notes.xml
+++ b/lib/asn1/doc/src/notes.xml
@@ -32,6 +32,22 @@
<p>This document describes the changes made to the asn1 application.</p>
+<section><title>Asn1 4.0.4</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Compiling multiple ASN.1 modules in the same directory
+ with parallel make (make -j) should now be safe.</p>
+ <p>
+ Own Id: OTP-13624</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Asn1 4.0.3</title>
<section><title>Improvements and New Features</title>
@@ -329,7 +345,7 @@
</item>
<item>
<p>
- The <c>asn1</c> application would fail to build if the
+ The ASN.1 application would fail to build if the
<c>.erlang</c> file printed something to standard output.</p>
<p>
Own Id: OTP-11360</p>
@@ -997,7 +1013,7 @@
also been extended. </item><item> The <c>configure</c>
scripts of <c>erl_interface</c> and <c>odbc</c> now
search for thread libraries and thread library quirks the
- same way as <c>erts</c> do. </item><item> The
+ same way as ERTS do. </item><item> The
<c>configure</c> script of the <c>odbc</c> application
now also looks for odbc libraries in <c>lib64</c> and
<c>lib/64</c> directories when building on a 64-bit
diff --git a/lib/asn1/src/asn1_db.erl b/lib/asn1/src/asn1_db.erl
index 557eca0ffd..869ea310aa 100644
--- a/lib/asn1/src/asn1_db.erl
+++ b/lib/asn1/src/asn1_db.erl
@@ -106,7 +106,9 @@ loop(#state{parent = Parent, monitor = MRef, table = Table,
loop(State);
{save, OutFile, Mod} ->
[{_,Mtab}] = ets:lookup(Table, Mod),
- ok = ets:tab2file(Mtab, OutFile),
+ TempFile = OutFile ++ ".#temp",
+ ok = ets:tab2file(Mtab, TempFile),
+ ok = file:rename(TempFile, OutFile),
loop(State);
{From, {new, Mod, Erule}} ->
[] = ets:lookup(Table, Mod), %Assertion.
diff --git a/lib/asn1/src/asn1ct.erl b/lib/asn1/src/asn1ct.erl
index dd269f095d..8783b5418d 100644
--- a/lib/asn1/src/asn1ct.erl
+++ b/lib/asn1/src/asn1ct.erl
@@ -221,9 +221,8 @@ check_pass(#st{code=M,file=File,includes=Includes,
{error,St#st{error=Reason}}
end.
-save_pass(#st{code=M,erule=Erule,dbfile=DbFile}=St) ->
+save_pass(#st{code=M,erule=Erule}=St) ->
ok = asn1ct_check:storeindb(#state{erule=Erule}, M),
- asn1_db:dbsave(DbFile,M#module.name),
{ok,St}.
parse_listing(#st{code=Code,outfile=OutFile0}=St) ->
diff --git a/lib/asn1/test/asn1_test_lib.erl b/lib/asn1/test/asn1_test_lib.erl
index 1b4c3b3c77..dc614db4f2 100644
--- a/lib/asn1/test/asn1_test_lib.erl
+++ b/lib/asn1/test/asn1_test_lib.erl
@@ -34,12 +34,16 @@ run_dialyzer() ->
compile(File, Config, Options) -> compile_all([File], Config, Options).
-compile_all(Files, Config, Options) ->
+compile_all(Files, Config, Options0) ->
DataDir = proplists:get_value(data_dir, Config),
CaseDir = proplists:get_value(case_dir, Config),
- [compile_file(filename:join(DataDir, F), [{outdir, CaseDir},
- debug_info|Options])
- || F <- Files],
+ Options = [{outdir,CaseDir},debug_info|Options0],
+
+ Comp = fun(F) ->
+ compile_file(filename:join(DataDir, F), Options)
+ end,
+ p_run(Comp, Files),
+
dialyze(Files, Options),
ok.
@@ -94,9 +98,9 @@ compile_file(File, Options) ->
try
ok = asn1ct:compile(File, [warnings_as_errors|Options])
catch
- Class:Reason ->
- ct:print("Failed to compile ~s\n", [File]),
- erlang:error({compile_failed, {File, Options}, {Class, Reason}})
+ _:Reason ->
+ ct:print("Failed to compile ~s\n~p", [File,Reason]),
+ error
end.
compile_erlang(Mod, Config, Options) ->
@@ -219,3 +223,38 @@ ber_get_len(<<0:1,L:7,T/binary>>) ->
ber_get_len(<<1:1,Octets:7,T0/binary>>) ->
<<L:Octets/unit:8,T/binary>> = T0,
{L,T}.
+
+%% p_run(fun(Data) -> ok|error, List) -> ok
+%% Will fail the test case if there were any errors.
+
+p_run(Test, List) ->
+ S = erlang:system_info(schedulers),
+ N = case test_server:is_cover() of
+ false ->
+ S + 1;
+ true ->
+ %% Cover is running. Using too many processes
+ %% could slow us down.
+ min(S, 4)
+ end,
+ %%io:format("p_run: ~p parallel processes\n", [N]),
+ p_run_loop(Test, List, N, [], 0).
+
+p_run_loop(_, [], _, [], Errors) ->
+ case Errors of
+ 0 -> ok;
+ N -> ct:fail({N,errors})
+ end;
+p_run_loop(Test, [H|T], N, Refs, Errors) when length(Refs) < N ->
+ {_,Ref} = erlang:spawn_monitor(fun() -> exit(Test(H)) end),
+ p_run_loop(Test, T, N, [Ref|Refs], Errors);
+p_run_loop(Test, List, N, Refs0, Errors0) ->
+ receive
+ {'DOWN',Ref,process,_,Res} ->
+ Errors = case Res of
+ ok -> Errors0;
+ error -> Errors0+1
+ end,
+ Refs = Refs0 -- [Ref],
+ p_run_loop(Test, List, N, Refs, Errors)
+ end.
diff --git a/lib/asn1/vsn.mk b/lib/asn1/vsn.mk
index 527af05da1..e4bf3e2236 100644
--- a/lib/asn1/vsn.mk
+++ b/lib/asn1/vsn.mk
@@ -1 +1 @@
-ASN1_VSN = 4.0.3
+ASN1_VSN = 4.0.4
diff --git a/lib/common_test/doc/src/common_test_app.xml b/lib/common_test/doc/src/common_test_app.xml
index 3f83747485..48ffe653e4 100644
--- a/lib/common_test/doc/src/common_test_app.xml
+++ b/lib/common_test/doc/src/common_test_app.xml
@@ -566,7 +566,7 @@
(which also causes the test case process to terminate).</p>
<p>Elements from the <c>Config</c> list can, for example, be read
- with <c>proplists:get_value/2</c> in <c>STDLIB</c>
+ with <c>proplists:get_value/2</c> in STDLIB
(or the macro <c>?config</c> defined in <c>ct.hrl</c>).</p>
<p>If you decide not to run the test case after all, return
diff --git a/lib/common_test/doc/src/ct.xml b/lib/common_test/doc/src/ct.xml
index 264bcff251..53ef41dd5b 100644
--- a/lib/common_test/doc/src/ct.xml
+++ b/lib/common_test/doc/src/ct.xml
@@ -620,6 +620,21 @@
</func>
<func>
+ <name>get_verbosity(Category) -&gt; Level | undefined</name>
+ <fsummary>Read the verbosity level for a logging category.</fsummary>
+ <type>
+ <v>Category = default | atom()</v>
+ <v>Level = integer()</v>
+ </type>
+ <desc><marker id="get_verbosity-1"/>
+ <p>This function returns the verbosity level for the specified logging
+ category. See the <seealso marker="write_test_chapter#logging">
+ User's Guide</seealso> for details. Use the value <c>default</c> to read
+ the general verbosity level.</p>
+ </desc>
+ </func>
+
+ <func>
<name>install(Opts) -&gt; ok | {error, Reason}</name>
<fsummary>Installs configuration files and event handlers.</fsummary>
<type>
@@ -777,7 +792,7 @@
caught by any installed event manager.</p>
<p>See also
- <seealso marker="stdlib:gen_event"><c>stdlib:gen_event(3)</c></seealso>.</p>
+ <seealso marker="stdlib:gen_event"><c>gen_event(3)</c></seealso>.</p>
</desc>
</func>
@@ -1225,6 +1240,21 @@
</func>
<func>
+ <name>set_verbosity(Category, Level) -&gt; ok</name>
+ <fsummary>Set the verbosity level for a logging category.</fsummary>
+ <type>
+ <v>Category = default | atom()</v>
+ <v>Level = integer()</v>
+ </type>
+ <desc><marker id="set_verbosity-2"/>
+ <p>Use this function to set, or modify, the verbosity level for a logging
+ category. See the <seealso marker="write_test_chapter#logging">
+ User's Guide</seealso> for details. Use the value <c>default</c> to set the
+ general verbosity level.</p>
+ </desc>
+ </func>
+
+ <func>
<name>sleep(Time) -&gt; ok</name>
<fsummary>This function, similar to timer:sleep/1, suspends the
test case for a specified time.</fsummary>
@@ -1236,7 +1266,7 @@
<v>Millisecs = integer() | float()</v>
</type>
<desc><marker id="sleep-1"/>
- <p>This function, similar to <c>timer:sleep/1</c> in <c>STDLIB</c>,
+ <p>This function, similar to <c>timer:sleep/1</c> in STDLIB,
suspends the test case for a specified time.
However, this function also multiplies <c>Time</c> with the
<c>multiply_timetraps</c> value (if set) and under certain
@@ -1330,7 +1360,7 @@
caught by any installed event manager.</p>
<p>See also
- <seealso marker="stdlib:gen_event"><c>stdlib:gen_event(3)</c></seealso>.
+ <seealso marker="stdlib:gen_event"><c>gen_event(3)</c></seealso>.
</p>
</desc>
</func>
diff --git a/lib/common_test/doc/src/ct_hooks.xml b/lib/common_test/doc/src/ct_hooks.xml
index 3b1e564b66..c2cf29c530 100644
--- a/lib/common_test/doc/src/ct_hooks.xml
+++ b/lib/common_test/doc/src/ct_hooks.xml
@@ -94,7 +94,7 @@
<seealso marker="#Module:id-1"><c>ct_hooks:id/1</c></seealso>,
or a <c>reference</c> (created using
<seealso marker="erts:erlang#make_ref-0">erlang:make_ref/0</seealso>
- in <c>ERTS</c>) if
+ in ERTS) if
<seealso marker="#Module:id-1"><c>ct_hooks:id/1</c></seealso>
is not implemented.</p>
diff --git a/lib/common_test/doc/src/ct_hooks_chapter.xml b/lib/common_test/doc/src/ct_hooks_chapter.xml
index 1998f15697..0e4c35e11f 100644
--- a/lib/common_test/doc/src/ct_hooks_chapter.xml
+++ b/lib/common_test/doc/src/ct_hooks_chapter.xml
@@ -374,7 +374,7 @@
<title>Example CTH</title>
<p>The following CTH logs information about a test run into a format
parseable by <seealso marker="kernel:file#consult-1">file:consult/1</seealso>
- (in <c>Kernel</c>):
+ (in Kernel):
</p>
<code>
%%% @doc Common Test Example Common Test Hook module.
@@ -499,13 +499,13 @@
<tag><c>cth_log_redirect</c></tag>
<item>
<p>Built-in</p>
- <p>Captures all <c>error_logger</c> and <c>SASL</c> logging
+ <p>Captures all <c>error_logger</c> and SASL logging
events and prints them to the current test case log. If an event cannot be
associated with a test case, it is printed in the <c>Common Test</c> framework log.
This happens for test cases running in parallel and events occuring
in-between test cases. You can configure the level of
- <seealso marker="sasl:sasl_app"><c>SASL</c></seealso> events report
- using the normal <c>SASL</c> mechanisms.</p>
+ <seealso marker="sasl:sasl_app">SASL</seealso> events report
+ using the normal SASL mechanisms.</p>
</item>
<tag><c>cth_surefire</c></tag>
<item>
diff --git a/lib/common_test/doc/src/ct_ssh.xml b/lib/common_test/doc/src/ct_ssh.xml
index d00737aa5a..137e4c3f1d 100644
--- a/lib/common_test/doc/src/ct_ssh.xml
+++ b/lib/common_test/doc/src/ct_ssh.xml
@@ -64,7 +64,7 @@
<p><c>ConnType = ssh | sftp</c>.</p>
<p>For other types, see
- <seealso marker="ssh:ssh"><c>ssh:ssh(3)</c></seealso>.</p>
+ <seealso marker="ssh:ssh"><c>ssh(3)</c></seealso>.</p>
<p>All time-out parameters in <c>ct_ssh</c> functions are values in
milliseconds.</p>
@@ -88,7 +88,7 @@
<tag><c>ssh_sftp_return() = term()</c></tag>
<item><marker id="type-ssh_sftp_return"/>
<p>Return value from an
- <seealso marker="ssh:ssh_sftp"><c>ssh:ssh_sftp</c></seealso>
+ <seealso marker="ssh:ssh_sftp"><c>ssh_sftp</c></seealso>
function.</p></item>
</taglist>
</section>
@@ -104,7 +104,7 @@
</type>
<desc><marker id="apread-4"/>
<p>For information and other types, see
- <seealso marker="ssh:ssh_sftp"><c>ssh:ssh_sftp(3)</c></seealso>.</p>
+ <seealso marker="ssh:ssh_sftp"><c>ssh_sftp(3)</c></seealso>.</p>
</desc>
</func>
@@ -118,7 +118,7 @@
</type>
<desc><marker id="apread-5"/>
<p>For information and other types, see
- <seealso marker="ssh:ssh_sftp"><c>ssh:ssh_sftp(3)</c></seealso>.</p>
+ <seealso marker="ssh:ssh_sftp"><c>ssh_sftp(3)</c></seealso>.</p>
</desc>
</func>
@@ -132,7 +132,7 @@
</type>
<desc><marker id="apwrite-4"/>
<p>For information and other types, see
- <seealso marker="ssh:ssh_sftp"><c>ssh:ssh_sftp(3)</c></seealso>.</p>
+ <seealso marker="ssh:ssh_sftp"><c>ssh_sftp(3)</c></seealso>.</p>
</desc>
</func>
@@ -146,7 +146,7 @@
</type>
<desc><marker id="apwrite-5"/>
<p>For information and other types, see
- <seealso marker="ssh:ssh_sftp"><c>ssh:ssh_sftp(3)</c></seealso>.</p>
+ <seealso marker="ssh:ssh_sftp"><c>ssh_sftp(3)</c></seealso>.</p>
</desc>
</func>
@@ -160,7 +160,7 @@
</type>
<desc><marker id="aread-3"/>
<p>For information and other types, see
- <seealso marker="ssh:ssh_sftp"><c>ssh:ssh_sftp(3)</c></seealso>.</p>
+ <seealso marker="ssh:ssh_sftp"><c>ssh_sftp(3)</c></seealso>.</p>
</desc>
</func>
@@ -174,7 +174,7 @@
</type>
<desc><marker id="aread-4"/>
<p>For information and other types, see
- <seealso marker="ssh:ssh_sftp"><c>ssh:ssh_sftp(3)</c></seealso>.</p>
+ <seealso marker="ssh:ssh_sftp"><c>ssh_sftp(3)</c></seealso>.</p>
</desc>
</func>
@@ -188,7 +188,7 @@
</type>
<desc><marker id="awrite-3"/>
<p>For information and other types, see
- <seealso marker="ssh:ssh_sftp"><c>ssh:ssh_sftp(3)</c></seealso>.</p>
+ <seealso marker="ssh:ssh_sftp"><c>ssh_sftp(3)</c></seealso>.</p>
</desc>
</func>
@@ -202,7 +202,7 @@
</type>
<desc><marker id="awrite-4"/>
<p>For information and other types, see
- <seealso marker="ssh:ssh_sftp"><c>ssh:ssh_sftp(3)</c></seealso>.</p>
+ <seealso marker="ssh:ssh_sftp"><c>ssh_sftp(3)</c></seealso>.</p>
</desc>
</func>
@@ -216,7 +216,7 @@
</type>
<desc><marker id="close-2"/>
<p>For information and other types, see
- <seealso marker="ssh:ssh_sftp"><c>ssh:ssh_sftp(3)</c></seealso>.</p>
+ <seealso marker="ssh:ssh_sftp"><c>ssh_sftp(3)</c></seealso>.</p>
</desc>
</func>
@@ -230,7 +230,7 @@
</type>
<desc><marker id="close-3"/>
<p>For information and other types, see
- <seealso marker="ssh:ssh_sftp"><c>ssh:ssh_sftp(3)</c></seealso>.</p>
+ <seealso marker="ssh:ssh_sftp"><c>ssh_sftp(3)</c></seealso>.</p>
</desc>
</func>
@@ -310,7 +310,7 @@
</type>
<desc><marker id="del_dir-2"/>
<p>For information and other types, see
- <seealso marker="ssh:ssh_sftp"><c>ssh:ssh_sftp(3)</c></seealso>.</p>
+ <seealso marker="ssh:ssh_sftp"><c>ssh_sftp(3)</c></seealso>.</p>
</desc>
</func>
@@ -324,7 +324,7 @@
</type>
<desc><marker id="del_dir-3"/>
<p>For information and other types, see
- <seealso marker="ssh:ssh_sftp"><c>ssh:ssh_sftp(3)</c></seealso>.</p>
+ <seealso marker="ssh:ssh_sftp"><c>ssh_sftp(3)</c></seealso>.</p>
</desc>
</func>
@@ -338,7 +338,7 @@
</type>
<desc><marker id="delete-2"/>
<p>For information and other types, see
- <seealso marker="ssh:ssh_sftp"><c>ssh:ssh_sftp(3)</c></seealso>.</p>
+ <seealso marker="ssh:ssh_sftp"><c>ssh_sftp(3)</c></seealso>.</p>
</desc>
</func>
@@ -352,7 +352,7 @@
</type>
<desc><marker id="delete-3"/>
<p>For information and other types, see
- <seealso marker="ssh:ssh_sftp"><c>ssh:ssh_sftp(3)</c></seealso>.</p>
+ <seealso marker="ssh:ssh_sftp"><c>ssh_sftp(3)</c></seealso>.</p>
</desc>
</func>
@@ -423,7 +423,7 @@
</type>
<desc><marker id="get_file_info-2"/>
<p>For information and other types, see
- <seealso marker="ssh:ssh_sftp"><c>ssh:ssh_sftp(3)</c></seealso>.</p>
+ <seealso marker="ssh:ssh_sftp"><c>ssh_sftp(3)</c></seealso>.</p>
</desc>
</func>
@@ -437,7 +437,7 @@
</type>
<desc><marker id="get_file_info-3"/>
<p>For information and other types, see
- <seealso marker="ssh:ssh_sftp"><c>ssh:ssh_sftp(3)</c></seealso>.</p>
+ <seealso marker="ssh:ssh_sftp"><c>ssh_sftp(3)</c></seealso>.</p>
</desc>
</func>
@@ -451,7 +451,7 @@
</type>
<desc><marker id="list_dir-2"/>
<p>For information and other types, see
- <seealso marker="ssh:ssh_sftp"><c>ssh:ssh_sftp(3)</c></seealso>.</p>
+ <seealso marker="ssh:ssh_sftp"><c>ssh_sftp(3)</c></seealso>.</p>
</desc>
</func>
@@ -465,7 +465,7 @@
</type>
<desc><marker id="list_dir-3"/>
<p>For information and other types, see
- <seealso marker="ssh:ssh_sftp"><c>ssh:ssh_sftp(3)</c></seealso>.</p>
+ <seealso marker="ssh:ssh_sftp"><c>ssh_sftp(3)</c></seealso>.</p>
</desc>
</func>
@@ -479,7 +479,7 @@
</type>
<desc><marker id="make_dir-2"/>
<p>For information and other types, see
- <seealso marker="ssh:ssh_sftp"><c>ssh:ssh_sftp(3)</c></seealso>.</p>
+ <seealso marker="ssh:ssh_sftp"><c>ssh_sftp(3)</c></seealso>.</p>
</desc>
</func>
@@ -493,7 +493,7 @@
</type>
<desc><marker id="make_dir-3"/>
<p>For information and other types, see
- <seealso marker="ssh:ssh_sftp"><c>ssh:ssh_sftp(3)</c></seealso>.</p>
+ <seealso marker="ssh:ssh_sftp"><c>ssh_sftp(3)</c></seealso>.</p>
</desc>
</func>
@@ -507,7 +507,7 @@
</type>
<desc><marker id="make_symlink-3"/>
<p>For information and other types, see
- <seealso marker="ssh:ssh_sftp"><c>ssh:ssh_sftp(3)</c></seealso>.</p>
+ <seealso marker="ssh:ssh_sftp"><c>ssh_sftp(3)</c></seealso>.</p>
</desc>
</func>
@@ -521,7 +521,7 @@
</type>
<desc><marker id="make_symlink-4"/>
<p>For information and other types, see
- <seealso marker="ssh:ssh_sftp"><c>ssh:ssh_sftp(3)</c></seealso>.</p>
+ <seealso marker="ssh:ssh_sftp"><c>ssh_sftp(3)</c></seealso>.</p>
</desc>
</func>
@@ -535,7 +535,7 @@
</type>
<desc><marker id="open-3"/>
<p>For information and other types, see
- <seealso marker="ssh:ssh_sftp"><c>ssh:ssh_sftp(3)</c></seealso>.</p>
+ <seealso marker="ssh:ssh_sftp"><c>ssh_sftp(3)</c></seealso>.</p>
</desc>
</func>
@@ -549,7 +549,7 @@
</type>
<desc><marker id="open-4"/>
<p>For information and other types, see
- <seealso marker="ssh:ssh_sftp"><c>ssh:ssh_sftp(3)</c></seealso>.</p>
+ <seealso marker="ssh:ssh_sftp"><c>ssh_sftp(3)</c></seealso>.</p>
</desc>
</func>
@@ -563,7 +563,7 @@
</type>
<desc><marker id="opendir-2"/>
<p>For information and other types, see
- <seealso marker="ssh:ssh_sftp"><c>ssh:ssh_sftp(3)</c></seealso>.</p>
+ <seealso marker="ssh:ssh_sftp"><c>ssh_sftp(3)</c></seealso>.</p>
</desc>
</func>
@@ -577,7 +577,7 @@
</type>
<desc><marker id="opendir-3"/>
<p>For information and other types, see
- <seealso marker="ssh:ssh_sftp"><c>ssh:ssh_sftp(3)</c></seealso>.</p>
+ <seealso marker="ssh:ssh_sftp"><c>ssh_sftp(3)</c></seealso>.</p>
</desc>
</func>
@@ -591,7 +591,7 @@
</type>
<desc><marker id="position-3"/>
<p>For information and other types, see
- <seealso marker="ssh:ssh_sftp"><c>ssh:ssh_sftp(3)</c></seealso>.</p>
+ <seealso marker="ssh:ssh_sftp"><c>ssh_sftp(3)</c></seealso>.</p>
</desc>
</func>
@@ -605,7 +605,7 @@
</type>
<desc><marker id="position-4"/>
<p>For information and other types, see
- <seealso marker="ssh:ssh_sftp"><c>ssh:ssh_sftp(3)</c></seealso>.</p>
+ <seealso marker="ssh:ssh_sftp"><c>ssh_sftp(3)</c></seealso>.</p>
</desc>
</func>
@@ -619,7 +619,7 @@
</type>
<desc><marker id="pread-4"/>
<p>For information and other types, see
- <seealso marker="ssh:ssh_sftp"><c>ssh:ssh_sftp(3)</c></seealso>.</p>
+ <seealso marker="ssh:ssh_sftp"><c>ssh_sftp(3)</c></seealso>.</p>
</desc>
</func>
@@ -633,7 +633,7 @@
</type>
<desc><marker id="pread-5"/>
<p>For information and other types, see
- <seealso marker="ssh:ssh_sftp"><c>ssh:ssh_sftp(3)</c></seealso>.</p>
+ <seealso marker="ssh:ssh_sftp"><c>ssh_sftp(3)</c></seealso>.</p>
</desc>
</func>
@@ -647,7 +647,7 @@
</type>
<desc><marker id="pwrite-4"/>
<p>For information and other types, see
- <seealso marker="ssh:ssh_sftp"><c>ssh:ssh_sftp(3)</c></seealso>.</p>
+ <seealso marker="ssh:ssh_sftp"><c>ssh_sftp(3)</c></seealso>.</p>
</desc>
</func>
@@ -661,7 +661,7 @@
</type>
<desc><marker id="pwrite-5"/>
<p>For information and other types, see
- <seealso marker="ssh:ssh_sftp"><c>ssh:ssh_sftp(3)</c></seealso>.</p>
+ <seealso marker="ssh:ssh_sftp"><c>ssh_sftp(3)</c></seealso>.</p>
</desc>
</func>
@@ -675,7 +675,7 @@
</type>
<desc><marker id="read-3"/>
<p>For information and other types, see
- <seealso marker="ssh:ssh_sftp"><c>ssh:ssh_sftp(3)</c></seealso>.</p>
+ <seealso marker="ssh:ssh_sftp"><c>ssh_sftp(3)</c></seealso>.</p>
</desc>
</func>
@@ -689,7 +689,7 @@
</type>
<desc><marker id="read-4"/>
<p>For information and other types, see
- <seealso marker="ssh:ssh_sftp"><c>ssh:ssh_sftp(3)</c></seealso>.</p>
+ <seealso marker="ssh:ssh_sftp"><c>ssh_sftp(3)</c></seealso>.</p>
</desc>
</func>
@@ -703,7 +703,7 @@
</type>
<desc><marker id="read_file-2"/>
<p>For information and other types, see
- <seealso marker="ssh:ssh_sftp"><c>ssh:ssh_sftp(3)</c></seealso>.</p>
+ <seealso marker="ssh:ssh_sftp"><c>ssh_sftp(3)</c></seealso>.</p>
</desc>
</func>
@@ -717,7 +717,7 @@
</type>
<desc><marker id="read_file-3"/>
<p>For information and other types, see
- <seealso marker="ssh:ssh_sftp"><c>ssh:ssh_sftp(3)</c></seealso>.</p>
+ <seealso marker="ssh:ssh_sftp"><c>ssh_sftp(3)</c></seealso>.</p>
</desc>
</func>
@@ -731,7 +731,7 @@
</type>
<desc><marker id="read_file_info-2"/>
<p>For information and other types, see
- <seealso marker="ssh:ssh_sftp"><c>ssh:ssh_sftp(3)</c></seealso>.</p>
+ <seealso marker="ssh:ssh_sftp"><c>ssh_sftp(3)</c></seealso>.</p>
</desc>
</func>
@@ -745,7 +745,7 @@
</type>
<desc><marker id="read_file_info-3"/>
<p>For information and other types, see
- <seealso marker="ssh:ssh_sftp"><c>ssh:ssh_sftp(3)</c></seealso>.</p>
+ <seealso marker="ssh:ssh_sftp"><c>ssh_sftp(3)</c></seealso>.</p>
</desc>
</func>
@@ -759,7 +759,7 @@
</type>
<desc><marker id="read_link-2"/>
<p>For information and other types, see
- <seealso marker="ssh:ssh_sftp"><c>ssh:ssh_sftp(3)</c></seealso>.</p>
+ <seealso marker="ssh:ssh_sftp"><c>ssh_sftp(3)</c></seealso>.</p>
</desc>
</func>
@@ -773,7 +773,7 @@
</type>
<desc><marker id="read_link-3"/>
<p>For information and other types, see
- <seealso marker="ssh:ssh_sftp"><c>ssh:ssh_sftp(3)</c></seealso>.</p>
+ <seealso marker="ssh:ssh_sftp"><c>ssh_sftp(3)</c></seealso>.</p>
</desc>
</func>
@@ -787,7 +787,7 @@
</type>
<desc><marker id="read_link_info-2"/>
<p>For information and other types, see
- <seealso marker="ssh:ssh_sftp"><c>ssh:ssh_sftp(3)</c></seealso>.</p>
+ <seealso marker="ssh:ssh_sftp"><c>ssh_sftp(3)</c></seealso>.</p>
</desc>
</func>
@@ -801,7 +801,7 @@
</type>
<desc><marker id="read_link_info-3"/>
<p>For information and other types, see
- <seealso marker="ssh:ssh_sftp"><c>ssh:ssh_sftp(3)</c></seealso>.</p>
+ <seealso marker="ssh:ssh_sftp"><c>ssh_sftp(3)</c></seealso>.</p>
</desc>
</func>
@@ -853,7 +853,7 @@ ChannelId, End, DefaultTimeout)</c></seealso>.</p>
<p>If <c>End</c> is a fun, this fun is called with one argument, the
data value in a received <c>ssh_cm</c> message (see
- <seealso marker="ssh:ssh_connection"><c>ssh:ssh_connection(3)</c></seealso>.
+ <seealso marker="ssh:ssh_connection"><c>ssh_connection(3)</c></seealso>.
The fun is to return either <c>true</c> to end the receiving
operation (and have the so far collected data returned) or
<c>false</c> to wait for more data from the server. Even if a fun
@@ -872,7 +872,7 @@ ChannelId, End, DefaultTimeout)</c></seealso>.</p>
</type>
<desc><marker id="rename-3"/>
<p>For information and other types, see
- <seealso marker="ssh:ssh_sftp"><c>ssh:ssh_sftp(3)</c></seealso>.</p>
+ <seealso marker="ssh:ssh_sftp"><c>ssh_sftp(3)</c></seealso>.</p>
</desc>
</func>
@@ -886,7 +886,7 @@ ChannelId, End, DefaultTimeout)</c></seealso>.</p>
</type>
<desc><marker id="rename-4"/>
<p>For information and other types, see
- <seealso marker="ssh:ssh_sftp"><c>ssh:ssh_sftp(3)</c></seealso>.</p>
+ <seealso marker="ssh:ssh_sftp"><c>ssh_sftp(3)</c></seealso>.</p>
</desc>
</func>
@@ -1070,7 +1070,7 @@ ChannelId, 0, Data, End, Timeout)</c></seealso>.</p>
</type>
<desc><marker id="write-3"/>
<p>For information and other types, see
- <seealso marker="ssh:ssh_sftp"><c>ssh:ssh_sftp(3)</c></seealso>.</p>
+ <seealso marker="ssh:ssh_sftp"><c>ssh_sftp(3)</c></seealso>.</p>
</desc>
</func>
@@ -1084,7 +1084,7 @@ ChannelId, 0, Data, End, Timeout)</c></seealso>.</p>
</type>
<desc><marker id="write-4"/>
<p>For information and other types, see
- <seealso marker="ssh:ssh_sftp"><c>ssh:ssh_sftp(3)</c></seealso>.</p>
+ <seealso marker="ssh:ssh_sftp"><c>ssh_sftp(3)</c></seealso>.</p>
</desc>
</func>
@@ -1098,7 +1098,7 @@ ChannelId, 0, Data, End, Timeout)</c></seealso>.</p>
</type>
<desc><marker id="write_file-3"/>
<p>For information and other types, see
- <seealso marker="ssh:ssh_sftp"><c>ssh:ssh_sftp(3)</c></seealso>.</p>
+ <seealso marker="ssh:ssh_sftp"><c>ssh_sftp(3)</c></seealso>.</p>
</desc>
</func>
@@ -1112,7 +1112,7 @@ ChannelId, 0, Data, End, Timeout)</c></seealso>.</p>
</type>
<desc><marker id="write_file-4"/>
<p>For information and other types, see
- <seealso marker="ssh:ssh_sftp"><c>ssh:ssh_sftp(3)</c></seealso>.</p>
+ <seealso marker="ssh:ssh_sftp"><c>ssh_sftp(3)</c></seealso>.</p>
</desc>
</func>
@@ -1126,7 +1126,7 @@ ChannelId, 0, Data, End, Timeout)</c></seealso>.</p>
</type>
<desc><marker id="write_file_info-3"/>
<p>For information and other types, see
- <seealso marker="ssh:ssh_sftp"><c>ssh:ssh_sftp(3)</c></seealso>.</p>
+ <seealso marker="ssh:ssh_sftp"><c>ssh_sftp(3)</c></seealso>.</p>
</desc>
</func>
@@ -1140,7 +1140,7 @@ ChannelId, 0, Data, End, Timeout)</c></seealso>.</p>
</type>
<desc><marker id="write_file_info-4"/>
<p>For information and other types, see
- <seealso marker="ssh:ssh_sftp"><c>ssh:ssh_sftp(3)</c></seealso>.</p>
+ <seealso marker="ssh:ssh_sftp"><c>ssh_sftp(3)</c></seealso>.</p>
</desc>
</func>
</funcs>
diff --git a/lib/common_test/doc/src/ct_telnet.xml b/lib/common_test/doc/src/ct_telnet.xml
index e2a45e894b..8e85cccc99 100644
--- a/lib/common_test/doc/src/ct_telnet.xml
+++ b/lib/common_test/doc/src/ct_telnet.xml
@@ -198,7 +198,7 @@
<item><marker id="type-prompt_regexp"/>
<p>Regular expression matching all possible prompts for a specific
target type. <c>regexp</c> must not have any groups, that is, when
- matching, <c>re:run/3</c> (in <c>STDLIB</c>) must return a list with
+ matching, <c>re:run/3</c> (in STDLIB) must return a list with
one single element.</p></item>
</taglist>
</section>
@@ -337,7 +337,7 @@
<c>FullMatch</c> is the string matched by the whole regular
expression, and <c>SubMatchN</c> is the string that matched
subexpression number <c>N</c>. Subexpressions are denoted with
- <c>(' ')</c> in the regular expression.</p>
+ <c>'(' ')'</c> in the regular expression.</p>
<p>If a <c>Tag</c> is speciifed, the returned <c>Match</c> also
includes the matched <c>Tag</c>. Otherwise, only <c>RxMatch</c>
diff --git a/lib/common_test/doc/src/event_handler_chapter.xml b/lib/common_test/doc/src/event_handler_chapter.xml
index 2978226a19..bd9ed21cb4 100644
--- a/lib/common_test/doc/src/event_handler_chapter.xml
+++ b/lib/common_test/doc/src/event_handler_chapter.xml
@@ -50,7 +50,7 @@
pass the information on. The event handlers are Erlang modules
implemented by the <c>Common Test</c> user according to the <c>gen_event</c>
behavior (for details, see module
- <seealso marker="stdlib:gen_event"><c>stdlib:gen_event</c></seealso> and
+ <seealso marker="stdlib:gen_event"><c>gen_event</c></seealso> and
section
<seealso marker="doc/design_principles:events"><c>gen_event Behaviour</c></seealso>
in OTP Design Principles in the System Documentation).
@@ -69,8 +69,8 @@
manager, either by telling <c>Common Test</c> to install them before the test
run (described later), or by adding the handlers dynamically during the test
run using
- <seealso marker="stdlib:gen_event#add_handler-3"><c>stdlib:gen_event:add_handler/3</c></seealso> or
- <seealso marker="stdlib:gen_event#add_sup_handler-3"><c>stdlib:gen_event:add_sup_handler/3</c></seealso>.
+ <seealso marker="stdlib:gen_event#add_handler-3"><c>gen_event:add_handler/3</c></seealso> or
+ <seealso marker="stdlib:gen_event#add_sup_handler-3"><c>gen_event:add_sup_handler/3</c></seealso>.
In the latter scenario, the reference of the <c>Common Test</c> event manager is
required. To get it, call
<seealso marker="ct#get_event_mgr_ref-0"><c>ct:get_event_mgr_ref/0</c></seealso>
diff --git a/lib/common_test/doc/src/introduction.xml b/lib/common_test/doc/src/introduction.xml
index 40724f24e9..df12bea6dd 100644
--- a/lib/common_test/doc/src/introduction.xml
+++ b/lib/common_test/doc/src/introduction.xml
@@ -45,7 +45,7 @@
</list>
<p><c>Common Test</c> also integrates use of the OTP
<seealso marker="tools:cover">cover</seealso> tool in application
- <c>Tools</c> for code coverage analysis of Erlang/OTP programs.</p>
+ Tools for code coverage analysis of Erlang/OTP programs.</p>
<p><c>Common Test</c> executes test suite programs automatically,
without operator interaction. Test progress and results are
diff --git a/lib/common_test/doc/src/notes.xml b/lib/common_test/doc/src/notes.xml
index 32ae699c7a..7653670d30 100644
--- a/lib/common_test/doc/src/notes.xml
+++ b/lib/common_test/doc/src/notes.xml
@@ -33,6 +33,42 @@
<file>notes.xml</file>
</header>
+<section><title>Common_Test 1.12.3</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ If the telnet server would pause during transmission of a
+ line of text before terminating the line, the
+ <c>ct_telnet:expect/3</c> function would print the line
+ twice in the test case HTML log. This problem has been
+ fixed.</p>
+ <p>
+ Own Id: OTP-13730 Aux Id: seq13135 </p>
+ </item>
+ <item>
+ <p>
+ The functions <c>ct:set_verbosity/2</c> and
+ <c>ct:get_verbosity/1</c> have been added in order to
+ make it possible for test cases, CT Hooks, or test
+ framework functions, to modify and read verbosity levels
+ for logging.</p>
+ <p>
+ Own Id: OTP-13841</p>
+ </item>
+ <item>
+ <p><c>make</c> (tools) and <c>ct_make</c> (common_test)
+ would crash if an Erlang source file contained a
+ <c>-warning()</c> directive.</p>
+ <p>
+ Own Id: OTP-13855</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Common_Test 1.12.2</title>
<section><title>Fixed Bugs and Malfunctions</title>
diff --git a/lib/common_test/doc/src/run_test_chapter.xml b/lib/common_test/doc/src/run_test_chapter.xml
index 43e36adfb6..76e306c4ed 100644
--- a/lib/common_test/doc/src/run_test_chapter.xml
+++ b/lib/common_test/doc/src/run_test_chapter.xml
@@ -274,7 +274,7 @@
<note><p>Directories passed to <c>Common Test</c> can have either relative or absolute paths.</p></note>
- <note><p>Any start flags to the Erlang runtime system (application <c>ERTS</c>) can also be passed as
+ <note><p>Any start flags to the Erlang runtime system (application ERTS) can also be passed as
parameters to <c>ct_run</c>. It is, for example, useful to be able to
pass directories to be added to the Erlang code server search path
with flag <c>-pa</c> or <c>-pz</c>. If you have common help- or library
@@ -286,7 +286,7 @@
<p>The absolute path of directory <c>chat_server/ebin</c>
is here passed to the code server. This is essential because relative
paths are stored by the code server as relative, and <c>Common Test</c> changes
- the current working directory of <c>ERTS</c> during the test run.</p>
+ the current working directory of ERTS during the test run.</p>
</note>
<p>The <c>ct_run</c> program sets the exit status before shutting down. The following values
@@ -1258,7 +1258,7 @@
<p>The minor log files contain full details of every single test
case, each in a separate file. This way, it is
straightforward to compare the latest results to that of previous
- test runs, even if the set of test cases changes. If application <c>SASL</c>
+ test runs, even if the set of test cases changes. If application SASL
is running, its logs are also printed to the current minor log file by the
<seealso marker="common_test:ct_hooks_chapter#builtin_cths">
cth_log_redirect built-in hook</seealso>.
diff --git a/lib/common_test/doc/src/write_test_chapter.xml b/lib/common_test/doc/src/write_test_chapter.xml
index 83daf771a6..1d3fbb6f76 100644
--- a/lib/common_test/doc/src/write_test_chapter.xml
+++ b/lib/common_test/doc/src/write_test_chapter.xml
@@ -269,10 +269,10 @@
<p>As parameter <c>Config</c> is a list of key-value tuples, that is,
a data type called a property list, it can be handled by the
- <seealso marker="stdlib:proplists"><c>stdlib:proplists</c></seealso> module.
+ <seealso marker="stdlib:proplists"><c>proplists</c></seealso> module.
A value can, for example, be searched for and returned with function
<seealso marker="stdlib:proplists#get_value-2"><c>proplists:get_value/2</c></seealso>.
- Also, or alternatively, the general <seealso marker="stdlib:lists"><c>stdlib:lists</c></seealso>
+ Also, or alternatively, the general <seealso marker="stdlib:lists"><c>lists</c></seealso>
module contains useful functions. Normally, the only operations
performed on <c>Config</c> is insert (adding a tuple to the head of the list)
and lookup. <c>Common Test</c> provides a simple macro named <c>?config</c>,
@@ -652,7 +652,7 @@
<title>Parallel Test Cases and I/O</title>
<p>A parallel test case has a private I/O server as its group leader.
(For a description of the group leader concept, see
- <seealso marker="erts:index"><c>ERTS</c></seealso>).
+ <seealso marker="erts:index">ERTS</seealso>).
The central I/O server process, which handles the output from
regular test cases and configuration functions, does not respond to I/O messages
during execution of parallel groups. This is important to understand
@@ -1031,9 +1031,13 @@
4. Categorized info, importance = 25
6. Categorized error, importance = 99</pre>
+ <p>The functions <seealso marker="ct#set_verbosity-2"><c>ct:set_verbosity/2</c></seealso>
+ and <seealso marker="ct#get_verbosity-1"><c>ct:get_verbosity/1</c></seealso> may be used
+ to modify and read verbosity levels during test execution.</p>
+
<p>The arguments <c>Format</c> and <c>FormatArgs</c> in <c>ct:log/print/pal</c> are
- always passed on to the <c>stdlib</c> function <c>io:format/3</c> (For details,
- see the <seealso marker="stdlib:io"><c>stdlib:io</c></seealso> manual page).</p>
+ always passed on to the STDLIB function <c>io:format/3</c> (For details,
+ see the <seealso marker="stdlib:io"><c>io</c></seealso> manual page).</p>
<p><c>ct:pal/4</c> and <c>ct:log/5</c> add headers to strings being printed to the
log file. The strings are also wrapped in div tags with a CSS class
diff --git a/lib/common_test/src/ct.erl b/lib/common_test/src/ct.erl
index d7ae81a5ce..f9f845e1a9 100644
--- a/lib/common_test/src/ct.erl
+++ b/lib/common_test/src/ct.erl
@@ -68,6 +68,7 @@
log/1, log/2, log/3, log/4, log/5,
print/1, print/2, print/3, print/4,
pal/1, pal/2, pal/3, pal/4,
+ set_verbosity/2, get_verbosity/1,
capture_start/0, capture_stop/0, capture_get/0, capture_get/1,
fail/1, fail/2, comment/1, comment/2, make_priv_dir/0,
testcases/2, userdata/2, userdata/3,
@@ -715,6 +716,24 @@ pal(Category,Importance,Format,Args) ->
ct_logs:tc_pal(Category,Importance,Format,Args).
%%%-----------------------------------------------------------------
+%%% @spec set_verbosity(Category, Level) -> ok
+%%% Category = default | atom()
+%%% Level = integer()
+%%%
+%%% @doc Set the verbosity level for a category
+set_verbosity(Category, Level) ->
+ ct_util:set_verbosity({Category,Level}).
+
+%%%-----------------------------------------------------------------
+%%% @spec get_verbosity(Category) -> Level | undefined
+%%% Category = default | atom()
+%%% Level = integer()
+%%%
+%%% @doc Read the verbosity level for a category
+get_verbosity(Category) ->
+ ct_util:get_verbosity(Category).
+
+%%%-----------------------------------------------------------------
%%% @spec capture_start() -> ok
%%%
%%% @doc Start capturing all text strings printed to stdout during
diff --git a/lib/common_test/src/ct_gen_conn.erl b/lib/common_test/src/ct_gen_conn.erl
index 23ba1ab981..8b59d3ab23 100644
--- a/lib/common_test/src/ct_gen_conn.erl
+++ b/lib/common_test/src/ct_gen_conn.erl
@@ -27,7 +27,7 @@
-export([start/4, stop/1, get_conn_pid/1, check_opts/1]).
-export([call/2, call/3, return/2, do_within_time/2]).
--export([log/3, start_log/1, cont_log/2, end_log/0]).
+-export([log/3, start_log/1, cont_log/2, cont_log_no_timestamp/2, end_log/0]).
%%----------------------------------------------------------------------
%% Exported types
@@ -175,6 +175,14 @@ cont_log(Format,Args) ->
log(cont_log,[Format,Args]).
%%%-----------------------------------------------------------------
+%%% @spec cont_log_no_timestamp(Format,Args) -> ok
+%%%
+%%% @doc Log activities on the current connection (tool-internal use only).
+%%% @see ct_logs:cont_log/2
+cont_log_no_timestamp(Format,Args) ->
+ log(cont_log_no_timestamp,[Format,Args]).
+
+%%%-----------------------------------------------------------------
%%% @spec end_log() -> ok
%%%
%%% @doc Log activities on the current connection (tool-internal use only).
diff --git a/lib/common_test/src/ct_logs.erl b/lib/common_test/src/ct_logs.erl
index 53245c596a..9282a9f81d 100644
--- a/lib/common_test/src/ct_logs.erl
+++ b/lib/common_test/src/ct_logs.erl
@@ -32,7 +32,7 @@
-export([init/2, close/2, init_tc/1, end_tc/1]).
-export([register_groupleader/2, unregister_groupleader/1]).
-export([get_log_dir/0, get_log_dir/1]).
--export([log/3, start_log/1, cont_log/2, end_log/0]).
+-export([log/3, start_log/1, cont_log/2, cont_log_no_timestamp/2, end_log/0]).
-export([set_stylesheet/2, clear_stylesheet/1]).
-export([add_external_logs/1, add_link/3]).
-export([make_last_run_index/0]).
@@ -373,6 +373,20 @@ cont_log(Format,Args) ->
ok.
%%%-----------------------------------------------------------------
+%%% @spec cont_log_no_timestamp(Format,Args) -> ok
+%%%
+%%% @doc Adds information about an activity (tool-internal use only).
+%%%
+%%% @see start_log/1
+%%% @see end_log/0
+cont_log_no_timestamp([],[]) ->
+ ok;
+cont_log_no_timestamp(Format,Args) ->
+ cast({log,sync,self(),group_leader(),ct_internal,?MAX_IMPORTANCE,
+ [{Format,Args}],true}),
+ ok.
+
+%%%-----------------------------------------------------------------
%%% @spec end_log() -> ok
%%%
%%% @doc Ends the logging of an activity (tool-internal use only).
@@ -595,7 +609,6 @@ div_header(Class,Printer) ->
div_footer() ->
"</pre></div>\n<pre>".
-
maybe_log_timestamp() ->
{MS,S,US} = ?now,
case get(log_timestamp) of
diff --git a/lib/common_test/src/ct_make.erl b/lib/common_test/src/ct_make.erl
index e7a9cfa843..f22959d457 100644
--- a/lib/common_test/src/ct_make.erl
+++ b/lib/common_test/src/ct_make.erl
@@ -342,5 +342,7 @@ check_includes2(Epp, File, ObjMTime) ->
epp:close(Epp),
false;
{error, _Error} ->
+ check_includes2(Epp, File, ObjMTime);
+ {warning, _Warning} ->
check_includes2(Epp, File, ObjMTime)
end.
diff --git a/lib/common_test/src/ct_telnet.erl b/lib/common_test/src/ct_telnet.erl
index 8fb411ec4f..34d27ed5f4 100644
--- a/lib/common_test/src/ct_telnet.erl
+++ b/lib/common_test/src/ct_telnet.erl
@@ -954,7 +954,7 @@ log(#state{name=Name,teln_pid=TelnPid,host=Host,port=Port},
true ->
ok;
false ->
- ct_gen_conn:cont_log(String,Args)
+ ct_gen_conn:cont_log_no_timestamp(String,Args)
end;
ForcePrint == true ->
@@ -965,7 +965,7 @@ log(#state{name=Name,teln_pid=TelnPid,host=Host,port=Port},
%% called
ct_gen_conn:log(heading(Action,Name1),String,Args);
false ->
- ct_gen_conn:cont_log(String,Args)
+ ct_gen_conn:cont_log_no_timestamp(String,Args)
end
end
end.
@@ -1224,7 +1224,6 @@ teln_expect1(Name,Pid,Data,Pattern,Acc,EO=#eo{idle_timeout=IdleTO,
EOMod = if TotalTO /= infinity -> EO#eo{total_timeout=trunc(TotalTO)};
true -> EO
end,
-
ExpectFun = case EOMod#eo.seq of
true -> fun() ->
seq_expect(Name,Pid,Data,Pattern,Acc,EOMod)
@@ -1247,38 +1246,34 @@ teln_expect1(Name,Pid,Data,Pattern,Acc,EO=#eo{idle_timeout=IdleTO,
true ->
IdleTO
end,
+ {PatOrPats1,Acc1,Rest1} = case NotFinished of
+ {nomatch,Rest0} ->
+ %% one expect
+ {Pattern,[],Rest0};
+ {continue,Pats0,Acc0,Rest0} ->
+ %% sequence
+ {Pats0,Acc0,Rest0}
+ end,
case timer:tc(ct_gen_conn, do_within_time, [Fun,BreakAfter]) of
- {_,{error,Reason}} ->
+ {_,{error,Reason}} ->
%% A timeout will occur when the telnet connection
%% is idle for EO#eo.idle_timeout milliseconds.
+ if Rest1 /= [] ->
+ log(name_or_pid(Name,Pid)," ~ts",[Rest1]);
+ true ->
+ ok
+ end,
{error,Reason};
{_,{ok,Data1}} when TotalTO == infinity ->
- case NotFinished of
- {nomatch,Rest} ->
- %% One expect
- teln_expect1(Name,Pid,Rest++Data1,
- Pattern,[],EOMod);
- {continue,Patterns1,Acc1,Rest} ->
- %% Sequence
- teln_expect1(Name,Pid,Rest++Data1,
- Patterns1,Acc1,EOMod)
- end;
+ teln_expect1(Name,Pid,Rest1++Data1,PatOrPats1,Acc1,EOMod);
{Elapsed,{ok,Data1}} ->
TVal = TotalTO - (Elapsed/1000),
if TVal =< 0 ->
{error,timeout};
true ->
EO1 = EO#eo{total_timeout = TVal},
- case NotFinished of
- {nomatch,Rest} ->
- %% One expect
- teln_expect1(Name,Pid,Rest++Data1,
- Pattern,[],EO1);
- {continue,Patterns1,Acc1,Rest} ->
- %% Sequence
- teln_expect1(Name,Pid,Rest++Data1,
- Patterns1,Acc1,EO1)
- end
+ teln_expect1(Name,Pid,Rest1++Data1,
+ PatOrPats1,Acc1,EO1)
end
end
end.
@@ -1416,14 +1411,14 @@ match_lines(Name,Pid,Data,Patterns,EO) ->
case one_line(Data,[]) of
{noline,Rest} when FoundPrompt=/=false ->
%% This is the line including the prompt
- case match_line(Name,Pid,Rest,Patterns,FoundPrompt,EO) of
+ case match_line(Name,Pid,Rest,Patterns,FoundPrompt,false,EO) of
nomatch ->
{nomatch,prompt};
{Tag,Match} ->
{Tag,Match,[]}
end;
{noline,Rest} when EO#eo.prompt_check==false ->
- case match_line(Name,Pid,Rest,Patterns,false,EO) of
+ case match_line(Name,Pid,Rest,Patterns,false,false,EO) of
nomatch ->
{nomatch,Rest};
{Tag,Match} ->
@@ -1432,7 +1427,7 @@ match_lines(Name,Pid,Data,Patterns,EO) ->
{noline,Rest} ->
{nomatch,Rest};
{Line,Rest} ->
- case match_line(Name,Pid,Line,Patterns,false,EO) of
+ case match_line(Name,Pid,Line,Patterns,false,true,EO) of
nomatch ->
match_lines(Name,Pid,Rest,Patterns,EO);
{Tag,Match} ->
@@ -1440,45 +1435,50 @@ match_lines(Name,Pid,Data,Patterns,EO) ->
end
end.
-
%% For one line, match each pattern
-match_line(Name,Pid,Line,Patterns,FoundPrompt,EO) ->
- match_line(Name,Pid,Line,Patterns,FoundPrompt,EO,match).
+match_line(Name,Pid,Line,Patterns,FoundPrompt,Terminated,EO) ->
+ match_line(Name,Pid,Line,Patterns,FoundPrompt,Terminated,EO,match).
-match_line(Name,Pid,Line,[prompt|Patterns],false,EO,RetTag) ->
- match_line(Name,Pid,Line,Patterns,false,EO,RetTag);
-match_line(Name,Pid,Line,[prompt|_Patterns],FoundPrompt,_EO,RetTag) ->
+match_line(Name,Pid,Line,[prompt|Patterns],false,Term,EO,RetTag) ->
+ match_line(Name,Pid,Line,Patterns,false,Term,EO,RetTag);
+match_line(Name,Pid,Line,[prompt|_Patterns],FoundPrompt,_Term,_EO,RetTag) ->
log(name_or_pid(Name,Pid)," ~ts",[Line]),
log(name_or_pid(Name,Pid),"PROMPT: ~ts",[FoundPrompt]),
{RetTag,{prompt,FoundPrompt}};
-match_line(Name,Pid,Line,[{prompt,PromptType}|_Patterns],FoundPrompt,_EO,RetTag)
- when PromptType==FoundPrompt ->
+match_line(Name,Pid,Line,[{prompt,PromptType}|_Patterns],FoundPrompt,_Term,
+ _EO,RetTag) when PromptType==FoundPrompt ->
log(name_or_pid(Name,Pid)," ~ts",[Line]),
log(name_or_pid(Name,Pid),"PROMPT: ~ts",[FoundPrompt]),
{RetTag,{prompt,FoundPrompt}};
-match_line(Name,Pid,Line,[{prompt,PromptType}|Patterns],FoundPrompt,EO,RetTag)
+match_line(Name,Pid,Line,[{prompt,PromptType}|Patterns],FoundPrompt,Term,
+ EO,RetTag)
when PromptType=/=FoundPrompt ->
- match_line(Name,Pid,Line,Patterns,FoundPrompt,EO,RetTag);
-match_line(Name,Pid,Line,[{Tag,Pattern}|Patterns],FoundPrompt,EO,RetTag) ->
+ match_line(Name,Pid,Line,Patterns,FoundPrompt,Term,EO,RetTag);
+match_line(Name,Pid,Line,[{Tag,Pattern}|Patterns],FoundPrompt,Term,EO,RetTag) ->
case re:run(Line,Pattern,[{capture,all,list}]) of
nomatch ->
- match_line(Name,Pid,Line,Patterns,FoundPrompt,EO,RetTag);
+ match_line(Name,Pid,Line,Patterns,FoundPrompt,Term,EO,RetTag);
{match,Match} ->
log(name_or_pid(Name,Pid),"MATCH: ~ts",[Line]),
{RetTag,{Tag,Match}}
end;
-match_line(Name,Pid,Line,[Pattern|Patterns],FoundPrompt,EO,RetTag) ->
+match_line(Name,Pid,Line,[Pattern|Patterns],FoundPrompt,Term,EO,RetTag) ->
case re:run(Line,Pattern,[{capture,all,list}]) of
nomatch ->
- match_line(Name,Pid,Line,Patterns,FoundPrompt,EO,RetTag);
+ match_line(Name,Pid,Line,Patterns,FoundPrompt,Term,EO,RetTag);
{match,Match} ->
log(name_or_pid(Name,Pid),"MATCH: ~ts",[Line]),
{RetTag,Match}
end;
-match_line(Name,Pid,Line,[],FoundPrompt,EO,match) ->
- match_line(Name,Pid,Line,EO#eo.haltpatterns,FoundPrompt,EO,halt);
-match_line(Name,Pid,Line,[],_FoundPrompt,_EO,halt) ->
+match_line(Name,Pid,Line,[],FoundPrompt,Term,EO,match) ->
+ match_line(Name,Pid,Line,EO#eo.haltpatterns,FoundPrompt,Term,EO,halt);
+%% print any terminated line that can not be matched
+match_line(Name,Pid,Line,[],_FoundPrompt,true,_EO,halt) ->
log(name_or_pid(Name,Pid)," ~ts",[Line]),
+ nomatch;
+%% if there's no line termination, Line is saved as Rest (above) and will
+%% be printed later
+match_line(_Name,_Pid,_Line,[],_FoundPrompt,false,_EO,halt) ->
nomatch.
one_line([$\n|Rest],Line) ->
diff --git a/lib/common_test/src/erl2html2.erl b/lib/common_test/src/erl2html2.erl
index e819f345de..7f3eb699de 100644
--- a/lib/common_test/src/erl2html2.erl
+++ b/lib/common_test/src/erl2html2.erl
@@ -130,6 +130,8 @@ parse_preprocessed_file(Epp, File, InCorrectFile) ->
throw({error,Reason,InCorrectFile});
{error,_Reason} ->
parse_preprocessed_file(Epp, File, InCorrectFile);
+ {warning,_} ->
+ parse_preprocessed_file(Epp, File, InCorrectFile);
{eof,_Location} ->
[]
end.
diff --git a/lib/common_test/test/ct_auto_compile_SUITE.erl b/lib/common_test/test/ct_auto_compile_SUITE.erl
index e6939d23c0..dface99b8f 100644
--- a/lib/common_test/test/ct_auto_compile_SUITE.erl
+++ b/lib/common_test/test/ct_auto_compile_SUITE.erl
@@ -27,6 +27,7 @@
%%% The suites used for the test are located in the data directory.
%%%-------------------------------------------------------------------
-module(ct_auto_compile_SUITE).
+-warning("Ignore me -- testing that the debugger can handle warnings").
-compile(export_all).
diff --git a/lib/common_test/test_server/ts_run.erl b/lib/common_test/test_server/ts_run.erl
index 66db1ff9a7..82ae44ec06 100644
--- a/lib/common_test/test_server/ts_run.erl
+++ b/lib/common_test/test_server/ts_run.erl
@@ -258,7 +258,7 @@ make_command(Vars, Spec, State) ->
run_batch(Vars, _Spec, State) ->
process_flag(trap_exit, true),
- Command = State#state.command ++ " -noinput -s erlang halt",
+ Command = State#state.command ++ " -noinput -eval \"erlang:halt(0,[{flush,false}]).\"",
ts_lib:progress(Vars, 1, "Command: ~ts~n", [Command]),
io:format(user, "Command: ~ts~n",[Command]),
Port = open_port({spawn, Command}, [stream, in, eof, exit_status]),
diff --git a/lib/common_test/vsn.mk b/lib/common_test/vsn.mk
index c68750886a..ab5cfd7a80 100644
--- a/lib/common_test/vsn.mk
+++ b/lib/common_test/vsn.mk
@@ -1 +1 @@
-COMMON_TEST_VSN = 1.12.2
+COMMON_TEST_VSN = 1.12.3
diff --git a/lib/compiler/doc/src/compile.xml b/lib/compiler/doc/src/compile.xml
index 61e214294e..3ce37b98e9 100644
--- a/lib/compiler/doc/src/compile.xml
+++ b/lib/compiler/doc/src/compile.xml
@@ -136,7 +136,7 @@
(see
<seealso marker="erts:absform">The Abstract Format</seealso>
in ERTS User's Guide) in the compiled beam module. Tools
- such as <c>Debugger</c>, <c>Xref</c>, and <c>Cover</c> require
+ such as Debugger, Xref, and Cover require
the debug information to be included.</p>
<p><em>Warning</em>: Source code can be reconstructed from
@@ -544,7 +544,7 @@ module.beam: module.erl \
compiler to be deprecated. Notice that the compiler does not know
about attribute <c>-deprecated()</c>, but uses an
assembled list of deprecated functions in Erlang/OTP. To
- do a more general check, the <c>Xref</c> tool can be used.
+ do a more general check, the Xref tool can be used.
See also
<seealso marker="tools:xref#deprecated_function">xref(3)</seealso>
and the function
@@ -846,7 +846,7 @@ pi() -> 3.1416.
<section>
<title>Inlining of List Functions</title>
<p>The compiler can also inline various list manipulation functions
- from the module <c>list</c> in <c>STDLIB</c>.</p>
+ from the module <c>list</c> in STDLIB.</p>
<p>This feature must be explicitly enabled with a compiler option or a
<c>-compile()</c> attribute in the source module.</p>
diff --git a/lib/compiler/doc/src/notes.xml b/lib/compiler/doc/src/notes.xml
index e25cdc580c..6aaf16e9a5 100644
--- a/lib/compiler/doc/src/notes.xml
+++ b/lib/compiler/doc/src/notes.xml
@@ -32,6 +32,60 @@
<p>This document describes the changes made to the Compiler
application.</p>
+<section><title>Compiler 7.0.2</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ If the compiler fails to write the BEAM file, it will now
+ report the reason of the error for the write operation.</p>
+ <p>
+ Own Id: OTP-13701</p>
+ </item>
+ <item>
+ <p>
+ Fixed an internal compiler error. (Thanks to Svilen
+ Ivanov for reporting this bug.)</p>
+ <p>
+ Own Id: OTP-13780 Aux Id: ERL-202 </p>
+ </item>
+ <item>
+ <p>
+ The compiler could crash when trying to compile a
+ complicated expression with multiple catches all on one
+ line . (Thanks to Thomas Arts for reporting this bug.)</p>
+ <p>
+ Own Id: OTP-13804 Aux Id: ERL-209 </p>
+ </item>
+ <item>
+ <p>
+ Eliminated a few internal compiler failures.</p>
+ <p>
+ Own Id: OTP-13863</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Compiler 7.0.1</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ A literal binary matching regression was introduced in
+ 19.0 where a match could fail to resolve to the right
+ clause. This has now been fixed.</p>
+ <p>
+ Own Id: OTP-13738</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Compiler 7.0</title>
<section><title>Fixed Bugs and Malfunctions</title>
@@ -263,7 +317,7 @@
<item>
<p>
The <c>cerl</c> and <c>cerl_trees</c> modules in the
- <c>compiler</c> application are now documented.</p>
+ Compiler application are now documented.</p>
<p>
Own Id: OTP-11978</p>
</item>
@@ -1965,7 +2019,7 @@
<c>RightExpr</c> or vice versa. The evaluation order is
only important if the expressions contains and/or depends
on operations with side-effects, such as message passing
- or <c>ets</c> operations.</p>
+ or ETS operations.</p>
<p>
Own Id: OTP-7206</p>
</item>
diff --git a/lib/compiler/doc/src/ref_man.xml b/lib/compiler/doc/src/ref_man.xml
index f5466553c0..c32c499008 100644
--- a/lib/compiler/doc/src/ref_man.xml
+++ b/lib/compiler/doc/src/ref_man.xml
@@ -30,7 +30,7 @@
<file>application.sgml</file>
</header>
<description>
- <p>The <c>Compiler</c> application compiles Erlang
+ <p>The Compiler application compiles Erlang
code to byte-code. The highly compact byte-code is executed by
the Erlang emulator.</p>
</description>
diff --git a/lib/compiler/src/beam_block.erl b/lib/compiler/src/beam_block.erl
index 85d332c56e..ec41925beb 100644
--- a/lib/compiler/src/beam_block.erl
+++ b/lib/compiler/src/beam_block.erl
@@ -159,14 +159,43 @@ find_fixpoint(OptFun, Is0) ->
end.
%% move_allocates(Is0) -> Is
-%% Move allocate instructions upwards in the instruction stream, in the
-%% hope of getting more possibilities for optimizing away moves later.
+%% Move allocate instructions upwards in the instruction stream
+%% (within the same block), in the hope of getting more possibilities
+%% for optimizing away moves later.
%%
-%% NOTE: Moving allocation instructions is only safe because it is done
-%% immediately after code generation so that we KNOW that if {x,X} is
-%% initialized, all x registers with lower numbers are also initialized.
-%% That assumption may not be true after other optimizations, such as
-%% the beam_utils:live_opt/1 optimization.
+%% For example, we can transform the following instructions:
+%%
+%% get_tuple_element x(1) Element => x(2)
+%% allocate_zero StackSize 3 %% x(0), x(1), x(2) are live
+%%
+%% to the following instructions:
+%%
+%% allocate_zero StackSize 2 %% x(0) and x(1) are live
+%% get_tuple_element x(1) Element => x(2)
+%%
+%% NOTE: Since the beam_reorder pass has been run, it is no longer
+%% safe to assume that if x(N) is initialized, then all lower-numbered
+%% x registers are also initialized.
+%%
+%% For example, in general it is not safe to transform the following
+%% instructions:
+%%
+%% get_tuple_element x(0) Element => x(1)
+%% allocate_zero StackSize 3 %x(0), x(1), x(2) are live
+%%
+%% to the following instructions:
+%%
+%% allocate_zero StackSize 3
+%% get_tuple_element x(0) Element => x(1)
+%%
+%% The transformation is safe if and only if x(1) has been
+%% initialized previously. Unfortunately, beam_reorder may have moved
+%% a get_tuple_element instruction so that x(1) is not always
+%% initialized when this code is reached. To find whether or not x(1)
+%% is initialized, we would need to analyze all code preceding these
+%% two instructions (across branches). Since we currently don't have
+%% any practical mechanism for doing that, we will have to
+%% conservatively assume that the transformation is unsafe.
move_allocates([{block,Bl0}|Is]) ->
Bl = move_allocates_1(reverse(Bl0), []),
@@ -175,27 +204,19 @@ move_allocates([I|Is]) ->
[I|move_allocates(Is)];
move_allocates([]) -> [].
-move_allocates_1([{set,[],[],{alloc,_,_}=Alloc}|Is0], Acc0) ->
- {Is,Acc} = move_allocates_2(Alloc, Is0, Acc0),
- move_allocates_1(Is, Acc);
+move_allocates_1([I|Is], [{set,[],[],{alloc,Live0,Info}}|Acc]=Acc0) ->
+ case {alloc_may_pass(I),alloc_live_regs(I, Live0)} of
+ {false,_} ->
+ move_allocates_1(Is, [I|Acc0]);
+ {true,not_possible} ->
+ move_allocates_1(Is, [I|Acc0]);
+ {true,Live} when is_integer(Live) ->
+ A = {set,[],[],{alloc,Live,Info}},
+ move_allocates_1(Is, [A,I|Acc])
+ end;
move_allocates_1([I|Is], Acc) ->
move_allocates_1(Is, [I|Acc]);
-move_allocates_1([], Is) -> Is.
-
-move_allocates_2({alloc,Live,Info}, [{set,[],[],{alloc,Live0,Info0}}|Is], Acc) ->
- Live = Live0, % Assertion.
- Alloc = {alloc,Live,combine_alloc(Info0, Info)},
- move_allocates_2(Alloc, Is, Acc);
-move_allocates_2({alloc,Live,Info}=Alloc0, [I|Is]=Is0, Acc) ->
- case alloc_may_pass(I) of
- false ->
- {Is0,[{set,[],[],Alloc0}|Acc]};
- true ->
- Alloc = {alloc,alloc_live_regs(I, Live),Info},
- move_allocates_2(Alloc, Is, [I|Acc])
- end;
-move_allocates_2(Alloc, [], Acc) ->
- {[],[{set,[],[],Alloc}|Acc]}.
+move_allocates_1([], Acc) -> Acc.
alloc_may_pass({set,_,_,{alloc,_,_}}) -> false;
alloc_may_pass({set,_,_,{set_tuple_element,_}}) -> false;
@@ -204,9 +225,6 @@ alloc_may_pass({set,_,_,put_list}) -> false;
alloc_may_pass({set,_,_,put}) -> false;
alloc_may_pass({set,_,_,_}) -> true.
-combine_alloc({_,Ns,Nh1,Init}, {_,nostack,Nh2,[]}) ->
- {zero,Ns,beam_utils:combine_heap_needs(Nh1, Nh2),Init}.
-
%% opt([Instruction]) -> [Instruction]
%% Optimize the instruction stream inside a basic block.
@@ -393,10 +411,19 @@ eliminate_use_of_from_reg([I]=Is, From, _To, Acc) ->
%% opt_alloc(Instructions) -> Instructions'
%% Optimises all allocate instructions.
+opt_alloc([{set,[],[],{alloc,Live0,Info0}},
+ {set,[],[],{alloc,Live,Info}}|Is]) ->
+ Live = Live0, %Assertion.
+ Alloc = combine_alloc(Info0, Info),
+ I = {set,[],[],{alloc,Live,Alloc}},
+ opt_alloc([I|Is]);
opt_alloc([{set,[],[],{alloc,R,{_,Ns,Nh,[]}}}|Is]) ->
[{set,[],[],opt_alloc(Is, Ns, Nh, R)}|Is];
opt_alloc([I|Is]) -> [I|opt_alloc(Is)];
opt_alloc([]) -> [].
+
+combine_alloc({_,Ns,Nh1,Init}, {_,nostack,Nh2,[]}) ->
+ {zero,Ns,beam_utils:combine_heap_needs(Nh1, Nh2),Init}.
%% opt_alloc(Instructions, FrameSize, HeapNeed, LivingRegs) -> [Instr]
%% Generates the optimal sequence of instructions for
@@ -445,13 +472,14 @@ count_ones(Bits, Acc) ->
alloc_live_regs({set,Ds,Ss,_}, Regs0) ->
Rset = x_live(Ss, x_dead(Ds, (1 bsl Regs0)-1)),
- live_regs(Rset).
+ live_regs(0, Rset).
-live_regs(Regs) ->
- live_regs_1(0, Regs).
-
-live_regs_1(N, 0) -> N;
-live_regs_1(N, Regs) -> live_regs_1(N+1, Regs bsr 1).
+live_regs(N, 0) ->
+ N;
+live_regs(N, Regs) when Regs band 1 =:= 1 ->
+ live_regs(N+1, Regs bsr 1);
+live_regs(_, _) ->
+ not_possible.
x_dead([{x,N}|Rs], Regs) -> x_dead(Rs, Regs band (bnot (1 bsl N)));
x_dead([_|Rs], Regs) -> x_dead(Rs, Regs);
diff --git a/lib/compiler/src/beam_dead.erl b/lib/compiler/src/beam_dead.erl
index b01f58f683..6f6d742293 100644
--- a/lib/compiler/src/beam_dead.erl
+++ b/lib/compiler/src/beam_dead.erl
@@ -272,17 +272,14 @@ backward([{jump,{f,To}}=J|[{bif,Op,_,Ops,Reg}|Is]=Is0], D, Acc) ->
catch
throw:not_possible -> backward(Is0, D, [J|Acc])
end;
-backward([{test,bs_start_match2,F,Live,[R,_]=Args,Ctxt}|Is], D,
+backward([{test,bs_start_match2,F,_,[R,_],Ctxt}=I|Is], D,
[{test,bs_match_string,F,[Ctxt,Bs]},
{test,bs_test_tail2,F,[Ctxt,0]}|Acc0]=Acc) ->
- {f,To0} = F,
- To = shortcut_bs_start_match(To0, R, D),
case beam_utils:is_killed(Ctxt, Acc0, D) of
true ->
- Eq = {test,is_eq_exact,{f,To},[R,{literal,Bs}]},
+ Eq = {test,is_eq_exact,F,[R,{literal,Bs}]},
backward(Is, D, [Eq|Acc0]);
false ->
- I = {test,bs_start_match2,{f,To},Live,Args,Ctxt},
backward(Is, D, [I|Acc])
end;
backward([{test,bs_start_match2,{f,To0},Live,[Src|_]=Info,Dst}|Is], D, Acc) ->
diff --git a/lib/compiler/src/beam_jump.erl b/lib/compiler/src/beam_jump.erl
index 09cd3aa2d4..48b5a32814 100644
--- a/lib/compiler/src/beam_jump.erl
+++ b/lib/compiler/src/beam_jump.erl
@@ -167,12 +167,18 @@ share_1([{label,L}=Lbl|Is], Dict0, Seq, Acc) ->
end;
share_1([{func_info,_,_,_}=I|Is], _, [], Acc) ->
reverse(Is, [I|Acc]);
+share_1([{'catch',_,_}=I|Is], Dict0, Seq, Acc) ->
+ Dict = clean_non_sharable(Dict0),
+ share_1(Is, Dict, [I|Seq], Acc);
share_1([{'try',_,_}=I|Is], Dict0, Seq, Acc) ->
Dict = clean_non_sharable(Dict0),
share_1(Is, Dict, [I|Seq], Acc);
share_1([{try_case,_}=I|Is], Dict0, Seq, Acc) ->
Dict = clean_non_sharable(Dict0),
share_1(Is, Dict, [I|Seq], Acc);
+share_1([{catch_end,_}=I|Is], Dict0, Seq, Acc) ->
+ Dict = clean_non_sharable(Dict0),
+ share_1(Is, Dict, [I|Seq], Acc);
share_1([I|Is], Dict, Seq, Acc) ->
case is_unreachable_after(I) of
false ->
@@ -182,18 +188,18 @@ share_1([I|Is], Dict, Seq, Acc) ->
end.
clean_non_sharable(Dict) ->
- %% We are passing in or out of a 'try' block. Remove
- %% sequences that should not shared over the boundaries
- %% of a 'try' block. Since the end of the sequence must match,
- %% the only possible match between a sequence outside and
- %% a sequence inside the 'try' block is a sequence that ends
- %% with an instruction that causes an exception. Any sequence
- %% that causes an exception must contain a line/1 instruction.
+ %% We are passing in or out of a 'catch' or 'try' block. Remove
+ %% sequences that should not be shared over the boundaries of the
+ %% block. Since the end of the sequence must match, the only
+ %% possible match between a sequence outside and a sequence inside
+ %% the 'catch'/'try' block is a sequence that ends with an
+ %% instruction that causes an exception. Any sequence that causes
+ %% an exception must contain a line/1 instruction.
maps:filter(fun(K, _V) -> sharable_with_try(K) end, Dict).
sharable_with_try([{line,_}|_]) ->
%% This sequence may cause an exception and may potentially
- %% match a sequence on the other side of the 'try' block
+ %% match a sequence on the other side of the 'catch'/'try' block
%% boundary.
false;
sharable_with_try([_|Is]) ->
diff --git a/lib/compiler/src/beam_validator.erl b/lib/compiler/src/beam_validator.erl
index 4c0cb6780a..16dba35adc 100644
--- a/lib/compiler/src/beam_validator.erl
+++ b/lib/compiler/src/beam_validator.erl
@@ -808,9 +808,11 @@ validate_bs_skip_utf(Fail, Ctx, Live, Vst0) ->
%% A possibility for garbage collection must not occur between setelement/3 and
%% set_tuple_element/3.
%%
+%% Note that #vst.current will be 'none' if the instruction is unreachable.
+%%
val_dsetel({move,_,_}, Vst) ->
Vst;
-val_dsetel({call_ext,3,{extfunc,erlang,setelement,3}}, #vst{current=St}=Vst) ->
+val_dsetel({call_ext,3,{extfunc,erlang,setelement,3}}, #vst{current=#st{}=St}=Vst) ->
Vst#vst{current=St#st{setelem=true}};
val_dsetel({set_tuple_element,_,_,_}, #vst{current=#st{setelem=false}}) ->
error(illegal_context_for_set_tuple_element);
diff --git a/lib/compiler/src/sys_core_fold.erl b/lib/compiler/src/sys_core_fold.erl
index e0de50f3ae..08b02101a6 100644
--- a/lib/compiler/src/sys_core_fold.erl
+++ b/lib/compiler/src/sys_core_fold.erl
@@ -468,7 +468,8 @@ bitstr(#c_bitstr{val=Val,size=Size}=BinSeg, Sub) ->
%% Currently, we don't attempt to check binaries because they
%% are difficult to check.
-is_safe_simple(#c_var{}, _) -> true;
+is_safe_simple(#c_var{}=Var, _) ->
+ not cerl:is_c_fname(Var);
is_safe_simple(#c_cons{hd=H,tl=T}, Sub) ->
is_safe_simple(H, Sub) andalso is_safe_simple(T, Sub);
is_safe_simple(#c_tuple{es=Es}, Sub) -> is_safe_simple_list(Es, Sub);
diff --git a/lib/compiler/test/beam_block_SUITE.erl b/lib/compiler/test/beam_block_SUITE.erl
index 4bcb252833..9fcb6e497d 100644
--- a/lib/compiler/test/beam_block_SUITE.erl
+++ b/lib/compiler/test/beam_block_SUITE.erl
@@ -21,7 +21,8 @@
-export([all/0,suite/0,groups/0,init_per_suite/1,end_per_suite/1,
init_per_group/2,end_per_group/2,
- get_map_elements/1,otp_7345/1,move_opt_across_gc_bif/1]).
+ get_map_elements/1,otp_7345/1,move_opt_across_gc_bif/1,
+ erl_202/1]).
%% The only test for the following functions is that
%% the code compiles and is accepted by beam_validator.
@@ -37,7 +38,8 @@ groups() ->
[{p,[parallel],
[get_map_elements,
otp_7345,
- move_opt_across_gc_bif
+ move_opt_across_gc_bif,
+ erl_202
]}].
init_per_suite(Config) ->
@@ -135,6 +137,27 @@ positive(speaking) ->
paris([], P) -> P + 1.
+
+%% See https://bugs.erlang.org/browse/ERL-202.
+%% Test that move_allocates/1 in beam_block doesn't move allocate
+%% when it would not be safe.
+
+-record(erl_202_r1, {y}).
+-record(erl_202_r2, {x}).
+
+erl_202(_Config) ->
+ Ref = make_ref(),
+ Ref = erl_202({{1,2},Ref}, 42),
+
+ {Ref} = erl_202({7,8}, #erl_202_r1{y=#erl_202_r2{x=Ref}}),
+
+ ok.
+
+erl_202({{_, _},X}, _) ->
+ X;
+erl_202({_, _}, #erl_202_r1{y=R2}) ->
+ {R2#erl_202_r2.x}.
+
%%%
%%% The only test of the following code is that it compiles.
%%%
diff --git a/lib/compiler/test/beam_jump_SUITE.erl b/lib/compiler/test/beam_jump_SUITE.erl
index 0b13adaff2..088f63606c 100644
--- a/lib/compiler/test/beam_jump_SUITE.erl
+++ b/lib/compiler/test/beam_jump_SUITE.erl
@@ -21,7 +21,7 @@
-export([all/0,suite/0,groups/0,init_per_suite/1,end_per_suite/1,
init_per_group/2,end_per_group/2,
- undefined_label/1]).
+ undefined_label/1,ambiguous_catch_try_state/1]).
suite() ->
[{ct_hooks,[ts_install_cth]}].
@@ -32,7 +32,8 @@ all() ->
groups() ->
[{p,[parallel],
- [undefined_label
+ [undefined_label,
+ ambiguous_catch_try_state
]}].
init_per_suite(Config) ->
@@ -57,3 +58,17 @@ flights(0, [], []) when [], 0; 0.0, [], false ->
clark;
flights(_, Reproduction, introduction) when false, Reproduction ->
responsible.
+
+%% [ERL-209] beam_jump would share 'catch' blocks, causing an
+%% ambiguous_catch_try_state error in beam_validator.
+
+ambiguous_catch_try_state(_Config) ->
+ {{'EXIT',{{case_clause,song},_}},{'EXIT',{{case_clause,song},_}}} =
+ checks(42),
+ ok.
+
+river() -> song.
+
+checks(Wanted) ->
+ %% Must be one line to cause the unsafe optimization.
+ {catch case river() of sheet -> begin +Wanted, if "da" -> Wanted end end end, catch case river() of sheet -> begin + Wanted, if "da" -> Wanted end end end}.
diff --git a/lib/compiler/test/beam_validator_SUITE.erl b/lib/compiler/test/beam_validator_SUITE.erl
index 263fd2ca7e..ca85eef688 100644
--- a/lib/compiler/test/beam_validator_SUITE.erl
+++ b/lib/compiler/test/beam_validator_SUITE.erl
@@ -32,7 +32,8 @@
bad_bin_match/1,bad_dsetel/1,
state_after_fault_in_catch/1,no_exception_in_catch/1,
undef_label/1,illegal_instruction/1,failing_gc_guard_bif/1,
- map_field_lists/1,cover_bin_opt/1]).
+ map_field_lists/1,cover_bin_opt/1,
+ val_dsetel/1]).
-include_lib("common_test/include/ct.hrl").
@@ -60,7 +61,7 @@ groups() ->
freg_state,bad_bin_match,bad_dsetel,
state_after_fault_in_catch,no_exception_in_catch,
undef_label,illegal_instruction,failing_gc_guard_bif,
- map_field_lists,cover_bin_opt]}].
+ map_field_lists,cover_bin_opt,val_dsetel]}].
init_per_suite(Config) ->
Config.
@@ -546,3 +547,23 @@ beam_val(M) ->
_ = [io:put_chars(beam_validator:format_error(E)) ||
E <- Errors],
Errors.
+
+%%%-------------------------------------------------------------------------
+
+val_dsetel(_Config) ->
+ self() ! 13,
+ {'EXIT',{{try_clause,participating},_}} = (catch night(0)),
+ ok.
+
+night(Turned) ->
+ receive
+ 13 ->
+ try participating of engine -> 16 after false end
+ end,
+ %% The setelement/3 call is unreachable.
+ Turned(setelement(#{true => Turned},
+ participating(Turned, "suit", 40, []),
+ Turned < Turned)),
+ ok.
+
+participating(_, _, _, _) -> ok.
diff --git a/lib/compiler/test/core_fold_SUITE.erl b/lib/compiler/test/core_fold_SUITE.erl
index 376d2c8e9a..ced0e39d06 100644
--- a/lib/compiler/test/core_fold_SUITE.erl
+++ b/lib/compiler/test/core_fold_SUITE.erl
@@ -26,7 +26,7 @@
unused_multiple_values_error/1,unused_multiple_values/1,
multiple_aliases/1,redundant_boolean_clauses/1,
mixed_matching_clauses/1,unnecessary_building/1,
- no_no_file/1]).
+ no_no_file/1,configuration/1]).
-export([foo/0,foo/1,foo/2,foo/3]).
@@ -45,7 +45,7 @@ groups() ->
unused_multiple_values_error,unused_multiple_values,
multiple_aliases,redundant_boolean_clauses,
mixed_matching_clauses,unnecessary_building,
- no_no_file]}].
+ no_no_file,configuration]}].
init_per_suite(Config) ->
@@ -499,4 +499,16 @@ experiment() ->
end,
ok.
+
+%% Make sure we don't try to move a fun into a guard.
+configuration(_Config) ->
+ {'EXIT',_} = (catch configuration()),
+ ok.
+
+configuration() ->
+ [forgotten || Components <- enemy, is_tuple(fun art/0)].
+
+art() ->
+ creating.
+
id(I) -> I.
diff --git a/lib/compiler/test/match_SUITE.erl b/lib/compiler/test/match_SUITE.erl
index 31402ac717..127679ba69 100644
--- a/lib/compiler/test/match_SUITE.erl
+++ b/lib/compiler/test/match_SUITE.erl
@@ -24,7 +24,7 @@
pmatch/1,mixed/1,aliases/1,non_matching_aliases/1,
match_in_call/1,untuplify/1,shortcut_boolean/1,letify_guard/1,
selectify/1,underscore/1,match_map/1,map_vars_used/1,
- coverage/1,grab_bag/1]).
+ coverage/1,grab_bag/1,literal_binary/1]).
-include_lib("common_test/include/ct.hrl").
@@ -40,7 +40,7 @@ groups() ->
match_in_call,untuplify,
shortcut_boolean,letify_guard,selectify,
underscore,match_map,map_vars_used,coverage,
- grab_bag]}].
+ grab_bag,literal_binary]}].
init_per_suite(Config) ->
@@ -574,6 +574,15 @@ grab_bag_remove_failure([{stretch,_,Mi}=Stretch | Specs], Unit, _MaxFailure) ->
ok
end.
+%% Regression in 19.0, reported by Alexei Sholik
+literal_binary(_Config) ->
+ 3 = literal_binary_match(bar,<<"y">>),
+ ok.
+
+literal_binary_match(bar, <<"x">>) -> 1;
+literal_binary_match(_, <<"x">>) -> 2;
+literal_binary_match(_, <<"y">>) -> 3;
+literal_binary_match(_, _) -> fail.
id(I) -> I.
diff --git a/lib/compiler/vsn.mk b/lib/compiler/vsn.mk
index 23dd4bd4b1..87fde38f2b 100644
--- a/lib/compiler/vsn.mk
+++ b/lib/compiler/vsn.mk
@@ -1 +1 @@
-COMPILER_VSN = 7.0
+COMPILER_VSN = 7.0.2
diff --git a/lib/crypto/c_src/crypto.c b/lib/crypto/c_src/crypto.c
index 7183c395ae..0e4e85cef7 100644
--- a/lib/crypto/c_src/crypto.c
+++ b/lib/crypto/c_src/crypto.c
@@ -37,7 +37,9 @@
#include <openssl/opensslconf.h>
#include <openssl/crypto.h>
+#ifndef OPENSSL_NO_DES
#include <openssl/des.h>
+#endif /* #ifndef OPENSSL_NO_DES */
/* #include <openssl/idea.h> This is not supported on the openssl OTP requires */
#include <openssl/dsa.h>
#include <openssl/rsa.h>
@@ -48,8 +50,12 @@
#include <openssl/ripemd.h>
#include <openssl/bn.h>
#include <openssl/objects.h>
-#include <openssl/rc4.h>
-#include <openssl/rc2.h>
+#ifndef OPENSSL_NO_RC4
+ #include <openssl/rc4.h>
+#endif /* OPENSSL_NO_RC4 */
+#ifndef OPENSSL_NO_RC2
+ #include <openssl/rc2.h>
+#endif
#include <openssl/blowfish.h>
#include <openssl/rand.h>
#include <openssl/evp.h>
@@ -458,16 +464,35 @@ struct cipher_type_t {
const size_t key_len; /* != 0 to also match on key_len */
};
+#ifdef OPENSSL_NO_DES
+#define COND_NO_DES_PTR(Ptr) (NULL)
+#else
+#define COND_NO_DES_PTR(Ptr) (Ptr)
+#endif
+
struct cipher_type_t cipher_types[] =
{
- {{"rc2_cbc"}, {&EVP_rc2_cbc}},
- {{"des_cbc"}, {&EVP_des_cbc}},
- {{"des_cfb"}, {&EVP_des_cfb8}},
- {{"des_ecb"}, {&EVP_des_ecb}},
- {{"des_ede3_cbc"}, {&EVP_des_ede3_cbc}},
- {{"des_ede3_cbf"},
+ {{"rc2_cbc"},
+#ifndef OPENSSL_NO_RC2
+ {&EVP_rc2_cbc}
+#else
+ {NULL}
+#endif
+ },
+ {{"des_cbc"}, {COND_NO_DES_PTR(&EVP_des_cbc)}},
+ {{"des_cfb"}, {COND_NO_DES_PTR(&EVP_des_cfb8)}},
+ {{"des_ecb"}, {COND_NO_DES_PTR(&EVP_des_ecb)}},
+ {{"des_ede3_cbc"}, {COND_NO_DES_PTR(&EVP_des_ede3_cbc)}},
+ {{"des_ede3_cbf"}, /* Misspelled, retained */
#ifdef HAVE_DES_ede3_cfb_encrypt
- {&EVP_des_ede3_cfb8}
+ {COND_NO_DES_PTR(&EVP_des_ede3_cfb8)}
+#else
+ {NULL}
+#endif
+ },
+ {{"des_ede3_cfb"},
+#ifdef HAVE_DES_ede3_cfb_encrypt
+ {COND_NO_DES_PTR(&EVP_des_ede3_cfb8)}
#else
{NULL}
#endif
@@ -749,7 +774,7 @@ static ERL_NIF_TERM algo_hash[8]; /* increase when extending the list */
static int algo_pubkey_cnt;
static ERL_NIF_TERM algo_pubkey[7]; /* increase when extending the list */
static int algo_cipher_cnt;
-static ERL_NIF_TERM algo_cipher[20]; /* increase when extending the list */
+static ERL_NIF_TERM algo_cipher[23]; /* increase when extending the list */
static void init_algorithms_types(ErlNifEnv* env)
{
@@ -785,10 +810,13 @@ static void init_algorithms_types(ErlNifEnv* env)
algo_pubkey[algo_pubkey_cnt++] = enif_make_atom(env, "srp");
algo_cipher_cnt = 0;
+#ifndef OPENSSL_NO_DES
algo_cipher[algo_cipher_cnt++] = enif_make_atom(env, "des3_cbc");
algo_cipher[algo_cipher_cnt++] = enif_make_atom(env, "des_ede3");
#ifdef HAVE_DES_ede3_cfb_encrypt
algo_cipher[algo_cipher_cnt++] = enif_make_atom(env, "des3_cbf");
+ algo_cipher[algo_cipher_cnt++] = enif_make_atom(env, "des3_cfb");
+#endif
#endif
algo_cipher[algo_cipher_cnt++] = enif_make_atom(env, "aes_cbc");
algo_cipher[algo_cipher_cnt++] = enif_make_atom(env, "aes_cbc128");
@@ -800,14 +828,21 @@ static void init_algorithms_types(ErlNifEnv* env)
#ifdef HAVE_AES_IGE
algo_cipher[algo_cipher_cnt++] = enif_make_atom(env,"aes_ige256");
#endif
+#ifndef OPENSSL_NO_DES
algo_cipher[algo_cipher_cnt++] = enif_make_atom(env,"des_cbc");
algo_cipher[algo_cipher_cnt++] = enif_make_atom(env,"des_cfb");
+ algo_cipher[algo_cipher_cnt++] = enif_make_atom(env,"des_ecb");
+#endif
algo_cipher[algo_cipher_cnt++] = enif_make_atom(env,"blowfish_cbc");
algo_cipher[algo_cipher_cnt++] = enif_make_atom(env,"blowfish_cfb64");
algo_cipher[algo_cipher_cnt++] = enif_make_atom(env,"blowfish_ofb64");
algo_cipher[algo_cipher_cnt++] = enif_make_atom(env,"blowfish_ecb");
+#ifndef OPENSSL_NO_RC2
algo_cipher[algo_cipher_cnt++] = enif_make_atom(env,"rc2_cbc");
+#endif
+#ifndef OPENSSL_NO_RC4
algo_cipher[algo_cipher_cnt++] = enif_make_atom(env,"rc4");
+#endif
#if defined(HAVE_GCM)
algo_cipher[algo_cipher_cnt++] = enif_make_atom(env,"aes_gcm");
#endif
@@ -2141,7 +2176,7 @@ static ERL_NIF_TERM dss_verify_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM
DSA *dsa;
int i;
- if (!argv[0] == atom_sha
+ if (argv[0] != atom_sha
|| !enif_inspect_binary(env, argv[1], &digest_bin)
|| digest_bin.size != SHA_DIGEST_LENGTH
|| !enif_inspect_binary(env, argv[2], &sign_bin)
@@ -2306,6 +2341,7 @@ static ERL_NIF_TERM do_exor(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
static ERL_NIF_TERM rc4_encrypt(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{/* (Key, Data) */
+#ifndef OPENSSL_NO_RC4
ErlNifBinary key, data;
RC4_KEY rc4_key;
ERL_NIF_TERM ret;
@@ -2319,10 +2355,14 @@ static ERL_NIF_TERM rc4_encrypt(ErlNifEnv* env, int argc, const ERL_NIF_TERM arg
enif_make_new_binary(env, data.size, &ret));
CONSUME_REDS(env,data);
return ret;
-}
+#else
+ return enif_raise_exception(env, atom_notsup);
+#endif
+}
static ERL_NIF_TERM rc4_set_key(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{/* (Key) */
+#ifndef OPENSSL_NO_RC4
ErlNifBinary key;
ERL_NIF_TERM ret;
@@ -2332,11 +2372,14 @@ static ERL_NIF_TERM rc4_set_key(ErlNifEnv* env, int argc, const ERL_NIF_TERM arg
RC4_set_key((RC4_KEY*)enif_make_new_binary(env, sizeof(RC4_KEY), &ret),
key.size, key.data);
return ret;
+#else
+ return enif_raise_exception(env, atom_notsup);
+#endif
}
static ERL_NIF_TERM rc4_encrypt_with_state(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{/* (State, Data) */
-
+#ifndef OPENSSL_NO_RC4
ErlNifBinary state, data;
RC4_KEY* rc4_key;
ERL_NIF_TERM new_state, new_data;
@@ -2352,7 +2395,10 @@ static ERL_NIF_TERM rc4_encrypt_with_state(ErlNifEnv* env, int argc, const ERL_N
enif_make_new_binary(env, data.size, &new_data));
CONSUME_REDS(env,data);
return enif_make_tuple2(env,new_state,new_data);
-}
+#else
+ return enif_raise_exception(env, atom_notsup);
+#endif
+}
static int get_rsa_private_key(ErlNifEnv* env, ERL_NIF_TERM key, RSA *rsa)
{
@@ -2467,7 +2513,7 @@ static ERL_NIF_TERM dss_sign_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM ar
DSA* dsa;
int i;
- if (!argv[0] == atom_sha
+ if (argv[0] != atom_sha
|| !enif_inspect_binary(env, argv[1], &digest_bin)
|| digest_bin.size != SHA_DIGEST_LENGTH) {
return enif_make_badarg(env);
diff --git a/lib/crypto/doc/src/crypto.xml b/lib/crypto/doc/src/crypto.xml
index 5a5627747c..eda0f7af51 100644
--- a/lib/crypto/doc/src/crypto.xml
+++ b/lib/crypto/doc/src/crypto.xml
@@ -136,7 +136,7 @@
<code>stream_cipher() = rc4 | aes_ctr </code>
<code>block_cipher() = aes_cbc | aes_cfb8 | aes_cfb128 | aes_ige256 | blowfish_cbc |
- blowfish_cfb64 | des_cbc | des_cfb | des3_cbc | des3_cbf | des_ede3 | rc2_cbc </code>
+ blowfish_cfb64 | des_cbc | des_cfb | des3_cbc | des3_cfb | des_ede3 | rc2_cbc </code>
<code>aead_cipher() = aes_gcm | chacha20_poly1305 </code>
@@ -161,7 +161,7 @@
</p>
<code> cipher_algorithms() = aes_cbc | aes_cfb8 | aes_cfb128 | aes_ctr | aes_gcm |
aes_ige256 | blowfish_cbc | blowfish_cfb64 | chacha20_poly1305 | des_cbc | des_cfb |
- des3_cbc | des3_cbf | des_ede3 | rc2_cbc | rc4 </code>
+ des3_cbc | des3_cfb | des_ede3 | rc2_cbc | rc4 </code>
<code> public_key_algorithms() = rsa |dss | ecdsa | dh | ecdh | ec_gf2m</code>
<p>Note that ec_gf2m is not strictly a public key algorithm, but a restriction on what curves are supported
with ecdsa and ecdh.
diff --git a/lib/crypto/doc/src/notes.xml b/lib/crypto/doc/src/notes.xml
index 7d3a85326f..4ae64e059e 100644
--- a/lib/crypto/doc/src/notes.xml
+++ b/lib/crypto/doc/src/notes.xml
@@ -31,6 +31,32 @@
</header>
<p>This document describes the changes made to the Crypto application.</p>
+<section><title>Crypto 3.7.1</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Crypto has been fixed to work against OpenSSL versions
+ with disabled DES ciphers. Correct spelling of cipher
+ algorithm 'des3_cfb' has been introduced; the previous
+ misspeling still works.</p>
+ <p>
+ Own Id: OTP-13783 Aux Id: ERL-203 </p>
+ </item>
+ <item>
+ <p>
+ The size of an internal array in crypto has been fixed to
+ not segfault when having all possible ciphers. Bug fix by
+ Duncan Overbruck.</p>
+ <p>
+ Own Id: OTP-13789 Aux Id: PR-1140 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Crypto 3.7</title>
<section><title>Improvements and New Features</title>
@@ -815,7 +841,7 @@
also been extended. </item><item> The <c>configure</c>
scripts of <c>erl_interface</c> and <c>odbc</c> now
search for thread libraries and thread library quirks the
- same way as <c>erts</c> do. </item><item> The
+ same way as ERTS do. </item><item> The
<c>configure</c> script of the <c>odbc</c> application
now also looks for odbc libraries in <c>lib64</c> and
<c>lib/64</c> directories when building on a 64-bit
diff --git a/lib/crypto/src/crypto.erl b/lib/crypto/src/crypto.erl
index 025d57e9c5..da8626e38a 100644
--- a/lib/crypto/src/crypto.erl
+++ b/lib/crypto/src/crypto.erl
@@ -274,7 +274,7 @@ hmac_final_n(Context, HashLen) ->
%% Ecrypt/decrypt %%%
-spec block_encrypt(des_cbc | des_cfb |
- des3_cbc | des3_cbf | des_ede3 |
+ des3_cbc | des3_cbf | des3_cfb | des_ede3 |
blowfish_cbc | blowfish_cfb64 | blowfish_ofb64 |
aes_cbc128 | aes_cfb8 | aes_cfb128 | aes_cbc256 | aes_ige256 |
aes_cbc |
@@ -301,6 +301,9 @@ block_encrypt(Type, Key0, Ivec, Data) when Type =:= des3_cbc;
block_encrypt(des3_cbf, Key0, Ivec, Data) ->
Key = check_des3_key(Key0),
block_crypt_nif(des_ede3_cbf, Key, Ivec, Data, true);
+block_encrypt(des3_cfb, Key0, Ivec, Data) ->
+ Key = check_des3_key(Key0),
+ block_crypt_nif(des_ede3_cfb, Key, Ivec, Data, true);
block_encrypt(aes_ige256, Key, Ivec, Data) ->
aes_ige_crypt_nif(Key, Ivec, Data, true);
block_encrypt(aes_gcm, Key, Ivec, {AAD, Data}) ->
@@ -311,7 +314,7 @@ block_encrypt(chacha20_poly1305, Key, Ivec, {AAD, Data}) ->
chacha20_poly1305_encrypt(Key, Ivec, AAD, Data).
-spec block_decrypt(des_cbc | des_cfb |
- des3_cbc | des3_cbf | des_ede3 |
+ des3_cbc | des3_cbf | des3_cfb | des_ede3 |
blowfish_cbc | blowfish_cfb64 | blowfish_ofb64 |
aes_cbc128 | aes_cfb8 | aes_cfb128 | aes_cbc256 | aes_ige256 |
aes_cbc |
@@ -338,6 +341,9 @@ block_decrypt(Type, Key0, Ivec, Data) when Type =:= des3_cbc;
block_decrypt(des3_cbf, Key0, Ivec, Data) ->
Key = check_des3_key(Key0),
block_crypt_nif(des_ede3_cbf, Key, Ivec, Data, false);
+block_decrypt(des3_cfb, Key0, Ivec, Data) ->
+ Key = check_des3_key(Key0),
+ block_crypt_nif(des_ede3_cfb, Key, Ivec, Data, false);
block_decrypt(aes_ige256, Key, Ivec, Data) ->
notsup_to_error(aes_ige_crypt_nif(Key, Ivec, Data, false));
block_decrypt(aes_gcm, Key, Ivec, {AAD, Data, Tag}) ->
@@ -857,10 +863,10 @@ des_ede3_cbc_decrypt(Key1, Key2, Key3, IVec, Data) ->
binary().
des3_cfb_encrypt(Key1, Key2, Key3, IVec, Data) ->
- block_encrypt(des3_cbf, [Key1, Key2, Key3], IVec, Data).
+ block_encrypt(des3_cfb, [Key1, Key2, Key3], IVec, Data).
des3_cfb_decrypt(Key1, Key2, Key3, IVec, Data) ->
- block_decrypt(des3_cbf, [Key1, Key2, Key3], IVec, Data).
+ block_decrypt(des3_cfb, [Key1, Key2, Key3], IVec, Data).
%%
%% Blowfish
diff --git a/lib/crypto/test/crypto_SUITE.erl b/lib/crypto/test/crypto_SUITE.erl
index 6732f27824..7b07cef33f 100644
--- a/lib/crypto/test/crypto_SUITE.erl
+++ b/lib/crypto/test/crypto_SUITE.erl
@@ -50,6 +50,7 @@ all() ->
{group, des_cfb},
{group, des3_cbc},
{group, des3_cbf},
+ {group, des3_cfb},
{group, des_ede3},
{group, blowfish_cbc},
{group, blowfish_ecb},
@@ -94,6 +95,7 @@ groups() ->
{des3_cbc,[], [block]},
{des_ede3,[], [block]},
{des3_cbf,[], [block]},
+ {des3_cfb,[], [block]},
{rc2_cbc,[], [block]},
{aes_cbc128,[], [block]},
{aes_cfb8,[], [block]},
@@ -381,11 +383,8 @@ block_cipher({Type, Key, IV, PlainText, CipherText}) ->
ct:fail({{crypto, block_decrypt, [Type, Key, IV, CipherText]}, {expected, Plain}, {got, Other1}})
end.
-block_cipher_increment({Type, Key, IV, PlainTexts}) when Type == des_cbc;
- Type == des3_cbc;
- Type == aes_cbc;
- Type == des_cbf
- ->
+block_cipher_increment({Type, Key, IV, PlainTexts})
+ when Type == des_cbc; Type == aes_cbc; Type == des3_cbc ->
block_cipher_increment(Type, Key, IV, IV, PlainTexts, iolist_to_binary(PlainTexts), []);
block_cipher_increment({Type, Key, IV, PlainTexts, _CipherText}) when Type == aes_cbc ->
Plain = iolist_to_binary(PlainTexts),
@@ -582,6 +581,8 @@ do_block_iolistify({des3_cbc = Type, Key, IV, PlainText}) ->
{Type, Key, IV, des_iolistify(PlainText)};
do_block_iolistify({des3_cbf = Type, Key, IV, PlainText}) ->
{Type, Key, IV, des_iolistify(PlainText)};
+do_block_iolistify({des3_cfb = Type, Key, IV, PlainText}) ->
+ {Type, Key, IV, des_iolistify(PlainText)};
do_block_iolistify({des_ede3 = Type, Key, IV, PlainText}) ->
{Type, Key, IV, des_iolistify(PlainText)};
do_block_iolistify({Type, Key, PlainText}) ->
@@ -792,6 +793,9 @@ group_config(des3_cbc, Config) ->
group_config(des3_cbf, Config) ->
Block = des3_cbf(),
[{block, Block} | Config];
+group_config(des3_cfb, Config) ->
+ Block = des3_cfb(),
+ [{block, Block} | Config];
group_config(des_ede3, Config) ->
Block = des_ede3(),
[{block, Block} | Config];
@@ -1193,7 +1197,16 @@ des_ede3() ->
des3_cbf() ->
[{des3_cbf,
- [hexstr2bin("0123456789abcdef"),
+ [hexstr2bin("0123456789abcdef"),
+ hexstr2bin("fedcba9876543210"),
+ hexstr2bin("0f2d4b6987a5c3e1")],
+ hexstr2bin("1234567890abcdef"),
+ <<"Now is the time for all ">>
+ }].
+
+des3_cfb() ->
+ [{des3_cfb,
+ [hexstr2bin("0123456789abcdef"),
hexstr2bin("fedcba9876543210"),
hexstr2bin("0f2d4b6987a5c3e1")],
hexstr2bin("1234567890abcdef"),
diff --git a/lib/crypto/test/old_crypto_SUITE.erl b/lib/crypto/test/old_crypto_SUITE.erl
index 0d97290d10..324ed39c6d 100644
--- a/lib/crypto/test/old_crypto_SUITE.erl
+++ b/lib/crypto/test/old_crypto_SUITE.erl
@@ -58,6 +58,7 @@
des_cfb_iter/1,
des_ecb/1,
des3_cbc/1,
+ des3_cbf/1,
des3_cfb/1,
rc2_cbc/1,
aes_cfb/1,
@@ -102,7 +103,7 @@ groups() ->
hmac_rfc2202, hmac_rfc4231_sha224, hmac_rfc4231_sha256,
hmac_rfc4231_sha384, hmac_rfc4231_sha512,
des_cbc, aes_cfb, aes_cbc,
- des_cfb, des_cfb_iter, des3_cbc, des3_cfb, rc2_cbc,
+ des_cfb, des_cfb_iter, des3_cbc, des3_cbf, des3_cfb, rc2_cbc,
aes_cbc_iter, aes_ctr, aes_ctr_stream, des_cbc_iter, des_ecb,
rand_uniform_test, strong_rand_test,
rsa_verify_test, dsa_verify_test, rsa_sign_test,
@@ -969,6 +970,9 @@ des_cbc(doc) ->
des_cbc(suite) ->
[];
des_cbc(Config) when is_list(Config) ->
+ if_supported(des_cbc, fun des_cbc_do/0).
+
+des_cbc_do() ->
?line Key = hexstr2bin("0123456789abcdef"),
?line IVec = hexstr2bin("1234567890abcdef"),
?line Plain = "Now is the time for all ",
@@ -992,6 +996,9 @@ des_cbc_iter(doc) ->
des_cbc_iter(suite) ->
[];
des_cbc_iter(Config) when is_list(Config) ->
+ if_supported(des_cbc, fun des_cbc_iter_do/0).
+
+des_cbc_iter_do() ->
?line Key = hexstr2bin("0123456789abcdef"),
?line IVec = hexstr2bin("1234567890abcdef"),
?line Plain1 = "Now is the time ",
@@ -1011,6 +1018,9 @@ des_cfb(doc) ->
des_cfb(suite) ->
[];
des_cfb(Config) when is_list(Config) ->
+ if_supported(des_cfb, fun des_cfb_do/0).
+
+des_cfb_do() ->
?line Key = hexstr2bin("0123456789abcdef"),
?line IVec = hexstr2bin("1234567890abcdef"),
?line Plain = "Now is the",
@@ -1027,6 +1037,9 @@ des_cfb_iter(doc) ->
des_cfb_iter(suite) ->
[];
des_cfb_iter(Config) when is_list(Config) ->
+ if_supported(des_cfb, fun des_cfb_iter_do/0).
+
+des_cfb_iter_do() ->
?line Key = hexstr2bin("0123456789abcdef"),
?line IVec = hexstr2bin("1234567890abcdef"),
?line Plain1 = "Now i",
@@ -1045,6 +1058,9 @@ des_ecb(doc) ->
des_ecb(suite) ->
[];
des_ecb(Config) when is_list(Config) ->
+ if_supported(des_ecb, fun des_ecb_do/0).
+
+des_ecb_do() ->
?line Key = hexstr2bin("0123456789abcdef"),
?line Cipher1 = crypto:des_ecb_encrypt(Key, "Now is t"),
?line m(Cipher1, hexstr2bin("3fa40e8a984d4815")),
@@ -1064,7 +1080,9 @@ rc2_cbc(doc) ->
"Encrypt and decrypt according to RC2 CBC and check the result. "
"Example stripped out from public_key application test";
rc2_cbc(Config) when is_list(Config) ->
-
+ if_supported(rc2_cbc, fun rc2_cbc_do/0).
+
+rc2_cbc_do() ->
Key = <<146,210,160,124,215,227,153,239,227,17,222,140,3,93,27,191>>,
IV = <<72,91,135,182,25,42,35,210>>,
@@ -1081,6 +1099,9 @@ des3_cbc(doc) ->
des3_cbc(suite) ->
[];
des3_cbc(Config) when is_list(Config) ->
+ if_supported(des3_cbc, fun des3_cbc_do/0).
+
+des3_cbc_do() ->
?line Key1 = hexstr2bin("0123456789abcdef"),
?line Key2 = hexstr2bin("fedcba9876543210"),
?line Key3 = hexstr2bin("0f2d4b6987a5c3e1"),
@@ -1112,6 +1133,19 @@ des3_cbc(Config) when is_list(Config) ->
%%
%%
+des3_cbf(doc) ->
+ "Encrypt and decrypt according to CFB 3DES, and check the result.";
+des3_cbf(suite) ->
+ [];
+des3_cbf(Config) when is_list(Config) ->
+ case openssl_version() of
+ V when V < 16#90705F -> {skipped,"OpenSSL version too old"};
+ _ ->
+ if_supported(des3_cbf, fun des3_cfb_do/0)
+ end.
+
+%%
+%%
des3_cfb(doc) ->
"Encrypt and decrypt according to CFB 3DES, and check the result.";
des3_cfb(suite) ->
@@ -1119,7 +1153,8 @@ des3_cfb(suite) ->
des3_cfb(Config) when is_list(Config) ->
case openssl_version() of
V when V < 16#90705F -> {skipped,"OpenSSL version too old"};
- _ -> des3_cfb_do()
+ _ ->
+ if_supported(des3_cfb, fun des3_cfb_do/0)
end.
des3_cfb_do() ->
@@ -2084,6 +2119,9 @@ rc4_test(doc) ->
rc4_test(suite) ->
[];
rc4_test(Config) when is_list(Config) ->
+ if_supported(rc4, fun rc4_test_do/0).
+
+rc4_test_do() ->
CT1 = <<"Yo baby yo">>,
R1 = <<118,122,68,110,157,166,141,212,139,39>>,
K = "apaapa",
@@ -2099,6 +2137,9 @@ rc4_stream_test(doc) ->
rc4_stream_test(suite) ->
[];
rc4_stream_test(Config) when is_list(Config) ->
+ if_supported(rc4, fun rc4_stream_test_do/0).
+
+rc4_stream_test_do() ->
CT1 = <<"Yo ">>,
CT2 = <<"baby yo">>,
K = "apaapa",
diff --git a/lib/crypto/vsn.mk b/lib/crypto/vsn.mk
index 96466869d1..bbee24554a 100644
--- a/lib/crypto/vsn.mk
+++ b/lib/crypto/vsn.mk
@@ -1 +1 @@
-CRYPTO_VSN = 3.7
+CRYPTO_VSN = 3.7.1
diff --git a/lib/debugger/doc/src/Makefile b/lib/debugger/doc/src/Makefile
index 6c9617ca69..0f724b6f17 100644
--- a/lib/debugger/doc/src/Makefile
+++ b/lib/debugger/doc/src/Makefile
@@ -114,7 +114,7 @@ release_docs_spec: docs
$(INSTALL_DIR) "$(RELSYSDIR)/doc/pdf"
$(INSTALL_DATA) $(TOP_PDF_FILE) "$(RELSYSDIR)/doc/pdf"
$(INSTALL_DIR) "$(RELSYSDIR)/doc/html"
- (/bin/cp -rf $(HTMLDIR) "$(RELSYSDIR)/doc")
+ ($(CP) -rf $(HTMLDIR) "$(RELSYSDIR)/doc")
$(INSTALL_DATA) $(INFO_FILE) "$(RELSYSDIR)"
$(INSTALL_DIR) "$(RELEASE_PATH)/man/man3"
$(INSTALL_DATA) $(MAN3DIR)/* "$(RELEASE_PATH)/man/man3"
diff --git a/lib/debugger/doc/src/i.xml b/lib/debugger/doc/src/i.xml
index db89f23494..628b91e9e4 100644
--- a/lib/debugger/doc/src/i.xml
+++ b/lib/debugger/doc/src/i.xml
@@ -45,7 +45,7 @@
attached manually or automatically.</p>
<p>By preference, these functions can be included in module
- <seealso marker="stdlib:shell_default"><c>stdlib:shell_default</c></seealso>.
+ <seealso marker="stdlib:shell_default"><c>shell_default</c></seealso>.
By default, they are included in that module.</p>
</description>
@@ -372,7 +372,7 @@
</fsummary>
<desc>
<p>Returns the current version number of the interpreter.
- Same as the version number of the <c>Debugger</c> application.</p>
+ Same as the version number of the Debugger application.</p>
</desc>
</func>
diff --git a/lib/debugger/doc/src/notes.xml b/lib/debugger/doc/src/notes.xml
index 2e0d834269..93bc46ddbe 100644
--- a/lib/debugger/doc/src/notes.xml
+++ b/lib/debugger/doc/src/notes.xml
@@ -33,6 +33,22 @@
<p>This document describes the changes made to the Debugger
application.</p>
+<section><title>Debugger 4.2.1</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Update build scripts to not make assumtions about where
+ env, cp and perl are located.</p>
+ <p>
+ Own Id: OTP-13800</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Debugger 4.2</title>
<section><title>Improvements and New Features</title>
diff --git a/lib/debugger/src/dbg_iserver.erl b/lib/debugger/src/dbg_iserver.erl
index 3561454685..3e959e8e30 100644
--- a/lib/debugger/src/dbg_iserver.erl
+++ b/lib/debugger/src/dbg_iserver.erl
@@ -214,7 +214,6 @@ handle_call({new_process, Pid, Meta, Function}, _From, State) ->
%% Code loading
handle_call({load, Mod, Src, Bin}, _From, State) ->
-
%% Create an ETS table for storing information about the module
Db = State#state.db,
ModDb = ets:new(Mod, [ordered_set, public]),
@@ -285,23 +284,28 @@ handle_call({contents, Mod, Pid}, _From, State) ->
{reply, {ok, Bin}, State};
handle_call({raw_contents, Mod, Pid}, _From, State) ->
Db = State#state.db,
- [{{Mod, refs}, ModDbs}] = ets:lookup(Db, {Mod, refs}),
- ModDb = if
- Pid =:= any -> hd(ModDbs);
- true ->
- lists:foldl(fun(T, not_found) ->
- [{T, Pids}] = ets:lookup(Db, T),
- case lists:member(Pid, Pids) of
- true -> T;
- false -> not_found
- end;
- (_T, T) -> T
- end,
- not_found,
- ModDbs)
- end,
- [{mod_raw, Bin}] = ets:lookup(ModDb, mod_raw),
- {reply, {ok, Bin}, State};
+ case ets:lookup(Db, {Mod, refs}) of
+ [{{Mod, refs}, ModDbs}] ->
+ ModDb =
+ if
+ Pid =:= any -> hd(ModDbs);
+ true ->
+ lists:foldl(fun(T, not_found) ->
+ [{T, Pids}] = ets:lookup(Db, T),
+ case lists:member(Pid, Pids) of
+ true -> T;
+ false -> not_found
+ end;
+ (_T, T) -> T
+ end,
+ not_found,
+ ModDbs)
+ end,
+ [{mod_raw, Bin}] = ets:lookup(ModDb, mod_raw),
+ {reply, {ok, Bin}, State};
+ [] -> % code not interpreted
+ {reply, not_found, State}
+ end;
handle_call({is_interpreted, Mod, Name, Arity}, _From, State) ->
Db = State#state.db,
Reply = case ets:lookup(Db, {Mod, refs}) of
diff --git a/lib/debugger/src/dbg_wx_trace.erl b/lib/debugger/src/dbg_wx_trace.erl
index 6af19af33b..29c8e8cefb 100644
--- a/lib/debugger/src/dbg_wx_trace.erl
+++ b/lib/debugger/src/dbg_wx_trace.erl
@@ -818,11 +818,14 @@ gui_show_module(Win, Mod, Line, _Cm, Pid, How) ->
gui_load_module(Win, Mod, _Pid) ->
dbg_wx_trace_win:display(Win,{text, "Loading module..."}),
- %% Contents = int:contents(Mod, Pid),
- {ok, Contents} = dbg_iserver:call({raw_contents, Mod, any}),
- Win2 = dbg_wx_trace_win:show_code(Win, Mod, Contents),
- dbg_wx_trace_win:display(Win,{text, ""}),
- Win2.
+ case dbg_iserver:call({raw_contents, Mod, any}) of
+ {ok, Contents} ->
+ Win2 = dbg_wx_trace_win:show_code(Win, Mod, Contents),
+ dbg_wx_trace_win:display(Win,{text, ""}),
+ Win2;
+ not_found ->
+ dbg_wx_trace_win:show_no_code(Win)
+ end.
gui_update_bindings(Win,Meta) ->
Bs = int:meta(Meta, bindings, nostack),
diff --git a/lib/debugger/src/dbg_wx_view.erl b/lib/debugger/src/dbg_wx_view.erl
index 91fc0d08cb..86d009238f 100644
--- a/lib/debugger/src/dbg_wx_view.erl
+++ b/lib/debugger/src/dbg_wx_view.erl
@@ -263,7 +263,11 @@ shortcut(_) -> false.
gui_load_module(Win, Mod) ->
dbg_wx_trace_win:display(Win,{text, "Loading module..."}),
- {ok, Contents} = dbg_iserver:call({raw_contents, Mod, any}),
- Win2 = dbg_wx_trace_win:show_code(Win, Mod, Contents),
- dbg_wx_trace_win:display(Win,{text, ""}),
- Win2.
+ case dbg_iserver:call({raw_contents, Mod, any}) of
+ {ok, Contents} ->
+ Win2 = dbg_wx_trace_win:show_code(Win, Mod, Contents),
+ dbg_wx_trace_win:display(Win,{text, ""}),
+ Win2;
+ not_found ->
+ dbg_wx_trace_win:show_no_code(Win)
+ end.
diff --git a/lib/debugger/vsn.mk b/lib/debugger/vsn.mk
index dd496013cd..f5440865ef 100644
--- a/lib/debugger/vsn.mk
+++ b/lib/debugger/vsn.mk
@@ -1 +1 @@
-DEBUGGER_VSN = 4.2
+DEBUGGER_VSN = 4.2.1
diff --git a/lib/dialyzer/doc/src/notes.xml b/lib/dialyzer/doc/src/notes.xml
index 6400072b1f..b0f0a9aef0 100644
--- a/lib/dialyzer/doc/src/notes.xml
+++ b/lib/dialyzer/doc/src/notes.xml
@@ -32,6 +32,29 @@
<p>This document describes the changes made to the Dialyzer
application.</p>
+<section><title>Dialyzer 3.0.2</title>
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p> The translation of forms to types is improved for
+ opaque types in a few cases. </p>
+ <p>
+ Own Id: OTP-13682</p>
+ </item>
+ <item>
+ <p> Add warning suppression to compiler-generated case
+ statements. Warnings about clauses that cannot match and
+ are also compiler generated are suppressed unless none of
+ the clauses return. </p>
+ <p>
+ Own Id: OTP-13723 Aux Id: ERL-159, PR-1121 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Dialyzer 3.0.1</title>
<section><title>Fixed Bugs and Malfunctions</title>
diff --git a/lib/dialyzer/src/dialyzer.erl b/lib/dialyzer/src/dialyzer.erl
index bcac8afe64..7f86520c06 100644
--- a/lib/dialyzer/src/dialyzer.erl
+++ b/lib/dialyzer/src/dialyzer.erl
@@ -282,7 +282,7 @@ cl_check_log(none) ->
cl_check_log(Output) ->
io:format(" Check output file `~s' for details\n", [Output]).
--spec format_warning(raw_warning()) -> string().
+-spec format_warning(raw_warning() | dial_warning()) -> string().
format_warning(W) ->
format_warning(W, basename).
diff --git a/lib/dialyzer/src/dialyzer_contracts.erl b/lib/dialyzer/src/dialyzer_contracts.erl
index 976a2b8955..a72368f9f8 100644
--- a/lib/dialyzer/src/dialyzer_contracts.erl
+++ b/lib/dialyzer/src/dialyzer_contracts.erl
@@ -768,19 +768,9 @@ picky_contract_check(CSig0, Sig0, MFA, WarningInfo, Contract, RecDict, Acc) ->
end.
extra_contract_warning(MFA, WarningInfo, Contract, CSig, Sig, RecDict) ->
- %% We do not want to depend upon erl_types:t_to_string() possibly
- %% hiding the contents of opaque types.
- SigUnopaque = erl_types:t_unopaque(Sig),
- CSigUnopaque = erl_types:t_unopaque(CSig),
- SigString0 =
- lists:flatten(dialyzer_utils:format_sig(SigUnopaque, RecDict)),
- ContractString0 =
- lists:flatten(dialyzer_utils:format_sig(CSigUnopaque, RecDict)),
- %% The only difference is in record fields containing 'undefined' or not.
- IsUndefRecordFieldsRelated = SigString0 =:= ContractString0,
{IsRemoteTypesRelated, SubtypeRelation} =
is_remote_types_related(Contract, CSig, Sig, MFA, RecDict),
- case IsUndefRecordFieldsRelated orelse IsRemoteTypesRelated of
+ case IsRemoteTypesRelated of
true ->
no_warning;
false ->
diff --git a/lib/dialyzer/test/behaviour_SUITE_data/dialyzer_options b/lib/dialyzer/test/behaviour_SUITE_data/dialyzer_options
index 50991c9bc5..cb6a88786e 100644
--- a/lib/dialyzer/test/behaviour_SUITE_data/dialyzer_options
+++ b/lib/dialyzer/test/behaviour_SUITE_data/dialyzer_options
@@ -1 +1,2 @@
{dialyzer_options, []}.
+{time_limit, 2}.
diff --git a/lib/dialyzer/test/map_SUITE_data/src/opaque_bif.erl b/lib/dialyzer/test/map_SUITE_data/src/opaque_bif.erl
new file mode 100644
index 0000000000..40214a1887
--- /dev/null
+++ b/lib/dialyzer/test/map_SUITE_data/src/opaque_bif.erl
@@ -0,0 +1,13 @@
+-module(opaque_bif).
+-export([o1/1]).
+-export_type([opaque_any_map/0]).
+-opaque opaque_any_map() :: map().
+
+%% ERL-249: A bug with opaque arguments to maps:merge/2
+%% Reported by Felipe Ripoll on 6/9/2016
+-spec o1(opaque_any_map()) -> opaque_any_map().
+o1(Map) ->
+ maps:merge(o1_c(), Map).
+
+-spec o1_c() -> opaque_any_map().
+o1_c() -> #{}.
diff --git a/lib/dialyzer/test/opaque_SUITE_data/dialyzer_options b/lib/dialyzer/test/opaque_SUITE_data/dialyzer_options
index 3ff26b87db..ffdf8270c8 100644
--- a/lib/dialyzer/test/opaque_SUITE_data/dialyzer_options
+++ b/lib/dialyzer/test/opaque_SUITE_data/dialyzer_options
@@ -1 +1,2 @@
{dialyzer_options, [{warnings, [no_unused, no_return]}]}.
+{time_limit, 2}.
diff --git a/lib/dialyzer/vsn.mk b/lib/dialyzer/vsn.mk
index b9a28afdd9..6723876208 100644
--- a/lib/dialyzer/vsn.mk
+++ b/lib/dialyzer/vsn.mk
@@ -1 +1 @@
-DIALYZER_VSN = 3.0.1
+DIALYZER_VSN = 3.0.2
diff --git a/lib/diameter/doc/src/diameter.xml b/lib/diameter/doc/src/diameter.xml
index d68a78ed6d..72181a42b0 100644
--- a/lib/diameter/doc/src/diameter.xml
+++ b/lib/diameter/doc/src/diameter.xml
@@ -94,12 +94,12 @@ in this module.</p>
<taglist>
-<tag><c>Address()</c></tag>
-<tag><c>DiameterIdentity()</c></tag>
-<tag><c>Grouped()</c></tag>
-<tag><c>OctetString()</c></tag>
-<tag><c>Time()</c></tag>
-<tag><c>Unsigned32()</c></tag>
+<tag><c>Address()</c></tag><item/>
+<tag><c>DiameterIdentity()</c></tag><item/>
+<tag><c>Grouped()</c></tag><item/>
+<tag><c>OctetString()</c></tag><item/>
+<tag><c>Time()</c></tag><item/>
+<tag><c>Unsigned32()</c></tag><item/>
<tag><c>UTF8String()</c></tag>
<item>
<p>
@@ -159,8 +159,7 @@ Has one the following types.</p>
<p>
Unique identifier for the application in the scope of the
service.
-Defaults to the value of the <c>dictionary</c> option if
-unspecified.</p>
+Defaults to the value of the <c>dictionary</c> option.</p>
</item>
<tag><c>{dictionary, atom()}</c></tag>
@@ -187,7 +186,7 @@ Initial callback state.
The prevailing state is passed to some
&man_app;
callbacks, which can then return a new state.
-Defaults to the value of the <c>alias</c> option if unspecified.</p>
+Defaults to the value of the <c>alias</c> option.</p>
</item>
<tag><c>{call_mutates_state, true|false}</c></tag>
@@ -195,7 +194,7 @@ Defaults to the value of the <c>alias</c> option if unspecified.</p>
<p>
Whether or not the &app_pick_peer;
application callback can modify the application state.
-Defaults to <c>false</c> if unspecified.</p>
+Defaults to <c>false</c>.</p>
<warning>
<p>
@@ -228,7 +227,7 @@ question is as if a callback had taken place and returned
<c>{error, failure}</c>.</p>
<p>
-Defaults to <c>discard</c> if unspecified.</p>
+Defaults to <c>discard</c>.</p>
</item>
<tag><c>{request_errors, answer_3xxx|answer|callback}</c></tag>
@@ -249,7 +248,7 @@ place and its return value determines the answer sent to the peer, if
any.</p>
<p>
-Defaults to <c>answer_3xxx</c> if unspecified.</p>
+Defaults to <c>answer_3xxx</c>.</p>
<note>
<p>
@@ -339,8 +338,8 @@ Has one of the following types.</p>
<taglist>
-<tag><c>{'Origin-Host', &dict_DiameterIdentity;}</c></tag>
-<tag><c>{'Origin-Realm', &dict_DiameterIdentity;}</c></tag>
+<tag><c>{'Origin-Host', &dict_DiameterIdentity;}</c></tag><item/>
+<tag><c>{'Origin-Realm', &dict_DiameterIdentity;}</c></tag><item/>
<tag><c>{'Host-IP-Address', [&dict_Address;]}</c></tag>
<item>
<p>
@@ -352,8 +351,8 @@ question communicates an address list as described in
&man_transport;</p>
</item>
-<tag><c>{'Vendor-Id', &dict_Unsigned32;}</c></tag>
-<tag><c>{'Product-Name', &dict_UTF8String;}</c></tag>
+<tag><c>{'Vendor-Id', &dict_Unsigned32;}</c></tag><item/>
+<tag><c>{'Product-Name', &dict_UTF8String;}</c></tag><item/>
<tag><c>{'Origin-State-Id', &dict_Unsigned32;}</c></tag>
<item>
<p>
@@ -366,8 +365,8 @@ can be used as to retrieve a value that is computed when the diameter
application is started.</p>
</item>
-<tag><c>{'Supported-Vendor-Id', [&dict_Unsigned32;]}</c></tag>
-<tag><c>{'Auth-Application-Id', [&dict_Unsigned32;]}</c></tag>
+<tag><c>{'Supported-Vendor-Id', [&dict_Unsigned32;]}</c></tag><item/>
+<tag><c>{'Auth-Application-Id', [&dict_Unsigned32;]}</c></tag><item/>
<tag><c>{'Inband-Security-Id', [&dict_Unsigned32;]}</c></tag>
<item>
<p>
@@ -377,9 +376,9 @@ If 1 (TLS) is specified then TLS is selected if the CER/CEA received
from the peer offers it.</p>
</item>
-<tag><c>{'Acct-Application-Id', [&dict_Unsigned32;]}</c></tag>
-<tag><c>{'Vendor-Specific-Application-Id', [&dict_Grouped;]}</c></tag>
-<tag><c>{'Firmware-Revision', &dict_Unsigned32;}</c></tag>
+<tag><c>{'Acct-Application-Id', [&dict_Unsigned32;]}</c></tag><item/>
+<tag><c>{'Vendor-Specific-Application-Id', [&dict_Grouped;]}</c></tag><item/>
+<tag><c>{'Firmware-Revision', &dict_Unsigned32;}</c></tag><item/>
</taglist>
@@ -567,9 +566,8 @@ Can have one of the following types.</p>
<taglist>
-<tag><c>start</c></tag>
+<tag><c>start</c></tag><item/>
<tag><c>stop</c></tag>
-
<item>
<p>
The service is being started or stopped.
@@ -578,8 +576,8 @@ No event follows a <c>stop</c> event, and this event
implies the termination of all transport processes.</p>
</item>
-<tag><c>{up, Ref, Peer, Config, Pkt}</c></tag>
-<tag><c>{up, Ref, Peer, Config}</c></tag>
+<tag><c>{up, Ref, Peer, Config, Pkt}</c></tag><item/>
+<tag><c>{up, Ref, Peer, Config}</c></tag><item/>
<tag><c>{down, Ref, Peer, Config}</c></tag>
<item>
<pre>
@@ -788,8 +786,8 @@ be matched by corresponding &capability; configuration, of
</item>
-<marker id="incoming_maxlen"/>
-<tag><c>{incoming_maxlen, 0..16777215}</c></tag>
+<tag>
+<marker id="incoming_maxlen"/><c>{incoming_maxlen, 0..16777215}</c></tag>
<item>
<p>
Bound on the expected size of incoming Diameter messages.
@@ -917,8 +915,8 @@ Options <c>monitor</c> and <c>link</c> are ignored.</p>
Defaults to the empty list.</p>
</item>
-<marker id="strict_mbit"/>
-<tag><c>{strict_mbit, boolean()}</c></tag>
+<tag>
+<marker id="strict_mbit"/><c>{strict_mbit, boolean()}</c></tag>
<item>
<p>
Whether or not to regard an AVP setting the M-bit as erroneous when
@@ -935,7 +933,7 @@ Defaults to <c>true</c>.</p>
<p>
RFC 6733 is unclear about the semantics of the M-bit.
One the one hand, the CCF specification in section 3.2 documents AVP
-in a command grammar as meaning <b>any</b> arbitrary AVP; on the
+in a command grammar as meaning <em>any</em> arbitrary AVP; on the
other hand, 1.3.4 states that AVPs setting the M-bit cannot be added
to an existing command: the modified command must instead be
placed in a new Diameter application.</p>
@@ -945,7 +943,7 @@ allowing arbitrary AVPs setting the M-bit in a command makes its
interpretation implementation-dependent, since there's no
guarantee that all implementations will understand the same set of
arbitrary AVPs in the context of a given command.
-However, interpreting <c>AVP</c> in a command grammar as <b>any</b>
+However, interpreting <c>AVP</c> in a command grammar as any
AVP, regardless of M-bit, renders 1.3.4 meaningless, since the receiver
can simply ignore any AVP it thinks isn't relevant, regardless of the
sender's intent.</p>
@@ -960,8 +958,8 @@ occur in the message in question.</p>
</item>
-<marker id="string_decode"/>
-<tag><c>{string_decode, boolean()}</c></tag>
+<tag>
+<marker id="string_decode"/><c>{string_decode, boolean()}</c></tag>
<item>
<p>
Whether or not to decode AVPs of type &dict_OctetString; and its
@@ -1031,8 +1029,9 @@ Option passed to &add_transport;.
Has one of the following types.</p>
<taglist>
-<marker id="applications"/>
-<tag><c>{applications, [&application_alias;]}</c></tag>
+
+<tag>
+<marker id="applications"/><c>{applications, [&application_alias;]}</c></tag>
<item>
<p>
Diameter applications to which the transport should be restricted.
@@ -1050,8 +1049,8 @@ implies having to set matching *-Application-Id AVPs in a
</item>
-<marker id="capabilities"/>
-<tag><c>{capabilities, [&capability;]}</c></tag>
+<tag>
+<marker id="capabilities"/><c>{capabilities, [&capability;]}</c></tag>
<item>
<p>
AVPs used to construct outgoing CER/CEA messages.
@@ -1064,8 +1063,8 @@ may be particularly appropriate for Inband-Security-Id, in case
TLS is desired over TCP as implemented by &man_tcp;.</p>
</item>
-<marker id="capabilities_cb"/>
-<tag><c>{capabilities_cb, &evaluable;}</c></tag>
+<tag>
+<marker id="capabilities_cb"/><c>{capabilities_cb, &evaluable;}</c></tag>
<item>
<p>
Callback invoked upon reception of CER/CEA during capabilities
@@ -1112,8 +1111,8 @@ case the corresponding callbacks are applied until either all return
<c>ok</c> or one does not.</p>
</item>
-<marker id="capx_timeout"/>
-<tag><c>{capx_timeout, &dict_Unsigned32;}</c></tag>
+<tag>
+<marker id="capx_timeout"/><c>{capx_timeout, &dict_Unsigned32;}</c></tag>
<item>
<p>
Number of milliseconds after which a transport process having an
@@ -1127,8 +1126,8 @@ For a listening transport, the peer determines the timing.</p>
Defaults to 10000.</p>
</item>
-<marker id="connect_timer"/>
-<tag><c>{connect_timer, Tc}</c></tag>
+<tag>
+<marker id="connect_timer"/><c>{connect_timer, Tc}</c></tag>
<item>
<pre>
Tc = &dict_Unsigned32;
@@ -1158,9 +1157,8 @@ Defaults to 30000 for a connecting transport and 60000 for a listening
transport.</p>
</item>
-<marker id="disconnect_cb"/>
-<tag><c>{disconnect_cb, &evaluable;}</c></tag>
-
+<tag>
+<marker id="disconnect_cb"/><c>{disconnect_cb, &evaluable;}</c></tag>
<item>
<p>
Callback invoked prior to terminating the transport process of a
@@ -1236,8 +1234,8 @@ configured them.</p>
Defaults to a single callback returning <c>dpr</c>.</p>
</item>
-<marker id="dpa_timeout"/>
-<tag><c>{dpa_timeout, &dict_Unsigned32;}</c></tag>
+<tag>
+<marker id="dpa_timeout"/><c>{dpa_timeout, &dict_Unsigned32;}</c></tag>
<item>
<p>
Number of milliseconds after which a transport connection is
@@ -1247,8 +1245,8 @@ terminated following an outgoing DPR if DPA is not received.</p>
Defaults to 1000.</p>
</item>
-<marker id="dpr_timeout"/>
-<tag><c>{dpr_timeout, &dict_Unsigned32;}</c></tag>
+<tag>
+<marker id="dpr_timeout"/><c>{dpr_timeout, &dict_Unsigned32;}</c></tag>
<item>
<p>
Number of milliseconds after which a transport connection is
@@ -1259,8 +1257,8 @@ connection.</p>
Defaults to 5000.</p>
</item>
-<marker id="length_errors"/>
-<tag><c>{length_errors, exit|handle|discard}</c></tag>
+<tag>
+<marker id="length_errors"/><c>{length_errors, exit|handle|discard}</c></tag>
<item>
<p>
How to deal with errors in the Message Length field of the
@@ -1308,8 +1306,8 @@ the same peer.</p>
</item>
-<marker id="spawn_opt"/>
-<tag><c>{spawn_opt, [term()]}</c></tag>
+<tag>
+<marker id="spawn_opt"/><c>{spawn_opt, [term()]}</c></tag>
<item>
<p>
Options passed to &spawn_opt; when spawning a process for an
@@ -1320,15 +1318,15 @@ Options <c>monitor</c> and <c>link</c> are ignored.</p>
Defaults to the list configured on the service if not specified.</p>
</item>
-<marker id="transport_config"/>
-<tag><c>{transport_config, term()}</c></tag>
+<tag>
+<marker id="transport_config"/><c>{transport_config, term()}</c></tag><item/>
<tag><c>{transport_config, term(), &dict_Unsigned32; | infinity}</c></tag>
<item>
<p>
Term passed as the third argument to the &transport_start; function of
the relevant &transport_module; in order to
start a transport process.
-Defaults to the empty list if unspecified.</p>
+Defaults to the empty list.</p>
<p>
The 3-tuple form additionally specifies an interval, in milliseconds,
@@ -1349,12 +1347,12 @@ request a connection with one peer over SCTP or another
To listen on both SCTP and TCP, define one transport for each.</p>
</item>
-<marker id="transport_module"/>
-<tag><c>{transport_module, atom()}</c></tag>
+<tag>
+<marker id="transport_module"/><c>{transport_module, atom()}</c></tag>
<item>
<p>
Module implementing a transport process as defined in &man_transport;.
-Defaults to <c>diameter_tcp</c> if unspecified.</p>
+Defaults to <c>diameter_tcp</c>.</p>
<p>
Multiple <c>transport_module</c> and &transport_config;
@@ -1369,8 +1367,9 @@ modules in order until one establishes a connection within the
corresponding timeout (see below) or all fail.</p>
</item>
-<marker id="watchdog_config"/>
-<tag><c>{watchdog_config, [{okay|suspect, non_neg_integer()}]}</c></tag>
+<tag>
+<marker id="watchdog_config"/><c>{watchdog_config,
+ [{okay|suspect, non_neg_integer()}]}</c></tag>
<item>
<p>
Configuration that alters the behaviour of the watchdog
@@ -1393,8 +1392,8 @@ misbehaving nodes during test.</p>
</warning>
</item>
-<marker id="watchdog_timer"/>
-<tag><c>{watchdog_timer, TwInit}</c></tag>
+<tag>
+<marker id="watchdog_timer"/><c>{watchdog_timer, TwInit}</c></tag>
<item>
<pre>
TwInit = &dict_Unsigned32;
@@ -1412,7 +1411,7 @@ the callback.</p>
<p>
An integer value must be at least 6000 as required by RFC 3539.
-Defaults to 30000 if unspecified.</p>
+Defaults to 30000.</p>
</item>
</taglist>
@@ -1422,10 +1421,10 @@ Unrecognized options are silently ignored but are returned unmodified
by &service_info; and can be referred to
in predicate functions passed to &remove_transport;.</p>
-<marker id="transport_ref"/>
</item>
-<tag><c>transport_ref() = reference()</c></tag>
+<tag>
+<marker id="transport_ref"/><c>transport_ref() = reference()</c></tag>
<item>
<p>
Reference returned by &add_transport; that
@@ -1682,17 +1681,17 @@ returned.</p>
<taglist>
-<tag><c>'Origin-Host'</c></tag>
-<tag><c>'Origin-Realm'</c></tag>
-<tag><c>'Vendor-Id'</c></tag>
-<tag><c>'Product-Name'</c></tag>
-<tag><c>'Origin-State-Id'</c></tag>
-<tag><c>'Host-IP-Address'</c></tag>
-<tag><c>'Supported-Vendor'</c></tag>
-<tag><c>'Auth-Application-Id'</c></tag>
-<tag><c>'Inband-Security-Id'</c></tag>
-<tag><c>'Acct-Application-Id'</c></tag>
-<tag><c>'Vendor-Specific-Application-Id'</c></tag>
+<tag><c>'Origin-Host'</c></tag><item/>
+<tag><c>'Origin-Realm'</c></tag><item/>
+<tag><c>'Vendor-Id'</c></tag><item/>
+<tag><c>'Product-Name'</c></tag><item/>
+<tag><c>'Origin-State-Id'</c></tag><item/>
+<tag><c>'Host-IP-Address'</c></tag><item/>
+<tag><c>'Supported-Vendor'</c></tag><item/>
+<tag><c>'Auth-Application-Id'</c></tag><item/>
+<tag><c>'Inband-Security-Id'</c></tag><item/>
+<tag><c>'Acct-Application-Id'</c></tag><item/>
+<tag><c>'Vendor-Specific-Application-Id'</c></tag><item/>
<tag><c>'Firmware-Revision'</c></tag>
<item>
<p>
diff --git a/lib/diameter/doc/src/diameter_app.xml b/lib/diameter/doc/src/diameter_app.xml
index 973b6eb732..dfcd00975b 100644
--- a/lib/diameter/doc/src/diameter_app.xml
+++ b/lib/diameter/doc/src/diameter_app.xml
@@ -90,17 +90,14 @@ is called in response to an incoming Diameter request message.</p>
</list>
-</description>
-
-<note>
<p>
-The arities given for the the callback functions here assume no extra
-arguments.
+The arities for the the callback functions here assume no extra arguments.
All functions will also be passed any extra arguments configured with
the callback module itself when calling &mod_start_service;
and, for the call-specific callbacks, any extra arguments passed to
&mod_call;.</p>
-</note>
+
+</description>
<!-- ===================================================================== -->
<!-- ===================================================================== -->
@@ -110,9 +107,8 @@ and, for the call-specific callbacks, any extra arguments passed to
<taglist>
-<marker id="capabilities"/>
-
-<tag><c>capabilities() = #diameter_caps{}</c></tag>
+<tag>
+<marker id="capabilities"/><c>capabilities() = #diameter_caps{}</c></tag>
<item>
<p>
A record containing the identities of
@@ -126,9 +122,8 @@ Optional or possibly multiple values are encoded as lists of values,
mandatory values as the bare value.</p>
</item>
-<marker id="message"/>
-
-<tag><c>message() = &codec_message;</c></tag>
+<tag>
+<marker id="message"/><c>message() = &codec_message;</c></tag>
<item>
<p>
The representation of a Diameter message as passed to
@@ -136,9 +131,8 @@ The representation of a Diameter message as passed to
</item>
-<marker id="packet"/>
-
-<tag><c>packet() = &codec_packet;</c></tag>
+<tag>
+<marker id="packet"/><c>packet() = &codec_packet;</c></tag>
<item>
<p>
A container for incoming and outgoing Diameter messages that's passed
@@ -146,25 +140,22 @@ through encode/decode and transport.
Fields should not be set in return values except as documented.</p>
</item>
-<marker id="peer_ref"/>
-
-<tag><c>peer_ref() = term()</c></tag>
+<tag>
+<marker id="peer_ref"/><c>peer_ref() = term()</c></tag>
<item>
<p>
A term identifying a transport connection with a Diameter peer.</p>
</item>
-<marker id="peer"/>
-
-<tag><c>peer() = {&peer_ref;, &capabilities;}</c></tag>
+<tag>
+<marker id="peer"/><c>peer() = {&peer_ref;, &capabilities;}</c></tag>
<item>
<p>
A tuple representing a Diameter peer connection.</p>
</item>
-<marker id="state"/>
-
-<tag><c>state() = term()</c></tag>
+<tag>
+<marker id="state"/><c>state() = term()</c></tag>
<item>
<p>
The state maintained by the application callback functions
diff --git a/lib/diameter/doc/src/diameter_codec.xml b/lib/diameter/doc/src/diameter_codec.xml
index a0313e2877..91e96058dd 100644
--- a/lib/diameter/doc/src/diameter_codec.xml
+++ b/lib/diameter/doc/src/diameter_codec.xml
@@ -88,10 +88,9 @@ files resulting from dictionary file compilation.</p>
<taglist>
-<marker id="integers"/>
-
-<tag><c>uint8()&nbsp; = 0..255</c></tag>
-<tag><c>uint24() = 0..16777215</c></tag>
+<tag>
+<marker id="integers"/><c>uint8()&nbsp; = 0..255</c></tag><item/>
+<tag><c>uint24() = 0..16777215</c></tag><item/>
<tag><c>uint32() = 0..4294967295</c></tag>
<item>
<p>
@@ -99,9 +98,8 @@ files resulting from dictionary file compilation.</p>
headers.</p>
</item>
-<marker id="avp"/>
-
-<tag><c>avp() = #diameter_avp{}</c></tag>
+<tag>
+<marker id="avp"/><c>avp() = #diameter_avp{}</c></tag>
<item>
<p>
The application-neutral representation of an AVP.
@@ -116,9 +114,9 @@ Fields have the following types.</p>
<taglist>
-<tag><c>code = uint32()</c></tag>
-<tag><c>is_mandatory = boolean()</c></tag>
-<tag><c>need_encryption = boolean()</c></tag>
+<tag><c>code = uint32()</c></tag><item/>
+<tag><c>is_mandatory = boolean()</c></tag><item/>
+<tag><c>need_encryption = boolean()</c></tag><item/>
<tag><c>vendor_id = uint32() | undefined</c></tag>
<item>
<p>
@@ -167,9 +165,8 @@ Possible types are <c>undefined</c> and the Diameter types:
</item>
-<marker id="dictionary"/>
-
-<tag><c>dictionary() = module()</c></tag>
+<tag>
+<marker id="dictionary"/><c>dictionary() = module()</c></tag>
<item>
<p>
@@ -179,9 +176,8 @@ The interface provided by a dictionary module is an
implementation detail that may change.</p>
</item>
-<marker id="header"/>
-
-<tag><c>header() = #diameter_header{}</c></tag>
+<tag>
+<marker id="header"/><c>header() = #diameter_header{}</c></tag>
<item>
<p>
The record representation of the Diameter header.
@@ -204,11 +200,11 @@ Fields have the following types.</p>
<taglist>
-<tag><c>version = uint8()</c></tag>
-<tag><c>length = uint24()</c></tag>
-<tag><c>cmd_code = uint24()</c></tag>
-<tag><c>application_id = uint32()</c></tag>
-<tag><c>hop_by_hop_id = uint32()</c></tag>
+<tag><c>version = uint8()</c></tag><item/>
+<tag><c>length = uint24()</c></tag><item/>
+<tag><c>cmd_code = uint24()</c></tag><item/>
+<tag><c>application_id = uint32()</c></tag><item/>
+<tag><c>hop_by_hop_id = uint32()</c></tag><item/>
<tag><c>end_to_end_id = uint32()</c></tag>
<item>
<p>
@@ -217,9 +213,9 @@ Hop-by-Hop Identifier and End-to-End Identifier fields of the Diameter
header.</p>
</item>
-<tag><c>is_request = boolean()</c></tag>
-<tag><c>is_proxiable = boolean()</c></tag>
-<tag><c>is_error = boolean()</c></tag>
+<tag><c>is_request = boolean()</c></tag><item/>
+<tag><c>is_proxiable = boolean()</c></tag><item/>
+<tag><c>is_error = boolean()</c></tag><item/>
<tag><c>is_retransmitted = boolean()</c></tag>
<item>
<p>
@@ -232,9 +228,8 @@ header.</p>
</item>
-<marker id="message"/>
-
-<tag><c>message() = record() | list()</c></tag>
+<tag>
+<marker id="message"/><c>message() = record() | list()</c></tag>
<item>
<p>
The representation of a Diameter message as passed to
@@ -257,9 +252,8 @@ question: messages are sent exactly as specified.</p>
</item>
-<marker id="packet"/>
-
-<tag><c>packet() = #diameter_packet{}</c></tag>
+<tag>
+<marker id="packet"/><c>packet() = #diameter_packet{}</c></tag>
<item>
<p>
A container for incoming and outgoing Diameter messages.
@@ -296,7 +290,7 @@ corresponding values.</p>
<warning>
<p>
-A record-valued <c>msg</c> field does <b>not</b> imply an absence of
+A record-valued <c>msg</c> field does <em>not</em> imply an absence of
decode errors.
The <c>errors</c> field should also be examined.</p>
</warning>
diff --git a/lib/diameter/doc/src/diameter_dict.xml b/lib/diameter/doc/src/diameter_dict.xml
index ae40f99aee..9584d682c2 100644
--- a/lib/diameter/doc/src/diameter_dict.xml
+++ b/lib/diameter/doc/src/diameter_dict.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8" ?>
-<!DOCTYPE erlref SYSTEM "fileref.dtd" [
+<!DOCTYPE fileref SYSTEM "fileref.dtd" [
<!ENTITY format
'<seealso marker="#FILE_FORMAT">FILE FORMAT</seealso>'>
<!ENTITY records
@@ -121,9 +121,8 @@ The order in which sections are specified is unimportant.</p>
<taglist>
-<marker id="id"/>
-
-<tag><c>@id Number</c></tag>
+<tag>
+<marker id="id"/><c>@id Number</c></tag>
<item>
<p>
Defines the integer Number as the Diameter Application Id of the
@@ -146,14 +145,13 @@ Example:</p>
</item>
-<marker id="name"/>
-
-<tag><c>@name Mod</c></tag>
+<tag>
+<marker id="name"/><c>@name Mod</c></tag>
<item>
<p>
Defines the name of the generated dictionary module.
Can occur at most once and defaults to the name of the dictionary file
-minus any extension if unspecified.
+minus any extension.
The section has empty content.</p>
<p>
@@ -169,9 +167,8 @@ Example:</p>
</item>
-<marker id="prefix"/>
-
-<tag><c>@prefix Name</c></tag>
+<tag>
+<marker id="prefix"/><c>@prefix Name</c></tag>
<item>
<p>
Defines Name as the prefix to be added to record and constant names
@@ -194,9 +191,8 @@ Example:</p>
</item>
-<marker id="vendor"/>
-
-<tag><c>@vendor Number Name</c></tag>
+<tag>
+<marker id="vendor"/><c>@vendor Number Name</c></tag>
<item>
<p>
Defines the integer Number as the the default Vendor-Id of AVPs for
@@ -216,9 +212,8 @@ Example:</p>
</item>
-<marker id="avp_vendor_id"/>
-
-<tag><c>@avp_vendor_id Number</c></tag>
+<tag>
+<marker id="avp_vendor_id"/><c>@avp_vendor_id Number</c></tag>
<item>
<p>
Defines the integer Number as the Vendor-Id of the AVPs listed in the
@@ -238,9 +233,8 @@ Region-Set
</item>
-<marker id="inherits"/>
-
-<tag><c>@inherits Mod</c></tag>
+<tag>
+<marker id="inherits"/><c>@inherits Mod</c></tag>
<item>
<p>
Defines the name of a dictionary module containing AVP
@@ -274,9 +268,8 @@ Example:</p>
</pre>
</item>
-<marker id="avp_types"/>
-
-<tag><c>@avp_types</c></tag>
+<tag>
+<marker id="avp_types"/><c>@avp_types</c></tag>
<item>
<p>
Defines the name, code, type and flags of individual AVPs.
@@ -308,9 +301,8 @@ The P flag has been deprecated by &the_rfc;.</p>
</item>
-<marker id="custom_types"/>
-
-<tag><c>@custom_types Mod</c></tag>
+<tag>
+<marker id="custom_types"/><c>@custom_types Mod</c></tag>
<item>
<p>
Specifies AVPs for which module Mod provides encode/decode functions.
@@ -331,9 +323,8 @@ Framed-IP-Address
</pre>
</item>
-<marker id="codecs"/>
-
-<tag><c>@codecs Mod</c></tag>
+<tag>
+<marker id="codecs"/><c>@codecs Mod</c></tag>
<item>
<p>
Like <c>@custom_types</c> but requires the specified module to export
@@ -350,9 +341,8 @@ Framed-IP-Address
</pre>
</item>
-<marker id="messages"/>
-
-<tag><c>@messages</c></tag>
+<tag>
+<marker id="messages"/><c>@messages</c></tag>
<item>
<p>
Defines the messages of the application.
@@ -397,9 +387,8 @@ RTA ::= &lt; Diameter Header: 287, PXY >
</item>
-<marker id="grouped"/>
-
-<tag><c>@grouped</c></tag>
+<tag>
+<marker id="grouped"/><c>@grouped</c></tag>
<item>
<p>
Defines the contents of the AVPs of the application having type
@@ -424,9 +413,8 @@ Specifying a Vendor-Id in the definition of a grouped AVP is
equivalent to specifying it with <c>@avp_vendor_id</c>.</p>
</item>
-<marker id="enum"/>
-
-<tag><c>@enum Name</c></tag>
+<tag>
+<marker id="enum"/><c>@enum Name</c></tag>
<item>
<p>
Defines values of AVP Name having type Enumerated.
@@ -452,9 +440,8 @@ REMOVE_SIP_SERVER 3
</pre>
</item>
-<marker id="end"/>
-
-<tag><c>@end</c></tag>
+<tag>
+<marker id="end"/><c>@end</c></tag>
<item>
<p>
Causes parsing of the dictionary to terminate:
diff --git a/lib/diameter/doc/src/diameter_examples.xml b/lib/diameter/doc/src/diameter_examples.xml
index 853ef96bb3..2e1f2b3c03 100644
--- a/lib/diameter/doc/src/diameter_examples.xml
+++ b/lib/diameter/doc/src/diameter_examples.xml
@@ -42,4 +42,3 @@ Example code can be found in the diameter application's
<c>examples</c> subdirectory.</p>
</chapter>
-
diff --git a/lib/diameter/doc/src/diameter_tcp.xml b/lib/diameter/doc/src/diameter_tcp.xml
index 00ccc39c15..6ca280c52b 100644
--- a/lib/diameter/doc/src/diameter_tcp.xml
+++ b/lib/diameter/doc/src/diameter_tcp.xml
@@ -150,7 +150,7 @@ Options <c>binary</c>,
<c>packet</c> and <c>active</c> cannot be specified.
Also, option <c>port</c> can be specified for a listening transport
to specify the local listening port, the default being the standardized
-3868 if unspecified.
+3868.
Note that the option <c>ip</c> specifies the local address.</p>
<p>
diff --git a/lib/diameter/doc/src/diameter_transport.xml b/lib/diameter/doc/src/diameter_transport.xml
index 736d4cbfbd..294e8a8864 100644
--- a/lib/diameter/doc/src/diameter_transport.xml
+++ b/lib/diameter/doc/src/diameter_transport.xml
@@ -65,9 +65,8 @@ parent).</p>
<taglist>
-<marker id="message"/>
-
-<tag><c>message() = binary() | &codec_packet;</c></tag>
+<tag>
+<marker id="message"/><c>message() = binary() | &codec_packet;</c></tag>
<item>
<p>
A Diameter message as passed over the transport interface.</p>
@@ -160,9 +159,9 @@ It should exit if its transport connection with its peer is lost.</p>
</funcs>
<!-- ===================================================================== -->
-<marker id="MESSAGES"/>
<section>
+<marker id="MESSAGES"/>
<title>MESSAGES</title>
<p>
@@ -234,7 +233,7 @@ established a connection with the peer.
Not sent if the transport process has <c>Type=connect</c>.</p>
</item>
-<tag><c>{diameter, {self(), connected, Remote}}</c></tag>
+<tag><c>{diameter, {self(), connected, Remote}}</c></tag><item/>
<tag><c>{diameter, {self(), connected, Remote, [LocalAddr]}}</c></tag>
<item>
<p>
diff --git a/lib/diameter/doc/src/diameterc.xml b/lib/diameter/doc/src/diameterc.xml
index 5bffe9a771..8f1c660989 100644
--- a/lib/diameter/doc/src/diameterc.xml
+++ b/lib/diameter/doc/src/diameterc.xml
@@ -11,7 +11,7 @@
<comref>
<header>
<copyright>
-<year>2011</year><year>2013</year>
+<year>2011</year><year>2016</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -79,14 +79,14 @@ Write generated source to the specified directory.
Defaults to the current working directory.</p>
</item>
-<tag><![CDATA[-E]]></tag>
+<tag><![CDATA[-E]]></tag><item/>
<tag><![CDATA[-H]]></tag>
<item>
<p>
Suppress erl and hrl generation, respectively.</p>
</item>
-<tag><![CDATA[--name <name>]]></tag>
+<tag><![CDATA[--name <name>]]></tag><item/>
<tag><![CDATA[--prefix <prefix>]]></tag>
<item>
<p>
diff --git a/lib/diameter/doc/src/notes.xml b/lib/diameter/doc/src/notes.xml
index b1be7bdcf7..c2bbed2e5a 100644
--- a/lib/diameter/doc/src/notes.xml
+++ b/lib/diameter/doc/src/notes.xml
@@ -43,6 +43,31 @@ first.</p>
<!-- ===================================================================== -->
+<section><title>diameter 1.12.1</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Close diameter_tcp/sctp listening sockets at
+ diameter:stop_service/1.</p>
+ <p>
+ Broken by OTP-13611.</p>
+ <p>
+ Own Id: OTP-13787 Aux Id: OTP-13611 </p>
+ </item>
+ <item>
+ <p>
+ Update build scripts to not make assumtions about where
+ env, cp and perl are located.</p>
+ <p>
+ Own Id: OTP-13800</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>diameter 1.12</title>
<section><title>Fixed Bugs and Malfunctions</title>
diff --git a/lib/diameter/examples/code/relay.erl b/lib/diameter/examples/code/relay.erl
index 3846b1d161..cf4ce8848b 100644
--- a/lib/diameter/examples/code/relay.erl
+++ b/lib/diameter/examples/code/relay.erl
@@ -53,7 +53,7 @@
{'Auth-Application-Id', [16#FFFFFFFF]},
{string_decode, false},
{application, [{alias, relay},
- {dictionary, diameter_relay},
+ {dictionary, diameter_gen_relay},
{module, relay_cb}]}]).
%% start/1
diff --git a/lib/diameter/src/Makefile b/lib/diameter/src/Makefile
index 7f61620fc1..6bf748a727 100644
--- a/lib/diameter/src/Makefile
+++ b/lib/diameter/src/Makefile
@@ -123,7 +123,7 @@ ERL_COMPILE_FLAGS += \
# erl/hrl from dictionary file.
gen/diameter_gen_%.erl gen/diameter_gen_%.hrl: dict/%.dia
$(dia_verbose) \
- ../bin/diameterc -o gen -i $(EBIN) $<
+ escript ../bin/diameterc -o gen -i $(EBIN) $<
opt: $(TARGET_FILES)
diff --git a/lib/diameter/src/base/diameter_lib.erl b/lib/diameter/src/base/diameter_lib.erl
index b835e87967..3928769b5e 100644
--- a/lib/diameter/src/base/diameter_lib.erl
+++ b/lib/diameter/src/base/diameter_lib.erl
@@ -299,8 +299,28 @@ spawn_opts(server, Opts) ->
spawn_opts(worker, Opts) ->
opts(5000, Opts).
-opts(HeapSize, Opts) ->
- [{min_heap_size, HeapSize} | lists:keydelete(min_heap_size, 1, Opts)].
+%% These setting are historical rather than useful. In particular, the
+%% server setting can bloat many processes unnecessarily. Let them be
+%% disabled with -diameter min_heap_size false.
+
+opts(Def, Opts) ->
+ Key = min_heap_size,
+ case getenv(Key, Def) of
+ N when is_integer(N), 0 =< N ->
+ [{Key, N} | lists:keydelete(Key, 1, Opts)];
+ _ ->
+ Opts
+ end.
+
+%% getenv/1
+
+getenv(Key, Def) ->
+ case application:get_env(Key) of
+ {ok, T} ->
+ T;
+ undefined ->
+ Def
+ end.
%% ---------------------------------------------------------------------------
%% # wait/1
diff --git a/lib/diameter/src/base/diameter_traffic.erl b/lib/diameter/src/base/diameter_traffic.erl
index 2112941d5e..d93a3e71e3 100644
--- a/lib/diameter/src/base/diameter_traffic.erl
+++ b/lib/diameter/src/base/diameter_traffic.erl
@@ -123,8 +123,16 @@ peer_up(TPid) ->
%% ---------------------------------------------------------------------------
peer_down(TPid) ->
- ets:delete(?REQUEST_TABLE, TPid),
- failover(TPid).
+ ets:delete_object(?REQUEST_TABLE, {TPid}),
+ lists:foreach(fun failover/1, ets:lookup(?REQUEST_TABLE, TPid)).
+%% Note that a request process can store its request after failover
+%% notifications are sent here: insert_request/2 sends the notification
+%% in that case.
+
+%% failover/1
+
+failover({_TPid, {Pid, TRef}}) ->
+ Pid ! {failover, TRef}.
%% ---------------------------------------------------------------------------
%% incr/4
@@ -911,7 +919,7 @@ failed(Rec, FailedAvp, Dict) ->
{'Failed-AVP', [FailedAvp]}
catch
error: _ ->
- Avps = Dict:'get-'('AVP', Rec),
+ Avps = Dict:'#get-'('AVP', Rec),
A = #diameter_avp{name = 'Failed-AVP',
value = FailedAvp},
{'AVP', [A|Avps]}
@@ -1452,7 +1460,7 @@ make_request_packet(#diameter_packet{header = Hdr} = Pkt,
make_request_packet(Msg, Pkt) ->
Pkt#diameter_packet{msg = Msg}.
-%% make_retransmit_packet/2
+%% make_retransmit_packet/1
make_retransmit_packet(#diameter_packet{msg = [#diameter_header{} = Hdr
| Avps]}
@@ -1703,16 +1711,13 @@ send_request(TPid, #diameter_packet{bin = Bin} = Pkt, Req, _SvcName, Timeout)
when node() == node(TPid) ->
Seqs = diameter_codec:sequence_numbers(Bin),
TRef = erlang:start_timer(Timeout, self(), TPid),
- Entry = {Seqs, Req, TRef},
+ Entry = {Seqs, #request{handler = Pid} = Req, TRef},
- %% Ensure that request table is cleaned even if we receive an exit
- %% signal. An alternative would be to simply trap exits, but
- %% callbacks are applied in this process, and these could possibly
- %% be expecting the prevailing behaviour.
- Self = self(),
- spawn(fun() -> diameter_lib:wait([Self]), erase_request(Entry) end),
+ %% Ensure that request table is cleaned even if the process is
+ %% killed.
+ spawn(fun() -> diameter_lib:wait([Pid]), delete_request(Entry) end),
- store_request(Entry, TPid),
+ insert_request(Entry),
send(TPid, Pkt),
TRef;
@@ -1774,6 +1779,8 @@ retransmit({TPid, Caps, App}
SvcName,
Timeout,
[]).
+%% When sending a binary, it's up to prepare_retransmit to modify it
+%% accordingly.
retransmit({send, Msg},
Transport,
@@ -1818,15 +1825,21 @@ resend_request(Pkt0,
TRef = send_request(TPid, Pkt, Req, SvcName, Tmo),
{TRef, Req}.
-%% store_request/2
+%% insert_request/1
-store_request(T, TPid) ->
- ets:insert(?REQUEST_TABLE, T),
- ets:member(?REQUEST_TABLE, TPid)
- orelse begin
- {_Seqs, _Req, TRef} = T,
- self() ! {failover, TRef} %% failover/1 may have missed
- end.
+insert_request({_Seqs, #request{transport = TPid}, TRef} = T) ->
+ ets:insert(?REQUEST_TABLE, [T, {TPid, {self(), TRef}}]),
+ is_peer_up(TPid)
+ orelse (self() ! {failover, TRef}). %% failover/1 may have missed
+
+%% is_peer_up/1
+%%
+%% Is the entry written by peer_up/1 and deleted by peer_down/1 still
+%% in the request table?
+
+is_peer_up(TPid) ->
+ Spec = [{{TPid}, [], ['$_']}],
+ '$end_of_table' /= ets:select(?REQUEST_TABLE, Spec, 1).
%% lookup_request/2
%%
@@ -1846,16 +1859,11 @@ lookup_request(Msg, TPid) ->
false
end.
-%% erase_request/1
-
-erase_request(T) ->
- ets:delete_object(?REQUEST_TABLE, T).
-
-%% match_requests/1
+%% delete_request/1
-match_requests(TPid) ->
- Pat = {'_', #request{transport = TPid, _ = '_'}, '_'},
- ets:select(?REQUEST_TABLE, [{Pat, [], ['$_']}]).
+delete_request({_Seqs, #request{handler = Pid, transport = TPid}, TRef} = T) ->
+ Spec = [{R, [], [true]} || R <- [T, {TPid, {Pid, TRef}}]],
+ ets:select_delete(?REQUEST_TABLE, Spec).
%% have_request/2
@@ -1864,28 +1872,6 @@ have_request(Pkt, TPid) ->
Pat = {Seqs, #request{transport = TPid, _ = '_'}, '_'},
'$end_of_table' /= ets:select(?REQUEST_TABLE, [{Pat, [], ['$_']}], 1).
-%% ---------------------------------------------------------------------------
-%% # failover/1-2
-%% ---------------------------------------------------------------------------
-
-failover(TPid)
- when is_pid(TPid) ->
- lists:foreach(fun failover/1, match_requests(TPid));
-%% Note that a request process can store its request after failover
-%% notifications are sent here: store_request/2 sends the notification
-%% in that case.
-
-%% Failover as a consequence of peer_down/1: inform the
-%% request process.
-failover({_, Req, TRef}) ->
- #request{handler = Pid,
- packet = #diameter_packet{msg = M}}
- = Req,
- M /= undefined andalso (Pid ! {failover, TRef}).
-%% Failover is not performed when msg = binary() since sending
-%% pre-encoded binaries is only partially supported. (Mostly for
-%% test.)
-
%% get_destination/2
get_destination(Dict, Msg) ->
diff --git a/lib/diameter/src/compiler/diameter_codegen.erl b/lib/diameter/src/compiler/diameter_codegen.erl
index cdaa9aa7f9..4007d6b7b1 100644
--- a/lib/diameter/src/compiler/diameter_codegen.erl
+++ b/lib/diameter/src/compiler/diameter_codegen.erl
@@ -790,20 +790,7 @@ header() ->
("%% -------------------------------------------------------------------\n"
"%% This is a generated file.\n"
"%% -------------------------------------------------------------------\n"
- "\n"
- "%%\n"
- "%% Copyright (c) Ericsson AB. All rights reserved.\n"
- "%%\n"
- "%% The information in this document is the property of Ericsson.\n"
- "%%\n"
- "%% Except as specifically authorized in writing by Ericsson, the\n"
- "%% receiver of this document shall keep the information contained\n"
- "%% herein confidential and shall protect the same in whole or in\n"
- "%% part from disclosure and dissemination to third parties.\n"
- "%%\n"
- "%% Disclosure and disseminations to the receivers employees shall\n"
- "%% only be made on a strict need to know basis.\n"
- "%%\n\n").
+ "\n").
hrl_header(Name) ->
header() ++ "-hrl_name('" ++ ?S(Name) ++ ".hrl').\n".
diff --git a/lib/diameter/src/diameter.appup.src b/lib/diameter/src/diameter.appup.src
index 618d5a7f10..b1b8e38d39 100644
--- a/lib/diameter/src/diameter.appup.src
+++ b/lib/diameter/src/diameter.appup.src
@@ -49,7 +49,11 @@
{"1.10", [{restart_application, diameter}]}, %% 18.0
{"1.11", [{restart_application, diameter}]}, %% 18.1
{"1.11.1", [{restart_application, diameter}]}, %% 18.2
- {"1.11.2", [{restart_application, diameter}]} %% 18.3
+ {"1.11.2", [{restart_application, diameter}]}, %% 18.3
+ {"1.12", [{load_module, diameter_lib}, %% 19.0
+ {load_module, diameter_traffic},
+ {load_module, diameter_tcp},
+ {load_module, diameter_sctp}]}
],
[
{"0.9", [{restart_application, diameter}]},
@@ -80,6 +84,10 @@
{"1.10", [{restart_application, diameter}]},
{"1.11", [{restart_application, diameter}]},
{"1.11.1", [{restart_application, diameter}]},
- {"1.11.2", [{restart_application, diameter}]}
+ {"1.11.2", [{restart_application, diameter}]},
+ {"1.12", [{load_module, diameter_sctp},
+ {load_module, diameter_tcp},
+ {load_module, diameter_traffic},
+ {load_module, diameter_lib}]}
]
}.
diff --git a/lib/diameter/src/transport/diameter_sctp.erl b/lib/diameter/src/transport/diameter_sctp.erl
index 4a005b853d..f48e4347ee 100644
--- a/lib/diameter/src/transport/diameter_sctp.erl
+++ b/lib/diameter/src/transport/diameter_sctp.erl
@@ -98,7 +98,7 @@
-record(listener,
{ref :: reference(),
socket :: gen_sctp:sctp_socket(),
- count = 0 :: uint(), %% attached transport processes
+ service = false :: false | pid(), %% service process
pending = {0, queue:new()},
accept :: [match()]}).
%% Field pending implements two queues: the first of transport-to-be
@@ -129,11 +129,14 @@
-> {ok, pid(), [inet:ip_address()]}
when Ref :: diameter:transport_ref().
-start(T, #diameter_service{capabilities = Caps}, Opts)
+start(T, Svc, Opts)
when is_list(Opts) ->
+ #diameter_service{capabilities = Caps,
+ pid = SPid}
+ = Svc,
diameter_sctp_sup:start(), %% start supervisors on demand
Addrs = Caps#diameter_caps.host_ip_address,
- s(T, Addrs, lists:map(fun ip/1, Opts)).
+ s(T, Addrs, SPid, lists:map(fun ip/1, Opts)).
ip({ifaddr, A}) ->
{ip, A};
@@ -144,18 +147,22 @@ ip(T) ->
%% when there is not yet an association to assign it, or at comm_up on
%% a new association in which case the call retrieves a transport from
%% the pending queue.
-s({accept, Ref} = A, Addrs, Opts) ->
- {LPid, LAs} = listener(Ref, {Opts, Addrs}),
- try gen_server:call(LPid, {A, self()}, infinity) of
- {ok, TPid} -> {ok, TPid, LAs}
+s({accept, Ref} = A, Addrs, SPid, Opts) ->
+ {ok, LPid, LAs} = listener(Ref, {Opts, Addrs}),
+ try gen_server:call(LPid, {A, self(), SPid}, infinity) of
+ {ok, TPid} ->
+ {ok, TPid, LAs};
+ No ->
+ {error, No}
catch
- exit: Reason -> {error, Reason}
+ exit: Reason ->
+ {error, Reason}
end;
%% This implementation is due to there being no accept call in
%% gen_sctp in order to be able to accept a new association only
%% *after* an accepting transport has been spawned.
-s({connect = C, Ref}, Addrs, Opts) ->
+s({connect = C, Ref}, Addrs, _SPid, Opts) ->
diameter_sctp_sup:start_child({C, self(), Opts, Addrs, Ref}).
%% start_link/1
@@ -281,24 +288,23 @@ i({K, Ref}, #transport{mode = {accept, _}} = S) ->
%% Accepting processes can be started concurrently: ensure only one
%% listener is started.
-listener(LRef, T) ->
- diameter_sync:call({?MODULE, listener, LRef},
- {?MODULE, listener, [{LRef, T}]},
+listener(Ref, T) ->
+ diameter_sync:call({?MODULE, listener, Ref},
+ {?MODULE, listener, [{Ref, T}]},
infinity,
infinity).
-listener({LRef, T}) ->
- l(diameter_reg:match({?MODULE, listener, {LRef, '_'}}), LRef, T).
+listener({Ref, T}) ->
+ l(diameter_reg:match({?MODULE, listener, {Ref, '_'}}), Ref, T).
%% Existing listening process ...
l([{{?MODULE, listener, {_, AS}}, LPid}], _, _) ->
- {LAs, _Sock} = AS,
- {LPid, LAs};
+ {LAs, _Sock} = AS,
+ {ok, LPid, LAs};
%% ... or not.
-l([], LRef, T) ->
- {ok, LPid, LAs} = diameter_sctp_sup:start_child({listen, LRef, T}),
- {LPid, LAs}.
+l([], Ref, T) ->
+ diameter_sctp_sup:start_child({listen, Ref, T}).
%% open/3
@@ -364,11 +370,17 @@ type(T) ->
%% # handle_call/3
%% ---------------------------------------------------------------------------
-handle_call({{accept, Ref}, Pid}, _, #listener{ref = Ref,
- count = K}
- = S) ->
+handle_call({{accept, Ref}, Pid}, _, #listener{ref = Ref} = S) ->
{TPid, NewS} = accept(Ref, Pid, S),
- {reply, {ok, TPid}, NewS#listener{count = K+1}};
+ {reply, {ok, TPid}, NewS};
+
+handle_call({{accept, _} = T, Pid, SPid}, From, #listener{service = P} = S) ->
+ handle_call({T, Pid}, From, if not is_pid(P), is_pid(SPid) ->
+ monitor(process, SPid),
+ S#listener{service = SPid};
+ true ->
+ S
+ end);
handle_call(_, _, State) ->
{reply, nok, State}.
@@ -441,6 +453,13 @@ l({sctp, Sock, _RA, _RP, Data} = T, #listener{socket = Sock,
setopts(Sock),
NewS;
+%% Service process has died.
+l({'DOWN', _, process, Pid, _} = T, #listener{service = Pid,
+ socket = Sock}) ->
+ gen_sctp:close(Sock),
+ x(T);
+
+%% Accepting process has died.
l({'DOWN', _MRef, process, TPid, _}, #listener{pending = {_,Q}} = S) ->
down(queue:member(TPid, Q), TPid, S);
@@ -454,20 +473,17 @@ l({transport, remove, _} = T, #listener{socket = Sock}) ->
%% Accepting transport has died.
%% One that's waiting for transport start in the pending queue ...
-down(true, TPid, #listener{pending = {N,Q},
- count = K}
- = S) ->
+down(true, TPid, #listener{pending = {N,Q}} = S) ->
NQ = queue:filter(fun(P) -> P /= TPid end, Q),
if N < 0 -> %% awaiting an association ...
- S#listener{count = K-1,
- pending = {N+1, NQ}};
+ S#listener{pending = {N+1, NQ}};
true -> %% ... or one has been assigned
S#listener{pending = {N-1, NQ}}
end;
%% ... or one that's already attached.
-down(false, _TPid, #listener{count = K} = S) ->
- S#listener{count = K-1}.
+down(false, _TPid, S) ->
+ S.
%% t/2
%%
diff --git a/lib/diameter/src/transport/diameter_tcp.erl b/lib/diameter/src/transport/diameter_tcp.erl
index 546c2cfa5e..44abc5c3b4 100644
--- a/lib/diameter/src/transport/diameter_tcp.erl
+++ b/lib/diameter/src/transport/diameter_tcp.erl
@@ -71,11 +71,8 @@
%% a process owning the listening port.
%% Listener process state.
--record(listener, {socket :: inet:socket(),
- count = 1 :: non_neg_integer()}). %% accepting processes
-%% The count of accepting processes was previously used to terminate
-%% the listening process, but diameter_reg:subscribe/2 is now used for
-%% this. Leave the the count for trace purposes.
+-record(listener, {socket :: inet:socket(),
+ service = false :: false | pid()}). %% service process
%% Monitor process state.
-record(monitor,
@@ -138,11 +135,15 @@
| {ok, pid()}
when Ref :: diameter:transport_ref().
-start({T, Ref}, #diameter_service{capabilities = Caps}, Opts) ->
+start({T, Ref}, Svc, Opts) ->
+ #diameter_service{capabilities = Caps,
+ pid = SPid}
+ = Svc,
+
diameter_tcp_sup:start(), %% start tcp supervisors on demand
{Mod, Rest} = split(Opts),
Addrs = Caps#diameter_caps.host_ip_address,
- Arg = {T, Ref, Mod, self(), Rest, Addrs},
+ Arg = {T, Ref, Mod, self(), Rest, Addrs, SPid},
diameter_tcp_sup:start_child(Arg).
split([{module, M} | Opts]) ->
@@ -196,7 +197,7 @@ init(T) ->
%% i/1
%% A transport process.
-i({T, Ref, Mod, Pid, Opts, Addrs})
+i({T, Ref, Mod, Pid, Opts, Addrs, SPid})
when T == accept;
T == connect ->
monitor(process, Pid),
@@ -214,7 +215,7 @@ i({T, Ref, Mod, Pid, Opts, Addrs})
?DEFAULT_FRAGMENT_TIMEOUT),
?IS_TIMEOUT(Tmo) orelse ?ERROR({fragment_timer, Tmo}),
Throttle = proplists:get_value(throttle_cb, OwnOpts, false),
- Sock = init(T, Ref, Mod, Pid, SslOpts, Rest, Addrs),
+ Sock = init(T, Ref, Mod, Pid, SslOpts, Rest, Addrs, SPid),
MPid ! {stop, self()}, %% tell the monitor to die
M = if SslOpts -> ssl; true -> Mod end,
putr(?REF_KEY, Ref),
@@ -228,6 +229,11 @@ i({T, Ref, Mod, Pid, Opts, Addrs})
%% Put the reference in the process dictionary since we now use it
%% advertise the ssl socket after TLS upgrade.
+i({T, _Ref, _Mod, _Pid, _Opts, _Addrs} = Arg) %% from old code
+ when T == accept;
+ T == connect ->
+ i(erlang:append_element(Arg, _SPid = false));
+
%% A monitor process to kill the transport if the parent dies.
i(#monitor{parent = Pid, transport = TPid} = S) ->
proc_lib:init_ack({ok, self()}),
@@ -240,16 +246,18 @@ i(#monitor{parent = Pid, transport = TPid} = S) ->
%% death. However, a link can be unlinked and this is exactly what
%% gen_tcp seems to so. Links should be left to supervisors.
-i({listen, LRef, APid, {Mod, Opts, Addrs}}) ->
- [_] = diameter_config:subscribe(LRef, transport), %% assert existence
+i({listen = L, Ref, _APid, T}) -> %% from old code
+ i({L, Ref, T});
+
+i({listen, Ref, {Mod, Opts, Addrs}}) ->
+ [_] = diameter_config:subscribe(Ref, transport), %% assert existence
{[LA, LP], Rest} = proplists:split(Opts, [ip, port]),
LAddrOpt = get_addr(LA, Addrs),
LPort = get_port(LP),
{ok, LSock} = Mod:listen(LPort, gen_opts(LAddrOpt, Rest)),
LAddr = laddr(LAddrOpt, Mod, LSock),
- true = diameter_reg:add_new({?MODULE, listener, {LRef, {LAddr, LSock}}}),
+ true = diameter_reg:add_new({?MODULE, listener, {Ref, {LAddr, LSock}}}),
proc_lib:init_ack({ok, self(), {LAddr, LSock}}),
- monitor(process, APid),
#listener{socket = LSock}.
laddr([], Mod, Sock) ->
@@ -268,21 +276,22 @@ ssl_opts([{ssl_options, Opts}])
ssl_opts(T) ->
?ERROR({ssl_options, T}).
-%% init/7
+%% init/8
%% Establish a TLS connection before capabilities exchange ...
-init(Type, Ref, Mod, Pid, true, Opts, Addrs) ->
- init(Type, Ref, ssl, Pid, [{cb_info, ?TCP_CB(Mod)} | Opts], Addrs);
+init(Type, Ref, Mod, Pid, true, Opts, Addrs, SPid) ->
+ init(Type, Ref, ssl, Pid, [{cb_info, ?TCP_CB(Mod)} | Opts], Addrs, SPid);
%% ... or not.
-init(Type, Ref, Mod, Pid, _, Opts, Addrs) ->
- init(Type, Ref, Mod, Pid, Opts, Addrs).
+init(Type, Ref, Mod, Pid, _, Opts, Addrs, SPid) ->
+ init(Type, Ref, Mod, Pid, Opts, Addrs, SPid).
-%% init/6
+%% init/7
-init(accept = T, Ref, Mod, Pid, Opts, Addrs) ->
+init(accept = T, Ref, Mod, Pid, Opts, Addrs, SPid) ->
{[Matches], Rest} = proplists:split(Opts, [accept]),
- {LAddr, LSock} = listener(Ref, {Mod, Rest, Addrs}),
+ {ok, LPid, {LAddr, LSock}} = listener(Ref, {Mod, Rest, Addrs}),
+ ok = gen_server:call(LPid, {accept, SPid}, infinity),
proc_lib:init_ack({ok, self(), [LAddr]}),
Sock = ok(accept(Mod, LSock)),
ok = accept_peer(Mod, Sock, accept(Matches)),
@@ -290,7 +299,7 @@ init(accept = T, Ref, Mod, Pid, Opts, Addrs) ->
diameter_peer:up(Pid),
Sock;
-init(connect = T, Ref, Mod, Pid, Opts, Addrs) ->
+init(connect = T, Ref, Mod, Pid, Opts, Addrs, _SPid) ->
{[LA, RA, RP], Rest} = proplists:split(Opts, [ip, raddr, rport]),
LAddrOpt = get_addr(LA, Addrs),
RAddr = get_addr(RA),
@@ -344,24 +353,26 @@ accept(Opts) ->
%% Accepting processes can be started concurrently: ensure only one
%% listener is started.
-listener(LRef, T) ->
- diameter_sync:call({?MODULE, listener, LRef},
- {?MODULE, listener, [{LRef, T, self()}]},
+listener(Ref, T) ->
+ diameter_sync:call({?MODULE, listener, Ref},
+ {?MODULE, listener, [{Ref, T, self()}]},
infinity,
infinity).
-listener({LRef, T, TPid}) ->
- l(diameter_reg:match({?MODULE, listener, {LRef, '_'}}), LRef, T, TPid).
+%% listener/1
+
+listener({Ref, T, _TPid}) ->
+ l(diameter_reg:match({?MODULE, listener, {Ref, '_'}}), Ref, T).
+
+%% l/3
%% Existing listening process ...
-l([{{?MODULE, listener, {_, AS}}, LPid}], _, _, TPid) ->
- LPid ! {accept, TPid},
- AS;
+l([{{?MODULE, listener, {_, AS}}, LPid}], _, _) ->
+ {ok, LPid, AS};
%% ... or not.
-l([], LRef, T, TPid) ->
- {ok, _, AS} = diameter_tcp_sup:start_child({listen, LRef, TPid, T}),
- AS.
+l([], Ref, T) ->
+ diameter_tcp_sup:start_child({listen, Ref, T}).
%% get_addr/1
@@ -440,6 +451,14 @@ portnr(Sock) ->
%% # handle_call/3
%% ---------------------------------------------------------------------------
+handle_call({accept, SPid}, _From, #listener{service = P} = S) ->
+ {reply, ok, if not is_pid(P), is_pid(SPid) ->
+ monitor(process, SPid),
+ S#listener{service = SPid};
+ true ->
+ S
+ end};
+
handle_call(_, _, State) ->
{reply, nok, State}.
@@ -507,19 +526,20 @@ m({'DOWN', _, process, Pid, _}, #monitor{parent = Pid,
%%
%% Transition listener state.
-%% An accepting transport is attaching.
-l({accept, TPid}, #listener{count = N} = S) ->
- monitor(process, TPid),
- S#listener{count = N+1};
-
-%% Accepting process has died.
-l({'DOWN', _, process, _, _}, #listener{count = N} = S) ->
- S#listener{count = N-1};
+%% Service process has died.
+l({'DOWN', _, process, Pid, _} = T, #listener{service = Pid,
+ socket = Sock}) ->
+ gen_tcp:close(Sock),
+ x(T);
%% Transport has been removed.
l({transport, remove, _} = T, #listener{socket = Sock}) ->
gen_tcp:close(Sock),
- x(T).
+ x(T);
+
+%% Possibly death of an accepting process monitored in old code.
+l(_, S) ->
+ S.
%% t/2
%%
diff --git a/lib/diameter/test/diameter_traffic_SUITE.erl b/lib/diameter/test/diameter_traffic_SUITE.erl
index 6f3a4801ee..4c82d4dee2 100644
--- a/lib/diameter/test/diameter_traffic_SUITE.erl
+++ b/lib/diameter/test/diameter_traffic_SUITE.erl
@@ -248,17 +248,14 @@ all() ->
groups() ->
Ts = tc(),
Sctp = ?util:have_sctp(),
- [{?util:name([R,D,A,C]), [parallel], Ts} || R <- ?ENCODINGS,
- D <- ?RFCS,
- A <- ?ENCODINGS,
- C <- ?CONTAINERS]
+ [{B, [P], Ts} || {B,P} <- [{true, shuffle}, {false, parallel}]]
++
[{?util:name([T,R,D,A,C,SD,CD]),
[],
[start_services,
add_transports,
result_codes,
- {group, ?util:name([R,D,A,C])},
+ {group, SD orelse CD},
remove_transports,
stop_services]}
|| T <- ?TRANSPORTS,
diff --git a/lib/diameter/test/diameter_util.erl b/lib/diameter/test/diameter_util.erl
index 37fcbbc267..cca28dd23c 100644
--- a/lib/diameter/test/diameter_util.erl
+++ b/lib/diameter/test/diameter_util.erl
@@ -195,13 +195,21 @@ unique_string() ->
%% have_sctp/0
have_sctp() ->
- case gen_sctp:open() of
- {ok, Sock} ->
- gen_sctp:close(Sock),
- true;
- {error, E} when E == eprotonosupport;
- E == esocktnosupport -> %% fail on any other reason
- false
+ case erlang:system_info(system_architecture) of
+ %% We do not support the sctp version present in solaris
+ %% version "sparc-sun-solaris2.10", that behaves differently
+ %% from later versions and linux
+ "sparc-sun-solaris2.10" ->
+ false;
+ _->
+ case gen_sctp:open() of
+ {ok, Sock} ->
+ gen_sctp:close(Sock),
+ true;
+ {error, E} when E == eprotonosupport;
+ E == esocktnosupport -> %% fail on any other reason
+ false
+ end
end.
%% ---------------------------------------------------------------------------
diff --git a/lib/diameter/vsn.mk b/lib/diameter/vsn.mk
index cb750c69a3..23219950bb 100644
--- a/lib/diameter/vsn.mk
+++ b/lib/diameter/vsn.mk
@@ -17,5 +17,5 @@
# %CopyrightEnd%
APPLICATION = diameter
-DIAMETER_VSN = 1.12
+DIAMETER_VSN = 1.12.1
APP_VSN = $(APPLICATION)-$(DIAMETER_VSN)$(PRE_VSN)
diff --git a/lib/edoc/doc/overview.edoc b/lib/edoc/doc/overview.edoc
index d2bba9d744..3a2118928a 100644
--- a/lib/edoc/doc/overview.edoc
+++ b/lib/edoc/doc/overview.edoc
@@ -377,6 +377,12 @@ The following tags can be used before a function definition:
Useful for debug/test functions, etc. The content can be
used as a comment; it is ignored by EDoc.</dd>
+ <dt><a name="ftag-param">`@param'</a></dt>
+ <dd>Provide more information on a single parameter of the
+ enclosing function. The content consists of a parameter name,
+ followed by one or more whitespace characters, and XHTML text.
+ </dd>
+
<dt><a name="ftag-private">`@private'</a></dt>
<dd>Marks the function as private (i.e., not part of the public
interface), so that it will not appear in the normal
@@ -386,6 +392,10 @@ The following tags can be used before a function definition:
always "private".) The content can be used as a comment; it is
ignored by EDoc.</dd>
+ <dt><a name="ftag-returns">`@returns'</a></dt>
+ <dd>Specify additional information about the value returned by
+ the function. Content consists of XHTML text.</dd>
+
<dt><a name="ftag-see">`@see'</a></dt>
<dd>Make a reference to a module, function, datatype, or
application. (See {@section References}.)
diff --git a/lib/edoc/doc/src/notes.xml b/lib/edoc/doc/src/notes.xml
index ae8147c564..e6ad2c683f 100644
--- a/lib/edoc/doc/src/notes.xml
+++ b/lib/edoc/doc/src/notes.xml
@@ -32,6 +32,21 @@
<p>This document describes the changes made to the EDoc
application.</p>
+<section><title>Edoc 0.8</title>
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>Improve types and specs in OTP documentation generated
+ from Erlang source files. </p>
+ <p>
+ Own Id: OTP-13720 Aux Id: ERL-120 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Edoc 0.7.19</title>
<section><title>Improvements and New Features</title>
diff --git a/lib/edoc/vsn.mk b/lib/edoc/vsn.mk
index f38800b3e0..d3cc732e9c 100644
--- a/lib/edoc/vsn.mk
+++ b/lib/edoc/vsn.mk
@@ -1 +1 @@
-EDOC_VSN = 0.7.19
+EDOC_VSN = 0.8
diff --git a/lib/erl_docgen/doc/src/notes.xml b/lib/erl_docgen/doc/src/notes.xml
index 21dc617d55..cf24161d43 100644
--- a/lib/erl_docgen/doc/src/notes.xml
+++ b/lib/erl_docgen/doc/src/notes.xml
@@ -31,7 +31,22 @@
</header>
<p>This document describes the changes made to the <em>erl_docgen</em> application.</p>
- <section><title>Erl_Docgen 0.5</title>
+ <section><title>Erl_Docgen 0.6</title>
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>Improve types and specs in OTP documentation generated
+ from Erlang source files. </p>
+ <p>
+ Own Id: OTP-13720 Aux Id: ERL-120 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Erl_Docgen 0.5</title>
<section><title>Fixed Bugs and Malfunctions</title>
<list>
diff --git a/lib/erl_docgen/vsn.mk b/lib/erl_docgen/vsn.mk
index 98d1c3f7be..6489d26327 100644
--- a/lib/erl_docgen/vsn.mk
+++ b/lib/erl_docgen/vsn.mk
@@ -1 +1 @@
-ERL_DOCGEN_VSN = 0.5
+ERL_DOCGEN_VSN = 0.6
diff --git a/lib/erl_interface/doc/src/notes.xml b/lib/erl_interface/doc/src/notes.xml
index a69c5aac11..4ef5454f44 100644
--- a/lib/erl_interface/doc/src/notes.xml
+++ b/lib/erl_interface/doc/src/notes.xml
@@ -31,6 +31,23 @@
</header>
<p>This document describes the changes made to the Erl_interface application.</p>
+<section><title>Erl_Interface 3.9.1</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Look for .erlang.cookie in windows system directory if
+ HOMEDRIVE and HOMEPATH is not set. The same behaviour as
+ the VM.</p>
+ <p>
+ Own Id: OTP-13849</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Erl_Interface 3.9</title>
<section><title>Fixed Bugs and Malfunctions</title>
diff --git a/lib/erl_interface/src/connect/ei_connect.c b/lib/erl_interface/src/connect/ei_connect.c
index 6dc51adee1..624100ad49 100644
--- a/lib/erl_interface/src/connect/ei_connect.c
+++ b/lib/erl_interface/src/connect/ei_connect.c
@@ -1708,28 +1708,36 @@ error:
static int get_home(char *buf, int size)
{
- char* homedrive;
- char* homepath;
-
#ifdef __WIN32__
- homedrive = getenv("HOMEDRIVE");
- homepath = getenv("HOMEPATH");
-#else
- homedrive = "";
- homepath = getenv("HOME");
-#endif
+ char* homedrive = getenv("HOMEDRIVE");
+ char* homepath = getenv("HOMEPATH");
- if (!homedrive || !homepath) {
- buf[0] = '.';
- buf[1] = '\0';
- return 1;
- } else if (strlen(homedrive)+strlen(homepath) < size-1) {
+ if (homedrive && homepath) {
+ if (strlen(homedrive)+strlen(homepath) >= size)
+ return 0;
strcpy(buf, homedrive);
strcat(buf, homepath);
return 1;
}
-
- return 0;
+ else {
+ int len = GetWindowsDirectory(buf, size);
+ if (len) {
+ return (len < size);
+ }
+ }
+#else
+ char* homepath = getenv("HOME");
+ if (homepath) {
+ if (strlen(homepath) >= size)
+ return 0;
+ strcpy(buf, homepath);
+ return 1;
+ }
+#endif
+
+ buf[0] = '.';
+ buf[1] = '\0';
+ return 1;
}
diff --git a/lib/erl_interface/src/misc/ei_portio.h b/lib/erl_interface/src/misc/ei_portio.h
index fbb61b0ccf..bded811a35 100644
--- a/lib/erl_interface/src/misc/ei_portio.h
+++ b/lib/erl_interface/src/misc/ei_portio.h
@@ -21,7 +21,7 @@
*/
#ifndef _EI_PORTIO_H
#define _EI_PORTIO_H
-#if !defined(__WIN32__) || !defined(VXWORKS)
+#if !defined(__WIN32__) && !defined(VXWORKS)
#ifdef HAVE_WRITEV
/* Declaration of struct iovec *iov should be visible in this scope. */
#include <sys/uio.h>
diff --git a/lib/erl_interface/vsn.mk b/lib/erl_interface/vsn.mk
index 33705d1e8b..82be43b7df 100644
--- a/lib/erl_interface/vsn.mk
+++ b/lib/erl_interface/vsn.mk
@@ -1,2 +1,2 @@
-EI_VSN = 3.9
+EI_VSN = 3.9.1
ERL_INTERFACE_VSN = $(EI_VSN)
diff --git a/lib/eunit/doc/src/notes.xml b/lib/eunit/doc/src/notes.xml
index 88602e8222..6ae3c04bc8 100644
--- a/lib/eunit/doc/src/notes.xml
+++ b/lib/eunit/doc/src/notes.xml
@@ -33,6 +33,23 @@
</header>
<p>This document describes the changes made to the EUnit application.</p>
+<section><title>Eunit 2.3.1</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ When asserts were moved out to a separate header file,
+ the automatic enabling of asserts when testing is enabled
+ stopped working.</p>
+ <p>
+ Own Id: OTP-13892</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Eunit 2.3</title>
<section><title>Improvements and New Features</title>
diff --git a/lib/eunit/include/eunit.hrl b/lib/eunit/include/eunit.hrl
index 7fd6c206a4..8a4cad1e7e 100644
--- a/lib/eunit/include/eunit.hrl
+++ b/lib/eunit/include/eunit.hrl
@@ -51,7 +51,9 @@
%% note that the main switch used within this file is NOTEST; however,
%% both TEST and EUNIT may be used to check whether testing is enabled
-ifndef(NOTEST).
--undef(NOASSERT). % testing requires that assertions are enabled
+-ifndef(ASSERT).
+-define(ASSERT, true). % testing requires that assertions are enabled
+-endif.
-ifndef(TEST).
-define(TEST, true).
-endif.
diff --git a/lib/eunit/vsn.mk b/lib/eunit/vsn.mk
index b551ee6eb6..83d826f8b6 100644
--- a/lib/eunit/vsn.mk
+++ b/lib/eunit/vsn.mk
@@ -1 +1 @@
-EUNIT_VSN = 2.3
+EUNIT_VSN = 2.3.1
diff --git a/lib/gs/doc/src/Makefile b/lib/gs/doc/src/Makefile
index b270bc84fe..a24ec77e31 100644
--- a/lib/gs/doc/src/Makefile
+++ b/lib/gs/doc/src/Makefile
@@ -148,7 +148,7 @@ release_docs_spec: docs
$(INSTALL_DIR) "$(RELSYSDIR)/doc/pdf"
$(INSTALL_DATA) $(TOP_PDF_FILE) "$(RELSYSDIR)/doc/pdf"
$(INSTALL_DIR) "$(RELSYSDIR)/doc/html"
- (/bin/cp -rf $(HTMLDIR) "$(RELSYSDIR)/doc")
+ ($(CP) -rf $(HTMLDIR) "$(RELSYSDIR)/doc")
$(INSTALL_DATA) $(INFO_FILE) "$(RELSYSDIR)"
$(INSTALL_DIR) "$(RELEASE_PATH)/man/man3"
$(INSTALL_DATA) $(MAN3DIR)/* "$(RELEASE_PATH)/man/man3"
diff --git a/lib/gs/doc/src/notes.xml b/lib/gs/doc/src/notes.xml
index 20188c75e2..4f3f0645a5 100644
--- a/lib/gs/doc/src/notes.xml
+++ b/lib/gs/doc/src/notes.xml
@@ -31,7 +31,23 @@
</header>
<p>This document describes the changes made to the GS application.</p>
- <section><title>GS 1.6.1</title>
+ <section><title>GS 1.6.2</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Update build scripts to not make assumtions about where
+ env, cp and perl are located.</p>
+ <p>
+ Own Id: OTP-13800</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>GS 1.6.1</title>
<section><title>Improvements and New Features</title>
<list>
diff --git a/lib/gs/vsn.mk b/lib/gs/vsn.mk
index c762507bab..d2c5d15a96 100644
--- a/lib/gs/vsn.mk
+++ b/lib/gs/vsn.mk
@@ -1,2 +1,2 @@
-GS_VSN = 1.6.1
+GS_VSN = 1.6.2
diff --git a/lib/hipe/amd64/hipe_amd64_ra_sse2_postconditions.erl b/lib/hipe/amd64/hipe_amd64_ra_sse2_postconditions.erl
index 5451f1fe7d..b1f7bd7572 100644
--- a/lib/hipe/amd64/hipe_amd64_ra_sse2_postconditions.erl
+++ b/lib/hipe/amd64/hipe_amd64_ra_sse2_postconditions.erl
@@ -87,22 +87,29 @@ do_fp_unop(I, TempMap) ->
%%% Fix an fmove op.
do_fmove(I, TempMap) ->
#fmove{src=Src,dst=Dst} = I,
- case is_mem_opnd(Dst, TempMap) and is_mem_opnd(Src, TempMap) of
+ case
+ (is_mem_opnd(Src, TempMap) andalso is_mem_opnd(Dst, TempMap))
+ orelse (is_mem_opnd(Src, TempMap) andalso (not is_float_temp(Dst)))
+ orelse ((not is_float_temp(Src)) andalso is_mem_opnd(Dst, TempMap))
+ of
true ->
- Tmp = clone(Src),
+ Tmp = spill_temp(double),
{[#fmove{src=Src, dst=Tmp},I#fmove{src=Tmp,dst=Dst}],
true};
false ->
{[I], false}
end.
+is_float_temp(#x86_temp{type=Type}) -> Type =:= double;
+is_float_temp(#x86_mem{}) -> false.
+
%%% Check if an operand denotes a memory cell (mem or pseudo).
is_mem_opnd(Opnd, TempMap) ->
R =
case Opnd of
#x86_mem{} -> true;
- #x86_temp{} ->
+ #x86_temp{type=double} ->
Reg = hipe_x86:temp_reg(Opnd),
case hipe_x86:temp_is_allocatable(Opnd) of
true ->
@@ -176,6 +183,9 @@ clone(Dst) ->
#x86_mem{} -> hipe_x86:mem_type(Dst);
#x86_temp{} -> hipe_x86:temp_type(Dst)
end,
+ spill_temp(Type).
+
+spill_temp(Type) ->
hipe_x86:mk_new_temp(Type).
%%% Make a certain reg into a clone of Dst
diff --git a/lib/hipe/arm/hipe_arm_registers.erl b/lib/hipe/arm/hipe_arm_registers.erl
index 24cd929d41..dcf039676b 100644
--- a/lib/hipe/arm/hipe_arm_registers.erl
+++ b/lib/hipe/arm/hipe_arm_registers.erl
@@ -67,6 +67,8 @@
-define(R15, 15).
-define(LAST_PRECOLOURED, 15). % must handle both GPR and FPR ranges
+-define(LR, ?R14).
+
-define(ARG0, ?R1).
-define(ARG1, ?R2).
-define(ARG2, ?R3).
@@ -114,7 +116,7 @@ stack_pointer() -> ?STACK_POINTER.
proc_pointer() -> ?PROC_POINTER.
-lr() -> ?R14.
+lr() -> ?LR.
pc() -> ?R15.
@@ -198,7 +200,9 @@ call_clobbered() -> % does the RA strip the type or not?
].
tailcall_clobbered() -> % tailcall crapola needs one temp
- [{?TEMP1,tagged},{?TEMP1,untagged}].
+ [{?TEMP1,tagged},{?TEMP1,untagged}
+ ,{?LR,tagged},{?LR,untagged}
+ ].
live_at_return() ->
[%%{?LR,untagged},
diff --git a/lib/hipe/arm/hipe_rtl_to_arm.erl b/lib/hipe/arm/hipe_rtl_to_arm.erl
index ad5a559995..93342aba33 100644
--- a/lib/hipe/arm/hipe_rtl_to_arm.erl
+++ b/lib/hipe/arm/hipe_rtl_to_arm.erl
@@ -148,10 +148,11 @@ mk_shift_ir(S, Dst, Src1, ShiftOp, Src2) ->
mk_li(Tmp, Src1,
mk_shift_rr(S, Dst, Tmp, ShiftOp, Src2)).
-mk_shift_ri(S, Dst, Src1, ShiftOp, Src2) when is_integer(Src2) ->
- if Src2 >= 0, Src2 < 32 -> ok;
- true -> io:format("~w: excessive immediate shift ~w\n", [?MODULE,Src2])
- end,
+mk_shift_ri(S, Dst, Src1, ShiftOp, 0)
+ when ShiftOp =:= lsl; ShiftOp =:= lsr; ShiftOp =:= asr ->
+ [hipe_arm:mk_move(S, Dst, Src1)];
+mk_shift_ri(S, Dst, Src1, ShiftOp, Src2)
+ when is_integer(Src2), Src2 > 0, Src2 < 32 ->
Am1 = {Src1,ShiftOp,Src2},
[hipe_arm:mk_move(S, Dst, Am1)].
diff --git a/lib/hipe/cerl/erl_bif_types.erl b/lib/hipe/cerl/erl_bif_types.erl
index 9453ca6c6f..230fce2e68 100644
--- a/lib/hipe/cerl/erl_bif_types.erl
+++ b/lib/hipe/cerl/erl_bif_types.erl
@@ -124,7 +124,7 @@
t_map_entries/2,
t_map_put/3,
t_map_update/3,
- map_pairwise_merge/3
+ t_map_pairwise_merge/4
]).
-ifdef(DO_ERL_BIF_TYPES_TEST).
@@ -1689,10 +1689,10 @@ type(maps, merge, 2, Xs, Opaques) ->
BDefK = t_map_def_key(MapB, Opaques),
ADefV = t_map_def_val(MapA, Opaques),
BDefV = t_map_def_val(MapB, Opaques),
- t_map(map_pairwise_merge(
+ t_map(t_map_pairwise_merge(
fun(K, _, _, mandatory, V) -> {K, mandatory, V};
(K, MNess, VA, optional, VB) -> {K, MNess, t_sup(VA,VB)}
- end, MapA, MapB),
+ end, MapA, MapB, Opaques),
t_sup(ADefK, BDefK), t_sup(ADefV, BDefV))
end, Opaques);
type(maps, put, 3, Xs, Opaques) ->
diff --git a/lib/hipe/cerl/erl_types.erl b/lib/hipe/cerl/erl_types.erl
index 243bb6e25d..15f7b793a1 100644
--- a/lib/hipe/cerl/erl_types.erl
+++ b/lib/hipe/cerl/erl_types.erl
@@ -159,6 +159,7 @@
t_map_get/2, t_map_get/3,
t_map_is_key/2, t_map_is_key/3,
t_map_update/2, t_map_update/3,
+ t_map_pairwise_merge/4,
t_map_put/2, t_map_put/3,
t_matchstate/0,
t_matchstate/2,
@@ -219,8 +220,7 @@
is_erl_type/1,
atom_to_string/1,
var_table__new/0,
- cache__new/0,
- map_pairwise_merge/3
+ cache__new/0
]).
%%-define(DO_ERL_TYPES_TEST, true).
@@ -494,9 +494,9 @@ t_contains_opaque(?function(Domain, Range), Opaques) ->
t_contains_opaque(Domain, Opaques)
orelse t_contains_opaque(Range, Opaques);
t_contains_opaque(?identifier(_Types), _Opaques) -> false;
-t_contains_opaque(?integer(_Types), _Opaques) -> false;
t_contains_opaque(?int_range(_From, _To), _Opaques) -> false;
t_contains_opaque(?int_set(_Set), _Opaques) -> false;
+t_contains_opaque(?integer(_Types), _Opaques) -> false;
t_contains_opaque(?list(Type, Tail, _), Opaques) ->
t_contains_opaque(Type, Opaques) orelse t_contains_opaque(Tail, Opaques);
t_contains_opaque(?map(_, _, _) = Map, Opaques) ->
@@ -1768,13 +1768,26 @@ mapdict_insert(E1={K1,_,_}, [E2={K2,_,_}|T]) when K1 > K2 ->
[E2|mapdict_insert(E1, T)];
mapdict_insert(E={_,_,_}, T) -> [E|T].
+-type map_pairwise_merge_fun() :: fun((erl_type(),
+ t_map_mandatoriness(), erl_type(),
+ t_map_mandatoriness(), erl_type())
+ -> t_map_pair() | false).
+
+-spec t_map_pairwise_merge(map_pairwise_merge_fun(), erl_type(), erl_type(),
+ opaques()) -> t_map_dict().
+t_map_pairwise_merge(F, MapA, MapB, Opaques) ->
+ do_opaque(MapA, Opaques,
+ fun(UMapA) ->
+ do_opaque(MapB, Opaques,
+ fun(UMapB) ->
+ map_pairwise_merge(F, UMapA, UMapB)
+ end)
+ end).
+
%% Merges the pairs of two maps together. Missing pairs become (?opt, DefV) or
%% (?opt, ?none), depending on whether K \in DefK.
--spec map_pairwise_merge(fun((erl_type(),
- t_map_mandatoriness(), erl_type(),
- t_map_mandatoriness(), erl_type())
- -> t_map_pair() | false),
- erl_type(), erl_type()) -> t_map_dict().
+-spec map_pairwise_merge(map_pairwise_merge_fun(), erl_type(), erl_type())
+ -> t_map_dict().
map_pairwise_merge(F, ?map(APairs, ADefK, ADefV),
?map(BPairs, BDefK, BDefV)) ->
map_pairwise_merge(F, APairs, ADefK, ADefV, BPairs, BDefK, BDefV).
@@ -4749,7 +4762,7 @@ type_from_form1(Name, Args, ArgsLen, R, TypeName, TypeNames, S, D, L, C) ->
{Rep, L2, C2} = recur_limit(Fun, D, L1, TypeName, TypeNames),
Rep1 = choose_opaque_type(Rep, Type),
Rep2 = case cannot_have_opaque(Rep1, TypeName, TypeNames) of
- true -> Rep1;
+ true -> Rep;
false ->
ArgTypes2 = subst_all_vars_to_any_list(ArgTypes),
t_opaque(Module, Name, ArgTypes2, Rep1)
@@ -4821,7 +4834,7 @@ remote_from_form1(RemMod, Name, Args, ArgsLen, RemDict, RemType, TypeNames,
NewRep1 = choose_opaque_type(NewRep, Type),
NewRep2 =
case cannot_have_opaque(NewRep1, RemType, TypeNames) of
- true -> NewRep1;
+ true -> NewRep;
false ->
ArgTypes2 = subst_all_vars_to_any_list(ArgTypes),
t_opaque(Mod, Name, ArgTypes2, NewRep1)
diff --git a/lib/hipe/doc/src/notes.xml b/lib/hipe/doc/src/notes.xml
index e2a1524be6..fc529fba61 100644
--- a/lib/hipe/doc/src/notes.xml
+++ b/lib/hipe/doc/src/notes.xml
@@ -31,6 +31,46 @@
</header>
<p>This document describes the changes made to HiPE.</p>
+<section><title>Hipe 3.15.2</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Fixed various hipe compiler backend bugs affecting mostly
+ ARM and SPARC.</p>
+ <p>
+ Own Id: OTP-13846 Aux Id: PR-1146 </p>
+ </item>
+ <item>
+ <p>
+ Fixed some Dialyzer warnings and code cleanup for the
+ Sparc compiler backend.</p>
+ <p>
+ Own Id: OTP-13861 Aux Id: PR-1148 </p>
+ </item>
+ <item>
+ <p> Fix erl_bif_types opaque bug. </p>
+ <p>
+ Own Id: OTP-13878 Aux Id: PR-1161, ERL-249 </p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Fix erl_types opaque match order</p>
+ <p>
+ Own Id: OTP-13876</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Hipe 3.15.1</title>
<section><title>Fixed Bugs and Malfunctions</title>
diff --git a/lib/hipe/ppc/hipe_ppc.erl b/lib/hipe/ppc/hipe_ppc.erl
index 0fa96162f6..380e791bc1 100644
--- a/lib/hipe/ppc/hipe_ppc.erl
+++ b/lib/hipe/ppc/hipe_ppc.erl
@@ -167,8 +167,10 @@ temp_is_precoloured(#ppc_temp{reg=Reg,type=Type}) ->
_ -> hipe_ppc_registers:is_precoloured_gpr(Reg)
end.
-mk_simm16(Value) -> #ppc_simm16{value=Value}.
-mk_uimm16(Value) -> #ppc_uimm16{value=Value}.
+mk_simm16(Value) when Value >= -(1 bsl 15), Value < (1 bsl 15) ->
+ #ppc_simm16{value=Value}.
+mk_uimm16(Value) when Value >= 0, Value < (1 bsl 16) ->
+ #ppc_uimm16{value=Value}.
mk_mfa(M, F, A) -> #ppc_mfa{m=M, f=F, a=A}.
@@ -240,7 +242,11 @@ mk_li(Dst, Value, Tail) -> % Dst can be R0
Value =< 16#7FFFFFFF ->
mk_li32(Dst, R0, Value, Tail);
true ->
- Highest = (Value bsr 48), % Value@highest
+ Highest = case (Value bsr 48) of % Value@highest
+ TopBitSet when TopBitSet >= (1 bsl 15) ->
+ TopBitSet - (1 bsl 16); % encoder needs it to be negative
+ FitsSimm16 -> FitsSimm16
+ end,
Higher = (Value bsr 32) band 16#FFFF, % Value@higher
High = (Value bsr 16) band 16#FFFF, % Value@h
Low = Value band 16#FFFF, % Value@l
diff --git a/lib/hipe/ppc/hipe_ppc_assemble.erl b/lib/hipe/ppc/hipe_ppc_assemble.erl
index ff9da01b11..d89ff6235c 100644
--- a/lib/hipe/ppc/hipe_ppc_assemble.erl
+++ b/lib/hipe/ppc/hipe_ppc_assemble.erl
@@ -175,7 +175,8 @@ do_slwi_opnds(Dst, Src1, {uimm,N}) when is_integer(N), 0 =< N, N < 32 ->
{Dst, Src1, {sh,N}, {mb,0}, {me,31-N}}.
do_srwi_opnds(Dst, Src1, {uimm,N}) when is_integer(N), 0 =< N, N < 32 ->
- {Dst, Src1, {sh,32-N}, {mb,N}, {me,31}}.
+ %% SH should be 0 (not 32) when N is 0
+ {Dst, Src1, {sh,(32-N) band 31}, {mb,N}, {me,31}}.
do_srawi_src2({uimm,N}) when is_integer(N), 0 =< N, N < 32 -> {sh,N}.
@@ -184,7 +185,8 @@ do_sldi_opnds(Dst, Src1, {uimm,N}) when is_integer(N), 0 =< N, N < 64 ->
{Dst, Src1, {sh6,N}, {me6,63-N}}.
do_srdi_opnds(Dst, Src1, {uimm,N}) when is_integer(N), 0 =< N, N < 64 ->
- {Dst, Src1, {sh6,64-N}, {mb6,N}}.
+ %% SH should be 0 (not 64) when N is 0
+ {Dst, Src1, {sh6,(64-N) band 63}, {mb6,N}}.
do_sradi_src2({uimm,N}) when is_integer(N), 0 =< N, N < 64 -> {sh6,N}.
@@ -246,6 +248,7 @@ do_load(I) ->
case LdOp of
'ld' -> do_disp_ds(Disp);
'ldu' -> do_disp_ds(Disp);
+ 'lwa' -> do_disp_ds(Disp);
_ -> do_disp(Disp)
end,
NewBase = do_reg(Base),
diff --git a/lib/hipe/sparc/hipe_rtl_to_sparc.erl b/lib/hipe/sparc/hipe_rtl_to_sparc.erl
index eef5ba8d96..f9c043eafe 100644
--- a/lib/hipe/sparc/hipe_rtl_to_sparc.erl
+++ b/lib/hipe/sparc/hipe_rtl_to_sparc.erl
@@ -750,13 +750,25 @@ xaluop_commutes(XAluOp) ->
xaluop_is_shift(XAluOp) ->
case XAluOp of
+ 'add' -> false;
+ 'addcc' -> false;
+ 'and' -> false;
+ 'andcc' -> false;
+ 'cmpcc' -> false;
+ 'ldsb' -> false;
+ 'ldub' -> false;
+ 'lduw' -> false;
+ 'or' -> false;
'sll' -> true;
- 'srl' -> true;
+ %% 'sllx' -> true;
+ 'smul' -> false;
'sra' -> true;
- 'sllx' -> true;
- 'srlx' -> true;
- 'srax' -> true;
- _ -> false
+ %% 'srax' -> true;
+ 'srl' -> true;
+ %% 'srlx' -> true;
+ 'sub' -> false;
+ 'subcc' -> false;
+ 'xor' -> false
end.
%%% Convert an extended SPARC AluOp back to a plain AluOp.
@@ -764,9 +776,23 @@ xaluop_is_shift(XAluOp) ->
xaluop_normalise(XAluOp) ->
case XAluOp of
- 'cmp' -> 'sub';
+ 'add' -> 'add';
+ 'addcc' -> 'addcc';
+ 'and' -> 'and';
+ 'andcc' -> 'andcc';
+ %% 'cmp' -> 'sub';
'cmpcc' -> 'subcc';
- _ -> XAluOp
+ 'ldsb' -> 'ldsb';
+ 'ldub' -> 'ldub';
+ 'lduw' -> 'lduw';
+ 'or' -> 'or';
+ 'sll' -> 'sll';
+ 'smul' -> 'smul';
+ 'sra' -> 'sra';
+ 'srl' -> 'srl';
+ 'sub' -> 'sub';
+ 'subcc' -> 'subcc';
+ 'xor' -> 'xor'
end.
%%% Convert an RTL condition code.
diff --git a/lib/hipe/sparc/hipe_sparc_registers.erl b/lib/hipe/sparc/hipe_sparc_registers.erl
index 884215702b..6681a10070 100644
--- a/lib/hipe/sparc/hipe_sparc_registers.erl
+++ b/lib/hipe/sparc/hipe_sparc_registers.erl
@@ -86,6 +86,8 @@
-define(I7, 31).
-define(LAST_PRECOLOURED,31). % must handle both GRP and FPR ranges
+-define(RA, ?O7).
+
-define(ARG0, ?O1).
-define(ARG1, ?O2).
-define(ARG2, ?O3).
@@ -174,7 +176,7 @@ stack_pointer() -> ?STACK_POINTER.
proc_pointer() -> ?PROC_POINTER.
-return_address() -> ?O7.
+return_address() -> ?RA.
g0() -> ?G0.
@@ -283,7 +285,9 @@ call_clobbered() -> % does the RA strip the type or not?
].
tailcall_clobbered() -> % tailcall crapola needs one temp
- [{?TEMP1,tagged},{?TEMP1,untagged}].
+ [{?TEMP1,tagged},{?TEMP1,untagged}
+ ,{?RA,tagged},{?RA,untagged}
+ ].
live_at_return() ->
[{?HEAP_POINTER,untagged},
diff --git a/lib/hipe/test/hipe_testsuite_driver.erl b/lib/hipe/test/hipe_testsuite_driver.erl
index 03ec7adfd0..a3048d907e 100644
--- a/lib/hipe/test/hipe_testsuite_driver.erl
+++ b/lib/hipe/test/hipe_testsuite_driver.erl
@@ -99,7 +99,7 @@ write_suite(Suite) ->
write_header(#suite{suitename = SuiteName, outputfile = OutputFile,
testcases = TestCases}) ->
Exports = format_export(TestCases),
- TimeLimit = 3, %% with 1 or 2 it fails on some slow machines...
+ TimeLimit = 5, %% with 1 or 2 it fails on some slow machines...
io:format(OutputFile,
"%% ATTENTION!\n"
"%% This is an automatically generated file. Do not edit.\n\n"
diff --git a/lib/hipe/vsn.mk b/lib/hipe/vsn.mk
index e61c1a042c..f00ff0cf2e 100644
--- a/lib/hipe/vsn.mk
+++ b/lib/hipe/vsn.mk
@@ -1 +1 @@
-HIPE_VSN = 3.15.1
+HIPE_VSN = 3.15.2
diff --git a/lib/hipe/x86/hipe_x86_postpass.erl b/lib/hipe/x86/hipe_x86_postpass.erl
index 939baeccec..4515822a34 100644
--- a/lib/hipe/x86/hipe_x86_postpass.erl
+++ b/lib/hipe/x86/hipe_x86_postpass.erl
@@ -95,7 +95,8 @@ peep([I=#move{src=#x86_temp{reg=Src}, dst=#x86_temp{reg=Dst}},
%% ElimBinALMDouble
%% ----------------
-peep([Move=#move{src=Src, dst=Dst}, Alu=#alu{src=Src, dst=Dst}|Insns], Res, Lst) ->
+peep([Move=#move{src=Src, dst=Dst}, Alu=#alu{src=Src, dst=Dst}|Insns], Res, Lst)
+ when not is_record(Dst, x86_mem) ->
peep([Alu#alu{src=Dst}|Insns], [Move|Res], [elimBinALMDouble|Lst]);
diff --git a/lib/ic/doc/src/Makefile b/lib/ic/doc/src/Makefile
index c9691df7af..19f12ac6b9 100644
--- a/lib/ic/doc/src/Makefile
+++ b/lib/ic/doc/src/Makefile
@@ -225,7 +225,7 @@ release_docs_spec: docs
$(INSTALL_DATA) $(TOP_PDF_FILE) "$(RELSYSDIR)/doc/pdf"
$(INSTALL_DATA) $(INFO_FILE) "$(RELSYSDIR)"
$(INSTALL_DIR) "$(RELSYSDIR)/doc/html"
- (/bin/cp -rf $(HTMLDIR) "$(RELSYSDIR)/doc")
+ ($(CP) -rf $(HTMLDIR) "$(RELSYSDIR)/doc")
$(INSTALL_DIR) "$(RELEASE_PATH)/man/man3"
$(INSTALL_DATA) $(MAN3_FILES) "$(RELEASE_PATH)/man/man3"
diff --git a/lib/ic/doc/src/notes.xml b/lib/ic/doc/src/notes.xml
index 08b02bc4a4..ea8bf758cf 100644
--- a/lib/ic/doc/src/notes.xml
+++ b/lib/ic/doc/src/notes.xml
@@ -31,7 +31,29 @@
<file>notes.xml</file>
</header>
- <section><title>IC 4.4.1</title>
+ <section><title>IC 4.4.2</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p> Correct bugs when path to mib or idl spec files
+ contains UTF-8 characters. </p>
+ <p>
+ Own Id: OTP-13718 Aux Id: ERL-179 </p>
+ </item>
+ <item>
+ <p>
+ Update build scripts to not make assumtions about where
+ env, cp and perl are located.</p>
+ <p>
+ Own Id: OTP-13800</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>IC 4.4.1</title>
<section><title>Improvements and New Features</title>
<list>
diff --git a/lib/ic/src/ic_codegen.erl b/lib/ic/src/ic_codegen.erl
index adad021da1..a3f141f606 100644
--- a/lib/ic/src/ic_codegen.erl
+++ b/lib/ic/src/ic_codegen.erl
@@ -245,8 +245,8 @@ emit_stub_head(G, F1, Name, java) ->
stub_header(G, Name) ->
["Implementation stub file",
"",
- io_lib:format("Target: ~s", [Name]),
- io_lib:format("Source: ~s", [ic_genobj:idlfile(G)]),
+ io_lib:format("Target: ~ts", [Name]),
+ io_lib:format("Source: ~ts", [ic_genobj:idlfile(G)]),
io_lib:format("IC vsn: ~s", [?COMPILERVSN]),
"",
"This file is automatically generated. DO NOT EDIT IT."].
@@ -298,8 +298,8 @@ emit_hrl_head(G, Fd, Name, c_server) ->
hrl_header(G, Name) ->
["",
- io_lib:format("Target: ~s", [Name]),
- io_lib:format("Source: ~s", [ic_genobj:idlfile(G)]),
+ io_lib:format("Target: ~ts", [Name]),
+ io_lib:format("Source: ~ts", [ic_genobj:idlfile(G)]),
io_lib:format("IC vsn: ~s", [?COMPILERVSN]),
"",
"This file is automatically generated. DO NOT EDIT IT."].
diff --git a/lib/ic/vsn.mk b/lib/ic/vsn.mk
index 7d00ae0170..f0e5e7c266 100644
--- a/lib/ic/vsn.mk
+++ b/lib/ic/vsn.mk
@@ -1 +1 @@
-IC_VSN = 4.4.1
+IC_VSN = 4.4.2
diff --git a/lib/inets/doc/src/http_server.xml b/lib/inets/doc/src/http_server.xml
index aeda961714..65b3dcde95 100644
--- a/lib/inets/doc/src/http_server.xml
+++ b/lib/inets/doc/src/http_server.xml
@@ -4,7 +4,7 @@
<chapter>
<header>
<copyright>
- <year>2004</year><year>2015</year>
+ <year>2004</year><year>2016</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -40,8 +40,8 @@
<item>Secure Sockets Layer (SSL)</item>
<item>Erlang Scripting Interface (ESI)</item>
<item>Common Gateway Interface (CGI)</item>
- <item>User Authentication (using <c>Mnesia</c>,
- <c>Dets</c> or plain text database)</item>
+ <item>User Authentication (using Mnesia,
+ Dets or plain text database)</item>
<item>Common Logfile Format (with or without disk_log(3) support)</item>
<item>URL Aliasing</item>
<item>Action Mappings</item>
@@ -563,7 +563,7 @@ http://your.server.org/eval?httpd_example:print(atom_to_list(apply(erlang,halt,[
<title>mod_auth - User Authentication</title>
<p>The <seealso marker="mod_auth">mod_auth(3)</seealso>
module provides for basic user authentication using
- textual files, <c>Dets</c> databases as well as <c>Mnesia</c> databases.</p>
+ textual files, Dets databases as well as Mnesia databases.</p>
<p>Uses the following Erlang Web Server API interaction data:
</p>
<list type="bulleted">
@@ -580,15 +580,15 @@ http://your.server.org/eval?httpd_example:print(atom_to_list(apply(erlang,halt,[
<section>
<title>Mnesia As Authentication Database</title>
- <p>If <c>Mnesia</c> is used as storage method, <c>Mnesia</c> must be
- started before the HTTP server. The first time <c>Mnesia</c> is
+ <p>If Mnesia is used as storage method, Mnesia must be
+ started before the HTTP server. The first time Mnesia is
started, the schema and the tables must be created before
- <c>Mnesia</c> is started. A simple example of a module with two
- functions that creates and start <c>Mnesia</c> is provided
+ Mnesia is started. A simple example of a module with two
+ functions that creates and start Mnesia is provided
here. Function <c>first_start/0</c> is to be used the first
time. It creates the schema and the tables.
<c>start/0</c> is to be used in consecutive startups.
- <c>start/0</c> starts <c>Mnesia</c> and waits for the tables to
+ <c>start/0</c> starts Mnesia and waits for the tables to
be initiated. This function must only be used when the
schema and the tables are already created.</p>
@@ -616,25 +616,25 @@ start() ->
mnesia:start(),
mnesia:wait_for_tables([httpd_user, httpd_group], 60000). </code>
- <p>To create the <c>Mnesia</c> tables, we use two records defined in
+ <p>To create the Mnesia tables, we use two records defined in
<c>mod_auth.hrl</c>, so that file must be included. <c>first_start/0</c>
creates a schema that specifies on which nodes the database is to reside.
- Then it starts <c>Mnesia</c> and creates the tables. The first argument
+ Then it starts Mnesia and creates the tables. The first argument
is the name of the tables, the second argument is a list of options of
how to create the table, see
- <seealso marker="mnesia:mnesia"><c>mnesia</c></seealso>, documentation for
+ <seealso marker="mnesia:mnesia"><c>mnesia(3)</c></seealso>, documentation for
more information. As the implementation of the <c>mod_auth_mnesia</c>
saves one row for each user, the type must be <c>bag</c>.
When the schema and the tables are created, function
<seealso marker="mnesia:mnesia#start-0">mnesia:start/0</seealso>
- is used to start <c>Mnesia</c> and
- waits for the tables to be loaded. <c>Mnesia</c> uses the
+ is used to start Mnesia and
+ waits for the tables to be loaded. Mnesia uses the
directory specified as <c>mnesia_dir</c> at startup if specified,
- otherwise <c>Mnesia</c> uses the current directory. For security
- reasons, ensure that the <c>Mnesia</c> tables are stored outside
+ otherwise Mnesia uses the current directory. For security
+ reasons, ensure that the Mnesia tables are stored outside
the document tree of the HTTP server. If they are placed in the
directory which it protects, clients can download the tables.
- Only the <c>Dets</c> and <c>Mnesia</c> storage
+ Only the Dets and Mnesia storage
methods allow writing of dynamic user data to disk. <c>plain</c> is
a read only method.</p>
</section>
@@ -669,7 +669,7 @@ start() ->
<section>
<title>mod_disk_log - Logging Using Disk_Log.</title>
<p>Standard logging using the "Common Logfile Format" and
- <seealso marker="kernel:disk_log">kernel:disk_log(3)</seealso>.</p>
+ <seealso marker="kernel:disk_log">disk_log(3)</seealso>.</p>
<p>Uses the following Erlang Web Server API interaction data:
</p>
<list type="bulleted">
diff --git a/lib/inets/doc/src/httpc.xml b/lib/inets/doc/src/httpc.xml
index ca9b268a03..13471aab2c 100644
--- a/lib/inets/doc/src/httpc.xml
+++ b/lib/inets/doc/src/httpc.xml
@@ -4,7 +4,7 @@
<erlref>
<header>
<copyright>
- <year>2004</year><year>2015</year>
+ <year>2004</year><year>2016</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -72,7 +72,7 @@
<p><c>profile() = atom()</c></p>
<p><c>path() = string()</c> representing a file path or directory path</p>
<p><c>ip_address()</c> = See the
- <seealso marker="kernel:inet">inet(3)</seealso> manual page in <c>Kernel</c>.</p>
+ <seealso marker="kernel:inet">inet(3)</seealso> manual page in Kernel.</p>
<p><c>socket_opt()</c> = See the options used by
<seealso marker="kernel:gen_tcp">gen_tcp(3)</seealso> <c>gen_tcp(3)</c> and
<seealso marker="ssl:ssl">ssl(3)</seealso> connect(s)</p>
diff --git a/lib/inets/doc/src/httpd.xml b/lib/inets/doc/src/httpd.xml
index 62b92b8356..d74635fc01 100644
--- a/lib/inets/doc/src/httpd.xml
+++ b/lib/inets/doc/src/httpd.xml
@@ -4,7 +4,7 @@
<erlref>
<header>
<copyright>
- <year>1997</year><year>2015</year>
+ <year>1997</year><year>2016</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -455,7 +455,7 @@ text/plain asc txt</pre>
directory. Several files can be given, in which case the server
returns the first it finds, for example:</p>
- <code>{directory_index, ["index.hml", "welcome.html"]}</code>
+ <code>{directory_index, ["index.html", "welcome.html"]}</code>
<p>Access to http://your.server.org/docs/ would return
http://your.server.org/docs/index.html or
@@ -711,7 +711,7 @@ text/plain asc txt</pre>
<item>
<p>Sets the type of authentication database that is used for the
directory. The key difference between the different methods is
- that dynamic data can be saved when <c>Mnesia</c> and <c>Dets</c>
+ that dynamic data can be saved when Mnesia and Dets
are used.
This property is called <c>AuthDbType</c> in the Apache-like
configuration files.</p>
@@ -731,10 +731,10 @@ text/plain asc txt</pre>
<code> ragnar:s7Xxv7
edward:wwjau8 </code>
- <p>If the <c>Dets</c> storage method is used, the user database is
- maintained by <c>Dets</c> and must not be edited by hand. Use the
+ <p>If the Dets storage method is used, the user database is
+ maintained by Dets and must not be edited by hand. Use the
API functions in module <c>mod_auth</c> to create/edit the user
- database. This directive is ignored if the <c>Mnesia</c>
+ database. This directive is ignored if the Mnesia
storage method is used. For security reasons, ensure that
<c>auth_user_file</c> is stored outside the document tree of the web
server. If it is placed in the directory that it protects,
@@ -753,10 +753,10 @@ text/plain asc txt</pre>
<code>group1: bob joe ante</code>
- <p>If the <c>Dets</c> storage method is used, the group database is
- maintained by <c>Dets</c> and must not be edited by hand. Use the
+ <p>If the Dets storage method is used, the group database is
+ maintained by Dets and must not be edited by hand. Use the
API for module <c>mod_auth</c> to create/edit the group database.
- This directive is ignored if the <c>Mnesia</c> storage method is used.
+ This directive is ignored if the Mnesia storage method is used.
For security reasons, ensure that the <c>auth_group_file</c> is
stored outside the document tree of the web server. If it is
placed in the directory that it protects, clients
diff --git a/lib/inets/doc/src/mod_auth.xml b/lib/inets/doc/src/mod_auth.xml
index 4b7088b2c5..c4f844622b 100644
--- a/lib/inets/doc/src/mod_auth.xml
+++ b/lib/inets/doc/src/mod_auth.xml
@@ -33,7 +33,7 @@
<modulesummary>User authentication using text files, Dets, or Mnesia database.</modulesummary>
<description>
<p>This module provides for basic user authentication using
- textual files, <c>Dets</c> databases, or <c>Mnesia</c> databases.</p>
+ textual files, Dets databases, or Mnesia databases.</p>
</description>
<funcs>
diff --git a/lib/inets/doc/src/mod_esi.xml b/lib/inets/doc/src/mod_esi.xml
index 8279fdc824..006fca1bdf 100644
--- a/lib/inets/doc/src/mod_esi.xml
+++ b/lib/inets/doc/src/mod_esi.xml
@@ -61,7 +61,7 @@
<tag><c>{server_port, integer()}</c></tag>
<item><p>Servers port number.</p></item>
- <tag><c>{request_method, "GET | "PUT" | "DELETE | "POST" | "PATCH"}</c></tag>
+ <tag><c>{request_method, "GET | "PUT" | "DELETE" | "POST" | "PATCH"}</c></tag>
<item><p>HTTP request method.</p></item>
<tag><c>{remote_adress, inet:ip_address()} </c></tag>
diff --git a/lib/inets/doc/src/notes.xml b/lib/inets/doc/src/notes.xml
index 2f071f049f..0c7604ef65 100644
--- a/lib/inets/doc/src/notes.xml
+++ b/lib/inets/doc/src/notes.xml
@@ -33,7 +33,39 @@
<file>notes.xml</file>
</header>
- <section><title>Inets 6.3.1</title>
+ <section><title>Inets 6.3.3</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ The legacy option 'inet6fb4' for inets had stopped
+ working. This bug has now been corrected. Fix by Edwin
+ Fine in bugs.erlang.org ERL-200 and Github PR#1132.</p>
+ <p>
+ Own Id: OTP-13776 Aux Id: ERL-200 PR-1132 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Inets 6.3.2</title>
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ PUT and DELETE support has been added to mod_esi</p>
+ <p>
+ Own Id: OTP-13688 Aux Id: seq13149 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Inets 6.3.1</title>
<section><title>Fixed Bugs and Malfunctions</title>
<list>
diff --git a/lib/inets/src/http_client/httpc_response.erl b/lib/inets/src/http_client/httpc_response.erl
index 91256fa6a2..d8bdac24e3 100644
--- a/lib/inets/src/http_client/httpc_response.erl
+++ b/lib/inets/src/http_client/httpc_response.erl
@@ -110,27 +110,30 @@ result(Response = {{_, 300, _}, _, _},
redirect(Response, Request);
result(Response = {{_, Code, _}, _, _},
+ Request = #request{settings =
+ #http_options{autoredirect = true},
+ method = post}) when (Code =:= 301) orelse
+ (Code =:= 302) orelse
+ (Code =:= 303) ->
+ redirect(Response, Request#request{method = get});
+result(Response = {{_, Code, _}, _, _},
+ Request = #request{settings =
+ #http_options{autoredirect = true},
+ method = post}) when (Code =:= 307) ->
+ redirect(Response, Request);
+result(Response = {{_, Code, _}, _, _},
Request = #request{settings =
#http_options{autoredirect = true},
- method = head}) when (Code =:= 301) orelse
+ method = Method}) when (Code =:= 301) orelse
(Code =:= 302) orelse
(Code =:= 303) orelse
(Code =:= 307) ->
- redirect(Response, Request);
-result(Response = {{_, Code, _}, _, _},
- Request = #request{settings =
- #http_options{autoredirect = true},
- method = get}) when (Code =:= 301) orelse
- (Code =:= 302) orelse
- (Code =:= 303) orelse
- (Code =:= 307) ->
- redirect(Response, Request);
-result(Response = {{_, 303, _}, _, _},
- Request = #request{settings =
- #http_options{autoredirect = true},
- method = post}) ->
- redirect(Response, Request#request{method = get});
-
+ case lists:member(Method, [get, head, options, trace]) of
+ true ->
+ redirect(Response, Request);
+ false ->
+ transparent(Response, Request)
+ end;
result(Response = {{_,503,_}, _, _}, Request) ->
status_service_unavailable(Response, Request);
diff --git a/lib/inets/src/http_server/httpd_conf.erl b/lib/inets/src/http_server/httpd_conf.erl
index e5182ca23c..9e54f2b2c5 100644
--- a/lib/inets/src/http_server/httpd_conf.erl
+++ b/lib/inets/src/http_server/httpd_conf.erl
@@ -395,7 +395,8 @@ validate_properties(Properties) ->
%% That is, if property A depends on property B.
%% The only sunch preperty at this time is bind_address that depends
%% on ipfamily.
-validate_properties2(Properties) ->
+validate_properties2(Properties0) ->
+ Properties = fix_ipfamily(Properties0),
case proplists:get_value(bind_address, Properties) of
undefined ->
case proplists:get_value(sock_type, Properties, ip_comm) of
@@ -422,6 +423,15 @@ validate_properties2(Properties) ->
end
end.
+fix_ipfamily(Properties) ->
+ case proplists:get_value(ipfamily, Properties) of
+ undefined ->
+ Properties;
+ IpFamily ->
+ NewProps = proplists:delete(ipfamily, Properties),
+ [{ipfamily, validate_ipfamily(IpFamily)} | NewProps]
+ end.
+
add_inet_defaults(Properties) ->
case proplists:get_value(ipfamily, Properties) of
undefined ->
diff --git a/lib/inets/src/http_server/httpd_example.erl b/lib/inets/src/http_server/httpd_example.erl
index 424d269859..c893b10dca 100644
--- a/lib/inets/src/http_server/httpd_example.erl
+++ b/lib/inets/src/http_server/httpd_example.erl
@@ -20,7 +20,7 @@
%%
-module(httpd_example).
-export([print/1]).
--export([get/2, post/2, yahoo/2, test1/2, get_bin/2, peer/2]).
+-export([get/2, put/2, post/2, yahoo/2, test1/2, get_bin/2, peer/2]).
-export([newformat/3]).
%% These are used by the inets test-suite
@@ -59,6 +59,11 @@ get(_Env,[]) ->
get(Env,Input) ->
default(Env,Input).
+put(Env,{Input,_Body}) ->
+ default(Env,Input);
+put(Env,Input) ->
+ default(Env,Input).
+
get_bin(_Env,_Input) ->
[list_to_binary(header()),
list_to_binary(top("GET Example")),
@@ -94,7 +99,7 @@ default(Env,Input) ->
io_lib:format("~p",[httpd:parse_query(Input)]),"\n",
footer()].
-peer(Env, Input) ->
+peer(Env, _Input) ->
Header =
case proplists:get_value(peer_cert, Env) of
undefined ->
@@ -161,7 +166,7 @@ sleep(T) -> receive after T -> ok end.
%% ------------------------------------------------------
-chunk_timeout(SessionID, _, StrInt) ->
+chunk_timeout(SessionID, _, _StrInt) ->
mod_esi:deliver(SessionID, "Tranfer-Encoding:chunked/html\r\n\r\n"),
mod_esi:deliver(SessionID, top("Test chunk encoding timeout")),
timer:sleep(20000),
diff --git a/lib/inets/src/http_server/mod_esi.erl b/lib/inets/src/http_server/mod_esi.erl
index 2800250727..b21af1418c 100644
--- a/lib/inets/src/http_server/mod_esi.erl
+++ b/lib/inets/src/http_server/mod_esi.erl
@@ -241,7 +241,7 @@ alias_match_str(Alias, eval_script_alias) ->
%%------------------------ Erl mechanism --------------------------------
erl(#mod{method = Method} = ModData, ESIBody, Modules)
- when (Method =:= "GET") orelse (Method =:= "HEAD") ->
+ when (Method =:= "GET") orelse (Method =:= "HEAD") orelse (Method =:= "DELETE") ->
?hdrt("erl", [{method, Method}]),
case httpd_util:split(ESIBody,":|%3A|/",2) of
{ok, [ModuleName, FuncAndInput]} ->
@@ -264,35 +264,32 @@ erl(#mod{method = Method} = ModData, ESIBody, Modules)
{proceed, [{status,{400, none, BadRequest}} | ModData#mod.data]}
end;
-erl(#mod{request_uri = ReqUri,
- method = "PUT",
- http_version = Version,
- data = Data}, _ESIBody, _Modules) ->
- ?hdrt("erl", [{method, put}]),
- {proceed, [{status,{501,{"PUT", ReqUri, Version},
- ?NICE("Erl mechanism doesn't support method PUT")}}|
- Data]};
-
-erl(#mod{request_uri = ReqUri,
- method = "DELETE",
- http_version = Version,
- data = Data}, _ESIBody, _Modules) ->
- ?hdrt("erl", [{method, delete}]),
- {proceed,[{status,{501,{"DELETE", ReqUri, Version},
- ?NICE("Erl mechanism doesn't support method DELETE")}}|
- Data]};
-
-erl(#mod{request_uri = ReqUri,
- method = "PATCH",
- http_version = Version,
- data = Data}, _ESIBody, _Modules) ->
- ?hdrt("erl", [{method, patch}]),
- {proceed, [{status,{501,{"PATCH", ReqUri, Version},
- ?NICE("Erl mechanism doesn't support method PATCH")}}|
- Data]};
+erl(#mod{method = "PUT", entity_body = Body} = ModData,
+ ESIBody, Modules) ->
+ case httpd_util:split(ESIBody,":|%3A|/",2) of
+ {ok, [ModuleName, FuncAndInput]} ->
+ case httpd_util:split(FuncAndInput,"[\?/]",2) of
+ {ok, [FunctionName, Input]} ->
+ generate_webpage(ModData, ESIBody, Modules,
+ list_to_atom(ModuleName),
+ FunctionName, {Input,Body},
+ [{entity_body, Body} |
+ script_elements(FuncAndInput, Input)]);
+ {ok, [FunctionName]} ->
+ generate_webpage(ModData, ESIBody, Modules,
+ list_to_atom(ModuleName),
+ FunctionName, {undefined,Body},
+ [{entity_body, Body} |
+ script_elements(FuncAndInput, "")]);
+ {ok, BadRequest} ->
+ {proceed,[{status,{400,none, BadRequest}} |
+ ModData#mod.data]}
+ end;
+ {ok, BadRequest} ->
+ {proceed, [{status,{400, none, BadRequest}} | ModData#mod.data]}
+ end;
-erl(#mod{method = "POST",
- entity_body = Body} = ModData, ESIBody, Modules) ->
+erl(#mod{method = "POST", entity_body = Body} = ModData, ESIBody, Modules) ->
?hdrt("erl", [{method, post}]),
case httpd_util:split(ESIBody,":|%3A|/",2) of
{ok,[ModuleName, Function]} ->
@@ -301,7 +298,16 @@ erl(#mod{method = "POST",
Function, Body, [{entity_body, Body}]);
{ok, BadRequest} ->
{proceed,[{status, {400, none, BadRequest}} | ModData#mod.data]}
- end.
+ end;
+
+erl(#mod{request_uri = ReqUri,
+ method = "PATCH",
+ http_version = Version,
+ data = Data}, _ESIBody, _Modules) ->
+ ?hdrt("erl", [{method, patch}]),
+ {proceed, [{status,{501,{"PATCH", ReqUri, Version},
+ ?NICE("Erl mechanism doesn't support method PATCH")}}|
+ Data]}.
generate_webpage(ModData, ESIBody, [all], Module, FunctionName,
Input, ScriptElements) ->
diff --git a/lib/inets/test/httpc_SUITE.erl b/lib/inets/test/httpc_SUITE.erl
index 932567ec55..57da82c6ad 100644
--- a/lib/inets/test/httpc_SUITE.erl
+++ b/lib/inets/test/httpc_SUITE.erl
@@ -500,10 +500,11 @@ redirect_multiple_choises(Config) when is_list(Config) ->
httpc:request(get, {URL300, []}, [{autoredirect, false}], []).
%%-------------------------------------------------------------------------
redirect_moved_permanently() ->
- [{doc, "If the 301 status code is received in response to a request other "
- "than GET or HEAD, the user agent MUST NOT automatically redirect the request "
- "unless it can be confirmed by the user, since this might change "
- "the conditions under which the request was issued."}].
+ [{doc, "The server SHOULD generate a Location header field in the response "
+ "containing a preferred URI reference for the new permanent URI. The user "
+ "agent MAY use the Location field value for automatic redirection. The server's "
+ "response payload usually contains a short hypertext note with a "
+ "hyperlink to the new URI(s)."}].
redirect_moved_permanently(Config) when is_list(Config) ->
URL301 = url(group_name(Config), "/301.html", Config),
@@ -514,15 +515,16 @@ redirect_moved_permanently(Config) when is_list(Config) ->
{ok, {{_,200,_}, [_ | _], []}}
= httpc:request(head, {URL301, []}, [], []),
- {ok, {{_,301,_}, [_ | _], [_|_]}}
+ {ok, {{_,200,_}, [_ | _], [_|_]}}
= httpc:request(post, {URL301, [],"text/plain", "foobar"},
[], []).
%%-------------------------------------------------------------------------
redirect_found() ->
- [{doc," If the 302 status code is received in response to a request other "
- "than GET or HEAD, the user agent MUST NOT automatically redirect the "
- "request unless it can be confirmed by the user, since this might change "
- "the conditions under which the request was issued."}].
+ [{doc, "The server SHOULD generate a Location header field in the response "
+ "containing a URI reference for the different URI. The user agent MAY "
+ "use the Location field value for automatic redirection. The server's "
+ "response payload usually contains a short hypertext note with a "
+ "hyperlink to the different URI(s)."}].
redirect_found(Config) when is_list(Config) ->
URL302 = url(group_name(Config), "/302.html", Config),
@@ -533,14 +535,14 @@ redirect_found(Config) when is_list(Config) ->
{ok, {{_,200,_}, [_ | _], []}}
= httpc:request(head, {URL302, []}, [], []),
- {ok, {{_,302,_}, [_ | _], [_|_]}}
+ {ok, {{_,200,_}, [_ | _], [_|_]}}
= httpc:request(post, {URL302, [],"text/plain", "foobar"},
[], []).
%%-------------------------------------------------------------------------
redirect_see_other() ->
[{doc, "The different URI SHOULD be given by the Location field in the response. "
"Unless the request method was HEAD, the entity of the response SHOULD contain a short "
- "hypertext note with a hyperlink to the new URI(s). "}].
+ "hypertext note with a hyperlink to the new URI(s)."}].
redirect_see_other(Config) when is_list(Config) ->
URL303 = url(group_name(Config), "/303.html", Config),
@@ -556,10 +558,11 @@ redirect_see_other(Config) when is_list(Config) ->
[], []).
%%-------------------------------------------------------------------------
redirect_temporary_redirect() ->
- [{doc," If the 307 status code is received in response to a request other "
- "than GET or HEAD, the user agent MUST NOT automatically redirect the request "
- "unless it can be confirmed by the user, since this might change "
- "the conditions under which the request was issued."}].
+ [{doc, "The server SHOULD generate a Location header field in the response "
+ "containing a URI reference for the different URI. The user agent MAY "
+ "use the Location field value for automatic redirection. The server's "
+ "response payload usually contains a short hypertext note with a "
+ "hyperlink to the different URI(s)."}].
redirect_temporary_redirect(Config) when is_list(Config) ->
URL307 = url(group_name(Config), "/307.html", Config),
@@ -570,7 +573,7 @@ redirect_temporary_redirect(Config) when is_list(Config) ->
{ok, {{_,200,_}, [_ | _], []}}
= httpc:request(head, {URL307, []}, [], []),
- {ok, {{_,307,_}, [_ | _], [_|_]}}
+ {ok, {{_,200,_}, [_ | _], [_|_]}}
= httpc:request(post, {URL307, [],"text/plain", "foobar"},
[], []).
diff --git a/lib/inets/test/httpd_SUITE.erl b/lib/inets/test/httpd_SUITE.erl
index 3194b5ad3d..28e77151f2 100644
--- a/lib/inets/test/httpd_SUITE.erl
+++ b/lib/inets/test/httpd_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2013-2015. All Rights Reserved.
+%% Copyright Ericsson AB 2013-2016. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -119,8 +119,10 @@ groups() ->
]},
{htaccess, [], [htaccess_1_1, htaccess_1_0, htaccess_0_9]},
{security, [], [security_1_1, security_1_0]}, %% Skip 0.9 as causes timing issus in test code
- {http_1_1, [], [host, chunked, expect, cgi, cgi_chunked_encoding_test,
- trace, range, if_modified_since, mod_esi_chunk_timeout] ++ http_head() ++ http_get() ++ load()},
+ {http_1_1, [],
+ [host, chunked, expect, cgi, cgi_chunked_encoding_test,
+ trace, range, if_modified_since, mod_esi_chunk_timeout,
+ esi_put] ++ http_head() ++ http_get() ++ load()},
{http_1_0, [], [host, cgi, trace] ++ http_head() ++ http_get() ++ load()},
{http_0_9, [], http_head() ++ http_get() ++ load()}
].
@@ -283,20 +285,50 @@ init_per_testcase(Case, Config) when Case == host; Case == trace ->
http_1_1 ->
httpd_1_1
end,
- [{version_cb, Cb} | proplists:delete(version_cb, Config)];
+ dbg(
+ Case,
+ [{version_cb, Cb} | proplists:delete(version_cb, Config)],
+ init);
init_per_testcase(range, Config) ->
ct:timetrap({seconds, 20}),
DocRoot = proplists:get_value(doc_root, Config),
create_range_data(DocRoot),
- Config;
+ dbg(range, Config, init);
-init_per_testcase(_, Config) ->
+init_per_testcase(Case, Config) ->
ct:timetrap({seconds, 20}),
- Config.
-
-end_per_testcase(_Case, _Config) ->
- ok.
+ dbg(Case, Config, init).
+
+end_per_testcase(Case, Config) ->
+ dbg(Case, Config, 'end').
+
+
+dbg(Case, Config, Status) ->
+ Cases = [esi_put],
+ case lists:member(Case, Cases) of
+ true ->
+ case Status of
+ init ->
+ dbg:tracer(),
+ dbg:p(all, c),
+ dbg:tpl(httpd_example, cx),
+ dbg:tpl(mod_esi, generate_webpage, cx),
+ io:format("dbg: started~n"),
+ Config;
+ 'end' ->
+ io:format("dbg: stopped~n"),
+ dbg:stop_clear(),
+ ok
+ end;
+ false ->
+ case Status of
+ init ->
+ Config;
+ 'end' ->
+ ok
+ end
+ end.
%%-------------------------------------------------------------------------
%% Test cases starts here.
@@ -765,6 +797,14 @@ esi(Config) when is_list(Config) ->
ok = http_status("GET /cgi-bin/erl/httpd_example:peer ",
Config, [{statuscode, 200},
{header, "peer-cert-exist", peer(Config)}]).
+
+%%-------------------------------------------------------------------------
+esi_put() ->
+ [{doc, "Test mod_esi PUT"}].
+
+esi_put(Config) when is_list(Config) ->
+ ok = http_status("PUT /cgi-bin/erl/httpd_example/put/123342234123 ",
+ Config, [{statuscode, 200}]).
%%-------------------------------------------------------------------------
mod_esi_chunk_timeout(Config) when is_list(Config) ->
diff --git a/lib/inets/test/inets_SUITE.erl b/lib/inets/test/inets_SUITE.erl
index 5eaf3a28a0..38b8229389 100644
--- a/lib/inets/test/inets_SUITE.erl
+++ b/lib/inets/test/inets_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2015. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2016. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -212,11 +212,19 @@ start_httpd(Config) when is_list(Config) ->
Pids0 = [ServicePid || {_, ServicePid} <- inets:services()],
true = lists:member(Pid0, Pids0),
[_|_] = inets:services_info(),
-
inets:stop(httpd, Pid0),
ct:sleep(500),
+ Pids1 = [ServicePid || {_, ServicePid} <- inets:services()],
+ false = lists:member(Pid0, Pids1),
+ {ok, Pid0b} =
+ inets:start(httpd, [{port, 0}, {ipfamily, inet6fb4} | HttpdConf]),
+ Pids0b = [ServicePid || {_, ServicePid} <- inets:services()],
+ true = lists:member(Pid0b, Pids0b),
+ [_|_] = inets:services_info(),
+ inets:stop(httpd, Pid0b),
+ ct:sleep(500),
Pids1 = [ServicePid || {_, ServicePid} <- inets:services()],
- false = lists:member(Pid0, Pids1),
+ false = lists:member(Pid0b, Pids1),
{ok, Pid1} =
inets:start(httpd, [{port, 0}, {ipfamily, inet} | HttpdConf],
stand_alone),
diff --git a/lib/inets/test/inets_sup_SUITE.erl b/lib/inets/test/inets_sup_SUITE.erl
index 5b8b1463c8..1e664337e6 100644
--- a/lib/inets/test/inets_sup_SUITE.erl
+++ b/lib/inets/test/inets_sup_SUITE.erl
@@ -33,7 +33,7 @@ suite() ->
all() ->
[default_tree, ftpc_worker, tftpd_worker,
- httpd_subtree, httpd_subtree_profile,
+ httpd_config, httpd_subtree, httpd_subtree_profile,
httpc_subtree].
groups() ->
@@ -52,9 +52,32 @@ end_per_suite(_) ->
inets:stop(),
ok.
-init_per_testcase(httpd_subtree, Config) ->
+init_per_testcase(httpd_config = TC, Config) ->
+ PrivDir = proplists:get_value(priv_dir, Config),
+ Dir = filename:join(PrivDir, TC),
+ ok = file:make_dir(Dir),
+
+ FallbackConfig = [{port, 0},
+ {server_name,"www.test"},
+ {modules, [mod_get]},
+ {server_root, Dir},
+ {document_root, Dir},
+ {bind_address, any},
+ {ipfamily, inet6fb4}],
+ try
+ inets:stop(),
+ inets:start(),
+ inets:start(httpd, FallbackConfig),
+ Config
+ catch
+ _:Reason ->
+ inets:stop(),
+ exit({failed_starting_inets, Reason})
+ end;
+
+init_per_testcase(httpd_subtree = TC, Config) ->
PrivDir = proplists:get_value(priv_dir, Config),
- Dir = filename:join(PrivDir, "root"),
+ Dir = filename:join(PrivDir, TC),
ok = file:make_dir(Dir),
SimpleConfig = [{port, 0},
@@ -75,9 +98,9 @@ init_per_testcase(httpd_subtree, Config) ->
exit({failed_starting_inets, Reason})
end;
-init_per_testcase(httpd_subtree_profile, Config) ->
+init_per_testcase(httpd_subtree_profile = TC, Config) ->
PrivDir = proplists:get_value(priv_dir, Config),
- Dir = filename:join(PrivDir, "root"),
+ Dir = filename:join(PrivDir, TC),
ok = file:make_dir(Dir),
SimpleConfig = [{port, 0},
@@ -193,6 +216,11 @@ tftpd_worker(Config) when is_list(Config) ->
[] = supervisor:which_children(tftp_sup),
ok.
+httpd_config() ->
+ [{doc, "Makes sure the httpd config works for inet6fb4."}].
+httpd_config(Config) when is_list(Config) ->
+ do_httpd_subtree(Config, default).
+
httpd_subtree() ->
[{doc, "Makes sure the httpd sub tree is correct."}].
httpd_subtree(Config) when is_list(Config) ->
diff --git a/lib/inets/vsn.mk b/lib/inets/vsn.mk
index 3408c3b128..f668ef106c 100644
--- a/lib/inets/vsn.mk
+++ b/lib/inets/vsn.mk
@@ -19,6 +19,6 @@
# %CopyrightEnd%
APPLICATION = inets
-INETS_VSN = 6.3.1
+INETS_VSN = 6.3.3
PRE_VSN =
APP_VSN = "$(APPLICATION)-$(INETS_VSN)$(PRE_VSN)"
diff --git a/lib/jinterface/doc/src/Makefile b/lib/jinterface/doc/src/Makefile
index cd1e61a795..508c8e01b5 100644
--- a/lib/jinterface/doc/src/Makefile
+++ b/lib/jinterface/doc/src/Makefile
@@ -166,7 +166,7 @@ release_docs_spec: docs
$(INSTALL_DIR) "$(RELSYSDIR)/doc/html"
$(INSTALL_DIR) "$(RELSYSDIR)/doc/html/java/$(JAVA_PKG_PATH)"
$(INSTALL_DATA) $(INFO_FILE) "$(RELSYSDIR)"
- (/bin/cp -rf ../html "$(RELSYSDIR)/doc")
+ ($(CP) -rf ../html "$(RELSYSDIR)/doc")
# $(INSTALL_DATA) $(GIF_FILES) $(EXTRA_FILES) $(HTML_FILES) \
# "$(RELSYSDIR)/doc/html"
diff --git a/lib/jinterface/doc/src/notes.xml b/lib/jinterface/doc/src/notes.xml
index 3151fc4b5e..30f607c357 100644
--- a/lib/jinterface/doc/src/notes.xml
+++ b/lib/jinterface/doc/src/notes.xml
@@ -31,6 +31,22 @@
</header>
<p>This document describes the changes made to the Jinterface application.</p>
+<section><title>Jinterface 1.7.1</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Update build scripts to not make assumtions about where
+ env, cp and perl are located.</p>
+ <p>
+ Own Id: OTP-13800</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Jinterface 1.7</title>
<section><title>Fixed Bugs and Malfunctions</title>
diff --git a/lib/jinterface/vsn.mk b/lib/jinterface/vsn.mk
index 752b34e78c..c29d9df3d7 100644
--- a/lib/jinterface/vsn.mk
+++ b/lib/jinterface/vsn.mk
@@ -1 +1 @@
-JINTERFACE_VSN = 1.7
+JINTERFACE_VSN = 1.7.1
diff --git a/lib/kernel/doc/src/app.xml b/lib/kernel/doc/src/app.xml
index 5e0da409a3..d2e9390d7e 100644
--- a/lib/kernel/doc/src/app.xml
+++ b/lib/kernel/doc/src/app.xml
@@ -151,7 +151,7 @@ ApplicationVersion = string()</code>
application is allowed to be started. <c>systools</c> uses
this list to generate correct start scripts. Defaults to
the empty list, but notice that all applications have
- dependencies to (at least) <c>Kernel</c> and <c>STDLIB</c>.</p>
+ dependencies to (at least) Kernel and STDLIB.</p>
</item>
<tag><c>env</c></tag>
<item>
@@ -171,7 +171,7 @@ ApplicationVersion = string()</code>
implemented as a supervision tree, otherwise the application
controller does not know how to start it. <c>mod</c>
can be omitted for applications without processes, typically
- code libraries, for example, <c>STDLIB</c>.</p>
+ code libraries, for example, STDLIB.</p>
</item>
<tag><c>start_phases</c></tag>
<item>
@@ -236,7 +236,7 @@ ApplicationVersion = string()</code>
<section>
<title>See Also</title>
<p><seealso marker="application"><c>application(3)</c></seealso>,
- <seealso marker="sasl:systools"><c>sasl:systools(3)</c></seealso></p>
+ <seealso marker="sasl:systools"><c>systools(3)</c></seealso></p>
</section>
</fileref>
diff --git a/lib/kernel/doc/src/application.xml b/lib/kernel/doc/src/application.xml
index 8d33aa86e7..886286b76d 100644
--- a/lib/kernel/doc/src/application.xml
+++ b/lib/kernel/doc/src/application.xml
@@ -200,7 +200,7 @@
<seealso marker="app"><c>app(4)</c></seealso>.</p>
<p>If <c><anno>Distributed</anno> == {<anno>Application</anno>,[<anno>Time</anno>,]<anno>Nodes</anno>}</c>,
the application becomes distributed. The argument overrides
- the value for the application in the <c>Kernel</c> configuration
+ the value for the application in the Kernel configuration
parameter <c>distributed</c>. <c><anno>Application</anno></c> must be
the application name (same as in the first argument).
If a node crashes and <c><anno>Time</anno></c> is specified,
@@ -221,7 +221,7 @@ Nodes = [cp1@cave, {cp2@cave, cp3@cave}]</code>
the application is to be started at <c>cp2@cave</c>
or <c>cp3@cave</c>.</p>
<p>If <c>Distributed == default</c>, the value for
- the application in the <c>Kernel</c> configuration parameter
+ the application in the Kernel configuration parameter
<c>distributed</c> is used.</p>
</desc>
</func>
@@ -267,7 +267,7 @@ Nodes = [cp1@cave, {cp2@cave, cp3@cave}]</code>
started, <c>Application</c> is started as well.</p>
<p>By default, all applications are loaded with permission
<c>true</c> on all nodes. The permission can be configured
- using the <c>Kernel</c> configuration parameter <c>permissions</c>.</p>
+ using the Kernel configuration parameter <c>permissions</c>.</p>
</desc>
</func>
<func>
diff --git a/lib/kernel/doc/src/auth.xml b/lib/kernel/doc/src/auth.xml
index 03f983b96d..5901446960 100644
--- a/lib/kernel/doc/src/auth.xml
+++ b/lib/kernel/doc/src/auth.xml
@@ -47,7 +47,7 @@
<desc>
<p>Use
<seealso marker="erts:erlang#erlang:get_cookie/0"><c>erlang:get_cookie()</c></seealso>
- in <c>ERTS</c> instead.</p>
+ in ERTS instead.</p>
</desc>
</func>
<func>
@@ -59,7 +59,7 @@
<desc>
<p>Use
<seealso marker="erts:erlang#erlang:set_cookie/2"><c>erlang:set_cookie(node(), <anno>Cookie</anno>)</c>
- in <c>ERTS</c></seealso> instead.</p>
+ in ERTS</seealso> instead.</p>
</desc>
</func>
<func>
diff --git a/lib/kernel/doc/src/code.xml b/lib/kernel/doc/src/code.xml
index d3611d6a03..3143cdc825 100644
--- a/lib/kernel/doc/src/code.xml
+++ b/lib/kernel/doc/src/code.xml
@@ -86,11 +86,11 @@
an <c>ebin</c> directory are ignored.</p>
<p>All application directories found in the additional directories
appears before the standard OTP applications, except for the
- <c>Kernel</c> and <c>STDLIB</c> applications, which are placed before
+ Kernel and STDLIB applications, which are placed before
any additional applications. In other words, modules found in any
of the additional library directories override modules with
- the same name in OTP, except for modules in <c>Kernel</c> and
- <c>STDLIB</c>.</p>
+ the same name in OTP, except for modules in Kernel and
+ STDLIB.</p>
<p>Environment variable <c>ERL_LIBS</c> (if defined) is to contain
a colon-separated (for Unix-like systems) or semicolon-separated
(for Windows) list of additional libraries.</p>
@@ -151,7 +151,7 @@ zip:create("mnesia-4.4.7.ez",
<c>$OTPROOT/lib/mnesia.ez/mnesia/ebin</c> or
<c>$OTPROOT/lib/mnesia-4.4.7.ez/mnesia-4.4.7/ebin</c>.</p>
- <p>The code server uses module <c>erl_prim_loader</c> in <c>ERTS</c>
+ <p>The code server uses module <c>erl_prim_loader</c> in ERTS
(possibly through <c>erl_boot_server</c>) to read code files from
archives. However, the functions in <c>erl_prim_loader</c> can also be
used by other applications to read files from archives. For
@@ -382,9 +382,14 @@ zip:create("mnesia-4.4.7.ez",
<name name="add_pathsa" arity="1"/>
<fsummary>Add directories to the beginning of the code path.</fsummary>
<desc>
- <p>Adds the directories in <c><anno>Dirs</anno></c> to the beginning of
- the code path. If a <c><anno>Dir</anno></c> exists, it is removed
- from the old position in the code path.</p>
+ <p>Traverses <c><anno>Dirs</anno></c> and adds
+ each <c><anno>Dir</anno></c> to the beginning of the code
+ path. This means that the order of <c><anno>Dirs</anno></c>
+ is reversed in the resulting code path. For example, if you
+ add <c>[Dir1,Dir2]</c>, the resulting path will
+ be <c>[Dir2,Dir1|OldCodePath]</c>.</p>
+ <p>If a <c><anno>Dir</anno></c> already exists in the code
+ path, it is removed from the old position.</p>
<p>Always returns <c>ok</c>, regardless of the validity of each
individual <c><anno>Dir</anno></c>.</p>
</desc>
@@ -651,6 +656,11 @@ ok = code:finish_loading(Prepared),
<p>Purges the code for <c><anno>Module</anno></c>, that is, removes code
marked as old. If some processes still linger in the old code,
these processes are killed before the code is removed.</p>
+ <note><p>As of ERTS version 9.0, a process is only considered
+ to be lingering in the code if it has direct references to the code.
+ For more information see documentation of
+ <seealso marker="erts:erlang#check_process_code/3"><c>erlang:check_process_code/3</c></seealso>,
+ which is used in order to determine this.</p></note>
<p>Returns <c>true</c> if successful and any process is needed to
be killed, otherwise <c>false</c>.</p>
</desc>
@@ -661,6 +671,11 @@ ok = code:finish_loading(Prepared),
<desc>
<p>Purges the code for <c><anno>Module</anno></c>, that is, removes code
marked as old, but only if no processes linger in it.</p>
+ <note><p>As of ERTS version 9.0, a process is only considered
+ to be lingering in the code if it has direct references to the code.
+ For more information see documentation of
+ <seealso marker="erts:erlang#check_process_code/3"><c>erlang:check_process_code/3</c></seealso>,
+ which is used in order to determine this.</p></note>
<p>Returns <c>false</c> if the module cannot be purged because
of processes lingering in old code, otherwise <c>true</c>.</p>
</desc>
@@ -678,9 +693,9 @@ ok = code:finish_loading(Prepared),
<p>Normally, <c><anno>Loaded</anno></c> is the absolute filename
<c>Filename</c> from which the code is obtained. If the module
is preloaded (see
- <seealso marker="sasl:script"><c>sasl:script(4)</c></seealso>),
+ <seealso marker="sasl:script"><c>script(4)</c></seealso>),
<c>Loaded==preloaded</c>. If the module is Cover-compiled (see
- <seealso marker="tools:cover"><c>tools:cover(3)</c></seealso>),
+ <seealso marker="tools:cover"><c>cover(3)</c></seealso>),
<c>Loaded==cover_compiled</c>.</p>
</desc>
</func>
diff --git a/lib/kernel/doc/src/erl_boot_server.xml b/lib/kernel/doc/src/erl_boot_server.xml
index 897365f9b9..4109251387 100644
--- a/lib/kernel/doc/src/erl_boot_server.xml
+++ b/lib/kernel/doc/src/erl_boot_server.xml
@@ -38,13 +38,13 @@
command-line flag <c>-loader inet</c>. All hosts specified
with command-line flag <c>-hosts Host</c> must have one
instance of this server running.</p>
- <p>This server can be started with the <c>Kernel</c> configuration
+ <p>This server can be started with the Kernel configuration
parameter <c>start_boot_server</c>.</p>
<p>The <c>erl_boot_server</c> can read regular files and
files in archives. See <seealso marker="code"><c>code(3)</c></seealso>
and
<seealso marker="erts:erl_prim_loader"><c>erl_prim_loader(3)</c></seealso>
- in <c>ERTS</c>.</p>
+ in ERTS.</p>
<warning><p>The support for loading code from archive files is
experimental. It is released before it is ready
to obtain early feedback. The file format, semantics,
diff --git a/lib/kernel/doc/src/erl_ddll.xml b/lib/kernel/doc/src/erl_ddll.xml
index a5ce58ef3e..75114e015c 100644
--- a/lib/kernel/doc/src/erl_ddll.xml
+++ b/lib/kernel/doc/src/erl_ddll.xml
@@ -201,7 +201,7 @@
<desc>
<p>Removes a driver monitor in much the same way as
<seealso marker="erts:erlang#erlang:demonitor/1"><c>erlang:demonitor/1</c></seealso>
- in <c>ERTS</c>
+ in ERTS
does with process monitors. For details about how to create
driver monitors, see
<seealso marker="#monitor/2"><c>monitor/2</c></seealso>,
@@ -431,7 +431,7 @@
<p>Creates a driver monitor and works in many
ways as
<seealso marker="erts:erlang#erlang:monitor/2"><c>erlang:monitor/2</c></seealso>
- in <c>ERTS</c>,
+ in ERTS,
does for processes. When a driver changes state, the monitor
results in a monitor message that is sent to the calling
process. <c><anno>MonitorRef</anno></c> returned by this function is
@@ -745,7 +745,7 @@
<p>This parameter is the name of the driver
to be used in subsequent calls to function
<seealso marker="erts:erlang#open_port/2"><c>erlang:open_port</c></seealso>
- in <c>ERTS</c>.
+ in ERTS.
The name can be specified as an <c>iolist()</c> or
an <c>atom()</c>. The name specified when loading is used
to find the object file (with the help of <c><anno>Path</anno></c>
diff --git a/lib/kernel/doc/src/error_logger.xml b/lib/kernel/doc/src/error_logger.xml
index a8273e59e2..814e8eac46 100644
--- a/lib/kernel/doc/src/error_logger.xml
+++ b/lib/kernel/doc/src/error_logger.xml
@@ -33,7 +33,7 @@
<description>
<p>The Erlang <em>error logger</em> is an event manager (see
<seealso marker="doc/design_principles:des_princ">OTP Design Principles</seealso> and
- <seealso marker="stdlib:gen_event"><c>stdlib:gen_event(3)</c></seealso>),
+ <seealso marker="stdlib:gen_event"><c>gen_event(3)</c></seealso>),
registered as <c>error_logger</c>. Errors, warnings, and info events
are sent to the error logger from the Erlang runtime system and
the different Erlang/OTP applications. The events are, by default,
@@ -44,12 +44,12 @@
executing.</p>
<p>Initially, <c>error_logger</c> has only a primitive event
handler, which buffers and prints the raw event messages. During
- system startup, the <c>Kernel</c> application replaces this with a
+ system startup, the Kernel application replaces this with a
<em>standard event handler</em>, by default one that writes
- nicely formatted output to the terminal. <c>Kernel</c> can also be
+ nicely formatted output to the terminal. Kernel can also be
configured so that events are logged to a file instead, or not logged at all,
see <seealso marker="kernel_app"><c>kernel(6)</c></seealso>.</p>
- <p>Also the <c>SASL</c> application, if started, adds its own event
+ <p>Also the SASL application, if started, adds its own event
handler, which by default writes supervisor, crash, and progress
reports to the terminal. See
<seealso marker="sasl:sasl_app"><c>sasl(6)</c></seealso>.</p>
@@ -58,9 +58,9 @@
User-defined event handlers can be added to handle application-specific
events, see
<seealso marker="#add_report_handler/1"><c>add_report_handler/1,2</c></seealso>.
- Also, a useful event handler is provided in <c>STDLIB</c> for multi-file
+ Also, a useful event handler is provided in STDLIB for multi-file
logging of events, see
- <seealso marker="stdlib:log_mf_h"><c>stdlib:log_mf_h(3)</c></seealso>.</p>
+ <seealso marker="stdlib:log_mf_h"><c>log_mf_h(3)</c></seealso>.</p>
<p>Warning events were introduced in Erlang/OTP R9C and are enabled
by default as from Erlang/OTP 18.0. To retain backwards compatibility
with existing user-defined event handlers, the warning events can be
@@ -82,7 +82,7 @@
<p>Adds a new event handler to the error logger. The event
handler must be implemented as a <c>gen_event</c> callback
module, see
- <seealso marker="stdlib:gen_event"><c>stdlib:gen_event(3)</c></seealso>.</p>
+ <seealso marker="stdlib:gen_event"><c>gen_event(3)</c></seealso>.</p>
<p><c><anno>Handler</anno></c> is typically the name of the callback module
and <c><anno>Args</anno></c> is an optional term (defaults to []) passed
to the initialization callback function <c><anno>Handler</anno>:init/1</c>.
@@ -97,7 +97,7 @@
<desc>
<p>Deletes an event handler from the error logger by calling
<c>gen_event:delete_handler(error_logger, <anno>Handler</anno>, [])</c>,
- see <seealso marker="stdlib:gen_event"><c>stdlib:gen_event(3)</c></seealso>.</p>
+ see <seealso marker="stdlib:gen_event"><c>gen_event(3)</c></seealso>.</p>
</desc>
</func>
<func>
@@ -110,7 +110,7 @@
The <c><anno>Format</anno></c> and <c><anno>Data</anno></c> arguments
are the same as the arguments of
<seealso marker="stdlib:io#format/2"><c>io:format/2</c></seealso>
- in <c>STDLIB</c>.
+ in STDLIB.
The event is handled by the standard event handler.</p>
<p><em>Example:</em></p>
<pre>
@@ -171,7 +171,7 @@ ok</pre>
The <c><anno>Format</anno></c> and <c><anno>Data</anno></c> arguments
are the same as the arguments of
<seealso marker="stdlib:io#format/2"><c>io:format/2</c></seealso>
- in <c>STDLIB</c>. The event is handled by the standard event handler.</p>
+ in STDLIB. The event is handled by the standard event handler.</p>
<p><em>Example:</em></p>
<pre>
1> <input>error_logger:info_msg("Something happened in ~p~n", [a_module]).</input>
@@ -235,7 +235,7 @@ ok</pre>
<p>Enables or disables printout of standard events to a file.</p>
<p>This is done by adding or deleting the standard event handler
for output to file. Thus, calling this function overrides
- the value of the <c>Kernel</c> <c>error_logger</c> configuration
+ the value of the Kernel <c>error_logger</c> configuration
parameter.</p>
<p>Enabling file logging can be used together with calling
<c>tty(false)</c>, to have a silent system where
@@ -274,7 +274,7 @@ ok</pre>
to the terminal.</p>
<p>This is done by adding or deleting the standard event handler
for output to the terminal. Thus, calling this function overrides
- the value of the <c>Kernel</c> <c>error_logger</c> configuration parameter.</p>
+ the value of the Kernel <c>error_logger</c> configuration parameter.</p>
</desc>
</func>
<func>
@@ -323,7 +323,7 @@ ok</pre>
The <c><anno>Format</anno></c> and <c><anno>Data</anno></c> arguments
are the same as the arguments of
<seealso marker="stdlib:io#format/2"><c>io:format/2</c></seealso>
- in <c>STDLIB</c>.
+ in STDLIB.
The event is handled by the standard event handler. It is tagged
as an error, warning, or info, see
<seealso marker="#warning_map/0"><c>warning_map/0</c></seealso>.</p>
@@ -416,8 +416,8 @@ ok</pre>
</section>
<section>
<title>See Also</title>
- <p><seealso marker="stdlib:gen_event"><c>stdlib:gen_event(3)</c></seealso>,
- <seealso marker="stdlib:log_mf_h"><c>stdlib:log_mf_h(3)</c></seealso>
+ <p><seealso marker="stdlib:gen_event"><c>gen_event(3)</c></seealso>,
+ <seealso marker="stdlib:log_mf_h"><c>log_mf_h(3)</c></seealso>
<seealso marker="kernel_app"><c>kernel(6)</c></seealso>
<seealso marker="sasl:sasl_app"><c>sasl(6)</c></seealso></p>
</section>
diff --git a/lib/kernel/doc/src/file.xml b/lib/kernel/doc/src/file.xml
index 7d86c3ebcb..09497482cf 100644
--- a/lib/kernel/doc/src/file.xml
+++ b/lib/kernel/doc/src/file.xml
@@ -79,7 +79,7 @@
<seealso marker="#list_dir_all"><c>list_dir_all/1</c></seealso> and
<seealso marker="#read_link_all"><c>read_link_all/1</c></seealso>.</p>
- <p>See also section <seealso marker="stdlib:unicode_usage#notes-about-raw-filenames">Notes About Raw Filenames</seealso> in the <c>STDLIB</c> User´s Giude.</p>
+ <p>See also section <seealso marker="stdlib:unicode_usage#notes-about-raw-filenames">Notes About Raw Filenames</seealso> in the STDLIB User's Guide.</p>
</description>
@@ -277,7 +277,7 @@ f.txt: {person, "kalle", 25}.
{ok,[{person,"kalle",25},{person,"pelle",30}]}</pre>
<p>The encoding of <c><anno>Filename</anno></c> can be set
by a comment, as described in
- <seealso marker="stdlib:epp#encoding"><c>stdlib:epp(3)</c></seealso>.</p>
+ <seealso marker="stdlib:epp#encoding"><c>epp(3)</c></seealso>.</p>
</desc>
</func>
<func>
@@ -445,7 +445,7 @@ f.txt: {person, "kalle", 25}.
</taglist>
<p>The encoding of <c><anno>Filename</anno></c> can be set
by a comment, as described in
- <seealso marker="stdlib:epp#encoding"><c>stdlib:epp(3)</c></seealso>.</p>
+ <seealso marker="stdlib:epp#encoding"><c>epp(3)</c></seealso>.</p>
</desc>
</func>
<func>
@@ -455,7 +455,7 @@ f.txt: {person, "kalle", 25}.
<p>The same as <c>eval/1</c>, but the variable bindings
<c><anno>Bindings</anno></c> are used in the evaluation. For information
about the variable bindings, see
- <seealso marker="stdlib:erl_eval"><c>stdlib:erl_eval(3)</c></seealso>.</p>
+ <seealso marker="stdlib:erl_eval"><c>erl_eval(3)</c></seealso>.</p>
</desc>
</func>
<func>
@@ -830,7 +830,7 @@ f.txt: {person, "kalle", 25}.
this module (<c>file</c>) for reading and writing data as the interfaces
provided here work with byte-oriented data. Using other (Unicode)
encodings makes the
- <seealso marker="stdlib:io"><c>stdlib:io(3)</c></seealso> functions
+ <seealso marker="stdlib:io"><c>io(3)</c></seealso> functions
<c>get_chars</c>, <c>get_line</c>, and <c>put_chars</c> more suitable,
as they can work with the full Unicode range.</p>
<p>If data is sent to an <c>io_device()</c> in a format that cannot be
@@ -847,7 +847,7 @@ f.txt: {person, "kalle", 25}.
that is,
<seealso marker="#read/2"><c>read/2</c></seealso> are
returned "as is". If module
- <seealso marker="stdlib:io"><c>stdlib:io(3)</c></seealso> is used for
+ <seealso marker="stdlib:io"><c>io(3)</c></seealso> is used for
writing, the file can only cope with Unicode characters up to code point
255 (the ISO Latin-1 range).</p>
</item>
@@ -861,7 +861,7 @@ f.txt: {person, "kalle", 25}.
the file lies beyond the ISO Latin-1 range (0..255), but failure occurs
if the data contains Unicode code points beyond that range. The file is
best read with the functions in the Unicode aware module
- <seealso marker="stdlib:io"><c>stdlib:io(3)</c></seealso>.</p>
+ <seealso marker="stdlib:io"><c>io(3)</c></seealso>.</p>
<p>Bytes written to the file by any means are translated to UTF-8 encoding
before being stored on the disk file.</p>
</item>
@@ -891,7 +891,7 @@ f.txt: {person, "kalle", 25}.
So a file can be analyzed in latin1 encoding for, for example, a BOM,
positioned beyond the BOM and then be set for the right encoding before
further reading. For functions identifying BOMs, see module
- <seealso marker="stdlib:unicode"><c>stdlib:unicode(3)</c></seealso>. </p>
+ <seealso marker="stdlib:unicode"><c>unicode(3)</c></seealso>. </p>
<p>This option is not allowed on <c>raw</c> files.</p>
</item>
<tag><c>ram</c></tag>
@@ -932,7 +932,7 @@ f.txt: {person, "kalle", 25}.
closed and the process itself is terminated.
An <c><anno>IoDevice</anno></c> returned from this call can be used
as an argument to the I/O functions (see
- <seealso marker="stdlib:io"><c>stdlib:io(3)</c></seealso>).</p>
+ <seealso marker="stdlib:io"><c>io(3)</c></seealso>).</p>
<note>
<p>In previous versions of <c>file</c>, modes were specified
as one of the atoms <c>read</c>, <c>write</c>, or
@@ -1055,7 +1055,7 @@ f.txt: {person, "kalle", 25}.
</taglist>
<p>The encoding of <c><anno>Filename</anno></c> can be set
by a comment as described in
- <seealso marker="stdlib:epp#encoding"><c>stdlib:epp(3)</c></seealso>.</p>
+ <seealso marker="stdlib:epp#encoding"><c>epp(3)</c></seealso>.</p>
</desc>
</func>
<func>
@@ -1128,7 +1128,7 @@ f.txt: {person, "kalle", 25}.
</taglist>
<p>The encoding of <c><anno>Filename</anno></c> can be set
by a comment as described in
- <seealso marker="stdlib:epp#encoding"><c>stdlib:epp(3)</c></seealso>.</p>
+ <seealso marker="stdlib:epp#encoding"><c>epp(3)</c></seealso>.</p>
</desc>
</func>
<func>
@@ -1389,7 +1389,7 @@ f.txt: {person, "kalle", 25}.
<c>{ok, <anno>FileInfo</anno>}</c> if successful, otherwise
<c>{error, <anno>Reason</anno>}</c>. <c><anno>FileInfo</anno></c>
is a record
- <c>file_info</c>, defined in the <c>Kernel</c> include file
+ <c>file_info</c>, defined in the Kernel include file
<c>file.hrl</c>. Include the following directive in the module
from which the function is called:</p>
<code type="none">
@@ -1552,7 +1552,7 @@ f.txt: {person, "kalle", 25}.
raw line-oriented reading.</p>
<p>If <c>encoding</c> is set to something else than <c>latin1</c>, the
<c>read_line/1</c> call fails if the data contains characters larger than 255,
- why module <seealso marker="stdlib:io"><c>stdlib:io(3)</c></seealso> is to be
+ why module <seealso marker="stdlib:io"><c>io(3)</c></seealso> is to be
preferred when reading such a file.</p>
<p>The function returns:</p>
<taglist>
@@ -1970,7 +1970,7 @@ f.txt: {person, "kalle", 25}.
<p>Changes file information. Returns <c>ok</c> if successful,
otherwise <c>{error, <anno>Reason</anno>}</c>.
<c><anno>FileInfo</anno></c> is a record
- <c>file_info</c>, defined in the <c>Kernel</c> include file
+ <c>file_info</c>, defined in the Kernel include file
<c>file.hrl</c>. Include the following directive in the module
from which the function is called:</p>
<code type="none">
diff --git a/lib/kernel/doc/src/gen_tcp.xml b/lib/kernel/doc/src/gen_tcp.xml
index 83242c2df8..08454b9832 100644
--- a/lib/kernel/doc/src/gen_tcp.xml
+++ b/lib/kernel/doc/src/gen_tcp.xml
@@ -216,7 +216,7 @@ do_recv(Sock, Bs) ->
time-out in milliseconds. Defaults to <c>infinity</c>.</p>
<note>
<p>The default values for options specified to <c>connect</c> can
- be affected by the <c>Kernel</c> configuration parameter
+ be affected by the Kernel configuration parameter
<c>inet_default_connect_options</c>. For details, see
<seealso marker="inet"><c>inet(3)</c></seealso>.</p>
</note>
@@ -293,7 +293,7 @@ do_recv(Sock, Bs) ->
<seealso marker="#accept/1"><c>accept/1,2</c></seealso>.</p>
<note>
<p>The default values for options specified to <c>listen</c> can
- be affected by the <c>Kernel</c> configuration parameter
+ be affected by the Kernel configuration parameter
<c>inet_default_listen_options</c>. For details, see
<seealso marker="inet"><c>inet(3)</c></seealso>.</p>
</note>
@@ -307,7 +307,7 @@ do_recv(Sock, Bs) ->
<type_desc variable="HttpPacket">See the description of
<c>HttpPacket</c> in
<seealso marker="erts:erlang#decode_packet/3"><c>erlang:decode_packet/3</c></seealso>
- in <c>ERTS</c>.
+ in ERTS.
</type_desc>
<desc>
<p>Receives a packet from a socket in passive
diff --git a/lib/kernel/doc/src/heart.xml b/lib/kernel/doc/src/heart.xml
index 864f8facac..59a046bf4d 100644
--- a/lib/kernel/doc/src/heart.xml
+++ b/lib/kernel/doc/src/heart.xml
@@ -83,7 +83,7 @@
<c><![CDATA[SIGKILL]]></c>:</p>
<pre>
% <input>erl -heart -env HEART_KILL_SIGNAL SIGABRT ...</input></pre>
- <p> If heart should <b>not</b> kill the Erlang runtime system, this can be indicated
+ <p> If heart should <em>not</em> kill the Erlang runtime system, this can be indicated
using the environment variable <c><![CDATA[HEART_NO_KILL=TRUE]]></c>.
This can be useful if the command executed by heart takes care of this,
for example as part of a specific cleanup sequence.
diff --git a/lib/kernel/doc/src/inet.xml b/lib/kernel/doc/src/inet.xml
index c0dce2f50c..d907cef7d3 100644
--- a/lib/kernel/doc/src/inet.xml
+++ b/lib/kernel/doc/src/inet.xml
@@ -36,7 +36,7 @@
<seealso marker="erts:inet_cfg">ERTS User's Guide:
Inet Configuration</seealso> for more information about how to
configure an Erlang runtime system for IP communication.</p>
- <p>The following two <c>Kernel</c> configuration parameters affect the
+ <p>The following two Kernel configuration parameters affect the
behavior of all sockets opened on an Erlang node:</p>
<list type="bulleted">
<item><p><c>inet_default_connect_options</c> can contain a list of
@@ -48,7 +48,7 @@
<p>When <c>accept</c> is issued, the values of the listening socket options
are inherited. No such application variable is therefore needed for
<c>accept</c>.</p>
- <p>Using the <c>Kernel</c> configuration parameters above, one
+ <p>Using the Kernel configuration parameters above, one
can set default options for all TCP sockets on a node, but use this
with care. Options such as <c>{delay_send,true}</c> can be
specified in this way. The following is an example of starting an Erlang
@@ -95,7 +95,7 @@ fe80::204:acff:fe17:bf38
<datatype>
<name name="hostent"/>
<desc>
- <p>The record is defined in the <c>Kernel</c> include file
+ <p>The record is defined in the Kernel include file
<c>"inet.hrl"</c>.</p>
<p>Add the following directive to the module:</p>
<code>
@@ -151,6 +151,12 @@ fe80::204:acff:fe17:bf38
<name name="socket_address"/>
</datatype>
<datatype>
+ <name name="socket_getopt"/>
+ </datatype>
+ <datatype>
+ <name name="socket_setopt"/>
+ </datatype>
+ <datatype>
<name name="returned_non_ip_address"/>
<desc>
<p>
@@ -327,8 +333,6 @@ fe80::204:acff:fe17:bf38
<func>
<name name="getopts" arity="2"/>
<fsummary>Get one or more options for a socket.</fsummary>
- <type name="socket_getopt"/>
- <type name="socket_setopt"/>
<desc>
<p>Gets one or more options for a socket. For a list of available
options, see
@@ -387,7 +391,7 @@ get_tcpi_sacked(Sock) ->
<<_:28/binary,TcpiSacked:32/native,_/binary>> = Info,
TcpiSacked.]]></code>
<p>Preferably, you would check the machine type, the operating system,
- and the <c>Kernel</c> version before executing anything similar to
+ and the Kernel version before executing anything similar to
this code.</p>
</desc>
</func>
@@ -580,7 +584,6 @@ get_tcpi_sacked(Sock) ->
<func>
<name name="setopts" arity="2"/>
<fsummary>Set one or more options for a socket.</fsummary>
- <type name="socket_setopt"/>
<desc>
<p>Sets one or more options for a socket.</p>
<p>The following options are available:</p>
@@ -658,7 +661,7 @@ get_tcpi_sacked(Sock) ->
<p>The size of the user-level software buffer used by
the driver. Not to be confused with options <c>sndbuf</c>
and <c>recbuf</c>, which correspond to the
- <c>Kernel</c> socket buffers. It is recommended
+ Kernel socket buffers. It is recommended
to have <c>val(buffer) &gt;= max(val(sndbuf),val(recbuf))</c> to
avoid performance issues because of unnecessary copying.
<c>val(buffer)</c> is automatically set to the above
@@ -717,7 +720,7 @@ get_tcpi_sacked(Sock) ->
<p>The socket message queue is set to a busy
state when the amount of data on the message
queue reaches this limit. Notice that this limit only
- concerns data that has not yet reached the <c>ERTS</c> internal
+ concerns data that has not yet reached the ERTS internal
socket implementation. Defaults to 8 kB.</p>
<p>Senders of data to the socket are suspended if
either the socket message queue is busy or the socket
@@ -733,7 +736,7 @@ get_tcpi_sacked(Sock) ->
<tag><c>{high_watermark, Size}</c> (TCP/IP sockets)</tag>
<item>
<p>The socket is set to a busy state when the amount
- of data queued internally by the <c>ERTS</c> socket implementation
+ of data queued internally by the ERTS socket implementation
reaches this limit. Defaults to 8 kB.</p>
<p>Senders of data to the socket are suspended if
either the socket message queue is busy or the socket
@@ -813,7 +816,7 @@ get_tcpi_sacked(Sock) ->
socket message queue is set in a not busy state when
the amount of data queued in the message queue falls
below this limit. Notice that this limit only concerns data
- that has not yet reached the <c>ERTS</c> internal socket
+ that has not yet reached the ERTS internal socket
implementation. Defaults to 4 kB.</p>
<p>Senders that are suspended because of either a
busy message queue or a busy socket are resumed
@@ -831,7 +834,7 @@ get_tcpi_sacked(Sock) ->
<item>
<p>If the socket is in a busy state, the socket is
set in a not busy state when the amount of data
- queued internally by the <c>ERTS</c> socket implementation
+ queued internally by the ERTS socket implementation
falls below this limit. Defaults to 4 kB.</p>
<p>Senders that are suspended because of a
busy message queue or a busy socket are resumed
@@ -951,7 +954,7 @@ setcap cap_sys_admin,cap_sys_ptrace,cap_dac_read_search+epi beam.smp</code>
are returned with the format according to <c>HttpPacket</c>
described in
<seealso marker="erts:erlang#decode_packet/3">
- <c>erlang:decode_packet/3</c></seealso> in <c>ERTS</c>.
+ <c>erlang:decode_packet/3</c></seealso> in ERTS.
A socket in passive
mode returns <c>{ok, HttpPacket}</c> from <c>gen_tcp:recv</c>
while an active socket sends messages like
@@ -1089,6 +1092,15 @@ setcap cap_sys_admin,cap_sys_ptrace,cap_dac_read_search+epi beam.smp</code>
The option is ignored on platforms where it is not
implemented. Use with caution.</p>
</item>
+ <tag><c>{tclass, Integer}</c></tag>
+ <item>
+ <p>
+ Sets <c>IPV6_TCLASS IP</c> level options on platforms
+ where this is implemented. The behavior and allowed range
+ varies between different systems.
+ The option is ignored on platforms where it is not
+ implemented. Use with caution.</p>
+ </item>
</taglist>
<p>In addition to these options, <em>raw</em>
option specifications can be used. The raw options are
@@ -1127,7 +1139,7 @@ inet:setopts(Sock,[{raw,6,8,<<30:32/native>>}]),]]></code>
can respond differently to this kind of option
manipulation. Use with care.</p>
<p>Notice that the default options for TCP/IP sockets can be
- changed with the <c>Kernel</c> configuration parameters mentioned in
+ changed with the Kernel configuration parameters mentioned in
the beginning of this manual page.</p>
</desc>
</func>
diff --git a/lib/kernel/doc/src/init_stub.xml b/lib/kernel/doc/src/init_stub.xml
index df89b174ca..1297c8264d 100644
--- a/lib/kernel/doc/src/init_stub.xml
+++ b/lib/kernel/doc/src/init_stub.xml
@@ -34,6 +34,6 @@
<modulesummary>Coordination of system startup.</modulesummary>
<description>
<p>This module is moved to the
- <seealso marker="erts:init"><c>ERTS</c></seealso> application.</p>
+ <seealso marker="erts:init">ERTS</seealso> application.</p>
</description>
</erlref>
diff --git a/lib/kernel/doc/src/kernel_app.xml b/lib/kernel/doc/src/kernel_app.xml
index 9e6fb60bb7..df681a505f 100644
--- a/lib/kernel/doc/src/kernel_app.xml
+++ b/lib/kernel/doc/src/kernel_app.xml
@@ -31,12 +31,12 @@
<app>kernel</app>
<appsummary>The Kernel application.</appsummary>
<description>
- <p>The <c>Kernel</c> application has all the code necessary to run
+ <p>The Kernel application has all the code necessary to run
the Erlang runtime system: file servers, code servers,
and so on.</p>
- <p>The <c>Kernel</c> application is the first application started. It is
+ <p>The Kernel application is the first application started. It is
mandatory in the sense that the minimal system based on
- Erlang/OTP consists of <c>Kernel</c> and <c>STDLIB</c>. <c>Kernel</c>
+ Erlang/OTP consists of Kernel and STDLIB. Kernel
contains the following functional areas:</p>
<list type="bulleted">
<item>Start, stop, supervision, configuration, and distribution of applications</item>
@@ -53,13 +53,13 @@
<section>
<title>Error Logger Event Handlers</title>
<p>Two standard error logger event handlers are defined in
- the <c>Kernel</c> application. These are described in
+ the Kernel application. These are described in
<seealso marker="error_logger"><c>error_logger(3)</c></seealso>.</p>
</section>
<section>
<title>Configuration</title>
- <p>The following configuration parameters are defined for the <c>Kernel</c>
+ <p>The following configuration parameters are defined for the Kernel
application. For more information about configuration parameters,
see file <seealso marker="app"><c>app(4)</c></seealso>.</p>
<taglist>
@@ -162,8 +162,8 @@
depth to which terms are printed by the error logger event
handlers included in OTP. This
configuration parameter is used by the two event handlers
- defined by the <c>Kernel</c> application and the two event
- handlers in the <c>SASL</c> application.
+ defined by the Kernel application and the two event
+ handlers in the SASL application.
(If you have implemented your own error handlers, this configuration
parameter has no effect on them.)</p>
@@ -173,7 +173,7 @@
<c>~P</c> and <c>~W</c>, respectively, and <c>Depth</c> is
used as the depth parameter. For details, see
<seealso marker="stdlib:io#format/2"><c>io:format/2</c></seealso>
- in <c>STDLIB</c>.</p>
+ in STDLIB.</p>
<note><p>A reasonable starting value for <c>Depth</c> is
<c>30</c>. We recommend to test crashing various processes in your
@@ -217,12 +217,14 @@
</item>
<tag><c>{inet_dist_listen_options, Opts}</c></tag>
<item>
+ <marker id="inet_dist_listen_options"></marker>
<p>Defines a list of extra socket options to be used when opening the
listening socket for a distributed Erlang node.
See <seealso marker="gen_tcp#listen/2"><c>gen_tcp:listen/2</c></seealso>.</p>
</item>
<tag><c>{inet_dist_connect_options, Opts}</c></tag>
<item>
+ <marker id="inet_dist_connect_options"></marker>
<p>Defines a list of extra socket options to be used when connecting to
other distributed Erlang nodes.
See <seealso marker="gen_tcp#connect/4"><c>gen_tcp:connect/4</c></seealso>.</p>
@@ -239,7 +241,7 @@
<p>The name (string) of an Inet user configuration file. For details,
see section
<seealso marker="erts:inet_cfg"><c>Inet Configuration</c></seealso>
- in the <c>ERTS</c> User's Guide.</p>
+ in the ERTS User's Guide.</p>
</item>
<tag><c>net_setuptime = SetupTime</c></tag>
<item>
@@ -358,7 +360,7 @@ MaxT = TickTime + TickTime / 4</code>
<tag><c>start_timer = true | false</c></tag>
<item>
<p>Starts the <c>timer_server</c> if the parameter is
- <c>true</c> (see <seealso marker="stdlib:timer"><c>stdlib:timer(3)</c></seealso>).
+ <c>true</c> (see <seealso marker="stdlib:timer"><c>timer(3)</c></seealso>).
This parameter is to be set to <c>true</c> in an embedded system
using this service.</p>
<p>Defaults to <c>false</c>.</p>
diff --git a/lib/kernel/doc/src/net_kernel.xml b/lib/kernel/doc/src/net_kernel.xml
index f48a534d4f..4e2b0c69db 100644
--- a/lib/kernel/doc/src/net_kernel.xml
+++ b/lib/kernel/doc/src/net_kernel.xml
@@ -55,7 +55,7 @@ $ <input>erl -sname foobar</input></pre>
<seealso marker="erts:erl"><c>erl</c></seealso>.</p>
<p>Normally, connections are established automatically when
another node is referenced. This functionality can be disabled
- by setting <c>Kernel</c> configuration parameter
+ by setting Kernel configuration parameter
<c>dist_auto_connect</c> to <c>false</c>, see
<seealso marker="kernel_app"><c>kernel(6)</c></seealso>. In this case,
connections must be established explicitly by calling
@@ -116,6 +116,21 @@ $ <input>erl -sname foobar</input></pre>
</func>
<func>
+ <name name="getopts" arity="2"/>
+ <fsummary>Get distribution socket options.</fsummary>
+ <desc>
+ <p>Get one or more options for the distribution socket
+ connected to <c><anno>Node</anno></c>.</p>
+ <p>If <c><anno>Node</anno></c> is a connected node
+ the return value is the same as from
+ <seealso marker="inet#getopts/2"><c>inet:getopts(Sock, Options)</c></seealso>
+ where <c>Sock</c> is the distribution socket for <c><anno>Node</anno></c>.</p>
+ <p>Returns <c>ignored</c> if the local node is not alive or
+ <c>{error, noconnection}</c> if <c><anno>Node</anno></c> is not connected.</p>
+ </desc>
+ </func>
+
+ <func>
<name name="monitor_nodes" arity="1"/>
<name name="monitor_nodes" arity="2"/>
<fsummary>Subscribe to node status change messages.</fsummary>
@@ -131,7 +146,7 @@ $ <input>erl -sname foobar</input></pre>
are stopped. Two
option lists are considered the same if they contain the same
set of options.</p>
- <p>As from <c>Kernel</c> version 2.11.4, and <c>ERTS</c> version
+ <p>As from Kernel version 2.11.4, and ERTS version
5.5.4, the following is guaranteed:</p>
<list type="bulleted">
<item><p><c>nodeup</c> messages are delivered before delivery
@@ -141,13 +156,13 @@ $ <input>erl -sname foobar</input></pre>
messages from the remote node that have been passed
through the connection have been delivered.</p></item>
</list>
- <p>Notice that this is <em>not</em> guaranteed for <c>Kernel</c>
+ <p>Notice that this is <em>not</em> guaranteed for Kernel
versions before 2.11.4.</p>
- <p>As from <c>Kernel</c> version 2.11.4, subscriptions can also be
+ <p>As from Kernel version 2.11.4, subscriptions can also be
made before the <c>net_kernel</c> server is started, that is,
<c>net_kernel:monitor_nodes/[1,2]</c> does not return
<c>ignored</c>.</p>
- <p>As from <c>Kernel</c> version 2.13, and <c>ERTS</c> version
+ <p>As from Kernel version 2.13, and ERTS version
5.7, the following is guaranteed:</p>
<list type="bulleted">
<item><p><c>nodeup</c> messages are delivered after the
@@ -157,7 +172,7 @@ $ <input>erl -sname foobar</input></pre>
corresponding node has disappeared in results from
<c>erlang:nodes/X</c>.</p></item>
</list>
- <p>Notice that this is <em>not</em> guaranteed for <c>Kernel</c>
+ <p>Notice that this is <em>not</em> guaranteed for Kernel
versions before 2.13.</p>
<p>The format of the node status change messages depends on
<c><anno>Options</anno></c>. If <c><anno>Options</anno></c> is
@@ -289,6 +304,27 @@ $ <input>erl -sname foobar</input></pre>
</func>
<func>
+ <name name="setopts" arity="2"/>
+ <fsummary>Set distribution socket options.</fsummary>
+ <desc>
+ <p>Set one or more options for distribution sockets.
+ Argument <c><anno>Node</anno></c> can be either one node name
+ or the atom <c>new</c> to affect the distribution sockets of all
+ future connected nodes.</p>
+ <p>The return value is the same as from
+ <seealso marker="inet#setopts/2"><c>inet:setopts/2</c></seealso>
+ or <c>{error, noconnection}</c> if <c><anno>Node</anno></c> is not
+ a connected node or <c>new</c>.</p>
+ <p>If <c><anno>Node</anno></c> is <c>new</c> the <c><anno>Options</anno></c>
+ will then also be added to kernel configration parameters
+ <seealso marker="kernel:kernel_app#inet_dist_listen_options">inet_dist_listen_options</seealso>
+ and
+ <seealso marker="kernel:kernel_app#inet_dist_connect_options">inet_dist_connect_options</seealso>.</p>
+ <p>Returns <c>ignored</c> if the local node is not alive.</p>
+ </desc>
+ </func>
+
+ <func>
<name>start([Name]) -> {ok, pid()} | {error, Reason}</name>
<name>start([Name, NameType]) -> {ok, pid()} | {error, Reason}</name>
<name>start([Name, NameType, Ticktime]) -> {ok, pid()} | {error, Reason}</name>
diff --git a/lib/kernel/doc/src/notes.xml b/lib/kernel/doc/src/notes.xml
index f37433110c..5bcc0b7c09 100644
--- a/lib/kernel/doc/src/notes.xml
+++ b/lib/kernel/doc/src/notes.xml
@@ -31,6 +31,109 @@
</header>
<p>This document describes the changes made to the Kernel application.</p>
+<section><title>Kernel 5.1</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Fix a memory leak when calling
+ seq_trace:get_system_tracer().</p>
+ <p>
+ Own Id: OTP-13742</p>
+ </item>
+ <item>
+ <p>
+ Fix for the problem that when adding the ebin directory
+ of an application to the code path, the
+ <c>code:priv_dir/1</c> function returns an incorrect path
+ to the priv directory of the same application.</p>
+ <p>
+ Own Id: OTP-13758 Aux Id: ERL-195 </p>
+ </item>
+ <item>
+ <p>
+ Fix code_server crash when adding code paths of two
+ levels.</p>
+ <p>
+ Own Id: OTP-13765 Aux Id: ERL-194 </p>
+ </item>
+ <item>
+ <p>
+ Respect -proto_dist switch while connection to EPMD</p>
+ <p>
+ Own Id: OTP-13770 Aux Id: PR-1129 </p>
+ </item>
+ <item>
+ <p>
+ Fixed a bug where init:stop could deadlock if a process
+ with infinite shutdown timeout (e.g. a supervisor)
+ attempted to load code while terminating.</p>
+ <p>
+ Own Id: OTP-13802</p>
+ </item>
+ <item>
+ <p>
+ Close stdin of commands run in os:cmd. This is a
+ backwards compatiblity fix that restores the behaviour of
+ pre 19.0 os:cmd.</p>
+ <p>
+ Own Id: OTP-13867 Aux Id: seq13178 </p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Add <c>net_kernel:setopts/2</c> and
+ <c>net_kernel:getopts/2</c> to control options for
+ distribution sockets in runtime.</p>
+ <p>
+ Own Id: OTP-13564</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Kernel 5.0.2</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ When calling os:cmd from a process that has set trap_exit
+ to true an 'EXIT' message would be left in the message
+ queue. This bug was introduced in kernel vsn 5.0.1.</p>
+ <p>
+ Own Id: OTP-13813</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Kernel 5.0.1</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Fix a os:cmd bug where creating a background job using
+ &amp; would cause os:cmd to hang until the background job
+ terminated or closed its stdout and stderr file
+ descriptors. This bug has existed from kernel 5.0.</p>
+ <p>
+ Own Id: OTP-13741</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Kernel 5.0</title>
<section><title>Fixed Bugs and Malfunctions</title>
@@ -3447,7 +3550,7 @@
types (for instance, <c>ensure_loaded/1</c> now only
accepts an atom as documented; it used to accept a string
too).</p>
- <p><c>Dialyzer</c> will generally emit warnings for any
+ <p>Dialyzer will generally emit warnings for any
calls that use undocumented argument types. Even if the
call happens to still work in R12B, you should correct
your code. A future release will adhere to the
diff --git a/lib/kernel/doc/src/rpc.xml b/lib/kernel/doc/src/rpc.xml
index 8cad9fe4fc..5944e9321a 100644
--- a/lib/kernel/doc/src/rpc.xml
+++ b/lib/kernel/doc/src/rpc.xml
@@ -320,7 +320,7 @@
<fsummary>Information about a process.</fsummary>
<desc>
<p>Location transparent version of the BIF
- <seealso marker="erts:erlang#process_info/1"><c>erlang:process_info/1</c></seealso> in <c>ERTS</c>.</p>
+ <seealso marker="erts:erlang#process_info/1"><c>erlang:process_info/1</c></seealso> in ERTS.</p>
</desc>
</func>
@@ -330,7 +330,7 @@
<fsummary>Information about a process.</fsummary>
<desc>
<p>Location transparent version of the BIF
- <seealso marker="erts:erlang#process_info/2"><c>erlang:process_info/2</c></seealso> in <c>ERTS</c>.</p>
+ <seealso marker="erts:erlang#process_info/2"><c>erlang:process_info/2</c></seealso> in ERTS.</p>
</desc>
</func>
diff --git a/lib/kernel/doc/src/zlib_stub.xml b/lib/kernel/doc/src/zlib_stub.xml
index b111581b10..9ab9c4eb62 100644
--- a/lib/kernel/doc/src/zlib_stub.xml
+++ b/lib/kernel/doc/src/zlib_stub.xml
@@ -34,6 +34,6 @@
<modulesummary>Zlib compression interface.</modulesummary>
<description>
<p>This module is moved to the
- <seealso marker="erts:zlib"><c>ERTS</c></seealso> application.</p>
+ <seealso marker="erts:zlib">ERTS</seealso> application.</p>
</description>
</erlref>
diff --git a/lib/kernel/include/dist_util.hrl b/lib/kernel/include/dist_util.hrl
index 43e50d4325..320e916c04 100644
--- a/lib/kernel/include/dist_util.hrl
+++ b/lib/kernel/include/dist_util.hrl
@@ -63,7 +63,7 @@
f_getll, %% Get low level port or pid.
f_address, %% The address of the "socket",
%% generated from Socket,Node
- %% These two are used in the tick loop,
+ %% These three are used in the tick loop,
%% so they are not fun's to avoid holding old code.
mf_tick, %% Takes the socket as parameters and
%% sends a tick, this is no fun, it
@@ -74,7 +74,11 @@
%% {ok, RecvCnt, SendCnt, SendPend} for
%% a given socket. This is a {M,F},
%% returning {error, Reason on failure}
- request_type = normal
+ request_type = normal,
+
+ %% New in kernel-5.1 (OTP 19.1):
+ mf_setopts, %% netkernel:setopts on active connection
+ mf_getopts %% netkernel:getopts on active connection
}).
diff --git a/lib/kernel/src/code_server.erl b/lib/kernel/src/code_server.erl
index 6174136507..59b26176bf 100644
--- a/lib/kernel/src/code_server.erl
+++ b/lib/kernel/src/code_server.erl
@@ -135,10 +135,14 @@ split_paths([], _S, Path, Paths) ->
-spec call(term()) -> term().
call(Req) ->
+ Ref = erlang:monitor(process, ?MODULE),
?MODULE ! {code_call, self(), Req},
receive
{?MODULE, Reply} ->
- Reply
+ erlang:demonitor(Ref,[flush]),
+ Reply;
+ {'DOWN',Ref,process,_,_} ->
+ exit({'DOWN',code_server,Req})
end.
reply(Pid, Res) ->
@@ -807,7 +811,13 @@ clear_namedb([], _) ->
%% Dir must be a complete pathname (not only a name).
insert_dir(Dir, Db) ->
Splitted = filename:split(Dir),
- Name = get_name_from_splitted(Splitted),
+ case get_name_from_splitted(Splitted) of
+ Name when Name /= "ebin", Name /= "." ->
+ Name;
+ _ ->
+ SplittedAbsName = filename:split(absname(Dir)),
+ Name = get_name_from_splitted(SplittedAbsName)
+ end,
AppDir = filename:join(del_ebin_1(Splitted)),
do_insert_name(Name, AppDir, Db).
@@ -933,15 +943,25 @@ del_ebin(Dir) ->
filename:join(del_ebin_1(filename:split(Dir))).
del_ebin_1([Parent,App,"ebin"]) ->
- Ext = archive_extension(),
- case filename:basename(Parent, Ext) of
- Parent ->
- %% Plain directory.
+ case filename:basename(Parent) of
+ [] ->
+ %% Parent is the root directory
[Parent,App];
- Archive ->
- %% Archive.
- [Archive]
+ _ ->
+ Ext = archive_extension(),
+ case filename:basename(Parent, Ext) of
+ Parent ->
+ %% Plain directory.
+ [Parent,App];
+ Archive ->
+ %% Archive.
+ [Archive]
+ end
end;
+del_ebin_1(Path = [_App,"ebin"]) ->
+ del_ebin_1(filename:split(absname(filename:join(Path))));
+del_ebin_1(["ebin"]) ->
+ del_ebin_1(filename:split(absname("ebin")));
del_ebin_1([H|T]) ->
[H|del_ebin_1(T)];
del_ebin_1([]) ->
@@ -1372,11 +1392,10 @@ finish_on_load(PidRef, OnLoadRes, #state{on_load=OnLoad0}=St0) ->
finish_on_load_1(Mod, OnLoadRes, Waiting, St) ->
Keep = OnLoadRes =:= ok,
- erlang:finish_after_on_load(Mod, Keep),
+ erts_code_purger:finish_after_on_load(Mod, Keep),
Res = case Keep of
false ->
_ = finish_on_load_report(Mod, OnLoadRes),
- _ = erts_code_purger:purge(Mod),
{error,on_load_failure};
true ->
{module,Mod}
diff --git a/lib/kernel/src/dist_util.erl b/lib/kernel/src/dist_util.erl
index 47d0c1b861..8d2fc4d4b7 100644
--- a/lib/kernel/src/dist_util.erl
+++ b/lib/kernel/src/dist_util.erl
@@ -143,7 +143,11 @@ handshake_other_started(#hs_data{request_type=ReqType}=HSData0) ->
ChallengeB = recv_challenge_reply(HSData, ChallengeA, MyCookie),
send_challenge_ack(HSData, gen_digest(ChallengeB, HisCookie)),
?debug({dist_util, self(), accept_connection, Node}),
- connection(HSData).
+ connection(HSData);
+
+handshake_other_started(OldHsData) when element(1,OldHsData) =:= hs_data ->
+ handshake_other_started(convert_old_hsdata(OldHsData)).
+
%%
%% check if connecting node is allowed to connect
@@ -330,7 +334,20 @@ handshake_we_started(#hs_data{request_type=ReqType,
gen_digest(ChallengeA,HisCookie)),
reset_timer(NewHSData#hs_data.timer),
recv_challenge_ack(NewHSData, MyChallenge, MyCookie),
- connection(NewHSData).
+ connection(NewHSData);
+
+handshake_we_started(OldHsData) when element(1,OldHsData) =:= hs_data ->
+ handshake_we_started(convert_old_hsdata(OldHsData)).
+
+convert_old_hsdata({hs_data, KP, ON, TN, S, T, TF, A, OV, OF, OS, FS, FR,
+ FS_PRE, FS_POST, FG, FA, MFT, MFG, RT}) ->
+ #hs_data{
+ kernel_pid = KP, other_node = ON, this_node = TN, socket = S, timer = T,
+ this_flags = TF, allowed = A, other_version = OV, other_flags = OF,
+ other_started = OS, f_send = FS, f_recv = FR, f_setopts_pre_nodeup = FS_PRE,
+ f_setopts_post_nodeup = FS_POST, f_getll = FG, f_address = FA,
+ mf_tick = MFT, mf_getstat = MFG, request_type = RT}.
+
%% --------------------------------------------------------------
%% The connection has been established.
@@ -350,15 +367,15 @@ connection(#hs_data{other_node = Node,
mark_nodeup(HSData,Address),
case FPostNodeup(Socket) of
ok ->
- con_loop(HSData#hs_data.kernel_pid,
- Node,
- Socket,
- Address,
- HSData#hs_data.this_node,
- PType,
- #tick{},
- HSData#hs_data.mf_tick,
- HSData#hs_data.mf_getstat);
+ con_loop({HSData#hs_data.kernel_pid,
+ Node,
+ Socket,
+ PType,
+ HSData#hs_data.mf_tick,
+ HSData#hs_data.mf_getstat,
+ HSData#hs_data.mf_setopts,
+ HSData#hs_data.mf_getopts},
+ #tick{});
_ ->
?shutdown2(Node, connection_setup_failed)
end;
@@ -454,8 +471,8 @@ mark_nodeup(#hs_data{kernel_pid = Kernel,
?shutdown(Node)
end.
-con_loop(Kernel, Node, Socket, TcpAddress,
- MyNode, Type, Tick, MFTick, MFGetstat) ->
+con_loop({Kernel, Node, Socket, Type, MFTick, MFGetstat, MFSetOpts, MFGetOpts}=ConData,
+ Tick) ->
receive
{tcp_closed, Socket} ->
?shutdown2(Node, connection_closed);
@@ -468,15 +485,12 @@ con_loop(Kernel, Node, Socket, TcpAddress,
_ ->
ignore_it
end,
- con_loop(Kernel, Node, Socket, TcpAddress, MyNode, Type,
- Tick, MFTick, MFGetstat);
+ con_loop(ConData, Tick);
{Kernel, tick} ->
case send_tick(Socket, Tick, Type,
MFTick, MFGetstat) of
{ok, NewTick} ->
- con_loop(Kernel, Node, Socket, TcpAddress,
- MyNode, Type, NewTick, MFTick,
- MFGetstat);
+ con_loop(ConData, NewTick);
{error, not_responding} ->
error_msg("** Node ~p not responding **~n"
"** Removing (timedout) connection **~n",
@@ -489,13 +503,24 @@ con_loop(Kernel, Node, Socket, TcpAddress,
case MFGetstat(Socket) of
{ok, Read, Write, _} ->
From ! {self(), get_status, {ok, Read, Write}},
- con_loop(Kernel, Node, Socket, TcpAddress,
- MyNode,
- Type, Tick,
- MFTick, MFGetstat);
+ con_loop(ConData, Tick);
_ ->
?shutdown2(Node, get_status_failed)
- end
+ end;
+ {From, Ref, {setopts, Opts}} ->
+ Ret = case MFSetOpts of
+ undefined -> {error, enotsup};
+ _ -> MFSetOpts(Socket, Opts)
+ end,
+ From ! {Ref, Ret},
+ con_loop(ConData, Tick);
+ {From, Ref, {getopts, Opts}} ->
+ Ret = case MFGetOpts of
+ undefined -> {error, enotsup};
+ _ -> MFGetOpts(Socket, Opts)
+ end,
+ From ! {Ref, Ret},
+ con_loop(ConData, Tick)
end.
diff --git a/lib/kernel/src/erl_epmd.erl b/lib/kernel/src/erl_epmd.erl
index f8ef4a475d..7bc9e2ede3 100644
--- a/lib/kernel/src/erl_epmd.erl
+++ b/lib/kernel/src/erl_epmd.erl
@@ -103,6 +103,10 @@ names(EpmdAddr) ->
register_node(Name, PortNo) ->
register_node(Name, PortNo, inet).
+register_node(Name, PortNo, inet_tcp) ->
+ register_node(Name, PortNo, inet);
+register_node(Name, PortNo, inet6_tcp) ->
+ register_node(Name, PortNo, inet6);
register_node(Name, PortNo, Family) ->
gen_server:call(erl_epmd, {register, Name, PortNo, Family}, infinity).
@@ -403,8 +407,6 @@ select_best_version(L1, _H1, _L2, H2) when L1 > H2 ->
0;
select_best_version(_L1, H1, L2, _H2) when L2 > H1 ->
0;
-select_best_version(_L1, H1, L2, _H2) when L2 > H1 ->
- 0;
select_best_version(_L1, H1, _L2, H2) ->
erlang:min(H1, H2).
diff --git a/lib/kernel/src/inet.erl b/lib/kernel/src/inet.erl
index a91a6ed517..f5c13ecdd7 100644
--- a/lib/kernel/src/inet.erl
+++ b/lib/kernel/src/inet.erl
@@ -75,6 +75,7 @@
-export_type([address_family/0, hostent/0, hostname/0, ip4_address/0,
ip6_address/0, ip_address/0, port_number/0,
local_address/0, socket_address/0, returned_non_ip_address/0,
+ socket_setopt/0, socket_getopt/0,
posix/0, socket/0, stat_option/0]).
%% imports
-import(lists, [append/1, duplicate/2, filter/2, foldl/3]).
@@ -676,7 +677,7 @@ parse_strict_address(Addr) ->
%% Return a list of available options
options() ->
[
- tos, priority, reuseaddr, keepalive, dontroute, linger,
+ tos, tclass, priority, reuseaddr, keepalive, dontroute, linger,
broadcast, sndbuf, recbuf, nodelay, ipv6_v6only,
buffer, header, active, packet, deliver, mode,
multicast_if, multicast_ttl, multicast_loop,
@@ -697,7 +698,7 @@ stats() ->
%% Available options for tcp:connect
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
connect_options() ->
- [tos, priority, reuseaddr, keepalive, linger, sndbuf, recbuf, nodelay,
+ [tos, tclass, priority, reuseaddr, keepalive, linger, sndbuf, recbuf, nodelay,
header, active, packet, packet_size, buffer, mode, deliver, line_delimiter,
exit_on_close, high_watermark, low_watermark, high_msgq_watermark,
low_msgq_watermark, send_timeout, send_timeout_close, delay_send, raw,
@@ -765,7 +766,7 @@ con_add(Name, Val, #connect_opts{} = R, Opts, AllOpts) ->
%% Available options for tcp:listen
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
listen_options() ->
- [tos, priority, reuseaddr, keepalive, linger, sndbuf, recbuf, nodelay,
+ [tos, tclass, priority, reuseaddr, keepalive, linger, sndbuf, recbuf, nodelay,
header, active, packet, buffer, mode, deliver, backlog, ipv6_v6only,
exit_on_close, high_watermark, low_watermark, high_msgq_watermark,
low_msgq_watermark, send_timeout, send_timeout_close, delay_send,
@@ -845,7 +846,7 @@ tcp_module_1(Opts, Address) ->
%% Available options for udp:open
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
udp_options() ->
- [tos, priority, reuseaddr, sndbuf, recbuf, header, active, buffer, mode,
+ [tos, tclass, priority, reuseaddr, sndbuf, recbuf, header, active, buffer, mode,
deliver, ipv6_v6only,
broadcast, dontroute, multicast_if, multicast_ttl, multicast_loop,
add_membership, drop_membership, read_packets,raw,
@@ -916,7 +917,7 @@ udp_module(Opts) ->
% (*) passing of open FDs ("fdopen") is not supported.
sctp_options() ->
[ % The following are generic inet options supported for SCTP sockets:
- mode, active, buffer, tos, priority, dontroute, reuseaddr, linger, sndbuf,
+ mode, active, buffer, tos, tclass, priority, dontroute, reuseaddr, linger, sndbuf,
recbuf, ipv6_v6only, high_msgq_watermark, low_msgq_watermark,
% Other options are SCTP-specific (though they may be similar to their
diff --git a/lib/kernel/src/inet6_tcp_dist.erl b/lib/kernel/src/inet6_tcp_dist.erl
index 3aa61973af..9b6c2745d5 100644
--- a/lib/kernel/src/inet6_tcp_dist.erl
+++ b/lib/kernel/src/inet6_tcp_dist.erl
@@ -24,6 +24,7 @@
-export([listen/1, accept/1, accept_connection/5,
setup/5, close/1, select/1, is_node_name/1]).
+-export([setopts/2, getopts/2]).
%% ------------------------------------------------------------
%% Select this protocol based on node name
@@ -72,3 +73,9 @@ close(Socket) ->
is_node_name(Node) when is_atom(Node) ->
inet_tcp_dist:is_node_name(Node).
+
+setopts(S, Opts) ->
+ inet_tcp_dist:setopts(S, Opts).
+
+getopts(S, Opts) ->
+ inet_tcp_dist:getopts(S, Opts).
diff --git a/lib/kernel/src/inet_int.hrl b/lib/kernel/src/inet_int.hrl
index c8a8962e78..4e8f59a3b9 100644
--- a/lib/kernel/src/inet_int.hrl
+++ b/lib/kernel/src/inet_int.hrl
@@ -153,6 +153,7 @@
-define(INET_LOPT_NETNS, 38).
-define(INET_LOPT_TCP_SHOW_ECONNRESET, 39).
-define(INET_LOPT_LINE_DELIM, 40).
+-define(INET_OPT_TCLASS, 41).
% Specific SCTP options: separate range:
-define(SCTP_OPT_RTOINFO, 100).
-define(SCTP_OPT_ASSOCINFO, 101).
diff --git a/lib/kernel/src/inet_tcp_dist.erl b/lib/kernel/src/inet_tcp_dist.erl
index f91d7ef7c3..3084bd599a 100644
--- a/lib/kernel/src/inet_tcp_dist.erl
+++ b/lib/kernel/src/inet_tcp_dist.erl
@@ -24,13 +24,16 @@
-export([listen/1, accept/1, accept_connection/5,
setup/5, close/1, select/1, is_node_name/1]).
+%% Optional
+-export([setopts/2, getopts/2]).
+
%% Generalized dist API
-export([gen_listen/2, gen_accept/2, gen_accept_connection/6,
gen_setup/6, gen_select/2]).
%% internal exports
--export([accept_loop/3,do_accept/7,do_setup/7,getstat/1]).
+-export([accept_loop/3,do_accept/7,do_setup/7,getstat/1,tick/2]).
-import(error_logger,[error_msg/2]).
@@ -74,7 +77,7 @@ gen_listen(Driver, Name) ->
TcpAddress = get_tcp_address(Driver, Socket),
{_,Port} = TcpAddress#net_address.address,
ErlEpmd = net_kernel:epmd_module(),
- case ErlEpmd:register_node(Name, Port) of
+ case ErlEpmd:register_node(Name, Port, Driver) of
{ok, Creation} ->
{ok, {Socket, TcpAddress, Creation}};
Error ->
@@ -215,8 +218,10 @@ do_accept(Driver, Kernel, AcceptPid, Socket, MyNode, Allowed, SetupTime) ->
inet:getll(S)
end,
f_address = fun(S, Node) -> get_remote_id(Driver, S, Node) end,
- mf_tick = fun(S) -> tick(Driver, S) end,
- mf_getstat = fun ?MODULE:getstat/1
+ mf_tick = fun(S) -> ?MODULE:tick(Driver, S) end,
+ mf_getstat = fun ?MODULE:getstat/1,
+ mf_setopts = fun ?MODULE:setopts/2,
+ mf_getopts = fun ?MODULE:getopts/2
},
dist_util:handshake_other_started(HSData);
{false,IP} ->
@@ -320,6 +325,7 @@ do_setup(Driver, Kernel, Node, Type, MyNode, LongOrShortNames, SetupTime) ->
{packet, 4},
nodelay()])
end,
+
f_getll = fun inet:getll/1,
f_address =
fun(_,_) ->
@@ -329,9 +335,11 @@ do_setup(Driver, Kernel, Node, Type, MyNode, LongOrShortNames, SetupTime) ->
protocol = tcp,
family = AddressFamily}
end,
- mf_tick = fun(S) -> tick(Driver, S) end,
+ mf_tick = fun(S) -> ?MODULE:tick(Driver, S) end,
mf_getstat = fun ?MODULE:getstat/1,
- request_type = Type
+ request_type = Type,
+ mf_setopts = fun ?MODULE:setopts/2,
+ mf_getopts = fun ?MODULE:getopts/2
},
dist_util:handshake_we_started(HSData);
_ ->
@@ -492,3 +500,12 @@ split_stat([], R, W, P) ->
{ok, R, W, P}.
+setopts(S, Opts) ->
+ case [Opt || {K,_}=Opt <- Opts,
+ K =:= active orelse K =:= deliver orelse K =:= packet] of
+ [] -> inet:setopts(S,Opts);
+ Opts1 -> {error, {badopts,Opts1}}
+ end.
+
+getopts(S, Opts) ->
+ inet:getopts(S, Opts).
diff --git a/lib/kernel/src/kernel.appup.src b/lib/kernel/src/kernel.appup.src
index d16e200cb3..82cf73cbda 100644
--- a/lib/kernel/src/kernel.appup.src
+++ b/lib/kernel/src/kernel.appup.src
@@ -18,9 +18,9 @@
%% %CopyrightEnd%
{"%VSN%",
%% Up from - max one major revision back
- [{<<"5\\.0(\\.[0-9]+)*">>,[restart_new_emulator]}, % OTP-19.*
+ [{<<"5\\.[0-1](\\.[0-9]+)*">>,[restart_new_emulator]}, % OTP-19.*
{<<"4\\.[0-2](\\.[0-9]+)*">>,[restart_new_emulator]}], % OTP-18.*
%% Down to - max one major revision back
- [{<<"5\\.0(\\.[0-9]+)*">>,[restart_new_emulator]}, % OTP-19.*
+ [{<<"5\\.[0-1](\\.[0-9]+)*">>,[restart_new_emulator]}, % OTP-19.*
{<<"4\\.[0-2](\\.[0-9]+)*">>,[restart_new_emulator]}] % OTP-18.*
}.
diff --git a/lib/kernel/src/net_kernel.erl b/lib/kernel/src/net_kernel.erl
index ac19f4935b..0c679e7349 100644
--- a/lib/kernel/src/net_kernel.erl
+++ b/lib/kernel/src/net_kernel.erl
@@ -59,6 +59,8 @@
connect_node/1,
monitor_nodes/1,
monitor_nodes/2,
+ setopts/2,
+ getopts/2,
start/1,
stop/0]).
@@ -111,7 +113,7 @@
}).
-record(listen, {
- listen, %% listen pid
+ listen, %% listen socket
accept, %% accepting pid
address, %% #net_address
module %% proto module
@@ -384,7 +386,7 @@ init({Name, LongOrShortNames, TickT, CleanHalt}) ->
connections =
ets:new(sys_dist,[named_table,
protected,
- {keypos, 2}]),
+ {keypos, #connection.node}]),
listen = Listeners,
allowed = [],
verbose = 0
@@ -554,6 +556,38 @@ handle_call({new_ticktime,_T,_TP},
#state{tick = #tick_change{time = T}} = State) ->
async_reply({reply, {ongoing_change_to, T}, State}, From);
+handle_call({setopts, new, Opts}, From, State) ->
+ Ret = setopts_new(Opts, State),
+ async_reply({reply, Ret, State}, From);
+
+handle_call({setopts, Node, Opts}, From, State) ->
+ Return =
+ case ets:lookup(sys_dist, Node) of
+ [Conn] when Conn#connection.state =:= up ->
+ case call_owner(Conn#connection.owner, {setopts, Opts}) of
+ {ok, Ret} -> Ret;
+ _ -> {error, noconnection}
+ end;
+
+ _ ->
+ {error, noconnection}
+ end,
+ async_reply({reply, Return, State}, From);
+
+handle_call({getopts, Node, Opts}, From, State) ->
+ Return =
+ case ets:lookup(sys_dist, Node) of
+ [Conn] when Conn#connection.state =:= up ->
+ case call_owner(Conn#connection.owner, {getopts, Opts}) of
+ {ok, Ret} -> Ret;
+ _ -> {error, noconnection}
+ end;
+
+ _ ->
+ {error, noconnection}
+ end,
+ async_reply({reply, Return, State}, From);
+
handle_call(_Msg, _From, State) ->
{noreply, State}.
@@ -1608,3 +1642,93 @@ async_gen_server_reply(From, Msg) ->
{'EXIT', _} ->
ok
end.
+
+call_owner(Owner, Msg) ->
+ Mref = monitor(process, Owner),
+ Owner ! {self(), Mref, Msg},
+ receive
+ {Mref, Reply} ->
+ erlang:demonitor(Mref, [flush]),
+ {ok, Reply};
+ {'DOWN', Mref, _, _, _} ->
+ error
+ end.
+
+
+-spec setopts(Node, Options) -> ok | {error, Reason} | ignored when
+ Node :: node() | new,
+ Options :: [inet:socket_setopt()],
+ Reason :: inet:posix() | noconnection.
+
+setopts(Node, Opts) when is_atom(Node), is_list(Opts) ->
+ request({setopts, Node, Opts}).
+
+setopts_new(Opts, State) ->
+ %% First try setopts on listening socket(s)
+ %% Bail out on failure.
+ %% If successful, we are pretty sure Opts are ok
+ %% and we continue with config params and pending connections.
+ case setopts_on_listen(Opts, State#state.listen) of
+ ok ->
+ setopts_new_1(Opts);
+ Fail -> Fail
+ end.
+
+setopts_on_listen(_, []) -> ok;
+setopts_on_listen(Opts, [#listen {listen = LSocket, module = Mod} | T]) ->
+ try Mod:setopts(LSocket, Opts) of
+ ok ->
+ setopts_on_listen(Opts, T);
+ Fail -> Fail
+ catch
+ error:undef -> {error, enotsup}
+ end.
+
+setopts_new_1(Opts) ->
+ ConnectOpts = case application:get_env(kernel, inet_dist_connect_options) of
+ {ok, CO} -> CO;
+ _ -> []
+ end,
+ application:set_env(kernel, inet_dist_connect_options,
+ merge_opts(Opts,ConnectOpts)),
+ ListenOpts = case application:get_env(kernel, inet_dist_listen_options) of
+ {ok, LO} -> LO;
+ _ -> []
+ end,
+ application:set_env(kernel, inet_dist_listen_options,
+ merge_opts(Opts, ListenOpts)),
+ case lists:keyfind(nodelay, 1, Opts) of
+ {nodelay, ND} when is_boolean(ND) ->
+ application:set_env(kernel, dist_nodelay, ND);
+ _ -> ignore
+ end,
+
+ %% Update any pending connections
+ PendingConns = ets:select(sys_dist, [{'_',
+ [{'=/=',{element,#connection.state,'$_'},up}],
+ ['$_']}]),
+ lists:foreach(fun(#connection{state = pending, owner = Owner}) ->
+ call_owner(Owner, {setopts, Opts});
+ (#connection{state = up_pending, pending_owner = Owner}) ->
+ call_owner(Owner, {setopts, Opts});
+ (_) -> ignore
+ end, PendingConns),
+ ok.
+
+merge_opts([], B) ->
+ B;
+merge_opts([H|T], B0) ->
+ {Key, _} = H,
+ B1 = lists:filter(fun({K,_}) -> K =/= Key end, B0),
+ merge_opts(T, [H | B1]).
+
+-spec getopts(Node, Options) ->
+ {'ok', OptionValues} | {'error', Reason} | ignored when
+ Node :: node(),
+ Options :: [inet:socket_getopt()],
+ OptionValues :: [inet:socket_setopt()],
+ Reason :: inet:posix() | noconnection.
+
+getopts(Node, Opts) when is_atom(Node), is_list(Opts) ->
+ request({getopts, Node, Opts}).
+
diff --git a/lib/kernel/src/os.erl b/lib/kernel/src/os.erl
index f0ad26b1f2..f8519d3a5e 100644
--- a/lib/kernel/src/os.erl
+++ b/lib/kernel/src/os.erl
@@ -226,11 +226,13 @@ extensions() ->
Command :: atom() | io_lib:chars().
cmd(Cmd) ->
validate(Cmd),
- {SpawnCmd, SpawnOpts, SpawnInput} = mk_cmd(os:type(), Cmd),
+ {SpawnCmd, SpawnOpts, SpawnInput, Eot} = mk_cmd(os:type(), Cmd),
Port = open_port({spawn, SpawnCmd}, [binary, stderr_to_stdout,
- stream, in, eof, hide | SpawnOpts]),
+ stream, in, hide | SpawnOpts]),
+ MonRef = erlang:monitor(port, Port),
true = port_command(Port, SpawnInput),
- Bytes = get_data(Port, []),
+ Bytes = get_data(Port, MonRef, Eot, []),
+ demonitor(MonRef, [flush]),
String = unicode:characters_to_list(Bytes),
if %% Convert to unicode list if possible otherwise return bytes
is_list(String) -> String;
@@ -243,7 +245,7 @@ mk_cmd({win32,Wtype}, Cmd) ->
{false,_} -> lists:concat(["cmd /c", Cmd]);
{Cspec,_} -> lists:concat([Cspec," /c",Cmd])
end,
- {Command, [], []};
+ {Command, [], [], <<>>};
mk_cmd(OsType,Cmd) when is_atom(Cmd) ->
mk_cmd(OsType, atom_to_list(Cmd));
mk_cmd(_,Cmd) ->
@@ -252,7 +254,20 @@ mk_cmd(_,Cmd) ->
{"/bin/sh -s unix:cmd", [out],
%% We insert a new line after the command, in case the command
%% contains a comment character.
- ["(", unicode:characters_to_binary(Cmd), "\n); exit\n"]}.
+ %%
+ %% The </dev/null closes stdin, which means that programs
+ %% that use a closed stdin as an termination indicator works.
+ %% An example of such a program is 'more'.
+ %%
+ %% The "echo ^D" is used to indicate that the program has executed
+ %% and we should return any output we have gotten. We cannot use
+ %% termination of the child or closing of stdin/stdout as then
+ %% starting background jobs from os:cmd will block os:cmd.
+ %%
+ %% I tried changing this to be "better", but got bombarded with
+ %% backwards incompatibility bug reports, so leave this as it is.
+ ["(", unicode:characters_to_binary(Cmd), "\n) </dev/null; echo \"\^D\"\n"],
+ <<$\^D>>}.
validate(Atom) when is_atom(Atom) ->
ok;
@@ -267,21 +282,44 @@ validate1([List|Rest]) when is_list(List) ->
validate1([]) ->
ok.
-get_data(Port, Sofar) ->
+get_data(Port, MonRef, Eot, Sofar) ->
receive
{Port, {data, Bytes}} ->
- get_data(Port, [Sofar,Bytes]);
- {Port, eof} ->
- Port ! {self(), close},
- receive
- {Port, closed} ->
- true
- end,
- receive
- {'EXIT', Port, _} ->
- ok
- after 1 -> % force context switch
- ok
- end,
+ case eot(Bytes, Eot) of
+ more ->
+ get_data(Port, MonRef, Eot, [Sofar,Bytes]);
+ Last ->
+ Port ! {self(), close},
+ flush_until_closed(Port),
+ flush_exit(Port),
+ iolist_to_binary([Sofar, Last])
+ end;
+ {'DOWN', MonRef, _, _ , _} ->
+ flush_exit(Port),
iolist_to_binary(Sofar)
end.
+
+eot(_Bs, <<>>) ->
+ more;
+eot(Bs, Eot) ->
+ case binary:match(Bs, Eot) of
+ nomatch -> more;
+ {Pos, _} ->
+ binary:part(Bs,{0, Pos})
+ end.
+
+flush_until_closed(Port) ->
+ receive
+ {Port, {data, _Bytes}} ->
+ flush_until_closed(Port);
+ {Port, closed} ->
+ true
+ end.
+
+flush_exit(Port) ->
+ receive
+ {'EXIT', Port, _} ->
+ ok
+ after 1 -> % force context switch
+ ok
+ end.
diff --git a/lib/kernel/test/code_SUITE.erl b/lib/kernel/test/code_SUITE.erl
index 8da67c89f8..c5167efa56 100644
--- a/lib/kernel/test/code_SUITE.erl
+++ b/lib/kernel/test/code_SUITE.erl
@@ -36,6 +36,7 @@
code_archive/1, code_archive2/1, on_load/1, on_load_binary/1,
on_load_embedded/1, on_load_errors/1, on_load_update/1,
on_load_purge/1, on_load_self_call/1, on_load_pending/1,
+ on_load_deleted/1,
big_boot_embedded/1,
native_early_modules/1, get_mode/1,
normalized_paths/1]).
@@ -66,6 +67,7 @@ all() ->
bad_erl_libs, code_archive, code_archive2, on_load,
on_load_binary, on_load_embedded, on_load_errors, on_load_update,
on_load_purge, on_load_self_call, on_load_pending,
+ on_load_deleted,
big_boot_embedded, native_early_modules, get_mode, normalized_paths].
groups() ->
@@ -1602,6 +1604,98 @@ on_load_pending(_Config) ->
ok = Mod:t(),
ok.
+on_load_deleted(_Config) ->
+ Mod = ?FUNCTION_NAME,
+
+ R0 = fun() ->
+ Tree = ?Q(["-module('@Mod@').\n",
+ "-on_load(f/0).\n",
+ "f() -> ok.\n"]),
+ merl:print(Tree),
+ {ok,Mod,Code} = merl:compile(Tree),
+ {module,Mod} = code:load_binary(Mod, "", Code)
+ end,
+ delete_before_reload(Mod, R0),
+ delete_before_reload(Mod, R0),
+
+ R1 = fun() ->
+ Tree = ?Q(["-module('@Mod@').\n",
+ "-on_load(f/0).\n",
+ "f() -> fail.\n"]),
+ merl:print(Tree),
+ {ok,Mod,Code} = merl:compile(Tree),
+ {error,on_load_failure} = code:load_binary(Mod, "", Code)
+ end,
+ delete_before_reload(Mod, R1),
+ delete_before_reload(Mod, R1),
+
+ OtherMod = list_to_atom(lists:concat([Mod,"_42"])),
+ OtherTree = ?Q(["-module('@OtherMod@').\n"]),
+ merl:print(OtherTree),
+ {ok,OtherMod,OtherCode} = merl:compile(OtherTree),
+
+ R2 = fun() ->
+ RegName = 'on_load__registered_name',
+ Tree = ?Q(["-module('@Mod@').\n",
+ "-on_load(f/0).\n",
+ "f() ->\n",
+ " register('@RegName@', self()),\n",
+ " receive _ -> ok end.\n"]),
+ merl:print(Tree),
+ {ok,Mod,Code} = merl:compile(Tree),
+ spawn(fun() ->
+ {module,Mod} = code:load_binary(Mod, "", Code)
+ end),
+ receive after 1 -> ok end,
+ {module,OtherMod} = code:load_binary(OtherMod, "",
+ OtherCode),
+ RegName ! stop
+ end,
+ delete_before_reload(Mod, R2),
+
+ ok.
+
+delete_before_reload(Mod, Reload) ->
+ false = check_old_code(Mod),
+
+ Tree1 = ?Q(["-module('@Mod@').\n",
+ "-export([f/1]).\n",
+ "f(Parent) ->\n",
+ " register('@Mod@', self()),\n",
+ " Parent ! started,\n",
+ " receive _ -> ok end.\n"]),
+ merl:print(Tree1),
+ {ok,Mod,Code1} = merl:compile(Tree1),
+
+ Self = self(),
+ spawn(fun() ->
+ {module,Mod} = code:load_binary(Mod, "", Code1),
+ Mod:f(Self)
+ end),
+ receive started -> ok end,
+
+ true = code:delete(Mod),
+ true = check_old_code(Mod),
+
+ Reload(),
+
+ %% When loading the the module with the -on_load() function,
+ %% the reference to the old code would be lost. Make sure that
+ %% the old code is remembered and is still preventing the
+ %% purge.
+ false = code:soft_purge(Mod),
+
+ %% Get rid of the old code.
+ Mod ! stop,
+ receive after 1 -> ok end,
+ true = code:soft_purge(Mod),
+
+ %% Unload the version of the module with the -on_load() function.
+ true = code:delete(Mod),
+ true = code:soft_purge(Mod),
+
+ ok.
+
%% Test that the native code of early loaded modules is loaded.
native_early_modules(Config) when is_list(Config) ->
diff --git a/lib/kernel/test/erl_distribution_SUITE.erl b/lib/kernel/test/erl_distribution_SUITE.erl
index eb58e92224..e43be77428 100644
--- a/lib/kernel/test/erl_distribution_SUITE.erl
+++ b/lib/kernel/test/erl_distribution_SUITE.erl
@@ -25,6 +25,7 @@
init_per_group/2,end_per_group/2]).
-export([tick/1, tick_change/1, illegal_nodenames/1, hidden_node/1,
+ setopts/1,
table_waste/1, net_setuptime/1,
inet_dist_options_options/1,
@@ -42,6 +43,8 @@
-export([get_socket_priorities/0,
tick_cli_test/1, tick_cli_test1/1,
tick_serv_test/2, tick_serv_test1/1,
+ run_remote_test/1,
+ setopts_do/2,
keep_conn/1, time_ping/1]).
-export([init_per_testcase/2, end_per_testcase/2]).
@@ -66,6 +69,7 @@ suite() ->
all() ->
[tick, tick_change, illegal_nodenames, hidden_node,
+ setopts,
table_waste, net_setuptime, inet_dist_options_options,
{group, monitor_nodes}].
@@ -282,6 +286,165 @@ tick_cli_test1(Node) ->
end
end.
+setopts(Config) when is_list(Config) ->
+ register(setopts_regname, self()),
+ [N1,N2,N3,N4] = get_nodenames(4, setopts),
+
+ {_N1F,Port1} = start_node_unconnected(N1, ?MODULE, run_remote_test,
+ ["setopts_do", atom_to_list(node()), "1", "ping"]),
+ 0 = wait_for_port_exit(Port1),
+
+ {_N2F,Port2} = start_node_unconnected(N2, ?MODULE, run_remote_test,
+ ["setopts_do", atom_to_list(node()), "2", "ping"]),
+ 0 = wait_for_port_exit(Port2),
+
+ {ok, LSock} = gen_tcp:listen(0, [{packet,2}, {active,false}]),
+ {ok, LTcpPort} = inet:port(LSock),
+
+ {N3F,Port3} = start_node_unconnected(N3, ?MODULE, run_remote_test,
+ ["setopts_do", atom_to_list(node()),
+ "1", integer_to_list(LTcpPort)]),
+ wait_and_connect(LSock, N3F, Port3),
+ 0 = wait_for_port_exit(Port3),
+
+ {N4F,Port4} = start_node_unconnected(N4, ?MODULE, run_remote_test,
+ ["setopts_do", atom_to_list(node()),
+ "2", integer_to_list(LTcpPort)]),
+ wait_and_connect(LSock, N4F, Port4),
+ 0 = wait_for_port_exit(Port4),
+
+ ok.
+
+wait_and_connect(LSock, NodeName, NodePort) ->
+ {ok, Sock} = gen_tcp:accept(LSock),
+ {ok, "Connect please"} = gen_tcp:recv(Sock, 0),
+ flush_from_port(NodePort),
+ pong = net_adm:ping(NodeName),
+ gen_tcp:send(Sock, "Connect done"),
+ gen_tcp:close(Sock).
+
+
+flush_from_port(Port) ->
+ flush_from_port(Port, 10).
+
+flush_from_port(Port, Timeout) ->
+ receive
+ {Port,{data,String}} ->
+ io:format("~p: ~s\n", [Port, String]),
+ flush_from_port(Port, Timeout)
+ after Timeout ->
+ timeout
+ end.
+
+wait_for_port_exit(Port) ->
+ case (receive M -> M end) of
+ {Port,{exit_status,Status}} ->
+ Status;
+ {Port,{data,String}} ->
+ io:format("~p: ~s\n", [Port, String]),
+ wait_for_port_exit(Port)
+ end.
+
+run_remote_test([FuncStr, TestNodeStr | Args]) ->
+ Status = try
+ io:format("Node ~p started~n", [node()]),
+ TestNode = list_to_atom(TestNodeStr),
+ io:format("Node ~p spawning function ~p~n", [node(), FuncStr]),
+ {Pid,Ref} = spawn_monitor(?MODULE, list_to_atom(FuncStr), [TestNode, Args]),
+ io:format("Node ~p waiting for function ~p~n", [node(), FuncStr]),
+ receive
+ {'DOWN', Ref, process, Pid, normal} ->
+ 0;
+ Other ->
+ io:format("Node ~p got unexpected msg: ~p\n",[node(), Other]),
+ 1
+ end
+ catch
+ C:E ->
+ io:format("Node ~p got EXCEPTION ~p:~p\nat ~p\n",
+ [node(), C, E, erlang:get_stacktrace()]),
+ 2
+ end,
+ io:format("Node ~p doing halt(~p).\n",[node(), Status]),
+ erlang:halt(Status).
+
+% Do the actual test on the remote node
+setopts_do(TestNode, [OptNr, ConnectData]) ->
+ [] = nodes(),
+ {Opt, Val} = opt_from_nr(OptNr),
+ ok = net_kernel:setopts(new, [{Opt, Val}]),
+
+ [] = nodes(),
+ {error, noconnection} = net_kernel:getopts(TestNode, [Opt]),
+
+ case ConnectData of
+ "ping" -> % We connect
+ net_adm:ping(TestNode);
+ TcpPort -> % Other connect
+ {ok, Sock} = gen_tcp:connect("localhost", list_to_integer(TcpPort),
+ [{active,false},{packet,2}]),
+ ok = gen_tcp:send(Sock, "Connect please"),
+ {ok, "Connect done"} = gen_tcp:recv(Sock, 0),
+ gen_tcp:close(Sock)
+ end,
+ [TestNode] = nodes(),
+ {ok, [{Opt,Val}]} = net_kernel:getopts(TestNode, [Opt]),
+ {error, noconnection} = net_kernel:getopts('pixie@fairyland', [Opt]),
+
+ NewVal = change_val(Val),
+ ok = net_kernel:setopts(TestNode, [{Opt, NewVal}]),
+ {ok, [{Opt,NewVal}]} = net_kernel:getopts(TestNode, [Opt]),
+
+ ok = net_kernel:setopts(TestNode, [{Opt, Val}]),
+ {ok, [{Opt,Val}]} = net_kernel:getopts(TestNode, [Opt]),
+
+ ok.
+
+opt_from_nr("1") -> {nodelay, true};
+opt_from_nr("2") -> {nodelay, false}.
+
+change_val(true) -> false;
+change_val(false) -> true.
+
+start_node_unconnected(Name, Mod, Func, Args) ->
+ FullName = full_node_name(Name),
+ CmdLine = mk_node_cmdline(Name,Mod,Func,Args),
+ io:format("Starting node ~p: ~s~n", [FullName, CmdLine]),
+ case open_port({spawn, CmdLine}, [exit_status]) of
+ Port when is_port(Port) ->
+ {FullName, Port};
+ Error ->
+ exit({failed_to_start_node, FullName, Error})
+ end.
+
+full_node_name(PreName) ->
+ HostSuffix = lists:dropwhile(fun ($@) -> false; (_) -> true end,
+ atom_to_list(node())),
+ list_to_atom(atom_to_list(PreName) ++ HostSuffix).
+
+mk_node_cmdline(Name,Mod,Func,Args) ->
+ Static = "-noinput",
+ Pa = filename:dirname(code:which(?MODULE)),
+ Prog = case catch init:get_argument(progname) of
+ {ok,[[P]]} -> P;
+ _ -> exit(no_progname_argument_found)
+ end,
+ NameSw = case net_kernel:longnames() of
+ false -> "-sname ";
+ true -> "-name ";
+ _ -> exit(not_distributed_node)
+ end,
+ {ok, Pwd} = file:get_cwd(),
+ NameStr = atom_to_list(Name),
+ Prog ++ " "
+ ++ Static ++ " "
+ ++ NameSw ++ " " ++ NameStr
+ ++ " -pa " ++ Pa
+ ++ " -env ERL_CRASH_DUMP " ++ Pwd ++ "/erl_crash_dump." ++ NameStr
+ ++ " -setcookie " ++ atom_to_list(erlang:get_cookie())
+ ++ " -run " ++ atom_to_list(Mod) ++ " " ++ atom_to_list(Func)
+ ++ " " ++ string:join(Args, " ").
+
%% OTP-4255.
tick_change(Config) when is_list(Config) ->
diff --git a/lib/kernel/test/file_SUITE.erl b/lib/kernel/test/file_SUITE.erl
index 5f049c6f99..c37d114a58 100644
--- a/lib/kernel/test/file_SUITE.erl
+++ b/lib/kernel/test/file_SUITE.erl
@@ -493,22 +493,13 @@ read_write_file(Config) when is_list(Config) ->
%% Try writing and reading back some term
SomeTerm = {"This term",{will,be},[written,$t,$o],1,file,[]},
- ok = ?FILE_MODULE:write_file(Name,term_to_binary(SomeTerm)),
- {ok,Bin1} = ?FILE_MODULE:read_file(Name),
- SomeTerm = binary_to_term(Bin1),
+ Bin1 = term_to_binary(SomeTerm),
+ ok = do_read_write_file(Name, Bin1),
%% Try a "null" term
NullTerm = [],
- ok = ?FILE_MODULE:write_file(Name,term_to_binary(NullTerm)),
- {ok,Bin2} = ?FILE_MODULE:read_file(Name),
- NullTerm = binary_to_term(Bin2),
-
- %% Try some "complicated" types
- BigNum = 123456789012345678901234567890,
- ComplTerm = {self(),make_ref(),BigNum,3.14159},
- ok = ?FILE_MODULE:write_file(Name,term_to_binary(ComplTerm)),
- {ok,Bin3} = ?FILE_MODULE:read_file(Name),
- ComplTerm = binary_to_term(Bin3),
+ Bin2 = term_to_binary(NullTerm),
+ ok = do_read_write_file(Name, Bin2),
%% Try reading a nonexistent file
Name2 = filename:join(RootDir,
@@ -519,25 +510,42 @@ read_write_file(Config) when is_list(Config) ->
{error, enoent} = ?FILE_MODULE:read_file(''),
%% Try writing to a bad filename
- {error, enoent} =
- ?FILE_MODULE:write_file("",term_to_binary(NullTerm)),
+ {error, enoent} = do_read_write_file("", Bin2),
%% Try writing something else than a binary
- {error, badarg} = ?FILE_MODULE:write_file(Name,{1,2,3}),
- {error, badarg} = ?FILE_MODULE:write_file(Name,self()),
+ {error, badarg} = do_read_write_file(Name, {1,2,3}),
+ {error, badarg} = do_read_write_file(Name, self()),
%% Some non-term binaries
- ok = ?FILE_MODULE:write_file(Name,[]),
- {ok,Bin4} = ?FILE_MODULE:read_file(Name),
- 0 = byte_size(Bin4),
+ ok = do_read_write_file(Name, []),
- ok = ?FILE_MODULE:write_file(Name,[Bin1,[],[[Bin2]]]),
- {ok,Bin5} = ?FILE_MODULE:read_file(Name),
- {Bin1,Bin2} = split_binary(Bin5,byte_size(Bin1)),
+ %% Write some iolists
+ ok = do_read_write_file(Name, [Bin1,[],[[Bin2]]]),
+ ok = do_read_write_file(Name, ["string",<<"binary">>]),
+ ok = do_read_write_file(Name, "pure string"),
[] = flush(),
ok.
+do_read_write_file(Name, Data) ->
+ case ?FILE_MODULE:write_file(Name, Data) of
+ ok ->
+ BinData = iolist_to_binary(Data),
+ {ok,BinData} = ?FILE_MODULE:read_file(Name),
+
+ ok = ?FILE_MODULE:write_file(Name, Data, []),
+ {ok,BinData} = ?FILE_MODULE:read_file(Name),
+
+ ok = ?FILE_MODULE:write_file(Name, Data, [raw]),
+ {ok,BinData} = ?FILE_MODULE:read_file(Name),
+
+ ok;
+ {error,_}=Res ->
+ Res = ?FILE_MODULE:write_file(Name, Data, []),
+ Res = ?FILE_MODULE:write_file(Name, Data, [raw]),
+ Res
+ end.
+
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
diff --git a/lib/kernel/test/gen_sctp_SUITE.erl b/lib/kernel/test/gen_sctp_SUITE.erl
index f836b2aa94..620ab235a0 100644
--- a/lib/kernel/test/gen_sctp_SUITE.erl
+++ b/lib/kernel/test/gen_sctp_SUITE.erl
@@ -117,7 +117,11 @@ xfer_min(Config) when is_list(Config) ->
Stream = 0,
Data = <<"The quick brown fox jumps over a lazy dog 0123456789">>,
Loopback = {127,0,0,1},
+ StatOpts =
+ [recv_avg,recv_cnt,recv_max,recv_oct,
+ send_avg,send_cnt,send_max,send_oct],
{ok,Sb} = gen_sctp:open([{type,seqpacket}]),
+ {ok,SbStat1} = inet:getstat(Sb, StatOpts),
{ok,Pb} = inet:port(Sb),
ok = gen_sctp:listen(Sb, true),
@@ -212,6 +216,8 @@ xfer_min(Config) when is_list(Config) ->
assoc_id=SbAssocId}} =
recv_event(log_ok(gen_sctp:recv(Sb, infinity))),
ok = gen_sctp:close(Sa),
+ {ok,SbStat2} = inet:getstat(Sb, StatOpts),
+ [] = filter_stat_eq(SbStat1, SbStat2),
ok = gen_sctp:close(Sb),
receive
@@ -220,6 +226,18 @@ xfer_min(Config) when is_list(Config) ->
end,
ok.
+filter_stat_eq([], []) ->
+ [];
+filter_stat_eq([{Tag,Val1}=Stat|SbStat1], [{Tag,Val2}|SbStat2]) ->
+ if
+ Val1 == Val2 ->
+ [Stat|filter_stat_eq(SbStat1, SbStat2)];
+ true ->
+ filter_stat_eq(SbStat1, SbStat2)
+ end.
+
+
+
%% Minimal data transfer in active mode.
xfer_active(Config) when is_list(Config) ->
Timeout = 2000,
@@ -383,26 +401,28 @@ def_sndrcvinfo(Config) when is_list(Config) ->
assoc_id=S2AssocId} = S2AssocChange =
log_ok(gen_sctp:connect(S2, Loopback, P1, [])),
?LOGVAR(S2AssocChange),
- case recv_event(log_ok(gen_sctp:recv(S1))) of
- {Loopback,P2,
- #sctp_assoc_change{
- state=comm_up,
- error=0,
- assoc_id=S1AssocId}} ->
- ?LOGVAR(S1AssocId);
- {Loopback,P2,
- #sctp_paddr_change{
- state=addr_confirmed,
- error=0,
- assoc_id=S1AssocId}} ->
- ?LOGVAR(S1AssocId),
+ S1AssocId =
+ case recv_event(log_ok(gen_sctp:recv(S1))) of
{Loopback,P2,
#sctp_assoc_change{
state=comm_up,
error=0,
- assoc_id=S1AssocId}} =
- recv_event(log_ok(gen_sctp:recv(S1)))
- end,
+ assoc_id=AssocId}} ->
+ AssocId;
+ {Loopback,P2,
+ #sctp_paddr_change{
+ state=addr_confirmed,
+ error=0,
+ assoc_id=AssocId}} ->
+ {Loopback,P2,
+ #sctp_assoc_change{
+ state=comm_up,
+ error=0,
+ assoc_id=AssocId}} =
+ recv_event(log_ok(gen_sctp:recv(S1))),
+ AssocId
+ end,
+ ?LOGVAR(S1AssocId),
#sctp_sndrcvinfo{
ppid=17, context=0, timetolive=0} = %, assoc_id=S1AssocId} =
@@ -1055,6 +1075,7 @@ peeloff(Config, SockOpts) when is_list(Config) ->
Addr = {127,0,0,1},
Stream = 0,
Timeout = 333,
+ StartTime = timestamp(),
S1 = socket_open([{ifaddr,Addr}|SockOpts], Timeout),
?LOGVAR(S1),
P1 = socket_call(S1, get_port),
@@ -1077,7 +1098,7 @@ peeloff(Config, SockOpts) when is_list(Config) ->
state=comm_up,
assoc_id=AssocId2}}} -> AssocId2
after Timeout ->
- socket_bailout([S1,S2])
+ socket_bailout([S1,S2], StartTime)
end,
?LOGVAR(S2Ai),
S1Ai =
@@ -1087,7 +1108,7 @@ peeloff(Config, SockOpts) when is_list(Config) ->
state=comm_up,
assoc_id=AssocId1}}} -> AssocId1
after Timeout ->
- socket_bailout([S1,S2])
+ socket_bailout([S1,S2], StartTime)
end,
?LOGVAR(S1Ai),
%%
@@ -1095,13 +1116,13 @@ peeloff(Config, SockOpts) when is_list(Config) ->
receive
{S1,{Addr,P2,S1Ai,Stream,<<"Number one">>}} -> ok
after Timeout ->
- socket_bailout([S1,S2])
+ socket_bailout([S1,S2], StartTime)
end,
socket_call(S2, {send,Socket1,S1Ai,Stream,<<"Number two">>}),
receive
{S2,{Addr,P1,S2Ai,Stream,<<"Number two">>}} -> ok
after Timeout ->
- socket_bailout([S1,S2])
+ socket_bailout([S1,S2], StartTime)
end,
%%
S3 = socket_peeloff(Socket1, S1Ai, SockOpts, Timeout),
@@ -1120,31 +1141,31 @@ peeloff(Config, SockOpts) when is_list(Config) ->
receive
{S2,{Addr,P3,S2Ai,Stream,<<"Number three">>}} -> ok
after Timeout ->
- socket_bailout([S1,S2,S3])
+ socket_bailout([S1,S2,S3], StartTime)
end,
socket_call(S3, {send,Socket2,S2Ai,Stream,<<"Number four">>}),
receive
{S3,{Addr,P2,S3Ai,Stream,<<"Number four">>}} -> ok
after Timeout ->
- socket_bailout([S1,S2,S3])
+ socket_bailout([S1,S2,S3], StartTime)
end,
%%
inet:i(sctp),
- socket_close_verbose(S1),
- socket_close_verbose(S2),
+ socket_close_verbose(S1, StartTime),
+ socket_close_verbose(S2, StartTime),
receive
{S3,{Addr,P2,#sctp_shutdown_event{assoc_id=S3Ai_X}}} ->
match_unless_solaris(S3Ai, S3Ai_X)
after Timeout ->
- socket_bailout([S3])
+ socket_bailout([S3], StartTime)
end,
receive
{S3,{Addr,P2,#sctp_assoc_change{state=shutdown_comp,
assoc_id=S3Ai}}} -> ok
after Timeout ->
- socket_bailout([S3])
+ socket_bailout([S3], StartTime)
end,
- socket_close_verbose(S3),
+ socket_close_verbose(S3, StartTime),
[] = flush(),
ok.
@@ -1156,6 +1177,7 @@ buffers(Config) when is_list(Config) ->
Addr = {127,0,0,1},
Stream = 1,
Timeout = 3333,
+ StartTime = timestamp(),
S1 = socket_open([{ip,Addr}], Timeout),
?LOGVAR(S1),
P1 = socket_call(S1, get_port),
@@ -1174,7 +1196,7 @@ buffers(Config) when is_list(Config) ->
state=comm_up,
assoc_id=AssocId2}}} -> AssocId2
after Timeout ->
- socket_bailout([S1,S2])
+ socket_bailout([S1,S2], StartTime)
end,
S1Ai =
receive
@@ -1183,7 +1205,7 @@ buffers(Config) when is_list(Config) ->
state=comm_up,
assoc_id=AssocId1}}} -> AssocId1
after Timeout ->
- socket_bailout([S1,S2])
+ socket_bailout([S1,S2], StartTime)
end,
%%
socket_call(S1, {setopts,[{recbuf,Limit}]}),
@@ -1197,22 +1219,22 @@ buffers(Config) when is_list(Config) ->
receive
{S1,{Addr,P2,S1Ai,Stream,Data}} -> ok
after Timeout ->
- socket_bailout([S1,S2])
+ socket_bailout([S1,S2], StartTime)
end,
%%
- socket_close_verbose(S1),
+ socket_close_verbose(S1, StartTime),
receive
{S2,{Addr,P1,#sctp_shutdown_event{assoc_id=S2Ai}}} -> ok
after Timeout ->
- socket_bailout([S2])
+ socket_bailout([S2], StartTime)
end,
receive
{S2,{Addr,P1,#sctp_assoc_change{state=shutdown_comp,
assoc_id=S2Ai}}} -> ok
after Timeout ->
- socket_bailout([S2])
+ socket_bailout([S2], StartTime)
end,
- socket_close_verbose(S2),
+ socket_close_verbose(S2, StartTime),
[] = flush(),
ok.
@@ -1521,8 +1543,8 @@ socket_peeloff(Socket, AssocId, SocketOpts, Timeout) ->
end,
s_start(Starter, Timeout).
-socket_close_verbose(S) ->
- History = socket_history(socket_close(S)),
+socket_close_verbose(S, StartTime) ->
+ History = socket_history(socket_close(S), StartTime),
io:format("socket_close ~p:~n ~p.~n", [S,History]),
History.
@@ -1535,19 +1557,19 @@ socket_call(S, Request) ->
%% socket_get(S, Key) ->
%% s_req(S, {get,Key}).
-socket_bailout([S|Ss]) ->
- History = socket_history(socket_close(S)),
+socket_bailout([S|Ss], StartTime) ->
+ History = socket_history(socket_close(S), StartTime),
io:format("bailout ~p:~n ~p.~n", [S,History]),
- socket_bailout(Ss);
-socket_bailout([]) ->
+ socket_bailout(Ss, StartTime);
+socket_bailout([], _) ->
io:format("flush: ~p.~n", [flush()]),
ct:fail(socket_bailout).
-socket_history({State,Flush}) ->
+socket_history({State,Flush}, StartTime) ->
{lists:keysort(
2,
lists:flatten(
- [[{Key,Val} || Val <- Vals]
+ [[{Key,{TS-StartTime,Val}} || {TS,Val} <- Vals]
|| {Key,Vals} <- gb_trees:to_list(State)])),
Flush}.
@@ -1610,14 +1632,12 @@ s_loop(Socket, Timeout, Parent, Handler, State) ->
{Parent,Ref,exit} ->
ok = gen_sctp:close(Socket),
Key = exit,
- Val = {now(),Socket},
- NewState = gb_push(Key, Val, State),
+ NewState = gb_push(Key, Socket, State),
Parent ! {self(),Ref,{NewState,flush()}};
{Parent,Ref,{Msg}} ->
Result = Handler(Msg),
Key = req,
- Val = {now(),{Msg,Result}},
- NewState = gb_push(Key, Val, State),
+ NewState = gb_push(Key, {Msg,Result}, State),
Parent ! {self(),Ref,Result},
s_loop(Socket, Timeout, Parent, Handler, NewState);
%% {Parent,Ref,{get,Key}} ->
@@ -1627,16 +1647,15 @@ s_loop(Socket, Timeout, Parent, Handler, State) ->
{[#sctp_sndrcvinfo{stream=Stream,assoc_id=AssocId}=SRI],Data}}
when not is_tuple(Data) ->
case gb_get({assoc_change,AssocId}, State) of
- [{_,{Addr,Port,
- #sctp_assoc_change{
- state=comm_up,
- inbound_streams=Is}}}|_]
+ [{Addr,Port,
+ #sctp_assoc_change{
+ state=comm_up,
+ inbound_streams=Is}}|_]
when 0 =< Stream, Stream < Is-> ok;
[] -> ok
end,
Key = {msg,AssocId,Stream},
- Val = {now(),{Addr,Port,SRI,Data}},
- NewState = gb_push(Key, Val, State),
+ NewState = gb_push(Key, {Addr,Port,SRI,Data}, State),
Parent ! {self(),{Addr,Port,AssocId,Stream,Data}},
again(Socket),
s_loop(Socket, Timeout, Parent, Handler, NewState);
@@ -1647,13 +1666,12 @@ s_loop(Socket, Timeout, Parent, Handler, State) ->
[] -> ok
end,
Key = {assoc_change,AssocId},
- Val = {now(),{Addr,Port,SAC}},
case {gb_get(Key, State),St} of
{[],_} -> ok;
- {[{_,{Addr,Port,#sctp_assoc_change{state=comm_up}}}|_],_}
+ {[{Addr,Port,#sctp_assoc_change{state=comm_up}}|_],_}
when St =:= comm_lost; St =:= shutdown_comp -> ok
end,
- NewState = gb_push(Key, Val, State),
+ NewState = gb_push(Key, {Addr,Port,SAC}, State),
Parent ! {self(),{Addr,Port,SAC}},
again(Socket),
s_loop(Socket, Timeout, Parent, Handler, NewState);
@@ -1667,14 +1685,13 @@ s_loop(Socket, Timeout, Parent, Handler, State) ->
[] -> ok
end,
case {gb_get({assoc_change,AssocId}, State),St} of
- {[{_,{Addr,Port,#sctp_assoc_change{state=comm_up}}}|_],_}
+ {[{Addr,Port,#sctp_assoc_change{state=comm_up}}|_],_}
when St =:= addr_available;
St =:= addr_confirmed -> ok;
{[],addr_confirmed} -> ok
end,
Key = {paddr_change,AssocId},
- Val = {now(),{Addr,Port,SPC}},
- NewState = gb_push(Key, Val, State),
+ NewState = gb_push(Key, {Addr,Port,SPC}, State),
again(Socket),
s_loop(Socket, Timeout, Parent, Handler, NewState);
{sctp,Socket,Addr,Port,
@@ -1684,12 +1701,11 @@ s_loop(Socket, Timeout, Parent, Handler, State) ->
[] -> ok
end,
case gb_get({assoc_change,AssocId}, State) of
- [{_,{Addr,Port,#sctp_assoc_change{state=comm_up}}}|_] -> ok;
+ [{Addr,Port,#sctp_assoc_change{state=comm_up}}|_] -> ok;
[] -> ok
end,
Key = {shutdown_event,AssocId},
- Val = {now(),{Addr,Port}},
- NewState = gb_push(Key, Val, State),
+ NewState = gb_push(Key, {Addr,Port}, State),
Parent ! {self(), {Addr,Port,SSE}},
again(Socket),
s_loop(Socket, Timeout, Parent, Handler, NewState);
@@ -1707,11 +1723,12 @@ again(Socket) ->
end.
gb_push(Key, Val, GBT) ->
+ TS = timestamp(),
case gb_trees:lookup(Key, GBT) of
none ->
- gb_trees:insert(Key, [Val], GBT);
+ gb_trees:insert(Key, [{TS,Val}], GBT);
{value,V} ->
- gb_trees:update(Key, [Val|V], GBT)
+ gb_trees:update(Key, [{TS,Val}|V], GBT)
end.
gb_get(Key, GBT) ->
@@ -1719,7 +1736,7 @@ gb_get(Key, GBT) ->
none ->
[];
{value,V} ->
- V
+ [Val || {_TS,Val} <- V]
end.
match_unless_solaris(A, B) ->
@@ -1727,3 +1744,6 @@ match_unless_solaris(A, B) ->
{unix,sunos} -> B;
_ -> A = B
end.
+
+timestamp() ->
+ erlang:monotonic_time().
diff --git a/lib/kernel/test/gen_tcp_api_SUITE.erl b/lib/kernel/test/gen_tcp_api_SUITE.erl
index 77ec89b4f4..92a74465b7 100644
--- a/lib/kernel/test/gen_tcp_api_SUITE.erl
+++ b/lib/kernel/test/gen_tcp_api_SUITE.erl
@@ -38,7 +38,7 @@
t_local_basic/1, t_local_unbound/1, t_local_fdopen/1,
t_local_fdopen_listen/1, t_local_fdopen_listen_unbound/1,
t_local_fdopen_connect/1, t_local_fdopen_connect_unbound/1,
- t_local_abstract/1]).
+ t_local_abstract/1, t_accept_inet6_tclass/1]).
-export([getsockfd/0,closesockfd/1]).
@@ -50,6 +50,7 @@ all() ->
[{group, t_accept}, {group, t_connect}, {group, t_recv},
t_shutdown_write, t_shutdown_both, t_shutdown_error,
t_shutdown_async, t_fdopen, t_fdconnect, t_implicit_inet6,
+ t_accept_inet6_tclass,
{group, t_local}].
groups() ->
@@ -521,6 +522,24 @@ local_handshake(S, SAddr, C, CAddr) ->
SData = ok(gen_tcp:recv(C, length(SData))),
ok.
+t_accept_inet6_tclass(Config) when is_list(Config) ->
+ TClassOpt = {tclass,8#56 bsl 2}, % Expedited forwarding
+ case gen_tcp:listen(0, [inet6,TClassOpt]) of
+ {ok,L} ->
+ LPort = ok(inet:port(L)),
+ Loopback = {0,0,0,0,0,0,0,1},
+ Sa = ok(gen_tcp:connect(Loopback, LPort, [])),
+ Sb = ok(gen_tcp:accept(L)),
+ [TClassOpt] = ok(inet:getopts(Sb, [tclass])),
+ ok = gen_tcp:close(Sb),
+ ok = gen_tcp:close(Sa),
+ ok = gen_tcp:close(L),
+ ok;
+ {error,_} ->
+ {skip,"IPv6 TCLASS not supported"}
+ end.
+
+
%%% Utilities
%% Calls M:F/length(A), which should return a timeout error, and complete
diff --git a/lib/kernel/test/init_SUITE.erl b/lib/kernel/test/init_SUITE.erl
index 1370e23195..2b59eb2bfe 100644
--- a/lib/kernel/test/init_SUITE.erl
+++ b/lib/kernel/test/init_SUITE.erl
@@ -27,7 +27,8 @@
-export([get_arguments/1, get_argument/1, boot_var/1, restart/1,
many_restarts/0, many_restarts/1,
get_plain_arguments/1,
- reboot/1, stop_status/1, stop/1, get_status/1, script_id/1]).
+ reboot/1, stop_status/1, stop/1, get_status/1, script_id/1,
+ find_system_processes/0]).
-export([boot1/1, boot2/1]).
-export([init_per_testcase/2, end_per_testcase/2]).
@@ -355,12 +356,16 @@ wait_for(N,Node,EHPid) ->
restart(Config) when is_list(Config) ->
Args = args(),
+ Pa = " -pa " ++ filename:dirname(code:which(?MODULE)),
+
%% Currently test_server:start_node cannot be used. The restarted
%% node immediately halts due to the implementation of
%% test_server:start_node.
- {ok, Node} = loose_node:start(init_test, Args, ?DEFAULT_TIMEOUT_SEC),
+ {ok, Node} = loose_node:start(init_test, Args ++ Pa, ?DEFAULT_TIMEOUT_SEC),
%% Ok, the node is up, now the real test test begins.
erlang:monitor_node(Node, true),
+ SysProcs0 = rpc:call(Node, ?MODULE, find_system_processes, []),
+ [InitPid, PurgerPid, LitCollectorPid, DirtyCodePid] = SysProcs0,
InitPid = rpc:call(Node, erlang, whereis, [init]),
PurgerPid = rpc:call(Node, erlang, whereis, [erts_code_purger]),
Procs = rpc:call(Node, erlang, processes, []),
@@ -375,6 +380,9 @@ restart(Config) when is_list(Config) ->
end,
ok = wait_restart(30, Node),
+ SysProcs1 = rpc:call(Node, ?MODULE, find_system_processes, []),
+ [InitPid1, PurgerPid1, LitCollectorPid1, DirtyCodePid1] = SysProcs1,
+
%% Still the same init process!
InitPid1 = rpc:call(Node, erlang, whereis, [init]),
InitP = pid_to_list(InitPid),
@@ -385,8 +393,24 @@ restart(Config) when is_list(Config) ->
PurgerP = pid_to_list(PurgerPid),
PurgerP = pid_to_list(PurgerPid1),
+ %% and same literal area collector process!
+ case LitCollectorPid of
+ undefined -> undefined = LitCollectorPid1;
+ _ ->
+ LitCollectorP = pid_to_list(LitCollectorPid),
+ LitCollectorP = pid_to_list(LitCollectorPid1)
+ end,
+
+ %% and same dirty process code checker process!
+ case DirtyCodePid of
+ undefined -> undefined = DirtyCodePid1;
+ _ ->
+ DirtyCodeP = pid_to_list(DirtyCodePid),
+ DirtyCodeP = pid_to_list(DirtyCodePid1)
+ end,
+
NewProcs0 = rpc:call(Node, erlang, processes, []),
- NewProcs = NewProcs0 -- [InitPid1, PurgerPid1],
+ NewProcs = NewProcs0 -- SysProcs1,
case check_processes(NewProcs, MaxPid) of
true ->
ok;
@@ -406,6 +430,37 @@ restart(Config) when is_list(Config) ->
loose_node:stop(Node),
ok.
+-record(sys_procs, {init,
+ code_purger,
+ literal_collector,
+ dirty_proc_checker}).
+
+find_system_processes() ->
+ find_system_procs(processes(), #sys_procs{}).
+
+find_system_procs([], SysProcs) ->
+ [SysProcs#sys_procs.init,
+ SysProcs#sys_procs.code_purger,
+ SysProcs#sys_procs.literal_collector,
+ SysProcs#sys_procs.dirty_proc_checker];
+find_system_procs([P|Ps], SysProcs) ->
+ case process_info(P, initial_call) of
+ {initial_call,{otp_ring0,start,2}} ->
+ undefined = SysProcs#sys_procs.init,
+ find_system_procs(Ps, SysProcs#sys_procs{init = P});
+ {initial_call,{erts_code_purger,start,0}} ->
+ undefined = SysProcs#sys_procs.code_purger,
+ find_system_procs(Ps, SysProcs#sys_procs{code_purger = P});
+ {initial_call,{erts_literal_area_collector,start,0}} ->
+ undefined = SysProcs#sys_procs.literal_collector,
+ find_system_procs(Ps, SysProcs#sys_procs{literal_collector = P});
+ {initial_call,{erts_dirty_process_code_checker,start,0}} ->
+ undefined = SysProcs#sys_procs.dirty_proc_checker,
+ find_system_procs(Ps, SysProcs#sys_procs{dirty_proc_checker = P});
+ _ ->
+ find_system_procs(Ps, SysProcs)
+ end.
+
wait_restart(0, _Node) ->
ct:fail(not_restarted);
wait_restart(N, Node) ->
diff --git a/lib/kernel/test/os_SUITE.erl b/lib/kernel/test/os_SUITE.erl
index 2a1e5016ec..e76d6ec482 100644
--- a/lib/kernel/test/os_SUITE.erl
+++ b/lib/kernel/test/os_SUITE.erl
@@ -24,7 +24,8 @@
init_per_testcase/2,end_per_testcase/2]).
-export([space_in_cwd/1, quoting/1, cmd_unicode/1, space_in_name/1, bad_command/1,
find_executable/1, unix_comment_in_command/1, deep_list_command/1,
- large_output_command/1, perf_counter_api/1]).
+ large_output_command/1, background_command/0, background_command/1,
+ message_leak/1, close_stdin/0, close_stdin/1, perf_counter_api/1]).
-include_lib("common_test/include/ct.hrl").
@@ -35,7 +36,8 @@ suite() ->
all() ->
[space_in_cwd, quoting, cmd_unicode, space_in_name, bad_command,
find_executable, unix_comment_in_command, deep_list_command,
- large_output_command, perf_counter_api].
+ large_output_command, background_command, message_leak,
+ close_stdin, perf_counter_api].
groups() ->
[].
@@ -52,6 +54,14 @@ init_per_group(_GroupName, Config) ->
end_per_group(_GroupName, Config) ->
Config.
+init_per_testcase(TC, Config)
+ when TC =:= background_command; TC =:= close_stdin ->
+ case os:type() of
+ {win32, _} ->
+ {skip,"Should not work on windows"};
+ _ ->
+ Config
+ end;
init_per_testcase(_TC,Config) ->
Config.
@@ -261,13 +271,48 @@ deep_list_command(Config) when is_list(Config) ->
%% FYI: [$e, $c, "ho"] =:= io_lib:format("ec~s", ["ho"])
ok.
-%% Test to take sure that the correct data is
+%% Test to make sure that the correct data is
%% received when doing large commands.
large_output_command(Config) when is_list(Config) ->
%% Maximum allowed on windows is 8192, so we test well below that
AAA = lists:duplicate(7000, $a),
comp(AAA,os:cmd("echo " ++ AAA)).
+%% Test that it is possible on unix to start a background task using os:cmd.
+background_command() ->
+ [{timetrap, {seconds, 5}}].
+background_command(_Config) ->
+ %% This testcase fails when the os:cmd takes
+ %% longer then the 5 second timeout
+ os:cmd("sleep 10&").
+
+%% Test that message does not leak to the calling process
+message_leak(_Config) ->
+ process_flag(trap_exit, true),
+
+ os:cmd("echo hello"),
+ [] = receive_all(),
+
+ case os:type() of
+ {unix, _} ->
+ os:cmd("for i in $(seq 1 100); do echo hello; done&"),
+ [] = receive_all();
+ _ ->
+ ok % Cannot background on non-unix
+ end,
+
+ process_flag(trap_exit, false).
+
+%% Test that os:cmd closes stdin of the program that is executed
+close_stdin() ->
+ [{timetrap, {seconds, 5}}].
+close_stdin(Config) ->
+ DataDir = proplists:get_value(data_dir, Config),
+ Fds = filename:join(DataDir, "my_fds"),
+
+ "-1" = os:cmd(Fds).
+
+
%% Test that the os:perf_counter api works as expected
perf_counter_api(_Config) ->
diff --git a/lib/kernel/test/os_SUITE_data/Makefile.src b/lib/kernel/test/os_SUITE_data/Makefile.src
index 912d0cbcb1..f83f781411 100644
--- a/lib/kernel/test/os_SUITE_data/Makefile.src
+++ b/lib/kernel/test/os_SUITE_data/Makefile.src
@@ -3,7 +3,7 @@ LD = @LD@
CFLAGS = @CFLAGS@ -I@erl_include@ @DEFS@
CROSSLDFLAGS = @CROSSLDFLAGS@
-PROGS = my_echo@exe@
+PROGS = my_echo@exe@ my_fds@exe@
all: $(PROGS)
@@ -12,3 +12,9 @@ my_echo@exe@: my_echo@obj@
my_echo@obj@: my_echo.c
$(CC) -c -o my_echo@obj@ $(CFLAGS) my_echo.c
+
+my_fds@exe@: my_fds@obj@
+ $(LD) $(CROSSLDFLAGS) -o my_fds my_fds@obj@ @LIBS@
+
+my_fds@obj@: my_fds.c
+ $(CC) -c -o my_fds@obj@ $(CFLAGS) my_fds.c
diff --git a/lib/kernel/test/os_SUITE_data/my_fds.c b/lib/kernel/test/os_SUITE_data/my_fds.c
new file mode 100644
index 0000000000..704a4d1e1d
--- /dev/null
+++ b/lib/kernel/test/os_SUITE_data/my_fds.c
@@ -0,0 +1,9 @@
+#include <stdio.h>
+
+int
+main(int argc, char** argv)
+{
+ char buff[1];
+ int res = read(stdin, buff, 1);
+ printf("%d", res);
+}
diff --git a/lib/kernel/vsn.mk b/lib/kernel/vsn.mk
index e7d422d03c..d3b2d18ae5 100644
--- a/lib/kernel/vsn.mk
+++ b/lib/kernel/vsn.mk
@@ -1 +1 @@
-KERNEL_VSN = 5.0
+KERNEL_VSN = 5.1
diff --git a/lib/mnesia/doc/src/Mnesia_chap1.xml b/lib/mnesia/doc/src/Mnesia_chap1.xml
index 9dfeb5efe4..035e934ed2 100644
--- a/lib/mnesia/doc/src/Mnesia_chap1.xml
+++ b/lib/mnesia/doc/src/Mnesia_chap1.xml
@@ -32,38 +32,38 @@
<rev>C</rev>
<file>Mnesia_chap1.xml</file>
</header>
- <p>The <c>Mnesia</c> application provides a heavy duty real-time
+ <p>The Mnesia application provides a heavy duty real-time
distributed database.</p>
<section>
<title>Scope</title>
<p>This User's Guide describes how to
- build <c>Mnesia</c> database applications, and how to integrate
- and use the <c>Mnesia</c> database management system with
+ build Mnesia database applications, and how to integrate
+ and use the Mnesia database management system with
OTP. Programming constructs are described, and numerous
programming examples are included to illustrate the use of
- <c>Mnesia</c>.</p>
+ Mnesia.</p>
<p>This User's Guide is organized as follows:</p>
<list type="bulleted">
<item><seealso marker="Mnesia_overview">Mnesia</seealso>
provides an introduction to
- <c>Mnesia</c>.
+ Mnesia.
</item>
<item><seealso marker="Mnesia_chap2">Getting Started</seealso>
- introduces <c>Mnesia</c> with an example database. Examples
+ introduces Mnesia with an example database. Examples
are included how to start an Erlang session, specify a
- <c>Mnesia</c> database directory, initialize a database
- schema, start <c>Mnesia</c>, and create tables. Initial
+ Mnesia database directory, initialize a database
+ schema, start Mnesia, and create tables. Initial
prototyping of record definitions is also discussed.
</item>
<item><seealso marker="Mnesia_chap3">Build a Mnesia
Database</seealso> more formally describes the steps
- introduced in the previous section, namely the <c>Mnesia</c>
- functions that define a database schema, start <c>Mnesia</c>,
+ introduced in the previous section, namely the Mnesia
+ functions that define a database schema, start Mnesia,
and create the required tables.
</item>
<item><seealso marker="Mnesia_chap4">Transactions and Other Access Contexts</seealso>
- describes the transactions properties that make <c>Mnesia</c> into
+ describes the transactions properties that make Mnesia into
a fault tolerant, real-time distributed database management
system. This section also describes the concept of locking
to ensure consistency in tables, and "dirty
@@ -76,16 +76,16 @@
features include indexing, checkpoints, distribution and fault
tolerance, disc-less nodes, replication manipulation, local
content tables, concurrency, and object-based programming in
- <c>Mnesia</c>.
+ Mnesia.
</item>
<item><seealso marker="Mnesia_chap7">Mnesia System
Information</seealso> describes the files contained in the
- <c>Mnesia</c> database directory, database configuration data,
+ Mnesia database directory, database configuration data,
core and table dumps, as well as the important subject of
backup, fall-back, and disaster recovery principles.
</item>
<item><seealso marker="Mnesia_chap8">Combine Mnesia with
- SNMP</seealso> is a short section that outlines <c>Mnesia</c>
+ SNMP</seealso> is a short section that outlines Mnesia
integrated with SNMP.
</item>
<item><seealso marker="Mnesia_App_A">Appendix A: Backup
diff --git a/lib/mnesia/doc/src/Mnesia_chap8.xml b/lib/mnesia/doc/src/Mnesia_chap8.xml
index f1a469e315..4a2eed84d7 100644
--- a/lib/mnesia/doc/src/Mnesia_chap8.xml
+++ b/lib/mnesia/doc/src/Mnesia_chap8.xml
@@ -51,11 +51,11 @@
</item>
</list>
<p>All these approaches have different advantages and
- disadvantages. <c>Mnesia</c> applications can easily be opened to
+ disadvantages. Mnesia applications can easily be opened to
the SNMP protocol. A direct 1-to-1 mapping can be established
- between <c>Mnesia</c> tables and SNMP tables. This means
- that a <c>Mnesia</c> table can be configured to be <em>both</em>
- a <c>Mnesia</c> table and an SNMP table. A number of functions to
+ between Mnesia tables and SNMP tables. This means
+ that a Mnesia table can be configured to be <em>both</em>
+ a Mnesia table and an SNMP table. A number of functions to
control this behavior are described in the Reference Manual.</p>
</section>
</chapter>
diff --git a/lib/mnesia/doc/src/Mnesia_overview.xml b/lib/mnesia/doc/src/Mnesia_overview.xml
index d2d597b85d..63f2309284 100644
--- a/lib/mnesia/doc/src/Mnesia_overview.xml
+++ b/lib/mnesia/doc/src/Mnesia_overview.xml
@@ -39,14 +39,14 @@
high level of fault tolerance that is required in many nonstop
systems, combined with requirements on the DBMS to run in the same
address space as the application, have led us to implement a new
- DBMS, called <c>Mnesia</c>.</p>
- <p><c>Mnesia</c> is implemented in, and tightly connected to Erlang.
+ DBMS, called Mnesia.</p>
+ <p>Mnesia is implemented in, and tightly connected to Erlang.
It provides the functionality that is necessary for the
implementation of fault tolerant telecommunications systems.</p>
- <p><c>Mnesia</c> is a multiuser distributed DBMS specially made for
+ <p>Mnesia is a multiuser distributed DBMS specially made for
industrial telecommunications applications written in Erlang,
which is also the intended target language.
- <c>Mnesia</c> tries to address all the data
+ Mnesia tries to address all the data
management issues required for typical telecommunications systems.
It has a number of features that are not normally found in traditional
databases.</p>
@@ -54,7 +54,7 @@
from the features provided by traditional DBMSs. The applications now
implemented in Erlang need a mixture of a broad range
of features, which generally are not satisfied by traditional DBMSs.
- <c>Mnesia</c> is designed with requirements like the following in
+ Mnesia is designed with requirements like the following in
mind:</p>
<list type="ordered">
<item>Fast real-time key/value lookup
@@ -71,9 +71,9 @@
<item>Complex objects
</item>
</list>
- <p><c>Mnesia</c> is designed with the typical data management problems
- of telecommunications applications in mind. This sets <c>Mnesia</c>
- apart from most other DBMS. Hence <c>Mnesia</c>
+ <p>Mnesia is designed with the typical data management problems
+ of telecommunications applications in mind. This sets Mnesia
+ apart from most other DBMS. Hence Mnesia
combines many concepts found in traditional databases such as
transactions and queries with concepts found in data management
systems for telecommunications applications, for example:</p>
@@ -86,7 +86,7 @@
suspending it.
</item>
</list>
- <p><c>Mnesia</c> is also interesting because of its tight coupling to
+ <p>Mnesia is also interesting because of its tight coupling to
Erlang, thus almost turning Erlang into a database programming
language. This has many benefits, the foremost is that
the impedance mismatch between the data format used by the DBMS
@@ -97,7 +97,7 @@
<title>Mnesia Database Management System (DBMS)</title>
<section>
<title>Features</title>
- <p><c>Mnesia</c> contains the following features that combine to
+ <p>Mnesia contains the following features that combine to
produce a fault-tolerant, distributed DBMS written in Erlang:
</p>
<list type="bulleted">
@@ -118,7 +118,7 @@
functions can be called within one transaction.
</item>
<item>Several transactions can run concurrently, and their execution
- is fully synchronized by the DBMS. <c>Mnesia</c> ensures that no
+ is fully synchronized by the DBMS. Mnesia ensures that no
two processes manipulate data simultaneously.
</item>
<item>Transactions can be assigned the property of being executed on
@@ -132,29 +132,29 @@
<section>
<title>Add-On Application</title>
- <p>Query List Comprehension (QLC) can be used with <c>Mnesia</c>
+ <p>Query List Comprehension (QLC) can be used with Mnesia
to produce specialized functions that enhance the operational
- ability of <c>Mnesia</c>. QLC has its own documentation as part
+ ability of Mnesia. QLC has its own documentation as part
of the OTP documentation set. The main features of QLC
- when used with <c>Mnesia</c> are as follows:</p>
+ when used with Mnesia are as follows:</p>
<list type="bulleted">
- <item>QLC can optimize the query compiler for the <c>Mnesia</c>
+ <item>QLC can optimize the query compiler for the Mnesia
DBMS, essentially making the DBMS more efficient.
</item>
<item>QLC can be used as a database programming
- language for <c>Mnesia</c>. It includes a notation called "list
+ language for Mnesia. It includes a notation called "list
comprehensions" and can be used to make complex database
queries over a set of tables.
</item>
</list>
<p>For information about QLC, see the
<seealso marker="stdlib:qlc">qlc</seealso> manual page
- in <c>STDLIB</c>.</p>
+ in STDLIB.</p>
</section>
<section>
<title>When to Use Mnesia</title>
- <p>Use <c>Mnesia</c> with the following types of applications:</p>
+ <p>Use Mnesia with the following types of applications:</p>
<list type="bulleted">
<item>Applications that need to replicate data.
</item>
@@ -166,7 +166,7 @@
<item>Applications that use soft real-time characteristics.
</item>
</list>
- <p><c>Mnesia</c> is not as appropriate with the
+ <p>Mnesia is not as appropriate with the
following types of applications:</p>
<list type="bulleted">
<item>Programs that process plain text or binary data files.
@@ -176,14 +176,14 @@
library module <c>dets</c>, which is a disc-based version
of the module <c>ets</c>. For information about <c>dets</c>,
see the <seealso marker="stdlib:dets">dets</seealso>
- manual page in <c>STDLIB</c>.
+ manual page in STDLIB.
</item>
<item>Applications that need disc logging facilities.
Those applications can
use the module <c>disk_log</c> by preference. For
information about <c>disk_log</c>, see the
<seealso marker="kernel:disk_log">disk_log</seealso>
- manual page in <c>Kernel</c>.
+ manual page in Kernel.
</item>
<item>Hard real-time systems.
</item>
diff --git a/lib/mnesia/doc/src/mnesia.xml b/lib/mnesia/doc/src/mnesia.xml
index b35214cde9..621b6047ee 100644
--- a/lib/mnesia/doc/src/mnesia.xml
+++ b/lib/mnesia/doc/src/mnesia.xml
@@ -37,7 +37,7 @@
<description>
<p>The following are some of the most important and attractive
- capabilities provided by <c>Mnesia</c>:</p>
+ capabilities provided by Mnesia:</p>
<list type="bulleted">
<item>A relational/object hybrid data model that is suitable
for telecommunications applications.
@@ -62,15 +62,15 @@
reconfigured at runtime without stopping the system.
</item>
</list>
- <p>This Reference Manual describes the <c>Mnesia</c> API. This
- includes functions that define and manipulate <c>Mnesia</c>
+ <p>This Reference Manual describes the Mnesia API. This
+ includes functions that define and manipulate Mnesia
tables.</p>
<p>All functions in this Reference Manual can be used in any
combination with queries using the list comprehension notation.
For information about the query notation, see the
<seealso marker="stdlib:qlc">qlc</seealso>
- manual page in <c>STDLIB</c>.</p>
- <p>Data in <c>Mnesia</c> is organized as a set of tables. Each table
+ manual page in STDLIB.</p>
+ <p>Data in Mnesia is organized as a set of tables. Each table
has a name that must be an atom. Each table is made up of
Erlang records. The user is responsible for the record
definitions. Each table also has a set of properties. The
@@ -121,14 +121,14 @@
<item>
<p><c>index</c>. This is a list of attribute names, or
integers, which specify the tuple positions on which
- <c>Mnesia</c> is to build and maintain an extra index
+ Mnesia is to build and maintain an extra index
table.</p>
</item>
<item>
<p><c>local_content</c>. When an application requires
tables whose contents are local to each node,
<c>local_content</c> tables can be used. The table name
- is known to all <c>Mnesia</c> nodes, but its content is
+ is known to all Mnesia nodes, but its content is
unique on each node. This means that access to such a table
must be done locally. Set field <c>local_content</c> to
<c>true</c> to enable the <c>local_content</c>
@@ -143,7 +143,7 @@
avoid inconsistencies because of network splits.</p>
</item>
<item>
- <p><c>snmp</c>. Each (set-based) <c>Mnesia</c> table can be
+ <p><c>snmp</c>. Each (set-based) Mnesia table can be
automatically turned into a Simple Network Management
Protocol (SNMP) ordered table as well.
This property specifies the types of the SNMP keys.</p>
@@ -174,7 +174,7 @@
copy of each modified record during the transaction. During
iteration, that is, <c>mnesia:fold[lr]/4</c>,
<c>mnesia:next/2</c>, <c>mnesia:prev/2</c>, and
- <c>mnesia:snmp_get_next_index/2</c>, <c>Mnesia</c>
+ <c>mnesia:snmp_get_next_index/2</c>, Mnesia
compensates for every written or deleted record, which can
reduce the performance.</p>
<p>If possible, avoid writing or deleting records in the same
@@ -188,7 +188,7 @@
<desc>
<p>Makes the transaction silently
return the tuple <c>{aborted, Reason}</c>.
- Termination of a <c>Mnesia</c> transaction means that
+ Termination of a Mnesia transaction means that
an exception is thrown to an enclosing <c>catch</c>.
Thus, the expression <c>catch mnesia:abort(x)</c> does
not terminate the transaction.</p>
@@ -324,7 +324,7 @@
<c>mnesia:ets</c>. Argument <c>AccessMod</c>
is the name of a callback module, which implements the
<c>mnesia_access</c> behavior.</p>
- <p><c>Mnesia</c> forwards calls to the following functions:</p>
+ <p>Mnesia forwards calls to the following functions:</p>
<list type="bulleted">
<item>mnesia:lock/2 (read_lock_table/1, write_lock_table/1)
</item>
@@ -391,15 +391,15 @@
</item>
</list>
<p><c>ActivityId</c> is a record that represents the identity
- of the enclosing <c>Mnesia</c> activity. The first field
+ of the enclosing Mnesia activity. The first field
(obtained with <c>element(1, ActivityId)</c>) contains an
atom, which can be interpreted as the activity type:
<c>ets</c>, <c>async_dirty</c>, <c>sync_dirty</c>, or
<c>tid</c>. <c>tid</c> means that the activity is a
transaction. The structure of the rest of the identity
- record is internal to <c>Mnesia</c>.</p>
+ record is internal to Mnesia.</p>
<p><c>Opaque</c> is an opaque data structure that is internal
- to <c>Mnesia</c>.</p>
+ to Mnesia.</p>
</desc>
</func>
<func>
@@ -458,7 +458,7 @@ mnesia:add_table_index(person, age)</code>
<desc>
<marker id="async_dirty"></marker>
<p>Calls the <c>Fun</c> in a context that is not protected by
- a transaction. The <c>Mnesia</c> function calls performed in
+ a transaction. The Mnesia function calls performed in
the <c>Fun</c> are mapped to the corresponding dirty
functions. This still involves logging, replication, and
subscriptions, but there is no locking, local transaction
@@ -467,7 +467,7 @@ mnesia:add_table_index(person, age)</code>
for normal <c>mnesia:dirty_*</c> operations, the operations
are performed semi-asynchronously. For details, see
<c>mnesia:activity/4</c> and the User's Guide.</p>
- <p>The <c>Mnesia</c> tables can be manipulated without
+ <p>The Mnesia tables can be manipulated without
using transactions. This has some serious disadvantages, but
is considerably faster, as the transaction manager is not
involved and no locks are set. A dirty operation does,
@@ -480,7 +480,7 @@ mnesia:add_table_index(person, age)</code>
read records dirty than within a transaction.</p>
<p>Depending on the application, it can be a good idea to use
the dirty functions for certain operations. Almost all
- <c>Mnesia</c> functions that can be called within
+ Mnesia functions that can be called within
transactions have a dirty equivalent, which is much more
efficient.</p>
<p>However, notice that there is a risk that the database can
@@ -497,7 +497,7 @@ mnesia:add_table_index(person, age)</code>
<fsummary>Backs up all tables in the database.</fsummary>
<desc>
<marker id="backup"></marker>
- <p>Activates a new checkpoint covering all <c>Mnesia</c> tables,
+ <p>Activates a new checkpoint covering all Mnesia tables,
including the schema, with maximum degree of redundancy, and
performs a backup using <c>backup_checkpoint/2/3</c>. The
default value of the backup callback module <c>BackupMod</c>
@@ -529,16 +529,16 @@ mnesia:add_table_index(person, age)</code>
<taglist>
<tag><c>extra_db_nodes</c></tag>
<item>
- <p><c>Value</c> is a list of nodes that <c>Mnesia</c>
+ <p><c>Value</c> is a list of nodes that Mnesia
is to try to connect to. <c>ReturnValue</c> is those
- nodes in <c>Value</c> that <c>Mnesia</c> is connected
+ nodes in <c>Value</c> that Mnesia is connected
to.</p>
<p>Notice that this function must only be used to connect
to newly started RAM nodes (N.D.R.S.N.) with an empty
schema. If, for example, this function is used after
the network has been partitioned, it can lead to
inconsistent tables.</p>
- <p>Notice that <c>Mnesia</c> can be connected to other
+ <p>Notice that Mnesia can be connected to other
nodes than those returned in <c>ReturnValue</c>.</p>
</item>
<tag><c>dc_dump_limit</c></tag>
@@ -548,7 +548,7 @@ mnesia:add_table_index(person, age)</code>
Configuration Parameters</seealso>. <c>ReturnValue</c>
is the new value. Notice that this configuration
parameter is not persistent. It is lost when
- <c>Mnesia</c> has stopped.</p>
+ Mnesia has stopped.</p>
</item>
</taglist>
</desc>
@@ -562,9 +562,9 @@ mnesia:add_table_index(person, age)</code>
<c>read_write</c> but it can also be set to the atom
<c>read_only</c>. If <c>AccessMode</c> is set to
<c>read_only</c>, updates to the table cannot be
- performed. At startup, <c>Mnesia</c> always loads
+ performed. At startup, Mnesia always loads
<c>read_only</c> tables locally regardless of when and if
- <c>Mnesia</c> is terminated on other nodes.</p>
+ Mnesia is terminated on other nodes.</p>
</desc>
</func>
<func>
@@ -620,13 +620,13 @@ mnesia:change_table_copy_type(person, node(), disc_copies)</code>
<desc>
<marker id="create_schema"></marker>
<p>Creates a new database on disc. Various files are
- created in the local <c>Mnesia</c> directory of each node.
+ created in the local Mnesia directory of each node.
Notice that the directory must be unique for each node.
Two nodes must never share the same directory. If possible,
use a local disc device to improve performance.</p>
<p><c>mnesia:create_schema/1</c> fails if any of the
Erlang nodes given as <c>DiscNodes</c> are not alive, if
- <c>Mnesia</c> is running on any of the nodes, or if any
+ Mnesia is running on any of the nodes, or if any
of the nodes already have a schema. Use
<c>mnesia:delete_schema/1</c> to get rid of old faulty
schemas.</p>
@@ -638,10 +638,10 @@ mnesia:change_table_copy_type(person, node(), disc_copies)</code>
</func>
<func>
<name>create_table(Name, TabDef) -> {atomic, ok} | {aborted, Reason}</name>
- <fsummary>Creates a <c>Mnesia</c> table called <c>Name</c>with properties as described by argument <c>TabDef</c>.</fsummary>
+ <fsummary>Creates a Mnesia table called <c>Name</c>with properties as described by argument <c>TabDef</c>.</fsummary>
<desc>
<marker id="create_table"></marker>
- <p>Creates a <c>Mnesia</c> table called
+ <p>Creates a Mnesia table called
<c>Name</c> according to argument <c>TabDef</c>. This
list must be a list of <c>{Item, Value}</c> tuples,
where the following values are allowed:</p>
@@ -652,8 +652,8 @@ mnesia:change_table_copy_type(person, node(), disc_copies)</code>
set to the atom <c>read_only</c>. If <c>AccessMode</c>
is set to <c>read_only</c>, updates to the table
cannot be performed.</p>
- <p>At startup, <c>Mnesia</c> always loads <c>read_only</c>
- table locally regardless of when and if <c>Mnesia</c> is
+ <p>At startup, Mnesia always loads <c>read_only</c>
+ table locally regardless of when and if Mnesia is
terminated on other nodes. This argument returns the
access mode of the table. The access mode can be
<c>read_only</c> or <c>read_write</c>.</p>
@@ -693,7 +693,7 @@ mnesia:change_table_copy_type(person, node(), disc_copies)</code>
<item>
<p><c>{index, Intlist}</c>, where
<c>Intlist</c> is a list of attribute names (atoms) or
- record fields for which <c>Mnesia</c> is to build and
+ record fields for which Mnesia is to build and
maintain an extra index table. The <c>qlc</c> query
compiler <em>may</em> be able to optimize queries
if there are indexes available.</p>
@@ -735,10 +735,10 @@ mnesia:change_table_copy_type(person, node(), disc_copies)</code>
<c>mnesia:create_table/2</c>, the table is immediately
accessible by SNMP. Therefore applications that use
SNMP to manipulate and control the system can be
- designed easily, since <c>Mnesia</c> provides a
+ designed easily, since Mnesia provides a
direct mapping between the logical tables that make up
an SNMP control application and the physical data that
- makes up a <c>Mnesia</c> table.</p>
+ makes up a Mnesia table.</p>
</item>
<item>
<p><c>{storage_properties, [{Backend, Properties}]</c>
@@ -746,7 +746,7 @@ mnesia:change_table_copy_type(person, node(), disc_copies)</code>
<c>Backend</c> can currently be <c>ets</c> or <c>dets</c>.
<c>Properties</c> is a list of options sent to the
back end storage during table creation. <c>Properties</c>
- cannot contain properties already used by <c>Mnesia</c>,
+ cannot contain properties already used by Mnesia,
such as <c>type</c> or <c>named_table</c>.</p>
<p>For example:</p>
<code type="none">
@@ -776,7 +776,7 @@ mnesia:create_table(table, [{ram_copies, [node()]}, {disc_only_copies, nodes()},
mnesia:create_table(person,
[{ram_copies, [N1, N2]},
{attributes, record_info(fields, person)}]).</code>
- <p>If it is required that <c>Mnesia</c> must build and
+ <p>If it is required that Mnesia must build and
maintain an extra index table on attribute <c>address</c>
of all the <c>person</c> records that are inserted in the
table, the following code would be issued:</p>
@@ -819,8 +819,8 @@ mnesia:create_table(person,
When the last replica is deleted with this
function, the table disappears entirely.</p>
<p>This function can also be used to delete a replica of
- the table named <c>schema</c>. The <c>Mnesia</c> node is
- then removed. Notice that <c>Mnesia</c> must be
+ the table named <c>schema</c>. The Mnesia node is
+ then removed. Notice that Mnesia must be
stopped on the node first.</p>
</desc>
</func>
@@ -891,9 +891,9 @@ mnesia:create_table(person,
<c>mnesia:create_schema/1</c>.
<c>mnesia:delete_schema/1</c> fails if any of the Erlang
nodes given as <c>DiscNodes</c> are not alive, or if
- <c>Mnesia</c> is running on any of the nodes.</p>
+ Mnesia is running on any of the nodes.</p>
<p>After the database is deleted, it can still be possible
- to start <c>Mnesia</c> as a disc-less node. This depends
+ to start Mnesia as a disc-less node. This depends
on how configuration parameter <c>schema_location</c> is
set.</p>
<warning>
@@ -1100,7 +1100,7 @@ mnesia:create_table(person,
<name>dirty_update_counter(Tab, Key, Incr) -> NewVal | exit({aborted, Reason})</name>
<fsummary>Dirty update of a counter record.</fsummary>
<desc>
- <p><c>Mnesia</c> has no special counter records. However,
+ <p>Mnesia has no special counter records. However,
records of the form <c>{Tab, Key, Integer}</c> can be used
as (possibly disc-resident) counters when <c>Tab</c> is a
<c>set</c>. This function updates a counter with a positive
@@ -1147,7 +1147,7 @@ mnesia:create_table(person,
<desc>
<marker id="dump_log"></marker>
<p>Performs a user-initiated dump of the local log file.
- This is usually not necessary, as <c>Mnesia</c> by default
+ This is usually not necessary, as Mnesia by default
manages this automatically. See configuration parameters
<seealso marker="#dump_log_time_threshold">dump_log_time_threshold</seealso>
and
@@ -1172,7 +1172,7 @@ mnesia:create_table(person,
<fsummary>Dumps local tables into a text file.</fsummary>
<desc>
<marker id="dump_to_textfile"></marker>
- <p>Dumps all local tables of a <c>Mnesia</c> system into a
+ <p>Dumps all local tables of a Mnesia system into a
text file, which can be edited (by a normal text editor)
and then be reloaded with
<c>mnesia:load_textfile/1</c>. Only use this function for
@@ -1182,10 +1182,10 @@ mnesia:create_table(person,
</func>
<func>
<name>error_description(Error) -> String</name>
- <fsummary>Returns a string describing a particular <c>Mnesia</c> error.</fsummary>
+ <fsummary>Returns a string describing a particular Mnesia error.</fsummary>
<desc>
<marker id="error_description"></marker>
- <p>All <c>Mnesia</c> transactions, including all the schema
+ <p>All Mnesia transactions, including all the schema
update functions, either return value <c>{atomic, Val}</c>
or the tuple <c>{aborted, Reason}</c>. <c>Reason</c> can
be either of the atoms in the following list. The
@@ -1264,8 +1264,8 @@ mnesia:create_table(person,
<desc>
<marker id="ets"></marker>
<p>Calls the <c>Fun</c> in a raw context that is not protected by
- a transaction. The <c>Mnesia</c> function call is performed in
- the <c>Fun</c> and performed directly on the local <c>ets</c>
+ a transaction. The Mnesia function call is performed in
+ the <c>Fun</c> and performed directly on the local ETS
tables on the assumption that the local storage type is
<c>ram_copies</c> and the tables are not replicated to other
nodes. Subscriptions are not triggered and checkpoints are
@@ -1321,13 +1321,13 @@ mnesia:create_table(person,
<fsummary>Forces a table to be loaded into the system.</fsummary>
<desc>
<marker id="force_load_table"></marker>
- <p>The <c>Mnesia</c> algorithm for table load can lead to a
+ <p>The Mnesia algorithm for table load can lead to a
situation where a table cannot be loaded. This situation
- occurs when a node is started and <c>Mnesia</c> concludes, or
+ occurs when a node is started and Mnesia concludes, or
suspects, that another copy of the table was active after
this local copy became inactive because of a system crash.</p>
<p>If this situation is not acceptable, this function can be
- used to override the strategy of the <c>Mnesia</c> table
+ used to override the strategy of the Mnesia table
load algorithm. This can lead to a situation where some
transaction effects are lost with an inconsistent database as
result, but for some applications high availability is more
@@ -1402,9 +1402,9 @@ mnesia:create_table(person,
<desc>
<marker id="info"></marker>
<p>Prints system information on the terminal.
- This function can be used even if <c>Mnesia</c> is not
+ This function can be used even if Mnesia is not
started. However, more information is displayed if
- <c>Mnesia</c> is started.</p>
+ Mnesia is started.</p>
</desc>
</func>
<func>
@@ -1431,7 +1431,7 @@ mnesia:create_table(person,
<p>Installs a backup as fallback. The fallback is used to
restore the database at the next startup. Installation of
fallbacks requires Erlang to be operational on all the
- involved nodes, but it does not matter if <c>Mnesia</c>
+ involved nodes, but it does not matter if Mnesia
is running or not. The installation of the fallback fails
if the local node is not one of the disc-resident nodes
in the backup.</p>
@@ -1465,14 +1465,14 @@ mnesia:create_table(person,
<p><c>{mnesia_dir, AlternateDir}</c>.
This argument is only valid if the scope of the
installation is <c>local</c>. Normally the installation
- of a fallback is targeted to the <c>Mnesia</c> directory,
+ of a fallback is targeted to the Mnesia directory,
as configured with configuration parameter
<c>-mnesia dir</c>. But by explicitly supplying an
<c>AlternateDir</c>, the fallback is installed there
- regardless of the <c>Mnesia</c> directory configuration
+ regardless of the Mnesia directory configuration
parameter setting. After installation of a fallback on
- an alternative <c>Mnesia</c> directory, that directory
- is fully prepared for use as an active <c>Mnesia</c>
+ an alternative Mnesia directory, that directory
+ is fully prepared for use as an active Mnesia
directory.</p>
<p>This is a dangerous feature that must be
used with care. By unintentional mixing of directories,
@@ -1509,7 +1509,7 @@ mnesia:create_table(person,
<marker id="load_textfile"></marker>
<p>Loads a series of definitions and data found in the
text file (generated with <c>mnesia:dump_to_textfile/1</c>)
- into <c>Mnesia</c>. This function also starts <c>Mnesia</c>
+ into Mnesia. This function also starts Mnesia
and possibly creates a new schema. This function is
intended for educational purposes only. It is recommended
to use other functions to deal with real backups.</p>
@@ -1558,7 +1558,7 @@ mnesia:create_table(person,
</taglist>
<p>Conflicting lock requests are automatically queued if there
is no risk of a deadlock. Otherwise the transaction must be
- terminated and executed again. <c>Mnesia</c> does this
+ terminated and executed again. Mnesia does this
automatically as long as the upper limit of the maximum
<c>retries</c> is not reached. For details, see
<c>mnesia:transaction/3</c>.</p>
@@ -1726,19 +1726,19 @@ mnesia:create_table(person,
</func>
<func>
<name>report_event(Event) -> ok</name>
- <fsummary>Reports a user event to the <c>Mnesia</c> event handler.</fsummary>
+ <fsummary>Reports a user event to the Mnesia event handler.</fsummary>
<desc>
<marker id="report_event"></marker>
- <p>When tracing a system of <c>Mnesia</c> applications it is
- useful to be able to interleave <c>Mnesia</c> own events with
+ <p>When tracing a system of Mnesia applications it is
+ useful to be able to interleave Mnesia own events with
application-related events that give information about the
application context.</p>
<p>Whenever the application begins a
- new and demanding <c>Mnesia</c> task, or if it enters a new
+ new and demanding Mnesia task, or if it enters a new
interesting phase in its execution, it can be a good idea to
use <c>mnesia:report_event/1</c>. <c>Event</c> can be
any term and generates a <c>{mnesia_user, Event}</c> event
- for any processes that subscribe to <c>Mnesia</c> system
+ for any processes that subscribe to Mnesia system
events.</p>
</desc>
</func>
@@ -1748,7 +1748,7 @@ mnesia:create_table(person,
<desc>
<marker id="restore"></marker>
<p>With this function, tables can be restored online from a
- backup without restarting <c>Mnesia</c>.
+ backup without restarting Mnesia.
<c>Opaque</c> is forwarded to the backup module.
<c>Args</c> is a list of the following tuples:</p>
<list type="bulleted">
@@ -1873,7 +1873,7 @@ mnesia:create_table(person,
<p>For a complete description of <c>select</c>, see the
<seealso marker="erts:index">ERTS</seealso> User's Guide and the
<seealso marker="stdlib:ets">ets</seealso> manual page in
- <c>STDLIB</c>.</p>
+ STDLIB.</p>
<p>For example, to find the names of all male persons older
than 30 in table <c>Tab</c>:</p>
<code type="none">
@@ -1920,10 +1920,10 @@ mnesia:select(Tab,[{MatchHead, [Guard], [Result]}]),</code>
</func>
<func>
<name>set_debug_level(Level) -> OldLevel</name>
- <fsummary>Changes the internal debug level of <c>Mnesia</c>.</fsummary>
+ <fsummary>Changes the internal debug level of Mnesia.</fsummary>
<desc>
<marker id="set_debug_level"></marker>
- <p>Changes the internal debug level of <c>Mnesia</c>.
+ <p>Changes the internal debug level of Mnesia.
For details, see
<seealso marker="#configuration_parameters">Section
Configuration Parameters</seealso>.</p>
@@ -1934,7 +1934,7 @@ mnesia:select(Tab,[{MatchHead, [Guard], [Result]}]),</code>
<fsummary>Sets the master nodes for all tables.</fsummary>
<desc>
<marker id="set_master_nodes_1"></marker>
- <p>For each table <c>Mnesia</c> determines its replica nodes
+ <p>For each table Mnesia determines its replica nodes
(<c>TabNodes</c>) and starts
<c>mnesia:set_master_nodes(Tab, TabMasterNodes)</c>. where
<c>TabMasterNodes</c> is the intersection of
@@ -1952,16 +1952,16 @@ mnesia:select(Tab,[{MatchHead, [Guard], [Result]}]),</code>
that can have caused an inconsistent database, it can use the
function <c>mnesia:set_master_nodes(Tab, MasterNodes)</c> to
define from which nodes each table is to be loaded.
- At startup, the <c>Mnesia</c> normal table load algorithm is
+ At startup, the Mnesia normal table load algorithm is
bypassed and the table is loaded from one of the master nodes
- defined for the table, regardless of when and if <c>Mnesia</c>
+ defined for the table, regardless of when and if Mnesia
terminated on other nodes. <c>MasterNodes</c> can only
contain nodes where the table has a replica. If the
<c>MasterNodes</c> list is empty, the master node recovery
mechanism for the particular table is reset, and the
normal load mechanism is used at the next restart.</p>
<p>The master node setting is always local. It can be
- changed regardless if <c>Mnesia</c> is started or not.</p>
+ changed regardless if Mnesia is started or not.</p>
<p>The database can also become inconsistent if
configuration parameter <c>max_wait_for_decision</c> is used
or if <c>mnesia:force_load_table/1</c> is used.</p>
@@ -1976,7 +1976,7 @@ mnesia:select(Tab,[{MatchHead, [Guard], [Result]}]),</code>
</func>
<func>
<name>snmp_get_mnesia_key(Tab, RowIndex) -> {ok, Key} | undefined</name>
- <fsummary>Gets the corresponding <c>Mnesia</c> key from an SNMP index.</fsummary>
+ <fsummary>Gets the corresponding Mnesia key from an SNMP index.</fsummary>
<type>
<v>Tab ::= atom()</v>
<v>RowIndex ::= [integer()]</v>
@@ -1984,7 +1984,7 @@ mnesia:select(Tab,[{MatchHead, [Guard], [Result]}]),</code>
<v>key() ::= integer() | string() | [integer()]</v>
</type>
<desc>
- <p>Transforms an SNMP index to the corresponding <c>Mnesia</c>
+ <p>Transforms an SNMP index to the corresponding Mnesia
key. If the SNMP table has multiple keys, the key is a tuple
of the key columns.</p>
</desc>
@@ -2020,7 +2020,7 @@ mnesia:select(Tab,[{MatchHead, [Guard], [Result]}]),</code>
</func>
<func>
<name>snmp_open_table(Tab, SnmpStruct) -> {aborted, R} | {atomic, ok}</name>
- <fsummary>Organizes a <c>Mnesia</c> table as an SNMP table.</fsummary>
+ <fsummary>Organizes a Mnesia table as an SNMP table.</fsummary>
<type>
<v>Tab ::= atom()</v>
<v>SnmpStruct ::= [{key, type()}]</v>
@@ -2029,14 +2029,14 @@ mnesia:select(Tab,[{MatchHead, [Guard], [Result]}]),</code>
</type>
<desc>
<p>A direct one-to-one mapping can be established between
- <c>Mnesia</c> tables and SNMP tables. Many telecommunication
+ Mnesia tables and SNMP tables. Many telecommunication
applications are controlled and monitored by the SNMP
- protocol. This connection between <c>Mnesia</c> and SNMP
+ protocol. This connection between Mnesia and SNMP
makes it simple and convenient to achieve this mapping.</p>
<p>Argument <c>SnmpStruct</c> is a list of SNMP
information. Currently, the only information needed is
information about the key types in the table. Multiple
- keys cannot be handled in <c>Mnesia</c>, but many SNMP
+ keys cannot be handled in Mnesia, but many SNMP
tables have multiple keys. Therefore, the following
convention is used: if a table has multiple keys, these must
always be stored as a tuple of the keys. Information about
@@ -2069,39 +2069,39 @@ mnesia:create_table(employee,
<p>When a table is SNMP ordered, modifications are more
expensive than usual, O(logN). Also, more memory is used.</p>
<p>Notice that only the lexicographical SNMP ordering is
- implemented in <c>Mnesia</c>, not the actual SNMP monitoring.</p>
+ implemented in Mnesia, not the actual SNMP monitoring.</p>
</desc>
</func>
<func>
<name>start() -> ok | {error, Reason}</name>
- <fsummary>Starts a local <c>Mnesia</c> system.</fsummary>
+ <fsummary>Starts a local Mnesia system.</fsummary>
<desc>
<marker id="start"></marker>
- <p>The startup procedure for a set of <c>Mnesia</c> nodes is a
- fairly complicated operation. A <c>Mnesia</c> system consists
- of a set of nodes, with <c>Mnesia</c> started locally on all
+ <p>The startup procedure for a set of Mnesia nodes is a
+ fairly complicated operation. A Mnesia system consists
+ of a set of nodes, with Mnesia started locally on all
participating nodes. Normally, each node has a directory where
- all the <c>Mnesia</c> files are written. This directory is
- referred to as the <c>Mnesia</c> directory. <c>Mnesia</c> can
+ all the Mnesia files are written. This directory is
+ referred to as the Mnesia directory. Mnesia can
also be started on disc-less nodes. For more information
about disc-less nodes, see <c>mnesia:create_schema/1</c>
and the User's Guide.</p>
- <p>The set of nodes that makes up a <c>Mnesia</c> system is kept
- in a schema. <c>Mnesia</c> nodes can be added to or removed
+ <p>The set of nodes that makes up a Mnesia system is kept
+ in a schema. Mnesia nodes can be added to or removed
from the schema. The initial schema is normally created on
disc with the function <c>mnesia:create_schema/1</c>. On
disc-less nodes, a tiny default schema is generated each time
- <c>Mnesia</c> is started. During the startup procedure,
- <c>Mnesia</c> exchanges schema information between the nodes
+ Mnesia is started. During the startup procedure,
+ Mnesia exchanges schema information between the nodes
to verify that the table definitions are compatible.</p>
<p>Each schema has a unique cookie, which can be regarded as a
unique schema identifier. The cookie must be the same on all
- nodes where <c>Mnesia</c> is supposed to run. For details,
+ nodes where Mnesia is supposed to run. For details,
see the User's Guide.</p>
- <p>The schema file and all other files that <c>Mnesia</c>
- needs are kept in the <c>Mnesia</c> directory. The
+ <p>The schema file and all other files that Mnesia
+ needs are kept in the Mnesia directory. The
command-line option <c>-mnesia dir Dir</c> can be used to
- specify the location of this directory to the <c>Mnesia</c>
+ specify the location of this directory to the Mnesia
system. If no such command-line option is found, the name
of the directory defaults to <c>Mnesia.Node</c>.</p>
<p><c>application:start(mnesia)</c> can also be used.</p>
@@ -2109,10 +2109,10 @@ mnesia:create_table(employee,
</func>
<func>
<name>stop() -> stopped</name>
- <fsummary>Stops <c>Mnesia</c> locally.</fsummary>
+ <fsummary>Stops Mnesia locally.</fsummary>
<desc>
<marker id="stop"></marker>
- <p>Stops <c>Mnesia</c> locally on the current node.</p>
+ <p>Stops Mnesia locally on the current node.</p>
<p><c>application:stop(mnesia)</c> can also be used.</p>
</desc>
</func>
@@ -2132,7 +2132,7 @@ mnesia:create_table(employee,
<desc>
<marker id="sync_dirty"></marker>
<p>Calls the <c>Fun</c> in a context that is not protected by
- a transaction. The <c>Mnesia</c> function calls performed in
+ a transaction. The Mnesia function calls performed in
the <c>Fun</c> are mapped to the corresponding dirty functions.
It is performed in almost the same context as
<c>mnesia:async_dirty/1,2</c>. The difference is that the
@@ -2167,10 +2167,10 @@ mnesia:create_table(employee,
</func>
<func>
<name>system_info(InfoKey) -> Info | exit({aborted, Reason})</name>
- <fsummary>Returns information about the <c>Mnesia</c> system.</fsummary>
+ <fsummary>Returns information about the Mnesia system.</fsummary>
<desc>
<marker id="system_info"></marker>
- <p>Returns information about the <c>Mnesia</c> system, such as
+ <p>Returns information about the Mnesia system, such as
transaction statistics, <c>db_nodes</c>, and configuration
parameters. The valid keys are as follows:</p>
<list type="bulleted">
@@ -2188,7 +2188,7 @@ mnesia:create_table(employee,
</item>
<item>
<p><c>auto_repair</c>. Returns <c>true</c> or <c>false</c>
- to indicate if <c>Mnesia</c> is configured to start the
+ to indicate if Mnesia is configured to start the
auto-repair facility on corrupted disc files.</p>
</item>
<item>
@@ -2209,20 +2209,20 @@ mnesia:create_table(employee,
in the list of nodes if they explicitly have been added
to the schema, for example, with
<c>mnesia:add_table_copy/3</c>. The function can be
- started even if <c>Mnesia</c> is not yet running.</p>
+ started even if Mnesia is not yet running.</p>
</item>
<item>
<p><c>debug</c>. Returns the current debug level of
- <c>Mnesia</c>.</p>
+ Mnesia.</p>
</item>
<item>
- <p><c>directory</c>. Returns the name of the <c>Mnesia</c>
- directory. It can be called even if <c>Mnesia</c> is
+ <p><c>directory</c>. Returns the name of the Mnesia
+ directory. It can be called even if Mnesia is
not yet running.</p>
</item>
<item>
<p><c>dump_log_load_regulation</c>. Returns a boolean that
- tells if <c>Mnesia</c> is configured to regulate the
+ tells if Mnesia is configured to regulate the
dumper process load.</p>
<p>This feature is temporary and will be removed in future
releases.</p>
@@ -2233,9 +2233,9 @@ mnesia:create_table(employee,
</item>
<item>
<p><c>dump_log_update_in_place</c>. Returns a boolean that
- tells if <c>Mnesia</c> is configured to perform the
- updates in the <c>dets</c> files directly, or if the
- updates are to be performed in a copy of the <c>dets</c>
+ tells if Mnesia is configured to perform the
+ updates in the Dets files directly, or if the
+ updates are to be performed in a copy of the Dets
files.</p>
</item>
<item>
@@ -2253,13 +2253,13 @@ mnesia:create_table(employee,
</item>
<item>
<p><c>held_locks</c>. Returns a list of all
- locks held by the local <c>Mnesia</c> lock manager.</p>
+ locks held by the local Mnesia lock manager.</p>
</item>
<item>
<p><c>is_running</c>. Returns <c>yes</c> or <c>no</c> to
- indicate if <c>Mnesia</c> is running. It can
+ indicate if Mnesia is running. It can
also return <c>starting</c> or <c>stopping</c>. Can be
- called even if <c>Mnesia</c> is not yet running.</p>
+ called even if Mnesia is not yet running.</p>
</item>
<item>
<p><c>local_tables</c>. Returns a list
@@ -2272,7 +2272,7 @@ mnesia:create_table(employee,
</item>
<item>
<p><c>log_version</c>. Returns the version
- number of the <c>Mnesia</c> transaction log format.</p>
+ number of the Mnesia transaction log format.</p>
</item>
<item>
<p><c>master_node_tables</c>. Returns a
@@ -2280,25 +2280,25 @@ mnesia:create_table(employee,
</item>
<item>
<p><c>protocol_version</c>. Returns the version number of
- the <c>Mnesia</c> inter-process communication protocol.</p>
+ the Mnesia inter-process communication protocol.</p>
</item>
<item>
<p><c>running_db_nodes</c>. Returns a list of nodes where
- <c>Mnesia</c> currently is running. This function can be
- called even if <c>Mnesia</c> is not yet running, but it
+ Mnesia currently is running. This function can be
+ called even if Mnesia is not yet running, but it
then has slightly different semantics.</p>
- <p>If <c>Mnesia</c> is down on the local node, the function
+ <p>If Mnesia is down on the local node, the function
returns those other <c>db_nodes</c> and
<c>extra_db_nodes</c> that for the moment are
operational.</p>
- <p>If <c>Mnesia</c> is started, the function returns
- those nodes that <c>Mnesia</c> on the local node is fully
- connected to. Only those nodes that <c>Mnesia</c> has
+ <p>If Mnesia is started, the function returns
+ those nodes that Mnesia on the local node is fully
+ connected to. Only those nodes that Mnesia has
exchanged schema information with are included as
<c>running_db_nodes</c>. After the merge of schemas, the
- local <c>Mnesia</c> system is fully operable and
+ local Mnesia system is fully operable and
applications can perform access of remote replicas.
- Before the schema merge, <c>Mnesia</c> only operates
+ Before the schema merge, Mnesia only operates
locally. Sometimes there are more nodes included in the
<c>running_db_nodes</c> list than all <c>db_nodes</c>
and <c>extra_db_nodes</c> together.</p>
@@ -2322,17 +2322,17 @@ mnesia:create_table(employee,
<item>
<p><c>transaction_failures</c>. Returns a
number that indicates how many transactions have
- failed since <c>Mnesia</c> was started.</p>
+ failed since Mnesia was started.</p>
</item>
<item>
<p><c>transaction_commits</c>. Returns a
number that indicates how many transactions have
- terminated successfully since <c>Mnesia</c> was started.</p>
+ terminated successfully since Mnesia was started.</p>
</item>
<item>
<p><c>transaction_restarts</c>. Returns a
number that indicates how many transactions have been
- restarted since <c>Mnesia</c> was started.</p>
+ restarted since Mnesia was started.</p>
</item>
<item>
<p><c>transaction_log_writes</c>.
@@ -2342,12 +2342,12 @@ mnesia:create_table(employee,
</item>
<item>
<p><c>use_dir</c>. Returns a boolean that indicates if
- the <c>Mnesia</c> directory is used or not. Can be
- started even if <c>Mnesia</c> is not yet running.</p>
+ the Mnesia directory is used or not. Can be
+ started even if Mnesia is not yet running.</p>
</item>
<item>
<p><c>version</c>. Returns the current
- version number of <c>Mnesia</c>.</p>
+ version number of Mnesia.</p>
</item>
</list>
</desc>
@@ -2359,13 +2359,13 @@ mnesia:create_table(employee,
<marker id="table"></marker>
<p>Returns a Query List Comprehension (QLC) query handle,
see the <seealso marker="stdlib:qlc">qlc(3)</seealso>
- manual page in <c>STDLIB</c>. The module <c>qlc</c>
- implements a query language that can use <c>Mnesia</c>
+ manual page in STDLIB. The module <c>qlc</c>
+ implements a query language that can use Mnesia
tables as sources of data. Calling
<c>mnesia:table/1,2</c> is the means to make the
<c>mnesia</c> table <c>Tab</c> usable to QLC.</p>
- <p><c>Option</c> can contain <c>Mnesia</c>
- options or QLC options. <c>Mnesia</c> recognizes the
+ <p><c>Option</c> can contain Mnesia
+ options or QLC options. Mnesia recognizes the
following options (any other option is forwarded to
QLC).</p>
<list type="bulleted">
@@ -2374,7 +2374,7 @@ mnesia:create_table(employee,
</item>
<item><c>{n_objects,Number}</c>, where <c>n_objects</c>
specifies (roughly) the number of objects returned
- from <c>Mnesia</c> to QLC. Queries to remote tables
+ from Mnesia to QLC. Queries to remote tables
can need a larger chunk to reduce network overhead.
By default, <c>100</c> objects at a time are returned.
</item>
@@ -2413,7 +2413,7 @@ mnesia:create_table(employee,
<desc>
<marker id="table_info"></marker>
<p>The <c>table_info/2</c> function takes two arguments.
- The first is the name of a <c>Mnesia</c> table.
+ The first is the name of a Mnesia table.
The second is one of the following keys:</p>
<list type="bulleted">
<item>
@@ -2465,7 +2465,7 @@ mnesia:create_table(employee,
</item>
<item>
<p><c>load_node</c>. Returns the name of
- the node that <c>Mnesia</c> loaded the table from. The
+ the node that Mnesia loaded the table from. The
structure of the returned value is unspecified, but
can be useful for debugging purposes.</p>
</item>
@@ -2476,7 +2476,7 @@ mnesia:create_table(employee,
</item>
<item>
<p><c>load_reason</c>. Returns the
- reason of why <c>Mnesia</c> decided to load the table.
+ reason of why Mnesia decided to load the table.
The structure of the returned value is unspecified, but
can be useful for debugging purposes.</p>
</item>
@@ -2620,7 +2620,7 @@ raise(Name, Amount) ->
several processes running on different nodes can concurrently
execute the function <c>raise/2</c> without interfering
with each other.</p>
- <p>Since <c>Mnesia</c> detects deadlocks, a transaction can be
+ <p>Since Mnesia detects deadlocks, a transaction can be
restarted any number of times. This function attempts a
restart as specified in <c>Retries</c>. <c>Retries</c> must
be an integer greater than 0 or the atom <c>infinity</c>.
@@ -2643,7 +2643,7 @@ raise(Name, Amount) ->
<p><c>NewAttributeList</c> and <c>NewRecordName</c>
specify the attributes and the new record type of the
converted table. Table name always remains unchanged. If
- <c>record_name</c> is changed, only the <c>Mnesia</c>
+ <c>record_name</c> is changed, only the Mnesia
functions that use table identifiers work, for example,
<c>mnesia:write/3</c> works, but not <c>mnesia:write/1</c>.</p>
</desc>
@@ -2706,7 +2706,7 @@ raise(Name, Amount) ->
distributed operation that is either performed on all
nodes with disc resident schema, or none. Uninstallation of
fallbacks requires Erlang to be operational on all
- involved nodes, but it does not matter if <c>Mnesia</c> is
+ involved nodes, but it does not matter if Mnesia is
running or not. Which nodes that are considered as
disc-resident nodes is determined from the schema
information in the local fallback.</p>
@@ -2792,28 +2792,28 @@ raise(Name, Amount) ->
<section>
<title>Configuration Parameters</title>
<marker id="configuration_parameters"></marker>
- <p><c>Mnesia</c> reads the following application configuration
+ <p>Mnesia reads the following application configuration
parameters:</p>
<list type="bulleted">
<item>
<p><c>-mnesia access_module Module</c>. The name of the
- <c>Mnesia</c> activity access callback module. Default is
+ Mnesia activity access callback module. Default is
<c>mnesia</c>.</p>
</item>
<item>
<p><c>-mnesia auto_repair true | false</c>. This flag
- controls if <c>Mnesia</c> automatically tries to repair
+ controls if Mnesia automatically tries to repair
files that have not been properly closed. Default is
<c>true</c>.</p>
</item>
<item>
<p><c>-mnesia backup_module Module</c>. The name of the
- <c>Mnesia</c> backup callback module. Default is
+ Mnesia backup callback module. Default is
<c>mnesia_backup</c>.</p>
</item>
<item>
<p><c>-mnesia debug Level</c>. Controls the debug level
- of <c>Mnesia</c>. The possible values are as follows:</p>
+ of Mnesia. The possible values are as follows:</p>
<taglist>
<tag><c>none</c></tag>
<item>
@@ -2825,7 +2825,7 @@ raise(Name, Amount) ->
events generate <c>{mnesia_info, Format, Args}</c>
system events. Processes can subscribe to these events with
<c>mnesia:subscribe/1</c>. The events are always sent to
- the <c>Mnesia</c> event handler.</p>
+ the Mnesia event handler.</p>
</item>
<tag><c>debug</c></tag>
<item>
@@ -2834,15 +2834,15 @@ raise(Name, Amount) ->
<c>{mnesia_info, Format, Args}</c> system events.
Processes can subscribe to these events with
<c>mnesia:subscribe/1</c>. The events are always sent to
- the <c>Mnesia</c> event handler. On this debug level,
- the <c>Mnesia</c> event handler starts subscribing to
+ the Mnesia event handler. On this debug level,
+ the Mnesia event handler starts subscribing to
updates in the schema table.</p>
</item>
<tag><c>trace</c></tag>
<item>
<p>Activates all events at the debug level. On this
- level, the <c>Mnesia</c> event handler starts subscribing
- to updates on all <c>Mnesia</c> tables. This level is
+ level, the Mnesia event handler starts subscribing
+ to updates on all Mnesia tables. This level is
intended only for debugging small toy systems, as many
large events can be generated.</p>
</item>
@@ -2856,7 +2856,7 @@ raise(Name, Amount) ->
</item>
<item>
<p><c>-mnesia core_dir Directory</c>. The name of the
- directory where <c>Mnesia</c> core files is stored, or
+ directory where Mnesia core files is stored, or
false. Setting it implies that also RAM-only nodes
generate a core file if a crash occurs.</p>
</item>
@@ -2870,9 +2870,9 @@ raise(Name, Amount) ->
</item>
<item>
<p><c>-mnesia dir Directory</c>. The name of the directory
- where all <c>Mnesia</c> data is stored. The directory name
+ where all Mnesia data is stored. The directory name
must be unique for the current node. Two nodes must never
- share the the same <c>Mnesia</c> directory. The results
+ share the the same Mnesia directory. The results
are unpredictable.</p>
</item>
<item>
@@ -2915,44 +2915,44 @@ raise(Name, Amount) ->
</item>
<item>
<p><c>-mnesia event_module Module</c>. The name of the
- <c>Mnesia</c> event handler callback module. Default is
+ Mnesia event handler callback module. Default is
<c>mnesia_event</c>.</p>
</item>
<item>
<p><c>-mnesia extra_db_nodes Nodes</c> specifies a list of
nodes, in addition to the ones found in the schema, with
- which <c>Mnesia</c> is also to establish contact. Default
+ which Mnesia is also to establish contact. Default
is <c>[]</c> (empty list).</p>
</item>
<item>
<p><c>-mnesia fallback_error_function {UserModule, UserFunc}</c>.
Specifies a user-supplied callback function, which is
- called if a fallback is installed and <c>Mnesia</c> goes
- down on another node. <c>Mnesia</c> calls the function
+ called if a fallback is installed and Mnesia goes
+ down on another node. Mnesia calls the function
with one argument, the name of the dying node, for example,
- <c>UserModule:UserFunc(DyingNode)</c>. <c>Mnesia</c> must
+ <c>UserModule:UserFunc(DyingNode)</c>. Mnesia must
be restarted, otherwise the database can be inconsistent.
- The default behavior is to terminate <c>Mnesia</c>.</p>
+ The default behavior is to terminate Mnesia.</p>
</item>
<item>
<p><c>-mnesia max_wait_for_decision Timeout</c>. Specifies
- how long <c>Mnesia</c> waits for other nodes to share their
+ how long Mnesia waits for other nodes to share their
knowledge about the outcome of an unclear transaction. By
default, <c>Timeout</c> is set to the atom <c>infinity</c>.
- This implies that if <c>Mnesia</c> upon startup detects
+ This implies that if Mnesia upon startup detects
a "heavyweight transaction" whose outcome is unclear, the
- local <c>Mnesia</c> waits until <c>Mnesia</c> is started
+ local Mnesia waits until Mnesia is started
on some (in the worst case all) of the other nodes that were
involved in the interrupted transaction. This is a rare
- situation, but if it occurs, <c>Mnesia</c> does not guess if
+ situation, but if it occurs, Mnesia does not guess if
the transaction on the other nodes was committed or
- terminated. <c>Mnesia</c> waits until it knows the outcome
+ terminated. Mnesia waits until it knows the outcome
and then acts accordingly.</p>
<p>If <c>Timeout</c> is set to an integer value in
- milliseconds, <c>Mnesia</c> forces "heavyweight transactions"
+ milliseconds, Mnesia forces "heavyweight transactions"
to be finished, even if the outcome of the transaction for
the moment is unclear. After <c>Timeout</c> milliseconds,
- <c>Mnesia</c> commits or terminates the transaction and
+ Mnesia commits or terminates the transaction and
continues with the startup. This can lead to a situation
where the transaction is committed on some nodes and
terminated on other nodes. If the transaction is a
@@ -2976,14 +2976,14 @@ raise(Name, Amount) ->
</item>
<item>
<p><c>-mnesia schema_location Loc</c>. Controls where
- <c>Mnesia</c> looks for its schema. Parameter
+ Mnesia looks for its schema. Parameter
<c>Loc</c> can be one of the following atoms:</p>
<taglist>
<tag><c>disc</c></tag>
<item>
<p>Mandatory disc. The schema is assumed to be located
- in the <c>Mnesia</c> directory. If the schema cannot
- be found, <c>Mnesia</c> refuses to start. This is the
+ in the Mnesia directory. If the schema cannot
+ be found, Mnesia refuses to start. This is the
old behavior.</p>
</item>
<tag><c>ram</c></tag>
@@ -3001,10 +3001,10 @@ raise(Name, Amount) ->
<tag><c>opt_disc</c></tag>
<item>
<p>Optional disc. The schema can reside on disc or in
- RAM. If the schema is found on disc, <c>Mnesia</c>
+ RAM. If the schema is found on disc, Mnesia
starts as a disc-based node and the storage type of
the schema table is <c>disc_copies</c>. If no schema is
- found on disc, <c>Mnesia</c> starts as a disc-less node
+ found on disc, Mnesia starts as a disc-less node
and the storage type of the schema table is
<c>ram_copies</c>. Default value for the application
parameter is <c>opt_disc</c>.</p>
@@ -3012,7 +3012,7 @@ raise(Name, Amount) ->
</taglist>
</item>
</list>
- <p>First, the <c>SASL</c> application parameters are checked,
+ <p>First, the SASL application parameters are checked,
then the command-line flags are checked, and finally, the
default value is chosen.</p>
</section>
diff --git a/lib/mnesia/doc/src/mnesia_frag_hash.xml b/lib/mnesia/doc/src/mnesia_frag_hash.xml
index 95f5f8aa07..51b32129b6 100644
--- a/lib/mnesia/doc/src/mnesia_frag_hash.xml
+++ b/lib/mnesia/doc/src/mnesia_frag_hash.xml
@@ -87,13 +87,13 @@
the new one.</p>
<p><c>NewState</c> is stored as <c>hash_state</c> among the
other <c>frag_properties</c>.</p>
- <p>As a part of the <c>add_frag</c> procedure, <c>Mnesia</c> iterates
+ <p>As a part of the <c>add_frag</c> procedure, Mnesia iterates
over all fragments corresponding to the <c>IterFrags</c> numbers
and starts <c>key_to_frag_number(NewState,RecordKey)</c> for
each record. If the new fragment differs from the old
fragment, the record is moved to the new fragment.</p>
<p>As the <c>add_frag</c> procedure is a part of a schema
- transaction, <c>Mnesia</c> acquires write locks on the
+ transaction, Mnesia acquires write locks on the
affected tables. That is, both the fragments corresponding
to <c>IterFrags</c> and those corresponding to
<c>AdditionalLockFrags</c>.</p>
@@ -112,7 +112,7 @@
<desc>
<p><c>NewState</c> is stored as <c>hash_state</c> among the
other <c>frag_properties</c>.</p>
- <p>As a part of the <c>del_frag</c> procedure, <c>Mnesia</c> iterates
+ <p>As a part of the <c>del_frag</c> procedure, Mnesia iterates
over all fragments corresponding to the <c>IterFrags</c> numbers
and starts <c>key_to_frag_number(NewState,RecordKey)</c> for
each record. If the new fragment differs from the old
@@ -120,7 +120,7 @@
<p>Notice that all records in the last fragment must be moved to
another fragment, as the entire fragment is deleted.</p>
<p>As the <c>del_frag</c> procedure is a part of a schema
- transaction, <c>Mnesia</c> acquires write locks on the
+ transaction, Mnesia acquires write locks on the
affected tables. That is, both the fragments corresponding
to <c>IterFrags</c> and those corresponding to
<c>AdditionalLockFrags</c>.</p>
@@ -134,7 +134,7 @@
<v>Reason = term()</v>
</type>
<desc>
- <p>Starts whenever <c>Mnesia</c> needs to determine
+ <p>Starts whenever Mnesia needs to determine
which fragment a certain record belongs to. It is typically
started at <c>read</c>, <c>write</c>, and <c>delete</c>.</p>
</desc>
@@ -149,7 +149,7 @@
<v>Reason = term()</v>
</type>
<desc>
- <p>This function is called whenever <c>Mnesia</c> needs to determine
+ <p>This function is called whenever Mnesia needs to determine
which fragments that need to be searched for a <c>MatchSpec</c>.
It is typically called by <c>select</c> and
<c>match_object</c>.</p>
diff --git a/lib/mnesia/doc/src/mnesia_registry.xml b/lib/mnesia/doc/src/mnesia_registry.xml
index cd778ae072..a76f716981 100644
--- a/lib/mnesia/doc/src/mnesia_registry.xml
+++ b/lib/mnesia/doc/src/mnesia_registry.xml
@@ -38,20 +38,20 @@
<modulesummary>Dump support for registries in erl_interface.</modulesummary>
<description>
<p>This module is usually part of the <c>erl_interface</c>
- application, but is currently part of the <c>Mnesia</c>
+ application, but is currently part of the Mnesia
application.</p>
<p>This module is mainly intended for internal use within OTP,
but it has two functions that are exported for public use.</p>
<p>On C-nodes, <c>erl_interface</c> has support for registry
tables. These tables reside in RAM on the C-node, but can also
- be dumped into <c>Mnesia</c> tables. By default, the dumping
+ be dumped into Mnesia tables. By default, the dumping
of registry tables through <c>erl_interface</c> causes a
- corresponding <c>Mnesia</c> table to be created with
+ corresponding Mnesia table to be created with
<c>mnesia_registry:create_table/1</c>, if necessary.</p>
<p>Tables that are created with these functions can be
- administered as all other <c>Mnesia</c> tables. They can be
+ administered as all other Mnesia tables. They can be
included in backups, replicas can be added, and so on.
- The tables are normal <c>Mnesia</c> tables owned by the user
+ The tables are normal Mnesia tables owned by the user
of the corresponding <c>erl_interface</c> registries.</p>
</description>
@@ -68,7 +68,7 @@
that is, <c>{ram_copies,[node()]}</c> or
<c>{disc_copies,[node()]}</c>.</p>
<p>This function is used by <c>erl_interface</c> to
- create the <c>Mnesia</c> table if it does not already
+ create the Mnesia table if it does not already
exist.</p>
</desc>
</func>
diff --git a/lib/mnesia/doc/src/notes.xml b/lib/mnesia/doc/src/notes.xml
index 7d8e8d0c44..e621bd593c 100644
--- a/lib/mnesia/doc/src/notes.xml
+++ b/lib/mnesia/doc/src/notes.xml
@@ -39,7 +39,21 @@
thus constitutes one section in this document. The title of each
section is the version number of Mnesia.</p>
- <section><title>Mnesia 4.14</title>
+ <section><title>Mnesia 4.14.1</title>
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p> Correct some minor documentation issues. </p>
+ <p>
+ Own Id: OTP-13891</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Mnesia 4.14</title>
<section><title>Improvements and New Features</title>
<list>
diff --git a/lib/mnesia/doc/src/part.xml b/lib/mnesia/doc/src/part.xml
index 101bdb29d4..d3ffe93937 100644
--- a/lib/mnesia/doc/src/part.xml
+++ b/lib/mnesia/doc/src/part.xml
@@ -30,7 +30,7 @@
<file>part.sgml</file>
</header>
<description>
- <p>The <c>Mnesia</c> application is a distributed Database Management
+ <p>The Mnesia application is a distributed Database Management
System (DBMS), appropriate for telecommunications applications and other
Erlang applications, which require continuous operation and exhibit soft
real-time properties.</p>
diff --git a/lib/mnesia/doc/src/ref_man.xml b/lib/mnesia/doc/src/ref_man.xml
index 662f0d61d6..7fb71b9c45 100644
--- a/lib/mnesia/doc/src/ref_man.xml
+++ b/lib/mnesia/doc/src/ref_man.xml
@@ -33,7 +33,7 @@
<file>refman.sgml</file>
</header>
<description>
- <p>The <c>Mnesia</c> application is a distributed Database Management
+ <p>The Mnesia application is a distributed Database Management
System (DBMS), appropriate for telecommunications applications and other
Erlang applications, which require continuous operation and exhibit soft
real-time properties.</p>
diff --git a/lib/mnesia/vsn.mk b/lib/mnesia/vsn.mk
index fb4200f62d..f08e364276 100644
--- a/lib/mnesia/vsn.mk
+++ b/lib/mnesia/vsn.mk
@@ -1 +1 @@
-MNESIA_VSN = 4.14
+MNESIA_VSN = 4.14.1
diff --git a/lib/observer/doc/src/notes.xml b/lib/observer/doc/src/notes.xml
index f79f75fead..659eb28292 100644
--- a/lib/observer/doc/src/notes.xml
+++ b/lib/observer/doc/src/notes.xml
@@ -32,6 +32,22 @@
<p>This document describes the changes made to the Observer
application.</p>
+<section><title>Observer 2.2.2</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Fixed error handling in observer when mnesia tables was
+ requested and not available.</p>
+ <p>
+ Own Id: OTP-13845 Aux Id: ERL-237 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Observer 2.2.1</title>
<section><title>Fixed Bugs and Malfunctions</title>
diff --git a/lib/observer/doc/src/ttb.xml b/lib/observer/doc/src/ttb.xml
index 94ecef24b4..7cd15e15d3 100644
--- a/lib/observer/doc/src/ttb.xml
+++ b/lib/observer/doc/src/ttb.xml
@@ -114,7 +114,8 @@ ttb:p(all, call).</input></pre>
<v>Opt = {file,Client} | {handler, FormatHandler} | {process_info,PI} |
shell | {shell, ShellSpec} | {timer, TimerSpec} |
{overload_check, {MSec, Module, Function}} |
- {flush, MSec} | resume | {resume, FetchTimeout}</v>
+ {flush, MSec} | resume | {resume, FetchTimeout} |
+ {queue_size, QueueSize}</v>
<v>TimerSpec = MSec | {MSec, StopOpts}</v>
<v>MSec = FetchTimeout = integer()</v>
<v>Module = Function = atom() </v>
@@ -126,6 +127,7 @@ ttb:p(all, call).</input></pre>
<v>FormatHandler = See format/2</v>
<v>PI = true | false </v>
<v>ShellSpec = true | false | only</v>
+ <v>QueueSize = non_neg_integer()</v>
</type>
<desc>
<p>Starts a file trace port on all specified nodes
@@ -147,6 +149,18 @@ ttb:p(all, call).</input></pre>
<c>Client</c> must be <c>{local, File}</c>. All
trace information is then sent to the trace control node where
it is written to file.</p></item>
+ <tag><c>queue_size</c></tag>
+ <item><p>When tracing to shell or <c>{local,File}</c>, an ip
+ trace driver is used internally. The ip trace driver has a
+ queue of maximum <c>QueueSize</c> messages waiting to be
+ delivered. If the driver cannot deliver messages as fast as
+ they are produced, the queue size might be exceeded and
+ messages are dropped. This parameter is optional, and is
+ only useful if many <c>{drop,N}</c> trace messages are
+ received by the trace handler. It has no meaning if shell
+ or <c>{local,File}</c> is not used. See
+ <seealso marker="runtime_tools:dbg#trace_port/2">dbg:trace_port/2</seealso>
+ for more information about the ip trace driver.</p></item>
<tag><c>process_info</c></tag>
<item><p>Indicates if process
information is to be collected. If <c>PI = true</c> (which is
@@ -485,7 +499,7 @@ ttb:p(all, call).</input></pre>
<p>For a description of the <c>match_spec()</c> syntax,
see section
<seealso marker="erts:match_spec"><c>Match Specifications in Erlang</c></seealso>
- in <c>ERTS</c>, which explains the general match specification "language".
+ in ERTS, which explains the general match specification "language".
</p>
<note>
<p>The <em>system tracer</em> for sequential tracing is
diff --git a/lib/observer/src/observer_tv_wx.erl b/lib/observer/src/observer_tv_wx.erl
index 59f6443551..968a7620aa 100644
--- a/lib/observer/src/observer_tv_wx.erl
+++ b/lib/observer/src/observer_tv_wx.erl
@@ -124,6 +124,8 @@ handle_event(#wx{id=Id}, State = #state{node=Node, grid=Grid, opt=Opt0})
end,
case get_tables2(Node, Opt) of
Error = {error, _} ->
+ Id =:= ?ID_MNESIA andalso
+ wxMenuBar:check(observer_wx:get_menubar(), ?ID_ETS, true),
self() ! Error,
{noreply, State};
Tables ->
@@ -217,22 +219,32 @@ handle_info(refresh_interval, State = #state{node=Node, grid=Grid, opt=Opt,
{noreply, State#state{tabs=Tabs}}
end;
-handle_info({active, Node}, State = #state{parent=Parent, grid=Grid, opt=Opt,
+handle_info({active, Node}, State = #state{parent=Parent, grid=Grid, opt=Opt0,
timer=Timer0}) ->
- Tables = get_tables(Node, Opt),
+ {Tables, Opt} = case Opt0#opt.type =:= mnesia andalso get_tables2(Node, Opt0) of
+ Ts when is_list(Ts) ->
+ {Ts, Opt0};
+ _ -> % false or error getting mnesia tables
+ Opt1 = Opt0#opt{type=ets},
+ {get_tables(Node, Opt1), Opt1}
+ end,
Tabs = update_grid(Grid, Opt, Tables),
wxWindow:setFocus(Grid),
create_menus(Parent, Opt),
Timer = observer_lib:start_timer(Timer0),
- {noreply, State#state{node=Node, tabs=Tabs, timer=Timer}};
+ {noreply, State#state{node=Node, tabs=Tabs, timer=Timer, opt=Opt}};
handle_info(not_active, State = #state{timer = Timer0}) ->
Timer = observer_lib:stop_timer(Timer0),
{noreply, State#state{timer=Timer}};
-handle_info({error, Error}, State) ->
+handle_info({error, Error}, #state{opt=Opt}=State) ->
handle_error(Error),
- {noreply, State};
+ case Opt#opt.type of
+ mnesia -> wxMenuBar:check(observer_wx:get_menubar(), ?ID_ETS, true);
+ _ -> ok
+ end,
+ {noreply, State#state{opt=Opt#opt{type=ets}}};
handle_info(_Event, State) ->
{noreply, State}.
diff --git a/lib/observer/src/observer_wx.erl b/lib/observer/src/observer_wx.erl
index 301bb4b32f..5732c12006 100644
--- a/lib/observer/src/observer_wx.erl
+++ b/lib/observer/src/observer_wx.erl
@@ -21,7 +21,7 @@
-behaviour(wx_object).
-export([start/0, stop/0]).
--export([create_menus/2, get_attrib/1, get_tracer/0, get_active_node/0,
+-export([create_menus/2, get_attrib/1, get_tracer/0, get_active_node/0, get_menubar/0,
set_status/1, create_txt_dialog/4, try_rpc/4, return_to_localnode/2]).
-export([init/1, handle_event/2, handle_cast/2, terminate/2, code_change/3,
@@ -94,6 +94,9 @@ get_tracer() ->
get_active_node() ->
wx_object:call(observer, get_active_node).
+get_menubar() ->
+ wx_object:call(observer, get_menubar).
+
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
init(_Args) ->
@@ -391,6 +394,9 @@ handle_call(get_tracer, _From, State=#state{trace_panel=TraceP}) ->
handle_call(get_active_node, _From, State=#state{node=Node}) ->
{reply, Node, State};
+handle_call(get_menubar, _From, State=#state{menubar=MenuBar}) ->
+ {reply, MenuBar, State};
+
handle_call(stop, From, State) ->
stop_servers(State),
{noreply, State#state{reply_to=From}};
diff --git a/lib/observer/src/ttb.erl b/lib/observer/src/ttb.erl
index ac6c4572eb..87a50e046b 100644
--- a/lib/observer/src/ttb.erl
+++ b/lib/observer/src/ttb.erl
@@ -78,6 +78,11 @@ do_tracer(Nodes0,PI,Client,Traci) ->
do_tracer(Clients,PI,Traci) ->
Shell = proplists:get_value(shell, Traci, false),
+ IpPortSpec =
+ case proplists:get_value(queue_size, Traci) of
+ undefined -> 0;
+ QS -> {0,QS}
+ end,
DefShell = fun(Trace) -> dbg:dhandler(Trace, standard_io) end,
{ClientSucc,Succ} =
lists:foldl(
@@ -98,7 +103,7 @@ do_tracer(Clients,PI,Traci) ->
[_,H] = string:tokens(atom_to_list(N),"@"),
H
end,
- case catch dbg:tracer(N,port,dbg:trace_port(ip,0)) of
+ case catch dbg:tracer(N,port,dbg:trace_port(ip,IpPortSpec)) of
{ok,N} ->
{ok,Port} = dbg:trace_port_control(N,get_listen_port),
{ok,T} = dbg:get_tracer(N),
@@ -160,6 +165,8 @@ opt([{resume,MSec}|O],{PI,Client,Traci}) ->
opt(O,{PI,Client,[{resume, {true, MSec}}|Traci]});
opt([{flush,MSec}|O],{PI,Client,Traci}) ->
opt(O,{PI,Client,[{flush, MSec}|Traci]});
+opt([{queue_size,QueueSize}|O],{PI,Client,Traci}) ->
+ opt(O,{PI,Client,[{queue_size,QueueSize}|Traci]});
opt([],Opt) ->
ensure_opt(Opt).
diff --git a/lib/observer/vsn.mk b/lib/observer/vsn.mk
index 9a7c6a546f..444089151e 100644
--- a/lib/observer/vsn.mk
+++ b/lib/observer/vsn.mk
@@ -1 +1 @@
-OBSERVER_VSN = 2.2.1
+OBSERVER_VSN = 2.2.2
diff --git a/lib/odbc/configure.in b/lib/odbc/configure.in
index cb2b23d534..d26daa5eda 100644
--- a/lib/odbc/configure.in
+++ b/lib/odbc/configure.in
@@ -147,7 +147,7 @@ AC_SUBST(THR_LIBS)
odbc_lib_link_success=no
AC_SUBST(TARGET_FLAGS)
case $host_os in
- darwin1[[0-4]].*|darwin[[0-9]].*)
+ darwin1[[0-5]].*|darwin[[0-9]].*)
TARGET_FLAGS="-DUNIX"
if test ! -d "$with_odbc" || test "$with_odbc" = "yes"; then
ODBC_LIB= -L"/usr/lib"
diff --git a/lib/odbc/doc/src/notes.xml b/lib/odbc/doc/src/notes.xml
index d3dd39616f..7fb19a072e 100644
--- a/lib/odbc/doc/src/notes.xml
+++ b/lib/odbc/doc/src/notes.xml
@@ -32,7 +32,23 @@
<p>This document describes the changes made to the odbc application.
</p>
- <section><title>ODBC 2.11.2</title>
+ <section><title>ODBC 2.11.3</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ ODBC build configure has been updated to accept Mac OS X
+ El Capitan. Fixed by Lee Bannard.</p>
+ <p>
+ Own Id: OTP-13781</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>ODBC 2.11.2</title>
<section><title>Improvements and New Features</title>
<list>
@@ -585,7 +601,7 @@
also been extended. </item><item> The <c>configure</c>
scripts of <c>erl_interface</c> and <c>odbc</c> now
search for thread libraries and thread library quirks the
- same way as <c>erts</c> do. </item><item> The
+ same way as ERTS do. </item><item> The
<c>configure</c> script of the <c>odbc</c> application
now also looks for odbc libraries in <c>lib64</c> and
<c>lib/64</c> directories when building on a 64-bit
diff --git a/lib/odbc/vsn.mk b/lib/odbc/vsn.mk
index 957c6b42eb..a7c8f8079b 100644
--- a/lib/odbc/vsn.mk
+++ b/lib/odbc/vsn.mk
@@ -1 +1 @@
-ODBC_VSN = 2.11.2
+ODBC_VSN = 2.11.3
diff --git a/lib/parsetools/doc/src/notes.xml b/lib/parsetools/doc/src/notes.xml
index b826b4d03a..30a9374e81 100644
--- a/lib/parsetools/doc/src/notes.xml
+++ b/lib/parsetools/doc/src/notes.xml
@@ -31,6 +31,22 @@
</header>
<p>This document describes the changes made to the Parsetools application.</p>
+<section><title>Parsetools 2.1.3</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p> Yecc generates Dialyzer suppressions to avoid
+ warnings when operator precedence declarations are used.
+ </p>
+ <p>
+ Own Id: OTP-13681</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Parsetools 2.1.2</title>
<section><title>Improvements and New Features</title>
diff --git a/lib/parsetools/doc/src/yecc.xml b/lib/parsetools/doc/src/yecc.xml
index 6ec9fb5f0e..9188bd2a22 100644
--- a/lib/parsetools/doc/src/yecc.xml
+++ b/lib/parsetools/doc/src/yecc.xml
@@ -201,7 +201,7 @@
<p>The grammar starts with an optional <c>header</c> section. The
header is put first in the generated file, before the module
declaration. The purpose of the header is to provide a means to
- make the documentation generated by <c>EDoc</c> look nicer. Each
+ make the documentation generated by EDoc look nicer. Each
header line should be enclosed in double quotes, and newlines
will be inserted between the lines. For example:</p>
<code>
diff --git a/lib/parsetools/src/leex.erl b/lib/parsetools/src/leex.erl
index 15d42a4d9c..602e47404d 100644
--- a/lib/parsetools/src/leex.erl
+++ b/lib/parsetools/src/leex.erl
@@ -1586,6 +1586,8 @@ out_dfa_graph(St, DFA, DF) ->
case file:open(St#leex.gfile, [write]) of
{ok,Gfile} ->
try
+ %% Set the same encoding as infile:
+ set_encoding(St, Gfile),
io:fwrite(Gfile, "digraph DFA {~n", []),
out_dfa_states(Gfile, DFA, DF),
out_dfa_edges(Gfile, DFA),
@@ -1621,7 +1623,7 @@ out_dfa_edges(File, DFA) ->
foreach(fun (T) ->
Crs = orddict:fetch(T, Tdict),
Edgelab = dfa_edgelabel(Crs),
- io:fwrite(File, " ~b -> ~b [label=\"~s\"];~n",
+ io:fwrite(File, " ~b -> ~b [label=\"~ts\"];~n",
[S,T,Edgelab])
end, sort(orddict:fetch_keys(Tdict)))
end, DFA).
diff --git a/lib/parsetools/src/yecc.erl b/lib/parsetools/src/yecc.erl
index 1b426141a1..f6b80eb1b4 100644
--- a/lib/parsetools/src/yecc.erl
+++ b/lib/parsetools/src/yecc.erl
@@ -1978,7 +1978,8 @@ output_goto(St, [{_Nonterminal, []} | Go], StateInfo) ->
output_goto(St, Go, StateInfo);
output_goto(St0, [{Nonterminal, List} | Go], StateInfo) ->
F = function_name(yeccgoto, Nonterminal),
- St10 = output_goto1(St0, List, F, StateInfo, true),
+ St05 = fwrite(St0, <<"-dialyzer({nowarn_function, ~w/7}).\n">>, [F]),
+ St10 = output_goto1(St05, List, F, StateInfo, true),
St = output_goto_fini(F, Nonterminal, St10),
output_goto(St, Go, StateInfo);
output_goto(St, [], _StateInfo) ->
diff --git a/lib/parsetools/test/leex_SUITE.erl b/lib/parsetools/test/leex_SUITE.erl
index 949ef3c36e..84f9c996ac 100644
--- a/lib/parsetools/test/leex_SUITE.erl
+++ b/lib/parsetools/test/leex_SUITE.erl
@@ -408,12 +408,12 @@ unicode(Config) when is_list(Config) ->
Ts = [{unicode_1,
<<"%% -*- coding: utf-8 -*-\n"
"Definitions.\n"
- "RTLarrow = (←)\n"
+ "RTLarrow = (←)\n"
"Rules.\n"
- "{RTLarrow} : {token,{'<-',TokenLine}}.\n"
+ "{RTLarrow} : {token,{\"←\",TokenLine}}.\n"
"Erlang code.\n"
"-export([t/0]).\n"
- "t() -> {ok, [{'<-', 1}], 1} = string(\"←\"), ok.">>,
+ "t() -> {ok, [{\"←\", 1}], 1} = string(\"←\"), ok.">>,
default,
ok}],
@@ -1137,7 +1137,7 @@ run_test(Config, Def, Pre) ->
XrlFile = filename:join(DataDir, DefFile),
ErlFile = filename:join(DataDir, Filename),
Opts = [return, warn_unused_vars,{outdir,DataDir}],
- ok = file:write_file(XrlFile, Def, [{encoding, unicode}]),
+ ok = file:write_file(XrlFile, Def),
LOpts = [return, {report, false} |
case Pre of
default ->
diff --git a/lib/parsetools/test/yecc_SUITE.erl b/lib/parsetools/test/yecc_SUITE.erl
index 3710569aba..e91ddb11d1 100644
--- a/lib/parsetools/test/yecc_SUITE.erl
+++ b/lib/parsetools/test/yecc_SUITE.erl
@@ -342,7 +342,7 @@ syntax(Config) when is_list(Config) ->
{L2,_,{bad_inline,{yeccpars2_2_,1}}}]}],
[]} = compile:file(Parserfile1, [basic_validation,return]),
?line L1 = 31 + SzYeccPre,
- ?line L2 = 38 + SzYeccPre
+ ?line L2 = 39 + SzYeccPre
end(),
%% Bad macro in action. OTP-7224.
@@ -360,7 +360,7 @@ syntax(Config) when is_list(Config) ->
{L2,_,{bad_inline,{yeccpars2_2_,1}}}]}],
[]} = compile:file(Parserfile1, [basic_validation,return]),
?line L1 = 31 + SzYeccPre,
- ?line L2 = 38 + SzYeccPre
+ ?line L2 = 39 + SzYeccPre
end(),
%% Check line numbers. OTP-7224.
@@ -1623,7 +1623,7 @@ otp_7292(Config) when is_list(Config) ->
[{_,[{16,_,{unused_function,{foo,0}}}]}]} =
compile:file(Parserfile1, [basic_validation, return]),
L1 = 41 + SzYeccPre,
- L2 = 48 + SzYeccPre
+ L2 = 49 + SzYeccPre
end(),
YeccPre = filename:join(Dir, "yeccpre.hrl"),
@@ -1641,7 +1641,7 @@ otp_7292(Config) when is_list(Config) ->
[{_,[{16,_,{unused_function,{foo,0}}}]}]} =
compile:file(Parserfile1, [basic_validation, return]),
?line L1 = 40 + SzYeccPre,
- ?line L2 = 47 + SzYeccPre
+ ?line L2 = 48 + SzYeccPre
end(),
file:delete(YeccPre),
diff --git a/lib/parsetools/vsn.mk b/lib/parsetools/vsn.mk
index befdd82d6e..07cd959c98 100644
--- a/lib/parsetools/vsn.mk
+++ b/lib/parsetools/vsn.mk
@@ -1 +1 @@
-PARSETOOLS_VSN = 2.1.2
+PARSETOOLS_VSN = 2.1.3
diff --git a/lib/public_key/doc/src/public_key_app.xml b/lib/public_key/doc/src/public_key_app.xml
index 1f87932b6c..923a9f1dfb 100644
--- a/lib/public_key/doc/src/public_key_app.xml
+++ b/lib/public_key/doc/src/public_key_app.xml
@@ -61,7 +61,7 @@
<section>
<title>DEPENDENCIES</title>
<p>The <c>public_key</c> application uses the
- Crypto application to preform cryptographic operations and the
+ Crypto application to perform cryptographic operations and the
ASN-1 application to handle PKIX-ASN-1 specifications, hence
these applications must be loaded for the <c>public_key</c> application to work.
In an embedded environment this means they must be started with
@@ -72,7 +72,7 @@
<section>
<title>ERROR LOGGER AND EVENT HANDLERS</title>
<p> The <c>public_key</c> application is a library application
- and does not use the error logger. The functions will either sucssed
+ and does not use the error logger. The functions will either succeed
or fail with a runtime error.
</p>
</section>
diff --git a/lib/public_key/test/pbe_SUITE.erl b/lib/public_key/test/pbe_SUITE.erl
index 004eaefc27..44caf479e5 100644
--- a/lib/public_key/test/pbe_SUITE.erl
+++ b/lib/public_key/test/pbe_SUITE.erl
@@ -219,7 +219,12 @@ pbes2() ->
pbes2(Config) when is_list(Config) ->
decode_encode_key_file("pbes2_des_cbc_enc_key.pem", "password", "DES-CBC", Config),
decode_encode_key_file("pbes2_des_ede3_cbc_enc_key.pem", "password", "DES-EDE3-CBC", Config),
- decode_encode_key_file("pbes2_rc2_cbc_enc_key.pem", "password", "RC2-CBC", Config).
+ case lists:member(rc2_cbc, proplists:get_value(ciphers, crypto:supports())) of
+ true ->
+ decode_encode_key_file("pbes2_rc2_cbc_enc_key.pem", "password", "RC2-CBC", Config);
+ false ->
+ ok
+ end.
check_key_info(#'PrivateKeyInfo'{privateKeyAlgorithm =
#'PrivateKeyInfo_privateKeyAlgorithm'{algorithm = ?rsaEncryption},
diff --git a/lib/reltool/doc/src/notes.xml b/lib/reltool/doc/src/notes.xml
index 6df4924d0a..2365a68feb 100644
--- a/lib/reltool/doc/src/notes.xml
+++ b/lib/reltool/doc/src/notes.xml
@@ -38,7 +38,38 @@
thus constitutes one section in this document. The title of each
section is the version number of Reltool.</p>
- <section><title>Reltool 0.7.1</title>
+ <section><title>Reltool 0.7.2</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Dependencies specified in .app files would earlier only
+ be followed for applications that are included in a 'rel'
+ spec in the reltool config. For other applications, only
+ xref would decide the dependencies.</p>
+ <p>
+ Some dependency chains would even be missed for
+ applications that are included in a 'rel' spec in the
+ reltool config. E.g.</p>
+ <p>
+ <list> <item>Application x has y as included application,
+ and y in turn has z as included application. Then z is
+ not included. </item> <item>Application x has y in its
+ 'applications' tag in the .app file, and y in turn has z
+ as included application. Then z is not included.</item>
+ </list></p>
+ <p>
+ These bugs are now corrected.</p>
+ <p>
+ Own Id: OTP-11993</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Reltool 0.7.1</title>
<section><title>Improvements and New Features</title>
<list>
@@ -546,8 +577,8 @@
<p>The handling of applications included in releases has
been improved. Applications that are required to be
started before other applications in a release are now
- automatically included in the release. The <c>kernel</c>
- and <c>stdlib</c> applications are always included as
+ automatically included in the release. The Kernel
+ and STDLIB applications are always included as
they are mandatory.</p>
<p>Applications that are (explicitly or implicitly)
included in a release are now automatically included as
diff --git a/lib/reltool/doc/src/part.xml b/lib/reltool/doc/src/part.xml
index 5904084879..e608d548ea 100644
--- a/lib/reltool/doc/src/part.xml
+++ b/lib/reltool/doc/src/part.xml
@@ -31,7 +31,7 @@
<rev>%VSN%</rev>
</header>
<description>
- <p><c>Reltool</c> is a release management tool. It analyses a given
+ <p>Reltool is a release management tool. It analyses a given
Erlang/OTP installation and determines various dependencies
between applications. The <c>graphical</c> frontend depicts the
dependencies and enables interactive customization of a
diff --git a/lib/reltool/doc/src/ref_man.xml b/lib/reltool/doc/src/ref_man.xml
index 38f270b79a..1684f075ff 100644
--- a/lib/reltool/doc/src/ref_man.xml
+++ b/lib/reltool/doc/src/ref_man.xml
@@ -31,7 +31,7 @@
<rev>%VSN%</rev>
</header>
<description>
- <p><c>Reltool</c> is a release management tool. It analyses a given
+ <p>Reltool is a release management tool. It analyses a given
Erlang/OTP installation and determines various dependencies
between applications. The <c>graphical</c> frontend depicts the
dependencies and enables interactive customization of a
diff --git a/lib/reltool/doc/src/reltool.xml b/lib/reltool/doc/src/reltool.xml
index 38448e7961..5bfbee966b 100644
--- a/lib/reltool/doc/src/reltool.xml
+++ b/lib/reltool/doc/src/reltool.xml
@@ -47,7 +47,7 @@
<p>The tool uses an installed Erlang/OTP system as input.
<c>root_dir</c> is the root directory of the analysed system and
- it defaults to the system executing <c>reltool</c>. Applications
+ it defaults to the system executing Reltool. Applications
may also be located outside <c>root_dir</c>. <c>lib_dirs</c>
defines library directories where additional applications
may reside and it defaults to the directories
@@ -56,7 +56,7 @@
<p>An application directory <c>AppDir</c> under a library
directory is recognized by the existence of an <c>AppDir/ebin</c>
- directory. If this does not exist, <c>reltool</c> will not
+ directory. If this does not exist, Reltool will not
consider <c>AppDir</c> at all when looking for applications.</p>
<p>It is recommended that application directories are named as the
@@ -81,14 +81,14 @@
<tag><c>config</c></tag>
<item>
<p>This is the main option and it controls the configuration
- of <c>reltool</c>. It can either be a <c>sys</c> tuple or
+ of Reltool. It can either be a <c>sys</c> tuple or
a name of a <c>file</c> containing a sys tuple.</p>
</item>
<tag><c>trap_exit</c></tag>
<item>
<p>This option controls the error handling behavior of
- <c>reltool</c>. By default the window processes traps
+ Reltool. By default the window processes traps
exit, but this behavior can altered by setting
<c>trap_exit</c> to <c>false</c>.</p>
</item>
diff --git a/lib/reltool/doc/src/reltool_intro.xml b/lib/reltool/doc/src/reltool_intro.xml
index 8e232b8838..2980ad7977 100644
--- a/lib/reltool/doc/src/reltool_intro.xml
+++ b/lib/reltool/doc/src/reltool_intro.xml
@@ -34,7 +34,7 @@
<rev>%VSN%</rev>
<file>reltool_intro.xml</file>
</header>
- <p><c>Reltool</c> is a release management tool. It analyses a given
+ <p>Reltool is a release management tool. It analyses a given
Erlang/OTP installation and determines various dependencies between
applications. The <c>graphical</c> frontend depicts the dependencies and
enables interactive customization of a target system. The backend provides a
@@ -82,7 +82,7 @@
and about the Erlang/OTP development system:</p>
<list type="bulleted">
<item>
- <p>the Reference Manual of <c>Reltool</c></p>
+ <p>the Reference Manual of Reltool</p>
</item>
<item>
<p>the Erlang/OTP <c>System Principles</c></p>
diff --git a/lib/reltool/src/reltool.hrl b/lib/reltool/src/reltool.hrl
index 9ac22b9450..89b5f3a997 100644
--- a/lib/reltool/src/reltool.hrl
+++ b/lib/reltool/src/reltool.hrl
@@ -154,19 +154,19 @@
-record(app_info,
{
- description = "" :: string(),
- id = "" :: string(),
- vsn = "" :: app_vsn(),
- modules = [] :: [mod_name()],
- maxP = infinity :: integer() | infinity,
- maxT = infinity :: integer() | infinity,
- registered = [] :: [atom()],
- incl_apps = [] :: [app_name()],
- applications = [] :: [app_name()],
- env = [] :: [{atom(), term()}],
- mod = undefined :: {mod_name(), [term()]} | undefined,
- start_phases = undefined :: [{atom(), term()}] | undefined,
- runtime_dependencies = [] :: [string()]
+ description = "" :: '_' | string(),
+ id = "" :: '_' | string(),
+ vsn = "" :: '_' | app_vsn(),
+ modules = [] :: '_' | [mod_name()],
+ maxP = infinity :: '_' | integer() | infinity,
+ maxT = infinity :: '_' | integer() | infinity,
+ registered = [] :: '_' | [atom()],
+ incl_apps = [] :: '_' | '$3' | [app_name()],
+ applications = [] :: '_' | '$2' | [app_name()],
+ env = [] :: '_' | [{atom(), term()}],
+ mod = undefined :: '_' | {mod_name(), [term()]} | undefined,
+ start_phases = undefined :: '_' | [{atom(), term()}] | undefined,
+ runtime_dependencies = [] :: '_' | [string()]
}).
-record(regexp, {source, compiled}).
diff --git a/lib/reltool/vsn.mk b/lib/reltool/vsn.mk
index 76f69fd294..2b23ff6f20 100644
--- a/lib/reltool/vsn.mk
+++ b/lib/reltool/vsn.mk
@@ -1 +1 @@
-RELTOOL_VSN = 0.7.1
+RELTOOL_VSN = 0.7.2
diff --git a/lib/runtime_tools/doc/src/dbg.xml b/lib/runtime_tools/doc/src/dbg.xml
index 14a81b2293..95f74d4607 100644
--- a/lib/runtime_tools/doc/src/dbg.xml
+++ b/lib/runtime_tools/doc/src/dbg.xml
@@ -78,7 +78,7 @@
<p>Pseudo function that by means of a <c>parse_transform</c>
translates the <em>literal</em><c>fun()</c> typed as parameter in
the function call to a match specification as described in
- the <c>match_spec</c> manual of <c>ERTS</c> users guide.
+ the <c>match_spec</c> manual of ERTS users guide.
(with literal I mean that the <c>fun()</c> needs to
textually be written as the parameter of the function, it
cannot be held in a variable which in turn is passed to the
@@ -954,7 +954,7 @@ Error: fun containing local erlang function calls ('is_atomm' called in guard)\
as the tuple <c>{drop, N}</c> where <c>N</c> is the number of consecutive messages
dropped. In case of heavy tracing, drop's are likely to occur,
and they surely occur if no client is reading the trace
- messages.</p>
+ messages. The default value of <c>QueSize</c> is 200.</p>
</desc>
</func>
<func>
diff --git a/lib/runtime_tools/doc/src/notes.xml b/lib/runtime_tools/doc/src/notes.xml
index 6bec7cb9ca..0b830593b4 100644
--- a/lib/runtime_tools/doc/src/notes.xml
+++ b/lib/runtime_tools/doc/src/notes.xml
@@ -32,6 +32,20 @@
<p>This document describes the changes made to the Runtime_Tools
application.</p>
+<section><title>Runtime_Tools 1.10.1</title>
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p> Correct some minor documentation issues. </p>
+ <p>
+ Own Id: OTP-13891</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Runtime_Tools 1.10</title>
<section><title>Fixed Bugs and Malfunctions</title>
@@ -668,7 +682,7 @@
also been extended. </item><item> The <c>configure</c>
scripts of <c>erl_interface</c> and <c>odbc</c> now
search for thread libraries and thread library quirks the
- same way as <c>erts</c> do. </item><item> The
+ same way as ERTS do. </item><item> The
<c>configure</c> script of the <c>odbc</c> application
now also looks for odbc libraries in <c>lib64</c> and
<c>lib/64</c> directories when building on a 64-bit
diff --git a/lib/runtime_tools/src/dbg.erl b/lib/runtime_tools/src/dbg.erl
index c0d4665bda..f17aa528ed 100644
--- a/lib/runtime_tools/src/dbg.erl
+++ b/lib/runtime_tools/src/dbg.erl
@@ -427,7 +427,7 @@ trace_port(file, Filename) ->
trace_port1(file, Filename, nowrap);
trace_port(ip, Portno) when is_integer(Portno) ->
- trace_port(ip,{Portno,50});
+ trace_port(ip,{Portno,200});
trace_port(ip, {Portno, Qsiz}) when is_integer(Portno), is_integer(Qsiz) ->
fun() ->
diff --git a/lib/runtime_tools/vsn.mk b/lib/runtime_tools/vsn.mk
index b33f6f4721..0fc86e42f7 100644
--- a/lib/runtime_tools/vsn.mk
+++ b/lib/runtime_tools/vsn.mk
@@ -1 +1 @@
-RUNTIME_TOOLS_VSN = 1.10
+RUNTIME_TOOLS_VSN = 1.10.1
diff --git a/lib/sasl/doc/src/alarm_handler.xml b/lib/sasl/doc/src/alarm_handler.xml
index 8550a88b28..4160757164 100644
--- a/lib/sasl/doc/src/alarm_handler.xml
+++ b/lib/sasl/doc/src/alarm_handler.xml
@@ -54,7 +54,7 @@
format is defined by the user. For example, an event handler
for SNMP can be defined, together with an alarm Management
Information Base (MIB).</p>
- <p>The alarm handler is part of the <c>SASL</c> application.</p>
+ <p>The alarm handler is part of the SASL application.</p>
<p>When writing new event handlers for the alarm handler, the
following events must be handled:</p>
<taglist>
@@ -76,7 +76,7 @@
{NewHandler, Args})</c>. <c>NewHandler:init({Args, {alarm_handler,
Alarms}})</c> is called. For more details, see
<seealso marker="stdlib:gen_event"><c>gen_event(3)</c></seealso>
- in <c>STDLIB</c>.</p>
+ in STDLIB.</p>
</description>
<funcs>
diff --git a/lib/sasl/doc/src/appup.xml b/lib/sasl/doc/src/appup.xml
index 6fbdcb9f5b..a43a966dcb 100644
--- a/lib/sasl/doc/src/appup.xml
+++ b/lib/sasl/doc/src/appup.xml
@@ -362,12 +362,12 @@ point_of_no_return</pre>
system is restarted.</p>
<pre>
restart_new_emulator</pre>
- <p>This instruction is used when the application <c>ERTS</c>,
- <c>Kernel</c>, <c>STDLIB</c>, or <c>SASL</c> is
+ <p>This instruction is used when the application ERTS,
+ Kernel, STDLIB, or SASL is
upgraded. It shuts down the current emulator and starts a new
one. All processes are terminated gracefully, and the new
- version of <c>ERTS</c>, <c>Kernel</c>, <c>STDLIB</c>, and
- <c>SASL</c> are used when the emulator restarts.
+ version of ERTS, Kernel, STDLIB, and
+ SASL are used when the emulator restarts.
Only one <c>restart_new_emulator</c> instruction is allowed
in the <c>relup</c> file, and it must be placed first.
<seealso marker="systools#make_relup/3"><c>systools:make_relup/3,4</c></seealso>
@@ -385,7 +385,7 @@ restart_new_emulator</pre>
<warning>
<p>As stated earlier, instruction <c>restart_new_emulator</c>
causes the emulator to be restarted with new versions of
- <c>ERTS</c>, <c>Kernel</c>, <c>STDLIB</c>, and <c>SASL</c>.
+ ERTS>, Kernel, STDLIB, and SASL.
However, all other applications do at startup run their old
versions in this new emulator. This is usually no problem,
but every now and then incompatible changes occur to the
diff --git a/lib/sasl/doc/src/error_logging.xml b/lib/sasl/doc/src/error_logging.xml
index 8464a41ff9..4b2c960bbb 100644
--- a/lib/sasl/doc/src/error_logging.xml
+++ b/lib/sasl/doc/src/error_logging.xml
@@ -32,20 +32,20 @@
<rev>B</rev>
<file>error_logging.xml</file>
</header>
- <p>The <c>SASL</c> application introduces three types of reports:</p>
+ <p>The SASL application introduces three types of reports:</p>
<list type="bulleted">
<item>Supervisor report</item>
<item>Progress report</item>
<item>Crash report</item>
</list>
- <p>When the <c>SASL</c> application is started, it adds a handler that
+ <p>When the SASL application is started, it adds a handler that
formats and writes these reports, as specified in the configuration
- parameters for <c>SASL</c>, that is, the environment variables
- in the <c>SASL</c> application specification, which is found in the
- <c>.app</c> file of <c>SASL</c>. For details, see the
+ parameters for SASL, that is, the environment variables
+ in the SASL application specification, which is found in the
+ <c>.app</c> file of SASL. For details, see the
<seealso marker="sasl_app"><c>sasl(6)</c></seealso> application in the
Reference Manual and the <seealso marker="kernel:app"><c>app(4)</c></seealso>
- file in the <c>Kernel</c> Reference Manual.</p>
+ file in the Kernel Reference Manual.</p>
<section>
<title>Supervisor Report</title>
@@ -180,14 +180,14 @@
<p>The report browser is used to browse and format error reports
written by the error logger handler
<seealso marker="stdlib:log_mf_h"><c>log_mf_h</c></seealso>
- defined in <c>STDLIB</c>.</p>
+ defined in STDLIB.</p>
<p>The <c>log_mf_h</c> handler writes all reports to a
report logging directory, which is specified when
- configuring the <c>SASL</c> application.</p>
+ configuring the SASL application.</p>
<p>If the report browser is
used offline, the reports can be copied to another directory
specified when starting the browser. If no such directory
- is specified, the browser reads reports from the <c>SASL</c>
+ is specified, the browser reads reports from the SASL
<c>error_logger_mf_dir</c>.</p>
<section>
diff --git a/lib/sasl/doc/src/notes.xml b/lib/sasl/doc/src/notes.xml
index dae73f8b23..055d433524 100644
--- a/lib/sasl/doc/src/notes.xml
+++ b/lib/sasl/doc/src/notes.xml
@@ -31,6 +31,59 @@
</header>
<p>This document describes the changes made to the SASL application.</p>
+<section><title>SASL 3.0.1</title>
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Improved dirty scheduler support. A purge of a module can
+ now be performed without having to wait for completion of
+ all ongoing dirty NIF calls.</p>
+ <p>
+ Note that when enabling support for dirty schedulers, a
+ new purge strategy will as of ERTS version 8.1 be
+ enabled. This new strategy is not fully backwards
+ compatible with the strategy used by default. For more
+ information see the documentation of <seealso
+ marker="erts:erlang#check_process_code/3"><c>erlang:check_process_code/3</c></seealso>.</p>
+ <p>
+ Own Id: OTP-13808 Aux Id: OTP-13833 </p>
+ </item>
+ <item>
+ <p>
+ A new purge strategy has been introduced. The new
+ strategy will by default be disabled during the OTP 19
+ release, but will be the only strategy available as of
+ the OTP 20 release.</p>
+ <p>
+ The new strategy is slightly incompatible with the
+ strategy being used by default in OTP 19. Using the
+ default strategy, processes holding <c>fun</c>s that
+ refer to the module being purged either fail a soft
+ purge, or will be killed during a hard purge. The new
+ strategy completely ignores <c>fun</c>s. If <c>fun</c>s
+ referring to the code being purged exist, and are used
+ after a purge, an exception will be raised upon usage.
+ That is, the behavior will be exactly the same as the
+ case when a <c>fun</c> is received by a process after the
+ purge.</p>
+ <p>
+ The new strategy can optionally be enabled when building
+ OTP during OTP 19, and will automatically be enabled if
+ the runtime system is built with support for dirty
+ schedulers.</p>
+ <p>
+ For more information see the documentation of <seealso
+ marker="erts:erlang#check_process_code/3"><c>erlang:check_process_code/3</c></seealso>.</p>
+ <p>
+ Own Id: OTP-13833</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>SASL 3.0</title>
<section><title>Improvements and New Features</title>
@@ -824,7 +877,7 @@
<list>
<item>
<p>Use an infinity timeout in all calls to
- <c>gen_server:call()</c> in the <c>sasl</c>
+ <c>gen_server:call()</c> in the SASL
application.</p>
<p>
Own Id: OTP-8506 Aux Id: seq11509 </p>
diff --git a/lib/sasl/doc/src/part.xml b/lib/sasl/doc/src/part.xml
index f531ed2dea..659710487e 100644
--- a/lib/sasl/doc/src/part.xml
+++ b/lib/sasl/doc/src/part.xml
@@ -30,7 +30,7 @@
<file>part.xml</file>
</header>
<description>
- <p>The System Architecture Support Libraries <c>SASL</c> application
+ <p>The System Architecture Support Libraries SASL application
provides support for alarm handling, release handling, and
related functions.</p>
</description>
diff --git a/lib/sasl/doc/src/rb.xml b/lib/sasl/doc/src/rb.xml
index 1ce19046eb..d5df4fd345 100644
--- a/lib/sasl/doc/src/rb.xml
+++ b/lib/sasl/doc/src/rb.xml
@@ -38,7 +38,7 @@
<p>The Report Browser (RB) tool is used to browse and
format error reports written by the error logger handler
<seealso marker="stdlib:log_mf_h"><c>log_mf_h</c></seealso>
- in <c>STDLIB</c>.</p>
+ in STDLIB.</p>
</description>
<funcs>
@@ -62,7 +62,7 @@
reports that match that filter.</p>
<p>The reports are matched using the
<seealso marker="stdlib:proplists"><c>proplists</c></seealso>
- module in <c>STDLIB</c>. The report must be a proplist
+ module in STDLIB. The report must be a proplist
to be matched against any of the filters.</p>
<p>If the filter has the form <c>{Key, RegExp, re}</c>, the
report must contain an element with key equal to <c>Key</c> and
@@ -102,7 +102,7 @@
</list>
<p>For a definition of valid regular expressions and options, see
the <seealso marker="stdlib:re"><c>re</c></seealso> module in
- <c>STDLIB</c> and in particular function <c>re:run/3</c>.</p>
+ STDLIB and in particular function <c>re:run/3</c>.</p>
<p>For details about data type <c>mp()</c>, see
<seealso marker="stdlib:re#type-mp"><c>re:mp()</c></seealso>.</p>
</desc>
diff --git a/lib/sasl/doc/src/ref_man.xml b/lib/sasl/doc/src/ref_man.xml
index 42045df5ec..78cf1eb26e 100644
--- a/lib/sasl/doc/src/ref_man.xml
+++ b/lib/sasl/doc/src/ref_man.xml
@@ -30,7 +30,7 @@
<file>application.xml</file>
</header>
<description>
- <p>The <c>SASL</c> application provides support for alarm handling,
+ <p>The SASL application provides support for alarm handling,
release handling, and related functions.</p>
</description>
<xi:include href="sasl_app.xml"/>
diff --git a/lib/sasl/doc/src/rel.xml b/lib/sasl/doc/src/rel.xml
index d6558c06b4..9356b2cd47 100644
--- a/lib/sasl/doc/src/rel.xml
+++ b/lib/sasl/doc/src/rel.xml
@@ -59,7 +59,7 @@
<tag><c>Vsn = string()</c></tag>
<item><p>Release version.</p></item>
<tag><c>EVsn = string()</c></tag>
- <item><p><c>ERTS</c> version the release is intended for.</p></item>
+ <item><p>ERTS version the release is intended for.</p></item>
<tag><c>Application = atom()</c></tag>
<item><p>Name of an application included in the release.</p></item>
<tag><c>AppVsn = string()</c></tag>
@@ -82,8 +82,8 @@
to the same value as in the application resource file.</p></item>
</taglist>
<note>
- <p>The list of applications must contain the <c>Kernel</c> and
- <c>STDLIB</c> applications.</p>
+ <p>The list of applications must contain the Kernel and
+ STDLIB applications.</p>
</note>
</section>
diff --git a/lib/sasl/doc/src/release_handler.xml b/lib/sasl/doc/src/release_handler.xml
index bcbc5f5339..8f073807fb 100644
--- a/lib/sasl/doc/src/release_handler.xml
+++ b/lib/sasl/doc/src/release_handler.xml
@@ -31,7 +31,7 @@
<module>release_handler</module>
<modulesummary>Unpacking and Installation of Release Packages</modulesummary>
<description>
- <p>The <em>release handler</em> process belongs to the <c>SASL</c>
+ <p>The <em>release handler</em> process belongs to the SASL
application, which is responsible for <em>release handling</em>,
that is, unpacking, installation, and removal of release packages.</p>
<p>An introduction to release handling and an example is provided in
@@ -44,7 +44,7 @@
directory of the previous version of the release, where
<c>$ROOT</c> is the installation root directory,
<seealso marker="kernel:code#root_dir/0"><c>code:root_dir()</c></seealso>.
- Another <c>releases</c> directory can be specified using the <c>SASL</c>
+ Another <c>releases</c> directory can be specified using the SASL
configuration parameter <c>releases_dir</c> or the OS environment
variable <c>RELDIR</c>. The release handler must have write access
to this directory to install the new release.
@@ -56,7 +56,7 @@
<item>A boot script, <c>Name.boot</c></item>
</list>
<p>The <c>.rel</c> file contains information about the release: its name,
- version, and which <c>ERTS</c> and application versions it uses.</p>
+ version, and which ERTS and application versions it uses.</p>
<p>A release package can also contain:</p>
<list type="bulleted">
<item>A release upgrade file, <c>relup</c></item>
@@ -115,7 +115,7 @@
<em>System Documentation</em>. In this case, the system
configuration file <c>sys.config</c> is mandatory.</p>
<p>The installation of a new release can restart the system. Which
- program to use is specified by the <c>SASL</c> configuration
+ program to use is specified by the SASL configuration
parameter <c>start_prg</c>, which defaults
to <c>$ROOT/bin/start</c>.</p>
<p>The emulator restart on Windows NT expects that the system is
@@ -132,7 +132,7 @@
is made permanent.</p>
<p>The release handler at a node running on a diskless machine,
or with a read-only file system, must be configured accordingly
- using the following <c>SASL</c> configuration parameters (for
+ using the following SASL configuration parameters (for
details, see <seealso marker="sasl_app">sasl(6)</seealso>):</p>
<taglist>
<tag><c>masters</c></tag>
@@ -287,7 +287,7 @@
returned, the emulator is restarted
before the upgrade instructions are executed. This
occurs if the emulator or any of the applications
- <c>Kernel</c>, <c>STDLIB</c>, or <c>SASL</c>
+ Kernel, STDLIB, or SASL
are updated. The new emulator version
and these core applications execute after the restart.
For all other applications the old versions are
@@ -310,13 +310,13 @@
<tag><c>code_change_timeout</c></tag>
<item><p>Defines the time-out
for all calls to
- <seealso marker="stdlib:sys#change_code/4"><c>stdlib:sys:change_code</c></seealso>.
+ <seealso marker="stdlib:sys#change_code/4"><c>sys:change_code</c></seealso>.
If no value is specified or <c>default</c> is specified, the
default value defined in <c>sys</c> is used.</p></item>
<tag><c>suspend_timeout</c></tag>
<item><p>Defines the time-out for
all calls to
- <seealso marker="stdlib:sys#suspend/1"><c>stdlib:sys:suspend</c></seealso>.
+ <seealso marker="stdlib:sys#suspend/1"><c>sys:suspend</c></seealso>.
If no value is specified, the values defined by the <c>Timeout</c>
parameter of the <c>upgrade</c> or <c>suspend</c> instructions are used.
If <c>default</c> is specified, the default value defined in
@@ -342,7 +342,7 @@ release_handler:set_unpacked(RelFile, [{myapp,"1.0","/home/user"},...]).
=> {ok,NewVsn}</code>
<p>If <c>NewVsn</c> is installed with option
<c>{update_paths,true}</c>, then
- <seealso marker="kernel:code#lib_dir/1"><c>kernel:code:lib_dir(myapp)</c></seealso>
+ <seealso marker="kernel:code#lib_dir/1"><c>code:lib_dir(myapp)</c></seealso>
returns <c>/home/user/myapp-1.0</c>.</p></item>
</taglist>
<note>
@@ -801,7 +801,7 @@ release_handler:set_unpacked(RelFile, [{myapp,"1.0","/home/user"},...]).
is an atom named from the Posix error codes, such as
<c>enoent</c>, <c>eacces</c>, or <c>eisdir</c>. See
<seealso marker="kernel:file"><c>file(3)</c></seealso>
- in <c>Kernel</c>.</p></item>
+ in Kernel.</p></item>
<tag><c>Posix</c></tag>
<item><p>Some file operation failed, as for the previous item in
the list.</p></item>
diff --git a/lib/sasl/doc/src/sasl_app.xml b/lib/sasl/doc/src/sasl_app.xml
index 52f449c2a8..0576397f9b 100644
--- a/lib/sasl/doc/src/sasl_app.xml
+++ b/lib/sasl/doc/src/sasl_app.xml
@@ -31,17 +31,17 @@
<app>sasl</app>
<appsummary>The SASL application</appsummary>
<description>
- <p>The <c>SASL</c> application provides the following services:</p>
+ <p>The SASL application provides the following services:</p>
<list type="bulleted">
<item><c>alarm_handler</c></item>
<item><c>rb</c></item>
<item><c>release_handler</c></item>
<item><c>systools</c></item>
</list>
- <p>The <c>SASL</c> application also includes <c>error_logger</c> event
- handlers for formatting <c>SASL</c> error and crash reports.</p>
+ <p>The SASL application also includes <c>error_logger</c> event
+ handlers for formatting SASL error and crash reports.</p>
<note>
- <p>The <c>SASL</c> application in OTP has nothing to do with
+ <p>The SASL application in OTP has nothing to do with
"Simple Authentication and Security Layer" (RFC 4422).</p>
</note>
</description>
@@ -49,7 +49,7 @@
<section>
<title>Error Logger Event Handlers</title>
<p>The following error logger event handlers are used by
- the <c>SASL</c> application.</p>
+ the SASL application.</p>
<taglist>
<tag><c>sasl_report_tty_h</c></tag>
<item>
@@ -57,7 +57,7 @@
reports</em>, and <em>progress reports</em> to <c>stdio</c>.
This error logger event handler uses
<seealso marker="kernel:kernel_app#error_logger_format_depth">error_logger_format_depth</seealso>
- in the <c>Kernel</c> application to limit how much detail is
+ in the Kernel application to limit how much detail is
printed in crash and supervisor reports.</p>
</item>
<tag><c>sasl_report_file_h</c></tag>
@@ -66,7 +66,7 @@
report</em>, and <em>progress report</em> to a single file.
This error logger event handler uses
<seealso marker="kernel:kernel_app#error_logger_format_depth">error_logger_format_depth</seealso>
- in the <c>Kernel</c> application to limit the details
+ in the Kernel application to limit the details
printed in crash and supervisor reports.</p>
</item>
<tag><c>log_mf_h</c></tag>
@@ -75,9 +75,9 @@
error logger to disk. Multiple files and log rotation are
used. For efficiency reasons, each event is written as a
binary. For more information about this handler,
- see <seealso marker="stdlib:log_mf_h">the <c>STDLIB</c> Reference
+ see <seealso marker="stdlib:log_mf_h">the STDLIB Reference
Manual</seealso>.</p>
- <p>To activate this event handler, three <c>SASL</c>
+ <p>To activate this event handler, three SASL
configuration parameters must be set,
<c>error_logger_mf_dir</c>, <c>error_logger_mf_maxbytes</c>,
and <c>error_logger_mf_maxfiles</c>. The next section provides
@@ -88,9 +88,9 @@
<section>
<title>Configuration</title>
- <p>The following configuration parameters are defined for the <c>SASL</c>
+ <p>The following configuration parameters are defined for the SASL
application. For more information about configuration parameters, see
- <seealso marker="kernel:app"><c>app(4)</c></seealso> in <c>Kernel</c>.</p>
+ <seealso marker="kernel:app"><c>app(4)</c></seealso> in Kernel.</p>
<p>All configuration parameters are optional.</p>
<taglist>
<tag><c><![CDATA[sasl_error_logger = Value ]]></c></tag>
@@ -112,7 +112,7 @@
Use <c>[append]</c> to have the <c>FileName</c> open in append mode.
<c>FileName</c> is a string.</p></item>
<tag><c>false</c></tag>
- <item><p>No <c>SASL</c> error logger handler is installed.</p></item>
+ <item><p>No SASL error logger handler is installed.</p></item>
</taglist>
</item>
<tag><c><![CDATA[errlog_type = error | progress | all ]]></c></tag>
diff --git a/lib/sasl/doc/src/sasl_intro.xml b/lib/sasl/doc/src/sasl_intro.xml
index b71dafb192..f74a7c1db8 100644
--- a/lib/sasl/doc/src/sasl_intro.xml
+++ b/lib/sasl/doc/src/sasl_intro.xml
@@ -32,7 +32,7 @@
<section>
<title>Scope</title>
- <p>The <c>SASL</c> application provides support for:</p>
+ <p>The SASL application provides support for:</p>
<list type="bulleted">
<item>Error logging</item>
<item>Alarm handling</item>
diff --git a/lib/sasl/doc/src/script.xml b/lib/sasl/doc/src/script.xml
index 8ed132354d..b40ff28179 100644
--- a/lib/sasl/doc/src/script.xml
+++ b/lib/sasl/doc/src/script.xml
@@ -88,7 +88,7 @@
follows:</p>
<list type="bulleted">
<item><c>-pa Dir1 Dir2 ... DirN</c> adds the directories
- <c>Dir1, Dir2, ..., DirN</c> to the front of the initial
+ <c>DirN, DirN-1, ..., Dir2, Dir1</c> to the front of the initial
load path.</item>
<item><c>-pz Dir1 Dir2 ... DirN</c> adds the directories
<c>Dir1, Dir2, ..., DirN</c> to the end of the initial
diff --git a/lib/sasl/doc/src/systools.xml b/lib/sasl/doc/src/systools.xml
index 7446762de4..fa503fa573 100644
--- a/lib/sasl/doc/src/systools.xml
+++ b/lib/sasl/doc/src/systools.xml
@@ -85,7 +85,7 @@
</item>
<item>
<p>If the emulator needs to be restarted after upgrading or
- downgrading, that is, if the <c>ERTS</c> version differs
+ downgrading, that is, if the ERTS version differs
between <c>Name.rel</c> and <c>Name2.rel</c></p>
</item>
</list>
@@ -201,10 +201,10 @@
between the applications. Where there are no dependencies,
the order in the <c>.rel</c> file is kept.</p>
<p>The function fails if the mandatory
- applications <c>Kernel</c> and <c>STDLIB</c> are not
+ applications Kernel and STDLIB are not
included in the <c>.rel</c> file and have start
type <c>permanent</c> (which is default).</p>
- <p>If <c>SASL</c> is not included as an application in
+ <p>If SASL is not included as an application in
the <c>.rel</c> file, a warning is issued because such a
release cannot be used in an upgrade. To turn off this
warning, add option <c>no_warn_sasl</c>.</p>
diff --git a/lib/sasl/src/sasl.app.src b/lib/sasl/src/sasl.app.src
index 4ee8a7d6c8..633cdfa070 100644
--- a/lib/sasl/src/sasl.app.src
+++ b/lib/sasl/src/sasl.app.src
@@ -46,5 +46,5 @@
{errlog_type, all}]},
{mod, {sasl, []}},
{runtime_dependencies, ["tools-2.6.14","stdlib-3.0","kernel-5.0",
- "erts-8.0"]}]}.
+ "erts-8.1"]}]}.
diff --git a/lib/sasl/src/systools_make.erl b/lib/sasl/src/systools_make.erl
index 352e4984df..efe6cc9eb4 100644
--- a/lib/sasl/src/systools_make.erl
+++ b/lib/sasl/src/systools_make.erl
@@ -1491,8 +1491,9 @@ mandatory_modules() ->
preloaded() ->
%% Sorted
[erl_prim_loader,erl_tracer,erlang,
- erts_code_purger,
- erts_internal,init,otp_ring0,prim_eval,prim_file,
+ erts_code_purger,erts_dirty_process_code_checker,
+ erts_internal,erts_literal_area_collector,
+ init,otp_ring0,prim_eval,prim_file,
prim_inet,prim_zip,zlib].
%%______________________________________________________________________
diff --git a/lib/sasl/vsn.mk b/lib/sasl/vsn.mk
index fd0fc9b8b5..a7d7c09cde 100644
--- a/lib/sasl/vsn.mk
+++ b/lib/sasl/vsn.mk
@@ -1 +1 @@
-SASL_VSN = 3.0
+SASL_VSN = 3.0.1
diff --git a/lib/snmp/doc/src/notes.xml b/lib/snmp/doc/src/notes.xml
index 0f5c35b300..3323d32878 100644
--- a/lib/snmp/doc/src/notes.xml
+++ b/lib/snmp/doc/src/notes.xml
@@ -34,7 +34,35 @@
</header>
- <section><title>SNMP 5.2.3</title>
+ <section><title>SNMP 5.2.4</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p> Correct bugs when path to mib or idl spec files
+ contains UTF-8 characters. </p>
+ <p>
+ Own Id: OTP-13718 Aux Id: ERL-179 </p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Solves snmp config string handling as reported by ERL-164
+ and solved by PR-1100</p>
+ <p>
+ Own Id: OTP-13706</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>SNMP 5.2.3</title>
<section><title>Improvements and New Features</title>
<list>
diff --git a/lib/snmp/src/app/snmp.app.src b/lib/snmp/src/app/snmp.app.src
index b593e9ea84..d25f66f44a 100644
--- a/lib/snmp/src/app/snmp.app.src
+++ b/lib/snmp/src/app/snmp.app.src
@@ -23,12 +23,12 @@
{vsn, "%VSN%"},
{modules, [
%% Compiler modules (not in the runtime part of the app)
-% snmpc,
-% snmpc_lib,
-% snmpc_mib_gram,
-% snmpc_mib_to_hrl,
-% snmpc_misc,
-% snmpc_tok,
+ snmpc,
+ snmpc_lib,
+ snmpc_mib_gram,
+ snmpc_mib_to_hrl,
+ snmpc_misc,
+ snmpc_tok,
%% Application modules
snmp,
diff --git a/lib/snmp/src/compile/snmpc.erl b/lib/snmp/src/compile/snmpc.erl
index db1f9ee61b..d86692aaf6 100644
--- a/lib/snmp/src/compile/snmpc.erl
+++ b/lib/snmp/src/compile/snmpc.erl
@@ -64,7 +64,7 @@ compile(Input, _Output, Options) ->
{ok, _} ->
ok;
{error, Reason} ->
- io:format("~p", [Reason]),
+ io:format("~tp", [Reason]),
error
end.
@@ -126,7 +126,14 @@ compile(FileName) ->
%%----------------------------------------------------------------------
compile(FileName, Options) when is_list(FileName) ->
- true = snmpc_misc:is_string(FileName),
+ case snmpc_misc:check_file(FileName) of
+ true ->
+ compile_1(FileName, Options);
+ false ->
+ {error, {invalid_file, FileName}}
+ end.
+
+compile_1(FileName, Options) ->
DefOpts = [{deprecated, true},
{group_check, true},
{i, ["./"]},
diff --git a/lib/snmp/src/compile/snmpc_misc.erl b/lib/snmp/src/compile/snmpc_misc.erl
index 933d629746..312074f2e7 100644
--- a/lib/snmp/src/compile/snmpc_misc.erl
+++ b/lib/snmp/src/compile/snmpc_misc.erl
@@ -29,7 +29,7 @@
bits_to_int/2,
ensure_trailing_dir_delimiter/1,
foreach/3,
- is_string/1,
+ check_file/1,
read_mib/1,
read_noexit/2,
strip_extension_from_filename/2,
@@ -86,21 +86,21 @@ to_upper([C|Cs]) -> [C|to_upper(Cs)];
to_upper([]) -> [].
-is_string([]) -> true;
-is_string([Tkn | Str])
- when is_integer(Tkn) andalso (Tkn >= 0) andalso (Tkn =< 255) ->
- is_string(Str);
-is_string(_) -> false.
-
-
+check_file(FileName) ->
+ case filename:extension(FileName) of
+ ".mib" ->
+ filelib:is_regular(FileName);
+ _ ->
+ filelib:is_regular(FileName ++ ".mib")
+ end.
+
+
foreach(Function, ExtraArgs, [H | T]) ->
apply(Function, [H | ExtraArgs]),
foreach(Function, ExtraArgs, T);
foreach(_Function, _ExtraArgs, []) ->
true.
-
-
%%----------------------------------------------------------------------
%% Returns: {ok, Mib}|{error, Reason}
%% The reason for having the function if this module is:
diff --git a/lib/snmp/test/snmp_agent_test.erl b/lib/snmp/test/snmp_agent_test.erl
index 8ae495bb1b..3c1a6f2afd 100644
--- a/lib/snmp/test/snmp_agent_test.erl
+++ b/lib/snmp/test/snmp_agent_test.erl
@@ -647,22 +647,22 @@ init_per_group(GroupName, Config) ->
snmp_test_lib:init_group_top_dir(GroupName, Config).
init_per_group_ipv6(GroupName, Config, Init) ->
+ {ok, Hostname0} = inet:gethostname(),
case ct:require(ipv6_hosts) of
ok ->
- case gen_udp:open(0, [inet6]) of
- {ok, S} ->
- ok = gen_udp:close(S),
- Init(
- snmp_test_lib:init_group_top_dir(
- GroupName,
- [{ipfamily, inet6},
- {ip, ?LOCALHOST(inet6)}
- | lists:keydelete(ip, 1, Config)]));
- {error, _} ->
- {skip, "Host seems to not support IPv6"}
- end;
+ case lists:member(list_to_atom(Hostname0), ct:get_config(ipv6_hosts)) of
+ true ->
+ Init(
+ snmp_test_lib:init_group_top_dir(
+ GroupName,
+ [{ipfamily, inet6},
+ {ip, ?LOCALHOST(inet6)}
+ | lists:keydelete(ip, 1, Config)]));
+ false ->
+ {skip, "Host does not support IPV6"}
+ end;
_ ->
- {skip, "Host does not support IPV6"}
+ {skip, "Test config ipv6_hosts is missing"}
end.
end_per_group(all_tcs, Config) ->
diff --git a/lib/snmp/test/snmp_app_test.erl b/lib/snmp/test/snmp_app_test.erl
index 6e7e85d3b4..5e69866f9a 100644
--- a/lib/snmp/test/snmp_app_test.erl
+++ b/lib/snmp/test/snmp_app_test.erl
@@ -23,366 +23,29 @@
%%----------------------------------------------------------------------
-module(snmp_app_test).
--export([
- all/0, groups/0,
- init_per_group/2, end_per_group/2,
- init_per_suite/1, end_per_suite/1,
- init_per_testcase/2, end_per_testcase/2,
+%% Note: This directive should only be used in test suites.
+-compile(export_all).
- fields/1,
- modules/1,
- exportall/1,
- app_depend/1,
-
- start_and_stop_empty/1,
- start_and_stop_with_agent/1,
- start_and_stop_with_manager/1,
- start_and_stop_with_agent_and_manager/1,
- start_epmty_and_then_agent_and_manager_and_stop/1,
- start_with_agent_and_then_manager_and_stop/1,
- start_with_manager_and_then_agent_and_stop/1
- ]).
-
-
--include_lib("kernel/include/file.hrl").
-include_lib("common_test/include/ct.hrl").
--include("snmp_test_lib.hrl").
-
-
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%%--------------------------------------------------------------------
+%% Common Test interface functions -----------------------------------
+%%--------------------------------------------------------------------
all() ->
- Cases =
- [
- fields,
- modules,
- exportall,
- app_depend,
- {group, start_and_stop}
- ],
- Cases.
-
-groups() ->
- [{start_and_stop, [],
- [start_and_stop_empty,
- start_and_stop_with_agent,
- start_and_stop_with_manager,
- start_and_stop_with_agent_and_manager,
- start_epmty_and_then_agent_and_manager_and_stop,
- start_with_agent_and_then_manager_and_stop,
- start_with_manager_and_then_agent_and_stop]}].
-
-init_per_group(_GroupName, Config) ->
- Config.
-
-end_per_group(_GroupName, Config) ->
- Config.
-
-
-init_per_suite(Config) when is_list(Config) ->
- ?DISPLAY_SUITE_INFO(),
-
- %% Note that part of this stuff (the suite top dir creation)
- %% may already be done (if we run the entire snmp suite).
-
- PrivDir = ?config(priv_dir, Config),
- TopDir = filename:join(PrivDir, app),
- case file:make_dir(TopDir) of
- ok ->
- ok;
- {error, eexist} ->
- ok;
- Error ->
- fail({failed_creating_subsuite_top_dir, Error})
- end,
- AppFile =
- case is_app() of
- {ok, File} ->
- io:format("File: ~n~p~n", [File]),
- snmp:print_version_info(),
- File;
- {error, Reason} ->
- fail(Reason)
- end,
- [{app_topdir, TopDir}, {app_file, AppFile} | Config].
-
-
-is_app() ->
- is_app(?APPLICATION).
-
-is_app(App) ->
- LibDir = code:lib_dir(App),
- File = filename:join([LibDir, "ebin", atom_to_list(App) ++ ".app"]),
- case file:consult(File) of
- {ok, [{application, App, AppFile}]} ->
- {ok, AppFile};
- Error ->
- {error, {invalid_format, Error}}
- end.
-
-end_per_suite(suite) -> [];
-end_per_suite(doc) -> [];
-end_per_suite(Config) when is_list(Config) ->
- Config.
-
-
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-
-%% Test server callbacks
-init_per_testcase(_Case, Config) ->
- Config.
-
-end_per_testcase(_Case, Config) ->
- Config.
-
-
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-
-fields(suite) ->
- [];
-fields(doc) ->
- [];
-fields(Config) when is_list(Config) ->
- AppFile = key1search(app_file, Config),
- Fields = [vsn, description, modules, registered, applications],
- case check_fields(Fields, AppFile, []) of
- [] ->
- ok;
- Missing ->
- fail({missing_fields, Missing})
- end.
-
-check_fields([], _AppFile, Missing) ->
- Missing;
-check_fields([Field|Fields], AppFile, Missing) ->
- check_fields(Fields, AppFile, check_field(Field, AppFile, Missing)).
-
-check_field(Name, AppFile, Missing) ->
- io:format("checking field: ~p~n", [Name]),
- case lists:keymember(Name, 1, AppFile) of
- true ->
- Missing;
- false ->
- [Name|Missing]
- end.
-
-
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-
-modules(suite) ->
- [];
-modules(doc) ->
- [];
-modules(Config) when is_list(Config) ->
- AppFile = key1search(app_file, Config),
- Mods = key1search(modules, AppFile),
- EbinList = get_ebin_mods(snmp),
- case missing_modules(Mods, EbinList, []) of
- [] ->
- ok;
- Missing ->
- fail({missing_modules, Missing})
- end,
- Allowed = [snmpc,
- snmpc_lib,
- snmpc_misc,
- snmpc_mib_gram,
- snmpc_mib_to_hrl,
- snmpc_tok],
- case extra_modules(Mods, EbinList, Allowed, []) of
- [] ->
- ok;
- Extra ->
- fail({extra_modules, Extra})
- end,
- {ok, Mods}.
-
-get_ebin_mods(App) ->
- LibDir = code:lib_dir(App),
- EbinDir = filename:join([LibDir,"ebin"]),
- {ok, Files0} = file:list_dir(EbinDir),
- Files1 = [lists:reverse(File) || File <- Files0],
- [list_to_atom(lists:reverse(Name)) || [$m,$a,$e,$b,$.|Name] <- Files1].
-
-
-missing_modules([], _Ebins, Missing) ->
- Missing;
-missing_modules([Mod|Mods], Ebins, Missing) ->
- case lists:member(Mod, Ebins) of
- true ->
- missing_modules(Mods, Ebins, Missing);
- false ->
- io:format("missing module: ~p~n", [Mod]),
- missing_modules(Mods, Ebins, [Mod|Missing])
- end.
-
-
-extra_modules(_Mods, [], Allowed, Extra) ->
- Extra--Allowed;
-extra_modules(Mods, [Mod|Ebins], Allowed, Extra) ->
- case lists:member(Mod, Mods) of
- true ->
- extra_modules(Mods, Ebins, Allowed, Extra);
- false ->
- io:format("superfluous module: ~p~n", [Mod]),
- extra_modules(Mods, Ebins, Allowed, [Mod|Extra])
- end.
-
-
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-
-
-exportall(suite) ->
- [];
-exportall(doc) ->
- [];
-exportall(Config) when is_list(Config) ->
- AppFile = key1search(app_file, Config),
- Mods = key1search(modules, AppFile),
- check_export_all(Mods).
-
-
-check_export_all([]) ->
- ok;
-check_export_all([Mod|Mods]) ->
- case (catch apply(Mod, module_info, [compile])) of
- {'EXIT', {undef, _}} ->
- check_export_all(Mods);
- O ->
- case lists:keysearch(options, 1, O) of
- false ->
- check_export_all(Mods);
- {value, {options, List}} ->
- case lists:member(export_all, List) of
- true ->
- fail({export_all, Mod});
- false ->
- check_export_all(Mods)
- end
- end
- end.
-
-
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-
-app_depend(suite) ->
- [];
-app_depend(doc) ->
- [];
-app_depend(Config) when is_list(Config) ->
- AppFile = key1search(app_file, Config),
- Apps = key1search(applications, AppFile),
- check_apps(Apps).
-
-
-check_apps([]) ->
- ok;
-check_apps([App|Apps]) ->
- case is_app(App) of
- {ok, _} ->
- check_apps(Apps);
- Error ->
- throw({error, {missing_app, {App, Error}}})
- end.
-
-
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-
-
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-
-start_and_stop_empty(suite) ->
- [];
-start_and_stop_empty(doc) ->
- ["Start and stop the application empty (no configured components)"];
-start_and_stop_empty(Config) when is_list(Config) ->
- ?line false = ?IS_SNMP_RUNNING(),
-
- ?line ok = snmp:start(),
-
- ?line true = ?IS_SNMP_RUNNING(),
-
- ?line ok = snmp:stop(),
-
- ?line false = ?IS_SNMP_RUNNING(),
-
- ok.
-
-
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-
-start_and_stop_with_agent(suite) ->
- [];
-start_and_stop_with_agent(doc) ->
- ["Start and stop the application with the agent pre-configured"];
-start_and_stop_with_agent(Config) when is_list(Config) ->
- ?SKIP(not_implemented_yet).
-
-
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-
-start_and_stop_with_manager(suite) ->
- [];
-start_and_stop_with_manager(doc) ->
- ["Start and stop the application with the manager pre-configured"];
-start_and_stop_with_manager(Config) when is_list(Config) ->
- ?SKIP(not_implemented_yet).
-
-
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-
-start_and_stop_with_agent_and_manager(suite) ->
- [];
-start_and_stop_with_agent_and_manager(doc) ->
- ["Start and stop the application with both the agent "
- "and the manager pre-configured"];
-start_and_stop_with_agent_and_manager(Config) when is_list(Config) ->
- ?SKIP(not_implemented_yet).
-
-
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-
-start_epmty_and_then_agent_and_manager_and_stop(suite) ->
- [];
-start_epmty_and_then_agent_and_manager_and_stop(doc) ->
- ["Start the application empty, then start the agent and then "
- "the manager and then stop the application"];
-start_epmty_and_then_agent_and_manager_and_stop(Config) when is_list(Config) ->
- ?SKIP(not_implemented_yet).
-
-
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-
-start_with_agent_and_then_manager_and_stop(suite) ->
- [];
-start_with_agent_and_then_manager_and_stop(doc) ->
- ["Start the application with the agent pre-configured, "
- "then start the manager and then stop the application"];
-start_with_agent_and_then_manager_and_stop(Config) when is_list(Config) ->
- ?SKIP(not_implemented_yet).
-
-
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-
-start_with_manager_and_then_agent_and_stop(suite) ->
- [];
-start_with_manager_and_then_agent_and_stop(doc) ->
- ["Start the application with the manager pre-configured, "
- "then start the agent and then stop the application"];
-start_with_manager_and_then_agent_and_stop(Config) when is_list(Config) ->
- ?SKIP(not_implemented_yet).
-
-
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-
-
-fail(Reason) ->
- exit({suite_failed, Reason}).
-
-key1search(Key, L) ->
- case lists:keysearch(Key, 1, L) of
- undefined ->
- fail({not_found, Key, L});
- {value, {Key, Value}} ->
- Value
- end.
+ [
+ app,
+ appup
+ ].
+
+%%--------------------------------------------------------------------
+%% Test Cases --------------------------------------------------------
+%%--------------------------------------------------------------------
+app() ->
+ [{doc, "Test that the snmp app file is ok"}].
+app(Config) when is_list(Config) ->
+ ok = test_server:app_test(snmp).
+%%--------------------------------------------------------------------
+appup() ->
+ [{doc, "Test that the snmp appup file is ok"}].
+appup(Config) when is_list(Config) ->
+ ok = test_server:appup_test(snmp).
diff --git a/lib/snmp/test/snmp_manager_test.erl b/lib/snmp/test/snmp_manager_test.erl
index d17882e765..71f4017d8b 100644
--- a/lib/snmp/test/snmp_manager_test.erl
+++ b/lib/snmp/test/snmp_manager_test.erl
@@ -583,38 +583,38 @@ init_per_group(event_tests_mt = GroupName, Config) ->
GroupName,
[{manager_net_if_module, snmpm_net_if_mt} | Config]);
init_per_group(ipv6_mt = GroupName, Config) ->
+ {ok, Hostname0} = inet:gethostname(),
case ct:require(ipv6_hosts) of
ok ->
- case gen_udp:open(0, [inet6]) of
- {ok, S} ->
- ok = gen_udp:close(S),
+ case lists:member(list_to_atom(Hostname0), ct:get_config(ipv6_hosts)) of
+ true ->
ipv6_init(
snmp_test_lib:init_group_top_dir(
GroupName,
[{manager_net_if_module, snmpm_net_if_mt}
| Config]));
- {error, _} ->
- {skip, "Host seems to not support IPv6"}
+ false ->
+ {skip, "Host does not support IPv6"}
end;
_ ->
- {skip, "Host does not support IPV6"}
+ {skip, "Test config ipv6_hosts is missing"}
end;
init_per_group(ipv6 = GroupName, Config) ->
+ {ok, Hostname0} = inet:gethostname(),
case ct:require(ipv6_hosts) of
ok ->
- case gen_udp:open(0, [inet6]) of
- {ok, S} ->
- ok = gen_udp:close(S),
+ case lists:member(list_to_atom(Hostname0), ct:get_config(ipv6_hosts)) of
+ true ->
ipv6_init(snmp_test_lib:init_group_top_dir(GroupName, Config));
- {error, _} ->
- {skip, "Host seems to not support IPv6"}
+ false ->
+ {skip, "Host does not support IPv6"}
end;
_ ->
- {skip, "Host does not support IPV6"}
+ {skip, "Test config ipv6_hosts is missing"}
end;
init_per_group(GroupName, Config) ->
snmp_test_lib:init_group_top_dir(GroupName, Config).
-
+
end_per_group(_GroupName, Config) ->
%% Do we really need to do this?
lists:keydelete(snmp_group_top_dir, 1, Config).
diff --git a/lib/snmp/test/snmp_to_snmpnet_SUITE.erl b/lib/snmp/test/snmp_to_snmpnet_SUITE.erl
index ac9e37bc8b..24c14d86ea 100644
--- a/lib/snmp/test/snmp_to_snmpnet_SUITE.erl
+++ b/lib/snmp/test/snmp_to_snmpnet_SUITE.erl
@@ -121,14 +121,14 @@ init_per_group(_, Config) ->
Config.
init_per_group_ipv6(Families, Config) ->
+ {ok, Hostname0} = inet:gethostname(),
case ct:require(ipv6_hosts) of
ok ->
- case gen_udp:open(0, [inet6]) of
- {ok, S} ->
- ok = gen_udp:close(S),
+ case lists:member(list_to_atom(Hostname0), ct:get_config(ipv6_hosts)) of
+ true ->
init_per_group_ip(Families, Config);
- {error, _} ->
- {skip, "Host seems to not support IPv6"}
+ false ->
+ {skip, "Host does not support IPv6"}
end;
_ ->
{skip, "Test config ipv6_hosts is missing"}
diff --git a/lib/snmp/vsn.mk b/lib/snmp/vsn.mk
index f95b428290..28eba0d0d6 100644
--- a/lib/snmp/vsn.mk
+++ b/lib/snmp/vsn.mk
@@ -19,6 +19,6 @@
# %CopyrightEnd%
APPLICATION = snmp
-SNMP_VSN = 5.2.3
+SNMP_VSN = 5.2.4
PRE_VSN =
APP_VSN = "$(APPLICATION)-$(SNMP_VSN)$(PRE_VSN)"
diff --git a/lib/ssh/doc/src/notes.xml b/lib/ssh/doc/src/notes.xml
index f9d11b2a60..b990c18e9a 100644
--- a/lib/ssh/doc/src/notes.xml
+++ b/lib/ssh/doc/src/notes.xml
@@ -30,6 +30,68 @@
<file>notes.xml</file>
</header>
+<section><title>Ssh 4.3.2</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Upgrade of an established client connection could crash
+ because the ssh client supervisors children had wrong
+ type. This is fixed now.</p>
+ <p>
+ Own Id: OTP-13782 Aux Id: seq13158 </p>
+ </item>
+ <item>
+ <p>
+ Partly checks the public key early in public key
+ authorization</p>
+ <p>
+ Own Id: OTP-13847 Aux Id:
+ defensics-ssh3.1.0-190243,205277,219318 </p>
+ </item>
+ <item>
+ <p>
+ Corrected handling of SHA for ECDSA (Elliptic curve
+ public keys)</p>
+ <p>
+ Own Id: OTP-13850 Aux Id: defensics-ssh3.1.0-214168 </p>
+ </item>
+ <item>
+ <p>
+ Problems found by test suites as well as by
+ Codenomicon/Defensics fixed: - reduce max random padding
+ to 15 bytes (Codenomicon/Defensics) - inclomplete pdu
+ handling (Codenomicon/Defensics) - badmatch in test suite
+ - non-blocking send fixes deadlock in
+ ssh_connection_SUITE:interrupted_send</p>
+ <p>
+ Own Id: OTP-13854</p>
+ </item>
+ <item>
+ <p>
+ Caller is now notified when a tcp close is received.</p>
+ <p>
+ Own Id: OTP-13859 Aux Id: seq13177 </p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Use application:ensure_all_started/2 instead of
+ hard-coding deps</p>
+ <p>
+ Own Id: OTP-13843 Aux Id: PR-1147 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Ssh 4.3.1</title>
<section><title>Fixed Bugs and Malfunctions</title>
diff --git a/lib/ssh/doc/src/ssh.xml b/lib/ssh/doc/src/ssh.xml
index e6c54d27bf..ef9f7cbd9b 100644
--- a/lib/ssh/doc/src/ssh.xml
+++ b/lib/ssh/doc/src/ssh.xml
@@ -4,7 +4,7 @@
<erlref>
<header>
<copyright>
- <year>2004</year><year>2015</year>
+ <year>2004</year><year>2016</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -756,7 +756,7 @@
<p>Utility function that starts the applications <c>crypto</c>, <c>public_key</c>,
and <c>ssh</c>. Default type is <c>temporary</c>.
For more information, see the <seealso marker="kernel:application">application(3)</seealso>
- manual page in <c>kernel</c>.</p>
+ manual page in Kernel.</p>
</desc>
</func>
@@ -769,7 +769,7 @@
<desc>
<p>Stops the <c>ssh</c> application.
For more information, see the <seealso marker="kernel:application">application(3)</seealso>
- manual page in <c>kernel</c>.</p>
+ manual page in Kernel.</p>
</desc>
</func>
diff --git a/lib/ssh/doc/src/ssh_app.xml b/lib/ssh/doc/src/ssh_app.xml
index f6ce44c015..5cc4c24889 100644
--- a/lib/ssh/doc/src/ssh_app.xml
+++ b/lib/ssh/doc/src/ssh_app.xml
@@ -4,7 +4,7 @@
<appref>
<header>
<copyright>
- <year>2012</year><year>2015</year>
+ <year>2012</year><year>2016</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -203,7 +203,7 @@
<section>
<title>Unicode support</title>
<p>Unicode filenames are supported if the emulator and the underlaying OS support it. See section DESCRIPTION in the
- <seealso marker="kernel:file">file</seealso> manual page in <c>kernel</c> for information about this subject.
+ <seealso marker="kernel:file">file</seealso> manual page in Kernel for information about this subject.
</p>
<p>The shell and the cli both support unicode.
</p>
diff --git a/lib/ssh/doc/src/ssh_channel.xml b/lib/ssh/doc/src/ssh_channel.xml
index 907b0b3bec..7b598494f7 100644
--- a/lib/ssh/doc/src/ssh_channel.xml
+++ b/lib/ssh/doc/src/ssh_channel.xml
@@ -139,7 +139,7 @@
enters the <c>ssh_channel</c> process receive loop and become an
<c>ssh_channel process</c>. The process must have been started using
one of the start functions in <c>proc_lib</c>, see the <seealso
- marker="stdlib:proc_lib">proc_lib(3)</seealso> manual page in <c>stdlib</c>.
+ marker="stdlib:proc_lib">proc_lib(3)</seealso> manual page in STDLIB.
The user is responsible for any initialization of the process
and must call <seealso marker = "#init-1">ssh_channel:init/1</seealso>.
</p>
diff --git a/lib/ssh/src/ssh.app.src b/lib/ssh/src/ssh.app.src
index 3245ba5197..76b7d8cd55 100644
--- a/lib/ssh/src/ssh.app.src
+++ b/lib/ssh/src/ssh.app.src
@@ -45,7 +45,7 @@
"erts-6.0",
"kernel-3.0",
"public_key-1.1",
- "stdlib-3.0"
+ "stdlib-3.1"
]}]}.
diff --git a/lib/ssh/src/ssh.erl b/lib/ssh/src/ssh.erl
index 0570853a9b..1d7be3547b 100644
--- a/lib/ssh/src/ssh.erl
+++ b/lib/ssh/src/ssh.erl
@@ -52,16 +52,15 @@
%% is temporary. see application(3)
%%--------------------------------------------------------------------
start() ->
- application:start(crypto),
- application:start(asn1),
- application:start(public_key),
- application:start(ssh).
+ start(temporary).
start(Type) ->
- application:start(crypto, Type),
- application:start(asn1),
- application:start(public_key, Type),
- application:start(ssh, Type).
+ case application:ensure_all_started(ssh, Type) of
+ {ok, _} ->
+ ok;
+ Other ->
+ Other
+ end.
%%--------------------------------------------------------------------
-spec stop() -> ok | {error, term()}.
@@ -90,7 +89,7 @@ connect(Socket, Options, Timeout) when is_port(Socket) ->
{error, Error};
{_SocketOptions, SshOptions} ->
case valid_socket_to_use(Socket, Options) of
- ok ->
+ ok ->
{ok, {Host,_Port}} = inet:sockname(Socket),
Opts = [{user_pid,self()}, {host,fmt_host(Host)} | SshOptions],
ssh_connection_handler:start_connection(client, Socket, Opts, Timeout);
@@ -128,23 +127,23 @@ connect(Host, Port, Options, Timeout) ->
-spec close(pid()) -> ok.
%%
%% Description: Closes an ssh connection.
-%%--------------------------------------------------------------------
+%%--------------------------------------------------------------------
close(ConnectionRef) ->
ssh_connection_handler:stop(ConnectionRef).
%%--------------------------------------------------------------------
-spec connection_info(pid(), [atom()]) -> [{atom(), term()}].
%%
-%% Description: Retrieves information about a connection.
-%%--------------------------------------------------------------------
+%% Description: Retrieves information about a connection.
+%%--------------------------------------------------------------------
connection_info(ConnectionRef, Options) ->
ssh_connection_handler:connection_info(ConnectionRef, Options).
%%--------------------------------------------------------------------
-spec channel_info(pid(), channel_id(), [atom()]) -> [{atom(), term()}].
%%
-%% Description: Retrieves information about a connection.
-%%--------------------------------------------------------------------
+%% Description: Retrieves information about a connection.
+%%--------------------------------------------------------------------
channel_info(ConnectionRef, ChannelId, Options) ->
ssh_connection_handler:channel_info(ConnectionRef, ChannelId, Options).
@@ -153,9 +152,9 @@ channel_info(ConnectionRef, ChannelId, Options) ->
-spec daemon(integer()|port(), proplists:proplist()) -> {ok, pid()} | {error, term()}.
-spec daemon(any | inet:ip_address(), integer(), proplists:proplist()) -> {ok, pid()} | {error, term()}.
-%% Description: Starts a server listening for SSH connections
+%% Description: Starts a server listening for SSH connections
%% on the given port.
-%%--------------------------------------------------------------------
+%%--------------------------------------------------------------------
daemon(Port) ->
daemon(Port, []).
@@ -188,9 +187,9 @@ daemon_info(Pid) ->
-spec stop_listener(pid()) -> ok.
-spec stop_listener(inet:ip_address(), integer()) -> ok.
%%
-%% Description: Stops the listener, but leaves
+%% Description: Stops the listener, but leaves
%% existing connections started by the listener up and running.
-%%--------------------------------------------------------------------
+%%--------------------------------------------------------------------
stop_listener(SysSup) ->
ssh_system_sup:stop_listener(SysSup).
stop_listener(Address, Port) ->
@@ -202,9 +201,9 @@ stop_listener(Address, Port, Profile) ->
-spec stop_daemon(pid()) -> ok.
-spec stop_daemon(inet:ip_address(), integer()) -> ok.
%%
-%% Description: Stops the listener and all connections started by
+%% Description: Stops the listener and all connections started by
%% the listener.
-%%--------------------------------------------------------------------
+%%--------------------------------------------------------------------
stop_daemon(SysSup) ->
ssh_system_sup:stop_system(SysSup).
stop_daemon(Address, Port) ->
@@ -243,7 +242,7 @@ start_shell({ok, ConnectionRef}) ->
case ssh_connection:session_channel(ConnectionRef, infinity) of
{ok,ChannelId} ->
success = ssh_connection:ptty_alloc(ConnectionRef, ChannelId, []),
- Args = [{channel_cb, ssh_shell},
+ Args = [{channel_cb, ssh_shell},
{init_args,[ConnectionRef, ChannelId]},
{cm, ConnectionRef}, {channel_id, ChannelId}],
{ok, State} = ssh_channel:init([Args]),
@@ -256,7 +255,7 @@ start_shell(Error) ->
%%--------------------------------------------------------------------
%%--------------------------------------------------------------------
-default_algorithms() ->
+default_algorithms() ->
ssh_transport:default_algorithms().
%%--------------------------------------------------------------------
@@ -296,13 +295,13 @@ daemon_shell_opt(Options) ->
daemon_host_inet_opt(HostAddr, Options1) ->
case HostAddr of
any ->
- {ok, Host0} = inet:gethostname(),
+ {ok, Host0} = inet:gethostname(),
{Host0, proplists:get_value(inet, Options1, inet), Options1};
{_,_,_,_} ->
- {HostAddr, inet,
+ {HostAddr, inet,
[{ip, HostAddr} | Options1]};
{_,_,_,_,_,_,_,_} ->
- {HostAddr, inet6,
+ {HostAddr, inet6,
[{ip, HostAddr} | Options1]}
end.
@@ -313,8 +312,8 @@ start_daemon(Socket, Options) ->
{error, Error};
{SocketOptions, SshOptions} ->
case valid_socket_to_use(Socket, Options) of
- ok ->
- try
+ ok ->
+ try
do_start_daemon(Socket, [{role,server}|SshOptions], SocketOptions)
catch
throw:bad_fd -> {error,bad_fd};
@@ -330,16 +329,16 @@ start_daemon(Host, Port, Options, Inet) ->
{error, _Reason} = Error ->
Error;
{SocketOptions, SshOptions}->
- try
+ try
do_start_daemon(Host, Port, [{role,server}|SshOptions] , [Inet|SocketOptions])
catch
throw:bad_fd -> {error,bad_fd};
_C:_E -> {error,{cannot_start_daemon,_C,_E}}
end
end.
-
+
do_start_daemon(Socket, SshOptions, SocketOptions) ->
- {ok, {IP,Port}} =
+ {ok, {IP,Port}} =
try {ok,_} = inet:sockname(Socket)
catch
_:_ -> throw(bad_socket)
@@ -351,7 +350,7 @@ do_start_daemon(Socket, SshOptions, SocketOptions) ->
{address, Host},
{port, Port},
{role, server},
- {socket_opts, SocketOptions},
+ {socket_opts, SocketOptions},
{ssh_opts, SshOptions}],
{_, Callback, _} = proplists:get_value(transport, SshOptions, {tcp, gen_tcp, tcp_closed}),
case ssh_system_sup:system_supervisor(Host, Port, Profile) of
@@ -385,7 +384,7 @@ do_start_daemon(Socket, SshOptions, SocketOptions) ->
end.
do_start_daemon(Host0, Port0, SshOptions, SocketOptions) ->
- {Host,Port1} =
+ {Host,Port1} =
try
case proplists:get_value(fd, SocketOptions) of
undefined ->
@@ -402,21 +401,21 @@ do_start_daemon(Host0, Port0, SshOptions, SocketOptions) ->
{Port, WaitRequestControl, Opts0} =
case Port1 of
0 -> %% Allocate the socket here to get the port number...
- {_, Callback, _} =
+ {_, Callback, _} =
proplists:get_value(transport, SshOptions, {tcp, gen_tcp, tcp_closed}),
{ok,LSock} = ssh_acceptor:callback_listen(Callback, 0, SocketOptions),
{ok,{_,LPort}} = inet:sockname(LSock),
- {LPort,
- {LSock,Callback},
+ {LPort,
+ {LSock,Callback},
[{lsocket,LSock},{lsock_owner,self()}]
};
_ ->
{Port1, false, []}
end,
- Opts = [{address, Host},
+ Opts = [{address, Host},
{port, Port},
{role, server},
- {socket_opts, SocketOptions},
+ {socket_opts, SocketOptions},
{ssh_opts, SshOptions} | Opts0],
case ssh_system_sup:system_supervisor(Host, Port, Profile) of
undefined ->
@@ -465,7 +464,7 @@ find_hostport(Fd) ->
{ok, HostPort} = inet:sockname(S),
ok = inet:close(S),
HostPort.
-
+
handle_options(Opts) ->
try handle_option(algs_compatibility(proplists:unfold(Opts)), [], []) of
@@ -480,9 +479,9 @@ handle_options(Opts) ->
algs_compatibility(Os0) ->
%% Take care of old options 'public_key_alg' and 'pref_public_key_algs'
case proplists:get_value(public_key_alg, Os0) of
- undefined ->
+ undefined ->
Os0;
- A when is_atom(A) ->
+ A when is_atom(A) ->
%% Skip public_key_alg if pref_public_key_algs is defined:
Os = lists:keydelete(public_key_alg, 1, Os0),
case proplists:get_value(pref_public_key_algs,Os) of
@@ -492,7 +491,7 @@ algs_compatibility(Os0) ->
[{pref_public_key_algs,['ssh-dss','ssh-rsa']} | Os];
undefined ->
throw({error, {eoptions, {public_key_alg,A} }});
- _ ->
+ _ ->
Os
end;
V ->
@@ -620,7 +619,7 @@ handle_ssh_option({silently_accept_hosts, Value} = Opt) when is_boolean(Value) -
Opt;
handle_ssh_option({user_interaction, Value} = Opt) when is_boolean(Value) ->
Opt;
-handle_ssh_option({preferred_algorithms,[_|_]} = Opt) ->
+handle_ssh_option({preferred_algorithms,[_|_]} = Opt) ->
handle_pref_algs(Opt);
handle_ssh_option({dh_gex_groups,L0}) when is_list(L0) ->
@@ -629,7 +628,7 @@ handle_ssh_option({dh_gex_groups,L0}) when is_list(L0) ->
lists:foldl(
fun({N,G,P}, Acc) when is_integer(N),N>0,
is_integer(G),G>0,
- is_integer(P),P>0 ->
+ is_integer(P),P>0 ->
[{N,{G,P}} | Acc];
({N,{G,P}}, Acc) when is_integer(N),N>0,
is_integer(G),G>0,
@@ -637,7 +636,7 @@ handle_ssh_option({dh_gex_groups,L0}) when is_list(L0) ->
[{N,{G,P}} | Acc];
({N,GPs}, Acc) when is_list(GPs) ->
lists:foldr(fun({Gi,Pi}, Acci) when is_integer(Gi),Gi>0,
- is_integer(Pi),Pi>0 ->
+ is_integer(Pi),Pi>0 ->
[{N,{Gi,Pi}} | Acci]
end, Acc, GPs)
end, [], L0))};
@@ -647,7 +646,7 @@ handle_ssh_option({dh_gex_groups,{Tag,File=[C|_]}}=Opt) when is_integer(C), C>0,
Tag == ssh_moduli_file ->
{ok,GroupDefs} =
case Tag of
- file ->
+ file ->
file:consult(File);
ssh_moduli_file ->
case file:open(File,[read]) of
@@ -672,14 +671,14 @@ handle_ssh_option({dh_gex_groups,{Tag,File=[C|_]}}=Opt) when is_integer(C), C>0,
catch
_:_ ->
throw({error, {{eoptions, Opt}, "Bad format in file: "++File}})
- end;
-
+ end;
+
-handle_ssh_option({dh_gex_limits,{Min,Max}} = Opt) when is_integer(Min), Min>0,
+handle_ssh_option({dh_gex_limits,{Min,Max}} = Opt) when is_integer(Min), Min>0,
is_integer(Max), Max>=Min ->
%% Server
Opt;
-handle_ssh_option({dh_gex_limits,{Min,I,Max}} = Opt) when is_integer(Min), Min>0,
+handle_ssh_option({dh_gex_limits,{Min,I,Max}} = Opt) when is_integer(Min), Min>0,
is_integer(I), I>=Min,
is_integer(Max), Max>=I ->
%% Client
@@ -724,7 +723,7 @@ handle_ssh_option({keyboard_interact_fun, Value} = Opt) when is_function(Value,3
Opt;
handle_ssh_option({compression, Value} = Opt) when is_atom(Value) ->
Opt;
-handle_ssh_option({exec, {Module, Function, _}} = Opt) when is_atom(Module),
+handle_ssh_option({exec, {Module, Function, _}} = Opt) when is_atom(Module),
is_atom(Function) ->
Opt;
handle_ssh_option({exec, Function} = Opt) when is_function(Function) ->
@@ -772,7 +771,7 @@ handle_ssh_option({quiet_mode, Value} = Opt) when is_boolean(Value) ->
Opt;
handle_ssh_option({idle_time, Value} = Opt) when is_integer(Value), Value > 0 ->
Opt;
-handle_ssh_option({rekey_limit, Value} = Opt) when is_integer(Value) ->
+handle_ssh_option({rekey_limit, Value} = Opt) when is_integer(Value) ->
Opt;
handle_ssh_option({id_string, random}) ->
{id_string, {random,2,5}}; %% 2 - 5 random characters
@@ -814,11 +813,11 @@ handle_pref_algs({preferred_algorithms,Algs}) ->
of
DefAlgs -> handle_pref_alg(Key,Vals,DefAlgs)
catch
- _:_ -> throw({error, {{eoptions, {preferred_algorithms,Key}},
+ _:_ -> throw({error, {{eoptions, {preferred_algorithms,Key}},
"Bad preferred_algorithms key"}})
end || {Key,Vals} <- Algs]
};
-
+
Dups ->
throw({error, {{eoptions, {preferred_algorithms,Dups}}, "Duplicates found"}})
catch
@@ -857,13 +856,13 @@ handle_pref_alg(Key,
) ->
handle_pref_alg(Key, lists:reverse(Vs), Sup);
-handle_pref_alg(Key,
+handle_pref_alg(Key,
Vs=[V|_],
Sup=[{client2server,_},{server2client,_}]
) when is_atom(V) ->
handle_pref_alg(Key, [{client2server,Vs},{server2client,Vs}], Sup);
-handle_pref_alg(Key,
+handle_pref_alg(Key,
Vs=[V|_],
Sup=[S|_]
) when is_atom(V), is_atom(S) ->
@@ -878,14 +877,14 @@ chk_alg_vs(OptKey, Values, SupportedValues) ->
[] -> Values;
Bad -> throw({error, {{eoptions, {OptKey,Bad}}, "Unsupported value(s) found"}})
end.
-
+
handle_ip(Inet) -> %% Default to ipv4
case lists:member(inet, Inet) of
true ->
Inet;
false ->
case lists:member(inet6, Inet) of
- true ->
+ true ->
Inet;
false ->
[inet | Inet]
@@ -916,8 +915,8 @@ directory_exist_readable(Dir) ->
{error, Error} ->
{error, Error}
end.
-
-
+
+
collect_per_size(L) ->
lists:foldr(
@@ -948,7 +947,7 @@ read_moduli_file(D, I, Acc) ->
read_moduli_file(D, I+1, Acc)
end
end.
-
+
handle_user_pref_pubkey_algs([], Acc) ->
{true, lists:reverse(Acc)};
handle_user_pref_pubkey_algs([H|T], Acc) ->
@@ -963,7 +962,7 @@ handle_user_pref_pubkey_algs([H|T], Acc) ->
false
end.
-fmt_host({A,B,C,D}) ->
+fmt_host({A,B,C,D}) ->
lists:concat([A,".",B,".",C,".",D]);
-fmt_host(T={_,_,_,_,_,_,_,_}) ->
+fmt_host(T={_,_,_,_,_,_,_,_}) ->
lists:flatten(string:join([io_lib:format("~.16B",[A]) || A <- tuple_to_list(T)], ":")).
diff --git a/lib/ssh/src/ssh.hrl b/lib/ssh/src/ssh.hrl
index 868f3a9181..4cd91177f6 100644
--- a/lib/ssh/src/ssh.hrl
+++ b/lib/ssh/src/ssh.hrl
@@ -127,7 +127,7 @@
recv_sequence = 0,
keyex_key,
keyex_info,
- random_length_padding = 255, % From RFC 4253 section 6.
+ random_length_padding = 15, % From RFC 4253 section 6.
%% User auth
user,
diff --git a/lib/ssh/src/ssh_auth.erl b/lib/ssh/src/ssh_auth.erl
index fb5e086656..ac35b70209 100644
--- a/lib/ssh/src/ssh_auth.erl
+++ b/lib/ssh/src/ssh_auth.erl
@@ -140,7 +140,7 @@ publickey_msg([Alg, #ssh{user = User,
session_id = SessionId,
service = Service,
opts = Opts} = Ssh]) ->
- Hash = sha, %% Maybe option?!
+ Hash = ssh_transport:sha(Alg),
KeyCb = proplists:get_value(key_cb, Opts, ssh_file),
case KeyCb:user_key(Alg, Opts) of
{ok, PrivKey} ->
@@ -260,32 +260,54 @@ handle_userauth_request(#ssh_msg_userauth_request{user = User,
handle_userauth_request(#ssh_msg_userauth_request{user = User,
service = "ssh-connection",
method = "publickey",
- data = Data},
- SessionId,
+ data = <<?BYTE(?FALSE),
+ ?UINT32(ALen), BAlg:ALen/binary,
+ ?UINT32(KLen), KeyBlob:KLen/binary,
+ _/binary
+ >>
+ },
+ _SessionId,
#ssh{opts = Opts,
userauth_supported_methods = Methods} = Ssh) ->
- <<?BYTE(HaveSig), ?UINT32(ALen), BAlg:ALen/binary,
- ?UINT32(KLen), KeyBlob:KLen/binary, SigWLen/binary>> = Data,
- Alg = binary_to_list(BAlg),
- case HaveSig of
- ?TRUE ->
- case verify_sig(SessionId, User, "ssh-connection", Alg,
- KeyBlob, SigWLen, Opts) of
- true ->
- {authorized, User,
- ssh_transport:ssh_packet(
- #ssh_msg_userauth_success{}, Ssh)};
- false ->
- {not_authorized, {User, undefined},
- ssh_transport:ssh_packet(#ssh_msg_userauth_failure{
- authentications = Methods,
- partial_success = false}, Ssh)}
- end;
- ?FALSE ->
+
+ case pre_verify_sig(User, binary_to_list(BAlg),
+ KeyBlob, Opts) of
+ true ->
{not_authorized, {User, undefined},
ssh_transport:ssh_packet(
- #ssh_msg_userauth_pk_ok{algorithm_name = Alg,
- key_blob = KeyBlob}, Ssh)}
+ #ssh_msg_userauth_pk_ok{algorithm_name = binary_to_list(BAlg),
+ key_blob = KeyBlob}, Ssh)};
+ false ->
+ {not_authorized, {User, undefined},
+ ssh_transport:ssh_packet(#ssh_msg_userauth_failure{
+ authentications = Methods,
+ partial_success = false}, Ssh)}
+ end;
+
+handle_userauth_request(#ssh_msg_userauth_request{user = User,
+ service = "ssh-connection",
+ method = "publickey",
+ data = <<?BYTE(?TRUE),
+ ?UINT32(ALen), BAlg:ALen/binary,
+ ?UINT32(KLen), KeyBlob:KLen/binary,
+ SigWLen/binary>>
+ },
+ SessionId,
+ #ssh{opts = Opts,
+ userauth_supported_methods = Methods} = Ssh) ->
+
+ case verify_sig(SessionId, User, "ssh-connection",
+ binary_to_list(BAlg),
+ KeyBlob, SigWLen, Opts) of
+ true ->
+ {authorized, User,
+ ssh_transport:ssh_packet(
+ #ssh_msg_userauth_success{}, Ssh)};
+ false ->
+ {not_authorized, {User, undefined},
+ ssh_transport:ssh_packet(#ssh_msg_userauth_failure{
+ authentications = Methods,
+ partial_success = false}, Ssh)}
end;
handle_userauth_request(#ssh_msg_userauth_request{user = User,
@@ -384,10 +406,22 @@ handle_userauth_info_response(#ssh_msg_userauth_info_response{num_responses = 1,
kb_tries_left = KbTriesLeft,
user = User,
userauth_supported_methods = Methods} = Ssh) ->
+ SendOneEmpty = proplists:get_value(tstflg, Opts) == one_empty,
case check_password(User, unicode:characters_to_list(Password), Opts, Ssh) of
+ {true,Ssh1} when SendOneEmpty==true ->
+ Msg = #ssh_msg_userauth_info_request{name = "",
+ instruction = "",
+ language_tag = "",
+ num_prompts = 0,
+ data = <<?BOOLEAN(?FALSE)>>
+ },
+ {authorized_but_one_more, User,
+ ssh_transport:ssh_packet(Msg, Ssh1)};
+
{true,Ssh1} ->
{authorized, User,
ssh_transport:ssh_packet(#ssh_msg_userauth_success{}, Ssh1)};
+
{false,Ssh1} ->
{not_authorized, {User, {error,"Bad user or password"}},
ssh_transport:ssh_packet(#ssh_msg_userauth_failure{
@@ -397,6 +431,11 @@ handle_userauth_info_response(#ssh_msg_userauth_info_response{num_responses = 1,
)}
end;
+handle_userauth_info_response({extra,#ssh_msg_userauth_info_response{}},
+ #ssh{user = User} = Ssh) ->
+ {authorized, User,
+ ssh_transport:ssh_packet(#ssh_msg_userauth_success{}, Ssh)};
+
handle_userauth_info_response(#ssh_msg_userauth_info_response{},
_Auth) ->
ssh_connection_handler:disconnect(
@@ -473,19 +512,34 @@ get_password_option(Opts, User) ->
false -> proplists:get_value(password, Opts, false)
end.
-verify_sig(SessionId, User, Service, Alg, KeyBlob, SigWLen, Opts) ->
- {ok, Key} = decode_public_key_v2(KeyBlob, Alg),
- KeyCb = proplists:get_value(key_cb, Opts, ssh_file),
+pre_verify_sig(User, Alg, KeyBlob, Opts) ->
+ try
+ {ok, Key} = decode_public_key_v2(KeyBlob, Alg),
+ KeyCb = proplists:get_value(key_cb, Opts, ssh_file),
+ KeyCb:is_auth_key(Key, User, Opts)
+ catch
+ _:_ ->
+ false
+ end.
- case KeyCb:is_auth_key(Key, User, Opts) of
- true ->
- PlainText = build_sig_data(SessionId, User,
- Service, KeyBlob, Alg),
- <<?UINT32(AlgSigLen), AlgSig:AlgSigLen/binary>> = SigWLen,
- <<?UINT32(AlgLen), _Alg:AlgLen/binary,
- ?UINT32(SigLen), Sig:SigLen/binary>> = AlgSig,
- ssh_transport:verify(PlainText, sha, Sig, Key);
- false ->
+verify_sig(SessionId, User, Service, Alg, KeyBlob, SigWLen, Opts) ->
+ try
+ {ok, Key} = decode_public_key_v2(KeyBlob, Alg),
+ KeyCb = proplists:get_value(key_cb, Opts, ssh_file),
+
+ case KeyCb:is_auth_key(Key, User, Opts) of
+ true ->
+ PlainText = build_sig_data(SessionId, User,
+ Service, KeyBlob, Alg),
+ <<?UINT32(AlgSigLen), AlgSig:AlgSigLen/binary>> = SigWLen,
+ <<?UINT32(AlgLen), _Alg:AlgLen/binary,
+ ?UINT32(SigLen), Sig:SigLen/binary>> = AlgSig,
+ ssh_transport:verify(PlainText, ssh_transport:sha(list_to_atom(Alg)), Sig, Key);
+ false ->
+ false
+ end
+ catch
+ _:_ ->
false
end.
diff --git a/lib/ssh/src/ssh_cli.erl b/lib/ssh/src/ssh_cli.erl
index 74cd2e081a..8af0ecc5f9 100644
--- a/lib/ssh/src/ssh_cli.erl
+++ b/lib/ssh/src/ssh_cli.erl
@@ -208,8 +208,15 @@ handle_msg({Group, Req}, #state{group = Group, buf = Buf, pty = Pty,
write_chars(ConnectionHandler, ChannelId, Chars),
{ok, State#state{buf = NewBuf}};
-handle_msg({'EXIT', Group, _Reason}, #state{group = Group,
- channel = ChannelId} = State) ->
+handle_msg({'EXIT', Group, Reason}, #state{group = Group,
+ cm = ConnectionHandler,
+ channel = ChannelId} = State) ->
+ Status = case Reason of
+ normal -> 0;
+ _ -> -1
+ end,
+ ssh_connection:exit_status(ConnectionHandler, ChannelId, Status),
+ ssh_connection:send_eof(ConnectionHandler, ChannelId),
{stop, ChannelId, State};
handle_msg(_, State) ->
diff --git a/lib/ssh/src/ssh_connection_handler.erl b/lib/ssh/src/ssh_connection_handler.erl
index f9f4c82351..facf6b561a 100644
--- a/lib/ssh/src/ssh_connection_handler.erl
+++ b/lib/ssh/src/ssh_connection_handler.erl
@@ -60,7 +60,8 @@
]).
%%% Behaviour callbacks
--export([handle_event/4, terminate/3, format_status/2, code_change/4]).
+-export([callback_mode/0, handle_event/4, terminate/3,
+ format_status/2, code_change/4]).
%%% Exports not intended to be used :). They are used for spawning and tests
-export([init_connection_handler/3, % proc_lib:spawn needs this
@@ -338,6 +339,7 @@ renegotiate_data(ConnectionHandler) ->
ssh_params :: #ssh{}
| undefined,
socket :: inet:socket(),
+ sender :: pid() | undefined,
decrypted_data_buffer = <<>> :: binary(),
encrypted_data_buffer = <<>> :: binary(),
undecrypted_packet_length :: undefined | non_neg_integer(),
@@ -366,22 +368,21 @@ init_connection_handler(Role, Socket, Opts) ->
{Protocol, Callback, CloseTag} =
proplists:get_value(transport, Opts, ?DefaultTransport),
S0#data{ssh_params = init_ssh_record(Role, Socket, Opts),
- transport_protocol = Protocol,
- transport_cb = Callback,
- transport_close_tag = CloseTag
+ sender = spawn_link(fun() -> nonblocking_sender(Socket, Callback) end),
+ transport_protocol = Protocol,
+ transport_cb = Callback,
+ transport_close_tag = CloseTag
}
of
S ->
gen_statem:enter_loop(?MODULE,
[], %%[{debug,[trace,log,statistics,debug]} || Role==server],
- handle_event_function,
{hello,Role},
S)
catch
_:Error ->
gen_statem:enter_loop(?MODULE,
[],
- handle_event_function,
{init_error,Error},
S0)
end.
@@ -504,6 +505,9 @@ init_ssh_record(Role, Socket, Opts) ->
%%% ######## Error in the initialisation ####
+callback_mode() ->
+ handle_event_function.
+
handle_event(_, _Event, {init_error,Error}, _) ->
case Error of
{badmatch,{error,enotconn}} ->
@@ -523,7 +527,7 @@ handle_event(_, _Event, {init_error,Error}, _) ->
%% The very first event that is sent when the we are set as controlling process of Socket
handle_event(_, socket_control, {hello,_}, D) ->
VsnMsg = ssh_transport:hello_version_msg(string_version(D#data.ssh_params)),
- ok = send_bytes(VsnMsg, D),
+ send_bytes(VsnMsg, D),
case inet:getopts(Socket=D#data.socket, [recbuf]) of
{ok, [{recbuf,Size}]} ->
%% Set the socket to the hello text line handling mode:
@@ -548,7 +552,7 @@ handle_event(_, {info_line,_Line}, {hello,Role}, D) ->
server ->
%% But the client may NOT send them to the server. Openssh answers with cleartext,
%% and so do we
- ok = send_bytes("Protocol mismatch.", D),
+ send_bytes("Protocol mismatch.", D),
{stop, {shutdown,"Protocol mismatch in version exchange. Client sent info lines."}}
end;
@@ -563,7 +567,7 @@ handle_event(_, {version_exchange,Version}, {hello,Role}, D) ->
{active, once},
{recbuf, D#data.inet_initial_recbuf_size}]),
{KeyInitMsg, SshPacket, Ssh} = ssh_transport:key_exchange_init_msg(Ssh1),
- ok = send_bytes(SshPacket, D),
+ send_bytes(SshPacket, D),
{next_state, {kexinit,Role,init}, D#data{ssh_params = Ssh,
key_exchange_init_msg = KeyInitMsg}};
not_supported ->
@@ -581,7 +585,7 @@ handle_event(_, {#ssh_msg_kexinit{}=Kex, Payload}, {kexinit,Role,ReNeg},
Ssh1 = ssh_transport:key_init(peer_role(Role), D#data.ssh_params, Payload),
Ssh = case ssh_transport:handle_kexinit_msg(Kex, OwnKex, Ssh1) of
{ok, NextKexMsg, Ssh2} when Role==client ->
- ok = send_bytes(NextKexMsg, D),
+ send_bytes(NextKexMsg, D),
Ssh2;
{ok, Ssh2} when Role==server ->
Ssh2
@@ -594,43 +598,43 @@ handle_event(_, {#ssh_msg_kexinit{}=Kex, Payload}, {kexinit,Role,ReNeg},
%%%---- diffie-hellman
handle_event(_, #ssh_msg_kexdh_init{} = Msg, {key_exchange,server,ReNeg}, D) ->
{ok, KexdhReply, Ssh1} = ssh_transport:handle_kexdh_init(Msg, D#data.ssh_params),
- ok = send_bytes(KexdhReply, D),
+ send_bytes(KexdhReply, D),
{ok, NewKeys, Ssh} = ssh_transport:new_keys_message(Ssh1),
- ok = send_bytes(NewKeys, D),
+ send_bytes(NewKeys, D),
{next_state, {new_keys,server,ReNeg}, D#data{ssh_params=Ssh}};
handle_event(_, #ssh_msg_kexdh_reply{} = Msg, {key_exchange,client,ReNeg}, D) ->
{ok, NewKeys, Ssh} = ssh_transport:handle_kexdh_reply(Msg, D#data.ssh_params),
- ok = send_bytes(NewKeys, D),
+ send_bytes(NewKeys, D),
{next_state, {new_keys,client,ReNeg}, D#data{ssh_params=Ssh}};
%%%---- diffie-hellman group exchange
handle_event(_, #ssh_msg_kex_dh_gex_request{} = Msg, {key_exchange,server,ReNeg}, D) ->
{ok, GexGroup, Ssh} = ssh_transport:handle_kex_dh_gex_request(Msg, D#data.ssh_params),
- ok = send_bytes(GexGroup, D),
+ send_bytes(GexGroup, D),
{next_state, {key_exchange_dh_gex_init,server,ReNeg}, D#data{ssh_params=Ssh}};
handle_event(_, #ssh_msg_kex_dh_gex_request_old{} = Msg, {key_exchange,server,ReNeg}, D) ->
{ok, GexGroup, Ssh} = ssh_transport:handle_kex_dh_gex_request(Msg, D#data.ssh_params),
- ok = send_bytes(GexGroup, D),
+ send_bytes(GexGroup, D),
{next_state, {key_exchange_dh_gex_init,server,ReNeg}, D#data{ssh_params=Ssh}};
handle_event(_, #ssh_msg_kex_dh_gex_group{} = Msg, {key_exchange,client,ReNeg}, D) ->
{ok, KexGexInit, Ssh} = ssh_transport:handle_kex_dh_gex_group(Msg, D#data.ssh_params),
- ok = send_bytes(KexGexInit, D),
+ send_bytes(KexGexInit, D),
{next_state, {key_exchange_dh_gex_reply,client,ReNeg}, D#data{ssh_params=Ssh}};
%%%---- elliptic curve diffie-hellman
handle_event(_, #ssh_msg_kex_ecdh_init{} = Msg, {key_exchange,server,ReNeg}, D) ->
{ok, KexEcdhReply, Ssh1} = ssh_transport:handle_kex_ecdh_init(Msg, D#data.ssh_params),
- ok = send_bytes(KexEcdhReply, D),
+ send_bytes(KexEcdhReply, D),
{ok, NewKeys, Ssh} = ssh_transport:new_keys_message(Ssh1),
- ok = send_bytes(NewKeys, D),
+ send_bytes(NewKeys, D),
{next_state, {new_keys,server,ReNeg}, D#data{ssh_params=Ssh}};
handle_event(_, #ssh_msg_kex_ecdh_reply{} = Msg, {key_exchange,client,ReNeg}, D) ->
{ok, NewKeys, Ssh} = ssh_transport:handle_kex_ecdh_reply(Msg, D#data.ssh_params),
- ok = send_bytes(NewKeys, D),
+ send_bytes(NewKeys, D),
{next_state, {new_keys,client,ReNeg}, D#data{ssh_params=Ssh}};
@@ -638,9 +642,9 @@ handle_event(_, #ssh_msg_kex_ecdh_reply{} = Msg, {key_exchange,client,ReNeg}, D)
handle_event(_, #ssh_msg_kex_dh_gex_init{} = Msg, {key_exchange_dh_gex_init,server,ReNeg}, D) ->
{ok, KexGexReply, Ssh1} = ssh_transport:handle_kex_dh_gex_init(Msg, D#data.ssh_params),
- ok = send_bytes(KexGexReply, D),
+ send_bytes(KexGexReply, D),
{ok, NewKeys, Ssh} = ssh_transport:new_keys_message(Ssh1),
- ok = send_bytes(NewKeys, D),
+ send_bytes(NewKeys, D),
{next_state, {new_keys,server,ReNeg}, D#data{ssh_params=Ssh}};
@@ -648,7 +652,7 @@ handle_event(_, #ssh_msg_kex_dh_gex_init{} = Msg, {key_exchange_dh_gex_init,serv
handle_event(_, #ssh_msg_kex_dh_gex_reply{} = Msg, {key_exchange_dh_gex_reply,client,ReNeg}, D) ->
{ok, NewKeys, Ssh1} = ssh_transport:handle_kex_dh_gex_reply(Msg, D#data.ssh_params),
- ok = send_bytes(NewKeys, D),
+ send_bytes(NewKeys, D),
{next_state, {new_keys,client,ReNeg}, D#data{ssh_params=Ssh1}};
@@ -660,7 +664,7 @@ handle_event(_, #ssh_msg_newkeys{} = Msg, {new_keys,Role,init}, D) ->
Ssh = case Role of
client ->
{MsgReq, Ssh2} = ssh_auth:service_request_msg(Ssh1),
- ok = send_bytes(MsgReq, D),
+ send_bytes(MsgReq, D),
Ssh2;
server ->
Ssh1
@@ -678,7 +682,7 @@ handle_event(_, Msg = #ssh_msg_service_request{name=ServiceName}, StateName = {s
"ssh-userauth" ->
Ssh0 = #ssh{session_id=SessionId} = D#data.ssh_params,
{ok, {Reply, Ssh}} = ssh_auth:handle_userauth_request(Msg, SessionId, Ssh0),
- ok = send_bytes(Reply, D),
+ send_bytes(Reply, D),
{next_state, {userauth,server}, D#data{ssh_params = Ssh}};
_ ->
@@ -690,7 +694,7 @@ handle_event(_, Msg = #ssh_msg_service_request{name=ServiceName}, StateName = {s
handle_event(_, #ssh_msg_service_accept{name = "ssh-userauth"}, {service_request,client},
#data{ssh_params = #ssh{service="ssh-userauth"} = Ssh0} = State) ->
{Msg, Ssh} = ssh_auth:init_userauth_request_msg(Ssh0),
- ok = send_bytes(Msg, State),
+ send_bytes(Msg, State),
{next_state, {userauth,client}, State#data{auth_user = Ssh#ssh.user, ssh_params = Ssh}};
@@ -707,7 +711,7 @@ handle_event(_,
%% Probably the very first userauth_request but we deny unauthorized login
{not_authorized, _, {Reply,Ssh}} =
ssh_auth:handle_userauth_request(Msg, Ssh0#ssh.session_id, Ssh0),
- ok = send_bytes(Reply, D),
+ send_bytes(Reply, D),
{keep_state, D#data{ssh_params = Ssh}};
{"ssh-connection", "ssh-connection", Method} ->
@@ -717,7 +721,7 @@ handle_event(_,
%% Yepp! we support this method
case ssh_auth:handle_userauth_request(Msg, Ssh0#ssh.session_id, Ssh0) of
{authorized, User, {Reply, Ssh}} ->
- ok = send_bytes(Reply, D),
+ send_bytes(Reply, D),
D#data.starter ! ssh_connected,
connected_fun(User, Method, D),
{next_state, {connected,server},
@@ -725,11 +729,11 @@ handle_event(_,
ssh_params = Ssh#ssh{authenticated = true}}};
{not_authorized, {User, Reason}, {Reply, Ssh}} when Method == "keyboard-interactive" ->
retry_fun(User, Reason, D),
- ok = send_bytes(Reply, D),
+ send_bytes(Reply, D),
{next_state, {userauth_keyboard_interactive,server}, D#data{ssh_params = Ssh}};
{not_authorized, {User, Reason}, {Reply, Ssh}} ->
retry_fun(User, Reason, D),
- ok = send_bytes(Reply, D),
+ send_bytes(Reply, D),
{keep_state, D#data{ssh_params = Ssh}}
end;
false ->
@@ -818,9 +822,21 @@ handle_event(_, #ssh_msg_userauth_info_response{} = Msg, {userauth_keyboard_inte
{not_authorized, {User, Reason}, {Reply, Ssh}} ->
retry_fun(User, Reason, D),
send_bytes(Reply, D),
- {next_state, {userauth,server}, D#data{ssh_params = Ssh}}
+ {next_state, {userauth,server}, D#data{ssh_params = Ssh}};
+
+ {authorized_but_one_more, _User, {Reply, Ssh}} ->
+ send_bytes(Reply, D),
+ {next_state, {userauth_keyboard_interactive_extra,server}, D#data{ssh_params = Ssh}}
end;
+handle_event(_, #ssh_msg_userauth_info_response{} = Msg, {userauth_keyboard_interactive_extra, server}, D) ->
+ {authorized, User, {Reply, Ssh}} = ssh_auth:handle_userauth_info_response({extra,Msg}, D#data.ssh_params),
+ send_bytes(Reply, D),
+ D#data.starter ! ssh_connected,
+ connected_fun(User, "keyboard-interactive", D),
+ {next_state, {connected,server}, D#data{auth_user = User,
+ ssh_params = Ssh#ssh{authenticated = true}}};
+
handle_event(_, Msg = #ssh_msg_userauth_failure{}, {userauth_keyboard_interactive, client},
#data{ssh_params = Ssh0} = D0) ->
Prefs = [{Method,M,F,A} || {Method,M,F,A} <- Ssh0#ssh.userauth_preference,
@@ -1234,9 +1250,12 @@ handle_event(internal, prepare_next_packet, _, D) ->
handle_event(info, {CloseTag,Socket}, StateName,
D = #data{socket = Socket,
transport_close_tag = CloseTag}) ->
- disconnect(#ssh_msg_disconnect{code = ?SSH_DISCONNECT_BY_APPLICATION,
- description = "Connection closed"},
- StateName, D);
+ %% Simulate a disconnect from the peer
+ handle_event(info,
+ #ssh_msg_disconnect{code = ?SSH_DISCONNECT_BY_APPLICATION,
+ description = "Connection closed"},
+ StateName,
+ D);
handle_event(info, {timeout, {_, From} = Request}, _,
#data{connection_state = #connection{requests = Requests} = C0} = D) ->
@@ -1401,12 +1420,12 @@ fmt_stat_rec(FieldNames, Rec, Exclude) ->
state_name(),
#data{},
term()
- ) -> {gen_statem:callback_mode(), state_name(), #data{}}.
+ ) -> {ok, state_name(), #data{}}.
%% . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
code_change(_OldVsn, StateName, State, _Extra) ->
- {handle_event_function, StateName, State}.
+ {ok, StateName, State}.
%%====================================================================
@@ -1428,18 +1447,15 @@ start_the_connection_child(UserPid, Role, Socket, Options) ->
%% Stopping
-type finalize_termination_result() :: ok .
-finalize_termination(_StateName, #data{transport_cb = Transport,
- connection_state = Connection,
- socket = Socket}) ->
- case Connection of
+finalize_termination(_StateName, D) ->
+ case D#data.connection_state of
#connection{system_supervisor = SysSup,
sub_system_supervisor = SubSysSup} when is_pid(SubSysSup) ->
ssh_system_sup:stop_subsystem(SysSup, SubSysSup);
_ ->
do_nothing
end,
- (catch Transport:close(Socket)),
- ok.
+ close_transport(D).
%%--------------------------------------------------------------------
%% "Invert" the Role
@@ -1494,8 +1510,33 @@ send_msg(Msg, State=#data{ssh_params=Ssh0}) when is_tuple(Msg) ->
send_bytes(Bytes, State),
State#data{ssh_params=Ssh}.
-send_bytes(Bytes, #data{socket = Socket, transport_cb = Transport}) ->
- Transport:send(Socket, Bytes).
+send_bytes(Bytes, #data{sender = Sender}) ->
+ Sender ! {send,Bytes},
+ ok.
+
+close_transport(D) ->
+ D#data.sender ! close,
+ ok.
+
+
+nonblocking_sender(Socket, Callback) ->
+ receive
+ {send, Bytes} ->
+ case Callback:send(Socket, Bytes) of
+ ok ->
+ nonblocking_sender(Socket, Callback);
+ E = {error,_} ->
+ exit({shutdown,E})
+ end;
+
+ close ->
+ case Callback:close(Socket) of
+ ok ->
+ ok;
+ E = {error,_} ->
+ exit({shutdown,E})
+ end
+ end.
handle_version({2, 0} = NumVsn, StrVsn, Ssh0) ->
Ssh = counterpart_versions(NumVsn, StrVsn, Ssh0),
diff --git a/lib/ssh/src/ssh_transport.erl b/lib/ssh/src/ssh_transport.erl
index 7cb3b75ac0..15b80de30a 100644
--- a/lib/ssh/src/ssh_transport.erl
+++ b/lib/ssh/src/ssh_transport.erl
@@ -46,7 +46,7 @@
handle_kex_ecdh_reply/2,
extract_public_key/1,
ssh_packet/2, pack/2,
- sign/3, verify/4]).
+ sha/1, sign/3, verify/4]).
%%% For test suites
-export([pack/3]).
@@ -1619,6 +1619,11 @@ kex_h(SSH, Key, Min, NBits, Max, Prime, Gen, E, F, K) ->
crypto:hash(sha((SSH#ssh.algorithms)#alg.kex), L).
+sha('ssh-rsa') -> sha;
+sha('ssh-dss') -> sha;
+sha('ecdsa-sha2-nistp256') -> sha(secp256r1);
+sha('ecdsa-sha2-nistp384') -> sha(secp384r1);
+sha('ecdsa-sha2-nistp521') -> sha(secp521r1);
sha(secp256r1) -> sha256;
sha(secp384r1) -> sha384;
sha(secp521r1) -> sha512;
diff --git a/lib/ssh/test/ssh_algorithms_SUITE.erl b/lib/ssh/test/ssh_algorithms_SUITE.erl
index 0f68130a05..8b2db0e1a8 100644
--- a/lib/ssh/test/ssh_algorithms_SUITE.erl
+++ b/lib/ssh/test/ssh_algorithms_SUITE.erl
@@ -180,21 +180,19 @@ simple_exec(Config) ->
%%--------------------------------------------------------------------
%% Testing if no group matches
simple_exec_groups_no_match_too_small(Config) ->
- try simple_exec_group({400,500,600}, Config)
- of
- _ -> ct:fail("Exec though no group available")
- catch
- error:{badmatch,{error,"No possible diffie-hellman-group-exchange group found"}} ->
- ok
- end.
+ try_exec_simple_group({400,500,600}, Config).
simple_exec_groups_no_match_too_large(Config) ->
- try simple_exec_group({9200,9500,9700}, Config)
+ try_exec_simple_group({9200,9500,9700}, Config).
+
+
+try_exec_simple_group(Group, Config) ->
+ try simple_exec_group(Group, Config)
of
_ -> ct:fail("Exec though no group available")
catch
- error:{badmatch,{error,"No possible diffie-hellman-group-exchange group found"}} ->
- ok
+ error:{badmatch,{error,"No possible diffie-hellman-group-exchange group found"}} -> ok;
+ error:{badmatch,{error,"Connection closed"}} -> ok
end.
%%--------------------------------------------------------------------
diff --git a/lib/ssh/test/ssh_basic_SUITE.erl b/lib/ssh/test/ssh_basic_SUITE.erl
index d52d453007..51e0d5196b 100644
--- a/lib/ssh/test/ssh_basic_SUITE.erl
+++ b/lib/ssh/test/ssh_basic_SUITE.erl
@@ -67,7 +67,8 @@
shell_unicode_string/1,
ssh_info_print/1,
key_callback/1,
- key_callback_options/1
+ key_callback_options/1,
+ shell_exit_status/1
]).
%%% Common test callbacks
@@ -106,7 +107,8 @@ all() ->
multi_daemon_opt_fd,
packet_size_zero,
ssh_info_print,
- {group, login_bad_pwd_no_retry}
+ {group, login_bad_pwd_no_retry},
+ shell_exit_status
].
groups() ->
@@ -1167,6 +1169,33 @@ login_bad_pwd_no_retry(Config, AuthMethods) ->
end
end.
+
+%%----------------------------------------------------------------------------
+%%% Test that when shell REPL exit with reason normal client receives status 0
+shell_exit_status(Config) when is_list(Config) ->
+ process_flag(trap_exit, true),
+ SystemDir = proplists:get_value(data_dir, Config),
+ UserDir = proplists:get_value(priv_dir, Config),
+
+ ShellFun = fun (_User) -> spawn(fun() -> ok end) end,
+ {Pid, Host, Port} = ssh_test_lib:daemon([{system_dir, SystemDir},
+ {user_dir, UserDir},
+ {user_passwords, [{"vego", "morot"}]},
+ {shell, ShellFun},
+ {failfun, fun ssh_test_lib:failfun/2}]),
+ ConnectionRef =
+ ssh_test_lib:connect(Host, Port, [{silently_accept_hosts, true},
+ {user_dir, UserDir},
+ {user, "vego"},
+ {password, "morot"},
+ {user_interaction, false}]),
+
+ {ok, ChannelId} = ssh_connection:session_channel(ConnectionRef, infinity),
+ ok = ssh_connection:shell(ConnectionRef, ChannelId),
+ ssh_test_lib:receive_exec_end(ConnectionRef, ChannelId),
+ ssh:stop_daemon(Pid).
+
+
%%--------------------------------------------------------------------
%% Internal functions ------------------------------------------------
%%--------------------------------------------------------------------
diff --git a/lib/ssh/vsn.mk b/lib/ssh/vsn.mk
index 575c1af3a9..09e707ad07 100644
--- a/lib/ssh/vsn.mk
+++ b/lib/ssh/vsn.mk
@@ -1,5 +1,5 @@
#-*-makefile-*- ; force emacs to enter makefile-mode
-SSH_VSN = 4.3.1
+SSH_VSN = 4.3.3
APP_VSN = "ssh-$(SSH_VSN)"
diff --git a/lib/ssl/doc/src/notes.xml b/lib/ssl/doc/src/notes.xml
index 3b6f988a2d..c7f50777a8 100644
--- a/lib/ssl/doc/src/notes.xml
+++ b/lib/ssl/doc/src/notes.xml
@@ -28,6 +28,90 @@
<p>This document describes the changes made to the SSL application.</p>
+<section><title>SSL 8.0.3</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ A timing related bug in event handling could cause
+ interoperability problems between an erlang TLS server
+ and some TLS clients, especially noticed with Firefox as
+ TLS client.</p>
+ <p>
+ Own Id: OTP-13917</p>
+ </item>
+ <item>
+ <p>
+ Correct ECC curve selection, the error could cause the
+ default to always be selected.</p>
+ <p>
+ Own Id: OTP-13918</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>SSL 8.0.2</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Correctly formed handshake messages received out of order
+ will now correctly fail the connection with unexpected
+ message.</p>
+ <p>
+ Own Id: OTP-13853</p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ ssl application now behaves gracefully also on partially
+ incorrect input from peer.</p>
+ <p>
+ Own Id: OTP-13834</p>
+ </item>
+ <item>
+ <p>
+ Add application environment configuration
+ bypass_pem_cache. This can be used as a workaround for
+ the current implementation of the PEM-cache that has
+ proven to be a bottleneck.</p>
+ <p>
+ Own Id: OTP-13883</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>SSL 8.0.1</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ The TLS/SSL protocol version selection for the SSL server
+ has been corrected to follow RFC 5246 Appendix E.1
+ especially in case where the list of supported versions
+ has gaps. Now the server selects the highest protocol
+ version it supports that is not higher than what the
+ client supports.</p>
+ <p>
+ Own Id: OTP-13753 Aux Id: seq13150 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>SSL 8.0</title>
<section><title>Fixed Bugs and Malfunctions</title>
diff --git a/lib/ssl/doc/src/ssl_app.xml b/lib/ssl/doc/src/ssl_app.xml
index a66e947bc1..f317dfded4 100644
--- a/lib/ssl/doc/src/ssl_app.xml
+++ b/lib/ssl/doc/src/ssl_app.xml
@@ -141,6 +141,16 @@
marker="ssl#clear_pem_cache-0">ssl:clear_pem_cache/0</seealso>
</item>
+
+ <tag><c><![CDATA[bypass_pem_cache = boolean() <optional>]]></c></tag>
+ <item>
+ <p>Introduced in ssl-8.0.2. Disables the PEM-cache.
+ The PEM cache has proven to be a bottleneck, until the
+ implementation has been improved this can be used as
+ a workaround. Defaults to false.
+ </p>
+ </item>
+
<tag><c><![CDATA[alert_timeout = integer() <optional>]]></c></tag>
<item>
<p>
diff --git a/lib/ssl/doc/src/ssl_distribution.xml b/lib/ssl/doc/src/ssl_distribution.xml
index 4bd5f67202..1150043e76 100644
--- a/lib/ssl/doc/src/ssl_distribution.xml
+++ b/lib/ssl/doc/src/ssl_distribution.xml
@@ -71,8 +71,8 @@
<section>
<title>Building Boot Scripts Including the ssl Application</title>
<p>Boot scripts are built using the <c>systools</c> utility in the
- <c>sasl</c> application. For more information on <c>systools</c>,
- see the <c>sasl</c> documentation. This is only an example of
+ SASL application. For more information on <c>systools</c>,
+ see the SASL documentation. This is only an example of
what can be done.</p>
<p>The simplest boot script possible includes only the Kernel
diff --git a/lib/ssl/src/dtls_connection.erl b/lib/ssl/src/dtls_connection.erl
index b8be686b99..4f1f050e4b 100644
--- a/lib/ssl/src/dtls_connection.erl
+++ b/lib/ssl/src/dtls_connection.erl
@@ -42,22 +42,17 @@
-export([next_record/1, next_event/3]).
%% Handshake handling
--export([%%renegotiate/2,
- send_handshake/2, queue_handshake/2, queue_change_cipher/2]).
+-export([renegotiate/2,
+ reinit_handshake_data/1,
+ send_handshake/2, queue_handshake/2, queue_change_cipher/2,
+ select_sni_extension/1]).
%% Alert and close handling
--export([%%send_alert/2, handle_own_alert/4, handle_close_alert/3,
- handle_normal_shutdown/3 %%, close/5
- %%alert_user/6, alert_user/9
- ]).
+-export([send_alert/2, close/5]).
%% Data handling
--export([%%write_application_data/3,
- read_application_data/2,
- passive_receive/2, next_record_if_active/1%,
- %%handle_common_event/4,
- %handle_packet/3
+-export([passive_receive/2, next_record_if_active/1, handle_common_event/4
]).
%% gen_statem state functions
@@ -65,9 +60,7 @@
hello/3, certify/3, cipher/3, abbreviated/3, %% Handshake states
connection/3]).
%% gen_statem callbacks
--export([terminate/3, code_change/4, format_status/2]).
-
--define(GEN_STATEM_CB_MODE, state_functions).
+-export([callback_mode/0, terminate/3, code_change/4, format_status/2]).
%%====================================================================
%% Internal application API
@@ -104,10 +97,11 @@ send_handshake(Handshake, State) ->
send_handshake_flight(queue_handshake(Handshake, State)).
queue_flight_buffer(Msg, #state{negotiated_version = Version,
- connection_states = #connection_states{
- current_write =
- #connection_state{epoch = Epoch}},
+ connection_states = ConnectionStates,
flight_buffer = Flight} = State) ->
+ ConnectionState =
+ ssl_record:current_connection_state(ConnectionStates, write),
+ Epoch = maps:get(epoch, ConnectionState),
State#state{flight_buffer = Flight ++ [{Version, Epoch, Msg}]}.
queue_handshake(Handshake, #state{negotiated_version = Version,
@@ -141,6 +135,25 @@ send_alert(Alert, #state{negotiated_version = Version,
Transport:send(Socket, BinMsg),
State0#state{connection_states = ConnectionStates}.
+close(downgrade, _,_,_,_) ->
+ ok;
+%% Other
+close(_, Socket, Transport, _,_) ->
+ Transport:close(Socket).
+
+reinit_handshake_data(#state{protocol_buffers = Buffers} = State) ->
+ State#state{premaster_secret = undefined,
+ public_key_info = undefined,
+ tls_handshake_history = ssl_handshake:init_handshake_history(),
+ protocol_buffers =
+ Buffers#protocol_buffers{dtls_fragment_state =
+ dtls_handshake:dtls_handshake_new_flight(0)}}.
+
+select_sni_extension(#client_hello{extensions = HelloExtensions}) ->
+ HelloExtensions#hello_extensions.sni;
+select_sni_extension(_) ->
+ undefined.
+
%%====================================================================
%% tls_connection_sup API
%%====================================================================
@@ -161,12 +174,15 @@ init([Role, Host, Port, Socket, Options, User, CbInfo]) ->
State0 = initial_state(Role, Host, Port, Socket, Options, User, CbInfo),
try
State = ssl_connection:ssl_config(State0#state.ssl_options, Role, State0),
- gen_statem:enter_loop(?MODULE, [], ?GEN_STATEM_CB_MODE, init, State)
+ gen_statem:enter_loop(?MODULE, [], init, State)
catch
throw:Error ->
- gen_statem:enter_loop(?MODULE, [], ?GEN_STATEM_CB_MODE, error, {Error,State0})
+ gen_statem:enter_loop(?MODULE, [], error, {Error,State0})
end.
+callback_mode() ->
+ state_functions.
+
%%--------------------------------------------------------------------
%% State functionsconnection/2
%%--------------------------------------------------------------------
@@ -216,9 +232,7 @@ error(_, _, _) ->
#state{}) ->
gen_statem:state_function_result().
%%--------------------------------------------------------------------
-hello(internal, #client_hello{client_version = ClientVersion,
- extensions = #hello_extensions{ec_point_formats = EcPointFormats,
- elliptic_curves = EllipticCurves}} = Hello,
+hello(internal, #client_hello{client_version = ClientVersion} = Hello,
State = #state{connection_states = ConnectionStates0,
port = Port, session = #session{own_certificate = Cert} = Session0,
renegotiation = {Renegotiation, _},
@@ -231,7 +245,7 @@ hello(internal, #client_hello{client_version = ClientVersion,
case dtls_handshake:hello(Hello, SslOpts, {Port, Session0, Cache, CacheCb,
ConnectionStates0, Cert, KeyExAlg}, Renegotiation) of
#alert{} = Alert ->
- handle_own_alert(Alert, ClientVersion, hello, State);
+ ssl_connection:handle_own_alert(Alert, ClientVersion, hello, State);
{Version, {Type, Session},
ConnectionStates, Protocol0, ServerHelloExt, HashSign} ->
Protocol = case Protocol0 of
@@ -244,7 +258,6 @@ hello(internal, #client_hello{client_version = ClientVersion,
negotiated_version = Version,
hashsign_algorithm = HashSign,
session = Session,
- client_ecc = {EllipticCurves, EcPointFormats},
negotiated_protocol = Protocol}, ?MODULE)
end;
hello(internal, #server_hello{} = Hello,
@@ -255,7 +268,7 @@ hello(internal, #server_hello{} = Hello,
ssl_options = SslOptions} = State) ->
case dtls_handshake:hello(Hello, SslOptions, ConnectionStates0, Renegotiation) of
#alert{} = Alert ->
- handle_own_alert(Alert, ReqVersion, hello, State);
+ ssl_connection:handle_own_alert(Alert, ReqVersion, hello, State);
{Version, NewId, ConnectionStates, ProtoExt, Protocol} ->
ssl_connection:handle_session(Hello,
Version, NewId, ConnectionStates, ProtoExt, Protocol, State)
@@ -334,7 +347,7 @@ handle_info({Protocol, _, Data}, StateName,
{Record, State} ->
next_event(StateName, Record, State);
#alert{} = Alert ->
- handle_normal_shutdown(Alert, StateName, State0),
+ ssl_connection:handle_normal_shutdown(Alert, StateName, State0),
{stop, {shutdown, own_alert}}
end;
handle_info({CloseTag, Socket}, StateName,
@@ -354,7 +367,7 @@ handle_info({CloseTag, Socket}, StateName,
%%invalidate_session(Role, Host, Port, Session)
ok
end,
- handle_normal_shutdown(?ALERT_REC(?FATAL, ?CLOSE_NOTIFY), StateName, State),
+ ssl_connection:handle_normal_shutdown(?ALERT_REC(?FATAL, ?CLOSE_NOTIFY), StateName, State),
{stop, {shutdown, transport_closed}};
handle_info(Msg, StateName, State) ->
ssl_connection:handle_info(Msg, StateName, State).
@@ -362,6 +375,51 @@ handle_info(Msg, StateName, State) ->
handle_call(Event, From, StateName, State) ->
ssl_connection:handle_call(Event, From, StateName, State, ?MODULE).
+handle_common_event(internal, #alert{} = Alert, StateName,
+ #state{negotiated_version = Version} = State) ->
+ ssl_connection:handle_own_alert(Alert, Version, StateName, State);
+
+%%% DTLS record protocol level handshake messages
+handle_common_event(internal, #ssl_tls{type = ?HANDSHAKE} = Record,
+ StateName,
+ #state{protocol_buffers =
+ #protocol_buffers{dtls_packets = Packets0,
+ dtls_fragment_state = HsState0} = Buffers,
+ negotiated_version = Version} = State0) ->
+ try
+ {Packets1, HsState} = dtls_handshake:get_dtls_handshake(Record, HsState0),
+ State =
+ State0#state{protocol_buffers =
+ Buffers#protocol_buffers{dtls_fragment_state = HsState}},
+ Events = dtls_handshake_events(Packets0 ++ Packets1),
+ case StateName of
+ connection ->
+ ssl_connection:hibernate_after(StateName, State, Events);
+ _ ->
+ {next_state, StateName, State, Events}
+ end
+ catch throw:#alert{} = Alert ->
+ ssl_connection:handle_own_alert(Alert, Version, StateName, State0)
+ end;
+%%% DTLS record protocol level application data messages
+handle_common_event(internal, #ssl_tls{type = ?APPLICATION_DATA, fragment = Data}, StateName, State) ->
+ {next_state, StateName, State, [{next_event, internal, {application_data, Data}}]};
+%%% DTLS record protocol level change cipher messages
+handle_common_event(internal, #ssl_tls{type = ?CHANGE_CIPHER_SPEC, fragment = Data}, StateName, State) ->
+ {next_state, StateName, State, [{next_event, internal, #change_cipher_spec{type = Data}}]};
+%%% DTLS record protocol level Alert messages
+handle_common_event(internal, #ssl_tls{type = ?ALERT, fragment = EncAlerts}, StateName,
+ #state{negotiated_version = Version} = State) ->
+ case decode_alerts(EncAlerts) of
+ Alerts = [_|_] ->
+ handle_alerts(Alerts, {next_state, StateName, State});
+ #alert{} = Alert ->
+ ssl_connection:handle_own_alert(Alert, Version, StateName, State)
+ end;
+%% Ignore unknown TLS record level protocol messages
+handle_common_event(internal, #ssl_tls{type = _Unknown}, StateName, State) ->
+ {next_state, StateName, State}.
+
%%--------------------------------------------------------------------
%% Description:This function is called by a gen_fsm when it is about
%% to terminate. It should be the opposite of Module:init/1 and do any
@@ -376,7 +434,7 @@ terminate(Reason, StateName, State) ->
%% Description: Convert process state when code is changed
%%--------------------------------------------------------------------
code_change(_OldVsn, StateName, State, _Extra) ->
- {?GEN_STATEM_CB_MODE, StateName, State}.
+ {ok, StateName, State}.
format_status(Type, Data) ->
ssl_connection:format_status(Type, Data).
@@ -384,10 +442,21 @@ format_status(Type, Data) ->
%%--------------------------------------------------------------------
%%% Internal functions
%%--------------------------------------------------------------------
+
+dtls_handshake_events([]) ->
+ throw(?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE, malformed_handshake));
+dtls_handshake_events(Packets) ->
+ lists:map(fun(Packet) ->
+ {next_event, internal, {handshake, Packet}}
+ end, Packets).
+
+
encode_handshake(Handshake, Version, ConnectionStates0, Hist0) ->
{Seq, ConnectionStates} = sequence(ConnectionStates0),
{EncHandshake, Frag} = dtls_handshake:encode_handshake(Handshake, Version, Seq),
- Hist = ssl_handshake:update_handshake_history(Hist0, EncHandshake),
+ %% DTLS does not have an equivalent version to SSLv2. So v2 hello compatibility
+ %% will always be false
+ Hist = ssl_handshake:update_handshake_history(Hist0, EncHandshake, false),
{Frag, ConnectionStates, Hist}.
encode_change_cipher(#change_cipher_spec{}, Version, ConnectionStates) ->
@@ -423,12 +492,12 @@ encode_handshake_record(_Version, _Epoch, _Space, _MsgType, _MsgSeq, _Len, <<>>,
encode_handshake_record(Version, Epoch, Space, MsgType, MsgSeq, Len, Bin,
Offset, MRS, Encoded0, CS0) ->
MaxFragmentLen = Space - 25,
- case Bin of
- <<BinFragment:MaxFragmentLen/bytes, Rest/binary>> ->
- ok;
+ {BinFragment, Rest} =
+ case Bin of
+ <<BinFragment0:MaxFragmentLen/bytes, Rest0/binary>> ->
+ {BinFragment0, Rest0};
_ ->
- BinFragment = Bin,
- Rest = <<>>
+ {Bin, <<>>}
end,
FragLength = byte_size(BinFragment),
Frag = [MsgType, ?uint24(Len), ?uint16(MsgSeq), ?uint24(Offset), ?uint24(FragLength), BinFragment],
@@ -459,13 +528,13 @@ finish_pack_records({[], Acc}) ->
finish_pack_records({Buf, Acc}) ->
lists:reverse([lists:reverse(Buf)|Acc]).
-%% decode_alerts(Bin) ->
-%% ssl_alert:decode(Bin).
+decode_alerts(Bin) ->
+ ssl_alert:decode(Bin).
initial_state(Role, Host, Port, Socket, {SSLOptions, SocketOptions}, User,
{CbModule, DataTag, CloseTag, ErrorTag}) ->
#ssl_options{beast_mitigation = BeastMitigation} = SSLOptions,
- ConnectionStates = ssl_record:init_connection_states(Role, BeastMitigation),
+ ConnectionStates = dtls_record:init_connection_states(Role, BeastMitigation),
SessionCacheCb = case application:get_env(ssl, session_cb) of
{ok, Cb} when is_atom(Cb) ->
@@ -548,7 +617,7 @@ passive_receive(State0 = #state{user_data_buffer = Buffer}, StateName) ->
{Record, State} = next_record(State0),
next_event(StateName, Record, State);
_ ->
- {Record, State} = read_application_data(<<>>, State0),
+ {Record, State} = ssl_connection:read_application_data(<<>>, State0),
next_event(StateName, Record, State)
end.
@@ -560,7 +629,7 @@ next_event(connection = StateName, no_record, State0, Actions) ->
{no_record, State} ->
ssl_connection:hibernate_after(StateName, State, Actions);
{#ssl_tls{} = Record, State} ->
- {next_state, StateName, State, [{next_event, internal, {dtls_record, Record}} | Actions]};
+ {next_state, StateName, State, [{next_event, internal, {protocol_record, Record}} | Actions]};
{#alert{} = Alert, State} ->
{next_state, StateName, State, [{next_event, internal, Alert} | Actions]}
end;
@@ -569,20 +638,11 @@ next_event(StateName, Record, State, Actions) ->
no_record ->
{next_state, StateName, State, Actions};
#ssl_tls{} = Record ->
- {next_state, StateName, State, [{next_event, internal, {dtls_record, Record}} | Actions]};
+ {next_state, StateName, State, [{next_event, internal, {protocol_record, Record}} | Actions]};
#alert{} = Alert ->
{next_state, StateName, State, [{next_event, internal, Alert} | Actions]}
end.
-read_application_data(_,State) ->
- {#ssl_tls{fragment = <<"place holder">>}, State}.
-
-handle_own_alert(_,_,_, State) -> %% Place holder
- {stop, {shutdown, own_alert}, State}.
-
-handle_normal_shutdown(_, _, _State) -> %% Place holder
- ok.
-
%% TODO This generates dialyzer warnings, has to be handled differently.
%% handle_packet(Address, Port, Packet) ->
%% try dtls_record:get_dtls_records(Packet, <<>>) of
@@ -631,5 +691,34 @@ handle_normal_shutdown(_, _, _State) -> %% Place holder
%% address_to_bin({A,B,C,D,E,F,G,H}, Port) ->
%% <<A:16,B:16,C:16,D:16,E:16,F:16,G:16,H:16,Port:16>>.
-sequence(#connection_states{dtls_write_msg_seq = Seq} = CS) ->
- {Seq, CS#connection_states{dtls_write_msg_seq = Seq + 1}}.
+sequence(#{write_msg_seq := Seq} = ConnectionState) ->
+ {Seq, ConnectionState#{write_msg_seq => Seq + 1}}.
+
+renegotiate(#state{role = client} = State, Actions) ->
+ %% Handle same way as if server requested
+ %% the renegotiation
+ Hs0 = ssl_handshake:init_handshake_history(),
+ {next_state, connection, State#state{tls_handshake_history = Hs0,
+ protocol_buffers = #protocol_buffers{}},
+ [{next_event, internal, #hello_request{}} | Actions]};
+
+renegotiate(#state{role = server,
+ connection_states = CS0} = State0, Actions) ->
+ HelloRequest = ssl_handshake:hello_request(),
+ CS = CS0#{write_msg_seq => 0},
+ State1 = send_handshake(HelloRequest,
+ State0#state{connection_states =
+ CS}),
+ Hs0 = ssl_handshake:init_handshake_history(),
+ {Record, State} = next_record(State1#state{tls_handshake_history = Hs0,
+ protocol_buffers = #protocol_buffers{}}),
+ next_event(hello, Record, State, Actions).
+
+handle_alerts([], Result) ->
+ Result;
+handle_alerts(_, {stop,_} = Stop) ->
+ Stop;
+handle_alerts([Alert | Alerts], {next_state, StateName, State}) ->
+ handle_alerts(Alerts, ssl_connection:handle_alert(Alert, StateName, State));
+handle_alerts([Alert | Alerts], {next_state, StateName, State, _Actions}) ->
+ handle_alerts(Alerts, ssl_connection:handle_alert(Alert, StateName, State)).
diff --git a/lib/ssl/src/dtls_handshake.erl b/lib/ssl/src/dtls_handshake.erl
index 5a799cf441..c6535d5928 100644
--- a/lib/ssl/src/dtls_handshake.erl
+++ b/lib/ssl/src/dtls_handshake.erl
@@ -35,7 +35,7 @@
%% Internal application API
%%====================================================================
%%--------------------------------------------------------------------
--spec client_hello(host(), inet:port_number(), #connection_states{},
+-spec client_hello(host(), inet:port_number(), ssl_record:connection_states(),
#ssl_options{}, integer(), atom(), boolean(), der_cert()) ->
#client_hello{}.
%%
@@ -48,7 +48,7 @@ client_hello(Host, Port, ConnectionStates, SslOpts,
Cache, CacheCb, Renegotiation, OwnCert).
%%--------------------------------------------------------------------
--spec client_hello(host(), inet:port_number(), term(), #connection_states{},
+-spec client_hello(host(), inet:port_number(), term(), ssl_record:connection_states(),
#ssl_options{}, integer(), atom(), boolean(), der_cert()) ->
#client_hello{}.
%%
@@ -61,7 +61,7 @@ client_hello(Host, Port, Cookie, ConnectionStates,
Cache, CacheCb, Renegotiation, OwnCert) ->
Version = dtls_record:highest_protocol_version(Versions),
Pending = ssl_record:pending_connection_state(ConnectionStates, read),
- SecParams = Pending#connection_state.security_parameters,
+ SecParams = maps:get(security_parameters, Pending),
CipherSuites = ssl_handshake:available_suites(UserSuites, Version),
Extensions = ssl_handshake:client_hello_extensions(Host, dtls_v1:corresponding_tls_version(Version), CipherSuites,
@@ -445,29 +445,23 @@ enc_handshake(#client_hello{client_version = {Major, Minor},
cookie = Cookie,
cipher_suites = CipherSuites,
compression_methods = CompMethods,
- extensions = HelloExtensions}, Version) ->
+ extensions = HelloExtensions}, _Version) ->
SIDLength = byte_size(SessionID),
- BinCookie = enc_client_hello_cookie(Version, Cookie),
+ CookieLength = byte_size(Cookie),
BinCompMethods = list_to_binary(CompMethods),
CmLength = byte_size(BinCompMethods),
BinCipherSuites = list_to_binary(CipherSuites),
CsLength = byte_size(BinCipherSuites),
ExtensionsBin = ssl_handshake:encode_hello_extensions(HelloExtensions),
-
+
{?CLIENT_HELLO, <<?BYTE(Major), ?BYTE(Minor), Random:32/binary,
?BYTE(SIDLength), SessionID/binary,
- BinCookie/binary,
+ ?BYTE(CookieLength), Cookie/binary,
?UINT16(CsLength), BinCipherSuites/binary,
?BYTE(CmLength), BinCompMethods/binary, ExtensionsBin/binary>>};
enc_handshake(HandshakeMsg, Version) ->
ssl_handshake:encode_handshake(HandshakeMsg, Version).
-enc_client_hello_cookie(_, <<>>) ->
- <<>>;
-enc_client_hello_cookie(_, Cookie) ->
- CookieLength = byte_size(Cookie),
- <<?BYTE(CookieLength), Cookie/binary>>.
-
decode_handshake(_Version, ?CLIENT_HELLO, <<?BYTE(Major), ?BYTE(Minor), Random:32/binary,
?BYTE(SID_length), Session_ID:SID_length/binary,
?BYTE(Cookie_length), Cookie:Cookie_length/binary,
diff --git a/lib/ssl/src/dtls_record.erl b/lib/ssl/src/dtls_record.erl
index 5387fcafa8..8a6e2d315c 100644
--- a/lib/ssl/src/dtls_record.erl
+++ b/lib/ssl/src/dtls_record.erl
@@ -30,7 +30,7 @@
-include("ssl_cipher.hrl").
%% Handling of incoming data
--export([get_dtls_records/2]).
+-export([get_dtls_records/2, init_connection_states/2]).
%% Decoding
-export([decode_cipher_text/2]).
@@ -58,7 +58,26 @@
%%====================================================================
%% Internal application API
%%====================================================================
-
+%%--------------------------------------------------------------------
+-spec init_connection_states(client | server, one_n_minus_one | zero_n | disabled) ->
+ ssl_record:connection_states().
+%% %
+ %
+%% Description: Creates a connection_states record with appropriate
+%% values for the initial SSL connection setup.
+%%--------------------------------------------------------------------
+init_connection_states(Role, BeastMitigation) ->
+ ConnectionEnd = ssl_record:record_protocol_role(Role),
+ Current = initial_connection_state(ConnectionEnd, BeastMitigation),
+ Pending = ssl_record:empty_connection_state(ConnectionEnd, BeastMitigation),
+ #{write_msg_seq => 0,
+ prvious_read => undefined,
+ current_read => Current,
+ pending_read => Pending,
+ prvious_write => undefined,
+ current_write => Current,
+ pending_write => Pending}.
+
%%--------------------------------------------------------------------
-spec get_dtls_records(binary(), binary()) -> {[binary()], binary()} | #alert{}.
%%
@@ -122,63 +141,59 @@ get_dtls_records_aux(Data, Acc) ->
end.
encode_plain_text(Type, Version, Data,
- #connection_states{current_write =
- #connection_state{
- epoch = Epoch,
- sequence_number = Seq,
- compression_state=CompS0,
- security_parameters=
- #security_parameters{
- cipher_type = ?AEAD,
- compression_algorithm=CompAlg}
- }= WriteState0} = ConnectionStates) ->
+ #{current_write :=
+ #{epoch := Epoch,
+ sequence_number := Seq,
+ compression_state := CompS0,
+ security_parameters :=
+ #security_parameters{
+ cipher_type = ?AEAD,
+ compression_algorithm = CompAlg}
+ }= WriteState0} = ConnectionStates) ->
{Comp, CompS1} = ssl_record:compress(CompAlg, Data, CompS0),
- WriteState1 = WriteState0#connection_state{compression_state = CompS1},
+ WriteState1 = WriteState0#{compression_state => CompS1},
AAD = calc_aad(Type, Version, Epoch, Seq),
{CipherFragment, WriteState} = ssl_record:cipher_aead(dtls_v1:corresponding_tls_version(Version),
Comp, WriteState1, AAD),
CipherText = encode_tls_cipher_text(Type, Version, Epoch, Seq, CipherFragment),
- {CipherText, ConnectionStates#connection_states{current_write =
- WriteState#connection_state{sequence_number = Seq +1}}};
+ {CipherText, ConnectionStates#{current_write => WriteState#{sequence_number => Seq +1}}};
encode_plain_text(Type, Version, Data,
- #connection_states{current_write=#connection_state{
- epoch = Epoch,
- sequence_number = Seq,
- compression_state=CompS0,
- security_parameters=
- #security_parameters{compression_algorithm=CompAlg}
- }= WriteState0} = ConnectionStates) ->
+ #{current_write :=
+ #{epoch := Epoch,
+ sequence_number := Seq,
+ compression_state := CompS0,
+ security_parameters :=
+ #security_parameters{compression_algorithm = CompAlg}
+ }= WriteState0} = ConnectionStates) ->
{Comp, CompS1} = ssl_record:compress(CompAlg, Data, CompS0),
- WriteState1 = WriteState0#connection_state{compression_state = CompS1},
+ WriteState1 = WriteState0#{compression_state => CompS1},
MacHash = calc_mac_hash(WriteState1, Type, Version, Epoch, Seq, Comp),
{CipherFragment, WriteState} = ssl_record:cipher(dtls_v1:corresponding_tls_version(Version),
Comp, WriteState1, MacHash),
CipherText = encode_tls_cipher_text(Type, Version, Epoch, Seq, CipherFragment),
- {CipherText, ConnectionStates#connection_states{current_write =
- WriteState#connection_state{sequence_number = Seq +1}}}.
+ {CipherText, ConnectionStates#{current_write => WriteState#{sequence_number => Seq +1}}}.
decode_cipher_text(#ssl_tls{type = Type, version = Version,
epoch = Epoch,
sequence_number = Seq,
fragment = CipherFragment} = CipherText,
- #connection_states{current_read =
- #connection_state{
- compression_state = CompressionS0,
- security_parameters=
- #security_parameters{
- cipher_type = ?AEAD,
- compression_algorithm=CompAlg}
- } = ReadState0}= ConnnectionStates0) ->
+ #{current_read :=
+ #{compression_state := CompressionS0,
+ security_parameters :=
+ #security_parameters{
+ cipher_type = ?AEAD,
+ compression_algorithm = CompAlg}
+ } = ReadState0} = ConnnectionStates0) ->
AAD = calc_aad(Type, Version, Epoch, Seq),
case ssl_record:decipher_aead(dtls_v1:corresponding_tls_version(Version),
CipherFragment, ReadState0, AAD) of
{PlainFragment, ReadState1} ->
{Plain, CompressionS1} = ssl_record:uncompress(CompAlg,
PlainFragment, CompressionS0),
- ConnnectionStates = ConnnectionStates0#connection_states{
- current_read = ReadState1#connection_state{
- compression_state = CompressionS1}},
+ ConnnectionStates = ConnnectionStates0#{
+ current_read => ReadState1#{
+ compression_state => CompressionS1}},
{CipherText#ssl_tls{fragment = Plain}, ConnnectionStates};
#alert{} = Alert ->
Alert
@@ -188,13 +203,12 @@ decode_cipher_text(#ssl_tls{type = Type, version = Version,
epoch = Epoch,
sequence_number = Seq,
fragment = CipherFragment} = CipherText,
- #connection_states{current_read =
- #connection_state{
- compression_state = CompressionS0,
- security_parameters=
- #security_parameters{
- compression_algorithm=CompAlg}
- } = ReadState0}= ConnnectionStates0) ->
+ #{current_read :=
+ #{compression_state := CompressionS0,
+ security_parameters :=
+ #security_parameters{
+ compression_algorithm = CompAlg}
+ } = ReadState0}= ConnnectionStates0) ->
{PlainFragment, Mac, ReadState1} = ssl_record:decipher(dtls_v1:corresponding_tls_version(Version),
CipherFragment, ReadState0, true),
MacHash = calc_mac_hash(ReadState1, Type, Version, Epoch, Seq, PlainFragment),
@@ -202,17 +216,17 @@ decode_cipher_text(#ssl_tls{type = Type, version = Version,
true ->
{Plain, CompressionS1} = ssl_record:uncompress(CompAlg,
PlainFragment, CompressionS0),
- ConnnectionStates = ConnnectionStates0#connection_states{
- current_read = ReadState1#connection_state{
- compression_state = CompressionS1}},
+ ConnnectionStates = ConnnectionStates0#{
+ current_read => ReadState1#{
+ compression_state => CompressionS1}},
{CipherText#ssl_tls{fragment = Plain}, ConnnectionStates};
false ->
?ALERT_REC(?FATAL, ?BAD_RECORD_MAC)
end.
%%--------------------------------------------------------------------
--spec encode_change_cipher_spec(dtls_version(), #connection_states{}) ->
- {iolist(), #connection_states{}}.
+-spec encode_change_cipher_spec(dtls_version(), ssl_record:connection_states()) ->
+ {iolist(), ssl_record:connection_states()}.
%%
%% Description: Encodes a change_cipher_spec-message to send on the ssl socket.
%%--------------------------------------------------------------------
@@ -352,92 +366,87 @@ is_acceptable_version(Version, Versions) ->
%%--------------------------------------------------------------------
--spec init_connection_state_seq(dtls_version(), #connection_states{}) ->
- #connection_state{}.
+-spec init_connection_state_seq(dtls_version(), ssl_record:connection_states()) ->
+ ssl_record:connection_state().
%%
%% Description: Copy the read sequence number to the write sequence number
%% This is only valid for DTLS in the first client_hello
%%--------------------------------------------------------------------
init_connection_state_seq({254, _},
- #connection_states{
- current_read = Read = #connection_state{epoch = 0},
- current_write = Write = #connection_state{epoch = 0}} = CS0) ->
- CS0#connection_states{current_write =
- Write#connection_state{
- sequence_number = Read#connection_state.sequence_number}};
+ #{current_read := #{epoch := 0} = Read,
+ current_write := #{epoch := 0} = Write} = CS0) ->
+ Seq = maps:get(sequence_number, Read),
+ CS0#{current_write => Write#{sequence_number => Seq}};
init_connection_state_seq(_, CS) ->
CS.
%%--------------------------------------------------------
--spec current_connection_state_epoch(#connection_states{}, read | write) ->
+-spec current_connection_state_epoch(ssl_record:connection_states(), read | write) ->
integer().
%%
%% Description: Returns the epoch the connection_state record
%% that is currently defined as the current conection state.
%%--------------------------------------------------------------------
-current_connection_state_epoch(#connection_states{current_read = Current},
+current_connection_state_epoch(#{current_read := Current},
read) ->
- Current#connection_state.epoch;
-current_connection_state_epoch(#connection_states{current_write = Current},
+ maps:get(epoch, Current);
+current_connection_state_epoch(#{current_write := Current},
write) ->
- Current#connection_state.epoch.
+ maps:get(epoch, Current).
%%--------------------------------------------------------------------
--spec connection_state_by_epoch(#connection_states{}, integer(), read | write) ->
- #connection_state{}.
+-spec connection_state_by_epoch(ssl_record:connection_states(), integer(), read | write) ->
+ ssl_record:connection_state().
%%
%% Description: Returns the instance of the connection_state record
%% that is defined by the Epoch.
%%--------------------------------------------------------------------
-connection_state_by_epoch(#connection_states{current_read = CS}, Epoch, read)
- when CS#connection_state.epoch == Epoch ->
+connection_state_by_epoch(#{current_read := #{epoch := Epoch}} = CS, Epoch, read) ->
CS;
-connection_state_by_epoch(#connection_states{pending_read = CS}, Epoch, read)
- when CS#connection_state.epoch == Epoch ->
+connection_state_by_epoch(#{pending_read := #{epoch := Epoch}} = CS, Epoch, read) ->
CS;
-connection_state_by_epoch(#connection_states{current_write = CS}, Epoch, write)
- when CS#connection_state.epoch == Epoch ->
+connection_state_by_epoch(#{current_write := #{epoch := Epoch}} = CS, Epoch, write) ->
CS;
-connection_state_by_epoch(#connection_states{pending_write = CS}, Epoch, write)
- when CS#connection_state.epoch == Epoch ->
+connection_state_by_epoch(#{pending_write := #{epoch := Epoch}} = CS, Epoch, write) ->
CS.
%%--------------------------------------------------------------------
--spec set_connection_state_by_epoch(#connection_states{},
- #connection_state{}, read | write)
- -> #connection_states{}.
+-spec set_connection_state_by_epoch(ssl_record:connection_states(),
+ ssl_record:connection_state(), read | write)
+ -> ssl_record:connection_states().
%%
%% Description: Returns the instance of the connection_state record
%% that is defined by the Epoch.
%%--------------------------------------------------------------------
-set_connection_state_by_epoch(ConnectionStates0 =
- #connection_states{current_read = CS},
- NewCS = #connection_state{epoch = Epoch}, read)
- when CS#connection_state.epoch == Epoch ->
- ConnectionStates0#connection_states{current_read = NewCS};
-
-set_connection_state_by_epoch(ConnectionStates0 =
- #connection_states{pending_read = CS},
- NewCS = #connection_state{epoch = Epoch}, read)
- when CS#connection_state.epoch == Epoch ->
- ConnectionStates0#connection_states{pending_read = NewCS};
-
-set_connection_state_by_epoch(ConnectionStates0 =
- #connection_states{current_write = CS},
- NewCS = #connection_state{epoch = Epoch}, write)
- when CS#connection_state.epoch == Epoch ->
- ConnectionStates0#connection_states{current_write = NewCS};
-
-set_connection_state_by_epoch(ConnectionStates0 =
- #connection_states{pending_write = CS},
- NewCS = #connection_state{epoch = Epoch}, write)
- when CS#connection_state.epoch == Epoch ->
- ConnectionStates0#connection_states{pending_write = NewCS}.
+set_connection_state_by_epoch(#{current_read := #{epoch := Epoch}} = ConnectionStates0,
+ NewCS = #{epoch := Epoch}, read) ->
+ ConnectionStates0#{current_read => NewCS};
+set_connection_state_by_epoch(#{pending_read := #{epoch := Epoch}} = ConnectionStates0,
+ NewCS = #{epoch := Epoch}, read) ->
+ ConnectionStates0#{pending_read => NewCS};
+set_connection_state_by_epoch(#{current_write := #{epoch := Epoch}} = ConnectionStates0,
+ NewCS = #{epoch := Epoch}, write) ->
+ ConnectionStates0#{current_write => NewCS};
+set_connection_state_by_epoch(#{pending_write := #{epoch := Epoch}} = ConnectionStates0,
+NewCS = #{epoch := Epoch}, write) ->
+ ConnectionStates0#{pending_write => NewCS}.
%%--------------------------------------------------------------------
%%% Internal functions
%%--------------------------------------------------------------------
-
+initial_connection_state(ConnectionEnd, BeastMitigation) ->
+ #{security_parameters =>
+ ssl_record:initial_security_params(ConnectionEnd),
+ epoch => 0,
+ sequence_number => 1,
+ beast_mitigation => BeastMitigation,
+ compression_state => undefined,
+ cipher_state => undefined,
+ mac_secret => undefined,
+ secure_renegotiation => undefined,
+ client_verify_data => undefined,
+ server_verify_data => undefined
+ }.
lowest_list_protocol_version(Ver, []) ->
Ver;
@@ -454,8 +463,8 @@ encode_tls_cipher_text(Type, {MajVer, MinVer}, Epoch, Seq, Fragment) ->
[<<?BYTE(Type), ?BYTE(MajVer), ?BYTE(MinVer), ?UINT16(Epoch),
?UINT48(Seq), ?UINT16(Length)>>, Fragment].
-calc_mac_hash(#connection_state{mac_secret = MacSecret,
- security_parameters = #security_parameters{mac_algorithm = MacAlg}},
+calc_mac_hash(#{mac_secret := MacSecret,
+ security_parameters := #security_parameters{mac_algorithm = MacAlg}},
Type, Version, Epoch, SeqNo, Fragment) ->
Length = erlang:iolist_size(Fragment),
NewSeq = (Epoch bsl 48) + SeqNo,
diff --git a/lib/ssl/src/ssl.app.src b/lib/ssl/src/ssl.app.src
index b26efbd88f..00b0513891 100644
--- a/lib/ssl/src/ssl.app.src
+++ b/lib/ssl/src/ssl.app.src
@@ -55,7 +55,7 @@
{applications, [crypto, public_key, kernel, stdlib]},
{env, []},
{mod, {ssl_app, []}},
- {runtime_dependencies, ["stdlib-3.0","public_key-1.2","kernel-3.0",
+ {runtime_dependencies, ["stdlib-3.1","public_key-1.2","kernel-3.0",
"erts-7.0","crypto-3.3", "inets-5.10.7"]}]}.
diff --git a/lib/ssl/src/ssl.appup.src b/lib/ssl/src/ssl.appup.src
index 11728128c4..32252386b4 100644
--- a/lib/ssl/src/ssl.appup.src
+++ b/lib/ssl/src/ssl.appup.src
@@ -1,18 +1,11 @@
%% -*- erlang -*-
{"%VSN%",
[
- {<<"7\\..*">>, [{restart_application, ssl}]},
- {<<"6\\..*">>, [{restart_application, ssl}]},
- {<<"5\\..*">>, [{restart_application, ssl}]},
- {<<"4\\..*">>, [{restart_application, ssl}]},
- {<<"3\\..*">>, [{restart_application, ssl}]}
+ {<<"^8[.]0([.][0-9]+)?$">>, [{restart_application, ssl}]},
+ {<<"^[3-7][.][^.].*">>, [{restart_application, ssl}]}
],
[
- {<<"7\\..*">>, [{restart_application, ssl}]},
- {<<"6\\..*">>, [{restart_application, ssl}]},
- {<<"5\\..*">>, [{restart_application, ssl}]},
- {<<"4\\..*">>, [{restart_application, ssl}]},
- {<<"3\\..*">>, [{restart_application, ssl}]}
- ]
+ {<<"^8[.]0([.][0-9]+)?$">>, [{restart_application, ssl}]},
+ {<<"^[3-7][.][^.].*">>, [{restart_application, ssl}]}
+ ]
}.
-
diff --git a/lib/ssl/src/ssl.erl b/lib/ssl/src/ssl.erl
index d2aeb3258f..27b753af2e 100644
--- a/lib/ssl/src/ssl.erl
+++ b/lib/ssl/src/ssl.erl
@@ -43,7 +43,7 @@
renegotiate/1, prf/5, negotiated_protocol/1, negotiated_next_protocol/1,
connection_information/1, connection_information/2]).
%% Misc
--export([handle_options/2]).
+-export([handle_options/2, tls_version/1]).
-deprecated({negotiated_next_protocol, 1, next_major_release}).
-deprecated({connection_info, 1, next_major_release}).
@@ -607,6 +607,11 @@ format_error(Error) ->
Other
end.
+tls_version({3, _} = Version) ->
+ Version;
+tls_version({254, _} = Version) ->
+ dtls_v1:corresponding_tls_version(Version).
+
%%%--------------------------------------------------------------
%%% Internal functions
%%%--------------------------------------------------------------------
diff --git a/lib/ssl/src/ssl_alert.erl b/lib/ssl/src/ssl_alert.erl
index db71b16d80..05dfb4c1b3 100644
--- a/lib/ssl/src/ssl_alert.erl
+++ b/lib/ssl/src/ssl_alert.erl
@@ -39,8 +39,8 @@
%%====================================================================
%%--------------------------------------------------------------------
--spec encode(#alert{}, ssl_record:ssl_version(), #connection_states{}) ->
- {iolist(), #connection_states{}}.
+-spec encode(#alert{}, ssl_record:ssl_version(), ssl_record:connection_states()) ->
+ {iolist(), ssl_record:connection_states()}.
%%
%% Description: Encodes an alert
%%--------------------------------------------------------------------
diff --git a/lib/ssl/src/ssl_certificate.erl b/lib/ssl/src/ssl_certificate.erl
index 3ec3f50e05..f359655d85 100644
--- a/lib/ssl/src/ssl_certificate.erl
+++ b/lib/ssl/src/ssl_certificate.erl
@@ -64,7 +64,7 @@ trusted_cert_and_path(CertChain, CertDbHandle, CertDbRef, PartialChainHandler) -
{ok, IssuerId} = public_key:pkix_issuer_id(OtpCert, self),
{self, IssuerId};
false ->
- other_issuer(OtpCert, BinCert, CertDbHandle)
+ other_issuer(OtpCert, BinCert, CertDbHandle, CertDbRef)
end,
case SignedAndIssuerID of
@@ -200,7 +200,7 @@ certificate_chain(OtpCert, BinCert, CertDbHandle, CertsDbRef, Chain) ->
{_, true = SelfSigned} ->
certificate_chain(CertDbHandle, CertsDbRef, Chain, ignore, ignore, SelfSigned);
{{error, issuer_not_found}, SelfSigned} ->
- case find_issuer(OtpCert, BinCert, CertDbHandle) of
+ case find_issuer(OtpCert, BinCert, CertDbHandle, CertsDbRef) of
{ok, {SerialNr, Issuer}} ->
certificate_chain(CertDbHandle, CertsDbRef, Chain,
SerialNr, Issuer, SelfSigned);
@@ -232,7 +232,7 @@ certificate_chain(CertDbHandle, CertsDbRef, Chain, SerialNr, Issuer, _SelfSigned
{ok, undefined, lists:reverse(Chain)}
end.
-find_issuer(OtpCert, BinCert, CertDbHandle) ->
+find_issuer(OtpCert, BinCert, CertDbHandle, CertsDbRef) ->
IsIssuerFun =
fun({_Key, {_Der, #'OTPCertificate'{} = ErlCertCandidate}}, Acc) ->
case public_key:pkix_is_issuer(OtpCert, ErlCertCandidate) of
@@ -250,12 +250,24 @@ find_issuer(OtpCert, BinCert, CertDbHandle) ->
Acc
end,
- try ssl_pkix_db:foldl(IsIssuerFun, issuer_not_found, CertDbHandle) of
- issuer_not_found ->
- {error, issuer_not_found}
- catch
- {ok, _IssuerId} = Return ->
- Return
+ if is_reference(CertsDbRef) -> % actual DB exists
+ try ssl_pkix_db:foldl(IsIssuerFun, issuer_not_found, CertDbHandle) of
+ issuer_not_found ->
+ {error, issuer_not_found}
+ catch
+ {ok, _IssuerId} = Return ->
+ Return
+ end;
+ is_tuple(CertsDbRef), element(1,CertsDbRef) =:= extracted -> % cache bypass byproduct
+ {extracted, CertsData} = CertsDbRef,
+ DB = [Entry || {decoded, Entry} <- CertsData],
+ try lists:foldl(IsIssuerFun, issuer_not_found, DB) of
+ issuer_not_found ->
+ {error, issuer_not_found}
+ catch
+ {ok, _IssuerId} = Return ->
+ Return
+ end
end.
is_valid_extkey_usage(KeyUse, client) ->
@@ -281,12 +293,12 @@ public_key(#'OTPSubjectPublicKeyInfo'{algorithm = #'PublicKeyAlgorithm'{algorith
subjectPublicKey = Key}) ->
{Key, Params}.
-other_issuer(OtpCert, BinCert, CertDbHandle) ->
+other_issuer(OtpCert, BinCert, CertDbHandle, CertDbRef) ->
case public_key:pkix_issuer_id(OtpCert, other) of
{ok, IssuerId} ->
{other, IssuerId};
{error, issuer_not_found} ->
- case find_issuer(OtpCert, BinCert, CertDbHandle) of
+ case find_issuer(OtpCert, BinCert, CertDbHandle, CertDbRef) of
{ok, IssuerId} ->
{other, IssuerId};
Other ->
diff --git a/lib/ssl/src/ssl_cipher.erl b/lib/ssl/src/ssl_cipher.erl
index e935c033c7..02873ce522 100644
--- a/lib/ssl/src/ssl_cipher.erl
+++ b/lib/ssl/src/ssl_cipher.erl
@@ -333,21 +333,27 @@ anonymous_suites({3, N}) ->
anonymous_suites(N)
when N >= 3 ->
[?TLS_DH_anon_WITH_AES_128_GCM_SHA256,
- ?TLS_DH_anon_WITH_AES_256_GCM_SHA384
- ] ++ anonymous_suites(0);
-
-anonymous_suites(_) ->
- [?TLS_DH_anon_WITH_RC4_128_MD5,
- ?TLS_DH_anon_WITH_DES_CBC_SHA,
- ?TLS_DH_anon_WITH_3DES_EDE_CBC_SHA,
- ?TLS_DH_anon_WITH_AES_128_CBC_SHA,
- ?TLS_DH_anon_WITH_AES_256_CBC_SHA,
+ ?TLS_DH_anon_WITH_AES_256_GCM_SHA384,
?TLS_DH_anon_WITH_AES_128_CBC_SHA256,
?TLS_DH_anon_WITH_AES_256_CBC_SHA256,
- ?TLS_ECDH_anon_WITH_RC4_128_SHA,
- ?TLS_ECDH_anon_WITH_3DES_EDE_CBC_SHA,
?TLS_ECDH_anon_WITH_AES_128_CBC_SHA,
- ?TLS_ECDH_anon_WITH_AES_256_CBC_SHA].
+ ?TLS_ECDH_anon_WITH_AES_256_CBC_SHA,
+ ?TLS_ECDH_anon_WITH_3DES_EDE_CBC_SHA,
+ ?TLS_DH_anon_WITH_RC4_128_MD5];
+
+anonymous_suites(2) ->
+ [?TLS_ECDH_anon_WITH_AES_128_CBC_SHA,
+ ?TLS_ECDH_anon_WITH_AES_256_CBC_SHA,
+ ?TLS_ECDH_anon_WITH_3DES_EDE_CBC_SHA,
+ ?TLS_DH_anon_WITH_DES_CBC_SHA,
+ ?TLS_DH_anon_WITH_RC4_128_MD5];
+
+anonymous_suites(N) when N == 0;
+ N == 1 ->
+ [?TLS_DH_anon_WITH_RC4_128_MD5,
+ ?TLS_DH_anon_WITH_3DES_EDE_CBC_SHA,
+ ?TLS_DH_anon_WITH_DES_CBC_SHA
+ ].
%%--------------------------------------------------------------------
-spec psk_suites(ssl_record:ssl_version() | integer()) -> [cipher_suite()].
@@ -1458,6 +1464,9 @@ is_acceptable_cipher(Cipher, Algos)
is_acceptable_cipher(Cipher, Algos)
when Cipher == chacha20_poly1305 ->
proplists:get_bool(Cipher, Algos);
+is_acceptable_cipher(Cipher, Algos)
+ when Cipher == rc4_128 ->
+ proplists:get_bool(rc4, Algos);
is_acceptable_cipher(_, _) ->
true.
diff --git a/lib/ssl/src/ssl_connection.erl b/lib/ssl/src/ssl_connection.erl
index adee59393e..08fca76123 100644
--- a/lib/ssl/src/ssl_connection.erl
+++ b/lib/ssl/src/ssl_connection.erl
@@ -58,6 +58,13 @@
-export([handle_info/3, handle_call/5, handle_session/7, ssl_config/3,
prepare_connection/2, hibernate_after/3]).
+%% Alert and close handling
+-export([handle_own_alert/4,handle_alert/3,
+ handle_normal_shutdown/3
+ ]).
+
+%% Data handling
+-export([write_application_data/3, read_application_data/2]).
%%====================================================================
%% Internal application API
@@ -264,7 +271,7 @@ prf(ConnectionPid, Secret, Label, Seed, WantedLength) ->
%%--------------------------------------------------------------------
-spec handle_session(#server_hello{}, ssl_record:ssl_version(),
- binary(), #connection_states{}, _,_, #state{}) ->
+ binary(), ssl_record:connection_states(), _,_, #state{}) ->
gen_statem:state_function_result().
%%--------------------------------------------------------------------
handle_session(#server_hello{cipher_suite = CipherSuite,
@@ -272,19 +279,21 @@ handle_session(#server_hello{cipher_suite = CipherSuite,
Version, NewId, ConnectionStates, ProtoExt, Protocol0,
#state{session = #session{session_id = OldId},
negotiated_version = ReqVersion,
- negotiated_protocol = CurrentProtocol} = State0) ->
+ negotiated_protocol = CurrentProtocol} = State0) ->
{KeyAlgorithm, _, _, _} =
ssl_cipher:suite_definition(CipherSuite),
PremasterSecret = make_premaster_secret(ReqVersion, KeyAlgorithm),
- {ExpectNPN, Protocol} = case Protocol0 of
- undefined -> {false, CurrentProtocol};
- _ -> {ProtoExt =:= npn, Protocol0}
- end,
+ {ExpectNPN, Protocol} = case Protocol0 of
+ undefined ->
+ {false, CurrentProtocol};
+ _ ->
+ {ProtoExt =:= npn, Protocol0}
+ end,
State = State0#state{key_algorithm = KeyAlgorithm,
- negotiated_version = Version,
+ negotiated_version = Version,
connection_states = ConnectionStates,
premaster_secret = PremasterSecret,
expecting_next_protocol_negotiation = ExpectNPN,
@@ -382,7 +391,7 @@ abbreviated(internal, #finished{verify_data = Data} = Finished,
session = #session{master_secret = MasterSecret},
connection_states = ConnectionStates0} =
State0, Connection) ->
- case ssl_handshake:verify_connection(Version, Finished, client,
+ case ssl_handshake:verify_connection(ssl:tls_version(Version), Finished, client,
get_current_prf(ConnectionStates0, write),
MasterSecret, Handshake) of
verified ->
@@ -392,7 +401,7 @@ abbreviated(internal, #finished{verify_data = Data} = Finished,
expecting_finished = false}, Connection),
Connection:next_event(connection, Record, State);
#alert{} = Alert ->
- Connection:handle_own_alert(Alert, Version, abbreviated, State0)
+ handle_own_alert(Alert, Version, abbreviated, State0)
end;
abbreviated(internal, #finished{verify_data = Data} = Finished,
@@ -400,7 +409,7 @@ abbreviated(internal, #finished{verify_data = Data} = Finished,
session = #session{master_secret = MasterSecret},
negotiated_version = Version,
connection_states = ConnectionStates0} = State0, Connection) ->
- case ssl_handshake:verify_connection(Version, Finished, server,
+ case ssl_handshake:verify_connection(ssl:tls_version(Version), Finished, server,
get_pending_prf(ConnectionStates0, write),
MasterSecret, Handshake0) of
verified ->
@@ -412,7 +421,7 @@ abbreviated(internal, #finished{verify_data = Data} = Finished,
{Record, State} = prepare_connection(State1#state{expecting_finished = false}, Connection),
Connection:next_event(connection, Record, State);
#alert{} = Alert ->
- Connection:handle_own_alert(Alert, Version, abbreviated, State0)
+ handle_own_alert(Alert, Version, abbreviated, State0)
end;
%% only allowed to send next_protocol message after change cipher spec
@@ -452,9 +461,9 @@ certify(internal, #certificate{asn1_certificates = []},
#state{role = server, negotiated_version = Version,
ssl_options = #ssl_options{verify = verify_peer,
fail_if_no_peer_cert = true}} =
- State, Connection) ->
+ State, _Connection) ->
Alert = ?ALERT_REC(?FATAL,?HANDSHAKE_FAILURE),
- Connection:handle_own_alert(Alert, Version, certify, State);
+ handle_own_alert(Alert, Version, certify, State);
certify(internal, #certificate{asn1_certificates = []},
#state{role = server,
@@ -469,9 +478,9 @@ certify(internal, #certificate{},
#state{role = server,
negotiated_version = Version,
ssl_options = #ssl_options{verify = verify_none}} =
- State, Connection) ->
+ State, _Connection) ->
Alert = ?ALERT_REC(?FATAL,?UNEXPECTED_MESSAGE, unrequested_certificate),
- Connection:handle_own_alert(Alert, Version, certify, State);
+ handle_own_alert(Alert, Version, certify, State);
certify(internal, #certificate{} = Cert,
#state{negotiated_version = Version,
@@ -492,7 +501,7 @@ certify(internal, #certificate{} = Cert,
handle_peer_cert(Role, PeerCert, PublicKeyInfo,
State#state{client_certificate_requested = false}, Connection);
#alert{} = Alert ->
- Connection:handle_own_alert(Alert, Version, certify, State)
+ handle_own_alert(Alert, Version, certify, State)
end;
certify(internal, #server_key_exchange{exchange_keys = Keys},
@@ -506,10 +515,10 @@ certify(internal, #server_key_exchange{exchange_keys = Keys},
Alg == psk; Alg == dhe_psk; Alg == rsa_psk;
Alg == srp_dss; Alg == srp_rsa; Alg == srp_anon ->
- Params = ssl_handshake:decode_server_key(Keys, Alg, Version),
+ Params = ssl_handshake:decode_server_key(Keys, Alg, ssl:tls_version(Version)),
%% Use negotiated value if TLS-1.2 otherwhise return default
- HashSign = negotiated_hashsign(Params#server_key_params.hashsign, Alg, PubKeyInfo, Version),
+ HashSign = negotiated_hashsign(Params#server_key_params.hashsign, Alg, PubKeyInfo, ssl:tls_version(Version)),
case is_anonymous(Alg) of
true ->
@@ -517,13 +526,13 @@ certify(internal, #server_key_exchange{exchange_keys = Keys},
State#state{hashsign_algorithm = HashSign}, Connection);
false ->
case ssl_handshake:verify_server_key(Params, HashSign,
- ConnectionStates, Version, PubKeyInfo) of
+ ConnectionStates, ssl:tls_version(Version), PubKeyInfo) of
true ->
calculate_secret(Params#server_key_params.params,
State#state{hashsign_algorithm = HashSign},
Connection);
false ->
- Connection:handle_own_alert(?ALERT_REC(?FATAL, ?DECRYPT_ERROR),
+ handle_own_alert(?ALERT_REC(?FATAL, ?DECRYPT_ERROR),
Version, certify, State)
end
end;
@@ -533,9 +542,9 @@ certify(internal, #certificate_request{} = CertRequest,
role = client,
ssl_options = #ssl_options{signature_algs = SupportedHashSigns},
negotiated_version = Version} = State0, Connection) ->
- case ssl_handshake:select_hashsign(CertRequest, Cert, SupportedHashSigns, Version) of
+ case ssl_handshake:select_hashsign(CertRequest, Cert, SupportedHashSigns, ssl:tls_version(Version)) of
#alert {} = Alert ->
- Connection:handle_own_alert(Alert, Version, certify, State0);
+ handle_own_alert(Alert, Version, certify, State0);
NegotiatedHashSign ->
{Record, State} = Connection:next_record(State0#state{client_certificate_requested = true}),
Connection:next_event(certify, Record,
@@ -554,7 +563,7 @@ certify(internal, #server_hello_done{},
when Alg == psk ->
case ssl_handshake:premaster_secret({Alg, PSKIdentity}, PSKLookup) of
#alert{} = Alert ->
- Connection:handle_own_alert(Alert, Version, certify, State0);
+ handle_own_alert(Alert, Version, certify, State0);
PremasterSecret ->
State = master_secret(PremasterSecret,
State0#state{premaster_secret = PremasterSecret}),
@@ -575,7 +584,7 @@ certify(internal, #server_hello_done{},
case ssl_handshake:premaster_secret({Alg, PSKIdentity}, PSKLookup,
RSAPremasterSecret) of
#alert{} = Alert ->
- Connection:handle_own_alert(Alert, Version, certify, State0);
+ handle_own_alert(Alert, Version, certify, State0);
PremasterSecret ->
State = master_secret(PremasterSecret,
State0#state{premaster_secret = RSAPremasterSecret}),
@@ -589,13 +598,13 @@ certify(internal, #server_hello_done{},
negotiated_version = Version,
premaster_secret = undefined,
role = client} = State0, Connection) ->
- case ssl_handshake:master_secret(record_cb(Connection), Version, Session,
+ case ssl_handshake:master_secret(ssl:tls_version(Version), Session,
ConnectionStates0, client) of
{MasterSecret, ConnectionStates} ->
State = State0#state{connection_states = ConnectionStates},
client_certify_and_key_exchange(State, Connection);
#alert{} = Alert ->
- Connection:handle_own_alert(Alert, Version, certify, State0)
+ handle_own_alert(Alert, Version, certify, State0)
end;
%% Master secret is calculated from premaster_secret
@@ -605,7 +614,7 @@ certify(internal, #server_hello_done{},
negotiated_version = Version,
premaster_secret = PremasterSecret,
role = client} = State0, Connection) ->
- case ssl_handshake:master_secret(record_cb(Connection), Version, PremasterSecret,
+ case ssl_handshake:master_secret(ssl:tls_version(Version), PremasterSecret,
ConnectionStates0, client) of
{MasterSecret, ConnectionStates} ->
Session = Session0#session{master_secret = MasterSecret},
@@ -613,7 +622,7 @@ certify(internal, #server_hello_done{},
session = Session},
client_certify_and_key_exchange(State, Connection);
#alert{} = Alert ->
- Connection:handle_own_alert(Alert, Version, certify, State0)
+ handle_own_alert(Alert, Version, certify, State0)
end;
certify(internal = Type, #client_key_exchange{} = Msg,
@@ -627,11 +636,11 @@ certify(internal = Type, #client_key_exchange{} = Msg,
certify(internal, #client_key_exchange{exchange_keys = Keys},
State = #state{key_algorithm = KeyAlg, negotiated_version = Version}, Connection) ->
try
- certify_client_key_exchange(ssl_handshake:decode_client_key(Keys, KeyAlg, Version),
+ certify_client_key_exchange(ssl_handshake:decode_client_key(Keys, KeyAlg, ssl:tls_version(Version)),
State, Connection)
catch
#alert{} = Alert ->
- Connection:handle_own_alert(Alert, Version, certify, State)
+ handle_own_alert(Alert, Version, certify, State)
end;
certify(Type, Msg, State, Connection) ->
@@ -662,21 +671,21 @@ cipher(internal, #certificate_verify{signature = Signature,
%% Use negotiated value if TLS-1.2 otherwhise return default
HashSign = negotiated_hashsign(CertHashSign, KexAlg, PublicKeyInfo, Version),
case ssl_handshake:certificate_verify(Signature, PublicKeyInfo,
- Version, HashSign, MasterSecret, Handshake) of
+ ssl:tls_version(Version), HashSign, MasterSecret, Handshake) of
valid ->
{Record, State} = Connection:next_record(State0),
Connection:next_event(cipher, Record,
State#state{cert_hashsign_algorithm = HashSign});
#alert{} = Alert ->
- Connection:handle_own_alert(Alert, Version, cipher, State0)
+ handle_own_alert(Alert, Version, cipher, State0)
end;
%% client must send a next protocol message if we are expecting it
cipher(internal, #finished{},
#state{role = server, expecting_next_protocol_negotiation = true,
negotiated_protocol = undefined, negotiated_version = Version} = State0,
- Connection) ->
- Connection:handle_own_alert(?ALERT_REC(?FATAL,?UNEXPECTED_MESSAGE), Version, cipher, State0);
+ _Connection) ->
+ handle_own_alert(?ALERT_REC(?FATAL,?UNEXPECTED_MESSAGE), Version, cipher, State0);
cipher(internal, #finished{verify_data = Data} = Finished,
#state{negotiated_version = Version,
@@ -688,7 +697,7 @@ cipher(internal, #finished{verify_data = Data} = Finished,
= Session0,
connection_states = ConnectionStates0,
tls_handshake_history = Handshake0} = State, Connection) ->
- case ssl_handshake:verify_connection(Version, Finished,
+ case ssl_handshake:verify_connection(ssl:tls_version(Version), Finished,
opposite_role(Role),
get_current_prf(ConnectionStates0, read),
MasterSecret, Handshake0) of
@@ -697,7 +706,7 @@ cipher(internal, #finished{verify_data = Data} = Finished,
cipher_role(Role, Data, Session,
State#state{expecting_finished = false}, Connection);
#alert{} = Alert ->
- Connection:handle_own_alert(Alert, Version, cipher, State)
+ handle_own_alert(Alert, Version, cipher, State)
end;
%% only allowed to send next_protocol message after change cipher spec
@@ -730,7 +739,7 @@ connection({call, From}, {application_data, Data},
%% parallize send and receive decoding and not block the receiver
%% if sending is overloading the socket.
try
- Connection:write_application_data(Data, From, State)
+ write_application_data(Data, From, State)
catch throw:Error ->
hibernate_after(connection, State, [{reply, From, Error}])
end;
@@ -801,35 +810,37 @@ handle_common_event(internal, {handshake, {#hello_request{}, _}}, StateName, #st
when StateName =/= connection ->
{keep_state_and_data};
handle_common_event(internal, {handshake, {Handshake, Raw}}, StateName,
- #state{tls_handshake_history = Hs0} = State0, Connection) ->
+ #state{tls_handshake_history = Hs0,
+ ssl_options = #ssl_options{v2_hello_compatible = V2HComp}} = State0,
+ Connection) ->
+
+ PossibleSNI = Connection:select_sni_extension(Handshake),
%% This function handles client SNI hello extension when Handshake is
%% a client_hello, which needs to be determined by the connection callback.
%% In other cases this is a noop
- State = Connection:handle_sni_extension(Handshake, State0),
- HsHist = ssl_handshake:update_handshake_history(Hs0, Raw),
+ State = handle_sni_extension(PossibleSNI, State0),
+ HsHist = ssl_handshake:update_handshake_history(Hs0, Raw, V2HComp),
{next_state, StateName, State#state{tls_handshake_history = HsHist},
[{next_event, internal, Handshake}]};
-handle_common_event(internal, {tls_record, TLSRecord}, StateName, State, Connection) ->
- Connection:handle_common_event(internal, TLSRecord, StateName, State);
+handle_common_event(internal, {protocol_record, TLSorDTLSRecord}, StateName, State, Connection) ->
+ Connection:handle_common_event(internal, TLSorDTLSRecord, StateName, State);
handle_common_event(timeout, hibernate, _, _, _) ->
{keep_state_and_data, [hibernate]};
handle_common_event(internal, {application_data, Data}, StateName, State0, Connection) ->
- case Connection:read_application_data(Data, State0) of
+ case read_application_data(Data, State0) of
{stop, Reason, State} ->
{stop, Reason, State};
{Record, State} ->
Connection:next_event(StateName, Record, State)
end;
handle_common_event(internal, #change_cipher_spec{type = <<1>>}, StateName,
- #state{negotiated_version = Version} = State, Connection) ->
- Connection:handle_own_alert(?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE), Version,
+ #state{negotiated_version = Version} = State, _) ->
+ handle_own_alert(?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE), Version,
StateName, State);
-handle_common_event(internal, _, _, _, _) ->
- {keep_state_and_data, [postpone]};
handle_common_event(_Type, Msg, StateName, #state{negotiated_version = Version} = State,
- Connection) ->
+ _) ->
Alert = ?ALERT_REC(?FATAL,?UNEXPECTED_MESSAGE),
- Connection:handle_own_alert(Alert, Version, {StateName, Msg}, State).
+ handle_own_alert(Alert, Version, {StateName, Msg}, State).
handle_call({application_data, _Data}, _, _, _, _) ->
%% In renegotiation priorities handshake, send data when handshake is finished
@@ -907,9 +918,8 @@ handle_call(renegotiate, From, StateName, _, _) when StateName =/= connection ->
handle_call({prf, Secret, Label, Seed, WantedLength}, From, _,
#state{connection_states = ConnectionStates,
negotiated_version = Version}, _) ->
- ConnectionState =
+ #{security_parameters := SecParams} =
ssl_record:current_connection_state(ConnectionStates, read),
- SecParams = ConnectionState#connection_state.security_parameters,
#security_parameters{master_secret = MasterSecret,
client_random = ClientRandom,
server_random = ServerRandom,
@@ -924,7 +934,7 @@ handle_call({prf, Secret, Label, Seed, WantedLength}, From, _,
(client_random, Acc) -> [ClientRandom|Acc];
(server_random, Acc) -> [ServerRandom|Acc]
end, [], Seed)),
- ssl_handshake:prf(Version, PRFAlgorithm, SecretToUse, Label, SeedToUse, WantedLength)
+ ssl_handshake:prf(ssl:tls_version(Version), PRFAlgorithm, SecretToUse, Label, SeedToUse, WantedLength)
catch
exit:_ -> {error, badarg};
error:Reason -> {error, Reason}
@@ -935,20 +945,19 @@ handle_call(_,_,_,_,_) ->
handle_info({ErrorTag, Socket, econnaborted}, StateName,
#state{socket = Socket, transport_cb = Transport,
- start_or_recv_from = StartFrom, role = Role,
protocol_cb = Connection,
+ start_or_recv_from = StartFrom, role = Role,
error_tag = ErrorTag,
tracker = Tracker} = State) when StateName =/= connection ->
- Connection:alert_user(Transport, Tracker,Socket,
- StartFrom, ?ALERT_REC(?FATAL, ?CLOSE_NOTIFY), Role),
+ alert_user(Transport, Tracker,Socket,
+ StartFrom, ?ALERT_REC(?FATAL, ?CLOSE_NOTIFY), Role, Connection),
{stop, normal, State};
handle_info({ErrorTag, Socket, Reason}, StateName, #state{socket = Socket,
- protocol_cb = Connection,
error_tag = ErrorTag} = State) ->
Report = io_lib:format("SSL: Socket error: ~p ~n", [Reason]),
error_logger:info_report(Report),
- Connection:handle_normal_shutdown(?ALERT_REC(?FATAL, ?CLOSE_NOTIFY), StateName, State),
+ handle_normal_shutdown(?ALERT_REC(?FATAL, ?CLOSE_NOTIFY), StateName, State),
{stop, normal, State};
handle_info({'DOWN', MonitorRef, _, _, _}, _,
@@ -1051,13 +1060,124 @@ format_status(terminate, [_, StateName, State]) ->
ssl_options = NewOptions,
flight_buffer = ?SECRET_PRINTOUT}
}}]}].
+
+%%--------------------------------------------------------------------
+%%%
+%%--------------------------------------------------------------------
+write_application_data(Data0, From,
+ #state{socket = Socket,
+ negotiated_version = Version,
+ protocol_cb = Connection,
+ transport_cb = Transport,
+ connection_states = ConnectionStates0,
+ socket_options = SockOpts,
+ ssl_options = #ssl_options{renegotiate_at = RenegotiateAt}} = State) ->
+ Data = encode_packet(Data0, SockOpts),
+
+ case time_to_renegotiate(Data, ConnectionStates0, RenegotiateAt) of
+ true ->
+ Connection:renegotiate(State#state{renegotiation = {true, internal}},
+ [{next_event, {call, From}, {application_data, Data0}}]);
+ false ->
+ {Msgs, ConnectionStates} = ssl_record:encode_data(Data, Version, ConnectionStates0),
+ Result = Transport:send(Socket, Msgs),
+ ssl_connection:hibernate_after(connection, State#state{connection_states = ConnectionStates},
+ [{reply, From, Result}])
+ end.
+
+read_application_data(Data, #state{user_application = {_Mon, Pid},
+ socket = Socket,
+ protocol_cb = Connection,
+ transport_cb = Transport,
+ socket_options = SOpts,
+ bytes_to_read = BytesToRead,
+ start_or_recv_from = RecvFrom,
+ timer = Timer,
+ user_data_buffer = Buffer0,
+ tracker = Tracker} = State0) ->
+ Buffer1 = if
+ Buffer0 =:= <<>> -> Data;
+ Data =:= <<>> -> Buffer0;
+ true -> <<Buffer0/binary, Data/binary>>
+ end,
+ case get_data(SOpts, BytesToRead, Buffer1) of
+ {ok, ClientData, Buffer} -> % Send data
+ SocketOpt = deliver_app_data(Transport, Socket, SOpts,
+ ClientData, Pid, RecvFrom, Tracker, Connection),
+ cancel_timer(Timer),
+ State = State0#state{user_data_buffer = Buffer,
+ start_or_recv_from = undefined,
+ timer = undefined,
+ bytes_to_read = undefined,
+ socket_options = SocketOpt
+ },
+ if
+ SocketOpt#socket_options.active =:= false; Buffer =:= <<>> ->
+ %% Passive mode, wait for active once or recv
+ %% Active and empty, get more data
+ Connection:next_record_if_active(State);
+ true -> %% We have more data
+ read_application_data(<<>>, State)
+ end;
+ {more, Buffer} -> % no reply, we need more data
+ Connection:next_record(State0#state{user_data_buffer = Buffer});
+ {passive, Buffer} ->
+ Connection:next_record_if_active(State0#state{user_data_buffer = Buffer});
+ {error,_Reason} -> %% Invalid packet in packet mode
+ deliver_packet_error(Transport, Socket, SOpts, Buffer1, Pid, RecvFrom, Tracker, Connection),
+ {stop, normal, State0}
+ end.
+%%--------------------------------------------------------------------
+%%%
+%%--------------------------------------------------------------------
+handle_alert(#alert{level = ?FATAL} = Alert, StateName,
+ #state{socket = Socket, transport_cb = Transport,
+ protocol_cb = Connection,
+ ssl_options = SslOpts, start_or_recv_from = From, host = Host,
+ port = Port, session = Session, user_application = {_Mon, Pid},
+ role = Role, socket_options = Opts, tracker = Tracker}) ->
+ invalidate_session(Role, Host, Port, Session),
+ log_alert(SslOpts#ssl_options.log_alert, StateName, Alert),
+ alert_user(Transport, Tracker, Socket, StateName, Opts, Pid, From, Alert, Role, Connection),
+ {stop, normal};
+
+handle_alert(#alert{level = ?WARNING, description = ?CLOSE_NOTIFY} = Alert,
+ StateName, State) ->
+ handle_normal_shutdown(Alert, StateName, State),
+ {stop, {shutdown, peer_close}};
+
+handle_alert(#alert{level = ?WARNING, description = ?NO_RENEGOTIATION} = Alert, StateName,
+ #state{ssl_options = SslOpts, renegotiation = {true, internal}} = State) ->
+ log_alert(SslOpts#ssl_options.log_alert, StateName, Alert),
+ handle_normal_shutdown(Alert, StateName, State),
+ {stop, {shutdown, peer_close}};
+
+handle_alert(#alert{level = ?WARNING, description = ?NO_RENEGOTIATION} = Alert, StateName,
+ #state{ssl_options = SslOpts, renegotiation = {true, From},
+ protocol_cb = Connection} = State0) ->
+ log_alert(SslOpts#ssl_options.log_alert, StateName, Alert),
+ gen_statem:reply(From, {error, renegotiation_rejected}),
+ {Record, State} = Connection:next_record(State0),
+ %% Go back to connection!
+ Connection:next_event(connection, Record, State);
+
+%% Gracefully log and ignore all other warning alerts
+handle_alert(#alert{level = ?WARNING} = Alert, StateName,
+ #state{ssl_options = SslOpts, protocol_cb = Connection} = State0) ->
+ log_alert(SslOpts#ssl_options.log_alert, StateName, Alert),
+ {Record, State} = Connection:next_record(State0),
+ Connection:next_event(StateName, Record, State).
+
%%--------------------------------------------------------------------
%%% Internal functions
%%--------------------------------------------------------------------
connection_info(#state{sni_hostname = SNIHostname,
session = #session{cipher_suite = CipherSuite},
- negotiated_version = Version, ssl_options = Opts}) ->
- [{protocol, tls_record:protocol_version(Version)},
+ protocol_cb = Connection,
+ negotiated_version = {_,_} = Version,
+ ssl_options = Opts}) ->
+ RecordCB = record_cb(Connection),
+ [{protocol, RecordCB:protocol_version(Version)},
{cipher_suite, ssl_cipher:erl_suite_definition(CipherSuite)},
{sni_hostname, SNIHostname}] ++ ssl_options_list(Opts).
@@ -1069,7 +1189,7 @@ do_server_hello(Type, #hello_extensions{next_protocol_negotiation = NextProtocol
= State0, Connection) when is_atom(Type) ->
ServerHello =
- ssl_handshake:server_hello(SessId, Version, ConnectionStates0, ServerHelloExt),
+ ssl_handshake:server_hello(SessId, ssl:tls_version(Version), ConnectionStates0, ServerHelloExt),
State = server_hello(ServerHello,
State0#state{expecting_next_protocol_negotiation =
NextProtocols =/= undefined}, Connection),
@@ -1096,14 +1216,14 @@ new_server_hello(#server_hello{cipher_suite = CipherSuite,
Connection:next_event(certify, Record, State)
catch
#alert{} = Alert ->
- Connection:handle_own_alert(Alert, Version, hello, State0)
+ handle_own_alert(Alert, Version, hello, State0)
end.
resumed_server_hello(#state{session = Session,
connection_states = ConnectionStates0,
negotiated_version = Version} = State0, Connection) ->
- case ssl_handshake:master_secret(record_cb(Connection), Version, Session,
+ case ssl_handshake:master_secret(ssl:tls_version(Version), Session,
ConnectionStates0, server) of
{_, ConnectionStates1} ->
State1 = State0#state{connection_states = ConnectionStates1,
@@ -1113,7 +1233,7 @@ resumed_server_hello(#state{session = Session,
{Record, State} = Connection:next_record(State2),
Connection:next_event(abbreviated, Record, State);
#alert{} = Alert ->
- Connection:handle_own_alert(Alert, Version, hello, State0)
+ handle_own_alert(Alert, Version, hello, State0)
end.
server_hello(ServerHello, State0, Connection) ->
@@ -1179,7 +1299,7 @@ verify_client_cert(#state{client_certificate_requested = true, role = client,
tls_handshake_history = Handshake0} = State, Connection) ->
case ssl_handshake:client_certificate_verify(OwnCert, MasterSecret,
- Version, HashSign, PrivateKey, Handshake0) of
+ ssl:tls_version(Version), HashSign, PrivateKey, Handshake0) of
#certificate_verify{} = Verified ->
Connection:queue_handshake(Verified, State);
ignore ->
@@ -1202,7 +1322,7 @@ client_certify_and_key_exchange(#state{negotiated_version = Version} =
Connection:next_event(cipher, Record, State)
catch
throw:#alert{} = Alert ->
- Connection:handle_own_alert(Alert, Version, certify, State0)
+ handle_own_alert(Alert, Version, certify, State0)
end.
do_client_certify_and_key_exchange(State0, Connection) ->
@@ -1293,12 +1413,11 @@ key_exchange(#state{role = server, key_algorithm = Algo,
Algo == dhe_rsa;
Algo == dh_anon ->
DHKeys = public_key:generate_key(Params),
- ConnectionState =
+ #{security_parameters := SecParams} =
ssl_record:pending_connection_state(ConnectionStates0, read),
- SecParams = ConnectionState#connection_state.security_parameters,
#security_parameters{client_random = ClientRandom,
server_random = ServerRandom} = SecParams,
- Msg = ssl_handshake:key_exchange(server, Version, {dh, DHKeys, Params,
+ Msg = ssl_handshake:key_exchange(server, ssl:tls_version(Version), {dh, DHKeys, Params,
HashSignAlgo, ClientRandom,
ServerRandom,
PrivateKey}),
@@ -1311,22 +1430,23 @@ key_exchange(#state{role = server, private_key = Key, key_algorithm = Algo} = St
key_exchange(#state{role = server, key_algorithm = Algo,
hashsign_algorithm = HashSignAlgo,
private_key = PrivateKey,
+ session = #session{ecc = ECCCurve},
connection_states = ConnectionStates0,
negotiated_version = Version
} = State0, Connection)
when Algo == ecdhe_ecdsa; Algo == ecdhe_rsa;
Algo == ecdh_anon ->
- ECDHKeys = public_key:generate_key(select_curve(State0)),
- ConnectionState =
+ ECDHKeys = public_key:generate_key(ECCCurve),
+ #{security_parameters := SecParams} =
ssl_record:pending_connection_state(ConnectionStates0, read),
- SecParams = ConnectionState#connection_state.security_parameters,
#security_parameters{client_random = ClientRandom,
server_random = ServerRandom} = SecParams,
- Msg = ssl_handshake:key_exchange(server, Version, {ecdh, ECDHKeys,
- HashSignAlgo, ClientRandom,
- ServerRandom,
- PrivateKey}),
+ Msg = ssl_handshake:key_exchange(server, ssl:tls_version(Version),
+ {ecdh, ECDHKeys,
+ HashSignAlgo, ClientRandom,
+ ServerRandom,
+ PrivateKey}),
State = Connection:queue_handshake(Msg, State0),
State#state{diffie_hellman_keys = ECDHKeys};
@@ -1340,14 +1460,14 @@ key_exchange(#state{role = server, key_algorithm = psk,
connection_states = ConnectionStates0,
negotiated_version = Version
} = State0, Connection) ->
- ConnectionState =
+ #{security_parameters := SecParams} =
ssl_record:pending_connection_state(ConnectionStates0, read),
- SecParams = ConnectionState#connection_state.security_parameters,
#security_parameters{client_random = ClientRandom,
server_random = ServerRandom} = SecParams,
- Msg = ssl_handshake:key_exchange(server, Version, {psk, PskIdentityHint,
- HashSignAlgo, ClientRandom,
- ServerRandom,
+ Msg = ssl_handshake:key_exchange(server, ssl:tls_version(Version),
+ {psk, PskIdentityHint,
+ HashSignAlgo, ClientRandom,
+ ServerRandom,
PrivateKey}),
Connection:queue_handshake(Msg, State0);
@@ -1360,16 +1480,16 @@ key_exchange(#state{role = server, key_algorithm = dhe_psk,
negotiated_version = Version
} = State0, Connection) ->
DHKeys = public_key:generate_key(Params),
- ConnectionState =
+ #{security_parameters := SecParams} =
ssl_record:pending_connection_state(ConnectionStates0, read),
- SecParams = ConnectionState#connection_state.security_parameters,
#security_parameters{client_random = ClientRandom,
server_random = ServerRandom} = SecParams,
- Msg = ssl_handshake:key_exchange(server, Version, {dhe_psk,
- PskIdentityHint, DHKeys, Params,
- HashSignAlgo, ClientRandom,
- ServerRandom,
- PrivateKey}),
+ Msg = ssl_handshake:key_exchange(server, ssl:tls_version(Version),
+ {dhe_psk,
+ PskIdentityHint, DHKeys, Params,
+ HashSignAlgo, ClientRandom,
+ ServerRandom,
+ PrivateKey}),
State = Connection:queue_handshake(Msg, State0),
State#state{diffie_hellman_keys = DHKeys};
@@ -1383,15 +1503,15 @@ key_exchange(#state{role = server, key_algorithm = rsa_psk,
connection_states = ConnectionStates0,
negotiated_version = Version
} = State0, Connection) ->
- ConnectionState =
+ #{security_parameters := SecParams} =
ssl_record:pending_connection_state(ConnectionStates0, read),
- SecParams = ConnectionState#connection_state.security_parameters,
#security_parameters{client_random = ClientRandom,
server_random = ServerRandom} = SecParams,
- Msg = ssl_handshake:key_exchange(server, Version, {psk, PskIdentityHint,
- HashSignAlgo, ClientRandom,
- ServerRandom,
- PrivateKey}),
+ Msg = ssl_handshake:key_exchange(server, ssl:tls_version(Version),
+ {psk, PskIdentityHint,
+ HashSignAlgo, ClientRandom,
+ ServerRandom,
+ PrivateKey}),
Connection:queue_handshake(Msg, State0);
key_exchange(#state{role = server, key_algorithm = Algo,
@@ -1412,15 +1532,15 @@ key_exchange(#state{role = server, key_algorithm = Algo,
Keys0 = {_,_} ->
Keys0
end,
- ConnectionState =
+ #{security_parameters := SecParams} =
ssl_record:pending_connection_state(ConnectionStates0, read),
- SecParams = ConnectionState#connection_state.security_parameters,
#security_parameters{client_random = ClientRandom,
server_random = ServerRandom} = SecParams,
- Msg = ssl_handshake:key_exchange(server, Version, {srp, Keys, SrpParams,
- HashSignAlgo, ClientRandom,
- ServerRandom,
- PrivateKey}),
+ Msg = ssl_handshake:key_exchange(server, ssl:tls_version(Version),
+ {srp, Keys, SrpParams,
+ HashSignAlgo, ClientRandom,
+ ServerRandom,
+ PrivateKey}),
State = Connection:queue_handshake(Msg, State0),
State#state{srp_params = SrpParams,
srp_keys = Keys};
@@ -1430,7 +1550,7 @@ key_exchange(#state{role = client,
public_key_info = PublicKeyInfo,
negotiated_version = Version,
premaster_secret = PremasterSecret} = State0, Connection) ->
- Msg = rsa_key_exchange(Version, PremasterSecret, PublicKeyInfo),
+ Msg = rsa_key_exchange(ssl:tls_version(Version), PremasterSecret, PublicKeyInfo),
Connection:queue_handshake(Msg, State0);
key_exchange(#state{role = client,
@@ -1441,7 +1561,7 @@ key_exchange(#state{role = client,
when Algorithm == dhe_dss;
Algorithm == dhe_rsa;
Algorithm == dh_anon ->
- Msg = ssl_handshake:key_exchange(client, Version, {dh, DhPubKey}),
+ Msg = ssl_handshake:key_exchange(client, ssl:tls_version(Version), {dh, DhPubKey}),
Connection:queue_handshake(Msg, State0);
key_exchange(#state{role = client,
@@ -1451,14 +1571,14 @@ key_exchange(#state{role = client,
when Algorithm == ecdhe_ecdsa; Algorithm == ecdhe_rsa;
Algorithm == ecdh_ecdsa; Algorithm == ecdh_rsa;
Algorithm == ecdh_anon ->
- Msg = ssl_handshake:key_exchange(client, Version, {ecdh, Keys}),
+ Msg = ssl_handshake:key_exchange(client, ssl:tls_version(Version), {ecdh, Keys}),
Connection:queue_handshake(Msg, State0);
key_exchange(#state{role = client,
ssl_options = SslOpts,
key_algorithm = psk,
negotiated_version = Version} = State0, Connection) ->
- Msg = ssl_handshake:key_exchange(client, Version,
+ Msg = ssl_handshake:key_exchange(client, ssl:tls_version(Version),
{psk, SslOpts#ssl_options.psk_identity}),
Connection:queue_handshake(Msg, State0);
@@ -1467,7 +1587,7 @@ key_exchange(#state{role = client,
key_algorithm = dhe_psk,
negotiated_version = Version,
diffie_hellman_keys = {DhPubKey, _}} = State0, Connection) ->
- Msg = ssl_handshake:key_exchange(client, Version,
+ Msg = ssl_handshake:key_exchange(client, ssl:tls_version(Version),
{dhe_psk,
SslOpts#ssl_options.psk_identity, DhPubKey}),
Connection:queue_handshake(Msg, State0);
@@ -1478,7 +1598,7 @@ key_exchange(#state{role = client,
negotiated_version = Version,
premaster_secret = PremasterSecret}
= State0, Connection) ->
- Msg = rsa_psk_key_exchange(Version, SslOpts#ssl_options.psk_identity,
+ Msg = rsa_psk_key_exchange(ssl:tls_version(Version), SslOpts#ssl_options.psk_identity,
PremasterSecret, PublicKeyInfo),
Connection:queue_handshake(Msg, State0);
@@ -1490,7 +1610,7 @@ key_exchange(#state{role = client,
when Algorithm == srp_dss;
Algorithm == srp_rsa;
Algorithm == srp_anon ->
- Msg = ssl_handshake:key_exchange(client, Version, {srp, ClientPubKey}),
+ Msg = ssl_handshake:key_exchange(client, ssl:tls_version(Version), {srp, ClientPubKey}),
Connection:queue_handshake(Msg, State0).
rsa_key_exchange(Version, PremasterSecret, PublicKeyInfo = {Algorithm, _, _})
@@ -1503,7 +1623,7 @@ rsa_key_exchange(Version, PremasterSecret, PublicKeyInfo = {Algorithm, _, _})
Algorithm == ?sha384WithRSAEncryption;
Algorithm == ?sha512WithRSAEncryption
->
- ssl_handshake:key_exchange(client, Version,
+ ssl_handshake:key_exchange(client, ssl:tls_version(Version),
{premaster_secret, PremasterSecret,
PublicKeyInfo});
rsa_key_exchange(_, _, _) ->
@@ -1520,7 +1640,7 @@ rsa_psk_key_exchange(Version, PskIdentity, PremasterSecret,
Algorithm == ?sha384WithRSAEncryption;
Algorithm == ?sha512WithRSAEncryption
->
- ssl_handshake:key_exchange(client, Version,
+ ssl_handshake:key_exchange(client, ssl:tls_version(Version),
{psk_premaster_secret, PskIdentity, PremasterSecret,
PublicKeyInfo});
rsa_psk_key_exchange(_, _, _, _) ->
@@ -1532,12 +1652,14 @@ request_client_cert(#state{ssl_options = #ssl_options{verify = verify_peer,
cert_db = CertDbHandle,
cert_db_ref = CertDbRef,
negotiated_version = Version} = State0, Connection) ->
- #connection_state{security_parameters =
- #security_parameters{cipher_suite = CipherSuite}} =
+ #{security_parameters :=
+ #security_parameters{cipher_suite = CipherSuite}} =
ssl_record:pending_connection_state(ConnectionStates0, read),
- HashSigns = ssl_handshake:available_signature_algs(SupportedHashSigns, Version, [Version]),
+ TLSVersion = ssl:tls_version(Version),
+ HashSigns = ssl_handshake:available_signature_algs(SupportedHashSigns,
+ TLSVersion, [TLSVersion]),
Msg = ssl_handshake:certificate_request(CipherSuite, CertDbHandle, CertDbRef,
- HashSigns, Version),
+ HashSigns, TLSVersion),
State = Connection:queue_handshake(Msg, State0),
State#state{client_certificate_requested = true};
@@ -1550,7 +1672,7 @@ calculate_master_secret(PremasterSecret,
connection_states = ConnectionStates0,
session = Session0} = State0, Connection,
_Current, Next) ->
- case ssl_handshake:master_secret(record_cb(Connection), Version, PremasterSecret,
+ case ssl_handshake:master_secret(ssl:tls_version(Version), PremasterSecret,
ConnectionStates0, server) of
{MasterSecret, ConnectionStates} ->
Session = Session0#session{master_secret = MasterSecret},
@@ -1559,7 +1681,7 @@ calculate_master_secret(PremasterSecret,
{Record, State} = Connection:next_record(State1),
Connection:next_event(Next, Record, State);
#alert{} = Alert ->
- Connection:handle_own_alert(Alert, Version, certify, State0)
+ handle_own_alert(Alert, Version, certify, State0)
end.
finalize_handshake(State0, StateName, Connection) ->
@@ -1592,7 +1714,7 @@ finished(#state{role = Role, negotiated_version = Version,
connection_states = ConnectionStates0,
tls_handshake_history = Handshake0} = State0, StateName, Connection) ->
MasterSecret = Session#session.master_secret,
- Finished = ssl_handshake:finished(Version, Role,
+ Finished = ssl_handshake:finished(ssl:tls_version(Version), Role,
get_current_prf(ConnectionStates0, write),
MasterSecret, Handshake0),
ConnectionStates = save_verify_data(Role, Finished, ConnectionStates0, StateName),
@@ -1657,7 +1779,7 @@ master_secret(#alert{} = Alert, _) ->
master_secret(PremasterSecret, #state{session = Session,
negotiated_version = Version, role = Role,
connection_states = ConnectionStates0} = State) ->
- case ssl_handshake:master_secret(tls_record, Version, PremasterSecret,
+ case ssl_handshake:master_secret(ssl:tls_version(Version), PremasterSecret,
ConnectionStates0, Role) of
{MasterSecret, ConnectionStates} ->
State#state{
@@ -1724,11 +1846,6 @@ cipher_role(server, Data, Session, #state{connection_states = ConnectionStates0
{Record, State} = prepare_connection(State1, Connection),
Connection:next_event(connection, Record, State).
-select_curve(#state{client_ecc = {[Curve|_], _}}) ->
- {namedCurve, Curve};
-select_curve(_) ->
- {namedCurve, ?secp256r1}.
-
is_anonymous(Algo) when Algo == dh_anon;
Algo == ecdh_anon;
Algo == psk;
@@ -1740,11 +1857,11 @@ is_anonymous(_) ->
false.
get_current_prf(CStates, Direction) ->
- CS = ssl_record:current_connection_state(CStates, Direction),
- CS#connection_state.security_parameters#security_parameters.prf_algorithm.
+ #{security_parameters := SecParams} = ssl_record:current_connection_state(CStates, Direction),
+ SecParams#security_parameters.prf_algorithm.
get_pending_prf(CStates, Direction) ->
- CS = ssl_record:pending_connection_state(CStates, Direction),
- CS#connection_state.security_parameters#security_parameters.prf_algorithm.
+ #{security_parameters := SecParams} = ssl_record:pending_connection_state(CStates, Direction),
+ SecParams#security_parameters.prf_algorithm.
opposite_role(client) ->
server;
@@ -1966,7 +2083,7 @@ handle_resumed_session(SessId, #state{connection_states = ConnectionStates0,
session_cache = Cache,
session_cache_cb = CacheCb} = State0) ->
Session = CacheCb:lookup(Cache, {{Host, Port}, SessId}),
- case ssl_handshake:master_secret(tls_record, Version, Session,
+ case ssl_handshake:master_secret(ssl:tls_version(Version), Session,
ConnectionStates0, client) of
{_, ConnectionStates} ->
{Record, State} =
@@ -1975,7 +2092,7 @@ handle_resumed_session(SessId, #state{connection_states = ConnectionStates0,
session = Session}),
Connection:next_event(abbreviated, Record, State);
#alert{} = Alert ->
- Connection:handle_own_alert(Alert, Version, hello, State0)
+ handle_own_alert(Alert, Version, hello, State0)
end.
make_premaster_secret({MajVer, MinVer}, rsa) ->
@@ -2042,7 +2159,7 @@ handle_active_option(_, StateName, To, Reply, #state{user_data_buffer = <<>>} =
%% user_data_buffer =/= <<>>
handle_active_option(_, StateName0, To, Reply, #state{protocol_cb = Connection} = State0) ->
- case Connection:read_application_data(<<>>, State0) of
+ case read_application_data(<<>>, State0) of
{stop, Reason, State} ->
{stop, Reason, State};
{Record, State1} ->
@@ -2056,3 +2173,264 @@ handle_active_option(_, StateName0, To, Reply, #state{protocol_cb = Connection}
Stop
end
end.
+
+encode_packet(Data, #socket_options{packet=Packet}) ->
+ case Packet of
+ 1 -> encode_size_packet(Data, 8, (1 bsl 8) - 1);
+ 2 -> encode_size_packet(Data, 16, (1 bsl 16) - 1);
+ 4 -> encode_size_packet(Data, 32, (1 bsl 32) - 1);
+ _ -> Data
+ end.
+
+encode_size_packet(Bin, Size, Max) ->
+ Len = erlang:byte_size(Bin),
+ case Len > Max of
+ true -> throw({error, {badarg, {packet_to_large, Len, Max}}});
+ false -> <<Len:Size, Bin/binary>>
+ end.
+
+time_to_renegotiate(_Data,
+ #{current_write := #{sequence_number := Num}},
+ RenegotiateAt) ->
+
+ %% We could do test:
+ %% is_time_to_renegotiate((erlang:byte_size(_Data) div ?MAX_PLAIN_TEXT_LENGTH) + 1, RenegotiateAt),
+ %% but we chose to have a some what lower renegotiateAt and a much cheaper test
+ is_time_to_renegotiate(Num, RenegotiateAt).
+
+is_time_to_renegotiate(N, M) when N < M->
+ false;
+is_time_to_renegotiate(_,_) ->
+ true.
+
+
+%% Picks ClientData
+get_data(_, _, <<>>) ->
+ {more, <<>>};
+%% Recv timed out save buffer data until next recv
+get_data(#socket_options{active=false}, undefined, Buffer) ->
+ {passive, Buffer};
+get_data(#socket_options{active=Active, packet=Raw}, BytesToRead, Buffer)
+ when Raw =:= raw; Raw =:= 0 -> %% Raw Mode
+ if
+ Active =/= false orelse BytesToRead =:= 0 ->
+ %% Active true or once, or passive mode recv(0)
+ {ok, Buffer, <<>>};
+ byte_size(Buffer) >= BytesToRead ->
+ %% Passive Mode, recv(Bytes)
+ <<Data:BytesToRead/binary, Rest/binary>> = Buffer,
+ {ok, Data, Rest};
+ true ->
+ %% Passive Mode not enough data
+ {more, Buffer}
+ end;
+get_data(#socket_options{packet=Type, packet_size=Size}, _, Buffer) ->
+ PacketOpts = [{packet_size, Size}],
+ case decode_packet(Type, Buffer, PacketOpts) of
+ {more, _} ->
+ {more, Buffer};
+ Decoded ->
+ Decoded
+ end.
+
+decode_packet({http, headers}, Buffer, PacketOpts) ->
+ decode_packet(httph, Buffer, PacketOpts);
+decode_packet({http_bin, headers}, Buffer, PacketOpts) ->
+ decode_packet(httph_bin, Buffer, PacketOpts);
+decode_packet(Type, Buffer, PacketOpts) ->
+ erlang:decode_packet(Type, Buffer, PacketOpts).
+
+%% Just like with gen_tcp sockets, an ssl socket that has been configured with
+%% {packet, http} (or {packet, http_bin}) will automatically switch to expect
+%% HTTP headers after it sees a HTTP Request or HTTP Response line. We
+%% represent the current state as follows:
+%% #socket_options.packet =:= http: Expect a HTTP Request/Response line
+%% #socket_options.packet =:= {http, headers}: Expect HTTP Headers
+%% Note that if the user has explicitly configured the socket to expect
+%% HTTP headers using the {packet, httph} option, we don't do any automatic
+%% switching of states.
+deliver_app_data(Transport, Socket, SOpts = #socket_options{active=Active, packet=Type},
+ Data, Pid, From, Tracker, Connection) ->
+ send_or_reply(Active, Pid, From, format_reply(Transport, Socket, SOpts, Data, Tracker, Connection)),
+ SO = case Data of
+ {P, _, _, _} when ((P =:= http_request) or (P =:= http_response)),
+ ((Type =:= http) or (Type =:= http_bin)) ->
+ SOpts#socket_options{packet={Type, headers}};
+ http_eoh when tuple_size(Type) =:= 2 ->
+ % End of headers - expect another Request/Response line
+ {Type1, headers} = Type,
+ SOpts#socket_options{packet=Type1};
+ _ ->
+ SOpts
+ end,
+ case Active of
+ once ->
+ SO#socket_options{active=false};
+ _ ->
+ SO
+ end.
+
+format_reply(_, _,#socket_options{active = false, mode = Mode, packet = Packet,
+ header = Header}, Data, _, _) ->
+ {ok, do_format_reply(Mode, Packet, Header, Data)};
+format_reply(Transport, Socket, #socket_options{active = _, mode = Mode, packet = Packet,
+ header = Header}, Data, Tracker, Connection) ->
+ {ssl, ssl_socket:socket(self(), Transport, Socket, Connection, Tracker),
+ do_format_reply(Mode, Packet, Header, Data)}.
+
+deliver_packet_error(Transport, Socket, SO= #socket_options{active = Active}, Data, Pid, From, Tracker, Connection) ->
+ send_or_reply(Active, Pid, From, format_packet_error(Transport, Socket, SO, Data, Tracker, Connection)).
+
+format_packet_error(_, _,#socket_options{active = false, mode = Mode}, Data, _, _) ->
+ {error, {invalid_packet, do_format_reply(Mode, raw, 0, Data)}};
+format_packet_error(Transport, Socket, #socket_options{active = _, mode = Mode}, Data, Tracker, Connection) ->
+ {ssl_error, ssl_socket:socket(self(), Transport, Socket, Connection, Tracker),
+ {invalid_packet, do_format_reply(Mode, raw, 0, Data)}}.
+
+do_format_reply(binary, _, N, Data) when N > 0 -> % Header mode
+ header(N, Data);
+do_format_reply(binary, _, _, Data) ->
+ Data;
+do_format_reply(list, Packet, _, Data)
+ when Packet == http; Packet == {http, headers};
+ Packet == http_bin; Packet == {http_bin, headers};
+ Packet == httph; Packet == httph_bin ->
+ Data;
+do_format_reply(list, _,_, Data) ->
+ binary_to_list(Data).
+
+header(0, <<>>) ->
+ <<>>;
+header(_, <<>>) ->
+ [];
+header(0, Binary) ->
+ Binary;
+header(N, Binary) ->
+ <<?BYTE(ByteN), NewBinary/binary>> = Binary,
+ [ByteN | header(N-1, NewBinary)].
+
+send_or_reply(false, _Pid, From, Data) when From =/= undefined ->
+ gen_statem:reply(From, Data);
+%% Can happen when handling own alert or tcp error/close and there is
+%% no outstanding gen_fsm sync events
+send_or_reply(false, no_pid, _, _) ->
+ ok;
+send_or_reply(_, Pid, _From, Data) ->
+ send_user(Pid, Data).
+
+send_user(Pid, Msg) ->
+ Pid ! Msg.
+
+alert_user(Transport, Tracker, Socket, connection, Opts, Pid, From, Alert, Role, Connection) ->
+ alert_user(Transport, Tracker, Socket, Opts#socket_options.active, Pid, From, Alert, Role, Connection);
+alert_user(Transport, Tracker, Socket,_, _, _, From, Alert, Role, Connection) ->
+ alert_user(Transport, Tracker, Socket, From, Alert, Role, Connection).
+
+alert_user(Transport, Tracker, Socket, From, Alert, Role, Connection) ->
+ alert_user(Transport, Tracker, Socket, false, no_pid, From, Alert, Role, Connection).
+
+alert_user(_, _, _, false = Active, Pid, From, Alert, Role, _) when From =/= undefined ->
+ %% If there is an outstanding ssl_accept | recv
+ %% From will be defined and send_or_reply will
+ %% send the appropriate error message.
+ ReasonCode = ssl_alert:reason_code(Alert, Role),
+ send_or_reply(Active, Pid, From, {error, ReasonCode});
+alert_user(Transport, Tracker, Socket, Active, Pid, From, Alert, Role, Connection) ->
+ case ssl_alert:reason_code(Alert, Role) of
+ closed ->
+ send_or_reply(Active, Pid, From,
+ {ssl_closed, ssl_socket:socket(self(),
+ Transport, Socket, Connection, Tracker)});
+ ReasonCode ->
+ send_or_reply(Active, Pid, From,
+ {ssl_error, ssl_socket:socket(self(),
+ Transport, Socket, Connection, Tracker), ReasonCode})
+ end.
+
+log_alert(true, Info, Alert) ->
+ Txt = ssl_alert:alert_txt(Alert),
+ error_logger:format("SSL: ~p: ~s\n", [Info, Txt]);
+log_alert(false, _, _) ->
+ ok.
+
+handle_own_alert(Alert, Version, StateName,
+ #state{transport_cb = Transport,
+ socket = Socket,
+ connection_states = ConnectionStates,
+ ssl_options = SslOpts} = State) ->
+ try %% Try to tell the other side
+ {BinMsg, _} =
+ ssl_alert:encode(Alert, Version, ConnectionStates),
+ Transport:send(Socket, BinMsg)
+ catch _:_ -> %% Can crash if we are in a uninitialized state
+ ignore
+ end,
+ try %% Try to tell the local user
+ log_alert(SslOpts#ssl_options.log_alert, StateName, Alert),
+ handle_normal_shutdown(Alert,StateName, State)
+ catch _:_ ->
+ ok
+ end,
+ {stop, {shutdown, own_alert}}.
+
+handle_normal_shutdown(Alert, _, #state{socket = Socket,
+ transport_cb = Transport,
+ protocol_cb = Connection,
+ start_or_recv_from = StartFrom,
+ tracker = Tracker,
+ role = Role, renegotiation = {false, first}}) ->
+ alert_user(Transport, Tracker,Socket, StartFrom, Alert, Role, Connection);
+
+handle_normal_shutdown(Alert, StateName, #state{socket = Socket,
+ socket_options = Opts,
+ transport_cb = Transport,
+ protocol_cb = Connection,
+ user_application = {_Mon, Pid},
+ tracker = Tracker,
+ start_or_recv_from = RecvFrom, role = Role}) ->
+ alert_user(Transport, Tracker, Socket, StateName, Opts, Pid, RecvFrom, Alert, Role, Connection).
+
+invalidate_session(client, Host, Port, Session) ->
+ ssl_manager:invalidate_session(Host, Port, Session);
+invalidate_session(server, _, Port, Session) ->
+ ssl_manager:invalidate_session(Port, Session).
+
+handle_sni_extension(undefined, State) ->
+ State;
+handle_sni_extension(#sni{hostname = Hostname}, State0) ->
+ NewOptions = update_ssl_options_from_sni(State0#state.ssl_options, Hostname),
+ case NewOptions of
+ undefined ->
+ State0;
+ _ ->
+ {ok, Ref, CertDbHandle, FileRefHandle, CacheHandle, CRLDbHandle, OwnCert, Key, DHParams} =
+ ssl_config:init(NewOptions, State0#state.role),
+ State0#state{
+ session = State0#state.session#session{own_certificate = OwnCert},
+ file_ref_db = FileRefHandle,
+ cert_db_ref = Ref,
+ cert_db = CertDbHandle,
+ crl_db = CRLDbHandle,
+ session_cache = CacheHandle,
+ private_key = Key,
+ diffie_hellman_params = DHParams,
+ ssl_options = NewOptions,
+ sni_hostname = Hostname
+ }
+ end.
+
+update_ssl_options_from_sni(OrigSSLOptions, SNIHostname) ->
+ SSLOption =
+ case OrigSSLOptions#ssl_options.sni_fun of
+ undefined ->
+ proplists:get_value(SNIHostname,
+ OrigSSLOptions#ssl_options.sni_hosts);
+ SNIFun ->
+ SNIFun(SNIHostname)
+ end,
+ case SSLOption of
+ undefined ->
+ undefined;
+ _ ->
+ ssl:handle_options(SSLOption, OrigSSLOptions)
+ end.
diff --git a/lib/ssl/src/ssl_connection.hrl b/lib/ssl/src/ssl_connection.hrl
index 4b54943ddf..fca3e11894 100644
--- a/lib/ssl/src/ssl_connection.hrl
+++ b/lib/ssl/src/ssl_connection.hrl
@@ -46,8 +46,9 @@
socket :: port(),
ssl_options :: #ssl_options{},
socket_options :: #socket_options{},
- connection_states :: #connection_states{} | secret_printout(),
+ connection_states :: ssl_record:connection_states() | secret_printout(),
protocol_buffers :: term() | secret_printout() , %% #protocol_buffers{} from tls_record.hrl or dtls_recor.hrl
+ unprocessed_handshake_events = 0 :: integer(),
tls_handshake_history :: ssl_handshake:ssl_handshake_history() | secret_printout()
| 'undefined',
cert_db :: reference() | 'undefined',
@@ -81,7 +82,6 @@
expecting_next_protocol_negotiation = false ::boolean(),
expecting_finished = false ::boolean(),
negotiated_protocol = undefined :: undefined | binary(),
- client_ecc, % {Curves, PointFmt}
tracker :: pid() | 'undefined', %% Tracker process for listen socket
sni_hostname = undefined,
downgrade,
diff --git a/lib/ssl/src/ssl_crl.erl b/lib/ssl/src/ssl_crl.erl
index d9f21e04ac..01be1fb9ab 100644
--- a/lib/ssl/src/ssl_crl.erl
+++ b/lib/ssl/src/ssl_crl.erl
@@ -47,7 +47,7 @@ trusted_cert_and_path(CRL, issuer_not_found, {Db, DbRef} = DbHandle) ->
{ok, unknown_crl_ca, []}
end.
-find_issuer(CRL, {Db,_}) ->
+find_issuer(CRL, {Db,DbRef}) ->
Issuer = public_key:pkix_normalize_name(public_key:pkix_crl_issuer(CRL)),
IsIssuerFun =
fun({_Key, {_Der,ErlCertCandidate}}, Acc) ->
@@ -55,15 +55,27 @@ find_issuer(CRL, {Db,_}) ->
(_, Acc) ->
Acc
end,
-
- try ssl_pkix_db:foldl(IsIssuerFun, issuer_not_found, Db) of
- issuer_not_found ->
- {error, issuer_not_found}
- catch
- {ok, _} = Result ->
- Result
+ if is_reference(DbRef) -> % actual DB exists
+ try ssl_pkix_db:foldl(IsIssuerFun, issuer_not_found, Db) of
+ issuer_not_found ->
+ {error, issuer_not_found}
+ catch
+ {ok, _} = Result ->
+ Result
+ end;
+ is_tuple(DbRef), element(1,DbRef) =:= extracted -> % cache bypass byproduct
+ {extracted, CertsData} = DbRef,
+ Certs = [Entry || {decoded, Entry} <- CertsData],
+ try lists:foldl(IsIssuerFun, issuer_not_found, Certs) of
+ issuer_not_found ->
+ {error, issuer_not_found}
+ catch
+ {ok, _} = Result ->
+ Result
+ end
end.
+
verify_crl_issuer(CRL, ErlCertCandidate, Issuer, NotIssuer) ->
TBSCert = ErlCertCandidate#'OTPCertificate'.tbsCertificate,
case public_key:pkix_normalize_name(TBSCert#'OTPTBSCertificate'.subject) of
diff --git a/lib/ssl/src/ssl_handshake.erl b/lib/ssl/src/ssl_handshake.erl
index bca341c8bc..5b51ac0916 100644
--- a/lib/ssl/src/ssl_handshake.erl
+++ b/lib/ssl/src/ssl_handshake.erl
@@ -51,8 +51,8 @@
%% Handle handshake messages
-export([certify/10, client_certificate_verify/6, certificate_verify/6, verify_signature/5,
- master_secret/5, server_key_exchange_hash/2, verify_connection/6,
- init_handshake_history/0, update_handshake_history/2, verify_server_key/5
+ master_secret/4, server_key_exchange_hash/2, verify_connection/6,
+ init_handshake_history/0, update_handshake_history/3, verify_server_key/5
]).
%% Encode/Decode
@@ -94,15 +94,14 @@ hello_request() ->
#hello_request{}.
%%--------------------------------------------------------------------
--spec server_hello(#session{}, ssl_record:ssl_version(), #connection_states{},
+-spec server_hello(#session{}, ssl_record:ssl_version(), ssl_record:connection_states(),
#hello_extensions{}) -> #server_hello{}.
%%
%% Description: Creates a server hello message.
%%--------------------------------------------------------------------
server_hello(SessionId, Version, ConnectionStates, Extensions) ->
- Pending = ssl_record:pending_connection_state(ConnectionStates, read),
- SecParams = Pending#connection_state.security_parameters,
-
+ #{security_parameters := SecParams} =
+ ssl_record:pending_connection_state(ConnectionStates, read),
#server_hello{server_version = Version,
cipher_suite = SecParams#security_parameters.cipher_suite,
compression_method =
@@ -335,9 +334,8 @@ verify_server_key(#server_key_params{params_bin = EncParams,
signature = Signature},
HashSign = {HashAlgo, _},
ConnectionStates, Version, PubKeyInfo) ->
- ConnectionState =
+ #{security_parameters := SecParams} =
ssl_record:pending_connection_state(ConnectionStates, read),
- SecParams = ConnectionState#connection_state.security_parameters,
#security_parameters{client_random = ClientRandom,
server_random = ServerRandom} = SecParams,
Hash = server_key_exchange_hash(HashAlgo,
@@ -447,7 +445,7 @@ init_handshake_history() ->
{[], []}.
%%--------------------------------------------------------------------
--spec update_handshake_history(ssl_handshake:ssl_handshake_history(), Data ::term()) ->
+-spec update_handshake_history(ssl_handshake:ssl_handshake_history(), Data ::term(), boolean()) ->
ssl_handshake:ssl_handshake_history().
%%
%% Description: Update the handshake history buffer with Data.
@@ -457,14 +455,14 @@ update_handshake_history(Handshake, % special-case SSL2 client hello
?UINT16(CSLength), ?UINT16(0),
?UINT16(CDLength),
CipherSuites:CSLength/binary,
- ChallengeData:CDLength/binary>>) ->
+ ChallengeData:CDLength/binary>>, true) ->
update_handshake_history(Handshake,
<<?CLIENT_HELLO, ?BYTE(Major), ?BYTE(Minor),
?UINT16(CSLength), ?UINT16(0),
?UINT16(CDLength),
CipherSuites:CSLength/binary,
- ChallengeData:CDLength/binary>>);
-update_handshake_history({Handshake0, _Prev}, Data) ->
+ ChallengeData:CDLength/binary>>, true);
+update_handshake_history({Handshake0, _Prev}, Data, _) ->
{[Data|Handshake0], Handshake0}.
%% %%--------------------------------------------------------------------
@@ -658,7 +656,7 @@ select_hashsign(#certificate_request{hashsign_algorithms = #hash_sign_algos{hash
?ALERT_REC(?FATAL, ?INSUFFICIENT_SECURITY, no_suitable_signature_algorithm)
end;
select_hashsign(#certificate_request{}, Cert, _, Version) ->
- select_hashsign(undefined, Cert, undefined, undefined, Version).
+ select_hashsign(undefined, Cert, undefined, [], Version).
%%--------------------------------------------------------------------
-spec select_hashsign_algs({atom(), atom()}| undefined, oid(), ssl_record:ssl_version()) ->
@@ -696,33 +694,32 @@ select_hashsign_algs(undefined, ?'id-dsa', _) ->
%%--------------------------------------------------------------------
--spec master_secret(atom(), ssl_record:ssl_version(), #session{} | binary(), #connection_states{},
- client | server) -> {binary(), #connection_states{}} | #alert{}.
+-spec master_secret(ssl_record:ssl_version(), #session{} | binary(), ssl_record:connection_states(),
+ client | server) -> {binary(), ssl_record:connection_states()} | #alert{}.
%%
%% Description: Sets or calculates the master secret and calculate keys,
%% updating the pending connection states. The Mastersecret and the update
%% connection states are returned or an alert if the calculation fails.
%%-------------------------------------------------------------------
-master_secret(RecordCB, Version, #session{master_secret = Mastersecret},
+master_secret(Version, #session{master_secret = Mastersecret},
ConnectionStates, Role) ->
- ConnectionState =
+ #{security_parameters := SecParams} =
ssl_record:pending_connection_state(ConnectionStates, read),
- SecParams = ConnectionState#connection_state.security_parameters,
- try master_secret(RecordCB, Version, Mastersecret, SecParams,
+ try master_secret(Version, Mastersecret, SecParams,
ConnectionStates, Role)
catch
exit:_ ->
?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE, key_calculation_failure)
end;
-master_secret(RecordCB, Version, PremasterSecret, ConnectionStates, Role) ->
- ConnectionState =
+master_secret(Version, PremasterSecret, ConnectionStates, Role) ->
+ #{security_parameters := SecParams} =
ssl_record:pending_connection_state(ConnectionStates, read),
- SecParams = ConnectionState#connection_state.security_parameters,
+
#security_parameters{prf_algorithm = PrfAlgo,
client_random = ClientRandom,
server_random = ServerRandom} = SecParams,
- try master_secret(RecordCB, Version,
+ try master_secret(Version,
calc_master_secret(Version,PrfAlgo,PremasterSecret,
ClientRandom, ServerRandom),
SecParams, ConnectionStates, Role)
@@ -1219,13 +1216,18 @@ certificate_authorities(CertDbHandle, CertDbRef) ->
end,
list_to_binary([Enc(Cert) || {_, Cert} <- Authorities]).
-certificate_authorities_from_db(CertDbHandle, CertDbRef) ->
+certificate_authorities_from_db(CertDbHandle, CertDbRef) when is_reference(CertDbRef) ->
ConnectionCerts = fun({{Ref, _, _}, Cert}, Acc) when Ref == CertDbRef ->
[Cert | Acc];
(_, Acc) ->
Acc
end,
- ssl_pkix_db:foldl(ConnectionCerts, [], CertDbHandle).
+ ssl_pkix_db:foldl(ConnectionCerts, [], CertDbHandle);
+certificate_authorities_from_db(_CertDbHandle, {extracted, CertDbData}) ->
+ %% Cache disabled, Ref contains data
+ lists:foldl(fun({decoded, {_Key,Cert}}, Acc) -> [Cert | Acc] end,
+ [], CertDbData).
+
%%-------------Extension handling --------------------------------
@@ -1305,35 +1307,67 @@ handle_server_hello_extensions(RecordCB, Random, CipherSuite, Compression,
end.
select_version(RecordCB, ClientVersion, Versions) ->
- ServerVersion = RecordCB:highest_protocol_version(Versions),
- RecordCB:lowest_protocol_version(ClientVersion, ServerVersion).
+ do_select_version(RecordCB, ClientVersion, Versions).
+
+do_select_version(_, ClientVersion, []) ->
+ ClientVersion;
+do_select_version(RecordCB, ClientVersion, [Version | Versions]) ->
+ case RecordCB:is_higher(Version, ClientVersion) of
+ true ->
+ %% Version too high for client - keep looking
+ do_select_version(RecordCB, ClientVersion, Versions);
+ false ->
+ %% Version ok for client - look for a higher
+ do_select_version(RecordCB, ClientVersion, Versions, Version)
+ end.
+%%
+do_select_version(_, _, [], GoodVersion) ->
+ GoodVersion;
+do_select_version(
+ RecordCB, ClientVersion, [Version | Versions], GoodVersion) ->
+ BetterVersion =
+ case RecordCB:is_higher(Version, ClientVersion) of
+ true ->
+ %% Version too high for client
+ GoodVersion;
+ false ->
+ %% Version ok for client
+ case RecordCB:is_higher(Version, GoodVersion) of
+ true ->
+ %% Use higher version
+ Version;
+ false ->
+ GoodVersion
+ end
+ end,
+ do_select_version(RecordCB, ClientVersion, Versions, BetterVersion).
renegotiation_info(_, client, _, false) ->
#renegotiation_info{renegotiated_connection = undefined};
renegotiation_info(_RecordCB, server, ConnectionStates, false) ->
- CS = ssl_record:current_connection_state(ConnectionStates, read),
- case CS#connection_state.secure_renegotiation of
+ ConnectionState = ssl_record:current_connection_state(ConnectionStates, read),
+ case maps:get(secure_renegotiation, ConnectionState) of
true ->
#renegotiation_info{renegotiated_connection = ?byte(0)};
false ->
#renegotiation_info{renegotiated_connection = undefined}
end;
renegotiation_info(_RecordCB, client, ConnectionStates, true) ->
- CS = ssl_record:current_connection_state(ConnectionStates, read),
- case CS#connection_state.secure_renegotiation of
+ ConnectionState = ssl_record:current_connection_state(ConnectionStates, read),
+ case maps:get(secure_renegotiation, ConnectionState) of
true ->
- Data = CS#connection_state.client_verify_data,
+ Data = maps:get(client_verify_data, ConnectionState),
#renegotiation_info{renegotiated_connection = Data};
false ->
#renegotiation_info{renegotiated_connection = undefined}
end;
renegotiation_info(_RecordCB, server, ConnectionStates, true) ->
- CS = ssl_record:current_connection_state(ConnectionStates, read),
- case CS#connection_state.secure_renegotiation of
+ ConnectionState = ssl_record:current_connection_state(ConnectionStates, read),
+ case maps:get(secure_renegotiation, ConnectionState) of
true ->
- CData = CS#connection_state.client_verify_data,
- SData =CS#connection_state.server_verify_data,
+ CData = maps:get(client_verify_data, ConnectionState),
+ SData = maps:get(server_verify_data, ConnectionState),
#renegotiation_info{renegotiated_connection = <<CData/binary, SData/binary>>};
false ->
#renegotiation_info{renegotiated_connection = undefined}
@@ -1356,9 +1390,9 @@ handle_renegotiation_info(_RecordCB, _, undefined, ConnectionStates, false, _, _
handle_renegotiation_info(_RecordCB, client, #renegotiation_info{renegotiated_connection = ClientServerVerify},
ConnectionStates, true, _, _) ->
- CS = ssl_record:current_connection_state(ConnectionStates, read),
- CData = CS#connection_state.client_verify_data,
- SData = CS#connection_state.server_verify_data,
+ ConnectionState = ssl_record:current_connection_state(ConnectionStates, read),
+ CData = maps:get(client_verify_data, ConnectionState),
+ SData = maps:get(server_verify_data, ConnectionState),
case <<CData/binary, SData/binary>> == ClientServerVerify of
true ->
{ok, ConnectionStates};
@@ -1372,8 +1406,8 @@ handle_renegotiation_info(_RecordCB, server, #renegotiation_info{renegotiated_co
true ->
?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE, {server_renegotiation, empty_renegotiation_info_scsv});
false ->
- CS = ssl_record:current_connection_state(ConnectionStates, read),
- Data = CS#connection_state.client_verify_data,
+ ConnectionState = ssl_record:current_connection_state(ConnectionStates, read),
+ Data = maps:get(client_verify_data, ConnectionState),
case Data == ClientVerify of
true ->
{ok, ConnectionStates};
@@ -1394,8 +1428,8 @@ handle_renegotiation_info(RecordCB, server, undefined, ConnectionStates, true, S
end.
handle_renegotiation_info(_RecordCB, ConnectionStates, SecureRenegotation) ->
- CS = ssl_record:current_connection_state(ConnectionStates, read),
- case {SecureRenegotation, CS#connection_state.secure_renegotiation} of
+ ConnectionState = ssl_record:current_connection_state(ConnectionStates, read),
+ case {SecureRenegotation, maps:get(secure_renegotiation, ConnectionState)} of
{_, true} ->
?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE, already_secure);
{true, false} ->
@@ -1613,7 +1647,7 @@ calc_finished({3, 0}, Role, _PrfAlgo, MasterSecret, Handshake) ->
calc_finished({3, N}, Role, PrfAlgo, MasterSecret, Handshake) ->
tls_v1:finished(Role, N, PrfAlgo, MasterSecret, lists:reverse(Handshake)).
-master_secret(_RecordCB, Version, MasterSecret,
+master_secret(Version, MasterSecret,
#security_parameters{
bulk_cipher_algorithm = BCA,
client_random = ClientRandom,
@@ -1696,18 +1730,16 @@ hello_pending_connection_states(_RecordCB, Role, Version, CipherSuite, Random, C
NewWriteSecParams,
ConnectionStates).
-hello_security_parameters(client, Version, ConnectionState, CipherSuite, Random,
+hello_security_parameters(client, Version, #{security_parameters := SecParams}, CipherSuite, Random,
Compression) ->
- SecParams = ConnectionState#connection_state.security_parameters,
NewSecParams = ssl_cipher:security_parameters(Version, CipherSuite, SecParams),
NewSecParams#security_parameters{
server_random = Random,
compression_algorithm = Compression
};
-hello_security_parameters(server, Version, ConnectionState, CipherSuite, Random,
+hello_security_parameters(server, Version, #{security_parameters := SecParams}, CipherSuite, Random,
Compression) ->
- SecParams = ConnectionState#connection_state.security_parameters,
NewSecParams = ssl_cipher:security_parameters(Version, CipherSuite, SecParams),
NewSecParams#security_parameters{
client_random = Random,
diff --git a/lib/ssl/src/ssl_manager.erl b/lib/ssl/src/ssl_manager.erl
index c7dcbaabe9..5bd9521de7 100644
--- a/lib/ssl/src/ssl_manager.erl
+++ b/lib/ssl/src/ssl_manager.erl
@@ -115,13 +115,25 @@ start_link_dist(Opts) ->
%% Description: Do necessary initializations for a new connection.
%%--------------------------------------------------------------------
connection_init({der, _} = Trustedcerts, Role, CRLCache) ->
- call({connection_init, Trustedcerts, Role, CRLCache});
+ case bypass_pem_cache() of
+ true ->
+ {ok, Extracted} = ssl_pkix_db:extract_trusted_certs(Trustedcerts),
+ call({connection_init, Extracted, Role, CRLCache});
+ false ->
+ call({connection_init, Trustedcerts, Role, CRLCache})
+ end;
connection_init(<<>> = Trustedcerts, Role, CRLCache) ->
call({connection_init, Trustedcerts, Role, CRLCache});
connection_init(Trustedcerts, Role, CRLCache) ->
- call({connection_init, Trustedcerts, Role, CRLCache}).
+ case bypass_pem_cache() of
+ true ->
+ {ok, Extracted} = ssl_pkix_db:extract_trusted_certs(Trustedcerts),
+ call({connection_init, Extracted, Role, CRLCache});
+ false ->
+ call({connection_init, Trustedcerts, Role, CRLCache})
+ end.
%%--------------------------------------------------------------------
-spec cache_pem_file(binary(), term()) -> {ok, term()} | {error, reason()}.
@@ -129,13 +141,18 @@ connection_init(Trustedcerts, Role, CRLCache) ->
%% Description: Cache a pem file and return its content.
%%--------------------------------------------------------------------
cache_pem_file(File, DbHandle) ->
- case ssl_pkix_db:lookup_cached_pem(DbHandle, File) of
- [{Content,_}] ->
- {ok, Content};
- [Content] ->
- {ok, Content};
- undefined ->
- call({cache_pem, File})
+ case bypass_pem_cache() of
+ true ->
+ ssl_pkix_db:decode_pem_file(File);
+ false ->
+ case ssl_pkix_db:lookup_cached_pem(DbHandle, File) of
+ [{Content,_}] ->
+ {ok, Content};
+ [Content] ->
+ {ok, Content};
+ undefined ->
+ call({cache_pem, File})
+ end
end.
%%--------------------------------------------------------------------
@@ -506,6 +523,14 @@ delay_time() ->
?CLEAN_SESSION_DB
end.
+bypass_pem_cache() ->
+ case application:get_env(ssl, bypass_pem_cache) of
+ {ok, Bool} when is_boolean(Bool) ->
+ Bool;
+ _ ->
+ false
+ end.
+
max_session_cache_size(CacheType) ->
case application:get_env(ssl, CacheType) of
{ok, Size} when is_integer(Size) ->
diff --git a/lib/ssl/src/ssl_pkix_db.erl b/lib/ssl/src/ssl_pkix_db.erl
index b16903d7c7..0006ce14d9 100644
--- a/lib/ssl/src/ssl_pkix_db.erl
+++ b/lib/ssl/src/ssl_pkix_db.erl
@@ -29,10 +29,11 @@
-include_lib("kernel/include/file.hrl").
-export([create/0, add_crls/3, remove_crls/2, remove/1, add_trusted_certs/3,
+ extract_trusted_certs/1,
remove_trusted_certs/2, insert/3, remove/2, clear/1, db_size/1,
ref_count/3, lookup_trusted_cert/4, foldl/3, select_cert_by_issuer/2,
lookup_cached_pem/2, cache_pem_file/2, cache_pem_file/3,
- lookup/2]).
+ decode_pem_file/1, lookup/2]).
%%====================================================================
%% Internal application API
@@ -82,12 +83,22 @@ remove(Dbs) ->
%% <SerialNumber, Issuer>. Ref is used as it is specified
%% for each connection which certificates are trusted.
%%--------------------------------------------------------------------
-lookup_trusted_cert(DbHandle, Ref, SerialNumber, Issuer) ->
+lookup_trusted_cert(DbHandle, Ref, SerialNumber, Issuer) when is_reference(Ref) ->
case lookup({Ref, SerialNumber, Issuer}, DbHandle) of
undefined ->
undefined;
[Certs] ->
{ok, Certs}
+ end;
+lookup_trusted_cert(_DbHandle, {extracted,Certs}, SerialNumber, Issuer) ->
+ try
+ [throw(Cert)
+ || {decoded, {{_Ref,CertSerial,CertIssuer}, Cert}} <- Certs,
+ CertSerial =:= SerialNumber, CertIssuer =:= Issuer],
+ undefined
+ catch
+ Cert ->
+ {ok, Cert}
end.
lookup_cached_pem([_, _, PemChache | _], File) ->
@@ -103,6 +114,9 @@ lookup_cached_pem(PemChache, File) ->
%% runtime database. Returns Ref that should be handed to lookup_trusted_cert
%% together with the cert serialnumber and issuer.
%%--------------------------------------------------------------------
+add_trusted_certs(_Pid, {extracted, _} = Certs, _) ->
+ {ok, Certs};
+
add_trusted_certs(_Pid, {der, DerList}, [CertDb, _,_ | _]) ->
NewRef = make_ref(),
add_certs_from_der(DerList, NewRef, CertDb),
@@ -122,6 +136,21 @@ add_trusted_certs(_Pid, File, [CertsDb, RefDb, PemChache | _] = Db) ->
undefined ->
new_trusted_cert_entry(File, Db)
end.
+
+extract_trusted_certs({der, DerList}) ->
+ {ok, {extracted, certs_from_der(DerList)}};
+extract_trusted_certs(File) ->
+ case file:read_file(File) of
+ {ok, PemBin} ->
+ Content = public_key:pem_decode(PemBin),
+ DerList = [Cert || {'Certificate', Cert, not_encrypted} <- Content],
+ {ok, {extracted, certs_from_der(DerList)}};
+ Error ->
+ %% Have to simulate a failure happening in a server for
+ %% external handlers.
+ {error, {badmatch, Error}}
+ end.
+
%%--------------------------------------------------------------------
%%
%% Description: Cache file as binary in DB
@@ -141,6 +170,18 @@ cache_pem_file(Ref, File, [_CertsDb, _RefDb, PemChache| _]) ->
insert(File, {Content, Ref}, PemChache),
{ok, Content}.
+-spec decode_pem_file(binary()) -> {ok, term()}.
+decode_pem_file(File) ->
+ case file:read_file(File) of
+ {ok, PemBin} ->
+ Content = public_key:pem_decode(PemBin),
+ {ok, Content};
+ Error ->
+ %% Have to simulate a failure happening in a server for
+ %% external handlers.
+ {error, {badmatch, Error}}
+ end.
+
%%--------------------------------------------------------------------
-spec remove_trusted_certs(reference(), db_handle()) -> ok.
%%
@@ -203,6 +244,8 @@ select_cert_by_issuer(Cache, Issuer) ->
%%
%% Description: Updates a reference counter in a <Db>.
%%--------------------------------------------------------------------
+ref_count({extracted, _}, _Db, _N) ->
+ not_cached;
ref_count(Key, Db, N) ->
ets:update_counter(Db,Key,N).
@@ -248,23 +291,39 @@ add_certs_from_der(DerList, Ref, CertsDb) ->
[Add(Cert) || Cert <- DerList],
ok.
+certs_from_der(DerList) ->
+ Ref = make_ref(),
+ [Decoded || Cert <- DerList,
+ Decoded <- [decode_certs(Ref, Cert)],
+ Decoded =/= undefined].
+
add_certs_from_pem(PemEntries, Ref, CertsDb) ->
Add = fun(Cert) -> add_certs(Cert, Ref, CertsDb) end,
[Add(Cert) || {'Certificate', Cert, not_encrypted} <- PemEntries],
ok.
add_certs(Cert, Ref, CertsDb) ->
+ try
+ {decoded, {Key, Val}} = decode_certs(Ref, Cert),
+ insert(Key, Val, CertsDb)
+ catch
+ error:_ ->
+ ok
+ end.
+
+decode_certs(Ref, Cert) ->
try ErlCert = public_key:pkix_decode_cert(Cert, otp),
TBSCertificate = ErlCert#'OTPCertificate'.tbsCertificate,
SerialNumber = TBSCertificate#'OTPTBSCertificate'.serialNumber,
Issuer = public_key:pkix_normalize_name(
TBSCertificate#'OTPTBSCertificate'.issuer),
- insert({Ref, SerialNumber, Issuer}, {Cert,ErlCert}, CertsDb)
+ {decoded, {{Ref, SerialNumber, Issuer}, {Cert, ErlCert}}}
catch
error:_ ->
Report = io_lib:format("SSL WARNING: Ignoring a CA cert as "
"it could not be correctly decoded.~n", []),
- error_logger:info_report(Report)
+ error_logger:info_report(Report),
+ undefined
end.
new_trusted_cert_entry(File, [CertsDb, RefDb, _ | _] = Db) ->
diff --git a/lib/ssl/src/ssl_record.erl b/lib/ssl/src/ssl_record.erl
index 5bb1c92c2d..71cd0279f3 100644
--- a/lib/ssl/src/ssl_record.erl
+++ b/lib/ssl/src/ssl_record.erl
@@ -30,8 +30,7 @@
-include("ssl_alert.hrl").
%% Connection state handling
--export([init_connection_states/2,
- current_connection_state/2, pending_connection_state/2,
+-export([initial_security_params/1, current_connection_state/2, pending_connection_state/2,
activate_pending_connection_state/2,
set_security_params/3,
set_mac_secret/4,
@@ -39,7 +38,8 @@
set_pending_cipher_state/4,
set_renegotiation_flag/2,
set_client_verify_data/3,
- set_server_verify_data/3]).
+ set_server_verify_data/3,
+ empty_connection_state/2, initial_connection_state/2, record_protocol_role/1]).
%% Encoding records
-export([encode_handshake/3, encode_alert_record/3,
@@ -52,122 +52,92 @@
-export([cipher/4, decipher/4, is_correct_mac/2,
cipher_aead/4, decipher_aead/4]).
--export_type([ssl_version/0, ssl_atom_version/0]).
+-export_type([ssl_version/0, ssl_atom_version/0, connection_states/0, connection_state/0]).
-type ssl_version() :: {integer(), integer()}.
-type ssl_atom_version() :: tls_record:tls_atom_version().
-
+-type connection_states() :: term(). %% Map
+-type connection_state() :: term(). %% Map
%%====================================================================
%% Internal application API
%%====================================================================
+
%%--------------------------------------------------------------------
--spec init_connection_states(client | server, one_n_minus_one | zero_n | disabled ) ->
- #connection_states{}.
-%%
-%% Description: Creates a connection_states record with appropriate
-%% values for the initial SSL connection setup.
-%%--------------------------------------------------------------------
-init_connection_states(Role, BeastMitigation) ->
- ConnectionEnd = record_protocol_role(Role),
- Current = initial_connection_state(ConnectionEnd, BeastMitigation),
- Pending = empty_connection_state(ConnectionEnd, BeastMitigation),
- #connection_states{dtls_write_msg_seq = 1, % only used by dtls
- current_read = Current,
- pending_read = Pending,
- current_write = Current,
- pending_write = Pending
- }.
-
-%%--------------------------------------------------------------------
--spec current_connection_state(#connection_states{}, read | write) ->
- #connection_state{}.
+-spec current_connection_state(connection_states(), read | write) ->
+ connection_state().
%%
-%% Description: Returns the instance of the connection_state record
+%% Description: Returns the instance of the connection_state map
%% that is currently defined as the current conection state.
%%--------------------------------------------------------------------
-current_connection_state(#connection_states{current_read = Current},
- read) ->
- Current;
-current_connection_state(#connection_states{current_write = Current},
- write) ->
- Current.
+current_connection_state(ConnectionStates, read) ->
+ maps:get(current_read, ConnectionStates);
+current_connection_state(ConnectionStates, write) ->
+ maps:get(current_write, ConnectionStates).
%%--------------------------------------------------------------------
--spec pending_connection_state(#connection_states{}, read | write) ->
- term().
+-spec pending_connection_state(connection_states(), read | write) ->
+ connection_state().
%%
-%% Description: Returns the instance of the connection_state record
-%% that is currently defined as the pending conection state.
+%% Description: Returns the instance of the connection_state map
+%% that is pendingly defined as the pending conection state.
%%--------------------------------------------------------------------
-pending_connection_state(#connection_states{pending_read = Pending},
- read) ->
- Pending;
-pending_connection_state(#connection_states{pending_write = Pending},
- write) ->
- Pending.
-
+pending_connection_state(ConnectionStates, read) ->
+ maps:get(pending_read, ConnectionStates);
+pending_connection_state(ConnectionStates, write) ->
+ maps:get(pending_write, ConnectionStates).
%%--------------------------------------------------------------------
--spec activate_pending_connection_state(#connection_states{}, read | write) ->
- #connection_states{}.
+-spec activate_pending_connection_state(connection_states(), read | write) ->
+ connection_states().
%%
%% Description: Creates a new instance of the connection_states record
%% where the pending state of <Type> has been activated.
%%--------------------------------------------------------------------
-activate_pending_connection_state(States =
- #connection_states{current_read = Current,
- pending_read = Pending},
+activate_pending_connection_state(#{current_read := Current,
+ pending_read := Pending} = States,
read) ->
- NewCurrent = Pending#connection_state{epoch = dtls_next_epoch(Current),
- sequence_number = 0},
- BeastMitigation = Pending#connection_state.beast_mitigation,
- SecParams = Pending#connection_state.security_parameters,
+ #{secure_renegotiation := SecureRenegotation} = Current,
+ #{beast_mitigation := BeastMitigation,
+ security_parameters := SecParams} = Pending,
+ NewCurrent = Pending#{sequence_number => 0},
ConnectionEnd = SecParams#security_parameters.connection_end,
EmptyPending = empty_connection_state(ConnectionEnd, BeastMitigation),
- SecureRenegotation = NewCurrent#connection_state.secure_renegotiation,
- NewPending = EmptyPending#connection_state{secure_renegotiation = SecureRenegotation},
- States#connection_states{current_read = NewCurrent,
- pending_read = NewPending
- };
-
-activate_pending_connection_state(States =
- #connection_states{current_write = Current,
- pending_write = Pending},
+ NewPending = EmptyPending#{secure_renegotiation => SecureRenegotation},
+ States#{current_read => NewCurrent,
+ pending_read => NewPending
+ };
+
+activate_pending_connection_state(#{current_write := Current,
+ pending_write := Pending} = States,
write) ->
- NewCurrent = Pending#connection_state{epoch = dtls_next_epoch(Current),
- sequence_number = 0},
- BeastMitigation = Pending#connection_state.beast_mitigation,
- SecParams = Pending#connection_state.security_parameters,
+ NewCurrent = Pending#{sequence_number => 0},
+ #{secure_renegotiation := SecureRenegotation} = Current,
+ #{beast_mitigation := BeastMitigation,
+ security_parameters := SecParams} = Pending,
ConnectionEnd = SecParams#security_parameters.connection_end,
EmptyPending = empty_connection_state(ConnectionEnd, BeastMitigation),
- SecureRenegotation = NewCurrent#connection_state.secure_renegotiation,
- NewPending = EmptyPending#connection_state{secure_renegotiation = SecureRenegotation},
- States#connection_states{current_write = NewCurrent,
- pending_write = NewPending
- }.
-
+ NewPending = EmptyPending#{secure_renegotiation => SecureRenegotation},
+ States#{current_write => NewCurrent,
+ pending_write => NewPending
+ }.
%%--------------------------------------------------------------------
-spec set_security_params(#security_parameters{}, #security_parameters{},
- #connection_states{}) -> #connection_states{}.
+ connection_states()) -> connection_states().
%%
%% Description: Creates a new instance of the connection_states record
%% where the pending states gets its security parameters updated.
%%--------------------------------------------------------------------
-set_security_params(ReadParams, WriteParams, States =
- #connection_states{pending_read = Read,
- pending_write = Write}) ->
- States#connection_states{pending_read =
- Read#connection_state{security_parameters =
- ReadParams},
- pending_write =
- Write#connection_state{security_parameters =
- WriteParams}
- }.
+set_security_params(ReadParams, WriteParams,
+ #{pending_read := Read,
+ pending_write := Write} = States) ->
+ States#{pending_read => Read#{security_parameters => ReadParams},
+ pending_write => Write#{security_parameters => WriteParams}
+ }.
%%--------------------------------------------------------------------
-spec set_mac_secret(binary(), binary(), client | server,
- #connection_states{}) -> #connection_states{}.
+ connection_states()) -> connection_states().
%%
%% Description: update the mac_secret field in pending connection states
%%--------------------------------------------------------------------
@@ -177,152 +147,145 @@ set_mac_secret(ClientWriteMacSecret, ServerWriteMacSecret, server, States) ->
set_mac_secret(ClientWriteMacSecret, ServerWriteMacSecret, States).
set_mac_secret(ReadMacSecret, WriteMacSecret,
- States = #connection_states{pending_read = Read,
- pending_write = Write}) ->
- States#connection_states{
- pending_read = Read#connection_state{mac_secret = ReadMacSecret},
- pending_write = Write#connection_state{mac_secret = WriteMacSecret}
+ States = #{pending_read := Read,
+ pending_write := Write}) ->
+ States#{pending_read => Read#{mac_secret => ReadMacSecret},
+ pending_write => Write#{mac_secret => WriteMacSecret}
}.
%%--------------------------------------------------------------------
--spec set_master_secret(binary(), #connection_states{}) -> #connection_states{}.
+-spec set_master_secret(binary(), connection_states()) -> connection_states().
%%
%% Description: Set master_secret in pending connection states
%%--------------------------------------------------------------------
set_master_secret(MasterSecret,
- States = #connection_states{pending_read = Read,
- pending_write = Write}) ->
- ReadSecPar = Read#connection_state.security_parameters,
- Read1 = Read#connection_state{
- security_parameters = ReadSecPar#security_parameters{
- master_secret = MasterSecret}},
- WriteSecPar = Write#connection_state.security_parameters,
- Write1 = Write#connection_state{
- security_parameters = WriteSecPar#security_parameters{
- master_secret = MasterSecret}},
- States#connection_states{pending_read = Read1, pending_write = Write1}.
-
-%%--------------------------------------------------------------------
--spec set_renegotiation_flag(boolean(), #connection_states{}) -> #connection_states{}.
+ States = #{pending_read := Read = #{security_parameters := ReadSecPar},
+ pending_write := Write = #{security_parameters := WriteSecPar}}) ->
+ Read1 = Read#{security_parameters => ReadSecPar#security_parameters{
+ master_secret = MasterSecret}},
+ Write1 = Write#{security_parameters => WriteSecPar#security_parameters{
+ master_secret = MasterSecret}},
+ States#{pending_read => Read1, pending_write => Write1}.
+
+%%--------------------------------------------------------------------
+-spec set_renegotiation_flag(boolean(), connection_states()) -> connection_states().
%%
%% Description: Set secure_renegotiation in pending connection states
%%--------------------------------------------------------------------
-set_renegotiation_flag(Flag, #connection_states{
- current_read = CurrentRead0,
- current_write = CurrentWrite0,
- pending_read = PendingRead0,
- pending_write = PendingWrite0}
+set_renegotiation_flag(Flag, #{current_read := CurrentRead0,
+ current_write := CurrentWrite0,
+ pending_read := PendingRead0,
+ pending_write := PendingWrite0}
= ConnectionStates) ->
- CurrentRead = CurrentRead0#connection_state{secure_renegotiation = Flag},
- CurrentWrite = CurrentWrite0#connection_state{secure_renegotiation = Flag},
- PendingRead = PendingRead0#connection_state{secure_renegotiation = Flag},
- PendingWrite = PendingWrite0#connection_state{secure_renegotiation = Flag},
- ConnectionStates#connection_states{current_read = CurrentRead,
- current_write = CurrentWrite,
- pending_read = PendingRead,
- pending_write = PendingWrite}.
+ CurrentRead = CurrentRead0#{secure_renegotiation => Flag},
+ CurrentWrite = CurrentWrite0#{secure_renegotiation => Flag},
+ PendingRead = PendingRead0#{secure_renegotiation => Flag},
+ PendingWrite = PendingWrite0#{secure_renegotiation => Flag},
+ ConnectionStates#{current_read => CurrentRead,
+ current_write => CurrentWrite,
+ pending_read => PendingRead,
+ pending_write => PendingWrite}.
%%--------------------------------------------------------------------
-spec set_client_verify_data(current_read | current_write | current_both,
- binary(), #connection_states{})->
- #connection_states{}.
+ binary(), connection_states())->
+ connection_states().
%%
%% Description: Set verify data in connection states.
%%--------------------------------------------------------------------
set_client_verify_data(current_read, Data,
- #connection_states{current_read = CurrentRead0,
- pending_write = PendingWrite0}
+ #{current_read := CurrentRead0,
+ pending_write := PendingWrite0}
= ConnectionStates) ->
- CurrentRead = CurrentRead0#connection_state{client_verify_data = Data},
- PendingWrite = PendingWrite0#connection_state{client_verify_data = Data},
- ConnectionStates#connection_states{current_read = CurrentRead,
- pending_write = PendingWrite};
+ CurrentRead = CurrentRead0#{client_verify_data => Data},
+ PendingWrite = PendingWrite0#{client_verify_data => Data},
+ ConnectionStates#{current_read => CurrentRead,
+ pending_write => PendingWrite};
set_client_verify_data(current_write, Data,
- #connection_states{pending_read = PendingRead0,
- current_write = CurrentWrite0}
+ #{pending_read := PendingRead0,
+ current_write := CurrentWrite0}
= ConnectionStates) ->
- PendingRead = PendingRead0#connection_state{client_verify_data = Data},
- CurrentWrite = CurrentWrite0#connection_state{client_verify_data = Data},
- ConnectionStates#connection_states{pending_read = PendingRead,
- current_write = CurrentWrite};
+ PendingRead = PendingRead0#{client_verify_data => Data},
+ CurrentWrite = CurrentWrite0#{client_verify_data => Data},
+ ConnectionStates#{pending_read => PendingRead,
+ current_write => CurrentWrite};
set_client_verify_data(current_both, Data,
- #connection_states{current_read = CurrentRead0,
- current_write = CurrentWrite0}
+ #{current_read := CurrentRead0,
+ current_write := CurrentWrite0}
= ConnectionStates) ->
- CurrentRead = CurrentRead0#connection_state{client_verify_data = Data},
- CurrentWrite = CurrentWrite0#connection_state{client_verify_data = Data},
- ConnectionStates#connection_states{current_read = CurrentRead,
- current_write = CurrentWrite}.
+ CurrentRead = CurrentRead0#{client_verify_data => Data},
+ CurrentWrite = CurrentWrite0#{client_verify_data => Data},
+ ConnectionStates#{current_read => CurrentRead,
+ current_write => CurrentWrite}.
%%--------------------------------------------------------------------
-spec set_server_verify_data(current_read | current_write | current_both,
- binary(), #connection_states{})->
- #connection_states{}.
+ binary(), connection_states())->
+ connection_states().
%%
%% Description: Set verify data in pending connection states.
%%--------------------------------------------------------------------
set_server_verify_data(current_write, Data,
- #connection_states{pending_read = PendingRead0,
- current_write = CurrentWrite0}
+ #{pending_read := PendingRead0,
+ current_write := CurrentWrite0}
= ConnectionStates) ->
- PendingRead = PendingRead0#connection_state{server_verify_data = Data},
- CurrentWrite = CurrentWrite0#connection_state{server_verify_data = Data},
- ConnectionStates#connection_states{pending_read = PendingRead,
- current_write = CurrentWrite};
+ PendingRead = PendingRead0#{server_verify_data => Data},
+ CurrentWrite = CurrentWrite0#{server_verify_data => Data},
+ ConnectionStates#{pending_read => PendingRead,
+ current_write => CurrentWrite};
set_server_verify_data(current_read, Data,
- #connection_states{current_read = CurrentRead0,
- pending_write = PendingWrite0}
+ #{current_read := CurrentRead0,
+ pending_write := PendingWrite0}
= ConnectionStates) ->
- CurrentRead = CurrentRead0#connection_state{server_verify_data = Data},
- PendingWrite = PendingWrite0#connection_state{server_verify_data = Data},
- ConnectionStates#connection_states{current_read = CurrentRead,
- pending_write = PendingWrite};
+ CurrentRead = CurrentRead0#{server_verify_data => Data},
+ PendingWrite = PendingWrite0#{server_verify_data => Data},
+ ConnectionStates#{current_read => CurrentRead,
+ pending_write => PendingWrite};
set_server_verify_data(current_both, Data,
- #connection_states{current_read = CurrentRead0,
- current_write = CurrentWrite0}
+ #{current_read := CurrentRead0,
+ current_write := CurrentWrite0}
= ConnectionStates) ->
- CurrentRead = CurrentRead0#connection_state{server_verify_data = Data},
- CurrentWrite = CurrentWrite0#connection_state{server_verify_data = Data},
- ConnectionStates#connection_states{current_read = CurrentRead,
- current_write = CurrentWrite}.
+ CurrentRead = CurrentRead0#{server_verify_data => Data},
+ CurrentWrite = CurrentWrite0#{server_verify_data => Data},
+ ConnectionStates#{current_read => CurrentRead,
+ current_write => CurrentWrite}.
%%--------------------------------------------------------------------
--spec set_pending_cipher_state(#connection_states{}, #cipher_state{},
+-spec set_pending_cipher_state(connection_states(), #cipher_state{},
#cipher_state{}, client | server) ->
- #connection_states{}.
+ connection_states().
%%
%% Description: Set the cipher state in the specified pending connection state.
%%--------------------------------------------------------------------
-set_pending_cipher_state(#connection_states{pending_read = Read,
- pending_write = Write} = States,
+set_pending_cipher_state(#{pending_read := Read,
+ pending_write := Write} = States,
ClientState, ServerState, server) ->
- States#connection_states{
- pending_read = Read#connection_state{cipher_state = ClientState},
- pending_write = Write#connection_state{cipher_state = ServerState}};
+ States#{
+ pending_read => Read#{cipher_state => ClientState},
+ pending_write => Write#{cipher_state => ServerState}};
-set_pending_cipher_state(#connection_states{pending_read = Read,
- pending_write = Write} = States,
+set_pending_cipher_state(#{pending_read := Read,
+ pending_write := Write} = States,
ClientState, ServerState, client) ->
- States#connection_states{
- pending_read = Read#connection_state{cipher_state = ServerState},
- pending_write = Write#connection_state{cipher_state = ClientState}}.
+ States#{
+ pending_read => Read#{cipher_state => ServerState},
+ pending_write => Write#{cipher_state => ClientState}}.
%%--------------------------------------------------------------------
--spec encode_handshake(iolist(), ssl_version(), #connection_states{}) ->
- {iolist(), #connection_states{}}.
+-spec encode_handshake(iolist(), ssl_version(), connection_states()) ->
+ {iolist(), connection_states()}.
%%
%% Description: Encodes a handshake message to send on the ssl-socket.
%%--------------------------------------------------------------------
encode_handshake(Frag, Version,
- #connection_states{current_write =
- #connection_state{
- beast_mitigation = BeastMitigation,
- security_parameters =
- #security_parameters{bulk_cipher_algorithm = BCA}}} =
+ #{current_write :=
+ #{beast_mitigation := BeastMitigation,
+ security_parameters :=
+ #security_parameters{bulk_cipher_algorithm = BCA}}} =
ConnectionStates)
-when is_list(Frag) ->
+ when is_list(Frag) ->
case iolist_size(Frag) of
N when N > ?MAX_PLAIN_TEXT_LENGTH ->
Data = split_bin(iolist_to_binary(Frag), ?MAX_PLAIN_TEXT_LENGTH, Version, BCA, BeastMitigation),
@@ -341,8 +304,8 @@ encode_handshake(Frag, Version, ConnectionStates) ->
encode_plain_text(?HANDSHAKE, Version, Frag, ConnectionStates).
%%--------------------------------------------------------------------
--spec encode_alert_record(#alert{}, ssl_version(), #connection_states{}) ->
- {iolist(), #connection_states{}}.
+-spec encode_alert_record(#alert{}, ssl_version(), connection_states()) ->
+ {iolist(), connection_states()}.
%%
%% Description: Encodes an alert message to send on the ssl-socket.
%%--------------------------------------------------------------------
@@ -352,8 +315,8 @@ encode_alert_record(#alert{level = Level, description = Description},
ConnectionStates).
%%--------------------------------------------------------------------
--spec encode_change_cipher_spec(ssl_version(), #connection_states{}) ->
- {iolist(), #connection_states{}}.
+-spec encode_change_cipher_spec(ssl_version(), connection_states()) ->
+ {iolist(), connection_states()}.
%%
%% Description: Encodes a change_cipher_spec-message to send on the ssl socket.
%%--------------------------------------------------------------------
@@ -361,15 +324,14 @@ encode_change_cipher_spec(Version, ConnectionStates) ->
encode_plain_text(?CHANGE_CIPHER_SPEC, Version, <<1:8>>, ConnectionStates).
%%--------------------------------------------------------------------
--spec encode_data(binary(), ssl_version(), #connection_states{}) ->
- {iolist(), #connection_states{}}.
+-spec encode_data(binary(), ssl_version(), connection_states()) ->
+ {iolist(), connection_states()}.
%%
%% Description: Encodes data to send on the ssl-socket.
%%--------------------------------------------------------------------
encode_data(Frag, Version,
- #connection_states{current_write = #connection_state{
- beast_mitigation = BeastMitigation,
- security_parameters =
+ #{current_write := #{beast_mitigation := BeastMitigation,
+ security_parameters :=
#security_parameters{bulk_cipher_algorithm = BCA}}} =
ConnectionStates) ->
Data = split_bin(Frag, ?MAX_PLAIN_TEXT_LENGTH, Version, BCA, BeastMitigation),
@@ -390,73 +352,74 @@ compressions() ->
[?byte(?NULL)].
%%--------------------------------------------------------------------
--spec cipher(ssl_version(), iodata(), #connection_state{}, MacHash::binary()) ->
- {CipherFragment::binary(), #connection_state{}}.
+-spec cipher(ssl_version(), iodata(), connection_state(), MacHash::binary()) ->
+ {CipherFragment::binary(), connection_state()}.
%%
%% Description: Payload encryption
%%--------------------------------------------------------------------
cipher(Version, Fragment,
- #connection_state{cipher_state = CipherS0,
- security_parameters=
- #security_parameters{bulk_cipher_algorithm =
- BulkCipherAlgo}
- } = WriteState0, MacHash) ->
-
+ #{cipher_state := CipherS0,
+ security_parameters :=
+ #security_parameters{bulk_cipher_algorithm =
+ BulkCipherAlgo}
+ } = WriteState0, MacHash) ->
+
{CipherFragment, CipherS1} =
ssl_cipher:cipher(BulkCipherAlgo, CipherS0, MacHash, Fragment, Version),
- {CipherFragment, WriteState0#connection_state{cipher_state = CipherS1}}.
+ {CipherFragment, WriteState0#{cipher_state => CipherS1}}.
%%--------------------------------------------------------------------
--spec cipher_aead(ssl_version(), iodata(), #connection_state{}, MacHash::binary()) ->
- {CipherFragment::binary(), #connection_state{}}.
+-spec cipher_aead(ssl_version(), iodata(), connection_state(), MacHash::binary()) ->
+ {CipherFragment::binary(), connection_state()}.
%%
%% Description: Payload encryption
%%--------------------------------------------------------------------
cipher_aead(Version, Fragment,
- #connection_state{cipher_state = CipherS0,
- sequence_number = SeqNo,
- security_parameters=
- #security_parameters{bulk_cipher_algorithm =
- BulkCipherAlgo}
- } = WriteState0, AAD) ->
-
+ #{cipher_state := CipherS0,
+ sequence_number := SeqNo,
+ security_parameters :=
+ #security_parameters{bulk_cipher_algorithm =
+ BulkCipherAlgo}
+ } = WriteState0, AAD) ->
+
{CipherFragment, CipherS1} =
ssl_cipher:cipher_aead(BulkCipherAlgo, CipherS0, SeqNo, AAD, Fragment, Version),
- {CipherFragment, WriteState0#connection_state{cipher_state = CipherS1}}.
+ {CipherFragment, WriteState0#{cipher_state => CipherS1}}.
%%--------------------------------------------------------------------
--spec decipher(ssl_version(), binary(), #connection_state{}, boolean()) -> {binary(), binary(), #connection_state{}} | #alert{}.
+-spec decipher(ssl_version(), binary(), connection_state(), boolean()) -> {binary(), binary(), connection_state} | #alert{}.
%%
%% Description: Payload decryption
%%--------------------------------------------------------------------
decipher(Version, CipherFragment,
- #connection_state{security_parameters =
- #security_parameters{bulk_cipher_algorithm =
- BulkCipherAlgo,
- hash_size = HashSz},
- cipher_state = CipherS0
- } = ReadState, PaddingCheck) ->
+ #{security_parameters :=
+ #security_parameters{bulk_cipher_algorithm =
+ BulkCipherAlgo,
+ hash_size = HashSz},
+ cipher_state := CipherS0
+ } = ReadState, PaddingCheck) ->
case ssl_cipher:decipher(BulkCipherAlgo, HashSz, CipherS0, CipherFragment, Version, PaddingCheck) of
{PlainFragment, Mac, CipherS1} ->
- CS1 = ReadState#connection_state{cipher_state = CipherS1},
+ CS1 = ReadState#{cipher_state => CipherS1},
{PlainFragment, Mac, CS1};
#alert{} = Alert ->
Alert
end.
%%--------------------------------------------------------------------
--spec decipher_aead(ssl_version(), binary(), #connection_state{}, binary()) -> {binary(), binary(), #connection_state{}} | #alert{}.
+-spec decipher_aead(ssl_version(), binary(), connection_state(), binary()) ->
+ {binary(), binary(), connection_state()} | #alert{}.
%%
%% Description: Payload decryption
%%--------------------------------------------------------------------
decipher_aead(Version, CipherFragment,
- #connection_state{sequence_number = SeqNo,
- security_parameters =
- #security_parameters{bulk_cipher_algorithm =
- BulkCipherAlgo},
- cipher_state = CipherS0
- } = ReadState, AAD) ->
+ #{sequence_number := SeqNo,
+ security_parameters :=
+ #security_parameters{bulk_cipher_algorithm =
+ BulkCipherAlgo},
+ cipher_state := CipherS0
+ } = ReadState, AAD) ->
case ssl_cipher:decipher_aead(BulkCipherAlgo, CipherS0, SeqNo, AAD, CipherFragment, Version) of
{PlainFragment, CipherS1} ->
- CS1 = ReadState#connection_state{cipher_state = CipherS1},
+ CS1 = ReadState#{cipher_state => CipherS1},
{PlainFragment, CS1};
#alert{} = Alert ->
Alert
@@ -466,8 +429,15 @@ decipher_aead(Version, CipherFragment,
%%--------------------------------------------------------------------
empty_connection_state(ConnectionEnd, BeastMitigation) ->
SecParams = empty_security_params(ConnectionEnd),
- #connection_state{security_parameters = SecParams,
- beast_mitigation = BeastMitigation}.
+ #{security_parameters => SecParams,
+ beast_mitigation => BeastMitigation,
+ compression_state => undefined,
+ cipher_state => undefined,
+ mac_secret => undefined,
+ secure_renegotiation => undefined,
+ client_verify_data => undefined,
+ server_verify_data => undefined
+ }.
empty_security_params(ConnectionEnd = ?CLIENT) ->
#security_parameters{connection_end = ConnectionEnd,
@@ -481,10 +451,10 @@ random() ->
Random_28_bytes = ssl_cipher:random_bytes(28),
<<?UINT32(Secs_since_1970), Random_28_bytes/binary>>.
-dtls_next_epoch(#connection_state{epoch = undefined}) -> %% SSL/TLS
- undefined;
-dtls_next_epoch(#connection_state{epoch = Epoch}) -> %% DTLS
- Epoch + 1.
+%% dtls_next_epoch(#connection_state{epoch = undefined}) -> %% SSL/TLS
+%% undefined;
+%% dtls_next_epoch(#connection_state{epoch = Epoch}) -> %% DTLS
+%% Epoch + 1.
is_correct_mac(Mac, Mac) ->
true;
@@ -497,11 +467,17 @@ record_protocol_role(server) ->
?SERVER.
initial_connection_state(ConnectionEnd, BeastMitigation) ->
- #connection_state{security_parameters =
- initial_security_params(ConnectionEnd),
- sequence_number = 0,
- beast_mitigation = BeastMitigation
- }.
+ #{security_parameters =>
+ initial_security_params(ConnectionEnd),
+ sequence_number => 0,
+ beast_mitigation => BeastMitigation,
+ compression_state => undefined,
+ cipher_state => undefined,
+ mac_secret => undefined,
+ secure_renegotiation => undefined,
+ client_verify_data => undefined,
+ server_verify_data => undefined
+ }.
initial_security_params(ConnectionEnd) ->
SecParams = #security_parameters{connection_end = ConnectionEnd,
diff --git a/lib/ssl/src/ssl_record.hrl b/lib/ssl/src/ssl_record.hrl
index a41264ff9b..ed007f58d7 100644
--- a/lib/ssl/src/ssl_record.hrl
+++ b/lib/ssl/src/ssl_record.hrl
@@ -30,29 +30,27 @@
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%% Connection states - RFC 4346 section 6.1
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
--record(connection_state, {
- security_parameters,
- compression_state,
- cipher_state,
- mac_secret,
- epoch, %% Only used by DTLS
- sequence_number,
- %% RFC 5746
- secure_renegotiation,
- client_verify_data,
- server_verify_data,
- %% How to do BEAST mitigation?
- beast_mitigation
- }).
-
--record(connection_states, {
- dtls_write_msg_seq, %% Only used by DTLS
+%% For documentation purposes are now maps in implementation
+%% -record(connection_state, {
+%% security_parameters,
+%% compression_state,
+%% cipher_state,
+%% mac_secret,
+%% sequence_number,
+%% %% RFC 5746
+%% secure_renegotiation,
+%% client_verify_data,
+%% server_verify_data,
+%% %% How to do BEAST mitigation?
+%% beast_mitigation
+%% }).
- current_read,
- pending_read,
- current_write,
- pending_write
- }).
+%% -record(connection_states, {
+%% current_read,
+%% pending_read,
+%% current_write,
+%% pending_write,
+%% }).
-record(security_parameters, {
cipher_suite,
diff --git a/lib/ssl/src/ssl_sup.erl b/lib/ssl/src/ssl_sup.erl
index 7fa1f7dc9e..ba20f65f44 100644
--- a/lib/ssl/src/ssl_sup.erl
+++ b/lib/ssl/src/ssl_sup.erl
@@ -47,11 +47,13 @@ init([]) ->
SessionCertManager = session_and_cert_manager_child_spec(),
TLSConnetionManager = tls_connection_manager_child_spec(),
%% Not supported yet
- %%DTLSConnetionManager = tls_connection_manager_child_spec(),
+ %%DTLSConnetionManager = dtls_connection_manager_child_spec(),
%% Handles emulated options so that they inherited by the accept socket, even when setopts is performed on
%% the listen socket
ListenOptionsTracker = listen_options_tracker_child_spec(),
- {ok, {{one_for_all, 10, 3600}, [SessionCertManager, TLSConnetionManager, ListenOptionsTracker]}}.
+ {ok, {{one_for_all, 10, 3600}, [SessionCertManager, TLSConnetionManager,
+ %%DTLSConnetionManager,
+ ListenOptionsTracker]}}.
manager_opts() ->
@@ -93,15 +95,14 @@ tls_connection_manager_child_spec() ->
{Name, StartFunc, Restart, Shutdown, Type, Modules}.
%% dtls_connection_manager_child_spec() ->
-%% Name = dtls_connection,
+%% Name = dtls_connection,
%% StartFunc = {dtls_connection_sup, start_link, []},
-%% Restart = permanent,
+%% Restart = permanent,
%% Shutdown = 4000,
%% Modules = [dtls_connection, ssl_connection],
%% Type = supervisor,
%% {Name, StartFunc, Restart, Shutdown, Type, Modules}.
-
listen_options_tracker_child_spec() ->
Name = ssl_socket,
StartFunc = {ssl_listen_tracker_sup, start_link, []},
diff --git a/lib/ssl/src/ssl_tls_dist_proxy.erl b/lib/ssl/src/ssl_tls_dist_proxy.erl
index a920f54ed2..08947f24dd 100644
--- a/lib/ssl/src/ssl_tls_dist_proxy.erl
+++ b/lib/ssl/src/ssl_tls_dist_proxy.erl
@@ -117,7 +117,7 @@ handle_call({listen, Driver, Name}, _From, State) ->
{ok, WorldTcpAddress} = get_tcp_address(World),
{_,Port} = WorldTcpAddress#net_address.address,
ErlEpmd = net_kernel:epmd_module(),
- case ErlEpmd:register_node(Name, Port) of
+ case ErlEpmd:register_node(Name, Port, Driver) of
{ok, Creation} ->
{reply, {ok, {Socket, TcpAddress, Creation}},
State#state{listen={Socket, World}}};
diff --git a/lib/ssl/src/tls_connection.erl b/lib/ssl/src/tls_connection.erl
index 9880befa94..932bb139c1 100644
--- a/lib/ssl/src/tls_connection.erl
+++ b/lib/ssl/src/tls_connection.erl
@@ -51,27 +51,21 @@
%% Handshake handling
-export([renegotiate/2, send_handshake/2,
queue_handshake/2, queue_change_cipher/2,
- reinit_handshake_data/1, handle_sni_extension/2]).
+ reinit_handshake_data/1, select_sni_extension/1]).
%% Alert and close handling
--export([send_alert/2, handle_own_alert/4, handle_close_alert/3,
- handle_normal_shutdown/3,
- close/5, alert_user/6, alert_user/9
- ]).
+-export([send_alert/2, close/5]).
%% Data handling
--export([write_application_data/3, read_application_data/2,
- passive_receive/2, next_record_if_active/1, handle_common_event/4]).
+-export([passive_receive/2, next_record_if_active/1, handle_common_event/4]).
%% gen_statem state functions
-export([init/3, error/3, downgrade/3, %% Initiation and take down states
hello/3, certify/3, cipher/3, abbreviated/3, %% Handshake states
connection/3]).
%% gen_statem callbacks
--export([terminate/3, code_change/4, format_status/2]).
+-export([callback_mode/0, terminate/3, code_change/4, format_status/2]).
--define(GEN_STATEM_CB_MODE, state_functions).
-
%%====================================================================
%% Internal application API
%%====================================================================
@@ -109,9 +103,10 @@ send_handshake(Handshake, State) ->
queue_handshake(Handshake, #state{negotiated_version = Version,
tls_handshake_history = Hist0,
flight_buffer = Flight0,
+ ssl_options = #ssl_options{v2_hello_compatible = V2HComp},
connection_states = ConnectionStates0} = State0) ->
{BinHandshake, ConnectionStates, Hist} =
- encode_handshake(Handshake, Version, ConnectionStates0, Hist0),
+ encode_handshake(Handshake, Version, ConnectionStates0, Hist0, V2HComp),
State0#state{connection_states = ConnectionStates,
tls_handshake_history = Hist,
flight_buffer = Flight0 ++ [BinHandshake]}.
@@ -149,6 +144,11 @@ reinit_handshake_data(State) ->
tls_handshake_history = ssl_handshake:init_handshake_history()
}.
+select_sni_extension(#client_hello{extensions = HelloExtensions}) ->
+ HelloExtensions#hello_extensions.sni;
+select_sni_extension(_) ->
+ undefined.
+
%%====================================================================
%% tls_connection_sup API
%%====================================================================
@@ -169,11 +169,14 @@ init([Role, Host, Port, Socket, Options, User, CbInfo]) ->
State0 = initial_state(Role, Host, Port, Socket, Options, User, CbInfo),
try
State = ssl_connection:ssl_config(State0#state.ssl_options, Role, State0),
- gen_statem:enter_loop(?MODULE, [], ?GEN_STATEM_CB_MODE, init, State)
+ gen_statem:enter_loop(?MODULE, [], init, State)
catch throw:Error ->
- gen_statem:enter_loop(?MODULE, [], ?GEN_STATEM_CB_MODE, error, {Error, State0})
+ gen_statem:enter_loop(?MODULE, [], error, {Error, State0})
end.
+callback_mode() ->
+ state_functions.
+
%%--------------------------------------------------------------------
%% State functions
%%--------------------------------------------------------------------
@@ -185,7 +188,7 @@ init([Role, Host, Port, Socket, Options, User, CbInfo]) ->
init({call, From}, {start, Timeout},
#state{host = Host, port = Port, role = client,
- ssl_options = SslOpts,
+ ssl_options = #ssl_options{v2_hello_compatible = V2HComp} = SslOpts,
session = #session{own_certificate = Cert} = Session0,
transport_cb = Transport, socket = Socket,
connection_states = ConnectionStates0,
@@ -201,7 +204,7 @@ init({call, From}, {start, Timeout},
HelloVersion = tls_record:lowest_protocol_version(SslOpts#ssl_options.versions),
Handshake0 = ssl_handshake:init_handshake_history(),
{BinMsg, ConnectionStates, Handshake} =
- encode_handshake(Hello, HelloVersion, ConnectionStates0, Handshake0),
+ encode_handshake(Hello, HelloVersion, ConnectionStates0, Handshake0, V2HComp),
Transport:send(Socket, BinMsg),
State1 = State0#state{connection_states = ConnectionStates,
negotiated_version = Version, %% Requested version
@@ -213,7 +216,7 @@ init({call, From}, {start, Timeout},
{Record, State} = next_record(State1),
next_event(hello, Record, State);
init(Type, Event, State) ->
- ssl_connection:init(Type, Event, State, ?MODULE).
+ gen_handshake(ssl_connection, init, Type, Event, State).
%%--------------------------------------------------------------------
-spec error(gen_statem:event_type(),
@@ -234,9 +237,7 @@ error(_, _, _) ->
#state{}) ->
gen_statem:state_function_result().
%%--------------------------------------------------------------------
-hello(internal, #client_hello{client_version = ClientVersion,
- extensions = #hello_extensions{ec_point_formats = EcPointFormats,
- elliptic_curves = EllipticCurves}} = Hello,
+hello(internal, #client_hello{client_version = ClientVersion} = Hello,
#state{connection_states = ConnectionStates0,
port = Port, session = #session{own_certificate = Cert} = Session0,
renegotiation = {Renegotiation, _},
@@ -249,7 +250,7 @@ hello(internal, #client_hello{client_version = ClientVersion,
case tls_handshake:hello(Hello, SslOpts, {Port, Session0, Cache, CacheCb,
ConnectionStates0, Cert, KeyExAlg}, Renegotiation) of
#alert{} = Alert ->
- handle_own_alert(Alert, ClientVersion, hello, State);
+ ssl_connection:handle_own_alert(Alert, ClientVersion, hello, State);
{Version, {Type, Session},
ConnectionStates, Protocol0, ServerHelloExt, HashSign} ->
Protocol = case Protocol0 of
@@ -257,13 +258,12 @@ hello(internal, #client_hello{client_version = ClientVersion,
_ -> Protocol0
end,
- ssl_connection:hello(internal, {common_client_hello, Type, ServerHelloExt},
+ gen_handshake(ssl_connection, hello, internal, {common_client_hello, Type, ServerHelloExt},
State#state{connection_states = ConnectionStates,
negotiated_version = Version,
hashsign_algorithm = HashSign,
session = Session,
- client_ecc = {EllipticCurves, EcPointFormats},
- negotiated_protocol = Protocol}, ?MODULE)
+ negotiated_protocol = Protocol})
end;
hello(internal, #server_hello{} = Hello,
#state{connection_states = ConnectionStates0,
@@ -273,42 +273,42 @@ hello(internal, #server_hello{} = Hello,
ssl_options = SslOptions} = State) ->
case tls_handshake:hello(Hello, SslOptions, ConnectionStates0, Renegotiation) of
#alert{} = Alert ->
- handle_own_alert(Alert, ReqVersion, hello, State);
+ ssl_connection:handle_own_alert(Alert, ReqVersion, hello, State);
{Version, NewId, ConnectionStates, ProtoExt, Protocol} ->
ssl_connection:handle_session(Hello,
Version, NewId, ConnectionStates, ProtoExt, Protocol, State)
end;
hello(info, Event, State) ->
- handle_info(Event, hello, State);
+ gen_info(Event, hello, State);
hello(Type, Event, State) ->
- ssl_connection:hello(Type, Event, State, ?MODULE).
+ gen_handshake(ssl_connection, hello, Type, Event, State).
%%--------------------------------------------------------------------
-spec abbreviated(gen_statem:event_type(), term(), #state{}) ->
gen_statem:state_function_result().
%%--------------------------------------------------------------------
abbreviated(info, Event, State) ->
- handle_info(Event, abbreviated, State);
+ gen_info(Event, abbreviated, State);
abbreviated(Type, Event, State) ->
- ssl_connection:abbreviated(Type, Event, State, ?MODULE).
+ gen_handshake(ssl_connection, abbreviated, Type, Event, State).
%%--------------------------------------------------------------------
-spec certify(gen_statem:event_type(), term(), #state{}) ->
gen_statem:state_function_result().
%%--------------------------------------------------------------------
certify(info, Event, State) ->
- handle_info(Event, certify, State);
+ gen_info(Event, certify, State);
certify(Type, Event, State) ->
- ssl_connection:certify(Type, Event, State, ?MODULE).
+ gen_handshake(ssl_connection, certify, Type, Event, State).
%%--------------------------------------------------------------------
-spec cipher(gen_statem:event_type(), term(), #state{}) ->
gen_statem:state_function_result().
%%--------------------------------------------------------------------
cipher(info, Event, State) ->
- handle_info(Event, cipher, State);
+ gen_info(Event, cipher, State);
cipher(Type, Event, State) ->
- ssl_connection:cipher(Type, Event, State, ?MODULE).
+ gen_handshake(ssl_connection, cipher, Type, Event, State).
%%--------------------------------------------------------------------
-spec connection(gen_statem:event_type(),
@@ -316,7 +316,7 @@ cipher(Type, Event, State) ->
gen_statem:state_function_result().
%%--------------------------------------------------------------------
connection(info, Event, State) ->
- handle_info(Event, connection, State);
+ gen_info(Event, connection, State);
connection(internal, #hello_request{},
#state{role = client, host = Host, port = Port,
session = #session{own_certificate = Cert} = Session0,
@@ -373,7 +373,7 @@ handle_info({Protocol, _, Data}, StateName,
{Record, State} ->
next_event(StateName, Record, State);
#alert{} = Alert ->
- handle_normal_shutdown(Alert, StateName, State0),
+ ssl_connection:handle_normal_shutdown(Alert, StateName, State0),
{stop, {shutdown, own_alert}}
end;
handle_info({CloseTag, Socket}, StateName,
@@ -393,14 +393,14 @@ handle_info({CloseTag, Socket}, StateName,
%%invalidate_session(Role, Host, Port, Session)
ok
end,
- handle_normal_shutdown(?ALERT_REC(?FATAL, ?CLOSE_NOTIFY), StateName, State),
+ ssl_connection:handle_normal_shutdown(?ALERT_REC(?FATAL, ?CLOSE_NOTIFY), StateName, State),
{stop, {shutdown, transport_closed}};
handle_info(Msg, StateName, State) ->
ssl_connection:handle_info(Msg, StateName, State).
handle_common_event(internal, #alert{} = Alert, StateName,
#state{negotiated_version = Version} = State) ->
- handle_own_alert(Alert, Version, StateName, State);
+ ssl_connection:handle_own_alert(Alert, Version, StateName, State);
%%% TLS record protocol level handshake messages
handle_common_event(internal, #ssl_tls{type = ?HANDSHAKE, fragment = Data},
@@ -418,10 +418,10 @@ handle_common_event(internal, #ssl_tls{type = ?HANDSHAKE, fragment = Data},
connection ->
ssl_connection:hibernate_after(StateName, State, Events);
_ ->
- {next_state, StateName, State, Events}
+ {next_state, StateName, State#state{unprocessed_handshake_events = unprocessed_events(Events)}, Events}
end
catch throw:#alert{} = Alert ->
- handle_own_alert(Alert, Version, StateName, State0)
+ ssl_connection:handle_own_alert(Alert, Version, StateName, State0)
end;
%%% TLS record protocol level application data messages
handle_common_event(internal, #ssl_tls{type = ?APPLICATION_DATA, fragment = Data}, StateName, State) ->
@@ -432,11 +432,19 @@ handle_common_event(internal, #ssl_tls{type = ?CHANGE_CIPHER_SPEC, fragment = Da
%%% TLS record protocol level Alert messages
handle_common_event(internal, #ssl_tls{type = ?ALERT, fragment = EncAlerts}, StateName,
#state{negotiated_version = Version} = State) ->
- case decode_alerts(EncAlerts) of
+ try decode_alerts(EncAlerts) of
Alerts = [_|_] ->
handle_alerts(Alerts, {next_state, StateName, State});
+ [] ->
+ ssl_connection:handle_own_alert(?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE, empty_alert),
+ Version, StateName, State);
#alert{} = Alert ->
- handle_own_alert(Alert, Version, StateName, State)
+ ssl_connection:handle_own_alert(Alert, Version, StateName, State)
+ catch
+ _:_ ->
+ ssl_connection:handle_own_alert(?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE, alert_decode_error),
+ Version, StateName, State)
+
end;
%% Ignore unknown TLS record level protocol messages
handle_common_event(internal, #ssl_tls{type = _Unknown}, StateName, State) ->
@@ -457,16 +465,16 @@ format_status(Type, Data) ->
%%--------------------------------------------------------------------
code_change(_OldVsn, StateName, State0, {Direction, From, To}) ->
State = convert_state(State0, Direction, From, To),
- {?GEN_STATEM_CB_MODE, StateName, State};
+ {ok, StateName, State};
code_change(_OldVsn, StateName, State, _) ->
- {?GEN_STATEM_CB_MODE, StateName, State}.
+ {ok, StateName, State}.
%%--------------------------------------------------------------------
%%% Internal functions
%%--------------------------------------------------------------------
-encode_handshake(Handshake, Version, ConnectionStates0, Hist0) ->
+encode_handshake(Handshake, Version, ConnectionStates0, Hist0, V2HComp) ->
Frag = tls_handshake:encode_handshake(Handshake, Version),
- Hist = ssl_handshake:update_handshake_history(Hist0, Frag),
+ Hist = ssl_handshake:update_handshake_history(Hist0, Frag, V2HComp),
{Encoded, ConnectionStates} =
ssl_record:encode_handshake(Frag, Version, ConnectionStates0),
{Encoded, ConnectionStates, Hist}.
@@ -480,7 +488,7 @@ decode_alerts(Bin) ->
initial_state(Role, Host, Port, Socket, {SSLOptions, SocketOptions, Tracker}, User,
{CbModule, DataTag, CloseTag, ErrorTag}) ->
#ssl_options{beast_mitigation = BeastMitigation} = SSLOptions,
- ConnectionStates = ssl_record:init_connection_states(Role, BeastMitigation),
+ ConnectionStates = tls_record:init_connection_states(Role, BeastMitigation),
SessionCacheCb = case application:get_env(ssl, session_cb) of
{ok, Cb} when is_atom(Cb) ->
@@ -515,23 +523,6 @@ initial_state(Role, Host, Port, Socket, {SSLOptions, SocketOptions, Tracker}, Us
flight_buffer = []
}.
-
-update_ssl_options_from_sni(OrigSSLOptions, SNIHostname) ->
- SSLOption =
- case OrigSSLOptions#ssl_options.sni_fun of
- undefined ->
- proplists:get_value(SNIHostname,
- OrigSSLOptions#ssl_options.sni_hosts);
- SNIFun ->
- SNIFun(SNIHostname)
- end,
- case SSLOption of
- undefined ->
- undefined;
- _ ->
- ssl:handle_options(SSLOption, OrigSSLOptions)
- end.
-
next_tls_record(Data, #state{protocol_buffers = #protocol_buffers{tls_record_buffer = Buf0,
tls_cipher_texts = CT0} = Buffers} = State0) ->
case tls_record:get_tls_records(Data, Buf0) of
@@ -543,7 +534,9 @@ next_tls_record(Data, #state{protocol_buffers = #protocol_buffers{tls_record_buf
#alert{} = Alert ->
Alert
end.
-
+next_record(#state{unprocessed_handshake_events = N} = State) when N > 0 ->
+ {no_record, State#state{unprocessed_handshake_events = N-1}};
+
next_record(#state{protocol_buffers =
#protocol_buffers{tls_packets = [], tls_cipher_texts = [CT | Rest]}
= Buffers,
@@ -579,7 +572,7 @@ passive_receive(State0 = #state{user_data_buffer = Buffer}, StateName) ->
{Record, State} = next_record(State0),
next_event(StateName, Record, State);
_ ->
- {Record, State} = read_application_data(<<>>, State0),
+ {Record, State} = ssl_connection:read_application_data(<<>>, State0),
next_event(StateName, Record, State)
end.
@@ -591,7 +584,7 @@ next_event(connection = StateName, no_record, State0, Actions) ->
{no_record, State} ->
ssl_connection:hibernate_after(StateName, State, Actions);
{#ssl_tls{} = Record, State} ->
- {next_state, StateName, State, [{next_event, internal, {tls_record, Record}} | Actions]};
+ {next_state, StateName, State, [{next_event, internal, {protocol_record, Record}} | Actions]};
{#alert{} = Alert, State} ->
{next_state, StateName, State, [{next_event, internal, Alert} | Actions]}
end;
@@ -600,169 +593,11 @@ next_event(StateName, Record, State, Actions) ->
no_record ->
{next_state, StateName, State, Actions};
#ssl_tls{} = Record ->
- {next_state, StateName, State, [{next_event, internal, {tls_record, Record}} | Actions]};
+ {next_state, StateName, State, [{next_event, internal, {protocol_record, Record}} | Actions]};
#alert{} = Alert ->
{next_state, StateName, State, [{next_event, internal, Alert} | Actions]}
end.
-read_application_data(Data, #state{user_application = {_Mon, Pid},
- socket = Socket,
- transport_cb = Transport,
- socket_options = SOpts,
- bytes_to_read = BytesToRead,
- start_or_recv_from = RecvFrom,
- timer = Timer,
- user_data_buffer = Buffer0,
- tracker = Tracker} = State0) ->
- Buffer1 = if
- Buffer0 =:= <<>> -> Data;
- Data =:= <<>> -> Buffer0;
- true -> <<Buffer0/binary, Data/binary>>
- end,
- case get_data(SOpts, BytesToRead, Buffer1) of
- {ok, ClientData, Buffer} -> % Send data
- SocketOpt = deliver_app_data(Transport, Socket, SOpts, ClientData, Pid, RecvFrom, Tracker),
- cancel_timer(Timer),
- State = State0#state{user_data_buffer = Buffer,
- start_or_recv_from = undefined,
- timer = undefined,
- bytes_to_read = undefined,
- socket_options = SocketOpt
- },
- if
- SocketOpt#socket_options.active =:= false; Buffer =:= <<>> ->
- %% Passive mode, wait for active once or recv
- %% Active and empty, get more data
- next_record_if_active(State);
- true -> %% We have more data
- read_application_data(<<>>, State)
- end;
- {more, Buffer} -> % no reply, we need more data
- next_record(State0#state{user_data_buffer = Buffer});
- {passive, Buffer} ->
- next_record_if_active(State0#state{user_data_buffer = Buffer});
- {error,_Reason} -> %% Invalid packet in packet mode
- deliver_packet_error(Transport, Socket, SOpts, Buffer1, Pid, RecvFrom, Tracker),
- {stop, normal, State0}
- end.
-
-%% Picks ClientData
-get_data(_, _, <<>>) ->
- {more, <<>>};
-%% Recv timed out save buffer data until next recv
-get_data(#socket_options{active=false}, undefined, Buffer) ->
- {passive, Buffer};
-get_data(#socket_options{active=Active, packet=Raw}, BytesToRead, Buffer)
- when Raw =:= raw; Raw =:= 0 -> %% Raw Mode
- if
- Active =/= false orelse BytesToRead =:= 0 ->
- %% Active true or once, or passive mode recv(0)
- {ok, Buffer, <<>>};
- byte_size(Buffer) >= BytesToRead ->
- %% Passive Mode, recv(Bytes)
- <<Data:BytesToRead/binary, Rest/binary>> = Buffer,
- {ok, Data, Rest};
- true ->
- %% Passive Mode not enough data
- {more, Buffer}
- end;
-get_data(#socket_options{packet=Type, packet_size=Size}, _, Buffer) ->
- PacketOpts = [{packet_size, Size}],
- case decode_packet(Type, Buffer, PacketOpts) of
- {more, _} ->
- {more, Buffer};
- Decoded ->
- Decoded
- end.
-
-decode_packet({http, headers}, Buffer, PacketOpts) ->
- decode_packet(httph, Buffer, PacketOpts);
-decode_packet({http_bin, headers}, Buffer, PacketOpts) ->
- decode_packet(httph_bin, Buffer, PacketOpts);
-decode_packet(Type, Buffer, PacketOpts) ->
- erlang:decode_packet(Type, Buffer, PacketOpts).
-
-%% Just like with gen_tcp sockets, an ssl socket that has been configured with
-%% {packet, http} (or {packet, http_bin}) will automatically switch to expect
-%% HTTP headers after it sees a HTTP Request or HTTP Response line. We
-%% represent the current state as follows:
-%% #socket_options.packet =:= http: Expect a HTTP Request/Response line
-%% #socket_options.packet =:= {http, headers}: Expect HTTP Headers
-%% Note that if the user has explicitly configured the socket to expect
-%% HTTP headers using the {packet, httph} option, we don't do any automatic
-%% switching of states.
-deliver_app_data(Transport, Socket, SOpts = #socket_options{active=Active, packet=Type},
- Data, Pid, From, Tracker) ->
- send_or_reply(Active, Pid, From, format_reply(Transport, Socket, SOpts, Data, Tracker)),
- SO = case Data of
- {P, _, _, _} when ((P =:= http_request) or (P =:= http_response)),
- ((Type =:= http) or (Type =:= http_bin)) ->
- SOpts#socket_options{packet={Type, headers}};
- http_eoh when tuple_size(Type) =:= 2 ->
- % End of headers - expect another Request/Response line
- {Type1, headers} = Type,
- SOpts#socket_options{packet=Type1};
- _ ->
- SOpts
- end,
- case Active of
- once ->
- SO#socket_options{active=false};
- _ ->
- SO
- end.
-
-format_reply(_, _,#socket_options{active = false, mode = Mode, packet = Packet,
- header = Header}, Data, _) ->
- {ok, do_format_reply(Mode, Packet, Header, Data)};
-format_reply(Transport, Socket, #socket_options{active = _, mode = Mode, packet = Packet,
- header = Header}, Data, Tracker) ->
- {ssl, ssl_socket:socket(self(), Transport, Socket, ?MODULE, Tracker),
- do_format_reply(Mode, Packet, Header, Data)}.
-
-deliver_packet_error(Transport, Socket, SO= #socket_options{active = Active}, Data, Pid, From, Tracker) ->
- send_or_reply(Active, Pid, From, format_packet_error(Transport, Socket, SO, Data, Tracker)).
-
-format_packet_error(_, _,#socket_options{active = false, mode = Mode}, Data, _) ->
- {error, {invalid_packet, do_format_reply(Mode, raw, 0, Data)}};
-format_packet_error(Transport, Socket, #socket_options{active = _, mode = Mode}, Data, Tracker) ->
- {ssl_error, ssl_socket:socket(self(), Transport, Socket, ?MODULE, Tracker),
- {invalid_packet, do_format_reply(Mode, raw, 0, Data)}}.
-
-do_format_reply(binary, _, N, Data) when N > 0 -> % Header mode
- header(N, Data);
-do_format_reply(binary, _, _, Data) ->
- Data;
-do_format_reply(list, Packet, _, Data)
- when Packet == http; Packet == {http, headers};
- Packet == http_bin; Packet == {http_bin, headers};
- Packet == httph; Packet == httph_bin ->
- Data;
-do_format_reply(list, _,_, Data) ->
- binary_to_list(Data).
-
-header(0, <<>>) ->
- <<>>;
-header(_, <<>>) ->
- [];
-header(0, Binary) ->
- Binary;
-header(N, Binary) ->
- <<?BYTE(ByteN), NewBinary/binary>> = Binary,
- [ByteN | header(N-1, NewBinary)].
-
-send_or_reply(false, _Pid, From, Data) when From =/= undefined ->
- gen_statem:reply(From, Data);
-%% Can happen when handling own alert or tcp error/close and there is
-%% no outstanding gen_fsm sync events
-send_or_reply(false, no_pid, _, _) ->
- ok;
-send_or_reply(_, Pid, _From, Data) ->
- send_user(Pid, Data).
-
-send_user(Pid, Msg) ->
- Pid ! Msg.
-
tls_handshake_events([]) ->
throw(?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE, malformed_handshake));
tls_handshake_events(Packets) ->
@@ -770,55 +605,7 @@ tls_handshake_events(Packets) ->
{next_event, internal, {handshake, Packet}}
end, Packets).
-write_application_data(Data0, From,
- #state{socket = Socket,
- negotiated_version = Version,
- transport_cb = Transport,
- connection_states = ConnectionStates0,
- socket_options = SockOpts,
- ssl_options = #ssl_options{renegotiate_at = RenegotiateAt}} = State) ->
- Data = encode_packet(Data0, SockOpts),
-
- case time_to_renegotiate(Data, ConnectionStates0, RenegotiateAt) of
- true ->
- renegotiate(State#state{renegotiation = {true, internal}},
- [{next_event, {call, From}, {application_data, Data0}}]);
- false ->
- {Msgs, ConnectionStates} = ssl_record:encode_data(Data, Version, ConnectionStates0),
- Result = Transport:send(Socket, Msgs),
- ssl_connection:hibernate_after(connection, State#state{connection_states = ConnectionStates},
- [{reply, From, Result}])
- end.
-
-encode_packet(Data, #socket_options{packet=Packet}) ->
- case Packet of
- 1 -> encode_size_packet(Data, 8, (1 bsl 8) - 1);
- 2 -> encode_size_packet(Data, 16, (1 bsl 16) - 1);
- 4 -> encode_size_packet(Data, 32, (1 bsl 32) - 1);
- _ -> Data
- end.
-
-encode_size_packet(Bin, Size, Max) ->
- Len = erlang:byte_size(Bin),
- case Len > Max of
- true -> throw({error, {badarg, {packet_to_large, Len, Max}}});
- false -> <<Len:Size, Bin/binary>>
- end.
-
-time_to_renegotiate(_Data,
- #connection_states{current_write =
- #connection_state{sequence_number = Num}},
- RenegotiateAt) ->
-
- %% We could do test:
- %% is_time_to_renegotiate((erlang:byte_size(_Data) div ?MAX_PLAIN_TEXT_LENGTH) + 1, RenegotiateAt),
- %% but we chose to have a some what lower renegotiateAt and a much cheaper test
- is_time_to_renegotiate(Num, RenegotiateAt).
-is_time_to_renegotiate(N, M) when N < M->
- false;
-is_time_to_renegotiate(_,_) ->
- true.
renegotiate(#state{role = client} = State, Actions) ->
%% Handle same way as if server requested
%% the renegotiation
@@ -848,131 +635,10 @@ handle_alerts([], Result) ->
handle_alerts(_, {stop,_} = Stop) ->
Stop;
handle_alerts([Alert | Alerts], {next_state, StateName, State}) ->
- handle_alerts(Alerts, handle_alert(Alert, StateName, State));
+ handle_alerts(Alerts, ssl_connection:handle_alert(Alert, StateName, State));
handle_alerts([Alert | Alerts], {next_state, StateName, State, _Actions}) ->
- handle_alerts(Alerts, handle_alert(Alert, StateName, State)).
-handle_alert(#alert{level = ?FATAL} = Alert, StateName,
- #state{socket = Socket, transport_cb = Transport,
- ssl_options = SslOpts, start_or_recv_from = From, host = Host,
- port = Port, session = Session, user_application = {_Mon, Pid},
- role = Role, socket_options = Opts, tracker = Tracker}) ->
- invalidate_session(Role, Host, Port, Session),
- log_alert(SslOpts#ssl_options.log_alert, StateName, Alert),
- alert_user(Transport, Tracker, Socket, StateName, Opts, Pid, From, Alert, Role),
- {stop, normal};
-
-handle_alert(#alert{level = ?WARNING, description = ?CLOSE_NOTIFY} = Alert,
- StateName, State) ->
- handle_normal_shutdown(Alert, StateName, State),
- {stop, {shutdown, peer_close}};
-
-handle_alert(#alert{level = ?WARNING, description = ?NO_RENEGOTIATION} = Alert, StateName,
- #state{ssl_options = SslOpts, renegotiation = {true, internal}} = State) ->
- log_alert(SslOpts#ssl_options.log_alert, StateName, Alert),
- handle_normal_shutdown(Alert, StateName, State),
- {stop, {shutdown, peer_close}};
-
-handle_alert(#alert{level = ?WARNING, description = ?NO_RENEGOTIATION} = Alert, StateName,
- #state{ssl_options = SslOpts, renegotiation = {true, From}} = State0) ->
- log_alert(SslOpts#ssl_options.log_alert, StateName, Alert),
- gen_statem:reply(From, {error, renegotiation_rejected}),
- {Record, State} = next_record(State0),
- %% Go back to connection!
- next_event(connection, Record, State);
-
-%% Gracefully log and ignore all other warning alerts
-handle_alert(#alert{level = ?WARNING} = Alert, StateName,
- #state{ssl_options = SslOpts} = State0) ->
- log_alert(SslOpts#ssl_options.log_alert, StateName, Alert),
- {Record, State} = next_record(State0),
- next_event(StateName, Record, State).
+ handle_alerts(Alerts, ssl_connection:handle_alert(Alert, StateName, State)).
-alert_user(Transport, Tracker, Socket, connection, Opts, Pid, From, Alert, Role) ->
- alert_user(Transport, Tracker, Socket, Opts#socket_options.active, Pid, From, Alert, Role);
-alert_user(Transport, Tracker, Socket,_, _, _, From, Alert, Role) ->
- alert_user(Transport, Tracker, Socket, From, Alert, Role).
-
-alert_user(Transport, Tracker, Socket, From, Alert, Role) ->
- alert_user(Transport, Tracker, Socket, false, no_pid, From, Alert, Role).
-
-alert_user(_, _, _, false = Active, Pid, From, Alert, Role) when From =/= undefined ->
- %% If there is an outstanding ssl_accept | recv
- %% From will be defined and send_or_reply will
- %% send the appropriate error message.
- ReasonCode = ssl_alert:reason_code(Alert, Role),
- send_or_reply(Active, Pid, From, {error, ReasonCode});
-alert_user(Transport, Tracker, Socket, Active, Pid, From, Alert, Role) ->
- case ssl_alert:reason_code(Alert, Role) of
- closed ->
- send_or_reply(Active, Pid, From,
- {ssl_closed, ssl_socket:socket(self(),
- Transport, Socket, ?MODULE, Tracker)});
- ReasonCode ->
- send_or_reply(Active, Pid, From,
- {ssl_error, ssl_socket:socket(self(),
- Transport, Socket, ?MODULE, Tracker), ReasonCode})
- end.
-
-log_alert(true, Info, Alert) ->
- Txt = ssl_alert:alert_txt(Alert),
- error_logger:format("SSL: ~p: ~s\n", [Info, Txt]);
-log_alert(false, _, _) ->
- ok.
-
-handle_own_alert(Alert, Version, StateName,
- #state{transport_cb = Transport,
- socket = Socket,
- connection_states = ConnectionStates,
- ssl_options = SslOpts} = State) ->
- try %% Try to tell the other side
- {BinMsg, _} =
- ssl_alert:encode(Alert, Version, ConnectionStates),
- Transport:send(Socket, BinMsg)
- catch _:_ -> %% Can crash if we are in a uninitialized state
- ignore
- end,
- try %% Try to tell the local user
- log_alert(SslOpts#ssl_options.log_alert, StateName, Alert),
- handle_normal_shutdown(Alert,StateName, State)
- catch _:_ ->
- ok
- end,
- {stop, {shutdown, own_alert}}.
-
-handle_normal_shutdown(Alert, _, #state{socket = Socket,
- transport_cb = Transport,
- start_or_recv_from = StartFrom,
- tracker = Tracker,
- role = Role, renegotiation = {false, first}}) ->
- alert_user(Transport, Tracker,Socket, StartFrom, Alert, Role);
-
-handle_normal_shutdown(Alert, StateName, #state{socket = Socket,
- socket_options = Opts,
- transport_cb = Transport,
- user_application = {_Mon, Pid},
- tracker = Tracker,
- start_or_recv_from = RecvFrom, role = Role}) ->
- alert_user(Transport, Tracker, Socket, StateName, Opts, Pid, RecvFrom, Alert, Role).
-
-handle_close_alert(Data, StateName, State0) ->
- case next_tls_record(Data, State0) of
- {#ssl_tls{type = ?ALERT, fragment = EncAlerts}, State} ->
- [Alert|_] = decode_alerts(EncAlerts),
- handle_normal_shutdown(Alert, StateName, State);
- _ ->
- ok
- end.
-
-cancel_timer(undefined) ->
- ok;
-cancel_timer(Timer) ->
- erlang:cancel_timer(Timer),
- ok.
-
-invalidate_session(client, Host, Port, Session) ->
- ssl_manager:invalidate_session(Host, Port, Session);
-invalidate_session(server, _, Port, Session) ->
- ssl_manager:invalidate_session(Port, Session).
%% User closes or recursive call!
close({close, Timeout}, Socket, Transport = gen_tcp, _,_) ->
@@ -1011,31 +677,44 @@ convert_options_partial_chain(Options, up) ->
convert_options_partial_chain(Options, down) ->
list_to_tuple(proplists:delete(partial_chain, tuple_to_list(Options))).
-handle_sni_extension(#client_hello{extensions = HelloExtensions}, State0) ->
- case HelloExtensions#hello_extensions.sni of
- undefined ->
- State0;
- #sni{hostname = Hostname} ->
- NewOptions = update_ssl_options_from_sni(State0#state.ssl_options, Hostname),
- case NewOptions of
- undefined ->
- State0;
- _ ->
- {ok, Ref, CertDbHandle, FileRefHandle, CacheHandle, CRLDbHandle, OwnCert, Key, DHParams} =
- ssl_config:init(NewOptions, State0#state.role),
- State0#state{
- session = State0#state.session#session{own_certificate = OwnCert},
- file_ref_db = FileRefHandle,
- cert_db_ref = Ref,
- cert_db = CertDbHandle,
- crl_db = CRLDbHandle,
- session_cache = CacheHandle,
- private_key = Key,
- diffie_hellman_params = DHParams,
- ssl_options = NewOptions,
- sni_hostname = Hostname
- }
- end
+gen_handshake(GenConnection, StateName, Type, Event,
+ #state{negotiated_version = Version} = State) ->
+ try GenConnection:StateName(Type, Event, State, ?MODULE) of
+ Result ->
+ Result
+ catch
+ _:_ ->
+ ssl_connection:handle_own_alert(?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE,
+ malformed_handshake_data),
+ Version, StateName, State)
+ end.
+
+gen_info(Event, connection = StateName, #state{negotiated_version = Version} = State) ->
+ try handle_info(Event, StateName, State) of
+ Result ->
+ Result
+ catch
+ _:_ ->
+ ssl_connection:handle_own_alert(?ALERT_REC(?FATAL, ?INTERNAL_ERROR,
+ malformed_data),
+ Version, StateName, State)
end;
-handle_sni_extension(_, State) ->
- State.
+
+gen_info(Event, StateName, #state{negotiated_version = Version} = State) ->
+ try handle_info(Event, StateName, State) of
+ Result ->
+ Result
+ catch
+ _:_ ->
+ ssl_connection:handle_own_alert(?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE,
+ malformed_handshake_data),
+ Version, StateName, State)
+ end.
+
+unprocessed_events(Events) ->
+ %% The first handshake event will be processed immediately
+ %% as it is entered first in the event queue and
+ %% when it is processed there will be length(Events)-1
+ %% handshake events left to process before we should
+ %% process more TLS-records received on the socket.
+ erlang:length(Events)-1.
diff --git a/lib/ssl/src/tls_handshake.erl b/lib/ssl/src/tls_handshake.erl
index 566b7db332..a2486bf752 100644
--- a/lib/ssl/src/tls_handshake.erl
+++ b/lib/ssl/src/tls_handshake.erl
@@ -41,7 +41,7 @@
%% Internal application API
%%====================================================================
%%--------------------------------------------------------------------
--spec client_hello(host(), inet:port_number(), #connection_states{},
+-spec client_hello(host(), inet:port_number(), ssl_record:connection_states(),
#ssl_options{}, integer(), atom(), boolean(), der_cert()) ->
#client_hello{}.
%%
@@ -54,8 +54,7 @@ client_hello(Host, Port, ConnectionStates,
} = SslOpts,
Cache, CacheCb, Renegotiation, OwnCert) ->
Version = tls_record:highest_protocol_version(Versions),
- Pending = ssl_record:pending_connection_state(ConnectionStates, read),
- SecParams = Pending#connection_state.security_parameters,
+ #{security_parameters := SecParams} = ssl_record:pending_connection_state(ConnectionStates, read),
AvailableCipherSuites = ssl_handshake:available_suites(UserSuites, Version),
Extensions = ssl_handshake:client_hello_extensions(Host, Version,
AvailableCipherSuites,
@@ -78,14 +77,14 @@ client_hello(Host, Port, ConnectionStates,
%%--------------------------------------------------------------------
-spec hello(#server_hello{} | #client_hello{}, #ssl_options{},
- #connection_states{} | {inet:port_number(), #session{}, db_handle(),
- atom(), #connection_states{},
+ ssl_record:connection_states() | {inet:port_number(), #session{}, db_handle(),
+ atom(), ssl_record:connection_states(),
binary() | undefined, ssl_cipher:key_algo()},
boolean()) ->
{tls_record:tls_version(), session_id(),
- #connection_states{}, alpn | npn, binary() | undefined}|
+ ssl_record:connection_states(), alpn | npn, binary() | undefined}|
{tls_record:tls_version(), {resumed | new, #session{}},
- #connection_states{}, binary() | undefined,
+ ssl_record:connection_states(), binary() | undefined,
#hello_extensions{}, {ssl_cipher:hash(), ssl_cipher:sign_algo()} | undefined} |
#alert{}.
%%
@@ -109,19 +108,25 @@ hello(#client_hello{client_version = ClientVersion,
cipher_suites = CipherSuites} = Hello,
#ssl_options{versions = Versions} = SslOpts,
Info, Renegotiation) ->
- Version = ssl_handshake:select_version(tls_record, ClientVersion, Versions),
- case ssl_cipher:is_fallback(CipherSuites) of
+ try
+ Version = ssl_handshake:select_version(tls_record, ClientVersion, Versions),
+ case ssl_cipher:is_fallback(CipherSuites) of
true ->
- Highest = tls_record:highest_protocol_version(Versions),
- case tls_record:is_higher(Highest, Version) of
- true ->
- ?ALERT_REC(?FATAL, ?INAPPROPRIATE_FALLBACK);
- false ->
- handle_client_hello(Version, Hello, SslOpts, Info, Renegotiation)
- end;
- false ->
- handle_client_hello(Version, Hello, SslOpts, Info, Renegotiation)
- end.
+ Highest = tls_record:highest_protocol_version(Versions),
+ case tls_record:is_higher(Highest, Version) of
+ true ->
+ ?ALERT_REC(?FATAL, ?INAPPROPRIATE_FALLBACK);
+ false ->
+ handle_client_hello(Version, Hello, SslOpts, Info, Renegotiation)
+ end;
+ false ->
+ handle_client_hello(Version, Hello, SslOpts, Info, Renegotiation)
+ end
+ catch
+ _:_ ->
+ ?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE, malformed_handshake_data)
+ end.
+
%%--------------------------------------------------------------------
-spec encode_handshake(tls_handshake(), tls_record:tls_version()) -> iolist().
%%
@@ -187,8 +192,13 @@ handle_client_hello(Version, #client_hello{session_id = SugesstedId,
get_tls_handshake_aux(Version, <<?BYTE(Type), ?UINT24(Length),
Body:Length/binary,Rest/binary>>, #ssl_options{v2_hello_compatible = V2Hello} = Opts, Acc) ->
Raw = <<?BYTE(Type), ?UINT24(Length), Body/binary>>,
- Handshake = decode_handshake(Version, Type, Body, V2Hello),
- get_tls_handshake_aux(Version, Rest, Opts, [{Handshake,Raw} | Acc]);
+ try decode_handshake(Version, Type, Body, V2Hello) of
+ Handshake ->
+ get_tls_handshake_aux(Version, Rest, Opts, [{Handshake,Raw} | Acc])
+ catch
+ _:_ ->
+ throw(?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE, handshake_decode_error))
+ end;
get_tls_handshake_aux(_Version, Data, _, Acc) ->
{lists:reverse(Acc), Data}.
diff --git a/lib/ssl/src/tls_record.erl b/lib/ssl/src/tls_record.erl
index 9348c8bbdd..5331dd1303 100644
--- a/lib/ssl/src/tls_record.erl
+++ b/lib/ssl/src/tls_record.erl
@@ -32,7 +32,7 @@
-include("ssl_cipher.hrl").
%% Handling of incoming data
--export([get_tls_records/2]).
+-export([get_tls_records/2, init_connection_states/2]).
%% Decoding
-export([decode_cipher_text/3]).
@@ -56,12 +56,28 @@
%%====================================================================
%% Internal application API
%%====================================================================
+%%--------------------------------------------------------------------
+-spec init_connection_states(client | server, one_n_minus_one | zero_n | disabled) ->
+ ssl_record:connection_states().
+%% %
+ %
+%% Description: Creates a connection_states record with appropriate
+%% values for the initial SSL connection setup.
+%%--------------------------------------------------------------------
+init_connection_states(Role, BeastMitigation) ->
+ ConnectionEnd = ssl_record:record_protocol_role(Role),
+ Current = initial_connection_state(ConnectionEnd, BeastMitigation),
+ Pending = ssl_record:empty_connection_state(ConnectionEnd, BeastMitigation),
+ #{current_read => Current,
+ pending_read => Pending,
+ current_write => Current,
+ pending_write => Pending}.
%%--------------------------------------------------------------------
-spec get_tls_records(binary(), binary()) -> {[binary()], binary()} | #alert{}.
%%
-%% Description: Given old buffer and new data from TCP, packs up a records
%% and returns it as a list of tls_compressed binaries also returns leftover
+%% Description: Given old buffer and new data from TCP, packs up a records
%% data
%%--------------------------------------------------------------------
get_tls_records(Data, <<>>) ->
@@ -129,63 +145,61 @@ get_tls_records_aux(Data, Acc) ->
end.
encode_plain_text(Type, Version, Data,
- #connection_states{current_write =
- #connection_state{
- sequence_number = Seq,
- compression_state=CompS0,
- security_parameters=
- #security_parameters{
- cipher_type = ?AEAD,
- compression_algorithm=CompAlg}
- }= WriteState0} = ConnectionStates) ->
+ #{current_write :=
+ #{sequence_number := Seq,
+ compression_state := CompS0,
+ security_parameters :=
+ #security_parameters{
+ cipher_type = ?AEAD,
+ compression_algorithm = CompAlg}
+ }= WriteState0} = ConnectionStates) ->
{Comp, CompS1} = ssl_record:compress(CompAlg, Data, CompS0),
- WriteState1 = WriteState0#connection_state{compression_state = CompS1},
+ WriteState1 = WriteState0#{compression_state => CompS1},
AAD = calc_aad(Type, Version, WriteState1),
{CipherFragment, WriteState} = ssl_record:cipher_aead(Version, Comp, WriteState1, AAD),
CipherText = encode_tls_cipher_text(Type, Version, CipherFragment),
- {CipherText, ConnectionStates#connection_states{current_write = WriteState#connection_state{sequence_number = Seq +1}}};
+ {CipherText, ConnectionStates#{current_write => WriteState#{sequence_number => Seq +1}}};
encode_plain_text(Type, Version, Data,
- #connection_states{current_write =
- #connection_state{
- sequence_number = Seq,
- compression_state=CompS0,
- security_parameters=
- #security_parameters{compression_algorithm=CompAlg}
- }= WriteState0} = ConnectionStates) ->
+ #{current_write :=
+ #{sequence_number := Seq,
+ compression_state := CompS0,
+ security_parameters :=
+ #security_parameters{compression_algorithm = CompAlg}
+ }= WriteState0} = ConnectionStates) ->
{Comp, CompS1} = ssl_record:compress(CompAlg, Data, CompS0),
- WriteState1 = WriteState0#connection_state{compression_state = CompS1},
+ WriteState1 = WriteState0#{compression_state => CompS1},
MacHash = calc_mac_hash(Type, Version, Comp, WriteState1),
{CipherFragment, WriteState} = ssl_record:cipher(Version, Comp, WriteState1, MacHash),
CipherText = encode_tls_cipher_text(Type, Version, CipherFragment),
- {CipherText, ConnectionStates#connection_states{current_write = WriteState#connection_state{sequence_number = Seq +1}}}.
+ {CipherText, ConnectionStates#{current_write => WriteState#{sequence_number => Seq +1}}};
+encode_plain_text(_,_,_, CS) ->
+ exit({cs, CS}).
%%--------------------------------------------------------------------
--spec decode_cipher_text(#ssl_tls{}, #connection_states{}, boolean()) ->
- {#ssl_tls{}, #connection_states{}}| #alert{}.
+-spec decode_cipher_text(#ssl_tls{}, ssl_record:connection_states(), boolean()) ->
+ {#ssl_tls{}, ssl_record:connection_states()}| #alert{}.
%%
%% Description: Decode cipher text
%%--------------------------------------------------------------------
decode_cipher_text(#ssl_tls{type = Type, version = Version,
fragment = CipherFragment} = CipherText,
- #connection_states{current_read =
- #connection_state{
- compression_state = CompressionS0,
- sequence_number = Seq,
- security_parameters=
- #security_parameters{
- cipher_type = ?AEAD,
- compression_algorithm=CompAlg}
- } = ReadState0} = ConnnectionStates0, _) ->
+ #{current_read :=
+ #{compression_state := CompressionS0,
+ sequence_number := Seq,
+ security_parameters :=
+ #security_parameters{
+ cipher_type = ?AEAD,
+ compression_algorithm = CompAlg}
+ } = ReadState0} = ConnnectionStates0, _) ->
AAD = calc_aad(Type, Version, ReadState0),
case ssl_record:decipher_aead(Version, CipherFragment, ReadState0, AAD) of
{PlainFragment, ReadState1} ->
{Plain, CompressionS1} = ssl_record:uncompress(CompAlg,
PlainFragment, CompressionS0),
- ConnnectionStates = ConnnectionStates0#connection_states{
- current_read = ReadState1#connection_state{
- sequence_number = Seq + 1,
- compression_state = CompressionS1}},
+ ConnnectionStates = ConnnectionStates0#{
+ current_read => ReadState1#{sequence_number => Seq + 1,
+ compression_state => CompressionS1}},
{CipherText#ssl_tls{fragment = Plain}, ConnnectionStates};
#alert{} = Alert ->
Alert
@@ -193,13 +207,12 @@ decode_cipher_text(#ssl_tls{type = Type, version = Version,
decode_cipher_text(#ssl_tls{type = Type, version = Version,
fragment = CipherFragment} = CipherText,
- #connection_states{current_read =
- #connection_state{
- compression_state = CompressionS0,
- sequence_number = Seq,
- security_parameters=
- #security_parameters{compression_algorithm=CompAlg}
- } = ReadState0} = ConnnectionStates0, PaddingCheck) ->
+ #{current_read :=
+ #{compression_state := CompressionS0,
+ sequence_number := Seq,
+ security_parameters :=
+ #security_parameters{compression_algorithm = CompAlg}
+ } = ReadState0} = ConnnectionStates0, PaddingCheck) ->
case ssl_record:decipher(Version, CipherFragment, ReadState0, PaddingCheck) of
{PlainFragment, Mac, ReadState1} ->
MacHash = calc_mac_hash(Type, Version, PlainFragment, ReadState1),
@@ -207,10 +220,10 @@ decode_cipher_text(#ssl_tls{type = Type, version = Version,
true ->
{Plain, CompressionS1} = ssl_record:uncompress(CompAlg,
PlainFragment, CompressionS0),
- ConnnectionStates = ConnnectionStates0#connection_states{
- current_read = ReadState1#connection_state{
- sequence_number = Seq + 1,
- compression_state = CompressionS1}},
+ ConnnectionStates = ConnnectionStates0#{
+ current_read => ReadState1#{
+ sequence_number => Seq + 1,
+ compression_state => CompressionS1}},
{CipherText#ssl_tls{fragment = Plain}, ConnnectionStates};
false ->
?ALERT_REC(?FATAL, ?BAD_RECORD_MAC)
@@ -375,6 +388,18 @@ is_acceptable_version(_,_) ->
%%--------------------------------------------------------------------
%%% Internal functions
%%--------------------------------------------------------------------
+initial_connection_state(ConnectionEnd, BeastMitigation) ->
+ #{security_parameters =>
+ ssl_record:initial_security_params(ConnectionEnd),
+ sequence_number => 0,
+ beast_mitigation => BeastMitigation,
+ compression_state => undefined,
+ cipher_state => undefined,
+ mac_secret => undefined,
+ secure_renegotiation => undefined,
+ client_verify_data => undefined,
+ server_verify_data => undefined
+ }.
lowest_list_protocol_version(Ver, []) ->
Ver;
@@ -413,15 +438,15 @@ sufficient_tlsv1_2_crypto_support() ->
proplists:get_bool(sha256, proplists:get_value(hashs, CryptoSupport)).
calc_mac_hash(Type, Version,
- PlainFragment, #connection_state{sequence_number = SeqNo,
- mac_secret = MacSecret,
- security_parameters =
- SecPars}) ->
+ PlainFragment, #{sequence_number := SeqNo,
+ mac_secret := MacSecret,
+ security_parameters:=
+ SecPars}) ->
Length = erlang:iolist_size(PlainFragment),
mac_hash(Version, SecPars#security_parameters.mac_algorithm,
MacSecret, SeqNo, Type,
Length, PlainFragment).
calc_aad(Type, {MajVer, MinVer},
- #connection_state{sequence_number = SeqNo}) ->
+ #{sequence_number := SeqNo}) ->
<<SeqNo:64/integer, ?BYTE(Type), ?BYTE(MajVer), ?BYTE(MinVer)>>.
diff --git a/lib/ssl/test/ssl.spec b/lib/ssl/test/ssl.spec
index 86e14c033e..0ad94e22bc 100644
--- a/lib/ssl/test/ssl.spec
+++ b/lib/ssl/test/ssl.spec
@@ -1,4 +1,5 @@
{suites,"../ssl_test",all}.
{skip_cases, "../ssl_test",
- ssl_bench_SUITE, [setup_sequential, setup_concurrent, payload_simple],
+ ssl_bench_SUITE, [setup_sequential, setup_concurrent, payload_simple,
+ use_pem_cache, bypass_pem_cache],
"Benchmarks run separately"}.
diff --git a/lib/ssl/test/ssl_ECC_SUITE.erl b/lib/ssl/test/ssl_ECC_SUITE.erl
index 69ac9908fa..258922d128 100644
--- a/lib/ssl/test/ssl_ECC_SUITE.erl
+++ b/lib/ssl/test/ssl_ECC_SUITE.erl
@@ -145,7 +145,7 @@ init_per_testcase(TestCase, Config) ->
ssl_test_lib:ct_log_supported_protocol_versions(Config),
ct:log("Ciphers: ~p~n ", [ ssl:cipher_suites()]),
end_per_testcase(TestCase, Config),
- ssl:start(),
+ ssl_test_lib:clean_start(),
ct:timetrap({seconds, 15}),
Config.
diff --git a/lib/ssl/test/ssl_alpn_handshake_SUITE.erl b/lib/ssl/test/ssl_alpn_handshake_SUITE.erl
index da181faf64..9d57e89b9b 100644
--- a/lib/ssl/test/ssl_alpn_handshake_SUITE.erl
+++ b/lib/ssl/test/ssl_alpn_handshake_SUITE.erl
@@ -71,7 +71,7 @@ init_per_suite(Config) ->
catch crypto:stop(),
try crypto:start() of
ok ->
- ssl:start(),
+ ssl_test_lib:clean_start(),
{ok, _} = make_certs:all(proplists:get_value(data_dir, Config),
proplists:get_value(priv_dir, Config)),
ssl_test_lib:cert_options(Config)
diff --git a/lib/ssl/test/ssl_basic_SUITE.erl b/lib/ssl/test/ssl_basic_SUITE.erl
index 665dbb1df3..322f93b94c 100644
--- a/lib/ssl/test/ssl_basic_SUITE.erl
+++ b/lib/ssl/test/ssl_basic_SUITE.erl
@@ -40,6 +40,7 @@
-define(SLEEP, 500).
-define(RENEGOTIATION_DISABLE_TIME, 12000).
-define(CLEAN_SESSION_DB, 60000).
+-define(SEC_RENEGOTIATION_TIMEOUT, 30).
%%--------------------------------------------------------------------
%% Common Test interface functions -----------------------------------
@@ -249,7 +250,7 @@ init_per_suite(Config0) ->
catch crypto:stop(),
try crypto:start() of
ok ->
- ssl:start(),
+ ssl_test_lib:clean_start(),
%% make rsa certs using oppenssl
{ok, _} = make_certs:all(proplists:get_value(data_dir, Config0),
proplists:get_value(priv_dir, Config0)),
@@ -306,6 +307,7 @@ init_per_testcase(protocol_versions, Config) ->
init_per_testcase(reuse_session_expired, Config) ->
ssl:stop(),
application:load(ssl),
+ ssl_test_lib:clean_env(),
application:set_env(ssl, session_lifetime, ?EXPIRE),
application:set_env(ssl, session_delay_cleanup_time, 500),
ssl:start(),
@@ -315,6 +317,7 @@ init_per_testcase(reuse_session_expired, Config) ->
init_per_testcase(empty_protocol_versions, Config) ->
ssl:stop(),
application:load(ssl),
+ ssl_test_lib:clean_env(),
application:set_env(ssl, protocol_version, []),
ssl:start(),
ct:timetrap({seconds, 5}),
@@ -340,7 +343,7 @@ init_per_testcase(TestCase, Config) when TestCase == client_renegotiate;
TestCase == renegotiate_dos_mitigate_passive;
TestCase == renegotiate_dos_mitigate_absolute ->
ssl_test_lib:ct_log_supported_protocol_versions(Config),
- ct:timetrap({seconds, 30}),
+ ct:timetrap({seconds, ?SEC_RENEGOTIATION_TIMEOUT + 5}),
Config;
init_per_testcase(TestCase, Config) when TestCase == psk_cipher_suites;
@@ -350,6 +353,11 @@ init_per_testcase(TestCase, Config) when TestCase == psk_cipher_suites;
TestCase == ciphers_dsa_signed_certs;
TestCase == ciphers_dsa_signed_certs_openssl_names;
TestCase == anonymous_cipher_suites;
+ TestCase == ciphers_ecdsa_signed_certs;
+ TestCase == ciphers_ecdsa_signed_certs_openssl_names;
+ TestCase == anonymous_cipher_suites;
+ TestCase == psk_anon_cipher_suites;
+ TestCase == psk_anon_with_hint_cipher_suites;
TestCase == versions_option,
TestCase == tls_tcp_connect_big ->
ssl_test_lib:ct_log_supported_protocol_versions(Config),
@@ -435,7 +443,9 @@ init_per_testcase(accept_pool, Config) ->
ssl_test_lib:ct_log_supported_protocol_versions(Config),
Config
end;
-
+init_per_testcase(controller_dies, Config) ->
+ ct:timetrap({seconds, 10}),
+ Config;
init_per_testcase(_TestCase, Config) ->
ssl_test_lib:ct_log_supported_protocol_versions(Config),
ct:timetrap({seconds, 5}),
@@ -446,6 +456,11 @@ end_per_testcase(reuse_session_expired, Config) ->
application:unset_env(ssl, session_delay_cleanup_time),
end_per_testcase(default_action, Config);
+end_per_testcase(Case, Config) when Case == protocol_versions;
+ Case == empty_protocol_versions->
+ application:unset_env(ssl, protocol_versions),
+ end_per_testcase(default_action, Config);
+
end_per_testcase(_TestCase, Config) ->
Config.
@@ -2156,7 +2171,7 @@ anonymous_cipher_suites()->
[{doc,"Test the anonymous ciphersuites"}].
anonymous_cipher_suites(Config) when is_list(Config) ->
Version = ssl_test_lib:protocol_version(Config),
- Ciphers = ssl_test_lib:anonymous_suites(),
+ Ciphers = ssl_test_lib:anonymous_suites(Version),
run_suites(Ciphers, Version, Config, anonymous).
%%-------------------------------------------------------------------
psk_cipher_suites() ->
@@ -2257,8 +2272,8 @@ default_reject_anonymous(Config) when is_list(Config) ->
{ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
ClientOpts = ssl_test_lib:ssl_options(client_opts, Config),
ServerOpts = ssl_test_lib:ssl_options(server_opts, Config),
-
- [Cipher | _] = ssl_test_lib:anonymous_suites(),
+ Version = tls_record:highest_protocol_version(tls_record:supported_protocol_versions()),
+ [CipherSuite | _] = ssl_test_lib:anonymous_suites(Version),
Server = ssl_test_lib:start_server_error([{node, ServerNode}, {port, 0},
{from, self()},
@@ -2268,7 +2283,7 @@ default_reject_anonymous(Config) when is_list(Config) ->
{host, Hostname},
{from, self()},
{options,
- [{ciphers,[Cipher]} |
+ [{ciphers,[CipherSuite]} |
ClientOpts]}]),
ssl_test_lib:check_result(Server, {error, {tls_alert, "insufficient security"}},
@@ -4293,7 +4308,7 @@ erlang_ssl_receive(Socket, Data) ->
erlang_ssl_receive(Socket, tl(Data));
Other ->
ct:fail({unexpected_message, Other})
- after ?SLEEP * 3 * test_server:timetrap_scale_factor() ->
+ after timer:seconds(?SEC_RENEGOTIATION_TIMEOUT) * test_server:timetrap_scale_factor() ->
ct:fail({did_not_get, Data})
end.
@@ -4422,7 +4437,7 @@ run_suites(Ciphers, Version, Config, Type) ->
anonymous ->
%% No certs in opts!
{ssl_test_lib:ssl_options(client_verification_opts, Config),
- ssl_test_lib:ssl_options(server_anon, Config)};
+ [{reuseaddr, true}, {ciphers, ssl_test_lib:anonymous_suites(Version)}]};
psk ->
{ssl_test_lib:ssl_options(client_psk, Config),
ssl_test_lib:ssl_options(server_psk, Config)};
diff --git a/lib/ssl/test/ssl_bench_SUITE.erl b/lib/ssl/test/ssl_bench_SUITE.erl
index ed439a425f..21989f8d99 100644
--- a/lib/ssl/test/ssl_bench_SUITE.erl
+++ b/lib/ssl/test/ssl_bench_SUITE.erl
@@ -25,11 +25,12 @@
suite() -> [{ct_hooks,[{ts_install_cth,[{nodenames,2}]}]}].
-all() -> [{group, setup}, {group, payload}].
+all() -> [{group, setup}, {group, payload}, {group, pem_cache}].
groups() ->
[{setup, [{repeat, 3}], [setup_sequential, setup_concurrent]},
- {payload, [{repeat, 3}], [payload_simple]}
+ {payload, [{repeat, 3}], [payload_simple]},
+ {pem_cache, [{repeat, 3}], [use_pem_cache, bypass_pem_cache]}
].
init_per_group(_GroupName, Config) ->
@@ -49,9 +50,33 @@ init_per_suite(Config) ->
end_per_suite(_Config) ->
ok.
+init_per_testcase(use_pem_cache, Conf) ->
+ case bypass_pem_cache_supported() of
+ false -> {skipped, "PEM cache bypass support required"};
+ true ->
+ application:set_env(ssl, bypass_pem_cache, false),
+ Conf
+ end;
+init_per_testcase(bypass_pem_cache, Conf) ->
+ case bypass_pem_cache_supported() of
+ false -> {skipped, "PEM cache bypass support required"};
+ true ->
+ application:set_env(ssl, bypass_pem_cache, true),
+ Conf
+ end;
init_per_testcase(_Func, Conf) ->
Conf.
+end_per_testcase(use_pem_cache, _Config) ->
+ case bypass_pem_cache_supported() of
+ false -> ok;
+ true -> application:set_env(ssl, bypass_pem_cache, false)
+ end;
+end_per_testcase(bypass_pem_cache, _Config) ->
+ case bypass_pem_cache_supported() of
+ false -> ok;
+ true -> application:set_env(ssl, bypass_pem_cache, false)
+ end;
end_per_testcase(_Func, _Conf) ->
ok.
@@ -94,6 +119,18 @@ payload_simple(Config) ->
{suite, "ssl"}, {name, "Payload simple"}]}),
ok.
+use_pem_cache(_Config) ->
+ {ok, Result} = do_test(ssl, pem_cache, 100, 500, node()),
+ ct_event:notify(#event{name = benchmark_data,
+ data=[{value, Result},
+ {suite, "ssl"}, {name, "Use PEM cache"}]}).
+
+bypass_pem_cache(_Config) ->
+ {ok, Result} = do_test(ssl, pem_cache, 100, 500, node()),
+ ct_event:notify(#event{name = benchmark_data,
+ data=[{value, Result},
+ {suite, "ssl"}, {name, "Bypass PEM cache"}]}).
+
ssl() ->
test(ssl, ?COUNT, node()).
@@ -172,6 +209,18 @@ server_init(ssl, payload, Loop, _, Server) ->
ssl:close(TSocket)
end,
setup_server_connection(Socket, Test);
+server_init(ssl, pem_cache, Loop, _, Server) ->
+ {ok, Socket} = ssl:listen(0, ssl_opts(listen_der)),
+ {ok, {_Host, Port}} = ssl:sockname(Socket),
+ {ok, Host} = inet:gethostname(),
+ Server ! {self(), {init, Host, Port}},
+ Test = fun(TSocket) ->
+ ok = ssl:ssl_accept(TSocket),
+ Size = byte_size(msg()),
+ server_echo(TSocket, Size, Loop),
+ ssl:close(TSocket)
+ end,
+ setup_server_connection(Socket, Test);
server_init(Type, Tc, _, _, Server) ->
io:format("No server init code for ~p ~p~n",[Type, Tc]),
@@ -185,6 +234,11 @@ client_init(Master, ssl, payload, Host, Port) ->
Master ! {self(), init},
Size = byte_size(msg()),
{Sock, Size};
+client_init(Master, ssl, pem_cache, Host, Port) ->
+ {ok, Sock} = ssl:connect(Host, Port, ssl_opts(connect_der)),
+ Master ! {self(), init},
+ Size = byte_size(msg()),
+ {Sock, Size};
client_init(_Me, Type, Tc, Host, Port) ->
io:format("No client init code for ~p ~p~n",[Type, Tc]),
{Host, Port}.
@@ -228,6 +282,13 @@ payload(Loop, ssl, D = {Socket, Size}) when Loop > 0 ->
payload(_, _, {Socket, _}) ->
ssl:close(Socket).
+pem_cache(N, ssl, Data = {Socket, Size}) when N > 0 ->
+ ok = ssl:send(Socket, msg()),
+ {ok, _} = ssl:recv(Socket, Size),
+ pem_cache(N-1, ssl, Data);
+pem_cache(_, _, {Socket, _}) ->
+ ssl:close(Socket).
+
msg() ->
<<"Hello",
0:(512*8),
@@ -352,16 +413,43 @@ stop_profile(fprof, File) ->
ssl_opts(listen) ->
[{backlog, 500} | ssl_opts("server")];
ssl_opts(connect) ->
- [{verify, verify_peer}
- | ssl_opts("client")];
+ [{verify, verify_peer} | ssl_opts("client")];
+ssl_opts(listen_der) ->
+ [{backlog, 500} | ssl_opts("server_der")];
+ssl_opts(connect_der) ->
+ [{verify, verify_peer} | ssl_opts("client_der")];
ssl_opts(Role) ->
- Dir = filename:join([code:lib_dir(ssl), "examples", "certs", "etc"]),
+ CertData = cert_data(Role),
[{active, false},
{depth, 2},
{reuseaddr, true},
{mode,binary},
{nodelay, true},
- {ciphers, [{dhe_rsa,aes_256_cbc,sha}]},
- {cacertfile, filename:join([Dir, Role, "cacerts.pem"])},
+ {ciphers, [{dhe_rsa,aes_256_cbc,sha}]}
+ |CertData].
+
+cert_data(Der) when Der =:= "server_der"; Der =:= "client_der" ->
+ [Role,_] = string:tokens(Der, "_"),
+ Dir = filename:join([code:lib_dir(ssl), "examples", "certs", "etc"]),
+ {ok, CaCert0} = file:read_file(filename:join([Dir, Role, "cacerts.pem"])),
+ {ok, Cert0} = file:read_file(filename:join([Dir, Role, "cert.pem"])),
+ {ok, Key0} = file:read_file(filename:join([Dir, Role, "key.pem"])),
+ [{_, Cert, _}] = public_key:pem_decode(Cert0),
+ CaCert1 = public_key:pem_decode(CaCert0),
+ CaCert = [CCert || {_, CCert, _} <- CaCert1],
+ [{KeyType, Key, _}] = public_key:pem_decode(Key0),
+ [{cert, Cert},
+ {cacerts, CaCert},
+ {key, {KeyType, Key}}];
+cert_data(Role) ->
+ Dir = filename:join([code:lib_dir(ssl), "examples", "certs", "etc"]),
+ [{cacertfile, filename:join([Dir, Role, "cacerts.pem"])},
{certfile, filename:join([Dir, Role, "cert.pem"])},
{keyfile, filename:join([Dir, Role, "key.pem"])}].
+
+bypass_pem_cache_supported() ->
+ %% This function is currently critical to support cache bypass
+ %% and did not exist in prior versions.
+ catch ssl_pkix_db:module_info(), % ensure module is loaded
+ erlang:function_exported(ssl_pkix_db, extract_trusted_certs, 1).
+
diff --git a/lib/ssl/test/ssl_certificate_verify_SUITE.erl b/lib/ssl/test/ssl_certificate_verify_SUITE.erl
index c83c513eb3..4c6f1d7c01 100644
--- a/lib/ssl/test/ssl_certificate_verify_SUITE.erl
+++ b/lib/ssl/test/ssl_certificate_verify_SUITE.erl
@@ -85,7 +85,7 @@ init_per_suite(Config0) ->
catch crypto:stop(),
try crypto:start() of
ok ->
- ssl:start(),
+ ssl_test_lib:clean_start(),
%% make rsa certs using oppenssl
{ok, _} = make_certs:all(proplists:get_value(data_dir, Config0),
proplists:get_value(priv_dir, Config0)),
diff --git a/lib/ssl/test/ssl_crl_SUITE.erl b/lib/ssl/test/ssl_crl_SUITE.erl
index 00636e5660..bc2822f0c4 100644
--- a/lib/ssl/test/ssl_crl_SUITE.erl
+++ b/lib/ssl/test/ssl_crl_SUITE.erl
@@ -72,7 +72,7 @@ init_per_suite(Config) ->
false ->
{skip, io_lib:format("Bad openssl version: ~p",[OpenSSL_version])};
_ ->
- catch crypto:stop(),
+ end_per_suite(Config),
try crypto:start() of
ok ->
{ok, Hostname0} = inet:gethostname(),
@@ -136,7 +136,7 @@ init_per_testcase(Case, Config0) ->
true ->
end_per_testcase(Case, Config0),
inets:start(),
- ssl:start(),
+ ssl_test_lib:clean_start(),
ServerRoot = make_dir_path([proplists:get_value(priv_dir, Config0), idp_crl, tmp]),
%% start a HTTP server to serve the CRLs
{ok, Httpd} = inets:start(httpd, [{ipfamily, proplists:get_value(ipfamily, Config0)},
@@ -155,7 +155,7 @@ init_per_testcase(Case, Config0) ->
[{cert_dir, CertDir} | Config];
false ->
end_per_testcase(Case, Config0),
- ssl:start(),
+ ssl_test_lib:clean_start(),
Config0
end.
diff --git a/lib/ssl/test/ssl_handshake_SUITE.erl b/lib/ssl/test/ssl_handshake_SUITE.erl
index a671e3e307..51f0651568 100644
--- a/lib/ssl/test/ssl_handshake_SUITE.erl
+++ b/lib/ssl/test/ssl_handshake_SUITE.erl
@@ -60,7 +60,7 @@ init_per_testcase(ignore_hassign_extension_pre_tls_1_2, Config0) ->
ok ->
case is_supported(sha512) of
true ->
- ssl:start(),
+ ssl_test_lib:clean_start(),
%% make rsa certs using oppenssl
{ok, _} = make_certs:all(proplists:get_value(data_dir, Config0),
proplists:get_value(priv_dir, Config0)),
diff --git a/lib/ssl/test/ssl_npn_handshake_SUITE.erl b/lib/ssl/test/ssl_npn_handshake_SUITE.erl
index c55fa73cfb..a02881f1ae 100644
--- a/lib/ssl/test/ssl_npn_handshake_SUITE.erl
+++ b/lib/ssl/test/ssl_npn_handshake_SUITE.erl
@@ -68,7 +68,7 @@ init_per_suite(Config) ->
catch crypto:stop(),
try crypto:start() of
ok ->
- ssl:start(),
+ ssl_test_lib:clean_start(),
{ok, _} = make_certs:all(proplists:get_value(data_dir, Config),
proplists:get_value(priv_dir, Config)),
ssl_test_lib:cert_options(Config)
diff --git a/lib/ssl/test/ssl_npn_hello_SUITE.erl b/lib/ssl/test/ssl_npn_hello_SUITE.erl
index 00eb9fee4f..69aeea10c5 100644
--- a/lib/ssl/test/ssl_npn_hello_SUITE.erl
+++ b/lib/ssl/test/ssl_npn_hello_SUITE.erl
@@ -41,6 +41,15 @@ all() ->
create_server_hello_with_advertised_protocols_test,
create_server_hello_with_no_advertised_protocols_test].
+init_per_suite(Config) ->
+ catch crypto:stop(),
+ try crypto:start() of
+ ok ->
+ Config
+ catch _:_ ->
+ {skip, "Crypto did not start"}
+ end.
+
init_per_testcase(_TestCase, Config) ->
ssl_test_lib:ct_log_supported_protocol_versions(Config),
ct:timetrap({seconds, 5}),
@@ -126,15 +135,12 @@ create_server_handshake(Npn) ->
}, Vsn).
create_connection_states() ->
- #connection_states{
- pending_read = #connection_state{
- security_parameters = #security_parameters{
+ #{pending_read => #{security_parameters => #security_parameters{
server_random = <<1:256>>,
compression_algorithm = 1,
cipher_suite = ?TLS_DHE_DSS_WITH_DES_CBC_SHA
}
- },
- current_read = #connection_state {
- secure_renegotiation = false
- }
- }.
+ },
+ current_read => #{secure_renegotiation => false
+ }
+ }.
diff --git a/lib/ssl/test/ssl_packet_SUITE.erl b/lib/ssl/test/ssl_packet_SUITE.erl
index e49d432c21..942e68967a 100644
--- a/lib/ssl/test/ssl_packet_SUITE.erl
+++ b/lib/ssl/test/ssl_packet_SUITE.erl
@@ -41,7 +41,7 @@
-define(MANY, 1000).
-define(SOME, 50).
--define(BASE_TIMEOUT_SECONDS, 15).
+-define(BASE_TIMEOUT_SECONDS, 30).
-define(SOME_SCALE, 20).
-define(MANY_SCALE, 20).
@@ -140,7 +140,7 @@ init_per_suite(Config) ->
catch crypto:stop(),
try crypto:start() of
ok ->
- ssl:start(),
+ ssl_test_lib:clean_start(),
{ok, _} = make_certs:all(proplists:get_value(data_dir, Config),
proplists:get_value(priv_dir, Config)),
ssl_test_lib:cert_options(Config)
@@ -162,6 +162,7 @@ init_per_group(GroupName, Config) ->
{skip, "Missing crypto support"}
end;
_ ->
+ ssl:stop(),
ssl:start(),
Config
end.
@@ -276,6 +277,7 @@ packet_raw_active_once_many_small() ->
[{doc,"Test packet option {packet, raw} in active once mode."}].
packet_raw_active_once_many_small(Config) when is_list(Config) ->
+ ct:timetrap({seconds, ?BASE_TIMEOUT_SECONDS * ?MANY_SCALE}),
Data = "Packet option is {packet, raw}",
packet(Config, Data, send_raw, active_once_raw, ?MANY, raw, once).
@@ -392,6 +394,7 @@ packet_0_active_some_big() ->
[{doc,"Test packet option {packet, 0} in active mode."}].
packet_0_active_some_big(Config) when is_list(Config) ->
+ ct:timetrap({seconds, ?BASE_TIMEOUT_SECONDS * ?SOME_SCALE}),
Data = lists:append(lists:duplicate(100, "1234567890")),
packet(Config, Data, send, active_raw, ?SOME, 0, true).
@@ -427,6 +430,7 @@ packet_2_active_some_big() ->
[{doc,"Test packet option {packet, 2} in active mode"}].
packet_2_active_some_big(Config) when is_list(Config) ->
+ ct:timetrap({seconds, ?BASE_TIMEOUT_SECONDS * ?SOME_SCALE}),
Data = lists:append(lists:duplicate(100, "1234567890")),
packet(Config, Data, send, active_packet, ?SOME, 2, true).
@@ -1900,6 +1904,31 @@ header_decode_two_bytes_one_sent_passive(Config) when is_list(Config) ->
%%--------------------------------------------------------------------
%% Internal functions ------------------------------------------------
%%--------------------------------------------------------------------
+
+packet(Config, Data, Send, Recv, Quantity, Packet, Active) when Packet == 0;
+ Packet == raw ->
+ ClientOpts = ssl_test_lib:ssl_options(client_opts, Config),
+ ServerOpts = ssl_test_lib:ssl_options(server_opts, Config),
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+
+ Server = ssl_test_lib:start_server([{node, ClientNode}, {port, 0},
+ {from, self()},
+ {mfa, {?MODULE, Send ,[Data, Quantity]}},
+ {options, [{nodelay, true},{packet, Packet} | ServerOpts]}]),
+ Port = ssl_test_lib:inet_port(Server),
+ Client = ssl_test_lib:start_client([{node, ServerNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa, {?MODULE, Recv, [Data, Quantity]}},
+ {options, [{active, Active}, {nodelay, true},
+ {packet, Packet} |
+ ClientOpts]}]),
+
+ ssl_test_lib:check_result(Client, ok),
+
+ ssl_test_lib:close(Server),
+ ssl_test_lib:close(Client);
+
packet(Config, Data, Send, Recv, Quantity, Packet, Active) ->
ClientOpts = ssl_test_lib:ssl_options(client_opts, Config),
ServerOpts = ssl_test_lib:ssl_options(server_opts, Config),
@@ -1982,26 +2011,19 @@ active_once_raw(Socket, Data, N) ->
active_once_raw(_, _, 0, _) ->
ok;
-active_once_raw(Socket, Data, N, Acc) ->
- receive
- {ssl, Socket, Byte} when length(Byte) == 1 ->
- ssl:setopts(Socket, [{active, once}]),
+active_once_raw(Socket, Data, N, Acc0) ->
+ case lists:prefix(Data, Acc0) of
+ true ->
+ DLen = length(Data),
+ Start = DLen + 1,
+ Len = length(Acc0) - DLen,
+ Acc = string:substr(Acc0, Start, Len),
+ active_once_raw(Socket, Data, N-1, Acc);
+ false ->
receive
- {ssl, Socket, _} ->
- ssl:setopts(Socket, [{active, once}]),
- active_once_raw(Socket, Data, N-1, [])
- end;
- {ssl, Socket, Data} ->
- ssl:setopts(Socket, [{active, once}]),
- active_once_raw(Socket, Data, N-1, []);
- {ssl, Socket, Other} ->
- case Acc ++ Other of
- Data ->
- ssl:setopts(Socket, [{active, once}]),
- active_once_raw(Socket, Data, N-1, []);
- NewAcc ->
+ {ssl, Socket, Info} ->
ssl:setopts(Socket, [{active, once}]),
- active_once_raw(Socket, Data, N, NewAcc)
+ active_once_raw(Socket, Data, N, Acc0 ++ Info)
end
end.
diff --git a/lib/ssl/test/ssl_payload_SUITE.erl b/lib/ssl/test/ssl_payload_SUITE.erl
index cb0571d0a7..cb1957327a 100644
--- a/lib/ssl/test/ssl_payload_SUITE.erl
+++ b/lib/ssl/test/ssl_payload_SUITE.erl
@@ -70,7 +70,7 @@ init_per_suite(Config) ->
catch crypto:stop(),
try crypto:start() of
ok ->
- ssl:start(),
+ ssl_test_lib:clean_start(),
{ok, _} = make_certs:all(proplists:get_value(data_dir, Config), proplists:get_value(priv_dir, Config)),
ssl_test_lib:cert_options(Config)
catch _:_ ->
@@ -104,8 +104,13 @@ init_per_testcase(TestCase, Config) when TestCase == server_echos_passive_huge;
TestCase == client_echos_passive_huge;
TestCase == client_echos_active_once_huge;
TestCase == client_echos_active_huge ->
- ct:timetrap({seconds, 90}),
- Config;
+ case erlang:system_info(system_architecture) of
+ "sparc-sun-solaris2.10" ->
+ {skip,"Will take to long time on an old Sparc"};
+ _ ->
+ ct:timetrap({seconds, 90}),
+ Config
+ end;
init_per_testcase(TestCase, Config) when TestCase == server_echos_passive_big;
TestCase == server_echos_active_once_big;
diff --git a/lib/ssl/test/ssl_pem_cache_SUITE.erl b/lib/ssl/test/ssl_pem_cache_SUITE.erl
index 13b0ce8ed9..02c98fc40f 100644
--- a/lib/ssl/test/ssl_pem_cache_SUITE.erl
+++ b/lib/ssl/test/ssl_pem_cache_SUITE.erl
@@ -43,7 +43,7 @@ init_per_suite(Config0) ->
catch crypto:stop(),
try crypto:start() of
ok ->
- ssl:start(),
+ ssl_test_lib:clean_start(),
%% make rsa certs using oppenssl
{ok, _} = make_certs:all(proplists:get_value(data_dir, Config0),
proplists:get_value(priv_dir, Config0)),
@@ -63,14 +63,15 @@ end_per_group(_GroupName, Config) ->
Config.
init_per_testcase(pem_cleanup = Case, Config) ->
- end_per_testcase(Case, Config) ,
application:load(ssl),
+ end_per_testcase(Case, Config) ,
application:set_env(ssl, ssl_pem_cache_clean, ?CLEANUP_INTERVAL),
ssl:start(),
ct:timetrap({minutes, 1}),
Config.
end_per_testcase(_TestCase, Config) ->
+ ssl_test_lib:clean_env(),
ssl:stop(),
Config.
diff --git a/lib/ssl/test/ssl_session_cache_SUITE.erl b/lib/ssl/test/ssl_session_cache_SUITE.erl
index b352844ba0..28637fc32d 100644
--- a/lib/ssl/test/ssl_session_cache_SUITE.erl
+++ b/lib/ssl/test/ssl_session_cache_SUITE.erl
@@ -58,7 +58,7 @@ init_per_suite(Config0) ->
catch crypto:stop(),
try crypto:start() of
ok ->
- ssl:start(),
+ ssl_test_lib:clean_start(),
%% make rsa certs using
{ok, _} = make_certs:all(proplists:get_value(data_dir, Config0),
proplists:get_value(priv_dir, Config0)),
diff --git a/lib/ssl/test/ssl_sni_SUITE.erl b/lib/ssl/test/ssl_sni_SUITE.erl
index 34ef2e6af9..4e916a7f03 100644
--- a/lib/ssl/test/ssl_sni_SUITE.erl
+++ b/lib/ssl/test/ssl_sni_SUITE.erl
@@ -41,7 +41,7 @@ init_per_suite(Config0) ->
catch crypto:stop(),
try crypto:start() of
ok ->
- ssl:start(),
+ ssl_test_lib:clean_start(),
{ok, _} = make_certs:all(proplists:get_value(data_dir, Config0),
proplists:get_value(priv_dir, Config0)),
ssl_test_lib:cert_options(Config0)
diff --git a/lib/ssl/test/ssl_test_lib.erl b/lib/ssl/test/ssl_test_lib.erl
index fd8af5efaa..cab22a60a8 100644
--- a/lib/ssl/test/ssl_test_lib.erl
+++ b/lib/ssl/test/ssl_test_lib.erl
@@ -398,7 +398,7 @@ cert_options(Config) ->
{ssl_imp, new}]},
{server_opts, [{ssl_imp, new},{reuseaddr, true}, {cacertfile, ServerCaCertFile},
{certfile, ServerCertFile}, {keyfile, ServerKeyFile}]},
- {server_anon, [{ssl_imp, new},{reuseaddr, true}, {ciphers, anonymous_suites()}]},
+ %%{server_anon, [{ssl_imp, new},{reuseaddr, true}, {ciphers, anonymous_suites()}]},
{client_psk, [{ssl_imp, new},{reuseaddr, true},
{psk_identity, "Test-User"},
{user_lookup_fun, {fun user_lookup/3, PskSharedSecret}}]},
@@ -807,22 +807,24 @@ send_selected_port(_,_,_) ->
rsa_suites(CounterPart) ->
ECC = is_sane_ecc(CounterPart),
FIPS = is_fips(CounterPart),
+ CryptoSupport = crypto:supports(),
+ Ciphers = proplists:get_value(ciphers, CryptoSupport),
lists:filter(fun({rsa, des_cbc, sha}) when FIPS == true ->
false;
({dhe_rsa, des_cbc, sha}) when FIPS == true ->
false;
- ({rsa, _, _}) ->
- true;
- ({dhe_rsa, _, _}) ->
- true;
- ({ecdhe_rsa, _, _}) when ECC == true ->
- true;
- ({rsa, _, _, _}) ->
- true;
- ({dhe_rsa, _, _,_}) ->
- true;
- ({ecdhe_rsa, _, _,_}) when ECC == true ->
- true;
+ ({rsa, Cipher, _}) ->
+ lists:member(Cipher, Ciphers);
+ ({dhe_rsa, Cipher, _}) ->
+ lists:member(Cipher, Ciphers);
+ ({ecdhe_rsa, Cipher, _}) when ECC == true ->
+ lists:member(Cipher, Ciphers);
+ ({rsa, Cipher, _, _}) ->
+ lists:member(Cipher, Ciphers);
+ ({dhe_rsa, Cipher, _,_}) ->
+ lists:member(Cipher, Ciphers);
+ ({ecdhe_rsa, Cipher, _,_}) when ECC == true ->
+ lists:member(Cipher, Ciphers);
(_) ->
false
end,
@@ -906,19 +908,8 @@ string_regex_filter(Str, Search) when is_list(Str) ->
string_regex_filter(_Str, _Search) ->
false.
-anonymous_suites() ->
- Suites =
- [{dh_anon, rc4_128, md5},
- {dh_anon, des_cbc, sha},
- {dh_anon, '3des_ede_cbc', sha},
- {dh_anon, aes_128_cbc, sha},
- {dh_anon, aes_256_cbc, sha},
- {dh_anon, aes_128_gcm, null, sha256},
- {dh_anon, aes_256_gcm, null, sha384},
- {ecdh_anon,rc4_128,sha},
- {ecdh_anon,'3des_ede_cbc',sha},
- {ecdh_anon,aes_128_cbc,sha},
- {ecdh_anon,aes_256_cbc,sha}],
+anonymous_suites(Version) ->
+ Suites = ssl_cipher:anonymous_suites(Version),
ssl_cipher:filter_suites(Suites).
psk_suites() ->
@@ -1353,3 +1344,19 @@ ct_log_supported_protocol_versions(Config) ->
_ ->
ct:log("TLS/SSL version ~p~n ", [tls_record:supported_protocol_versions()])
end.
+
+clean_env() ->
+ application:unset_env(ssl, protocol_version),
+ application:unset_env(ssl, session_lifetime),
+ application:unset_env(ssl, session_cb),
+ application:unset_env(ssl, session_cb_init_args),
+ application:unset_env(ssl, session_cache_client_max),
+ application:unset_env(ssl, session_cache_server_max),
+ application:unset_env(ssl, ssl_pem_cache_clean),
+ application:unset_env(ssl, alert_timeout).
+
+clean_start() ->
+ ssl:stop(),
+ application:load(ssl),
+ clean_env(),
+ ssl:start().
diff --git a/lib/ssl/test/ssl_to_openssl_SUITE.erl b/lib/ssl/test/ssl_to_openssl_SUITE.erl
index b3109b5de9..9ecfe5b0ea 100644
--- a/lib/ssl/test/ssl_to_openssl_SUITE.erl
+++ b/lib/ssl/test/ssl_to_openssl_SUITE.erl
@@ -55,7 +55,9 @@ groups() ->
basic_tests() ->
[basic_erlang_client_openssl_server,
basic_erlang_server_openssl_client,
- expired_session].
+ expired_session,
+ ssl2_erlang_server_openssl_client_comp
+ ].
all_versions_tests() ->
[
@@ -74,7 +76,8 @@ all_versions_tests() ->
ciphers_dsa_signed_certs,
erlang_client_bad_openssl_server,
expired_session,
- ssl2_erlang_server_openssl_client].
+ ssl2_erlang_server_openssl_client
+ ].
alpn_tests() ->
[erlang_client_alpn_openssl_server_alpn,
@@ -116,7 +119,7 @@ init_per_suite(Config0) ->
catch crypto:stop(),
try crypto:start() of
ok ->
- ssl:start(),
+ ssl_test_lib:clean_start(),
{ok, _} = make_certs:all(proplists:get_value(data_dir, Config0),
proplists:get_value(priv_dir, Config0)),
Config1 = ssl_test_lib:make_dsa_cert(Config0),
@@ -180,7 +183,8 @@ special_init(TestCase, Config)
{ok, Version} = application:get_env(ssl, protocol_version),
check_sane_openssl_renegotaite(Config, Version);
-special_init(ssl2_erlang_server_openssl_client, Config) ->
+special_init(Case, Config) when Case == ssl2_erlang_server_openssl_client;
+ Case == ssl2_erlang_server_openssl_client_comp ->
case ssl_test_lib:supports_ssl_tls_version(sslv2) of
true ->
Config;
@@ -954,8 +958,52 @@ ssl2_erlang_server_openssl_client(Config) when is_list(Config) ->
Data = "From openssl to erlang",
Server = ssl_test_lib:start_server_error([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {options, ServerOpts}]),
+ Port = ssl_test_lib:inet_port(Server),
+
+ Exe = "openssl",
+ Args = ["s_client", "-connect", "localhost:" ++ integer_to_list(Port),
+ "-ssl2", "-msg"],
+
+ OpenSslPort = ssl_test_lib:portable_open_port(Exe, Args),
+ true = port_command(OpenSslPort, Data),
+
+ ct:log("Ports ~p~n", [[erlang:port_info(P) || P <- erlang:ports()]]),
+ receive
+ {'EXIT', OpenSslPort, _} = Exit ->
+ ct:log("Received: ~p ~n", [Exit]),
+ ok
+ end,
+ receive
+ {'EXIT', _, _} = UnkownExit ->
+ Msg = lists:flatten(io_lib:format("Received: ~p ~n", [UnkownExit])),
+ ct:log(Msg),
+ ct:comment(Msg),
+ ok
+ after 0 ->
+ ok
+ end,
+ ssl_test_lib:check_result(Server, {error, {tls_alert, "handshake failure"}}),
+ process_flag(trap_exit, false).
+%%--------------------------------------------------------------------
+ssl2_erlang_server_openssl_client_comp() ->
+ [{doc,"Test that ssl v2 clients are rejected"}].
+
+ssl2_erlang_server_openssl_client_comp(Config) when is_list(Config) ->
+ process_flag(trap_exit, true),
+ ServerOpts = ssl_test_lib:ssl_options(server_opts, Config),
+ V2Compat = proplists:get_value(v2_hello_compatible, Config),
+
+ ServerOpts = ssl_test_lib:ssl_options(server_opts, Config),
+
+ {_, ServerNode, _} = ssl_test_lib:run_where(Config),
+
+ Data = "From openssl to erlang",
+
+ Server = ssl_test_lib:start_server_error([{node, ServerNode}, {port, 0},
{from, self()},
- {options, ServerOpts}]),
+ {options, [{v2_hello_compatible, V2Compat} | ServerOpts]}]),
Port = ssl_test_lib:inet_port(Server),
Exe = "openssl",
@@ -1264,7 +1312,7 @@ client_check_result(Port, DataExpected, DataReceived) ->
_ ->
client_check_result(Port, DataExpected, NewData)
end
- after 3000 ->
+ after 20000 ->
ct:fail({"Time out on openSSL Client", {expected, DataExpected},
{got, DataReceived}})
end.
diff --git a/lib/ssl/test/ssl_upgrade_SUITE.erl b/lib/ssl/test/ssl_upgrade_SUITE.erl
index 113b3b4158..f6af1e6182 100644
--- a/lib/ssl/test/ssl_upgrade_SUITE.erl
+++ b/lib/ssl/test/ssl_upgrade_SUITE.erl
@@ -29,7 +29,8 @@
server,
client,
soft,
- result_proxy
+ result_proxy,
+ skip
}).
all() ->
@@ -73,8 +74,15 @@ major_upgrade(Config) when is_list(Config) ->
minor_upgrade(Config) when is_list(Config) ->
ct_release_test:upgrade(ssl, minor,{?MODULE, #state{config = Config}}, Config).
-upgrade_init(CTData, #state{config = Config} = State) ->
- {ok, {_, _, Up, _Down}} = ct_release_test:get_appup(CTData, ssl),
+upgrade_init(CtData, State) ->
+ {ok,{FromVsn,ToVsn}} = ct_release_test:get_app_vsns(CtData, ssl),
+ upgrade_init(FromVsn, ToVsn, CtData, State).
+
+upgrade_init(_, "8.0.2", _, State) ->
+ %% Requires stdlib upgrade so it will be a node upgrade!
+ State#state{skip = true};
+upgrade_init(_, _, CtData, #state{config = Config} = State) ->
+ {ok, {_, _, Up, _Down}} = ct_release_test:get_appup(CtData, ssl),
ct:pal("Up: ~p", [Up]),
Soft = is_soft(Up), %% It is symmetrical, if upgrade is soft so is downgrade
Pid = spawn(?MODULE, result_proxy_init, [[]]),
@@ -88,6 +96,8 @@ upgrade_init(CTData, #state{config = Config} = State) ->
State#state{soft = Soft, result_proxy = Pid}
end.
+upgrade_upgraded(_, #state{skip = true} = State) ->
+ State;
upgrade_upgraded(_, #state{soft = false, config = Config, result_proxy = Pid} = State) ->
ct:pal("Restart upgrade ~n", []),
{Server, Client} = restart_start_connection(Config, Pid),
@@ -96,7 +106,6 @@ upgrade_upgraded(_, #state{soft = false, config = Config, result_proxy = Pid} =
ssl_test_lib:close(Client),
ok = Result,
State;
-
upgrade_upgraded(_, #state{server = Server0, client = Client0,
config = Config, soft = true,
result_proxy = Pid} = State) ->
@@ -110,6 +119,8 @@ upgrade_upgraded(_, #state{server = Server0, client = Client0,
{Server, Client} = soft_start_connection(Config, Pid),
State#state{server = Server, client = Client}.
+upgrade_downgraded(_, #state{skip = true} = State) ->
+ State;
upgrade_downgraded(_, #state{soft = false, config = Config, result_proxy = Pid} = State) ->
ct:pal("Restart downgrade: ~n", []),
{Server, Client} = restart_start_connection(Config, Pid),
@@ -119,7 +130,6 @@ upgrade_downgraded(_, #state{soft = false, config = Config, result_proxy = Pid}
Pid ! stop,
ok = Result,
State;
-
upgrade_downgraded(_, #state{server = Server, client = Client, soft = true, result_proxy = Pid} = State) ->
ct:pal("Soft downgrade: ~n", []),
Server ! changed_version,
diff --git a/lib/ssl/vsn.mk b/lib/ssl/vsn.mk
index 3b51fa8c6b..59732c7926 100644
--- a/lib/ssl/vsn.mk
+++ b/lib/ssl/vsn.mk
@@ -1 +1 @@
-SSL_VSN = 8.0
+SSL_VSN = 8.0.3
diff --git a/lib/stdlib/doc/src/dets.xml b/lib/stdlib/doc/src/dets.xml
index 3b134d00b7..2e4261d72e 100644
--- a/lib/stdlib/doc/src/dets.xml
+++ b/lib/stdlib/doc/src/dets.xml
@@ -391,7 +391,7 @@
<item>
<p><c>{hash, Hash}</c> - Describes which BIF is
used to calculate the hash values of the objects stored in the
- <c>dets</c> table. Possible values of <c>Hash</c>:</p>
+ Dets table. Possible values of <c>Hash</c>:</p>
<list>
<item>
<p><c>hash</c> - Implies that the <c>erlang:hash/2</c> BIF
diff --git a/lib/stdlib/doc/src/ets.xml b/lib/stdlib/doc/src/ets.xml
index b8e262208d..5f5d2b7f36 100644
--- a/lib/stdlib/doc/src/ets.xml
+++ b/lib/stdlib/doc/src/ets.xml
@@ -369,7 +369,7 @@
variable that in turn is passed to the function.</p>
<p>The parse transform is provided in the <c>ms_transform</c>
module and the source <em>must</em> include
- file <c>ms_transform.hrl</c> in <c>STDLIB</c> for this
+ file <c>ms_transform.hrl</c> in STDLIB for this
pseudo function to work. Failing to include the hrl file in
the source results in a runtime error, not a compile
time error. The include file is easiest included by adding line
@@ -1458,7 +1458,7 @@ is_integer(X), is_integer(Y), X + Y < 4711]]></code>
specification returned <c>true</c>.</fsummary>
<desc>
<p>Matches the objects in table <c><anno>Tab</anno></c> using a
- <seealso marker="#match_spec">match specificationc</seealso>. If the
+ <seealso marker="#match_spec">match specification</seealso>. If the
match specification returns <c>true</c> for an object, that object
considered a match and is counted. For any other result from
the match specification the object is not considered a match and is
@@ -1644,7 +1644,7 @@ is_integer(X), is_integer(Y), X + Y < 4711]]></code>
</taglist>
<p>Whenever option <c>extended_info</c> is used, it
results in a file not readable by versions of ETS before
- that in <c>STDLIB</c> 1.15.1</p>
+ that in STDLIB 1.15.1</p>
<p>If option <c>sync</c> is set to <c>true</c>, it ensures that
the content of the file is written to the disk before
<c>tab2file</c> returns. Defaults to <c>{sync, false}</c>.</p>
@@ -1725,7 +1725,7 @@ is_integer(X), is_integer(Y), X + Y < 4711]]></code>
<p>A tuple <c>{<anno>Major</anno>,<anno>Minor</anno>}</c>
containing the major and
minor version of the file format for ETS table dumps. This
- version field was added beginning with <c>STDLIB</c> 1.5.1.
+ version field was added beginning with STDLIB 1.5.1.
Files dumped with older versions return <c>{0,0}</c> in this
field.</p>
</item>
diff --git a/lib/stdlib/doc/src/gen_statem.xml b/lib/stdlib/doc/src/gen_statem.xml
index c57a31fa21..3322571b2c 100644
--- a/lib/stdlib/doc/src/gen_statem.xml
+++ b/lib/stdlib/doc/src/gen_statem.xml
@@ -97,6 +97,9 @@ gen_statem module Callback module
gen_statem:start
gen_statem:start_link -----> Module:init/1
+Server start or code change
+ -----> Module:callback_mode/0
+
gen_statem:stop -----> Module:terminate/3
gen_statem:call
@@ -116,9 +119,11 @@ erlang:'!' -----> Module:StateName/3
</p>
<p>
If a callback function fails or returns a bad value,
- the <c>gen_statem</c> terminates. However, an exception of class
+ the <c>gen_statem</c> terminates, unless otherwise stated.
+ However, an exception of class
<seealso marker="erts:erlang#throw/1"><c>throw</c></seealso>
- is not regarded as an error but as a valid return.
+ is not regarded as an error but as a valid return
+ from all callback functions.
</p>
<marker id="state_function"/>
<p>
@@ -127,7 +132,8 @@ erlang:'!' -----> Module:StateName/3
in a <c>gen_statem</c> is the callback function that is called
for all events in this state. It is selected depending on which
<seealso marker="#type-callback_mode"><em>callback mode</em></seealso>
- that the implementation specifies when the server starts.
+ that the callback module defines with the callback function
+ <seealso marker="#Module:callback_mode/0"><c>Module:callback_mode/0</c></seealso>.
</p>
<p>
When the
@@ -138,9 +144,9 @@ erlang:'!' -----> Module:StateName/3
This gathers all code for a specific state
in one function as the <c>gen_statem</c> engine
branches depending on state name.
- Notice that in this mode the mandatory callback function
+ Notice the fact that there is a mandatory callback function
<seealso marker="#Module:terminate/3"><c>Module:terminate/3</c></seealso>
- makes the state name <c>terminate</c> unusable.
+ makes the state name <c>terminate</c> unusable in this mode.
</p>
<p>
When the
@@ -249,11 +255,10 @@ erlang:'!' -----> Module:StateName/3
-behaviour(gen_statem).
-export([start/0,push/0,get_count/0,stop/0]).
--export([terminate/3,code_change/4,init/1]).
+-export([terminate/3,code_change/4,init/1,callback_mode/0]).
-export([on/3,off/3]).
name() -> pushbutton_statem. % The registered server name
-callback_mode() -> state_functions.
%% API. This example uses a registered name name()
%% and does not link to the caller.
@@ -270,15 +275,14 @@ stop() ->
terminate(_Reason, _State, _Data) ->
void.
code_change(_Vsn, State, Data, _Extra) ->
- {callback_mode(),State,Data}.
+ {ok,State,Data}.
init([]) ->
- %% Set the callback mode and initial state + data.
- %% Data is used only as a counter.
+ %% Set the initial state + data. Data is used only as a counter.
State = off, Data = 0,
- {callback_mode(),State,Data}.
-
+ {ok,State,Data}.
+callback_mode() -> state_functions.
-%%% State functions
+%%% State function(s)
off({call,From}, push, Data) ->
%% Go to 'on', increment count and reply
@@ -326,18 +330,13 @@ ok
To compare styles, here follows the same example using
<seealso marker="#type-callback_mode"><em>callback mode</em></seealso>
<c>state_functions</c>, or rather the code to replace
- from function <c>init/1</c> of the <c>pushbutton.erl</c>
+ after function <c>init/1</c> of the <c>pushbutton.erl</c>
example file above:
</p>
<code type="erl">
-init([]) ->
- %% Set the callback mode and initial state + data.
- %% Data is used only as a counter.
- State = off, Data = 0,
- {handle_event_function,State,Data}.
-
+callback_mode() -> handle_event_function.
-%%% Event handling
+%%% State function(s)
handle_event({call,From}, push, off, Data) ->
%% Go to 'on', increment count and reply
@@ -426,8 +425,8 @@ handle_event(_, _, State, Data) ->
<desc>
<p>
Debug option that can be used when starting
- a <c>gen_statem</c> server through, for example,
- <seealso marker="#enter_loop/5"><c>enter_loop/5</c></seealso>.
+ a <c>gen_statem</c> server through,
+ <seealso marker="#enter_loop/4"><c>enter_loop/4-6</c></seealso>.
</p>
<p>
For every entry in <c><anno>Dbgs</anno></c>,
@@ -525,12 +524,9 @@ handle_event(_, _, State, Data) ->
<desc>
<p>
The <em>callback mode</em> is selected when starting the
- <c>gen_statem</c> using the return value from
- <seealso marker="#Module:init/1"><c>Module:init/1</c></seealso>
- or when calling
- <seealso marker="#enter_loop/5"><c>enter_loop/5,6,7</c></seealso>,
- and with the return value from
- <seealso marker="#Module:code_change/4"><c>Module:code_change/4</c></seealso>.
+ <c>gen_statem</c> and after code change
+ using the return value from
+ <seealso marker="#Module:callback_mode/0"><c>Module:callback_mode/0</c></seealso>.
</p>
<taglist>
<tag><c>state_functions</c></tag>
@@ -691,7 +687,7 @@ handle_event(_, _, State, Data) ->
<seealso marker="#state_function">state function</seealso>, from
<seealso marker="#Module:init/1"><c>Module:init/1</c></seealso>
or by giving them to
- <seealso marker="#enter_loop/6"><c>enter_loop/6,7</c></seealso>.
+ <seealso marker="#enter_loop/5"><c>enter_loop/5,6</c></seealso>.
</p>
<p>
Actions are executed in the containing list order.
@@ -923,7 +919,8 @@ handle_event(_, _, State, Data) ->
</p>
<note>
<p>
- To avoid getting a late reply in the caller's
+ For <c><anno>Timeout</anno> =/= infinity</c>,
+ to avoid getting a late reply in the caller's
inbox, this function spawns a proxy process that
does the call. A late reply gets delivered to the
dead proxy process, hence gets discarded. This is
@@ -958,35 +955,36 @@ handle_event(_, _, State, Data) ->
</func>
<func>
- <name name="enter_loop" arity="5"/>
+ <name name="enter_loop" arity="4"/>
<fsummary>Enter the <c>gen_statem</c> receive loop.</fsummary>
<desc>
<p>
The same as
- <seealso marker="#enter_loop/7"><c>enter_loop/7</c></seealso>
- except that no
+ <seealso marker="#enter_loop/6"><c>enter_loop/6</c></seealso>
+ with <c>Actions = []</c> except that no
<seealso marker="#type-server_name"><c>server_name()</c></seealso>
- must have been registered.
+ must have been registered. This creates an anonymous server.
</p>
</desc>
</func>
<func>
- <name name="enter_loop" arity="6"/>
+ <name name="enter_loop" arity="5"/>
<fsummary>Enter the <c>gen_statem</c> receive loop.</fsummary>
<desc>
<p>
If <c><anno>Server_or_Actions</anno></c> is a <c>list()</c>,
the same as
- <seealso marker="#enter_loop/7"><c>enter_loop/7</c></seealso>
+ <seealso marker="#enter_loop/6"><c>enter_loop/6</c></seealso>
except that no
<seealso marker="#type-server_name"><c>server_name()</c></seealso>
must have been registered and
<c>Actions = <anno>Server_or_Actions</anno></c>.
+ This creates an anonymous server.
</p>
<p>
Otherwise the same as
- <seealso marker="#enter_loop/7"><c>enter_loop/7</c></seealso>
+ <seealso marker="#enter_loop/6"><c>enter_loop/6</c></seealso>
with
<c>Server = <anno>Server_or_Actions</anno></c> and
<c>Actions = []</c>.
@@ -995,7 +993,7 @@ handle_event(_, _, State, Data) ->
</func>
<func>
- <name name="enter_loop" arity="7"/>
+ <name name="enter_loop" arity="6"/>
<fsummary>Enter the <c>gen_statem</c> receive loop.</fsummary>
<desc>
<p>
@@ -1015,21 +1013,31 @@ handle_event(_, _, State, Data) ->
the <c>gen_statem</c> behavior provides.
</p>
<p>
- <c><anno>Module</anno></c>, <c><anno>Opts</anno></c>, and
- <c><anno>Server</anno></c> have the same meanings
- as when calling
+ <c><anno>Module</anno></c>, <c><anno>Opts</anno></c>
+ have the same meaning as when calling
<seealso marker="#start_link/3"><c>start[_link]/3,4</c></seealso>.
+ </p>
+ <p>
+ If <c><anno>Server</anno></c> is <c>self()</c> an anonymous
+ server is created just as when using
+ <seealso marker="#start_link/3"><c>start[_link]/3</c></seealso>.
+ If <c><anno>Server</anno></c> is a
+ <seealso marker="#type-server_name"><c>server_name()</c></seealso>
+ a named server is created just as when using
+ <seealso marker="#start_link/4"><c>start[_link]/4</c></seealso>.
However, the
<seealso marker="#type-server_name"><c>server_name()</c></seealso>
name must have been registered accordingly
- <em>before</em> this function is called.</p>
+ <em>before</em> this function is called.
+ </p>
<p>
- <c><anno>CallbackMode</anno></c>, <c><anno>State</anno></c>,
- <c><anno>Data</anno></c>, and <c><anno>Actions</anno></c>
+ <c><anno>State</anno></c>, <c><anno>Data</anno></c>,
+ and <c><anno>Actions</anno></c>
have the same meanings as in the return value of
<seealso marker="#Module:init/1"><c>Module:init/1</c></seealso>.
- Also, the callback module <c><anno>Module</anno></c>
- does not need to export an <c>init/1</c> function.
+ Also, the callback module does not need to export a
+ <seealso marker="#Module:init/1"><c>Module:init/1</c></seealso>
+ function.
</p>
<p>
The function fails if the calling process was not started by a
@@ -1253,6 +1261,48 @@ handle_event(_, _, State, Data) ->
<funcs>
<func>
+ <name>Module:callback_mode() -> CallbackMode</name>
+ <fsummary>Update the internal state during upgrade/downgrade.</fsummary>
+ <type>
+ <v>
+ CallbackMode =
+ <seealso marker="#type-callback_mode">callback_mode()</seealso>
+ </v>
+ </type>
+ <desc>
+ <p>
+ This function is called by a <c>gen_statem</c>
+ when it needs to find out the
+ <seealso marker="#type-callback_mode"><em>callback mode</em></seealso>
+ of the callback module. The value is cached by <c>gen_statem</c>
+ for efficiency reasons, so this function is only called
+ once after server start and after code change,
+ but before the first
+ <seealso marker="#state_function">state function</seealso>
+ is called. More occasions may be added in future versions
+ of <c>gen_statem</c>.
+ </p>
+ <p>
+ Server start happens either when
+ <seealso marker="#Module:init/1"><c>Module:init/1</c></seealso>
+ returns or when
+ <seealso marker="#enter_loop/4"><c>enter_loop/4-6</c></seealso>
+ is called. Code change happens when
+ <seealso marker="#Module:code_change/4"><c>Module:code_change/4</c></seealso>
+ returns.
+ </p>
+ <note>
+ <p>
+ If this function's body does not consist of solely one of two
+ possible
+ <seealso marker="#type-callback_mode">atoms</seealso>
+ the callback module is doing something strange.
+ </p>
+ </note>
+ </desc>
+ </func>
+
+ <func>
<name>Module:code_change(OldVsn, OldState, OldData, Extra) ->
Result
</name>
@@ -1262,11 +1312,7 @@ handle_event(_, _, State, Data) ->
<v>&nbsp;&nbsp;Vsn = term()</v>
<v>OldState = NewState = term()</v>
<v>Extra = term()</v>
- <v>Result = {NewCallbackMode,NewState,NewData} | Reason</v>
- <v>
- NewCallbackMode =
- <seealso marker="#type-callback_mode">callback_mode()</seealso>
- </v>
+ <v>Result = {ok,NewState,NewData} | Reason</v>
<v>
OldState = NewState =
<seealso marker="#type-state">state()</seealso>
@@ -1295,21 +1341,6 @@ handle_event(_, _, State, Data) ->
<c>Module</c>. If no such attribute is defined, the version
is the checksum of the Beam file.
</p>
- <note>
- <p>
- If you would dare to change
- <seealso marker="#type-callback_mode"><em>callback mode</em></seealso>
- during release upgrade/downgrade, the upgrade is no problem,
- as the new code surely knows what <em>callback mode</em>
- it needs. However, for a downgrade this function must
- know from argument <c>Extra</c> that comes from the
- <seealso marker="sasl:appup"><c>sasl:appup</c></seealso>
- file what <em>callback mode</em> the old code did use.
- It can also be possible to figure this out
- from argument <c>{down,Vsn}</c>, as <c>Vsn</c>
- in effect defines the old callback module version.
- </p>
- </note>
<p>
<c>OldState</c> and <c>OldData</c> is the internal state
of the <c>gen_statem</c>.
@@ -1321,31 +1352,32 @@ handle_event(_, _, State, Data) ->
<p>
If successful, the function must return the updated
internal state in an
- <c>{NewCallbackMode,NewState,NewData}</c> tuple.
+ <c>{ok,NewState,NewData}</c> tuple.
</p>
<p>
- If the function returns <c>Reason</c>, the ongoing
- upgrade fails and rolls back to the old release.</p>
- <p>
- This function can use
- <seealso marker="erts:erlang#throw/1"><c>erlang:throw/1</c></seealso>
- to return <c>Result</c> or <c>Reason</c>.
+ If the function returns a failure <c>Reason</c>, the ongoing
+ upgrade fails and rolls back to the old release.
+ Note that <c>Reason</c> can not be an <c>{ok,_,_}</c> tuple
+ since that will be regarded as a
+ <c>{ok,NewState,NewData}</c> tuple,
+ and that a tuple matching <c>{ok,_}</c>
+ is an also invalid failure <c>Reason</c>.
+ It is recommended to use an atom as <c>Reason</c> since
+ it will be wrapped in an <c>{error,Reason}</c> tuple.
</p>
</desc>
</func>
<func>
<name>Module:init(Args) -> Result</name>
- <fsummary>Initialize process and internal state.</fsummary>
+ <fsummary>
+ Optional function for initializing process and internal state.
+ </fsummary>
<type>
<v>Args = term()</v>
- <v>Result = {CallbackMode,State,Data}</v>
- <v>&nbsp;| {CallbackMode,State,Data,Actions}</v>
+ <v>Result = {ok,State,Data}</v>
+ <v>&nbsp;| {ok,State,Data,Actions}</v>
<v>&nbsp;| {stop,Reason} | ignore</v>
- <v>
- CallbackMode =
- <seealso marker="#type-callback_mode">callback_mode()</seealso>
- </v>
<v>State = <seealso marker="#type-state">state()</seealso></v>
<v>
Data = <seealso marker="#type-data">data()</seealso>
@@ -1364,7 +1396,7 @@ handle_event(_, _, State, Data) ->
<seealso marker="#start_link/3"><c>start_link/3,4</c></seealso>
or
<seealso marker="#start/3"><c>start/3,4</c></seealso>,
- this function is called by the new process to initialize
+ this optional function is called by the new process to initialize
the implementation state and server data.
</p>
<p>
@@ -1373,11 +1405,8 @@ handle_event(_, _, State, Data) ->
</p>
<p>
If the initialization is successful, the function is to
- return <c>{CallbackMode,State,Data}</c> or
- <c>{CallbackMode,State,Data,Actions}</c>.
- <c>CallbackMode</c> selects the
- <seealso marker="#type-callback_mode"><em>callback mode</em></seealso>
- of the <c>gen_statem</c>.
+ return <c>{ok,State,Data}</c> or
+ <c>{ok,State,Data,Actions}</c>.
<c>State</c> is the initial
<seealso marker="#type-state"><c>state()</c></seealso>
and <c>Data</c> the initial server
@@ -1395,11 +1424,16 @@ handle_event(_, _, State, Data) ->
or <c>ignore</c>; see
<seealso marker="#start_link/3"><c>start_link/3,4</c></seealso>.
</p>
- <p>
- This function can use
- <seealso marker="erts:erlang#throw/1"><c>erlang:throw/1</c></seealso>
- to return <c>Result</c>.
- </p>
+ <note>
+ <p>
+ This callback is optional, so a callback module does not need
+ to export it, but most do. If this function is not exported,
+ the <c>gen_statem</c> should be started through
+ <seealso marker="proc_lib"><c>proc_lib</c></seealso>
+ and
+ <seealso marker="#enter_loop/4"><c>enter_loop/4-6</c></seealso>.
+ </p>
+ </note>
</desc>
</func>
@@ -1430,10 +1464,14 @@ handle_event(_, _, State, Data) ->
This callback is optional, so a callback module does not need
to export it. The <c>gen_statem</c> module provides a default
implementation of this function that returns
- <c>{State,Data}</c>. If this callback fails, the default
- function returns <c>{State,Info}</c>,
- where <c>Info</c> informs of the crash but no details,
- to hide possibly sensitive data.
+ <c>{State,Data}</c>.
+ </p>
+ <p>
+ If this callback is exported but fails,
+ to hide possibly sensitive data,
+ the default function will instead return <c>{State,Info}</c>,
+ where <c>Info</c> says nothing but the fact that
+ <c>format_status/2</c> has crashed.
</p>
</note>
<p>This function is called by a <c>gen_statem</c> process when
@@ -1494,11 +1532,6 @@ handle_event(_, _, State, Data) ->
printed in log files. Another use is to hide sensitive data from
being written to the error log.
</p>
- <p>
- This function can use
- <seealso marker="erts:erlang#throw/1"><c>erlang:throw/1</c></seealso>
- to return <c>Status</c>.
- </p>
</desc>
</func>
@@ -1573,9 +1606,12 @@ handle_event(_, _, State, Data) ->
see <seealso marker="#type-action"><c>action()</c></seealso>.
</p>
<p>
- These functions can use
- <seealso marker="erts:erlang#throw/1"><c>erlang:throw/1</c></seealso>,
- to return the result.
+ Note the fact that you can use
+ <seealso marker="erts:erlang#throw/1"><c>throw</c></seealso>
+ to return the result, which can be useful.
+ For example to bail out with <c>throw(keep_state_and_data)</c>
+ from deep within complex code that is in no position to
+ return <c>{next_state,State,Data}</c>.
</p>
</desc>
</func>
@@ -1648,11 +1684,6 @@ handle_event(_, _, State, Data) ->
and an error report is issued using
<seealso marker="kernel:error_logger#format/2"><c>error_logger:format/2</c></seealso>.
</p>
- <p>
- This function can use
- <seealso marker="erts:erlang#throw/1"><c>erlang:throw/1</c></seealso>
- to return <c>Ignored</c>, which is ignored anyway.
- </p>
</desc>
</func>
</funcs>
diff --git a/lib/stdlib/doc/src/notes.xml b/lib/stdlib/doc/src/notes.xml
index ad2599c5a0..554150380f 100644
--- a/lib/stdlib/doc/src/notes.xml
+++ b/lib/stdlib/doc/src/notes.xml
@@ -31,6 +31,81 @@
</header>
<p>This document describes the changes made to the STDLIB application.</p>
+<section><title>STDLIB 3.1</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ The <c>zip:unzip/1,2</c> and <c>zip:extract/1,2</c>
+ functions have been updated to handle directory traversal
+ exploits. Any element in the zip file that contains a
+ path that points to a directory above the top level
+ working directory, <c>cwd</c>, will instead be extracted
+ in <c>cwd</c>. An error message is printed for any such
+ element in the zip file during the unzip operation. The
+ <c>keep_old_files</c> option determines if a file will
+ overwrite a previous file with the same name within the
+ zip file.</p>
+ <p>
+ Own Id: OTP-13633</p>
+ </item>
+ <item>
+ <p> Correct the contracts for
+ <c>ets:match_object/1,3</c>. </p>
+ <p>
+ Own Id: OTP-13721 Aux Id: PR-1113 </p>
+ </item>
+ <item>
+ <p>
+ Errors in type specification and Emacs template
+ generation for <c>gen_statem:code_change/4</c> has been
+ fixed from bugs.erlang.org's Jira cases ERL-172 and
+ ERL-187.</p>
+ <p>
+ Own Id: OTP-13746 Aux Id: ERL-172, ERL-187 </p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ gen_statem has been changed to set the callback mode for
+ a server to what Module:callback_mode/0 returns. This
+ facilitates e.g code downgrade since the callback mode
+ now becomes a property of the currently active code, not
+ of the server process.</p>
+ <p>
+ Exception handling from Module:init/1 has also been
+ improved.</p>
+ <p>
+ *** POTENTIAL INCOMPATIBILITY ***</p>
+ <p>
+ Own Id: OTP-13752</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>STDLIB 3.0.1</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p> Correct a bug regarding typed records in the Erlang
+ shell. The bug was introduced in OTP-19.0. </p>
+ <p>
+ Own Id: OTP-13719 Aux Id: ERL-182 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>STDLIB 3.0</title>
<section><title>Fixed Bugs and Malfunctions</title>
@@ -511,7 +586,7 @@
</item>
<item>
<p>
- The <c>stdlib</c> reference manual is updated to show
+ The STDLIB reference manual is updated to show
correct information about the return value of
<c>gen_fsm:reply/2</c>.</p>
<p>
@@ -6221,7 +6296,7 @@
documentation for <c>compile</c> on how to provide the key
for encrypting, and the documentation for <c>beam_lib</c>
on how to provide the key for decryption so that tools such
- as the Debugger, <c>xref</c>, or <c>cover</c> can be used.</p>
+ as the Debugger, Xref, or Cover can be used.</p>
<p>The <c>beam_lib:chunks/2</c> functions now accepts an
additional chunk type <c>compile_info</c> to retrieve
the compilation information directly as a term. (Thanks
diff --git a/lib/stdlib/doc/src/proc_lib.xml b/lib/stdlib/doc/src/proc_lib.xml
index 58ca5644cf..da03c39a26 100644
--- a/lib/stdlib/doc/src/proc_lib.xml
+++ b/lib/stdlib/doc/src/proc_lib.xml
@@ -59,9 +59,9 @@
<p>When a process that is started using <c>proc_lib</c> terminates
abnormally (that is, with another exit reason than <c>normal</c>,
<c>shutdown</c>, or <c>{shutdown,Term}</c>), a <em>crash report</em>
- is generated, which is written to terminal by the default <c>SASL</c>
+ is generated, which is written to terminal by the default SASL
event handler. That is, the crash report is normally only visible
- if the <c>SASL</c> application is started; see
+ if the SASL application is started; see
<seealso marker="sasl:sasl_app"><c>sasl(6)</c></seealso> and section
<seealso marker="sasl:error_logging">SASL Error Logging</seealso>
in the SASL User's Guide.</p>
diff --git a/lib/stdlib/doc/src/queue.xml b/lib/stdlib/doc/src/queue.xml
index a46ca47033..9f3aff03a3 100644
--- a/lib/stdlib/doc/src/queue.xml
+++ b/lib/stdlib/doc/src/queue.xml
@@ -401,7 +401,7 @@
</func>
<func>
- <name name="liat" arity="1"/>v
+ <name name="liat" arity="1"/>
<fsummary>Remove the tail item from a queue.</fsummary>
<desc>
<p>Returns a queue <c><anno>Q2</anno></c> that is the result of removing
diff --git a/lib/stdlib/doc/src/stdlib_app.xml b/lib/stdlib/doc/src/stdlib_app.xml
index cde73269a8..f857cc394b 100644
--- a/lib/stdlib/doc/src/stdlib_app.xml
+++ b/lib/stdlib/doc/src/stdlib_app.xml
@@ -31,14 +31,14 @@
<app>STDLIB</app>
<appsummary>The STDLIB application.</appsummary>
<description>
- <p>The <c>STDLIB</c> application is mandatory in the sense that the minimal
- system based on Erlang/OTP consists of <c>Kernel</c> and <c>STDLIB</c>.
- The <c>STDLIB</c> application contains no services.</p>
+ <p>The STDLIB application is mandatory in the sense that the minimal
+ system based on Erlang/OTP consists of Kernel and STDLIB.
+ The STDLIB application contains no services.</p>
</description>
<section>
<title>Configuration</title>
- <p>The following configuration parameters are defined for the <c>STDLIB</c>
+ <p>The following configuration parameters are defined for the STDLIB
application. For more information about configuration parameters, see the
<seealso marker="kernel:app"><c>app(4)</c></seealso> module in Kernel.</p>
diff --git a/lib/stdlib/doc/src/timer.xml b/lib/stdlib/doc/src/timer.xml
index 8f2ce36b06..fcaccdb2cb 100644
--- a/lib/stdlib/doc/src/timer.xml
+++ b/lib/stdlib/doc/src/timer.xml
@@ -253,7 +253,7 @@
is needed. This is useful during development, but in a
target system the server is to be started explicitly. Use
configuration parameters for
- <seealso marker="kernel:index"><c>Kernel</c></seealso> for this.</p>
+ <seealso marker="kernel:index">Kernel</seealso> for this.</p>
</desc>
</func>
diff --git a/lib/stdlib/doc/src/unicode_usage.xml b/lib/stdlib/doc/src/unicode_usage.xml
index 7f79ac88a1..efc8b75075 100644
--- a/lib/stdlib/doc/src/unicode_usage.xml
+++ b/lib/stdlib/doc/src/unicode_usage.xml
@@ -274,8 +274,8 @@
marker="stdlib:io"><c>io</c></seealso> module, the file
handling, the <seealso
marker="stdlib:unicode"><c>unicode</c></seealso> module, and
- the bit syntax). Today most modules in <c>Kernel</c> and
- <c>STDLIB</c>, as well as the VM are Unicode-aware.</p>
+ the bit syntax). Today most modules in Kernel and
+ STDLIB, as well as the VM are Unicode-aware.</p>
</item>
<tag>File I/O</tag>
<item>
@@ -765,7 +765,7 @@ Eshell V5.10.1 (abort with ^G)
file system). The Unicode character list is used to denote
filenames or directory names. If the file system content is
listed, you also get Unicode lists as return value. The support
- lies in the <c>Kernel</c> and <c>STDLIB</c> modules, which is why
+ lies in the Kernel and STDLIB modules, which is why
most applications (that does not explicitly require the filenames
to be in the ISO Latin-1 range) benefit from the Unicode support
without change.</p>
@@ -843,7 +843,7 @@ Eshell V5.10.1 (abort with ^G)
<title>Notes About Raw Filenames</title>
<marker id="notes-about-raw-filenames"/>
<p>Raw filenames were introduced together with Unicode filename support
- in <c>ERTS</c> 5.8.2 (Erlang/OTP R14B01). The reason &quot;raw
+ in ERTS 5.8.2 (Erlang/OTP R14B01). The reason &quot;raw
filenames&quot; were introduced in the system was
to be able to represent
filenames, specified in different encodings on the same system,
diff --git a/lib/stdlib/doc/src/zip.xml b/lib/stdlib/doc/src/zip.xml
index de23608046..0b5eac1e16 100644
--- a/lib/stdlib/doc/src/zip.xml
+++ b/lib/stdlib/doc/src/zip.xml
@@ -138,7 +138,7 @@
<p>File information as in
<seealso marker="kernel:file#read_file_info/1">
<c>file:read_file_info/1</c></seealso>
- in <c>Kernel</c></p>
+ in Kernel</p>
</item>
<tag><c>comment</c></tag>
<item>
@@ -345,7 +345,7 @@
prepended to filenames when extracting them from the
zip archive. (Acting like
<seealso marker="kernel:file#set_cwd/1">
- <c>file:set_cwd/1</c></seealso> in <c>Kernel</c>,
+ <c>file:set_cwd/1</c></seealso> in Kernel,
but without changing the global <c>cwd</c> property.)</p>
</item>
</taglist>
@@ -420,7 +420,7 @@
(<c>cwd</c>). This is prepended to filenames when adding them,
although not in the zip archive (acting like
<seealso marker="kernel:file#set_cwd/1">
- <c>file:set_cwd/1</c></seealso> in <c>Kernel</c>, but without
+ <c>file:set_cwd/1</c></seealso> in Kernel, but without
changing the global <c>cwd</c> property.).</p>
</item>
<tag><c>{compress, <anno>What</anno>}</c></tag>
diff --git a/lib/stdlib/src/beam_lib.erl b/lib/stdlib/src/beam_lib.erl
index fe9df601eb..d7ee5c1f5d 100644
--- a/lib/stdlib/src/beam_lib.erl
+++ b/lib/stdlib/src/beam_lib.erl
@@ -55,7 +55,7 @@
-type beam() :: module() | file:filename() | binary().
--type forms() :: [erl_parse:abstract_form()].
+-type forms() :: [erl_parse:abstract_form() | erl_parse:form_info()].
-type abst_code() :: {AbstVersion :: atom(), forms()} | 'no_abstract_code'.
-type dataB() :: binary().
diff --git a/lib/stdlib/src/edlin_expand.erl b/lib/stdlib/src/edlin_expand.erl
index ec64470461..5f821caef0 100644
--- a/lib/stdlib/src/edlin_expand.erl
+++ b/lib/stdlib/src/edlin_expand.erl
@@ -118,7 +118,7 @@ format_col([A|T], Width, Len, Acc0) ->
{H1, _} -> H1;
H2 -> H2
end,
- Acc = [io_lib:format("~-*s", [Width,H]) | Acc0],
+ Acc = [io_lib:format("~-*ts", [Width,H]) | Acc0],
format_col(T, Width, Len+Width, Acc);
format_col([], _, _, Acc) ->
lists:reverse(Acc, "\n").
diff --git a/lib/stdlib/src/gen_statem.erl b/lib/stdlib/src/gen_statem.erl
index 23bddafeed..3b3477b282 100644
--- a/lib/stdlib/src/gen_statem.erl
+++ b/lib/stdlib/src/gen_statem.erl
@@ -24,7 +24,7 @@
[start/3,start/4,start_link/3,start_link/4,
stop/1,stop/3,
cast/2,call/2,call/3,
- enter_loop/5,enter_loop/6,enter_loop/7,
+ enter_loop/4,enter_loop/5,enter_loop/6,
reply/1,reply/2]).
%% gen callbacks
@@ -63,8 +63,8 @@
{To :: pid(), Tag :: term()}. % Reply-to specifier for call
-type state() ::
- state_name() | % For state callback function StateName/5
- term(). % For state callback function handle_event/5
+ state_name() | % For StateName/3 callback functios
+ term(). % For handle_event/4 callback function
-type state_name() :: atom().
@@ -174,28 +174,33 @@
%% an {ok, ...} tuple. Thereafter the state callbacks are called
%% for all events to this server.
-callback init(Args :: term()) ->
- {callback_mode(), state(), data()} |
- {callback_mode(), state(), data(), [action()] | action()} |
+ {ok, state(), data()} |
+ {ok, state(), data(), [action()] | action()} |
'ignore' |
{'stop', Reason :: term()}.
-%% Example state callback for callback_mode() =:= state_functions
-%% state name 'state_name'.
+%% This callback shall return the callback mode of the callback module.
%%
-%% In this mode all states has to be type state_name() i.e atom().
+%% It is called once after init/0 and code_change/4 but before
+%% the first state callback StateName/3 or handle_event/4.
+-callback callback_mode() -> callback_mode().
+
+%% Example state callback for StateName = 'state_name'
+%% when callback_mode() =:= state_functions.
+%%
+%% In this mode all states has to be of type state_name() i.e atom().
%%
-%% Note that state callbacks and only state callbacks have arity 5
-%% and that is intended.
+%% Note that the only callbacks that have arity 3 are these
+%% StateName/3 callbacks and terminate/3, so the state name
+%% 'terminate' is unusable in this mode.
-callback state_name(
event_type(),
EventContent :: term(),
Data :: data()) ->
state_function_result().
%%
-%% State callback for callback_mode() =:= handle_event_function.
-%%
-%% Note that state callbacks and only state callbacks have arity 5
-%% and that is intended.
+%% State callback for all states
+%% when callback_mode() =:= handle_event_function.
-callback handle_event(
event_type(),
EventContent :: term(),
@@ -219,9 +224,8 @@
OldState :: state(),
OldData :: data(),
Extra :: term()) ->
- {NewCallbackMode :: callback_mode(),
- NewState :: state(),
- NewData :: data()}.
+ {ok, NewState :: state(), NewData :: data()} |
+ (Reason :: term()).
%% Format the callback module state in some sensible that is
%% often condensed way. For StatusOption =:= 'normal' the perferred
@@ -239,10 +243,13 @@
[init/1, % One may use enter_loop/5,6,7 instead
format_status/2, % Has got a default implementation
%%
- state_name/3, % Example for callback_mode =:= state_functions:
- %% there has to be a StateName/5 callback function for every StateName.
+ state_name/3, % Example for callback_mode() =:= state_functions:
+ %% there has to be a StateName/3 callback function
+ %% for every StateName in your state machine but the state name
+ %% 'state_name' does of course not have to be used.
%%
- handle_event/4]). % For callback_mode =:= handle_event_function
+ handle_event/4 % For callback_mode() =:= handle_event_function
+ ]).
%% Type validation functions
callback_mode(CallbackMode) ->
@@ -450,43 +457,35 @@ reply({To,Tag}, Reply) when is_pid(To) ->
%% the same arguments as you would have returned from init/1
-spec enter_loop(
Module :: module(), Opts :: [debug_opt()],
- CallbackMode :: callback_mode(),
State :: state(), Data :: data()) ->
no_return().
-enter_loop(Module, Opts, CallbackMode, State, Data) ->
- enter_loop(Module, Opts, CallbackMode, State, Data, self()).
+enter_loop(Module, Opts, State, Data) ->
+ enter_loop(Module, Opts, State, Data, self()).
%%
-spec enter_loop(
Module :: module(), Opts :: [debug_opt()],
- CallbackMode :: callback_mode(),
State :: state(), Data :: data(),
Server_or_Actions ::
server_name() | pid() | [action()]) ->
no_return().
-enter_loop(Module, Opts, CallbackMode, State, Data, Server_or_Actions) ->
+enter_loop(Module, Opts, State, Data, Server_or_Actions) ->
if
is_list(Server_or_Actions) ->
- enter_loop(
- Module, Opts, CallbackMode, State, Data,
- self(), Server_or_Actions);
+ enter_loop(Module, Opts, State, Data, self(), Server_or_Actions);
true ->
- enter_loop(
- Module, Opts, CallbackMode, State, Data,
- Server_or_Actions, [])
+ enter_loop(Module, Opts, State, Data, Server_or_Actions, [])
end.
%%
-spec enter_loop(
Module :: module(), Opts :: [debug_opt()],
- CallbackMode :: callback_mode(),
State :: state(), Data :: data(),
Server :: server_name() | pid(),
Actions :: [action()] | action()) ->
no_return().
-enter_loop(Module, Opts, CallbackMode, State, Data, Server, Actions) ->
+enter_loop(Module, Opts, State, Data, Server, Actions) ->
is_atom(Module) orelse error({atom,Module}),
- callback_mode(CallbackMode) orelse error({callback_mode,CallbackMode}),
Parent = gen:get_parent(),
- enter(Module, Opts, CallbackMode, State, Data, Server, Actions, Parent).
+ enter(Module, Opts, State, Data, Server, Actions, Parent).
%%---------------------------------------------------------------------------
%% API helpers
@@ -514,7 +513,7 @@ send(Proc, Msg) ->
end.
%% Here the init_it/6 and enter_loop/5,6,7 functions converge
-enter(Module, Opts, CallbackMode, State, Data, Server, Actions, Parent) ->
+enter(Module, Opts, State, Data, Server, Actions, Parent) ->
%% The values should already have been type checked
Name = gen:get_proc_name(Server),
Debug = gen:debug_options(Name, Opts),
@@ -530,7 +529,7 @@ enter(Module, Opts, CallbackMode, State, Data, Server, Actions, Parent) ->
[Actions,{postpone,false}]
end,
S = #{
- callback_mode => CallbackMode,
+ callback_mode => undefined,
module => Module,
name => Name,
%% All fields below will be replaced according to the arguments to
@@ -558,9 +557,15 @@ init_it(Starter, Parent, ServerRef, Module, Args, Opts) ->
Result ->
init_result(Starter, Parent, ServerRef, Module, Result, Opts);
Class:Reason ->
+ Stacktrace = erlang:get_stacktrace(),
+ Name = gen:get_proc_name(ServerRef),
gen:unregister_name(ServerRef),
proc_lib:init_ack(Starter, {error,Reason}),
- erlang:raise(Class, Reason, erlang:get_stacktrace())
+ error_info(
+ Class, Reason, Stacktrace,
+ #{name => Name, callback_mode => undefined},
+ [], [], undefined),
+ erlang:raise(Class, Reason, Stacktrace)
end.
%%---------------------------------------------------------------------------
@@ -568,30 +573,12 @@ init_it(Starter, Parent, ServerRef, Module, Args, Opts) ->
init_result(Starter, Parent, ServerRef, Module, Result, Opts) ->
case Result of
- {CallbackMode,State,Data} ->
- case callback_mode(CallbackMode) of
- true ->
- proc_lib:init_ack(Starter, {ok,self()}),
- enter(
- Module, Opts, CallbackMode, State, Data,
- ServerRef, [], Parent);
- false ->
- Error = {callback_mode,CallbackMode},
- proc_lib:init_ack(Starter, {error,Error}),
- exit(Error)
- end;
- {CallbackMode,State,Data,Actions} ->
- case callback_mode(CallbackMode) of
- true ->
- proc_lib:init_ack(Starter, {ok,self()}),
- enter(
- Module, Opts, CallbackMode, State, Data,
- ServerRef, Actions, Parent);
- false ->
- Error = {callback_mode,CallbackMode},
- proc_lib:init_ack(Starter, {error,Error}),
- exit(Error)
- end;
+ {ok,State,Data} ->
+ proc_lib:init_ack(Starter, {ok,self()}),
+ enter(Module, Opts, State, Data, ServerRef, [], Parent);
+ {ok,State,Data,Actions} ->
+ proc_lib:init_ack(Starter, {ok,self()}),
+ enter(Module, Opts, State, Data, ServerRef, Actions, Parent);
{stop,Reason} ->
gen:unregister_name(ServerRef),
proc_lib:init_ack(Starter, {error,Reason}),
@@ -601,8 +588,14 @@ init_result(Starter, Parent, ServerRef, Module, Result, Opts) ->
proc_lib:init_ack(Starter, ignore),
exit(normal);
_ ->
- Error = {bad_return_value,Result},
+ Name = gen:get_proc_name(ServerRef),
+ gen:unregister_name(ServerRef),
+ Error = {bad_return_from_init,Result},
proc_lib:init_ack(Starter, {error,Error}),
+ error_info(
+ error, Error, ?STACKTRACE(),
+ #{name => Name, callback_mode => undefined},
+ [], [], undefined),
exit(Error)
end.
@@ -630,11 +623,9 @@ system_code_change(
Result -> Result
end
of
- {NewCallbackMode,NewState,NewData} ->
- callback_mode(NewCallbackMode) orelse
- error({callback_mode,NewCallbackMode}),
+ {ok,NewState,NewData} ->
{ok,
- S#{callback_mode := NewCallbackMode,
+ S#{callback_mode := undefined,
state := NewState,
data := NewData}};
{ok,_} = Error ->
@@ -675,14 +666,14 @@ format_status(
%% them, not as the real erlang messages. Use trace for that.
%%---------------------------------------------------------------------------
-print_event(Dev, {in,Event}, {Name,_}) ->
+print_event(Dev, {in,Event}, {Name,State}) ->
io:format(
- Dev, "*DBG* ~p received ~s~n",
- [Name,event_string(Event)]);
-print_event(Dev, {out,Reply,{To,_Tag}}, {Name,_}) ->
+ Dev, "*DBG* ~p receive ~s in state ~p~n",
+ [Name,event_string(Event),State]);
+print_event(Dev, {out,Reply,{To,_Tag}}, {Name,State}) ->
io:format(
- Dev, "*DBG* ~p sent ~p to ~p~n",
- [Name,Reply,To]);
+ Dev, "*DBG* ~p send ~p to ~p from state ~p~n",
+ [Name,Reply,To,State]);
print_event(Dev, {Tag,Event,NextState}, {Name,State}) ->
StateString =
case NextState of
@@ -874,22 +865,36 @@ loop_event(
%%
try
case CallbackMode of
+ undefined ->
+ Module:callback_mode();
state_functions ->
- Module:State(Type, Content, Data);
+ erlang:apply(Module, State, [Type,Content,Data]);
handle_event_function ->
Module:handle_event(Type, Content, State, Data)
- end of
+ end
+ of
+ Result when CallbackMode =:= undefined ->
+ loop_event_callback_mode(
+ Parent, Debug, S, Events, State, Data, P, Event, Result);
Result ->
loop_event_result(
Parent, Debug, S, Events, State, Data, P, Event, Result)
catch
+ Result when CallbackMode =:= undefined ->
+ loop_event_callback_mode(
+ Parent, Debug, S, Events, State, Data, P, Event, Result);
Result ->
loop_event_result(
Parent, Debug, S, Events, State, Data, P, Event, Result);
- error:badarg when CallbackMode =:= state_functions ->
+ error:badarg ->
case erlang:get_stacktrace() of
- [{erlang,apply,[Module,State,_],_}|Stacktrace] ->
- Args = [Type,Content,Data],
+ [{erlang,apply,
+ [Module,State,[Type,Content,Data]=Args],
+ _}
+ |Stacktrace]
+ when CallbackMode =:= state_functions ->
+ %% We get here e.g if apply fails
+ %% due to State not being an atom
terminate(
error,
{undef_state_function,{Module,State,Args}},
@@ -901,24 +906,29 @@ loop_event(
Debug, S, [Event|Events], State, Data, P)
end;
error:undef ->
- %% Process an undef to check for the simple mistake
+ %% Process undef to check for the simple mistake
%% of calling a nonexistent state function
+ %% to make the undef more precise
case erlang:get_stacktrace() of
- [{Module,State,
- [Type,Content,Data]=Args,
- _}
+ [{Module,callback_mode,[]=Args,_}
+ |Stacktrace]
+ when CallbackMode =:= undefined ->
+ terminate(
+ error,
+ {undef_callback,{Module,callback_mode,Args}},
+ Stacktrace,
+ Debug, S, [Event|Events], State, Data, P);
+ [{Module,State,[Type,Content,Data]=Args,_}
|Stacktrace]
- when CallbackMode =:= state_functions ->
+ when CallbackMode =:= state_functions ->
terminate(
error,
{undef_state_function,{Module,State,Args}},
Stacktrace,
Debug, S, [Event|Events], State, Data, P);
- [{Module,handle_event,
- [Type,Content,State,Data]=Args,
- _}
+ [{Module,handle_event,[Type,Content,State,Data]=Args,_}
|Stacktrace]
- when CallbackMode =:= handle_event_function ->
+ when CallbackMode =:= handle_event_function ->
terminate(
error,
{undef_state_function,{Module,handle_event,Args}},
@@ -936,6 +946,25 @@ loop_event(
Debug, S, [Event|Events], State, Data, P)
end.
+%% Interpret callback_mode() result
+loop_event_callback_mode(
+ Parent, Debug, S, Events, State, Data, P, Event, CallbackMode) ->
+ case callback_mode(CallbackMode) of
+ true ->
+ Hibernate = false, % We have already GC:ed recently
+ loop_event(
+ Parent, Debug,
+ S#{callback_mode := CallbackMode},
+ Events,
+ State, Data, P, Event, Hibernate);
+ false ->
+ terminate(
+ error,
+ {bad_return_from_callback_mode,CallbackMode},
+ ?STACKTRACE(),
+ Debug, S, [Event|Events], State, Data, P)
+ end.
+
%% Interpret all callback return variants
loop_event_result(
Parent, Debug, S, Events, State, Data, P, Event, Result) ->
@@ -988,7 +1017,9 @@ loop_event_result(
State, Data, P, Event, State, Actions);
_ ->
terminate(
- error, {bad_return_value,Result}, ?STACKTRACE(),
+ error,
+ {bad_return_from_state_function,Result},
+ ?STACKTRACE(),
Debug, S, [Event|Events], State, Data, P)
end.
@@ -1025,20 +1056,26 @@ loop_event_actions(
Postpone, Hibernate, Timeout, NextEvents);
false ->
terminate(
- error, {bad_action,Action}, ?STACKTRACE(),
+ error,
+ {bad_action_from_state_function,Action},
+ ?STACKTRACE(),
Debug, S, [Event|Events], State, NewData, P)
end;
{next_event,Type,Content} ->
case event_type(Type) of
true ->
+ NewDebug =
+ sys_debug(Debug, S, State, {in,{Type,Content}}),
loop_event_actions(
- Parent, Debug, S, Events,
+ Parent, NewDebug, S, Events,
State, NewData, P, Event, NextState, Actions,
Postpone, Hibernate, Timeout,
[{Type,Content}|NextEvents]);
false ->
terminate(
- error, {bad_action,Action}, ?STACKTRACE(),
+ error,
+ {bad_action_from_state_function,Action},
+ ?STACKTRACE(),
Debug, S, [Event|Events], State, NewData, P)
end;
%% Actions that set options
@@ -1049,7 +1086,9 @@ loop_event_actions(
NewPostpone, Hibernate, Timeout, NextEvents);
{postpone,_} ->
terminate(
- error, {bad_action,Action}, ?STACKTRACE(),
+ error,
+ {bad_action_from_state_function,Action},
+ ?STACKTRACE(),
Debug, S, [Event|Events], State, NewData, P);
postpone ->
loop_event_actions(
@@ -1063,7 +1102,9 @@ loop_event_actions(
Postpone, NewHibernate, Timeout, NextEvents);
{hibernate,_} ->
terminate(
- error, {bad_action,Action}, ?STACKTRACE(),
+ error,
+ {bad_action_from_state_function,Action},
+ ?STACKTRACE(),
Debug, S, [Event|Events], State, NewData, P);
hibernate ->
loop_event_actions(
@@ -1082,7 +1123,9 @@ loop_event_actions(
Postpone, Hibernate, NewTimeout, NextEvents);
{timeout,_,_} ->
terminate(
- error, {bad_action,Action}, ?STACKTRACE(),
+ error,
+ {bad_action_from_state_function,Action},
+ ?STACKTRACE(),
Debug, S, [Event|Events], State, NewData, P);
infinity -> % Clear timer - it will never trigger
loop_event_actions(
@@ -1097,7 +1140,9 @@ loop_event_actions(
Postpone, Hibernate, NewTimeout, NextEvents);
_ ->
terminate(
- error, {bad_action,Action}, ?STACKTRACE(),
+ error,
+ {bad_action_from_state_function,Action},
+ ?STACKTRACE(),
Debug, S, [Event|Events], State, NewData, P)
end;
%%
@@ -1169,7 +1214,9 @@ do_reply_then_terminate(
NewDebug, S, Q, State, Data, P, Rs);
_ ->
terminate(
- error, {bad_action,R}, ?STACKTRACE(),
+ error,
+ {bad_reply_action_from_state_function,R},
+ ?STACKTRACE(),
Debug, S, Q, State, Data, P)
end.
@@ -1188,8 +1235,9 @@ terminate(
C:R ->
ST = erlang:get_stacktrace(),
error_info(
- C, R, ST, Debug, S, Q, P,
+ C, R, ST, S, Q, P,
format_status(terminate, get(), S, State, Data)),
+ sys:print_log(Debug),
erlang:raise(C, R, ST)
end,
case Reason of
@@ -1198,8 +1246,9 @@ terminate(
{shutdown,_} -> ok;
_ ->
error_info(
- Class, Reason, Stacktrace, Debug, S, Q, P,
- format_status(terminate, get(), S, State, Data))
+ Class, Reason, Stacktrace, S, Q, P,
+ format_status(terminate, get(), S, State, Data)),
+ sys:print_log(Debug)
end,
case Stacktrace of
[] ->
@@ -1209,7 +1258,7 @@ terminate(
end.
error_info(
- Class, Reason, Stacktrace, Debug,
+ Class, Reason, Stacktrace,
#{name := Name, callback_mode := CallbackMode},
Q, P, FmtData) ->
{FixedReason,FixedStacktrace} =
@@ -1276,9 +1325,7 @@ error_info(
case FixedStacktrace of
[] -> [];
_ -> [FixedStacktrace]
- end),
- sys:print_log(Debug),
- ok.
+ end).
%% Call Module:format_status/2 or return a default value
@@ -1291,7 +1338,7 @@ format_status(Opt, PDict, #{module := Module}, State, Data) ->
_:_ ->
format_status_default(
Opt, State,
- "Module:format_status/2 crashed")
+ atom_to_list(Module) ++ ":format_status/2 crashed")
end;
false ->
format_status_default(Opt, State, Data)
@@ -1299,10 +1346,10 @@ format_status(Opt, PDict, #{module := Module}, State, Data) ->
%% The default Module:format_status/2
format_status_default(Opt, State, Data) ->
- SSD = {State,Data},
+ StateData = {State,Data},
case Opt of
terminate ->
- SSD;
+ StateData;
_ ->
- [{data,[{"State",SSD}]}]
+ [{data,[{"State",StateData}]}]
end.
diff --git a/lib/stdlib/src/shell.erl b/lib/stdlib/src/shell.erl
index 82a3a2be4f..28f37ef8bf 100644
--- a/lib/stdlib/src/shell.erl
+++ b/lib/stdlib/src/shell.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2015. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2016. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -768,6 +768,8 @@ used_records({call,_,{atom,_,record_info},[A,{atom,_,Name}]}) ->
{name, Name, A};
used_records({call,Line,{tuple,_,[M,F]},As}) ->
used_records({call,Line,{remote,Line,M,F},As});
+used_records({type,_,record,[{atom,_,Name}|Fs]}) ->
+ {name, Name, Fs};
used_records(T) when is_tuple(T) ->
{expr, tuple_to_list(T)};
used_records(E) ->
diff --git a/lib/stdlib/src/stdlib.appup.src b/lib/stdlib/src/stdlib.appup.src
index 9877662743..e917b7ea1f 100644
--- a/lib/stdlib/src/stdlib.appup.src
+++ b/lib/stdlib/src/stdlib.appup.src
@@ -18,9 +18,9 @@
%% %CopyrightEnd%
{"%VSN%",
%% Up from - max one major revision back
- [{<<"3\\.0(\\.[0-9]+)*">>,[restart_new_emulator]}, % OTP-19.*
+ [{<<"3\\.[0-1](\\.[0-9]+)*">>,[restart_new_emulator]}, % OTP-19.*
{<<"2\\.[5-8](\\.[0-9]+)*">>,[restart_new_emulator]}], % OTP-18.*
%% Down to - max one major revision back
- [{<<"3\\.0(\\.[0-9]+)*">>,[restart_new_emulator]}, % OTP-19.*
+ [{<<"3\\.[0-1](\\.[0-9]+)*">>,[restart_new_emulator]}, % OTP-19.*
{<<"2\\.[5-8](\\.[0-9]+)*">>,[restart_new_emulator]}] % OTP-18.*
}.
diff --git a/lib/stdlib/src/supervisor.erl b/lib/stdlib/src/supervisor.erl
index c81e72689c..1cd65fbf18 100644
--- a/lib/stdlib/src/supervisor.erl
+++ b/lib/stdlib/src/supervisor.erl
@@ -1087,6 +1087,10 @@ wait_dynamic_children(#child{restart_type=RType} = Child, Pids, Sz,
wait_dynamic_children(Child, ?SETS:del_element(Pid, Pids), Sz-1,
TRef, EStack);
+ {'DOWN', _MRef, process, Pid, {shutdown, _}} ->
+ wait_dynamic_children(Child, ?SETS:del_element(Pid, Pids), Sz-1,
+ TRef, EStack);
+
{'DOWN', _MRef, process, Pid, normal} when RType =/= permanent ->
wait_dynamic_children(Child, ?SETS:del_element(Pid, Pids), Sz-1,
TRef, EStack);
diff --git a/lib/stdlib/src/zip.erl b/lib/stdlib/src/zip.erl
index f8ba6f18e9..340cc21390 100644
--- a/lib/stdlib/src/zip.erl
+++ b/lib/stdlib/src/zip.erl
@@ -279,7 +279,8 @@ do_openzip_get(F, #openzip{files = Files, in = In0, input = Input,
case file_name_search(F, Files) of
{#zip_file{offset = Offset},_}=ZFile ->
In1 = Input({seek, bof, Offset}, In0),
- case get_z_file(In1, Z, Input, Output, [], fun silent/1, CWD, ZFile) of
+ case get_z_file(In1, Z, Input, Output, [], fun silent/1,
+ CWD, ZFile, fun all/1) of
{file, R, _In2} -> {ok, R};
_ -> throw(file_not_found)
end;
@@ -1403,9 +1404,10 @@ get_z_files([{#zip_file{offset = Offset},_} = ZFile | Rest], Z, In0,
true ->
In1 = Input({seek, bof, Offset}, In0),
{In2, Acc1} =
- case get_z_file(In1, Z, Input, Output, OpO, FB, CWD, ZFile) of
+ case get_z_file(In1, Z, Input, Output, OpO, FB,
+ CWD, ZFile, Filter) of
{file, GZD, Inx} -> {Inx, [GZD | Acc0]};
- {dir, Inx} -> {Inx, Acc0}
+ {_, Inx} -> {Inx, Acc0}
end,
get_z_files(Rest, Z, In2, Opts, Acc1);
_ ->
@@ -1413,7 +1415,8 @@ get_z_files([{#zip_file{offset = Offset},_} = ZFile | Rest], Z, In0,
end.
%% get a file from the archive, reading chunks
-get_z_file(In0, Z, Input, Output, OpO, FB, CWD, {ZipFile,Extra}) ->
+get_z_file(In0, Z, Input, Output, OpO, FB,
+ CWD, {ZipFile,Extra}, Filter) ->
case Input({read, ?LOCAL_FILE_HEADER_SZ}, In0) of
{eof, In1} ->
{eof, In1};
@@ -1433,29 +1436,64 @@ get_z_file(In0, Z, Input, Output, OpO, FB, CWD, {ZipFile,Extra}) ->
end,
{BFileN, In3} = Input({read, FileNameLen + ExtraLen}, In1),
{FileName, _} = get_file_name_extra(FileNameLen, ExtraLen, BFileN),
- FileName1 = add_cwd(CWD, FileName),
- case lists:last(FileName) of
- $/ ->
- %% perhaps this should always be done?
- Output({ensure_dir,FileName1},[]),
- {dir, In3};
- _ ->
- %% FileInfo = local_file_header_to_file_info(LH)
- %%{Out, In4, CRC, UncompSize} =
- {Out, In4, CRC, _UncompSize} =
- get_z_data(CompMethod, In3, FileName1,
- CompSize, Input, Output, OpO, Z),
- In5 = skip_z_data_descriptor(GPFlag, Input, In4),
- %% TODO This should be fixed some day:
- %% In5 = Input({set_file_info, FileName, FileInfo#file_info{size=UncompSize}}, In4),
- FB(FileName),
- CRC =:= CRC32 orelse throw({bad_crc, FileName}),
- {file, Out, In5}
+ ReadAndWrite =
+ case check_valid_location(CWD, FileName) of
+ {true,FileName1} ->
+ true;
+ {false,FileName1} ->
+ Filter({ZipFile#zip_file{name = FileName1},Extra})
+ end,
+ case ReadAndWrite of
+ true ->
+ case lists:last(FileName) of
+ $/ ->
+ %% perhaps this should always be done?
+ Output({ensure_dir,FileName1},[]),
+ {dir, In3};
+ _ ->
+ %% FileInfo = local_file_header_to_file_info(LH)
+ %%{Out, In4, CRC, UncompSize} =
+ {Out, In4, CRC, _UncompSize} =
+ get_z_data(CompMethod, In3, FileName1,
+ CompSize, Input, Output, OpO, Z),
+ In5 = skip_z_data_descriptor(GPFlag, Input, In4),
+ %% TODO This should be fixed some day:
+ %% In5 = Input({set_file_info, FileName,
+ %% FileInfo#file_info{size=UncompSize}}, In4),
+ FB(FileName),
+ CRC =:= CRC32 orelse throw({bad_crc, FileName}),
+ {file, Out, In5}
+ end;
+ false ->
+ {ignore, In3}
end;
_ ->
throw(bad_local_file_header)
end.
+%% make sure FileName doesn't have relative path that points over CWD
+check_valid_location(CWD, FileName) ->
+ %% check for directory traversal exploit
+ case check_dir_level(filename:split(FileName), 0) of
+ {FileOrDir,Level} when Level < 0 ->
+ CWD1 = if CWD == "" -> "./";
+ true -> CWD
+ end,
+ error_logger:format("Illegal path: ~ts, extracting in ~ts~n",
+ [add_cwd(CWD,FileName),CWD1]),
+ {false,add_cwd(CWD, FileOrDir)};
+ _ ->
+ {true,add_cwd(CWD, FileName)}
+ end.
+
+check_dir_level([FileOrDir], Level) ->
+ {FileOrDir,Level};
+check_dir_level(["." | Parts], Level) ->
+ check_dir_level(Parts, Level);
+check_dir_level([".." | Parts], Level) ->
+ check_dir_level(Parts, Level-1);
+check_dir_level([_Dir | Parts], Level) ->
+ check_dir_level(Parts, Level+1).
get_file_name_extra(FileNameLen, ExtraLen, B) ->
case B of
diff --git a/lib/stdlib/test/edlin_expand_SUITE.erl b/lib/stdlib/test/edlin_expand_SUITE.erl
index ccffa2e244..718d91c6a3 100644
--- a/lib/stdlib/test/edlin_expand_SUITE.erl
+++ b/lib/stdlib/test/edlin_expand_SUITE.erl
@@ -21,7 +21,7 @@
-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
init_per_testcase/2, end_per_testcase/2,
init_per_group/2,end_per_group/2]).
--export([normal/1, quoted_fun/1, quoted_module/1, quoted_both/1]).
+-export([normal/1, quoted_fun/1, quoted_module/1, quoted_both/1, erl_1152/1]).
-include_lib("common_test/include/ct.hrl").
@@ -36,7 +36,7 @@ suite() ->
{timetrap,{minutes,1}}].
all() ->
- [normal, quoted_fun, quoted_module, quoted_both].
+ [normal, quoted_fun, quoted_module, quoted_both, erl_1152].
groups() ->
[].
@@ -149,5 +149,12 @@ quoted_both(Config) when is_list(Config) ->
{yes,"weird-fun-name'()",[]} = do_expand("'ExpandTestCaps1':'#"),
ok.
+erl_1152(Config) when is_list(Config) ->
+ "\n"++"foo"++" "++[1089]++_ = do_format(["foo",[1089]]),
+ ok.
+
do_expand(String) ->
edlin_expand:expand(lists:reverse(String)).
+
+do_format(StringList) ->
+ lists:flatten(edlin_expand:format_matches(StringList)).
diff --git a/lib/stdlib/test/ets_SUITE.erl b/lib/stdlib/test/ets_SUITE.erl
index 8c1c625676..b02d17bdb6 100644
--- a/lib/stdlib/test/ets_SUITE.erl
+++ b/lib/stdlib/test/ets_SUITE.erl
@@ -47,6 +47,7 @@
fixtable_next/1, fixtable_insert/1, rename/1, rename_unnamed/1, evil_rename/1,
update_element/1, update_counter/1, evil_update_counter/1, partly_bound/1, match_heavy/1]).
-export([update_counter_with_default/1]).
+-export([update_counter_table_growth/1]).
-export([member/1]).
-export([memory/1]).
-export([select_fail/1]).
@@ -100,6 +101,7 @@
heavy_lookup_element_do/1, member_do/1, otp_5340_do/1, otp_7665_do/1, meta_wb_do/1,
do_heavy_concurrent/1, tab2file2_do/2, exit_large_table_owner_do/2,
types_do/1, sleeper/0, memory_do/1, update_counter_with_default_do/1,
+ update_counter_table_growth_do/1,
ms_tracee_dummy/1, ms_tracee_dummy/2, ms_tracee_dummy/3, ms_tracee_dummy/4
]).
@@ -137,6 +139,7 @@ all() ->
rename, rename_unnamed, evil_rename, update_element,
update_counter, evil_update_counter,
update_counter_with_default, partly_bound,
+ update_counter_table_growth,
match_heavy, {group, fold}, member, t_delete_object,
t_init_table, t_whitebox, t_delete_all_objects,
t_insert_list, t_test_ms, t_select_delete, t_ets_dets,
@@ -1955,6 +1958,16 @@ update_counter_with_default_do(Opts) ->
ok.
+update_counter_table_growth(_Config) ->
+ repeat_for_opts(update_counter_table_growth_do).
+
+update_counter_table_growth_do(Opts) ->
+ Set = ets_new(b, [set | Opts]),
+ [ets:update_counter(Set, N, {2, 1}, {N, 1}) || N <- lists:seq(1,10000)],
+ OrderedSet = ets_new(b, [ordered_set | Opts]),
+ [ets:update_counter(OrderedSet, N, {2, 1}, {N, 1}) || N <- lists:seq(1,10000)],
+ ok.
+
%% Check that a first-next sequence always works on a fixed table.
fixtable_next(Config) when is_list(Config) ->
repeat_for_opts(fixtable_next_do, [write_concurrency,all_types]).
diff --git a/lib/stdlib/test/gen_statem_SUITE.erl b/lib/stdlib/test/gen_statem_SUITE.erl
index 364314f91b..1d1417c2e6 100644
--- a/lib/stdlib/test/gen_statem_SUITE.erl
+++ b/lib/stdlib/test/gen_statem_SUITE.erl
@@ -37,33 +37,32 @@ all() ->
{group, stop_handle_event},
{group, abnormal},
{group, abnormal_handle_event},
- shutdown, stop_and_reply, event_order,
+ shutdown, stop_and_reply, event_order, code_change,
{group, sys},
hibernate, enter_loop].
groups() ->
- [{start, [],
- [start1, start2, start3, start4, start5, start6, start7,
- start8, start9, start10, start11, start12, next_events]},
- {start_handle_event, [],
- [start1, start2, start3, start4, start5, start6, start7,
- start8, start9, start10, start11, start12, next_events]},
- {stop, [],
- [stop1, stop2, stop3, stop4, stop5, stop6, stop7, stop8, stop9, stop10]},
- {stop_handle_event, [],
- [stop1, stop2, stop3, stop4, stop5, stop6, stop7, stop8, stop9, stop10]},
- {abnormal, [], [abnormal1, abnormal2]},
- {abnormal_handle_event, [], [abnormal1, abnormal2]},
- {sys, [],
- [sys1, code_change,
- call_format_status,
- error_format_status, terminate_crash_format,
- get_state, replace_state]},
- {sys_handle_event, [],
- [sys1,
- call_format_status,
- error_format_status, terminate_crash_format,
- get_state, replace_state]}].
+ [{start, [], tcs(start)},
+ {start_handle_event, [], tcs(start)},
+ {stop, [], tcs(stop)},
+ {stop_handle_event, [], tcs(stop)},
+ {abnormal, [], tcs(abnormal)},
+ {abnormal_handle_event, [], tcs(abnormal)},
+ {sys, [], tcs(sys)},
+ {sys_handle_event, [], tcs(sys)}].
+
+tcs(start) ->
+ [start1, start2, start3, start4, start5, start6, start7,
+ start8, start9, start10, start11, start12, next_events];
+tcs(stop) ->
+ [stop1, stop2, stop3, stop4, stop5, stop6, stop7, stop8, stop9, stop10];
+tcs(abnormal) ->
+ [abnormal1, abnormal2];
+tcs(sys) ->
+ [sys1, call_format_status,
+ error_format_status, terminate_crash_format,
+ get_state, replace_state].
+
init_per_suite(Config) ->
Config.
@@ -461,10 +460,10 @@ abnormal2(Config) ->
{ok,Pid} = gen_statem:start_link(?MODULE, start_arg(Config, []), []),
%% bad return value in the gen_statem loop
- {{bad_return_value,badreturn},_} =
+ {{bad_return_from_state_function,badreturn},_} =
?EXPECT_FAILURE(gen_statem:call(Pid, badreturn), Reason),
receive
- {'EXIT',Pid,{bad_return_value,badreturn}} -> ok
+ {'EXIT',Pid,{bad_return_from_state_function,badreturn}} -> ok
after 5000 ->
ct:fail(gen_statem_did_not_die)
end,
@@ -633,11 +632,13 @@ sys1(Config) ->
sys:resume(Pid),
stop_it(Pid).
-code_change(Config) ->
- Mode = handle_event_function,
- {ok,Pid} = gen_statem:start(?MODULE, start_arg(Config, []), []),
+code_change(_Config) ->
+ {ok,Pid} =
+ gen_statem:start(
+ ?MODULE, {callback_mode,state_functions,[]}, []),
{idle,data} = sys:get_state(Pid),
sys:suspend(Pid),
+ Mode = handle_event_function,
sys:change_code(Pid, ?MODULE, old_vsn, Mode),
sys:resume(Pid),
{idle,{old_vsn,data,Mode}} = sys:get_state(Pid),
@@ -708,7 +709,7 @@ error_format_status(Config) ->
gen_statem:start(
?MODULE, start_arg(Config, {data,Data}), []),
%% bad return value in the gen_statem loop
- {{bad_return_value,badreturn},_} =
+ {{bad_return_from_state_function,badreturn},_} =
?EXPECT_FAILURE(gen_statem:call(Pid, badreturn), Reason),
receive
{error,_,
@@ -716,7 +717,7 @@ error_format_status(Config) ->
"** State machine"++_,
[Pid,{{call,_},badreturn},
{formatted,idle,Data},
- error,{bad_return_value,badreturn}|_]}} ->
+ error,{bad_return_from_state_function,badreturn}|_]}} ->
ok;
Other when is_tuple(Other), element(1, Other) =:= error ->
error_logger_forwarder:unregister(),
@@ -1029,11 +1030,7 @@ enter_loop(_Config) ->
end,
%% Process not started using proc_lib
- CallbackMode = state_functions,
- Pid4 =
- spawn_link(
- gen_statem, enter_loop,
- [?MODULE,[],CallbackMode,state0,[]]),
+ Pid4 = spawn_link(gen_statem, enter_loop, [?MODULE,[],state0,[]]),
receive
{'EXIT',Pid4,process_was_not_started_by_proc_lib} ->
ok
@@ -1107,21 +1104,18 @@ enter_loop(Reg1, Reg2) ->
anon -> ignore
end,
proc_lib:init_ack({ok, self()}),
- CallbackMode = state_functions,
case Reg2 of
local ->
gen_statem:enter_loop(
- ?MODULE, [], CallbackMode, state0, [], {local,armitage});
+ ?MODULE, [], state0, [], {local,armitage});
global ->
gen_statem:enter_loop(
- ?MODULE, [], CallbackMode, state0, [], {global,armitage});
+ ?MODULE, [], state0, [], {global,armitage});
via ->
gen_statem:enter_loop(
- ?MODULE, [], CallbackMode, state0, [],
- {via, dummy_via, armitage});
+ ?MODULE, [], state0, [], {via, dummy_via, armitage});
anon ->
- gen_statem:enter_loop(
- ?MODULE, [], CallbackMode, state0, [])
+ gen_statem:enter_loop(?MODULE, [], state0, [])
end.
@@ -1266,33 +1260,39 @@ init(stop_shutdown) ->
{stop,shutdown};
init(sleep) ->
?t:sleep(1000),
- {state_functions,idle,data};
+ {ok,idle,data};
init(hiber) ->
- {state_functions,hiber_idle,[]};
+ {ok,hiber_idle,[]};
init(hiber_now) ->
- {state_functions,hiber_idle,[],[hibernate]};
+ {ok,hiber_idle,[],[hibernate]};
init({data, Data}) ->
- {state_functions,idle,Data};
+ {ok,idle,Data};
init({callback_mode,CallbackMode,Arg}) ->
- case init(Arg) of
- {_,State,Data,Ops} ->
- {CallbackMode,State,Data,Ops};
- {_,State,Data} ->
- {CallbackMode,State,Data};
- Other ->
- Other
- end;
+ ets:new(?MODULE, [named_table,private]),
+ ets:insert(?MODULE, {callback_mode,CallbackMode}),
+ init(Arg);
init({map_statem,#{init := Init}=Machine}) ->
+ ets:new(?MODULE, [named_table,private]),
+ ets:insert(?MODULE, {callback_mode,handle_event_function}),
case Init() of
{ok,State,Data,Ops} ->
- {handle_event_function,State,[Data|Machine],Ops};
+ {ok,State,[Data|Machine],Ops};
{ok,State,Data} ->
- {handle_event_function,State,[Data|Machine]};
+ {ok,State,[Data|Machine]};
Other ->
Other
end;
init([]) ->
- {state_functions,idle,data}.
+ {ok,idle,data}.
+
+callback_mode() ->
+ try ets:lookup(?MODULE, callback_mode) of
+ [{callback_mode,CallbackMode}] ->
+ CallbackMode
+ catch
+ error:badarg ->
+ state_functions
+ end.
terminate(_, _State, crash_terminate) ->
exit({crash,terminate});
@@ -1568,7 +1568,12 @@ wrap_result(Result) ->
code_change(OldVsn, State, Data, CallbackMode) ->
- {CallbackMode,State,{OldVsn,Data,CallbackMode}}.
+ io:format(
+ "code_change(~p, ~p, ~p, ~p)~n", [OldVsn,State,Data,CallbackMode]),
+ ets:insert(?MODULE, {callback_mode,CallbackMode}),
+ io:format(
+ "code_change(~p, ~p, ~p, ~p)~n", [OldVsn,State,Data,CallbackMode]),
+ {ok,State,{OldVsn,Data,CallbackMode}}.
format_status(terminate, [_Pdict,State,Data]) ->
{formatted,State,Data};
diff --git a/lib/stdlib/test/shell_SUITE.erl b/lib/stdlib/test/shell_SUITE.erl
index dc82e92876..c409a6949b 100644
--- a/lib/stdlib/test/shell_SUITE.erl
+++ b/lib/stdlib/test/shell_SUITE.erl
@@ -30,7 +30,7 @@
progex_bit_syntax/1, progex_records/1,
progex_lc/1, progex_funs/1,
otp_5990/1, otp_6166/1, otp_6554/1,
- otp_7184/1, otp_7232/1, otp_8393/1, otp_10302/1]).
+ otp_7184/1, otp_7232/1, otp_8393/1, otp_10302/1, otp_13719/1]).
-export([ start_restricted_from_shell/1,
start_restricted_on_command_line/1,restricted_local/1]).
@@ -91,7 +91,7 @@ groups() ->
progex_funs]},
{tickets, [],
[otp_5990, otp_6166, otp_6554, otp_7184,
- otp_7232, otp_8393, otp_10302]}].
+ otp_7232, otp_8393, otp_10302, otp_13719]}].
init_per_suite(Config) ->
Config.
@@ -2810,6 +2810,19 @@ otp_10302(Config) when is_list(Config) ->
test_server:stop_node(Node),
ok.
+otp_13719(Config) when is_list(Config) ->
+ Test = <<"-module(otp_13719).
+ -record(bar, {}).
+ -record(foo, {bar :: #bar{}}).">>,
+ File = filename("otp_13719.erl", Config),
+ Beam = filename("otp_13719.beam", Config),
+ ok = compile_file(Config, File, Test, []),
+ RR = "rr(\"" ++ Beam ++ "\"). #foo{}.",
+ "[bar,foo]\n#foo{bar = undefined}.\n" = t(RR),
+ file:delete(filename("test.beam", Config)),
+ file:delete(File),
+ ok.
+
scan(B) ->
F = fun(Ts) ->
case erl_parse:parse_term(Ts) of
diff --git a/lib/stdlib/test/zip_SUITE.erl b/lib/stdlib/test/zip_SUITE.erl
index 2add5a39a2..7d90795c9e 100644
--- a/lib/stdlib/test/zip_SUITE.erl
+++ b/lib/stdlib/test/zip_SUITE.erl
@@ -25,6 +25,7 @@
zip_to_binary/1,
unzip_options/1, zip_options/1, list_dir_options/1, aliases/1,
openzip_api/1, zip_api/1, open_leak/1, unzip_jar/1,
+ unzip_traversal_exploit/1,
compress_control/1,
foldl/1]).
@@ -38,7 +39,8 @@ all() ->
[borderline, atomic, bad_zip, unzip_from_binary,
unzip_to_binary, zip_to_binary, unzip_options,
zip_options, list_dir_options, aliases, openzip_api,
- zip_api, open_leak, unzip_jar, compress_control, foldl].
+ zip_api, open_leak, unzip_jar, compress_control, foldl,
+ unzip_traversal_exploit].
groups() ->
[].
@@ -377,6 +379,52 @@ unzip_options(Config) when is_list(Config) ->
0 = delete_files([Subdir]),
ok.
+%% Test that unzip handles directory traversal exploit (OTP-13633)
+unzip_traversal_exploit(Config) ->
+ DataDir = proplists:get_value(data_dir, Config),
+ PrivDir = proplists:get_value(priv_dir, Config),
+ ZipName = filename:join(DataDir, "exploit.zip"),
+
+ %% $ zipinfo -1 test/zip_SUITE_data/exploit.zip
+ %% clash.txt
+ %% ../clash.txt
+ %% ../above.txt
+ %% subdir/../in_root_dir.txt
+
+ %% create a temp directory
+ SubDir = filename:join(PrivDir, "exploit_test"),
+ ok = file:make_dir(SubDir),
+
+ ClashFile = filename:join(SubDir,"clash.txt"),
+ AboveFile = filename:join(SubDir,"above.txt"),
+ RelativePathFile = filename:join(SubDir,"subdir/../in_root_dir.txt"),
+
+ %% unzip in SubDir
+ {ok, [ClashFile, ClashFile, AboveFile, RelativePathFile]} =
+ zip:unzip(ZipName, [{cwd,SubDir}]),
+
+ {ok,<<"This file will overwrite other file.\n">>} =
+ file:read_file(ClashFile),
+ {ok,_} = file:read_file(AboveFile),
+ {ok,_} = file:read_file(RelativePathFile),
+
+ %% clean up
+ delete_files([SubDir]),
+
+ %% create the temp directory again
+ ok = file:make_dir(SubDir),
+
+ %% unzip in SubDir
+ {ok, [ClashFile, AboveFile, RelativePathFile]} =
+ zip:unzip(ZipName, [{cwd,SubDir},keep_old_files]),
+
+ {ok,<<"This is the original file.\n">>} =
+ file:read_file(ClashFile),
+
+ %% clean up
+ delete_files([SubDir]),
+ ok.
+
%% Test unzip a jar file (OTP-7382).
unzip_jar(Config) when is_list(Config) ->
DataDir = proplists:get_value(data_dir, Config),
diff --git a/lib/stdlib/test/zip_SUITE_data/exploit.zip b/lib/stdlib/test/zip_SUITE_data/exploit.zip
new file mode 100644
index 0000000000..afb8dbd192
--- /dev/null
+++ b/lib/stdlib/test/zip_SUITE_data/exploit.zip
Binary files differ
diff --git a/lib/stdlib/vsn.mk b/lib/stdlib/vsn.mk
index 46e3ceac03..c74343d9ca 100644
--- a/lib/stdlib/vsn.mk
+++ b/lib/stdlib/vsn.mk
@@ -1 +1 @@
-STDLIB_VSN = 3.0
+STDLIB_VSN = 3.1
diff --git a/lib/syntax_tools/doc/src/notes.xml b/lib/syntax_tools/doc/src/notes.xml
index ef207f7c3d..82c4484d96 100644
--- a/lib/syntax_tools/doc/src/notes.xml
+++ b/lib/syntax_tools/doc/src/notes.xml
@@ -32,6 +32,40 @@
<p>This document describes the changes made to the Syntax_Tools
application.</p>
+<section><title>Syntax_Tools 2.1</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p> Fix a bug where <c>erl_tidy</c> crashed on the tilde
+ character when printing to standard output. </p>
+ <p>
+ Own Id: OTP-13725 Aux Id: ERL-151, PR-1071 </p>
+ </item>
+ <item>
+ <p><c>merl_transform</c> could get into an infinite loop
+ when syntactically incorrect text was passed to a
+ <c>merl:qquote/2,3</c> call.</p>
+ <p>
+ Own Id: OTP-13755</p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>Improve types and specs in OTP documentation generated
+ from Erlang source files. </p>
+ <p>
+ Own Id: OTP-13720 Aux Id: ERL-120 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Syntax_Tools 2.0</title>
<section><title>Improvements and New Features</title>
diff --git a/lib/syntax_tools/src/erl_tidy.erl b/lib/syntax_tools/src/erl_tidy.erl
index 5d3fc6f062..9e273dfb84 100644
--- a/lib/syntax_tools/src/erl_tidy.erl
+++ b/lib/syntax_tools/src/erl_tidy.erl
@@ -210,7 +210,7 @@ file__defaults() ->
{verbose, false}].
default_printer() ->
- fun (Tree, Options) -> erl_prettypr:format(Tree, Options) end.
+ fun erl_prettypr:format/2.
%% =====================================================================
%% @spec file(Name) -> ok
@@ -257,7 +257,7 @@ file(Name) ->
%%
%% <dt>{printer, Function}</dt>
%% <dd><ul>
-%% <li>`Function = (syntaxTree()) -> string()'</li>
+%% <li>`Function = (syntaxTree(), [term()]) -> string()'</li>
%% </ul>
%%
%% Specifies a function for prettyprinting Erlang syntax trees.
diff --git a/lib/syntax_tools/src/merl_tests.erl b/lib/syntax_tools/src/merl_tests.erl
index c1aae3100e..27db594050 100644
--- a/lib/syntax_tools/src/merl_tests.erl
+++ b/lib/syntax_tools/src/merl_tests.erl
@@ -48,6 +48,21 @@ parse_error_test_() ->
f(merl:quote("{")))
].
+transform_parse_error_test_() ->
+ [?_assertEqual("merl:quote(\"{\")",
+ f(merl_transform:parse_transform(
+ [?Q("merl:quote(\"{\")")], []))),
+ ?_assertEqual("merl:quote(2, \"{\")",
+ f(merl_transform:parse_transform(
+ [?Q("merl:quote(2, \"{\")")], []))),
+ ?_assertEqual("merl:qquote(\"{\", [{var, V}])",
+ f(merl_transform:parse_transform(
+ [?Q("merl:qquote(\"{\", [{var, V}])")], []))),
+ ?_assertEqual("merl:qquote(2, \"{\", [{var, V}])",
+ f(merl_transform:parse_transform(
+ [?Q("merl:qquote(2, \"{\", [{var, V}])")], [])))
+ ].
+
term_test_() ->
[?_assertEqual(tuple, erl_syntax:type(merl:term({}))),
?_assertEqual("{foo, 42}", f(merl:term({foo, 42})))
diff --git a/lib/syntax_tools/src/merl_transform.erl b/lib/syntax_tools/src/merl_transform.erl
index fe58b6a122..497baddd0a 100644
--- a/lib/syntax_tools/src/merl_transform.erl
+++ b/lib/syntax_tools/src/merl_transform.erl
@@ -104,10 +104,15 @@ expand_qquote([Text, Env], T, Line) ->
case erl_syntax:is_literal(Text) of
true ->
As = [Line, erl_syntax:concrete(Text)],
- %% expand further if possible
- expand(merl:qquote(Line, "merl:subst(_@tree, _@env)",
- [{tree, eval_call(Line, quote, As, T)},
- {env, Env}]));
+ case eval_call(Line, quote, As, failed) of
+ failed ->
+ T;
+ T1 ->
+ %% expand further if possible
+ expand(merl:qquote(Line, "merl:subst(_@tree, _@env)",
+ [{tree, T1},
+ {env, Env}]))
+ end;
false ->
T
end;
diff --git a/lib/syntax_tools/test/merl_SUITE.erl b/lib/syntax_tools/test/merl_SUITE.erl
index 945972d405..52bbd9b3b8 100644
--- a/lib/syntax_tools/test/merl_SUITE.erl
+++ b/lib/syntax_tools/test/merl_SUITE.erl
@@ -29,12 +29,14 @@
init_per_group/2,end_per_group/2]).
%% Test cases
--export([merl_smoke_test/1]).
+-export([merl_smoke_test/1,
+ transform_parse_error_test/1]).
suite() -> [{ct_hooks,[ts_install_cth]}].
all() ->
- [merl_smoke_test].
+ [merl_smoke_test,
+ transform_parse_error_test].
groups() ->
[].
@@ -84,6 +86,21 @@ merl_smoke_test(Config) when is_list(Config) ->
end)),
ok.
+transform_parse_error_test(_Config) ->
+ ?assertEqual("merl:quote(\"{\")",
+ f(merl_transform:parse_transform(
+ [?Q("merl:quote(\"{\")")], []))),
+ ?assertEqual("merl:quote(2, \"{\")",
+ f(merl_transform:parse_transform(
+ [?Q("merl:quote(2, \"{\")")], []))),
+ ?assertEqual("merl:qquote(\"{\", [{var, V}])",
+ f(merl_transform:parse_transform(
+ [?Q("merl:qquote(\"{\", [{var, V}])")], []))),
+ ?assertEqual("merl:qquote(2, \"{\", [{var, V}])",
+ f(merl_transform:parse_transform(
+ [?Q("merl:qquote(2, \"{\", [{var, V}])")], []))),
+ ok.
+
%% utilities
f(Ts) when is_list(Ts) ->
diff --git a/lib/syntax_tools/vsn.mk b/lib/syntax_tools/vsn.mk
index f09c2a01d0..c0ca083c38 100644
--- a/lib/syntax_tools/vsn.mk
+++ b/lib/syntax_tools/vsn.mk
@@ -1 +1 @@
-SYNTAX_TOOLS_VSN = 2.0
+SYNTAX_TOOLS_VSN = 2.1
diff --git a/lib/tools/doc/src/erlang_mode.xml b/lib/tools/doc/src/erlang_mode.xml
index 00cf5196b4..7fef74813b 100644
--- a/lib/tools/doc/src/erlang_mode.xml
+++ b/lib/tools/doc/src/erlang_mode.xml
@@ -252,7 +252,14 @@
behavior</item>
<item>gen_event - skeleton for the OTP gen_event behavior</item>
<item>gen_fsm - skeleton for the OTP gen_fsm behavior</item>
- <item>gen_statem - skeleton for the OTP gen_statem behavior</item>
+ <item>
+ gen_statem (StateName/3) - skeleton for the OTP gen_statem behavior
+ using state name functions
+ </item>
+ <item>
+ gen_statem (handle_event/4) - skeleton for the OTP gen_statem behavior
+ using one state function
+ </item>
<item>Library module - skeleton for a module that does not
implement a process.</item>
<item>Corba callback - skeleton for a Corba callback module.</item>
diff --git a/lib/tools/doc/src/notes.xml b/lib/tools/doc/src/notes.xml
index a0a817c0f2..2d9bee0dd1 100644
--- a/lib/tools/doc/src/notes.xml
+++ b/lib/tools/doc/src/notes.xml
@@ -31,6 +31,38 @@
</header>
<p>This document describes the changes made to the Tools application.</p>
+<section><title>Tools 2.8.6</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Errors in type specification and Emacs template
+ generation for <c>gen_statem:code_change/4</c> has been
+ fixed from bugs.erlang.org's Jira cases ERL-172 and
+ ERL-187.</p>
+ <p>
+ Own Id: OTP-13746 Aux Id: ERL-172, ERL-187 </p>
+ </item>
+ <item>
+ <p>
+ Fix gc_start/gc_end in fprof tags when parsing old trace
+ logs.</p>
+ <p>
+ Own Id: OTP-13778 Aux Id: PR-1136 </p>
+ </item>
+ <item>
+ <p><c>make</c> (tools) and <c>ct_make</c> (common_test)
+ would crash if an Erlang source file contained a
+ <c>-warning()</c> directive.</p>
+ <p>
+ Own Id: OTP-13855</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Tools 2.8.5</title>
<section><title>Fixed Bugs and Malfunctions</title>
diff --git a/lib/tools/doc/src/xref_chapter.xml b/lib/tools/doc/src/xref_chapter.xml
index 8b14e03064..872793bdcb 100644
--- a/lib/tools/doc/src/xref_chapter.xml
+++ b/lib/tools/doc/src/xref_chapter.xml
@@ -234,7 +234,7 @@
operand of the intersection operator <c>*</c> is implicitly
converted to the more special type of the second operand.</item>
<tag><c>xref:q(s, "(Mod) tools").</c></tag>
- <item>All modules of the <c>tools</c> application.</item>
+ <item>All modules of the Tools application.</item>
<tag><c>xref:q(s, '"xref_.*" : Mod').</c></tag>
<item>All modules with a name beginning with <c>xref_</c>.</item>
<tag><c>xref:q(s, "# E&nbsp;|&nbsp;X&nbsp;").</c></tag>
@@ -252,9 +252,9 @@
<tag><c>xref:q(s, "XC * (ME - strict ME)").</c></tag>
<item>External calls within some module.</item>
<tag><c>xref:q(s, "E&nbsp;|||&nbsp;kernel").</c></tag>
- <item>All calls within the <c>kernel</c> application. </item>
+ <item>All calls within the Kernel application. </item>
<tag><c>xref:q(s, "closure&nbsp;E&nbsp;|&nbsp;kernel&nbsp;||&nbsp;kernel").</c></tag>
- <item>All direct and indirect calls within the <c>kernel</c>
+ <item>All direct and indirect calls within the Kernel
application. Both the calling and the used functions of
indirect calls are defined in modules of the kernel
application, but it is possible that some functions outside
diff --git a/lib/tools/emacs/erlang-skels.el b/lib/tools/emacs/erlang-skels.el
index ce26c83295..0284c9d686 100644
--- a/lib/tools/emacs/erlang-skels.el
+++ b/lib/tools/emacs/erlang-skels.el
@@ -56,8 +56,10 @@
erlang-skel-gen-event erlang-skel-header)
("gen_fsm" "gen-fsm"
erlang-skel-gen-fsm erlang-skel-header)
- ("gen_statem" "gen-statem"
- erlang-skel-gen-statem erlang-skel-header)
+ ("gen_statem (StateName/3)" "gen-statem-StateName"
+ erlang-skel-gen-statem-StateName erlang-skel-header)
+ ("gen_statem (handle_event/4)" "gen-statem-handle-event"
+ erlang-skel-gen-statem-handle-event erlang-skel-header)
("wx_object" "wx-object"
erlang-skel-wx-object erlang-skel-header)
("Library module" "gen-lib"
@@ -497,6 +499,7 @@ Please see the function `tempo-define-template'.")
"%% {stop, Reason}" n
(erlang-skel-separator-end 2)
"init([]) ->" n>
+ "process_flag(trap_exit, true)," n>
"{ok, #state{}}." n
n
(erlang-skel-separator-start 2)
@@ -740,6 +743,7 @@ Please see the function `tempo-define-template'.")
"%% {stop, StopReason}" n
(erlang-skel-separator-end 2)
"init([]) ->" n>
+ "process_flag(trap_exit, true)," n>
"{ok, state_name, #state{}}." n
n
(erlang-skel-separator-start 2)
@@ -860,7 +864,7 @@ Please see the function `tempo-define-template'.")
"*The template of a gen_fsm.
Please see the function `tempo-define-template'.")
-(defvar erlang-skel-gen-statem
+(defvar erlang-skel-gen-statem-StateName
'((erlang-skel-include erlang-skel-large-header)
"-behaviour(gen_statem)." n n
@@ -868,9 +872,8 @@ Please see the function `tempo-define-template'.")
"-export([start_link/0])." n
n
"%% gen_statem callbacks" n
- "-export([init/1, terminate/3, code_change/4])." n
+ "-export([callback_mode/0, init/1, terminate/3, code_change/4])." n
"-export([state_name/3])." n
- "-export([handle_event/4])." n
n
"-define(SERVER, ?MODULE)." n
n
@@ -899,48 +902,151 @@ Please see the function `tempo-define-template'.")
(erlang-skel-separator-start 2)
"%% @private" n
"%% @doc" n
+ "%% Define the callback_mode() for this callback module." n
+ (erlang-skel-separator-end 2)
+ "-spec callback_mode() -> gen_statem:callback_mode()." n
+ "callback_mode() -> state_functions." n
+ n
+ (erlang-skel-separator-start 2)
+ "%% @private" n
+ "%% @doc" n
"%% Whenever a gen_statem is started using gen_statem:start/[3,4] or" n
"%% gen_statem:start_link/[3,4], this function is called by the new" n
"%% process to initialize." n
(erlang-skel-separator-end 2)
- "-spec init(Args :: term()) -> " n>
- "{gen_statem:callback_mode()," n>
- "State :: term(), Data :: term()} |" n>
- "{gen_statem:callback_mode()," n>
- "State :: term(), Data :: term()," n>
+ "-spec init(Args :: term()) ->" n>
+ "{ok, State :: term(), Data :: term()} |" n>
+ "{ok, State :: term(), Data :: term()," n>
"[gen_statem:action()] | gen_statem:action()} |" n>
"ignore |" n>
"{stop, Reason :: term()}." n
"init([]) ->" n>
- "{state_functions, state_name, #data{}}." n
+ "process_flag(trap_exit, true)," n>
+ "{ok, state_name, #data{}}." n
n
(erlang-skel-separator-start 2)
"%% @private" n
"%% @doc" n
- "%% If the gen_statem runs with CallbackMode =:= state_functions" n
- "%% there should be one instance of this function for each possible" n
- "%% state name. Whenever a gen_statem receives an event," n
- "%% the instance of this function with the same name" n
- "%% as the current state name StateName is called to" n
- "%% handle the event." n
+ "%% There should be one function like this for each state name." n
+ "%% Whenever a gen_statem receives an event, the function " n
+ "%% with the name of the current state (StateName) " n
+ "%% is called to handle the event." n
+ "%%" n
+ "%% NOTE: If there is an exported function handle_event/4, it is called" n
+ "%% instead of StateName/3 functions like this!" n
(erlang-skel-separator-end 2)
"-spec state_name(" n>
"gen_statem:event_type(), Msg :: term()," n>
"Data :: term()) ->" n>
- "gen_statem:state_function_result(). " n
+ "gen_statem:state_function_result()." n
"state_name({call,Caller}, _Msg, Data) ->" n>
"{next_state, state_name, Data, [{reply,Caller,ok}]}." n
n
(erlang-skel-separator-start 2)
"%% @private" n
"%% @doc" n
- "%% If the gen_statem runs with CallbackMode =:= handle_event_function" n
- "%% this function is called for every event a gen_statem receives." n
+ "%% This function is called by a gen_statem when it is about to" n
+ "%% terminate. It should be the opposite of Module:init/1 and do any" n
+ "%% necessary cleaning up. When it returns, the gen_statem terminates with" n
+ "%% Reason. The return value is ignored." n
+ (erlang-skel-separator-end 2)
+ "-spec terminate(Reason :: term(), State :: term(), Data :: term()) ->" n>
+ "any()." n
+ "terminate(_Reason, _State, _Data) ->" n>
+ "void." n
+ n
+ (erlang-skel-separator-start 2)
+ "%% @private" n
+ "%% @doc" n
+ "%% Convert process state when code is changed" n
+ (erlang-skel-separator-end 2)
+ "-spec code_change(" n>
+ "OldVsn :: term() | {down,term()}," n>
+ "State :: term(), Data :: term(), Extra :: term()) ->" n>
+ "{ok, NewState :: term(), NewData :: term()} |" n>
+ "(Reason :: term())." n
+ "code_change(_OldVsn, State, Data, _Extra) ->" n>
+ "{ok, State, Data}." n
+ n
+ (erlang-skel-double-separator-start 3)
+ "%%% Internal functions" n
+ (erlang-skel-double-separator-end 3)
+ )
+ "*The template of a gen_statem (StateName/3).
+Please see the function `tempo-define-template'.")
+
+(defvar erlang-skel-gen-statem-handle-event
+ '((erlang-skel-include erlang-skel-large-header)
+ "-behaviour(gen_statem)." n n
+
+ "%% API" n
+ "-export([start_link/0])." n
+ n
+ "%% gen_statem callbacks" n
+ "-export([callback_mode/0, init/1, terminate/3, code_change/4])." n
+ "-export([handle_event/4])." n
+ n
+ "-define(SERVER, ?MODULE)." n
+ n
+ "-record(data, {})." n
+ n
+ (erlang-skel-double-separator-start 3)
+ "%%% API" n
+ (erlang-skel-double-separator-end 3) n
+ (erlang-skel-separator-start 2)
+ "%% @doc" n
+ "%% Creates a gen_statem process which calls Module:init/1 to" n
+ "%% initialize. To ensure a synchronized start-up procedure, this" n
+ "%% function does not return until Module:init/1 has returned." n
+ "%%" n
+ (erlang-skel-separator-end 2)
+ "-spec start_link() ->" n>
+ "{ok, Pid :: pid()} |" n>
+ "ignore |" n>
+ "{error, Error :: term()}." n
+ "start_link() ->" n>
+ "gen_statem:start_link({local, ?SERVER}, ?MODULE, [], [])." n
+ n
+ (erlang-skel-double-separator-start 3)
+ "%%% gen_statem callbacks" n
+ (erlang-skel-double-separator-end 3) n
+ (erlang-skel-separator-start 2)
+ "%% @private" n
+ "%% @doc" n
+ "%% Define the callback_mode() for this callback module." n
+ (erlang-skel-separator-end 2)
+ "-spec callback_mode() -> gen_statem:callback_mode()." n
+ "callback_mode() -> handle_event_function." n
+ n
+ (erlang-skel-separator-start 2)
+ "%% @private" n
+ "%% @doc" n
+ "%% Whenever a gen_statem is started using gen_statem:start/[3,4] or" n
+ "%% gen_statem:start_link/[3,4], this function is called by the new" n
+ "%% process to initialize." n
+ (erlang-skel-separator-end 2)
+ "-spec init(Args :: term()) ->" n>
+ "{ok, State :: term(), Data :: term()} |" n>
+ "{ok, State :: term(), Data :: term()," n>
+ "[gen_statem:action()] | gen_statem:action()} |" n>
+ "ignore |" n>
+ "{stop, Reason :: term()}." n
+ "init([]) ->" n>
+ "process_flag(trap_exit, true)," n>
+ "{ok, state_name, #data{}}." n
+ n
+ (erlang-skel-separator-start 2)
+ "%% @private" n
+ "%% @doc" n
+ "%% This function is called for every event a gen_statem receives." n
+ "%%" n
+ "%% NOTE: If there is no exported function handle_event/4," n
+ "%% StateName/3 functions are called instead!" n
(erlang-skel-separator-end 2)
"-spec handle_event(" n>
"gen_statem:event_type(), Msg :: term()," n>
"State :: term(), Data :: term()) ->" n>
- "gen_statem:handle_event_result(). " n
+ "gen_statem:handle_event_result()." n
"handle_event({call,From}, _Msg, State, Data) ->" n>
"{next_state, State, Data, [{reply,From,ok}]}." n
n
@@ -965,7 +1071,8 @@ Please see the function `tempo-define-template'.")
"-spec code_change(" n>
"OldVsn :: term() | {down,term()}," n>
"State :: term(), Data :: term(), Extra :: term()) ->" n>
- "{ok, NewState :: term(), NewData :: term()}." n
+ "{ok, NewState :: term(), NewData :: term()} |" n>
+ "(Reason :: term())." n
"code_change(_OldVsn, State, Data, _Extra) ->" n>
"{ok, State, Data}." n
n
@@ -973,7 +1080,7 @@ Please see the function `tempo-define-template'.")
"%%% Internal functions" n
(erlang-skel-double-separator-end 3)
)
- "*The template of a gen_statem.
+ "*The template of a gen_statem (handle_event/4).
Please see the function `tempo-define-template'.")
(defvar erlang-skel-wx-object
diff --git a/lib/tools/emacs/erlang-test.el b/lib/tools/emacs/erlang-test.el
index 9a146632c5..ba6190d194 100644
--- a/lib/tools/emacs/erlang-test.el
+++ b/lib/tools/emacs/erlang-test.el
@@ -52,28 +52,42 @@ concatenated to form an erlang file to test on.")
(ert-deftest erlang-test-tags ()
(let* ((dir (make-temp-file "erlang-test" t))
- (erlang-file (expand-file-name "erlang_test.erl" dir))
- (tags-file (expand-file-name "TAGS" dir))
- tags-file-name tags-table-list erlang-buffer)
- (unwind-protect
- (progn
- (erlang-test-create-erlang-file erlang-file)
- (erlang-test-compile-tags erlang-file tags-file)
- (setq erlang-buffer (find-file-noselect erlang-file))
- (with-current-buffer erlang-buffer
- (setq-local tags-file-name tags-file))
- ;; Setting global tags-file-name is a workaround for
- ;; GNU Emacs bug#23164.
- (setq tags-file-name tags-file)
- (erlang-test-completion-table)
- (erlang-test-xref-find-definitions erlang-file erlang-buffer))
- (when (buffer-live-p erlang-buffer)
- (kill-buffer erlang-buffer))
- (let ((tags-buffer (find-buffer-visiting tags-file)))
- (when (buffer-live-p tags-buffer)
- (kill-buffer tags-buffer)))
- (when (file-exists-p dir)
- (delete-directory dir t)))))
+ (erlang-file (expand-file-name "erlang_test.erl" dir))
+ (tags-file (expand-file-name "TAGS" dir))
+ (old-tags-file-name (default-value 'tags-file-name))
+ (old-tags-table-list (default-value 'tags-table-list))
+ tags-file-name
+ tags-table-list
+ tags-table-set-list
+ erlang-buffer
+ erlang-mode-hook
+ prog-mode-hook
+ erlang-shell-mode-hook
+ tags-add-tables)
+ (unwind-protect
+ (progn
+ (setq-default tags-file-name nil)
+ (setq-default tags-table-list nil)
+ (erlang-test-create-erlang-file erlang-file)
+ (erlang-test-compile-tags erlang-file tags-file)
+ (setq erlang-buffer (find-file-noselect erlang-file))
+ (with-current-buffer erlang-buffer
+ (setq-local tags-file-name tags-file))
+ ;; Setting global tags-file-name is a workaround for
+ ;; GNU Emacs bug#23164.
+ (setq tags-file-name tags-file)
+ (erlang-test-complete-at-point tags-file)
+ (erlang-test-completion-table)
+ (erlang-test-xref-find-definitions erlang-file erlang-buffer))
+ (when (buffer-live-p erlang-buffer)
+ (kill-buffer erlang-buffer))
+ (let ((tags-buffer (find-buffer-visiting tags-file)))
+ (when (buffer-live-p tags-buffer)
+ (kill-buffer tags-buffer)))
+ (when (file-exists-p dir)
+ (delete-directory dir t))
+ (setq-default tags-file-name old-tags-file-name)
+ (setq-default tags-table-list old-tags-table-list))))
(defun erlang-test-create-erlang-file (erlang-file)
(with-temp-file erlang-file
@@ -116,6 +130,54 @@ concatenated to form an erlang file to test on.")
(should (eq expected-line (line-number-at-pos)))
(should (= (point-at-bol) (point))))
+(defun erlang-test-complete-at-point (tags-file)
+ (with-temp-buffer
+ (erlang-mode)
+ (setq-local tags-file-name tags-file)
+ (insert "\nerlang_test:fun")
+ (erlang-complete-tag)
+ (should (looking-back "erlang_test:function"))
+ (insert "\nfun")
+ (erlang-complete-tag)
+ (should (looking-back "function"))
+ (insert "\nerlang_")
+ (erlang-complete-tag)
+ (should (looking-back "erlang_test:"))))
+
+
+(ert-deftest erlang-test-compile-options ()
+ (erlang-test-format-opt t
+ "t")
+ (erlang-test-format-opt nil
+ "nil")
+ (erlang-test-format-opt (cons 1 2)
+ "{1, 2}")
+ (erlang-test-format-opt (list 1)
+ "[1]")
+ (erlang-test-format-opt (list 1 2)
+ "[1, 2]")
+ (erlang-test-format-opt (list 1 2 3)
+ "[1, 2, 3]")
+ (erlang-test-format-opt 'symbol
+ "symbol")
+ (erlang-test-format-opt "string"
+ "\"string\"")
+ (erlang-test-format-opt []
+ "{}")
+ (erlang-test-format-opt [1]
+ "{1}")
+ (erlang-test-format-opt [1 2]
+ "{1, 2}")
+ (erlang-test-format-opt [1 2 (3 [4 5 6] 7)]
+ "{1, 2, [3, {4, 5, 6}, 7]}"))
+
+(defun erlang-test-format-opt (elisp &optional expected-erlang)
+ (let ((erlang (inferior-erlang-format-opt elisp)))
+ (message "%s -> %s" elisp erlang)
+ (when expected-erlang
+ (should (equal erlang expected-erlang)))
+ erlang))
+
(provide 'erlang-test)
diff --git a/lib/tools/emacs/erlang.el b/lib/tools/emacs/erlang.el
index 9adc4f1ec8..788de75b3a 100644
--- a/lib/tools/emacs/erlang.el
+++ b/lib/tools/emacs/erlang.el
@@ -1436,8 +1436,6 @@ Other commands:
(erlang-skel-init)
(when (fboundp 'tempo-use-tag-list)
(tempo-use-tag-list 'erlang-tempo-tags))
- (when (boundp 'xref-backend-functions)
- (add-hook 'xref-backend-functions #'erlang-etags--xref-backend nil t))
(run-hooks 'erlang-mode-hook)
(if (zerop (buffer-size))
(run-hooks 'erlang-new-file-hook)))
@@ -1546,9 +1544,7 @@ Other commands:
(set (make-local-variable 'outline-regexp) "[[:lower:]0-9_]+ *(.*) *-> *$")
(set (make-local-variable 'outline-level) (lambda () 1))
(set (make-local-variable 'add-log-current-defun-function)
- 'erlang-current-defun)
- (set (make-local-variable 'find-tag-default-function)
- 'erlang-find-tag-for-completion))
+ 'erlang-current-defun))
(defun erlang-font-lock-init ()
"Initialize Font Lock for Erlang mode."
@@ -3231,18 +3227,16 @@ With argument, do this that many times."
(interactive "p")
(or arg (setq arg 1))
(while (and (looking-at "[ \t]*[%\n]")
- (zerop (forward-line 1))))
+ (zerop (forward-line 1))))
;; Move to the next clause.
(erlang-beginning-of-clause (- arg))
(beginning-of-line);; Just to be sure...
(let ((continue t))
(while (and (not (bobp)) continue)
(forward-line -1)
- (skip-chars-forward " \t")
- (if (looking-at "[%\n]")
- nil
- (end-of-line)
- (setq continue nil)))))
+ (unless (looking-at "[ \t]*[%\n]")
+ (end-of-line)
+ (setq continue nil)))))
(defun erlang-mark-clause ()
"Put mark at end of clause, point at beginning."
@@ -4350,11 +4344,6 @@ as on the old form `tag'.
In the completion list, `module:tag' and `module:' shows up.
-Call this function from an appropriate init file, or add it to
-Erlang mode hook with the commands:
- (add-hook 'erlang-mode-hook 'erlang-tags-init)
- (add-hook 'erlang-shell-mode-hook 'erlang-tags-init)
-
This function only works under Emacs 18 and Emacs 19. Currently, It
is not implemented under XEmacs. (Hint: The Emacs 19 etags module
works under XEmacs.)"
@@ -4365,11 +4354,16 @@ works under XEmacs.)"
(setq erlang-tags-installed t))
(t
(require 'etags)
- ;; Test on a function available in the Emacs 19 version
- ;; of tags but not in the XEmacs version.
- (when (fboundp 'find-tag-noselect)
- (erlang-tags-define-keys (current-local-map))
- (setq erlang-tags-installed t)))))
+ (set (make-local-variable 'find-tag-default-function)
+ 'erlang-find-tag-for-completion)
+ (if (>= emacs-major-version 25)
+ (add-hook 'xref-backend-functions
+ #'erlang-etags--xref-backend nil t)
+ ;; Test on a function available in the Emacs 19 version
+ ;; of tags but not in the XEmacs version.
+ (when (fboundp 'find-tag-noselect)
+ (erlang-tags-define-keys (current-local-map))
+ (setq erlang-tags-installed t))))))
@@ -4773,8 +4767,6 @@ for a tag on the form `module:tag'."
;;; completion-table' containing all normal tags plus tags on the form
;;; `module:tag' and `module:'.
-;; PENDING - Should probably make use of the
-;; `completion-at-point-functions' hook instead of this advice.
(when (and (locate-library "etags")
(require 'etags)
(fboundp 'etags-tags-completion-table)
@@ -4782,11 +4774,7 @@ for a tag on the form `module:tag'."
(if (fboundp 'advice-add)
;; Emacs 24.4+
(advice-add 'etags-tags-completion-table :around
- (lambda (oldfun)
- (if erlang-replace-etags-tags-completion-table
- (erlang-etags-tags-completion-table)
- (funcall oldfun)))
- (list :name 'erlang-replace-tags-table))
+ #'erlang-etags-tags-completion-table-advice)
;; Emacs 23.1-24.3
(defadvice etags-tags-completion-table (around
erlang-replace-tags-table
@@ -4795,6 +4783,11 @@ for a tag on the form `module:tag'."
(setq ad-return-value (erlang-etags-tags-completion-table))
ad-do-it))))
+(defun erlang-etags-tags-completion-table-advice (oldfun)
+ (if erlang-replace-etags-tags-completion-table
+ (erlang-etags-tags-completion-table)
+ (funcall oldfun)))
+
(defun erlang-complete-tag ()
"Perform tags completion on the text around point.
Completes to the set of names listed in the current tags table.
@@ -4805,8 +4798,7 @@ about Erlang modules."
(condition-case nil
(require 'etags)
(error nil))
- (cond ((and erlang-tags-installed
- (fboundp 'etags-tags-completion-table)
+ (cond ((and (fboundp 'etags-tags-completion-table)
(fboundp 'tags-lazy-completion-table)) ; Emacs 23.1+
(let ((erlang-replace-etags-tags-completion-table t))
(complete-tag)))
@@ -5211,19 +5203,10 @@ The following special commands are available:
(setq comint-input-ignoredups t)
(setq comint-scroll-show-maximum-output t)
(setq comint-scroll-to-bottom-on-output t)
- ;; In Emacs 19.30, `add-hook' has got a `local' flag, use it. If
- ;; the call fails, just call the normal `add-hook'.
- (condition-case nil
- (progn
- (add-hook 'comint-output-filter-functions
- 'inferior-erlang-strip-delete nil t)
- (add-hook 'comint-output-filter-functions
- 'inferior-erlang-strip-ctrl-m nil t))
- (error
- (funcall (symbol-function 'make-local-hook)
- 'comint-output-filter-functions) ; obsolete as of Emacs 21.1
- (add-hook 'comint-output-filter-functions 'inferior-erlang-strip-delete)
- (add-hook 'comint-output-filter-functions 'inferior-erlang-strip-ctrl-m)))
+ (add-hook 'comint-output-filter-functions
+ 'inferior-erlang-strip-delete nil t)
+ (add-hook 'comint-output-filter-functions
+ 'inferior-erlang-strip-ctrl-m nil t)
;; Some older versions of comint don't have an input ring.
(if (fboundp 'comint-read-input-ring)
(progn
@@ -5249,6 +5232,7 @@ The following special commands are available:
(define-key map [menu-bar compilation]
(cons "Errors" compilation-menu-map)))
map)))))
+ (erlang-tags-init)
(run-hooks 'erlang-shell-mode-hook))
@@ -5728,31 +5712,29 @@ unless the optional NO-DISPLAY is non-nil."
(defun inferior-erlang-format-comma-opts (opts)
(if (null opts)
""
- (concat ", " (inferior-erlang-format-opts opts))))
-
-(defun inferior-erlang-format-opts (opts)
- (concat "[" (inferior-erlang-string-join (mapcar 'inferior-erlang-format-opt
- opts)
- ", ")
- "]"))
+ (concat ", " (inferior-erlang-format-opt opts))))
(defun inferior-erlang-format-opt (opt)
(cond ((stringp opt) (concat "\"" opt "\""))
- ((atom opt) (format "%s" opt))
- ((consp opt) (concat "{" (inferior-erlang-string-join
- (mapcar 'inferior-erlang-format-opt
- (list (car opt) (cdr opt)))
- ", ")
- "}"))
- (t (error (format "Unexpected opt %s" opt)))))
-
-(defun inferior-erlang-string-join (strs sep)
- (let ((result (or (car strs) "")))
- (setq strs (cdr strs))
- (while strs
- (setq result (concat result sep (car strs)))
- (setq strs (cdr strs)))
- result))
+ ((vectorp opt) (inferior-erlang-tuple (append opt nil)))
+ ((atom opt) (format "%s" opt))
+ ((consp opt) (if (listp (cdr opt))
+ (inferior-erlang-list opt)
+ (inferior-erlang-tuple (list (car opt) (cdr opt)))))
+ (t (error "Unexpected erlang compile option %s" opt))))
+
+(defun inferior-erlang-tuple (opts)
+ (concat "{" (mapconcat 'inferior-erlang-format-opt
+ opts
+ ", ")
+ "}"))
+
+(defun inferior-erlang-list (opts)
+ (concat "[" (mapconcat 'inferior-erlang-format-opt
+ opts
+ ", ")
+ "]"))
+
(defun erlang-local-buffer-file-name ()
;; When editing a file remotely via tramp,
diff --git a/lib/tools/src/fprof.erl b/lib/tools/src/fprof.erl
index 8db23dd151..1291a3e5ec 100644
--- a/lib/tools/src/fprof.erl
+++ b/lib/tools/src/fprof.erl
@@ -1636,6 +1636,11 @@ trace_handler({trace_ts, Pid, gc_major_start, _Func, TS} = Trace, Table, _, Dump
dump_stack(Dump, get(Pid), Trace),
trace_gc_start(Table, Pid, TS),
TS;
+
+trace_handler({trace_ts, Pid, gc_start, _Func, TS} = Trace, Table, _, Dump) ->
+ dump_stack(Dump, get(Pid), Trace),
+ trace_gc_start(Table, Pid, TS),
+ TS;
%%
%% gc_end
@@ -1648,6 +1653,12 @@ trace_handler({trace_ts, Pid, gc_major_end, _Func, TS} = Trace, Table, _, Dump)
dump_stack(Dump, get(Pid), Trace),
trace_gc_end(Table, Pid, TS),
TS;
+
+trace_handler({trace_ts, Pid, gc_end, _Func, TS} = Trace, Table, _, Dump) ->
+ dump_stack(Dump, get(Pid), Trace),
+ trace_gc_end(Table, Pid, TS),
+ TS;
+
%%
%% link
trace_handler({trace_ts, Pid, link, _OtherPid, TS} = Trace,
diff --git a/lib/tools/src/make.erl b/lib/tools/src/make.erl
index 26378f28a0..37e67cbe34 100644
--- a/lib/tools/src/make.erl
+++ b/lib/tools/src/make.erl
@@ -317,5 +317,7 @@ check_includes2(Epp, File, ObjMTime) ->
epp:close(Epp),
false;
{error, _Error} ->
+ check_includes2(Epp, File, ObjMTime);
+ {warning, _Warning} ->
check_includes2(Epp, File, ObjMTime)
end.
diff --git a/lib/tools/src/tools.app.src b/lib/tools/src/tools.app.src
index a00969eabe..18166cceb0 100644
--- a/lib/tools/src/tools.app.src
+++ b/lib/tools/src/tools.app.src
@@ -40,7 +40,7 @@
{env, [{file_util_search_methods,[{"", ""}, {"ebin", "esrc"}, {"ebin", "src"}]}
]
},
- {runtime_dependencies, ["stdlib-2.5","runtime_tools-1.8.14",
+ {runtime_dependencies, ["stdlib-3.1","runtime_tools-1.8.14",
"kernel-3.0","inets-5.10","erts-7.0",
"compiler-5.0"]}
]
diff --git a/lib/tools/test/cover_SUITE.erl b/lib/tools/test/cover_SUITE.erl
index b4c9264b30..90e113c178 100644
--- a/lib/tools/test/cover_SUITE.erl
+++ b/lib/tools/test/cover_SUITE.erl
@@ -1001,41 +1001,40 @@ otp_6115(Config) when is_list(Config) ->
%% Cover compile f1, but not f2
{ok, f1} = cover:compile(f1),
+ %% This test used to ensure that a process containing a
+ %% fun refering to cover compiled code was killed.
+ %% check_process_code may however ignore funs as of ERTS
+ %% version 8.1. The test has therefore been rewritten to
+ %% check that a process with a direct reference (in this
+ %% case a return address) to the code is killed.
+ %%
%% If f1 is cover compiled, a process P is started with a
- %% reference to the fun created in start_fail/0, and cover:stop() is
- %% called, then P should be killed.
- %% This is because (the fun held by P) references the cover
+ %% direct reference to the f1, and cover:stop() is called,
+ %% then P should be killed.
+ %% This is because of the return address to the cover
%% compiled code which should be *unloaded* when cover:stop() is
%% called -- running cover compiled code when there is no cover
%% server and thus no ets tables to bump counters in, makes no
%% sense.
- Pid1 = f1:start_a(),
- Pid2 = f1:start_b(),
+ Pid = spawn(fun () -> f1:non_tail_call_f2_wait() end),
%% Now stop cover
cover:stop(),
%% Ensure that f1 is loaded (and not cover compiled), and that
- %% both Pid1 and Pid2 are dead.
+ %% both Pid is dead.
case code:which(f1) of
Beam when is_list(Beam) ->
ok;
Other ->
ct:fail({"f1 is not reloaded", Other})
end,
- case process_info(Pid1) of
+ case process_info(Pid) of
undefined ->
ok;
- _PI1 ->
- RefToOldP1 = erlang:check_process_code(Pid1, f1),
- ct:fail({"Pid1 still alive", RefToOldP1})
- end,
- case process_info(Pid2) of
- undefined ->
- ok;
- _PI2 ->
- RefToOldP2 = erlang:check_process_code(Pid1, f2),
- ct:fail({"Pid2 still alive", RefToOldP2})
+ _PI ->
+ RefToOldP = erlang:check_process_code(Pid, f1),
+ ct:fail({"Pid still alive", RefToOldP})
end,
file:set_cwd(CWD),
diff --git a/lib/tools/test/cover_SUITE_data/otp_6115/f1.erl b/lib/tools/test/cover_SUITE_data/otp_6115/f1.erl
index 5399b33f19..fc4a62e70e 100644
--- a/lib/tools/test/cover_SUITE_data/otp_6115/f1.erl
+++ b/lib/tools/test/cover_SUITE_data/otp_6115/f1.erl
@@ -1,13 +1,6 @@
-module(f1).
--export([start_a/0, start_b/0]).
+-export([non_tail_call_f2_wait/0]).
-start_a() ->
- f2:start(fun() ->
- ok
- end).
-
-start_b() ->
- f2:start(fun fun1/0).
-
-fun1() ->
- ok.
+non_tail_call_f2_wait() ->
+ f2:wait(),
+ im_back.
diff --git a/lib/tools/test/cover_SUITE_data/otp_6115/f2.erl b/lib/tools/test/cover_SUITE_data/otp_6115/f2.erl
index 72a6a64c4d..4bc88035c7 100644
--- a/lib/tools/test/cover_SUITE_data/otp_6115/f2.erl
+++ b/lib/tools/test/cover_SUITE_data/otp_6115/f2.erl
@@ -1,13 +1,5 @@
-module(f2).
--export([start/1]).
+-export([wait/0]).
-start(Fun) ->
- spawn(fun() ->
- wait(Fun)
- end).
-
-wait(Fun) ->
- receive
- go ->
- Fun()
- end.
+wait() ->
+ receive after infinity -> ok end.
diff --git a/lib/tools/test/make_SUITE_data/test1.erl b/lib/tools/test/make_SUITE_data/test1.erl
index f4a133008e..9e21bdc767 100644
--- a/lib/tools/test/make_SUITE_data/test1.erl
+++ b/lib/tools/test/make_SUITE_data/test1.erl
@@ -3,6 +3,8 @@
-vsn('$Revision: /main/release/2 $').
-compile(export_all).
+-warning("a warning").
+
f1() ->
true.
diff --git a/lib/tools/vsn.mk b/lib/tools/vsn.mk
index e6287b0430..e066dbf5e9 100644
--- a/lib/tools/vsn.mk
+++ b/lib/tools/vsn.mk
@@ -1 +1 @@
-TOOLS_VSN = 2.8.5
+TOOLS_VSN = 2.8.6
diff --git a/lib/wx/api_gen/wx_extra/wxListCtrl.erl b/lib/wx/api_gen/wx_extra/wxListCtrl.erl
index acdb69fdeb..355a4cdfd1 100644
--- a/lib/wx/api_gen/wx_extra/wxListCtrl.erl
+++ b/lib/wx/api_gen/wx_extra/wxListCtrl.erl
@@ -43,33 +43,36 @@ SortItems>>
<<EXPORT:wxListCtrl new/0, new/1, new/2 wxListCtrl:EXPORT>>
<<wxListCtrl_new_0
-%% @spec () -> wxListCtrl()
%% @doc See <a href="http://www.wxwidgets.org/manuals/stable/wx_wxlistctrl.html#wxlistctrlwxlistctrl">external documentation</a>.
+-spec new() -> wxListCtrl().
new() ->
wxe_util:construct(~s, <<>>).
wxListCtrl_new_0>>
<<wxListCtrl_new_2
-%% @spec (Parent::wxWindow:wxWindow()) -> wxListCtrl()
-%% @equiv new(Parent, [])
+-spec new(Parent) -> wxListCtrl() when
+ Parent::wxWindow:wxWindow().
new(Parent)
when is_record(Parent, wx_ref) ->
new(Parent, []).
-%% @spec (Parent::wxWindow:wxWindow(), [Option]) -> wxListCtrl()
-%% Option = {winid, integer()} |
-%% {pos, {X::integer(),Y::integer()}} |
-%% {size, {W::integer(),H::integer()}} |
-%% {style, integer()} |
-%% {validator, wx:wx()} |
-%% {onGetItemText, OnGetItemText} |
-%% {onGetItemAttr, OnGetItemAttr} |
-%% {onGetItemColumnImage, OnGetItemColumnImage}
+%% @doc Creates a listctrl with optional callback functions:
%%
-%% OnGetItemText = (This, Item, Column) -> wxString()
-%% OnGetItemAttr = (This, Item) -> wxListItemAttr()
+%% OnGetItemText = (This, Item, Column) -> unicode:charlist()
+%% OnGetItemAttr = (This, Item) -> wxListItemAttr:wxListItemAttr()
%% OnGetItemColumnImage = (This, Item, Column) -> integer()
-%% @doc See <a href="http://www.wxwidgets.org/manuals/stable/wx_wxlistctrl.html#wxlistctrlwxlistctrl">external documentation</a>.
+%%
+%% See <a href="http://www.wxwidgets.org/manuals/stable/wx_wxlistctrl.html#wxlistctrlwxlistctrl">external documentation</a>.
+-spec new(Parent, [Option]) -> wxListCtrl() when
+ Parent::wxWindow:wxWindow(),
+ Option::{winid, integer()} |
+ {pos, {X::integer(),Y::integer()}} |
+ {size, {W::integer(),H::integer()}} |
+ {style, integer()} |
+ {validator, wx:wx_object()} |
+ {onGetItemText, function()} |
+ {onGetItemAttr, function()} |
+ {onGetItemColumnImage, function()}.
new(#wx_ref{type=ParentT,ref=ParentRef}, Options)
when is_list(Options)->
@@ -101,26 +104,27 @@ wxListCtrl_new_2>>
<<EXPORT:Create create/2, create/3 Create:EXPORT>>
<<Create
-%% @spec (This::wxListCtrl(), Parent::wxWindow:wxWindow()) -> bool()
%% @equiv create(This,Parent, [])
+-spec create(This, Parent) -> wxListCtrl() when
+ This::wxWindow:wxWindow(),
+ Parent::wxWindow:wxWindow().
create(This,Parent)
when is_record(This, wx_ref),is_record(Parent, wx_ref) ->
create(This,Parent, []).
-%% @spec (This::wxListCtrl(), Parent::wxWindow:wxWindow(), [Option]) -> bool()
-%% Option = {winid, integer()} |
-%% {pos, {X::integer(),Y::integer()}} |
-%% {size, {W::integer(),H::integer()}} |
-%% {style, integer()} |
-%% {validator, wx:wx()} |
-%% {onGetItemText, OnGetItemText} |
-%% {onGetItemAttr, OnGetItemAttr} |
-%% {onGetItemColumnImage, OnGetItemColumnImage}
-%%
-%% OnGetItemText = (This, Item, Column) -> wxString()
-%% OnGetItemAttr = (This, Item) -> wxListItemAttr()
-%% OnGetItemColumnImage = (This, Item, Column) -> integer()
%% @doc See <a href="http://www.wxwidgets.org/manuals/stable/wx_wxlistctrl.html#wxlistctrlcreate">external documentation</a>.
+-spec create(This, Parent, [Option]) -> wxListCtrl() when
+ This::wxWindow:wxWindow(),
+ Parent::wxWindow:wxWindow(),
+ Option::{winid, integer()} |
+ {pos, {X::integer(),Y::integer()}} |
+ {size, {W::integer(),H::integer()}} |
+ {style, integer()} |
+ {validator, wx:wx_object()} |
+ {onGetItemText, function()} |
+ {onGetItemAttr, function()} |
+ {onGetItemColumnImage, function()}.
+
create(#wx_ref{type=ThisT,ref=ThisRef},#wx_ref{type=ParentT,ref=ParentRef}, Options)
when is_list(Options) ->
?CLASS(ThisT,wxListCtrl),
diff --git a/lib/wx/api_gen/wx_extra/wxXmlResource.erl b/lib/wx/api_gen/wx_extra/wxXmlResource.erl
index 7700e2333e..b29ffba7c6 100644
--- a/lib/wx/api_gen/wx_extra/wxXmlResource.erl
+++ b/lib/wx/api_gen/wx_extra/wxXmlResource.erl
@@ -21,8 +21,6 @@
<<EXPORT:xrcctrl xrcctrl/3 xrcctrl:EXPORT>>
<<xrcctrl
-%% @spec (Window::wxWindow:wxWindow(),Name::string(), Type::atom()) -> wx:wxObject()
-
%% @doc Looks up a control with Name in a window created with XML
%% resources. You can use it to set/get values from controls.
%% The object is type casted to <b>Type</b>.
@@ -32,6 +30,10 @@
%% true = wxXmlResource:loadDialog(Xrc, Dlg, Frame, "controls_dialog"), <br />
%% LCtrl = xrcctrl(Dlg, "controls_listctrl", wxListCtrl), <br />
%% wxListCtrl:insertColumn(LCtrl, 0, "Name", [{width, 200}]), <br />
+-spec xrcctrl(Window, Name, Type) -> wx:wx_object() when
+ Window::wxWindow:wxWindow(),
+ Name::string(),
+ Type::atom().
xrcctrl(Window = #wx_ref{}, Name, Type) when is_list(Name), is_atom(Type) ->
%% Func Id ~s
diff --git a/lib/wx/c_src/wxe_main.cpp b/lib/wx/c_src/wxe_main.cpp
index 6fcde42eb5..c7565e33bd 100644
--- a/lib/wx/c_src/wxe_main.cpp
+++ b/lib/wx/c_src/wxe_main.cpp
@@ -67,6 +67,7 @@ int load_native_gui()
int start_native_gui(wxe_data *sd)
{
int res;
+ ErlDrvThreadOpts *opts = NULL;
wxe_status_m = erl_drv_mutex_create((char *) "wxe_status_m");
wxe_status_c = erl_drv_cond_create((char *)"wxe_status_c");
@@ -78,8 +79,11 @@ int start_native_gui(wxe_data *sd)
res = erl_drv_steal_main_thread((char *)"wxwidgets",
&wxe_thread,wxe_main_loop,(void *) sd->pdl,NULL);
#else
+ opts = erl_drv_thread_opts_create((char *)"wx thread");
+ opts->suggested_stack_size = 8192;
res = erl_drv_thread_create((char *)"wxwidgets",
- &wxe_thread,wxe_main_loop,(void *) sd->pdl,NULL);
+ &wxe_thread,wxe_main_loop,(void *) sd->pdl,opts);
+ erl_drv_thread_opts_destroy(opts);
#endif
if(res == 0) {
erl_drv_mutex_lock(wxe_status_m);
diff --git a/lib/wx/doc/src/notes.xml b/lib/wx/doc/src/notes.xml
index 4f0e166924..70ff0a92b7 100644
--- a/lib/wx/doc/src/notes.xml
+++ b/lib/wx/doc/src/notes.xml
@@ -32,6 +32,23 @@
<p>This document describes the changes made to the wxErlang
application.</p>
+<section><title>Wx 1.7.1</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Increased the stacksize for the wx thread. The default
+ stacksize on Windows is 1MB which is not enough if the
+ user created many nested dialogs.</p>
+ <p>
+ Own Id: OTP-13816</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Wx 1.7</title>
<section><title>Fixed Bugs and Malfunctions</title>
diff --git a/lib/wx/include/wx.hrl b/lib/wx/include/wx.hrl
index af7cca7ed2..30baeebb18 100644
--- a/lib/wx/include/wx.hrl
+++ b/lib/wx/include/wx.hrl
@@ -33,94 +33,51 @@
%% Here comes the definitions of all event records.
%% they contain the event type and possible some extra information.
--record(wxInitDialog, {type :: wxInitDialogEventType()}). %% Callback event: {@link wxInitDialogEvent}
--type wxInitDialogEventType() :: 'init_dialog'.
--type wxInitDialog() :: #wxInitDialog{}. %% Callback event: {@link wxInitDialogEvent}
-
--record(wxClose, {type :: wxCloseEventType()}). %% Callback event: {@link wxCloseEvent}
--type wxCloseEventType() :: 'close_window' | 'end_session' | 'query_end_session'.
--type wxClose() :: #wxClose{}. %% Callback event: {@link wxCloseEvent}
-
--record(wxStyledText,{type :: wxStyledTextEventType(), %% Callback event: {@link wxStyledTextEvent}
- position :: integer(),
- key :: integer(),
- modifiers :: integer(),
- modificationType :: integer(),
- text :: unicode:chardata(),
- length :: integer(),
- linesAdded :: integer(),
- line :: integer(),
- foldLevelNow :: integer(),
- foldLevelPrev :: integer(),
- margin :: integer(),
- message :: integer(),
- wParam :: integer(),
- lParam :: integer(),
- listType :: integer(),
- x :: integer(),
- y :: integer(),
- dragText :: unicode:chardata(),
- dragAllowMove :: boolean(),
- dragResult :: wx:wx_enum()}).
--type wxStyledTextEventType() :: 'stc_change' | 'stc_styleneeded' | 'stc_charadded' | 'stc_savepointreached' | 'stc_savepointleft' | 'stc_romodifyattempt' | 'stc_key' | 'stc_doubleclick' | 'stc_updateui' | 'stc_modified' | 'stc_macrorecord' | 'stc_marginclick' | 'stc_needshown' | 'stc_painted' | 'stc_userlistselection' | 'stc_uridropped' | 'stc_dwellstart' | 'stc_dwellend' | 'stc_start_drag' | 'stc_drag_over' | 'stc_do_drop' | 'stc_zoom' | 'stc_hotspot_click' | 'stc_hotspot_dclick' | 'stc_calltip_click' | 'stc_autocomp_selection'.
--type wxStyledText() :: #wxStyledText{}. %% Callback event: {@link wxStyledTextEvent}
-
--record(wxFileDirPicker,{type :: wxFileDirPickerEventType(), %% Callback event: {@link wxFileDirPickerEvent}
- path :: unicode:chardata()}).
--type wxFileDirPickerEventType() :: 'command_filepicker_changed' | 'command_dirpicker_changed'.
--type wxFileDirPicker() :: #wxFileDirPicker{}. %% Callback event: {@link wxFileDirPickerEvent}
-
--record(wxNotebook,{type :: wxNotebookEventType(), %% Callback event: {@link wxNotebookEvent}
- nSel :: integer(),
- nOldSel :: integer()}).
--type wxNotebookEventType() :: 'command_notebook_page_changed' | 'command_notebook_page_changing'.
--type wxNotebook() :: #wxNotebook{}. %% Callback event: {@link wxNotebookEvent}
+-record(wxFocus,{type :: wxFocusEventType(), %% Callback event: {@link wxFocusEvent}
+ win :: wxWindow:wxWindow()}).
+-type wxFocusEventType() :: 'set_focus' | 'kill_focus'.
+-type wxFocus() :: #wxFocus{}. %% Callback event: {@link wxFocusEvent}
--record(wxIdle, {type :: wxIdleEventType()}). %% Callback event: {@link wxIdleEvent}
--type wxIdleEventType() :: 'idle'.
--type wxIdle() :: #wxIdle{}. %% Callback event: {@link wxIdleEvent}
+-record(wxCommand,{type :: wxCommandEventType(), %% Callback event: {@link wxCommandEvent}
+ cmdString :: unicode:chardata(),
+ commandInt :: integer(),
+ extraLong :: integer()}).
+-type wxCommandEventType() :: 'command_button_clicked' | 'command_checkbox_clicked' | 'command_choice_selected' | 'command_listbox_selected' | 'command_listbox_doubleclicked' | 'command_text_updated' | 'command_text_enter' | 'command_menu_selected' | 'command_slider_updated' | 'command_radiobox_selected' | 'command_radiobutton_selected' | 'command_scrollbar_updated' | 'command_vlbox_selected' | 'command_combobox_selected' | 'command_tool_rclicked' | 'command_tool_enter' | 'command_checklistbox_toggled' | 'command_togglebutton_clicked' | 'command_left_click' | 'command_left_dclick' | 'command_right_click' | 'command_set_focus' | 'command_kill_focus' | 'command_enter'.
+-type wxCommand() :: #wxCommand{}. %% Callback event: {@link wxCommandEvent}
-record(wxColourPicker,{type :: wxColourPickerEventType(), %% Callback event: {@link wxColourPickerEvent}
colour :: wx:wx_colour()}).
-type wxColourPickerEventType() :: 'command_colourpicker_changed'.
-type wxColourPicker() :: #wxColourPicker{}. %% Callback event: {@link wxColourPickerEvent}
--record(wxSplitter, {type :: wxSplitterEventType()}). %% Callback event: {@link wxSplitterEvent}
--type wxSplitterEventType() :: 'command_splitter_sash_pos_changed' | 'command_splitter_sash_pos_changing' | 'command_splitter_doubleclicked' | 'command_splitter_unsplit'.
--type wxSplitter() :: #wxSplitter{}. %% Callback event: {@link wxSplitterEvent}
-
--record(wxSash,{type :: wxSashEventType(), %% Callback event: {@link wxSashEvent}
- edge :: wx:wx_enum(),
- dragRect :: {X::integer(), Y::integer(), W::integer(), H::integer()},
- dragStatus :: wx:wx_enum()}).
--type wxSashEventType() :: 'sash_dragged'.
--type wxSash() :: #wxSash{}. %% Callback event: {@link wxSashEvent}
-
-record(wxHelp, {type :: wxHelpEventType()}). %% Callback event: {@link wxHelpEvent}
-type wxHelpEventType() :: 'help' | 'detailed_help'.
-type wxHelp() :: #wxHelp{}. %% Callback event: {@link wxHelpEvent}
--record(wxDisplayChanged, {type :: wxDisplayChangedEventType()}). %% Callback event: {@link wxDisplayChangedEvent}
--type wxDisplayChangedEventType() :: 'display_changed'.
--type wxDisplayChanged() :: #wxDisplayChanged{}. %% Callback event: {@link wxDisplayChangedEvent}
-
--record(wxMouseCaptureLost, {type :: wxMouseCaptureLostEventType()}). %% Callback event: {@link wxMouseCaptureLostEvent}
--type wxMouseCaptureLostEventType() :: 'mouse_capture_lost'.
--type wxMouseCaptureLost() :: #wxMouseCaptureLost{}. %% Callback event: {@link wxMouseCaptureLostEvent}
+-record(wxSpin,{type :: wxSpinEventType(), %% Callback event: {@link wxSpinEvent}
+ commandInt :: integer()}).
+-type wxSpinEventType() :: 'command_spinctrl_updated' | 'spin_up' | 'spin_down' | 'spin'.
+-type wxSpin() :: #wxSpin{}. %% Callback event: {@link wxSpinEvent}
--record(wxFontPicker,{type :: wxFontPickerEventType(), %% Callback event: {@link wxFontPickerEvent}
- font :: wxFont:wxFont()}).
--type wxFontPickerEventType() :: 'command_fontpicker_changed'.
--type wxFontPicker() :: #wxFontPicker{}. %% Callback event: {@link wxFontPickerEvent}
+-record(wxAuiManager,{type :: wxAuiManagerEventType(), %% Callback event: {@link wxAuiManagerEvent}
+ manager :: wxAuiManager:wxAuiManager(),
+ pane :: wxAuiPaneInfo:wxAuiPaneInfo(),
+ button :: integer(),
+ veto_flag :: boolean(),
+ canveto_flag :: boolean(),
+ dc :: wxDC:wxDC()}).
+-type wxAuiManagerEventType() :: 'aui_pane_button' | 'aui_pane_close' | 'aui_pane_maximize' | 'aui_pane_restore' | 'aui_pane_activated' | 'aui_render' | 'aui_find_manager'.
+-type wxAuiManager() :: #wxAuiManager{}. %% Callback event: {@link wxAuiManagerEvent}
--record(wxFocus,{type :: wxFocusEventType(), %% Callback event: {@link wxFocusEvent}
- win :: wxWindow:wxWindow()}).
--type wxFocusEventType() :: 'set_focus' | 'kill_focus'.
--type wxFocus() :: #wxFocus{}. %% Callback event: {@link wxFocusEvent}
+-record(wxDate,{type :: wxDateEventType(), %% Callback event: {@link wxDateEvent}
+ date :: wx:wx_datetime()}).
+-type wxDateEventType() :: 'date_changed'.
+-type wxDate() :: #wxDate{}. %% Callback event: {@link wxDateEvent}
--record(wxPaletteChanged, {type :: wxPaletteChangedEventType()}). %% Callback event: {@link wxPaletteChangedEvent}
--type wxPaletteChangedEventType() :: 'palette_changed'.
--type wxPaletteChanged() :: #wxPaletteChanged{}. %% Callback event: {@link wxPaletteChangedEvent}
+-record(wxIconize,{type :: wxIconizeEventType(), %% Callback event: {@link wxIconizeEvent}
+ iconized :: boolean()}).
+-type wxIconizeEventType() :: 'iconize'.
+-type wxIconize() :: #wxIconize{}. %% Callback event: {@link wxIconizeEvent}
-record(wxScroll,{type :: wxScrollEventType(), %% Callback event: {@link wxScrollEvent}
commandInt :: integer(),
@@ -128,38 +85,41 @@
-type wxScrollEventType() :: 'scroll_top' | 'scroll_bottom' | 'scroll_lineup' | 'scroll_linedown' | 'scroll_pageup' | 'scroll_pagedown' | 'scroll_thumbtrack' | 'scroll_thumbrelease' | 'scroll_changed'.
-type wxScroll() :: #wxScroll{}. %% Callback event: {@link wxScrollEvent}
--record(wxChildFocus, {type :: wxChildFocusEventType()}). %% Callback event: {@link wxChildFocusEvent}
--type wxChildFocusEventType() :: 'child_focus'.
--type wxChildFocus() :: #wxChildFocus{}. %% Callback event: {@link wxChildFocusEvent}
+-record(wxSplitter, {type :: wxSplitterEventType()}). %% Callback event: {@link wxSplitterEvent}
+-type wxSplitterEventType() :: 'command_splitter_sash_pos_changed' | 'command_splitter_sash_pos_changing' | 'command_splitter_doubleclicked' | 'command_splitter_unsplit'.
+-type wxSplitter() :: #wxSplitter{}. %% Callback event: {@link wxSplitterEvent}
--record(wxAuiNotebook,{type :: wxAuiNotebookEventType(), %% Callback event: {@link wxAuiNotebookEvent}
- old_selection :: integer(),
- selection :: integer(),
- drag_source :: wxAuiNotebook:wxAuiNotebook()}).
--type wxAuiNotebookEventType() :: 'command_auinotebook_page_close' | 'command_auinotebook_page_changed' | 'command_auinotebook_page_changing' | 'command_auinotebook_button' | 'command_auinotebook_begin_drag' | 'command_auinotebook_end_drag' | 'command_auinotebook_drag_motion' | 'command_auinotebook_allow_dnd' | 'command_auinotebook_tab_middle_down' | 'command_auinotebook_tab_middle_up' | 'command_auinotebook_tab_right_down' | 'command_auinotebook_tab_right_up' | 'command_auinotebook_page_closed' | 'command_auinotebook_drag_done' | 'command_auinotebook_bg_dclick'.
--type wxAuiNotebook() :: #wxAuiNotebook{}. %% Callback event: {@link wxAuiNotebookEvent}
+-record(wxPaletteChanged, {type :: wxPaletteChangedEventType()}). %% Callback event: {@link wxPaletteChangedEvent}
+-type wxPaletteChangedEventType() :: 'palette_changed'.
+-type wxPaletteChanged() :: #wxPaletteChanged{}. %% Callback event: {@link wxPaletteChangedEvent}
--record(wxSize,{type :: wxSizeEventType(), %% Callback event: {@link wxSizeEvent}
- size :: {W::integer(), H::integer()},
- rect :: {X::integer(), Y::integer(), W::integer(), H::integer()}}).
--type wxSizeEventType() :: 'size'.
--type wxSize() :: #wxSize{}. %% Callback event: {@link wxSizeEvent}
+-record(wxNotebook,{type :: wxNotebookEventType(), %% Callback event: {@link wxNotebookEvent}
+ nSel :: integer(),
+ nOldSel :: integer()}).
+-type wxNotebookEventType() :: 'command_notebook_page_changed' | 'command_notebook_page_changing'.
+-type wxNotebook() :: #wxNotebook{}. %% Callback event: {@link wxNotebookEvent}
--record(wxCommand,{type :: wxCommandEventType(), %% Callback event: {@link wxCommandEvent}
- cmdString :: unicode:chardata(),
- commandInt :: integer(),
- extraLong :: integer()}).
--type wxCommandEventType() :: 'command_button_clicked' | 'command_checkbox_clicked' | 'command_choice_selected' | 'command_listbox_selected' | 'command_listbox_doubleclicked' | 'command_text_updated' | 'command_text_enter' | 'command_menu_selected' | 'command_slider_updated' | 'command_radiobox_selected' | 'command_radiobutton_selected' | 'command_scrollbar_updated' | 'command_vlbox_selected' | 'command_combobox_selected' | 'command_tool_rclicked' | 'command_tool_enter' | 'command_checklistbox_toggled' | 'command_togglebutton_clicked' | 'command_left_click' | 'command_left_dclick' | 'command_right_click' | 'command_set_focus' | 'command_kill_focus' | 'command_enter'.
--type wxCommand() :: #wxCommand{}. %% Callback event: {@link wxCommandEvent}
+-record(wxContextMenu,{type :: wxContextMenuEventType(), %% Callback event: {@link wxContextMenuEvent}
+ pos :: {X::integer(), Y::integer()}}).
+-type wxContextMenuEventType() :: 'context_menu'.
+-type wxContextMenu() :: #wxContextMenu{}. %% Callback event: {@link wxContextMenuEvent}
--record(wxMaximize, {type :: wxMaximizeEventType()}). %% Callback event: {@link wxMaximizeEvent}
--type wxMaximizeEventType() :: 'maximize'.
--type wxMaximize() :: #wxMaximize{}. %% Callback event: {@link wxMaximizeEvent}
+-record(wxFontPicker,{type :: wxFontPickerEventType(), %% Callback event: {@link wxFontPickerEvent}
+ font :: wxFont:wxFont()}).
+-type wxFontPickerEventType() :: 'command_fontpicker_changed'.
+-type wxFontPicker() :: #wxFontPicker{}. %% Callback event: {@link wxFontPickerEvent}
--record(wxSpin,{type :: wxSpinEventType(), %% Callback event: {@link wxSpinEvent}
- commandInt :: integer()}).
--type wxSpinEventType() :: 'command_spinctrl_updated' | 'spin_up' | 'spin_down' | 'spin'.
--type wxSpin() :: #wxSpin{}. %% Callback event: {@link wxSpinEvent}
+-record(wxChildFocus, {type :: wxChildFocusEventType()}). %% Callback event: {@link wxChildFocusEvent}
+-type wxChildFocusEventType() :: 'child_focus'.
+-type wxChildFocus() :: #wxChildFocus{}. %% Callback event: {@link wxChildFocusEvent}
+
+-record(wxTaskBarIcon, {type :: wxTaskBarIconEventType()}). %% Callback event: {@link wxTaskBarIconEvent}
+-type wxTaskBarIconEventType() :: 'taskbar_move' | 'taskbar_left_down' | 'taskbar_left_up' | 'taskbar_right_down' | 'taskbar_right_up' | 'taskbar_left_dclick' | 'taskbar_right_dclick'.
+-type wxTaskBarIcon() :: #wxTaskBarIcon{}. %% Callback event: {@link wxTaskBarIconEvent}
+
+-record(wxWindowDestroy, {type :: wxWindowDestroyEventType()}). %% Callback event: {@link wxWindowDestroyEvent}
+-type wxWindowDestroyEventType() :: 'destroy'.
+-type wxWindowDestroy() :: #wxWindowDestroy{}. %% Callback event: {@link wxWindowDestroyEvent}
-record(wxMenu,{type :: wxMenuEventType(), %% Callback event: {@link wxMenuEvent}
menuId :: integer(),
@@ -167,30 +127,63 @@
-type wxMenuEventType() :: 'menu_open' | 'menu_close' | 'menu_highlight'.
-type wxMenu() :: #wxMenu{}. %% Callback event: {@link wxMenuEvent}
+-record(wxActivate,{type :: wxActivateEventType(), %% Callback event: {@link wxActivateEvent}
+ active :: boolean()}).
+-type wxActivateEventType() :: 'activate' | 'activate_app' | 'hibernate'.
+-type wxActivate() :: #wxActivate{}. %% Callback event: {@link wxActivateEvent}
+
+-record(wxGrid,{type :: wxGridEventType(), %% Callback event: {@link wxGridEvent}
+ row :: integer(),
+ col :: integer(),
+ x :: integer(),
+ y :: integer(),
+ selecting :: boolean(),
+ control :: boolean(),
+ meta :: boolean(),
+ shift :: boolean(),
+ alt :: boolean()}).
+-type wxGridEventType() :: 'grid_cell_left_click' | 'grid_cell_right_click' | 'grid_cell_left_dclick' | 'grid_cell_right_dclick' | 'grid_label_left_click' | 'grid_label_right_click' | 'grid_label_left_dclick' | 'grid_label_right_dclick' | 'grid_row_size' | 'grid_col_size' | 'grid_range_select' | 'grid_cell_change' | 'grid_select_cell' | 'grid_editor_shown' | 'grid_editor_hidden' | 'grid_editor_created' | 'grid_cell_begin_drag'.
+-type wxGrid() :: #wxGrid{}. %% Callback event: {@link wxGridEvent}
+
+-record(wxPaint, {type :: wxPaintEventType()}). %% Callback event: {@link wxPaintEvent}
+-type wxPaintEventType() :: 'paint'.
+-type wxPaint() :: #wxPaint{}. %% Callback event: {@link wxPaintEvent}
+
-record(wxShow,{type :: wxShowEventType(), %% Callback event: {@link wxShowEvent}
show :: boolean()}).
-type wxShowEventType() :: 'show'.
-type wxShow() :: #wxShow{}. %% Callback event: {@link wxShowEvent}
--record(wxWindowDestroy, {type :: wxWindowDestroyEventType()}). %% Callback event: {@link wxWindowDestroyEvent}
--type wxWindowDestroyEventType() :: 'destroy'.
--type wxWindowDestroy() :: #wxWindowDestroy{}. %% Callback event: {@link wxWindowDestroyEvent}
-
--record(wxContextMenu,{type :: wxContextMenuEventType(), %% Callback event: {@link wxContextMenuEvent}
- pos :: {X::integer(), Y::integer()}}).
--type wxContextMenuEventType() :: 'context_menu'.
--type wxContextMenu() :: #wxContextMenu{}. %% Callback event: {@link wxContextMenuEvent}
-
--record(wxActivate,{type :: wxActivateEventType(), %% Callback event: {@link wxActivateEvent}
- active :: boolean()}).
--type wxActivateEventType() :: 'activate' | 'activate_app' | 'hibernate'.
--type wxActivate() :: #wxActivate{}. %% Callback event: {@link wxActivateEvent}
+-record(wxStyledText,{type :: wxStyledTextEventType(), %% Callback event: {@link wxStyledTextEvent}
+ position :: integer(),
+ key :: integer(),
+ modifiers :: integer(),
+ modificationType :: integer(),
+ text :: unicode:chardata(),
+ length :: integer(),
+ linesAdded :: integer(),
+ line :: integer(),
+ foldLevelNow :: integer(),
+ foldLevelPrev :: integer(),
+ margin :: integer(),
+ message :: integer(),
+ wParam :: integer(),
+ lParam :: integer(),
+ listType :: integer(),
+ x :: integer(),
+ y :: integer(),
+ dragText :: unicode:chardata(),
+ dragAllowMove :: boolean(),
+ dragResult :: wx:wx_enum()}).
+-type wxStyledTextEventType() :: 'stc_change' | 'stc_styleneeded' | 'stc_charadded' | 'stc_savepointreached' | 'stc_savepointleft' | 'stc_romodifyattempt' | 'stc_key' | 'stc_doubleclick' | 'stc_updateui' | 'stc_modified' | 'stc_macrorecord' | 'stc_marginclick' | 'stc_needshown' | 'stc_painted' | 'stc_userlistselection' | 'stc_uridropped' | 'stc_dwellstart' | 'stc_dwellend' | 'stc_start_drag' | 'stc_drag_over' | 'stc_do_drop' | 'stc_zoom' | 'stc_hotspot_click' | 'stc_hotspot_dclick' | 'stc_calltip_click' | 'stc_autocomp_selection'.
+-type wxStyledText() :: #wxStyledText{}. %% Callback event: {@link wxStyledTextEvent}
--record(wxMove,{type :: wxMoveEventType(), %% Callback event: {@link wxMoveEvent}
- pos :: {X::integer(), Y::integer()},
- rect :: {X::integer(), Y::integer(), W::integer(), H::integer()}}).
--type wxMoveEventType() :: 'move'.
--type wxMove() :: #wxMove{}. %% Callback event: {@link wxMoveEvent}
+-record(wxAuiNotebook,{type :: wxAuiNotebookEventType(), %% Callback event: {@link wxAuiNotebookEvent}
+ old_selection :: integer(),
+ selection :: integer(),
+ drag_source :: wxAuiNotebook:wxAuiNotebook()}).
+-type wxAuiNotebookEventType() :: 'command_auinotebook_page_close' | 'command_auinotebook_page_changed' | 'command_auinotebook_page_changing' | 'command_auinotebook_button' | 'command_auinotebook_begin_drag' | 'command_auinotebook_end_drag' | 'command_auinotebook_drag_motion' | 'command_auinotebook_allow_dnd' | 'command_auinotebook_tab_middle_down' | 'command_auinotebook_tab_middle_up' | 'command_auinotebook_tab_right_down' | 'command_auinotebook_tab_right_up' | 'command_auinotebook_page_closed' | 'command_auinotebook_drag_done' | 'command_auinotebook_bg_dclick'.
+-type wxAuiNotebook() :: #wxAuiNotebook{}. %% Callback event: {@link wxAuiNotebookEvent}
-record(wxList,{type :: wxListEventType(), %% Callback event: {@link wxListEvent}
code :: integer(),
@@ -201,9 +194,9 @@
-type wxListEventType() :: 'command_list_begin_drag' | 'command_list_begin_rdrag' | 'command_list_begin_label_edit' | 'command_list_end_label_edit' | 'command_list_delete_item' | 'command_list_delete_all_items' | 'command_list_key_down' | 'command_list_insert_item' | 'command_list_col_click' | 'command_list_col_right_click' | 'command_list_col_begin_drag' | 'command_list_col_dragging' | 'command_list_col_end_drag' | 'command_list_item_selected' | 'command_list_item_deselected' | 'command_list_item_right_click' | 'command_list_item_middle_click' | 'command_list_item_activated' | 'command_list_item_focused' | 'command_list_cache_hint'.
-type wxList() :: #wxList{}. %% Callback event: {@link wxListEvent}
--record(wxClipboardText, {type :: wxClipboardTextEventType()}). %% Callback event: {@link wxClipboardTextEvent}
--type wxClipboardTextEventType() :: 'command_text_copy' | 'command_text_cut' | 'command_text_paste'.
--type wxClipboardText() :: #wxClipboardText{}. %% Callback event: {@link wxClipboardTextEvent}
+-record(wxUpdateUI, {type :: wxUpdateUIEventType()}). %% Callback event: {@link wxUpdateUIEvent}
+-type wxUpdateUIEventType() :: 'update_ui'.
+-type wxUpdateUI() :: #wxUpdateUI{}. %% Callback event: {@link wxUpdateUIEvent}
-record(wxScrollWin,{type :: wxScrollWinEventType(), %% Callback event: {@link wxScrollWinEvent}
commandInt :: integer(),
@@ -211,92 +204,81 @@
-type wxScrollWinEventType() :: 'scrollwin_top' | 'scrollwin_bottom' | 'scrollwin_lineup' | 'scrollwin_linedown' | 'scrollwin_pageup' | 'scrollwin_pagedown' | 'scrollwin_thumbtrack' | 'scrollwin_thumbrelease'.
-type wxScrollWin() :: #wxScrollWin{}. %% Callback event: {@link wxScrollWinEvent}
--record(wxIconize,{type :: wxIconizeEventType(), %% Callback event: {@link wxIconizeEvent}
- iconized :: boolean()}).
--type wxIconizeEventType() :: 'iconize'.
--type wxIconize() :: #wxIconize{}. %% Callback event: {@link wxIconizeEvent}
+-record(wxJoystick,{type :: wxJoystickEventType(), %% Callback event: {@link wxJoystickEvent}
+ pos :: {X::integer(), Y::integer()},
+ zPosition :: integer(),
+ buttonChange :: integer(),
+ buttonState :: integer(),
+ joyStick :: integer()}).
+-type wxJoystickEventType() :: 'joy_button_down' | 'joy_button_up' | 'joy_move' | 'joy_zmove'.
+-type wxJoystick() :: #wxJoystick{}. %% Callback event: {@link wxJoystickEvent}
--record(wxUpdateUI, {type :: wxUpdateUIEventType()}). %% Callback event: {@link wxUpdateUIEvent}
--type wxUpdateUIEventType() :: 'update_ui'.
--type wxUpdateUI() :: #wxUpdateUI{}. %% Callback event: {@link wxUpdateUIEvent}
+-record(wxWindowCreate, {type :: wxWindowCreateEventType()}). %% Callback event: {@link wxWindowCreateEvent}
+-type wxWindowCreateEventType() :: 'create'.
+-type wxWindowCreate() :: #wxWindowCreate{}. %% Callback event: {@link wxWindowCreateEvent}
--record(wxMouse,{type :: wxMouseEventType(), %% Callback event: {@link wxMouseEvent}
+-record(wxClose, {type :: wxCloseEventType()}). %% Callback event: {@link wxCloseEvent}
+-type wxCloseEventType() :: 'close_window' | 'end_session' | 'query_end_session'.
+-type wxClose() :: #wxClose{}. %% Callback event: {@link wxCloseEvent}
+
+-record(wxKey,{type :: wxKeyEventType(), %% Callback event: {@link wxKeyEvent}
x :: integer(),
y :: integer(),
- leftDown :: boolean(),
- middleDown :: boolean(),
- rightDown :: boolean(),
+ keyCode :: integer(),
controlDown :: boolean(),
shiftDown :: boolean(),
altDown :: boolean(),
metaDown :: boolean(),
- wheelRotation :: integer(),
- wheelDelta :: integer(),
- linesPerAction :: integer()}).
--type wxMouseEventType() :: 'left_down' | 'left_up' | 'middle_down' | 'middle_up' | 'right_down' | 'right_up' | 'motion' | 'enter_window' | 'leave_window' | 'left_dclick' | 'middle_dclick' | 'right_dclick' | 'mousewheel'.
--type wxMouse() :: #wxMouse{}. %% Callback event: {@link wxMouseEvent}
-
--record(wxTree,{type :: wxTreeEventType(), %% Callback event: {@link wxTreeEvent}
- item :: integer(),
- itemOld :: integer(),
- pointDrag :: {X::integer(), Y::integer()}}).
--type wxTreeEventType() :: 'command_tree_begin_drag' | 'command_tree_begin_rdrag' | 'command_tree_begin_label_edit' | 'command_tree_end_label_edit' | 'command_tree_delete_item' | 'command_tree_get_info' | 'command_tree_set_info' | 'command_tree_item_expanded' | 'command_tree_item_expanding' | 'command_tree_item_collapsed' | 'command_tree_item_collapsing' | 'command_tree_sel_changed' | 'command_tree_sel_changing' | 'command_tree_key_down' | 'command_tree_item_activated' | 'command_tree_item_right_click' | 'command_tree_item_middle_click' | 'command_tree_end_drag' | 'command_tree_state_image_click' | 'command_tree_item_gettooltip' | 'command_tree_item_menu'.
--type wxTree() :: #wxTree{}. %% Callback event: {@link wxTreeEvent}
-
--record(wxSysColourChanged, {type :: wxSysColourChangedEventType()}). %% Callback event: {@link wxSysColourChangedEvent}
--type wxSysColourChangedEventType() :: 'sys_colour_changed'.
--type wxSysColourChanged() :: #wxSysColourChanged{}. %% Callback event: {@link wxSysColourChangedEvent}
+ scanCode :: boolean(),
+ uniChar :: integer(),
+ rawCode :: integer(),
+ rawFlags :: integer()}).
+-type wxKeyEventType() :: 'char' | 'char_hook' | 'key_down' | 'key_up'.
+-type wxKey() :: #wxKey{}. %% Callback event: {@link wxKeyEvent}
--record(wxNavigationKey,{type :: wxNavigationKeyEventType(), %% Callback event: {@link wxNavigationKeyEvent}
- flags :: integer(),
- focus :: wxWindow:wxWindow()}).
--type wxNavigationKeyEventType() :: 'navigation_key'.
--type wxNavigationKey() :: #wxNavigationKey{}. %% Callback event: {@link wxNavigationKeyEvent}
+-record(wxIdle, {type :: wxIdleEventType()}). %% Callback event: {@link wxIdleEvent}
+-type wxIdleEventType() :: 'idle'.
+-type wxIdle() :: #wxIdle{}. %% Callback event: {@link wxIdleEvent}
-record(wxQueryNewPalette, {type :: wxQueryNewPaletteEventType()}). %% Callback event: {@link wxQueryNewPaletteEvent}
-type wxQueryNewPaletteEventType() :: 'query_new_palette'.
-type wxQueryNewPalette() :: #wxQueryNewPalette{}. %% Callback event: {@link wxQueryNewPaletteEvent}
--record(wxMouseCaptureChanged, {type :: wxMouseCaptureChangedEventType()}). %% Callback event: {@link wxMouseCaptureChangedEvent}
--type wxMouseCaptureChangedEventType() :: 'mouse_capture_changed'.
--type wxMouseCaptureChanged() :: #wxMouseCaptureChanged{}. %% Callback event: {@link wxMouseCaptureChangedEvent}
-
-record(wxHtmlLink,{type :: wxHtmlLinkEventType(), %% Callback event: {@link wxHtmlLinkEvent}
linkInfo :: wx:wx_wxHtmlLinkInfo()}).
-type wxHtmlLinkEventType() :: 'command_html_link_clicked'.
-type wxHtmlLink() :: #wxHtmlLink{}. %% Callback event: {@link wxHtmlLinkEvent}
--record(wxKey,{type :: wxKeyEventType(), %% Callback event: {@link wxKeyEvent}
- x :: integer(),
- y :: integer(),
- keyCode :: integer(),
- controlDown :: boolean(),
- shiftDown :: boolean(),
- altDown :: boolean(),
- metaDown :: boolean(),
- scanCode :: boolean(),
- uniChar :: integer(),
- rawCode :: integer(),
- rawFlags :: integer()}).
--type wxKeyEventType() :: 'char' | 'char_hook' | 'key_down' | 'key_up'.
--type wxKey() :: #wxKey{}. %% Callback event: {@link wxKeyEvent}
+-record(wxInitDialog, {type :: wxInitDialogEventType()}). %% Callback event: {@link wxInitDialogEvent}
+-type wxInitDialogEventType() :: 'init_dialog'.
+-type wxInitDialog() :: #wxInitDialog{}. %% Callback event: {@link wxInitDialogEvent}
--record(wxTaskBarIcon, {type :: wxTaskBarIconEventType()}). %% Callback event: {@link wxTaskBarIconEvent}
--type wxTaskBarIconEventType() :: 'taskbar_move' | 'taskbar_left_down' | 'taskbar_left_up' | 'taskbar_right_down' | 'taskbar_right_up' | 'taskbar_left_dclick' | 'taskbar_right_dclick'.
--type wxTaskBarIcon() :: #wxTaskBarIcon{}. %% Callback event: {@link wxTaskBarIconEvent}
+-record(wxMaximize, {type :: wxMaximizeEventType()}). %% Callback event: {@link wxMaximizeEvent}
+-type wxMaximizeEventType() :: 'maximize'.
+-type wxMaximize() :: #wxMaximize{}. %% Callback event: {@link wxMaximizeEvent}
--record(wxGrid,{type :: wxGridEventType(), %% Callback event: {@link wxGridEvent}
- row :: integer(),
- col :: integer(),
- x :: integer(),
- y :: integer(),
- selecting :: boolean(),
- control :: boolean(),
- meta :: boolean(),
- shift :: boolean(),
- alt :: boolean()}).
--type wxGridEventType() :: 'grid_cell_left_click' | 'grid_cell_right_click' | 'grid_cell_left_dclick' | 'grid_cell_right_dclick' | 'grid_label_left_click' | 'grid_label_right_click' | 'grid_label_left_dclick' | 'grid_label_right_dclick' | 'grid_row_size' | 'grid_col_size' | 'grid_range_select' | 'grid_cell_change' | 'grid_select_cell' | 'grid_editor_shown' | 'grid_editor_hidden' | 'grid_editor_created' | 'grid_cell_begin_drag'.
--type wxGrid() :: #wxGrid{}. %% Callback event: {@link wxGridEvent}
+-record(wxClipboardText, {type :: wxClipboardTextEventType()}). %% Callback event: {@link wxClipboardTextEvent}
+-type wxClipboardTextEventType() :: 'command_text_copy' | 'command_text_cut' | 'command_text_paste'.
+-type wxClipboardText() :: #wxClipboardText{}. %% Callback event: {@link wxClipboardTextEvent}
+
+-record(wxTree,{type :: wxTreeEventType(), %% Callback event: {@link wxTreeEvent}
+ item :: integer(),
+ itemOld :: integer(),
+ pointDrag :: {X::integer(), Y::integer()}}).
+-type wxTreeEventType() :: 'command_tree_begin_drag' | 'command_tree_begin_rdrag' | 'command_tree_begin_label_edit' | 'command_tree_end_label_edit' | 'command_tree_delete_item' | 'command_tree_get_info' | 'command_tree_set_info' | 'command_tree_item_expanded' | 'command_tree_item_expanding' | 'command_tree_item_collapsed' | 'command_tree_item_collapsing' | 'command_tree_sel_changed' | 'command_tree_sel_changing' | 'command_tree_key_down' | 'command_tree_item_activated' | 'command_tree_item_right_click' | 'command_tree_item_middle_click' | 'command_tree_end_drag' | 'command_tree_state_image_click' | 'command_tree_item_gettooltip' | 'command_tree_item_menu'.
+-type wxTree() :: #wxTree{}. %% Callback event: {@link wxTreeEvent}
+
+-record(wxErase,{type :: wxEraseEventType(), %% Callback event: {@link wxEraseEvent}
+ dc :: wxDC:wxDC()}).
+-type wxEraseEventType() :: 'erase_background'.
+-type wxErase() :: #wxErase{}. %% Callback event: {@link wxEraseEvent}
+
+-record(wxSash,{type :: wxSashEventType(), %% Callback event: {@link wxSashEvent}
+ edge :: wx:wx_enum(),
+ dragRect :: {X::integer(), Y::integer(), W::integer(), H::integer()},
+ dragStatus :: wx:wx_enum()}).
+-type wxSashEventType() :: 'sash_dragged'.
+-type wxSash() :: #wxSash{}. %% Callback event: {@link wxSashEvent}
-record(wxCalendar,{type :: wxCalendarEventType(), %% Callback event: {@link wxCalendarEvent}
wday :: wx:wx_enum(),
@@ -304,42 +286,52 @@
-type wxCalendarEventType() :: 'calendar_sel_changed' | 'calendar_day_changed' | 'calendar_month_changed' | 'calendar_year_changed' | 'calendar_doubleclicked' | 'calendar_weekday_clicked'.
-type wxCalendar() :: #wxCalendar{}. %% Callback event: {@link wxCalendarEvent}
--record(wxWindowCreate, {type :: wxWindowCreateEventType()}). %% Callback event: {@link wxWindowCreateEvent}
--type wxWindowCreateEventType() :: 'create'.
--type wxWindowCreate() :: #wxWindowCreate{}. %% Callback event: {@link wxWindowCreateEvent}
+-record(wxMouse,{type :: wxMouseEventType(), %% Callback event: {@link wxMouseEvent}
+ x :: integer(),
+ y :: integer(),
+ leftDown :: boolean(),
+ middleDown :: boolean(),
+ rightDown :: boolean(),
+ controlDown :: boolean(),
+ shiftDown :: boolean(),
+ altDown :: boolean(),
+ metaDown :: boolean(),
+ wheelRotation :: integer(),
+ wheelDelta :: integer(),
+ linesPerAction :: integer()}).
+-type wxMouseEventType() :: 'left_down' | 'left_up' | 'middle_down' | 'middle_up' | 'right_down' | 'right_up' | 'motion' | 'enter_window' | 'leave_window' | 'left_dclick' | 'middle_dclick' | 'right_dclick' | 'mousewheel'.
+-type wxMouse() :: #wxMouse{}. %% Callback event: {@link wxMouseEvent}
--record(wxDate,{type :: wxDateEventType(), %% Callback event: {@link wxDateEvent}
- date :: wx:wx_datetime()}).
--type wxDateEventType() :: 'date_changed'.
--type wxDate() :: #wxDate{}. %% Callback event: {@link wxDateEvent}
+-record(wxSize,{type :: wxSizeEventType(), %% Callback event: {@link wxSizeEvent}
+ size :: {W::integer(), H::integer()},
+ rect :: {X::integer(), Y::integer(), W::integer(), H::integer()}}).
+-type wxSizeEventType() :: 'size'.
+-type wxSize() :: #wxSize{}. %% Callback event: {@link wxSizeEvent}
--record(wxAuiManager,{type :: wxAuiManagerEventType(), %% Callback event: {@link wxAuiManagerEvent}
- manager :: wxAuiManager:wxAuiManager(),
- pane :: wxAuiPaneInfo:wxAuiPaneInfo(),
- button :: integer(),
- veto_flag :: boolean(),
- canveto_flag :: boolean(),
- dc :: wxDC:wxDC()}).
--type wxAuiManagerEventType() :: 'aui_pane_button' | 'aui_pane_close' | 'aui_pane_maximize' | 'aui_pane_restore' | 'aui_pane_activated' | 'aui_render' | 'aui_find_manager'.
--type wxAuiManager() :: #wxAuiManager{}. %% Callback event: {@link wxAuiManagerEvent}
+-record(wxSysColourChanged, {type :: wxSysColourChangedEventType()}). %% Callback event: {@link wxSysColourChangedEvent}
+-type wxSysColourChangedEventType() :: 'sys_colour_changed'.
+-type wxSysColourChanged() :: #wxSysColourChanged{}. %% Callback event: {@link wxSysColourChangedEvent}
--record(wxJoystick,{type :: wxJoystickEventType(), %% Callback event: {@link wxJoystickEvent}
+-record(wxDisplayChanged, {type :: wxDisplayChangedEventType()}). %% Callback event: {@link wxDisplayChangedEvent}
+-type wxDisplayChangedEventType() :: 'display_changed'.
+-type wxDisplayChanged() :: #wxDisplayChanged{}. %% Callback event: {@link wxDisplayChangedEvent}
+
+-record(wxMove,{type :: wxMoveEventType(), %% Callback event: {@link wxMoveEvent}
pos :: {X::integer(), Y::integer()},
- zPosition :: integer(),
- buttonChange :: integer(),
- buttonState :: integer(),
- joyStick :: integer()}).
--type wxJoystickEventType() :: 'joy_button_down' | 'joy_button_up' | 'joy_move' | 'joy_zmove'.
--type wxJoystick() :: #wxJoystick{}. %% Callback event: {@link wxJoystickEvent}
+ rect :: {X::integer(), Y::integer(), W::integer(), H::integer()}}).
+-type wxMoveEventType() :: 'move'.
+-type wxMove() :: #wxMove{}. %% Callback event: {@link wxMoveEvent}
--record(wxPaint, {type :: wxPaintEventType()}). %% Callback event: {@link wxPaintEvent}
--type wxPaintEventType() :: 'paint'.
--type wxPaint() :: #wxPaint{}. %% Callback event: {@link wxPaintEvent}
+-record(wxNavigationKey,{type :: wxNavigationKeyEventType(), %% Callback event: {@link wxNavigationKeyEvent}
+ flags :: integer(),
+ focus :: wxWindow:wxWindow()}).
+-type wxNavigationKeyEventType() :: 'navigation_key'.
+-type wxNavigationKey() :: #wxNavigationKey{}. %% Callback event: {@link wxNavigationKeyEvent}
--record(wxErase,{type :: wxEraseEventType(), %% Callback event: {@link wxEraseEvent}
- dc :: wxDC:wxDC()}).
--type wxEraseEventType() :: 'erase_background'.
--type wxErase() :: #wxErase{}. %% Callback event: {@link wxEraseEvent}
+-record(wxFileDirPicker,{type :: wxFileDirPickerEventType(), %% Callback event: {@link wxFileDirPickerEvent}
+ path :: unicode:chardata()}).
+-type wxFileDirPickerEventType() :: 'command_filepicker_changed' | 'command_dirpicker_changed'.
+-type wxFileDirPicker() :: #wxFileDirPicker{}. %% Callback event: {@link wxFileDirPickerEvent}
-record(wxSetCursor,{type :: wxSetCursorEventType(), %% Callback event: {@link wxSetCursorEvent}
x :: integer(),
@@ -348,6 +340,14 @@
-type wxSetCursorEventType() :: 'set_cursor'.
-type wxSetCursor() :: #wxSetCursor{}. %% Callback event: {@link wxSetCursorEvent}
+-record(wxMouseCaptureChanged, {type :: wxMouseCaptureChangedEventType()}). %% Callback event: {@link wxMouseCaptureChangedEvent}
+-type wxMouseCaptureChangedEventType() :: 'mouse_capture_changed'.
+-type wxMouseCaptureChanged() :: #wxMouseCaptureChanged{}. %% Callback event: {@link wxMouseCaptureChangedEvent}
+
+-record(wxMouseCaptureLost, {type :: wxMouseCaptureLostEventType()}). %% Callback event: {@link wxMouseCaptureLostEvent}
+-type wxMouseCaptureLostEventType() :: 'mouse_capture_lost'.
+-type wxMouseCaptureLost() :: #wxMouseCaptureLost{}. %% Callback event: {@link wxMouseCaptureLostEvent}
+
-type event() :: wxActivate() | wxAuiManager() | wxAuiNotebook() | wxCalendar() | wxChildFocus() | wxClipboardText() | wxClose() | wxColourPicker() | wxCommand() | wxContextMenu() | wxDate() | wxDisplayChanged() | wxErase() | wxFileDirPicker() | wxFocus() | wxFontPicker() | wxGrid() | wxHelp() | wxHtmlLink() | wxIconize() | wxIdle() | wxInitDialog() | wxJoystick() | wxKey() | wxList() | wxMaximize() | wxMenu() | wxMouse() | wxMouseCaptureChanged() | wxMouseCaptureLost() | wxMove() | wxNavigationKey() | wxNotebook() | wxPaint() | wxPaletteChanged() | wxQueryNewPalette() | wxSash() | wxScroll() | wxScrollWin() | wxSetCursor() | wxShow() | wxSize() | wxSpin() | wxSplitter() | wxStyledText() | wxSysColourChanged() | wxTaskBarIcon() | wxTree() | wxUpdateUI() | wxWindowCreate() | wxWindowDestroy().
-type wxEventType() :: wxActivateEventType() | wxAuiManagerEventType() | wxAuiNotebookEventType() | wxCalendarEventType() | wxChildFocusEventType() | wxClipboardTextEventType() | wxCloseEventType() | wxColourPickerEventType() | wxCommandEventType() | wxContextMenuEventType() | wxDateEventType() | wxDisplayChangedEventType() | wxEraseEventType() | wxFileDirPickerEventType() | wxFocusEventType() | wxFontPickerEventType() | wxGridEventType() | wxHelpEventType() | wxHtmlLinkEventType() | wxIconizeEventType() | wxIdleEventType() | wxInitDialogEventType() | wxJoystickEventType() | wxKeyEventType() | wxListEventType() | wxMaximizeEventType() | wxMenuEventType() | wxMouseCaptureChangedEventType() | wxMouseCaptureLostEventType() | wxMouseEventType() | wxMoveEventType() | wxNavigationKeyEventType() | wxNotebookEventType() | wxPaintEventType() | wxPaletteChangedEventType() | wxQueryNewPaletteEventType() | wxSashEventType() | wxScrollEventType() | wxScrollWinEventType() | wxSetCursorEventType() | wxShowEventType() | wxSizeEventType() | wxSpinEventType() | wxSplitterEventType() | wxStyledTextEventType() | wxSysColourChangedEventType() | wxTaskBarIconEventType() | wxTreeEventType() | wxUpdateUIEventType() | wxWindowCreateEventType() | wxWindowDestroyEventType().
diff --git a/lib/wx/src/gen/wxListCtrl.erl b/lib/wx/src/gen/wxListCtrl.erl
index d1a063d900..851686062a 100644
--- a/lib/wx/src/gen/wxListCtrl.erl
+++ b/lib/wx/src/gen/wxListCtrl.erl
@@ -94,31 +94,34 @@ parent_class(_Class) -> erlang:error({badtype, ?MODULE}).
-type wxListCtrl() :: wx:wx_object().
-%% @spec () -> wxListCtrl()
%% @doc See <a href="http://www.wxwidgets.org/manuals/stable/wx_wxlistctrl.html#wxlistctrlwxlistctrl">external documentation</a>.
+-spec new() -> wxListCtrl().
new() ->
wxe_util:construct(?wxListCtrl_new_0, <<>>).
-%% @spec (Parent::wxWindow:wxWindow()) -> wxListCtrl()
-%% @equiv new(Parent, [])
+-spec new(Parent) -> wxListCtrl() when
+ Parent::wxWindow:wxWindow().
new(Parent)
when is_record(Parent, wx_ref) ->
new(Parent, []).
-%% @spec (Parent::wxWindow:wxWindow(), [Option]) -> wxListCtrl()
-%% Option = {winid, integer()} |
-%% {pos, {X::integer(),Y::integer()}} |
-%% {size, {W::integer(),H::integer()}} |
-%% {style, integer()} |
-%% {validator, wx:wx()} |
-%% {onGetItemText, OnGetItemText} |
-%% {onGetItemAttr, OnGetItemAttr} |
-%% {onGetItemColumnImage, OnGetItemColumnImage}
+%% @doc Creates a listctrl with optional callback functions:
%%
-%% OnGetItemText = (This, Item, Column) -> wxString()
-%% OnGetItemAttr = (This, Item) -> wxListItemAttr()
+%% OnGetItemText = (This, Item, Column) -> unicode:charlist()
+%% OnGetItemAttr = (This, Item) -> wxListItemAttr:wxListItemAttr()
%% OnGetItemColumnImage = (This, Item, Column) -> integer()
-%% @doc See <a href="http://www.wxwidgets.org/manuals/stable/wx_wxlistctrl.html#wxlistctrlwxlistctrl">external documentation</a>.
+%%
+%% See <a href="http://www.wxwidgets.org/manuals/stable/wx_wxlistctrl.html#wxlistctrlwxlistctrl">external documentation</a>.
+-spec new(Parent, [Option]) -> wxListCtrl() when
+ Parent::wxWindow:wxWindow(),
+ Option::{winid, integer()} |
+ {pos, {X::integer(),Y::integer()}} |
+ {size, {W::integer(),H::integer()}} |
+ {style, integer()} |
+ {validator, wx:wx_object()} |
+ {onGetItemText, function()} |
+ {onGetItemAttr, function()} |
+ {onGetItemColumnImage, function()}.
new(#wx_ref{type=ParentT,ref=ParentRef}, Options)
when is_list(Options)->
@@ -185,26 +188,27 @@ clearAll(#wx_ref{type=ThisT,ref=ThisRef}) ->
<<ThisRef:32/?UI>>).
-%% @spec (This::wxListCtrl(), Parent::wxWindow:wxWindow()) -> bool()
%% @equiv create(This,Parent, [])
+-spec create(This, Parent) -> wxListCtrl() when
+ This::wxWindow:wxWindow(),
+ Parent::wxWindow:wxWindow().
create(This,Parent)
when is_record(This, wx_ref),is_record(Parent, wx_ref) ->
create(This,Parent, []).
-%% @spec (This::wxListCtrl(), Parent::wxWindow:wxWindow(), [Option]) -> bool()
-%% Option = {winid, integer()} |
-%% {pos, {X::integer(),Y::integer()}} |
-%% {size, {W::integer(),H::integer()}} |
-%% {style, integer()} |
-%% {validator, wx:wx()} |
-%% {onGetItemText, OnGetItemText} |
-%% {onGetItemAttr, OnGetItemAttr} |
-%% {onGetItemColumnImage, OnGetItemColumnImage}
-%%
-%% OnGetItemText = (This, Item, Column) -> wxString()
-%% OnGetItemAttr = (This, Item) -> wxListItemAttr()
-%% OnGetItemColumnImage = (This, Item, Column) -> integer()
%% @doc See <a href="http://www.wxwidgets.org/manuals/stable/wx_wxlistctrl.html#wxlistctrlcreate">external documentation</a>.
+-spec create(This, Parent, [Option]) -> wxListCtrl() when
+ This::wxWindow:wxWindow(),
+ Parent::wxWindow:wxWindow(),
+ Option::{winid, integer()} |
+ {pos, {X::integer(),Y::integer()}} |
+ {size, {W::integer(),H::integer()}} |
+ {style, integer()} |
+ {validator, wx:wx_object()} |
+ {onGetItemText, function()} |
+ {onGetItemAttr, function()} |
+ {onGetItemColumnImage, function()}.
+
create(#wx_ref{type=ThisT,ref=ThisRef},#wx_ref{type=ParentT,ref=ParentRef}, Options)
when is_list(Options) ->
?CLASS(ThisT,wxListCtrl),
diff --git a/lib/wx/src/gen/wxXmlResource.erl b/lib/wx/src/gen/wxXmlResource.erl
index ae02c74751..aa65b8b04e 100644
--- a/lib/wx/src/gen/wxXmlResource.erl
+++ b/lib/wx/src/gen/wxXmlResource.erl
@@ -334,8 +334,6 @@ unload(#wx_ref{type=ThisT,ref=ThisRef},Filename)
<<ThisRef:32/?UI,(byte_size(Filename_UC)):32/?UI,(Filename_UC)/binary, 0:(((8- ((0+byte_size(Filename_UC)) band 16#7)) band 16#7))/unit:8>>).
-%% @spec (Window::wxWindow:wxWindow(),Name::string(), Type::atom()) -> wx:wxObject()
-
%% @doc Looks up a control with Name in a window created with XML
%% resources. You can use it to set/get values from controls.
%% The object is type casted to <b>Type</b>.
@@ -345,6 +343,10 @@ unload(#wx_ref{type=ThisT,ref=ThisRef},Filename)
%% true = wxXmlResource:loadDialog(Xrc, Dlg, Frame, "controls_dialog"), <br />
%% LCtrl = xrcctrl(Dlg, "controls_listctrl", wxListCtrl), <br />
%% wxListCtrl:insertColumn(LCtrl, 0, "Name", [{width, 200}]), <br />
+-spec xrcctrl(Window, Name, Type) -> wx:wx_object() when
+ Window::wxWindow:wxWindow(),
+ Name::string(),
+ Type::atom().
xrcctrl(Window = #wx_ref{}, Name, Type) when is_list(Name), is_atom(Type) ->
%% Func Id ?wxXmlResource_xrcctrl
diff --git a/lib/wx/src/wx_object.erl b/lib/wx/src/wx_object.erl
index 40ee308358..40170b6eb1 100644
--- a/lib/wx/src/wx_object.erl
+++ b/lib/wx/src/wx_object.erl
@@ -55,7 +55,7 @@
%% When stop is returned in one of the functions above with Reason =
%% normal | shutdown | Term, terminate(State) is called. It lets the
%% user module clean up, it is always called when server terminates or
-%% when wxObject() in the driver is deleted. If the Parent process
+%% when wx_object() in the driver is deleted. If the Parent process
%% terminates the Module:terminate/2 function is called. <br/>
%% terminate(Reason, State)
%%
@@ -171,58 +171,59 @@
%% -----------------------------------------------------------------
-%% @spec (Mod, Args, Options) -> wxWindow:wxWindow()
-%% Mod = atom()
-%% Args = term()
-%% Options = [{timeout, Timeout} | {debug, [Flag]}]
-%% Flag = trace | log | {logfile, File} | statistics | debug
%% @doc Starts a generic wx_object server and invokes Mod:init(Args) in the
%% new process.
+-spec start(Mod, Args, Options) -> wxWindow:wxWindow() | {error, term()} when
+ Mod::atom(),
+ Args::term(),
+ Flag::trace | log | {logfile, string()} | statistics | debug,
+ Options::[{timeout, timeout()} | {debug, [Flag]}].
start(Mod, Args, Options) ->
gen_response(gen:start(?MODULE, nolink, Mod, Args, [get(?WXE_IDENTIFIER)|Options])).
-
-%% @spec (Name, Mod, Args, Options) -> wxWindow:wxWindow()
-%% Name = {local, atom()}
-%% Mod = atom()
-%% Args = term()
-%% Options = [{timeout, Timeout} | {debug, [Flag]}]
-%% Flag = trace | log | {logfile, File} | statistics | debug
+
%% @doc Starts a generic wx_object server and invokes Mod:init(Args) in the
%% new process.
+-spec start(Name, Mod, Args, Options) -> wxWindow:wxWindow() | {error, term()} when
+ Name::{local, atom()},
+ Mod::atom(),
+ Args::term(),
+ Flag::trace | log | {logfile, string()} | statistics | debug,
+ Options::[{timeout, timeout()} | {debug, [Flag]}].
start(Name, Mod, Args, Options) ->
gen_response(gen:start(?MODULE, nolink, Name, Mod, Args, [get(?WXE_IDENTIFIER)|Options])).
-%% @spec (Mod, Args, Options) -> wxWindow:wxWindow()
-%% Mod = atom()
-%% Args = term()
-%% Options = [{timeout, Timeout} | {debug, [Flag]}]
-%% Flag = trace | log | {logfile, File} | statistics | debug
%% @doc Starts a generic wx_object server and invokes Mod:init(Args) in the
%% new process.
+-spec start_link(Mod, Args, Options) -> wxWindow:wxWindow() | {error, term()} when
+ Mod::atom(),
+ Args::term(),
+ Flag::trace | log | {logfile, string()} | statistics | debug,
+ Options::[{timeout, timeout()} | {debug, [Flag]}].
start_link(Mod, Args, Options) ->
gen_response(gen:start(?MODULE, link, Mod, Args, [get(?WXE_IDENTIFIER)|Options])).
-%% @spec (Name, Mod, Args, Options) -> wxWindow:wxWindow()
-%% Name = {local, atom()}
-%% Mod = atom()
-%% Args = term()
-%% Options = [{timeout, Timeout} | {debug, [Flag]}]
-%% Flag = trace | log | {logfile, File} | statistics | debug
%% @doc Starts a generic wx_object server and invokes Mod:init(Args) in the
%% new process.
+-spec start_link(Name, Mod, Args, Options) -> wxWindow:wxWindow() | {error, term()} when
+ Name::{local, atom()},
+ Mod::atom(),
+ Args::term(),
+ Flag::trace | log | {logfile, string()} | statistics | debug,
+ Options::[{timeout, timeout()} | {debug, [Flag]}].
start_link(Name, Mod, Args, Options) ->
gen_response(gen:start(?MODULE, link, Name, Mod, Args, [get(?WXE_IDENTIFIER)|Options])).
-
+
gen_response({ok, Pid}) ->
receive {ack, Pid, Ref = #wx_ref{}} -> Ref end;
gen_response(Reply) ->
Reply.
-%% @spec (Ref::wxObject()|atom()|pid()) -> ok
%% @doc Stops a generic wx_object server with reason 'normal'.
%% Invokes terminate(Reason,State) in the server. The call waits until
%% the process is terminated. If the process does not exist, an
%% exception is raised.
+-spec stop(Obj) -> ok when
+ Obj::wx:wx_object()|atom()|pid().
stop(Ref = #wx_ref{state=Pid}) when is_pid(Pid) ->
try
gen:stop(Pid)
@@ -236,11 +237,14 @@ stop(Name) when is_atom(Name) orelse is_pid(Name) ->
erlang:error({ExitReason, {?MODULE, stop, [Name]}})
end.
-%% @spec (Ref::wxObject()|atom()|pid(), Reason::term(), Timeout::timeout()) -> ok
%% @doc Stops a generic wx_object server with the given Reason.
%% Invokes terminate(Reason,State) in the server. The call waits until
%% the process is terminated. If the call times out, or if the process
%% does not exist, an exception is raised.
+-spec stop(Obj, Reason, Timeout) -> ok when
+ Obj::wx:wx_object()|atom()|pid(),
+ Reason::term(),
+ Timeout::timeout().
stop(Ref = #wx_ref{state=Pid}, Reason, Timeout) when is_pid(Pid) ->
try
gen:stop(Pid, Reason, Timeout)
@@ -254,12 +258,14 @@ stop(Name, Reason, Timeout) when is_atom(Name) orelse is_pid(Name) ->
erlang:error({ExitReason, {?MODULE, stop, [Name, Reason, Timeout]}})
end.
-%% @spec (Ref::wxObject()|atom()|pid(), Request::term()) -> term()
%% @doc Make a call to a wx_object server.
%% The call waits until it gets a result.
%% Invokes handle_call(Request, From, State) in the server
+-spec call(Obj, Request) -> term() when
+ Obj::wx:wx_object()|atom()|pid(),
+ Request::term().
call(Ref = #wx_ref{state=Pid}, Request) when is_pid(Pid) ->
- try
+ try
{ok,Res} = gen:call(Pid, '$gen_call', Request, infinity),
Res
catch _:Reason ->
@@ -272,10 +278,13 @@ call(Name, Request) when is_atom(Name) orelse is_pid(Name) ->
catch _:Reason ->
erlang:error({Reason, {?MODULE, call, [Name, Request]}})
end.
-
-%% @spec (Ref::wxObject()|atom()|pid(), Request::term(), Timeout::integer()) -> term()
+
%% @doc Make a call to a wx_object server with a timeout.
%% Invokes handle_call(Request, From, State) in server
+-spec call(Obj, Request, Timeout) -> term() when
+ Obj::wx:wx_object()|atom()|pid(),
+ Request::term(),
+ Timeout::integer().
call(Ref = #wx_ref{state=Pid}, Request, Timeout) when is_pid(Pid) ->
try
{ok,Res} = gen:call(Pid, '$gen_call', Request, Timeout),
@@ -291,10 +300,11 @@ call(Name, Request, Timeout) when is_atom(Name) orelse is_pid(Name) ->
erlang:error({Reason, {?MODULE, call, [Name, Request, Timeout]}})
end.
-%% @spec (Ref::wxObject()|atom()|pid(), Request::term()) -> ok
%% @doc Make a cast to a wx_object server.
%% Invokes handle_cast(Request, State) in the server
-
+-spec cast(Obj, Request) -> ok when
+ Obj::wx:wx_object()|atom()|pid(),
+ Request::term().
cast(#wx_ref{state=Pid}, Request) when is_pid(Pid) ->
Pid ! {'$gen_cast',Request},
ok;
@@ -302,21 +312,23 @@ cast(Name, Request) when is_atom(Name) orelse is_pid(Name) ->
Name ! {'$gen_cast',Request},
ok.
-%% @spec (Ref::wxObject()) -> pid()
%% @doc Get the pid of the object handle.
+-spec get_pid(Obj) -> pid() when
+ Obj::wx:wx_object()|atom()|pid().
get_pid(#wx_ref{state=Pid}) when is_pid(Pid) ->
Pid.
-%% @spec (Ref::wxObject(), pid()) -> wxObject()
%% @doc Sets the controlling process of the object handle.
+-spec set_pid(Obj, pid()) -> wx:wx_object() when
+ Obj::wx:wx_object()|atom()|pid().
set_pid(#wx_ref{}=R, Pid) when is_pid(Pid) ->
R#wx_ref{state=Pid}.
%% -----------------------------------------------------------------
%% Send a reply to the client.
%% -----------------------------------------------------------------
-%% @spec (From::tuple(), Reply::term()) -> pid()
%% @doc Get the pid of the object handle.
+-spec reply({pid(), Tag::term()}, Reply::term()) -> pid().
reply({To, Tag}, Reply) ->
catch To ! {Tag, Reply}.
diff --git a/lib/wx/vsn.mk b/lib/wx/vsn.mk
index de4e5e1935..0ce63d9f71 100644
--- a/lib/wx/vsn.mk
+++ b/lib/wx/vsn.mk
@@ -1 +1 @@
-WX_VSN = 1.7
+WX_VSN = 1.7.1
diff --git a/lib/xmerl/doc/src/notes.xml b/lib/xmerl/doc/src/notes.xml
index 0abcb87998..12e64537ed 100644
--- a/lib/xmerl/doc/src/notes.xml
+++ b/lib/xmerl/doc/src/notes.xml
@@ -32,6 +32,21 @@
<p>This document describes the changes made to the Xmerl application.</p>
+<section><title>Xmerl 1.3.12</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p> Fix a number of broken links in the xmerl
+ documentation. </p>
+ <p>
+ Own Id: OTP-13880</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Xmerl 1.3.11</title>
<section><title>Improvements and New Features</title>
diff --git a/lib/xmerl/src/xmerl_eventp.erl b/lib/xmerl/src/xmerl_eventp.erl
index 2cb76abc6e..8d7ea25e24 100644
--- a/lib/xmerl/src/xmerl_eventp.erl
+++ b/lib/xmerl/src/xmerl_eventp.erl
@@ -25,6 +25,90 @@
%% Each contain more elaborate settings of xmerl_scan that makes usage of
%% the customization functions.
%%
+%% @type xmlElement() = #xmlElement{}.
+%%
+%% @type option_list(). <p>Options allow to customize the behaviour of the
+%% scanner.
+%% See also <a href="xmerl_examples.html">tutorial</a> on customization
+%% functions.
+%% </p>
+%% <p>
+%% Possible options are:
+%% </p>
+%% <dl>
+%% <dt><code>{acc_fun, Fun}</code></dt>
+%% <dd>Call back function to accumulate contents of entity.</dd>
+%% <dt><code>{continuation_fun, Fun} |
+%% {continuation_fun, Fun, ContinuationState}</code></dt>
+%% <dd>Call back function to decide what to do if the scanner runs into EOF
+%% before the document is complete.</dd>
+%% <dt><code>{event_fun, Fun} |
+%% {event_fun, Fun, EventState}</code></dt>
+%% <dd>Call back function to handle scanner events.</dd>
+%% <dt><code>{fetch_fun, Fun} |
+%% {fetch_fun, Fun, FetchState}</code></dt>
+%% <dd>Call back function to fetch an external resource.</dd>
+%% <dt><code>{hook_fun, Fun} |
+%% {hook_fun, Fun, HookState}</code></dt>
+%% <dd>Call back function to process the document entities once
+%% identified.</dd>
+%% <dt><code>{close_fun, Fun}</code></dt>
+%% <dd>Called when document has been completely parsed.</dd>
+%% <dt><code>{rules, ReadFun, WriteFun, RulesState} |
+%% {rules, Rules}</code></dt>
+%% <dd>Handles storing of scanner information when parsing.</dd>
+%% <dt><code>{user_state, UserState}</code></dt>
+%% <dd>Global state variable accessible from all customization functions</dd>
+%%
+%% <dt><code>{fetch_path, PathList}</code></dt>
+%% <dd>PathList is a list of
+%% directories to search when fetching files. If the file in question
+%% is not in the fetch_path, the URI will be used as a file
+%% name.</dd>
+%% <dt><code>{space, Flag}</code></dt>
+%% <dd>'preserve' (default) to preserve spaces, 'normalize' to
+%% accumulate consecutive whitespace and replace it with one space.</dd>
+%% <dt><code>{line, Line}</code></dt>
+%% <dd>To specify starting line for scanning in document which contains
+%% fragments of XML.</dd>
+%% <dt><code>{namespace_conformant, Flag}</code></dt>
+%% <dd>Controls whether to behave as a namespace conformant XML parser,
+%% 'false' (default) to not otherwise 'true'.</dd>
+%% <dt><code>{validation, Flag}</code></dt>
+%% <dd>Controls whether to process as a validating XML parser:
+%% 'off' (default) no validation, or validation 'dtd' by DTD or 'schema'
+%% by XML Schema. 'false' and 'true' options are obsolete
+%% (i.e. they may be removed in a future release), if used 'false'
+%% equals 'off' and 'true' equals 'dtd'.</dd>
+%% <dt><code>{schemaLocation, [{Namespace,Link}|...]}</code></dt>
+%% <dd>Tells explicitly which XML Schema documents to use to validate
+%% the XML document. Used together with the
+%% <code>{validation,schema}</code> option.</dd>
+%% <dt><code>{quiet, Flag}</code></dt>
+%% <dd>Set to 'true' if xmerl should behave quietly and not output any
+%% information to standard output (default 'false').</dd>
+%% <dt><code>{doctype_DTD, DTD}</code></dt>
+%% <dd>Allows to specify DTD name when it isn't available in the XML
+%% document. This option has effect only together with
+%% <code>{validation,'dtd'</code> option.</dd>
+%% <dt><code>{xmlbase, Dir}</code></dt>
+%% <dd>XML Base directory. If using string/1 default is current directory.
+%% If using file/1 default is directory of given file.</dd>
+%% <dt><code>{encoding, Enc}</code></dt>
+%% <dd>Set default character set used (default UTF-8).
+%% This character set is used only if not explicitly given by the XML
+%% declaration. </dd>
+%% <dt><code>{document, Flag}</code></dt>
+%% <dd>Set to 'true' if xmerl should return a complete XML document
+%% as an xmlDocument record (default 'false').</dd>
+%% <dt><code>{comments, Flag}</code></dt>
+%% <dd>Set to 'false' if xmerl should skip comments otherwise they will
+%% be returned as xmlComment records (default 'true').</dd>
+%% <dt><code>{default_attrs, Flag}</code></dt>
+%% <dd>Set to 'true' if xmerl should add to elements missing attributes
+%% with a defined default value (default 'false').</dd>
+%% </dl>
+%%
-module(xmerl_eventp).
-vsn('0.19').
-date('03-09-17').
diff --git a/lib/xmerl/src/xmerl_scan.erl b/lib/xmerl/src/xmerl_scan.erl
index 2147a46a13..5e0459ec21 100644
--- a/lib/xmerl/src/xmerl_scan.erl
+++ b/lib/xmerl/src/xmerl_scan.erl
@@ -111,13 +111,16 @@
%% <dd>Set to 'true' if xmerl should add to elements missing attributes
%% with a defined default value (default 'false').</dd>
%% </dl>
+%% @type xmlElement() = #xmlElement{}.
+%% The record definition is found in xmerl.hrl.
+%% @type xmlDocument() = #xmlDocument{}.
+%% The record definition is found in xmerl.hrl.
%% @type document() = xmlElement() | xmlDocument(). <p>
%% The document returned by <tt>xmerl_scan:string/[1,2]</tt> and
%% <tt>xmerl_scan:file/[1,2]</tt>. The type of the returned record depends on
%% the value of the document option passed to the function.
%% </p>
-
-module(xmerl_scan).
-vsn('0.20').
-date('03-09-16').
diff --git a/lib/xmerl/src/xmerl_xpath.erl b/lib/xmerl/src/xmerl_xpath.erl
index bbebda1030..6146feba49 100644
--- a/lib/xmerl/src/xmerl_xpath.erl
+++ b/lib/xmerl/src/xmerl_xpath.erl
@@ -43,13 +43,27 @@
%% </pre>
%%
%% @type nodeEntity() =
-%% xmlElement()
-%% | xmlAttribute()
-%% | xmlText()
-%% | xmlPI()
-%% | xmlComment()
-%% | xmlNsNode()
-%% | xmlDocument()
+%% #xmlElement{}
+%% | #xmlAttribute{}
+%% | #xmlText{}
+%% | #xmlPI{}
+%% | #xmlComment{}
+%% | #xmlNsNode{}
+%% | #xmlDocument{}
+%%
+%% @type docNodes() = #xmlElement{}
+%% | #xmlAttribute{}
+%% | #xmlText{}
+%% | #xmlPI{}
+%% | #xmlComment{}
+%% | #xmlNsNode{}
+%%
+%% @type docEntity() = #xmlDocument{} | [docNodes()]
+%%
+%% @type xPathString() = string()
+%%
+%% @type parentList() = [{atom(), integer()}]
+%%
%% @type option_list(). <p>Options allows to customize the behaviour of the
%% XPath scanner.
%% </p>
@@ -115,7 +129,7 @@ string(Str, Doc, Options) ->
%% Parents = parentList()
%% Doc = nodeEntity()
%% Options = option_list()
-%% Scalar = xmlObj
+%% Scalar = #xmlObj{}
%% @doc Extracts the nodes from the parsed XML tree according to XPath.
%% xmlObj is a record with fields type and value,
%% where type is boolean | number | string
diff --git a/lib/xmerl/src/xmerl_xs.erl b/lib/xmerl/src/xmerl_xs.erl
index 3e9f6622b8..1ce76cfa41 100644
--- a/lib/xmerl/src/xmerl_xs.erl
+++ b/lib/xmerl/src/xmerl_xs.erl
@@ -45,7 +45,6 @@
% XSLT package which is written i C++.
% See also the <a href="xmerl_xs_examples.html">Tutorial</a>.
% </p>
-
-module(xmerl_xs).
-export([xslapply/2, value_of/1, select/2, built_in_rules/2 ]).
@@ -71,15 +70,13 @@
%% xslapply(fun template/1, E),
%% "&lt;/h1>"];
%% </pre>
-
xslapply(Fun, EList) when is_list(EList) ->
- lists:map( Fun, EList);
+ lists:map(Fun, EList);
xslapply(Fun, E = #xmlElement{})->
lists:map( Fun, E#xmlElement.content).
-
%% @spec value_of(E) -> List
-%% E = unknown()
+%% E = term()
%%
%% @doc Concatenates all text nodes within the tree.
%%
diff --git a/lib/xmerl/src/xmerl_xsd.erl b/lib/xmerl/src/xmerl_xsd.erl
index 4b5efae8dd..a89b3159ec 100644
--- a/lib/xmerl/src/xmerl_xsd.erl
+++ b/lib/xmerl/src/xmerl_xsd.erl
@@ -49,6 +49,7 @@
%% <dd>It is possible by this option to provide a state with process
%% information from an earlier validation.</dd>
%% </dl>
+%% @type filename() = string()
%% @end
%%%-------------------------------------------------------------------
-module(xmerl_xsd).
@@ -138,7 +139,7 @@ state2file(S=#xsd_state{schema_name=SN}) ->
%% @spec state2file(State,FileName) -> ok | {error,Reason}
%% State = global_state()
-%% FileName = filename()
+%% FileName = string()
%% @doc Saves the schema state with all information of the processed
%% schema in a file. You can provide the file name for the saved
%% state. FileName is saved with the <code>.xss</code> extension
@@ -153,7 +154,7 @@ state2file(S,FileName) when is_record(S,xsd_state) ->
%% @spec file2state(FileName) -> {ok,State} | {error,Reason}
%% State = global_state()
-%% FileName = filename()
+%% FileName = string()
%% @doc Reads the schema state with all information of the processed
%% schema from a file created with <code>state2file/[1,2]</code>. The
%% format of this file is internal. The state can then be used
@@ -202,7 +203,7 @@ xmerl_xsd_vsn_check(S=#xsd_state{vsn=MD5_VSN}) ->
process_validate(Schema,Xml) ->
process_validate(Schema,Xml,[]).
%% @spec process_validate(Schema,Element,Options) -> Result
-%% Schema = filename()
+%% Schema = string()
%% Element = XmlElement
%% Options = option_list()
%% Result = {ValidXmlElement,State} | {error,Reason}
@@ -282,7 +283,7 @@ validate3(_,_,S) ->
process_schema(Schema) ->
process_schema(Schema,[]).
%% @spec process_schema(Schema,Options) -> Result
-%% Schema = filename()
+%% Schema = string()
%% Result = {ok,State} | {error,Reason}
%% State = global_state()
%% Reason = [ErrorReason] | ErrorReason
@@ -324,7 +325,7 @@ process_schema2({SE,_},State,_Schema) ->
process_schemas(Schemas) ->
process_schemas(Schemas,[]).
%% @spec process_schemas(Schemas,Options) -> Result
-%% Schemas = [{NameSpace,filename()}|Schemas] | []
+%% Schemas = [{NameSpace,string()}|Schemas] | []
%% Result = {ok,State} | {error,Reason}
%% Reason = [ErrorReason] | ErrorReason
%% Options = option_list()
@@ -5426,7 +5427,7 @@ add_key_once(Key,N,El,L) ->
%% {filename:join([[io_lib:format("/~w(~w)",[X,Y])||{X,Y}<-Parents],Type]),Pos}.
%% @spec format_error(Errors) -> Result
-%% Errors = error_tuple() | [error_tuple()]
+%% Errors = tuple() | [tuple()]
%% Result = string() | [string()]
%% @doc Formats error descriptions to human readable strings.
format_error(L) when is_list(L) ->
diff --git a/lib/xmerl/vsn.mk b/lib/xmerl/vsn.mk
index a78a035a1f..95adaa5bb0 100644
--- a/lib/xmerl/vsn.mk
+++ b/lib/xmerl/vsn.mk
@@ -1 +1 @@
-XMERL_VSN = 1.3.11
+XMERL_VSN = 1.3.12
diff --git a/make/emd2exml.in b/make/emd2exml.in
index 903d707716..b4e052fef5 100644..100755
--- a/make/emd2exml.in
+++ b/make/emd2exml.in
@@ -1,4 +1,4 @@
-#!/usr/bin/env escript
+#!@ENV@ escript
%% -*- erlang -*-
%%! -smp disable
diff --git a/make/make_emakefile b/make/make_emakefile.in
index 56440d9bf0..fbca77887a 100755
--- a/make/make_emakefile
+++ b/make/make_emakefile.in
@@ -1,4 +1,4 @@
-#!/usr/bin/env perl
+#!@PERL@
# -*- cperl -*-
use strict;
diff --git a/make/otp.mk.in b/make/otp.mk.in
index 3b2e1a5bba..7e2945b561 100644
--- a/make/otp.mk.in
+++ b/make/otp.mk.in
@@ -260,6 +260,7 @@ DEFAULT_GIF_FILES = $(HTMLDIR)/min_head.gif
XSLTPROC = @XSLTPROC@
FOP = @FOP@
XMLLINT = @XMLLINT@
+CP = @CP@
DOCGEN=$(ERL_TOP)/lib/erl_docgen
FOP_CONFIG = $(DOCGEN)/priv/fop.xconf
diff --git a/otp_versions.table b/otp_versions.table
index 102d57deff..1598b8bf95 100644
--- a/otp_versions.table
+++ b/otp_versions.table
@@ -1,5 +1,16 @@
+OTP-19.1.1 : ssl-8.0.3 # asn1-4.0.4 common_test-1.12.3 compiler-7.0.2 cosEvent-2.2.1 cosEventDomain-1.2.1 cosFileTransfer-1.2.1 cosNotification-1.2.2 cosProperty-1.2.1 cosTime-1.2.2 cosTransactions-1.3.2 crypto-3.7.1 debugger-4.2.1 dialyzer-3.0.2 diameter-1.12.1 edoc-0.8 eldap-1.2.2 erl_docgen-0.6 erl_interface-3.9.1 erts-8.1 et-1.6 eunit-2.3.1 gs-1.6.2 hipe-3.15.2 ic-4.4.2 inets-6.3.3 jinterface-1.7.1 kernel-5.1 megaco-3.18.1 mnesia-4.14.1 observer-2.2.2 odbc-2.11.3 orber-3.8.2 os_mon-2.4.1 otp_mibs-1.1.1 parsetools-2.1.3 percept-0.9 public_key-1.2 reltool-0.7.2 runtime_tools-1.10.1 sasl-3.0.1 snmp-5.2.4 ssh-4.3.2 stdlib-3.1 syntax_tools-2.1 tools-2.8.6 typer-0.9.11 wx-1.7.1 xmerl-1.3.12 :
+OTP-19.1 : asn1-4.0.4 common_test-1.12.3 compiler-7.0.2 crypto-3.7.1 debugger-4.2.1 dialyzer-3.0.2 diameter-1.12.1 edoc-0.8 erl_docgen-0.6 erl_interface-3.9.1 erts-8.1 eunit-2.3.1 gs-1.6.2 hipe-3.15.2 ic-4.4.2 inets-6.3.3 jinterface-1.7.1 kernel-5.1 mnesia-4.14.1 observer-2.2.2 odbc-2.11.3 parsetools-2.1.3 reltool-0.7.2 runtime_tools-1.10.1 sasl-3.0.1 snmp-5.2.4 ssh-4.3.2 ssl-8.0.2 stdlib-3.1 syntax_tools-2.1 tools-2.8.6 wx-1.7.1 xmerl-1.3.12 # cosEvent-2.2.1 cosEventDomain-1.2.1 cosFileTransfer-1.2.1 cosNotification-1.2.2 cosProperty-1.2.1 cosTime-1.2.2 cosTransactions-1.3.2 eldap-1.2.2 et-1.6 megaco-3.18.1 orber-3.8.2 os_mon-2.4.1 otp_mibs-1.1.1 percept-0.9 public_key-1.2 typer-0.9.11 :
+OTP-19.0.7 : erts-8.0.5 # asn1-4.0.3 common_test-1.12.2 compiler-7.0.1 cosEvent-2.2.1 cosEventDomain-1.2.1 cosFileTransfer-1.2.1 cosNotification-1.2.2 cosProperty-1.2.1 cosTime-1.2.2 cosTransactions-1.3.2 crypto-3.7 debugger-4.2 dialyzer-3.0.1 diameter-1.12 edoc-0.7.19 eldap-1.2.2 erl_docgen-0.5 erl_interface-3.9 et-1.6 eunit-2.3 gs-1.6.1 hipe-3.15.1 ic-4.4.1 inets-6.3.2 jinterface-1.7 kernel-5.0.2 megaco-3.18.1 mnesia-4.14 observer-2.2.1 odbc-2.11.2 orber-3.8.2 os_mon-2.4.1 otp_mibs-1.1.1 parsetools-2.1.2 percept-0.9 public_key-1.2 reltool-0.7.1 runtime_tools-1.10 sasl-3.0 snmp-5.2.3 ssh-4.3.1 ssl-8.0.1 stdlib-3.0.1 syntax_tools-2.0 tools-2.8.5 typer-0.9.11 wx-1.7 xmerl-1.3.11 :
+OTP-19.0.6 : erts-8.0.4 # asn1-4.0.3 common_test-1.12.2 compiler-7.0.1 cosEvent-2.2.1 cosEventDomain-1.2.1 cosFileTransfer-1.2.1 cosNotification-1.2.2 cosProperty-1.2.1 cosTime-1.2.2 cosTransactions-1.3.2 crypto-3.7 debugger-4.2 dialyzer-3.0.1 diameter-1.12 edoc-0.7.19 eldap-1.2.2 erl_docgen-0.5 erl_interface-3.9 et-1.6 eunit-2.3 gs-1.6.1 hipe-3.15.1 ic-4.4.1 inets-6.3.2 jinterface-1.7 kernel-5.0.2 megaco-3.18.1 mnesia-4.14 observer-2.2.1 odbc-2.11.2 orber-3.8.2 os_mon-2.4.1 otp_mibs-1.1.1 parsetools-2.1.2 percept-0.9 public_key-1.2 reltool-0.7.1 runtime_tools-1.10 sasl-3.0 snmp-5.2.3 ssh-4.3.1 ssl-8.0.1 stdlib-3.0.1 syntax_tools-2.0 tools-2.8.5 typer-0.9.11 wx-1.7 xmerl-1.3.11 :
+OTP-19.0.5 : kernel-5.0.2 # asn1-4.0.3 common_test-1.12.2 compiler-7.0.1 cosEvent-2.2.1 cosEventDomain-1.2.1 cosFileTransfer-1.2.1 cosNotification-1.2.2 cosProperty-1.2.1 cosTime-1.2.2 cosTransactions-1.3.2 crypto-3.7 debugger-4.2 dialyzer-3.0.1 diameter-1.12 edoc-0.7.19 eldap-1.2.2 erl_docgen-0.5 erl_interface-3.9 erts-8.0.3 et-1.6 eunit-2.3 gs-1.6.1 hipe-3.15.1 ic-4.4.1 inets-6.3.2 jinterface-1.7 megaco-3.18.1 mnesia-4.14 observer-2.2.1 odbc-2.11.2 orber-3.8.2 os_mon-2.4.1 otp_mibs-1.1.1 parsetools-2.1.2 percept-0.9 public_key-1.2 reltool-0.7.1 runtime_tools-1.10 sasl-3.0 snmp-5.2.3 ssh-4.3.1 ssl-8.0.1 stdlib-3.0.1 syntax_tools-2.0 tools-2.8.5 typer-0.9.11 wx-1.7 xmerl-1.3.11 :
+OTP-19.0.4 : erts-8.0.3 # asn1-4.0.3 common_test-1.12.2 compiler-7.0.1 cosEvent-2.2.1 cosEventDomain-1.2.1 cosFileTransfer-1.2.1 cosNotification-1.2.2 cosProperty-1.2.1 cosTime-1.2.2 cosTransactions-1.3.2 crypto-3.7 debugger-4.2 dialyzer-3.0.1 diameter-1.12 edoc-0.7.19 eldap-1.2.2 erl_docgen-0.5 erl_interface-3.9 et-1.6 eunit-2.3 gs-1.6.1 hipe-3.15.1 ic-4.4.1 inets-6.3.2 jinterface-1.7 kernel-5.0.1 megaco-3.18.1 mnesia-4.14 observer-2.2.1 odbc-2.11.2 orber-3.8.2 os_mon-2.4.1 otp_mibs-1.1.1 parsetools-2.1.2 percept-0.9 public_key-1.2 reltool-0.7.1 runtime_tools-1.10 sasl-3.0 snmp-5.2.3 ssh-4.3.1 ssl-8.0.1 stdlib-3.0.1 syntax_tools-2.0 tools-2.8.5 typer-0.9.11 wx-1.7 xmerl-1.3.11 :
+OTP-19.0.3 : inets-6.3.2 kernel-5.0.1 ssl-8.0.1 # asn1-4.0.3 common_test-1.12.2 compiler-7.0.1 cosEvent-2.2.1 cosEventDomain-1.2.1 cosFileTransfer-1.2.1 cosNotification-1.2.2 cosProperty-1.2.1 cosTime-1.2.2 cosTransactions-1.3.2 crypto-3.7 debugger-4.2 dialyzer-3.0.1 diameter-1.12 edoc-0.7.19 eldap-1.2.2 erl_docgen-0.5 erl_interface-3.9 erts-8.0.2 et-1.6 eunit-2.3 gs-1.6.1 hipe-3.15.1 ic-4.4.1 jinterface-1.7 megaco-3.18.1 mnesia-4.14 observer-2.2.1 odbc-2.11.2 orber-3.8.2 os_mon-2.4.1 otp_mibs-1.1.1 parsetools-2.1.2 percept-0.9 public_key-1.2 reltool-0.7.1 runtime_tools-1.10 sasl-3.0 snmp-5.2.3 ssh-4.3.1 stdlib-3.0.1 syntax_tools-2.0 tools-2.8.5 typer-0.9.11 wx-1.7 xmerl-1.3.11 :
+OTP-19.0.2 : compiler-7.0.1 erts-8.0.2 stdlib-3.0.1 # asn1-4.0.3 common_test-1.12.2 cosEvent-2.2.1 cosEventDomain-1.2.1 cosFileTransfer-1.2.1 cosNotification-1.2.2 cosProperty-1.2.1 cosTime-1.2.2 cosTransactions-1.3.2 crypto-3.7 debugger-4.2 dialyzer-3.0.1 diameter-1.12 edoc-0.7.19 eldap-1.2.2 erl_docgen-0.5 erl_interface-3.9 et-1.6 eunit-2.3 gs-1.6.1 hipe-3.15.1 ic-4.4.1 inets-6.3.1 jinterface-1.7 kernel-5.0 megaco-3.18.1 mnesia-4.14 observer-2.2.1 odbc-2.11.2 orber-3.8.2 os_mon-2.4.1 otp_mibs-1.1.1 parsetools-2.1.2 percept-0.9 public_key-1.2 reltool-0.7.1 runtime_tools-1.10 sasl-3.0 snmp-5.2.3 ssh-4.3.1 ssl-8.0 syntax_tools-2.0 tools-2.8.5 typer-0.9.11 wx-1.7 xmerl-1.3.11 :
OTP-19.0.1 : dialyzer-3.0.1 erts-8.0.1 inets-6.3.1 observer-2.2.1 ssh-4.3.1 tools-2.8.5 # asn1-4.0.3 common_test-1.12.2 compiler-7.0 cosEvent-2.2.1 cosEventDomain-1.2.1 cosFileTransfer-1.2.1 cosNotification-1.2.2 cosProperty-1.2.1 cosTime-1.2.2 cosTransactions-1.3.2 crypto-3.7 debugger-4.2 diameter-1.12 edoc-0.7.19 eldap-1.2.2 erl_docgen-0.5 erl_interface-3.9 et-1.6 eunit-2.3 gs-1.6.1 hipe-3.15.1 ic-4.4.1 jinterface-1.7 kernel-5.0 megaco-3.18.1 mnesia-4.14 odbc-2.11.2 orber-3.8.2 os_mon-2.4.1 otp_mibs-1.1.1 parsetools-2.1.2 percept-0.9 public_key-1.2 reltool-0.7.1 runtime_tools-1.10 sasl-3.0 snmp-5.2.3 ssl-8.0 stdlib-3.0 syntax_tools-2.0 typer-0.9.11 wx-1.7 xmerl-1.3.11 :
OTP-19.0 : asn1-4.0.3 common_test-1.12.2 compiler-7.0 cosEvent-2.2.1 cosEventDomain-1.2.1 cosFileTransfer-1.2.1 cosNotification-1.2.2 cosProperty-1.2.1 cosTime-1.2.2 cosTransactions-1.3.2 crypto-3.7 debugger-4.2 dialyzer-3.0 diameter-1.12 edoc-0.7.19 eldap-1.2.2 erl_docgen-0.5 erl_interface-3.9 erts-8.0 et-1.6 eunit-2.3 gs-1.6.1 hipe-3.15.1 ic-4.4.1 inets-6.3 jinterface-1.7 kernel-5.0 megaco-3.18.1 mnesia-4.14 observer-2.2 odbc-2.11.2 orber-3.8.2 os_mon-2.4.1 otp_mibs-1.1.1 parsetools-2.1.2 percept-0.9 public_key-1.2 reltool-0.7.1 runtime_tools-1.10 sasl-3.0 snmp-5.2.3 ssh-4.3 ssl-8.0 stdlib-3.0 syntax_tools-2.0 tools-2.8.4 typer-0.9.11 wx-1.7 xmerl-1.3.11 # :
+OTP-18.3.4.4 : erts-7.3.1.2 # asn1-4.0.2 common_test-1.12.1.1 compiler-6.0.3 cosEvent-2.2 cosEventDomain-1.2 cosFileTransfer-1.2 cosNotification-1.2.1 cosProperty-1.2 cosTime-1.2.1 cosTransactions-1.3.1 crypto-3.6.3 debugger-4.1.2 dialyzer-2.9 diameter-1.11.2 edoc-0.7.18 eldap-1.2.1 erl_docgen-0.4.2 erl_interface-3.8.2 et-1.5.1 eunit-2.2.13 gs-1.6 hipe-3.15 ic-4.4 inets-6.2.4 jinterface-1.6.1 kernel-4.2 megaco-3.18 mnesia-4.13.4 observer-2.1.2 odbc-2.11.1 orber-3.8.1 os_mon-2.4 ose-1.1 otp_mibs-1.1 parsetools-2.1.1 percept-0.8.11 public_key-1.1.1 reltool-0.7 runtime_tools-1.9.3 sasl-2.7 snmp-5.2.2 ssh-4.2.2.2 ssl-7.3.3.1 stdlib-2.8 syntax_tools-1.7 test_server-3.10 tools-2.8.3 typer-0.9.10 webtool-0.9.1 wx-1.6.1 xmerl-1.3.10 :
+OTP-18.3.4.3 : ssh-4.2.2.2 # asn1-4.0.2 common_test-1.12.1.1 compiler-6.0.3 cosEvent-2.2 cosEventDomain-1.2 cosFileTransfer-1.2 cosNotification-1.2.1 cosProperty-1.2 cosTime-1.2.1 cosTransactions-1.3.1 crypto-3.6.3 debugger-4.1.2 dialyzer-2.9 diameter-1.11.2 edoc-0.7.18 eldap-1.2.1 erl_docgen-0.4.2 erl_interface-3.8.2 erts-7.3.1.1 et-1.5.1 eunit-2.2.13 gs-1.6 hipe-3.15 ic-4.4 inets-6.2.4 jinterface-1.6.1 kernel-4.2 megaco-3.18 mnesia-4.13.4 observer-2.1.2 odbc-2.11.1 orber-3.8.1 os_mon-2.4 ose-1.1 otp_mibs-1.1 parsetools-2.1.1 percept-0.8.11 public_key-1.1.1 reltool-0.7 runtime_tools-1.9.3 sasl-2.7 snmp-5.2.2 ssl-7.3.3.1 stdlib-2.8 syntax_tools-1.7 test_server-3.10 tools-2.8.3 typer-0.9.10 webtool-0.9.1 wx-1.6.1 xmerl-1.3.10 :
+OTP-18.3.4.2 : common_test-1.12.1.1 erts-7.3.1.1 ssl-7.3.3.1 # asn1-4.0.2 compiler-6.0.3 cosEvent-2.2 cosEventDomain-1.2 cosFileTransfer-1.2 cosNotification-1.2.1 cosProperty-1.2 cosTime-1.2.1 cosTransactions-1.3.1 crypto-3.6.3 debugger-4.1.2 dialyzer-2.9 diameter-1.11.2 edoc-0.7.18 eldap-1.2.1 erl_docgen-0.4.2 erl_interface-3.8.2 et-1.5.1 eunit-2.2.13 gs-1.6 hipe-3.15 ic-4.4 inets-6.2.4 jinterface-1.6.1 kernel-4.2 megaco-3.18 mnesia-4.13.4 observer-2.1.2 odbc-2.11.1 orber-3.8.1 os_mon-2.4 ose-1.1 otp_mibs-1.1 parsetools-2.1.1 percept-0.8.11 public_key-1.1.1 reltool-0.7 runtime_tools-1.9.3 sasl-2.7 snmp-5.2.2 ssh-4.2.2.1 stdlib-2.8 syntax_tools-1.7 test_server-3.10 tools-2.8.3 typer-0.9.10 webtool-0.9.1 wx-1.6.1 xmerl-1.3.10 :
OTP-18.3.4.1 : ssh-4.2.2.1 # asn1-4.0.2 common_test-1.12.1 compiler-6.0.3 cosEvent-2.2 cosEventDomain-1.2 cosFileTransfer-1.2 cosNotification-1.2.1 cosProperty-1.2 cosTime-1.2.1 cosTransactions-1.3.1 crypto-3.6.3 debugger-4.1.2 dialyzer-2.9 diameter-1.11.2 edoc-0.7.18 eldap-1.2.1 erl_docgen-0.4.2 erl_interface-3.8.2 erts-7.3.1 et-1.5.1 eunit-2.2.13 gs-1.6 hipe-3.15 ic-4.4 inets-6.2.4 jinterface-1.6.1 kernel-4.2 megaco-3.18 mnesia-4.13.4 observer-2.1.2 odbc-2.11.1 orber-3.8.1 os_mon-2.4 ose-1.1 otp_mibs-1.1 parsetools-2.1.1 percept-0.8.11 public_key-1.1.1 reltool-0.7 runtime_tools-1.9.3 sasl-2.7 snmp-5.2.2 ssl-7.3.3 stdlib-2.8 syntax_tools-1.7 test_server-3.10 tools-2.8.3 typer-0.9.10 webtool-0.9.1 wx-1.6.1 xmerl-1.3.10 :
OTP-18.3.4 : inets-6.2.4 ssl-7.3.3 # asn1-4.0.2 common_test-1.12.1 compiler-6.0.3 cosEvent-2.2 cosEventDomain-1.2 cosFileTransfer-1.2 cosNotification-1.2.1 cosProperty-1.2 cosTime-1.2.1 cosTransactions-1.3.1 crypto-3.6.3 debugger-4.1.2 dialyzer-2.9 diameter-1.11.2 edoc-0.7.18 eldap-1.2.1 erl_docgen-0.4.2 erl_interface-3.8.2 erts-7.3.1 et-1.5.1 eunit-2.2.13 gs-1.6 hipe-3.15 ic-4.4 jinterface-1.6.1 kernel-4.2 megaco-3.18 mnesia-4.13.4 observer-2.1.2 odbc-2.11.1 orber-3.8.1 os_mon-2.4 ose-1.1 otp_mibs-1.1 parsetools-2.1.1 percept-0.8.11 public_key-1.1.1 reltool-0.7 runtime_tools-1.9.3 sasl-2.7 snmp-5.2.2 ssh-4.2.2 stdlib-2.8 syntax_tools-1.7 test_server-3.10 tools-2.8.3 typer-0.9.10 webtool-0.9.1 wx-1.6.1 xmerl-1.3.10 :
OTP-18.3.3 : common_test-1.12.1 inets-6.2.3 ssl-7.3.2 # asn1-4.0.2 compiler-6.0.3 cosEvent-2.2 cosEventDomain-1.2 cosFileTransfer-1.2 cosNotification-1.2.1 cosProperty-1.2 cosTime-1.2.1 cosTransactions-1.3.1 crypto-3.6.3 debugger-4.1.2 dialyzer-2.9 diameter-1.11.2 edoc-0.7.18 eldap-1.2.1 erl_docgen-0.4.2 erl_interface-3.8.2 erts-7.3.1 et-1.5.1 eunit-2.2.13 gs-1.6 hipe-3.15 ic-4.4 jinterface-1.6.1 kernel-4.2 megaco-3.18 mnesia-4.13.4 observer-2.1.2 odbc-2.11.1 orber-3.8.1 os_mon-2.4 ose-1.1 otp_mibs-1.1 parsetools-2.1.1 percept-0.8.11 public_key-1.1.1 reltool-0.7 runtime_tools-1.9.3 sasl-2.7 snmp-5.2.2 ssh-4.2.2 stdlib-2.8 syntax_tools-1.7 test_server-3.10 tools-2.8.3 typer-0.9.10 webtool-0.9.1 wx-1.6.1 xmerl-1.3.10 :
diff --git a/system/README b/system/README
deleted file mode 100644
index 97ec9177c4..0000000000
--- a/system/README
+++ /dev/null
@@ -1,78 +0,0 @@
-Erlang/OTP December 6, 2013
-
-
-Release of Erlang 5.10.4/OTP R16B03
-
-
-1. GENERAL
- -------
-
-1.1 Installation Guide
-
- The installation guide can be found in
-
- <inst-root>doc/installation_guide/users_guide.html
-
-1.2 Java
-
- The Java sources were compiled using Java version 1.5.
-
-1.3 Disk space
-
- An installation of Erlang/OTP needs approximately 300 MB of
- disk space.
-
-1.4 The package contains HTML documentation. You can also get this
- documentation preformatted for printing in PDF format from
-
- http://www.erlang.se/doc/
-
- This site also contains HTML documentation for older releases
- as a reference.
-
-1.5 The Erlang system can run old BEAM files compiled with R11B-2
- or later (and almost all BEAM files compiled with R11B-0 or
- R11B-1). BEAM files from R10B or earlier are not supported.
-
- To get the best performance, you should recompile your
- application code with the R15B compiler.
-
-
-2. NOTES ABOUT THE SOLARIS VERSION
- -------------------------------
-
-2.1 For the Sparc Solaris environment, Solaris10 (2.10) and above is
- supported. The emulator runs on older Solaris 8 (2.8) versions
- and above. Older Solaris versions are neither tested nor supported.
- Also an Ultrasparc (sun4u architecture) is required.
-
-
-3. NOTES ABOUT THE VXWORKS VERSION
- -------------------------------
-
-3.1 The platform VxWorks is discontinued in the sense that only the
- libraries (erl_interface and ic's libraries) are supported. The VxWorks
- release is still packaged as a full release, but no support will be
- available for anything but the communication libraries.
-
-4 NOTES ABOUT THE LINUX VERSIONS
- -----------------------------
-
-4.1 The following linux distributions/version combinations are supported
- and tested:
-
- SuSE 10.1 x86, SuSE 10.1 x86_64, SuSE 11.0 x86, SuSE 11.0 x86_64
-
-5. APPLICATIONS NOTES
- ------------------
-
-
-6. MORE INFORMATION
- ----------------
-
- Commercial customers please visit http://www.erlang.se to find
- releases and more information. Commercial support is given through
-
- Open source users please visit http://www.erlang.org and direct
- problems to the open source community through the mailing list.
diff --git a/system/doc/design_principles/statem.xml b/system/doc/design_principles/statem.xml
index aea623851a..57e47431b8 100644
--- a/system/doc/design_principles/statem.xml
+++ b/system/doc/design_principles/statem.xml
@@ -33,7 +33,7 @@
<p>
This section is to be read with the
<seealso marker="stdlib:gen_statem"><c>gen_statem(3)</c></seealso>
- manual page in <c>STDLIB</c>, where all interface functions and callback
+ manual page in STDLIB, where all interface functions and callback
functions are described in detail.
</p>
<note>
@@ -52,7 +52,7 @@
<section>
<title>Event-Driven State Machines</title>
<p>
- Established Automata theory does not deal much with
+ Established Automata Theory does not deal much with
how a state transition is triggered,
but assumes that the output is a function
of the input (and the state) and that they are
@@ -226,11 +226,10 @@ handle_event(EventType, EventContent, State, Data) ->
-module(code_lock).
-behaviour(gen_statem).
-define(NAME, code_lock).
--define(CALLBACK_MODE, state_functions).
-export([start_link/1]).
-export([button/1]).
--export([init/1,terminate/3,code_change/4]).
+-export([init/1,callback_mode/0,terminate/3,code_change/4]).
-export([locked/3,open/3]).
start_link(Code) ->
@@ -242,7 +241,10 @@ button(Digit) ->
init(Code) ->
do_lock(),
Data = #{code => Code, remaining => Code},
- {?CALLBACK_MODE,locked,Data}.
+ {ok,locked,Data}.
+
+callback_mode() ->
+ state_functions.
locked(
cast, {button,Digit},
@@ -273,7 +275,7 @@ terminate(_Reason, State, _Data) ->
State =/= locked andalso do_lock(),
ok.
code_change(_Vsn, State, Data, _Extra) ->
- {?CALLBACK_MODE,State,Data}.
+ {ok,State,Data}.
]]></code>
<p>The code is explained in the next sections.</p>
</section>
@@ -308,7 +310,7 @@ start_link(Code) ->
as <c>{global,Name}</c>, then the <c>gen_statem</c> is
registered using
<seealso marker="kernel:global#register_name/2"><c>global:register_name/2</c></seealso>
- in <c>Kernel</c>.
+ in Kernel.
</p>
</item>
<item>
@@ -343,14 +345,8 @@ start_link(Code) ->
<p>
If name registration succeeds, the new <c>gen_statem</c> process
calls callback function <c>code_lock:init(Code)</c>.
- This function is expected to return <c>{CallbackMode,State,Data}</c>,
- where
- <seealso marker="#callback_modes"><c>CallbackMode</c></seealso>
- selects callback module state function mode, in this case
- <seealso marker="stdlib:gen_statem#type-callback_mode"><c>state_functions</c></seealso>
- through macro <c>?CALLBACK_MODE</c>. That is, each state
- has got its own handler function.
- <c>State</c> is the initial state of the <c>gen_statem</c>,
+ This function is expected to return <c>{ok,State,Data}</c>,
+ where <c>State</c> is the initial state of the <c>gen_statem</c>,
in this case <c>locked</c>; assuming that the door is locked to begin
with. <c>Data</c> is the internal server data of the <c>gen_statem</c>.
Here the server data is a <seealso marker="stdlib:maps">map</seealso>
@@ -359,11 +355,12 @@ start_link(Code) ->
that stores the remaining correct button sequence
(the same as the <c>code</c> to begin with).
</p>
+
<code type="erl"><![CDATA[
init(Code) ->
do_lock(),
Data = #{code => Code, remaining => Code},
- {?CALLBACK_MODE,locked,Data}.
+ {ok,locked,Data}.
]]></code>
<p>Function
<seealso marker="stdlib:gen_statem#start_link/3"><c>gen_statem:start_link</c></seealso>
@@ -380,6 +377,21 @@ init(Code) ->
can be used to start a standalone <c>gen_statem</c>, that is,
a <c>gen_statem</c> that is not part of a supervision tree.
</p>
+
+ <code type="erl"><![CDATA[
+callback_mode() ->
+ state_functions.
+ ]]></code>
+ <p>
+ Function
+ <seealso marker="stdlib:gen_statem#Module:callback_mode/0"><c>Module:callback_mode/0</c></seealso>
+ selects the
+ <seealso marker="#callback_modes"><c>CallbackMode</c></seealso>
+ for the callback module, in this case
+ <seealso marker="stdlib:gen_statem#type-callback_mode"><c>state_functions</c></seealso>.
+ That is, each state has got its own handler function.
+ </p>
+
</section>
<!-- =================================================================== -->
@@ -533,12 +545,11 @@ handle_event({call,From}, code_length, #{code := Code} = Data) ->
</p>
<code type="erl"><![CDATA[
...
--define(CALLBACK_MODE, handle_event_function).
-
-...
-export([handle_event/4]).
...
+callback_mode() ->
+ handle_event_function.
handle_event(cast, {button,Digit}, State, #{code := Code} = Data) ->
case State of
@@ -597,7 +608,7 @@ init(Args) ->
callback function <c>terminate(shutdown, State, Data)</c>.
</p>
<p>
- In the following example, function <c>terminate/3</c>
+ In this example, function <c>terminate/3</c>
locks the door if it is open, so we do not accidentally leave the door
open when the supervision tree terminates:
</p>
@@ -962,16 +973,13 @@ do_unlock() ->
</p>
<code type="erl"><![CDATA[
...
--define(CALLBACK_MODE, state_functions).
-
-...
-
init(Code) ->
process_flag(trap_exit, true),
Data = #{code => Code},
- enter(?CALLBACK_MODE, locked, Data).
+ enter(ok, locked, Data).
-...
+callback_mode() ->
+ state_functions.
locked(internal, enter, _Data) ->
do_lock(),
@@ -1027,11 +1035,10 @@ enter(Tag, State, Data) ->
-module(code_lock).
-behaviour(gen_statem).
-define(NAME, code_lock_2).
--define(CALLBACK_MODE, state_functions).
-export([start_link/1,stop/0]).
-export([button/1,code_length/0]).
--export([init/1,terminate/3,code_change/4]).
+-export([init/1,callback_mode/0,terminate/3,code_change/4]).
-export([locked/3,open/3]).
start_link(Code) ->
@@ -1047,7 +1054,10 @@ code_length() ->
init(Code) ->
process_flag(trap_exit, true),
Data = #{code => Code},
- enter(?CALLBACK_MODE, locked, Data).
+ enter(ok, locked, Data).
+
+callback_mode() ->
+ state_functions.
locked(internal, enter, #{code := Code} = Data) ->
do_lock(),
@@ -1091,7 +1101,7 @@ terminate(_Reason, State, _Data) ->
State =/= locked andalso do_lock(),
ok.
code_change(_Vsn, State, Data, _Extra) ->
- {?CALLBACK_MODE,State,Data}.
+ {ok,State,Data}.
]]></code>
</section>
@@ -1106,12 +1116,11 @@ code_change(_Vsn, State, Data, _Extra) ->
</p>
<code type="erl"><![CDATA[
...
--define(CALLBACK_MODE, handle_event_function).
-
-...
-export([handle_event/4]).
...
+callback_mode() ->
+ handle_event_function.
%% State: locked
handle_event(internal, enter, locked, #{code := Code} = Data) ->
@@ -1208,7 +1217,8 @@ format_status(Opt, [_PDict,State,Data]) ->
<seealso marker="stdlib:gen_statem#Module:format_status/2"><c>Module:format_status/2</c></seealso>
function. If you do not, a default implementation is used that
does the same as this example function without filtering
- the <c>Data</c> term, that is, <c>StateData = {State,Data}</c>.
+ the <c>Data</c> term, that is, <c>StateData = {State,Data}</c>,
+ in this example containing sensitive information.
</p>
</section>
@@ -1273,11 +1283,10 @@ format_status(Opt, [_PDict,State,Data]) ->
-module(code_lock).
-behaviour(gen_statem).
-define(NAME, code_lock_3).
--define(CALLBACK_MODE, handle_event_function).
-export([start_link/2,stop/0]).
-export([button/1,code_length/0,set_lock_button/1]).
--export([init/1,terminate/3,code_change/4,format_status/2]).
+-export([init/1,callback_mode/0,terminate/3,code_change/4,format_status/2]).
-export([handle_event/4]).
start_link(Code, LockButton) ->
@@ -1296,7 +1305,10 @@ set_lock_button(LockButton) ->
init({Code,LockButton}) ->
process_flag(trap_exit, true),
Data = #{code => Code, remaining => undefined, timer => undefined},
- enter(?CALLBACK_MODE, {locked,LockButton}, Data, []).
+ enter(ok, {locked,LockButton}, Data, []).
+
+callback_mode() ->
+ handle_event_function.
handle_event(
{call,From}, {set_lock_button,NewLockButton},
@@ -1366,7 +1378,7 @@ terminate(_Reason, State, _Data) ->
State =/= locked andalso do_lock(),
ok.
code_change(_Vsn, State, Data, _Extra) ->
- {?CALLBACK_MODE,State,Data}.
+ {ok,State,Data}.
format_status(Opt, [_PDict,State,Data]) ->
StateData =
{State,
diff --git a/system/doc/efficiency_guide/advanced.xml b/system/doc/efficiency_guide/advanced.xml
index 016302fe50..eee2648f34 100644
--- a/system/doc/efficiency_guide/advanced.xml
+++ b/system/doc/efficiency_guide/advanced.xml
@@ -156,7 +156,7 @@
<seealso marker="erts:erl#max_processes"><c>+P</c></seealso>
command-line flag in the
<seealso marker="erts:erl"><c>erl(1)</c></seealso>
- manual page in <c>erts</c>.</cell>
+ manual page in ERTS.</cell>
</row>
<row>
<cell>Known nodes</cell>
@@ -236,7 +236,7 @@
<seealso marker="erts:erl#max_ports"><c>+Q</c></seealso>
command-line flag in the
<seealso marker="erts:erl"><c>erl(1)</c></seealso> manual page
- in <c>erts</c>.</cell>
+ in ERTS.</cell>
</row>
<row>
<cell><marker id="files_sockets"></marker>Open files and
diff --git a/system/doc/efficiency_guide/profiling.xml b/system/doc/efficiency_guide/profiling.xml
index 1f3d503170..bf50a03fa6 100644
--- a/system/doc/efficiency_guide/profiling.xml
+++ b/system/doc/efficiency_guide/profiling.xml
@@ -128,7 +128,7 @@
performance impact. Using <c>fprof</c> is just a matter of
calling a few library functions, see the
<seealso marker="tools:fprof">fprof</seealso> manual page in
- <c>tools</c> .<c>fprof</c> was introduced in R8.</p>
+ Tools .<c>fprof</c> was introduced in R8.</p>
</section>
<section>
@@ -138,7 +138,7 @@
and in which function calls this time has been spent. Time is
shown as percentage of total time and absolute time. For more
information, see the <seealso marker="tools:eprof">eprof</seealso>
- manual page in <c>tools</c>.</p>
+ manual page in Tools.</p>
</section>
<section>
@@ -152,7 +152,7 @@
optimization. Using <c>cover</c> is just a matter of calling a
few library functions, see the
<seealso marker="tools:cover">cover</seealso> manual page in
- <c>tools</c>.</p>
+ Tools.</p>
</section>
<section>
@@ -165,7 +165,7 @@
any modules to profile (compared with <c>cover</c>).
For more information, see the
<seealso marker="tools:cprof">cprof</seealso> manual page in
- <c>tools</c>.</p>
+ Tools.</p>
</section>
<section>
diff --git a/system/doc/embedded/embedded_nt.xml b/system/doc/embedded/embedded_nt.xml
index a1a4b90f3c..8e05100585 100644
--- a/system/doc/embedded/embedded_nt.xml
+++ b/system/doc/embedded/embedded_nt.xml
@@ -62,7 +62,7 @@
<p>For Windows NT running on standard PCs with ISA and/or PCI bus,
an extension card with a hardware watchdog can be installed.</p>
<p>For more information, see the <c>heart(3)</c> manual page in
- <c>kernel</c>.</p>
+ Kernel.</p>
</section>
</section>
@@ -72,7 +72,7 @@
to install the Erlang process as a Windows system service.
This service can start after Windows NT has booted.</p>
<p>For more information, see the <c>erlsrv</c> manual page
- in <c>erts</c>.</p>
+ in ERTS.</p>
</section>
</chapter>
diff --git a/system/doc/embedded/embedded_solaris.xml b/system/doc/embedded/embedded_solaris.xml
index f8febcc546..eaa334fb39 100644
--- a/system/doc/embedded/embedded_solaris.xml
+++ b/system/doc/embedded/embedded_solaris.xml
@@ -190,7 +190,7 @@ esac</pre>
the onboard hardware watchdog can be activated,
provided a VME bus driver is added to the operating system
(see also Installation Problems).</p>
- <p>See also the <c>heart(3)</c> manual page in <c>kernel</c>.</p>
+ <p>See also the <c>heart(3)</c> manual page in Kernel.</p>
</section>
<section>
@@ -206,7 +206,7 @@ esac</pre>
<pre>
chown 0 /usr/sbin/reboot
chmod 4755 /usr/sbin/reboot</pre>
- <p>See also the <c>heart(3)</c> manual page in <c>kernel</c>.</p>
+ <p>See also the <c>heart(3)</c> manual page in Kernel.</p>
</section>
<section>
@@ -413,8 +413,8 @@ chown root mod_syslog]]></code>
<section>
<title>Related Documents</title>
<p>See the <c>os_mon(3)</c> application,
- the <c>application(3)</c> manual page in <c>kernel</c>,
- and the <c>erl(1)</c> manual page in <c>erts</c>.</p>
+ the <c>application(3)</c> manual page in Kernel,
+ and the <c>erl(1)</c> manual page in ERTS.</p>
</section>
</section>
@@ -474,7 +474,7 @@ chown root mod_syslog]]></code>
default, it must be called <c>start</c> and reside in
<c><![CDATA[<ERL_INSTALL_DIR>/bin]]></c>. Another start
program can be used, by using configuration parameter
- <c>start_prg</c> in application <c>sasl</c>.</p>
+ <c>start_prg</c> in application SASL.</p>
<p>The start program must call <c>run_erl</c> as shown below.
It must also take an optional parameter, which defaults to
<c><![CDATA[<ERL_INSTALL_DIR>/releases/start_erl.data]]></c>.</p>
@@ -484,7 +484,7 @@ chown root mod_syslog]]></code>
<p>The <c><![CDATA[<RELDIR>]]></c> directory is where new release
packets are installed, and where the release handler keeps
information about releases. For more information, see the
- <c>release_handler(3)</c> manual page in <c>sasl</c>.</p>
+ <c>release_handler(3)</c> manual page in SASL.</p>
<p>The following script illustrates the default behaviour of the
program:</p>
<code type="none"><![CDATA[
@@ -624,7 +624,7 @@ export RELDIR
exec $BINDIR/erlexec -boot $RELDIR/$VSN/start -config $RELDIR/$VSN/sys $*</code>
<p>If a diskless and/or read-only client node with the
- <c>sasl</c> configuration parameter <c>static_emulator</c> set
+ SASL configuration parameter <c>static_emulator</c> set
to <c>true</c> is about to start, the <c>-boot</c> and
<c>-config</c> flags must be changed.</p>
<p>As such a client cannot
diff --git a/system/doc/embedded/starting.xml b/system/doc/embedded/starting.xml
index 720383e8ac..11bf9b412a 100644
--- a/system/doc/embedded/starting.xml
+++ b/system/doc/embedded/starting.xml
@@ -69,7 +69,7 @@
default, it must be called <c>start</c> and reside in
<c><![CDATA[<ERL_INSTALL_DIR>/bin]]></c>. Another start program can be
used, by using the configuration parameter <c>start_prg</c> in
- the application <c>sasl</c>.</p>
+ application SASL.</p>
<p>The start program must call <c>run_erl</c> as shown below.
It must also take an optional parameter which defaults to
<c><![CDATA[<ERL_INSTALL_DIR>/bin/start_erl.data]]></c>.
@@ -80,8 +80,8 @@
</p>
<p>The <c><![CDATA[<RELDIR>]]></c> directory is where new release packets
are installed, and where the release handler keeps information
- about releases. See <c>release_handler(3)</c> in the
- application <c>sasl</c> for further information.
+ about releases. See <c>release_handler(3)</c> in
+ application SASL for further information.
</p>
<p>The following script illustrates the default behaviour of the
program.
@@ -228,7 +228,7 @@ export PROGNAME
export RELDIR
exec $BINDIR/erlexec -boot $RELDIR/$VSN/start -config $RELDIR/$VSN/sys $* </code>
- <p>If a diskless and/or read-only client node with the <c>sasl</c>
+ <p>If a diskless and/or read-only client node with the SASL
configuration parameter <c>static_emulator</c> set to <c>true</c>
is about to start the <c>-boot</c> and <c>-config</c> flags must be
changed. As such a client can not read a new <c>start_erl.data</c>
diff --git a/system/doc/oam/oam_intro.xml b/system/doc/oam/oam_intro.xml
index cdcb6e3111..8b8d69e638 100644
--- a/system/doc/oam/oam_intro.xml
+++ b/system/doc/oam/oam_intro.xml
@@ -178,7 +178,7 @@
<section>
<title>MIB Structure</title>
<p>The top-level OTP MIB is called <c>OTP-REG</c> and it is
- included in the <c>sasl</c> application. All other OTP MIBs
+ included in the SASL application. All other OTP MIBs
import some objects from this MIB.</p>
<p>Each MIB is contained in one application. The MIB text
@@ -188,7 +188,7 @@
<c><![CDATA[include/<MIB>.hrl]]></c>, and the compiled MIBs
are stored under <c><![CDATA[priv/mibs/<MIB>.bin]]></c>.
For example, the <c>OTP-MIB</c> is included in the
- <c>sasl</c> application:</p>
+ SASL application:</p>
<code type="none">
sasl-1.3/mibs/OTP-MIB.mib
@@ -211,11 +211,11 @@ snmp:c("MY-MIB", [{il, ["sasl/priv/mibs"]}]).</code>
<p>The following MIBs are defined in the OTP system:</p>
<list type="bulleted">
- <item><p><c>OTP-REG)</c> (in <c>sasl</c>) contains the top-level
+ <item><p><c>OTP-REG)</c> (in SASL) contains the top-level
OTP registration objects, used by all other MIBs.</p></item>
- <item><p><c>OTP-TC</c> (in <c>sasl</c>) contains the general
+ <item><p><c>OTP-TC</c> (in SASL) contains the general
Textual Conventions, which can be used by any other MIB.</p></item>
- <item><p><c>OTP-MIB</c> (in <c>sasl</c>) contains objects for
+ <item><p><c>OTP-MIB</c> (in SASL) contains objects for
instrumentation of the Erlang nodes, the Erlang machines,
and the applications in the system.</p></item>
<item><p><c>OTP-OS-MON-MIB</c> (in <c>oc_mon</c>) contains
diff --git a/system/doc/programming_examples/records.xml b/system/doc/programming_examples/records.xml
index da346dd0b3..074aa636b4 100644
--- a/system/doc/programming_examples/records.xml
+++ b/system/doc/programming_examples/records.xml
@@ -93,7 +93,7 @@ person</pre>
at compile time, not at runtime. For details on records
in the shell, see the
<seealso marker="stdlib:shell">shell(3)</seealso>
- manual page in <c>stdlib</c>.</p>
+ manual page in STDLIB.</p>
</section>
<section>
diff --git a/system/doc/reference_manual/typespec.xml b/system/doc/reference_manual/typespec.xml
index f17e5df277..1899efd5f3 100644
--- a/system/doc/reference_manual/typespec.xml
+++ b/system/doc/reference_manual/typespec.xml
@@ -47,7 +47,7 @@
<list type="bulleted">
<item>To document function interfaces</item>
<item>To provide more information for bug detection tools,
- such as <c>Dialyzer</c></item>
+ such as Dialyzer</item>
<item>To be exploited by documentation tools, such as EDoc, for
generating program documentation of various forms</item>
</list>
diff --git a/system/doc/top/Makefile b/system/doc/top/Makefile
index 7f9cec540b..116ec688fa 100644
--- a/system/doc/top/Makefile
+++ b/system/doc/top/Makefile
@@ -35,7 +35,7 @@ RELSYSDIR = "$(RELEASE_PATH)/doc"
GIF_FILES =
-INFO_FILES = ../../README ../../COPYRIGHT PR.template
+INFO_FILES = ../../../README.md ../../COPYRIGHT PR.template
TOPDOCDIR=.
diff --git a/system/doc/tutorial/c_port.xmlsrc b/system/doc/tutorial/c_port.xmlsrc
index 695f16515d..3c3bc48044 100644
--- a/system/doc/tutorial/c_port.xmlsrc
+++ b/system/doc/tutorial/c_port.xmlsrc
@@ -98,11 +98,11 @@ loop(Port) ->
{call, Caller, Msg} ->
Port ! {self(), {command, encode(Msg)}},
receive
- {Port, {data, Data}} ->
+ {Port, {data, Data}} ->
Caller ! {complex, decode(Data)}
end,
loop(Port)
- end.</pre>
+ end.</pre>
<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