aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--HOWTO/INSTALL.md4
-rw-r--r--bootstrap/bin/start.bootbin5391 -> 5395 bytes
-rw-r--r--bootstrap/bin/start_clean.bootbin5391 -> 5395 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/beam_asm.beambin11424 -> 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_validator.beambin30824 -> 30828 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/cerl.beambin32080 -> 32080 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/compile.beambin39112 -> 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/sys_pre_expand.beambin13472 -> 13084 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/v3_core.beambin54484 -> 55180 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/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.app2
-rw-r--r--bootstrap/lib/kernel/ebin/kernel.appup2
-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/os.beambin3776 -> 4228 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/eval_bits.beambin8092 -> 8384 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.in7
-rw-r--r--erts/configure.in55
-rw-r--r--erts/doc/src/erl_nif.xml12
-rw-r--r--erts/doc/src/erlang.xml68
-rw-r--r--erts/doc/src/notes.xml23
-rw-r--r--erts/doc/src/zlib.xml12
-rw-r--r--erts/emulator/Makefile.in31
-rw-r--r--erts/emulator/beam/atom.names12
-rw-r--r--erts/emulator/beam/beam_bif_load.c909
-rw-r--r--erts/emulator/beam/beam_emu.c77
-rw-r--r--erts/emulator/beam/beam_load.c165
-rw-r--r--erts/emulator/beam/beam_load.h10
-rw-r--r--erts/emulator/beam/bif.tab8
-rw-r--r--erts/emulator/beam/copy.c84
-rw-r--r--erts/emulator/beam/erl_alloc.types3
-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.c2
-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.c118
-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_nif.c180
-rw-r--r--erts/emulator/beam/erl_process.c366
-rw-r--r--erts/emulator/beam/erl_process.h31
-rw-r--r--erts/emulator/beam/erl_thr_progress.c8
-rw-r--r--erts/emulator/beam/erl_time_sup.c6
-rw-r--r--erts/emulator/beam/erl_trace.c34
-rw-r--r--erts/emulator/beam/global.h93
-rw-r--r--erts/emulator/drivers/common/inet_drv.c1
-rw-r--r--erts/emulator/drivers/common/zlib_drv.c58
-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/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.erl (renamed from lib/hipe/util/hipe_vectors.hrl)21
-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.erl12
-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.erl64
-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/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.erl21
-rw-r--r--erts/emulator/test/nif_SUITE_data/nif_SUITE.c48
-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/run_erl.c29
-rw-r--r--erts/example/time_compat.erl4
-rw-r--r--erts/preloaded/ebin/erlang.beambin104816 -> 105100 bytes
-rw-r--r--erts/preloaded/ebin/erts_code_purger.beambin8696 -> 11168 bytes
-rw-r--r--erts/preloaded/ebin/erts_dirty_process_code_checker.beambin0 -> 2144 bytes
-rw-r--r--erts/preloaded/ebin/erts_internal.beambin10536 -> 11116 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 -> 76268 bytes
-rw-r--r--erts/preloaded/ebin/zlib.beambin14136 -> 14320 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.erl141
-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.erl6
-rw-r--r--erts/preloaded/src/zlib.erl41
-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/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/compiler/src/beam_block.erl98
-rw-r--r--lib/compiler/src/beam_dead.erl23
-rw-r--r--lib/compiler/src/beam_jump.erl22
-rw-r--r--lib/compiler/src/sys_pre_expand.erl18
-rw-r--r--lib/compiler/src/v3_core.erl24
-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/bs_bincomp_SUITE.erl1
-rw-r--r--lib/compiler/test/bs_utf_SUITE.erl1
-rw-r--r--lib/compiler/test/match_SUITE.erl10
-rw-r--r--lib/crypto/c_src/crypto.c2
-rw-r--r--lib/debugger/doc/src/Makefile2
-rw-r--r--lib/debugger/src/dbg_iload.erl19
-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/test/bs_bincomp_SUITE.erl1
-rw-r--r--lib/dialyzer/src/dialyzer.erl2
-rw-r--r--lib/diameter/src/Makefile2
-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/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/flow/cfg.inc78
-rw-r--r--lib/hipe/flow/liveness.inc22
-rw-r--r--lib/hipe/icode/hipe_icode_bincomp.erl28
-rw-r--r--lib/hipe/icode/hipe_icode_coordinator.erl29
-rw-r--r--lib/hipe/icode/hipe_icode_range.erl133
-rw-r--r--lib/hipe/icode/hipe_icode_ssa.erl9
-rw-r--r--lib/hipe/icode/hipe_icode_type.erl2
-rw-r--r--lib/hipe/main/hipe.app.src1
-rw-r--r--lib/hipe/misc/Makefile2
-rw-r--r--lib/hipe/misc/hipe_sdi.erl103
-rw-r--r--lib/hipe/misc/hipe_segment_trees.erl181
-rw-r--r--lib/hipe/ppc/hipe_ppc.erl12
-rw-r--r--lib/hipe/ppc/hipe_ppc_assemble.erl7
-rw-r--r--lib/hipe/regalloc/Makefile1
-rw-r--r--lib/hipe/regalloc/hipe_ig_moves.erl11
-rw-r--r--lib/hipe/sparc/hipe_sparc_registers.erl8
-rw-r--r--lib/hipe/ssa/hipe_ssa_liveness.inc27
-rw-r--r--lib/hipe/util/Makefile1
-rw-r--r--lib/hipe/util/hipe_vectors.erl40
-rw-r--r--lib/hipe/x86/hipe_x86_frame.erl23
-rw-r--r--lib/hipe/x86/hipe_x86_postpass.erl3
-rw-r--r--lib/ic/doc/src/Makefile2
-rw-r--r--lib/inets/doc/src/httpd.xml2
-rw-r--r--lib/inets/doc/src/mod_esi.xml2
-rw-r--r--lib/inets/doc/src/notes.xml17
-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/src/inets_app/inets.appup.src6
-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/kernel/doc/src/code.xml10
-rw-r--r--lib/kernel/doc/src/inet.xml9
-rw-r--r--lib/kernel/doc/src/kernel_app.xml2
-rw-r--r--lib/kernel/doc/src/net_kernel.xml36
-rw-r--r--lib/kernel/doc/src/notes.xml35
-rw-r--r--lib/kernel/include/dist_util.hrl8
-rw-r--r--lib/kernel/src/code_server.erl6
-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.erl1
-rw-r--r--lib/kernel/src/inet6_tcp_dist.erl7
-rw-r--r--lib/kernel/src/inet_tcp_dist.erl29
-rw-r--r--lib/kernel/src/net_kernel.erl128
-rw-r--r--lib/kernel/src/os.erl64
-rw-r--r--lib/kernel/test/erl_distribution_SUITE.erl163
-rw-r--r--lib/kernel/test/gen_sctp_SUITE.erl148
-rw-r--r--lib/kernel/test/init_SUITE.erl61
-rw-r--r--lib/kernel/test/os_SUITE.erl40
-rw-r--r--lib/kernel/test/zlib_SUITE.erl38
-rw-r--r--lib/kernel/vsn.mk2
-rw-r--r--lib/odbc/configure.in2
-rw-r--r--lib/reltool/src/reltool.hrl26
-rw-r--r--lib/sasl/src/sasl.app.src2
-rw-r--r--lib/sasl/src/systools_make.erl5
-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.erl21
-rw-r--r--lib/ssh/src/ssh_connection_handler.erl104
-rw-r--r--lib/ssh/test/ssh_algorithms_SUITE.erl18
-rw-r--r--lib/ssh/vsn.mk2
-rw-r--r--lib/ssl/doc/src/notes.xml20
-rw-r--r--lib/ssl/src/dtls_connection.erl13
-rw-r--r--lib/ssl/src/ssl.app.src2
-rw-r--r--lib/ssl/src/ssl.appup.src14
-rw-r--r--lib/ssl/src/ssl_connection.erl2
-rw-r--r--lib/ssl/src/ssl_handshake.erl2
-rw-r--r--lib/ssl/src/ssl_tls_dist_proxy.erl2
-rw-r--r--lib/ssl/src/tls_connection.erl74
-rw-r--r--lib/ssl/src/tls_handshake.erl39
-rw-r--r--lib/ssl/test/ssl_basic_SUITE.erl7
-rw-r--r--lib/ssl/test/ssl_crl_SUITE.erl2
-rw-r--r--lib/ssl/test/ssl_npn_hello_SUITE.erl9
-rw-r--r--lib/ssl/test/ssl_packet_SUITE.erl4
-rw-r--r--lib/ssl/test/ssl_payload_SUITE.erl9
-rw-r--r--lib/ssl/test/ssl_to_openssl_SUITE.erl3
-rw-r--r--lib/ssl/test/ssl_upgrade_SUITE.erl20
-rw-r--r--lib/ssl/vsn.mk2
-rw-r--r--lib/stdlib/doc/src/ets.xml2
-rw-r--r--lib/stdlib/doc/src/gen_statem.xml247
-rw-r--r--lib/stdlib/src/eval_bits.erl14
-rw-r--r--lib/stdlib/src/gen_statem.erl260
-rw-r--r--lib/stdlib/test/gen_statem_SUITE.erl121
-rw-r--r--lib/stdlib/vsn.mk2
-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/tools/doc/src/erlang_mode.xml9
-rw-r--r--lib/tools/emacs/erlang-skels.el149
-rw-r--r--lib/tools/src/fprof.erl11
-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/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/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
-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.table5
-rw-r--r--system/doc/design_principles/statem.xml84
309 files changed, 6570 insertions, 2331 deletions
diff --git a/HOWTO/INSTALL.md b/HOWTO/INSTALL.md
index 8632f46264..551ecdd224 100644
--- a/HOWTO/INSTALL.md
+++ b/HOWTO/INSTALL.md
@@ -429,6 +429,10 @@ Some of the available `configure` options are:
and **not supported**. This functionality **will** be subject to backward
incompatible changes. Note that you should **not** enable the dirty scheduler
functionality on production systems. It is only provided for testing.
+ This switch also imply `--enable-new-purge-strategy` (see below).
+* `--enable-new-purge-strategy` - Enable the purge strategy that will be
+ introduced in ERTS version 9.0 (OTP 20). Note that this switch will be
+ removed in OTP 20.
If you or your system has special requirements please read the `Makefile` for
additional configuration information.
diff --git a/bootstrap/bin/start.boot b/bootstrap/bin/start.boot
index 295c805ea8..2a1234e925 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 295c805ea8..2a1234e925 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 38b3c31cab..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_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 da62dd7191..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 283fcb7478..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, "7.0"},
+ {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/sys_pre_expand.beam b/bootstrap/lib/compiler/ebin/sys_pre_expand.beam
index 0914282354..353306191c 100644
--- a/bootstrap/lib/compiler/ebin/sys_pre_expand.beam
+++ b/bootstrap/lib/compiler/ebin/sys_pre_expand.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/v3_core.beam b/bootstrap/lib/compiler/ebin/v3_core.beam
index e8e120e145..af9edb4442 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/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.app b/bootstrap/lib/kernel/ebin/kernel.app
index a4600713e5..9bf6195a94 100644
--- a/bootstrap/lib/kernel/ebin/kernel.app
+++ b/bootstrap/lib/kernel/ebin/kernel.app
@@ -22,7 +22,7 @@
{application, kernel,
[
{description, "ERTS CXC 138 10"},
- {vsn, "5.0"},
+ {vsn, "5.0.1"},
{modules, [application,
application_controller,
application_master,
diff --git a/bootstrap/lib/kernel/ebin/kernel.appup b/bootstrap/lib/kernel/ebin/kernel.appup
index 1ed9771492..60e4217586 100644
--- a/bootstrap/lib/kernel/ebin/kernel.appup
+++ b/bootstrap/lib/kernel/ebin/kernel.appup
@@ -16,7 +16,7 @@
%% limitations under the License.
%%
%% %CopyrightEnd%
-{"5.0",
+{"5.0.1",
%% Up from - max one major revision back
[{<<"5\\.0(\\.[0-9]+)*">>,[restart_new_emulator]}, % OTP-19.*
{<<"4\\.[0-2](\\.[0-9]+)*">>,[restart_new_emulator]}], % OTP-18.*
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/os.beam b/bootstrap/lib/kernel/ebin/os.beam
index 9ebe6c770f..f3ad1d53d4 100644
--- a/bootstrap/lib/kernel/ebin/os.beam
+++ b/bootstrap/lib/kernel/ebin/os.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/eval_bits.beam b/bootstrap/lib/stdlib/ebin/eval_bits.beam
index 0529da7250..01df81bb14 100644
--- a/bootstrap/lib/stdlib/ebin/eval_bits.beam
+++ b/bootstrap/lib/stdlib/ebin/eval_bits.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..ea98dc9836 100644
--- a/configure.in
+++ b/configure.in
@@ -129,6 +129,13 @@ AC_PROG_CC
AC_PROG_CXX
AC_CHECK_TOOL(LD, [ld])
+_search_path=/bin:/usr/bin:/usr/local/bin:$PATH
+
+AC_PATH_PROG(ENV, [env], false, $_search_path)
+if test "$ac_cv_path_ENV" = false; then
+ AC_MSG_ERROR([No 'env' command found])
+fi
+
#
# We need GNU make, complain if we can't find it
#
diff --git a/erts/configure.in b/erts/configure.in
index c5b08547cb..c0326beac2 100644
--- a/erts/configure.in
+++ b/erts/configure.in
@@ -121,6 +121,7 @@ AS_HELP_STRING([--enable-bootstrap-only],
enable_hipe=no
enable_sctp=no
enable_dirty_schedulers=no
+ enable_new_purge=no
fi
])
@@ -139,6 +140,13 @@ AS_HELP_STRING([--enable-dirty-schedulers], [enable dirty scheduler support]),
*) enable_dirty_schedulers=yes ;;
esac ], enable_dirty_schedulers=default)
+AC_ARG_ENABLE(new-purge-strategy,
+AS_HELP_STRING([--enable-new-purge-strategy], [enable new code purge strategy]),
+[ case "$enableval" in
+ no) enable_new_purge=no ;;
+ *) enable_new_purge=yes ;;
+ esac ], enable_new_purge=default)
+
AC_ARG_ENABLE(smp-support,
AS_HELP_STRING([--enable-smp-support], [enable smp support])
AS_HELP_STRING([--disable-smp-support], [disable smp support]),
@@ -738,6 +746,11 @@ if test "$ac_cv_path_MKDIR" = false; then
AC_MSG_ERROR([No 'mkdir' command found])
fi
+AC_PATH_PROG(CP, cp, false, $_search_path)
+if test "$ac_cv_path_CP" = false; then
+ AC_MSG_ERROR([No 'cp' command found])
+fi
+
_search_path=
@@ -1023,6 +1036,27 @@ esac
AC_MSG_RESULT($DIRTY_SCHEDULER_SUPPORT)
AC_SUBST(DIRTY_SCHEDULER_SUPPORT)
+AC_MSG_CHECKING(whether the new code purge strategy should be enabled)
+case $enable_new_purge-$enable_dirty_schedulers in
+ yes-*)
+ AC_MSG_RESULT(yes)
+ enable_new_purge=yes;;
+ default-yes)
+ AC_MSG_RESULT(yes; forced by dirty scheduler support)
+ enable_new_purge=yes;;
+ no-yes)
+ AC_MSG_ERROR([Dirty schedulers enabled, but new code purge strategy disabled]);;
+ *)
+ AC_MSG_RESULT(no)
+ enable_new_purge=no;;
+esac
+
+if test $enable_new_purge = yes; then
+ AC_DEFINE(ERTS_NEW_PURGE_STRATEGY, 1, [Define if you want to use the new code purge strategy])
+fi
+NEW_PURGE_STRATEGY=$enable_new_purge
+AC_SUBST(NEW_PURGE_STRATEGY)
+
if test $ERTS_BUILD_SMP_EMU = yes; then
if test $found_threads = no; then
@@ -1354,6 +1388,8 @@ AC_ARG_ENABLE(builtin-zlib,
Z_LIB=
if test "x$enable_builtin_zlib" = "xyes"; then
+ AC_DEFINE(HAVE_ZLIB_INFLATEGETDICTIONARY, 1,
+ [Define if your zlib version defines inflateGetDictionary.])
AC_MSG_NOTICE([Using our own built-in zlib source])
else
AC_MSG_CHECKING(for zlib 1.2.5 or higher)
@@ -1380,6 +1416,11 @@ error
AC_MSG_RESULT(no)
])
LIBS=$zlib_save_LIBS
+
+AC_MSG_CHECKING(for zlib inflateGetDictionary presence)
+AC_SEARCH_LIBS(inflateGetDictionary, [z],
+ AC_DEFINE(HAVE_ZLIB_INFLATEGETDICTIONARY, 1,
+ [Define if your zlib version defines inflateGetDictionary.]))
fi
AC_SUBST(Z_LIB)
@@ -4817,8 +4858,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 +4868,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/erl_nif.xml b/erts/doc/src/erl_nif.xml
index 2b627281fe..4ec5ab78d8 100644
--- a/erts/doc/src/erl_nif.xml
+++ b/erts/doc/src/erl_nif.xml
@@ -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>
diff --git a/erts/doc/src/erlang.xml b/erts/doc/src/erlang.xml
index 2b7a3b85e5..d5eae2d66d 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>
@@ -776,6 +801,29 @@ 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>
<p>Failures:</p>
@@ -8451,7 +8499,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 +8527,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>
@@ -8709,7 +8757,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 +9038,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 +9049,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>
diff --git a/erts/doc/src/notes.xml b/erts/doc/src/notes.xml
index 748fa895e8..ca3f654c36 100644
--- a/erts/doc/src/notes.xml
+++ b/erts/doc/src/notes.xml
@@ -32,6 +32,29 @@
<p>This document describes the changes made to the ERTS application.</p>
+<section><title>Erts 8.0.3</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Fixed a race that could cause a lost wakeup of a process
+ that timed out in a <c>receive ... after</c>. This bug
+ was introduced in ERTS version 7.0.</p>
+ <p>
+ Own Id: OTP-13798 Aux Id: OTP-11997 </p>
+ </item>
+ <item>
+ <p>
+ Fixed segfault after writing an erl crash dump.</p>
+ <p>
+ Own Id: OTP-13799</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Erts 8.0.2</title>
<section><title>Fixed Bugs and Malfunctions</title>
diff --git a/erts/doc/src/zlib.xml b/erts/doc/src/zlib.xml
index 138414a880..e1924fffee 100644
--- a/erts/doc/src/zlib.xml
+++ b/erts/doc/src/zlib.xml
@@ -576,6 +576,18 @@ unpack(Z, Compressed, Dict) ->
</func>
<func>
+ <name name="inflateGetDictionary" arity="1"/>
+ <fsummary>Return the decompression dictionary.</fsummary>
+ <desc>
+ <p>Returns the decompression dictionary currently in use
+ by the stream. This function must be called between
+ <seealso marker="#inflateInit/1"><c>inflateInit/1,2</c></seealso>
+ and <seealso marker="#inflateEnd/1"><c>inflateEnd</c></seealso>.</p>
+ <p>Only supported if ERTS was compiled with zlib >= 1.2.8.</p>
+ </desc>
+ </func>
+
+ <func>
<name name="open" arity="0"/>
<fsummary>Open a stream and return a stream reference.</fsummary>
<desc>
diff --git a/erts/emulator/Makefile.in b/erts/emulator/Makefile.in
index 3430ea5f7b..43d1914b42 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..9dae67cb2d 100644
--- a/erts/emulator/beam/atom.names
+++ b/erts/emulator/beam/atom.names
@@ -65,6 +65,7 @@ atom undefined_lambda
atom DOWN='DOWN'
atom UP='UP'
atom EXIT='EXIT'
+atom abort
atom aborted
atom abs_path
atom absoluteURI
@@ -165,6 +166,7 @@ atom commandv
atom compact
atom compat_rel
atom compile
+atom complete
atom compressed
atom config_h
atom convert_time_unit
@@ -176,6 +178,7 @@ atom const
atom context_switches
atom control
atom copy
+atom copy_literals
atom counters
atom cpu
atom cpu_timestamp
@@ -195,6 +198,7 @@ atom dgroup_leader
atom dictionary
atom dirty_cpu
atom dirty_cpu_schedulers_online
+atom dirty_execution
atom dirty_io
atom disable_trace
atom disabled
@@ -235,6 +239,7 @@ atom erlang
atom ERROR='ERROR'
atom error_handler
atom error_logger
+atom erts_code_purger
atom erts_internal
atom ets
atom ETS_TRANSFER='ETS-TRANSFER'
@@ -384,8 +389,10 @@ atom merge_trap
atom meta
atom meta_match_spec
atom micro_seconds
+atom microsecond
atom microstate_accounting
atom milli_seconds
+atom millisecond
atom min_heap_size
atom min_bin_vheap_size
atom minor_version
@@ -402,11 +409,13 @@ atom more
atom multi_scheduling
atom multiline
atom nano_seconds
+atom nanosecond
atom name
atom named_table
atom namelist
atom native
atom native_addresses
+atom need_gc
atom Neq='=/='
atom Neqeq='/='
atom net_kernel
@@ -485,6 +494,7 @@ atom pause
atom pending
atom pending_driver
atom pending_process
+atom pending_purge_lambda
atom pending_reload
atom permanent
atom pid
@@ -494,6 +504,7 @@ atom port_count
atom port_limit
atom port_op
atom positive
+atom prepare
atom print
atom priority
atom private
@@ -554,6 +565,7 @@ atom schedulers_online
atom scheme
atom scientific
atom scope
+atom second
atom seconds
atom send_to_non_existing_process
atom sensitive
diff --git a/erts/emulator/beam/beam_bif_load.c b/erts/emulator/beam/beam_bif_load.c
index 15e878ba65..5c28fb1a98 100644
--- a/erts/emulator/beam/beam_bif_load.c
+++ b/erts/emulator/beam/beam_bif_load.c
@@ -37,13 +37,85 @@
#include "erl_bits.h"
#include "erl_thr_progress.h"
+#ifdef HIPE
+# include "hipe_stack.h"
+#endif
+
+static struct {
+ Eterm module;
+ erts_smp_mtx_t mtx;
+ Export *pending_purge_lambda;
+ Eterm *sprocs;
+ Eterm def_sprocs[10];
+ Uint sp_size;
+ Uint sp_ix;
+ ErlFunEntry **funs;
+ ErlFunEntry *def_funs[10];
+ Uint fe_size;
+ Uint fe_ix;
+} purge_state;
+
+Process *erts_code_purger = NULL;
+
+ErtsLiteralArea *erts_copy_literal_area = NULL;
+#ifdef ERTS_DIRTY_SCHEDULERS
+Process *erts_dirty_process_code_checker;
+#endif
+#ifdef ERTS_NEW_PURGE_STRATEGY
+Process *erts_literal_area_collector = NULL;
+
+typedef struct ErtsLiteralAreaRef_ ErtsLiteralAreaRef;
+struct ErtsLiteralAreaRef_ {
+ ErtsLiteralAreaRef *next;
+ ErtsLiteralArea *literal_area;
+};
+
+struct {
+ erts_smp_mtx_t mtx;
+ ErtsLiteralAreaRef *first;
+ ErtsLiteralAreaRef *last;
+} release_literal_areas;
+#endif
+
static void set_default_trace_pattern(Eterm module);
static Eterm check_process_code(Process* rp, Module* modp, Uint flags, int *redsp, int fcalls);
static void delete_code(Module* modp);
-static void decrement_refc(BeamCodeHeader*);
static int any_heap_ref_ptrs(Eterm* start, Eterm* end, char* mod_start, Uint mod_size);
static int any_heap_refs(Eterm* start, Eterm* end, char* mod_start, Uint mod_size);
+static void
+init_purge_state(void)
+{
+ purge_state.module = THE_NON_VALUE;
+
+ erts_smp_mtx_init(&purge_state.mtx, "purge_state");
+
+ purge_state.pending_purge_lambda =
+ erts_export_put(am_erts_code_purger, am_pending_purge_lambda, 3);
+
+ purge_state.sprocs = &purge_state.def_sprocs[0];
+ purge_state.sp_size = sizeof(purge_state.def_sprocs);
+ purge_state.sp_size /= sizeof(purge_state.def_sprocs[0]);
+ purge_state.sp_ix = 0;
+
+ purge_state.funs = &purge_state.def_funs[0];
+ purge_state.fe_size = sizeof(purge_state.def_funs);
+ purge_state.fe_size /= sizeof(purge_state.def_funs[0]);
+ purge_state.fe_ix = 0;
+}
+
+void
+erts_beam_bif_load_init(void)
+{
+#ifdef ERTS_NEW_PURGE_STRATEGY
+ erts_smp_mtx_init(&release_literal_areas.mtx, "release_literal_areas");
+ release_literal_areas.first = NULL;
+ release_literal_areas.last = NULL;
+#endif
+
+ init_purge_state();
+}
+
BIF_RETTYPE code_is_module_native_1(BIF_ALIST_1)
{
Module* modp;
@@ -516,6 +588,43 @@ badarg:
BIF_ERROR(BIF_P, BADARG);
}
+BIF_RETTYPE erts_internal_check_dirty_process_code_2(BIF_ALIST_2)
+{
+#if !defined(ERTS_DIRTY_SCHEDULERS)
+ BIF_ERROR(BIF_P, EXC_NOTSUP);
+#else
+ Process *rp;
+ int reds = 0;
+ Eterm res;
+
+ if (BIF_P != erts_dirty_process_code_checker)
+ BIF_ERROR(BIF_P, EXC_NOTSUP);
+
+ if (is_not_internal_pid(BIF_ARG_1))
+ BIF_ERROR(BIF_P, BADARG);
+
+ if (is_not_atom(BIF_ARG_2))
+ BIF_ERROR(BIF_P, BADARG);
+
+ rp = erts_pid2proc_not_running(BIF_P, ERTS_PROC_LOCK_MAIN,
+ BIF_ARG_1, ERTS_PROC_LOCK_MAIN);
+ if (rp == ERTS_PROC_LOCK_BUSY)
+ ERTS_BIF_YIELD2(bif_export[BIF_erts_internal_check_dirty_process_code_2],
+ BIF_P, BIF_ARG_1, BIF_ARG_2);
+ if (!rp)
+ BIF_RET(am_false);
+
+ res = erts_check_process_code(rp, BIF_ARG_2, 0, &reds, BIF_P->fcalls);
+
+ if (BIF_P != rp)
+ erts_smp_proc_unlock(rp, ERTS_PROC_LOCK_MAIN);
+
+ ASSERT(is_value(res));
+
+ BIF_RET2(res, reds);
+#endif
+}
+
BIF_RETTYPE delete_module_1(BIF_ALIST_1)
{
ErtsCodeIndex code_ix;
@@ -748,6 +857,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 +873,244 @@ 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;
+ 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 +1204,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)
@@ -873,12 +1222,12 @@ check_process_code(Process* rp, Module* modp, Uint flags, int *redsp, int fcalls
continue;
{
ErlHeapFragment *hf;
- Uint lit_sz;
+ Uint lit_sz = 0;
for (hf=hfrag; hf; hf = hf->next) {
if (check_mod_funs(rp, &hfrag->off_heap, mod_start, mod_size))
return am_true;
- lit_sz = hfrag_literal_size(&hf->mem[0], &hf->mem[hf->used_size],
- literals, lit_bsize);
+ lit_sz += hfrag_literal_size(&hf->mem[0], &hf->mem[hf->used_size],
+ literals, lit_bsize);
}
if (lit_sz > 0) {
ErlHeapFragment *bp = new_message_buffer(lit_sz);
@@ -903,12 +1252,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 +1259,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 +1341,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 +1354,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 +1484,438 @@ hfrag_literal_copy(Eterm **hpp, ErlOffHeap *ohp,
}
}
-#undef in_area
+#ifdef ERTS_NEW_PURGE_STRATEGY
+
+ErtsThrPrgrLaterOp later_literal_area_switch;
#ifdef ERTS_SMP
-static void copy_literals_commit(void*);
+static void
+complete_literal_area_switch(void *unused)
+{
+ 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);
+}
#endif
-copy_literals_t erts_clrange = {NULL, 0, THE_NON_VALUE};
+#endif /* ERTS_NEW_PURGE_STRATEGY */
-/* 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 -----
- * ...
- */
+BIF_RETTYPE erts_internal_release_literal_area_switch_0(BIF_ALIST_0)
+{
+#ifndef ERTS_NEW_PURGE_STRATEGY
+ BIF_ERROR(BIF_P, EXC_NOTSUP);
+#else
+ ErtsLiteralAreaRef *la_ref;
+ if (BIF_P != erts_literal_area_collector)
+ BIF_ERROR(BIF_P, EXC_NOTSUP);
-BIF_RETTYPE erts_internal_copy_literals_2(BIF_ALIST_2)
-{
- ErtsCodeIndex code_ix;
- Eterm res = am_true;
+ erts_smp_mtx_lock(&release_literal_areas.mtx);
- if (is_not_atom(BIF_ARG_1) || (am_true != BIF_ARG_2 && am_false != BIF_ARG_2)) {
- BIF_ERROR(BIF_P, BADARG);
+ 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;
}
- 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_unlock(&release_literal_areas.mtx);
- code_ix = erts_active_code_ix();
+ if (erts_copy_literal_area)
+ erts_release_literal_area(erts_copy_literal_area);
- 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;
+ if (!la_ref) {
+ erts_copy_literal_area = NULL;
+ BIF_RET(am_false);
}
-#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_copy_literal_area = la_ref->literal_area;
+
+ erts_free(ERTS_ALC_T_LITERAL_REF, la_ref);
+
+#ifndef ERTS_SMP
+ BIF_RET(am_true);
+#else
+ erts_schedule_thr_prgr_later_op(complete_literal_area_switch,
+ NULL,
+ &later_literal_area_switch);
erts_suspend(BIF_P, ERTS_PROC_LOCK_MAIN, NULL);
ERTS_BIF_YIELD_RETURN(BIF_P, 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: {
+ /*
+ * Prepare for purge by marking all fun
+ * entries referring to the code to purge
+ * with "pending purge" markers.
+ */
+ ErtsCodeIndex code_ix;
+ Module* modp;
+ Eterm res;
- /*
- * Correct module?
- */
+ if (is_value(purge_state.module))
+ BIF_ERROR(BIF_P, BADARG);
- if ((modp = erts_get_module(BIF_ARG_1, code_ix)) == NULL) {
- ERTS_BIF_PREP_RET(ret, am_false);
- }
- else {
- erts_rwlock_old_code(code_ix);
+ code_ix = erts_active_code_ix();
/*
- * Any code to purge?
+ * Correct module?
*/
- if (!modp->old.code_hdr) {
- ERTS_BIF_PREP_RET(ret, am_false);
- }
+ modp = erts_get_module(BIF_ARG_1, code_ix);
+ if (!modp)
+ res = am_false;
else {
/*
- * Unload any NIF library
+ * Any code to purge?
*/
- if (modp->old.nif != NULL) {
- /* ToDo: Do unload nif without blocking */
- erts_rwunlock_old_code(code_ix);
- erts_smp_proc_unlock(BIF_P, ERTS_PROC_LOCK_MAIN);
- erts_smp_thr_progress_block();
- is_blocking = 1;
- erts_rwlock_old_code(code_ix);
- erts_unload_nif(modp->old.nif);
- modp->old.nif = NULL;
+ erts_rlock_old_code(code_ix);
+ if (!modp->old.code_hdr)
+ res = am_false;
+ else {
+ BeamInstr* code;
+ BeamInstr* end;
+ erts_smp_mtx_lock(&purge_state.mtx);
+ purge_state.module = BIF_ARG_1;
+ erts_smp_mtx_unlock(&purge_state.mtx);
+ res = am_true;
+ code = (BeamInstr*) modp->old.code_hdr;
+ end = (BeamInstr *)((char *)code + modp->old.code_length);
+ erts_fun_purge_prepare(code, end);
+#if !defined(ERTS_NEW_PURGE_STRATEGY)
+ ASSERT(!erts_copy_literal_area);
+ erts_copy_literal_area = modp->old.code_hdr->literal_area;
+#endif
}
-
+ erts_runlock_old_code(code_ix);
+ }
+
+#ifndef ERTS_SMP
+ BIF_RET(res);
+#else
+ if (res != am_true)
+ BIF_RET(res);
+ else {
/*
- * Remove the old code.
+ * We'll be resumed when all schedulers are guaranteed
+ * to see the "pending purge" markers that we've made on
+ * all fun entries of the code that we are about to purge.
+ * Processes trying to call these funs will be suspended
+ * before calling the funs. That is we are guaranteed not
+ * to get any more direct references into the code while
+ * checking for such references...
*/
- ASSERT(erts_total_code_size >= modp->old.code_length);
- erts_total_code_size -= modp->old.code_length;
- code = (BeamInstr*) modp->old.code_hdr;
- end = (BeamInstr *)((char *)code + modp->old.code_length);
- erts_cleanup_funs_on_purge(code, end);
- beam_catches_delmod(modp->old.catches, code, modp->old.code_length,
- code_ix);
- decrement_refc(modp->old.code_hdr);
- if (modp->old.code_hdr->literals_start) {
- erts_free(ERTS_ALC_T_LITERAL, modp->old.code_hdr->literals_start);
- }
- erts_free(ERTS_ALC_T_CODE, (void *) code);
- modp->old.code_hdr = NULL;
- modp->old.code_length = 0;
- modp->old.catches = BEAM_CATCHES_NIL;
- erts_remove_from_ranges(code);
- ERTS_BIF_PREP_RET(ret, am_true);
+ erts_schedule_thr_prgr_later_op(resume_purger,
+ NULL,
+ &purger_lop_data);
+ erts_suspend(BIF_P, ERTS_PROC_LOCK_MAIN, NULL);
+ ERTS_BIF_YIELD_RETURN(BIF_P, am_true);
}
- erts_rwunlock_old_code(code_ix);
+#endif
}
- if (is_blocking) {
- erts_smp_thr_progress_unblock();
- erts_smp_proc_lock(BIF_P, ERTS_PROC_LOCK_MAIN);
+
+ case am_abort: {
+ /*
+ * Soft purge that detected direct references into the code
+ * we set out to purge. Abort the purge.
+ */
+
+ if (purge_state.module != BIF_ARG_1)
+ BIF_ERROR(BIF_P, BADARG);
+
+ erts_fun_purge_abort_prepare(purge_state.funs, purge_state.fe_ix);
+
+#if !defined(ERTS_NEW_PURGE_STRATEGY)
+ ASSERT(erts_copy_literal_area);
+ erts_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);
}
- oh = oh->next;
+ else {
+
+ erts_rwlock_old_code(code_ix);
+
+ /*
+ * Any code to purge?
+ */
+ if (!modp->old.code_hdr) {
+ ERTS_BIF_PREP_RET(ret, am_false);
+ }
+ else {
+ /*
+ * Unload any NIF library
+ */
+ if (modp->old.nif != NULL) {
+ /* ToDo: Do unload nif without blocking */
+ erts_rwunlock_old_code(code_ix);
+ erts_smp_proc_unlock(BIF_P, ERTS_PROC_LOCK_MAIN);
+ erts_smp_thr_progress_block();
+ is_blocking = 1;
+ erts_rwlock_old_code(code_ix);
+ erts_unload_nif(modp->old.nif);
+ modp->old.nif = NULL;
+ }
+
+ /*
+ * Remove the old code.
+ */
+ ASSERT(erts_total_code_size >= modp->old.code_length);
+ erts_total_code_size -= modp->old.code_length;
+ code = (BeamInstr*) modp->old.code_hdr;
+ erts_fun_purge_complete(purge_state.funs, purge_state.fe_ix);
+ beam_catches_delmod(modp->old.catches, code, modp->old.code_length,
+ code_ix);
+ literals = modp->old.code_hdr->literal_area;
+ modp->old.code_hdr->literal_area = NULL;
+ erts_free(ERTS_ALC_T_CODE, (void *) code);
+ modp->old.code_hdr = NULL;
+ modp->old.code_length = 0;
+ modp->old.catches = BEAM_CATCHES_NIL;
+ erts_remove_from_ranges(code);
+ ERTS_BIF_PREP_RET(ret, am_true);
+ }
+ erts_rwunlock_old_code(code_ix);
+ }
+ if (is_blocking) {
+ erts_smp_thr_progress_unblock();
+ erts_smp_proc_lock(BIF_P, ERTS_PROC_LOCK_MAIN);
+ }
+
+ erts_release_code_write_permission();
+
+ finalize_purge_operation(BIF_P, ret == am_true);
+
+#if !defined(ERTS_NEW_PURGE_STRATEGY)
+
+ ASSERT(erts_copy_literal_area == literals);
+ erts_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 757b69787a..0ac22b637e 100644
--- a/erts/emulator/beam/beam_emu.c
+++ b/erts/emulator/beam/beam_emu.c
@@ -2161,7 +2161,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;
@@ -5249,19 +5252,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 +5377,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 +5386,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);
@@ -6506,34 +6504,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 517004d3a0..5810cce4c7 100644
--- a/erts/emulator/beam/beam_load.c
+++ b/erts/emulator/beam/beam_load.c
@@ -105,7 +105,7 @@ typedef struct {
*/
typedef struct genop {
- int op; /* Opcode. */
+ unsigned int op; /* Opcode. */
int arity; /* Number of arguments. */
GenOpArg def_args[MAX_OPARGS]; /* Default buffer for arguments. */
GenOpArg* a; /* The arguments. */
@@ -283,8 +283,8 @@ typedef struct LoaderState {
byte* code_start; /* Start of code file. */
unsigned code_size; /* Size of code file. */
int specific_op; /* Specific opcode (-1 if not found). */
- int num_functions; /* Number of functions in module. */
- int num_labels; /* Number of labels. */
+ unsigned int num_functions; /* Number of functions in module. */
+ unsigned int num_labels; /* Number of labels. */
BeamCodeHeader* hdr; /* Loaded code header */
BeamInstr* codev; /* Loaded code buffer */
int codev_size; /* Size of code buffer in words. */
@@ -303,13 +303,13 @@ typedef struct LoaderState {
* Atom table.
*/
- int num_atoms; /* Number of atoms in atom table. */
+ unsigned int num_atoms; /* Number of atoms in atom table. */
Eterm* atom; /* Atom table. */
- int num_exps; /* Number of exports. */
+ unsigned int num_exps; /* Number of exports. */
ExportEntry* export; /* Pointer to export table. */
- int num_imports; /* Number of imports. */
+ unsigned int num_imports; /* Number of imports. */
ImportEntry* import; /* Import entry (translated information). */
/*
@@ -323,8 +323,8 @@ typedef struct LoaderState {
* Lambda table.
*/
- int num_lambdas; /* Number of lambdas in table. */
- int lambdas_allocated; /* Size of allocated lambda table. */
+ unsigned int num_lambdas; /* Number of lambdas in table. */
+ unsigned int lambdas_allocated; /* Size of allocated lambda table. */
Lambda* lambdas; /* Pointer to lambdas. */
Lambda def_lambdas[16]; /* Default storage for lambda table. */
char* lambda_error; /* Delayed missing 'FunT' error. */
@@ -333,8 +333,8 @@ typedef struct LoaderState {
* Literals (constant pool).
*/
- int num_literals; /* Number of literals in table. */
- int allocated_literals; /* Number of literal entries allocated. */
+ unsigned int num_literals; /* Number of literals in table. */
+ unsigned int allocated_literals; /* Number of literal entries allocated. */
Literal* literals; /* Array of literals. */
LiteralPatch* literal_patches; /* Operands that need to be patched. */
Uint total_literal_size; /* Total heap size for all literals. */
@@ -343,13 +343,13 @@ typedef struct LoaderState {
* Line table.
*/
BeamInstr* line_item; /* Line items from the BEAM file. */
- int num_line_items; /* Number of line items. */
+ unsigned int num_line_items;/* Number of line items. */
LineInstr* line_instr; /* Line instructions */
- int num_line_instrs; /* Maximum number of line instructions */
- int current_li; /* Current line instruction */
- int* func_line; /* Mapping from function to first line instr */
+ unsigned int num_line_instrs; /* Maximum number of line instructions */
+ unsigned int current_li; /* Current line instruction */
+ unsigned int* func_line; /* Mapping from function to first line instr */
Eterm* fname; /* List of file names */
- int num_fnames; /* Number of filenames in fname table */
+ unsigned int num_fnames; /* Number of filenames in fname table */
int loc_size; /* Size of location info in bytes (2/4) */
} LoaderState;
@@ -663,7 +663,7 @@ erts_prepare_loading(Binary* magic, Process *c_p, Eterm group_leader,
stp->hdr->compile_ptr = NULL;
stp->hdr->compile_size = 0;
stp->hdr->compile_size_on_heap = 0;
- stp->hdr->literals_start = NULL;
+ stp->hdr->literal_area = NULL;
stp->hdr->md5_ptr = NULL;
/*
@@ -1005,8 +1005,9 @@ loader_state_dtor(Binary* magic)
stp->bin = 0;
}
if (stp->hdr != 0) {
- if (stp->hdr->literals_start) {
- erts_free(ERTS_ALC_T_LITERAL, stp->hdr->literals_start);
+ if (stp->hdr->literal_area) {
+ erts_release_literal_area(stp->hdr->literal_area);
+ stp->hdr->literal_area = NULL;
}
erts_free(ERTS_ALC_T_CODE, stp->hdr);
stp->hdr = 0;
@@ -1330,7 +1331,7 @@ verify_chunks(LoaderState* stp)
static int
load_atom_table(LoaderState* stp)
{
- int i;
+ unsigned int i;
GetInt(stp, 4, stp->num_atoms);
stp->num_atoms++;
@@ -1375,13 +1376,13 @@ load_atom_table(LoaderState* stp)
static int
load_import_table(LoaderState* stp)
{
- int i;
+ unsigned int i;
GetInt(stp, 4, stp->num_imports);
stp->import = erts_alloc(ERTS_ALC_T_PREPARED_CODE,
stp->num_imports * sizeof(ImportEntry));
for (i = 0; i < stp->num_imports; i++) {
- int n;
+ unsigned int n;
Eterm mod;
Eterm func;
Uint arity;
@@ -1389,17 +1390,17 @@ load_import_table(LoaderState* stp)
GetInt(stp, 4, n);
if (n >= stp->num_atoms) {
- LoadError2(stp, "import entry %d: invalid atom number %d", i, n);
+ LoadError2(stp, "import entry %u: invalid atom number %u", i, n);
}
mod = stp->import[i].module = stp->atom[n];
GetInt(stp, 4, n);
if (n >= stp->num_atoms) {
- LoadError2(stp, "import entry %d: invalid atom number %d", i, n);
+ LoadError2(stp, "import entry %u: invalid atom number %u", i, n);
}
func = stp->import[i].function = stp->atom[n];
GetInt(stp, 4, arity);
if (arity > MAX_REG) {
- LoadError2(stp, "import entry %d: invalid arity %d", i, arity);
+ LoadError2(stp, "import entry %u: invalid arity %d", i, arity);
}
stp->import[i].arity = arity;
stp->import[i].patches = 0;
@@ -1427,12 +1428,12 @@ load_import_table(LoaderState* stp)
static int
read_export_table(LoaderState* stp)
{
- int i;
+ unsigned int i;
BeamInstr* address;
GetInt(stp, 4, stp->num_exps);
if (stp->num_exps > stp->num_functions) {
- LoadError2(stp, "%d functions exported; only %d functions defined",
+ LoadError2(stp, "%u functions exported; only %u functions defined",
stp->num_exps, stp->num_functions);
}
stp->export
@@ -1450,16 +1451,16 @@ read_export_table(LoaderState* stp)
stp->export[i].function = func;
GetInt(stp, 4, arity);
if (arity > MAX_REG) {
- LoadError2(stp, "export table entry %d: absurdly high arity %d", i, arity);
+ LoadError2(stp, "export table entry %u: absurdly high arity %u", i, arity);
}
stp->export[i].arity = arity;
GetInt(stp, 4, n);
if (n >= stp->num_labels) {
- LoadError3(stp, "export table entry %d: invalid label %d (highest defined label is %d)", i, n, stp->num_labels);
+ LoadError3(stp, "export table entry %u: invalid label %u (highest defined label is %u)", i, n, stp->num_labels);
}
value = stp->labels[n].value;
if (value == 0) {
- LoadError2(stp, "export table entry %d: label %d not resolved", i, n);
+ LoadError2(stp, "export table entry %u: label %u not resolved", i, n);
}
stp->export[i].address = address = stp->codev + value;
@@ -1520,7 +1521,7 @@ is_bif(Eterm mod, Eterm func, unsigned arity)
static int
read_lambda_table(LoaderState* stp)
{
- int i;
+ unsigned int i;
GetInt(stp, 4, stp->num_lambdas);
if (stp->num_lambdas > stp->lambdas_allocated) {
@@ -1540,12 +1541,12 @@ read_lambda_table(LoaderState* stp)
GetAtom(stp, n, stp->lambdas[i].function);
GetInt(stp, 4, arity);
if (arity > MAX_REG) {
- LoadError2(stp, "lambda entry %d: absurdly high arity %d", i, arity);
+ LoadError2(stp, "lambda entry %u: absurdly high arity %u", i, arity);
}
stp->lambdas[i].arity = arity;
GetInt(stp, 4, n);
if (n >= stp->num_labels) {
- LoadError3(stp, "lambda entry %d: invalid label %d (highest defined label is %d)",
+ LoadError3(stp, "lambda entry %u: invalid label %u (highest defined label is %u)",
i, n, stp->num_labels);
}
stp->lambdas[i].label = n;
@@ -1566,7 +1567,7 @@ read_lambda_table(LoaderState* stp)
static int
read_literal_table(LoaderState* stp)
{
- int i;
+ unsigned int i;
uLongf uncompressed_sz;
byte* uncompressed = 0;
@@ -1588,7 +1589,7 @@ read_literal_table(LoaderState* stp)
}
for (i = 0; i < stp->num_literals; i++) {
- int sz;
+ Uint sz;
Sint heap_size;
byte* p;
Eterm val;
@@ -1597,7 +1598,7 @@ read_literal_table(LoaderState* stp)
GetInt(stp, 4, sz); /* Size of external term format. */
GetString(stp, p, sz);
if ((heap_size = erts_decode_ext_size(p, sz)) < 0) {
- LoadError1(stp, "literal %d: bad external format", i);
+ LoadError1(stp, "literal %u: bad external format", i);
}
if (heap_size > 0) {
@@ -1607,7 +1608,7 @@ read_literal_table(LoaderState* stp)
val = erts_decode_ext(&factory, &p, 0);
if (is_non_value(val)) {
- LoadError1(stp, "literal %d: bad external format", i);
+ LoadError1(stp, "literal %u: bad external format", i);
}
erts_factory_close(&factory);
stp->literals[i].heap_frags = factory.heap_frags;
@@ -1617,7 +1618,7 @@ read_literal_table(LoaderState* stp)
erts_factory_dummy_init(&factory);
val = erts_decode_ext(&factory, &p, 0);
if (is_non_value(val)) {
- LoadError1(stp, "literal %d: bad external format", i);
+ LoadError1(stp, "literal %u: bad external format", i);
}
ASSERT(is_immed(val));
stp->literals[i].heap_frags = NULL;
@@ -1640,9 +1641,9 @@ read_line_table(LoaderState* stp)
{
unsigned version;
ERTS_DECLARE_DUMMY(unsigned flags);
- int num_line_items;
+ unsigned int num_line_items;
BeamInstr* lp;
- int i;
+ unsigned int i;
BeamInstr fname_index;
BeamInstr tag;
@@ -1721,7 +1722,7 @@ read_line_table(LoaderState* stp)
}
} else if (tag == TAG_a) {
if (val > stp->num_fnames) {
- LoadError2(stp, "file index overflow (%d/%d)",
+ LoadError2(stp, "file index overflow (%u/%u)",
val, stp->num_fnames);
}
fname_index = val;
@@ -1757,9 +1758,9 @@ read_line_table(LoaderState* stp)
stp->num_line_instrs *
sizeof(LineInstr));
stp->current_li = 0;
- stp->func_line = (int *) erts_alloc(ERTS_ALC_T_PREPARED_CODE,
- stp->num_functions *
- sizeof(int));
+ stp->func_line = (unsigned int *) erts_alloc(ERTS_ALC_T_PREPARED_CODE,
+ stp->num_functions *
+ sizeof(int));
return 1;
@@ -1783,6 +1784,10 @@ read_code_header(LoaderState* stp)
*/
GetInt(stp, 4, head_size);
+ if (head_size > stp->file_left) {
+ LoadError2(stp, "invalid code header size %u; bytes left %u",
+ head_size, stp->file_left);
+ }
stp->code_start = stp->file_p + head_size;
stp->code_size = stp->file_left - head_size;
stp->file_left = head_size;
@@ -1887,7 +1892,7 @@ load_code(LoaderState* stp)
ci = stp->ci;
for (;;) {
- int new_op;
+ unsigned int new_op;
GenOp* tmp_op;
ASSERT(ci <= codev_size);
@@ -1895,10 +1900,10 @@ load_code(LoaderState* stp)
get_next_instr:
GetByte(stp, new_op);
if (new_op >= NUM_GENERIC_OPS) {
- LoadError1(stp, "invalid opcode %d", new_op);
+ LoadError1(stp, "invalid opcode %u", new_op);
}
if (gen_opc[new_op].name[0] == '\0') {
- LoadError1(stp, "invalid opcode %d", new_op);
+ LoadError1(stp, "invalid opcode %u", new_op);
}
@@ -2368,7 +2373,7 @@ load_code(LoaderState* stp)
VerifyTag(stp, tag, TAG_u);
last_label = tmp_op->a[arg].val;
if (!(0 < last_label && last_label < stp->num_labels)) {
- LoadError2(stp, "invalid label num %d (0 < label < %d)",
+ LoadError2(stp, "invalid label num %u (0 < label < %u)",
tmp_op->a[arg].val, stp->num_labels);
}
if (stp->labels[last_label].value != 0) {
@@ -2512,7 +2517,7 @@ load_code(LoaderState* stp)
{
Sint offset;
if (function_number >= stp->num_functions) {
- LoadError1(stp, "too many functions in module (header said %d)",
+ LoadError1(stp, "too many functions in module (header said %u)",
stp->num_functions);
}
@@ -2591,14 +2596,14 @@ load_code(LoaderState* stp)
if (stp->line_item) {
BeamInstr item = code[ci-1];
BeamInstr loc;
- int li;
+ unsigned int li;
if (item >= stp->num_line_items) {
- LoadError2(stp, "line instruction index overflow (%d/%d)",
+ LoadError2(stp, "line instruction index overflow (%u/%u)",
item, stp->num_line_items);
}
li = stp->current_li;
if (li >= stp->num_line_instrs) {
- LoadError2(stp, "line instruction table overflow (%d/%d)",
+ LoadError2(stp, "line instruction table overflow (%u/%u)",
li, stp->num_line_instrs);
}
loc = stp->line_item[item];
@@ -4517,13 +4522,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,
@@ -4533,7 +4541,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;
@@ -4544,6 +4552,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);
@@ -4555,8 +4564,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];
@@ -4716,7 +4725,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;
@@ -5388,7 +5397,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,
@@ -5599,6 +5608,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)
{
@@ -5986,11 +6017,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
@@ -6004,7 +6035,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 */
@@ -6018,8 +6049,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];
@@ -6330,9 +6361,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 92a9abddbf..6be4031822 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 978bcd4bba..ea66f165a0 100644
--- a/erts/emulator/beam/bif.tab
+++ b/erts/emulator/beam/bif.tab
@@ -163,6 +163,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
@@ -176,6 +177,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
@@ -644,8 +647,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/copy.c b/erts/emulator/beam/copy.c
index ccc4cbad43..c4dcd6a3cc 100644
--- a/erts/emulator/beam/copy.c
+++ b/erts/emulator/beam/copy.c
@@ -40,7 +40,8 @@ static void move_one_frag(Eterm** hpp, ErlHeapFragment*, ErlOffHeap*, int);
/*
* Copy object "obj" to process p.
*/
-Eterm copy_object_x(Eterm obj, Process* to, Uint extra) {
+Eterm copy_object_x(Eterm obj, Process* to, Uint extra)
+{
if (!is_immed(obj)) {
Uint size = size_object(obj);
Eterm* hp = HAllocX(to, size, extra);
@@ -70,33 +71,46 @@ Eterm copy_object_x(Eterm obj, Process* to, Uint extra) {
* Return the "flat" size of the object.
*/
-Uint size_object(Eterm obj)
+#define in_literal_purge_area(PTR) \
+ (lit_purge_ptr && ( \
+ (lit_purge_ptr <= (PTR) && \
+ (PTR) < (lit_purge_ptr + lit_purge_sz))))
+
+Uint size_object_x(Eterm obj, erts_literal_area_t *litopt)
{
Uint sum = 0;
Eterm* ptr;
int arity;
+ Eterm *lit_purge_ptr = litopt ? litopt->lit_purge_ptr : NULL;
+ Uint lit_purge_sz = litopt ? litopt->lit_purge_sz : 0;
#ifdef DEBUG
Eterm mypid = erts_get_current_pid();
#endif
-
DECLARE_ESTACK(s);
-
VERBOSE(DEBUG_SHCOPY, ("[pid=%T] size_object %p\n", mypid, obj));
for (;;) {
switch (primary_tag(obj)) {
case TAG_PRIMARY_LIST:
- sum += 2;
ptr = list_val(obj);
+ if (litopt && erts_is_literal(obj,ptr) && !in_literal_purge_area(ptr)) {
+ goto pop_next;
+ }
+ sum += 2;
obj = *ptr++;
if (!IS_CONST(obj)) {
ESTACK_PUSH(s, obj);
- }
+ }
obj = *ptr;
break;
case TAG_PRIMARY_BOXED:
{
- Eterm hdr = *boxed_val(obj);
+ Eterm hdr;
+ ptr = boxed_val(obj);
+ if (litopt && erts_is_literal(obj,ptr) && !in_literal_purge_area(ptr)) {
+ goto pop_next;
+ }
+ hdr = *ptr;
ASSERT(is_header(hdr));
switch (hdr & _TAG_HEADER_MASK) {
case ARITYVAL_SUBTAG:
@@ -279,10 +293,6 @@ do { \
#define COUNT_OFF_HEAP (0)
-#define IN_LITERAL_PURGE_AREA(info, ptr) \
- ((info)->range_ptr && ( \
- (info)->range_ptr <= (ptr) && \
- (ptr) < ((info)->range_ptr + (info)->range_sz)))
/*
* Return the real size of an object and find sharing information
* This currently returns the same as erts_debug:size/1.
@@ -599,7 +609,7 @@ cleanup:
/*
* Copy a structure to a heap.
*/
-Eterm copy_struct_x(Eterm obj, Uint sz, Eterm** hpp, ErlOffHeap* off_heap, Uint *bsz)
+Eterm copy_struct_x(Eterm obj, Uint sz, Eterm** hpp, ErlOffHeap* off_heap, Uint *bsz, erts_literal_area_t *litopt)
{
char* hstart;
Uint hsize;
@@ -616,6 +626,8 @@ Eterm copy_struct_x(Eterm obj, Uint sz, Eterm** hpp, ErlOffHeap* off_heap, Uint
Eterm hdr;
Eterm *hend;
int i;
+ Eterm *lit_purge_ptr = litopt ? litopt->lit_purge_ptr : NULL;
+ Uint lit_purge_sz = litopt ? litopt->lit_purge_sz : 0;
#ifdef DEBUG
Eterm org_obj = obj;
Uint org_sz = sz;
@@ -651,7 +663,6 @@ Eterm copy_struct_x(Eterm obj, Uint sz, Eterm** hpp, ErlOffHeap* off_heap, Uint
L_copy:
while (hp != htop) {
obj = *hp;
-
switch (primary_tag(obj)) {
case TAG_PRIMARY_IMMED1:
hp++;
@@ -667,6 +678,10 @@ Eterm copy_struct_x(Eterm obj, Uint sz, Eterm** hpp, ErlOffHeap* off_heap, Uint
L_copy_list:
tailp = argp;
+ if (litopt && erts_is_literal(obj,objp) && !in_literal_purge_area(objp)) {
+ *tailp = obj;
+ goto L_copy;
+ }
for (;;) {
tp = tailp;
elem = CAR(objp);
@@ -674,18 +689,23 @@ Eterm copy_struct_x(Eterm obj, Uint sz, Eterm** hpp, ErlOffHeap* off_heap, Uint
hbot -= 2;
CAR(hbot) = elem;
tailp = &CDR(hbot);
- }
- else {
+ } else {
CAR(htop) = elem;
tailp = &CDR(htop);
htop += 2;
}
*tp = make_list(tailp - 1);
obj = CDR(objp);
+
if (!is_list(obj)) {
break;
}
objp = list_val(obj);
+
+ if (litopt && erts_is_literal(obj,objp) && !in_literal_purge_area(objp)) {
+ *tailp = obj;
+ goto L_copy;
+ }
}
switch (primary_tag(obj)) {
case TAG_PRIMARY_IMMED1: *tailp = obj; goto L_copy;
@@ -695,7 +715,7 @@ Eterm copy_struct_x(Eterm obj, Uint sz, Eterm** hpp, ErlOffHeap* off_heap, Uint
"%s, line %d: Internal error in copy_struct: 0x%08x\n",
__FILE__, __LINE__,obj);
}
-
+
case TAG_PRIMARY_BOXED:
if (ErtsInArea(boxed_val(obj),hstart,hsize)) {
hp++;
@@ -705,6 +725,10 @@ Eterm copy_struct_x(Eterm obj, Uint sz, Eterm** hpp, ErlOffHeap* off_heap, Uint
L_copy_boxed:
objp = boxed_val(obj);
+ if (litopt && erts_is_literal(obj,objp) && !in_literal_purge_area(objp)) {
+ *argp = obj;
+ break;
+ }
hdr = *objp;
switch (hdr & _TAG_HEADER_MASK) {
case ARITYVAL_SUBTAG:
@@ -765,7 +789,7 @@ Eterm copy_struct_x(Eterm obj, Uint sz, Eterm** hpp, ErlOffHeap* off_heap, Uint
extra_bytes = 1;
} else {
extra_bytes = 0;
- }
+ }
real_size = size+extra_bytes;
objp = binary_val(real_bin);
if (thing_subtag(*objp) == HEAP_BINARY_SUBTAG) {
@@ -780,7 +804,7 @@ Eterm copy_struct_x(Eterm obj, Uint sz, Eterm** hpp, ErlOffHeap* off_heap, Uint
} else {
ProcBin* from = (ProcBin *) objp;
ProcBin* to;
-
+
ASSERT(thing_subtag(*objp) == REFC_BINARY_SUBTAG);
if (from->flags) {
erts_emasculate_writable_binary(from);
@@ -900,6 +924,12 @@ Eterm copy_struct_x(Eterm obj, Uint sz, Eterm** hpp, ErlOffHeap* off_heap, Uint
*bsz = hend - hbot;
} else {
#ifdef DEBUG
+ if (!eq(org_obj, res)) {
+ erts_exit(ERTS_ABORT_EXIT,
+ "Internal error in copy_struct() when copying %T:"
+ " not equal to copy %T\n",
+ org_obj, res);
+ }
if (htop != hbot)
erts_exit(ERTS_ABORT_EXIT,
"Internal error in copy_struct() when copying %T:"
@@ -1036,6 +1066,8 @@ Uint copy_shared_calculate(Eterm obj, erts_shcopy_t *info)
Uint e;
unsigned sz;
Eterm* ptr;
+ Eterm *lit_purge_ptr = info->lit_purge_ptr;
+ Uint lit_purge_sz = info->lit_purge_sz;
#ifdef DEBUG
Eterm mypid = erts_get_current_pid();
#endif
@@ -1081,7 +1113,7 @@ Uint copy_shared_calculate(Eterm obj, erts_shcopy_t *info)
/* off heap list pointers are copied verbatim */
if (erts_is_literal(obj,ptr)) {
VERBOSE(DEBUG_SHCOPY, ("[pid=%T] bypassed copying %p is %T\n", mypid, ptr, obj));
- if (IN_LITERAL_PURGE_AREA(info,ptr))
+ if (in_literal_purge_area(ptr))
info->literal_size += size_object(obj);
goto pop_next;
}
@@ -1132,7 +1164,7 @@ Uint copy_shared_calculate(Eterm obj, erts_shcopy_t *info)
/* off heap pointers to boxes are copied verbatim */
if (erts_is_literal(obj,ptr)) {
VERBOSE(DEBUG_SHCOPY, ("[pid=%T] bypassed copying %p is %T\n", mypid, ptr, obj));
- if (IN_LITERAL_PURGE_AREA(info,ptr))
+ if (in_literal_purge_area(ptr))
info->literal_size += size_object(obj);
goto pop_next;
}
@@ -1298,6 +1330,8 @@ Uint copy_shared_perform(Eterm obj, Uint size, erts_shcopy_t *info,
Eterm* resp;
Eterm *hbot, *hend;
unsigned remaining;
+ Eterm *lit_purge_ptr = info->lit_purge_ptr;
+ Uint lit_purge_sz = info->lit_purge_sz;
#ifdef DEBUG
Eterm mypid = erts_get_current_pid();
Eterm saved_obj = obj;
@@ -1347,11 +1381,11 @@ Uint copy_shared_perform(Eterm obj, Uint size, erts_shcopy_t *info,
ptr = list_val(obj);
/* off heap list pointers are copied verbatim */
if (erts_is_literal(obj,ptr)) {
- if (!IN_LITERAL_PURGE_AREA(info,ptr)) {
+ if (!in_literal_purge_area(ptr)) {
*resp = obj;
} else {
Uint bsz = 0;
- *resp = copy_struct_x(obj, hbot - hp, &hp, off_heap, &bsz);
+ *resp = copy_struct_x(obj, hbot - hp, &hp, off_heap, &bsz, NULL); /* copy literal */
hbot -= bsz;
}
goto cleanup_next;
@@ -1415,11 +1449,11 @@ Uint copy_shared_perform(Eterm obj, Uint size, erts_shcopy_t *info,
ptr = boxed_val(obj);
/* off heap pointers to boxes are copied verbatim */
if (erts_is_literal(obj,ptr)) {
- if (!IN_LITERAL_PURGE_AREA(info,ptr)) {
+ if (!in_literal_purge_area(ptr)) {
*resp = obj;
} else {
Uint bsz = 0;
- *resp = copy_struct_x(obj, hbot - hp, &hp, off_heap, &bsz);
+ *resp = copy_struct_x(obj, hbot - hp, &hp, off_heap, &bsz, NULL); /* copy literal */
hbot -= bsz;
}
goto cleanup_next;
@@ -1923,7 +1957,7 @@ move_one_frag(Eterm** hpp, ErlHeapFragment* frag, ErlOffHeap* off_heap, int lite
if (is_header(val)) {
struct erl_off_heap_header* hdr = (struct erl_off_heap_header*)hp;
ASSERT(ptr + header_arity(val) < end);
- MOVE_BOXED(ptr, val, hp, &dummy_ref);
+ MOVE_BOXED(ptr, val, hp, &dummy_ref);
switch (val & _HEADER_SUBTAG_MASK) {
case REFC_BINARY_SUBTAG:
case FUN_SUBTAG:
diff --git a/erts/emulator/beam/erl_alloc.types b/erts/emulator/beam/erl_alloc.types
index 227fedfb69..971052b947 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;
@@ -367,6 +368,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 e5d3f38ce4..107c0c01d9 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 12ae086b31..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:
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..ef18a508a5 100644
--- a/erts/emulator/beam/erl_gc.c
+++ b/erts/emulator/beam/erl_gc.c
@@ -885,6 +885,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 +999,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 +1266,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,
@@ -2155,26 +2157,18 @@ copy_one_frag(Eterm** hpp, ErlOffHeap* off_heap,
*hp++ = val;
break;
case TAG_PRIMARY_LIST:
-#ifdef SHCOPY_SEND
if (erts_is_literal(val,list_val(val))) {
*hp++ = val;
} else {
*hp++ = offset_ptr(val, offs);
}
-#else
- *hp++ = offset_ptr(val, offs);
-#endif
break;
case TAG_PRIMARY_BOXED:
-#ifdef SHCOPY_SEND
if (erts_is_literal(val,boxed_val(val))) {
*hp++ = val;
} else {
*hp++ = offset_ptr(val, offs);
}
-#else
- *hp++ = offset_ptr(val, offs);
-#endif
break;
case TAG_PRIMARY_HEADER:
*hp++ = val;
@@ -2260,10 +2254,12 @@ move_msgq_to_heap(Process *p)
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);
+ /* Tokens does not use literal optimization */
ERL_MESSAGE_TOKEN(mp) = copy_struct(ERL_MESSAGE_TOKEN(mp),
bp->used_size,
- &factory.hp,
- factory.off_heap);
+ &factory.hp,
+ factory.off_heap);
+
erts_cleanup_offheap(&bp->off_heap);
}
ERL_MESSAGE_TERM(mp) = erts_decode_dist_ext(&factory,
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..e9f0586edd 100644
--- a/erts/emulator/beam/erl_message.c
+++ b/erts/emulator/beam/erl_message.c
@@ -696,6 +696,9 @@ erts_send_message(Process* sender,
erts_aint32_t receiver_state;
#ifdef SHCOPY_SEND
erts_shcopy_t info;
+#else
+ erts_literal_area_t litarea;
+ INITIALIZE_LITERAL_PURGE_AREA(litarea);
#endif
#ifdef USE_VM_PROBES
@@ -725,7 +728,7 @@ erts_send_message(Process* sender,
*/
if (have_seqtrace(stoken)) {
seq_trace_update_send(sender);
- seq_trace_output(stoken, message, SEQ_TRACE_SEND,
+ seq_trace_output(stoken, message, SEQ_TRACE_SEND,
receiver->common.id, sender);
seq_trace_size = 6; /* TUPLE5 */
}
@@ -741,7 +744,7 @@ erts_send_message(Process* sender,
INITIALIZE_SHCOPY(info);
msize = copy_shared_calculate(message, &info);
#else
- msize = size_object(message);
+ msize = size_object_litopt(message, &litarea);
#endif
mp = erts_alloc_message_heap_state(receiver,
&receiver_state,
@@ -760,7 +763,7 @@ erts_send_message(Process* sender,
DESTROY_SHCOPY(info);
#else
if (is_not_immed(message))
- message = copy_struct(message, msize, &hp, ohp);
+ message = copy_struct_litopt(message, msize, &hp, ohp, &litarea);
#endif
if (is_immed(stoken))
token = stoken;
@@ -796,7 +799,7 @@ erts_send_message(Process* sender,
INITIALIZE_SHCOPY(info);
msize = copy_shared_calculate(message, &info);
#else
- msize = size_object(message);
+ msize = size_object_litopt(message, &litarea);
#endif
mp = erts_alloc_message_heap_state(receiver,
&receiver_state,
@@ -810,7 +813,7 @@ erts_send_message(Process* sender,
DESTROY_SHCOPY(info);
#else
if (is_not_immed(message))
- message = copy_struct(message, msize, &hp, ohp);
+ message = copy_struct_litopt(message, msize, &hp, ohp, &litarea);
#endif
}
#ifdef USE_VM_PROBES
@@ -998,8 +1001,8 @@ erts_move_messages_off_heap(Process *c_p)
hp = hfrag->mem;
if (is_not_immed(ERL_MESSAGE_TERM(mp)))
ERL_MESSAGE_TERM(mp) = copy_struct(ERL_MESSAGE_TERM(mp),
- msg_sz, &hp,
- &hfrag->off_heap);
+ msg_sz, &hp,
+ &hfrag->off_heap);
if (is_not_immed(ERL_MESSAGE_TOKEN(mp)))
ERL_MESSAGE_TOKEN(mp) = copy_struct(ERL_MESSAGE_TOKEN(mp),
token_sz, &hp,
diff --git a/erts/emulator/beam/erl_nif.c b/erts/emulator/beam/erl_nif.c
index c6127a4967..f572577bde 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);
@@ -208,11 +210,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 +230,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 +242,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 +269,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));
@@ -339,11 +339,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
@@ -600,17 +637,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);
@@ -627,13 +679,16 @@ int enif_send(ErlNifEnv* env, const ErlNifPid* to_pid,
MBUF(&menv->phony_proc) = NULL;
}
} else {
- Uint sz = size_object(msg);
+ erts_literal_area_t litarea;
ErlOffHeap *ohp;
Eterm *hp;
+ Uint sz;
+ INITIALIZE_LITERAL_PURGE_AREA(litarea);
+ sz = size_object_litopt(msg, &litarea);
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);
@@ -649,15 +704,18 @@ int enif_send(ErlNifEnv* env, const ErlNifPid* to_pid,
ohp = &bp->off_heap;
}
}
- msg = copy_struct(msg, sz, &hp, ohp);
+ msg = copy_struct_litopt(msg, sz, &hp, ohp, &litarea);
}
ERL_MESSAGE_TERM(mp) = msg;
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 +748,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 +761,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 +786,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);
diff --git a/erts/emulator/beam/erl_process.c b/erts/emulator/beam/erl_process.c
index b5d8c5bc75..bcbb9b3115 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 = erts_proc_lookup(BIF_ARG_1);
+ Process *rp;
+ ErtsProcLocks rp_locks = 0;
+ ErlOffHeap *ohp;
+ ErtsMessage *mp;
+ Eterm *hp, msg;
+ Uint hsz, osz;
+ BIF_RETTYPE ret;
+
+ switch (st->type) {
+ case ERTS_PSTT_CPC:
+ rp = erts_dirty_process_code_checker;
+ ASSERT(fail_state & (ERTS_PSFLG_DIRTY_RUNNING
+ | ERTS_PSFLG_DIRTY_RUNNING_SYS));
+ if (c_p == rp) {
+ ERTS_BIF_PREP_RET(ret, am_dirty_execution);
+ return ret;
+ }
+ break;
+ default:
+ rp = NULL;
+ ERTS_INTERNAL_ERROR("Non-dispatchable system task");
+ break;
+ }
+
+ ERTS_BIF_PREP_RET(ret, am_ok);
+
+ /*
+ * Send message on the form: {Requester, Target, Operation}
+ */
+
+ ASSERT(is_immed(st->requester));
+ ASSERT(is_immed(target));
+ ASSERT(is_immed(prio));
+
+ osz = size_object(operation);
+ hsz = 5 /* 4-tuple */ + osz;
+
+ mp = erts_alloc_message_heap(rp, &rp_locks, hsz, &hp, &ohp);
+
+ msg = copy_struct(operation, osz, &hp, ohp);
+ msg = TUPLE4(hp, st->requester, target, prio, msg);
+
+ erts_queue_message(rp, rp_locks, mp, msg, st->requester);
+
+ if (rp_locks)
+ erts_smp_proc_unlock(rp, rp_locks);
+
+ return ret;
+}
+
+#endif
+
+static BIF_RETTYPE
+request_system_task(Process *c_p, Eterm requester, Eterm target,
+ Eterm priority, Eterm operation)
+{
+ BIF_RETTYPE ret;
+ Process *rp = erts_proc_lookup(target);
ErtsProcSysTask *st = NULL;
- erts_aint32_t prio;
+ erts_aint32_t prio, fail_state = ERTS_PSFLG_EXITING;
Eterm noproc_res, req_type;
- if (!rp && !is_internal_pid(BIF_ARG_1)) {
- if (!is_external_pid(BIF_ARG_1))
+ if (!rp && !is_internal_pid(target)) {
+ if (!is_external_pid(target))
goto badarg;
- if (external_pid_dist_entry(BIF_ARG_1) != erts_this_dist_entry)
+ if (external_pid_dist_entry(target) != erts_this_dist_entry)
goto badarg;
}
- switch (BIF_ARG_2) {
+ switch (priority) {
case am_max: prio = PRIORITY_MAX; break;
case am_high: prio = PRIORITY_HIGH; break;
case am_normal: prio = PRIORITY_NORMAL; break;
@@ -10494,11 +10633,11 @@ erts_internal_request_system_task_3(BIF_ALIST_3)
default: goto badarg;
}
- if (is_not_tuple(BIF_ARG_3))
+ if (is_not_tuple(operation))
goto badarg;
else {
int i;
- Eterm *tp = tuple_val(BIF_ARG_3);
+ Eterm *tp = tuple_val(operation);
Uint arity = arityval(*tp);
Eterm req_id;
Uint req_id_sz;
@@ -10536,7 +10675,7 @@ erts_internal_request_system_task_3(BIF_ALIST_3)
ERTS_INIT_OFF_HEAP(&st->off_heap);
hp = &st->heap[0];
- st->requester = BIF_P->common.id;
+ st->requester = requester;
st->reply_tag = req_type;
st->req_id_sz = req_id_sz;
st->req_id = req_id_sz == 0 ? req_id : copy_struct(req_id,
@@ -10570,26 +10709,85 @@ erts_internal_request_system_task_3(BIF_ALIST_3)
st->type = ERTS_PSTT_CPC;
if (!rp)
goto noproc;
+#ifdef ERTS_DIRTY_SCHEDULERS
+ /*
+ * If the process should start executing dirty
+ * code it is important that this task is
+ * aborted. Therefore this strict fail state...
+ */
+ fail_state |= (ERTS_PSFLG_DIRTY_RUNNING
+ | ERTS_PSFLG_DIRTY_RUNNING_SYS);
+#endif
break;
+#ifdef ERTS_NEW_PURGE_STRATEGY
+ case am_copy_literals:
+ if (st->arg[0] != am_true && st->arg[0] != am_false)
+ goto badarg;
+ st->type = ERTS_PSTT_CLA;
+ noproc_res = am_ok;
+ if (!rp)
+ goto noproc;
+ break;
+#endif
+
default:
goto badarg;
}
- if (!schedule_process_sys_task(rp, prio, st)) {
- noproc:
- notify_sys_task_executed(BIF_P, st, noproc_res);
+ if (!schedule_process_sys_task(rp, prio, st, &fail_state)) {
+ Eterm failure;
+ if (fail_state & ERTS_PSFLG_EXITING) {
+ noproc:
+ failure = noproc_res;
+ }
+#ifdef ERTS_DIRTY_SCHEDULERS
+ else if (fail_state & (ERTS_PSFLG_DIRTY_RUNNING
+ | ERTS_PSFLG_DIRTY_RUNNING_SYS)) {
+ ret = dispatch_system_task(c_p, fail_state, st,
+ target, priority, operation);
+ goto cleanup_return;
+ }
+#endif
+ else {
+ ERTS_INTERNAL_ERROR("Unknown failure schedule_process_sys_task()");
+ failure = am_internal_error;
+ }
+ notify_sys_task_executed(c_p, st, failure);
}
- BIF_RET(am_ok);
+ ERTS_BIF_PREP_RET(ret, am_ok);
+
+ return ret;
badarg:
+ ERTS_BIF_PREP_ERROR(ret, c_p, BADARG);
+
+#ifdef ERTS_DIRTY_SCHEDULERS
+cleanup_return:
+#endif
+
if (st) {
erts_cleanup_offheap(&st->off_heap);
erts_free(ERTS_ALC_T_PROC_SYS_TSK, st);
}
- BIF_ERROR(BIF_P, BADARG);
+
+ return ret;
+}
+
+BIF_RETTYPE
+erts_internal_request_system_task_3(BIF_ALIST_3)
+{
+ return request_system_task(BIF_P, BIF_P->common.id,
+ BIF_ARG_1, BIF_ARG_2, BIF_ARG_3);
+}
+
+BIF_RETTYPE
+erts_internal_request_system_task_4(BIF_ALIST_4)
+{
+ return request_system_task(BIF_P, BIF_ARG_1,
+ BIF_ARG_2, BIF_ARG_3, BIF_ARG_4);
}
static void
@@ -10598,7 +10796,7 @@ erts_schedule_generic_sys_task(Eterm pid, ErtsProcSysTaskType type)
Process *rp = erts_proc_lookup(pid);
if (rp) {
ErtsProcSysTask *st;
- erts_aint32_t state;
+ erts_aint32_t state, fail_state;
int i;
st = erts_alloc(ERTS_ALC_T_PROC_SYS_TSK,
@@ -10613,21 +10811,103 @@ erts_schedule_generic_sys_task(Eterm pid, ErtsProcSysTaskType type)
ERTS_INIT_OFF_HEAP(&st->off_heap);
state = erts_smp_atomic32_read_nob(&rp->state);
- if (!schedule_process_sys_task(rp, ERTS_PSFLGS_GET_USR_PRIO(state), st))
+ fail_state = ERTS_PSFLG_EXITING;
+
+ if (!schedule_process_sys_task(rp, ERTS_PSFLGS_GET_USR_PRIO(state),
+ st, &fail_state))
erts_free(ERTS_ALC_T_PROC_SYS_TSK, st);
}
}
+
void
erts_schedule_complete_off_heap_message_queue_change(Eterm pid)
{
erts_schedule_generic_sys_task(pid, ERTS_PSTT_COHMQ);
}
+#ifdef ERTS_DIRTY_SCHEDULERS
+
+static void
+flush_dirty_trace_messages(void *vpid)
+{
+ Process *proc;
+ Eterm pid;
+#ifdef ARCH_64
+ pid = (Eterm) vpid;
+#else
+ pid = *((Eterm *) vpid);
+ erts_free(ERTS_ALC_T_DIRTY_SL, vpid);
+#endif
+
+ proc = erts_proc_lookup(pid);
+ if (proc)
+ (void) erts_flush_trace_messages(proc, 0);
+}
+
+#endif /* ERTS_DIRTY_SCHEDULERS */
+
void
-erts_schedule_flush_trace_messages(Eterm pid)
+erts_schedule_flush_trace_messages(Process *proc, int force_on_proc)
{
+#ifdef ERTS_SMP
+ ErtsThrPrgrDelayHandle dhndl;
+#endif
+ Eterm pid = proc->common.id;
+
+#ifdef ERTS_DIRTY_SCHEDULERS
+ erts_aint32_t state;
+
+ if (!force_on_proc) {
+ state = erts_smp_atomic32_read_nob(&proc->state);
+ if (state & (ERTS_PSFLG_DIRTY_RUNNING
+ | ERTS_PSFLG_DIRTY_RUNNING_SYS)) {
+ goto sched_flush_dirty;
+ }
+ }
+#endif
+
+#ifdef ERTS_SMP
+ dhndl = erts_thr_progress_unmanaged_delay();
+#endif
+
erts_schedule_generic_sys_task(pid, ERTS_PSTT_FTMQ);
+
+#ifdef ERTS_SMP
+ erts_thr_progress_unmanaged_continue(dhndl);
+#endif
+
+#ifdef ERTS_DIRTY_SCHEDULERS
+ if (!force_on_proc) {
+ state = erts_smp_atomic32_read_mb(&proc->state);
+ if (state & (ERTS_PSFLG_DIRTY_RUNNING
+ | ERTS_PSFLG_DIRTY_RUNNING_SYS)) {
+ void *vargp;
+
+ sched_flush_dirty:
+ /*
+ * We traced 'proc' from another thread than
+ * it is executing on, and it is executing
+ * on a dirty scheduler. It might take a
+ * significant amount of time before it is
+ * scheduled out (where it gets opportunity
+ * to flush messages). We therefore schedule
+ * the flush on the first ordinary scheduler.
+ */
+
+#ifdef ARCH_64
+ vargp = (void *) pid;
+#else
+ {
+ Eterm *argp = erts_alloc(ERTS_ALC_T_DIRTY_SL, sizeof(Eterm));
+ *argp = pid;
+ vargp = (void *) argp;
+ }
+#endif
+ erts_schedule_misc_aux_work(1, flush_dirty_trace_messages, vargp);
+ }
+ }
+#endif
}
static void
@@ -11099,6 +11379,9 @@ erl_create_process(Process* parent, /* Parent of process (default group leader).
#ifdef SHCOPY_SPAWN
erts_shcopy_t info;
INITIALIZE_SHCOPY(info);
+#else
+ erts_literal_area_t litarea;
+ INITIALIZE_LITERAL_PURGE_AREA(litarea);
#endif
erts_smp_proc_lock(parent, ERTS_PROC_LOCKS_ALL_MINOR);
@@ -11157,7 +11440,7 @@ erl_create_process(Process* parent, /* Parent of process (default group leader).
#ifdef SHCOPY_SPAWN
arg_size = copy_shared_calculate(args, &info);
#else
- arg_size = size_object(args);
+ arg_size = size_object_litopt(args, &litarea);
#endif
heap_need = arg_size;
@@ -11181,7 +11464,7 @@ erl_create_process(Process* parent, /* Parent of process (default group leader).
}
p->schedule_count = 0;
ASSERT(p->min_heap_size == erts_next_heap_size(p->min_heap_size, 0));
-
+
p->u.initial[INITIAL_MOD] = mod;
p->u.initial[INITIAL_FUN] = func;
p->u.initial[INITIAL_ARI] = (Uint) arity;
@@ -11239,7 +11522,7 @@ erl_create_process(Process* parent, /* Parent of process (default group leader).
p->arg_reg[2] = copy_shared_perform(args, arg_size, &info, &p->htop, &p->off_heap);
DESTROY_SHCOPY(info);
#else
- p->arg_reg[2] = copy_struct(args, arg_size, &p->htop, &p->off_heap);
+ p->arg_reg[2] = copy_struct_litopt(args, arg_size, &p->htop, &p->off_heap, &litarea);
#endif
p->arity = 3;
@@ -12276,7 +12559,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..94c13bb948 100644
--- a/erts/emulator/beam/erl_process.h
+++ b/erts/emulator/beam/erl_process.h
@@ -1767,7 +1767,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 +2473,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..26d71f573f 100644
--- a/erts/emulator/beam/erl_thr_progress.c
+++ b/erts/emulator/beam/erl_thr_progress.c
@@ -969,8 +969,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 +997,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..f4d92564c1 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)) {
@@ -3141,9 +3164,10 @@ erts_tracer_update(ErtsTracer *tracer, const ErtsTracer new_tracer)
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 bf2e50e52f..d7dd6371b7 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,30 @@ 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 ErtsLiteralArea *erts_copy_literal_area;
+#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 copy_literals_t erts_clrange;
+extern Process *erts_code_purger;
/* beam_load.c */
typedef struct {
@@ -1085,19 +1101,26 @@ typedef struct {
Eterm* shtable_start;
ErtsAlcType_t shtable_alloc_type;
Uint literal_size;
- Eterm *range_ptr;
- Uint range_sz;
+ Eterm *lit_purge_ptr;
+ Uint lit_purge_sz;
} erts_shcopy_t;
-#define INITIALIZE_SHCOPY(info) \
-do { \
- 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; \
-} while(0)
+#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; \
+ if (larea__) { \
+ info.lit_purge_ptr = &larea__->start[0]; \
+ info.lit_purge_sz = larea__->end - info.lit_purge_ptr; \
+ } \
+ else { \
+ info.lit_purge_ptr = NULL; \
+ info.lit_purge_sz = 0; \
+ } \
+ } while(0)
#define DESTROY_SHCOPY(info) \
do { \
@@ -1113,18 +1136,42 @@ do { \
} while(0)
/* copy.c */
+typedef struct {
+ Eterm *lit_purge_ptr;
+ Uint lit_purge_sz;
+} erts_literal_area_t;
+
+#define INITIALIZE_LITERAL_PURGE_AREA(Area) \
+ do { \
+ ErtsLiteralArea *larea__ = erts_copy_literal_area; \
+ if (larea__) { \
+ (Area).lit_purge_ptr = &larea__->start[0]; \
+ (Area).lit_purge_sz = larea__->end - (Area).lit_purge_ptr; \
+ } \
+ else { \
+ (Area).lit_purge_ptr = NULL; \
+ (Area).lit_purge_sz = 0; \
+ } \
+ } while(0)
+
Eterm copy_object_x(Eterm, Process*, Uint);
#define copy_object(Term, Proc) copy_object_x(Term,Proc,0)
-Uint size_object(Eterm);
+Uint size_object_x(Eterm, erts_literal_area_t*);
+#define size_object(Term) size_object_x(Term,NULL)
+#define size_object_litopt(Term,LitArea) size_object_x(Term,LitArea)
+
Uint copy_shared_calculate(Eterm, erts_shcopy_t*);
Eterm copy_shared_perform(Eterm, Uint, erts_shcopy_t*, Eterm**, ErlOffHeap*);
Uint size_shared(Eterm);
-Eterm copy_struct_x(Eterm, Uint, Eterm**, ErlOffHeap*, Uint* bsz);
+Eterm copy_struct_x(Eterm, Uint, Eterm**, ErlOffHeap*, Uint*, erts_literal_area_t*);
#define copy_struct(Obj,Sz,HPP,OH) \
- copy_struct_x(Obj,Sz,HPP,OH,NULL)
+ copy_struct_x(Obj,Sz,HPP,OH,NULL,NULL)
+#define copy_struct_litopt(Obj,Sz,HPP,OH,LitArea) \
+ copy_struct_x(Obj,Sz,HPP,OH,NULL,LitArea)
+
Eterm copy_shallow(Eterm*, Uint, Eterm**, ErlOffHeap*);
void erts_move_multi_frags(Eterm** hpp, ErlOffHeap*, ErlHeapFragment* first,
diff --git a/erts/emulator/drivers/common/inet_drv.c b/erts/emulator/drivers/common/inet_drv.c
index 5ce0e1de9e..254d3baeb1 100644
--- a/erts/emulator/drivers/common/inet_drv.c
+++ b/erts/emulator/drivers/common/inet_drv.c
@@ -11407,6 +11407,7 @@ static void packet_inet_command(ErlDrvData e, char* buf, ErlDrvSizeT len)
VALGRIND_MAKE_MEM_DEFINED(mhdr.msg_control, mhdr.msg_controllen); /*suppress "uninitialised bytes"*/
mhdr.msg_flags = 0; /* Not used with "sendmsg" */
+ inet_output_count(desc, data_len);
/* Now do the actual sending. NB: "flags" in "sendmsg" itself are NOT
used: */
code = sock_sendmsg(desc->s, &mhdr, 0);
diff --git a/erts/emulator/drivers/common/zlib_drv.c b/erts/emulator/drivers/common/zlib_drv.c
index 440ba956d8..066cf87c9d 100644
--- a/erts/emulator/drivers/common/zlib_drv.c
+++ b/erts/emulator/drivers/common/zlib_drv.c
@@ -44,30 +44,34 @@
#define INFLATE_INIT 8
#define INFLATE_INIT2 9
#define INFLATE_SETDICT 10
-#define INFLATE_SYNC 11
-#define INFLATE_RESET 12
-#define INFLATE_END 13
-#define INFLATE 14
+#define INFLATE_GETDICT 11
+#define INFLATE_SYNC 12
+#define INFLATE_RESET 13
+#define INFLATE_END 14
+#define INFLATE 15
-#define CRC32_0 15
-#define CRC32_1 16
-#define CRC32_2 17
+#define CRC32_0 16
+#define CRC32_1 17
+#define CRC32_2 18
-#define SET_BUFSZ 18
-#define GET_BUFSZ 19
-#define GET_QSIZE 20
+#define SET_BUFSZ 19
+#define GET_BUFSZ 20
+#define GET_QSIZE 21
-#define ADLER32_1 21
-#define ADLER32_2 22
+#define ADLER32_1 22
+#define ADLER32_2 23
-#define CRC32_COMBINE 23
-#define ADLER32_COMBINE 24
+#define CRC32_COMBINE 24
+#define ADLER32_COMBINE 25
-#define INFLATE_CHUNK 25
+#define INFLATE_CHUNK 26
#define DEFAULT_BUFSZ 4000
+/* According to zlib documentation, it can never exceed this */
+#define INFL_DICT_SZ 32768
+
/* This flag is used in the same places, where zlib return codes
* (Z_OK, Z_STREAM_END, Z_NEED_DICT) are. So, we need to set it to
* relatively large value to avoid possible value clashes in future.
@@ -248,6 +252,20 @@ static int zlib_output(ZLibData* d)
return zlib_output_init(d);
}
+#ifdef HAVE_ZLIB_INFLATEGETDICTIONARY
+static int zlib_inflate_get_dictionary(ZLibData* d)
+{
+ ErlDrvBinary* dbin = driver_alloc_binary(INFL_DICT_SZ);
+ uInt dlen = 0;
+ int res = inflateGetDictionary(&d->s, (unsigned char*)dbin->orig_bytes, &dlen);
+ if ((res == Z_OK) && (driver_output_binary(d->port, NULL, 0, dbin, 0, dlen) < 0)) {
+ res = Z_ERRNO;
+ }
+ driver_free_binary(dbin);
+ return res;
+}
+#endif
+
static int zlib_inflate(ZLibData* d, int flush)
{
int res = Z_OK;
@@ -586,6 +604,16 @@ static ErlDrvSSizeT zlib_ctl(ErlDrvData drv_data, unsigned int command, char *bu
res = inflateSetDictionary(&d->s, (unsigned char*)buf, len);
return zlib_return(res, rbuf, rlen);
+ case INFLATE_GETDICT:
+#ifdef HAVE_ZLIB_INFLATEGETDICTIONARY
+ if (d->state != ST_INFLATE) goto badarg;
+ res = zlib_inflate_get_dictionary(d);
+ return zlib_return(res, rbuf, rlen);
+#else
+ errno = ENOTSUP;
+ return zlib_return(Z_ERRNO, rbuf, rlen);
+#endif
+
case INFLATE_SYNC:
if (d->state != ST_INFLATE) goto badarg;
if (len != 0) goto badarg;
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/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/lib/hipe/util/hipe_vectors.hrl b/erts/emulator/test/code_SUITE_data/my_code_test2.erl
index d4556e9dc4..57973535d4 100644
--- a/lib/hipe/util/hipe_vectors.hrl
+++ b/erts/emulator/test/code_SUITE_data/my_code_test2.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2008-2016. All Rights Reserved.
+%% 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.
@@ -17,13 +17,16 @@
%%
%% %CopyrightEnd%
%%
-%%-define(USE_TUPLES, true).
--define(USE_GBTREES, true).
--ifdef(USE_TUPLES).
--type hipe_vector() :: tuple().
--endif.
+-module(my_code_test2).
--ifdef(USE_GBTREES).
--type hipe_vector() :: gb_trees:tree().
--endif.
+-export([make_fun/1, make_fun2/0]).
+
+make_fun(A) ->
+ fun(X) -> A + X end.
+
+make_fun2() ->
+ fun (F1,F2) ->
+ F1(),
+ F2()
+ end.
diff --git a/erts/emulator/test/dirty_nif_SUITE.erl b/erts/emulator/test/dirty_nif_SUITE.erl
index 83b098a704..658bdc41b6 100644
--- a/erts/emulator/test/dirty_nif_SUITE.erl
+++ b/erts/emulator/test/dirty_nif_SUITE.erl
@@ -33,7 +33,8 @@
dirty_nif_exception/1, call_dirty_nif_exception/1,
dirty_scheduler_exit/1, dirty_call_while_terminated/1,
dirty_heap_access/1, dirty_process_info/1,
- dirty_process_register/1, dirty_process_trace/1]).
+ dirty_process_register/1, dirty_process_trace/1,
+ code_purge/1, dirty_nif_send_traced/1]).
-define(nif_stub,nif_stub_error(?LINE)).
@@ -48,7 +49,9 @@ all() ->
dirty_heap_access,
dirty_process_info,
dirty_process_register,
- dirty_process_trace].
+ dirty_process_trace,
+ code_purge,
+ dirty_nif_send_traced].
init_per_suite(Config) ->
try erlang:system_info(dirty_cpu_schedulers) of
@@ -145,9 +148,9 @@ dirty_scheduler_exit(Config) when is_list(Config) ->
[ok] = mcall(Node,
[fun() ->
ok = erlang:load_nif(NifLib, []),
- Start = erlang:monotonic_time(milli_seconds),
+ Start = erlang:monotonic_time(millisecond),
ok = test_dirty_scheduler_exit(),
- End = erlang:monotonic_time(milli_seconds),
+ End = erlang:monotonic_time(millisecond),
io:format("Time=~p ms~n", [End-Start]),
ok
end]),
@@ -230,7 +233,11 @@ dirty_call_while_terminated(Config) when is_list(Config) ->
process_info(self(),
binary))),
process_flag(trap_exit, OT),
- ok.
+ try
+ blipp:blupp(Bin)
+ catch
+ _ : _ -> ok
+ end.
dirty_heap_access(Config) when is_list(Config) ->
{ok, Node} = start_node(Config),
@@ -349,6 +356,103 @@ dirty_process_trace(Config) when is_list(Config) ->
ok
end).
+dirty_code_test_code() ->
+ "
+-module(dirty_code_test).
+
+-export([func/1]).
+
+func(Fun) ->
+ Fun(),
+ blipp:blapp().
+
+".
+
+code_purge(Config) when is_list(Config) ->
+ Path = ?config(data_dir, Config),
+ File = filename:join(Path, "dirty_code_test.erl"),
+ ok = file:write_file(File, dirty_code_test_code()),
+ {ok, dirty_code_test, Bin} = compile:file(File, [binary]),
+ {module, dirty_code_test} = erlang:load_module(dirty_code_test, Bin),
+ Start = erlang:monotonic_time(),
+ {Pid1, Mon1} = spawn_monitor(fun () ->
+ dirty_code_test:func(fun () ->
+ %% Sleep for 6 seconds
+ %% in dirty nif...
+ dirty_sleeper()
+ end)
+ end),
+ {module, dirty_code_test} = erlang:load_module(dirty_code_test, Bin),
+ {Pid2, Mon2} = spawn_monitor(fun () ->
+ dirty_code_test:func(fun () ->
+ %% Sleep for 6 seconds
+ %% in dirty nif...
+ dirty_sleeper()
+ end)
+ end),
+ receive
+ {'DOWN', Mon1, process, Pid1, _} ->
+ ct:fail(premature_death)
+ after 100 ->
+ ok
+ end,
+ true = erlang:purge_module(dirty_code_test),
+ receive
+ {'DOWN', Mon1, process, Pid1, Reason1} ->
+ killed = Reason1
+ end,
+ receive
+ {'DOWN', Mon2, process, Pid2, _} ->
+ ct:fail(premature_death)
+ after 100 ->
+ ok
+ end,
+ true = erlang:delete_module(dirty_code_test),
+ receive
+ {'DOWN', Mon2, process, Pid2, _} ->
+ ct:fail(premature_death)
+ after 100 ->
+ ok
+ end,
+ true = erlang:purge_module(dirty_code_test),
+ receive
+ {'DOWN', Mon2, process, Pid2, Reason2} ->
+ killed = Reason2
+ end,
+ End = erlang:monotonic_time(),
+ Time = erlang:convert_time_unit(End-Start, native, milli_seconds),
+ io:format("Time=~p~n", [Time]),
+ true = Time =< 1000,
+ ok.
+
+dirty_nif_send_traced(Config) when is_list(Config) ->
+ Parent = self(),
+ Rcvr = spawn_link(fun() ->
+ Self = self(),
+ receive {ok, Self} -> ok end,
+ Parent ! {Self, received}
+ end),
+ Sndr = spawn_link(fun () ->
+ receive {Parent, go} -> ok end,
+ {ok, Rcvr} = send_wait_from_dirty_nif(Rcvr),
+ Parent ! {self(), sent}
+ end),
+ 1 = erlang:trace(Sndr, true, [send]),
+ Start = erlang:monotonic_time(),
+ Sndr ! {self(), go},
+ receive {trace, Sndr, send, {ok, Rcvr}, Rcvr} -> ok end,
+ receive {Rcvr, received} -> ok end,
+ End1 = erlang:monotonic_time(),
+ Time1 = erlang:convert_time_unit(End1-Start, native, 1000),
+ io:format("Time1: ~p milliseconds~n", [Time1]),
+ true = Time1 < 500,
+ receive {Sndr, sent} -> ok end,
+ End2 = erlang:monotonic_time(),
+ Time2 = erlang:convert_time_unit(End2-Start, native, 1000),
+ io:format("Time2: ~p milliseconds~n", [Time2]),
+ true = Time2 >= 1900,
+ ok.
+
%%
%% Internal...
%%
@@ -400,7 +504,7 @@ start_node(Config, Args) when is_list(Config) ->
++ "-"
++ atom_to_list(proplists:get_value(testcase, Config))
++ "-"
- ++ integer_to_list(erlang:system_time(seconds))
+ ++ integer_to_list(erlang:system_time(second))
++ "-"
++ integer_to_list(erlang:unique_integer([positive]))),
test_server:start_node(Name, slave, [{args, "-pa "++Pa++" "++Args}]).
@@ -431,6 +535,7 @@ mcall(Node, Funs) ->
lib_loaded() -> false.
call_dirty_nif(_,_,_) -> ?nif_stub.
send_from_dirty_nif(_) -> ?nif_stub.
+send_wait_from_dirty_nif(_) -> ?nif_stub.
call_dirty_nif_exception(_) -> ?nif_stub.
call_dirty_nif_zero_args() -> ?nif_stub.
dirty_call_while_terminated_nif(_) -> ?nif_stub.
diff --git a/erts/emulator/test/dirty_nif_SUITE_data/dirty_nif_SUITE.c b/erts/emulator/test/dirty_nif_SUITE_data/dirty_nif_SUITE.c
index d92933a096..a0019e5d95 100644
--- a/erts/emulator/test/dirty_nif_SUITE_data/dirty_nif_SUITE.c
+++ b/erts/emulator/test/dirty_nif_SUITE_data/dirty_nif_SUITE.c
@@ -100,6 +100,32 @@ static ERL_NIF_TERM send_from_dirty_nif(ErlNifEnv* env, int argc, const ERL_NIF_
return result;
}
+static ERL_NIF_TERM send_wait_from_dirty_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
+{
+ ERL_NIF_TERM result;
+ ErlNifPid pid;
+ ErlNifEnv* menv;
+ int res;
+
+ if (!enif_get_local_pid(env, argv[0], &pid))
+ return enif_make_badarg(env);
+ result = enif_make_tuple2(env, enif_make_atom(env, "ok"), enif_make_pid(env, &pid));
+ menv = enif_alloc_env();
+ res = enif_send(env, &pid, menv, result);
+ enif_free_env(menv);
+
+#ifdef __WIN32__
+ Sleep(2000);
+#else
+ sleep(2);
+#endif
+
+ if (!res)
+ return enif_make_badarg(env);
+ else
+ return result;
+}
+
static ERL_NIF_TERM call_dirty_nif_exception(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
switch (argc) {
@@ -237,6 +263,7 @@ static ErlNifFunc nif_funcs[] =
{"lib_loaded", 0, lib_loaded},
{"call_dirty_nif", 3, call_dirty_nif},
{"send_from_dirty_nif", 1, send_from_dirty_nif, ERL_NIF_DIRTY_JOB_CPU_BOUND},
+ {"send_wait_from_dirty_nif", 1, send_wait_from_dirty_nif, ERL_NIF_DIRTY_JOB_IO_BOUND},
{"call_dirty_nif_exception", 1, call_dirty_nif_exception, ERL_NIF_DIRTY_JOB_IO_BOUND},
{"call_dirty_nif_zero_args", 0, call_dirty_nif_zero_args, ERL_NIF_DIRTY_JOB_CPU_BOUND},
{"dirty_sleeper", 0, dirty_sleeper, ERL_NIF_DIRTY_JOB_IO_BOUND},
diff --git a/erts/emulator/test/distribution_SUITE.erl b/erts/emulator/test/distribution_SUITE.erl
index c6939a695d..6994bfef83 100644
--- a/erts/emulator/test/distribution_SUITE.erl
+++ b/erts/emulator/test/distribution_SUITE.erl
@@ -43,7 +43,7 @@
lost_exit/1, link_to_dead/1, link_to_dead_new_node/1,
applied_monitor_node/1, ref_port_roundtrip/1, nil_roundtrip/1,
trap_bif_1/1, trap_bif_2/1, trap_bif_3/1,
- stop_dist/1,
+ stop_dist/1,
dist_auto_connect_never/1, dist_auto_connect_once/1,
dist_parallel_send/1,
atom_roundtrip/1,
@@ -66,13 +66,13 @@
sendersender/4, sendersender2/4]).
%% epmd_module exports
--export([start_link/0, register_node/2, port_please/2]).
+-export([start_link/0, register_node/2, register_node/3, port_please/2]).
suite() ->
[{ct_hooks,[ts_install_cth]},
{timetrap, {minutes, 4}}].
-all() ->
+all() ->
[ping, {group, bulk_send}, {group, local_send},
link_to_busy, exit_to_busy, lost_exit, link_to_dead,
link_to_dead_new_node, applied_monitor_node,
@@ -83,7 +83,7 @@ all() ->
bad_dist_structure, {group, bad_dist_ext},
start_epmd_false, epmd_module].
-groups() ->
+groups() ->
[{bulk_send, [], [bulk_send_small, bulk_send_big, bulk_send_bigbig]},
{local_send, [],
[local_send_small, local_send_big, local_send_legal]},
@@ -844,59 +844,50 @@ dist_auto_connect_once(Config) when is_list(Config) ->
%% Result is sent here through relay node.
dist_auto_connect_never(Config) when is_list(Config) ->
Self = self(),
- {ok, RelayNode} =
- start_node(dist_auto_connect_relay),
- spawn(RelayNode,
+ {ok, RelayNode} = start_node(dist_auto_connect_relay),
+ spawn(RelayNode,
fun() ->
register(dist_auto_connect_relay, self()),
- dist_auto_connect_relay(Self)
+ dist_auto_connect_relay(Self)
end),
{ok, Handle} = dist_auto_connect_start(dist_auto_connect, never),
- Result =
- receive
- {do_dist_auto_connect, ok} ->
- ok;
- {do_dist_auto_connect, Error} ->
- {error, Error};
- Other ->
- {error, Other}
- after 32000 ->
- timeout
- end,
+ Result = receive
+ {do_dist_auto_connect, ok} ->
+ ok;
+ {do_dist_auto_connect, Error} ->
+ {error, Error};
+ Other ->
+ {error, Other}
+ after 32000 ->
+ timeout
+ end,
stop_node(RelayNode),
- Stopped = dist_auto_connect_stop(Handle),
- Junk =
- receive
- {do_dist_auto_connect, _} = J ->
- J
- after 0 ->
- ok
- end,
+ Stopped = dist_auto_connect_stop(Handle),
+ Junk = receive
+ {do_dist_auto_connect, _} = J -> J
+ after 0 -> ok
+ end,
{ok, ok, ok} = {Result, Stopped, Junk},
ok.
do_dist_auto_connect([never]) ->
Node = list_to_atom("dist_auto_connect_relay@" ++ hostname()),
- io:format("~p:do_dist_auto_connect([false]) Node=~p~n",
- [?MODULE, Node]),
+ io:format("~p:do_dist_auto_connect([false]) Node=~p~n", [?MODULE, Node]),
Ping = net_adm:ping(Node),
- io:format("~p:do_dist_auto_connect([false]) Ping=~p~n",
- [?MODULE, Ping]),
+ io:format("~p:do_dist_auto_connect([false]) Ping=~p~n", [?MODULE, Ping]),
Result = case Ping of
pang -> ok;
_ -> {error, Ping}
end,
- io:format("~p:do_dist_auto_connect([false]) Result=~p~n",
- [?MODULE, Result]),
+ io:format("~p:do_dist_auto_connect([false]) Result=~p~n", [?MODULE, Result]),
net_kernel:connect_node(Node),
catch {dist_auto_connect_relay, Node} ! {do_dist_auto_connect, Result};
% receive after 1000 -> ok end,
% halt();
do_dist_auto_connect(Arg) ->
- io:format("~p:do_dist_auto_connect(~p)~n",
- [?MODULE, Arg]),
+ io:format("~p:do_dist_auto_connect(~p)~n", [?MODULE, Arg]),
receive after 10000 -> ok end,
halt().
@@ -912,11 +903,11 @@ dist_auto_connect_start(Name, Value) when is_list(Name), is_atom(Value) ->
[%"xterm -e ",
atom_to_list(lib:progname()),
% " -noinput ",
- " -detached ",
+ " -detached ",
long_or_short(), " ", Name,
" -setcookie ", Cookie,
" -pa ", ModuleDir,
- " -s ", atom_to_list(?MODULE),
+ " -s ", atom_to_list(?MODULE),
" do_dist_auto_connect ", ValueStr,
" -kernel dist_auto_connect ", ValueStr]),
io:format("~p:dist_auto_connect_start() cmd: ~p~n", [?MODULE, Cmd]),
@@ -947,7 +938,7 @@ dist_auto_connect_stop(Port, Node, Pid, N) when is_integer(N) ->
end.
-dist_auto_connect_relay(Parent) ->
+dist_auto_connect_relay(Parent) ->
receive X ->
catch Parent ! X
end,
@@ -1321,7 +1312,7 @@ get_conflicting_unicode_atoms(CIX, N) ->
start_monitor(Offender,P) ->
Parent = self(),
Q = spawn(Offender,
- fun () ->
+ fun () ->
Ref = erlang:monitor(process,P),
Parent ! {self(),ref,Ref},
receive
@@ -1458,8 +1449,8 @@ bad_dist_structure(Config) when is_list(Config) ->
pong = rpc:call(Victim, net_adm, ping, [Offender]),
P ! two,
P ! check_msgs,
- receive
- {P, messages_checked} -> ok
+ receive
+ {P, messages_checked} -> ok
after 5000 ->
exit(victim_is_dead)
end,
@@ -1765,7 +1756,7 @@ send_bad_structure(Offender,Victim,Bad,WhereToPutSelf,PayLoad) ->
pong = net_adm:ping(Node),
DPrt = dport(Node),
Bad1 = case WhereToPutSelf of
- 0 ->
+ 0 ->
Bad;
N when N > 0 ->
setelement(N,Bad,self())
@@ -1779,8 +1770,8 @@ send_bad_structure(Offender,Victim,Bad,WhereToPutSelf,PayLoad) ->
port_command(DPrt, DData),
Parent ! {DData,Done}
end),
- receive
- {WhatSent,Done} ->
+ receive
+ {WhatSent,Done} ->
io:format("Offender sent ~p~n",[WhatSent]),
ok
after 5000 ->
@@ -1887,7 +1878,7 @@ dmsg_fake_hdr2() ->
1, size(A2), A2,
2, size(A3), A3].
-dmsg_ext(Term) ->
+dmsg_ext(Term) ->
<<131, Res/binary>> = term_to_binary(Term),
Res.
@@ -1934,7 +1925,9 @@ epmd_module(Config) when is_list(Config) ->
start_link() ->
ignore.
-register_node(_Name, Port) ->
+register_node(Name, Port) ->
+ register_node(Name, Port, inet_tcp).
+register_node(_Name, Port, _Driver) ->
%% Save the port number we're listening on.
application:set_env(kernel, dist_listen_port, Port),
Creation = rand:uniform(3),
@@ -1957,7 +1950,7 @@ port_please(_Name, _Ip) ->
%%% Utilities
timestamp() ->
- erlang:monotonic_time(milli_seconds).
+ erlang:monotonic_time(millisecond).
start_node(X) ->
start_node(X, [], []).
@@ -1972,7 +1965,7 @@ start_node(Name, Args, Rel) when is_atom(Name), is_list(Rel) ->
[] -> [];
_ -> [{erl,[{release,Rel}]}]
end,
- test_server:start_node(Name, slave,
+ test_server:start_node(Name, slave,
[{args,
Args++" -setcookie "++Cookie++" -pa \""++Pa++"\""}
| RelArg]);
@@ -1981,7 +1974,7 @@ start_node(Config, Args, Rel) when is_list(Config), is_list(Rel) ->
++ "-"
++ atom_to_list(proplists:get_value(testcase, Config))
++ "-"
- ++ integer_to_list(erlang:system_time(seconds))
+ ++ integer_to_list(erlang:system_time(second))
++ "-"
++ integer_to_list(erlang:unique_integer([positive])))),
start_node(Name, Args, Rel).
@@ -2040,17 +2033,15 @@ inet_rpc_server_loop(Sock) ->
start_relay_node(Node, Args) ->
Pa = filename:dirname(code:which(?MODULE)),
Cookie = "NOT"++atom_to_list(erlang:get_cookie()),
- {ok, LSock} = gen_tcp:listen(0, [binary, {packet, 4},
- {active, false}]),
+ {ok, LSock} = gen_tcp:listen(0, [binary, {packet, 4}, {active, false}]),
{ok, Port} = inet:port(LSock),
{ok, Host} = inet:gethostname(),
RunArg = "-run " ++ atom_to_list(?MODULE) ++ " inet_rpc_server " ++
Host ++ " " ++ integer_to_list(Port),
- {ok, NN} =
- test_server:start_node(Node, peer,
- [{args, Args ++
- " -setcookie "++Cookie++" -pa "++Pa++" "++
- RunArg}]),
+ {ok, NN} = test_server:start_node(Node, peer,
+ [{args, Args ++
+ " -setcookie "++Cookie++" -pa "++Pa++" "++
+ RunArg}]),
[N,H] = string:tokens(atom_to_list(NN),"@"),
{ok, Sock} = gen_tcp:accept(LSock),
pang = net_adm:ping(NN),
@@ -2066,7 +2057,7 @@ wait_dead(N,H,0) ->
wait_dead(N,H,X) ->
case erl_epmd:port_please(N,H) of
{port,_,_} ->
- receive
+ receive
after 1000 ->
ok
end,
@@ -2100,7 +2091,7 @@ node_monitor(Master) ->
Master ! {nodeup, node(), Node}
end,
Nodes0),
- io:format("~p ~p: ~p~n", [node(), erlang:system_time(micro_seconds), Nodes0]),
+ io:format("~p ~p: ~p~n", [node(), erlang:system_time(microsecond), Nodes0]),
node_monitor_loop(Master);
false ->
net_kernel:monitor_nodes(false, Opts),
@@ -2121,7 +2112,7 @@ node_monitor_loop(Master) ->
receive
{nodeup, Node, _InfoList} = Msg ->
Master ! {nodeup, node(), Node},
- io:format("~p ~p: ~p~n", [node(), erlang:system_time(micro_seconds), Msg]),
+ io:format("~p ~p: ~p~n", [node(), erlang:system_time(microsecond), Msg]),
node_monitor_loop(Master);
{nodedown, Node, InfoList} = Msg ->
Reason = case lists:keysearch(nodedown_reason, 1, InfoList) of
@@ -2129,7 +2120,7 @@ node_monitor_loop(Master) ->
_ -> undefined
end,
Master ! {nodedown, node(), Node, Reason},
- io:format("~p ~p: ~p~n", [node(), erlang:system_time(micro_seconds), Msg]),
+ io:format("~p ~p: ~p~n", [node(), erlang:system_time(microsecond), Msg]),
node_monitor_loop(Master)
end.
diff --git a/erts/emulator/test/driver_SUITE.erl b/erts/emulator/test/driver_SUITE.erl
index f134a197aa..1df72193a6 100644
--- a/erts/emulator/test/driver_SUITE.erl
+++ b/erts/emulator/test/driver_SUITE.erl
@@ -114,7 +114,7 @@
-define(MAX_DATA_SIZE, 16384).
% This is the allowed delay when testing the driver timer functionality
--define(delay, 100).
+-define(delay, 400).
-define(heap_binary_size, 64).
@@ -401,7 +401,7 @@ try_timeouts(Port, Timeout) ->
true ->
try_timeouts(Port, Timeout div 2)
end
- after Timeout + ?delay ->
+ after Timeout + 100*?delay ->
ct:fail("driver failed to timeout")
end.
@@ -437,7 +437,7 @@ try_cancel(Port, Timeout) ->
Timeout == 0 -> ok;
true -> try_cancel(Port, Timeout div 2)
end
- after ?delay ->
+ after 100*?delay ->
ct:fail("No message from driver")
end
end.
@@ -505,7 +505,7 @@ try_change_timer(Port, Timeout) ->
true ->
try_timeouts(Port, Timeout div 2)
end
- after Timeout + ?delay ->
+ after Timeout + 100*?delay ->
ct:fail("driver failed to timeout")
end.
@@ -2427,7 +2427,7 @@ erl_millisecs() ->
erl_millisecs(erlang:monotonic_time()).
erl_millisecs(MonotonicTime) ->
- (1000*MonotonicTime)/erlang:convert_time_unit(1,seconds,native).
+ (1000*MonotonicTime)/erlang:convert_time_unit(1,second,native).
%% Start/stop drivers.
start_driver(Config, Name, Binary) ->
@@ -2481,7 +2481,7 @@ start_node(Config) when is_list(Config) ->
++ "-"
++ atom_to_list(proplists:get_value(testcase, Config))
++ "-"
- ++ integer_to_list(erlang:system_time(seconds))
+ ++ integer_to_list(erlang:system_time(second))
++ "-"
++ integer_to_list(erlang:unique_integer([positive]))),
test_server:start_node(Name, slave, [{args, "-pa "++Pa}]).
diff --git a/erts/emulator/test/erl_link_SUITE.erl b/erts/emulator/test/erl_link_SUITE.erl
index 93d2065ba3..89e1aefb50 100644
--- a/erts/emulator/test/erl_link_SUITE.erl
+++ b/erts/emulator/test/erl_link_SUITE.erl
@@ -1011,7 +1011,7 @@ get_names(N, T, Acc) ->
++ "-"
++ atom_to_list(T)
++ "-"
- ++ integer_to_list(erlang:system_time(seconds))
+ ++ integer_to_list(erlang:system_time(second))
++ "-"
++ integer_to_list(erlang:unique_integer([positive]))) | Acc]).
diff --git a/erts/emulator/test/estone_SUITE.erl b/erts/emulator/test/estone_SUITE.erl
index 1180a45585..3ce849b88e 100644
--- a/erts/emulator/test/estone_SUITE.erl
+++ b/erts/emulator/test/estone_SUITE.erl
@@ -364,7 +364,7 @@ monotonic_time() ->
try erlang:monotonic_time() catch error:undef -> erlang:now() end.
subtr(Before, After) when is_integer(Before), is_integer(After) ->
- erlang:convert_time_unit(After-Before, native, micro_seconds);
+ erlang:convert_time_unit(After-Before, native, microsecond);
subtr({_,_,_}=Before, {_,_,_}=After) ->
timer:now_diff(After, Before).
diff --git a/erts/emulator/test/float_SUITE.erl b/erts/emulator/test/float_SUITE.erl
index e85addae3a..36b1f9179f 100644
--- a/erts/emulator/test/float_SUITE.erl
+++ b/erts/emulator/test/float_SUITE.erl
@@ -276,7 +276,7 @@ start_node(Config) when is_list(Config) ->
++ "-"
++ atom_to_list(proplists:get_value(testcase, Config))
++ "-"
- ++ integer_to_list(erlang:system_time(seconds))
+ ++ integer_to_list(erlang:system_time(second))
++ "-"
++ integer_to_list(erlang:unique_integer([positive]))),
test_server:start_node(Name, slave, [{args, "-pa "++Pa}]).
diff --git a/erts/emulator/test/hipe_SUITE.erl b/erts/emulator/test/hipe_SUITE.erl
new file mode 100644
index 0000000000..3e682b8d88
--- /dev/null
+++ b/erts/emulator/test/hipe_SUITE.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(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),
+
+ %% 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/message_queue_data_SUITE.erl b/erts/emulator/test/message_queue_data_SUITE.erl
index 44e77dfad0..e084b9482d 100644
--- a/erts/emulator/test/message_queue_data_SUITE.erl
+++ b/erts/emulator/test/message_queue_data_SUITE.erl
@@ -200,7 +200,7 @@ start_node(Config, Opts) when is_list(Config), is_list(Opts) ->
++ "-"
++ atom_to_list(proplists:get_value(testcase, Config))
++ "-"
- ++ integer_to_list(erlang:system_time(seconds))
+ ++ integer_to_list(erlang:system_time(second))
++ "-"
++ integer_to_list(erlang:unique_integer([positive]))),
test_server:start_node(Name, slave, [{args, Opts++" -pa "++Pa}]).
diff --git a/erts/emulator/test/monitor_SUITE.erl b/erts/emulator/test/monitor_SUITE.erl
index 90d2bd8c5d..827ed817cc 100644
--- a/erts/emulator/test/monitor_SUITE.erl
+++ b/erts/emulator/test/monitor_SUITE.erl
@@ -697,7 +697,7 @@ mixer(Config) when is_list(Config) ->
named_down(Config) when is_list(Config) ->
Name = list_to_atom(atom_to_list(?MODULE)
++ "-named_down-"
- ++ integer_to_list(erlang:system_time(seconds))
+ ++ integer_to_list(erlang:system_time(second))
++ "-" ++ integer_to_list(erlang:unique_integer([positive]))),
Prio = process_flag(priority,high),
%% Spawn a bunch of high prio cpu bound processes to prevent
diff --git a/erts/emulator/test/mtx_SUITE.erl b/erts/emulator/test/mtx_SUITE.erl
index 1493e52655..12928ed6d8 100644
--- a/erts/emulator/test/mtx_SUITE.erl
+++ b/erts/emulator/test/mtx_SUITE.erl
@@ -443,7 +443,7 @@ hammer_ets_rwlock_test(XOpts, UW, C, N, NP, SC) ->
{'DOWN', M, process, P, _} -> ok
end
end, Ps),
- Res = (Stop-Start)/erlang:convert_time_unit(1,seconds,native),
+ Res = (Stop-Start)/erlang:convert_time_unit(1,second,native),
Caller ! {?MODULE, self(), Res}
end,
TP = spawn_link(T),
diff --git a/erts/emulator/test/nif_SUITE.erl b/erts/emulator/test/nif_SUITE.erl
index a0e9f1bad6..8df2733fac 100644
--- a/erts/emulator/test/nif_SUITE.erl
+++ b/erts/emulator/test/nif_SUITE.erl
@@ -1443,6 +1443,17 @@ otp_9828_loop(Bin, Val) ->
consume_timeslice(Config) when is_list(Config) ->
+ case {erlang:system_info(debug_compiled),
+ erlang:system_info(lock_checking)} of
+ {false, false} ->
+ consume_timeslice_test(Config);
+ {false, true} ->
+ {skipped, "Lock checking enabled"};
+ _ ->
+ {skipped, "Debug compiled"}
+ end.
+
+consume_timeslice_test(Config) when is_list(Config) ->
CONTEXT_REDS = 2000,
Me = self(),
Go = make_ref(),
@@ -1692,7 +1703,7 @@ nif_raise_exceptions(NifFunc) ->
end, ok, ExcTerms).
-define(ERL_NIF_TIME_ERROR, -9223372036854775808).
--define(TIME_UNITS, [seconds, milli_seconds, micro_seconds, nano_seconds]).
+-define(TIME_UNITS, [second, millisecond, microsecond, nanosecond]).
nif_monotonic_time(Config) ->
?ERL_NIF_TIME_ERROR = monotonic_time(invalid_time_unit),
@@ -1758,8 +1769,8 @@ chk_toffs([TU|TUs]) ->
chk_toffs(TUs).
nif_convert_time_unit(Config) ->
- ?ERL_NIF_TIME_ERROR = convert_time_unit(0, seconds, invalid_time_unit),
- ?ERL_NIF_TIME_ERROR = convert_time_unit(0, invalid_time_unit, seconds),
+ ?ERL_NIF_TIME_ERROR = convert_time_unit(0, second, invalid_time_unit),
+ ?ERL_NIF_TIME_ERROR = convert_time_unit(0, invalid_time_unit, second),
?ERL_NIF_TIME_ERROR = convert_time_unit(0, invalid_time_unit, invalid_time_unit),
lists:foreach(fun (Offset) ->
lists:foreach(fun (Diff) ->
@@ -1808,7 +1819,7 @@ nif_convert_time_unit(Config) ->
ctu_loop(0) ->
ok;
ctu_loop(N) ->
- chk_ctu(erlang:monotonic_time(nano_seconds)),
+ chk_ctu(erlang:monotonic_time(nanosecond)),
ctu_loop(N-1).
chk_ctu(Time) ->
@@ -1823,7 +1834,7 @@ chk_ctu(Time, [FromTU|FromTUs]) ->
chk_ctu(_Time, _FromTU, []) ->
ok;
chk_ctu(Time, FromTU, [ToTU|ToTUs]) ->
- T = erlang:convert_time_unit(Time, nano_seconds, FromTU),
+ T = erlang:convert_time_unit(Time, nanosecond, FromTU),
TE = erlang:convert_time_unit(T, FromTU, ToTU),
TN = convert_time_unit(T, FromTU, ToTU),
case TE =:= TN of
diff --git a/erts/emulator/test/nif_SUITE_data/nif_SUITE.c b/erts/emulator/test/nif_SUITE_data/nif_SUITE.c
index 13846244d4..f2b1ef9d24 100644
--- a/erts/emulator/test/nif_SUITE_data/nif_SUITE.c
+++ b/erts/emulator/test/nif_SUITE_data/nif_SUITE.c
@@ -38,10 +38,10 @@ static ERL_NIF_TERM atom_self;
static ERL_NIF_TERM atom_ok;
static ERL_NIF_TERM atom_join;
static ERL_NIF_TERM atom_binary_resource_type;
-static ERL_NIF_TERM atom_seconds;
-static ERL_NIF_TERM atom_milli_seconds;
-static ERL_NIF_TERM atom_micro_seconds;
-static ERL_NIF_TERM atom_nano_seconds;
+static ERL_NIF_TERM atom_second;
+static ERL_NIF_TERM atom_millisecond;
+static ERL_NIF_TERM atom_microsecond;
+static ERL_NIF_TERM atom_nanosecond;
typedef struct
@@ -147,10 +147,10 @@ static int load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info)
atom_ok = enif_make_atom(env,"ok");
atom_join = enif_make_atom(env,"join");
atom_binary_resource_type = enif_make_atom(env,"binary_resource_type");
- atom_seconds = enif_make_atom(env,"seconds");
- atom_milli_seconds = enif_make_atom(env,"milli_seconds");
- atom_micro_seconds = enif_make_atom(env,"micro_seconds");
- atom_nano_seconds = enif_make_atom(env,"nano_seconds");
+ atom_second = enif_make_atom(env,"second");
+ atom_millisecond = enif_make_atom(env,"millisecond");
+ atom_microsecond = enif_make_atom(env,"microsecond");
+ atom_nanosecond = enif_make_atom(env,"nanosecond");
*priv_data = data;
return 0;
@@ -1808,13 +1808,13 @@ static ERL_NIF_TERM monotonic_time(ErlNifEnv* env, int argc, const ERL_NIF_TERM
if (argc != 1)
return atom_false;
- if (enif_compare(argv[0], atom_seconds) == 0)
+ if (enif_compare(argv[0], atom_second) == 0)
time_unit = ERL_NIF_SEC;
- else if (enif_compare(argv[0], atom_milli_seconds) == 0)
+ else if (enif_compare(argv[0], atom_millisecond) == 0)
time_unit = ERL_NIF_MSEC;
- else if (enif_compare(argv[0], atom_micro_seconds) == 0)
+ else if (enif_compare(argv[0], atom_microsecond) == 0)
time_unit = ERL_NIF_USEC;
- else if (enif_compare(argv[0], atom_nano_seconds) == 0)
+ else if (enif_compare(argv[0], atom_nanosecond) == 0)
time_unit = ERL_NIF_NSEC;
else
time_unit = 4711; /* invalid time unit */
@@ -1829,13 +1829,13 @@ static ERL_NIF_TERM time_offset(ErlNifEnv* env, int argc, const ERL_NIF_TERM arg
if (argc != 1)
return atom_false;
- if (enif_compare(argv[0], atom_seconds) == 0)
+ if (enif_compare(argv[0], atom_second) == 0)
time_unit = ERL_NIF_SEC;
- else if (enif_compare(argv[0], atom_milli_seconds) == 0)
+ else if (enif_compare(argv[0], atom_millisecond) == 0)
time_unit = ERL_NIF_MSEC;
- else if (enif_compare(argv[0], atom_micro_seconds) == 0)
+ else if (enif_compare(argv[0], atom_microsecond) == 0)
time_unit = ERL_NIF_USEC;
- else if (enif_compare(argv[0], atom_nano_seconds) == 0)
+ else if (enif_compare(argv[0], atom_nanosecond) == 0)
time_unit = ERL_NIF_NSEC;
else
time_unit = 4711; /* invalid time unit */
@@ -1856,24 +1856,24 @@ static ERL_NIF_TERM convert_time_unit(ErlNifEnv* env, int argc, const ERL_NIF_TE
val = (ErlNifTime) i64;
- if (enif_compare(argv[1], atom_seconds) == 0)
+ if (enif_compare(argv[1], atom_second) == 0)
from = ERL_NIF_SEC;
- else if (enif_compare(argv[1], atom_milli_seconds) == 0)
+ else if (enif_compare(argv[1], atom_millisecond) == 0)
from = ERL_NIF_MSEC;
- else if (enif_compare(argv[1], atom_micro_seconds) == 0)
+ else if (enif_compare(argv[1], atom_microsecond) == 0)
from = ERL_NIF_USEC;
- else if (enif_compare(argv[1], atom_nano_seconds) == 0)
+ else if (enif_compare(argv[1], atom_nanosecond) == 0)
from = ERL_NIF_NSEC;
else
from = 4711; /* invalid time unit */
- if (enif_compare(argv[2], atom_seconds) == 0)
+ if (enif_compare(argv[2], atom_second) == 0)
to = ERL_NIF_SEC;
- else if (enif_compare(argv[2], atom_milli_seconds) == 0)
+ else if (enif_compare(argv[2], atom_millisecond) == 0)
to = ERL_NIF_MSEC;
- else if (enif_compare(argv[2], atom_micro_seconds) == 0)
+ else if (enif_compare(argv[2], atom_microsecond) == 0)
to = ERL_NIF_USEC;
- else if (enif_compare(argv[2], atom_nano_seconds) == 0)
+ else if (enif_compare(argv[2], atom_nanosecond) == 0)
to = ERL_NIF_NSEC;
else
to = 4711; /* invalid time unit */
diff --git a/erts/emulator/test/node_container_SUITE.erl b/erts/emulator/test/node_container_SUITE.erl
index 536c91d4ae..af18545bff 100644
--- a/erts/emulator/test/node_container_SUITE.erl
+++ b/erts/emulator/test/node_container_SUITE.erl
@@ -1092,7 +1092,7 @@ wait_until(Pred) ->
get_nodefirstname_string() ->
atom_to_list(?MODULE)
++ "-"
- ++ integer_to_list(erlang:system_time(seconds))
+ ++ integer_to_list(erlang:system_time(second))
++ "-"
++ integer_to_list(erlang:unique_integer([positive])).
diff --git a/erts/emulator/test/old_scheduler_SUITE.erl b/erts/emulator/test/old_scheduler_SUITE.erl
index f91d84beea..ffe7d40139 100644
--- a/erts/emulator/test/old_scheduler_SUITE.erl
+++ b/erts/emulator/test/old_scheduler_SUITE.erl
@@ -309,13 +309,13 @@ receiver(T0, TimeSec, Main, {P1,P1N}, {P2,P2N}) ->
%% uncomment lines below to get life sign (debug)
receiver(T0, Time, Main, P1,P1N,P1Rs, P2,P2N,P2Rs, 0) ->
- % T = erlang:convert_time_unit(erlang:monotonic_time() - T0, native, milli_seconds),
+ % T = erlang:convert_time_unit(erlang:monotonic_time() - T0, native, millisecond),
% erlang:display({round(T/1000),P1Rs,P2Rs}),
receiver(T0, Time, Main, P1,P1N,P1Rs, P2,P2N,P2Rs, 100000);
receiver(T0, Time, Main, P1,P1N,P1Rs, P2,P2N,P2Rs, C) ->
Remain = Time - erlang:convert_time_unit(erlang:monotonic_time() - T0,
- native, milli_seconds), % test time remaining
+ native, millisecond), % test time remaining
Remain1 = if Remain < 0 ->
0;
true ->
diff --git a/erts/emulator/test/port_SUITE.erl b/erts/emulator/test/port_SUITE.erl
index 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/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/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..a1eb126098 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..22817be8f4 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..ae4861c336 100644
--- a/erts/preloaded/ebin/prim_inet.beam
+++ b/erts/preloaded/ebin/prim_inet.beam
Binary files differ
diff --git a/erts/preloaded/ebin/zlib.beam b/erts/preloaded/ebin/zlib.beam
index 4c48742344..6a7ad9164f 100644
--- a/erts/preloaded/ebin/zlib.beam
+++ b/erts/preloaded/ebin/zlib.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..ee4fcedd2d 100644
--- a/erts/preloaded/src/erts_code_purger.erl
+++ b/erts/preloaded/src/erts_code_purger.erl
@@ -22,7 +22,7 @@
%% Purpose : Implement system process erts_code_purger
%% to handle code module purging.
--export([start/0, purge/1, soft_purge/1]).
+-export([start/0, purge/1, soft_purge/1, pending_purge_lambda/3]).
-spec start() -> term().
start() ->
@@ -40,10 +40,43 @@ loop() ->
Res = do_soft_purge(Mod),
From ! {reply, soft_purge, Res, Ref};
+ {test_purge, Mod, From, Type, Ref} when is_atom(Mod), is_pid(From) ->
+ do_test_purge(Mod, From, Type, Ref);
+
_Other -> ignore
end,
loop().
+%%
+%% Processes that tries to call a fun that belongs to
+%% a module that currently is being purged will end
+%% up here (pending_purge_lambda) in a suspended state.
+%% When the purge operation completes or aborts (soft
+%% purge that failed) these processes will be resumed.
+%%
+pending_purge_lambda(_Module, Fun, Args) ->
+ %%
+ %% When the process is resumed, the following
+ %% scenarios exist:
+ %% * The code that the fun refers to is still
+ %% there due to a failed soft purge. The
+ %% call to the fun will succeed via apply/2.
+ %% * The code was purged, and a current version
+ %% of the module is loaded which does not
+ %% contain this fun. The call will result
+ %% in an exception being raised.
+ %% * The code was purged, and no current
+ %% version of the module is loaded. An attempt
+ %% to load the module (via the error_handler)
+ %% will be made. This may or may not succeed.
+ %% If the module is loaded, it may or may
+ %% not contain the fun. The call will
+ %% succeed if the error_handler was able
+ %% to load the module and loaded module
+ %% contains this fun; otherwise, an exception
+ %% will be raised.
+ %%
+ apply(Fun, Args).
%% purge(Module)
%% Kill all processes running code from *old* Module, and then purge the
@@ -60,16 +93,14 @@ purge(Mod) when is_atom(Mod) ->
Result
end.
-
do_purge(Mod) ->
- case erts_internal:copy_literals(Mod, true) of
- false ->
- {false, false};
- true ->
- DidKill = check_proc_code(erlang:processes(), Mod, true),
- true = erts_internal:copy_literals(Mod, false),
- WasPurged = erts_internal:purge_module(Mod),
- {WasPurged, DidKill}
+ case erts_internal:purge_module(Mod, prepare) of
+ false ->
+ {false, false};
+ true ->
+ DidKill = check_proc_code(erlang:processes(), Mod, true),
+ true = erts_internal:purge_module(Mod, complete),
+ {true, DidKill}
end.
%% soft_purge(Module)
@@ -85,21 +116,17 @@ soft_purge(Mod) ->
Result
end.
-
do_soft_purge(Mod) ->
- case erts_internal:copy_literals(Mod, true) of
+ case erts_internal:purge_module(Mod, prepare) of
false ->
true;
true ->
- DoPurge = check_proc_code(erlang:processes(), Mod, false),
- true = erts_internal:copy_literals(Mod, false),
- case DoPurge of
- false ->
- false;
- true ->
- erts_internal:purge_module(Mod),
- true
- end
+ Res = check_proc_code(erlang:processes(), Mod, false),
+ erts_internal:purge_module(Mod,
+ case Res of
+ false -> abort;
+ true -> complete
+ end)
end.
%%
@@ -283,8 +310,7 @@ cpc_sched_kill(Pid,
cpc_request(#cpc_static{tag = Tag, module = Mod}, Pid, AllowGc) ->
erts_internal:check_process_code(Pid, Mod, [{async, {Tag, Pid, AllowGc}},
- {allow_gc, AllowGc},
- {copy_literals, true}]).
+ {allow_gc, AllowGc}]).
cpc_request_gc(CpcS, [Pid|Pids]) ->
cpc_request(CpcS, Pid, true),
@@ -297,3 +323,72 @@ cpc_init(CpcS, [Pid|Pids], NoReqs) ->
cpc_init(CpcS, Pids, NoReqs+1).
% end of check_proc_code() implementation.
+
+%%
+%% FOR TESTING ONLY
+%%
+%% do_test_purge() is for testing only. The purge is done
+%% as usual, but the tester can control when to enter the
+%% specific phases.
+%%
+do_test_purge(Mod, From, Type, Ref) when Type == true; Type == false ->
+ Mon = erlang:monitor(process, From),
+ Res = case Type of
+ true -> do_test_hard_purge(Mod, From, Ref, Mon);
+ false -> do_test_soft_purge(Mod, From, Ref, Mon)
+ end,
+ From ! {test_purge, Res, Ref},
+ erlang:demonitor(Mon, [flush]),
+ ok;
+do_test_purge(_, _, _, _) ->
+ ok.
+
+do_test_soft_purge(Mod, From, Ref, Mon) ->
+ PrepRes = erts_internal:purge_module(Mod, prepare),
+ TestRes = test_progress(started, From, Mon, Ref, ok),
+ case PrepRes of
+ false ->
+ _ = test_progress(continued, From, Mon, Ref, TestRes),
+ true;
+ true ->
+ Res = check_proc_code(erlang:processes(), Mod, false),
+ _ = test_progress(continued, From, Mon, Ref, TestRes),
+ erts_internal:purge_module(Mod,
+ case Res of
+ false -> abort;
+ true -> complete
+ end)
+ end.
+
+do_test_hard_purge(Mod, From, Ref, Mon) ->
+ PrepRes = erts_internal:purge_module(Mod, prepare),
+ TestRes = test_progress(started, From, Mon, Ref, ok),
+ case PrepRes of
+ false ->
+ _ = test_progress(continued, From, Mon, Ref, TestRes),
+ {false, false};
+ true ->
+ DidKill = check_proc_code(erlang:processes(), Mod, true),
+ _ = test_progress(continued, From, Mon, Ref, TestRes),
+ true = erts_internal:purge_module(Mod, complete),
+ {true, DidKill}
+ end.
+
+test_progress(_State, _From, _Mon, _Ref, died) ->
+ %% Test process died; continue so we wont
+ %% leave the system in an inconsistent
+ %% state...
+ died;
+test_progress(started, From, Mon, Ref, ok) ->
+ From ! {started, Ref},
+ receive
+ {'DOWN', Mon, process, From, _} -> died;
+ {continue, Ref} -> ok
+ end;
+test_progress(continued, From, Mon, Ref, ok) ->
+ From ! {continued, Ref},
+ receive
+ {'DOWN', Mon, process, From, _} -> died;
+ {complete, Ref} -> ok
+ end.
+
diff --git a/erts/preloaded/src/erts_dirty_process_code_checker.erl b/erts/preloaded/src/erts_dirty_process_code_checker.erl
new file mode 100644
index 0000000000..911642082c
--- /dev/null
+++ b/erts/preloaded/src/erts_dirty_process_code_checker.erl
@@ -0,0 +1,82 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2016. All Rights Reserved.
+%%
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+-module(erts_dirty_process_code_checker).
+
+-export([start/0]).
+
+%%
+%% The erts_dirty_process_code_checker is started at
+%% VM boot by the VM. It is a spawned as a system
+%% process, i.e, the whole VM will terminate if
+%% this process terminates.
+%%
+start() ->
+ process_flag(trap_exit, true),
+ msg_loop().
+
+msg_loop() ->
+ _ = receive
+ Request ->
+ handle_request(Request)
+ end,
+ msg_loop().
+
+check_process(Requester, Target, ReqId, Module) ->
+ Result = erts_internal:check_dirty_process_code(Target, Module),
+ Requester ! {check_process_code, ReqId, Result}.
+
+handle_request({Requester,
+ Target,
+ Prio,
+ {check_process_code,
+ ReqId,
+ Module,
+ _Flags} = Op}) ->
+ %%
+ %% Target may have stopped executing dirty since the
+ %% initial request was made. Check its current state
+ %% and try to send the request if possible; otherwise,
+ %% check the dirty executing process and send the result...
+ %%
+ try
+ case erts_internal:is_process_executing_dirty(Target) of
+ true ->
+ check_process(Requester, Target, ReqId, Module);
+ false ->
+ case erts_internal:request_system_task(Requester,
+ Target,
+ Prio,
+ Op) of
+ ok ->
+ ok;
+ dirty_execution ->
+ check_process(Requester, Target, ReqId, Module)
+ end
+ end
+ catch
+ _ : _ ->
+ ok %% Ignore all failures; someone passed us garbage...
+ end;
+handle_request(_Garbage) ->
+ ignore.
+
+
+
diff --git a/erts/preloaded/src/erts_internal.erl b/erts/preloaded/src/erts_internal.erl
index 2459ea2a2c..6229754c8c 100644
--- a/erts/preloaded/src/erts_internal.erl
+++ b/erts/preloaded/src/erts_internal.erl
@@ -38,11 +38,13 @@
-export([system_check/1,
gather_system_check_result/1]).
--export([request_system_task/3]).
+-export([request_system_task/3, request_system_task/4]).
-export([check_process_code/3]).
--export([copy_literals/2]).
--export([purge_module/1]).
+-export([check_dirty_process_code/2]).
+-export([is_process_executing_dirty/1]).
+-export([release_literal_area_switch/0]).
+-export([purge_module/2]).
-export([flush_monitor_messages/3]).
@@ -204,14 +206,25 @@ port_info(_Result, _Item) ->
-spec request_system_task(Pid, Prio, Request) -> 'ok' when
Prio :: 'max' | 'high' | 'normal' | 'low',
Request :: {'garbage_collect', term()}
- | {'check_process_code', term(), module(), non_neg_integer()},
+ | {'check_process_code', term(), module(), non_neg_integer()}
+ | {'copy_literals', term(), boolean()},
Pid :: pid().
request_system_task(_Pid, _Prio, _Request) ->
erlang:nif_error(undefined).
+-spec request_system_task(RequesterPid, TargetPid, Prio, Request) -> 'ok' | 'dirty_execution' when
+ Prio :: 'max' | 'high' | 'normal' | 'low',
+ Request :: {'garbage_collect', term()}
+ | {'check_process_code', term(), module(), non_neg_integer()}
+ | {'copy_literals', term(), boolean()},
+ RequesterPid :: pid(),
+ TargetPid :: pid().
+
+request_system_task(_RequesterPid, _TargetPid, _Prio, _Request) ->
+ erlang:nif_error(undefined).
+
-define(ERTS_CPC_ALLOW_GC, (1 bsl 0)).
--define(ERTS_CPC_COPY_LITERALS, (1 bsl 1)).
-spec check_process_code(Module, Flags) -> boolean() when
Module :: module(),
@@ -223,7 +236,7 @@ check_process_code(_Module, _Flags) ->
Pid :: pid(),
Module :: module(),
RequestId :: term(),
- Option :: {async, RequestId} | {allow_gc, boolean()} | {copy_literals, boolean()},
+ Option :: {async, RequestId} | {allow_gc, boolean()},
OptionList :: [Option],
CheckResult :: boolean() | aborted.
check_process_code(Pid, Module, OptionList) ->
@@ -265,8 +278,6 @@ get_cpc_opts([{async, _ReqId} = AsyncTuple | Options], _OldAsync, Flags) ->
get_cpc_opts(Options, AsyncTuple, Flags);
get_cpc_opts([{allow_gc, AllowGC} | Options], Async, Flags) ->
get_cpc_opts(Options, Async, cpc_flags(Flags, ?ERTS_CPC_ALLOW_GC, AllowGC));
-get_cpc_opts([{copy_literals, CopyLit} | Options], Async, Flags) ->
- get_cpc_opts(Options, Async, cpc_flags(Flags, ?ERTS_CPC_COPY_LITERALS, CopyLit));
get_cpc_opts([], Async, Flags) ->
{Async, Flags}.
@@ -275,15 +286,26 @@ cpc_flags(OldFlags, Bit, true) ->
cpc_flags(OldFlags, Bit, false) ->
OldFlags band (bnot Bit).
--spec copy_literals(Module,Bool) -> 'true' | 'false' | 'aborted' when
- Module :: module(),
- Bool :: boolean().
-copy_literals(_Mod, _Bool) ->
+-spec check_dirty_process_code(Pid,Module) -> 'true' | 'false' when
+ Pid :: pid(),
+ Module :: module().
+check_dirty_process_code(_Pid,_Module) ->
erlang:nif_error(undefined).
--spec purge_module(Module) -> boolean() when
- Module :: module().
-purge_module(_Module) ->
+-spec is_process_executing_dirty(Pid) -> 'true' | 'false' when
+ Pid :: pid().
+is_process_executing_dirty(_Pid) ->
+ erlang:nif_error(undefined).
+
+-spec release_literal_area_switch() -> 'true' | 'false'.
+
+release_literal_area_switch() ->
+ erlang:nif_error(undefined).
+
+-spec purge_module(Module, Op) -> boolean() when
+ Module :: module(),
+ Op :: 'prepare' | 'abort' | 'complete'.
+purge_module(_Module, _Op) ->
erlang:nif_error(undefined).
-spec system_check(Type) -> 'ok' when
diff --git a/erts/preloaded/src/erts_literal_area_collector.erl b/erts/preloaded/src/erts_literal_area_collector.erl
new file mode 100644
index 0000000000..3befad8dfb
--- /dev/null
+++ b/erts/preloaded/src/erts_literal_area_collector.erl
@@ -0,0 +1,113 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2016. All Rights Reserved.
+%%
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
+%%
+%% %CopyrightEnd%
+%%
+-module(erts_literal_area_collector).
+
+-export([start/0]).
+
+%% Currently we only allow two outstanding literal
+%% copying jobs that garbage collect in order to
+%% copy the literals. Maybe we could allow more
+%% than two outstanding processes, but for now we
+%% play it safe...
+-define(MAX_GC_OUTSTND, 2).
+
+%%
+%% The erts_literal_area_collector is started at
+%% VM boot by the VM. It is a spawned as a system
+%% process, i.e, the whole VM will terminate if
+%% this process terminates.
+%%
+start() ->
+ process_flag(trap_exit, true),
+ msg_loop(undefined, 0, 0, []).
+
+%%
+%% The VM will send us a 'copy_literals' message
+%% when it has a new literal area that needs to
+%% be handled is added. We will also be informed
+%% about more areas when we call
+%% erts_internal:release_literal_area_switch().
+%%
+msg_loop(Area, Outstnd, GcOutstnd, NeedGC) ->
+ receive
+
+ %% A new area to handle has arrived...
+ copy_literals when Outstnd == 0 ->
+ switch_area();
+
+ %% Process (_Pid) has completed the request...
+ {copy_literals, {Area, _GcAllowed, _Pid}, ok} when Outstnd == 1 ->
+ switch_area(); %% Last process completed...
+ {copy_literals, {Area, false, _Pid}, ok} ->
+ msg_loop(Area, Outstnd-1, GcOutstnd, NeedGC);
+ {copy_literals, {Area, true, _Pid}, ok} when NeedGC == [] ->
+ msg_loop(Area, Outstnd-1, GcOutstnd-1, []);
+ {copy_literals, {Area, true, _Pid}, ok} ->
+ send_copy_req(hd(NeedGC), Area, true),
+ msg_loop(Area, Outstnd-1, GcOutstnd, tl(NeedGC));
+
+ %% Process (Pid) failed to complete the request
+ %% since it needs to garbage collect in order to
+ %% complete the request...
+ {copy_literals, {Area, false, Pid}, need_gc} when GcOutstnd < ?MAX_GC_OUTSTND ->
+ send_copy_req(Pid, Area, true),
+ msg_loop(Area, Outstnd, GcOutstnd+1, NeedGC);
+ {copy_literals, {Area, false, Pid}, need_gc} ->
+ msg_loop(Area, Outstnd, GcOutstnd, [Pid|NeedGC]);
+
+ %% Not handled message regarding the area that we
+ %% currently are working with. Crash the VM so
+ %% we notice this bug...
+ {copy_literals, {Area, _, _}, _} = Msg when erlang:is_reference(Area) ->
+ exit({not_handled_message, Msg});
+
+ %% Unexpected garbage message. Get rid of it...
+ _Ignore ->
+ msg_loop(Area, Outstnd, GcOutstnd, NeedGC)
+
+ end.
+
+switch_area() ->
+ Res = erts_internal:release_literal_area_switch(),
+ erlang:garbage_collect(), %% Almost no live data now...
+ case Res of
+ false ->
+ %% No more areas to handle...
+ msg_loop(undefined, 0, 0, []);
+ true ->
+ %% Send requests to all processes to copy
+ %% all live data they have referring to the
+ %% literal area that is to be released...
+ Area = make_ref(),
+ Outstnd = send_copy_reqs(erlang:processes(), Area, false),
+ msg_loop(Area, Outstnd, 0, [])
+ end.
+
+send_copy_reqs(Ps, Area, GC) ->
+ send_copy_reqs(Ps, Area, GC, 0).
+
+send_copy_reqs([], _Area, _GC, N) ->
+ N;
+send_copy_reqs([P|Ps], Area, GC, N) ->
+ send_copy_req(P, Area, GC),
+ send_copy_reqs(Ps, Area, GC, N+1).
+
+send_copy_req(P, Area, GC) ->
+ erts_internal:request_system_task(P, normal, {copy_literals, {Area, GC, P}, GC}).
diff --git a/erts/preloaded/src/init.erl b/erts/preloaded/src/init.erl
index 45468b3b9c..962528f7ab 100644
--- a/erts/preloaded/src/init.erl
+++ b/erts/preloaded/src/init.erl
@@ -423,9 +423,6 @@ loop(State) ->
Loaded = State#state.loaded, %% boot_loop but is handled here
From ! {init,Loaded}, %% anyway.
loop(State);
- {From, {ensure_loaded, _}} ->
- From ! {init, not_allowed},
- loop(State);
Msg ->
loop(handle_msg(Msg,State))
end.
@@ -465,6 +462,8 @@ do_handle_msg(Msg,State) ->
From ! {init,ok},
{new_state,State#state{subscribed = [Pid|Subscribed]}}
end;
+ {From, {ensure_loaded, _}} ->
+ From ! {init, not_allowed};
X ->
case whereis(user) of
undefined ->
@@ -670,9 +669,9 @@ unload(_) ->
do_unload(sub([heart|erlang:pre_loaded()],erlang:loaded())).
do_unload([M|Mods]) ->
- catch erts_internal:purge_module(M),
+ catch erlang:purge_module(M),
catch erlang:delete_module(M),
- catch erts_internal:purge_module(M),
+ catch erlang:purge_module(M),
do_unload(Mods);
do_unload([]) ->
purge_all_hipe_refs(),
diff --git a/erts/preloaded/src/prim_inet.erl b/erts/preloaded/src/prim_inet.erl
index 560810d222..bcf16402b0 100644
--- a/erts/preloaded/src/prim_inet.erl
+++ b/erts/preloaded/src/prim_inet.erl
@@ -2401,13 +2401,13 @@ get_addrs([F|Addrs]) ->
{Addr,Rest} = get_addr(F, Addrs),
[Addr|get_addrs(Rest)].
-get_addr(?INET_AF_LOCAL, [0]) ->
- {{local,<<>>},[]};
get_addr(?INET_AF_LOCAL, [N|Addr]) ->
{A,Rest} = lists:split(N, Addr),
{{local,iolist_to_binary(A)},Rest};
+get_addr(?INET_AF_UNSPEC, Rest) ->
+ {{unspec,<<>>},Rest};
get_addr(?INET_AF_UNDEFINED, Rest) ->
- {{undefined,0},Rest};
+ {{undefined,<<>>},Rest};
get_addr(Family, [P1,P0|Addr]) ->
{IP,Rest} = get_ip(Family, Addr),
{{IP,?u16(P1, P0)},Rest}.
diff --git a/erts/preloaded/src/zlib.erl b/erts/preloaded/src/zlib.erl
index fa0f28c5c3..8cd3e39fd7 100644
--- a/erts/preloaded/src/zlib.erl
+++ b/erts/preloaded/src/zlib.erl
@@ -23,7 +23,8 @@
-export([open/0,close/1,deflateInit/1,deflateInit/2,deflateInit/6,
deflateSetDictionary/2,deflateReset/1,deflateParams/3,
deflate/2,deflate/3,deflateEnd/1,
- inflateInit/1,inflateInit/2,inflateSetDictionary/2,
+ inflateInit/1,inflateInit/2,
+ inflateSetDictionary/2,inflateGetDictionary/1,
inflateSync/1,inflateReset/1,inflate/2,inflateEnd/1,
inflateChunk/1, inflateChunk/2,
setBufSize/2,getBufSize/1,
@@ -98,25 +99,26 @@
-define(INFLATE_INIT, 8).
-define(INFLATE_INIT2, 9).
-define(INFLATE_SETDICT, 10).
--define(INFLATE_SYNC, 11).
--define(INFLATE_RESET, 12).
--define(INFLATE_END, 13).
--define(INFLATE, 14).
--define(INFLATE_CHUNK, 25).
+-define(INFLATE_GETDICT, 11).
+-define(INFLATE_SYNC, 12).
+-define(INFLATE_RESET, 13).
+-define(INFLATE_END, 14).
+-define(INFLATE, 15).
+-define(INFLATE_CHUNK, 26).
--define(CRC32_0, 15).
--define(CRC32_1, 16).
--define(CRC32_2, 17).
+-define(CRC32_0, 16).
+-define(CRC32_1, 17).
+-define(CRC32_2, 18).
--define(SET_BUFSZ, 18).
--define(GET_BUFSZ, 19).
--define(GET_QSIZE, 20).
+-define(SET_BUFSZ, 19).
+-define(GET_BUFSZ, 20).
+-define(GET_QSIZE, 21).
--define(ADLER32_1, 21).
--define(ADLER32_2, 22).
+-define(ADLER32_1, 22).
+-define(ADLER32_2, 23).
--define(CRC32_COMBINE, 23).
--define(ADLER32_COMBINE, 24).
+-define(CRC32_COMBINE, 24).
+-define(ADLER32_COMBINE, 25).
%%------------------------------------------------------------------------
@@ -242,6 +244,13 @@ inflateInit(Z, WindowBits) ->
inflateSetDictionary(Z, Dictionary) ->
call(Z, ?INFLATE_SETDICT, Dictionary).
+-spec inflateGetDictionary(Z) -> Dictionary when
+ Z :: zstream(),
+ Dictionary :: iolist().
+inflateGetDictionary(Z) ->
+ _ = call(Z, ?INFLATE_GETDICT, []),
+ collect(Z).
+
-spec inflateSync(zstream()) -> 'ok'.
inflateSync(Z) ->
call(Z, ?INFLATE_SYNC, []).
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 dff6b615fc..acd4509304 100644
--- a/erts/vsn.mk
+++ b/erts/vsn.mk
@@ -18,7 +18,7 @@
# %CopyrightEnd%
#
-VSN = 8.0.2
+VSN = 8.0.3
# Port number 4365 in 4.2
# Port number 4366 in 4.3
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/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 6f6d742293..3606af9d75 100644
--- a/lib/compiler/src/beam_dead.erl
+++ b/lib/compiler/src/beam_dead.erl
@@ -272,14 +272,18 @@ 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,_,[R,_],Ctxt}=I|Is], D,
+backward([{test,bs_start_match2,F,Live,[R,_]=Args,Ctxt}|Is], D,
[{test,bs_match_string,F,[Ctxt,Bs]},
{test,bs_test_tail2,F,[Ctxt,0]}|Acc0]=Acc) ->
+ {f,To0} = F,
case beam_utils:is_killed(Ctxt, Acc0, D) of
true ->
- Eq = {test,is_eq_exact,F,[R,{literal,Bs}]},
+ To = shortcut_bs_context_to_binary(To0, R, D),
+ Eq = {test,is_eq_exact,{f,To},[R,{literal,Bs}]},
backward(Is, D, [Eq|Acc0]);
false ->
+ To = shortcut_bs_start_match(To0, R, D),
+ 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) ->
@@ -551,6 +555,21 @@ shortcut_bs_start_match_1([{test,bs_start_match2,{f,To},_,[Reg|_],_}|_],
shortcut_bs_start_match_1(_, _, To, _) ->
To.
+%% shortcut_bs_context_to_binary(TargetLabel, Reg) -> TargetLabel
+%% If a bs_start_match2 instruction has been eliminated, the
+%% bs_context_to_binary instruction can be eliminated too.
+
+shortcut_bs_context_to_binary(To, Reg, D) ->
+ shortcut_bs_ctb_1(beam_utils:code_at(To, D), Reg, To, D).
+
+shortcut_bs_ctb_1([{bs_context_to_binary,Reg}|Is], Reg, To, D) ->
+ shortcut_bs_ctb_1(Is, Reg, To, D);
+shortcut_bs_ctb_1([{jump,{f,To}}|_], Reg, _, D) ->
+ Code = beam_utils:code_at(To, D),
+ shortcut_bs_ctb_1(Code, Reg, To, D);
+shortcut_bs_ctb_1(_, _, To, _) ->
+ To.
+
%% shortcut_rel_op(FailLabel, Operator, [Operand], D) -> FailLabel'
%% Try to shortcut the given test instruction. Example:
%%
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/sys_pre_expand.erl b/lib/compiler/src/sys_pre_expand.erl
index 7ab4e1845c..f996a2d2d7 100644
--- a/lib/compiler/src/sys_pre_expand.erl
+++ b/lib/compiler/src/sys_pre_expand.erl
@@ -520,9 +520,8 @@ new_fun_name(#expand{func=F,arity=A,fcount=I}=St, FName) ->
%% pattern_bin([Element], State) -> {[Element],[Variable],[UsedVar],State}.
-pattern_bin(Es0, St) ->
- Es1 = bin_expand_strings(Es0),
- foldr(fun (E, Acc) -> pattern_element(E, Acc) end, {[],St}, Es1).
+pattern_bin(Es, St) ->
+ foldr(fun (E, Acc) -> pattern_element(E, Acc) end, {[],St}, Es).
pattern_element({bin_element,Line,Expr0,Size0,Type0}, {Es,St0}) ->
{Expr1,St1} = pattern(Expr0, St0),
@@ -558,9 +557,8 @@ coerce_to_float(E, _) -> E.
%% expr_bin([Element], State) -> {[Element],State}.
-expr_bin(Es0, St) ->
- Es1 = bin_expand_strings(Es0),
- foldr(fun (E, Acc) -> bin_element(E, Acc) end, {[],St}, Es1).
+expr_bin(Es, St) ->
+ foldr(fun (E, Acc) -> bin_element(E, Acc) end, {[],St}, Es).
bin_element({bin_element,Line,Expr,Size,Type}, {Es,St0}) ->
{Expr1,St1} = expr(Expr, St0),
@@ -570,14 +568,6 @@ bin_element({bin_element,Line,Expr,Size,Type}, {Es,St0}) ->
{Size2,Type1} = make_bit_type(Line, Size1, Type),
{[{bin_element,Line,Expr1,Size2,Type1}|Es],St2}.
-bin_expand_strings(Es) ->
- foldr(fun ({bin_element,Line,{string,_,S},Sz,Ts}, Es1) ->
- foldr(fun (C, Es2) ->
- [{bin_element,Line,{char,Line,C},Sz,Ts}|Es2]
- end, Es1, S);
- (E, Es1) -> [E|Es1]
- end, [], Es).
-
%% new_var_name(State) -> {VarName,State}.
new_var_name(St) ->
diff --git a/lib/compiler/src/v3_core.erl b/lib/compiler/src/v3_core.erl
index d71411de80..634ec68736 100644
--- a/lib/compiler/src/v3_core.erl
+++ b/lib/compiler/src/v3_core.erl
@@ -901,7 +901,7 @@ try_after(As, St0) ->
expr_bin(Es0, Anno, St0) ->
case constant_bin(Es0) of
error ->
- {Es,Eps,St} = expr_bin_1(Es0, St0),
+ {Es,Eps,St} = expr_bin_1(bin_expand_strings(Es0), St0),
{#ibinary{anno=#a{anno=Anno},segments=Es},Eps,St};
Bin ->
{#c_literal{anno=Anno,val=Bin},[],St0}
@@ -923,7 +923,8 @@ constant_bin(Es) ->
constant_bin_1(Es) ->
verify_suitable_fields(Es),
EmptyBindings = erl_eval:new_bindings(),
- EvalFun = fun({integer,_,I}, B) -> {value,I,B};
+ EvalFun = fun({string,_,S}, B) -> {value,S,B};
+ ({integer,_,I}, B) -> {value,I,B};
({char,_,C}, B) -> {value,C,B};
({float,_,F}, B) -> {value,F,B};
({atom,_,undefined}, B) -> {value,undefined,B}
@@ -944,6 +945,9 @@ verify_suitable_fields([{bin_element,_,Val,SzTerm,Opts}|Es]) ->
end,
{unit,Unit} = keyfind(unit, 1, Opts),
case {SzTerm,Val} of
+ {{atom,_,undefined},{string,_,_}} ->
+ %% UTF-8/16/32.
+ ok;
{{atom,_,undefined},{char,_,_}} ->
%% UTF-8/16/32.
ok;
@@ -983,6 +987,14 @@ count_bits(Int) ->
count_bits_1(0, Bits) -> Bits;
count_bits_1(Int, Bits) -> count_bits_1(Int bsr 64, Bits+64).
+bin_expand_strings(Es) ->
+ foldr(fun ({bin_element,Line,{string,_,S},Sz,Ts}, Es1) ->
+ foldr(fun (C, Es2) ->
+ [{bin_element,Line,{char,Line,C},Sz,Ts}|Es2]
+ end, Es1, S);
+ (E, Es1) -> [E|Es1]
+ end, [], Es).
+
expr_bin_1(Es, St) ->
foldr(fun (E, {Ces,Esp,St0}) ->
{Ce,Ep,St1} = bitstr(E, St0),
@@ -1394,6 +1406,9 @@ bc_elem_size({bin,_,El}, St0) ->
bc_elem_size(_, _) ->
throw(impossible).
+bc_elem_size_1([{bin_element,_,{string,_,String},{integer,_,N},Flags}|Es], Bits, Vars) ->
+ {unit,U} = keyfind(unit, 1, Flags),
+ bc_elem_size_1(Es, Bits+U*N*length(String), Vars);
bc_elem_size_1([{bin_element,_,_,{integer,_,N},Flags}|Es], Bits, Vars) ->
{unit,U} = keyfind(unit, 1, Flags),
bc_elem_size_1(Es, Bits+U*N, Vars);
@@ -1513,6 +1528,9 @@ bc_list_length(_, _) ->
bc_bin_size({bin,_,Els}) ->
bc_bin_size_1(Els, 0).
+bc_bin_size_1([{bin_element,_,{string,_,String},{integer,_,Sz},Flags}|Els], N) ->
+ {unit,U} = keyfind(unit, 1, Flags),
+ bc_bin_size_1(Els, N+U*Sz*length(String));
bc_bin_size_1([{bin_element,_,_,{integer,_,Sz},Flags}|Els], N) ->
{unit,U} = keyfind(unit, 1, Flags),
bc_bin_size_1(Els, N+U*Sz);
@@ -1736,7 +1754,7 @@ pat_alias_map_pairs_1([]) -> [].
%% pat_bin([BinElement], State) -> [BinSeg].
-pat_bin(Ps, St) -> [pat_segment(P, St) || P <- Ps].
+pat_bin(Ps, St) -> [pat_segment(P, St) || P <- bin_expand_strings(Ps)].
pat_segment({bin_element,L,Val,Size,[Type,{unit,Unit}|Flags]}, St) ->
Anno = lineno_anno(L, St),
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/bs_bincomp_SUITE.erl b/lib/compiler/test/bs_bincomp_SUITE.erl
index 4743821337..dd1d245f88 100644
--- a/lib/compiler/test/bs_bincomp_SUITE.erl
+++ b/lib/compiler/test/bs_bincomp_SUITE.erl
@@ -56,6 +56,7 @@ end_per_group(_GroupName, Config) ->
byte_aligned(Config) when is_list(Config) ->
cs_init(),
<<"abcdefg">> = cs(<< <<(X+32)>> || <<X>> <= <<"ABCDEFG">> >>),
+ <<"AxyzBxyzCxyz">> = cs(<< <<X, "xyz">> || <<X>> <= <<"ABC">> >>),
<<1:32/little,2:32/little,3:32/little,4:32/little>> =
cs(<< <<X:32/little>> || <<X:32>> <= <<1:32,2:32,3:32,4:32>> >>),
cs(<<1:32/little,2:32/little,3:32/little,4:32/little>> =
diff --git a/lib/compiler/test/bs_utf_SUITE.erl b/lib/compiler/test/bs_utf_SUITE.erl
index c894041f72..ef3fc54b37 100644
--- a/lib/compiler/test/bs_utf_SUITE.erl
+++ b/lib/compiler/test/bs_utf_SUITE.erl
@@ -235,6 +235,7 @@ utf32_to_unicode(<<>>) -> [].
literals(Config) when is_list(Config) ->
abc_utf8 = match_literal(<<"abc"/utf8>>),
abc_utf8 = match_literal(<<$a,$b,$c>>),
+ abc_utf8 = match_literal(<<$a/utf8,$b/utf8,$c/utf8>>),
abc_utf16be = match_literal(<<"abc"/utf16>>),
abc_utf16be = match_literal(<<$a:16,$b:16,$c:16>>),
diff --git a/lib/compiler/test/match_SUITE.erl b/lib/compiler/test/match_SUITE.erl
index 127679ba69..52b2da05f7 100644
--- a/lib/compiler/test/match_SUITE.erl
+++ b/lib/compiler/test/match_SUITE.erl
@@ -576,7 +576,15 @@ grab_bag_remove_failure([{stretch,_,Mi}=Stretch | Specs], Unit, _MaxFailure) ->
%% Regression in 19.0, reported by Alexei Sholik
literal_binary(_Config) ->
- 3 = literal_binary_match(bar,<<"y">>),
+ 3 = literal_binary_match(bar, <<"y">>),
+
+ %% While we are at it, also test the remaining code paths
+ %% in literal_binary_match/2.
+ 1 = literal_binary_match(bar, <<"x">>),
+ 2 = literal_binary_match(foo, <<"x">>),
+ 3 = literal_binary_match(foo, <<"y">>),
+ fail = literal_binary_match(bar, <<"z">>),
+ fail = literal_binary_match(foo, <<"z">>),
ok.
literal_binary_match(bar, <<"x">>) -> 1;
diff --git a/lib/crypto/c_src/crypto.c b/lib/crypto/c_src/crypto.c
index 5270c9131e..ce149aa96a 100644
--- a/lib/crypto/c_src/crypto.c
+++ b/lib/crypto/c_src/crypto.c
@@ -756,7 +756,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[21]; /* increase when extending the list */
static void init_algorithms_types(ErlNifEnv* env)
{
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/src/dbg_iload.erl b/lib/debugger/src/dbg_iload.erl
index 369b456524..f83684b605 100644
--- a/lib/debugger/src/dbg_iload.erl
+++ b/lib/debugger/src/dbg_iload.erl
@@ -216,7 +216,7 @@ pattern({op,_,'-',{float,Anno,I}}) ->
pattern({op,_,'+',{float,Anno,I}}) ->
{value,ln(Anno),I};
pattern({bin,Anno,Grp}) ->
- Grp1 = pattern_list(Grp),
+ Grp1 = pattern_list(bin_expand_strings(Grp)),
{bin,ln(Anno),Grp1};
pattern({bin_element,Anno,Expr,Size,Type}) ->
Expr1 = pattern(Expr),
@@ -297,7 +297,7 @@ gexpr({map,Anno,E0,Fs0}) ->
Fs1 = map_fields(Fs0, fun gexpr/1),
{map,ln(Anno),E1,Fs1};
gexpr({bin,Anno,Flds0}) ->
- Flds = gexpr_list(Flds0),
+ Flds = gexpr_list(bin_expand_strings(Flds0)),
{bin,ln(Anno),Flds};
gexpr({bin_element,Anno,Expr0,Size0,Type}) ->
Expr = gexpr(Expr0),
@@ -506,7 +506,7 @@ expr({op,Anno,Op,L0,R0}, _Lc) ->
R1 = expr(R0, false), %They see the same variables
{op,ln(Anno),Op,[L1,R1]};
expr({bin,Anno,Grp}, _Lc) ->
- Grp1 = expr_list(Grp),
+ Grp1 = expr_list(bin_expand_strings(Grp)),
{bin,ln(Anno),Grp1};
expr({bin_element,Anno,Expr,Size,Type}, _Lc) ->
Expr1 = expr(Expr, false),
@@ -519,6 +519,19 @@ consify([A|As]) ->
{cons,0,A,consify(As)};
consify([]) -> {value,0,[]}.
+%% The debugger converts both strings "abc" and lists [67, 68, 69]
+%% into {value, Line, [67, 68, 69]}, making it impossible to later
+%% distingish one or the other inside binaries when evaluating. To
+%% avoid <<[67, 68, 69]>> from evaluating, we convert strings into
+%% chars to avoid the ambiguity.
+bin_expand_strings(Es) ->
+ lists:foldr(fun ({bin_element,Line,{string,_,S},Sz,Ts}, Es1) ->
+ lists:foldr(fun (C, Es2) ->
+ [{bin_element,Line,{char,Line,C},Sz,Ts}|Es2]
+ end, Es1, S);
+ (E, Es1) -> [E|Es1]
+ end, [], Es).
+
%% -type expr_list([Expression]) -> [Expression].
%% These expressions are processed "in parallel" for purposes of variable
%% definition etc.
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/test/bs_bincomp_SUITE.erl b/lib/debugger/test/bs_bincomp_SUITE.erl
index 39e2240f2d..064e9567b3 100644
--- a/lib/debugger/test/bs_bincomp_SUITE.erl
+++ b/lib/debugger/test/bs_bincomp_SUITE.erl
@@ -66,6 +66,7 @@ end_per_group(_GroupName, Config) ->
byte_aligned(Config) when is_list(Config) ->
<<"abcdefg">> = << <<(X+32)>> || <<X>> <= <<"ABCDEFG">> >>,
+ <<"AxyzBxyzCxyz">> = << <<X, "xyz">> || <<X>> <= <<"ABC">> >>,
<<1:32/little,2:32/little,3:32/little,4:32/little>> =
<< <<X:32/little>> || <<X:32>> <= <<1:32,2:32,3:32,4:32>> >>,
<<1:32/little,2:32/little,3:32/little,4:32/little>> =
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/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/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/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/flow/cfg.inc b/lib/hipe/flow/cfg.inc
index 0bad2a8dd7..a18bfbc526 100644
--- a/lib/hipe/flow/cfg.inc
+++ b/lib/hipe/flow/cfg.inc
@@ -343,7 +343,7 @@ remove_pred(HT, FromL, PredL) ->
case gb_trees:lookup(FromL, HT) of
{value, {Block, Succ, Preds}} ->
Code = hipe_bb:code(Block),
- NewCode = remove_pred_from_phis(Code, PredL, []),
+ NewCode = remove_pred_from_phis(PredL, Code),
NewBlock = hipe_bb:code_update(Block, NewCode),
gb_trees:update(FromL, {NewBlock,Succ,lists:delete(PredL,Preds)}, HT);
none ->
@@ -374,20 +374,20 @@ add_pred(HT, ToL, PredL) ->
-ifdef(CFG_CAN_HAVE_PHI_NODES).
%% phi-instructions in a removed block's successors must be aware of
%% the change.
-remove_pred_from_phis(List = [I|Left], Label, Acc) ->
+remove_pred_from_phis(Label, List = [I|Left]) ->
case is_phi(I) of
- true ->
- NewAcc = [phi_remove_pred(I, Label)|Acc],
- remove_pred_from_phis(Left, Label, NewAcc);
+ true ->
+ NewI = phi_remove_pred(I, Label),
+ [NewI | remove_pred_from_phis(Label, Left)];
false ->
- lists:reverse(Acc) ++ List
+ List
end;
-remove_pred_from_phis([], _Label, Acc) ->
- lists:reverse(Acc).
+remove_pred_from_phis(_Label, []) ->
+ [].
-else.
%% this is used for code representations like those of back-ends which
%% do not have phi-nodes.
-remove_pred_from_phis(Code, _Label, _Acc) ->
+remove_pred_from_phis(_Label, Code) ->
Code.
-endif.
@@ -927,24 +927,52 @@ merge(BB, BB2, BB2_Label) ->
remove_unreachable_code(CFG) ->
Start = start_label(CFG),
- Reachable = find_reachable([Start], CFG, gb_sets:from_list([Start])),
- %% Reachable is an ordset: it comes from gb_sets:to_list/1.
- %% So use ordset:subtract instead of '--' below.
- Labels = ordsets:from_list(labels(CFG)),
- case ordsets:subtract(Labels, Reachable) of
- [] ->
- CFG;
+ %% No unreachable block will make another block reachable, so no fixpoint
+ %% looping is required
+ Reachable = find_reachable([], [Start], CFG, #{Start=>[]}),
+ case [L || L <- labels(CFG), not maps:is_key(L, Reachable)] of
+ [] -> CFG;
Remove ->
- NewCFG = lists:foldl(fun(X, Acc) -> bb_remove(Acc, X) end, CFG, Remove),
- remove_unreachable_code(NewCFG)
+ HT0 = CFG#cfg.table,
+ HT1 = lists:foldl(fun gb_trees:delete/2, HT0, Remove),
+ ReachableP = fun(Lbl) -> maps:is_key(Lbl, Reachable) end,
+ HT = gb_trees:map(fun(_,B)->prune_preds(B, ReachableP)end, HT1),
+ CFG#cfg{table=HT}
end.
-find_reachable([Label|Left], CFG, Acc) ->
- NewAcc = gb_sets:add(Label, Acc),
- Succ = succ(CFG, Label),
- find_reachable([X || X <- Succ, not gb_sets:is_member(X, Acc)] ++ Left,
- CFG, NewAcc);
-find_reachable([], _CFG, Acc) ->
- gb_sets:to_list(Acc).
+find_reachable([], [], _CFG, Acc) -> Acc;
+find_reachable([Succ|Succs], Left, CFG, Acc) ->
+ case Acc of
+ #{Succ := _} -> find_reachable(Succs, Left, CFG, Acc);
+ #{} -> find_reachable(Succs, [Succ|Left], CFG, Acc#{Succ => []})
+ end;
+find_reachable([], [Label|Left], CFG, Acc) ->
+ find_reachable(succ(CFG, Label), Left, CFG, Acc).
+
+%% Batch prune unreachable predecessors. Asymptotically faster than deleting
+%% unreachable blocks one at a time with bb_remove, at least when
+%% CFG_CAN_HAVE_PHI_NODES is undefined. Otherwise a phi_remove_preds might be
+%% needed to achieve that.
+prune_preds(B={Block, Succ, Preds}, ReachableP) ->
+ case lists:partition(ReachableP, Preds) of
+ {_, []} -> B;
+ {NewPreds, Unreach} ->
+ NewCode = remove_preds_from_phis(Unreach, hipe_bb:code(Block)),
+ {hipe_bb:code_update(Block, NewCode), Succ, NewPreds}
+ end.
+-ifdef(CFG_CAN_HAVE_PHI_NODES).
+remove_preds_from_phis(_, []) -> [];
+remove_preds_from_phis(Preds, List=[I|Left]) ->
+ case is_phi(I) of
+ false -> List;
+ true ->
+ NewI = lists:foldl(fun(L,IA)->phi_remove_pred(IA,L)end,
+ I, Preds),
+ [NewI | remove_preds_from_phis(Preds, Left)]
+ end.
+-else.
+remove_preds_from_phis(_, Code) -> Code.
-endif.
+
+-endif. %% -ifdef(REMOVE_UNREACHABLE_CODE)
diff --git a/lib/hipe/flow/liveness.inc b/lib/hipe/flow/liveness.inc
index a1caa3e0ad..bffaa4e3df 100644
--- a/lib/hipe/flow/liveness.inc
+++ b/lib/hipe/flow/liveness.inc
@@ -49,6 +49,10 @@
-endif.
-include("../flow/cfg.hrl").
+-include("../main/hipe.hrl").
+
+-opaque liveness() :: map().
+-export_type([liveness/0]).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%
@@ -72,7 +76,7 @@
%% The generic liveness analysis
%%
--spec analyze(cfg()) -> gb_trees:tree().
+-spec analyze(cfg()) -> liveness().
-ifdef(HIPE_LIVENESS_CALC_LARGEST_LIVESET).
analyze(CFG) ->
@@ -188,6 +192,7 @@ update_livein(Label, NewLiveIn, Liveness) ->
%%
%% LiveOut for a block is the union of the successors LiveIn
%%
+-spec liveout(liveness(), _) -> [_].
liveout(Liveness, L) ->
Succ = successors(L, Liveness),
@@ -210,7 +215,7 @@ successors(L, Liveness) ->
{_GK, _LiveIn, Successors} = liveness_lookup(L, Liveness),
Successors.
--spec livein(gb_trees:tree(), _) -> [_].
+-spec livein(liveness(), _) -> [_].
livein(Liveness, L) ->
{_GK, LiveIn, _Successors} = liveness_lookup(L, Liveness),
@@ -292,18 +297,15 @@ strip([{_,Y}|Xs]) ->
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%
+-compile({inline, [liveness_lookup/2, liveness_update/3]}).
+
liveness_init(List) ->
- liveness_init(List, gb_trees:empty()).
+ maps:from_list(List).
-liveness_init([{Lbl, Data}|Left], Acc) ->
- liveness_init(Left, gb_trees:insert(Lbl, Data, Acc));
-liveness_init([], Acc) ->
- Acc.
-
liveness_lookup(Label, Liveness) ->
- gb_trees:get(Label, Liveness).
+ maps:get(Label, Liveness).
liveness_update(Label, Val, Liveness) ->
- gb_trees:update(Label, Val, Liveness).
+ maps:update(Label, Val, Liveness).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
diff --git a/lib/hipe/icode/hipe_icode_bincomp.erl b/lib/hipe/icode/hipe_icode_bincomp.erl
index 5a27519141..5ee6fe2c87 100644
--- a/lib/hipe/icode/hipe_icode_bincomp.erl
+++ b/lib/hipe/icode/hipe_icode_bincomp.erl
@@ -40,8 +40,8 @@
-spec cfg(cfg()) -> cfg().
cfg(Cfg1) ->
- StartLbls = ordsets:from_list([hipe_icode_cfg:start_label(Cfg1)]),
- find_bs_get_integer(StartLbls, Cfg1, StartLbls).
+ StartLbl = hipe_icode_cfg:start_label(Cfg1),
+ find_bs_get_integer([StartLbl], Cfg1, set_from_list([StartLbl])).
find_bs_get_integer([Lbl|Rest], Cfg, Visited) ->
BB = hipe_icode_cfg:bb(Cfg, Lbl),
@@ -55,10 +55,10 @@ find_bs_get_integer([Lbl|Rest], Cfg, Visited) ->
not_ok ->
Cfg
end,
- Succs = ordsets:from_list(hipe_icode_cfg:succ(NewCfg, Lbl)),
- NewSuccs = ordsets:subtract(Succs, Visited),
- NewLbls = ordsets:union(NewSuccs, Rest),
- NewVisited = ordsets:union(NewSuccs, Visited),
+ Succs = hipe_icode_cfg:succ(NewCfg, Lbl),
+ NewSuccs = not_visited(Succs, Visited),
+ NewLbls = NewSuccs ++ Rest,
+ NewVisited = set_union(set_from_list(NewSuccs), Visited),
find_bs_get_integer(NewLbls, NewCfg, NewVisited);
find_bs_get_integer([], Cfg, _) ->
Cfg.
@@ -177,3 +177,19 @@ make_butlast([{Res, Size}|Rest], Var) ->
[Var, hipe_icode:mk_const((1 bsl Size)-1)]),
hipe_icode:mk_primop([NewVar], 'bsr', [Var, hipe_icode:mk_const(Size)])
|make_butlast(Rest, NewVar)].
+
+%%--------------------------------------------------------------------
+%% Sets
+
+set_from_list([]) -> #{};
+set_from_list(L) ->
+ maps:from_list([{E, []} || E <- L]).
+
+not_visited([], _) -> [];
+not_visited([E|T], M) ->
+ case M of
+ #{E := _} -> not_visited(T, M);
+ _ -> [E|not_visited(T, M)]
+ end.
+
+set_union(A, B) -> maps:merge(A, B).
diff --git a/lib/hipe/icode/hipe_icode_coordinator.erl b/lib/hipe/icode/hipe_icode_coordinator.erl
index d2f8748535..b073954ce7 100644
--- a/lib/hipe/icode/hipe_icode_coordinator.erl
+++ b/lib/hipe/icode/hipe_icode_coordinator.erl
@@ -106,12 +106,29 @@ handle_no_change_done(MFA, {Queue, Busy}) ->
{Queue, Busy -- [MFA]}.
last_action(PM, ServerPid, Mod, All) ->
- lists:foreach(fun (MFA) ->
- gb_trees:get(MFA, PM) ! {done, final_funs(ServerPid, Mod)},
- receive
- {done_rewrite, MFA} -> ok
- end
- end, All).
+ last_action(PM, ServerPid, Mod, All, []).
+
+last_action(_, _, _, [], []) -> ok;
+last_action(PM, ServerPid, Mod, [], [MFA|Busy]) ->
+ receive
+ {done_rewrite, MFA} ->
+ last_action(PM, ServerPid, Mod, [], Busy)
+ end;
+last_action(PM, ServerPid, Mod, All0, Busy) ->
+ receive
+ {done_rewrite, MFA} ->
+ last_action(PM, ServerPid, Mod, All0, Busy -- [MFA])
+ after 0 ->
+ case ?MAX_CONCURRENT - length(Busy) of
+ X when is_integer(X), X > 0 ->
+ [MFA|All1] = All0,
+ gb_trees:get(MFA, PM) ! {done, final_funs(ServerPid, Mod)},
+ last_action(PM, ServerPid, Mod, All1, [MFA|Busy]);
+ X when is_integer(X) ->
+ Busy1 = receive {done_rewrite, MFA} -> Busy -- [MFA] end,
+ last_action(PM, ServerPid, Mod, All0, Busy1)
+ end
+ end.
restart_funs({Queue, Busy} = QB, PM, All, ServerPid) ->
case ?MAX_CONCURRENT - length(Busy) of
diff --git a/lib/hipe/icode/hipe_icode_range.erl b/lib/hipe/icode/hipe_icode_range.erl
index 12ed796690..af160769a1 100644
--- a/lib/hipe/icode/hipe_icode_range.erl
+++ b/lib/hipe/icode/hipe_icode_range.erl
@@ -73,8 +73,8 @@
-type final_fun() :: fun((mfa(), [range()]) -> 'ok').
-type data() :: {mfa(), args_fun(), call_fun(), final_fun()}.
-type label() :: non_neg_integer().
--type info() :: gb_trees:tree().
--type work_list() :: {[label()], [label()], sets:set()}.
+-type info() :: map().
+-type work_list() :: {[label()], [label()], set(label())}.
-type variable() :: #icode_variable{}.
-type annotated_variable() :: #icode_variable{}.
-type argument() :: #icode_const{} | variable().
@@ -82,10 +82,9 @@
-type instr_split_info() :: {icode_instr(), [{label(),info()}]}.
-type last_instr_return() :: {instr_split_info(), range()}.
--record(state, {info_map = gb_trees:empty() :: info(),
- counter = dict:new() :: dict:dict(),
- cfg :: cfg(),
- liveness = gb_trees:empty() :: gb_trees:tree(),
+-record(state, {info_map = #{} :: info(),
+ cfg :: cfg(),
+ liveness :: hipe_icode_ssa:liveness(),
ret_type :: range(),
lookup_fun :: call_fun(),
result_action :: final_fun()}).
@@ -187,17 +186,16 @@ safe_analyse(CFG, Data={MFA,_,_,_}) ->
rewrite_blocks(State) ->
CFG = state__cfg(State),
Start = hipe_icode_cfg:start_label(CFG),
- rewrite_blocks([Start], State, [Start]).
+ rewrite_blocks([Start], State, set_from_list([Start])).
--spec rewrite_blocks([label()], state(), [label()]) -> state().
+-spec rewrite_blocks([label()], state(), set(label())) -> state().
rewrite_blocks([Next|Rest], State, Visited) ->
Info = state__info_in(State, Next),
{NewState, NewLabels} = analyse_block(Next, Info, State, true),
- NewLabelsSet = ordsets:from_list(NewLabels),
- RealNew = ordsets:subtract(NewLabelsSet, Visited),
- NewVisited = ordsets:union([RealNew, Visited, [Next]]),
- NewWork = ordsets:union([RealNew, Rest]),
+ RealNew = not_visited(NewLabels, Visited),
+ NewVisited = set_union(set_from_list(RealNew), Visited),
+ NewWork = RealNew ++ Rest,
rewrite_blocks(NewWork, NewState, NewVisited);
rewrite_blocks([], State, _) ->
State.
@@ -1661,8 +1659,8 @@ state__init(Cfg, {MFA, ArgsFun, CallFun, FinalFun}) ->
false ->
NewParams = lists:zipwith(fun update_info/2, Params, Ranges),
NewCfg = hipe_icode_cfg:params_update(Cfg, NewParams),
- Info = enter_defines(NewParams, gb_trees:empty()),
- InfoMap = gb_trees:insert({Start, in}, Info, gb_trees:empty()),
+ Info = enter_defines(NewParams, #{}),
+ InfoMap = #{{Start, in} => Info},
#state{info_map=InfoMap, cfg=NewCfg, liveness=Liveness,
ret_type=none_type(),
lookup_fun=CallFun, result_action=FinalFun}
@@ -1700,7 +1698,7 @@ state__info_in(S, Label) ->
state__info(S, {Label, in}).
state__info(#state{info_map=IM}, Key) ->
- gb_trees:get(Key, IM).
+ maps:get(Key, IM).
state__update_info(State, LabelInfo, Rewrite) ->
update_info(LabelInfo, State, [], Rewrite).
@@ -1721,60 +1719,58 @@ update_info([], State, LabelAcc, _Rewrite) ->
state__info_in_update(S=#state{info_map=IM,liveness=Liveness}, Label, Info) ->
LabelIn = {Label, in},
- case gb_trees:lookup(LabelIn, IM) of
- none ->
+ case IM of
+ #{LabelIn := OldInfo} ->
+ OldVars = maps:keys(OldInfo),
+ case join_info_in(OldVars, OldInfo, Info) of
+ fixpoint ->
+ fixpoint;
+ NewInfo ->
+ S#state{info_map=IM#{LabelIn := NewInfo}}
+ end;
+ _ ->
LiveIn = hipe_icode_ssa:ssa_liveness__livein(Liveness, Label),
NamesLiveIn = [hipe_icode:var_name(Var) || Var <- LiveIn,
hipe_icode:is_var(Var)],
- OldInfo = gb_trees:empty(),
+ OldInfo = #{},
case join_info_in(NamesLiveIn, OldInfo, Info) of
fixpoint ->
- S#state{info_map=gb_trees:insert(LabelIn, OldInfo, IM)};
- NewInfo ->
- S#state{info_map=gb_trees:enter(LabelIn, NewInfo, IM)}
- end;
- {value, OldInfo} ->
- OldVars = gb_trees:keys(OldInfo),
- case join_info_in(OldVars, OldInfo, Info) of
- fixpoint ->
- fixpoint;
+ S#state{info_map=IM#{LabelIn => OldInfo}};
NewInfo ->
- S#state{info_map=gb_trees:update(LabelIn, NewInfo, IM)}
+ S#state{info_map=IM#{LabelIn => NewInfo}}
end
end.
join_info_in(Vars, OldInfo, NewInfo) ->
- case join_info_in(Vars, OldInfo, NewInfo, gb_trees:empty(), false) of
+ case join_info_in(Vars, OldInfo, NewInfo, #{}, false) of
{Res, true} -> Res;
{_, false} -> fixpoint
end.
join_info_in([Var|Left], Info1, Info2, Acc, Changed) ->
- Type1 = gb_trees:lookup(Var, Info1),
- Type2 = gb_trees:lookup(Var, Info2),
- case {Type1, Type2} of
- {none, none} ->
- NewTree = gb_trees:insert(Var, none_type(), Acc),
- join_info_in(Left, Info1, Info2, NewTree, true);
- {none, {value, Val}} ->
- NewTree = gb_trees:insert(Var, Val, Acc),
- join_info_in(Left, Info1, Info2, NewTree, true);
- {{value, Val}, none} ->
- NewTree = gb_trees:insert(Var, Val, Acc),
+ case {Info1, Info2} of
+ {#{Var := Val}, #{Var := Val}} ->
+ NewTree = Acc#{Var => Val},
join_info_in(Left, Info1, Info2, NewTree, Changed);
- {{value, Val}, {value, Val}} ->
- NewTree = gb_trees:insert(Var, Val, Acc),
- join_info_in(Left, Info1, Info2, NewTree, Changed);
- {{value, Val1}, {value, Val2}} ->
- {NewChanged, NewVal} =
+ {#{Var := Val1}, #{Var := Val2}} ->
+ {NewChanged, NewVal} =
case sup(Val1, Val2) of
Val1 ->
{Changed, Val1};
Val ->
{true, Val}
end,
- NewTree = gb_trees:insert(Var, NewVal, Acc),
- join_info_in(Left, Info1, Info2, NewTree, NewChanged)
+ NewTree = Acc#{Var => NewVal},
+ join_info_in(Left, Info1, Info2, NewTree, NewChanged);
+ {_, #{Var := Val}} ->
+ NewTree = Acc#{Var => Val},
+ join_info_in(Left, Info1, Info2, NewTree, true);
+ {#{Var := Val}, _} ->
+ NewTree = Acc#{Var => Val},
+ join_info_in(Left, Info1, Info2, NewTree, Changed);
+ {_, _} ->
+ NewTree = Acc#{Var => none_type()},
+ join_info_in(Left, Info1, Info2, NewTree, true)
end;
join_info_in([], _Info1, _Info2, Acc, NewChanged) ->
{Acc, NewChanged}.
@@ -1786,7 +1782,7 @@ enter_defines([], Info) -> Info.
enter_define({PossibleVar, Range = #range{}}, Info) ->
case hipe_icode:is_var(PossibleVar) of
true ->
- gb_trees:enter(hipe_icode:var_name(PossibleVar), Range, Info);
+ Info#{hipe_icode:var_name(PossibleVar) => Range};
false ->
Info
end;
@@ -1795,7 +1791,7 @@ enter_define(PossibleVar, Info) ->
true ->
case hipe_icode:variable_annotation(PossibleVar) of
{range_anno, #ann{range=Range}, _} ->
- gb_trees:enter(hipe_icode:var_name(PossibleVar), Range, Info);
+ Info#{hipe_icode:var_name(PossibleVar) => Range};
_ ->
Info
end;
@@ -1810,11 +1806,10 @@ enter_vals(Ins, Info) ->
lookup(PossibleVar, Info) ->
case hipe_icode:is_var(PossibleVar) of
true ->
- case gb_trees:lookup(hipe_icode:var_name(PossibleVar), Info) of
- none ->
- none_type();
- {value, Val} ->
- Val
+ PossibleVarName = hipe_icode:var_name(PossibleVar),
+ case Info of
+ #{PossibleVarName := Val} -> Val;
+ _ -> none_type()
end;
false ->
none_type()
@@ -1828,10 +1823,10 @@ lookup(PossibleVar, Info) ->
init_work(State) ->
%% Labels = hipe_icode_cfg:reverse_postorder(state__cfg(State)),
Labels = [hipe_icode_cfg:start_label(state__cfg(State))],
- {Labels, [], sets:from_list(Labels)}.
+ {Labels, [], set_from_list(Labels)}.
get_work({[Label|Left], List, Set}) ->
- NewWork = {Left, List, sets:del_element(Label, Set)},
+ NewWork = {Left, List, maps:remove(Label, Set)},
{Label, NewWork};
get_work({[], [], _Set}) ->
fixpoint;
@@ -1839,12 +1834,12 @@ get_work({[], List, Set}) ->
get_work({lists:reverse(List), [], Set}).
add_work(Work = {List1, List2, Set}, [Label|Left]) ->
- case sets:is_element(Label, Set) of
- true ->
+ case Set of
+ #{Label := _} ->
add_work(Work, Left);
- false ->
+ _ ->
%% io:format("Adding work: ~w\n", [Label]),
- add_work({List1, [Label|List2], sets:add_element(Label, Set)}, Left)
+ add_work({List1, [Label|List2], Set#{Label => []}}, Left)
end;
add_work(Work, []) ->
Work.
@@ -1959,3 +1954,21 @@ next_down_limit(X) when is_integer(X), X > -16#8000000 -> -16#8000000;
next_down_limit(X) when is_integer(X), X > -16#80000000 -> -16#80000000;
next_down_limit(X) when is_integer(X), X > -16#800000000000000 -> -16#800000000000000;
next_down_limit(_X) -> neg_inf.
+
+%%--------------------------------------------------------------------
+%% Sets
+
+-type set(E) :: #{E => []}.
+
+set_from_list([]) -> #{};
+set_from_list(L) ->
+ maps:from_list([{E, []} || E <- L]).
+
+not_visited([], _) -> [];
+not_visited([E|T], M) ->
+ case M of
+ #{E := []} -> not_visited(T, M);
+ _ -> [E|not_visited(T, M)]
+ end.
+
+set_union(A, B) -> maps:merge(A, B).
diff --git a/lib/hipe/icode/hipe_icode_ssa.erl b/lib/hipe/icode/hipe_icode_ssa.erl
index b222fbc7d2..aca13a2ff0 100644
--- a/lib/hipe/icode/hipe_icode_ssa.erl
+++ b/lib/hipe/icode/hipe_icode_ssa.erl
@@ -34,13 +34,16 @@
-define(LIVENESS, hipe_icode_liveness).
-define(LIVENESS_NEEDED, true).
+-export_type([liveness/0]).
+
-include("hipe_icode.hrl").
-include("../ssa/hipe_ssa.inc").
%% Declarations for exported functions which are Icode-specific.
--spec ssa_liveness__analyze(#cfg{}) -> gb_trees:tree().
--spec ssa_liveness__livein(_, icode_lbl()) -> [#icode_variable{}].
-%% -spec ssa_liveness__livein(_, icode_lbl(), _) -> [#icode_var{}].
+-opaque liveness() :: liveness(icode_lbl(), #icode_variable{}).
+-spec ssa_liveness__analyze(#cfg{}) -> liveness().
+-spec ssa_liveness__livein(liveness(), icode_lbl()) -> [#icode_variable{}].
+%% -spec ssa_liveness__livein(liveness(), icode_lbl(), _) -> [#icode_var{}].
%%----------------------------------------------------------------------
%% Auxiliary operations which seriously differ between Icode and RTL.
diff --git a/lib/hipe/icode/hipe_icode_type.erl b/lib/hipe/icode/hipe_icode_type.erl
index 794c27ebcc..3f0e2998f1 100644
--- a/lib/hipe/icode/hipe_icode_type.erl
+++ b/lib/hipe/icode/hipe_icode_type.erl
@@ -100,7 +100,7 @@
-record(state, {info_map = gb_trees:empty() :: gb_trees:tree(),
cfg :: cfg(),
- liveness = gb_trees:empty() :: gb_trees:tree(),
+ liveness :: hipe_icode_ssa:liveness(),
arg_types :: [erl_types:erl_type()],
ret_type = [t_none()] :: [erl_types:erl_type()],
lookupfun :: call_fun(),
diff --git a/lib/hipe/main/hipe.app.src b/lib/hipe/main/hipe.app.src
index f8487151d7..acae2c637d 100644
--- a/lib/hipe/main/hipe.app.src
+++ b/lib/hipe/main/hipe.app.src
@@ -171,6 +171,7 @@
hipe_rtl_to_sparc,
hipe_rtl_to_x86,
hipe_rtl_varmap,
+ hipe_segment_trees,
hipe_sdi,
hipe_sparc,
hipe_sparc_assemble,
diff --git a/lib/hipe/misc/Makefile b/lib/hipe/misc/Makefile
index 72cfff21a8..e5033e444b 100644
--- a/lib/hipe/misc/Makefile
+++ b/lib/hipe/misc/Makefile
@@ -44,7 +44,7 @@ RELSYSDIR = $(RELEASE_PATH)/lib/hipe-$(VSN)
# Target Specs
# ----------------------------------------------------
ifdef HIPE_ENABLED
-HIPE_MODULES = hipe_data_pp hipe_pack_constants hipe_sdi
+HIPE_MODULES = hipe_data_pp hipe_pack_constants hipe_sdi hipe_segment_trees
else
HIPE_MODULES =
endif
diff --git a/lib/hipe/misc/hipe_sdi.erl b/lib/hipe/misc/hipe_sdi.erl
index fbb4b105f6..5ca64bc669 100644
--- a/lib/hipe/misc/hipe_sdi.erl
+++ b/lib/hipe/misc/hipe_sdi.erl
@@ -36,10 +36,13 @@
%%------------------------------------------------------------------------
-type hipe_array() :: integer(). % declare this in hipe.hrl or builtin?
+-type hipe_vector(E) :: {} | {E} | {E, E} | {E, E, E} | tuple().
-type label() :: non_neg_integer().
-type address() :: non_neg_integer().
+-type parents() :: {hipe_vector(_ :: integer()), hipe_segment_trees:tree()}.
+
%%------------------------------------------------------------------------
-record(label_data, {address :: address(),
@@ -168,9 +171,11 @@ mk_long(N) ->
%%% - Since the graph is traversed from child to parent nodes in
%%% Step 3, the edges are represented by a vector PARENTS[0..n-1]
%%% such that PARENTS[j] = { i | i is a parent of j }.
-%%% - An explicit PARENTS graph would have size O(n^2). Instead we
-%%% compute PARENTS[j] from the SDI vector when needed. This
-%%% reduces memory overheads, and may reduce time overheads too.
+%%% - An explicit PARENTS graph would have size O(n^2). Instead, we
+%%% observe that (i is a parent of j) iff (j \in range(i)), where
+%%% range(i) is a constant function. We can thus precompute all the
+%%% ranges i and insert them into a data structure built for such
+%%% queries. In this case, we use a segment tree.
-spec mk_span(non_neg_integer(), tuple()) -> hipe_array().
mk_span(N, SDIS) ->
@@ -188,7 +193,29 @@ initSPAN(SdiNr, N, SDIS, SPAN) ->
initSPAN(SdiNr+1, N, SDIS, SPAN)
end.
-mk_parents(N, SDIS) -> {N,SDIS}.
+-spec mk_parents(non_neg_integer(), tuple()) -> parents().
+mk_parents(N, SDIS) ->
+ PrevSDIS = vector_from_list(select_prev_sdis(N-1, SDIS, [])),
+ Ranges = parents_generate_ranges(N-1, PrevSDIS, []),
+ {PrevSDIS, hipe_segment_trees:build(Ranges)}.
+
+select_prev_sdis(-1, _SDIS, Acc) -> Acc;
+select_prev_sdis(SdiNr, SDIS, Acc) ->
+ #sdi_data{prevSdi=PrevSdi} = vector_sub(SDIS, SdiNr),
+ select_prev_sdis(SdiNr-1, SDIS, [PrevSdi|Acc]).
+
+parents_generate_ranges(-1, _PrevSDIS, Acc) -> Acc;
+parents_generate_ranges(SdiNr, PrevSDIS, Acc) ->
+ %% inclusive
+ {LO,HI} = parents_generate_range(SdiNr, PrevSDIS),
+ parents_generate_ranges(SdiNr-1, PrevSDIS, [{LO,HI}|Acc]).
+
+-compile({inline, parents_generate_range/2}).
+parents_generate_range(SdiNr, PrevSDIS) ->
+ PrevSdi = vector_sub(PrevSDIS, SdiNr),
+ if SdiNr =< PrevSdi -> {SdiNr+1, PrevSdi}; % forwards
+ true -> {PrevSdi+1, SdiNr-1} % backwards
+ end.
%%% "After the structure is built we process it as follows.
%%% For any node i whose listed span exceeds the architectural
@@ -209,7 +236,7 @@ mk_parents(N, SDIS) -> {N,SDIS}.
%%% and PARENTS are no longer useful.
-spec update_long(non_neg_integer(), tuple(), hipe_array(),
- {non_neg_integer(),tuple()},hipe_array()) -> 'ok'.
+ parents(),hipe_array()) -> 'ok'.
update_long(N, SDIS, SPAN, PARENTS, LONG) ->
WKL = initWKL(N-1, SDIS, SPAN, []),
processWKL(WKL, SDIS, SPAN, PARENTS, LONG).
@@ -225,46 +252,32 @@ initWKL(SdiNr, SDIS, SPAN, WKL) ->
end.
-spec processWKL([non_neg_integer()], tuple(), hipe_array(),
- {non_neg_integer(), tuple()}, hipe_array()) -> 'ok'.
+ parents(), hipe_array()) -> 'ok'.
processWKL([], _SDIS, _SPAN, _PARENTS, _LONG) -> ok;
-processWKL([Child|WKL], SDIS, SPAN, PARENTS, LONG) ->
- WKL2 = updateChild(Child, WKL, SDIS, SPAN, PARENTS, LONG),
+processWKL([Child|WKL], SDIS, SPAN, PARENTS0, LONG) ->
+ {WKL2, PARENTS} =
+ case array_sub(SPAN, Child) of
+ 0 -> {WKL, PARENTS0}; % removed
+ _ ->
+ SdiData = vector_sub(SDIS, Child),
+ Incr = sdiLongIncr(SdiData),
+ array_update(LONG, Child, Incr),
+ array_update(SPAN, Child, 0), % remove child
+ PARENTS1 = deleteParent(PARENTS0, Child),
+ PS = parentsOfChild(PARENTS1, Child),
+ {updateParents(PS, Child, Incr, SDIS, SPAN, WKL), PARENTS1}
+ end,
processWKL(WKL2, SDIS, SPAN, PARENTS, LONG).
--spec updateChild(non_neg_integer(), [non_neg_integer()], tuple(), hipe_array(),
- {non_neg_integer(),tuple()}, hipe_array()) -> [non_neg_integer()].
-updateChild(Child, WKL, SDIS, SPAN, PARENTS, LONG) ->
- case array_sub(SPAN, Child) of
- 0 -> WKL; % removed
- _ ->
- SdiData = vector_sub(SDIS, Child),
- Incr = sdiLongIncr(SdiData),
- array_update(LONG, Child, Incr),
- array_update(SPAN, Child, 0), % remove child
- PS = parentsOfChild(PARENTS, Child),
- updateParents(PS, Child, Incr, SDIS, SPAN, WKL)
- end.
+-spec parentsOfChild(parents(), non_neg_integer()) -> [non_neg_integer()].
+parentsOfChild({_PrevSDIS, SegTree}, Child) ->
+ hipe_segment_trees:intersect(Child, SegTree).
--spec parentsOfChild({non_neg_integer(),tuple()},
- non_neg_integer()) -> [non_neg_integer()].
-parentsOfChild({N,SDIS}, Child) ->
- parentsOfChild(N-1, SDIS, Child, []).
-
--spec parentsOfChild(integer(), tuple(), non_neg_integer(),
- [non_neg_integer()]) -> [non_neg_integer()].
-parentsOfChild(-1, _SDIS, _Child, PS) -> PS;
-parentsOfChild(SdiNr, SDIS, Child, PS) ->
- SdiData = vector_sub(SDIS, SdiNr),
- #sdi_data{prevSdi=PrevSdi} = SdiData,
- {LO,HI} = % inclusive
- if SdiNr =< PrevSdi -> {SdiNr+1, PrevSdi}; % forwards
- true -> {PrevSdi+1, SdiNr-1} % backwards
- end,
- NewPS =
- if LO =< Child, Child =< HI -> [SdiNr | PS];
- true -> PS
- end,
- parentsOfChild(SdiNr-1, SDIS, Child, NewPS).
+-spec deleteParent(parents(), non_neg_integer()) -> parents().
+deleteParent({PrevSDIS, SegTree0}, Parent) ->
+ {LO,HI} = parents_generate_range(Parent, PrevSDIS),
+ SegTree = hipe_segment_trees:delete(Parent, LO, HI, SegTree0),
+ {PrevSDIS, SegTree}.
-spec updateParents([non_neg_integer()], non_neg_integer(),
byte(), tuple(), hipe_array(),
@@ -297,10 +310,12 @@ updateWKL(SdiNr, SDIS, SdiSpan, WKL) ->
false -> [SdiNr|WKL]
end.
+-compile({inline, sdiSpanIsShort/2}). %% Only called once
-spec sdiSpanIsShort(#sdi_data{}, integer()) -> boolean().
sdiSpanIsShort(#sdi_data{si = #sdi_info{lb = LB, ub = UB}}, SdiSpan) ->
SdiSpan >= LB andalso SdiSpan =< UB.
+-compile({inline, sdiLongIncr/1}). %% Only called once
-spec sdiLongIncr(#sdi_data{}) -> byte().
sdiLongIncr(#sdi_data{si = #sdi_info{incr = Incr}}) -> Incr.
@@ -361,9 +376,11 @@ applyIncr([{Label,LabelData}|List], INCREMENT, LabelMap) ->
%%% Currently implemented as tuples.
%%% Used for the 'SDIS' and 'PARENTS' vectors.
--spec vector_from_list([#sdi_data{}]) -> tuple().
+-spec vector_from_list([E]) -> hipe_vector(E).
vector_from_list(Values) -> list_to_tuple(Values).
+-compile({inline, vector_sub/2}).
+-spec vector_sub(hipe_vector(E), non_neg_integer()) -> V when V :: E.
vector_sub(Vec, I) -> element(I+1, Vec).
%%% ADT for mutable integer arrays, indexed from 0 to N-1.
@@ -373,8 +390,10 @@ vector_sub(Vec, I) -> element(I+1, Vec).
-spec mk_array_of_zeros(non_neg_integer()) -> hipe_array().
mk_array_of_zeros(N) -> hipe_bifs:array(N, 0).
+-compile({inline, array_update/3}).
-spec array_update(hipe_array(), non_neg_integer(), integer()) -> hipe_array().
array_update(A, I, V) -> hipe_bifs:array_update(A, I, V).
+-compile({inline, array_sub/2}).
-spec array_sub(hipe_array(), non_neg_integer()) -> integer().
array_sub(A, I) -> hipe_bifs:array_sub(A, I).
diff --git a/lib/hipe/misc/hipe_segment_trees.erl b/lib/hipe/misc/hipe_segment_trees.erl
new file mode 100644
index 0000000000..22146396c3
--- /dev/null
+++ b/lib/hipe/misc/hipe_segment_trees.erl
@@ -0,0 +1,181 @@
+%%%
+%%% %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%
+%%%
+%%% Segment trees, with a delete operation.
+%%%
+%%% Keys are the (0-based) indices into the list passed to build/1.
+%%%
+%%% Range bounds are inclusive.
+%%%
+
+-module(hipe_segment_trees).
+
+-export([build/1, intersect/2, delete/4]).
+
+-record(segment_tree, {
+ lo :: integer(),
+ hi :: integer(),
+ root :: tnode()
+ }).
+
+%% X =< Mid belongs in Left
+-define(NODE(Left, Right, Mid, Segments), {Left, Right, Mid, Segments}).
+
+-define(POINT_LEAF(Val), Val).
+-define(RANGE_LEAF(Lo, Hi), {Lo, Hi}).
+
+-type segments() :: [non_neg_integer()].
+-type leaf() :: segments().
+-type tnode() :: ?NODE(tnode(), tnode(), integer(), segments()) | leaf().
+
+-opaque tree() :: #segment_tree{} | nil.
+-export_type([tree/0]).
+
+%% @doc Builds a segment tree of the given intervals.
+-spec build([{integer(), integer()}]) -> tree().
+build(ListOfIntervals) ->
+ case
+ lists:usort(
+ lists:append(
+ [[Lo, Hi] || {Lo, Hi} <- ListOfIntervals, Lo =< Hi]))
+ of
+ [] -> nil;
+ Endpoints ->
+ Tree0 = empty_tree_from_endpoints(Endpoints),
+ [Lo|_] = Endpoints,
+ Hi = lists:last(Endpoints),
+ Tree1 = insert_intervals(0, ListOfIntervals, Lo, Hi, Tree0),
+ Tree = squash_empty_subtrees(Tree1),
+ #segment_tree{lo=Lo, hi=Hi, root=Tree}
+ end.
+
+empty_tree_from_endpoints(Endpoints) ->
+ Leaves = leaves(Endpoints),
+ {T, [], _, _} = balanced_bst(Leaves, length(Leaves)),
+ T.
+
+leaves([Endpoint]) -> [?POINT_LEAF(Endpoint)];
+leaves([A | [B|_] = Tail]) ->
+ %% We omit the range leaf if it's empty
+ case A<B-1 of
+ true -> [?POINT_LEAF(A),?RANGE_LEAF(A+1,B-1) | leaves(Tail)];
+ false -> [?POINT_LEAF(A) | leaves(Tail)]
+ end.
+
+balanced_bst(L, S) when S > 1 ->
+ Sm = S, %% - 1
+ S2 = Sm div 2,
+ S1 = Sm - S2,
+ {Left, L1, LeftLo, LeftHi} = balanced_bst(L, S1),
+ {Right, L2, _, RightHi} = balanced_bst(L1, S2),
+ T = ?NODE(Left, Right, LeftHi, []),
+ {T, L2, LeftLo, RightHi};
+balanced_bst([?RANGE_LEAF(Lo, Hi) | L], 1) ->
+ {[], L, Lo, Hi};
+balanced_bst([?POINT_LEAF(Val) | L], 1) ->
+ {[], L, Val, Val}.
+
+insert_intervals(_Ix, [], _Lo, _Hi, Tree) -> Tree;
+insert_intervals(Ix, [Int|Ints], Lo, Hi, Tree) ->
+ insert_intervals(Ix + 1, Ints, Lo, Hi,
+ insert_interval(Ix, Int, Lo, Hi, Tree)).
+
+insert_interval(_, {Lo, Hi}, _, _, Node) when Lo > Hi -> Node;
+insert_interval(I, Int={Lo,Hi}, NLo, NHi,
+ ?NODE(Left0, Right0, Mid, Segments)) ->
+ if Lo =< NLo, NHi =< Hi ->
+ ?NODE(Left0, Right0, Mid, [I|Segments]);
+ true ->
+ Left = case intervals_intersect(Lo, Hi, NLo, Mid) of
+ true -> insert_interval(I, Int, NLo, Mid, Left0);
+ false -> Left0
+ end,
+ Right = case intervals_intersect(Lo, Hi, Mid+1, NHi) of
+ true -> insert_interval(I, Int, Mid+1, NHi, Right0);
+ false -> Right0
+ end,
+ ?NODE(Left, Right, Mid, Segments)
+ end;
+insert_interval(I, {_Lo,_Hi}, _NLo, _NHi, Leaf) -> [I|Leaf].
+
+intervals_intersect(ALo, AHi, BLo, BHi) ->
+ (ALo =< AHi) andalso (BLo =< BHi) %% both nonempty
+ andalso nonempty_intervals_intersect(ALo, AHi, BLo, BHi).
+
+%% Purely optional optimisation
+squash_empty_subtrees(?NODE(Left0, Right0, Mid, Segs)) ->
+ build_squash_node(squash_empty_subtrees(Left0),
+ squash_empty_subtrees(Right0),
+ Mid, Segs);
+squash_empty_subtrees(Leaf) -> Leaf.
+
+build_squash_node([], [], _, Segs) -> Segs;
+build_squash_node(Left, Right, Mid, Segs) ->
+ ?NODE(Left, Right, Mid, Segs).
+
+%% @doc Returns the indices of the intervals in the tree that contains Point.
+-spec intersect(integer(), tree()) -> [non_neg_integer()].
+intersect(Point, nil) when is_integer(Point) -> [];
+intersect(Point, #segment_tree{lo=Lo, hi=Hi, root=Root})
+ when is_integer(Point) ->
+ case Lo =< Point andalso Point =< Hi of
+ false -> [];
+ true -> intersect_1(Point, Root, [])
+ end.
+
+intersect_1(Point, ?NODE(Left, Right, Mid, Segs), Acc0) ->
+ Child = if Point =< Mid -> Left; true -> Right end,
+ intersect_1(Point, Child, Segs ++ Acc0);
+intersect_1(_, LeafSegs, Acc) -> LeafSegs ++ Acc.
+
+%% @doc Deletes the interval {Lo, Hi}, which had index Index in the list passed
+%% to build/1.
+-spec delete(non_neg_integer(), integer(), integer(), tree()) -> tree().
+delete(_, _, _, nil) -> nil;
+delete(_, Lo, Hi, Tree) when Lo > Hi -> Tree;
+delete(_, Lo, Hi, Tree = #segment_tree{lo=TLo, hi=THi})
+ when Hi < TLo; Lo > THi -> Tree;
+delete(Index, Lo, Hi, Tree = #segment_tree{lo=TLo, hi=THi, root=Root0})
+ when is_integer(Lo), is_integer(Hi) ->
+ Root = delete_1(Index, Lo, Hi, TLo, THi, Root0),
+ Tree#segment_tree{root=Root}.
+
+delete_1(I, Lo, Hi, NLo, NHi, ?NODE(Left0, Right0, Mid, Segments)) ->
+ if Lo =< NLo, NHi =< Hi ->
+ ?NODE(Left0, Right0, Mid, delete_2(Segments, I));
+ true ->
+ Left = case nonempty_intervals_intersect(Lo, Hi, NLo, Mid) of
+ true -> delete_1(I, Lo, Hi, NLo, Mid, Left0);
+ false -> Left0
+ end,
+ Right = case nonempty_intervals_intersect(Lo, Hi, Mid+1, NHi) of
+ true -> delete_1(I, Lo, Hi, Mid+1, NHi, Right0);
+ false -> Right0
+ end,
+ %% We could do build_squash_node here, is it worth it?
+ ?NODE(Left, Right, Mid, Segments)
+ end;
+delete_1(I, _Lo, _Hi, _NLo, _NHi, Leaf) -> delete_2(Leaf, I).
+
+delete_2([I|Segs], I) -> Segs;
+delete_2([S|Segs], I) -> [S|delete_2(Segs,I)].
+
+-compile({inline,nonempty_intervals_intersect/4}).
+nonempty_intervals_intersect(ALo, AHi, BLo, BHi) ->
+ (BLo =< AHi) andalso (ALo =< BHi).
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/regalloc/Makefile b/lib/hipe/regalloc/Makefile
index aaa4418f37..ceb535f1c7 100644
--- a/lib/hipe/regalloc/Makefile
+++ b/lib/hipe/regalloc/Makefile
@@ -123,7 +123,6 @@ $(EBIN)/hipe_amd64_specific_x87.beam: hipe_x86_specific_x87.erl
$(EBIN)/hipe_coalescing_regalloc.beam: ../main/hipe.hrl
$(EBIN)/hipe_graph_coloring_regalloc.beam: ../main/hipe.hrl
$(EBIN)/hipe_ig.beam: ../main/hipe.hrl ../flow/cfg.hrl hipe_spillcost.hrl
-$(EBIN)/hipe_ig_moves.beam: ../util/hipe_vectors.hrl
$(EBIN)/hipe_ls_regalloc.beam: ../main/hipe.hrl
$(EBIN)/hipe_optimistic_regalloc.beam: ../main/hipe.hrl
$(EBIN)/hipe_regalloc_loop.beam: ../main/hipe.hrl
diff --git a/lib/hipe/regalloc/hipe_ig_moves.erl b/lib/hipe/regalloc/hipe_ig_moves.erl
index b679453de0..2a70606dab 100644
--- a/lib/hipe/regalloc/hipe_ig_moves.erl
+++ b/lib/hipe/regalloc/hipe_ig_moves.erl
@@ -25,8 +25,6 @@
new_move/3,
get_moves/1]).
--include("../util/hipe_vectors.hrl").
-
%%-----------------------------------------------------------------------------
%% The main data structure; its fields are:
%% - movelist : mapping from temp to set of associated move numbers
@@ -34,11 +32,13 @@
%% - moveinsns : list of move instructions, in descending move number order
%% - moveset : set of move instructions
--record(ig_moves, {movelist :: hipe_vector(),
+-record(ig_moves, {movelist :: movelist(),
nrmoves = 0 :: non_neg_integer(),
moveinsns = [] :: [{_,_}],
moveset = gb_sets:empty() :: gb_sets:set()}).
+-type movelist() :: hipe_vectors:vector(ordsets:ordset(non_neg_integer())).
+
%%-----------------------------------------------------------------------------
-spec new(non_neg_integer()) -> #ig_moves{}.
@@ -66,7 +66,8 @@ new_move(Dst, Src, IG_moves) ->
moveset = gb_sets:insert(MoveInsn, MoveSet)}
end.
--spec add_movelist(non_neg_integer(), non_neg_integer(), hipe_vector()) -> hipe_vector().
+-spec add_movelist(non_neg_integer(), non_neg_integer(), movelist())
+ -> movelist().
add_movelist(MoveNr, Temp, MoveList) ->
AssocMoves = hipe_vectors:get(MoveList, Temp),
@@ -74,7 +75,7 @@ add_movelist(MoveNr, Temp, MoveList) ->
%% ordset due to the ordsets:union in hipe_coalescing_regalloc:combine().
hipe_vectors:set(MoveList, Temp, ordsets:add_element(MoveNr, AssocMoves)).
--spec get_moves(#ig_moves{}) -> {hipe_vector(), non_neg_integer(), tuple()}.
+-spec get_moves(#ig_moves{}) -> {movelist(), non_neg_integer(), tuple()}.
get_moves(IG_moves) -> % -> {MoveList, NrMoves, MoveInsns}
{IG_moves#ig_moves.movelist,
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/ssa/hipe_ssa_liveness.inc b/lib/hipe/ssa/hipe_ssa_liveness.inc
index 78488c65fc..46df8b66ad 100644
--- a/lib/hipe/ssa/hipe_ssa_liveness.inc
+++ b/lib/hipe/ssa/hipe_ssa_liveness.inc
@@ -40,6 +40,15 @@
ssa_liveness__livein/2]).
%% ssa_liveness__livein/3],
%% ssa_liveness__liveout/2]).
+-type set(E) :: gb_sets:set(E).
+-type liveness(Label, Var) ::
+ #{Label => {{Gen :: set(Var),
+ Kill :: set(Var),
+ {TotalDirGen :: set(Var),
+ DirGen :: gb_trees:tree(Label, set(Var))}},
+ LiveIn :: set(Var),
+ LiveOut :: set(Var),
+ Successors :: [Label]}}.
-endif.
%% -ifdef(DEBUG_LIVENESS).
%% -export([pp_liveness/1]).
@@ -262,21 +271,15 @@ update_directed_gen({Pred, Var}, Map)->
%%
%% liveness
%%
+-compile({inline, [liveness_lookup/2, liveness_update/3]}).
liveness_init(List) ->
- liveness_init1(List, gb_trees:empty()).
+ maps:from_list(List).
-liveness_init1([{Label, Info}|Left], Map) ->
- liveness_init1(Left, gb_trees:insert(Label, Info, Map));
-liveness_init1([], Map) ->
- Map.
-
-liveness_lookup(Label, Map) ->
- {value, Info} = gb_trees:lookup(Label, Map),
- Info.
-
-liveness_update(Label, NewInfo, Map) ->
- gb_trees:update(Label, NewInfo, Map).
+liveness_lookup(Label, Liveness) ->
+ maps:get(Label, Liveness).
+liveness_update(Label, Val, Liveness) ->
+ maps:update(Label, Val, Liveness).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
diff --git a/lib/hipe/util/Makefile b/lib/hipe/util/Makefile
index 66e9421c25..04de7f7823 100644
--- a/lib/hipe/util/Makefile
+++ b/lib/hipe/util/Makefile
@@ -113,4 +113,3 @@ release_docs_spec:
$(EBIN)/hipe_timing.beam: ../main/hipe.hrl
-$(EBIN)/hipe_vectors.beam: hipe_vectors.hrl
diff --git a/lib/hipe/util/hipe_vectors.erl b/lib/hipe/util/hipe_vectors.erl
index 7f6c8e91c2..90d736d02c 100644
--- a/lib/hipe/util/hipe_vectors.erl
+++ b/lib/hipe/util/hipe_vectors.erl
@@ -33,11 +33,25 @@
%% list_to_vector/1,
list/1]).
--include("hipe_vectors.hrl").
+%%-define(USE_TUPLES, true).
+%%-define(USE_GBTREES, true).
+-define(USE_ARRAYS, true).
+
+-type vector() :: vector(_).
+-export_type([vector/0, vector/1]).
+
+-spec new(non_neg_integer(), V) -> vector(E) when V :: E.
+-spec set(vector(E), non_neg_integer(), V :: E) -> vector(E).
+-spec get(vector(E), non_neg_integer()) -> E.
+-spec size(vector(_)) -> non_neg_integer().
+-spec vector_to_list(vector(E)) -> [E].
+%% -spec list_to_vector([E]) -> vector(E).
+-spec list(vector(E)) -> [{non_neg_integer(), E}].
%% ---------------------------------------------------------------------
-ifdef(USE_TUPLES).
+-opaque vector(_) :: tuple().
new(N, V) ->
erlang:make_tuple(N, V).
@@ -68,8 +82,8 @@ get(Vec, Ix) -> element(Ix+1, Vec).
%% ---------------------------------------------------------------------
-ifdef(USE_GBTREES).
+-opaque vector(E) :: gb_trees:tree(non_neg_integer(), E).
--spec new(non_neg_integer(), _) -> hipe_vector().
new(N, V) when is_integer(N), N >= 0 ->
gb_trees:from_orddict(mklist(N, V)).
@@ -81,14 +95,11 @@ mklist(M, N, V) when M < N ->
mklist(_, _, _) ->
[].
--spec size(hipe_vector()) -> non_neg_integer().
size(V) -> gb_trees:size(V).
--spec list(hipe_vector()) -> [{_, _}].
list(Vec) ->
gb_trees:to_list(Vec).
-%% -spec list_to_vector([_]) -> hipe_vector().
%% list_to_vector(Xs) ->
%% gb_trees:from_orddict(index(Xs, 0)).
%%
@@ -97,16 +108,29 @@ list(Vec) ->
%% index([],_) ->
%% [].
--spec vector_to_list(hipe_vector()) -> [_].
vector_to_list(V) ->
gb_trees:values(V).
--spec set(hipe_vector(), non_neg_integer(), _) -> hipe_vector().
set(Vec, Ix, V) ->
gb_trees:update(Ix, V, Vec).
--spec get(hipe_vector(), non_neg_integer()) -> any().
get(Vec, Ix) ->
gb_trees:get(Ix, Vec).
-endif. %% ifdef USE_GBTREES
+
+%% ---------------------------------------------------------------------
+
+-ifdef(USE_ARRAYS).
+%%-opaque vector(E) :: array:array(E).
+-type vector(E) :: array:array(E). % Work around dialyzer bug
+
+new(N, V) -> array:new(N, {default, V}).
+size(V) -> array:size(V).
+list(Vec) -> array:to_orddict(Vec).
+%% list_to_vector(Xs) -> array:from_list(Xs).
+vector_to_list(V) -> array:to_list(V).
+set(Vec, Ix, V) -> array:set(Ix, V, Vec).
+get(Vec, Ix) -> array:get(Ix, Vec).
+
+-endif. %% ifdef USE_ARRAYS
diff --git a/lib/hipe/x86/hipe_x86_frame.erl b/lib/hipe/x86/hipe_x86_frame.erl
index 8851ead250..4cdc04007d 100644
--- a/lib/hipe/x86/hipe_x86_frame.erl
+++ b/lib/hipe/x86/hipe_x86_frame.erl
@@ -622,26 +622,31 @@ find_temps([I|Insns], S0) ->
find_temps([], S) ->
S.
+-compile({inline, [tset_empty/0, tset_size/1, tset_insert/2,
+ tset_filter/2, tset_to_list/1]}).
+
tset_empty() ->
- gb_sets:new().
+ #{}.
tset_size(S) ->
- gb_sets:size(S).
+ map_size(S).
tset_insert(S, T) ->
- gb_sets:add_element(T, S).
+ S#{T => []}.
-tset_add_list(S, Ts) ->
- gb_sets:union(S, gb_sets:from_list(Ts)).
+tset_add_list(S, []) -> S;
+tset_add_list(S, [T|Ts]) ->
+ tset_add_list(S#{T => []}, Ts).
-tset_del_list(S, Ts) ->
- gb_sets:subtract(S, gb_sets:from_list(Ts)).
+tset_del_list(S, []) -> S;
+tset_del_list(S, [T|Ts]) ->
+ tset_del_list(maps:remove(T,S), Ts).
tset_filter(S, F) ->
- gb_sets:filter(F, S).
+ maps:filter(fun(K, _V) -> F(K) end, S).
tset_to_list(S) ->
- gb_sets:to_list(S).
+ maps:keys(S).
%%%
%%% Compute minimum permissible frame size, ignoring spilled temps.
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/inets/doc/src/httpd.xml b/lib/inets/doc/src/httpd.xml
index 62b92b8356..b05ae72983 100644
--- a/lib/inets/doc/src/httpd.xml
+++ b/lib/inets/doc/src/httpd.xml
@@ -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
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..caa5a083a3 100644
--- a/lib/inets/doc/src/notes.xml
+++ b/lib/inets/doc/src/notes.xml
@@ -33,7 +33,22 @@
<file>notes.xml</file>
</header>
- <section><title>Inets 6.3.1</title>
+ <section><title>Inets 6.3.2</title>
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ PUT and DELETE support has been added to mod_esi</p>
+ <p>
+ Own Id: OTP-13688 Aux Id: seq13149 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Inets 6.3.1</title>
<section><title>Fixed Bugs and Malfunctions</title>
<list>
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/src/inets_app/inets.appup.src b/lib/inets/src/inets_app/inets.appup.src
index 3a31daeb20..d4d21f6774 100644
--- a/lib/inets/src/inets_app/inets.appup.src
+++ b/lib/inets/src/inets_app/inets.appup.src
@@ -18,10 +18,16 @@
%% %CopyrightEnd%
{"%VSN%",
[
+ {<<"6.3.1">>,
+ [{load_module, mod_esi, soft_purge, soft_purge, []}
+ ]},
{<<"6\\..*">>,[{restart_application, inets}]},
{<<"5\\..*">>,[{restart_application, inets}]}
],
[
+ {<<"6.3.1">>,
+ [{load_module, mod_esi, soft_purge, soft_purge, []}
+ ]},
{<<"6\\..*">>,[{restart_application, inets}]},
{<<"5\\..*">>,[{restart_application, inets}]}
]
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..747724a86b 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.2
PRE_VSN =
APP_VSN = "$(APPLICATION)-$(INETS_VSN)$(PRE_VSN)"
diff --git a/lib/jinterface/doc/src/Makefile b/lib/jinterface/doc/src/Makefile
index cd1e61a795..508c8e01b5 100644
--- a/lib/jinterface/doc/src/Makefile
+++ b/lib/jinterface/doc/src/Makefile
@@ -166,7 +166,7 @@ release_docs_spec: docs
$(INSTALL_DIR) "$(RELSYSDIR)/doc/html"
$(INSTALL_DIR) "$(RELSYSDIR)/doc/html/java/$(JAVA_PKG_PATH)"
$(INSTALL_DATA) $(INFO_FILE) "$(RELSYSDIR)"
- (/bin/cp -rf ../html "$(RELSYSDIR)/doc")
+ ($(CP) -rf ../html "$(RELSYSDIR)/doc")
# $(INSTALL_DATA) $(GIF_FILES) $(EXTRA_FILES) $(HTML_FILES) \
# "$(RELSYSDIR)/doc/html"
diff --git a/lib/kernel/doc/src/code.xml b/lib/kernel/doc/src/code.xml
index d3611d6a03..21f8a2d54a 100644
--- a/lib/kernel/doc/src/code.xml
+++ b/lib/kernel/doc/src/code.xml
@@ -651,6 +651,11 @@ ok = code:finish_loading(Prepared),
<p>Purges the code for <c><anno>Module</anno></c>, that is, removes code
marked as old. If some processes still linger in the old code,
these processes are killed before the code is removed.</p>
+ <note><p>As of ERTS version 9.0, a process is only considered
+ to be lingering in the code if it has direct references to the code.
+ For more information see documentation of
+ <seealso marker="erts:erlang#check_process_code/3"><c>erlang:check_process_code/3</c></seealso>,
+ which is used in order to determine this.</p></note>
<p>Returns <c>true</c> if successful and any process is needed to
be killed, otherwise <c>false</c>.</p>
</desc>
@@ -661,6 +666,11 @@ ok = code:finish_loading(Prepared),
<desc>
<p>Purges the code for <c><anno>Module</anno></c>, that is, removes code
marked as old, but only if no processes linger in it.</p>
+ <note><p>As of ERTS version 9.0, a process is only considered
+ to be lingering in the code if it has direct references to the code.
+ For more information see documentation of
+ <seealso marker="erts:erlang#check_process_code/3"><c>erlang:check_process_code/3</c></seealso>,
+ which is used in order to determine this.</p></note>
<p>Returns <c>false</c> if the module cannot be purged because
of processes lingering in old code, otherwise <c>true</c>.</p>
</desc>
diff --git a/lib/kernel/doc/src/inet.xml b/lib/kernel/doc/src/inet.xml
index c0dce2f50c..8cc21bf3e2 100644
--- a/lib/kernel/doc/src/inet.xml
+++ b/lib/kernel/doc/src/inet.xml
@@ -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
@@ -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>
diff --git a/lib/kernel/doc/src/kernel_app.xml b/lib/kernel/doc/src/kernel_app.xml
index 9e6fb60bb7..dc0291babe 100644
--- a/lib/kernel/doc/src/kernel_app.xml
+++ b/lib/kernel/doc/src/kernel_app.xml
@@ -215,12 +215,14 @@
<p>Defines the <c>First..Last</c> port range for the listener
socket of a distributed Erlang node.</p>
</item>
+ <marker id="inet_dist_listen_options"></marker>
<tag><c>{inet_dist_listen_options, Opts}</c></tag>
<item>
<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>
+ <marker id="inet_dist_connect_options"></marker>
<tag><c>{inet_dist_connect_options, Opts}</c></tag>
<item>
<p>Defines a list of extra socket options to be used when connecting to
diff --git a/lib/kernel/doc/src/net_kernel.xml b/lib/kernel/doc/src/net_kernel.xml
index f48a534d4f..3c1b3d5190 100644
--- a/lib/kernel/doc/src/net_kernel.xml
+++ b/lib/kernel/doc/src/net_kernel.xml
@@ -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>
@@ -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..3d35f6f57f 100644
--- a/lib/kernel/doc/src/notes.xml
+++ b/lib/kernel/doc/src/notes.xml
@@ -31,6 +31,41 @@
</header>
<p>This document describes the changes made to the Kernel application.</p>
+<section><title>Kernel 5.0.2</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ When calling os:cmd from a process that has set trap_exit
+ to true an 'EXIT' message would be left in the message
+ queue. This bug was introduced in kernel vsn 5.0.1.</p>
+ <p>
+ Own Id: OTP-13813</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Kernel 5.0.1</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Fix a os:cmd bug where creating a background job using
+ &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>
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 1792f9e908..48541ec500 100644
--- a/lib/kernel/src/code_server.erl
+++ b/lib/kernel/src/code_server.erl
@@ -135,10 +135,14 @@ split_paths([], _S, Path, Paths) ->
-spec call(term()) -> term().
call(Req) ->
+ Ref = erlang:monitor(process, ?MODULE),
?MODULE ! {code_call, self(), Req},
receive
{?MODULE, Reply} ->
- Reply
+ erlang:demonitor(Ref,[flush]),
+ Reply;
+ {'DOWN',Ref,process,_,_} ->
+ exit({'DOWN',code_server,Req})
end.
reply(Pid, Res) ->
diff --git a/lib/kernel/src/dist_util.erl b/lib/kernel/src/dist_util.erl
index 47d0c1b861..8d2fc4d4b7 100644
--- a/lib/kernel/src/dist_util.erl
+++ b/lib/kernel/src/dist_util.erl
@@ -143,7 +143,11 @@ handshake_other_started(#hs_data{request_type=ReqType}=HSData0) ->
ChallengeB = recv_challenge_reply(HSData, ChallengeA, MyCookie),
send_challenge_ack(HSData, gen_digest(ChallengeB, HisCookie)),
?debug({dist_util, self(), accept_connection, Node}),
- connection(HSData).
+ connection(HSData);
+
+handshake_other_started(OldHsData) when element(1,OldHsData) =:= hs_data ->
+ handshake_other_started(convert_old_hsdata(OldHsData)).
+
%%
%% check if connecting node is allowed to connect
@@ -330,7 +334,20 @@ handshake_we_started(#hs_data{request_type=ReqType,
gen_digest(ChallengeA,HisCookie)),
reset_timer(NewHSData#hs_data.timer),
recv_challenge_ack(NewHSData, MyChallenge, MyCookie),
- connection(NewHSData).
+ connection(NewHSData);
+
+handshake_we_started(OldHsData) when element(1,OldHsData) =:= hs_data ->
+ handshake_we_started(convert_old_hsdata(OldHsData)).
+
+convert_old_hsdata({hs_data, KP, ON, TN, S, T, TF, A, OV, OF, OS, FS, FR,
+ FS_PRE, FS_POST, FG, FA, MFT, MFG, RT}) ->
+ #hs_data{
+ kernel_pid = KP, other_node = ON, this_node = TN, socket = S, timer = T,
+ this_flags = TF, allowed = A, other_version = OV, other_flags = OF,
+ other_started = OS, f_send = FS, f_recv = FR, f_setopts_pre_nodeup = FS_PRE,
+ f_setopts_post_nodeup = FS_POST, f_getll = FG, f_address = FA,
+ mf_tick = MFT, mf_getstat = MFG, request_type = RT}.
+
%% --------------------------------------------------------------
%% The connection has been established.
@@ -350,15 +367,15 @@ connection(#hs_data{other_node = Node,
mark_nodeup(HSData,Address),
case FPostNodeup(Socket) of
ok ->
- con_loop(HSData#hs_data.kernel_pid,
- Node,
- Socket,
- Address,
- HSData#hs_data.this_node,
- PType,
- #tick{},
- HSData#hs_data.mf_tick,
- HSData#hs_data.mf_getstat);
+ con_loop({HSData#hs_data.kernel_pid,
+ Node,
+ Socket,
+ PType,
+ HSData#hs_data.mf_tick,
+ HSData#hs_data.mf_getstat,
+ HSData#hs_data.mf_setopts,
+ HSData#hs_data.mf_getopts},
+ #tick{});
_ ->
?shutdown2(Node, connection_setup_failed)
end;
@@ -454,8 +471,8 @@ mark_nodeup(#hs_data{kernel_pid = Kernel,
?shutdown(Node)
end.
-con_loop(Kernel, Node, Socket, TcpAddress,
- MyNode, Type, Tick, MFTick, MFGetstat) ->
+con_loop({Kernel, Node, Socket, Type, MFTick, MFGetstat, MFSetOpts, MFGetOpts}=ConData,
+ Tick) ->
receive
{tcp_closed, Socket} ->
?shutdown2(Node, connection_closed);
@@ -468,15 +485,12 @@ con_loop(Kernel, Node, Socket, TcpAddress,
_ ->
ignore_it
end,
- con_loop(Kernel, Node, Socket, TcpAddress, MyNode, Type,
- Tick, MFTick, MFGetstat);
+ con_loop(ConData, Tick);
{Kernel, tick} ->
case send_tick(Socket, Tick, Type,
MFTick, MFGetstat) of
{ok, NewTick} ->
- con_loop(Kernel, Node, Socket, TcpAddress,
- MyNode, Type, NewTick, MFTick,
- MFGetstat);
+ con_loop(ConData, NewTick);
{error, not_responding} ->
error_msg("** Node ~p not responding **~n"
"** Removing (timedout) connection **~n",
@@ -489,13 +503,24 @@ con_loop(Kernel, Node, Socket, TcpAddress,
case MFGetstat(Socket) of
{ok, Read, Write, _} ->
From ! {self(), get_status, {ok, Read, Write}},
- con_loop(Kernel, Node, Socket, TcpAddress,
- MyNode,
- Type, Tick,
- MFTick, MFGetstat);
+ con_loop(ConData, Tick);
_ ->
?shutdown2(Node, get_status_failed)
- end
+ end;
+ {From, Ref, {setopts, Opts}} ->
+ Ret = case MFSetOpts of
+ undefined -> {error, enotsup};
+ _ -> MFSetOpts(Socket, Opts)
+ end,
+ From ! {Ref, Ret},
+ con_loop(ConData, Tick);
+ {From, Ref, {getopts, Opts}} ->
+ Ret = case MFGetOpts of
+ undefined -> {error, enotsup};
+ _ -> MFGetOpts(Socket, Opts)
+ end,
+ From ! {Ref, Ret},
+ con_loop(ConData, Tick)
end.
diff --git a/lib/kernel/src/erl_epmd.erl b/lib/kernel/src/erl_epmd.erl
index f8ef4a475d..7bc9e2ede3 100644
--- a/lib/kernel/src/erl_epmd.erl
+++ b/lib/kernel/src/erl_epmd.erl
@@ -103,6 +103,10 @@ names(EpmdAddr) ->
register_node(Name, PortNo) ->
register_node(Name, PortNo, inet).
+register_node(Name, PortNo, inet_tcp) ->
+ register_node(Name, PortNo, inet);
+register_node(Name, PortNo, inet6_tcp) ->
+ register_node(Name, PortNo, inet6);
register_node(Name, PortNo, Family) ->
gen_server:call(erl_epmd, {register, Name, PortNo, Family}, infinity).
@@ -403,8 +407,6 @@ select_best_version(L1, _H1, _L2, H2) when L1 > H2 ->
0;
select_best_version(_L1, H1, L2, _H2) when L2 > H1 ->
0;
-select_best_version(_L1, H1, L2, _H2) when L2 > H1 ->
- 0;
select_best_version(_L1, H1, _L2, H2) ->
erlang:min(H1, H2).
diff --git a/lib/kernel/src/inet.erl b/lib/kernel/src/inet.erl
index a91a6ed517..75dd800c6b 100644
--- a/lib/kernel/src/inet.erl
+++ b/lib/kernel/src/inet.erl
@@ -75,6 +75,7 @@
-export_type([address_family/0, hostent/0, hostname/0, ip4_address/0,
ip6_address/0, ip_address/0, port_number/0,
local_address/0, socket_address/0, returned_non_ip_address/0,
+ socket_setopt/0, socket_getopt/0,
posix/0, socket/0, stat_option/0]).
%% imports
-import(lists, [append/1, duplicate/2, filter/2, foldl/3]).
diff --git a/lib/kernel/src/inet6_tcp_dist.erl b/lib/kernel/src/inet6_tcp_dist.erl
index 3aa61973af..9b6c2745d5 100644
--- a/lib/kernel/src/inet6_tcp_dist.erl
+++ b/lib/kernel/src/inet6_tcp_dist.erl
@@ -24,6 +24,7 @@
-export([listen/1, accept/1, accept_connection/5,
setup/5, close/1, select/1, is_node_name/1]).
+-export([setopts/2, getopts/2]).
%% ------------------------------------------------------------
%% Select this protocol based on node name
@@ -72,3 +73,9 @@ close(Socket) ->
is_node_name(Node) when is_atom(Node) ->
inet_tcp_dist:is_node_name(Node).
+
+setopts(S, Opts) ->
+ inet_tcp_dist:setopts(S, Opts).
+
+getopts(S, Opts) ->
+ inet_tcp_dist:getopts(S, Opts).
diff --git a/lib/kernel/src/inet_tcp_dist.erl b/lib/kernel/src/inet_tcp_dist.erl
index f91d7ef7c3..3084bd599a 100644
--- a/lib/kernel/src/inet_tcp_dist.erl
+++ b/lib/kernel/src/inet_tcp_dist.erl
@@ -24,13 +24,16 @@
-export([listen/1, accept/1, accept_connection/5,
setup/5, close/1, select/1, is_node_name/1]).
+%% Optional
+-export([setopts/2, getopts/2]).
+
%% Generalized dist API
-export([gen_listen/2, gen_accept/2, gen_accept_connection/6,
gen_setup/6, gen_select/2]).
%% internal exports
--export([accept_loop/3,do_accept/7,do_setup/7,getstat/1]).
+-export([accept_loop/3,do_accept/7,do_setup/7,getstat/1,tick/2]).
-import(error_logger,[error_msg/2]).
@@ -74,7 +77,7 @@ gen_listen(Driver, Name) ->
TcpAddress = get_tcp_address(Driver, Socket),
{_,Port} = TcpAddress#net_address.address,
ErlEpmd = net_kernel:epmd_module(),
- case ErlEpmd:register_node(Name, Port) of
+ case ErlEpmd:register_node(Name, Port, Driver) of
{ok, Creation} ->
{ok, {Socket, TcpAddress, Creation}};
Error ->
@@ -215,8 +218,10 @@ do_accept(Driver, Kernel, AcceptPid, Socket, MyNode, Allowed, SetupTime) ->
inet:getll(S)
end,
f_address = fun(S, Node) -> get_remote_id(Driver, S, Node) end,
- mf_tick = fun(S) -> tick(Driver, S) end,
- mf_getstat = fun ?MODULE:getstat/1
+ mf_tick = fun(S) -> ?MODULE:tick(Driver, S) end,
+ mf_getstat = fun ?MODULE:getstat/1,
+ mf_setopts = fun ?MODULE:setopts/2,
+ mf_getopts = fun ?MODULE:getopts/2
},
dist_util:handshake_other_started(HSData);
{false,IP} ->
@@ -320,6 +325,7 @@ do_setup(Driver, Kernel, Node, Type, MyNode, LongOrShortNames, SetupTime) ->
{packet, 4},
nodelay()])
end,
+
f_getll = fun inet:getll/1,
f_address =
fun(_,_) ->
@@ -329,9 +335,11 @@ do_setup(Driver, Kernel, Node, Type, MyNode, LongOrShortNames, SetupTime) ->
protocol = tcp,
family = AddressFamily}
end,
- mf_tick = fun(S) -> tick(Driver, S) end,
+ mf_tick = fun(S) -> ?MODULE:tick(Driver, S) end,
mf_getstat = fun ?MODULE:getstat/1,
- request_type = Type
+ request_type = Type,
+ mf_setopts = fun ?MODULE:setopts/2,
+ mf_getopts = fun ?MODULE:getopts/2
},
dist_util:handshake_we_started(HSData);
_ ->
@@ -492,3 +500,12 @@ split_stat([], R, W, P) ->
{ok, R, W, P}.
+setopts(S, Opts) ->
+ case [Opt || {K,_}=Opt <- Opts,
+ K =:= active orelse K =:= deliver orelse K =:= packet] of
+ [] -> inet:setopts(S,Opts);
+ Opts1 -> {error, {badopts,Opts1}}
+ end.
+
+getopts(S, Opts) ->
+ inet:getopts(S, Opts).
diff --git a/lib/kernel/src/net_kernel.erl b/lib/kernel/src/net_kernel.erl
index ac19f4935b..0c679e7349 100644
--- a/lib/kernel/src/net_kernel.erl
+++ b/lib/kernel/src/net_kernel.erl
@@ -59,6 +59,8 @@
connect_node/1,
monitor_nodes/1,
monitor_nodes/2,
+ setopts/2,
+ getopts/2,
start/1,
stop/0]).
@@ -111,7 +113,7 @@
}).
-record(listen, {
- listen, %% listen pid
+ listen, %% listen socket
accept, %% accepting pid
address, %% #net_address
module %% proto module
@@ -384,7 +386,7 @@ init({Name, LongOrShortNames, TickT, CleanHalt}) ->
connections =
ets:new(sys_dist,[named_table,
protected,
- {keypos, 2}]),
+ {keypos, #connection.node}]),
listen = Listeners,
allowed = [],
verbose = 0
@@ -554,6 +556,38 @@ handle_call({new_ticktime,_T,_TP},
#state{tick = #tick_change{time = T}} = State) ->
async_reply({reply, {ongoing_change_to, T}, State}, From);
+handle_call({setopts, new, Opts}, From, State) ->
+ Ret = setopts_new(Opts, State),
+ async_reply({reply, Ret, State}, From);
+
+handle_call({setopts, Node, Opts}, From, State) ->
+ Return =
+ case ets:lookup(sys_dist, Node) of
+ [Conn] when Conn#connection.state =:= up ->
+ case call_owner(Conn#connection.owner, {setopts, Opts}) of
+ {ok, Ret} -> Ret;
+ _ -> {error, noconnection}
+ end;
+
+ _ ->
+ {error, noconnection}
+ end,
+ async_reply({reply, Return, State}, From);
+
+handle_call({getopts, Node, Opts}, From, State) ->
+ Return =
+ case ets:lookup(sys_dist, Node) of
+ [Conn] when Conn#connection.state =:= up ->
+ case call_owner(Conn#connection.owner, {getopts, Opts}) of
+ {ok, Ret} -> Ret;
+ _ -> {error, noconnection}
+ end;
+
+ _ ->
+ {error, noconnection}
+ end,
+ async_reply({reply, Return, State}, From);
+
handle_call(_Msg, _From, State) ->
{noreply, State}.
@@ -1608,3 +1642,93 @@ async_gen_server_reply(From, Msg) ->
{'EXIT', _} ->
ok
end.
+
+call_owner(Owner, Msg) ->
+ Mref = monitor(process, Owner),
+ Owner ! {self(), Mref, Msg},
+ receive
+ {Mref, Reply} ->
+ erlang:demonitor(Mref, [flush]),
+ {ok, Reply};
+ {'DOWN', Mref, _, _, _} ->
+ error
+ end.
+
+
+-spec setopts(Node, Options) -> ok | {error, Reason} | ignored when
+ Node :: node() | new,
+ Options :: [inet:socket_setopt()],
+ Reason :: inet:posix() | noconnection.
+
+setopts(Node, Opts) when is_atom(Node), is_list(Opts) ->
+ request({setopts, Node, Opts}).
+
+setopts_new(Opts, State) ->
+ %% First try setopts on listening socket(s)
+ %% Bail out on failure.
+ %% If successful, we are pretty sure Opts are ok
+ %% and we continue with config params and pending connections.
+ case setopts_on_listen(Opts, State#state.listen) of
+ ok ->
+ setopts_new_1(Opts);
+ Fail -> Fail
+ end.
+
+setopts_on_listen(_, []) -> ok;
+setopts_on_listen(Opts, [#listen {listen = LSocket, module = Mod} | T]) ->
+ try Mod:setopts(LSocket, Opts) of
+ ok ->
+ setopts_on_listen(Opts, T);
+ Fail -> Fail
+ catch
+ error:undef -> {error, enotsup}
+ end.
+
+setopts_new_1(Opts) ->
+ ConnectOpts = case application:get_env(kernel, inet_dist_connect_options) of
+ {ok, CO} -> CO;
+ _ -> []
+ end,
+ application:set_env(kernel, inet_dist_connect_options,
+ merge_opts(Opts,ConnectOpts)),
+ ListenOpts = case application:get_env(kernel, inet_dist_listen_options) of
+ {ok, LO} -> LO;
+ _ -> []
+ end,
+ application:set_env(kernel, inet_dist_listen_options,
+ merge_opts(Opts, ListenOpts)),
+ case lists:keyfind(nodelay, 1, Opts) of
+ {nodelay, ND} when is_boolean(ND) ->
+ application:set_env(kernel, dist_nodelay, ND);
+ _ -> ignore
+ end,
+
+ %% Update any pending connections
+ PendingConns = ets:select(sys_dist, [{'_',
+ [{'=/=',{element,#connection.state,'$_'},up}],
+ ['$_']}]),
+ lists:foreach(fun(#connection{state = pending, owner = Owner}) ->
+ call_owner(Owner, {setopts, Opts});
+ (#connection{state = up_pending, pending_owner = Owner}) ->
+ call_owner(Owner, {setopts, Opts});
+ (_) -> ignore
+ end, PendingConns),
+ ok.
+
+merge_opts([], B) ->
+ B;
+merge_opts([H|T], B0) ->
+ {Key, _} = H,
+ B1 = lists:filter(fun({K,_}) -> K =/= Key end, B0),
+ merge_opts(T, [H | B1]).
+
+-spec getopts(Node, Options) ->
+ {'ok', OptionValues} | {'error', Reason} | ignored when
+ Node :: node(),
+ Options :: [inet:socket_getopt()],
+ OptionValues :: [inet:socket_setopt()],
+ Reason :: inet:posix() | noconnection.
+
+getopts(Node, Opts) when is_atom(Node), is_list(Opts) ->
+ request({getopts, Node, Opts}).
+
diff --git a/lib/kernel/src/os.erl b/lib/kernel/src/os.erl
index f0ad26b1f2..05bbf1069e 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,8 @@ 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"]}.
+ ["(", unicode:characters_to_binary(Cmd), "\n); echo \"\^D\"\n"],
+ <<$\^D>>}.
validate(Atom) when is_atom(Atom) ->
ok;
@@ -267,21 +270,44 @@ validate1([List|Rest]) when is_list(List) ->
validate1([]) ->
ok.
-get_data(Port, Sofar) ->
+get_data(Port, MonRef, Eot, Sofar) ->
receive
{Port, {data, Bytes}} ->
- get_data(Port, [Sofar,Bytes]);
- {Port, eof} ->
- Port ! {self(), close},
- receive
- {Port, closed} ->
- true
- end,
- receive
- {'EXIT', Port, _} ->
- ok
- after 1 -> % force context switch
- ok
- end,
+ case eot(Bytes, Eot) of
+ more ->
+ get_data(Port, MonRef, Eot, [Sofar,Bytes]);
+ Last ->
+ Port ! {self(), close},
+ flush_until_closed(Port),
+ flush_exit(Port),
+ iolist_to_binary([Sofar, Last])
+ end;
+ {'DOWN', MonRef, _, _ , _} ->
+ flush_exit(Port),
iolist_to_binary(Sofar)
end.
+
+eot(_Bs, <<>>) ->
+ more;
+eot(Bs, Eot) ->
+ case binary:match(Bs, Eot) of
+ nomatch -> more;
+ {Pos, _} ->
+ binary:part(Bs,{0, Pos})
+ end.
+
+flush_until_closed(Port) ->
+ receive
+ {Port, {data, _Bytes}} ->
+ flush_until_closed(Port);
+ {Port, closed} ->
+ true
+ end.
+
+flush_exit(Port) ->
+ receive
+ {'EXIT', Port, _} ->
+ ok
+ after 1 -> % force context switch
+ ok
+ end.
diff --git a/lib/kernel/test/erl_distribution_SUITE.erl b/lib/kernel/test/erl_distribution_SUITE.erl
index eb58e92224..e43be77428 100644
--- a/lib/kernel/test/erl_distribution_SUITE.erl
+++ b/lib/kernel/test/erl_distribution_SUITE.erl
@@ -25,6 +25,7 @@
init_per_group/2,end_per_group/2]).
-export([tick/1, tick_change/1, illegal_nodenames/1, hidden_node/1,
+ setopts/1,
table_waste/1, net_setuptime/1,
inet_dist_options_options/1,
@@ -42,6 +43,8 @@
-export([get_socket_priorities/0,
tick_cli_test/1, tick_cli_test1/1,
tick_serv_test/2, tick_serv_test1/1,
+ run_remote_test/1,
+ setopts_do/2,
keep_conn/1, time_ping/1]).
-export([init_per_testcase/2, end_per_testcase/2]).
@@ -66,6 +69,7 @@ suite() ->
all() ->
[tick, tick_change, illegal_nodenames, hidden_node,
+ setopts,
table_waste, net_setuptime, inet_dist_options_options,
{group, monitor_nodes}].
@@ -282,6 +286,165 @@ tick_cli_test1(Node) ->
end
end.
+setopts(Config) when is_list(Config) ->
+ register(setopts_regname, self()),
+ [N1,N2,N3,N4] = get_nodenames(4, setopts),
+
+ {_N1F,Port1} = start_node_unconnected(N1, ?MODULE, run_remote_test,
+ ["setopts_do", atom_to_list(node()), "1", "ping"]),
+ 0 = wait_for_port_exit(Port1),
+
+ {_N2F,Port2} = start_node_unconnected(N2, ?MODULE, run_remote_test,
+ ["setopts_do", atom_to_list(node()), "2", "ping"]),
+ 0 = wait_for_port_exit(Port2),
+
+ {ok, LSock} = gen_tcp:listen(0, [{packet,2}, {active,false}]),
+ {ok, LTcpPort} = inet:port(LSock),
+
+ {N3F,Port3} = start_node_unconnected(N3, ?MODULE, run_remote_test,
+ ["setopts_do", atom_to_list(node()),
+ "1", integer_to_list(LTcpPort)]),
+ wait_and_connect(LSock, N3F, Port3),
+ 0 = wait_for_port_exit(Port3),
+
+ {N4F,Port4} = start_node_unconnected(N4, ?MODULE, run_remote_test,
+ ["setopts_do", atom_to_list(node()),
+ "2", integer_to_list(LTcpPort)]),
+ wait_and_connect(LSock, N4F, Port4),
+ 0 = wait_for_port_exit(Port4),
+
+ ok.
+
+wait_and_connect(LSock, NodeName, NodePort) ->
+ {ok, Sock} = gen_tcp:accept(LSock),
+ {ok, "Connect please"} = gen_tcp:recv(Sock, 0),
+ flush_from_port(NodePort),
+ pong = net_adm:ping(NodeName),
+ gen_tcp:send(Sock, "Connect done"),
+ gen_tcp:close(Sock).
+
+
+flush_from_port(Port) ->
+ flush_from_port(Port, 10).
+
+flush_from_port(Port, Timeout) ->
+ receive
+ {Port,{data,String}} ->
+ io:format("~p: ~s\n", [Port, String]),
+ flush_from_port(Port, Timeout)
+ after Timeout ->
+ timeout
+ end.
+
+wait_for_port_exit(Port) ->
+ case (receive M -> M end) of
+ {Port,{exit_status,Status}} ->
+ Status;
+ {Port,{data,String}} ->
+ io:format("~p: ~s\n", [Port, String]),
+ wait_for_port_exit(Port)
+ end.
+
+run_remote_test([FuncStr, TestNodeStr | Args]) ->
+ Status = try
+ io:format("Node ~p started~n", [node()]),
+ TestNode = list_to_atom(TestNodeStr),
+ io:format("Node ~p spawning function ~p~n", [node(), FuncStr]),
+ {Pid,Ref} = spawn_monitor(?MODULE, list_to_atom(FuncStr), [TestNode, Args]),
+ io:format("Node ~p waiting for function ~p~n", [node(), FuncStr]),
+ receive
+ {'DOWN', Ref, process, Pid, normal} ->
+ 0;
+ Other ->
+ io:format("Node ~p got unexpected msg: ~p\n",[node(), Other]),
+ 1
+ end
+ catch
+ C:E ->
+ io:format("Node ~p got EXCEPTION ~p:~p\nat ~p\n",
+ [node(), C, E, erlang:get_stacktrace()]),
+ 2
+ end,
+ io:format("Node ~p doing halt(~p).\n",[node(), Status]),
+ erlang:halt(Status).
+
+% Do the actual test on the remote node
+setopts_do(TestNode, [OptNr, ConnectData]) ->
+ [] = nodes(),
+ {Opt, Val} = opt_from_nr(OptNr),
+ ok = net_kernel:setopts(new, [{Opt, Val}]),
+
+ [] = nodes(),
+ {error, noconnection} = net_kernel:getopts(TestNode, [Opt]),
+
+ case ConnectData of
+ "ping" -> % We connect
+ net_adm:ping(TestNode);
+ TcpPort -> % Other connect
+ {ok, Sock} = gen_tcp:connect("localhost", list_to_integer(TcpPort),
+ [{active,false},{packet,2}]),
+ ok = gen_tcp:send(Sock, "Connect please"),
+ {ok, "Connect done"} = gen_tcp:recv(Sock, 0),
+ gen_tcp:close(Sock)
+ end,
+ [TestNode] = nodes(),
+ {ok, [{Opt,Val}]} = net_kernel:getopts(TestNode, [Opt]),
+ {error, noconnection} = net_kernel:getopts('pixie@fairyland', [Opt]),
+
+ NewVal = change_val(Val),
+ ok = net_kernel:setopts(TestNode, [{Opt, NewVal}]),
+ {ok, [{Opt,NewVal}]} = net_kernel:getopts(TestNode, [Opt]),
+
+ ok = net_kernel:setopts(TestNode, [{Opt, Val}]),
+ {ok, [{Opt,Val}]} = net_kernel:getopts(TestNode, [Opt]),
+
+ ok.
+
+opt_from_nr("1") -> {nodelay, true};
+opt_from_nr("2") -> {nodelay, false}.
+
+change_val(true) -> false;
+change_val(false) -> true.
+
+start_node_unconnected(Name, Mod, Func, Args) ->
+ FullName = full_node_name(Name),
+ CmdLine = mk_node_cmdline(Name,Mod,Func,Args),
+ io:format("Starting node ~p: ~s~n", [FullName, CmdLine]),
+ case open_port({spawn, CmdLine}, [exit_status]) of
+ Port when is_port(Port) ->
+ {FullName, Port};
+ Error ->
+ exit({failed_to_start_node, FullName, Error})
+ end.
+
+full_node_name(PreName) ->
+ HostSuffix = lists:dropwhile(fun ($@) -> false; (_) -> true end,
+ atom_to_list(node())),
+ list_to_atom(atom_to_list(PreName) ++ HostSuffix).
+
+mk_node_cmdline(Name,Mod,Func,Args) ->
+ Static = "-noinput",
+ Pa = filename:dirname(code:which(?MODULE)),
+ Prog = case catch init:get_argument(progname) of
+ {ok,[[P]]} -> P;
+ _ -> exit(no_progname_argument_found)
+ end,
+ NameSw = case net_kernel:longnames() of
+ false -> "-sname ";
+ true -> "-name ";
+ _ -> exit(not_distributed_node)
+ end,
+ {ok, Pwd} = file:get_cwd(),
+ NameStr = atom_to_list(Name),
+ Prog ++ " "
+ ++ Static ++ " "
+ ++ NameSw ++ " " ++ NameStr
+ ++ " -pa " ++ Pa
+ ++ " -env ERL_CRASH_DUMP " ++ Pwd ++ "/erl_crash_dump." ++ NameStr
+ ++ " -setcookie " ++ atom_to_list(erlang:get_cookie())
+ ++ " -run " ++ atom_to_list(Mod) ++ " " ++ atom_to_list(Func)
+ ++ " " ++ string:join(Args, " ").
+
%% OTP-4255.
tick_change(Config) when is_list(Config) ->
diff --git a/lib/kernel/test/gen_sctp_SUITE.erl b/lib/kernel/test/gen_sctp_SUITE.erl
index f836b2aa94..620ab235a0 100644
--- a/lib/kernel/test/gen_sctp_SUITE.erl
+++ b/lib/kernel/test/gen_sctp_SUITE.erl
@@ -117,7 +117,11 @@ xfer_min(Config) when is_list(Config) ->
Stream = 0,
Data = <<"The quick brown fox jumps over a lazy dog 0123456789">>,
Loopback = {127,0,0,1},
+ StatOpts =
+ [recv_avg,recv_cnt,recv_max,recv_oct,
+ send_avg,send_cnt,send_max,send_oct],
{ok,Sb} = gen_sctp:open([{type,seqpacket}]),
+ {ok,SbStat1} = inet:getstat(Sb, StatOpts),
{ok,Pb} = inet:port(Sb),
ok = gen_sctp:listen(Sb, true),
@@ -212,6 +216,8 @@ xfer_min(Config) when is_list(Config) ->
assoc_id=SbAssocId}} =
recv_event(log_ok(gen_sctp:recv(Sb, infinity))),
ok = gen_sctp:close(Sa),
+ {ok,SbStat2} = inet:getstat(Sb, StatOpts),
+ [] = filter_stat_eq(SbStat1, SbStat2),
ok = gen_sctp:close(Sb),
receive
@@ -220,6 +226,18 @@ xfer_min(Config) when is_list(Config) ->
end,
ok.
+filter_stat_eq([], []) ->
+ [];
+filter_stat_eq([{Tag,Val1}=Stat|SbStat1], [{Tag,Val2}|SbStat2]) ->
+ if
+ Val1 == Val2 ->
+ [Stat|filter_stat_eq(SbStat1, SbStat2)];
+ true ->
+ filter_stat_eq(SbStat1, SbStat2)
+ end.
+
+
+
%% Minimal data transfer in active mode.
xfer_active(Config) when is_list(Config) ->
Timeout = 2000,
@@ -383,26 +401,28 @@ def_sndrcvinfo(Config) when is_list(Config) ->
assoc_id=S2AssocId} = S2AssocChange =
log_ok(gen_sctp:connect(S2, Loopback, P1, [])),
?LOGVAR(S2AssocChange),
- case recv_event(log_ok(gen_sctp:recv(S1))) of
- {Loopback,P2,
- #sctp_assoc_change{
- state=comm_up,
- error=0,
- assoc_id=S1AssocId}} ->
- ?LOGVAR(S1AssocId);
- {Loopback,P2,
- #sctp_paddr_change{
- state=addr_confirmed,
- error=0,
- assoc_id=S1AssocId}} ->
- ?LOGVAR(S1AssocId),
+ S1AssocId =
+ case recv_event(log_ok(gen_sctp:recv(S1))) of
{Loopback,P2,
#sctp_assoc_change{
state=comm_up,
error=0,
- assoc_id=S1AssocId}} =
- recv_event(log_ok(gen_sctp:recv(S1)))
- end,
+ assoc_id=AssocId}} ->
+ AssocId;
+ {Loopback,P2,
+ #sctp_paddr_change{
+ state=addr_confirmed,
+ error=0,
+ assoc_id=AssocId}} ->
+ {Loopback,P2,
+ #sctp_assoc_change{
+ state=comm_up,
+ error=0,
+ assoc_id=AssocId}} =
+ recv_event(log_ok(gen_sctp:recv(S1))),
+ AssocId
+ end,
+ ?LOGVAR(S1AssocId),
#sctp_sndrcvinfo{
ppid=17, context=0, timetolive=0} = %, assoc_id=S1AssocId} =
@@ -1055,6 +1075,7 @@ peeloff(Config, SockOpts) when is_list(Config) ->
Addr = {127,0,0,1},
Stream = 0,
Timeout = 333,
+ StartTime = timestamp(),
S1 = socket_open([{ifaddr,Addr}|SockOpts], Timeout),
?LOGVAR(S1),
P1 = socket_call(S1, get_port),
@@ -1077,7 +1098,7 @@ peeloff(Config, SockOpts) when is_list(Config) ->
state=comm_up,
assoc_id=AssocId2}}} -> AssocId2
after Timeout ->
- socket_bailout([S1,S2])
+ socket_bailout([S1,S2], StartTime)
end,
?LOGVAR(S2Ai),
S1Ai =
@@ -1087,7 +1108,7 @@ peeloff(Config, SockOpts) when is_list(Config) ->
state=comm_up,
assoc_id=AssocId1}}} -> AssocId1
after Timeout ->
- socket_bailout([S1,S2])
+ socket_bailout([S1,S2], StartTime)
end,
?LOGVAR(S1Ai),
%%
@@ -1095,13 +1116,13 @@ peeloff(Config, SockOpts) when is_list(Config) ->
receive
{S1,{Addr,P2,S1Ai,Stream,<<"Number one">>}} -> ok
after Timeout ->
- socket_bailout([S1,S2])
+ socket_bailout([S1,S2], StartTime)
end,
socket_call(S2, {send,Socket1,S1Ai,Stream,<<"Number two">>}),
receive
{S2,{Addr,P1,S2Ai,Stream,<<"Number two">>}} -> ok
after Timeout ->
- socket_bailout([S1,S2])
+ socket_bailout([S1,S2], StartTime)
end,
%%
S3 = socket_peeloff(Socket1, S1Ai, SockOpts, Timeout),
@@ -1120,31 +1141,31 @@ peeloff(Config, SockOpts) when is_list(Config) ->
receive
{S2,{Addr,P3,S2Ai,Stream,<<"Number three">>}} -> ok
after Timeout ->
- socket_bailout([S1,S2,S3])
+ socket_bailout([S1,S2,S3], StartTime)
end,
socket_call(S3, {send,Socket2,S2Ai,Stream,<<"Number four">>}),
receive
{S3,{Addr,P2,S3Ai,Stream,<<"Number four">>}} -> ok
after Timeout ->
- socket_bailout([S1,S2,S3])
+ socket_bailout([S1,S2,S3], StartTime)
end,
%%
inet:i(sctp),
- socket_close_verbose(S1),
- socket_close_verbose(S2),
+ socket_close_verbose(S1, StartTime),
+ socket_close_verbose(S2, StartTime),
receive
{S3,{Addr,P2,#sctp_shutdown_event{assoc_id=S3Ai_X}}} ->
match_unless_solaris(S3Ai, S3Ai_X)
after Timeout ->
- socket_bailout([S3])
+ socket_bailout([S3], StartTime)
end,
receive
{S3,{Addr,P2,#sctp_assoc_change{state=shutdown_comp,
assoc_id=S3Ai}}} -> ok
after Timeout ->
- socket_bailout([S3])
+ socket_bailout([S3], StartTime)
end,
- socket_close_verbose(S3),
+ socket_close_verbose(S3, StartTime),
[] = flush(),
ok.
@@ -1156,6 +1177,7 @@ buffers(Config) when is_list(Config) ->
Addr = {127,0,0,1},
Stream = 1,
Timeout = 3333,
+ StartTime = timestamp(),
S1 = socket_open([{ip,Addr}], Timeout),
?LOGVAR(S1),
P1 = socket_call(S1, get_port),
@@ -1174,7 +1196,7 @@ buffers(Config) when is_list(Config) ->
state=comm_up,
assoc_id=AssocId2}}} -> AssocId2
after Timeout ->
- socket_bailout([S1,S2])
+ socket_bailout([S1,S2], StartTime)
end,
S1Ai =
receive
@@ -1183,7 +1205,7 @@ buffers(Config) when is_list(Config) ->
state=comm_up,
assoc_id=AssocId1}}} -> AssocId1
after Timeout ->
- socket_bailout([S1,S2])
+ socket_bailout([S1,S2], StartTime)
end,
%%
socket_call(S1, {setopts,[{recbuf,Limit}]}),
@@ -1197,22 +1219,22 @@ buffers(Config) when is_list(Config) ->
receive
{S1,{Addr,P2,S1Ai,Stream,Data}} -> ok
after Timeout ->
- socket_bailout([S1,S2])
+ socket_bailout([S1,S2], StartTime)
end,
%%
- socket_close_verbose(S1),
+ socket_close_verbose(S1, StartTime),
receive
{S2,{Addr,P1,#sctp_shutdown_event{assoc_id=S2Ai}}} -> ok
after Timeout ->
- socket_bailout([S2])
+ socket_bailout([S2], StartTime)
end,
receive
{S2,{Addr,P1,#sctp_assoc_change{state=shutdown_comp,
assoc_id=S2Ai}}} -> ok
after Timeout ->
- socket_bailout([S2])
+ socket_bailout([S2], StartTime)
end,
- socket_close_verbose(S2),
+ socket_close_verbose(S2, StartTime),
[] = flush(),
ok.
@@ -1521,8 +1543,8 @@ socket_peeloff(Socket, AssocId, SocketOpts, Timeout) ->
end,
s_start(Starter, Timeout).
-socket_close_verbose(S) ->
- History = socket_history(socket_close(S)),
+socket_close_verbose(S, StartTime) ->
+ History = socket_history(socket_close(S), StartTime),
io:format("socket_close ~p:~n ~p.~n", [S,History]),
History.
@@ -1535,19 +1557,19 @@ socket_call(S, Request) ->
%% socket_get(S, Key) ->
%% s_req(S, {get,Key}).
-socket_bailout([S|Ss]) ->
- History = socket_history(socket_close(S)),
+socket_bailout([S|Ss], StartTime) ->
+ History = socket_history(socket_close(S), StartTime),
io:format("bailout ~p:~n ~p.~n", [S,History]),
- socket_bailout(Ss);
-socket_bailout([]) ->
+ socket_bailout(Ss, StartTime);
+socket_bailout([], _) ->
io:format("flush: ~p.~n", [flush()]),
ct:fail(socket_bailout).
-socket_history({State,Flush}) ->
+socket_history({State,Flush}, StartTime) ->
{lists:keysort(
2,
lists:flatten(
- [[{Key,Val} || Val <- Vals]
+ [[{Key,{TS-StartTime,Val}} || {TS,Val} <- Vals]
|| {Key,Vals} <- gb_trees:to_list(State)])),
Flush}.
@@ -1610,14 +1632,12 @@ s_loop(Socket, Timeout, Parent, Handler, State) ->
{Parent,Ref,exit} ->
ok = gen_sctp:close(Socket),
Key = exit,
- Val = {now(),Socket},
- NewState = gb_push(Key, Val, State),
+ NewState = gb_push(Key, Socket, State),
Parent ! {self(),Ref,{NewState,flush()}};
{Parent,Ref,{Msg}} ->
Result = Handler(Msg),
Key = req,
- Val = {now(),{Msg,Result}},
- NewState = gb_push(Key, Val, State),
+ NewState = gb_push(Key, {Msg,Result}, State),
Parent ! {self(),Ref,Result},
s_loop(Socket, Timeout, Parent, Handler, NewState);
%% {Parent,Ref,{get,Key}} ->
@@ -1627,16 +1647,15 @@ s_loop(Socket, Timeout, Parent, Handler, State) ->
{[#sctp_sndrcvinfo{stream=Stream,assoc_id=AssocId}=SRI],Data}}
when not is_tuple(Data) ->
case gb_get({assoc_change,AssocId}, State) of
- [{_,{Addr,Port,
- #sctp_assoc_change{
- state=comm_up,
- inbound_streams=Is}}}|_]
+ [{Addr,Port,
+ #sctp_assoc_change{
+ state=comm_up,
+ inbound_streams=Is}}|_]
when 0 =< Stream, Stream < Is-> ok;
[] -> ok
end,
Key = {msg,AssocId,Stream},
- Val = {now(),{Addr,Port,SRI,Data}},
- NewState = gb_push(Key, Val, State),
+ NewState = gb_push(Key, {Addr,Port,SRI,Data}, State),
Parent ! {self(),{Addr,Port,AssocId,Stream,Data}},
again(Socket),
s_loop(Socket, Timeout, Parent, Handler, NewState);
@@ -1647,13 +1666,12 @@ s_loop(Socket, Timeout, Parent, Handler, State) ->
[] -> ok
end,
Key = {assoc_change,AssocId},
- Val = {now(),{Addr,Port,SAC}},
case {gb_get(Key, State),St} of
{[],_} -> ok;
- {[{_,{Addr,Port,#sctp_assoc_change{state=comm_up}}}|_],_}
+ {[{Addr,Port,#sctp_assoc_change{state=comm_up}}|_],_}
when St =:= comm_lost; St =:= shutdown_comp -> ok
end,
- NewState = gb_push(Key, Val, State),
+ NewState = gb_push(Key, {Addr,Port,SAC}, State),
Parent ! {self(),{Addr,Port,SAC}},
again(Socket),
s_loop(Socket, Timeout, Parent, Handler, NewState);
@@ -1667,14 +1685,13 @@ s_loop(Socket, Timeout, Parent, Handler, State) ->
[] -> ok
end,
case {gb_get({assoc_change,AssocId}, State),St} of
- {[{_,{Addr,Port,#sctp_assoc_change{state=comm_up}}}|_],_}
+ {[{Addr,Port,#sctp_assoc_change{state=comm_up}}|_],_}
when St =:= addr_available;
St =:= addr_confirmed -> ok;
{[],addr_confirmed} -> ok
end,
Key = {paddr_change,AssocId},
- Val = {now(),{Addr,Port,SPC}},
- NewState = gb_push(Key, Val, State),
+ NewState = gb_push(Key, {Addr,Port,SPC}, State),
again(Socket),
s_loop(Socket, Timeout, Parent, Handler, NewState);
{sctp,Socket,Addr,Port,
@@ -1684,12 +1701,11 @@ s_loop(Socket, Timeout, Parent, Handler, State) ->
[] -> ok
end,
case gb_get({assoc_change,AssocId}, State) of
- [{_,{Addr,Port,#sctp_assoc_change{state=comm_up}}}|_] -> ok;
+ [{Addr,Port,#sctp_assoc_change{state=comm_up}}|_] -> ok;
[] -> ok
end,
Key = {shutdown_event,AssocId},
- Val = {now(),{Addr,Port}},
- NewState = gb_push(Key, Val, State),
+ NewState = gb_push(Key, {Addr,Port}, State),
Parent ! {self(), {Addr,Port,SSE}},
again(Socket),
s_loop(Socket, Timeout, Parent, Handler, NewState);
@@ -1707,11 +1723,12 @@ again(Socket) ->
end.
gb_push(Key, Val, GBT) ->
+ TS = timestamp(),
case gb_trees:lookup(Key, GBT) of
none ->
- gb_trees:insert(Key, [Val], GBT);
+ gb_trees:insert(Key, [{TS,Val}], GBT);
{value,V} ->
- gb_trees:update(Key, [Val|V], GBT)
+ gb_trees:update(Key, [{TS,Val}|V], GBT)
end.
gb_get(Key, GBT) ->
@@ -1719,7 +1736,7 @@ gb_get(Key, GBT) ->
none ->
[];
{value,V} ->
- V
+ [Val || {_TS,Val} <- V]
end.
match_unless_solaris(A, B) ->
@@ -1727,3 +1744,6 @@ match_unless_solaris(A, B) ->
{unix,sunos} -> B;
_ -> A = B
end.
+
+timestamp() ->
+ erlang:monotonic_time().
diff --git a/lib/kernel/test/init_SUITE.erl b/lib/kernel/test/init_SUITE.erl
index 1370e23195..2b59eb2bfe 100644
--- a/lib/kernel/test/init_SUITE.erl
+++ b/lib/kernel/test/init_SUITE.erl
@@ -27,7 +27,8 @@
-export([get_arguments/1, get_argument/1, boot_var/1, restart/1,
many_restarts/0, many_restarts/1,
get_plain_arguments/1,
- reboot/1, stop_status/1, stop/1, get_status/1, script_id/1]).
+ reboot/1, stop_status/1, stop/1, get_status/1, script_id/1,
+ find_system_processes/0]).
-export([boot1/1, boot2/1]).
-export([init_per_testcase/2, end_per_testcase/2]).
@@ -355,12 +356,16 @@ wait_for(N,Node,EHPid) ->
restart(Config) when is_list(Config) ->
Args = args(),
+ Pa = " -pa " ++ filename:dirname(code:which(?MODULE)),
+
%% Currently test_server:start_node cannot be used. The restarted
%% node immediately halts due to the implementation of
%% test_server:start_node.
- {ok, Node} = loose_node:start(init_test, Args, ?DEFAULT_TIMEOUT_SEC),
+ {ok, Node} = loose_node:start(init_test, Args ++ Pa, ?DEFAULT_TIMEOUT_SEC),
%% Ok, the node is up, now the real test test begins.
erlang:monitor_node(Node, true),
+ SysProcs0 = rpc:call(Node, ?MODULE, find_system_processes, []),
+ [InitPid, PurgerPid, LitCollectorPid, DirtyCodePid] = SysProcs0,
InitPid = rpc:call(Node, erlang, whereis, [init]),
PurgerPid = rpc:call(Node, erlang, whereis, [erts_code_purger]),
Procs = rpc:call(Node, erlang, processes, []),
@@ -375,6 +380,9 @@ restart(Config) when is_list(Config) ->
end,
ok = wait_restart(30, Node),
+ SysProcs1 = rpc:call(Node, ?MODULE, find_system_processes, []),
+ [InitPid1, PurgerPid1, LitCollectorPid1, DirtyCodePid1] = SysProcs1,
+
%% Still the same init process!
InitPid1 = rpc:call(Node, erlang, whereis, [init]),
InitP = pid_to_list(InitPid),
@@ -385,8 +393,24 @@ restart(Config) when is_list(Config) ->
PurgerP = pid_to_list(PurgerPid),
PurgerP = pid_to_list(PurgerPid1),
+ %% and same literal area collector process!
+ case LitCollectorPid of
+ undefined -> undefined = LitCollectorPid1;
+ _ ->
+ LitCollectorP = pid_to_list(LitCollectorPid),
+ LitCollectorP = pid_to_list(LitCollectorPid1)
+ end,
+
+ %% and same dirty process code checker process!
+ case DirtyCodePid of
+ undefined -> undefined = DirtyCodePid1;
+ _ ->
+ DirtyCodeP = pid_to_list(DirtyCodePid),
+ DirtyCodeP = pid_to_list(DirtyCodePid1)
+ end,
+
NewProcs0 = rpc:call(Node, erlang, processes, []),
- NewProcs = NewProcs0 -- [InitPid1, PurgerPid1],
+ NewProcs = NewProcs0 -- SysProcs1,
case check_processes(NewProcs, MaxPid) of
true ->
ok;
@@ -406,6 +430,37 @@ restart(Config) when is_list(Config) ->
loose_node:stop(Node),
ok.
+-record(sys_procs, {init,
+ code_purger,
+ literal_collector,
+ dirty_proc_checker}).
+
+find_system_processes() ->
+ find_system_procs(processes(), #sys_procs{}).
+
+find_system_procs([], SysProcs) ->
+ [SysProcs#sys_procs.init,
+ SysProcs#sys_procs.code_purger,
+ SysProcs#sys_procs.literal_collector,
+ SysProcs#sys_procs.dirty_proc_checker];
+find_system_procs([P|Ps], SysProcs) ->
+ case process_info(P, initial_call) of
+ {initial_call,{otp_ring0,start,2}} ->
+ undefined = SysProcs#sys_procs.init,
+ find_system_procs(Ps, SysProcs#sys_procs{init = P});
+ {initial_call,{erts_code_purger,start,0}} ->
+ undefined = SysProcs#sys_procs.code_purger,
+ find_system_procs(Ps, SysProcs#sys_procs{code_purger = P});
+ {initial_call,{erts_literal_area_collector,start,0}} ->
+ undefined = SysProcs#sys_procs.literal_collector,
+ find_system_procs(Ps, SysProcs#sys_procs{literal_collector = P});
+ {initial_call,{erts_dirty_process_code_checker,start,0}} ->
+ undefined = SysProcs#sys_procs.dirty_proc_checker,
+ find_system_procs(Ps, SysProcs#sys_procs{dirty_proc_checker = P});
+ _ ->
+ find_system_procs(Ps, SysProcs)
+ end.
+
wait_restart(0, _Node) ->
ct:fail(not_restarted);
wait_restart(N, Node) ->
diff --git a/lib/kernel/test/os_SUITE.erl b/lib/kernel/test/os_SUITE.erl
index 2a1e5016ec..19ab3713a1 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, 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,
+ perf_counter_api].
groups() ->
[].
@@ -52,6 +54,13 @@ init_per_group(_GroupName, Config) ->
end_per_group(_GroupName, Config) ->
Config.
+init_per_testcase(background_command, Config) ->
+ case os:type() of
+ {win32, _} ->
+ {skip,"Should not work on windows"};
+ _ ->
+ Config
+ end;
init_per_testcase(_TC,Config) ->
Config.
@@ -261,13 +270,38 @@ 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("while true; do echo hello; done&"),
+ [] = receive_all();
+ _ ->
+ ok % Cannot background on non-unix
+ end,
+
+ process_flag(trap_exit, false).
+
%% Test that the os:perf_counter api works as expected
perf_counter_api(_Config) ->
diff --git a/lib/kernel/test/zlib_SUITE.erl b/lib/kernel/test/zlib_SUITE.erl
index f8257bd20c..4b67fce9a8 100644
--- a/lib/kernel/test/zlib_SUITE.erl
+++ b/lib/kernel/test/zlib_SUITE.erl
@@ -83,7 +83,7 @@ groups() ->
[api_open_close, api_deflateInit,
api_deflateSetDictionary, api_deflateReset,
api_deflateParams, api_deflate, api_deflateEnd,
- api_inflateInit, api_inflateSetDictionary,
+ api_inflateInit, api_inflateSetDictionary, api_inflateGetDictionary,
api_inflateSync, api_inflateReset, api_inflate, api_inflateChunk,
api_inflateEnd, api_setBufsz, api_getBufsz, api_crc32,
api_adler32, api_getQSize, api_un_compress, api_un_zip,
@@ -288,6 +288,42 @@ api_inflateSetDictionary(Config) when is_list(Config) ->
?m({'EXIT',{stream_error,_}}, zlib:inflateSetDictionary(Z1,Dict)),
?m(ok, zlib:close(Z1)).
+%% Test inflateGetDictionary.
+api_inflateGetDictionary(Config) when is_list(Config) ->
+ Z1 = zlib:open(),
+ IsOperationSupported =
+ case catch zlib:inflateGetDictionary(Z1) of
+ {'EXIT',{einval,_}} -> true;
+ {'EXIT',{enotsup,_}} -> false
+ end,
+ _ = zlib:close(Z1),
+ api_inflateGetDictionary_if_supported(IsOperationSupported).
+
+api_inflateGetDictionary_if_supported(false) ->
+ {skip, "inflateGetDictionary/1 unsupported in current setup"};
+api_inflateGetDictionary_if_supported(true) ->
+ % Compress payload using custom dictionary
+ Z1 = zlib:open(),
+ ?m(ok, zlib:deflateInit(Z1)),
+ Dict = <<"foobar barfoo foo bar far boo">>,
+ ?m(_, zlib:deflateSetDictionary(Z1, Dict)),
+ Payload = <<"foobarbarbar">>,
+ Compressed = zlib:deflate(Z1, Payload, finish),
+ ?m(ok, zlib:close(Z1)),
+
+ % Decompress and test dictionary extraction
+ Z2 = zlib:open(),
+ ?m(ok, zlib:inflateInit(Z2)),
+ ?m(<<>>, iolist_to_binary(zlib:inflateGetDictionary(Z2))),
+ ?m({'EXIT',{stream_error,_}}, zlib:inflateSetDictionary(Z2, Dict)),
+ ?m({'EXIT',{{need_dictionary,_},_}}, zlib:inflate(Z2, Compressed)),
+ ?m(ok, zlib:inflateSetDictionary(Z2, Dict)),
+ ?m(Dict, iolist_to_binary(zlib:inflateGetDictionary(Z2))),
+ ?m(Payload, iolist_to_binary(zlib:inflate(Z2, Compressed))),
+ ?m(ok, zlib:close(Z2)),
+ ?m(?BARG, zlib:inflateSetDictionary(Z2, Dict)),
+ ok.
+
%% Test inflateSync.
api_inflateSync(Config) when is_list(Config) ->
{skip,"inflateSync/1 sucks"}.
diff --git a/lib/kernel/vsn.mk b/lib/kernel/vsn.mk
index e7d422d03c..cdd200a234 100644
--- a/lib/kernel/vsn.mk
+++ b/lib/kernel/vsn.mk
@@ -1 +1 @@
-KERNEL_VSN = 5.0
+KERNEL_VSN = 5.0.2
diff --git a/lib/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/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/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/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..1dcf5d0708 100644
--- a/lib/ssh/src/ssh_auth.erl
+++ b/lib/ssh/src/ssh_auth.erl
@@ -264,12 +264,23 @@ handle_userauth_request(#ssh_msg_userauth_request{user = User,
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),
+
+ <<?BYTE(HaveSig),
+ ?UINT32(ALen), BAlg:ALen/binary,
+ Rest/binary>> = Data,
+
+ {KeyBlob, SigWLen} =
+ case Rest of
+ <<?UINT32(KLen0), KeyBlob0:KLen0/binary, SigWLen0/binary>> ->
+ {KeyBlob0, SigWLen0};
+ <<>> ->
+ {<<>>, <<>>}
+ end,
+
case HaveSig of
?TRUE ->
- case verify_sig(SessionId, User, "ssh-connection", Alg,
+ case verify_sig(SessionId, User, "ssh-connection",
+ binary_to_list(BAlg),
KeyBlob, SigWLen, Opts) of
true ->
{authorized, User,
@@ -284,7 +295,7 @@ handle_userauth_request(#ssh_msg_userauth_request{user = User,
?FALSE ->
{not_authorized, {User, undefined},
ssh_transport:ssh_packet(
- #ssh_msg_userauth_pk_ok{algorithm_name = Alg,
+ #ssh_msg_userauth_pk_ok{algorithm_name = binary_to_list(BAlg),
key_blob = KeyBlob}, Ssh)}
end;
diff --git a/lib/ssh/src/ssh_connection_handler.erl b/lib/ssh/src/ssh_connection_handler.erl
index f9f4c82351..2eb29c9b32 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 ->
@@ -1401,12 +1405,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 +1432,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 +1495,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/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/vsn.mk b/lib/ssh/vsn.mk
index 575c1af3a9..212b99c695 100644
--- a/lib/ssh/vsn.mk
+++ b/lib/ssh/vsn.mk
@@ -1,5 +1,5 @@
#-*-makefile-*- ; force emacs to enter makefile-mode
-SSH_VSN = 4.3.1
+SSH_VSN = 4.3.2
APP_VSN = "ssh-$(SSH_VSN)"
diff --git a/lib/ssl/doc/src/notes.xml b/lib/ssl/doc/src/notes.xml
index 3b6f988a2d..9d68ee0eee 100644
--- a/lib/ssl/doc/src/notes.xml
+++ b/lib/ssl/doc/src/notes.xml
@@ -28,6 +28,26 @@
<p>This document describes the changes made to the SSL application.</p>
+<section><title>SSL 8.0.1</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ The TLS/SSL protocol version selection for the SSL server
+ has been corrected to follow RFC 5246 Appendix E.1
+ especially in case where the list of supported versions
+ has gaps. Now the server selects the highest protocol
+ version it supports that is not higher than what the
+ client supports.</p>
+ <p>
+ Own Id: OTP-13753 Aux Id: seq13150 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>SSL 8.0</title>
<section><title>Fixed Bugs and Malfunctions</title>
diff --git a/lib/ssl/src/dtls_connection.erl b/lib/ssl/src/dtls_connection.erl
index b8be686b99..a0d9982aaa 100644
--- a/lib/ssl/src/dtls_connection.erl
+++ b/lib/ssl/src/dtls_connection.erl
@@ -65,9 +65,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
@@ -161,12 +159,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
%%--------------------------------------------------------------------
@@ -376,7 +377,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).
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 b6a6b00565..22e24af0a8 100644
--- a/lib/ssl/src/ssl.appup.src
+++ b/lib/ssl/src/ssl.appup.src
@@ -1,17 +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_connection.erl b/lib/ssl/src/ssl_connection.erl
index adee59393e..8a990870e8 100644
--- a/lib/ssl/src/ssl_connection.erl
+++ b/lib/ssl/src/ssl_connection.erl
@@ -824,8 +824,6 @@ 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,
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),
diff --git a/lib/ssl/src/ssl_handshake.erl b/lib/ssl/src/ssl_handshake.erl
index 47af1f861c..081efda768 100644
--- a/lib/ssl/src/ssl_handshake.erl
+++ b/lib/ssl/src/ssl_handshake.erl
@@ -658,7 +658,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()) ->
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..8b828f3421 100644
--- a/lib/ssl/src/tls_connection.erl
+++ b/lib/ssl/src/tls_connection.erl
@@ -68,10 +68,8 @@
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
%%====================================================================
@@ -169,11 +167,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
%%--------------------------------------------------------------------
@@ -213,7 +214,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(),
@@ -257,13 +258,13 @@ hello(internal, #client_hello{client_version = ClientVersion,
_ -> Protocol0
end,
- ssl_connection:hello(internal, {common_client_hello, Type, ServerHelloExt},
+ gen_handshake(ssl_connection, hello, internal, {common_client_hello, Type, ServerHelloExt},
State#state{connection_states = ConnectionStates,
negotiated_version = Version,
hashsign_algorithm = HashSign,
session = Session,
client_ecc = {EllipticCurves, EcPointFormats},
- negotiated_protocol = Protocol}, ?MODULE)
+ negotiated_protocol = Protocol})
end;
hello(internal, #server_hello{} = Hello,
#state{connection_states = ConnectionStates0,
@@ -279,36 +280,36 @@ hello(internal, #server_hello{} = 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 +317,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,
@@ -432,11 +433,16 @@ 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});
+ [] ->
+ handle_own_alert(?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE, empty_alert), Version, StateName, State);
#alert{} = Alert ->
handle_own_alert(Alert, Version, StateName, State)
+ catch
+ _:_ ->
+ 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,9 +463,9 @@ 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
@@ -1039,3 +1045,31 @@ handle_sni_extension(#client_hello{extensions = HelloExtensions}, State0) ->
end;
handle_sni_extension(_, State) ->
State.
+
+gen_handshake(GenConnection, StateName, Type, Event, #state{negotiated_version = Version} = State) ->
+ try GenConnection:StateName(Type, Event, State, ?MODULE) of
+ Result ->
+ Result
+ catch
+ _:_ ->
+ 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
+ _:_ ->
+ handle_own_alert(?ALERT_REC(?FATAL, ?INTERNAL_ERROR, malformed_data), Version, StateName, State)
+ end;
+
+gen_info(Event, StateName, #state{negotiated_version = Version} = State) ->
+ try handle_info(Event, StateName, State) of
+ Result ->
+ Result
+ catch
+ _:_ ->
+ handle_own_alert(?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE, malformed_handshake_data), Version, StateName, State)
+ end.
+
diff --git a/lib/ssl/src/tls_handshake.erl b/lib/ssl/src/tls_handshake.erl
index 566b7db332..6e593950d9 100644
--- a/lib/ssl/src/tls_handshake.erl
+++ b/lib/ssl/src/tls_handshake.erl
@@ -109,19 +109,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 +193,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/test/ssl_basic_SUITE.erl b/lib/ssl/test/ssl_basic_SUITE.erl
index 665dbb1df3..38341f77aa 100644
--- a/lib/ssl/test/ssl_basic_SUITE.erl
+++ b/lib/ssl/test/ssl_basic_SUITE.erl
@@ -340,7 +340,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, 90}),
Config;
init_per_testcase(TestCase, Config) when TestCase == psk_cipher_suites;
@@ -350,6 +350,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),
diff --git a/lib/ssl/test/ssl_crl_SUITE.erl b/lib/ssl/test/ssl_crl_SUITE.erl
index 00636e5660..e37e127440 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(),
diff --git a/lib/ssl/test/ssl_npn_hello_SUITE.erl b/lib/ssl/test/ssl_npn_hello_SUITE.erl
index 00eb9fee4f..6ae9efe5e9 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}),
diff --git a/lib/ssl/test/ssl_packet_SUITE.erl b/lib/ssl/test/ssl_packet_SUITE.erl
index e49d432c21..17237118a0 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,6 +140,7 @@ init_per_suite(Config) ->
catch crypto:stop(),
try crypto:start() of
ok ->
+ ssl:stop(),
ssl:start(),
{ok, _} = make_certs:all(proplists:get_value(data_dir, Config),
proplists:get_value(priv_dir, Config)),
@@ -162,6 +163,7 @@ init_per_group(GroupName, Config) ->
{skip, "Missing crypto support"}
end;
_ ->
+ ssl:stop(),
ssl:start(),
Config
end.
diff --git a/lib/ssl/test/ssl_payload_SUITE.erl b/lib/ssl/test/ssl_payload_SUITE.erl
index cb0571d0a7..c0b762760d 100644
--- a/lib/ssl/test/ssl_payload_SUITE.erl
+++ b/lib/ssl/test/ssl_payload_SUITE.erl
@@ -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_to_openssl_SUITE.erl b/lib/ssl/test/ssl_to_openssl_SUITE.erl
index b3109b5de9..83a4dae0a1 100644
--- a/lib/ssl/test/ssl_to_openssl_SUITE.erl
+++ b/lib/ssl/test/ssl_to_openssl_SUITE.erl
@@ -116,6 +116,7 @@ init_per_suite(Config0) ->
catch crypto:stop(),
try crypto:start() of
ok ->
+ ssl:stop(),
ssl:start(),
{ok, _} = make_certs:all(proplists:get_value(data_dir, Config0),
proplists:get_value(priv_dir, Config0)),
@@ -1264,7 +1265,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 6afac59109..914eb43505 100644
--- a/lib/ssl/vsn.mk
+++ b/lib/ssl/vsn.mk
@@ -1 +1 @@
-SSL_VSN = 8.0.1
+SSL_VSN = 8.0.2
diff --git a/lib/stdlib/doc/src/ets.xml b/lib/stdlib/doc/src/ets.xml
index b8e262208d..3653c6a632 100644
--- a/lib/stdlib/doc/src/ets.xml
+++ b/lib/stdlib/doc/src/ets.xml
@@ -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
diff --git a/lib/stdlib/doc/src/gen_statem.xml b/lib/stdlib/doc/src/gen_statem.xml
index ed44eef912..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 = {CallbackMode,NewState,NewData} | Reason</v>
- <v>
- CallbackMode =
- <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,39 +1352,32 @@ handle_event(_, _, State, Data) ->
<p>
If successful, the function must return the updated
internal state in an
- <c>{CallbackMode,NewState,NewData}</c> tuple.
+ <c>{ok,NewState,NewData}</c> tuple.
</p>
<p>
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 a 3-tuple since that
- will be regarded as a
- <c>{CallbackMode,NewState,NewData}</c> tuple,
+ 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 invalid failure <c>Reason</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>
- <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>.
- </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>
@@ -1372,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>
@@ -1381,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
@@ -1403,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>
@@ -1438,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
@@ -1502,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>
@@ -1581,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>
@@ -1656,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/src/eval_bits.erl b/lib/stdlib/src/eval_bits.erl
index 80667023fb..631faa3be5 100644
--- a/lib/stdlib/src/eval_bits.erl
+++ b/lib/stdlib/src/eval_bits.erl
@@ -67,16 +67,20 @@ expr_grp([Field | FS], Bs0, Lf, Acc) ->
expr_grp([], Bs0, _Lf, Acc) ->
{value,Acc,Bs0}.
+eval_field({bin_element, _, {string, _, S}, {integer,_,8}, [integer,{unit,1},unsigned,big]}, Bs0, _Fun) ->
+ Latin1 = [C band 16#FF || C <- S],
+ {list_to_binary(Latin1),Bs0};
eval_field({bin_element, _, {string, _, S}, default, default}, Bs0, _Fun) ->
Latin1 = [C band 16#FF || C <- S],
{list_to_binary(Latin1),Bs0};
-eval_field({bin_element, Line, {string, _, S}, Size0, Options0}, Bs, _Fun) ->
- {_Size,[Type,_Unit,_Sign,Endian]} =
+eval_field({bin_element, Line, {string, _, S}, Size0, Options0}, Bs0, Fun) ->
+ {Size1,[Type,{unit,Unit},Sign,Endian]} =
make_bit_type(Line, Size0, Options0),
- Res = << <<(eval_exp_field1(C, no_size, no_unit,
- Type, Endian, no_sign))/binary>> ||
+ {value,Size,Bs1} = Fun(Size1, Bs0),
+ Res = << <<(eval_exp_field1(C, Size, Unit,
+ Type, Endian, Sign))/binary>> ||
C <- S >>,
- {Res,Bs};
+ {Res,Bs1};
eval_field({bin_element,Line,E,Size0,Options0}, Bs0, Fun) ->
{value,V,Bs1} = Fun(E, Bs0),
{Size1,[Type,{unit,Unit},Sign,Endian]} =
diff --git a/lib/stdlib/src/gen_statem.erl b/lib/stdlib/src/gen_statem.erl
index c02e6a1a19..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,7 @@
OldState :: state(),
OldData :: data(),
Extra :: term()) ->
- {CallbackMode :: callback_mode(),
- NewState :: state(),
- NewData :: data()} |
+ {ok, NewState :: state(), NewData :: data()} |
(Reason :: term()).
%% Format the callback module state in some sensible that is
@@ -240,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) ->
@@ -451,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
@@ -515,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),
@@ -531,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
@@ -559,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.
%%---------------------------------------------------------------------------
@@ -569,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}),
@@ -602,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.
@@ -631,11 +623,9 @@ system_code_change(
Result -> Result
end
of
- {CallbackMode,NewState,NewData} ->
- callback_mode(CallbackMode) orelse
- error({callback_mode,CallbackMode}),
+ {ok,NewState,NewData} ->
{ok,
- S#{callback_mode := CallbackMode,
+ S#{callback_mode := undefined,
state := NewState,
data := NewData}};
{ok,_} = Error ->
@@ -676,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
@@ -875,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}},
@@ -902,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}},
@@ -937,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) ->
@@ -989,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.
@@ -1026,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
@@ -1050,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(
@@ -1064,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(
@@ -1083,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(
@@ -1098,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;
%%
@@ -1170,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.
@@ -1189,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
@@ -1199,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
[] ->
@@ -1210,7 +1258,7 @@ terminate(
end.
error_info(
- Class, Reason, Stacktrace, Debug,
+ Class, Reason, Stacktrace,
#{name := Name, callback_mode := CallbackMode},
Q, P, FmtData) ->
{FixedReason,FixedStacktrace} =
@@ -1277,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
@@ -1292,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)
@@ -1300,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/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/vsn.mk b/lib/stdlib/vsn.mk
index 41037b8f53..c74343d9ca 100644
--- a/lib/stdlib/vsn.mk
+++ b/lib/stdlib/vsn.mk
@@ -1 +1 @@
-STDLIB_VSN = 3.0.1
+STDLIB_VSN = 3.1
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/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/emacs/erlang-skels.el b/lib/tools/emacs/erlang-skels.el
index c1152f31a4..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,30 +902,38 @@ 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>
+ "{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>
@@ -934,8 +945,103 @@ Please see the function `tempo-define-template'.")
(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>
@@ -965,17 +1071,16 @@ Please see the function `tempo-define-template'.")
"-spec code_change(" n>
"OldVsn :: term() | {down,term()}," n>
"State :: term(), Data :: term(), Extra :: term()) ->" n>
- "{gen_statem:callback_mode()," n>
- "NewState :: term(), NewData :: term()} |" n>
+ "{ok, NewState :: term(), NewData :: term()} |" n>
"(Reason :: term())." n
"code_change(_OldVsn, State, Data, _Extra) ->" n>
- "{state_functions, State, Data}." 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.
+ "*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/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/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/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/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/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 71c4107eac..9d7d0a78bb 100644
--- a/otp_versions.table
+++ b/otp_versions.table
@@ -1,6 +1,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 :
diff --git a/system/doc/design_principles/statem.xml b/system/doc/design_principles/statem.xml
index aea623851a..f785ca9650 100644
--- a/system/doc/design_principles/statem.xml
+++ b/system/doc/design_principles/statem.xml
@@ -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>
@@ -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,