aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--bootstrap/bin/no_dot_erlang.bootbin5949 -> 5951 bytes
-rw-r--r--bootstrap/bin/start.bootbin5949 -> 5951 bytes
-rw-r--r--bootstrap/bin/start_clean.bootbin5949 -> 5951 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/beam_a.beambin2600 -> 2716 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/beam_asm.beambin11288 -> 11332 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/beam_block.beambin10036 -> 12036 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/beam_bs.beambin5516 -> 5464 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/beam_bsm.beambin11980 -> 11936 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/beam_clean.beambin6380 -> 6552 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/beam_dead.beambin12884 -> 13052 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/beam_dict.beambin5068 -> 5060 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/beam_disasm.beambin21604 -> 21680 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/beam_except.beambin3244 -> 3216 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/beam_flatten.beambin2844 -> 2988 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/beam_jump.beambin8980 -> 8968 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/beam_opcodes.beambin7176 -> 7304 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/beam_peep.beambin2896 -> 2872 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/beam_receive.beambin6156 -> 6112 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/beam_record.beambin2264 -> 2236 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/beam_reorder.beambin1940 -> 1904 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/beam_split.beambin2136 -> 2168 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/beam_trim.beambin7508 -> 7508 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/beam_type.beambin17668 -> 17964 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/beam_utils.beambin21484 -> 21896 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/beam_validator.beambin29016 -> 30012 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/beam_z.beambin2712 -> 3284 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/cerl.beambin30060 -> 30040 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/cerl_clauses.beambin2936 -> 2932 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/cerl_inline.beambin37736 -> 37664 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/cerl_trees.beambin20864 -> 20864 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/compile.beambin41352 -> 41332 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/compiler.appup2
-rw-r--r--bootstrap/lib/compiler/ebin/core_lint.beambin12720 -> 12716 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/core_parse.beambin62500 -> 62024 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/core_pp.beambin11708 -> 11704 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/core_scan.beambin6680 -> 6672 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/sys_core_alias.beambin6016 -> 5984 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/sys_core_bsm.beambin5148 -> 5120 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/sys_core_dsetel.beambin6752 -> 6708 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/sys_core_fold.beambin46424 -> 47428 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/sys_core_fold_lists.beambin4432 -> 4408 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/sys_core_inline.beambin3984 -> 3976 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/sys_pre_attributes.beambin2724 -> 2716 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/v3_codegen.beambin64364 -> 64756 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/v3_core.beambin57760 -> 57684 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/v3_kernel.beambin55996 -> 55868 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/v3_kernel_pp.beambin12456 -> 12456 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/application_controller.beambin30752 -> 30688 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/application_master.beambin6356 -> 6344 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/application_starter.beambin1196 -> 1188 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/auth.beambin6312 -> 6312 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/code.beambin13104 -> 13100 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/code_server.beambin23952 -> 24000 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/disk_log.beambin31896 -> 31848 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/disk_log_1.beambin23964 -> 23888 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/disk_log_server.beambin6364 -> 6360 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/dist_ac.beambin24872 -> 24844 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/dist_util.beambin12168 -> 12140 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/erl_boot_server.beambin5760 -> 5744 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/erl_epmd.beambin7184 -> 7176 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/erl_signal_handler.beambin956 -> 956 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/error_handler.beambin1628 -> 1596 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/error_logger.beambin6280 -> 6280 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/erts_debug.beambin6008 -> 5908 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/file.beambin14088 -> 14052 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/file_io_server.beambin15716 -> 15700 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/global.beambin31248 -> 31240 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/global_group.beambin17020 -> 16916 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/group.beambin14652 -> 14636 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/group_history.beambin5728 -> 5720 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/heart.beambin5352 -> 5352 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/hipe_unified_loader.beambin12492 -> 12496 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/inet.beambin23224 -> 23220 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/inet_config.beambin7532 -> 7532 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/inet_db.beambin26416 -> 26404 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/inet_dns.beambin19116 -> 19112 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/inet_gethost_native.beambin10096 -> 10084 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/inet_hosts.beambin2120 -> 2128 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/inet_parse.beambin13804 -> 13792 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/inet_res.beambin14256 -> 14256 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/inet_tcp_dist.beambin7376 -> 7364 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/inet_udp.beambin1924 -> 1924 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/kernel.app2
-rw-r--r--bootstrap/lib/kernel/ebin/kernel.appup8
-rw-r--r--bootstrap/lib/kernel/ebin/net_adm.beambin2920 -> 2920 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/net_kernel.beambin25612 -> 25580 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/os.beambin4876 -> 5148 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/pg2.beambin7860 -> 7860 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/raw_file_io_deflate.beambin2704 -> 2704 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/raw_file_io_inflate.beambin4240 -> 4240 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/rpc.beambin7984 -> 7984 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/user.beambin11496 -> 11488 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/wrap_log_reader.beambin3124 -> 3124 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/array.beambin11776 -> 11776 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/beam_lib.beambin19440 -> 19436 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/binary.beambin2760 -> 2952 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/c.beambin17452 -> 17420 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/calendar.beambin4972 -> 4960 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/dets.beambin48896 -> 48872 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/dets_server.beambin6744 -> 6732 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/dets_utils.beambin27204 -> 27260 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/dets_v9.beambin47860 -> 47804 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/dict.beambin9520 -> 9516 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/digraph.beambin7876 -> 7852 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/edlin.beambin10996 -> 10952 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/edlin_expand.beambin3984 -> 3976 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/epp.beambin27724 -> 27708 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/erl_bits.beambin2468 -> 2452 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/erl_compile.beambin7168 -> 7168 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/erl_eval.beambin30304 -> 30268 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/erl_expand_records.beambin21740 -> 21732 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/erl_lint.beambin92196 -> 92104 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/erl_parse.beambin97948 -> 97400 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/erl_pp.beambin26624 -> 26616 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/erl_tar.beambin32328 -> 33388 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/error_logger_file_h.beambin4060 -> 4056 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/error_logger_tty_h.beambin4936 -> 4936 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/escript.beambin16864 -> 16848 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/ets.beambin22292 -> 22268 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/eval_bits.beambin8144 -> 8144 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/file_sorter.beambin29256 -> 29132 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/filelib.beambin10692 -> 10688 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/filename.beambin15124 -> 15096 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/gb_sets.beambin8388 -> 8400 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/gen.beambin5504 -> 5504 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/gen_event.beambin13460 -> 13432 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/gen_fsm.beambin11068 -> 11048 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/gen_server.beambin14600 -> 14352 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/gen_statem.beambin17908 -> 19656 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/io.beambin6028 -> 6020 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/io_lib.beambin12052 -> 12040 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/io_lib_format.beambin13268 -> 13268 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/io_lib_pretty.beambin17064 -> 17048 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/lib.beambin14980 -> 14968 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/lists.beambin29860 -> 29872 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/ms_transform.beambin19456 -> 19440 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/pool.beambin3744 -> 3744 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/proc_lib.beambin11620 -> 11568 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/proplists.beambin4708 -> 4708 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/qlc.beambin68916 -> 68796 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/qlc_pt.beambin75060 -> 75004 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/queue.beambin6144 -> 6124 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/re.beambin13256 -> 13240 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/sets.beambin6524 -> 6524 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/shell.beambin29848 -> 29796 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/slave.beambin4736 -> 4740 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/sofs.beambin37520 -> 37420 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/stdlib.appup2
-rw-r--r--bootstrap/lib/stdlib/ebin/string.beambin35608 -> 35368 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/supervisor.beambin21924 -> 21880 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/sys.beambin8364 -> 8356 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/unicode_util.beambin194732 -> 194724 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/uri_string.beambin26552 -> 26712 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/win32reg.beambin5356 -> 5356 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/zip.beambin26232 -> 26148 bytes
-rw-r--r--bootstrap/lib/stdlib/include/assert.hrl22
-rw-r--r--erts/configure.in45
-rw-r--r--erts/doc/src/absform.xml42
-rw-r--r--erts/doc/src/alt_dist.xml11
-rw-r--r--erts/doc/src/erl.xml14
-rw-r--r--erts/doc/src/erl_dist_protocol.xml7
-rw-r--r--erts/doc/src/erl_nif.xml124
-rw-r--r--erts/doc/src/erlang.xml1642
-rw-r--r--erts/doc/src/erts_alloc.xml75
-rw-r--r--erts/doc/src/match_spec.xml3
-rw-r--r--erts/doc/src/notes.xml171
-rw-r--r--erts/emulator/Makefile.in10
-rw-r--r--erts/emulator/beam/atom.c2
-rw-r--r--erts/emulator/beam/atom.names49
-rw-r--r--erts/emulator/beam/beam_bif_load.c101
-rw-r--r--erts/emulator/beam/beam_debug.c6
-rw-r--r--erts/emulator/beam/beam_emu.c17
-rw-r--r--erts/emulator/beam/beam_load.c63
-rw-r--r--erts/emulator/beam/bif.c1770
-rw-r--r--erts/emulator/beam/bif.h69
-rw-r--r--erts/emulator/beam/bif.tab21
-rw-r--r--erts/emulator/beam/binary.c5
-rw-r--r--erts/emulator/beam/break.c163
-rw-r--r--erts/emulator/beam/bs_instrs.tab3
-rw-r--r--erts/emulator/beam/copy.c9
-rw-r--r--erts/emulator/beam/dist.c1862
-rw-r--r--erts/emulator/beam/dist.h115
-rw-r--r--erts/emulator/beam/erl_afit_alloc.c2
-rw-r--r--erts/emulator/beam/erl_alloc.c290
-rw-r--r--erts/emulator/beam/erl_alloc.h12
-rw-r--r--erts/emulator/beam/erl_alloc.types15
-rw-r--r--erts/emulator/beam/erl_alloc_util.c890
-rw-r--r--erts/emulator/beam/erl_alloc_util.h155
-rw-r--r--erts/emulator/beam/erl_ao_firstfit_alloc.c293
-rw-r--r--erts/emulator/beam/erl_ao_firstfit_alloc.h16
-rw-r--r--erts/emulator/beam/erl_bestfit_alloc.c2
-rw-r--r--erts/emulator/beam/erl_bif_binary.c211
-rw-r--r--erts/emulator/beam/erl_bif_chksum.c6
-rw-r--r--erts/emulator/beam/erl_bif_ddll.c12
-rw-r--r--erts/emulator/beam/erl_bif_info.c1027
-rw-r--r--erts/emulator/beam/erl_bif_port.c95
-rw-r--r--erts/emulator/beam/erl_bif_re.c18
-rw-r--r--erts/emulator/beam/erl_bif_trace.c3
-rw-r--r--erts/emulator/beam/erl_bif_unique.h4
-rw-r--r--erts/emulator/beam/erl_bits.h2
-rw-r--r--erts/emulator/beam/erl_db.c30
-rw-r--r--erts/emulator/beam/erl_db_util.c118
-rw-r--r--erts/emulator/beam/erl_debug.c36
-rw-r--r--erts/emulator/beam/erl_drv_thread.c8
-rw-r--r--erts/emulator/beam/erl_gc.c393
-rw-r--r--erts/emulator/beam/erl_gc.h29
-rw-r--r--erts/emulator/beam/erl_goodfit_alloc.c14
-rw-r--r--erts/emulator/beam/erl_hl_timer.c40
-rw-r--r--erts/emulator/beam/erl_init.c107
-rw-r--r--erts/emulator/beam/erl_instrument.c2
-rw-r--r--erts/emulator/beam/erl_io_queue.c36
-rw-r--r--erts/emulator/beam/erl_lock_check.c15
-rw-r--r--erts/emulator/beam/erl_map.c4
-rw-r--r--erts/emulator/beam/erl_message.c335
-rw-r--r--erts/emulator/beam/erl_message.h259
-rw-r--r--erts/emulator/beam/erl_monitor_link.c1341
-rw-r--r--erts/emulator/beam/erl_monitor_link.h2326
-rw-r--r--erts/emulator/beam/erl_monitors.c1073
-rw-r--r--erts/emulator/beam/erl_monitors.h187
-rw-r--r--erts/emulator/beam/erl_msacc.c4
-rw-r--r--erts/emulator/beam/erl_msacc.h4
-rw-r--r--erts/emulator/beam/erl_mtrace.c22
-rw-r--r--erts/emulator/beam/erl_nif.c776
-rw-r--r--erts/emulator/beam/erl_nif.h15
-rw-r--r--erts/emulator/beam/erl_nif_api_funcs.h19
-rw-r--r--erts/emulator/beam/erl_node_container_utils.h2
-rw-r--r--erts/emulator/beam/erl_node_tables.c437
-rw-r--r--erts/emulator/beam/erl_node_tables.h64
-rw-r--r--erts/emulator/beam/erl_port.h87
-rw-r--r--erts/emulator/beam/erl_port_task.c25
-rw-r--r--erts/emulator/beam/erl_proc_sig_queue.c3925
-rw-r--r--erts/emulator/beam/erl_proc_sig_queue.h690
-rw-r--r--erts/emulator/beam/erl_process.c2216
-rw-r--r--erts/emulator/beam/erl_process.h267
-rw-r--r--erts/emulator/beam/erl_process_dict.c158
-rw-r--r--erts/emulator/beam/erl_process_dump.c172
-rw-r--r--erts/emulator/beam/erl_process_lock.c98
-rw-r--r--erts/emulator/beam/erl_process_lock.h56
-rw-r--r--erts/emulator/beam/erl_ptab.h6
-rw-r--r--erts/emulator/beam/erl_rbtree.h181
-rw-r--r--erts/emulator/beam/erl_sched_spec_pre_alloc.c13
-rw-r--r--erts/emulator/beam/erl_term.h10
-rw-r--r--erts/emulator/beam/erl_time.h11
-rw-r--r--erts/emulator/beam/erl_time_sup.c95
-rw-r--r--erts/emulator/beam/erl_trace.c34
-rw-r--r--erts/emulator/beam/erl_unicode.c12
-rw-r--r--erts/emulator/beam/erl_utils.h2
-rw-r--r--erts/emulator/beam/erl_vm.h2
-rw-r--r--erts/emulator/beam/external.c29
-rw-r--r--erts/emulator/beam/external.h5
-rw-r--r--erts/emulator/beam/global.h23
-rw-r--r--erts/emulator/beam/hash.c4
-rw-r--r--erts/emulator/beam/instrs.tab10
-rw-r--r--erts/emulator/beam/io.c903
-rw-r--r--erts/emulator/beam/lttng-wrapper.h4
-rw-r--r--erts/emulator/beam/msg_instrs.tab97
-rw-r--r--erts/emulator/beam/ops.tab9
-rw-r--r--erts/emulator/beam/packet_parser.c8
-rw-r--r--erts/emulator/beam/sys.h86
-rw-r--r--erts/emulator/beam/utils.c3
-rw-r--r--erts/emulator/hipe/hipe_amd64.c117
-rw-r--r--erts/emulator/hipe/hipe_bif0.c6
-rw-r--r--erts/emulator/hipe/hipe_gc.c16
-rw-r--r--erts/emulator/hipe/hipe_mkliterals.c10
-rw-r--r--erts/emulator/hipe/hipe_mode_switch.c9
-rw-r--r--erts/emulator/hipe/hipe_native_bif.c80
-rw-r--r--erts/emulator/internal_doc/CarrierMigration.md138
-rw-r--r--erts/emulator/nifs/common/prim_file_nif.c6
-rw-r--r--erts/emulator/nifs/common/prim_file_nif.h2
-rw-r--r--erts/emulator/nifs/unix/unix_prim_file.c21
-rw-r--r--erts/emulator/nifs/win32/win_prim_file.c2
-rw-r--r--erts/emulator/sys/common/erl_check_io.c4
-rw-r--r--erts/emulator/sys/common/erl_mmap.c103
-rw-r--r--erts/emulator/sys/common/erl_mmap.h16
-rw-r--r--erts/emulator/sys/common/erl_mseg.c10
-rw-r--r--erts/emulator/sys/common/erl_mseg.h2
-rw-r--r--erts/emulator/sys/unix/erl_child_setup.c17
-rw-r--r--erts/emulator/sys/unix/sys_drivers.c3
-rw-r--r--erts/emulator/sys/unix/sys_uds.c36
-rw-r--r--erts/emulator/sys/unix/sys_uds.h14
-rw-r--r--erts/emulator/test/alloc_SUITE.erl142
-rw-r--r--erts/emulator/test/alloc_SUITE_data/allocator_test.h5
-rw-r--r--erts/emulator/test/alloc_SUITE_data/migration.c112
-rw-r--r--erts/emulator/test/bif_SUITE.erl152
-rw-r--r--erts/emulator/test/binary_SUITE.erl1
-rw-r--r--erts/emulator/test/dirty_nif_SUITE_data/dirty_nif_SUITE.c31
-rw-r--r--erts/emulator/test/efile_SUITE.erl21
-rw-r--r--erts/emulator/test/erl_link_SUITE.erl266
-rw-r--r--erts/emulator/test/iovec_SUITE.erl46
-rw-r--r--erts/emulator/test/module_info_SUITE.erl18
-rw-r--r--erts/emulator/test/monitor_SUITE.erl10
-rw-r--r--erts/emulator/test/nif_SUITE.erl36
-rw-r--r--erts/emulator/test/nif_SUITE_data/nif_SUITE.c47
-rw-r--r--erts/emulator/test/port_SUITE.erl9
-rw-r--r--erts/emulator/test/port_trace_SUITE.erl14
-rw-r--r--erts/emulator/test/process_SUITE.erl28
-rw-r--r--erts/emulator/test/signal_SUITE.erl412
-rw-r--r--erts/emulator/test/trace_SUITE.erl91
-rw-r--r--erts/etc/common/erlexec.c2
-rw-r--r--erts/etc/common/inet_gethost.c4
-rw-r--r--erts/etc/unix/etp-commands.in175
-rw-r--r--erts/lib_src/common/erl_printf_format.c2
-rw-r--r--erts/preloaded/ebin/erl_prim_loader.beambin54268 -> 54160 bytes
-rw-r--r--erts/preloaded/ebin/erlang.beambin103008 -> 102724 bytes
-rw-r--r--erts/preloaded/ebin/erts_dirty_process_code_checker.beambin2100 -> 0 bytes
-rw-r--r--erts/preloaded/ebin/erts_dirty_process_signal_handler.beambin0 -> 2740 bytes
-rw-r--r--erts/preloaded/ebin/erts_internal.beambin13964 -> 15104 bytes
-rw-r--r--erts/preloaded/ebin/prim_file.beambin27428 -> 27496 bytes
-rw-r--r--erts/preloaded/src/Makefile4
-rw-r--r--erts/preloaded/src/erl_prim_loader.erl2
-rw-r--r--erts/preloaded/src/erlang.erl84
-rw-r--r--erts/preloaded/src/erts_dirty_process_code_checker.erl81
-rw-r--r--erts/preloaded/src/erts_dirty_process_signal_handler.erl97
-rw-r--r--erts/preloaded/src/erts_internal.erl61
-rw-r--r--erts/preloaded/src/init.erl4
-rw-r--r--erts/preloaded/src/prim_file.erl6
-rw-r--r--erts/preloaded/src/prim_zip.erl12
-rw-r--r--erts/test/z_SUITE.erl2
-rw-r--r--erts/vsn.mk2
-rw-r--r--lib/asn1/doc/src/notes.xml17
-rw-r--r--lib/asn1/src/asn1ct_gen_per.erl18
-rw-r--r--lib/asn1/vsn.mk2
-rw-r--r--lib/common_test/doc/src/notes.xml16
-rw-r--r--lib/common_test/src/common_test.app.src2
-rw-r--r--lib/common_test/src/ct.erl8
-rw-r--r--lib/common_test/src/ct_hooks.erl6
-rw-r--r--lib/common_test/src/ct_run.erl12
-rw-r--r--lib/common_test/src/ct_testspec.erl7
-rw-r--r--lib/common_test/src/ct_util.erl5
-rw-r--r--lib/common_test/src/ct_webtool.erl2
-rw-r--r--lib/common_test/src/test_server.erl33
-rw-r--r--lib/common_test/src/test_server_ctrl.erl6
-rw-r--r--lib/common_test/src/test_server_sup.erl4
-rw-r--r--lib/common_test/src/vts.erl2
-rw-r--r--lib/common_test/test/ct_netconfc_SUITE_data/ns.erl3
-rw-r--r--lib/common_test/test/ct_unicode_SUITE_data/unicode_atoms_SUITE.erl2
-rw-r--r--lib/common_test/test_server/ts_install_cth.erl3
-rw-r--r--lib/common_test/vsn.mk2
-rw-r--r--lib/compiler/doc/src/notes.xml25
-rw-r--r--lib/compiler/src/beam_a.erl8
-rw-r--r--lib/compiler/src/beam_asm.erl14
-rw-r--r--lib/compiler/src/beam_block.erl236
-rw-r--r--lib/compiler/src/beam_clean.erl21
-rw-r--r--lib/compiler/src/beam_dead.erl49
-rw-r--r--lib/compiler/src/beam_disasm.erl4
-rw-r--r--lib/compiler/src/beam_flatten.erl9
-rw-r--r--lib/compiler/src/beam_split.erl7
-rw-r--r--lib/compiler/src/beam_type.erl706
-rw-r--r--lib/compiler/src/beam_utils.erl116
-rw-r--r--lib/compiler/src/beam_validator.erl70
-rw-r--r--lib/compiler/src/beam_z.erl30
-rw-r--r--lib/compiler/src/compile.erl10
-rw-r--r--lib/compiler/src/core_parse.yrl2
-rwxr-xr-xlib/compiler/src/genop.tab10
-rw-r--r--lib/compiler/src/v3_codegen.erl50
-rw-r--r--lib/compiler/src/v3_core.erl10
-rw-r--r--lib/compiler/src/v3_kernel.erl13
-rw-r--r--lib/compiler/src/v3_kernel_pp.erl5
-rw-r--r--lib/compiler/test/beam_block_SUITE.erl72
-rw-r--r--lib/compiler/test/beam_type_SUITE.erl100
-rw-r--r--lib/compiler/test/beam_validator_SUITE.erl28
-rw-r--r--lib/compiler/test/beam_validator_SUITE_data/bad_try_catch_nesting.S64
-rw-r--r--lib/compiler/test/beam_validator_SUITE_data/bad_tuples.S88
-rw-r--r--lib/compiler/test/bs_construct_SUITE.erl9
-rw-r--r--lib/compiler/test/bs_match_SUITE.erl8
-rw-r--r--lib/compiler/test/compile_SUITE.erl17
-rw-r--r--lib/compiler/test/compile_SUITE_data/big.erl4
-rw-r--r--lib/compiler/test/lc_SUITE.erl33
-rw-r--r--lib/compiler/test/misc_SUITE.erl12
-rw-r--r--lib/compiler/vsn.mk2
-rw-r--r--lib/crypto/c_src/crypto.c68
-rw-r--r--lib/crypto/doc/src/notes.xml16
-rw-r--r--lib/crypto/src/crypto.erl31
-rw-r--r--lib/crypto/test/engine_SUITE.erl7
-rw-r--r--lib/crypto/vsn.mk2
-rw-r--r--lib/debugger/src/dbg_debugged.erl38
-rw-r--r--lib/debugger/src/dbg_ieval.erl5
-rw-r--r--lib/debugger/src/dbg_wx_mon.erl4
-rw-r--r--lib/debugger/src/dbg_wx_trace.erl4
-rw-r--r--lib/debugger/test/int_eval_SUITE.erl9
-rw-r--r--lib/debugger/test/int_eval_SUITE_data/my_int_eval_module.erl4
-rw-r--r--lib/dialyzer/doc/src/notes.xml26
-rw-r--r--lib/dialyzer/src/dialyzer_cl_parse.erl4
-rw-r--r--lib/dialyzer/src/dialyzer_contracts.erl4
-rw-r--r--lib/dialyzer/src/dialyzer_dataflow.erl23
-rw-r--r--lib/dialyzer/src/dialyzer_plt.erl20
-rw-r--r--lib/dialyzer/src/dialyzer_typesig.erl11
-rw-r--r--lib/dialyzer/src/typer.erl4
-rw-r--r--lib/dialyzer/test/behaviour_SUITE_data/src/proper/proper_typeserver.erl10
-rw-r--r--lib/dialyzer/test/opaque_SUITE_data/src/para_bug/same.erl15
-rw-r--r--lib/dialyzer/test/opaque_SUITE_data/src/para_bug/same_type.erl13
-rw-r--r--lib/dialyzer/test/opaque_SUITE_data/src/proper/proper_typeserver.erl10
-rw-r--r--lib/dialyzer/test/options1_SUITE_data/src/compiler/beam_validator.erl2
-rw-r--r--lib/dialyzer/test/r9c_SUITE_data/src/mnesia/mnesia_tm.erl2
-rw-r--r--lib/dialyzer/test/small_SUITE_data/results/chars6
-rw-r--r--lib/dialyzer/test/small_SUITE_data/results/extra_range4
-rw-r--r--lib/dialyzer/test/small_SUITE_data/results/record_match3
-rw-r--r--lib/dialyzer/test/small_SUITE_data/results/stacktrace5
-rw-r--r--lib/dialyzer/test/small_SUITE_data/src/chars.erl18
-rw-r--r--lib/dialyzer/test/small_SUITE_data/src/extra_range.erl59
-rw-r--r--lib/dialyzer/test/small_SUITE_data/src/record_match.erl17
-rw-r--r--lib/dialyzer/test/small_SUITE_data/src/stacktrace.erl73
-rw-r--r--lib/dialyzer/vsn.mk2
-rw-r--r--lib/diameter/doc/src/diameter.xml14
-rw-r--r--lib/diameter/doc/src/notes.xml26
-rw-r--r--lib/diameter/src/base/diameter_reg.erl17
-rw-r--r--lib/diameter/src/base/diameter_service.erl22
-rw-r--r--lib/diameter/src/diameter.appup.src15
-rw-r--r--lib/diameter/vsn.mk4
-rw-r--r--lib/et/src/et_wx_contents_viewer.erl4
-rw-r--r--lib/eunit/src/eunit_lib.erl4
-rw-r--r--lib/eunit/src/eunit_listener.erl3
-rw-r--r--lib/eunit/src/eunit_proc.erl2
-rw-r--r--lib/eunit/src/eunit_test.erl51
-rw-r--r--lib/hipe/cerl/Makefile2
-rw-r--r--lib/hipe/cerl/cerl_messagean.erl1095
-rw-r--r--lib/hipe/cerl/erl_bif_types.erl10
-rw-r--r--lib/hipe/doc/src/hipe_app.xml35
-rw-r--r--lib/hipe/doc/src/notes.xml39
-rw-r--r--lib/hipe/icode/hipe_beam_to_icode.erl10
-rw-r--r--lib/hipe/icode/hipe_icode_inline_bifs.erl22
-rw-r--r--lib/hipe/main/hipe.app.src1
-rw-r--r--lib/hipe/main/hipe_main.erl6
-rw-r--r--lib/hipe/rtl/hipe_rtl_verify_gcsafe.erl1
-rw-r--r--lib/hipe/vsn.mk2
-rw-r--r--lib/hipe/x86/hipe_rtl_to_x86.erl24
-rw-r--r--lib/hipe/x86/hipe_x86_assemble.erl1
-rw-r--r--lib/inets/doc/src/http_client.xml15
-rw-r--r--lib/inets/doc/src/http_uri.xml35
-rw-r--r--lib/inets/doc/src/httpc.xml18
-rw-r--r--lib/inets/doc/src/notes.xml32
-rw-r--r--lib/inets/src/http_client/httpc.erl56
-rw-r--r--lib/inets/src/http_client/httpc_handler.erl37
-rw-r--r--lib/inets/src/http_client/httpc_internal.hrl6
-rw-r--r--lib/inets/src/http_client/httpc_manager.erl10
-rw-r--r--lib/inets/src/http_lib/http_uri.erl45
-rw-r--r--lib/inets/src/http_server/httpd_response.erl4
-rw-r--r--lib/inets/src/inets_app/inets.appup.src4
-rw-r--r--lib/inets/src/inets_app/inets_internal.hrl2
-rw-r--r--lib/inets/test/http_test_lib.erl21
-rw-r--r--lib/inets/test/httpc_SUITE.erl112
-rw-r--r--lib/inets/test/uri_SUITE.erl21
-rw-r--r--lib/inets/vsn.mk2
-rw-r--r--lib/kernel/doc/src/code.xml18
-rw-r--r--lib/kernel/doc/src/file.xml3
-rw-r--r--lib/kernel/doc/src/inet.xml19
-rw-r--r--lib/kernel/doc/src/inet_res.xml6
-rw-r--r--lib/kernel/doc/src/notes.xml48
-rw-r--r--lib/kernel/doc/src/os.xml41
-rw-r--r--lib/kernel/doc/src/rpc.xml11
-rw-r--r--lib/kernel/include/dist.hrl26
-rw-r--r--lib/kernel/src/code.erl7
-rw-r--r--lib/kernel/src/code_server.erl27
-rw-r--r--lib/kernel/src/disk_log_1.erl2
-rw-r--r--lib/kernel/src/dist_util.erl203
-rw-r--r--lib/kernel/src/error_handler.erl4
-rw-r--r--lib/kernel/src/erts_debug.erl17
-rw-r--r--lib/kernel/src/file.erl4
-rw-r--r--lib/kernel/src/group.erl250
-rw-r--r--lib/kernel/src/hipe_unified_loader.erl1
-rw-r--r--lib/kernel/src/inet.erl17
-rw-r--r--lib/kernel/src/inet_hosts.erl5
-rw-r--r--lib/kernel/src/inet_res.erl28
-rw-r--r--lib/kernel/src/net_kernel.erl16
-rw-r--r--lib/kernel/src/os.erl41
-rw-r--r--lib/kernel/src/rpc.erl15
-rw-r--r--lib/kernel/test/code_SUITE.erl21
-rw-r--r--lib/kernel/test/erl_distribution_SUITE.erl27
-rw-r--r--lib/kernel/test/file_SUITE.erl49
-rw-r--r--lib/kernel/test/gen_sctp_SUITE.erl6
-rw-r--r--lib/kernel/test/gen_tcp_api_SUITE.erl4
-rw-r--r--lib/kernel/test/gen_udp_SUITE.erl4
-rw-r--r--lib/kernel/test/inet_SUITE.erl33
-rw-r--r--lib/kernel/test/inet_res_SUITE.erl4
-rw-r--r--lib/kernel/test/init_SUITE.erl67
-rw-r--r--lib/kernel/test/os_SUITE.erl18
-rw-r--r--lib/kernel/test/pdict_SUITE.erl32
-rw-r--r--lib/kernel/test/prim_file_SUITE.erl6
-rw-r--r--lib/kernel/vsn.mk2
-rw-r--r--lib/mnesia/src/mnesia.erl4
-rw-r--r--lib/mnesia/src/mnesia.hrl4
-rw-r--r--lib/mnesia/src/mnesia_bup.erl8
-rw-r--r--lib/mnesia/src/mnesia_checkpoint.erl8
-rw-r--r--lib/mnesia/src/mnesia_controller.erl5
-rw-r--r--lib/mnesia/src/mnesia_dumper.erl11
-rw-r--r--lib/mnesia/src/mnesia_frag.erl5
-rw-r--r--lib/mnesia/src/mnesia_lib.erl16
-rw-r--r--lib/mnesia/src/mnesia_loader.erl11
-rw-r--r--lib/mnesia/src/mnesia_locker.erl7
-rw-r--r--lib/mnesia/src/mnesia_recover.erl6
-rw-r--r--lib/mnesia/src/mnesia_schema.erl9
-rw-r--r--lib/mnesia/src/mnesia_tm.erl22
-rw-r--r--lib/mnesia/test/mnesia_test_lib.erl4
-rw-r--r--lib/mnesia/test/mnesia_test_lib.hrl12
-rw-r--r--lib/observer/doc/src/notes.xml58
-rw-r--r--lib/observer/src/cdv_html_wx.erl6
-rw-r--r--lib/observer/src/cdv_port_cb.erl17
-rw-r--r--lib/observer/src/cdv_proc_cb.erl4
-rw-r--r--lib/observer/src/cdv_sched_cb.erl18
-rw-r--r--lib/observer/src/crashdump_viewer.erl188
-rw-r--r--lib/observer/src/crashdump_viewer.hrl16
-rw-r--r--lib/observer/src/observer.app.src5
-rw-r--r--lib/observer/src/observer_alloc_wx.erl4
-rw-r--r--lib/observer/src/observer_perf_wx.erl4
-rw-r--r--lib/observer/src/observer_procinfo.erl2
-rw-r--r--lib/observer/src/observer_traceoptions_wx.erl2
-rw-r--r--lib/observer/test/crashdump_viewer_SUITE.erl4
-rw-r--r--lib/observer/vsn.mk2
-rw-r--r--lib/os_mon/src/disksup.erl4
-rw-r--r--lib/parsetools/include/yeccpre.hrl3
-rw-r--r--lib/parsetools/src/yeccparser.erl14
-rw-r--r--lib/parsetools/test/yecc_SUITE.erl3
-rw-r--r--lib/public_key/src/pubkey_cert.erl95
-rw-r--r--lib/public_key/src/public_key.app.src2
-rw-r--r--lib/public_key/src/public_key.erl2
-rw-r--r--lib/reltool/doc/src/reltool_examples.xml58
-rw-r--r--lib/reltool/src/reltool_app_win.erl4
-rw-r--r--lib/reltool/src/reltool_mod_win.erl4
-rw-r--r--lib/reltool/src/reltool_server.erl4
-rw-r--r--lib/reltool/src/reltool_sys_win.erl6
-rw-r--r--lib/runtime_tools/doc/src/Makefile8
-rw-r--r--lib/runtime_tools/doc/src/notes.xml32
-rw-r--r--lib/runtime_tools/doc/src/ref_man.xml1
-rw-r--r--lib/runtime_tools/doc/src/scheduler.xml135
-rw-r--r--lib/runtime_tools/doc/src/specs.xml1
-rw-r--r--lib/runtime_tools/src/Makefile1
-rw-r--r--lib/runtime_tools/src/erts_alloc_config.erl8
-rw-r--r--lib/runtime_tools/src/observer_backend.erl2
-rw-r--r--lib/runtime_tools/src/runtime_tools.app.src1
-rw-r--r--lib/runtime_tools/src/scheduler.erl152
-rw-r--r--lib/runtime_tools/src/system_information.erl40
-rw-r--r--lib/runtime_tools/test/Makefile1
-rw-r--r--lib/runtime_tools/test/dbg_SUITE.erl3
-rw-r--r--lib/runtime_tools/test/scheduler_SUITE.erl104
-rw-r--r--lib/runtime_tools/vsn.mk2
-rw-r--r--lib/sasl/doc/src/release_handler.xml2
-rw-r--r--lib/sasl/doc/src/systools.xml6
-rw-r--r--lib/sasl/src/systools_make.erl24
-rw-r--r--lib/sasl/test/release_handler_SUITE.erl8
-rw-r--r--lib/sasl/test/systools_SUITE.erl37
-rw-r--r--lib/snmp/doc/src/notes.xml18
-rw-r--r--lib/snmp/doc/src/snmp_impl_example_agent.xml10
-rw-r--r--lib/snmp/vsn.mk2
-rw-r--r--lib/ssh/doc/src/notes.xml222
-rw-r--r--lib/ssh/doc/src/ssh.xml105
-rw-r--r--lib/ssh/doc/src/ssh_connection.xml6
-rw-r--r--lib/ssh/doc/src/ssh_sftp.xml7
-rw-r--r--lib/ssh/src/ssh.erl3
-rw-r--r--lib/ssh/src/ssh.hrl4
-rw-r--r--lib/ssh/src/ssh_acceptor.erl16
-rw-r--r--lib/ssh/src/ssh_acceptor_sup.erl5
-rw-r--r--lib/ssh/src/ssh_channel_sup.erl11
-rw-r--r--lib/ssh/src/ssh_cli.erl265
-rw-r--r--lib/ssh/src/ssh_connection.erl28
-rw-r--r--lib/ssh/src/ssh_connection_handler.erl108
-rw-r--r--lib/ssh/src/ssh_connection_sup.erl5
-rw-r--r--lib/ssh/src/ssh_options.erl16
-rw-r--r--lib/ssh/src/ssh_subsystem_sup.erl8
-rw-r--r--lib/ssh/src/ssh_sup.erl15
-rw-r--r--lib/ssh/src/ssh_system_sup.erl13
-rw-r--r--lib/ssh/src/ssh_transport.erl5
-rw-r--r--lib/ssh/src/sshc_sup.erl14
-rw-r--r--lib/ssh/src/sshd_sup.erl6
-rw-r--r--lib/ssh/test/Makefile1
-rw-r--r--lib/ssh/test/ssh_basic_SUITE.erl373
-rw-r--r--lib/ssh/test/ssh_compat_SUITE.erl1019
-rwxr-xr-xlib/ssh/test/ssh_compat_SUITE_data/build_scripts/create-dropbear-ssh28
-rwxr-xr-xlib/ssh/test/ssh_compat_SUITE_data/build_scripts/create-dropbear-ssh-run27
-rwxr-xr-xlib/ssh/test/ssh_compat_SUITE_data/build_scripts/create-ssh-image3
-rwxr-xr-xlib/ssh/test/ssh_compat_SUITE_data/build_scripts/create-ssl-image14
-rwxr-xr-xlib/ssh/test/ssh_compat_SUITE_data/build_scripts/create_all28
-rw-r--r--lib/ssh/test/ssh_connection_SUITE.erl199
-rw-r--r--lib/ssh/test/ssh_engine_SUITE.erl26
-rw-r--r--lib/ssh/test/ssh_options_SUITE.erl122
-rw-r--r--lib/ssh/test/ssh_renegotiate_SUITE.erl237
-rw-r--r--lib/ssh/test/ssh_renegotiate_SUITE_data/id_dsa13
-rw-r--r--lib/ssh/test/ssh_renegotiate_SUITE_data/id_rsa15
-rw-r--r--lib/ssh/test/ssh_renegotiate_SUITE_data/ssh_host_dsa_key13
-rw-r--r--lib/ssh/test/ssh_renegotiate_SUITE_data/ssh_host_dsa_key.pub11
-rw-r--r--lib/ssh/test/ssh_renegotiate_SUITE_data/ssh_host_rsa_key16
-rw-r--r--lib/ssh/test/ssh_renegotiate_SUITE_data/ssh_host_rsa_key.pub5
-rw-r--r--lib/ssh/test/ssh_sup_SUITE.erl136
-rw-r--r--lib/ssh/test/ssh_test_lib.erl12
-rw-r--r--lib/ssh/test/ssh_test_lib.hrl7
-rw-r--r--lib/ssh/test/ssh_to_openssh_SUITE.erl359
-rw-r--r--lib/ssh/vsn.mk3
-rw-r--r--lib/ssl/doc/src/notes.xml106
-rw-r--r--lib/ssl/doc/src/ssl.xml220
-rw-r--r--lib/ssl/doc/src/ssl_app.xml33
-rw-r--r--lib/ssl/doc/src/ssl_crl_cache.xml6
-rw-r--r--lib/ssl/doc/src/ssl_distribution.xml50
-rw-r--r--lib/ssl/doc/src/ssl_introduction.xml7
-rw-r--r--lib/ssl/doc/src/ssl_protocol.xml24
-rw-r--r--lib/ssl/doc/src/using_ssl.xml80
-rw-r--r--lib/ssl/src/Makefile1
-rw-r--r--lib/ssl/src/dtls_connection.erl3
-rw-r--r--lib/ssl/src/dtls_record.erl1
-rw-r--r--lib/ssl/src/dtls_v1.erl8
-rw-r--r--lib/ssl/src/ssl.app.src3
-rw-r--r--lib/ssl/src/ssl.appup.src2
-rw-r--r--lib/ssl/src/ssl.erl152
-rw-r--r--lib/ssl/src/ssl_alert.erl4
-rw-r--r--lib/ssl/src/ssl_cipher.erl307
-rw-r--r--lib/ssl/src/ssl_connection.erl9
-rw-r--r--lib/ssl/src/ssl_crl_cache.erl10
-rw-r--r--lib/ssl/src/ssl_handshake.erl35
-rw-r--r--lib/ssl/src/ssl_internal.hrl2
-rw-r--r--lib/ssl/src/ssl_record.erl2
-rw-r--r--lib/ssl/src/ssl_v2.erl38
-rw-r--r--lib/ssl/src/tls_connection.erl17
-rw-r--r--lib/ssl/src/tls_handshake.erl55
-rw-r--r--lib/ssl/src/tls_record.erl32
-rw-r--r--lib/ssl/test/ssl_basic_SUITE.erl222
-rw-r--r--lib/ssl/test/ssl_engine_SUITE.erl39
-rw-r--r--lib/ssl/test/ssl_handshake_SUITE.erl10
-rw-r--r--lib/ssl/test/ssl_test_lib.erl107
-rw-r--r--lib/ssl/test/ssl_to_openssl_SUITE.erl745
-rw-r--r--lib/ssl/vsn.mk2
-rw-r--r--lib/stdlib/doc/src/assert_hrl.xml2
-rw-r--r--lib/stdlib/doc/src/erl_tar.xml43
-rw-r--r--lib/stdlib/doc/src/ets.xml37
-rw-r--r--lib/stdlib/doc/src/io.xml95
-rw-r--r--lib/stdlib/doc/src/lists.xml14
-rw-r--r--lib/stdlib/doc/src/notes.xml56
-rw-r--r--lib/stdlib/doc/src/timer.xml2
-rw-r--r--lib/stdlib/doc/src/uri_string.xml57
-rw-r--r--lib/stdlib/include/assert.hrl22
-rw-r--r--lib/stdlib/src/beam_lib.erl3
-rw-r--r--lib/stdlib/src/binary.erl28
-rw-r--r--lib/stdlib/src/c.erl8
-rw-r--r--lib/stdlib/src/dets.erl12
-rw-r--r--lib/stdlib/src/dets_utils.erl3
-rw-r--r--lib/stdlib/src/epp.erl18
-rw-r--r--lib/stdlib/src/erl_lint.erl8
-rw-r--r--lib/stdlib/src/erl_tar.erl80
-rw-r--r--lib/stdlib/src/erl_tar.hrl16
-rw-r--r--lib/stdlib/src/escript.erl18
-rw-r--r--lib/stdlib/src/ets.erl23
-rw-r--r--lib/stdlib/src/file_sorter.erl8
-rw-r--r--lib/stdlib/src/gen.erl80
-rw-r--r--lib/stdlib/src/gen_server.erl31
-rw-r--r--lib/stdlib/src/gen_statem.erl46
-rw-r--r--lib/stdlib/src/io_lib.erl58
-rw-r--r--lib/stdlib/src/io_lib_format.erl43
-rw-r--r--lib/stdlib/src/lists.erl19
-rw-r--r--lib/stdlib/src/proc_lib.erl12
-rw-r--r--lib/stdlib/src/qlc.erl53
-rw-r--r--lib/stdlib/src/shell.erl7
-rw-r--r--lib/stdlib/src/string.erl8
-rw-r--r--lib/stdlib/src/uri_string.erl65
-rw-r--r--lib/stdlib/src/zip.erl3
-rw-r--r--lib/stdlib/test/array_SUITE.erl8
-rw-r--r--lib/stdlib/test/dets_SUITE.erl4
-rw-r--r--lib/stdlib/test/error_logger_h_SUITE.erl3
-rw-r--r--lib/stdlib/test/ets_SUITE.erl63
-rw-r--r--lib/stdlib/test/gen_statem_SUITE.erl15
-rw-r--r--lib/stdlib/test/io_SUITE.erl87
-rw-r--r--lib/stdlib/test/lists_SUITE.erl20
-rw-r--r--lib/stdlib/test/proc_lib_SUITE.erl4
-rw-r--r--lib/stdlib/test/rand_SUITE.erl82
-rw-r--r--lib/stdlib/test/re_SUITE.erl15
-rw-r--r--lib/stdlib/test/stdlib_bench_SUITE.erl16
-rw-r--r--lib/stdlib/test/string_SUITE.erl14
-rw-r--r--lib/stdlib/test/tar_SUITE.erl30
-rw-r--r--lib/stdlib/test/unicode_util_SUITE.erl52
-rw-r--r--lib/stdlib/test/uri_string_SUITE.erl55
-rw-r--r--lib/stdlib/test/zip_SUITE.erl3
-rw-r--r--lib/stdlib/vsn.mk2
-rw-r--r--lib/tools/doc/src/lcnt.xml2
-rw-r--r--lib/tools/doc/src/notes.xml40
-rw-r--r--lib/tools/emacs/Makefile20
-rw-r--r--lib/tools/emacs/erlang-skels.el248
-rw-r--r--lib/tools/emacs/erlang.el130
-rw-r--r--lib/tools/emacs/test.erl.indented784
-rw-r--r--lib/tools/emacs/test.erl.orig784
-rw-r--r--lib/tools/src/fprof.erl3
-rw-r--r--lib/tools/src/lcnt.erl9
-rw-r--r--lib/tools/test/emacs_SUITE.erl73
-rw-r--r--lib/tools/test/emacs_SUITE_data/comments25
-rw-r--r--lib/tools/test/emacs_SUITE_data/comprehensions47
-rw-r--r--lib/tools/test/emacs_SUITE_data/funcs174
-rw-r--r--lib/tools/test/emacs_SUITE_data/highlight78
-rw-r--r--lib/tools/test/emacs_SUITE_data/icr157
-rw-r--r--lib/tools/test/emacs_SUITE_data/macros31
-rw-r--r--lib/tools/test/emacs_SUITE_data/records35
-rw-r--r--lib/tools/test/emacs_SUITE_data/terms174
-rw-r--r--lib/tools/test/emacs_SUITE_data/try_catch166
-rw-r--r--lib/tools/test/emacs_SUITE_data/type_specs110
-rw-r--r--lib/tools/test/lcnt_SUITE.erl15
-rw-r--r--lib/tools/vsn.mk2
-rw-r--r--lib/wx/api_gen/gen_util.erl4
-rw-r--r--lib/wx/api_gen/gl_gen.erl4
-rw-r--r--lib/wx/api_gen/gl_gen_erl.erl8
-rw-r--r--lib/wx/api_gen/wx_gen.erl4
-rw-r--r--lib/wx/examples/demo/ex_aui.erl3
-rw-r--r--lib/wx/src/wx.erl10
-rw-r--r--lib/wx/src/wxe_server.erl8
-rw-r--r--lib/xmerl/src/xmerl_xsd.erl2
-rw-r--r--lib/xmerl/test/xmerl_SUITE_data/eventp/CMOM.xml2
-rw-r--r--lib/xmerl/test/xmerl_SUITE_data/eventp/CelloMOM.xml2
-rw-r--r--otp_versions.table8
-rw-r--r--system/doc/design_principles/release_structure.xml6
-rw-r--r--system/doc/efficiency_guide/advanced.xml8
-rw-r--r--system/doc/reference_manual/modules.xml6
-rw-r--r--system/doc/system_principles/create_target.xmlsrc8
705 files changed, 28333 insertions, 17857 deletions
diff --git a/bootstrap/bin/no_dot_erlang.boot b/bootstrap/bin/no_dot_erlang.boot
index c8ea423483..010993cc67 100644
--- a/bootstrap/bin/no_dot_erlang.boot
+++ b/bootstrap/bin/no_dot_erlang.boot
Binary files differ
diff --git a/bootstrap/bin/start.boot b/bootstrap/bin/start.boot
index c8ea423483..010993cc67 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 c8ea423483..010993cc67 100644
--- a/bootstrap/bin/start_clean.boot
+++ b/bootstrap/bin/start_clean.boot
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/beam_a.beam b/bootstrap/lib/compiler/ebin/beam_a.beam
index 57affa6b30..72f044da26 100644
--- a/bootstrap/lib/compiler/ebin/beam_a.beam
+++ b/bootstrap/lib/compiler/ebin/beam_a.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/beam_asm.beam b/bootstrap/lib/compiler/ebin/beam_asm.beam
index 5e94a4b715..cca4e97485 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 082458338e..18f5dc6967 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_bs.beam b/bootstrap/lib/compiler/ebin/beam_bs.beam
index b624c01c96..1361b0b08d 100644
--- a/bootstrap/lib/compiler/ebin/beam_bs.beam
+++ b/bootstrap/lib/compiler/ebin/beam_bs.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/beam_bsm.beam b/bootstrap/lib/compiler/ebin/beam_bsm.beam
index 9a3a7623af..6bf3b576b2 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 959137f06f..ab18a32fa8 100644
--- a/bootstrap/lib/compiler/ebin/beam_clean.beam
+++ b/bootstrap/lib/compiler/ebin/beam_clean.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/beam_dead.beam b/bootstrap/lib/compiler/ebin/beam_dead.beam
index 7294026d65..33ee4dea87 100644
--- a/bootstrap/lib/compiler/ebin/beam_dead.beam
+++ b/bootstrap/lib/compiler/ebin/beam_dead.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/beam_dict.beam b/bootstrap/lib/compiler/ebin/beam_dict.beam
index 9c66f6d9f3..2e9c7d0f00 100644
--- a/bootstrap/lib/compiler/ebin/beam_dict.beam
+++ b/bootstrap/lib/compiler/ebin/beam_dict.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/beam_disasm.beam b/bootstrap/lib/compiler/ebin/beam_disasm.beam
index 256efdb750..5983f341f5 100644
--- a/bootstrap/lib/compiler/ebin/beam_disasm.beam
+++ b/bootstrap/lib/compiler/ebin/beam_disasm.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/beam_except.beam b/bootstrap/lib/compiler/ebin/beam_except.beam
index 19b30319ab..6eed82587a 100644
--- a/bootstrap/lib/compiler/ebin/beam_except.beam
+++ b/bootstrap/lib/compiler/ebin/beam_except.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/beam_flatten.beam b/bootstrap/lib/compiler/ebin/beam_flatten.beam
index 7fcbf5dcde..8758e98a71 100644
--- a/bootstrap/lib/compiler/ebin/beam_flatten.beam
+++ b/bootstrap/lib/compiler/ebin/beam_flatten.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/beam_jump.beam b/bootstrap/lib/compiler/ebin/beam_jump.beam
index e51f1b179b..5551ba9f90 100644
--- a/bootstrap/lib/compiler/ebin/beam_jump.beam
+++ b/bootstrap/lib/compiler/ebin/beam_jump.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/beam_opcodes.beam b/bootstrap/lib/compiler/ebin/beam_opcodes.beam
index 04305d7896..98b2c19b48 100644
--- a/bootstrap/lib/compiler/ebin/beam_opcodes.beam
+++ b/bootstrap/lib/compiler/ebin/beam_opcodes.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/beam_peep.beam b/bootstrap/lib/compiler/ebin/beam_peep.beam
index 58c0b1c623..d2b10e030b 100644
--- a/bootstrap/lib/compiler/ebin/beam_peep.beam
+++ b/bootstrap/lib/compiler/ebin/beam_peep.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/beam_receive.beam b/bootstrap/lib/compiler/ebin/beam_receive.beam
index c662b603a4..8d16d7d39d 100644
--- a/bootstrap/lib/compiler/ebin/beam_receive.beam
+++ b/bootstrap/lib/compiler/ebin/beam_receive.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/beam_record.beam b/bootstrap/lib/compiler/ebin/beam_record.beam
index 2901d6c33f..55cac5310d 100644
--- a/bootstrap/lib/compiler/ebin/beam_record.beam
+++ b/bootstrap/lib/compiler/ebin/beam_record.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/beam_reorder.beam b/bootstrap/lib/compiler/ebin/beam_reorder.beam
index d3d0b89a35..05562e19ea 100644
--- a/bootstrap/lib/compiler/ebin/beam_reorder.beam
+++ b/bootstrap/lib/compiler/ebin/beam_reorder.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/beam_split.beam b/bootstrap/lib/compiler/ebin/beam_split.beam
index 88b3f96d8c..8165aab002 100644
--- a/bootstrap/lib/compiler/ebin/beam_split.beam
+++ b/bootstrap/lib/compiler/ebin/beam_split.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/beam_trim.beam b/bootstrap/lib/compiler/ebin/beam_trim.beam
index 9143b1615d..75fcff822e 100644
--- a/bootstrap/lib/compiler/ebin/beam_trim.beam
+++ b/bootstrap/lib/compiler/ebin/beam_trim.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/beam_type.beam b/bootstrap/lib/compiler/ebin/beam_type.beam
index d3ca9d07de..0c8341a6c9 100644
--- a/bootstrap/lib/compiler/ebin/beam_type.beam
+++ b/bootstrap/lib/compiler/ebin/beam_type.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/beam_utils.beam b/bootstrap/lib/compiler/ebin/beam_utils.beam
index 24aaf40e10..1e9c30d00a 100644
--- a/bootstrap/lib/compiler/ebin/beam_utils.beam
+++ b/bootstrap/lib/compiler/ebin/beam_utils.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/beam_validator.beam b/bootstrap/lib/compiler/ebin/beam_validator.beam
index e87c1ed4ae..25f8772409 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/beam_z.beam b/bootstrap/lib/compiler/ebin/beam_z.beam
index 03685931e3..57698dbb3b 100644
--- a/bootstrap/lib/compiler/ebin/beam_z.beam
+++ b/bootstrap/lib/compiler/ebin/beam_z.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/cerl.beam b/bootstrap/lib/compiler/ebin/cerl.beam
index c49aac27a0..61515da20a 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/cerl_clauses.beam b/bootstrap/lib/compiler/ebin/cerl_clauses.beam
index fe1a83018e..fdd67aebde 100644
--- a/bootstrap/lib/compiler/ebin/cerl_clauses.beam
+++ b/bootstrap/lib/compiler/ebin/cerl_clauses.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/cerl_inline.beam b/bootstrap/lib/compiler/ebin/cerl_inline.beam
index 7ae6b03424..36888f6363 100644
--- a/bootstrap/lib/compiler/ebin/cerl_inline.beam
+++ b/bootstrap/lib/compiler/ebin/cerl_inline.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/cerl_trees.beam b/bootstrap/lib/compiler/ebin/cerl_trees.beam
index fc525300df..3deb60c450 100644
--- a/bootstrap/lib/compiler/ebin/cerl_trees.beam
+++ b/bootstrap/lib/compiler/ebin/cerl_trees.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/compile.beam b/bootstrap/lib/compiler/ebin/compile.beam
index 27e0802959..82d601c128 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.appup b/bootstrap/lib/compiler/ebin/compiler.appup
index 2973178217..a8847fc407 100644
--- a/bootstrap/lib/compiler/ebin/compiler.appup
+++ b/bootstrap/lib/compiler/ebin/compiler.appup
@@ -16,7 +16,7 @@
%% limitations under the License.
%%
%% %CopyrightEnd%
-{"7.1.3",
+{"7.1.4",
[{<<".*">>,[{restart_application, compiler}]}],
[{<<".*">>,[{restart_application, compiler}]}]
}.
diff --git a/bootstrap/lib/compiler/ebin/core_lint.beam b/bootstrap/lib/compiler/ebin/core_lint.beam
index a6f6a886ca..f3b420121a 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 d3085e14fb..cd4a22c6d8 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_pp.beam b/bootstrap/lib/compiler/ebin/core_pp.beam
index 57ed874762..59503a2933 100644
--- a/bootstrap/lib/compiler/ebin/core_pp.beam
+++ b/bootstrap/lib/compiler/ebin/core_pp.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/core_scan.beam b/bootstrap/lib/compiler/ebin/core_scan.beam
index 6195c3f759..bc0190fdb1 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_alias.beam b/bootstrap/lib/compiler/ebin/sys_core_alias.beam
index 3757572a10..52e46da7ad 100644
--- a/bootstrap/lib/compiler/ebin/sys_core_alias.beam
+++ b/bootstrap/lib/compiler/ebin/sys_core_alias.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/sys_core_bsm.beam b/bootstrap/lib/compiler/ebin/sys_core_bsm.beam
index 22d045a8a7..451b2ff3b5 100644
--- a/bootstrap/lib/compiler/ebin/sys_core_bsm.beam
+++ b/bootstrap/lib/compiler/ebin/sys_core_bsm.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/sys_core_dsetel.beam b/bootstrap/lib/compiler/ebin/sys_core_dsetel.beam
index b2ca054d45..c189549b16 100644
--- a/bootstrap/lib/compiler/ebin/sys_core_dsetel.beam
+++ b/bootstrap/lib/compiler/ebin/sys_core_dsetel.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 df1daa74d7..62e56d92b0 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_core_fold_lists.beam b/bootstrap/lib/compiler/ebin/sys_core_fold_lists.beam
index 346ddcb2a7..0ad7ab7f9c 100644
--- a/bootstrap/lib/compiler/ebin/sys_core_fold_lists.beam
+++ b/bootstrap/lib/compiler/ebin/sys_core_fold_lists.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/sys_core_inline.beam b/bootstrap/lib/compiler/ebin/sys_core_inline.beam
index b64e1be0ad..40f6ebf167 100644
--- a/bootstrap/lib/compiler/ebin/sys_core_inline.beam
+++ b/bootstrap/lib/compiler/ebin/sys_core_inline.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/sys_pre_attributes.beam b/bootstrap/lib/compiler/ebin/sys_pre_attributes.beam
index 2dd8388c18..91a29c5266 100644
--- a/bootstrap/lib/compiler/ebin/sys_pre_attributes.beam
+++ b/bootstrap/lib/compiler/ebin/sys_pre_attributes.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/v3_codegen.beam b/bootstrap/lib/compiler/ebin/v3_codegen.beam
index 2e88f9c333..bf1bcaafd2 100644
--- a/bootstrap/lib/compiler/ebin/v3_codegen.beam
+++ b/bootstrap/lib/compiler/ebin/v3_codegen.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/v3_core.beam b/bootstrap/lib/compiler/ebin/v3_core.beam
index 01055394ce..b4672b1315 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 1a5f4eb193..082bd6b75d 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_kernel_pp.beam b/bootstrap/lib/compiler/ebin/v3_kernel_pp.beam
index 57f30e63d8..fb780c2a43 100644
--- a/bootstrap/lib/compiler/ebin/v3_kernel_pp.beam
+++ b/bootstrap/lib/compiler/ebin/v3_kernel_pp.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/application_controller.beam b/bootstrap/lib/kernel/ebin/application_controller.beam
index f7b9037688..5e63aa4e8a 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 9547b5e159..4bafc43619 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/application_starter.beam b/bootstrap/lib/kernel/ebin/application_starter.beam
index 354b96eae5..ca7b77abcb 100644
--- a/bootstrap/lib/kernel/ebin/application_starter.beam
+++ b/bootstrap/lib/kernel/ebin/application_starter.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/auth.beam b/bootstrap/lib/kernel/ebin/auth.beam
index 41b47131c4..e8904c142a 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.beam b/bootstrap/lib/kernel/ebin/code.beam
index 108b562bea..102ac41ac2 100644
--- a/bootstrap/lib/kernel/ebin/code.beam
+++ b/bootstrap/lib/kernel/ebin/code.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/code_server.beam b/bootstrap/lib/kernel/ebin/code_server.beam
index bdc9dea570..922c5e6308 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 48af9c8383..76264b975b 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 bd20da839e..05c002ba67 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 c323958241..f3ca0687dd 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 c59c245f66..7419ba7c27 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 0c1804c2ca..3a4d230a7f 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_boot_server.beam b/bootstrap/lib/kernel/ebin/erl_boot_server.beam
index 226052b9ac..d921ffabd9 100644
--- a/bootstrap/lib/kernel/ebin/erl_boot_server.beam
+++ b/bootstrap/lib/kernel/ebin/erl_boot_server.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/erl_epmd.beam b/bootstrap/lib/kernel/ebin/erl_epmd.beam
index da09ca6122..d06aa1add3 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/erl_signal_handler.beam b/bootstrap/lib/kernel/ebin/erl_signal_handler.beam
index bbb5e7fcb4..881e36e6fb 100644
--- a/bootstrap/lib/kernel/ebin/erl_signal_handler.beam
+++ b/bootstrap/lib/kernel/ebin/erl_signal_handler.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/error_handler.beam b/bootstrap/lib/kernel/ebin/error_handler.beam
index f1a466ee6c..1ce96ca432 100644
--- a/bootstrap/lib/kernel/ebin/error_handler.beam
+++ b/bootstrap/lib/kernel/ebin/error_handler.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/error_logger.beam b/bootstrap/lib/kernel/ebin/error_logger.beam
index bc11d2eb54..4490480184 100644
--- a/bootstrap/lib/kernel/ebin/error_logger.beam
+++ b/bootstrap/lib/kernel/ebin/error_logger.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/erts_debug.beam b/bootstrap/lib/kernel/ebin/erts_debug.beam
index 0145b2e3c9..834f45ebfa 100644
--- a/bootstrap/lib/kernel/ebin/erts_debug.beam
+++ b/bootstrap/lib/kernel/ebin/erts_debug.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/file.beam b/bootstrap/lib/kernel/ebin/file.beam
index 7fa8c2a5c5..df612743bd 100644
--- a/bootstrap/lib/kernel/ebin/file.beam
+++ b/bootstrap/lib/kernel/ebin/file.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/file_io_server.beam b/bootstrap/lib/kernel/ebin/file_io_server.beam
index 00c19eb71c..d37ed85781 100644
--- a/bootstrap/lib/kernel/ebin/file_io_server.beam
+++ b/bootstrap/lib/kernel/ebin/file_io_server.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/global.beam b/bootstrap/lib/kernel/ebin/global.beam
index 7597ed4a09..8b057dff1a 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 9d119f0c96..b072f96e5d 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 99bd4bf343..43c35f0a3a 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/group_history.beam b/bootstrap/lib/kernel/ebin/group_history.beam
index 5e8390f36d..97f98ebe2f 100644
--- a/bootstrap/lib/kernel/ebin/group_history.beam
+++ b/bootstrap/lib/kernel/ebin/group_history.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/heart.beam b/bootstrap/lib/kernel/ebin/heart.beam
index bf849fb229..0c3caaf969 100644
--- a/bootstrap/lib/kernel/ebin/heart.beam
+++ b/bootstrap/lib/kernel/ebin/heart.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/hipe_unified_loader.beam b/bootstrap/lib/kernel/ebin/hipe_unified_loader.beam
index 8ca0b915a2..16841cc417 100644
--- a/bootstrap/lib/kernel/ebin/hipe_unified_loader.beam
+++ b/bootstrap/lib/kernel/ebin/hipe_unified_loader.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/inet.beam b/bootstrap/lib/kernel/ebin/inet.beam
index 7790401681..c1f7d2e8b0 100644
--- a/bootstrap/lib/kernel/ebin/inet.beam
+++ b/bootstrap/lib/kernel/ebin/inet.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/inet_config.beam b/bootstrap/lib/kernel/ebin/inet_config.beam
index 257bfe46de..5b50b553d7 100644
--- a/bootstrap/lib/kernel/ebin/inet_config.beam
+++ b/bootstrap/lib/kernel/ebin/inet_config.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/inet_db.beam b/bootstrap/lib/kernel/ebin/inet_db.beam
index 155536239d..e86ce51920 100644
--- a/bootstrap/lib/kernel/ebin/inet_db.beam
+++ b/bootstrap/lib/kernel/ebin/inet_db.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/inet_dns.beam b/bootstrap/lib/kernel/ebin/inet_dns.beam
index 12b7468fa6..c437ed85d7 100644
--- a/bootstrap/lib/kernel/ebin/inet_dns.beam
+++ b/bootstrap/lib/kernel/ebin/inet_dns.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/inet_gethost_native.beam b/bootstrap/lib/kernel/ebin/inet_gethost_native.beam
index 387f03b8df..ccd9f21cd4 100644
--- a/bootstrap/lib/kernel/ebin/inet_gethost_native.beam
+++ b/bootstrap/lib/kernel/ebin/inet_gethost_native.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/inet_hosts.beam b/bootstrap/lib/kernel/ebin/inet_hosts.beam
index 209d86690d..601a6704ee 100644
--- a/bootstrap/lib/kernel/ebin/inet_hosts.beam
+++ b/bootstrap/lib/kernel/ebin/inet_hosts.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/inet_parse.beam b/bootstrap/lib/kernel/ebin/inet_parse.beam
index 5949536932..561e53dcbd 100644
--- a/bootstrap/lib/kernel/ebin/inet_parse.beam
+++ b/bootstrap/lib/kernel/ebin/inet_parse.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/inet_res.beam b/bootstrap/lib/kernel/ebin/inet_res.beam
index a2d5924dd0..8c18d0d1ad 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 4575ad2d9b..2aa0a7270a 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/inet_udp.beam b/bootstrap/lib/kernel/ebin/inet_udp.beam
index 317a0d4b51..d358ee15dc 100644
--- a/bootstrap/lib/kernel/ebin/inet_udp.beam
+++ b/bootstrap/lib/kernel/ebin/inet_udp.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/kernel.app b/bootstrap/lib/kernel/ebin/kernel.app
index 4979bf69b5..d027eb6bcb 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.4.1"},
+ {vsn, "5.4.2"},
{modules, [application,
application_controller,
application_master,
diff --git a/bootstrap/lib/kernel/ebin/kernel.appup b/bootstrap/lib/kernel/ebin/kernel.appup
index 7ddb592671..aad99fd30b 100644
--- a/bootstrap/lib/kernel/ebin/kernel.appup
+++ b/bootstrap/lib/kernel/ebin/kernel.appup
@@ -16,9 +16,11 @@
%% limitations under the License.
%%
%% %CopyrightEnd%
-{"5.4",
+{"5.4.2",
%% Up from - max one major revision back
- [{<<"5\\.3(\\.[0-9]+)*">>,[restart_new_emulator]}], % OTP-20.*
+ [{<<"5\\.[0-3](\\.[0-9]+)*">>,[restart_new_emulator]}, % OTP-19.*, OTP-20.0
+ {<<"5\\.4(\\.[0-9]+)*">>,[restart_new_emulator]}], % OTP-20.1+
%% Down to - max one major revision back
- [{<<"5\\.3(\\.[0-9]+)*">>,[restart_new_emulator]}] % OTP-20.*
+ [{<<"5\\.[0-3](\\.[0-9]+)*">>,[restart_new_emulator]}, % OTP-19.*, OTP-20.0
+ {<<"5\\.4(\\.[0-9]+)*">>,[restart_new_emulator]}] % OTP-20.1+
}.
diff --git a/bootstrap/lib/kernel/ebin/net_adm.beam b/bootstrap/lib/kernel/ebin/net_adm.beam
index 79dad6cfa5..05aa46dcb8 100644
--- a/bootstrap/lib/kernel/ebin/net_adm.beam
+++ b/bootstrap/lib/kernel/ebin/net_adm.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/net_kernel.beam b/bootstrap/lib/kernel/ebin/net_kernel.beam
index eea9fde3c6..0355024eb4 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 76894e9bc9..f2a003d44f 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/pg2.beam b/bootstrap/lib/kernel/ebin/pg2.beam
index 8039120d20..4661129bc9 100644
--- a/bootstrap/lib/kernel/ebin/pg2.beam
+++ b/bootstrap/lib/kernel/ebin/pg2.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/raw_file_io_deflate.beam b/bootstrap/lib/kernel/ebin/raw_file_io_deflate.beam
index 9060a2bffe..222817d912 100644
--- a/bootstrap/lib/kernel/ebin/raw_file_io_deflate.beam
+++ b/bootstrap/lib/kernel/ebin/raw_file_io_deflate.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/raw_file_io_inflate.beam b/bootstrap/lib/kernel/ebin/raw_file_io_inflate.beam
index 4c1df2bb57..228a8f28a5 100644
--- a/bootstrap/lib/kernel/ebin/raw_file_io_inflate.beam
+++ b/bootstrap/lib/kernel/ebin/raw_file_io_inflate.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/rpc.beam b/bootstrap/lib/kernel/ebin/rpc.beam
index 7be3630e48..e4ed087628 100644
--- a/bootstrap/lib/kernel/ebin/rpc.beam
+++ b/bootstrap/lib/kernel/ebin/rpc.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/user.beam b/bootstrap/lib/kernel/ebin/user.beam
index 58bd43a18e..e57d6896bd 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/wrap_log_reader.beam b/bootstrap/lib/kernel/ebin/wrap_log_reader.beam
index b38ddd7288..a0f24625b4 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/stdlib/ebin/array.beam b/bootstrap/lib/stdlib/ebin/array.beam
index 68bf4904ce..1dd8846725 100644
--- a/bootstrap/lib/stdlib/ebin/array.beam
+++ b/bootstrap/lib/stdlib/ebin/array.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/beam_lib.beam b/bootstrap/lib/stdlib/ebin/beam_lib.beam
index 9b70424aae..7ffab34254 100644
--- a/bootstrap/lib/stdlib/ebin/beam_lib.beam
+++ b/bootstrap/lib/stdlib/ebin/beam_lib.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/binary.beam b/bootstrap/lib/stdlib/ebin/binary.beam
index 85fc484c32..e712319947 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/c.beam b/bootstrap/lib/stdlib/ebin/c.beam
index fb24f24b98..b3f62c467d 100644
--- a/bootstrap/lib/stdlib/ebin/c.beam
+++ b/bootstrap/lib/stdlib/ebin/c.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/calendar.beam b/bootstrap/lib/stdlib/ebin/calendar.beam
index 3b38655345..89a9168805 100644
--- a/bootstrap/lib/stdlib/ebin/calendar.beam
+++ b/bootstrap/lib/stdlib/ebin/calendar.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/dets.beam b/bootstrap/lib/stdlib/ebin/dets.beam
index 7ffca2b7ee..9384a4379f 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 cfe0257a9f..31f0f2701a 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 76b65ad011..265facaa57 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_v9.beam b/bootstrap/lib/stdlib/ebin/dets_v9.beam
index adcb52b007..f53f9fcf85 100644
--- a/bootstrap/lib/stdlib/ebin/dets_v9.beam
+++ b/bootstrap/lib/stdlib/ebin/dets_v9.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/dict.beam b/bootstrap/lib/stdlib/ebin/dict.beam
index b04ad73e70..345df4c87f 100644
--- a/bootstrap/lib/stdlib/ebin/dict.beam
+++ b/bootstrap/lib/stdlib/ebin/dict.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/digraph.beam b/bootstrap/lib/stdlib/ebin/digraph.beam
index 606cf5c08b..c51ecf54fc 100644
--- a/bootstrap/lib/stdlib/ebin/digraph.beam
+++ b/bootstrap/lib/stdlib/ebin/digraph.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/edlin.beam b/bootstrap/lib/stdlib/ebin/edlin.beam
index 807c7df5c7..c8d4cac351 100644
--- a/bootstrap/lib/stdlib/ebin/edlin.beam
+++ b/bootstrap/lib/stdlib/ebin/edlin.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/edlin_expand.beam b/bootstrap/lib/stdlib/ebin/edlin_expand.beam
index 68515c00a0..62e209dcc7 100644
--- a/bootstrap/lib/stdlib/ebin/edlin_expand.beam
+++ b/bootstrap/lib/stdlib/ebin/edlin_expand.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/epp.beam b/bootstrap/lib/stdlib/ebin/epp.beam
index 66ddbaea09..58e29d0c3b 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_bits.beam b/bootstrap/lib/stdlib/ebin/erl_bits.beam
index 5b2a46aec1..b9b75ecaef 100644
--- a/bootstrap/lib/stdlib/ebin/erl_bits.beam
+++ b/bootstrap/lib/stdlib/ebin/erl_bits.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/erl_compile.beam b/bootstrap/lib/stdlib/ebin/erl_compile.beam
index d40909b3cf..a317ae28a1 100644
--- a/bootstrap/lib/stdlib/ebin/erl_compile.beam
+++ b/bootstrap/lib/stdlib/ebin/erl_compile.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/erl_eval.beam b/bootstrap/lib/stdlib/ebin/erl_eval.beam
index 95de15156d..eb913d070d 100644
--- a/bootstrap/lib/stdlib/ebin/erl_eval.beam
+++ b/bootstrap/lib/stdlib/ebin/erl_eval.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/erl_expand_records.beam b/bootstrap/lib/stdlib/ebin/erl_expand_records.beam
index 9032984049..1869788f7b 100644
--- a/bootstrap/lib/stdlib/ebin/erl_expand_records.beam
+++ b/bootstrap/lib/stdlib/ebin/erl_expand_records.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/erl_lint.beam b/bootstrap/lib/stdlib/ebin/erl_lint.beam
index c6c62b3657..b8350e8975 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 6ac2e29240..bfc5843912 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 16843acd17..ca3d18a5db 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/erl_tar.beam b/bootstrap/lib/stdlib/ebin/erl_tar.beam
index f2c935dad1..ba7382f874 100644
--- a/bootstrap/lib/stdlib/ebin/erl_tar.beam
+++ b/bootstrap/lib/stdlib/ebin/erl_tar.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/error_logger_file_h.beam b/bootstrap/lib/stdlib/ebin/error_logger_file_h.beam
index 7cc374d4b6..2f2e54de1a 100644
--- a/bootstrap/lib/stdlib/ebin/error_logger_file_h.beam
+++ b/bootstrap/lib/stdlib/ebin/error_logger_file_h.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/error_logger_tty_h.beam b/bootstrap/lib/stdlib/ebin/error_logger_tty_h.beam
index a4b3321379..d504f97f9f 100644
--- a/bootstrap/lib/stdlib/ebin/error_logger_tty_h.beam
+++ b/bootstrap/lib/stdlib/ebin/error_logger_tty_h.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/escript.beam b/bootstrap/lib/stdlib/ebin/escript.beam
index 5721d41a52..5ae2752a6d 100644
--- a/bootstrap/lib/stdlib/ebin/escript.beam
+++ b/bootstrap/lib/stdlib/ebin/escript.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/ets.beam b/bootstrap/lib/stdlib/ebin/ets.beam
index 06576e533a..f2a649e0d1 100644
--- a/bootstrap/lib/stdlib/ebin/ets.beam
+++ b/bootstrap/lib/stdlib/ebin/ets.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/eval_bits.beam b/bootstrap/lib/stdlib/ebin/eval_bits.beam
index 76b1a2a2ae..0a3c0b47fb 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 22e7f2c2b4..8a9e501e1b 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/filelib.beam b/bootstrap/lib/stdlib/ebin/filelib.beam
index e632631389..7fc4d0b06a 100644
--- a/bootstrap/lib/stdlib/ebin/filelib.beam
+++ b/bootstrap/lib/stdlib/ebin/filelib.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/filename.beam b/bootstrap/lib/stdlib/ebin/filename.beam
index 7fff3a33cc..42e8e24580 100644
--- a/bootstrap/lib/stdlib/ebin/filename.beam
+++ b/bootstrap/lib/stdlib/ebin/filename.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/gb_sets.beam b/bootstrap/lib/stdlib/ebin/gb_sets.beam
index 7b3b19efb8..4b524f17a3 100644
--- a/bootstrap/lib/stdlib/ebin/gb_sets.beam
+++ b/bootstrap/lib/stdlib/ebin/gb_sets.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/gen.beam b/bootstrap/lib/stdlib/ebin/gen.beam
index 14d8f04cba..0c36dfe05a 100644
--- a/bootstrap/lib/stdlib/ebin/gen.beam
+++ b/bootstrap/lib/stdlib/ebin/gen.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/gen_event.beam b/bootstrap/lib/stdlib/ebin/gen_event.beam
index f4b87ab26d..21806df360 100644
--- a/bootstrap/lib/stdlib/ebin/gen_event.beam
+++ b/bootstrap/lib/stdlib/ebin/gen_event.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/gen_fsm.beam b/bootstrap/lib/stdlib/ebin/gen_fsm.beam
index 566aa94342..fccfecd7d8 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 6ec210dada..3f2d31465f 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/gen_statem.beam b/bootstrap/lib/stdlib/ebin/gen_statem.beam
index b2c88ce783..c79b4a16f9 100644
--- a/bootstrap/lib/stdlib/ebin/gen_statem.beam
+++ b/bootstrap/lib/stdlib/ebin/gen_statem.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/io.beam b/bootstrap/lib/stdlib/ebin/io.beam
index 40109e2a13..e1841e184c 100644
--- a/bootstrap/lib/stdlib/ebin/io.beam
+++ b/bootstrap/lib/stdlib/ebin/io.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/io_lib.beam b/bootstrap/lib/stdlib/ebin/io_lib.beam
index 41fa869dde..adb2ac56b0 100644
--- a/bootstrap/lib/stdlib/ebin/io_lib.beam
+++ b/bootstrap/lib/stdlib/ebin/io_lib.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/io_lib_format.beam b/bootstrap/lib/stdlib/ebin/io_lib_format.beam
index e564659aa8..713f6236aa 100644
--- a/bootstrap/lib/stdlib/ebin/io_lib_format.beam
+++ b/bootstrap/lib/stdlib/ebin/io_lib_format.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/io_lib_pretty.beam b/bootstrap/lib/stdlib/ebin/io_lib_pretty.beam
index b2f489b702..bf9e95e6be 100644
--- a/bootstrap/lib/stdlib/ebin/io_lib_pretty.beam
+++ b/bootstrap/lib/stdlib/ebin/io_lib_pretty.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/lib.beam b/bootstrap/lib/stdlib/ebin/lib.beam
index 649d4452ad..7d342a59a9 100644
--- a/bootstrap/lib/stdlib/ebin/lib.beam
+++ b/bootstrap/lib/stdlib/ebin/lib.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/lists.beam b/bootstrap/lib/stdlib/ebin/lists.beam
index 5b91ffeca9..bfcebe04a4 100644
--- a/bootstrap/lib/stdlib/ebin/lists.beam
+++ b/bootstrap/lib/stdlib/ebin/lists.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/ms_transform.beam b/bootstrap/lib/stdlib/ebin/ms_transform.beam
index 3113f71b58..8228a076ba 100644
--- a/bootstrap/lib/stdlib/ebin/ms_transform.beam
+++ b/bootstrap/lib/stdlib/ebin/ms_transform.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/pool.beam b/bootstrap/lib/stdlib/ebin/pool.beam
index bc3b2a861c..b056cf44ec 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/proc_lib.beam b/bootstrap/lib/stdlib/ebin/proc_lib.beam
index 191dc94be7..6689783d5d 100644
--- a/bootstrap/lib/stdlib/ebin/proc_lib.beam
+++ b/bootstrap/lib/stdlib/ebin/proc_lib.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/proplists.beam b/bootstrap/lib/stdlib/ebin/proplists.beam
index 3750c1ddb3..a9768d2929 100644
--- a/bootstrap/lib/stdlib/ebin/proplists.beam
+++ b/bootstrap/lib/stdlib/ebin/proplists.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/qlc.beam b/bootstrap/lib/stdlib/ebin/qlc.beam
index 5b2d4bad4c..6c2895ac79 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 c9d329fe86..d40c197029 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/queue.beam b/bootstrap/lib/stdlib/ebin/queue.beam
index c4cce1f88d..8c6b611247 100644
--- a/bootstrap/lib/stdlib/ebin/queue.beam
+++ b/bootstrap/lib/stdlib/ebin/queue.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/re.beam b/bootstrap/lib/stdlib/ebin/re.beam
index 3f316c9a44..784cf2fe7a 100644
--- a/bootstrap/lib/stdlib/ebin/re.beam
+++ b/bootstrap/lib/stdlib/ebin/re.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/sets.beam b/bootstrap/lib/stdlib/ebin/sets.beam
index 2337a8a8d9..3f90d78b8f 100644
--- a/bootstrap/lib/stdlib/ebin/sets.beam
+++ b/bootstrap/lib/stdlib/ebin/sets.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/shell.beam b/bootstrap/lib/stdlib/ebin/shell.beam
index ca29384984..e2dfa3c636 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 f72a207e3e..596dda4ed5 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 ef540df4f2..bfefd0c3a9 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.appup b/bootstrap/lib/stdlib/ebin/stdlib.appup
index 9996bfae81..659cc434fc 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.4.2",
+{"3.4.3",
%% Up from - max one major revision back
[{<<"3\\.4(\\.[0-9]+)*">>,[restart_new_emulator]}], % OTP-20.*
%% Down to - max one major revision back
diff --git a/bootstrap/lib/stdlib/ebin/string.beam b/bootstrap/lib/stdlib/ebin/string.beam
index 670cd530e9..feeeec6a84 100644
--- a/bootstrap/lib/stdlib/ebin/string.beam
+++ b/bootstrap/lib/stdlib/ebin/string.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/supervisor.beam b/bootstrap/lib/stdlib/ebin/supervisor.beam
index 61ae3c3de0..61e8fca92c 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/sys.beam b/bootstrap/lib/stdlib/ebin/sys.beam
index abf6451cb0..0cec9f4be8 100644
--- a/bootstrap/lib/stdlib/ebin/sys.beam
+++ b/bootstrap/lib/stdlib/ebin/sys.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/unicode_util.beam b/bootstrap/lib/stdlib/ebin/unicode_util.beam
index 12280f5e58..ab18c78028 100644
--- a/bootstrap/lib/stdlib/ebin/unicode_util.beam
+++ b/bootstrap/lib/stdlib/ebin/unicode_util.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/uri_string.beam b/bootstrap/lib/stdlib/ebin/uri_string.beam
index 5880b38385..95b7a2e6ae 100644
--- a/bootstrap/lib/stdlib/ebin/uri_string.beam
+++ b/bootstrap/lib/stdlib/ebin/uri_string.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/win32reg.beam b/bootstrap/lib/stdlib/ebin/win32reg.beam
index 93f6458657..3194d09463 100644
--- a/bootstrap/lib/stdlib/ebin/win32reg.beam
+++ b/bootstrap/lib/stdlib/ebin/win32reg.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/zip.beam b/bootstrap/lib/stdlib/ebin/zip.beam
index e6ba80cc7d..3e19c83090 100644
--- a/bootstrap/lib/stdlib/ebin/zip.beam
+++ b/bootstrap/lib/stdlib/ebin/zip.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/include/assert.hrl b/bootstrap/lib/stdlib/include/assert.hrl
index 2fbaeba0b2..2ec89e7d8a 100644
--- a/bootstrap/lib/stdlib/include/assert.hrl
+++ b/bootstrap/lib/stdlib/include/assert.hrl
@@ -309,7 +309,7 @@
{unexpected_success, __V}]})
catch
Class:Term -> ok;
- __C:__T ->
+ __C:__T:__S ->
erlang:error({assertException,
[{module, ?MODULE},
{line, ?LINE},
@@ -318,8 +318,7 @@
"{ "++(??Class)++" , "++(??Term)
++" , [...] }"},
{unexpected_exception,
- {__C, __T,
- erlang:get_stacktrace()}}]})
+ {__C, __T, __S}}]})
end
end)())
end).
@@ -338,7 +337,7 @@
{unexpected_success, __V}]})
catch
Class:Term -> ok;
- __C:__T ->
+ __C:__T:__S ->
erlang:error({assertException,
[{module, ?MODULE},
{line, ?LINE},
@@ -348,8 +347,7 @@
"{ "++(??Class)++" , "++(??Term)
++" , [...] }"},
{unexpected_exception,
- {__C, __T,
- erlang:get_stacktrace()}}]})
+ {__C, __T, __S}}]})
end
end)())
end).
@@ -378,7 +376,7 @@
try (Expr) of
_ -> ok
catch
- __C:__T ->
+ __C:__T:__S ->
case __C of
Class ->
case __T of
@@ -391,9 +389,7 @@
"{ "++(??Class)++" , "
++(??Term)++" , [...] }"},
{unexpected_exception,
- {__C, __T,
- erlang:get_stacktrace()
- }}]});
+ {__C, __T, __S}}]});
_ -> ok
end;
_ -> ok
@@ -407,7 +403,7 @@
try (Expr) of
_ -> ok
catch
- __C:__T ->
+ __C:__T:__S ->
case __C of
Class ->
case __T of
@@ -421,9 +417,7 @@
"{ "++(??Class)++" , "
++(??Term)++" , [...] }"},
{unexpected_exception,
- {__C, __T,
- erlang:get_stacktrace()
- }}]});
+ {__C, __T, __S}}]});
_ -> ok
end;
_ -> ok
diff --git a/erts/configure.in b/erts/configure.in
index f15bb56435..820247b4b8 100644
--- a/erts/configure.in
+++ b/erts/configure.in
@@ -1760,6 +1760,25 @@ if test $ac_cv_sizeof_void_p = 8; then
fi
AC_SUBST(BITS64)
+AC_MSG_CHECKING([for C compiler 'restrict' support])
+restrict_keyword=""
+for x in restrict __restrict; do
+ AC_TRY_COMPILE([int * $x foo(int * $x arg);
+ int * $x foo(int * $x arg)
+ { int * $x var=arg; return var;}
+ ],[],
+ [restrict_keyword=$x],[])
+ if test "x$restrict_keyword" != "x"; then
+ break
+ fi
+done
+AC_DEFINE_UNQUOTED(ERTS_RESTRICT,[$restrict_keyword],[Type qualifier restrict])
+if test "x$restrict_keyword" != "x"; then
+ AC_MSG_RESULT(yes)
+else
+ AC_MSG_RESULT(no)
+fi
+
if test "x$ac_compiler_gnu" = "xyes"; then
AC_MSG_CHECKING([if we should add -fno-tree-copyrename to CFLAGS for computed gotos to work properly])
AC_TRY_COMPILE([],[
@@ -1779,8 +1798,6 @@ else
AC_MSG_RESULT(no)
fi
-
-
AC_MSG_CHECKING([for broken gcc-4.3.0 compiler])
AC_TRY_RUN([
/* pr36339.c */
@@ -2704,6 +2721,13 @@ LIBS=$saved_libs
dnl restore CPPFLAGS
CPPFLAGS=$saved_cppflags
+case $ARCH in
+ x86|amd64)
+ AC_DEFINE(ERTS_STRUCTURE_ALIGNED_ALLOC, 1, [Define if structure alignment is enough for allocators. If not defined, 64-bit alignment will be forced.]);;
+ *)
+ ;;
+esac
+
LM_SYS_IPV6
LM_SYS_MULTICAST
ERL_TIME_CORRECTION
@@ -2749,23 +2773,6 @@ if test "$cross_compiling" != "yes" && test X${enable_hipe} != Xno; then
fi
fi
-dnl Check to disable -fPIE and friends for HiPE on amd64
-if test X${enable_hipe} = Xyes && test X$ARCH = Xamd64; then
- AC_TRY_COMPILE(, [#if defined(__pie__) || defined(__PIE__)
- #error -fPIE is enabled by default
- #endif],
- [AC_MSG_NOTICE([No -fPIE enabled by default])],
- [AC_MSG_WARN([Security feature -fPIE will be disabled for HiPE])
- STATIC_CFLAGS="-fno-PIE $STATIC_CFLAGS"
- saved_LDFLAGS=$LDFLAGS
- LDFLAGS="-no-pie $LDFLAGS"
- AC_TRY_LINK(,, [],
- [LDFLAGS="-fno-PIE $saved_LDFLAGS"
- AC_TRY_LINK(,, [],
- [AC_MSG_WARN([Linked does not accept option -no-pie nor -fno-PIE])
- LDFLAGS=$saved_LDFLAGS])])])
-fi
-
if test X${enable_hipe} = Xyes; then
case $OPSYS in
linux)
diff --git a/erts/doc/src/absform.xml b/erts/doc/src/absform.xml
index e49c8c32e9..2ada903edb 100644
--- a/erts/doc/src/absform.xml
+++ b/erts/doc/src/absform.xml
@@ -4,7 +4,7 @@
<chapter>
<header>
<copyright>
- <year>2001</year><year>2017</year>
+ <year>2001</year><year>2018</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -614,26 +614,58 @@
<item>
<p>If C is a catch clause <c>P -> B</c>,
where <c>P</c> is a pattern and <c>B</c> is a body, then
- Rep(C) = <c>{clause,LINE,[Rep({throw,P,_})],[],Rep(B)}</c>.</p>
+ Rep(C) = <c>{clause,LINE,[Rep({throw,P,_})],[],Rep(B)}</c>,
+ that is, a catch clause with an explicit exception class
+ <c>throw</c> and with or without an explicit stacktrace
+ variable <c>_</c> cannot be distinguished from a catch clause
+ without an explicit exception class and without an explicit
+ stacktrace variable.</p>
</item>
<item>
<p>If C is a catch clause <c>X : P -> B</c>,
where <c>X</c> is an atomic literal or a variable pattern,
<c>P</c> is a pattern, and <c>B</c> is a body, then
- Rep(C) = <c>{clause,LINE,[Rep({X,P,_})],[],Rep(B)}</c>.</p>
+ Rep(C) = <c>{clause,LINE,[Rep({X,P,_})],[],Rep(B)}</c>,
+ that is, a catch clause with an explicit exception class and
+ with an explicit stacktrace variable <c>_</c> cannot be
+ distinguished from a catch clause with an explicit exception
+ class and without an explicit stacktrace variable.</p>
+ </item>
+ <item>
+ <p>If C is a catch clause <c>X : P : S -> B</c>,
+ where <c>X</c> is an atomic literal or a variable pattern,
+ <c>P</c> is a pattern, <c>S</c> is a variable, and <c>B</c>
+ is a body, then
+ Rep(C) = <c>{clause,LINE,[Rep({X,P,S})],[],Rep(B)}</c>.</p>
</item>
<item>
<p>If C is a catch clause <c>P when Gs -> B</c>,
where <c>P</c> is a pattern, <c>Gs</c> is a guard sequence,
and <c>B</c> is a body, then
- Rep(C) = <c>{clause,LINE,[Rep({throw,P,_})],Rep(Gs),Rep(B)}</c>.</p>
+ Rep(C) = <c>{clause,LINE,[Rep({throw,P,_})],Rep(Gs),Rep(B)}</c>,
+ that is, a catch clause with an explicit exception class
+ <c>throw</c> and with or without an explicit stacktrace
+ variable <c>_</c> cannot be distinguished from a catch clause
+ without an explicit exception class and without an explicit
+ stacktrace variable.</p>
</item>
<item>
<p>If C is a catch clause <c>X : P when Gs -> B</c>,
where <c>X</c> is an atomic literal or a variable pattern,
<c>P</c> is a pattern, <c>Gs</c> is a guard sequence,
and <c>B</c> is a body, then
- Rep(C) = <c>{clause,LINE,[Rep({X,P,_})],Rep(Gs),Rep(B)}</c>.</p>
+ Rep(C) = <c>{clause,LINE,[Rep({X,P,_})],Rep(Gs),Rep(B)}</c>,
+ that is, a catch clause with an explicit exception class and
+ with an explicit stacktrace variable <c>_</c> cannot be
+ distinguished from a catch clause with an explicit exception
+ class and without an explicit stacktrace variable.</p>
+ </item>
+ <item>
+ <p>If C is a catch clause <c>X : P : S when Gs -> B</c>,
+ where <c>X</c> is an atomic literal or a variable pattern,
+ <c>P</c> is a pattern, <c>Gs</c> is a guard sequence,
+ <c>S</c> is a variable, and <c>B</c> is a body, then
+ Rep(C) = <c>{clause,LINE,[Rep({X,P,S})],Rep(Gs),Rep(B)}</c>.</p>
</item>
<item>
<p>If C is a function clause <c>( Ps ) -> B</c>,
diff --git a/erts/doc/src/alt_dist.xml b/erts/doc/src/alt_dist.xml
index d3731a5391..92d40d8558 100644
--- a/erts/doc/src/alt_dist.xml
+++ b/erts/doc/src/alt_dist.xml
@@ -782,10 +782,9 @@
<taglist>
<tag><c>DFLAG_DIST_HDR_ATOM_CACHE</c></tag>
<item>Do not use atom cache over this connection.</item>
- <tag><c>DFLAGS_STRICT_ORDER_DELIVERY</c></tag>
- <item>Do not use any features that require strict
- order delivery.</item>
</taglist>
+ <p>Use function <c>dist_util:strict_order_flags/0</c> to get all flags
+ for features that require strict order delivery.</p>
<p>
This flag field is optional.
</p>
@@ -819,9 +818,9 @@
<p>
The data delivery order can be relaxed by disabling
features that require strict ordering. This is done by
- passing the <c>?DFLAGS_STRICT_ORDER_DELIVERY</c>
- <seealso marker="erl_dist_protocol#dflags">distribution
- flags</seealso> in the
+ passing the
+ <seealso marker="erl_dist_protocol#dflags">distribution flags</seealso>
+ returned by <c>dist_util:strict_order_flags/0</c> in the
<seealso marker="alt_dist#hs_data_reject_flags"><c>reject_flags</c></seealso>
field of the <seealso marker="#hs_data_record"><c>#hs_data{}</c></seealso>
record used when setting up the connection. When relaxed
diff --git a/erts/doc/src/erl.xml b/erts/doc/src/erl.xml
index bd824b3405..00dec37590 100644
--- a/erts/doc/src/erl.xml
+++ b/erts/doc/src/erl.xml
@@ -360,11 +360,12 @@
</item>
<tag><c><![CDATA[-mode interactive | embedded]]></c></tag>
<item>
- <p>Indicates if the system is to load code dynamically
- (<c><![CDATA[interactive]]></c>), or if all code is to be loaded
- during system initialization (<c><![CDATA[embedded]]></c>); see
- <seealso marker="kernel:code"><c>code(3)</c></seealso>.
- Defaults to <c><![CDATA[interactive]]></c>.</p>
+ <p>Modules are auto loaded when they are first referenced if the
+ runtime system runs in <c><![CDATA[interactive]]></c> mode, which is
+ the default. In <c><![CDATA[embedded]]></c> mode modules are not auto
+ loaded. The latter is recommended when the boot script preloads all
+ modules, as conventionally happens in OTP releases. See
+ <seealso marker="kernel:code"><c>code(3)</c></seealso></p>.
</item>
<tag><c><![CDATA[-name Name]]></c></tag>
<item>
@@ -571,7 +572,7 @@
<tag><marker id="async_thread_pool_size"/><c><![CDATA[+A size]]></c></tag>
<item>
<p>Sets the number of threads in async thread pool. Valid range
- is 0-1024. Defaults to 10 if thread support is available.</p>
+ is 0-1024. Defaults to 1.</p>
</item>
<tag><c><![CDATA[+B [c | d | i]]]></c></tag>
<item>
@@ -1693,4 +1694,3 @@ code:load_abs("..../user_default"). ]]></code>
<seealso marker="tools:make"><c>make(3)</c></seealso></p>
</section>
</comref>
-
diff --git a/erts/doc/src/erl_dist_protocol.xml b/erts/doc/src/erl_dist_protocol.xml
index a78b13aaa4..98a9a76b60 100644
--- a/erts/doc/src/erl_dist_protocol.xml
+++ b/erts/doc/src/erl_dist_protocol.xml
@@ -849,10 +849,9 @@ DiB == gen_digest(ChA, ICA)?
</item>
</taglist>
<p>
- There are also a collection of <c>DFLAG</c>s bitwise or:ed
- together in the <c>DFLAGS_STRICT_ORDER_DELIVERY</c> macro.
- These flags corresponds to features that require strict
- ordering of data over distribution channels.
+ There is also function <c>dist_util:strict_order_flags/0</c>
+ returning all flags (bitwise or:ed together) corresponding to features
+ that require strict ordering of data over distribution channels.
</p>
</section>
</section>
diff --git a/erts/doc/src/erl_nif.xml b/erts/doc/src/erl_nif.xml
index 23f0c66429..cabc07d020 100644
--- a/erts/doc/src/erl_nif.xml
+++ b/erts/doc/src/erl_nif.xml
@@ -540,7 +540,7 @@ int writeiovec(ErlNifEnv *env, ERL_NIF_TERM term, ERL_NIF_TERM *tail,
have to wait for a very long time. Blocking multi-scheduling, that
is, calling <seealso marker="erlang#system_flag_multi_scheduling">
<c>erlang:system_flag(multi_scheduling, block)</c></seealso>, can
- also take a very long time to complete. This becaue all ongoing
+ also take a very long time to complete. This is because all ongoing
dirty operations on all dirty schedulers must complete before
the block operation can complete.</p>
<p>Many operations communicating with a process executing a
@@ -590,7 +590,7 @@ int writeiovec(ErlNifEnv *env, ERL_NIF_TERM term, ERL_NIF_TERM *tail,
<c>unload</c> is called to release the library. All are
described individually below.</p>
<p>The fourth argument <c>NULL</c> is ignored. It
- was earlier used for the deprectated <c>reload</c> callback
+ was earlier used for the deprecated <c>reload</c> callback
which is no longer supported since OTP 20.</p>
<p>If compiling a NIF for static inclusion through
<c>--enable-static-nifs</c>, you must define <c>STATIC_ERLANG_NIF</c>
@@ -809,7 +809,7 @@ typedef void ErlNifResourceDtor(ErlNifEnv* env, void* obj);</code>
<tag><marker id="ErlNifResourceDown"/><c>ErlNifResourceDown</c></tag>
<item>
<code type="none">
-typedef void ErlNifResourceDown(ErlNifEnv* env, void* obj, const ErlNifPid* pid, const ErlNifMonitor* mon);</code>
+typedef void ErlNifResourceDown(ErlNifEnv* env, void* obj, ErlNifPid* pid, ErlNifMonitor* mon);</code>
<p>The function prototype of a resource down function,
called on the behalf of <seealso marker="#enif_monitor_process">
enif_monitor_process</seealso>. <c>obj</c> is the resource, <c>pid</c>
@@ -875,7 +875,7 @@ typedef enum {
<item>
<p>An enumeration of the properties that can be requested from
<seealso marker="#enif_make_unique_integer">
- <c>enif_unique_integer</c></seealso>.
+ <c>enif_make_unique_integer</c></seealso>.
For default properties, use value <c>0</c>.</p>
<taglist>
<tag><c>ERL_NIF_UNIQUE_POSITIVE</c></tag>
@@ -974,6 +974,10 @@ typedef struct {
<seealso marker="#enif_make_binary"><c>enif_make_binary</c></seealso>.
An allocated (and owned) <c>ErlNifBinary</c> can be kept between NIF
calls.</p>
+ <p>If you do not need to reallocate or keep the data alive across NIF
+ calls, consider using <seealso marker="#enif_make_new_binary">
+ <c>enif_make_new_binary</c></seealso> instead as it will allocate
+ small binaries on the process heap when possible.</p>
<p>Returns <c>true</c> on success, or <c>false</c> if allocation
fails.</p>
</desc>
@@ -1100,6 +1104,16 @@ typedef struct {
</func>
<func>
+ <name><ret>char*</ret>
+ <nametext>enif_cond_name(ErlNifCond* cnd)</nametext></name>
+ <fsummary></fsummary>
+ <desc>
+ <p>Same as <seealso marker="erl_driver#erl_drv_cond_name">
+ <c>erl_drv_cond_name</c></seealso>.</p>
+ </desc>
+ </func>
+
+ <func>
<name><ret>void</ret>
<nametext>enif_cond_signal(ErlNifCond *cnd)</nametext></name>
<fsummary></fsummary>
@@ -1232,6 +1246,18 @@ typedef struct {
</func>
<func>
+ <name><ret>int</ret><nametext>enif_fprintf(FILE *stream, const char *format, ...)</nametext></name>
+ <fsummary>Format strings and Erlang terms.</fsummary>
+ <desc>
+ <p>Similar to <c>fprintf</c> but this format string also accepts
+ <c>"%T"</c>, which formats Erlang terms.</p>
+ <p>This function was originally intenden for debugging purpose. It is not
+ recommended to print very large terms with <c>%T</c>. The function may
+ change <c>errno</c>, even if successful.</p>
+ </desc>
+ </func>
+
+ <func>
<name><ret>void</ret><nametext>enif_free(void* ptr)</nametext></name>
<fsummary>Free dynamic memory.</fsummary>
<desc>
@@ -2159,6 +2185,20 @@ enif_inspect_iovec(env, max_elements, term, &amp;tail, &amp;iovec);
</func>
<func>
+ <name><ret>int</ret>
+ <nametext>enif_make_map_from_arrays(ErlNifEnv* env, ERL_NIF_TERM keys[],
+ ERL_NIF_TERM values[], size_t cnt, ERL_NIF_TERM *map_out)</nametext></name>
+ <fsummary>Make map term from the given keys and values.</fsummary>
+ <desc>
+ <p>Makes a map term from the given keys and values.</p>
+ <p>If successful, this function sets <c>*map_out</c> to the new map and
+ returns <c>true</c>. Returns <c>false</c> there are any duplicate
+ keys.</p>
+ <p>All keys and values must belong to <c>env</c>.</p>
+ </desc>
+ </func>
+
+ <func>
<name><ret>unsigned char *</ret><nametext>enif_make_new_binary(ErlNifEnv*
env, size_t size, ERL_NIF_TERM* termp)</nametext></name>
<fsummary>Allocate and create a new binary term.</fsummary>
@@ -2232,7 +2272,7 @@ enif_inspect_iovec(env, max_elements, term, &amp;tail, &amp;iovec);
<seealso marker="#enif_get_resource"><c>enif_get_resource</c></seealso>.</p>
</item>
<item>
- <p>A resoure term can be serialized with <c>term_to_binary</c> and later
+ <p>A resource term can be serialized with <c>term_to_binary</c> and later
be fully recreated if the resource object is still alive when
<c>binary_to_term</c> is called. A <em>stale</em> resource term will be
returned from <c>binary_to_term</c> if the resource object has
@@ -2612,6 +2652,16 @@ enif_map_iterator_destroy(env, &amp;iter);</code>
</func>
<func>
+ <name><ret>char*</ret>
+ <nametext>enif_mutex_name(ErlNifMutex* mtx)</nametext></name>
+ <fsummary></fsummary>
+ <desc>
+ <p>Same as <seealso marker="erl_driver#erl_drv_mutex_name">
+ <c>erl_drv_mutex_name</c></seealso>.</p>
+ </desc>
+ </func>
+
+ <func>
<name><ret>int</ret>
<nametext>enif_mutex_trylock(ErlNifMutex *mtx)</nametext></name>
<fsummary></fsummary>
@@ -2856,6 +2906,16 @@ enif_map_iterator_destroy(env, &amp;iter);</code>
</func>
<func>
+ <name><ret>char*</ret>
+ <nametext>enif_rwlock_name(ErlNifRWLock* rwlck)</nametext></name>
+ <fsummary></fsummary>
+ <desc>
+ <p>Same as <seealso marker="erl_driver#erl_drv_rwlock_name">
+ <c>erl_drv_rwlock_name</c></seealso>.</p>
+ </desc>
+ </func>
+
+ <func>
<name><ret>void</ret>
<nametext>enif_rwlock_rlock(ErlNifRWLock *rwlck)</nametext></name>
<fsummary></fsummary>
@@ -2976,7 +3036,7 @@ enif_map_iterator_destroy(env, &amp;iter);</code>
<p>Argument <c>mode</c> describes the type of events to wait for. It can be
<c>ERL_NIF_SELECT_READ</c>, <c>ERL_NIF_SELECT_WRITE</c> or a bitwise
OR combination to wait for both. It can also be <c>ERL_NIF_SELECT_STOP</c>
- which is described further below. When a read or write event is triggerred,
+ which is described further below. When a read or write event is triggered,
a notification message like this is sent to the process identified by
<c>pid</c>:</p>
<code type="none">{select, Obj, Ref, ready_input | ready_output}</code>
@@ -3027,7 +3087,7 @@ enif_map_iterator_destroy(env, &amp;iter);</code>
<item>The stop callback was scheduled to run on some other thread
or later by this thread.</item>
</taglist>
- <p>Returns a negative value if the call failed where the follwing bits can be set:</p>
+ <p>Returns a negative value if the call failed where the following bits can be set:</p>
<taglist>
<tag><c>ERL_NIF_SELECT_INVALID_EVENT</c></tag>
<item>Argument <c>event</c> is not a valid OS event object.</item>
@@ -3035,9 +3095,9 @@ enif_map_iterator_destroy(env, &amp;iter);</code>
<item>The system call failed to add the event object to the poll set.</item>
</taglist>
<note>
- <p>Use bitwise AND to test for specific bits in the return vaue.
+ <p>Use bitwise AND to test for specific bits in the return value.
New significant bits may be added in future releases to give more detailed
- information for both failed and successful calls. Do NOT use equallity tests
+ information for both failed and successful calls. Do NOT use equality tests
like <c>==</c>, as that may cause your application to stop working.</p>
<p>Example:</p>
<code type="none">
@@ -3060,9 +3120,10 @@ if (retval &amp; ERL_NIF_SELECT_STOP_CALLED) {
</name>
<fsummary>Get the pid of the calling process.</fsummary>
<desc>
- <p>Initializes the pid variable <c>*pid</c> to represent the
- calling process.</p>
- <p>Returns <c>pid</c>.</p>
+ <p>Initializes the <seealso marker="#ErlNifPid"><c>ErlNifPid</c></seealso>
+ variable at <c>*pid</c> to represent the calling process.</p>
+ <p>Returns <c>pid</c> if successful, or NULL if <c>caller_env</c> is not
+ a <seealso marker="#ErlNifEnv">process-bound environment</seealso>.</p>
</desc>
</func>
@@ -3101,7 +3162,7 @@ if (retval &amp; ERL_NIF_SELECT_STOP_CALLED) {
<c>enif_free_env</c></seealso> of cleared for reuse with
<seealso marker="#enif_clear_env"><c>enif_clear_env</c></seealso>.</p>
<p>If <c>msg_env</c> is set to <c>NULL</c>, the <c>msg</c> term is
- copied and the original term and its environemt is still valid after
+ copied and the original term and its environment is still valid after
the call.</p>
<p>This function is only thread-safe when the emulator with SMP support
is used. It can only be used in a non-SMP emulator from a NIF-calling
@@ -3131,6 +3192,9 @@ if (retval &amp; ERL_NIF_SELECT_STOP_CALLED) {
<desc>
<p>Similar to <c>snprintf</c> but this format string also accepts
<c>"%T"</c>, which formats Erlang terms.</p>
+ <p>This function was originally intenden for debugging purpose. It is not
+ recommended to print very large terms with <c>%T</c>. The function may
+ change <c>errno</c>, even if successful.</p>
</desc>
</func>
@@ -3194,6 +3258,16 @@ if (retval &amp; ERL_NIF_SELECT_STOP_CALLED) {
</func>
<func>
+ <name><ret>char*</ret>
+ <nametext>enif_thread_name(ErlNifTid tid)</nametext></name>
+ <fsummary>Thread name</fsummary>
+ <desc>
+ <p>Same as <seealso marker="erl_driver#erl_drv_thread_name">
+ <c>erl_drv_thread_name</c></seealso>.</p>
+ </desc>
+ </func>
+
+ <func>
<name><ret>ErlNifThreadOpts *</ret>
<nametext>enif_thread_opts_create(char *name)</nametext></name>
<fsummary></fsummary>
@@ -3311,6 +3385,30 @@ if (retval &amp; ERL_NIF_SELECT_STOP_CALLED) {
<func>
<name><ret>int</ret>
+ <nametext>enif_vfprintf(FILE *stream, const char *format, va_list ap)
+ </nametext></name>
+ <fsummary>Format strings and Erlang terms.</fsummary>
+ <desc>
+ <p>Equivalent to <seealso marker="#enif_fprintf"><c>enif_fprintf</c></seealso>
+ except that its called with a <c>va_list</c> instead of a variable number of
+ arguments.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>int</ret>
+ <nametext>enif_vsnprintf(char *str, size_t size, const char *format, va_list ap)
+ </nametext></name>
+ <fsummary>Format strings and Erlang terms.</fsummary>
+ <desc>
+ <p>Equivalent to <seealso marker="#enif_snprintf"><c>enif_snprintf</c></seealso>
+ except that its called with a <c>va_list</c> instead of a variable number of
+ arguments.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>int</ret>
<nametext>enif_whereis_pid(ErlNifEnv *env,
ERL_NIF_TERM name, ErlNifPid *pid)</nametext></name>
<fsummary>Looks up a process by its registered name.</fsummary>
diff --git a/erts/doc/src/erlang.xml b/erts/doc/src/erlang.xml
index 24e0773e41..c086928bb3 100644
--- a/erts/doc/src/erlang.xml
+++ b/erts/doc/src/erlang.xml
@@ -5040,7 +5040,6 @@ RealSystem = system + MissedSystem</code>
<item><c>initial_call</c></item>
<item><c>status</c></item>
<item><c>message_queue_len</c></item>
- <item><c>messages</c></item>
<item><c>links</c></item>
<item><c>dictionary</c></item>
<item><c>trap_exit</c></item>
@@ -5700,11 +5699,17 @@ true</pre>
<type name="dst"/>
<desc>
<p>Sends a message and returns <c><anno>Msg</anno></c>. This
- is the same as <c><anno>Dest</anno> ! <anno>Msg</anno></c>.</p>
+ is the same as using the <seealso marker="doc/reference_manual:expressions#send">
+ send operator</seealso>:
+ <c><anno>Dest</anno> ! <anno>Msg</anno></c>.</p>
<p><c><anno>Dest</anno></c> can be a remote or local process identifier,
a (local) port, a locally registered name, or a tuple
<c>{<anno>RegName</anno>, <anno>Node</anno>}</c>
- for a registered name at another node.</p>
+ for a registered name at another node.</p>
+ <p>The function fails with a <c>badarg</c> run-time error if
+ <c><anno>Dest</anno></c> is an atom name, but this name is not
+ registered. This is the only case when <c>send</c> fails for an
+ unreachable destination <c><anno>Dest</anno></c> (of correct type).</p>
</desc>
</func>
@@ -6753,17 +6758,20 @@ lists:map(
be included in the result. That is, all scheduler threads
that are expected to handle CPU bound work. If you also
want information about dirty I/O schedulers, use
- <seealso marker="#statistics_scheduler_wall_time_all"><c>statistics(scheduler_wall_time_all)</c></seealso>
+ <seealso marker="#statistics_scheduler_wall_time_all">
+ <c>statistics(scheduler_wall_time_all)</c></seealso>
instead.</p>
<p>Normal schedulers will have scheduler identifiers in
the range <c>1 =&lt; <anno>SchedulerId</anno> =&lt;
- </c><seealso marker="#system_info_schedulers"><c>erlang:system_info(schedulers)</c></seealso>.
+ </c><seealso marker="#system_info_schedulers">
+ <c>erlang:system_info(schedulers)</c></seealso>.
Dirty CPU schedulers will have scheduler identifiers in
the range <c>erlang:system_info(schedulers) &lt;
<anno>SchedulerId</anno> =&lt; erlang:system_info(schedulers)
+
- </c><seealso marker="#system_info_dirty_cpu_schedulers"><c>erlang:system_info(dirty_cpu_schedulers)</c></seealso>.
+ </c><seealso marker="#system_info_dirty_cpu_schedulers">
+ <c>erlang:system_info(dirty_cpu_schedulers)</c></seealso>.
</p>
<note><p>The different types of schedulers handle
specific types of jobs. Every job is assigned to a specific
@@ -6839,13 +6847,16 @@ ok
schedulers.</p>
<p>Dirty IO schedulers will have scheduler identifiers in
the range
- <seealso marker="#system_info_schedulers"><c>erlang:system_info(schedulers)</c></seealso><c>
+ <seealso marker="#system_info_schedulers">
+ <c>erlang:system_info(schedulers)</c></seealso><c>
+
- </c><seealso marker="#system_info_dirty_cpu_schedulers"><c>erlang:system_info(dirty_cpu_schedulers)</c></seealso><c> &lt;
+ </c><seealso marker="#system_info_dirty_cpu_schedulers">
+ <c>erlang:system_info(dirty_cpu_schedulers)</c></seealso><c> &lt;
<anno>SchedulerId</anno> =&lt; erlang:system_info(schedulers)
+ erlang:system_info(dirty_cpu_schedulers)
+
- </c><seealso marker="#system_info_dirty_io_schedulers"><c>erlang:system_info(dirty_io_schedulers)</c></seealso>.</p>
+ </c><seealso marker="#system_info_dirty_io_schedulers">
+ <c>erlang:system_info(dirty_io_schedulers)</c></seealso>.</p>
<note><p>Note that work executing on dirty I/O schedulers
are expected to mainly wait for I/O. That is, when you
get high scheduler utilization on dirty I/O schedulers,
@@ -7116,6 +7127,23 @@ ok
<func>
<name name="system_flag" arity="2" clause_i="4"/>
+ <fsummary>Set system flag for erts_alloc.</fsummary>
+ <desc>
+ <p>Sets system flags for
+ <seealso marker="erts:erts_alloc"><c>erts_alloc(3)</c></seealso>.
+ <c><anno>Alloc</anno></c> is the allocator to affect, for example
+ <c>binary_alloc</c>. <c><anno>F</anno></c> is the flag to change and
+ <c><anno>V</anno></c> is the new value.</p>
+ <p>Only a subset of all <c>erts_alloc</c> flags can be changed
+ at run time. This subset is currently only the flag
+ <seealso marker="erts:erts_alloc#M_sbct"><c>sbct</c></seealso>.</p>
+ <p>Returns <c>ok</c> if the flag was set or <c>notsup</c> if not
+ supported by <c>erts_alloc</c>.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name name="system_flag" arity="2" clause_i="5"/>
<fsummary>Set system flag fullsweep_after.</fsummary>
<desc>
<p>Sets system flag <c>fullsweep_after</c>.
@@ -7134,7 +7162,7 @@ ok
</func>
<func>
- <name name="system_flag" arity="2" clause_i="5"
+ <name name="system_flag" arity="2" clause_i="6"
anchor="system_flag_microstate_accounting"/>
<fsummary>Set system flag microstate_accounting.</fsummary>
<desc>
@@ -7148,7 +7176,7 @@ ok
</func>
<func>
- <name name="system_flag" arity="2" clause_i="6"/>
+ <name name="system_flag" arity="2" clause_i="7"/>
<fsummary>Set system flag min_heap_size.</fsummary>
<desc>
<p>Sets the default minimum heap size for processes. The size
@@ -7163,7 +7191,7 @@ ok
</func>
<func>
- <name name="system_flag" arity="2" clause_i="7"/>
+ <name name="system_flag" arity="2" clause_i="8"/>
<fsummary>Set system flag min_bin_vheap_size.</fsummary>
<desc>
<p>Sets the default minimum binary virtual heap size for
@@ -7180,7 +7208,7 @@ ok
</func>
<func>
- <name name="system_flag" arity="2" clause_i="8"
+ <name name="system_flag" arity="2" clause_i="9"
anchor="system_flag_max_heap_size"/>
<fsummary>Set system flag max_heap_size.</fsummary>
<type name="max_heap_size"/>
@@ -7198,7 +7226,7 @@ ok
</func>
<func>
- <name name="system_flag" arity="2" clause_i="9"
+ <name name="system_flag" arity="2" clause_i="10"
anchor="system_flag_multi_scheduling"/>
<fsummary>Set system flag multi_scheduling.</fsummary>
<desc>
@@ -7254,7 +7282,7 @@ ok
</func>
<func>
- <name name="system_flag" arity="2" clause_i="10"
+ <name name="system_flag" arity="2" clause_i="11"
anchor="system_flag_scheduler_bind_type"/>
<fsummary>Set system flag scheduler_bind_type.</fsummary>
<type name="scheduler_bind_type"/>
@@ -7381,7 +7409,7 @@ ok
</func>
<func>
- <name name="system_flag" arity="2" clause_i="11"
+ <name name="system_flag" arity="2" clause_i="12"
anchor="system_flag_scheduler_wall_time"/>
<fsummary>Set system flag scheduler_wall_time.</fsummary>
<desc>
@@ -7394,7 +7422,7 @@ ok
</func>
<func>
- <name name="system_flag" arity="2" clause_i="12"
+ <name name="system_flag" arity="2" clause_i="13"
anchor="system_flag_schedulers_online"/>
<fsummary>Set system flag schedulers_online.</fsummary>
<desc>
@@ -7423,7 +7451,7 @@ ok
</func>
<func>
- <name name="system_flag" arity="2" clause_i="13"/>
+ <name name="system_flag" arity="2" clause_i="14"/>
<fsummary>Set system flag trace_control_word.</fsummary>
<desc>
<p>Sets the value of the node trace control word to
@@ -7437,7 +7465,7 @@ ok
</func>
<func>
- <name name="system_flag" arity="2" clause_i="14"
+ <name name="system_flag" arity="2" clause_i="15"
anchor="system_flag_time_offset"/>
<fsummary>Finalize the time offset.</fsummary>
<desc>
@@ -7467,11 +7495,144 @@ ok
</func>
<func>
- <name name="system_info" arity="1" clause_i="1"/>
- <name name="system_info" arity="1" clause_i="2"/>
- <name name="system_info" arity="1" clause_i="3"/>
- <name name="system_info" arity="1" clause_i="4"/>
- <name name="system_info" arity="1" clause_i="5"/>
+ <name name="system_info" arity="1" clause_i="75"/>
+ <fsummary>System info overview.</fsummary>
+ <desc>
+ <p>Returns information about the current system.
+ The documentation of this function is broken into the following
+ sections in order to make it easier to navigate.</p>
+ <taglist>
+ <tag><seealso marker="#system_info_allocator">
+ <c>Memory Allocation</c></seealso></tag>
+ <item>
+ <p>
+ <seealso marker="#system_info_allocated_areas"><c>allocated_areas</c></seealso>,
+ <seealso marker="#system_info_allocator"><c>allocator</c></seealso>,
+ <seealso marker="#system_info_alloc_util_allocators"><c>alloc_util_allocators</c></seealso>,
+ <seealso marker="#system_info_allocator_sizes"><c>allocator_sizes</c></seealso>,
+ <seealso marker="#system_info_elib_malloc"><c>elib_malloc</c></seealso>
+ </p>
+ </item>
+ <tag><seealso marker="#system_info_cpu_topology">
+ <c>CPU Topology</c></seealso></tag>
+ <item>
+ <p>
+ <seealso marker="#system_info_cpu_topology"><c>cpu_topology</c></seealso>,
+ <seealso marker="#system_info_logical_processors"><c>logical_processors</c></seealso>,
+ <seealso marker="#system_info_update_cpu_info"><c>update_cpu_info</c></seealso>
+ </p>
+ </item>
+ <tag><seealso marker="#system_info_process">
+ <c>Process Information</c></seealso></tag>
+ <item>
+ <p>
+ <seealso marker="#system_info_fullsweep_after"><c>fullsweep_after</c></seealso>,
+ <seealso marker="#system_info_garbage_collection"><c>garbage_collection</c></seealso>,
+ <seealso marker="#system_info_heap_sizes"><c>heap_sizes</c></seealso>,
+ <seealso marker="#system_info_heap_type"><c>heap_type</c></seealso>,
+ <seealso marker="#system_info_max_heap_size"><c>max_heap_size</c></seealso>,
+ <seealso marker="#system_info_message_queue_data"><c>message_queue_data</c></seealso>,
+ <seealso marker="#system_info_min_heap_size"><c>min_heap_size</c></seealso>,
+ <seealso marker="#system_info_min_bin_vheap_size"><c>min_bin_vheap_size</c></seealso>,
+ <seealso marker="#system_info_procs"><c>procs</c></seealso>
+ </p>
+ </item>
+ <tag><seealso marker="#system_info_limits">
+ <c>System Limits</c></seealso></tag>
+ <item>
+ <p>
+ <seealso marker="#system_info_atom_count"><c>atom_count</c></seealso>,
+ <seealso marker="#system_info_atom_limit"><c>atom_limit</c></seealso>,
+ <seealso marker="#system_info_ets_limit"><c>ets_limit</c></seealso>,
+ <seealso marker="#system_info_port_count"><c>port_count</c></seealso>,
+ <seealso marker="#system_info_port_limit"><c>port_limit</c></seealso>,
+ <seealso marker="#system_info_process_count"><c>process_count</c></seealso>,
+ <seealso marker="#system_info_process_limit"><c>process_limit</c></seealso>
+ </p>
+ </item>
+ <tag><seealso marker="#system_info_time">
+ <c>System Time</c></seealso></tag>
+ <item>
+ <p>
+ <seealso marker="#system_info_end_time"><c>end_time</c></seealso>,
+ <seealso marker="#system_info_os_monotonic_time_source"><c>os_monotonic_time_source</c></seealso>,
+ <seealso marker="#system_info_os_system_time_source"><c>os_system_time_source</c></seealso>,
+ <seealso marker="#system_info_start_time"><c>start_time</c></seealso>,
+ <seealso marker="#system_info_time_correction"><c>time_correction</c></seealso>,
+ <seealso marker="#system_info_time_offset"><c>time_offset</c></seealso>,
+ <seealso marker="#system_info_time_warp_mode"><c>time_warp_mode</c></seealso>,
+ <seealso marker="#system_info_tolerant_timeofday"><c>tolerant_timeofday</c></seealso>
+ </p>
+ </item>
+ <tag><seealso marker="#system_info_scheduler">
+ <c>Scheduler Information</c></seealso></tag>
+ <item>
+ <p>
+ <seealso marker="#system_info_dirty_cpu_schedulers"><c>dirty_cpu_schedulers</c></seealso>,
+ <seealso marker="#system_info_dirty_cpu_schedulers_online"><c>dirty_cpu_schedulers_online</c></seealso>,
+ <seealso marker="#system_info_dirty_io_schedulers"><c>dirty_io_schedulers</c></seealso>,
+ <seealso marker="#system_info_multi_scheduling"><c>multi_scheduling</c></seealso>,
+ <seealso marker="#system_info_multi_scheduling_blockers"><c>multi_scheduling_blockers</c></seealso>,
+ <seealso marker="#system_info_normal_multi_scheduling_blockers"><c>normal_multi_scheduling_blockers</c></seealso>,
+ <seealso marker="#system_info_scheduler_bind_type"><c>scheduler_bind_type</c></seealso>,
+ <seealso marker="#system_info_scheduler_bindings"><c>scheduler_bindings</c></seealso>,
+ <seealso marker="#system_info_scheduler_id"><c>scheduler_id</c></seealso>,
+ <seealso marker="#system_info_schedulers"><c>schedulers</c></seealso>,
+ <seealso marker="#system_info_smp_support"><c>smp_support</c></seealso>,
+ <seealso marker="#system_info_threads"><c>threads</c></seealso>,
+ <seealso marker="#system_info_thread_pool_size"><c>thread_pool_size</c></seealso>
+ </p>
+ </item>
+ <tag><seealso marker="#system_info_dist">
+ <c>Distribution Information</c></seealso></tag>
+ <item>
+ <p>
+ <seealso marker="#system_info_creation"><c>creation</c></seealso>,
+ <seealso marker="#system_info_delayed_node_table_gc"><c>delayed_node_table_gc</c></seealso>,
+ <seealso marker="#system_info_dist"><c>dist</c></seealso>,
+ <seealso marker="#system_info_dist_buf_busy_limit"><c>dist_buf_busy_limit</c></seealso>,
+ <seealso marker="#system_info_dist_ctrl"><c>dist_ctrl</c></seealso>
+ </p>
+ </item>
+ <tag><seealso marker="#system_info_misc">
+ <c>System Information</c></seealso></tag>
+ <item>
+ <p>
+ <seealso marker="#system_info_build_type"><c>build_type</c></seealso>,
+ <seealso marker="#system_info_c_compiler_used"><c>c_compiler_used</c></seealso>,
+ <seealso marker="#system_info_check_io"><c>check_io</c></seealso>,
+ <seealso marker="#system_info_compat_rel"><c>compat_rel</c></seealso>,
+ <seealso marker="#system_info_debug_compiled"><c>debug_compiled</c></seealso>,
+ <seealso marker="#system_info_driver_version"><c>driver_version</c></seealso>,
+ <seealso marker="#system_info_dynamic_trace"><c>dynamic_trace</c></seealso>,
+ <seealso marker="#system_info_dynamic_trace_probes"><c>dynamic_trace_probes</c></seealso>,
+ <seealso marker="#system_info_info"><c>info</c></seealso>,
+ <seealso marker="#system_info_kernel_poll"><c>kernel_poll</c></seealso>,
+ <seealso marker="#system_info_loaded"><c>loaded</c></seealso>,
+ <seealso marker="#system_info_machine"><c>machine</c></seealso>,
+ <seealso marker="#system_info_modified_timing_level"><c>modified_timing_level</c></seealso>,
+ <seealso marker="#system_info_nif_version"><c>nif_version</c></seealso>,
+ <seealso marker="#system_info_otp_release"><c>otp_release</c></seealso>,
+ <seealso marker="#system_info_port_parallelism"><c>port_parallelism</c></seealso>,
+ <seealso marker="#system_info_system_version"><c>system_version</c></seealso>,
+ <seealso marker="#system_info_system_architecture"><c>system_architecture</c></seealso>,
+ <seealso marker="#system_info_trace_control_word"><c>trace_control_word</c></seealso>,
+ <seealso marker="#system_info_version"><c>version</c></seealso>,
+ <seealso marker="#system_info_wordsize"><c>wordsize</c></seealso>
+ </p>
+ </item>
+ </taglist>
+ </desc>
+ </func>
+
+ <func>
+ <name name="system_info" arity="1" clause_i="1"
+ anchor="system_info_allocator"/> <!-- allocated_areas -->
+ <name name="system_info" arity="1" clause_i="2"/> <!-- allocator -->
+ <name name="system_info" arity="1" clause_i="3"/> <!-- {allocator, _} -->
+ <name name="system_info" arity="1" clause_i="4"/> <!-- alloc_util_allocators -->
+ <name name="system_info" arity="1" clause_i="5"/> <!-- {allocator_sizes, _} -->
+ <name name="system_info" arity="1" clause_i="27"/> <!-- elib_malloc -->
<fsummary>Information about the system allocators.</fsummary>
<type variable="Allocator" name_i="2"/>
<type variable="Version" name_i="2"/>
@@ -7480,12 +7641,13 @@ ok
<type variable="Alloc" name_i="3"/>
<desc>
<marker id="system_info_allocator_tags"></marker>
- <p>Returns various information about the allocators of the
- current system (emulator) as specified by
+ <p>Returns various information about the memory allocators
+ of the current system (emulator) as specified by
<c><anno>Item</anno></c>:</p>
<marker id="system_info_allocated_areas"></marker>
<taglist>
- <tag><c>allocated_areas</c></tag>
+ <tag><marker id="system_info_allocated_areas"/>
+ <c>allocated_areas</c></tag>
<item>
<p>Returns a list of tuples with information about
miscellaneous allocated memory areas.</p>
@@ -7507,9 +7669,9 @@ ok
<seealso marker="#memory/0">
<c>erlang:memory/0,1</c></seealso>.</p>
</item>
- <tag><c>allocator</c></tag>
+ <tag><marker id="system_info_allocator"/>
+ <c>allocator</c></tag>
<item>
- <marker id="system_info_allocator"></marker>
<p>Returns <c>{<anno>Allocator</anno>, <anno>Version</anno>,
<anno>Features</anno>, <anno>Settings</anno></c>, where:</p>
<list type="bulleted">
@@ -7542,19 +7704,9 @@ ok
<seealso marker="erts:erts_alloc#flags">
<c>erts_alloc(3)</c></seealso>.</p>
</item>
- <tag><c>alloc_util_allocators</c></tag>
- <item>
- <marker id="system_info_alloc_util_allocators"></marker>
- <p>Returns a list of the names of all allocators using
- the ERTS internal <c>alloc_util</c> framework
- as atoms. For more information, see section
- <seealso marker="erts:erts_alloc#alloc_util">The
- alloc_util framework</seealso>
- in <c>erts_alloc(3)</c>.</p>
- </item>
- <tag><c>{allocator, <anno>Alloc</anno>}</c></tag>
+ <tag><marker id="system_info_allocator_tuple"></marker>
+ <c>{allocator, <anno>Alloc</anno>}</c></tag>
<item>
- <marker id="system_info_allocator_tuple"></marker>
<p>Returns information about the specified allocator.
As from ERTS 5.6.1, the return value is a list
of <c>{instance, InstanceNo, InstanceInfo}</c> tuples,
@@ -7599,9 +7751,19 @@ ok
values. The first value is the memory pool size and
the second value is the used memory size.</p>
</item>
- <tag><c>{allocator_sizes, <anno>Alloc</anno>}</c></tag>
+ <tag><marker id="system_info_alloc_util_allocators"/>
+ <c>alloc_util_allocators</c></tag>
+ <item>
+ <p>Returns a list of the names of all allocators using
+ the ERTS internal <c>alloc_util</c> framework
+ as atoms. For more information, see section
+ <seealso marker="erts:erts_alloc#alloc_util">The
+ alloc_util framework</seealso>
+ in <c>erts_alloc(3)</c>.</p>
+ </item>
+ <tag><marker id="system_info_allocator_sizes"/>
+ <c>{allocator_sizes, <anno>Alloc</anno>}</c></tag>
<item>
- <marker id="system_info_allocator_sizes"></marker>
<p>Returns various size information for the specified
allocator. The information returned is a subset of the
information returned by
@@ -7609,14 +7771,23 @@ ok
<c>erlang:system_info({allocator,
<anno>Alloc</anno>})</c></seealso>.</p>
</item>
+ <tag><marker id="system_info_elib_malloc"/>
+ <c>elib_malloc</c></tag>
+ <item>
+ <p>This option will be removed in a future release.
+ The return value will always be <c>false</c>, as the
+ <c>elib_malloc</c> allocator has been removed.</p>
+ </item>
</taglist>
</desc>
</func>
<func>
<name name="system_info" arity="1" clause_i="12"
- anchor="system_info_cpu_topology"/>
- <name name="system_info" arity="1" clause_i="13"/>
+ anchor="system_info_cpu_topology"/> <!-- cpu_topology -->
+ <name name="system_info" arity="1" clause_i="13"/> <!-- {cpu_topology, _} -->
+ <name name="system_info" arity="1" clause_i="37"/> <!-- logical_processors -->
+ <name name="system_info" arity="1" clause_i="72"/> <!-- update_cpu_info -->
<fsummary>Information about the CPU topology of the system.</fsummary>
<type name="cpu_topology"/>
<type name="level_entry"/>
@@ -7647,7 +7818,8 @@ ok
the current system (emulator) as specified by
<c><anno>Item</anno></c>:</p>
<taglist>
- <tag><c>cpu_topology</c></tag>
+ <tag><marker id="system_info_cpu_topology"/>
+ <c>cpu_topology</c></tag>
<item>
<p>Returns the <c><anno>CpuTopology</anno></c> currently used by
the emulator. The CPU topology is used when binding schedulers
@@ -7710,31 +7882,89 @@ ok
<seealso marker="#system_info_cpu_topology">
<c>cpu_topology</c></seealso>.</p>
</item>
+ <tag><marker id="system_info_logical_processors"/>
+ <c>logical_processors</c></tag>
+ <item>
+ <p>Returns the detected number of logical processors configured
+ in the system. The return value is either an integer, or
+ the atom <c>unknown</c> if the emulator cannot
+ detect the configured logical processors.</p>
+ </item>
+ <tag><marker id="system_info_logical_processors_available"/>
+ <c>logical_processors_available</c></tag>
+ <item>
+ <p>Returns the detected number of logical processors available
+ to the Erlang runtime system. The return value is either an
+ integer, or the atom <c>unknown</c> if the emulator
+ cannot detect the available logical processors. The number
+ of available logical processors is less than or equal to
+ the number of <seealso marker="#system_info_logical_processors_online">
+ logical processors online</seealso>.</p>
+ </item>
+ <tag><marker id="system_info_logical_processors_online"/>
+ <c>logical_processors_online</c></tag>
+ <item>
+ <p>Returns the detected number of logical processors online on
+ the system. The return value is either an integer,
+ or the atom <c>unknown</c> if the emulator cannot
+ detect logical processors online. The number of logical
+ processors online is less than or equal to the number of
+ <seealso marker="#system_info_logical_processors">logical processors
+ configured</seealso>.</p>
+ </item>
+ <tag><marker id="system_info_update_cpu_info"/>
+ <c>update_cpu_info</c></tag>
+ <item>
+ <p>The runtime system rereads the CPU information available
+ and updates its internally stored information about the
+ <seealso marker="#system_info_cpu_topology_detected">detected
+ CPU topology</seealso> and the number of logical processors
+ <seealso marker="#system_info_logical_processors">configured</seealso>,
+ <seealso marker="#system_info_logical_processors_online">online</seealso>,
+ and <seealso marker="#system_info_logical_processors_available">
+ available</seealso>.</p>
+ <p>If the CPU information has changed since the last time
+ it was read, the atom <c>changed</c> is returned, otherwise
+ the atom <c>unchanged</c>. If the CPU information has changed,
+ you probably want to
+ <seealso marker="#system_flag_schedulers_online">adjust the
+ number of schedulers online</seealso>. You typically want
+ to have as many schedulers online as
+ <seealso marker="#system_info_logical_processors_available">logical
+ processors available</seealso>.</p>
+ </item>
</taglist>
</desc>
</func>
<func>
- <name name="system_info" arity="1" clause_i="29"/>
- <name name="system_info" arity="1" clause_i="30"/>
- <name name="system_info" arity="1" clause_i="38"/>
- <name name="system_info" arity="1" clause_i="39"/>
- <name name="system_info" arity="1" clause_i="40"/>
- <name name="system_info" arity="1" clause_i="41"/>
+ <name name="system_info" arity="1" clause_i="30"
+ anchor="system_info_process"/> <!-- fullsweep_after -->
+ <name name="system_info" arity="1" clause_i="31"/> <!-- garbage_collection -->
+ <name name="system_info" arity="1" clause_i="32"/> <!-- heap_sizes -->
+ <name name="system_info" arity="1" clause_i="33"/> <!-- heap_type -->
+ <name name="system_info" arity="1" clause_i="39"/> <!-- max_heap_size -->
+ <name name="system_info" arity="1" clause_i="40"/> <!-- message_queue_data -->
+ <name name="system_info" arity="1" clause_i="41"/> <!-- min_heap_size -->
+ <name name="system_info" arity="1" clause_i="42"/> <!-- min_bin_vheap_size -->
+ <name name="system_info" arity="1" clause_i="56"/> <!-- procs -->
<fsummary>Information about the default process heap settings.</fsummary>
<type name="message_queue_data"/>
<type name="max_heap_size"/>
<desc>
+ <marker id="system_info_process_tags"/>
<p>Returns information about the default process heap settings:</p>
<taglist>
- <tag><c>fullsweep_after</c></tag>
+ <tag><marker id="system_info_fullsweep_after"/>
+ <c>fullsweep_after</c></tag>
<item>
<p>Returns <c>{fullsweep_after, integer() >= 0}</c>, which is
the <c>fullsweep_after</c> garbage collection setting used
by default. For more information, see
<c>garbage_collection</c> described below.</p>
</item>
- <tag><c>garbage_collection</c></tag>
+ <tag><marker id="system_info_garbage_collection"/>
+ <c>garbage_collection</c></tag>
<item>
<p>Returns a list describing the default garbage collection
settings. A process spawned on the local node by a
@@ -7747,7 +7977,30 @@ ok
can spawn a process that does not use the default
settings.</p>
</item>
- <tag><c>max_heap_size</c></tag>
+ <tag><marker id="system_info_heap_sizes"/>
+ <c>heap_sizes</c></tag>
+ <item>
+ <p>Returns a list of integers representing valid heap sizes
+ in words. All Erlang heaps are sized from sizes in this
+ list.</p>
+ </item>
+ <tag><marker id="system_info_heap_type"/>
+ <c>heap_type</c></tag>
+ <item>
+ <p>Returns the heap type used by the current emulator. One
+ heap type exists:</p>
+ <taglist>
+ <tag><c>private</c></tag>
+ <item>
+ Each process has a heap reserved for its use and no
+ references between heaps of different processes are
+ allowed. Messages passed between processes are copied
+ between heaps.
+ </item>
+ </taglist>
+ </item>
+ <tag><marker id="system_info_max_heap_size"/>
+ <c>max_heap_size</c></tag>
<item>
<p>Returns <c>{max_heap_size, <anno>MaxHeapSize</anno>}</c>,
where <c><anno>MaxHeapSize</anno></c> is the current
@@ -7775,173 +8028,364 @@ ok
<seealso marker="#process_flag_message_queue_data">
<c>process_flag(message_queue_data, MQD)</c></seealso>.</p>
</item>
- <tag><c>min_heap_size</c></tag>
+ <tag><marker id="system_info_min_heap_size"/>
+ <c>min_heap_size</c></tag>
<item>
<p>Returns <c>{min_heap_size, <anno>MinHeapSize</anno>}</c>,
where <c><anno>MinHeapSize</anno></c> is the current
system-wide minimum heap size for spawned processes.</p>
</item>
- <tag><c>min_bin_vheap_size</c></tag>
+ <tag><marker id="system_info_min_bin_vheap_size"/>
+ <c>min_bin_vheap_size</c></tag>
<item>
<p>Returns <c>{min_bin_vheap_size,
<anno>MinBinVHeapSize</anno>}</c>, where
<c><anno>MinBinVHeapSize</anno></c> is the current system-wide
minimum binary virtual heap size for spawned processes.</p>
</item>
+ <tag><marker id="system_info_procs"/>
+ <c>procs</c></tag>
+ <item>
+ <p>Returns a binary containing a string of process and port
+ information formatted as in Erlang crash dumps. For more
+ information, see section <seealso marker="erts:crash_dump">
+ How to interpret the Erlang crash dumps</seealso>
+ in the User's Guide.</p>
+ </item>
</taglist>
</desc>
</func>
<func>
- <name name="system_info" arity="1" clause_i="6"/>
- <name name="system_info" arity="1" clause_i="7"/>
- <name name="system_info" arity="1" clause_i="8"/>
- <name name="system_info" arity="1" clause_i="9"/>
- <name name="system_info" arity="1" clause_i="10"/>
- <name name="system_info" arity="1" clause_i="11"/>
- <name name="system_info" arity="1" clause_i="14"/>
- <name name="system_info" arity="1" clause_i="15"/>
- <name name="system_info" arity="1" clause_i="16"/>
- <name name="system_info" arity="1" clause_i="17"/>
- <name name="system_info" arity="1" clause_i="18"/>
- <name name="system_info" arity="1" clause_i="19"/>
- <name name="system_info" arity="1" clause_i="20"/>
- <name name="system_info" arity="1" clause_i="21"/>
- <name name="system_info" arity="1" clause_i="22"/>
- <name name="system_info" arity="1" clause_i="23"/>
- <name name="system_info" arity="1" clause_i="24"/>
- <name name="system_info" arity="1" clause_i="25"/>
- <name name="system_info" arity="1" clause_i="26"/>
- <name name="system_info" arity="1" clause_i="27"/>
- <name name="system_info" arity="1" clause_i="28"/>
- <name name="system_info" arity="1" clause_i="31"/>
- <name name="system_info" arity="1" clause_i="32"/>
- <name name="system_info" arity="1" clause_i="33"/>
- <name name="system_info" arity="1" clause_i="34"/>
- <name name="system_info" arity="1" clause_i="35"/>
- <name name="system_info" arity="1" clause_i="36"/>
- <name name="system_info" arity="1" clause_i="37"/>
- <name name="system_info" arity="1" clause_i="42"/>
- <name name="system_info" arity="1" clause_i="43"/>
- <name name="system_info" arity="1" clause_i="44"/>
- <name name="system_info" arity="1" clause_i="45"/>
- <name name="system_info" arity="1" clause_i="46"/>
- <name name="system_info" arity="1" clause_i="47"/>
- <name name="system_info" arity="1" clause_i="48"/>
- <name name="system_info" arity="1" clause_i="49"/>
- <name name="system_info" arity="1" clause_i="50"/>
- <name name="system_info" arity="1" clause_i="51"/>
- <name name="system_info" arity="1" clause_i="52"/>
- <name name="system_info" arity="1" clause_i="53"/>
- <name name="system_info" arity="1" clause_i="54"/>
- <name name="system_info" arity="1" clause_i="55"/>
- <name name="system_info" arity="1" clause_i="56"/>
- <name name="system_info" arity="1" clause_i="57"/>
- <name name="system_info" arity="1" clause_i="58"/>
- <name name="system_info" arity="1" clause_i="59"/>
- <name name="system_info" arity="1" clause_i="60"/>
- <name name="system_info" arity="1" clause_i="61"/>
- <name name="system_info" arity="1" clause_i="62"/>
- <name name="system_info" arity="1" clause_i="63"/>
- <name name="system_info" arity="1" clause_i="64"/>
- <name name="system_info" arity="1" clause_i="65"/>
- <name name="system_info" arity="1" clause_i="66"/>
- <name name="system_info" arity="1" clause_i="67"/>
- <name name="system_info" arity="1" clause_i="68"/>
- <name name="system_info" arity="1" clause_i="69"/>
- <name name="system_info" arity="1" clause_i="70"/>
- <name name="system_info" arity="1" clause_i="71"/>
- <fsummary>Information about the system.</fsummary>
+ <name name="system_info" arity="1" clause_i="6"
+ anchor="system_info_limits"/> <!-- atom_count -->
+ <name name="system_info" arity="1" clause_i="7"/> <!-- atom_limit -->
+ <name name="system_info" arity="1" clause_i="29"/> <!-- ets_limit -->
+ <name name="system_info" arity="1" clause_i="52"/> <!-- port_count -->
+ <name name="system_info" arity="1" clause_i="53"/> <!-- port_limit -->
+ <name name="system_info" arity="1" clause_i="54"/> <!-- process_count -->
+ <name name="system_info" arity="1" clause_i="55"/> <!-- process_limit -->
+ <fsummary>Information about various system limits.</fsummary>
<desc>
- <p>Returns various information about the current system
- (emulator) as specified by <c><anno>Item</anno></c>:</p>
+ <marker id="system_info_limits"/>
+ <p>Returns information about the current system
+ (emulator) limits as specified by <c><anno>Item</anno></c>:</p>
<taglist>
- <tag><c>atom_count</c></tag>
+ <tag><marker id="system_info_atom_count"/>
+ <c>atom_count</c></tag>
<item>
- <marker id="system_info_atom_count"></marker>
<p>Returns the number of atoms currently existing at the
- local node. The value is given as an integer.</p>
+ local node. The value is given as an integer.</p>
</item>
- <tag><c>atom_limit</c></tag>
+ <tag><marker id="system_info_atom_limit"/>
+ <c>atom_limit</c></tag>
<item>
- <marker id="system_info_atom_limit"></marker>
<p>Returns the maximum number of atoms allowed.
- This limit can be increased at startup by passing
- command-line flag
- <seealso marker="erts:erl#+t"><c>+t</c></seealso> to
- <c>erl(1)</c>.
+ This limit can be increased at startup by passing
+ command-line flag
+ <seealso marker="erts:erl#+t"><c>+t</c></seealso> to
+ <c>erl(1)</c>.
</p>
</item>
- <tag><c>build_type</c></tag>
+ <tag><marker id="system_info_ets_limit"/>
+ <c>ets_limit</c></tag>
<item>
- <p>Returns an atom describing the build type of the runtime
- system. This is normally the atom <c>opt</c> for optimized.
- Other possible return values are <c>debug</c>, <c>purify</c>,
- <c>quantify</c>, <c>purecov</c>, <c>gcov</c>, <c>valgrind</c>,
- <c>gprof</c>, and <c>lcnt</c>. Possible return values
- can be added or removed at any time without prior notice.</p>
+ <p>Returns the maximum number of ETS tables allowed. This
+ limit can be increased at startup by passing
+ command-line flag
+ <seealso marker="erts:erl#+e"><c>+e</c></seealso> to
+ <c>erl(1)</c> or by setting environment variable
+ <c>ERL_MAX_ETS_TABLES</c> before starting the Erlang
+ runtime system.</p>
</item>
- <tag><c>c_compiler_used</c></tag>
+ <tag><marker id="system_info_port_count"/><c>port_count</c></tag>
<item>
- <p>Returns a two-tuple describing the C compiler used when
- compiling the runtime system. The first element is an
- atom describing the name of the compiler, or <c>undefined</c>
- if unknown. The second element is a term describing the
- version of the compiler, or <c>undefined</c> if unknown.</p>
+ <p>Returns the number of ports currently existing at the
+ local node. The value is given as an integer. This is
+ the same value as returned by
+ <c>length(erlang:ports())</c>, but more efficient.</p>
</item>
- <tag><c>check_io</c></tag>
+ <tag><marker id="system_info_port_limit"/>
+ <c>port_limit</c></tag>
<item>
- <p>Returns a list containing miscellaneous information
- about the emulators internal I/O checking. Notice that
- the content of the returned list can vary between
- platforms and over time. It is only guaranteed
- that a list is returned.</p>
+ <p>Returns the maximum number of simultaneously existing
+ ports at the local node as an integer. This limit can be
+ configured at startup by using command-line flag
+ <seealso marker="erl#+Q"><c>+Q</c></seealso> in <c>erl(1)</c>.</p>
</item>
- <tag><c>compat_rel</c></tag>
+ <tag><marker id="system_info_process_count"/>
+ <c>process_count</c></tag>
<item>
- <p>Returns the compatibility mode of the local node as
- an integer. The integer returned represents the
- Erlang/OTP release that the current emulator has been
- set to be backward compatible with. The compatibility
- mode can be configured at startup by using command-line flag
- <seealso marker="erts:erl#compat_rel"><c>+R</c></seealso> in
- <c>erl(1)</c>.</p>
+ <p>Returns the number of processes currently existing at the
+ local node. The value is given as an integer. This is
+ the same value as returned by
+ <c>length(processes())</c>, but more efficient.</p>
</item>
- <tag><c>cpu_topology</c></tag>
+ <tag><marker id="system_info_process_limit"/>
+ <c>process_limit</c></tag>
<item>
- <p>See <seealso
- marker="#system_info_cpu_topology_tags">above</seealso>.</p>
+ <p>Returns the maximum number of simultaneously existing
+ processes at the local node. The value is given as an
+ integer. This limit can be configured at startup by using
+ command-line flag <seealso marker="erl#+P"><c>+P</c></seealso>
+ in <c>erl(1)</c>.</p>
</item>
- <tag><c>creation</c></tag>
+ </taglist>
+ </desc>
+ </func>
+
+ <func>
+ <name name="system_info" arity="1" clause_i="26"
+ anchor="system_info_time"/> <!-- end_time -->
+ <name name="system_info" arity="1" clause_i="49"/> <!-- os_monotonic_time_source -->
+ <name name="system_info" arity="1" clause_i="50"/> <!-- os_system_time_source -->
+ <name name="system_info" arity="1" clause_i="62"/> <!-- start_time -->
+ <name name="system_info" arity="1" clause_i="67"/> <!-- time_correction -->
+ <name name="system_info" arity="1" clause_i="68"/> <!-- time_offset -->
+ <name name="system_info" arity="1" clause_i="69"/> <!-- time_warp_mode -->
+ <name name="system_info" arity="1" clause_i="70"/> <!-- tolerant_timeofday -->
+ <fsummary>Information about system time.</fsummary>
+ <desc>
+ <marker id="system_info_time_tags"/>
+ <p>Returns information about the current system
+ (emulator) time as specified by <c><anno>Item</anno></c>:</p>
+ <taglist>
+ <tag><marker id="system_info_end_time"/><c>end_time</c></tag>
<item>
- <p>Returns the creation of the local node as an integer.
- The creation is changed when a node is restarted. The
- creation of a node is stored in process identifiers, port
- identifiers, and references. This makes it (to some
- extent) possible to distinguish between identifiers from
- different incarnations of a node. The valid
- creations are integers in the range 1..3, but this will
- probably change in a future release. If the node is not
- alive, <c>0</c> is returned.</p>
+ <p>The last <seealso marker="#monotonic_time/0">Erlang monotonic
+ time</seealso> in <c>native</c>
+ <seealso marker="#type_time_unit">time unit</seealso> that
+ can be represented internally in the current Erlang runtime system
+ instance. The time between the
+ <seealso marker="#system_info_start_time">start time</seealso> and
+ the end time is at least a quarter of a millennium.</p>
</item>
- <tag><c>debug_compiled</c></tag>
+ <tag><marker id="system_info_os_monotonic_time_source"/>
+ <c>os_monotonic_time_source</c></tag>
<item>
- <p>Returns <c>true</c> if the emulator has been
- debug-compiled, otherwise <c>false</c>.</p>
+ <p>Returns a list containing information about the source of
+ <seealso marker="erts:time_correction#OS_Monotonic_Time">OS
+ monotonic time</seealso> that is used by the runtime system.</p>
+ <p>If <c>[]</c> is returned, no OS monotonic time is
+ available. The list contains two-tuples with <c>Key</c>s
+ as first element, and <c>Value</c>s as second element. The
+ order of these tuples is undefined. The following
+ tuples can be part of the list, but more tuples can be
+ introduced in the future:</p>
+ <taglist>
+ <tag><c>{function, Function}</c></tag>
+ <item><p><c>Function</c> is the name of the function
+ used. This tuple always exists if OS monotonic time is
+ available to the runtime system.</p>
+ </item>
+ <tag><c>{clock_id, ClockId}</c></tag>
+ <item><p>This tuple only exists if <c>Function</c>
+ can be used with different clocks. <c>ClockId</c>
+ corresponds to the clock identifier used when calling
+ <c>Function</c>.</p>
+ </item>
+ <tag><c>{resolution, OsMonotonicTimeResolution}</c></tag>
+ <item><p>Highest possible
+ <seealso marker="time_correction#Time_Resolution">
+ resolution</seealso>
+ of current OS monotonic time source as parts per
+ second. If no resolution information can be retrieved
+ from the OS, <c>OsMonotonicTimeResolution</c> is
+ set to the resolution of the time unit of
+ <c>Function</c>s return value. That is, the actual
+ resolution can be lower than
+ <c>OsMonotonicTimeResolution</c>. Notice that
+ the resolution does not say anything about the
+ <seealso marker="time_correction#Time_Accuracy">
+ accuracy</seealso> or whether the
+ <seealso marker="time_correction#Time_Precision">
+ precision</seealso> aligns with the resolution. You do,
+ however, know that the precision is not better than
+ <c>OsMonotonicTimeResolution</c>.</p>
+ </item>
+ <tag><c>{extended, Extended}</c></tag>
+ <item><p><c>Extended</c> equals <c>yes</c> if
+ the range of time values has been extended;
+ otherwise <c>Extended</c> equals <c>no</c>. The
+ range must be extended if <c>Function</c>
+ returns values that wrap fast. This typically
+ is the case when the return value is a 32-bit value.</p>
+ </item>
+ <tag><c>{parallel, Parallel}</c></tag>
+ <item><p><c>Parallel</c> equals <c>yes</c> if
+ <c>Function</c> is called in parallel from multiple
+ threads. If it is not called in parallel, because
+ calls must be serialized, <c>Parallel</c> equals
+ <c>no</c>.</p>
+ </item>
+ <tag><c>{time, OsMonotonicTime}</c></tag>
+ <item><p><c>OsMonotonicTime</c> equals current OS
+ monotonic time in <c>native</c>
+ <seealso marker="#type_time_unit">time unit</seealso>.</p>
+ </item>
+ </taglist>
</item>
- <tag><c>delayed_node_table_gc</c></tag>
+ <tag><marker id="system_info_os_system_time_source"/>
+ <c>os_system_time_source</c></tag>
<item>
- <marker id="system_info_delayed_node_table_gc"></marker>
- <p>Returns the amount of time in seconds garbage collection
- of an entry in a node table is delayed. This limit can be set
- on startup by passing command-line flag
- <seealso marker="erts:erl#+zdntgc"><c>+zdntgc</c></seealso>
- to <c>erl(1)</c>. For more information, see the documentation of
- the command-line flag.</p>
+ <p>Returns a list containing information about the source of
+ <seealso marker="erts:time_correction#OS_System_Time">OS
+ system time</seealso> that is used by the runtime system.</p>
+ <p>The list contains two-tuples with <c>Key</c>s
+ as first element, and <c>Value</c>s as second element. The
+ order if these tuples is undefined. The following
+ tuples can be part of the list, but more tuples can be
+ introduced in the future:</p>
+ <taglist>
+ <tag><c>{function, Function}</c></tag>
+ <item><p><c>Function</c> is the name of the funcion used.</p>
+ </item>
+ <tag><c>{clock_id, ClockId}</c></tag>
+ <item><p>Exists only if <c>Function</c>
+ can be used with different clocks. <c>ClockId</c>
+ corresponds to the clock identifier used when calling
+ <c>Function</c>.</p>
+ </item>
+ <tag><c>{resolution, OsSystemTimeResolution}</c></tag>
+ <item><p>Highest possible
+ <seealso marker="time_correction#Time_Resolution">
+ resolution</seealso>
+ of current OS system time source as parts per
+ second. If no resolution information can be retrieved
+ from the OS, <c>OsSystemTimeResolution</c> is
+ set to the resolution of the time unit of
+ <c>Function</c>s return value. That is, the actual
+ resolution can be lower than
+ <c>OsSystemTimeResolution</c>. Notice that
+ the resolution does not say anything about the
+ <seealso marker="time_correction#Time_Accuracy">
+ accuracy</seealso> or whether the
+ <seealso marker="time_correction#Time_Precision">
+ precision</seealso> do align with the resolution. You do,
+ however, know that the precision is not better than
+ <c>OsSystemTimeResolution</c>.</p>
+ </item>
+ <tag><c>{parallel, Parallel}</c></tag>
+ <item><p><c>Parallel</c> equals <c>yes</c> if
+ <c>Function</c> is called in parallel from multiple
+ threads. If it is not called in parallel, because
+ calls needs to be serialized, <c>Parallel</c> equals
+ <c>no</c>.</p>
+ </item>
+ <tag><c>{time, OsSystemTime}</c></tag>
+ <item><p><c>OsSystemTime</c> equals current OS
+ system time in <c>native</c>
+ <seealso marker="#type_time_unit">time unit</seealso>.</p>
+ </item>
+ </taglist>
+ </item>
+ <tag><marker id="system_info_start_time"/><c>start_time</c></tag>
+ <item>
+ <p>The <seealso marker="#monotonic_time/0">Erlang monotonic
+ time</seealso> in <c>native</c>
+ <seealso marker="#type_time_unit">time unit</seealso> at the
+ time when current Erlang runtime system instance started.</p>
+ <p>See also <seealso marker="#system_info_end_time">
+ <c>erlang:system_info(end_time)</c></seealso>.</p>
</item>
- <tag><c>dirty_cpu_schedulers</c></tag>
+ <tag><marker id="system_info_time_correction"/>
+ <c>time_correction</c></tag>
+ <item>
+ <p>Returns a boolean value indicating whether
+ <seealso marker="time_correction#Time_Correction">
+ time correction</seealso> is enabled or not.</p>
+ </item>
+ <tag><marker id="system_info_time_offset"/>
+ <c>time_offset</c></tag>
+ <item>
+ <p>Returns the state of the time offset:</p>
+ <taglist>
+ <tag><c>preliminary</c></tag>
+ <item>
+ <p>The time offset is preliminary, and will be changed
+ and finalized later. The preliminary time offset
+ is used during the preliminary phase of the
+ <seealso marker="time_correction#Single_Time_Warp_Mode">
+ single time warp mode</seealso>.</p>
+ </item>
+ <tag><c>final</c></tag>
+ <item>
+ <p>The time offset is final. This either because
+ <seealso marker="time_correction#No_Time_Warp_Mode">
+ no time warp mode</seealso> is used, or because the time
+ offset have been finalized when
+ <seealso marker="time_correction#Single_Time_Warp_Mode">
+ single time warp mode</seealso> is used.</p>
+ </item>
+ <tag><c>volatile</c></tag>
+ <item>
+ <p>The time offset is volatile. That is, it can
+ change at any time. This is because
+ <seealso marker="time_correction#Multi_Time_Warp_Mode">
+ multi-time warp mode</seealso> is used.</p>
+ </item>
+ </taglist>
+ </item>
+ <tag><marker id="system_info_time_warp_mode"/>
+ <c>time_warp_mode</c></tag>
+ <item>
+ <p>Returns a value identifying the
+ <seealso marker="time_correction#Time_Warp_Modes">
+ time warp mode</seealso> that is used:</p>
+ <taglist>
+ <tag><c>no_time_warp</c></tag>
+ <item>The <seealso marker="time_correction#No_Time_Warp_Mode">
+ no time warp mode</seealso> is used.
+ </item>
+ <tag><c>single_time_warp</c></tag>
+ <item>The <seealso marker="time_correction#Single_Time_Warp_Mode">
+ single time warp mode</seealso> is used.
+ </item>
+ <tag><c>multi_time_warp</c></tag>
+ <item>The <seealso marker="time_correction#Multi_Time_Warp_Mode">
+ multi-time warp mode</seealso> is used.
+ </item>
+ </taglist>
+ </item>
+ <tag><marker id="system_info_tolerant_timeofday"/>
+ <c>tolerant_timeofday</c></tag>
+ <item>
+ <p>Returns whether a pre ERTS 7.0 backwards compatible
+ compensation for sudden changes of system time is <c>enabled</c>
+ or <c>disabled</c>. Such compensation is <c>enabled</c> when the
+ <seealso marker="#system_info_time_offset">time offset</seealso>
+ is <c>final</c>, and
+ <seealso marker="#system_info_time_correction">
+ time correction</seealso> is enabled.</p>
+ </item>
+ </taglist>
+ </desc>
+ </func>
+
+ <func>
+ <name name="system_info" arity="1" clause_i="17"
+ anchor="system_info_scheduler"/> <!-- dirty_cpu_schedulers -->
+ <name name="system_info" arity="1" clause_i="18"/> <!-- dirty_cpu_schedulers_online -->
+ <name name="system_info" arity="1" clause_i="19"/> <!-- dirty_io_schedulers -->
+ <name name="system_info" arity="1" clause_i="44"/> <!-- multi_scheduling -->
+ <name name="system_info" arity="1" clause_i="45"/> <!-- multi_scheduling_blockers -->
+ <name name="system_info" arity="1" clause_i="47"/> <!-- normal_multi_scheduling_blockers -->
+ <name name="system_info" arity="1" clause_i="57"/> <!-- scheduler_bind_type -->
+ <name name="system_info" arity="1" clause_i="58"/> <!-- scheduler_bindings -->
+ <name name="system_info" arity="1" clause_i="59"/> <!-- scheduler_id -->
+ <name name="system_info" arity="1" clause_i="60"/> <!-- schedulers -->
+ <name name="system_info" arity="1" clause_i="61"/> <!-- smp_support -->
+ <name name="system_info" arity="1" clause_i="65"/> <!-- threads -->
+ <name name="system_info" arity="1" clause_i="66"/> <!-- thread_pool_size -->
+ <fsummary>Information about system schedulers.</fsummary>
+ <desc>
+ <marker id="system_info_scheduler_tags"/>
+ <p>Returns information about schedulers, scheduling and threads in the
+ current system as specified by <c><anno>Item</anno></c>:</p>
+ <taglist>
+ <tag><marker id="system_info_dirty_cpu_schedulers"/>
+ <c>dirty_cpu_schedulers</c></tag>
<item>
- <marker id="system_info_dirty_cpu_schedulers"></marker>
<p>Returns the number of dirty CPU scheduler threads used by
the emulator. Dirty CPU schedulers execute CPU-bound
native functions, such as NIFs, linked-in driver code,
@@ -7972,9 +8416,9 @@ ok
<c>erlang:system_flag(schedulers_online,
SchedulersOnline)</c></seealso>.</p>
</item>
- <tag><c>dirty_cpu_schedulers_online</c></tag>
+ <tag><marker id="system_info_dirty_cpu_schedulers_online"/>
+ <c>dirty_cpu_schedulers_online</c></tag>
<item>
- <marker id="system_info_dirty_cpu_schedulers_online"></marker>
<p>Returns the number of dirty CPU schedulers online.
The return value satisfies
<c><![CDATA[1 <= DirtyCPUSchedulersOnline <= N]]></c>,
@@ -7996,9 +8440,9 @@ ok
<c>erlang:system_flag(dirty_cpu_schedulers_online,
DirtyCPUSchedulersOnline)</c></seealso>.</p>
</item>
- <tag><c>dirty_io_schedulers</c></tag>
+ <tag><marker id="system_info_dirty_io_schedulers"/>
+ <c>dirty_io_schedulers</c></tag>
<item>
- <marker id="system_info_dirty_io_schedulers"></marker>
<p>Returns the number of dirty I/O schedulers as an integer.
Dirty I/O schedulers execute I/O-bound native functions,
such as NIFs and linked-in driver code, which cannot be
@@ -8015,179 +8459,9 @@ ok
<c>erlang:system_flag(dirty_cpu_schedulers_online,
DirtyCPUSchedulersOnline)</c></seealso>.</p>
</item>
- <tag><c>dist</c></tag>
- <item>
- <p>Returns a binary containing a string of distribution
- information formatted as in Erlang crash dumps. For more
- information, see section <seealso marker="erts:crash_dump">
- How to interpret the Erlang crash dumps</seealso>
- in the User's Guide.</p>
- </item>
- <tag><c>dist_buf_busy_limit</c></tag>
- <item>
- <marker id="system_info_dist_buf_busy_limit"></marker>
- <p>Returns the value of the distribution buffer busy limit
- in bytes. This limit can be set at startup by passing
- command-line flag
- <seealso marker="erts:erl#+zdbbl"><c>+zdbbl</c></seealso>
- to <c>erl(1)</c>.</p>
- </item>
- <tag><c>dist_ctrl</c></tag>
- <item>
- <p>Returns a list of tuples
- <c>{<anno>Node</anno>, <anno>ControllingEntity</anno>}</c>,
- one entry for each connected remote node.
- <c><anno>Node</anno></c> is the node name
- and <c><anno>ControllingEntity</anno></c> is the port or process
- identifier responsible for the communication to that node.
- More specifically, <c><anno>ControllingEntity</anno></c> for
- nodes connected through TCP/IP (the normal case) is the socket
- used in communication with the specific node.</p>
- </item>
- <tag><c>driver_version</c></tag>
- <item>
- <p>Returns a string containing the Erlang driver version
- used by the runtime system. It has the form
- <seealso marker="erts:erl_driver#version_management">
- "&lt;major ver&gt;.&lt;minor ver&gt;"</seealso>.</p>
- </item>
- <tag><c>dynamic_trace</c></tag>
- <item>
- <p>Returns an atom describing the dynamic trace framework
- compiled into the virtual machine. It can be
- <c>dtrace</c>, <c>systemtap</c>, or <c>none</c>. For a
- commercial or standard build, it is always <c>none</c>.
- The other return values indicate a custom configuration
- (for example, <c>./configure --with-dynamic-trace=dtrace</c>).
- For more information about dynamic tracing, see
- <seealso marker="runtime_tools:dyntrace">
- <c>dyntrace(3)</c></seealso> manual page and the
- <c>README.dtrace</c>/<c>README.systemtap</c> files in the
- Erlang source code top directory.</p>
- </item>
- <tag><c>dynamic_trace_probes</c></tag>
- <item>
- <p>Returns a <c>boolean()</c> indicating if dynamic trace
- probes (<c>dtrace</c> or <c>systemtap</c>) are built into
- the emulator. This can only be <c>true</c> if the virtual
- machine was built for dynamic tracing (that is,
- <c>system_info(dynamic_trace)</c> returns
- <c>dtrace</c> or <c>systemtap</c>).</p>
- </item>
- <tag><marker id="system_info_end_time"/><c>end_time</c></tag>
- <item>
- <p>The last <seealso marker="#monotonic_time/0">Erlang monotonic
- time</seealso> in <c>native</c>
- <seealso marker="#type_time_unit">time unit</seealso> that
- can be represented internally in the current Erlang runtime system
- instance. The time between the
- <seealso marker="#system_info_start_time">start time</seealso> and
- the end time is at least a quarter of a millennium.</p>
- </item>
- <tag><c>elib_malloc</c></tag>
- <item>
- <p>This option will be removed in a future release.
- The return value will always be <c>false</c>, as the
- <c>elib_malloc</c> allocator has been removed.</p>
- </item>
- <tag><c>ets_limit</c></tag>
- <item>
- <p>Returns the maximum number of ETS tables allowed. This
- limit can be increased at startup by passing
- command-line flag
- <seealso marker="erts:erl#+e"><c>+e</c></seealso> to
- <c>erl(1)</c> or by setting environment variable
- <c>ERL_MAX_ETS_TABLES</c> before starting the Erlang
- runtime system.</p>
- </item>
- <tag><c>heap_sizes</c></tag>
- <item>
- <p>Returns a list of integers representing valid heap sizes
- in words. All Erlang heaps are sized from sizes in this
- list.</p>
- </item>
- <tag><c>heap_type</c></tag>
- <item>
- <p>Returns the heap type used by the current emulator. One
- heap type exists:</p>
- <taglist>
- <tag><c>private</c></tag>
- <item>
- Each process has a heap reserved for its use and no
- references between heaps of different processes are
- allowed. Messages passed between processes are copied
- between heaps.
- </item>
- </taglist>
- </item>
- <tag><c>info</c></tag>
- <item>
- <p>Returns a binary containing a string of miscellaneous
- system information formatted as in Erlang crash dumps.
- For more information, see section
- <seealso marker="erts:crash_dump">
- How to interpret the Erlang crash dumps</seealso>
- in the User's Guide.</p>
- </item>
- <tag><c>kernel_poll</c></tag>
- <item>
- <p>Returns <c>true</c> if the emulator uses some kind of
- kernel-poll implementation, otherwise <c>false</c>.</p>
- </item>
- <tag><c>loaded</c></tag>
- <item>
- <p>Returns a binary containing a string of loaded module
- information formatted as in Erlang crash dumps. For more
- information, see section
- <seealso marker="erts:crash_dump">How to interpret the Erlang
- crash dumps</seealso> in the User's Guide.</p>
- </item>
- <tag><c>logical_processors</c></tag>
- <item>
- <marker id="logical_processors"></marker>
- <p>Returns the detected number of logical processors configured
- in the system. The return value is either an integer, or
- the atom <c>unknown</c> if the emulator cannot
- detect the configured logical processors.</p>
- </item>
- <tag><c>logical_processors_available</c></tag>
+ <tag><marker id="system_info_multi_scheduling"/>
+ <c>multi_scheduling</c></tag>
<item>
- <marker id="logical_processors_available"></marker>
- <p>Returns the detected number of logical processors available
- to the Erlang runtime system. The return value is either an
- integer, or the atom <c>unknown</c> if the emulator
- cannot detect the available logical processors. The number
- of available logical processors is less than or equal to
- the number of <seealso marker="#logical_processors_online">
- logical processors online</seealso>.</p>
- </item>
- <tag><c>logical_processors_online</c></tag>
- <item>
- <marker id="logical_processors_online"></marker>
- <p>Returns the detected number of logical processors online on
- the system. The return value is either an integer,
- or the atom <c>unknown</c> if the emulator cannot
- detect logical processors online. The number of logical
- processors online is less than or equal to the number of
- <seealso marker="#logical_processors">logical processors
- configured</seealso>.</p>
- </item>
- <tag><c>machine</c></tag>
- <item>
- <p>Returns a string containing the Erlang machine name.</p>
- </item>
- <tag><c>modified_timing_level</c></tag>
- <item>
- <p>Returns the modified timing-level (an integer) if
- modified timing is enabled, otherwise <c>undefined</c>.
- For more information about modified timing, see
- command-line flag
- <seealso marker="erts:erl#+T"><c>+T</c></seealso>
- in <c>erl(1)</c></p>
- </item>
- <tag><c>multi_scheduling</c></tag>
- <item>
- <marker id="system_info_multi_scheduling"></marker>
<p>Returns one of the following:</p>
<taglist>
<tag><c>disabled</c></tag>
@@ -8227,9 +8501,9 @@ ok
and <seealso marker="#system_info_schedulers">
<c>erlang:system_info(schedulers)</c></seealso>.</p>
</item>
- <tag><c>multi_scheduling_blockers</c></tag>
+ <tag><marker id="system_info_multi_scheduling_blockers"/>
+ <c>multi_scheduling_blockers</c></tag>
<item>
- <marker id="system_info_multi_scheduling_blockers"></marker>
<p>Returns a list of <c><anno>Pid</anno></c>s when
multi-scheduling is blocked, otherwise the empty list is
returned. The <c><anno>Pid</anno></c>s in the list
@@ -8247,15 +8521,9 @@ ok
and <seealso marker="#system_info_schedulers">
<c>erlang:system_info(schedulers)</c></seealso>.</p>
</item>
- <tag><c>nif_version</c></tag>
- <item>
- <p>Returns a string containing the version of the Erlang NIF
- interface used by the runtime system. It is on the form
- "&lt;major ver&gt;.&lt;minor ver&gt;".</p>
- </item>
- <tag><c>normal_multi_scheduling_blockers</c></tag>
+ <tag><marker id="system_info_normal_multi_scheduling_blockers"/>
+ <c>normal_multi_scheduling_blockers</c></tag>
<item>
- <marker id="system_info_normal_multi_scheduling_blockers"></marker>
<p>Returns a list of <c><anno>Pid</anno></c>s when
normal multi-scheduling is blocked (that is, all normal schedulers
but one is blocked), otherwise the empty list is returned.
@@ -8273,192 +8541,9 @@ ok
and <seealso marker="#system_info_schedulers">
<c>erlang:system_info(schedulers)</c></seealso>.</p>
</item>
- <tag><marker id="system_info_otp_release"/>
- <c>otp_release</c></tag>
- <item>
- <marker id="system_info_otp_release"></marker>
- <p>Returns a string containing the OTP release number of the
- OTP release that the currently executing ERTS application
- is part of.</p>
- <p>As from Erlang/OTP 17, the OTP release number corresponds to
- the major OTP version number. No
- <c>erlang:system_info()</c> argument gives the exact OTP
- version. This is because the exact OTP version in the general case
- is difficult to determine. For more information, see the
- description of versions in
- <seealso marker="doc/system_principles:versions">
- System principles</seealso> in System Documentation.</p>
- </item>
- <tag><marker id="system_info_os_monotonic_time_source"/>
- <c>os_monotonic_time_source</c></tag>
+ <tag><marker id="system_info_scheduler_bind_type"/>
+ <c>scheduler_bind_type</c></tag>
<item>
- <p>Returns a list containing information about the source of
- <seealso marker="erts:time_correction#OS_Monotonic_Time">OS
- monotonic time</seealso> that is used by the runtime system.</p>
- <p>If <c>[]</c> is returned, no OS monotonic time is
- available. The list contains two-tuples with <c>Key</c>s
- as first element, and <c>Value</c>s as second element. The
- order of these tuples is undefined. The following
- tuples can be part of the list, but more tuples can be
- introduced in the future:</p>
- <taglist>
- <tag><c>{function, Function}</c></tag>
- <item><p><c>Function</c> is the name of the function
- used. This tuple always exists if OS monotonic time is
- available to the runtime system.</p>
- </item>
- <tag><c>{clock_id, ClockId}</c></tag>
- <item><p>This tuple only exists if <c>Function</c>
- can be used with different clocks. <c>ClockId</c>
- corresponds to the clock identifier used when calling
- <c>Function</c>.</p>
- </item>
- <tag><c>{resolution, OsMonotonicTimeResolution}</c></tag>
- <item><p>Highest possible
- <seealso marker="time_correction#Time_Resolution">
- resolution</seealso>
- of current OS monotonic time source as parts per
- second. If no resolution information can be retrieved
- from the OS, <c>OsMonotonicTimeResolution</c> is
- set to the resolution of the time unit of
- <c>Function</c>s return value. That is, the actual
- resolution can be lower than
- <c>OsMonotonicTimeResolution</c>. Notice that
- the resolution does not say anything about the
- <seealso marker="time_correction#Time_Accuracy">
- accuracy</seealso> or whether the
- <seealso marker="time_correction#Time_Precision">
- precision</seealso> aligns with the resolution. You do,
- however, know that the precision is not better than
- <c>OsMonotonicTimeResolution</c>.</p>
- </item>
- <tag><c>{extended, Extended}</c></tag>
- <item><p><c>Extended</c> equals <c>yes</c> if
- the range of time values has been extended;
- otherwise <c>Extended</c> equals <c>no</c>. The
- range must be extended if <c>Function</c>
- returns values that wrap fast. This typically
- is the case when the return value is a 32-bit value.</p>
- </item>
- <tag><c>{parallel, Parallel}</c></tag>
- <item><p><c>Parallel</c> equals <c>yes</c> if
- <c>Function</c> is called in parallel from multiple
- threads. If it is not called in parallel, because
- calls must be serialized, <c>Parallel</c> equals
- <c>no</c>.</p>
- </item>
- <tag><c>{time, OsMonotonicTime}</c></tag>
- <item><p><c>OsMonotonicTime</c> equals current OS
- monotonic time in <c>native</c>
- <seealso marker="#type_time_unit">time unit</seealso>.</p>
- </item>
- </taglist>
- </item>
- <tag><marker id="system_info_os_system_time_source"/>
- <c>os_system_time_source</c></tag>
- <item>
- <p>Returns a list containing information about the source of
- <seealso marker="erts:time_correction#OS_System_Time">OS
- system time</seealso> that is used by the runtime system.</p>
- <p>The list contains two-tuples with <c>Key</c>s
- as first element, and <c>Value</c>s as second element. The
- order if these tuples is undefined. The following
- tuples can be part of the list, but more tuples can be
- introduced in the future:</p>
- <taglist>
- <tag><c>{function, Function}</c></tag>
- <item><p><c>Function</c> is the name of the funcion used.</p>
- </item>
- <tag><c>{clock_id, ClockId}</c></tag>
- <item><p>Exists only if <c>Function</c>
- can be used with different clocks. <c>ClockId</c>
- corresponds to the clock identifier used when calling
- <c>Function</c>.</p>
- </item>
- <tag><c>{resolution, OsSystemTimeResolution}</c></tag>
- <item><p>Highest possible
- <seealso marker="time_correction#Time_Resolution">
- resolution</seealso>
- of current OS system time source as parts per
- second. If no resolution information can be retrieved
- from the OS, <c>OsSystemTimeResolution</c> is
- set to the resolution of the time unit of
- <c>Function</c>s return value. That is, the actual
- resolution can be lower than
- <c>OsSystemTimeResolution</c>. Notice that
- the resolution does not say anything about the
- <seealso marker="time_correction#Time_Accuracy">
- accuracy</seealso> or whether the
- <seealso marker="time_correction#Time_Precision">
- precision</seealso> do align with the resolution. You do,
- however, know that the precision is not better than
- <c>OsSystemTimeResolution</c>.</p>
- </item>
- <tag><c>{parallel, Parallel}</c></tag>
- <item><p><c>Parallel</c> equals <c>yes</c> if
- <c>Function</c> is called in parallel from multiple
- threads. If it is not called in parallel, because
- calls needs to be serialized, <c>Parallel</c> equals
- <c>no</c>.</p>
- </item>
- <tag><c>{time, OsSystemTime}</c></tag>
- <item><p><c>OsSystemTime</c> equals current OS
- system time in <c>native</c>
- <seealso marker="#type_time_unit">time unit</seealso>.</p>
- </item>
- </taglist>
- </item>
- <tag><c>port_parallelism</c></tag>
- <item>
- <marker id="system_info_port_parallelism"></marker>
- <p>Returns the default port parallelism scheduling hint used.
- For more information, see command-line argument
- <seealso marker="erl#+spp"><c>+spp</c></seealso>
- in <c>erl(1)</c>.</p>
- </item>
- <tag><marker id="system_info_port_count"/><c>port_count</c></tag>
- <item>
- <p>Returns the number of ports currently existing at the
- local node. The value is given as an integer. This is
- the same value as returned by
- <c>length(erlang:ports())</c>, but more efficient.</p>
- </item>
- <tag><c>port_limit</c></tag>
- <item>
- <marker id="system_info_port_limit"></marker>
- <p>Returns the maximum number of simultaneously existing
- ports at the local node as an integer. This limit can be
- configured at startup by using command-line flag
- <seealso marker="erl#+Q"><c>+Q</c></seealso> in <c>erl(1)</c>.</p>
- </item>
- <tag><marker id="system_info_process_count"/>
- <c>process_count</c></tag>
- <item>
- <p>Returns the number of processes currently existing at the
- local node. The value is given as an integer. This is
- the same value as returned by
- <c>length(processes())</c>, but more efficient.</p>
- </item>
- <tag><c>process_limit</c></tag>
- <item>
- <marker id="system_info_process_limit"></marker>
- <p>Returns the maximum number of simultaneously existing
- processes at the local node. The value is given as an
- integer. This limit can be configured at startup by using
- command-line flag <seealso marker="erl#+P"><c>+P</c></seealso>
- in <c>erl(1)</c>.</p>
- </item>
- <tag><c>procs</c></tag>
- <item>
- <p>Returns a binary containing a string of process and port
- information formatted as in Erlang crash dumps. For more
- information, see section <seealso marker="erts:crash_dump">
- How to interpret the Erlang crash dumps</seealso>
- in the User's Guide.</p>
- </item>
- <tag><c>scheduler_bind_type</c></tag>
- <item>
- <marker id="system_info_scheduler_bind_type"></marker>
<p>Returns information about how the user has requested
schedulers to be bound or not bound.</p>
<p>Notice that although a user has requested
@@ -8472,9 +8557,9 @@ ok
<seealso marker="#system_info_scheduler_bindings">
<c>erlang:system_info(scheduler_bindings)</c></seealso>.</p>
</item>
- <tag><c>scheduler_bindings</c></tag>
+ <tag><marker id="system_info_scheduler_bindings"/>
+ <c>scheduler_bindings</c></tag>
<item>
- <marker id="system_info_scheduler_bindings"></marker>
<p>Returns information about the currently used scheduler
bindings.</p>
<p>A tuple of a size equal to
@@ -8498,9 +8583,9 @@ ok
<seealso marker="#system_info_schedulers_online">
<c>erlang:system_info(schedulers_online)</c></seealso>.</p>
</item>
- <tag><c>scheduler_id</c></tag>
+ <tag><marker id="system_info_scheduler_id"/>
+ <c>scheduler_id</c></tag>
<item>
- <marker id="system_info_scheduler_id"></marker>
<p>Returns the scheduler ID (<c>SchedulerId</c>) of the
scheduler thread that the calling process is executing
on. <c><anno>SchedulerId</anno></c> is a positive integer,
@@ -8510,9 +8595,9 @@ ok
<seealso marker="#system_info_schedulers">
<c>erlang:system_info(schedulers)</c></seealso>.</p>
</item>
- <tag><c>schedulers</c></tag>
+ <tag><marker id="system_info_schedulers"/>
+ <c>schedulers</c></tag>
<item>
- <marker id="system_info_schedulers"></marker>
<p>Returns the number of scheduler threads used by
the emulator. Scheduler threads online schedules Erlang
processes and Erlang ports, and execute Erlang code
@@ -8539,9 +8624,9 @@ ok
<c>erlang:system_info(multi_scheduling_blockers)</c></seealso>.
</p>
</item>
- <tag><c>schedulers_online</c></tag>
+ <tag><marker id="system_info_schedulers_online"/>
+ <c>schedulers_online</c></tag>
<item>
- <marker id="system_info_schedulers_online"></marker>
<p>Returns the number of schedulers online. The scheduler
identifiers of schedulers online satisfy the relationship
<c><![CDATA[1 <= SchedulerId <=
@@ -8553,34 +8638,18 @@ ok
<c>erlang:system_flag(schedulers_online,
SchedulersOnline)</c></seealso>.</p>
</item>
- <tag><c>smp_support</c></tag>
+ <tag><marker id="system_info_smp_support"/>
+ <c>smp_support</c></tag>
<item>
<p>Returns <c>true</c>.</p>
</item>
- <tag><marker id="system_info_start_time"/><c>start_time</c></tag>
- <item>
- <p>The <seealso marker="#monotonic_time/0">Erlang monotonic
- time</seealso> in <c>native</c>
- <seealso marker="#type_time_unit">time unit</seealso> at the
- time when current Erlang runtime system instance started.</p>
- <p>See also <seealso marker="#system_info_end_time">
- <c>erlang:system_info(end_time)</c></seealso>.</p>
- </item>
- <tag><c>system_version</c></tag>
- <item>
- <p>Returns a string containing version number and
- some important properties, such as the number of schedulers.</p>
- </item>
- <tag><c>system_architecture</c></tag>
- <item>
- <p>Returns a string containing the processor and OS
- architecture the emulator is built for.</p>
- </item>
- <tag><c>threads</c></tag>
+ <tag><marker id="system_info_threads"/>
+ <c>threads</c></tag>
<item>
<p>Returns <c>true</c>.</p>
</item>
- <tag><c>thread_pool_size</c></tag>
+ <tag><marker id="system_info_thread_pool_size"/>
+ <c>thread_pool_size</c></tag>
<item>
<marker id="system_info_thread_pool_size"></marker>
<p>Returns the number of async threads in the async thread
@@ -8589,111 +8658,341 @@ ok
<c>erl_driver:driver_async()</c></seealso>).
The value is given as an integer.</p>
</item>
- <tag><c>time_correction</c></tag>
+ </taglist>
+ </desc>
+ </func>
+
+ <func>
+ <name name="system_info" arity="1" clause_i="14"
+ anchor="system_info_dist"/> <!-- creation -->
+ <name name="system_info" arity="1" clause_i="16"/> <!-- delayed_node_table_gc -->
+ <name name="system_info" arity="1" clause_i="20"/> <!-- dist -->
+ <name name="system_info" arity="1" clause_i="21"/> <!-- dist_buf_busy_limit -->
+ <name name="system_info" arity="1" clause_i="22"/> <!-- dist_ctrl -->
+ <fsummary>Information about erlang distribution.</fsummary>
+ <desc>
+ <marker id="system_info_dist_tags"/>
+ <p>Returns information about Erlang Distribution in the
+ current system as specified by <c><anno>Item</anno></c>:</p>
+ <taglist>
+ <tag><marker id="system_info_creation"/>
+ <c>creation</c></tag>
<item>
- <marker id="system_info_time_correction"></marker>
- <p>Returns a boolean value indicating whether
- <seealso marker="time_correction#Time_Correction">
- time correction</seealso> is enabled or not.</p>
+ <p>Returns the creation of the local node as an integer.
+ The creation is changed when a node is restarted. The
+ creation of a node is stored in process identifiers, port
+ identifiers, and references. This makes it (to some
+ extent) possible to distinguish between identifiers from
+ different incarnations of a node. The valid
+ creations are integers in the range 1..3, but this will
+ probably change in a future release. If the node is not
+ alive, <c>0</c> is returned.</p>
</item>
- <tag><c>time_offset</c></tag>
+ <tag><marker id="system_info_delayed_node_table_gc"/>
+ <c>delayed_node_table_gc</c></tag>
<item>
- <marker id="system_info_time_offset"></marker>
- <p>Returns the state of the time offset:</p>
- <taglist>
- <tag><c>preliminary</c></tag>
- <item>
- <p>The time offset is preliminary, and will be changed
- and finalized later. The preliminary time offset
- is used during the preliminary phase of the
- <seealso marker="time_correction#Single_Time_Warp_Mode">
- single time warp mode</seealso>.</p>
- </item>
- <tag><c>final</c></tag>
- <item>
- <p>The time offset is final. This either because
- <seealso marker="time_correction#No_Time_Warp_Mode">
- no time warp mode</seealso> is used, or because the time
- offset have been finalized when
- <seealso marker="time_correction#Single_Time_Warp_Mode">
- single time warp mode</seealso> is used.</p>
- </item>
- <tag><c>volatile</c></tag>
- <item>
- <p>The time offset is volatile. That is, it can
- change at any time. This is because
- <seealso marker="time_correction#Multi_Time_Warp_Mode">
- multi-time warp mode</seealso> is used.</p>
- </item>
- </taglist>
+ <p>Returns the amount of time in seconds garbage collection
+ of an entry in a node table is delayed. This limit can be set
+ on startup by passing command-line flag
+ <seealso marker="erts:erl#+zdntgc"><c>+zdntgc</c></seealso>
+ to <c>erl(1)</c>. For more information, see the documentation of
+ the command-line flag.</p>
</item>
- <tag><marker id="system_info_time_warp_mode"/>
- <c>time_warp_mode</c></tag>
+ <tag><marker id="system_info_dist"/>
+ <c>dist</c></tag>
<item>
- <p>Returns a value identifying the
- <seealso marker="time_correction#Time_Warp_Modes">
- time warp mode</seealso> that is used:</p>
- <taglist>
- <tag><c>no_time_warp</c></tag>
- <item>The <seealso marker="time_correction#No_Time_Warp_Mode">
- no time warp mode</seealso> is used.
- </item>
- <tag><c>single_time_warp</c></tag>
- <item>The <seealso marker="time_correction#Single_Time_Warp_Mode">
- single time warp mode</seealso> is used.
- </item>
- <tag><c>multi_time_warp</c></tag>
- <item>The <seealso marker="time_correction#Multi_Time_Warp_Mode">
- multi-time warp mode</seealso> is used.
- </item>
- </taglist>
+ <p>Returns a binary containing a string of distribution
+ information formatted as in Erlang crash dumps. For more
+ information, see section <seealso marker="erts:crash_dump">
+ How to interpret the Erlang crash dumps</seealso>
+ in the User's Guide.</p>
</item>
- <tag><c>tolerant_timeofday</c></tag>
+ <tag><marker id="system_info_dist_buf_busy_limit"/>
+ <c>dist_buf_busy_limit</c></tag>
<item>
- <marker id="system_info_tolerant_timeofday"></marker>
- <p>Returns whether a pre ERTS 7.0 backwards compatible
- compensation for sudden changes of system time is <c>enabled</c>
- or <c>disabled</c>. Such compensation is <c>enabled</c> when the
- <seealso marker="#system_info_time_offset">time offset</seealso>
- is <c>final</c>, and
- <seealso marker="#system_info_time_correction">
- time correction</seealso> is enabled.</p>
+ <p>Returns the value of the distribution buffer busy limit
+ in bytes. This limit can be set at startup by passing
+ command-line flag
+ <seealso marker="erts:erl#+zdbbl"><c>+zdbbl</c></seealso>
+ to <c>erl(1)</c>.</p>
</item>
- <tag><c>trace_control_word</c></tag>
+ <tag><marker id="system_info_dist_ctrl"/>
+ <c>dist_ctrl</c></tag>
+ <item>
+ <p>Returns a list of tuples
+ <c>{<anno>Node</anno>, <anno>ControllingEntity</anno>}</c>,
+ one entry for each connected remote node.
+ <c><anno>Node</anno></c> is the node name
+ and <c><anno>ControllingEntity</anno></c> is the port or process
+ identifier responsible for the communication to that node.
+ More specifically, <c><anno>ControllingEntity</anno></c> for
+ nodes connected through TCP/IP (the normal case) is the socket
+ used in communication with the specific node.</p>
+ </item>
+ </taglist>
+ </desc>
+ </func>
+
+ <func>
+ <!-- <name name="system_info" arity="1" clause_i="1"/> allocated_areas -->
+ <!-- <name name="system_info" arity="1" clause_i="2"/> allocated -->
+ <!-- <name name="system_info" arity="1" clause_i="3"/> {allocator, _} -->
+ <!-- <name name="system_info" arity="1" clause_i="4"/> alloc_util_allocators -->
+ <!-- <name name="system_info" arity="1" clause_i="5"/> {allocator_sizes, _} -->
+ <!-- <name name="system_info" arity="1" clause_i="6"/> atom_count -->
+ <!-- <name name="system_info" arity="1" clause_i="7"/> atom_limit -->
+ <name name="system_info" arity="1" clause_i="8"
+ anchor="system_info_misc"/> <!-- build_type -->
+ <name name="system_info" arity="1" clause_i="9"/> <!-- c_compiler_used -->
+ <name name="system_info" arity="1" clause_i="10"/> <!-- check_io -->
+ <name name="system_info" arity="1" clause_i="11"/> <!-- compat_rel -->
+ <!-- <name name="system_info" arity="1" clause_i="12"/> cpu_topology -->
+ <!-- <name name="system_info" arity="1" clause_i="13"/> {cpu_topology, _} -->
+ <!-- <name name="system_info" arity="1" clause_i="14"/> creation -->
+ <name name="system_info" arity="1" clause_i="15"/> <!-- debug_compiled -->
+ <!-- <name name="system_info" arity="1" clause_i="16"/> delayed_node_table_gc -->
+ <!-- <name name="system_info" arity="1" clause_i="17"/> dirty_cpu_schedulers -->
+ <!-- <name name="system_info" arity="1" clause_i="18"/> dirty_cpu_schedulers_online -->
+ <!-- <name name="system_info" arity="1" clause_i="19"/> dirty_io_schedulers -->
+ <!-- <name name="system_info" arity="1" clause_i="20"/> dist -->
+ <!-- <name name="system_info" arity="1" clause_i="21"/> dist_buf_busy_limit -->
+ <!-- <name name="system_info" arity="1" clause_i="22"/> dist_ctrl -->
+ <name name="system_info" arity="1" clause_i="23"/> <!-- driver_version -->
+ <name name="system_info" arity="1" clause_i="24"/> <!-- dynamic_trace -->
+ <name name="system_info" arity="1" clause_i="25"/> <!-- dynamic_trace_probes -->
+ <!-- <name name="system_info" arity="1" clause_i="26"/> end_time -->
+ <!-- <name name="system_info" arity="1" clause_i="27"/> elib_malloc -->
+ <!-- <name name="system_info" arity="1" clause_i="28"/> eager_check_io, removed -->
+ <!-- <name name="system_info" arity="1" clause_i="29"/> ets_limit -->
+ <!-- <name name="system_info" arity="1" clause_i="30"/> fullsweep_after -->
+ <!-- <name name="system_info" arity="1" clause_i="31"/> garbage_collection -->
+ <!-- <name name="system_info" arity="1" clause_i="32"/> heap_sizes -->
+ <!-- <name name="system_info" arity="1" clause_i="33"/> heap_type -->
+ <name name="system_info" arity="1" clause_i="34"/> <!-- info -->
+ <name name="system_info" arity="1" clause_i="35"/> <!-- kernel_poll -->
+ <name name="system_info" arity="1" clause_i="36"/> <!-- loaded -->
+ <!-- <name name="system_info" arity="1" clause_i="37"/> logical_processors -->
+ <name name="system_info" arity="1" clause_i="38"/> <!-- machine -->
+ <!-- <name name="system_info" arity="1" clause_i="39"/> max_heap_size -->
+ <!-- <name name="system_info" arity="1" clause_i="40"/> message_queue_data -->
+ <!-- <name name="system_info" arity="1" clause_i="41"/> min_heap_size -->
+ <!-- <name name="system_info" arity="1" clause_i="42"/> min_bin_vheap_size -->
+ <name name="system_info" arity="1" clause_i="43"/> <!-- modified_timing_level -->
+ <!-- <name name="system_info" arity="1" clause_i="44"/> multi_scheduling -->
+ <!-- <name name="system_info" arity="1" clause_i="45"/> multi_scheduling_blockers -->
+ <name name="system_info" arity="1" clause_i="46"/> <!-- nif_version -->
+ <!-- n<name name="system_info" arity="1" clause_i="47"/> ormal_multi_scheduling_blockers -->
+ <name name="system_info" arity="1" clause_i="48"/> <!-- otp_release -->
+ <!-- <name name="system_info" arity="1" clause_i="49"/> os_monotonic_time_source -->
+ <!-- <name name="system_info" arity="1" clause_i="50"/> os_system_time_source -->
+ <name name="system_info" arity="1" clause_i="51"/> <!-- port_parallelism -->
+ <!-- <name name="system_info" arity="1" clause_i="52"/> port_count -->
+ <!-- <name name="system_info" arity="1" clause_i="53"/> port_limit -->
+ <!-- <name name="system_info" arity="1" clause_i="54"/> process_count -->
+ <!-- <name name="system_info" arity="1" clause_i="55"/> process_limit -->
+ <!-- <name name="system_info" arity="1" clause_i="56"/> procs -->
+ <!-- <name name="system_info" arity="1" clause_i="57"/> scheduler_bind_type -->
+ <!-- <name name="system_info" arity="1" clause_i="58"/> scheduler_bindings -->
+ <!-- <name name="system_info" arity="1" clause_i="59"/> scheduler_id -->
+ <!-- <name name="system_info" arity="1" clause_i="60"/> schedulers -->
+ <!-- <name name="system_info" arity="1" clause_i="61"/> smp_support -->
+ <!-- <name name="system_info" arity="1" clause_i="62"/> start_time -->
+ <name name="system_info" arity="1" clause_i="63"/> <!-- system_version -->
+ <name name="system_info" arity="1" clause_i="64"/> <!-- system_architecture -->
+ <!-- <name name="system_info" arity="1" clause_i="65"/> threads -->
+ <!-- <name name="system_info" arity="1" clause_i="66"/> thread_pool_size -->
+ <!-- <name name="system_info" arity="1" clause_i="67"/> time_correction -->
+ <!-- <name name="system_info" arity="1" clause_i="68"/> time_offset -->
+ <!-- <name name="system_info" arity="1" clause_i="69"/> time_warp_mode -->
+ <!-- <name name="system_info" arity="1" clause_i="70"/> tolerant_timeofday -->
+ <name name="system_info" arity="1" clause_i="71"/> <!-- trace_control_word -->
+ <!-- <name name="system_info" arity="1" clause_i="72"/> update_cpu_info -->
+ <name name="system_info" arity="1" clause_i="73"/> <!-- version -->
+ <name name="system_info" arity="1" clause_i="74"/> <!-- wordsize -->
+ <!-- <name name="system_info" arity="1" clause_i="75"/> overview -->
+ <fsummary>Information about the system.</fsummary>
+ <desc>
+ <marker id="system_info_misc_tags"/>
+ <p>Returns various information about the current system
+ (emulator) as specified by <c><anno>Item</anno></c>:</p>
+ <taglist>
+ <tag><marker id="system_info_build_type"/>
+ <c>build_type</c></tag>
+ <item>
+ <p>Returns an atom describing the build type of the runtime
+ system. This is normally the atom <c>opt</c> for optimized.
+ Other possible return values are <c>debug</c>, <c>purify</c>,
+ <c>quantify</c>, <c>purecov</c>, <c>gcov</c>, <c>valgrind</c>,
+ <c>gprof</c>, and <c>lcnt</c>. Possible return values
+ can be added or removed at any time without prior notice.</p>
+ </item>
+ <tag><marker id="system_info_c_compiler_used"/>
+ <c>c_compiler_used</c></tag>
+ <item>
+ <p>Returns a two-tuple describing the C compiler used when
+ compiling the runtime system. The first element is an
+ atom describing the name of the compiler, or <c>undefined</c>
+ if unknown. The second element is a term describing the
+ version of the compiler, or <c>undefined</c> if unknown.</p>
+ </item>
+ <tag><marker id="system_info_check_io"/>
+ <c>check_io</c></tag>
+ <item>
+ <p>Returns a list containing miscellaneous information
+ about the emulators internal I/O checking. Notice that
+ the content of the returned list can vary between
+ platforms and over time. It is only guaranteed
+ that a list is returned.</p>
+ </item>
+ <tag><marker id="system_info_compat_rel"/>
+ <c>compat_rel</c></tag>
+ <item>
+ <p>Returns the compatibility mode of the local node as
+ an integer. The integer returned represents the
+ Erlang/OTP release that the current emulator has been
+ set to be backward compatible with. The compatibility
+ mode can be configured at startup by using command-line flag
+ <seealso marker="erts:erl#compat_rel"><c>+R</c></seealso> in
+ <c>erl(1)</c>.</p>
+ </item>
+ <tag><marker id="system_info_debug_compiled"/>
+ <c>debug_compiled</c></tag>
+ <item>
+ <p>Returns <c>true</c> if the emulator has been
+ debug-compiled, otherwise <c>false</c>.</p>
+ </item>
+ <tag><marker id="system_info_driver_version"/>
+ <c>driver_version</c></tag>
+ <item>
+ <p>Returns a string containing the Erlang driver version
+ used by the runtime system. It has the form
+ <seealso marker="erts:erl_driver#version_management">
+ "&lt;major ver&gt;.&lt;minor ver&gt;"</seealso>.</p>
+ </item>
+ <tag><marker id="system_info_dynamic_trace"/>
+ <c>dynamic_trace</c></tag>
+ <item>
+ <p>Returns an atom describing the dynamic trace framework
+ compiled into the virtual machine. It can be
+ <c>dtrace</c>, <c>systemtap</c>, or <c>none</c>. For a
+ commercial or standard build, it is always <c>none</c>.
+ The other return values indicate a custom configuration
+ (for example, <c>./configure --with-dynamic-trace=dtrace</c>).
+ For more information about dynamic tracing, see
+ <seealso marker="runtime_tools:dyntrace">
+ <c>dyntrace(3)</c></seealso> manual page and the
+ <c>README.dtrace</c>/<c>README.systemtap</c> files in the
+ Erlang source code top directory.</p>
+ </item>
+ <tag><marker id="system_info_dynamic_trace_probes"/>
+ <c>dynamic_trace_probes</c></tag>
+ <item>
+ <p>Returns a <c>boolean()</c> indicating if dynamic trace
+ probes (<c>dtrace</c> or <c>systemtap</c>) are built into
+ the emulator. This can only be <c>true</c> if the virtual
+ machine was built for dynamic tracing (that is,
+ <c>system_info(dynamic_trace)</c> returns
+ <c>dtrace</c> or <c>systemtap</c>).</p>
+ </item>
+ <tag><marker id="system_info_info"/>
+ <c>info</c></tag>
+ <item>
+ <p>Returns a binary containing a string of miscellaneous
+ system information formatted as in Erlang crash dumps.
+ For more information, see section
+ <seealso marker="erts:crash_dump">
+ How to interpret the Erlang crash dumps</seealso>
+ in the User's Guide.</p>
+ </item>
+ <tag><marker id="system_info_kernel_poll"/>
+ <c>kernel_poll</c></tag>
+ <item>
+ <p>Returns <c>true</c> if the emulator uses some kind of
+ kernel-poll implementation, otherwise <c>false</c>.</p>
+ </item>
+ <tag><marker id="system_info_loaded"/>
+ <c>loaded</c></tag>
+ <item>
+ <p>Returns a binary containing a string of loaded module
+ information formatted as in Erlang crash dumps. For more
+ information, see section
+ <seealso marker="erts:crash_dump">How to interpret the Erlang
+ crash dumps</seealso> in the User's Guide.</p>
+ </item>
+ <tag><marker id="system_info_machine"/>
+ <c>machine</c></tag>
+ <item>
+ <p>Returns a string containing the Erlang machine name.</p>
+ </item>
+ <tag><marker id="system_info_modified_timing_level"/>
+ <c>modified_timing_level</c></tag>
+ <item>
+ <p>Returns the modified timing-level (an integer) if
+ modified timing is enabled, otherwise <c>undefined</c>.
+ For more information about modified timing, see
+ command-line flag
+ <seealso marker="erts:erl#+T"><c>+T</c></seealso>
+ in <c>erl(1)</c></p>
+ </item>
+ <tag><marker id="system_info_nif_version"/>
+ <c>nif_version</c></tag>
+ <item>
+ <p>Returns a string containing the version of the Erlang NIF
+ interface used by the runtime system. It is on the form
+ "&lt;major ver&gt;.&lt;minor ver&gt;".</p>
+ </item>
+ <tag><marker id="system_info_otp_release"/>
+ <c>otp_release</c></tag>
+ <item>
+ <marker id="system_info_otp_release"></marker>
+ <p>Returns a string containing the OTP release number of the
+ OTP release that the currently executing ERTS application
+ is part of.</p>
+ <p>As from Erlang/OTP 17, the OTP release number corresponds to
+ the major OTP version number. No
+ <c>erlang:system_info()</c> argument gives the exact OTP
+ version. This is because the exact OTP version in the general case
+ is difficult to determine. For more information, see the
+ description of versions in
+ <seealso marker="doc/system_principles:versions">
+ System principles</seealso> in System Documentation.</p>
+ </item>
+ <tag><marker id="system_info_port_parallelism"/>
+ <c>port_parallelism</c></tag>
+ <item>
+ <p>Returns the default port parallelism scheduling hint used.
+ For more information, see command-line argument
+ <seealso marker="erl#+spp"><c>+spp</c></seealso>
+ in <c>erl(1)</c>.</p>
+ </item>
+ <tag><marker id="system_info_system_version"/>
+ <c>system_version</c></tag>
+ <item>
+ <p>Returns a string containing version number and
+ some important properties, such as the number of schedulers.</p>
+ </item>
+ <tag><marker id="system_info_system_architecture"/>
+ <c>system_architecture</c></tag>
+ <item>
+ <p>Returns a string containing the processor and OS
+ architecture the emulator is built for.</p>
+ </item>
+ <tag><marker id="system_info_trace_control_word"/>
+ <c>trace_control_word</c></tag>
<item>
<p>Returns the value of the node trace control word. For
more information, see function <c>get_tcw</c> in section
<seealso marker="erts:match_spec#get_tcw">
Match Specifications in Erlang</seealso> in the User's Guide.</p>
</item>
- <tag><c>update_cpu_info</c></tag>
- <item>
- <marker id="update_cpu_info"></marker>
- <p>The runtime system rereads the CPU information available
- and updates its internally stored information about the
- <seealso marker="#system_info_cpu_topology_detected">detected
- CPU topology</seealso> and the number of logical processors
- <seealso marker="#logical_processors">configured</seealso>,
- <seealso marker="#logical_processors_online">online</seealso>,
- and <seealso marker="#logical_processors_available">
- available</seealso>.</p>
- <p>If the CPU information has changed since the last time
- it was read, the atom <c>changed</c> is returned, otherwise
- the atom <c>unchanged</c>. If the CPU information has changed,
- you probably want to
- <seealso marker="#system_flag_schedulers_online">adjust the
- number of schedulers online</seealso>. You typically want
- to have as many schedulers online as
- <seealso marker="#logical_processors_available">logical
- processors available</seealso>.</p>
- </item>
- <tag><c>version</c></tag>
+ <tag><marker id="system_info_version"/>
+ <c>version</c></tag>
<item>
- <marker id="system_info_version"></marker>
<p>Returns a string containing the version number of the
emulator.</p>
</item>
- <tag><c>wordsize</c></tag>
+ <tag><marker id="system_info_wordsize"/>
+ <c>wordsize</c></tag>
<item>
<p>Same as <c>{wordsize, internal}</c>.</p>
</item>
@@ -8715,13 +9014,6 @@ ok
64-bit architecture, 8 is returned.</p>
</item>
</taglist>
- <note>
- <p>Argument <c>scheduler</c> has changed name to
- <c>scheduler_id</c> to avoid mix up with argument
- <c>schedulers</c>. Argument <c>scheduler</c> was
- introduced in ERTS 5.5 and renamed in
- ERTS 5.5.1.</p>
- </note>
</desc>
</func>
diff --git a/erts/doc/src/erts_alloc.xml b/erts/doc/src/erts_alloc.xml
index 580780e73b..53e136d76c 100644
--- a/erts/doc/src/erts_alloc.xml
+++ b/erts/doc/src/erts_alloc.xml
@@ -68,8 +68,7 @@
fixed size data types.</item>
<tag><c>exec_alloc</c></tag>
<item>Allocator used by the <seealso marker="hipe:HiPE_app"><c>HiPE</c></seealso>
- application for native executable code on specific architectures
- (x86_64).</item>
+ application for native executable code.</item>
<tag><c>std_alloc</c></tag>
<item>Allocator used for most memory blocks not allocated through any of
the other allocators described above.</item>
@@ -225,6 +224,33 @@
used. The time complexity is proportional to log N, where
N is the number of free blocks.</p>
</item>
+ <tag>Age order first fit carrier address order first fit</tag>
+ <item>
+ <p>Strategy: Find the <em>oldest carrier</em> that
+ can satisfy the requested block size, then find a block within
+ that carrier using the "address order first fit" strategy.</p>
+ <p>Implementation: A balanced binary search tree is
+ used. The time complexity is proportional to log N, where
+ N is the number of free blocks.</p>
+ </item>
+ <tag>Age order first fit carrier best fit</tag>
+ <item>
+ <p>Strategy: Find the <em>oldest carrier</em> that
+ can satisfy the requested block size, then find a block within
+ that carrier using the "best fit" strategy.</p>
+ <p>Implementation: Balanced binary search trees are
+ used. The time complexity is proportional to log N, where
+ N is the number of free blocks.</p>
+ </item>
+ <tag>Age order first fit carrier address order best fit</tag>
+ <item>
+ <p>Strategy: Find the <em>oldest carrier</em> that
+ can satisfy the requested block size, then find a block within
+ that carrier using the "address order best fit" strategy.</p>
+ <p>Implementation: Balanced binary search trees are
+ used. The time complexity is proportional to log N, where
+ N is the number of free blocks.</p>
+ </item>
<tag>Good fit</tag>
<item>
<p>Strategy: Try to find the best fit, but settle for the best fit
@@ -467,7 +493,8 @@
fetched, it will function as an ordinary carrier. This feature has
special requirements on the
<seealso marker="#M_as">allocation strategy</seealso> used. Only
- the strategies <c>aoff</c>, <c>aoffcbf</c>, and <c>aoffcaobf</c>
+ the strategies <c>aoff</c>, <c>aoffcbf</c>, <c>aoffcaobf</c>,
+ <c>ageffcaoff</c>m, <c>ageffcbf</c> and <c>ageffcaobf</c>
support abandoned carriers.</p>
<p>This feature also requires
<seealso marker="#M_t">multiple thread specific instances</seealso>
@@ -478,8 +505,30 @@
allocators based on the <c>alloc_util</c> framework, except
<c>temp_alloc</c> (which would be pointless).</p>
</item>
+
+ <tag><marker id="M_acfml"/><c><![CDATA[+M<S>acfml <bytes>]]></c>
+ </tag>
+ <item>
+ <p>Abandon carrier free block min limit. A valid <c><![CDATA[<bytes>]]></c>
+ is a positive integer representing a block size limit. The largest
+ free block in a carrier must be at least <c>bytes</c> large, for the
+ carrier to be abandoned. The default is zero but can be changed
+ in the future.</p>
+ <p>See also <seealso marker="#M_acul"><c>acul</c></seealso>.</p>
+ </item>
+
+ <tag><marker id="M_acnl"/><c><![CDATA[+M<S>acnl <amount>]]></c>
+ </tag>
+ <item>
+ <p>Abandon carrier number limit. A valid <c><![CDATA[<amount>]]></c>
+ is a positive integer representing max number of abandoned carriers per
+ allocator instance. Defaults to 1000 which will practically disable
+ the limit, but this can be changed in the future.</p>
+ <p>See also <seealso marker="#M_acul"><c>acul</c></seealso>.</p>
+ </item>
+
<tag><marker id="M_as"/>
- <c><![CDATA[+M<S>as bf|aobf|aoff|aoffcbf|aoffcaobf|gf|af]]></c></tag>
+ <c><![CDATA[+M<S>as bf|aobf|aoff|aoffcbf|aoffcaobf|ageffcaoff|ageffcbf|ageffcaobf|gf|af]]></c></tag>
<item>
<p>Allocation strategy. The following strategies are valid:</p>
<list type="bulleted">
@@ -490,6 +539,11 @@
</item>
<item><c>aoffcaobf</c> (address order first fit carrier address
order best fit)</item>
+ <item><c>ageffcaoff</c> (age order first fit carrier address order first fit)</item>
+ <item><c>ageffcbf</c> (age order first fit carrier best fit)
+ </item>
+ <item><c>ageffcaobf</c> (age order first fit carrier address
+ order best fit)</item>
<item><c>gf</c> (good fit)</item>
<item><c>af</c> (a fit)</item>
</list>
@@ -668,19 +722,6 @@
</section>
<section>
- <title>Special Flag for exec_alloc</title>
- <taglist>
- <tag><marker id="MXscs"/><c><![CDATA[+MXscs <size in MB>]]></c></tag>
- <item>
- <p><c>exec_alloc</c> super carrier size (in MB). The amount of
- <em>virtual</em> address space reserved for native executable code
- used by the <seealso marker="hipe:HiPE_app"><c>HiPE</c></seealso> application
- on specific architectures (x86_64). Defaults to <c>512</c>.</p>
- </item>
- </taglist>
- </section>
-
- <section>
<title>Instrumentation Flags</title>
<taglist>
<tag><marker id="Mim"/><c>+Mim true|false</c></tag>
diff --git a/erts/doc/src/match_spec.xml b/erts/doc/src/match_spec.xml
index 2a14f1e47b..644b989800 100644
--- a/erts/doc/src/match_spec.xml
+++ b/erts/doc/src/match_spec.xml
@@ -405,7 +405,8 @@
<c><![CDATA[tracer]]></c>.</p>
<p>If a tracer is specified in both lists, the tracer in the
enable list takes precedence. If no tracer is specified, the same
- tracer as the process executing the match specification is used.</p>
+ tracer as the process executing the match specification is used (not the meta tracer).
+ If that process doesn't have tracer either, then trace flags are ignored.</p>
<p>When using a <seealso marker="erl_tracer">tracer module</seealso>,
the module must be loaded before the match specification is
executed. If it is not loaded, the match fails.</p>
diff --git a/erts/doc/src/notes.xml b/erts/doc/src/notes.xml
index e91e56e581..fcc7ec5b66 100644
--- a/erts/doc/src/notes.xml
+++ b/erts/doc/src/notes.xml
@@ -31,6 +31,175 @@
</header>
<p>This document describes the changes made to the ERTS application.</p>
+<section><title>Erts 9.3</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Fixed a <c>configure</c> test for <c>libz</c> internals
+ that unintentionally caused various native code in OTP to
+ link against <c>libz</c>. Under certain circumstances
+ this caused the build of OTP to fail.</p>
+ <p>
+ Own Id: OTP-14840 Aux Id: ERL-529 </p>
+ </item>
+ <item>
+ <p>
+ File names containing unicode codepoints larger than 255
+ were not correctly encoded in stack traces.</p>
+ <p>
+ Own Id: OTP-14847 Aux Id: ERL-544 </p>
+ </item>
+ <item>
+ <p>
+ Fix HiPE bug for binary constructs like
+ <c>&lt;&lt;X/utf8&gt;&gt;</c> which could in rare cases
+ cause faulty results or VM crash.</p>
+ <p>
+ This fix affects both the <c>hipe</c> compiler and
+ <c>erts</c> runtime in an <em>incompatible</em> way. Old
+ hipe compiled files need to be recompiled to load and run
+ properly as native.</p>
+ <p>
+ *** POTENTIAL INCOMPATIBILITY ***</p>
+ <p>
+ Own Id: OTP-14850 Aux Id: PR-1664 </p>
+ </item>
+ <item>
+ <p>
+ Fix <c>term_to_binary/2</c> spec for
+ <c>minor_version</c>.</p>
+ <p>
+ Own Id: OTP-14876 Aux Id: ERL-548 </p>
+ </item>
+ <item>
+ <p>
+ Fix bug in erlang:binary_to_integer/2 where invalid
+ characters were not detected for bases larger then 10.
+ e.g. <c>binary_to_integer(&lt;&lt;":"&gt;&gt;, 16)</c>
+ would return 3 and not badarg as it should.</p>
+ <p>
+ Own Id: OTP-14879</p>
+ </item>
+ <item>
+ <p>Fixed bug in <c>float_to_list/2</c> and
+ <c>float_to_binary/2</c> with option <c>decimals</c> that
+ caused a faulty rounding up of the last decimal digit for
+ about 6% of floats with a fraction part.</p>
+ <p>For example, <c>float_to_list(0.145,
+ [{decimals,1}])</c> returned <c>"0.2"</c> instead of
+ <c>"0.1"</c>.</p>
+ <p>
+ Own Id: OTP-14890</p>
+ </item>
+ <item>
+ <p>
+ Fix bug causing slow hipe execution in modules loaded
+ early during boot or loaded by <c>code:atomic_load</c> or
+ <c>code:finish_loading</c>.</p>
+ <p>
+ Own Id: OTP-14891</p>
+ </item>
+ <item>
+ <p>Fixed a buffer overflow in an internal string
+ formatting function that could be hit if specifying a
+ long floating-point format specifier to
+ <c>erts_sprintf</c> or similar.</p>
+ <p>
+ Own Id: OTP-14920</p>
+ </item>
+ <item>
+ <p><c>erlang:iolist_to_iovec/1</c> and
+ <c>enif_inspect_iovec</c> will no longer fail when
+ provided with binaries that have been matched-out on a
+ non-byte boundary.</p>
+ <p>
+ Own Id: OTP-14921</p>
+ </item>
+ <item>
+ <p><c>iolist_to_binary/1</c> and
+ <c>erlang:iolist_to_iovec/1</c> will now badarg if
+ supplied with a bitstring (without a list).</p>
+ <p>
+ Own Id: OTP-14926</p>
+ </item>
+ <item>
+ <p>
+ Reject loading modules with names containing directory
+ separators ('/' or '\' on Windows).</p>
+ <p>
+ Own Id: OTP-14933 Aux Id: ERL-564, PR-1716 </p>
+ </item>
+ <item>
+ <p>
+ Fix potential dead-lock when the tracer process dies
+ while a the traced process is running on a dirty
+ scheduler.</p>
+ <p>
+ Own Id: OTP-14938</p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ More crash dump info such as: process binary virtual heap
+ stats, full info for process causing out-of-mem during
+ GC, more port related info, and dirty scheduler info.</p>
+ <p>
+ Own Id: OTP-14820</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Erts 9.2.1</title>
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Improve search algorithm of abandoned memory carriers.
+ Instead of limited linear search, each allocator instance
+ maintain a balanced search tree of all its abandoned
+ carriers for faster and more exhaustive search.</p>
+ <p>
+ Own Id: OTP-14915 Aux Id: ERIERL-88 </p>
+ </item>
+ <item>
+ <p>
+ New erts_alloc command line options <c>+M_acnl</c> and
+ <c>+M_acfml</c> to limit carrier abandonment.</p>
+ <p>
+ Own Id: OTP-14916 Aux Id: ERIERL-88 </p>
+ </item>
+ <item>
+ <p>
+ New family of <c>erts_alloc</c> strategies: Age Order
+ First Fit. Similar to "address order", but instead the
+ oldest possible carrier is always chosen for allocation.</p>
+ <p>
+ Own Id: OTP-14917 Aux Id: ERIERL-88 </p>
+ </item>
+ <item>
+ <p>
+ Add possibility to change allocator options at runtime
+ with <c>system_info(erts_alloc, ...)</c>. Only option
+ <c>sbct</c> (single block carrier threshold) is currently
+ supported via this interface.</p>
+ <p>
+ Own Id: OTP-14918 Aux Id: ERIERL-88 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Erts 9.2</title>
<section><title>Fixed Bugs and Malfunctions</title>
@@ -10922,7 +11091,7 @@
<c>update_cpu_info</c> will make the runtime system
reread and update the internally stored CPU information.
For more information see the documentation of <seealso
- marker="erlang#update_cpu_info">erlang:system_info(update_cpu_info)</seealso>.</p>
+ marker="erlang#system_info_update_cpu_info">erlang:system_info(update_cpu_info)</seealso>.</p>
<p>
The CPU topology is now automatically detected on Windows
systems with less than 33 logical processors. The runtime
diff --git a/erts/emulator/Makefile.in b/erts/emulator/Makefile.in
index 9c79bf3da0..92cf40c1ae 100644
--- a/erts/emulator/Makefile.in
+++ b/erts/emulator/Makefile.in
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1996-2017. All Rights Reserved.
+# Copyright Ericsson AB 1996-2018. 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.
@@ -645,7 +645,7 @@ PRELOAD_BEAM = $(ERL_TOP)/erts/preloaded/ebin/otp_ring0.beam \
$(ERL_TOP)/erts/preloaded/ebin/erts_internal.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
+ $(ERL_TOP)/erts/preloaded/ebin/erts_dirty_process_signal_handler.beam
ifeq ($(TARGET),win32)
# On windows the preloaded objects are in a resource object.
@@ -844,7 +844,7 @@ RUN_OBJS += \
$(OBJDIR)/utils.o $(OBJDIR)/bif.o \
$(OBJDIR)/io.o $(OBJDIR)/erl_printf_term.o\
$(OBJDIR)/erl_debug.o $(OBJDIR)/erl_md5.o \
- $(OBJDIR)/erl_message.o \
+ $(OBJDIR)/erl_message.o $(OBJDIR)/erl_proc_sig_queue.o \
$(OBJDIR)/erl_process_dict.o $(OBJDIR)/erl_process_lock.o \
$(OBJDIR)/erl_port_task.o $(OBJDIR)/erl_arith.o \
$(OBJDIR)/time.o $(OBJDIR)/erl_time_sup.o \
@@ -858,11 +858,11 @@ RUN_OBJS += \
$(OBJDIR)/register.o $(OBJDIR)/break.o \
$(OBJDIR)/erl_async.o $(OBJDIR)/erl_lock_check.o \
$(OBJDIR)/erl_gc.o $(OBJDIR)/erl_lock_count.o \
- $(OBJDIR)/erl_posix_str.o \
+ $(OBJDIR)/erl_posix_str.o \
$(OBJDIR)/erl_bits.o $(OBJDIR)/erl_math.o \
$(OBJDIR)/erl_fun.o $(OBJDIR)/erl_bif_port.o \
$(OBJDIR)/erl_term.o $(OBJDIR)/erl_node_tables.o \
- $(OBJDIR)/erl_monitors.o $(OBJDIR)/erl_process_dump.o \
+ $(OBJDIR)/erl_monitor_link.o $(OBJDIR)/erl_process_dump.o \
$(OBJDIR)/erl_hl_timer.o $(OBJDIR)/erl_cpu_topology.o \
$(OBJDIR)/erl_drv_thread.o $(OBJDIR)/erl_bif_chksum.o \
$(OBJDIR)/erl_bif_re.o $(OBJDIR)/erl_unicode.o \
diff --git a/erts/emulator/beam/atom.c b/erts/emulator/beam/atom.c
index bbe1cb3e11..e5b7616a0d 100644
--- a/erts/emulator/beam/atom.c
+++ b/erts/emulator/beam/atom.c
@@ -452,7 +452,7 @@ init_atom_table(void)
/* Ordinary atoms */
for (i = 0; erl_atom_names[i] != 0; i++) {
int ix;
- a.len = strlen(erl_atom_names[i]);
+ a.len = sys_strlen(erl_atom_names[i]);
a.latin1_chars = a.len;
a.name = (byte*)erl_atom_names[i];
a.slot.index = i;
diff --git a/erts/emulator/beam/atom.names b/erts/emulator/beam/atom.names
index cb4fab51f1..cceca66850 100644
--- a/erts/emulator/beam/atom.names
+++ b/erts/emulator/beam/atom.names
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1996-2017. All Rights Reserved.
+# Copyright Ericsson AB 1996-2018. 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.
@@ -48,7 +48,7 @@ atom Empty=''
#
# Used in the Beam emulator loop. (Smaller literals usually means tighter code.)
#
-atom fun infinity timeout normal call return
+atom infinity timeout normal call return
atom throw error exit
atom undefined
@@ -69,11 +69,9 @@ atom DOWN='DOWN'
atom UP='UP'
atom EXIT='EXIT'
atom abort
-atom aborted
atom abs_path
atom absoluteURI
atom ac
-atom accessor
atom active
atom active_tasks
atom active_tasks_all
@@ -88,7 +86,6 @@ atom allocated_areas
atom allocator
atom allocator_sizes
atom alloc_util_allocators
-atom allow_gc
atom allow_passive_connect
atom already_loaded
atom amd64
@@ -109,6 +106,7 @@ atom atom
atom atom_used
atom attributes
atom auto_connect
+atom await_exit
atom await_microstate_accounting_modifications
atom await_port_send_result
atom await_proc_exit
@@ -122,9 +120,7 @@ atom bag
atom band
atom big
atom bif_return_trap
-atom bif_timer_server
atom binary
-atom binary_bin_to_list_trap
atom binary_copy_trap
atom binary_find_trap
atom binary_longest_prefix_trap
@@ -179,7 +175,6 @@ atom convert_time_unit
atom connect
atom connected
atom connection_closed
-atom cons
atom const
atom context_switches
atom control
@@ -199,7 +194,6 @@ atom debug_flags
atom decimals
atom default
atom delay_trap
-atom depth
atom dictionary
atom dirty_bif_exception
atom dirty_bif_result
@@ -221,7 +215,6 @@ atom dist_data
atom Div='/'
atom div
atom dmonitor_node
-atom dmonitor_p
atom DollarDollar='$$'
atom DollarUnderscore='$_'
atom dollar_endonly
@@ -246,15 +239,14 @@ atom Eqeq='=='
atom erl_tracer
atom erlang
atom erl_signal_server
-atom ERROR='ERROR'
atom error_handler
atom error_logger
atom erts_code_purger
atom erts_debug
+atom erts_dflags
atom erts_internal
atom ets
atom ETS_TRANSFER='ETS-TRANSFER'
-atom event
atom exact_reductions
atom exception_from
atom exception_trace
@@ -281,27 +273,20 @@ atom force
atom format_cpu_topology
atom free
atom fullsweep_after
-atom fullsweep_if_old_binaries
-atom fun
-atom function
atom functions
atom function_clause
atom garbage_collecting
atom garbage_collection
atom garbage_collection_info
-atom gc_end
atom gc_major_end
atom gc_major_start
atom gc_max_heap_size
atom gc_minor_end
atom gc_minor_start
-atom gc_start
atom Ge='>='
atom generational
-atom get_data
atom get_seq_token
atom get_tcw
-atom getenv
atom gather_gc_info_result
atom gather_io_bytes
atom gather_microstate_accounting_result
@@ -343,7 +328,6 @@ atom initial_call
atom input
atom internal
atom internal_error
-atom internal_status
atom instruction_counts
atom invalid
atom is_constant
@@ -389,14 +373,12 @@ atom match_spec_result
atom max
atom maximum
atom max_heap_size
-atom max_tables max_processes
atom mbuf_size
atom md5
atom memory
atom memory_internal
atom memory_types
atom message
-atom message_binary
atom message_queue_data
atom message_queue_len
atom messages
@@ -443,21 +425,18 @@ atom new_processes
atom new_ports
atom new_uniq
atom newline
-atom next
atom no
atom nomatch
atom none
atom no_auto_capture
atom noconnect
atom noconnection
-atom nocookie
atom node
atom node_type
atom nodedown
atom nodedown_reason
atom nodeup
atom noeol
-atom nofile
atom noproc
atom normal
atom nosuspend
@@ -479,7 +458,6 @@ atom notempty_atstart
atom notify
atom notsup
atom nouse_stdio
-atom objects
atom off_heap
atom offset
atom ok
@@ -549,7 +527,6 @@ atom re_run_trap
atom read_concurrency
atom ready_input
atom ready_output
-atom ready_async
atom reason
atom receive
atom recent_size
@@ -577,7 +554,8 @@ atom running_procs
atom runtime
atom safe
atom save_calls
-atom scheduler
+atom sbct
+atom scheduler
atom scheduler_id
atom scheduler_wall_time
atom scheduler_wall_time_all
@@ -595,7 +573,6 @@ atom sequential_trace_token
atom serial
atom set
atom set_cpu_topology
-atom set_data
atom set_on_first_link
atom set_on_first_spawn
atom set_on_link
@@ -603,8 +580,6 @@ atom set_on_spawn
atom set_seq_token
atom set_tcw
atom set_tcw_fake
-atom separate
-atom shared
atom sighup
atom sigterm
atom sigusr1
@@ -620,7 +595,6 @@ atom sigtstp
atom sigquit
atom silent
atom size
-atom sl_alloc
atom spawn_executable
atom spawn_driver
atom spawned
@@ -628,7 +602,6 @@ atom ssl_tls
atom stack_size
atom start
atom status
-atom static
atom stderr_to_stdout
atom stop
atom stream
@@ -638,14 +611,11 @@ atom sunrm
atom suspend
atom suspended
atom suspending
-atom sys_misc
atom system
-atom system_error
atom system_flag_scheduler_wall_time
atom system_limit
atom system_version
atom system_architecture
-atom SYSTEM='SYSTEM'
atom table
atom term_to_binary_trap
atom this
@@ -663,7 +633,7 @@ atom total_heap_size
atom total_run_queue_lengths
atom total_run_queue_lengths_all
atom tpkt
-atom trace trace_ts traced
+atom trace traced
atom trace_control_word
atom trace_status
atom tracer
@@ -672,7 +642,6 @@ atom trim
atom trim_all
atom try_clause
atom true
-atom tuple
atom type
atom ucompile
atom ucp
@@ -689,14 +658,11 @@ atom unblock_normal
atom uniq
atom unless_suspending
atom unloaded
-atom unloading
atom unloaded_only
atom unload_cancelled
atom value
-atom values
atom version
atom visible
-atom wait
atom waiting
atom wall_clock
atom warning
@@ -707,3 +673,4 @@ atom xor
atom x86
atom yes
atom yield
+atom nifs
diff --git a/erts/emulator/beam/beam_bif_load.c b/erts/emulator/beam/beam_bif_load.c
index 87367b44ab..5c76aafae7 100644
--- a/erts/emulator/beam/beam_bif_load.c
+++ b/erts/emulator/beam/beam_bif_load.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1999-2017. All Rights Reserved.
+ * Copyright Ericsson AB 1999-2018. 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.
@@ -37,6 +37,7 @@
#include "erl_bits.h"
#include "erl_thr_progress.h"
#include "erl_nfunc_sched.h"
+#include "erl_proc_sig_queue.h"
#ifdef HIPE
# include "hipe_bif0.h"
# define IF_HIPE(X) (X)
@@ -65,7 +66,6 @@ static struct {
Process *erts_code_purger = NULL;
-Process *erts_dirty_process_code_checker;
erts_atomic_t erts_copy_literal_area__;
#define ERTS_SET_COPY_LITERAL_AREA(LA) \
erts_atomic_set_nob(&erts_copy_literal_area__, \
@@ -607,7 +607,9 @@ BIF_RETTYPE erts_internal_check_dirty_process_code_2(BIF_ALIST_2)
int reds = 0;
Eterm res;
- if (BIF_P != erts_dirty_process_code_checker)
+ if (BIF_P != erts_dirty_process_signal_handler
+ && BIF_P != erts_dirty_process_signal_handler_high
+ && BIF_P != erts_dirty_process_signal_handler_max)
BIF_ERROR(BIF_P, EXC_NOTSUP);
if (is_not_internal_pid(BIF_ARG_1))
@@ -901,15 +903,59 @@ static void hfrag_literal_copy(Eterm **hpp, ErlOffHeap *ohp,
Eterm *start, Eterm *end,
char *lit_start, Uint lit_size);
+static ERTS_INLINE void
+msg_copy_literal_area(ErtsMessage *msgp, int *redsp,
+ char *literals, Uint lit_bsize)
+{
+ ErlHeapFragment *hfrag, *hf;
+ Uint lit_sz = 0;
+
+ *redsp += 1;
+
+ if (!ERTS_SIG_IS_INTERNAL_MSG(msgp) || !msgp->data.attached)
+ return;
+
+ if (msgp->data.attached == ERTS_MSG_COMBINED_HFRAG)
+ hfrag = &msgp->hfrag;
+ else
+ hfrag = msgp->data.heap_frag;
+
+ 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;
+ }
+}
+
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;
+ ErtsMessage *mfp;
la = ERTS_COPY_LITERAL_AREA();
if (!la)
@@ -927,46 +973,13 @@ erts_proc_copy_literal_area(Process *c_p, int *redsp, int fcalls, int gc_allowed
*/
erts_proc_lock(c_p, ERTS_PROC_LOCK_MSGQ);
- ERTS_MSGQ_MV_INQ2PRIVQ(c_p);
+ erts_proc_sig_fetch(c_p);
erts_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;
- }
- }
+ ERTS_FOREACH_SIG_PRIVQS(c_p, msgp, msg_copy_literal_area(msgp,
+ redsp,
+ literals,
+ lit_bsize));
if (gc_allowed) {
/*
@@ -1041,8 +1054,8 @@ erts_proc_copy_literal_area(Process *c_p, int *redsp, int fcalls, int gc_allowed
* 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 (mfp = c_p->msg_frag; mfp; mfp = mfp->next) {
+ hfrag = erts_message_to_heap_frag(mfp);
for (; hfrag; hfrag = hfrag->next) {
Eterm *hp, *hp_end;
diff --git a/erts/emulator/beam/beam_debug.c b/erts/emulator/beam/beam_debug.c
index 8f02d509a9..5eb68b817e 100644
--- a/erts/emulator/beam/beam_debug.c
+++ b/erts/emulator/beam/beam_debug.c
@@ -228,11 +228,11 @@ erts_debug_instructions_0(BIF_ALIST_0)
Eterm res = NIL;
for (i = 0; i < num_instructions; i++) {
- needed += 2*strlen(opc[i].name);
+ needed += 2*sys_strlen(opc[i].name);
}
hp = HAlloc(BIF_P, needed);
for (i = num_instructions-1; i >= 0; i--) {
- Eterm s = erts_bld_string_n(&hp, 0, opc[i].name, strlen(opc[i].name));
+ Eterm s = erts_bld_string_n(&hp, 0, opc[i].name, sys_strlen(opc[i].name));
res = erts_bld_cons(&hp, 0, s, res);
}
return res;
@@ -431,7 +431,7 @@ print_op(fmtfn_t to, void *to_arg, int op, int size, BeamInstr* addr)
* the packing program backwards and in reverse.
*/
- prog = start_prog + strlen(start_prog);
+ prog = start_prog + sys_strlen(start_prog);
while (start_prog < prog) {
prog--;
switch (*prog) {
diff --git a/erts/emulator/beam/beam_emu.c b/erts/emulator/beam/beam_emu.c
index ef9abcde08..fb87be3f17 100644
--- a/erts/emulator/beam/beam_emu.c
+++ b/erts/emulator/beam/beam_emu.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1996-2017. All Rights Reserved.
+ * Copyright Ericsson AB 1996-2018. 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.
@@ -44,6 +44,7 @@
#include "hipe_bif1.h"
#endif
#include "dtrace-wrapper.h"
+#include "erl_proc_sig_queue.h"
/* #define HARDDEBUG 1 */
@@ -749,7 +750,7 @@ void process_main(Eterm * x_reg_array, FloatDef* f_reg_array)
dtrace_proc_str(c_p, process_buf);
if (ERTS_PROC_IS_EXITING(c_p)) {
- strcpy(fun_buf, "<exiting>");
+ sys_strcpy(fun_buf, "<exiting>");
} else {
ErtsCodeMFA *cmfa = find_function_from_pc(c_p->i);
if (cmfa) {
@@ -1229,7 +1230,7 @@ void erts_dirty_process_main(ErtsSchedulerData *esdp)
dtrace_proc_str(c_p, process_buf);
if (ERTS_PROC_IS_EXITING(c_p)) {
- strcpy(fun_buf, "<exiting>");
+ sys_strcpy(fun_buf, "<exiting>");
} else {
ErtsCodeMFA *cmfa = find_function_from_pc(c_p->i);
if (cmfa) {
@@ -1454,7 +1455,7 @@ handle_error(Process* c_p, BeamInstr* pc, Eterm* reg, ErtsCodeMFA *bif_mfa)
reg[3] = c_p->ftrace;
if ((new_pc = next_catch(c_p, reg))) {
c_p->cp = 0; /* To avoid keeping stale references. */
- c_p->msg.saved_last = 0; /* No longer safe to use this position */
+ ERTS_RECV_MARK_CLEAR(c_p); /* No longer safe to use this position */
return new_pc;
}
if (c_p->catches > 0) erts_exit(ERTS_ERROR_EXIT, "Catch not found");
@@ -2419,8 +2420,8 @@ erts_hibernate(Process* c_p, Eterm* reg)
* shrink the heap.
*/
erts_proc_lock(c_p, ERTS_PROC_LOCK_MSGQ|ERTS_PROC_LOCK_STATUS);
- ERTS_MSGQ_MV_INQ2PRIVQ(c_p);
- if (!c_p->msg.len) {
+ erts_proc_sig_fetch(c_p);
+ if (!c_p->sig_qs.len) {
erts_proc_unlock(c_p, ERTS_PROC_LOCK_MSGQ|ERTS_PROC_LOCK_STATUS);
c_p->fvalue = NIL;
PROCESS_MAIN_CHK_LOCKS(c_p);
@@ -2428,8 +2429,8 @@ erts_hibernate(Process* c_p, Eterm* reg)
ERTS_VERIFY_UNUSED_TEMP_ALLOC(c_p);
PROCESS_MAIN_CHK_LOCKS(c_p);
erts_proc_lock(c_p, ERTS_PROC_LOCK_MSGQ|ERTS_PROC_LOCK_STATUS);
- ERTS_MSGQ_MV_INQ2PRIVQ(c_p);
- if (!c_p->msg.len)
+ erts_proc_sig_fetch(c_p);
+ if (!c_p->sig_qs.len)
erts_atomic32_read_band_relb(&c_p->state, ~ERTS_PSFLG_ACTIVE);
ASSERT(!ERTS_PROC_IS_EXITING(c_p));
}
diff --git a/erts/emulator/beam/beam_load.c b/erts/emulator/beam/beam_load.c
index e242fe9140..af620d7432 100644
--- a/erts/emulator/beam/beam_load.c
+++ b/erts/emulator/beam/beam_load.c
@@ -459,7 +459,7 @@ typedef struct LoaderState {
#ifdef DEBUG
# define GARBAGE 0xCC
-# define DEBUG_INIT_GENOP(Dst) memset(Dst, GARBAGE, sizeof(GenOp))
+# define DEBUG_INIT_GENOP(Dst) sys_memset(Dst, GARBAGE, sizeof(GenOp))
#else
# define DEBUG_INIT_GENOP(Dst)
#endif
@@ -554,6 +554,7 @@ static Eterm get_module_info(Process* p, ErtsCodeIndex code_ix,
static Eterm exported_from_module(Process* p, ErtsCodeIndex code_ix,
Eterm mod);
static Eterm functions_in_module(Process* p, BeamCodeHeader*);
+static Eterm nifs_in_module(Process* p, Eterm module);
static Eterm attributes_for_module(Process* p, BeamCodeHeader*);
static Eterm compilation_info_for_module(Process* p, BeamCodeHeader*);
static Eterm md5_of_module(Process* p, BeamCodeHeader*);
@@ -1422,7 +1423,7 @@ load_atom_table(LoaderState* stp, ErtsAtomEncoding enc)
Atom* ap;
ap = atom_tab(atom_val(stp->atom[1]));
- memcpy(sbuf, ap->name, ap->len);
+ sys_memcpy(sbuf, ap->name, ap->len);
sbuf[ap->len] = '\0';
LoadError1(stp, "module name in object code is %s", sbuf);
}
@@ -2089,7 +2090,7 @@ load_code(LoaderState* stp)
erts_alloc(ERTS_ALC_T_LOADER_TMP,
(arity+last_op->a[arg].val)
*sizeof(GenOpArg));
- memcpy(last_op->a, last_op->def_args,
+ sys_memcpy(last_op->a, last_op->def_args,
arity*sizeof(GenOpArg));
arity += last_op->a[arg].val;
break;
@@ -4915,7 +4916,9 @@ freeze_code(LoaderState* stp)
line_items[i] = codev + stp->ci - 1;
line_tab->fname_ptr = (Eterm*) &line_items[i + 1];
- memcpy(line_tab->fname_ptr, stp->fname, stp->num_fnames*sizeof(Eterm));
+ if (stp->num_fnames)
+ sys_memcpy(line_tab->fname_ptr, stp->fname,
+ stp->num_fnames*sizeof(Eterm));
line_tab->loc_size = stp->loc_size;
if (stp->loc_size == 2) {
@@ -5498,8 +5501,8 @@ transform_engine(LoaderState* st)
case TOP_store_rest_args:
{
GENOP_ARITY(instr, instr->arity+num_rest_args);
- memcpy(instr->a, instr->def_args, ap*sizeof(GenOpArg));
- memcpy(instr->a+ap, rest_args, num_rest_args*sizeof(GenOpArg));
+ sys_memcpy(instr->a, instr->def_args, ap*sizeof(GenOpArg));
+ sys_memcpy(instr->a+ap, rest_args, num_rest_args*sizeof(GenOpArg));
ap += num_rest_args;
}
break;
@@ -5952,6 +5955,8 @@ get_module_info(Process* p, ErtsCodeIndex code_ix, BeamCodeHeader* code_hdr,
return exported_from_module(p, code_ix, module);
} else if (what == am_functions) {
return functions_in_module(p, code_hdr);
+ } else if (what == am_nifs) {
+ return nifs_in_module(p, module);
} else if (what == am_attributes) {
return attributes_for_module(p, code_hdr);
} else if (what == am_compile) {
@@ -6005,6 +6010,46 @@ functions_in_module(Process* p, /* Process whose heap to use. */
}
/*
+ * Builds a list of all NIFs in the given module:
+ * [{Name, Arity},...]
+ */
+Eterm
+nifs_in_module(Process* p, Eterm module)
+{
+ Eterm nif_list, *hp;
+ Module *mod;
+
+ mod = erts_get_module(module, erts_active_code_ix());
+ nif_list = NIL;
+
+ if (mod->curr.nif != NULL) {
+ int func_count, func_ix;
+ ErlNifFunc *funcs;
+
+ func_count = erts_nif_get_funcs(mod->curr.nif, &funcs);
+ hp = HAlloc(p, func_count * 5);
+
+ for (func_ix = func_count - 1; func_ix >= 0; func_ix--) {
+ Eterm name, arity, pair;
+ ErlNifFunc *func;
+
+ func = &funcs[func_ix];
+
+ name = am_atom_put(func->name, sys_strlen(func->name));
+ arity = make_small(func->arity);
+
+ pair = TUPLE2(hp, name, arity);
+ hp += 3;
+
+ nif_list = CONS(hp, pair, nif_list);
+ hp += 2;
+ }
+ }
+
+ return nif_list;
+}
+
+/*
* Returns 'true' if mod has any native compiled functions, otherwise 'false'
*/
@@ -6447,7 +6492,7 @@ stub_copy_info(LoaderState* stp,
Sint decoded_size;
Uint size = stp->chunks[chunk].size;
if (size != 0) {
- memcpy(info, stp->chunks[chunk].start, size);
+ sys_memcpy(info, stp->chunks[chunk].start, size);
*ptr_word = info;
decoded_size = erts_decode_ext_size(info, size);
if (decoded_size < 0) {
@@ -7017,8 +7062,8 @@ void dbg_set_traced_mfa(const char* m, const char* f, Uint a)
{
unsigned i = dbg_trace_ix++;
ASSERT(i < MFA_MAX);
- dbg_trace_m[i] = am_atom_put(m, strlen(m));
- dbg_trace_f[i] = am_atom_put(f, strlen(f));
+ dbg_trace_m[i] = am_atom_put(m, sys_strlen(m));
+ dbg_trace_f[i] = am_atom_put(f, sys_strlen(f));
dbg_trace_a[i] = a;
}
diff --git a/erts/emulator/beam/bif.c b/erts/emulator/beam/bif.c
index f086c434ea..232597c5b6 100644
--- a/erts/emulator/beam/bif.c
+++ b/erts/emulator/beam/bif.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1996-2017. All Rights Reserved.
+ * Copyright Ericsson AB 1996-2018. 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.
@@ -46,11 +46,12 @@
#include "erl_bif_unique.h"
#include "erl_map.h"
#include "erl_msacc.h"
+#include "erl_proc_sig_queue.h"
Export *erts_await_result;
+static Export await_exit_trap;
static Export* flush_monitor_messages_trap = NULL;
static Export* set_cpu_topology_trap = NULL;
-static Export* await_proc_exit_trap = NULL;
static Export* await_port_send_result_trap = NULL;
Export* erts_format_cpu_topology_trap = NULL;
static Export dsend_continue_trap_export;
@@ -92,83 +93,51 @@ BIF_RETTYPE spawn_3(BIF_ALIST_3)
/* Utility to add a new link between processes p and another internal
* process (rpid). Process p must be the currently executing process.
*/
-static int insert_internal_link(Process* p, Eterm rpid)
-{
- Process *rp;
- ErtsProcLocks rp_locks = ERTS_PROC_LOCK_LINK;
-
- ASSERT(is_internal_pid(rpid));
-
- if (IS_TRACED(p)
- && (ERTS_TRACE_FLAGS(p) & (F_TRACE_SOL|F_TRACE_SOL1))) {
- rp_locks = ERTS_PROC_LOCKS_ALL;
- }
-
- erts_proc_lock(p, ERTS_PROC_LOCK_LINK);
-
- /* get a pointer to the process struct of the linked process */
- rp = erts_pid2proc_opt(p, ERTS_PROC_LOCK_MAIN|ERTS_PROC_LOCK_LINK,
- rpid, rp_locks,
- ERTS_P2P_FLG_ALLOW_OTHER_X);
-
- if (!rp) {
- erts_proc_unlock(p, ERTS_PROC_LOCK_LINK);
- return 0;
- }
-
- if (p != rp) {
- erts_add_link(&ERTS_P_LINKS(p), LINK_PID, rp->common.id);
- erts_add_link(&ERTS_P_LINKS(rp), LINK_PID, p->common.id);
-
- ASSERT(IS_TRACER_VALID(ERTS_TRACER(p)));
-
- if (IS_TRACED(p)) {
- if (ERTS_TRACE_FLAGS(p) & (F_TRACE_SOL|F_TRACE_SOL1)) {
- ERTS_TRACE_FLAGS(rp) |= (ERTS_TRACE_FLAGS(p) & TRACEE_FLAGS);
- erts_tracer_replace(&rp->common, ERTS_TRACER(p));
- if (ERTS_TRACE_FLAGS(p) & F_TRACE_SOL1) { /* maybe override */
- ERTS_TRACE_FLAGS(rp) &= ~(F_TRACE_SOL1 | F_TRACE_SOL);
- ERTS_TRACE_FLAGS(p) &= ~(F_TRACE_SOL1 | F_TRACE_SOL);
- }
- }
- }
- }
- if (IS_TRACED_FL(rp, F_TRACE_PROCS))
- trace_proc(p, p == rp ? rp_locks : ERTS_PROC_LOCK_MAIN|ERTS_PROC_LOCK_LINK,
- rp, am_getting_linked, p->common.id);
-
- if (p == rp)
- erts_proc_unlock(p, rp_locks & ~ERTS_PROC_LOCK_MAIN);
- else {
- erts_proc_unlock(p, ERTS_PROC_LOCK_LINK);
- erts_proc_unlock(rp, rp_locks);
- }
-
- return 1;
-}
-
/* create a link to the process */
BIF_RETTYPE link_1(BIF_ALIST_1)
{
- DistEntry *dep;
-
if (IS_TRACED_FL(BIF_P, F_TRACE_PROCS)) {
trace_proc(BIF_P, ERTS_PROC_LOCK_MAIN, BIF_P, am_link, BIF_ARG_1);
}
/* check that the pid or port which is our argument is OK */
if (is_internal_pid(BIF_ARG_1)) {
- if (insert_internal_link(BIF_P, BIF_ARG_1)) {
- BIF_RET(am_true);
- }
- else {
- goto res_no_proc;
- }
+ int created;
+ ErtsLinkData *ldp;
+ ErtsLink *lnk;
+
+ if (BIF_P->common.id == BIF_ARG_1)
+ BIF_RET(am_true);
+
+ if (!erts_proc_lookup(BIF_ARG_1))
+ goto res_no_proc;
+
+ lnk = erts_link_tree_lookup_create(&ERTS_P_LINKS(BIF_P),
+ &created,
+ ERTS_LNK_TYPE_PROC,
+ BIF_P->common.id,
+ BIF_ARG_1);
+ if (!created)
+ BIF_RET(am_true);
+
+ ldp = erts_link_to_data(lnk);
+
+
+ if (erts_proc_sig_send_link(BIF_P, BIF_ARG_1, &ldp->b))
+ BIF_RET(am_true);
+
+ erts_link_tree_delete(&ERTS_P_LINKS(BIF_P), lnk);
+ erts_link_release_both(ldp);
+ goto res_no_proc;
}
if (is_internal_port(BIF_ARG_1)) {
- int send_link_signal = 0;
+ int created;
+ ErtsLinkData *ldp;
+ ErtsLink *lnk;
+ Eterm ref;
+ Eterm *refp;
Port *prt = erts_port_lookup(BIF_ARG_1,
(erts_port_synchronous_ops
? ERTS_PORT_SFLGS_INVALID_DRIVER_LOOKUP
@@ -177,31 +146,31 @@ BIF_RETTYPE link_1(BIF_ALIST_1)
goto res_no_proc;
}
- erts_proc_lock(BIF_P, ERTS_PROC_LOCK_LINK);
-
- if (erts_add_link(&ERTS_P_LINKS(BIF_P), LINK_PID, BIF_ARG_1) >= 0)
- send_link_signal = 1;
- /* else: already linked */
+ lnk = erts_link_tree_lookup_create(&ERTS_P_LINKS(BIF_P),
+ &created,
+ ERTS_LNK_TYPE_PORT,
+ BIF_P->common.id,
+ BIF_ARG_1);
+ if (!created)
+ BIF_RET(am_true);
- erts_proc_unlock(BIF_P, ERTS_PROC_LOCK_LINK);
+ ldp = erts_link_to_data(lnk);
+ refp = erts_port_synchronous_ops ? &ref : NULL;
- if (send_link_signal) {
- Eterm ref;
- Eterm *refp = erts_port_synchronous_ops ? &ref : NULL;
-
- switch (erts_port_link(BIF_P, prt, BIF_P->common.id, refp)) {
- case ERTS_PORT_OP_DROPPED:
- case ERTS_PORT_OP_BADARG:
- goto res_no_proc;
- case ERTS_PORT_OP_SCHEDULED:
- if (refp) {
- ASSERT(is_internal_ordinary_ref(ref));
- BIF_TRAP3(await_port_send_result_trap, BIF_P, ref, am_true, am_true);
- }
- default:
- break;
- }
- }
+ switch (erts_port_link(BIF_P, prt, &ldp->b, refp)) {
+ case ERTS_PORT_OP_DROPPED:
+ case ERTS_PORT_OP_BADARG:
+ erts_link_tree_delete(&ERTS_P_LINKS(BIF_P), lnk);
+ erts_link_release_both(ldp);
+ goto res_no_proc;
+ case ERTS_PORT_OP_SCHEDULED:
+ if (refp) {
+ ASSERT(is_internal_ordinary_ref(ref));
+ BIF_TRAP3(await_port_send_result_trap, BIF_P, ref, am_true, am_true);
+ }
+ default:
+ break;
+ }
BIF_RET(am_true);
}
else if (is_external_port(BIF_ARG_1)
@@ -210,317 +179,203 @@ BIF_RETTYPE link_1(BIF_ALIST_1)
}
if (is_external_pid(BIF_ARG_1)) {
+ ErtsLinkData *ldp;
+ int created;
+ DistEntry *dep;
+ ErtsLink *lnk;
+ int code;
+ ErtsDSigData dsd;
+
+ dep = external_pid_dist_entry(BIF_ARG_1);
+ if (dep == erts_this_dist_entry)
+ goto res_no_proc;
- erts_proc_lock(BIF_P, ERTS_PROC_LOCK_LINK);
-
- /* We may earn time by checking first that we're not linked already */
- if (erts_lookup_link(ERTS_P_LINKS(BIF_P), BIF_ARG_1) != NULL) {
- erts_proc_unlock(BIF_P, ERTS_PROC_LOCK_LINK);
- BIF_RET(am_true);
- }
- else {
- ErtsLink *lnk;
- int code;
- ErtsDSigData dsd;
- dep = external_pid_dist_entry(BIF_ARG_1);
- if (dep == erts_this_dist_entry) {
- erts_proc_unlock(BIF_P, ERTS_PROC_LOCK_LINK);
- goto res_no_proc;
- }
-
- code = erts_dsig_prepare(&dsd, dep, BIF_P,
- (ERTS_PROC_LOCK_MAIN | ERTS_PROC_LOCK_LINK),
- ERTS_DSP_RLOCK, 0, 1);
- switch (code) {
- case ERTS_DSIG_PREP_NOT_ALIVE:
- case ERTS_DSIG_PREP_NOT_CONNECTED: {
- ErtsProcLocks locks = ERTS_PROC_LOCK_MAIN | ERTS_PROC_LOCK_LINK;
- erts_aint32_t state;
- erts_proc_lock(BIF_P, (ERTS_PROC_LOCKS_ALL & ~locks));
- locks = ERTS_PROC_LOCKS_ALL;
- erts_send_exit_signal(BIF_P, BIF_ARG_1, BIF_P, &locks,
- am_noconnection, NIL, NULL, 0);
- erts_proc_unlock(BIF_P, locks & ERTS_PROC_LOCKS_ALL_MINOR);
-
- /*
- * Copy-paste from old dist_exit_3, not sure if we really
- * need erts_handle_pending_exit when exit_2 does not.
- */
- state = erts_atomic32_read_acqb(&BIF_P->state);
- if (state & (ERTS_PSFLG_EXITING|ERTS_PSFLG_PENDING_EXIT)) {
- if (state & ERTS_PSFLG_PENDING_EXIT)
- erts_handle_pending_exit(BIF_P, ERTS_PROC_LOCK_MAIN);
- ERTS_BIF_EXITED(BIF_P);
- }
- BIF_RET(am_true);
- }
- case ERTS_DSIG_PREP_PENDING:
- case ERTS_DSIG_PREP_CONNECTED:
- /*
- * We have (pending) connection.
- * Setup link and enqueue link signal.
- */
- erts_de_links_lock(dep);
-
- erts_add_link(&ERTS_P_LINKS(BIF_P), LINK_PID, BIF_ARG_1);
- lnk = erts_add_or_lookup_link(&(dep->nlinks),
- LINK_PID,
- BIF_P->common.id);
- ASSERT(lnk != NULL);
- erts_add_link(&ERTS_LINK_ROOT(lnk), LINK_PID, BIF_ARG_1);
-
- erts_de_links_unlock(dep);
- erts_de_runlock(dep);
- erts_proc_unlock(BIF_P, ERTS_PROC_LOCK_LINK);
-
- code = erts_dsig_send_link(&dsd, BIF_P->common.id, BIF_ARG_1);
- if (code == ERTS_DSIG_SEND_YIELD)
- ERTS_BIF_YIELD_RETURN(BIF_P, am_true);
- BIF_RET(am_true);
- default:
- ERTS_ASSERT(! "Invalid dsig prepare result");
- }
- }
+ lnk = erts_link_tree_lookup_create(&ERTS_P_LINKS(BIF_P),
+ &created,
+ ERTS_LNK_TYPE_DIST_PROC,
+ BIF_P->common.id,
+ BIF_ARG_1);
+
+ if (!created)
+ BIF_RET(am_true); /* Already present... */
+
+ ldp = erts_link_to_data(lnk);
+
+ code = erts_dsig_prepare(&dsd, dep, BIF_P,
+ ERTS_PROC_LOCK_MAIN,
+ ERTS_DSP_RLOCK, 0, 1);
+ switch (code) {
+ case ERTS_DSIG_PREP_NOT_ALIVE:
+ case ERTS_DSIG_PREP_NOT_CONNECTED:
+ erts_link_set_dead_dist(&ldp->b, dep->sysname);
+ erts_proc_sig_send_link_exit(NULL, BIF_ARG_1, &ldp->b,
+ am_noconnection, NIL);
+ BIF_RET(am_true);
+
+ case ERTS_DSIG_PREP_PENDING:
+ case ERTS_DSIG_PREP_CONNECTED: {
+ /*
+ * We have (pending) connection.
+ * Setup link and enqueue link signal.
+ */
+#ifdef DEBUG
+ int inserted =
+#endif
+ erts_link_dist_insert(&ldp->b, dep->mld);
+ ASSERT(inserted);
+ erts_de_runlock(dep);
+
+ code = erts_dsig_send_link(&dsd, BIF_P->common.id, BIF_ARG_1);
+ if (code == ERTS_DSIG_SEND_YIELD)
+ ERTS_BIF_YIELD_RETURN(BIF_P, am_true);
+ BIF_RET(am_true);
+ break;
+ }
+ default:
+ ERTS_ASSERT(! "Invalid dsig prepare result");
+ }
}
BIF_ERROR(BIF_P, BADARG);
-res_no_proc: {
- erts_aint32_t state = erts_atomic32_read_nob(&BIF_P->state);
- if (state & ERTS_PSFLG_TRAP_EXIT) {
- ErtsProcLocks locks = ERTS_PROC_LOCK_MAIN;
- erts_deliver_exit_message(BIF_ARG_1, BIF_P, &locks, am_noproc, NIL);
- erts_proc_unlock(BIF_P, ~ERTS_PROC_LOCK_MAIN & locks);
- BIF_RET(am_true);
- }
- else
- BIF_ERROR(BIF_P, EXC_NOPROC);
+res_no_proc:
+ if (BIF_P->flags & F_TRAP_EXIT) {
+ ErtsProcLocks locks = ERTS_PROC_LOCK_MAIN;
+ erts_deliver_exit_message(BIF_ARG_1, BIF_P, &locks, am_noproc, NIL);
+ erts_proc_unlock(BIF_P, ~ERTS_PROC_LOCK_MAIN & locks);
+ BIF_RET(am_true);
+ }
+ else {
+ /*
+ * This behaviour is *really* sad but link/1 has
+ * behaved like this for ages (and this behaviour is
+ * actually documented)... :'-(
+ *
+ * The proper behavior would have been to
+ * send calling process an exit signal..
+ */
+ BIF_ERROR(BIF_P, EXC_NOPROC);
}
}
-/* This function is allowed to return range of values handled by demonitor/1-2
- * Namely: atoms true, false, yield, internal_error, badarg or THE_NON_VALUE
- */
static Eterm
-remote_demonitor(Process *c_p, DistEntry *dep, Eterm ref, Eterm to)
+demonitor(Process *c_p, Eterm ref, Eterm *multip)
{
- ErtsDSigData dsd;
- ErtsMonitor *dmon;
- ErtsMonitor *mon;
- int code;
- Eterm res = am_false;
-
- ERTS_LC_ASSERT((ERTS_PROC_LOCK_MAIN|ERTS_PROC_LOCK_LINK)
- == erts_proc_lc_my_proc_locks(c_p));
-
- code = erts_dsig_prepare(&dsd, dep, c_p, ERTS_PROC_LOCK_MAIN,
- ERTS_DSP_RLOCK, 0, 0);
- switch (code) {
- case ERTS_DSIG_PREP_NOT_ALIVE:
- case ERTS_DSIG_PREP_NOT_CONNECTED:
- /*
- * In the smp case this is possible if the node goes
- * down just before the call to demonitor.
- */
- if (dep) {
- erts_de_links_lock(dep);
- dmon = erts_remove_monitor(&dep->monitors, ref);
- erts_de_links_unlock(dep);
- if (dmon)
- erts_destroy_monitor(dmon);
- }
- mon = erts_remove_monitor(&ERTS_P_MONITORS(c_p), ref);
- erts_proc_unlock(c_p, ERTS_PROC_LOCK_LINK);
-
- res = am_true;
- break;
-
- case ERTS_DSIG_PREP_PENDING:
- case ERTS_DSIG_PREP_CONNECTED:
+ ErtsMonitor *mon; /* The monitor entry to delete */
- erts_de_links_lock(dep);
- mon = erts_remove_monitor(&ERTS_P_MONITORS(c_p), ref);
- dmon = erts_remove_monitor(&dep->monitors, ref);
- erts_de_links_unlock(dep);
- erts_de_runlock(dep);
- erts_proc_unlock(c_p, ERTS_PROC_LOCK_LINK);
+ *multip = am_false;
- if (!dmon) {
- /*
- * This is possible when smp support is enabled.
- * 'DOWN' message just arrived.
- */
- res = am_true;
- }
- else {
- /*
- * Soft (no force) send, use ->data in dist slot
- * monitor list since in case of monitor name
- * the atom is stored there. Yield if necessary.
- */
- code = erts_dsig_send_demonitor(&dsd,
- c_p->common.id,
- (mon->name != NIL
- ? mon->name
- : mon->u.pid),
- ref,
- 0);
- res = (code == ERTS_DSIG_SEND_YIELD ? am_yield : am_true);
- erts_destroy_monitor(dmon);
- }
- break;
- default:
- ERTS_ASSERT(! "Invalid dsig prepare result");
- }
-
-
- /*
- * We aren't allowed to destroy 'mon' until now, since 'to'
- * may refer into 'mon' (external pid).
- */
- ASSERT(mon); /* Since link lock wasn't released between
- lookup and remove */
- erts_destroy_monitor(mon);
-
- ERTS_LC_ASSERT(ERTS_PROC_LOCK_MAIN == erts_proc_lc_my_proc_locks(c_p));
- return res;
-}
+ if (is_not_internal_ref(ref)) {
+ if (is_external_ref(ref)
+ && (erts_this_dist_entry
+ == external_ref_dist_entry(ref))) {
+ return am_false;
+ }
+ return am_badarg; /* Not monitored by this monitor's ref */
+ }
-static ERTS_INLINE void
-demonitor_local_process(Process *c_p, Eterm ref, Eterm to, Eterm *res)
-{
- Process *rp = erts_pid2proc_opt(c_p,
- ERTS_PROC_LOCK_MAIN|ERTS_PROC_LOCK_LINK,
- to,
- ERTS_PROC_LOCK_LINK,
- ERTS_P2P_FLG_ALLOW_OTHER_X);
- ErtsMonitor *mon = erts_remove_monitor(&ERTS_P_MONITORS(c_p), ref);
-
- if (!mon)
- *res = am_false;
- else
- {
- *res = am_true;
- erts_destroy_monitor(mon);
- }
- if (rp) {
- ErtsMonitor *rmon;
- rmon = erts_remove_monitor(&ERTS_P_MONITORS(rp), ref);
- if (rp != c_p)
- erts_proc_unlock(rp, ERTS_PROC_LOCK_LINK);
- if (rmon != NULL)
- erts_destroy_monitor(rmon);
- }
- else {
- ERTS_ASSERT_IS_NOT_EXITING(c_p);
- }
-}
+ mon = erts_monitor_tree_lookup(ERTS_P_MONITORS(c_p), ref);
+ if (!mon)
+ return am_false;
-static ERTS_INLINE BIF_RETTYPE
-demonitor_local_port(Process *origin, Eterm ref, Eterm target)
-{
- BIF_RETTYPE res = am_false;
- Port *port = erts_port_lookup_raw(target);
+ if (!erts_monitor_is_origin(mon))
+ return am_badarg;
- if (!port) {
- BIF_ERROR(origin, BADARG);
- }
- erts_proc_unlock(origin, ERTS_PROC_LOCK_LINK);
+ erts_monitor_tree_delete(&ERTS_P_MONITORS(c_p), mon);
- if (port) {
- Eterm trap_ref;
- switch (erts_port_demonitor(origin, ERTS_PORT_DEMONITOR_NORMAL,
- port, ref, &trap_ref)) {
- case ERTS_PORT_OP_DROPPED:
- case ERTS_PORT_OP_BADARG:
- break;
- case ERTS_PORT_OP_SCHEDULED:
- BIF_TRAP3(await_port_send_result_trap, origin, trap_ref,
- am_busy_port, am_true);
- /* the busy_port atom will never be returned, because it cannot be
- * returned from erts_port_(de)monitor, but just in case if in future
- * internal API changes - you may see this atom */
- default:
- break;
- }
- }
- else {
- ERTS_ASSERT_IS_NOT_EXITING(origin);
- }
- BIF_RET(res);
-}
+ switch (mon->type) {
-/* Can return atom true, false, yield, internal_error, badarg or
- * THE_NON_VALUE if error occurred or trap has been set up
- */
-static
-BIF_RETTYPE demonitor(Process *c_p, Eterm ref, Eterm *multip)
-{
- ErtsMonitor *mon = NULL; /* The monitor entry to delete */
- Eterm to = NIL; /* Monitor link traget */
- DistEntry *dep = NULL; /* Target's distribution entry */
- BIF_RETTYPE res = am_false;
- int unlock_link = 1;
+ case ERTS_MON_TYPE_TIME_OFFSET:
+ *multip = am_true;
+ erts_demonitor_time_offset(mon);
+ return am_true;
+
+ case ERTS_MON_TYPE_PORT: {
+ Port *prt;
+ ASSERT(is_internal_port(mon->other.item));
+ prt = erts_port_lookup(mon->other.item, ERTS_PORT_SFLGS_DEAD);
+ if (!prt || erts_port_demonitor(c_p, prt, mon) == ERTS_PORT_OP_DROPPED)
+ erts_monitor_release(mon);
+ return am_true;
+ }
- erts_proc_lock(c_p, ERTS_PROC_LOCK_LINK);
+ case ERTS_MON_TYPE_PROC:
+ erts_proc_sig_send_demonitor(mon);
+ return am_true;
- if (is_not_internal_ref(ref)) {
- res = am_badarg;
- goto done; /* Cannot be this monitor's ref */
- }
+ case ERTS_MON_TYPE_DIST_PROC: {
+ ErtsMonitorData *mdp = erts_monitor_to_data(mon);
+ Eterm to = mon->other.item;
+ DistEntry *dep;
+ int code = ERTS_DSIG_SEND_OK;
+ int deleted;
+ ErtsDSigData dsd;
- mon = erts_lookup_monitor(ERTS_P_MONITORS(c_p), ref);
- if (!mon) {
- goto done;
- }
+ ASSERT(is_external_pid(to) || is_node_name_atom(to));
- switch (mon->type) {
- case MON_TIME_OFFSET:
- *multip = am_true;
- erts_demonitor_time_offset(ref);
- res = am_true;
- break;
- case MON_ORIGIN:
- to = mon->u.pid;
- *multip = am_false;
- if (is_atom(to)) {
+ if (is_external_pid(to))
+ dep = external_pid_dist_entry(to);
+ else {
/* Monitoring a name at node to */
- ASSERT(is_node_name_atom(to));
dep = erts_sysname_to_connected_dist_entry(to);
ASSERT(dep != erts_this_dist_entry);
- } else if (is_port(to)) {
- if (port_dist_entry(to) != erts_this_dist_entry) {
- goto badarg;
+ if (!dep) {
+ erts_monitor_release(mon);
+ return am_false;
}
- res = demonitor_local_port(c_p, ref, to);
- unlock_link = 0;
- goto done;
- } else {
- ASSERT(is_pid(to));
- dep = pid_dist_entry(to);
}
- if (dep != erts_this_dist_entry) {
- res = remote_demonitor(c_p, dep, ref, to);
- /* remote_demonitor() unlocks link lock on c_p */
- unlock_link = 0;
+
+ code = erts_dsig_prepare(&dsd, dep, c_p, ERTS_PROC_LOCK_MAIN,
+ ERTS_DSP_RLOCK, 0, 0);
+
+ deleted = erts_monitor_dist_delete(&mdp->target);
+
+ switch (code) {
+ case ERTS_DSIG_PREP_NOT_ALIVE:
+ case ERTS_DSIG_PREP_NOT_CONNECTED:
+ /*
+ * In the smp case this is possible if the node goes
+ * down just before the call to demonitor.
+ */
+ break;
+
+ case ERTS_DSIG_PREP_PENDING:
+ case ERTS_DSIG_PREP_CONNECTED: {
+ Eterm watched;
+
+ erts_de_runlock(dep);
+
+ if (mon->flags & ERTS_ML_FLG_NAME)
+ watched = ((ErtsMonitorDataExtended *) mdp)->u.name;
+ else
+ watched = to;
+
+ /*
+ * Soft (no force) send, use ->data in dist slot
+ * monitor list since in case of monitor name
+ * the atom is stored there. Yield if necessary.
+ */
+ code = erts_dsig_send_demonitor(&dsd, c_p->common.id,
+ watched, mdp->ref, 0);
+ break;
}
- else { /* Local monitor */
- demonitor_local_process(c_p, ref, to, &res);
+
+ default:
+ ERTS_INTERNAL_ERROR("invalid result from erts_dsig_prepare()");
+ break;
}
- break;
- default /* case */ :
-badarg:
- res = am_badarg; /* will be converted to error by caller */
- *multip = am_false;
- break;
- }
-done:
- if (unlock_link)
- erts_proc_unlock(c_p, ERTS_PROC_LOCK_LINK);
+ if (deleted)
+ erts_monitor_release(&mdp->target);
+
+ erts_monitor_release(mon);
+ return code == ERTS_DSIG_SEND_YIELD ? am_yield : am_true;
+ }
- ERTS_LC_ASSERT(ERTS_PROC_LOCK_MAIN == erts_proc_lc_my_proc_locks(c_p));
- BIF_RET(res);
+ default:
+ ERTS_INTERNAL_ERROR("Unexpected monitor type");
+ return am_false;
+ }
}
BIF_RETTYPE demonitor_1(BIF_ALIST_1)
@@ -528,25 +383,23 @@ BIF_RETTYPE demonitor_1(BIF_ALIST_1)
Eterm multi;
switch (demonitor(BIF_P, BIF_ARG_1, &multi)) {
case am_false:
- case am_true: BIF_RET(am_true);
- case THE_NON_VALUE: BIF_RET(THE_NON_VALUE);
- case am_yield: ERTS_BIF_YIELD_RETURN(BIF_P, am_true);
- case am_badarg: BIF_ERROR(BIF_P, BADARG);
-
- case am_internal_error:
+ case am_true:
+ BIF_RET(am_true);
+ case am_yield:
+ ERTS_BIF_YIELD_RETURN(BIF_P, am_true);
+ case am_badarg:
default:
- ASSERT(! "demonitor(): internal error");
- BIF_ERROR(BIF_P, EXC_INTERNAL_ERROR);
+ BIF_ERROR(BIF_P, BADARG);
}
}
BIF_RETTYPE demonitor_2(BIF_ALIST_2)
{
- BIF_RETTYPE res = am_true;
- Eterm multi = am_false;
- int info = 0;
- int flush = 0;
- Eterm list = BIF_ARG_2;
+ BIF_RETTYPE res;
+ Eterm multi = am_false;
+ int info = 0;
+ int flush = 0;
+ Eterm list = BIF_ARG_2;
while (is_list(list)) {
Eterm* consp = list_val(list);
@@ -566,10 +419,9 @@ BIF_RETTYPE demonitor_2(BIF_ALIST_2)
if (is_not_nil(list))
goto badarg;
+ res = am_true;
switch (demonitor(BIF_P, BIF_ARG_1, &multi)) {
- case THE_NON_VALUE:
- /* If other error occurred or trap has been set up - pass through */
- BIF_RET(THE_NON_VALUE);
+
case am_false:
if (info)
res = am_false;
@@ -578,20 +430,29 @@ flush_messages:
BIF_TRAP3(flush_monitor_messages_trap, BIF_P,
BIF_ARG_1, multi, res);
}
+ /* Fall through... */
+
case am_true:
if (multi == am_true && flush)
goto flush_messages;
BIF_RET(res);
+
case am_yield:
- ERTS_BIF_YIELD_RETURN(BIF_P, am_true);
+ /* return true after yield... */
+ if (flush) {
+ ERTS_VBUMP_ALL_REDS(BIF_P);
+ goto flush_messages;
+ }
+ ERTS_BIF_YIELD_RETURN(BIF_P, am_true);
+
case am_badarg:
-badarg:
- BIF_ERROR(BIF_P, BADARG);
- case am_internal_error:
default:
- ASSERT(! "demonitor(): internal error");
- BIF_ERROR(BIF_P, EXC_INTERNAL_ERROR);
+ break;
+
}
+
+badarg:
+ BIF_ERROR(BIF_P, BADARG);
}
/* Type must be atomic object! */
@@ -631,292 +492,212 @@ erts_queue_monitor_message(Process *p,
erts_queue_message(p, *p_locksp, msgp, tup, am_system);
}
-static Eterm
-local_pid_monitor(Process *p, Eterm target, Eterm mon_ref, int boolean)
+BIF_RETTYPE monitor_2(BIF_ALIST_2)
{
- Eterm ret = mon_ref;
- Process *rp;
- ErtsProcLocks p_locks = ERTS_PROC_LOCK_MAIN|ERTS_PROC_LOCK_LINK;
+ Eterm target = BIF_ARG_2;
+ Eterm tmp_heap[3];
+ Eterm ref, id, name;
+ ErtsMonitorData *mdp;
+
+ if (BIF_ARG_1 == am_process) {
+ DistEntry *dep;
+ int byname;
+
+ if (is_internal_pid(target)) {
+ name = NIL;
+ id = target;
+
+ local_process:
+
+ ref = erts_make_ref(BIF_P);
+ if (id != BIF_P->common.id) {
+ mdp = erts_monitor_create(ERTS_MON_TYPE_PROC,
+ ref, BIF_P->common.id,
+ id, name);
+ erts_monitor_tree_insert(&ERTS_P_MONITORS(BIF_P),
+ &mdp->origin);
+
+ if (!erts_proc_sig_send_monitor(&mdp->target, id))
+ erts_proc_sig_send_monitor_down(&mdp->target,
+ am_noproc);
+ }
+ BIF_RET(ref);
+ }
- if (target == p->common.id) {
- return ret;
- }
+ if (is_atom(target)) {
+ local_named_process:
+ name = target;
+ id = erts_whereis_name_to_id(BIF_P, target);
+ if (is_internal_pid(id))
+ goto local_process;
+ target = TUPLE2(&tmp_heap[0], name,
+ erts_this_dist_entry->sysname);
+ goto noproc;
+ }
- erts_proc_lock(p, ERTS_PROC_LOCK_LINK);
- rp = erts_pid2proc_opt(p, p_locks,
- target, ERTS_PROC_LOCK_LINK,
- ERTS_P2P_FLG_ALLOW_OTHER_X);
- if (!rp) {
- erts_proc_unlock(p, ERTS_PROC_LOCK_LINK);
- p_locks &= ~ERTS_PROC_LOCK_LINK;
- if (boolean)
- ret = am_false;
- else
- erts_queue_monitor_message(p, &p_locks,
- mon_ref, am_process, target, am_noproc);
- }
- else {
- ASSERT(rp != p);
+ if (is_external_pid(target)) {
+ ErtsDSigData dsd;
+ int code;
+
+ dep = external_pid_dist_entry(target);
+ if (dep == erts_this_dist_entry)
+ goto noproc;
+
+ id = target;
+ name = NIL;
+ byname = 0;
+
+ remote_process:
+
+ ref = erts_make_ref(BIF_P);
+ mdp = erts_monitor_create(ERTS_MON_TYPE_DIST_PROC, ref,
+ BIF_P->common.id, id, name);
+ erts_monitor_tree_insert(&ERTS_P_MONITORS(BIF_P), &mdp->origin);
+
+ code = erts_dsig_prepare(&dsd, dep,
+ BIF_P, ERTS_PROC_LOCK_MAIN,
+ ERTS_DSP_RLOCK, 0, 1);
+ switch (code) {
+ case ERTS_DSIG_PREP_NOT_ALIVE:
+ case ERTS_DSIG_PREP_NOT_CONNECTED:
+ erts_monitor_set_dead_dist(&mdp->target, dep->sysname);
+ erts_proc_sig_send_monitor_down(&mdp->target, am_noconnection);
+ code = ERTS_DSIG_SEND_OK;
+ break;
- if (boolean)
- ret = am_true;
+ case ERTS_DSIG_PREP_PENDING:
+ case ERTS_DSIG_PREP_CONNECTED: {
+#ifdef DEBUG
+ int inserted =
+#endif
- erts_add_monitor(&ERTS_P_MONITORS(p), MON_ORIGIN, mon_ref, target, NIL);
- erts_add_monitor(&ERTS_P_MONITORS(rp), MON_TARGET, mon_ref, p->common.id, NIL);
+ erts_monitor_dist_insert(&mdp->target, dep->mld);
+ ASSERT(inserted);
+ erts_de_runlock(dep);
- erts_proc_unlock(rp, ERTS_PROC_LOCK_LINK);
- }
-
- erts_proc_unlock(p, p_locks & ~ERTS_PROC_LOCK_MAIN);
+ code = erts_dsig_send_monitor(&dsd, BIF_P->common.id, target, ref);
+ break;
+ }
- return ret;
-}
+ default:
+ ERTS_ASSERT(! "Invalid dsig prepare result");
+ code = ERTS_DSIG_SEND_OK;
+ break;
+ }
-static BIF_RETTYPE
-local_port_monitor(Process *origin, Eterm target)
-{
- BIF_RETTYPE ref = erts_make_ref(origin);
- Port *port = erts_sig_lookup_port(origin, target);
- ErtsProcLocks p_locks = ERTS_PROC_LOCK_MAIN;
+ if (byname)
+ erts_deref_dist_entry(dep);
- if (!port) {
-res_no_proc:
- /* Send the DOWN message immediately. Ref is made on the fly because
- * caller has never seen it yet. */
- erts_queue_monitor_message(origin, &p_locks, ref,
- am_port, target, am_noproc);
- }
- else {
- switch (erts_port_monitor(origin, port, target, &ref)) {
- case ERTS_PORT_OP_DROPPED:
- case ERTS_PORT_OP_BADARG:
- goto res_no_proc;
- case ERTS_PORT_OP_SCHEDULED:
- BIF_TRAP3(await_port_send_result_trap, origin, ref,
- am_busy_port, ref);
- /* the busy_port atom will never be returned, because it cannot be
- * returned from erts_port_monitor, but just in case if in future
- * internal API changes - you may see this atom */
- default:
- break;
+ if (code == ERTS_DSIG_SEND_YIELD)
+ ERTS_BIF_YIELD_RETURN(BIF_P, ref);
+ BIF_RET(ref);
}
- }
- erts_proc_unlock(origin, p_locks & ~ERTS_PROC_LOCK_MAIN);
- BIF_RET(ref);
-}
-/* Type = process | port :: atom(), 1st argument passed to erlang:monitor/2
- */
-static BIF_RETTYPE
-local_name_monitor(Process *self, Eterm type, Eterm target_name)
-{
- BIF_RETTYPE ret = erts_make_ref(self);
+ if (is_tuple(target)) {
+ Eterm *tpl = tuple_val(target);
+ if (arityval(tpl[0]) != 2)
+ goto badarg;
+ if (is_not_atom(tpl[1]) || is_not_atom(tpl[2]))
+ goto badarg;
+ if (!erts_is_alive && tpl[2] != am_Noname)
+ goto badarg;
+ target = tpl[1];
+ dep = erts_find_or_insert_dist_entry(tpl[2]);
+ if (dep == erts_this_dist_entry) {
+ erts_deref_dist_entry(dep);
+ goto local_named_process;
+ }
- ErtsProcLocks p_locks = ERTS_PROC_LOCK_MAIN | ERTS_PROC_LOCK_LINK;
- Process *proc = NULL;
- Port *port = NULL;
+ id = dep->sysname;
+ name = target;
+ byname = 1;
+ goto remote_process;
+ }
- erts_proc_lock(self, ERTS_PROC_LOCK_LINK);
+ /* badarg... */
+ }
+ else if (BIF_ARG_1 == am_port) {
+
+ if (is_internal_port(target)) {
+ Port *prt;
+ name = NIL;
+ id = target;
+ local_port:
+ ref = erts_make_ref(BIF_P);
+ mdp = erts_monitor_create(ERTS_MON_TYPE_PORT, ref,
+ BIF_P->common.id, id, name);
+ erts_monitor_tree_insert(&ERTS_P_MONITORS(BIF_P), &mdp->origin);
+ prt = erts_port_lookup(id, ERTS_PORT_SFLGS_INVALID_LOOKUP);
+ if (!prt || erts_port_monitor(BIF_P, prt, &mdp->target) == ERTS_PORT_OP_DROPPED)
+ erts_proc_sig_send_monitor_down(&mdp->target, am_noproc);
+ BIF_RET(ref);
+ }
- erts_whereis_name(self, p_locks, target_name,
- &proc, ERTS_PROC_LOCK_LINK,
- ERTS_P2P_FLG_ALLOW_OTHER_X,
- &port, 0);
+ if (is_atom(target)) {
+ local_named_port:
+ name = target;
+ id = erts_whereis_name_to_id(BIF_P, target);
+ if (is_internal_port(id))
+ goto local_port;
+ target = TUPLE2(&tmp_heap[0], name,
+ erts_this_dist_entry->sysname);
+ goto noproc;
+ }
- /* If the name is not registered,
- * or if we asked for proc and got a port,
- * or if we asked for port and got a proc,
- * we just send the 'DOWN' message.
- */
- if ((!proc && !port) ||
- (type == am_process && port) ||
- (type == am_port && proc)) {
- DeclareTmpHeap(lhp,3,self);
- Eterm item;
- UseTmpHeap(3,self);
-
- erts_proc_unlock(self, ERTS_PROC_LOCK_LINK);
- p_locks &= ~ERTS_PROC_LOCK_LINK;
-
- item = TUPLE2(lhp, target_name, erts_this_dist_entry->sysname);
- erts_queue_monitor_message(self, &p_locks,
- ret,
- type, /* = process|port :: atom() */
- item, am_noproc);
- UnUseTmpHeap(3,self);
- }
- else if (port) {
- erts_proc_unlock(self, p_locks & ~ERTS_PROC_LOCK_MAIN);
- p_locks &= ~ERTS_PROC_LOCK_MAIN;
-
- switch (erts_port_monitor(self, port, target_name, &ret)) {
- case ERTS_PORT_OP_DONE:
- return ret;
- case ERTS_PORT_OP_SCHEDULED: { /* Scheduled a signal */
- ASSERT(is_internal_ordinary_ref(ret));
- BIF_TRAP3(await_port_send_result_trap, self,
- ret, am_true, ret);
- /* bif_trap returns */
- } break;
- default:
+ if (is_external_port(target)) {
+ if (erts_this_dist_entry == external_port_dist_entry(target))
+ goto noproc;
goto badarg;
}
- }
- else if (proc != self) {
- erts_add_monitor(&ERTS_P_MONITORS(self), MON_ORIGIN, ret,
- proc->common.id, target_name);
- erts_add_monitor(&ERTS_P_MONITORS(proc), MON_TARGET, ret,
- self->common.id, target_name);
- erts_proc_unlock(proc, ERTS_PROC_LOCK_LINK);
- }
-
- if (p_locks) {
- erts_proc_unlock(self, p_locks & ~ERTS_PROC_LOCK_MAIN);
- }
- BIF_RET(ret);
-badarg:
- if (p_locks) {
- erts_proc_unlock(self, p_locks & ~ERTS_PROC_LOCK_MAIN);
- }
- BIF_ERROR(self, BADARG);
-}
-
-static BIF_RETTYPE
-remote_monitor(Process *p, Eterm bifarg1, Eterm bifarg2,
- DistEntry *dep, Eterm target, int byname)
-{
- ErtsDSigData dsd;
- BIF_RETTYPE ret;
- int code;
- ASSERT(dep);
- erts_proc_lock(p, ERTS_PROC_LOCK_LINK);
- code = erts_dsig_prepare(&dsd, dep,
- p, (ERTS_PROC_LOCK_MAIN | ERTS_PROC_LOCK_LINK),
- ERTS_DSP_RLOCK, 0, 1);
- switch (code) {
- case ERTS_DSIG_PREP_NOT_ALIVE:
- case ERTS_DSIG_PREP_NOT_CONNECTED:
- erts_proc_unlock(p, ERTS_PROC_LOCK_LINK);
- ERTS_BIF_PREP_TRAP2(ret, dmonitor_p_trap, p, bifarg1, bifarg2);
- break;
- case ERTS_DSIG_PREP_PENDING:
- case ERTS_DSIG_PREP_CONNECTED:
- {
- Eterm p_trgt, p_name, d_name, mon_ref;
+ if (is_tuple(target)) {
+ Eterm *tpl = tuple_val(target);
+ if (arityval(tpl[0]) != 2)
+ goto badarg;
+ if (is_not_atom(tpl[1]) || is_not_atom(tpl[2]))
+ goto badarg;
+ if (tpl[2] == erts_this_dist_entry->sysname) {
+ target = tpl[1];
+ goto local_named_port;
+ }
+ }
- mon_ref = erts_make_ref(p);
+ /* badarg... */
+ }
+ else if (BIF_ARG_1 == am_time_offset) {
- if (byname) {
- p_trgt = dep->sysname;
- p_name = target;
- d_name = target;
- }
- else {
- p_trgt = target;
- p_name = NIL;
- d_name = NIL;
- }
+ if (target != am_clock_service)
+ goto badarg;
+ ref = erts_make_ref(BIF_P);
+ mdp = erts_monitor_create(ERTS_MON_TYPE_TIME_OFFSET,
+ ref, BIF_P->common.id,
+ am_clock_service, NIL);
+ erts_monitor_tree_insert(&ERTS_P_MONITORS(BIF_P), &mdp->origin);
- erts_de_links_lock(dep);
+ erts_monitor_time_offset(&mdp->target);
- erts_add_monitor(&ERTS_P_MONITORS(p), MON_ORIGIN, mon_ref, p_trgt,
- p_name);
- erts_add_monitor(&(dep->monitors), MON_TARGET, mon_ref, p->common.id,
- d_name);
+ BIF_RET(ref);
+ }
- erts_de_links_unlock(dep);
- erts_de_runlock(dep);
- erts_proc_unlock(p, ERTS_PROC_LOCK_LINK);
+badarg:
- code = erts_dsig_send_monitor(&dsd, p->common.id, target, mon_ref);
- if (code == ERTS_DSIG_SEND_YIELD)
- ERTS_BIF_PREP_YIELD_RETURN(ret, p, mon_ref);
- else
- ERTS_BIF_PREP_RET(ret, mon_ref);
- }
- break;
- default:
- ERTS_ASSERT(! "Invalid dsig prepare result");
- }
+ BIF_ERROR(BIF_P, BADARG);
- BIF_RET(ret);
-}
-
-BIF_RETTYPE monitor_2(BIF_ALIST_2)
-{
- Eterm target = BIF_ARG_2;
- BIF_RETTYPE ret;
- DistEntry *dep = NULL;
+noproc: {
+ ErtsProcLocks locks = ERTS_PROC_LOCK_MAIN;
- /* Only process monitors are implemented */
- switch (BIF_ARG_1) {
- case am_time_offset: {
- Eterm ref;
- if (BIF_ARG_2 != am_clock_service) {
- goto badarg;
- }
- ref = erts_make_ref(BIF_P);
- erts_proc_lock(BIF_P, ERTS_PROC_LOCK_LINK);
- erts_add_monitor(&ERTS_P_MONITORS(BIF_P), MON_TIME_OFFSET,
- ref, am_clock_service, NIL);
- erts_proc_unlock(BIF_P, ERTS_PROC_LOCK_LINK);
- erts_monitor_time_offset(BIF_P->common.id, ref);
- BIF_RET(ref);
- }
- case am_process:
- case am_port:
- break;
- default:
- goto badarg;
- }
+ ref = erts_make_ref(BIF_P);
+ erts_queue_monitor_message(BIF_P,
+ &locks,
+ ref,
+ BIF_ARG_1,
+ target,
+ am_noproc);
+ if (locks != ERTS_PROC_LOCK_MAIN)
+ erts_proc_unlock(BIF_P, locks & ~ERTS_PROC_LOCK_MAIN);
- if (is_internal_pid(target) && BIF_ARG_1 == am_process) {
-local_pid:
- ret = local_pid_monitor(BIF_P, target, erts_make_ref(BIF_P), 0);
- } else if (is_external_pid(target) && BIF_ARG_1 == am_process) {
- dep = external_pid_dist_entry(target);
- if (dep == erts_this_dist_entry)
- goto local_pid;
- ret = remote_monitor(BIF_P, BIF_ARG_1, BIF_ARG_2, dep, target, 0);
- } else if (is_internal_port(target) && BIF_ARG_1 == am_port) {
-local_port:
- ret = local_port_monitor(BIF_P, target);
- } else if (is_external_port(target) && BIF_ARG_1 == am_port) {
- dep = external_port_dist_entry(target);
- if (dep == erts_this_dist_entry) {
- goto local_port;
- }
- goto badarg; /* No want remote port */
- } else if (is_atom(target)) {
- ret = local_name_monitor(BIF_P, BIF_ARG_1, target);
- } else if (is_tuple(target)) {
- Eterm *tp = tuple_val(target);
- Eterm remote_node;
- Eterm name;
- if (arityval(*tp) != 2) {
- goto badarg;
- }
- remote_node = tp[2];
- name = tp[1];
- if (!is_atom(remote_node) || !is_atom(name)) {
- goto badarg;
- }
- if (!erts_is_alive && remote_node != am_Noname) {
- goto badarg; /* Remote monitor from (this) undistributed node */
- }
- dep = erts_find_or_insert_dist_entry(remote_node);
- if (dep == erts_this_dist_entry) {
- ret = local_name_monitor(BIF_P, BIF_ARG_1, name);
- } else {
- ret = remote_monitor(BIF_P, BIF_ARG_1, BIF_ARG_2, dep, name, 1);
- }
- erts_deref_dist_entry(dep);
- } else {
-badarg:
- ERTS_BIF_PREP_ERROR(ret, BIF_P, BADARG);
+ BIF_RET(ref);
}
- return ret;
}
/**********************************************************************/
@@ -1089,91 +870,74 @@ BIF_RETTYPE spawn_opt_1(BIF_ALIST_1)
/* remove a link from a process */
BIF_RETTYPE unlink_1(BIF_ALIST_1)
{
- Process *rp;
- DistEntry *dep;
- ErtsLink *l = NULL, *rl = NULL;
- ErtsProcLocks cp_locks = ERTS_PROC_LOCK_MAIN;
-
- /*
- * SMP specific note concerning incoming exit signals:
- * We have to have at least the status lock during removal of
- * the link half on current process, and check for and handle
- * a present pending exit while the status lock is held. This
- * in order to ensure that we wont be exited by a link after
- * it has been removed.
- *
- * (We also have to have the link lock, of course, in order to
- * be allowed to remove the link...)
- */
-
if (IS_TRACED_FL(BIF_P, F_TRACE_PROCS)) {
- trace_proc(BIF_P, cp_locks, BIF_P, am_unlink, BIF_ARG_1);
+ trace_proc(BIF_P, ERTS_PROC_LOCK_MAIN,
+ BIF_P, am_unlink, BIF_ARG_1);
}
- if (is_internal_port(BIF_ARG_1)) {
- erts_proc_lock(BIF_P, ERTS_PROC_LOCK_LINK|ERTS_PROC_LOCK_STATUS);
- if (ERTS_PROC_PENDING_EXIT(BIF_P))
- goto handle_pending_exit;
-
- l = erts_remove_link(&ERTS_P_LINKS(BIF_P), BIF_ARG_1);
+ if (is_internal_pid(BIF_ARG_1)) {
+ ErtsLink *lnk = erts_link_tree_lookup(ERTS_P_LINKS(BIF_P), BIF_ARG_1);
+ if (lnk) {
+ erts_link_tree_delete(&ERTS_P_LINKS(BIF_P), lnk);
+ erts_proc_sig_send_unlink(BIF_P, lnk);
+ }
+ BIF_RET(am_true);
+ }
- erts_proc_unlock(BIF_P, ERTS_PROC_LOCK_LINK|ERTS_PROC_LOCK_STATUS);
+ if (is_internal_port(BIF_ARG_1)) {
+ ErtsLink *lnk = erts_link_tree_lookup(ERTS_P_LINKS(BIF_P), BIF_ARG_1);
- if (l) {
+ if (lnk) {
+ Eterm ref;
+ Eterm *refp = erts_port_synchronous_ops ? &ref : NULL;
+ ErtsPortOpResult res = ERTS_PORT_OP_DROPPED;
Port *prt;
- erts_destroy_link(l);
+ erts_link_tree_delete(&ERTS_P_LINKS(BIF_P), lnk);
/* Send unlink signal */
prt = erts_port_lookup(BIF_ARG_1, ERTS_PORT_SFLGS_DEAD);
if (prt) {
- ErtsPortOpResult res;
- Eterm ref;
- Eterm *refp = erts_port_synchronous_ops ? &ref : NULL;
#ifdef DEBUG
ref = NIL;
#endif
- res = erts_port_unlink(BIF_P, prt, BIF_P->common.id, refp);
+ res = erts_port_unlink(BIF_P, prt, lnk, refp);
- if (refp && res == ERTS_PORT_OP_SCHEDULED) {
- ASSERT(is_internal_ordinary_ref(ref));
- BIF_TRAP3(await_port_send_result_trap, BIF_P, ref, am_true, am_true);
- }
}
+
+ if (res == ERTS_PORT_OP_DROPPED)
+ erts_link_release(lnk);
+ else if (refp && res == ERTS_PORT_OP_SCHEDULED) {
+ ASSERT(is_internal_ordinary_ref(ref));
+ BIF_TRAP3(await_port_send_result_trap, BIF_P, ref, am_true, am_true);
+ }
}
BIF_RET(am_true);
}
- else if (is_external_port(BIF_ARG_1)
- && external_port_dist_entry(BIF_ARG_1) == erts_this_dist_entry) {
- BIF_RET(am_true);
- }
-
- if (is_not_pid(BIF_ARG_1))
- BIF_ERROR(BIF_P, BADARG);
if (is_external_pid(BIF_ARG_1)) {
- ErtsDistLinkData dld;
+ ErtsLink *lnk, *dlnk;
+ ErtsLinkData *ldp;
+ DistEntry *dep;
int code;
ErtsDSigData dsd;
- /* Blind removal, we might have trapped or anything, this leaves
- us in a state where monitors might be inconsistent, but the dist
- code should take care of it. */
- erts_proc_lock(BIF_P, ERTS_PROC_LOCK_LINK|ERTS_PROC_LOCK_STATUS);
- if (ERTS_PROC_PENDING_EXIT(BIF_P))
- goto handle_pending_exit;
- l = erts_remove_link(&ERTS_P_LINKS(BIF_P), BIF_ARG_1);
-
- erts_proc_unlock(BIF_P,
- ERTS_PROC_LOCK_LINK|ERTS_PROC_LOCK_STATUS);
-
- if (l)
- erts_destroy_link(l);
dep = external_pid_dist_entry(BIF_ARG_1);
- if (dep == erts_this_dist_entry) {
+ if (dep == erts_this_dist_entry)
BIF_RET(am_true);
- }
+
+ lnk = erts_link_tree_lookup(ERTS_P_LINKS(BIF_P), BIF_ARG_1);
+ if (!lnk)
+ BIF_RET(am_true);
+
+ erts_link_tree_delete(&ERTS_P_LINKS(BIF_P), lnk);
+ dlnk = erts_link_to_other(lnk, &ldp);
+
+ if (erts_link_dist_delete(dlnk))
+ erts_link_release_both(ldp);
+ else
+ erts_link_release(lnk);
code = erts_dsig_prepare(&dsd, dep, BIF_P, ERTS_PROC_LOCK_MAIN,
ERTS_DSP_NO_LOCK, 0, 0);
@@ -1181,74 +945,27 @@ BIF_RETTYPE unlink_1(BIF_ALIST_1)
case ERTS_DSIG_PREP_NOT_ALIVE:
case ERTS_DSIG_PREP_NOT_CONNECTED:
BIF_RET(am_true);
-
case ERTS_DSIG_PREP_PENDING:
case ERTS_DSIG_PREP_CONNECTED:
- erts_remove_dist_link(&dld, BIF_P->common.id, BIF_ARG_1, dep);
code = erts_dsig_send_unlink(&dsd, BIF_P->common.id, BIF_ARG_1);
- erts_destroy_dist_link(&dld);
if (code == ERTS_DSIG_SEND_YIELD)
ERTS_BIF_YIELD_RETURN(BIF_P, am_true);
- BIF_RET(am_true);
-
+ break;
default:
ASSERT(! "Invalid dsig prepare result");
BIF_ERROR(BIF_P, EXC_INTERNAL_ERROR);
}
- }
-
- /* Internal pid... */
-
- erts_proc_lock(BIF_P, ERTS_PROC_LOCK_LINK|ERTS_PROC_LOCK_STATUS);
-
- cp_locks |= ERTS_PROC_LOCK_LINK|ERTS_PROC_LOCK_STATUS;
- /* get process struct */
- rp = erts_pid2proc_opt(BIF_P, cp_locks,
- BIF_ARG_1, ERTS_PROC_LOCK_LINK,
- ERTS_P2P_FLG_ALLOW_OTHER_X);
-
- if (ERTS_PROC_PENDING_EXIT(BIF_P)) {
- if (rp && rp != BIF_P)
- erts_proc_unlock(rp, ERTS_PROC_LOCK_LINK);
- goto handle_pending_exit;
- }
-
- /* unlink and ignore errors */
- l = erts_remove_link(&ERTS_P_LINKS(BIF_P), BIF_ARG_1);
- if (l != NULL)
- erts_destroy_link(l);
-
- if (!rp) {
- ERTS_ASSERT_IS_NOT_EXITING(BIF_P);
+ BIF_RET(am_true);
}
- else {
- rl = erts_remove_link(&ERTS_P_LINKS(rp), BIF_P->common.id);
- if (rl != NULL)
- erts_destroy_link(rl);
-
- if (IS_TRACED_FL(rp, F_TRACE_PROCS) && rl != NULL) {
- erts_proc_unlock(BIF_P, ERTS_PROC_LOCK_STATUS);
- cp_locks &= ~ERTS_PROC_LOCK_STATUS;
- trace_proc(BIF_P, (ERTS_PROC_LOCK_MAIN | ERTS_PROC_LOCK_LINK),
- rp, am_getting_unlinked, BIF_P->common.id);
- }
- if (rp != BIF_P)
- erts_proc_unlock(rp, ERTS_PROC_LOCK_LINK);
+ if (is_external_port(BIF_ARG_1)) {
+ if (external_port_dist_entry(BIF_ARG_1) == erts_this_dist_entry)
+ BIF_RET(am_true);
+ /* Links to Remote ports not supported... */
}
-
- erts_proc_unlock(BIF_P, cp_locks & ~ERTS_PROC_LOCK_MAIN);
- BIF_RET(am_true);
-
- handle_pending_exit:
- erts_handle_pending_exit(BIF_P, (ERTS_PROC_LOCK_MAIN
- | ERTS_PROC_LOCK_LINK
- | ERTS_PROC_LOCK_STATUS));
- ASSERT(ERTS_PROC_IS_EXITING(BIF_P));
- erts_proc_unlock(BIF_P, ERTS_PROC_LOCK_LINK|ERTS_PROC_LOCK_STATUS);
- ERTS_BIF_EXITED(BIF_P);
+ BIF_ERROR(BIF_P, BADARG);
}
BIF_RETTYPE hibernate_3(BIF_ALIST_3)
@@ -1493,22 +1210,69 @@ BIF_RETTYPE raise_3(BIF_ALIST_3)
return am_badarg;
}
+static BIF_RETTYPE
+erts_internal_await_exit_trap(BIF_ALIST_0)
+{
+ /*
+ * We have sent ourselves an exit signal which will
+ * terminate ourselves. Handle all signals until
+ * terminated in order to ensure that signal order
+ * is preserved. Yield if necessary.
+ */
+ erts_aint32_t state;
+ int reds = ERTS_BIF_REDS_LEFT(BIF_P);
+ (void) erts_proc_sig_handle_incoming(BIF_P, &state, &reds,
+ reds, !0);
+ BUMP_REDS(BIF_P, reds);
+ if (state & ERTS_PSFLG_EXITING)
+ ERTS_BIF_EXITED(BIF_P);
+
+ ERTS_BIF_YIELD0(&await_exit_trap, BIF_P);
+}
+
/**********************************************************************/
-/* send an exit message to another process (if trapping exits) or
- exit the other process */
+/* send an exit signal to another process */
-BIF_RETTYPE exit_2(BIF_ALIST_2)
+static BIF_RETTYPE send_exit_signal_bif(Process *c_p, Eterm id, Eterm reason, int exit2)
{
- Process *rp;
+ BIF_RETTYPE ret_val;
- /*
- * If the first argument is not a pid, or a local port it is an error.
- */
+ /*
+ * 'id' not a process id, nor a local port id is a 'badarg' error.
+ */
- if (is_internal_port(BIF_ARG_1)) {
+ if (is_internal_pid(id)) {
+ /*
+ * Preserve the very old and *very strange* behaviour
+ * of erlang:exit/2...
+ *
+ * - terminate ourselves even though exit reason
+ * is normal (unless we trap exit)
+ * - terminate ourselves before exit/2 return
+ */
+ int exit2_suicide = (exit2
+ && c_p->common.id == id
+ && (reason == am_kill
+ || !(c_p->flags & F_TRAP_EXIT)));
+ erts_proc_sig_send_exit(c_p, c_p->common.id, id,
+ reason, NIL, exit2_suicide);
+ if (!exit2_suicide)
+ ERTS_BIF_PREP_RET(ret_val, am_true);
+ else {
+ erts_proc_lock(c_p, ERTS_PROC_LOCK_MSGQ);
+ erts_proc_sig_fetch(c_p);
+ erts_proc_unlock(c_p, ERTS_PROC_LOCK_MSGQ);
+ ERTS_BIF_PREP_TRAP0(ret_val, &await_exit_trap, c_p);
+ }
+ }
+ else if (is_internal_port(id)) {
Eterm ref, *refp;
Uint32 invalid_flags;
Port *prt;
+ ErtsPortOpResult res = ERTS_PORT_OP_DONE;
+#ifdef DEBUG
+ ref = NIL;
+#endif
if (erts_port_synchronous_ops) {
refp = &ref;
@@ -1519,108 +1283,75 @@ BIF_RETTYPE exit_2(BIF_ALIST_2)
invalid_flags = ERTS_PORT_SFLGS_INVALID_LOOKUP;
}
- prt = erts_port_lookup(BIF_ARG_1, invalid_flags);
-
- if (prt) {
- ErtsPortOpResult res;
-
-#ifdef DEBUG
- ref = NIL;
-#endif
-
- res = erts_port_exit(BIF_P, 0, prt, BIF_P->common.id, BIF_ARG_2, refp);
-
- ERTS_BIF_CHK_EXITED(BIF_P);
-
- if (refp && res == ERTS_PORT_OP_SCHEDULED) {
- ASSERT(is_internal_ordinary_ref(ref));
- BIF_TRAP3(await_port_send_result_trap, BIF_P, ref, am_true, am_true);
- }
-
- }
-
- BIF_RET(am_true);
+ prt = erts_port_lookup(id, invalid_flags);
+ if (prt)
+ res = erts_port_exit(c_p, 0, prt, c_p->common.id, reason, refp);
+
+ if (!refp || res != ERTS_PORT_OP_SCHEDULED)
+ ERTS_BIF_PREP_RET(ret_val, am_true);
+ else {
+ ASSERT(is_internal_ordinary_ref(ref));
+ ERTS_BIF_PREP_TRAP3(ret_val, await_port_send_result_trap,
+ c_p, ref, am_true, am_true);
+ }
}
- else if(is_external_port(BIF_ARG_1)
- && external_port_dist_entry(BIF_ARG_1) == erts_this_dist_entry)
- BIF_RET(am_true);
-
- /*
- * If it is a remote pid, send a signal to the remote node.
- */
-
- if (is_external_pid(BIF_ARG_1)) {
- int code;
- ErtsDSigData dsd;
- DistEntry *dep;
-
- dep = external_pid_dist_entry(BIF_ARG_1);
- ERTS_ASSERT(dep);
- if(dep == erts_this_dist_entry)
- BIF_RET(am_true);
-
- code = erts_dsig_prepare(&dsd, dep, BIF_P, ERTS_PROC_LOCK_MAIN,
- ERTS_DSP_NO_LOCK, 0, 1);
- switch (code) {
- case ERTS_DSIG_PREP_NOT_ALIVE:
- case ERTS_DSIG_PREP_NOT_CONNECTED:
- BIF_RET(am_true);
- case ERTS_DSIG_PREP_PENDING:
- case ERTS_DSIG_PREP_CONNECTED:
- code = erts_dsig_send_exit2(&dsd, BIF_P->common.id, BIF_ARG_1, BIF_ARG_2);
- if (code == ERTS_DSIG_SEND_YIELD)
- ERTS_BIF_YIELD_RETURN(BIF_P, am_true);
- BIF_RET(am_true);
- default:
- ERTS_ASSERT(! "Invalid dsig prepare result");
- }
+ else if (is_external_pid(id)) {
+ DistEntry *dep = external_pid_dist_entry(id);
+ if (dep == erts_this_dist_entry)
+ ERTS_BIF_PREP_RET(ret_val, am_true); /* Old incarnation of this node... */
+ else {
+ int code;
+ ErtsDSigData dsd;
+
+ code = erts_dsig_prepare(&dsd, dep, c_p, ERTS_PROC_LOCK_MAIN,
+ ERTS_DSP_NO_LOCK, 0, 1);
+ switch (code) {
+ case ERTS_DSIG_PREP_NOT_ALIVE:
+ case ERTS_DSIG_PREP_NOT_CONNECTED:
+ ERTS_BIF_PREP_RET(ret_val, am_true);
+ break;
+ case ERTS_DSIG_PREP_PENDING:
+ case ERTS_DSIG_PREP_CONNECTED:
+ code = erts_dsig_send_exit2(&dsd, c_p->common.id, id, reason);
+ if (code == ERTS_DSIG_SEND_YIELD)
+ ERTS_BIF_PREP_YIELD_RETURN(ret_val, c_p, am_true);
+ else
+ ERTS_BIF_PREP_RET(ret_val, am_true);
+ break;
+ default:
+ ASSERT(! "Invalid dsig prepare result");
+ ERTS_BIF_PREP_ERROR(ret_val, c_p, EXC_INTERNAL_ERROR);
+ break;
+ }
+ }
}
- else if (is_not_internal_pid(BIF_ARG_1)) {
- BIF_ERROR(BIF_P, BADARG);
+ else if (is_external_port(id)) {
+ DistEntry *dep = external_port_dist_entry(id);
+ if(dep == erts_this_dist_entry)
+ ERTS_BIF_PREP_RET(ret_val, am_true); /* Old incarnation of this node... */
+ else
+ ERTS_BIF_PREP_ERROR(ret_val, c_p, BADARG);
}
else {
- /*
- * The pid is internal. Verify that it refers to an existing process.
- */
- ErtsProcLocks rp_locks;
-
- if (BIF_ARG_1 == BIF_P->common.id) {
- rp_locks = ERTS_PROC_LOCKS_ALL;
- rp = BIF_P;
- erts_proc_lock(rp, ERTS_PROC_LOCKS_ALL_MINOR);
- }
- else {
- rp_locks = ERTS_PROC_LOCKS_XSIG_SEND;
- rp = erts_pid2proc(BIF_P, ERTS_PROC_LOCK_MAIN,
- BIF_ARG_1, rp_locks);
- if (!rp) {
- BIF_RET(am_true);
- }
- }
+ /* Not an id of a process or a port... */
- /*
- * Send an exit signal.
- */
- erts_send_exit_signal(BIF_P,
- BIF_P->common.id,
- rp,
- &rp_locks,
- BIF_ARG_2,
- NIL,
- NULL,
- BIF_P == rp ? ERTS_XSIG_FLG_NO_IGN_NORMAL : 0);
- if (rp == BIF_P)
- rp_locks &= ~ERTS_PROC_LOCK_MAIN;
- if (rp_locks)
- erts_proc_unlock(rp, rp_locks);
- /*
- * We may have exited ourselves and may have to take action.
- */
- ERTS_BIF_CHK_EXITED(BIF_P);
- BIF_RET(am_true);
+ ERTS_BIF_PREP_ERROR(ret_val, c_p, BADARG);
}
+
+ return ret_val;
+}
+
+BIF_RETTYPE exit_2(BIF_ALIST_2)
+{
+ return send_exit_signal_bif(BIF_P, BIF_ARG_1, BIF_ARG_2, !0);
+}
+
+BIF_RETTYPE exit_signal_2(BIF_ALIST_2)
+{
+ return send_exit_signal_bif(BIF_P, BIF_ARG_1, BIF_ARG_2, 0);
}
+
/**********************************************************************/
/* this sets some process info- trapping exits or the error handler */
@@ -1709,42 +1440,18 @@ BIF_RETTYPE process_flag_2(BIF_ALIST_2)
BIF_RET(old_value);
}
else if (BIF_ARG_1 == am_trap_exit) {
- erts_aint32_t state;
- Uint trap_exit;
- if (BIF_ARG_2 == am_true) {
- trap_exit = 1;
- } else if (BIF_ARG_2 == am_false) {
- trap_exit = 0;
- } else {
- goto error;
- }
- /*
- * NOTE: It is important that we check for pending exit signals
- * and handle them before returning if trap_exit is set to
- * true. For more info, see implementation of
- * erts_send_exit_signal().
- */
- erts_proc_lock(BIF_P, ERTS_PROC_LOCKS_XSIG_SEND);
- if (trap_exit)
- state = erts_atomic32_read_bor_mb(&BIF_P->state,
- ERTS_PSFLG_TRAP_EXIT);
+ old_value = (BIF_P->flags & F_TRAP_EXIT) ? am_true : am_false;
+ if (BIF_ARG_2 == am_true)
+ BIF_P->flags |= F_TRAP_EXIT;
+ else if (BIF_ARG_2 == am_false)
+ BIF_P->flags &= ~F_TRAP_EXIT;
else
- state = erts_atomic32_read_band_mb(&BIF_P->state,
- ~ERTS_PSFLG_TRAP_EXIT);
- erts_proc_unlock(BIF_P, ERTS_PROC_LOCKS_XSIG_SEND);
-
- if (state & ERTS_PSFLG_PENDING_EXIT) {
- erts_handle_pending_exit(BIF_P, ERTS_PROC_LOCK_MAIN);
- ERTS_BIF_EXITED(BIF_P);
- }
-
- old_value = (state & ERTS_PSFLG_TRAP_EXIT) ? am_true : am_false;
+ goto error;
BIF_RET(old_value);
}
else if (BIF_ARG_1 == am_scheduler) {
ErtsRunQueue *old, *new, *curr;
Sint sched;
- erts_aint32_t state;
if (!is_small(BIF_ARG_2))
goto error;
@@ -1753,23 +1460,23 @@ BIF_RETTYPE process_flag_2(BIF_ALIST_2)
goto error;
if (sched == 0) {
+ old = erts_bind_runq_proc(BIF_P, 0);
new = NULL;
- state = erts_atomic32_read_band_mb(&BIF_P->state,
- ~ERTS_PSFLG_BOUND);
}
else {
+ int bound = !0;
new = erts_schedid2runq(sched);
- erts_atomic_set_nob(&BIF_P->run_queue, (erts_aint_t) new);
- state = erts_atomic32_read_bor_mb(&BIF_P->state,
- ERTS_PSFLG_BOUND);
+ old = erts_set_runq_proc(BIF_P, new, &bound);
+ if (!bound)
+ old = NULL;
}
+ old_value = old ? make_small(old->ix+1) : make_small(0);
+
curr = erts_proc_sched_data(BIF_P)->run_queue;
- old = (ERTS_PSFLG_BOUND & state) ? curr : NULL;
ASSERT(!old || old == curr);
- old_value = old ? make_small(old->ix+1) : make_small(0);
if (new && new != curr)
ERTS_BIF_YIELD_RETURN_X(BIF_P, old_value, am_scheduler);
else
@@ -2143,9 +1850,6 @@ do_send(Process *p, Eterm to, Eterm msg, Eterm *refp, ErtsSendContext *ctx)
}
switch (erts_port_command(p, ps_flags, pt, msg, refp)) {
- case ERTS_PORT_OP_CALLER_EXIT:
- /* We are exiting... */
- return SEND_USER_ERROR;
case ERTS_PORT_OP_BUSY:
/* Nothing has been sent */
if (ctx->suspend)
@@ -3102,7 +2806,7 @@ BIF_RETTYPE list_to_integer_2(BIF_ALIST_2)
static int do_float_to_charbuf(Process *p, Eterm efloat, Eterm list,
char *fbuf, int sizeof_fbuf) {
- const static int arity_two = make_arityval(2);
+ Eterm arity_two = make_arityval(2);
int decimals = SYS_DEFAULT_FLOAT_DECIMALS;
int compact = 0;
enum fmt_type_ {
@@ -3466,7 +3170,7 @@ BIF_RETTYPE binary_to_float_1(BIF_ALIST_1)
if (bit_offs)
erts_copy_bits(bytes, bit_offs, 1, buf, 0, 1, size*8);
else
- memcpy(buf, bytes, size);
+ sys_memcpy(buf, bytes, size);
buf[size] = '\0';
@@ -4200,10 +3904,10 @@ BIF_RETTYPE list_to_port_1(BIF_ALIST_1)
buf[i] = '\0'; /* null terminal */
cp = &buf[0];
- if (strncmp("#Port<", cp, 6) != 0)
+ if (sys_strncmp("#Port<", cp, 6) != 0)
goto bad;
- cp += 6; /* strlen("#Port<") */
+ cp += 6; /* sys_strlen("#Port<") */
if (sscanf(cp, "%u.%u>", (unsigned int*)&n, (unsigned int*)&p) < 2)
goto bad;
@@ -4374,14 +4078,88 @@ BIF_RETTYPE group_leader_0(BIF_ALIST_0)
}
/**********************************************************************/
-/* arg1 == leader, arg2 == new member */
+/* set group leader */
-BIF_RETTYPE group_leader_2(BIF_ALIST_2)
+int
+erts_set_group_leader(Process *proc, Eterm new_gl)
{
- Process* new_member;
+
+ erts_aint32_t state;
- if (is_not_pid(BIF_ARG_1)) {
- BIF_ERROR(BIF_P, BADARG);
+ ASSERT(is_pid(new_gl));
+
+ state = erts_atomic32_read_nob(&proc->state);
+
+ if (state & ERTS_PSFLG_EXITING)
+ return 0;
+
+ ERTS_LC_ASSERT(ERTS_PROC_LOCK_MAIN & erts_proc_lc_my_proc_locks(proc));
+
+ if (!(state & ERTS_PSFLG_DIRTY_RUNNING))
+ proc->group_leader = STORE_NC_IN_PROC(proc, new_gl);
+ else {
+ ErlHeapFragment *bp;
+ Eterm *hp;
+ /*
+ * Currently executing on a dirty scheduler,
+ * so we are not allowed to write to its heap.
+ * Store group leader pid in heap fragment.
+ */
+ bp = new_message_buffer(NC_HEAP_SIZE(new_gl));
+ hp = bp->mem;
+ proc->group_leader = STORE_NC(&hp,
+ &proc->off_heap,
+ new_gl);
+ bp->next = proc->mbuf;
+ proc->mbuf = bp;
+ proc->mbuf_sz += bp->used_size;
+ }
+
+ return !0;
+}
+
+BIF_RETTYPE erts_internal_group_leader_3(BIF_ALIST_3)
+{
+ if (is_not_pid(BIF_ARG_1))
+ BIF_ERROR(BIF_P, BADARG);
+ if (is_not_internal_pid(BIF_ARG_2))
+ BIF_ERROR(BIF_P, BADARG);
+ if (is_not_internal_ref(BIF_ARG_3))
+ BIF_ERROR(BIF_P, BADARG);
+
+ erts_proc_sig_send_group_leader(BIF_P,
+ BIF_ARG_2,
+ BIF_ARG_1,
+ BIF_ARG_3);
+ BIF_RET(am_ok);
+}
+
+BIF_RETTYPE erts_internal_group_leader_2(BIF_ALIST_2)
+{
+ if (is_not_pid(BIF_ARG_1))
+ BIF_RET(am_badarg);
+
+ if (is_internal_pid(BIF_ARG_2)) {
+ Process *rp;
+ int res;
+
+ if (BIF_ARG_2 == BIF_P->common.id)
+ rp = BIF_P;
+ else {
+ rp = erts_try_lock_sig_free_proc(BIF_ARG_2,
+ ERTS_PROC_LOCK_MAIN);
+ if (!rp)
+ BIF_RET(am_badarg);
+ if (rp == ERTS_PROC_LOCK_BUSY)
+ BIF_RET(am_false);
+ }
+
+ res = erts_set_group_leader(rp, BIF_ARG_1);
+
+ if (rp != BIF_P)
+ erts_proc_unlock(rp, ERTS_PROC_LOCK_MAIN);
+
+ BIF_RET(res ? am_true : am_badarg);
}
if (is_external_pid(BIF_ARG_2)) {
@@ -4409,74 +4187,8 @@ BIF_RETTYPE group_leader_2(BIF_ALIST_2)
ERTS_ASSERT(! "Invalid dsig prepare result");
}
}
- else if (is_internal_pid(BIF_ARG_2)) {
- int await_x;
- ErtsProcLocks locks = ERTS_PROC_LOCK_MAIN|ERTS_PROC_LOCK_STATUS;
- new_member = erts_pid2proc_nropt(BIF_P, ERTS_PROC_LOCK_MAIN,
- BIF_ARG_2, locks);
- if (!new_member)
- BIF_ERROR(BIF_P, BADARG);
-
- if (new_member == ERTS_PROC_LOCK_BUSY)
- ERTS_BIF_YIELD2(bif_export[BIF_group_leader_2], BIF_P,
- BIF_ARG_1, BIF_ARG_2);
-
- await_x = (new_member != BIF_P
- && ERTS_PROC_PENDING_EXIT(new_member));
- if (!await_x) {
- if (is_immed(BIF_ARG_1))
- new_member->group_leader = BIF_ARG_1;
- else {
- locks &= ~ERTS_PROC_LOCK_STATUS;
- erts_proc_unlock(new_member, ERTS_PROC_LOCK_STATUS);
- if (new_member == BIF_P
- || !(erts_atomic32_read_nob(&new_member->state)
- & ERTS_PSFLG_DIRTY_RUNNING)) {
- new_member->group_leader = STORE_NC_IN_PROC(new_member,
- BIF_ARG_1);
- }
- else {
- ErlHeapFragment *bp;
- Eterm *hp;
- /*
- * Other process executing on a dirty scheduler,
- * so we are not allowed to write to its heap.
- * Store in heap fragment.
- */
-
- bp = new_message_buffer(NC_HEAP_SIZE(BIF_ARG_1));
- hp = bp->mem;
- new_member->group_leader = STORE_NC(&hp,
- &new_member->off_heap,
- BIF_ARG_1);
- bp->next = new_member->mbuf;
- new_member->mbuf = bp;
- new_member->mbuf_sz += bp->used_size;
- }
- }
- }
-
- if (new_member == BIF_P)
- locks &= ~ERTS_PROC_LOCK_MAIN;
- if (locks)
- erts_proc_unlock(new_member, locks);
-
- if (await_x) {
- /* Wait for new_member to terminate; then badarg */
- Eterm args[2] = {BIF_ARG_1, BIF_ARG_2};
- ERTS_BIF_AWAIT_X_APPLY_TRAP(BIF_P,
- BIF_ARG_2,
- am_erlang,
- am_group_leader,
- args,
- 2);
- }
- BIF_RET(am_true);
- }
- else {
- BIF_ERROR(BIF_P, BADARG);
- }
+ BIF_RET(am_badarg);
}
BIF_RETTYPE system_flag_2(BIF_ALIST_2)
@@ -4663,7 +4375,6 @@ BIF_RETTYPE system_flag_2(BIF_ALIST_2)
BIF_RET(ret);
} else if (BIF_ARG_1 == make_small(1)) {
int i, max;
- ErtsMessage* mp;
erts_proc_unlock(BIF_P, ERTS_PROC_LOCK_MAIN);
erts_thr_progress_block();
@@ -4678,16 +4389,8 @@ BIF_RETTYPE system_flag_2(BIF_ALIST_2)
#endif
p->seq_trace_clock = 0;
p->seq_trace_lastcnt = 0;
- ERTS_MSGQ_MV_INQ2PRIVQ(p);
- mp = p->msg.first;
- while(mp != NULL) {
-#ifdef USE_VM_PROBES
- ERL_MESSAGE_TOKEN(mp) = (ERL_MESSAGE_DT_UTAG(mp) != NIL) ? am_have_dt_utag : NIL;
-#else
- ERL_MESSAGE_TOKEN(mp) = NIL;
-#endif
- mp = mp->next;
- }
+
+ erts_proc_sig_clear_seq_trace_tokens(p);
}
}
@@ -4805,6 +4508,8 @@ BIF_RETTYPE system_flag_2(BIF_ALIST_2)
"scheduled for removal in Erlang/OTP 18. For more\n"
"information see the erlang:system_flag/2 documentation.\n");
return erts_bind_schedulers(BIF_P, BIF_ARG_2);
+ } else if (ERTS_IS_ATOM_STR("erts_alloc", BIF_ARG_1)) {
+ return erts_alloc_set_dyn_param(BIF_P, BIF_ARG_2);
}
error:
BIF_ERROR(BIF_P, BADARG);
@@ -4948,85 +4653,6 @@ static BIF_RETTYPE bif_return_trap(BIF_ALIST_2)
BIF_RET(res);
}
-/*
- * NOTE: The erts_bif_prep_await_proc_exit_*() functions are
- * tightly coupled with the implementation of erlang:await_proc_exit/3.
- * The erts_bif_prep_await_proc_exit_*() functions can safely call
- * skip_current_msgq() since they know that erlang:await_proc_exit/3
- * unconditionally will do a monitor and then unconditionally will
- * wait for the corresponding 'DOWN' message in a receive, and no other
- * receive is done before this receive. This optimization removes an
- * unnecessary scan of the currently existing message queue (which
- * can be large). If the erlang:await_proc_exit/3 implementation
- * is changed so that the above isn't true, nasty bugs in later
- * receives, etc, may appear.
- */
-
-static ERTS_INLINE int
-skip_current_msgq(Process *c_p)
-{
- int res;
-#if defined(ERTS_ENABLE_LOCK_CHECK)
- erts_proc_lc_chk_only_proc_main(c_p);
-#endif
-
- erts_proc_lock(c_p, ERTS_PROC_LOCKS_MSG_RECEIVE);
- if (ERTS_PROC_PENDING_EXIT(c_p)) {
- KILL_CATCHES(c_p);
- c_p->freason = EXC_EXIT;
- res = 0;
- }
- else {
- ERTS_MSGQ_MV_INQ2PRIVQ(c_p);
- c_p->msg.save = c_p->msg.last;
- res = 1;
- }
- erts_proc_unlock(c_p, ERTS_PROC_LOCKS_MSG_RECEIVE);
- return res;
-}
-
-void
-erts_bif_prep_await_proc_exit_data_trap(Process *c_p, Eterm pid, Eterm ret)
-{
- if (skip_current_msgq(c_p)) {
- ERTS_BIF_PREP_TRAP3_NO_RET(await_proc_exit_trap, c_p, pid, am_data, ret);
- }
-}
-
-void
-erts_bif_prep_await_proc_exit_reason_trap(Process *c_p, Eterm pid)
-{
- if (skip_current_msgq(c_p)) {
- ERTS_BIF_PREP_TRAP3_NO_RET(await_proc_exit_trap, c_p,
- pid, am_reason, am_undefined);
- }
-}
-
-void
-erts_bif_prep_await_proc_exit_apply_trap(Process *c_p,
- Eterm pid,
- Eterm module,
- Eterm function,
- Eterm args[],
- int nargs)
-{
- ASSERT(is_atom(module) && is_atom(function));
- if (skip_current_msgq(c_p)) {
- Eterm term;
- Eterm *hp;
- int i;
-
- hp = HAlloc(c_p, 4+2*nargs);
- term = NIL;
- for (i = nargs-1; i >= 0; i--) {
- term = CONS(hp, args[i], term);
- hp += 2;
- }
- term = TUPLE3(hp, module, function, term);
- ERTS_BIF_PREP_TRAP3_NO_RET(await_proc_exit_trap, c_p, pid, am_apply, term);
- }
-}
-
Export bif_return_trap_export;
void erts_init_trap_export(Export* ep, Eterm m, Eterm f, Uint a,
@@ -5062,6 +4688,9 @@ void erts_init_bif(void)
am_erts_internal, am_dsend_continue_trap, 1,
dsend_continue_trap_1);
+ erts_init_trap_export(&await_exit_trap, am_erts_internal,
+ am_await_exit, 0, erts_internal_await_exit_trap);
+
flush_monitor_messages_trap = erts_export_put(am_erts_internal,
am_flush_monitor_messages,
3);
@@ -5076,7 +4705,6 @@ void erts_init_bif(void)
erts_format_cpu_topology_trap = erts_export_put(am_erlang,
am_format_cpu_topology,
1);
- await_proc_exit_trap = erts_export_put(am_erlang,am_await_proc_exit,3);
await_port_send_result_trap
= erts_export_put(am_erts_internal, am_await_port_send_result, 3);
system_flag_scheduler_wall_time_trap
diff --git a/erts/emulator/beam/bif.h b/erts/emulator/beam/bif.h
index a2bc883dbe..a33421d762 100644
--- a/erts/emulator/beam/bif.h
+++ b/erts/emulator/beam/bif.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1996-2016. All Rights Reserved.
+ * Copyright Ericsson AB 1996-2018. 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.
@@ -306,7 +306,7 @@ do { \
(Proc)->freason = TRAP; \
} while (0)
-#define BIF_TRAP0(p, Trap_) do { \
+#define BIF_TRAP0(Trap_, p) do { \
(p)->arity = 0; \
(p)->i = (BeamInstr*) ((Trap_)->addressv[erts_active_code_ix()]); \
(p)->freason = TRAP; \
@@ -404,7 +404,7 @@ do { \
#define ERTS_BIF_YIELD0(TRP, P) \
do { \
ERTS_VBUMP_ALL_REDS((P)); \
- BIF_TRAP0((TRP), (P)); \
+ BIF_TRAP0((TRP), (P)); \
} while (0)
#define ERTS_BIF_YIELD1(TRP, P, A0) \
@@ -428,7 +428,7 @@ do { \
#define ERTS_BIF_EXITED(PROC) \
do { \
KILL_CATCHES((PROC)); \
- BIF_ERROR((PROC), EXC_EXIT); \
+ BIF_ERROR((PROC), EXTAG_EXIT); \
} while (0)
#define ERTS_BIF_CHK_EXITED(PROC) \
@@ -437,67 +437,6 @@ do { \
ERTS_BIF_EXITED((PROC)); \
} while (0)
-/*
- * The ERTS_BIF_*_AWAIT_X_*_TRAP makros either exits the caller, or
- * sets up a trap to erlang:await_proc_exit/3.
- *
- * The caller is acquired to hold the 'main' lock on C_P. No other locks
- * are allowed to be held.
- */
-
-#define ERTS_BIF_PREP_AWAIT_X_DATA_TRAP(RET, C_P, PID, DATA) \
-do { \
- erts_bif_prep_await_proc_exit_data_trap((C_P), (PID), (DATA)); \
- (RET) = THE_NON_VALUE; \
-} while (0)
-
-#define ERTS_BIF_PREP_AWAIT_X_REASON_TRAP(RET, C_P, PID) \
-do { \
- erts_bif_prep_await_proc_exit_reason_trap((C_P), (PID)); \
- (RET) = THE_NON_VALUE; \
-} while (0)
-
-#define ERTS_BIF_PREP_AWAIT_X_APPLY_TRAP(RET, C_P, PID, M, F, A, AN) \
-do { \
- erts_bif_prep_await_proc_exit_apply_trap((C_P), (PID), \
- (M), (F), (A), (AN)); \
- (RET) = THE_NON_VALUE; \
-} while (0)
-
-#define ERTS_BIF_AWAIT_X_DATA_TRAP(C_P, PID, DATA) \
-do { \
- erts_bif_prep_await_proc_exit_data_trap((C_P), (PID), (DATA)); \
- return THE_NON_VALUE; \
-} while (0)
-
-#define ERTS_BIF_AWAIT_X_REASON_TRAP(C_P, PID) \
-do { \
- erts_bif_prep_await_proc_exit_reason_trap((C_P), (PID)); \
- return THE_NON_VALUE; \
-} while (0)
-
-#define ERTS_BIF_AWAIT_X_APPLY_TRAP(C_P, PID, M, F, A, AN) \
-do { \
- erts_bif_prep_await_proc_exit_apply_trap((C_P), (PID), \
- (M), (F), (A), (AN)); \
- return THE_NON_VALUE; \
-} while (0)
-
-void
-erts_bif_prep_await_proc_exit_data_trap(Process *c_p,
- Eterm pid,
- Eterm data);
-void
-erts_bif_prep_await_proc_exit_reason_trap(Process *c_p,
- Eterm pid);
-void
-erts_bif_prep_await_proc_exit_apply_trap(Process *c_p,
- Eterm pid,
- Eterm module,
- Eterm function,
- Eterm args[],
- int nargs);
-
int erts_call_dirty_bif(ErtsSchedulerData *esdp, Process *c_p,
BeamInstr *I, Eterm *reg);
diff --git a/erts/emulator/beam/bif.tab b/erts/emulator/beam/bif.tab
index c0d5e8ce74..687fd39d58 100644
--- a/erts/emulator/beam/bif.tab
+++ b/erts/emulator/beam/bif.tab
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1996-2017. All Rights Reserved.
+# Copyright Ericsson AB 1996-2018. 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.
@@ -62,6 +62,7 @@ bif erlang:erase/0
bif erlang:erase/1
bif erlang:exit/1
bif erlang:exit/2
+bif erlang:exit_signal/2
bif erlang:external_size/1
bif erlang:external_size/2
gcbif erlang:float/1
@@ -73,7 +74,8 @@ bif erlang:get/0
bif erlang:get/1
bif erlang:get_keys/1
bif erlang:group_leader/0
-bif erlang:group_leader/2
+bif erts_internal:group_leader/2
+bif erts_internal:group_leader/3
bif erlang:halt/2
bif erlang:phash/2
bif erlang:phash2/1
@@ -187,6 +189,8 @@ bif erts_internal:release_literal_area_switch/0
bif erts_internal:scheduler_wall_time/1
+bif erts_internal:dirty_process_handle_signals/1
+
# inet_db support
bif erlang:port_set_data/2
bif erlang:port_get_data/1
@@ -262,6 +266,7 @@ bif erlang:demonitor/1
bif erlang:demonitor/2
bif erlang:is_process_alive/1
+bif erts_internal:is_process_alive/2
bif erlang:error/1 error_1
bif erlang:error/2 error_2
@@ -435,13 +440,6 @@ bif erts_debug:dirty_io/2
bif erts_debug:dirty/3
#
-# Monitor testing bif's...
-#
-bif erts_debug:dump_monitors/1
-bif erts_debug:dump_links/1
-
-
-#
# Lock counter bif's
#
bif erts_debug:lcnt_control/2
@@ -551,9 +549,6 @@ bif binary:last/1
bif binary:at/2
bif binary:part/2 binary_binary_part_2
bif binary:part/3 binary_binary_part_3
-bif binary:bin_to_list/1
-bif binary:bin_to_list/2
-bif binary:bin_to_list/3
bif binary:list_to_bin/1
bif binary:copy/1
bif binary:copy/2
@@ -693,6 +688,8 @@ bif erlang:iolist_to_iovec/1
# New in 21.0
#
+bif erts_internal:get_dflags/0
bif erts_internal:new_connection/1
bif erts_internal:abort_connection/2
bif erts_internal:map_next/3
+bif ets:whereis/1
diff --git a/erts/emulator/beam/binary.c b/erts/emulator/beam/binary.c
index d4db1a4188..95d324d2c1 100644
--- a/erts/emulator/beam/binary.c
+++ b/erts/emulator/beam/binary.c
@@ -953,7 +953,10 @@ HIPE_WRAPPER_BIF_DISABLE_GC(iolist_to_binary, 1)
BIF_RETTYPE iolist_to_binary_1(BIF_ALIST_1)
{
if (is_binary(BIF_ARG_1)) {
- BIF_RET(BIF_ARG_1);
+ if (binary_bitsize(BIF_ARG_1) == 0) {
+ BIF_RET(BIF_ARG_1);
+ }
+ BIF_ERROR(BIF_P, BADARG);
}
return erts_list_to_binary_bif(BIF_P, BIF_ARG_1, bif_export[BIF_iolist_to_binary_1]);
}
diff --git a/erts/emulator/beam/break.c b/erts/emulator/beam/break.c
index e7acea0c5f..4dabac3512 100644
--- a/erts/emulator/beam/break.c
+++ b/erts/emulator/beam/break.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1996-2017. All Rights Reserved.
+ * Copyright Ericsson AB 1996-2018. 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.
@@ -39,6 +39,7 @@
#include "erl_instrument.h"
#include "erl_hl_timer.h"
#include "erl_thr_progress.h"
+#include "erl_proc_sig_queue.h"
/* Forward declarations -- should really appear somewhere else */
static void process_killer(void);
@@ -107,36 +108,11 @@ process_killer(void)
if ((j = sys_get_key(0)) <= 0)
erts_exit(0, "");
switch(j) {
- case 'k': {
- ErtsProcLocks rp_locks = ERTS_PROC_LOCKS_XSIG_SEND;
- erts_aint32_t state;
- erts_proc_inc_refc(rp);
- erts_proc_lock(rp, rp_locks);
- state = erts_atomic32_read_acqb(&rp->state);
- if (state & (ERTS_PSFLG_FREE
- | ERTS_PSFLG_EXITING
- | ERTS_PSFLG_ACTIVE
- | ERTS_PSFLG_ACTIVE_SYS
- | ERTS_PSFLG_IN_RUNQ
- | ERTS_PSFLG_RUNNING
- | ERTS_PSFLG_RUNNING_SYS
- | ERTS_PSFLG_DIRTY_RUNNING
- | ERTS_PSFLG_DIRTY_RUNNING_SYS)) {
- erts_printf("Can only kill WAITING processes this way\n");
- }
- else {
- (void) erts_send_exit_signal(NULL,
- NIL,
- rp,
- &rp_locks,
- am_kill,
- NIL,
- NULL,
- 0);
- }
- erts_proc_unlock(rp, rp_locks);
- erts_proc_dec_refc(rp);
- }
+ case 'k':
+ /* Send a 'kill' exit signal from init process */
+ erts_proc_sig_send_exit(NULL, erts_init_process_id,
+ rp->common.id, am_kill, NIL,
+ 0);
case 'n': br = 1; break;
case 'r': return;
default: return;
@@ -161,47 +137,64 @@ static void doit_print_link(ErtsLink *lnk, void *vpcontext)
if (pcontext->is_first) {
pcontext->is_first = 0;
- erts_print(to, to_arg, "%T", lnk->pid);
+ erts_print(to, to_arg, "%T", lnk->other.item);
} else {
- erts_print(to, to_arg, ", %T", lnk->pid);
+ erts_print(to, to_arg, ", %T", lnk->other.item);
}
}
static void doit_print_monitor(ErtsMonitor *mon, void *vpcontext)
{
+ ErtsMonitorData *mdp;
PrintMonitorContext *pcontext = vpcontext;
fmtfn_t to = pcontext->to;
void *to_arg = pcontext->to_arg;
char *prefix = ", ";
- if (pcontext->is_first) {
- pcontext->is_first = 0;
- prefix = "";
- }
-
+ mdp = erts_monitor_to_data(mon);
switch (mon->type) {
- case MON_ORIGIN:
- if (is_atom(mon->u.pid)) { /* dist by name */
- ASSERT(is_node_name_atom(mon->u.pid));
- erts_print(to, to_arg, "%s{to,{%T,%T},%T}", prefix, mon->name,
- mon->u.pid, mon->ref);
- } else if (is_atom(mon->name)){ /* local by name */
- erts_print(to, to_arg, "%s{to,{%T,%T},%T}", prefix, mon->name,
- erts_this_dist_entry->sysname, mon->ref);
- } else { /* local and distributed by pid */
- erts_print(to, to_arg, "%s{to,%T,%T}", prefix, mon->u.pid, mon->ref);
- }
- break;
- case MON_TARGET:
- erts_print(to, to_arg, "%s{from,%T,%T}", prefix, mon->u.pid, mon->ref);
- break;
- case MON_NIF_TARGET: {
- ErtsResource* rsrc = mon->u.resource;
- erts_print(to, to_arg, "%s{from,{%T,%T},%T}", prefix, rsrc->type->module,
- rsrc->type->name, mon->ref);
- break;
- }
+ case ERTS_MON_TYPE_PROC:
+ case ERTS_MON_TYPE_PORT:
+ case ERTS_MON_TYPE_TIME_OFFSET:
+ case ERTS_MON_TYPE_DIST_PROC:
+ case ERTS_MON_TYPE_RESOURCE:
+ case ERTS_MON_TYPE_NODE:
+
+ if (pcontext->is_first) {
+ pcontext->is_first = 0;
+ prefix = "";
+ }
+
+ if (erts_monitor_is_target(mon)) {
+ if (mon->type != ERTS_MON_TYPE_RESOURCE)
+ erts_print(to, to_arg, "%s{from,%T,%T}", prefix, mon->other.item, mdp->ref);
+ else {
+ ErtsResource* rsrc = mon->other.ptr;
+ erts_print(to, to_arg, "%s{from,{%T,%T},%T}", prefix, rsrc->type->module,
+ rsrc->type->name, mdp->ref);
+ }
+ }
+ else {
+ if (!(mon->flags & ERTS_ML_FLG_NAME))
+ erts_print(to, to_arg, "%s{to,%T,%T}", prefix, mon->other.item, mdp->ref);
+ else {
+ ErtsMonitorDataExtended *mdep = (ErtsMonitorDataExtended *) mdp;
+ Eterm node;
+ if (mdep->dist)
+ node = mdep->dist->nodename;
+ else
+ node = erts_this_dist_entry->sysname;
+ erts_print(to, to_arg, "%s{to,{%T,%T},%T}", prefix, mdep->u.name,
+ node, mdp->ref);
+ }
+ }
+
+ break;
+
+ default:
+ /* ignore other monitors... */
+ break;
}
}
@@ -209,7 +202,6 @@ static void doit_print_monitor(ErtsMonitor *mon, void *vpcontext)
void
print_process_info(fmtfn_t to, void *to_arg, Process *p)
{
- time_t approx_started;
int garbing = 0;
int running = 0;
struct saved_calls *scb;
@@ -258,17 +250,22 @@ print_process_info(fmtfn_t to, void *to_arg, Process *p)
}
erts_print(to, to_arg, "Spawned by: %T\n", p->parent);
- approx_started = (time_t) p->approx_started;
- erts_print(to, to_arg, "Started: %s", ctime(&approx_started));
- ERTS_MSGQ_MV_INQ2PRIVQ(p);
- erts_print(to, to_arg, "Message queue length: %d\n", p->msg.len);
+
+ erts_proc_lock(p, ERTS_PROC_LOCK_MSGQ);
+ erts_proc_sig_fetch(p);
+ erts_proc_unlock(p, ERTS_PROC_LOCK_MSGQ);
+ erts_print(to, to_arg, "Message queue length: %d\n", p->sig_qs.len);
/* display the message queue only if there is anything in it */
- if (!ERTS_IS_CRASH_DUMPING && p->msg.first != NULL && !garbing) {
- ErtsMessage* mp;
+ if (!ERTS_IS_CRASH_DUMPING && p->sig_qs.first != NULL && !garbing) {
erts_print(to, to_arg, "Message queue: [");
- for (mp = p->msg.first; mp; mp = mp->next)
- erts_print(to, to_arg, mp->next ? "%T," : "%T", ERL_MESSAGE_TERM(mp));
+ ERTS_FOREACH_SIG_PRIVQS(
+ p, mp,
+ {
+ if (ERTS_SIG_IS_NON_MSG((ErtsSignal *) mp))
+ erts_print(to, to_arg, mp->next ? "%T," : "%T",
+ ERL_MESSAGE_TERM(mp));
+ });
erts_print(to, to_arg, "]\n");
}
@@ -309,11 +306,12 @@ print_process_info(fmtfn_t to, void *to_arg, Process *p)
}
/* display the links only if there are any*/
- if (ERTS_P_LINKS(p) || ERTS_P_MONITORS(p)) {
+ if (ERTS_P_LINKS(p) || ERTS_P_MONITORS(p) || ERTS_P_LT_MONITORS(p)) {
PrintMonitorContext context = {1, to, to_arg};
erts_print(to, to_arg,"Link list: [");
- erts_doforall_links(ERTS_P_LINKS(p), &doit_print_link, &context);
- erts_doforall_monitors(ERTS_P_MONITORS(p), &doit_print_monitor, &context);
+ erts_link_tree_foreach(ERTS_P_LINKS(p), doit_print_link, &context);
+ erts_monitor_tree_foreach(ERTS_P_MONITORS(p), doit_print_monitor, &context);
+ erts_monitor_list_foreach(ERTS_P_LT_MONITORS(p), doit_print_monitor, &context);
erts_print(to, to_arg,"]\n");
}
@@ -336,6 +334,12 @@ print_process_info(fmtfn_t to, void *to_arg, Process *p)
erts_print(to, to_arg, "Heap unused: %bpu\n", (p->hend - p->htop));
erts_print(to, to_arg, "OldHeap unused: %bpu\n",
(OLD_HEAP(p) == NULL) ? 0 : (OLD_HEND(p) - OLD_HTOP(p)) );
+ erts_print(to, to_arg, "BinVHeap: %b64u\n", p->off_heap.overhead);
+ erts_print(to, to_arg, "OldBinVHeap: %b64u\n", BIN_OLD_VHEAP(p));
+ erts_print(to, to_arg, "BinVHeap unused: %b64u\n",
+ BIN_VHEAP_SZ(p) - p->off_heap.overhead);
+ erts_print(to, to_arg, "OldBinVHeap unused: %b64u\n",
+ BIN_OLD_VHEAP_SZ(p) - BIN_OLD_VHEAP(p));
erts_print(to, to_arg, "Memory: %beu\n", erts_process_memory(p, !0));
if (garbing) {
@@ -503,7 +507,7 @@ do_break(void)
/* check if we're in console mode and, if so,
halt immediately if break is called */
mode = erts_read_env("ERL_CONSOLE_MODE");
- if (mode && strcmp(mode, "window") != 0)
+ if (mode && sys_strcmp(mode, "window") != 0)
erts_exit(0, "");
erts_free_read_env(mode);
#endif /* __WIN32__ */
@@ -891,6 +895,21 @@ erl_crash_dump_v(char *file, int line, char* fmt, va_list args)
erts_print_scheduler_info(to, to_arg, ERTS_SCHEDULER_IX(i)),
erts_cbprintf(to, to_arg, "** crashed **\n"));
}
+ for (i = 0; i < erts_no_dirty_cpu_schedulers; i++) {
+ ERTS_SYS_TRY_CATCH(
+ erts_print_scheduler_info(to, to_arg, ERTS_DIRTY_CPU_SCHEDULER_IX(i)),
+ erts_cbprintf(to, to_arg, "** crashed **\n"));
+ }
+ erts_cbprintf(to, to_arg, "=dirty_cpu_run_queue\n");
+ erts_print_run_queue_info(to, to_arg, ERTS_DIRTY_CPU_RUNQ);
+
+ for (i = 0; i < erts_no_dirty_io_schedulers; i++) {
+ ERTS_SYS_TRY_CATCH(
+ erts_print_scheduler_info(to, to_arg, ERTS_DIRTY_IO_SCHEDULER_IX(i)),
+ erts_cbprintf(to, to_arg, "** crashed **\n"));
+ }
+ erts_cbprintf(to, to_arg, "=dirty_io_run_queue\n");
+ erts_print_run_queue_info(to, to_arg, ERTS_DIRTY_IO_RUNQ);
#endif
diff --git a/erts/emulator/beam/bs_instrs.tab b/erts/emulator/beam/bs_instrs.tab
index b11903a47b..94e0000c8b 100644
--- a/erts/emulator/beam/bs_instrs.tab
+++ b/erts/emulator/beam/bs_instrs.tab
@@ -990,6 +990,9 @@ ctx_to_bin.execute() {
Uint hole_size;
Uint orig = mb->orig;
ErlSubBin* sb = (ErlSubBin *) boxed_val(context);
+ /* Since we're going to overwrite the match state with the result, an
+ * ErlBinMatchState must be at least as large as an ErlSubBin. */
+ ERTS_CT_ASSERT(sizeof(ErlSubBin) <= sizeof(ErlBinMatchState));
hole_size = 1 + header_arity(sb->thing_word) - ERL_SUB_BIN_SIZE;
sb->thing_word = HEADER_SUB_BIN;
sb->size = BYTE_OFFSET(size);
diff --git a/erts/emulator/beam/copy.c b/erts/emulator/beam/copy.c
index 10bf197405..7769a914db 100644
--- a/erts/emulator/beam/copy.c
+++ b/erts/emulator/beam/copy.c
@@ -611,7 +611,7 @@ Eterm copy_struct_x(Eterm obj, Uint sz, Eterm** hpp, ErlOffHeap* off_heap, Uint
Eterm* htop;
Eterm* hbot;
Eterm* hp;
- Eterm* objp;
+ Eterm* ERTS_RESTRICT objp;
Eterm* tp;
Eterm res;
Eterm elem;
@@ -1821,7 +1821,8 @@ all_clean:
*
* NOTE: Assumes that term is a tuple (ptr is an untagged tuple ptr).
*/
-Eterm copy_shallow(Eterm* ptr, Uint sz, Eterm** hpp, ErlOffHeap* off_heap)
+Eterm copy_shallow(Eterm* ERTS_RESTRICT ptr, Uint sz, Eterm** hpp,
+ ErlOffHeap* off_heap)
{
Eterm* tp = ptr;
Eterm* hp = *hpp;
@@ -1985,7 +1986,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);
+ ptr = move_boxed(ptr, val, &hp, &dummy_ref);
switch (val & _HEADER_SUBTAG_MASK) {
case REF_SUBTAG:
if (is_ordinary_ref_thing(hdr))
@@ -2002,7 +2003,7 @@ move_one_frag(Eterm** hpp, ErlHeapFragment* frag, ErlOffHeap* off_heap, int lite
}
else { /* must be a cons cell */
ASSERT(ptr+1 < end);
- move_cons(&ptr, val, &hp, &dummy_ref);
+ move_cons(ptr, val, &hp, &dummy_ref);
ptr += 2;
}
}
diff --git a/erts/emulator/beam/dist.c b/erts/emulator/beam/dist.c
index 30390cdb5e..fdf307da1b 100644
--- a/erts/emulator/beam/dist.c
+++ b/erts/emulator/beam/dist.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1996-2017. All Rights Reserved.
+ * Copyright Ericsson AB 1996-2018. 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.
@@ -45,6 +45,7 @@
#include "erl_binary.h"
#include "erl_thr_progress.h"
#include "dtrace-wrapper.h"
+#include "erl_proc_sig_queue.h"
#define DIST_CTL_DEFAULT_SIZE 64
@@ -109,7 +110,6 @@ int erts_dist_buf_busy_limit;
/* distribution trap functions */
Export* dmonitor_node_trap = NULL;
-Export* dmonitor_p_trap = NULL;
/* local variables */
static Export *dist_ctrl_put_data_trap;
@@ -184,6 +184,161 @@ get_suspended_on_de(DistEntry *dep, erts_aint32_t unset_qflgs)
}
}
+#define ERTS_MON_LNK_FIRE_LIMIT 100
+
+static void monitor_connection_down(ErtsMonitor *mon, void *unused)
+{
+ if (erts_monitor_is_origin(mon))
+ erts_proc_sig_send_demonitor(mon);
+ else
+ erts_proc_sig_send_monitor_down(mon, am_noconnection);
+}
+
+static void link_connection_down(ErtsLink *lnk, void *vdist)
+{
+ erts_proc_sig_send_link_exit(NULL, THE_NON_VALUE, lnk,
+ am_noconnection, NIL);
+}
+
+typedef enum {
+ ERTS_CML_CLEANUP_STATE_LINKS,
+ ERTS_CML_CLEANUP_STATE_MONITORS,
+ ERTS_CML_CLEANUP_STATE_ONAME_MONITORS,
+ ERTS_CML_CLEANUP_STATE_NODE_MONITORS
+} ErtsConMonLnkCleaupState;
+
+typedef struct {
+ ErtsConMonLnkCleaupState state;
+ ErtsMonLnkDist *dist;
+ void *yield_state;
+ int trigger_node_monitors;
+ Eterm nodename;
+ Eterm visability;
+ Eterm reason;
+ ErlOffHeap oh;
+ Eterm heap[1];
+} ErtsConMonLnkCleanup;
+
+static void
+con_monitor_link_cleanup(void *vcmlcp)
+{
+ ErtsConMonLnkCleanup *cmlcp = vcmlcp;
+ ErtsMonLnkDist *dist = cmlcp->dist;
+ ErtsSchedulerData *esdp;
+ int yield;
+
+ switch (cmlcp->state) {
+ case ERTS_CML_CLEANUP_STATE_LINKS:
+ yield = erts_link_list_foreach_delete_yielding(&dist->links,
+ link_connection_down,
+ NULL, &cmlcp->yield_state,
+ ERTS_MON_LNK_FIRE_LIMIT);
+ if (yield)
+ break;
+
+ ASSERT(!cmlcp->yield_state);
+ cmlcp->state = ERTS_CML_CLEANUP_STATE_MONITORS;
+ case ERTS_CML_CLEANUP_STATE_MONITORS:
+ yield = erts_monitor_list_foreach_delete_yielding(&dist->monitors,
+ monitor_connection_down,
+ NULL, &cmlcp->yield_state,
+ ERTS_MON_LNK_FIRE_LIMIT);
+ if (yield)
+ break;
+
+ ASSERT(!cmlcp->yield_state);
+ cmlcp->state = ERTS_CML_CLEANUP_STATE_ONAME_MONITORS;
+ case ERTS_CML_CLEANUP_STATE_ONAME_MONITORS:
+ yield = erts_monitor_tree_foreach_delete_yielding(&dist->orig_name_monitors,
+ monitor_connection_down,
+ NULL, &cmlcp->yield_state,
+ ERTS_MON_LNK_FIRE_LIMIT/2);
+ if (yield)
+ break;
+
+ cmlcp->dist = NULL;
+ erts_mon_link_dist_dec_refc(dist);
+
+ ASSERT(!cmlcp->yield_state);
+ cmlcp->state = ERTS_CML_CLEANUP_STATE_NODE_MONITORS;
+ case ERTS_CML_CLEANUP_STATE_NODE_MONITORS:
+ if (cmlcp->trigger_node_monitors) {
+ send_nodes_mon_msgs(NULL,
+ am_nodedown,
+ cmlcp->nodename,
+ cmlcp->visability,
+ cmlcp->reason);
+ }
+ erts_cleanup_offheap(&cmlcp->oh);
+ erts_free(ERTS_ALC_T_CML_CLEANUP, vcmlcp);
+ return; /* done */
+ }
+
+ /* yield... */
+
+ esdp = erts_get_scheduler_data();
+ ASSERT(esdp && esdp->type == ERTS_SCHED_NORMAL);
+ erts_schedule_misc_aux_work((int) esdp->no,
+ con_monitor_link_cleanup,
+ (void *) cmlcp);
+}
+
+static void
+schedule_con_monitor_link_cleanup(ErtsMonLnkDist *dist,
+ Eterm nodename,
+ Eterm visability,
+ Eterm reason)
+{
+ if (dist || is_value(nodename)) {
+ ErtsSchedulerData *esdp;
+ ErtsConMonLnkCleanup *cmlcp;
+ Uint rsz, size;
+
+ size = sizeof(ErtsConMonLnkCleanup);
+
+ if (is_non_value(reason) || is_immed(reason)) {
+ rsz = 0;
+ size -= sizeof(Eterm);
+ }
+ else {
+ rsz = size_object(reason);
+ size += sizeof(Eterm) * (rsz - 1);
+ }
+
+ cmlcp = erts_alloc(ERTS_ALC_T_CML_CLEANUP, size);
+
+ ERTS_INIT_OFF_HEAP(&cmlcp->oh);
+
+ cmlcp->yield_state = NULL;
+ cmlcp->dist = dist;
+ if (!dist)
+ cmlcp->state = ERTS_CML_CLEANUP_STATE_NODE_MONITORS;
+ else {
+ cmlcp->state = ERTS_CML_CLEANUP_STATE_LINKS;
+ erts_mtx_lock(&dist->mtx);
+ ASSERT(dist->alive);
+ dist->alive = 0;
+ erts_mtx_unlock(&dist->mtx);
+ }
+
+ cmlcp->trigger_node_monitors = is_value(nodename);
+ cmlcp->nodename = nodename;
+ cmlcp->visability = visability;
+ if (rsz == 0)
+ cmlcp->reason = reason;
+ else {
+ Eterm *hp = &cmlcp->heap[0];
+ cmlcp->reason = copy_struct(reason, rsz, &hp, &cmlcp->oh);
+ }
+
+ esdp = erts_get_scheduler_data();
+ ASSERT(esdp && esdp->type == ERTS_SCHED_NORMAL);
+ erts_schedule_misc_aux_work((int) esdp->no,
+ con_monitor_link_cleanup,
+ (void *) cmlcp);
+ }
+}
+
/*
** A full node name constists of a "n@h"
**
@@ -235,170 +390,6 @@ int is_node_name_atom(Eterm a)
return is_node_name((char*)atom_tab(i)->name, atom_tab(i)->len);
}
-typedef struct {
- DistEntry *dep;
- Eterm *lhp;
-} NetExitsContext;
-
-/*
-** This function is called when a distribution
-** port or process terminates
-*/
-static void doit_monitor_net_exits(ErtsMonitor *mon, void *vnecp)
-{
- Process *rp;
- ErtsMonitor *rmon;
- DistEntry *dep = ((NetExitsContext *) vnecp)->dep;
- ErtsProcLocks rp_locks = ERTS_PROC_LOCK_LINK;
-
- rp = erts_pid2proc(NULL, 0, mon->u.pid, rp_locks);
- if (!rp)
- goto done;
-
- if (mon->type == MON_ORIGIN) {
- /* local pid is being monitored */
- rmon = erts_remove_monitor(&ERTS_P_MONITORS(rp), mon->ref);
- /* ASSERT(rmon != NULL); nope, can happen during process exit */
- if (rmon != NULL) {
- erts_destroy_monitor(rmon);
- }
- } else {
- DeclareTmpHeapNoproc(lhp,3);
- Eterm watched;
- UseTmpHeapNoproc(3);
- ASSERT(mon->type == MON_TARGET);
- rmon = erts_remove_monitor(&ERTS_P_MONITORS(rp), mon->ref);
- /* ASSERT(rmon != NULL); can happen during process exit */
- if (rmon != NULL) {
- ASSERT(rmon->type == MON_ORIGIN);
- ASSERT(is_atom(rmon->name) || is_nil(rmon->name));
- watched = (is_atom(rmon->name)
- ? TUPLE2(lhp, rmon->name, dep->sysname)
- : rmon->u.pid);
- rp_locks |= ERTS_PROC_LOCKS_MSG_SEND;
- erts_proc_lock(rp, ERTS_PROC_LOCKS_MSG_SEND);
- erts_queue_monitor_message(rp, &rp_locks, mon->ref, am_process,
- watched, am_noconnection);
- erts_destroy_monitor(rmon);
- }
- UnUseTmpHeapNoproc(3);
- }
- erts_proc_unlock(rp, rp_locks);
- done:
- erts_destroy_monitor(mon);
-}
-
-typedef struct {
- NetExitsContext *necp;
- ErtsLink *lnk;
-} LinkNetExitsContext;
-
-/*
-** This is the function actually doing the job of sending exit messages
-** for links in a dist entry upon net_exit (the node goes down), NB,
-** only process links, not node monitors are handled here,
-** they reside in a separate tree....
-*/
-static void doit_link_net_exits_sub(ErtsLink *sublnk, void *vlnecp)
-{
- ErtsLink *lnk = ((LinkNetExitsContext *) vlnecp)->lnk; /* the local pid */
- ErtsLink *rlnk;
- Process *rp;
-
- ASSERT(lnk->type == LINK_PID);
- if (is_internal_pid(lnk->pid)) {
- int xres;
- ErtsProcLocks rp_locks = ERTS_PROC_LOCK_LINK|ERTS_PROC_LOCKS_XSIG_SEND;
-
- rp = erts_pid2proc(NULL, 0, lnk->pid, rp_locks);
- if (!rp) {
- goto done;
- }
-
- rlnk = erts_remove_link(&ERTS_P_LINKS(rp), sublnk->pid);
- xres = erts_send_exit_signal(NULL,
- sublnk->pid,
- rp,
- &rp_locks,
- am_noconnection,
- NIL,
- NULL,
- 0);
-
- if (rlnk) {
- erts_destroy_link(rlnk);
- if (xres >= 0 && IS_TRACED_FL(rp, F_TRACE_PROCS)) {
- /* We didn't exit the process and it is traced */
- trace_proc(NULL, 0, rp, am_getting_unlinked, sublnk->pid);
- }
- }
- erts_proc_unlock(rp, rp_locks);
- }
- done:
- erts_destroy_link(sublnk);
-
-}
-
-
-
-
-
-/*
-** This function is called when a distribution
-** port or process terminates, once for each link on the high level,
-** it in turn traverses the link subtree for the specific link node...
-*/
-static void doit_link_net_exits(ErtsLink *lnk, void *vnecp)
-{
- LinkNetExitsContext lnec = {(NetExitsContext *) vnecp, lnk};
- ASSERT(lnk->type == LINK_PID);
- erts_sweep_links(ERTS_LINK_ROOT(lnk), &doit_link_net_exits_sub, (void *) &lnec);
-#ifdef DEBUG
- ERTS_LINK_ROOT(lnk) = NULL;
-#endif
- erts_destroy_link(lnk);
-}
-
-
-static void doit_node_link_net_exits(ErtsLink *lnk, void *vnecp)
-{
- DistEntry *dep = ((NetExitsContext *) vnecp)->dep;
- Eterm name = dep->sysname;
- Process *rp;
- ErtsLink *rlnk;
- Uint i,n;
- ASSERT(lnk->type == LINK_NODE);
- if (is_internal_pid(lnk->pid)) {
- ErtsProcLocks rp_locks = ERTS_PROC_LOCK_LINK;
- ErlOffHeap *ohp;
- rp = erts_proc_lookup(lnk->pid);
- if (!rp)
- goto done;
- erts_proc_lock(rp, rp_locks);
- if (!ERTS_PROC_IS_EXITING(rp)) {
- rlnk = erts_remove_link(&ERTS_P_LINKS(rp), name);
- if (rlnk != NULL) {
- ASSERT(is_atom(rlnk->pid) && (rlnk->type == LINK_NODE));
- erts_destroy_link(rlnk);
- }
- n = ERTS_LINK_REFC(lnk);
- for (i = 0; i < n; ++i) {
- Eterm tup;
- Eterm *hp;
- ErtsMessage *msgp;
-
- msgp = erts_alloc_message_heap(rp, &rp_locks,
- 3, &hp, &ohp);
- tup = TUPLE2(hp, am_nodedown, name);
- erts_queue_message(rp, rp_locks, msgp, tup, am_system);
- }
- }
- erts_proc_unlock(rp, rp_locks);
- }
- done:
- erts_destroy_link(lnk);
-}
-
static void
set_node_not_alive(void *unused)
{
@@ -444,15 +435,8 @@ inc_no_nodes(void)
static void
kill_dist_ctrl_proc(void *vpid)
{
- Eterm pid = (Eterm) vpid;
- ErtsProcLocks rp_locks = ERTS_PROC_LOCKS_XSIG_SEND;
- Process *rp = erts_pid2proc(NULL, 0, pid, rp_locks);
- if (rp) {
- erts_send_exit_signal(NULL, rp->common.id, rp, &rp_locks,
- am_kill, NIL, NULL, 0);
- if (rp_locks)
- erts_proc_unlock(rp, rp_locks);
- }
+ erts_proc_sig_send_exit(NULL, (Eterm) vpid, (Eterm) vpid,
+ am_kill, NIL, 0);
}
static void
@@ -572,10 +556,7 @@ int erts_do_net_exits(DistEntry *dep, Eterm reason)
}
}
else { /* Call from distribution controller (port/process) */
- NetExitsContext nec = {dep};
- ErtsLink *nlinks;
- ErtsLink *node_links;
- ErtsMonitor *monitors;
+ ErtsMonLnkDist *mld;
Uint32 flags;
erts_atomic_set_mb(&dep->dist_cmd_scheduled, 1);
@@ -588,27 +569,21 @@ int erts_do_net_exits(DistEntry *dep, Eterm reason)
erts_port_task_abort(&dep->dist_cmd);
}
- if (dep->status & ERTS_DE_SFLG_EXITING) {
+ if (dep->state == ERTS_DE_STATE_EXITING) {
#ifdef DEBUG
ASSERT(erts_atomic32_read_nob(&dep->qflgs) & ERTS_DE_QFLG_EXIT);
#endif
}
else {
- dep->status |= ERTS_DE_SFLG_EXITING;
+ dep->state = ERTS_DE_STATE_EXITING;
erts_mtx_lock(&dep->qlock);
ASSERT(!(erts_atomic32_read_nob(&dep->qflgs) & ERTS_DE_QFLG_EXIT));
erts_atomic32_read_bor_relb(&dep->qflgs, ERTS_DE_QFLG_EXIT);
erts_mtx_unlock(&dep->qlock);
}
- erts_de_links_lock(dep);
- monitors = dep->monitors;
- nlinks = dep->nlinks;
- node_links = dep->node_links;
- dep->monitors = NULL;
- dep->nlinks = NULL;
- dep->node_links = NULL;
- erts_de_links_unlock(dep);
+ mld = dep->mld;
+ dep->mld = NULL;
nodename = dep->sysname;
flags = dep->flags;
@@ -617,15 +592,14 @@ int erts_do_net_exits(DistEntry *dep, Eterm reason)
erts_de_rwunlock(dep);
- erts_sweep_monitors(monitors, &doit_monitor_net_exits, (void *) &nec);
- erts_sweep_links(nlinks, &doit_link_net_exits, (void *) &nec);
- erts_sweep_links(node_links, &doit_node_link_net_exits, (void *) &nec);
-
- send_nodes_mon_msgs(NULL,
- am_nodedown,
- nodename,
- flags & DFLAG_PUBLISHED ? am_visible : am_hidden,
- reason == am_normal ? am_connection_closed : reason);
+ schedule_con_monitor_link_cleanup(mld,
+ nodename,
+ (flags & DFLAG_PUBLISHED
+ ? am_visible
+ : am_hidden),
+ (reason == am_normal
+ ? am_connection_closed
+ : reason));
clear_dist_entry(dep);
}
@@ -641,6 +615,14 @@ trap_function(Eterm func, int arity)
return erts_export_put(am_erlang, func, arity);
}
+/*
+ * Sync with dist_util.erl:
+ *
+ * -record(erts_dflags,
+ * {default, mandatory, addable, rejectable, strict_order}).
+ */
+static Eterm erts_dflags_record;
+
void init_dist(void)
{
init_nodes_monitors();
@@ -653,10 +635,19 @@ void init_dist(void)
/* Lookup/Install all references to trap functions */
dmonitor_node_trap = trap_function(am_dmonitor_node,3);
- dmonitor_p_trap = trap_function(am_dmonitor_p, 2);
dist_ctrl_put_data_trap = erts_export_put(am_erts_internal,
am_dist_ctrl_put_data,
2);
+ {
+ Eterm* hp = erts_alloc(ERTS_ALC_T_LITERAL, (1+6)*sizeof(Eterm));
+ erts_dflags_record = TUPLE6(hp, am_erts_dflags,
+ make_small(DFLAG_DIST_DEFAULT),
+ make_small(DFLAG_DIST_MANDATORY),
+ make_small(DFLAG_DIST_ADDABLE),
+ make_small(DFLAG_DIST_REJECTABLE),
+ make_small(DFLAG_DIST_STRICT_ORDER));
+ erts_set_literal_tag(&erts_dflags_record, hp, (1+6));
+ }
}
#define ErtsDistOutputBuf2Binary(OB) \
@@ -753,21 +744,13 @@ static void clear_dist_entry(DistEntry *dep)
cache = dep->cache;
dep->cache = NULL;
-#ifdef DEBUG
- erts_de_links_lock(dep);
- ASSERT(!dep->nlinks);
- ASSERT(!dep->node_links);
- ASSERT(!dep->monitors);
- erts_de_links_unlock(dep);
-#endif
-
erts_mtx_lock(&dep->qlock);
erts_atomic64_set_nob(&dep->in, 0);
erts_atomic64_set_nob(&dep->out, 0);
obuf = clear_de_out_queues(dep);
- dep->status = 0;
+ dep->state = ERTS_DE_STATE_IDLE;
suspendees = get_suspended_on_de(dep, ERTS_DE_QFLGS_ALL);
erts_mtx_unlock(&dep->qlock);
@@ -869,8 +852,7 @@ erts_dsig_send_unlink(ErtsDSigData *dsdp, Eterm local, Eterm remote)
/* A local process that's being monitored by a remote one exits. We send:
- {DOP_MONITOR_P_EXIT, Local pid or name, Remote pid, ref, reason},
- which is rather sad as only the ref is needed, no pid's... */
+ {DOP_MONITOR_P_EXIT, Local pid or name, Remote pid, ref, reason} */
int
erts_dsig_send_m_exit(ErtsDSigData *dsdp, Eterm watcher, Eterm watched,
Eterm ref, Eterm reason)
@@ -891,12 +873,6 @@ erts_dsig_send_m_exit(ErtsDSigData *dsdp, Eterm watcher, Eterm watched,
ctl = TUPLE5(&ctl_heap[0], make_small(DOP_MONITOR_P_EXIT),
watched, watcher, ref, reason);
-#ifdef DEBUG
- erts_de_links_lock(dsdp->dep);
- ASSERT(!erts_lookup_monitor(dsdp->dep->monitors, ref));
- erts_de_links_unlock(dsdp->dep);
-#endif
-
res = dsig_send_ctl(dsdp, ctl, 1);
UnUseTmpHeapNoproc(6);
return res;
@@ -935,8 +911,7 @@ erts_dsig_send_monitor(ErtsDSigData *dsdp, Eterm watcher, Eterm watched,
/* A local process monitoring a remote one wants to stop monitoring, either
because of a demonitor bif call or because the local process died. We send
- {DOP_DEMONITOR_P, Local pid, Remote pid or name, ref}, which is once again
- rather redundant as only the ref will be needed on the other side... */
+ {DOP_DEMONITOR_P, Local pid, Remote pid or name, ref} */
int
erts_dsig_send_demonitor(ErtsDSigData *dsdp, Eterm watcher,
Eterm watched, Eterm ref, int force)
@@ -1258,8 +1233,6 @@ int erts_net_message(Port *prt,
Sint type;
Eterm token;
Eterm token_size;
- ErtsMonitor *mon;
- ErtsLink *lnk;
Uint tuple_arity;
int res;
Uint32 connection_id;
@@ -1357,88 +1330,89 @@ int erts_net_message(Port *prt,
token = NIL;
switch (type = unsigned_val(tuple[1])) {
- case DOP_LINK:
+ case DOP_LINK: {
+ ErtsDSigData dsd;
+ int code;
+
if (tuple_arity != 3) {
goto invalid_message;
}
from = tuple[2];
to = tuple[3]; /* local proc to link to */
- if (is_not_pid(from) || is_not_pid(to)) {
- goto invalid_message;
- }
+ if (is_not_external_pid(from))
+ goto invalid_message;
- rp = erts_pid2proc_opt(NULL, 0,
- to, ERTS_PROC_LOCK_LINK,
- ERTS_P2P_FLG_ALLOW_OTHER_X);
- if (!rp) {
- /* This is tricky (we MUST force a distributed send) */
- ErtsDSigData dsd;
- int code;
- code = erts_dsig_prepare(&dsd, dep, NULL, 0, ERTS_DSP_NO_LOCK, 0, 0);
- if (code == ERTS_DSIG_PREP_CONNECTED) {
- code = erts_dsig_send_exit(&dsd, to, from, am_noproc);
- ASSERT(code == ERTS_DSIG_SEND_OK);
- }
- break;
- }
+ if (dep != external_pid_dist_entry(from))
+ goto invalid_message;
- erts_de_links_lock(dep);
- res = erts_add_link(&ERTS_P_LINKS(rp), LINK_PID, from);
+ if (is_external_pid(to)) {
+ if (external_pid_dist_entry(to) != erts_this_dist_entry)
+ goto invalid_message;
+ /* old incarnation of node; reply noproc... */
+ }
+ else if (is_internal_pid(to)) {
+ ErtsLinkData *ldp = erts_link_create(ERTS_LNK_TYPE_DIST_PROC,
+ from, to);
+ ASSERT(ldp->a.other.item == to);
+ ASSERT(eq(ldp->b.other.item, from));
+#ifdef DEBUG
+ code =
+#endif
+ erts_link_dist_insert(&ldp->a, dep->mld);
+ ASSERT(code);
- if (res < 0) {
- /* It was already there! Lets skip the rest... */
- erts_de_links_unlock(dep);
- erts_proc_unlock(rp, ERTS_PROC_LOCK_LINK);
- break;
- }
- lnk = erts_add_or_lookup_link(&(dep->nlinks), LINK_PID, rp->common.id);
- erts_add_link(&(ERTS_LINK_ROOT(lnk)), LINK_PID, from);
- erts_de_links_unlock(dep);
+ if (erts_proc_sig_send_link(NULL, to, &ldp->b))
+ break; /* done */
+
+ /* Failed to send signal; cleanup and reply noproc... */
- if (IS_TRACED_FL(rp, F_TRACE_PROCS))
- trace_proc(NULL, 0, rp, am_getting_linked, from);
+#ifdef DEBUG
+ code =
+#endif
+ erts_link_dist_delete(&ldp->a);
+ ASSERT(code);
+ erts_link_release_both(ldp);
+ }
+
+ code = erts_dsig_prepare(&dsd, dep, NULL, 0, ERTS_DSP_NO_LOCK, 0, 0);
+ if (code == ERTS_DSIG_PREP_CONNECTED) {
+ code = erts_dsig_send_exit(&dsd, to, from, am_noproc);
+ ASSERT(code == ERTS_DSIG_SEND_OK);
+ }
- erts_proc_unlock(rp, ERTS_PROC_LOCK_LINK);
break;
+ }
case DOP_UNLINK: {
- ErtsDistLinkData dld;
if (tuple_arity != 3) {
goto invalid_message;
}
from = tuple[2];
to = tuple[3];
- if (is_not_pid(from) || is_not_pid(to)) {
+ if (is_not_external_pid(from))
+ goto invalid_message;
+ if (dep != external_pid_dist_entry(from))
goto invalid_message;
- }
-
- rp = erts_pid2proc_opt(NULL, 0,
- to, ERTS_PROC_LOCK_LINK,
- ERTS_P2P_FLG_ALLOW_OTHER_X);
- if (!rp)
- break;
-
- lnk = erts_remove_link(&ERTS_P_LINKS(rp), from);
- if (IS_TRACED_FL(rp, F_TRACE_PROCS) && lnk != NULL) {
- trace_proc(NULL, 0, rp, am_getting_unlinked, from);
- }
+ if (is_external_pid(to)
+ && erts_this_dist_entry == external_pid_dist_entry(from))
+ break;
- erts_proc_unlock(rp, ERTS_PROC_LOCK_LINK);
+ if (is_not_internal_pid(to))
+ goto invalid_message;
- erts_remove_dist_link(&dld, to, from, dep);
- erts_destroy_dist_link(&dld);
- if (lnk)
- erts_destroy_link(lnk);
+ erts_proc_sig_send_dist_unlink(dep, from, to);
break;
}
case DOP_MONITOR_P: {
/* A remote process wants to monitor us, we get:
{DOP_MONITOR_P, Remote pid, local pid or name, ref} */
- Eterm name;
-
+ Eterm pid, name;
+ ErtsDSigData dsd;
+ int code;
+
if (tuple_arity != 4) {
goto invalid_message;
}
@@ -1447,44 +1421,64 @@ int erts_net_message(Port *prt,
watched = tuple[3]; /* local proc to monitor */
ref = tuple[4];
- if (is_not_ref(ref)) {
+ if (is_not_external_pid(watcher))
+ goto invalid_message;
+ else if (external_pid_dist_entry(watcher) != dep)
+ goto invalid_message;
+
+ if (is_not_ref(ref))
goto invalid_message;
- }
- if (is_atom(watched)) {
- name = watched;
- rp = erts_whereis_process(NULL, 0,
- watched, ERTS_PROC_LOCK_LINK,
- ERTS_P2P_FLG_ALLOW_OTHER_X);
- }
- else {
- name = NIL;
- rp = erts_pid2proc_opt(NULL, 0,
- watched, ERTS_PROC_LOCK_LINK,
- ERTS_P2P_FLG_ALLOW_OTHER_X);
- }
+ if (is_internal_pid(watched)) {
+ name = NIL;
+ pid = watched;
+ }
+ else if (is_atom(watched)) {
+ name = watched;
+ pid = erts_whereis_name_to_id(NULL, watched);
+ /* if port or undefined; reply noproc... */
+ }
+ else if (is_external_pid(watched)
+ && external_pid_dist_entry(watched) == erts_this_dist_entry) {
+ name = NIL;
+ pid = am_undefined; /* old incarnation; reply noproc... */
+ }
+ else
+ goto invalid_message;
- if (!rp) {
- ErtsDSigData dsd;
- int code;
- code = erts_dsig_prepare(&dsd, dep, NULL, 0, ERTS_DSP_NO_LOCK, 0, 0);
- if (code == ERTS_DSIG_PREP_CONNECTED) {
- code = erts_dsig_send_m_exit(&dsd, watcher, watched, ref,
- am_noproc);
- ASSERT(code == ERTS_DSIG_SEND_OK);
- }
- }
- else {
- if (is_atom(watched))
- watched = rp->common.id;
- erts_de_links_lock(dep);
- erts_add_monitor(&(dep->monitors), MON_ORIGIN, ref, watched, name);
- erts_add_monitor(&ERTS_P_MONITORS(rp), MON_TARGET, ref, watcher, name);
- erts_de_links_unlock(dep);
- erts_proc_unlock(rp, ERTS_PROC_LOCK_LINK);
- }
+ if (is_internal_pid(pid)) {
+ ErtsMonitorData *mdp;
+ mdp = erts_monitor_create(ERTS_MON_TYPE_DIST_PROC,
+ ref, watcher, pid, name);
- break;
+#ifdef DEBUG
+ code =
+#endif
+ erts_monitor_dist_insert(&mdp->origin, dep->mld);
+ ASSERT(code);
+
+ if (erts_proc_sig_send_monitor(&mdp->target, pid))
+ break; /* done */
+
+ /* Failed to send to local proc; cleanup reply noproc... */
+
+#ifdef DEBUG
+ code =
+#endif
+ erts_monitor_dist_delete(&mdp->origin);
+ ASSERT(code);
+ erts_monitor_release_both(mdp);
+
+ }
+
+ code = erts_dsig_prepare(&dsd, dep, NULL, 0, ERTS_DSP_NO_LOCK, 0, 0);
+ if (code == ERTS_DSIG_PREP_CONNECTED) {
+ code = erts_dsig_send_m_exit(&dsd, watcher, watched, ref,
+ am_noproc);
+ ASSERT(code == ERTS_DSIG_SEND_OK);
+ }
+
+ break;
}
case DOP_DEMONITOR_P:
@@ -1495,36 +1489,43 @@ int erts_net_message(Port *prt,
if (tuple_arity != 4) {
goto invalid_message;
}
- /* watcher = tuple[2]; */
- /* watched = tuple[3]; May be an atom in case of monitor name */
+
+ watcher = tuple[2];
+ watched = tuple[3];
ref = tuple[4];
- if(is_not_ref(ref)) {
+ if (is_not_ref(ref)) {
goto invalid_message;
}
- erts_de_links_lock(dep);
- mon = erts_remove_monitor(&(dep->monitors),ref);
- erts_de_links_unlock(dep);
- /* ASSERT(mon != NULL); can happen in case of broken dist message */
- if (mon == NULL) {
- break;
- }
- watched = mon->u.pid;
- erts_destroy_monitor(mon);
- rp = erts_pid2proc_opt(NULL, 0,
- watched, ERTS_PROC_LOCK_LINK,
- ERTS_P2P_FLG_ALLOW_OTHER_X);
- if (!rp) {
- break;
- }
- mon = erts_remove_monitor(&ERTS_P_MONITORS(rp), ref);
- erts_proc_unlock(rp, ERTS_PROC_LOCK_LINK);
- ASSERT(mon != NULL);
- if (mon == NULL) {
- break;
- }
- erts_destroy_monitor(mon);
+ if (is_not_external_pid(watcher) || external_pid_dist_entry(watcher) != dep)
+ goto invalid_message;
+
+ if (is_internal_pid(watched))
+ erts_proc_sig_send_dist_demonitor(watched, ref);
+ else if (is_external_pid(watched)
+ && external_pid_dist_entry(watched) == erts_this_dist_entry) {
+ /* old incarnation; ignore it */
+ ;
+ }
+ else if (is_atom(watched)) {
+ ErtsMonLnkDist *mld = dep->mld;
+ ErtsMonitor *mon;
+
+ erts_mtx_lock(&mld->mtx);
+
+ mon = erts_monitor_tree_lookup(mld->orig_name_monitors, ref);
+ if (mon)
+ erts_monitor_tree_delete(&mld->orig_name_monitors, mon);
+
+ erts_mtx_unlock(&mld->mtx);
+
+ if (mon)
+ erts_proc_sig_send_demonitor(mon);
+ }
+ else
+ goto invalid_message;
+
break;
case DOP_REG_SEND_TT:
@@ -1651,68 +1652,36 @@ int erts_net_message(Port *prt,
/* We are monitoring a process on the remote node which dies, we get
{DOP_MONITOR_P_EXIT, Remote pid or name, Local pid, ref, reason} */
-
- DeclareTmpHeapNoproc(lhp,3);
- Eterm sysname;
- ErtsProcLocks rp_locks = ERTS_PROC_LOCKS_MSG_SEND|ERTS_PROC_LOCK_LINK;
-
if (tuple_arity != 5) {
goto invalid_message;
}
- /* watched = tuple[2]; */ /* remote proc which died */
- /* watcher = tuple[3]; */
+ watched = tuple[2]; /* remote proc or name which died */
+ watcher = tuple[3];
ref = tuple[4];
reason = tuple[5];
- if(is_not_ref(ref)) {
+ if (is_not_ref(ref))
goto invalid_message;
- }
- erts_de_links_lock(dep);
- sysname = dep->sysname;
- mon = erts_remove_monitor(&(dep->monitors), ref);
- /*
- * If demonitor was performed at the same time as the
- * monitored process exits, monitoring side will have
- * removed info about monitor. In this case, do nothing
- * and everything will be as it should.
- */
- erts_de_links_unlock(dep);
- if (mon == NULL) {
- break;
- }
- rp = erts_pid2proc(NULL, 0, mon->u.pid, rp_locks);
+ if (is_not_external_pid(watched) && is_not_atom(watched))
+ goto invalid_message;
- erts_destroy_monitor(mon);
- if (rp == NULL) {
- break;
- }
-
- mon = erts_remove_monitor(&ERTS_P_MONITORS(rp), ref);
+ if (is_not_internal_pid(watcher)) {
+ if (!is_external_pid(watcher))
+ goto invalid_message;
+ if (erts_this_dist_entry == external_pid_dist_entry(watcher))
+ break;
+ goto invalid_message;
+ }
- if (mon == NULL) {
- erts_proc_unlock(rp, rp_locks);
- break;
- }
- UseTmpHeapNoproc(3);
-
- watched = (is_not_nil(mon->name)
- ? TUPLE2(&lhp[0], mon->name, sysname)
- : mon->u.pid);
-
- erts_queue_monitor_message(rp, &rp_locks,
- ref, am_process, watched, reason);
- erts_proc_unlock(rp, rp_locks);
- erts_destroy_monitor(mon);
- UnUseTmpHeapNoproc(3);
+ erts_proc_sig_send_dist_monitor_down(dep, ref, watched,
+ watcher, reason);
break;
}
case DOP_EXIT_TT:
case DOP_EXIT: {
- ErtsDistLinkData dld;
- ErtsProcLocks rp_locks = ERTS_PROC_LOCK_LINK|ERTS_PROC_LOCKS_XSIG_SEND;
/* 'from', which 'to' is linked to, died */
if (type == DOP_EXIT) {
if (tuple_arity != 4) {
@@ -1732,56 +1701,19 @@ int erts_net_message(Port *prt,
token = tuple[4];
reason = tuple[5];
}
- if (is_not_pid(from) || is_not_internal_pid(to)) {
+ if (is_not_external_pid(from)
+ || dep != external_pid_dist_entry(from)
+ || is_not_internal_pid(to)) {
goto invalid_message;
}
- rp = erts_pid2proc(NULL, 0, to, rp_locks);
- if (!rp)
- lnk = NULL;
- else {
- lnk = erts_remove_link(&ERTS_P_LINKS(rp), from);
-
- /* If lnk == NULL, we have unlinked on this side, i.e.
- * ignore exit.
- */
- if (lnk) {
- int xres;
-#if 0
- /* Arndt: Maybe it should never be 'kill', but it can be,
- namely when a linked process does exit(kill). Until we know
- whether that is incorrect and what should happen instead,
- we leave the assertion out. */
- ASSERT(reason != am_kill); /* should never be kill (killed) */
-#endif
- xres = erts_send_exit_signal(NULL,
- from,
- rp,
- &rp_locks,
- reason,
- token,
- NULL,
- ERTS_XSIG_FLG_IGN_KILL);
- if (xres >= 0 && IS_TRACED_FL(rp, F_TRACE_PROCS)) {
- /* We didn't exit the process and it is traced */
- if (rp_locks & ERTS_PROC_LOCKS_XSIG_SEND) {
- erts_proc_unlock(rp, ERTS_PROC_LOCKS_XSIG_SEND);
- rp_locks &= ~ERTS_PROC_LOCKS_XSIG_SEND;
- }
- trace_proc(NULL, 0, rp, am_getting_unlinked, from);
- }
- }
- erts_proc_unlock(rp, rp_locks);
- }
- erts_remove_dist_link(&dld, to, from, dep);
- if (lnk)
- erts_destroy_link(lnk);
- erts_destroy_dist_link(&dld);
+ erts_proc_sig_send_dist_link_exit(dep,
+ from, to,
+ reason, token);
break;
}
case DOP_EXIT2_TT:
- case DOP_EXIT2: {
- ErtsProcLocks rp_locks = ERTS_PROC_LOCKS_XSIG_SEND;
+ case DOP_EXIT2:
/* 'from' is send an exit signal to 'to' */
if (type == DOP_EXIT2) {
if (tuple_arity != 4) {
@@ -1803,20 +1735,10 @@ int erts_net_message(Port *prt,
if (is_not_pid(from) || is_not_internal_pid(to)) {
goto invalid_message;
}
- rp = erts_pid2proc(NULL, 0, to, rp_locks);
- if (rp) {
- (void) erts_send_exit_signal(NULL,
- from,
- rp,
- &rp_locks,
- reason,
- token,
- NULL,
- 0);
- erts_proc_unlock(rp, rp_locks);
- }
+
+ erts_proc_sig_send_exit(NULL, from, to, reason, token, 0);
break;
- }
+
case DOP_GROUP_LEADER:
if (tuple_arity != 3) {
goto invalid_message;
@@ -1827,11 +1749,7 @@ int erts_net_message(Port *prt,
goto invalid_message;
}
- rp = erts_pid2proc(NULL, 0, to, ERTS_PROC_LOCK_MAIN);
- if (!rp)
- break;
- rp->group_leader = STORE_NC_IN_PROC(rp, from);
- erts_proc_unlock(rp, ERTS_PROC_LOCK_MAIN);
+ (void) erts_proc_sig_send_group_leader(NULL, to, from, NIL);
break;
default:
@@ -1982,6 +1900,7 @@ erts_dsig_send(ErtsDSigData *dsdp, struct erts_dsig_send_context* ctx)
break;
}
ctx->u.ec.flags = ctx->flags;
+ ctx->u.ec.hopefull_flags = 0;
ctx->u.ec.level = 0;
ctx->u.ec.wstack.wstart = NULL;
ctx->obuf->msg_start = ctx->obuf->ext_endp;
@@ -2005,6 +1924,7 @@ erts_dsig_send(ErtsDSigData *dsdp, struct erts_dsig_send_context* ctx)
ctx->data_size = ctx->obuf->ext_endp - ctx->obuf->extp;
+ ctx->obuf->hopefull_flags = ctx->u.ec.hopefull_flags;
/*
* Signal encoded; now verify that the connection still exists,
* and if so enqueue the signal and schedule it for send.
@@ -2012,8 +1932,8 @@ erts_dsig_send(ErtsDSigData *dsdp, struct erts_dsig_send_context* ctx)
ctx->obuf->next = NULL;
erts_de_rlock(dep);
cid = dep->cid;
- if (!(dep->status & (ERTS_DE_SFLG_PENDING | ERTS_DE_SFLG_CONNECTED))
- || dep->status & ERTS_DE_SFLG_EXITING
+ if (dep->state == ERTS_DE_STATE_EXITING
+ || dep->state == ERTS_DE_STATE_IDLE
|| dep->connection_id != dsdp->connection_id) {
/* Not the same connection as when we started; drop message... */
erts_de_runlock(dep);
@@ -2088,7 +2008,7 @@ erts_dsig_send(ErtsDSigData *dsdp, struct erts_dsig_send_context* ctx)
}
erts_mtx_unlock(&dep->qlock);
- if (!(dep->status & ERTS_DE_SFLG_PENDING)) {
+ if (dep->state != ERTS_DE_STATE_PENDING) {
if (is_internal_port(dep->cid))
erts_schedule_dist_command(NULL, dep);
}
@@ -2270,7 +2190,7 @@ int
erts_dist_command(Port *prt, int initial_reds)
{
Sint reds = initial_reds - ERTS_PORT_REDS_DIST_CMD_START;
- Uint32 status;
+ enum dist_entry_state state;
Uint32 flags;
Sint qsize, obufsize = 0;
ErtsDistOutputQueue oq, foq;
@@ -2285,17 +2205,17 @@ erts_dist_command(Port *prt, int initial_reds)
erts_de_rlock(dep);
flags = dep->flags;
- status = dep->status;
+ state = dep->state;
send = dep->send;
erts_de_runlock(dep);
- if (status & ERTS_DE_SFLG_EXITING) {
+ if (state == ERTS_DE_STATE_EXITING) {
erts_deliver_port_exit(prt, prt->common.id, am_killed, 0, 1);
reds -= ERTS_PORT_REDS_DIST_CMD_EXIT;
return initial_reds - reds;
}
- ASSERT(!(status & ERTS_DE_SFLG_PENDING));
+ ASSERT(state != ERTS_DE_STATE_PENDING);
ASSERT(send);
@@ -2831,7 +2751,7 @@ dist_ctrl_get_data_1(BIF_ALIST_1)
erts_de_rlock(dep);
- if (dep->status & ERTS_DE_SFLG_EXITING)
+ if (dep->state == ERTS_DE_STATE_EXITING)
goto return_none;
ASSERT(dep->cid == BIF_P->common.id);
@@ -2934,9 +2854,9 @@ erts_dist_port_not_busy(Port *prt)
static void kill_connection(DistEntry *dep)
{
ERTS_LC_ASSERT(erts_lc_is_de_rwlocked(dep));
- ASSERT(dep->status == ERTS_DE_SFLG_CONNECTED);
+ ASSERT(dep->state == ERTS_DE_STATE_CONNECTED);
- dep->status |= ERTS_DE_SFLG_EXITING;
+ dep->state = ERTS_DE_STATE_EXITING;
erts_mtx_lock(&dep->qlock);
ASSERT(!(erts_atomic32_read_nob(&dep->qflgs) & ERTS_DE_QFLG_EXIT));
erts_atomic32_read_bor_nob(&dep->qflgs, ERTS_DE_QFLG_EXIT);
@@ -2953,7 +2873,7 @@ erts_kill_dist_connection(DistEntry *dep, Uint32 connection_id)
{
erts_de_rwlock(dep);
if (connection_id == dep->connection_id
- && dep->status == ERTS_DE_SFLG_CONNECTED) {
+ && dep->state == ERTS_DE_STATE_CONNECTED) {
kill_connection(dep);
}
@@ -2969,57 +2889,55 @@ static void doit_print_monitor_info(ErtsMonitor *mon, void *vptdp)
{
fmtfn_t to = ((struct print_to_data *) vptdp)->to;
void *arg = ((struct print_to_data *) vptdp)->arg;
- Process *rp;
- ErtsMonitor *rmon;
- rp = erts_proc_lookup(mon->u.pid);
- if (!rp || (rmon = erts_lookup_monitor(ERTS_P_MONITORS(rp), mon->ref)) == NULL) {
- erts_print(to, arg, "Warning, stray monitor for: %T\n", mon->u.pid);
- } else if (mon->type == MON_ORIGIN) {
- /* Local pid is being monitored */
+ ErtsMonitorDataExtended *mdep;
+
+ ASSERT(mon->flags & ERTS_ML_FLG_EXTENDED);
+
+ mdep = (ErtsMonitorDataExtended *) erts_monitor_to_data(mon);
+
+ ASSERT(mdep->dist);
+
+ if (erts_monitor_is_origin(mon)) {
erts_print(to, arg, "Remotely monitored by: %T %T\n",
- mon->u.pid, rmon->u.pid);
- } else {
- erts_print(to, arg, "Remote monitoring: %T ", mon->u.pid);
- if (is_not_atom(rmon->u.pid))
- erts_print(to, arg, "%T\n", rmon->u.pid);
- else
- erts_print(to, arg, "{%T, %T}\n",
- rmon->name,
- rmon->u.pid); /* which in this case is the
- remote system name... */
+ mon->other.item, mdep->md.target.other.item);
+ }
+ else {
+ erts_print(to, arg, "Remote monitoring: %T ", mon->other.item);
+ if (mon->flags & ERTS_ML_FLG_NAME)
+ erts_print(to, arg, "{%T, %T}\n", mdep->u.name, mdep->dist->nodename);
+ else
+ erts_print(to, arg, "%T\n", mdep->md.origin.other.item);
}
}
-static void print_monitor_info(fmtfn_t to, void *arg, ErtsMonitor *mon)
+static void print_monitor_info(fmtfn_t to, void *arg, DistEntry *dep)
{
struct print_to_data ptd = {to, arg};
- erts_doforall_monitors(mon,&doit_print_monitor_info,&ptd);
-}
-
-typedef struct {
- struct print_to_data *ptdp;
- Eterm from;
-} PrintLinkContext;
-
-static void doit_print_link_info2(ErtsLink *lnk, void *vpplc)
-{
- PrintLinkContext *pplc = (PrintLinkContext *) vpplc;
- erts_print(pplc->ptdp->to, pplc->ptdp->arg, "Remote link: %T %T\n",
- pplc->from, lnk->pid);
+ if (dep->mld) {
+ erts_monitor_list_foreach(dep->mld->monitors,
+ doit_print_monitor_info,
+ (void *) &ptd);
+ erts_monitor_tree_foreach(dep->mld->orig_name_monitors,
+ doit_print_monitor_info,
+ (void *) &ptd);
+ }
}
static void doit_print_link_info(ErtsLink *lnk, void *vptdp)
{
- if (is_internal_pid(lnk->pid) && erts_proc_lookup(lnk->pid)) {
- PrintLinkContext plc = {(struct print_to_data *) vptdp, lnk->pid};
- erts_doforall_links(ERTS_LINK_ROOT(lnk), &doit_print_link_info2, &plc);
- }
+ struct print_to_data *ptdp = vptdp;
+ ErtsLink *lnk2 = erts_link_to_other(lnk, NULL);
+ erts_print(ptdp->to, ptdp->arg, "Remote link: %T %T\n",
+ lnk2->other.item, lnk->other.item);
}
-static void print_link_info(fmtfn_t to, void *arg, ErtsLink *lnk)
+static void print_link_info(fmtfn_t to, void *arg, DistEntry *dep)
{
struct print_to_data ptd = {to, arg};
- erts_doforall_links(lnk, &doit_print_link_info, (void *) &ptd);
+ if (dep->mld)
+ erts_link_list_foreach(dep->mld->links,
+ doit_print_link_info,
+ (void *) &ptd);
}
typedef struct {
@@ -3027,23 +2945,6 @@ typedef struct {
Eterm sysname;
} PrintNodeLinkContext;
-
-static void doit_print_nodelink_info(ErtsLink *lnk, void *vpcontext)
-{
- PrintNodeLinkContext *pcontext = vpcontext;
-
- if (is_internal_pid(lnk->pid) && erts_proc_lookup(lnk->pid))
- erts_print(pcontext->ptd.to, pcontext->ptd.arg,
- "Remote monitoring: %T %T\n", lnk->pid, pcontext->sysname);
-}
-
-static void print_nodelink_info(fmtfn_t to, void *arg, ErtsLink *lnk, Eterm sysname)
-{
- PrintNodeLinkContext context = {{to, arg}, sysname};
- erts_doforall_links(lnk, &doit_print_nodelink_info, &context);
-}
-
-
static int
info_dist_entry(fmtfn_t to, void *arg, DistEntry *dep, int visible, int connected)
{
@@ -3074,8 +2975,8 @@ info_dist_entry(fmtfn_t to, void *arg, DistEntry *dep, int visible, int connecte
erts_print(to, arg, "Name: %T", dep->sysname);
erts_print(to, arg, "\n");
if (!connected && is_nil(dep->cid)) {
- if (dep->nlinks) {
- erts_print(to, arg, "Error: Got links to not connected node:%T\n",
+ if (dep->mld) {
+ erts_print(to, arg, "Error: Got links/monitors to not connected node:%T\n",
dep->sysname);
}
return 0;
@@ -3084,9 +2985,8 @@ info_dist_entry(fmtfn_t to, void *arg, DistEntry *dep, int visible, int connecte
erts_print(to, arg, "Controller: %T\n", dep->cid, to);
erts_print_node_info(to, arg, dep->sysname, NULL, NULL);
- print_monitor_info(to, arg, dep->monitors);
- print_link_info(to, arg, dep->nlinks);
- print_nodelink_info(to, arg, dep->node_links, dep->sysname);
+ print_monitor_info(to, arg, dep);
+ print_link_info(to, arg, dep);
return 0;
@@ -3173,8 +3073,7 @@ BIF_RETTYPE setnode_2(BIF_ALIST_2)
goto error;
/* Check that all trap functions are defined !! */
- if (dmonitor_node_trap->addressv[0] == NULL ||
- dmonitor_p_trap->addressv[0] == NULL) {
+ if (dmonitor_node_trap->addressv[0] == NULL) {
goto error;
}
@@ -3341,7 +3240,7 @@ BIF_RETTYPE setnode_3(BIF_ALIST_3)
goto badarg;
}
- if (dep->status & ERTS_DE_SFLG_EXITING) {
+ if (dep->state == ERTS_DE_STATE_EXITING) {
/* Suspend on dist entry waiting for the exit to finish */
ErtsProcList *plp = erts_proclist_create(BIF_P);
plp->next = NULL;
@@ -3351,8 +3250,8 @@ BIF_RETTYPE setnode_3(BIF_ALIST_3)
erts_mtx_unlock(&dep->qlock);
goto yield;
}
- if (dep->status != ERTS_DE_SFLG_PENDING) {
- if (dep->status == 0)
+ if (dep->state != ERTS_DE_STATE_PENDING) {
+ if (dep->state == ERTS_DE_STATE_IDLE)
erts_set_dist_entry_pending(dep);
else
goto badarg;
@@ -3390,7 +3289,7 @@ BIF_RETTYPE setnode_3(BIF_ALIST_3)
goto done; /* Already set */
}
- if (dep->status & ERTS_DE_SFLG_EXITING) {
+ if (dep->state == ERTS_DE_STATE_EXITING) {
/* Suspend on dist entry waiting for the exit to finish */
ErtsProcList *plp = erts_proclist_create(BIF_P);
plp->next = NULL;
@@ -3400,8 +3299,8 @@ BIF_RETTYPE setnode_3(BIF_ALIST_3)
erts_mtx_unlock(&dep->qlock);
goto yield;
}
- if (dep->status != ERTS_DE_SFLG_PENDING) {
- if (dep->status == 0)
+ if (dep->state != ERTS_DE_STATE_PENDING) {
+ if (dep->state == ERTS_DE_STATE_IDLE)
erts_set_dist_entry_pending(dep);
else
goto badarg;
@@ -3437,7 +3336,7 @@ BIF_RETTYPE setnode_3(BIF_ALIST_3)
#ifdef DEBUG
ASSERT(erts_atomic_read_nob(&dep->qsize) == 0
- || (dep->status & ERTS_DE_SFLG_PENDING));
+ || (dep->state == ERTS_DE_STATE_PENDING));
#endif
if (flags & DFLAG_DIST_HDR_ATOM_CACHE)
@@ -3506,6 +3405,11 @@ BIF_RETTYPE setnode_3(BIF_ALIST_3)
goto done;
}
+BIF_RETTYPE erts_internal_get_dflags_0(BIF_ALIST_0)
+{
+ return erts_dflags_record;
+}
+
BIF_RETTYPE erts_internal_new_connection_1(BIF_ALIST_1)
{
DistEntry* dep;
@@ -3525,15 +3429,20 @@ BIF_RETTYPE erts_internal_new_connection_1(BIF_ALIST_1)
erts_de_rwlock(dep);
- if (ERTS_DE_IS_CONNECTED(dep) || dep->status & ERTS_DE_SFLG_PENDING)
+ switch (dep->state) {
+ case ERTS_DE_STATE_PENDING:
+ case ERTS_DE_STATE_CONNECTED:
conn_id = dep->connection_id;
- else if (dep->status == 0) {
+ break;
+ case ERTS_DE_STATE_IDLE:
erts_set_dist_entry_pending(dep);
conn_id = dep->connection_id;
- }
- else {
- ASSERT(dep->status & ERTS_DE_SFLG_EXITING);
+ break;
+ case ERTS_DE_STATE_EXITING:
conn_id = (dep->connection_id + 1) & ERTS_DIST_CON_ID_MASK;
+ break;
+ default:
+ erts_exit(ERTS_ABORT_EXIT, "Invalid dep->state (%d)\n", dep->state);
}
erts_de_rwunlock(dep);
hp = HAlloc(BIF_P, 3 + ERTS_MAGIC_REF_THING_SIZE);
@@ -3548,29 +3457,20 @@ static Sint abort_connection(DistEntry* dep, Uint32 conn_id)
if (dep->connection_id != conn_id)
;
- else if (dep->status == ERTS_DE_SFLG_CONNECTED) {
+ else if (dep->state == ERTS_DE_STATE_CONNECTED) {
kill_connection(dep);
}
- else if (dep->status == ERTS_DE_SFLG_PENDING) {
- NetExitsContext nec = {dep};
- ErtsLink *nlinks;
- ErtsLink *node_links;
- ErtsMonitor *monitors;
+ else if (dep->state == ERTS_DE_STATE_PENDING) {
ErtsAtomCache *cache;
ErtsDistOutputBuf *obuf;
ErtsProcList *resume_procs;
Sint reds = 0;
+ ErtsMonLnkDist *mld;
ASSERT(is_nil(dep->cid));
- erts_de_links_lock(dep);
- monitors = dep->monitors;
- nlinks = dep->nlinks;
- node_links = dep->node_links;
- dep->monitors = NULL;
- dep->nlinks = NULL;
- dep->node_links = NULL;
- erts_de_links_unlock(dep);
+ mld = dep->mld;
+ dep->mld = NULL;
cache = dep->cache;
dep->cache = NULL;
@@ -3589,9 +3489,8 @@ static Sint abort_connection(DistEntry* dep, Uint32 conn_id)
erts_de_rwunlock(dep);
- erts_sweep_monitors(monitors, &doit_monitor_net_exits, &nec);
- erts_sweep_links(nlinks, &doit_link_net_exits, &nec);
- erts_sweep_links(node_links, &doit_node_link_net_exits, &nec);
+ schedule_con_monitor_link_cleanup(mld, THE_NON_VALUE,
+ THE_NON_VALUE, THE_NON_VALUE);
if (resume_procs) {
int resumed = erts_resume_processes(resume_procs);
@@ -3600,6 +3499,17 @@ static Sint abort_connection(DistEntry* dep, Uint32 conn_id)
delete_cache(cache);
free_de_out_queues(dep, obuf);
+
+ /*
+ * We wait to make DistEntry idle and accept new connection attempts
+ * until all is cleared and deallocated. This to get some back pressure
+ * against repeated failing connection attempts saturating all CPUs
+ * with cleanup jobs.
+ */
+ erts_de_rwlock(dep);
+ ASSERT(dep->state == ERTS_DE_STATE_EXITING);
+ dep->state = ERTS_DE_STATE_IDLE;
+ erts_de_rwunlock(dep);
return reds;
}
erts_de_rwunlock(dep);
@@ -3631,7 +3541,7 @@ BIF_RETTYPE erts_internal_abort_connection_2(BIF_ALIST_2)
int erts_auto_connect(DistEntry* dep, Process *proc, ErtsProcLocks proc_locks)
{
erts_de_rwlock(dep);
- if (dep->status != 0) {
+ if (dep->state != ERTS_DE_STATE_IDLE) {
erts_de_rwunlock(dep);
}
else {
@@ -3820,8 +3730,8 @@ BIF_RETTYPE is_alive_0(BIF_ALIST_0)
static BIF_RETTYPE
monitor_node(Process* p, Eterm Node, Eterm Bool, Eterm Options)
{
- DistEntry *dep;
- ErtsLink *lnk;
+ BIF_RETTYPE ret;
+ DistEntry *dep = NULL;
Eterm l;
int async_connect = 1;
@@ -3840,33 +3750,71 @@ monitor_node(Process* p, Eterm Node, Eterm Bool, Eterm Options)
if (l != NIL) {
BIF_ERROR(p, BADARG);
}
+ if (l != NIL)
+ goto badarg;
- if (is_not_atom(Node) ||
- ((Bool != am_true) && (Bool != am_false)) ||
- ((erts_this_node->sysname == am_Noname)
- && (Node != erts_this_node->sysname))) {
- BIF_ERROR(p, BADARG);
+ if (is_not_atom(Node))
+ goto badarg;
+
+ if (erts_this_node->sysname == am_Noname && Node != am_Noname)
+ goto badarg;
+
+ switch (Bool) {
+
+ case am_false: {
+ ErtsMonitor *mon;
+ /*
+ * Before OTP-21, monitor_node(Node, false) triggered
+ * auto-connect and a 'nodedown' message if that failed.
+ * Now it's a simple no-op which feels more reasonable.
+ */
+ mon = erts_monitor_tree_lookup(ERTS_P_MONITORS(p), Node);
+ if (mon) {
+ ErtsMonitorDataExtended *mdep;
+ ASSERT(erts_monitor_is_origin(mon));
+
+ mdep = (ErtsMonitorDataExtended *) erts_monitor_to_data(mon);
+
+ ASSERT((mdep->u.refc > 0));
+ if (--mdep->u.refc == 0) {
+ if (!mdep->uptr.node_monitors)
+ erts_monitor_tree_delete(&ERTS_P_MONITORS(p), mon);
+ else {
+ ErtsMonitor *sub_mon;
+ ErtsMonitorDataExtended *sub_mdep;
+ sub_mon = erts_monitor_list_last(mdep->uptr.node_monitors);
+ erts_monitor_list_delete(&mdep->uptr.node_monitors, sub_mon);
+ sub_mon->flags &= ~ERTS_ML_FLG_IN_SUBTABLE;
+ sub_mdep = ((ErtsMonitorDataExtended *)
+ erts_monitor_to_data(sub_mon));
+ sub_mdep->uptr.node_monitors = mdep->uptr.node_monitors;
+ mdep->uptr.node_monitors = NULL;
+ erts_monitor_tree_replace(&ERTS_P_MONITORS(p), mon, sub_mon);
+ }
+ if (erts_monitor_dist_delete(&mdep->md.target))
+ erts_monitor_release_both((ErtsMonitorData *) mdep);
+ else
+ erts_monitor_release(mon);
+ }
+ }
+ break;
}
- if (Bool == am_true) {
+ case am_true: {
ErtsDSigData dsd;
dsd.node = Node;
+
dep = erts_find_or_insert_dist_entry(Node);
if (dep == erts_this_dist_entry)
- goto done;
-
- erts_proc_lock(p, ERTS_PROC_LOCK_LINK);
+ break;
switch (erts_dsig_prepare(&dsd, dep, p,
- (ERTS_PROC_LOCK_MAIN | ERTS_PROC_LOCK_LINK),
+ ERTS_PROC_LOCK_MAIN,
ERTS_DSP_RLOCK, 0, async_connect)) {
case ERTS_DSIG_PREP_NOT_ALIVE:
case ERTS_DSIG_PREP_NOT_CONNECTED:
/* Trap to either send 'nodedown' or do passive connection attempt */
- trap:
- erts_proc_unlock(p, ERTS_PROC_LOCK_LINK);
- erts_deref_dist_entry(dep);
- BIF_TRAP3(dmonitor_node_trap, p, Node, Bool, Options);
+ goto do_trap;
case ERTS_DSIG_PREP_PENDING:
if (!async_connect) {
/*
@@ -3874,67 +3822,87 @@ monitor_node(Process* p, Eterm Node, Eterm Bool, Eterm Options)
* to ensure passive connection attempt
*/
erts_de_runlock(dep);
- goto trap;
+ goto do_trap;
}
/*fall through*/
- case ERTS_DSIG_PREP_CONNECTED:
- erts_de_links_lock(dep);
- erts_de_runlock(dep);
- lnk = erts_add_or_lookup_link(&(dep->node_links), LINK_NODE,
- p->common.id);
- ++ERTS_LINK_REFC(lnk);
- lnk = erts_add_or_lookup_link(&ERTS_P_LINKS(p), LINK_NODE, Node);
- ++ERTS_LINK_REFC(lnk);
- erts_de_links_unlock(dep);
+ case ERTS_DSIG_PREP_CONNECTED: {
+ ErtsMonitor *mon;
+ ErtsMonitorDataExtended *mdep;
+ int created;
+
+ mon = erts_monitor_tree_lookup_create(&ERTS_P_MONITORS(p),
+ &created,
+ ERTS_MON_TYPE_NODE,
+ p->common.id,
+ Node);
+ mdep = (ErtsMonitorDataExtended *) erts_monitor_to_data(mon);
+ if (created) {
+#ifdef DEBUG
+ int inserted =
+#endif
+ erts_monitor_dist_insert(&mdep->md.target, dep->mld);
+ ASSERT(inserted);
+ ASSERT(mdep->dist->connection_id == dep->connection_id);
+ }
+ else if (mdep->dist->connection_id != dep->connection_id) {
+ ErtsMonitorDataExtended *mdep2;
+ ErtsMonitor *mon2;
+#ifdef DEBUG
+ int inserted;
+#endif
+ mdep2 = ((ErtsMonitorDataExtended *)
+ erts_monitor_create(ERTS_MON_TYPE_NODE, NIL,
+ p->common.id, Node, NIL));
+ mon2 = &mdep2->md.origin;
+#ifdef DEBUG
+ inserted =
+#endif
+ erts_monitor_dist_insert(&mdep->md.target, dep->mld);
+ ASSERT(inserted);
+ ASSERT(mdep2->dist->connection_id == dep->connection_id);
+
+ mdep2->uptr.node_monitors = mdep->uptr.node_monitors;
+ mdep->uptr.node_monitors = NULL;
+ erts_monitor_tree_replace(&ERTS_P_MONITORS(p), mon, mon2);
+ erts_monitor_list_insert(&mdep2->uptr.node_monitors, mon);
+ mon->flags |= ERTS_ML_FLG_IN_SUBTABLE;
+ mdep = mdep2;
+ }
+
+ mdep->u.refc++;
+
break;
+ }
+
default:
ERTS_ASSERT(! "Invalid dsig prepare result");
}
- erts_deref_dist_entry(dep);
- }
- else { /* Bool == false */
- dep = erts_sysname_to_connected_dist_entry(Node);
- if (!dep) {
- /*
- * Before OTP-21 this case triggered auto-connect
- * and a 'nodedown' message if that failed.
- * Now it's a simple no-op which feels more reasonable.
- */
- BIF_RET(am_true);
- }
- if (dep == erts_this_dist_entry)
- goto done;
- erts_proc_lock(p, ERTS_PROC_LOCK_LINK);
- erts_de_rlock(dep);
- if (!(dep->status & (ERTS_DE_SFLG_PENDING | ERTS_DE_SFLG_CONNECTED))) {
- erts_proc_unlock(p, ERTS_PROC_LOCK_LINK);
- erts_de_runlock(dep);
- goto done;
- }
- erts_de_links_lock(dep);
erts_de_runlock(dep);
- lnk = erts_lookup_link(dep->node_links, p->common.id);
- if (lnk != NULL) {
- if ((--ERTS_LINK_REFC(lnk)) == 0) {
- erts_destroy_link(erts_remove_link(&(dep->node_links),
- p->common.id));
- }
- }
- lnk = erts_lookup_link(ERTS_P_LINKS(p), Node);
- if (lnk != NULL) {
- if ((--ERTS_LINK_REFC(lnk)) == 0) {
- erts_destroy_link(erts_remove_link(&ERTS_P_LINKS(p),
- Node));
- }
- }
- erts_de_links_unlock(dep);
+
+ break;
}
- erts_proc_unlock(p, ERTS_PROC_LOCK_LINK);
+ default:
+ goto badarg;
+ }
- done:
- BIF_RET(am_true);
+ ERTS_BIF_PREP_RET(ret, am_true);
+
+do_return:
+
+ if (dep)
+ erts_deref_dist_entry(dep);
+
+ return ret;
+
+do_trap:
+ ERTS_BIF_PREP_TRAP3(ret, dmonitor_node_trap, p, Node, Bool, Options);
+ goto do_return;
+
+badarg:
+ ERTS_BIF_PREP_ERROR(ret, p, BADARG);
+ goto do_return;
}
BIF_RETTYPE monitor_node_3(BIF_ALIST_3)
@@ -3985,18 +3953,9 @@ BIF_RETTYPE net_kernel_dflag_unicode_io_1(BIF_ALIST_1)
#define ERTS_NODES_MON_OPT_TYPES \
(ERTS_NODES_MON_OPT_TYPE_VISIBLE|ERTS_NODES_MON_OPT_TYPE_HIDDEN)
-typedef struct ErtsNodesMonitor_ ErtsNodesMonitor;
-struct ErtsNodesMonitor_ {
- ErtsNodesMonitor *prev;
- ErtsNodesMonitor *next;
- Process *proc;
- Uint16 opts;
- Uint16 no;
-};
-
static erts_mtx_t nodes_monitors_mtx;
-static ErtsNodesMonitor *nodes_monitors;
-static ErtsNodesMonitor *nodes_monitors_end;
+static ErtsMonitor *nodes_monitors;
+static Uint no_nodes_monitors;
/*
* Nodes monitors are stored in a double linked list. 'nodes_monitors'
@@ -4015,109 +3974,169 @@ init_nodes_monitors(void)
erts_mtx_init(&nodes_monitors_mtx, "nodes_monitors", NIL,
ERTS_LOCK_FLAGS_PROPERTY_STATIC | ERTS_LOCK_FLAGS_CATEGORY_DISTRIBUTION);
nodes_monitors = NULL;
- nodes_monitors_end = NULL;
+ no_nodes_monitors = 0;
}
-static ERTS_INLINE Uint
-nodes_mon_msg_sz(ErtsNodesMonitor *nmp, Eterm what, Eterm reason)
+Eterm
+erts_monitor_nodes(Process *c_p, Eterm on, Eterm olist)
{
- Uint sz;
- if (!nmp->opts) {
- sz = 3;
- }
- else {
- sz = 0;
+ Eterm key, old_value, opts_list = olist;
+ Uint opts = (Uint) 0;
+
+ ASSERT(c_p);
+ ERTS_LC_ASSERT(erts_proc_lc_my_proc_locks(c_p) == ERTS_PROC_LOCK_MAIN);
- if (nmp->opts & ERTS_NODES_MON_OPT_TYPES)
- sz += 2 + 3;
+ if (on != am_true && on != am_false)
+ return THE_NON_VALUE;
- if (what == am_nodedown
- && (nmp->opts & ERTS_NODES_MON_OPT_DOWN_REASON)) {
- if (is_not_immed(reason))
- sz += size_object(reason);
- sz += 2 + 3;
+ if (is_not_nil(opts_list)) {
+ int all = 0, visible = 0, hidden = 0;
+
+ while (is_list(opts_list)) {
+ Eterm *cp = list_val(opts_list);
+ Eterm opt = CAR(cp);
+ opts_list = CDR(cp);
+ if (opt == am_nodedown_reason)
+ opts |= ERTS_NODES_MON_OPT_DOWN_REASON;
+ else if (is_tuple(opt)) {
+ Eterm* tp = tuple_val(opt);
+ if (arityval(tp[0]) != 2)
+ return THE_NON_VALUE;
+ switch (tp[1]) {
+ case am_node_type:
+ switch (tp[2]) {
+ case am_visible:
+ if (hidden || all)
+ return THE_NON_VALUE;
+ opts |= ERTS_NODES_MON_OPT_TYPE_VISIBLE;
+ visible = 1;
+ break;
+ case am_hidden:
+ if (visible || all)
+ return THE_NON_VALUE;
+ opts |= ERTS_NODES_MON_OPT_TYPE_HIDDEN;
+ hidden = 1;
+ break;
+ case am_all:
+ if (visible || hidden)
+ return THE_NON_VALUE;
+ opts |= ERTS_NODES_MON_OPT_TYPES;
+ all = 1;
+ break;
+ default:
+ return THE_NON_VALUE;
+ }
+ break;
+ default:
+ return THE_NON_VALUE;
+ }
+ }
+ else {
+ return THE_NON_VALUE;
+ }
}
- sz += 4;
+ if (is_not_nil(opts_list))
+ return THE_NON_VALUE;
}
- return sz;
+
+ key = make_small(opts);
+
+ if (on == am_true) {
+ ErtsMonitorDataExtended *mdep;
+ ErtsMonitor *omon;
+ int created;
+ omon = erts_monitor_tree_lookup_create(&ERTS_P_MONITORS(c_p),
+ &created,
+ ERTS_MON_TYPE_NODES,
+ c_p->common.id,
+ key);
+ mdep = (ErtsMonitorDataExtended *) erts_monitor_to_data(omon);
+ if (created) {
+ erts_mtx_lock(&nodes_monitors_mtx);
+ no_nodes_monitors++;
+ erts_monitor_list_insert(&nodes_monitors, &mdep->md.target);
+ erts_mtx_unlock(&nodes_monitors_mtx);
+ }
+ old_value = mdep->u.refc;
+ mdep->u.refc++;
+ }
+ else {
+ ErtsMonitorDataExtended *mdep;
+ ErtsMonitor *omon;
+ omon = erts_monitor_tree_lookup(ERTS_P_MONITORS(c_p), key);
+ if (!omon)
+ old_value = 0;
+ else {
+ mdep = (ErtsMonitorDataExtended *) erts_monitor_to_data(omon);
+ old_value = mdep->u.refc;
+ ASSERT(mdep->u.refc > 0);
+ erts_mtx_lock(&nodes_monitors_mtx);
+ ASSERT(no_nodes_monitors > 0);
+ no_nodes_monitors--;
+ ASSERT(erts_monitor_is_in_table(&mdep->md.target));
+ erts_monitor_list_delete(&nodes_monitors, &mdep->md.target);
+ erts_mtx_unlock(&nodes_monitors_mtx);
+ erts_monitor_tree_delete(&ERTS_P_MONITORS(c_p), omon);
+ erts_monitor_release_both((ErtsMonitorData *) mdep);
+ }
+ }
+
+ return erts_make_integer(old_value, c_p);
}
-static ERTS_INLINE void
-send_nodes_mon_msg(Process *rp,
- ErtsProcLocks *rp_locksp,
- ErtsNodesMonitor *nmp,
- Eterm node,
- Eterm what,
- Eterm type,
- Eterm reason,
- Uint sz)
+void
+erts_monitor_nodes_delete(ErtsMonitor *omon)
{
- Eterm msg;
- Eterm *hp;
- ErtsMessage *mp;
- ErlOffHeap *ohp;
-#ifdef DEBUG
- Eterm *hend;
-#endif
+ ErtsMonitorData *mdp;
- mp = erts_alloc_message_heap(rp, rp_locksp, sz, &hp, &ohp);
-#ifdef DEBUG
- hend = hp + sz;
-#endif
+ ASSERT(omon->type == ERTS_MON_TYPE_NODES);
+ ASSERT(erts_monitor_is_origin(omon));
- if (!nmp->opts) {
- msg = TUPLE2(hp, what, node);
-#ifdef DEBUG
- hp += 3;
-#endif
- }
- else {
- Eterm tup;
- Eterm info = NIL;
+ mdp = erts_monitor_to_data(omon);
- if (nmp->opts & (ERTS_NODES_MON_OPT_TYPE_VISIBLE
- | ERTS_NODES_MON_OPT_TYPE_HIDDEN)) {
+ erts_mtx_lock(&nodes_monitors_mtx);
+ ASSERT(erts_monitor_is_in_table(&mdp->target));
+ ASSERT(no_nodes_monitors > 0);
+ no_nodes_monitors--;
+ erts_monitor_list_delete(&nodes_monitors, &mdp->target);
+ erts_mtx_unlock(&nodes_monitors_mtx);
+ erts_monitor_release_both(mdp);
+}
- tup = TUPLE2(hp, am_node_type, type);
- hp += 3;
- info = CONS(hp, tup, info);
- hp += 2;
- }
+typedef struct {
+ Eterm pid;
+ Eterm options;
+} ErtsNodesMonitorData;
- if (what == am_nodedown
- && (nmp->opts & ERTS_NODES_MON_OPT_DOWN_REASON)) {
- Eterm rsn_cpy;
-
- if (is_immed(reason))
- rsn_cpy = reason;
- else {
- Eterm rsn_sz = size_object(reason);
- rsn_cpy = copy_struct(reason, rsn_sz, &hp, ohp);
- }
+typedef struct {
+ ErtsNodesMonitorData *nmdp;
+ Uint i;
+} ErtsNodesMonitorContext;
- tup = TUPLE2(hp, am_nodedown_reason, rsn_cpy);
- hp += 3;
- info = CONS(hp, tup, info);
- hp += 2;
- }
+static void
+save_nodes_monitor(ErtsMonitor *mon, void *vctxt)
+{
+ ErtsNodesMonitorContext *ctxt = vctxt;
+ ErtsMonitorData *mdp = erts_monitor_to_data(mon);
- msg = TUPLE3(hp, what, node, info);
-#ifdef DEBUG
- hp += 4;
-#endif
- }
+ ASSERT(erts_monitor_is_target(mon));
+ ASSERT(mon->type == ERTS_MON_TYPE_NODES);
+
+ ctxt->nmdp[ctxt->i].pid = mon->other.item;
+ ctxt->nmdp[ctxt->i].options = mdp->origin.other.item;
- ASSERT(hend == hp);
- erts_queue_message(rp, *rp_locksp, mp, msg, am_system);
+ ctxt->i++;
}
static void
send_nodes_mon_msgs(Process *c_p, Eterm what, Eterm node, Eterm type, Eterm reason)
{
- ErtsNodesMonitor *nmp;
- ErtsProcLocks rp_locks = 0; /* Init to shut up false warning */
- Process *rp = NULL;
+ Uint opts;
+ Uint i, no, reason_size;
+ ErtsNodesMonitorData def_buf[100];
+ ErtsNodesMonitorData *nmdp = &def_buf[0];
+ ErtsNodesMonitorContext ctxt;
ASSERT(is_immed(what));
ASSERT(is_immed(node));
@@ -4138,31 +4157,44 @@ send_nodes_mon_msgs(Process *c_p, Eterm what, Eterm node, Eterm type, Eterm reas
}
#endif
- ERTS_LC_ASSERT(!c_p
- || (erts_proc_lc_my_proc_locks(c_p)
- == ERTS_PROC_LOCK_MAIN));
+ ctxt.i = 0;
+
+ reason_size = is_immed(reason) ? 0 : size_object(reason);
+
erts_mtx_lock(&nodes_monitors_mtx);
+ if (no_nodes_monitors > sizeof(def_buf)/sizeof(def_buf[0]))
+ nmdp = erts_alloc(ERTS_ALC_T_TMP,
+ no_nodes_monitors*sizeof(ErtsNodesMonitorData));
+ ctxt.nmdp = nmdp;
+ erts_monitor_list_foreach(nodes_monitors,
+ save_nodes_monitor,
+ (void *) &ctxt);
+
+ ASSERT(ctxt.i == no_nodes_monitors);
+ no = no_nodes_monitors;
- for (nmp = nodes_monitors; nmp; nmp = nmp->next) {
- int i;
- Uint16 no;
- Uint sz;
+ erts_mtx_unlock(&nodes_monitors_mtx);
- ASSERT(nmp->proc != NULL);
+ for (i = 0; i < no; i++) {
+ Eterm tmp_heap[3+2+3+2+4 /* max need */];
+ Eterm *hp, msg;
+ Uint hsz;
- if (!nmp->opts) {
+ ASSERT(is_small(nmdp[i].options));
+ opts = (Uint) signed_val(nmdp[i].options);
+ if (!opts) {
if (type != am_visible)
continue;
}
else {
switch (type) {
case am_hidden:
- if (!(nmp->opts & ERTS_NODES_MON_OPT_TYPE_HIDDEN))
+ if (!(opts & ERTS_NODES_MON_OPT_TYPE_HIDDEN))
continue;
break;
case am_visible:
- if ((nmp->opts & ERTS_NODES_MON_OPT_TYPES)
- && !(nmp->opts & ERTS_NODES_MON_OPT_TYPE_VISIBLE))
+ if ((opts & ERTS_NODES_MON_OPT_TYPES)
+ && !(opts & ERTS_NODES_MON_OPT_TYPE_VISIBLE))
continue;
break;
default:
@@ -4170,342 +4202,162 @@ send_nodes_mon_msgs(Process *c_p, Eterm what, Eterm node, Eterm type, Eterm reas
}
}
- if (rp != nmp->proc) {
- if (rp) {
- if (rp == c_p)
- rp_locks &= ~ERTS_PROC_LOCK_MAIN;
- erts_proc_unlock(rp, rp_locks);
- }
-
- rp = nmp->proc;
- rp_locks = 0;
- if (rp == c_p)
- rp_locks |= ERTS_PROC_LOCK_MAIN;
- }
-
- ASSERT(rp);
-
- sz = nodes_mon_msg_sz(nmp, what, reason);
+ hsz = 0;
+ hp = &tmp_heap[0];
- for (i = 0, no = nmp->no; i < no; i++)
- send_nodes_mon_msg(rp,
- &rp_locks,
- nmp,
- node,
- what,
- type,
- reason,
- sz);
- }
+ if (!opts) {
+ msg = TUPLE2(hp, what, node);
+ hp += 3;
+ }
+ else {
+ Eterm tup;
+ Eterm info = NIL;
- if (rp) {
- if (rp == c_p)
- rp_locks &= ~ERTS_PROC_LOCK_MAIN;
- erts_proc_unlock(rp, rp_locks);
- }
+ if (opts & (ERTS_NODES_MON_OPT_TYPE_VISIBLE
+ | ERTS_NODES_MON_OPT_TYPE_HIDDEN)) {
- erts_mtx_unlock(&nodes_monitors_mtx);
-}
+ tup = TUPLE2(hp, am_node_type, type);
+ hp += 3;
+ info = CONS(hp, tup, info);
+ hp += 2;
+ }
-static Eterm
-insert_nodes_monitor(Process *c_p, Uint32 opts)
-{
- Uint16 no = 1;
- Eterm res = am_false;
- ErtsNodesMonitor *xnmp, *nmp;
+ if (what == am_nodedown
+ && (opts & ERTS_NODES_MON_OPT_DOWN_REASON)) {
+ hsz += reason_size;
+ tup = TUPLE2(hp, am_nodedown_reason, reason);
+ hp += 3;
+ info = CONS(hp, tup, info);
+ hp += 2;
+ }
- ERTS_LC_ASSERT(erts_lc_mtx_is_locked(&nodes_monitors_mtx));
- ERTS_LC_ASSERT(erts_proc_lc_my_proc_locks(c_p) & ERTS_PROC_LOCK_MAIN);
+ msg = TUPLE3(hp, what, node, info);
+ hp += 4;
+ }
- xnmp = c_p->nodes_monitors;
- if (xnmp) {
- ASSERT(!xnmp->prev || xnmp->prev->proc != c_p);
+ ASSERT(hp - &tmp_heap[0] <= sizeof(tmp_heap)/sizeof(tmp_heap[0]));
- while (1) {
- ASSERT(xnmp->proc == c_p);
- if (xnmp->opts == opts)
- break;
- if (!xnmp->next || xnmp->next->proc != c_p)
- break;
- xnmp = xnmp->next;
- }
- ASSERT(xnmp);
- ASSERT(xnmp->proc == c_p);
- ASSERT(xnmp->opts == opts
- || !xnmp->next
- || xnmp->next->proc != c_p);
-
- if (xnmp->opts != opts)
- goto alloc_new;
- else {
- res = am_true;
- no = xnmp->no++;
- if (!xnmp->no) {
- /*
- * 'no' wrapped; transfer all prevous monitors to new
- * element (which will be the next element in the list)
- * and set this to one...
- */
- xnmp->no = 1;
- goto alloc_new;
- }
- }
- }
- else {
- alloc_new:
- nmp = erts_alloc(ERTS_ALC_T_NODES_MON, sizeof(ErtsNodesMonitor));
- nmp->proc = c_p;
- nmp->opts = opts;
- nmp->no = no;
-
- if (xnmp) {
- ASSERT(nodes_monitors);
- ASSERT(c_p->nodes_monitors);
- nmp->next = xnmp->next;
- nmp->prev = xnmp;
- xnmp->next = nmp;
- if (nmp->next) {
- ASSERT(nodes_monitors_end != xnmp);
- ASSERT(nmp->next->prev == xnmp);
- nmp->next->prev = nmp;
- }
- else {
- ASSERT(nodes_monitors_end == xnmp);
- nodes_monitors_end = nmp;
- }
- }
- else {
- ASSERT(!c_p->nodes_monitors);
- c_p->nodes_monitors = nmp;
- nmp->next = NULL;
- nmp->prev = nodes_monitors_end;
- if (nodes_monitors_end) {
- ASSERT(nodes_monitors);
- nodes_monitors_end->next = nmp;
- }
- else {
- ASSERT(!nodes_monitors);
- nodes_monitors = nmp;
- }
- nodes_monitors_end = nmp;
- }
- }
- return res;
-}
+ hsz += hp - &tmp_heap[0];
-static Eterm
-remove_nodes_monitors(Process *c_p, Uint32 opts, int all)
-{
- Eterm res = am_false;
- ErtsNodesMonitor *nmp;
-
- ERTS_LC_ASSERT(erts_lc_mtx_is_locked(&nodes_monitors_mtx));
- ERTS_LC_ASSERT(erts_proc_lc_my_proc_locks(c_p) & ERTS_PROC_LOCK_MAIN);
-
- nmp = c_p->nodes_monitors;
- ASSERT(!nmp || !nmp->prev || nmp->prev->proc != c_p);
-
- while (nmp && nmp->proc == c_p) {
- if (!all && nmp->opts != opts)
- nmp = nmp->next;
- else { /* if (all || nmp->opts == opts) */
- ErtsNodesMonitor *free_nmp;
- res = am_true;
- if (nmp->prev) {
- ASSERT(nodes_monitors != nmp);
- nmp->prev->next = nmp->next;
- }
- else {
- ASSERT(nodes_monitors == nmp);
- nodes_monitors = nmp->next;
- }
- if (nmp->next) {
- ASSERT(nodes_monitors_end != nmp);
- nmp->next->prev = nmp->prev;
- }
- else {
- ASSERT(nodes_monitors_end == nmp);
- nodes_monitors_end = nmp->prev;
- }
- free_nmp = nmp;
- nmp = nmp->next;
- if (c_p->nodes_monitors == free_nmp)
- c_p->nodes_monitors = nmp && nmp->proc == c_p ? nmp : NULL;
- erts_free(ERTS_ALC_T_NODES_MON, free_nmp);
- }
+ erts_proc_sig_send_persistent_monitor_msg(ERTS_MON_TYPE_NODES,
+ nmdp[i].options,
+ am_system,
+ nmdp[i].pid,
+ msg,
+ hsz);
}
-
- ASSERT(!all || !c_p->nodes_monitors);
- return res;
-}
-void
-erts_delete_nodes_monitors(Process *c_p, ErtsProcLocks locks)
-{
-#if defined(ERTS_ENABLE_LOCK_CHECK)
- if (c_p) {
- ErtsProcLocks might_unlock = locks & ~ERTS_PROC_LOCK_MAIN;
- if (might_unlock)
- erts_proc_lc_might_unlock(c_p, might_unlock);
- }
-#endif
- if (erts_mtx_trylock(&nodes_monitors_mtx) == EBUSY) {
- ErtsProcLocks unlock_locks = locks & ~ERTS_PROC_LOCK_MAIN;
- if (c_p && unlock_locks)
- erts_proc_unlock(c_p, unlock_locks);
- erts_mtx_lock(&nodes_monitors_mtx);
- if (c_p && unlock_locks)
- erts_proc_lock(c_p, unlock_locks);
- }
- remove_nodes_monitors(c_p, 0, 1);
- erts_mtx_unlock(&nodes_monitors_mtx);
+ if (nmdp != &def_buf[0])
+ erts_free(ERTS_ALC_T_TMP, nmdp);
}
+
-Eterm
-erts_monitor_nodes(Process *c_p, Eterm on, Eterm olist)
-{
+typedef struct {
+ Eterm **hpp;
+ Uint *szp;
Eterm res;
- Eterm opts_list = olist;
- Uint16 opts = (Uint16) 0;
+} ErtsNodesMonitorInfoContext;
- ASSERT(c_p);
- ERTS_LC_ASSERT(erts_proc_lc_my_proc_locks(c_p) == ERTS_PROC_LOCK_MAIN);
-
- if (on != am_true && on != am_false)
- return THE_NON_VALUE;
- if (is_not_nil(opts_list)) {
- int all = 0, visible = 0, hidden = 0;
-
- while (is_list(opts_list)) {
- Eterm *cp = list_val(opts_list);
- Eterm opt = CAR(cp);
- opts_list = CDR(cp);
- if (opt == am_nodedown_reason)
- opts |= ERTS_NODES_MON_OPT_DOWN_REASON;
- else if (is_tuple(opt)) {
- Eterm* tp = tuple_val(opt);
- if (arityval(tp[0]) != 2)
- return THE_NON_VALUE;
- switch (tp[1]) {
- case am_node_type:
- switch (tp[2]) {
- case am_visible:
- if (hidden || all)
- return THE_NON_VALUE;
- opts |= ERTS_NODES_MON_OPT_TYPE_VISIBLE;
- visible = 1;
- break;
- case am_hidden:
- if (visible || all)
- return THE_NON_VALUE;
- opts |= ERTS_NODES_MON_OPT_TYPE_HIDDEN;
- hidden = 1;
- break;
- case am_all:
- if (visible || hidden)
- return THE_NON_VALUE;
- opts |= ERTS_NODES_MON_OPT_TYPES;
- all = 1;
- break;
- default:
- return THE_NON_VALUE;
- }
- break;
- default:
- return THE_NON_VALUE;
- }
- }
- else {
- return THE_NON_VALUE;
- }
- }
-
- if (is_not_nil(opts_list))
- return THE_NON_VALUE;
- }
-
- erts_mtx_lock(&nodes_monitors_mtx);
-
- if (on == am_true)
- res = insert_nodes_monitor(c_p, opts);
- else
- res = remove_nodes_monitors(c_p, opts, 0);
-
- erts_mtx_unlock(&nodes_monitors_mtx);
-
- return res;
+static void
+nodes_monitor_info(ErtsMonitor *mon, void *vctxt)
+{
+ ErtsMonitorDataExtended *mdep;
+ ErtsNodesMonitorInfoContext *ctxt = vctxt;
+ Uint no, i, opts, *szp;
+ Eterm **hpp, res;
+
+ hpp = ctxt->hpp;
+ szp = ctxt->szp;
+ res = ctxt->res;
+
+ ASSERT(erts_monitor_is_target(mon));
+ ASSERT(mon->type == ERTS_MON_TYPE_NODES);
+ mdep = (ErtsMonitorDataExtended *) erts_monitor_to_data(mon);
+ no = mdep->u.refc;
+
+ ASSERT(is_small(mdep->md.origin.other.item));
+ opts = (Uint) signed_val(mdep->md.origin.other.item);
+
+ for (i = 0; i < no; i++) {
+ Eterm olist = NIL;
+ if (opts & ERTS_NODES_MON_OPT_TYPES) {
+ Eterm type;
+ switch (opts & ERTS_NODES_MON_OPT_TYPES) {
+ case ERTS_NODES_MON_OPT_TYPES: type = am_all; break;
+ case ERTS_NODES_MON_OPT_TYPE_VISIBLE: type = am_visible; break;
+ case ERTS_NODES_MON_OPT_TYPE_HIDDEN: type = am_hidden; break;
+ default: erts_exit(ERTS_ABORT_EXIT, "Bad node type found\n");
+ }
+ olist = erts_bld_cons(hpp, szp,
+ erts_bld_tuple(hpp, szp, 2,
+ am_node_type,
+ type),
+ olist);
+ }
+ if (opts & ERTS_NODES_MON_OPT_DOWN_REASON)
+ olist = erts_bld_cons(hpp, szp, am_nodedown_reason, olist);
+ res = erts_bld_cons(hpp, szp,
+ erts_bld_tuple(hpp, szp, 2,
+ mon->other.item,
+ olist),
+ res);
+ }
+
+ ctxt->hpp = hpp;
+ ctxt->szp = szp;
+ ctxt->res = res;
}
-/*
- * Note, this function is only used for debuging.
- */
-
Eterm
erts_processes_monitoring_nodes(Process *c_p)
{
- ErtsNodesMonitor *nmp;
- Eterm res;
+ /*
+ * Note, this function is only used for debugging.
+ */
+ ErtsNodesMonitorInfoContext ctxt;
Eterm *hp;
- Eterm **hpp;
Uint sz;
- Uint *szp;
#ifdef DEBUG
Eterm *hend;
#endif
ASSERT(c_p);
ERTS_LC_ASSERT(erts_proc_lc_my_proc_locks(c_p) == ERTS_PROC_LOCK_MAIN);
+
+ erts_proc_unlock(c_p, ERTS_PROC_LOCK_MAIN);
+ erts_thr_progress_block();
+
erts_mtx_lock(&nodes_monitors_mtx);
sz = 0;
- szp = &sz;
- hpp = NULL;
+ ctxt.szp = &sz;
+ ctxt.hpp = NULL;
- bld_result:
- res = NIL;
-
- for (nmp = nodes_monitors_end; nmp; nmp = nmp->prev) {
- Uint16 i;
- for (i = 0; i < nmp->no; i++) {
- Eterm olist = NIL;
- if (nmp->opts & ERTS_NODES_MON_OPT_TYPES) {
- Eterm type;
- switch (nmp->opts & ERTS_NODES_MON_OPT_TYPES) {
- case ERTS_NODES_MON_OPT_TYPES: type = am_all; break;
- case ERTS_NODES_MON_OPT_TYPE_VISIBLE: type = am_visible; break;
- case ERTS_NODES_MON_OPT_TYPE_HIDDEN: type = am_hidden; break;
- default: erts_exit(ERTS_ABORT_EXIT, "Bad node type found\n");
- }
- olist = erts_bld_cons(hpp, szp,
- erts_bld_tuple(hpp, szp, 2,
- am_node_type,
- type),
- olist);
- }
- if (nmp->opts & ERTS_NODES_MON_OPT_DOWN_REASON)
- olist = erts_bld_cons(hpp, szp, am_nodedown_reason, olist);
- res = erts_bld_cons(hpp, szp,
- erts_bld_tuple(hpp, szp, 2,
- nmp->proc->common.id,
- olist),
- res);
- }
- }
+ while (1) {
+ ctxt.res = NIL;
+
+ erts_monitor_list_foreach(nodes_monitors,
+ nodes_monitor_info,
+ (void *) &ctxt);
+
+ if (ctxt.hpp)
+ break;
- if (!hpp) {
hp = HAlloc(c_p, sz);
#ifdef DEBUG
hend = hp + sz;
#endif
- hpp = &hp;
- szp = NULL;
- goto bld_result;
+ ctxt.hpp = &hp;
+ ctxt.szp = NULL;
}
ASSERT(hp == hend);
erts_mtx_unlock(&nodes_monitors_mtx);
- return res;
+ erts_thr_progress_unblock();
+ erts_proc_lock(c_p, ERTS_PROC_LOCK_MAIN);
+
+ return ctxt.res;
}
diff --git a/erts/emulator/beam/dist.h b/erts/emulator/beam/dist.h
index ea4697815f..c608fef816 100644
--- a/erts/emulator/beam/dist.h
+++ b/erts/emulator/beam/dist.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1996-2017. All Rights Reserved.
+ * Copyright Ericsson AB 1996-2018. 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.
@@ -40,26 +40,52 @@
#define DFLAG_UNICODE_IO 0x1000
#define DFLAG_DIST_HDR_ATOM_CACHE 0x2000
#define DFLAG_SMALL_ATOM_TAGS 0x4000
-#define DFLAG_INTERNAL_TAGS 0x8000
+#define DFLAG_INTERNAL_TAGS 0x8000 /* used by ETS 'compressed' option */
#define DFLAG_UTF8_ATOMS 0x10000
#define DFLAG_MAP_TAG 0x20000
#define DFLAG_BIG_CREATION 0x40000
#define DFLAG_SEND_SENDER 0x80000
-#define DFLAG_NO_MAGIC 0x100000
+#define DFLAG_NO_MAGIC 0x100000 /* internal for pending connection */
-/* Mandatory flags for distribution (sync with dist_util.erl) */
+/* Mandatory flags for distribution */
#define DFLAG_DIST_MANDATORY (DFLAG_EXTENDED_REFERENCES \
| DFLAG_EXTENDED_PIDS_PORTS \
| DFLAG_UTF8_ATOMS \
| DFLAG_NEW_FUN_TAGS)
-/* Additional optimistic flags when encoding toward pending connection */
-#define DFLAG_DIST_HOPEFULLY (DFLAG_NO_MAGIC \
- | DFLAG_EXPORT_PTR_TAG \
+/*
+ * Additional optimistic flags when encoding toward pending connection.
+ * If remote node (erl_interface) does not supporting these then we may need
+ * to transcode messages enqueued before connection setup was finished.
+ */
+#define DFLAG_DIST_HOPEFULLY (DFLAG_EXPORT_PTR_TAG \
| DFLAG_BIT_BINARIES \
| DFLAG_DIST_MONITOR \
| DFLAG_DIST_MONITOR_NAME)
+/* Our preferred set of flags. Used for connection setup handshake */
+#define DFLAG_DIST_DEFAULT (DFLAG_DIST_MANDATORY | DFLAG_DIST_HOPEFULLY \
+ | DFLAG_FUN_TAGS \
+ | DFLAG_NEW_FLOATS \
+ | DFLAG_UNICODE_IO \
+ | DFLAG_DIST_HDR_ATOM_CACHE \
+ | DFLAG_SMALL_ATOM_TAGS \
+ | DFLAG_UTF8_ATOMS \
+ | DFLAG_MAP_TAG \
+ | DFLAG_BIG_CREATION \
+ | DFLAG_SEND_SENDER)
+
+/* Flags addable by local distr implementations */
+#define DFLAG_DIST_ADDABLE DFLAG_DIST_DEFAULT
+
+/* Flags rejectable by local distr implementation */
+#define DFLAG_DIST_REJECTABLE (DFLAG_DIST_HDR_ATOM_CACHE \
+ | DFLAG_HIDDEN_ATOM_CACHE \
+ | DFLAG_ATOM_CACHE)
+
+/* Flags for all features needing strict order delivery */
+#define DFLAG_DIST_STRICT_ORDER DFLAG_DIST_HDR_ATOM_CACHE
+
/* All flags that should be enabled when term_to_binary/1 is used. */
#define TERM_TO_BINARY_DFLAGS (DFLAG_EXTENDED_REFERENCES \
| DFLAG_NEW_FUN_TAGS \
@@ -94,7 +120,6 @@
/* distribution trap functions */
extern Export* dmonitor_node_trap;
-extern Export* dmonitor_p_trap;
typedef enum {
ERTS_DSP_NO_LOCK,
@@ -111,14 +136,6 @@ typedef struct {
int no_suspend;
} ErtsDSigData;
-#define ERTS_DE_IS_NOT_CONNECTED(DEP) \
- (ERTS_LC_ASSERT(erts_lc_rwmtx_is_rlocked(&(DEP)->rwmtx) \
- || erts_lc_rwmtx_is_rwlocked(&(DEP)->rwmtx)), \
- (is_nil((DEP)->cid) || ((DEP)->status & ERTS_DE_SFLG_EXITING)))
-
-#define ERTS_DE_IS_CONNECTED(DEP) \
- (!ERTS_DE_IS_NOT_CONNECTED((DEP)))
-
#define ERTS_DE_BUSY_LIMIT (1024*1024)
extern int erts_dist_buf_busy_limit;
extern int erts_is_alive;
@@ -181,18 +198,18 @@ erts_dsig_prepare(ErtsDSigData *dsdp,
retry:
erts_de_rlock(dep);
- if (ERTS_DE_IS_CONNECTED(dep)) {
+ if (dep->state == ERTS_DE_STATE_CONNECTED) {
res = ERTS_DSIG_PREP_CONNECTED;
}
- else if (dep->status & ERTS_DE_SFLG_PENDING) {
+ else if (dep->state == ERTS_DE_STATE_PENDING) {
res = ERTS_DSIG_PREP_PENDING;
}
- else if (dep->status & ERTS_DE_SFLG_EXITING) {
+ else if (dep->state == ERTS_DE_STATE_EXITING) {
res = ERTS_DSIG_PREP_NOT_CONNECTED;
goto fail;
}
else if (connect) {
- ASSERT(dep->status == 0);
+ ASSERT(dep->state == ERTS_DE_STATE_IDLE);
erts_de_runlock(dep);
if (!erts_auto_connect(dep, proc, proc_locks)) {
return ERTS_DSIG_PREP_NOT_ALIVE;
@@ -200,7 +217,7 @@ retry:
goto retry;
}
else {
- ASSERT(dep->status == 0);
+ ASSERT(dep->state == ERTS_DE_STATE_IDLE);
res = ERTS_DSIG_PREP_NOT_CONNECTED;
goto fail;
}
@@ -256,58 +273,13 @@ void erts_schedule_dist_command(Port *prt, DistEntry *dist_entry)
#endif
-typedef struct {
- ErtsLink *d_lnk;
- ErtsLink *d_sub_lnk;
-} ErtsDistLinkData;
-
-ERTS_GLB_INLINE void erts_remove_dist_link(ErtsDistLinkData *,
- Eterm,
- Eterm,
- DistEntry *);
-ERTS_GLB_INLINE int erts_was_dist_link_removed(ErtsDistLinkData *);
-ERTS_GLB_INLINE void erts_destroy_dist_link(ErtsDistLinkData *);
-
-#if ERTS_GLB_INLINE_INCL_FUNC_DEF
-
-ERTS_GLB_INLINE void
-erts_remove_dist_link(ErtsDistLinkData *dldp,
- Eterm lid,
- Eterm rid,
- DistEntry *dep)
-{
- erts_de_links_lock(dep);
- dldp->d_lnk = erts_lookup_link(dep->nlinks, lid);
- if (!dldp->d_lnk)
- dldp->d_sub_lnk = NULL;
- else {
- dldp->d_sub_lnk = erts_remove_link(&ERTS_LINK_ROOT(dldp->d_lnk), rid);
- dldp->d_lnk = (ERTS_LINK_ROOT(dldp->d_lnk)
- ? NULL
- : erts_remove_link(&dep->nlinks, lid));
- }
- erts_de_links_unlock(dep);
-}
-
-ERTS_GLB_INLINE int
-erts_was_dist_link_removed(ErtsDistLinkData *dldp)
-{
- return dldp->d_sub_lnk != NULL;
-}
-
-ERTS_GLB_INLINE void
-erts_destroy_dist_link(ErtsDistLinkData *dldp)
-{
- if (dldp->d_lnk)
- erts_destroy_link(dldp->d_lnk);
- if (dldp->d_sub_lnk)
- erts_destroy_link(dldp->d_sub_lnk);
-}
-
+#ifdef DEBUG
+#define ERTS_DBG_CHK_NO_DIST_LNK(D, R, L) \
+ erts_dbg_chk_no_dist_proc_link((D), (R), (L))
+#else
+#define ERTS_DBG_CHK_NO_DIST_LNK(D, R, L)
#endif
-
-
/* Define for testing */
/* #define EXTREME_TTB_TRAPPING 1 */
@@ -328,6 +300,7 @@ typedef struct TTBSizeContext_ {
typedef struct TTBEncodeContext_ {
Uint flags;
+ Uint hopefull_flags;
int level;
byte* ep;
Eterm obj;
diff --git a/erts/emulator/beam/erl_afit_alloc.c b/erts/emulator/beam/erl_afit_alloc.c
index 4ebe37ee1d..23efe3bba4 100644
--- a/erts/emulator/beam/erl_afit_alloc.c
+++ b/erts/emulator/beam/erl_afit_alloc.c
@@ -181,7 +181,7 @@ static struct {
static void ERTS_INLINE atom_init(Eterm *atom, char *name)
{
- *atom = am_atom_put(name, strlen(name));
+ *atom = am_atom_put(name, sys_strlen(name));
}
#define AM_INIT(AM) atom_init(&am.AM, #AM)
diff --git a/erts/emulator/beam/erl_alloc.c b/erts/emulator/beam/erl_alloc.c
index 88285d8be6..061b9df627 100644
--- a/erts/emulator/beam/erl_alloc.c
+++ b/erts/emulator/beam/erl_alloc.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2002-2017. All Rights Reserved.
+ * Copyright Ericsson AB 2002-2018. 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.
@@ -40,7 +40,7 @@
#include "erl_bits.h"
#include "erl_instrument.h"
#include "erl_mseg.h"
-#include "erl_monitors.h"
+#include "erl_monitor_link.h"
#include "erl_hl_timer.h"
#include "erl_cpu_topology.h"
#include "erl_thr_queue.h"
@@ -160,7 +160,7 @@ enum allctr_type {
GOODFIT,
BESTFIT,
AFIT,
- AOFIRSTFIT
+ FIRSTFIT
};
struct au_init {
@@ -366,16 +366,9 @@ set_default_exec_alloc_opts(struct au_init *ip)
ip->init.util.rmbcmt = 0;
ip->init.util.acul = 0;
-# ifdef ERTS_HAVE_EXEC_MMAPPER
- ip->init.util.mseg_alloc = &erts_alcu_mmapper_mseg_alloc;
- ip->init.util.mseg_realloc = &erts_alcu_mmapper_mseg_realloc;
- ip->init.util.mseg_dealloc = &erts_alcu_mmapper_mseg_dealloc;
- ip->init.util.mseg_mmapper = &erts_exec_mmapper;
-# else
ip->init.util.mseg_alloc = &erts_alcu_exec_mseg_alloc;
ip->init.util.mseg_realloc = &erts_alcu_exec_mseg_realloc;
ip->init.util.mseg_dealloc = &erts_alcu_exec_mseg_dealloc;
-# endif
}
#endif /* ERTS_ALC_A_EXEC */
@@ -500,8 +493,9 @@ set_default_test_alloc_opts(struct au_init *ip)
SET_DEFAULT_ALLOC_OPTS(ip);
ip->enable = 0; /* Disabled by default */
ip->thr_spec = -1 * erts_no_schedulers;
- ip->atype = AOFIRSTFIT;
- ip->init.aoff.flavor = AOFF_BF;
+ ip->atype = FIRSTFIT;
+ ip->init.aoff.crr_order = FF_AOFF;
+ ip->init.aoff.blk_order = FF_BF;
ip->init.util.name_prefix = "test_";
ip->init.util.alloc_no = ERTS_ALC_A_TEST;
ip->init.util.mmbcs = 0; /* Main carrier size */
@@ -599,10 +593,10 @@ static ERTS_INLINE int
strategy_support_carrier_migration(struct au_init *auip)
{
/*
- * Currently only aoff, aoffcbf and aoffcaobf support carrier
+ * Currently only aoff* and ageff* support carrier
* migration, i.e, type AOFIRSTFIT.
*/
- return auip->atype == AOFIRSTFIT;
+ return auip->atype == FIRSTFIT;
}
static ERTS_INLINE void
@@ -617,8 +611,9 @@ adjust_carrier_migration_support(struct au_init *auip)
*/
if (!strategy_support_carrier_migration(auip)) {
/* Default to aoffcbf */
- auip->atype = AOFIRSTFIT;
- auip->init.aoff.flavor = AOFF_BF;
+ auip->atype = FIRSTFIT;
+ auip->init.aoff.crr_order = FF_AOFF;
+ auip->init.aoff.blk_order = FF_BF;
}
}
}
@@ -641,10 +636,10 @@ erts_alloc_init(int *argc, char **argv, ErtsAllocInitOpts *eaiop)
fix_type_sizes[ERTS_ALC_FIX_TYPE_IX(ERTS_ALC_T_PROC)]
= sizeof(Process);
- fix_type_sizes[ERTS_ALC_FIX_TYPE_IX(ERTS_ALC_T_MONITOR_SH)]
- = ERTS_MONITOR_SH_SIZE * sizeof(Uint);
- fix_type_sizes[ERTS_ALC_FIX_TYPE_IX(ERTS_ALC_T_NLINK_SH)]
- = ERTS_LINK_SH_SIZE * sizeof(Uint);
+ fix_type_sizes[ERTS_ALC_FIX_TYPE_IX(ERTS_ALC_T_MONITOR)]
+ = sizeof(ErtsMonitorDataHeap);
+ fix_type_sizes[ERTS_ALC_FIX_TYPE_IX(ERTS_ALC_T_LINK)]
+ = sizeof(ErtsLinkData);
fix_type_sizes[ERTS_ALC_FIX_TYPE_IX(ERTS_ALC_T_DRV_SEL_D_STATE)]
= sizeof(ErtsDrvSelectDataState);
fix_type_sizes[ERTS_ALC_FIX_TYPE_IX(ERTS_ALC_T_NIF_SEL_D_STATE)]
@@ -1132,7 +1127,7 @@ start_au_allocator(ErtsAlcType_t alctr_n,
&init->init.af,
&init->init.util);
break;
- case AOFIRSTFIT:
+ case FIRSTFIT:
as = erts_aoffalc_start((AOFFAllctr_t *) as0,
&init->init.aoff,
&init->init.util);
@@ -1217,31 +1212,41 @@ get_bool_value(char *param_end, char** argv, int* ip)
{
char *param = argv[*ip]+1;
char *value = get_value(param_end, argv, ip);
- if (strcmp(value, "true") == 0)
+ if (sys_strcmp(value, "true") == 0)
return 1;
- else if (strcmp(value, "false") == 0)
+ else if (sys_strcmp(value, "false") == 0)
return 0;
else
bad_value(param, param_end, value);
return -1;
}
+static Uint kb_to_bytes(Sint kb, Uint *bytes)
+{
+ const Uint max = ((~((Uint) 0))/1024) + 1;
+
+ if (kb < 0 || (Uint)kb > max)
+ return 0;
+ if ((Uint)kb == max)
+ *bytes = ~((Uint) 0);
+ else
+ *bytes = ((Uint) kb)*1024;
+ return 1;
+}
+
static Uint
get_kb_value(char *param_end, char** argv, int* ip)
{
Sint tmp;
- Uint max = ((~((Uint) 0))/1024) + 1;
+ Uint bytes = 0;
char *rest;
char *param = argv[*ip]+1;
char *value = get_value(param_end, argv, ip);
errno = 0;
tmp = (Sint) ErtsStrToSint(value, &rest, 10);
- if (errno != 0 || rest == value || tmp < 0 || max < ((Uint) tmp))
+ if (errno != 0 || rest == value || !kb_to_bytes(tmp, &bytes))
bad_value(param, param_end, value);
- if (max == (Uint) tmp)
- return ~((Uint) 0);
- else
- return ((Uint) tmp)*1024;
+ return bytes;
}
static UWord
@@ -1328,49 +1333,79 @@ handle_au_arg(struct au_init *auip,
switch (sub_param[0]) {
case 'a':
- if (has_prefix("acul", sub_param)) {
- if (!auip->carrier_migration_allowed) {
- if (!u_switch)
- goto bad_switch;
- else {
- /* ignore */
- (void) get_acul_value(auip, sub_param + 4, argv, ip);
- break;
- }
- }
- auip->init.util.acul = get_acul_value(auip, sub_param + 4, argv, ip);
- }
+ if (sub_param[1] == 'c') { /* Migration parameters "ac*" */
+ UWord value;
+ UWord* wp;
+ if (!auip->carrier_migration_allowed && !u_switch)
+ goto bad_switch;
+
+ if (has_prefix("acul", sub_param)) {
+ value = get_acul_value(auip, sub_param + 4, argv, ip);
+ wp = &auip->init.util.acul;
+ }
+ else if (has_prefix("acnl", sub_param)) {
+ value = get_amount_value(sub_param + 4, argv, ip);
+ wp = &auip->init.util.acnl;
+ }
+ else if (has_prefix("acfml", sub_param)) {
+ value = get_amount_value(sub_param + 5, argv, ip);
+ wp = &auip->init.util.acfml;
+ }
+ else
+ goto bad_switch;
+
+ if (auip->carrier_migration_allowed)
+ *wp = value;
+ }
else if(has_prefix("asbcst", sub_param)) {
auip->init.util.asbcst = get_kb_value(sub_param + 6, argv, ip);
}
else if(has_prefix("as", sub_param)) {
char *alg = get_value(sub_param + 2, argv, ip);
- if (strcmp("bf", alg) == 0) {
+ if (sys_strcmp("bf", alg) == 0) {
auip->atype = BESTFIT;
auip->init.bf.ao = 0;
}
- else if (strcmp("aobf", alg) == 0) {
+ else if (sys_strcmp("aobf", alg) == 0) {
auip->atype = BESTFIT;
auip->init.bf.ao = 1;
}
- else if (strcmp("gf", alg) == 0) {
+ else if (sys_strcmp("gf", alg) == 0) {
auip->atype = GOODFIT;
}
- else if (strcmp("af", alg) == 0) {
+ else if (sys_strcmp("af", alg) == 0) {
auip->atype = AFIT;
}
- else if (strcmp("aoff", alg) == 0) {
- auip->atype = AOFIRSTFIT;
- auip->init.aoff.flavor = AOFF_AOFF;
+ else if (sys_strcmp("aoff", alg) == 0) {
+ auip->atype = FIRSTFIT;
+ auip->init.aoff.crr_order = FF_AOFF;
+ auip->init.aoff.blk_order = FF_AOFF;
}
- else if (strcmp("aoffcbf", alg) == 0) {
- auip->atype = AOFIRSTFIT;
- auip->init.aoff.flavor = AOFF_BF;
+ else if (sys_strcmp("aoffcbf", alg) == 0) {
+ auip->atype = FIRSTFIT;
+ auip->init.aoff.crr_order = FF_AOFF;
+ auip->init.aoff.blk_order = FF_BF;
}
- else if (strcmp("aoffcaobf", alg) == 0) {
- auip->atype = AOFIRSTFIT;
- auip->init.aoff.flavor = AOFF_AOBF;
+ else if (sys_strcmp("aoffcaobf", alg) == 0) {
+ auip->atype = FIRSTFIT;
+ auip->init.aoff.crr_order = FF_AOFF;
+ auip->init.aoff.blk_order = FF_AOBF;
}
+ else if (sys_strcmp("ageffcaoff", alg) == 0) {
+ auip->atype = FIRSTFIT;
+ auip->init.aoff.crr_order = FF_AGEFF;
+ auip->init.aoff.blk_order = FF_AOFF;
+ }
+ else if (sys_strcmp("ageffcbf", alg) == 0) {
+ auip->atype = FIRSTFIT;
+ auip->init.aoff.crr_order = FF_AGEFF;
+ auip->init.aoff.blk_order = FF_BF;
+ }
+ else if (sys_strcmp("ageffcaobf", alg) == 0) {
+ auip->atype = FIRSTFIT;
+ auip->init.aoff.crr_order = FF_AGEFF;
+ auip->init.aoff.blk_order = FF_AOBF;
+ }
else {
bad_value(param, sub_param + 1, alg);
}
@@ -1526,10 +1561,8 @@ handle_args(int *argc, char **argv, erts_alc_hndl_args_init_t *init)
break;
case 'X':
if (has_prefix("scs", argv[i]+3)) {
-#ifdef ERTS_HAVE_EXEC_MMAPPER
- init->mseg.exec_mmap.scs =
-#endif
- get_mb_value(argv[i]+6, argv, &i);
+ /* Ignore obsolete */
+ (void) get_mb_value(argv[i]+6, argv, &i);
}
else
handle_au_arg(&init->exec_alloc, &argv[i][3], argv, &i, 0);
@@ -1646,7 +1679,7 @@ handle_args(int *argc, char **argv, erts_alc_hndl_args_init_t *init)
}
else if (has_prefix("e", param+2)) {
arg = get_value(param+3, argv, &i);
- if (strcmp("true", arg) != 0)
+ if (sys_strcmp("true", arg) != 0)
bad_value(param, param+3, arg);
}
else
@@ -1658,20 +1691,20 @@ handle_args(int *argc, char **argv, erts_alc_hndl_args_init_t *init)
case 'a': {
int a;
arg = get_value(argv[i]+4, argv, &i);
- if (strcmp("min", arg) == 0) {
+ if (sys_strcmp("min", arg) == 0) {
for (a = 0; a < aui_sz; a++)
aui[a]->enable = 0;
}
- else if (strcmp("max", arg) == 0) {
+ else if (sys_strcmp("max", arg) == 0) {
for (a = 0; a < aui_sz; a++)
aui[a]->enable = 1;
}
- else if (strcmp("config", arg) == 0) {
+ else if (sys_strcmp("config", arg) == 0) {
init->erts_alloc_config = 1;
}
- else if (strcmp("r9c", arg) == 0
- || strcmp("r10b", arg) == 0
- || strcmp("r11b", arg) == 0) {
+ else if (sys_strcmp("r9c", arg) == 0
+ || sys_strcmp("r10b", arg) == 0
+ || sys_strcmp("r11b", arg) == 0) {
set_default_sl_alloc_opts(&init->sl_alloc);
set_default_std_alloc_opts(&init->std_alloc);
set_default_ll_alloc_opts(&init->ll_alloc);
@@ -1683,7 +1716,7 @@ handle_args(int *argc, char **argv, erts_alc_hndl_args_init_t *init)
set_default_driver_alloc_opts(&init->fix_alloc);
init->driver_alloc.enable = 0;
- if (strcmp("r9c", arg) == 0) {
+ if (sys_strcmp("r9c", arg) == 0) {
init->sl_alloc.enable = 0;
init->std_alloc.enable = 0;
init->binary_alloc.enable = 0;
@@ -1710,18 +1743,18 @@ handle_args(int *argc, char **argv, erts_alc_hndl_args_init_t *init)
switch (argv[i][3]) {
case 's':
arg = get_value(argv[i]+4, argv, &i);
- if (strcmp("true", arg) == 0)
+ if (sys_strcmp("true", arg) == 0)
init->instr.stat = 1;
- else if (strcmp("false", arg) == 0)
+ else if (sys_strcmp("false", arg) == 0)
init->instr.stat = 0;
else
bad_value(param, param+3, arg);
break;
case 'm':
arg = get_value(argv[i]+4, argv, &i);
- if (strcmp("true", arg) == 0)
+ if (sys_strcmp("true", arg) == 0)
init->instr.map = 1;
- else if (strcmp("false", arg) == 0)
+ else if (sys_strcmp("false", arg) == 0)
init->instr.map = 0;
else
bad_value(param, param+3, arg);
@@ -1736,9 +1769,9 @@ handle_args(int *argc, char **argv, erts_alc_hndl_args_init_t *init)
case 'l':
if (has_prefix("pm", param+2)) {
arg = get_value(argv[i]+5, argv, &i);
- if (strcmp("all", arg) == 0)
+ if (sys_strcmp("all", arg) == 0)
lock_all_physical_memory = 1;
- else if (strcmp("no", arg) == 0)
+ else if (sys_strcmp("no", arg) == 0)
lock_all_physical_memory = 0;
else
bad_value(param, param+4, arg);
@@ -1788,8 +1821,8 @@ handle_args(int *argc, char **argv, erts_alc_hndl_args_init_t *init)
/* || init->instr.stat
|| init->instr.map */) {
while (i < *argc) {
- if(strcmp(argv[i], "-sname") == 0
- || strcmp(argv[i], "-name") == 0) {
+ if(sys_strcmp(argv[i], "-sname") == 0
+ || sys_strcmp(argv[i], "-name") == 0) {
if (i + 1 <*argc) {
init->instr.nodename = argv[i+1];
break;
@@ -2337,7 +2370,6 @@ erts_memory(fmtfn_t *print_to_p, void *print_to_arg, void *proc, Eterm earg)
}
tmp += erts_ptab_mem_size(&erts_proc);
tmp += erts_bif_timer_memory_size();
- tmp += erts_tot_link_lh_size();
size.processes = size.processes_used = tmp;
@@ -2348,12 +2380,11 @@ erts_memory(fmtfn_t *print_to_p, void *print_to_arg, void *proc, Eterm earg)
add_fix_values(&size.processes,
&size.processes_used,
fi,
- ERTS_ALC_T_MONITOR_SH);
-
+ ERTS_ALC_T_MONITOR);
add_fix_values(&size.processes,
&size.processes_used,
fi,
- ERTS_ALC_T_NLINK_SH);
+ ERTS_ALC_T_LINK);
add_fix_values(&size.processes,
&size.processes_used,
fi,
@@ -2584,11 +2615,6 @@ erts_allocated_areas(fmtfn_t *print_to_p, void *print_to_arg, void *proc)
i++;
values[i].arity = 2;
- values[i].name = "link_lh";
- values[i].ui[0] = erts_tot_link_lh_size();
- i++;
-
- values[i].arity = 2;
values[i].name = "process_table";
values[i].ui[0] = erts_ptab_mem_size(&erts_proc);
i++;
@@ -2650,7 +2676,7 @@ erts_allocated_areas(fmtfn_t *print_to_p, void *print_to_arg, void *proc)
Eterm atom;
if (hpp)
atom = am_atom_put(values[i].name,
- (int) strlen(values[i].name));
+ (int) sys_strlen(values[i].name));
else
atom = am_true;
@@ -2791,10 +2817,6 @@ erts_allocator_info(fmtfn_t to, void *arg)
erts_print(to, arg, "=allocator:erts_mmap.literal_mmap\n");
erts_mmap_info(&erts_literal_mmapper, &to, arg, NULL, NULL, &emis);
#endif
-#ifdef ERTS_HAVE_EXEC_MMAPPER
- erts_print(to, arg, "=allocator:erts_mmap.exec_mmap\n");
- erts_mmap_info(&erts_exec_mmapper, &to, arg, NULL, NULL, &emis);
-#endif
}
#endif
@@ -2841,7 +2863,7 @@ erts_allocator_options(void *proc)
for (a = ERTS_ALC_A_MIN; a <= ERTS_ALC_A_MAX; a++) {
Eterm tmp = NIL;
atoms[length] = am_atom_put((char *) ERTS_ALC_A2AD(a),
- strlen(ERTS_ALC_A2AD(a)));
+ sys_strlen(ERTS_ALC_A2AD(a)));
if (erts_allctrs_info[a].enabled) {
if (erts_allctrs_info[a].alloc_util) {
Allctr_t *allctr;
@@ -2935,7 +2957,7 @@ erts_allocator_options(void *proc)
for (a = ERTS_ALC_A_MIN; a <= ERTS_ALC_A_MAX; a++) {
if (erts_allctrs_info[a].enabled) {
terms[length++] = am_atom_put((char *) ERTS_ALC_A2AD(a),
- strlen(ERTS_ALC_A2AD(a)));
+ sys_strlen(ERTS_ALC_A2AD(a)));
}
}
@@ -2949,9 +2971,6 @@ erts_allocator_options(void *proc)
#if defined(ARCH_64) && defined(ERTS_HAVE_OS_PHYSICAL_MEMORY_RESERVATION)
terms[length++] = ERTS_MAKE_AM("literal_mmap");
#endif
-#ifdef ERTS_HAVE_EXEC_MMAPPER
- terms[length++] = ERTS_MAKE_AM("exec_mmap");
-#endif
features = length ? erts_bld_list(hpp, szp, length, terms) : NIL;
#if defined(__GLIBC__)
@@ -3041,9 +3060,6 @@ reply_alloc_info(void *vair)
# if defined(ARCH_64) && defined(ERTS_HAVE_OS_PHYSICAL_MEMORY_RESERVATION)
struct erts_mmap_info_struct mmap_info_literal;
# endif
-# ifdef ERTS_HAVE_EXEC_MMAPPER
- struct erts_mmap_info_struct mmap_info_exec;
-# endif
#endif
int i;
Eterm (*info_func)(Allctr_t *,
@@ -3172,17 +3188,6 @@ reply_alloc_info(void *vair)
erts_bld_atom(hpp,szp,"literal_mmap"),
ainfo);
# endif
-# ifdef ERTS_HAVE_EXEC_MMAPPER
- ai_list = erts_bld_cons(hpp, szp,
- ainfo, ai_list);
- ainfo = (air->only_sz ? NIL :
- erts_mmap_info(&erts_exec_mmapper, NULL, NULL,
- hpp, szp, &mmap_info_exec));
- ainfo = erts_bld_tuple3(hpp, szp,
- alloc_atom,
- erts_bld_atom(hpp,szp,"exec_mmap"),
- ainfo);
-# endif
#else /* !HAVE_ERTS_MMAP */
ainfo = erts_bld_tuple2(hpp, szp, alloc_atom,
am_false);
@@ -3392,6 +3397,65 @@ erts_request_alloc_info(struct process *c_p,
return 1;
}
+Eterm erts_alloc_set_dyn_param(Process* c_p, Eterm tuple)
+{
+ ErtsAllocatorThrSpec_t *tspec;
+ ErtsAlcType_t ai;
+ Allctr_t* allctr;
+ Eterm* tp;
+ Eterm res;
+
+ if (!is_tuple_arity(tuple, 3))
+ goto badarg;
+
+ tp = tuple_val(tuple);
+
+ /*
+ * Ex: {ets_alloc, sbct, 256000}
+ */
+ if (!is_atom(tp[1]) || !is_atom(tp[2]) || !is_integer(tp[3]))
+ goto badarg;
+
+ for (ai = ERTS_ALC_A_MIN; ai <= ERTS_ALC_A_MAX; ai++)
+ if (erts_is_atom_str(erts_alc_a2ad[ai], tp[1], 0))
+ break;
+
+ if (ai > ERTS_ALC_A_MAX)
+ goto badarg;
+
+ if (!erts_allctrs_info[ai].enabled ||
+ !erts_allctrs_info[ai].alloc_util) {
+ return am_notsup;
+ }
+
+ if (tp[2] == am_sbct) {
+ Uint sbct;
+ int i, ok;
+
+ if (!term_to_Uint(tp[3], &sbct))
+ goto badarg;
+
+ tspec = &erts_allctr_thr_spec[ai];
+ if (tspec->enabled) {
+ ok = 0;
+ for (i = 0; i < tspec->size; i++) {
+ allctr = tspec->allctr[i];
+ ok |= allctr->try_set_dyn_param(allctr, am_sbct, sbct);
+ }
+ }
+ else {
+ allctr = erts_allctrs_info[ai].extra;
+ ok = allctr->try_set_dyn_param(allctr, am_sbct, sbct);
+ }
+ return ok ? am_ok : am_notsup;
+ }
+ return am_notsup;
+
+badarg:
+ ERTS_BIF_PREP_ERROR(res, c_p, EXC_BADARG);
+ return res;
+}
+
/*
* The allocator wrapper prelocking stuff below is about the locking order.
* It only affects wrappers (erl_mtrace.c and erl_instrument.c) that keep locks
@@ -3528,7 +3592,7 @@ UWord erts_alc_test(UWord op, UWord a1, UWord a2, UWord a3)
&init.init.af,
&init.init.util);
break;
- case AOFIRSTFIT:
+ case FIRSTFIT:
allctr = erts_aoffalc_start((AOFFAllctr_t *)
erts_alloc(ERTS_ALC_T_UNDEF,
sizeof(AOFFAllctr_t)),
@@ -3622,7 +3686,9 @@ UWord erts_alc_test(UWord op, UWord a1, UWord a2, UWord a3)
case 0xf15: erts_free(ERTS_ALC_T_TEST, (void*)a1); return 0;
- case 0xf16: {
+ case 0xf16: return (UWord) erts_realloc(ERTS_ALC_T_TEST, (void*)a1, (Uint)a2);
+
+ case 0xf17: {
Uint extra_hdr_sz = UNIT_CEILING((Uint)a1);
ErtsAllocatorThrSpec_t* ts = &erts_allctr_thr_spec[ERTS_ALC_A_TEST];
Uint offset = ts->allctr[0]->mbc_header_size;
@@ -3649,7 +3715,7 @@ UWord erts_alc_test(UWord op, UWord a1, UWord a2, UWord a3)
*(void**)a3 = orig_destroying_mbc;
return offset;
}
- case 0xf17: {
+ case 0xf18: {
ErtsAllocatorThrSpec_t* ts = &erts_allctr_thr_spec[ERTS_ALC_A_TEST];
return ts->allctr[0]->largest_mbc_size;
}
@@ -3858,7 +3924,7 @@ set_memory_fence(void *ptr, Uint sz, ErtsAlcType_t n)
*(ui_ptr++) = sz;
*(ui_ptr++) = pattern;
- memcpy((void *) (((char *) ui_ptr)+sz), (void *) &pattern, sizeof(UWord));
+ sys_memcpy((void *) (((char *) ui_ptr)+sz), (void *) &pattern, sizeof(UWord));
#ifdef HARD_DEBUG
*mblkpp = hdbg_alloc((void *) ui_ptr, sz, n);
@@ -3898,7 +3964,7 @@ check_memory_fence(void *ptr, Uint *size, ErtsAlcType_t n, int func)
(UWord) ptr);
}
- memcpy((void *) &post_pattern, (void *) (((char *)ptr)+sz), sizeof(UWord));
+ sys_memcpy((void *) &post_pattern, (void *) (((char *)ptr)+sz), sizeof(UWord));
if (post_pattern != MK_PATTERN(n)
|| pre_pattern != post_pattern) {
diff --git a/erts/emulator/beam/erl_alloc.h b/erts/emulator/beam/erl_alloc.h
index 117f96a4ad..578a3717d9 100644
--- a/erts/emulator/beam/erl_alloc.h
+++ b/erts/emulator/beam/erl_alloc.h
@@ -126,8 +126,10 @@ typedef struct {
void *extra;
} ErtsAllocatorFunctions_t;
-extern ErtsAllocatorFunctions_t erts_allctrs[ERTS_ALC_A_MAX+1];
-extern ErtsAllocatorInfo_t erts_allctrs_info[ERTS_ALC_A_MAX+1];
+extern ErtsAllocatorFunctions_t
+ ERTS_WRITE_UNLIKELY(erts_allctrs[ERTS_ALC_A_MAX+1]);
+extern ErtsAllocatorInfo_t
+ ERTS_WRITE_UNLIKELY(erts_allctrs_info[ERTS_ALC_A_MAX+1]);
typedef struct {
int enabled;
@@ -144,7 +146,7 @@ typedef struct ErtsAllocatorWrapper_t_ {
void (*unlock)(void);
struct ErtsAllocatorWrapper_t_* next;
}ErtsAllocatorWrapper_t;
-ErtsAllocatorWrapper_t *erts_allctr_wrappers;
+extern ErtsAllocatorWrapper_t *erts_allctr_wrappers;
extern int erts_allctr_wrapper_prelocked;
extern erts_tsd_key_t erts_allctr_prelock_tsd_key;
void erts_allctr_wrapper_prelock_init(ErtsAllocatorWrapper_t* wrapper);
@@ -169,6 +171,8 @@ __decl_noreturn void erts_realloc_n_enomem(ErtsAlcType_t,void*,Uint)
__decl_noreturn void erts_alc_fatal_error(int,int,ErtsAlcType_t,...)
__noreturn;
+Eterm erts_alloc_set_dyn_param(struct process*, Eterm);
+
#undef ERTS_HAVE_IS_IN_LITERAL_RANGE
#if defined(ARCH_32) || defined(ERTS_HAVE_OS_PHYSICAL_MEMORY_RESERVATION)
# define ERTS_HAVE_IS_IN_LITERAL_RANGE
@@ -430,7 +434,7 @@ NAME##_free(TYPE *p) \
#ifdef DEBUG
#define ERTS_PRE_ALLOC_SIZE(SZ) ((SZ) < 1000 ? (SZ)/10 + 10 : 100)
-#define ERTS_PRE_ALLOC_CLOBBER(P, T) memset((void *) (P), 0xfd, sizeof(T))
+#define ERTS_PRE_ALLOC_CLOBBER(P, T) sys_memset((void *) (P), 0xfd, sizeof(T))
#else
#define ERTS_PRE_ALLOC_SIZE(SZ) ((SZ) > 1 ? (SZ) : 1)
#define ERTS_PRE_ALLOC_CLOBBER(P, T)
diff --git a/erts/emulator/beam/erl_alloc.types b/erts/emulator/beam/erl_alloc.types
index 8107f133aa..4a6a19b210 100644
--- a/erts/emulator/beam/erl_alloc.types
+++ b/erts/emulator/beam/erl_alloc.types
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2003-2017. All Rights Reserved.
+# Copyright Ericsson AB 2003-2018. 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.
@@ -282,6 +282,11 @@ type THR_PRGR_IDATA LONG_LIVED SYSTEM thr_prgr_internal_data
type THR_PRGR_DATA LONG_LIVED SYSTEM thr_prgr_data
type T_THR_PRGR_DATA SHORT_LIVED SYSTEM temp_thr_prgr_data
type RELEASE_LAREA SHORT_LIVED SYSTEM release_literal_area
+type SIG_DATA SHORT_LIVED PROCESSES signal_data
+type DIST_DEMONITOR SHORT_LIVED PROCESSES dist_demonitor
+type CML_CLEANUP SHORT_LIVED SYSTEM connection_ml_cleanup
+type ML_YIELD_STATE SHORT_LIVED SYSTEM monitor_link_yield_state
+type ML_DIST STANDARD SYSTEM monitor_link_dist
type ENVIRONMENT SYSTEM SYSTEM environment
@@ -326,8 +331,8 @@ type LCNT_VECTOR SHORT_LIVED SYSTEM lcnt_sample_vector
type DEBUG SHORT_LIVED SYSTEM debugging
type DDLL_PROCESS STANDARD SYSTEM ddll_processes
-type MONITOR_LH STANDARD PROCESSES monitor_lh
-type NLINK_LH STANDARD PROCESSES nlink_lh
+type MONITOR_EXT STANDARD PROCESSES monitor_extended
+type LINK_EXT STANDARD PROCESSES link_extended
type CODE LONG_LIVED CODE code
type LITERAL LITERAL CODE literal
type LITERAL_REF SHORT_LIVED CODE literal_area_ref
@@ -340,8 +345,8 @@ type LL_TEMP_TERM LONG_LIVED SYSTEM ll_temp_term
type NIF_TRAP_EXPORT STANDARD PROCESSES nif_trap_export_entry
type NIF_EXP_TRACE FIXED_SIZE PROCESSES nif_export_trace
type EXPORT LONG_LIVED CODE export_entry
-type MONITOR_SH FIXED_SIZE PROCESSES monitor_sh
-type NLINK_SH FIXED_SIZE PROCESSES nlink_sh
+type MONITOR FIXED_SIZE PROCESSES monitor
+type LINK FIXED_SIZE PROCESSES link
type AINFO_REQ SHORT_LIVED SYSTEM alloc_info_request
type SCHED_WTIME_REQ SHORT_LIVED SYSTEM sched_wall_time_request
type GC_INFO_REQ SHORT_LIVED SYSTEM gc_info_request
diff --git a/erts/emulator/beam/erl_alloc_util.c b/erts/emulator/beam/erl_alloc_util.c
index 4d4bddb93f..e148be7af6 100644
--- a/erts/emulator/beam/erl_alloc_util.c
+++ b/erts/emulator/beam/erl_alloc_util.c
@@ -362,8 +362,10 @@ do { \
#define ERTS_CRR_ALCTR_FLG_IN_POOL (((erts_aint_t) 1) << 0)
#define ERTS_CRR_ALCTR_FLG_BUSY (((erts_aint_t) 1) << 1)
+#define ERTS_CRR_ALCTR_FLG_HOMECOMING (((erts_aint_t) 1) << 2)
#define ERTS_CRR_ALCTR_FLG_MASK (ERTS_CRR_ALCTR_FLG_IN_POOL | \
- ERTS_CRR_ALCTR_FLG_BUSY)
+ ERTS_CRR_ALCTR_FLG_BUSY | \
+ ERTS_CRR_ALCTR_FLG_HOMECOMING)
#define SBC_HEADER_SIZE \
(UNIT_CEILING(offsetof(Carrier_t, cpool) \
@@ -563,7 +565,7 @@ do { \
DEBUG_CHECK_CARRIER_NO_SZ((AP)); \
} while (0)
-#define STAT_MBC_CPOOL_INSERT(AP, CRR) \
+#define STAT_MBC_ABANDON(AP, CRR) \
do { \
UWord csz__ = CARRIER_SZ((CRR)); \
if (IS_MSEG_CARRIER((CRR))) \
@@ -872,7 +874,7 @@ erts_alcu_literal_32_mseg_dealloc(Allctr_t *allctr, void *seg, Uint size,
#elif defined(ARCH_64) && defined(ERTS_HAVE_OS_PHYSICAL_MEMORY_RESERVATION)
/* For allocators that have their own mmapper (super carrier),
- * like literal_alloc and exec_alloc on amd64
+ * like literal_alloc.
*/
void*
erts_alcu_mmapper_mseg_alloc(Allctr_t *allctr, Uint *size_p, Uint flags)
@@ -914,10 +916,10 @@ erts_alcu_mmapper_mseg_dealloc(Allctr_t *allctr, void *seg, Uint size,
}
#endif /* ARCH_64 && ERTS_HAVE_OS_PHYSICAL_MEMORY_RESERVATION */
-#if defined(ERTS_ALC_A_EXEC) && !defined(ERTS_HAVE_EXEC_MMAPPER)
+#if defined(ERTS_ALC_A_EXEC)
/*
- * For exec_alloc on non-amd64 that just need memory with PROT_EXEC
+ * For exec_alloc that need memory with PROT_EXEC
*/
void*
erts_alcu_exec_mseg_alloc(Allctr_t *allctr, Uint *size_p, Uint flags)
@@ -956,7 +958,7 @@ erts_alcu_exec_mseg_dealloc(Allctr_t *allctr, void *seg, Uint size, Uint flags)
ASSERT(r == 0); (void)r;
erts_alcu_mseg_dealloc(allctr, seg, size, flags);
}
-#endif /* ERTS_ALC_A_EXEC && !ERTS_HAVE_EXEC_MMAPPER */
+#endif /* ERTS_ALC_A_EXEC */
#endif /* HAVE_ERTS_MSEG */
@@ -1153,89 +1155,23 @@ unlink_carrier(CarrierList_t *cl, Carrier_t *crr)
ASSERT(crr->next);
crr->next->prev = crr->prev;
}
-}
-
-
#ifdef DEBUG
-static int is_in_list(ErtsDoubleLink_t* sentinel, ErtsDoubleLink_t* node)
-{
- ErtsDoubleLink_t* p;
-
- ASSERT(node != sentinel);
- for (p = sentinel->next; p != sentinel; p = p->next) {
- if (p == node)
- return 1;
- }
- return 0;
-}
-#endif /* DEBUG */
-
-static ERTS_INLINE void
-link_edl_after(ErtsDoubleLink_t* after_me, ErtsDoubleLink_t* node)
-{
- ErtsDoubleLink_t* before_me = after_me->next;
- ASSERT(node != after_me && node != before_me);
- node->next = before_me;
- node->prev = after_me;
- before_me->prev = node;
- after_me->next = node;
-}
-
-static ERTS_INLINE void
-link_edl_before(ErtsDoubleLink_t* before_me, ErtsDoubleLink_t* node)
-{
- ErtsDoubleLink_t* after_me = before_me->prev;
- ASSERT(node != before_me && node != after_me);
- node->next = before_me;
- node->prev = after_me;
- before_me->prev = node;
- after_me->next = node;
-}
-
-static ERTS_INLINE void
-unlink_edl(ErtsDoubleLink_t* node)
-{
- node->next->prev = node->prev;
- node->prev->next = node->next;
-}
-
-static ERTS_INLINE void
-relink_edl_before(ErtsDoubleLink_t* before_me, ErtsDoubleLink_t* node)
-{
- if (node != before_me && node != before_me->prev) {
- unlink_edl(node);
- link_edl_before(before_me, node);
- }
+ crr->next = crr;
+ crr->prev = crr;
+#endif
}
static ERTS_INLINE int is_abandoned(Carrier_t *crr)
{
- return crr->cpool.abandoned.next != NULL;
-}
-
-static ERTS_INLINE void
-link_abandoned_carrier(ErtsDoubleLink_t* list, Carrier_t *crr)
-{
- ASSERT(!is_abandoned(crr));
-
- link_edl_after(list, &crr->cpool.abandoned);
-
- ASSERT(crr->cpool.abandoned.next != &crr->cpool.abandoned);
- ASSERT(crr->cpool.abandoned.prev != &crr->cpool.abandoned);
+ return crr->cpool.state != ERTS_MBC_IS_HOME;
}
static ERTS_INLINE void
unlink_abandoned_carrier(Carrier_t *crr)
{
- ASSERT(is_in_list(&crr->cpool.orig_allctr->cpool.pooled_list,
- &crr->cpool.abandoned) ||
- is_in_list(&crr->cpool.orig_allctr->cpool.traitor_list,
- &crr->cpool.abandoned));
-
- unlink_edl(&crr->cpool.abandoned);
-
- crr->cpool.abandoned.next = NULL;
- crr->cpool.abandoned.prev = NULL;
+ if (crr->cpool.state == ERTS_MBC_WAS_POOLED) {
+ aoff_remove_pooled_mbc(crr->cpool.orig_allctr, crr);
+ }
}
static ERTS_INLINE void
@@ -1243,24 +1179,19 @@ clear_busy_pool_carrier(Allctr_t *allctr, Carrier_t *crr)
{
if (crr) {
erts_aint_t max_size;
- erts_aint_t new_val;
+ erts_aint_t iallctr;
max_size = (erts_aint_t) allctr->largest_fblk_in_mbc(allctr, crr);
erts_atomic_set_nob(&crr->cpool.max_size, max_size);
- new_val = (((erts_aint_t) allctr)|ERTS_CRR_ALCTR_FLG_IN_POOL);
+ iallctr = erts_atomic_read_nob(&crr->allctr);
+ ERTS_ALC_CPOOL_ASSERT((iallctr & ~ERTS_CRR_ALCTR_FLG_HOMECOMING)
+ == ((erts_aint_t)allctr |
+ ERTS_CRR_ALCTR_FLG_IN_POOL |
+ ERTS_CRR_ALCTR_FLG_BUSY));
-#ifdef ERTS_ALC_CPOOL_DEBUG
- {
- erts_aint_t old_val = new_val|ERTS_CRR_ALCTR_FLG_BUSY;
-
- ERTS_ALC_CPOOL_ASSERT(old_val
- == erts_atomic_xchg_relb(&crr->allctr,
- new_val));
- }
-#else
- erts_atomic_set_relb(&crr->allctr, new_val);
-#endif
+ iallctr &= ~ERTS_CRR_ALCTR_FLG_BUSY;
+ erts_atomic_set_relb(&crr->allctr, iallctr);
}
}
@@ -1658,6 +1589,11 @@ dealloc_mbc(Allctr_t *allctr, Carrier_t *crr)
}
+static void set_new_allctr_abandon_limit(Allctr_t*);
+static void abandon_carrier(Allctr_t*, Carrier_t*);
+static void poolify_my_carrier(Allctr_t*, Carrier_t*);
+static void enqueue_homecoming(Allctr_t*, Carrier_t*);
+
static ERTS_INLINE Allctr_t*
get_pref_allctr(void *extra)
{
@@ -1724,9 +1660,23 @@ get_used_allctr(Allctr_t *pref_allctr, int pref_lock, void *p, UWord *sizep,
erts_aint_t act;
ERTS_ALC_CPOOL_ASSERT(!(iallctr & ERTS_CRR_ALCTR_FLG_BUSY));
- act = erts_atomic_cmpxchg_ddrb(&crr->allctr,
- iallctr|ERTS_CRR_ALCTR_FLG_BUSY,
- iallctr);
+ if (iallctr & ERTS_CRR_ALCTR_FLG_HOMECOMING) {
+ /*
+ * This carrier has just been given back to us by writing
+ * to crr->allctr with a write barrier (see abandon_carrier).
+ *
+ * We need a mathing read barrier to guarantee a correct view
+ * of the carrier for deallocation work.
+ */
+ act = erts_atomic_cmpxchg_rb(&crr->allctr,
+ iallctr|ERTS_CRR_ALCTR_FLG_BUSY,
+ iallctr);
+ }
+ else {
+ act = erts_atomic_cmpxchg_ddrb(&crr->allctr,
+ iallctr|ERTS_CRR_ALCTR_FLG_BUSY,
+ iallctr);
+ }
if (act == iallctr) {
*busy_pcrr_pp = crr;
break;
@@ -1742,13 +1692,6 @@ get_used_allctr(Allctr_t *pref_allctr, int pref_lock, void *p, UWord *sizep,
erts_mtx_unlock(&pref_allctr->mutex);
}
}
-
- ERTS_ALC_CPOOL_ASSERT(
- (((iallctr & ~ERTS_CRR_ALCTR_FLG_MASK) == (erts_aint_t) pref_allctr)
- ? (((iallctr & ERTS_CRR_ALCTR_FLG_MASK) == ERTS_CRR_ALCTR_FLG_IN_POOL)
- || ((iallctr & ERTS_CRR_ALCTR_FLG_MASK) == 0))
- : 1));
-
return used_allctr;
}
}
@@ -2000,9 +1943,9 @@ handle_delayed_fix_dealloc(Allctr_t *allctr, void *ptr)
/* Carrier migrated; need to redirect block to new owner... */
int cinit = used_allctr->dd.ix - allctr->dd.ix;
- ERTS_ALC_CPOOL_ASSERT(!busy_pcrr_p);
+ ERTS_ALC_CPOOL_ASSERT(!busy_pcrr_p);
- DEC_CC(allctr->calls.this_free);
+ DEC_CC(allctr->calls.this_free);
((ErtsAllctrFixDDBlock_t *) ptr)->fix_type = type;
if (ddq_enqueue(&used_allctr->dd.q, ptr, cinit))
@@ -2011,8 +1954,9 @@ handle_delayed_fix_dealloc(Allctr_t *allctr, void *ptr)
}
}
-static void
-schedule_dealloc_carrier(Allctr_t *allctr, Carrier_t *crr);
+static void schedule_dealloc_carrier(Allctr_t*, Carrier_t*);
+static void dealloc_my_carrier(Allctr_t*, Carrier_t*);
+
static ERTS_INLINE int
handle_delayed_dealloc(Allctr_t *allctr,
@@ -2074,39 +2018,61 @@ handle_delayed_dealloc(Allctr_t *allctr,
res = 1;
blk = UMEM2BLK(ptr);
- if (IS_FREE_LAST_MBC_BLK(blk)) {
+ if (blk->bhdr == HOMECOMING_MBC_BLK_HDR) {
/*
* A multiblock carrier that previously has been migrated away
- * from us and now is back to be deallocated. For more info
- * see schedule_dealloc_carrier().
- *
- * Note that we cannot use FBLK_TO_MBC(blk) since it
- * data has been overwritten by the queue.
+ * from us, was sent back to us either because
+ * - it became empty and we need to deallocated it, or
+ * - it was inserted into the pool and we need to update our pooled_tree
*/
- Carrier_t *crr = FIRST_BLK_TO_MBC(allctr, blk);
-
- /* Restore word overwritten by the dd-queue as it will be read
- * if this carrier is pulled from dc_list by cpool_fetch()
- */
- ERTS_ALC_CPOOL_ASSERT(FBLK_TO_MBC(blk) != crr);
- ERTS_CT_ASSERT(sizeof(ErtsAllctrDDBlock_t) == sizeof(void*));
-#ifdef MBC_ABLK_OFFSET_BITS
- blk->u.carrier = crr;
-#else
- blk->carrier = crr;
-#endif
+ Carrier_t *crr = ErtsContainerStruct(blk, Carrier_t,
+ cpool.homecoming_dd.blk);
+ Block_t* first_blk = MBC_TO_FIRST_BLK(allctr, crr);
+ erts_aint_t iallctr;
ERTS_ALC_CPOOL_ASSERT(ERTS_ALC_IS_CPOOL_ENABLED(allctr));
ERTS_ALC_CPOOL_ASSERT(allctr == crr->cpool.orig_allctr);
- ERTS_ALC_CPOOL_ASSERT(((erts_aint_t) allctr)
- != (erts_atomic_read_nob(&crr->allctr)
- & ~ERTS_CRR_ALCTR_FLG_MASK));
-
- erts_atomic_set_nob(&crr->allctr, ((erts_aint_t) allctr));
- schedule_dealloc_carrier(allctr, crr);
+ iallctr = erts_atomic_read_nob(&crr->allctr);
+ ASSERT(iallctr & ERTS_CRR_ALCTR_FLG_HOMECOMING);
+ while (1) {
+ if ((iallctr & (~ERTS_CRR_ALCTR_FLG_MASK |
+ ERTS_CRR_ALCTR_FLG_IN_POOL))
+ == (erts_aint_t)allctr) {
+ /*
+ * Carrier is home (mine and not in pool)
+ */
+ ASSERT(!(iallctr & ERTS_CRR_ALCTR_FLG_BUSY));
+ erts_atomic_set_nob(&crr->allctr, (erts_aint_t)allctr);
+ if (IS_FREE_LAST_MBC_BLK(first_blk))
+ dealloc_my_carrier(allctr, crr);
+ else
+ ASSERT(crr->cpool.state == ERTS_MBC_IS_HOME);
+ }
+ else {
+ erts_aint_t exp = iallctr;
+ erts_aint_t want = iallctr & ~ERTS_CRR_ALCTR_FLG_HOMECOMING;
+
+ iallctr = erts_atomic_cmpxchg_nob(&crr->allctr,
+ want,
+ exp);
+ if (iallctr != exp)
+ continue; /* retry */
+
+ ASSERT(crr->cpool.state != ERTS_MBC_IS_HOME);
+ unlink_abandoned_carrier(crr);
+ if (iallctr & ERTS_CRR_ALCTR_FLG_IN_POOL)
+ poolify_my_carrier(allctr, crr);
+ else
+ crr->cpool.state = ERTS_MBC_WAS_TRAITOR;
+ }
+ break;
+ }
}
else {
+ ASSERT(IS_SBC_BLK(blk) || (ABLK_TO_MBC(blk) !=
+ ErtsContainerStruct(blk, Carrier_t,
+ cpool.homecoming_dd.blk)));
INC_CC(allctr->calls.this_free);
@@ -2148,20 +2114,26 @@ enqueue_dealloc_other_instance(ErtsAlcType_t type,
erts_alloc_notify_delayed_dealloc(allctr->ix);
}
-
-static void
-set_new_allctr_abandon_limit(Allctr_t *allctr);
-static void
-abandon_carrier(Allctr_t *allctr, Carrier_t *crr);
-
+static ERTS_INLINE void
+update_pooled_tree(Allctr_t *allctr, Carrier_t *crr, Uint blk_sz)
+{
+ if (allctr == crr->cpool.orig_allctr && crr->cpool.state == ERTS_MBC_WAS_POOLED) {
+ /*
+ * Update pooled_tree with a potentially new (larger) max_sz
+ */
+ AOFF_RBTree_t* crr_node = &crr->cpool.pooled;
+ if (blk_sz > crr_node->hdr.bhdr) {
+ crr_node->hdr.bhdr = blk_sz;
+ erts_aoff_larger_max_size(crr_node);
+ }
+ }
+}
static ERTS_INLINE void
check_abandon_carrier(Allctr_t *allctr, Block_t *fblk, Carrier_t **busy_pcrr_pp)
{
Carrier_t *crr;
-
- if (busy_pcrr_pp && *busy_pcrr_pp)
- return;
+ UWord ncrr_in_pool, largest_fblk;
if (!ERTS_ALC_IS_CPOOL_ENABLED(allctr))
return;
@@ -2170,8 +2142,7 @@ check_abandon_carrier(Allctr_t *allctr, Block_t *fblk, Carrier_t **busy_pcrr_pp)
if (--allctr->cpool.check_limit_count <= 0)
set_new_allctr_abandon_limit(allctr);
- if (!erts_thr_progress_is_managed_thread())
- return;
+ ASSERT(erts_thr_progress_is_managed_thread());
if (allctr->cpool.disable_abandon)
return;
@@ -2179,6 +2150,9 @@ check_abandon_carrier(Allctr_t *allctr, Block_t *fblk, Carrier_t **busy_pcrr_pp)
if (allctr->mbcs.blocks.curr.size > allctr->cpool.abandon_limit)
return;
+ ncrr_in_pool = erts_atomic_read_nob(&allctr->cpool.stat.no_carriers);
+ if (ncrr_in_pool >= allctr->cpool.in_pool_limit)
+ return;
crr = FBLK_TO_MBC(fblk);
@@ -2189,9 +2163,14 @@ check_abandon_carrier(Allctr_t *allctr, Block_t *fblk, Carrier_t **busy_pcrr_pp)
return;
if (crr->cpool.thr_prgr != ERTS_THR_PRGR_INVALID
- && !erts_thr_progress_has_reached(crr->cpool.thr_prgr))
- return;
+ && !erts_thr_progress_has_reached(crr->cpool.thr_prgr))
+ return;
+
+ largest_fblk = allctr->largest_fblk_in_mbc(allctr, crr);
+ if (largest_fblk < allctr->cpool.fblk_min_limit)
+ return;
+ erts_atomic_set_nob(&crr->cpool.max_size, largest_fblk);
abandon_carrier(allctr, crr);
}
@@ -2237,6 +2216,7 @@ dealloc_block(Allctr_t *allctr, void *ptr, ErtsAlcFixList_t *fix, int dec_cc_on_
else {
Carrier_t *busy_pcrr_p;
Allctr_t *used_allctr;
+
used_allctr = get_used_allctr(allctr, ERTS_ALC_TS_PREF_LOCK_NO, ptr,
NULL, &busy_pcrr_p);
if (used_allctr == allctr) {
@@ -2253,10 +2233,10 @@ dealloc_block(Allctr_t *allctr, void *ptr, ErtsAlcFixList_t *fix, int dec_cc_on_
/* Carrier migrated; need to redirect block to new owner... */
int cinit = used_allctr->dd.ix - allctr->dd.ix;
- ERTS_ALC_CPOOL_ASSERT(!busy_pcrr_p);
+ ERTS_ALC_CPOOL_ASSERT(!busy_pcrr_p);
- if (dec_cc_on_redirect)
- DEC_CC(allctr->calls.this_free);
+ if (dec_cc_on_redirect)
+ DEC_CC(allctr->calls.this_free);
if (ddq_enqueue(&used_allctr->dd.q, ptr, cinit))
erts_alloc_notify_delayed_dealloc(used_allctr->ix);
}
@@ -2500,15 +2480,16 @@ mbc_free(Allctr_t *allctr, void *p, Carrier_t **busy_pcrr_pp)
ASSERT(blk_sz % sizeof(Unit_t) == 0);
ASSERT(IS_MBC_BLK(blk));
- if (is_first_blk
- && is_last_blk
- && allctr->main_carrier != FIRST_BLK_TO_MBC(allctr, blk)) {
- destroy_carrier(allctr, blk, busy_pcrr_pp);
+ if (is_first_blk && is_last_blk && crr != allctr->main_carrier) {
+ destroy_carrier(allctr, blk, busy_pcrr_pp);
}
else {
(*allctr->link_free_block)(allctr, blk);
HARD_CHECK_BLK_CARRIER(allctr, blk);
- check_abandon_carrier(allctr, blk, busy_pcrr_pp);
+ if (busy_pcrr_pp && *busy_pcrr_pp)
+ update_pooled_tree(allctr, crr, blk_sz);
+ else
+ check_abandon_carrier(allctr, blk, busy_pcrr_pp);
}
}
@@ -2542,8 +2523,19 @@ mbc_realloc(Allctr_t *allctr, void *p, Uint size, Uint32 alcu_flgs,
return NULL;
#else /* !MBC_REALLOC_ALWAYS_MOVES */
- if (busy_pcrr_pp && *busy_pcrr_pp)
- goto realloc_move; /* Don't want to use carrier in pool */
+ if (busy_pcrr_pp && *busy_pcrr_pp) {
+ /*
+ * Don't want to use carrier in pool
+ */
+ new_p = mbc_alloc(allctr, size);
+ if (!new_p)
+ return NULL;
+ new_blk = UMEM2BLK(new_p);
+ ASSERT(!(IS_MBC_BLK(new_blk) && ABLK_TO_MBC(new_blk) == *busy_pcrr_pp));
+ sys_memcpy(new_p, p, MIN(size, old_blk_sz - ABLK_HDR_SZ));
+ mbc_free(allctr, p, busy_pcrr_pp);
+ return new_p;
+ }
get_blk_sz = blk_sz = UMEMSZ2BLKSZ(allctr, size);
@@ -2776,7 +2768,7 @@ mbc_realloc(Allctr_t *allctr, void *p, Uint size, Uint32 alcu_flgs,
if (cand_blk_sz < get_blk_sz) {
/* We wont fit in cand_blk get a new one */
- realloc_move:
+
#endif /* !MBC_REALLOC_ALWAYS_MOVES */
new_p = mbc_alloc(allctr, size);
@@ -2880,8 +2872,7 @@ mbc_realloc(Allctr_t *allctr, void *p, Uint size, Uint32 alcu_flgs,
#define ERTS_ALC_MAX_DEALLOC_CARRIER 10
-#define ERTS_ALC_CPOOL_MAX_FETCH_INSPECT 20
-#define ERTS_ALC_CPOOL_MAX_TRAITOR_INSPECT 10
+#define ERTS_ALC_CPOOL_MAX_FETCH_INSPECT 100
#define ERTS_ALC_CPOOL_CHECK_LIMIT_COUNT 100
#define ERTS_ALC_CPOOL_MAX_FAILED_STAT_READS 3
@@ -3045,19 +3036,18 @@ cpool_insert(Allctr_t *allctr, Carrier_t *crr)
ErtsAlcCPoolData_t *cpd1p, *cpd2p;
erts_aint_t val;
ErtsAlcCPoolData_t *sentinel = &carrier_pool[allctr->alloc_no].sentinel;
+ Allctr_t *orig_allctr = crr->cpool.orig_allctr;
ERTS_ALC_CPOOL_ASSERT(allctr->alloc_no == ERTS_ALC_A_INVALID /* testcase */
|| erts_thr_progress_is_managed_thread());
- ERTS_ALC_CPOOL_ASSERT(erts_atomic_read_nob(&crr->allctr)
- == (erts_aint_t) allctr);
- erts_atomic_add_nob(&allctr->cpool.stat.blocks_size,
+ erts_atomic_add_nob(&orig_allctr->cpool.stat.blocks_size,
(erts_aint_t) crr->cpool.blocks_size);
- erts_atomic_add_nob(&allctr->cpool.stat.no_blocks,
+ erts_atomic_add_nob(&orig_allctr->cpool.stat.no_blocks,
(erts_aint_t) crr->cpool.blocks);
- erts_atomic_add_nob(&allctr->cpool.stat.carriers_size,
+ erts_atomic_add_nob(&orig_allctr->cpool.stat.carriers_size,
(erts_aint_t) CARRIER_SZ(crr));
- erts_atomic_inc_nob(&allctr->cpool.stat.no_carriers);
+ erts_atomic_inc_nob(&orig_allctr->cpool.stat.no_carriers);
/*
* We search in 'next' direction and begin by passing
@@ -3118,8 +3108,6 @@ cpool_insert(Allctr_t *allctr, Carrier_t *crr)
(erts_aint_t) &crr->cpool,
(erts_aint_t) cpd1p);
- erts_atomic_set_wb(&crr->allctr,
- ((erts_aint_t) allctr)|ERTS_CRR_ALCTR_FLG_IN_POOL);
LTTNG3(carrier_pool_put, ERTS_ALC_A2AD(allctr->alloc_no), allctr->ix, CARRIER_SZ(crr));
}
@@ -3221,130 +3209,126 @@ cpool_delete(Allctr_t *allctr, Allctr_t *prev_allctr, Carrier_t *crr)
static Carrier_t *
cpool_fetch(Allctr_t *allctr, UWord size)
{
- int i, i_stop, has_passed_sentinel;
+ enum { IGNORANT, HAS_SEEN_SENTINEL, THE_LAST_ONE } loop_state;
+ int i;
Carrier_t *crr;
+ Carrier_t *reinsert_crr = NULL;
ErtsAlcCPoolData_t *cpdp;
- ErtsAlcCPoolData_t *cpool_entrance;
+ ErtsAlcCPoolData_t *cpool_entrance = NULL;
ErtsAlcCPoolData_t *sentinel;
- ErtsDoubleLink_t* dl;
- ErtsDoubleLink_t* first_old_traitor;
ERTS_ALC_CPOOL_ASSERT(allctr->alloc_no == ERTS_ALC_A_INVALID /* testcase */
|| erts_thr_progress_is_managed_thread());
i = ERTS_ALC_CPOOL_MAX_FETCH_INSPECT;
- first_old_traitor = allctr->cpool.traitor_list.next;
- cpool_entrance = NULL;
LTTNG3(carrier_pool_get, ERTS_ALC_A2AD(allctr->alloc_no), allctr->ix, (unsigned long)size);
/*
- * Search my own pooled_list,
+ * Search my own pooled_tree,
* i.e my abandoned carriers that were in the pool last time I checked.
*/
+ do {
+ erts_aint_t exp, act;
+
+ crr = aoff_lookup_pooled_mbc(allctr, size);
+ if (!crr)
+ break;
+
+ ASSERT(crr->cpool.state == ERTS_MBC_WAS_POOLED);
+ ASSERT(crr->cpool.orig_allctr == allctr);
+
+ aoff_remove_pooled_mbc(allctr, crr);
+
+ exp = erts_atomic_read_nob(&crr->allctr);
+ if (exp & ERTS_CRR_ALCTR_FLG_IN_POOL) {
+ ASSERT((exp & ~ERTS_CRR_ALCTR_FLG_MASK) == (erts_aint_t)allctr);
+ if (erts_atomic_read_nob(&crr->cpool.max_size) < size) {
+ /*
+ * This carrier has been fetched and inserted back again
+ * by a foreign allocator. That's why it has a stale search size.
+ */
+ ASSERT(exp & ERTS_CRR_ALCTR_FLG_HOMECOMING);
+ crr->cpool.pooled.hdr.bhdr = erts_atomic_read_nob(&crr->cpool.max_size);
+ aoff_add_pooled_mbc(allctr, crr);
+ INC_CC(allctr->cpool.stat.skip_size);
+ continue;
+ }
+ else if (exp & ERTS_CRR_ALCTR_FLG_BUSY) {
+ /*
+ * This must be our own carrier as part of a realloc call.
+ * Skip it to make things simpler.
+ * Must wait to re-insert to not be found again by lookup.
+ */
+ ASSERT(!reinsert_crr);
+ reinsert_crr = crr;
+ INC_CC(allctr->cpool.stat.skip_busy);
+ continue;
+ }
- dl = allctr->cpool.pooled_list.next;
- while(dl != &allctr->cpool.pooled_list) {
- erts_aint_t exp, act;
- crr = (Carrier_t *) (((char *) dl) - offsetof(Carrier_t, cpool.abandoned));
-
- ASSERT(!is_in_list(&allctr->cpool.traitor_list, dl));
- ASSERT(crr->cpool.orig_allctr == allctr);
- dl = dl->next;
- exp = erts_atomic_read_rb(&crr->allctr);
- if ((exp & ERTS_CRR_ALCTR_FLG_MASK) == ERTS_CRR_ALCTR_FLG_IN_POOL
- && erts_atomic_read_nob(&crr->cpool.max_size) >= size) {
- /* Try to fetch it... */
- act = erts_atomic_cmpxchg_mb(&crr->allctr,
- (erts_aint_t) allctr,
- exp);
- if (act == exp) {
- cpool_delete(allctr, ((Allctr_t *) (act & ~ERTS_CRR_ALCTR_FLG_MASK)), crr);
- unlink_abandoned_carrier(crr);
-
- /* Move sentinel to continue next search from here */
- relink_edl_before(dl, &allctr->cpool.pooled_list);
- return crr;
- }
- exp = act;
- }
- if (exp & ERTS_CRR_ALCTR_FLG_IN_POOL) {
- if (!cpool_entrance)
- cpool_entrance = &crr->cpool;
- }
- else { /* Not in pool, move to traitor_list */
- unlink_abandoned_carrier(crr);
- link_abandoned_carrier(&allctr->cpool.traitor_list, crr);
- }
- if (--i <= 0) {
- /* Move sentinel to continue next search from here */
- relink_edl_before(dl, &allctr->cpool.pooled_list);
- return NULL;
- }
- }
+ /* Try to fetch it... */
+ act = erts_atomic_cmpxchg_mb(&crr->allctr,
+ exp & ~ERTS_CRR_ALCTR_FLG_IN_POOL,
+ exp);
+ if (act == exp) {
+ cpool_delete(allctr, allctr, crr);
+ crr->cpool.state = ERTS_MBC_IS_HOME;
+
+ if (reinsert_crr)
+ aoff_add_pooled_mbc(allctr, reinsert_crr);
+ return crr;
+ }
+ exp = act;
+ INC_CC(allctr->cpool.stat.skip_race);
+ }
+ else
+ INC_CC(allctr->cpool.stat.skip_not_pooled);
- /* Now search traitor_list.
- * i.e carriers employed by other allocators last time I checked.
- * They might have been abandoned since then.
- */
+ /* Not in pool anymore */
+ ASSERT(!(exp & ERTS_CRR_ALCTR_FLG_BUSY));
+ crr->cpool.state = ERTS_MBC_WAS_TRAITOR;
- i_stop = (i < ERTS_ALC_CPOOL_MAX_TRAITOR_INSPECT ?
- 0 : i - ERTS_ALC_CPOOL_MAX_TRAITOR_INSPECT);
- dl = first_old_traitor;
- while(dl != &allctr->cpool.traitor_list) {
- erts_aint_t exp, act;
- crr = (Carrier_t *) (((char *) dl) - offsetof(Carrier_t, cpool.abandoned));
- ASSERT(dl != &allctr->cpool.pooled_list);
- ASSERT(crr->cpool.orig_allctr == allctr);
- dl = dl->next;
- exp = erts_atomic_read_rb(&crr->allctr);
- if (exp & ERTS_CRR_ALCTR_FLG_IN_POOL) {
- if (!(exp & ERTS_CRR_ALCTR_FLG_BUSY)
- && erts_atomic_read_nob(&crr->cpool.max_size) >= size) {
- /* Try to fetch it... */
- act = erts_atomic_cmpxchg_mb(&crr->allctr,
- (erts_aint_t) allctr,
- exp);
- if (act == exp) {
- cpool_delete(allctr, ((Allctr_t *) (act & ~ERTS_CRR_ALCTR_FLG_MASK)), crr);
- unlink_abandoned_carrier(crr);
+ }while (--i > 0);
- /* Move sentinel to continue next search from here */
- relink_edl_before(dl, &allctr->cpool.traitor_list);
- return crr;
- }
- exp = act;
- }
- if (exp & ERTS_CRR_ALCTR_FLG_IN_POOL) {
- if (!cpool_entrance)
- cpool_entrance = &crr->cpool;
+ if (reinsert_crr)
+ aoff_add_pooled_mbc(allctr, reinsert_crr);
- /* Move to pooled_list */
- unlink_abandoned_carrier(crr);
- link_abandoned_carrier(&allctr->cpool.pooled_list, crr);
- }
- }
- if (--i <= i_stop) {
- /* Move sentinel to continue next search from here */
- relink_edl_before(dl, &allctr->cpool.traitor_list);
- if (i > 0)
- break;
- else
- return NULL;
- }
+ /*
+ * Try find a nice cpool_entrance
+ */
+ while (allctr->cpool.pooled_tree) {
+ erts_aint_t iallctr;
+
+ crr = ErtsContainerStruct(allctr->cpool.pooled_tree, Carrier_t, cpool.pooled);
+ iallctr = erts_atomic_read_nob(&crr->allctr);
+ if (iallctr & ERTS_CRR_ALCTR_FLG_IN_POOL) {
+ cpool_entrance = &crr->cpool;
+ break;
+ }
+ /* Not in pool anymore */
+ ASSERT(!(iallctr & ERTS_CRR_ALCTR_FLG_BUSY));
+ aoff_remove_pooled_mbc(allctr, crr);
+ crr->cpool.state = ERTS_MBC_WAS_TRAITOR;
+
+ if (--i <= 0) {
+ INC_CC(allctr->cpool.stat.fail_pooled);
+ return NULL;
+ }
}
+
/*
* Finally search the shared pool and try employ foreign carriers
*/
-
sentinel = &carrier_pool[allctr->alloc_no].sentinel;
if (cpool_entrance) {
- /* We saw a pooled carried above, use it as entrance into the pool
+ /*
+ * We saw a pooled carried above, use it as entrance into the pool
*/
cpdp = cpool_entrance;
}
else {
- /* No pooled carried seen above. Start search at cpool sentinel,
+ /*
+ * No pooled carried seen above. Start search at cpool sentinel,
* but begin by passing one element before trying to fetch.
* This in order to avoid contention with threads inserting elements.
*/
@@ -3354,8 +3338,8 @@ cpool_fetch(Allctr_t *allctr, UWord size)
goto check_dc_list;
}
- has_passed_sentinel = 0;
- while (1) {
+ loop_state = IGNORANT;
+ do {
erts_aint_t exp;
cpdp = cpool_aint2cpd(cpool_read(&cpdp->prev));
if (cpdp == cpool_entrance) {
@@ -3364,38 +3348,52 @@ cpool_fetch(Allctr_t *allctr, UWord size)
if (cpdp == sentinel)
break;
}
- i = 0; /* Last one to inspect */
+ loop_state = THE_LAST_ONE;
}
else if (cpdp == sentinel) {
- if (has_passed_sentinel) {
+ if (loop_state == HAS_SEEN_SENTINEL) {
/* We been here before. cpool_entrance must have been removed */
+ INC_CC(allctr->cpool.stat.entrance_removed);
break;
}
cpdp = cpool_aint2cpd(cpool_read(&cpdp->prev));
if (cpdp == sentinel)
break;
- has_passed_sentinel = 1;
+ loop_state = HAS_SEEN_SENTINEL;
}
- crr = (Carrier_t *)(((char *)cpdp) - offsetof(Carrier_t, cpool));
+ crr = ErtsContainerStruct(cpdp, Carrier_t, cpool);
exp = erts_atomic_read_rb(&crr->allctr);
- if (((exp & (ERTS_CRR_ALCTR_FLG_MASK)) == ERTS_CRR_ALCTR_FLG_IN_POOL)
- && (erts_atomic_read_nob(&cpdp->max_size) >= size)) {
+
+ if (erts_atomic_read_nob(&cpdp->max_size) < size) {
+ INC_CC(allctr->cpool.stat.skip_size);
+ }
+ else if ((exp & (ERTS_CRR_ALCTR_FLG_IN_POOL | ERTS_CRR_ALCTR_FLG_BUSY))
+ == ERTS_CRR_ALCTR_FLG_IN_POOL) {
erts_aint_t act;
- /* Try to fetch it... */
- act = erts_atomic_cmpxchg_mb(&crr->allctr,
- (erts_aint_t) allctr,
- exp);
+ erts_aint_t want = (((erts_aint_t) allctr)
+ | (exp & ERTS_CRR_ALCTR_FLG_HOMECOMING));
+ /* Try to fetch it... */
+ act = erts_atomic_cmpxchg_mb(&crr->allctr, want, exp);
if (act == exp) {
cpool_delete(allctr, ((Allctr_t *) (act & ~ERTS_CRR_ALCTR_FLG_MASK)), crr);
if (crr->cpool.orig_allctr == allctr) {
unlink_abandoned_carrier(crr);
- }
+ crr->cpool.state = ERTS_MBC_IS_HOME;
+ }
return crr;
}
}
- if (--i <= 0)
+
+ if (exp & ERTS_CRR_ALCTR_FLG_BUSY)
+ INC_CC(allctr->cpool.stat.skip_busy);
+ else
+ INC_CC(allctr->cpool.stat.skip_race);
+
+ if (--i <= 0) {
+ INC_CC(allctr->cpool.stat.fail_shared);
return NULL;
- }
+ }
+ }while (loop_state != THE_LAST_ONE);
check_dc_list:
/* Last; check our own pending dealloc carrier list... */
@@ -3404,23 +3402,23 @@ check_dc_list:
if (erts_atomic_read_nob(&crr->cpool.max_size) >= size) {
Block_t* blk;
unlink_carrier(&allctr->cpool.dc_list, crr);
-#ifdef ERTS_ALC_CPOOL_DEBUG
- ERTS_ALC_CPOOL_ASSERT(erts_atomic_xchg_nob(&crr->allctr,
- ((erts_aint_t) allctr))
- == (((erts_aint_t) allctr) & ~ERTS_CRR_ALCTR_FLG_MASK));
-#else
- erts_atomic_set_nob(&crr->allctr, ((erts_aint_t) allctr));
-#endif
+ ERTS_ALC_CPOOL_ASSERT(erts_atomic_read_nob(&crr->allctr)
+ == ((erts_aint_t) allctr));
blk = MBC_TO_FIRST_BLK(allctr, crr);
ASSERT(FBLK_TO_MBC(blk) == crr);
allctr->link_free_block(allctr, blk);
return crr;
}
crr = crr->prev;
- if (--i <= 0)
+ if (--i <= 0) {
+ INC_CC(allctr->cpool.stat.fail_pend_dealloc);
return NULL;
+ }
}
+ if (i != ERTS_ALC_CPOOL_MAX_FETCH_INSPECT)
+ INC_CC(allctr->cpool.stat.fail);
+
return NULL;
}
@@ -3475,9 +3473,6 @@ static void
schedule_dealloc_carrier(Allctr_t *allctr, Carrier_t *crr)
{
Allctr_t *orig_allctr;
- Block_t *blk;
- int check_pending_dealloc;
- erts_aint_t max_size;
ASSERT(IS_MB_CARRIER(crr));
@@ -3488,9 +3483,17 @@ schedule_dealloc_carrier(Allctr_t *allctr, Carrier_t *crr)
orig_allctr = crr->cpool.orig_allctr;
- if (allctr != orig_allctr) {
- int cinit = orig_allctr->dd.ix - allctr->dd.ix;
-
+ if (allctr == orig_allctr) {
+ if (!(erts_atomic_read_nob(&crr->allctr) & ERTS_CRR_ALCTR_FLG_HOMECOMING)) {
+ dealloc_my_carrier(allctr, crr);
+ }
+ /*else
+ * Carrier was abandoned earlier by other thread and
+ * is still waiting for us in dd-queue.
+ * handle_delayed_dealloc() will handle it when crr is dequeued.
+ */
+ }
+ else {
/*
* We send the carrier to its origin for deallocation.
* This in order:
@@ -3499,29 +3502,39 @@ schedule_dealloc_carrier(Allctr_t *allctr, Carrier_t *crr)
* - to ensure that we always only reuse empty carriers
* originating from our own thread specific mseg_alloc
* instance which is beneficial on NUMA systems.
- *
- * The receiver will recognize that this is a carrier to
- * deallocate (and not a block which is the common case)
- * since the block is an mbc block that is free and last
- * in the carrier.
*/
- blk = MBC_TO_FIRST_BLK(allctr, crr);
- ERTS_ALC_CPOOL_ASSERT(IS_FREE_LAST_MBC_BLK(blk));
-
- ERTS_ALC_CPOOL_ASSERT(IS_MBC_FIRST_ABLK(allctr, blk));
- ERTS_ALC_CPOOL_ASSERT(crr == FBLK_TO_MBC(blk));
- ERTS_ALC_CPOOL_ASSERT(crr == FIRST_BLK_TO_MBC(allctr, blk));
- ERTS_ALC_CPOOL_ASSERT(((erts_aint_t) allctr)
- == (erts_atomic_read_nob(&crr->allctr)
- & ~ERTS_CRR_ALCTR_FLG_MASK));
+ erts_aint_t iallctr;
+#ifdef ERTS_ALC_CPOOL_DEBUG
+ Block_t* first_blk = MBC_TO_FIRST_BLK(allctr, crr);
+ ERTS_ALC_CPOOL_ASSERT(IS_FREE_LAST_MBC_BLK(first_blk));
+
+ ERTS_ALC_CPOOL_ASSERT(IS_MBC_FIRST_ABLK(allctr, first_blk));
+ ERTS_ALC_CPOOL_ASSERT(crr == FBLK_TO_MBC(first_blk));
+ ERTS_ALC_CPOOL_ASSERT(crr == FIRST_BLK_TO_MBC(allctr, first_blk));
+ ERTS_ALC_CPOOL_ASSERT((erts_atomic_read_nob(&crr->allctr)
+ & ~ERTS_CRR_ALCTR_FLG_HOMECOMING)
+ == (erts_aint_t) allctr);
+#endif
- if (ddq_enqueue(&orig_allctr->dd.q, BLK2UMEM(blk), cinit))
- erts_alloc_notify_delayed_dealloc(orig_allctr->ix);
- return;
+ iallctr = (erts_aint_t)orig_allctr | ERTS_CRR_ALCTR_FLG_HOMECOMING;
+ if (!(erts_atomic_xchg_nob(&crr->allctr, iallctr)
+ & ERTS_CRR_ALCTR_FLG_HOMECOMING)) {
+ enqueue_homecoming(allctr, crr);
+ }
}
+}
+
+static void dealloc_my_carrier(Allctr_t *allctr, Carrier_t *crr)
+{
+ Block_t *blk;
+ int check_pending_dealloc;
+ erts_aint_t max_size;
- if (is_abandoned(crr))
- unlink_abandoned_carrier(crr);
+ ERTS_ALC_CPOOL_ASSERT(allctr == crr->cpool.orig_allctr);
+ if (is_abandoned(crr)) {
+ unlink_abandoned_carrier(crr);
+ crr->cpool.state = ERTS_MBC_IS_HOME;
+ }
if (crr->cpool.thr_prgr == ERTS_THR_PRGR_INVALID
|| erts_thr_progress_has_reached(crr->cpool.thr_prgr)) {
@@ -3553,6 +3566,7 @@ schedule_dealloc_carrier(Allctr_t *allctr, Carrier_t *crr)
static ERTS_INLINE void
cpool_init_carrier_data(Allctr_t *allctr, Carrier_t *crr)
{
+ crr->cpool.homecoming_dd.blk.bhdr = HOMECOMING_MBC_BLK_HDR;
erts_atomic_init_nob(&crr->cpool.next, ERTS_AINT_NULL);
erts_atomic_init_nob(&crr->cpool.prev, ERTS_AINT_NULL);
crr->cpool.orig_allctr = allctr;
@@ -3571,8 +3585,7 @@ cpool_init_carrier_data(Allctr_t *allctr, Carrier_t *crr)
limit = (csz/100)*allctr->cpool.util_limit;
crr->cpool.abandon_limit = limit;
}
- crr->cpool.abandoned.next = NULL;
- crr->cpool.abandoned.prev = NULL;
+ crr->cpool.state = ERTS_MBC_IS_HOME;
}
static void
@@ -3598,23 +3611,62 @@ set_new_allctr_abandon_limit(Allctr_t *allctr)
static void
abandon_carrier(Allctr_t *allctr, Carrier_t *crr)
{
- erts_aint_t max_size;
+ erts_aint_t iallctr;
- STAT_MBC_CPOOL_INSERT(allctr, crr);
+ STAT_MBC_ABANDON(allctr, crr);
unlink_carrier(&allctr->mbc_list, crr);
- if (crr->cpool.orig_allctr == allctr) {
- link_abandoned_carrier(&allctr->cpool.pooled_list, crr);
+ allctr->remove_mbc(allctr, crr);
+ set_new_allctr_abandon_limit(allctr);
+
+ cpool_insert(allctr, crr);
+
+
+ iallctr = erts_atomic_read_nob(&crr->allctr);
+ if (allctr == crr->cpool.orig_allctr) {
+ /* preserve HOMECOMING flag */
+ ASSERT((iallctr & ~ERTS_CRR_ALCTR_FLG_HOMECOMING) == (erts_aint_t)allctr);
+ erts_atomic_set_wb(&crr->allctr, iallctr | ERTS_CRR_ALCTR_FLG_IN_POOL);
+ poolify_my_carrier(allctr, crr);
}
+ else {
+ ASSERT((iallctr & ~ERTS_CRR_ALCTR_FLG_HOMECOMING) == (erts_aint_t)allctr);
+ iallctr = ((erts_aint_t)crr->cpool.orig_allctr |
+ ERTS_CRR_ALCTR_FLG_HOMECOMING |
+ ERTS_CRR_ALCTR_FLG_IN_POOL);
+ if (!(erts_atomic_xchg_wb(&crr->allctr, iallctr)
+ & ERTS_CRR_ALCTR_FLG_HOMECOMING)) {
+
+ enqueue_homecoming(allctr, crr);
+ }
+ }
+}
- allctr->remove_mbc(allctr, crr);
+static void
+enqueue_homecoming(Allctr_t* allctr, Carrier_t* crr)
+{
+ Allctr_t* orig_allctr = crr->cpool.orig_allctr;
+ const int cinit = orig_allctr->dd.ix - allctr->dd.ix;
+ Block_t* dd_blk = &crr->cpool.homecoming_dd.blk;
- max_size = (erts_aint_t) allctr->largest_fblk_in_mbc(allctr, crr);
- erts_atomic_set_nob(&crr->cpool.max_size, max_size);
+ /*
+ * The receiver will recognize this as a carrier
+ * (and not a block which is the common case)
+ * since the block header is HOMECOMING_MBC_BLK_HDR.
+ */
+ ASSERT(dd_blk->bhdr == HOMECOMING_MBC_BLK_HDR);
+ if (ddq_enqueue(&orig_allctr->dd.q, BLK2UMEM(dd_blk), cinit))
+ erts_alloc_notify_delayed_dealloc(orig_allctr->ix);
+}
- cpool_insert(allctr, crr);
+static void
+poolify_my_carrier(Allctr_t *allctr, Carrier_t *crr)
+{
+ ERTS_ALC_CPOOL_ASSERT(allctr == crr->cpool.orig_allctr);
- set_new_allctr_abandon_limit(allctr);
+ crr->cpool.pooled.hdr.bhdr = erts_atomic_read_nob(&crr->cpool.max_size);
+ aoff_add_pooled_mbc(allctr, crr);
+ crr->cpool.state = ERTS_MBC_WAS_POOLED;
}
static void
@@ -3771,6 +3823,7 @@ create_carrier(Allctr_t *allctr, Uint umem_sz, UWord flags)
crr = cpool_fetch(allctr, blk_sz);
if (crr) {
STAT_MBC_CPOOL_FETCH(allctr, crr);
+ INC_CC(allctr->cpool.stat.fetch);
link_carrier(&allctr->mbc_list, crr);
(*allctr->add_mbc)(allctr, crr);
blk = (*allctr->get_free_block)(allctr, blk_sz, NULL, 0);
@@ -4128,13 +4181,18 @@ destroy_carrier(Allctr_t *allctr, Block_t *blk, Carrier_t **busy_pcrr_pp)
#endif
if (busy_pcrr_pp && *busy_pcrr_pp) {
+ erts_aint_t iallctr = erts_atomic_read_nob(&crr->allctr);
ERTS_ALC_CPOOL_ASSERT(*busy_pcrr_pp == crr);
- *busy_pcrr_pp = NULL;
- ERTS_ALC_CPOOL_ASSERT(erts_atomic_read_nob(&crr->allctr)
- == (((erts_aint_t) allctr)
- | ERTS_CRR_ALCTR_FLG_IN_POOL
- | ERTS_CRR_ALCTR_FLG_BUSY));
- erts_atomic_set_nob(&crr->allctr, ((erts_aint_t) allctr));
+ ERTS_ALC_CPOOL_ASSERT((iallctr & ~ERTS_CRR_ALCTR_FLG_HOMECOMING)
+ == (((erts_aint_t) allctr)
+ | ERTS_CRR_ALCTR_FLG_IN_POOL
+ | ERTS_CRR_ALCTR_FLG_BUSY));
+ ERTS_ALC_CPOOL_ASSERT(allctr == crr->cpool.orig_allctr);
+
+ *busy_pcrr_pp = NULL;
+ erts_atomic_set_nob(&crr->allctr,
+ (iallctr & ~(ERTS_CRR_ALCTR_FLG_IN_POOL |
+ ERTS_CRR_ALCTR_FLG_BUSY)));
cpool_delete(allctr, allctr, crr);
}
else
@@ -4184,7 +4242,6 @@ static struct {
Eterm e;
Eterm t;
Eterm ramv;
- Eterm sbct;
#if HAVE_ERTS_MSEG
Eterm asbcst;
Eterm rsbcst;
@@ -4201,6 +4258,8 @@ static struct {
Eterm smbcs;
Eterm mbcgs;
Eterm acul;
+ Eterm acnl;
+ Eterm acfml;
#if HAVE_ERTS_MSEG
Eterm mmc;
@@ -4212,6 +4271,17 @@ static struct {
Eterm mbcs;
Eterm mbcs_pool;
+ Eterm fetch;
+ Eterm fail_pooled;
+ Eterm fail_shared;
+ Eterm fail_pend_dealloc;
+ Eterm fail;
+ Eterm skip_size;
+ Eterm skip_busy;
+ Eterm skip_not_pooled;
+ Eterm skip_homecoming;
+ Eterm skip_race;
+ Eterm entrance_removed;
Eterm sbcs;
Eterm sys_alloc_carriers_size;
@@ -4245,7 +4315,7 @@ static Eterm fix_type_atoms[ERTS_ALC_NO_FIXED_SIZES];
static ERTS_INLINE void atom_init(Eterm *atom, char *name)
{
- *atom = am_atom_put(name, strlen(name));
+ *atom = am_atom_put(name, sys_strlen(name));
}
#define AM_INIT(AM) atom_init(&am.AM, #AM)
@@ -4272,7 +4342,6 @@ init_atoms(Allctr_t *allctr)
AM_INIT(e);
AM_INIT(t);
AM_INIT(ramv);
- AM_INIT(sbct);
#if HAVE_ERTS_MSEG
AM_INIT(asbcst);
AM_INIT(rsbcst);
@@ -4289,6 +4358,8 @@ init_atoms(Allctr_t *allctr)
AM_INIT(smbcs);
AM_INIT(mbcgs);
AM_INIT(acul);
+ AM_INIT(acnl);
+ AM_INIT(acfml);
#if HAVE_ERTS_MSEG
AM_INIT(mmc);
@@ -4300,6 +4371,17 @@ init_atoms(Allctr_t *allctr)
AM_INIT(mbcs);
AM_INIT(mbcs_pool);
+ AM_INIT(fetch);
+ AM_INIT(fail_pooled);
+ AM_INIT(fail_shared);
+ AM_INIT(fail_pend_dealloc);
+ AM_INIT(fail);
+ AM_INIT(skip_size);
+ AM_INIT(skip_busy);
+ AM_INIT(skip_not_pooled);
+ AM_INIT(skip_homecoming);
+ AM_INIT(skip_race);
+ AM_INIT(entrance_removed);
AM_INIT(sbcs);
AM_INIT(sys_alloc_carriers_size);
@@ -4334,7 +4416,7 @@ init_atoms(Allctr_t *allctr)
for (ix = 0; ix < ERTS_ALC_NO_FIXED_SIZES; ix++) {
ErtsAlcType_t n = ERTS_ALC_N_MIN_A_FIXED_SIZE + ix;
char *name = (char *) ERTS_ALC_N2TD(n);
- size_t len = strlen(name);
+ size_t len = sys_strlen(name);
fix_type_atoms[ix] = am_atom_put(name, len);
}
}
@@ -4583,9 +4665,56 @@ info_cpool(Allctr_t *allctr,
if (hpp || szp) {
res = NIL;
+
+ if (!sz_only) {
+ add_3tup(hpp, szp, &res, am.fail_pooled,
+ bld_unstable_uint(hpp, szp, ERTS_ALC_CC_GIGA_VAL(allctr->cpool.stat.fail_pooled)),
+ bld_unstable_uint(hpp, szp, ERTS_ALC_CC_VAL(allctr->cpool.stat.fail_pooled)));
+
+ add_3tup(hpp, szp, &res, am.fail_shared,
+ bld_unstable_uint(hpp, szp, ERTS_ALC_CC_GIGA_VAL(allctr->cpool.stat.fail_shared)),
+ bld_unstable_uint(hpp, szp, ERTS_ALC_CC_VAL(allctr->cpool.stat.fail_shared)));
+
+ add_3tup(hpp, szp, &res, am.fail_pend_dealloc,
+ bld_unstable_uint(hpp, szp, ERTS_ALC_CC_GIGA_VAL(allctr->cpool.stat.fail_pend_dealloc)),
+ bld_unstable_uint(hpp, szp, ERTS_ALC_CC_VAL(allctr->cpool.stat.fail_pend_dealloc)));
+
+ add_3tup(hpp, szp, &res, am.fail,
+ bld_unstable_uint(hpp, szp, ERTS_ALC_CC_GIGA_VAL(allctr->cpool.stat.fail)),
+ bld_unstable_uint(hpp, szp, ERTS_ALC_CC_VAL(allctr->cpool.stat.fail)));
+
+ add_3tup(hpp, szp, &res, am.fetch,
+ bld_unstable_uint(hpp, szp, ERTS_ALC_CC_GIGA_VAL(allctr->cpool.stat.fetch)),
+ bld_unstable_uint(hpp, szp, ERTS_ALC_CC_VAL(allctr->cpool.stat.fetch)));
+
+ add_3tup(hpp, szp, &res, am.skip_size,
+ bld_unstable_uint(hpp, szp, ERTS_ALC_CC_GIGA_VAL(allctr->cpool.stat.skip_size)),
+ bld_unstable_uint(hpp, szp, ERTS_ALC_CC_VAL(allctr->cpool.stat.skip_size)));
+
+ add_3tup(hpp, szp, &res, am.skip_busy,
+ bld_unstable_uint(hpp, szp, ERTS_ALC_CC_GIGA_VAL(allctr->cpool.stat.skip_busy)),
+ bld_unstable_uint(hpp, szp, ERTS_ALC_CC_VAL(allctr->cpool.stat.skip_busy)));
+
+ add_3tup(hpp, szp, &res, am.skip_not_pooled,
+ bld_unstable_uint(hpp, szp, ERTS_ALC_CC_GIGA_VAL(allctr->cpool.stat.skip_not_pooled)),
+ bld_unstable_uint(hpp, szp, ERTS_ALC_CC_VAL(allctr->cpool.stat.skip_not_pooled)));
+
+ add_3tup(hpp, szp, &res, am.skip_homecoming,
+ bld_unstable_uint(hpp, szp, ERTS_ALC_CC_GIGA_VAL(allctr->cpool.stat.skip_homecoming)),
+ bld_unstable_uint(hpp, szp, ERTS_ALC_CC_VAL(allctr->cpool.stat.skip_homecoming)));
+
+ add_3tup(hpp, szp, &res, am.skip_race,
+ bld_unstable_uint(hpp, szp, ERTS_ALC_CC_GIGA_VAL(allctr->cpool.stat.skip_race)),
+ bld_unstable_uint(hpp, szp, ERTS_ALC_CC_VAL(allctr->cpool.stat.skip_race)));
+
+ add_3tup(hpp, szp, &res, am.entrance_removed,
+ bld_unstable_uint(hpp, szp, ERTS_ALC_CC_GIGA_VAL(allctr->cpool.stat.entrance_removed)),
+ bld_unstable_uint(hpp, szp, ERTS_ALC_CC_VAL(allctr->cpool.stat.entrance_removed)));
+
add_2tup(hpp, szp, &res,
am.carriers_size,
bld_unstable_uint(hpp, szp, csz));
+ }
if (!sz_only)
add_2tup(hpp, szp, &res,
am.carriers,
@@ -4725,20 +4854,20 @@ make_name_atoms(Allctr_t *allctr)
char realloc[] = "realloc";
char free[] = "free";
char buf[MAX_ATOM_CHARACTERS];
- size_t prefix_len = strlen(allctr->name_prefix);
+ size_t prefix_len = sys_strlen(allctr->name_prefix);
if (prefix_len > MAX_ATOM_CHARACTERS + sizeof(realloc) - 1)
erts_exit(ERTS_ERROR_EXIT,"Too long allocator name: %salloc\n",allctr->name_prefix);
- memcpy((void *) buf, (void *) allctr->name_prefix, prefix_len);
+ sys_memcpy((void *) buf, (void *) allctr->name_prefix, prefix_len);
- memcpy((void *) &buf[prefix_len], (void *) alloc, sizeof(alloc) - 1);
+ sys_memcpy((void *) &buf[prefix_len], (void *) alloc, sizeof(alloc) - 1);
allctr->name.alloc = am_atom_put(buf, prefix_len + sizeof(alloc) - 1);
- memcpy((void *) &buf[prefix_len], (void *) realloc, sizeof(realloc) - 1);
+ sys_memcpy((void *) &buf[prefix_len], (void *) realloc, sizeof(realloc) - 1);
allctr->name.realloc = am_atom_put(buf, prefix_len + sizeof(realloc) - 1);
- memcpy((void *) &buf[prefix_len], (void *) free, sizeof(free) - 1);
+ sys_memcpy((void *) &buf[prefix_len], (void *) free, sizeof(free) - 1);
allctr->name.free = am_atom_put(buf, prefix_len + sizeof(free) - 1);
}
@@ -4844,7 +4973,7 @@ info_options(Allctr_t *allctr,
Uint *szp)
{
Eterm res = THE_NON_VALUE;
- int acul;
+ UWord acul, acnl, acfml;
if (!allctr) {
if (print_to_p)
@@ -4857,6 +4986,8 @@ info_options(Allctr_t *allctr,
}
acul = allctr->cpool.util_limit;
+ acnl = allctr->cpool.in_pool_limit;
+ acfml = allctr->cpool.fblk_min_limit;
if (print_to_p) {
char topt[21]; /* Enough for any 64-bit integer */
@@ -4884,7 +5015,7 @@ info_options(Allctr_t *allctr,
"option lmbcs: %beu\n"
"option smbcs: %beu\n"
"option mbcgs: %beu\n"
- "option acul: %d\n",
+ "option acul: %bpu\n",
topt,
allctr->ramv ? "true" : "false",
allctr->sbc_threshold,
@@ -4909,9 +5040,15 @@ info_options(Allctr_t *allctr,
hpp, szp);
if (hpp || szp) {
+ add_2tup(hpp, szp, &res,
+ am.acfml,
+ bld_uint(hpp, szp, acfml));
+ add_2tup(hpp, szp, &res,
+ am.acnl,
+ bld_uint(hpp, szp, acnl));
add_2tup(hpp, szp, &res,
am.acul,
- bld_uint(hpp, szp, (UWord) acul));
+ bld_uint(hpp, szp, acul));
add_2tup(hpp, szp, &res,
am.mbcgs,
bld_uint(hpp, szp, allctr->mbc_growth_stages));
@@ -4947,7 +5084,7 @@ info_options(Allctr_t *allctr,
bld_uint(hpp, szp, allctr->mseg_opt.abs_shrink_th));
#endif
add_2tup(hpp, szp, &res,
- am.sbct,
+ am_sbct,
bld_uint(hpp, szp, allctr->sbc_threshold));
add_2tup(hpp, szp, &res, am.ramv, allctr->ramv ? am_true : am_false);
add_2tup(hpp, szp, &res, am.t, (allctr->t ? am_true : am_false));
@@ -5481,12 +5618,13 @@ erts_alcu_free_thr_pref(ErtsAlcType_t type, void *extra, void *p)
pref_allctr = get_pref_allctr(extra);
used_allctr = get_used_allctr(pref_allctr, ERTS_ALC_TS_PREF_LOCK_IF_USED,
p, NULL, &busy_pcrr_p);
- if (pref_allctr != used_allctr)
+ if (pref_allctr != used_allctr) {
enqueue_dealloc_other_instance(type,
- used_allctr,
- p,
- (used_allctr->dd.ix
- - pref_allctr->dd.ix));
+ used_allctr,
+ p,
+ (used_allctr->dd.ix
+ - pref_allctr->dd.ix));
+ }
else {
ERTS_ALCU_DBG_CHK_THR_ACCESS(used_allctr);
do_erts_alcu_free(type, used_allctr, p, &busy_pcrr_p);
@@ -5854,6 +5992,37 @@ erts_alcu_realloc_mv_thr_pref(ErtsAlcType_t type, void *extra,
+static Uint adjust_sbct(Allctr_t* allctr, Uint sbct)
+{
+#ifndef ARCH_64
+ if (sbct > 0) {
+ Uint max_mbc_block_sz = UNIT_CEILING(sbct - 1 + ABLK_HDR_SZ);
+ if (max_mbc_block_sz + UNIT_FLOOR(allctr->min_block_size - 1) > MBC_ABLK_SZ_MASK
+ || max_mbc_block_sz < sbct) { /* wrap around */
+ /*
+ * By limiting sbc_threshold to (hard limit - min_block_size)
+ * we avoid having to split off free "residue blocks"
+ * smaller than min_block_size.
+ */
+ max_mbc_block_sz = MBC_ABLK_SZ_MASK - UNIT_FLOOR(allctr->min_block_size - 1);
+ sbct = max_mbc_block_sz - ABLK_HDR_SZ + 1;
+ }
+ }
+#endif
+ return sbct;
+}
+
+int erts_alcu_try_set_dyn_param(Allctr_t* allctr, Eterm param, Uint value)
+{
+ const Uint MIN_DYN_SBCT = 4000; /* a lame catastrophe prevention */
+
+ if (param == am_sbct && value >= MIN_DYN_SBCT) {
+ allctr->sbc_threshold = adjust_sbct(allctr, value);
+ return 1;
+ }
+ return 0;
+}
+
/* ------------------------------------------------------------------------- */
int
@@ -5941,10 +6110,7 @@ erts_alcu_start(Allctr_t *allctr, AllctrInit_t *init)
allctr->min_block_size = sz;
}
- allctr->cpool.pooled_list.next = &allctr->cpool.pooled_list;
- allctr->cpool.pooled_list.prev = &allctr->cpool.pooled_list;
- allctr->cpool.traitor_list.next = &allctr->cpool.traitor_list;
- allctr->cpool.traitor_list.prev = &allctr->cpool.traitor_list;
+ allctr->cpool.pooled_tree = NULL;
allctr->cpool.dc_list.first = NULL;
allctr->cpool.dc_list.last = NULL;
allctr->cpool.abandon_limit = 0;
@@ -5954,24 +6120,18 @@ erts_alcu_start(Allctr_t *allctr, AllctrInit_t *init)
erts_atomic_init_nob(&allctr->cpool.stat.carriers_size, 0);
erts_atomic_init_nob(&allctr->cpool.stat.no_carriers, 0);
allctr->cpool.check_limit_count = ERTS_ALC_CPOOL_CHECK_LIMIT_COUNT;
- allctr->cpool.util_limit = init->ts ? 0 : init->acul;
-
- allctr->sbc_threshold = init->sbct;
-#ifndef ARCH_64
- if (allctr->sbc_threshold > 0) {
- Uint max_mbc_block_sz = UNIT_CEILING(allctr->sbc_threshold - 1 + ABLK_HDR_SZ);
- if (max_mbc_block_sz + UNIT_FLOOR(allctr->min_block_size - 1) > MBC_ABLK_SZ_MASK
- || max_mbc_block_sz < allctr->sbc_threshold) { /* wrap around */
- /*
- * By limiting sbc_threshold to (hard limit - min_block_size)
- * we avoid having to split off free "residue blocks"
- * smaller than min_block_size.
- */
- max_mbc_block_sz = MBC_ABLK_SZ_MASK - UNIT_FLOOR(allctr->min_block_size - 1);
- allctr->sbc_threshold = max_mbc_block_sz - ABLK_HDR_SZ + 1;
- }
+ if (!init->ts && init->acul && init->acnl) {
+ allctr->cpool.util_limit = init->acul;
+ allctr->cpool.in_pool_limit = init->acnl;
+ allctr->cpool.fblk_min_limit = init->acfml;
}
-#endif
+ else {
+ allctr->cpool.util_limit = 0;
+ allctr->cpool.in_pool_limit = 0;
+ allctr->cpool.fblk_min_limit = 0;
+ }
+
+ allctr->sbc_threshold = adjust_sbct(allctr, init->sbct);
#if HAVE_ERTS_MSEG
if (allctr->mseg_opt.abs_shrink_th > ~((UWord) 0) / 100)
@@ -6022,6 +6182,9 @@ erts_alcu_start(Allctr_t *allctr, AllctrInit_t *init)
allctr->sys_realloc = &erts_alcu_sys_realloc;
allctr->sys_dealloc = &erts_alcu_sys_dealloc;
}
+
+ allctr->try_set_dyn_param = &erts_alcu_try_set_dyn_param;
+
#if HAVE_ERTS_MSEG
if (init->mseg_alloc) {
ASSERT(init->mseg_realloc && init->mseg_dealloc);
@@ -6036,6 +6199,7 @@ erts_alcu_start(Allctr_t *allctr, AllctrInit_t *init)
allctr->mseg_realloc = &erts_alcu_mseg_realloc;
allctr->mseg_dealloc = &erts_alcu_mseg_dealloc;
}
+
/* If a custom carrier alloc function is specified, make sure it's used */
if (init->mseg_alloc && !init->sys_alloc) {
allctr->crr_set_flgs = CFLG_FORCE_MSEG;
diff --git a/erts/emulator/beam/erl_alloc_util.h b/erts/emulator/beam/erl_alloc_util.h
index faeb5ef368..05c8a0db3b 100644
--- a/erts/emulator/beam/erl_alloc_util.h
+++ b/erts/emulator/beam/erl_alloc_util.h
@@ -61,7 +61,9 @@ typedef struct {
UWord lmbcs;
UWord smbcs;
UWord mbcgs;
- int acul;
+ UWord acul;
+ UWord acnl;
+ UWord acfml;
void *fix;
size_t *fix_type_size;
@@ -116,6 +118,8 @@ typedef struct {
1024*1024, /* (bytes) smbcs: smallest mbc size */\
10, /* (amount) mbcgs: mbc growth stages */\
0, /* (%) acul: abandon carrier utilization limit */\
+ 1000, /* (amount) acnl: abandoned carriers number limit */\
+ 0, /* (bytes) acfml: abandoned carrier fblk min limit */\
/* --- Data not options -------------------------------------------- */\
NULL, /* (ptr) fix */\
NULL /* (ptr) fix_type_size */\
@@ -149,6 +153,8 @@ typedef struct {
128*1024, /* (bytes) smbcs: smallest mbc size */\
10, /* (amount) mbcgs: mbc growth stages */\
0, /* (%) acul: abandon carrier utilization limit */\
+ 1000, /* (amount) acnl: abandoned carriers number limit */\
+ 0, /* (bytes) acfml: abandoned carrier fblk min limit */\
/* --- Data not options -------------------------------------------- */\
NULL, /* (ptr) fix */\
NULL /* (ptr) fix_type_size */\
@@ -199,7 +205,7 @@ void* erts_alcu_mmapper_mseg_realloc(Allctr_t*, void *seg, Uint old_size, Uint *
void erts_alcu_mmapper_mseg_dealloc(Allctr_t*, void *seg, Uint size, Uint flags);
# endif
-# if defined(ERTS_ALC_A_EXEC) && !defined(ERTS_HAVE_EXEC_MMAPPER)
+# if defined(ERTS_ALC_A_EXEC)
void* erts_alcu_exec_mseg_alloc(Allctr_t*, Uint *size_p, Uint flags);
void* erts_alcu_exec_mseg_realloc(Allctr_t*, void *seg, Uint old_size, Uint *new_size_p);
void erts_alcu_exec_mseg_dealloc(Allctr_t*, void *seg, Uint size, Uint flags);
@@ -216,6 +222,8 @@ void erts_alcu_literal_32_sys_dealloc(Allctr_t*, void *ptr, Uint size, int supe
void erts_lcnt_update_allocator_locks(int enable);
#endif
+int erts_alcu_try_set_dyn_param(Allctr_t*, Eterm param, Uint value);
+
#endif /* !ERL_ALLOC_UTIL__ */
#if defined(GET_ERL_ALLOC_UTIL_IMPL) && !defined(ERL_ALLOC_UTIL_IMPL__)
@@ -296,41 +304,7 @@ void erts_lcnt_update_allocator_locks(int enable);
typedef union {char c[ERTS_ALLOC_ALIGN_BYTES]; long l; double d;} Unit_t;
-
-typedef struct ErtsDoubleLink_t_ {
- struct ErtsDoubleLink_t_ *next;
- struct ErtsDoubleLink_t_ *prev;
-}ErtsDoubleLink_t;
-
-typedef struct {
- erts_atomic_t next;
- erts_atomic_t prev;
- Allctr_t *orig_allctr; /* read-only while carrier is alive */
- ErtsThrPrgrVal thr_prgr;
- erts_atomic_t max_size;
- UWord abandon_limit;
- UWord blocks;
- UWord blocks_size;
- ErtsDoubleLink_t abandoned; /* node in pooled_list or traitor_list */
-} ErtsAlcCPoolData_t;
-
-
typedef struct Carrier_t_ Carrier_t;
-struct Carrier_t_ {
- UWord chdr;
- Carrier_t *next;
- Carrier_t *prev;
- erts_atomic_t allctr;
- ErtsAlcCPoolData_t cpool; /* Overwritten by block if sbc */
-};
-
-#define ERTS_ALC_CARRIER_TO_ALLCTR(C) \
- ((Allctr_t *) (erts_atomic_read_nob(&(C)->allctr) & ~FLG_MASK))
-
-typedef struct {
- Carrier_t *first;
- Carrier_t *last;
-} CarrierList_t;
typedef struct {
UWord bhdr;
@@ -344,6 +318,22 @@ typedef struct {
#endif
} Block_t;
+typedef union ErtsAllctrDDBlock_t_ ErtsAllctrDDBlock_t;
+
+union ErtsAllctrDDBlock_t_ {
+ erts_atomic_t atmc_next;
+ ErtsAllctrDDBlock_t *ptr_next;
+};
+
+typedef struct {
+ Block_t blk;
+#if !MBC_ABLK_OFFSET_BITS
+ ErtsAllctrDDBlock_t umem_;
+#endif
+} ErtsFakeDDBlock_t;
+
+
+
#define THIS_FREE_BLK_HDR_FLG (((UWord) 1) << 0)
#define PREV_FREE_BLK_HDR_FLG (((UWord) 1) << 1)
#define LAST_BLK_HDR_FLG (((UWord) 1) << 2)
@@ -352,14 +342,13 @@ typedef struct {
(THIS_FREE_BLK_HDR_FLG | PREV_FREE_BLK_HDR_FLG | LAST_BLK_HDR_FLG)
/*
- * FREE_LAST_MBC_BLK_HDR_FLGS is a special flag combo used for
- * distinguishing empty mbc's from allocated blocks in
- * handle_delayed_dealloc().
+ * HOMECOMING_MBC_BLK_HDR is a special block header combo used for
+ * distinguishing MBC's from allocated blocks in handle_delayed_dealloc().
*/
-#define FREE_LAST_MBC_BLK_HDR_FLGS (THIS_FREE_BLK_HDR_FLG | LAST_BLK_HDR_FLG)
+#define HOMECOMING_MBC_BLK_HDR (THIS_FREE_BLK_HDR_FLG | LAST_BLK_HDR_FLG)
#define IS_FREE_LAST_MBC_BLK(B) \
- (((B)->bhdr & FLG_MASK) == FREE_LAST_MBC_BLK_HDR_FLGS)
+ (((B)->bhdr & FLG_MASK) == (THIS_FREE_BLK_HDR_FLG | LAST_BLK_HDR_FLG))
#define IS_SBC_BLK(B) (((B)->bhdr & FLG_MASK) == SBC_BLK_HDR_FLG)
#define IS_MBC_BLK(B) (!IS_SBC_BLK((B)))
@@ -383,6 +372,57 @@ typedef struct {
typedef UWord FreeBlkFtr_t; /* Footer of a free block */
+/* This AOFF stuff really belong in erl_ao_firstfit_alloc.h */
+typedef struct AOFF_RBTree_t_ AOFF_RBTree_t;
+struct AOFF_RBTree_t_ {
+ Block_t hdr;
+ AOFF_RBTree_t *parent;
+ AOFF_RBTree_t *left;
+ AOFF_RBTree_t *right;
+ Uint32 flags;
+ Uint32 max_sz; /* of all blocks in this sub-tree */
+};
+
+void aoff_add_pooled_mbc(Allctr_t*, Carrier_t*);
+void aoff_remove_pooled_mbc(Allctr_t*, Carrier_t*);
+Carrier_t* aoff_lookup_pooled_mbc(Allctr_t*, Uint size);
+void erts_aoff_larger_max_size(AOFF_RBTree_t *node);
+
+typedef struct {
+ ErtsFakeDDBlock_t homecoming_dd;
+ erts_atomic_t next;
+ erts_atomic_t prev;
+ Allctr_t *orig_allctr; /* read-only while carrier is alive */
+ ErtsThrPrgrVal thr_prgr;
+ erts_atomic_t max_size;
+ UWord abandon_limit;
+ UWord blocks;
+ UWord blocks_size;
+ enum {
+ ERTS_MBC_IS_HOME,
+ ERTS_MBC_WAS_POOLED,
+ ERTS_MBC_WAS_TRAITOR
+ } state;
+ AOFF_RBTree_t pooled; /* node in pooled_tree */
+} ErtsAlcCPoolData_t;
+
+struct Carrier_t_ {
+ UWord chdr;
+ Carrier_t *next;
+ Carrier_t *prev;
+ erts_atomic_t allctr;
+ ErtsAlcCPoolData_t cpool; /* Overwritten by block if sbc */
+};
+
+#define ERTS_ALC_CARRIER_TO_ALLCTR(C) \
+ ((Allctr_t *) (erts_atomic_read_nob(&(C)->allctr) & ~FLG_MASK))
+
+typedef struct {
+ Carrier_t *first;
+ Carrier_t *last;
+} CarrierList_t;
+
+
typedef Uint64 CallCounter_t;
typedef struct {
@@ -419,13 +459,6 @@ typedef struct {
#endif
-typedef union ErtsAllctrDDBlock_t_ ErtsAllctrDDBlock_t;
-
-union ErtsAllctrDDBlock_t_ {
- erts_atomic_t atmc_next;
- ErtsAllctrDDBlock_t *ptr_next;
-};
-
typedef struct {
ErtsAllctrDDBlock_t marker;
erts_atomic_t last;
@@ -537,25 +570,37 @@ struct Allctr_t_ {
UWord crr_set_flgs;
UWord crr_clr_flgs;
- /* Carriers */
+ /* Carriers *employed* by this allocator */
CarrierList_t mbc_list;
CarrierList_t sbc_list;
struct {
- /* pooled_list, traitor list and dc_list contain only
- carriers _created_ by this allocator */
- ErtsDoubleLink_t pooled_list;
- ErtsDoubleLink_t traitor_list;
+ /* pooled_tree and dc_list contain only
+ carriers *created* by this allocator */
+ AOFF_RBTree_t* pooled_tree;
CarrierList_t dc_list;
UWord abandon_limit;
int disable_abandon;
int check_limit_count;
- int util_limit;
+ UWord util_limit; /* acul */
+ UWord in_pool_limit; /* acnl */
+ UWord fblk_min_limit; /* acmfl */
struct {
erts_atomic_t blocks_size;
erts_atomic_t no_blocks;
erts_atomic_t carriers_size;
erts_atomic_t no_carriers;
+ CallCounter_t fail_pooled;
+ CallCounter_t fail_shared;
+ CallCounter_t fail_pend_dealloc;
+ CallCounter_t fail;
+ CallCounter_t fetch;
+ CallCounter_t skip_size;
+ CallCounter_t skip_busy;
+ CallCounter_t skip_not_pooled;
+ CallCounter_t skip_homecoming;
+ CallCounter_t skip_race;
+ CallCounter_t entrance_removed;
} stat;
} cpool;
@@ -589,6 +634,8 @@ struct Allctr_t_ {
void* (*sys_realloc)(Allctr_t *allctr, void *ptr, Uint *size_p, Uint old_size, int superalign);
void (*sys_dealloc)(Allctr_t *allctr, void *ptr, Uint size, int superalign);
+ int (*try_set_dyn_param)(Allctr_t*, Eterm param, Uint value);
+
void (*init_atoms) (void);
#ifdef ERTS_ALLOC_UTIL_HARD_DEBUG
diff --git a/erts/emulator/beam/erl_ao_firstfit_alloc.c b/erts/emulator/beam/erl_ao_firstfit_alloc.c
index 05ba1f9891..0c5545401a 100644
--- a/erts/emulator/beam/erl_ao_firstfit_alloc.c
+++ b/erts/emulator/beam/erl_ao_firstfit_alloc.c
@@ -20,7 +20,7 @@
/*
- * Description: An "address order first fit" allocator
+ * Description: A family of "first fit" allocator strategies
* based on a Red-Black (binary search) Tree. The search,
* insert, and delete operations are all O(log n) operations
* on a Red-Black Tree.
@@ -40,6 +40,10 @@
* sorting order. Blocks within the same carrier are sorted
* wrt size instead of address. The 'max_sz' field is maintained
* in order to dismiss entire carriers with too small blocks.
+ * Age Order:
+ * Carriers are ordered by creation time instead of address.
+ * Oldest carrier with a large enough free block is chosen.
+ * No age order supported for blocks.
*
* Authors: Rickard Green/Sverker Eriksson
*/
@@ -53,10 +57,12 @@
#include "erl_ao_firstfit_alloc.h"
#ifdef DEBUG
+# define IS_DEBUG 1
#if 0
#define HARD_DEBUG
#endif
#else
+# define IS_DEBUG 0
#undef HARD_DEBUG
#endif
@@ -92,18 +98,6 @@
#define RBT_ASSERT(x)
#endif
-
-/* Types... */
-typedef struct AOFF_RBTree_t_ AOFF_RBTree_t;
-
-struct AOFF_RBTree_t_ {
- Block_t hdr;
- AOFF_RBTree_t *parent;
- AOFF_RBTree_t *left;
- AOFF_RBTree_t *right;
- Uint32 flags;
- Uint32 max_sz; /* of all blocks in this sub-tree */
-};
#define AOFF_BLK_SZ(B) MBC_FBLK_SZ(&(B)->hdr)
/* BF block nodes keeps list of all with equal size
@@ -121,6 +115,7 @@ typedef struct AOFF_Carrier_t_ AOFF_Carrier_t;
struct AOFF_Carrier_t_ {
Carrier_t crr;
AOFF_RBTree_t rbt_node; /* My node in the carrier tree */
+ Sint64 birth_time;
AOFF_RBTree_t* root; /* Root of my block tree */
};
#define RBT_NODE_TO_MBC(PTR) ErtsContainerStruct((PTR), AOFF_Carrier_t, rbt_node)
@@ -136,12 +131,12 @@ struct AOFF_Carrier_t_ {
*/
#ifdef HARD_DEBUG
-# define HARD_CHECK_IS_MEMBER(ROOT,NODE) rbt_assert_is_member(ROOT,NODE)
-# define HARD_CHECK_TREE(CRR,FLV,ROOT,SZ) check_tree(CRR, FLV, ROOT, SZ)
-static AOFF_RBTree_t * check_tree(Carrier_t* within_crr, enum AOFF_Flavor flavor, AOFF_RBTree_t* root, Uint);
+# define HARD_CHECK_IS_MEMBER(ROOT,NODE) ASSERT(rbt_is_member(ROOT,NODE))
+# define HARD_CHECK_TREE(CRR,ORDER,ROOT,SZ) check_tree(CRR, ORDER, ROOT, SZ)
+static AOFF_RBTree_t * check_tree(Carrier_t*, enum AOFFSortOrder, AOFF_RBTree_t*, Uint);
#else
# define HARD_CHECK_IS_MEMBER(ROOT,NODE)
-# define HARD_CHECK_TREE(CRR,FLV,ROOT,SZ)
+# define HARD_CHECK_TREE(CRR,ORDER,ROOT,SZ)
#endif
@@ -179,25 +174,63 @@ static ERTS_INLINE void lower_max_size(AOFF_RBTree_t *node,
else ASSERT(new_max == old_max);
}
-static ERTS_INLINE SWord cmp_blocks(enum AOFF_Flavor flavor,
+/*
+ * Set possibly new larger 'max_sz' of node and propagate change toward root
+ */
+void erts_aoff_larger_max_size(AOFF_RBTree_t *node)
+{
+ AOFF_RBTree_t* x = node;
+ const Uint new_sz = node->hdr.bhdr;
+
+ ASSERT(!x->left || x->left->max_sz <= x->max_sz);
+ ASSERT(!x->right || x->right->max_sz <= x->max_sz);
+
+ while (new_sz > x->max_sz) {
+ x->max_sz = new_sz;
+ x = x->parent;
+ if (!x)
+ break;
+ }
+}
+
+/* Compare nodes for both carrier and block trees */
+static ERTS_INLINE SWord cmp_blocks(enum AOFFSortOrder order,
AOFF_RBTree_t* lhs, AOFF_RBTree_t* rhs)
{
ASSERT(lhs != rhs);
- ASSERT(flavor == AOFF_AOFF || FBLK_TO_MBC(&lhs->hdr) == FBLK_TO_MBC(&rhs->hdr));
- if (flavor != AOFF_AOFF) {
- SWord diff = (SWord)AOFF_BLK_SZ(lhs) - (SWord)AOFF_BLK_SZ(rhs);
- if (diff || flavor == AOFF_BF) return diff;
+ if (order == FF_AGEFF) {
+ AOFF_Carrier_t* lc = RBT_NODE_TO_MBC(lhs);
+ AOFF_Carrier_t* rc = RBT_NODE_TO_MBC(rhs);
+ Sint64 diff = lc->birth_time - rc->birth_time;
+ #ifdef ARCH_64
+ if (diff)
+ return diff;
+ #else
+ if (diff < 0)
+ return -1;
+ else if (diff > 0)
+ return 1;
+ #endif
+ }
+ else {
+ ASSERT(order == FF_AOFF || FBLK_TO_MBC(&lhs->hdr) == FBLK_TO_MBC(&rhs->hdr));
+ if (order != FF_AOFF) {
+ SWord diff = (SWord)AOFF_BLK_SZ(lhs) - (SWord)AOFF_BLK_SZ(rhs);
+ if (diff || order == FF_BF) return diff;
+ }
}
return (char*)lhs - (char*)rhs;
}
-static ERTS_INLINE SWord cmp_cand_blk(enum AOFF_Flavor flavor,
+/* Compare candidate block. Only for block tree */
+static ERTS_INLINE SWord cmp_cand_blk(enum AOFFSortOrder order,
Block_t* cand_blk, AOFF_RBTree_t* rhs)
{
- if (flavor != AOFF_AOFF) {
+ ASSERT(order != FF_AGEFF);
+ if (order != FF_AOFF) {
if (BLK_TO_MBC(cand_blk) == FBLK_TO_MBC(&rhs->hdr)) {
SWord diff = (SWord)MBC_BLK_SZ(cand_blk) - (SWord)MBC_FBLK_SZ(&rhs->hdr);
- if (diff || flavor == AOFF_BF) return diff;
+ if (diff || order == FF_BF) return diff;
}
}
return (char*)cand_blk - (char*)rhs;
@@ -218,11 +251,8 @@ static UWord aoff_largest_fblk_in_mbc(Allctr_t*, Carrier_t*);
/* Generic tree functions used by both carrier and block trees. */
static void rbt_delete(AOFF_RBTree_t** root, AOFF_RBTree_t* del);
-static void rbt_insert(enum AOFF_Flavor flavor, AOFF_RBTree_t** root, AOFF_RBTree_t* blk);
+static void rbt_insert(enum AOFFSortOrder, AOFF_RBTree_t** root, AOFF_RBTree_t* blk);
static AOFF_RBTree_t* rbt_search(AOFF_RBTree_t* root, Uint size);
-#ifdef HARD_DEBUG
-static int rbt_assert_is_member(AOFF_RBTree_t* root, AOFF_RBTree_t* node);
-#endif
static Eterm info_options(Allctr_t *, char *, fmtfn_t *, void *, Uint **, Uint *);
static void init_atoms(void);
@@ -230,10 +260,17 @@ static void init_atoms(void);
static int atoms_initialized = 0;
+#ifndef ERTS_HAVE_OS_MONOTONIC_TIME_SUPPORT
+static erts_atomic64_t birth_time_counter;
+#endif
+
void
erts_aoffalc_init(void)
{
atoms_initialized = 0;
+#ifndef ERTS_HAVE_OS_MONOTONIC_TIME_SUPPORT
+ erts_atomic64_init_nob(&birth_time_counter, 0);
+#endif
}
Allctr_t *
@@ -254,11 +291,12 @@ erts_aoffalc_start(AOFFAllctr_t *alc,
sys_memcpy((void *) alc, (void *) &zero.allctr, sizeof(AOFFAllctr_t));
- alc->flavor = aoffinit->flavor;
+ alc->blk_order = aoffinit->blk_order;
+ alc->crr_order = aoffinit->crr_order;
allctr->mbc_header_size = sizeof(AOFF_Carrier_t);
allctr->min_mbc_size = MIN_MBC_SZ;
allctr->min_mbc_first_free_size = MIN_MBC_FIRST_FREE_SZ;
- allctr->min_block_size = (aoffinit->flavor == AOFF_BF ?
+ allctr->min_block_size = (aoffinit->blk_order == FF_BF ?
sizeof(AOFF_RBTreeList_t):sizeof(AOFF_RBTree_t));
allctr->vsn_str = ERTS_ALC_AOFF_ALLOC_VSN_STR;
@@ -487,9 +525,9 @@ aoff_unlink_free_block(Allctr_t *allctr, Block_t *blk)
AOFF_Carrier_t *crr = (AOFF_Carrier_t*) FBLK_TO_MBC(&del->hdr);
ASSERT(crr->rbt_node.hdr.bhdr == crr->root->max_sz);
- HARD_CHECK_TREE(&crr->crr, alc->flavor, crr->root, 0);
+ HARD_CHECK_TREE(&crr->crr, alc->blk_order, crr->root, 0);
- if (alc->flavor == AOFF_BF) {
+ if (alc->blk_order == FF_BF) {
ASSERT(del->flags & IS_BF_FLG);
if (IS_LIST_ELEM(del)) {
/* Remove from list */
@@ -510,14 +548,14 @@ aoff_unlink_free_block(Allctr_t *allctr, Block_t *blk)
replace(&crr->root, (AOFF_RBTree_t*)del, LIST_NEXT(del));
- HARD_CHECK_TREE(&crr->crr, alc->flavor, crr->root, 0);
+ HARD_CHECK_TREE(&crr->crr, alc->blk_order, crr->root, 0);
return;
}
}
rbt_delete(&crr->root, (AOFF_RBTree_t*)del);
- HARD_CHECK_TREE(&crr->crr, alc->flavor, crr->root, 0);
+ HARD_CHECK_TREE(&crr->crr, alc->blk_order, crr->root, 0);
/* Update the carrier tree with a potentially new (lower) max_sz
*/
@@ -715,32 +753,33 @@ aoff_link_free_block(Allctr_t *allctr, Block_t *block)
ASSERT(allctr == ERTS_ALC_CARRIER_TO_ALLCTR(&blk_crr->crr));
ASSERT(blk_crr->rbt_node.hdr.bhdr == (blk_crr->root ? blk_crr->root->max_sz : 0));
- HARD_CHECK_TREE(&blk_crr->crr, alc->flavor, blk_crr->root, 0);
+ HARD_CHECK_TREE(&blk_crr->crr, alc->blk_order, blk_crr->root, 0);
- rbt_insert(alc->flavor, &blk_crr->root, blk);
+ rbt_insert(alc->blk_order, &blk_crr->root, blk);
- /* Update the carrier tree with a potentially new (larger) max_sz
- */
+ /*
+ * Update carrier tree with a potentially new (larger) max_sz
+ */
crr_node = &blk_crr->rbt_node;
if (blk_sz > crr_node->hdr.bhdr) {
- ASSERT(blk_sz == blk_crr->root->max_sz);
- crr_node->hdr.bhdr = blk_sz;
- while (blk_sz > crr_node->max_sz) {
- crr_node->max_sz = blk_sz;
- crr_node = crr_node->parent;
- if (!crr_node) break;
- }
+ ASSERT(blk_sz == blk_crr->root->max_sz);
+ crr_node->hdr.bhdr = blk_sz;
+ while (blk_sz > crr_node->max_sz) {
+ crr_node->max_sz = blk_sz;
+ crr_node = crr_node->parent;
+ if (!crr_node) break;
+ }
}
- HARD_CHECK_TREE(&blk_crr->crr, alc->flavor, blk_crr->root, 0);
+ HARD_CHECK_TREE(NULL, alc->crr_order, alc->mbc_root, 0);
}
static void
-rbt_insert(enum AOFF_Flavor flavor, AOFF_RBTree_t** root, AOFF_RBTree_t* blk)
+rbt_insert(enum AOFFSortOrder order, AOFF_RBTree_t** root, AOFF_RBTree_t* blk)
{
Uint blk_sz = AOFF_BLK_SZ(blk);
#ifdef DEBUG
- blk->flags = (flavor == AOFF_BF) ? IS_BF_FLG : 0;
+ blk->flags = (order == FF_BF) ? IS_BF_FLG : 0;
#else
blk->flags = 0;
#endif
@@ -760,7 +799,7 @@ rbt_insert(enum AOFF_Flavor flavor, AOFF_RBTree_t** root, AOFF_RBTree_t* blk)
if (x->max_sz < blk_sz) {
x->max_sz = blk_sz;
}
- diff = cmp_blocks(flavor, blk, x);
+ diff = cmp_blocks(order, blk, x);
if (diff < 0) {
if (!x->left) {
blk->parent = x;
@@ -778,7 +817,7 @@ rbt_insert(enum AOFF_Flavor flavor, AOFF_RBTree_t** root, AOFF_RBTree_t* blk)
x = x->right;
}
else {
- ASSERT(flavor == AOFF_BF);
+ ASSERT(order == FF_BF);
ASSERT(blk->flags & IS_BF_FLG);
ASSERT(x->flags & IS_BF_FLG);
SET_LIST_ELEM(blk);
@@ -798,7 +837,7 @@ rbt_insert(enum AOFF_Flavor flavor, AOFF_RBTree_t** root, AOFF_RBTree_t* blk)
if (IS_RED(blk->parent))
tree_insert_fixup(root, blk);
}
- if (flavor == AOFF_BF) {
+ if (order == FF_BF) {
SET_TREE_NODE(blk);
LIST_NEXT(blk) = NULL;
}
@@ -826,6 +865,16 @@ rbt_search(AOFF_RBTree_t* root, Uint size)
}
}
+Carrier_t* aoff_lookup_pooled_mbc(Allctr_t* allctr, Uint size)
+{
+ AOFF_RBTree_t* node;
+
+ if (!allctr->cpool.pooled_tree)
+ return NULL;
+ node = rbt_search(allctr->cpool.pooled_tree, size);
+ return node ? ErtsContainerStruct(node, Carrier_t, cpool.pooled) : NULL;
+}
+
static Block_t *
aoff_get_free_block(Allctr_t *allctr, Uint size,
Block_t *cand_blk, Uint cand_size)
@@ -850,7 +899,7 @@ aoff_get_free_block(Allctr_t *allctr, Uint size,
/* Get block within carrier tree
*/
#ifdef HARD_DEBUG
- dbg_blk = HARD_CHECK_TREE(&crr->crr, alc->flavor, crr->root, size);
+ dbg_blk = HARD_CHECK_TREE(&crr->crr, alc->blk_order, crr->root, size);
#endif
blk = rbt_search(crr->root, size);
@@ -863,7 +912,7 @@ aoff_get_free_block(Allctr_t *allctr, Uint size,
if (!blk)
return NULL;
- if (cand_blk && cmp_cand_blk(alc->flavor, cand_blk, blk) < 0) {
+ if (cand_blk && cmp_cand_blk(alc->blk_order, cand_blk, blk) < 0) {
return NULL; /* cand_blk was better */
}
@@ -872,23 +921,32 @@ aoff_get_free_block(Allctr_t *allctr, Uint size,
return (Block_t *) blk;
}
+static ERTS_INLINE Sint64 get_birth_time(void)
+{
+#ifdef ERTS_HAVE_OS_MONOTONIC_TIME_SUPPORT
+ return (Sint64) erts_os_monotonic_time();
+#else
+ return (Sint64) erts_atomic64_inc_read_nob(&birth_time_counter);
+#endif
+}
+
static void aoff_creating_mbc(Allctr_t *allctr, Carrier_t *carrier)
{
AOFFAllctr_t *alc = (AOFFAllctr_t *) allctr;
AOFF_Carrier_t *crr = (AOFF_Carrier_t*) carrier;
AOFF_RBTree_t **root = &alc->mbc_root;
- HARD_CHECK_TREE(NULL, 0, *root, 0);
+ HARD_CHECK_TREE(NULL, alc->crr_order, *root, 0);
- /* Link carrier in address order tree
- */
crr->rbt_node.hdr.bhdr = 0;
- rbt_insert(AOFF_AOFF, root, &crr->rbt_node);
+ if (alc->crr_order == FF_AGEFF || IS_DEBUG)
+ crr->birth_time = get_birth_time();
+ rbt_insert(alc->crr_order, root, &crr->rbt_node);
/* aoff_link_free_block will add free block later */
crr->root = NULL;
- HARD_CHECK_TREE(NULL, 0, *root, 0);
+ HARD_CHECK_TREE(NULL, alc->crr_order, *root, 0);
}
#define IS_CRR_IN_TREE(CRR,ROOT) \
@@ -911,27 +969,38 @@ static void aoff_add_mbc(Allctr_t *allctr, Carrier_t *carrier)
AOFF_RBTree_t **root = &alc->mbc_root;
ASSERT(!IS_CRR_IN_TREE(crr, *root));
- HARD_CHECK_TREE(NULL, 0, *root, 0);
+ HARD_CHECK_TREE(NULL, alc->crr_order, *root, 0);
+
+ rbt_insert(alc->crr_order, root, &crr->rbt_node);
+
+ HARD_CHECK_TREE(NULL, alc->crr_order, *root, 0);
+}
+
+void aoff_add_pooled_mbc(Allctr_t *allctr, Carrier_t *crr)
+{
+ AOFF_RBTree_t **root = &allctr->cpool.pooled_tree;
+
+ ASSERT(allctr == crr->cpool.orig_allctr);
+ HARD_CHECK_TREE(NULL, 0, *root, 0);
/* Link carrier in address order tree
*/
- rbt_insert(AOFF_AOFF, root, &crr->rbt_node);
+ rbt_insert(FF_AOFF, root, &crr->cpool.pooled);
HARD_CHECK_TREE(NULL, 0, *root, 0);
}
static void aoff_remove_mbc(Allctr_t *allctr, Carrier_t *carrier)
{
- AOFFAllctr_t *alc = (AOFFAllctr_t *) allctr;
- AOFF_Carrier_t *crr = (AOFF_Carrier_t*) carrier;
- AOFF_RBTree_t **root = &alc->mbc_root;
+ AOFF_RBTree_t **root = &((AOFFAllctr_t*)allctr)->mbc_root;
+ AOFF_Carrier_t *crr = (AOFF_Carrier_t*)carrier;
ASSERT(allctr == ERTS_ALC_CARRIER_TO_ALLCTR(carrier));
if (!IS_CRR_IN_TREE(crr,*root))
- return;
+ return;
- HARD_CHECK_TREE(NULL, 0, *root, 0);
+ HARD_CHECK_TREE(NULL, alc->crr_order, *root, 0);
rbt_delete(root, &crr->rbt_node);
crr->rbt_node.parent = NULL;
@@ -939,9 +1008,27 @@ static void aoff_remove_mbc(Allctr_t *allctr, Carrier_t *carrier)
crr->rbt_node.right = NULL;
crr->rbt_node.max_sz = crr->rbt_node.hdr.bhdr;
- HARD_CHECK_TREE(NULL, 0, *root, 0);
+ HARD_CHECK_TREE(NULL, alc->crr_order, *root, 0);
+}
+
+void aoff_remove_pooled_mbc(Allctr_t *allctr, Carrier_t *crr)
+{
+ ASSERT(allctr == crr->cpool.orig_allctr);
+
+ HARD_CHECK_TREE(NULL, 0, allctr->cpool.pooled_tree, 0);
+
+ rbt_delete(&allctr->cpool.pooled_tree, &crr->cpool.pooled);
+#ifdef DEBUG
+ crr->cpool.pooled.parent = NULL;
+ crr->cpool.pooled.left = NULL;
+ crr->cpool.pooled.right = NULL;
+ crr->cpool.pooled.max_sz = 0;
+#endif
+ HARD_CHECK_TREE(NULL, 0, allctr->cpool.pooled_tree, 0);
+
}
+
static UWord aoff_largest_fblk_in_mbc(Allctr_t* allctr, Carrier_t* carrier)
{
AOFF_Carrier_t *crr = (AOFF_Carrier_t*) carrier;
@@ -955,47 +1042,35 @@ static UWord aoff_largest_fblk_in_mbc(Allctr_t* allctr, Carrier_t* carrier)
* info_options()
*/
+static const char* flavor_str[2][3] = {
+ {"ageffcaoff", "ageffcaobf", "ageffcbf"},
+ { "aoff", "aoffcaobf", "aoffcbf"}
+};
+static Eterm flavor_atoms[2][3];
+
static struct {
Eterm as;
- Eterm aoff;
- Eterm aoffcaobf;
- Eterm aoffcbf;
-#ifdef DEBUG
- Eterm end_of_atoms;
-#endif
} am;
-static void ERTS_INLINE atom_init(Eterm *atom, char *name)
+static void ERTS_INLINE atom_init(Eterm *atom, const char *name)
{
- *atom = am_atom_put(name, strlen(name));
+ *atom = am_atom_put(name, sys_strlen(name));
}
#define AM_INIT(AM) atom_init(&am.AM, #AM)
static void
init_atoms(void)
{
-#ifdef DEBUG
- Eterm *atom;
-#endif
+ int i, j;
if (atoms_initialized)
return;
-#ifdef DEBUG
- for (atom = (Eterm *) &am; atom <= &am.end_of_atoms; atom++) {
- *atom = THE_NON_VALUE;
- }
-#endif
AM_INIT(as);
- AM_INIT(aoff);
- AM_INIT(aoffcaobf);
- AM_INIT(aoffcbf);
-#ifdef DEBUG
- for (atom = (Eterm *) &am; atom < &am.end_of_atoms; atom++) {
- ASSERT(*atom != THE_NON_VALUE);
- }
-#endif
+ for (i = 0; i < 2; i++)
+ for (j = 0; j < 3; j++)
+ atom_init(&flavor_atoms[i][j], flavor_str[i][j]);
atoms_initialized = 1;
}
@@ -1021,15 +1096,16 @@ info_options(Allctr_t *allctr,
{
AOFFAllctr_t* alc = (AOFFAllctr_t*) allctr;
Eterm res = THE_NON_VALUE;
- const char* flavor_str[3] = {"aoff", "aoffcaobf", "aoffcbf"};
- Eterm flavor_atom[3] = {am.aoff, am.aoffcaobf, am.aoffcbf};
+
+ ASSERT(alc->crr_order >= 0 && alc->crr_order <= 1);
+ ASSERT(alc->blk_order >= 1 && alc->blk_order <= 3);
if (print_to_p) {
erts_print(*print_to_p,
print_to_arg,
"%sas: %s\n",
prefix,
- flavor_str[alc->flavor]);
+ flavor_str[alc->crr_order][alc->blk_order-1]);
}
if (hpp || szp) {
@@ -1039,7 +1115,8 @@ info_options(Allctr_t *allctr,
__FILE__, __LINE__);;
res = NIL;
- add_2tup(hpp, szp, &res, am.as, flavor_atom[alc->flavor]);
+ add_2tup(hpp, szp, &res, am.as,
+ flavor_atoms[alc->crr_order][alc->blk_order-1]);
}
return res;
@@ -1057,7 +1134,7 @@ UWord
erts_aoffalc_test(UWord op, UWord a1, UWord a2)
{
switch (op) {
- case 0x500: return (UWord) ((AOFFAllctr_t *) a1)->flavor == AOFF_AOBF;
+ case 0x500: return (UWord) ((AOFFAllctr_t *) a1)->blk_order == FF_AOBF;
case 0x501: {
AOFF_RBTree_t *node = ((AOFFAllctr_t *) a1)->mbc_root;
Uint size = (Uint) a2;
@@ -1072,7 +1149,7 @@ erts_aoffalc_test(UWord op, UWord a1, UWord a2)
case 0x507: return (UWord) IS_TREE_NODE((AOFF_RBTree_t *) a1);
case 0x508: return (UWord) 0; /* IS_BF_ALGO */
case 0x509: return (UWord) ((AOFF_RBTree_t *) a1)->max_sz;
- case 0x50a: return (UWord) ((AOFFAllctr_t *) a1)->flavor == AOFF_BF;
+ case 0x50a: return (UWord) ((AOFFAllctr_t *) a1)->blk_order == FF_BF;
case 0x50b: return (UWord) LIST_PREV(a1);
default: ASSERT(0); return ~((UWord) 0);
}
@@ -1085,12 +1162,13 @@ erts_aoffalc_test(UWord op, UWord a1, UWord a2)
#ifdef HARD_DEBUG
-
-static int rbt_assert_is_member(AOFF_RBTree_t* root, AOFF_RBTree_t* node)
+static int rbt_is_member(AOFF_RBTree_t* root, AOFF_RBTree_t* node)
{
while (node != root) {
- ASSERT(node->parent);
- ASSERT(node->parent->left == node || node->parent->right == node);
+ if (!node->parent || (node->parent->left != node &&
+ node->parent->right != node)) {
+ return 0;
+ }
node = node->parent;
}
return 1;
@@ -1132,7 +1210,7 @@ static void print_tree(AOFF_RBTree_t*);
*/
static AOFF_RBTree_t *
-check_tree(Carrier_t* within_crr, enum AOFF_Flavor flavor, AOFF_RBTree_t* root, Uint size)
+check_tree(Carrier_t* within_crr, enum AOFFSortOrder order, AOFF_RBTree_t* root, Uint size)
{
AOFF_RBTree_t *res = NULL;
Sint blacks;
@@ -1144,7 +1222,8 @@ check_tree(Carrier_t* within_crr, enum AOFF_Flavor flavor, AOFF_RBTree_t* root,
#ifdef PRINT_TREE
print_tree(root);
#endif
- ASSERT(within_crr || flavor == AOFF_AOFF);
+ ASSERT((within_crr && order >= FF_AOFF) ||
+ (!within_crr && order <= FF_AOFF));
if (!root)
return res;
@@ -1202,7 +1281,7 @@ check_tree(Carrier_t* within_crr, enum AOFF_Flavor flavor, AOFF_RBTree_t* root,
ASSERT(((char*)x + AOFF_BLK_SZ(x)) <= ((char*)crr + CARRIER_SZ(crr)));
}
- if (flavor == AOFF_BF) {
+ if (order == FF_BF) {
AOFF_RBTree_t* y = x;
AOFF_RBTree_t* nxt = LIST_NEXT(y);
ASSERT(IS_TREE_NODE(x));
@@ -1225,13 +1304,13 @@ check_tree(Carrier_t* within_crr, enum AOFF_Flavor flavor, AOFF_RBTree_t* root,
if (x->left) {
ASSERT(x->left->parent == x);
- ASSERT(cmp_blocks(flavor, x->left, x) < 0);
+ ASSERT(cmp_blocks(order, x->left, x) < 0);
ASSERT(x->left->max_sz <= x->max_sz);
}
if (x->right) {
ASSERT(x->right->parent == x);
- ASSERT(cmp_blocks(flavor, x->right, x) > 0);
+ ASSERT(cmp_blocks(order, x->right, x) > 0);
ASSERT(x->right->max_sz <= x->max_sz);
}
ASSERT(x->max_sz >= AOFF_BLK_SZ(x));
@@ -1240,7 +1319,7 @@ check_tree(Carrier_t* within_crr, enum AOFF_Flavor flavor, AOFF_RBTree_t* root,
|| x->max_sz == (x->right ? x->right->max_sz : 0));
if (size && AOFF_BLK_SZ(x) >= size) {
- if (!res || cmp_blocks(flavor, x, res) < 0) {
+ if (!res || cmp_blocks(order, x, res) < 0) {
res = x;
}
}
diff --git a/erts/emulator/beam/erl_ao_firstfit_alloc.h b/erts/emulator/beam/erl_ao_firstfit_alloc.h
index 7349c6ab19..9cf4fc81a8 100644
--- a/erts/emulator/beam/erl_ao_firstfit_alloc.h
+++ b/erts/emulator/beam/erl_ao_firstfit_alloc.h
@@ -28,14 +28,16 @@
typedef struct AOFFAllctr_t_ AOFFAllctr_t;
-enum AOFF_Flavor {
- AOFF_AOFF = 0,
- AOFF_AOBF = 1,
- AOFF_BF = 2
+enum AOFFSortOrder {
+ FF_AGEFF = 0,
+ FF_AOFF = 1,
+ FF_AOBF = 2,
+ FF_BF = 3
};
typedef struct {
- enum AOFF_Flavor flavor;
+ enum AOFFSortOrder blk_order;
+ enum AOFFSortOrder crr_order;
} AOFFAllctrInit_t;
#define ERTS_DEFAULT_AOFF_ALLCTR_INIT {0/*dummy*/}
@@ -53,12 +55,12 @@ Allctr_t *erts_aoffalc_start(AOFFAllctr_t *, AOFFAllctrInit_t*, AllctrInit_t *);
#define GET_ERL_ALLOC_UTIL_IMPL
#include "erl_alloc_util.h"
-
struct AOFFAllctr_t_ {
Allctr_t allctr; /* Has to be first! */
struct AOFF_RBTree_t_* mbc_root;
- enum AOFF_Flavor flavor;
+ enum AOFFSortOrder blk_order;
+ enum AOFFSortOrder crr_order;
};
UWord erts_aoffalc_test(UWord, UWord, UWord);
diff --git a/erts/emulator/beam/erl_bestfit_alloc.c b/erts/emulator/beam/erl_bestfit_alloc.c
index 6173c408e1..85fc4c3a85 100644
--- a/erts/emulator/beam/erl_bestfit_alloc.c
+++ b/erts/emulator/beam/erl_bestfit_alloc.c
@@ -875,7 +875,7 @@ static struct {
static void ERTS_INLINE atom_init(Eterm *atom, char *name)
{
- *atom = am_atom_put(name, strlen(name));
+ *atom = am_atom_put(name, sys_strlen(name));
}
#define AM_INIT(AM) atom_init(&am.AM, #AM)
diff --git a/erts/emulator/beam/erl_bif_binary.c b/erts/emulator/beam/erl_bif_binary.c
index 41c2ae08d3..469f6a1ea8 100644
--- a/erts/emulator/beam/erl_bif_binary.c
+++ b/erts/emulator/beam/erl_bif_binary.c
@@ -61,8 +61,6 @@ static Export binary_longest_prefix_trap_export;
static BIF_RETTYPE binary_longest_prefix_trap(BIF_ALIST_3);
static Export binary_longest_suffix_trap_export;
static BIF_RETTYPE binary_longest_suffix_trap(BIF_ALIST_3);
-static Export binary_bin_to_list_trap_export;
-static BIF_RETTYPE binary_bin_to_list_trap(BIF_ALIST_3);
static Export binary_copy_trap_export;
static BIF_RETTYPE binary_copy_trap(BIF_ALIST_2);
static Uint max_loop_limit;
@@ -86,10 +84,6 @@ void erts_init_bif_binary(void)
am_erlang, am_binary_longest_suffix_trap, 3,
&binary_longest_suffix_trap);
- erts_init_trap_export(&binary_bin_to_list_trap_export,
- am_erlang, am_binary_bin_to_list_trap, 3,
- &binary_bin_to_list_trap);
-
erts_init_trap_export(&binary_copy_trap_export,
am_erlang, am_binary_copy_trap, 2,
&binary_copy_trap);
@@ -371,7 +365,7 @@ static ACTrie *create_acdata(MyAllocator *my, Uint len,
acn->d = 0;
acn->final = 0;
acn->h = NULL;
- memset(acn->g, 0, sizeof(ACNode *) * ALPHABET_SIZE);
+ sys_memset(acn->g, 0, sizeof(ACNode *) * ALPHABET_SIZE);
#ifdef HARDDEBUG
act->idc = 0;
acn->id = 0;
@@ -394,7 +388,7 @@ static BMData *create_bmdata(MyAllocator *my, byte *x, Uint len,
init_my_allocator(my, datasize, data);
bmd = my_alloc(my, sizeof(BMData));
bmd->x = my_alloc(my,len);
- memcpy(bmd->x,x,len);
+ sys_memcpy(bmd->x,x,len);
bmd->len = len;
bmd->goodshift = my_alloc(my,sizeof(Uint) * len);
*the_bin = mb;
@@ -439,7 +433,7 @@ static void ac_add_one_pattern(MyAllocator *my, ACTrie *act, byte *x, Uint len)
nn->d = i+1;
nn->h = act->root;
nn->final = 0;
- memset(nn->g, 0, sizeof(ACNode *) * ALPHABET_SIZE);
+ sys_memset(nn->g, 0, sizeof(ACNode *) * ALPHABET_SIZE);
acn->g[x[i]] = nn;
++i;
acn = nn;
@@ -2270,7 +2264,7 @@ static BIF_RETTYPE do_longest_common(Process *p, Eterm list, int direction)
if (cd[i].type == CL_TYPE_HEAP_NOALLOC) {
unsigned char *tmp = cd[i].buff;
cd[i].buff = erts_alloc(ERTS_ALC_T_BINARY_BUFFER, cd[i].bufflen);
- memcpy(cd[i].buff,tmp,cd[i].bufflen);
+ sys_memcpy(cd[i].buff,tmp,cd[i].bufflen);
cd[i].type = CL_TYPE_HEAP;
}
}
@@ -2440,191 +2434,6 @@ BIF_RETTYPE binary_at_2(BIF_ALIST_2)
BIF_ERROR(BIF_P,BADARG);
}
-#define BIN_TO_LIST_OK 0
-#define BIN_TO_LIST_TRAP 1
-/* No badarg, checked before call */
-
-#define BIN_TO_LIST_LOOP_FACTOR 10
-
-static int do_bin_to_list(Process *p, byte *bytes, Uint bit_offs,
- Uint start, Sint *lenp, Eterm *termp)
-{
- Uint reds = get_reds(p, BIN_TO_LIST_LOOP_FACTOR); /* reds can never be 0 */
- Uint len = *lenp;
- Uint loops;
- Eterm *hp;
- Eterm term = *termp;
- Uint n;
-
- ASSERT(reds > 0);
-
- loops = MIN(reds,len);
-
- BUMP_REDS(p, loops / BIN_TO_LIST_LOOP_FACTOR);
-
- hp = HAlloc(p,2*loops);
- while (loops--) {
- --len;
- if (bit_offs) {
- n = ((((Uint) bytes[start+len]) << bit_offs) |
- (((Uint) bytes[start+len+1]) >> (8-bit_offs))) & 0xFF;
- } else {
- n = bytes[start+len];
- }
-
- term = CONS(hp,make_small(n),term);
- hp +=2;
- }
- *termp = term;
- *lenp = len;
- if (len) {
- BUMP_ALL_REDS(p);
- return BIN_TO_LIST_TRAP;
- }
- return BIN_TO_LIST_OK;
-}
-
-
-static BIF_RETTYPE do_trap_bin_to_list(Process *p, Eterm binary,
- Uint start, Sint len, Eterm sofar)
-{
- Eterm *hp;
- Eterm blob;
-
- hp = HAlloc(p,3);
- hp[0] = make_pos_bignum_header(2);
- hp[1] = start;
- hp[2] = (Uint) len;
- blob = make_big(hp);
- BIF_TRAP3(&binary_bin_to_list_trap_export, p, binary, blob, sofar);
-}
-
-static BIF_RETTYPE binary_bin_to_list_trap(BIF_ALIST_3)
-{
- Eterm *ptr;
- Uint start;
- Sint len;
- byte *bytes;
- Uint bit_offs;
- Uint bit_size;
- Eterm res = BIF_ARG_3;
-
- ptr = big_val(BIF_ARG_2);
- start = ptr[1];
- len = (Sint) ptr[2];
-
- ERTS_GET_BINARY_BYTES(BIF_ARG_1,bytes,bit_offs,bit_size);
- if (do_bin_to_list(BIF_P, bytes, bit_offs, start, &len, &res) ==
- BIN_TO_LIST_OK) {
- BIF_RET(res);
- }
- return do_trap_bin_to_list(BIF_P,BIF_ARG_1,start,len,res);
-}
-
-static BIF_RETTYPE binary_bin_to_list_common(Process *p,
- Eterm bin,
- Eterm epos,
- Eterm elen)
-{
- Uint pos;
- Sint len;
- size_t sz;
- byte *bytes;
- Uint bit_offs;
- Uint bit_size;
- Eterm res = NIL;
-
- if (is_not_binary(bin)) {
- goto badarg;
- }
- if (!term_to_Uint(epos, &pos)) {
- goto badarg;
- }
- if (!term_to_Sint(elen, &len)) {
- goto badarg;
- }
- if (len < 0) {
- Uint lentmp = -(Uint)len;
- /* overflow */
- if ((Sint)lentmp < 0) {
- goto badarg;
- }
- len = lentmp;
- if (len > pos) {
- goto badarg;
- }
- pos -= len;
- }
- /* overflow */
- if ((pos + len) < pos || (len > 0 && (pos + len) == pos)) {
- goto badarg;
- }
- sz = binary_size(bin);
-
- if (pos+len > sz) {
- goto badarg;
- }
- ERTS_GET_BINARY_BYTES(bin,bytes,bit_offs,bit_size);
- if (bit_size != 0) {
- goto badarg;
- }
- if(do_bin_to_list(p, bytes, bit_offs, pos, &len, &res) ==
- BIN_TO_LIST_OK) {
- BIF_RET(res);
- }
- return do_trap_bin_to_list(p,bin,pos,len,res);
-
- badarg:
- BIF_ERROR(p,BADARG);
-}
-
-BIF_RETTYPE binary_bin_to_list_3(BIF_ALIST_3)
-{
- return binary_bin_to_list_common(BIF_P,BIF_ARG_1,BIF_ARG_2,BIF_ARG_3);
-}
-
-BIF_RETTYPE binary_bin_to_list_2(BIF_ALIST_2)
-{
- Eterm *tp;
-
- if (is_not_tuple(BIF_ARG_2)) {
- goto badarg;
- }
- tp = tuple_val(BIF_ARG_2);
- if (arityval(*tp) != 2) {
- goto badarg;
- }
- return binary_bin_to_list_common(BIF_P,BIF_ARG_1,tp[1],tp[2]);
- badarg:
- BIF_ERROR(BIF_P,BADARG);
-}
-
-BIF_RETTYPE binary_bin_to_list_1(BIF_ALIST_1)
-{
- Uint pos = 0;
- Sint len;
- byte *bytes;
- Uint bit_offs;
- Uint bit_size;
- Eterm res = NIL;
-
- if (is_not_binary(BIF_ARG_1)) {
- goto badarg;
- }
- len = binary_size(BIF_ARG_1);
- ERTS_GET_BINARY_BYTES(BIF_ARG_1,bytes,bit_offs,bit_size);
- if (bit_size != 0) {
- goto badarg;
- }
- if(do_bin_to_list(BIF_P, bytes, bit_offs, pos, &len, &res) ==
- BIN_TO_LIST_OK) {
- BIF_RET(res);
- }
- return do_trap_bin_to_list(BIF_P,BIF_ARG_1,pos,len,res);
- badarg:
- BIF_ERROR(BIF_P,BADARG);
-}
-
HIPE_WRAPPER_BIF_DISABLE_GC(binary_list_to_bin, 1)
BIF_RETTYPE binary_list_to_bin_1(BIF_ALIST_1)
@@ -2747,7 +2556,7 @@ static BIF_RETTYPE do_binary_copy(Process *p, Eterm bin, Eterm en)
0);
if (!(cbs->temp_alloc)) { /* alignment not needed, need to copy */
byte *tmp = erts_alloc(ERTS_ALC_T_BINARY_BUFFER,size);
- memcpy(tmp,cbs->source,size);
+ sys_memcpy(tmp,cbs->source,size);
cbs->source = tmp;
cbs->source_type = BC_TYPE_HEAP;
} else {
@@ -2760,7 +2569,7 @@ static BIF_RETTYPE do_binary_copy(Process *p, Eterm bin, Eterm en)
pos = 0;
i = 0;
while (pos < reds) {
- memcpy(t+pos,cbs->source, size);
+ sys_memcpy(t+pos,cbs->source, size);
pos += size;
++i;
}
@@ -2785,7 +2594,7 @@ static BIF_RETTYPE do_binary_copy(Process *p, Eterm bin, Eterm en)
}
pos = 0;
while (pos < target_size) {
- memcpy(t+pos,source, size);
+ sys_memcpy(t+pos,source, size);
pos += size;
}
erts_free_aligned_binary_bytes(temp_alloc);
@@ -2815,7 +2624,7 @@ BIF_RETTYPE binary_copy_trap(BIF_ALIST_2)
if ((n-1) * size >= reds) {
Uint i = 0;
while ((pos - opos) < reds) {
- memcpy(t+pos,cbs->source, size);
+ sys_memcpy(t+pos,cbs->source, size);
pos += size;
++i;
}
@@ -2828,7 +2637,7 @@ BIF_RETTYPE binary_copy_trap(BIF_ALIST_2)
Eterm resbin;
Uint target_size = cbs->result->orig_size;
while (pos < target_size) {
- memcpy(t+pos,cbs->source, size);
+ sys_memcpy(t+pos,cbs->source, size);
pos += size;
}
save = cbs->result;
@@ -3220,7 +3029,7 @@ static void dump_bm_data(BMData *bm)
static void dump_ac_node(ACNode *node, int indent, int ch) {
int i;
char *spaces = erts_alloc(ERTS_ALC_T_TMP, 10 * indent + 1);
- memset(spaces,' ',10*indent);
+ sys_memset(spaces,' ',10*indent);
spaces[10*indent] = '\0';
erts_printf("%s-> %c\n",spaces,ch);
erts_printf("%sId: %u\n",spaces,(unsigned) node->id);
diff --git a/erts/emulator/beam/erl_bif_chksum.c b/erts/emulator/beam/erl_bif_chksum.c
index 9417803e14..9095bcd380 100644
--- a/erts/emulator/beam/erl_bif_chksum.c
+++ b/erts/emulator/beam/erl_bif_chksum.c
@@ -516,7 +516,7 @@ md5_2(BIF_ALIST_2)
/* No need to check context, this function cannot be called with unaligned
or badly sized context as it's always trapped to. */
bytes = binary_bytes(BIF_ARG_1);
- memcpy(&context,bytes,sizeof(MD5_CTX));
+ sys_memcpy(&context,bytes,sizeof(MD5_CTX));
rest = do_chksum(&md5_wrap,BIF_P,BIF_ARG_2,100,(void *) &context,&res,
&err);
if (err != 0) {
@@ -564,7 +564,7 @@ md5_update_2(BIF_ALIST_2)
erts_free_aligned_binary_bytes(temp_alloc);
BIF_ERROR(BIF_P, BADARG);
}
- memcpy(&context,bytes,sizeof(MD5_CTX));
+ sys_memcpy(&context,bytes,sizeof(MD5_CTX));
erts_free_aligned_binary_bytes(temp_alloc);
rest = do_chksum(&md5_wrap,BIF_P,BIF_ARG_2,100,(void *) &context,&res,
&err);
@@ -599,7 +599,7 @@ md5_final_1(BIF_ALIST_1)
goto error;
}
bin = erts_new_heap_binary(BIF_P, (byte *)NULL, 16, &result);
- memcpy(&ctx_copy, context, sizeof(MD5_CTX));
+ sys_memcpy(&ctx_copy, context, sizeof(MD5_CTX));
erts_free_aligned_binary_bytes(temp_alloc);
MD5Final(result, &ctx_copy);
BIF_RET(bin);
diff --git a/erts/emulator/beam/erl_bif_ddll.c b/erts/emulator/beam/erl_bif_ddll.c
index f673ef3194..579e9b12f4 100644
--- a/erts/emulator/beam/erl_bif_ddll.c
+++ b/erts/emulator/beam/erl_bif_ddll.c
@@ -1412,7 +1412,7 @@ static Eterm copy_ref(Eterm ref, Eterm *hp)
ErtsORefThing *ptr;
ASSERT(is_internal_ordinary_ref(ref));
ptr = ordinary_ref_thing_ptr(ref);
- memcpy(hp, ptr, sizeof(ErtsORefThing));
+ sys_memcpy(hp, ptr, sizeof(ErtsORefThing));
return (make_internal_ref(hp));
}
@@ -1454,9 +1454,9 @@ static void set_driver_reloading(DE_Handle *dh, Process *proc, char *path, char
dh->procs = p;
dh->status = ERL_DE_RELOAD;
dh->reload_full_path = erts_alloc(ERTS_ALC_T_DDLL_HANDLE, sys_strlen(path) + 1);
- strcpy(dh->reload_full_path,path);
+ sys_strcpy(dh->reload_full_path,path);
dh->reload_driver_name = erts_alloc(ERTS_ALC_T_DDLL_HANDLE, sys_strlen(name) + 1);
- strcpy(dh->reload_driver_name,name);
+ sys_strcpy(dh->reload_driver_name,name);
dh->reload_flags = flags;
}
@@ -1501,7 +1501,7 @@ static int do_load_driver_entry(DE_Handle *dh, char *path, char *name)
goto error;
}
- if (strcmp(name, dp->driver_name) != 0) {
+ if (sys_strcmp(name, dp->driver_name) != 0) {
res = ERL_DE_LOAD_ERROR_BAD_NAME;
goto error;
}
@@ -1799,7 +1799,7 @@ static char *pick_list_or_atom(Eterm name_term)
goto error;
}
name = erts_alloc(ERTS_ALC_T_DDLL_TMP_BUF, ap->len + 1);
- memcpy(name,ap->name,ap->len);
+ sys_memcpy(name,ap->name,ap->len);
name[ap->len] = '\0';
} else {
if (erts_iolist_size(name_term, &name_len)) {
@@ -1870,7 +1870,7 @@ static erts_driver_t *lookup_driver(char *name)
{
erts_driver_t *drv;
assert_drv_list_locked();
- for (drv = driver_list; drv != NULL && strcmp(drv->name, name); drv = drv->next)
+ for (drv = driver_list; drv != NULL && sys_strcmp(drv->name, name); drv = drv->next)
;
return drv;
}
diff --git a/erts/emulator/beam/erl_bif_info.c b/erts/emulator/beam/erl_bif_info.c
index 903f54e2fb..bdca93428e 100644
--- a/erts/emulator/beam/erl_bif_info.c
+++ b/erts/emulator/beam/erl_bif_info.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1999-2017. All Rights Reserved.
+ * Copyright Ericsson AB 1999-2018. 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.
@@ -50,6 +50,7 @@
#define ERTS_PTAB_WANT_DEBUG_FUNCS__
#include "erl_ptab.h"
#include "erl_time.h"
+#include "erl_proc_sig_queue.h"
#ifdef HIPE
#include "hipe_arch.h"
#endif
@@ -72,6 +73,9 @@ static Export *gather_msacc_res_trap;
static Export *gather_gc_info_res_trap;
static Export *gather_system_check_res_trap;
+static Export *is_process_alive_trap;
+
+
#define DECL_AM(S) Eterm AM_ ## S = am_atom_put(#S, sizeof(#S) - 1)
static char otp_version[] = ERLANG_OTP_VERSION;
@@ -211,21 +215,27 @@ bld_magic_ref_bin_list(Uint **hpp, Uint *szp, ErlOffHeap* oh)
make_monitor_list:
returns a list of records..
-record(erl_monitor, {
- type, % MON_ORIGIN or MON_TARGET (1 or 3)
- ref,
+ type, % process | port | time_offset | dist_process | resource
+ % | node | nodes | suspend
+ dir, % origin | target
+ ref, % reference or []
pid, % Process or nodename
- name % registered name or []
+ extra % registered name, integer or []
}).
*/
static void do_calc_mon_size(ErtsMonitor *mon, void *vpsz)
{
+ ErtsMonitorData *mdp = erts_monitor_to_data(mon);
Uint *psz = vpsz;
- *psz += NC_HEAP_SIZE(mon->ref);
- *psz += (mon->type == MON_NIF_TARGET ?
- erts_resource_ref_size(mon->u.resource) :
- (is_immed(mon->u.pid) ? 0 : NC_HEAP_SIZE(mon->u.pid)));
- *psz += 8; /* CONS + 5-tuple */
+ *psz += is_immed(mdp->ref) ? 0 : NC_HEAP_SIZE(mdp->ref);
+
+ if (mon->type == ERTS_MON_TYPE_RESOURCE && erts_monitor_is_target(mon))
+ *psz += erts_resource_ref_size(mon->other.ptr);
+ else
+ *psz += is_immed(mon->other.item) ? 0 : NC_HEAP_SIZE(mon->other.item);
+
+ *psz += 9; /* CONS + 6-tuple */
}
typedef struct {
@@ -237,35 +247,97 @@ typedef struct {
static void do_make_one_mon_element(ErtsMonitor *mon, void * vpmlc)
{
+ ErtsMonitorData *mdp = erts_monitor_to_data(mon);
MonListContext *pmlc = vpmlc;
- Eterm tup;
- Eterm r = STORE_NC(&(pmlc->hp), &MSO(pmlc->p), mon->ref);
- Eterm p = (mon->type == MON_NIF_TARGET ?
- erts_bld_resource_ref(&(pmlc->hp), &MSO(pmlc->p), mon->u.resource)
- : (is_immed(mon->u.pid) ? mon->u.pid
- : STORE_NC(&(pmlc->hp), &MSO(pmlc->p), mon->u.pid)));
- tup = TUPLE5(pmlc->hp, pmlc->tag, make_small(mon->type), r, p, mon->name);
- pmlc->hp += 6;
+ Eterm tup, t, d, r, p, x;
+
+ r = is_immed(mdp->ref) ? mdp->ref : STORE_NC(&(pmlc->hp), &MSO(pmlc->p), mdp->ref);
+ if (mon->type == ERTS_MON_TYPE_RESOURCE && erts_monitor_is_target(mon))
+ p = erts_bld_resource_ref(&(pmlc->hp), &MSO(pmlc->p), mon->other.ptr);
+ else
+ p = (is_immed(mon->other.item)
+ ? mon->other.item
+ : STORE_NC(&(pmlc->hp), &MSO(pmlc->p), mon->other.item));
+
+ if (mon->flags & ERTS_ML_FLG_NAME)
+ x = ((ErtsMonitorDataExtended *) mdp)->u.name;
+ else if (erts_monitor_is_target(mon))
+ x = NIL;
+ else if (mon->type == ERTS_MON_TYPE_NODE || mon->type == ERTS_MON_TYPE_NODES)
+ x = make_small(((ErtsMonitorDataExtended *) mdp)->u.refc);
+ else
+ x = NIL;
+
+ switch (mon->type) {
+ case ERTS_MON_TYPE_PROC:
+ t = am_process;
+ break;
+ case ERTS_MON_TYPE_PORT:
+ t = am_port;
+ break;
+ case ERTS_MON_TYPE_TIME_OFFSET:
+ t = am_time_offset;
+ break;
+ case ERTS_MON_TYPE_DIST_PROC: {
+ ERTS_DECL_AM(dist_process);
+ t = AM_dist_process;
+ break;
+ }
+ case ERTS_MON_TYPE_RESOURCE: {
+ ERTS_DECL_AM(resource);
+ t = AM_resource;
+ break;
+ }
+ case ERTS_MON_TYPE_NODE:
+ t = am_node;
+ break;
+ case ERTS_MON_TYPE_NODES: {
+ ERTS_DECL_AM(nodes);
+ t = AM_nodes;
+ break;
+ }
+ case ERTS_MON_TYPE_SUSPEND:
+ t = am_suspend;
+ break;
+ default:
+ ERTS_INTERNAL_ERROR("Unknown monitor type");
+ t = am_error;
+ break;
+ }
+ if (erts_monitor_is_target(mon)) {
+ ERTS_DECL_AM(target);
+ d = AM_target;
+ }
+ else {
+ ERTS_DECL_AM(origin);
+ d = AM_origin;
+ }
+ tup = TUPLE6(pmlc->hp, pmlc->tag, t, d, r, p, x);
+ pmlc->hp += 7;
pmlc->res = CONS(pmlc->hp, tup, pmlc->res);
pmlc->hp += 2;
}
static Eterm
-make_monitor_list(Process *p, ErtsMonitor *root)
+make_monitor_list(Process *p, int tree, ErtsMonitor *root, Eterm tail)
{
DECL_AM(erl_monitor);
Uint sz = 0;
MonListContext mlc;
+ void (*foreach)(ErtsMonitor *,
+ void (*)(ErtsMonitor *, void *),
+ void *);
- erts_doforall_monitors(root, &do_calc_mon_size, &sz);
- if (sz == 0) {
- return NIL;
- }
+ foreach = tree ? erts_monitor_tree_foreach : erts_monitor_list_foreach;
+
+ (*foreach)(root, do_calc_mon_size, &sz);
+ if (sz == 0)
+ return tail;
mlc.p = p;
mlc.hp = HAlloc(p,sz);
- mlc.res = NIL;
+ mlc.res = tail;
mlc.tag = AM_erl_monitor;
- erts_doforall_monitors(root, &do_make_one_mon_element, &mlc);
+ (*foreach)(root, do_make_one_mon_element, &mlc);
return mlc.res;
}
@@ -273,20 +345,22 @@ make_monitor_list(Process *p, ErtsMonitor *root)
make_link_list:
returns a list of records..
-record(erl_link, {
- type, % LINK_NODE or LINK_PID (1 or 3)
- pid, % Process or nodename
- targets % List of erl_link's or nil
+ type, % process | port | dist_process
+ pid, % Process or port
+ id % (address)
}).
*/
-static void do_calc_lnk_size(ErtsLink *lnk, void *vpsz)
+static void calc_lnk_size(ErtsLink *lnk, void *vpsz)
{
Uint *psz = vpsz;
- *psz += is_immed(lnk->pid) ? 0 : NC_HEAP_SIZE(lnk->pid);
- if (lnk->type != LINK_NODE && ERTS_LINK_ROOT(lnk) != NULL) {
- /* Node links use this pointer as ref counter... */
- erts_doforall_links(ERTS_LINK_ROOT(lnk),&do_calc_lnk_size,vpsz);
- }
+ Uint sz = 0;
+ ErtsLinkData *ldp = erts_link_to_data(lnk);
+
+ (void) erts_bld_uword(NULL, &sz, (UWord) ldp);
+
+ *psz += sz;
+ *psz += is_immed(lnk->other.item) ? 0 : size_object(lnk->other.item);
*psz += 7; /* CONS + 4-tuple */
}
@@ -297,37 +371,58 @@ typedef struct {
Eterm tag;
} LnkListContext;
-static void do_make_one_lnk_element(ErtsLink *lnk, void * vpllc)
+static void make_one_lnk_element(ErtsLink *lnk, void * vpllc)
{
LnkListContext *pllc = vpllc;
- Eterm tup;
- Eterm old_res, targets = NIL;
- Eterm p = (is_immed(lnk->pid)
- ? lnk->pid
- : STORE_NC(&(pllc->hp), &MSO(pllc->p), lnk->pid));
- if (lnk->type == LINK_NODE) {
- targets = make_small(ERTS_LINK_REFC(lnk));
- } else if (ERTS_LINK_ROOT(lnk) != NULL) {
- old_res = pllc->res;
- pllc->res = NIL;
- erts_doforall_links(ERTS_LINK_ROOT(lnk),&do_make_one_lnk_element, vpllc);
- targets = pllc->res;
- pllc->res = old_res;
- }
- tup = TUPLE4(pllc->hp, pllc->tag, make_small(lnk->type), p, targets);
+ Eterm tup, t, pid, id;
+ ErtsLinkData *ldp = erts_link_to_data(lnk);
+
+ id = erts_bld_uword(&pllc->hp, NULL, (UWord) ldp);
+
+ if (is_immed(lnk->other.item))
+ pid = lnk->other.item;
+ else {
+ Uint sz = size_object(lnk->other.item);
+ pid = copy_struct(lnk->other.item, sz, &(pllc->hp), &MSO(pllc->p));
+ }
+
+ switch (lnk->type) {
+ case ERTS_LNK_TYPE_PROC:
+ t = am_process;
+ break;
+ case ERTS_LNK_TYPE_PORT:
+ t = am_port;
+ break;
+ case ERTS_LNK_TYPE_DIST_PROC: {
+ ERTS_DECL_AM(dist_process);
+ t = AM_dist_process;
+ break;
+ }
+ default:
+ ERTS_INTERNAL_ERROR("Unkown link type");
+ t = am_undefined;
+ break;
+ }
+
+ tup = TUPLE4(pllc->hp, pllc->tag, t, pid, id);
pllc->hp += 5;
pllc->res = CONS(pllc->hp, tup, pllc->res);
pllc->hp += 2;
}
static Eterm
-make_link_list(Process *p, ErtsLink *root, Eterm tail)
+make_link_list(Process *p, int tree, ErtsLink *root, Eterm tail)
{
DECL_AM(erl_link);
Uint sz = 0;
LnkListContext llc;
+ void (*foreach)(ErtsLink *,
+ void (*)(ErtsLink *, void *),
+ void *);
- erts_doforall_links(root, &do_calc_lnk_size, &sz);
+ foreach = tree ? erts_link_tree_foreach : erts_link_list_foreach;
+
+ (*foreach)(root, calc_lnk_size, (void *) &sz);
if (sz == 0) {
return tail;
}
@@ -335,7 +430,7 @@ make_link_list(Process *p, ErtsLink *root, Eterm tail)
llc.hp = HAlloc(p,sz);
llc.res = tail;
llc.tag = AM_erl_link;
- erts_doforall_links(root, &do_make_one_lnk_element, &llc);
+ (*foreach)(root, make_one_lnk_element, (void *) &llc);
return llc.res;
}
@@ -381,6 +476,8 @@ typedef struct {
Eterm term;
ErtsResource* resource;
}entity;
+ int named;
+ Uint16 type;
Eterm node;
/* pid is actual target being monitored, no matter pid/port or name */
Eterm pid;
@@ -423,78 +520,103 @@ static void collect_one_link(ErtsLink *lnk, void *vmicp)
{
MonitorInfoCollection *micp = vmicp;
EXTEND_MONITOR_INFOS(micp);
- if (!(lnk->type == LINK_PID)) {
- return;
- }
- micp->mi[micp->mi_i].entity.term = lnk->pid;
- micp->sz += 2 + NC_HEAP_SIZE(lnk->pid);
+ micp->mi[micp->mi_i].entity.term = lnk->other.item;
+ micp->sz += 2 + NC_HEAP_SIZE(lnk->other.item);
micp->mi_i++;
}
static void collect_one_origin_monitor(ErtsMonitor *mon, void *vmicp)
{
- MonitorInfoCollection *micp = vmicp;
+ if (erts_monitor_is_origin(mon)) {
+ MonitorInfoCollection *micp = vmicp;
- if (mon->type != MON_ORIGIN) {
- return;
- }
- EXTEND_MONITOR_INFOS(micp);
- if (is_atom(mon->u.pid)) { /* external by name */
- micp->mi[micp->mi_i].entity.term = mon->name;
- micp->mi[micp->mi_i].node = mon->u.pid;
- micp->sz += 3; /* need one 2-tuple */
- } else if (is_external_pid(mon->u.pid)) { /* external by pid */
- micp->mi[micp->mi_i].entity.term = mon->u.pid;
- micp->mi[micp->mi_i].node = NIL;
- micp->sz += NC_HEAP_SIZE(mon->u.pid);
- } else if (!is_nil(mon->name)) { /* internal by name */
- micp->mi[micp->mi_i].entity.term = mon->name;
- micp->mi[micp->mi_i].node = erts_this_dist_entry->sysname;
- micp->sz += 3; /* need one 2-tuple */
- } else { /* internal by pid */
- micp->mi[micp->mi_i].entity.term = mon->u.pid;
- micp->mi[micp->mi_i].node = NIL;
- /* no additional heap space needed */
- }
-
- /* have always pid at hand, to assist with figuring out if its a port or
- * a process, when we monitored by name and process_info is requested.
- * See: erl_bif_info.c:process_info_aux section for am_monitors */
- micp->mi[micp->mi_i].pid = mon->u.pid;
+ EXTEND_MONITOR_INFOS(micp);
+
+ micp->mi[micp->mi_i].type = mon->type;
+
+ switch (mon->type) {
+ case ERTS_MON_TYPE_PROC:
+ case ERTS_MON_TYPE_PORT:
+ case ERTS_MON_TYPE_DIST_PROC:
+ case ERTS_MON_TYPE_TIME_OFFSET:
+ if (!(mon->flags & ERTS_ML_FLG_NAME)) {
+ micp->mi[micp->mi_i].named = 0;
+ micp->mi[micp->mi_i].entity.term = mon->other.item;
+ micp->mi[micp->mi_i].node = NIL;
+ if (is_not_atom(mon->other.item))
+ micp->sz += NC_HEAP_SIZE(mon->other.item);
+ }
+ else {
+ ErtsMonitorDataExtended *mdep;
+ micp->mi[micp->mi_i].named = !0;
+ mdep = (ErtsMonitorDataExtended *) erts_monitor_to_data(mon);
+ micp->mi[micp->mi_i].entity.term = mdep->u.name;
+ if (mdep->dist)
+ micp->mi[micp->mi_i].node = mdep->dist->nodename;
+ else
+ micp->mi[micp->mi_i].node = erts_this_dist_entry->sysname;
+ micp->sz += 3; /* need one 2-tuple */
+ }
- micp->mi_i++;
- micp->sz += 2 + 3; /* For a cons cell and a 2-tuple */
+ /* have always pid at hand, to assist with figuring out if its a port or
+ * a process, when we monitored by name and process_info is requested.
+ * See: erl_bif_info.c:process_info_aux section for am_monitors */
+ micp->mi[micp->mi_i].pid = mon->other.item;
+
+ micp->mi_i++;
+ micp->sz += 2 + 3; /* For a cons cell and a 2-tuple */
+ break;
+ default:
+ break;
+ }
+ }
}
static void collect_one_target_monitor(ErtsMonitor *mon, void *vmicp)
{
MonitorInfoCollection *micp = vmicp;
- if (mon->type != MON_TARGET && mon->type != MON_NIF_TARGET) {
- return;
- }
+ if (erts_monitor_is_target(mon)) {
- EXTEND_MONITOR_INFOS(micp);
+ EXTEND_MONITOR_INFOS(micp);
+ micp->mi[micp->mi_i].type = mon->type;
+ micp->mi[micp->mi_i].named = !!(mon->flags & ERTS_ML_FLG_NAME);
+ switch (mon->type) {
+
+ case ERTS_MON_TYPE_PROC:
+ case ERTS_MON_TYPE_PORT:
+ case ERTS_MON_TYPE_DIST_PROC:
+
+ micp->mi[micp->mi_i].entity.term = mon->other.item;
+ micp->mi[micp->mi_i].node = NIL;
+ micp->sz += NC_HEAP_SIZE(mon->other.item);
+
+ micp->sz += 2; /* cons */;
+ micp->mi_i++;
+ break;
+
+ case ERTS_MON_TYPE_RESOURCE:
+
+ micp->mi[micp->mi_i].entity.resource = mon->other.ptr;
+ micp->mi[micp->mi_i].node = NIL;
+ micp->sz += erts_resource_ref_size(mon->other.ptr);
+
+ micp->sz += 2; /* cons */;
+ micp->mi_i++;
+ break;
+
+ default:
+ break;
+ }
- if (mon->type == MON_NIF_TARGET) {
- micp->mi[micp->mi_i].entity.resource = mon->u.resource;
- micp->mi[micp->mi_i].node = make_small(MON_NIF_TARGET);
- micp->sz += erts_resource_ref_size(mon->u.resource);
- }
- else {
- micp->mi[micp->mi_i].entity.term = mon->u.pid;
- micp->mi[micp->mi_i].node = NIL;
- micp->sz += NC_HEAP_SIZE(mon->u.pid);
}
- micp->sz += 2; /* cons */;
- micp->mi_i++;
}
typedef struct {
Process *c_p;
ErtsProcLocks c_p_locks;
- ErtsSuspendMonitor **smi;
+ ErtsMonitorSuspend **smi;
Uint smi_i;
Uint smi_max;
int sz;
@@ -517,10 +639,10 @@ do { \
(SMICP)->smi, \
((SMICP)->smi_max \
+ ERTS_SMI_INC) \
- * sizeof(ErtsSuspendMonitor *)) \
+ * sizeof(ErtsMonitorSuspend *)) \
: erts_alloc(ERTS_ALC_T_TMP, \
ERTS_SMI_INC \
- * sizeof(ErtsSuspendMonitor *))); \
+ * sizeof(ErtsMonitorSuspend *))); \
(SMICP)->smi_max += ERTS_SMI_INC; \
} \
} while (0)
@@ -533,12 +655,13 @@ do { \
} while (0)
static void
-collect_one_suspend_monitor(ErtsSuspendMonitor *smon, void *vsmicp)
+collect_one_suspend_monitor(ErtsMonitor *mon, void *vsmicp)
{
+ ErtsMonitorSuspend *smon = erts_monitor_suspend(mon);
ErtsSuspendMonitorInfoCollection *smicp = vsmicp;
Process *suspendee = erts_pid2proc(smicp->c_p,
smicp->c_p_locks,
- smon->pid,
+ mon->other.item,
0);
if (suspendee) { /* suspendee is alive */
Sint a, p;
@@ -572,29 +695,23 @@ collect_one_suspend_monitor(ErtsSuspendMonitor *smon, void *vsmicp)
#define ERTS_PI_FAIL_TYPE_BADARG 0
#define ERTS_PI_FAIL_TYPE_YIELD 1
-#define ERTS_PI_FAIL_TYPE_AWAIT_EXIT 2
+#define ERTS_PI_FAIL_TYPE_EXITED 2
static ERTS_INLINE ErtsProcLocks
pi_locks(Eterm info)
{
switch (info) {
- case am_status:
case am_priority:
- case am_trap_exit:
- return ERTS_PROC_LOCK_STATUS;
- case am_links:
- case am_monitors:
- case am_monitored_by:
- case am_suspending:
- return ERTS_PROC_LOCK_LINK;
+ case am_status:
+ return 0;
+ case am_suspended:
+ return ERTS_PROC_LOCK_STATUS;
case am_messages:
case am_message_queue_len:
case am_total_heap_size:
- return ERTS_PROC_LOCK_MAIN|ERTS_PROC_LOCK_MSGQ;
- case am_memory:
- return ERTS_PROC_LOCK_MAIN|ERTS_PROC_LOCK_LINK|ERTS_PROC_LOCK_MSGQ;
+ return ERTS_PROC_LOCK_MAIN|ERTS_PROC_LOCK_MSGQ;
default:
- return ERTS_PROC_LOCK_MAIN;
+ return ERTS_PROC_LOCK_MAIN;
}
}
@@ -698,7 +815,6 @@ static Eterm pi_1_keys[] = {
am_initial_call,
am_status,
am_message_queue_len,
- am_messages,
am_links,
am_dictionary,
am_trap_exit,
@@ -743,7 +859,7 @@ process_info_init(void)
}
static ERTS_INLINE Process *
-pi_pid2proc(Process *c_p, Eterm pid, ErtsProcLocks info_locks)
+pi_lookup_proc(Process *c_p, Eterm pid, ErtsProcLocks *locks)
{
/*
* If the main lock is needed, we use erts_pid2proc_not_running()
@@ -758,23 +874,79 @@ pi_pid2proc(Process *c_p, Eterm pid, ErtsProcLocks info_locks)
* is currently running.
*/
- if (info_locks & ERTS_PROC_LOCK_MAIN)
- return erts_pid2proc_not_running(c_p, ERTS_PROC_LOCK_MAIN,
- pid, info_locks);
- else
- return erts_pid2proc(c_p, ERTS_PROC_LOCK_MAIN,
- pid, info_locks);
+ if ((*locks) & ERTS_PROC_LOCK_MAIN) {
+ erts_aint32_t state;
+ int local_only, done;
+ Process *rp;
+ ErtsProcLocks more_locks;
+
+ rp = erts_pid2proc_not_running(c_p, ERTS_PROC_LOCK_MAIN,
+ pid, ERTS_PROC_LOCK_MAIN);
+
+ if (!rp || rp == ERTS_PROC_LOCK_BUSY)
+ return rp;
+
+ if ((*locks) & ERTS_PROC_LOCK_MSGQ) {
+ /*
+ * Move in queue into private queue and
+ * release msgq lock, enabling others to
+ * send messages to the process while it
+ * is being inspected...
+ */
+ erts_proc_lock(rp, ERTS_PROC_LOCK_MSGQ);
+ erts_proc_sig_fetch(rp);
+ erts_proc_unlock(rp, ERTS_PROC_LOCK_MSGQ);
+ (*locks) &= ~ERTS_PROC_LOCK_MSGQ;
+ }
+
+ /*
+ * Handle all signals received up to this point
+ * in order to preserve signal order.
+ *
+ * FIX ME: Should be done yielding...
+ */
+ local_only = 0;
+ do {
+ int r = CONTEXT_REDS;
+ done = erts_proc_sig_handle_incoming(rp, &state, &r,
+ CONTEXT_REDS,
+ local_only);
+ local_only = !0;
+ BUMP_REDS(c_p, r);
+ } while (!done && !(state & ERTS_PSFLG_EXITING));
+
+ if (state & ERTS_PSFLG_EXITING) {
+ if (rp != c_p)
+ erts_proc_unlock(rp, ERTS_PROC_LOCK_MAIN);
+ return NULL;
+ }
+ more_locks = (*locks) & ~ERTS_PROC_LOCK_MAIN;
+ if (more_locks)
+ erts_proc_lock(rp, more_locks);
+ return rp;
+ }
+
+ ASSERT(!((*locks) & ERTS_PROC_LOCK_MSGQ));
+
+ if (*locks)
+ return erts_pid2proc(c_p, ERTS_PROC_LOCK_MAIN,
+ pid, *locks);
+
+ return erts_proc_lookup(pid);
}
+
+
static BIF_RETTYPE
process_info_aux(Process *BIF_P,
Process *rp,
ErtsProcLocks rp_locks,
Eterm rpid,
Eterm item,
- int always_wrap);
+ int always_wrap,
+ int *reds);
#define ERTS_PI_RES_ELEM_IX_BUF_INC 1024
#define ERTS_PI_DEF_RES_ELEM_IX_BUF_SZ ERTS_PI_ARGS
@@ -794,6 +966,7 @@ process_info_list(Process *c_p, Eterm pid, Eterm list, int always_wrap,
ErtsProcLocks locks = (ErtsProcLocks) 0;
int res_len, ix;
Process *rp = NULL;
+ int reds = 0;
*fail_type = ERTS_PI_FAIL_TYPE_BADARG;
@@ -845,9 +1018,14 @@ process_info_list(Process *c_p, Eterm pid, Eterm list, int always_wrap,
ASSERT(res_len > 0);
- rp = pi_pid2proc(c_p, pid, locks|ERTS_PROC_LOCK_STATUS);
+ rp = pi_lookup_proc(c_p, pid, &locks);
if (!rp) {
- res = am_undefined;
+ if (c_p->common.id != pid)
+ res = am_undefined;
+ else {
+ *fail_type = ERTS_PI_FAIL_TYPE_EXITED;
+ res = THE_NON_VALUE;
+ }
goto done;
}
else if (rp == ERTS_PROC_LOCK_BUSY) {
@@ -856,38 +1034,9 @@ process_info_list(Process *c_p, Eterm pid, Eterm list, int always_wrap,
*fail_type = ERTS_PI_FAIL_TYPE_YIELD;
goto done;
}
- else if (c_p != rp && ERTS_PROC_PENDING_EXIT(rp)) {
- locks |= ERTS_PROC_LOCK_STATUS;
- res = THE_NON_VALUE;
- *fail_type = ERTS_PI_FAIL_TYPE_AWAIT_EXIT;
- goto done;
- }
- else {
- ErtsProcLocks unlock_locks = 0;
-
- if (c_p == rp)
- locks |= ERTS_PROC_LOCK_MAIN;
-
- if (!(locks & ERTS_PROC_LOCK_STATUS))
- unlock_locks |= ERTS_PROC_LOCK_STATUS;
-
- if (locks & ERTS_PROC_LOCK_MSGQ) {
- /*
- * Move in queue into private queue and
- * release msgq lock, enabling others to
- * send messages to the process while it
- * is being inspected...
- */
- ASSERT(locks & ERTS_PROC_LOCK_MAIN);
- ERTS_MSGQ_MV_INQ2PRIVQ(rp);
- locks &= ~ERTS_PROC_LOCK_MSGQ;
- unlock_locks |= ERTS_PROC_LOCK_MSGQ;
- }
- if (unlock_locks)
- erts_proc_unlock(rp, unlock_locks);
-
- }
+ if (c_p == rp)
+ locks |= ERTS_PROC_LOCK_MAIN;
/*
* We always handle 'messages' first if it should be part
@@ -899,16 +1048,23 @@ process_info_list(Process *c_p, Eterm pid, Eterm list, int always_wrap,
if (want_messages) {
ix = pi_arg2ix(am_messages);
ASSERT(part_res[ix] == THE_NON_VALUE);
- part_res[ix] = process_info_aux(c_p, rp, locks, pid, am_messages, always_wrap);
- ASSERT(part_res[ix] != THE_NON_VALUE);
+ res = process_info_aux(c_p, rp, locks, pid, am_messages,
+ always_wrap, &reds);
+ ASSERT(res != am_undefined);
+ ASSERT(res != THE_NON_VALUE);
+ part_res[ix] = res;
}
for (; res_elem_ix_ix >= 0; res_elem_ix_ix--) {
ix = res_elem_ix[res_elem_ix_ix];
if (part_res[ix] == THE_NON_VALUE) {
arg = pi_ix2arg(ix);
- part_res[ix] = process_info_aux(c_p, rp, locks, pid, arg, always_wrap);
- ASSERT(part_res[ix] != THE_NON_VALUE);
+ res = process_info_aux(c_p, rp, locks, pid, arg,
+ always_wrap, &reds);
+ if (res == am_undefined)
+ goto done;
+ ASSERT(res != THE_NON_VALUE);
+ part_res[ix] = res;
}
}
@@ -948,6 +1104,8 @@ process_info_list(Process *c_p, Eterm pid, Eterm list, int always_wrap,
if (res_elem_ix != &def_res_elem_ix_buf[0])
erts_free(ERTS_ALC_T_TMP, res_elem_ix);
+ BUMP_REDS(c_p, reds);
+
return res;
}
@@ -971,8 +1129,8 @@ BIF_RETTYPE process_info_1(BIF_ALIST_1)
BIF_ERROR(BIF_P, BADARG);
case ERTS_PI_FAIL_TYPE_YIELD:
ERTS_BIF_YIELD1(bif_export[BIF_process_info_1], BIF_P, BIF_ARG_1);
- case ERTS_PI_FAIL_TYPE_AWAIT_EXIT:
- ERTS_BIF_AWAIT_X_DATA_TRAP(BIF_P, BIF_ARG_1, am_undefined);
+ case ERTS_PI_FAIL_TYPE_EXITED:
+ ERTS_BIF_EXITED(BIF_P);
default:
erts_exit(ERTS_ABORT_EXIT, "%s:%d: Internal error", __FILE__, __LINE__);
}
@@ -989,7 +1147,7 @@ BIF_RETTYPE process_info_2(BIF_ALIST_2)
Process *rp;
Eterm pid = BIF_ARG_1;
ErtsProcLocks info_locks;
- int fail_type;
+ int fail_type, reds = 0;
if (is_external_pid(pid)
&& external_pid_dist_entry(pid) == erts_this_dist_entry)
@@ -1011,8 +1169,8 @@ BIF_RETTYPE process_info_2(BIF_ALIST_2)
case ERTS_PI_FAIL_TYPE_YIELD:
ERTS_BIF_YIELD2(bif_export[BIF_process_info_2], BIF_P,
BIF_ARG_1, BIF_ARG_2);
- case ERTS_PI_FAIL_TYPE_AWAIT_EXIT:
- ERTS_BIF_AWAIT_X_DATA_TRAP(BIF_P, BIF_ARG_1, am_undefined);
+ case ERTS_PI_FAIL_TYPE_EXITED:
+ ERTS_BIF_EXITED(BIF_P);
default:
erts_exit(ERTS_ABORT_EXIT, "%s:%d: Internal error",
__FILE__, __LINE__);
@@ -1027,44 +1185,23 @@ BIF_RETTYPE process_info_2(BIF_ALIST_2)
info_locks = pi_locks(BIF_ARG_2);
- rp = pi_pid2proc(BIF_P, pid, info_locks|ERTS_PROC_LOCK_STATUS);
- if (!rp)
- res = am_undefined;
+ rp = pi_lookup_proc(BIF_P, pid, &info_locks);
+ if (!rp) {
+ if (BIF_P->common.id == pid)
+ ERTS_BIF_EXITED(BIF_P);
+ BIF_RET(am_undefined);
+ }
else if (rp == ERTS_PROC_LOCK_BUSY)
ERTS_BIF_YIELD2(bif_export[BIF_process_info_2], BIF_P,
BIF_ARG_1, BIF_ARG_2);
- else if (rp != BIF_P && ERTS_PROC_PENDING_EXIT(rp)) {
- erts_proc_unlock(rp, info_locks|ERTS_PROC_LOCK_STATUS);
- ERTS_BIF_AWAIT_X_DATA_TRAP(BIF_P, BIF_ARG_1, am_undefined);
- }
- else {
- ErtsProcLocks unlock_locks = 0;
- if (BIF_P == rp)
- info_locks |= ERTS_PROC_LOCK_MAIN;
-
- if (!(info_locks & ERTS_PROC_LOCK_STATUS))
- unlock_locks |= ERTS_PROC_LOCK_STATUS;
-
- if (info_locks & ERTS_PROC_LOCK_MSGQ) {
- /*
- * Move in queue into private queue and
- * release msgq lock, enabling others to
- * send messages to the process while it
- * is being inspected...
- */
- ASSERT(info_locks & ERTS_PROC_LOCK_MAIN);
- ERTS_MSGQ_MV_INQ2PRIVQ(rp);
- info_locks &= ~ERTS_PROC_LOCK_MSGQ;
- unlock_locks |= ERTS_PROC_LOCK_MSGQ;
- }
+ if (BIF_P == rp)
+ info_locks |= ERTS_PROC_LOCK_MAIN;
- if (unlock_locks)
- erts_proc_unlock(rp, unlock_locks);
+ res = process_info_aux(BIF_P, rp, info_locks, pid, BIF_ARG_2,
+ 0, &reds);
- res = process_info_aux(BIF_P, rp, info_locks, pid, BIF_ARG_2, 0);
- }
- ASSERT(is_value(res));
+ BUMP_REDS(BIF_P, reds);
if (BIF_P == rp)
info_locks &= ~ERTS_PROC_LOCK_MAIN;
@@ -1081,11 +1218,14 @@ process_info_aux(Process *BIF_P,
ErtsProcLocks rp_locks,
Eterm rpid,
Eterm item,
- int always_wrap)
+ int always_wrap,
+ int *reds)
{
Eterm *hp;
Eterm res = NIL;
+ (*reds)++;
+
ASSERT(rp);
/*
@@ -1144,14 +1284,15 @@ process_info_aux(Process *BIF_P,
break;
case am_status:
- res = erts_process_status(rp, rpid);
- ASSERT(res != am_undefined);
+ res = erts_process_state2status(erts_atomic32_read_nob(&rp->state));
+ if (res == am_exiting || res == am_free)
+ return am_undefined;
hp = HAlloc(BIF_P, 3);
break;
case am_messages: {
- if (rp->msg.len == 0 || ERTS_TRACE_FLAGS(rp) & F_SENSITIVE) {
+ if (rp->sig_qs.len == 0 || ERTS_TRACE_FLAGS(rp) & F_SENSITIVE) {
hp = HAlloc(BIF_P, 3);
} else {
ErtsMessageInfo *mip;
@@ -1162,16 +1303,17 @@ process_info_aux(Process *BIF_P,
#endif
mip = erts_alloc(ERTS_ALC_T_TMP,
- rp->msg.len*sizeof(ErtsMessageInfo));
+ rp->sig_qs.len*sizeof(ErtsMessageInfo));
/*
* Note that message queue may shrink when calling
- * erts_prep_msgq_for_inspection() since it removes
+ * erts_proc_sig_prep_msgq_for_inspection() since it removes
* corrupt distribution messages.
*/
- heap_need = erts_prep_msgq_for_inspection(BIF_P, rp, rp_locks, mip);
+ heap_need = erts_proc_sig_prep_msgq_for_inspection(BIF_P, rp,
+ rp_locks, mip);
heap_need += 3; /* top 2-tuple */
- heap_need += rp->msg.len*2; /* Cons cells */
+ heap_need += rp->sig_qs.len*2; /* Cons cells */
hp = HAlloc(BIF_P, heap_need); /* heap_need is exact */
#ifdef DEBUG
@@ -1179,7 +1321,7 @@ process_info_aux(Process *BIF_P,
#endif
/* Build list of messages... */
- for (i = rp->msg.len - 1, res = NIL; i >= 0; i--) {
+ for (i = rp->sig_qs.len - 1, res = NIL; i >= 0; i--) {
Eterm msg = ERL_MESSAGE_TERM(mip[i].msgp);
Uint sz = mip[i].size;
@@ -1192,6 +1334,11 @@ process_info_aux(Process *BIF_P,
ASSERT(hp_end == hp + 3);
+ if (rp->sig_qs.len > CONTEXT_REDS*4)
+ *reds += CONTEXT_REDS*4;
+ else
+ *reds += rp->sig_qs.len / 4;
+
erts_free(ERTS_ALC_T_TMP, mip);
}
break;
@@ -1199,7 +1346,7 @@ process_info_aux(Process *BIF_P,
case am_message_queue_len:
hp = HAlloc(BIF_P, 3);
- res = make_small(rp->msg.len);
+ res = make_small(rp->sig_qs.len);
break;
case am_links: {
@@ -1209,7 +1356,7 @@ process_info_aux(Process *BIF_P,
INIT_MONITOR_INFOS(mic);
- erts_doforall_links(ERTS_P_LINKS(rp),&collect_one_link,&mic);
+ erts_link_tree_foreach(ERTS_P_LINKS(rp), collect_one_link, (void *) &mic);
hp = HAlloc(BIF_P, 3 + mic.sz);
res = NIL;
@@ -1218,6 +1365,12 @@ process_info_aux(Process *BIF_P,
res = CONS(hp, item, res);
hp += 2;
}
+
+ if (mic.mi_i > CONTEXT_REDS*4)
+ *reds += CONTEXT_REDS*4;
+ else
+ *reds += mic.mi_i / 4;
+
DESTROY_MONITOR_INFOS(mic);
break;
}
@@ -1227,12 +1380,14 @@ process_info_aux(Process *BIF_P,
int i;
INIT_MONITOR_INFOS(mic);
- erts_doforall_monitors(ERTS_P_MONITORS(rp),
- &collect_one_origin_monitor, &mic);
+ erts_monitor_tree_foreach(ERTS_P_MONITORS(rp),
+ collect_one_origin_monitor,
+ (void *) &mic);
+
hp = HAlloc(BIF_P, 3 + mic.sz);
res = NIL;
for (i = 0; i < mic.mi_i; i++) {
- if (is_atom(mic.mi[i].entity.term)) {
+ if (mic.mi[i].named) {
/* Monitor by name.
* Build {process|port, {Name, Node}} and cons it.
*/
@@ -1252,11 +1407,28 @@ process_info_aux(Process *BIF_P,
hp += 2;
}
else {
- /* Monitor by pid. Build {process|port, Pid} and cons it. */
+ /* Build {process|port|time_offset, Pid|clock_service} and cons it. */
Eterm t;
- Eterm pid = STORE_NC(&hp, &MSO(BIF_P), mic.mi[i].entity.term);
+ Eterm pid;
+ Eterm m_type;
+
+ if (is_atom(mic.mi[i].entity.term))
+ pid = mic.mi[i].entity.term;
+ else
+ pid = STORE_NC(&hp, &MSO(BIF_P), mic.mi[i].entity.term);
+
+ switch (mic.mi[i].type) {
+ case ERTS_MON_TYPE_PORT:
+ m_type = am_port;
+ break;
+ case ERTS_MON_TYPE_TIME_OFFSET:
+ m_type = am_time_offset;
+ break;
+ default:
+ m_type = am_process;
+ break;
+ }
- Eterm m_type = is_port(mic.mi[i].pid) ? am_port : am_process;
ASSERT(is_pid(mic.mi[i].pid)
|| is_port(mic.mi[i].pid));
@@ -1266,6 +1438,12 @@ process_info_aux(Process *BIF_P,
hp += 2;
}
}
+
+ if (mic.mi_i > CONTEXT_REDS*4)
+ *reds += CONTEXT_REDS*4;
+ else
+ *reds += mic.mi_i / 4;
+
DESTROY_MONITOR_INFOS(mic);
break;
}
@@ -1276,20 +1454,34 @@ process_info_aux(Process *BIF_P,
Eterm item;
INIT_MONITOR_INFOS(mic);
- erts_doforall_monitors(ERTS_P_MONITORS(rp),&collect_one_target_monitor,&mic);
+ erts_monitor_list_foreach(ERTS_P_LT_MONITORS(rp),
+ collect_one_target_monitor,
+ (void *) &mic);
+ erts_monitor_tree_foreach(ERTS_P_MONITORS(rp),
+ collect_one_target_monitor,
+ (void *) &mic);
+
hp = HAlloc(BIF_P, 3 + mic.sz);
res = NIL;
for (i = 0; i < mic.mi_i; ++i) {
- if (mic.mi[i].node == make_small(MON_NIF_TARGET)) {
- item = erts_bld_resource_ref(&hp, &MSO(BIF_P), mic.mi[i].entity.resource);
- }
- else {
- item = STORE_NC(&hp, &MSO(BIF_P), mic.mi[i].entity.term);
- }
+ if (mic.mi[i].type == ERTS_MON_TYPE_RESOURCE)
+ item = erts_bld_resource_ref(&hp,
+ &MSO(BIF_P),
+ mic.mi[i].entity.resource);
+ else
+ item = STORE_NC(&hp,
+ &MSO(BIF_P),
+ mic.mi[i].entity.term);
res = CONS(hp, item, res);
hp += 2;
}
+
+ if (mic.mi_i > CONTEXT_REDS*4)
+ *reds += CONTEXT_REDS*4;
+ else
+ *reds += mic.mi_i / 4;
+
DESTROY_MONITOR_INFOS(mic);
break;
}
@@ -1306,11 +1498,11 @@ process_info_aux(Process *BIF_P,
BIF_P,
(BIF_P == rp
? ERTS_PROC_LOCK_MAIN
- : 0) | ERTS_PROC_LOCK_LINK);
+ : 0) | ERTS_PROC_LOCK_STATUS);
- erts_doforall_suspend_monitors(rp->suspend_monitors,
- &collect_one_suspend_monitor,
- &smic);
+ erts_monitor_tree_foreach(rp->suspend_monitors,
+ &collect_one_suspend_monitor,
+ &smic);
hp = HAlloc(BIF_P, 3 + smic.sz);
#ifdef DEBUG
hp_end = hp + smic.sz;
@@ -1334,12 +1526,17 @@ process_info_aux(Process *BIF_P,
pending = small_to_big(p, hp);
hp += BIG_UINT_HEAP_SIZE;
}
- item = TUPLE3(hp, smic.smi[i]->pid, active, pending);
+ item = TUPLE3(hp, smic.smi[i]->mon.other.item, active, pending);
hp += 4;
res = CONS(hp, item, res);
hp += 2;
}
+ if (smic.smi_i > CONTEXT_REDS*4)
+ *reds += CONTEXT_REDS*4;
+ else
+ *reds += smic.smi_i / 4;
+
ERTS_DESTROY_SUSPEND_MONITOR_INFOS(smic);
ASSERT(hp == hp_end);
@@ -1347,18 +1544,22 @@ process_info_aux(Process *BIF_P,
}
case am_dictionary:
- if (ERTS_TRACE_FLAGS(rp) & F_SENSITIVE) {
+ if (!rp->dictionary || (ERTS_TRACE_FLAGS(rp) & F_SENSITIVE)) {
res = NIL;
} else {
+ Uint num = rp->dictionary->numElements;
res = erts_dictionary_copy(BIF_P, rp->dictionary);
+ if (num > CONTEXT_REDS*4)
+ *reds += CONTEXT_REDS*4;
+ else
+ *reds += (int) num / 4;
}
hp = HAlloc(BIF_P, 3);
break;
case am_trap_exit: {
- erts_aint32_t state = erts_atomic32_read_nob(&rp->state);
hp = HAlloc(BIF_P, 3);
- if (state & ERTS_PSFLG_TRAP_EXIT)
+ if (rp->flags & F_TRAP_EXIT)
res = am_true;
else
res = am_false;
@@ -1415,7 +1616,6 @@ process_info_aux(Process *BIF_P,
}
case am_total_heap_size: {
- ErtsMessage *mp;
Uint total_heap_size;
Uint hsz = 3;
@@ -1425,10 +1625,19 @@ process_info_aux(Process *BIF_P,
total_heap_size += rp->mbuf_sz;
- if (rp->flags & F_ON_HEAP_MSGQ)
- for (mp = rp->msg.first; mp; mp = mp->next)
- if (mp->data.attached)
- total_heap_size += erts_msg_attached_data_size(mp);
+ if (rp->flags & F_ON_HEAP_MSGQ) {
+ ERTS_FOREACH_SIG_PRIVQS(
+ rp, mp,
+ {
+ if (ERTS_SIG_IS_MSG(mp) && mp->data.attached)
+ total_heap_size += erts_msg_attached_data_size(mp);
+ });
+
+ if (rp->sig_qs.len > CONTEXT_REDS*4)
+ *reds += CONTEXT_REDS*4;
+ else
+ *reds += rp->sig_qs.len / 4;
+ }
(void) erts_bld_uint(NULL, &hsz, total_heap_size);
hp = HAlloc(BIF_P, hsz);
@@ -1451,6 +1660,12 @@ process_info_aux(Process *BIF_P,
(void) erts_bld_uint(NULL, &hsz, size);
hp = HAlloc(BIF_P, hsz);
res = erts_bld_uint(&hp, NULL, size);
+
+ if (rp->sig_qs.len > CONTEXT_REDS*4)
+ *reds += CONTEXT_REDS*4;
+ else
+ *reds += rp->sig_qs.len / 4;
+
break;
}
@@ -1523,10 +1738,14 @@ process_info_aux(Process *BIF_P,
break;
}
- case am_priority:
+ case am_priority: {
+ erts_aint32_t state = erts_atomic32_read_nob(&rp->state);
+ if (ERTS_PSFLG_EXITING & state)
+ return am_undefined;
hp = HAlloc(BIF_P, 3);
- res = erts_get_process_priority(rp);
+ res = erts_get_process_priority(state);
break;
+ }
case am_trace:
hp = HAlloc(BIF_P, 3);
@@ -1629,6 +1848,8 @@ process_info_aux(Process *BIF_P,
(void) bld_magic_ref_bin_list(NULL, &sz, &MSO(rp));
hp = HAlloc(BIF_P, sz);
res = bld_magic_ref_bin_list(&hp, NULL, &MSO(rp));
+
+ *reds += 10;
break;
}
@@ -2378,12 +2599,12 @@ BIF_RETTYPE system_info_1(BIF_ALIST_1)
BIF_RET(os_version_tuple);
}
else if (BIF_ARG_1 == am_version) {
- int n = strlen(ERLANG_VERSION);
+ int n = sys_strlen(ERLANG_VERSION);
hp = HAlloc(BIF_P, ((sizeof ERLANG_VERSION)-1) * 2);
BIF_RET(buf_to_intlist(&hp, ERLANG_VERSION, n, NIL));
}
else if (BIF_ARG_1 == am_machine) {
- int n = strlen(EMULATOR);
+ int n = sys_strlen(EMULATOR);
hp = HAlloc(BIF_P, n*2);
BIF_RET(buf_to_intlist(&hp, EMULATOR, n, NIL));
}
@@ -2409,7 +2630,7 @@ BIF_RETTYPE system_info_1(BIF_ALIST_1)
res = erts_bld_cons(hpp, hszp,
erts_bld_tuple(hpp, hszp, 2,
erts_atom_put((byte *)opc[i].name,
- strlen(opc[i].name),
+ sys_strlen(opc[i].name),
ERTS_ATOM_ENC_LATIN1,
1),
erts_bld_uint(hpp, hszp,
@@ -2805,21 +3026,21 @@ BIF_RETTYPE system_info_1(BIF_ALIST_1)
Uint sz;
Eterm res = NIL, tup, text;
Eterm *hp = HAlloc(BIF_P, 3*(2 + 3) + /* three 2-tuples and three cons */
- 2*(strlen(erts_build_flags_CONFIG_H) +
- strlen(erts_build_flags_CFLAGS) +
- strlen(erts_build_flags_LDFLAGS)));
+ 2*(sys_strlen(erts_build_flags_CONFIG_H) +
+ sys_strlen(erts_build_flags_CFLAGS) +
+ sys_strlen(erts_build_flags_LDFLAGS)));
- sz = strlen(erts_build_flags_CONFIG_H);
+ sz = sys_strlen(erts_build_flags_CONFIG_H);
text = buf_to_intlist(&hp, erts_build_flags_CONFIG_H, sz, NIL);
tup = TUPLE2(hp, am_config_h, text); hp += 3;
res = CONS(hp, tup, res); hp += 2;
- sz = strlen(erts_build_flags_CFLAGS);
+ sz = sys_strlen(erts_build_flags_CFLAGS);
text = buf_to_intlist(&hp, erts_build_flags_CFLAGS, sz, NIL);
tup = TUPLE2(hp, am_cflags, text); hp += 3;
res = CONS(hp, tup, res); hp += 2;
- sz = strlen(erts_build_flags_LDFLAGS);
+ sz = sys_strlen(erts_build_flags_LDFLAGS);
text = buf_to_intlist(&hp, erts_build_flags_LDFLAGS, sz, NIL);
tup = TUPLE2(hp, am_ldflags, text); hp += 3;
res = CONS(hp, tup, res); hp += 2;
@@ -2863,6 +3084,16 @@ BIF_RETTYPE system_info_1(BIF_ALIST_1)
BIF_ERROR(BIF_P, BADARG);
}
+static void monitor_size(ErtsMonitor *mon, void *vsz)
+{
+ *((Uint *) vsz) = erts_monitor_size(mon);
+}
+
+static void link_size(ErtsMonitor *lnk, void *vsz)
+{
+ *((Uint *) vsz) = erts_link_size(lnk);
+}
+
/**********************************************************************/
/* Return information on ports */
/* Info:
@@ -2898,7 +3129,7 @@ erts_bld_port_info(Eterm **hpp, ErlOffHeap *ohp, Uint *szp, Port *prt,
INIT_MONITOR_INFOS(mic);
- erts_doforall_links(ERTS_P_LINKS(prt), &collect_one_link, &mic);
+ erts_link_tree_foreach(ERTS_P_LINKS(prt), collect_one_link, (void *) &mic);
if (szp)
*szp += mic.sz;
@@ -2922,11 +3153,11 @@ erts_bld_port_info(Eterm **hpp, ErlOffHeap *ohp, Uint *szp, Port *prt,
else if (item == am_monitors) {
MonitorInfoCollection mic;
int i;
- Eterm item;
INIT_MONITOR_INFOS(mic);
- erts_doforall_monitors(ERTS_P_MONITORS(prt),
- &collect_one_origin_monitor, &mic);
+ erts_monitor_tree_foreach(ERTS_P_MONITORS(prt),
+ collect_one_origin_monitor,
+ (void *) &mic);
if (szp)
*szp += mic.sz;
@@ -2935,11 +3166,10 @@ erts_bld_port_info(Eterm **hpp, ErlOffHeap *ohp, Uint *szp, Port *prt,
res = NIL;
for (i = 0; i < mic.mi_i; i++) {
Eterm t;
- Eterm m_type;
- item = STORE_NC(hpp, ohp, mic.mi[i].entity.term);
- m_type = is_port(item) ? am_port : am_process;
- t = TUPLE2(*hpp, m_type, item);
+ ASSERT(mic.mi[i].type == ERTS_MON_TYPE_PORT);
+ ASSERT(is_internal_pid(mic.mi[i].entity.term));
+ t = TUPLE2(*hpp, am_process, mic.mi[i].entity.term);
*hpp += 3;
res = CONS(*hpp, t, res);
*hpp += 2;
@@ -2958,15 +3188,19 @@ erts_bld_port_info(Eterm **hpp, ErlOffHeap *ohp, Uint *szp, Port *prt,
Eterm item;
INIT_MONITOR_INFOS(mic);
- erts_doforall_monitors(ERTS_P_MONITORS(prt),
- &collect_one_target_monitor, &mic);
+ erts_monitor_list_foreach(ERTS_P_LT_MONITORS(prt),
+ collect_one_target_monitor,
+ (void *) &mic);
+ erts_monitor_tree_foreach(ERTS_P_MONITORS(prt),
+ collect_one_target_monitor,
+ (void *) &mic);
if (szp)
*szp += mic.sz;
if (hpp) {
res = NIL;
for (i = 0; i < mic.mi_i; ++i) {
- ASSERT(mic.mi[i].node == NIL);
+ ASSERT(mic.mi[i].type != ERTS_MON_TYPE_RESOURCE);
item = STORE_NC(hpp, ohp, mic.mi[i].entity.term);
res = CONS(*hpp, item, res);
*hpp += 2;
@@ -3043,7 +3277,12 @@ erts_bld_port_info(Eterm **hpp, ErlOffHeap *ohp, Uint *szp, Port *prt,
*/
Uint size = 0;
- erts_doforall_links(ERTS_P_LINKS(prt), &erts_one_link_size, &size);
+ erts_link_tree_foreach(ERTS_P_LINKS(prt),
+ link_size, (void *) &size);
+ erts_monitor_tree_foreach(ERTS_P_MONITORS(prt),
+ monitor_size, (void *) &size);
+ erts_monitor_list_foreach(ERTS_P_LT_MONITORS(prt),
+ monitor_size, (void *) &size);
size += erts_port_data_size(prt);
@@ -3265,35 +3504,53 @@ fun_info_mfa_1(BIF_ALIST_1)
BIF_ERROR(p, BADARG);
}
+BIF_RETTYPE erts_internal_is_process_alive_2(BIF_ALIST_2)
+{
+ if (!is_internal_pid(BIF_ARG_1) || !is_internal_ordinary_ref(BIF_ARG_2))
+ BIF_ERROR(BIF_P, BADARG);
+ erts_proc_sig_send_is_alive_request(BIF_P, BIF_ARG_1, BIF_ARG_2);
+ BIF_RET(am_ok);
+}
+
BIF_RETTYPE is_process_alive_1(BIF_ALIST_1)
{
- if(is_internal_pid(BIF_ARG_1)) {
- Process *rp;
-
- if (BIF_ARG_1 == BIF_P->common.id)
- BIF_RET(am_true);
-
- rp = erts_proc_lookup_raw(BIF_ARG_1);
- if (!rp) {
- BIF_RET(am_false);
- }
- else {
- if (erts_atomic32_read_acqb(&rp->state)
- & (ERTS_PSFLG_PENDING_EXIT|ERTS_PSFLG_EXITING))
- ERTS_BIF_AWAIT_X_DATA_TRAP(BIF_P, BIF_ARG_1, am_false);
- else
- BIF_RET(am_true);
- }
- }
- else if(is_external_pid(BIF_ARG_1)) {
- if(external_pid_dist_entry(BIF_ARG_1) == erts_this_dist_entry)
+ if (is_internal_pid(BIF_ARG_1)) {
+ erts_aint32_t state;
+ Process *rp;
+
+ if (BIF_ARG_1 == BIF_P->common.id)
+ BIF_RET(am_true);
+
+ rp = erts_proc_lookup_raw(BIF_ARG_1);
+ if (!rp)
+ BIF_RET(am_false);
+
+ state = erts_atomic32_read_acqb(&rp->state);
+ if (state & (ERTS_PSFLG_EXITING
+ | ERTS_PSFLG_SIG_Q
+ | ERTS_PSFLG_SIG_IN_Q)) {
+ /*
+ * If in exiting state, trap out and send 'is alive'
+ * request and wait for it to complete termination.
+ *
+ * If process has signals enqueued, we need to
+ * send it an 'is alive' request via its signal
+ * queue in order to ensure that signal order is
+ * preserved (we may earlier have sent it an
+ * exit signal that has not been processed yet).
+ */
+ BIF_TRAP1(is_process_alive_trap, BIF_P, BIF_ARG_1);
+ }
+
+ BIF_RET(am_true);
+ }
+
+ if (is_external_pid(BIF_ARG_1)) {
+ if (external_pid_dist_entry(BIF_ARG_1) == erts_this_dist_entry)
BIF_RET(am_false); /* A pid from an old incarnation of this node */
- else
- BIF_ERROR(BIF_P, BADARG);
- }
- else {
- BIF_ERROR(BIF_P, BADARG);
}
+
+ BIF_ERROR(BIF_P, BADARG);
}
BIF_RETTYPE process_display_2(BIF_ALIST_2)
@@ -3311,16 +3568,6 @@ BIF_RETTYPE process_display_2(BIF_ALIST_2)
if (rp == ERTS_PROC_LOCK_BUSY)
ERTS_BIF_YIELD2(bif_export[BIF_process_display_2], BIF_P,
BIF_ARG_1, BIF_ARG_2);
- if (rp != BIF_P && ERTS_PROC_PENDING_EXIT(rp)) {
- Eterm args[2] = {BIF_ARG_1, BIF_ARG_2};
- erts_proc_unlock(rp, ERTS_PROC_LOCKS_ALL);
- ERTS_BIF_AWAIT_X_APPLY_TRAP(BIF_P,
- BIF_ARG_1,
- am_erlang,
- am_process_display,
- args,
- 2);
- }
erts_stack_dump(ERTS_PRINT_STDERR, NULL, rp);
erts_proc_unlock(rp, (BIF_P == rp
? ERTS_PROC_LOCKS_ALL_MINOR
@@ -3693,22 +3940,59 @@ BIF_RETTYPE erts_debug_get_internal_state_1(BIF_ALIST_1)
BIF_RET(erts_process_status(NULL, tp[2]));
}
}
+ else if (ERTS_IS_ATOM_STR("connection_id", tp[1])) {
+ DistEntry *dep;
+ Eterm *hp, res;
+ Uint con_id, hsz = 0;
+ if (!is_atom(tp[2]))
+ BIF_ERROR(BIF_P, BADARG);
+ dep = erts_sysname_to_connected_dist_entry(tp[2]);
+ if (!dep)
+ BIF_ERROR(BIF_P, BADARG);
+ erts_de_rlock(dep);
+ con_id = (Uint) dep->connection_id;
+ erts_de_runlock(dep);
+ (void) erts_bld_uint(NULL, &hsz, con_id);
+ hp = hsz ? HAlloc(BIF_P, hsz) : NULL;
+ res = erts_bld_uint(&hp, NULL, con_id);
+ BIF_RET(res);
+ }
else if (ERTS_IS_ATOM_STR("link_list", tp[1])) {
/* Used by erl_link_SUITE (emulator) */
if(is_internal_pid(tp[2])) {
+ erts_aint32_t state;
Eterm res;
Process *p;
+ int sigs_done, local_only;
p = erts_pid2proc(BIF_P,
ERTS_PROC_LOCK_MAIN,
tp[2],
- ERTS_PROC_LOCK_LINK);
+ ERTS_PROC_LOCK_MAIN);
if (!p) {
ERTS_ASSERT_IS_NOT_EXITING(BIF_P);
BIF_RET(am_undefined);
}
- res = make_link_list(BIF_P, ERTS_P_LINKS(p), NIL);
- erts_proc_unlock(p, ERTS_PROC_LOCK_LINK);
+
+ local_only = 0;
+ do {
+ int reds = CONTEXT_REDS;
+ sigs_done = erts_proc_sig_handle_incoming(p,
+ &state,
+ &reds,
+ CONTEXT_REDS,
+ local_only);
+ local_only = !0;
+ } while (!sigs_done && !(state & ERTS_PSFLG_EXITING));
+
+ if (!(state & ERTS_PSFLG_EXITING))
+ res = make_link_list(BIF_P, 1, ERTS_P_LINKS(p), NIL);
+ else if (BIF_P == p)
+ ERTS_BIF_EXITED(BIF_P);
+ else
+ res = am_undefined;
+ if (BIF_P != p)
+ erts_proc_unlock(p, ERTS_PROC_LOCK_MAIN);
BIF_RET(res);
}
else if(is_internal_port(tp[2])) {
@@ -3719,19 +4003,20 @@ BIF_RETTYPE erts_debug_get_internal_state_1(BIF_ALIST_1)
ERTS_PORT_SFLGS_INVALID_LOOKUP);
if(!p)
BIF_RET(am_undefined);
- res = make_link_list(BIF_P, ERTS_P_LINKS(p), NIL);
+ res = make_link_list(BIF_P, 1, ERTS_P_LINKS(p), NIL);
erts_port_release(p);
BIF_RET(res);
}
else if(is_node_name_atom(tp[2])) {
DistEntry *dep = erts_find_dist_entry(tp[2]);
if(dep) {
- Eterm subres;
- erts_de_links_lock(dep);
- subres = make_link_list(BIF_P, dep->nlinks, NIL);
- subres = make_link_list(BIF_P, dep->node_links, subres);
- erts_de_links_unlock(dep);
- BIF_RET(subres);
+ Eterm res = NIL;
+ if (dep->mld) {
+ erts_mtx_lock(&dep->mld->mtx);
+ res = make_link_list(BIF_P, 0, dep->mld->links, NIL);
+ erts_mtx_unlock(&dep->mld->mtx);
+ }
+ BIF_RET(res);
} else {
BIF_RET(am_undefined);
}
@@ -3740,27 +4025,54 @@ BIF_RETTYPE erts_debug_get_internal_state_1(BIF_ALIST_1)
else if (ERTS_IS_ATOM_STR("monitor_list", tp[1])) {
/* Used by erl_link_SUITE (emulator) */
if(is_internal_pid(tp[2])) {
+ erts_aint32_t state;
Process *p;
Eterm res;
+ int sigs_done, local_only;
p = erts_pid2proc(BIF_P,
ERTS_PROC_LOCK_MAIN,
tp[2],
- ERTS_PROC_LOCK_LINK);
+ ERTS_PROC_LOCK_MAIN);
if (!p) {
ERTS_ASSERT_IS_NOT_EXITING(BIF_P);
BIF_RET(am_undefined);
}
- res = make_monitor_list(BIF_P, ERTS_P_MONITORS(p));
- erts_proc_unlock(p, ERTS_PROC_LOCK_LINK);
+
+ local_only = 0;
+ do {
+ int reds = CONTEXT_REDS;
+ sigs_done = erts_proc_sig_handle_incoming(p,
+ &state,
+ &reds,
+ CONTEXT_REDS,
+ local_only);
+ local_only = !0;
+ } while (!sigs_done && !(state & ERTS_PSFLG_EXITING));
+
+ if (!(state & ERTS_PSFLG_EXITING)) {
+ res = make_monitor_list(BIF_P, 1, ERTS_P_MONITORS(p), NIL);
+ res = make_monitor_list(BIF_P, 0, ERTS_P_LT_MONITORS(p), res);
+ }
+ else {
+ if (BIF_P == p)
+ ERTS_BIF_EXITED(BIF_P);
+ else
+ res = am_undefined;
+ }
+ if (BIF_P != p)
+ erts_proc_unlock(p, ERTS_PROC_LOCK_MAIN);
BIF_RET(res);
} else if(is_node_name_atom(tp[2])) {
DistEntry *dep = erts_find_dist_entry(tp[2]);
if(dep) {
- Eterm ml;
- erts_de_links_lock(dep);
- ml = make_monitor_list(BIF_P, dep->monitors);
- erts_de_links_unlock(dep);
+ Eterm ml = NIL;
+ if (dep->mld) {
+ erts_mtx_lock(&dep->mld->mtx);
+ ml = make_monitor_list(BIF_P, 1, dep->mld->orig_name_monitors, NIL);
+ ml = make_monitor_list(BIF_P, 0, dep->mld->monitors, ml);
+ erts_mtx_unlock(&dep->mld->mtx);
+ }
BIF_RET(ml);
} else {
BIF_RET(am_undefined);
@@ -3778,18 +4090,6 @@ BIF_RETTYPE erts_debug_get_internal_state_1(BIF_ALIST_1)
}
BIF_RET(res);
}
- else if (ERTS_IS_ATOM_STR("have_pending_exit", tp[1])) {
- Process *rp = erts_pid2proc(BIF_P, ERTS_PROC_LOCK_MAIN,
- tp[2], ERTS_PROC_LOCK_STATUS);
- if (!rp) {
- BIF_RET(am_undefined);
- }
- else {
- Eterm res = ERTS_PROC_PENDING_EXIT(rp) ? am_true : am_false;
- erts_proc_unlock(rp, ERTS_PROC_LOCK_STATUS);
- BIF_RET(res);
- }
- }
else if (ERTS_IS_ATOM_STR("binary_info", tp[1])) {
Eterm bin = tp[2];
if (is_binary(bin)) {
@@ -3883,7 +4183,7 @@ BIF_RETTYPE erts_debug_get_internal_state_1(BIF_ALIST_1)
while (ix >= atom_table_size()) {
char tmp[20];
erts_snprintf(tmp, sizeof(tmp), "am%x", atom_table_size());
- erts_atom_put((byte *) tmp, strlen(tmp), ERTS_ATOM_ENC_LATIN1, 1);
+ erts_atom_put((byte *) tmp, sys_strlen(tmp), ERTS_ATOM_ENC_LATIN1, 1);
}
return make_atom(ix);
}
@@ -4116,57 +4416,6 @@ BIF_RETTYPE erts_debug_set_internal_state_2(BIF_ALIST_2)
erts_set_gc_state(BIF_P, enable);
BIF_RET(res);
}
- else if (ERTS_IS_ATOM_STR("send_fake_exit_signal", BIF_ARG_1)) {
- /* Used by signal_SUITE (emulator) */
-
- /* Testcases depend on the exit being received via
- a pending exit when the receiver is the same as
- the caller. */
- if (is_tuple(BIF_ARG_2)) {
- Eterm* tp = tuple_val(BIF_ARG_2);
- if (arityval(tp[0]) == 3
- && (is_pid(tp[1]) || is_port(tp[1]))
- && is_internal_pid(tp[2])) {
- int xres;
- ErtsProcLocks rp_locks = ERTS_PROC_LOCKS_XSIG_SEND;
- Process *rp = erts_pid2proc(BIF_P, ERTS_PROC_LOCK_MAIN,
- tp[2], rp_locks);
- if (!rp) {
- DECL_AM(dead);
- BIF_RET(AM_dead);
- }
-
- if (BIF_P == rp)
- rp_locks |= ERTS_PROC_LOCK_MAIN;
- xres = erts_send_exit_signal(NULL, /* NULL in order to
- force a pending exit
- when we send to our
- selves. */
- tp[1],
- rp,
- &rp_locks,
- tp[3],
- NIL,
- NULL,
- 0);
- if (BIF_P == rp)
- rp_locks &= ~ERTS_PROC_LOCK_MAIN;
- erts_proc_unlock(rp, rp_locks);
- if (xres > 1) {
- DECL_AM(message);
- BIF_RET(AM_message);
- }
- else if (xres == 0) {
- DECL_AM(unaffected);
- BIF_RET(AM_unaffected);
- }
- else {
- DECL_AM(exit);
- BIF_RET(AM_exit);
- }
- }
- }
- }
else if (ERTS_IS_ATOM_STR("colliding_names", BIF_ARG_1)) {
/* Used by ets_SUITE (stdlib) */
if (is_tuple(BIF_ARG_2)) {
@@ -4449,7 +4698,7 @@ static Eterm lcnt_build_lock_stats_term(Eterm **hpp, Uint *szp, erts_lcnt_lock_s
file = stats->file ? stats->file : "undefined";
- af = erts_atom_put((byte *)file, strlen(file), ERTS_ATOM_ENC_LATIN1, 1);
+ af = erts_atom_put((byte *)file, sys_strlen(file), ERTS_ATOM_ENC_LATIN1, 1);
uil = erts_bld_uint( hpp, szp, stats->line);
tloc = erts_bld_tuple(hpp, szp, 2, af, uil);
@@ -4495,7 +4744,7 @@ static Eterm lcnt_pretty_print_lock_id(erts_lcnt_lock_info_t *info) {
} else if(info->flags & ERTS_LOCK_FLAGS_CATEGORY_ALLOCATOR) {
if(is_small(id) && !sys_strcmp(info->name, "alcu_allocator")) {
const char *name = (const char*)ERTS_ALC_A2AD(signed_val(id));
- id = erts_atom_put((byte*)name, strlen(name), ERTS_ATOM_ENC_LATIN1, 1);
+ id = erts_atom_put((byte*)name, sys_strlen(name), ERTS_ATOM_ENC_LATIN1, 1);
}
}
@@ -4515,8 +4764,8 @@ static Eterm lcnt_build_lock_term(Eterm **hpp, Uint *szp, lcnt_sample_t *sample,
lock_desc = erts_lock_flags_get_type_name(info->flags);
- type = erts_atom_put((byte*)lock_desc, strlen(lock_desc), ERTS_ATOM_ENC_LATIN1, 1);
- name = erts_atom_put((byte*)info->name, strlen(info->name), ERTS_ATOM_ENC_LATIN1, 1);
+ type = erts_atom_put((byte*)lock_desc, sys_strlen(lock_desc), ERTS_ATOM_ENC_LATIN1, 1);
+ name = erts_atom_put((byte*)info->name, sys_strlen(info->name), ERTS_ATOM_ENC_LATIN1, 1);
/* Only attempt to resolve ids when actually emitting the term. This ought
* to be safe since all immediates are the same size. */
@@ -4552,11 +4801,11 @@ static Eterm lcnt_build_result_term(Eterm **hpp, Uint *szp, erts_lcnt_time_t *du
dtns = bld_unstable_uint64(hpp, szp, duration->ns);
tdt = erts_bld_tuple(hpp, szp, 2, dts, dtns);
- adur = erts_atom_put((byte *)str_duration, strlen(str_duration), ERTS_ATOM_ENC_LATIN1, 1);
+ adur = erts_atom_put((byte *)str_duration, sys_strlen(str_duration), ERTS_ATOM_ENC_LATIN1, 1);
tdur = erts_bld_tuple(hpp, szp, 2, adur, tdt);
/* lock tuple */
- aloc = erts_atom_put((byte *)str_locks, strlen(str_locks), ERTS_ATOM_ENC_LATIN1, 1);
+ aloc = erts_atom_put((byte *)str_locks, sys_strlen(str_locks), ERTS_ATOM_ENC_LATIN1, 1);
for(i = 0; i < current_locks->size; i++) {
lloc = lcnt_build_lock_term(hpp, szp, &current_locks->elements[i], lloc);
@@ -4610,7 +4859,7 @@ static Eterm lcnt_build_category_list(Eterm **hpp, Uint *szp, erts_lock_flags_t
for(i = 0; lcnt_category_map[i].name != NULL; i++) {
if(mask & lcnt_category_map[i].flag) {
Eterm category = erts_atom_put((byte*)lcnt_category_map[i].name,
- strlen(lcnt_category_map[i].name),
+ sys_strlen(lcnt_category_map[i].name),
ERTS_ATOM_ENC_UTF8, 0);
res = erts_bld_cons(hpp, szp, category, res);
@@ -4740,14 +4989,14 @@ BIF_RETTYPE erts_debug_lcnt_control_2(BIF_ALIST_2)
static void os_info_init(void)
{
- Eterm type = erts_atom_put((byte *) os_type, strlen(os_type), ERTS_ATOM_ENC_LATIN1, 1);
+ Eterm type = erts_atom_put((byte *) os_type, sys_strlen(os_type), ERTS_ATOM_ENC_LATIN1, 1);
Eterm flav;
int major, minor, build;
char* buf = erts_alloc(ERTS_ALC_T_TMP, 1024); /* More than enough */
Eterm* hp;
os_flavor(buf, 1024);
- flav = erts_atom_put((byte *) buf, strlen(buf), ERTS_ATOM_ENC_LATIN1, 1);
+ flav = erts_atom_put((byte *) buf, sys_strlen(buf), ERTS_ATOM_ENC_LATIN1, 1);
erts_free(ERTS_ALC_T_TMP, (void *) buf);
hp = erts_alloc(ERTS_ALC_T_LITERAL, (3+4)*sizeof(Eterm));
os_type_tuple = TUPLE2(hp, type, flav);
@@ -4780,6 +5029,10 @@ erts_bif_info_init(void)
= erts_export_put(am_erts_internal, am_gather_microstate_accounting_result, 2);
gather_system_check_res_trap
= erts_export_put(am_erts_internal, am_gather_system_check_result, 1);
+
+ is_process_alive_trap = erts_export_put(am_erts_internal, am_is_process_alive, 1);
+
+
process_info_init();
os_info_init();
}
diff --git a/erts/emulator/beam/erl_bif_port.c b/erts/emulator/beam/erl_bif_port.c
index 9f0c90ff7b..7fe4e02782 100644
--- a/erts/emulator/beam/erl_bif_port.c
+++ b/erts/emulator/beam/erl_bif_port.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2001-2017. All Rights Reserved.
+ * Copyright Ericsson AB 2001-2018. 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.
@@ -43,6 +43,7 @@
#include "erl_bits.h"
#include "erl_bif_unique.h"
#include "dtrace-wrapper.h"
+#include "erl_proc_sig_queue.h"
static Port *open_port(Process* p, Eterm name, Eterm settings, int *err_typep, int *err_nump);
static int merge_global_environment(erts_osenv_t *env, Eterm key_value_pairs);
@@ -53,10 +54,13 @@ char *erts_default_arg0 = "default";
BIF_RETTYPE erts_internal_open_port_2(BIF_ALIST_2)
{
+ BIF_RETTYPE ret;
Port *port;
Eterm res;
char *str;
int err_type, err_num;
+ ErtsLinkData *ldp;
+ ErtsLink *lnk;
port = open_port(BIF_P, BIF_ARG_1, BIF_ARG_2, &err_type, &err_num);
if (!port) {
@@ -71,13 +75,23 @@ BIF_RETTYPE erts_internal_open_port_2(BIF_ALIST_2)
BIF_ERROR(BIF_P, EXC_INTERNAL_ERROR);
} else if (err_type == -2) {
str = erl_errno_id(err_num);
- res = erts_atom_put((byte *) str, strlen(str), ERTS_ATOM_ENC_LATIN1, 1);
+ res = erts_atom_put((byte *) str, sys_strlen(str), ERTS_ATOM_ENC_LATIN1, 1);
} else {
res = am_einval;
}
BIF_RET(res);
}
+ ldp = erts_link_create(ERTS_LNK_TYPE_PORT, BIF_P->common.id, port->common.id);
+ ASSERT(ldp->a.other.item == port->common.id);
+ ASSERT(ldp->b.other.item == BIF_P->common.id);
+ /*
+ * This link should not already be present, but can potentially
+ * due to id wrapping...
+ */
+ lnk = erts_link_tree_lookup_insert(&ERTS_P_LINKS(BIF_P), &ldp->a);
+ erts_link_tree_insert(&ERTS_P_LINKS(port), &ldp->b);
+
if (port->drv_ptr->flags & ERL_DRV_FLAG_USE_INIT_ACK) {
/* Copied from erl_port_task.c */
@@ -86,39 +100,30 @@ BIF_RETTYPE erts_internal_open_port_2(BIF_ALIST_2)
erts_make_ref_in_array(port->async_open_port->ref);
port->async_open_port->to = BIF_P->common.id;
- erts_proc_lock(BIF_P, ERTS_PROC_LOCKS_MSG_RECEIVE | ERTS_PROC_LOCK_LINK);
- if (ERTS_PROC_PENDING_EXIT(BIF_P)) {
- /* need to exit caller instead */
- erts_proc_unlock(BIF_P, ERTS_PROC_LOCKS_MSG_RECEIVE | ERTS_PROC_LOCK_LINK);
- KILL_CATCHES(BIF_P);
- BIF_P->freason = EXC_EXIT;
- erts_port_release(port);
- BIF_RET(am_badarg);
- }
-
- ERTS_MSGQ_MV_INQ2PRIVQ(BIF_P);
- BIF_P->msg.save = BIF_P->msg.last;
-
- erts_proc_unlock(BIF_P, ERTS_PROC_LOCKS_MSG_RECEIVE);
+ /*
+ * We unconditionaly *must* do a receive on a message
+ * containing the reference after this...
+ */
+ ERTS_RECV_MARK_SAVE(BIF_P);
+ ERTS_RECV_MARK_SET(BIF_P);
res = erts_proc_store_ref(BIF_P, port->async_open_port->ref);
} else {
res = port->common.id;
- erts_proc_lock(BIF_P, ERTS_PROC_LOCK_LINK);
}
- erts_add_link(&ERTS_P_LINKS(port), LINK_PID, BIF_P->common.id);
- erts_add_link(&ERTS_P_LINKS(BIF_P), LINK_PID, port->common.id);
-
if (IS_TRACED_FL(BIF_P, F_TRACE_PROCS))
- trace_proc(BIF_P, ERTS_PROC_LOCK_MAIN|ERTS_PROC_LOCK_LINK, BIF_P,
+ trace_proc(BIF_P, ERTS_PROC_LOCK_MAIN, BIF_P,
am_link, port->common.id);
- erts_proc_unlock(BIF_P, ERTS_PROC_LOCK_LINK);
+ ERTS_BIF_PREP_RET(ret, res);
erts_port_release(port);
- BIF_RET(res);
+ if (lnk)
+ erts_link_release(lnk);
+
+ return ret;
}
static ERTS_INLINE Port *
@@ -195,7 +200,6 @@ BIF_RETTYPE erts_internal_port_command_3(BIF_ALIST_3)
#endif
switch (erts_port_output(BIF_P, flags, prt, prt->common.id, BIF_ARG_2, &ref)) {
- case ERTS_PORT_OP_CALLER_EXIT:
case ERTS_PORT_OP_BADARG:
case ERTS_PORT_OP_DROPPED:
ERTS_BIF_PREP_RET(res, am_badarg);
@@ -254,7 +258,6 @@ BIF_RETTYPE erts_internal_port_call_3(BIF_ALIST_3)
op = (unsigned int) uint_op;
switch (erts_port_call(BIF_P, prt, op, BIF_ARG_3, &retval)) {
- case ERTS_PORT_OP_CALLER_EXIT:
case ERTS_PORT_OP_DROPPED:
case ERTS_PORT_OP_BADARG:
retval = am_badarg;
@@ -272,11 +275,8 @@ BIF_RETTYPE erts_internal_port_call_3(BIF_ALIST_3)
}
state = erts_atomic32_read_acqb(&BIF_P->state);
- if (state & (ERTS_PSFLG_EXITING|ERTS_PSFLG_PENDING_EXIT)) {
- if (state & ERTS_PSFLG_PENDING_EXIT)
- erts_handle_pending_exit(BIF_P, ERTS_PROC_LOCK_MAIN);
+ if (state & ERTS_PSFLG_EXITING)
ERTS_BIF_EXITED(BIF_P);
- }
BIF_RET(retval);
}
@@ -302,7 +302,6 @@ BIF_RETTYPE erts_internal_port_control_3(BIF_ALIST_3)
op = (unsigned int) uint_op;
switch (erts_port_control(BIF_P, prt, op, BIF_ARG_3, &retval)) {
- case ERTS_PORT_OP_CALLER_EXIT:
case ERTS_PORT_OP_BADARG:
case ERTS_PORT_OP_DROPPED:
retval = am_badarg;
@@ -320,11 +319,8 @@ BIF_RETTYPE erts_internal_port_control_3(BIF_ALIST_3)
}
state = erts_atomic32_read_acqb(&BIF_P->state);
- if (state & (ERTS_PSFLG_EXITING|ERTS_PSFLG_PENDING_EXIT)) {
- if (state & ERTS_PSFLG_PENDING_EXIT)
- erts_handle_pending_exit(BIF_P, ERTS_PROC_LOCK_MAIN);
+ if (state & ERTS_PSFLG_EXITING)
ERTS_BIF_EXITED(BIF_P);
- }
BIF_RET(retval);
}
@@ -347,7 +343,6 @@ BIF_RETTYPE erts_internal_port_close_1(BIF_ALIST_1)
BIF_RET(am_badarg);
switch (erts_port_exit(BIF_P, 0, prt, BIF_P->common.id, am_normal, &ref)) {
- case ERTS_PORT_OP_CALLER_EXIT:
case ERTS_PORT_OP_BADARG:
case ERTS_PORT_OP_DROPPED:
BIF_RET(am_badarg);
@@ -380,7 +375,6 @@ BIF_RETTYPE erts_internal_port_connect_2(BIF_ALIST_2)
#endif
switch (erts_port_connect(BIF_P, 0, prt, BIF_P->common.id, BIF_ARG_2, &ref)) {
- case ERTS_PORT_OP_CALLER_EXIT:
case ERTS_PORT_OP_BADARG:
case ERTS_PORT_OP_DROPPED:
BIF_RET(am_badarg);
@@ -418,7 +412,6 @@ BIF_RETTYPE erts_internal_port_info_1(BIF_ALIST_1)
}
switch (erts_port_info(BIF_P, prt, THE_NON_VALUE, &retval)) {
- case ERTS_PORT_OP_CALLER_EXIT:
case ERTS_PORT_OP_BADARG:
BIF_RET(am_badarg);
case ERTS_PORT_OP_DROPPED:
@@ -457,7 +450,6 @@ BIF_RETTYPE erts_internal_port_info_2(BIF_ALIST_2)
}
switch (erts_port_info(BIF_P, prt, BIF_ARG_2, &retval)) {
- case ERTS_PORT_OP_CALLER_EXIT:
case ERTS_PORT_OP_BADARG:
BIF_RET(am_badarg);
case ERTS_PORT_OP_DROPPED:
@@ -639,6 +631,27 @@ BIF_RETTYPE port_get_data_1(BIF_ALIST_1)
BIF_RET(res);
}
+Eterm erts_port_data_read(Port* prt)
+{
+ Eterm res;
+ erts_aint_t data;
+
+ data = erts_atomic_read_ddrb(&prt->data);
+ if (data == (erts_aint_t)NULL)
+ return am_undefined; /* Port terminated by racing thread */
+
+ if ((data & 0x3) != 0) {
+ res = (Eterm) (UWord) data;
+ ASSERT(is_immed(res));
+ }
+ else {
+ ErtsPortDataHeap *pdhp = (ErtsPortDataHeap *) data;
+ res = pdhp->data;
+ }
+ return res;
+}
+
+
/*
* Open a port. Most of the work is not done here but rather in
* the file io.c.
@@ -1124,7 +1137,7 @@ http_bld_string(struct packet_callback_args* pca, Uint **hpp, Uint *szp,
ErlHeapBin* bin = (ErlHeapBin*) *hpp;
bin->thing_word = header_heap_bin(len);
bin->size = len;
- memcpy(bin->data, str, len);
+ sys_memcpy(bin->data, str, len);
*hpp += size;
}
}
@@ -1302,9 +1315,9 @@ int ssl_tls_erl(void* arg, unsigned type, unsigned major, unsigned minor,
Eterm bin = new_binary(pca->p, NULL, plen+len);
byte* bin_ptr = binary_bytes(bin);
- memcpy(bin_ptr+plen, buf, len);
+ sys_memcpy(bin_ptr+plen, buf, len);
if (plen) {
- memcpy(bin_ptr, prefix, plen);
+ sys_memcpy(bin_ptr, prefix, plen);
}
/* {ssl_tls,NIL,ContentType,{Major,Minor},Bin} */
diff --git a/erts/emulator/beam/erl_bif_re.c b/erts/emulator/beam/erl_bif_re.c
index bc819505e7..4d769c2d46 100644
--- a/erts/emulator/beam/erl_bif_re.c
+++ b/erts/emulator/beam/erl_bif_re.c
@@ -514,7 +514,7 @@ re_version_0(BIF_ALIST_0)
Eterm ret;
size_t version_size = 0;
byte *version = (byte *) erts_pcre_version();
- version_size = strlen((const char *) version);
+ version_size = sys_strlen((const char *) version);
ret = new_binary(BIF_P, version, version_size);
BIF_RET(ret);
}
@@ -998,7 +998,7 @@ build_capture(Eterm capture_spec[CAPSPEC_SIZE], const pcre *code)
has_dupnames = ((options & PCRE_DUPNAMES) != 0);
for(i=0;i<top;++i) {
- if (last == NULL || !has_dupnames || strcmp((char *) last+2,(char *) nametable+2)) {
+ if (last == NULL || !has_dupnames || sys_strcmp((char *) last+2,(char *) nametable+2)) {
ASSERT(ri->num_spec >= 0);
++(ri->num_spec);
if(ri->num_spec > sallocated) {
@@ -1048,7 +1048,7 @@ build_capture(Eterm capture_spec[CAPSPEC_SIZE], const pcre *code)
(tmpbsiz = ap->len + 1));
}
}
- memcpy(tmpb,ap->name,ap->len);
+ sys_memcpy(tmpb,ap->name,ap->len);
tmpb[ap->len] = '\0';
} else {
ErlDrvSizeT slen;
@@ -1210,7 +1210,7 @@ re_run(Process *p, Eterm arg1, Eterm arg2, Eterm arg3)
erts_pcre_fullinfo(result, NULL, PCRE_INFO_CAPTURECOUNT, &capture_count);
ovsize = 3*(capture_count+1);
restart.code = erts_alloc(ERTS_ALC_T_RE_SUBJECT, code_size);
- memcpy(restart.code, result, code_size);
+ sys_memcpy(restart.code, result, code_size);
erts_pcre_free(result);
erts_free(ERTS_ALC_T_RE_TMP_BUF, expr);
/*unicode = (pflags & PARSE_FLAG_UNICODE) ? 1 : 0;*/
@@ -1252,7 +1252,7 @@ re_run(Process *p, Eterm arg1, Eterm arg2, Eterm arg3)
BIF_ERROR(p, BADARG);
}
restart.code = erts_alloc(ERTS_ALC_T_RE_SUBJECT, code_size);
- memcpy(restart.code, code_tmp, code_size);
+ sys_memcpy(restart.code, code_tmp, code_size);
erts_free_aligned_binary_bytes(temp_alloc);
}
@@ -1364,7 +1364,7 @@ handle_iolist:
RestartContext *restartp = ERTS_MAGIC_BIN_DATA(mbp);
Eterm magic_ref;
Eterm *hp;
- memcpy(restartp,&restart,sizeof(RestartContext));
+ sys_memcpy(restartp,&restart,sizeof(RestartContext));
BUMP_ALL_REDS(p);
hp = HAlloc(p, ERTS_MAGIC_REF_THING_SIZE);
magic_ref = erts_mk_magic_ref(&hp, &MSO(p), mbp);
@@ -1517,7 +1517,7 @@ re_inspect_2(BIF_ALIST_2)
last = NULL;
name = nametable;
for(i=0;i<top;++i) {
- if (last == NULL || !has_dupnames || strcmp((char *) last+2,
+ if (last == NULL || !has_dupnames || sys_strcmp((char *) last+2,
(char *) name+2)) {
++num_names;
}
@@ -1531,9 +1531,9 @@ re_inspect_2(BIF_ALIST_2)
name = nametable;
j = 0;
for(i=0;i<top;++i) {
- if (last == NULL || !has_dupnames || strcmp((char *) last+2,
+ if (last == NULL || !has_dupnames || sys_strcmp((char *) last+2,
(char *) name+2)) {
- tmp_vec[j++] = new_binary(BIF_P, (byte *) name+2, strlen((char *) name+2));
+ tmp_vec[j++] = new_binary(BIF_P, (byte *) name+2, sys_strlen((char *) name+2));
}
last = name;
name += entrysize;
diff --git a/erts/emulator/beam/erl_bif_trace.c b/erts/emulator/beam/erl_bif_trace.c
index 22942b40c4..a076f0bf54 100644
--- a/erts/emulator/beam/erl_bif_trace.c
+++ b/erts/emulator/beam/erl_bif_trace.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1999-2017. All Rights Reserved.
+ * Copyright Ericsson AB 1999-2018. 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.
@@ -40,6 +40,7 @@
#include "erl_binary.h"
#include "erl_thr_progress.h"
#include "erl_bif_unique.h"
+#include "erl_proc_sig_queue.h"
#define DECL_AM(S) Eterm AM_ ## S = am_atom_put(#S, sizeof(#S) - 1)
diff --git a/erts/emulator/beam/erl_bif_unique.h b/erts/emulator/beam/erl_bif_unique.h
index 9aa631fde9..6af9d4ac8c 100644
--- a/erts/emulator/beam/erl_bif_unique.h
+++ b/erts/emulator/beam/erl_bif_unique.h
@@ -307,7 +307,7 @@ erts_iref_storage_clean(ErtsIRefStorage *iref)
if (iref->is_magic && erts_refc_dectest(&iref->u.mb->intern.refc, 0) == 0)
erts_ref_bin_free(iref->u.mb);
#ifdef DEBUG
- memset((void *) iref, 0xf, sizeof(ErtsIRefStorage));
+ sys_memset((void *) iref, 0xf, sizeof(ErtsIRefStorage));
#endif
}
@@ -342,7 +342,7 @@ erts_iref_storage_make_ref(ErtsIRefStorage *iref,
#ifdef DEBUG
if (clean_storage)
- memset((void *) iref, 0xf, sizeof(ErtsIRefStorage));
+ sys_memset((void *) iref, 0xf, sizeof(ErtsIRefStorage));
#endif
return make_internal_ref(hp);
diff --git a/erts/emulator/beam/erl_bits.h b/erts/emulator/beam/erl_bits.h
index b9d141d585..a3816fa820 100644
--- a/erts/emulator/beam/erl_bits.h
+++ b/erts/emulator/beam/erl_bits.h
@@ -111,7 +111,7 @@ typedef struct erl_bin_match_struct{
#define copy_binary_to_buffer(DstBuffer, DstBufOffset, SrcBuffer, SrcBufferOffset, NumBits) \
do { \
if (BIT_OFFSET(DstBufOffset) == 0 && (SrcBufferOffset == 0) && \
- (BIT_OFFSET(NumBits)==0)) { \
+ (BIT_OFFSET(NumBits)==0) && (NumBits != 0)) { \
sys_memcpy(DstBuffer+BYTE_OFFSET(DstBufOffset), \
SrcBuffer, NBYTES(NumBits)); \
} else { \
diff --git a/erts/emulator/beam/erl_db.c b/erts/emulator/beam/erl_db.c
index 3ba0886464..a76d769283 100644
--- a/erts/emulator/beam/erl_db.c
+++ b/erts/emulator/beam/erl_db.c
@@ -1753,6 +1753,28 @@ BIF_RETTYPE ets_new_2(BIF_ALIST_2)
BIF_RET(ret);
}
+/*
+** Retrieves the tid() of a named ets table.
+*/
+BIF_RETTYPE ets_whereis_1(BIF_ALIST_1)
+{
+ DbTable* tb;
+ Eterm res;
+
+ if (is_not_atom(BIF_ARG_1)) {
+ BIF_ERROR(BIF_P, BADARG);
+ }
+
+ if ((tb = db_get_table(BIF_P, BIF_ARG_1, DB_INFO, LCK_READ)) == NULL) {
+ BIF_RET(am_undefined);
+ }
+
+ res = make_tid(BIF_P, tb);
+ db_unlock(tb, LCK_READ);
+
+ BIF_RET(res);
+}
+
/*
** The lookup BIF
*/
@@ -3126,7 +3148,8 @@ BIF_RETTYPE ets_info_1(BIF_ALIST_1)
static Eterm fields[] = {am_protection, am_keypos, am_type, am_named_table,
am_node, am_size, am_name, am_heir, am_owner, am_memory, am_compressed,
am_write_concurrency,
- am_read_concurrency};
+ am_read_concurrency,
+ am_id};
Eterm results[sizeof(fields)/sizeof(Eterm)];
DbTable* tb;
Eterm res;
@@ -4016,7 +4039,10 @@ static Eterm table_info(Process* p, DbTable* tb, Eterm What)
ret = is_table_named(tb) ? am_true : am_false;
} else if (What == am_compressed) {
ret = tb->common.compress ? am_true : am_false;
+ } else if (What == am_id) {
+ ret = make_tid(p, tb);
}
+
/*
* For debugging purposes
*/
@@ -4235,7 +4261,7 @@ erts_ets_colliding_names(Process* p, Eterm name, Uint cnt)
while (index >= atom_table_size()) {
char tmp[20];
erts_snprintf(tmp, sizeof(tmp), "am%x", atom_table_size());
- erts_atom_put((byte *) tmp, strlen(tmp), ERTS_ATOM_ENC_LATIN1, 1);
+ erts_atom_put((byte *) tmp, sys_strlen(tmp), ERTS_ATOM_ENC_LATIN1, 1);
}
list = CONS(hp, make_atom(index), list);
hp += 2;
diff --git a/erts/emulator/beam/erl_db_util.c b/erts/emulator/beam/erl_db_util.c
index e017b9552b..1e8e9e5e94 100644
--- a/erts/emulator/beam/erl_db_util.c
+++ b/erts/emulator/beam/erl_db_util.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1998-2017. All Rights Reserved.
+ * Copyright Ericsson AB 1998-2018. 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.
@@ -38,6 +38,7 @@
#include "erl_binary.h"
#include "erl_map.h"
#include "erl_thr_progress.h"
+#include "erl_proc_sig_queue.h"
#include "erl_db_util.h"
@@ -74,12 +75,35 @@ DBIF_TABLE_GUARD | DBIF_TABLE_BODY | DBIF_TRACE_GUARD | DBIF_TRACE_BODY
typedef struct DMC_STACK_TYPE(Type) { \
int pos; \
int siz; \
- Type def[DMC_DEFAULT_SIZE]; \
+ int bytes; \
Type *data; \
+ Type def[DMC_DEFAULT_SIZE]; \
} DMC_STACK_TYPE(Type)
+
+
+typedef int Dummy;
+DMC_DECLARE_STACK_TYPE(Dummy);
+
+static void dmc_stack_grow(DMC_Dummy_stack* s)
+{
+ int was_bytes = s->bytes;
+ s->siz *= 2;
+ s->bytes *= 2;
+ if (s->data == s->def) {
+ s->data = erts_alloc(ERTS_ALC_T_DB_MC_STK, s->bytes);
+ sys_memcpy(s->data, s->def, was_bytes);
+ }
+ else {
+ s->data = erts_realloc(ERTS_ALC_T_DB_MC_STK, s->data, s->bytes);
+ }
+}
-#define DMC_INIT_STACK(Name) \
- (Name).pos = 0; (Name).siz = DMC_DEFAULT_SIZE; (Name).data = (Name).def
+#define DMC_INIT_STACK(Name) do { \
+ (Name).pos = 0; \
+ (Name).siz = DMC_DEFAULT_SIZE; \
+ (Name).bytes = sizeof((Name).def); \
+ (Name).data = (Name).def; \
+} while (0)
#define DMC_STACK_DATA(Name) (Name).data
@@ -87,21 +111,19 @@ typedef struct DMC_STACK_TYPE(Type) { \
#define DMC_PUSH(On, What) \
do { \
- if ((On).pos >= (On).siz) { \
- (On).siz *= 2; \
- (On).data \
- = (((On).def == (On).data) \
- ? memcpy(erts_alloc(ERTS_ALC_T_DB_MC_STK, \
- (On).siz*sizeof(*((On).data))), \
- (On).def, \
- DMC_DEFAULT_SIZE*sizeof(*((On).data))) \
- : erts_realloc(ERTS_ALC_T_DB_MC_STK, \
- (void *) (On).data, \
- (On).siz*sizeof(*((On).data)))); \
- } \
+ if ((On).pos >= (On).siz) \
+ dmc_stack_grow((DMC_Dummy_stack*)&(On)); \
(On).data[(On).pos++] = What; \
} while (0)
+#define DMC_PUSH2(On, A, B) \
+do { \
+ if ((On).pos+1 >= (On).siz) \
+ dmc_stack_grow((DMC_Dummy_stack*)&(On)); \
+ (On).data[(On).pos++] = A; \
+ (On).data[(On).pos++] = B; \
+} while (0)
+
#define DMC_POP(From) (From).data[--(From).pos]
#define DMC_TOP(From) (From).data[(From).pos - 1]
@@ -155,6 +177,7 @@ set_tracee_flags(Process *tracee_p, ErtsTracer tracer,
: am_false);
erts_tracer_replace(&tracee_p->common, tracer);
ERTS_TRACE_FLAGS(tracee_p) = flags;
+
return ret;
}
/*
@@ -1518,7 +1541,7 @@ restart:
context.current_match < num_progs;
++context.current_match) { /* This loop is long,
too long */
- memset(heap.vars, 0, heap.size * sizeof(*heap.vars));
+ sys_memset(heap.vars, 0, heap.size * sizeof(*heap.vars));
t = context.matchexpr[context.current_match];
context.stack_used = 0;
structure_checked = 0;
@@ -1537,8 +1560,7 @@ restart:
if (is_flatmap(t)) {
num_iters = flatmap_get_size(flatmap_val(t));
if (!structure_checked) {
- DMC_PUSH(text, matchMap);
- DMC_PUSH(text, num_iters);
+ DMC_PUSH2(text, matchMap, num_iters);
}
structure_checked = 0;
for (i = 0; i < num_iters; ++i) {
@@ -1558,8 +1580,7 @@ restart:
}
goto error;
}
- DMC_PUSH(text, matchKey);
- DMC_PUSH(text, dmc_private_copy(&context, key));
+ DMC_PUSH2(text, matchKey, dmc_private_copy(&context, key));
{
int old_stack = ++(context.stack_used);
Eterm value = flatmap_get_values(flatmap_val(t))[i];
@@ -1587,8 +1608,7 @@ restart:
Eterm *kv;
num_iters = hashmap_size(t);
if (!structure_checked) {
- DMC_PUSH(text, matchMap);
- DMC_PUSH(text, num_iters);
+ DMC_PUSH2(text, matchMap, num_iters);
}
structure_checked = 0;
@@ -1614,8 +1634,7 @@ restart:
DESTROY_WSTACK(wstack);
goto error;
}
- DMC_PUSH(text, matchKey);
- DMC_PUSH(text, dmc_private_copy(&context, key));
+ DMC_PUSH2(text, matchKey, dmc_private_copy(&context, key));
{
int old_stack = ++(context.stack_used);
res = dmc_one_term(&context, &heap, &stack, &text,
@@ -1645,8 +1664,7 @@ restart:
num_iters = arityval(*tuple_val(t));
if (!structure_checked) { /* i.e. we did not
pop it */
- DMC_PUSH(text,matchTuple);
- DMC_PUSH(text,num_iters);
+ DMC_PUSH2(text, matchTuple, num_iters);
}
structure_checked = 0;
for (i = 1; i <= num_iters; ++i) {
@@ -2140,7 +2158,7 @@ restart:
case matchEqFloat:
if (!is_float(*ep))
FAIL();
- if (memcmp(float_val(*ep) + 1, pc, sizeof(double)))
+ if (sys_memcmp(float_val(*ep) + 1, pc, sizeof(double)))
FAIL();
pc += TermWords(2);
++ep;
@@ -2742,8 +2760,8 @@ Eterm db_format_dmc_err_info(Process *p, DMCErrInfo *ei)
if (vnum >= 0)
erts_snprintf(buff,sizeof(buff)+20,tmp->error_string, vnum);
else
- strcpy(buff,tmp->error_string);
- sl = strlen(buff);
+ sys_strcpy(buff,tmp->error_string);
+ sl = sys_strlen(buff);
shp = HAlloc(p, sl * 2 + 5);
sev = (tmp->severity == dmcWarning) ?
am_atom_put("warning",7) :
@@ -3535,20 +3553,17 @@ static DMCRet dmc_one_term(DMCContext *context,
return retRestart;
}
if (heap->vars[n].is_bound) {
- DMC_PUSH(*text,matchCmp);
- DMC_PUSH(*text,n);
+ DMC_PUSH2(*text, matchCmp, n);
} else { /* Not bound, bind! */
if (n >= heap->vars_used)
heap->vars_used = n + 1;
- DMC_PUSH(*text,matchBind);
- DMC_PUSH(*text,n);
+ DMC_PUSH2(*text, matchBind, n);
heap->vars[n].is_bound = 1;
}
} else if (c == am_Underscore) {
DMC_PUSH(*text, matchSkip);
} else { /* Any immediate value */
- DMC_PUSH(*text, matchEq);
- DMC_PUSH(*text, (Uint) c);
+ DMC_PUSH2(*text, matchEq, (Uint) c);
}
break;
case TAG_PRIMARY_LIST:
@@ -3561,9 +3576,8 @@ static DMCRet dmc_one_term(DMCContext *context,
switch ((hdr & _TAG_HEADER_MASK) >> _TAG_PRIMARY_SIZE) {
case (_TAG_HEADER_ARITYVAL >> _TAG_PRIMARY_SIZE):
n = arityval(*tuple_val(c));
- DMC_PUSH(*text, matchPushT);
+ DMC_PUSH2(*text, matchPushT, n);
++(context->stack_used);
- DMC_PUSH(*text, n);
DMC_PUSH(*stack, c);
break;
case (_TAG_HEADER_MAP >> _TAG_PRIMARY_SIZE):
@@ -3571,9 +3585,8 @@ static DMCRet dmc_one_term(DMCContext *context,
n = flatmap_get_size(flatmap_val(c));
else
n = hashmap_size(c);
- DMC_PUSH(*text, matchPushM);
+ DMC_PUSH2(*text, matchPushM, n);
++(context->stack_used);
- DMC_PUSH(*text, n);
DMC_PUSH(*stack, c);
break;
case (_TAG_HEADER_REF >> _TAG_PRIMARY_SIZE):
@@ -3598,8 +3611,7 @@ static DMCRet dmc_one_term(DMCContext *context,
break;
}
case (_TAG_HEADER_FLOAT >> _TAG_PRIMARY_SIZE):
- DMC_PUSH(*text,matchEqFloat);
- DMC_PUSH(*text, (Uint) float_val(c)[1]);
+ DMC_PUSH2(*text, matchEqFloat, (Uint) float_val(c)[1]);
#ifdef ARCH_64
DMC_PUSH(*text, (Uint) 0);
#else
@@ -3607,8 +3619,7 @@ static DMCRet dmc_one_term(DMCContext *context,
#endif
break;
default: /* BINARY, FUN, VECTOR, or EXTERNAL */
- DMC_PUSH(*text, matchEqBin);
- DMC_PUSH(*text, dmc_private_copy(context, c));
+ DMC_PUSH2(*text, matchEqBin, dmc_private_copy(context, c));
break;
}
break;
@@ -3658,8 +3669,7 @@ static void do_emit_constant(DMCContext *context, DMC_STACK_TYPE(UWord) *text,
emb->next = context->save;
context->save = emb;
}
- DMC_PUSH(*text,matchPushC);
- DMC_PUSH(*text,(Uint) tmp);
+ DMC_PUSH2(*text, matchPushC, (Uint)tmp);
if (++context->stack_used > context->stack_need)
context->stack_need = context->stack_used;
}
@@ -3797,8 +3807,7 @@ dmc_tuple(DMCContext *context, DMCHeap *heap, DMC_STACK_TYPE(UWord) *text,
*constant = 1;
return retOk;
}
- DMC_PUSH(*text, matchMkTuple);
- DMC_PUSH(*text, nelems);
+ DMC_PUSH2(*text, matchMkTuple, nelems);
context->stack_used -= (nelems - 1);
*constant = 0;
return retOk;
@@ -3825,13 +3834,11 @@ dmc_map(DMCContext *context, DMCHeap *heap, DMC_STACK_TYPE(UWord) *text,
*constant = 1;
return retOk;
}
- DMC_PUSH(*text, matchPushC);
- DMC_PUSH(*text, dmc_private_copy(context, m->keys));
+ DMC_PUSH2(*text, matchPushC, dmc_private_copy(context, m->keys));
if (++context->stack_used > context->stack_need) {
context->stack_need = context->stack_used;
}
- DMC_PUSH(*text, matchMkFlatMap);
- DMC_PUSH(*text, nelems);
+ DMC_PUSH2(*text, matchMkFlatMap, nelems);
context->stack_used -= nelems;
*constant = 0;
return retOk;
@@ -3883,8 +3890,7 @@ dmc_map(DMCContext *context, DMCHeap *heap, DMC_STACK_TYPE(UWord) *text,
do_emit_constant(context, text, CDR(kv));
}
}
- DMC_PUSH(*text, matchMkHashMap);
- DMC_PUSH(*text, nelems);
+ DMC_PUSH2(*text, matchMkHashMap, nelems);
context->stack_used -= nelems;
DESTROY_WSTACK(wstack);
return retOk;
@@ -5044,7 +5050,7 @@ static int match_compact(ErlHeapFragment *expr, DMCErrInfo *err_info)
ASSERT(j < x);
erts_snprintf(buff+1, sizeof(buff) - 1, "%u", (unsigned) j);
/* Yes, writing directly into terms, they ARE off heap */
- *p = erts_atom_put((byte *) buff, strlen(buff),
+ *p = erts_atom_put((byte *) buff, sys_strlen(buff),
ERTS_ATOM_ENC_LATIN1, 1);
}
++p;
@@ -5505,7 +5511,7 @@ void db_match_dis(Binary *bp)
++t;
{
double num;
- memcpy(&num,t,sizeof(double));
+ sys_memcpy(&num,t,sizeof(double));
t += TermWords(2);
erts_printf("EqFloat\t%f\n", num);
}
diff --git a/erts/emulator/beam/erl_debug.c b/erts/emulator/beam/erl_debug.c
index bf8244564a..db78378257 100644
--- a/erts/emulator/beam/erl_debug.c
+++ b/erts/emulator/beam/erl_debug.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1998-2017. All Rights Reserved.
+ * Copyright Ericsson AB 1998-2018. 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.
@@ -404,15 +404,16 @@ void verify_process(Process *p)
erts_exit(ERTS_ERROR_EXIT,"Wild pointer found in " name " of %T!\n",p->common.id); }
- ErtsMessage* mp = p->msg.first;
-
VERBOSE(DEBUG_MEMORY,("Verify process: %T...\n",p->common.id));
- while (mp != NULL) {
- VERIFY_ETERM("message term",ERL_MESSAGE_TERM(mp));
- VERIFY_ETERM("message token",ERL_MESSAGE_TOKEN(mp));
- mp = mp->next;
- }
+ ERTS_FOREACH_SIG_PRIVQS(
+ p, mp,
+ {
+ if (ERTS_SIG_IS_MSG(mp)) {
+ VERIFY_ETERM("message term",ERL_MESSAGE_TERM(mp));
+ VERIFY_ETERM("message token",ERL_MESSAGE_TOKEN(mp));
+ }
+ });
erts_check_stack(p);
erts_check_heap(p);
@@ -532,16 +533,15 @@ static void print_process_memory(Process *p)
erts_printf("-- %-*s ---%s-%s-%s-%s--\n",
PTR_SIZE, "PCB", dashes, dashes, dashes, dashes);
- if (p->msg.first != NULL) {
- ErtsMessage* mp;
- erts_printf(" Message Queue:\n");
- mp = p->msg.first;
- while (mp != NULL) {
- erts_printf("| 0x%0*lx | 0x%0*lx |\n",PTR_SIZE,
- ERL_MESSAGE_TERM(mp),PTR_SIZE,ERL_MESSAGE_TOKEN(mp));
- mp = mp->next;
- }
- }
+
+ erts_printf(" Message Queue:\n");
+ ERTS_FOREACH_SIG_PRIVQS(
+ p, mp,
+ {
+ if (ERTS_SIG_IS_MSG(mp))
+ erts_printf("| 0x%0*lx | 0x%0*lx |\n",PTR_SIZE,
+ ERL_MESSAGE_TERM(mp),PTR_SIZE,ERL_MESSAGE_TOKEN(mp));
+ });
if (p->dictionary != NULL) {
int n = ERTS_PD_SIZE(p->dictionary);
diff --git a/erts/emulator/beam/erl_drv_thread.c b/erts/emulator/beam/erl_drv_thread.c
index 71d4534ef9..4cf42fce57 100644
--- a/erts/emulator/beam/erl_drv_thread.c
+++ b/erts/emulator/beam/erl_drv_thread.c
@@ -439,7 +439,7 @@ erl_drv_tsd_key_create(char *name, ErlDrvTSDKey *key)
name_copy = no_name;
else {
name_copy = erts_alloc_fnf(ERTS_ALC_T_DRV_TSD,
- sizeof(char)*(strlen(name) + 1));
+ sizeof(char)*(sys_strlen(name) + 1));
if (!name_copy) {
*key = -1;
return ENOMEM;
@@ -750,7 +750,7 @@ erl_drv_steal_main_thread(char *name,
+ (name ? sys_strlen(name) + 1 : 0)));
if (!dtid)
return ENOMEM;
- memset(dtid,0,sizeof(ErlDrvTid_));
+ sys_memset(dtid,0,sizeof(ErlDrvTid_));
dtid->tid = (void * ) -1;
dtid->drv_thr = 1;
dtid->func = func;
@@ -763,8 +763,8 @@ erl_drv_steal_main_thread(char *name,
*tid = NULL;
/* Ignore options and name... */
- memcpy(buff,&func,sizeof(void* (*)(void*)));
- memcpy(buff + sizeof(void* (*)(void*)),&arg,sizeof(void *));
+ sys_memcpy(buff,&func,sizeof(void* (*)(void*)));
+ sys_memcpy(buff + sizeof(void* (*)(void*)),&arg,sizeof(void *));
write(erts_darwin_main_thread_pipe[1],buff,buff_sz);
return 0;
}
diff --git a/erts/emulator/beam/erl_gc.c b/erts/emulator/beam/erl_gc.c
index 0da4468f9c..5da8e3eda5 100644
--- a/erts/emulator/beam/erl_gc.c
+++ b/erts/emulator/beam/erl_gc.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2002-2017. All Rights Reserved.
+ * Copyright Ericsson AB 2002-2018. 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.
@@ -43,6 +43,7 @@
#include "erl_bif_unique.h"
#include "dist.h"
#include "erl_nfunc_sched.h"
+#include "erl_proc_sig_queue.h"
#define ERTS_INACT_WR_PB_LEAVE_MUCH_LIMIT 1
#define ERTS_INACT_WR_PB_LEAVE_MUCH_PERCENTAGE 20
@@ -116,6 +117,7 @@ typedef struct {
static Uint setup_rootset(Process*, Eterm*, int, Rootset*);
static void cleanup_rootset(Rootset *rootset);
static Eterm *full_sweep_heaps(Process *p,
+ ErlHeapFragment *live_hf_end,
int hibernate,
Eterm *n_heap, Eterm* n_htop,
char *oh, Uint oh_size,
@@ -142,7 +144,7 @@ static Eterm* sweep_literal_area(Eterm* n_hp, Eterm* n_htop,
static Eterm* sweep_literals_to_old_heap(Eterm* heap_ptr, Eterm* heap_end, Eterm* htop,
char* src, Uint src_size);
static Eterm* collect_live_heap_frags(Process* p, ErlHeapFragment *live_hf_end,
- Eterm* heap, Eterm* htop, Eterm* objv, int nobj);
+ Eterm* htop);
static int adjust_after_fullsweep(Process *p, int need, Eterm *objv, int nobj);
static void shrink_new_heap(Process *p, Uint new_sz, Eterm *objv, int nobj);
static void grow_new_heap(Process *p, Uint new_sz, Eterm* objv, int nobj);
@@ -153,7 +155,6 @@ static void offset_rootset(Process *p, Sint offs, char* area, Uint area_size,
Eterm* objv, int nobj);
static void offset_off_heap(Process* p, Sint offs, char* area, Uint area_size);
static void offset_mqueue(Process *p, Sint offs, char* area, Uint area_size);
-static void move_msgq_to_heap(Process *p);
static int reached_max_heap_size(Process *p, Uint total_heap_size,
Uint extra_heap_size, Uint extra_old_heap_size);
static void init_gc_info(ErtsGCInfo *gcip);
@@ -188,6 +189,21 @@ static struct {
ErtsGCInfo info;
} dirty_gc;
+static void move_msgs_to_heap(Process *c_p)
+{
+ Uint64 pre_oh, post_oh;
+
+ pre_oh = c_p->off_heap.overhead;
+ erts_proc_sig_move_msgs_to_heap(c_p);
+ post_oh = c_p->off_heap.overhead;
+
+ if (pre_oh != post_oh) {
+ /* Got new binaries; update bin vheap size... */
+ c_p->bin_vheap_sz = next_vheap_size(c_p, post_oh,
+ c_p->bin_vheap_sz);
+ }
+}
+
static ERTS_INLINE int
gc_cost(Uint gc_moved_live_words, Uint resize_moved_words)
{
@@ -564,20 +580,26 @@ young_gen_usage(Process *p)
hsz = p->mbuf_sz;
if (p->flags & F_ON_HEAP_MSGQ) {
- ErtsMessage *mp;
- for (mp = p->msg.first; mp; mp = mp->next) {
- /*
- * We leave not yet decoded distribution messages
- * as they are in the queue since it is not
- * possible to determine a maximum size until
- * actual decoding. However, we use their estimated
- * size when calculating need, and by this making
- * it more likely that they will fit on the heap
- * when actually decoded.
- */
- if (mp->data.attached)
- hsz += erts_msg_attached_data_size(mp);
- }
+ ERTS_FOREACH_SIG_PRIVQS(
+ p, mp,
+ {
+ /*
+ * We leave not yet decoded distribution messages
+ * as they are in the queue since it is not
+ * possible to determine a maximum size until
+ * actual decoding. However, we use their estimated
+ * size when calculating need, and by this making
+ * it more likely that they will fit on the heap
+ * when actually decoded.
+ *
+ * We however ignore off heap messages...
+ */
+ if (ERTS_SIG_IS_MSG(mp)
+ && mp->data.attached
+ && mp->data.attached != ERTS_MSG_COMBINED_HFRAG) {
+ hsz += erts_msg_attached_data_size(mp);
+ }
+ });
}
hsz += p->htop - p->heap;
@@ -620,7 +642,7 @@ check_for_possibly_long_gc(Process *p, Uint ygen_usage)
sz = ygen_usage;
sz += p->hend - p->stop;
if (p->flags & F_ON_HEAP_MSGQ)
- sz += p->msg.len;
+ sz += p->sig_qs.len;
if (major)
sz += p->old_htop - p->old_heap;
@@ -760,13 +782,9 @@ do_major_collection:
long_gc/large_gc triggers when this happens as process was
killed before a GC could be done. */
if (reds == -2) {
- ErtsProcLocks locks = ERTS_PROC_LOCKS_ALL;
int res;
- erts_proc_lock(p, ERTS_PROC_LOCKS_ALL_MINOR);
- erts_send_exit_signal(p, p->common.id, p, &locks,
- am_kill, NIL, NULL, 0);
- erts_proc_unlock(p, locks & ERTS_PROC_LOCKS_ALL_MINOR);
+ erts_set_self_exiting(p, am_killed);
delay_gc_after_start:
/* erts_send_exit_signal looks for ERTS_PSFLG_GC, so
@@ -917,6 +935,7 @@ garbage_collect_hibernate(Process* p, int check_long_gc)
htop = heap;
htop = full_sweep_heaps(p,
+ ERTS_INVALID_HFRAG_PTR,
1,
heap,
htop,
@@ -1161,7 +1180,7 @@ erts_garbage_collect_literals(Process* p, Eterm* literals,
roots++;
- while (g_sz--) {
+ for ( ; g_sz--; g_ptr++) {
Eterm gval = *g_ptr;
switch (primary_tag(gval)) {
@@ -1170,26 +1189,21 @@ erts_garbage_collect_literals(Process* p, Eterm* literals,
val = *ptr;
if (IS_MOVED_BOXED(val)) {
ASSERT(is_boxed(val));
- *g_ptr++ = val;
+ *g_ptr = val;
} else if (ErtsInArea(ptr, area, area_size)) {
- move_boxed(&ptr,val,&old_htop,g_ptr++);
- } else {
- g_ptr++;
+ move_boxed(ptr,val,&old_htop,g_ptr);
}
break;
case TAG_PRIMARY_LIST:
ptr = list_val(gval);
val = *ptr;
if (IS_MOVED_CONS(val)) { /* Moved */
- *g_ptr++ = ptr[1];
+ *g_ptr = ptr[1];
} else if (ErtsInArea(ptr, area, area_size)) {
- move_cons(&ptr,val,&old_htop,g_ptr++);
- } else {
- g_ptr++;
- }
+ move_cons(ptr,val,&old_htop,g_ptr);
+ }
break;
default:
- g_ptr++;
break;
}
}
@@ -1378,7 +1392,7 @@ minor_collection(Process* p, ErlHeapFragment *live_hf_end,
new_sz, objv, nobj);
if (p->flags & F_ON_HEAP_MSGQ)
- move_msgq_to_heap(p);
+ move_msgs_to_heap(p);
new_mature = p->old_htop - prev_old_htop;
@@ -1479,25 +1493,29 @@ do_minor(Process *p, ErlHeapFragment *live_hf_end,
n_htop = n_heap = (Eterm*) ERTS_HEAP_ALLOC(ERTS_ALC_T_HEAP,
sizeof(Eterm)*new_sz);
+ n = setup_rootset(p, objv, nobj, &rootset);
+ roots = rootset.roots;
+
+ /*
+ * All allocations done. Start defile heap with move markers.
+ * A crash dump due to allocation failure above will see a healthy heap.
+ */
+
if (live_hf_end != ERTS_INVALID_HFRAG_PTR) {
/*
* Move heap frags that we know are completely live
* directly into the new young heap generation.
*/
- n_htop = collect_live_heap_frags(p, live_hf_end, n_heap, n_htop,
- objv, nobj);
+ n_htop = collect_live_heap_frags(p, live_hf_end, n_htop);
}
- n = setup_rootset(p, objv, nobj, &rootset);
- roots = rootset.roots;
-
GENSWEEP_NSTACK(p, old_htop, n_htop);
while (n--) {
Eterm* g_ptr = roots->v;
Uint g_sz = roots->sz;
roots++;
- while (g_sz--) {
+ for ( ; g_sz--; g_ptr++) {
gval = *g_ptr;
switch (primary_tag(gval)) {
@@ -1507,14 +1525,12 @@ do_minor(Process *p, ErlHeapFragment *live_hf_end,
val = *ptr;
if (IS_MOVED_BOXED(val)) {
ASSERT(is_boxed(val));
- *g_ptr++ = val;
+ *g_ptr = val;
} else if (ErtsInArea(ptr, mature, mature_size)) {
- move_boxed(&ptr,val,&old_htop,g_ptr++);
+ move_boxed(ptr,val,&old_htop,g_ptr);
} else if (ErtsInYoungGen(gval, ptr, oh, oh_size)) {
- move_boxed(&ptr,val,&n_htop,g_ptr++);
- } else {
- g_ptr++;
- }
+ move_boxed(ptr,val,&n_htop,g_ptr);
+ }
break;
}
@@ -1522,19 +1538,15 @@ do_minor(Process *p, ErlHeapFragment *live_hf_end,
ptr = list_val(gval);
val = *ptr;
if (IS_MOVED_CONS(val)) { /* Moved */
- *g_ptr++ = ptr[1];
+ *g_ptr = ptr[1];
} else if (ErtsInArea(ptr, mature, mature_size)) {
- move_cons(&ptr,val,&old_htop,g_ptr++);
+ move_cons(ptr,val,&old_htop,g_ptr);
} else if (ErtsInYoungGen(gval, ptr, oh, oh_size)) {
- move_cons(&ptr,val,&n_htop,g_ptr++);
- } else {
- g_ptr++;
- }
+ move_cons(ptr,val,&n_htop,g_ptr);
+ }
break;
}
-
default:
- g_ptr++;
break;
}
}
@@ -1568,9 +1580,9 @@ do_minor(Process *p, ErlHeapFragment *live_hf_end,
ASSERT(is_boxed(val));
*n_hp++ = val;
} else if (ErtsInArea(ptr, mature, mature_size)) {
- move_boxed(&ptr,val,&old_htop,n_hp++);
+ move_boxed(ptr,val,&old_htop,n_hp++);
} else if (ErtsInYoungGen(gval, ptr, oh, oh_size)) {
- move_boxed(&ptr,val,&n_htop,n_hp++);
+ move_boxed(ptr,val,&n_htop,n_hp++);
} else {
n_hp++;
}
@@ -1582,9 +1594,9 @@ do_minor(Process *p, ErlHeapFragment *live_hf_end,
if (IS_MOVED_CONS(val)) {
*n_hp++ = ptr[1];
} else if (ErtsInArea(ptr, mature, mature_size)) {
- move_cons(&ptr,val,&old_htop,n_hp++);
+ move_cons(ptr,val,&old_htop,n_hp++);
} else if (ErtsInYoungGen(gval, ptr, oh, oh_size)) {
- move_cons(&ptr,val,&n_htop,n_hp++);
+ move_cons(ptr,val,&n_htop,n_hp++);
} else {
n_hp++;
}
@@ -1604,10 +1616,10 @@ do_minor(Process *p, ErlHeapFragment *live_hf_end,
*origptr = val;
mb->base = binary_bytes(val);
} else if (ErtsInArea(ptr, mature, mature_size)) {
- move_boxed(&ptr,val,&old_htop,origptr);
+ move_boxed(ptr,val,&old_htop,origptr);
mb->base = binary_bytes(mb->orig);
} else if (ErtsInYoungGen(*origptr, ptr, oh, oh_size)) {
- move_boxed(&ptr,val,&n_htop,origptr);
+ move_boxed(ptr,val,&n_htop,origptr);
mb->base = binary_bytes(mb->orig);
}
}
@@ -1733,16 +1745,8 @@ major_collection(Process* p, ErlHeapFragment *live_hf_end,
n_htop = n_heap = (Eterm *) ERTS_HEAP_ALLOC(ERTS_ALC_T_HEAP,
sizeof(Eterm)*new_sz);
- if (live_hf_end != ERTS_INVALID_HFRAG_PTR) {
- /*
- * Move heap frags that we know are completely live
- * directly into the heap.
- */
- n_htop = collect_live_heap_frags(p, live_hf_end, n_heap, n_htop,
- objv, nobj);
- }
-
- n_htop = full_sweep_heaps(p, 0, n_heap, n_htop, oh, oh_size, objv, nobj);
+ n_htop = full_sweep_heaps(p, live_hf_end, 0, n_heap, n_htop, oh, oh_size,
+ objv, nobj);
/* Move the stack to the end of the heap */
stk_sz = HEAP_END(p) - p->stop;
@@ -1773,7 +1777,7 @@ major_collection(Process* p, ErlHeapFragment *live_hf_end,
HIGH_WATER(p) = HEAP_TOP(p);
if (p->flags & F_ON_HEAP_MSGQ)
- move_msgq_to_heap(p);
+ move_msgs_to_heap(p);
ErtsGcQuickSanityCheck(p);
@@ -1789,6 +1793,7 @@ major_collection(Process* p, ErlHeapFragment *live_hf_end,
static Eterm *
full_sweep_heaps(Process *p,
+ ErlHeapFragment *live_hf_end,
int hibernate,
Eterm *n_heap, Eterm* n_htop,
char *oh, Uint oh_size,
@@ -1805,6 +1810,19 @@ full_sweep_heaps(Process *p,
n = setup_rootset(p, objv, nobj, &rootset);
+ /*
+ * All allocations done. Start defile heap with move markers.
+ * A crash dump due to allocation failure above will see a healthy heap.
+ */
+
+ if (live_hf_end != ERTS_INVALID_HFRAG_PTR) {
+ /*
+ * Move heap frags that we know are completely live
+ * directly into the heap.
+ */
+ n_htop = collect_live_heap_frags(p, live_hf_end, n_htop);
+ }
+
#ifdef HIPE
if (hibernate)
hipe_empty_nstack(p);
@@ -1818,7 +1836,7 @@ full_sweep_heaps(Process *p,
Eterm g_sz = roots->sz;
roots++;
- while (g_sz--) {
+ for ( ; g_sz--; g_ptr++) {
Eterm* ptr;
Eterm val;
Eterm gval = *g_ptr;
@@ -1830,32 +1848,26 @@ full_sweep_heaps(Process *p,
val = *ptr;
if (IS_MOVED_BOXED(val)) {
ASSERT(is_boxed(val));
- *g_ptr++ = val;
+ *g_ptr = val;
} else if (!erts_is_literal(gval, ptr)) {
- move_boxed(&ptr,val,&n_htop,g_ptr++);
- } else {
- g_ptr++;
+ move_boxed(ptr,val,&n_htop,g_ptr);
}
- continue;
+ break;
}
case TAG_PRIMARY_LIST: {
ptr = list_val(gval);
val = *ptr;
if (IS_MOVED_CONS(val)) {
- *g_ptr++ = ptr[1];
+ *g_ptr = ptr[1];
} else if (!erts_is_literal(gval, ptr)) {
- move_cons(&ptr,val,&n_htop,g_ptr++);
- } else {
- g_ptr++;
+ move_cons(ptr,val,&n_htop,g_ptr);
}
- continue;
+ break;
}
- default: {
- g_ptr++;
- continue;
- }
+ default:
+ break;
}
}
}
@@ -2134,7 +2146,7 @@ sweep(Eterm *n_hp, Eterm *n_htop,
ASSERT(is_boxed(val));
*n_hp++ = val;
} else if (ERTS_IS_IN_SWEEP_AREA(gval, ptr)) {
- move_boxed(&ptr,val,&n_htop,n_hp++);
+ move_boxed(ptr,val,&n_htop,n_hp++);
} else {
n_hp++;
}
@@ -2146,7 +2158,7 @@ sweep(Eterm *n_hp, Eterm *n_htop,
if (IS_MOVED_CONS(val)) {
*n_hp++ = ptr[1];
} else if (ERTS_IS_IN_SWEEP_AREA(gval, ptr)) {
- move_cons(&ptr,val,&n_htop,n_hp++);
+ move_cons(ptr,val,&n_htop,n_hp++);
} else {
n_hp++;
}
@@ -2167,7 +2179,7 @@ sweep(Eterm *n_hp, Eterm *n_htop,
*origptr = val;
mb->base = binary_bytes(*origptr);
} else if (ERTS_IS_IN_SWEEP_AREA(*origptr, ptr)) {
- move_boxed(&ptr,val,&n_htop,origptr);
+ move_boxed(ptr,val,&n_htop,origptr);
mb->base = binary_bytes(*origptr);
}
}
@@ -2230,7 +2242,7 @@ sweep_literals_to_old_heap(Eterm* heap_ptr, Eterm* heap_end, Eterm* htop,
ASSERT(is_boxed(val));
*heap_ptr++ = val;
} else if (ErtsInArea(ptr, src, src_size)) {
- move_boxed(&ptr,val,&htop,heap_ptr++);
+ move_boxed(ptr,val,&htop,heap_ptr++);
} else {
heap_ptr++;
}
@@ -2242,7 +2254,7 @@ sweep_literals_to_old_heap(Eterm* heap_ptr, Eterm* heap_end, Eterm* htop,
if (IS_MOVED_CONS(val)) {
*heap_ptr++ = ptr[1];
} else if (ErtsInArea(ptr, src, src_size)) {
- move_cons(&ptr,val,&htop,heap_ptr++);
+ move_cons(ptr,val,&htop,heap_ptr++);
} else {
heap_ptr++;
}
@@ -2263,7 +2275,7 @@ sweep_literals_to_old_heap(Eterm* heap_ptr, Eterm* heap_end, Eterm* htop,
*origptr = val;
mb->base = binary_bytes(*origptr);
} else if (ErtsInArea(ptr, src, src_size)) {
- move_boxed(&ptr,val,&htop,origptr);
+ move_boxed(ptr,val,&htop,origptr);
mb->base = binary_bytes(*origptr);
}
}
@@ -2296,11 +2308,11 @@ move_one_area(Eterm* n_htop, char* src, Uint src_size)
ASSERT(val != ERTS_HOLE_MARKER);
if (is_header(val)) {
ASSERT(ptr + header_arity(val) < end);
- move_boxed(&ptr, val, &n_htop, &dummy_ref);
+ ptr = move_boxed(ptr, val, &n_htop, &dummy_ref);
}
else { /* must be a cons cell */
ASSERT(ptr+1 < end);
- move_cons(&ptr, val, &n_htop, &dummy_ref);
+ move_cons(ptr, val, &n_htop, &dummy_ref);
ptr += 2;
}
}
@@ -2313,9 +2325,7 @@ move_one_area(Eterm* n_htop, char* src, Uint src_size)
*/
static Eterm*
-collect_live_heap_frags(Process* p, ErlHeapFragment *live_hf_end,
- Eterm* n_hstart, Eterm* n_htop,
- Eterm* objv, int nobj)
+collect_live_heap_frags(Process* p, ErlHeapFragment *live_hf_end, Eterm* n_htop)
{
ErlHeapFragment* qb;
char* frag_begin;
@@ -2339,9 +2349,9 @@ collect_live_heap_frags(Process* p, ErlHeapFragment *live_hf_end,
return n_htop;
}
-static ERTS_INLINE void
-copy_one_frag(Eterm** hpp, ErlOffHeap* off_heap,
- ErlHeapFragment *bp, Eterm *refs, int nrefs)
+void
+erts_copy_one_frag(Eterm** hpp, ErlOffHeap* off_heap,
+ ErlHeapFragment *bp, Eterm *refs, int nrefs)
{
Uint sz;
int i;
@@ -2447,61 +2457,6 @@ copy_one_frag(Eterm** hpp, ErlOffHeap* off_heap,
bp->off_heap.first = NULL;
}
-static void
-move_msgq_to_heap(Process *p)
-{
-
- ErtsMessage **mpp = &p->msg.first;
- Uint64 pre_oh = MSO(p).overhead;
-
- while (*mpp) {
- ErtsMessage *mp = *mpp;
-
- if (mp->data.attached) {
- ErlHeapFragment *bp;
-
- /*
- * We leave not yet decoded distribution messages
- * as they are in the queue since it is not
- * possible to determine a maximum size until
- * actual decoding...
- */
- if (is_value(ERL_MESSAGE_TERM(mp))) {
-
- bp = erts_message_to_heap_frag(mp);
-
- if (bp->next)
- erts_move_multi_frags(&p->htop, &p->off_heap, bp,
- mp->m, ERL_MESSAGE_REF_ARRAY_SZ, 0);
- else
- copy_one_frag(&p->htop, &p->off_heap, bp,
- mp->m, ERL_MESSAGE_REF_ARRAY_SZ);
-
- if (mp->data.attached != ERTS_MSG_COMBINED_HFRAG) {
- mp->data.heap_frag = NULL;
- free_message_buffer(bp);
- }
- else {
- ErtsMessage *new_mp = erts_alloc_message(0, NULL);
- sys_memcpy((void *) new_mp->m, (void *) mp->m,
- sizeof(Eterm)*ERL_MESSAGE_REF_ARRAY_SZ);
- erts_msgq_replace_msg_ref(&p->msg, new_mp, mpp);
- mp->next = NULL;
- erts_cleanup_messages(mp);
- mp = new_mp;
- }
- }
- }
-
- mpp = &(*mpp)->next;
- }
-
- if (pre_oh != MSO(p).overhead) {
- /* Got new binaries; update vheap size... */
- BIN_VHEAP_SZ(p) = next_vheap_size(p, MSO(p).overhead, BIN_VHEAP_SZ(p));
- }
-}
-
static Uint
setup_rootset(Process *p, Eterm *objv, int nobj, Rootset *rootset)
{
@@ -2590,11 +2545,10 @@ setup_rootset(Process *p, Eterm *objv, int nobj, Rootset *rootset)
* We do not have off heap message queue enabled, i.e. we
* need to add message queue to rootset...
*/
- ErtsMessage *mp;
/* Ensure large enough rootset... */
- if (n + p->msg.len > rootset->size) {
- Uint new_size = n + p->msg.len;
+ if (n + p->sig_qs.len > rootset->size) {
+ Uint new_size = n + p->sig_qs.len;
ERTS_GC_ASSERT(roots == rootset->def);
roots = erts_alloc(ERTS_ALC_T_ROOTSET,
new_size*sizeof(Roots));
@@ -2602,18 +2556,19 @@ setup_rootset(Process *p, Eterm *objv, int nobj, Rootset *rootset)
rootset->size = new_size;
}
- for (mp = p->msg.first; mp; mp = mp->next) {
-
- if (!mp->data.attached) {
- /*
- * Message may refer data on heap;
- * add it to rootset...
- */
- roots[n].v = mp->m;
- roots[n].sz = ERL_MESSAGE_REF_ARRAY_SZ;
- n++;
- }
- }
+ ERTS_FOREACH_SIG_PRIVQS(
+ p, mp,
+ {
+ if (ERTS_SIG_IS_INTERNAL_MSG(mp) && !mp->data.attached) {
+ /*
+ * Message may refer data on heap;
+ * add it to rootset...
+ */
+ roots[n].v = mp->m;
+ roots[n].sz = ERL_MESSAGE_REF_ARRAY_SZ;
+ n++;
+ }
+ });
break;
}
}
@@ -3124,51 +3079,59 @@ offset_off_heap(Process* p, Sint offs, char* area, Uint area_size)
}
}
+#ifndef USE_VM_PROBES
+#define ERTS_OFFSET_DT_UTAG(MP, A, ASZ, O)
+#else
+#define ERTS_OFFSET_DT_UTAG(MP, A, ASZ, O) \
+ do { \
+ Eterm utag__ = ERL_MESSAGE_DT_UTAG((MP)); \
+ if (is_boxed(utag__) && ErtsInArea(ptr_val(utag__), (A), (ASZ))) { \
+ ERL_MESSAGE_DT_UTAG((MP)) = offset_ptr(utag__, (O)); \
+ } \
+ } while (0)
+#endif
+
+static ERTS_INLINE void
+offset_message(ErtsMessage *mp, Sint offs, char* area, Uint area_size)
+{
+ Eterm mesg = ERL_MESSAGE_TERM(mp);
+ if (ERTS_SIG_IS_MSG_TAG(mesg)) {
+ if (ERTS_SIG_IS_INTERNAL_MSG_TAG(mesg)) {
+ switch (primary_tag(mesg)) {
+ case TAG_PRIMARY_LIST:
+ case TAG_PRIMARY_BOXED:
+ if (ErtsInArea(ptr_val(mesg), area, area_size)) {
+ ERL_MESSAGE_TERM(mp) = offset_ptr(mesg, offs);
+ }
+ break;
+ }
+ }
+ mesg = ERL_MESSAGE_TOKEN(mp);
+ if (is_boxed(mesg) && ErtsInArea(ptr_val(mesg), area, area_size)) {
+ ERL_MESSAGE_TOKEN(mp) = offset_ptr(mesg, offs);
+ }
+
+ ERTS_OFFSET_DT_UTAG(mp, area, area_size, offs);
+
+ ASSERT((is_nil(ERL_MESSAGE_TOKEN(mp)) ||
+ is_tuple(ERL_MESSAGE_TOKEN(mp)) ||
+ is_atom(ERL_MESSAGE_TOKEN(mp))));
+ }
+}
+
/*
* Offset pointers in message queue.
*/
static void
offset_mqueue(Process *p, Sint offs, char* area, Uint area_size)
{
- ErtsMessage* mp = p->msg.first;
-
- if ((p->flags & (F_OFF_HEAP_MSGQ|F_OFF_HEAP_MSGQ_CHNG)) != F_OFF_HEAP_MSGQ) {
-
- while (mp != NULL) {
- Eterm mesg = ERL_MESSAGE_TERM(mp);
- if (is_value(mesg)) {
- switch (primary_tag(mesg)) {
- case TAG_PRIMARY_LIST:
- case TAG_PRIMARY_BOXED:
- if (ErtsInArea(ptr_val(mesg), area, area_size)) {
- ERL_MESSAGE_TERM(mp) = offset_ptr(mesg, offs);
- }
- break;
- }
- }
- mesg = ERL_MESSAGE_TOKEN(mp);
- if (is_boxed(mesg) && ErtsInArea(ptr_val(mesg), area, area_size)) {
- ERL_MESSAGE_TOKEN(mp) = offset_ptr(mesg, offs);
- }
-#ifdef USE_VM_PROBES
- mesg = ERL_MESSAGE_DT_UTAG(mp);
- if (is_boxed(mesg) && ErtsInArea(ptr_val(mesg), area, area_size)) {
- ERL_MESSAGE_DT_UTAG(mp) = offset_ptr(mesg, offs);
- }
-#endif
-
- ASSERT((is_nil(ERL_MESSAGE_TOKEN(mp)) ||
- is_tuple(ERL_MESSAGE_TOKEN(mp)) ||
- is_atom(ERL_MESSAGE_TOKEN(mp))));
- mp = mp->next;
- }
-
- }
+ if ((p->flags & (F_OFF_HEAP_MSGQ|F_OFF_HEAP_MSGQ_CHNG)) != F_OFF_HEAP_MSGQ)
+ ERTS_FOREACH_SIG_PRIVQS(p, mp, offset_message(mp, offs, area, area_size));
}
static void ERTS_INLINE
offset_one_rootset(Process *p, Sint offs, char* area, Uint area_size,
- Eterm* objv, int nobj)
+ Eterm* objv, int nobj)
{
Eterm *v;
Uint sz;
@@ -3281,8 +3244,7 @@ reply_gc_info(void *vgcirp)
gcireq_free(vgcirp);
}
-void erts_sub_binary_to_heap_binary(Eterm **pp, Eterm **hpp, Eterm *orig) {
- Eterm *ptr = *pp;
+Eterm* erts_sub_binary_to_heap_binary(Eterm *ptr, Eterm **hpp, Eterm *orig) {
Eterm *htop = *hpp;
Eterm gval;
ErlSubBin *sb = (ErlSubBin *)ptr;
@@ -3310,7 +3272,7 @@ void erts_sub_binary_to_heap_binary(Eterm **pp, Eterm **hpp, Eterm *orig) {
htop += heap_bin_size(sb->size);
*hpp = htop;
- *pp = ptr;
+ return ptr;
}
@@ -3386,7 +3348,6 @@ erts_process_gc_info(Process *p, Uint *sizep, Eterm **hpp,
};
Eterm res = THE_NON_VALUE;
- ErtsMessage *mp;
ERTS_CT_ASSERT(sizeof(values)/sizeof(*values) == sizeof(tags)/sizeof(*tags));
ERTS_CT_ASSERT(sizeof(values)/sizeof(*values) == ERTS_PROCESS_GC_INFO_MAX_TERMS);
@@ -3405,9 +3366,15 @@ erts_process_gc_info(Process *p, Uint *sizep, Eterm **hpp,
be the same as adding old_heap_block_size + heap_block_size
+ mbuf_size.
*/
- for (mp = p->msg.first; mp; mp = mp->next)
- if (mp->data.attached)
- values[2] += erts_msg_attached_data_size(mp);
+ ERTS_FOREACH_SIG_PRIVQS(
+ p, mp,
+ {
+ if (ERTS_SIG_IS_MSG(mp)
+ && mp->data.attached
+ && mp->data.attached != ERTS_MSG_COMBINED_HFRAG) {
+ values[2] += erts_msg_attached_data_size(mp);
+ }
+ });
}
res = erts_bld_atom_uword_2tup_list(hpp,
diff --git a/erts/emulator/beam/erl_gc.h b/erts/emulator/beam/erl_gc.h
index 6a529b8443..63d03cdf8b 100644
--- a/erts/emulator/beam/erl_gc.h
+++ b/erts/emulator/beam/erl_gc.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2007-2017. All Rights Reserved.
+ * Copyright Ericsson AB 2007-2018. 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.
@@ -33,14 +33,15 @@
#define IS_MOVED_BOXED(x) (!is_header((x)))
#define IS_MOVED_CONS(x) (is_non_value((x)))
-void erts_sub_binary_to_heap_binary(Eterm **pp, Eterm **hpp, Eterm *orig);
+Eterm* erts_sub_binary_to_heap_binary(Eterm *ptr, Eterm **hpp, Eterm *orig);
-ERTS_GLB_INLINE void move_cons(Eterm **pp, Eterm car, Eterm **hpp, Eterm *orig);
+ERTS_GLB_INLINE void move_cons(Eterm *ERTS_RESTRICT ptr, Eterm car, Eterm **hpp,
+ Eterm *orig);
#if ERTS_GLB_INLINE_INCL_FUNC_DEF
-ERTS_GLB_INLINE void move_cons(Eterm **pp, Eterm car, Eterm **hpp, Eterm *orig)
+ERTS_GLB_INLINE void move_cons(Eterm *ERTS_RESTRICT ptr, Eterm car, Eterm **hpp,
+ Eterm *orig)
{
- Eterm *ptr = *pp;
- Eterm *htop = *hpp;
+ Eterm *ERTS_RESTRICT htop = *hpp;
Eterm gval;
htop[0] = car; /* copy car */
@@ -53,14 +54,15 @@ ERTS_GLB_INLINE void move_cons(Eterm **pp, Eterm car, Eterm **hpp, Eterm *orig)
}
#endif
-ERTS_GLB_INLINE void move_boxed(Eterm **pp, Eterm hdr, Eterm **hpp, Eterm *orig);
+ERTS_GLB_INLINE Eterm* move_boxed(Eterm *ERTS_RESTRICT ptr, Eterm hdr, Eterm **hpp,
+ Eterm *orig);
#if ERTS_GLB_INLINE_INCL_FUNC_DEF
-ERTS_GLB_INLINE void move_boxed(Eterm **pp, Eterm hdr, Eterm **hpp, Eterm *orig)
+ERTS_GLB_INLINE Eterm* move_boxed(Eterm *ERTS_RESTRICT ptr, Eterm hdr, Eterm **hpp,
+ Eterm *orig)
{
Eterm gval;
Sint nelts;
- Eterm *ptr = *pp;
- Eterm *htop = *hpp;
+ Eterm *ERTS_RESTRICT htop = *hpp;
ASSERT(is_header(hdr));
nelts = header_arity(hdr);
@@ -71,8 +73,7 @@ ERTS_GLB_INLINE void move_boxed(Eterm **pp, Eterm hdr, Eterm **hpp, Eterm *orig)
/* convert sub-binary to heap-binary if applicable */
if (sb->bitsize == 0 && sb->bitoffs == 0 &&
sb->is_writable == 0 && sb->size <= sizeof(Eterm) * 3) {
- erts_sub_binary_to_heap_binary(pp, hpp, orig);
- return;
+ return erts_sub_binary_to_heap_binary(ptr, hpp, orig);
}
}
nelts++;
@@ -90,7 +91,7 @@ ERTS_GLB_INLINE void move_boxed(Eterm **pp, Eterm hdr, Eterm **hpp, Eterm *orig)
while (nelts--) *htop++ = *ptr++;
*hpp = htop;
- *pp = ptr;
+ return ptr;
}
#endif
@@ -180,6 +181,8 @@ void erts_free_heap_frags(struct process* p);
Eterm erts_max_heap_size_map(Sint, Uint, Eterm **, Uint *);
int erts_max_heap_size(Eterm, Uint *, Uint *);
void erts_deallocate_young_generation(Process *c_p);
+void erts_copy_one_frag(Eterm** hpp, ErlOffHeap* off_heap,
+ ErlHeapFragment *bp, Eterm *refs, int nrefs);
#if defined(DEBUG) || defined(ERTS_OFFHEAP_DEBUG)
int erts_dbg_within_proc(Eterm *ptr, Process *p, Eterm* real_htop);
#endif
diff --git a/erts/emulator/beam/erl_goodfit_alloc.c b/erts/emulator/beam/erl_goodfit_alloc.c
index 50aa41b4d2..e3ba67f0af 100644
--- a/erts/emulator/beam/erl_goodfit_alloc.c
+++ b/erts/emulator/beam/erl_goodfit_alloc.c
@@ -170,6 +170,7 @@ static void unlink_free_block (Allctr_t *, Block_t *);
static void update_last_aux_mbc (Allctr_t *, Carrier_t *);
static Eterm info_options (Allctr_t *, char *, fmtfn_t *,
void *, Uint **, Uint *);
+static int gfalc_try_set_dyn_param(Allctr_t*, Eterm param, Uint value);
static void init_atoms (void);
#ifdef ERTS_ALLOC_UTIL_HARD_DEBUG
@@ -250,6 +251,8 @@ erts_gfalc_start(GFAllctr_t *gfallctr,
if (!erts_alcu_start(allctr, init))
return NULL;
+ allctr->try_set_dyn_param = gfalc_try_set_dyn_param;
+
if (allctr->min_block_size != MIN_BLK_SZ)
return NULL;
@@ -505,7 +508,7 @@ static struct {
static void ERTS_INLINE atom_init(Eterm *atom, char *name)
{
- *atom = am_atom_put(name, strlen(name));
+ *atom = am_atom_put(name, sys_strlen(name));
}
#define AM_INIT(AM) atom_init(&am.AM, #AM)
@@ -584,6 +587,15 @@ info_options(Allctr_t *allctr,
return res;
}
+static int gfalc_try_set_dyn_param(Allctr_t* allctr, Eterm param, Uint value)
+{
+ if (param == am_sbct) {
+ /* Cannot change 'sbct' without rearranging buckets */
+ return 0;
+ }
+ return erts_alcu_try_set_dyn_param(allctr, param, value);
+}
+
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\
* NOTE: erts_gfalc_test() is only supposed to be used for testing. *
* *
diff --git a/erts/emulator/beam/erl_hl_timer.c b/erts/emulator/beam/erl_hl_timer.c
index bda2c9b94d..6ec6f8065e 100644
--- a/erts/emulator/beam/erl_hl_timer.c
+++ b/erts/emulator/beam/erl_hl_timer.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2015-2017. All Rights Reserved.
+ * Copyright Ericsson AB 2015-2018. 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.
@@ -38,6 +38,7 @@
#define ERTS_WANT_TIMER_WHEEL_API
#include "erl_time.h"
#include "erl_hl_timer.h"
+#include "erl_proc_sig_queue.h"
#ifdef ERTS_MAGIC_REF_BIF_TIMERS
#include "erl_binary.h"
#endif
@@ -2470,28 +2471,21 @@ access_bif_timer(Process *c_p, Eterm tref, int cancel, int async, int info)
req->rrefn[1] = rrefn[1];
req->rrefn[2] = rrefn[2];
- erts_proc_lock(c_p, ERTS_PROC_LOCKS_MSG_RECEIVE);
-
- if (ERTS_PROC_PENDING_EXIT(c_p))
- ERTS_VBUMP_ALL_REDS(c_p);
- else {
- /*
- * Caller needs to wait for a message containing
- * the ref that we just created. No such message
- * can exist in callers message queue at this time.
- * We therefore move the save pointer of the
- * callers message queue to the end of the queue.
- *
- * NOTE: It is of vital importance that the caller
- * immediately do a receive unconditionaly
- * waiting for the message with the reference;
- * otherwise, next receive will *not* work
- * as expected!
- */
- ERTS_MSGQ_MV_INQ2PRIVQ(c_p);
- c_p->msg.save = c_p->msg.last;
- }
- erts_proc_unlock(c_p, ERTS_PROC_LOCKS_MSG_RECEIVE);
+ /*
+ * Caller needs to wait for a message containing
+ * the ref that we just created. No such message
+ * can exist in callers message queue at this time.
+ * We therefore move the save pointer of the
+ * callers message queue to the end of the queue.
+ *
+ * NOTE: It is of vital importance that the caller
+ * immediately do a receive unconditionaly
+ * waiting for the message with the reference;
+ * otherwise, next receive will *not* work
+ * as expected!
+ */
+ ERTS_RECV_MARK_SAVE(c_p);
+ ERTS_RECV_MARK_SET(c_p);
ERTS_BIF_PREP_TRAP1(ret, erts_await_result, c_p, rref);
}
diff --git a/erts/emulator/beam/erl_init.c b/erts/emulator/beam/erl_init.c
index 4846ccd2d3..1e20d48a73 100644
--- a/erts/emulator/beam/erl_init.c
+++ b/erts/emulator/beam/erl_init.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1997-2017. All Rights Reserved.
+ * Copyright Ericsson AB 1997-2018. 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.
@@ -51,6 +51,8 @@
#include "erl_time.h"
#include "erl_check_io.h"
#include "erl_osenv.h"
+#include "erl_proc_sig_queue.h"
+
#ifdef HIPE
#include "hipe_mode_switch.h" /* for hipe_mode_switch_init() */
#include "hipe_signal.h" /* for hipe_signal_init() */
@@ -60,7 +62,7 @@
# include <sys/resource.h>
#endif
-#define ERTS_DEFAULT_NO_ASYNC_THREADS 10
+#define ERTS_DEFAULT_NO_ASYNC_THREADS 1
#define ERTS_DEFAULT_SCHED_STACK_SIZE 128
#define ERTS_DEFAULT_DCPU_SCHED_STACK_SIZE 40
@@ -126,6 +128,8 @@ const Eterm etp_hole_marker = 0;
static int modified_sched_thread_suggested_stack_size = 0;
+Eterm erts_init_process_id;
+
/*
* Note about VxWorks: All variables must be initialized by executable code,
* not by an initializer. Otherwise a new instance of the emulator will
@@ -238,7 +242,7 @@ progname(char *fullname)
{
int i;
- i = strlen(fullname);
+ i = sys_strlen(fullname);
while (i >= 0) {
if ((fullname[i] != '/') && (fullname[i] != '\\'))
i--;
@@ -304,8 +308,9 @@ erl_init(int ncpu,
int node_tab_delete_delay,
ErtsDbSpinCount db_spin_count)
{
+ erts_monitor_link_init();
+ erts_proc_sig_queue_init();
erts_bif_unique_init();
- erts_init_monitors();
erts_init_time(time_correction, time_warp_mode);
erts_init_sys_common_misc();
erts_init_process(ncpu, proc_tab_sz, legacy_proc_tab);
@@ -413,7 +418,7 @@ erl_first_process_otp(char* modname, void* code, unsigned size, int argc, char**
}
static Eterm
-erl_system_process_otp(Eterm parent_pid, char* modname, int off_heap_msgq)
+erl_system_process_otp(Eterm parent_pid, char* modname, int off_heap_msgq, int prio)
{
Eterm start_mod;
Process* parent;
@@ -428,9 +433,16 @@ erl_system_process_otp(Eterm parent_pid, char* modname, int off_heap_msgq)
parent = erts_pid2proc(NULL, 0, parent_pid, ERTS_PROC_LOCK_MAIN);
- so.flags = erts_default_spo_flags|SPO_SYSTEM_PROC;
+ so.flags = erts_default_spo_flags|SPO_SYSTEM_PROC|SPO_USE_ARGS;
if (off_heap_msgq)
so.flags |= SPO_OFF_HEAP_MSGQ;
+ so.min_heap_size = H_MIN_SIZE;
+ so.min_vheap_size = BIN_VH_MIN_SIZE;
+ so.max_heap_size = H_MAX_SIZE;
+ so.max_heap_flags = H_MAX_FLAGS;
+ so.priority = prio;
+ so.max_gen_gcs = (Uint16) erts_atomic32_read_nob(&erts_max_gen_gcs);
+ so.scheduler = 0;
res = erl_create_process(parent, start_mod, am_start, NIL, &so);
erts_proc_unlock(parent, ERTS_PROC_LOCK_MAIN);
return res;
@@ -810,7 +822,7 @@ early_init(int *argc, char **argv) /*
if (argc && argv) {
int i = 1;
while (i < *argc) {
- if (strcmp(argv[i], "--") == 0) { /* end of emulator options */
+ if (sys_strcmp(argv[i], "--") == 0) { /* end of emulator options */
i++;
break;
}
@@ -892,7 +904,7 @@ early_init(int *argc, char **argv) /*
else if (argv[i][2] == 'D') {
char *arg;
char *type = argv[i]+3;
- if (strncmp(type, "Pcpu", 4) == 0) {
+ if (sys_strncmp(type, "Pcpu", 4) == 0) {
int ptot, ponln;
arg = get_arg(argv[i]+7, argv[i+1], &i);
switch (sscanf(arg, "%d:%d", &ptot, &ponln)) {
@@ -929,7 +941,7 @@ early_init(int *argc, char **argv) /*
VERBOSE(DEBUG_SYSTEM,
("using %d:%d dirty CPU scheduler percentages\n",
dirty_cpu_scheds_pctg, dirty_cpu_scheds_onln_pctg));
- } else if (strncmp(type, "cpu", 3) == 0) {
+ } else if (sys_strncmp(type, "cpu", 3) == 0) {
int tot, onln;
arg = get_arg(argv[i]+6, argv[i+1], &i);
switch (sscanf(arg, "%d:%d", &tot, &onln)) {
@@ -980,7 +992,7 @@ early_init(int *argc, char **argv) /*
}
VERBOSE(DEBUG_SYSTEM,
("using %d:%d dirty CPU scheduler(s)\n", tot, onln));
- } else if (strncmp(type, "io", 2) == 0) {
+ } else if (sys_strncmp(type, "io", 2) == 0) {
arg = get_arg(argv[i]+5, argv[i+1], &i);
dirty_io_scheds = atoi(arg);
if (dirty_io_scheds < 0 ||
@@ -1200,7 +1212,6 @@ erl_start(int argc, char **argv)
ErtsTimeWarpMode time_warp_mode;
int node_tab_delete_delay = ERTS_NODE_TAB_DELAY_GC_DEFAULT;
ErtsDbSpinCount db_spin_count = ERTS_DB_SPNCNT_NORMAL;
- Eterm otp_ring0_pid;
set_default_time_adj(&time_correction,
&time_warp_mode);
@@ -1242,7 +1253,7 @@ erl_start(int argc, char **argv)
if (argv[i][0] != '-') {
erts_usage();
}
- if (strcmp(argv[i], "--") == 0) { /* end of emulator options */
+ if (sys_strcmp(argv[i], "--") == 0) { /* end of emulator options */
i++;
break;
}
@@ -1270,12 +1281,12 @@ erl_start(int argc, char **argv)
("using display items %d\n",display_items));
break;
case 'p':
- if (!strncmp(argv[i],"-pc",3)) {
+ if (!sys_strncmp(argv[i],"-pc",3)) {
int printable_chars = ERL_PRINTABLE_CHARACTERS_LATIN1;
arg = get_arg(argv[i]+3, argv[i+1], &i);
- if (!strcmp(arg,"unicode")) {
+ if (!sys_strcmp(arg,"unicode")) {
printable_chars = ERL_PRINTABLE_CHARACTERS_UNICODE;
- } else if (strcmp(arg,"latin1")) {
+ } else if (sys_strcmp(arg,"latin1")) {
erts_fprintf(stderr, "bad range of printable "
"characters: %s\n", arg);
erts_usage();
@@ -1287,7 +1298,7 @@ erl_start(int argc, char **argv)
erts_usage();
}
case 'f':
- if (!strncmp(argv[i],"-fn",3)) {
+ if (!sys_strncmp(argv[i],"-fn",3)) {
int warning_type = ERL_FILENAME_WARNING_WARNING;
arg = get_arg(argv[i]+3, argv[i+1], &i);
switch (*arg) {
@@ -1470,9 +1481,9 @@ erl_start(int argc, char **argv)
}
} else if (has_prefix("maxk", sub_param)) {
arg = get_arg(sub_param+4, argv[i+1], &i);
- if (strcmp(arg,"true") == 0) {
+ if (sys_strcmp(arg,"true") == 0) {
H_MAX_FLAGS |= MAX_HEAP_SIZE_KILL;
- } else if (strcmp(arg,"false") == 0) {
+ } else if (sys_strcmp(arg,"false") == 0) {
H_MAX_FLAGS &= ~MAX_HEAP_SIZE_KILL;
} else {
erts_fprintf(stderr, "bad max heap kill %s\n", arg);
@@ -1481,9 +1492,9 @@ erl_start(int argc, char **argv)
VERBOSE(DEBUG_SYSTEM, ("using max heap kill %d\n", H_MAX_FLAGS));
} else if (has_prefix("maxel", sub_param)) {
arg = get_arg(sub_param+5, argv[i+1], &i);
- if (strcmp(arg,"true") == 0) {
+ if (sys_strcmp(arg,"true") == 0) {
H_MAX_FLAGS |= MAX_HEAP_SIZE_LOG;
- } else if (strcmp(arg,"false") == 0) {
+ } else if (sys_strcmp(arg,"false") == 0) {
H_MAX_FLAGS &= ~MAX_HEAP_SIZE_LOG;
} else {
erts_fprintf(stderr, "bad max heap error logger %s\n", arg);
@@ -1601,7 +1612,7 @@ erl_start(int argc, char **argv)
case 'P': /* set maximum number of processes */
arg = get_arg(argv[i]+2, argv[i+1], &i);
- if (strcmp(arg, "legacy") == 0)
+ if (sys_strcmp(arg, "legacy") == 0)
legacy_proc_tab = 1;
else {
errno = 0;
@@ -1617,7 +1628,7 @@ erl_start(int argc, char **argv)
case 'Q': /* set maximum number of ports */
arg = get_arg(argv[i]+2, argv[i+1], &i);
- if (strcmp(arg, "legacy") == 0)
+ if (sys_strcmp(arg, "legacy") == 0)
legacy_port_tab = 1;
else {
errno = 0;
@@ -1635,11 +1646,11 @@ erl_start(int argc, char **argv)
case 'S' : /* Was handled in early_init() just read past it */
if (argv[i][2] == 'D') {
char* type = argv[i]+3;
- if (strcmp(type, "Pcpu") == 0)
+ if (sys_strcmp(type, "Pcpu") == 0)
(void) get_arg(argv[i]+7, argv[i+1], &i);
- if (strcmp(type, "cpu") == 0)
+ if (sys_strcmp(type, "cpu") == 0)
(void) get_arg(argv[i]+6, argv[i+1], &i);
- else if (strcmp(type, "io") == 0)
+ else if (sys_strcmp(type, "io") == 0)
(void) get_arg(argv[i]+5, argv[i+1], &i);
} else if (argv[i][2] == 'P')
(void) get_arg(argv[i]+3, argv[i+1], &i);
@@ -1746,6 +1757,7 @@ erl_start(int argc, char **argv)
}
else if (has_prefix("ecio", sub_param)) {
/* ignore argument, eager check io no longer used */
+ arg = get_arg(sub_param+4, argv[i+1], &i);
}
else if (has_prefix("pp", sub_param)) {
arg = get_arg(sub_param+2, argv[i+1], &i);
@@ -2190,8 +2202,8 @@ erl_start(int argc, char **argv)
erts_initialized = 1;
- otp_ring0_pid = erl_first_process_otp("otp_ring0", NULL, 0,
- boot_argc, boot_argv);
+ erts_init_process_id = erl_first_process_otp("otp_ring0", NULL, 0,
+ boot_argc, boot_argv);
{
/*
@@ -2201,14 +2213,18 @@ erl_start(int argc, char **argv)
*/
Eterm pid;
- pid = erl_system_process_otp(otp_ring0_pid, "erts_code_purger", !0);
+ pid = erl_system_process_otp(erts_init_process_id,
+ "erts_code_purger", !0,
+ PRIORITY_NORMAL);
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);
- pid = erl_system_process_otp(otp_ring0_pid, "erts_literal_area_collector", !0);
+ pid = erl_system_process_otp(erts_init_process_id,
+ "erts_literal_area_collector",
+ !0, PRIORITY_NORMAL);
erts_literal_area_collector
= (Process *) erts_ptab_pix2intptr_ddrb(&erts_proc,
internal_pid_index(pid));
@@ -2216,14 +2232,35 @@ erl_start(int argc, char **argv)
&& erts_literal_area_collector->common.id == pid);
erts_proc_inc_refc(erts_literal_area_collector);
- pid = erl_system_process_otp(otp_ring0_pid, "erts_dirty_process_code_checker", !0);
- erts_dirty_process_code_checker
+ pid = erl_system_process_otp(erts_init_process_id,
+ "erts_dirty_process_signal_handler",
+ !0, PRIORITY_NORMAL);
+ erts_dirty_process_signal_handler
= (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);
-
+ ASSERT(erts_dirty_process_signal_handler
+ && erts_dirty_process_signal_handler->common.id == pid);
+ erts_proc_inc_refc(erts_dirty_process_signal_handler);
+
+ pid = erl_system_process_otp(erts_init_process_id,
+ "erts_dirty_process_signal_handler",
+ !0, PRIORITY_HIGH);
+ erts_dirty_process_signal_handler_high
+ = (Process *) erts_ptab_pix2intptr_ddrb(&erts_proc,
+ internal_pid_index(pid));
+ ASSERT(erts_dirty_process_signal_handler_high
+ && erts_dirty_process_signal_handler_high->common.id == pid);
+ erts_proc_inc_refc(erts_dirty_process_signal_handler_high);
+
+ pid = erl_system_process_otp(erts_init_process_id,
+ "erts_dirty_process_signal_handler",
+ !0, PRIORITY_MAX);
+ erts_dirty_process_signal_handler_max
+ = (Process *) erts_ptab_pix2intptr_ddrb(&erts_proc,
+ internal_pid_index(pid));
+ ASSERT(erts_dirty_process_signal_handler_max
+ && erts_dirty_process_signal_handler_max->common.id == pid);
+ erts_proc_inc_refc(erts_dirty_process_signal_handler_max);
}
erts_start_schedulers();
diff --git a/erts/emulator/beam/erl_instrument.c b/erts/emulator/beam/erl_instrument.c
index 634509f880..2f70e7996e 100644
--- a/erts/emulator/beam/erl_instrument.c
+++ b/erts/emulator/beam/erl_instrument.c
@@ -103,7 +103,7 @@ static struct {
static void ERTS_INLINE atom_init(Eterm *atom, const char *name)
{
- *atom = am_atom_put((char *) name, strlen(name));
+ *atom = am_atom_put((char *) name, sys_strlen(name));
}
#define AM_INIT(AM) atom_init(&am.AM, #AM)
diff --git a/erts/emulator/beam/erl_io_queue.c b/erts/emulator/beam/erl_io_queue.c
index c93b5248d9..d779d1031a 100644
--- a/erts/emulator/beam/erl_io_queue.c
+++ b/erts/emulator/beam/erl_io_queue.c
@@ -801,12 +801,11 @@ static Eterm iol2v_make_sub_bin(iol2v_state_t *state, Eterm bin_term,
ERTS_GET_REAL_BIN(bin_term, orig_pb_term,
byte_offset, bit_offset, bit_size);
- (void)bit_offset;
- (void)bit_size;
+ ASSERT(bit_size == 0);
sb->thing_word = HEADER_SUB_BIN;
+ sb->bitoffs = bit_offset;
sb->bitsize = 0;
- sb->bitoffs = 0;
sb->orig = orig_pb_term;
sb->is_writable = 0;
@@ -975,7 +974,7 @@ static int iol2v_append_binary(iol2v_state_t *state, Eterm bin_term) {
parent_header = binary_val(parent_binary);
binary_size = binary_size(bin_term);
- if (bit_offset != 0 || bit_size != 0) {
+ if (bit_size != 0) {
return 0;
} else if (binary_size == 0) {
state->bytereds_spent += 1;
@@ -1017,8 +1016,16 @@ static int iol2v_append_binary(iol2v_state_t *state, Eterm bin_term) {
* then just copy it into the accumulator. */
iol2v_expand_acc(state, binary_size);
- sys_memcpy(&(state->acc)->orig_bytes[state->acc_size],
- binary_data, binary_size);
+ if (ERTS_LIKELY(bit_offset == 0)) {
+ sys_memcpy(&(state->acc)->orig_bytes[state->acc_size],
+ binary_data, binary_size);
+ } else {
+ ASSERT(binary_size <= ERTS_UWORD_MAX / 8);
+
+ erts_copy_bits(binary_data, bit_offset, 1,
+ (byte*)&(state->acc)->orig_bytes[state->acc_size], 0, 1,
+ binary_size * 8);
+ }
state->acc_size += binary_size;
} else {
@@ -1029,8 +1036,16 @@ static int iol2v_append_binary(iol2v_state_t *state, Eterm bin_term) {
iol2v_expand_acc(state, spill);
- sys_memcpy(&(state->acc)->orig_bytes[state->acc_size],
- binary_data, spill);
+ if (ERTS_LIKELY(bit_offset == 0)) {
+ sys_memcpy(&(state->acc)->orig_bytes[state->acc_size],
+ binary_data, spill);
+ } else {
+ ASSERT(binary_size <= ERTS_UWORD_MAX / 8);
+
+ erts_copy_bits(binary_data, bit_offset, 1,
+ (byte*)&(state->acc)->orig_bytes[state->acc_size], 0, 1,
+ spill * 8);
+ }
state->acc_size += spill;
@@ -1179,7 +1194,10 @@ BIF_RETTYPE iolist_to_iovec_1(BIF_ALIST_1) {
if (is_nil(BIF_ARG_1)) {
BIF_RET(NIL);
} else if (is_binary(BIF_ARG_1)) {
- if (binary_size(BIF_ARG_1) != 0) {
+ if (binary_bitsize(BIF_ARG_1) != 0) {
+ ASSERT(!(BIF_P->flags & F_DISABLE_GC));
+ BIF_ERROR(BIF_P, BADARG);
+ } else if (binary_size(BIF_ARG_1) != 0) {
Eterm *hp = HAlloc(BIF_P, 2);
BIF_RET(CONS(hp, BIF_ARG_1, NIL));
diff --git a/erts/emulator/beam/erl_lock_check.c b/erts/emulator/beam/erl_lock_check.c
index 64950fc252..0ced5ec310 100644
--- a/erts/emulator/beam/erl_lock_check.c
+++ b/erts/emulator/beam/erl_lock_check.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2005-2017. All Rights Reserved.
+ * Copyright Ericsson AB 2005-2018. 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.
@@ -92,7 +92,6 @@ static erts_lc_lock_order_t erts_lock_order[] = {
{ "db_hash_slot", "address" },
{ "resource_monitors", "address" },
{ "driver_list", NULL },
- { "proc_link", "pid" },
{ "proc_msgq", "pid" },
{ "proc_btm", "pid" },
{ "dist_entry", "address" },
@@ -259,7 +258,7 @@ static ERTS_INLINE void lc_free(void *p)
{
erts_lc_free_block_t *fb = (erts_lc_free_block_t *) p;
#ifdef DEBUG
- memset((void *) p, 0xdf, sizeof(erts_lc_free_block_t));
+ sys_memset((void *) p, 0xdf, sizeof(erts_lc_free_block_t));
#endif
lc_lock();
fb->next = free_blocks;
@@ -289,12 +288,12 @@ static void *lc_core_alloc(void)
}
for (i = 1; i < ERTS_LC_FB_CHUNK_SIZE - 1; i++) {
#ifdef DEBUG
- memset((void *) &fbs[i], 0xdf, sizeof(erts_lc_free_block_t));
+ sys_memset((void *) &fbs[i], 0xdf, sizeof(erts_lc_free_block_t));
#endif
fbs[i].next = &fbs[i+1];
}
#ifdef DEBUG
- memset((void *) &fbs[ERTS_LC_FB_CHUNK_SIZE-1],
+ sys_memset((void *) &fbs[ERTS_LC_FB_CHUNK_SIZE-1],
0xdf, sizeof(erts_lc_free_block_t));
#endif
lc_lock();
@@ -695,7 +694,7 @@ erts_lc_get_lock_order_id(char *name)
erts_fprintf(stderr, "Missing lock name\n");
else {
for (i = 0; i < ERTS_LOCK_ORDER_SIZE; i++)
- if (strcmp(erts_lock_order[i].name, name) == 0)
+ if (sys_strcmp(erts_lock_order[i].name, name) == 0)
return i;
erts_fprintf(stderr,
"Lock name '%s' missing in lock order "
@@ -1322,12 +1321,12 @@ erts_lc_init(void)
static erts_lc_free_block_t fbs[ERTS_LC_FB_CHUNK_SIZE];
for (i = 0; i < ERTS_LC_FB_CHUNK_SIZE - 1; i++) {
#ifdef DEBUG
- memset((void *) &fbs[i], 0xdf, sizeof(erts_lc_free_block_t));
+ sys_memset((void *) &fbs[i], 0xdf, sizeof(erts_lc_free_block_t));
#endif
fbs[i].next = &fbs[i+1];
}
#ifdef DEBUG
- memset((void *) &fbs[ERTS_LC_FB_CHUNK_SIZE-1],
+ sys_memset((void *) &fbs[ERTS_LC_FB_CHUNK_SIZE-1],
0xdf, sizeof(erts_lc_free_block_t));
#endif
fbs[ERTS_LC_FB_CHUNK_SIZE-1].next = NULL;
diff --git a/erts/emulator/beam/erl_map.c b/erts/emulator/beam/erl_map.c
index 8047a9567f..4ec6960997 100644
--- a/erts/emulator/beam/erl_map.c
+++ b/erts/emulator/beam/erl_map.c
@@ -490,7 +490,9 @@ Eterm erts_map_from_ks_and_vs(ErtsHeapFactory *factory, Eterm *ks0, Eterm *vs0,
sys_memcpy(ks, ks0, n * sizeof(Eterm));
sys_memcpy(vs, vs0, n * sizeof(Eterm));
- erts_validate_and_sort_flatmap(mp);
+ if (!erts_validate_and_sort_flatmap(mp)) {
+ return THE_NON_VALUE;
+ }
return make_flatmap(mp);
} else {
diff --git a/erts/emulator/beam/erl_message.c b/erts/emulator/beam/erl_message.c
index abf194cf94..98feb95d99 100644
--- a/erts/emulator/beam/erl_message.c
+++ b/erts/emulator/beam/erl_message.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1997-2017. All Rights Reserved.
+ * Copyright Ericsson AB 1997-2018. 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.
@@ -33,6 +33,7 @@
#include "erl_binary.h"
#include "dtrace-wrapper.h"
#include "beam_bp.h"
+#include "erl_proc_sig_queue.h"
ERTS_SCHED_PREF_QUICK_ALLOC_IMPL(message_ref,
ErtsMessageRef,
@@ -207,7 +208,7 @@ erts_cleanup_messages(ErtsMessage *msgp)
while (mp) {
ErtsMessage *fmp;
ErlHeapFragment *bp;
- if (is_non_value(ERL_MESSAGE_TERM(mp))) {
+ if (ERTS_SIG_IS_EXTERNAL_MSG(mp)) {
if (is_not_immed(ERL_MESSAGE_TOKEN(mp))) {
bp = (ErlHeapFragment *) mp->data.dist_ext->ext_endp;
erts_cleanup_offheap(&bp->off_heap);
@@ -215,10 +216,13 @@ erts_cleanup_messages(ErtsMessage *msgp)
if (mp->data.dist_ext)
erts_free_dist_ext_copy(mp->data.dist_ext);
}
- else {
- if (mp->data.attached != ERTS_MSG_COMBINED_HFRAG)
+ else {
+ if (ERTS_SIG_IS_INTERNAL_MSG(mp)
+ && mp->data.attached != ERTS_MSG_COMBINED_HFRAG) {
bp = mp->data.heap_frag;
+ }
else {
+ mp->data.attached = ERTS_MSG_COMBINED_HFRAG;
bp = mp->hfrag.next;
erts_cleanup_offheap(&mp->hfrag.off_heap);
}
@@ -272,6 +276,7 @@ erts_queue_dist_message(Process *rcvr,
mp = erts_alloc_message(0, NULL);
mp->data.dist_ext = dist_ext;
+ ERL_MESSAGE_FROM(mp) = dist_ext->dep->sysname;
ERL_MESSAGE_TERM(mp) = THE_NON_VALUE;
#ifdef USE_VM_PROBES
ERL_MESSAGE_DT_UTAG(mp) = NIL;
@@ -294,46 +299,15 @@ erts_queue_dist_message(Process *rcvr,
}
}
+
state = erts_atomic32_read_acqb(&rcvr->state);
- if (state & (ERTS_PSFLG_PENDING_EXIT|ERTS_PSFLG_EXITING)) {
+ if (state & ERTS_PSFLG_EXITING) {
if (!(rcvr_locks & ERTS_PROC_LOCK_MSGQ))
erts_proc_unlock(rcvr, ERTS_PROC_LOCK_MSGQ);
/* Drop message if receiver is exiting or has a pending exit ... */
erts_cleanup_messages(mp);
}
- else
- if (IS_TRACED_FL(rcvr, F_TRACE_RECEIVE)) {
- if (from == am_Empty)
- from = dist_ext->dep->sysname;
-
- /* Ahh... need to decode it in order to trace it... */
- if (!(rcvr_locks & ERTS_PROC_LOCK_MSGQ))
- erts_proc_unlock(rcvr, ERTS_PROC_LOCK_MSGQ);
- if (!erts_decode_dist_message(rcvr, rcvr_locks, mp, 0))
- erts_free_message(mp);
- else {
- Eterm msg = ERL_MESSAGE_TERM(mp);
- token = ERL_MESSAGE_TOKEN(mp);
-#ifdef USE_VM_PROBES
- if (DTRACE_ENABLED(message_queued)) {
- DTRACE_CHARBUF(receiver_name, DTRACE_TERM_BUF_SIZE);
-
- dtrace_proc_str(rcvr, receiver_name);
- if (have_seqtrace(token)) {
- tok_label = signed_val(SEQ_TRACE_T_LABEL(token));
- tok_lastcnt = signed_val(SEQ_TRACE_T_LASTCNT(token));
- tok_serial = signed_val(SEQ_TRACE_T_SERIAL(token));
- }
- DTRACE6(message_queued,
- receiver_name, size_object(msg), rcvr->msg.len,
- tok_label, tok_lastcnt, tok_serial);
- }
-#endif
- erts_queue_message(rcvr, rcvr_locks, mp, msg, from);
- }
- }
else {
- /* Enqueue message on external format */
#ifdef USE_VM_PROBES
if (DTRACE_ENABLED(message_queued)) {
@@ -349,7 +323,7 @@ erts_queue_dist_message(Process *rcvr,
* TODO: We don't know the real size of the external message here.
* -1 will appear to a D script as 4294967295.
*/
- DTRACE6(message_queued, receiver_name, -1, rcvr->msg.len + 1,
+ DTRACE6(message_queued, receiver_name, -1, rcvr->sig_qs.len + 1,
tok_label, tok_lastcnt, tok_serial);
}
#endif
@@ -359,9 +333,7 @@ erts_queue_dist_message(Process *rcvr,
if (!(rcvr_locks & ERTS_PROC_LOCK_MSGQ))
erts_proc_unlock(rcvr, ERTS_PROC_LOCK_MSGQ);
- erts_proc_notify_new_message(rcvr,
- rcvr_locks
- );
+ erts_proc_notify_new_message(rcvr, rcvr_locks);
}
}
@@ -372,15 +344,14 @@ queue_messages(Process* receiver,
ErtsProcLocks receiver_locks,
ErtsMessage* first,
ErtsMessage** last,
- Uint len,
- Eterm from)
+ Uint len)
{
- ErtsTracingEvent* te;
Sint res;
int locked_msgq = 0;
erts_aint32_t state;
ASSERT(is_value(ERL_MESSAGE_TERM(first)));
+ ASSERT(is_value(ERL_MESSAGE_FROM(first)));
ASSERT(ERL_MESSAGE_TOKEN(first) == am_undefined ||
ERL_MESSAGE_TOKEN(first) == NIL ||
is_tuple(ERL_MESSAGE_TOKEN(first)));
@@ -398,7 +369,7 @@ queue_messages(Process* receiver,
state = *receiver_state;
else
state = erts_atomic32_read_nob(&receiver->state);
- if (state & (ERTS_PSFLG_EXITING|ERTS_PSFLG_PENDING_EXIT))
+ if (state & ERTS_PSFLG_EXITING)
goto exiting;
need_locks = receiver_locks & ERTS_PROC_LOCKS_HIGHER_THAN(ERTS_PROC_LOCK_MSGQ);
@@ -414,7 +385,7 @@ queue_messages(Process* receiver,
state = erts_atomic32_read_nob(&receiver->state);
- if (state & (ERTS_PSFLG_PENDING_EXIT|ERTS_PSFLG_EXITING)) {
+ if (state & ERTS_PSFLG_EXITING) {
exiting:
/* Drop message if receiver is exiting or has a pending exit... */
if (locked_msgq)
@@ -423,7 +394,7 @@ queue_messages(Process* receiver,
return 0;
}
- res = receiver->msg.len;
+ res = receiver->sig_qs.len;
if (receiver_locks & ERTS_PROC_LOCK_MAIN) {
/*
* We move 'in queue' to 'private queue' and place
@@ -433,8 +404,8 @@ queue_messages(Process* receiver,
* we don't need to include the 'in queue' in
* the root set when garbage collecting.
*/
- res += receiver->msg_inq.len;
- ERTS_MSGQ_MV_INQ2PRIVQ(receiver);
+ res += receiver->sig_inq.len;
+ erts_proc_sig_fetch(receiver);
LINK_MESSAGE_PRIVQ(receiver, first, last, len);
}
else
@@ -442,38 +413,6 @@ queue_messages(Process* receiver,
LINK_MESSAGE(receiver, first, last, len);
}
- if (IS_TRACED_FL(receiver, F_TRACE_RECEIVE)
- && (te = &erts_receive_tracing[erts_active_bp_ix()],
- te->on)) {
-
- ErtsMessage *msg = first;
-
-#ifdef USE_VM_PROBES
- if (DTRACE_ENABLED(message_queued)) {
- DTRACE_CHARBUF(receiver_name, DTRACE_TERM_BUF_SIZE);
- Sint tok_label = 0;
- Sint tok_lastcnt = 0;
- Sint tok_serial = 0;
- Eterm seq_trace_token = ERL_MESSAGE_TOKEN(msg);
-
- dtrace_proc_str(receiver, receiver_name);
- if (seq_trace_token != NIL && is_tuple(seq_trace_token)) {
- tok_label = signed_val(SEQ_TRACE_T_LABEL(seq_trace_token));
- tok_lastcnt = signed_val(SEQ_TRACE_T_LASTCNT(seq_trace_token));
- tok_serial = signed_val(SEQ_TRACE_T_SERIAL(seq_trace_token));
- }
- DTRACE6(message_queued,
- receiver_name, size_object(ERL_MESSAGE_TERM(msg)),
- receiver->msg.len,
- tok_label, tok_lastcnt, tok_serial);
- }
-#endif
- while (msg) {
- trace_receive(receiver, from, ERL_MESSAGE_TERM(msg), te);
- msg = msg->next;
- }
-
- }
if (locked_msgq) {
erts_proc_unlock(receiver, ERTS_PROC_LOCK_MSGQ);
}
@@ -489,8 +428,9 @@ queue_message(Process* receiver,
ErtsMessage* mp, Eterm msg, Eterm from)
{
ERL_MESSAGE_TERM(mp) = msg;
+ ERL_MESSAGE_FROM(mp) = from;
return queue_messages(receiver, receiver_state, receiver_locks,
- mp, &mp->next, 1, from);
+ mp, &mp->next, 1);
}
Sint
@@ -503,11 +443,10 @@ erts_queue_message(Process* receiver, ErtsProcLocks receiver_locks,
Sint
erts_queue_messages(Process* receiver, ErtsProcLocks receiver_locks,
- ErtsMessage* first, ErtsMessage** last, Uint len,
- Eterm from)
+ ErtsMessage* first, ErtsMessage** last, Uint len)
{
return queue_messages(receiver, NULL, receiver_locks,
- first, last, len, from);
+ first, last, len);
}
void
@@ -616,7 +555,7 @@ erts_try_alloc_message_on_heap(Process *pp,
}
else {
in_message_fragment:
- if (!((*psp) & ERTS_PSFLG_ON_HEAP_MSGQ)) {
+ if ((*psp) & ERTS_PSFLG_OFF_HEAP_MSGQ) {
mp = erts_alloc_message(sz, hpp);
*ohpp = sz == 0 ? NULL : &mp->hfrag.off_heap;
}
@@ -922,70 +861,77 @@ Sint
erts_move_messages_off_heap(Process *c_p)
{
int reds = 1;
+ int i;
+ ErtsMessage *msgq[] = {c_p->sig_qs.first, c_p->sig_qs.cont};
/*
* Move all messages off heap. This *only* occurs when the
* process had off heap message disabled and just enabled
* it...
*/
- ErtsMessage *mp;
- reds += c_p->msg.len / 10;
+ reds += c_p->sig_qs.len / 10;
ASSERT(erts_atomic32_read_nob(&c_p->state)
& ERTS_PSFLG_OFF_HEAP_MSGQ);
ASSERT(c_p->flags & F_OFF_HEAP_MSGQ_CHNG);
- for (mp = c_p->msg.first; mp; mp = mp->next) {
- Uint msg_sz, token_sz;
+ for (i = 0; i < sizeof(msgq)/sizeof(msgq[0]); i++) {
+ ErtsMessage *mp;
+ for (mp = msgq[i]; mp; mp = mp->next) {
+ Uint msg_sz, token_sz;
#ifdef USE_VM_PROBES
- Uint utag_sz;
+ Uint utag_sz;
#endif
- Eterm *hp;
- ErlHeapFragment *hfrag;
+ Eterm *hp;
+ ErlHeapFragment *hfrag;
+
+ if (!ERTS_SIG_IS_INTERNAL_MSG(mp))
+ continue;
- if (mp->data.attached)
- continue;
+ if (mp->data.attached)
+ continue;
- if (is_immed(ERL_MESSAGE_TERM(mp))
+ if (is_immed(ERL_MESSAGE_TERM(mp))
#ifdef USE_VM_PROBES
- && is_immed(ERL_MESSAGE_DT_UTAG(mp))
+ && is_immed(ERL_MESSAGE_DT_UTAG(mp))
#endif
- && is_not_immed(ERL_MESSAGE_TOKEN(mp)))
- continue;
+ && is_not_immed(ERL_MESSAGE_TOKEN(mp)))
+ continue;
- /*
- * The message refers into the heap. Copy the message
- * from the heap into a heap fragment and attach
- * it to the message...
- */
- msg_sz = size_object(ERL_MESSAGE_TERM(mp));
+ /*
+ * The message refers into the heap. Copy the message
+ * from the heap into a heap fragment and attach
+ * it to the message...
+ */
+ msg_sz = size_object(ERL_MESSAGE_TERM(mp));
#ifdef USE_VM_PROBES
- utag_sz = size_object(ERL_MESSAGE_DT_UTAG(mp));
+ utag_sz = size_object(ERL_MESSAGE_DT_UTAG(mp));
#endif
- token_sz = size_object(ERL_MESSAGE_TOKEN(mp));
+ token_sz = size_object(ERL_MESSAGE_TOKEN(mp));
- hfrag = new_message_buffer(msg_sz
+ hfrag = new_message_buffer(msg_sz
#ifdef USE_VM_PROBES
- + utag_sz
+ + utag_sz
#endif
- + token_sz);
- 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);
- if (is_not_immed(ERL_MESSAGE_TOKEN(mp)))
- ERL_MESSAGE_TOKEN(mp) = copy_struct(ERL_MESSAGE_TOKEN(mp),
- token_sz, &hp,
- &hfrag->off_heap);
+ + token_sz);
+ 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);
+ if (is_not_immed(ERL_MESSAGE_TOKEN(mp)))
+ ERL_MESSAGE_TOKEN(mp) = copy_struct(ERL_MESSAGE_TOKEN(mp),
+ token_sz, &hp,
+ &hfrag->off_heap);
#ifdef USE_VM_PROBES
- if (is_not_immed(ERL_MESSAGE_DT_UTAG(mp)))
- ERL_MESSAGE_DT_UTAG(mp) = copy_struct(ERL_MESSAGE_DT_UTAG(mp),
- utag_sz, &hp,
- &hfrag->off_heap);
+ if (is_not_immed(ERL_MESSAGE_DT_UTAG(mp)))
+ ERL_MESSAGE_DT_UTAG(mp) = copy_struct(ERL_MESSAGE_DT_UTAG(mp),
+ utag_sz, &hp,
+ &hfrag->off_heap);
#endif
- mp->data.heap_frag = hfrag;
- reds += 1;
+ mp->data.heap_frag = hfrag;
+ reds += 1;
+ }
}
return reds;
@@ -1015,7 +961,7 @@ erts_complete_off_heap_message_queue_change(Process *c_p)
else {
reds += 2;
erts_proc_lock(c_p, ERTS_PROC_LOCK_MSGQ);
- ERTS_MSGQ_MV_INQ2PRIVQ(c_p);
+ erts_proc_sig_fetch(c_p);
erts_proc_unlock(c_p, ERTS_PROC_LOCK_MSGQ);
reds += erts_move_messages_off_heap(c_p);
}
@@ -1079,8 +1025,6 @@ erts_change_message_queue_management(Process *c_p, Eterm new_state)
case am_on_heap:
c_p->flags |= F_ON_HEAP_MSGQ;
c_p->flags &= ~F_OFF_HEAP_MSGQ;
- erts_atomic32_read_bor_nob(&c_p->state,
- ERTS_PSFLG_ON_HEAP_MSGQ);
/*
* We are not allowed to clear ERTS_PSFLG_OFF_HEAP_MSGQ
* if a off heap change is ongoing. It will be adjusted
@@ -1106,8 +1050,6 @@ erts_change_message_queue_management(Process *c_p, Eterm new_state)
break;
case am_off_heap:
c_p->flags &= ~F_ON_HEAP_MSGQ;
- erts_atomic32_read_band_nob(&c_p->state,
- ~ERTS_PSFLG_ON_HEAP_MSGQ);
goto change_to_off_heap;
default:
res = THE_NON_VALUE; /* badarg */
@@ -1229,139 +1171,6 @@ erts_decode_dist_message(Process *proc, ErtsProcLocks proc_locks,
return 1;
}
-/*
- * ERTS_INSPECT_MSGQ_KEEP_OH_MSGS == 0 will move off heap messages
- * into the heap of the inspected process if off_heap_message_queue
- * is false when process_info(_, messages) is called. That is, the
- * following GC will have more data in the rootset compared to the
- * scenario when process_info(_, messages) had not been called.
- *
- * ERTS_INSPECT_MSGQ_KEEP_OH_MSGS != 0 will keep off heap messages
- * off heap when process_info(_, messages) is called regardless of
- * the off_heap_message_queue setting of the process. That is, it
- * will change the following execution of the process as little as
- * possible.
- */
-#define ERTS_INSPECT_MSGQ_KEEP_OH_MSGS 1
-
-Uint
-erts_prep_msgq_for_inspection(Process *c_p, Process *rp,
- ErtsProcLocks rp_locks, ErtsMessageInfo *mip)
-{
- Uint tot_heap_size;
- ErtsMessage* mp;
- Sint i;
- int self_on_heap;
-
- /*
- * Prepare the message queue for inspection
- * by process_info().
- *
- *
- * - Decode all messages on external format
- * - Remove all corrupt dist messages from queue
- * - Save pointer to, and heap size need of each
- * message in the mip array.
- * - Return total heap size need for all messages
- * that needs to be copied.
- *
- * If ERTS_INSPECT_MSGQ_KEEP_OH_MSGS == 0:
- * - In case off heap messages is disabled and
- * we are inspecting our own queue, move all
- * off heap data into the heap.
- */
-
- self_on_heap = c_p == rp && !(c_p->flags & F_OFF_HEAP_MSGQ);
-
- tot_heap_size = 0;
- i = 0;
- mp = rp->msg.first;
- while (mp) {
- Eterm msg = ERL_MESSAGE_TERM(mp);
-
- mip[i].size = 0;
-
- if (is_non_value(msg)) {
- /* Dist message on external format; decode it... */
- if (mp->data.attached)
- erts_decode_dist_message(rp, rp_locks, mp,
- ERTS_INSPECT_MSGQ_KEEP_OH_MSGS);
-
- msg = ERL_MESSAGE_TERM(mp);
-
- if (is_non_value(msg)) {
- ErtsMessage **mpp;
- ErtsMessage *bad_mp = mp;
- /*
- * Bad distribution message; remove
- * it from the queue...
- */
- ASSERT(!mp->data.attached);
-
- mpp = i == 0 ? &rp->msg.first : &mip[i-1].msgp->next;
-
- ASSERT(*mpp == bad_mp);
-
- erts_msgq_update_internal_pointers(&rp->msg, mpp, &bad_mp->next);
-
- mp = mp->next;
- *mpp = mp;
- rp->msg.len--;
- bad_mp->next = NULL;
- erts_cleanup_messages(bad_mp);
- continue;
- }
- }
-
- ASSERT(is_value(msg));
-
-#if ERTS_INSPECT_MSGQ_KEEP_OH_MSGS
- if (is_not_immed(msg) && (!self_on_heap || mp->data.attached)) {
- Uint sz = size_object(msg);
- mip[i].size = sz;
- tot_heap_size += sz;
- }
-#else
- if (self_on_heap) {
- if (mp->data.attached) {
- ErtsMessage *tmp = NULL;
- if (mp->data.attached != ERTS_MSG_COMBINED_HFRAG) {
- erts_link_mbuf_to_proc(rp, mp->data.heap_frag);
- mp->data.attached = NULL;
- }
- else {
- /*
- * Need to replace the message reference since
- * we will get references to the message data
- * from the heap...
- */
- ErtsMessage **mpp;
- tmp = erts_alloc_message(0, NULL);
- sys_memcpy((void *) tmp->m, (void *) mp->m,
- sizeof(Eterm)*ERL_MESSAGE_REF_ARRAY_SZ);
- mpp = i == 0 ? &rp->msg.first : &mip[i-1].msgp->next;
- erts_msgq_replace_msg_ref(&rp->msg, tmp, mpp);
- erts_save_message_in_proc(rp, mp);
- mp = tmp;
- }
- }
- }
- else if (is_not_immed(msg)) {
- Uint sz = size_object(msg);
- mip[i].size = sz;
- tot_heap_size += sz;
- }
-
-#endif
-
- mip[i].msgp = mp;
- i++;
- mp = mp->next;
- }
-
- return tot_heap_size;
-}
-
void erts_factory_proc_init(ErtsHeapFactory* factory,
Process* p)
{
diff --git a/erts/emulator/beam/erl_message.h b/erts/emulator/beam/erl_message.h
index a14f4f51d8..f56a252aef 100644
--- a/erts/emulator/beam/erl_message.h
+++ b/erts/emulator/beam/erl_message.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1997-2016. All Rights Reserved.
+ * Copyright Ericsson AB 1997-2018. 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.
@@ -21,6 +21,11 @@
#ifndef __ERL_MESSAGE_H__
#define __ERL_MESSAGE_H__
+#include "sys.h"
+#define ERTS_PROC_SIG_QUEUE_TYPE_ONLY
+#include "erl_proc_sig_queue.h"
+#undef ERTS_PROC_SIG_QUEUE_TYPE_ONLY
+
struct proc_bin;
struct external_thing_;
@@ -118,15 +123,16 @@ struct erl_heap_fragment {
};
/* m[0] = message, m[1] = seq trace token */
-#define ERL_MESSAGE_REF_ARRAY_SZ 2
+#define ERL_MESSAGE_REF_ARRAY_SZ 3
#define ERL_MESSAGE_TERM(mp) ((mp)->m[0])
#define ERL_MESSAGE_TOKEN(mp) ((mp)->m[1])
+#define ERL_MESSAGE_FROM(mp) ((mp)->m[2])
#ifdef USE_VM_PROBES
/* m[2] = dynamic trace user tag */
#undef ERL_MESSAGE_REF_ARRAY_SZ
-#define ERL_MESSAGE_REF_ARRAY_SZ 3
-#define ERL_MESSAGE_DT_UTAG(mp) ((mp)->m[2])
+#define ERL_MESSAGE_REF_ARRAY_SZ 4
+#define ERL_MESSAGE_DT_UTAG(mp) ((mp)->m[3])
#else
#endif
@@ -157,28 +163,79 @@ struct erl_mesg {
ErlHeapFragment hfrag;
};
+/*
+ * The ErtsMessage struct is only one special type
+ * of signal. All signal structs have a common
+ * begining and can be differentiated by looking
+ * at the ErtsSignal 'common.tag' field.
+ *
+ * - An ordinary message will have a value
+ * - A distribution message that has not been
+ * decoded yet will have the non-value.
+ * - Other signals will have an external pid
+ * header tag. In order to differentiate
+ * between those signals one needs to look
+ * at the arity part of the header (see
+ * erts_proc_sig_queue.h).
+ */
+
+#define ERTS_SIG_IS_NON_MSG_TAG(Tag) \
+ (is_external_pid_header((Tag)))
+
+#define ERTS_SIG_IS_NON_MSG(Sig) \
+ ERTS_SIG_IS_NON_MSG_TAG(((ErtsSignal *) (Sig))->common.tag)
+
+#define ERTS_SIG_IS_INTERNAL_MSG_TAG(Tag) \
+ (!is_header((Tag)))
+#define ERTS_SIG_IS_INTERNAL_MSG(Sig) \
+ ERTS_SIG_IS_INTERNAL_MSG_TAG(((ErtsSignal *) (Sig))->common.tag)
+
+#define ERTS_SIG_IS_EXTERNAL_MSG_TAG(Tag) \
+ ((Tag) == THE_NON_VALUE)
+#define ERTS_SIG_IS_EXTERNAL_MSG(Sig) \
+ ERTS_SIG_IS_EXTERNAL_MSG_TAG(((ErtsSignal *) (Sig))->common.tag)
+
+#define ERTS_SIG_IS_MSG_TAG(Tag) \
+ (!ERTS_SIG_IS_NON_MSG_TAG(Tag))
+#define ERTS_SIG_IS_MSG(Sig) \
+ ERTS_SIG_IS_MSG_TAG(((ErtsSignal *) (Sig))->common.tag)
+
+typedef union {
+ ErtsSignalCommon common;
+ ErtsMessageRef msg;
+} ErtsSignal;
+
+typedef struct {
+ /* pointers to next pointers pointing to... */
+ ErtsMessage **next; /* ... next (non-message) signal */
+ ErtsMessage **last; /* ... next (non-message) signal */
+} ErtsMsgQNMSigs;
+
/* Size of default message buffer (erl_message.c) */
#define ERL_MESSAGE_BUF_SZ 500
typedef struct {
- ErtsMessage* first;
- ErtsMessage** last; /* point to the last next pointer */
- ErtsMessage** save;
- Sint len; /* queue length */
+ /* inner queue */
+ ErtsMessage *first;
+ ErtsMessage **last; /* point to the last next pointer */
+ ErtsMessage **save;
- /*
- * The following field is used by the recv_mark/1 and
- * recv_set/1 instructions.
- */
- ErtsMessage** saved_last; /* saved last pointer */
-} ErlMessageQueue;
+ /* middle queue */
+ ErtsMessage *cont;
+ ErtsMessage **cont_last;
+ ErtsMsgQNMSigs nmsigs;
+ /* Common for inner and middle queue */
+ ErtsMessage **saved_last; /* saved last pointer */
+ Sint len; /* message queue length (inner+middle) */
+} ErtsSignalPrivQueues;
typedef struct {
ErtsMessage* first;
ErtsMessage** last; /* point to the last next pointer */
Sint len; /* queue length */
-} ErlMessageInQueue;
+ ErtsMsgQNMSigs nmsigs;
+} ErtsSignalInQueue;
typedef struct erl_trace_message_queue__ {
struct erl_trace_message_queue__ *next; /* point to the next receiver */
@@ -188,9 +245,46 @@ typedef struct erl_trace_message_queue__ {
Sint len; /* queue length */
} ErlTraceMessageQueue;
+#define ERTS_RECV_MARK_SAVE(P) \
+ do { \
+ erts_proc_lock((P), ERTS_PROC_LOCK_MSGQ); \
+ if ((P)->sig_inq.first) \
+ erts_proc_sig_fetch((P)); \
+ erts_proc_unlock((P), ERTS_PROC_LOCK_MSGQ); \
+ if ((P)->sig_qs.cont) { \
+ (P)->sig_qs.saved_last = (P)->sig_qs.cont_last; \
+ (P)->flags |= F_DEFERRED_SAVED_LAST; \
+ } \
+ else { \
+ (P)->sig_qs.saved_last = (P)->sig_qs.last; \
+ (P)->flags &= ~F_DEFERRED_SAVED_LAST; \
+ } \
+ } while (0)
+
+#define ERTS_RECV_MARK_SET(P) \
+ do { \
+ if ((P)->sig_qs.saved_last) { \
+ if ((P)->flags & F_DEFERRED_SAVED_LAST) { \
+ /* Points to middle queue; use end of inner */ \
+ (P)->sig_qs.save = (P)->sig_qs.last; \
+ ASSERT(!PEEK_MESSAGE((P))); \
+ } \
+ else { \
+ /* Points to inner queue; safe to use */ \
+ (P)->sig_qs.save = (P)->sig_qs.saved_last; \
+ } \
+ } \
+ } while (0)
+
+#define ERTS_RECV_MARK_CLEAR(P) \
+ do { \
+ (P)->sig_qs.saved_last = NULL; \
+ (P)->flags &= ~F_DEFERRED_SAVED_LAST; \
+ } while (0)
+
/* Get "current" message */
-#define PEEK_MESSAGE(p) (*(p)->msg.save)
+#define PEEK_MESSAGE(p) (*(p)->sig_qs.save)
#ifdef USE_VM_PROBES
#define LINK_MESSAGE_DTAG(mp, dt) ERL_MESSAGE_DT_UTAG(mp) = dt
@@ -198,58 +292,69 @@ typedef struct erl_trace_message_queue__ {
#define LINK_MESSAGE_DTAG(mp, dt)
#endif
-#define LINK_MESSAGE_IMPL(p, first_msg, last_msg, num_msgs, where) do { \
- *(p)->where.last = (first_msg); \
- (p)->where.last = (last_msg); \
- (p)->where.len += (num_msgs); \
- } while(0)
+#ifdef USE_VM_PROBES
+# define ERTS_MSG_RECV_TRACED(P) \
+ ((ERTS_TRACE_FLAGS((P)) & F_TRACE_RECEIVE) \
+ || DTRACE_ENABLED(message_queued))
+#else
+# define ERTS_MSG_RECV_TRACED(P) \
+ (ERTS_TRACE_FLAGS((P)) & F_TRACE_RECEIVE)
+
+#endif
/* Add message last in private message queue */
-#define LINK_MESSAGE_PRIVQ(p, first_msg, last_msg, len) \
+#define LINK_MESSAGE_PRIVQ(p, first_msg, last_msg, num_msgs) \
do { \
- LINK_MESSAGE_IMPL(p, first_msg, last_msg, len, msg); \
+ ERTS_HDBG_CHECK_SIGNAL_PRIV_QUEUE__((p), "before"); \
+ if ((p)->sig_qs.cont || ERTS_MSG_RECV_TRACED((p))) { \
+ *(p)->sig_qs.cont_last = (first_msg); \
+ (p)->sig_qs.cont_last = (last_msg); \
+ } \
+ else { \
+ *(p)->sig_qs.last = (first_msg); \
+ (p)->sig_qs.last = (last_msg); \
+ } \
+ (p)->sig_qs.len += (num_msgs); \
+ ERTS_HDBG_CHECK_SIGNAL_PRIV_QUEUE__((p), "after"); \
} while (0)
/* Add message last_msg in message queue */
-#define LINK_MESSAGE(p, first_msg, last_msg, len) \
- LINK_MESSAGE_IMPL(p, first_msg, last_msg, len, msg_inq)
-
-#define ERTS_MSGQ_MV_INQ2PRIVQ(p) \
- do { \
- if (p->msg_inq.first) { \
- *p->msg.last = p->msg_inq.first; \
- p->msg.last = p->msg_inq.last; \
- p->msg.len += p->msg_inq.len; \
- p->msg_inq.first = NULL; \
- p->msg_inq.last = &p->msg_inq.first; \
- p->msg_inq.len = 0; \
- } \
- } while (0)
-
+#define LINK_MESSAGE(p, first_msg, last_msg, num_msgs) \
+ do { \
+ ERTS_HDBG_CHECK_SIGNAL_IN_QUEUE__((p), "before"); \
+ *(p)->sig_inq.last = (first_msg); \
+ (p)->sig_inq.last = (last_msg); \
+ (p)->sig_inq.len += (num_msgs); \
+ ERTS_HDBG_CHECK_SIGNAL_IN_QUEUE__((p), "before"); \
+ } while(0)
/* Unlink current message */
-#define UNLINK_MESSAGE(p,msgp) do { \
- ErtsMessage* __mp = (msgp)->next; \
- *(p)->msg.save = __mp; \
- (p)->msg.len--; \
- if (__mp == NULL) \
- (p)->msg.last = (p)->msg.save; \
-} while(0)
+#define UNLINK_MESSAGE(p,msgp) \
+ do { \
+ ErtsMessage *mp__ = (msgp)->next; \
+ ERTS_HDBG_CHECK_SIGNAL_PRIV_QUEUE__((p), "before"); \
+ *(p)->sig_qs.save = mp__; \
+ (p)->sig_qs.len--; \
+ if (mp__ == NULL) \
+ (p)->sig_qs.last = (p)->sig_qs.save; \
+ ERTS_HDBG_CHECK_SIGNAL_PRIV_QUEUE__((p), "after"); \
+ } while(0)
/*
* Reset message save point (after receive match).
* Also invalidate the saved position since it may no
* longer be safe to use.
*/
-#define JOIN_MESSAGE(p) do { \
- (p)->msg.save = &(p)->msg.first; \
- (p)->msg.saved_last = 0; \
-} while(0)
+#define JOIN_MESSAGE(p) \
+ do { \
+ (p)->sig_qs.save = &(p)->sig_qs.first; \
+ ERTS_RECV_MARK_CLEAR((p)); \
+ } while(0)
/* Save current message */
#define SAVE_MESSAGE(p) \
- (p)->msg.save = &(*(p)->msg.save)->next
+ (p)->sig_qs.save = &(*(p)->sig_qs.save)->next
#define ERTS_SND_FLG_NO_SEQ_TRACE (((unsigned) 1) << 0)
@@ -276,6 +381,7 @@ typedef struct erl_trace_message_queue__ {
(MP)->next = NULL; \
ERL_MESSAGE_TERM(MP) = THE_NON_VALUE; \
ERL_MESSAGE_TOKEN(MP) = NIL; \
+ ERL_MESSAGE_FROM(MP) = NIL; \
ERL_MESSAGE_DT_UTAG_INIT(MP); \
MP->data.attached = NULL; \
} while (0)
@@ -288,7 +394,7 @@ void free_message_buffer(ErlHeapFragment *);
void erts_queue_dist_message(Process*, ErtsProcLocks, ErtsDistExternal *, Eterm, Eterm);
Sint erts_queue_message(Process*, ErtsProcLocks,ErtsMessage*, Eterm, Eterm);
Sint erts_queue_messages(Process*, ErtsProcLocks,
- ErtsMessage*, ErtsMessage**, Uint, Eterm);
+ ErtsMessage*, ErtsMessage**, Uint);
void erts_deliver_exit_message(Eterm, Process*, ErtsProcLocks *, Eterm, Eterm);
Sint erts_send_message(Process*, Process*, ErtsProcLocks*, Eterm, unsigned);
void erts_link_mbuf_to_proc(Process *proc, ErlHeapFragment *bp);
@@ -305,16 +411,6 @@ int erts_decode_dist_message(Process *, ErtsProcLocks, ErtsMessage *, int);
void erts_cleanup_messages(ErtsMessage *mp);
-typedef struct {
- Uint size;
- ErtsMessage *msgp;
-} ErtsMessageInfo;
-
-Uint erts_prep_msgq_for_inspection(Process *c_p,
- Process *rp,
- ErtsProcLocks rp_locks,
- ErtsMessageInfo *mip);
-
void *erts_alloc_message_ref(void);
void erts_free_message_ref(void *);
@@ -356,12 +452,6 @@ ERTS_GLB_FORCE_INLINE ErtsMessage *erts_shrink_message(ErtsMessage *mp, Uint sz,
ERTS_GLB_FORCE_INLINE void erts_free_message(ErtsMessage *mp);
ERTS_GLB_INLINE Uint erts_used_frag_sz(const ErlHeapFragment*);
ERTS_GLB_INLINE Uint erts_msg_attached_data_size(ErtsMessage *msg);
-ERTS_GLB_INLINE void erts_msgq_update_internal_pointers(ErlMessageQueue *msgq,
- ErtsMessage **newpp,
- ErtsMessage **oldpp);
-ERTS_GLB_INLINE void erts_msgq_replace_msg_ref(ErlMessageQueue *msgq,
- ErtsMessage *newp,
- ErtsMessage **oldpp);
#define ERTS_MSG_COMBINED_HFRAG ((void *) 0x1)
@@ -465,28 +555,6 @@ ERTS_GLB_INLINE Uint erts_msg_attached_data_size(ErtsMessage *msg)
}
}
-ERTS_GLB_INLINE void
-erts_msgq_update_internal_pointers(ErlMessageQueue *msgq,
- ErtsMessage **newpp,
- ErtsMessage **oldpp)
-{
- if (msgq->save == oldpp)
- msgq->save = newpp;
- if (msgq->last == oldpp)
- msgq->last = newpp;
- if (msgq->saved_last == oldpp)
- msgq->saved_last = newpp;
-}
-
-ERTS_GLB_INLINE void
-erts_msgq_replace_msg_ref(ErlMessageQueue *msgq, ErtsMessage *newp, ErtsMessage **oldpp)
-{
- ErtsMessage *oldp = *oldpp;
- newp->next = oldp->next;
- erts_msgq_update_internal_pointers(msgq, &newp->next, &oldp->next);
- *oldpp = newp;
-}
-
#endif
Uint erts_mbuf_size(Process *p);
@@ -500,4 +568,17 @@ Uint erts_mbuf_size(Process *p);
# define ERTS_CHK_MBUF_SZ(P) ((void) 1)
#endif
+#define ERTS_FOREACH_SIG_PRIVQS(PROC, MVAR, CODE) \
+ do { \
+ int i__; \
+ ErtsMessage *msgs__[] = {(PROC)->sig_qs.first, \
+ (PROC)->sig_qs.cont}; \
+ for (i__ = 0; i__ < sizeof(msgs__)/sizeof(msgs__[0]); i__++) { \
+ ErtsMessage *MVAR; \
+ for (MVAR = msgs__[i__]; MVAR; MVAR = MVAR->next) { \
+ CODE; \
+ } \
+ } \
+ } while (0)
+
#endif
diff --git a/erts/emulator/beam/erl_monitor_link.c b/erts/emulator/beam/erl_monitor_link.c
new file mode 100644
index 0000000000..70f36fb6b7
--- /dev/null
+++ b/erts/emulator/beam/erl_monitor_link.c
@@ -0,0 +1,1341 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB 2018. 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%
+ */
+
+/*
+ * Description: Monitor and link implementation.
+ *
+ * Author: Rickard Green
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include "sys.h"
+#include <stddef.h>
+#include "global.h"
+#include "erl_node_tables.h"
+#include "erl_monitor_link.h"
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\
+ * Red-black tree implementation used for monitors and links. *
+\* */
+
+static ERTS_INLINE Eterm
+ml_get_key(ErtsMonLnkNode *mln)
+{
+ char *ptr = (char *) mln;
+ ptr += mln->key_offset;
+
+#ifdef ERTS_ML_DEBUG
+ switch (mln->type) {
+ case ERTS_MON_TYPE_PROC:
+ case ERTS_MON_TYPE_PORT:
+ case ERTS_MON_TYPE_DIST_PROC:
+ case ERTS_MON_TYPE_TIME_OFFSET:
+ case ERTS_MON_TYPE_RESOURCE: {
+ ErtsMonitorData *mdp = erts_monitor_to_data(mln);
+ ERTS_ML_ASSERT(&mdp->ref == (Eterm *) ptr);
+ break;
+ }
+ case ERTS_LNK_TYPE_PROC:
+ case ERTS_LNK_TYPE_DIST_PROC:
+ case ERTS_LNK_TYPE_PORT:
+ case ERTS_MON_TYPE_NODE:
+ case ERTS_MON_TYPE_NODES:
+ case ERTS_MON_TYPE_SUSPEND:
+ ERTS_ML_ASSERT(&mln->other.item == (Eterm *) ptr);
+ break;
+ default:
+ ERTS_INTERNAL_ERROR("Invalid type");
+ break;
+ }
+#endif
+
+ return *((Eterm *) ptr);
+}
+
+/*
+ * Comparison functions for valid *and only valid* keys. That
+ * is, if the set of valid keys is changed, this function needs
+ * to be updated...
+ *
+ * Note that this function does *not* order terms according to
+ * term order, and that the order may vary between emulator
+ * restarts...
+ */
+static int
+ml_cmp_keys(Eterm key1, Eterm key2)
+{
+ /*
+ * A monitor key is either an internal pid, a (nodename) atom,
+ * a small (monitor nodes bitmask), an ordinary internal ref,
+ * or an external ref.
+ *
+ * Order between monitors with keys of different types:
+ * internal pid < atom < small < internal ref < external ref
+ *
+ * A link key is either a pid, an internal port
+ *
+ * Order between links with keys of different types:
+ * internal pid < internal port < external pid
+ *
+ */
+ ERTS_ML_ASSERT(is_internal_pid(key1)
+ || is_internal_port(key1)
+ || is_atom(key1)
+ || is_small(key1)
+ || is_external_pid(key1)
+ || is_internal_ordinary_ref(key1)
+ || is_external_ref(key1));
+
+ ERTS_ML_ASSERT(is_internal_pid(key2)
+ || is_internal_port(key2)
+ || is_atom(key2)
+ || is_small(key2)
+ || is_external_pid(key2)
+ || is_internal_ordinary_ref(key2)
+ || is_external_ref(key2));
+
+ if (is_immed(key1)) {
+ int key1_tag, key2_tag;
+
+ ERTS_ML_ASSERT(is_internal_pid(key1)
+ || is_internal_port(key1)
+ || is_atom(key1)
+ || is_small(key1));
+
+ if (key1 == key2)
+ return 0;
+
+ if (is_boxed(key2))
+ return -1;
+
+ ERTS_ML_ASSERT(is_internal_pid(key2)
+ || is_internal_port(key2)
+ || is_atom(key2)
+ || is_small(key2));
+
+ key1_tag = (int) (key1 & _TAG_IMMED1_MASK);
+ key2_tag = (int) (key2 & _TAG_IMMED1_MASK);
+
+ if (key1_tag != key2_tag)
+ return key1_tag - key2_tag;
+
+ ASSERT((is_atom(key1) && is_atom(key2))
+ || (is_small(key1) && is_small(key2))
+ || (is_internal_pid(key1) && is_internal_pid(key2))
+ || (is_internal_port(key1) && is_internal_port(key2)));
+
+ return key1 < key2 ? -1 : 1;
+ }
+ else {
+ Eterm *w1, hdr1;
+
+ ERTS_ML_ASSERT(is_boxed(key1));
+
+ w1 = boxed_val(key1);
+ hdr1 = *w1;
+
+ if ((hdr1 & _TAG_HEADER_MASK) == _TAG_HEADER_REF) {
+ Eterm *w2;
+
+ if (!is_internal_ref(key2))
+ return is_immed(key2) ? 1 : -1;
+
+ w2 = internal_ref_val(key2);
+
+ ERTS_ML_ASSERT(w1[0] == ERTS_REF_THING_HEADER);
+ ERTS_ML_ASSERT(w2[0] == ERTS_REF_THING_HEADER);
+
+ return sys_memcmp((void *) &w1[1], (void *) &w2[1],
+ (ERTS_REF_THING_SIZE - 1)*sizeof(Eterm));
+ }
+
+ ERTS_ML_ASSERT(is_external(key1));
+
+ if (is_not_external(key2))
+ return 1;
+ else {
+ Uint ndw1, ndw2;
+ ExternalThing *et1, *et2;
+ ErlNode *n1, *n2;
+
+ ERTS_ML_ASSERT((is_external_ref(key1) && is_external_ref(key2))
+ || (is_external_pid(key1) && is_external_pid(key2)));
+
+ et1 = (ExternalThing *) w1;
+ et2 = (ExternalThing *) external_val(key2);
+
+ n1 = et1->node;
+ n2 = et2->node;
+
+ if (n1 != n2) {
+ if (n1->sysname != n2->sysname)
+ return n1->sysname < n2->sysname ? -1 : 1;
+ ASSERT(n1->creation != n2->creation);
+ return n1->creation < n2->creation ? -1 : 1;
+ }
+
+ ndw1 = external_thing_data_words(et1);
+ ndw2 = external_thing_data_words(et1);
+ if (ndw1 != ndw2)
+ return ndw1 < ndw2 ? -1 : 1;
+
+ return sys_memcmp((void *) &et1->data.ui[0],
+ (void *) &et2->data.ui[0],
+ ndw1*sizeof(Eterm));
+ }
+ }
+}
+
+#define ERTS_ML_TPFLG_RED (((UWord) 1) << 0)
+
+#define ERTS_ML_TPFLGS_MASK (ERTS_ML_TPFLG_RED)
+
+#define ERTS_RBT_PREFIX mon_lnk
+#define ERTS_RBT_T ErtsMonLnkNode
+#define ERTS_RBT_KEY_T Eterm
+#define ERTS_RBT_FLAGS_T UWord
+#define ERTS_RBT_INIT_EMPTY_TNODE(T) \
+ do { \
+ (T)->node.tree.parent = (UWord) NULL; \
+ (T)->node.tree.right = NULL; \
+ (T)->node.tree.left = NULL; \
+ } while (0)
+#define ERTS_RBT_IS_RED(T) \
+ (!!((T)->node.tree.parent & ERTS_ML_TPFLG_RED))
+#define ERTS_RBT_SET_RED(T) \
+ ((T)->node.tree.parent |= ERTS_ML_TPFLG_RED)
+#define ERTS_RBT_IS_BLACK(T) \
+ (!ERTS_RBT_IS_RED((T)))
+#define ERTS_RBT_SET_BLACK(T) \
+ ((T)->node.tree.parent &= ~ERTS_ML_TPFLG_RED)
+#define ERTS_RBT_GET_FLAGS(T) \
+ ((T)->node.tree.parent & ERTS_ML_TPFLGS_MASK)
+#define ERTS_RBT_SET_FLAGS(T, F) \
+ do { \
+ ERTS_ML_ASSERT((((UWord) (F)) & ~ERTS_ML_TPFLGS_MASK) == 0); \
+ (T)->node.tree.parent &= ~ERTS_ML_TPFLGS_MASK; \
+ (T)->node.tree.parent |= (F); \
+ } while (0)
+#define ERTS_RBT_GET_PARENT(T) \
+ ((ERTS_RBT_T *) ((T)->node.tree.parent & ~ERTS_ML_TPFLGS_MASK))
+#define ERTS_RBT_SET_PARENT(T, P) \
+ do { \
+ ERTS_ML_ASSERT((((UWord) (P)) & ERTS_ML_TPFLGS_MASK) == 0); \
+ (T)->node.tree.parent &= ERTS_ML_TPFLGS_MASK; \
+ (T)->node.tree.parent |= (UWord) (P); \
+ } while (0)
+#define ERTS_RBT_GET_RIGHT(T) ((T)->node.tree.right)
+#define ERTS_RBT_SET_RIGHT(T, R) ((T)->node.tree.right = (R))
+#define ERTS_RBT_GET_LEFT(T) ((T)->node.tree.left)
+#define ERTS_RBT_SET_LEFT(T, L) ((T)->node.tree.left = (L))
+#define ERTS_RBT_GET_KEY(T) (ml_get_key((T)))
+#define ERTS_RBT_CMP_KEYS(KX, KY) (ml_cmp_keys((KX), (KY)))
+#define ERTS_RBT_WANT_DELETE
+#define ERTS_RBT_WANT_LOOKUP
+#define ERTS_RBT_WANT_LOOKUP_INSERT
+#define ERTS_RBT_WANT_LOOKUP_CREATE
+#define ERTS_RBT_WANT_INSERT
+#define ERTS_RBT_WANT_REPLACE
+#define ERTS_RBT_WANT_FOREACH
+#define ERTS_RBT_WANT_FOREACH_YIELDING
+#define ERTS_RBT_WANT_FOREACH_DESTROY
+#define ERTS_RBT_WANT_FOREACH_DESTROY_YIELDING
+#define ERTS_RBT_UNDEF
+
+#include "erl_rbtree.h"
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\
+ * Tree Operations *
+\* */
+
+static ErtsMonLnkNode *
+ml_rbt_lookup(ErtsMonLnkNode *root, Eterm ref)
+{
+ ErtsMonLnkNode *ml = mon_lnk_rbt_lookup(root, ref);
+ ASSERT(!ml || (ml->flags & ERTS_ML_FLG_IN_TABLE));
+ return ml;
+}
+
+static ErtsMonLnkNode *
+ml_rbt_lookup_insert(ErtsMonLnkNode **root, ErtsMonLnkNode *ml)
+{
+ ErtsMonLnkNode *res;
+ ERTS_ML_ASSERT(!(ml->flags & ERTS_ML_FLG_IN_TABLE));
+ res = mon_lnk_rbt_lookup_insert(root, ml);
+ if (!res)
+ ml->flags |= ERTS_ML_FLG_IN_TABLE;
+ return res;
+}
+
+static ErtsMonLnkNode *
+ml_rbt_lookup_create(ErtsMonLnkNode ** root, Eterm key,
+ ErtsMonLnkNode *(*create)(Eterm, void *),
+ void *carg, int *created)
+{
+ ErtsMonLnkNode *ml;
+ ml = mon_lnk_rbt_lookup_create(root, key, create, carg, created);
+ if (*created)
+ ml->flags |= ERTS_ML_FLG_IN_TABLE;
+ return ml;
+}
+
+static void
+ml_rbt_insert(ErtsMonLnkNode **root, ErtsMonLnkNode *ml)
+{
+#ifdef ERTS_ML_DEBUG
+ ErtsMonLnkNode *res;
+ ERTS_ML_ASSERT(!(ml->flags & ERTS_ML_FLG_IN_TABLE));
+ res = ml_rbt_lookup(*root, ml_get_key(ml));
+ ERTS_ML_ASSERT(res == NULL);
+#endif
+
+ mon_lnk_rbt_insert(root, ml);
+ ml->flags |= ERTS_ML_FLG_IN_TABLE;
+}
+
+static void
+ml_rbt_replace(ErtsMonLnkNode **root, ErtsMonLnkNode *old, ErtsMonLnkNode *new)
+{
+ ERTS_ML_ASSERT(old->flags & ERTS_ML_FLG_IN_TABLE);
+ ERTS_ML_ASSERT(!(new->flags & ERTS_ML_FLG_IN_TABLE));
+
+ mon_lnk_rbt_replace(root, old, new);
+ old->flags &= ~ERTS_ML_FLG_IN_TABLE;
+ new->flags |= ERTS_ML_FLG_IN_TABLE;
+}
+
+static void
+ml_rbt_delete(ErtsMonLnkNode **root, ErtsMonLnkNode *ml)
+{
+ ERTS_ML_ASSERT(ml->flags & ERTS_ML_FLG_IN_TABLE);
+ mon_lnk_rbt_delete(root, ml);
+ ml->flags &= ~ERTS_ML_FLG_IN_TABLE;
+}
+
+
+static void
+ml_rbt_foreach(ErtsMonLnkNode *root,
+ void (*func)(ErtsMonLnkNode *, void *),
+ void *arg)
+{
+ mon_lnk_rbt_foreach(root, func, arg);
+}
+
+typedef struct {
+ ErtsMonLnkNode *root;
+ mon_lnk_rbt_yield_state_t rbt_ystate;
+} ErtsMonLnkYieldState;
+
+static int
+ml_rbt_foreach_yielding(ErtsMonLnkNode *root,
+ void (*func)(ErtsMonLnkNode *, void *),
+ void *arg,
+ void **vyspp,
+ Sint limit)
+{
+ int res;
+ ErtsMonLnkYieldState ys = {root, ERTS_RBT_YIELD_STAT_INITER};
+ ErtsMonLnkYieldState *ysp;
+
+ ysp = (ErtsMonLnkYieldState *) *vyspp;
+ if (!ysp)
+ ysp = &ys;
+ res = mon_lnk_rbt_foreach_yielding(ysp->root, func, arg,
+ &ysp->rbt_ystate, limit);
+ if (res == 0) {
+ if (ysp != &ys)
+ erts_free(ERTS_ALC_T_ML_YIELD_STATE, ysp);
+ *vyspp = NULL;
+ }
+ else {
+
+ if (ysp == &ys) {
+ ysp = erts_alloc(ERTS_ALC_T_ML_YIELD_STATE,
+ sizeof(ErtsMonLnkYieldState));
+ sys_memcpy((void *) ysp, (void *) &ys,
+ sizeof(ErtsMonLnkYieldState));
+ }
+
+ *vyspp = (void *) ysp;
+ }
+
+ return res;
+}
+
+typedef struct {
+ void (*func)(ErtsMonLnkNode *, void *);
+ void *arg;
+} ErtsMonLnkForeachDeleteContext;
+
+static void
+rbt_wrap_foreach_delete(ErtsMonLnkNode *ml, void *vctxt)
+{
+ ErtsMonLnkForeachDeleteContext *ctxt = vctxt;
+ ERTS_ML_ASSERT(ml->flags & ERTS_ML_FLG_IN_TABLE);
+ ml->flags &= ~ERTS_ML_FLG_IN_TABLE;
+ ctxt->func(ml, ctxt->arg);
+}
+
+static void
+ml_rbt_foreach_delete(ErtsMonLnkNode **root,
+ void (*func)(ErtsMonLnkNode *, void *),
+ void *arg)
+{
+ ErtsMonLnkForeachDeleteContext ctxt;
+ ctxt.func = func;
+ ctxt.arg = arg;
+ mon_lnk_rbt_foreach_destroy(root,
+ rbt_wrap_foreach_delete,
+ (void *) &ctxt);
+}
+
+static int
+ml_rbt_foreach_delete_yielding(ErtsMonLnkNode **root,
+ void (*func)(ErtsMonLnkNode *, void *),
+ void *arg,
+ void **vyspp,
+ Sint limit)
+{
+ int res;
+ ErtsMonLnkYieldState ys = {*root, ERTS_RBT_YIELD_STAT_INITER};
+ ErtsMonLnkYieldState *ysp;
+ ErtsMonLnkForeachDeleteContext ctxt;
+ ctxt.func = func;
+ ctxt.arg = arg;
+
+ ysp = (ErtsMonLnkYieldState *) *vyspp;
+ if (!ysp) {
+ *root = NULL;
+ ysp = &ys;
+ }
+ res = mon_lnk_rbt_foreach_destroy_yielding(&ysp->root,
+ rbt_wrap_foreach_delete,
+ (void *) &ctxt,
+ &ysp->rbt_ystate,
+ limit);
+ if (res == 0) {
+ if (ysp != &ys)
+ erts_free(ERTS_ALC_T_ML_YIELD_STATE, ysp);
+ *vyspp = NULL;
+ }
+ else {
+
+ if (ysp == &ys) {
+ ysp = erts_alloc(ERTS_ALC_T_ML_YIELD_STATE,
+ sizeof(ErtsMonLnkYieldState));
+ sys_memcpy((void *) ysp, (void *) &ys,
+ sizeof(ErtsMonLnkYieldState));
+ }
+
+ *vyspp = (void *) ysp;
+ }
+
+ return res;
+}
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\
+ * List Operations *
+\* */
+
+static int
+ml_dl_list_foreach_yielding(ErtsMonLnkNode *list,
+ void (*func)(ErtsMonLnkNode *, void *),
+ void *arg,
+ void **vyspp,
+ Sint limit)
+{
+ Sint cnt = 0;
+ ErtsMonLnkNode *ml = (ErtsMonLnkNode *) *vyspp;
+
+ ERTS_ML_ASSERT(!ml || list);
+
+ if (!ml)
+ ml = list;
+
+ if (ml) {
+ do {
+ ERTS_ML_ASSERT(ml->flags & ERTS_ML_FLG_IN_TABLE);
+ func(ml, arg);
+ ml = ml->node.list.next;
+ cnt++;
+ } while (ml != list && cnt < limit);
+ if (ml != list) {
+ *vyspp = (void *) ml;
+ return 1; /* yield */
+ }
+ }
+
+ *vyspp = NULL;
+ return 0; /* done */
+}
+
+static int
+ml_dl_list_foreach_delete_yielding(ErtsMonLnkNode **list,
+ void (*func)(ErtsMonLnkNode *, void *),
+ void *arg,
+ void **vyspp,
+ Sint limit)
+{
+ Sint cnt = 0;
+ ErtsMonLnkNode *first = *list;
+ ErtsMonLnkNode *ml = (ErtsMonLnkNode *) *vyspp;
+
+ ERTS_ML_ASSERT(!ml || first);
+
+ if (!ml)
+ ml = first;
+
+ if (ml) {
+ do {
+ ErtsMonLnkNode *next = ml->node.list.next;
+ ERTS_ML_ASSERT(ml->flags & ERTS_ML_FLG_IN_TABLE);
+ ml->flags &= ~ERTS_ML_FLG_IN_TABLE;
+ func(ml, arg);
+ ml = next;
+ cnt++;
+ } while (ml != first && cnt < limit);
+ if (ml != first) {
+ *vyspp = (void *) ml;
+ return 1; /* yield */
+ }
+ }
+
+ *vyspp = NULL;
+ *list = NULL;
+ return 0; /* done */
+}
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\
+ * Misc *
+\* */
+
+ErtsMonLnkDist *
+erts_mon_link_dist_create(Eterm nodename)
+{
+ ErtsMonLnkDist *mld = erts_alloc(ERTS_ALC_T_ML_DIST,
+ sizeof(ErtsMonLnkDist));
+ mld->nodename = nodename;
+ mld->connection_id = ~((Uint32) 0);
+ erts_atomic_init_nob(&mld->refc, 1);
+ erts_mtx_init(&mld->mtx, "dist_entry_links", nodename,
+ ERTS_LOCK_FLAGS_CATEGORY_DISTRIBUTION);
+ mld->alive = !0;
+ mld->links = NULL;
+ mld->monitors = NULL;
+ mld->orig_name_monitors = NULL;
+ return mld;
+}
+
+void
+erts_mon_link_dist_destroy__(ErtsMonLnkDist *mld)
+{
+ ERTS_ML_ASSERT(erts_atomic_read_nob(&mld->refc) == 0);
+ ERTS_ML_ASSERT(!mld->alive);
+ ERTS_ML_ASSERT(!mld->links);
+ ERTS_ML_ASSERT(!mld->monitors);
+ ERTS_ML_ASSERT(!mld->orig_name_monitors);
+
+ erts_mtx_destroy(&mld->mtx);
+ erts_free(ERTS_ALC_T_ML_DIST, mld);
+}
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\
+ * Monitor Operations *
+\* */
+
+#ifdef ERTS_ML_DEBUG
+size_t erts_monitor_origin_offset;
+size_t erts_monitor_origin_key_offset;
+size_t erts_monitor_target_offset;
+size_t erts_monitor_target_key_offset;
+size_t erts_monitor_node_key_offset;
+#endif
+
+static ERTS_INLINE void
+monitor_init(void)
+{
+#ifdef ERTS_ML_DEBUG
+ erts_monitor_origin_offset = offsetof(ErtsMonitorData, origin);
+ erts_monitor_origin_key_offset = offsetof(ErtsMonitorData, ref);
+ ASSERT(erts_monitor_origin_key_offset >= erts_monitor_origin_offset);
+ erts_monitor_origin_key_offset -= erts_monitor_origin_offset;
+ erts_monitor_target_offset = offsetof(ErtsMonitorData, target);
+ erts_monitor_target_key_offset = offsetof(ErtsMonitorData, ref);
+ ASSERT(erts_monitor_target_key_offset >= erts_monitor_target_offset);
+ erts_monitor_target_key_offset -= erts_monitor_target_offset;
+ erts_monitor_node_key_offset = offsetof(ErtsMonitor, other.item);
+#endif
+}
+
+ErtsMonitor *
+erts_monitor_tree_lookup(ErtsMonitor *root, Eterm key)
+{
+ ERTS_ML_ASSERT(is_internal_ordinary_ref(key)
+ || is_external_ref(key)
+ || is_atom(key)
+ || is_small(key)
+ || is_internal_pid(key));
+ return (ErtsMonitor *) ml_rbt_lookup((ErtsMonLnkNode *) root, key);
+}
+
+ErtsMonitor *
+erts_monotor_tree_lookup_insert(ErtsMonitor **root, ErtsMonitor *mon)
+{
+ return (ErtsMonitor *) ml_rbt_lookup_insert((ErtsMonLnkNode **) root,
+ (ErtsMonLnkNode *) mon);
+}
+
+typedef struct {
+ Uint16 type;
+ Eterm origin;
+} ErtsMonitorCreateCtxt;
+
+static ErtsMonLnkNode *
+create_monitor(Eterm target, void *vcctxt)
+{
+ ErtsMonitorCreateCtxt *cctxt = vcctxt;
+ ErtsMonitorData *mdp = erts_monitor_create(cctxt->type,
+ NIL,
+ cctxt->origin,
+ target,
+ NIL);
+ ERTS_ML_ASSERT(ml_cmp_keys(ml_get_key(&mdp->origin), target) == 0);
+ return (ErtsMonLnkNode *) &mdp->origin;
+}
+
+ErtsMonitor *
+erts_monitor_tree_lookup_create(ErtsMonitor **root, int *created, Uint16 type,
+ Eterm origin, Eterm target)
+{
+ ErtsMonitor *res;
+ ErtsMonitorCreateCtxt cctxt = {type, origin};
+
+ ERTS_ML_ASSERT(type == ERTS_MON_TYPE_NODE || type == ERTS_MON_TYPE_NODES);
+
+ res = (ErtsMonitor *) ml_rbt_lookup_create((ErtsMonLnkNode **) root,
+ target, create_monitor,
+ (void *) &cctxt,
+ created);
+
+ ERTS_ML_ASSERT(res && erts_monitor_is_origin(res));
+
+ return res;
+}
+
+void
+erts_monitor_tree_insert(ErtsMonitor **root, ErtsMonitor *mon)
+{
+ ml_rbt_insert((ErtsMonLnkNode **) root, (ErtsMonLnkNode *) mon);
+}
+
+void
+erts_monitor_tree_replace(ErtsMonitor **root, ErtsMonitor *old, ErtsMonitor *new)
+{
+ ml_rbt_replace((ErtsMonLnkNode **) root,
+ (ErtsMonLnkNode *) old,
+ (ErtsMonLnkNode *) new);
+}
+
+void
+erts_monitor_tree_delete(ErtsMonitor **root, ErtsMonitor *mon)
+{
+ ml_rbt_delete((ErtsMonLnkNode **) root, (ErtsMonLnkNode *) mon);
+}
+
+void
+erts_monitor_tree_foreach(ErtsMonitor *root,
+ void (*func)(ErtsMonitor *, void *),
+ void *arg)
+{
+ ml_rbt_foreach((ErtsMonLnkNode *) root,
+ (void (*)(ErtsMonLnkNode*, void*)) func,
+ arg);
+}
+
+int
+erts_monitor_tree_foreach_yielding(ErtsMonitor *root,
+ void (*func)(ErtsMonitor *, void *),
+ void *arg,
+ void **vyspp,
+ Sint limit)
+{
+ return ml_rbt_foreach_yielding((ErtsMonLnkNode *) root,
+ (void (*)(ErtsMonLnkNode*, void*)) func,
+ arg, vyspp, limit);
+}
+
+void
+erts_monitor_tree_foreach_delete(ErtsMonitor **root,
+ void (*func)(ErtsMonitor *, void *),
+ void *arg)
+{
+ ml_rbt_foreach_delete((ErtsMonLnkNode **) root,
+ (void (*)(ErtsMonLnkNode*, void*)) func,
+ arg);
+}
+
+int
+erts_monitor_tree_foreach_delete_yielding(ErtsMonitor **root,
+ void (*func)(ErtsMonitor *, void *),
+ void *arg,
+ void **vyspp,
+ Sint limit)
+{
+ return ml_rbt_foreach_delete_yielding((ErtsMonLnkNode **) root,
+ (void (*)(ErtsMonLnkNode*, void*)) func,
+ arg, vyspp, limit);
+}
+
+void
+erts_monitor_list_foreach(ErtsMonitor *list,
+ void (*func)(ErtsMonitor *, void *),
+ void *arg)
+{
+ void *ystate = NULL;
+ while (ml_dl_list_foreach_yielding((ErtsMonLnkNode *) list,
+ (void (*)(ErtsMonLnkNode *, void *)) func,
+ arg, &ystate, (Sint) INT_MAX));
+}
+
+int
+erts_monitor_list_foreach_yielding(ErtsMonitor *list,
+ void (*func)(ErtsMonitor *, void *),
+ void *arg,
+ void **vyspp,
+ Sint limit)
+{
+ return ml_dl_list_foreach_yielding((ErtsMonLnkNode *) list,
+ (void (*)(ErtsMonLnkNode *, void *)) func,
+ arg, vyspp, limit);
+}
+
+void
+erts_monitor_list_foreach_delete(ErtsMonitor **list,
+ void (*func)(ErtsMonitor *, void *),
+ void *arg)
+{
+ void *ystate = NULL;
+ while (ml_dl_list_foreach_delete_yielding((ErtsMonLnkNode **) list,
+ (void (*)(ErtsMonLnkNode*, void*)) func,
+ arg, &ystate, (Sint) INT_MAX));
+}
+
+int
+erts_monitor_list_foreach_delete_yielding(ErtsMonitor **list,
+ void (*func)(ErtsMonitor *, void *),
+ void *arg,
+ void **vyspp,
+ Sint limit)
+{
+ return ml_dl_list_foreach_delete_yielding((ErtsMonLnkNode **) list,
+ (void (*)(ErtsMonLnkNode*, void*)) func,
+ arg, vyspp, limit);
+}
+
+ErtsMonitorData *
+erts_monitor_create(Uint16 type, Eterm ref, Eterm orgn, Eterm trgt, Eterm name)
+{
+ ErtsMonitorData *mdp;
+
+ switch (type) {
+ case ERTS_MON_TYPE_PROC:
+ case ERTS_MON_TYPE_PORT:
+ case ERTS_MON_TYPE_TIME_OFFSET:
+ if (is_nil(name)) {
+ ErtsMonitorDataHeap *mdhp;
+ ErtsORefThing *ortp;
+
+ ERTS_ML_ASSERT(is_immed(orgn) && is_immed(trgt));
+ ERTS_ML_ASSERT(is_internal_ordinary_ref(ref));
+
+ mdhp = erts_alloc(ERTS_ALC_T_MONITOR, sizeof(ErtsMonitorDataHeap));
+ mdp = &mdhp->md;
+ ERTS_ML_ASSERT(((void *) mdp) == ((void *) mdhp));
+
+ ortp = (ErtsORefThing *) (char *) internal_ref_val(ref);
+ mdhp->oref_thing = *ortp;
+ mdp->ref = make_internal_ref(&mdhp->oref_thing.header);
+
+ mdp->origin.other.item = trgt;
+ mdp->origin.offset = (Uint16) offsetof(ErtsMonitorData, origin);
+ mdp->origin.key_offset = (Uint16) offsetof(ErtsMonitorData, ref);
+ ERTS_ML_ASSERT(mdp->origin.key_offset >= mdp->origin.offset);
+ mdp->origin.key_offset -= mdp->origin.offset;
+ mdp->origin.flags = (Uint16) 0;
+ mdp->origin.type = type;
+
+ mdp->target.other.item = orgn;
+ mdp->target.offset = (Uint16) offsetof(ErtsMonitorData, target);
+ mdp->target.key_offset = (Uint16) offsetof(ErtsMonitorData, ref);
+ ERTS_ML_ASSERT(mdp->target.key_offset >= mdp->target.offset);
+ mdp->target.key_offset -= mdp->target.offset;
+ mdp->target.flags = ERTS_ML_FLG_TARGET;
+ mdp->target.type = type;
+ break;
+ }
+ case ERTS_MON_TYPE_DIST_PROC:
+ case ERTS_MON_TYPE_RESOURCE:
+ case ERTS_MON_TYPE_NODE:
+ case ERTS_MON_TYPE_NODES: {
+ ErtsMonitorDataExtended *mdep;
+ Uint size = sizeof(ErtsMonitorDataExtended) - sizeof(Eterm);
+ Uint rsz, osz, tsz;
+ Eterm *hp;
+ ErlOffHeap oh;
+ Uint16 name_flag = is_nil(name) ? ((Uint16) 0) : ERTS_ML_FLG_NAME;
+
+ rsz = is_immed(ref) ? 0 : size_object(ref);
+ tsz = is_immed(trgt) ? 0 : size_object(trgt);
+ if (type == ERTS_MON_TYPE_RESOURCE)
+ osz = 0;
+ else
+ osz = is_immed(orgn) ? 0 : size_object(orgn);
+
+ size += (rsz + osz + tsz) * sizeof(Eterm);
+
+ mdep = erts_alloc(ERTS_ALC_T_MONITOR_EXT, size);
+
+ ERTS_INIT_OFF_HEAP(&oh);
+
+ hp = &mdep->heap[0];
+
+ mdp = &mdep->md;
+ ERTS_ML_ASSERT(((void *) mdp) == ((void *) mdep));
+
+ mdp->ref = rsz ? copy_struct(ref, rsz, &hp, &oh) : ref;
+
+ mdp->origin.other.item = tsz ? copy_struct(trgt, tsz, &hp, &oh) : trgt;
+ mdp->origin.offset = (Uint16) offsetof(ErtsMonitorData, origin);
+ mdp->origin.flags = ERTS_ML_FLG_EXTENDED|name_flag;
+ mdp->origin.type = type;
+
+ if (type == ERTS_MON_TYPE_RESOURCE)
+ mdp->target.other.ptr = (void *) orgn;
+ else
+ mdp->target.other.item = osz ? copy_struct(orgn, osz, &hp, &oh) : orgn;
+ mdp->target.offset = (Uint16) offsetof(ErtsMonitorData, target);
+ mdp->target.flags = ERTS_ML_FLG_TARGET|ERTS_ML_FLG_EXTENDED|name_flag;
+ mdp->target.type = type;
+
+ if (type == ERTS_MON_TYPE_NODE || type == ERTS_MON_TYPE_NODES) {
+ mdep->u.refc = 0;
+ mdp->origin.key_offset = (Uint16) offsetof(ErtsMonitor, other.item);
+ mdp->target.key_offset = (Uint16) offsetof(ErtsMonitor, other.item);
+ ERTS_ML_ASSERT(!oh.first);
+ mdep->uptr.node_monitors = NULL;
+ }
+ else {
+ mdep->u.name = name;
+
+ mdp->origin.key_offset = (Uint16) offsetof(ErtsMonitorData, ref);
+ ERTS_ML_ASSERT(mdp->origin.key_offset >= mdp->origin.offset);
+ mdp->origin.key_offset -= mdp->origin.offset;
+
+ mdp->target.key_offset = (Uint16) offsetof(ErtsMonitorData, ref);
+ ERTS_ML_ASSERT(mdp->target.key_offset >= mdp->target.offset);
+ mdp->target.key_offset -= mdp->target.offset;
+
+ mdep->uptr.ohhp = oh.first;
+ }
+ mdep->dist = NULL;
+ break;
+ }
+ case ERTS_MON_TYPE_SUSPEND:
+ ERTS_INTERNAL_ERROR("Use erts_monitor_suspend_create() instead...");
+ mdp = NULL;
+ break;
+ default:
+ ERTS_INTERNAL_ERROR("Invalid monitor type");
+ mdp = NULL;
+ break;
+ }
+
+ erts_atomic32_init_nob(&mdp->refc, 2);
+
+ return mdp;
+}
+
+/*
+ * erts_monitor_destroy__() should only be called from
+ * erts_monitor_release() or erts_monitor_release_both().
+ */
+void
+erts_monitor_destroy__(ErtsMonitorData *mdp)
+{
+ ERTS_ML_ASSERT(erts_atomic32_read_nob(&mdp->refc) == 0);
+ ERTS_ML_ASSERT(!(mdp->origin.flags & ERTS_ML_FLG_IN_TABLE));
+ ERTS_ML_ASSERT(!(mdp->target.flags & ERTS_ML_FLG_IN_TABLE));
+ ERTS_ML_ASSERT((mdp->origin.flags & ERTS_ML_FLGS_SAME)
+ == (mdp->target.flags & ERTS_ML_FLGS_SAME));
+ ERTS_ML_ASSERT(mdp->origin.type != ERTS_MON_TYPE_SUSPEND);
+
+ if (!(mdp->origin.flags & ERTS_ML_FLG_EXTENDED))
+ erts_free(ERTS_ALC_T_MONITOR, mdp);
+ else {
+ ErtsMonitorDataExtended *mdep = (ErtsMonitorDataExtended *) mdp;
+ ErlOffHeap oh;
+ if (mdp->origin.type == ERTS_MON_TYPE_NODE)
+ ERTS_ML_ASSERT(!mdep->uptr.node_monitors);
+ else if (mdep->uptr.ohhp) {
+ ERTS_INIT_OFF_HEAP(&oh);
+ oh.first = mdep->uptr.ohhp;
+ erts_cleanup_offheap(&oh);
+ }
+ if (mdep->dist)
+ erts_mon_link_dist_dec_refc(mdep->dist);
+ erts_free(ERTS_ALC_T_MONITOR_EXT, mdp);
+ }
+}
+
+void
+erts_monitor_set_dead_dist(ErtsMonitor *mon, Eterm nodename)
+{
+ ErtsMonitorDataExtended *mdep;
+ mdep = (ErtsMonitorDataExtended *) erts_monitor_to_data(mon);
+
+ ERTS_ML_ASSERT(mon->flags & ERTS_ML_FLG_EXTENDED);
+ ERTS_ML_ASSERT(mon->type == ERTS_MON_TYPE_DIST_PROC);
+ ERTS_ML_ASSERT(!mdep->dist);
+
+ mdep->dist = erts_mon_link_dist_create(nodename);
+ mdep->dist->alive = 0;
+}
+
+Uint
+erts_monitor_size(ErtsMonitor *mon)
+{
+ Uint size, refc;
+ ErtsMonitorData *mdp = erts_monitor_to_data(mon);
+
+ ERTS_ML_ASSERT(mon->type != ERTS_MON_TYPE_SUSPEND);
+
+ if (!(mon->flags & ERTS_ML_FLG_EXTENDED))
+ size = sizeof(ErtsMonitorDataHeap);
+ else {
+ ErtsMonitorDataExtended *mdep;
+ Uint hsz = 0;
+
+ mdep = (ErtsMonitorDataExtended *) mdp;
+
+ if (mon->type != ERTS_MON_TYPE_NODE
+ && mon->type != ERTS_MON_TYPE_NODES) {
+ if (!is_immed(mdep->md.ref))
+ hsz += NC_HEAP_SIZE(mdep->md.ref);
+ if (mon->type == ERTS_MON_TYPE_DIST_PROC) {
+ if (!is_immed(mdep->md.origin.other.item))
+ hsz += NC_HEAP_SIZE(mdep->md.origin.other.item);
+ if (!is_immed(mdep->md.target.other.item))
+ hsz += NC_HEAP_SIZE(mdep->md.target.other.item);
+ }
+ }
+ size = sizeof(ErtsMonitorDataExtended) + (hsz - 1)*sizeof(Eterm);
+ }
+
+ refc = (Uint) erts_atomic32_read_nob(&mdp->refc);
+ ASSERT(refc > 0);
+
+ return size / refc;
+}
+
+
+/* suspend monitors... */
+
+ErtsMonitorSuspend *
+erts_monitor_suspend_create(Eterm pid)
+{
+ ErtsMonitorSuspend *msp;
+
+ ERTS_ML_ASSERT(is_internal_pid(pid));
+
+ msp = erts_alloc(ERTS_ALC_T_SUSPEND_MON,
+ sizeof(ErtsMonitorSuspend));
+ msp->mon.offset = (Uint16) offsetof(ErtsMonitorSuspend, mon);
+ msp->mon.key_offset = (Uint16) offsetof(ErtsMonitor, other.item);
+ msp->mon.other.item = pid;
+ msp->mon.flags = 0;
+ msp->mon.type = ERTS_MON_TYPE_SUSPEND;
+ msp->pending = 0;
+ msp->active = 0;
+ return msp;
+}
+
+static ErtsMonLnkNode *
+create_monitor_suspend(Eterm pid, void *unused)
+{
+ ErtsMonitorSuspend *msp = erts_monitor_suspend_create(pid);
+ return (ErtsMonLnkNode *) &msp->mon;
+}
+
+ErtsMonitorSuspend *
+erts_monitor_suspend_tree_lookup_create(ErtsMonitor **root, int *created,
+ Eterm pid)
+{
+ ErtsMonitor *mon;
+ mon = (ErtsMonitor *) ml_rbt_lookup_create((ErtsMonLnkNode **) root,
+ pid, create_monitor_suspend,
+ NULL,
+ created);
+ return erts_monitor_suspend(mon);
+}
+
+void
+erts_monitor_suspend_destroy(ErtsMonitorSuspend *msp)
+{
+ ERTS_ML_ASSERT(!(msp->mon.flags & ERTS_ML_FLG_IN_TABLE));
+ erts_free(ERTS_ALC_T_SUSPEND_MON, msp);
+}
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\
+ * Link Operations *
+ * *
+\* */
+
+#ifdef ERTS_ML_DEBUG
+size_t erts_link_a_offset;
+size_t erts_link_b_offset;
+size_t erts_link_key_offset;
+#endif
+
+static ERTS_INLINE void
+link_init(void)
+{
+#ifdef ERTS_ML_DEBUG
+ erts_link_a_offset = offsetof(ErtsLinkData, a);
+ erts_link_b_offset = offsetof(ErtsLinkData, b);
+ erts_link_key_offset = offsetof(ErtsLink, other.item);
+#endif
+}
+
+ErtsLink *
+erts_link_tree_lookup(ErtsLink *root, Eterm key)
+{
+ ASSERT(is_pid(key) || is_internal_port(key));
+ return (ErtsLink *) ml_rbt_lookup((ErtsMonLnkNode *) root, key);
+}
+
+ErtsLink *
+erts_link_tree_lookup_insert(ErtsLink **root, ErtsLink *lnk)
+{
+ return (ErtsLink *) ml_rbt_lookup_insert((ErtsMonLnkNode **) root,
+ (ErtsMonLnkNode *) lnk);
+}
+
+typedef struct {
+ Uint16 type;
+ Eterm a;
+} ErtsLinkCreateCtxt;
+
+static ErtsMonLnkNode *
+create_link(Eterm b, void *vcctxt)
+{
+ ErtsLinkCreateCtxt *cctxt = vcctxt;
+ ErtsLinkData *ldp = erts_link_create(cctxt->type, cctxt->a, b);
+ ERTS_ML_ASSERT(ml_cmp_keys(ldp->a.other.item, b) == 0);
+ return (ErtsMonLnkNode *) &ldp->a;
+}
+
+ErtsLink *erts_link_tree_lookup_create(ErtsLink **root, int *created,
+ Uint16 type, Eterm this,
+ Eterm other)
+{
+ ErtsLinkCreateCtxt cctxt;
+ cctxt.type = type;
+ cctxt.a = this;
+ return (ErtsLink *) ml_rbt_lookup_create((ErtsMonLnkNode **) root,
+ other, create_link,
+ (void *) &cctxt,
+ created);
+}
+
+void
+erts_link_tree_insert(ErtsLink **root, ErtsLink *lnk)
+{
+ ml_rbt_insert((ErtsMonLnkNode **) root, (ErtsMonLnkNode *) lnk);
+}
+
+void
+erts_link_tree_replace(ErtsLink **root, ErtsLink *old, ErtsLink *new)
+{
+ ml_rbt_replace((ErtsMonLnkNode **) root,
+ (ErtsMonLnkNode *) old,
+ (ErtsMonLnkNode *) new);
+}
+
+void
+erts_link_tree_delete(ErtsLink **root, ErtsLink *lnk)
+{
+ ml_rbt_delete((ErtsMonLnkNode **) root, (ErtsMonLnkNode *) lnk);
+}
+
+void
+erts_link_tree_foreach(ErtsLink *root,
+ void (*func)(ErtsLink *, void *),
+ void *arg)
+{
+ ml_rbt_foreach((ErtsMonLnkNode *) root,
+ (void (*)(ErtsMonLnkNode*, void*)) func,
+ arg);
+
+}
+
+int
+erts_link_tree_foreach_yielding(ErtsLink *root,
+ void (*func)(ErtsLink *, void *),
+ void *arg,
+ void **vyspp,
+ Sint limit)
+{
+ return ml_rbt_foreach_yielding((ErtsMonLnkNode *) root,
+ (void (*)(ErtsMonLnkNode*, void*)) func,
+ arg, vyspp, limit);
+}
+
+void
+erts_link_tree_foreach_delete(ErtsLink **root,
+ void (*func)(ErtsLink *, void *),
+ void *arg)
+{
+ ml_rbt_foreach_delete((ErtsMonLnkNode **) root,
+ (void (*)(ErtsMonLnkNode*, void*)) func,
+ arg);
+}
+
+int
+erts_link_tree_foreach_delete_yielding(ErtsLink **root,
+ void (*func)(ErtsLink *, void *),
+ void *arg,
+ void **vyspp,
+ Sint limit)
+{
+ return ml_rbt_foreach_delete_yielding((ErtsMonLnkNode **) root,
+ (void (*)(ErtsMonLnkNode*, void*)) func,
+ arg, vyspp, limit);
+}
+
+void
+erts_link_list_foreach(ErtsLink *list,
+ void (*func)(ErtsLink *, void *),
+ void *arg)
+{
+ void *ystate = NULL;
+ while (ml_dl_list_foreach_yielding((ErtsMonLnkNode *) list,
+ (void (*)(ErtsMonLnkNode *, void *)) func,
+ arg, &ystate, (Sint) INT_MAX));
+}
+
+int
+erts_link_list_foreach_yielding(ErtsLink *list,
+ void (*func)(ErtsLink *, void *),
+ void *arg,
+ void **vyspp,
+ Sint limit)
+{
+ return ml_dl_list_foreach_yielding((ErtsMonLnkNode *) list,
+ (void (*)(ErtsMonLnkNode *, void *)) func,
+ arg, vyspp, limit);
+}
+
+void
+erts_link_list_foreach_delete(ErtsLink **list,
+ void (*func)(ErtsLink *, void *),
+ void *arg)
+{
+ void *ystate = NULL;
+ while (ml_dl_list_foreach_delete_yielding((ErtsMonLnkNode **) list,
+ (void (*)(ErtsMonLnkNode*, void*)) func,
+ arg, &ystate, (Sint) INT_MAX));
+}
+
+int
+erts_link_list_foreach_delete_yielding(ErtsLink **list,
+ void (*func)(ErtsLink *, void *),
+ void *arg,
+ void **vyspp,
+ Sint limit)
+{
+ return ml_dl_list_foreach_delete_yielding((ErtsMonLnkNode **) list,
+ (void (*)(ErtsMonLnkNode*, void*)) func,
+ arg, vyspp, limit);
+}
+
+ErtsLinkData *
+erts_link_create(Uint16 type, Eterm a, Eterm b)
+{
+ ErtsLinkData *ldp;
+
+#ifdef ERTS_ML_DEBUG
+ switch (type) {
+ case ERTS_LNK_TYPE_PROC:
+ ERTS_ML_ASSERT(is_internal_pid(a) && is_internal_pid(a));
+ break;
+ case ERTS_LNK_TYPE_PORT:
+ ERTS_ML_ASSERT(is_internal_pid(a) || is_internal_pid(b));
+ ERTS_ML_ASSERT(is_internal_port(a) || is_internal_port(b));
+ break;
+ case ERTS_LNK_TYPE_DIST_PROC:
+ ERTS_ML_ASSERT(is_internal_pid(a) || is_internal_pid(b));
+ ERTS_ML_ASSERT(is_external_pid(a) || is_external_pid(b));
+ break;
+ default:
+ ERTS_INTERNAL_ERROR("Invalid link type");
+ break;
+ }
+#endif
+
+ if (type != ERTS_LNK_TYPE_DIST_PROC) {
+ ldp = erts_alloc(ERTS_ALC_T_LINK, sizeof(ErtsLinkData));
+
+ ldp->a.other.item = b;
+ ldp->a.flags = (Uint16) 0;
+
+ ldp->b.other.item = a;
+ ldp->b.flags = (Uint16) 0;
+ }
+ else {
+ ErtsLinkDataExtended *ldep;
+ Uint size, hsz;
+ Eterm *hp;
+ ErlOffHeap oh;
+
+ if (is_internal_pid(a))
+ hsz = NC_HEAP_SIZE(b);
+ else
+ hsz = NC_HEAP_SIZE(a);
+ ERTS_ML_ASSERT(hsz > 0);
+
+ size = sizeof(ErtsLinkDataExtended) - sizeof(Eterm);
+ size += hsz*sizeof(Eterm);
+
+ ldp = erts_alloc(ERTS_ALC_T_LINK_EXT, size);
+
+ ldp->a.flags = ERTS_ML_FLG_EXTENDED;
+ ldp->b.flags = ERTS_ML_FLG_EXTENDED;
+
+ ldep = (ErtsLinkDataExtended *) ldp;
+ hp = &ldep->heap[0];
+
+ ERTS_INIT_OFF_HEAP(&oh);
+
+ if (is_internal_pid(a)) {
+ ldp->a.other.item = STORE_NC(&hp, &oh, b);
+ ldp->b.other.item = a;
+ }
+ else {
+ ldp->a.other.item = b;
+ ldp->b.other.item = STORE_NC(&hp, &oh, a);
+ }
+
+ ldep->ohhp = oh.first;
+ ldep->dist = NULL;
+ }
+
+ erts_atomic32_init_nob(&ldp->refc, 2);
+
+ ldp->a.key_offset = (Uint16) offsetof(ErtsLink, other.item);
+ ldp->a.offset = (Uint16) offsetof(ErtsLinkData, a);
+ ldp->a.type = type;
+
+ ldp->b.key_offset = (Uint16) offsetof(ErtsLink, other.item);
+ ldp->b.offset = (Uint16) offsetof(ErtsLinkData, b);
+ ldp->b.type = type;
+
+ return ldp;
+}
+
+/*
+ * erts_link_destroy__() should only be called from
+ * erts_link_release() or erts_link_release_both().
+ */
+void
+erts_link_destroy__(ErtsLinkData *ldp)
+{
+ ERTS_ML_ASSERT(erts_atomic32_read_nob(&ldp->refc) == 0);
+ ERTS_ML_ASSERT(!(ldp->a.flags & ERTS_ML_FLG_IN_TABLE));
+ ERTS_ML_ASSERT(!(ldp->b.flags & ERTS_ML_FLG_IN_TABLE));
+ ERTS_ML_ASSERT((ldp->a.flags & ERTS_ML_FLGS_SAME)
+ == (ldp->b.flags & ERTS_ML_FLGS_SAME));
+
+ if (!(ldp->a.flags & ERTS_ML_FLG_EXTENDED))
+ erts_free(ERTS_ALC_T_LINK, ldp);
+ else {
+ ErtsLinkDataExtended *ldep = (ErtsLinkDataExtended *) ldp;
+ ErlOffHeap oh;
+ if (ldep->ohhp) {
+ ERTS_INIT_OFF_HEAP(&oh);
+ oh.first = ldep->ohhp;
+ erts_cleanup_offheap(&oh);
+ }
+ if (ldep->dist)
+ erts_mon_link_dist_dec_refc(ldep->dist);
+ erts_free(ERTS_ALC_T_LINK_EXT, ldep);
+ }
+}
+
+void
+erts_link_set_dead_dist(ErtsLink *lnk, Eterm nodename)
+{
+ ErtsLinkDataExtended *ldep;
+ ldep = (ErtsLinkDataExtended *) erts_link_to_data(lnk);
+
+ ERTS_ML_ASSERT(lnk->flags & ERTS_ML_FLG_EXTENDED);
+ ERTS_ML_ASSERT(lnk->type == ERTS_LNK_TYPE_DIST_PROC);
+ ERTS_ML_ASSERT(!ldep->dist);
+
+ ldep->dist = erts_mon_link_dist_create(nodename);
+ ldep->dist->alive = 0;
+}
+
+Uint
+erts_link_size(ErtsLink *lnk)
+{
+ Uint size, refc;
+ ErtsLinkData *ldp = erts_link_to_data(lnk);
+
+ if (!(lnk->flags & ERTS_ML_FLG_EXTENDED))
+ size = sizeof(ErtsLinkData);
+ else {
+ ErtsLinkDataExtended *ldep = (ErtsLinkDataExtended *) ldp;
+
+ ASSERT(lnk->type == ERTS_LNK_TYPE_DIST_PROC);
+ ASSERT(is_external_pid_header(ldep->heap[0]));
+
+ size = sizeof(ErtsLinkDataExtended);
+ size += thing_arityval(ldep->heap[0])*sizeof(Eterm);
+ }
+
+ refc = (Uint) erts_atomic32_read_nob(&ldp->refc);
+ ASSERT(refc > 0);
+
+ return size / refc;
+}
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\
+ * Misc *
+\* */
+
+void
+erts_monitor_link_init(void)
+{
+ monitor_init();
+ link_init();
+}
diff --git a/erts/emulator/beam/erl_monitor_link.h b/erts/emulator/beam/erl_monitor_link.h
new file mode 100644
index 0000000000..603aead8cc
--- /dev/null
+++ b/erts/emulator/beam/erl_monitor_link.h
@@ -0,0 +1,2326 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB 2018. 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%
+ */
+
+/*
+ * Description: Monitor and link implementation.
+ *
+ * === Monitors ==================================================
+ *
+ * The monitor data structure contains:
+ * - an 'origin' part that should be inserted in a data structure
+ * of the origin entity, i.e., the entity monitoring another
+ * entity
+ * - a 'target' part that should be inserted in a data structure
+ * of the target entity, i.e., the entity being monitored by
+ * another entity
+ * - a shared part that contains information shared between both
+ * origin and target entities
+ *
+ * That is, the two halves of the monitor as well as shared data
+ * are allocated in one single continuous memory block. The
+ * origin and target parts can separately each be inserted in
+ * either a (red-black) tree, a (circular double linked) list, or
+ * in a process signal queue.
+ *
+ * Each process and port contains:
+ * - a monitor list for local target monitors that is accessed
+ * via the ERTS_P_LT_MONITORS() macro, and
+ * - a monitor tree for other monitors that is accessed via the
+ * ERTS_P_MONITORS() macro
+ *
+ * These fields of processes/ports are protected by the main lock
+ * of the process/port. These are only intended to be accessed by
+ * the process/port itself. When setting up or tearing down a
+ * monitor one should *only* operate on the monitor tree/list of
+ * the currently executing process/port and send signals to the
+ * other involved process/port so it can modify its own monitor
+ * tree/list by itself (see erl_proc_sig_queue.h). One should
+ * absolutely *not* acquire the lock of the other involved
+ * process/port and operate on its monitor tree/list directly.
+ *
+ * Each dist entry contains a monitor/link dist structure that
+ * contains:
+ * - a monitor tree for origin named monitors that is accessed via
+ * the field 'orig_name_monitors', and
+ * - a monitor list for other monitors that is accessed via the
+ * 'monitors' field.
+ * Monitors in these fields contain information about all monitors
+ * over this specific connection.
+ *
+ * The fields of the dist structure are protected by a mutex in
+ * the same dist structure. Operations on these fields are
+ * normally performed by the locally involved process only,
+ * except when a connection is taken down. However in the case
+ * of distributed named monitors that originates from another
+ * node this is not possible. That is this operation is also
+ * performed from another context that the locally involved
+ * process.
+ *
+ * Access to monitor trees are performed using the
+ * erts_monitor_tree_* functions below. Access to monitor lists
+ * are performed using the erts_monitor_list_* functions below.
+ *
+ *
+ * The different monitor types:
+ *
+ * --- ERTS_MON_TYPE_PROC ----------------------------------------
+ *
+ * A local process (origin) monitors another local process
+ * (target).
+ *
+ * Origin:
+ * Other Item: Target process identifier
+ * Target:
+ * Other Item: Origin process identifier
+ * Shared:
+ * Key: Reference
+ * Name: Name (atom) if by name
+ *
+ * Valid keys are only ordinary internal references.
+ *
+ * Origin part of the monitor is stored in the monitor tree of
+ * origin process and target part of the monitor is stored in
+ * monitor list for local targets on the target process.
+ *
+ * --- ERTS_MON_TYPE_PORT ----------------------------------------
+ *
+ * A local process (origin) monitors a local port (target), or a
+ * local port (origin) monitors a local process (target).
+ *
+ * Origin:
+ * Other Item: Target process/port identifier
+ * Target:
+ * Other Item: Origin process/port identifier
+ * Shared:
+ * Key: Reference
+ * Name: Name (atom) if by name
+ *
+ * Valid keys are only ordinary internal references.
+ *
+ * Origin part of the monitor is stored in the monitor tree of
+ * origin process/port and target part of the monitor is stored
+ * in monitor list for local targets on the target process/port.
+ *
+ *
+ * --- ERTS_MON_TYPE_TIME_OFFSET ---------------------------------
+ *
+ * A local process (origin) monitors time offset (target)
+ *
+ * Origin:
+ * Other Item: clock_service
+ * Target:
+ * Other Item: Origin process identifier
+ * Shared:
+ * Key: Reference
+ *
+ * Valid keys are only ordinary internal references.
+ *
+ * Origin part of the monitor is stored in the monitor tree of
+ * origin process and target part of the monitor is stored in
+ * monitor list referred by the variable 'time_offset_monitors'
+ * (see erl_time_sup.c).
+ *
+ *
+ * --- ERTS_MON_TYPE_DIST_PROC -----------------------------------
+ *
+ * A local process (origin) monitors a remote process (target).
+ * Origin node on local process and target node on dist entry.
+ *
+ * Origin:
+ * Other Item: Remote process identifier/Node name
+ * if by name
+ * Target:
+ * Other Item: Local process identifier
+ * Shared:
+ * Key: Reference
+ * Name: Name (atom) if by name
+ * Dist: Pointer to dist structure
+ *
+ * Valid keys are only ordinary internal references.
+ *
+ * Origin part of the monitor is stored in the monitor tree of
+ * origin process and target part of the monitor is stored in
+ * monitor list referred by 'monitors' field of the dist
+ * structure.
+ *
+ *
+ * A remote process (origin) monitors a local process (target).
+ * Origin node on dist entry and target node on local process.
+ *
+ * Origin:
+ * Other Item: Local process identifier
+ * Target:
+ * Other Item: Remote process identifier
+ * Shared:
+ * Key: Reference
+ * Name: Name (atom) if by name
+ *
+ * Valid keys are only external references.
+ *
+ * If monitor by name, the origin part of the monitor is stored
+ * in the monitor tree referred by 'orig_name_monitors' field in
+ * dist structure; otherwise in the monitor list referred by
+ * 'monitors' field in dist structure. The target part of the
+ * monitor is stored in the monitor tree of the local target
+ * process.
+ *
+ *
+ * --- ERTS_MON_TYPE_RESOURCE ------------------------------------
+ *
+ * A NIF resource (origin) monitors a process (target).
+ *
+ * Origin:
+ * Other Item: Target process identifier
+ * Target:
+ * Other Ptr: Pointer to resource
+ * Shared:
+ * Key: Reference
+ *
+ * Valid keys are only ordinary internal references.
+ *
+ * Origin part of the monitor is stored in the monitor tree of
+ * origin resource (see erl_nif.c) and target part of the
+ * monitor is stored in monitor list for local targets on the
+ * target process.
+ *
+ * --- ERTS_MON_TYPE_NODE ----------------------------------------
+ *
+ * A local process (origin) monitors a distribution connection
+ * (target) via erlang:monitor_node().
+ *
+ * Origin:
+ * Other Item: Node name (atom)
+ * Key: Node name
+ * Target:
+ * Other Item: Origin process identifier
+ * Key: Origin process identifier
+ * Shared:
+ * Refc: Number of invocations
+ *
+ * Valid keys are only node-name atoms and internal process
+ * identifiers.
+ *
+ * Origin part of the monitor is stored in the monitor tree of
+ * origin process and target part of the monitor is stored in
+ * monitor list referred by 'monitors' field of the dist
+ * structure.
+ *
+ * --- ERTS_MON_TYPE_NODES ---------------------------------------
+ *
+ * A local process (origin) monitors all connections (target),
+ * via net_kernel:monitor_nodes().
+ *
+ * Origin:
+ * Other Item: Bit mask (small)
+ * Key: Bit mask
+ * Target:
+ * Other Item: Origin process identifier
+ * Key: Origin process identifier
+ * Shared:
+ * Refc: Number of invocations
+ *
+ * Valid keys are only small integers and internal process
+ * identifiers.
+ *
+ * Origin part of the monitor is stored in the monitor tree of
+ * origin process and target part of the monitor is stored in
+ * monitor list referred by the variable 'nodes_monitors' (see
+ * dist.c).
+ *
+ * --- ERTS_MON_TYPE_SUSPEND -------------------------------------
+ *
+ * Suspend monitor.
+ *
+ * Other Item: Suspendee process identifier
+ * Key: Suspendee process identifier
+ *
+ * Valid keys are only ordinary internal references.
+ *
+ * This type of monitor is a bit strange and the whole process
+ * suspend functionality should be improved...
+ *
+ *
+ *
+ * === Links =====================================================
+ *
+ * The link data structure contains:
+ * - an 'a' part that should be inserted in a data structure of
+ * one entity and contains the identifier of the other involved
+ * entity (b)
+ * - a 'b' part that should be inserted in a data structure of
+ * the other involved entity and contains the identifier of the
+ * other involved entity (a)
+ * - shared part that contains information shared between both
+ * involved entities
+ *
+ * That is, the two halves of the link as well as shared data
+ * are allocated in one single continuous memory block. The 'a'
+ * and the 'b' parts can separately each be inserted in either
+ * a (red-black) tree, a (circular double linked) list, or in a
+ * process signal queue.
+ *
+ * Each process and port contains:
+ * - a link tree for links that is accessed via the
+ * ERTS_P_LINKS() macro
+ *
+ * This field of processes/ports is protected by the main lock of
+ * the process/port. It is only intended to be accessed by the
+ * process/port itself. When setting up or tearing down a link
+ * one should *only* operate on the link tree of the currently
+ * executing process/port and send signals to the other involved
+ * process/port so it can modify its own monitor tree by itself
+ * (see erl_proc_sig_queue.h). One should absolutely *not*
+ * acquire the lock of the other involved process/port and
+ * operate on its link tree directly.
+ *
+ * Each dist entry contains a monitor/link dist structure that
+ * contains:
+ * - a link list for links via the 'links' field.
+ * Links in this field contain information about all links over
+ * this specific connection.
+ *
+ * The fields of the dist structure are protected by a mutex in
+ * the same dist structure. Operation on the 'links' fields are
+ * normally performed by the locally involved process only,
+ * except when a connection is taken down.
+ *
+ * Access to link trees are performed using the erts_link_tree_*
+ * functions below. Access to link lists are performed using the
+ * erts_link_list_* functions below.
+ *
+ * There can only be one link between the same pair of
+ * processes/ports. Since a link can be simultaneously initiated
+ * from both ends we always save the link data structure with the
+ * lowest address if multiple links should appear between the
+ * same pair of processes/ports.
+ *
+ *
+ * The different link types:
+ *
+ * --- ERTS_LNK_TYPE_PROC -----------------------------------------
+ *
+ * A link between a local process A and a local process B.
+ *
+ * A:
+ * Other Item: B process identifier
+ * Key: B process identifier
+ * B:
+ * Other Item: A process identifier
+ * Key: A process identifier
+ *
+ * Valid keys are only internal process identifiers.
+ *
+ * 'A' part of the link stored in the link tree of process A and
+ * 'B' part of the link is stored in link tree of process B.
+ *
+ * --- ERTS_LNK_TYPE_PORT -----------------------------------------
+ *
+ * A link between a local process/port A and a local process/port
+ * B.
+ *
+ * A:
+ * Other Item: B process/port identifier
+ * Key: B process/port identifier
+ * B:
+ * Other Item: A process/port identifier
+ * Key: A process/port identifier
+ *
+ * Valid keys are internal process identifiers and internal port
+ * identifiers.
+ *
+ * 'A' part of the link stored in the link tree of process/port
+ * A and 'B' part of the link is stored in link tree of
+ * process/port B.
+ *
+ * --- ERTS_LNK_TYPE_DIST_PROC ------------------------------------
+ *
+ * A link between a local process and a remote process. Either of
+ * the processes can be used as A or B.
+ *
+ * A:
+ * Other Item: B process identifier
+ * Key: B process identifier
+ * B:
+ * Other Item: A process identifier
+ * Key: A process identifier
+ * Shared:
+ * Dist: Pointer to dist structure
+ *
+ * Valid keys are internal and external process identifiers.
+ *
+ * The part of the link with a remote pid as "other item" is
+ * stored in the link tree of the local process. The part of
+ * the link with a local pid as "other item" is stored in the
+ * links list of the dist structure.
+ *
+ * ===============================================================
+ *
+ * Author: Rickard Green
+ *
+ */
+
+#ifndef ERL_MONITOR_LINK_H__
+#define ERL_MONITOR_LINK_H__
+
+#define ERTS_PROC_SIG_QUEUE_TYPE_ONLY
+#include "erl_proc_sig_queue.h"
+#undef ERTS_PROC_SIG_QUEUE_TYPE_ONLY
+
+#if defined(DEBUG) || 0
+# define ERTS_ML_DEBUG
+#else
+# undef ERTS_ML_DEBUG
+#endif
+
+#ifdef ERTS_ML_DEBUG
+# define ERTS_ML_ASSERT ERTS_ASSERT
+#else
+# define ERTS_ML_ASSERT(E) ((void) 1)
+#endif
+
+#define ERTS_MON_TYPE_MAX ((Uint16) 7)
+
+#define ERTS_MON_TYPE_PROC ((Uint16) 0)
+#define ERTS_MON_TYPE_PORT ((Uint16) 1)
+#define ERTS_MON_TYPE_TIME_OFFSET ((Uint16) 2)
+#define ERTS_MON_TYPE_DIST_PROC ((Uint16) 3)
+#define ERTS_MON_TYPE_RESOURCE ((Uint16) 4)
+#define ERTS_MON_TYPE_NODE ((Uint16) 5)
+#define ERTS_MON_TYPE_NODES ((Uint16) 6)
+#define ERTS_MON_TYPE_SUSPEND ERTS_MON_TYPE_MAX
+
+#define ERTS_MON_LNK_TYPE_MAX (ERTS_MON_TYPE_MAX + ((Uint16) 3))
+#define ERTS_LNK_TYPE_MAX ERTS_MON_LNK_TYPE_MAX
+
+#define ERTS_LNK_TYPE_PROC (ERTS_MON_TYPE_MAX + ((Uint16) 1))
+#define ERTS_LNK_TYPE_PORT (ERTS_MON_TYPE_MAX + ((Uint16) 2))
+#define ERTS_LNK_TYPE_DIST_PROC ERTS_LNK_TYPE_MAX
+
+#define ERTS_ML_FLG_TARGET (((Uint16) 1) << 0)
+#define ERTS_ML_FLG_IN_TABLE (((Uint16) 1) << 1)
+#define ERTS_ML_FLG_IN_SUBTABLE (((Uint16) 1) << 2)
+#define ERTS_ML_FLG_NAME (((Uint16) 1) << 3)
+#define ERTS_ML_FLG_EXTENDED (((Uint16) 1) << 4)
+
+#define ERTS_ML_FLG_DBG_VISITED (((Uint16) 1) << 15)
+
+/* Flags that should be the same on both monitor/link halves */
+#define ERTS_ML_FLGS_SAME \
+ (ERTS_ML_FLG_EXTENDED|ERTS_ML_FLG_NAME)
+
+typedef struct ErtsMonLnkNode__ ErtsMonLnkNode;
+
+typedef struct {
+ UWord parent; /* Parent ptr and flags... */
+ ErtsMonLnkNode *right;
+ ErtsMonLnkNode *left;
+} ErtsMonLnkTreeNode;
+
+typedef struct {
+ ErtsMonLnkNode *next;
+ ErtsMonLnkNode *prev;
+} ErtsMonLnkListNode;
+
+struct ErtsMonLnkNode__ {
+ union {
+ ErtsSignalCommon signal;
+ ErtsMonLnkTreeNode tree;
+ ErtsMonLnkListNode list;
+ } node;
+ union {
+ Eterm item;
+ void *ptr;
+ } other;
+ Uint16 offset; /* offset from monitor/link data to this structure (node) */
+ Uint16 key_offset; /* offset from this structure (node) to key */
+ Uint16 flags;
+ Uint16 type;
+};
+
+typedef struct {
+ Eterm nodename;
+ Uint32 connection_id;
+ erts_atomic_t refc;
+ erts_mtx_t mtx;
+ int alive;
+ ErtsMonLnkNode *links; /* Link double linked circular list */
+ ErtsMonLnkNode *monitors; /* Monitor double linked circular list */
+ ErtsMonLnkNode *orig_name_monitors; /* Origin named monitors
+ read-black tree */
+} ErtsMonLnkDist;
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\
+ * Misc *
+\* */
+
+/**
+ *
+ * @brief Initialize monitor/link implementation
+ *
+ */
+void erts_monitor_link_init(void);
+
+/**
+ *
+ * @brief Create monitor/link dist structure to attach to dist entry
+ *
+ * Create dist structure containing monitor and link containers. This
+ * structure is to be attached to a connected dist entry.
+ *
+ * @param[in] nodename Node name as an atom
+ *
+ * @returns Pointer to dist structure
+ *
+ */
+ErtsMonLnkDist *erts_mon_link_dist_create(Eterm nodename);
+
+/**
+ *
+ * @brief Increase reference count of monitor/link dist structure
+ *
+ * @param[in] mld Pointer to dist structure
+ *
+ */
+ERTS_GLB_INLINE void erts_mon_link_dist_inc_refc(ErtsMonLnkDist *mld);
+
+/**
+ *
+ * @brief Decrease reference count of monitor/link dist structure
+ *
+ * @param[in] mld Pointer to dist structure
+ *
+ */
+ERTS_GLB_INLINE void erts_mon_link_dist_dec_refc(ErtsMonLnkDist *mld);
+
+/* internal functions... */
+ERTS_GLB_INLINE void erts_ml_dl_list_insert__(ErtsMonLnkNode **list,
+ ErtsMonLnkNode *ml);
+ERTS_GLB_INLINE void erts_ml_dl_list_delete__(ErtsMonLnkNode **list,
+ ErtsMonLnkNode *ml);
+ERTS_GLB_INLINE ErtsMonLnkNode *erts_ml_dl_list_first__(ErtsMonLnkNode *list);
+ERTS_GLB_INLINE ErtsMonLnkNode *erts_ml_dl_list_last__(ErtsMonLnkNode *list);
+void erts_mon_link_dist_destroy__(ErtsMonLnkDist *mld);
+ERTS_GLB_INLINE void *erts_ml_node_to_main_struct__(ErtsMonLnkNode *mln);
+
+/* implementations for globally inlined misc functions... */
+#if ERTS_GLB_INLINE_INCL_FUNC_DEF
+
+ERTS_GLB_INLINE void
+erts_mon_link_dist_inc_refc(ErtsMonLnkDist *mld)
+{
+ ERTS_ML_ASSERT(erts_atomic_read_nob(&mld->refc) > 0);
+ erts_atomic_inc_nob(&mld->refc);
+}
+
+ERTS_GLB_INLINE void
+erts_mon_link_dist_dec_refc(ErtsMonLnkDist *mld)
+{
+ ERTS_ML_ASSERT(erts_atomic_read_nob(&mld->refc) > 0);
+ if (erts_atomic_dec_read_nob(&mld->refc) == 0)
+ erts_mon_link_dist_destroy__(mld);
+}
+
+ERTS_GLB_INLINE void *
+erts_ml_node_to_main_struct__(ErtsMonLnkNode *mln)
+{
+ return (void *) (((char *) mln) - ((size_t) mln->offset));
+}
+
+ERTS_GLB_INLINE void
+erts_ml_dl_list_insert__(ErtsMonLnkNode **list, ErtsMonLnkNode *ml)
+{
+ ErtsMonLnkNode *first = *list;
+ ERTS_ML_ASSERT(!(ml->flags & ERTS_ML_FLG_IN_TABLE));
+ if (!first) {
+ ml->node.list.next = ml->node.list.prev = ml;
+ *list = ml;
+ }
+ else {
+ ERTS_ML_ASSERT(first->node.list.prev->node.list.next == first);
+ ERTS_ML_ASSERT(first->node.list.next->node.list.prev == first);
+ ml->node.list.next = first;
+ ml->node.list.prev = first->node.list.prev;
+ first->node.list.prev = ml;
+ ml->node.list.prev->node.list.next = ml;
+ }
+ ml->flags |= ERTS_ML_FLG_IN_TABLE;
+}
+
+ERTS_GLB_INLINE void
+erts_ml_dl_list_delete__(ErtsMonLnkNode **list, ErtsMonLnkNode *ml)
+{
+ ERTS_ML_ASSERT(ml->flags & ERTS_ML_FLG_IN_TABLE);
+ if (ml->node.list.next == ml) {
+ ERTS_ML_ASSERT(ml->node.list.prev == ml);
+ ERTS_ML_ASSERT(*list == ml);
+
+ *list = NULL;
+ }
+ else {
+ ERTS_ML_ASSERT(ml->node.list.prev->node.list.next == ml);
+ ERTS_ML_ASSERT(ml->node.list.prev != ml);
+ ERTS_ML_ASSERT(ml->node.list.next->node.list.prev == ml);
+ ERTS_ML_ASSERT(ml->node.list.next != ml);
+
+ if (*list == ml)
+ *list = ml->node.list.next;
+ ml->node.list.prev->node.list.next = ml->node.list.next;
+ ml->node.list.next->node.list.prev = ml->node.list.prev;
+ }
+ ml->flags &= ~ERTS_ML_FLG_IN_TABLE;
+}
+
+ERTS_GLB_INLINE ErtsMonLnkNode *
+erts_ml_dl_list_first__(ErtsMonLnkNode *list)
+{
+ return list;
+}
+
+ERTS_GLB_INLINE ErtsMonLnkNode *
+erts_ml_dl_list_last__(ErtsMonLnkNode *list)
+{
+ if (!list)
+ return NULL;
+ return list->node.list.prev;
+}
+
+#endif /* ERTS_GLB_INLINE_INCL_FUNC_DEF */
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\
+ * Monitor Operations *
+\* */
+
+
+typedef struct ErtsMonLnkNode__ ErtsMonitor;
+
+typedef struct {
+ ErtsMonitor origin;
+ ErtsMonitor target;
+ Eterm ref;
+ erts_atomic32_t refc;
+} ErtsMonitorData;
+
+typedef struct {
+ ErtsMonitorData md;
+ ErtsORefThing oref_thing;
+} ErtsMonitorDataHeap;
+
+typedef struct ErtsMonitorDataExtended__ ErtsMonitorDataExtended;
+
+struct ErtsMonitorDataExtended__ {
+ ErtsMonitorData md;
+ union {
+ Eterm name;
+ Uint refc;
+ } u;
+ union {
+ struct erl_off_heap_header *ohhp;
+ ErtsMonitor *node_monitors;
+ } uptr;
+ ErtsMonLnkDist *dist;
+ Eterm heap[1]; /* heap start... */
+};
+
+typedef struct {
+ ErtsMonitor mon;
+ int pending;
+ int active;
+} ErtsMonitorSuspend;
+
+/*
+ * --- Monitor tree operations ---
+ */
+
+/**
+ *
+ * @brief Lookup a monitor in a monitor tree
+ *
+ *
+ * @param[in] root Pointer to root of monitor tree
+ *
+ * @param[in] key Key of monitor to lookup
+ *
+ * @returns Pointer to a monitor with the
+ * key 'key', or NULL if no such
+ * monitor was found
+ *
+ */
+ErtsMonitor *erts_monitor_tree_lookup(ErtsMonitor *root, Eterm key);
+
+/**
+ *
+ * @brief Lookup or insert a monitor in a monitor tree
+ *
+ * When the funcion is called it is assumed that:
+ * - 'mon' monitor is not part of any tree or list
+ * If the above is not true, bad things will happen.
+ *
+ * @param[in,out] root Pointer to pointer to root of monitor tree
+ *
+ * @param[in] mon Monitor to insert if no monitor
+ * with the same key already exists
+ *
+ * @returns Pointer to a monitor with the
+ * key 'key'. If no monitor with the key
+ * 'key' was found and 'mon' was inserted
+ * 'mon' is returned.
+ *
+ */
+ErtsMonitor *erts_monotor_tree_lookup_insert(ErtsMonitor **root,
+ ErtsMonitor *mon);
+
+/**
+ *
+ * @brief Lookup or create a node or a nodes monitor in a monitor tree.
+ *
+ * Looks up an origin monitor with the key 'target' in the monitor tree.
+ * If it is not found, creates a monitor and returns a pointer to the
+ * origin monitor.
+ *
+ * When the funcion is called it is assumed that:
+ * - no target monitors with the key 'target' exists in the tree.
+ * If the above is not true, bad things will happen.
+ *
+ * @param[in,out] root Pointer to pointer to root of monitor tree
+ *
+ * @param[out] created Pointer to integer. The integer is set to
+ * a non-zero value if no monitor with key
+ * 'target' was found, and a new monitor
+ * was created. If a monitor was found, it
+ * is set to zero.
+ *
+ * @param[in] type ERTS_MON_TYPE_NODE | ERTS_MON_TYPE_NODES
+ *
+ * @param[in] origin The key of the origin
+ *
+ * @param[in] target The key of the target
+ *
+ */
+ErtsMonitor *erts_monitor_tree_lookup_create(ErtsMonitor **root, int *created,
+ Uint16 type, Eterm origin,
+ Eterm target);
+
+/**
+ *
+ * @brief Insert a monitor in a monitor tree
+ *
+ * When the funcion is called it is assumed that:
+ * - no monitors with the same key that 'mon' exist in the tree
+ * - 'mon' is not part of any list of tree
+ * If the above are not true, bad things will happen.
+ *
+ * @param[in,out] root Pointer to pointer to root of monitor tree
+ *
+ * @param[in] mon Monitor to insert.
+ *
+ */
+void erts_monitor_tree_insert(ErtsMonitor **root, ErtsMonitor *mon);
+
+/**
+ *
+ * @brief Replace a monitor in a monitor tree
+ *
+ * When the funcion is called it is assumed that:
+ * - 'old' monitor and 'new' monitor have exactly the same key
+ * - 'old' monitor is part of the tree
+ * - 'new' monitor is not part of any tree or list
+ * If the above are not true, bad things will happen.
+ *
+ * @param[in,out] root Pointer to pointer to root of monitor tree
+ *
+ * @param[in] old Monitor to remove from the tree
+ *
+ * @param[in] new Monitor to insert into the tree
+ *
+ */
+void erts_monitor_tree_replace(ErtsMonitor **root, ErtsMonitor *old,
+ ErtsMonitor *new);
+
+/**
+ *
+ * @brief Delete a monitor from a monitor tree
+ *
+ * When the funcion is called it is assumed that:
+ * - 'mon' monitor is part of the tree
+ * If the above is not true, bad things will happen.
+ *
+ * @param[in,out] root Pointer to pointer to root of monitor tree
+ *
+ * @param[in] mon Monitor to remove from the tree
+ *
+ */
+void erts_monitor_tree_delete(ErtsMonitor **root, ErtsMonitor *mon);
+
+/**
+ *
+ * @brief Call a function for each monitor in a monitor tree
+ *
+ * The funcion 'func' will be called with a pointer to a monitor
+ * as first argument and 'arg' as second argument for each monitor
+ * in the tree referred to by 'root'.
+ *
+ * @param[in] root Pointer to root of monitor tree
+ *
+ * @param[in] func Pointer to function to call
+ *
+ * @param[in] arg Argument to pass as second argument in
+ * calls to 'func'
+ *
+ */
+void erts_monitor_tree_foreach(ErtsMonitor *root,
+ void (*func)(ErtsMonitor *, void *),
+ void *arg);
+
+/**
+ *
+ * @brief Call a function for each monitor in a monitor tree. Yield
+ * if lots of monitors exist.
+ *
+ * The funcion 'func' will be called with a pointer to a monitor
+ * as first argument and 'arg' as second argument for each monitor
+ * in the tree referred to by 'root'.
+ *
+ * It is assumed that:
+ * - *yspp equals NULL on first call
+ * - this function is repetedly called with *yspp set
+ * as set when previous call returned until a non-zero
+ * value is returned.
+ * - no modifications are made on the tree between first call
+ * and the call that returns a non-zero value
+ * If the above are not true, bad things will happen.
+ *
+ * @param[in] root Pointer to root of monitor tree
+ *
+ * @param[in] func Pointer to function to call
+ *
+ * @param[in] arg Argument to pass as second argument in
+ * calls to 'func'
+ *
+ * @param[in,out] vyspp Pointer to a pointer to an internal state
+ * used by this function. At initial call
+ * *yspp should be NULL. When done *yspp
+ * will be NULL.
+ *
+ * @param[in] limit Maximum amount of monitors to process
+ * before yielding.
+ *
+ * @returns A non-zero value when all monitors has been
+ * processed, and zero when more work is needed.
+ *
+ */
+int erts_monitor_tree_foreach_yielding(ErtsMonitor *root,
+ void (*func)(ErtsMonitor *, void *),
+ void *arg,
+ void **vyspp,
+ Sint limit);
+
+/**
+ *
+ * @brief Delete all monitors from a monitor tree and call a function for
+ * each monitor
+ *
+ * The funcion 'func' will be called with a pointer to a monitor
+ * as first argument and 'arg' as second argument for each monitor
+ * in the tree referred to by 'root'.
+ *
+ * @param[in,out] root Pointer to pointer to root of monitor tree
+ *
+ * @param[in] func Pointer to function to call
+ *
+ * @param[in] arg Argument to pass as second argument in
+ * calls to 'func'
+ *
+ */
+void erts_monitor_tree_foreach_delete(ErtsMonitor **root,
+ void (*func)(ErtsMonitor *, void *),
+ void *arg);
+
+/**
+ *
+ * @brief Delete all monitors from a monitor tree and call a function for
+ * each monitor
+ *
+ * The funcion 'func' will be called with a pointer to a monitor
+ * as first argument and 'arg' as second argument for each monitor
+ * in the tree referred to by 'root'.
+ *
+ * It is assumed that:
+ * - *yspp equals NULL on first call
+ * - this function is repetededly called with *yspp set
+ * as set when previous call returned until a non-zero
+ * value is returned.
+ * - no modifications are made on the tree between first call
+ * and the call that returns a non-zero value
+ * If the above are not true, bad things will happen.
+ *
+ * @param[in,out] root Pointer to pointer to root of monitor tree
+ *
+ * @param[in] func Pointer to function to call
+ *
+ * @param[in] arg Argument to pass as second argument in
+ * calls to 'func'
+ *
+ * @param[in,out] vyspp Pointer to a pointer to an internal state
+ * used by this function. At initial call
+ * *yspp should be NULL. When done *yspp
+ * will be NULL.
+ *
+ * @param[in] limit Maximum amount of monitors to process
+ * before yielding.
+ *
+ * @returns A non-zero value when all monitors has been
+ * processed, and zero when more work is needed.
+ *
+ */
+int erts_monitor_tree_foreach_delete_yielding(ErtsMonitor **root,
+ void (*func)(ErtsMonitor *, void *),
+ void *arg,
+ void **vyspp,
+ Sint limit);
+
+/*
+ * --- Monitor list operations --
+ */
+
+/**
+ *
+ * @brief Insert a monitor in a monitor list
+ *
+ * When the funcion is called it is assumed that:
+ * - 'mon' monitor is not part of any list or tree
+ * If the above is not true, bad things will happen.
+ *
+ * @param[in,out] list Pointer to pointer to monitor list
+ *
+ * @param[in] mon Monitor to insert
+ *
+ */
+ERTS_GLB_INLINE void erts_monitor_list_insert(ErtsMonitor **list, ErtsMonitor *mon);
+
+/**
+ *
+ * @brief Delete a monitor from a monitor list
+ *
+ * When the funcion is called it is assumed that:
+ * - 'mon' monitor is part of the list
+ * If the above is not true, bad things will happen.
+ *
+ * @param[in,out] list Pointer to pointer to monitor list
+ *
+ * @param[in] mon Monitor to remove from the list
+ *
+ */
+ERTS_GLB_INLINE void erts_monitor_list_delete(ErtsMonitor **list, ErtsMonitor *mon);
+
+/**
+ *
+ * @brief Get a pointer to first monitor in a monitor list
+ *
+ * The monitor will still remain in the list after the return
+ *
+ * @param[in] list Pointer to monitor list
+ *
+ * @returns Pointer to first monitor in list if
+ * list is not empty. If list is empty
+ * NULL is returned.
+ *
+ */
+ERTS_GLB_INLINE ErtsMonitor *erts_monitor_list_first(ErtsMonitor *list);
+
+/**
+ *
+ * @brief Get a pointer to last monitor in a monitor list
+ *
+ * The monitor will still remain in the list after the return
+ *
+ * @param[in] list Pointer to monitor list
+ *
+ * @returns Pointer to last monitor in list if
+ * list is not empty. If list is empty
+ * NULL is returned.
+ *
+ */
+ERTS_GLB_INLINE ErtsMonitor *erts_monitor_list_last(ErtsMonitor *list);
+
+/**
+ *
+ * @brief Call a function for each monitor in a monitor list
+ *
+ * The funcion 'func' will be called with a pointer to a monitor
+ * as first argument and 'arg' as second argument for each monitor
+ * in the tree referred to by 'list'.
+ *
+ * @param[in] list Pointer to root of monitor list
+ *
+ * @param[in] func Pointer to function to call
+ *
+ * @param[in] arg Argument to pass as second argument in
+ * calls to 'func'
+ *
+ */
+void erts_monitor_list_foreach(ErtsMonitor *list,
+ void (*func)(ErtsMonitor *, void *),
+ void *arg);
+
+/**
+ *
+ * @brief Call a function for each monitor in a monitor list. Yield
+ * if lots of monitors exist.
+ *
+ * The funcion 'func' will be called with a pointer to a monitor
+ * as first argument and 'arg' as second argument for each monitor
+ * in the tree referred to by 'root'.
+ *
+ * It is assumed that:
+ * - *yspp equals NULL on first call
+ * - this function is repetedly called with *yspp set
+ * as set when previous call returned until a non-zero
+ * value is returned.
+ * - no modifications are made on the tree between first call
+ * and the call that returns a non-zero value
+ * If the above are not true, bad things will happen.
+ *
+ * @param[in] list Pointer to monitor list
+ *
+ * @param[in] func Pointer to function to call
+ *
+ * @param[in] arg Argument to pass as second argument in
+ * calls to 'func'
+ *
+ * @param[in,out] vyspp Pointer to a pointer to an internal state
+ * used by this function. At initial call
+ * *yspp should be NULL. When done *yspp
+ * will be NULL.
+ *
+ * @param[in] limit Maximum amount of monitors to process
+ * before yielding.
+ *
+ * @returns A non-zero value when all monitors has been
+ * processed, and zero when more work is needed.
+ *
+ */
+int erts_monitor_list_foreach_yielding(ErtsMonitor *list,
+ void (*func)(ErtsMonitor *, void *),
+ void *arg,
+ void **vyspp,
+ Sint limit);
+
+/**
+ *
+ * @brief Delete all monitors from a monitor list and call a function for
+ * each monitor
+ *
+ * The funcion 'func' will be called with a pointer to a monitor
+ * as first argument and 'arg' as second argument for each monitor
+ * in the tree referred to by 'root'.
+ *
+ * @param[in,out] list Pointer to pointer to monitor list
+ *
+ * @param[in] func Pointer to function to call
+ *
+ * @param[in] arg Argument to pass as second argument in
+ * calls to 'func'
+ *
+ */
+void erts_monitor_list_foreach_delete(ErtsMonitor **list,
+ void (*func)(ErtsMonitor *, void *),
+ void *arg);
+
+/**
+ *
+ * @brief Delete all monitors from a monitor list and call a function for
+ * each monitor
+ *
+ * The funcion 'func' will be called with a pointer to a monitor
+ * as first argument and 'arg' as second argument for each monitor
+ * in the tree referred to by 'root'.
+ *
+ * It is assumed that:
+ * - *yspp equals NULL on first call
+ * - this function is repetededly called with *yspp set
+ * as set when previous call returned until a non-zero
+ * value is returned.
+ * - no modifications are made on the tree between first
+ * and the call that returns a non-zero value
+ * If the above are not true, bad things will happen.
+ *
+ * @param[in,out] list Pointer to pointer to monitor list
+ *
+ * @param[in] func Pointer to function to call
+ *
+ * @param[in] arg Argument to pass as second argument in
+ * calls to 'func'
+ *
+ * @param[in,out] vyspp Pointer to a pointer to an internal state
+ * used by this function. At initial call
+ * *yspp should be NULL. When done *yspp
+ * will be NULL.
+ *
+ * @param[in] limit Maximum amount of monitors to process
+ * before yielding.
+ *
+ * @returns A non-zero value when all monitors has been
+ * processed, and zero when more work is needed.
+ *
+ */
+int erts_monitor_list_foreach_delete_yielding(ErtsMonitor **list,
+ void (*func)(ErtsMonitor *, void *),
+ void *arg,
+ void **vyspp,
+ Sint limit);
+
+/*
+ * --- Misc monitor operations ---
+ */
+
+/**
+ *
+ * @brief Create a monitor
+ *
+ * Can create all types of monitors exept for suspend monitors
+ *
+ * When the funcion is called it is assumed that:
+ * - 'ref' is an internal ordinary reference if type is ERTS_MON_TYPE_PROC,
+ * ERTS_MON_TYPE_PORT, ERTS_MON_TYPE_TIME_OFFSET, or ERTS_MON_TYPE_RESOURCE
+ * - 'ref' is NIL if type is ERTS_MON_TYPE_NODE or ERTS_MON_TYPE_NODES
+ * - 'ref' is and ordinary internal reference or an external reference if
+ * type is ERTS_MON_TYPE_DIST_PROC
+ * - 'name' is an atom or NIL if type is ERTS_MON_TYPE_PROC,
+ * ERTS_MON_TYPE_PORT, or ERTS_MON_TYPE_DIST_PROC
+ * - 'name is NIL if type is ERTS_MON_TYPE_TIME_OFFSET, ERTS_MON_TYPE_RESOURCE,
+ * ERTS_MON_TYPE_NODE, or ERTS_MON_TYPE_NODES
+ * If the above is not true, bad things will happen.
+ *
+ * @param[in] type ERTS_MON_TYPE_PROC, ERTS_MON_TYPE_PORT,
+ * ERTS_MON_TYPE_TIME_OFFSET, ERTS_MON_TYPE_DIST_PROC,
+ * ERTS_MON_TYPE_RESOURCE, ERTS_MON_TYPE_NODE,
+ * or ERTS_MON_TYPE_NODES
+ *
+ * @param[in] ref A reference or NIL depending on type
+ *
+ * @param[in] origin The key of the origin
+ *
+ * @param[in] target The key of the target
+ *
+ */
+ErtsMonitorData *erts_monitor_create(Uint16 type, Eterm ref, Eterm origin,
+ Eterm target, Eterm name);
+
+/**
+ *
+ * @brief Get pointer to monitor data structure
+ *
+ * @param[in] mon Pointer to monitor
+ *
+ * @returns Pointer to monitor data structure
+ *
+ */
+ERTS_GLB_INLINE ErtsMonitorData *erts_monitor_to_data(ErtsMonitor *mon);
+
+/**
+ *
+ * @brief Check if monitor is a target monitor
+ *
+ * @param[in] mon Pointer to monitor to check
+ *
+ * @returns A non-zero value if target monitor;
+ * otherwise zero
+ *
+ */
+ERTS_GLB_INLINE int erts_monitor_is_target(ErtsMonitor *mon);
+
+/**
+ *
+ * @brief Check if monitor is an origin monitor
+ *
+ * @param[in] mon Pointer to monitor to check
+ *
+ * @returns A non-zero value if origin monitor;
+ * otherwise zero
+ *
+ */
+ERTS_GLB_INLINE int erts_monitor_is_origin(ErtsMonitor *mon);
+
+/**
+ *
+ * @brief Check if monitor is in tree or list
+ *
+ * @param[in] mon Pointer to monitor to check
+ *
+ * @returns A non-zero value if in tree or list;
+ * otherwise zero
+ *
+ */
+ERTS_GLB_INLINE int erts_monitor_is_in_table(ErtsMonitor *mon);
+
+/**
+ *
+ * @brief Release monitor
+ *
+ * When both the origin and the target part of the monitor have
+ * been released the monitor structure will be deallocated.
+ *
+ * When the funcion is called it is assumed that:
+ * - 'mon' monitor is not part of any list or tree
+ * - 'mon' is not referred to by any other structures
+ * If the above are not true, bad things will happen.
+ *
+ * @param[in] mon Pointer to monitor
+ *
+ */
+ERTS_GLB_INLINE void erts_monitor_release(ErtsMonitor *mon);
+
+/**
+ *
+ * @brief Release both target and origin monitor structures simultaneously
+ *
+ * Release both the origin and target parts of the monitor
+ * simultaneously and deallocate the structure.
+ *
+ * When the funcion is called it is assumed that:
+ * - Neither the origin part nor the target part of the monitor
+ * are not part of any list or tree
+ * - Neither the origin part nor the target part of the monitor
+ * are referred to by any other structures
+ * If the above are not true, bad things will happen.
+ *
+ * @param[in] mdp Pointer to monitor data structure
+ *
+ */
+ERTS_GLB_INLINE void erts_monitor_release_both(ErtsMonitorData *mdp);
+
+/**
+ *
+ * @brief Insert monitor in dist monitor tree or list
+ *
+ * When the funcion is called it is assumed that:
+ * - 'mon' monitor is not part of any list or tree
+ * If the above is not true, bad things will happen.
+ *
+ * @param[in] mon Pointer to monitor
+ *
+ * @param[in] dist Pointer to dist structure
+ *
+ * @returns A non-zero value if inserted;
+ * otherwise, zero. The monitor
+ * is not inserted if the dist
+ * structure has been set in a
+ * dead state.
+ *
+ */
+ERTS_GLB_INLINE int erts_monitor_dist_insert(ErtsMonitor *mon, ErtsMonLnkDist *dist);
+
+/**
+ *
+ * @brief Delete monitor from dist monitor tree or list
+ *
+ * When the funcion is called it is assumed that:
+ * - 'mon' monitor earler has been inserted into 'dist'
+ * If the above is not true, bad things will happen.
+ *
+ * @param[in] mon Pointer to monitor
+ *
+ * @param[in] dist Pointer to dist structure
+ *
+ * @returns A non-zero value if deleted;
+ * otherwise, zero. The monitor
+ * is not deleted if the dist
+ * structure has been set in a
+ * dead state or if it has already
+ * been deleted.
+ *
+ */
+ERTS_GLB_INLINE int erts_monitor_dist_delete(ErtsMonitor *mon);
+
+/**
+ *
+ * @brief Set dead dist structure on monitor
+ *
+ * @param[in] mon Pointer to monitor
+ *
+ * @param[in] nodename Name of remote node
+ *
+ */
+void
+erts_monitor_set_dead_dist(ErtsMonitor *mon, Eterm nodename);
+
+/**
+ *
+ * @brief Get charged size of monitor
+ *
+ * If the other side of the monitor has been released, the
+ * whole size of the monitor data structure is returned; otherwise,
+ * half of the size is returned.
+ *
+ * When the funcion is called it is assumed that:
+ * - 'mon' has not been released
+ * If the above is not true, bad things will happen.
+ *
+ * @param[in] mon Pointer to monitor
+ *
+ * @returns Charged size in bytes
+ *
+ */
+Uint erts_monitor_size(ErtsMonitor *mon);
+
+
+/* internal function... */
+void erts_monitor_destroy__(ErtsMonitorData *mdp);
+
+/* implementations for globally inlined monitor functions... */
+#if ERTS_GLB_INLINE_INCL_FUNC_DEF
+
+ERTS_GLB_INLINE int
+erts_monitor_is_target(ErtsMonitor *mon)
+{
+ return !!(mon->flags & ERTS_ML_FLG_TARGET);
+}
+
+ERTS_GLB_INLINE int
+erts_monitor_is_origin(ErtsMonitor *mon)
+{
+ return !(mon->flags & ERTS_ML_FLG_TARGET);
+}
+
+ERTS_GLB_INLINE int
+erts_monitor_is_in_table(ErtsMonitor *mon)
+{
+ return !!(mon->flags & ERTS_ML_FLG_IN_TABLE);
+}
+
+ERTS_GLB_INLINE void
+erts_monitor_list_insert(ErtsMonitor **list, ErtsMonitor *mon)
+{
+ erts_ml_dl_list_insert__((ErtsMonLnkNode **) list, (ErtsMonLnkNode *) mon);
+}
+
+ERTS_GLB_INLINE void
+erts_monitor_list_delete(ErtsMonitor **list, ErtsMonitor *mon)
+{
+ erts_ml_dl_list_delete__((ErtsMonLnkNode **) list, (ErtsMonLnkNode *) mon);
+}
+
+ERTS_GLB_INLINE ErtsMonitor *
+erts_monitor_list_first(ErtsMonitor *list)
+{
+ return (ErtsMonitor *) erts_ml_dl_list_first__((ErtsMonLnkNode *) list);
+}
+
+ERTS_GLB_INLINE ErtsMonitor *
+erts_monitor_list_last(ErtsMonitor *list)
+{
+ return (ErtsMonitor *) erts_ml_dl_list_last__((ErtsMonLnkNode *) list);
+}
+
+#ifdef ERTS_ML_DEBUG
+extern size_t erts_monitor_origin_offset;
+extern size_t erts_monitor_origin_key_offset;
+extern size_t erts_monitor_target_offset;
+extern size_t erts_monitor_target_key_offset;
+extern size_t erts_monitor_node_key_offset;
+#endif
+
+ERTS_GLB_INLINE ErtsMonitorData *
+erts_monitor_to_data(ErtsMonitor *mon)
+{
+ ErtsMonitorData *mdp = erts_ml_node_to_main_struct__((ErtsMonLnkNode *) mon);
+
+#ifdef ERTS_ML_DEBUG
+ ERTS_ML_ASSERT(!(mdp->origin.flags & ERTS_ML_FLG_TARGET));
+ ERTS_ML_ASSERT(erts_monitor_origin_offset == (size_t) mdp->origin.offset);
+ ERTS_ML_ASSERT(!!(mdp->target.flags & ERTS_ML_FLG_TARGET));
+ ERTS_ML_ASSERT(erts_monitor_target_offset == (size_t) mdp->target.offset);
+ if (mon->type == ERTS_MON_TYPE_NODE || mon->type == ERTS_MON_TYPE_NODES) {
+ ERTS_ML_ASSERT(erts_monitor_node_key_offset == (size_t) mdp->origin.key_offset);
+ ERTS_ML_ASSERT(erts_monitor_node_key_offset == (size_t) mdp->target.key_offset);
+ }
+ else {
+ ERTS_ML_ASSERT(erts_monitor_origin_key_offset == (size_t) mdp->origin.key_offset);
+ ERTS_ML_ASSERT(erts_monitor_target_key_offset == (size_t) mdp->target.key_offset);
+ }
+#endif
+
+ return mdp;
+}
+
+ERTS_GLB_INLINE void
+erts_monitor_release(ErtsMonitor *mon)
+{
+ ErtsMonitorData *mdp = erts_monitor_to_data(mon);
+ ERTS_ML_ASSERT(!(mon->flags & ERTS_ML_FLG_IN_TABLE));
+ ERTS_ML_ASSERT(erts_atomic32_read_nob(&mdp->refc) > 0);
+
+ if (erts_atomic32_dec_read_nob(&mdp->refc) == 0)
+ erts_monitor_destroy__(mdp);
+}
+
+ERTS_GLB_INLINE void
+erts_monitor_release_both(ErtsMonitorData *mdp)
+{
+ ERTS_ML_ASSERT((mdp->origin.flags & ERTS_ML_FLGS_SAME)
+ == (mdp->target.flags & ERTS_ML_FLGS_SAME));
+ ERTS_ML_ASSERT(!(mdp->origin.flags & ERTS_ML_FLG_IN_TABLE));
+ ERTS_ML_ASSERT(!(mdp->target.flags & ERTS_ML_FLG_IN_TABLE));
+ ERTS_ML_ASSERT(erts_atomic32_read_nob(&mdp->refc) >= 2);
+
+ if (erts_atomic32_add_read_nob(&mdp->refc, (erts_aint32_t) -2) == 0)
+ erts_monitor_destroy__(mdp);
+}
+
+ERTS_GLB_INLINE int
+erts_monitor_dist_insert(ErtsMonitor *mon, ErtsMonLnkDist *dist)
+{
+ ErtsMonitorDataExtended *mdep;
+ int insert;
+
+ ERTS_ML_ASSERT(mon->flags & ERTS_ML_FLG_EXTENDED);
+ ERTS_ML_ASSERT(mon->type == ERTS_MON_TYPE_DIST_PROC
+ || mon->type == ERTS_MON_TYPE_NODE);
+
+ mdep = (ErtsMonitorDataExtended *) erts_monitor_to_data(mon);
+
+ ERTS_ML_ASSERT(!mdep->dist);
+ ERTS_ML_ASSERT(dist);
+ mdep->dist = dist;
+
+ erts_mon_link_dist_inc_refc(dist);
+
+ erts_mtx_lock(&dist->mtx);
+
+ insert = dist->alive;
+ if (insert) {
+ if ((mon->flags & (ERTS_ML_FLG_NAME
+ | ERTS_ML_FLG_TARGET)) == ERTS_ML_FLG_NAME)
+ erts_monitor_tree_insert(&dist->orig_name_monitors, mon);
+ else
+ erts_monitor_list_insert(&dist->monitors, mon);
+ }
+
+ erts_mtx_unlock(&dist->mtx);
+
+ return insert;
+}
+
+ERTS_GLB_INLINE int
+erts_monitor_dist_delete(ErtsMonitor *mon)
+{
+ ErtsMonitorDataExtended *mdep;
+ ErtsMonLnkDist *dist;
+ Uint16 flags;
+ int delete;
+
+ ERTS_ML_ASSERT(mon->flags & ERTS_ML_FLG_EXTENDED);
+ ERTS_ML_ASSERT(mon->type == ERTS_MON_TYPE_DIST_PROC
+ || mon->type == ERTS_MON_TYPE_NODE);
+
+ mdep = (ErtsMonitorDataExtended *) erts_monitor_to_data(mon);
+ dist = mdep->dist;
+ ERTS_ML_ASSERT(dist);
+
+ erts_mtx_lock(&dist->mtx);
+
+ flags = mon->flags;
+ delete = !!dist->alive & !!(flags & ERTS_ML_FLG_IN_TABLE);
+ if (delete) {
+ if ((flags & (ERTS_ML_FLG_NAME
+ | ERTS_ML_FLG_TARGET)) == ERTS_ML_FLG_NAME)
+ erts_monitor_tree_delete(&dist->orig_name_monitors, mon);
+ else
+ erts_monitor_list_delete(&dist->monitors, mon);
+ }
+
+ erts_mtx_unlock(&dist->mtx);
+
+ return delete;
+}
+
+
+#endif /* ERTS_GLB_INLINE_INCL_FUNC_DEF */
+
+/* suspend monitors... */
+ErtsMonitorSuspend *erts_monitor_suspend_create(Eterm pid);
+ErtsMonitorSuspend *erts_monitor_suspend_tree_lookup_create(ErtsMonitor **root,
+ int *created,
+ Eterm pid);
+void erts_monitor_suspend_destroy(ErtsMonitorSuspend *msp);
+
+ERTS_GLB_INLINE ErtsMonitorSuspend *erts_monitor_suspend(ErtsMonitor *mon);
+
+#if ERTS_GLB_INLINE_INCL_FUNC_DEF
+
+ERTS_GLB_INLINE ErtsMonitorSuspend *erts_monitor_suspend(ErtsMonitor *mon)
+{
+ ERTS_ML_ASSERT(!mon || mon->type == ERTS_MON_TYPE_SUSPEND);
+ return (ErtsMonitorSuspend *) mon;
+}
+
+#endif
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\
+ * Link Operations *
+\* */
+
+typedef struct ErtsMonLnkNode__ ErtsLink;
+
+typedef struct {
+ ErtsLink a;
+ ErtsLink b;
+ erts_atomic32_t refc;
+} ErtsLinkData;
+
+typedef struct {
+ ErtsLinkData ld;
+ struct erl_off_heap_header *ohhp;
+ ErtsMonLnkDist *dist;
+ Eterm heap[1]; /* heap start... */
+} ErtsLinkDataExtended;
+
+/*
+ * --- Link tree operations ---
+ */
+
+/**
+ *
+ * @brief Lookup a link in a link tree
+ *
+ *
+ * @param[in] root Pointer to root of link tree
+ *
+ * @param[in] key Key of link to lookup
+ *
+ * @returns Pointer to a link with the
+ * key 'key', or NULL if no such
+ * link was found
+ *
+ */
+ErtsLink *erts_link_tree_lookup(ErtsLink *root, Eterm item);
+
+/**
+ *
+ * @brief Lookup or insert a link in a link tree
+ *
+ * When the funcion is called it is assumed that:
+ * - 'lnk' link is not part of any tree or list
+ * If the above is not true, bad things will happen.
+ *
+ * @param[in,out] root Pointer to pointer to root of link tree
+ *
+ * @param[in] lnk Link to insert if no link
+ * with the same key already exists
+ *
+ * @returns Pointer to a link with the
+ * key 'key'. If no link with the key
+ * 'key' was found and 'lnk' was inserted
+ * 'lnk' is returned.
+ *
+ */
+ErtsLink *erts_link_tree_lookup_insert(ErtsLink **root, ErtsLink *lnk);
+
+/**
+ *
+ * @brief Lookup or create a link in a link tree.
+ *
+ * Looks up a link with the key 'other' in the link tree. If it is not
+ * found, creates and insert a link with the key 'other'.
+ *
+ * @param[in,out] root Pointer to pointer to root of link tree
+ *
+ * @param[out] created Pointer to integer. The integer is set to
+ * a non-zero value if no link with key
+ * 'other' was found, and a new link
+ * was created. If a link was found, it
+ * is set to zero.
+ *
+ * @param[in] type Type of link
+ *
+ * @param[in] this Id of this entity
+ *
+ * @param[in] other Id of other entity
+ *
+ */
+ErtsLink *erts_link_tree_lookup_create(ErtsLink **root, int *created,
+ Uint16 type, Eterm this, Eterm other);
+
+/**
+ *
+ * @brief Insert a link in a link tree
+ *
+ * When the funcion is called it is assumed that:
+ * - no links with the same key that 'lnk' exist in the tree
+ * - 'lnk' is not part of any list of tree
+ * If the above are not true, bad things will happen.
+ *
+ * @param[in,out] root Pointer to pointer to root of link tree
+ *
+ * @param[in] lnk Link to insert.
+ *
+ */
+void erts_link_tree_insert(ErtsLink **root, ErtsLink *lnk);
+
+/**
+ *
+ * @brief Replace a link in a link tree
+ *
+ * When the funcion is called it is assumed that:
+ * - 'old' link and 'new' link have exactly the same key
+ * - 'old' link is part of the tree
+ * - 'new' link is not part of any tree or list
+ * If the above are not true, bad things will happen.
+ *
+ * @param[in,out] root Pointer to pointer to root of link tree
+ *
+ * @param[in] old Link to remove from the tree
+ *
+ * @param[in] new Link to insert into the tree
+ *
+ */
+void erts_link_tree_replace(ErtsLink **root, ErtsLink *old, ErtsLink *new);
+
+/**
+ *
+ * @brief Replace a link in a link tree if key already exist based on adress
+ *
+ * Inserts the link 'lnk' in the tree if no link with the same key
+ * already exists in tree. If a link with the same key exists in
+ * the tree and 'lnk' has a lower address than the link in the
+ * tree, the existing link in the tree is replaced by 'lnk'.
+ *
+ * When the funcion is called it is assumed that:
+ * - 'lnk' link is not part of any tree or list
+ * If the above are not true, bad things will happen.
+ *
+ * @param[in,out] root Pointer to pointer to root of link tree
+ *
+ * @param[in] lnk Link to insert into the tree
+ *
+ * @returns A pointer to the link that is not part
+ * of the tree after this operation.
+ *
+ */
+ERTS_GLB_INLINE ErtsLink *erts_link_tree_insert_addr_replace(ErtsLink **root,
+ ErtsLink *lnk);
+
+/**
+ *
+ * @brief Delete a link from a link tree
+ *
+ * When the funcion is called it is assumed that:
+ * - 'lnk' link is part of the tree
+ * If the above is not true, bad things will happen.
+ *
+ * @param[in,out] root Pointer to pointer to root of link tree
+ *
+ * @param[in] lnk Link to remove from the tree
+ *
+ */
+void erts_link_tree_delete(ErtsLink **root, ErtsLink *lnk);
+
+/**
+ *
+ * @brief Delete a link from a link tree based on key
+ *
+ * If link 'lnk' is in the tree, 'lnk' is deleted from the tree.
+ * If link 'lnk' is not in the tree, another link with the same
+ * key as 'lnk' is deleted from the tree if such a link exist.
+ *
+ * When the funcion is called it is assumed that:
+ * - if 'lnk' link is part of a tree or list, it is part of this tree
+ * If the above is not true, bad things will happen.
+ *
+ * @param[in,out] root Pointer to pointer to root of link tree
+ *
+ * @param[in] lnk Link to remove from the tree
+ *
+ * @returns A pointer to the link that was deleted
+ * from the tree, or NULL in case no link
+ * was deleted from the tree
+ *
+ */
+ERTS_GLB_INLINE ErtsLink *erts_link_tree_key_delete(ErtsLink **root, ErtsLink *lnk);
+
+/**
+ *
+ * @brief Call a function for each link in a link tree
+ *
+ * The funcion 'func' will be called with a pointer to a link
+ * as first argument and 'arg' as second argument for each link
+ * in the tree referred to by 'root'.
+ *
+ * @param[in] root Pointer to root of link tree
+ *
+ * @param[in] func Pointer to function to call
+ *
+ * @param[in] arg Argument to pass as second argument in
+ * calls to 'func'
+ *
+ */
+void erts_link_tree_foreach(ErtsLink *root,
+ void (*func)(ErtsLink *, void *),
+ void *arg);
+
+/**
+ *
+ * @brief Call a function for each link in a link tree. Yield if lots
+ * of links exist.
+ *
+ * The funcion 'func' will be called with a pointer to a link
+ * as first argument and 'arg' as second argument for each link
+ * in the tree referred to by 'root'.
+ *
+ * It is assumed that:
+ * - *yspp equals NULL on first call
+ * - this function is repetedly called with *yspp set
+ * as set when previous call returned until a non-zero
+ * value is returned.
+ * - no modifications are made on the tree between first call
+ * and the call that returns a non-zero value
+ * If the above are not true, bad things will happen.
+ *
+ * @param[in] root Pointer to root of link tree
+ *
+ * @param[in] func Pointer to function to call
+ *
+ * @param[in] arg Argument to pass as second argument in
+ * calls to 'func'
+ *
+ * @param[in,out] vyspp Pointer to a pointer to an internal state
+ * used by this function. At initial call
+ * *yspp should be NULL. When done *yspp
+ * will be NULL.
+ *
+ * @param[in] limit Maximum amount of links to process
+ * before yielding.
+ *
+ * @returns A non-zero value when all links has been
+ * processed, and zero when more work is needed.
+ *
+ */
+int erts_link_tree_foreach_yielding(ErtsLink *root,
+ void (*func)(ErtsLink *, void *),
+ void *arg,
+ void **vyspp,
+ Sint limit);
+
+/**
+ *
+ * @brief Delete all links from a link tree and call a function for
+ * each link
+ *
+ * The funcion 'func' will be called with a pointer to a link
+ * as first argument and 'arg' as second argument for each link
+ * in the tree referred to by 'root'.
+ *
+ * @param[in,out] root Pointer to pointer to root of link tree
+ *
+ * @param[in] func Pointer to function to call
+ *
+ * @param[in] arg Argument to pass as second argument in
+ * calls to 'func'
+ *
+ */
+void erts_link_tree_foreach_delete(ErtsLink **root,
+ void (*func)(ErtsLink *, void *),
+ void *arg);
+
+/**
+ *
+ * @brief Delete all links from a link tree and call a function for
+ * each link
+ *
+ * The funcion 'func' will be called with a pointer to a link
+ * as first argument and 'arg' as second argument for each link
+ * in the tree referred to by 'root'.
+ *
+ * It is assumed that:
+ * - *yspp equals NULL on first call
+ * - this function is repetededly called with *yspp set
+ * as set when previous call returned until a non-zero
+ * value is returned.
+ * - no modifications are made on the tree between first call
+ * and the call that returns a non-zero value
+ * If the above are not true, bad things will happen.
+ *
+ * @param[in,out] root Pointer to pointer to root of link tree
+ *
+ * @param[in] func Pointer to function to call
+ *
+ * @param[in] arg Argument to pass as second argument in
+ * calls to 'func'
+ *
+ * @param[in,out] vyspp Pointer to a pointer to an internal state
+ * used by this function. At initial call
+ * *yspp should be NULL. When done *yspp
+ * will be NULL.
+ *
+ * @param[in] limit Maximum amount of links to process
+ * before yielding.
+ *
+ * @returns A non-zero value when all links has been
+ * processed, and zero when more work is needed.
+ *
+ */
+int erts_link_tree_foreach_delete_yielding(ErtsLink **root,
+ void (*func)(ErtsLink *, void *),
+ void *arg,
+ void **vyspp,
+ Sint limit);
+
+/*
+ * --- Link list operations ---
+ */
+
+/**
+ *
+ * @brief Insert a link in a link list
+ *
+ * When the funcion is called it is assumed that:
+ * - 'lnk' link is not part of any list or tree
+ * If the above is not true, bad things will happen.
+ *
+ * @param[in,out] list Pointer to pointer to link list
+ *
+ * @param[in] lnk Link to insert
+ *
+ */
+ERTS_GLB_INLINE void erts_link_list_insert(ErtsLink **list, ErtsLink *lnk);
+
+/**
+ *
+ * @brief Delete a link from a link list
+ *
+ * When the funcion is called it is assumed that:
+ * - 'lnk' link is part of the list
+ * If the above is not true, bad things will happen.
+ *
+ * @param[in,out] list Pointer to pointer to link list
+ *
+ * @param[in] lnk Link to remove from the list
+ *
+ */
+ERTS_GLB_INLINE void erts_link_list_delete(ErtsLink **list, ErtsLink *lnk);
+
+/**
+ *
+ * @brief Get a pointer to first link in a link list
+ *
+ * The link will still remain in the list after the return
+ *
+ * @param[in] list Pointer to link list
+ *
+ * @returns Pointer to first link in list if
+ * list is not empty. If list is empty
+ * NULL is returned.
+ *
+ */
+ERTS_GLB_INLINE ErtsLink *erts_link_list_first(ErtsLink *list);
+
+/**
+ *
+ * @brief Get a pointer to last link in a link list
+ *
+ * The link will still remain in the list after the return
+ *
+ * @param[in] list Pointer to link list
+ *
+ * @returns Pointer to last link in list if
+ * list is not empty. If list is empty
+ * NULL is returned.
+ *
+ */
+ERTS_GLB_INLINE ErtsLink *erts_link_list_last(ErtsLink *list);
+
+/**
+ *
+ * @brief Call a function for each link in a link list
+ *
+ * The funcion 'func' will be called with a pointer to a link
+ * as first argument and 'arg' as second argument for each link
+ * in the tree referred to by 'list'.
+ *
+ * @param[in] list Pointer to root of link list
+ *
+ * @param[in] func Pointer to function to call
+ *
+ * @param[in] arg Argument to pass as second argument in
+ * calls to 'func'
+ *
+ */
+void erts_link_list_foreach(ErtsLink *list,
+ void (*func)(ErtsLink *, void *),
+ void *arg);
+
+/**
+ *
+ * @brief Call a function for each link in a link list. Yield
+ * if lots of links exist.
+ *
+ * The funcion 'func' will be called with a pointer to a link
+ * as first argument and 'arg' as second argument for each link
+ * in the tree referred to by 'root'.
+ *
+ * It is assumed that:
+ * - *yspp equals NULL on first call
+ * - this function is repetedly called with *yspp set
+ * as set when previous call returned until a non-zero
+ * value is returned.
+ * - no modifications are made on the tree between first call
+ * and the call that returns a non-zero value
+ * If the above are not true, bad things will happen.
+ *
+ * @param[in] list Pointer to link list
+ *
+ * @param[in] func Pointer to function to call
+ *
+ * @param[in] arg Argument to pass as second argument in
+ * calls to 'func'
+ *
+ * @param[in,out] vyspp Pointer to a pointer to an internal state
+ * used by this function. At initial call
+ * *yspp should be NULL. When done *yspp
+ * will be NULL.
+ *
+ * @param[in] limit Maximum amount of links to process
+ * before yielding.
+ *
+ * @returns A non-zero value when all links has been
+ * processed, and zero when more work is needed.
+ *
+ */
+int erts_link_list_foreach_yielding(ErtsLink *list,
+ void (*func)(ErtsLink *, void *),
+ void *arg,
+ void **vyspp,
+ Sint limit);
+
+/**
+ *
+ * @brief Delete all links from a link list and call a function for
+ * each link
+ *
+ * The funcion 'func' will be called with a pointer to a link
+ * as first argument and 'arg' as second argument for each link
+ * in the tree referred to by 'root'.
+ *
+ * @param[in,out] list Pointer to pointer to link list
+ *
+ * @param[in] func Pointer to function to call
+ *
+ * @param[in] arg Argument to pass as second argument in
+ * calls to 'func'
+ *
+ */
+void erts_link_list_foreach_delete(ErtsLink **list,
+ void (*func)(ErtsLink *, void *),
+ void *arg);
+
+/**
+ *
+ * @brief Delete all links from a link list and call a function for
+ * each link
+ *
+ * The funcion 'func' will be called with a pointer to a link
+ * as first argument and 'arg' as second argument for each link
+ * in the tree referred to by 'root'.
+ *
+ * It is assumed that:
+ * - *yspp equals NULL on first call
+ * - this function is repetededly called with *yspp set
+ * as set when previous call returned until a non-zero
+ * value is returned.
+ * - no modifications are made on the tree between first
+ * and the call that returns a non-zero value
+ * If the above are not true, bad things will happen.
+ *
+ * @param[in,out] list Pointer to pointer to link list
+ *
+ * @param[in] func Pointer to function to call
+ *
+ * @param[in] arg Argument to pass as second argument in
+ * calls to 'func'
+ *
+ * @param[in,out] vyspp Pointer to a pointer to an internal state
+ * used by this function. At initial call
+ * *yspp should be NULL. When done *yspp
+ * will be NULL.
+ *
+ * @param[in] limit Maximum amount of links to process
+ * before yielding.
+ *
+ * @returns A non-zero value when all links has been
+ * processed, and zero when more work is needed.
+ *
+ */
+int erts_link_list_foreach_delete_yielding(ErtsLink **list,
+ void (*func)(ErtsLink *, void *),
+ void *arg,
+ void **vyspp,
+ Sint limit);
+
+/*
+ * --- Misc link operations ---
+ */
+
+/**
+ *
+ * @brief Create a link
+ *
+ * Can create all types of links
+ *
+ * When the funcion is called it is assumed that:
+ * - 'ref' is an internal ordinary reference if type is ERTS_MON_TYPE_PROC,
+ * ERTS_MON_TYPE_PORT, ERTS_MON_TYPE_TIME_OFFSET, or ERTS_MON_TYPE_RESOURCE
+ * - 'ref' is NIL if type is ERTS_MON_TYPE_NODE or ERTS_MON_TYPE_NODES
+ * - 'ref' is and ordinary internal reference or an external reference if
+ * type is ERTS_MON_TYPE_DIST_PROC
+ * - 'name' is an atom or NIL if type is ERTS_MON_TYPE_PROC,
+ * ERTS_MON_TYPE_PORT, or ERTS_MON_TYPE_DIST_PROC
+ * - 'name is NIL if type is ERTS_MON_TYPE_TIME_OFFSET, ERTS_MON_TYPE_RESOURCE,
+ * ERTS_MON_TYPE_NODE, or ERTS_MON_TYPE_NODES
+ * If the above is not true, bad things will happen.
+ *
+ * @param[in] type ERTS_MON_TYPE_PROC, ERTS_MON_TYPE_PORT,
+ * ERTS_MON_TYPE_TIME_OFFSET, ERTS_MON_TYPE_DIST_PROC,
+ * ERTS_MON_TYPE_RESOURCE, ERTS_MON_TYPE_NODE,
+ * or ERTS_MON_TYPE_NODES
+ *
+ * @param[in] a The key of entity a. Link structure a will
+ * have field other.item set to 'b'.
+ *
+ * @param[in] b The key of entity b. Link structure b will
+ * have field other.item set to 'a'.
+ *
+ */
+ErtsLinkData *erts_link_create(Uint16 type, Eterm a, Eterm b);
+
+/**
+ *
+ * @brief Get pointer to link data structure
+ *
+ * @param[in] lnk Pointer to link
+ *
+ * @returns Pointer to link data structure
+ *
+ */
+ERTS_GLB_INLINE ErtsLinkData *erts_link_to_data(ErtsLink *lnk);
+
+/**
+ *
+ * @brief Get pointer to the other link structure part of the link
+ *
+ * @param[in] lnk Pointer to link
+ *
+ * @param[out] ldpp Pointer to pointer to link data structure,
+ * if a non-NULL value is passed in the call
+ *
+ * @returns Pointer to other link
+ *
+ */
+ERTS_GLB_INLINE ErtsLink *erts_link_to_other(ErtsLink *lnk, ErtsLinkData **ldpp);
+
+/**
+ *
+ * @brief Check if link is in tree or list
+ *
+ * @param[in] lnk Pointer to lnk to check
+ *
+ * @returns A non-zero value if in tree or list;
+ * otherwise zero
+ *
+ */
+ERTS_GLB_INLINE int erts_link_is_in_table(ErtsLink *lnk);
+
+/**
+ *
+ * @brief Release link
+ *
+ * When both link halves part of the link have been released the link
+ * structure will be deallocated.
+ *
+ * When the funcion is called it is assumed that:
+ * - 'lnk' link is not part of any list or tree
+ * - 'lnk' is not referred to by any other structures
+ * If the above are not true, bad things will happen.
+ *
+ * @param[in] lnk Pointer to link
+ *
+ */
+ERTS_GLB_INLINE void erts_link_release(ErtsLink *lnk);
+
+/**
+ *
+ * @brief Release both link halves of a link simultaneously
+ *
+ * Release both halves of a link simultaneously and deallocate
+ * the structure.
+ *
+ * When the funcion is called it is assumed that:
+ * - Neither of the parts of the link are part of any list or tree
+ * - Neither of the parts of the link or the link data structure
+ * are referred to by any other structures
+ * If the above are not true, bad things will happen.
+ *
+ * @param[in] mdp Pointer to link data structure
+ *
+ */
+ERTS_GLB_INLINE void erts_link_release_both(ErtsLinkData *ldp);
+
+/**
+ *
+ * @brief Insert link in dist link list
+ *
+ * When the funcion is called it is assumed that:
+ * - 'lnk' link is not part of any list or tree
+ * If the above is not true, bad things will happen.
+ *
+ * @param[in] lnk Pointer to link
+ *
+ * @param[in] dist Pointer to dist structure
+ *
+ * @returns A non-zero value if inserted;
+ * otherwise, zero. The link
+ * is not inserted if the dist
+ * structure has been set in a
+ * dead state.
+ *
+ */
+ERTS_GLB_INLINE int erts_link_dist_insert(ErtsLink *lnk, ErtsMonLnkDist *dist);
+
+/**
+ *
+ * @brief Delete link from dist link list
+ *
+ * When the funcion is called it is assumed that:
+ * - 'lnk' link earler has been inserted into 'dist'
+ * If the above is not true, bad things will happen.
+ *
+ * @param[in] lnk Pointer to link
+ *
+ * @param[in] dist Pointer to dist structure
+ *
+ * @returns A non-zero value if deleted;
+ * otherwise, zero. The link
+ * is not deleted if the dist
+ * structure has been set in a
+ * dead state or if it has already
+ * been deleted.
+ *
+ */
+ERTS_GLB_INLINE int erts_link_dist_delete(ErtsLink *lnk);
+
+/**
+ *
+ * @brief Set dead dist structure on link
+ *
+ * @param[in] lnk Pointer to link
+ *
+ * @param[in] nodename Name of remote node
+ *
+ */
+void
+erts_link_set_dead_dist(ErtsLink *lnk, Eterm nodename);
+
+/**
+ *
+ * @brief Get charged size of link
+ *
+ * If the other side of the link has been released, the
+ * whole size of the link data structure is returned; otherwise,
+ * half of the size is returned.
+ *
+ * When the funcion is called it is assumed that:
+ * - 'lnk' has not been released
+ * If the above is not true, bad things will happen.
+ *
+ * @param[in] lnk Pointer to link
+ *
+ * @returns Charged size in bytes
+ *
+ */
+Uint erts_link_size(ErtsLink *lnk);
+
+/* internal function... */
+void erts_link_destroy__(ErtsLinkData *ldp);
+
+/* implementations for globally inlined link functions... */
+#if ERTS_GLB_INLINE_INCL_FUNC_DEF
+
+#ifdef ERTS_ML_DEBUG
+extern size_t erts_link_a_offset;
+extern size_t erts_link_b_offset;
+extern size_t erts_link_key_offset;
+#endif
+
+ERTS_GLB_INLINE ErtsLinkData *
+erts_link_to_data(ErtsLink *lnk)
+{
+ ErtsLinkData *ldp = erts_ml_node_to_main_struct__((ErtsMonLnkNode *) lnk);
+
+#ifdef ERTS_ML_DEBUG
+ ERTS_ML_ASSERT(erts_link_a_offset == (size_t) ldp->a.offset);
+ ERTS_ML_ASSERT(erts_link_key_offset == (size_t) ldp->a.key_offset);
+ ERTS_ML_ASSERT(erts_link_b_offset == (size_t) ldp->b.offset);
+ ERTS_ML_ASSERT(erts_link_key_offset == (size_t) ldp->b.key_offset);
+#endif
+
+ return ldp;
+}
+
+ERTS_GLB_INLINE ErtsLink *
+erts_link_to_other(ErtsLink *lnk, ErtsLinkData **ldpp)
+{
+ ErtsLinkData *ldp = erts_link_to_data(lnk);
+ if (ldpp)
+ *ldpp = ldp;
+ return lnk == &ldp->a ? &ldp->b : &ldp->a;
+}
+
+ERTS_GLB_INLINE int
+erts_link_is_in_table(ErtsLink *lnk)
+{
+ return !!(lnk->flags & ERTS_ML_FLG_IN_TABLE);
+}
+
+ERTS_GLB_INLINE void
+erts_link_list_insert(ErtsLink **list, ErtsLink *lnk)
+{
+ erts_ml_dl_list_insert__((ErtsMonLnkNode **) list, (ErtsMonLnkNode *) lnk);
+}
+
+ERTS_GLB_INLINE void
+erts_link_list_delete(ErtsLink **list, ErtsLink *lnk)
+{
+ erts_ml_dl_list_delete__((ErtsMonLnkNode **) list, (ErtsMonLnkNode *) lnk);
+}
+
+ERTS_GLB_INLINE ErtsLink *
+erts_link_list_first(ErtsLink *list)
+{
+ return (ErtsLink *) erts_ml_dl_list_first__((ErtsMonLnkNode *) list);
+}
+
+ERTS_GLB_INLINE ErtsLink *
+erts_link_list_last(ErtsLink *list)
+{
+ return (ErtsLink *) erts_ml_dl_list_last__((ErtsMonLnkNode *) list);
+}
+
+ERTS_GLB_INLINE void
+erts_link_release(ErtsLink *lnk)
+{
+ ErtsLinkData *ldp = erts_link_to_data(lnk);
+ ERTS_ML_ASSERT(!(lnk->flags & ERTS_ML_FLG_IN_TABLE));
+ ERTS_ML_ASSERT(erts_atomic32_read_nob(&ldp->refc) > 0);
+ if (erts_atomic32_dec_read_nob(&ldp->refc) == 0)
+ erts_link_destroy__(ldp);
+}
+
+ERTS_GLB_INLINE void
+erts_link_release_both(ErtsLinkData *ldp)
+{
+ ERTS_ML_ASSERT(!(ldp->a.flags & ERTS_ML_FLG_IN_TABLE));
+ ERTS_ML_ASSERT(!(ldp->b.flags & ERTS_ML_FLG_IN_TABLE));
+ ERTS_ML_ASSERT(erts_atomic32_read_nob(&ldp->refc) >= 2);
+ if (erts_atomic32_add_read_nob(&ldp->refc, (erts_aint32_t) -2) == 0)
+ erts_link_destroy__(ldp);
+}
+
+ERTS_GLB_INLINE ErtsLink *
+erts_link_tree_insert_addr_replace(ErtsLink **root, ErtsLink *lnk)
+{
+ ErtsLink *lnk2 = erts_link_tree_lookup_insert(root, lnk);
+ if (!lnk2)
+ return NULL;
+ if (lnk2 < lnk)
+ return lnk;
+ erts_link_tree_replace(root, lnk2, lnk);
+ return lnk2;
+}
+
+ERTS_GLB_INLINE ErtsLink *
+erts_link_tree_key_delete(ErtsLink **root, ErtsLink *lnk)
+{
+ ErtsLink *dlnk;
+ if (erts_link_is_in_table(lnk))
+ dlnk = lnk;
+ else
+ dlnk = erts_link_tree_lookup(*root, lnk->other.item);
+ if (dlnk)
+ erts_link_tree_delete(root, dlnk);
+ return dlnk;
+}
+
+ERTS_GLB_INLINE int
+erts_link_dist_insert(ErtsLink *lnk, ErtsMonLnkDist *dist)
+{
+ ErtsLinkDataExtended *ldep;
+ int insert;
+
+ ERTS_ML_ASSERT(lnk->flags & ERTS_ML_FLG_EXTENDED);
+ ERTS_ML_ASSERT(lnk->type == ERTS_LNK_TYPE_DIST_PROC);
+
+ ldep = (ErtsLinkDataExtended *) erts_link_to_data(lnk);
+
+ ERTS_ML_ASSERT(!ldep->dist);
+ ERTS_ML_ASSERT(dist);
+ ldep->dist = dist;
+
+ erts_mon_link_dist_inc_refc(dist);
+
+ erts_mtx_lock(&dist->mtx);
+
+ insert = dist->alive;
+ if (insert)
+ erts_link_list_insert(&dist->links, lnk);
+
+ erts_mtx_unlock(&dist->mtx);
+
+ return insert;
+}
+
+ERTS_GLB_INLINE int
+erts_link_dist_delete(ErtsLink *lnk)
+{
+ ErtsLinkDataExtended *ldep;
+ ErtsMonLnkDist *dist;
+ int delete;
+
+ ERTS_ML_ASSERT(lnk->flags & ERTS_ML_FLG_EXTENDED);
+ ERTS_ML_ASSERT(lnk->type == ERTS_LNK_TYPE_DIST_PROC);
+
+ ldep = (ErtsLinkDataExtended *) erts_link_to_data(lnk);
+ dist = ldep->dist;
+ if (!dist)
+ return -1;
+
+ erts_mtx_lock(&dist->mtx);
+
+ delete = !!dist->alive & !!(lnk->flags & ERTS_ML_FLG_IN_TABLE);
+ if (delete)
+ erts_link_list_delete(&dist->links, lnk);
+
+ erts_mtx_unlock(&dist->mtx);
+
+ return delete;
+}
+
+
+#endif /* ERTS_GLB_INLINE_INCL_FUNC_DEF */
+
+#endif /* ERL_MONITOR_LINK_H__ */
diff --git a/erts/emulator/beam/erl_monitors.c b/erts/emulator/beam/erl_monitors.c
deleted file mode 100644
index 1c840d89f6..0000000000
--- a/erts/emulator/beam/erl_monitors.c
+++ /dev/null
@@ -1,1073 +0,0 @@
-/*
- * %CopyrightBegin%
- *
- * Copyright Ericsson AB 2004-2017. 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%
- */
-
-/**************************************************************************
- * Monitors and links data structure manipulation.
- * Monitors and links are organized as AVL trees with the reference as
- * key in the monitor case and the pid of the linked process as key in the
- * link case. Lookups the order of the references is somewhat special. Local
- * references are strictly smaller than remote references and are sorted
- * by inlined comparison functionality. Remote references are handled by the
- * usual cmp function.
- * Each Monitor is tagged with different tags depending on which end of the
- * monitor it is.
- * A monitor is removed either explicitly by reference or all monitors are
- * removed when the process exits. No need to access the monitor by pid.
- **************************************************************************/
-
-#ifdef HAVE_CONFIG_H
-# include "config.h"
-#endif
-
-#include "sys.h"
-#include "erl_vm.h"
-#include "global.h"
-#include "erl_process.h"
-#include "error.h"
-#include "erl_db.h"
-#include "bif.h"
-#include "big.h"
-#include "erl_monitors.h"
-#include "erl_bif_unique.h"
-
-#define STACK_NEED 50
-#define MAX_MONITORS 0xFFFFFFFFUL
-
-#define DIR_LEFT 0
-#define DIR_RIGHT 1
-#define DIR_END 2
-
-static erts_atomic_t tot_link_lh_size;
-
-/* Implements the sort order in monitor trees, which is different from
- the ordinary term order.
- No short local ref's should ever exist (the ref is created by the bif's
- in runtime), therefore:
- All local ref's are less than external ref's
- Local ref's are inline-compared,
- External ref's are compared by cmp */
-
-#if 0
-#define CMP_MON_REF(Ref1,Ref2) \
-cmp((Ref1),(Ref2)) /* XXX, the inline comparision yet to be done */
-#else
-#define CMP_MON_REF(Ref1,Ref2) cmp_mon_ref((Ref1),(Ref2))
-#endif
-
-static ERTS_INLINE int cmp_mon_ref(Eterm ref1, Eterm ref2)
-{
- Eterm *b1, *b2;
-
-
- b1 = boxed_val(ref1);
- b2 = boxed_val(ref2);
- if (is_ref_thing_header(*b1)) {
- if (is_ref_thing_header(*b2)) {
- Uint32 *num1, *num2;
- if (is_ordinary_ref_thing(b1)) {
- ErtsORefThing *rtp = (ErtsORefThing *) b1;
- num1 = rtp->num;
- }
- else {
- ErtsMRefThing *mrtp = (ErtsMRefThing *) b1;
- num1 = mrtp->mb->refn;
- }
- if (is_ordinary_ref_thing(b2)) {
- ErtsORefThing *rtp = (ErtsORefThing *) b2;
- num2 = rtp->num;
- }
- else {
- ErtsMRefThing *mrtp = (ErtsMRefThing *) b2;
- num2 = mrtp->mb->refn;
- }
- return erts_internal_ref_number_cmp(num1, num2);
- }
- return -1;
- }
- if (is_ref_thing_header(*b2)) {
- return 1;
- }
- return CMP(ref1,ref2);
-}
-
-#define CP_LINK_VAL(To, Hp, From) \
-do { \
- if (is_immed(From)) \
- (To) = (From); \
- else { \
- Uint i__; \
- Uint len__; \
- ASSERT((Hp)); \
- ASSERT(is_internal_ordinary_ref((From)) \
- || is_external((From))); \
- (To) = make_boxed((Hp)); \
- len__ = thing_arityval(*boxed_val((From))) + 1; \
- for(i__ = 0; i__ < len__; i__++) \
- (*((Hp)++)) = boxed_val((From))[i__]; \
- if (is_external((To))) { \
- external_thing_ptr((To))->next = NULL; \
- erts_refc_inc(&(external_thing_ptr((To))->node->refc), 2);\
- } \
- } \
-} while (0)
-
-static ErtsMonitor *create_monitor(Uint type, Eterm ref, UWord entity, Eterm name)
-{
- Uint mon_size = ERTS_MONITOR_SIZE;
- ErtsMonitor *n;
- Eterm *hp;
-
- mon_size += NC_HEAP_SIZE(ref);
- if (type != MON_NIF_TARGET && is_not_immed(entity)) {
- mon_size += NC_HEAP_SIZE(entity);
- }
-
- if (mon_size <= ERTS_MONITOR_SH_SIZE) {
- n = (ErtsMonitor *) erts_alloc(ERTS_ALC_T_MONITOR_SH,
- mon_size*sizeof(Uint));
- } else {
- n = (ErtsMonitor *) erts_alloc(ERTS_ALC_T_MONITOR_LH,
- mon_size*sizeof(Uint));
- erts_atomic_add_nob(&tot_link_lh_size, mon_size*sizeof(Uint));
- }
- hp = n->heap;
-
-
- n->left = n->right = NULL; /* Always the same initial value*/
- n->type = (Uint16) type;
- n->balance = 0; /* Always the same initial value */
- n->name = name; /* atom() or [] */
- CP_LINK_VAL(n->ref, hp, ref); /*XXX Unnecessary check, never immediate*/
- if (type == MON_NIF_TARGET)
- n->u.resource = (ErtsResource*)entity;
- else
- CP_LINK_VAL(n->u.pid, hp, (Eterm)entity);
-
- return n;
-}
-
-static ErtsLink *create_link(Uint type, Eterm pid)
-{
- Uint lnk_size = ERTS_LINK_SIZE;
- ErtsLink *n;
- Eterm *hp;
-
- if (is_not_immed(pid)) {
- lnk_size += NC_HEAP_SIZE(pid);
- }
-
- if (lnk_size <= ERTS_LINK_SH_SIZE) {
- n = (ErtsLink *) erts_alloc(ERTS_ALC_T_NLINK_SH,
- lnk_size*sizeof(Uint));
- } else {
- n = (ErtsLink *) erts_alloc(ERTS_ALC_T_NLINK_LH,
- lnk_size*sizeof(Uint));
- erts_atomic_add_nob(&tot_link_lh_size, lnk_size*sizeof(Uint));
- }
- hp = n->heap;
-
-
- n->left = n->right = NULL; /* Always the same initial value*/
- n->type = (Uint16) type;
- n->balance = 0; /* Always the same initial value */
- if (n->type == LINK_NODE) {
- ERTS_LINK_REFC(n) = 0;
- } else {
- ERTS_LINK_ROOT(n) = NULL;
- }
- CP_LINK_VAL(n->pid, hp, pid);
-
- return n;
-}
-
-#undef CP_LINK_VAL
-
-static ErtsSuspendMonitor *create_suspend_monitor(Eterm pid)
-{
- ErtsSuspendMonitor *smon = erts_alloc(ERTS_ALC_T_SUSPEND_MON,
- sizeof(ErtsSuspendMonitor));
- smon->left = smon->right = NULL; /* Always the same initial value */
- smon->balance = 0; /* Always the same initial value */
- smon->pending = 0;
- smon->active = 0;
- smon->pid = pid;
- return smon;
-}
-
-void
-erts_init_monitors(void)
-{
- erts_atomic_init_nob(&tot_link_lh_size, 0);
-}
-
-Uint
-erts_tot_link_lh_size(void)
-{
- return (Uint) erts_atomic_read_nob(&tot_link_lh_size);
-}
-
-void erts_destroy_monitor(ErtsMonitor *mon)
-{
- Uint mon_size = ERTS_MONITOR_SIZE;
- ErlNode *node;
-
- ASSERT(is_not_immed(mon->ref));
- mon_size += NC_HEAP_SIZE(mon->ref);
- if (is_external(mon->ref)) {
- node = external_thing_ptr(mon->ref)->node;
- erts_deref_node_entry(node);
- }
- if (mon->type != MON_NIF_TARGET && is_not_immed(mon->u.pid)) {
- mon_size += NC_HEAP_SIZE(mon->u.pid);
- if (is_external(mon->u.pid)) {
- node = external_thing_ptr(mon->u.pid)->node;
- erts_deref_node_entry(node);
- }
- }
- if (mon_size <= ERTS_MONITOR_SH_SIZE) {
- erts_free(ERTS_ALC_T_MONITOR_SH, (void *) mon);
- } else {
- erts_free(ERTS_ALC_T_MONITOR_LH, (void *) mon);
- erts_atomic_add_nob(&tot_link_lh_size, -1*mon_size*sizeof(Uint));
- }
-}
-
-void erts_destroy_link(ErtsLink *lnk)
-{
- Uint lnk_size = ERTS_LINK_SIZE;
- ErlNode *node;
-
- ASSERT(lnk->type == LINK_NODE || ERTS_LINK_ROOT(lnk) == NULL);
-
- if (is_not_immed(lnk->pid)) {
- lnk_size += NC_HEAP_SIZE(lnk->pid);
- if (is_external(lnk->pid)) {
- node = external_thing_ptr(lnk->pid)->node;
- erts_deref_node_entry(node);
- }
- }
- if (lnk_size <= ERTS_LINK_SH_SIZE) {
- erts_free(ERTS_ALC_T_NLINK_SH, (void *) lnk);
- } else {
- erts_free(ERTS_ALC_T_NLINK_LH, (void *) lnk);
- erts_atomic_add_nob(&tot_link_lh_size, -1*lnk_size*sizeof(Uint));
- }
-}
-
-void erts_destroy_suspend_monitor(ErtsSuspendMonitor *smon)
-{
- erts_free(ERTS_ALC_T_SUSPEND_MON, smon);
-}
-
-static void insertion_rotation(int dstack[], int dpos,
- void *tstack[], int tpos,
- int state) {
-
- ErtsMonitorOrLink **this;
- ErtsMonitorOrLink *p1, *p2, *p;
- int dir;
-
- while (state && ( dir = dstack[--dpos] ) != DIR_END) {
- this = tstack[--tpos];
- p = *this;
- if (dir == DIR_LEFT) {
- switch (p->balance) {
- case 1:
- p->balance = 0;
- state = 0;
- break;
- case 0:
- p->balance = -1;
- break;
- case -1: /* The icky case */
- p1 = p->left;
- if (p1->balance == -1) { /* Single LL rotation */
- p->left = p1->right;
- p1->right = p;
- p->balance = 0;
- (*this) = p1;
- } else { /* Double RR rotation */
- p2 = p1->right;
- p1->right = p2->left;
- p2->left = p1;
- p->left = p2->right;
- p2->right = p;
- p->balance = (p2->balance == -1) ? +1 : 0;
- p1->balance = (p2->balance == 1) ? -1 : 0;
- (*this) = p2;
- }
- (*this)->balance = 0;
- state = 0;
- break;
- }
- } else { /* dir == DIR_RIGHT */
- switch (p->balance) {
- case -1:
- p->balance = 0;
- state = 0;
- break;
- case 0:
- p->balance = 1;
- break;
- case 1:
- p1 = p->right;
- if (p1->balance == 1) { /* Single RR rotation */
- p->right = p1->left;
- p1->left = p;
- p->balance = 0;
- (*this) = p1;
- } else { /* Double RL rotation */
- p2 = p1->left;
- p1->left = p2->right;
- p2->right = p1;
- p->right = p2->left;
- p2->left = p;
- p->balance = (p2->balance == 1) ? -1 : 0;
- p1->balance = (p2->balance == -1) ? 1 : 0;
- (*this) = p2;
- }
- (*this)->balance = 0;
- state = 0;
- break;
- }
- }
- }
-}
-
-void erts_add_monitor(ErtsMonitor **root, Uint type, Eterm ref, UWord entity,
- Eterm name)
-{
- void *tstack[STACK_NEED];
- int tpos = 0;
- int dstack[STACK_NEED+1];
- int dpos = 1;
- int state = 0;
- ErtsMonitor **this = root;
- Sint c;
-
- ASSERT(is_internal_ordinary_ref(ref) || is_external_ref(ref));
-
- dstack[0] = DIR_END;
- for (;;) {
- if (!*this) { /* Found our place */
- state = 1;
- *this = create_monitor(type,ref,entity,name);
- break;
- } else if ((c = CMP_MON_REF(ref,(*this)->ref)) < 0) {
- /* go left */
- dstack[dpos++] = DIR_LEFT;
- tstack[tpos++] = this;
- this = &((*this)->left);
- } else if (c > 0) { /* go right */
- dstack[dpos++] = DIR_RIGHT;
- tstack[tpos++] = this;
- this = &((*this)->right);
- } else { /* Equal key is an error for monitors */
- erts_exit(ERTS_ERROR_EXIT,"Insertion of already present monitor!");
- break;
- }
- }
- insertion_rotation(dstack, dpos, tstack, tpos, state);
-}
-
-
-/* Returns 0 if OK, < 0 if already present */
-int erts_add_link(ErtsLink **root, Uint type, Eterm pid)
-{
- void *tstack[STACK_NEED];
- int tpos = 0;
- int dstack[STACK_NEED+1];
- int dpos = 1;
- int state = 0;
- ErtsLink **this = root;
- Sint c;
-
- dstack[0] = DIR_END;
- for (;;) {
- if (!*this) { /* Found our place */
- state = 1;
- *this = create_link(type,pid);
- break;
- } else if ((c = CMP(pid,(*this)->pid)) < 0) {
- /* go left */
- dstack[dpos++] = DIR_LEFT;
- tstack[tpos++] = this;
- this = &((*this)->left);
- } else if (c > 0) { /* go right */
- dstack[dpos++] = DIR_RIGHT;
- tstack[tpos++] = this;
- this = &((*this)->right);
- } else { /* Equal key is an error for monitors */
- return -1;
- }
- }
- insertion_rotation(dstack, dpos, tstack, tpos, state);
- return 0;
-}
-
-ErtsSuspendMonitor *
-erts_add_or_lookup_suspend_monitor(ErtsSuspendMonitor **root, Eterm pid)
-{
- void *tstack[STACK_NEED];
- int tpos = 0;
- int dstack[STACK_NEED+1];
- int dpos = 1;
- int state = 0;
- ErtsSuspendMonitor **this = root;
- ErtsSuspendMonitor *res;
- Sint c;
-
- dstack[0] = DIR_END;
- for (;;) {
- if (!*this) { /* Found our place */
- state = 1;
- res = *this = create_suspend_monitor(pid);
- break;
- } else if ((c = CMP(pid,(*this)->pid)) < 0) {
- /* go left */
- dstack[dpos++] = DIR_LEFT;
- tstack[tpos++] = this;
- this = &((*this)->left);
- } else if (c > 0) { /* go right */
- dstack[dpos++] = DIR_RIGHT;
- tstack[tpos++] = this;
- this = &((*this)->right);
- } else { /* Already here... */
- ASSERT((*this)->pid == pid);
- return *this;
- }
- }
- insertion_rotation(dstack, dpos, tstack, tpos, state);
- return res;
-}
-
-
-/* Returns the new or old link structure */
-ErtsLink *erts_add_or_lookup_link(ErtsLink **root, Uint type, Eterm pid)
-{
- void *tstack[STACK_NEED];
- int tpos = 0;
- int dstack[STACK_NEED+1];
- int dpos = 1;
- int state = 0;
- ErtsLink **this = root;
- Sint c;
- ErtsLink *ret = NULL;
-
- dstack[0] = DIR_END;
- for (;;) {
- if (!*this) { /* Found our place */
- state = 1;
- *this = create_link(type,pid);
- ret = *this;
- break;
- } else if ((c = CMP(pid,(*this)->pid)) < 0) {
- /* go left */
- dstack[dpos++] = DIR_LEFT;
- tstack[tpos++] = this;
- this = &((*this)->left);
- } else if (c > 0) { /* go right */
- dstack[dpos++] = DIR_RIGHT;
- tstack[tpos++] = this;
- this = &((*this)->right);
- } else { /* Equal key is an error for monitors */
- return *this;
- }
- }
- insertion_rotation(dstack, dpos, tstack, tpos, state);
- return ret;
-}
-
-
-/*
- * Deletion helpers
- */
-static int balance_left(ErtsMonitorOrLink **this)
-{
- ErtsMonitorOrLink *p, *p1, *p2;
- int b1, b2, h = 1;
-
- p = *this;
- switch (p->balance) {
- case -1:
- p->balance = 0;
- break;
- case 0:
- p->balance = 1;
- h = 0;
- break;
- case 1:
- p1 = p->right;
- b1 = p1->balance;
- if (b1 >= 0) { /* Single RR rotation */
- p->right = p1->left;
- p1->left = p;
- if (b1 == 0) {
- p->balance = 1;
- p1->balance = -1;
- h = 0;
- } else {
- p->balance = p1->balance = 0;
- }
- (*this) = p1;
- } else { /* Double RL rotation */
- p2 = p1->left;
- b2 = p2->balance;
- p1->left = p2->right;
- p2->right = p1;
- p->right = p2->left;
- p2->left = p;
- p->balance = (b2 == 1) ? -1 : 0;
- p1->balance = (b2 == -1) ? 1 : 0;
- p2->balance = 0;
- (*this) = p2;
- }
- break;
- }
- return h;
-}
-
-static int balance_right(ErtsMonitorOrLink **this)
-{
- ErtsMonitorOrLink *p, *p1, *p2;
- int b1, b2, h = 1;
-
- p = *this;
- switch (p->balance) {
- case 1:
- p->balance = 0;
- break;
- case 0:
- p->balance = -1;
- h = 0;
- break;
- case -1:
- p1 = p->left;
- b1 = p1->balance;
- if (b1 <= 0) { /* Single LL rotation */
- p->left = p1->right;
- p1->right = p;
- if (b1 == 0) {
- p->balance = -1;
- p1->balance = 1;
- h = 0;
- } else {
- p->balance = p1->balance = 0;
- }
- (*this) = p1;
- } else { /* Double LR rotation */
- p2 = p1->right;
- b2 = p2->balance;
- p1->right = p2->left;
- p2->left = p1;
- p->left = p2->right;
- p2->right = p;
- p->balance = (b2 == -1) ? 1 : 0;
- p1->balance = (b2 == 1) ? -1 : 0;
- p2->balance = 0;
- (*this) = p2;
- }
- }
- return h;
-}
-
-static int delsub(ErtsMonitorOrLink **this)
-{
- ErtsMonitorOrLink **tstack[STACK_NEED];
- int tpos = 0;
- ErtsMonitorOrLink *q = (*this);
- ErtsMonitorOrLink **r = &(q->left);
- int h;
-
- /*
- * Walk down the tree to the right and search
- * for a void right child, pick that child out
- * and return it to be put in the deleted
- * object's place.
- */
-
- while ((*r)->right != NULL) {
- tstack[tpos++] = r;
- r = &((*r)->right);
- }
- *this = *r;
- *r = (*r)->left;
- (*this)->left = q->left;
- (*this)->right = q->right;
- (*this)->balance = q->balance;
- tstack[0] = &((*this)->left);
- h = 1;
- while (tpos && h) {
- r = tstack[--tpos];
- h = balance_right(r);
- }
- return h;
-}
-
-ErtsMonitor *erts_remove_monitor(ErtsMonitor **root, Eterm ref)
-{
- ErtsMonitor **tstack[STACK_NEED];
- int tpos = 0;
- int dstack[STACK_NEED+1];
- int dpos = 1;
- int state = 0;
- ErtsMonitor **this = root;
- Sint c;
- int dir;
- ErtsMonitor *q = NULL;
-
- dstack[0] = DIR_END;
- for (;;) {
- if (!*this) { /* Failure */
- return NULL;
- } else if ((c = CMP_MON_REF(ref,(*this)->ref)) < 0) {
- dstack[dpos++] = DIR_LEFT;
- tstack[tpos++] = this;
- this = &((*this)->left);
- } else if (c > 0) { /* go right */
- dstack[dpos++] = DIR_RIGHT;
- tstack[tpos++] = this;
- this = &((*this)->right);
- } else { /* Equal key, found the one to delete */
- q = (*this);
- if (q->right == NULL) {
- (*this) = q->left;
- state = 1;
- } else if (q->left == NULL) {
- (*this) = q->right;
- state = 1;
- } else {
- dstack[dpos++] = DIR_LEFT;
- tstack[tpos++] = this;
- state = delsub((ErtsMonitorOrLink **) this);
- }
- break;
- }
- }
- while (state && ( dir = dstack[--dpos] ) != DIR_END) {
- this = tstack[--tpos];
- if (dir == DIR_LEFT) {
- state = balance_left((ErtsMonitorOrLink **) this);
- } else {
- state = balance_right((ErtsMonitorOrLink **) this);
- }
- }
- return q;
-}
-
-ErtsLink *erts_remove_link(ErtsLink **root, Eterm pid)
-{
- ErtsLink **tstack[STACK_NEED];
- int tpos = 0;
- int dstack[STACK_NEED+1];
- int dpos = 1;
- int state = 0;
- ErtsLink **this = root;
- Sint c;
- int dir;
- ErtsLink *q = NULL;
-
- dstack[0] = DIR_END;
- for (;;) {
- if (!*this) { /* Failure */
- return NULL;
- } else if ((c = CMP(pid,(*this)->pid)) < 0) {
- dstack[dpos++] = DIR_LEFT;
- tstack[tpos++] = this;
- this = &((*this)->left);
- } else if (c > 0) { /* go right */
- dstack[dpos++] = DIR_RIGHT;
- tstack[tpos++] = this;
- this = &((*this)->right);
- } else { /* Equal key, found the one to delete */
- q = (*this);
- if (q->right == NULL) {
- (*this) = q->left;
- state = 1;
- } else if (q->left == NULL) {
- (*this) = q->right;
- state = 1;
- } else {
- dstack[dpos++] = DIR_LEFT;
- tstack[tpos++] = this;
- state = delsub((ErtsMonitorOrLink **) this);
- }
- break;
- }
- }
- while (state && ( dir = dstack[--dpos] ) != DIR_END) {
- this = tstack[--tpos];
- if (dir == DIR_LEFT) {
- state = balance_left((ErtsMonitorOrLink **) this);
- } else {
- state = balance_right((ErtsMonitorOrLink **) this);
- }
- }
- return q;
-}
-
-void
-erts_delete_suspend_monitor(ErtsSuspendMonitor **root, Eterm pid)
-{
- ErtsSuspendMonitor **tstack[STACK_NEED];
- int tpos = 0;
- int dstack[STACK_NEED+1];
- int dpos = 1;
- int state = 0;
- ErtsSuspendMonitor **this = root;
- Sint c;
- int dir;
- ErtsSuspendMonitor *q = NULL;
-
- dstack[0] = DIR_END;
- for (;;) {
- if (!*this) { /* Nothing found */
- return;
- } else if ((c = CMP(pid,(*this)->pid)) < 0) {
- dstack[dpos++] = DIR_LEFT;
- tstack[tpos++] = this;
- this = &((*this)->left);
- } else if (c > 0) { /* go right */
- dstack[dpos++] = DIR_RIGHT;
- tstack[tpos++] = this;
- this = &((*this)->right);
- } else { /* Equal key, found the one to delete */
- q = (*this);
- ASSERT(q->pid == pid);
- if (q->right == NULL) {
- (*this) = q->left;
- state = 1;
- } else if (q->left == NULL) {
- (*this) = q->right;
- state = 1;
- } else {
- dstack[dpos++] = DIR_LEFT;
- tstack[tpos++] = this;
- state = delsub((ErtsMonitorOrLink **) this);
- }
- erts_destroy_suspend_monitor(q);
- break;
- }
- }
- while (state && ( dir = dstack[--dpos] ) != DIR_END) {
- this = tstack[--tpos];
- if (dir == DIR_LEFT) {
- state = balance_left((ErtsMonitorOrLink **) this);
- } else {
- state = balance_right((ErtsMonitorOrLink **) this);
- }
- }
-}
-
-ErtsMonitor *erts_lookup_monitor(ErtsMonitor *root, Eterm ref)
-{
- Sint c;
-
- for (;;) {
- if (root == NULL || (c = CMP_MON_REF(ref,root->ref)) == 0) {
- return root;
- } else if (c < 0) {
- root = root->left;
- } else { /* c > 0 */
- root = root->right;
- }
- }
-}
-
-ErtsLink *erts_lookup_link(ErtsLink *root, Eterm pid)
-{
- Sint c;
-
- for (;;) {
- if (root == NULL || (c = CMP(pid,root->pid)) == 0) {
- return root;
- } else if (c < 0) {
- root = root->left;
- } else { /* c > 0 */
- root = root->right;
- }
- }
-}
-
-ErtsSuspendMonitor *
-erts_lookup_suspend_monitor(ErtsSuspendMonitor *root, Eterm pid)
-{
- Sint c;
-
- for (;;) {
- if (root == NULL || (c = CMP(pid,root->pid)) == 0) {
- return root;
- } else if (c < 0) {
- root = root->left;
- } else { /* c > 0 */
- root = root->right;
- }
- }
-}
-
-void erts_sweep_monitors(ErtsMonitor *root,
- void (*doit)(ErtsMonitor *, void *),
- void *context)
-{
- ErtsMonitor *tstack[STACK_NEED];
- int tpos = 0;
- int dstack[STACK_NEED+1];
- int dpos = 1;
- int dir;
-
- dstack[0] = DIR_END;
-
- for (;;) {
- if (root == NULL) {
- if ((dir = dstack[dpos-1]) == DIR_END) {
- return;
- }
- if (dir == DIR_LEFT) {
- /* Still has DIR_RIGHT to do */
- dstack[dpos-1] = DIR_RIGHT;
- root = (tstack[tpos-1])->right;
- } else {
- /* stacktop is an object to be deleted */
- (*doit)(tstack[--tpos],context); /* expeted to do the
- deletion */
- --dpos;
- root = NULL;
- }
- } else {
- dstack[dpos++] = DIR_LEFT;
- tstack[tpos++] = root;
- root = root->left;
- }
- }
-}
-
-void erts_sweep_links(ErtsLink *root,
- void (*doit)(ErtsLink *, void *),
- void *context)
-{
- ErtsLink *tstack[STACK_NEED];
- int tpos = 0;
- int dstack[STACK_NEED+1];
- int dpos = 1;
- int dir;
-
- dstack[0] = DIR_END;
-
- for (;;) {
- if (root == NULL) {
- if ((dir = dstack[dpos-1]) == DIR_END) {
- return;
- }
- if (dir == DIR_LEFT) {
- /* Still has DIR_RIGHT to do */
- dstack[dpos-1] = DIR_RIGHT;
- root = (tstack[tpos-1])->right;
- } else {
- /* stacktop is an object to be deleted */
- (*doit)(tstack[--tpos],context); /* expeted to do the
- deletion */
- --dpos;
- root = NULL;
- }
- } else {
- dstack[dpos++] = DIR_LEFT;
- tstack[tpos++] = root;
- root = root->left;
- }
- }
-}
-
-void erts_sweep_suspend_monitors(ErtsSuspendMonitor *root,
- void (*doit)(ErtsSuspendMonitor *, void *),
- void *context)
-{
- ErtsSuspendMonitor *tstack[STACK_NEED];
- int tpos = 0;
- int dstack[STACK_NEED+1];
- int dpos = 1;
- int dir;
-
- dstack[0] = DIR_END;
-
- for (;;) {
- if (root == NULL) {
- if ((dir = dstack[dpos-1]) == DIR_END) {
- return;
- }
- if (dir == DIR_LEFT) {
- /* Still has DIR_RIGHT to do */
- dstack[dpos-1] = DIR_RIGHT;
- root = (tstack[tpos-1])->right;
- } else {
- /* stacktop is an object to be deleted */
- (*doit)(tstack[--tpos],context); /* expeted to do the
- deletion */
- --dpos;
- root = NULL;
- }
- } else {
- dstack[dpos++] = DIR_LEFT;
- tstack[tpos++] = root;
- root = root->left;
- }
- }
-}
-
-
-/* Debug BIF, always present, but undocumented... */
-
-static void erts_dump_monitors(ErtsMonitor *root, int indent)
-{
- if (root == NULL)
- return;
- erts_dump_monitors(root->right,indent+2);
- erts_printf("%*s[%b16d:%b16u:%T:%T", indent, "", root->balance,
- root->type, root->ref, root->name);
- if (root->type == MON_NIF_TARGET)
- erts_printf(":%p]\n", root->u.resource);
- else
- erts_printf(":%T]\n", root->u.pid);
- erts_dump_monitors(root->left,indent+2);
-}
-
-static void erts_dump_links_aux(ErtsLink *root, int indent,
- erts_dsprintf_buf_t *dsbufp)
-{
- if (root == NULL)
- return;
- erts_dump_links_aux(root->right, indent+2, dsbufp);
- dsbufp->str_len = 0;
- erts_dsprintf(dsbufp, "%*s[%b16d:%b16u:%T:%p]", indent, "",
- root->balance, root->type, root->pid, ERTS_LINK_ROOT(root));
- if (ERTS_LINK_ROOT(root) != NULL) {
- ErtsLink *sub = ERTS_LINK_ROOT(root);
- int len = dsbufp->str_len;
- erts_dump_links_aux(sub->right, indent+len+5, dsbufp);
- erts_dsprintf(dsbufp, "-> %*s[%b16d:%b16u:%T:%p]", indent, "",
- sub->balance, sub->type, sub->pid, ERTS_LINK_ROOT(sub));
- erts_printf("%s\n", dsbufp->str);
- erts_dump_links_aux(sub->left, indent+len+5, dsbufp);
- } else {
- erts_printf("%s\n", dsbufp->str);
- }
- erts_dump_links_aux(root->left, indent+2, dsbufp);
-}
-
-static void erts_dump_links(ErtsLink *root, int indent)
-{
- erts_dsprintf_buf_t *dsbufp = erts_create_tmp_dsbuf(0);
- erts_dump_links_aux(root, indent, dsbufp);
- erts_destroy_tmp_dsbuf(dsbufp);
-}
-
-Eterm erts_debug_dump_monitors_1(BIF_ALIST_1)
-{
- Process *p = BIF_P;
- Eterm pid = BIF_ARG_1;
- Process *rp;
- DistEntry *dep;
- rp = erts_pid2proc(p, ERTS_PROC_LOCK_MAIN, pid, ERTS_PROC_LOCK_LINK);
- if (!rp) {
- ERTS_ASSERT_IS_NOT_EXITING(p);
- if (is_atom(pid) && is_node_name_atom(pid) &&
- (dep = erts_find_dist_entry(pid)) != NULL) {
- erts_printf("Dumping dist monitors-------------------\n");
- erts_de_links_lock(dep);
- erts_dump_monitors(dep->monitors,0);
- erts_de_links_unlock(dep);
- erts_printf("Monitors dumped-------------------------\n");
- BIF_RET(am_true);
- } else {
- BIF_ERROR(p,BADARG);
- }
- } else {
- erts_printf("Dumping pid monitors--------------------\n");
- erts_dump_monitors(ERTS_P_MONITORS(rp),0);
- erts_printf("Monitors dumped-------------------------\n");
- erts_proc_unlock(rp, ERTS_PROC_LOCK_LINK);
- BIF_RET(am_true);
- }
-}
-
-Eterm erts_debug_dump_links_1(BIF_ALIST_1)
-{
- Process *p = BIF_P;
- Eterm pid = BIF_ARG_1;
- Process *rp;
- DistEntry *dep;
- if (is_internal_port(pid)) {
- Port *rport = erts_id2port_sflgs(pid,
- p,
- ERTS_PROC_LOCK_MAIN,
- ERTS_PORT_SFLGS_INVALID_LOOKUP);
- if (rport) {
- erts_printf("Dumping port links----------------------\n");
- erts_dump_links(ERTS_P_LINKS(rport), 0);
- erts_printf("Links dumped----------------------------\n");
- erts_port_release(rport);
- BIF_RET(am_true);
- } else {
- BIF_ERROR(p,BADARG);
- }
- } else {
- rp = erts_pid2proc(p, ERTS_PROC_LOCK_MAIN, pid, ERTS_PROC_LOCK_LINK);
- if (!rp) {
- ERTS_ASSERT_IS_NOT_EXITING(p);
- if (is_atom(pid) && is_node_name_atom(pid) &&
- (dep = erts_find_dist_entry(pid)) != NULL) {
- erts_printf("Dumping dist links----------------------\n");
- erts_de_links_lock(dep);
- erts_dump_links(dep->nlinks,0);
- erts_de_links_unlock(dep);
- erts_printf("Links dumped----------------------------\n");
- BIF_RET(am_true);
- } else {
- BIF_ERROR(p,BADARG);
- }
-
- } else {
- erts_printf("Dumping pid links-----------------------\n");
- erts_dump_links(ERTS_P_LINKS(rp), 0);
- erts_printf("Links dumped----------------------------\n");
- erts_proc_unlock(rp, ERTS_PROC_LOCK_LINK);
- BIF_RET(am_true);
- }
- }
-}
-
-void erts_one_link_size(ErtsLink *lnk, void *vpu)
-{
- Uint *pu = vpu;
- *pu += ERTS_LINK_SIZE*sizeof(Uint);
- if(is_not_immed(lnk->pid))
- *pu += NC_HEAP_SIZE(lnk->pid)*sizeof(Uint);
- if (lnk->type != LINK_NODE && ERTS_LINK_ROOT(lnk) != NULL) {
- erts_doforall_links(ERTS_LINK_ROOT(lnk),&erts_one_link_size,vpu);
- }
-}
-void erts_one_mon_size(ErtsMonitor *mon, void *vpu)
-{
- Uint *pu = vpu;
- *pu += ERTS_MONITOR_SIZE*sizeof(Uint);
- if(mon->type != MON_NIF_TARGET && is_not_immed(mon->u.pid))
- *pu += NC_HEAP_SIZE(mon->u.pid)*sizeof(Uint);
- if(is_not_immed(mon->ref))
- *pu += NC_HEAP_SIZE(mon->ref)*sizeof(Uint);
-}
diff --git a/erts/emulator/beam/erl_monitors.h b/erts/emulator/beam/erl_monitors.h
deleted file mode 100644
index 1cacecd7e9..0000000000
--- a/erts/emulator/beam/erl_monitors.h
+++ /dev/null
@@ -1,187 +0,0 @@
-/*
- * %CopyrightBegin%
- *
- * Copyright Ericsson AB 2004-2017. 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%
- */
-
-/**********************************************************************
- * Header for monitors and links data structures.
- * Monitors are kept in an AVL tree and the data structures for
- * the four different types of monitors are like this:
- **********************************************************************
- * Local monitor by pid/port:
- * (Ref is always same in all involved data structures)
- **********************************************************************
- * Process/Port X Process Y
- * +-------------+ +-------------+
- * Type: | MON_ORIGIN | | MON_TARGET |
- * +-------------+ +-------------+
- * Pid: | Pid(Y) | | Pid/Port(X) |
- * +-------------+ +-------------+
- * Name: | [] | | [] |
- * +-------------+ +-------------+
- **********************************************************************
- * Local monitor by name: (Ref is always same in all involved data structures)
- **********************************************************************
- * Process X Process Y (name foo)
- * +-------------+ +-------------+
- * Type: | MON_ORIGIN | | MON_TARGET |
- * +-------------+ +-------------+
- * Pid: | Pid(Y) | | Pid(X) |
- * +-------------+ +-------------+
- * Name: | Atom(foo) | | Atom(foo) |
- * +-------------+ +-------------+
- **********************************************************************
- * Remote monitor by pid: (Ref is always same in all involved data structures)
- **********************************************************************
- * Node A | Node B
- * ---------------------------------+----------------------------------
- * Process X (@A) Distentry @A Distentry @B Process Y (@B)
- * for node B for node A
- * +-------------+ +-------------+ +-------------+ +-------------+
- * Type: | MON_ORIGIN | | MON_TARGET | | MON_ORIGIN | | MON_TARGET |
- * +-------------+ +-------------+ +-------------+ +-------------+
- * Pid: | Pid(Y) | | Pid(X) | | Pid(Y) | | Pid(X) |
- * +-------------+ +-------------+ +-------------+ +-------------+
- * Name: | [] | | [] | | [] | | [] |
- * +-------------+ +-------------+ +-------------+ +-------------+
- **********************************************************************
- * Remote monitor by name: (Ref is always same in all involved data structures)
- **********************************************************************
- * Node A | Node B
- * ---------------------------------+----------------------------------
- * Process X (@A) Distentry @A Distentry @B Process Y (@B)
- * for node B for node A (name foo)
- * +-------------+ +-------------+ +-------------+ +-------------+
- * Type: | MON_ORIGIN | | MON_TARGET | | MON_ORIGIN | | MON_TARGET |
- * +-------------+ +-------------+ +-------------+ +-------------+
- * Pid: | Atom(node B)| | Pid(X) | | Pid(Y) | | Pid(X) |
- * +-------------+ +-------------+ +-------------+ +-------------+
- * Name: | Atom(foo) | | Atom(foo) | | Atom(foo) | | Atom(foo) |
- * +-------------+ +-------------+ +-------------+ +-------------+
- * The reason for the node atom in X->pid is that we don't know the actual
- * pid of the monitored process on the other node when setting the monitor
- * (which is done asyncronously).
- **********************************************************************/
-#ifndef _ERL_MONITORS_H
-#define _ERL_MONITORS_H
-
-/* Type tags for monitors */
-#define MON_ORIGIN 1
-#define MON_TARGET 2
-#define MON_NIF_TARGET 3
-#define MON_TIME_OFFSET 4
-
-/* Type tags for links */
-#define LINK_PID 1 /* ...Or port */
-#define LINK_NODE 3 /* "Node monitor" */
-
-/* Size of a monitor without heap, in words (fixalloc) */
-#define ERTS_MONITOR_SIZE ((sizeof(ErtsMonitor) - sizeof(Uint))/sizeof(Uint))
-#define ERTS_MONITOR_SH_SIZE (ERTS_MONITOR_SIZE + ERTS_REF_THING_SIZE)
-#define ERTS_LINK_SIZE ((sizeof(ErtsLink) - sizeof(Uint))/sizeof(Uint))
-#define ERTS_LINK_SH_SIZE ERTS_LINK_SIZE /* Size of fix-alloced links */
-
-/* ErtsMonitor and ErtsLink *need* to begin in a similar way as
- ErtsMonitorOrLink */
-typedef struct erts_monitor_or_link {
- struct erts_monitor_or_link *left, *right;
- Sint16 balance;
-} ErtsMonitorOrLink;
-
-typedef struct erts_monitor {
- struct erts_monitor *left, *right;
- Sint16 balance;
- Uint16 type; /* MON_ORIGIN | MON_TARGET | MON_NIF_TARGET | MON_TIME_OFFSET */
- Eterm ref;
- union {
- Eterm pid; /* In case of distributed named monitor, this is the
- * nodename atom in MON_ORIGIN process, otherwise a pid or,
- * in case of a MON_TARGET, a port
- */
- struct ErtsResource_* resource; /* MON_NIF_TARGET */
- }u;
- Eterm name; /* When monitoring a named process: atom() else [] */
- Uint heap[1]; /* Larger in reality */
-} ErtsMonitor;
-
-typedef struct erts_link {
- struct erts_link *left, *right;
- Sint16 balance;
- Uint16 type; /* LINK_PID | LINK_NODE */
- Eterm pid; /* When node monitor,
- the node atom is here instead */
- union {
- struct erts_link *root; /* Used only in dist entries */
- Uint refc;
- } shared;
- Uint heap[1]; /* Larger in reality */
-} ErtsLink;
-
-typedef struct erts_suspend_monitor {
- struct erts_suspend_monitor *left, *right;
- Sint16 balance;
-
- int pending;
- int active;
- Eterm pid;
-} ErtsSuspendMonitor;
-
-#define ERTS_LINK_ROOT(Linkp) ((Linkp)->shared.root)
-#define ERTS_LINK_REFC(Linkp) ((Linkp)->shared.refc)
-
-Uint erts_tot_link_lh_size(void);
-
-
-/* Prototypes */
-void erts_destroy_monitor(ErtsMonitor *mon);
-void erts_add_monitor(ErtsMonitor **root, Uint type, Eterm ref, UWord entity,
- Eterm name);
-ErtsMonitor *erts_remove_monitor(ErtsMonitor **root, Eterm ref);
-ErtsMonitor *erts_lookup_monitor(ErtsMonitor *root, Eterm ref);
-void erts_sweep_monitors(ErtsMonitor *root,
- void (*doit)(ErtsMonitor *, void *),
- void *context);
-
-void erts_destroy_link(ErtsLink *lnk);
-/* Returns 0 if OK, < 0 if already present */
-int erts_add_link(ErtsLink **root, Uint type, Eterm pid);
-ErtsLink *erts_add_or_lookup_link(ErtsLink **root, Uint type, Eterm pid);
-ErtsLink *erts_remove_link(ErtsLink **root, Eterm pid);
-ErtsLink *erts_lookup_link(ErtsLink *root, Eterm pid);
-void erts_sweep_links(ErtsLink *root,
- void (*doit)(ErtsLink *, void *),
- void *context);
-
-void erts_destroy_suspend_monitor(ErtsSuspendMonitor *sproc);
-void erts_sweep_suspend_monitors(ErtsSuspendMonitor *root,
- void (*doit)(ErtsSuspendMonitor *, void *),
- void *context);
-ErtsSuspendMonitor *erts_add_or_lookup_suspend_monitor(ErtsSuspendMonitor **root,
- Eterm pid);
-ErtsSuspendMonitor *erts_lookup_suspend_monitor(ErtsSuspendMonitor *root,
- Eterm pid);
-void erts_delete_suspend_monitor(ErtsSuspendMonitor **root, Eterm pid);
-void erts_init_monitors(void);
-void erts_one_link_size(ErtsLink *lnk, void *vpu);
-void erts_one_mon_size(ErtsMonitor *mon, void *vpu);
-
-#define erts_doforall_monitors erts_sweep_monitors
-#define erts_doforall_links erts_sweep_links
-#define erts_doforall_suspend_monitors erts_sweep_suspend_monitors
-
-#endif /* _ERL_MONITORS_H */
diff --git a/erts/emulator/beam/erl_msacc.c b/erts/emulator/beam/erl_msacc.c
index d659842b7e..d13d6080e1 100644
--- a/erts/emulator/beam/erl_msacc.c
+++ b/erts/emulator/beam/erl_msacc.c
@@ -81,7 +81,7 @@ void erts_msacc_init(void) {
sizeof(Eterm)*ERTS_MSACC_STATE_COUNT);
for (i = 0; i < ERTS_MSACC_STATE_COUNT; i++) {
erts_msacc_state_atoms[i] = am_atom_put(erts_msacc_states[i],
- strlen(erts_msacc_states[i]));
+ sys_strlen(erts_msacc_states[i]));
}
}
@@ -191,7 +191,7 @@ Eterm erts_msacc_gather_stats(ErtsMsAcc *msacc, ErtsHeapFactory *factory) {
map->keys = key;
hp[0] = state_map;
hp[1] = msacc->id;
- hp[2] = am_atom_put(msacc->type,strlen(msacc->type));
+ hp[2] = am_atom_put(msacc->type,sys_strlen(msacc->type));
return make_flatmap(map);
}
diff --git a/erts/emulator/beam/erl_msacc.h b/erts/emulator/beam/erl_msacc.h
index 2588dec903..895b1ae319 100644
--- a/erts/emulator/beam/erl_msacc.h
+++ b/erts/emulator/beam/erl_msacc.h
@@ -159,12 +159,12 @@ struct erl_msacc_t_ {
#ifdef ERTS_ENABLE_MSACC
-extern erts_tsd_key_t erts_msacc_key;
+extern erts_tsd_key_t ERTS_WRITE_UNLIKELY(erts_msacc_key);
#ifdef ERTS_MSACC_ALWAYS_ON
#define erts_msacc_enabled 1
#else
-extern int erts_msacc_enabled;
+extern int ERTS_WRITE_UNLIKELY(erts_msacc_enabled);
#endif
#define ERTS_MSACC_TSD_GET() erts_tsd_get(erts_msacc_key)
diff --git a/erts/emulator/beam/erl_mtrace.c b/erts/emulator/beam/erl_mtrace.c
index f2a660f085..2807b443a1 100644
--- a/erts/emulator/beam/erl_mtrace.c
+++ b/erts/emulator/beam/erl_mtrace.c
@@ -314,7 +314,7 @@ disable_trace(int error, char *reason, int eno)
erts_fprintf(stderr, "%s: %s\n", mt_dis, reason);
else {
eno_str = erl_errno_id(eno);
- if (strcmp(eno_str, "unknown") == 0)
+ if (sys_strcmp(eno_str, "unknown") == 0)
erts_fprintf(stderr, "%s: %s: %d\n", mt_dis, reason, eno);
else
erts_fprintf(stderr, "%s: %s: %s\n", mt_dis, reason, eno_str);
@@ -401,9 +401,9 @@ write_trace_header(char *nodename, char *pid, char *hostname)
PUT_UI32(tracep, ERTS_MT_MAJOR_VSN);
PUT_UI32(tracep, ERTS_MT_MINOR_VSN);
- n_len = strlen(nodename);
- h_len = strlen(hostname);
- p_len = strlen(pid);
+ n_len = sys_strlen(nodename);
+ h_len = sys_strlen(hostname);
+ p_len = sys_strlen(pid);
hdr_prolog_len = (2*UI32_SZ
+ 3*UI16_SZ
+ 3*UI32_SZ
@@ -436,15 +436,15 @@ write_trace_header(char *nodename, char *pid, char *hostname)
PUT_UI32(tracep, start_time.usec);
PUT_UI8(tracep, (byte) n_len);
- memcpy((void *) tracep, (void *) nodename, n_len);
+ sys_memcpy((void *) tracep, (void *) nodename, n_len);
tracep += n_len;
PUT_UI8(tracep, (byte) h_len);
- memcpy((void *) tracep, (void *) hostname, h_len);
+ sys_memcpy((void *) tracep, (void *) hostname, h_len);
tracep += h_len;
PUT_UI8(tracep, (byte) p_len);
- memcpy((void *) tracep, (void *) pid, p_len);
+ sys_memcpy((void *) tracep, (void *) pid, p_len);
tracep += p_len;
ASSERT(startp + hdr_prolog_len == tracep);
@@ -472,7 +472,7 @@ write_trace_header(char *nodename, char *pid, char *hostname)
str = ERTS_ALC_A2AD(i);
ASSERT(str);
- str_len = strlen(str);
+ str_len = sys_strlen(str);
if (str_len >= (1 << 8)) {
disable_trace(1, "Excessively large allocator string", 0);
return 0;
@@ -493,7 +493,7 @@ write_trace_header(char *nodename, char *pid, char *hostname)
PUT_UI16(tracep, aflags);
PUT_UI16(tracep, (Uint16) i);
PUT_UI8( tracep, (byte) str_len);
- memcpy((void *) tracep, (void *) str, str_len);
+ sys_memcpy((void *) tracep, (void *) str, str_len);
tracep += str_len;
if (erts_allctrs_info[i].alloc_util) {
PUT_UI8(tracep, 2);
@@ -519,7 +519,7 @@ write_trace_header(char *nodename, char *pid, char *hostname)
str = ERTS_ALC_N2TD(i);
ASSERT(str);
- str_len = strlen(str);
+ str_len = sys_strlen(str);
if (str_len >= (1 << 8)) {
disable_trace(1, "Excessively large type string", 0);
return 0;
@@ -543,7 +543,7 @@ write_trace_header(char *nodename, char *pid, char *hostname)
PUT_UI16(tracep, nflags);
PUT_UI16(tracep, (Uint16) i);
PUT_UI8(tracep, (byte) str_len);
- memcpy((void *) tracep, (void *) str, str_len);
+ sys_memcpy((void *) tracep, (void *) str, str_len);
tracep += str_len;
PUT_UI16(tracep, no);
ASSERT(startp + entry_sz == tracep);
diff --git a/erts/emulator/beam/erl_nif.c b/erts/emulator/beam/erl_nif.c
index 94ebf88b56..f883b3f1e3 100644
--- a/erts/emulator/beam/erl_nif.c
+++ b/erts/emulator/beam/erl_nif.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2009-2017. All Rights Reserved.
+ * Copyright Ericsson AB 2009-2018. 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.
@@ -57,6 +57,7 @@
#include "erl_bif_unique.h"
#include "erl_utils.h"
#include "erl_io_queue.h"
+#include "erl_proc_sig_queue.h"
#undef ERTS_WANT_NFUNC_SCHED_INTERNALS__
#define ERTS_WANT_NFUNC_SCHED_INTERNALS__
#include "erl_nfunc_sched.h"
@@ -237,9 +238,11 @@ static void cache_env(ErlNifEnv* env);
static void full_flush_env(ErlNifEnv *env);
static void flush_env(ErlNifEnv* env);
-/* Temporary object header, auto-deallocated when NIF returns
- * or when independent environment is cleared.
- */
+/* Temporary object header, auto-deallocated when NIF returns or when
+ * independent environment is cleared.
+ *
+ * The payload can be accessed with &tmp_obj_ptr[1] but keep in mind that its
+ * first element must not require greater alignment than `next`. */
struct enif_tmp_obj_t {
struct enif_tmp_obj_t* next;
void (*dtor)(struct enif_tmp_obj_t*);
@@ -256,6 +259,46 @@ static ERTS_INLINE void free_tmp_objs(ErlNifEnv* env)
}
}
+/* Whether the given environment is bound to a process and will be cleaned up
+ * when the NIF returns. It's safe to use temp_alloc for objects in
+ * env->tmp_obj_list when this is true. */
+static ERTS_INLINE int is_proc_bound(ErlNifEnv *env)
+{
+ return env->mod_nif != NULL;
+}
+
+/* Allocates and attaches an object to the given environment, running its
+ * destructor when the environment is cleared. To avoid temporary variables the
+ * address of the allocated object is returned instead of the enif_tmp_obj_t.
+ *
+ * The destructor *must* call `erts_free(tmp_obj->allocator, tmp_obj)` to free
+ * the object. If the destructor needs to refer to the allocated object its
+ * address will be &tmp_obj[1]. */
+static ERTS_INLINE void *alloc_tmp_obj(ErlNifEnv *env, size_t size,
+ void (*dtor)(struct enif_tmp_obj_t*)) {
+ struct enif_tmp_obj_t *tmp_obj;
+ ErtsAlcType_t allocator;
+
+ allocator = is_proc_bound(env) ? ERTS_ALC_T_TMP : ERTS_ALC_T_NIF;
+
+ tmp_obj = erts_alloc(allocator, sizeof(struct enif_tmp_obj_t) + MAX(1, size));
+
+ tmp_obj->next = env->tmp_obj_list;
+ tmp_obj->allocator = allocator;
+ tmp_obj->dtor = dtor;
+
+ env->tmp_obj_list = tmp_obj;
+
+ return (void*)&tmp_obj[1];
+}
+
+/* Generic destructor for objects allocated through alloc_tmp_obj that don't
+ * care about their payload. */
+static void tmp_alloc_dtor(struct enif_tmp_obj_t *tmp_obj)
+{
+ erts_free(tmp_obj->allocator, tmp_obj);
+}
+
void erts_post_nif(ErlNifEnv* env)
{
erts_unblock_fpe(env->fpe_was_unmasked);
@@ -446,6 +489,7 @@ static void cache_env(ErlNifEnv* env)
env->hp_end = env->heap_frag->mem + env->heap_frag->alloc_size;
}
}
+
void* enif_priv_data(ErlNifEnv* env)
{
return env->mod_nif->priv_data;
@@ -486,7 +530,7 @@ setup_nif_env(struct enif_msg_environment_t* msg_env,
msg_env->env.tmp_obj_list = NULL;
msg_env->env.proc = &msg_env->phony_proc;
msg_env->env.exception_thrown = 0;
- memset(&msg_env->phony_proc, 0, sizeof(Process));
+ sys_memset(&msg_env->phony_proc, 0, sizeof(Process));
HEAP_START(&msg_env->phony_proc) = phony_heap;
HEAP_TOP(&msg_env->phony_proc) = phony_heap;
HEAP_LIMIT(&msg_env->phony_proc) = phony_heap;
@@ -615,7 +659,7 @@ int erts_flush_trace_messages(Process *c_p, ErtsProcLocks c_p_locks)
rp_locks = 0;
if (rp->common.id == c_p->common.id)
rp_locks = c_p_locks;
- erts_queue_messages(rp, rp_locks, first, last, len, c_p->common.id);
+ erts_queue_messages(rp, rp_locks, first, last, len);
if (rp->common.id == c_p->common.id)
rp_locks &= ~c_p_locks;
if (rp_locks)
@@ -657,6 +701,7 @@ int enif_send(ErlNifEnv* env, const ErlNifPid* to_pid,
Process* rp;
Process* c_p;
ErtsMessage *mp;
+ Eterm from;
Eterm receiver = to_pid->pid;
int scheduler;
@@ -735,7 +780,7 @@ int enif_send(ErlNifEnv* env, const ErlNifPid* to_pid,
msg = copy_struct_litopt(msg, sz, &hp, ohp, &litarea);
}
- ERL_MESSAGE_TERM(mp) = msg;
+ from = c_p ? c_p->common.id : am_undefined;
if (!env || !env->tracee) {
@@ -754,7 +799,6 @@ int enif_send(ErlNifEnv* env, const ErlNifPid* to_pid,
ErlTraceMessageQueue *msgq;
Process *t_p = env->tracee;
-
erts_proc_lock(t_p, ERTS_PROC_LOCK_TRACE);
msgq = t_p->trace_msg_q;
@@ -774,6 +818,9 @@ int enif_send(ErlNifEnv* env, const ErlNifPid* to_pid,
rp_locks & ERTS_PROC_LOCK_MSGQ ||
erts_proc_trylock(rp, ERTS_PROC_LOCK_MSGQ) == EBUSY) {
+ ERL_MESSAGE_TERM(mp) = msg;
+ ERL_MESSAGE_FROM(mp) = from;
+
if (!msgq) {
msgq = erts_alloc(ERTS_ALC_T_TRACE_MSG_QUEUE,
sizeof(ErlTraceMessageQueue));
@@ -803,8 +850,7 @@ int enif_send(ErlNifEnv* env, const ErlNifPid* to_pid,
}
}
- erts_queue_message(rp, rp_locks, mp, msg,
- c_p ? c_p->common.id : am_undefined);
+ erts_queue_message(rp, rp_locks, mp, msg, from);
done:
if (c_p == rp)
@@ -1019,11 +1065,6 @@ int enif_is_number(ErlNifEnv* env, ERL_NIF_TERM term)
return is_number(term);
}
-static ERTS_INLINE int is_proc_bound(ErlNifEnv* env)
-{
- return env->mod_nif != NULL;
-}
-
static void aligned_binary_dtor(struct enif_tmp_obj_t* obj)
{
erts_free_aligned_binary_bytes_extra((byte*)obj, obj->allocator);
@@ -1058,22 +1099,14 @@ int enif_inspect_binary(ErlNifEnv* env, Eterm bin_term, ErlNifBinary* bin)
u.tmp->dtor = &aligned_binary_dtor;
env->tmp_obj_list = u.tmp;
}
- bin->bin_term = bin_term;
bin->size = binary_size(bin_term);
bin->ref_bin = NULL;
ADD_READONLY_CHECK(env, bin->data, bin->size);
return 1;
}
-static void tmp_alloc_dtor(struct enif_tmp_obj_t* obj)
-{
- erts_free(obj->allocator, obj);
-}
-
int enif_inspect_iolist_as_binary(ErlNifEnv* env, Eterm term, ErlNifBinary* bin)
{
- struct enif_tmp_obj_t* tobj;
- ErtsAlcType_t allocator;
ErlDrvSizeT sz;
if (is_binary(term)) {
return enif_inspect_binary(env,term,bin);
@@ -1081,7 +1114,6 @@ int enif_inspect_iolist_as_binary(ErlNifEnv* env, Eterm term, ErlNifBinary* bin)
if (is_nil(term)) {
bin->data = (unsigned char*) &bin->data; /* dummy non-NULL */
bin->size = 0;
- bin->bin_term = THE_NON_VALUE;
bin->ref_bin = NULL;
return 1;
}
@@ -1089,16 +1121,8 @@ int enif_inspect_iolist_as_binary(ErlNifEnv* env, Eterm term, ErlNifBinary* bin)
return 0;
}
- allocator = is_proc_bound(env) ? ERTS_ALC_T_TMP : ERTS_ALC_T_NIF;
- tobj = erts_alloc(allocator, sz + sizeof(struct enif_tmp_obj_t));
- tobj->allocator = allocator;
- tobj->next = env->tmp_obj_list;
- tobj->dtor = &tmp_alloc_dtor;
- env->tmp_obj_list = tobj;
-
- bin->data = (unsigned char*) &tobj[1];
+ bin->data = alloc_tmp_obj(env, sz, &tmp_alloc_dtor);
bin->size = sz;
- bin->bin_term = THE_NON_VALUE;
bin->ref_bin = NULL;
erts_iolist_to_buf(term, (char*) bin->data, sz);
ADD_READONLY_CHECK(env, bin->data, bin->size);
@@ -1116,7 +1140,6 @@ int enif_alloc_binary(size_t size, ErlNifBinary* bin)
bin->size = size;
bin->data = (unsigned char*) refbin->orig_bytes;
- bin->bin_term = THE_NON_VALUE;
bin->ref_bin = refbin;
return 1;
}
@@ -1150,12 +1173,10 @@ void enif_release_binary(ErlNifBinary* bin)
{
if (bin->ref_bin != NULL) {
Binary* refbin = bin->ref_bin;
- ASSERT(bin->bin_term == THE_NON_VALUE);
erts_bin_release(refbin);
}
#ifdef DEBUG
bin->data = NULL;
- bin->bin_term = THE_NON_VALUE;
bin->ref_bin = NULL;
#endif
}
@@ -1312,29 +1333,51 @@ int enif_get_string(ErlNifEnv *env, ERL_NIF_TERM list, char* buf, unsigned len,
Eterm enif_make_binary(ErlNifEnv* env, ErlNifBinary* bin)
{
- if (bin->bin_term != THE_NON_VALUE) {
- return bin->bin_term;
- }
- else if (bin->ref_bin != NULL) {
- Binary* bptr = bin->ref_bin;
- Eterm bin_term;
-
- bin_term = erts_build_proc_bin(&MSO(env->proc),
- alloc_heap(env, PROC_BIN_SIZE),
- bptr);
- if (erts_refc_read(&bptr->intern.refc, 1) == 1) {
- /* Total ownership transfer */
- bin->ref_bin = NULL;
- bin->bin_term = bin_term;
- }
- return bin_term;
- }
- else {
- flush_env(env);
- bin->bin_term = new_binary(env->proc, bin->data, bin->size);
- cache_env(env);
- return bin->bin_term;
+ Eterm bin_term;
+
+ if (bin->ref_bin != NULL) {
+ Binary* binary = bin->ref_bin;
+
+ /* If the binary is smaller than the heap binary limit we'll return a
+ * heap binary to reduce the number of small refc binaries in the
+ * system. We can't simply release the refc binary right away however;
+ * the documentation states that the binary should be considered
+ * read-only from this point on, which implies that it should still be
+ * readable.
+ *
+ * We could keep it alive until we return by adding it to the temporary
+ * object list, but that requires an off-heap allocation which is
+ * potentially quite slow, so we create a dummy ProcBin instead and
+ * rely on the next minor GC to get rid of it. */
+ if (bin->size <= ERL_ONHEAP_BIN_LIMIT) {
+ ErlHeapBin* hb;
+
+ hb = (ErlHeapBin*)alloc_heap(env, heap_bin_size(bin->size));
+ hb->thing_word = header_heap_bin(bin->size);
+ hb->size = bin->size;
+
+ sys_memcpy(hb->data, bin->data, bin->size);
+
+ erts_build_proc_bin(&MSO(env->proc),
+ alloc_heap(env, PROC_BIN_SIZE),
+ binary);
+
+ bin_term = make_binary(hb);
+ } else {
+ bin_term = erts_build_proc_bin(&MSO(env->proc),
+ alloc_heap(env, PROC_BIN_SIZE),
+ binary);
+ }
+
+ /* Our (possibly shared) ownership has been transferred to the term. */
+ bin->ref_bin = NULL;
+ } else {
+ flush_env(env);
+ bin_term = new_binary(env->proc, bin->data, bin->size);
+ cache_env(env);
}
+
+ return bin_term;
}
Eterm enif_make_sub_binary(ErlNifEnv* env, ERL_NIF_TERM bin_term,
@@ -1923,6 +1966,12 @@ ErlNifTid enif_thread_self(void) { return erl_drv_thread_self(); }
int enif_equal_tids(ErlNifTid tid1, ErlNifTid tid2) { return erl_drv_equal_tids(tid1,tid2); }
void enif_thread_exit(void *resp) { erl_drv_thread_exit(resp); }
int enif_thread_join(ErlNifTid tid, void **respp) { return erl_drv_thread_join(tid,respp); }
+
+char* enif_mutex_name(ErlNifMutex *mtx) {return erl_drv_mutex_name(mtx); }
+char* enif_cond_name(ErlNifCond *cnd) { return erl_drv_cond_name(cnd); }
+char* enif_rwlock_name(ErlNifRWLock* rwlck) { return erl_drv_rwlock_name(rwlck); }
+char* enif_thread_name(ErlNifTid tid) { return erl_drv_thread_name(tid); }
+
int enif_getenv(const char *key, char *value, size_t *value_size) { return erl_drv_getenv(key, value, value_size); }
ErlNifTime enif_monotonic_time(ErlNifTimeUnit time_unit)
@@ -1945,16 +1994,21 @@ enif_convert_time_unit(ErlNifTime val,
(int) to);
}
-int enif_fprintf(void* filep, const char* format, ...)
+int enif_fprintf(FILE* filep, const char* format, ...)
{
int ret;
va_list arglist;
va_start(arglist, format);
- ret = erts_vfprintf((FILE*)filep, format, arglist);
+ ret = erts_vfprintf(filep, format, arglist);
va_end(arglist);
return ret;
}
+int enif_vfprintf(FILE* filep, const char *format, va_list ap)
+{
+ return erts_vfprintf(filep, format, ap);
+}
+
int enif_snprintf(char *buffer, size_t size, const char* format, ...)
{
int ret;
@@ -1965,6 +2019,12 @@ int enif_snprintf(char *buffer, size_t size, const char* format, ...)
return ret;
}
+int enif_vsnprintf(char* buffer, size_t size, const char *format, va_list ap)
+{
+ return erts_vsnprintf(buffer, size, format, ap);
+}
+
+
/***********************************************************
** Memory managed (GC'ed) "resource" objects **
***********************************************************/
@@ -2166,71 +2226,61 @@ static void rollback_opened_resource_types(void)
}
}
-struct destroy_monitor_ctx
-{
- ErtsResource* resource;
- int exiting_procs;
- int scheduler;
-};
+#ifdef ARCH_64
+# define ERTS_RESOURCE_DYING_FLAG (((Uint) 1) << 63)
+#else
+# define ERTS_RESOURCE_DYING_FLAG (((Uint) 1) << 31)
+#endif
+#define ERTS_RESOURCE_REFC_MASK (~ERTS_RESOURCE_DYING_FLAG)
-static void destroy_one_monitor(ErtsMonitor* mon, void* context)
+static ERTS_INLINE void
+rmon_set_dying(ErtsResourceMonitors *rms)
{
- struct destroy_monitor_ctx* ctx = (struct destroy_monitor_ctx*) context;
- Process* rp;
- ErtsMonitor *rmon = NULL;
- int is_exiting;
+ rms->refc |= ERTS_RESOURCE_DYING_FLAG;
+}
- ASSERT(mon->type == MON_ORIGIN);
- ASSERT(is_internal_pid(mon->u.pid));
- ASSERT(is_internal_ref(mon->ref));
+static ERTS_INLINE int
+rmon_is_dying(ErtsResourceMonitors *rms)
+{
+ return !!(rms->refc & ERTS_RESOURCE_DYING_FLAG);
+}
- if (ctx->scheduler > 0) { /* Normal scheduler */
- rp = erts_proc_lookup(mon->u.pid);
- }
- else {
- rp = erts_proc_lookup_inc_refc(mon->u.pid);
- }
+static ERTS_INLINE void
+rmon_refc_inc(ErtsResourceMonitors *rms)
+{
+ rms->refc++;
+}
- if (!rp) {
- is_exiting = 1;
- }
- if (rp) {
- erts_proc_lock(rp, ERTS_PROC_LOCK_LINK);
- if (ERTS_PROC_IS_EXITING(rp)) {
- is_exiting = 1;
- } else {
- rmon = erts_remove_monitor(&ERTS_P_MONITORS(rp), mon->ref);
- ASSERT(rmon);
- is_exiting = 0;
- }
- erts_proc_unlock(rp, ERTS_PROC_LOCK_LINK);
- if (ctx->scheduler <= 0)
- erts_proc_dec_refc(rp);
- }
- if (is_exiting) {
- ctx->resource->monitors->pending_failed_fire++;
- }
+static ERTS_INLINE Uint
+rmon_refc_dec_read(ErtsResourceMonitors *rms)
+{
+ Uint res;
+ ASSERT((rms->refc & ERTS_RESOURCE_REFC_MASK) != 0);
+ res = --rms->refc;
+ return res & ERTS_RESOURCE_REFC_MASK;
+}
- /* ToDo: Delay destruction after monitor_locks */
- if (rmon) {
- ASSERT(rmon->type == MON_NIF_TARGET);
- ASSERT(rmon->u.resource == ctx->resource);
- erts_destroy_monitor(rmon);
- }
- erts_destroy_monitor(mon);
+static ERTS_INLINE void
+rmon_refc_dec(ErtsResourceMonitors *rms)
+{
+ ASSERT((rms->refc & ERTS_RESOURCE_REFC_MASK) != 0);
+ --rms->refc;
}
-static void destroy_all_monitors(ErtsMonitor* monitors, ErtsResource* resource)
+static ERTS_INLINE Uint
+rmon_refc_read(ErtsResourceMonitors *rms)
{
- struct destroy_monitor_ctx ctx;
+ return rms->refc & ERTS_RESOURCE_REFC_MASK;
+}
- execution_state(NULL, NULL, &ctx.scheduler);
+static void dtor_demonitor(ErtsMonitor* mon, void* context)
+{
+ ASSERT(erts_monitor_is_origin(mon));
+ ASSERT(is_internal_pid(mon->other.item));
- ctx.resource = resource;
- erts_sweep_monitors(monitors, &destroy_one_monitor, &ctx);
+ erts_proc_sig_send_demonitor(mon);
}
-
# define NIF_RESOURCE_DTOR &nif_resource_dtor
static int nif_resource_dtor(Binary* bin)
@@ -2241,34 +2291,36 @@ static int nif_resource_dtor(Binary* bin)
if (resource->monitors) {
ErtsResourceMonitors* rm = resource->monitors;
+ int kill;
+ ErtsMonitor *root;
+ Uint refc;
ASSERT(type->down);
erts_mtx_lock(&rm->lock);
ASSERT(erts_refc_read(&bin->intern.refc, 0) == 0);
- if (rm->root) {
- ASSERT(!rm->is_dying);
- destroy_all_monitors(rm->root, resource);
+ kill = !rmon_is_dying(rm);
+ if (kill) {
+ rmon_set_dying(rm);
+ root = rm->root;
rm->root = NULL;
}
- if (rm->pending_failed_fire) {
- /*
- * Resource death struggle prolonged to serve exiting process(es).
- * Destructor will be called again when last exiting process
- * tries to fire its MON_NIF_TARGET monitor (and fails).
- *
- * This resource is doomed. It has no "real" references and
- * should get not get called upon to do anything except the
- * final destructor call.
- *
- * We keep refc at 0 and use a separate counter for exiting
- * processes to avoid resource getting revived by "dec_term".
- */
- ASSERT(!rm->is_dying);
- rm->is_dying = 1;
- erts_mtx_unlock(&rm->lock);
- return 0;
- }
+ refc = rmon_refc_read(rm);
erts_mtx_unlock(&rm->lock);
+
+ if (kill)
+ erts_monitor_tree_foreach_delete(&root,
+ dtor_demonitor,
+ NULL);
+
+ /*
+ * If resource->monitors->refc != 0 there are
+ * outstanding references to the resource from
+ * monitors that has not been removed yet.
+ * nif_resource_dtor() will be called again this
+ * reference count reach zero.
+ */
+ if (refc != 0)
+ return 0; /* we'll be back... */
erts_mtx_destroy(&rm->lock);
}
@@ -2298,54 +2350,82 @@ void erts_resource_stop(ErtsResource* resource, ErlNifEvent e,
post_nif_noproc(&msg_env);
}
-void erts_fire_nif_monitor(ErtsResource* resource, Eterm pid, Eterm ref)
+void erts_nif_demonitored(ErtsResource* resource)
{
- ErtsMonitor* rmon;
+ ErtsResourceMonitors* rmp = resource->monitors;
ErtsBinary* bin = ERTS_MAGIC_BIN_FROM_UNALIGNED_DATA(resource);
+ int free_me;
+
+ ASSERT(rmp);
+ ASSERT(resource->type->down);
+
+ erts_mtx_lock(&rmp->lock);
+ free_me = ((rmon_refc_dec_read(rmp) == 0) & !!rmon_is_dying(rmp));
+ erts_mtx_unlock(&rmp->lock);
+
+ if (free_me)
+ erts_bin_free(&bin->binary);
+}
+
+void erts_fire_nif_monitor(ErtsMonitor *tmon)
+{
+ ErtsResource* resource;
+ ErtsMonitorData *mdp;
+ ErtsMonitor *omon;
+ ErtsBinary* bin;
struct enif_msg_environment_t msg_env;
ErlNifPid nif_pid;
ErlNifMonitor nif_monitor;
- ErtsResourceMonitors* rmp = resource->monitors;
+ ErtsResourceMonitors* rmp;
+ Uint mrefc, brefc;
+ int active, is_dying;
+
+ ASSERT(tmon->type == ERTS_MON_TYPE_RESOURCE);
+ ASSERT(erts_monitor_is_target(tmon));
+
+ resource = tmon->other.ptr;
+ bin = ERTS_MAGIC_BIN_FROM_UNALIGNED_DATA(resource);
+ rmp = resource->monitors;
+
+ mdp = erts_monitor_to_data(tmon);
+ omon = &mdp->origin;
ASSERT(rmp);
ASSERT(resource->type->down);
erts_mtx_lock(&rmp->lock);
- rmon = erts_remove_monitor(&rmp->root, ref);
- if (!rmon) {
- int free_me = (--rmp->pending_failed_fire == 0) && rmp->is_dying;
- ASSERT(rmp->pending_failed_fire >= 0);
- erts_mtx_unlock(&rmp->lock);
-
- if (free_me) {
- ASSERT(erts_refc_read(&bin->binary.intern.refc, 0) == 0);
- erts_bin_free(&bin->binary);
- }
- return;
+
+ mrefc = rmon_refc_dec_read(rmp);
+ is_dying = rmon_is_dying(rmp);
+ active = !is_dying && erts_monitor_is_in_table(omon);
+
+ if (active) {
+ erts_monitor_tree_delete(&rmp->root, omon);
+ brefc = (Uint) erts_refc_inc_unless(&bin->binary.intern.refc, 0, 0);
}
- ASSERT(!rmp->is_dying);
- if (erts_refc_inc_unless(&bin->binary.intern.refc, 0, 0) == 0) {
- /*
- * Racing resource destruction.
- * To avoid a more complex refc-dance with destructing thread
- * we avoid calling 'down' and just silently remove the monitor.
- * This can happen even for non smp as destructor calls may be scheduled.
- */
- erts_mtx_unlock(&rmp->lock);
+
+ erts_mtx_unlock(&rmp->lock);
+
+ if (!active) {
+ ASSERT(!is_dying || erts_refc_read(&bin->binary.intern.refc, 0) == 0);
+ if (is_dying && mrefc == 0)
+ erts_bin_free(&bin->binary);
+ erts_monitor_release(tmon);
}
else {
- erts_mtx_unlock(&rmp->lock);
-
- ASSERT(rmon->u.pid == pid);
- erts_ref_to_driver_monitor(ref, &nif_monitor);
- nif_pid.pid = pid;
- pre_nif_noproc(&msg_env, resource->type->owner, NULL);
- resource->type->down(&msg_env.env, resource->data, &nif_pid, &nif_monitor);
- post_nif_noproc(&msg_env);
+ if (brefc > 0) {
+ ASSERT(is_internal_pid(omon->other.item));
+ erts_ref_to_driver_monitor(mdp->ref, &nif_monitor);
+ nif_pid.pid = omon->other.item;
+ pre_nif_noproc(&msg_env, resource->type->owner, NULL);
+ resource->type->down(&msg_env.env, resource->data, &nif_pid, &nif_monitor);
+ post_nif_noproc(&msg_env);
+
+ erts_bin_release(&bin->binary);
+ }
- erts_bin_release(&bin->binary);
+ erts_monitor_release_both(mdp);
}
- erts_destroy_monitor(rmon);
}
void* enif_alloc_resource(ErlNifResourceType* type, size_t data_sz)
@@ -2382,8 +2462,7 @@ void* enif_alloc_resource(ErlNifResourceType* type, size_t data_sz)
erts_mtx_init(&resource->monitors->lock, "resource_monitors", NIL,
ERTS_LOCK_FLAGS_CATEGORY_GENERIC);
resource->monitors->root = NULL;
- resource->monitors->pending_failed_fire = 0;
- resource->monitors->is_dying = 0;
+ resource->monitors->refc = 0;
resource->monitors->user_data_sz = data_sz;
}
else {
@@ -2398,7 +2477,7 @@ void enif_release_resource(void* obj)
ErtsBinary* bin = ERTS_MAGIC_BIN_FROM_UNALIGNED_DATA(resource);
ASSERT(ERTS_MAGIC_BIN_DESTRUCTOR(bin) == NIF_RESOURCE_DTOR);
- ASSERT(!(resource->monitors && resource->monitors->is_dying));
+ ASSERT(erts_refc_read(&bin->binary.intern.refc, 0) != 0);
#ifdef DEBUG
erts_refc_dec(&resource->nif_refc, 0);
#endif
@@ -2411,7 +2490,7 @@ void enif_keep_resource(void* obj)
ErtsBinary* bin = ERTS_MAGIC_BIN_FROM_UNALIGNED_DATA(resource);
ASSERT(ERTS_MAGIC_BIN_DESTRUCTOR(bin) == NIF_RESOURCE_DTOR);
- ASSERT(!(resource->monitors && resource->monitors->is_dying));
+ ASSERT(erts_refc_read(&bin->binary.intern.refc, 0) != 0);
#ifdef DEBUG
erts_refc_inc(&resource->nif_refc, 1);
#endif
@@ -2421,7 +2500,7 @@ void enif_keep_resource(void* obj)
Eterm erts_bld_resource_ref(Eterm** hpp, ErlOffHeap* oh, ErtsResource* resource)
{
ErtsBinary* bin = ERTS_MAGIC_BIN_FROM_UNALIGNED_DATA(resource);
- ASSERT(!(resource->monitors && resource->monitors->is_dying));
+ ASSERT(erts_refc_read(&bin->binary.intern.refc, 0) != 0);
return erts_mk_magic_ref(hpp, oh, &bin->binary);
}
@@ -2430,7 +2509,7 @@ ERL_NIF_TERM enif_make_resource(ErlNifEnv* env, void* obj)
ErtsResource* resource = DATA_TO_RESOURCE(obj);
ErtsBinary* bin = ERTS_MAGIC_BIN_FROM_UNALIGNED_DATA(resource);
Eterm* hp = alloc_heap(env, ERTS_MAGIC_REF_THING_SIZE);
- ASSERT(!(resource->monitors && resource->monitors->is_dying));
+ ASSERT(erts_refc_read(&bin->binary.intern.refc, 0) != 0);
return erts_mk_magic_ref(&hp, &MSO(env->proc), &bin->binary);
}
@@ -2855,6 +2934,44 @@ ERL_NIF_TERM enif_make_new_map(ErlNifEnv* env)
return make_flatmap(mp);
}
+int enif_make_map_from_arrays(ErlNifEnv *env,
+ ERL_NIF_TERM keys[],
+ ERL_NIF_TERM values[],
+ size_t cnt,
+ ERL_NIF_TERM *map_out)
+{
+ ErtsHeapFactory factory;
+ int succeeded;
+
+#ifdef ERTS_NIF_ASSERT_IN_ENV
+ size_t index = 0;
+
+ while (index < cnt) {
+ ASSERT_IN_ENV(env, keys[index], index, "key");
+ ASSERT_IN_ENV(env, values[index], index, "value");
+ index++;
+ }
+#endif
+
+ flush_env(env);
+
+ erts_factory_proc_prealloc_init(&factory, env->proc,
+ cnt * 2 + MAP_HEADER_FLATMAP_SZ + 1);
+
+ (*map_out) = erts_map_from_ks_and_vs(&factory, keys, values, cnt);
+ succeeded = (*map_out) != THE_NON_VALUE;
+
+ if (!succeeded) {
+ erts_factory_undo(&factory);
+ }
+
+ erts_factory_close(&factory);
+
+ cache_env(env);
+
+ return succeeded;
+}
+
int enif_make_map_put(ErlNifEnv* env,
Eterm map_in,
Eterm key,
@@ -3110,123 +3227,88 @@ int enif_map_iterator_get_pair(ErlNifEnv *env,
int enif_monitor_process(ErlNifEnv* env, void* obj, const ErlNifPid* target_pid,
ErlNifMonitor* monitor)
{
- int scheduler;
ErtsResource* rsrc = DATA_TO_RESOURCE(obj);
- Process *rp;
Eterm tmp[ERTS_REF_THING_SIZE];
Eterm ref;
- int retval;
+ ErtsResourceMonitors *rm;
+ ErtsMonitorData *mdp;
ASSERT(ERTS_MAGIC_BIN_FROM_UNALIGNED_DATA(rsrc)->magic_binary.destructor
== NIF_RESOURCE_DTOR);
- ASSERT(!(rsrc->monitors && rsrc->monitors->is_dying));
+ ASSERT(erts_refc_read(&ERTS_MAGIC_BIN_FROM_UNALIGNED_DATA(rsrc)->binary.intern.refc, 0) != 0);
ASSERT(!rsrc->monitors == !rsrc->type->down);
-
- if (!rsrc->monitors) {
+ rm = rsrc->monitors;
+ if (!rm) {
ASSERT(!rsrc->type->down);
return -1;
}
ASSERT(rsrc->type->down);
- execution_state(env, NULL, &scheduler);
+ ref = erts_make_ref_in_buffer(tmp);
- if (scheduler > 0) /* Normal scheduler */
- rp = erts_proc_lookup_raw(target_pid->pid);
- else
- rp = erts_proc_lookup_raw_inc_refc(target_pid->pid);
+ mdp = erts_monitor_create(ERTS_MON_TYPE_RESOURCE, ref,
+ (Eterm) rsrc, target_pid->pid, NIL);
+ erts_mtx_lock(&rm->lock);
+ ASSERT(!rmon_is_dying(rm));
+ erts_monitor_tree_insert(&rm->root, &mdp->origin);
+ rmon_refc_inc(rm);
+ erts_mtx_unlock(&rm->lock);
- if (!rp)
- return 1;
+ if (!erts_proc_sig_send_monitor(&mdp->target, target_pid->pid)) {
+ /* Failed to send monitor signal; cleanup... */
+#ifdef DEBUG
+ ErtsBinary* bin = ERTS_MAGIC_BIN_FROM_UNALIGNED_DATA(rsrc);
+#endif
- ref = erts_make_ref_in_buffer(tmp);
+ erts_mtx_lock(&rm->lock);
+ ASSERT(!rmon_is_dying(rm));
+ erts_monitor_tree_delete(&rm->root, &mdp->origin);
+ rmon_refc_dec(rm);
+ ASSERT(erts_refc_read(&bin->binary.intern.refc, 1) != 0);
+ erts_mtx_unlock(&rm->lock);
+ erts_monitor_release_both(mdp);
- erts_mtx_lock(&rsrc->monitors->lock);
- erts_proc_lock(rp, ERTS_PROC_LOCK_LINK);
- if (ERTS_PSFLG_FREE & erts_atomic32_read_nob(&rp->state)) {
- retval = 1;
- }
- else {
- erts_add_monitor(&rsrc->monitors->root, MON_ORIGIN, ref, rp->common.id, NIL);
- erts_add_monitor(&ERTS_P_MONITORS(rp), MON_NIF_TARGET, ref, (UWord)rsrc, NIL);
- retval = 0;
+ return 1;
}
- erts_proc_unlock(rp, ERTS_PROC_LOCK_LINK);
- erts_mtx_unlock(&rsrc->monitors->lock);
- if (scheduler <= 0)
- erts_proc_dec_refc(rp);
if (monitor)
erts_ref_to_driver_monitor(ref,monitor);
- return retval;
+ return 0;
}
int enif_demonitor_process(ErlNifEnv* env, void* obj, const ErlNifMonitor* monitor)
{
- int scheduler;
ErtsResource* rsrc = DATA_TO_RESOURCE(obj);
#ifdef DEBUG
ErtsBinary* bin = ERTS_MAGIC_BIN_FROM_UNALIGNED_DATA(rsrc);
#endif
- Process *rp;
+ ErtsResourceMonitors *rm;
ErtsMonitor *mon;
- ErtsMonitor *rmon = NULL;
Eterm ref_heap[ERTS_REF_THING_SIZE];
Eterm ref;
- int is_exiting;
ASSERT(bin->magic_binary.destructor == NIF_RESOURCE_DTOR);
- ASSERT(!(rsrc->monitors && rsrc->monitors->is_dying));
-
- execution_state(env, NULL, &scheduler);
+ ASSERT(erts_refc_read(&bin->binary.intern.refc, 0) != 0);
ref = erts_driver_monitor_to_ref(ref_heap, monitor);
- erts_mtx_lock(&rsrc->monitors->lock);
- mon = erts_remove_monitor(&rsrc->monitors->root, ref);
+ rm = rsrc->monitors;
+ erts_mtx_lock(&rm->lock);
+ ASSERT(!rmon_is_dying(rm));
+ mon = erts_monitor_tree_lookup(rm->root, ref);
+ if (mon)
+ erts_monitor_tree_delete(&rm->root, mon);
+ erts_mtx_unlock(&rm->lock);
- if (mon == NULL) {
- erts_mtx_unlock(&rsrc->monitors->lock);
+ if (!mon)
return 1;
- }
- ASSERT(mon->type == MON_ORIGIN);
- ASSERT(is_internal_pid(mon->u.pid));
+ ASSERT(erts_monitor_is_origin(mon));
+ ASSERT(is_internal_pid(mon->other.item));
- if (scheduler > 0) /* Normal scheduler */
- rp = erts_proc_lookup(mon->u.pid);
- else
- rp = erts_proc_lookup_inc_refc(mon->u.pid);
-
- if (!rp) {
- is_exiting = 1;
- }
- else {
- erts_proc_lock(rp, ERTS_PROC_LOCK_LINK);
- if (ERTS_PROC_IS_EXITING(rp)) {
- is_exiting = 1;
- } else {
- rmon = erts_remove_monitor(&ERTS_P_MONITORS(rp), ref);
- ASSERT(rmon);
- is_exiting = 0;
- }
- erts_proc_unlock(rp, ERTS_PROC_LOCK_LINK);
-
- if (scheduler <= 0)
- erts_proc_dec_refc(rp);
- }
- if (is_exiting) {
- rsrc->monitors->pending_failed_fire++;
- }
- erts_mtx_unlock(&rsrc->monitors->lock);
-
- if (rmon) {
- ASSERT(rmon->type == MON_NIF_TARGET);
- ASSERT(rmon->u.resource == rsrc);
- erts_destroy_monitor(rmon);
- }
- erts_destroy_monitor(mon);
+ erts_proc_sig_send_demonitor(mon);
return 0;
}
@@ -3284,8 +3366,8 @@ typedef struct {
Eterm sublist_start;
Eterm sublist_end;
- UWord offheap_size;
- UWord onheap_size;
+ UWord referenced_size;
+ UWord copied_size;
UWord iovec_len;
} iovec_slice_t;
@@ -3295,16 +3377,16 @@ static int examine_iovec_term(Eterm list, UWord max_length, iovec_slice_t *resul
result->sublist_start = list;
result->sublist_length = 0;
- result->offheap_size = 0;
- result->onheap_size = 0;
+ result->referenced_size = 0;
+ result->copied_size = 0;
result->iovec_len = 0;
lookahead = result->sublist_start;
while (is_list(lookahead)) {
- Eterm *binary_header, binary;
+ UWord byte_size;
+ Eterm binary;
Eterm *cell;
- UWord size;
cell = list_val(lookahead);
binary = CAR(cell);
@@ -3313,35 +3395,36 @@ static int examine_iovec_term(Eterm list, UWord max_length, iovec_slice_t *resul
return 0;
}
- size = binary_size(binary);
- binary_header = binary_val(binary);
+ byte_size = binary_size(binary);
+
+ if (byte_size > 0) {
+ int bit_offset, bit_size;
+ Eterm parent_binary;
+ UWord byte_offset;
+
+ int requires_copying;
- if (size > 0) {
- /* If we're a sub-binary we'll need to check our underlying binary
- * to determine whether we're on-heap or not. */
- if (thing_subtag(*binary_header) == SUB_BINARY_SUBTAG) {
- ErlSubBin *sb = (ErlSubBin*)binary_header;
+ ERTS_GET_REAL_BIN(binary, parent_binary, byte_offset,
+ bit_offset, bit_size);
- /* Reject bitstrings */
- if((sb->bitoffs + sb->bitsize) > 0) {
- return 0;
- }
+ (void)byte_offset;
- ASSERT(size <= binary_size(sb->orig));
- binary_header = binary_val(sb->orig);
+ if (bit_size != 0) {
+ return 0;
}
- if (thing_subtag(*binary_header) == HEAP_BINARY_SUBTAG) {
- ASSERT(size <= ERL_ONHEAP_BIN_LIMIT);
+ /* If we're unaligned or an on-heap binary we'll need to copy
+ * ourselves over to a temporary buffer. */
+ requires_copying = (bit_offset != 0) ||
+ thing_subtag(*binary_val(parent_binary)) == HEAP_BINARY_SUBTAG;
- result->iovec_len += 1;
- result->onheap_size += size;
+ if (requires_copying) {
+ result->copied_size += byte_size;
} else {
- ASSERT(thing_subtag(*binary_header) == REFC_BINARY_SUBTAG);
-
- result->iovec_len += 1 + size / MAX_SYSIOVEC_IOVLEN;
- result->offheap_size += size;
+ result->referenced_size += byte_size;
}
+
+ result->iovec_len += 1 + byte_size / MAX_SYSIOVEC_IOVLEN;
}
result->sublist_length += 1;
@@ -3361,7 +3444,9 @@ static int examine_iovec_term(Eterm list, UWord max_length, iovec_slice_t *resul
return 1;
}
-static void inspect_raw_binary_data(Eterm binary, ErlNifBinary *result) {
+static void marshal_iovec_binary(Eterm binary, ErlNifBinary *copy_buffer,
+ UWord *copy_offset, ErlNifBinary *result) {
+
Eterm *parent_header;
Eterm parent_binary;
@@ -3372,10 +3457,11 @@ static void inspect_raw_binary_data(Eterm binary, ErlNifBinary *result) {
ERTS_GET_REAL_BIN(binary, parent_binary, byte_offset, bit_offset, bit_size);
+ ASSERT(bit_size == 0);
+
parent_header = binary_val(parent_binary);
result->size = binary_size(binary);
- result->bin_term = binary;
if (thing_subtag(*parent_header) == REFC_BINARY_SUBTAG) {
ProcBin *pb = (ProcBin*)parent_header;
@@ -3398,24 +3484,48 @@ static void inspect_raw_binary_data(Eterm binary, ErlNifBinary *result) {
result->data = &((unsigned char*)&hb->data)[byte_offset];
result->ref_bin = NULL;
}
+
+ /* If this isn't an *aligned* refc binary, copy its contents to the buffer
+ * and reference that instead. */
+
+ if (result->ref_bin == NULL || bit_offset != 0) {
+ ASSERT(copy_buffer->ref_bin != NULL && copy_buffer->data != NULL);
+ ASSERT(result->size <= (copy_buffer->size - *copy_offset));
+
+ if (bit_offset == 0) {
+ sys_memcpy(&copy_buffer->data[*copy_offset],
+ result->data, result->size);
+ } else {
+ erts_copy_bits(result->data, bit_offset, 1,
+ (byte*)&copy_buffer->data[*copy_offset], 0, 1,
+ result->size * 8);
+ }
+
+ result->data = &copy_buffer->data[*copy_offset];
+ result->ref_bin = copy_buffer->ref_bin;
+
+ *copy_offset += result->size;
+ }
}
static int fill_iovec_with_slice(ErlNifEnv *env,
iovec_slice_t *slice,
ErlNifIOVec *iovec) {
- UWord onheap_offset, iovec_idx;
- ErlNifBinary onheap_data;
+ ErlNifBinary copy_buffer = {0};
+ UWord copy_offset, iovec_idx;
Eterm sublist_iterator;
- /* Set up a common refc binary for all on-heap binaries. */
- if (slice->onheap_size > 0) {
- if (!enif_alloc_binary(slice->onheap_size, &onheap_data)) {
+ /* Set up a common refc binary for all on-heap and unaligned binaries. */
+ if (slice->copied_size > 0) {
+ if (!enif_alloc_binary(slice->copied_size, &copy_buffer)) {
return 0;
}
+
+ ASSERT(copy_buffer.ref_bin != NULL);
}
sublist_iterator = slice->sublist_start;
- onheap_offset = 0;
+ copy_offset = 0;
iovec_idx = 0;
while (sublist_iterator != slice->sublist_end) {
@@ -3423,20 +3533,7 @@ static int fill_iovec_with_slice(ErlNifEnv *env,
Eterm *cell;
cell = list_val(sublist_iterator);
- inspect_raw_binary_data(CAR(cell), &raw_data);
-
- /* If this isn't a refc binary, copy its contents to the onheap buffer
- * and reference that instead. */
- if (raw_data.size > 0 && raw_data.ref_bin == NULL) {
- ASSERT(onheap_offset < onheap_data.size);
- ASSERT(slice->onheap_size > 0);
-
- sys_memcpy(&onheap_data.data[onheap_offset],
- raw_data.data, raw_data.size);
-
- raw_data.data = &onheap_data.data[onheap_offset];
- raw_data.ref_bin = onheap_data.ref_bin;
- }
+ marshal_iovec_binary(CAR(cell), &copy_buffer, &copy_offset, &raw_data);
while (raw_data.size > 0) {
UWord chunk_len = MIN(raw_data.size, MAX_SYSIOVEC_IOVLEN);
@@ -3467,16 +3564,18 @@ static int fill_iovec_with_slice(ErlNifEnv *env,
erts_refc_inc(&refc_binary->intern.refc, 1);
}
- if (slice->onheap_size > 0) {
+ if (slice->copied_size > 0) {
/* Transfer ownership to the iovec; we've taken references to it in
* the above loop. */
- enif_release_binary(&onheap_data);
+ enif_release_binary(&copy_buffer);
}
} else {
- if (slice->onheap_size > 0) {
- /* Attach the binary to our environment and let the GC take care of
- * it after returning. */
- enif_make_binary(env, &onheap_data);
+ if (slice->copied_size > 0) {
+ /* Attach the binary to our environment and let the next minor GC
+ * get rid of it. This is slightly faster than using the tmp object
+ * list since it avoids off-heap allocations. */
+ erts_build_proc_bin(&MSO(env->proc),
+ alloc_heap(env, PROC_BIN_SIZE), copy_buffer.ref_bin);
}
}
@@ -3502,19 +3601,14 @@ static int create_iovec_from_slice(ErlNifEnv *env,
alloc_size = binv_offset;
alloc_size += slice->iovec_len * sizeof(Binary*);
- /* If we have an environment we'll attach the allocated data to it. The
- * GC will take care of releasing it later on. */
+ /* When the user passes an environment, we attach the iovec to it so
+ * the user won't have to bother managing it (similar to
+ * enif_inspect_binary). It'll disappear once the environment is
+ * cleaned up. */
if (env != NULL) {
- ErlNifBinary gc_bin;
-
- if (!enif_alloc_binary(alloc_size, &gc_bin)) {
- return 0;
- }
-
- alloc_base = (char*)gc_bin.data;
- enif_make_binary(env, &gc_bin);
+ alloc_base = alloc_tmp_obj(env, alloc_size, &tmp_alloc_dtor);
} else {
- alloc_base = enif_alloc(alloc_size);
+ alloc_base = erts_alloc(ERTS_ALC_T_NIF, alloc_size);
}
iovec = (ErlNifIOVec*)alloc_base;
@@ -3523,12 +3617,12 @@ static int create_iovec_from_slice(ErlNifEnv *env,
iovec->flags = 0;
}
- iovec->size = slice->offheap_size + slice->onheap_size;
+ iovec->size = slice->referenced_size + slice->copied_size;
iovec->iovcnt = slice->iovec_len;
if(!fill_iovec_with_slice(env, slice, iovec)) {
if (env == NULL && !(iovec->flags & ERL_NIF_IOVEC_FLAGS_PREALLOC)) {
- enif_free(iovec);
+ erts_free(ERTS_ALC_T_NIF, iovec);
}
return 0;
@@ -3804,6 +3898,11 @@ static struct erl_module_nif* create_lib(const ErlNifEntry* src)
} else {
dst->sizeof_ErlNifResourceTypeInit = 0;
}
+ if (AT_LEAST_VERSION(src, 2, 14)) {
+ dst->min_erts = src->min_erts;
+ } else {
+ dst->min_erts = "erts-?";
+ }
return lib;
};
@@ -3916,14 +4015,20 @@ BIF_RETTYPE load_nif_2(BIF_ALIST_2)
(entry = erts_sys_ddll_call_nif_init(init_func)) == NULL)) {
ret = load_nif_error(BIF_P, bad_lib, "Library init-call unsuccessful");
}
+ else if (entry->major > ERL_NIF_MAJOR_VERSION
+ || (entry->major == ERL_NIF_MAJOR_VERSION
+ && entry->minor > ERL_NIF_MINOR_VERSION)) {
+ char* fmt = "That '%T' NIF library needs %s or newer. Either try to"
+ " recompile the NIF lib or use a newer erts runtime.";
+ ret = load_nif_error(BIF_P, bad_lib, fmt, mod_atom, entry->min_erts);
+ }
else if (entry->major < ERL_NIF_MIN_REQUIRED_MAJOR_VERSION_ON_LOAD
- || (ERL_NIF_MAJOR_VERSION < entry->major
- || (ERL_NIF_MAJOR_VERSION == entry->major
- && ERL_NIF_MINOR_VERSION < entry->minor))
|| (entry->major==2 && entry->minor == 5)) { /* experimental maps */
- ret = load_nif_error(BIF_P, bad_lib, "Library version (%d.%d) not compatible (with %d.%d).",
- entry->major, entry->minor, ERL_NIF_MAJOR_VERSION, ERL_NIF_MINOR_VERSION);
+ char* fmt = "That old NIF library (%d.%d) is not compatible with this "
+ "erts runtime (%d.%d). Try recompile the NIF lib.";
+ ret = load_nif_error(BIF_P, bad_lib, fmt, entry->major, entry->minor,
+ ERL_NIF_MAJOR_VERSION, ERL_NIF_MINOR_VERSION);
}
else if (AT_LEAST_VERSION(entry, 2, 1)
&& sys_strcmp(entry->vm_variant, ERL_NIF_VM_VARIANT) != 0) {
@@ -4218,34 +4323,31 @@ static unsigned calc_checksum(unsigned char* ptr, unsigned size);
struct readonly_check_t
{
- struct enif_tmp_obj_t hdr;
unsigned char* ptr;
unsigned size;
unsigned checksum;
};
static void add_readonly_check(ErlNifEnv* env, unsigned char* ptr, unsigned sz)
{
- ErtsAlcType_t allocator = is_proc_bound(env) ? ERTS_ALC_T_TMP : ERTS_ALC_T_NIF;
- struct readonly_check_t* obj = erts_alloc(allocator,
- sizeof(struct readonly_check_t));
- obj->hdr.allocator = allocator;
- obj->hdr.next = env->tmp_obj_list;
- env->tmp_obj_list = &obj->hdr;
- obj->hdr.dtor = &readonly_check_dtor;
+ struct readonly_check_t* obj;
+
+ obj = alloc_tmp_obj(env, sizeof(struct readonly_check_t),
+ &readonly_check_dtor);
+
obj->ptr = ptr;
obj->size = sz;
- obj->checksum = calc_checksum(ptr, sz);
+ obj->checksum = calc_checksum(ptr, sz);
}
-static void readonly_check_dtor(struct enif_tmp_obj_t* o)
+static void readonly_check_dtor(struct enif_tmp_obj_t* tmp_obj)
{
- struct readonly_check_t* obj = (struct readonly_check_t*) o;
- unsigned chksum = calc_checksum(obj->ptr, obj->size);
- if (chksum != obj->checksum) {
+ struct readonly_check_t* ro_check = (struct readonly_check_t*)&tmp_obj[1];
+ unsigned chksum = calc_checksum(ro_check->ptr, ro_check->size);
+ if (chksum != ro_check->checksum) {
fprintf(stderr, "\r\nReadonly data written by NIF, checksums differ"
- " %x != %x\r\nABORTING\r\n", chksum, obj->checksum);
+ " %x != %x\r\nABORTING\r\n", chksum, ro_check->checksum);
abort();
}
- erts_free(obj->hdr.allocator, obj);
+ erts_free(tmp_obj->allocator, tmp_obj);
}
static unsigned calc_checksum(unsigned char* ptr, unsigned size)
{
@@ -4320,7 +4422,7 @@ static void get_string_maybe(ErlNifEnv *env, const ERL_NIF_TERM term,
str_bin.size > bufsiz) {
*ptr = NULL;
} else {
- memcpy(buf, (char *) str_bin.data, str_bin.size);
+ sys_memcpy(buf, (char *) str_bin.data, str_bin.size);
buf[str_bin.size] = '\0';
*ptr = buf;
}
@@ -4337,7 +4439,7 @@ ERL_NIF_TERM erl_nif_user_trace_s1(ErlNifEnv* env, int argc,
message_bin.size > MESSAGE_BUFSIZ) {
return am_badarg;
}
- memcpy(messagebuf, (char *) message_bin.data, message_bin.size);
+ sys_memcpy(messagebuf, (char *) message_bin.data, message_bin.size);
messagebuf[message_bin.size] = '\0';
DTRACE1(user_trace_s1, messagebuf);
return am_true;
diff --git a/erts/emulator/beam/erl_nif.h b/erts/emulator/beam/erl_nif.h
index 053f7673c4..1906da732b 100644
--- a/erts/emulator/beam/erl_nif.h
+++ b/erts/emulator/beam/erl_nif.h
@@ -52,10 +52,12 @@
** 2.11: 19.0 enif_snprintf
** 2.12: 20.0 add enif_select, enif_open_resource_type_x
** 2.13: 20.1 add enif_ioq
-** 2.14: 21.0 add enif_ioq_peek_head
+** 2.14: 21.0 add enif_ioq_peek_head, enif_(mutex|cond|rwlock|thread)_name
+** enif_vfprintf, enif_vsnprintf, enif_make_map_from_arrays
*/
#define ERL_NIF_MAJOR_VERSION 2
#define ERL_NIF_MINOR_VERSION 14
+#define ERL_NIF_MIN_ERTS_VERSION "erts-10.0 (OTP-21)"
/*
* The emulator will refuse to load a nif-lib with a major version
@@ -70,6 +72,8 @@
#define ERL_NIF_MIN_REQUIRED_MAJOR_VERSION_ON_LOAD 2
#include <stdlib.h>
+#include <stdio.h>
+#include <stdarg.h>
#ifdef __cplusplus
extern "C" {
@@ -128,6 +132,9 @@ typedef struct enif_entry_t
/* Added in 2.12 */
size_t sizeof_ErlNifResourceTypeInit;
+
+ /* Added in 2.14 */
+ const char* min_erts;
}ErlNifEntry;
@@ -137,8 +144,9 @@ typedef struct
unsigned char* data;
/* Internals (avert your eyes) */
- ERL_NIF_TERM bin_term;
void* ref_bin;
+ /* for future additions to be ABI compatible (same struct size) */
+ void* __spare__[2];
}ErlNifBinary;
#if (defined(__WIN32__) || defined(_WIN32) || defined(_WIN32_))
@@ -349,7 +357,8 @@ ERL_NIF_INIT_DECL(NAME) \
LOAD, RELOAD, UPGRADE, UNLOAD, \
ERL_NIF_VM_VARIANT, \
1, \
- sizeof(ErlNifResourceTypeInit) \
+ sizeof(ErlNifResourceTypeInit), \
+ ERL_NIF_MIN_ERTS_VERSION \
}; \
ERL_NIF_INIT_BODY; \
return &entry; \
diff --git a/erts/emulator/beam/erl_nif_api_funcs.h b/erts/emulator/beam/erl_nif_api_funcs.h
index 3750fd9b68..61f8fcf6ed 100644
--- a/erts/emulator/beam/erl_nif_api_funcs.h
+++ b/erts/emulator/beam/erl_nif_api_funcs.h
@@ -92,7 +92,7 @@ ERL_NIF_API_FUNC_DECL(int,enif_thread_join,(ErlNifTid, void **respp));
ERL_NIF_API_FUNC_DECL(void*,enif_realloc,(void* ptr, size_t size));
ERL_NIF_API_FUNC_DECL(void,enif_system_info,(ErlNifSysInfo *sip, size_t si_size));
-ERL_NIF_API_FUNC_DECL(int,enif_fprintf,(void/* FILE* */ *filep, const char *format, ...));
+ERL_NIF_API_FUNC_DECL(int,enif_fprintf,(FILE* filep, const char *format, ...));
ERL_NIF_API_FUNC_DECL(int,enif_inspect_iolist_as_binary,(ErlNifEnv*, ERL_NIF_TERM term, ErlNifBinary* bin));
ERL_NIF_API_FUNC_DECL(ERL_NIF_TERM,enif_make_sub_binary,(ErlNifEnv*, ERL_NIF_TERM bin_term, size_t pos, size_t size));
ERL_NIF_API_FUNC_DECL(int,enif_get_string,(ErlNifEnv*, ERL_NIF_TERM list, char* buf, unsigned len, ErlNifCharEncoding));
@@ -200,6 +200,16 @@ ERL_NIF_API_FUNC_DECL(void,enif_free_iovec,(ErlNifIOVec *iov));
ERL_NIF_API_FUNC_DECL(int,enif_ioq_peek_head,(ErlNifEnv *env, ErlNifIOQueue *q, size_t *size, ERL_NIF_TERM *head));
+ERL_NIF_API_FUNC_DECL(char*,enif_mutex_name,(ErlNifMutex*));
+ERL_NIF_API_FUNC_DECL(char*,enif_cond_name,(ErlNifCond*));
+ERL_NIF_API_FUNC_DECL(char*,enif_rwlock_name,(ErlNifRWLock*));
+ERL_NIF_API_FUNC_DECL(char*,enif_thread_name,(ErlNifTid));
+
+ERL_NIF_API_FUNC_DECL(int,enif_vfprintf,(FILE*, const char *fmt, va_list));
+ERL_NIF_API_FUNC_DECL(int,enif_vsnprintf,(char*, size_t, const char *fmt, va_list));
+
+ERL_NIF_API_FUNC_DECL(int,enif_make_map_from_arrays,(ErlNifEnv *env, ERL_NIF_TERM keys[], ERL_NIF_TERM values[], size_t cnt, ERL_NIF_TERM *map_out));
+
/*
** ADD NEW ENTRIES HERE (before this comment) !!!
*/
@@ -375,6 +385,13 @@ ERL_NIF_API_FUNC_DECL(int,enif_ioq_peek_head,(ErlNifEnv *env, ErlNifIOQueue *q,
# define enif_inspect_iovec ERL_NIF_API_FUNC_MACRO(enif_inspect_iovec)
# define enif_free_iovec ERL_NIF_API_FUNC_MACRO(enif_free_iovec)
# define enif_ioq_peek_head ERL_NIF_API_FUNC_MACRO(enif_ioq_peek_head)
+# define enif_mutex_name ERL_NIF_API_FUNC_MACRO(enif_mutex_name)
+# define enif_cond_name ERL_NIF_API_FUNC_MACRO(enif_cond_name)
+# define enif_rwlock_name ERL_NIF_API_FUNC_MACRO(enif_rwlock_name)
+# define enif_thread_name ERL_NIF_API_FUNC_MACRO(enif_thread_name)
+# define enif_vfprintf ERL_NIF_API_FUNC_MACRO(enif_vfprintf)
+# define enif_vsnprintf ERL_NIF_API_FUNC_MACRO(enif_vsnprintf)
+# define enif_make_map_from_arrays ERL_NIF_API_FUNC_MACRO(enif_make_map_from_arrays)
/*
** ADD NEW ENTRIES HERE (before this comment)
diff --git a/erts/emulator/beam/erl_node_container_utils.h b/erts/emulator/beam/erl_node_container_utils.h
index 6ec428e282..99e938266b 100644
--- a/erts/emulator/beam/erl_node_container_utils.h
+++ b/erts/emulator/beam/erl_node_container_utils.h
@@ -318,5 +318,3 @@ extern ErtsPTab erts_port;
#define is_not_ref(x) (!is_ref(x))
#endif
-
-
diff --git a/erts/emulator/beam/erl_node_tables.c b/erts/emulator/beam/erl_node_tables.c
index eaf133f5c0..1f147011a8 100644
--- a/erts/emulator/beam/erl_node_tables.c
+++ b/erts/emulator/beam/erl_node_tables.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2001-2017. All Rights Reserved.
+ * Copyright Ericsson AB 2001-2018. 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.
@@ -31,6 +31,7 @@
#include "dtrace-wrapper.h"
#include "erl_binary.h"
#include "erl_bif_unique.h"
+#include "erl_proc_sig_queue.h"
Hash erts_dist_table;
Hash erts_node_table;
@@ -170,15 +171,11 @@ dist_table_alloc(void *dep_tmpl)
dep->cid = NIL;
erts_atomic_init_nob(&dep->input_handler, (erts_aint_t) NIL);
dep->connection_id = 0;
- dep->status = 0;
+ dep->state = ERTS_DE_STATE_IDLE;
dep->flags = 0;
dep->version = 0;
- erts_mtx_init(&dep->lnk_mtx, "dist_entry_links", sysname,
- ERTS_LOCK_FLAGS_CATEGORY_DISTRIBUTION);
- dep->node_links = NULL;
- dep->nlinks = NULL;
- dep->monitors = NULL;
+ dep->mld = NULL;
erts_mtx_init(&dep->qlock, "dist_entry_out_queue", sysname,
ERTS_LOCK_FLAGS_CATEGORY_DISTRIBUTION);
@@ -223,11 +220,9 @@ dist_table_free(void *vdep)
DistEntry *dep = (DistEntry *) vdep;
ASSERT(de_refc_read(dep, -1) == -1);
- ASSERT(dep->status == 0);
+ ASSERT(dep->state == ERTS_DE_STATE_IDLE);
ASSERT(is_nil(dep->cid));
- ASSERT(dep->nlinks == NULL);
- ASSERT(dep->node_links == NULL);
- ASSERT(dep->monitors == NULL);
+ ASSERT(dep->mld == NULL);
/* Link out */
@@ -250,7 +245,6 @@ dist_table_free(void *vdep)
ASSERT(!dep->cache);
erts_rwmtx_destroy(&dep->rwmtx);
- erts_mtx_destroy(&dep->lnk_mtx);
erts_mtx_destroy(&dep->qlock);
#ifdef DEBUG
@@ -556,14 +550,14 @@ erts_set_dist_entry_not_connected(DistEntry *dep)
ASSERT(dep != erts_this_dist_entry);
- if (dep->status & ERTS_DE_SFLG_PENDING) {
+ if (dep->state == ERTS_DE_STATE_PENDING) {
ASSERT(is_nil(dep->cid));
ASSERT(erts_no_of_pending_dist_entries > 0);
erts_no_of_pending_dist_entries--;
head = &erts_pending_dist_entries;
}
else {
- ASSERT(dep->status != 0);
+ ASSERT(dep->state != ERTS_DE_STATE_IDLE);
ASSERT(is_internal_port(dep->cid) || is_internal_pid(dep->cid));
if (dep->flags & DFLAG_PUBLISHED) {
ASSERT(erts_no_of_visible_dist_entries > 0);
@@ -588,7 +582,7 @@ erts_set_dist_entry_not_connected(DistEntry *dep)
if(dep->next)
dep->next->prev = dep->prev;
- dep->status &= ~(ERTS_DE_SFLG_PENDING | ERTS_DE_SFLG_CONNECTED);
+ dep->state = ERTS_DE_STATE_EXITING;
dep->flags = 0;
dep->prev = NULL;
dep->cid = NIL;
@@ -606,11 +600,13 @@ erts_set_dist_entry_not_connected(DistEntry *dep)
void
erts_set_dist_entry_pending(DistEntry *dep)
{
+ ErtsMonLnkDist *mld = erts_mon_link_dist_create(dep->sysname);
ERTS_LC_ASSERT(erts_lc_is_de_rwlocked(dep));
+
erts_rwmtx_rwlock(&erts_dist_table_rwmtx);
ASSERT(dep != erts_this_dist_entry);
- ASSERT(dep->status == 0);
+ ASSERT(dep->state == ERTS_DE_STATE_IDLE);
ASSERT(is_nil(dep->cid));
if(dep->prev) {
@@ -627,10 +623,14 @@ erts_set_dist_entry_pending(DistEntry *dep)
erts_no_of_not_connected_dist_entries--;
- dep->status = ERTS_DE_SFLG_PENDING;
- dep->flags = (DFLAG_DIST_MANDATORY | DFLAG_DIST_HOPEFULLY);
+ dep->state = ERTS_DE_STATE_PENDING;
+ dep->flags = (DFLAG_DIST_MANDATORY | DFLAG_DIST_HOPEFULLY | DFLAG_NO_MAGIC);
dep->connection_id = (dep->connection_id + 1) & ERTS_DIST_CON_ID_MASK;
+ ASSERT(!dep->mld);
+ mld->connection_id = dep->connection_id;
+ dep->mld = mld;
+
dep->prev = NULL;
dep->next = erts_pending_dist_entries;
if(erts_pending_dist_entries) {
@@ -647,12 +647,14 @@ erts_set_dist_entry_connected(DistEntry *dep, Eterm cid, Uint flags)
{
erts_aint32_t set_qflgs;
+ ASSERT(dep->mld);
+
ERTS_LC_ASSERT(erts_lc_is_de_rwlocked(dep));
erts_rwmtx_rwlock(&erts_dist_table_rwmtx);
ASSERT(dep != erts_this_dist_entry);
ASSERT(is_nil(dep->cid));
- ASSERT(dep->status & ERTS_DE_SFLG_PENDING);
+ ASSERT(dep->state == ERTS_DE_STATE_PENDING);
ASSERT(is_internal_port(cid) || is_internal_pid(cid));
if(dep->prev) {
@@ -670,8 +672,7 @@ erts_set_dist_entry_connected(DistEntry *dep, Eterm cid, Uint flags)
ASSERT(erts_no_of_pending_dist_entries > 0);
erts_no_of_pending_dist_entries--;
- dep->status &= ~ERTS_DE_SFLG_PENDING;
- dep->status |= ERTS_DE_SFLG_CONNECTED;
+ dep->state = ERTS_DE_STATE_CONNECTED;
dep->flags = flags & ~DFLAG_NO_MAGIC;
dep->cid = cid;
erts_atomic_set_nob(&dep->input_handler,
@@ -1050,14 +1051,16 @@ static void erts_lcnt_enable_dist_lock_count(void *dep_raw, void *enable) {
if(enable) {
erts_lcnt_install_new_lock_info(&dep->rwmtx.lcnt, "dist_entry", dep->sysname,
ERTS_LOCK_TYPE_RWMUTEX | ERTS_LOCK_FLAGS_CATEGORY_DISTRIBUTION);
- erts_lcnt_install_new_lock_info(&dep->lnk_mtx.lcnt, "dist_entry_links", dep->sysname,
- ERTS_LOCK_TYPE_MUTEX | ERTS_LOCK_FLAGS_CATEGORY_DISTRIBUTION);
erts_lcnt_install_new_lock_info(&dep->qlock.lcnt, "dist_entry_out_queue", dep->sysname,
ERTS_LOCK_TYPE_MUTEX | ERTS_LOCK_FLAGS_CATEGORY_DISTRIBUTION);
+ if (dep->mld)
+ erts_lcnt_install_new_lock_info(&dep->mld->mtx.lcnt, "dist_entry_links", dep->sysname,
+ ERTS_LOCK_TYPE_MUTEX | ERTS_LOCK_FLAGS_CATEGORY_DISTRIBUTION);
} else {
erts_lcnt_uninstall(&dep->rwmtx.lcnt);
- erts_lcnt_uninstall(&dep->lnk_mtx.lcnt);
erts_lcnt_uninstall(&dep->qlock.lcnt);
+ if (dep->mld)
+ erts_lcnt_uninstall(&dep->mld->mtx.lcnt);
}
}
@@ -1100,6 +1103,7 @@ static Eterm AM_system;
static Eterm AM_timer;
static Eterm AM_delayed_delete_timer;
static Eterm AM_thread_progress_delete_timer;
+static Eterm AM_signal;
static void setup_reference_table(void);
static Eterm reference_table_term(Uint **hpp, ErlOffHeap *ohp, Uint *szp);
@@ -1121,6 +1125,7 @@ typedef struct node_referrer_ {
int bin_ref;
int timer_ref;
int system_ref;
+ int signal_ref;
Eterm id;
Uint id_heap[ID_HEAP_SIZE];
ErlOffHeap off_heap;
@@ -1138,6 +1143,7 @@ typedef struct dist_referrer_ {
int node_ref;
int ctrl_ref;
int system_ref;
+ int signal_ref;
Eterm id;
Uint creation;
Uint id_heap[ID_HEAP_SIZE];
@@ -1191,6 +1197,7 @@ erts_get_node_and_dist_references(struct process *proc)
INIT_AM(system);
INIT_AM(delayed_delete_timer);
INIT_AM(thread_progress_delete_timer);
+ INIT_AM(signal);
references_atoms_need_init = 0;
}
@@ -1227,6 +1234,7 @@ erts_get_node_and_dist_references(struct process *proc)
#define MONITOR_REF 7
#define TIMER_REF 8
#define SYSTEM_REF 9
+#define SIGNAL_REF 10
#define INC_TAB_SZ 10
@@ -1261,6 +1269,7 @@ insert_dist_referrer(ReferredDist *referred_dist,
drp->node_ref = 0;
drp->ctrl_ref = 0;
drp->system_ref = 0;
+ drp->signal_ref = 0;
}
switch (type) {
@@ -1269,6 +1278,7 @@ insert_dist_referrer(ReferredDist *referred_dist,
case HEAP_REF: drp->heap_ref++; break;
case ETS_REF: drp->ets_ref++; break;
case SYSTEM_REF: drp->system_ref++; break;
+ case SIGNAL_REF: drp->signal_ref++; break;
default: ASSERT(0);
}
}
@@ -1322,6 +1332,7 @@ insert_node_referrer(ReferredNode *referred_node, int type, Eterm id)
nrp->bin_ref = 0;
nrp->timer_ref = 0;
nrp->system_ref = 0;
+ nrp->signal_ref = 0;
}
switch (type) {
@@ -1332,6 +1343,7 @@ insert_node_referrer(ReferredNode *referred_node, int type, Eterm id)
case MONITOR_REF: nrp->monitor_ref++; break;
case TIMER_REF: nrp->timer_ref++; break;
case SYSTEM_REF: nrp->system_ref++; break;
+ case SIGNAL_REF: nrp->signal_ref++; break;
default: ASSERT(0);
}
}
@@ -1439,49 +1451,145 @@ insert_offheap(ErlOffHeap *oh, int type, Eterm id)
}
}
-static void doit_insert_monitor(ErtsMonitor *monitor, void *p)
+static void insert_monitor_data(ErtsMonitor *mon, int type, Eterm id)
{
- Eterm *idp = p;
- if(monitor->type != MON_NIF_TARGET && is_external(monitor->u.pid))
- insert_node(external_thing_ptr(monitor->u.pid)->node, MONITOR_REF, *idp);
- if(is_external(monitor->ref))
- insert_node(external_thing_ptr(monitor->ref)->node, MONITOR_REF, *idp);
+ ErtsMonitorData *mdp = erts_monitor_to_data(mon);
+ if ((mdp->origin.flags & (ERTS_ML_FLG_DBG_VISITED
+ | ERTS_ML_FLG_EXTENDED)) == ERTS_ML_FLG_EXTENDED) {
+ if (mon->type != ERTS_MON_TYPE_NODE) {
+ ErtsMonitorDataExtended *mdep = (ErtsMonitorDataExtended *) mdp;
+ ASSERT(mon->flags & ERTS_ML_FLG_EXTENDED);
+ if (mdep->uptr.ohhp) {
+ ErlOffHeap oh;
+ ERTS_INIT_OFF_HEAP(&oh);
+ oh.first = mdep->uptr.ohhp;
+ insert_offheap(&oh, type, id);
+ }
+ }
+ }
+ mdp->origin.flags |= ERTS_ML_FLG_DBG_VISITED;
}
-static void doit_insert_link(ErtsLink *lnk, void *p)
+static void insert_monitor(ErtsMonitor *mon, void *idp)
{
- Eterm *idp = p;
- if(is_external(lnk->pid))
- insert_node(external_thing_ptr(lnk->pid)->node, LINK_REF,
- *idp);
+ Eterm id = *((Eterm *) idp);
+ insert_monitor_data(mon, MONITOR_REF, id);
}
+static void clear_visited_monitor(ErtsMonitor *mon, void *p)
+{
+ ErtsMonitorData *mdp = erts_monitor_to_data(mon);
+ mdp->origin.flags &= ~ERTS_ML_FLG_DBG_VISITED;
+}
static void
-insert_monitors(ErtsMonitor *monitors, Eterm id)
+insert_p_monitors(ErtsPTabElementCommon *p)
{
- erts_doforall_monitors(monitors,&doit_insert_monitor,&id);
+ Eterm id = p->id;
+ erts_monitor_tree_foreach(p->u.alive.monitors,
+ insert_monitor,
+ (void *) &id);
+ erts_monitor_list_foreach(p->u.alive.lt_monitors,
+ insert_monitor,
+ (void *) &id);
+}
+
+static void
+insert_dist_monitors(DistEntry *dep)
+{
+ if (dep->mld) {
+ erts_monitor_list_foreach(dep->mld->monitors,
+ insert_monitor,
+ (void *) &dep->sysname);
+ erts_monitor_tree_foreach(dep->mld->orig_name_monitors,
+ insert_monitor,
+ (void *) &dep->sysname);
+ }
}
static void
-insert_links(ErtsLink *lnk, Eterm id)
+clear_visited_p_monitors(ErtsPTabElementCommon *p)
{
- erts_doforall_links(lnk,&doit_insert_link,&id);
+ erts_monitor_tree_foreach(p->u.alive.monitors,
+ clear_visited_monitor,
+ NULL);
+ erts_monitor_list_foreach(p->u.alive.lt_monitors,
+ clear_visited_monitor,
+ NULL);
}
-static void doit_insert_link2(ErtsLink *lnk, void *p)
+static void
+clear_visited_dist_monitors(DistEntry *dep)
+{
+ if (dep->mld) {
+ erts_monitor_list_foreach(dep->mld->monitors,
+ clear_visited_monitor,
+ NULL);
+ erts_monitor_tree_foreach(dep->mld->orig_name_monitors,
+ clear_visited_monitor,
+ NULL);
+ }
+}
+
+static void insert_link_data(ErtsLink *lnk, int type, Eterm id)
+{
+ ErtsLinkData *ldp = erts_link_to_data(lnk);
+ if ((ldp->a.flags & (ERTS_ML_FLG_DBG_VISITED
+ | ERTS_ML_FLG_EXTENDED)) == ERTS_ML_FLG_EXTENDED) {
+ ErtsLinkDataExtended *ldep = (ErtsLinkDataExtended *) ldp;
+ if (ldep->ohhp) {
+ ErlOffHeap oh;
+ ERTS_INIT_OFF_HEAP(&oh);
+ oh.first = ldep->ohhp;
+ insert_offheap(&oh, type, id);
+ }
+ }
+ ldp->a.flags |= ERTS_ML_FLG_DBG_VISITED;
+}
+
+static void insert_link(ErtsLink *lnk, void *idp)
{
- Eterm *idp = p;
- if(is_external(lnk->pid))
- insert_node(external_thing_ptr(lnk->pid)->node, LINK_REF,
- *idp);
- insert_links(ERTS_LINK_ROOT(lnk), *idp);
+ Eterm id = *((Eterm *) idp);
+ insert_link_data(lnk, LINK_REF, id);
+}
+
+static void clear_visited_link(ErtsLink *lnk, void *p)
+{
+ ErtsLinkData *ldp = erts_link_to_data(lnk);
+ ldp->a.flags &= ~ERTS_ML_FLG_DBG_VISITED;
}
static void
-insert_links2(ErtsLink *lnk, Eterm id)
+insert_p_links(ErtsPTabElementCommon *p)
{
- erts_doforall_links(lnk,&doit_insert_link2,&id);
+ Eterm id = p->id;
+ erts_link_tree_foreach(p->u.alive.links, insert_link, (void *) &id);
+}
+
+static void
+insert_dist_links(DistEntry *dep)
+{
+ if (dep->mld)
+ erts_link_list_foreach(dep->mld->links,
+ insert_link,
+ (void *) &dep->sysname);
+}
+
+static void
+clear_visited_p_links(ErtsPTabElementCommon *p)
+{
+ erts_link_tree_foreach(p->u.alive.links,
+ clear_visited_link,
+ NULL);
+}
+
+static void
+clear_visited_dist_links(DistEntry *dep)
+{
+ if (dep->mld)
+ erts_link_list_foreach(dep->mld->links,
+ clear_visited_link,
+ NULL);
}
static void
@@ -1577,6 +1685,60 @@ insert_delayed_delete_dist_entry(void *state,
}
static void
+insert_message(ErtsMessage *msg, int type, Process *proc)
+{
+ ErlHeapFragment *heap_frag = NULL;
+
+ ASSERT(ERTS_SIG_IS_MSG(msg));
+ if (msg->data.attached) {
+ if (msg->data.attached == ERTS_MSG_COMBINED_HFRAG)
+ heap_frag = &msg->hfrag;
+ else if (ERTS_SIG_IS_INTERNAL_MSG(msg))
+ heap_frag = msg->data.heap_frag;
+ else {
+ if (msg->data.dist_ext->dep)
+ insert_dist_entry(msg->data.dist_ext->dep,
+ type, proc->common.id, 0);
+ if (is_not_nil(ERL_MESSAGE_TOKEN(msg)))
+ heap_frag = erts_dist_ext_trailer(msg->data.dist_ext);
+ }
+ }
+ while (heap_frag) {
+ insert_offheap(&(heap_frag->off_heap),
+ type,
+ proc->common.id);
+ heap_frag = heap_frag->next;
+ }
+}
+
+static void
+insert_sig_msg(ErtsMessage *msg, void *arg)
+{
+ insert_message(msg, SIGNAL_REF, (Process *) arg);
+}
+
+static void
+insert_sig_offheap(ErlOffHeap *ohp, void *arg)
+{
+ Process *proc = arg;
+ insert_offheap(ohp, SIGNAL_REF, proc->common.id);
+}
+
+static void
+insert_sig_monitor(ErtsMonitor *mon, void *arg)
+{
+ Process *proc = arg;
+ insert_monitor_data(mon, SIGNAL_REF, proc->common.id);
+}
+
+static void
+insert_sig_link(ErtsLink *lnk, void *arg)
+{
+ Process *proc = arg;
+ insert_link_data(lnk, SIGNAL_REF, proc->common.id);
+}
+
+static void
setup_reference_table(void)
{
ErlHeapFragment *hfp;
@@ -1631,10 +1793,7 @@ setup_reference_table(void)
Process *proc = erts_pix2proc(i);
if (proc) {
int mli;
- ErtsMessage *msg_list[] = {
- proc->msg.first,
- proc->msg_inq.first,
- proc->msg_frag};
+ ErtsMessage *msg_list[] = {proc->msg_frag};
/* Insert Heap */
insert_offheap(&(proc->off_heap),
@@ -1649,34 +1808,24 @@ setup_reference_table(void)
/* Insert msg buffers */
for (mli = 0; mli < sizeof(msg_list)/sizeof(msg_list[0]); mli++) {
ErtsMessage *msg;
- for (msg = msg_list[mli]; msg; msg = msg->next) {
- ErlHeapFragment *heap_frag = NULL;
- if (msg->data.attached) {
- if (msg->data.attached == ERTS_MSG_COMBINED_HFRAG)
- heap_frag = &msg->hfrag;
- else if (is_value(ERL_MESSAGE_TERM(msg)))
- heap_frag = msg->data.heap_frag;
- else {
- if (msg->data.dist_ext->dep)
- insert_dist_entry(msg->data.dist_ext->dep,
- HEAP_REF, proc->common.id, 0);
- if (is_not_nil(ERL_MESSAGE_TOKEN(msg)))
- heap_frag = erts_dist_ext_trailer(msg->data.dist_ext);
- }
- }
- while (heap_frag) {
- insert_offheap(&(heap_frag->off_heap),
- HEAP_REF,
- proc->common.id);
- heap_frag = heap_frag->next;
- }
- }
+ for (msg = msg_list[mli]; msg; msg = msg->next)
+ insert_message(msg, HEAP_REF, proc);
}
+
+ /* Insert signal queue */
+ erts_proc_sig_debug_foreach_sig(proc,
+ insert_sig_msg,
+ insert_sig_offheap,
+ insert_sig_monitor,
+ insert_sig_link,
+ (void *) proc);
+
/* Insert links */
- if (ERTS_P_LINKS(proc))
- insert_links(ERTS_P_LINKS(proc), proc->common.id);
- if (ERTS_P_MONITORS(proc))
- insert_monitors(ERTS_P_MONITORS(proc), proc->common.id);
+ insert_p_links(&proc->common);
+
+ /* Insert monitors */
+ insert_p_monitors(&proc->common);
+
{
DistEntry *dep = ERTS_PROC_GET_DIST_ENTRY(proc);
if (dep)
@@ -1706,11 +1855,9 @@ setup_reference_table(void)
continue;
/* Insert links */
- if (ERTS_P_LINKS(prt))
- insert_links(ERTS_P_LINKS(prt), prt->common.id);
+ insert_p_links(&prt->common);
/* Insert monitors */
- if (ERTS_P_MONITORS(prt))
- insert_monitors(ERTS_P_MONITORS(prt), prt->common.id);
+ insert_p_monitors(&prt->common);
/* Insert port data */
ohp = erts_port_data_offheap(prt);
if (ohp)
@@ -1759,41 +1906,25 @@ setup_reference_table(void)
/* Insert all dist links */
for(dep = erts_visible_dist_entries; dep; dep = dep->next) {
- if(dep->nlinks)
- insert_links2(dep->nlinks, dep->sysname);
- if(dep->node_links)
- insert_links(dep->node_links, dep->sysname);
- if(dep->monitors)
- insert_monitors(dep->monitors, dep->sysname);
+ insert_dist_links(dep);
+ insert_dist_monitors(dep);
}
for(dep = erts_hidden_dist_entries; dep; dep = dep->next) {
- if(dep->nlinks)
- insert_links2(dep->nlinks, dep->sysname);
- if(dep->node_links)
- insert_links(dep->node_links, dep->sysname);
- if(dep->monitors)
- insert_monitors(dep->monitors, dep->sysname);
+ insert_dist_links(dep);
+ insert_dist_monitors(dep);
}
for(dep = erts_pending_dist_entries; dep; dep = dep->next) {
- if(dep->nlinks)
- insert_links2(dep->nlinks, dep->sysname);
- if(dep->node_links)
- insert_links(dep->node_links, dep->sysname);
- if(dep->monitors)
- insert_monitors(dep->monitors, dep->sysname);
+ insert_dist_links(dep);
+ insert_dist_monitors(dep);
}
/* Not connected dist entries should not have any links,
but inspect them anyway */
for(dep = erts_not_connected_dist_entries; dep; dep = dep->next) {
- if(dep->nlinks)
- insert_links2(dep->nlinks, dep->sysname);
- if(dep->node_links)
- insert_links(dep->node_links, dep->sysname);
- if(dep->monitors)
- insert_monitors(dep->monitors, dep->sysname);
+ insert_dist_links(dep);
+ insert_dist_monitors(dep);
}
/* Insert all ets tables */
@@ -1878,6 +2009,10 @@ reference_table_term(Uint **hpp, ErlOffHeap *ohp, Uint *szp)
tup = MK_2TUP(AM_system, MK_UINT(nrp->system_ref));
nrl = MK_CONS(tup, nrl);
}
+ if(nrp->signal_ref) {
+ tup = MK_2TUP(AM_signal, MK_UINT(nrp->signal_ref));
+ nrl = MK_CONS(tup, nrl);
+ }
nrid = nrp->id;
if (!IS_CONST(nrp->id)) {
@@ -1901,24 +2036,25 @@ reference_table_term(Uint **hpp, ErlOffHeap *ohp, Uint *szp)
}
else if(is_internal_port(nrid)) {
ASSERT(!nrp->heap_ref && !nrp->ets_ref && !nrp->bin_ref
- && !nrp->timer_ref && !nrp->system_ref);
+ && !nrp->timer_ref && !nrp->system_ref && !nrp->signal_ref);
tup = MK_2TUP(AM_port, nrid);
}
else if(nrp->ets_ref) {
ASSERT(!nrp->heap_ref && !nrp->link_ref &&
!nrp->monitor_ref && !nrp->bin_ref
- && !nrp->timer_ref && !nrp->system_ref);
+ && !nrp->timer_ref && !nrp->system_ref && !nrp->signal_ref);
tup = MK_2TUP(AM_ets, nrid);
}
else if(nrp->bin_ref) {
ASSERT(is_small(nrid) || is_big(nrid));
ASSERT(!nrp->heap_ref && !nrp->ets_ref && !nrp->link_ref &&
!nrp->monitor_ref && !nrp->timer_ref
- && !nrp->system_ref);
+ && !nrp->system_ref && !nrp->signal_ref);
tup = MK_2TUP(AM_match_spec, nrid);
}
else {
- ASSERT(!nrp->heap_ref && !nrp->ets_ref && !nrp->bin_ref);
+ ASSERT(!nrp->heap_ref && !nrp->ets_ref && !nrp->bin_ref
+ && !nrp->signal_ref);
ASSERT(is_atom(nrid));
tup = MK_2TUP(AM_dist, nrid);
}
@@ -1962,30 +2098,36 @@ reference_table_term(Uint **hpp, ErlOffHeap *ohp, Uint *szp)
tup = MK_2TUP(AM_system, MK_UINT(drp->system_ref));
drl = MK_CONS(tup, drl);
}
+ if(drp->signal_ref) {
+ tup = MK_2TUP(AM_signal, MK_UINT(drp->signal_ref));
+ drl = MK_CONS(tup, drl);
+ }
if (is_internal_pid(drp->id)) {
ASSERT(!drp->node_ref);
tup = MK_2TUP(AM_process, drp->id);
}
else if(is_internal_port(drp->id)) {
- ASSERT(drp->ctrl_ref && !drp->node_ref);
+ ASSERT(drp->ctrl_ref && !drp->node_ref && !drp->signal_ref);
tup = MK_2TUP(AM_port, drp->id);
}
else if (is_tuple(drp->id)) {
Eterm *t;
ASSERT(drp->system_ref && !drp->node_ref
- && !drp->ctrl_ref && !drp->heap_ref && !drp->ets_ref);
+ && !drp->ctrl_ref && !drp->heap_ref && !drp->ets_ref
+ && !drp->signal_ref);
t = tuple_val(drp->id);
ASSERT(2 == arityval(t[0]));
tup = MK_2TUP(t[1], t[2]);
}
else if (drp->ets_ref) {
ASSERT(!drp->heap_ref && !drp->node_ref &&
- !drp->ctrl_ref && !drp->system_ref);
+ !drp->ctrl_ref && !drp->system_ref
+ && !drp->signal_ref);
tup = MK_2TUP(AM_ets, drp->id);
}
- else {
- ASSERT(!drp->ctrl_ref && drp->node_ref);
+ else {
+ ASSERT(!drp->ctrl_ref && drp->node_ref && !drp->signal_ref);
ASSERT(is_atom(drp->id));
tup = MK_2TUP(drp->id, MK_UINT(drp->creation));
tup = MK_2TUP(AM_node, tup);
@@ -2020,10 +2162,21 @@ reference_table_term(Uint **hpp, ErlOffHeap *ohp, Uint *szp)
}
+static void noop_sig_msg(ErtsMessage *msg, void *arg)
+{
+
+}
+
+static void noop_sig_offheap(ErlOffHeap *oh, void *arg)
+{
+
+}
+
static void
delete_reference_table(void)
{
- Uint i;
+ DistEntry *dep;
+ int i, max;
for(i = 0; i < no_referred_nodes; i++) {
NodeReferrer *nrp;
NodeReferrer *tnrp;
@@ -2055,6 +2208,60 @@ delete_reference_table(void)
inserted_bins = inserted_bins->next;
erts_free(ERTS_ALC_T_NC_TMP, (void *)ib);
}
+
+ /* Cleanup... */
+
+ max = erts_ptab_max(&erts_proc);
+ for (i = 0; i < max; i++) {
+ Process *proc = erts_pix2proc(i);
+ if (proc) {
+ clear_visited_p_links(&proc->common);
+ clear_visited_p_monitors(&proc->common);
+ erts_proc_sig_debug_foreach_sig(proc,
+ noop_sig_msg,
+ noop_sig_offheap,
+ clear_visited_monitor,
+ clear_visited_link,
+ (void *) proc);
+ }
+ }
+
+ max = erts_ptab_max(&erts_port);
+ for (i = 0; i < max; i++) {
+ erts_aint32_t state;
+ Port *prt;
+
+ prt = erts_pix2port(i);
+ if (!prt)
+ continue;
+
+ state = erts_atomic32_read_nob(&prt->state);
+ if (state & ERTS_PORT_SFLGS_DEAD)
+ continue;
+
+ clear_visited_p_links(&prt->common);
+ clear_visited_p_monitors(&prt->common);
+ }
+
+ for(dep = erts_visible_dist_entries; dep; dep = dep->next) {
+ clear_visited_dist_links(dep);
+ clear_visited_dist_monitors(dep);
+ }
+
+ for(dep = erts_hidden_dist_entries; dep; dep = dep->next) {
+ clear_visited_dist_links(dep);
+ clear_visited_dist_monitors(dep);
+ }
+
+ for(dep = erts_pending_dist_entries; dep; dep = dep->next) {
+ clear_visited_dist_links(dep);
+ clear_visited_dist_monitors(dep);
+ }
+
+ for(dep = erts_not_connected_dist_entries; dep; dep = dep->next) {
+ clear_visited_dist_links(dep);
+ clear_visited_dist_monitors(dep);
+ }
}
void
diff --git a/erts/emulator/beam/erl_node_tables.h b/erts/emulator/beam/erl_node_tables.h
index 8d29c83e15..9a792b10b1 100644
--- a/erts/emulator/beam/erl_node_tables.h
+++ b/erts/emulator/beam/erl_node_tables.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2001-2017. All Rights Reserved.
+ * Copyright Ericsson AB 2001-2018. 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.
@@ -18,7 +18,17 @@
* %CopyrightEnd%
*/
-#ifndef ERL_NODE_TABLES_H__
+#ifndef ERL_NODE_TABLES_BASIC__
+#define ERL_NODE_TABLES_BASIC__
+
+typedef struct dist_entry_ DistEntry;
+typedef struct ErtsDistOutputBuf_ ErtsDistOutputBuf;
+void erts_ref_dist_entry(DistEntry *dep);
+void erts_deref_dist_entry(DistEntry *dep);
+
+#endif
+
+#if !defined(ERL_NODE_TABLES_BASIC_ONLY) && !defined(ERL_NODE_TABLES_H__)
#define ERL_NODE_TABLES_H__
/*
@@ -42,14 +52,14 @@
#include "sys.h"
#include "hash.h"
#include "erl_alloc.h"
-#include "erl_process.h"
-#include "erl_monitors.h"
#define ERTS_PORT_TASK_ONLY_BASIC_TYPES__
#include "erl_port_task.h"
#undef ERTS_PORT_TASK_ONLY_BASIC_TYPES__
+#include "erl_process.h"
#define ERTS_BINARY_TYPES_ONLY__
#include "erl_binary.h"
#undef ERTS_BINARY_TYPES_ONLY__
+#include "erl_monitor_link.h"
#define ERTS_NODE_TAB_DELAY_GC_DEFAULT (60)
#define ERTS_NODE_TAB_DELAY_GC_MAX (100*1000*1000)
@@ -57,9 +67,12 @@
#define ERST_INTERNAL_CHANNEL_NO 0
-#define ERTS_DE_SFLG_PENDING (((Uint32) 1) << 0)
-#define ERTS_DE_SFLG_CONNECTED (((Uint32) 1) << 1)
-#define ERTS_DE_SFLG_EXITING (((Uint32) 1) << 2)
+enum dist_entry_state {
+ ERTS_DE_STATE_IDLE,
+ ERTS_DE_STATE_PENDING,
+ ERTS_DE_STATE_CONNECTED,
+ ERTS_DE_STATE_EXITING
+};
#define ERTS_DE_QFLG_BUSY (((erts_aint32_t) 1) << 0)
#define ERTS_DE_QFLG_EXIT (((erts_aint32_t) 1) << 1)
@@ -79,13 +92,13 @@
#define ERTS_DIST_OUTPUT_BUF_DBG_PATTERN ((Uint) 0xf713f713)
#endif
-typedef struct ErtsDistOutputBuf_ ErtsDistOutputBuf;
struct ErtsDistOutputBuf_ {
#ifdef DEBUG
Uint dbg_pattern;
byte *alloc_endp;
#endif
ErtsDistOutputBuf *next;
+ Uint hopefull_flags;
byte *extp;
byte *ext_endp;
byte *msg_start;
@@ -109,7 +122,7 @@ struct ErtsProcList_;
* unlock mutexes with higher numbers before mutexes with higher numbers.
*/
-typedef struct dist_entry_ {
+struct dist_entry_ {
HashBucket hash_bucket; /* Hash bucket */
struct dist_entry_ *next; /* Next entry in dist_table (not sorted) */
struct dist_entry_ *prev; /* Previous entry in dist_table (not sorted) */
@@ -121,23 +134,12 @@ typedef struct dist_entry_ {
Eterm cid; /* connection handler (pid or port),
NIL == free */
Uint32 connection_id; /* Connection id incremented on connect */
- Uint32 status; /* Slot status, like exiting reserved etc */
+ enum dist_entry_state state;
Uint32 flags; /* Distribution flags, like hidden,
atom cache etc. */
unsigned long version; /* Protocol version */
-
- erts_mtx_t lnk_mtx; /* Protects node_links, nlinks, and
- monitors. */
- ErtsLink *node_links; /* In a dist entry, node links are kept
- in a separate tree, while they are
- colocted with the ordinary link tree
- for processes. It's not due to confusion,
- it's because the link tree for the dist
- entry is in two levels, see erl_monitors.h
- */
- ErtsLink *nlinks; /* Link tree with subtrees */
- ErtsMonitor *monitors; /* Monitor tree */
+ ErtsMonLnkDist *mld; /* Monitors and links */
erts_mtx_t qlock; /* Protects qflgs and out_queue */
erts_atomic32_t qflgs;
@@ -159,7 +161,7 @@ typedef struct dist_entry_ {
ErtsThrPrgrLaterOp later_op;
struct transcode_context* transcode_ctx;
-} DistEntry;
+};
typedef struct erl_node_ {
HashBucket hash_bucket; /* Hash bucket */
@@ -215,16 +217,12 @@ int erts_dist_entry_destructor(Binary *bin);
DistEntry *erts_dhandle_to_dist_entry(Eterm dhandle);
Eterm erts_build_dhandle(Eterm **hpp, ErlOffHeap*, DistEntry*);
Eterm erts_make_dhandle(Process *c_p, DistEntry *dep);
-void erts_ref_dist_entry(DistEntry *dep);
-void erts_deref_dist_entry(DistEntry *dep);
ERTS_GLB_INLINE void erts_deref_node_entry(ErlNode *np);
ERTS_GLB_INLINE void erts_de_rlock(DistEntry *dep);
ERTS_GLB_INLINE void erts_de_runlock(DistEntry *dep);
ERTS_GLB_INLINE void erts_de_rwlock(DistEntry *dep);
ERTS_GLB_INLINE void erts_de_rwunlock(DistEntry *dep);
-ERTS_GLB_INLINE void erts_de_links_lock(DistEntry *dep);
-ERTS_GLB_INLINE void erts_de_links_unlock(DistEntry *dep);
#if ERTS_GLB_INLINE_INCL_FUNC_DEF
@@ -260,18 +258,6 @@ erts_de_rwunlock(DistEntry *dep)
erts_rwmtx_rwunlock(&dep->rwmtx);
}
-ERTS_GLB_INLINE void
-erts_de_links_lock(DistEntry *dep)
-{
- erts_mtx_lock(&dep->lnk_mtx);
-}
-
-ERTS_GLB_INLINE void
-erts_de_links_unlock(DistEntry *dep)
-{
- erts_mtx_unlock(&dep->lnk_mtx);
-}
-
#endif /* #if ERTS_GLB_INLINE_INCL_FUNC_DEF */
void erts_debug_test_node_tab_delayed_delete(Sint64 millisecs);
diff --git a/erts/emulator/beam/erl_port.h b/erts/emulator/beam/erl_port.h
index 9117eb1f72..2a98a6f00b 100644
--- a/erts/emulator/beam/erl_port.h
+++ b/erts/emulator/beam/erl_port.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2012-2017. All Rights Reserved.
+ * Copyright Ericsson AB 2012-2018. 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.
@@ -180,6 +180,7 @@ void erts_init_port_data(Port *);
void erts_cleanup_port_data(Port *);
Uint erts_port_data_size(Port *);
ErlOffHeap *erts_port_data_offheap(Port *);
+Eterm erts_port_data_read(Port* prt);
#define ERTS_PORT_GET_CONNECTED(PRT) \
((Eterm) erts_atomic_read_nob(&(PRT)->connected))
@@ -195,26 +196,52 @@ struct erl_drv_port_data_lock {
Port *prt;
};
+ERTS_GLB_INLINE void erts_init_runq_port(Port *prt, ErtsRunQueue *runq);
+ERTS_GLB_INLINE void erts_set_runq_port(Port *prt, ErtsRunQueue *runq);
+ERTS_GLB_INLINE ErtsRunQueue *erts_get_runq_port(Port *prt);
ERTS_GLB_INLINE ErtsRunQueue *erts_port_runq(Port *prt);
#if ERTS_GLB_INLINE_INCL_FUNC_DEF
+ERTS_GLB_INLINE void
+erts_init_runq_port(Port *prt, ErtsRunQueue *runq)
+{
+ if (!runq)
+ ERTS_INTERNAL_ERROR("Missing run-queue");
+ erts_atomic_init_nob(&prt->run_queue, (erts_aint_t) runq);
+}
+
+ERTS_GLB_INLINE void
+erts_set_runq_port(Port *prt, ErtsRunQueue *runq)
+{
+ if (!runq)
+ ERTS_INTERNAL_ERROR("Missing run-queue");
+ erts_atomic_set_nob(&prt->run_queue, (erts_aint_t) runq);
+}
+
+ERTS_GLB_INLINE ErtsRunQueue *
+erts_get_runq_port(Port *prt)
+{
+ ErtsRunQueue *runq;
+ runq = (ErtsRunQueue *) erts_atomic_read_nob(&prt->run_queue);
+ if (!runq)
+ ERTS_INTERNAL_ERROR("Missing run-queue");
+ return runq;
+}
+
+
ERTS_GLB_INLINE ErtsRunQueue *
erts_port_runq(Port *prt)
{
ErtsRunQueue *rq1, *rq2;
- rq1 = (ErtsRunQueue *) erts_atomic_read_nob(&prt->run_queue);
- if (!rq1)
- return NULL;
+ rq1 = erts_get_runq_port(prt);
while (1) {
erts_runq_lock(rq1);
- rq2 = (ErtsRunQueue *) erts_atomic_read_nob(&prt->run_queue);
+ rq2 = erts_get_runq_port(prt);
if (rq1 == rq2)
return rq1;
erts_runq_unlock(rq1);
rq1 = rq2;
- if (!rq1)
- return NULL;
}
}
@@ -346,7 +373,7 @@ Eterm erts_request_io_bytes(Process *c_p);
void print_port_info(Port *, fmtfn_t, void *);
void erts_port_free(Port *);
-void erts_fire_port_monitor(Port *prt, Eterm ref);
+void erts_fire_port_monitor(Port *prt, ErtsMonitor *tmon);
int erts_port_handle_xports(Port *);
#if defined(ERTS_ENABLE_LOCK_CHECK)
@@ -860,20 +887,20 @@ struct ErtsProc2PortSigData_ {
Eterm item;
} info;
struct {
- Eterm port;
- Eterm to;
+ Eterm port_id;
+ ErtsLink *lnk;
} link;
struct {
- Eterm from;
+ Eterm port_id;
+ ErtsLink *lnk;
} unlink;
struct {
- Eterm origin; /* who receives monitor event, pid */
- Eterm name; /* either name for named monitor, or port id */
+ Eterm port_id;
+ ErtsMonitor *mon;
} monitor;
struct {
- Eterm origin; /* who is at the other end of the monitor, pid */
- Eterm name; /* port id */
- Uint32 ref[ERTS_MAX_REF_NUMBERS]; /* box contents of a ref */
+ Eterm port_id;
+ ErtsMonitor *mon;
} demonitor;
} u;
} ;
@@ -919,7 +946,6 @@ typedef int (*ErtsProc2PortSigCallback)(Port *,
typedef enum {
ERTS_PORT_OP_BADARG,
- ERTS_PORT_OP_CALLER_EXIT,
ERTS_PORT_OP_BUSY,
ERTS_PORT_OP_BUSY_SCHEDULED,
ERTS_PORT_OP_SCHEDULED,
@@ -955,32 +981,13 @@ ErtsPortOpResult erts_port_command(Process *, int, Port *, Eterm, Eterm *);
ErtsPortOpResult erts_port_output(Process *, int, Port *, Eterm, Eterm, Eterm *);
ErtsPortOpResult erts_port_exit(Process *, int, Port *, Eterm, Eterm, Eterm *);
ErtsPortOpResult erts_port_connect(Process *, int, Port *, Eterm, Eterm, Eterm *);
-ErtsPortOpResult erts_port_link(Process *, Port *, Eterm, Eterm *);
-ErtsPortOpResult erts_port_unlink(Process *, Port *, Eterm, Eterm *);
+ErtsPortOpResult erts_port_link(Process *, Port *, ErtsLink *, Eterm *);
+ErtsPortOpResult erts_port_unlink(Process *, Port *, ErtsLink *, Eterm *);
ErtsPortOpResult erts_port_control(Process *, Port *, unsigned int, Eterm, Eterm *);
ErtsPortOpResult erts_port_call(Process *, Port *, unsigned int, Eterm, Eterm *);
ErtsPortOpResult erts_port_info(Process *, Port *, Eterm, Eterm *);
-
-/* Creates monitor between Origin and Target. Ref must be initialized to
- * a reference (ref may be rewritten to be used to serve additionally as a
- * signal id). Name is atom if user monitors port by name or NIL */
-ErtsPortOpResult erts_port_monitor(Process *origin, Port *target, Eterm name,
- Eterm *ref);
-
-typedef enum {
- /* Normal demonitor rules apply with locking and reductions bump */
- ERTS_PORT_DEMONITOR_NORMAL = 1,
- /* Relaxed demonitor rules when process is about to die, which means that
- * pid lookup won't work, locks won't work, no reductions bump. */
- ERTS_PORT_DEMONITOR_ORIGIN_ON_DEATHBED = 2,
-} ErtsDemonitorMode;
-
-/* Removes monitor between origin and target, identified by ref.
- * origin_is_dying can be 0 (false, normal locking rules and reductions bump
- * apply) or 1 (true, in case when we avoid origin locking) */
-ErtsPortOpResult erts_port_demonitor(Process *origin, ErtsDemonitorMode mode,
- Port *target, Eterm ref,
- Eterm *trap_ref);
+ErtsPortOpResult erts_port_monitor(Process *, Port *, ErtsMonitor *);
+ErtsPortOpResult erts_port_demonitor(Process *, Port *, ErtsMonitor *);
/* defined in erl_bif_port.c */
Port *erts_sig_lookup_port(Process *c_p, Eterm id_or_name);
diff --git a/erts/emulator/beam/erl_port_task.c b/erts/emulator/beam/erl_port_task.c
index a588477320..4a3671df0c 100644
--- a/erts/emulator/beam/erl_port_task.c
+++ b/erts/emulator/beam/erl_port_task.c
@@ -84,11 +84,10 @@ static void chk_task_queues(Port *pp, ErtsPortTask *execq, int processing_busy_q
#define LTTNG_DRIVER(TRACEPOINT, PP) do {} while(0)
#endif
-#define ERTS_LC_VERIFY_RQ(RQ, PP) \
- do { \
+#define ERTS_LC_VERIFY_RQ(RQ, PP) \
+ do { \
ERTS_LC_ASSERT(erts_lc_runq_is_locked(runq)); \
- ERTS_LC_ASSERT((RQ) == ((ErtsRunQueue *) \
- erts_atomic_read_nob(&(PP)->run_queue))); \
+ ERTS_LC_ASSERT((RQ) == erts_get_runq_port((PP))); \
} while (0)
#define ERTS_PT_STATE_SCHEDULED 0
@@ -1520,19 +1519,15 @@ erts_port_task_schedule(Eterm id,
/* Enqueue port on run-queue */
runq = erts_port_runq(pp);
- if (!runq)
- ERTS_INTERNAL_ERROR("Missing run-queue");
xrunq = erts_check_emigration_need(runq, ERTS_PORT_PRIO_LEVEL);
ERTS_LC_ASSERT(runq != xrunq);
ERTS_LC_VERIFY_RQ(runq, pp);
if (xrunq) {
/* Emigrate port ... */
- erts_atomic_set_nob(&pp->run_queue, (erts_aint_t) xrunq);
+ erts_set_runq_port(pp, xrunq);
erts_runq_unlock(runq);
runq = erts_port_runq(pp);
- if (!runq)
- ERTS_INTERNAL_ERROR("Missing run-queue");
}
enqueue_port(runq, pp);
@@ -1593,8 +1588,6 @@ erts_port_task_free_port(Port *pp)
ASSERT(!(erts_atomic32_read_nob(&pp->state) & ERTS_PORT_SFLGS_DEAD));
runq = erts_port_runq(pp);
- if (!runq)
- ERTS_INTERNAL_ERROR("Missing run-queue");
erts_port_task_sched_lock(&pp->sched);
flags = erts_atomic32_read_bor_relb(&pp->sched.flags,
ERTS_PTS_FLG_EXIT);
@@ -1805,7 +1798,7 @@ erts_port_task_execute(ErtsRunQueue *runq, Port **curr_port_pp)
erts_unblock_fpe(fpe_was_unmasked);
ERTS_MSACC_POP_STATE_M();
- ASSERT(runq == (ErtsRunQueue *) erts_atomic_read_nob(&pp->run_queue));
+ ASSERT(runq == erts_get_runq_port(pp));
active = finalize_exec(pp, &execq, processing_busy_q);
@@ -1831,11 +1824,10 @@ erts_port_task_execute(ErtsRunQueue *runq, Port **curr_port_pp)
}
else {
/* Emigrate port... */
- erts_atomic_set_nob(&pp->run_queue, (erts_aint_t) xrunq);
+ erts_set_runq_port(pp, xrunq);
erts_runq_unlock(runq);
xrunq = erts_port_runq(pp);
- ASSERT(xrunq);
enqueue_port(xrunq, pp);
erts_runq_unlock(xrunq);
erts_notify_inc_runq(xrunq);
@@ -2069,7 +2061,7 @@ void
erts_enqueue_port(ErtsRunQueue *rq, Port *pp)
{
ERTS_LC_ASSERT(erts_lc_runq_is_locked(rq));
- ASSERT(rq == (ErtsRunQueue *) erts_atomic_read_nob(&pp->run_queue));
+ ASSERT(rq == erts_get_runq_port(pp));
ASSERT(erts_atomic32_read_nob(&pp->sched.flags) & ERTS_PTS_FLG_IN_RUNQ);
enqueue_port(rq, pp);
}
@@ -2080,8 +2072,7 @@ erts_dequeue_port(ErtsRunQueue *rq)
Port *pp;
ERTS_LC_ASSERT(erts_lc_runq_is_locked(rq));
pp = pop_port(rq);
- ASSERT(!pp
- || rq == (ErtsRunQueue *) erts_atomic_read_nob(&pp->run_queue));
+ ASSERT(!pp || rq == erts_get_runq_port(pp));
ASSERT(!pp || (erts_atomic32_read_nob(&pp->sched.flags)
& ERTS_PTS_FLG_IN_RUNQ));
return pp;
diff --git a/erts/emulator/beam/erl_proc_sig_queue.c b/erts/emulator/beam/erl_proc_sig_queue.c
new file mode 100644
index 0000000000..b4759967a7
--- /dev/null
+++ b/erts/emulator/beam/erl_proc_sig_queue.c
@@ -0,0 +1,3925 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB 2018. 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%
+ */
+
+/*
+ * Description: Process signal queue implementation.
+ *
+ * Author: Rickard Green
+ */
+
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include "sys.h"
+#include "global.h"
+#include "dist.h"
+#include "erl_process.h"
+#include "erl_port_task.h"
+#include "erl_trace.h"
+#include "beam_bp.h"
+#include "big.h"
+#include "erl_gc.h"
+#include "bif.h"
+#include "erl_proc_sig_queue.h"
+
+#define ERTS_SIG_REDS_CNT_FACTOR 4
+#define ERTS_PROC_SIG_TRACE_COUNT_LIMIT 200
+
+/*
+ * Note that not all signal are handled using this functionality!
+ */
+
+#define ERTS_SIG_Q_OP_MAX 10
+
+#define ERTS_SIG_Q_OP_EXIT 0
+#define ERTS_SIG_Q_OP_EXIT_LINKED 1
+#define ERTS_SIG_Q_OP_MONITOR_DOWN 2
+#define ERTS_SIG_Q_OP_MONITOR 3
+#define ERTS_SIG_Q_OP_DEMONITOR 4
+#define ERTS_SIG_Q_OP_LINK 5
+#define ERTS_SIG_Q_OP_UNLINK 6
+#define ERTS_SIG_Q_OP_GROUP_LEADER 7
+#define ERTS_SIG_Q_OP_TRACE_CHANGE_STATE 8
+#define ERTS_SIG_Q_OP_PERSISTENT_MON_MSG 9
+#define ERTS_SIG_Q_OP_IS_ALIVE ERTS_SIG_Q_OP_MAX
+
+#define ERTS_SIG_Q_TYPE_MAX (ERTS_MON_LNK_TYPE_MAX + 5)
+
+#define ERTS_SIG_Q_TYPE_UNDEFINED \
+ (ERTS_MON_LNK_TYPE_MAX + 1)
+#define ERTS_SIG_Q_TYPE_DIST_LINK \
+ (ERTS_MON_LNK_TYPE_MAX + 2)
+#define ERTS_SIG_Q_TYPE_GEN_EXIT \
+ (ERTS_MON_LNK_TYPE_MAX + 3)
+#define ERTS_SIG_Q_TYPE_DIST_PROC_DEMONITOR \
+ (ERTS_MON_LNK_TYPE_MAX + 4)
+#define ERTS_SIG_Q_TYPE_ADJUST_TRACE_INFO \
+ ERTS_SIG_Q_TYPE_MAX
+
+
+#define ERTS_SIG_Q_OP_BITS 8
+#define ERTS_SIG_Q_OP_SHIFT 0
+#define ERTS_SIG_Q_OP_MASK ((1 << ERTS_SIG_Q_OP_BITS) - 1)
+
+#define ERTS_SIG_Q_TYPE_BITS 8
+#define ERTS_SIG_Q_TYPE_SHIFT ERTS_SIG_Q_OP_BITS
+#define ERTS_SIG_Q_TYPE_MASK ((1 << ERTS_SIG_Q_TYPE_BITS) - 1)
+
+#define ERTS_SIG_Q_NON_X_BITS__ (_HEADER_ARITY_OFFS \
+ + ERTS_SIG_Q_OP_BITS \
+ + ERTS_SIG_Q_TYPE_BITS)
+
+#define ERTS_SIG_Q_XTRA_BITS (32 - ERTS_SIG_Q_NON_X_BITS__)
+#define ERTS_SIG_Q_XTRA_SHIFT (ERTS_SIG_Q_OP_BITS \
+ + ERTS_SIG_Q_TYPE_BITS)
+#define ERTS_SIG_Q_XTRA_MASK ((1 << ERTS_SIG_Q_XTRA_BITS) - 1)
+
+#define ERTS_PROC_SIG_OP(Tag) \
+ ((int) (_unchecked_thing_arityval((Tag)) \
+ >> ERTS_SIG_Q_OP_SHIFT) & ERTS_SIG_Q_OP_MASK)
+
+#define ERTS_PROC_SIG_TYPE(Tag) \
+ ((Uint16) (_unchecked_thing_arityval((Tag)) \
+ >> ERTS_SIG_Q_TYPE_SHIFT) & ERTS_SIG_Q_TYPE_MASK)
+
+#define ERTS_PROC_SIG_XTRA(Tag) \
+ ((Uint32) (_unchecked_thing_arityval((Tag)) \
+ >> ERTS_SIG_Q_XTRA_SHIFT) & ERTS_SIG_Q_XTRA_MASK)
+
+#define ERTS_PROC_SIG_MAKE_TAG(Op, Type, Xtra) \
+ (ASSERT(0 <= (Xtra) && (Xtra) <= ERTS_SIG_Q_XTRA_MASK), \
+ _make_header((((Type) & ERTS_SIG_Q_TYPE_MASK) \
+ << ERTS_SIG_Q_TYPE_SHIFT) \
+ | (((Op) & ERTS_SIG_Q_OP_MASK) \
+ << ERTS_SIG_Q_OP_SHIFT) \
+ | (((Xtra) & ERTS_SIG_Q_XTRA_MASK) \
+ << ERTS_SIG_Q_XTRA_SHIFT), \
+ _TAG_HEADER_EXTERNAL_PID))
+
+Process *ERTS_WRITE_UNLIKELY(erts_dirty_process_signal_handler);
+Process *ERTS_WRITE_UNLIKELY(erts_dirty_process_signal_handler_high);
+Process *ERTS_WRITE_UNLIKELY(erts_dirty_process_signal_handler_max);
+
+void
+erts_proc_sig_queue_init(void)
+{
+ ERTS_CT_ASSERT(ERTS_SIG_Q_OP_MASK >= ERTS_SIG_Q_OP_MAX);
+ ERTS_CT_ASSERT(ERTS_SIG_Q_TYPE_MASK >= ERTS_SIG_Q_TYPE_MAX);
+}
+
+typedef struct {
+ int active;
+ int procs;
+ struct {
+ int active;
+#if defined(USE_VM_PROBES)
+ int vm_probes;
+ char receiver_name[DTRACE_TERM_BUF_SIZE];
+#endif
+ int receive_trace;
+ int bp_ix;
+ ErtsMessage **next;
+ ErtsTracingEvent *event;
+ } messages;
+} ErtsSigRecvTracing;
+
+typedef struct {
+ Eterm message;
+ Eterm from;
+ Eterm reason;
+ union {
+ Eterm ref;
+ int normal_kills;
+ } u;
+} ErtsExitSignalData;
+
+typedef struct {
+ Eterm message;
+ Eterm key;
+} ErtsPersistMonMsg;
+
+typedef struct {
+ ErtsSignalCommon common;
+ Eterm local; /* internal pid (immediate) */
+ Eterm remote; /* external pid (heap for it follow) */
+ Eterm heap[EXTERNAL_THING_HEAD_SIZE + 1];
+} ErtsSigDistLinkOp;
+
+typedef struct {
+ ErtsSignalCommon common;
+ Uint flags_on;
+ Uint flags_off;
+ Eterm tracer;
+} ErtsSigTraceInfo;
+
+#define ERTS_SIG_GL_FLG_ACTIVE (((erts_aint_t) 1) << 0)
+#define ERTS_SIG_GL_FLG_RECEIVER (((erts_aint_t) 1) << 1)
+#define ERTS_SIG_GL_FLG_SENDER (((erts_aint_t) 1) << 2)
+
+typedef struct {
+ ErtsSignalCommon common;
+ erts_atomic_t flags;
+ Eterm group_leader;
+ Eterm reply_to;
+ Eterm ref;
+ ErlOffHeap oh;
+ Eterm heap[1];
+} ErtsSigGroupLeader;
+
+typedef struct {
+ Eterm message;
+ Eterm requester;
+} ErtsIsAliveRequest;
+
+static int handle_msg_tracing(Process *c_p,
+ ErtsSigRecvTracing *tracing,
+ ErtsMessage ***next_nm_sig);
+static int handle_trace_change_state(Process *c_p,
+ ErtsSigRecvTracing *tracing,
+ Uint16 type,
+ ErtsMessage *sig,
+ ErtsMessage ***next_nm_sig);
+static void getting_unlinked(Process *c_p, Eterm unlinker);
+static void getting_linked(Process *c_p, Eterm linker);
+static void group_leader_reply(Process *c_p, Eterm to,
+ Eterm ref, int success);
+static int stretch_limit(Process *c_p, ErtsSigRecvTracing *tp,
+ int abs_lim, int *limp);
+
+#ifdef ERTS_PROC_SIG_HARD_DEBUG
+#define ERTS_PROC_SIG_HDBG_PRIV_CHKQ(P, T, NMN) \
+ do { \
+ ErtsMessage **nm_next__ = *(NMN); \
+ ErtsMessage **nm_last__ = (P)->sig_qs.nmsigs.last; \
+ if (!nm_next__ || !*nm_next__) { \
+ nm_next__ = NULL; \
+ nm_last__ = NULL; \
+ } \
+ proc_sig_hdbg_check_queue((P), \
+ 1, \
+ &(P)->sig_qs.cont, \
+ (P)->sig_qs.cont_last, \
+ nm_next__, \
+ nm_last__, \
+ (T), \
+ NULL, \
+ ERTS_PSFLG_FREE); \
+ } while (0);
+static Sint
+proc_sig_hdbg_check_queue(Process *c_p,
+ int privq,
+ ErtsMessage **sig_next,
+ ErtsMessage **sig_last,
+ ErtsMessage **sig_nm_next,
+ ErtsMessage **sig_nm_last,
+ ErtsSigRecvTracing *tracing,
+ int *found_saved_last_p,
+ erts_aint32_t sig_psflg);
+#else
+#define ERTS_PROC_SIG_HDBG_PRIV_CHKQ(P, T, NMN)
+#endif
+
+typedef struct {
+ ErtsSignalCommon common;
+ Eterm ref;
+ Eterm heap[1];
+} ErtsSigDistProcDemonitor;
+
+static void
+destroy_dist_proc_demonitor(ErtsSigDistProcDemonitor *dmon)
+{
+ Eterm ref = dmon->ref;
+ if (is_external(ref)) {
+ ExternalThing *etp = external_thing_ptr(ref);
+ erts_deref_node_entry(etp->node);
+ }
+ erts_free(ERTS_ALC_T_DIST_DEMONITOR, dmon);
+}
+
+static ERTS_INLINE ErtsSigDistLinkOp *
+make_sig_dist_link_op(int op, Eterm local, Eterm remote)
+{
+ Eterm *hp;
+ ErlOffHeap oh = {0};
+ ErtsSigDistLinkOp *sdlnk = erts_alloc(ERTS_ALC_T_SIG_DATA,
+ sizeof(ErtsSigDistLinkOp));
+ ASSERT(is_internal_pid(local));
+ ASSERT(is_external_pid(remote));
+
+ hp = &sdlnk->heap[0];
+
+ sdlnk->common.tag = ERTS_PROC_SIG_MAKE_TAG(op,
+ ERTS_SIG_Q_TYPE_DIST_LINK,
+ 0);
+ sdlnk->local = local;
+ sdlnk->remote = STORE_NC(&hp, &oh, remote);
+
+ ASSERT(&sdlnk->heap[0] < hp);
+ ASSERT(hp <= &sdlnk->heap[0] + sizeof(sdlnk->heap)/sizeof(sdlnk->heap[0]));
+ ASSERT(boxed_val(sdlnk->remote) == &sdlnk->heap[0]);
+
+ return sdlnk;
+}
+
+static ERTS_INLINE void
+destroy_sig_dist_link_op(ErtsSigDistLinkOp *sdlnk)
+{
+ ASSERT(is_external_pid(sdlnk->remote));
+ ASSERT(boxed_val(sdlnk->remote) == &sdlnk->heap[0]);
+ erts_deref_node_entry(((ExternalThing *) &sdlnk->heap[0])->node);
+ erts_free(ERTS_ALC_T_SIG_DATA, sdlnk);
+}
+
+static ERTS_INLINE ErtsExitSignalData *
+get_exit_signal_data(ErtsMessage *xsig)
+{
+ ASSERT(ERTS_SIG_IS_NON_MSG(xsig));
+ ASSERT((ERTS_PROC_SIG_OP(((ErtsSignal *) xsig)->common.tag)
+ == ERTS_SIG_Q_OP_EXIT)
+ || (ERTS_PROC_SIG_OP(((ErtsSignal *) xsig)->common.tag)
+ == ERTS_SIG_Q_OP_EXIT_LINKED)
+ || (ERTS_PROC_SIG_OP(((ErtsSignal *) xsig)->common.tag)
+ == ERTS_SIG_Q_OP_MONITOR_DOWN));
+ ASSERT(xsig->hfrag.alloc_size > xsig->hfrag.used_size);
+ ASSERT((xsig->hfrag.alloc_size - xsig->hfrag.used_size)*sizeof(UWord)
+ >= sizeof(ErtsExitSignalData));
+ return (ErtsExitSignalData *) (char *) (&xsig->hfrag.mem[0]
+ + xsig->hfrag.used_size);
+}
+
+static ERTS_INLINE void
+destroy_trace_info(ErtsSigTraceInfo *ti)
+{
+ if (is_value(ti->tracer))
+ erts_tracer_update(&ti->tracer, NIL);
+ erts_free(ERTS_ALC_T_SIG_DATA, ti);
+}
+
+static void
+destroy_sig_group_leader(ErtsSigGroupLeader *sgl)
+{
+ erts_cleanup_offheap(&sgl->oh);
+ erts_free(ERTS_ALC_T_SIG_DATA, sgl);
+}
+
+static ERTS_INLINE void
+sig_enqueue_trace(Process *c_p, ErtsMessage *sig, int op,
+ Process *rp, ErtsMessage **first,
+ ErtsMessage **last, ErtsMessage ***last_next)
+{
+ switch (op) {
+ case ERTS_SIG_Q_OP_LINK:
+ if (c_p
+ && ((!!IS_TRACED(c_p))
+ & (ERTS_TRACE_FLAGS(c_p) & (F_TRACE_SOL
+ | F_TRACE_SOL1)))) {
+ ErtsSigTraceInfo *ti;
+ Eterm tag;
+ /*
+ * Set on link enabled.
+ *
+ * Prepend a trace-change-state signal before the
+ * link signal...
+ */
+
+ tag = ERTS_PROC_SIG_MAKE_TAG(ERTS_SIG_Q_OP_TRACE_CHANGE_STATE,
+ ERTS_SIG_Q_TYPE_ADJUST_TRACE_INFO,
+ 0);
+ ti = erts_alloc(ERTS_ALC_T_SIG_DATA, sizeof(ErtsSigTraceInfo));
+ ti->common.next = *last;
+ ti->common.specific.next = &ti->common.next;
+ ti->common.tag = tag;
+ ti->flags_on = ERTS_TRACE_FLAGS(c_p) & TRACEE_FLAGS;
+ if (!(ti->flags_on & F_TRACE_SOL1))
+ ti->flags_off = 0;
+ else {
+ ti->flags_off = F_TRACE_SOL1|F_TRACE_SOL;
+ erts_proc_lock(c_p, ERTS_PROC_LOCKS_ALL_MINOR);
+ ERTS_TRACE_FLAGS(c_p) &= ~(F_TRACE_SOL1|F_TRACE_SOL);
+ erts_proc_unlock(c_p, ERTS_PROC_LOCKS_ALL_MINOR);
+ }
+ erts_tracer_update(&ti->tracer, ERTS_TRACER(c_p));
+ *first = (ErtsMessage *) ti;
+ *last_next = &ti->common.next;
+ }
+ break;
+
+#ifdef USE_VM_PROBES
+ case ERTS_SIG_Q_OP_EXIT:
+ case ERTS_SIG_Q_OP_EXIT_LINKED:
+
+ if (DTRACE_ENABLED(process_exit_signal)) {
+ Uint16 type = ERTS_PROC_SIG_TYPE(((ErtsSignal *) sig)->common.tag);
+ Eterm reason, from;
+
+ if (type == ERTS_SIG_Q_TYPE_GEN_EXIT) {
+ ErtsExitSignalData *xsigd = get_exit_signal_data(sig);
+ reason = xsigd->reason;
+ from = xsigd->from;
+ }
+ else {
+ ErtsLink *lnk = (ErtsLink *) sig, *olnk;
+
+ ASSERT(type == ERTS_LNK_TYPE_PROC
+ || type == ERTS_LNK_TYPE_PORT
+ || type == ERTS_LNK_TYPE_DIST_PROC);
+
+ olnk = erts_link_to_other(lnk, NULL);
+ reason = lnk->other.item;
+ from = olnk->other.item;
+ }
+
+ if (is_pid(from)) {
+
+ DTRACE_CHARBUF(sender_str, DTRACE_TERM_BUF_SIZE);
+ DTRACE_CHARBUF(receiver_str, DTRACE_TERM_BUF_SIZE);
+ DTRACE_CHARBUF(reason_buf, DTRACE_TERM_BUF_SIZE);
+
+ if (reason == am_kill) {
+ reason = am_killed;
+ }
+
+ dtrace_pid_str(from, sender_str);
+ dtrace_proc_str(rp, receiver_str);
+ erts_snprintf(reason_buf, sizeof(DTRACE_CHARBUF_NAME(reason_buf)) - 1, "%T", reason);
+ DTRACE3(process_exit_signal, sender_str, receiver_str, reason_buf);
+ }
+ }
+ break;
+
+#endif
+
+ default:
+ break;
+ }
+}
+
+static void
+sig_enqueue_trace_cleanup(ErtsMessage *first, ErtsSignal *sig, ErtsMessage *last)
+{
+ ErtsMessage *tmp;
+
+ /* The usual case; no tracing signals... */
+ if (sig == (ErtsSignal *) first && sig == (ErtsSignal *) last) {
+ sig->common.next = NULL;
+ return;
+ }
+
+ /* Got trace signals to clean up... */
+
+ tmp = first;
+
+ while (tmp) {
+ ErtsMessage *tmp_free = tmp;
+ tmp = tmp->next;
+ if (sig != (ErtsSignal *) tmp_free) {
+ switch (ERTS_PROC_SIG_OP(((ErtsSignal *) tmp_free)->common.tag)) {
+ case ERTS_SIG_Q_OP_TRACE_CHANGE_STATE:
+ destroy_trace_info((ErtsSigTraceInfo *) tmp_free);
+ break;
+ default:
+ ERTS_INTERNAL_ERROR("Unexpected signal op");
+ break;
+ }
+ }
+ }
+}
+
+static ERTS_INLINE erts_aint32_t
+enqueue_signals(Process *rp, ErtsMessage *first,
+ ErtsMessage *last, ErtsMessage **last_next,
+ erts_aint32_t in_state)
+{
+ erts_aint32_t state = in_state;
+ ErtsMessage **this = rp->sig_inq.last;
+
+ ERTS_HDBG_CHECK_SIGNAL_IN_QUEUE(rp);
+
+ ASSERT(!*this);
+ *this = first;
+ rp->sig_inq.last = &last->next;
+
+ if (!rp->sig_inq.nmsigs.next) {
+ ASSERT(!rp->sig_inq.nmsigs.last);
+ rp->sig_inq.nmsigs.next = this;
+ state = erts_atomic32_read_bor_nob(&rp->state,
+ ERTS_PSFLG_SIG_IN_Q);
+ ASSERT(!(state & ERTS_PSFLG_SIG_IN_Q));
+ }
+ else {
+ ErtsSignal *sig;
+ ASSERT(rp->sig_inq.nmsigs.last);
+
+ sig = (ErtsSignal *) *rp->sig_inq.nmsigs.last;
+
+ ASSERT(sig && !sig->common.specific.next);
+ ASSERT(state & ERTS_PSFLG_SIG_IN_Q);
+ sig->common.specific.next = this;
+ }
+
+ if (last_next) {
+ ASSERT(first != last);
+ rp->sig_inq.nmsigs.last = last_next;
+ }
+ else {
+ ASSERT(first == last);
+ rp->sig_inq.nmsigs.last = this;
+ }
+
+ ERTS_HDBG_CHECK_SIGNAL_IN_QUEUE(rp);
+
+ return state;
+}
+
+static ERTS_INLINE void
+ensure_dirty_proc_handled(Eterm pid,
+ erts_aint32_t state,
+ erts_aint32_t prio)
+{
+ if (state & (ERTS_PSFLG_DIRTY_RUNNING
+ | ERTS_PSFLG_DIRTY_RUNNING_SYS)) {
+ Eterm *hp;
+ ErtsMessage *mp;
+ Process *sig_handler;
+
+ if (prio < 0)
+ prio = (int) ERTS_PSFLGS_GET_USR_PRIO(state);
+
+ switch (prio) {
+ case PRIORITY_MAX:
+ sig_handler = erts_dirty_process_signal_handler_max;
+ break;
+ case PRIORITY_HIGH:
+ sig_handler = erts_dirty_process_signal_handler_high;
+ break;
+ default:
+ sig_handler = erts_dirty_process_signal_handler;
+ break;
+ }
+
+ /* Make sure signals are handled... */
+ mp = erts_alloc_message(0, &hp);
+ erts_queue_message(sig_handler, 0, mp, pid, am_system);
+ }
+}
+
+static int
+proc_queue_signal(Process *c_p, Eterm pid, ErtsSignal *sig, int op)
+{
+ int res;
+ Process *rp;
+ ErtsMessage *first, *last, **last_next;
+ ErtsSchedulerData *esdp = erts_get_scheduler_data();
+ int is_normal_sched = !!esdp && esdp->type == ERTS_SCHED_NORMAL;
+ erts_aint32_t state;
+
+ if (is_normal_sched)
+ rp = erts_proc_lookup_raw(pid);
+ else
+ rp = erts_proc_lookup_raw_inc_refc(pid);
+
+ if (!rp)
+ return 0;
+
+ sig->common.specific.next = NULL;
+ first = last = (ErtsMessage *) sig;
+ last_next = NULL;
+
+ /* may add signals before and/or after sig */
+ sig_enqueue_trace(c_p, first, op, rp,
+ &first, &last, &last_next);
+
+ last->next = NULL;
+
+ erts_proc_lock(rp, ERTS_PROC_LOCK_MSGQ);
+
+ state = erts_atomic32_read_nob(&rp->state);
+
+ if (ERTS_PSFLG_FREE & state)
+ res = 0;
+ else {
+ state = enqueue_signals(rp, first, last, last_next, state);
+ res = !0;
+ }
+
+ erts_proc_unlock(rp, ERTS_PROC_LOCK_MSGQ);
+
+ if (res == 0)
+ sig_enqueue_trace_cleanup(first, sig, last);
+
+ if (!(state & (ERTS_PSFLG_EXITING
+ | ERTS_PSFLG_ACTIVE_SYS
+ | ERTS_PSFLG_SIG_IN_Q))) {
+ /* Schedule process... */
+ state = erts_proc_sys_schedule(rp, state, 0);
+ }
+
+ ensure_dirty_proc_handled(rp->common.id, state, -1);
+
+ if (!is_normal_sched)
+ erts_proc_dec_refc(rp);
+
+ return res;
+}
+
+static int
+maybe_elevate_sig_handling_prio(Process *c_p, Eterm other)
+{
+ /*
+ * returns:
+ * > 0 -> elevated prio; process alive or exiting
+ * < 0 -> no elevation needed; process alive or exiting
+ * 0 -> process terminated (free)
+ */
+ int res;
+ Process *rp;
+ erts_aint32_t state, my_prio, other_prio;
+
+ rp = erts_proc_lookup_raw(other);
+ if (!rp)
+ res = 0;
+ else {
+ res = -1;
+ state = erts_atomic32_read_nob(&c_p->state);
+ my_prio = ERTS_PSFLGS_GET_USR_PRIO(state);
+
+ state = erts_atomic32_read_nob(&rp->state);
+ other_prio = ERTS_PSFLGS_GET_USR_PRIO(state);
+
+ if (other_prio > my_prio) {
+ /* Others prio is lower than mine; elevate it... */
+ res = !!erts_sig_prio(other, my_prio);
+ if (res) {
+ /* ensure handled if dirty executing... */
+ state = erts_atomic32_read_nob(&rp->state);
+ ensure_dirty_proc_handled(other, state, my_prio);
+ }
+ }
+ }
+ return res;
+}
+
+void
+erts_proc_sig_fetch(Process *proc)
+{
+#ifdef ERTS_PROC_SIG_HARD_DEBUG
+ ErtsSignalPrivQueues sig_qs = proc->sig_qs;
+ ErtsSignalInQueue sig_inq = proc->sig_inq;
+#endif
+
+ ERTS_LC_ASSERT(erts_thr_progress_is_blocking()
+ || ERTS_PROC_IS_EXITING(proc)
+ || ((erts_proc_lc_my_proc_locks(proc)
+ & (ERTS_PROC_LOCK_MAIN
+ | ERTS_PROC_LOCK_MSGQ))
+ == (ERTS_PROC_LOCK_MAIN
+ | ERTS_PROC_LOCK_MSGQ)));
+
+ if (!proc->sig_inq.first) {
+ ASSERT(proc->sig_inq.last == &proc->sig_inq.first);
+ ASSERT(proc->sig_inq.len == 0);
+ ASSERT(!proc->sig_inq.nmsigs.next);
+ ASSERT(!proc->sig_inq.nmsigs.last);
+ return;
+ }
+
+ ERTS_HDBG_CHECK_SIGNAL_IN_QUEUE(proc);
+ ERTS_HDBG_CHECK_SIGNAL_PRIV_QUEUE(proc);
+
+ if (!proc->sig_inq.nmsigs.next) {
+ ASSERT(!(ERTS_PSFLG_SIG_IN_Q
+ & erts_atomic32_read_nob(&proc->state)));
+ ASSERT(!proc->sig_inq.nmsigs.last);
+
+ if (proc->sig_qs.cont || ERTS_MSG_RECV_TRACED(proc)) {
+ *proc->sig_qs.cont_last = proc->sig_inq.first;
+ proc->sig_qs.cont_last = proc->sig_inq.last;
+ }
+ else {
+ *proc->sig_qs.last = proc->sig_inq.first;
+ proc->sig_qs.last = proc->sig_inq.last;
+ }
+ }
+ else {
+#ifdef DEBUG
+ erts_aint32_t s;
+#endif
+ ASSERT(proc->sig_inq.nmsigs.last);
+ if (!proc->sig_qs.nmsigs.last) {
+ ASSERT(!proc->sig_qs.nmsigs.next);
+ if (proc->sig_inq.nmsigs.next == &proc->sig_inq.first)
+ proc->sig_qs.nmsigs.next = proc->sig_qs.cont_last;
+ else
+ proc->sig_qs.nmsigs.next = proc->sig_inq.nmsigs.next;
+
+#ifdef DEBUG
+ s =
+#endif
+ erts_atomic32_read_bset_nob(&proc->state,
+ (ERTS_PSFLG_SIG_Q
+ | ERTS_PSFLG_SIG_IN_Q),
+ ERTS_PSFLG_SIG_Q);
+
+ ASSERT((s & (ERTS_PSFLG_SIG_Q|ERTS_PSFLG_SIG_IN_Q))
+ == ERTS_PSFLG_SIG_IN_Q);
+ }
+ else {
+ ErtsSignal *sig;
+ ASSERT(proc->sig_qs.nmsigs.next);
+ sig = ((ErtsSignal *) *proc->sig_qs.nmsigs.last);
+ ASSERT(ERTS_SIG_IS_NON_MSG(sig));
+ ASSERT(!sig->common.specific.next);
+ if (proc->sig_inq.nmsigs.next == &proc->sig_inq.first)
+ sig->common.specific.next = proc->sig_qs.cont_last;
+ else
+ sig->common.specific.next = proc->sig_inq.nmsigs.next;
+
+#ifdef DEBUG
+ s =
+#endif
+ erts_atomic32_read_band_nob(&proc->state,
+ ~ERTS_PSFLG_SIG_IN_Q);
+
+ ASSERT((s & (ERTS_PSFLG_SIG_Q|ERTS_PSFLG_SIG_IN_Q))
+ == (ERTS_PSFLG_SIG_Q|ERTS_PSFLG_SIG_IN_Q));
+ }
+ if (proc->sig_inq.nmsigs.last == &proc->sig_inq.first)
+ proc->sig_qs.nmsigs.last = proc->sig_qs.cont_last;
+ else
+ proc->sig_qs.nmsigs.last = proc->sig_inq.nmsigs.last;
+ proc->sig_inq.nmsigs.next = NULL;
+ proc->sig_inq.nmsigs.last = NULL;
+
+ *proc->sig_qs.cont_last = proc->sig_inq.first;
+ proc->sig_qs.cont_last = proc->sig_inq.last;
+ }
+
+ proc->sig_qs.len += proc->sig_inq.len;
+
+ proc->sig_inq.first = NULL;
+ proc->sig_inq.last = &proc->sig_inq.first;
+ proc->sig_inq.len = 0;
+
+ ERTS_HDBG_CHECK_SIGNAL_PRIV_QUEUE(proc);
+}
+
+void do_seq_trace_output(Eterm to, Eterm token, Eterm msg);
+
+static void
+send_gen_exit_signal(Process *c_p, Eterm from_tag,
+ Eterm from, Eterm to,
+ Sint16 op, Eterm reason, Eterm ref,
+ Eterm token, int normal_kills)
+{
+ ErtsExitSignalData *xsigd;
+ Eterm *hp, *start_hp, s_reason, s_ref, s_message, s_token, s_from;
+ ErtsMessage *mp;
+ ErlHeapFragment *hfrag;
+ ErlOffHeap *ohp;
+ Uint hsz, from_sz, reason_sz, ref_sz, token_sz;
+ int seq_trace;
+#ifdef USE_VM_PROBES
+ Eterm s_utag, utag;
+ Uint utag_sz;
+#endif
+
+ ASSERT(is_immed(from_tag));
+
+ hsz = sizeof(ErtsExitSignalData)/sizeof(Uint);
+
+ seq_trace = c_p && have_seqtrace(token);
+ if (seq_trace)
+ seq_trace_update_send(c_p);
+
+#ifdef USE_VM_PROBES
+ if (c_p && token != NIL && (DT_UTAG_FLAGS(c_p) & DT_UTAG_SPREADING)) {
+ utag_sz = size_object(DT_UTAG(c_p));
+ utag = DT_UTAG(c_p);
+ }
+ else if (token == am_have_dt_utag) {
+ utag_sz = 0;
+ utag = token = NIL;
+ }
+ hsz += utag_sz;
+#endif
+
+ token_sz = is_immed(token) ? 0 : size_object(token);
+ hsz += token_sz;
+
+ from_sz = is_immed(from) ? 0 : size_object(from);
+ hsz += from_sz;
+
+ reason_sz = is_immed(reason) ? 0 : size_object(reason);
+ hsz += reason_sz;
+
+ switch (op) {
+ case ERTS_SIG_Q_OP_EXIT:
+ case ERTS_SIG_Q_OP_EXIT_LINKED: {
+ /* {'EXIT', From, Reason} */
+ hsz += 4; /* 3-tuple */
+ ref_sz = 0;
+ break;
+ }
+ case ERTS_SIG_Q_OP_MONITOR_DOWN: {
+ /* {'DOWN', Ref, process, From, Reason} */
+ hsz += 6; /* 5-tuple */
+ ref_sz = NC_HEAP_SIZE(ref);
+ hsz += ref_sz;
+ break;
+ }
+ default:
+ ERTS_INTERNAL_ERROR("Invalid exit signal op");
+ break;
+ }
+
+ /*
+ * Allocate message combined with heap fragment...
+ */
+ mp = erts_alloc_message(hsz, &hp);
+ hfrag = &mp->hfrag;
+ mp->next = NULL;
+ ohp = &hfrag->off_heap;
+ start_hp = hp;
+
+ s_token = (is_immed(token)
+ ? token
+ : copy_struct(token, token_sz, &hp, ohp));
+
+ s_reason = (is_immed(reason)
+ ? reason
+ : copy_struct(reason, reason_sz, &hp, ohp));
+
+ s_from = (is_immed(from)
+ ? from
+ : copy_struct(from, from_sz, &hp, ohp));
+
+ if (!ref_sz)
+ s_ref = NIL;
+ else
+ s_ref = STORE_NC(&hp, ohp, ref);
+
+ switch (op) {
+ case ERTS_SIG_Q_OP_EXIT:
+ case ERTS_SIG_Q_OP_EXIT_LINKED:
+ /* {'EXIT', From, Reason} */
+ s_message = TUPLE3(hp, am_EXIT, s_from, s_reason);
+ hp += 4;
+ break;
+ case ERTS_SIG_Q_OP_MONITOR_DOWN:
+ /* {'DOWN', Ref, process, From, Reason} */
+ s_message = TUPLE5(hp, am_DOWN, s_ref, am_process, s_from, s_reason);
+ hp += 6;
+ break;
+ }
+
+#ifdef USE_VM_PROBES
+ s_utag = (is_immed(utag)
+ ? utag
+ : copy_struct(utag, utag_sz, &hp, ohp));
+ ERL_MESSAGE_DT_UTAG(mp) = s_utag;
+#endif
+
+ ERL_MESSAGE_TERM(mp) = ERTS_PROC_SIG_MAKE_TAG(op,
+ ERTS_SIG_Q_TYPE_GEN_EXIT,
+ 0);
+ ERL_MESSAGE_TOKEN(mp) = s_token;
+ ERL_MESSAGE_FROM(mp) = from_tag; /* immediate... */
+
+ hfrag->used_size = hp - start_hp;
+
+ xsigd = (ErtsExitSignalData *) (char *) hp;
+
+ xsigd->message = s_message;
+ xsigd->from = s_from;
+ xsigd->reason = s_reason;
+ if (is_nil(s_ref))
+ xsigd->u.normal_kills = normal_kills;
+ else {
+ ASSERT(is_ref(s_ref));
+ xsigd->u.ref = s_ref;
+ }
+
+ if (seq_trace)
+ do_seq_trace_output(to, s_token, s_message);
+
+ if (!proc_queue_signal(c_p, to, (ErtsSignal *) mp, op)) {
+ mp->next = NULL;
+ erts_cleanup_messages(mp);
+ }
+}
+
+void
+do_seq_trace_output(Eterm to, Eterm token, Eterm msg)
+{
+ /*
+ * We could do this when enqueuing the signal and avoid some
+ * locking. However, the enqueuing code would then always
+ * have the penalty of this seq-tracing code which we do not
+ * want...
+ */
+ ErtsSchedulerData *esdp = erts_get_scheduler_data();
+ int is_normal_sched = !!esdp && esdp->type == ERTS_SCHED_NORMAL;
+ Process *rp;
+
+ if (is_normal_sched)
+ rp = erts_proc_lookup_raw(to);
+ else
+ rp = erts_proc_lookup_raw_inc_refc(to);
+
+ erts_proc_lock(rp, ERTS_PROC_LOCK_MSGQ);
+
+ if (!ERTS_PROC_IS_EXITING(rp))
+ seq_trace_output(token, msg, SEQ_TRACE_SEND, to, rp);
+
+ erts_proc_unlock(rp, ERTS_PROC_LOCK_MSGQ);
+
+ if (!is_normal_sched)
+ erts_proc_dec_refc(rp);
+}
+
+void
+erts_proc_sig_send_persistent_monitor_msg(Uint16 type, Eterm key,
+ Eterm from, Eterm to,
+ Eterm msg, Uint msg_sz)
+{
+ ErtsPersistMonMsg *prst_mon;
+ ErtsMessage *mp;
+ ErlHeapFragment *hfrag;
+ Eterm *hp, *start_hp, message;
+ ErlOffHeap *ohp;
+ Uint hsz = sizeof(ErtsPersistMonMsg) + msg_sz;
+
+ /*
+ * Allocate message combined with heap fragment...
+ */
+ mp = erts_alloc_message(hsz, &hp);
+ hfrag = &mp->hfrag;
+ mp->next = NULL;
+ ohp = &hfrag->off_heap;
+ start_hp = hp;
+
+ ASSERT(msg_sz == size_object(msg));
+ message = copy_struct(msg, msg_sz, &hp, ohp);
+ hfrag->used_size = hp - start_hp;
+
+ prst_mon = (ErtsPersistMonMsg *) (char *) hp;
+ prst_mon->message = message;
+
+ switch (type) {
+ case ERTS_MON_TYPE_NODES:
+ ASSERT(is_small(key));
+ prst_mon->key = key;
+ break;
+
+ case ERTS_MON_TYPE_TIME_OFFSET:
+ ASSERT(is_internal_ref(key));
+ ASSERT(is_tuple_arity(message, 5));
+
+ prst_mon->key = tuple_val(message)[2];
+
+ ASSERT(eq(prst_mon->key, key));
+ break;
+
+ default:
+ ERTS_INTERNAL_ERROR("Invalid persistent monitor type");
+ prst_mon->key = key;
+ break;
+ }
+
+ ASSERT(is_immed(from));
+
+ ERL_MESSAGE_TERM(mp) = ERTS_PROC_SIG_MAKE_TAG(ERTS_SIG_Q_OP_PERSISTENT_MON_MSG,
+ type, 0);
+ ERL_MESSAGE_FROM(mp) = from;
+ ERL_MESSAGE_TOKEN(mp) = NIL;
+#ifdef USE_VM_PROBES
+ ERL_MESSAGE_DT_UTAG(mp) = NIL;
+#endif
+
+ if (!proc_queue_signal(NULL, to, (ErtsSignal *) mp,
+ ERTS_SIG_Q_OP_PERSISTENT_MON_MSG)) {
+ mp->next = NULL;
+ erts_cleanup_messages(mp);
+ }
+}
+
+static ERTS_INLINE Eterm
+get_persist_mon_msg(ErtsMessage *sig, Eterm *msg)
+{
+ ErtsPersistMonMsg *prst_mon;
+ prst_mon = ((ErtsPersistMonMsg *)
+ (char *) (&sig->hfrag.mem[0]
+ + sig->hfrag.used_size));
+ *msg = prst_mon->message;
+ return prst_mon->key;
+}
+
+void
+erts_proc_sig_send_exit(Process *c_p, Eterm from, Eterm to,
+ Eterm reason, Eterm token,
+ int normal_kills)
+{
+ Eterm from_tag;
+ ASSERT(!c_p || c_p->common.id == from);
+ if (is_immed(from)) {
+ ASSERT(is_internal_pid(from) || is_internal_port(from));
+ from_tag = from;
+ }
+ else {
+ DistEntry *dep;
+ ASSERT(is_external_pid(from));
+ dep = external_pid_dist_entry(from);
+ from_tag = dep->sysname;
+ }
+ send_gen_exit_signal(c_p, from_tag, from, to, ERTS_SIG_Q_OP_EXIT,
+ reason, NIL, token, normal_kills);
+}
+
+void
+erts_proc_sig_send_link_exit(Process *c_p, Eterm from, ErtsLink *lnk,
+ Eterm reason, Eterm token)
+{
+ Eterm to;
+ ASSERT(!c_p || c_p->common.id == from);
+ ASSERT(lnk);
+ to = lnk->other.item;
+ if (is_not_immed(reason) || is_not_nil(token)) {
+ ASSERT(is_internal_pid(from) || is_internal_port(from));
+ send_gen_exit_signal(c_p, from, from, to, ERTS_SIG_Q_OP_EXIT_LINKED,
+ reason, NIL, token, 0);
+ }
+ else {
+ /* Pass signal using old link structure... */
+ ErtsSignal *sig = (ErtsSignal *) lnk;
+ lnk->other.item = reason; /* pass reason via this other.item */
+ sig->common.tag = ERTS_PROC_SIG_MAKE_TAG(ERTS_SIG_Q_OP_EXIT_LINKED,
+ lnk->type, 0);
+ if (proc_queue_signal(c_p, to, sig, ERTS_SIG_Q_OP_EXIT_LINKED))
+ return; /* receiver will destroy lnk structure */
+ }
+ if (lnk)
+ erts_link_release(lnk);
+}
+
+int
+erts_proc_sig_send_link(Process *c_p, Eterm to, ErtsLink *lnk)
+{
+ ErtsSignal *sig;
+ Uint16 type = lnk->type;
+
+ ASSERT(!c_p || c_p->common.id == lnk->other.item);
+ ASSERT(lnk);
+ ASSERT(is_internal_pid(to));
+
+ sig = (ErtsSignal *) lnk;
+ sig->common.tag = ERTS_PROC_SIG_MAKE_TAG(ERTS_SIG_Q_OP_LINK,
+ type, 0);
+
+ return proc_queue_signal(c_p, to, sig, ERTS_SIG_Q_OP_LINK);
+}
+
+void
+erts_proc_sig_send_unlink(Process *c_p, ErtsLink *lnk)
+{
+ ErtsSignal *sig;
+ Eterm to;
+
+ ASSERT(lnk);
+
+ sig = (ErtsSignal *) lnk;
+ to = lnk->other.item;
+
+ ASSERT(is_internal_pid(to));
+
+ sig->common.tag = ERTS_PROC_SIG_MAKE_TAG(ERTS_SIG_Q_OP_UNLINK,
+ lnk->type, 0);
+
+ if (!proc_queue_signal(c_p, to, sig, ERTS_SIG_Q_OP_UNLINK))
+ erts_link_release(lnk);
+}
+
+void
+erts_proc_sig_send_dist_link_exit(DistEntry *dep,
+ Eterm from, Eterm to,
+ Eterm reason, Eterm token)
+{
+ send_gen_exit_signal(NULL, dep->sysname, from, to, ERTS_SIG_Q_OP_EXIT_LINKED,
+ reason, NIL, token, 0);
+}
+
+void
+erts_proc_sig_send_dist_unlink(DistEntry *dep, Eterm from, Eterm to)
+{
+ ErtsSignal *sig;
+
+ ASSERT(is_internal_pid(to));
+ ASSERT(is_external_pid(from));
+ ASSERT(dep == external_pid_dist_entry(from));
+
+ sig = (ErtsSignal *) make_sig_dist_link_op(ERTS_SIG_Q_OP_UNLINK,
+ to, from);
+
+ if (!proc_queue_signal(NULL, to, sig, ERTS_SIG_Q_OP_UNLINK))
+ destroy_sig_dist_link_op((ErtsSigDistLinkOp *) sig);
+}
+
+void
+erts_proc_sig_send_dist_monitor_down(DistEntry *dep, Eterm ref,
+ Eterm from, Eterm to,
+ Eterm reason)
+{
+ Eterm monitored, heap[3];
+ if (is_atom(from))
+ monitored = TUPLE2(&heap[0], from, dep->sysname);
+ else
+ monitored = from;
+ send_gen_exit_signal(NULL, dep->sysname, monitored,
+ to, ERTS_SIG_Q_OP_MONITOR_DOWN,
+ reason, ref, NIL, 0);
+}
+
+void
+erts_proc_sig_send_monitor_down(ErtsMonitor *mon, Eterm reason)
+{
+ Eterm to;
+
+ ASSERT(erts_monitor_is_target(mon));
+ ASSERT(!erts_monitor_is_in_table(mon));
+
+ to = mon->other.item;
+ ASSERT(is_internal_pid(to));
+
+ if (is_immed(reason)) {
+ /* Pass signal using old monitor structure... */
+ ErtsSignal *sig;
+
+ mon->other.item = reason; /* Pass immed reason via other.item... */
+ sig = (ErtsSignal *) mon;
+ sig->common.tag = ERTS_PROC_SIG_MAKE_TAG(ERTS_SIG_Q_OP_MONITOR_DOWN,
+ mon->type, 0);
+ if (proc_queue_signal(NULL, to, sig, ERTS_SIG_Q_OP_MONITOR_DOWN))
+ return; /* receiver will destroy mon structure */
+ }
+ else {
+ ErtsMonitorData *mdp = erts_monitor_to_data(mon);
+ Eterm from_tag, monitored, heap[3];
+
+ if (!(mon->flags & ERTS_ML_FLG_NAME)) {
+ from_tag = monitored = mdp->origin.other.item;
+ if (is_external_pid(from_tag)) {
+ DistEntry *dep = external_pid_dist_entry(from_tag);
+ from_tag = dep->sysname;
+ }
+ }
+ else {
+ ErtsMonitorDataExtended *mdep;
+ Eterm name, node;
+ mdep = (ErtsMonitorDataExtended *) mdp;
+ name = mdep->u.name;
+ ASSERT(is_atom(name));
+ if (mdep->dist) {
+ node = mdep->dist->nodename;
+ from_tag = node;
+ }
+ else {
+ node = erts_this_dist_entry->sysname;
+ from_tag = mdp->origin.other.item;
+ }
+ ASSERT(is_internal_port(from_tag)
+ || is_internal_pid(from_tag)
+ || is_atom(from_tag));
+ monitored = TUPLE2(&heap[0], name, node);
+ }
+ send_gen_exit_signal(NULL, from_tag, monitored,
+ to, ERTS_SIG_Q_OP_MONITOR_DOWN,
+ reason, mdp->ref, NIL, 0);
+ }
+ erts_monitor_release(mon);
+}
+
+void
+erts_proc_sig_send_dist_demonitor(Eterm to, Eterm ref)
+{
+ ErtsSigDistProcDemonitor *dmon;
+ ErtsSignal *sig;
+ Eterm *hp;
+ ErlOffHeap oh;
+ size_t size;
+
+ ERTS_INIT_OFF_HEAP(&oh);
+
+ ASSERT(is_internal_pid(to));
+
+ size = sizeof(ErtsSigDistProcDemonitor) - sizeof(Eterm);
+ ASSERT(is_ref(ref));
+ size += NC_HEAP_SIZE(ref)*sizeof(Eterm);
+
+ dmon = erts_alloc(ERTS_ALC_T_DIST_DEMONITOR, size);
+
+ hp = &dmon->heap[0];
+ dmon->ref = STORE_NC(&hp, &oh, ref);
+ sig = (ErtsSignal *) dmon;
+
+ sig->common.tag = ERTS_PROC_SIG_MAKE_TAG(ERTS_SIG_Q_OP_DEMONITOR,
+ ERTS_SIG_Q_TYPE_DIST_PROC_DEMONITOR,
+ 0);
+
+ if (!proc_queue_signal(NULL, to, sig, ERTS_SIG_Q_OP_DEMONITOR))
+ destroy_dist_proc_demonitor(dmon);
+}
+
+void
+erts_proc_sig_send_demonitor(ErtsMonitor *mon)
+{
+ ErtsSignal *sig = (ErtsSignal *) mon;
+ Uint16 type = mon->type;
+ Eterm to = mon->other.item;
+
+ ASSERT(is_internal_pid(to));
+ ASSERT(erts_monitor_is_origin(mon));
+ ASSERT(!erts_monitor_is_in_table(mon));
+
+ sig->common.tag = ERTS_PROC_SIG_MAKE_TAG(ERTS_SIG_Q_OP_DEMONITOR,
+ type, 0);
+
+ if (!proc_queue_signal(NULL, to, sig, ERTS_SIG_Q_OP_DEMONITOR))
+ erts_monitor_release(mon);
+}
+
+int
+erts_proc_sig_send_monitor(ErtsMonitor *mon, Eterm to)
+{
+ ErtsSignal *sig = (ErtsSignal *) mon;
+ Uint16 type = mon->type;
+
+ ASSERT(is_internal_pid(to) || to == am_undefined);
+ ASSERT(erts_monitor_is_target(mon));
+
+ sig->common.tag = ERTS_PROC_SIG_MAKE_TAG(ERTS_SIG_Q_OP_MONITOR,
+ type, 0);
+
+ return proc_queue_signal(NULL, to, sig, ERTS_SIG_Q_OP_MONITOR);
+}
+
+void
+erts_proc_sig_send_trace_change(Eterm to, Uint on, Uint off, Eterm tracer)
+{
+ ErtsSigTraceInfo *ti;
+ Eterm tag;
+
+ ti = erts_alloc(ERTS_ALC_T_SIG_DATA, sizeof(ErtsSigTraceInfo));
+ tag = ERTS_PROC_SIG_MAKE_TAG(ERTS_SIG_Q_OP_TRACE_CHANGE_STATE,
+ ERTS_SIG_Q_TYPE_ADJUST_TRACE_INFO,
+ 0);
+
+ ti->common.tag = tag;
+ ti->flags_off = off;
+ ti->flags_on = on;
+ ti->tracer = NIL;
+ if (is_not_nil(tracer))
+ erts_tracer_update(&ti->tracer, tracer);
+
+ if (!proc_queue_signal(NULL, to, (ErtsSignal *) ti,
+ ERTS_SIG_Q_OP_TRACE_CHANGE_STATE))
+ destroy_trace_info(ti);
+}
+
+void
+erts_proc_sig_send_group_leader(Process *c_p, Eterm to, Eterm gl, Eterm ref)
+{
+ int res;
+ ErtsSigGroupLeader *sgl;
+ Eterm *hp;
+ Uint gl_sz, ref_sz, size;
+ erts_aint_t init_flags = ERTS_SIG_GL_FLG_ACTIVE|ERTS_SIG_GL_FLG_RECEIVER;
+ if (c_p)
+ init_flags |= ERTS_SIG_GL_FLG_SENDER;
+
+ ASSERT(c_p ? is_internal_ref(ref) : ref == NIL);
+
+ gl_sz = is_immed(gl) ? 0 : size_object(gl);
+ ref_sz = is_immed(ref) ? 0 : size_object(ref);
+
+ size = sizeof(ErtsSigGroupLeader);
+
+ size += (gl_sz + ref_sz - 1) * sizeof(Eterm);
+
+ sgl = erts_alloc(ERTS_ALC_T_SIG_DATA, size);
+
+ erts_atomic_init_nob(&sgl->flags, init_flags);
+
+ ERTS_INIT_OFF_HEAP(&sgl->oh);
+
+ hp = &sgl->heap[0];
+
+ sgl->group_leader = is_immed(gl) ? gl : copy_struct(gl, gl_sz, &hp, &sgl->oh);
+ sgl->reply_to = c_p ? c_p->common.id : NIL;
+ sgl->ref = is_immed(ref) ? ref : copy_struct(ref, ref_sz, &hp, &sgl->oh);
+
+ sgl->common.tag = ERTS_PROC_SIG_MAKE_TAG(ERTS_SIG_Q_OP_GROUP_LEADER,
+ ERTS_SIG_Q_TYPE_UNDEFINED,
+ 0);
+
+ res = proc_queue_signal(c_p, to, (ErtsSignal *) sgl,
+ ERTS_SIG_Q_OP_GROUP_LEADER);
+
+ if (!res)
+ destroy_sig_group_leader(sgl);
+ else if (c_p) {
+ erts_aint_t flags, rm_flags = ERTS_SIG_GL_FLG_SENDER;
+ int prio_res = maybe_elevate_sig_handling_prio(c_p, to);
+ if (!prio_res)
+ rm_flags |= ERTS_SIG_GL_FLG_ACTIVE;
+ flags = erts_atomic_read_band_nob(&sgl->flags, ~rm_flags);
+ if (!prio_res && (flags & ERTS_SIG_GL_FLG_ACTIVE))
+ res = 0; /* We deactivated signal... */
+ if ((flags & ~rm_flags) == 0)
+ destroy_sig_group_leader(sgl);
+ }
+
+ if (!res && c_p)
+ group_leader_reply(c_p, c_p->common.id, ref, 0);
+}
+
+void
+erts_proc_sig_send_is_alive_request(Process *c_p, Eterm to, Eterm ref)
+{
+ ErlHeapFragment *hfrag;
+ Uint hsz;
+ Eterm *hp, *start_hp, ref_cpy, msg;
+ ErlOffHeap *ohp;
+ ErtsMessage *mp;
+ ErtsIsAliveRequest *alive_req;
+
+ ASSERT(is_internal_ordinary_ref(ref));
+
+ hsz = ERTS_REF_THING_SIZE + 3 + sizeof(ErtsIsAliveRequest)/sizeof(Eterm);
+
+ mp = erts_alloc_message(hsz, &hp);
+ hfrag = &mp->hfrag;
+ mp->next = NULL;
+ ohp = &hfrag->off_heap;
+ start_hp = hp;
+
+ ref_cpy = STORE_NC(&hp, ohp, ref);
+ msg = TUPLE2(hp, ref_cpy, am_false); /* default res 'false' */
+ hp += 3;
+
+ hfrag->used_size = hp - start_hp;
+
+ alive_req = (ErtsIsAliveRequest *) (char *) hp;
+ alive_req->message = msg;
+ alive_req->requester = c_p->common.id;
+
+ ERL_MESSAGE_TERM(mp) = ERTS_PROC_SIG_MAKE_TAG(ERTS_SIG_Q_OP_IS_ALIVE,
+ ERTS_SIG_Q_TYPE_UNDEFINED,
+ 0);
+ ERL_MESSAGE_TOKEN(mp) = NIL;
+ ERL_MESSAGE_FROM(mp) = am_system;
+#ifdef USE_VM_PROBES
+ ERL_MESSAGE_DT_UTAG(mp) = NIL;
+#endif
+
+ if (proc_queue_signal(c_p, to, (ErtsSignal *) mp, ERTS_SIG_Q_OP_IS_ALIVE))
+ (void) maybe_elevate_sig_handling_prio(c_p, to);
+ else {
+ /* It wasn't alive; reply to ourselves... */
+ mp->next = NULL;
+ mp->data.attached = ERTS_MSG_COMBINED_HFRAG;
+ erts_queue_message(c_p, ERTS_PROC_LOCK_MAIN,
+ mp, msg, am_system);
+ }
+}
+
+static void
+is_alive_response(Process *c_p, ErtsMessage *mp, int is_alive)
+{
+ /*
+ * Sender prepared the message for us. Just patch
+ * the result if necessary. The default prepared
+ * result is 'false'.
+ */
+ Process *rp;
+ ErtsIsAliveRequest *alive_req;
+
+ alive_req = (ErtsIsAliveRequest *) (char *) (&mp->hfrag.mem[0]
+ + mp->hfrag.used_size);
+
+
+ ASSERT(ERTS_SIG_IS_NON_MSG(mp));
+ ASSERT(ERTS_PROC_SIG_OP(((ErtsSignal *) mp)->common.tag)
+ == ERTS_SIG_Q_OP_IS_ALIVE);
+ ASSERT(mp->hfrag.alloc_size > mp->hfrag.used_size);
+ ASSERT((mp->hfrag.alloc_size - mp->hfrag.used_size)*sizeof(UWord)
+ >= sizeof(ErtsIsAliveRequest));
+ ASSERT(is_internal_pid(alive_req->requester));
+ ASSERT(alive_req->requester != c_p->common.id);
+ ASSERT(is_tuple_arity(alive_req->message, 2));
+ ASSERT(is_internal_ordinary_ref(tuple_val(alive_req->message)[1]));
+ ASSERT(tuple_val(alive_req->message)[2] == am_false);
+
+ ERL_MESSAGE_TERM(mp) = alive_req->message;
+ mp->data.attached = ERTS_MSG_COMBINED_HFRAG;
+ mp->next = NULL;
+
+ rp = erts_proc_lookup(alive_req->requester);
+ if (!rp)
+ erts_cleanup_messages(mp);
+ else {
+ if (is_alive) { /* patch result... */
+ Eterm *tp = tuple_val(alive_req->message);
+ tp[2] = am_true;
+ }
+ erts_queue_message(rp, 0, mp, alive_req->message, am_system);
+ }
+}
+
+static ERTS_INLINE void
+adjust_tracing_state(Process *c_p, ErtsSigRecvTracing *tracing, int setup)
+{
+ if (!IS_TRACED(c_p) || (ERTS_TRACE_FLAGS(c_p) & F_SENSITIVE)) {
+ tracing->messages.active = 0;
+ tracing->messages.receive_trace = 0;
+ tracing->messages.event = NULL;
+ tracing->messages.next = NULL;
+ tracing->procs = 0;
+ tracing->active = 0;
+ }
+ else {
+ Uint flgs = ERTS_TRACE_FLAGS(c_p);
+ int procs_trace = !!(flgs & F_TRACE_PROCS);
+ int recv_trace = !!(flgs & F_TRACE_RECEIVE);
+ /* procs tracing enabled? */
+
+ tracing->procs = procs_trace;
+
+ /* message receive tracing enabled? */
+ tracing->messages.receive_trace = recv_trace;
+ if (!recv_trace)
+ tracing->messages.event = NULL;
+ else {
+ if (tracing->messages.bp_ix < 0)
+ tracing->messages.bp_ix = erts_active_bp_ix();
+ tracing->messages.event = &erts_receive_tracing[tracing->messages.bp_ix];
+ }
+ if (setup) {
+ if (recv_trace)
+ tracing->messages.next = &c_p->sig_qs.cont;
+ else
+ tracing->messages.next = NULL;
+ }
+ tracing->messages.active = recv_trace;
+ tracing->active = recv_trace | procs_trace;
+ }
+
+#if defined(USE_VM_PROBES)
+ /* vm probe message_queued enabled? */
+
+ tracing->messages.vm_probes = DTRACE_ENABLED(message_queued);
+ if (tracing->messages.vm_probes) {
+ dtrace_proc_str(c_p, tracing->messages.receiver_name);
+ tracing->messages.active = !0;
+ tracing->active = !0;
+ if (setup && !tracing->messages.next)
+ tracing->messages.next = &c_p->sig_qs.cont;
+ }
+
+#endif
+}
+
+static ERTS_INLINE void
+setup_tracing_state(Process *c_p, ErtsSigRecvTracing *tracing)
+{
+ tracing->messages.bp_ix = -1;
+ adjust_tracing_state(c_p, tracing, !0);
+}
+
+static ERTS_INLINE void
+remove_iq_sig(Process *c_p, ErtsMessage *sig, ErtsMessage **next_sig)
+{
+ /*
+ * Remove signal from inner queue.
+ */
+ ASSERT(c_p->sig_qs.cont_last != &sig->next);
+ ASSERT(c_p->sig_qs.nmsigs.next != &sig->next);
+ ASSERT(c_p->sig_qs.nmsigs.last != &sig->next);
+
+ if (c_p->sig_qs.save == &sig->next)
+ c_p->sig_qs.save = next_sig;
+ if (c_p->sig_qs.last == &sig->next)
+ c_p->sig_qs.last = next_sig;
+ if (c_p->sig_qs.saved_last == &sig->next)
+ c_p->sig_qs.saved_last = next_sig;
+
+ *next_sig = sig->next;
+}
+
+static ERTS_INLINE void
+remove_mq_sig(Process *c_p, ErtsMessage *sig,
+ ErtsMessage **next_sig, ErtsMessage ***next_nm_sig)
+{
+ /*
+ * Remove signal from middle queue.
+ */
+ ASSERT(c_p->sig_qs.save != &sig->next);
+ ASSERT(c_p->sig_qs.last != &sig->next);
+
+ if (c_p->sig_qs.cont_last == &sig->next)
+ c_p->sig_qs.cont_last = next_sig;
+ if (c_p->sig_qs.saved_last == &sig->next)
+ c_p->sig_qs.saved_last = next_sig;
+ if (*next_nm_sig == &sig->next)
+ *next_nm_sig = next_sig;
+ if (c_p->sig_qs.nmsigs.last == &sig->next)
+ c_p->sig_qs.nmsigs.last = next_sig;
+
+ *next_sig = sig->next;
+}
+
+static ERTS_INLINE void
+remove_nm_sig(Process *c_p, ErtsMessage *sig, ErtsMessage ***next_nm_sig)
+{
+ ErtsMessage **next_sig = *next_nm_sig;
+ ASSERT(ERTS_SIG_IS_NON_MSG(sig));
+ ASSERT(*next_sig == sig);
+ *next_nm_sig = ((ErtsSignal *) sig)->common.specific.next;
+ remove_mq_sig(c_p, sig, next_sig, next_nm_sig);
+}
+
+static ERTS_INLINE void
+convert_to_msg(Process *c_p, ErtsMessage *sig, ErtsMessage *msg,
+ ErtsMessage ***next_nm_sig)
+{
+ ErtsMessage **next_sig = *next_nm_sig;
+ ASSERT(ERTS_SIG_IS_NON_MSG(sig));
+ *next_nm_sig = ((ErtsSignal *) sig)->common.specific.next;
+ c_p->sig_qs.len++;
+ *next_sig = msg;
+ remove_mq_sig(c_p, sig, &msg->next, next_nm_sig);
+}
+
+static ERTS_INLINE void
+convert_to_msgs(Process *c_p, ErtsMessage *sig, Uint no_msgs,
+ ErtsMessage *first_msg, ErtsMessage *last_msg,
+ ErtsMessage ***next_nm_sig)
+{
+ ErtsMessage **next_sig = *next_nm_sig;
+ ASSERT(ERTS_SIG_IS_NON_MSG(sig));
+ *next_nm_sig = ((ErtsSignal *) sig)->common.specific.next;
+ c_p->sig_qs.len += no_msgs;
+ *next_sig = first_msg;
+ remove_mq_sig(c_p, sig, &last_msg->next, next_nm_sig);
+}
+
+static ERTS_INLINE void
+insert_messages(Process *c_p, ErtsMessage **next, ErtsMessage *first,
+ ErtsMessage *last, Uint no_msgs, ErtsMessage ***next_nm_sig)
+{
+ last->next = *next;
+ if (c_p->sig_qs.cont_last == next)
+ c_p->sig_qs.cont_last = &last->next;
+ if (*next_nm_sig == next)
+ *next_nm_sig = &last->next;
+ if (c_p->sig_qs.nmsigs.last == next)
+ c_p->sig_qs.nmsigs.last = &last->next;
+ c_p->sig_qs.len += no_msgs;
+ *next = first;
+}
+
+static ERTS_INLINE void
+remove_mq_m_sig(Process *c_p, ErtsMessage *sig, ErtsMessage **next_sig, ErtsMessage ***next_nm_sig)
+{
+ /* Removing message... */
+ ASSERT(!ERTS_SIG_IS_NON_MSG(sig));
+ ASSERT(c_p->sig_qs.len > 0);
+ c_p->sig_qs.len--;
+ remove_mq_sig(c_p, sig, next_sig, next_nm_sig);
+}
+
+static ERTS_INLINE void
+remove_iq_m_sig(Process *c_p, ErtsMessage *sig, ErtsMessage **next_sig)
+{
+ /* Removing message... */
+ ASSERT(!ERTS_SIG_IS_NON_MSG(sig));
+ ASSERT(c_p->sig_qs.len > 0);
+ c_p->sig_qs.len--;
+ remove_iq_sig(c_p, sig, next_sig);
+}
+
+static ERTS_INLINE void
+convert_prepared_sig_to_msg(Process *c_p, ErtsMessage *sig, Eterm msg,
+ ErtsMessage ***next_nm_sig)
+{
+ /*
+ * Everything is already there except for the reference to
+ * the message and the combined hfrag marker that needs to be
+ * restored...
+ */
+ *next_nm_sig = ((ErtsSignal *) sig)->common.specific.next;
+ sig->data.attached = ERTS_MSG_COMBINED_HFRAG;
+ ERL_MESSAGE_TERM(sig) = msg;
+ c_p->sig_qs.len++;
+}
+
+static ERTS_INLINE int
+handle_exit_signal(Process *c_p, ErtsSigRecvTracing *tracing,
+ ErtsMessage *sig, ErtsMessage ***next_nm_sig,
+ int *exited)
+{
+ ErtsMessage *conv_msg = NULL;
+ ErtsExitSignalData *xsigd = NULL;
+ ErtsLinkData *ldp = NULL; /* Avoid erroneous warning... */
+ ErtsLink *dlnk = NULL; /* Avoid erroneous warning... */
+ Eterm tag = ((ErtsSignal *) sig)->common.tag;
+ Uint16 type = ERTS_PROC_SIG_TYPE(tag);
+ int op = ERTS_PROC_SIG_OP(tag);
+ int destroy = 0;
+ int ignore = 0;
+ int save = 0;
+ int exit = 0;
+ int cnt = 1;
+ Eterm reason;
+ Eterm from;
+
+ if (type == ERTS_SIG_Q_TYPE_GEN_EXIT) {
+ xsigd = get_exit_signal_data(sig);
+ from = xsigd->from;
+ reason = xsigd->reason;
+ if (op != ERTS_SIG_Q_OP_EXIT_LINKED)
+ ignore = 0;
+ else {
+ ErtsLink *llnk = erts_link_tree_lookup(ERTS_P_LINKS(c_p), from);
+ if (!llnk) {
+ /* Link no longer active; ignore... */
+ ignore = !0;
+ destroy = !0;
+ }
+ else {
+ ignore = 0;
+ erts_link_tree_delete(&ERTS_P_LINKS(c_p), llnk);
+ if (llnk->type != ERTS_LNK_TYPE_DIST_PROC)
+ erts_link_release(llnk);
+ else {
+ dlnk = erts_link_to_other(llnk, &ldp);
+ if (erts_link_dist_delete(dlnk))
+ erts_link_release_both(ldp);
+ else
+ erts_link_release(llnk);
+ }
+ }
+ }
+
+ if (!ignore) {
+
+ if ((op != ERTS_SIG_Q_OP_EXIT || reason != am_kill)
+ && (c_p->flags & F_TRAP_EXIT)) {
+ convert_prepared_sig_to_msg(c_p, sig,
+ xsigd->message, next_nm_sig);
+ conv_msg = sig;
+ }
+ else if (reason == am_normal && !xsigd->u.normal_kills) {
+ /* Ignore it... */
+ destroy = !0;
+ ignore = !0;
+ }
+ else {
+ /* Terminate... */
+ save = !0;
+ exit = !0;
+ if (op == ERTS_SIG_Q_OP_EXIT && reason == am_kill)
+ reason = am_killed;
+ }
+ }
+ }
+ else { /* Link exit */
+ ErtsLink *slnk = (ErtsLink *) sig;
+ ErtsLink *llnk = erts_link_to_other(slnk, &ldp);
+
+ ASSERT(type == ERTS_LNK_TYPE_PROC
+ || type == ERTS_LNK_TYPE_PORT
+ || type == ERTS_LNK_TYPE_DIST_PROC);
+
+ from = llnk->other.item;
+ reason = slnk->other.item; /* reason in other.item ... */
+ ASSERT(is_pid(from) || is_internal_port(from));
+ ASSERT(is_immed(reason));
+ ASSERT(op == ERTS_SIG_Q_OP_EXIT_LINKED);
+ dlnk = erts_link_tree_key_delete(&ERTS_P_LINKS(c_p), llnk);
+ if (!dlnk) {
+ ignore = !0; /* Link no longer active; ignore... */
+ ldp = NULL;
+ }
+ else {
+ Eterm pid;
+ ErtsMessage *mp;
+ ErtsProcLocks locks;
+ Uint hsz;
+ Eterm *hp;
+ ErlOffHeap *ohp;
+ ignore = 0;
+ if (dlnk == llnk)
+ dlnk = NULL;
+ else
+ ldp = NULL;
+
+ ASSERT(is_immed(reason));
+
+ if (!(c_p->flags & F_TRAP_EXIT)) {
+ if (reason == am_normal)
+ ignore = !0; /* Ignore it... */
+ else
+ exit = !0; /* Terminate... */
+ }
+ else {
+
+ /*
+ * Create and EXIT message and replace
+ * the original signal with the message...
+ */
+
+ locks = ERTS_PROC_LOCK_MAIN;
+
+ hsz = 4 + NC_HEAP_SIZE(from);
+
+ mp = erts_alloc_message_heap(c_p, &locks, hsz, &hp, &ohp);
+
+ if (locks != ERTS_PROC_LOCK_MAIN)
+ erts_proc_unlock(c_p, locks & ~ERTS_PROC_LOCK_MAIN);
+
+ pid = STORE_NC(&hp, ohp, from);
+
+ ERL_MESSAGE_TERM(mp) = TUPLE3(hp, am_EXIT, pid, reason);
+ ERL_MESSAGE_TOKEN(mp) = NIL;
+#ifdef USE_VM_PROBES
+ ERL_MESSAGE_DT_UTAG(mp) = NIL;
+#endif
+ if (is_immed(pid))
+ ERL_MESSAGE_FROM(mp) = pid;
+ else {
+ DistEntry *dep;
+ ASSERT(is_external_pid(pid));
+ dep = external_pid_dist_entry(pid);
+ ERL_MESSAGE_FROM(mp) = dep->sysname;
+ }
+
+ /* Replace original signal with the exit message... */
+ convert_to_msg(c_p, sig, mp, next_nm_sig);
+
+ cnt += 4;
+
+ conv_msg = mp;
+ }
+ }
+ destroy = !0;
+ }
+
+ if (ignore|exit) {
+ remove_nm_sig(c_p, sig, next_nm_sig);
+ if (exit) {
+ if (save) {
+ sig->data.attached = ERTS_MSG_COMBINED_HFRAG;
+ ERL_MESSAGE_TERM(sig) = xsigd->message;
+ erts_save_message_in_proc(c_p, sig);
+ }
+ /* Exit process... */
+ erts_set_self_exiting(c_p, reason);
+
+ cnt++;
+ }
+ }
+
+ if (!exit) {
+ if (conv_msg)
+ erts_proc_notify_new_message(c_p, ERTS_PROC_LOCK_MAIN);
+ if (op == ERTS_SIG_Q_OP_EXIT_LINKED && tracing->procs)
+ getting_unlinked(c_p, from);
+ }
+
+ if (destroy) {
+ cnt++;
+ if (type == ERTS_SIG_Q_TYPE_GEN_EXIT) {
+ sig->next = NULL;
+ erts_cleanup_messages(sig);
+ }
+ else {
+ if (ldp)
+ erts_link_release_both(ldp);
+ else {
+ if (dlnk)
+ erts_link_release(dlnk);
+ erts_link_release((ErtsLink *) sig);
+ }
+ }
+ }
+
+ *exited = exit;
+
+ return cnt;
+}
+
+static ERTS_INLINE int
+convert_prepared_down_message(Process *c_p, ErtsMessage *sig,
+ Eterm msg, ErtsMessage ***next_nm_sig)
+{
+ convert_prepared_sig_to_msg(c_p, sig, msg, next_nm_sig);
+ erts_proc_notify_new_message(c_p, ERTS_PROC_LOCK_MAIN);
+ return 1;
+}
+
+static int
+convert_to_down_message(Process *c_p,
+ ErtsMessage *sig,
+ ErtsMonitorData *mdp,
+ Uint16 mon_type,
+ ErtsMessage ***next_nm_sig)
+{
+ /*
+ * Create a 'DOWN' message and replace the signal
+ * with it...
+ */
+ int cnt = 0;
+ Eterm node = am_undefined;
+ ErtsMessage *mp;
+ ErtsProcLocks locks;
+ Uint hsz;
+ Eterm *hp, ref, from, type, reason;
+ ErlOffHeap *ohp;
+
+ ASSERT(mdp);
+ ASSERT((mdp->origin.flags & ERTS_ML_FLGS_SAME)
+ == (mdp->target.flags & ERTS_ML_FLGS_SAME));
+
+ hsz = 6; /* 5-tuple */
+
+ if (mdp->origin.flags & ERTS_ML_FLG_NAME)
+ hsz += 3; /* reg name 2-tuple */
+ else {
+ ASSERT(is_pid(mdp->origin.other.item)
+ || is_internal_port(mdp->origin.other.item));
+ hsz += NC_HEAP_SIZE(mdp->origin.other.item);
+ }
+
+ ASSERT(is_ref(mdp->ref));
+ hsz += NC_HEAP_SIZE(mdp->ref);
+
+ locks = ERTS_PROC_LOCK_MAIN;
+
+ /* reason is mdp->target.other.item */
+ reason = mdp->target.other.item;
+ ASSERT(is_immed(reason));
+
+ mp = erts_alloc_message_heap(c_p, &locks, hsz, &hp, &ohp);
+
+ if (locks != ERTS_PROC_LOCK_MAIN)
+ erts_proc_unlock(c_p, locks & ~ERTS_PROC_LOCK_MAIN);
+
+ cnt += 4;
+
+ ref = STORE_NC(&hp, ohp, mdp->ref);
+
+ if (!(mdp->origin.flags & ERTS_ML_FLG_NAME)) {
+ from = STORE_NC(&hp, ohp, mdp->origin.other.item);
+ }
+ else {
+ ErtsMonitorDataExtended *mdep;
+ ASSERT(mdp->origin.flags & ERTS_ML_FLG_EXTENDED);
+ mdep = (ErtsMonitorDataExtended *) mdp;
+ ASSERT(is_atom(mdep->u.name));
+ if (mdep->dist)
+ node = mdep->dist->nodename;
+ else
+ node = erts_this_dist_entry->sysname;
+ from = TUPLE2(hp, mdep->u.name, node);
+ hp += 3;
+ }
+
+ ASSERT(mdp->origin.type == mon_type);
+ switch (mon_type) {
+ case ERTS_MON_TYPE_PORT:
+ type = am_port;
+ if (mdp->origin.other.item == am_undefined) {
+ /* failed by name... */
+ ERL_MESSAGE_FROM(mp) = am_system;
+ }
+ else {
+ ASSERT(is_internal_port(mdp->origin.other.item));
+ ERL_MESSAGE_FROM(mp) = mdp->origin.other.item;
+ }
+ break;
+ case ERTS_MON_TYPE_PROC:
+ type = am_process;
+ if (mdp->origin.other.item == am_undefined) {
+ /* failed by name... */
+ ERL_MESSAGE_FROM(mp) = am_system;
+ }
+ else {
+ ASSERT(is_internal_pid(mdp->origin.other.item));
+ ERL_MESSAGE_FROM(mp) = mdp->origin.other.item;
+ }
+ break;
+ case ERTS_MON_TYPE_DIST_PROC:
+ type = am_process;
+ if (node == am_undefined) {
+ ErtsMonitorDataExtended *mdep;
+ ASSERT(mdp->origin.flags & ERTS_ML_FLG_EXTENDED);
+ mdep = (ErtsMonitorDataExtended *) mdp;
+ ASSERT(mdep->dist);
+ node = mdep->dist->nodename;
+ }
+ ASSERT(is_atom(node) && node != am_undefined);
+ ERL_MESSAGE_FROM(mp) = node;
+ break;
+ default:
+ ERTS_INTERNAL_ERROR("Unexpected monitor type");
+ type = am_undefined;
+ ERL_MESSAGE_FROM(mp) = am_undefined;
+ break;
+ }
+
+ ERL_MESSAGE_TERM(mp) = TUPLE5(hp, am_DOWN, ref,
+ type, from, reason);
+ hp += 6;
+
+ ERL_MESSAGE_TOKEN(mp) = NIL;
+#ifdef USE_VM_PROBES
+ ERL_MESSAGE_DT_UTAG(mp) = NIL;
+#endif
+ /* Replace original signal with the exit message... */
+ convert_to_msg(c_p, sig, mp, next_nm_sig);
+
+ cnt += 4;
+
+ erts_proc_notify_new_message(c_p, ERTS_PROC_LOCK_MAIN);
+
+ return cnt;
+}
+
+static ERTS_INLINE int
+convert_to_nodedown_messages(Process *c_p,
+ ErtsMessage *sig,
+ ErtsMonitorData *mdp,
+ ErtsMessage ***next_nm_sig)
+{
+ int cnt = 1;
+ Uint n;
+ ErtsMonitorDataExtended *mdep = (ErtsMonitorDataExtended *) mdp;
+
+ ASSERT((mdp->origin.flags & ERTS_ML_FLGS_SAME)
+ == (mdp->target.flags & ERTS_ML_FLGS_SAME));
+ ASSERT(mdp->origin.flags & ERTS_ML_FLG_EXTENDED);
+
+ n = mdep->u.refc;
+
+ if (n == 0)
+ remove_nm_sig(c_p, sig, next_nm_sig);
+ else {
+ Uint i;
+ ErtsMessage *nd_first = NULL;
+ ErtsMessage *nd_last = NULL;
+ ErtsProcLocks locks = ERTS_PROC_LOCK_MAIN;
+ Eterm node = mdep->dist->nodename;
+
+ ASSERT(is_atom(node));
+ ASSERT(n > 0);
+
+ for (i = 0; i < n; i++) {
+ ErtsMessage *mp;
+ ErlOffHeap *ohp;
+ Eterm *hp;
+
+ mp = erts_alloc_message_heap(c_p, &locks, 3, &hp, &ohp);
+
+ ERL_MESSAGE_TERM(mp) = TUPLE2(hp, am_nodedown, node);
+ ERL_MESSAGE_FROM(mp) = am_system;
+ ERL_MESSAGE_TOKEN(mp) = NIL;
+#ifdef USE_VM_PROBES
+ ERL_MESSAGE_DT_UTAG(mp) = NIL;
+#endif
+ mp->next = nd_first;
+ nd_first = mp;
+ if (!nd_last)
+ nd_last = mp;
+ cnt++;
+ }
+
+ if (locks != ERTS_PROC_LOCK_MAIN)
+ erts_proc_unlock(c_p, locks & ~ERTS_PROC_LOCK_MAIN);
+
+ /* Replace signal with 'nodedown' messages */
+ convert_to_msgs(c_p, sig, n, nd_first, nd_last, next_nm_sig);
+
+ erts_proc_notify_new_message(c_p, ERTS_PROC_LOCK_MAIN);
+ }
+ return cnt;
+}
+
+static int
+handle_nodedown(Process *c_p,
+ ErtsMessage *sig,
+ ErtsMonitorData *mdp,
+ ErtsMessage ***next_nm_sig)
+{
+ ErtsMonitorDataExtended *mdep = (ErtsMonitorDataExtended *) mdp;
+ ErtsMonitor *omon = &mdp->origin;
+ int not_in_subtab = !(omon->flags & ERTS_ML_FLG_IN_SUBTABLE);
+ int cnt = 1;
+
+ ASSERT(erts_monitor_is_in_table(omon));
+
+ if (not_in_subtab & !mdep->uptr.node_monitors)
+ erts_monitor_tree_delete(&ERTS_P_MONITORS(c_p), omon);
+ else if (not_in_subtab) {
+ ErtsMonitor *sub_mon;
+ ErtsMonitorDataExtended *sub_mdep;
+ sub_mon = erts_monitor_list_last(mdep->uptr.node_monitors);
+ ASSERT(sub_mon);
+ erts_monitor_list_delete(&mdep->uptr.node_monitors, sub_mon);
+ sub_mon->flags &= ~ERTS_ML_FLG_IN_SUBTABLE;
+ sub_mdep = (ErtsMonitorDataExtended *) erts_monitor_to_data(sub_mon);
+ ASSERT(!sub_mdep->uptr.node_monitors);
+ sub_mdep->uptr.node_monitors = mdep->uptr.node_monitors;
+ mdep->uptr.node_monitors = NULL;
+ erts_monitor_tree_replace(&ERTS_P_MONITORS(c_p), omon, sub_mon);
+ cnt += 2;
+ }
+ else {
+ ErtsMonitorDataExtended *top_mdep;
+ ErtsMonitor *top_mon;
+ ASSERT(is_atom(omon->other.item));
+ ASSERT(!mdep->uptr.node_monitors);
+ top_mon = erts_monitor_tree_lookup(ERTS_P_MONITORS(c_p),
+ omon->other.item);
+ ASSERT(top_mon);
+ top_mdep = (ErtsMonitorDataExtended *) erts_monitor_to_data(top_mon);
+ ASSERT(top_mdep->uptr.node_monitors);
+ erts_monitor_list_delete(&top_mdep->uptr.node_monitors, omon);
+ omon->flags &= ~ERTS_ML_FLG_IN_SUBTABLE;
+ cnt += 3;
+ }
+
+ return cnt + convert_to_nodedown_messages(c_p, sig, mdp, next_nm_sig);
+}
+
+static void
+handle_persistent_mon_msg(Process *c_p, Uint16 type,
+ ErtsMonitor *mon, ErtsMessage *sig,
+ Eterm msg, ErtsMessage ***next_nm_sig)
+{
+ convert_prepared_sig_to_msg(c_p, sig, msg, next_nm_sig);
+
+ switch (type) {
+
+ case ERTS_MON_TYPE_TIME_OFFSET:
+ ASSERT(mon->type == ERTS_MON_TYPE_TIME_OFFSET);
+ break;
+
+ case ERTS_MON_TYPE_NODES: {
+ ErtsMonitorDataExtended *mdep;
+ Uint n;
+ ASSERT(mon->type == ERTS_MON_TYPE_NODES);
+ mdep = (ErtsMonitorDataExtended *) erts_monitor_to_data(mon);
+ ERTS_ML_ASSERT(mdep->u.refc > 0);
+ n = mdep->u.refc;
+ n--;
+ if (n > 0) {
+ ErtsProcLocks locks = ERTS_PROC_LOCK_MAIN;
+ ErtsMessage *first = NULL, *prev, *last;
+ Uint hsz = size_object(msg);
+ Uint i;
+
+ for (i = 0; i < n; i++) {
+ Eterm *hp;
+ ErlOffHeap *ohp;
+
+ last = erts_alloc_message_heap(c_p, &locks, hsz, &hp, &ohp);
+
+ if (!first)
+ first = last;
+ else
+ prev->next = last;
+ prev = last;
+
+ ERL_MESSAGE_TERM(last) = copy_struct(msg, hsz, &hp, ohp);
+
+#ifdef USE_VM_PROBES
+ ASSERT(is_immed(ERL_MESSAGE_DT_UTAG(sig)));
+ ERL_MESSAGE_DT_UTAG(last) = ERL_MESSAGE_DT_UTAG(sig);
+#endif
+ ASSERT(is_immed(ERL_MESSAGE_TOKEN(sig)));
+ ERL_MESSAGE_TOKEN(last) = ERL_MESSAGE_TOKEN(sig);
+ ASSERT(is_immed(ERL_MESSAGE_FROM(sig)));
+ ERL_MESSAGE_FROM(last) = ERL_MESSAGE_FROM(sig);
+
+ }
+ if (locks != ERTS_PROC_LOCK_MAIN)
+ erts_proc_unlock(c_p, locks & ~ERTS_PROC_LOCK_MAIN);
+ insert_messages(c_p, &sig->next, first, last, n, next_nm_sig);
+ }
+ break;
+ }
+
+ default:
+ ERTS_INTERNAL_ERROR("Invalid type");
+ break;
+ }
+
+ erts_proc_notify_new_message(c_p, ERTS_PROC_LOCK_MAIN);
+}
+
+static void
+group_leader_reply(Process *c_p, Eterm to, Eterm ref, int success)
+{
+ Process *rp = erts_proc_lookup(to);
+
+ if (rp) {
+ ErtsProcLocks locks;
+ Uint sz;
+ Eterm *hp, msg, ref_cpy, result;
+ ErlOffHeap *ohp;
+ ErtsMessage *mp;
+
+ ASSERT(is_internal_ref(ref));
+
+ locks = c_p == rp ? ERTS_PROC_LOCK_MAIN : 0;
+ sz = size_object(ref);
+
+ mp = erts_alloc_message_heap(rp, &locks, sz+3,
+ &hp, &ohp);
+
+ ref_cpy = copy_struct(ref, sz, &hp, ohp);
+ result = success ? am_true : am_badarg;
+ msg = TUPLE2(hp, ref_cpy, result);
+
+ erts_queue_message(rp, locks, mp, msg, am_system);
+
+ if (c_p == rp)
+ locks &= ~ERTS_PROC_LOCK_MAIN;
+
+ if (locks)
+ erts_proc_unlock(rp, locks);
+ }
+}
+
+static void
+handle_group_leader(Process *c_p, ErtsSigGroupLeader *sgl)
+{
+ erts_aint_t flags;
+
+ flags = erts_atomic_read_band_nob(&sgl->flags, ~ERTS_SIG_GL_FLG_ACTIVE);
+ if (flags & ERTS_SIG_GL_FLG_ACTIVE) {
+ int res = erts_set_group_leader(c_p, sgl->group_leader);
+ if (is_internal_pid(sgl->reply_to))
+ group_leader_reply(c_p, sgl->reply_to, sgl->ref, res);
+ }
+
+ flags = erts_atomic_read_band_nob(&sgl->flags, ~ERTS_SIG_GL_FLG_RECEIVER);
+ if ((flags & ~ERTS_SIG_GL_FLG_RECEIVER) == 0)
+ destroy_sig_group_leader(sgl);
+}
+
+
+/*
+ * Called in order to handle incoming signals.
+ */
+
+int
+erts_proc_sig_handle_incoming(Process *c_p, erts_aint32_t *statep,
+ int *redsp, int max_reds, int local_only)
+{
+ Eterm tag;
+ erts_aint32_t state;
+ int cnt, limit, abs_lim, msg_tracing;
+ ErtsMessage *sig, ***next_nm_sig;
+ ErtsSigRecvTracing tracing;
+
+ ERTS_HDBG_CHECK_SIGNAL_PRIV_QUEUE(c_p);
+ ERTS_LC_ASSERT(ERTS_PROC_LOCK_MAIN == erts_proc_lc_my_proc_locks(c_p));
+
+ if (local_only)
+ state = -1; /* can never be a valid state... */
+ else {
+ state = erts_atomic32_read_nob(&c_p->state);
+ if (ERTS_PSFLG_SIG_IN_Q & state) {
+ erts_proc_lock(c_p, ERTS_PROC_LOCK_MSGQ);
+ erts_proc_sig_fetch(c_p);
+ erts_proc_unlock(c_p, ERTS_PROC_LOCK_MSGQ);
+ }
+ }
+
+ limit = *redsp;
+ *redsp = 0;
+
+ if (!c_p->sig_qs.cont) {
+ if (state == -1)
+ *statep = erts_atomic32_read_nob(&c_p->state);
+ else
+ *statep = state;
+ return !0;
+ }
+
+ next_nm_sig = &c_p->sig_qs.nmsigs.next;
+
+ setup_tracing_state(c_p, &tracing);
+ msg_tracing = tracing.messages.active;
+
+ limit *= ERTS_SIG_REDS_CNT_FACTOR;
+ abs_lim = ERTS_SIG_REDS_CNT_FACTOR*max_reds;
+ if (limit > abs_lim)
+ limit = abs_lim;
+
+ cnt = 0;
+
+ do {
+
+ if (msg_tracing) {
+ ERTS_PROC_SIG_HDBG_PRIV_CHKQ(c_p, &tracing, next_nm_sig);
+ if (handle_msg_tracing(c_p, &tracing, next_nm_sig) != 0) {
+ ERTS_PROC_SIG_HDBG_PRIV_CHKQ(c_p, &tracing, next_nm_sig);
+ break; /* tracing limit or end... */
+ }
+ ERTS_PROC_SIG_HDBG_PRIV_CHKQ(c_p, &tracing, next_nm_sig);
+ }
+
+ if (!*next_nm_sig)
+ break;
+
+ sig = **next_nm_sig;
+
+ ASSERT(sig);
+ ASSERT(ERTS_SIG_IS_NON_MSG(sig));
+
+ tag = ((ErtsSignal *) sig)->common.tag;
+
+ switch (ERTS_PROC_SIG_OP(tag)) {
+
+ case ERTS_SIG_Q_OP_EXIT:
+ case ERTS_SIG_Q_OP_EXIT_LINKED: {
+ int exited;
+
+ ERTS_PROC_SIG_HDBG_PRIV_CHKQ(c_p, &tracing, next_nm_sig);
+
+ cnt += handle_exit_signal(c_p, &tracing, sig,
+ next_nm_sig, &exited);
+
+ ERTS_PROC_SIG_HDBG_PRIV_CHKQ(c_p, &tracing, next_nm_sig);
+
+ if (exited)
+ goto stop; /* terminated by signal */
+ /* ignored or converted to exit message... */
+ break;
+ }
+
+ case ERTS_SIG_Q_OP_MONITOR_DOWN: {
+ Uint16 type = ERTS_PROC_SIG_TYPE(tag);
+ ErtsExitSignalData *xsigd = NULL;
+ ErtsMonitorData *mdp = NULL;
+ ErtsMonitor *omon = NULL, *tmon = NULL;
+
+ ERTS_PROC_SIG_HDBG_PRIV_CHKQ(c_p, &tracing, next_nm_sig);
+
+ switch (type) {
+ case ERTS_MON_TYPE_DIST_PROC:
+ case ERTS_MON_TYPE_PROC:
+ case ERTS_MON_TYPE_PORT:
+ tmon = (ErtsMonitor *) sig;
+ ASSERT(erts_monitor_is_target(tmon));
+ ASSERT(!erts_monitor_is_in_table(tmon));
+ mdp = erts_monitor_to_data(tmon);
+ if (erts_monitor_is_in_table(&mdp->origin)) {
+ omon = &mdp->origin;
+ erts_monitor_tree_delete(&ERTS_P_MONITORS(c_p),
+ omon);
+ cnt += convert_to_down_message(c_p, sig, mdp,
+ type, next_nm_sig);
+ }
+ break;
+ case ERTS_SIG_Q_TYPE_GEN_EXIT:
+ xsigd = get_exit_signal_data(sig);
+ omon = erts_monitor_tree_lookup(ERTS_P_MONITORS(c_p),
+ xsigd->u.ref);
+ if (omon) {
+ ASSERT(erts_monitor_is_origin(omon));
+ if (omon->type == ERTS_MON_TYPE_DIST_PROC) {
+ mdp = erts_monitor_to_data(omon);
+ if (erts_monitor_dist_delete(&mdp->target))
+ tmon = &mdp->target;
+ }
+ erts_monitor_tree_delete(&ERTS_P_MONITORS(c_p),
+ omon);
+ cnt += convert_prepared_down_message(c_p, sig,
+ xsigd->message,
+ next_nm_sig);
+ }
+ break;
+ case ERTS_MON_TYPE_NODE:
+ tmon = (ErtsMonitor *) sig;
+ ASSERT(erts_monitor_is_target(tmon));
+ ASSERT(!erts_monitor_is_in_table(tmon));
+ mdp = erts_monitor_to_data(tmon);
+ if (erts_monitor_is_in_table(&mdp->origin)) {
+ omon = &mdp->origin;
+ cnt += handle_nodedown(c_p, sig, mdp, next_nm_sig);
+ }
+ break;
+ default:
+ ERTS_INTERNAL_ERROR("invalid monitor type");
+ break;
+ }
+
+ if (omon) {
+ if (tmon)
+ erts_monitor_release_both(mdp);
+ else
+ erts_monitor_release(omon);
+ }
+ else {
+ remove_nm_sig(c_p, sig, next_nm_sig);
+ if (xsigd) {
+ sig->next = NULL;
+ erts_cleanup_messages(sig);
+ }
+ if (tmon)
+ erts_monitor_release(tmon);
+ }
+
+ ERTS_PROC_SIG_HDBG_PRIV_CHKQ(c_p, &tracing, next_nm_sig);
+ break;
+ }
+
+ case ERTS_SIG_Q_OP_PERSISTENT_MON_MSG: {
+ Uint16 type = ERTS_PROC_SIG_TYPE(tag);
+ ErtsMonitor *mon;
+ Eterm msg;
+ Eterm key;
+
+ ERTS_PROC_SIG_HDBG_PRIV_CHKQ(c_p, &tracing, next_nm_sig);
+
+ key = get_persist_mon_msg(sig, &msg);
+
+ cnt++;
+ mon = erts_monitor_tree_lookup(ERTS_P_MONITORS(c_p), key);
+ if (mon) {
+ ASSERT(erts_monitor_is_origin(mon));
+ handle_persistent_mon_msg(c_p, type, mon, sig,
+ msg, next_nm_sig);
+ }
+ else {
+ cnt++;
+ remove_nm_sig(c_p, sig, next_nm_sig);
+ sig->next = NULL;
+ erts_cleanup_messages(sig);
+ }
+
+ ERTS_PROC_SIG_HDBG_PRIV_CHKQ(c_p, &tracing, next_nm_sig);
+ break;
+ }
+
+ case ERTS_SIG_Q_OP_MONITOR: {
+ ErtsMonitor *mon = (ErtsMonitor *) sig;
+
+ ASSERT(erts_monitor_is_target(mon));
+ ERTS_PROC_SIG_HDBG_PRIV_CHKQ(c_p, &tracing, next_nm_sig);
+
+ remove_nm_sig(c_p, sig, next_nm_sig);
+
+ if (mon->type == ERTS_MON_TYPE_DIST_PROC)
+ erts_monitor_tree_insert(&ERTS_P_MONITORS(c_p), mon);
+ else
+ erts_monitor_list_insert(&ERTS_P_LT_MONITORS(c_p), mon);
+ ERTS_PROC_SIG_HDBG_PRIV_CHKQ(c_p, &tracing, next_nm_sig);
+ break;
+ }
+
+ case ERTS_SIG_Q_OP_DEMONITOR: {
+ Uint16 type = ERTS_PROC_SIG_TYPE(tag);
+
+ ERTS_PROC_SIG_HDBG_PRIV_CHKQ(c_p, &tracing, next_nm_sig);
+
+ remove_nm_sig(c_p, sig, next_nm_sig);
+
+ if (type == ERTS_SIG_Q_TYPE_DIST_PROC_DEMONITOR) {
+ ErtsMonitor *tmon;
+ ErtsSigDistProcDemonitor *dmon;
+ dmon = (ErtsSigDistProcDemonitor *) sig;
+ tmon = erts_monitor_tree_lookup(ERTS_P_MONITORS(c_p), dmon->ref);
+ destroy_dist_proc_demonitor(dmon);
+ cnt++;
+ if (tmon) {
+ ErtsMonitorData *mdp = erts_monitor_to_data(tmon);
+ ASSERT(erts_monitor_is_target(tmon));
+ erts_monitor_tree_delete(&ERTS_P_MONITORS(c_p), tmon);
+ if (!erts_monitor_dist_delete(&mdp->origin))
+ erts_monitor_release(tmon);
+ else
+ erts_monitor_release_both(mdp);
+ cnt += 2;
+ }
+ }
+ else {
+ ErtsMonitor *omon = (ErtsMonitor *) sig;
+ ErtsMonitorData *mdp = erts_monitor_to_data(omon);
+ ASSERT(omon->type == type);
+ ASSERT(erts_monitor_is_origin(omon));
+ ASSERT(!erts_monitor_is_in_table(omon));
+ if (!erts_monitor_is_in_table(&mdp->target))
+ erts_monitor_release(omon);
+ else {
+ ErtsMonitor *tmon = &mdp->target;
+ ASSERT(tmon->type == type);
+ if (type == ERTS_MON_TYPE_DIST_PROC)
+ erts_monitor_tree_delete(&ERTS_P_MONITORS(c_p), tmon);
+ else {
+ erts_monitor_list_delete(&ERTS_P_LT_MONITORS(c_p), tmon);
+ if (type == ERTS_MON_TYPE_RESOURCE) {
+ erts_nif_demonitored((ErtsResource *) tmon->other.ptr);
+ cnt++;
+ }
+ }
+ erts_monitor_release_both(mdp);
+ cnt++;
+ }
+ cnt++;
+ }
+ ERTS_PROC_SIG_HDBG_PRIV_CHKQ(c_p, &tracing, next_nm_sig);
+ break;
+ }
+
+ case ERTS_SIG_Q_OP_LINK: {
+ ErtsLink *rlnk, *lnk = (ErtsLink *) sig;
+
+ ERTS_PROC_SIG_HDBG_PRIV_CHKQ(c_p, &tracing, next_nm_sig);
+
+ remove_nm_sig(c_p, sig, next_nm_sig);
+ rlnk = erts_link_tree_insert_addr_replace(&ERTS_P_LINKS(c_p),
+ lnk);
+ if (!rlnk) {
+ if (tracing.procs)
+ getting_linked(c_p, lnk->other.item);
+ }
+ else {
+ if (rlnk->type != ERTS_LNK_TYPE_DIST_PROC)
+ erts_link_release(rlnk);
+ else {
+ ErtsLinkData *ldp;
+ ErtsLink *dlnk = erts_link_to_other(rlnk, &ldp);
+ if (erts_link_dist_delete(dlnk))
+ erts_link_release_both(ldp);
+ else
+ erts_link_release(rlnk);
+ }
+ }
+
+ ERTS_PROC_SIG_HDBG_PRIV_CHKQ(c_p, &tracing, next_nm_sig);
+ break;
+ }
+
+ case ERTS_SIG_Q_OP_UNLINK: {
+ Uint16 type = ERTS_PROC_SIG_TYPE(tag);
+ ErtsLinkData *ldp;
+ ErtsLink *llnk;
+
+ ERTS_PROC_SIG_HDBG_PRIV_CHKQ(c_p, &tracing, next_nm_sig);
+
+ remove_nm_sig(c_p, sig, next_nm_sig);
+ if (type == ERTS_SIG_Q_TYPE_DIST_LINK) {
+ ErtsSigDistLinkOp *sdlnk = (ErtsSigDistLinkOp *) sig;
+ ASSERT(type == ERTS_SIG_Q_TYPE_DIST_LINK);
+ ASSERT(is_external_pid(sdlnk->remote));
+ llnk = erts_link_tree_lookup(ERTS_P_LINKS(c_p), sdlnk->remote);
+ if (llnk) {
+ ErtsLink *dlnk = erts_link_to_other(llnk, &ldp);
+ erts_link_tree_delete(&ERTS_P_LINKS(c_p), llnk);
+ if (erts_link_dist_delete(dlnk))
+ erts_link_release_both(ldp);
+ else
+ erts_link_release(llnk);
+ cnt += 8;
+ if (tracing.procs)
+ getting_unlinked(c_p, sdlnk->remote);
+ }
+ destroy_sig_dist_link_op(sdlnk);
+ cnt++;
+ }
+ else {
+ ErtsLinkData *ldp;
+ ErtsLink *dlnk, *slnk;
+ slnk = (ErtsLink *) sig;
+ llnk = erts_link_to_other(slnk, &ldp);
+ dlnk = erts_link_tree_key_delete(&ERTS_P_LINKS(c_p), llnk);
+ if (!dlnk)
+ erts_link_release(slnk);
+ else {
+ if (tracing.procs)
+ getting_unlinked(c_p, llnk->other.item);
+ if (dlnk == llnk)
+ erts_link_release_both(ldp);
+ else {
+ erts_link_release(slnk);
+ erts_link_release(dlnk);
+ }
+ }
+ cnt += 2;
+ }
+
+ ERTS_PROC_SIG_HDBG_PRIV_CHKQ(c_p, &tracing, next_nm_sig);
+ break;
+ }
+
+ case ERTS_SIG_Q_OP_GROUP_LEADER: {
+ ErtsSigGroupLeader *sgl = (ErtsSigGroupLeader *) sig;
+ ERTS_PROC_SIG_HDBG_PRIV_CHKQ(c_p, &tracing, next_nm_sig);
+ remove_nm_sig(c_p, sig, next_nm_sig);
+ handle_group_leader(c_p, sgl);
+ ERTS_PROC_SIG_HDBG_PRIV_CHKQ(c_p, &tracing, next_nm_sig);
+ break;
+ }
+
+ case ERTS_SIG_Q_OP_IS_ALIVE:
+ ERTS_PROC_SIG_HDBG_PRIV_CHKQ(c_p, &tracing, next_nm_sig);
+ remove_nm_sig(c_p, sig, next_nm_sig);
+ is_alive_response(c_p, sig, !0);
+ ERTS_PROC_SIG_HDBG_PRIV_CHKQ(c_p, &tracing, next_nm_sig);
+ break;
+
+ case ERTS_SIG_Q_OP_TRACE_CHANGE_STATE: {
+ Uint16 type = ERTS_PROC_SIG_TYPE(tag);
+
+ ERTS_PROC_SIG_HDBG_PRIV_CHKQ(c_p, &tracing, next_nm_sig);
+ msg_tracing = handle_trace_change_state(c_p, &tracing,
+ type, sig,
+ next_nm_sig);
+ ERTS_PROC_SIG_HDBG_PRIV_CHKQ(c_p, &tracing, next_nm_sig);
+
+ break;
+ }
+
+ default:
+ ERTS_INTERNAL_ERROR("Unknown signal");
+ break;
+ }
+
+ cnt++;
+
+ } while (cnt <= limit || stretch_limit(c_p, &tracing, abs_lim, &limit));
+
+stop: {
+ int deferred_save, deferred_saved_last, res;
+
+ deferred_saved_last = !!(c_p->flags & F_DEFERRED_SAVED_LAST);
+ deferred_save = 0;
+
+ if (!deferred_saved_last)
+ deferred_save = 0;
+ else {
+ if (c_p->sig_qs.saved_last == &c_p->sig_qs.cont) {
+ c_p->sig_qs.saved_last = c_p->sig_qs.last;
+ c_p->flags &= ~F_DEFERRED_SAVED_LAST;
+ deferred_saved_last = deferred_save = 0;
+ }
+ else {
+ if (c_p->sig_qs.save == c_p->sig_qs.last)
+ deferred_save = !0;
+ else
+ deferred_save = 0;
+ }
+ }
+
+ ASSERT(c_p->sig_qs.saved_last != &c_p->sig_qs.cont);
+
+ if (ERTS_UNLIKELY(msg_tracing != 0)) {
+ /*
+ * All messages that has been traced should
+ * be moved to inner queue. Next signal in
+ * middle queue should either be next message
+ * to trace or next non-message signal.
+ */
+ ASSERT(tracing.messages.next);
+ if (*next_nm_sig) {
+ if (*next_nm_sig == tracing.messages.next)
+ *next_nm_sig = &c_p->sig_qs.cont;
+ if (c_p->sig_qs.nmsigs.last == tracing.messages.next)
+ c_p->sig_qs.nmsigs.last = &c_p->sig_qs.cont;
+ *statep = erts_atomic32_read_nob(&c_p->state);
+ }
+ else {
+ ASSERT(!c_p->sig_qs.nmsigs.next);
+ c_p->sig_qs.nmsigs.last = NULL;
+ state = erts_atomic32_read_band_nob(&c_p->state,
+ ~ERTS_PSFLG_SIG_Q);
+ state &= ~ERTS_PSFLG_SIG_Q;
+ *statep = state;
+ }
+
+ if (tracing.messages.next != &c_p->sig_qs.cont) {
+ *c_p->sig_qs.last = c_p->sig_qs.cont;
+ c_p->sig_qs.last = tracing.messages.next;
+
+ c_p->sig_qs.cont = *tracing.messages.next;
+ if (!c_p->sig_qs.cont)
+ c_p->sig_qs.cont_last = &c_p->sig_qs.cont;
+ *c_p->sig_qs.last = NULL;
+ }
+
+ res = !c_p->sig_qs.cont;
+ }
+ else if (*next_nm_sig) {
+ /*
+ * All messages prior to next non-message
+ * signal should be moved to inner queue.
+ * Next non-message signal to handle should
+ * be first in middle queue.
+ */
+ ASSERT(**next_nm_sig);
+ if (*next_nm_sig != &c_p->sig_qs.cont) {
+ *c_p->sig_qs.last = c_p->sig_qs.cont;
+ c_p->sig_qs.last = *next_nm_sig;
+
+ c_p->sig_qs.cont = **next_nm_sig;
+ if (c_p->sig_qs.nmsigs.last == *next_nm_sig)
+ c_p->sig_qs.nmsigs.last = &c_p->sig_qs.cont;
+ *next_nm_sig = &c_p->sig_qs.cont;
+ *c_p->sig_qs.last = NULL;
+ }
+
+ ASSERT(c_p->sig_qs.cont);
+
+ *statep = erts_atomic32_read_nob(&c_p->state);
+
+ res = 0;
+ }
+ else {
+ /*
+ * All non-message signals handled. All
+ * messages should be moved to inner queue.
+ * Middle queue should be empty.
+ */
+ ASSERT(!c_p->sig_qs.nmsigs.next);
+ c_p->sig_qs.nmsigs.last = NULL;
+
+ if (c_p->sig_qs.cont_last != &c_p->sig_qs.cont) {
+ ASSERT(!*c_p->sig_qs.last);
+ *c_p->sig_qs.last = c_p->sig_qs.cont;
+ c_p->sig_qs.last = c_p->sig_qs.cont_last;
+ ASSERT(!*c_p->sig_qs.last);
+
+ c_p->sig_qs.cont_last = &c_p->sig_qs.cont;
+ c_p->sig_qs.cont = NULL;
+ }
+
+ ASSERT(!c_p->sig_qs.cont);
+
+ state = erts_atomic32_read_band_nob(&c_p->state,
+ ~ERTS_PSFLG_SIG_Q);
+ state &= ~ERTS_PSFLG_SIG_Q;
+ *statep = state;
+ res = !0;
+ }
+
+ if (deferred_saved_last
+ && (c_p->sig_qs.saved_last == &c_p->sig_qs.cont)) {
+ c_p->sig_qs.saved_last = c_p->sig_qs.last;
+ c_p->flags &= ~F_DEFERRED_SAVED_LAST;
+ if (deferred_save)
+ c_p->sig_qs.save = c_p->sig_qs.saved_last;
+ }
+ else if (!res) {
+ if (deferred_save) {
+ c_p->sig_qs.save = c_p->sig_qs.last;
+ ASSERT(!PEEK_MESSAGE(c_p));
+ }
+ }
+ else {
+ c_p->flags &= ~F_DEFERRED_SAVED_LAST;
+ if (deferred_save)
+ c_p->sig_qs.save = c_p->sig_qs.saved_last;
+ }
+
+ ERTS_HDBG_CHECK_SIGNAL_PRIV_QUEUE(c_p);
+
+ *redsp = cnt/4 + 1;
+
+ return res;
+ }
+}
+
+static int
+stretch_limit(Process *c_p, ErtsSigRecvTracing *tp,
+ int abs_lim, int *limp)
+{
+ int lim;
+ /*
+ * Stretch limit up to a maximum of 'abs_lim' if
+ * there currently are no messages available to
+ * inspect by 'receive' and it might be possible
+ * to get messages available by processing
+ * signals (or trace messages).
+ */
+
+ lim = *limp;
+ ASSERT(abs_lim >= lim);
+ if (abs_lim == lim)
+ return 0;
+
+ if (!(c_p->flags & F_DEFERRED_SAVED_LAST)) {
+ ErtsSignal *sig;
+
+ if (PEEK_MESSAGE(c_p))
+ return 0;
+ sig = (ErtsSignal *) c_p->sig_qs.cont;
+ if (!sig)
+ return 0; /* No signals to process available... */
+ if (ERTS_SIG_IS_MSG(sig) && tp->messages.next != &c_p->sig_qs.cont)
+ return 0;
+ }
+
+ lim += ERTS_SIG_REDS_CNT_FACTOR*100;
+ if (lim > abs_lim)
+ lim = abs_lim;
+ *limp = lim;
+ return !0;
+}
+
+
+int
+erts_proc_sig_handle_exit(Process *c_p, int *redsp)
+{
+ int cnt, limit;
+ ErtsMessage *sig, ***next_nm_sig;
+
+ ERTS_HDBG_CHECK_SIGNAL_PRIV_QUEUE(c_p);
+ ERTS_LC_ASSERT(!erts_proc_lc_my_proc_locks(c_p));
+
+ ASSERT(!(ERTS_PSFLG_SIG_IN_Q & erts_atomic32_read_nob(&c_p->state)));
+
+ limit = *redsp;
+ limit *= ERTS_SIG_REDS_CNT_FACTOR;
+
+ *redsp = 1;
+
+ next_nm_sig = &c_p->sig_qs.nmsigs.next;
+
+ if (!*next_nm_sig) {
+ ASSERT(!c_p->sig_qs.nmsigs.last);
+ return !0; /* done... */
+ }
+
+ cnt = 0;
+
+ ERTS_PROC_SIG_HDBG_PRIV_CHKQ(c_p, NULL, next_nm_sig);
+
+ do {
+ Eterm tag;
+ Uint16 type;
+ int op;
+
+ sig = **next_nm_sig;
+
+ ASSERT(sig);
+ ASSERT(ERTS_SIG_IS_NON_MSG(sig));
+
+ tag = ((ErtsSignal *) sig)->common.tag;
+ type = ERTS_PROC_SIG_TYPE(tag);
+ op = ERTS_PROC_SIG_OP(tag);
+
+ remove_nm_sig(c_p, sig, next_nm_sig);
+
+ ERTS_PROC_SIG_HDBG_PRIV_CHKQ(c_p, NULL, next_nm_sig);
+
+ cnt++;
+
+ switch (op) {
+
+ case ERTS_SIG_Q_OP_EXIT:
+ case ERTS_SIG_Q_OP_EXIT_LINKED:
+ case ERTS_SIG_Q_OP_MONITOR_DOWN:
+ switch (type) {
+ case ERTS_SIG_Q_TYPE_GEN_EXIT:
+ sig->next = NULL;
+ erts_cleanup_messages(sig);
+ break;
+ case ERTS_LNK_TYPE_PORT:
+ case ERTS_LNK_TYPE_PROC:
+ case ERTS_LNK_TYPE_DIST_PROC:
+ erts_link_release((ErtsLink *) sig);
+ break;
+ case ERTS_MON_TYPE_PORT:
+ case ERTS_MON_TYPE_PROC:
+ case ERTS_MON_TYPE_DIST_PROC:
+ case ERTS_MON_TYPE_NODE:
+ erts_monitor_release((ErtsMonitor *) sig);
+ break;
+ default:
+ ERTS_INTERNAL_ERROR("Unexpected sig type");
+ break;
+ }
+ break;
+
+ case ERTS_SIG_Q_OP_PERSISTENT_MON_MSG:
+ sig->next = NULL;
+ erts_cleanup_messages(sig);
+ break;
+
+ case ERTS_SIG_Q_OP_MONITOR: {
+ ErtsProcExitContext pectxt = {c_p, am_noproc};
+ erts_proc_exit_handle_monitor((ErtsMonitor *) sig,
+ (void *) &pectxt);
+ cnt += 4;
+ break;
+ }
+
+ case ERTS_SIG_Q_OP_DEMONITOR:
+ if (type == ERTS_SIG_Q_TYPE_DIST_PROC_DEMONITOR)
+ destroy_dist_proc_demonitor((ErtsSigDistProcDemonitor *) sig);
+ else
+ erts_monitor_release((ErtsMonitor *) sig);
+ break;
+
+ case ERTS_SIG_Q_OP_LINK: {
+ ErtsProcExitContext pectxt = {c_p, am_noproc};
+ erts_proc_exit_handle_link((ErtsLink *) sig, (void *) &pectxt);
+ break;
+ }
+
+ case ERTS_SIG_Q_OP_UNLINK:
+ if (type == ERTS_SIG_Q_TYPE_DIST_LINK)
+ destroy_sig_dist_link_op((ErtsSigDistLinkOp *) sig);
+ else
+ erts_link_release((ErtsLink *) sig);
+ break;
+
+ case ERTS_SIG_Q_OP_GROUP_LEADER: {
+ ErtsSigGroupLeader *sgl = (ErtsSigGroupLeader *) sig;
+ handle_group_leader(c_p, sgl);
+ break;
+ }
+
+ case ERTS_SIG_Q_OP_IS_ALIVE:
+ ERTS_PROC_SIG_HDBG_PRIV_CHKQ(c_p, &tracing, next_nm_sig);
+ is_alive_response(c_p, sig, 0);
+ ERTS_PROC_SIG_HDBG_PRIV_CHKQ(c_p, &tracing, next_nm_sig);
+ break;
+
+ case ERTS_SIG_Q_OP_TRACE_CHANGE_STATE:
+ destroy_trace_info((ErtsSigTraceInfo *) sig);
+ break;
+
+ default:
+ ERTS_INTERNAL_ERROR("Unknown signal");
+ break;
+ }
+
+ } while (cnt >= limit && *next_nm_sig);
+
+ *redsp += cnt / ERTS_SIG_REDS_CNT_FACTOR;
+
+ if (*next_nm_sig)
+ return 0;
+
+ ASSERT(!c_p->sig_qs.nmsigs.next);
+ c_p->sig_qs.nmsigs.last = NULL;
+ (void) erts_atomic32_read_band_nob(&c_p->state,
+ ~ERTS_PSFLG_SIG_Q);
+ return !0;
+}
+
+#ifdef USE_VM_PROBES
+# define ERTS_CLEAR_SEQ_TOKEN(MP) \
+ ERL_MESSAGE_TOKEN((MP)) = ((ERL_MESSAGE_DT_UTAG((MP)) != NIL) \
+ ? am_have_dt_utag : NIL)
+#else
+# define ERTS_CLEAR_SEQ_TOKEN(MP) \
+ ERL_MESSAGE_TOKEN((MP)) = NIL
+#endif
+
+static ERTS_INLINE void
+clear_seq_trace_token(ErtsMessage *sig)
+{
+ if (ERTS_SIG_IS_MSG((ErtsSignal *) sig))
+ ERTS_CLEAR_SEQ_TOKEN(sig);
+ else {
+ Uint tag;
+ Uint16 op, type;
+
+ tag = ((ErtsSignal *) sig)->common.tag;
+ type = ERTS_PROC_SIG_TYPE(tag);
+ op = ERTS_PROC_SIG_OP(tag);
+
+ switch (op) {
+
+ case ERTS_SIG_Q_OP_EXIT:
+ case ERTS_SIG_Q_OP_EXIT_LINKED:
+ case ERTS_SIG_Q_OP_MONITOR_DOWN:
+ switch (type) {
+ case ERTS_SIG_Q_TYPE_GEN_EXIT:
+ ERTS_CLEAR_SEQ_TOKEN(sig);
+ break;
+ case ERTS_LNK_TYPE_PORT:
+ case ERTS_LNK_TYPE_PROC:
+ case ERTS_LNK_TYPE_DIST_PROC:
+ case ERTS_MON_TYPE_PORT:
+ case ERTS_MON_TYPE_PROC:
+ case ERTS_MON_TYPE_DIST_PROC:
+ case ERTS_MON_TYPE_NODE:
+ break;
+ default:
+ ERTS_INTERNAL_ERROR("Unexpected sig type");
+ break;
+ }
+ break;
+
+ case ERTS_SIG_Q_OP_PERSISTENT_MON_MSG:
+ ERTS_CLEAR_SEQ_TOKEN(sig);
+ break;
+
+ case ERTS_SIG_Q_OP_MONITOR:
+ case ERTS_SIG_Q_OP_DEMONITOR:
+ case ERTS_SIG_Q_OP_LINK:
+ case ERTS_SIG_Q_OP_UNLINK:
+ case ERTS_SIG_Q_OP_TRACE_CHANGE_STATE:
+ break;
+
+ default:
+ ERTS_INTERNAL_ERROR("Unknown signal");
+ break;
+ }
+ }
+}
+
+void
+erts_proc_sig_clear_seq_trace_tokens(Process *c_p)
+{
+ ASSERT(erts_thr_progress_is_blocking());
+ erts_proc_sig_fetch(c_p);
+ ERTS_FOREACH_SIG_PRIVQS(c_p, sig, clear_seq_trace_token(sig));
+}
+
+Uint
+erts_proc_sig_signal_size(ErtsSignal *sig)
+{
+ Eterm tag;
+ Uint16 type;
+ int op;
+ Uint size = 0;
+
+ ASSERT(sig);
+ ASSERT(ERTS_SIG_IS_NON_MSG(sig));
+
+ tag = sig->common.tag;
+ type = ERTS_PROC_SIG_TYPE(tag);
+ op = ERTS_PROC_SIG_OP(tag);
+
+ switch (op) {
+ case ERTS_SIG_Q_OP_EXIT:
+ case ERTS_SIG_Q_OP_EXIT_LINKED:
+ case ERTS_SIG_Q_OP_MONITOR_DOWN:
+ switch (type) {
+ case ERTS_SIG_Q_TYPE_GEN_EXIT:
+ size = ((ErtsMessage *) sig)->hfrag.alloc_size;
+ size *= sizeof(Eterm);
+ size += sizeof(ErtsMessage) - sizeof(Eterm);
+ break;
+ case ERTS_LNK_TYPE_PORT:
+ case ERTS_LNK_TYPE_PROC:
+ case ERTS_LNK_TYPE_DIST_PROC:
+ size = erts_link_size((ErtsLink *) sig);
+ break;
+ case ERTS_MON_TYPE_PORT:
+ case ERTS_MON_TYPE_PROC:
+ case ERTS_MON_TYPE_DIST_PROC:
+ case ERTS_MON_TYPE_NODE:
+ size = erts_monitor_size((ErtsMonitor *) sig);
+ default:
+ ERTS_INTERNAL_ERROR("Unexpected sig type");
+ break;
+ }
+ break;
+
+ case ERTS_SIG_Q_OP_PERSISTENT_MON_MSG:
+ case ERTS_SIG_Q_OP_IS_ALIVE:
+ size = ((ErtsMessage *) sig)->hfrag.alloc_size;
+ size *= sizeof(Eterm);
+ size += sizeof(ErtsMessage) - sizeof(Eterm);
+ break;
+
+ case ERTS_SIG_Q_OP_DEMONITOR:
+ if (type == ERTS_SIG_Q_TYPE_DIST_PROC_DEMONITOR) {
+ size = NC_HEAP_SIZE(((ErtsSigDistProcDemonitor *) sig)->ref);
+ size--;
+ size *= sizeof(Eterm);
+ size += sizeof(ErtsSigDistProcDemonitor);
+ break;
+ }
+ /* Fall through... */
+
+ case ERTS_SIG_Q_OP_MONITOR:
+ size = erts_monitor_size((ErtsMonitor *) sig);
+ break;
+
+ case ERTS_SIG_Q_OP_UNLINK:
+ if (type == ERTS_SIG_Q_TYPE_DIST_LINK) {
+ size = NC_HEAP_SIZE(((ErtsSigDistLinkOp *) sig)->remote);
+ size--;
+ size *= sizeof(Eterm);
+ size += sizeof(ErtsSigDistLinkOp);
+ break;
+ }
+ /* Fall through... */
+
+ case ERTS_SIG_Q_OP_LINK:
+ size = erts_link_size((ErtsLink *) sig);
+ break;
+
+ case ERTS_SIG_Q_OP_GROUP_LEADER: {
+ ErtsSigGroupLeader *sgl = (ErtsSigGroupLeader *) sig;
+ size = size_object(sgl->group_leader);
+ size += size_object(sgl->ref);
+ size *= sizeof(Eterm);
+ size += sizeof(ErtsSigGroupLeader) - sizeof(Eterm);
+ break;
+ }
+
+ case ERTS_SIG_Q_OP_TRACE_CHANGE_STATE:
+ size = sizeof(ErtsSigTraceInfo);
+ break;
+
+ default:
+ ERTS_INTERNAL_ERROR("Unknown signal");
+ break;
+ }
+
+ return size;
+}
+
+int
+erts_proc_sig_receive_helper(Process *c_p,
+ int fcalls,
+ int neg_o_reds,
+ ErtsMessage **msgpp,
+ int *get_outp)
+{
+ ErtsMessage *msgp;
+ int reds, consumed_reds, left_reds, max_reds;
+
+ /*
+ * Called from the loop-rec instruction when receive
+ * has reached end of inner (private) queue. This function
+ * tries to move more messages into the inner queue
+ * for the receive to handle. This by, processing the
+ * middle (private) queue and/or moving signals from
+ * the outer (public) queue into the middle queue.
+ *
+ * If this function succeeds in making more messages
+ * available in the inner queue, *msgpp points to next
+ * message. If *msgpp is set to NULL when:
+ * -- process became exiting. *get_outp is set to a
+ * value greater than zero.
+ * -- process needs to yield. *get_outp is set to a
+ * value less than zero.
+ * -- no more messages exist in any of the queues.
+ * *get_outp is set to zero and the message queue
+ * lock remains locked. This so the process can
+ * make its way to the appropriate wait instruction
+ * without racing with new incoming messages.
+ */
+
+ ASSERT(erts_proc_lc_my_proc_locks(c_p) == ERTS_PROC_LOCK_MAIN);
+ ASSERT(!ERTS_PROC_IS_EXITING(c_p));
+ ASSERT(!*msgpp);
+
+ left_reds = fcalls - neg_o_reds;
+ consumed_reds = 0;
+
+ while (!0) {
+ erts_aint32_t state;
+
+ if (!c_p->sig_qs.cont) {
+
+ consumed_reds += 4;
+ left_reds -= 4;
+ erts_proc_lock(c_p, ERTS_PROC_LOCK_MSGQ);
+ if (c_p->sig_inq.first)
+ erts_proc_sig_fetch(c_p);
+ /*
+ * Messages may have been moved directly to
+ * inner queue...
+ */
+ msgp = PEEK_MESSAGE(c_p);
+ if (msgp) {
+ erts_proc_unlock(c_p, ERTS_PROC_LOCK_MSGQ);
+ *get_outp = 0;
+ *msgpp = msgp;
+ return consumed_reds;
+ }
+
+ if (!c_p->sig_qs.cont) {
+ /*
+ * No messages! Return with message queue
+ * locked and let the process continue
+ * to wait instruction...
+ */
+ *get_outp = 0;
+ *msgpp = NULL;
+ return consumed_reds;
+ }
+ erts_proc_unlock(c_p, ERTS_PROC_LOCK_MSGQ);
+
+ if (left_reds <= 0) {
+ *get_outp = -1; /* yield */
+ *msgpp = NULL;
+
+ ASSERT(consumed_reds >= (fcalls - neg_o_reds));
+ return consumed_reds;
+ }
+
+ /* handle newly arrived signals... */
+ }
+
+ reds = ERTS_SIG_HANDLE_REDS_MAX_PREFERED;
+#ifdef DEBUG
+ /* test that it works also with very few reds */
+ max_reds = left_reds;
+ if (reds > left_reds)
+ reds = left_reds;
+#else
+ /* At least work preferred amount of reds... */
+ max_reds = left_reds;
+ if (max_reds < reds)
+ max_reds = reds;
+#endif
+ (void) erts_proc_sig_handle_incoming(c_p, &state, &reds,
+ max_reds, !0);
+ consumed_reds += reds;
+ left_reds -= reds;
+ /* we may have exited by an incoming signal... */
+ if (state & ERTS_PSFLG_EXITING) {
+ /*
+ * Process need to schedule out in order
+ * to terminate. Prepare this a bit...
+ */
+ ASSERT(c_p->flags & F_DELAY_GC);
+
+ c_p->flags &= ~F_DELAY_GC;
+ c_p->arity = 0;
+ c_p->current = NULL;
+ *get_outp = 1;
+ *msgpp = NULL;
+ return consumed_reds;
+ }
+
+ msgp = PEEK_MESSAGE(c_p);
+ if (msgp) {
+ *get_outp = 0;
+ *msgpp = msgp;
+ return consumed_reds;
+ }
+
+ if (left_reds <= 0) {
+ *get_outp = -1; /* yield */
+ *msgpp = NULL;
+
+ ASSERT(consumed_reds >= (fcalls - neg_o_reds));
+ return consumed_reds;
+ }
+
+ ASSERT(!c_p->sig_qs.cont);
+ /* Go fetch again... */
+ }
+}
+
+static int
+handle_trace_change_state(Process *c_p,
+ ErtsSigRecvTracing *tracing,
+ Uint16 type,
+ ErtsMessage *sig,
+ ErtsMessage ***next_nm_sig)
+{
+ ErtsSigTraceInfo *trace_info = (ErtsSigTraceInfo *) sig;
+ ErtsMessage **next = *next_nm_sig;
+ int msgs_active, old_msgs_active = !!tracing->messages.active;
+
+ ASSERT(sig == *next);
+
+ erts_proc_lock(c_p, ERTS_PROC_LOCKS_ALL_MINOR);
+
+ ERTS_TRACE_FLAGS(c_p) |= trace_info->flags_on;
+ ERTS_TRACE_FLAGS(c_p) &= ~trace_info->flags_off;
+ if (is_value(trace_info->tracer))
+ erts_tracer_replace(&c_p->common, trace_info->tracer);
+
+ erts_proc_unlock(c_p, ERTS_PROC_LOCKS_ALL_MINOR);
+
+ remove_nm_sig(c_p, sig, next_nm_sig);
+ destroy_trace_info(trace_info);
+ /*
+ * Adjust tracing state according to modifications made by
+ * the trace info signal...
+ */
+ adjust_tracing_state(c_p, tracing, 0);
+ msgs_active = !!tracing->messages.active;
+
+ if (old_msgs_active ^ msgs_active) {
+ if (msgs_active) {
+ ASSERT(!tracing->messages.next);
+ tracing->messages.next = next;
+ }
+ else {
+ ASSERT(tracing->messages.next);
+ tracing->messages.next = NULL;
+ }
+ }
+
+ ASSERT(!msgs_active || tracing->messages.next);
+
+ return msgs_active;
+}
+
+static void
+getting_unlinked(Process *c_p, Eterm unlinker)
+{
+ trace_proc(c_p, ERTS_PROC_LOCK_MAIN, c_p,
+ am_getting_unlinked, unlinker);
+}
+
+static void
+getting_linked(Process *c_p, Eterm linker)
+{
+ trace_proc(c_p, ERTS_PROC_LOCK_MAIN, c_p,
+ am_getting_linked, linker);
+}
+
+static ERTS_INLINE void
+handle_message_enqueued_tracing(Process *c_p,
+ ErtsSigRecvTracing *tracing,
+ ErtsMessage *msg)
+{
+ ASSERT(ERTS_SIG_IS_INTERNAL_MSG(msg));
+
+#if defined(USE_VM_PROBES)
+ if (tracing->messages.vm_probes && DTRACE_ENABLED(message_queued)) {
+ Sint tok_label = 0;
+ Sint tok_lastcnt = 0;
+ Sint tok_serial = 0;
+ Eterm seq_trace_token = ERL_MESSAGE_TOKEN(msg);
+
+ if (seq_trace_token != NIL && is_tuple(seq_trace_token)) {
+ tok_label = signed_val(SEQ_TRACE_T_LABEL(seq_trace_token));
+ tok_lastcnt = signed_val(SEQ_TRACE_T_LASTCNT(seq_trace_token));
+ tok_serial = signed_val(SEQ_TRACE_T_SERIAL(seq_trace_token));
+ }
+ /* Message intentionally not passed... */
+ DTRACE6(message_queued,
+ tracing->messages.receiver_name,
+ size_object(ERL_MESSAGE_TERM(msg)),
+ c_p->sig_qs.len,
+ tok_label, tok_lastcnt, tok_serial);
+ }
+#endif
+
+ if (tracing->messages.receive_trace && tracing->messages.event->on) {
+ ASSERT(IS_TRACED(c_p));
+ trace_receive(c_p,
+ ERL_MESSAGE_FROM(msg),
+ ERL_MESSAGE_TERM(msg),
+ tracing->messages.event);
+ }
+}
+
+static int
+handle_msg_tracing(Process *c_p, ErtsSigRecvTracing *tracing,
+ ErtsMessage ***next_nm_sig)
+{
+ ErtsMessage **next_sig, *sig;
+ int cnt = 0, limit = ERTS_PROC_SIG_TRACE_COUNT_LIMIT;
+
+ ASSERT(tracing->messages.next);
+ next_sig = tracing->messages.next;
+ sig = *next_sig;
+
+ /*
+ * Receive tracing active. Handle all messages
+ * until next non-message signal...
+ */
+
+ while (sig && ERTS_SIG_IS_MSG(sig)) {
+ if (cnt > limit) {
+ tracing->messages.next = next_sig;
+ return -1; /* Yield... */
+ }
+ if (ERTS_SIG_IS_EXTERNAL_MSG(sig)) {
+ cnt++;
+ if (!erts_decode_dist_message(c_p, ERTS_PROC_LOCK_MAIN,
+ sig, 0)) {
+ /* Bad dist message; remove it... */
+ remove_mq_m_sig(c_p, sig, next_sig, next_nm_sig);
+ sig = *next_sig;
+ continue;
+ }
+ }
+ handle_message_enqueued_tracing(c_p, tracing, sig);
+ cnt++;
+
+ next_sig = &(*next_sig)->next;
+ sig = *next_sig;
+ }
+
+ tracing->messages.next = next_sig;
+
+ if (!sig) {
+ ASSERT(!*next_nm_sig);
+ return 1; /* end... */
+ }
+
+ ASSERT(*next_nm_sig);
+ ASSERT(**next_nm_sig == sig);
+
+ /* Next signal a non-message signal... */
+ ASSERT(ERTS_SIG_IS_NON_MSG(sig));
+
+ /*
+ * Return and handle the non-message signal...
+ */
+
+ return 0;
+}
+
+/*
+ * ERTS_INSPECT_MSGQ_KEEP_OH_MSGS == 0 will move off heap messages
+ * into the heap of the inspected process if off_heap_message_queue
+ * is false when process_info(_, messages) is called. That is, the
+ * following GC will have more data in the rootset compared to the
+ * scenario when process_info(_, messages) had not been called.
+ *
+ * ERTS_INSPECT_MSGQ_KEEP_OH_MSGS != 0 will keep off heap messages
+ * off heap when process_info(_, messages) is called regardless of
+ * the off_heap_message_queue setting of the process. That is, it
+ * will change the following execution of the process as little as
+ * possible.
+ */
+#define ERTS_INSPECT_MSGQ_KEEP_OH_MSGS 1
+
+Uint
+erts_proc_sig_prep_msgq_for_inspection(Process *c_p, Process *rp,
+ ErtsProcLocks rp_locks,
+ ErtsMessageInfo *mip)
+{
+ Uint tot_heap_size;
+ ErtsMessage *mp, **mpp;
+ Sint i;
+ int self_on_heap;
+
+ /*
+ * Prepare the message queue for inspection
+ * by process_info().
+ *
+ *
+ * - Decode all messages on external format
+ * - Remove all corrupt dist messages from queue
+ * - Save pointer to, and heap size need of each
+ * message in the mip array.
+ * - Return total heap size need for all messages
+ * that needs to be copied.
+ *
+ * If ERTS_INSPECT_MSGQ_KEEP_OH_MSGS == 0:
+ * - In case off heap messages is disabled and
+ * we are inspecting our own queue, move all
+ * off heap data into the heap.
+ */
+
+ /*
+ * All non-message signals *need* to have been
+ * handled before calling this functions...
+ */
+ ASSERT(!rp->sig_qs.cont);
+ ASSERT(!rp->sig_qs.nmsigs.next && !rp->sig_qs.nmsigs.last);
+
+ self_on_heap = c_p == rp && !(c_p->flags & F_OFF_HEAP_MSGQ);
+
+ tot_heap_size = 0;
+ i = 0;
+ mpp = &rp->sig_qs.first;
+ mp = rp->sig_qs.first;
+ while (mp) {
+ Eterm msg = ERL_MESSAGE_TERM(mp);
+
+ mip[i].size = 0;
+
+ if (ERTS_SIG_IS_EXTERNAL_MSG(mp)) {
+ /* decode it... */
+ if (mp->data.attached)
+ erts_decode_dist_message(rp, rp_locks, mp,
+ ERTS_INSPECT_MSGQ_KEEP_OH_MSGS);
+
+ msg = ERL_MESSAGE_TERM(mp);
+
+ if (is_non_value(msg)) {
+ ErtsMessage *bad_mp = mp;
+ /*
+ * Bad distribution message; remove
+ * it from the queue...
+ */
+ ASSERT(!mp->data.attached);
+
+ ASSERT(*mpp == bad_mp);
+
+ remove_iq_m_sig(rp, mp, mpp);
+
+ mp = *mpp;
+
+ bad_mp->next = NULL;
+ erts_cleanup_messages(bad_mp);
+ continue;
+ }
+ }
+
+ ASSERT(is_value(msg));
+
+#if ERTS_INSPECT_MSGQ_KEEP_OH_MSGS
+ if (is_not_immed(msg) && (!self_on_heap || mp->data.attached)) {
+ Uint sz = size_object(msg);
+ mip[i].size = sz;
+ tot_heap_size += sz;
+ }
+#else
+ if (self_on_heap) {
+ if (mp->data.attached) {
+ ErtsMessage *tmp = NULL;
+ if (mp->data.attached != ERTS_MSG_COMBINED_HFRAG) {
+ erts_link_mbuf_to_proc(rp, mp->data.heap_frag);
+ mp->data.attached = NULL;
+ }
+ else {
+ /*
+ * Need to replace the message reference since
+ * we will get references to the message data
+ * from the heap...
+ */
+ ErtsMessage **mpp;
+ tmp = erts_alloc_message(0, NULL);
+ sys_memcpy((void *) tmp->m, (void *) mp->m,
+ sizeof(Eterm)*ERL_MESSAGE_REF_ARRAY_SZ);
+ mpp = i == 0 ? &rp->sig_qs.first : &mip[i-1].msgp->next;
+ erts_msgq_replace_msg_ref(&rp->msg, tmp, mpp);
+ erts_save_message_in_proc(rp, mp);
+ mp = tmp;
+ }
+ }
+ }
+ else if (is_not_immed(msg)) {
+ Uint sz = size_object(msg);
+ mip[i].size = sz;
+ tot_heap_size += sz;
+ }
+
+#endif
+
+ mip[i].msgp = mp;
+ i++;
+ mpp = &mp->next;
+ mp = mp->next;
+ }
+
+ return tot_heap_size;
+}
+
+static ERTS_INLINE void
+move_msg_to_heap(Process *c_p, ErtsMessage *mp)
+{
+ /*
+ * We leave not yet decoded distribution messages
+ * as they are in the queue since it is not
+ * possible to determine a maximum size until
+ * actual decoding...
+ *
+ * We also leave combined messages as they are...
+ */
+ if (ERTS_SIG_IS_INTERNAL_MSG(mp)
+ && mp->data.attached
+ && mp->data.attached != ERTS_MSG_COMBINED_HFRAG) {
+ ErlHeapFragment *bp;
+
+ bp = erts_message_to_heap_frag(mp);
+
+ if (bp->next)
+ erts_move_multi_frags(&c_p->htop, &c_p->off_heap, bp,
+ mp->m, ERL_MESSAGE_REF_ARRAY_SZ, 0);
+ else
+ erts_copy_one_frag(&c_p->htop, &c_p->off_heap, bp,
+ mp->m, ERL_MESSAGE_REF_ARRAY_SZ);
+
+ mp->data.heap_frag = NULL;
+ free_message_buffer(bp);
+ }
+}
+
+void
+erts_proc_sig_move_msgs_to_heap(Process *c_p)
+{
+ ERTS_HDBG_CHECK_SIGNAL_PRIV_QUEUE(c_p);
+
+ ERTS_FOREACH_SIG_PRIVQS(c_p, sig, move_msg_to_heap(c_p, sig));
+
+ ERTS_HDBG_CHECK_SIGNAL_PRIV_QUEUE(c_p);
+}
+
+
+BIF_RETTYPE
+erts_internal_dirty_process_handle_signals_1(BIF_ALIST_1)
+{
+ erts_aint32_t state, dirty, noproc;
+ int busy;
+ Process *rp;
+
+ if (BIF_P != erts_dirty_process_signal_handler
+ && BIF_P != erts_dirty_process_signal_handler_high
+ && BIF_P != erts_dirty_process_signal_handler_max)
+ BIF_ERROR(BIF_P, EXC_NOTSUP);
+
+ if (is_not_internal_pid(BIF_ARG_1))
+ BIF_RET(am_false);
+
+ rp = erts_proc_lookup_raw(BIF_ARG_1);
+ if (!rp)
+ BIF_RET(am_noproc);
+
+ state = erts_atomic32_read_nob(&rp->state);
+ dirty = (state & (ERTS_PSFLG_DIRTY_RUNNING
+ | ERTS_PSFLG_DIRTY_RUNNING_SYS));
+ if (!dirty)
+ BIF_RET(am_normal);
+
+ busy = erts_proc_trylock(rp, ERTS_PROC_LOCK_MAIN) == EBUSY;
+
+ state = erts_atomic32_read_mb(&rp->state);
+ noproc = (state & ERTS_PSFLG_FREE);
+ dirty = (state & (ERTS_PSFLG_DIRTY_RUNNING
+ | ERTS_PSFLG_DIRTY_RUNNING_SYS));
+
+ if (busy) {
+ if (noproc)
+ BIF_RET(am_noproc);
+ if (dirty)
+ BIF_RET(am_more); /* try again... */
+ BIF_RET(am_normal); /* will handle signals itself... */
+ }
+ else {
+ erts_aint32_t state;
+ int done;
+ Eterm res = am_false;
+ int reds = 0;
+
+ if (noproc)
+ res = am_noproc;
+ else if (!dirty)
+ res = am_normal; /* will handle signals itself... */
+ else {
+ reds = ERTS_BIF_REDS_LEFT(BIF_P);
+ done = erts_proc_sig_handle_incoming(rp, &state, &reds,
+ reds, 0);
+ if (done || (state & ERTS_PSFLG_EXITING))
+ res = am_ok;
+ else
+ res = am_more;
+ }
+
+ erts_proc_unlock(rp, ERTS_PROC_LOCK_MAIN);
+
+ if (reds)
+ BUMP_REDS(BIF_P, reds);
+
+ BIF_RET(res);
+ }
+}
+
+static void
+debug_foreach_sig_heap_frags(ErlHeapFragment *hfrag,
+ void (*oh_func)(ErlOffHeap *, void *),
+ void *arg)
+{
+ ErlHeapFragment *hf = hfrag;
+ while (hf) {
+ oh_func(&(hf->off_heap), arg);
+ hf = hf->next;
+ }
+}
+
+static void
+debug_foreach_sig_fake_oh(Eterm term,
+ void (*oh_func)(ErlOffHeap *, void *),
+ void *arg)
+{
+ if (is_external(term)) {
+ ErlOffHeap oh;
+ oh.overhead = 0;
+ oh.first = ((struct erl_off_heap_header *)
+ (char *) external_thing_ptr(term));
+ ASSERT(!oh.first->next);
+ oh_func(&oh, arg);
+ }
+
+}
+
+void
+erts_proc_sig_debug_foreach_sig(Process *c_p,
+ void (*msg_func)(ErtsMessage *, void *),
+ void (*oh_func)(ErlOffHeap *, void *),
+ void (*mon_func)(ErtsMonitor *, void *),
+ void (*lnk_func)(ErtsLink *, void *),
+ void *arg)
+{
+ ErtsMessage *queue[] = {c_p->sig_qs.first, c_p->sig_qs.cont, c_p->sig_inq.first};
+ int qix;
+
+ for (qix = 0; qix < sizeof(queue)/sizeof(queue[0]); qix++) {
+ ErtsMessage *sig;
+ for (sig = queue[qix]; sig; sig = sig->next) {
+
+ if (ERTS_SIG_IS_MSG(sig))
+ msg_func(sig, arg);
+ else {
+ Eterm tag;
+ Uint16 type;
+ int op;
+
+ ASSERT(sig);
+ ASSERT(ERTS_SIG_IS_NON_MSG(sig));
+
+ tag = ((ErtsSignal *) sig)->common.tag;
+ type = ERTS_PROC_SIG_TYPE(tag);
+ op = ERTS_PROC_SIG_OP(tag);
+
+ switch (op) {
+
+ case ERTS_SIG_Q_OP_EXIT:
+ case ERTS_SIG_Q_OP_EXIT_LINKED:
+ case ERTS_SIG_Q_OP_MONITOR_DOWN:
+ switch (type) {
+ case ERTS_SIG_Q_TYPE_GEN_EXIT:
+ debug_foreach_sig_heap_frags(&sig->hfrag, oh_func, arg);
+ break;
+ case ERTS_LNK_TYPE_PORT:
+ case ERTS_LNK_TYPE_PROC:
+ case ERTS_LNK_TYPE_DIST_PROC:
+ lnk_func((ErtsLink *) sig, arg);
+ break;
+ case ERTS_MON_TYPE_PORT:
+ case ERTS_MON_TYPE_PROC:
+ case ERTS_MON_TYPE_DIST_PROC:
+ case ERTS_MON_TYPE_NODE:
+ mon_func((ErtsMonitor *) sig, arg);
+ break;
+ default:
+ ERTS_INTERNAL_ERROR("Unexpected sig type");
+ break;
+ }
+ break;
+
+ case ERTS_SIG_Q_OP_PERSISTENT_MON_MSG:
+ debug_foreach_sig_heap_frags(&sig->hfrag, oh_func, arg);
+ break;
+
+ case ERTS_SIG_Q_OP_DEMONITOR:
+ if (type == ERTS_SIG_Q_TYPE_DIST_PROC_DEMONITOR) {
+ debug_foreach_sig_fake_oh(((ErtsSigDistProcDemonitor *) sig)->ref,
+ oh_func, arg);
+ break;
+ }
+ /* Fall through... */
+
+ case ERTS_SIG_Q_OP_MONITOR:
+ mon_func((ErtsMonitor *) sig, arg);
+ break;
+
+ case ERTS_SIG_Q_OP_UNLINK:
+ if (type == ERTS_SIG_Q_TYPE_DIST_LINK) {
+ debug_foreach_sig_fake_oh(((ErtsSigDistLinkOp *) sig)->remote,
+ oh_func, arg);
+ break;
+ }
+ /* Fall through... */
+
+ case ERTS_SIG_Q_OP_LINK:
+ lnk_func((ErtsLink *) sig, arg);
+ break;
+
+ case ERTS_SIG_Q_OP_GROUP_LEADER: {
+ ErtsSigGroupLeader *sgl = (ErtsSigGroupLeader *) sig;
+ oh_func(&sgl->oh, arg);
+ break;
+ }
+
+ case ERTS_SIG_Q_OP_IS_ALIVE:
+ case ERTS_SIG_Q_OP_TRACE_CHANGE_STATE:
+ break;
+
+ default:
+ ERTS_INTERNAL_ERROR("Unknown signal");
+ break;
+ }
+
+ }
+ }
+ }
+}
+
+#ifdef ERTS_PROC_SIG_HARD_DEBUG
+
+static void
+chk_eterm(Process *c_p, int privq, ErtsMessage *mp, Eterm term)
+{
+ ErlHeapFragment *bp;
+ Eterm *ptr = NULL;
+
+ switch (primary_tag(term)) {
+ case TAG_PRIMARY_IMMED1:
+ return;
+ case TAG_PRIMARY_LIST:
+ ptr = list_val(term);
+ ERTS_ASSERT(!is_header(CAR(ptr)));
+ ERTS_ASSERT(!is_header(CDR(ptr)));
+ break;
+ case TAG_PRIMARY_BOXED:
+ ptr = boxed_val(term);
+ ERTS_ASSERT(is_header(*ptr));
+ break;
+ case TAG_PRIMARY_HEADER:
+ default:
+ ERTS_INTERNAL_ERROR("Not valid term");
+ break;
+ }
+
+ if (erts_is_literal(term, ptr))
+ return;
+
+ for (bp = erts_message_to_heap_frag(mp); bp; bp = bp->next) {
+ if (bp->mem <= ptr && ptr < bp->mem + bp->used_size)
+ return;
+ bp = bp->next;
+ }
+
+ ERTS_ASSERT(privq);
+ ASSERT(erts_dbg_within_proc(ptr, c_p, NULL));
+}
+
+static Sint
+proc_sig_hdbg_check_queue(Process *proc,
+ int privq,
+ ErtsMessage **sig_next,
+ ErtsMessage **sig_last,
+ ErtsMessage **sig_nm_next,
+ ErtsMessage **sig_nm_last,
+ ErtsSigRecvTracing *tracing,
+ int *found_saved_last_p,
+ erts_aint32_t sig_psflg)
+{
+ ErtsMessage **next, *sig, **nm_next, **nm_last;
+ int last_nm_sig_found, nm_sigs = 0, found_next_trace = 0,
+ found_save = 0, last_sig_found = 0, found_saved_last = 0;
+ Sint msg_len = 0;
+ ErtsMessage **next_trace = tracing ? tracing->messages.next : NULL;
+ ErtsMessage **save = proc->sig_qs.save;
+ ErtsMessage **saved_last = proc->sig_qs.saved_last;
+
+
+ nm_next = sig_nm_next;
+ nm_last = sig_nm_last;
+ next = sig_next;
+ sig = *sig_next;
+
+ last_nm_sig_found = !nm_last;
+ if (last_nm_sig_found)
+ ERTS_ASSERT(!nm_next);
+ else
+ ERTS_ASSERT(nm_next);
+
+ while (1) {
+ ErtsSignal *nm_sig;
+
+ if (next == sig_last) {
+ ASSERT(!*next);
+ last_sig_found = 1;
+ }
+
+ if (next == save)
+ found_save = 1;
+
+ if (next == saved_last)
+ found_saved_last = 1;
+
+ if (next == next_trace) {
+ found_next_trace = 1;
+ ERTS_ASSERT(nm_sigs == 0);
+ }
+
+ while (sig && ERTS_SIG_IS_MSG(sig)) {
+ int i;
+ if (ERTS_SIG_IS_EXTERNAL_MSG(sig))
+ i = 1;
+ else
+ i = 0;
+ for (; i < ERL_MESSAGE_REF_ARRAY_SZ; i++)
+ chk_eterm(proc, privq, sig, sig->m[i]);
+
+ msg_len++;
+ next = &sig->next;
+ sig = sig->next;
+
+ if (next == sig_last) {
+ ASSERT(!*next);
+ last_sig_found = 1;
+ }
+
+ if (next == save)
+ found_save = 1;
+
+ if (next == saved_last)
+ found_saved_last = 1;
+
+ if (next == next_trace) {
+ found_next_trace = 1;
+ ERTS_ASSERT(nm_sigs == 0);
+ }
+ }
+
+ if (!sig)
+ break;
+
+ nm_sigs++;
+
+ ERTS_ASSERT(!last_nm_sig_found);
+ ERTS_ASSERT(ERTS_SIG_IS_NON_MSG(sig));
+
+ nm_sig = (ErtsSignal *) sig;
+
+ ERTS_ASSERT(nm_next == next);
+
+ if (nm_last == next) {
+ ASSERT(!nm_sig->common.specific.next);
+ last_nm_sig_found = 1;
+ }
+
+ nm_next = nm_sig->common.specific.next;
+ next = &nm_sig->common.next;
+ sig = nm_sig->common.next;
+
+ }
+
+ if (!privq) {
+ /* outer queue */
+ ERTS_ASSERT(!found_save);
+ ERTS_ASSERT(!found_saved_last);
+ }
+ else if (privq > 0) {
+ /* middle queue */
+ ERTS_ASSERT(!next_trace || found_next_trace);
+ ERTS_ASSERT(!found_save);
+ if (!found_saved_last_p) {
+ ERTS_ASSERT(!found_saved_last
+ || (proc->flags & F_DEFERRED_SAVED_LAST));
+ }
+ else {
+ if (*found_saved_last_p) {
+ ERTS_ASSERT(!found_saved_last);
+ ERTS_ASSERT(!(proc->flags & F_DEFERRED_SAVED_LAST));
+ }
+ else if (saved_last) {
+ ERTS_ASSERT(found_saved_last);
+ ERTS_ASSERT(proc->flags & F_DEFERRED_SAVED_LAST);
+ }
+ *found_saved_last_p |= found_saved_last;
+ }
+ }
+ else {
+ /* inner queue */
+ ERTS_ASSERT(!found_next_trace);
+ ERTS_ASSERT(nm_sigs == 0);
+ ERTS_ASSERT(found_save);
+ ERTS_ASSERT(!saved_last
+ || (found_saved_last
+ || (proc->flags & F_DEFERRED_SAVED_LAST)));
+ if (found_saved_last_p)
+ *found_saved_last_p |= found_saved_last;
+ }
+
+ ERTS_ASSERT(last_nm_sig_found);
+ ERTS_ASSERT(last_sig_found);
+
+ if (sig_psflg != ERTS_PSFLG_FREE) {
+ erts_aint32_t state = erts_atomic32_read_nob(&proc->state);
+ ERTS_ASSERT(nm_sigs ? !!(state & sig_psflg) : !(state & sig_psflg));
+ }
+
+ return msg_len;
+}
+
+void
+erts_proc_sig_hdbg_check_priv_queue(Process *p, char *what, char *file, int line)
+{
+ int found_saved_last = 0;
+ Sint len1, len2;
+ ERTS_LC_ASSERT(erts_thr_progress_is_blocking()
+ || ERTS_PROC_IS_EXITING(p)
+ || (ERTS_PROC_LOCK_MAIN
+ & erts_proc_lc_my_proc_locks(p)));
+ len1 = proc_sig_hdbg_check_queue(p,
+ -1,
+ &p->sig_qs.first,
+ p->sig_qs.last,
+ NULL,
+ NULL,
+ NULL,
+ &found_saved_last,
+ ERTS_PSFLG_FREE);
+ len2 = proc_sig_hdbg_check_queue(p,
+ 1,
+ &p->sig_qs.cont,
+ p->sig_qs.cont_last,
+ p->sig_qs.nmsigs.next,
+ p->sig_qs.nmsigs.last,
+ NULL,
+ &found_saved_last,
+ ERTS_PSFLG_SIG_Q);
+ if (p->sig_qs.saved_last)
+ ERTS_ASSERT(found_saved_last);
+ ERTS_ASSERT(p->sig_qs.len == len1 + len2);
+}
+
+void
+erts_proc_sig_hdbg_check_in_queue(Process *p, char *what, char *file, int line)
+{
+ Sint len;
+ ERTS_LC_ASSERT(erts_thr_progress_is_blocking()
+ || ERTS_PROC_IS_EXITING(p)
+ || (ERTS_PROC_LOCK_MSGQ
+ & erts_proc_lc_my_proc_locks(p)));
+ len = proc_sig_hdbg_check_queue(p,
+ 0,
+ &p->sig_inq.first,
+ p->sig_inq.last,
+ p->sig_inq.nmsigs.next,
+ p->sig_inq.nmsigs.last,
+ NULL,
+ NULL,
+ ERTS_PSFLG_SIG_IN_Q);
+ ASSERT(p->sig_inq.len == len);
+}
+
+#endif
diff --git a/erts/emulator/beam/erl_proc_sig_queue.h b/erts/emulator/beam/erl_proc_sig_queue.h
new file mode 100644
index 0000000000..56fe3e683e
--- /dev/null
+++ b/erts/emulator/beam/erl_proc_sig_queue.h
@@ -0,0 +1,690 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB 2018. 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%
+ */
+
+/*
+ * Description: Process signal queue implementation.
+ *
+ * Currently the following signals are handled:
+ * - Messages
+ * - Exit
+ * - Monitor
+ * - Demonitor
+ * - Monitor down
+ * - Persistent monitor message
+ * - Link
+ * - Unlink
+ * - Group leader
+ * - Is process alive
+ * - Trace change
+ *
+ * The signal queue consists of three parts:
+ * - Outer queue (sig_inq field in process struct)
+ * - Middle queue (sig_qs field in process struct)
+ * - Inner queue (sig_qs field in process struct)
+ *
+ * Incoming signals are placed in the outer queue
+ * by other processes, ports, or by the runtime system
+ * itself. This queue is protected by the msgq process
+ * lock and may be accessed by any other entity. While
+ * a signal is located in the outer queue, it is still
+ * in transit between sender and receiver.
+ *
+ * The middle and the inner queues are private to the
+ * receiving process and can only be accessed while
+ * holding the main process lock. The signal changes
+ * from being in transit to being received while in
+ * the middle queue. Non-message signals are handled
+ * immediately upon reception while message signals
+ * are moved into the inner queue.
+ *
+ * In the outer and middle queues both message signals
+ * and non-message signals are mixed. Signals in these
+ * queues are referenced using two single linked lists.
+ * One single linked list that go through all signals
+ * in the queue and another single linked list that
+ * goes through only non-message signals. The list
+ * through the non-message signals is used for fast
+ * access to these signals in the middle queue, since
+ * these should be handled immediately upon reception.
+ *
+ * The inner queue consists only of one single linked
+ * list through the message signals. A receive
+ * expression can only operate on messages once they
+ * have entered the inner queue.
+ *
+ * Author: Rickard Green
+ */
+
+#ifndef ERTS_PROC_SIG_QUEUE_H_TYPE__
+#define ERTS_PROC_SIG_QUEUE_H_TYPE__
+
+#if 0
+# define ERTS_PROC_SIG_HARD_DEBUG
+#endif
+
+struct erl_mesg;
+
+typedef struct {
+ struct erl_mesg *next;
+ union {
+ struct erl_mesg **next;
+ void *attachment;
+ } specific;
+ Eterm tag;
+} ErtsSignalCommon;
+
+#define ERTS_SIG_HANDLE_REDS_MAX_PREFERED (CONTEXT_REDS/40)
+
+#ifdef ERTS_PROC_SIG_HARD_DEBUG
+# define ERTS_HDBG_CHECK_SIGNAL_IN_QUEUE(P) \
+ ERTS_HDBG_CHECK_SIGNAL_IN_QUEUE__((P), "")
+# define ERTS_HDBG_CHECK_SIGNAL_PRIV_QUEUE(P) \
+ ERTS_HDBG_CHECK_SIGNAL_PRIV_QUEUE__((P), "")
+# define ERTS_HDBG_CHECK_SIGNAL_IN_QUEUE__(P, What) \
+ erts_proc_sig_hdbg_check_in_queue((P), (What), __FILE__, __LINE__)
+# define ERTS_HDBG_CHECK_SIGNAL_PRIV_QUEUE__(P, What) \
+ erts_proc_sig_hdbg_check_priv_queue((P), (What), __FILE__, __LINE__)
+struct process;
+void erts_proc_sig_hdbg_check_priv_queue(struct process *c_p, char *what,
+ char *file, int line);
+void erts_proc_sig_hdbg_check_in_queue(struct process *c_p, char *what,
+ char *file, int line);
+#else
+# define ERTS_HDBG_CHECK_SIGNAL_IN_QUEUE(P)
+# define ERTS_HDBG_CHECK_SIGNAL_PRIV_QUEUE(P)
+# define ERTS_HDBG_CHECK_SIGNAL_IN_QUEUE__(P, What)
+#define ERTS_HDBG_CHECK_SIGNAL_PRIV_QUEUE__(P, What)
+#endif
+
+#endif
+
+#if !defined(ERTS_PROC_SIG_QUEUE_H__) && !defined(ERTS_PROC_SIG_QUEUE_TYPE_ONLY)
+#define ERTS_PROC_SIG_QUEUE_H__
+
+struct dist_entry_;
+
+/*
+ * Send operations of currently supported process signals follow...
+ */
+
+/**
+ *
+ * @brief Send an exit signal to a process.
+ *
+ *
+ * @param[in] c_p Pointer to process struct of
+ * currently executing process.
+ *
+ * @param[in] from Identifier of sender.
+ *
+ * @param[in] to Identifier of local process
+ * to send signal to.
+ *
+ * @param[in] reason Exit reason.
+ *
+ * @param[in] token Seq trace token.
+ *
+ * @param[in] normal_kills If non-zero, also normal exit
+ * reason will kill the receiver
+ * if it is not trapping exit.
+ *
+ */
+void
+erts_proc_sig_send_exit(Process *c_p, Eterm from, Eterm to,
+ Eterm reason, Eterm token, int normal_kills);
+
+/**
+ *
+ * @brief Send an exit signal due to broken link to a process.
+ *
+ *
+ * @param[in] c_p Pointer to process struct of
+ * currently executing process.
+ *
+ * @param[in] from Identifier of sender.
+ *
+ * @param[in] lnk Pointer to link structure
+ * from the sending side. It
+ * should contain information
+ * about receiver.
+ *
+ * @param[in] reason Exit reason.
+ *
+ * @param[in] token Seq trace token.
+ *
+ */
+void
+erts_proc_sig_send_link_exit(Process *c_p, Eterm from, ErtsLink *lnk,
+ Eterm reason, Eterm token);
+
+/**
+ *
+ * @brief Send an link signal to a process.
+ *
+ *
+ * @param[in] c_p Pointer to process struct of
+ * currently executing process.
+ *
+ * @param[in] to Identifier of receiver.
+ *
+ * @param[in] lnk Pointer to link structure to
+ * insert on receiver side.
+ *
+ * @return A non-zero value if
+ * signal was successfully
+ * sent. If a zero, value
+ * the signal was not sent
+ * due to the receiver not
+ * existing. The sender
+ * needs to deallocate the
+ * link structure.
+ *
+ */
+int
+erts_proc_sig_send_link(Process *c_p, Eterm to, ErtsLink *lnk);
+
+/**
+ *
+ * @brief Send an unlink signal to a process.
+ *
+ *
+ * @param[in] c_p Pointer to process struct of
+ * currently executing process.
+ *
+ * @param[in] lnk Pointer to link structure from
+ * the sending side. It should
+ * contain information about
+ * receiver.
+ */
+void
+erts_proc_sig_send_unlink(Process *c_p, ErtsLink *lnk);
+
+/**
+ *
+ * @brief Send an exit signal due to broken link to a process.
+ *
+ * This function is used instead of erts_proc_sig_send_link_exit()
+ * when the signal arrives via the distribution and
+ * no link structure is available.
+ *
+ * @param[in] dep Distribution entry of channel
+ * that the signal arrived on.
+ *
+ * @param[in] from Identifier of sender.
+ *
+ * @param[in] to Identifier of receiver.
+ *
+ * @param[in] reason Exit reason.
+ *
+ * @param[in] token Seq trace token.
+ *
+ */
+void
+erts_proc_sig_send_dist_link_exit(struct dist_entry_ *dep,
+ Eterm from, Eterm to,
+ Eterm reason, Eterm token);
+
+/**
+ *
+ * @brief Send an unlink signal to a process.
+ *
+ * This function is used instead of erts_proc_sig_send_unlink()
+ * when the signal arrives via the distribution and
+ * no link structure is available.
+ *
+ * @param[in] dep Distribution entry of channel
+ * that the signal arrived on.
+ *
+ * @param[in] from Identifier of sender.
+ *
+ * @param[in] to Identifier of receiver.
+ *
+ */
+void
+erts_proc_sig_send_dist_unlink(struct dist_entry_ *dep,
+ Eterm from, Eterm to);
+
+/**
+ *
+ * @brief Send a monitor down signal to a process.
+ *
+ * @param[in] mon Pointer to target monitor
+ * structure from the sending
+ * side. It should contain
+ * information about receiver.
+ *
+ * @param[in] reason Exit reason.
+ *
+ */
+void
+erts_proc_sig_send_monitor_down(ErtsMonitor *mon, Eterm reason);
+
+/**
+ *
+ * @brief Send a demonitor signal to a process.
+ *
+ * @param[in] mon Pointer to origin monitor
+ * structure from the sending
+ * side. It should contain
+ * information about receiver.
+ *
+ * @param[in] reason Exit reason.
+ *
+ */
+void
+erts_proc_sig_send_demonitor(ErtsMonitor *mon);
+
+/**
+ *
+ * @brief Send a monitor signal to a process.
+ *
+ * @param[in] mon Pointer to target monitor
+ * structure to insert on
+ * receiver side.
+ *
+ * @param[in] to Identifier of receiver.
+ *
+ * @return A non-zero value if
+ * signal was successfully
+ * sent. If a zero, value
+ * the signal was not sent
+ * due to the receiver not
+ * existing. The sender
+ * needs to deallocate the
+ * monitor structure.
+ *
+ */
+int
+erts_proc_sig_send_monitor(ErtsMonitor *mon, Eterm to);
+
+/**
+ *
+ * @brief Send a monitor down signal to a process.
+ *
+ * This function is used instead of erts_proc_sig_send_monitor_down()
+ * when the signal arrives via the distribution and
+ * no link structure is available.
+ *
+ * @param[in] dep Pointer to distribution entry
+ * of channel that the signal
+ * arrived on.
+ *
+ * @param[in] ref Reference identifying the monitor.
+ *
+ * @param[in] from Identifier of sender.
+ *
+ * @param[in] to Identifier of receiver.
+ *
+ * @param[in] reason Exit reason.
+ *
+ */
+void
+erts_proc_sig_send_dist_monitor_down(DistEntry *dep, Eterm ref,
+ Eterm from, Eterm to,
+ Eterm reason);
+
+/**
+ *
+ * @brief Send a demonitor signal to a process.
+ *
+ * This function is used instead of erts_proc_sig_send_demonitor()
+ * when the signal arrives via the distribution and
+ * no monitor structure is available.
+ *
+ * @param[in] to Identifier of receiver.
+ *
+ * @param[in] ref Reference identifying the monitor.
+ *
+ */
+void
+erts_proc_sig_send_dist_demonitor(Eterm to, Eterm ref);
+
+/**
+ *
+ * @brief Send a persistent monitor triggered signal to a process.
+ *
+ * Used by monitors that are not auto disabled such as for
+ * example 'time_offset' monitors.
+ *
+ * @param[in] type Monitor type.
+ *
+ * @param[in] key Monitor key.
+ *
+ * @param[in] from Identifier of sender.
+ *
+ * @param[in] to Identifier of receiver.
+ *
+ * @param[in] msg Message template.
+ *
+ * @param[in] msg_sz Heap size of message template.
+ *
+ */
+void
+erts_proc_sig_send_persistent_monitor_msg(Uint16 type, Eterm key,
+ Eterm from, Eterm to,
+ Eterm msg, Uint msg_sz);
+
+/**
+ *
+ * @brief Send a trace change signal to a process.
+ *
+ * @param[in] to Identifier of receiver.
+ *
+ * @param[in] on Trace flags to enable.
+ *
+ * @param[in] off Trace flags to disable.
+ *
+ * @param[in] tracer Tracer to set. If the non-value,
+ * tracer will not be changed.
+ *
+ */
+void
+erts_proc_sig_send_trace_change(Eterm to, Uint on, Uint off,
+ Eterm tracer);
+
+/**
+ *
+ * @brief Send a group leader signal to a process.
+ *
+ * Set group-leader of receiving process. If sent locally,
+ * a response message '{Ref, Result}' is sent to the original
+ * sender when performed where Ref is the reference passed
+ * as 'ref' argument, and Result is either 'true' or 'badarg'.
+ *
+ * @param[in] c_p Pointer to process struct of
+ * currently executing process.
+ * NULL if signal arrived via
+ * distribution.
+ *
+ * @param[in] to Identifier of receiver.
+ *
+ * @param[in] gl Identifier of new group leader.
+ *
+ * @param[in] ref Reference to use in response
+ * message to locally sending
+ * process (i.e., c_p when c_p
+ * is non-null).
+ *
+ */
+void
+erts_proc_sig_send_group_leader(Process *c_p, Eterm to, Eterm gl,
+ Eterm ref);
+
+/**
+ *
+ * @brief Send an 'is process alive' signal to a process.
+ *
+ * A response message '{Ref, Result}' is sent to the
+ * sender when performed where Ref is the reference passed
+ * as 'ref' argument, and Result is either 'true' or 'false'.
+ *
+ * @param[in] c_p Pointer to process struct of
+ * currently executing process.
+ * NULL if signal arrived via
+ * distribution.
+ *
+ * @param[in] to Identifier of receiver.
+ *
+ * @param[in] ref Reference to use in response
+ * message to the sending
+ * process (i.e., c_p).
+ *
+ */
+void
+erts_proc_sig_send_is_alive_request(Process *c_p, Eterm to,
+ Eterm ref);
+
+/*
+ * End of send operations of currently supported process signals.
+ */
+
+
+/**
+ *
+ * @brief Handle incoming signals.
+ *
+ * Called by an ordinary scheduler in order to handle incoming
+ * signals for a process. The work is done on the middle part
+ * of the signal queue. The maximum amount of signals handled
+ * is limited by the amount of reductions given when calling.
+ * Note that a reduction does not necessarily map to a signal.
+ *
+ * @param[in] c_p Pointer to process struct of
+ * currently executing process.
+ *
+ * @param[out] statep Pointer to process state after
+ * signal handling. May not be NULL.
+ *
+ * @param[in,out] redsp Pointer to an integer containing
+ * reductions. On input, the amount
+ * of preferred reductions to be
+ * used by the call. On output, the
+ * amount of reductions consumed.
+ *
+ * @param[in] max_reds Absolute maximum of reductions
+ * to use. If the process cannot
+ * make progress after the preferred
+ * amount of reductions has been
+ * consumed, signal handling may
+ * proceed up to a maximum of
+ * 'max_reds' in order to make
+ * the process able to proceed
+ * with other tasks after handling
+ * has finished.
+ *
+ * @param[in] local_only If is zero, new signals may be
+ * fetched from the outer queue and
+ * put in the middle queue before
+ * signal handling is performed. If
+ * non-zero, no new signals will be
+ * fetched before handling begins.
+ *
+ * @return Returns a non-zero value, when
+ * no more signals to handle in the
+ * middle queue remain. A zero
+ * return value means that there
+ * remains signals in the middle
+ * queue.
+ */
+int
+erts_proc_sig_handle_incoming(Process *c_p, erts_aint32_t *statep,
+ int *redsp, int max_reds,
+ int local_only);
+
+/**
+ *
+ * @brief Handle remaining signals for an exiting process
+ *
+ * Called as part of termination of a process. It will handle
+ * remaining signals.
+ *
+ * @param[in] c_p Pointer to process struct of
+ * currently executing process.
+ *
+ * @param[in,out] redsp Pointer to an integer containing
+ * reductions. On input, the amount
+ * of maximum reductions to be
+ * used by the call. On output, the
+ * amount of reductions consumed.
+ *
+ * @return Returns a non-zero value, when
+ * no more signals to handle in the
+ * middle queue remain. A zero
+ * return value means that there
+ * remains signals in the middle
+ * queue.
+ */
+int
+erts_proc_sig_handle_exit(Process *c_p, int *redsp);
+
+/**
+ *
+ * @brief Helper for loop_rec instruction.
+ *
+ * This function should only be called from the loop_rec
+ * instruction (or equivalents). It is called when loop_rec
+ * reach the end of the inner queue (which is the only
+ * part of the signal queue that receive is allowed to
+ * operate on). When called, this function tries to make
+ * more messages available in the inner queue. This by
+ * fetching signals from the outer queue to the middle
+ * queue and/or processing signals in the middle queue.
+ *
+ * @param[in] c_p Pointer to process struct of
+ * currently executing process.
+ *
+ * @param[in] fcalls Content of FCALLS in
+ * process_main()
+ *
+ * @param[in] neg_o_reds Content of neg_o_reds in
+ * process_main()
+ *
+ * @param[out] msgpp Pointer to pointer to next
+ * available message to process.
+ * If *msgpp == NULL, no more
+ * messages are available.
+ *
+ * @param[out] get_outp Pointer to an integer
+ * indicating how to respond
+ * if no more messages are
+ * available (msgpp). If integer
+ * is set to zero, loop_rec
+ * should jump to an appropriate
+ * wait instruction. If zero,
+ * the message queue lock remain
+ * locked since the test for
+ * more messages was done.
+ * If the integer is set to a
+ * value larger that zero, the
+ * process exited. If the integer
+ * is set to a value less than
+ * zero, the process is required
+ * to yield.
+ *
+ *
+ * @return The amount of reductions
+ * consumed.
+ *
+ */
+int
+erts_proc_sig_receive_helper(Process *c_p, int fcalls,
+ int neg_o_reds, ErtsMessage **msgpp,
+ int *get_outp);
+
+/**
+ *
+ * @brief Fetch signals from the outer queue
+ *
+ * Fetches signals from outer queue and places them in the
+ * middle queue ready for signal handling. If the middle
+ * queue is empty, only message signals were present in the
+ * outer queue, and no receive tracing has been enabled on
+ * the process, the middle queue is bypassed and messages
+ * are delivered directly to the inner queue instead.
+ *
+ * @param[in] c_p Pointer to process struct of
+ * currently executing process.
+ *
+ */
+void erts_proc_sig_fetch(Process *p);
+
+typedef struct {
+ Uint size;
+ ErtsMessage *msgp;
+} ErtsMessageInfo;
+
+/**
+ *
+ * @brief Prepare signal queue for inspection by process_info()
+ *
+ *
+ * @param[in] c_p Pointer to process struct of
+ * currently executing process.
+ *
+ * @param[in] rp Pointer to process struct of
+ * process to inspect.
+ *
+ * @param[in] rp_locks Process locks held on 'rp'.
+ *
+ * @param[in] mip Pointer to array of
+ * ErtsMessageInfo structures.
+ *
+ */
+
+Uint erts_proc_sig_prep_msgq_for_inspection(Process *c_p,
+ Process *rp,
+ ErtsProcLocks rp_locks,
+ ErtsMessageInfo *mip);
+
+
+/**
+ *
+ * @brief Move message data of messages in private queues to heap
+ *
+ * Move message data of messages in private queues to the heap.
+ * This is part of GC of processes that uses on-heap message
+ * data.
+ *
+ * @param[in] c_p Pointer to process struct of
+ * currently executing process.
+ *
+ */
+void erts_proc_sig_move_msgs_to_heap(Process *c_p);
+
+/**
+ *
+ * @brief Size of signal in bytes.
+ *
+ * @param[in] sig Signal to inspect.
+ *
+ */
+Uint erts_proc_sig_signal_size(ErtsSignal *sig);
+
+
+/**
+ *
+ * @brief Clear seq trace tokens on all signals
+ *
+ * Assumes thread progress has been blocked!
+ *
+ * @param[in] c_p Pointer to process
+ *
+ */
+void
+erts_proc_sig_clear_seq_trace_tokens(Process *c_p);
+
+/**
+ * @brief Initialize this functionality
+ */
+void erts_proc_sig_queue_init(void);
+
+void
+erts_proc_sig_debug_foreach_sig(Process *c_p,
+ void (*msg_func)(ErtsMessage *, void *),
+ void (*oh_func)(ErlOffHeap *, void *),
+ void (*mon_func)(ErtsMonitor *, void *),
+ void (*lnk_func)(ErtsLink *, void *),
+ void *arg);
+
+extern Process *erts_dirty_process_signal_handler;
+extern Process *erts_dirty_process_signal_handler_high;
+extern Process *erts_dirty_process_signal_handler_max;
+
+#endif /* ERTS_PROC_SIG_QUEUE_H__ */
diff --git a/erts/emulator/beam/erl_process.c b/erts/emulator/beam/erl_process.c
index a807d60ec7..374583ec47 100644
--- a/erts/emulator/beam/erl_process.c
+++ b/erts/emulator/beam/erl_process.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1996-2017. All Rights Reserved.
+ * Copyright Ericsson AB 1996-2018. 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.
@@ -53,6 +53,7 @@
#include "erl_nfunc_sched.h"
#include "erl_check_io.h"
#include "erl_poll.h"
+#include "erl_proc_sig_queue.h"
#define ERTS_CHECK_TIME_REDS CONTEXT_REDS
#define ERTS_DELAYED_WAKEUP_INFINITY (~(Uint64) 0)
@@ -137,36 +138,6 @@ runq_got_work_to_execute(ErtsRunQueue *rq)
return runq_got_work_to_execute_flags(ERTS_RUNQ_FLGS_GET_NOB(rq));
}
-#undef RUNQ_READ_RQ
-#undef RUNQ_SET_RQ
-#define RUNQ_READ_RQ(X) ((ErtsRunQueue *) erts_atomic_read_nob((X)))
-#define RUNQ_SET_RQ(X, RQ) erts_atomic_set_nob((X), (erts_aint_t) (RQ))
-
-#ifdef DEBUG
-# if defined(ARCH_64)
-# define ERTS_DBG_SET_INVALID_RUNQP(RQP, N) \
- (RUNQ_SET_RQ((RQP), (0xdeadbeefdead0003LL | ((N) << 4)))
-# define ERTS_DBG_VERIFY_VALID_RUNQP(RQP) \
-do { \
- ASSERT((RQP) != NULL); \
- ASSERT(((((Uint) (RQP)) & ((Uint) 0x3))) == ((Uint) 0)); \
- ASSERT((((Uint) (RQP)) & ~((Uint) 0xffff)) != ((Uint) 0xdeadbeefdead0000LL));\
-} while (0)
-# else
-# define ERTS_DBG_SET_INVALID_RUNQP(RQP, N) \
- (RUNQ_SET_RQ((RQP), (0xdead0003 | ((N) << 4))))
-# define ERTS_DBG_VERIFY_VALID_RUNQP(RQP) \
-do { \
- ASSERT((RQP) != NULL); \
- ASSERT(((((UWord) (RQP)) & ((UWord) 1))) == ((UWord) 0)); \
- ASSERT((((UWord) (RQP)) & ~((UWord) 0xffff)) != ((UWord) 0xdead0000)); \
-} while (0)
-# endif
-#else
-# define ERTS_DBG_SET_INVALID_RUNQP(RQP, N)
-# define ERTS_DBG_VERIFY_VALID_RUNQP(RQP)
-#endif
-
const Process erts_invalid_process = {{ERTS_INVALID_PID}};
extern BeamInstr beam_apply[];
@@ -461,7 +432,8 @@ typedef enum {
ERTS_PSTT_CLA, /* Copy Literal Area */
ERTS_PSTT_COHMQ, /* Change off heap message queue */
ERTS_PSTT_FTMQ, /* Flush trace msg queue */
- ERTS_PSTT_ETS_FREE_FIXATION
+ ERTS_PSTT_ETS_FREE_FIXATION,
+ ERTS_PSTT_PRIO_SIG /* Elevate prio on signal management */
} ErtsProcSysTaskType;
#define ERTS_MAX_PROC_SYS_TASK_ARGS 2
@@ -609,7 +581,6 @@ dbg_chk_aux_work_val(erts_aint32_t value)
valid |= ERTS_SSI_AUX_WORK_CNCLD_TMRS;
valid |= ERTS_SSI_AUX_WORK_CNCLD_TMRS_THR_PRGR;
valid |= ERTS_SSI_AUX_WORK_THR_PRGR_LATER_OP;
- valid |= ERTS_SSI_AUX_WORK_PENDING_EXITERS;
#if HAVE_ERTS_MSEG
valid |= ERTS_SSI_AUX_WORK_MSEG_CACHE_CHECK;
#endif
@@ -631,7 +602,6 @@ dbg_chk_aux_work_val(erts_aint32_t value)
#define ERTS_DBG_CHK_SSI_AUX_WORK(SSI)
#endif
-static void do_handle_pending_exiters(ErtsProcList *);
static void wake_scheduler(ErtsRunQueue *rq);
#if defined(ERTS_ENABLE_LOCK_CHECK)
@@ -696,8 +666,6 @@ erts_pre_init_process(void)
= "MISC_THR_PRGR";
erts_aux_work_flag_descr[ERTS_SSI_AUX_WORK_MISC_IX]
= "MISC";
- erts_aux_work_flag_descr[ERTS_SSI_AUX_WORK_PENDING_EXITERS_IX]
- = "PENDING_EXITERS";
erts_aux_work_flag_descr[ERTS_SSI_AUX_WORK_SET_TMO_IX]
= "SET_TMO";
erts_aux_work_flag_descr[ERTS_SSI_AUX_WORK_MSEG_CACHE_CHECK_IX]
@@ -1538,6 +1506,20 @@ erts_proclist_destroy(ErtsProcList *plp)
proclist_destroy(plp);
}
+void
+erts_proclist_dump(fmtfn_t to, void *to_arg, ErtsProcList *plp)
+{
+ ErtsProcList *first = plp;
+
+ while (plp) {
+ erts_print(to, to_arg, "%T", plp->pid);
+ plp = plp->next;
+ if (plp == first)
+ break;
+ }
+ erts_print(to, to_arg, "\n");
+}
+
void *
erts_psd_set_init(Process *p, int ix, void *data)
{
@@ -2547,27 +2529,6 @@ handle_mseg_cache_check(ErtsAuxWorkData *awdp, erts_aint32_t aux_work, int waiti
static ERTS_INLINE erts_aint32_t
-handle_pending_exiters(ErtsAuxWorkData *awdp, erts_aint32_t aux_work, int waiting)
-{
- ErtsProcList *pnd_xtrs;
- ErtsRunQueue *rq;
-
- rq = awdp->esdp->run_queue;
- unset_aux_work_flags(awdp->ssi, ERTS_SSI_AUX_WORK_PENDING_EXITERS);
-
- erts_runq_lock(rq);
- pnd_xtrs = rq->procs.pending_exiters;
- rq->procs.pending_exiters = NULL;
- erts_runq_unlock(rq);
-
- if (erts_proclist_fetch(&pnd_xtrs, NULL))
- do_handle_pending_exiters(pnd_xtrs);
-
- return aux_work & ~ERTS_SSI_AUX_WORK_PENDING_EXITERS;
-}
-
-
-static ERTS_INLINE erts_aint32_t
handle_setup_aux_work_timer(ErtsAuxWorkData *awdp, erts_aint32_t aux_work, int waiting)
{
unset_aux_work_flags(awdp->ssi, ERTS_SSI_AUX_WORK_SET_TMO);
@@ -2648,9 +2609,6 @@ handle_aux_work(ErtsAuxWorkData *awdp, erts_aint32_t orig_aux_work, int waiting)
HANDLE_AUX_WORK(ERTS_SSI_AUX_WORK_MISC,
handle_misc_aux_work);
- HANDLE_AUX_WORK(ERTS_SSI_AUX_WORK_PENDING_EXITERS,
- handle_pending_exiters);
-
HANDLE_AUX_WORK(ERTS_SSI_AUX_WORK_SET_TMO,
handle_setup_aux_work_timer);
@@ -3926,21 +3884,16 @@ immigrate(ErtsRunQueue *c_rq, ErtsMigrationPath *mp)
Port *prt;
prt = erts_dequeue_port(rq);
if (prt)
- RUNQ_SET_RQ(&prt->run_queue, c_rq);
+ erts_set_runq_port(prt, c_rq);
erts_runq_unlock(rq);
if (prt) {
- /* port might terminate while we have no lock... */
rq = erts_port_runq(prt);
- if (rq) {
- if (rq != c_rq)
- erts_exit(ERTS_ABORT_EXIT,
- "%s:%d:%s(): Internal error",
- __FILE__, __LINE__, __func__);
- erts_enqueue_port(c_rq, prt);
- if (!iflag)
- return; /* done */
- erts_runq_unlock(c_rq);
- }
+ if (rq != c_rq)
+ ERTS_INTERNAL_ERROR("Unexpected run-queue");
+ erts_enqueue_port(c_rq, prt);
+ if (!iflag)
+ return; /* done */
+ erts_runq_unlock(c_rq);
}
}
else {
@@ -3954,12 +3907,11 @@ immigrate(ErtsRunQueue *c_rq, ErtsMigrationPath *mp)
while (proc) {
erts_aint32_t state;
state = erts_atomic32_read_acqb(&proc->state);
- if (!(ERTS_PSFLG_BOUND & state)
- && (prio == (int) ERTS_PSFLGS_GET_PRQ_PRIO(state))) {
+ if (prio == (int) ERTS_PSFLGS_GET_PRQ_PRIO(state)
+ && erts_try_change_runq_proc(proc, c_rq)) {
ErtsRunQueueInfo *rqi = &rq->procs.prio_info[prio];
unqueue_process(rq, rpq, rqi, prio, prev_proc, proc);
erts_runq_unlock(rq);
- RUNQ_SET_RQ(&proc->run_queue, c_rq);
rq_locked = 0;
erts_runq_lock(c_rq);
@@ -4140,21 +4092,13 @@ evacuate_run_queue(ErtsRunQueue *rq,
while (prt) {
ErtsRunQueue *prt_rq;
prt = erts_dequeue_port(rq);
- RUNQ_SET_RQ(&prt->run_queue, to_rq);
+ erts_set_runq_port(prt, to_rq);
erts_runq_unlock(rq);
- /*
- * The port might terminate while
- * we have no lock on it...
- */
prt_rq = erts_port_runq(prt);
- if (prt_rq) {
- if (prt_rq != to_rq)
- erts_exit(ERTS_ABORT_EXIT,
- "%s:%d:%s() internal error\n",
- __FILE__, __LINE__, __func__);
- erts_enqueue_port(to_rq, prt);
- erts_runq_unlock(to_rq);
- }
+ if (prt_rq != to_rq)
+ ERTS_INTERNAL_ERROR("Unexpected run-queue");
+ erts_enqueue_port(to_rq, prt);
+ erts_runq_unlock(to_rq);
erts_runq_lock(rq);
prt = rq->ports.start;
}
@@ -4165,8 +4109,6 @@ evacuate_run_queue(ErtsRunQueue *rq,
for (prio_q = 0; prio_q < ERTS_NO_PROC_PRIO_QUEUES; prio_q++) {
erts_aint32_t state;
Process *proc;
- int notify = 0;
- to_rq = NULL;
if (!mp->prio[prio_q].runq)
return;
@@ -4177,14 +4119,13 @@ evacuate_run_queue(ErtsRunQueue *rq,
while (proc) {
Process *real_proc;
int prio;
- erts_aint32_t max_qbit, qbit, real_state;
+ erts_aint32_t max_qbit, qbit;
prio = ERTS_PSFLGS_GET_PRQ_PRIO(state);
qbit = ((erts_aint32_t) 1) << prio;
if (!(state & ERTS_PSFLG_PROXY)) {
real_proc = proc;
- real_state = state;
}
else {
real_proc = erts_proc_lookup_raw(proc->common.id);
@@ -4192,7 +4133,6 @@ evacuate_run_queue(ErtsRunQueue *rq,
free_proxy_proc(proc);
goto handle_next_proc;
}
- real_state = erts_atomic32_read_acqb(&real_proc->state);
}
max_qbit = (state >> ERTS_PSFLGS_IN_PRQ_MASK_OFFSET);
@@ -4227,7 +4167,13 @@ evacuate_run_queue(ErtsRunQueue *rq,
goto handle_next_proc;
}
- if (ERTS_PSFLG_BOUND & real_state) {
+ prio = (int) ERTS_PSFLGS_GET_PRQ_PRIO(state);
+ to_rq = mp->prio[prio].runq;
+
+ if (!to_rq)
+ goto handle_next_proc;
+
+ if (!erts_try_change_runq_proc(proc, to_rq)) {
/* Bound processes get stuck here... */
proc->next = NULL;
if (sbpp->last)
@@ -4237,16 +4183,13 @@ evacuate_run_queue(ErtsRunQueue *rq,
sbpp->last = proc;
}
else {
- int prio = (int) ERTS_PSFLGS_GET_PRQ_PRIO(state);
erts_runq_unlock(rq);
- to_rq = mp->prio[prio].runq;
- RUNQ_SET_RQ(&proc->run_queue, to_rq);
-
erts_runq_lock(to_rq);
enqueue_process(to_rq, prio, proc);
erts_runq_unlock(to_rq);
- notify = 1;
+
+ smp_notify_inc_runq(to_rq);
erts_runq_lock(rq);
}
@@ -4254,8 +4197,7 @@ evacuate_run_queue(ErtsRunQueue *rq,
handle_next_proc:
proc = dequeue_process(rq, prio_q, &state);
}
- if (notify)
- smp_notify_inc_runq(to_rq);
+
}
}
@@ -4309,14 +4251,13 @@ try_steal_task_from_victim(ErtsRunQueue *rq, int *rq_lockedp, ErtsRunQueue *vrq,
proc = rpq->first;
while (proc) {
- erts_aint32_t state = erts_atomic32_read_acqb(&proc->state);
- if (!(ERTS_PSFLG_BOUND & state)) {
+ if (erts_try_change_runq_proc(proc, rq)) {
+ erts_aint32_t state = erts_atomic32_read_acqb(&proc->state);
/* Steal process */
int prio = (int) ERTS_PSFLGS_GET_PRQ_PRIO(state);
ErtsRunQueueInfo *rqi = &vrq->procs.prio_info[prio];
unqueue_process(vrq, rpq, rqi, prio, prev_proc, proc);
erts_runq_unlock(vrq);
- RUNQ_SET_RQ(&proc->run_queue, rq);
erts_runq_lock(rq);
*rq_lockedp = 1;
@@ -4341,26 +4282,14 @@ no_procs:
if (vrq->ports.start) {
ErtsRunQueue *prt_rq;
Port *prt = erts_dequeue_port(vrq);
- RUNQ_SET_RQ(&prt->run_queue, rq);
+ erts_set_runq_port(prt, rq);
erts_runq_unlock(vrq);
-
- /*
- * The port might terminate while
- * we have no lock on it...
- */
-
prt_rq = erts_port_runq(prt);
- if (!prt_rq)
- return 0;
- else {
- if (prt_rq != rq)
- erts_exit(ERTS_ABORT_EXIT,
- "%s:%d:%s() internal error\n",
- __FILE__, __LINE__, __func__);
- *rq_lockedp = 1;
- erts_enqueue_port(rq, prt);
- return !0;
- }
+ if (prt_rq != rq)
+ ERTS_INTERNAL_ERROR("Unexpected run-queue");
+ *rq_lockedp = 1;
+ erts_enqueue_port(rq, prt);
+ return !0;
}
erts_runq_unlock(vrq);
@@ -5838,7 +5767,6 @@ erts_init_scheduling(int no_schedulers, int no_schedulers_online, int no_poll_th
rq->wakeup_other = 0;
rq->wakeup_other_reds = 0;
- rq->procs.pending_exiters = NULL;
rq->procs.context_switches = 0;
rq->procs.reductions = 0;
@@ -6116,7 +6044,8 @@ make_proxy_proc(Process *prev_proxy, Process *proc, erts_aint32_t prio)
{
erts_aint32_t state;
Process *proxy;
- ErtsRunQueue *rq = RUNQ_READ_RQ(&proc->run_queue);
+ int bound;
+ ErtsRunQueue *rq = erts_get_runq_proc(proc, &bound);
state = (ERTS_PSFLG_PROXY
| ERTS_PSFLG_IN_RUNQ
@@ -6129,7 +6058,7 @@ make_proxy_proc(Process *prev_proxy, Process *proc, erts_aint32_t prio)
proxy = prev_proxy;
ASSERT(erts_atomic32_read_nob(&proxy->state) & ERTS_PSFLG_PROXY);
erts_atomic32_set_nob(&proxy->state, state);
- RUNQ_SET_RQ(&proc->run_queue, rq);
+ (void) erts_set_runq_proc(proc, rq, &bound);
}
else {
proxy = erts_alloc(ERTS_ALC_T_PROC, sizeof(Process));
@@ -6142,8 +6071,7 @@ make_proxy_proc(Process *prev_proxy, Process *proc, erts_aint32_t prio)
}
#endif
erts_atomic32_init_nob(&proxy->state, state);
- erts_atomic_init_nob(&proxy->run_queue,
- erts_atomic_read_nob(&proc->run_queue));
+ erts_init_runq_proc(proc, rq, bound);
}
proxy->common.id = proc->common.id;
@@ -6334,18 +6262,21 @@ select_enqueue_run_queue(int enqueue, int enq_prio, Process *p, erts_aint32_t st
default: {
ErtsRunQueue* runq;
+ int bound;
ASSERT(enqueue == ERTS_ENQUEUE_NORMAL_QUEUE
|| enqueue == -ERTS_ENQUEUE_NORMAL_QUEUE);
- runq = erts_get_runq_proc(p);
+ runq = erts_get_runq_proc(p, &bound);
- if (!(ERTS_PSFLG_BOUND & state)) {
+ if (!bound) {
ErtsRunQueue *new_runq = erts_check_emigration_need(runq, enq_prio);
- if (new_runq) {
- RUNQ_SET_RQ(&p->run_queue, new_runq);
- runq = new_runq;
- }
+ if (new_runq) {
+ if (erts_try_change_runq_proc(p, new_runq))
+ runq = new_runq;
+ else
+ runq = erts_get_runq_proc(p, NULL);
+ }
}
ASSERT(runq);
@@ -6370,11 +6301,36 @@ schedule_out_process(ErtsRunQueue *c_rq, erts_aint32_t state, Process *p,
int enqueue; /* < 0 -> use proxy */
ErtsRunQueue* runq;
+ ASSERT(!(state & (ERTS_PSFLG_DIRTY_IO_PROC
+ |ERTS_PSFLG_DIRTY_CPU_PROC))
+ || (BeamIsOpCode(*p->i, op_call_nif)
+ || BeamIsOpCode(*p->i, op_apply_bif)));
+
+ a = state;
+
+ /* Clear activ-sys if needed... */
+ while (1) {
+ n = e = a;
+ if (a & ERTS_PSFLG_ACTIVE_SYS) {
+ if (a & (ERTS_PSFLG_SIG_Q
+ | ERTS_PSFLG_SIG_IN_Q
+ | ERTS_PSFLG_SYS_TASKS))
+ break;
+ /* Clear active-sys */
+ n &= ~ERTS_PSFLG_ACTIVE_SYS;
+ }
+ a = erts_atomic32_cmpxchg_nob(&p->state, n, e);
+ if (a == e) {
+ a = n;
+ break;
+ }
+ }
+
if (!is_normal_sched)
running_flgs = ERTS_PSFLG_DIRTY_RUNNING|ERTS_PSFLG_DIRTY_RUNNING_SYS;
else {
running_flgs = ERTS_PSFLG_RUNNING|ERTS_PSFLG_RUNNING_SYS;
- if (state & ERTS_PSFLG_DIRTY_ACTIVE_SYS
+ if ((a & ERTS_PSFLG_DIRTY_ACTIVE_SYS)
&& (p->flags & (F_DELAY_GC|F_DISABLE_GC))) {
/*
* Delay dirty GC; will be enabled automatically
@@ -6387,14 +6343,12 @@ schedule_out_process(ErtsRunQueue *c_rq, erts_aint32_t state, Process *p,
*/
ASSERT(!(p->flags & (F_DIRTY_CLA | F_DIRTY_GC_HIBERNATE)));
- state = erts_atomic32_read_band_nob(&p->state,
- ~ERTS_PSFLG_DIRTY_ACTIVE_SYS);
- state &= ~ERTS_PSFLG_DIRTY_ACTIVE_SYS;
+ a = erts_atomic32_read_band_nob(&p->state,
+ ~ERTS_PSFLG_DIRTY_ACTIVE_SYS);
+ a &= ~ERTS_PSFLG_DIRTY_ACTIVE_SYS;
}
}
- a = state;
-
while (1) {
n = e = a;
@@ -6402,6 +6356,11 @@ schedule_out_process(ErtsRunQueue *c_rq, erts_aint32_t state, Process *p,
enqueue = ERTS_ENQUEUE_NOT;
+ ASSERT(((a & (ERTS_PSFLG_EXITING|ERTS_PSFLG_FREE))
+ != ERTS_PSFLG_EXITING)
+ || ((a & (ERTS_PSFLG_ACTIVE|ERTS_PSFLG_SUSPENDED))
+ == ERTS_PSFLG_ACTIVE));
+
n &= ~running_flgs;
if ((a & (ERTS_PSFLG_ACTIVE_SYS|ERTS_PSFLG_DIRTY_ACTIVE_SYS))
|| (a & (ERTS_PSFLG_ACTIVE|ERTS_PSFLG_SUSPENDED)) == ERTS_PSFLG_ACTIVE) {
@@ -6626,7 +6585,7 @@ change_proc_schedule_state(Process *p,
}
- *statep = a;
+ *statep = n;
return enqueue;
}
@@ -6651,6 +6610,95 @@ erts_schedule_process(Process *p, erts_aint32_t state, ErtsProcLocks locks)
schedule_process(p, state, locks);
}
+static ERTS_INLINE erts_aint32_t
+active_sys_enqueue(Process *p, erts_aint32_t state,
+ erts_aint32_t enable_flags, int status_locked)
+{
+ /*
+ * This function may or may not be called with status locke held.
+ * It always returns without the status lock held!
+ */
+ unsigned int prof_runnable_procs = erts_system_profile_flags.runnable_procs;
+ erts_aint32_t n, a = state, enq_prio = -1;
+ int slocked = status_locked;
+ int enqueue; /* < 0 -> use proxy */
+
+ /* Status lock prevents out of order "runnable proc" trace msgs */
+ ERTS_LC_ASSERT(slocked || !(ERTS_PROC_LOCK_STATUS & erts_proc_lc_my_proc_locks(p)));
+ ERTS_LC_ASSERT(!slocked || (ERTS_PROC_LOCK_STATUS & erts_proc_lc_my_proc_locks(p)));
+
+ if (!prof_runnable_procs) {
+ if (slocked) {
+ erts_proc_unlock(p, ERTS_PROC_LOCK_STATUS);
+ slocked = 0;
+ }
+ }
+ else {
+ if (!slocked) {
+ erts_proc_lock(p, ERTS_PROC_LOCK_STATUS);
+ slocked = !0;
+ }
+ }
+
+ ASSERT(!(state & ERTS_PSFLG_PROXY));
+
+ while (1) {
+ erts_aint32_t e;
+ n = e = a;
+
+ if (a & ERTS_PSFLG_FREE)
+ goto cleanup; /* We don't want to schedule free processes... */
+
+ enqueue = ERTS_ENQUEUE_NOT;
+ n |= enable_flags;
+ n |= ERTS_PSFLG_ACTIVE_SYS;
+ if (!(a & (ERTS_PSFLG_RUNNING
+ | ERTS_PSFLG_RUNNING_SYS
+ | ERTS_PSFLG_DIRTY_RUNNING
+ | ERTS_PSFLG_DIRTY_RUNNING_SYS)))
+ enqueue = check_enqueue_in_prio_queue(p, &enq_prio, &n, a);
+ a = erts_atomic32_cmpxchg_mb(&p->state, n, e);
+ if (a == e)
+ break;
+ if (a == n && enqueue == ERTS_ENQUEUE_NOT)
+ goto cleanup;
+ }
+
+ if (prof_runnable_procs) {
+
+ if (!(a & (ERTS_PSFLG_ACTIVE_SYS
+ | ERTS_PSFLG_RUNNING
+ | ERTS_PSFLG_RUNNING_SYS
+ | ERTS_PSFLG_DIRTY_RUNNING
+ | ERTS_PSFLG_DIRTY_RUNNING_SYS))
+ && (!(a & ERTS_PSFLG_ACTIVE) || (a & ERTS_PSFLG_SUSPENDED))) {
+ /* We activated a prevously inactive process */
+ profile_runnable_proc(p, am_active);
+ }
+
+ erts_proc_unlock(p, ERTS_PROC_LOCK_STATUS);
+ slocked = 0;
+ }
+
+ add2runq(enqueue, enq_prio, p, n, NULL);
+
+cleanup:
+
+ if (slocked)
+ erts_proc_unlock(p, ERTS_PROC_LOCK_STATUS);
+
+ ERTS_LC_ASSERT(!(ERTS_PROC_LOCK_STATUS & erts_proc_lc_my_proc_locks(p)));
+
+ return n;
+}
+
+erts_aint32_t
+erts_proc_sys_schedule(Process *p, erts_aint32_t state, erts_aint32_t enable_flag)
+{
+ /* We are not allowed to call this function with status lock held... */
+ return active_sys_enqueue(p, state, enable_flag, 0);
+}
+
static int
schedule_process_sys_task(Process *p, erts_aint32_t prio, ErtsProcSysTask *st,
erts_aint32_t *fail_state_p)
@@ -6658,19 +6706,16 @@ schedule_process_sys_task(Process *p, erts_aint32_t prio, ErtsProcSysTask *st,
int res;
int locked;
ErtsProcSysTaskQs *stqs, *free_stqs;
- erts_aint32_t fail_state, state, a, n, enq_prio;
- int enqueue; /* < 0 -> use proxy */
- unsigned int prof_runnable_procs;
+ erts_aint32_t fail_state, state;
fail_state = *fail_state_p;
res = 1; /* prepare for success */
st->next = st->prev = st; /* Prep for empty prio queue */
state = erts_atomic32_read_nob(&p->state);
- prof_runnable_procs = erts_system_profile_flags.runnable_procs;
locked = 0;
free_stqs = NULL;
- if (state & ERTS_PSFLG_ACTIVE_SYS)
+ if (state & ERTS_PSFLG_SYS_TASKS)
stqs = NULL;
else {
alloc_qs:
@@ -6690,6 +6735,7 @@ schedule_process_sys_task(Process *p, erts_aint32_t prio, ErtsProcSysTask *st,
state = erts_atomic32_read_nob(&p->state);
if (state & fail_state) {
+ erts_proc_unlock(p, ERTS_PROC_LOCK_STATUS);
*fail_state_p = (state & fail_state);
free_stqs = stqs;
res = 0;
@@ -6737,69 +6783,14 @@ schedule_process_sys_task(Process *p, erts_aint32_t prio, ErtsProcSysTask *st,
state = n;
}
-
- a = state;
- enq_prio = -1;
-
- /* Status lock prevents out of order "runnable proc" trace msgs */
- ERTS_LC_ASSERT(ERTS_PROC_LOCK_STATUS & erts_proc_lc_my_proc_locks(p));
-
- if (!prof_runnable_procs) {
- erts_proc_unlock(p, ERTS_PROC_LOCK_STATUS);
- locked = 0;
- }
-
- ASSERT(!(state & ERTS_PSFLG_PROXY));
-
- while (1) {
- erts_aint32_t e;
- n = e = a;
-
- if (a & ERTS_PSFLG_FREE)
- goto cleanup; /* We don't want to schedule free processes... */
-
- enqueue = ERTS_ENQUEUE_NOT;
- n |= ERTS_PSFLG_ACTIVE_SYS;
- if (!(a & (ERTS_PSFLG_RUNNING
- | ERTS_PSFLG_RUNNING_SYS
- | ERTS_PSFLG_DIRTY_RUNNING
- | ERTS_PSFLG_DIRTY_RUNNING_SYS)))
- enqueue = check_enqueue_in_prio_queue(p, &enq_prio, &n, a);
- a = erts_atomic32_cmpxchg_mb(&p->state, n, e);
- if (a == e)
- break;
- if (a == n && enqueue == ERTS_ENQUEUE_NOT)
- goto cleanup;
- }
-
- if (prof_runnable_procs) {
-
- if (!(a & (ERTS_PSFLG_ACTIVE_SYS
- | ERTS_PSFLG_RUNNING
- | ERTS_PSFLG_RUNNING_SYS
- | ERTS_PSFLG_DIRTY_RUNNING
- | ERTS_PSFLG_DIRTY_RUNNING_SYS))
- && (!(a & ERTS_PSFLG_ACTIVE) || (a & ERTS_PSFLG_SUSPENDED))) {
- /* We activated a prevously inactive process */
- profile_runnable_proc(p, am_active);
- }
-
- erts_proc_unlock(p, ERTS_PROC_LOCK_STATUS);
- locked = 0;
- }
-
- add2runq(enqueue, enq_prio, p, n, NULL);
+ /* active_sys_enqueue() always return with status lock unlocked */
+ (void) active_sys_enqueue(p, state, ERTS_PSFLG_SYS_TASKS, locked);
cleanup:
- if (locked)
- erts_proc_unlock(p, ERTS_PROC_LOCK_STATUS);
-
if (free_stqs)
proc_sys_task_queues_free(free_stqs);
- ERTS_LC_ASSERT(!(ERTS_PROC_LOCK_STATUS & erts_proc_lc_my_proc_locks(p)));
-
return res;
}
@@ -8578,11 +8569,13 @@ static ERTS_INLINE void
cancel_suspend_of_suspendee(Process *p, ErtsProcLocks p_locks)
{
if (is_not_nil(p->suspendee)) {
+ ErtsMonitor *mon;
+ Eterm suspendee = p->suspendee;
Process *rp;
if (!(p_locks & ERTS_PROC_LOCK_STATUS))
erts_proc_lock(p, ERTS_PROC_LOCK_STATUS);
rp = erts_pid2proc(p, p_locks|ERTS_PROC_LOCK_STATUS,
- p->suspendee, ERTS_PROC_LOCK_STATUS);
+ suspendee, ERTS_PROC_LOCK_STATUS);
if (rp) {
erts_resume(rp, ERTS_PROC_LOCK_STATUS);
erts_proc_unlock(rp, ERTS_PROC_LOCK_STATUS);
@@ -8590,6 +8583,14 @@ cancel_suspend_of_suspendee(Process *p, ErtsProcLocks p_locks)
if (!(p_locks & ERTS_PROC_LOCK_STATUS))
erts_proc_unlock(p, ERTS_PROC_LOCK_STATUS);
p->suspendee = NIL;
+
+ mon = erts_monitor_tree_lookup(p->suspend_monitors,
+ suspendee);
+ if (mon) {
+ erts_monitor_tree_delete(&p->suspend_monitors,
+ mon);
+ erts_monitor_suspend_destroy(erts_monitor_suspend(mon));
+ }
}
}
@@ -8748,8 +8749,7 @@ pid2proc_not_running(Process *c_p, ErtsProcLocks c_p_locks,
{
erts_aint32_t state;
state = erts_atomic32_read_nob(&rp->state);
- ASSERT((state & ERTS_PSFLG_PENDING_EXIT)
- || !(state & ERTS_PSFLG_RUNNING));
+ ASSERT(!(state & ERTS_PSFLG_RUNNING));
}
#endif
@@ -8804,7 +8804,7 @@ erts_pid2proc_nropt(Process *c_p, ErtsProcLocks c_p_locks,
static ERTS_INLINE int
do_bif_suspend_process(Process *c_p,
- ErtsSuspendMonitor *smon,
+ ErtsMonitorSuspend *smon,
Process *suspendee)
{
ASSERT(suspendee);
@@ -8837,21 +8837,25 @@ handle_pend_bif_sync_suspend(Process *suspendee,
suspender = erts_pid2proc(suspendee,
suspendee_locks,
suspender_pid,
- ERTS_PROC_LOCK_LINK|ERTS_PROC_LOCK_STATUS);
+ ERTS_PROC_LOCK_STATUS);
if (suspender) {
+ ErtsMonitorSuspend *smon;
+ ErtsMonitor *mon;
+ mon = erts_monitor_tree_lookup(suspender->suspend_monitors,
+ suspendee->common.id);
+ smon = erts_monitor_suspend(mon);
+
ASSERT(is_nil(suspender->suspendee));
- if (!suspendee_alive)
- erts_delete_suspend_monitor(&suspender->suspend_monitors,
- suspendee->common.id);
+ if (!suspendee_alive) {
+ if (mon) {
+ erts_monitor_tree_delete(&suspender->suspend_monitors,
+ mon);
+ erts_monitor_suspend_destroy(smon);
+ }
+ }
else {
#ifdef DEBUG
- int res;
-#endif
- ErtsSuspendMonitor *smon;
- smon = erts_lookup_suspend_monitor(suspender->suspend_monitors,
- suspendee->common.id);
-#ifdef DEBUG
- res =
+ int res =
#endif
do_bif_suspend_process(suspendee, smon, suspendee);
ASSERT(!smon || res != 0);
@@ -8860,9 +8864,8 @@ handle_pend_bif_sync_suspend(Process *suspendee,
/* suspender is suspended waiting for suspendee to suspend;
resume suspender */
ASSERT(suspender != suspendee);
- resume_process(suspender, ERTS_PROC_LOCK_LINK|ERTS_PROC_LOCK_STATUS);
- erts_proc_unlock(suspender,
- ERTS_PROC_LOCK_LINK|ERTS_PROC_LOCK_STATUS);
+ resume_process(suspender, ERTS_PROC_LOCK_STATUS);
+ erts_proc_unlock(suspender, ERTS_PROC_LOCK_STATUS);
}
}
@@ -8880,26 +8883,29 @@ handle_pend_bif_async_suspend(Process *suspendee,
suspender = erts_pid2proc(suspendee,
suspendee_locks,
suspender_pid,
- ERTS_PROC_LOCK_LINK);
+ ERTS_PROC_LOCK_STATUS);
if (suspender) {
+ ErtsMonitorSuspend *smon;
+ ErtsMonitor *mon;
+ mon = erts_monitor_tree_lookup(suspender->suspend_monitors,
+ suspendee->common.id);
+ smon = erts_monitor_suspend(mon);
ASSERT(is_nil(suspender->suspendee));
- if (!suspendee_alive)
- erts_delete_suspend_monitor(&suspender->suspend_monitors,
- suspendee->common.id);
+ if (!suspendee_alive) {
+ if (mon) {
+ erts_monitor_tree_delete(&suspender->suspend_monitors,
+ mon);
+ erts_monitor_suspend_destroy(smon);
+ }
+ }
else {
#ifdef DEBUG
- int res;
-#endif
- ErtsSuspendMonitor *smon;
- smon = erts_lookup_suspend_monitor(suspender->suspend_monitors,
- suspendee->common.id);
-#ifdef DEBUG
- res =
+ int res =
#endif
- do_bif_suspend_process(suspendee, smon, suspendee);
+ do_bif_suspend_process(suspendee, smon, suspendee);
ASSERT(!smon || res != 0);
}
- erts_proc_unlock(suspender, ERTS_PROC_LOCK_LINK);
+ erts_proc_unlock(suspender, ERTS_PROC_LOCK_STATUS);
}
}
@@ -8913,8 +8919,9 @@ suspend_process_2(BIF_ALIST_2)
{
Eterm res;
Process* suspendee = NULL;
- ErtsSuspendMonitor *smon;
+ ErtsMonitorSuspend *smon;
ErtsProcLocks xlocks = (ErtsProcLocks) 0;
+ int created;
/* Options and default values: */
int asynchronous = 0;
@@ -8947,9 +8954,7 @@ suspend_process_2(BIF_ALIST_2)
goto badarg;
}
- xlocks = ERTS_PROC_LOCK_LINK | (asynchronous
- ? (ErtsProcLocks) 0
- : ERTS_PROC_LOCK_STATUS);
+ xlocks = ERTS_PROC_LOCK_STATUS;
erts_proc_lock(BIF_P, xlocks);
@@ -8960,15 +8965,14 @@ suspend_process_2(BIF_ALIST_2)
if (!suspendee)
goto no_suspendee;
- smon = erts_add_or_lookup_suspend_monitor(&BIF_P->suspend_monitors,
- BIF_ARG_1);
-
- /* ... but a little trickier with SMP support ... */
+ smon = erts_monitor_suspend_tree_lookup_create(&BIF_P->suspend_monitors,
+ &created,
+ BIF_ARG_1);
if (asynchronous) {
/* --- Asynchronous suspend begin ---------------------------------- */
- ERTS_LC_ASSERT(ERTS_PROC_LOCK_LINK
+ ERTS_LC_ASSERT(ERTS_PROC_LOCK_STATUS
& erts_proc_lc_my_proc_locks(BIF_P));
ERTS_LC_ASSERT(ERTS_PROC_LOCK_STATUS
== erts_proc_lc_my_proc_locks(suspendee));
@@ -9010,9 +9014,9 @@ suspend_process_2(BIF_ALIST_2)
else /* if (!asynchronous) */ {
/* --- Synchronous suspend begin ----------------------------------- */
- ERTS_LC_ASSERT(((ERTS_PROC_LOCK_LINK|ERTS_PROC_LOCK_STATUS)
+ ERTS_LC_ASSERT(((ERTS_PROC_LOCK_STATUS|ERTS_PROC_LOCK_STATUS)
& erts_proc_lc_my_proc_locks(BIF_P))
- == (ERTS_PROC_LOCK_LINK|ERTS_PROC_LOCK_STATUS));
+ == (ERTS_PROC_LOCK_STATUS|ERTS_PROC_LOCK_STATUS));
ERTS_LC_ASSERT(ERTS_PROC_LOCK_STATUS
== erts_proc_lc_my_proc_locks(suspendee));
@@ -9097,9 +9101,15 @@ suspend_process_2(BIF_ALIST_2)
ERTS_BIF_PREP_ERROR(res, BIF_P, SYSTEM_LIMIT);
goto do_return;
- no_suspendee:
- BIF_P->suspendee = NIL;
- erts_delete_suspend_monitor(&BIF_P->suspend_monitors, BIF_ARG_1);
+ no_suspendee: {
+ ErtsMonitor *mon;
+ BIF_P->suspendee = NIL;
+ mon = erts_monitor_tree_lookup(BIF_P->suspend_monitors, BIF_ARG_1);
+ if (mon) {
+ erts_monitor_tree_delete(&BIF_P->suspend_monitors, mon);
+ erts_monitor_suspend_destroy(erts_monitor_suspend(mon));
+ }
+ }
badarg:
ERTS_BIF_PREP_ERROR(res, BIF_P, BADARG);
@@ -9126,15 +9136,17 @@ suspend_process_2(BIF_ALIST_2)
BIF_RETTYPE
resume_process_1(BIF_ALIST_1)
{
- ErtsSuspendMonitor *smon;
+ ErtsMonitor *mon;
+ ErtsMonitorSuspend *smon;
Process *suspendee;
int is_active;
if (BIF_P->common.id == BIF_ARG_1)
BIF_ERROR(BIF_P, BADARG);
- erts_proc_lock(BIF_P, ERTS_PROC_LOCK_LINK);
- smon = erts_lookup_suspend_monitor(BIF_P->suspend_monitors, BIF_ARG_1);
+ erts_proc_lock(BIF_P, ERTS_PROC_LOCK_STATUS);
+ mon = erts_monitor_tree_lookup(BIF_P->suspend_monitors, BIF_ARG_1);
+ smon = erts_monitor_suspend(mon);
if (!smon) {
/* No previous suspend or dead suspendee */
@@ -9151,20 +9163,17 @@ resume_process_1(BIF_ALIST_1)
}
else if (smon->active) {
smon->active--;
- ASSERT(smon->pending >= 0);
+ ASSERT(smon->pending == 0);
is_active = 1;
}
else {
/* No previous suspend or dead suspendee */
- goto error;
+ goto no_suspendee;
}
if (smon->active || smon->pending || !is_active) {
/* Leave the suspendee as it is; just verify that it is still alive */
- suspendee = erts_pid2proc(BIF_P,
- ERTS_PROC_LOCK_MAIN|ERTS_PROC_LOCK_LINK,
- BIF_ARG_1,
- 0);
+ suspendee = erts_proc_lookup(BIF_ARG_1);
if (!suspendee)
goto no_suspendee;
@@ -9172,11 +9181,18 @@ resume_process_1(BIF_ALIST_1)
else {
/* Resume */
suspendee = erts_pid2proc(BIF_P,
- ERTS_PROC_LOCK_MAIN|ERTS_PROC_LOCK_LINK,
+ ERTS_PROC_LOCK_MAIN|ERTS_PROC_LOCK_STATUS,
BIF_ARG_1,
ERTS_PROC_LOCK_STATUS);
- if (!suspendee)
+ if (!suspendee) {
+ mon = erts_monitor_tree_lookup(BIF_P->suspend_monitors, BIF_ARG_1);
+ smon = erts_monitor_suspend(mon);
+ if (!mon)
+ goto error;
goto no_suspendee;
+ }
+
+ ASSERT(mon == erts_monitor_tree_lookup(BIF_P->suspend_monitors, BIF_ARG_1));
ASSERT(ERTS_PSFLG_SUSPENDED
& erts_atomic32_read_nob(&suspendee->state));
@@ -9186,19 +9202,24 @@ resume_process_1(BIF_ALIST_1)
erts_proc_unlock(suspendee, ERTS_PROC_LOCK_STATUS);
}
- if (!smon->active && !smon->pending)
- erts_delete_suspend_monitor(&BIF_P->suspend_monitors, BIF_ARG_1);
+ if (!smon->active && !smon->pending) {
+ ASSERT(mon);
+ erts_monitor_tree_delete(&BIF_P->suspend_monitors, mon);
+ erts_monitor_suspend_destroy(smon);
+ }
- erts_proc_unlock(BIF_P, ERTS_PROC_LOCK_LINK);
+ erts_proc_unlock(BIF_P, ERTS_PROC_LOCK_STATUS);
BIF_RET(am_true);
no_suspendee:
/* cleanup */
- erts_delete_suspend_monitor(&BIF_P->suspend_monitors, BIF_ARG_1);
+ ASSERT(mon);
+ erts_monitor_tree_delete(&BIF_P->suspend_monitors, mon);
+ erts_monitor_suspend_destroy(smon);
error:
- erts_proc_unlock(BIF_P, ERTS_PROC_LOCK_LINK);
+ erts_proc_unlock(BIF_P, ERTS_PROC_LOCK_STATUS);
BIF_ERROR(BIF_P, BADARG);
}
@@ -9417,9 +9438,8 @@ erts_resume_processes(ErtsProcList *list)
}
Eterm
-erts_get_process_priority(Process *p)
+erts_get_process_priority(erts_aint32_t state)
{
- erts_aint32_t state = erts_atomic32_read_nob(&p->state);
switch (ERTS_PSFLGS_GET_USR_PRIO(state)) {
case PRIORITY_MAX: return am_max;
case PRIORITY_HIGH: return am_high;
@@ -9469,7 +9489,10 @@ erts_set_process_priority(Process *p, Eterm value)
}
max_qbit = 0;
- if (a & ERTS_PSFLG_ACTIVE_SYS)
+ ASSERT((a & ERTS_PSFLG_SYS_TASKS)
+ ? !!p->sys_task_qs
+ : !p->sys_task_qs);
+ if (a & ERTS_PSFLG_SYS_TASKS)
max_qbit |= p->sys_task_qs->qmask;
if (a & ERTS_PSFLG_DELAYED_SYS) {
ErtsProcSysTaskQs *qs;
@@ -9492,8 +9515,8 @@ erts_set_process_priority(Process *p, Eterm value)
aprio = PRIORITY_LOW;
break;
default:
- ERTS_INTERNAL_ERROR("Invalid qmask");
- aprio = -1;
+ aprio = nprio;
+ break;
}
if (aprio > nprio) /* low value -> high prio */
@@ -9669,10 +9692,6 @@ Process *erts_schedule(ErtsSchedulerData *esdp, Process *p, int calls)
/* have to re-read state after taking lock */
state = erts_atomic32_read_nob(&p->state);
- 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
@@ -9987,12 +10006,10 @@ Process *erts_schedule(ErtsSchedulerData *esdp, Process *p, int calls)
& ((state & (ERTS_PSFLG_SUSPENDED
| ERTS_PSFLG_EXITING
| ERTS_PSFLG_FREE
- | ERTS_PSFLG_PENDING_EXIT
| ERTS_PSFLG_ACTIVE_SYS
| ERTS_PSFLG_DIRTY_ACTIVE_SYS))
!= ERTS_PSFLG_SUSPENDED)
- & (!(state & (ERTS_PSFLG_EXITING
- | ERTS_PSFLG_PENDING_EXIT))
+ & (!(state & ERTS_PSFLG_EXITING)
| (!!is_normal_sched))
);
@@ -10079,8 +10096,11 @@ Process *erts_schedule(ErtsSchedulerData *esdp, Process *p, int calls)
p->scheduler_data = esdp;
}
else {
+ if (!(state & ERTS_PSFLGS_DIRTY_WORK)) {
+ /* Dirty work completed... */
+ goto sunlock_sched_out_proc;
+ }
if (state & (ERTS_PSFLG_ACTIVE_SYS
- | ERTS_PSFLG_PENDING_EXIT
| ERTS_PSFLG_EXITING)) {
/*
* IMPORTANT! We need to take care of
@@ -10103,13 +10123,6 @@ Process *erts_schedule(ErtsSchedulerData *esdp, Process *p, int calls)
&& (state & ERTS_PSFLG_DIRTY_IO_PROC)));
}
- if (state & ERTS_PSFLG_PENDING_EXIT) {
- erts_handle_pending_exit(p,
- ERTS_PROC_LOCK_MAIN|ERTS_PROC_LOCK_STATUS);
- state = erts_atomic32_read_nob(&p->state);
- }
-
-
erts_proc_unlock(p, ERTS_PROC_LOCK_STATUS);
/* Clear tracer if it has been removed */
@@ -10131,24 +10144,47 @@ Process *erts_schedule(ErtsSchedulerData *esdp, Process *p, int calls)
}
if (is_normal_sched) {
-
if (state & ERTS_PSFLG_RUNNING_SYS) {
- /*
- * GC is normally never delayed when a process
- * is scheduled out, but might be when executing
- * hand written beam assembly in
- * prim_eval:'receive'. If GC is delayed we are
- * not allowed to execute system tasks.
- */
- if (!(p->flags & F_DELAY_GC)) {
- int cost = execute_sys_tasks(p, &state, reds);
- calls += cost;
- reds -= cost;
- if (reds <= 0)
- goto sched_out_proc;
- if (state & ERTS_PSFLGS_DIRTY_WORK)
- goto sched_out_proc;
+ if (state & (ERTS_PSFLG_SIG_Q|ERTS_PSFLG_SIG_IN_Q)) {
+ int local_only = !!(p->flags & F_LOCAL_SIGS_ONLY);
+ if (!local_only || (state & ERTS_PSFLG_SIG_Q)) {
+ int sig_reds;
+ /*
+ * If we have dirty work scheduled we allow
+ * usage of all reductions since we need to
+ * handle all signals before doing dirty
+ * work...
+ */
+ if (state & ERTS_PSFLGS_DIRTY_WORK)
+ sig_reds = reds;
+ else
+ sig_reds = ERTS_SIG_HANDLE_REDS_MAX_PREFERED;
+ (void) erts_proc_sig_handle_incoming(p,
+ &state,
+ &sig_reds,
+ sig_reds,
+ local_only);
+ reds -= sig_reds;
+ }
}
+ if ((state & (ERTS_PSFLG_SYS_TASKS
+ | ERTS_PSFLG_EXITING)) == ERTS_PSFLG_SYS_TASKS) {
+ /*
+ * GC is normally never delayed when a process
+ * is scheduled out, but might be when executing
+ * hand written beam assembly in
+ * prim_eval:'receive'. If GC is delayed we are
+ * not allowed to execute system tasks.
+ */
+ if (!(p->flags & F_DELAY_GC)) {
+ int cost = execute_sys_tasks(p, &state, reds);
+ calls += cost;
+ reds -= cost;
+ }
+ }
+
+ if (reds <= 0 || (state & ERTS_PSFLGS_DIRTY_WORK))
+ goto sched_out_proc;
ASSERT(state & psflg_running_sys);
ASSERT(!(state & psflg_running));
@@ -10158,7 +10194,7 @@ Process *erts_schedule(ErtsSchedulerData *esdp, Process *p, int calls)
if (((state & (ERTS_PSFLG_SUSPENDED
| ERTS_PSFLG_ACTIVE)) != ERTS_PSFLG_ACTIVE)
- && !(state & ERTS_PSFLG_EXITING)) {
+ & !(state & ERTS_PSFLG_EXITING)) {
goto sched_out_proc;
}
@@ -10200,15 +10236,6 @@ Process *erts_schedule(ErtsSchedulerData *esdp, Process *p, int calls)
ERTS_CHK_HAVE_ONLY_MAIN_PROC_LOCK(p);
- /* Never run a suspended process */
-#ifdef DEBUG
- {
- erts_aint32_t dstate = erts_atomic32_read_nob(&p->state);
- ASSERT(!(ERTS_PSFLG_SUSPENDED & dstate)
- || (ERTS_PSFLG_DIRTY_RUNNING_SYS & dstate));
- }
-#endif
-
ASSERT(erts_proc_read_refc(p) > 0);
if (!(state & ERTS_PSFLG_EXITING) && ERTS_PTMR_IS_TIMED_OUT(p)) {
@@ -10221,6 +10248,40 @@ Process *erts_schedule(ErtsSchedulerData *esdp, Process *p, int calls)
ERTS_PTMR_CLEAR(p);
}
+ /* if exiting, we *shall* exit... */
+ ASSERT(!(state & ERTS_PSFLG_EXITING)
+ || p->i == (BeamInstr *) beam_exit
+ || p->i == (BeamInstr *) beam_continue_exit);
+
+#ifdef DEBUG
+ if (is_normal_sched) {
+ if (state & ERTS_PSFLGS_DIRTY_WORK)
+ ERTS_INTERNAL_ERROR("Executing dirty code on normal scheduler");
+ }
+ else {
+ if (!(state & ERTS_PSFLGS_DIRTY_WORK)) {
+ if (esdp->type == ERTS_SCHED_DIRTY_CPU)
+ ERTS_INTERNAL_ERROR("Executing normal code on dirty CPU scheduler");
+ else if (esdp->type == ERTS_SCHED_DIRTY_IO)
+ ERTS_INTERNAL_ERROR("Executing normal code on dirty IO scheduler");
+ else
+ ERTS_INTERNAL_ERROR("Executing normal code on dirty UNKNOWN scheduler");
+ }
+ }
+ {
+ erts_aint32_t dstate = erts_atomic32_read_nob(&p->state);
+
+ /* Never run a suspended process */
+ ASSERT(!(ERTS_PSFLG_SUSPENDED & dstate)
+ || (ERTS_PSFLG_DIRTY_RUNNING_SYS & dstate));
+
+ /* Do not execute on the wrong type of scheduler... */
+ ASSERT(is_normal_sched
+ ? !(dstate & ERTS_PSFLGS_DIRTY_WORK)
+ : !!(dstate & ERTS_PSFLGS_DIRTY_WORK));
+ }
+#endif
+
return p;
}
}
@@ -10420,7 +10481,7 @@ fetch_sys_task(Process *c_p, erts_aint32_t state, int *qmaskp, int *priop)
n |= (prio << ERTS_PSFLGS_ACT_PRIO_OFFSET);
if (!qmask)
- n &= ~ERTS_PSFLG_ACTIVE_SYS;
+ n &= ~ERTS_PSFLG_SYS_TASKS;
if (a == n)
break;
@@ -10440,6 +10501,8 @@ done:
return st;
}
+
+static void exit_permanent_prio_elevation(Process *c_p, erts_aint32_t state);
static void save_gc_task(Process *c_p, ErtsProcSysTask *st, int prio);
static void save_dirty_task(Process *c_p, ErtsProcSysTask *st);
@@ -10460,12 +10523,8 @@ execute_sys_tasks(Process *c_p, erts_aint32_t *statep, int in_reds)
int st_prio;
Eterm st_res;
- if (state & (ERTS_PSFLG_EXITING|ERTS_PSFLG_PENDING_EXIT)) {
- if (state & ERTS_PSFLG_PENDING_EXIT)
- erts_handle_pending_exit(c_p, ERTS_PROC_LOCK_MAIN);
- ASSERT(ERTS_PROC_IS_EXITING(c_p));
+ if (state & ERTS_PSFLG_EXITING)
break;
- }
st = fetch_sys_task(c_p, state, &qmask, &st_prio);
if (!st)
@@ -10561,6 +10620,43 @@ execute_sys_tasks(Process *c_p, erts_aint32_t *statep, int in_reds)
reds -= erts_db_execute_free_fixation(c_p, (DbFixation*)st->arg[0]);
st_res = am_true;
break;
+ case ERTS_PSTT_PRIO_SIG: {
+ erts_aint32_t fail_state, state;
+ int local_only, sig_res, sig_reds = reds;
+ st_res = am_false;
+
+ if (st->arg[0] == am_true)
+ local_only = !0;
+ else
+ local_only = 0;
+
+ sig_reds = reds;
+ sig_res = erts_proc_sig_handle_incoming(c_p, &state, &sig_reds,
+ reds, local_only);
+ reds -= sig_reds;
+
+ if (state & ERTS_PSFLG_EXITING) {
+ exit_permanent_prio_elevation(c_p, state);
+ break;
+ }
+
+ if (sig_res)
+ break;
+
+ st->arg[0] = am_true;
+
+ fail_state = ERTS_PSFLG_EXITING;
+
+ if (schedule_process_sys_task(c_p, st_prio, st, &fail_state)) {
+ /* Successfully rescheduled task... */
+ st = NULL;
+ }
+ else {
+ state = erts_atomic32_read_nob(&c_p->state);
+ exit_permanent_prio_elevation(c_p, state);
+ }
+ break;
+ }
default:
ERTS_INTERNAL_ERROR("Invalid process sys task type");
st_res = am_false;
@@ -10609,6 +10705,10 @@ cleanup_sys_tasks(Process *c_p, erts_aint32_t in_state, int in_reds)
}
switch (st->type) {
+ case ERTS_PSTT_PRIO_SIG:
+ state = erts_atomic32_read_nob(&c_p->state);
+ exit_permanent_prio_elevation(c_p, state);
+ /* fall through... */
case ERTS_PSTT_GC_MAJOR:
case ERTS_PSTT_GC_MINOR:
case ERTS_PSTT_CPC:
@@ -10637,6 +10737,36 @@ cleanup_sys_tasks(Process *c_p, erts_aint32_t in_state, int in_reds)
return reds;
}
+static void
+exit_permanent_prio_elevation(Process *c_p, erts_aint32_t state)
+{
+ erts_aint32_t a;
+ /*
+ * we are about to terminate; permanently elevate
+ * prio in order to ensure high prio signal
+ * handling...
+ */
+ a = state;
+ while (1) {
+ erts_aint32_t aprio, uprio, n, e;
+ ASSERT(a & ERTS_PSFLG_EXITING);
+ ASSERT(!(a & ERTS_PSFLG_FREE));
+ aprio = ERTS_PSFLGS_GET_ACT_PRIO(a);
+ uprio = ERTS_PSFLGS_GET_USR_PRIO(a);
+ if (aprio >= uprio)
+ break; /* user prio >= actual prio */
+ /*
+ * actual prio is higher than user prio; raise
+ * user prio to actual prio...
+ */
+ n = e = a;
+ n &= ~ERTS_PSFLGS_USR_PRIO_MASK;
+ n |= aprio << ERTS_PSFLGS_USR_PRIO_OFFSET;
+ a = erts_atomic32_cmpxchg_mb(&c_p->state, n, e);
+ if (a == e)
+ break;
+ }
+}
void
erts_execute_dirty_system_task(Process *c_p)
@@ -10666,8 +10796,8 @@ erts_execute_dirty_system_task(Process *c_p)
if (c_p->flags & F_DIRTY_GC_HIBERNATE) {
erts_proc_lock(c_p, ERTS_PROC_LOCK_MSGQ|ERTS_PROC_LOCK_STATUS);
- ERTS_MSGQ_MV_INQ2PRIVQ(c_p);
- if (c_p->msg.len)
+ erts_proc_sig_fetch(c_p);
+ if (c_p->sig_qs.len)
c_p->flags &= ~F_DIRTY_GC_HIBERNATE; /* operation aborted... */
else {
erts_proc_unlock(c_p, ERTS_PROC_LOCK_MSGQ|ERTS_PROC_LOCK_STATUS);
@@ -10739,7 +10869,7 @@ dispatch_system_task(Process *c_p, erts_aint_t fail_state,
switch (st->type) {
case ERTS_PSTT_CPC:
- rp = erts_dirty_process_code_checker;
+ rp = erts_dirty_process_signal_handler;
ASSERT(fail_state & (ERTS_PSFLG_DIRTY_RUNNING
| ERTS_PSFLG_DIRTY_RUNNING_SYS));
if (c_p == rp) {
@@ -10956,13 +11086,15 @@ erts_internal_request_system_task_4(BIF_ALIST_4)
BIF_ARG_2, BIF_ARG_3, BIF_ARG_4);
}
-static void
-erts_schedule_generic_sys_task(Eterm pid, ErtsProcSysTaskType type, void* arg)
+static int
+schedule_generic_sys_task(Eterm pid, ErtsProcSysTaskType type,
+ int prio, Eterm arg0, Eterm arg1)
{
- Process *rp = erts_proc_lookup(pid);
+ int res = 0;
+ Process *rp = erts_proc_lookup_raw(pid);
if (rp) {
ErtsProcSysTask *st;
- erts_aint32_t state, fail_state;
+ erts_aint32_t st_prio, fail_state;
st = erts_alloc(ERTS_ALC_T_PROC_SYS_TSK,
ERTS_PROC_SYS_TASK_SIZE(0));
@@ -10971,31 +11103,46 @@ erts_schedule_generic_sys_task(Eterm pid, ErtsProcSysTaskType type, void* arg)
st->reply_tag = NIL;
st->req_id = NIL;
st->req_id_sz = 0;
- st->arg[0] = (Eterm)arg;
+ st->arg[0] = arg0;
+ st->arg[1] = arg1;
ERTS_INIT_OFF_HEAP(&st->off_heap);
- state = erts_atomic32_read_nob(&rp->state);
- fail_state = ERTS_PSFLG_EXITING;
-
- if (!schedule_process_sys_task(rp, ERTS_PSFLGS_GET_USR_PRIO(state),
- st, &fail_state))
+ if (prio >= 0) {
+ st_prio = (erts_aint32_t) prio;
+ fail_state = ERTS_PSFLG_FREE;
+ }
+ else {
+ erts_aint32_t state = erts_atomic32_read_nob(&rp->state);
+ st_prio = ERTS_PSFLGS_GET_USR_PRIO(state);
+ fail_state = ERTS_PSFLG_EXITING;
+ }
+ res = schedule_process_sys_task(rp, st_prio, st, &fail_state);
+ if (!res)
erts_free(ERTS_ALC_T_PROC_SYS_TSK, st);
}
+ return res;
}
-
void
erts_schedule_complete_off_heap_message_queue_change(Eterm pid)
{
- erts_schedule_generic_sys_task(pid, ERTS_PSTT_COHMQ, NULL);
+ schedule_generic_sys_task(pid, ERTS_PSTT_COHMQ,
+ -1, NIL, NIL);
}
void
erts_schedule_ets_free_fixation(Eterm pid, DbFixation* fix)
{
- erts_schedule_generic_sys_task(pid, ERTS_PSTT_ETS_FREE_FIXATION, fix);
+ schedule_generic_sys_task(pid, ERTS_PSTT_ETS_FREE_FIXATION,
+ -1, (Eterm) fix, NIL);
}
+int
+erts_sig_prio(Eterm pid, int prio)
+{
+ return schedule_generic_sys_task(pid, ERTS_PSTT_PRIO_SIG,
+ prio, am_false, NIL);
+}
static void
flush_dirty_trace_messages(void *vpid)
@@ -11035,7 +11182,7 @@ erts_schedule_flush_trace_messages(Process *proc, int force_on_proc)
dhndl = erts_thr_progress_unmanaged_delay();
- erts_schedule_generic_sys_task(pid, ERTS_PSTT_FTMQ, NULL);
+ schedule_generic_sys_task(pid, ERTS_PSTT_FTMQ, -1, NIL, NIL);
erts_thr_progress_unmanaged_continue(dhndl);
@@ -11231,9 +11378,11 @@ erts_set_gc_state(Process *c_p, int enable)
#endif
erts_atomic32_read_bset_nob(&c_p->state,
- (ERTS_PSFLG_DELAYED_SYS
- | ERTS_PSFLG_ACTIVE_SYS),
- ERTS_PSFLG_ACTIVE_SYS);
+ (ERTS_PSFLG_DELAYED_SYS
+ | ERTS_PSFLG_ACTIVE_SYS
+ | ERTS_PSFLG_SYS_TASKS),
+ (ERTS_PSFLG_ACTIVE_SYS
+ | ERTS_PSFLG_SYS_TASKS));
#ifdef DEBUG
ASSERT(state & ERTS_PSFLG_DELAYED_SYS);
@@ -11462,6 +11611,7 @@ typedef struct {
Process *proc;
erts_aint32_t state;
ErtsRunQueue *run_queue;
+ int bound;
} ErtsEarlyProcInit;
static void early_init_process_struct(void *varg, Eterm data)
@@ -11472,10 +11622,9 @@ static void early_init_process_struct(void *varg, Eterm data)
proc->common.id = make_internal_pid(data);
erts_atomic32_init_nob(&proc->dirty_state, 0);
proc->dirty_sys_tasks = NULL;
+ erts_init_runq_proc(proc, arg->run_queue, arg->bound);
erts_atomic32_init_relb(&proc->state, arg->state);
- RUNQ_SET_RQ(&proc->run_queue, arg->run_queue);
-
erts_proc_lock_init(proc); /* All locks locked */
}
@@ -11484,7 +11633,7 @@ static void early_init_process_struct(void *varg, Eterm data)
** Allocate process and find out where to place next process.
*/
static Process*
-alloc_process(ErtsRunQueue *rq, erts_aint32_t state)
+alloc_process(ErtsRunQueue *rq, int bound, erts_aint32_t state)
{
ErtsEarlyProcInit init_arg;
Process *p;
@@ -11493,9 +11642,12 @@ alloc_process(ErtsRunQueue *rq, erts_aint32_t state)
if (!p)
return NULL;
+ ASSERT(rq);
+
init_arg.proc = (Process *) p;
- init_arg.run_queue = rq;
init_arg.state = state;
+ init_arg.run_queue = rq;
+ init_arg.bound = bound;
ERTS_CT_ASSERT(offsetof(Process,common) == 0);
@@ -11511,7 +11663,6 @@ alloc_process(ErtsRunQueue *rq, erts_aint32_t state)
ASSERT(internal_pid_serial(p->common.id) <= ERTS_MAX_PID_SERIAL);
- p->approx_started = erts_get_approx_time();
p->rcount = 0;
p->heap = NULL;
@@ -11530,6 +11681,7 @@ erl_create_process(Process* parent, /* Parent of process (default group leader).
Eterm args, /* Arguments for function (must be well-formed list). */
ErlSpawnOpts* so) /* Options for spawn. */
{
+ int bound = 0;
Uint flags = 0;
ErtsRunQueue *rq = NULL;
Process *p;
@@ -11566,7 +11718,7 @@ erl_create_process(Process* parent, /* Parent of process (default group leader).
ASSERT(0 <= ix && ix < erts_no_run_queues);
rq = ERTS_RUNQ_IX(ix);
/* Unsupported feature... */
- state |= ERTS_PSFLG_BOUND;
+ bound = !0;
}
prio = (erts_aint32_t) so->priority;
}
@@ -11579,17 +11731,16 @@ erl_create_process(Process* parent, /* Parent of process (default group leader).
flags |= F_OFF_HEAP_MSGQ;
}
else if (so->flags & SPO_ON_HEAP_MSGQ) {
- state |= ERTS_PSFLG_ON_HEAP_MSGQ;
flags |= F_ON_HEAP_MSGQ;
}
ASSERT((flags & F_ON_HEAP_MSGQ) || (flags & F_OFF_HEAP_MSGQ));
if (!rq)
- rq = erts_get_runq_proc(parent);
+ rq = erts_get_runq_proc(parent, NULL);
- p = alloc_process(rq, state); /* All proc locks are locked by this thread
- on success */
+ p = alloc_process(rq, bound, state); /* All proc locks are locked by this thread
+ on success */
if (!p) {
erts_send_error_to_logger_str(parent->group_leader,
"Too many processes\n");
@@ -11597,11 +11748,6 @@ erl_create_process(Process* parent, /* Parent of process (default group leader).
goto error;
}
- ASSERT((erts_atomic32_read_nob(&p->state)
- & ERTS_PSFLG_ON_HEAP_MSGQ)
- || (erts_atomic32_read_nob(&p->state)
- & ERTS_PSFLG_OFF_HEAP_MSGQ));
-
#ifdef SHCOPY_SPAWN
arg_size = copy_shared_calculate(args, &info);
#else
@@ -11699,7 +11845,7 @@ erl_create_process(Process* parent, /* Parent of process (default group leader).
p->common.u.alive.reg = NULL;
ERTS_P_LINKS(p) = NULL;
ERTS_P_MONITORS(p) = NULL;
- p->nodes_monitors = NULL;
+ ERTS_P_LT_MONITORS(p) = NULL;
p->suspend_monitors = NULL;
ASSERT(is_pid(parent->group_leader));
@@ -11716,14 +11862,20 @@ erl_create_process(Process* parent, /* Parent of process (default group leader).
erts_get_default_proc_tracing(&ERTS_TRACE_FLAGS(p), &ERTS_TRACER(p));
- p->msg.first = NULL;
- p->msg.last = &p->msg.first;
- p->msg.save = &p->msg.first;
- p->msg.saved_last = &p->msg.first;
- p->msg.len = 0;
- p->msg_inq.first = NULL;
- p->msg_inq.last = &p->msg_inq.first;
- p->msg_inq.len = 0;
+ p->sig_qs.first = NULL;
+ p->sig_qs.last = &p->sig_qs.first;
+ p->sig_qs.cont = NULL;
+ p->sig_qs.cont_last = &p->sig_qs.cont;
+ p->sig_qs.save = &p->sig_qs.first;
+ p->sig_qs.saved_last = NULL;
+ p->sig_qs.len = 0;
+ p->sig_qs.nmsigs.next = NULL;
+ p->sig_qs.nmsigs.last = NULL;
+ p->sig_inq.first = NULL;
+ p->sig_inq.last = &p->sig_inq.first;
+ p->sig_inq.len = 0;
+ p->sig_inq.nmsigs.next = NULL;
+ p->sig_inq.nmsigs.last = NULL;
p->bif_timers = NULL;
p->mbuf = NULL;
p->msg_frag = NULL;
@@ -11750,8 +11902,6 @@ erl_create_process(Process* parent, /* Parent of process (default group leader).
p->scheduler_data = NULL;
p->suspendee = NIL;
p->pending_suspenders = NULL;
- p->pending_exit.reason = THE_NON_VALUE;
- p->pending_exit.bp = NULL;
#if !defined(NO_FPE_SIGNALS) || defined(HIPE)
p->fp_exception = 0;
@@ -11805,30 +11955,33 @@ erl_create_process(Process* parent, /* Parent of process (default group leader).
*/
if (so->flags & SPO_LINK) {
-#ifdef DEBUG
- int ret;
-#endif
-#ifdef DEBUG
- ret = erts_add_link(&ERTS_P_LINKS(parent), LINK_PID, p->common.id);
- ASSERT(ret == 0);
- ret = erts_add_link(&ERTS_P_LINKS(p), LINK_PID, parent->common.id);
- ASSERT(ret == 0);
-#else
- erts_add_link(&ERTS_P_LINKS(parent), LINK_PID, p->common.id);
- erts_add_link(&ERTS_P_LINKS(p), LINK_PID, parent->common.id);
-#endif
-
+ ErtsLink *lnk;
+ ErtsLinkData *ldp = erts_link_create(ERTS_LNK_TYPE_PROC,
+ parent->common.id,
+ p->common.id);
+ lnk = erts_link_tree_lookup_insert(&ERTS_P_LINKS(parent), &ldp->a);
+ if (lnk) {
+ /*
+ * This should more or less never happen, but could
+ * potentially happen if pid:s wrap...
+ */
+ erts_link_release(lnk);
+ }
+ erts_link_tree_insert(&ERTS_P_LINKS(p), &ldp->b);
}
/*
* Test whether this process should be initially monitored by its parent.
*/
if (so->flags & SPO_MONITOR) {
- Eterm mref;
-
- mref = erts_make_ref(parent);
- erts_add_monitor(&ERTS_P_MONITORS(parent), MON_ORIGIN, mref, p->common.id, NIL);
- erts_add_monitor(&ERTS_P_MONITORS(p), MON_TARGET, mref, parent->common.id, NIL);
+ Eterm mref = erts_make_ref(parent);
+ ErtsMonitorData *mdp = erts_monitor_create(ERTS_MON_TYPE_PROC,
+ mref,
+ parent->common.id,
+ p->common.id,
+ NIL);
+ erts_monitor_tree_insert(&ERTS_P_MONITORS(parent), &mdp->origin);
+ erts_monitor_list_insert(&ERTS_P_LT_MONITORS(p), &mdp->target);
so->mref = mref;
}
@@ -11913,13 +12066,23 @@ void erts_init_empty_process(Process *p)
p->mbuf_sz = 0;
erts_atomic_init_nob(&p->psd, (erts_aint_t) NULL);
ERTS_P_MONITORS(p) = NULL;
+ ERTS_P_LT_MONITORS(p) = NULL;
ERTS_P_LINKS(p) = NULL; /* List of links */
- p->nodes_monitors = NULL;
p->suspend_monitors = NULL;
- p->msg.first = NULL;
- p->msg.last = &p->msg.first;
- p->msg.save = &p->msg.first;
- p->msg.len = 0;
+ p->sig_qs.first = NULL;
+ p->sig_qs.last = &p->sig_qs.first;
+ p->sig_qs.cont = NULL;
+ p->sig_qs.cont_last = &p->sig_qs.cont;
+ p->sig_qs.save = &p->sig_qs.first;
+ p->sig_qs.saved_last = NULL;
+ p->sig_qs.len = 0;
+ p->sig_qs.nmsigs.next = NULL;
+ p->sig_qs.nmsigs.last = NULL;
+ p->sig_inq.first = NULL;
+ p->sig_inq.last = &p->sig_inq.first;
+ p->sig_inq.len = 0;
+ p->sig_inq.nmsigs.next = NULL;
+ p->sig_inq.nmsigs.last = NULL;
p->bif_timers = NULL;
p->dictionary = NULL;
p->seq_trace_clock = 0;
@@ -11947,7 +12110,6 @@ void erts_init_empty_process(Process *p)
p->def_arg_reg[5] = 0;
p->parent = NIL;
- p->approx_started = 0;
p->static_flags = 0;
p->common.u.alive.started_interval = 0;
@@ -11967,16 +12129,11 @@ void erts_init_empty_process(Process *p)
erts_atomic32_init_nob(&p->state, (erts_aint32_t) PRIORITY_NORMAL);
p->scheduler_data = NULL;
- p->msg_inq.first = NULL;
- p->msg_inq.last = &p->msg_inq.first;
- p->msg_inq.len = 0;
p->suspendee = NIL;
p->pending_suspenders = NULL;
- p->pending_exit.reason = THE_NON_VALUE;
- p->pending_exit.bp = NULL;
erts_proc_lock_init(p);
erts_proc_unlock(p, ERTS_PROC_LOCKS_ALL);
- RUNQ_SET_RQ(&p->run_queue, ERTS_RUNQ_IX(0));
+ erts_init_runq_proc(p, ERTS_RUNQ_IX(0), 0);
#if !defined(NO_FPE_SIGNALS) || defined(HIPE)
p->fp_exception = 0;
@@ -12009,11 +12166,11 @@ erts_debug_verify_clean_empty_process(Process* p)
ASSERT(p->old_heap == NULL);
ASSERT(ERTS_P_MONITORS(p) == NULL);
+ ASSERT(ERTS_P_LT_MONITORS(p) == NULL);
ASSERT(ERTS_P_LINKS(p) == NULL);
- ASSERT(p->nodes_monitors == NULL);
ASSERT(p->suspend_monitors == NULL);
- ASSERT(p->msg.first == NULL);
- ASSERT(p->msg.len == 0);
+ ASSERT(p->sig_qs.first == NULL);
+ ASSERT(p->sig_qs.len == 0);
ASSERT(p->bif_timers == NULL);
ASSERT(p->dictionary == NULL);
ASSERT(p->catches == 0);
@@ -12023,12 +12180,10 @@ erts_debug_verify_clean_empty_process(Process* p)
ASSERT(p->parent == NIL);
- ASSERT(p->msg_inq.first == NULL);
- ASSERT(p->msg_inq.len == 0);
+ ASSERT(p->sig_inq.first == NULL);
+ ASSERT(p->sig_inq.len == 0);
ASSERT(p->suspendee == NIL);
ASSERT(p->pending_suspenders == NULL);
- ASSERT(p->pending_exit.reason == THE_NON_VALUE);
- ASSERT(p->pending_exit.bp == NULL);
/* Thing that erts_cleanup_empty_process() cleans up */
@@ -12129,817 +12284,330 @@ delete_process(Process* p)
erts_erase_dicts(p);
/* free all pending messages */
- erts_cleanup_messages(p->msg.first);
- p->msg.first = NULL;
+ erts_cleanup_messages(p->sig_qs.first);
+ p->sig_qs.first = NULL;
+ erts_cleanup_messages(p->sig_qs.cont);
+ p->sig_qs.cont = NULL;
- ASSERT(!p->nodes_monitors);
ASSERT(!p->suspend_monitors);
p->fvalue = NIL;
}
static ERTS_INLINE void
-set_proc_exiting(Process *p,
- erts_aint32_t in_state,
- Eterm reason,
- ErlHeapFragment *bp)
-{
- erts_aint32_t state = in_state, enq_prio = -1;
- int enqueue;
- ERTS_LC_ASSERT(erts_proc_lc_my_proc_locks(p) == ERTS_PROC_LOCKS_ALL);
-
- enqueue = change_proc_schedule_state(p,
- (ERTS_PSFLG_SUSPENDED
- | ERTS_PSFLG_PENDING_EXIT
- | ERTS_PSFLGS_DIRTY_WORK),
- ERTS_PSFLG_EXITING|ERTS_PSFLG_ACTIVE,
- &state,
- &enq_prio,
- ERTS_PROC_LOCKS_ALL);
-
- p->fvalue = reason;
- if (bp)
- erts_link_mbuf_to_proc(p, bp);
- /*
- * We used to set freason to EXC_EXIT here, but there is no need to
- * save the stack trace since this process irreversibly is going to
- * exit.
- */
- p->freason = EXTAG_EXIT;
- KILL_CATCHES(p);
- p->i = (BeamInstr *) beam_exit;
-
-
- add2runq(enqueue, enq_prio, p, state, NULL);
-}
-
-static ERTS_INLINE erts_aint32_t
-set_proc_self_exiting(Process *c_p)
+set_self_exiting(Process *c_p, Eterm reason, int *enqueue,
+ erts_aint32_t *prio, erts_aint32_t *state)
{
-#ifdef DEBUG
- int enqueue;
-#endif
- erts_aint32_t state, enq_prio = -1;
+ erts_aint32_t st, enq_prio = -1;
+ int enq;
ERTS_LC_ASSERT(erts_proc_lc_my_proc_locks(c_p) == ERTS_PROC_LOCKS_ALL);
- state = erts_atomic32_read_nob(&c_p->state);
- ASSERT(state & (ERTS_PSFLG_RUNNING
- |ERTS_PSFLG_RUNNING_SYS
- | ERTS_PSFLG_DIRTY_RUNNING
- | ERTS_PSFLG_DIRTY_RUNNING_SYS));
-
-#ifdef DEBUG
- enqueue =
-#endif
- change_proc_schedule_state(c_p,
- ERTS_PSFLG_SUSPENDED|ERTS_PSFLG_PENDING_EXIT,
- ERTS_PSFLG_EXITING|ERTS_PSFLG_ACTIVE,
- &state,
- &enq_prio,
- ERTS_PROC_LOCKS_ALL);
-
- ASSERT(!enqueue);
- return state;
-}
-
+ c_p->fvalue = reason;
-void
-erts_handle_pending_exit(Process *c_p, ErtsProcLocks locks)
-{
- ErtsProcLocks xlocks;
- ASSERT(is_value(c_p->pending_exit.reason));
- ERTS_LC_ASSERT(erts_proc_lc_my_proc_locks(c_p) == locks);
- ERTS_LC_ASSERT(locks & ERTS_PROC_LOCK_MAIN);
- ERTS_LC_ASSERT(!((ERTS_PSFLG_EXITING|ERTS_PSFLG_FREE)
- & erts_atomic32_read_nob(&c_p->state)));
-
- /* Ensure that all locks on c_p are locked before proceeding... */
- if (locks == ERTS_PROC_LOCKS_ALL)
- xlocks = 0;
- else {
- xlocks = ~locks & ERTS_PROC_LOCKS_ALL;
- if (erts_proc_trylock(c_p, xlocks) == EBUSY) {
- erts_proc_unlock(c_p, locks & ~ERTS_PROC_LOCK_MAIN);
- erts_proc_lock(c_p, ERTS_PROC_LOCKS_ALL_MINOR);
- }
- }
-
- set_proc_exiting(c_p,
- erts_atomic32_read_acqb(&c_p->state),
- c_p->pending_exit.reason,
- c_p->pending_exit.bp);
- c_p->pending_exit.reason = THE_NON_VALUE;
- c_p->pending_exit.bp = NULL;
+ st = erts_atomic32_read_nob(&c_p->state);
+ ASSERT(enqueue || (st & (ERTS_PSFLG_RUNNING
+ |ERTS_PSFLG_RUNNING_SYS
+ | ERTS_PSFLG_DIRTY_RUNNING
+ | ERTS_PSFLG_DIRTY_RUNNING_SYS)));
- if (xlocks)
- erts_proc_unlock(c_p, xlocks);
-}
-
-static void save_pending_exiter(Process *p, ErtsProcList *plp);
-
-static void
-do_handle_pending_exiters(ErtsProcList *pnd_xtrs)
-{
- /* 'list' is expected to have been fetched (i.e. not a ring anymore) */
- ErtsProcList *plp = pnd_xtrs;
+ enq = change_proc_schedule_state(c_p,
+ (ERTS_PSFLG_SUSPENDED
+ | ERTS_PSFLGS_DIRTY_WORK),
+ ERTS_PSFLG_EXITING|ERTS_PSFLG_ACTIVE,
+ &st,
+ &enq_prio,
+ ERTS_PROC_LOCKS_ALL);
- while (plp) {
- ErtsProcList *next_plp = plp->next;
- Process *p = erts_proc_lookup(plp->pid);
- if (p) {
- erts_aint32_t state;
- /*
- * If the process is running on a normal scheduler, the
- * pending exit will soon be detected and handled by the
- * scheduler running the process (at schedule in/out).
- */
- if (erts_proc_trylock(p, ERTS_PROC_LOCKS_ALL) != EBUSY) {
- if (erts_proclist_same(plp, p)) {
- state = erts_atomic32_read_acqb(&p->state);
- if (!(state & (ERTS_PSFLG_RUNNING
- | ERTS_PSFLG_RUNNING_SYS
- | ERTS_PSFLG_EXITING))) {
- ASSERT(state & ERTS_PSFLG_PENDING_EXIT);
- erts_handle_pending_exit(p, ERTS_PROC_LOCKS_ALL);
- }
- }
- erts_proc_unlock(p, ERTS_PROC_LOCKS_ALL);
- }
- else {
- erts_proc_lock(p, ERTS_PROC_LOCK_STATUS);
- if (erts_proclist_same(plp, p)) {
- state = erts_atomic32_read_acqb(&p->state);
- if (!(state & (ERTS_PSFLG_RUNNING
- | ERTS_PSFLG_RUNNING_SYS
- | ERTS_PSFLG_EXITING))) {
- /*
- * Save process and try to acquire all
- * locks at a later time...
- */
- save_pending_exiter(p, plp);
- plp = NULL;
- }
- }
- erts_proc_unlock(p, ERTS_PROC_LOCK_STATUS);
- }
- }
- if (plp)
- proclist_destroy(plp);
- plp = next_plp;
- }
+ ASSERT(enqueue || !enq);
+ if (enqueue)
+ *enqueue = enq;
+ if (prio)
+ *prio = enq_prio;
+ if (state)
+ *state = st;
}
-static void
-save_pending_exiter(Process *p, ErtsProcList *plp)
+void
+erts_set_self_exiting(Process *c_p, Eterm reason)
{
- ErtsSchedulerSleepInfo *ssi;
- ErtsRunQueue *rq;
-
- ERTS_LC_ASSERT(ERTS_PROC_LOCK_STATUS & erts_proc_lc_my_proc_locks(p));
-
- rq = RUNQ_READ_RQ(&p->run_queue);
- ASSERT(rq && !ERTS_RUNQ_IX_IS_DIRTY(rq->ix));
-
- if (!plp)
- plp = proclist_create(p);
-
- erts_runq_lock(rq);
-
- erts_proclist_store_last(&rq->procs.pending_exiters, plp);
-
- non_empty_runq(rq);
-
- ssi = rq->scheduler->ssi;
-
- erts_runq_unlock(rq);
+ int enqueue;
+ erts_aint32_t enq_prio, state;
+ ERTS_LC_ASSERT(erts_proc_lc_my_proc_locks(c_p) == ERTS_PROC_LOCK_MAIN);
- set_aux_work_flags_wakeup_nob(ssi, ERTS_SSI_AUX_WORK_PENDING_EXITERS);
-}
+ erts_proc_lock(c_p, ERTS_PROC_LOCKS_ALL_MINOR);
+ set_self_exiting(c_p, reason, &enqueue, &enq_prio, &state);
+ c_p->freason = EXTAG_EXIT;
+ KILL_CATCHES(c_p);
+ c_p->i = (BeamInstr *) beam_exit;
-/*
- * This function delivers an EXIT message to a process
- * which is trapping EXITs.
- */
+ /* Always active when exiting... */
+ ASSERT(state & ERTS_PSFLG_ACTIVE);
-static ERTS_INLINE void
-send_exit_message(Process *to, ErtsProcLocks *to_locksp,
- Eterm exit_term, Uint term_size, Eterm token)
-{
- ErtsMessage *mp;
- ErlOffHeap *ohp;
- Eterm* hp;
- Eterm mess;
-#ifdef SHCOPY_SEND
- erts_shcopy_t info;
-#endif
+ /*
+ * If we are terminating a process that currently
+ * is executing on a dirty scheduler. It *should*
+ * be scheduled on a normal scheduler...
+ */
+ ASSERT(!(state & (ERTS_PSFLG_DIRTY_RUNNING
+ | ERTS_PSFLG_DIRTY_RUNNING_SYS))
+ || enqueue == ERTS_ENQUEUE_NORMAL_QUEUE
+ || enqueue == -ERTS_ENQUEUE_NORMAL_QUEUE);
- if (!have_seqtrace(token)) {
-#ifdef SHCOPY_SEND
- INITIALIZE_SHCOPY(info);
- term_size = copy_shared_calculate(exit_term, &info);
- mp = erts_alloc_message_heap(to, to_locksp, term_size, &hp, &ohp);
- mess = copy_shared_perform(exit_term, term_size, &info, &hp, ohp);
- DESTROY_SHCOPY(info);
-#else
- mp = erts_alloc_message_heap(to, to_locksp, term_size, &hp, &ohp);
- mess = copy_struct(exit_term, term_size, &hp, ohp);
-#endif
- erts_queue_message(to, *to_locksp, mp, mess, am_system);
- } else {
- Eterm temp_token;
- Uint sz_token;
-
- ASSERT(is_tuple(token));
- sz_token = size_object(token);
-#ifdef SHCOPY_SEND
- INITIALIZE_SHCOPY(info);
- term_size = copy_shared_calculate(exit_term, &info);
- mp = erts_alloc_message_heap(to, to_locksp, term_size+sz_token, &hp, &ohp);
- mess = copy_shared_perform(exit_term, term_size, &info, &hp, ohp);
- DESTROY_SHCOPY(info);
-#else
- mp = erts_alloc_message_heap(to, to_locksp, term_size+sz_token, &hp, &ohp);
- mess = copy_struct(exit_term, term_size, &hp, ohp);
-#endif
- /* the trace token must in this case be updated by the caller */
- seq_trace_output(token, mess, SEQ_TRACE_SEND, to->common.id, to);
- temp_token = copy_struct(token, sz_token, &hp, ohp);
- ERL_MESSAGE_TOKEN(mp) = temp_token;
- erts_queue_message(to, *to_locksp, mp, mess, am_system);
- }
+ erts_proc_unlock(c_p, ERTS_PROC_LOCKS_ALL_MINOR);
+ if (enqueue)
+ add2runq(enqueue, enq_prio, c_p, state, NULL);
}
-/*
- *
- * *** Exit signal behavior ***
- *
- * Exit signals are asynchronous (truly asynchronous in the
- * SMP emulator). When the signal is received the receiver receives an
- * 'EXIT' message if it is trapping exits; otherwise, it will either
- * ignore the signal if the exit reason is normal, or go into an
- * exiting state (ERTS_PSFLG_EXITING). When a process has gone into the
- * exiting state it will not execute any more Erlang code, but it might
- * take a while before it actually exits. The exit signal is being
- * received when the 'EXIT' message is put in the message queue, the
- * signal is dropped, or when it changes state into exiting. The time it
- * is in the exiting state before actually exiting is undefined (it
- * might take a really long time under certain conditions). The
- * receiver of the exit signal does not break links or trigger monitors
- * until it actually exits.
- *
- * Exit signals and other signals, e.g. messages, have to be received
- * by a receiver in the same order as sent by a sender.
- *
- *
- *
- * Exit signal implementation in the SMP emulator:
- *
- * If the receiver is trapping exits, the signal is transformed
- * into an 'EXIT' message and sent as a normal message, if the
- * reason is normal the signal is dropped; otherwise, the process
- * is determined to be exited. The interesting case is when the
- * process is to be exited and this is what is described below.
- *
- * If it is possible, the receiver is set in the exiting state straight
- * away and we are done; otherwise, the sender places the exit reason
- * in the pending_exit field of the process struct and if necessary
- * adds the receiver to the run queue. It is typically not possible
- * to set a scheduled process or a process which we cannot get all locks
- * on without releasing locks on it in an exiting state straight away.
- *
- * The receiver will poll the pending_exit field when it reach certain
- * places during it's execution. When it discovers the pending exit
- * it will change state into the exiting state. If the receiver wasn't
- * scheduled when the pending exit was set, the first scheduler that
- * schedules a new process will set the receiving process in the exiting
- * state just before it schedules next process.
- *
- * When the exit signal is placed in the pending_exit field, the signal
- * is considered as being in transit on the Erlang level. The signal is
- * actually in some kind of semi transit state, since we have already
- * determined how it should be received. It will exit the process no
- * matter what if it is received (the process may exit by itself before
- * reception of the exit signal). The signal is received when it is
- * discovered in the pending_exit field by the receiver.
- *
- * The receiver have to poll the pending_exit field at least before:
- * - moving messages from the message in queue to the private message
- * queue. This in order to preserve signal order.
- * - unlink. Otherwise the process might get exited on a link that
- * have been removed.
- * - changing the trap_exit flag to true. This in order to simplify the
- * implementation; otherwise, we would have to transform the signal
- * into an 'EXIT' message when setting the trap_exit flag to true. We
- * would also have to maintain a queue of exit signals in transit.
- * - being scheduled in or out.
- */
-
-static ERTS_INLINE int
-send_exit_signal(Process *c_p, /* current process if and only
- if reason is stored on it */
- Eterm from, /* Id of sender of signal */
- Process *rp, /* receiving process */
- ErtsProcLocks *rp_locks,/* current locks on receiver */
- Eterm reason, /* exit reason */
- Eterm exit_tuple, /* Prebuild exit tuple
- or THE_NON_VALUE */
- Uint exit_tuple_sz, /* Size of prebuilt exit tuple
- (if exit_tuple != THE_NON_VALUE) */
- Eterm token, /* token */
- Process *token_update, /* token updater */
- Uint32 flags /* flags */
- )
-{
- erts_aint32_t state = erts_atomic32_read_nob(&rp->state);
- Eterm rsn = reason == am_kill ? am_killed : reason;
-
- ERTS_LC_ASSERT(*rp_locks == erts_proc_lc_my_proc_locks(rp));
- ERTS_LC_ASSERT((*rp_locks & ERTS_PROC_LOCKS_XSIG_SEND)
- == ERTS_PROC_LOCKS_XSIG_SEND);
-
- ASSERT(reason != THE_NON_VALUE);
-
-#ifdef USE_VM_PROBES
- if(DTRACE_ENABLED(process_exit_signal) && is_pid(from)) {
- DTRACE_CHARBUF(sender_str, DTRACE_TERM_BUF_SIZE);
- DTRACE_CHARBUF(receiver_str, DTRACE_TERM_BUF_SIZE);
- DTRACE_CHARBUF(reason_buf, DTRACE_TERM_BUF_SIZE);
-
- dtrace_pid_str(from, sender_str);
- dtrace_proc_str(rp, receiver_str);
- erts_snprintf(reason_buf, sizeof(DTRACE_CHARBUF_NAME(reason_buf)) - 1, "%T", reason);
- DTRACE3(process_exit_signal, sender_str, receiver_str, reason_buf);
+void
+erts_proc_exit_handle_monitor(ErtsMonitor *mon, void *vctxt)
+{
+ Process *c_p = ((ErtsProcExitContext *) vctxt)->c_p;
+ Eterm reason = ((ErtsProcExitContext *) vctxt)->reason;
+ ErtsMonitorData *mdp = NULL;
+
+ if (erts_monitor_is_target(mon)) {
+ /* We are being watched... */
+ switch (mon->type) {
+ case ERTS_MON_TYPE_PROC:
+ erts_proc_sig_send_monitor_down(mon, reason);
+ mon = NULL;
+ break;
+ case ERTS_MON_TYPE_PORT: {
+ Port *prt;
+ ASSERT(is_internal_port(mon->other.item));
+ prt = erts_id2port(mon->other.item);
+ if (prt) {
+ erts_fire_port_monitor(prt, mon);
+ erts_port_release(prt);
+ mon = NULL;
+ }
+ break;
+ }
+ case ERTS_MON_TYPE_RESOURCE:
+ erts_fire_nif_monitor(mon);
+ mon = NULL;
+ break;
+ case ERTS_MON_TYPE_DIST_PROC: {
+ ErtsMonLnkDist *dist;
+ DistEntry *dep;
+ ErtsDSigData dsd;
+ int code;
+ Eterm watcher;
+ Eterm watched;
+
+ mdp = erts_monitor_to_data(mon);
+
+ if (mon->flags & ERTS_ML_FLG_NAME)
+ watched = ((ErtsMonitorDataExtended *) mdp)->u.name;
+ else
+ watched = c_p->common.id;
+ ASSERT(is_internal_pid(watched) || is_atom(watched));
+
+ watcher = mon->other.item;
+ ASSERT(is_external_pid(watcher));
+ dep = external_pid_dist_entry(watcher);
+ ASSERT(dep);
+ dist = ((ErtsMonitorDataExtended *) mdp)->dist;
+ ASSERT(dist);
+ code = erts_dsig_prepare(&dsd, dep, NULL, 0,
+ ERTS_DSP_NO_LOCK, 0, 0);
+ switch (code) {
+ case ERTS_DSIG_PREP_CONNECTED:
+ case ERTS_DSIG_PREP_PENDING:
+ if (dist->connection_id == dsd.connection_id) {
+ code = erts_dsig_send_m_exit(&dsd,
+ watcher,
+ watched,
+ mdp->ref,
+ reason);
+ ASSERT(code == ERTS_DSIG_SEND_OK);
+ }
+ default:
+ break;
+ }
+ if (!erts_monitor_dist_delete(&mdp->origin))
+ mdp = NULL;
+ break;
+ }
+ default:
+ ERTS_INTERNAL_ERROR("Invalid target monitor type");
+ break;
+ }
}
-#endif
-
- if ((state & ERTS_PSFLG_TRAP_EXIT)
- && (reason != am_kill || (flags & ERTS_XSIG_FLG_IGN_KILL))) {
- /* have to release the status and trace lock in order to send the exit message */
- erts_proc_unlock(rp, *rp_locks & (ERTS_PROC_LOCKS_XSIG_SEND|ERTS_PROC_LOCK_TRACE));
- *rp_locks &= ~(ERTS_PROC_LOCKS_XSIG_SEND|ERTS_PROC_LOCK_TRACE);
- if (have_seqtrace(token) && token_update)
- seq_trace_update_send(token_update);
- if (is_value(exit_tuple))
- send_exit_message(rp, rp_locks, exit_tuple, exit_tuple_sz, token);
- else
- erts_deliver_exit_message(from, rp, rp_locks, rsn, token);
- return 1; /* Receiver will get a message */
- }
- else if (reason != am_normal || (flags & ERTS_XSIG_FLG_NO_IGN_NORMAL)) {
- if (!(state & (ERTS_PSFLG_EXITING|ERTS_PSFLG_PENDING_EXIT))) {
- ASSERT(!rp->pending_exit.bp);
-
- if (rp == c_p && (*rp_locks & ERTS_PROC_LOCK_MAIN)) {
- /* Ensure that all locks on c_p are locked before
- proceeding... */
- if (*rp_locks != ERTS_PROC_LOCKS_ALL) {
- ErtsProcLocks need_locks = (~(*rp_locks)
- & ERTS_PROC_LOCKS_ALL);
- if (erts_proc_trylock(c_p, need_locks) == EBUSY) {
- erts_proc_unlock(c_p,
- *rp_locks & ~ERTS_PROC_LOCK_MAIN);
- erts_proc_lock(c_p, ERTS_PROC_LOCKS_ALL_MINOR);
- }
- *rp_locks = ERTS_PROC_LOCKS_ALL;
- }
- set_proc_exiting(c_p, state, rsn, NULL);
- }
- else if (!(state & (ERTS_PSFLG_RUNNING
- | ERTS_PSFLG_RUNNING_SYS
- | ERTS_PSFLG_DIRTY_RUNNING_SYS))) {
- /* Process not running ... */
- ErtsProcLocks need_locks = ~(*rp_locks) & ERTS_PROC_LOCKS_ALL;
- ErlHeapFragment *bp = NULL;
- Eterm rsn_cpy;
- if (need_locks
- && erts_proc_trylock(rp, need_locks) == EBUSY) {
- /* ... but we havn't got all locks on it ... */
- save_pending_exiter(rp, NULL);
- /*
- * The pending exit will be discovered when next
- * process is scheduled in
- */
- goto set_pending_exit;
- }
- /* ...and we have all locks on it... */
- *rp_locks = ERTS_PROC_LOCKS_ALL;
-
- state = erts_atomic32_read_nob(&rp->state);
-
- if (is_immed(rsn))
- rsn_cpy = rsn;
- else {
- Eterm *hp;
- ErlOffHeap *ohp;
- Uint rsn_sz = size_object(rsn);
- if (state & ERTS_PSFLG_DIRTY_RUNNING) {
- bp = new_message_buffer(rsn_sz);
- ohp = &bp->off_heap;
- hp = &bp->mem[0];
- }
- else
- {
- hp = HAlloc(rp, rsn_sz);
- ohp = &rp->off_heap;
- }
- rsn_cpy = copy_struct(rsn, rsn_sz, &hp, ohp);
- }
-
- set_proc_exiting(rp, state, rsn_cpy, bp);
- }
- else { /* Process running... */
-
- /*
- * The pending exit will be discovered when the process
- * is scheduled out if not discovered earlier.
- */
-
- set_pending_exit:
- if (is_immed(rsn)) {
- rp->pending_exit.reason = rsn;
- }
- else {
- Eterm *hp;
- Uint sz = size_object(rsn);
- ErlHeapFragment *bp = new_message_buffer(sz);
-
- hp = &bp->mem[0];
- rp->pending_exit.reason = copy_struct(rsn,
- sz,
- &hp,
- &bp->off_heap);
- rp->pending_exit.bp = bp;
- }
-
- /*
- * If no dirty work has been scheduled, pending exit will
- * be discovered when the process is scheduled. If dirty work
- * has been scheduled, we may need to add it to a normal run
- * queue...
- */
- {
- erts_aint32_t a = erts_atomic32_read_nob(&rp->state);
- while (1) {
- erts_aint32_t n, e;
- int dwork;
- n = e = a;
- n |= ERTS_PSFLG_PENDING_EXIT;
- dwork = !!(n & ERTS_PSFLGS_DIRTY_WORK);
- n &= ~ERTS_PSFLGS_DIRTY_WORK;
- a = erts_atomic32_cmpxchg_mb(&rp->state, n, e);
- if (a == e) {
- if (dwork)
- erts_schedule_process(rp, n, *rp_locks);
- break;
- }
- }
+ else { /* Origin monitor */
+ /* We are watching someone else... */
+ switch (mon->type) {
+ case ERTS_MON_TYPE_PROC:
+ erts_proc_sig_send_demonitor(mon);
+ mon = NULL;
+ break;
+ case ERTS_MON_TYPE_TIME_OFFSET:
+ erts_demonitor_time_offset(mon);
+ mon = NULL;
+ break;
+ case ERTS_MON_TYPE_NODE:
+ mdp = erts_monitor_to_data(mon);
+ if (!erts_monitor_dist_delete(&mdp->target))
+ mdp = NULL;
+ break;
+ case ERTS_MON_TYPE_NODES:
+ erts_monitor_nodes_delete(mon);
+ mon = NULL;
+ break;
+ case ERTS_MON_TYPE_PORT: {
+ Port *prt;
+ ASSERT(is_internal_port(mon->other.item));
+ prt = erts_port_lookup_raw(mon->other.item);
+ if (prt) {
+ erts_proc_lock(c_p, ERTS_PROC_LOCK_MAIN);
+ if (erts_port_demonitor(c_p, prt, mon) != ERTS_PORT_OP_DROPPED)
+ mon = NULL;
+ erts_proc_unlock(c_p, ERTS_PROC_LOCK_MAIN);
+ }
+ break;
+ }
+ case ERTS_MON_TYPE_DIST_PROC: {
+ ErtsMonLnkDist *dist;
+ DistEntry *dep;
+ ErtsDSigData dsd;
+ int code;
+ Eterm watched;
+
+ mdp = erts_monitor_to_data(mon);
+ dist = ((ErtsMonitorDataExtended *) mdp)->dist;
+ ASSERT(dist);
+ if (mon->flags & ERTS_ML_FLG_NAME) {
+ watched = ((ErtsMonitorDataExtended *) mdp)->u.name;
+ ASSERT(is_atom(watched));
+ dep = erts_sysname_to_connected_dist_entry(dist->nodename);
+ }
+ else {
+ watched = mon->other.item;
+ ASSERT(is_external_pid(watched));
+ dep = external_pid_dist_entry(watched);
+ }
+ code = erts_dsig_prepare(&dsd, dep, NULL, 0,
+ ERTS_DSP_NO_LOCK, 0, 0);
+ switch (code) {
+ case ERTS_DSIG_PREP_CONNECTED:
+ case ERTS_DSIG_PREP_PENDING:
+ if (dist->connection_id == dsd.connection_id) {
+ code = erts_dsig_send_demonitor(&dsd,
+ c_p->common.id,
+ watched,
+ mdp->ref,
+ 1);
+ ASSERT(code == ERTS_DSIG_SEND_OK);
}
- }
- }
- /* else:
- *
- * The receiver already has a pending exit (or is exiting)
- * so we drop this signal.
- *
- * NOTE: dropping this exit signal is based on the assumption
- * that the receiver *will* exit; either on the pending
- * exit or by itself before seeing the pending exit.
- */
- return -1; /* Receiver will exit */
+ default:
+ break;
+ }
+ if (!erts_monitor_dist_delete(&mdp->target))
+ mdp = NULL;
+ break;
+ }
+ default:
+ ERTS_INTERNAL_ERROR("Invalid origin monitor type");
+ break;
+ }
}
- return 0; /* Receiver unaffected */
-}
-
-
-int
-erts_send_exit_signal(Process *c_p,
- Eterm from,
- Process *rp,
- ErtsProcLocks *rp_locks,
- Eterm reason,
- Eterm token,
- Process *token_update,
- Uint32 flags)
-{
- return send_exit_signal(c_p,
- from,
- rp,
- rp_locks,
- reason,
- THE_NON_VALUE,
- 0,
- token,
- token_update,
- flags);
+ if (mdp)
+ erts_monitor_release_both(mdp);
+ else if (mon)
+ erts_monitor_release(mon);
}
-typedef struct {
- Eterm reason;
- Process *p;
-} ExitMonitorContext;
-
-static void doit_exit_monitor(ErtsMonitor *mon, void *vpcontext)
-{
- ExitMonitorContext *pcontext = vpcontext;
- DistEntry *dep;
- ErtsMonitor *rmon;
-
- switch (mon->type) {
- case MON_ORIGIN:
- /* We are monitoring someone else, we need to demonitor that one.. */
- if (is_atom(mon->u.pid)) { /* remote by name */
- ASSERT(is_node_name_atom(mon->u.pid));
- dep = erts_sysname_to_connected_dist_entry(mon->u.pid);
- if (dep) {
- erts_de_links_lock(dep);
- rmon = erts_remove_monitor(&(dep->monitors), mon->ref);
- erts_de_links_unlock(dep);
- if (rmon) {
- ErtsDSigData dsd;
- int code = erts_dsig_prepare(&dsd, dep, NULL, 0,
- ERTS_DSP_NO_LOCK, 0, 0);
- if (code == ERTS_DSIG_PREP_CONNECTED ||
- code == ERTS_DSIG_PREP_PENDING) {
-
- code = erts_dsig_send_demonitor(&dsd,
- rmon->u.pid,
- mon->name,
- mon->ref,
- 1);
- ASSERT(code == ERTS_DSIG_SEND_OK);
- }
- erts_destroy_monitor(rmon);
- }
- }
- } else {
- ASSERT(is_pid(mon->u.pid) || is_port(mon->u.pid));
- /* if is local by pid or name */
- if (is_internal_pid(mon->u.pid)) {
- Process *rp = erts_pid2proc(NULL, 0, mon->u.pid, ERTS_PROC_LOCK_LINK);
- if (!rp) {
- goto done;
- }
- rmon = erts_remove_monitor(&ERTS_P_MONITORS(rp), mon->ref);
- erts_proc_unlock(rp, ERTS_PROC_LOCK_LINK);
- if (rmon == NULL) {
- goto done;
- }
- erts_destroy_monitor(rmon);
- } else if (is_internal_port(mon->u.pid)) {
- /* Is a local port */
- Port *prt = erts_port_lookup_raw(mon->u.pid);
- if (!prt) {
- goto done;
- }
- erts_port_demonitor(pcontext->p,
- ERTS_PORT_DEMONITOR_ORIGIN_ON_DEATHBED,
- prt, mon->ref, NULL);
- } else { /* remote by pid */
- ASSERT(is_external_pid(mon->u.pid));
- dep = external_pid_dist_entry(mon->u.pid);
- ASSERT(dep != NULL);
- if (dep) {
- erts_de_links_lock(dep);
- rmon = erts_remove_monitor(&(dep->monitors), mon->ref);
- erts_de_links_unlock(dep);
- if (rmon) {
- ErtsDSigData dsd;
- int code = erts_dsig_prepare(&dsd, dep, NULL, 0,
- ERTS_DSP_NO_LOCK, 0, 0);
- if (code == ERTS_DSIG_PREP_CONNECTED ||
- code == ERTS_DSIG_PREP_PENDING) {
-
- code = erts_dsig_send_demonitor(&dsd,
- rmon->u.pid,
- mon->u.pid,
- mon->ref,
- 1);
- ASSERT(code == ERTS_DSIG_SEND_OK);
- }
- erts_destroy_monitor(rmon);
- }
- }
- }
- }
- break;
- case MON_TARGET:
- ASSERT(is_pid(mon->u.pid) || is_internal_port(mon->u.pid));
- if (is_internal_port(mon->u.pid)) {
- Port *prt = erts_id2port(mon->u.pid);
- if (prt == NULL) {
- goto done;
- }
- erts_fire_port_monitor(prt, mon->ref);
- erts_port_release(prt);
- } else if (is_internal_pid(mon->u.pid)) {/* local by name or pid */
- Eterm watched;
- Process *rp;
- DeclareTmpHeapNoproc(lhp,3);
- ErtsProcLocks rp_locks = (ERTS_PROC_LOCK_LINK
- | ERTS_PROC_LOCKS_MSG_SEND);
- rp = erts_pid2proc(NULL, 0, mon->u.pid, rp_locks);
- if (rp == NULL) {
- goto done;
- }
- UseTmpHeapNoproc(3);
- rmon = erts_remove_monitor(&ERTS_P_MONITORS(rp), mon->ref);
- if (rmon) {
- erts_destroy_monitor(rmon);
- watched = (is_atom(mon->name)
- ? TUPLE2(lhp, mon->name,
- erts_this_dist_entry->sysname)
- : pcontext->p->common.id);
- erts_queue_monitor_message(rp, &rp_locks, mon->ref, am_process,
- watched, pcontext->reason);
- }
- UnUseTmpHeapNoproc(3);
- /* else: demonitor while we exited, i.e. do nothing... */
- erts_proc_unlock(rp, rp_locks);
- } else { /* external by pid or name */
- ASSERT(is_external_pid(mon->u.pid));
- dep = external_pid_dist_entry(mon->u.pid);
- ASSERT(dep != NULL);
- if (dep) {
- erts_de_links_lock(dep);
- rmon = erts_remove_monitor(&(dep->monitors), mon->ref);
- erts_de_links_unlock(dep);
- if (rmon) {
- ErtsDSigData dsd;
- int code = erts_dsig_prepare(&dsd, dep, NULL, 0,
- ERTS_DSP_NO_LOCK, 0, 0);
- if (code == ERTS_DSIG_PREP_CONNECTED) {
- code = erts_dsig_send_m_exit(&dsd,
- mon->u.pid,
- (rmon->name != NIL
- ? rmon->name
- : rmon->u.pid),
- mon->ref,
- pcontext->reason);
- ASSERT(code == ERTS_DSIG_SEND_OK);
- }
- erts_destroy_monitor(rmon);
- }
- }
- }
- break;
- case MON_NIF_TARGET:
- erts_fire_nif_monitor(mon->u.resource,
- pcontext->p->common.id,
- mon->ref);
+void
+erts_proc_exit_handle_link(ErtsLink *lnk, void *vctxt)
+{
+ Process *c_p = ((ErtsProcExitContext *) vctxt)->c_p;
+ Eterm reason = ((ErtsProcExitContext *) vctxt)->reason;
+ ErtsLinkData *ldp = NULL;
+
+ switch (lnk->type) {
+ case ERTS_LNK_TYPE_PROC:
+ ASSERT(is_internal_pid(lnk->other.item));
+ erts_proc_sig_send_link_exit(c_p, c_p->common.id, lnk,
+ reason, SEQ_TRACE_TOKEN(c_p));
+ lnk = NULL;
+ break;
+ case ERTS_LNK_TYPE_PORT: {
+ Port *prt;
+ ASSERT(is_internal_port(lnk->other.item));
+ prt = erts_port_lookup(lnk->other.item,
+ ERTS_PORT_SFLGS_INVALID_LOOKUP);
+ if (prt)
+ erts_port_exit(NULL,
+ (ERTS_PORT_SIG_FLG_FORCE_SCHED
+ | ERTS_PORT_SIG_FLG_BROKEN_LINK),
+ prt,
+ c_p->common.id,
+ reason,
+ NULL);
+ break;
+ }
+ case ERTS_LNK_TYPE_DIST_PROC: {
+ DistEntry *dep;
+ ErtsMonLnkDist *dist;
+ ErtsLink *dlnk;
+ ErtsDSigData dsd;
+ int code;
+
+ dlnk = erts_link_to_other(lnk, &ldp);
+ dist = ((ErtsLinkDataExtended *) ldp)->dist;
+
+ ASSERT(is_external_pid(lnk->other.item));
+ dep = external_pid_dist_entry(lnk->other.item);
+
+ ASSERT(dep != erts_this_dist_entry);
+
+ if (!erts_link_dist_delete(dlnk))
+ ldp = NULL;
+
+ erts_proc_lock(c_p, ERTS_PROC_LOCK_MAIN);
+ code = erts_dsig_prepare(&dsd, dep, c_p, 0, ERTS_DSP_NO_LOCK, 0, 0);
+ switch (code) {
+ case ERTS_DSIG_PREP_CONNECTED:
+ case ERTS_DSIG_PREP_PENDING:
+ if (dist->connection_id == dsd.connection_id) {
+ code = erts_dsig_send_exit_tt(&dsd,
+ c_p->common.id,
+ lnk->other.item,
+ reason,
+ SEQ_TRACE_TOKEN(c_p));
+ ASSERT(code == ERTS_DSIG_SEND_OK);
+ }
+ }
+ erts_proc_unlock(c_p, ERTS_PROC_LOCK_MAIN);
break;
- case MON_TIME_OFFSET:
- erts_demonitor_time_offset(mon->ref);
- break;
- default:
- ERTS_INTERNAL_ERROR("Invalid monitor type");
}
- done:
- /* As the monitors are previously removed from the process,
- distribution operations will not cause monitors to disappear,
- we can safely delete it. */
-
- erts_destroy_monitor(mon);
-}
-
-typedef struct {
- Process *p;
- Eterm reason;
- Eterm exit_tuple;
- Uint exit_tuple_sz;
-} ExitLinkContext;
-
-static void doit_exit_link(ErtsLink *lnk, void *vpcontext)
-{
- ExitLinkContext *pcontext = vpcontext;
- /* Unpack context, it's readonly */
- Process *p = pcontext->p;
- Eterm reason = pcontext->reason;
- Eterm exit_tuple = pcontext->exit_tuple;
- Uint exit_tuple_sz = pcontext->exit_tuple_sz;
- Eterm item = lnk->pid;
- ErtsLink *rlnk;
- DistEntry *dep;
- Process *rp;
-
-
- switch(lnk->type) {
- case LINK_PID:
- if(is_internal_port(item)) {
- Port *prt = erts_port_lookup(item, ERTS_PORT_SFLGS_INVALID_LOOKUP);
- if (prt)
- erts_port_exit(NULL,
- (ERTS_PORT_SIG_FLG_FORCE_SCHED
- | ERTS_PORT_SIG_FLG_BROKEN_LINK),
- prt,
- p->common.id,
- reason,
- NULL);
- }
- else if(is_external_port(item)) {
- erts_dsprintf_buf_t *dsbufp = erts_create_logger_dsbuf();
- erts_dsprintf(dsbufp,
- "Erroneous link between %T and external port %T "
- "found\n",
- p->common.id,
- item);
- erts_send_error_to_logger_nogl(dsbufp);
- ASSERT(0); /* It isn't possible to setup such a link... */
- }
- else if (is_internal_pid(item)) {
- ErtsProcLocks rp_locks = (ERTS_PROC_LOCK_LINK
- | ERTS_PROC_LOCKS_XSIG_SEND);
- rp = erts_pid2proc(NULL, 0, item, rp_locks);
- if (rp) {
- rlnk = erts_remove_link(&ERTS_P_LINKS(rp), p->common.id);
- /* If rlnk == NULL, we got unlinked while exiting,
- i.e., do nothing... */
- if (rlnk) {
- int xres;
- erts_destroy_link(rlnk);
- xres = send_exit_signal(NULL,
- p->common.id,
- rp,
- &rp_locks,
- reason,
- exit_tuple,
- exit_tuple_sz,
- SEQ_TRACE_TOKEN(p),
- p,
- ERTS_XSIG_FLG_IGN_KILL);
- if (xres >= 0 && IS_TRACED_FL(rp, F_TRACE_PROCS)) {
- /* We didn't exit the process and it is traced */
- if (IS_TRACED_FL(rp, F_TRACE_PROCS)) {
- if (rp_locks & ERTS_PROC_LOCKS_XSIG_SEND) {
- erts_proc_unlock(rp, ERTS_PROC_LOCKS_XSIG_SEND);
- rp_locks &= ~ERTS_PROC_LOCKS_XSIG_SEND;
- }
- trace_proc(NULL, 0, rp, am_getting_unlinked, p->common.id);
- }
- }
- }
- ASSERT(rp != p);
- erts_proc_unlock(rp, rp_locks);
- }
- }
- else if (is_external_pid(item)) {
- dep = external_pid_dist_entry(item);
- if(dep != erts_this_dist_entry) {
- ErtsDSigData dsd;
- int code;
- ErtsDistLinkData dld;
- erts_remove_dist_link(&dld, p->common.id, item, dep);
- if (dld.d_lnk) {
- erts_proc_lock(p, ERTS_PROC_LOCK_MAIN);
- code = erts_dsig_prepare(&dsd, dep, p, 0, ERTS_DSP_NO_LOCK, 0, 0);
- if (code == ERTS_DSIG_PREP_CONNECTED ||
- code == ERTS_DSIG_PREP_PENDING) {
-
- code = erts_dsig_send_exit_tt(&dsd, p->common.id, item,
- reason, SEQ_TRACE_TOKEN(p));
- ASSERT(code == ERTS_DSIG_SEND_OK);
- }
- erts_proc_unlock(p, ERTS_PROC_LOCK_MAIN);
- }
- erts_destroy_dist_link(&dld);
- }
- }
- break;
- case LINK_NODE:
- ASSERT(is_node_name_atom(item));
- dep = erts_sysname_to_connected_dist_entry(item);
- if(dep) {
- /* dist entries have node links in a separate structure to
- avoid confusion */
- erts_de_links_lock(dep);
- rlnk = erts_remove_link(&(dep->node_links), p->common.id);
- erts_de_links_unlock(dep);
- if (rlnk)
- erts_destroy_link(rlnk);
- }
- break;
-
default:
- erts_exit(ERTS_ERROR_EXIT, "bad type in link list\n");
- break;
+ ERTS_INTERNAL_ERROR("Unexpected link type");
+ break;
}
- erts_destroy_link(lnk);
+
+ if (ldp)
+ erts_link_release_both(ldp);
+ else if (lnk)
+ erts_link_release(lnk);
}
static void
-resume_suspend_monitor(ErtsSuspendMonitor *smon, void *vc_p)
+resume_suspend_monitor(ErtsMonitor *mon, void *vc_p)
{
+ ErtsMonitorSuspend *smon = erts_monitor_suspend(mon);
Process *suspendee = erts_pid2proc((Process *) vc_p, ERTS_PROC_LOCK_MAIN,
- smon->pid, ERTS_PROC_LOCK_STATUS);
+ smon->mon.other.item, ERTS_PROC_LOCK_STATUS);
if (suspendee) {
ASSERT(suspendee != vc_p);
if (smon->active)
resume_process(suspendee, ERTS_PROC_LOCK_STATUS);
erts_proc_unlock(suspendee, ERTS_PROC_LOCK_STATUS);
}
- erts_destroy_suspend_monitor(smon);
+ erts_monitor_suspend_destroy(smon);
}
/* this function fishishes a process and propagates exit messages - called
@@ -12948,8 +12616,6 @@ void
erts_do_exit_process(Process* p, Eterm reason)
{
p->arity = 0; /* No live registers */
- p->fvalue = reason;
-
#ifdef USE_VM_PROBES
if (DTRACE_ENABLED(process_exit)) {
@@ -12973,18 +12639,11 @@ erts_do_exit_process(Process* p, Eterm reason)
process from exiting until the lock has been released. */
erts_proc_lock(p, ERTS_PROC_LOCKS_ALL_MINOR);
- if (ERTS_PSFLG_PENDING_EXIT & set_proc_self_exiting(p)) {
- /* Process exited before pending exit was received... */
- p->pending_exit.reason = THE_NON_VALUE;
- if (p->pending_exit.bp) {
- free_message_buffer(p->pending_exit.bp);
- p->pending_exit.bp = NULL;
- }
- }
+ set_self_exiting(p, reason, NULL, NULL, NULL);
cancel_suspend_of_suspendee(p, ERTS_PROC_LOCKS_ALL);
- ERTS_MSGQ_MV_INQ2PRIVQ(p);
+ erts_proc_sig_fetch(p);
if (IS_TRACED(p)) {
if (IS_TRACED_FL(p, F_TRACE_CALLS))
@@ -13021,13 +12680,15 @@ erts_do_exit_process(Process* p, Eterm reason)
void
erts_continue_exit_process(Process *p)
{
- ErtsLink* lnk;
- ErtsMonitor *mon;
+ ErtsLink *links;
+ ErtsMonitor *monitors;
+ ErtsMonitor *lt_monitors;
ErtsProcLocks curr_locks = ERTS_PROC_LOCK_MAIN;
Eterm reason = p->fvalue;
DistEntry *dep = NULL;
erts_aint32_t state;
int delay_del_proc = 0;
+ ErtsProcExitContext pectxt;
#ifdef DEBUG
int yield_allowed = 1;
@@ -13099,16 +12760,13 @@ erts_continue_exit_process(Process *p)
erts_set_gc_state(p, 1);
state = erts_atomic32_read_acqb(&p->state);
- if (state & ERTS_PSFLG_ACTIVE_SYS
- || p->dirty_sys_tasks
- ) {
+ if ((state & ERTS_PSFLG_SYS_TASKS) || p->dirty_sys_tasks) {
if (cleanup_sys_tasks(p, state, CONTEXT_REDS) >= CONTEXT_REDS/2)
goto yield;
}
#ifdef DEBUG
erts_proc_lock(p, ERTS_PROC_LOCK_STATUS);
- ASSERT(p->sys_task_qs == NULL);
ASSERT(ERTS_PROC_GET_DELAYED_GC_TASK_QS(p) == NULL);
ASSERT(p->dirty_sys_tasks == NULL);
erts_proc_unlock(p, ERTS_PROC_LOCK_STATUS);
@@ -13119,18 +12777,10 @@ erts_continue_exit_process(Process *p)
p->flags &= ~F_USING_DDLL;
}
- if (p->nodes_monitors) {
- erts_delete_nodes_monitors(p, ERTS_PROC_LOCK_MAIN);
- p->nodes_monitors = NULL;
- }
-
-
- if (p->suspend_monitors) {
- erts_sweep_suspend_monitors(p->suspend_monitors,
- resume_suspend_monitor,
- p);
- p->suspend_monitors = NULL;
- }
+ if (p->suspend_monitors)
+ erts_monitor_tree_foreach_delete(&p->suspend_monitors,
+ resume_suspend_monitor,
+ p);
/*
* The registered name *should* be the last "erlang resource" to
@@ -13159,8 +12809,9 @@ erts_continue_exit_process(Process *p)
* Note! The monitor and link fields will be overwritten
* by erts_ptab_delete_element() below.
*/
- mon = ERTS_P_MONITORS(p);
- lnk = ERTS_P_LINKS(p);
+ links = ERTS_P_LINKS(p);
+ monitors = ERTS_P_MONITORS(p);
+ lt_monitors = ERTS_P_LT_MONITORS(p);
{
/* Do *not* use erts_get_runq_proc() */
@@ -13197,7 +12848,9 @@ erts_continue_exit_process(Process *p)
n = e = a;
ASSERT(a & ERTS_PSFLG_EXITING);
n |= ERTS_PSFLG_FREE;
- n &= ~ERTS_PSFLG_ACTIVE;
+ n &= ~(ERTS_PSFLG_ACTIVE
+ | ERTS_PSFLG_ACTIVE_SYS
+ | ERTS_PSFLG_DIRTY_ACTIVE_SYS);
if ((n & ERTS_PSFLG_IN_RUNQ) && !refc_inced) {
erts_proc_inc_refc(p);
refc_inced = 1;
@@ -13225,40 +12878,70 @@ erts_continue_exit_process(Process *p)
? ERTS_PROC_SET_DIST_ENTRY(p, NULL)
: NULL);
- erts_proc_unlock(p, ERTS_PROC_LOCKS_ALL);
+
+ /*
+ * It might show up signal prio elevation tasks until we
+ * have entered free state. Cleanup such tasks now.
+ */
+ state = erts_atomic32_read_acqb(&p->state);
+ if (!(state & ERTS_PSFLG_SYS_TASKS))
+ erts_proc_unlock(p, ERTS_PROC_LOCKS_ALL);
+ else {
+ erts_proc_unlock(p, ERTS_PROC_LOCKS_ALL_MINOR);
+
+ do {
+ (void) cleanup_sys_tasks(p, state, CONTEXT_REDS);
+ state = erts_atomic32_read_acqb(&p->state);
+ } while (state & ERTS_PSFLG_SYS_TASKS);
+
+ erts_proc_unlock(p, ERTS_PROC_LOCK_MAIN);
+ }
+
+#ifdef DEBUG
+ erts_proc_lock(p, ERTS_PROC_LOCK_STATUS);
+ ASSERT(p->sys_task_qs == NULL);
+ erts_proc_unlock(p, ERTS_PROC_LOCK_STATUS);
+#endif
if (dep) {
erts_do_net_exits(dep, (reason == am_kill) ? am_killed : reason);
erts_deref_dist_entry(dep);
}
- /*
- * Pre-build the EXIT tuple if there are any links.
- */
- if (lnk) {
- DeclareTmpHeap(tmp_heap,4,p);
- Eterm exit_tuple;
- Uint exit_tuple_sz;
- Eterm* hp;
-
- UseTmpHeap(4,p);
- hp = &tmp_heap[0];
+ pectxt.c_p = p;
+ pectxt.reason = reason;
- exit_tuple = TUPLE3(hp, am_EXIT, p->common.id, reason);
+ if (links) {
+ erts_link_tree_foreach_delete(&links,
+ erts_proc_exit_handle_link,
+ (void *) &pectxt);
+ ASSERT(!links);
+ }
- exit_tuple_sz = size_object(exit_tuple);
+ if (monitors) {
+ erts_monitor_tree_foreach_delete(&monitors,
+ erts_proc_exit_handle_monitor,
+ (void *) &pectxt);
+ ASSERT(!monitors);
+ }
- {
- ExitLinkContext context = {p, reason, exit_tuple, exit_tuple_sz};
- erts_sweep_links(lnk, &doit_exit_link, &context);
- }
- UnUseTmpHeap(4,p);
+ if (lt_monitors) {
+ erts_monitor_list_foreach_delete(&lt_monitors,
+ erts_proc_exit_handle_monitor,
+ (void *) &pectxt);
+ ASSERT(!lt_monitors);
}
- {
- ExitMonitorContext context = {reason, p};
- erts_sweep_monitors(mon,&doit_exit_monitor,&context); /* Allocates TmpHeap, but we
- have none here */
+ erts_proc_sig_fetch(p);
+ /*
+ * erts_proc_sig_handle_exit() implements yielding.
+ * However, this function cannot handle it yet... loop
+ * until done...
+ */
+ while (!0) {
+ int reds = CONTEXT_REDS;
+ if (erts_proc_sig_handle_exit(p, &reds))
+ break;
}
erts_proc_lock(p, ERTS_PROC_LOCK_MAIN);
@@ -13297,6 +12980,40 @@ erts_continue_exit_process(Process *p)
BUMP_ALL_REDS(p);
}
+Process *
+erts_try_lock_sig_free_proc(Eterm pid, ErtsProcLocks locks)
+{
+ Process *rp = erts_proc_lookup_raw(pid);
+ erts_aint32_t state;
+
+ if (!rp)
+ return NULL;
+
+ ERTS_LC_ASSERT(!erts_proc_lc_my_proc_locks(rp));
+
+ state = erts_atomic32_read_nob(&rp->state);
+ if (state & ERTS_PSFLG_EXITING)
+ return NULL;
+
+ if (state & (ERTS_PSFLG_SIG_IN_Q|ERTS_PSFLG_SIG_Q))
+ return ERTS_PROC_LOCK_BUSY;
+ if (erts_proc_trylock(rp, locks) == EBUSY)
+ return ERTS_PROC_LOCK_BUSY;
+
+ state = erts_atomic32_read_nob(&rp->state);
+ if (state & ERTS_PSFLG_EXITING) {
+ erts_proc_unlock(rp, locks);
+ return NULL;
+ }
+
+ if (state & (ERTS_PSFLG_SIG_IN_Q|ERTS_PSFLG_SIG_Q)) {
+ erts_proc_unlock(rp, locks);
+ return ERTS_PROC_LOCK_BUSY;
+ }
+
+ return rp;
+}
+
/*
* Stack dump functions follow.
*/
@@ -13398,16 +13115,33 @@ stack_element_dump(fmtfn_t to, void *to_arg, Eterm* sp, int yreg)
return yreg;
}
+static void print_current_process_info(fmtfn_t, void *to_arg, ErtsSchedulerData*);
+
/*
* Print scheduler information
*/
void
-erts_print_scheduler_info(fmtfn_t to, void *to_arg, ErtsSchedulerData *esdp) {
+erts_print_scheduler_info(fmtfn_t to, void *to_arg, ErtsSchedulerData *esdp)
+{
int i;
erts_aint32_t flg;
- Process *p;
- erts_print(to, to_arg, "=scheduler:%u\n", esdp->no);
+ switch (esdp->type) {
+ case ERTS_SCHED_NORMAL:
+ erts_print(to, to_arg, "=scheduler:%u\n", esdp->no);
+ break;
+ case ERTS_SCHED_DIRTY_CPU:
+ erts_print(to, to_arg, "=dirty_cpu_scheduler:%u\n",
+ (esdp->dirty_no + erts_no_schedulers));
+ break;
+ case ERTS_SCHED_DIRTY_IO:
+ erts_print(to, to_arg, "=dirty_io_scheduler:%u\n",
+ (esdp->dirty_no + erts_no_schedulers + erts_no_dirty_cpu_schedulers));
+ break;
+ default:
+ erts_print(to, to_arg, "=unknown_scheduler_type:%u\n", esdp->type);
+ break;
+ }
flg = erts_atomic32_read_dirty(&esdp->ssi->flags);
erts_print(to, to_arg, "Scheduler Sleep Info Flags: ");
@@ -13453,10 +13187,24 @@ erts_print_scheduler_info(fmtfn_t to, void *to_arg, ErtsSchedulerData *esdp) {
}
erts_print(to, to_arg, "\n");
- erts_print(to, to_arg, "Current Port: ");
- if (esdp->current_port)
- erts_print(to, to_arg, "%T", esdp->current_port->common.id);
- erts_print(to, to_arg, "\n");
+ if (esdp->type == ERTS_SCHED_NORMAL) {
+ erts_print(to, to_arg, "Current Port: ");
+ if (esdp->current_port)
+ erts_print(to, to_arg, "%T", esdp->current_port->common.id);
+ erts_print(to, to_arg, "\n");
+
+ erts_print_run_queue_info(to, to_arg, esdp->run_queue);
+ }
+
+ /* This *MUST* to be the last information in scheduler block */
+ print_current_process_info(to, to_arg, esdp);
+}
+
+void erts_print_run_queue_info(fmtfn_t to, void *to_arg,
+ ErtsRunQueue *run_queue)
+{
+ erts_aint32_t flg;
+ int i;
for (i = 0; i < ERTS_NO_PROC_PRIO_LEVELS; i++) {
erts_print(to, to_arg, "Run Queue ");
@@ -13478,12 +13226,12 @@ erts_print_scheduler_info(fmtfn_t to, void *to_arg, ErtsSchedulerData *esdp) {
break;
}
erts_print(to, to_arg, "Length: %d\n",
- erts_atomic32_read_dirty(&esdp->run_queue->procs.prio_info[i].len));
+ erts_atomic32_read_dirty(&run_queue->procs.prio_info[i].len));
}
erts_print(to, to_arg, "Run Queue Port Length: %d\n",
- erts_atomic32_read_dirty(&esdp->run_queue->ports.info.len));
+ erts_atomic32_read_dirty(&run_queue->ports.info.len));
- flg = erts_atomic32_read_dirty(&esdp->run_queue->flags);
+ flg = erts_atomic32_read_dirty(&run_queue->flags);
erts_print(to, to_arg, "Run Queue Flags: ");
for (i = 0; i < ERTS_RUNQ_FLG_MAX && flg; i++) {
erts_aint32_t chk = (1 << i);
@@ -13550,9 +13298,15 @@ erts_print_scheduler_info(fmtfn_t to, void *to_arg, ErtsSchedulerData *esdp) {
}
}
erts_print(to, to_arg, "\n");
+}
+
+
+static void print_current_process_info(fmtfn_t to, void *to_arg,
+ ErtsSchedulerData* esdp)
+{
+ Process *p = esdp->current_process;
+ erts_aint32_t flg;
- /* This *MUST* to be the last information in scheduler block */
- p = esdp->current_process;
erts_print(to, to_arg, "Current Process: ");
if (esdp->current_process && !(ERTS_TRACE_FLAGS(p) & F_SENSITIVE)) {
flg = erts_atomic32_read_dirty(&p->state);
diff --git a/erts/emulator/beam/erl_process.h b/erts/emulator/beam/erl_process.h
index 66d7848f89..0550fb05b5 100644
--- a/erts/emulator/beam/erl_process.h
+++ b/erts/emulator/beam/erl_process.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1996-2017. All Rights Reserved.
+ * Copyright Ericsson AB 1996-2018. 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.
@@ -51,7 +51,7 @@ typedef struct process Process;
#include "erl_process_dict.h"
#include "erl_node_container_utils.h"
#include "erl_node_tables.h"
-#include "erl_monitors.h"
+#include "erl_monitor_link.h"
#include "erl_hl_timer.h"
#include "erl_time.h"
#include "erl_atom_table.h"
@@ -75,8 +75,6 @@ typedef struct process Process;
#include "erl_thr_progress.h"
#undef ERL_THR_PROGRESS_TSD_TYPE_ONLY
-struct ErtsNodesMonitor_;
-
#define ERTS_HAVE_SCHED_UTIL_BALANCING_SUPPORT_OPT 0
#define ERTS_HAVE_SCHED_UTIL_BALANCING_SUPPORT 0
@@ -105,13 +103,13 @@ struct saved_calls {
};
extern Export exp_send, exp_receive, exp_timeout;
-extern int erts_sched_compact_load;
-extern int erts_sched_balance_util;
-extern Uint erts_no_schedulers;
-extern Uint erts_no_total_schedulers;
-extern Uint erts_no_dirty_cpu_schedulers;
-extern Uint erts_no_dirty_io_schedulers;
-extern Uint erts_no_run_queues;
+extern int ERTS_WRITE_UNLIKELY(erts_sched_compact_load);
+extern int ERTS_WRITE_UNLIKELY(erts_sched_balance_util);
+extern Uint ERTS_WRITE_UNLIKELY(erts_no_schedulers);
+extern Uint ERTS_WRITE_UNLIKELY(erts_no_total_schedulers);
+extern Uint ERTS_WRITE_UNLIKELY(erts_no_dirty_cpu_schedulers);
+extern Uint ERTS_WRITE_UNLIKELY(erts_no_dirty_io_schedulers);
+extern Uint ERTS_WRITE_UNLIKELY(erts_no_run_queues);
extern int erts_sched_thread_suggested_stack_size;
extern int erts_dcpu_sched_thread_suggested_stack_size;
extern int erts_dio_sched_thread_suggested_stack_size;
@@ -244,6 +242,9 @@ extern int erts_dio_sched_thread_suggested_stack_size;
(erts_aint32_t) (MSK), \
(erts_aint32_t) (FLGS)))
+#define ERTS_RUNQ_POINTER_MASK (~((erts_aint_t) 3))
+#define ERTS_RUNQ_BOUND_FLAG ((erts_aint_t) 1)
+
typedef enum {
ERTS_SCHDLR_SSPND_DONE_MSCHED_BLOCKED,
ERTS_SCHDLR_SSPND_DONE_NMSCHED_BLOCKED,
@@ -308,7 +309,6 @@ typedef enum {
ERTS_SSI_AUX_WORK_ASYNC_READY_CLEAN_IX,
ERTS_SSI_AUX_WORK_MISC_THR_PRGR_IX,
ERTS_SSI_AUX_WORK_MISC_IX,
- ERTS_SSI_AUX_WORK_PENDING_EXITERS_IX,
ERTS_SSI_AUX_WORK_SET_TMO_IX,
ERTS_SSI_AUX_WORK_MSEG_CACHE_CHECK_IX,
ERTS_SSI_AUX_WORK_YIELD_IX,
@@ -342,8 +342,6 @@ typedef enum {
(((erts_aint32_t) 1) << ERTS_SSI_AUX_WORK_MISC_THR_PRGR_IX)
#define ERTS_SSI_AUX_WORK_MISC \
(((erts_aint32_t) 1) << ERTS_SSI_AUX_WORK_MISC_IX)
-#define ERTS_SSI_AUX_WORK_PENDING_EXITERS \
- (((erts_aint32_t) 1) << ERTS_SSI_AUX_WORK_PENDING_EXITERS_IX)
#define ERTS_SSI_AUX_WORK_SET_TMO \
(((erts_aint32_t) 1) << ERTS_SSI_AUX_WORK_SET_TMO_IX)
#define ERTS_SSI_AUX_WORK_MSEG_CACHE_CHECK \
@@ -488,7 +486,6 @@ struct ErtsRunQueue_ {
int wakeup_other_reds;
struct {
- ErtsProcList *pending_exiters;
Uint context_switches;
Uint reductions;
@@ -522,7 +519,7 @@ typedef union {
char align[ERTS_ALC_CACHE_LINE_ALIGN_SIZE(sizeof(ErtsRunQueue))];
} ErtsAlignedRunQueue;
-extern ErtsAlignedRunQueue *erts_aligned_run_queues;
+extern ErtsAlignedRunQueue * ERTS_WRITE_UNLIKELY(erts_aligned_run_queues);
#define ERTS_PROC_REDUCTIONS_EXECUTED(SD, RQ, PRIO, REDS, AREDS)\
do { \
@@ -675,9 +672,9 @@ typedef union {
char align[ERTS_ALC_CACHE_LINE_ALIGN_SIZE(sizeof(ErtsSchedulerData))];
} ErtsAlignedSchedulerData;
-extern ErtsAlignedSchedulerData *erts_aligned_scheduler_data;
-extern ErtsAlignedSchedulerData *erts_aligned_dirty_cpu_scheduler_data;
-extern ErtsAlignedSchedulerData *erts_aligned_dirty_io_scheduler_data;
+extern ErtsAlignedSchedulerData * ERTS_WRITE_UNLIKELY(erts_aligned_scheduler_data);
+extern ErtsAlignedSchedulerData * ERTS_WRITE_UNLIKELY(erts_aligned_dirty_cpu_scheduler_data);
+extern ErtsAlignedSchedulerData * ERTS_WRITE_UNLIKELY(erts_aligned_dirty_io_scheduler_data);
#if defined(ERTS_ENABLE_LOCK_CHECK)
@@ -983,14 +980,10 @@ struct process {
Process *next; /* Pointer to next process in run queue */
- struct ErtsNodesMonitor_ *nodes_monitors;
-
- ErtsSuspendMonitor *suspend_monitors; /* Processes suspended by
- this process via
- erlang:suspend_process/1 */
-
- ErlMessageQueue msg; /* Message queue */
+ ErtsMonitor *suspend_monitors; /* Processes suspended by this process via
+ erlang:suspend_process/1 */
+ ErtsSignalPrivQueues sig_qs; /* Signal queues */
ErtsBifTimers *bif_timers; /* Bif timers aiming at this process */
ProcDict *dictionary; /* Process dictionary, may be NULL */
@@ -1019,7 +1012,6 @@ struct process {
* Information mainly for post-mortem use (erl crash dump).
*/
Eterm parent; /* Pid of process that created this process. */
- erts_approx_time_t approx_started; /* Time when started. */
Uint32 static_flags; /* Flags that do *not* change */
@@ -1050,9 +1042,8 @@ struct process {
erts_atomic32_t state; /* Process state flags (see ERTS_PSFLG_*) */
erts_atomic32_t dirty_state; /* Process dirty state flags (see ERTS_PDSFLG_*) */
- ErlMessageInQueue msg_inq;
+ ErtsSignalInQueue sig_inq;
ErlTraceMessageQueue *trace_msg_q;
- ErtsPendExit pending_exit;
erts_proc_lock_t lock;
ErtsSchedulerData *scheduler_data;
Eterm suspendee;
@@ -1086,6 +1077,7 @@ struct process {
#endif
};
+extern Eterm erts_init_process_id; /* pid of init process */
extern const Process erts_invalid_process;
#ifdef CHECK_FOR_HOLES
@@ -1162,20 +1154,20 @@ void erts_check_for_holes(Process* p);
#define ERTS_PSFLG_IN_PRQ_LOW ERTS_PSFLG_BIT(3)
#define ERTS_PSFLG_FREE ERTS_PSFLG_BIT(4)
#define ERTS_PSFLG_EXITING ERTS_PSFLG_BIT(5)
-#define ERTS_PSFLG_PENDING_EXIT ERTS_PSFLG_BIT(6)
+#define ERTS_PSFLG_UNUSED ERTS_PSFLG_BIT(6)
#define ERTS_PSFLG_ACTIVE ERTS_PSFLG_BIT(7)
#define ERTS_PSFLG_IN_RUNQ ERTS_PSFLG_BIT(8)
#define ERTS_PSFLG_RUNNING ERTS_PSFLG_BIT(9)
#define ERTS_PSFLG_SUSPENDED ERTS_PSFLG_BIT(10)
#define ERTS_PSFLG_GC ERTS_PSFLG_BIT(11)
-#define ERTS_PSFLG_BOUND ERTS_PSFLG_BIT(12)
-#define ERTS_PSFLG_TRAP_EXIT ERTS_PSFLG_BIT(13)
+#define ERTS_PSFLG_SYS_TASKS ERTS_PSFLG_BIT(12)
+#define ERTS_PSFLG_SIG_IN_Q ERTS_PSFLG_BIT(13)
#define ERTS_PSFLG_ACTIVE_SYS ERTS_PSFLG_BIT(14)
#define ERTS_PSFLG_RUNNING_SYS ERTS_PSFLG_BIT(15)
#define ERTS_PSFLG_PROXY ERTS_PSFLG_BIT(16)
#define ERTS_PSFLG_DELAYED_SYS ERTS_PSFLG_BIT(17)
#define ERTS_PSFLG_OFF_HEAP_MSGQ ERTS_PSFLG_BIT(18)
-#define ERTS_PSFLG_ON_HEAP_MSGQ ERTS_PSFLG_BIT(19)
+#define ERTS_PSFLG_SIG_Q ERTS_PSFLG_BIT(19)
#define ERTS_PSFLG_DIRTY_CPU_PROC ERTS_PSFLG_BIT(20)
#define ERTS_PSFLG_DIRTY_IO_PROC ERTS_PSFLG_BIT(21)
#define ERTS_PSFLG_DIRTY_ACTIVE_SYS ERTS_PSFLG_BIT(22)
@@ -1194,7 +1186,6 @@ void erts_check_for_holes(Process* p);
| ERTS_PSFLG_IN_PRQ_LOW)
#define ERTS_PSFLGS_VOLATILE_HEAP (ERTS_PSFLG_EXITING \
- | ERTS_PSFLG_PENDING_EXIT \
| ERTS_PSFLG_DIRTY_RUNNING \
| ERTS_PSFLG_DIRTY_RUNNING_SYS)
@@ -1270,7 +1261,7 @@ void erts_check_for_holes(Process* p);
#define SPO_OFF_HEAP_MSGQ 16
#define SPO_ON_HEAP_MSGQ 32
-extern int erts_default_spo_flags;
+extern int ERTS_WRITE_UNLIKELY(erts_default_spo_flags);
/*
* The following struct contains options for a process to be spawned.
@@ -1326,10 +1317,10 @@ extern erts_rwmtx_t erts_cpu_bind_rwmtx;
** erts_system_monitor must be != NIL, to allow testing on just
** the erts_system_monitor_* variables.
*/
-extern Eterm erts_system_monitor;
-extern Uint erts_system_monitor_long_gc;
-extern Uint erts_system_monitor_long_schedule;
-extern Uint erts_system_monitor_large_heap;
+extern Eterm ERTS_WRITE_UNLIKELY(erts_system_monitor);
+extern Uint ERTS_WRITE_UNLIKELY(erts_system_monitor_long_gc);
+extern Uint ERTS_WRITE_UNLIKELY(erts_system_monitor_long_schedule);
+extern Uint ERTS_WRITE_UNLIKELY(erts_system_monitor_large_heap);
struct erts_system_monitor_flags_t {
unsigned int busy_port : 1;
unsigned int busy_dist_port : 1;
@@ -1377,6 +1368,9 @@ extern int erts_system_profile_ts_type;
#define F_DIRTY_MAJOR_GC (1 << 23) /* Dirty major GC scheduled */
#define F_DIRTY_MINOR_GC (1 << 24) /* Dirty minor GC scheduled */
#define F_HIBERNATED (1 << 25) /* Hibernated */
+#define F_LOCAL_SIGS_ONLY (1 << 26)
+#define F_TRAP_EXIT (1 << 27) /* Trapping exit */
+#define F_DEFERRED_SAVED_LAST (1 << 28)
/*
* F_DISABLE_GC and F_DELAY_GC are similar. Both will prevent
@@ -1442,7 +1436,7 @@ extern int erts_system_profile_ts_type;
| F_TRACE_ARITY_ONLY | F_TRACE_RETURN_TO \
| F_TRACE_SILENT | F_TRACE_SCHED_PROCS | F_TRACE_PORTS \
| F_TRACE_SCHED_PORTS | F_TRACE_SCHED_NO \
- | F_TRACE_SCHED_EXIT)
+ | F_TRACE_SCHED_EXIT )
#define ERTS_TRACEE_MODIFIER_FLAGS \
@@ -1455,6 +1449,14 @@ extern int erts_system_profile_ts_type;
#define SEQ_TRACE_FLAG(N) (1 << (ERTS_TRACE_TS_TYPE_BITS + (N)))
+#define ERTS_SIG_ENABLE_TRACE_FLAGS \
+ ( F_TRACE_RECEIVE | F_TRACE_PROCS)
+
+/*
+ * F_TRACE_RECEIVE is always enabled/disable via signaling.
+ * F_TRACE_PROCS enable/disable F_TRACE_PROCS_SIG via signaling.
+ */
+
/* Sequential trace flags */
/* SEQ_TRACE_TIMESTAMP_MASK is a bit-field */
@@ -1481,10 +1483,6 @@ extern int erts_system_profile_ts_type;
#define DT_UTAG_FLAGS(P) ((P)->dt_utag_flags)
#endif
-/* Option flags to erts_send_exit_signal() */
-#define ERTS_XSIG_FLG_IGN_KILL (((Uint32) 1) << 0)
-#define ERTS_XSIG_FLG_NO_IGN_NORMAL (((Uint32) 1) << 1)
-
#define CANCEL_TIMER(P) \
do { \
if ((P)->flags & (F_INSLPQUEUE|F_TIMO)) { \
@@ -1546,6 +1544,7 @@ Uint64 erts_step_proc_interval(void);
ErtsProcList *erts_proclist_create(Process *);
ErtsProcList *erts_proclist_copy(ErtsProcList *);
void erts_proclist_destroy(ErtsProcList *);
+void erts_proclist_dump(fmtfn_t to, void *to_arg, ErtsProcList*);
ERTS_GLB_INLINE int erts_proclist_same(ErtsProcList *, Process *);
ERTS_GLB_INLINE void erts_proclist_store_first(ErtsProcList **, ErtsProcList *);
@@ -1730,6 +1729,7 @@ struct db_fixation;
void erts_schedule_ets_free_fixation(Eterm pid, struct db_fixation*);
void erts_schedule_flush_trace_messages(Process *proc, int force_on_proc);
int erts_flush_trace_messages(Process *c_p, ErtsProcLocks locks);
+int erts_sig_prio(Eterm pid, int prio);
#if defined(ERTS_ENABLE_LOCK_CHECK)
int erts_dbg_check_halloc_lock(Process *p);
@@ -1778,8 +1778,10 @@ ErtsRunQueue *erts_schedid2runq(Uint);
Process *erts_schedule(ErtsSchedulerData *, Process*, int);
void erts_schedule_misc_op(void (*)(void *), void *);
Eterm erl_create_process(Process*, Eterm, Eterm, Eterm, ErlSpawnOpts*);
+void erts_set_self_exiting(Process *, Eterm);
void erts_do_exit_process(Process*, Eterm);
void erts_continue_exit_process(Process *);
+void erts_proc_exit_link(Process *, ErtsLink *, Uint16, Eterm, Eterm);
/* Begin System profile */
Uint erts_runnable_process_count(void);
/* End System profile */
@@ -1792,10 +1794,18 @@ void erts_stack_dump(fmtfn_t to, void *to_arg, Process *);
void erts_limited_stack_trace(fmtfn_t to, void *to_arg, Process *);
void erts_program_counter_info(fmtfn_t to, void *to_arg, Process *);
void erts_print_scheduler_info(fmtfn_t to, void *to_arg, ErtsSchedulerData *esdp);
+void erts_print_run_queue_info(fmtfn_t, void *to_arg, ErtsRunQueue*);
void erts_dump_extended_process_state(fmtfn_t to, void *to_arg, erts_aint32_t psflg);
void erts_dump_process_state(fmtfn_t to, void *to_arg, erts_aint32_t psflg);
-Eterm erts_get_process_priority(Process *p);
+typedef struct {
+ Process *c_p;
+ Eterm reason;
+} ErtsProcExitContext;
+void erts_proc_exit_handle_monitor(ErtsMonitor *mon, void *vctxt);
+void erts_proc_exit_handle_link(ErtsLink *lnk, void *vctxt);
+
+Eterm erts_get_process_priority(erts_aint32_t state);
Eterm erts_set_process_priority(Process *p, Eterm prio);
Uint erts_get_total_context_switches(void);
@@ -1813,18 +1823,6 @@ void erts_suspend(Process*, ErtsProcLocks, Port*);
void erts_resume(Process*, ErtsProcLocks);
int erts_resume_processes(ErtsProcList *);
-int erts_send_exit_signal(Process *,
- Eterm,
- Process *,
- ErtsProcLocks *,
- Eterm,
- Eterm,
- Process *,
- Uint32);
-void erts_handle_pending_exit(Process *, ErtsProcLocks);
-#define ERTS_PROC_PENDING_EXIT(P) \
- (ERTS_PSFLG_PENDING_EXIT & erts_atomic32_read_acqb(&(P)->state))
-
void erts_deep_process_dump(fmtfn_t, void *);
Eterm erts_get_reader_groups_map(Process *c_p);
@@ -1856,6 +1854,8 @@ do { \
ErtsSchedulerData *erts_get_scheduler_data(void);
void erts_schedule_process(Process *, erts_aint32_t, ErtsProcLocks);
+erts_aint32_t erts_proc_sys_schedule(Process *p, erts_aint32_t state,
+ erts_aint32_t enable_flag);
ERTS_GLB_INLINE void erts_proc_notify_new_message(Process *p, ErtsProcLocks locks);
ERTS_GLB_INLINE void erts_schedule_dirty_sys_execution(Process *c_p);
@@ -1887,8 +1887,7 @@ erts_schedule_dirty_sys_execution(Process *c_p)
/* Don't set dirty-active-sys if we are about to exit... */
while (!(a & (ERTS_PSFLG_DIRTY_ACTIVE_SYS
- | ERTS_PSFLG_EXITING
- | ERTS_PSFLG_PENDING_EXIT))) {
+ | ERTS_PSFLG_EXITING))) {
e = a;
n = a | ERTS_PSFLG_DIRTY_ACTIVE_SYS;
a = erts_atomic32_cmpxchg_mb(&c_p->state, n, e);
@@ -2167,7 +2166,12 @@ ERTS_GLB_INLINE int erts_is_scheduler_bound(ErtsSchedulerData *esdp);
ERTS_GLB_INLINE Process *erts_get_current_process(void);
ERTS_GLB_INLINE Eterm erts_get_current_pid(void);
ERTS_GLB_INLINE Uint erts_get_scheduler_id(void);
-ERTS_GLB_INLINE ErtsRunQueue *erts_get_runq_proc(Process *p);
+ERTS_GLB_INLINE void erts_init_runq_proc(Process *p, ErtsRunQueue *rq, int bnd);
+ERTS_GLB_INLINE ErtsRunQueue *erts_set_runq_proc(Process *p, ErtsRunQueue *rq, int *boundp);
+ERTS_GLB_INLINE int erts_try_change_runq_proc(Process *p, ErtsRunQueue *rq);
+ERTS_GLB_INLINE ErtsRunQueue *erts_bind_runq_proc(Process *p, int bind);
+ERTS_GLB_INLINE int erts_proc_runq_is_bound(Process *p);
+ERTS_GLB_INLINE ErtsRunQueue *erts_get_runq_proc(Process *p, int *boundp);
ERTS_GLB_INLINE ErtsRunQueue *erts_get_runq_current(ErtsSchedulerData *esdp);
ERTS_GLB_INLINE void erts_runq_lock(ErtsRunQueue *rq);
ERTS_GLB_INLINE int erts_runq_trylock(ErtsRunQueue *rq);
@@ -2247,11 +2251,146 @@ Uint erts_get_scheduler_id(void)
return esdp ? esdp->no : (Uint) 0;
}
+/**
+ * Init run-queue of process.
+ *
+ * @param p[in,out] Process
+ * @param rq[in] Run-queue that process will be assigned to
+ * @param bnd[in,out] If non-zero binds process to run-queue.
+ */
+
+ERTS_GLB_INLINE void
+erts_init_runq_proc(Process *p, ErtsRunQueue *rq, int bnd)
+{
+ erts_aint_t rqint = (erts_aint_t) rq;
+ if (bnd)
+ rqint |= ERTS_RUNQ_BOUND_FLAG;
+ erts_atomic_init_nob(&p->run_queue, rqint);
+}
+
+/**
+ * Forcibly set run-queue of process.
+ *
+ * @param p[in,out] Process
+ * @param rq[in] Run-queue that process will be assigned to
+ * @param bndp[in,out] Pointer to integer. On input non-zero
+ * value causes the process to be bound to
+ * the run-queue. On output, indicating
+ * wether process previously was bound or
+ * not.
+ * @return Previous run-queue.
+ */
+
+ERTS_GLB_INLINE ErtsRunQueue *
+erts_set_runq_proc(Process *p, ErtsRunQueue *rq, int *bndp)
+{
+ erts_aint_t rqint = (erts_aint_t) rq;
+ ASSERT(bndp);
+ ASSERT(rq);
+ if (*bndp)
+ rqint |= ERTS_RUNQ_BOUND_FLAG;
+ rqint = erts_atomic_xchg_nob(&p->run_queue, rqint);
+ *bndp = (int) (rqint & ERTS_RUNQ_BOUND_FLAG);
+ return (ErtsRunQueue *) (rqint & ERTS_RUNQ_POINTER_MASK);
+}
+
+/**
+ * Try to change run-queue assignment of a process.
+ *
+ * @param p[in,out] Process
+ * @param rq[int] Run-queue that process will be assigned to
+ * @return Non-zero if the run-queue assignment was
+ * successfully changed.
+ */
+
+ERTS_GLB_INLINE int
+erts_try_change_runq_proc(Process *p, ErtsRunQueue *rq)
+{
+ erts_aint_t old_rqint, new_rqint;
+
+ ASSERT(rq);
+
+ new_rqint = (erts_aint_t) rq;
+ old_rqint = (erts_aint_t) erts_atomic_read_nob(&p->run_queue);
+ while (1) {
+ erts_aint_t act_rqint;
+
+ if (old_rqint & ERTS_RUNQ_BOUND_FLAG)
+ return 0;
+
+ act_rqint = erts_atomic_cmpxchg_nob(&p->run_queue,
+ new_rqint,
+ old_rqint);
+ if (act_rqint == old_rqint)
+ return !0;
+ }
+}
+
+/**
+ *
+ * Bind or unbind process to/from currently used run-queue.
+ *
+ * @param p Process
+ * @param bind Bind if non-zero; otherwise unbind
+ * @return Pointer to previously bound run-queue,
+ * or NULL if previously unbound
+ */
+
+ERTS_GLB_INLINE ErtsRunQueue *
+erts_bind_runq_proc(Process *p, int bind)
+{
+ erts_aint_t rqint;
+ if (bind)
+ rqint = erts_atomic_read_bor_nob(&p->run_queue,
+ ERTS_RUNQ_BOUND_FLAG);
+ else
+ rqint = erts_atomic_read_band_nob(&p->run_queue,
+ ~ERTS_RUNQ_BOUND_FLAG);
+ if (rqint & ERTS_RUNQ_BOUND_FLAG)
+ return (ErtsRunQueue *) (rqint & ERTS_RUNQ_POINTER_MASK);
+ else
+ return NULL;
+}
+
+/**
+ * Determine wether a process is bound to a run-queue or not.
+ *
+ * @return Returns a non-zero value if bound,
+ * and zero of not bound.
+ */
+
+ERTS_GLB_INLINE int
+erts_proc_runq_is_bound(Process *p)
+{
+ erts_aint_t rqint = erts_atomic_read_nob(&p->run_queue);
+ return (int) (rqint & ERTS_RUNQ_BOUND_FLAG);
+}
+
+/**
+ * Set run-queue of process.
+ *
+ * @param p[in,out] Process
+ * @param bndp[out] Pointer to integer. If non-NULL pointer,
+ * the integer will be set to a non-zero
+ * value if the process is bound to the
+ * run-queue.
+ * @return Pointer to the normal run-queue that
+ * the process currently is assigend to.
+ * A process is always assigned to a
+ * normal run-queue.
+ */
+
ERTS_GLB_INLINE ErtsRunQueue *
-erts_get_runq_proc(Process *p)
+erts_get_runq_proc(Process *p, int *bndp)
{
- ASSERT(ERTS_AINT_NULL != erts_atomic_read_nob(&p->run_queue));
- return (ErtsRunQueue *) erts_atomic_read_nob(&p->run_queue);
+ erts_aint_t rqint = erts_atomic_read_nob(&p->run_queue);
+ ErtsRunQueue *rq;
+ if (bndp)
+ *bndp = (int) (rqint & ERTS_RUNQ_BOUND_FLAG);
+ rqint &= ERTS_RUNQ_POINTER_MASK;
+ rq = (ErtsRunQueue *) rqint;
+ ASSERT(rq);
+ return rq;
}
ERTS_GLB_INLINE ErtsRunQueue *
@@ -2435,6 +2574,8 @@ ERTS_TIME2REDS_IMPL__(ErtsMonotonicTime start, ErtsMonotonicTime end)
}
#endif
+Process *erts_try_lock_sig_free_proc(Eterm pid,
+ ErtsProcLocks locks);
Process *erts_pid2proc_not_running(Process *,
ErtsProcLocks,
diff --git a/erts/emulator/beam/erl_process_dict.c b/erts/emulator/beam/erl_process_dict.c
index 3c80f0e0f6..aee88841ae 100644
--- a/erts/emulator/beam/erl_process_dict.c
+++ b/erts/emulator/beam/erl_process_dict.c
@@ -79,6 +79,8 @@
/* Array access macro */
#define ARRAY_GET(PDict, Index) (ASSERT((Index) < (PDict)->arraySize), \
(PDict)->data[Index])
+#define ARRAY_GET_PTR(PDict, Index) (ASSERT((Index) < (PDict)->arraySize), \
+ &(PDict)->data[Index])
#define ARRAY_PUT(PDict, Index, Val) (ASSERT((Index) < (PDict)->arraySize), \
(PDict)->data[Index] = (Val))
@@ -92,7 +94,7 @@ static void pd_hash_erase_all(Process *p);
static Eterm pd_hash_get_with_hval(Process *p, Eterm bucket, Eterm id);
static Eterm pd_hash_get_keys(Process *p, Eterm value);
static Eterm pd_hash_get_all_keys(Process *p, ProcDict *pd);
-static Eterm pd_hash_get_all(Process *p, ProcDict *pd);
+static Eterm pd_hash_get_all(Process *p, ProcDict *pd, int keep_dict);
static Eterm pd_hash_put(Process *p, Eterm id, Eterm value);
static void shrink(Process *p, Eterm* ret);
@@ -281,7 +283,7 @@ BIF_RETTYPE get_0(BIF_ALIST_0)
{
Eterm ret;
PD_CHECK(BIF_P->dictionary);
- ret = pd_hash_get_all(BIF_P, BIF_P->dictionary);
+ ret = pd_hash_get_all(BIF_P, BIF_P->dictionary, 1);
PD_CHECK(BIF_P->dictionary);
BIF_RET(ret);
}
@@ -329,7 +331,7 @@ BIF_RETTYPE erase_0(BIF_ALIST_0)
{
Eterm ret;
PD_CHECK(BIF_P->dictionary);
- ret = pd_hash_get_all(BIF_P, BIF_P->dictionary);
+ ret = pd_hash_get_all(BIF_P, BIF_P->dictionary, 0);
pd_hash_erase_all(BIF_P);
PD_CHECK(BIF_P->dictionary);
BIF_RET(ret);
@@ -541,29 +543,46 @@ static Eterm pd_hash_get_keys(Process *p, Eterm value)
static Eterm
-pd_hash_get_all(Process *p, ProcDict *pd)
+pd_hash_get_all(Process *p, ProcDict *pd, int keep_dict)
{
Eterm* hp;
+ Eterm* tp;
Eterm res = NIL;
Eterm tmp, tmp2;
unsigned int i;
unsigned int num;
+ Uint need;
if (pd == NULL) {
return res;
}
num = HASH_RANGE(pd);
- hp = HAlloc(p, pd->numElements * 2);
-
+
+ /*
+ * If this is not erase/0, then must copy all key-value tuples
+ * as they may be mutated by put/2.
+ */
+ need = pd->numElements * (keep_dict ? 2+3 : 2);
+ hp = HAlloc(p, need);
+
for (i = 0; i < num; ++i) {
tmp = ARRAY_GET(pd, i);
if (is_boxed(tmp)) {
- ASSERT(is_tuple(tmp));
+ if (keep_dict) {
+ tp = tuple_val(tmp);
+ tmp = TUPLE2(hp, tp[1], tp[2]);
+ hp += 3;
+ }
res = CONS(hp, tmp, res);
hp += 2;
} else if (is_list(tmp)) {
while (tmp != NIL) {
tmp2 = TCAR(tmp);
+ if (keep_dict) {
+ tp = tuple_val(tmp2);
+ tmp2 = TUPLE2(hp, tp[1], tp[2]);
+ hp += 3;
+ }
res = CONS(hp, tmp2, res);
hp += 2;
tmp = TCDR(tmp);
@@ -577,11 +596,14 @@ static Eterm pd_hash_put(Process *p, Eterm id, Eterm value)
{
unsigned int hval;
Eterm *hp;
+ Eterm *tp;
+ Eterm *bucket;
Eterm tpl;
Eterm old;
+ Eterm old_val = am_undefined;
Eterm tmp;
int needed;
- int i = 0;
+ int new_key = 1;
#ifdef DEBUG
Eterm *hp_limit;
#endif
@@ -595,7 +617,8 @@ static Eterm pd_hash_put(Process *p, Eterm id, Eterm value)
p->dictionary->numElements = 0;
}
hval = pd_hash_value(p->dictionary, id);
- old = ARRAY_GET(p->dictionary, hval);
+ bucket = ARRAY_GET_PTR(p->dictionary, hval);
+ old = *bucket;
/*
* Calculate the number of heap words needed and garbage
@@ -603,32 +626,49 @@ static Eterm pd_hash_put(Process *p, Eterm id, Eterm value)
*/
needed = 3; /* {Key,Value} tuple */
if (is_boxed(old)) {
- /*
- * We don't want to compare keys twice, so we'll always
- * reserve the space for two CONS cells.
- */
- needed += 2+2;
+ ASSERT(is_tuple(old));
+ tp = tuple_val(old);
+ if (EQ(tp[1], id)) {
+ old_val = tp[2];
+ if (is_immed(value)) {
+ tp[2] = value; /* DESTRUCTIVE HEAP ASSIGNMENT */
+ return old_val;
+ }
+ new_key = 0;
+ }
+ else {
+ needed += 2+2;
+ }
} else if (is_list(old)) {
- i = 0;
- for (tmp = old; tmp != NIL && !EQ(tuple_val(TCAR(tmp))[1], id); tmp = TCDR(tmp)) {
- ++i;
- }
- if (is_nil(tmp)) {
- i = -1;
- needed += 2;
- } else {
- needed += 2*(i+1);
+ Eterm* prev_cdr = bucket;
+
+ needed += 2;
+ for (tmp = old; tmp != NIL; prev_cdr = &TCDR(tmp), tmp = *prev_cdr) {
+ tp = tuple_val(TCAR(tmp));
+ if (EQ(tp[1], id)) {
+ old_val = tp[2];
+ if (is_immed(value)) {
+ tp[2] = value; /* DESTRUCTIVE HEAP ASSIGNMENT */
+ return old_val;
+ }
+ new_key = 0;
+ /* Unlink old {Key,Value} from list */
+ *prev_cdr = TCDR(tmp); /* maybe DESTRUCTIVE HEAP ASSIGNMENT */
+ break;
+ }
}
}
if (HeapWordsLeft(p) < needed) {
Eterm root[3];
root[0] = id;
root[1] = value;
- root[2] = old;
+ root[2] = old_val;
erts_garbage_collect(p, needed, root, 3);
id = root[0];
value = root[1];
- old = root[2];
+ old_val = root[2];
+ ASSERT(bucket == ARRAY_GET_PTR(p->dictionary, hval));
+ old = *bucket;
}
#ifdef DEBUG
hp_limit = p->htop + needed;
@@ -644,66 +684,29 @@ static Eterm pd_hash_put(Process *p, Eterm id, Eterm value)
* Update the dictionary.
*/
if (is_nil(old)) {
- ARRAY_PUT(p->dictionary, hval, tpl);
- ++(p->dictionary->numElements);
+ *bucket = tpl;
} else if (is_boxed(old)) {
ASSERT(is_tuple(old));
- if (EQ(tuple_val(old)[1],id)) {
- ARRAY_PUT(p->dictionary, hval, tpl);
- return tuple_val(old)[2];
+ if (!new_key) {
+ ASSERT(EQ(tuple_val(old)[1],id));
+ *bucket = tpl;
+ return old_val;
} else {
hp = HeapOnlyAlloc(p, 4);
tmp = CONS(hp, old, NIL);
hp += 2;
- ++(p->dictionary->numElements);
- ARRAY_PUT(p->dictionary, hval, CONS(hp, tpl, tmp));
+ *bucket = CONS(hp, tpl, tmp);
hp += 2;
ASSERT(hp <= hp_limit);
}
} else if (is_list(old)) {
- if (i == -1) {
- /*
- * New key. Simply prepend the tuple to the beginning of the list.
- */
- hp = HeapOnlyAlloc(p, 2);
- ARRAY_PUT(p->dictionary, hval, CONS(hp, tpl, old));
- hp += 2;
- ASSERT(hp <= hp_limit);
- ++(p->dictionary->numElements);
- } else {
- /*
- * i = Number of CDRs to skip to reach the changed element in the list.
- *
- * Replace old value in list. To avoid pointers from the old generation
- * to the new, we must rebuild the list from the beginning up to and
- * including the changed element.
- */
- Eterm nlist;
- int j;
-
- hp = HeapOnlyAlloc(p, (i+1)*2);
-
- /* Find the list element to change. */
- for (j = 0, nlist = old; j < i; j++, nlist = TCDR(nlist)) {
- ;
- }
- ASSERT(EQ(tuple_val(TCAR(nlist))[1], id));
- nlist = TCDR(nlist); /* Unchanged part of list. */
-
- /* Rebuild list before the updated element. */
- for (tmp = old; i-- > 0; tmp = TCDR(tmp)) {
- nlist = CONS(hp, TCAR(tmp), nlist);
- hp += 2;
- }
- ASSERT(EQ(tuple_val(TCAR(tmp))[1], id));
-
- /* Put the updated element first in the new list. */
- nlist = CONS(hp, tpl, nlist);
- hp += 2;
- ASSERT(hp <= hp_limit);
- ARRAY_PUT(p->dictionary, hval, nlist);
- return tuple_val(TCAR(tmp))[2];
- }
+ /*
+ * Simply prepend the tuple to the beginning of the list.
+ */
+ hp = HeapOnlyAlloc(p, 2);
+ *bucket = CONS(hp, tpl, *bucket);
+ hp += 2;
+ ASSERT(hp <= hp_limit);
} else {
#ifdef DEBUG
erts_fprintf(stderr,
@@ -714,10 +717,13 @@ static Eterm pd_hash_put(Process *p, Eterm id, Eterm value)
erts_exit(ERTS_ERROR_EXIT, "Damaged process dictionary found during put/2.");
}
+
+ p->dictionary->numElements += new_key;
+
if (HASH_RANGE(p->dictionary) <= p->dictionary->numElements) {
grow(p);
}
- return am_undefined;
+ return old_val;
}
/*
diff --git a/erts/emulator/beam/erl_process_dump.c b/erts/emulator/beam/erl_process_dump.c
index f562fc961b..00659f9f49 100644
--- a/erts/emulator/beam/erl_process_dump.c
+++ b/erts/emulator/beam/erl_process_dump.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2003-2017. All Rights Reserved.
+ * Copyright Ericsson AB 2003-2018. 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.
@@ -35,6 +35,7 @@
#include "erl_map.h"
#define ERTS_WANT_EXTERNAL_TAGS
#include "external.h"
+#include "erl_proc_sig_queue.h"
#define PTR_FMT "%bpX"
#define ETERM_FMT "%beX"
@@ -78,8 +79,17 @@ erts_deep_process_dump(fmtfn_t to, void *to_arg)
Process *p = erts_pix2proc(i);
if (p && p->i != ENULL) {
erts_aint32_t state = erts_atomic32_read_acqb(&p->state);
- if (!(state & (ERTS_PSFLG_EXITING|ERTS_PSFLG_GC)))
- dump_process_info(to, to_arg, p);
+ if (state & ERTS_PSFLG_EXITING)
+ continue;
+ if (state & ERTS_PSFLG_GC) {
+ ErtsSchedulerData *sdp = erts_get_scheduler_data();
+ if (!sdp || p != sdp->current_process)
+ continue;
+
+ /* We want to dump the garbing process that caused the dump */
+ }
+
+ dump_process_info(to, to_arg, p);
}
}
@@ -87,17 +97,35 @@ erts_deep_process_dump(fmtfn_t to, void *to_arg)
dump_binaries(to, to_arg, all_binaries);
}
+static void
+monitor_size(ErtsMonitor *mon, void *vsize)
+{
+ *((Uint *) vsize) += erts_monitor_size(mon);
+}
+
+static void
+link_size(ErtsMonitor *lnk, void *vsize)
+{
+ *((Uint *) vsize) += erts_link_size(lnk);
+}
+
Uint erts_process_memory(Process *p, int incl_msg_inq) {
- ErtsMessage *mp;
Uint size = 0;
struct saved_calls *scb;
size += sizeof(Process);
- if (incl_msg_inq)
- ERTS_MSGQ_MV_INQ2PRIVQ(p);
+ if (incl_msg_inq) {
+ erts_proc_lock(p, ERTS_PROC_LOCK_MSGQ);
+ erts_proc_sig_fetch(p);
+ erts_proc_unlock(p, ERTS_PROC_LOCK_MSGQ);
+ }
- erts_doforall_links(ERTS_P_LINKS(p), &erts_one_link_size, &size);
- erts_doforall_monitors(ERTS_P_MONITORS(p), &erts_one_mon_size, &size);
+ erts_link_tree_foreach(ERTS_P_LINKS(p),
+ link_size, (void *) &size);
+ erts_monitor_tree_foreach(ERTS_P_MONITORS(p),
+ monitor_size, (void *) &size);
+ erts_monitor_list_foreach(ERTS_P_LT_MONITORS(p),
+ monitor_size, (void *) &size);
size += (p->heap_sz + p->mbuf_sz) * sizeof(Eterm);
if (p->abandoned_heap)
size += (p->hend - p->heap) * sizeof(Eterm);
@@ -105,11 +133,16 @@ Uint erts_process_memory(Process *p, int incl_msg_inq) {
size += (p->old_hend - p->old_heap) * sizeof(Eterm);
- size += p->msg.len * sizeof(ErtsMessage);
+ size += p->sig_qs.len * sizeof(ErtsMessage);
- for (mp = p->msg.first; mp; mp = mp->next)
- if (mp->data.attached)
- size += erts_msg_attached_data_size(mp)*sizeof(Eterm);
+ ERTS_FOREACH_SIG_PRIVQS(
+ p, mp,
+ {
+ if (ERTS_SIG_IS_NON_MSG((ErtsSignal *) mp))
+ size += erts_proc_sig_signal_size((ErtsSignal *) mp);
+ else if (mp->data.attached)
+ size += erts_msg_attached_data_size(mp) * sizeof(Eterm);
+ });
if (p->arg_reg != p->def_arg_reg) {
size += p->arity * sizeof(p->arg_reg[0]);
@@ -128,62 +161,75 @@ Uint erts_process_memory(Process *p, int incl_msg_inq) {
return size;
}
+static ERTS_INLINE void
+dump_msg(fmtfn_t to, void *to_arg, ErtsMessage *mp)
+{
+ if (ERTS_SIG_IS_MSG((ErtsSignal *) mp)) {
+ Eterm mesg = ERL_MESSAGE_TERM(mp);
+ if (is_value(mesg))
+ dump_element(to, to_arg, mesg);
+ else
+ dump_dist_ext(to, to_arg, mp->data.dist_ext);
+ mesg = ERL_MESSAGE_TOKEN(mp);
+ erts_print(to, to_arg, ":");
+ dump_element(to, to_arg, mesg);
+ erts_print(to, to_arg, "\n");
+ }
+}
+
+static ERTS_INLINE void
+heap_dump_msg(fmtfn_t to, void *to_arg, ErtsMessage *mp)
+{
+ if (ERTS_SIG_IS_MSG((ErtsSignal *) mp)) {
+ Eterm mesg = ERL_MESSAGE_TERM(mp);
+ if (is_value(mesg))
+ heap_dump(to, to_arg, mesg);
+ mesg = ERL_MESSAGE_TOKEN(mp);
+ heap_dump(to, to_arg, mesg);
+ }
+}
+
static void
dump_process_info(fmtfn_t to, void *to_arg, Process *p)
{
Eterm* sp;
- ErtsMessage* mp;
int yreg = -1;
- ERTS_MSGQ_MV_INQ2PRIVQ(p);
+ if (ERTS_TRACE_FLAGS(p) & F_SENSITIVE)
+ return;
- if ((ERTS_TRACE_FLAGS(p) & F_SENSITIVE) == 0 && p->msg.first) {
+ erts_proc_sig_fetch(p);
+
+ if (p->sig_qs.first || p->sig_qs.cont) {
erts_print(to, to_arg, "=proc_messages:%T\n", p->common.id);
- for (mp = p->msg.first; mp != NULL; mp = mp->next) {
- Eterm mesg = ERL_MESSAGE_TERM(mp);
- if (is_value(mesg))
- dump_element(to, to_arg, mesg);
- else
- dump_dist_ext(to, to_arg, mp->data.dist_ext);
- mesg = ERL_MESSAGE_TOKEN(mp);
- erts_print(to, to_arg, ":");
- dump_element(to, to_arg, mesg);
- erts_print(to, to_arg, "\n");
- }
+ ERTS_FOREACH_SIG_PRIVQS(p, mp, dump_msg(to, to_arg, mp));
}
- if ((ERTS_TRACE_FLAGS(p) & F_SENSITIVE) == 0) {
- if (p->dictionary) {
- erts_print(to, to_arg, "=proc_dictionary:%T\n", p->common.id);
- erts_deep_dictionary_dump(to, to_arg,
- p->dictionary, dump_element_nl);
- }
+ if (p->dictionary) {
+ erts_print(to, to_arg, "=proc_dictionary:%T\n", p->common.id);
+ erts_deep_dictionary_dump(to, to_arg,
+ p->dictionary, dump_element_nl);
}
- if ((ERTS_TRACE_FLAGS(p) & F_SENSITIVE) == 0) {
- erts_print(to, to_arg, "=proc_stack:%T\n", p->common.id);
- for (sp = p->stop; sp < STACK_START(p); sp++) {
- yreg = stack_element_dump(to, to_arg, sp, yreg);
- }
+ erts_print(to, to_arg, "=proc_stack:%T\n", p->common.id);
+ for (sp = p->stop; sp < STACK_START(p); sp++) {
+ yreg = stack_element_dump(to, to_arg, sp, yreg);
+ }
- erts_print(to, to_arg, "=proc_heap:%T\n", p->common.id);
- for (sp = p->stop; sp < STACK_START(p); sp++) {
- Eterm term = *sp;
-
- if (!is_catch(term) && !is_CP(term)) {
- heap_dump(to, to_arg, term);
- }
- }
- for (mp = p->msg.first; mp != NULL; mp = mp->next) {
- Eterm mesg = ERL_MESSAGE_TERM(mp);
- if (is_value(mesg))
- heap_dump(to, to_arg, mesg);
- mesg = ERL_MESSAGE_TOKEN(mp);
- heap_dump(to, to_arg, mesg);
- }
- if (p->dictionary) {
- erts_deep_dictionary_dump(to, to_arg, p->dictionary, heap_dump);
- }
+ erts_print(to, to_arg, "=proc_heap:%T\n", p->common.id);
+ for (sp = p->stop; sp < STACK_START(p); sp++) {
+ Eterm term = *sp;
+
+ if (!is_catch(term) && !is_CP(term)) {
+ heap_dump(to, to_arg, term);
+ }
+ }
+
+ if (p->sig_qs.first || p->sig_qs.cont)
+ ERTS_FOREACH_SIG_PRIVQS(p, mp, heap_dump_msg(to, to_arg, mp));
+
+ if (p->dictionary) {
+ erts_deep_dictionary_dump(to, to_arg, p->dictionary, heap_dump);
}
}
@@ -989,8 +1035,8 @@ erts_dump_extended_process_state(fmtfn_t to, void *to_arg, erts_aint32_t psflg)
erts_print(to, to_arg, "FREE"); break;
case ERTS_PSFLG_EXITING:
erts_print(to, to_arg, "EXITING"); break;
- case ERTS_PSFLG_PENDING_EXIT:
- erts_print(to, to_arg, "PENDING_EXIT"); break;
+ case ERTS_PSFLG_UNUSED:
+ erts_print(to, to_arg, "UNUSED"); break;
case ERTS_PSFLG_ACTIVE:
erts_print(to, to_arg, "ACTIVE"); break;
case ERTS_PSFLG_IN_RUNQ:
@@ -1001,10 +1047,10 @@ erts_dump_extended_process_state(fmtfn_t to, void *to_arg, erts_aint32_t psflg)
erts_print(to, to_arg, "SUSPENDED"); break;
case ERTS_PSFLG_GC:
erts_print(to, to_arg, "GC"); break;
- case ERTS_PSFLG_BOUND:
- erts_print(to, to_arg, "BOUND"); break;
- case ERTS_PSFLG_TRAP_EXIT:
- erts_print(to, to_arg, "TRAP_EXIT"); break;
+ case ERTS_PSFLG_SYS_TASKS:
+ erts_print(to, to_arg, "SYS_TASKS"); break;
+ case ERTS_PSFLG_SIG_IN_Q:
+ erts_print(to, to_arg, "SIG_IN_Q"); break;
case ERTS_PSFLG_ACTIVE_SYS:
erts_print(to, to_arg, "ACTIVE_SYS"); break;
case ERTS_PSFLG_RUNNING_SYS:
@@ -1015,8 +1061,8 @@ erts_dump_extended_process_state(fmtfn_t to, void *to_arg, erts_aint32_t psflg)
erts_print(to, to_arg, "DELAYED_SYS"); break;
case ERTS_PSFLG_OFF_HEAP_MSGQ:
erts_print(to, to_arg, "OFF_HEAP_MSGQ"); break;
- case ERTS_PSFLG_ON_HEAP_MSGQ:
- erts_print(to, to_arg, "ON_HEAP_MSGQ"); break;
+ case ERTS_PSFLG_SIG_Q:
+ erts_print(to, to_arg, "SIG_Q"); break;
case ERTS_PSFLG_DIRTY_CPU_PROC:
erts_print(to, to_arg, "DIRTY_CPU_PROC"); break;
case ERTS_PSFLG_DIRTY_IO_PROC:
diff --git a/erts/emulator/beam/erl_process_lock.c b/erts/emulator/beam/erl_process_lock.c
index 431867f27e..44c7892040 100644
--- a/erts/emulator/beam/erl_process_lock.c
+++ b/erts/emulator/beam/erl_process_lock.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2007-2017. All Rights Reserved.
+ * Copyright Ericsson AB 2007-2018. 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.
@@ -101,7 +101,6 @@ static void cleanup_tse(void);
#ifdef ERTS_ENABLE_LOCK_CHECK
static struct {
Sint16 proc_lock_main;
- Sint16 proc_lock_link;
Sint16 proc_lock_msgq;
Sint16 proc_lock_btm;
Sint16 proc_lock_status;
@@ -140,7 +139,6 @@ erts_init_proc_lock(int cpus)
#endif
#ifdef ERTS_ENABLE_LOCK_CHECK
lc_id.proc_lock_main = erts_lc_get_lock_order_id("proc_main");
- lc_id.proc_lock_link = erts_lc_get_lock_order_id("proc_link");
lc_id.proc_lock_msgq = erts_lc_get_lock_order_id("proc_msgq");
lc_id.proc_lock_btm = erts_lc_get_lock_order_id("proc_btm");
lc_id.proc_lock_status = erts_lc_get_lock_order_id("proc_status");
@@ -1055,12 +1053,6 @@ erts_proc_lock_init(Process *p)
#ifdef ERTS_ENABLE_LOCK_CHECK
erts_lc_trylock(1, &p->lock.main.lc);
#endif
- erts_mtx_init(&p->lock.link, "proc_link", p->common.id,
- ERTS_LOCK_FLAGS_CATEGORY_PROCESS);
- ethr_mutex_lock(&p->lock.link.mtx);
-#ifdef ERTS_ENABLE_LOCK_CHECK
- erts_lc_trylock(1, &p->lock.link.lc);
-#endif
erts_mtx_init(&p->lock.msgq, "proc_msgq", p->common.id,
ERTS_LOCK_FLAGS_CATEGORY_PROCESS);
ethr_mutex_lock(&p->lock.msgq.mtx);
@@ -1102,7 +1094,6 @@ erts_proc_lock_fin(Process *p)
{
#if ERTS_PROC_LOCK_RAW_MUTEX_IMPL
erts_mtx_destroy(&p->lock.main);
- erts_mtx_destroy(&p->lock.link);
erts_mtx_destroy(&p->lock.msgq);
erts_mtx_destroy(&p->lock.btm);
erts_mtx_destroy(&p->lock.status);
@@ -1144,8 +1135,6 @@ void erts_lcnt_enable_proc_lock_count(Process *proc, int enable) {
erts_lcnt_init_lock_info_idx(carrier, ERTS_LCNT_PROCLOCK_IDX_MAIN,
"proc_main", proc->common.id, ERTS_LOCK_TYPE_PROCLOCK);
- erts_lcnt_init_lock_info_idx(carrier, ERTS_LCNT_PROCLOCK_IDX_LINK,
- "proc_link", proc->common.id, ERTS_LOCK_TYPE_PROCLOCK);
erts_lcnt_init_lock_info_idx(carrier, ERTS_LCNT_PROCLOCK_IDX_MSGQ,
"proc_msgq", proc->common.id, ERTS_LOCK_TYPE_PROCLOCK);
erts_lcnt_init_lock_info_idx(carrier, ERTS_LCNT_PROCLOCK_IDX_BTM,
@@ -1200,10 +1189,6 @@ erts_proc_lc_lock(Process *p, ErtsProcLocks locks, char *file, unsigned int line
lck.id = lc_id.proc_lock_main;
erts_lc_lock_x(&lck,file,line);
}
- if (locks & ERTS_PROC_LOCK_LINK) {
- lck.id = lc_id.proc_lock_link;
- erts_lc_lock_x(&lck,file,line);
- }
if (locks & ERTS_PROC_LOCK_MSGQ) {
lck.id = lc_id.proc_lock_msgq;
erts_lc_lock_x(&lck,file,line);
@@ -1233,10 +1218,6 @@ erts_proc_lc_trylock(Process *p, ErtsProcLocks locks, int locked,
lck.id = lc_id.proc_lock_main;
erts_lc_trylock_x(locked, &lck, file, line);
}
- if (locks & ERTS_PROC_LOCK_LINK) {
- lck.id = lc_id.proc_lock_link;
- erts_lc_trylock_x(locked, &lck, file, line);
- }
if (locks & ERTS_PROC_LOCK_MSGQ) {
lck.id = lc_id.proc_lock_msgq;
erts_lc_trylock_x(locked, &lck, file, line);
@@ -1277,10 +1258,6 @@ erts_proc_lc_unlock(Process *p, ErtsProcLocks locks)
lck.id = lc_id.proc_lock_msgq;
erts_lc_unlock(&lck);
}
- if (locks & ERTS_PROC_LOCK_LINK) {
- lck.id = lc_id.proc_lock_link;
- erts_lc_unlock(&lck);
- }
if (locks & ERTS_PROC_LOCK_MAIN) {
lck.id = lc_id.proc_lock_main;
erts_lc_unlock(&lck);
@@ -1312,10 +1289,6 @@ erts_proc_lc_might_unlock(Process *p, ErtsProcLocks locks)
lck.id = lc_id.proc_lock_msgq;
erts_lc_might_unlock(&lck);
}
- if (locks & ERTS_PROC_LOCK_LINK) {
- lck.id = lc_id.proc_lock_link;
- erts_lc_might_unlock(&lck);
- }
if (locks & ERTS_PROC_LOCK_MAIN) {
lck.id = lc_id.proc_lock_main;
erts_lc_might_unlock(&lck);
@@ -1323,8 +1296,6 @@ erts_proc_lc_might_unlock(Process *p, ErtsProcLocks locks)
#elif ERTS_PROC_LOCK_RAW_MUTEX_IMPL
if (locks & ERTS_PROC_LOCK_MAIN)
erts_lc_might_unlock(&p->lock.main.lc);
- if (locks & ERTS_PROC_LOCK_LINK)
- erts_lc_might_unlock(&p->lock.link.lc);
if (locks & ERTS_PROC_LOCK_MSGQ)
erts_lc_might_unlock(&p->lock.msgq.lc);
if (locks & ERTS_PROC_LOCK_BTM)
@@ -1348,10 +1319,6 @@ erts_proc_lc_require_lock(Process *p, ErtsProcLocks locks, char *file,
lck.id = lc_id.proc_lock_main;
erts_lc_require_lock(&lck, file, line);
}
- if (locks & ERTS_PROC_LOCK_LINK) {
- lck.id = lc_id.proc_lock_link;
- erts_lc_require_lock(&lck, file, line);
- }
if (locks & ERTS_PROC_LOCK_MSGQ) {
lck.id = lc_id.proc_lock_msgq;
erts_lc_require_lock(&lck, file, line);
@@ -1371,8 +1338,6 @@ erts_proc_lc_require_lock(Process *p, ErtsProcLocks locks, char *file,
#elif ERTS_PROC_LOCK_RAW_MUTEX_IMPL
if (locks & ERTS_PROC_LOCK_MAIN)
erts_lc_require_lock(&p->lock.main.lc, file, line);
- if (locks & ERTS_PROC_LOCK_LINK)
- erts_lc_require_lock(&p->lock.link.lc, file, line);
if (locks & ERTS_PROC_LOCK_MSGQ)
erts_lc_require_lock(&p->lock.msgq.lc, file, line);
if (locks & ERTS_PROC_LOCK_BTM)
@@ -1407,10 +1372,6 @@ erts_proc_lc_unrequire_lock(Process *p, ErtsProcLocks locks)
lck.id = lc_id.proc_lock_msgq;
erts_lc_unrequire_lock(&lck);
}
- if (locks & ERTS_PROC_LOCK_LINK) {
- lck.id = lc_id.proc_lock_link;
- erts_lc_unrequire_lock(&lck);
- }
if (locks & ERTS_PROC_LOCK_MAIN) {
lck.id = lc_id.proc_lock_main;
erts_lc_unrequire_lock(&lck);
@@ -1418,8 +1379,6 @@ erts_proc_lc_unrequire_lock(Process *p, ErtsProcLocks locks)
#elif ERTS_PROC_LOCK_RAW_MUTEX_IMPL
if (locks & ERTS_PROC_LOCK_MAIN)
erts_lc_unrequire_lock(&p->lock.main.lc);
- if (locks & ERTS_PROC_LOCK_LINK)
- erts_lc_unrequire_lock(&p->lock.link.lc);
if (locks & ERTS_PROC_LOCK_MSGQ)
erts_lc_unrequire_lock(&p->lock.msgq.lc);
if (locks & ERTS_PROC_LOCK_BTM)
@@ -1443,8 +1402,6 @@ erts_proc_lc_trylock_force_busy(Process *p, ErtsProcLocks locks)
if (locks & ERTS_PROC_LOCK_MAIN)
lck.id = lc_id.proc_lock_main;
- else if (locks & ERTS_PROC_LOCK_LINK)
- lck.id = lc_id.proc_lock_link;
else if (locks & ERTS_PROC_LOCK_MSGQ)
lck.id = lc_id.proc_lock_msgq;
else if (locks & ERTS_PROC_LOCK_BTM)
@@ -1487,10 +1444,6 @@ void erts_proc_lc_chk_only_proc(Process *p, ErtsProcLocks locks)
have_locks[have_locks_len].id = lc_id.proc_lock_main;
have_locks[have_locks_len++].extra = p->common.id;
}
- if (locks & ERTS_PROC_LOCK_LINK) {
- have_locks[have_locks_len].id = lc_id.proc_lock_link;
- have_locks[have_locks_len++].extra = p->common.id;
- }
if (locks & ERTS_PROC_LOCK_MSGQ) {
have_locks[have_locks_len].id = lc_id.proc_lock_msgq;
have_locks[have_locks_len++].extra = p->common.id;
@@ -1511,8 +1464,6 @@ void erts_proc_lc_chk_only_proc(Process *p, ErtsProcLocks locks)
erts_lc_lock_t have_locks[6];
if (locks & ERTS_PROC_LOCK_MAIN)
have_locks[have_locks_len++] = p->lock.main.lc;
- if (locks & ERTS_PROC_LOCK_LINK)
- have_locks[have_locks_len++] = p->lock.link.lc;
if (locks & ERTS_PROC_LOCK_MSGQ)
have_locks[have_locks_len++] = p->lock.msgq.lc;
if (locks & ERTS_PROC_LOCK_BTM)
@@ -1540,10 +1491,6 @@ erts_proc_lc_chk_have_proc_locks(Process *p, ErtsProcLocks locks)
have_locks[have_locks_len].id = lc_id.proc_lock_main;
have_locks[have_locks_len++].extra = p->common.id;
}
- if (locks & ERTS_PROC_LOCK_LINK) {
- have_locks[have_locks_len].id = lc_id.proc_lock_link;
- have_locks[have_locks_len++].extra = p->common.id;
- }
if (locks & ERTS_PROC_LOCK_MSGQ) {
have_locks[have_locks_len].id = lc_id.proc_lock_msgq;
have_locks[have_locks_len++].extra = p->common.id;
@@ -1564,8 +1511,6 @@ erts_proc_lc_chk_have_proc_locks(Process *p, ErtsProcLocks locks)
erts_lc_lock_t have_locks[6];
if (locks & ERTS_PROC_LOCK_MAIN)
have_locks[have_locks_len++] = p->lock.main.lc;
- if (locks & ERTS_PROC_LOCK_LINK)
- have_locks[have_locks_len++] = p->lock.link.lc;
if (locks & ERTS_PROC_LOCK_MSGQ)
have_locks[have_locks_len++] = p->lock.msgq.lc;
if (locks & ERTS_PROC_LOCK_BTM)
@@ -1603,14 +1548,6 @@ erts_proc_lc_chk_proc_locks(Process *p, ErtsProcLocks locks)
have_not_locks[have_not_locks_len].id = lc_id.proc_lock_main;
have_not_locks[have_not_locks_len++].extra = p->common.id;
}
- if (locks & ERTS_PROC_LOCK_LINK) {
- have_locks[have_locks_len].id = lc_id.proc_lock_link;
- have_locks[have_locks_len++].extra = p->common.id;
- }
- else {
- have_not_locks[have_not_locks_len].id = lc_id.proc_lock_link;
- have_not_locks[have_not_locks_len++].extra = p->common.id;
- }
if (locks & ERTS_PROC_LOCK_MSGQ) {
have_locks[have_locks_len].id = lc_id.proc_lock_msgq;
have_locks[have_locks_len++].extra = p->common.id;
@@ -1651,10 +1588,6 @@ erts_proc_lc_chk_proc_locks(Process *p, ErtsProcLocks locks)
have_locks[have_locks_len++] = p->lock.main.lc;
else
have_not_locks[have_not_locks_len++] = p->lock.main.lc;
- if (locks & ERTS_PROC_LOCK_LINK)
- have_locks[have_locks_len++] = p->lock.link.lc;
- else
- have_not_locks[have_not_locks_len++] = p->lock.link.lc;
if (locks & ERTS_PROC_LOCK_MSGQ)
have_locks[have_locks_len++] = p->lock.msgq.lc;
else
@@ -1680,13 +1613,10 @@ erts_proc_lc_chk_proc_locks(Process *p, ErtsProcLocks locks)
ErtsProcLocks
erts_proc_lc_my_proc_locks(Process *p)
{
- int resv[6];
+ int resv[5];
ErtsProcLocks res = 0;
#if ERTS_PROC_LOCK_OWN_IMPL
- erts_lc_lock_t locks[6] = {ERTS_LC_LOCK_INIT(lc_id.proc_lock_main,
- p->common.id,
- ERTS_LOCK_TYPE_PROCLOCK),
- ERTS_LC_LOCK_INIT(lc_id.proc_lock_link,
+ erts_lc_lock_t locks[5] = {ERTS_LC_LOCK_INIT(lc_id.proc_lock_main,
p->common.id,
ERTS_LOCK_TYPE_PROCLOCK),
ERTS_LC_LOCK_INIT(lc_id.proc_lock_msgq,
@@ -1702,26 +1632,23 @@ erts_proc_lc_my_proc_locks(Process *p)
p->common.id,
ERTS_LOCK_TYPE_PROCLOCK)};
#elif ERTS_PROC_LOCK_RAW_MUTEX_IMPL
- erts_lc_lock_t locks[6] = {p->lock.main.lc,
- p->lock.link.lc,
+ erts_lc_lock_t locks[5] = {p->lock.main.lc,
p->lock.msgq.lc,
p->lock.btm.lc,
p->lock.status.lc,
p->lock.trace.lc};
#endif
- erts_lc_have_locks(resv, locks, 6);
+ erts_lc_have_locks(resv, locks, 5);
if (resv[0])
res |= ERTS_PROC_LOCK_MAIN;
if (resv[1])
- res |= ERTS_PROC_LOCK_LINK;
- if (resv[2])
res |= ERTS_PROC_LOCK_MSGQ;
- if (resv[3])
+ if (resv[2])
res |= ERTS_PROC_LOCK_BTM;
- if (resv[4])
+ if (resv[3])
res |= ERTS_PROC_LOCK_STATUS;
- if (resv[5])
+ if (resv[4])
res |= ERTS_PROC_LOCK_TRACE;
return res;
@@ -1730,15 +1657,14 @@ erts_proc_lc_my_proc_locks(Process *p)
void
erts_proc_lc_chk_no_proc_locks(char *file, int line)
{
- int resv[6];
- int ids[6] = {lc_id.proc_lock_main,
- lc_id.proc_lock_link,
+ int resv[5];
+ int ids[5] = {lc_id.proc_lock_main,
lc_id.proc_lock_msgq,
lc_id.proc_lock_btm,
lc_id.proc_lock_status,
lc_id.proc_lock_trace};
- erts_lc_have_lock_ids(resv, ids, 6);
- if (!ERTS_IS_CRASH_DUMPING && (resv[0] || resv[1] || resv[2] || resv[3] || resv[4] || resv[5])) {
+ erts_lc_have_lock_ids(resv, ids, 5);
+ if (!ERTS_IS_CRASH_DUMPING && (resv[0] || resv[1] || resv[2] || resv[3] || resv[4])) {
erts_lc_fail("%s:%d: Thread has process locks locked when expected "
"not to have any process locks locked",
file, line);
diff --git a/erts/emulator/beam/erl_process_lock.h b/erts/emulator/beam/erl_process_lock.h
index 9d5691d3c4..43f396c547 100644
--- a/erts/emulator/beam/erl_process_lock.h
+++ b/erts/emulator/beam/erl_process_lock.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2007-2017. All Rights Reserved.
+ * Copyright Ericsson AB 2007-2018. 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.
@@ -66,7 +66,7 @@
#endif
-#define ERTS_PROC_LOCK_MAX_BIT 5
+#define ERTS_PROC_LOCK_MAX_BIT 4
typedef erts_aint32_t ErtsProcLocks;
@@ -82,19 +82,17 @@ typedef struct erts_proc_lock_t_ {
/* Each erts_mtx_t has its own lock counter ^ */
#define ERTS_LCNT_PROCLOCK_IDX_MAIN 0
- #define ERTS_LCNT_PROCLOCK_IDX_LINK 1
- #define ERTS_LCNT_PROCLOCK_IDX_MSGQ 2
- #define ERTS_LCNT_PROCLOCK_IDX_BTM 3
- #define ERTS_LCNT_PROCLOCK_IDX_STATUS 4
- #define ERTS_LCNT_PROCLOCK_IDX_TRACE 5
+ #define ERTS_LCNT_PROCLOCK_IDX_MSGQ 1
+ #define ERTS_LCNT_PROCLOCK_IDX_BTM 2
+ #define ERTS_LCNT_PROCLOCK_IDX_STATUS 3
+ #define ERTS_LCNT_PROCLOCK_IDX_TRACE 4
- #define ERTS_LCNT_PROCLOCK_COUNT 6
+ #define ERTS_LCNT_PROCLOCK_COUNT 5
erts_lcnt_ref_t lcnt_carrier;
#endif
#elif ERTS_PROC_LOCK_RAW_MUTEX_IMPL
erts_mtx_t main;
- erts_mtx_t link;
erts_mtx_t msgq;
erts_mtx_t btm;
erts_mtx_t status;
@@ -118,27 +116,18 @@ typedef struct erts_proc_lock_t_ {
#define ERTS_PROC_LOCK_MAIN (((ErtsProcLocks) 1) << 0)
/*
- * Link lock:
- * Protects the following fields in the process structure:
- * * nlinks
- * * monitors
- * * suspend_monitors
- */
-#define ERTS_PROC_LOCK_LINK (((ErtsProcLocks) 1) << 1)
-
-/*
* Message queue lock:
* Protects the following fields in the process structure:
* * msg_inq
*/
-#define ERTS_PROC_LOCK_MSGQ (((ErtsProcLocks) 1) << 2)
+#define ERTS_PROC_LOCK_MSGQ (((ErtsProcLocks) 1) << 1)
/*
* Bif timer lock:
* Protects the following fields in the process structure:
* * bif_timers
*/
-#define ERTS_PROC_LOCK_BTM (((ErtsProcLocks) 1) << 3)
+#define ERTS_PROC_LOCK_BTM (((ErtsProcLocks) 1) << 2)
/*
* Status lock:
@@ -148,7 +137,7 @@ typedef struct erts_proc_lock_t_ {
* * sys_tasks
* * ...
*/
-#define ERTS_PROC_LOCK_STATUS (((ErtsProcLocks) 1) << 4)
+#define ERTS_PROC_LOCK_STATUS (((ErtsProcLocks) 1) << 3)
/*
* Trace message lock:
@@ -277,9 +266,6 @@ void erts_lcnt_proc_lock(erts_proc_lock_t *lock, ErtsProcLocks locks) {
if (locks & ERTS_PROC_LOCK_MAIN) {
erts_lcnt_lock_idx(carrier, ERTS_LCNT_PROCLOCK_IDX_MAIN);
}
- if (locks & ERTS_PROC_LOCK_LINK) {
- erts_lcnt_lock_idx(carrier, ERTS_LCNT_PROCLOCK_IDX_LINK);
- }
if (locks & ERTS_PROC_LOCK_MSGQ) {
erts_lcnt_lock_idx(carrier, ERTS_LCNT_PROCLOCK_IDX_MSGQ);
}
@@ -307,9 +293,6 @@ void erts_lcnt_proc_lock_post_x(erts_proc_lock_t *lock, ErtsProcLocks locks,
if (locks & ERTS_PROC_LOCK_MAIN) {
erts_lcnt_lock_post_x_idx(carrier, ERTS_LCNT_PROCLOCK_IDX_MAIN, file, line);
}
- if (locks & ERTS_PROC_LOCK_LINK) {
- erts_lcnt_lock_post_x_idx(carrier, ERTS_LCNT_PROCLOCK_IDX_LINK, file, line);
- }
if (locks & ERTS_PROC_LOCK_MSGQ) {
erts_lcnt_lock_post_x_idx(carrier, ERTS_LCNT_PROCLOCK_IDX_MSGQ, file, line);
}
@@ -336,9 +319,6 @@ void erts_lcnt_proc_lock_unacquire(erts_proc_lock_t *lock, ErtsProcLocks locks)
if (locks & ERTS_PROC_LOCK_MAIN) {
erts_lcnt_lock_unacquire_idx(carrier, ERTS_LCNT_PROCLOCK_IDX_MAIN);
}
- if (locks & ERTS_PROC_LOCK_LINK) {
- erts_lcnt_lock_unacquire_idx(carrier, ERTS_LCNT_PROCLOCK_IDX_LINK);
- }
if (locks & ERTS_PROC_LOCK_MSGQ) {
erts_lcnt_lock_unacquire_idx(carrier, ERTS_LCNT_PROCLOCK_IDX_MSGQ);
}
@@ -365,9 +345,6 @@ void erts_lcnt_proc_unlock(erts_proc_lock_t *lock, ErtsProcLocks locks) {
if (locks & ERTS_PROC_LOCK_MAIN) {
erts_lcnt_unlock_idx(carrier, ERTS_LCNT_PROCLOCK_IDX_MAIN);
}
- if (locks & ERTS_PROC_LOCK_LINK) {
- erts_lcnt_unlock_idx(carrier, ERTS_LCNT_PROCLOCK_IDX_LINK);
- }
if (locks & ERTS_PROC_LOCK_MSGQ) {
erts_lcnt_unlock_idx(carrier, ERTS_LCNT_PROCLOCK_IDX_MSGQ);
}
@@ -394,9 +371,6 @@ void erts_lcnt_proc_trylock(erts_proc_lock_t *lock, ErtsProcLocks locks, int res
if (locks & ERTS_PROC_LOCK_MAIN) {
erts_lcnt_trylock_idx(carrier, ERTS_LCNT_PROCLOCK_IDX_MAIN, res);
}
- if (locks & ERTS_PROC_LOCK_LINK) {
- erts_lcnt_trylock_idx(carrier, ERTS_LCNT_PROCLOCK_IDX_LINK, res);
- }
if (locks & ERTS_PROC_LOCK_MSGQ) {
erts_lcnt_trylock_idx(carrier, ERTS_LCNT_PROCLOCK_IDX_MSGQ, res);
}
@@ -640,9 +614,6 @@ erts_proc_raw_trylock__(Process *p, ErtsProcLocks locks)
if (locks & ERTS_PROC_LOCK_MAIN)
if (erts_mtx_trylock(&p->lock.main) == EBUSY)
goto busy_main;
- if (locks & ERTS_PROC_LOCK_LINK)
- if (erts_mtx_trylock(&p->lock.link) == EBUSY)
- goto busy_link;
if (locks & ERTS_PROC_LOCK_MSGQ)
if (erts_mtx_trylock(&p->lock.msgq) == EBUSY)
goto busy_msgq;
@@ -668,9 +639,6 @@ busy_btm:
if (locks & ERTS_PROC_LOCK_MSGQ)
erts_mtx_unlock(&p->lock.msgq);
busy_msgq:
- if (locks & ERTS_PROC_LOCK_LINK)
- erts_mtx_unlock(&p->lock.link);
-busy_link:
if (locks & ERTS_PROC_LOCK_MAIN)
erts_mtx_unlock(&p->lock.main);
busy_main:
@@ -741,8 +709,6 @@ erts_proc_lock__(Process *p,
#elif ERTS_PROC_LOCK_RAW_MUTEX_IMPL
if (locks & ERTS_PROC_LOCK_MAIN)
erts_mtx_lock(&p->lock.main);
- if (locks & ERTS_PROC_LOCK_LINK)
- erts_mtx_lock(&p->lock.link);
if (locks & ERTS_PROC_LOCK_MSGQ)
erts_mtx_lock(&p->lock.msgq);
if (locks & ERTS_PROC_LOCK_BTM)
@@ -844,8 +810,6 @@ erts_proc_unlock__(Process *p,
erts_mtx_unlock(&p->lock.btm);
if (locks & ERTS_PROC_LOCK_MSGQ)
erts_mtx_unlock(&p->lock.msgq);
- if (locks & ERTS_PROC_LOCK_LINK)
- erts_mtx_unlock(&p->lock.link);
if (locks & ERTS_PROC_LOCK_MAIN)
erts_mtx_unlock(&p->lock.main);
#endif
diff --git a/erts/emulator/beam/erl_ptab.h b/erts/emulator/beam/erl_ptab.h
index 4858cc8ab8..94f0247492 100644
--- a/erts/emulator/beam/erl_ptab.h
+++ b/erts/emulator/beam/erl_ptab.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2012-2016. All Rights Reserved.
+ * Copyright Ericsson AB 2012-2018. 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.
@@ -35,7 +35,7 @@
#include "erl_thr_progress.h"
#undef ERL_THR_PROGRESS_TSD_TYPE_ONLY
#include "erl_alloc.h"
-#include "erl_monitors.h"
+#include "erl_monitor_link.h"
#define ERTS_TRACER(P) ((P)->common.tracer)
#define ERTS_TRACER_MODULE(T) (CAR(list_val(T)))
@@ -44,6 +44,7 @@
#define ERTS_P_LINKS(P) ((P)->common.u.alive.links)
#define ERTS_P_MONITORS(P) ((P)->common.u.alive.monitors)
+#define ERTS_P_LT_MONITORS(P) ((P)->common.u.alive.lt_monitors)
#define IS_TRACED(p) \
(ERTS_TRACER(p) != NIL)
@@ -68,6 +69,7 @@ typedef struct {
struct reg_proc *reg;
ErtsLink *links;
ErtsMonitor *monitors;
+ ErtsMonitor *lt_monitors;
} alive;
/* --- While being released --- */
diff --git a/erts/emulator/beam/erl_rbtree.h b/erts/emulator/beam/erl_rbtree.h
index e59d6900b0..e50abf5cec 100644
--- a/erts/emulator/beam/erl_rbtree.h
+++ b/erts/emulator/beam/erl_rbtree.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2015-2017. All Rights Reserved.
+ * Copyright Ericsson AB 2015-2018. 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.
@@ -50,8 +50,14 @@
* - ERTS_RBT_GET_LEFT(T) - Get left child node.
* - ERTS_RBT_SET_LEFT(T, L) - Set left child node.
* - ERTS_RBT_GET_KEY(T) - Get key of node.
- * - ERTS_RBT_IS_LT(KX, KY) - Is key KX less than key KY?
- * - ERTS_RBT_IS_EQ(KX, KY) - Is key KX equal to key KY?
+ * Either:
+ * - ERTS_RBT_CMP_KEYS(KX, KY) - Compare keys...
+ * or:
+ * - ERTS_RBT_IS_LT(KX, KY) - Is key KX less than key KY?
+ * - ERTS_RBT_IS_EQ(KX, KY) - Is key KX equal to key KY?
+ *
+ * If ERTS_RBT_CMP_KEYS is defined ERTS_RBT_IS_LT and
+ * ERTS_RBT_IS_EQ will be redefined using ERTS_RBT_CMP_KEYS
*
* Optional defines:
*
@@ -337,6 +343,15 @@
* Should only be used for debuging.
*/
+#ifdef ERTS_RBT_CMP_KEYS
+
+# undef ERTS_RBT_IS_LT
+# define ERTS_RBT_IS_LT(KX, KY) (ERTS_RBT_CMP_KEYS((KX), (KY)) < 0)
+
+# undef ERTS_RBT_IS_EQ
+# define ERTS_RBT_IS_EQ(KX, KY) (ERTS_RBT_CMP_KEYS((KX), (KY)) == 0)
+
+#endif
/*
* Check that we have all mandatory defines
@@ -396,6 +411,16 @@
# error Missing definition of ERTS_RBT_IS_EQ
#endif
+#undef ERTS_RBT_IS_GT__
+#ifdef ERTS_RBT_CMP_KEYS
+# define ERTS_RBT_IS_GT__(KX, KY) \
+ (ERTS_RBT_CMP_KEYS((KX), (KY)) > 0)
+#else
+# define ERTS_RBT_IS_GT__(KX, KY) \
+ (!ERTS_RBT_IS_LT((KX), (KY)) && !ERTS_RBT_IS_EQ((KX), (KY)))
+
+#endif
+
#if defined(ERTS_RBT_HARD_DEBUG) || defined(DEBUG)
# ifndef ERTS_RBT_DEBUG
# define ERTS_RBT_DEBUG 1
@@ -1007,19 +1032,30 @@ ERTS_RBT_FUNC__(insert_aux__)(ERTS_RBT_T **root, ERTS_RBT_T *n, int lookup)
ERTS_RBT_T *p, *x = *root;
while (1) {
- ERTS_RBT_KEY_T kx;
+ ERTS_RBT_KEY_T kx = ERTS_RBT_GET_KEY(x);
ERTS_RBT_T *c;
+ int kres;
+#ifdef ERTS_RBT_CMP_KEYS
+ int kcmp = ERTS_RBT_CMP_KEYS(kn, kx);
+ kres = kcmp == 0;
+#else
+ kres = ERTS_RBT_IS_EQ(kn, kx);
+#endif
- kx = ERTS_RBT_GET_KEY(x);
-
- if (lookup && ERTS_RBT_IS_EQ(kn, kx)) {
+ if (lookup && kres) {
ERTS_RBT_HDBG_CHECK_TREE__(*root, NULL);
return x;
}
- if (ERTS_RBT_IS_LT(kn, kx)) {
+#ifdef ERTS_RBT_CMP_KEYS
+ kres = kcmp < 0;
+#else
+ kres = ERTS_RBT_IS_LT(kn, kx);
+#endif
+
+ if (kres) {
c = ERTS_RBT_GET_LEFT(x);
if (!c) {
ERTS_RBT_SET_PARENT(n, x);
@@ -1075,6 +1111,101 @@ ERTS_RBT_FUNC__(insert)(ERTS_RBT_T **root, ERTS_RBT_T *n)
#endif /* ERTS_RBT_WANT_INSERT */
+#ifdef ERTS_RBT_WANT_LOOKUP_CREATE
+static ERTS_INLINE ERTS_RBT_T *
+ERTS_RBT_FUNC__(lookup_create)(ERTS_RBT_T **root,
+ ERTS_RBT_KEY_T kn,
+ ERTS_RBT_T *(*create)(ERTS_RBT_KEY_T, void *),
+ void *arg,
+ int *created)
+{
+ ERTS_RBT_T *n;
+ ERTS_RBT_HDBG_CHECK_TREE__(*root, NULL);
+
+ if (!*root) {
+ n = (*create)(kn, arg);
+ ERTS_RBT_INIT_EMPTY_TNODE(n);
+ ERTS_RBT_ASSERT(ERTS_RBT_IS_EQ(ERTS_RBT_GET_KEY(n), kn));
+ ERTS_RBT_SET_BLACK(n);
+ *root = n;
+ *created = !0;
+#ifdef ERTS_RBT_UPDATE_ATTACHED_DATA_CHGROOT
+ ERTS_RBT_UPDATE_ATTACHED_DATA_CHGROOT(NULL, n);
+#endif
+ }
+ else {
+ ERTS_RBT_T *p, *x = *root;
+
+ while (1) {
+ ERTS_RBT_KEY_T kx = ERTS_RBT_GET_KEY(x);
+ ERTS_RBT_T *c;
+ int kres;
+#ifdef ERTS_RBT_CMP_KEYS
+ int kcmp = ERTS_RBT_CMP_KEYS(kn, kx);
+ kres = kcmp == 0;
+#else
+ kres = ERTS_RBT_IS_EQ(kn, kx);
+#endif
+
+ if (kres) {
+
+ ERTS_RBT_HDBG_CHECK_TREE__(*root, NULL);
+
+ *created = 0;
+ return x;
+ }
+
+#ifdef ERTS_RBT_CMP_KEYS
+ kres = kcmp < 0;
+#else
+ kres = ERTS_RBT_IS_LT(kn, kx);
+#endif
+
+ if (kres) {
+ c = ERTS_RBT_GET_LEFT(x);
+ if (!c) {
+ n = (*create)(kn, arg);
+ ERTS_RBT_INIT_EMPTY_TNODE(n);
+ ERTS_RBT_ASSERT(ERTS_RBT_IS_EQ(ERTS_RBT_GET_KEY(n), kn));
+ *created = !0;
+ ERTS_RBT_SET_PARENT(n, x);
+ ERTS_RBT_SET_LEFT(x, n);
+ p = x;
+ break;
+ }
+ }
+ else {
+ c = ERTS_RBT_GET_RIGHT(x);
+ if (!c) {
+ n = (*create)(kn, arg);
+ ERTS_RBT_INIT_EMPTY_TNODE(n);
+ ERTS_RBT_ASSERT(ERTS_RBT_IS_EQ(ERTS_RBT_GET_KEY(n), kn));
+ *created = !0;
+ ERTS_RBT_SET_PARENT(n, x);
+ ERTS_RBT_SET_RIGHT(x, n);
+ p = x;
+ break;
+ }
+ }
+
+ x = c;
+ }
+
+ ERTS_RBT_ASSERT(ERTS_RBT_IS_EQ(ERTS_RBT_GET_KEY(n), kn));
+ ERTS_RBT_ASSERT(p);
+
+ ERTS_RBT_SET_RED(n);
+ if (ERTS_RBT_IS_RED(p))
+ ERTS_RBT_FUNC__(insert_fixup__)(root, n);
+ }
+
+ ERTS_RBT_HDBG_CHECK_TREE__(*root, n);
+
+ return n;
+}
+
+#endif /* ERTS_RBT_WANT_LOOKUP_CREATE */
+
#ifdef ERTS_RBT_WANT_LOOKUP
static ERTS_RBT_API_INLINE__ ERTS_RBT_T *
@@ -1088,11 +1219,24 @@ ERTS_RBT_FUNC__(lookup)(ERTS_RBT_T *root, ERTS_RBT_KEY_T key)
while (1) {
ERTS_RBT_KEY_T kx = ERTS_RBT_GET_KEY(x);
ERTS_RBT_T *c;
+ int kres;
+#ifdef ERTS_RBT_CMP_KEYS
+ int kcmp = ERTS_RBT_CMP_KEYS(key, kx);
+ kres = kcmp == 0;
+#else
+ kres = ERTS_RBT_IS_EQ(key, kx);
+#endif
- if (ERTS_RBT_IS_EQ(key, kx))
+ if (kres)
return x;
- if (ERTS_RBT_IS_LT(key, kx)) {
+#ifdef ERTS_RBT_CMP_KEYS
+ kres = kcmp < 0;
+#else
+ kres = ERTS_RBT_IS_LT(key, kx);
+#endif
+
+ if (kres) {
c = ERTS_RBT_GET_LEFT(x);
if (!c)
return NULL;
@@ -1426,14 +1570,14 @@ ERTS_RBT_FUNC__(foreach_large)(ERTS_RBT_T *root,
#ifdef ERTS_RBT_WANT_FOREACH_YIELDING
-static ERTS_RBT_API_INLINE__ void
+static ERTS_RBT_API_INLINE__ int
ERTS_RBT_FUNC__(foreach_yielding)(ERTS_RBT_T *root,
void (*op)(ERTS_RBT_T *, void *),
void *arg,
ERTS_RBT_YIELD_STATE_T__ *ystate,
Sint ylimit)
{
- (void) ERTS_RBT_FUNC__(foreach_unordered__)(*root, 0, op, arg,
+ return ERTS_RBT_FUNC__(foreach_unordered__)(&root, 0, op, arg,
1, ystate, ylimit);
}
@@ -1630,8 +1774,7 @@ ERTS_RBT_FUNC__(hdbg_check_tree)(ERTS_RBT_T *root, ERTS_RBT_T *n)
kx = ERTS_RBT_GET_KEY(x);
kc = ERTS_RBT_GET_KEY(c);
- ERTS_RBT_ASSERT(ERTS_RBT_IS_LT(kc, kx)
- || ERTS_RBT_IS_EQ(kc, kx));
+ ERTS_RBT_ASSERT(!ERTS_RBT_IS_GT__(kc, kx));
x = c;
}
@@ -1649,8 +1792,8 @@ ERTS_RBT_FUNC__(hdbg_check_tree)(ERTS_RBT_T *root, ERTS_RBT_T *n)
kx = ERTS_RBT_GET_KEY(x);
kc = ERTS_RBT_GET_KEY(c);
- ERTS_RBT_ASSERT(ERTS_RBT_IS_LT(kx, kc)
- || ERTS_RBT_IS_EQ(kx, kc));
+ ERTS_RBT_ASSERT(!ERTS_RBT_IS_GT__(kx, kc));
+
x = c;
}
@@ -1672,8 +1815,8 @@ ERTS_RBT_FUNC__(hdbg_check_tree)(ERTS_RBT_T *root, ERTS_RBT_T *n)
kx = ERTS_RBT_GET_KEY(x);
kc = ERTS_RBT_GET_KEY(c);
- ERTS_RBT_ASSERT(ERTS_RBT_IS_LT(kx, kc)
- || ERTS_RBT_IS_EQ(kx, kc));
+ ERTS_RBT_ASSERT(!ERTS_RBT_IS_GT__(kx, kc));
+
/* Go down tree of x's sibling... */
x = c;
break;
@@ -1707,6 +1850,7 @@ ERTS_RBT_FUNC__(hdbg_check_tree)(ERTS_RBT_T *root, ERTS_RBT_T *n)
#undef ERTS_RBT_NEED_FOREACH_ORDERED__
#undef ERTS_RBT_NEED_HDBG_CHECK_TREE__
#undef ERTS_RBT_HDBG_CHECK_TREE__
+#undef ERTS_RBT_IS_GT__
#ifdef ERTS_RBT_UNDEF
# undef ERTS_RBT_PREFIX
@@ -1727,6 +1871,7 @@ ERTS_RBT_FUNC__(hdbg_check_tree)(ERTS_RBT_T *root, ERTS_RBT_T *n)
# undef ERTS_RBT_GET_LEFT
# undef ERTS_RBT_SET_LEFT
# undef ERTS_RBT_GET_KEY
+# undef ERTS_RBT_CMP_KEYS
# undef ERTS_RBT_IS_LT
# undef ERTS_RBT_IS_EQ
# undef ERTS_RBT_UNDEF
diff --git a/erts/emulator/beam/erl_sched_spec_pre_alloc.c b/erts/emulator/beam/erl_sched_spec_pre_alloc.c
index ab204303d7..4a6e02281a 100644
--- a/erts/emulator/beam/erl_sched_spec_pre_alloc.c
+++ b/erts/emulator/beam/erl_sched_spec_pre_alloc.c
@@ -47,6 +47,15 @@ erts_sspa_create(size_t blk_sz, int pa_size, int nthreads, const char* name)
int cix;
int no_blocks = pa_size;
int no_blocks_per_chunk;
+ size_t aligned_blk_sz;
+
+#if !defined(ERTS_STRUCTURE_ALIGNED_ALLOC)
+ /* Force 64-bit alignment... */
+ aligned_blk_sz = ((blk_sz - 1) / 8) * 8 + 8;
+#else
+ /* Alignment of structure is enough... */
+ aligned_blk_sz = blk_sz;
+#endif
if (!name) { /* schedulers only variant */
ASSERT(!nthreads);
@@ -68,7 +77,7 @@ erts_sspa_create(size_t blk_sz, int pa_size, int nthreads, const char* name)
}
no_blocks = no_blocks_per_chunk * nthreads;
chunk_mem_size = ERTS_ALC_CACHE_LINE_ALIGN_SIZE(sizeof(erts_sspa_chunk_header_t));
- chunk_mem_size += blk_sz * no_blocks_per_chunk;
+ chunk_mem_size += aligned_blk_sz * no_blocks_per_chunk;
chunk_mem_size = ERTS_ALC_CACHE_LINE_ALIGN_SIZE(chunk_mem_size);
tot_size = ERTS_ALC_CACHE_LINE_ALIGN_SIZE(sizeof(erts_sspa_data_t));
tot_size += chunk_mem_size * nthreads;
@@ -115,7 +124,7 @@ erts_sspa_create(size_t blk_sz, int pa_size, int nthreads, const char* name)
blk = (erts_sspa_blk_t *) p;
for (i = 0; i < no_blocks_per_chunk; i++) {
blk = (erts_sspa_blk_t *) p;
- p += blk_sz;
+ p += aligned_blk_sz;
blk->next_ptr = (erts_sspa_blk_t *) p;
}
diff --git a/erts/emulator/beam/erl_term.h b/erts/emulator/beam/erl_term.h
index 7d42d1b396..18483fca35 100644
--- a/erts/emulator/beam/erl_term.h
+++ b/erts/emulator/beam/erl_term.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2000-2017. All Rights Reserved.
+ * Copyright Ericsson AB 2000-2018. 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.
@@ -313,7 +313,8 @@ _ET_DECLARE_CHECKED(Uint,header_arity,Eterm)
#define MAX_ARITYVAL ((((Uint)1) << 24) - 1)
#define ERTS_MAX_TUPLE_SIZE MAX_ARITYVAL
-#define make_arityval(sz) _make_header((sz),_TAG_HEADER_ARITYVAL)
+#define make_arityval(sz) (ASSERT((sz) <= MAX_ARITYVAL), \
+ _make_header((sz),_TAG_HEADER_ARITYVAL))
#define is_arity_value(x) (((x) & _TAG_HEADER_MASK) == _TAG_HEADER_ARITYVAL)
#define is_sane_arity_value(x) ((((x) & _TAG_HEADER_MASK) == _TAG_HEADER_ARITYVAL) && \
(((x) >> _HEADER_ARITY_OFFS) <= MAX_ARITYVAL))
@@ -344,6 +345,9 @@ _ET_DECLARE_CHECKED(Uint,thing_subtag,Eterm)
*
* To help find code which makes unwarranted assumptions about zero,
* we now use a non-zero bit-pattern in debug mode.
+ *
+ * In order to be able to differentiata against values, the non-value
+ * needs to be tagged as a header of some sort.
*/
#if ET_DEBUG
# ifdef HIPE
@@ -354,7 +358,7 @@ _ET_DECLARE_CHECKED(Uint,thing_subtag,Eterm)
# define THE_NON_VALUE _make_header(0,_TAG_HEADER_FLOAT)
# endif
#else
-#define THE_NON_VALUE (0)
+#define THE_NON_VALUE (TAG_PRIMARY_HEADER)
#endif
#define is_non_value(x) ((x) == THE_NON_VALUE)
#define is_value(x) ((x) != THE_NON_VALUE)
diff --git a/erts/emulator/beam/erl_time.h b/erts/emulator/beam/erl_time.h
index 27164d50a0..968f21fd51 100644
--- a/erts/emulator/beam/erl_time.h
+++ b/erts/emulator/beam/erl_time.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2006-2017. All Rights Reserved.
+ * Copyright Ericsson AB 2006-2018. 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.
@@ -21,6 +21,8 @@
#ifndef ERL_TIME_H__
#define ERL_TIME_H__
+#include "erl_monitor_link.h"
+
#if 0
# define ERTS_TW_DEBUG
#endif
@@ -79,8 +81,8 @@ typedef ErtsMonotonicTime * ErtsNextTimeoutRef;
extern SysTimeval erts_first_emu_time;
-void erts_monitor_time_offset(Eterm id, Eterm ref);
-int erts_demonitor_time_offset(Eterm ref);
+void erts_monitor_time_offset(ErtsMonitor *mon);
+void erts_demonitor_time_offset(ErtsMonitor *mon);
int erts_init_time_sup(int, ErtsTimeWarpMode);
void erts_late_init_time_sup(void);
@@ -107,9 +109,6 @@ void erts_p_slpq(void);
void erts_get_now_cpu(Uint* megasec, Uint* sec, Uint* microsec);
#endif
-typedef UWord erts_approx_time_t;
-erts_approx_time_t erts_get_approx_time(void);
-
int erts_has_time_correction(void);
int erts_check_time_adj_support(int time_correction,
ErtsTimeWarpMode time_warp_mode);
diff --git a/erts/emulator/beam/erl_time_sup.c b/erts/emulator/beam/erl_time_sup.c
index f2e0900fec..e5bb3cc15f 100644
--- a/erts/emulator/beam/erl_time_sup.c
+++ b/erts/emulator/beam/erl_time_sup.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1999-2017. All Rights Reserved.
+ * Copyright Ericsson AB 1999-2018. 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.
@@ -35,6 +35,7 @@
#include "erl_time.h"
#include "erl_driver.h"
#include "erl_nif.h"
+#include "erl_proc_sig_queue.h"
static erts_mtx_t erts_get_time_mtx;
@@ -191,17 +192,6 @@ static struct {
ErtsTimeSupData erts_time_sup__ erts_align_attribute(ERTS_CACHE_LINE_SIZE);
-/*
- * erts_get_approx_time() returns an *approximate* time
- * in seconds. NOTE that this time may jump backwards!!!
- */
-erts_approx_time_t
-erts_get_approx_time(void)
-{
- ErtsSystemTime stime = erts_os_system_time();
- return (erts_approx_time_t) ERTS_MONOTONIC_TO_SEC(stime);
-}
-
static ERTS_INLINE void
init_time_offset(ErtsMonotonicTime offset)
{
@@ -1881,36 +1871,33 @@ void erts_get_now_cpu(Uint* megasec, Uint* sec, Uint* microsec) {
#include "big.h"
void
-erts_monitor_time_offset(Eterm id, Eterm ref)
+erts_monitor_time_offset(ErtsMonitor *mon)
{
erts_mtx_lock(&erts_get_time_mtx);
- erts_add_monitor(&time_offset_monitors, MON_TIME_OFFSET, ref, id, NIL);
+ erts_monitor_list_insert(&time_offset_monitors, mon);
no_time_offset_monitors++;
erts_mtx_unlock(&erts_get_time_mtx);
}
-int
-erts_demonitor_time_offset(Eterm ref)
+void
+erts_demonitor_time_offset(ErtsMonitor *mon)
{
- int res;
- ErtsMonitor *mon;
- ASSERT(is_internal_ref(ref));
+ ErtsMonitorData *mdp = erts_monitor_to_data(mon);
+ ASSERT(erts_monitor_is_origin(mon));
+ ASSERT(mon->type == ERTS_MON_TYPE_TIME_OFFSET);
+
erts_mtx_lock(&erts_get_time_mtx);
- if (is_internal_ordinary_ref(ref))
- mon = erts_remove_monitor(&time_offset_monitors, ref);
- else
- mon = NULL;
- if (!mon)
- res = 0;
- else {
- ASSERT(no_time_offset_monitors > 0);
- no_time_offset_monitors--;
- res = 1;
- }
+
+ ASSERT(erts_monitor_is_in_table(&mdp->target));
+
+ erts_monitor_list_delete(&time_offset_monitors, &mdp->target);
+
+ ASSERT(no_time_offset_monitors > 0);
+ no_time_offset_monitors--;
+
erts_mtx_unlock(&erts_get_time_mtx);
- if (res)
- erts_destroy_monitor(mon);
- return res;
+
+ erts_monitor_release_both(mdp);
}
typedef struct {
@@ -1928,17 +1915,19 @@ static void
save_time_offset_monitor(ErtsMonitor *mon, void *vcntxt)
{
ErtsTimeOffsetMonitorContext *cntxt;
+ ErtsMonitorData *mdp = erts_monitor_to_data(mon);
Eterm *from_hp, *to_hp;
Uint mix;
int hix;
cntxt = (ErtsTimeOffsetMonitorContext *) vcntxt;
mix = (cntxt->ix)++;
- cntxt->to_mon_info[mix].pid = mon->u.pid;
+ ASSERT(is_internal_pid(mon->other.item));
+ cntxt->to_mon_info[mix].pid = mon->other.item;
to_hp = &cntxt->to_mon_info[mix].heap[0];
- ASSERT(is_internal_ordinary_ref(mon->ref));
- from_hp = internal_ref_val(mon->ref);
+ ASSERT(is_internal_ordinary_ref(mdp->ref));
+ from_hp = internal_ref_val(mdp->ref);
ASSERT(thing_arityval(*from_hp) + 1 == ERTS_REF_THING_SIZE);
for (hix = 0; hix < ERTS_REF_THING_SIZE; hix++)
@@ -1983,9 +1972,9 @@ send_time_offset_changed_notifications(void *new_offsetp)
cntxt.ix = 0;
cntxt.to_mon_info = to_mon_info;
- erts_doforall_monitors(time_offset_monitors,
- save_time_offset_monitor,
- &cntxt);
+ erts_monitor_list_foreach(time_offset_monitors,
+ save_time_offset_monitor,
+ &cntxt);
ASSERT(cntxt.ix == no_monitors);
}
@@ -2019,26 +2008,14 @@ send_time_offset_changed_notifications(void *new_offsetp)
ASSERT(*patch_refp == THE_NON_VALUE);
for (mix = 0; mix < no_monitors; mix++) {
- Process *rp = erts_proc_lookup(to_mon_info[mix].pid);
- if (rp) {
- Eterm ref = to_mon_info[mix].ref;
- ErtsProcLocks rp_locks = ERTS_PROC_LOCK_LINK;
- erts_proc_lock(rp, ERTS_PROC_LOCK_LINK);
- if (erts_lookup_monitor(ERTS_P_MONITORS(rp), ref)) {
- ErtsMessage *mp;
- ErlOffHeap *ohp;
- Eterm message;
-
- mp = erts_alloc_message_heap(rp, &rp_locks,
- hsz, &hp, &ohp);
- *patch_refp = ref;
- ASSERT(hsz == size_object(message_template));
- message = copy_struct(message_template, hsz, &hp, ohp);
- erts_queue_message(rp, rp_locks, mp, message, am_clock_service);
- }
- erts_proc_unlock(rp, rp_locks);
- }
- }
+ *patch_refp = to_mon_info[mix].ref;
+ erts_proc_sig_send_persistent_monitor_msg(ERTS_MON_TYPE_TIME_OFFSET,
+ *patch_refp,
+ am_clock_service,
+ to_mon_info[mix].pid,
+ message_template,
+ hsz);
+ }
erts_free(ERTS_ALC_T_TMP, tmp);
}
diff --git a/erts/emulator/beam/erl_trace.c b/erts/emulator/beam/erl_trace.c
index 4b996d8fc2..018894f685 100644
--- a/erts/emulator/beam/erl_trace.c
+++ b/erts/emulator/beam/erl_trace.c
@@ -2533,7 +2533,7 @@ load_tracer_nif(const ErtsTracer tracer)
for(i = 0; i < num_of_funcs; i++) {
for (j = 0; j < NIF_TRACER_TYPES; j++) {
- if (strcmp(tracers[j].name, funcs[i].name) == 0 && tracers[j].arity == funcs[i].arity) {
+ if (sys_strcmp(tracers[j].name, funcs[i].name) == 0 && tracers[j].arity == funcs[i].arity) {
tracers[j].cb = &(funcs[i]);
break;
}
@@ -2783,24 +2783,28 @@ is_tracer_enabled(Process* c_p, ErtsProcLocks c_p_locks,
ASSERT(0);
}
- /* Only remove tracer on self() and ports */
+ /* Only remove tracer on (self() or ports) AND we are on a normal scheduler */
if (is_internal_port(t_p->id) || (c_p && c_p->common.id == t_p->id)) {
+ ErtsSchedulerData *esdp = erts_get_scheduler_data();
ErtsProcLocks c_p_xlocks = 0;
- if (is_internal_pid(t_p->id)) {
- ERTS_LC_ASSERT(erts_proc_lc_my_proc_locks(c_p) & ERTS_PROC_LOCK_MAIN);
- if (c_p_locks != ERTS_PROC_LOCKS_ALL) {
- c_p_xlocks = ~c_p_locks & ERTS_PROC_LOCKS_ALL;
- if (erts_proc_trylock(c_p, c_p_xlocks) == EBUSY) {
- erts_proc_unlock(c_p, c_p_locks & ~ERTS_PROC_LOCK_MAIN);
- erts_proc_lock(c_p, ERTS_PROC_LOCKS_ALL_MINOR);
+ if (esdp && !ERTS_SCHEDULER_IS_DIRTY(esdp)) {
+ if (is_internal_pid(t_p->id)) {
+ ERTS_LC_ASSERT(erts_proc_lc_my_proc_locks(c_p) & ERTS_PROC_LOCK_MAIN);
+ if (c_p_locks != ERTS_PROC_LOCKS_ALL) {
+ c_p_xlocks = ~c_p_locks & ERTS_PROC_LOCKS_ALL;
+ if (erts_proc_trylock(c_p, c_p_xlocks) == EBUSY) {
+ erts_proc_unlock(c_p, c_p_locks & ~ERTS_PROC_LOCK_MAIN);
+ erts_proc_lock(c_p, ERTS_PROC_LOCKS_ALL_MINOR);
+ }
}
}
- }
- erts_tracer_replace(t_p, erts_tracer_nil);
- t_p->trace_flags &= ~TRACEE_FLAGS;
- if (c_p_xlocks)
- erts_proc_unlock(c_p, c_p_xlocks);
+ erts_tracer_replace(t_p, erts_tracer_nil);
+ t_p->trace_flags &= ~TRACEE_FLAGS;
+
+ if (c_p_xlocks)
+ erts_proc_unlock(c_p, c_p_xlocks);
+ }
}
return 0;
@@ -3015,7 +3019,7 @@ static void *tracer_alloc_fun(void* tmpl)
ErtsTracerNif *obj = erts_alloc(ERTS_ALC_T_TRACER_NIF,
sizeof(ErtsTracerNif) +
sizeof(ErtsThrPrgrLaterOp));
- memcpy(obj, tmpl, sizeof(*obj));
+ sys_memcpy(obj, tmpl, sizeof(*obj));
return obj;
}
diff --git a/erts/emulator/beam/erl_unicode.c b/erts/emulator/beam/erl_unicode.c
index 604f0be127..8673e029e6 100644
--- a/erts/emulator/beam/erl_unicode.c
+++ b/erts/emulator/beam/erl_unicode.c
@@ -144,7 +144,7 @@ static Eterm make_magic_bin_for_restart(Process *p, RestartContext *rc)
cleanup_restart_context_bin);
RestartContext *restartp = ERTS_MAGIC_BIN_DATA(mbp);
Eterm *hp;
- memcpy(restartp,rc,sizeof(RestartContext));
+ sys_memcpy(restartp,rc,sizeof(RestartContext));
hp = HAlloc(p, ERTS_MAGIC_REF_THING_SIZE);
return erts_mk_magic_ref(&hp, &MSO(p), mbp);
}
@@ -254,12 +254,12 @@ static Uint copy_utf8_bin(byte *target, byte *source, Uint size,
ASSERT(need > 0);
ASSERT(from_source > 0);
if (size < from_source) {
- memcpy(leftover + (*num_leftovers), source, size);
+ sys_memcpy(leftover + (*num_leftovers), source, size);
*num_leftovers += size;
return 0;
}
/* leftover has room for four bytes (see bif) */
- memcpy(leftover + (*num_leftovers),source,from_source);
+ sys_memcpy(leftover + (*num_leftovers),source,from_source);
c = copy_utf8_bin(target, leftover, need, NULL, NULL, &tmp_err_pos, characters);
if (tmp_err_pos != 0) {
*err_pos = source;
@@ -291,7 +291,7 @@ static Uint copy_utf8_bin(byte *target, byte *source, Uint size,
size -= 2; copied += 2;
} else if (((*source) & ((byte) 0xF0)) == 0xE0) {
if (leftover && size < 3) {
- memcpy(leftover, source, (int) size);
+ sys_memcpy(leftover, source, (int) size);
*num_leftovers = (int) size;
break;
}
@@ -313,7 +313,7 @@ static Uint copy_utf8_bin(byte *target, byte *source, Uint size,
size -= 3; copied += 3;
} else if (((*source) & ((byte) 0xF8)) == 0xF0) {
if (leftover && size < 4) {
- memcpy(leftover, source, (int) size);
+ sys_memcpy(leftover, source, (int) size);
*num_leftovers = (int) size;
break;
}
@@ -2108,7 +2108,7 @@ char *erts_convert_filename_to_encoding(Eterm name, char *statbuf, size_t statbu
} else {
name_buf = statbuf;
}
- memcpy(name_buf,bytes,size);
+ sys_memcpy(name_buf,bytes,size);
name_buf[size]=0;
} else {
name_buf = erts_convert_filename_to_wchar(bytes, size,
diff --git a/erts/emulator/beam/erl_utils.h b/erts/emulator/beam/erl_utils.h
index 44d8c85867..e4087e0ac8 100644
--- a/erts/emulator/beam/erl_utils.h
+++ b/erts/emulator/beam/erl_utils.h
@@ -91,7 +91,7 @@ Eterm erts_bld_tuple(Uint **hpp, Uint *szp, Uint arity, ...);
#define erts_bld_tuple5(H,S,E1,E2,E3,E4,E5) erts_bld_tuple(H,S,5,E1,E2,E3,E4,E5)
Eterm erts_bld_tuplev(Uint **hpp, Uint *szp, Uint arity, Eterm terms[]);
Eterm erts_bld_string_n(Uint **hpp, Uint *szp, const char *str, Sint len);
-#define erts_bld_string(hpp,szp,str) erts_bld_string_n(hpp,szp,str,strlen(str))
+#define erts_bld_string(hpp,szp,str) erts_bld_string_n(hpp,szp,str,sys_strlen(str))
Eterm erts_bld_list(Uint **hpp, Uint *szp, Sint length, Eterm terms[]);
Eterm erts_bld_2tup_list(Uint **hpp, Uint *szp,
Sint length, Eterm terms1[], Uint terms2[]);
diff --git a/erts/emulator/beam/erl_vm.h b/erts/emulator/beam/erl_vm.h
index 76980b5871..f8391fb665 100644
--- a/erts/emulator/beam/erl_vm.h
+++ b/erts/emulator/beam/erl_vm.h
@@ -69,7 +69,7 @@
# ifdef CHECK_FOR_HOLES
# define INIT_HEAP_MEM(p,sz) erts_set_hole_marker(HEAP_TOP(p), (sz))
# else
-# define INIT_HEAP_MEM(p,sz) memset(HEAP_TOP(p),0x01,(sz)*sizeof(Eterm*))
+# define INIT_HEAP_MEM(p,sz) sys_memset(HEAP_TOP(p),0x01,(sz)*sizeof(Eterm*))
# endif
#else
# define INIT_HEAP_MEM(p,sz) ((void)0)
diff --git a/erts/emulator/beam/external.c b/erts/emulator/beam/external.c
index b12a021e41..fb42969a8f 100644
--- a/erts/emulator/beam/external.c
+++ b/erts/emulator/beam/external.c
@@ -711,8 +711,8 @@ erts_prepare_dist_ext(ErtsDistExternal *edep,
erts_de_rlock(dep);
- if (dep->status != ERTS_DE_SFLG_CONNECTED &&
- dep->status != ERTS_DE_SFLG_PENDING) {
+ if (dep->state != ERTS_DE_STATE_CONNECTED &&
+ dep->state != ERTS_DE_STATE_PENDING) {
erts_de_runlock(dep);
return ERTS_PREP_DIST_EXT_CLOSED;
}
@@ -1953,7 +1953,7 @@ static Eterm erts_term_to_binary_int(Process* p, Eterm Term, int level, Uint fla
context_b = erts_create_magic_binary(sizeof(TTBContext), \
ttb_context_destructor); \
context = ERTS_MAGIC_BIN_DATA(context_b); \
- memcpy(context,&c_buff,sizeof(TTBContext)); \
+ sys_memcpy(context,&c_buff,sizeof(TTBContext)); \
} \
} while (0)
@@ -2873,6 +2873,8 @@ enc_term_int(TTBEncodeContext* ctx, ErtsAtomCacheMap *acmp, Eterm obj, byte* ep,
ep[j] = 0; /* Zero unused bits at end of binary */
data_dst = ep;
ep += j + 1;
+ if (ctx)
+ ctx->hopefull_flags |= DFLAG_BIT_BINARIES;
} else {
/*
* Bit-level binary, but the receiver doesn't support it.
@@ -2908,6 +2910,8 @@ enc_term_int(TTBEncodeContext* ctx, ErtsAtomCacheMap *acmp, Eterm obj, byte* ep,
ep = enc_atom(acmp, exp->info.mfa.function, ep, dflags);
ep = enc_term(acmp, make_small(exp->info.mfa.arity),
ep, dflags, off_heap);
+ if (ctx)
+ ctx->hopefull_flags |= DFLAG_EXPORT_PTR_TAG;
} else {
/* Tag, arity */
*ep++ = SMALL_TUPLE_EXT;
@@ -4729,11 +4733,13 @@ Sint transcode_dist_obuf(ErtsDistOutputBuf* ob,
struct transcode_context* ctx = dep->transcode_ctx;
if (!ctx) { /* first call for 'ob' */
-
- if (~dflags & (DFLAG_BIT_BINARIES | DFLAG_EXPORT_PTR_TAG)) {
+ ASSERT(!(ob->hopefull_flags & ~(Uint)(DFLAG_BIT_BINARIES |
+ DFLAG_EXPORT_PTR_TAG)));
+ if (~dflags & ob->hopefull_flags) {
/*
- * Receiver does not support bitstrings and/or export funs.
- * We need to transcode control and message terms to use tuple fallbacks.
+ * Receiver does not support bitstrings and/or export funs
+ * and output buffer contains such message tags (hopefull_flags).
+ * Must transcode control and message terms to use tuple fallbacks.
*/
ctx = erts_alloc(ERTS_ALC_T_DIST_TRANSCODE, sizeof(struct transcode_context));
dep->transcode_ctx = ctx;
@@ -4760,7 +4766,7 @@ Sint transcode_dist_obuf(ErtsDistOutputBuf* ob,
ctx->state = TRANSCODE_ENC_CTL;
}
}
- else {
+ else if (!(dflags & DFLAG_DIST_HDR_ATOM_CACHE)) {
/*
* No need for full transcoding, but primitive receiver (erl_/jinterface)
* expects VERSION_MAGIC before both control and message terms.
@@ -4777,8 +4783,10 @@ Sint transcode_dist_obuf(ErtsDistOutputBuf* ob,
*--(ob->extp) = VERSION_MAGIC;
goto done;
}
+ else
+ goto done;
}
- else {
+ else { /* continue after yield */
ASSERT(ctx->dbg_ob == ob);
}
ctx->b2t.reds = reds * B2T_BYTES_PER_REDUCTION;
@@ -4831,6 +4839,7 @@ Sint transcode_dist_obuf(ErtsDistOutputBuf* ob,
ob->msg_start = ob->ext_endp;
ctx->ttb.wstack.wstart = NULL;
ctx->ttb.flags = dflags;
+ ctx->ttb.hopefull_flags = 0;
ctx->ttb.level = 0;
ctx->state = TRANSCODE_ENC_MSG;
@@ -4843,7 +4852,7 @@ Sint transcode_dist_obuf(ErtsDistOutputBuf* ob,
reds /= TERM_TO_BINARY_LOOP_FACTOR;
ASSERT(ob->ext_endp <= ob->alloc_endp);
-
+ ASSERT(!ctx->ttb.hopefull_flags);
}
transcode_free_ctx(dep);
diff --git a/erts/emulator/beam/external.h b/erts/emulator/beam/external.h
index f9f8abcc27..bbd9b4bad2 100644
--- a/erts/emulator/beam/external.h
+++ b/erts/emulator/beam/external.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1996-2016. All Rights Reserved.
+ * Copyright Ericsson AB 1996-2018. 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.
@@ -83,7 +83,10 @@
#ifndef ERL_EXTERNAL_H__
#define ERL_EXTERNAL_H__
+#define ERL_NODE_TABLES_BASIC_ONLY
#include "erl_node_tables.h"
+#undef ERL_NODE_TABLES_BASIC_ONLY
+#include "erl_alloc.h"
#define ERTS_ATOM_CACHE_SIZE 2048
diff --git a/erts/emulator/beam/global.h b/erts/emulator/beam/global.h
index 86e2c351af..d853b2e352 100644
--- a/erts/emulator/beam/global.h
+++ b/erts/emulator/beam/global.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1996-2017. All Rights Reserved.
+ * Copyright Ericsson AB 1996-2018. 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.
@@ -87,9 +87,7 @@ typedef struct
{
erts_mtx_t lock;
ErtsMonitor* root;
- int pending_failed_fire;
- int is_dying;
-
+ Uint refc;
size_t user_data_sz;
} ErtsResourceMonitors;
@@ -116,7 +114,8 @@ 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);
extern void erts_resource_stop(ErtsResource*, ErlNifEvent, int is_direct_call);
-void erts_fire_nif_monitor(ErtsResource*, Eterm pid, Eterm ref);
+void erts_fire_nif_monitor(ErtsMonitor *tmon);
+void erts_nif_demonitored(ErtsResource* resource);
extern Eterm erts_nif_taints(Process* p);
extern void erts_print_nif_taints(fmtfn_t to, void* to_arg);
void erts_unload_nif(struct erl_module_nif* nif);
@@ -368,7 +367,7 @@ do {\
UWord _wsz = ESTACK_COUNT(s);\
(dst)->start = erts_alloc((s).alloc_type,\
DEF_ESTACK_SIZE * sizeof(Eterm));\
- memcpy((dst)->start, (s).start,_wsz*sizeof(Eterm));\
+ sys_memcpy((dst)->start, (s).start,_wsz*sizeof(Eterm));\
(dst)->sp = (dst)->start + _wsz;\
(dst)->end = (dst)->start + DEF_ESTACK_SIZE;\
(dst)->edefault = NULL;\
@@ -536,7 +535,7 @@ do {\
UWord _wsz = WSTACK_COUNT(s);\
(dst)->wstart = erts_alloc(s.alloc_type,\
DEF_WSTACK_SIZE * sizeof(UWord));\
- memcpy((dst)->wstart, s.wstart,_wsz*sizeof(UWord));\
+ sys_memcpy((dst)->wstart, s.wstart,_wsz*sizeof(UWord));\
(dst)->wsp = (dst)->wstart + _wsz;\
(dst)->wend = (dst)->wstart + DEF_WSTACK_SIZE;\
(dst)->wdefault = NULL;\
@@ -886,6 +885,7 @@ void erts_init_trap_export(Export* ep, Eterm m, Eterm f, Uint a,
Eterm (*bif)(Process*, Eterm*, BeamInstr*));
void erts_init_bif(void);
Eterm erl_send(Process *p, Eterm to, Eterm msg);
+int erts_set_group_leader(Process *proc, Eterm new_gl);
/* erl_bif_op.c */
@@ -908,7 +908,6 @@ extern erts_atomic_t erts_copy_literal_area__;
#define ERTS_COPY_LITERAL_AREA() \
((ErtsLiteralArea *) erts_atomic_read_nob(&erts_copy_literal_area__))
extern Process *erts_literal_area_collector;
-extern Process *erts_dirty_process_code_checker;
extern Process *erts_code_purger;
@@ -948,8 +947,8 @@ void erts_update_ranges(BeamInstr* code, Uint size);
void erts_remove_from_ranges(BeamInstr* code);
UWord erts_ranges_sz(void);
void erts_lookup_function_info(FunctionInfo* fi, BeamInstr* pc, int full_info);
-ErtsLiteralArea** erts_dump_lit_areas;
-Uint erts_dump_num_lit_areas;
+extern ErtsLiteralArea** erts_dump_lit_areas;
+extern Uint erts_dump_num_lit_areas;
/* break.c */
void init_break_handler(void);
@@ -1066,13 +1065,13 @@ Eterm copy_struct_x(Eterm, Uint, Eterm**, ErlOffHeap*, Uint*, erts_literal_area_
#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*);
+Eterm copy_shallow(Eterm* ERTS_RESTRICT, Uint, Eterm**, ErlOffHeap*);
void erts_move_multi_frags(Eterm** hpp, ErlOffHeap*, ErlHeapFragment* first,
Eterm* refs, unsigned nrefs, int literals);
/* Utilities */
-extern void erts_delete_nodes_monitors(Process *, ErtsProcLocks);
+void erts_monitor_nodes_delete(ErtsMonitor *);
extern Eterm erts_monitor_nodes(Process *, Eterm, Eterm);
extern Eterm erts_processes_monitoring_nodes(Process *);
extern int erts_do_net_exits(DistEntry*, Eterm);
diff --git a/erts/emulator/beam/hash.c b/erts/emulator/beam/hash.c
index 8548e30e8b..6a31489473 100644
--- a/erts/emulator/beam/hash.c
+++ b/erts/emulator/beam/hash.c
@@ -152,7 +152,7 @@ Hash* hash_init(int type, Hash* h, char* name, int size, HashFunctions fun)
h->bucket = (HashBucket**) fun.meta_alloc(h->meta_alloc_type, sz);
- sys_memzero(h->bucket, sz);
+ memzero(h->bucket, sz);
h->is_allocated = 0;
h->name = name;
h->fun = fun;
@@ -224,7 +224,7 @@ static void rehash(Hash* h, int grow)
sz = h->size*sizeof(HashBucket*);
new_bucket = (HashBucket **) h->fun.meta_alloc(h->meta_alloc_type, sz);
- sys_memzero(new_bucket, sz);
+ memzero(new_bucket, sz);
for (i = 0; i < old_size; i++) {
HashBucket* b = h->bucket[i];
diff --git a/erts/emulator/beam/instrs.tab b/erts/emulator/beam/instrs.tab
index f19027b1ec..6a531fcc09 100644
--- a/erts/emulator/beam/instrs.tab
+++ b/erts/emulator/beam/instrs.tab
@@ -322,6 +322,16 @@ get_list(Src, Hd, Tl) {
$Tl = tl;
}
+get_hd(Src, Hd) {
+ Eterm* tmp_ptr = list_val($Src);
+ $Hd = CAR(tmp_ptr);
+}
+
+get_tl(Src, Tl) {
+ Eterm* tmp_ptr = list_val($Src);
+ $Tl = CDR(tmp_ptr);
+}
+
i_get(Src, Dst) {
$Dst = erts_pd_hash_get(c_p, $Src);
}
diff --git a/erts/emulator/beam/io.c b/erts/emulator/beam/io.c
index 2c1b7871c4..9f87285b71 100644
--- a/erts/emulator/beam/io.c
+++ b/erts/emulator/beam/io.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1996-2017. All Rights Reserved.
+ * Copyright Ericsson AB 1996-2018. 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.
@@ -53,6 +53,7 @@
#include "erl_hl_timer.h"
#include "erl_time.h"
#include "erl_io_queue.h"
+#include "erl_proc_sig_queue.h"
extern ErlDrvEntry fd_driver_entry;
extern ErlDrvEntry vanilla_driver_entry;
@@ -298,7 +299,6 @@ static Port *create_port(char *name,
erts_aint32_t state = ERTS_PORT_SFLG_CONNECTED;
erts_aint32_t x_pts_flgs = 0;
- ErtsRunQueue *runq;
if (!driver_lock) {
/* Align size for mutex following port struct */
port_size = size = ERTS_ALC_DATA_ALIGN_SIZE(sizeof(Port));
@@ -347,11 +347,16 @@ static Port *create_port(char *name,
p += sizeof(erts_mtx_t);
state |= ERTS_PORT_SFLG_PORT_SPECIFIC_LOCK;
}
- if (erts_get_scheduler_data())
- runq = erts_get_runq_current(NULL);
- else
- runq = ERTS_RUNQ_IX(0);
- erts_atomic_set_nob(&prt->run_queue, (erts_aint_t) runq);
+
+ {
+ ErtsRunQueue *runq;
+ ErtsSchedulerData *esdp = erts_get_scheduler_data();
+ if (esdp)
+ runq = erts_get_runq_current(esdp);
+ else
+ runq = ERTS_RUNQ_IX(0);
+ erts_init_runq_port(prt, runq);
+ }
prt->xports = NULL;
@@ -362,6 +367,7 @@ static Port *create_port(char *name,
prt->drv_ptr = driver;
ERTS_P_LINKS(prt) = NULL;
ERTS_P_MONITORS(prt) = NULL;
+ ERTS_P_LT_MONITORS(prt) = NULL;
prt->linebuf = NULL;
prt->suspended = NULL;
erts_init_port_data(prt);
@@ -584,7 +590,7 @@ erts_open_driver(erts_driver_t* driver, /* Pointer to driver. */
*/
for (d = driver_list; d; d = d->next) {
- if (strcmp(d->name, name) == 0 &&
+ if (sys_strcmp(d->name, name) == 0 &&
erts_ddll_driver_ok(d->handle)) {
driver = d;
break;
@@ -634,7 +640,7 @@ erts_open_driver(erts_driver_t* driver, /* Pointer to driver. */
trace_port_open(port,
pid,
erts_atom_put((byte *) port->name,
- strlen(port->name),
+ sys_strlen(port->name),
ERTS_ATOM_ENC_LATIN1,
1));
}
@@ -744,8 +750,8 @@ driver_create_port(ErlDrvPort creator_port_ix, /* Creating port */
Port *creator_port;
Port* port;
erts_driver_t *driver;
- Process *rp;
erts_mtx_t *driver_lock = NULL;
+ ErtsLinkData *ldp;
ERTS_CHK_NO_PROC_LOCKS;
@@ -757,17 +763,13 @@ driver_create_port(ErlDrvPort creator_port_ix, /* Creating port */
if (creator_port == ERTS_INVALID_ERL_DRV_PORT)
return ERTS_INVALID_ERL_DRV_PORT;
- rp = erts_proc_lookup(pid);
- if (!rp)
- return ERTS_INVALID_ERL_DRV_PORT;
-
ERTS_LC_ASSERT(erts_lc_is_port_locked(creator_port));
driver = creator_port->drv_ptr;
erts_rwmtx_rlock(&erts_driver_list_lock);
if (!erts_ddll_driver_ok(driver->handle)) {
erts_rwmtx_runlock(&erts_driver_list_lock);
- return ERTS_INVALID_ERL_DRV_PORT;
+ return ERTS_INVALID_ERL_DRV_PORT;
}
if (driver->handle != NULL) {
@@ -795,9 +797,15 @@ driver_create_port(ErlDrvPort creator_port_ix, /* Creating port */
}
ERTS_LC_ASSERT(erts_lc_is_port_locked(port));
- erts_proc_lock(rp, ERTS_PROC_LOCK_LINK);
- if (ERTS_PROC_IS_EXITING(rp)) {
- erts_proc_unlock(rp, ERTS_PROC_LOCK_LINK);
+ ldp = erts_link_create(ERTS_LNK_TYPE_PORT,
+ port->common.id, pid);
+ ASSERT(ldp->a.other.item == pid);
+ ASSERT(ldp->b.other.item == port->common.id);
+ erts_link_tree_insert(&ERTS_P_LINKS(port), &ldp->a);
+
+ if (!erts_proc_sig_send_link(NULL, pid, &ldp->b)) {
+ erts_link_tree_delete(&ERTS_P_LINKS(port), &ldp->a);
+ erts_link_release_both(ldp);
if (driver->handle) {
erts_rwmtx_rlock(&erts_driver_list_lock);
erts_ddll_decrement_port_count(driver->handle);
@@ -808,10 +816,6 @@ driver_create_port(ErlDrvPort creator_port_ix, /* Creating port */
return ERTS_INVALID_ERL_DRV_PORT;
}
- erts_add_link(&ERTS_P_LINKS(port), LINK_PID, pid);
- erts_add_link(&ERTS_P_LINKS(rp), LINK_PID, port->common.id);
- erts_proc_unlock(rp, ERTS_PROC_LOCK_LINK);
-
if (!driver_lock) {
ErtsXPortsList *xplp = xports_list_alloc();
xplp->port = port;
@@ -1163,21 +1167,10 @@ erts_schedule_proc2port_signal(Process *c_p,
* otherwise, next receive will *not* work
* as expected!
*/
- erts_proc_lock(c_p, ERTS_PROC_LOCKS_MSG_RECEIVE);
-
- if (ERTS_PROC_PENDING_EXIT(c_p)) {
- /* need to exit caller instead */
- erts_proc_unlock(c_p, ERTS_PROC_LOCKS_MSG_RECEIVE);
- KILL_CATCHES(c_p);
- c_p->freason = EXC_EXIT;
- return ERTS_PORT_OP_CALLER_EXIT;
- }
- ERTS_MSGQ_MV_INQ2PRIVQ(c_p);
- c_p->msg.save = c_p->msg.last;
-
- erts_proc_unlock(c_p, (ERTS_PROC_LOCKS_MSG_RECEIVE
- | ERTS_PROC_LOCK_MAIN));
+ ERTS_RECV_MARK_SAVE(c_p);
+ ERTS_RECV_MARK_SET(c_p);
+ erts_proc_unlock(c_p, ERTS_PROC_LOCK_MAIN);
}
@@ -1235,29 +1228,12 @@ erts_schedule_port2port_signal(Eterm port_num, ErtsProc2PortSigData *sigdp,
static ERTS_INLINE void
send_badsig(Port *prt) {
- ErtsProcLocks rp_locks = ERTS_PROC_LOCKS_XSIG_SEND;
- Process* rp;
Eterm connected = ERTS_PORT_GET_CONNECTED(prt);
ERTS_CHK_NO_PROC_LOCKS;
ERTS_LC_ASSERT(erts_get_scheduler_id());
-
ASSERT(is_internal_pid(connected));
-
- rp = erts_proc_lookup_raw(connected);
- if (rp) {
- erts_proc_lock(rp, rp_locks);
- if (!ERTS_PROC_IS_EXITING(rp))
- (void) erts_send_exit_signal(NULL,
- prt->common.id,
- rp,
- &rp_locks,
- am_badsig,
- NIL,
- NULL,
- 0);
- if (rp_locks)
- erts_proc_unlock(rp, rp_locks);
- } /* exit sent */
+ erts_proc_sig_send_exit(NULL, prt->common.id, connected,
+ am_badsig, NIL, 0);
} /* send_badsig */
static void
@@ -2190,11 +2166,11 @@ call_deliver_port_exit(int bang_op,
}
if (broken_link) {
- ErtsLink *lnk = erts_remove_link(&ERTS_P_LINKS(prt), from);
- if (lnk)
- erts_destroy_link(lnk);
- else
+ ErtsLink *lnk = erts_link_tree_lookup(ERTS_P_LINKS(prt), from);
+ if (!lnk)
return ERTS_PORT_OP_DROPPED;
+ erts_link_tree_delete(&ERTS_P_LINKS(prt), lnk);
+ erts_link_release(lnk);
}
if (IS_TRACED_FL(prt, F_TRACE_RECEIVE))
@@ -2363,27 +2339,45 @@ set_port_connected(int bang_op,
#endif
}
else { /* Port BIF operation */
- Process *rp = erts_proc_lookup_raw(connect);
- if (!rp)
- return ERTS_PORT_OP_DROPPED;
- erts_proc_lock(rp, ERTS_PROC_LOCK_LINK);
- if (ERTS_PROC_IS_EXITING(rp)) {
- erts_proc_unlock(rp, ERTS_PROC_LOCK_LINK);
- return ERTS_PORT_OP_DROPPED;
- }
+ int created;
+ ErtsLink *lnk;
+
+ if (is_not_internal_pid(connect))
+ return ERTS_PORT_OP_DROPPED;
+
+ lnk = erts_link_tree_lookup_create(&ERTS_P_LINKS(prt), &created,
+ ERTS_LNK_TYPE_PORT, prt->common.id,
+ connect);
+ if (created) {
+ ErtsLinkData *ldp;
+ ErtsLink *olnk = erts_link_to_other(lnk, &ldp);
+ ASSERT(olnk->other.item == prt->common.id);
+ if (!erts_proc_sig_send_link(NULL, connect, olnk)) {
+ erts_link_tree_delete(&ERTS_P_LINKS(prt), lnk);
+ erts_link_release_both(ldp);
+ return ERTS_PORT_OP_DROPPED;
+ }
+ if (IS_TRACED_FL(prt, F_TRACE_PORTS))
+ trace_port(prt, am_getting_linked, connect);
+ }
- erts_add_link(&ERTS_P_LINKS(rp), LINK_PID, prt->common.id);
- erts_add_link(&ERTS_P_LINKS(prt), LINK_PID, connect);
+#ifdef USE_VM_PROBES
+ if (DTRACE_ENABLED(port_connect)) {
+ Eterm old_connected = ERTS_PORT_GET_CONNECTED(prt);
+ DTRACE_CHARBUF(process_str, DTRACE_TERM_BUF_SIZE);
+ DTRACE_CHARBUF(port_str, DTRACE_TERM_BUF_SIZE);
+ DTRACE_CHARBUF(newprocess_str, DTRACE_TERM_BUF_SIZE);
- if (IS_TRACED_FL(rp, F_TRACE_PROCS))
- trace_proc(NULL, 0, rp, am_getting_linked, prt->common.id);
+ dtrace_pid_str(old_connected, process_str);
+ erts_snprintf(port_str, sizeof(DTRACE_CHARBUF_NAME(port_str)),
+ "%T", prt->common.id);
+ dtrace_pid_str(connect, newprocess_str);
+ DTRACE4(port_connect, process_str, port_str, prt->name, newprocess_str);
+ }
+#endif
ERTS_PORT_SET_CONNECTED(prt, connect);
- erts_proc_unlock(rp, ERTS_PROC_LOCK_LINK);
-
- if (IS_TRACED_FL(prt, F_TRACE_PORTS))
- trace_port(prt, am_getting_linked, connect);
if (IS_TRACED_FL(prt, F_TRACE_RECEIVE))
trace_port_receive(prt, from, am_connect, connect);
if (IS_TRACED_FL(prt, F_TRACE_SEND)) {
@@ -2391,18 +2385,6 @@ set_port_connected(int bang_op,
trace_port_send(prt, from, TUPLE2(hp, prt->common.id, am_connected), 1);
}
-#ifdef USE_VM_PROBES
- if (DTRACE_ENABLED(port_connect)) {
- DTRACE_CHARBUF(process_str, DTRACE_TERM_BUF_SIZE);
- DTRACE_CHARBUF(port_str, DTRACE_TERM_BUF_SIZE);
- DTRACE_CHARBUF(newprocess_str, DTRACE_TERM_BUF_SIZE);
-
- dtrace_pid_str(connect, process_str);
- erts_snprintf(port_str, sizeof(DTRACE_CHARBUF_NAME(port_str)), "%T", prt->common.id);
- dtrace_proc_str(rp, newprocess_str);
- DTRACE4(port_connect, process_str, port_str, prt->name, newprocess_str);
- }
-#endif
}
return ERTS_PORT_OP_DONE;
@@ -2490,13 +2472,24 @@ erts_port_connect(Process *c_p,
}
static void
-port_unlink(Port *prt, Eterm from)
+port_unlink(Port *prt, ErtsLink *lnk)
{
- ErtsLink *lnk = erts_remove_link(&ERTS_P_LINKS(prt), from);
- if (lnk) {
+ ErtsLinkData *ldp;
+ ErtsLink *dlnk, *llnk;
+
+ llnk = erts_link_to_other(lnk, &ldp);
+ dlnk = erts_link_tree_key_delete(&ERTS_P_LINKS(prt), llnk);
+ if (!dlnk)
+ erts_link_release(lnk);
+ else {
if (IS_TRACED_FL(prt, F_TRACE_PORTS))
- trace_port(prt, am_getting_unlinked, from);
- erts_destroy_link(lnk);
+ trace_port(prt, am_getting_unlinked, llnk->other.item);
+ if (dlnk == llnk)
+ erts_link_release_both(ldp);
+ else {
+ erts_link_release(lnk);
+ erts_link_release(dlnk);
+ }
}
}
@@ -2504,14 +2497,14 @@ static int
port_sig_unlink(Port *prt, erts_aint32_t state, int op, ErtsProc2PortSigData *sigdp)
{
if (op == ERTS_PROC2PORT_SIG_EXEC)
- port_unlink(prt, sigdp->u.unlink.from);
+ port_unlink(prt, sigdp->u.unlink.lnk);
if (sigdp->flags & ERTS_P2P_SIG_DATA_FLG_REPLY)
port_sched_op_reply(sigdp->caller, sigdp->ref, am_true, prt);
return ERTS_PORT_REDS_UNLINK;
}
ErtsPortOpResult
-erts_port_unlink(Process *c_p, Port *prt, Eterm from, Eterm *refp)
+erts_port_unlink(Process *c_p, Port *prt, ErtsLink *lnk, Eterm *refp)
{
ErtsProc2PortSigData *sigdp;
ErtsTryImmDrvCallState try_call_state
@@ -2524,7 +2517,7 @@ erts_port_unlink(Process *c_p, Port *prt, Eterm from, Eterm *refp)
switch (try_imm_drv_call(&try_call_state)) {
case ERTS_TRY_IMM_DRV_CALL_OK:
- port_unlink(prt, from);
+ port_unlink(prt, lnk);
finalize_imm_drv_call(&try_call_state);
BUMP_REDS(c_p, ERTS_PORT_REDS_UNLINK);
return ERTS_PORT_OP_DONE;
@@ -2537,11 +2530,12 @@ erts_port_unlink(Process *c_p, Port *prt, Eterm from, Eterm *refp)
sigdp = erts_port_task_alloc_p2p_sig_data();
sigdp->flags = ERTS_P2P_SIG_TYPE_UNLINK;
- sigdp->u.unlink.from = from;
-
+ sigdp->u.unlink.port_id = prt->common.id;
+ sigdp->u.unlink.lnk = lnk;
+
return erts_schedule_proc2port_signal(c_p,
prt,
- c_p ? c_p->common.id : from,
+ c_p->common.id,
refp,
sigdp,
0,
@@ -2550,45 +2544,24 @@ erts_port_unlink(Process *c_p, Port *prt, Eterm from, Eterm *refp)
}
static void
-port_link_failure(Eterm port_id, Eterm linker)
+port_link_failure(Eterm port_id, ErtsLink *lnk)
{
- Process *rp;
- ErtsProcLocks rp_locks = ERTS_PROC_LOCK_LINK|ERTS_PROC_LOCKS_XSIG_SEND;
- ASSERT(is_internal_pid(linker));
- rp = erts_pid2proc(NULL, 0, linker, rp_locks);
- if (rp) {
- ErtsLink *rlnk = erts_remove_link(&ERTS_P_LINKS(rp), port_id);
- if (rlnk) {
- int xres = erts_send_exit_signal(NULL,
- port_id,
- rp,
- &rp_locks,
- am_noproc,
- NIL,
- NULL,
- 0);
- if (xres >= 0) {
- /* We didn't exit the process and it is traced */
- if (IS_TRACED_FL(rp, F_TRACE_PROCS))
- trace_proc(NULL, 0, rp, am_getting_unlinked, port_id);
- }
- if (rp_locks)
- erts_proc_unlock(rp, rp_locks);
- }
- }
+ erts_proc_sig_send_link_exit(NULL, port_id, lnk, am_noproc, NIL);
}
static void
-port_link(Port *prt, erts_aint32_t state, Eterm to)
+port_link(Port *prt, erts_aint32_t state, ErtsLink *lnk)
{
- if (IS_TRACED_FL(prt, F_TRACE_PORTS))
- trace_port(prt, am_getting_linked, to);
- if (!(state & ERTS_PORT_SFLGS_INVALID_LOOKUP)) {
- erts_add_link(&ERTS_P_LINKS(prt), LINK_PID, to);
- } else {
- port_link_failure(prt->common.id, to);
- if (IS_TRACED_FL(prt, F_TRACE_PORTS))
- trace_port(prt, am_unlink, to);
+ if (state & ERTS_PORT_SFLGS_INVALID_LOOKUP)
+ port_link_failure(prt->common.id, lnk);
+ else {
+ ErtsLink *rlnk;
+ rlnk = erts_link_tree_insert_addr_replace(&ERTS_P_LINKS(prt),
+ lnk);
+ if (rlnk)
+ erts_link_release(rlnk);
+ else if (IS_TRACED_FL(prt, F_TRACE_PORTS))
+ trace_port(prt, am_getting_linked, lnk->other.item);
}
}
@@ -2596,17 +2569,16 @@ static int
port_sig_link(Port *prt, erts_aint32_t state, int op, ErtsProc2PortSigData *sigdp)
{
if (op == ERTS_PROC2PORT_SIG_EXEC)
- port_link(prt, state, sigdp->u.link.to);
- else {
- port_link_failure(sigdp->u.link.port, sigdp->u.link.to);
- }
+ port_link(prt, state, sigdp->u.link.lnk);
+ else
+ port_link_failure(sigdp->u.link.port_id, sigdp->u.link.lnk);
if (sigdp->flags & ERTS_P2P_SIG_DATA_FLG_REPLY)
port_sched_op_reply(sigdp->caller, sigdp->ref, am_true, prt);
return ERTS_PORT_REDS_LINK;
}
ErtsPortOpResult
-erts_port_link(Process *c_p, Port *prt, Eterm to, Eterm *refp)
+erts_port_link(Process *c_p, Port *prt, ErtsLink *lnk, Eterm *refp)
{
ErtsProc2PortSigData *sigdp;
ErtsTryImmDrvCallState try_call_state
@@ -2619,7 +2591,7 @@ erts_port_link(Process *c_p, Port *prt, Eterm to, Eterm *refp)
switch (try_imm_drv_call(&try_call_state)) {
case ERTS_TRY_IMM_DRV_CALL_OK:
- port_link(prt, try_call_state.state, to);
+ port_link(prt, try_call_state.state, lnk);
finalize_imm_drv_call(&try_call_state);
BUMP_REDS(c_p, ERTS_PORT_REDS_LINK);
return ERTS_PORT_OP_DONE;
@@ -2632,12 +2604,12 @@ erts_port_link(Process *c_p, Port *prt, Eterm to, Eterm *refp)
sigdp = erts_port_task_alloc_p2p_sig_data();
sigdp->flags = ERTS_P2P_SIG_TYPE_LINK;
- sigdp->u.link.port = prt->common.id;
- sigdp->u.link.to = to;
+ sigdp->u.link.port_id = prt->common.id;
+ sigdp->u.link.lnk = lnk;
return erts_schedule_proc2port_signal(c_p,
prt,
- c_p ? c_p->common.id : to,
+ c_p->common.id,
refp,
sigdp,
0,
@@ -2646,51 +2618,23 @@ erts_port_link(Process *c_p, Port *prt, Eterm to, Eterm *refp)
}
static void
-port_monitor_failure(Eterm port_id, Eterm origin, Eterm ref_DOWN)
+port_monitor_failure(Eterm port_id, ErtsMonitor *mon)
{
- Process *origin_p;
- ErtsProcLocks p_locks = ERTS_PROC_LOCK_LINK;
- ASSERT(is_internal_pid(origin));
-
- origin_p = erts_pid2proc(NULL, 0, origin, p_locks);
- if (! origin_p) { return; }
-
- /* Send the DOWN message immediately. Ref is made on the fly because
- * caller has never seen it yet. */
- erts_queue_monitor_message(origin_p, &p_locks, ref_DOWN,
- am_port, port_id, am_noproc);
- erts_proc_unlock(origin_p, p_locks);
+ erts_proc_sig_send_monitor_down(mon, am_noproc);
}
/* Origin wants to monitor port Prt. State contains possible error, which has
* happened just before. Name is either NIL or an atom, if user monitors
* a port by name. Ref is premade reference that will be returned to user */
static void
-port_monitor(Port *prt, erts_aint32_t state, Eterm origin,
- Eterm name, Eterm ref)
+port_monitor(Port *prt, erts_aint32_t state, ErtsMonitor *mon)
{
- Eterm name_or_nil = is_atom(name) ? name : NIL;
-
- ASSERT(is_pid(origin));
- ASSERT(is_atom(name) || is_port(name) || name == NIL);
- ASSERT(is_internal_ordinary_ref(ref));
-
- if (!(state & ERTS_PORT_SFLGS_INVALID_LOOKUP)) {
- ErtsProcLocks p_locks = ERTS_PROC_LOCK_LINK;
-
- Process *origin_p = erts_pid2proc(NULL, 0, origin, p_locks);
- if (! origin_p) {
- goto failure;
- }
- erts_add_monitor(&ERTS_P_MONITORS(origin_p), MON_ORIGIN, ref,
- prt->common.id, name_or_nil);
- erts_add_monitor(&ERTS_P_MONITORS(prt), MON_TARGET, ref,
- origin, name_or_nil);
-
- erts_proc_unlock(origin_p, p_locks);
- } else {
-failure:
- port_monitor_failure(prt->common.id, origin, ref);
+ ASSERT(prt);
+ if (state & ERTS_PORT_SFLGS_INVALID_LOOKUP)
+ port_monitor_failure(prt->common.id, mon);
+ else {
+ ASSERT(erts_monitor_is_target(mon));
+ erts_monitor_list_insert(&ERTS_P_LT_MONITORS(prt), mon);
}
}
@@ -2698,24 +2642,11 @@ static int
port_sig_monitor(Port *prt, erts_aint32_t state, int op,
ErtsProc2PortSigData *sigdp)
{
- Eterm hp[ERTS_REF_THING_SIZE];
- Eterm ref = make_internal_ref(&hp);
- write_ref_thing(hp, sigdp->ref[0], sigdp->ref[1], sigdp->ref[2]);
-
- if (op == ERTS_PROC2PORT_SIG_EXEC) {
- /* erts_add_monitor call inside port_monitor will copy ref from hp */
- port_monitor(prt, state,
- sigdp->u.monitor.origin,
- sigdp->u.monitor.name,
- ref);
- } else {
- port_monitor_failure(sigdp->u.monitor.name,
- sigdp->u.monitor.origin,
- ref);
- }
- if (sigdp->flags & ERTS_P2P_SIG_DATA_FLG_REPLY) {
- port_sched_op_reply(sigdp->caller, sigdp->ref, am_true, prt);
- }
+ if (op == ERTS_PROC2PORT_SIG_EXEC)
+ port_monitor(prt, state, sigdp->u.monitor.mon);
+ else
+ port_monitor_failure(sigdp->u.monitor.port_id,
+ sigdp->u.monitor.mon);
return ERTS_PORT_REDS_MONITOR;
}
@@ -2723,95 +2654,63 @@ port_sig_monitor(Port *prt, erts_aint32_t state, int op,
* a reference (ref may be rewritten to be used to serve additionally as a
* signal id). Name is atom if user monitors port by name or NIL */
ErtsPortOpResult
-erts_port_monitor(Process *origin, Port *port, Eterm name, Eterm *refp)
+erts_port_monitor(Process *c_p, Port *port, ErtsMonitor *mon)
{
ErtsProc2PortSigData *sigdp;
ErtsTryImmDrvCallState try_call_state
= ERTS_INIT_TRY_IMM_DRV_CALL_STATE(
- origin, port, ERTS_PORT_SFLGS_INVALID_LOOKUP,
+ c_p,
+ port,
+ ERTS_PORT_SFLGS_INVALID_LOOKUP,
0,
- 0, /* trap_ref is always set so !trap_ref always is false */
+ !0,
am_monitor);
- ASSERT(origin);
+ ASSERT(c_p);
ASSERT(port);
- ASSERT(is_atom(name) || is_port(name));
- ASSERT(refp);
+ ASSERT(mon);
switch (try_imm_drv_call(&try_call_state)) {
case ERTS_TRY_IMM_DRV_CALL_OK:
- port_monitor(port, try_call_state.state, origin->common.id, name, *refp);
+ port_monitor(port, try_call_state.state, mon);
finalize_imm_drv_call(&try_call_state);
- BUMP_REDS(origin, ERTS_PORT_REDS_MONITOR);
+ BUMP_REDS(c_p, ERTS_PORT_REDS_MONITOR);
return ERTS_PORT_OP_DONE;
case ERTS_TRY_IMM_DRV_CALL_INVALID_PORT:
- return ERTS_PORT_OP_BADARG;
+ return ERTS_PORT_OP_DROPPED;
default:
break; /* Schedule call instead... */
}
sigdp = erts_port_task_alloc_p2p_sig_data();
sigdp->flags = ERTS_P2P_SIG_TYPE_MONITOR;
- sigdp->u.monitor.origin = origin->common.id;
- sigdp->u.monitor.name = name; /* either named monitor, or port id */
+ sigdp->u.monitor.port_id = port->common.id;
+ sigdp->u.monitor.mon = mon;
/* Ref contents will be initialized here */
- return erts_schedule_proc2port_signal(origin, port, origin->common.id,
- refp, sigdp, 0, NULL,
+ return erts_schedule_proc2port_signal(c_p,
+ port,
+ c_p->common.id,
+ NULL,
+ sigdp,
+ 0,
+ NULL,
port_sig_monitor);
}
-static void
-port_demonitor_failure(Eterm port_id, Eterm origin, Eterm ref)
-{
- Process *origin_p;
- ErtsProcLocks rp_locks = ERTS_PROC_LOCK_LINK;
- ErtsMonitor *mon1;
- ASSERT(is_internal_pid(origin));
-
- origin_p = erts_pid2proc(NULL, 0, origin, rp_locks);
- if (! origin_p) { return; }
-
- /* do not send any DOWN messages, drop monitors on process */
- mon1 = erts_remove_monitor(&ERTS_P_MONITORS(origin_p), ref);
- if (mon1 != NULL) {
- erts_destroy_monitor(mon1);
- }
-
- erts_proc_unlock(origin_p, rp_locks);
-}
-
/* Origin wants to demonitor port Prt. State contains possible error, which has
* happened just before. Ref is reference to monitor */
static void
-port_demonitor(Port *port, erts_aint32_t state, Eterm origin, Eterm ref)
+port_demonitor(Port *port, erts_aint32_t state, ErtsMonitor *mon)
{
- ASSERT(port);
- ASSERT(is_pid(origin));
- ASSERT(is_internal_ref(ref));
-
- if (!(state & ERTS_PORT_SFLGS_INVALID_LOOKUP)) {
- ErtsProcLocks p_locks = ERTS_PROC_LOCK_LINK;
- Process *origin_p = erts_pid2proc(NULL, 0, origin, p_locks);
- if (origin_p) {
- ErtsMonitor *mon1 = erts_remove_monitor(&ERTS_P_MONITORS(origin_p),
- ref);
- if (mon1 != NULL) {
- erts_destroy_monitor(mon1);
- }
- }
- if (1) {
- ErtsMonitor *mon2 = erts_remove_monitor(&ERTS_P_MONITORS(port),
- ref);
- if (mon2 != NULL) {
- erts_destroy_monitor(mon2);
- }
- }
- if (origin_p) { /* when origin is dying, it won't be found */
- erts_proc_unlock(origin_p, p_locks);
- }
- } else {
- port_demonitor_failure(port->common.id, origin, ref);
+ ErtsMonitorData *mdp = erts_monitor_to_data(mon);
+ ASSERT(port && mon);
+ ASSERT(erts_monitor_is_origin(mon));
+ if (!erts_monitor_is_in_table(&mdp->target))
+ erts_monitor_release(mon);
+ else {
+ erts_monitor_list_delete(&ERTS_P_LT_MONITORS(port), &mdp->target);
+ erts_monitor_release_both(mdp);
}
}
@@ -2819,73 +2718,47 @@ static int
port_sig_demonitor(Port *prt, erts_aint32_t state, int op,
ErtsProc2PortSigData *sigdp)
{
- Eterm hp[ERTS_REF_THING_SIZE];
- Eterm ref = make_internal_ref(&hp);
- write_ref_thing(hp, sigdp->u.demonitor.ref[0],
- sigdp->u.demonitor.ref[1],
- sigdp->u.demonitor.ref[2]);
- if (op == ERTS_PROC2PORT_SIG_EXEC) {
- port_demonitor(prt, state, sigdp->u.demonitor.origin, ref);
- } else {
- port_demonitor_failure(sigdp->u.demonitor.name,
- sigdp->u.demonitor.origin,
- ref);
- }
- if (sigdp->flags & ERTS_P2P_SIG_DATA_FLG_REPLY) {
- port_sched_op_reply(sigdp->caller, sigdp->ref, am_true, prt);
- }
+ if (op == ERTS_PROC2PORT_SIG_EXEC)
+ port_demonitor(prt, state, sigdp->u.demonitor.mon);
+ else
+ erts_monitor_release(sigdp->u.demonitor.mon);
return ERTS_PORT_REDS_DEMONITOR;
}
-/* Removes monitor between origin and target, identified by ref.
- * Mode defines normal or relaxed demonitor rules (process is at death) */
-ErtsPortOpResult erts_port_demonitor(Process *origin, ErtsDemonitorMode mode,
- Port *target, Eterm ref,
- Eterm *trap_ref)
+ErtsPortOpResult
+erts_port_demonitor(Process *c_p, Port *prt, ErtsMonitor *mon)
{
- Process *c_p = mode == ERTS_PORT_DEMONITOR_NORMAL ? origin : NULL;
ErtsProc2PortSigData *sigdp;
ErtsTryImmDrvCallState try_call_state
= ERTS_INIT_TRY_IMM_DRV_CALL_STATE(
c_p,
- target, ERTS_PORT_SFLGS_INVALID_LOOKUP,
+ prt, ERTS_PORT_SFLGS_INVALID_LOOKUP,
0,
- !trap_ref,
+ !0,
am_demonitor);
- ASSERT(origin);
- ASSERT(target);
- ASSERT(is_internal_ref(ref));
+ ASSERT(c_p && prt && mon);
switch (try_imm_drv_call(&try_call_state)) {
case ERTS_TRY_IMM_DRV_CALL_OK:
- port_demonitor(target, try_call_state.state, origin->common.id, ref);
+ port_demonitor(prt, try_call_state.state, mon);
finalize_imm_drv_call(&try_call_state);
- if (mode == ERTS_PORT_DEMONITOR_NORMAL) {
- BUMP_REDS(origin, ERTS_PORT_REDS_DEMONITOR);
- }
+ BUMP_REDS(c_p, ERTS_PORT_REDS_DEMONITOR);
return ERTS_PORT_OP_DONE;
case ERTS_TRY_IMM_DRV_CALL_INVALID_PORT:
- return ERTS_PORT_OP_BADARG;
+ return ERTS_PORT_OP_DROPPED;
default:
break; /* Schedule call instead... */
}
sigdp = erts_port_task_alloc_p2p_sig_data();
sigdp->flags = ERTS_P2P_SIG_TYPE_DEMONITOR;
- sigdp->u.demonitor.origin = origin->common.id;
- sigdp->u.demonitor.name = target->common.id;
- {
- Uint32 *nums = internal_ref_numbers(ref);
- /* Start from 1 skip ref arity */
- sys_memcpy(sigdp->u.demonitor.ref,
- nums,
- sizeof(sigdp->u.demonitor.ref));
- }
+ sigdp->u.demonitor.port_id = prt->common.id;
+ sigdp->u.demonitor.mon = mon;
/* Ref contents will be initialized here */
- return erts_schedule_proc2port_signal(c_p, target, origin->common.id,
- trap_ref, sigdp, 0, NULL,
+ return erts_schedule_proc2port_signal(c_p, prt, c_p->common.id,
+ NULL, sigdp, 0, NULL,
port_sig_demonitor);
}
@@ -2894,11 +2767,13 @@ init_ack_send_reply(Port *port, Eterm resp)
{
if (!is_internal_port(resp)) {
- Process *rp = erts_proc_lookup_raw(port->async_open_port->to);
- erts_proc_lock(rp, ERTS_PROC_LOCK_LINK);
- erts_remove_link(&ERTS_P_LINKS(port), port->async_open_port->to);
- erts_remove_link(&ERTS_P_LINKS(rp), port->common.id);
- erts_proc_unlock(rp, ERTS_PROC_LOCK_LINK);
+ Eterm proc = port->async_open_port->to;
+ ErtsLink *lnk = erts_link_tree_lookup(ERTS_P_LINKS(port),
+ proc);
+ if (lnk) {
+ erts_link_tree_delete(&ERTS_P_LINKS(port), lnk);
+ erts_proc_sig_send_unlink(NULL, lnk);
+ }
}
port_sched_op_reply(port->async_open_port->to,
port->async_open_port->ref,
@@ -2925,7 +2800,7 @@ erl_drv_init_ack(ErlDrvPort ix, ErlDrvData res) {
break;
case -2: {
char *str = erl_errno_id(errno);
- resp = erts_atom_put((byte *) str, strlen(str),
+ resp = erts_atom_put((byte *) str, sys_strlen(str),
ERTS_ATOM_ENC_LATIN1, 1);
break;
}
@@ -3676,6 +3551,7 @@ terminate_port(Port *prt)
ASSERT(!ERTS_P_LINKS(prt));
ASSERT(!ERTS_P_MONITORS(prt));
+ ASSERT(!ERTS_P_LT_MONITORS(prt));
/* state may be altered by kill_port() below */
state = erts_atomic32_read_band_nob(&prt->state,
@@ -3763,146 +3639,25 @@ erts_terminate_port(Port *pp)
terminate_port(pp);
}
-static void port_fire_one_monitor(ErtsMonitor *mon, void *ctx0);
-static void sweep_one_monitor(ErtsMonitor *mon, void *vpsc)
-{
- switch (mon->type) {
- case MON_ORIGIN: {
- ErtsMonitor *rmon;
- Process *rp;
-
- ASSERT(is_internal_pid(mon->u.pid));
- rp = erts_pid2proc(NULL, 0, mon->u.pid, ERTS_PROC_LOCK_LINK);
- if (!rp) {
- goto done;
- }
- rmon = erts_remove_monitor(&ERTS_P_MONITORS(rp), mon->ref);
- erts_proc_unlock(rp, ERTS_PROC_LOCK_LINK);
- if (rmon == NULL) {
- goto done;
- }
- erts_destroy_monitor(rmon);
- } break;
- case MON_TARGET: {
- port_fire_one_monitor(mon, vpsc); /* forward call */
- } break;
- }
- done:
- erts_destroy_monitor(mon);
-}
-
-
-
typedef struct {
- Port *port;
+ Eterm port_id;
Eterm reason;
-} SweepContext;
+} ErtsPortExitContext;
-static void sweep_one_link(ErtsLink *lnk, void *vpsc)
+static void link_port_exit(ErtsLink *lnk, void *vpectxt)
{
- SweepContext *psc = vpsc;
- DistEntry *dep;
- Process *rp;
- Eterm port_id = psc->port->common.id;
-
- ASSERT(lnk->type == LINK_PID);
-
- if (IS_TRACED_FL(psc->port, F_TRACE_PORTS))
- trace_port(psc->port, am_unlink, lnk->pid);
-
- if (is_external_pid(lnk->pid)) {
- dep = external_pid_dist_entry(lnk->pid);
- if(dep != erts_this_dist_entry) {
- ErtsDistLinkData dld;
- ErtsDSigData dsd;
- int code;
- code = erts_dsig_prepare(&dsd, dep, NULL, 0, ERTS_DSP_NO_LOCK, 0, 0);
- switch (code) {
- case ERTS_DSIG_PREP_NOT_ALIVE:
- case ERTS_DSIG_PREP_NOT_CONNECTED:
- break;
- case ERTS_DSIG_PREP_PENDING:
- case ERTS_DSIG_PREP_CONNECTED:
- erts_remove_dist_link(&dld, port_id, lnk->pid, dep);
- erts_destroy_dist_link(&dld);
- code = erts_dsig_send_exit(&dsd, port_id, lnk->pid,
- psc->reason);
- ASSERT(code == ERTS_DSIG_SEND_OK);
- break;
- default:
- ASSERT(! "Invalid dsig prepare result");
- break;
- }
- }
- } else {
- ErtsProcLocks rp_locks = ERTS_PROC_LOCK_LINK|ERTS_PROC_LOCKS_XSIG_SEND;
- ASSERT(is_internal_pid(lnk->pid));
- rp = erts_pid2proc(NULL, 0, lnk->pid, rp_locks);
- if (rp) {
- ErtsLink *rlnk = erts_remove_link(&ERTS_P_LINKS(rp), port_id);
-
- if (rlnk) {
- int xres = erts_send_exit_signal(NULL,
- port_id,
- rp,
- &rp_locks,
- psc->reason,
- NIL,
- NULL,
- 0);
- if (xres >= 0) {
- if (rp_locks & ERTS_PROC_LOCKS_XSIG_SEND) {
- erts_proc_unlock(rp, ERTS_PROC_LOCKS_XSIG_SEND);
- rp_locks &= ~ERTS_PROC_LOCKS_XSIG_SEND;
- }
- /* We didn't exit the process and it is traced */
- if (IS_TRACED_FL(rp, F_TRACE_PROCS))
- trace_proc(NULL, 0, rp, am_getting_unlinked, port_id);
- }
- erts_destroy_link(rlnk);
- }
-
- erts_proc_unlock(rp, rp_locks);
- }
- }
- erts_destroy_link(lnk);
+ ErtsPortExitContext *pectxt = vpectxt;
+ erts_proc_sig_send_link_exit(NULL, pectxt->port_id,
+ lnk, pectxt->reason, NIL);
}
-static void
-port_fire_one_monitor(ErtsMonitor *mon, void *ctx0)
+static void monitor_port_exit(ErtsMonitor *mon, void *vpectxt)
{
- Process *origin;
- ErtsProcLocks origin_locks;
-
- if (mon->type != MON_TARGET || ! is_pid(mon->u.pid)) {
- return;
- }
- /*
- * Proceed here if someone monitors us, we (port) are the target and
- * origin is some process
- */
- origin_locks = ERTS_PROC_LOCKS_MSG_SEND | ERTS_PROC_LOCK_LINK;
-
- origin = erts_pid2proc(NULL, 0, mon->u.pid, origin_locks);
- if (origin) {
- DeclareTmpHeapNoproc(lhp,3);
- SweepContext *ctx = (SweepContext *)ctx0;
- ErtsMonitor *rmon;
- Eterm watched = (is_atom(mon->name)
- ? TUPLE2(lhp, mon->name, erts_this_dist_entry->sysname)
- : ctx->port->common.id);
-
- erts_queue_monitor_message(origin, &origin_locks, mon->ref, am_port,
- watched, ctx->reason);
- UnUseTmpHeapNoproc(3);
-
- rmon = erts_remove_monitor(&ERTS_P_MONITORS(origin), mon->ref);
- erts_proc_unlock(origin, origin_locks);
-
- if (rmon) {
- erts_destroy_monitor(rmon);
- }
- }
+ ErtsPortExitContext *pectxt = vpectxt;
+ if (erts_monitor_is_target(mon))
+ erts_proc_sig_send_monitor_down(mon, pectxt->reason);
+ else
+ erts_proc_sig_send_demonitor(mon);
}
/* 'from' is sending 'this_port' an exit signal, (this_port must be internal).
@@ -3919,9 +3674,12 @@ int
erts_deliver_port_exit(Port *prt, Eterm from, Eterm reason, int send_closed,
int drop_normal)
{
- ErtsLink *lnk;
+ ErtsLink *links;
+ ErtsMonitor *monitors;
+ ErtsMonitor *lt_monitors;
Eterm modified_reason;
erts_aint32_t state, set_state_flags;
+ ErtsPortExitContext pectxt;
ERTS_CHK_NO_PROC_LOCKS;
ERTS_LC_ASSERT(erts_lc_is_port_locked(prt));
@@ -3969,23 +3727,36 @@ erts_deliver_port_exit(Port *prt, Eterm from, Eterm reason, int send_closed,
set_busy_port(ERTS_Port2ErlDrvPort(prt), 0);
+ links = ERTS_P_LINKS(prt);
+ ERTS_P_LINKS(prt) = NULL;
+ monitors = ERTS_P_MONITORS(prt);
+ ERTS_P_MONITORS(prt) = NULL;
+ lt_monitors = ERTS_P_LT_MONITORS(prt);
+ ERTS_P_LT_MONITORS(prt) = NULL;
+
if (prt->common.u.alive.reg != NULL)
(void) erts_unregister_name(NULL, 0, prt, prt->common.u.alive.reg->name);
- {
- SweepContext sc = {prt, modified_reason};
- lnk = ERTS_P_LINKS(prt);
- ERTS_P_LINKS(prt) = NULL;
- erts_sweep_links(lnk, &sweep_one_link, &sc);
+ pectxt.port_id = prt->common.id;
+ pectxt.reason = modified_reason;
+
+ if (links)
+ erts_monitor_tree_foreach_delete(&links,
+ link_port_exit,
+ (void *) &pectxt);
+
+ if (monitors || lt_monitors) {
+ DRV_MONITOR_LOCK_PDL(prt);
+ if (monitors)
+ erts_monitor_tree_foreach_delete(&monitors,
+ monitor_port_exit,
+ (void *) &pectxt);
+ if (lt_monitors)
+ erts_monitor_list_foreach_delete(&lt_monitors,
+ monitor_port_exit,
+ (void *) &pectxt);
+ DRV_MONITOR_UNLOCK_PDL(prt);
}
- DRV_MONITOR_LOCK_PDL(prt);
- {
- SweepContext ctx = {prt, modified_reason};
- ErtsMonitor *moni = ERTS_P_MONITORS(prt);
- ERTS_P_MONITORS(prt) = NULL;
- erts_sweep_monitors(moni, &sweep_one_monitor, &ctx);
- }
- DRV_MONITOR_UNLOCK_PDL(prt);
if ((state & ERTS_PORT_SFLG_DISTRIBUTION) && prt->dist_entry) {
erts_do_net_exits(prt->dist_entry, modified_reason);
@@ -5065,14 +4836,105 @@ typedef struct {
static void prt_one_monitor(ErtsMonitor *mon, void *vprtd)
{
+ ErtsMonitorData *mdp = erts_monitor_to_data(mon);
prt_one_lnk_data *prtd = (prt_one_lnk_data *) vprtd;
- erts_print(prtd->to, prtd->arg, "(%T,%T)", mon->u.pid, mon->ref);
+ if (mon->type == ERTS_MON_TYPE_RESOURCE && erts_monitor_is_target(mon))
+ erts_print(prtd->to, prtd->arg, "(%p,%T)", mon->other.ptr, mdp->ref);
+ else
+ erts_print(prtd->to, prtd->arg, "(%T,%T)", mon->other.item, mdp->ref);
}
static void prt_one_lnk(ErtsLink *lnk, void *vprtd)
{
prt_one_lnk_data *prtd = (prt_one_lnk_data *) vprtd;
- erts_print(prtd->to, prtd->arg, "%T", lnk->pid);
+ erts_print(prtd->to, prtd->arg, "%T", lnk->other.item);
+}
+
+static void dump_port_state(fmtfn_t to, void *arg, erts_aint32_t state)
+{
+ erts_aint32_t rest;
+ int unknown = 0;
+ char delim = ' ';
+
+ erts_print(to, arg, "State:");
+
+ rest = state;
+ while (rest) {
+ erts_aint32_t chk = (rest ^ (rest-1)) & rest; /* lowest set bit */
+ char* s;
+
+ rest &= ~chk;
+ switch (chk) {
+ case ERTS_PORT_SFLG_CONNECTED: s = "CONNECTED"; break;
+ case ERTS_PORT_SFLG_EXITING: s = "EXITING"; break;
+ case ERTS_PORT_SFLG_DISTRIBUTION: s = "DISTR"; break;
+ case ERTS_PORT_SFLG_BINARY_IO: s = "BINARY_IO"; break;
+ case ERTS_PORT_SFLG_SOFT_EOF: s = "SOFT_EOF"; break;
+ case ERTS_PORT_SFLG_CLOSING: s = "CLOSING"; break;
+ case ERTS_PORT_SFLG_SEND_CLOSED: s = "SEND_CLOSED"; break;
+ case ERTS_PORT_SFLG_LINEBUF_IO: s = "LINEBUF_IO"; break;
+ case ERTS_PORT_SFLG_FREE: s = "FREE"; break;
+ case ERTS_PORT_SFLG_INITIALIZING: s = "INITIALIZING"; break;
+ case ERTS_PORT_SFLG_PORT_SPECIFIC_LOCK: s = "PORT_LOCK"; break;
+ case ERTS_PORT_SFLG_INVALID: s = "INVALID"; break;
+ case ERTS_PORT_SFLG_HALT: s = "HALT"; break;
+#ifdef DEBUG
+ case ERTS_PORT_SFLG_PORT_DEBUG: s = "DEBUG"; break;
+#endif
+ default:
+ unknown = 1;
+ continue;
+ }
+ erts_print(to, arg, "%c%s", delim, s);
+ delim = '|';
+ }
+ if (unknown || !state)
+ erts_print(to, arg, "%c0x%x\n", delim, state);
+ else
+ erts_print(to, arg, "\n");
+}
+
+static void dump_port_task_flags(fmtfn_t to, void *arg, Port* p)
+{
+ erts_aint32_t flags = erts_atomic32_read_nob(&p->sched.flags);
+ erts_aint32_t unknown = 0;
+ char delim = ' ';
+
+ if (!flags)
+ return;
+
+ erts_print(to, arg, "Task Flags:");
+
+ while (flags) {
+ erts_aint32_t chk = (flags ^ (flags-1)) & flags; /* lowest set bit */
+ char* s;
+
+ flags &= ~chk;
+ switch (chk) {
+ case ERTS_PTS_FLG_IN_RUNQ: s = "IN_RUNQ"; break;
+ case ERTS_PTS_FLG_EXEC: s = "EXEC"; break;
+ case ERTS_PTS_FLG_HAVE_TASKS: s = "HAVE_TASKS"; break;
+ case ERTS_PTS_FLG_EXIT: s = "EXIT"; break;
+ case ERTS_PTS_FLG_BUSY_PORT: s = "BUSY_PORT"; break;
+ case ERTS_PTS_FLG_BUSY_PORT_Q: s = "BUSY_Q"; break;
+ case ERTS_PTS_FLG_CHK_UNSET_BUSY_PORT_Q: s = "CHK_UNSET_BUSY_Q"; break;
+ case ERTS_PTS_FLG_HAVE_BUSY_TASKS: s = "BUSY_TASKS"; break;
+ case ERTS_PTS_FLG_HAVE_NS_TASKS: s = "NS_TASKS"; break;
+ case ERTS_PTS_FLG_PARALLELISM: s = "PARALLELISM"; break;
+ case ERTS_PTS_FLG_FORCE_SCHED: s = "FORCE_SCHED"; break;
+ case ERTS_PTS_FLG_EXITING: s = "EXITING"; break;
+ case ERTS_PTS_FLG_EXEC_IMM: s = "EXEC_IMM"; break;
+ default:
+ unknown |= chk;
+ continue;
+ }
+ erts_print(to, arg, "%c%s", delim, s);
+ delim = '|';
+ }
+ if (unknown)
+ erts_print(to, arg, "%cUNKNOWN(0x%x)\n", delim, unknown);
+ else
+ erts_print(to, arg, "\n");
}
void
@@ -5084,6 +4946,8 @@ print_port_info(Port *p, fmtfn_t to, void *arg)
return;
erts_print(to, arg, "=port:%T\n", p->common.id);
+ dump_port_state(to, arg, state);
+ dump_port_task_flags(to, arg, p);
erts_print(to, arg, "Slot: %d\n", internal_port_index(p->common.id));
if (state & ERTS_PORT_SFLG_CONNECTED) {
erts_print(to, arg, "Connected: %T", ERTS_PORT_GET_CONNECTED(p));
@@ -5095,17 +4959,24 @@ print_port_info(Port *p, fmtfn_t to, void *arg)
prtd.to = to;
prtd.arg = arg;
erts_print(to, arg, "Links: ");
- erts_doforall_links(ERTS_P_LINKS(p), &prt_one_lnk, &prtd);
+ erts_link_tree_foreach(ERTS_P_LINKS(p), prt_one_lnk, (void *) &prtd);
erts_print(to, arg, "\n");
}
- if (ERTS_P_MONITORS(p)) {
+ if (ERTS_P_MONITORS(p) || ERTS_P_LT_MONITORS(p)) {
prt_one_lnk_data prtd;
prtd.to = to;
prtd.arg = arg;
erts_print(to, arg, "Monitors: ");
- erts_doforall_monitors(ERTS_P_MONITORS(p), &prt_one_monitor, &prtd);
+ erts_monitor_tree_foreach(ERTS_P_MONITORS(p), prt_one_monitor,
+ (void *) &prtd);
+ erts_monitor_list_foreach(ERTS_P_LT_MONITORS(p), prt_one_monitor,
+ (void *) &prtd);
erts_print(to, arg, "\n");
}
+ if (p->suspended) {
+ erts_print(to, arg, "Suspended: ");
+ erts_proclist_dump(to, arg, p->suspended);
+ }
if (p->common.u.alive.reg != NULL)
erts_print(to, arg, "Registered as: %T\n", p->common.u.alive.reg->name);
@@ -5123,6 +4994,14 @@ print_port_info(Port *p, fmtfn_t to, void *arg)
} else {
erts_print(to, arg, "Port controls linked-in driver: %s\n",p->name);
}
+ erts_print(to, arg, "Input: %beu\n", p->bytes_in);
+ erts_print(to, arg, "Output: %beu\n", p->bytes_out);
+ erts_print(to, arg, "Queue: %beu\n", erts_ioq_size(&p->ioq));
+ {
+ Eterm port_data = erts_port_data_read(p);
+ if (port_data != am_undefined)
+ erts_print(to, arg, "Port Data: %T\n", port_data);
+ }
}
void
@@ -6928,24 +6807,23 @@ static int do_driver_monitor_process(Port *prt,
ErlDrvMonitor *monitor)
{
Eterm buf[ERTS_REF_THING_SIZE];
- Process *rp;
Eterm ref;
+ ErtsMonitorData *mdp;
- if (prt->drv_ptr->process_exit == NULL) {
+ if (!prt->drv_ptr->process_exit)
return -1;
- }
- rp = erts_pid2proc_opt(NULL, 0,
- (Eterm) process, ERTS_PROC_LOCK_LINK,
- ERTS_P2P_FLG_ALLOW_OTHER_X);
- if (!rp) {
- return 1;
- }
ref = erts_make_ref_in_buffer(buf);
- erts_add_monitor(&ERTS_P_MONITORS(prt), MON_ORIGIN, ref, rp->common.id, NIL);
- erts_add_monitor(&ERTS_P_MONITORS(rp), MON_TARGET, ref, prt->common.id, NIL);
+ mdp = erts_monitor_create(ERTS_MON_TYPE_PORT, ref,
+ prt->common.id, process, NIL);
+
+ if (!erts_proc_sig_send_monitor(&mdp->target, process)) {
+ erts_monitor_release_both(mdp);
+ return 1;
+ }
+
+ erts_monitor_tree_insert(&ERTS_P_MONITORS(prt), &mdp->origin);
- erts_proc_unlock(rp, ERTS_PROC_LOCK_LINK);
erts_ref_to_driver_monitor(ref,monitor);
return 0;
}
@@ -6978,37 +6856,17 @@ int driver_monitor_process(ErlDrvPort drvport,
static int do_driver_demonitor_process(Port *prt, const ErlDrvMonitor *monitor)
{
Eterm heap[ERTS_REF_THING_SIZE];
- Process *rp;
Eterm ref;
ErtsMonitor *mon;
- Eterm to;
ref = erts_driver_monitor_to_ref(heap, monitor);
- mon = erts_lookup_monitor(ERTS_P_MONITORS(prt), ref);
- if (mon == NULL) {
- return 1;
- }
- ASSERT(mon->type == MON_ORIGIN);
- to = mon->u.pid;
- ASSERT(is_internal_pid(to));
- rp = erts_pid2proc_opt(NULL,
- 0,
- to,
- ERTS_PROC_LOCK_LINK,
- ERTS_P2P_FLG_ALLOW_OTHER_X);
- mon = erts_remove_monitor(&ERTS_P_MONITORS(prt), ref);
- if (mon) {
- erts_destroy_monitor(mon);
- }
- if (rp) {
- ErtsMonitor *rmon;
- rmon = erts_remove_monitor(&ERTS_P_MONITORS(rp), ref);
- erts_proc_unlock(rp, ERTS_PROC_LOCK_LINK);
- if (rmon != NULL) {
- erts_destroy_monitor(rmon);
- }
- }
+ mon = erts_monitor_tree_lookup(ERTS_P_MONITORS(prt), ref);
+ if (!mon || !erts_monitor_is_origin(mon))
+ return 1;
+
+ erts_monitor_tree_delete(&ERTS_P_MONITORS(prt), mon);
+ erts_proc_sig_send_demonitor(mon);
return 0;
}
@@ -7037,19 +6895,16 @@ static ErlDrvTermData do_driver_get_monitored_process(Port *prt,const ErlDrvMoni
{
Eterm ref;
ErtsMonitor *mon;
- Eterm to;
Eterm heap[ERTS_REF_THING_SIZE];
ref = erts_driver_monitor_to_ref(heap, monitor);
- mon = erts_lookup_monitor(ERTS_P_MONITORS(prt), ref);
- if (mon == NULL) {
+ mon = erts_monitor_tree_lookup(ERTS_P_MONITORS(prt), ref);
+ if (!mon || !erts_monitor_is_origin(mon))
return driver_term_nil;
- }
- ASSERT(mon->type == MON_ORIGIN);
- to = mon->u.pid;
- ASSERT(is_internal_pid(to));
- return (ErlDrvTermData) to;
+
+ ASSERT(is_internal_pid(mon->other.item));
+ return (ErlDrvTermData) mon->other.item;
}
@@ -7081,24 +6936,27 @@ int driver_compare_monitors(const ErlDrvMonitor *monitor1,
ERTS_REF_THING_SIZE*sizeof(Eterm));
}
-void erts_fire_port_monitor(Port *prt, Eterm ref)
+void erts_fire_port_monitor(Port *prt, ErtsMonitor *tmon)
{
- ErtsMonitor *rmon;
+ ErtsMonitorData *mdp;
void (*callback)(ErlDrvData drv_data, ErlDrvMonitor *monitor);
ErlDrvMonitor drv_monitor;
int fpe_was_unmasked;
ERTS_MSACC_PUSH_STATE_M();
ERTS_LC_ASSERT(erts_lc_is_port_locked(prt));
- ASSERT(prt->drv_ptr != NULL);
+ ASSERT(prt->drv_ptr != NULL);
+ ASSERT(erts_monitor_is_target(tmon));
+ mdp = erts_monitor_to_data(tmon);
DRV_MONITOR_LOCK_PDL(prt);
- if (erts_lookup_monitor(ERTS_P_MONITORS(prt), ref) == NULL) {
+ if (!erts_monitor_is_in_table(&mdp->origin)) {
DRV_MONITOR_UNLOCK_PDL(prt);
+ erts_monitor_release(tmon);
return;
}
callback = prt->drv_ptr->process_exit;
ASSERT(callback != NULL);
- erts_ref_to_driver_monitor(ref,&drv_monitor);
+ erts_ref_to_driver_monitor(mdp->ref,&drv_monitor);
ERTS_MSACC_SET_STATE_CACHED_M(ERTS_MSACC_STATE_PORT);
DRV_MONITOR_UNLOCK_PDL(prt);
#ifdef USE_VM_PROBES
@@ -7122,11 +6980,9 @@ void erts_fire_port_monitor(Port *prt, Eterm ref)
DRV_MONITOR_LOCK_PDL(prt);
ERTS_MSACC_POP_STATE_M();
/* remove monitor *after* callback */
- rmon = erts_remove_monitor(&ERTS_P_MONITORS(prt), ref);
+ erts_monitor_tree_delete(&ERTS_P_MONITORS(prt), &mdp->origin);
DRV_MONITOR_UNLOCK_PDL(prt);
- if (rmon) {
- erts_destroy_monitor(rmon);
- }
+ erts_monitor_release_both(mdp);
}
@@ -7171,8 +7027,7 @@ driver_failure_term(ErlDrvPort ix, Eterm term, int eof)
int driver_exit(ErlDrvPort ix, int err)
{
Port* prt = erts_drvport2port(ix);
- Process* rp;
- ErtsLink *lnk, *rlnk = NULL;
+ ErtsLink *lnk;
Eterm connected;
ERTS_CHK_NO_PROC_LOCKS;
@@ -7181,22 +7036,10 @@ int driver_exit(ErlDrvPort ix, int err)
return -1;
connected = ERTS_PORT_GET_CONNECTED(prt);
- rp = erts_pid2proc(NULL, 0, connected, ERTS_PROC_LOCK_LINK);
- if (rp) {
- rlnk = erts_remove_link(&ERTS_P_LINKS(rp),prt->common.id);
- }
-
- lnk = erts_remove_link(&ERTS_P_LINKS(prt), connected);
-
- if (rp)
- erts_proc_unlock(rp, ERTS_PROC_LOCK_LINK);
-
- if (rlnk != NULL) {
- erts_destroy_link(rlnk);
- }
-
- if (lnk != NULL) {
- erts_destroy_link(lnk);
+ lnk = erts_link_tree_lookup(ERTS_P_LINKS(prt), connected);
+ if (lnk) {
+ erts_link_tree_delete(&ERTS_P_LINKS(prt), lnk);
+ erts_proc_sig_send_unlink(NULL, lnk);
}
if (err == 0)
@@ -7219,7 +7062,7 @@ int driver_failure_atom(ErlDrvPort ix, char* string)
{
return driver_failure_term(ix,
erts_atom_put((byte *) string,
- strlen(string),
+ sys_strlen(string),
ERTS_ATOM_ENC_LATIN1,
1),
0);
diff --git a/erts/emulator/beam/lttng-wrapper.h b/erts/emulator/beam/lttng-wrapper.h
index 0bc75c1552..e7f2971bf7 100644
--- a/erts/emulator/beam/lttng-wrapper.h
+++ b/erts/emulator/beam/lttng-wrapper.h
@@ -61,13 +61,13 @@
#define lttng_proc_to_mfa_str(p, Name) \
do { \
if (ERTS_PROC_IS_EXITING((p))) { \
- strcpy(Name, "<exiting>"); \
+ sys_strcpy(Name, "<exiting>"); \
} else { \
BeamInstr *_fptr = find_function_from_pc((p)->i); \
if (_fptr) { \
lttng_mfa_to_str(_fptr[0],_fptr[1],_fptr[2], Name); \
} else { \
- strcpy(Name, "<unknown>"); \
+ sys_strcpy(Name, "<unknown>"); \
} \
} \
} while(0)
diff --git a/erts/emulator/beam/msg_instrs.tab b/erts/emulator/beam/msg_instrs.tab
index d6d4d2fb49..6055e35717 100644
--- a/erts/emulator/beam/msg_instrs.tab
+++ b/erts/emulator/beam/msg_instrs.tab
@@ -2,7 +2,7 @@
//
// %CopyrightBegin%
//
-// Copyright Ericsson AB 2017. All Rights Reserved.
+// Copyright Ericsson AB 2017-2018. 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.
@@ -45,23 +45,16 @@
i_recv_mark() {
/*
- * Save the current position in message buffer.
+ * Save the current end of message queue
*/
- c_p->msg.saved_last = c_p->msg.last;
+ ERTS_RECV_MARK_SAVE(c_p);
}
i_recv_set() {
/*
- * If c_p->msg.saved_last is non-zero, it points to the first
- * message that could possibly be matched out.
- *
- * If c_p->msg.saved_last is zero, it means that it was invalidated
- * because another receive was executed before this i_recv_set()
- * instruction was reached.
+ * If previously saved recv mark, set peek position to it
*/
- if (c_p->msg.saved_last) {
- c_p->msg.save = c_p->msg.saved_last;
- }
+ ERTS_RECV_MARK_SET(c_p);
SET_I($NEXT_INSTRUCTION);
goto loop_rec_top__;
//| -no_next
@@ -79,7 +72,6 @@ i_loop_rec(Dest) {
/* Entry point from recv_set */
loop_rec_top__:
- ;
/*
* We need to disable GC while matching messages
@@ -89,34 +81,59 @@ i_loop_rec(Dest) {
ASSERT(!(c_p->flags & F_DELAY_GC));
c_p->flags |= F_DELAY_GC;
- /* Entry point from loop_rec_end */
+ /* Entry point from loop_rec_end (and locally) */
loop_rec__:
+ if (FCALLS <= 0 && FCALLS <= neg_o_reds) {
+ $SET_CP_I_ABS(I);
+ c_p->flags &= ~F_DELAY_GC;
+ SWAPOUT;
+ c_p->arity = 0;
+ c_p->current = NULL;
+ goto do_schedule;
+ }
+
+ ASSERT(!ERTS_PROC_IS_EXITING(c_p));
+
PROCESS_MAIN_CHK_LOCKS(c_p);
msgp = PEEK_MESSAGE(c_p);
- if (!msgp) {
- erts_proc_lock(c_p, ERTS_PROC_LOCKS_MSG_RECEIVE);
- /* Make sure messages wont pass exit signals... */
- if (ERTS_PROC_PENDING_EXIT(c_p)) {
- erts_proc_unlock(c_p, ERTS_PROC_LOCKS_MSG_RECEIVE);
- SWAPOUT;
- c_p->flags &= ~F_DELAY_GC;
- c_p->arity = 0;
- goto do_schedule; /* Will be rescheduled for exit */
- }
- ERTS_MSGQ_MV_INQ2PRIVQ(c_p);
- msgp = PEEK_MESSAGE(c_p);
- if (msgp) {
- erts_proc_unlock(c_p, ERTS_PROC_LOCKS_MSG_RECEIVE);
- } else {
+ if (ERTS_UNLIKELY(msgp == NULL)) {
+ int get_out;
+ SWAPOUT;
+ FCALLS -= erts_proc_sig_receive_helper(c_p, FCALLS, neg_o_reds,
+ &msgp, &get_out);
+ SWAPIN;
+ if (ERTS_UNLIKELY(msgp == NULL)) {
+ if (get_out) {
+ if (get_out < 0) {
+ ASSERT(FCALLS <= 0 && FCALLS <= neg_o_reds);
+ goto loop_rec__; /* yield */
+ }
+ else {
+ ASSERT(ERTS_PROC_IS_EXITING(c_p));
+ goto do_schedule; /* exit */
+ }
+ }
+
+ /*
+ * If there are no more messages in queue
+ * (and we are not yielding or exiting)
+ * erts_proc_sig_receive_helper()
+ * returns with message queue lock locked...
+ */
c_p->flags &= ~F_DELAY_GC;
$SET_I_REL($Dest);
Goto(*I); /* Jump to a wait or wait_timeout instruction */
}
}
- if (is_non_value(ERL_MESSAGE_TERM(msgp))) {
+
+ ASSERT(msgp == PEEK_MESSAGE(c_p));
+ ASSERT(msgp && ERTS_SIG_IS_MSG(msgp));
+
+ if (ERTS_UNLIKELY(ERTS_SIG_IS_EXTERNAL_MSG(msgp))) {
+ FCALLS -= 10; /* FIXME: bump appropriate amount... */
SWAPOUT; /* erts_decode_dist_message() may write to heap... */
if (!erts_decode_dist_message(c_p, ERTS_PROC_LOCK_MAIN, msgp, 0)) {
/*
@@ -127,13 +144,16 @@ i_loop_rec(Dest) {
ASSERT(HTOP == c_p->htop && E == c_p->stop);
/* TODO: Add DTrace probe for this bad message situation? */
UNLINK_MESSAGE(c_p, msgp);
- c_p->msg.saved_last = 0; /* Better safe than sorry. */
msgp->next = NULL;
erts_cleanup_messages(msgp);
goto loop_rec__;
}
SWAPIN;
}
+
+ ASSERT(msgp == PEEK_MESSAGE(c_p));
+ ASSERT(ERTS_SIG_IS_INTERNAL_MSG(msgp));
+
r(0) = ERL_MESSAGE_TERM(msgp);
}
@@ -215,7 +235,7 @@ remove_message() {
}
DTRACE6(message_receive,
receiver_name, size_object(ERL_MESSAGE_TERM(msgp)),
- c_p->msg.len - 1, tok_label, tok_lastcnt, tok_serial);
+ c_p->sig_qs.len - 1, tok_label, tok_lastcnt, tok_serial);
}
#endif
UNLINK_MESSAGE(c_p, msgp);
@@ -252,17 +272,8 @@ loop_rec_end(Dest) {
$SET_I_REL($Dest);
SAVE_MESSAGE(c_p);
- if (FCALLS > 0 || FCALLS > neg_o_reds) {
- FCALLS--;
- goto loop_rec__;
- }
-
- c_p->flags &= ~F_DELAY_GC;
- $SET_CP_I_ABS(I);
- SWAPOUT;
- c_p->arity = 0;
- c_p->current = NULL;
- goto do_schedule;
+ FCALLS--;
+ goto loop_rec__;
}
timeout_locked() {
diff --git a/erts/emulator/beam/ops.tab b/erts/emulator/beam/ops.tab
index 1f4a8eadb0..bc765a8c94 100644
--- a/erts/emulator/beam/ops.tab
+++ b/erts/emulator/beam/ops.tab
@@ -182,6 +182,9 @@ get_list r x y
get_list r y r
get_list r x r
+get_hd xy xy
+get_tl xy xy
+
# Old-style catch.
catch y f
catch_end y
@@ -1410,7 +1413,7 @@ has_map_fields Fail Src Size Rest=* => \
## Transform get_map_elements(s) #{ K1 := V1, K2 := V2 }
-get_map_elements Fail Src=xy Size=u==2 Rest=* => \
+get_map_elements Fail Src Size=u==2 Rest=* => \
gen_get_map_element(Fail, Src, Size, Rest)
get_map_elements Fail Src Size Rest=* | map_key_sort(Size, Rest) => \
gen_get_map_elements(Fail, Src, Size, Rest)
@@ -1420,8 +1423,12 @@ i_get_map_elements f? s I
i_get_map_element Fail Src=xy Key=y Dst => \
move Key x | i_get_map_element Fail Src x Dst
+i_get_map_element_hash Fail Src=c Key Hash Dst => \
+ move Src x | i_get_map_element_hash Fail x Key Hash Dst
i_get_map_element_hash f? xy c I xy
+i_get_map_element Fail Src=c Key Dst => \
+ move Src x | i_get_map_element Fail x Key Dst
i_get_map_element f? xy x xy
#
diff --git a/erts/emulator/beam/packet_parser.c b/erts/emulator/beam/packet_parser.c
index f14910bc72..de1d481105 100644
--- a/erts/emulator/beam/packet_parser.c
+++ b/erts/emulator/beam/packet_parser.c
@@ -200,7 +200,7 @@ static int http_init(void)
for (i = 0; i < HTTP_HDR_HASH_SIZE; i++)
http_hdr_hash[i] = NULL;
for (i = 0; http_hdr_strings[i] != NULL; i++) {
- ASSERT(strlen(http_hdr_strings[i]) <= HTTP_MAX_NAME_LEN);
+ ASSERT(sys_strlen(http_hdr_strings[i]) <= HTTP_MAX_NAME_LEN);
http_hdr_table[i].index = i;
http_hash_insert(http_hdr_strings[i],
&http_hdr_table[i],
@@ -516,7 +516,7 @@ static http_atom_t* http_hash_lookup(const char* name, int len,
while (ap != NULL) {
if ((ap->h == h) && (ap->len == len) &&
- (strncmp(ap->name, name, len) == 0))
+ (sys_strncmp(ap->name, name, len) == 0))
return ap;
ap = ap->next;
}
@@ -656,7 +656,7 @@ int packet_parse_http(const char* buf, int len, int* statep,
if (*statep == 0) {
/* start-line = Request-Line | Status-Line */
- if (n >= 5 && (strncmp(buf, "HTTP/", 5) == 0)) {
+ if (n >= 5 && (sys_strncmp(buf, "HTTP/", 5) == 0)) {
int major = 0;
int minor = 0;
int status = 0;
@@ -750,7 +750,7 @@ int packet_parse_http(const char* buf, int len, int* statep,
}
if (n < 8)
return -1;
- if (strncmp(ptr, "HTTP/", 5) != 0)
+ if (sys_strncmp(ptr, "HTTP/", 5) != 0)
return -1;
ptr += 5;
n -= 5;
diff --git a/erts/emulator/beam/sys.h b/erts/emulator/beam/sys.h
index 290e0b209a..c21acadd8d 100644
--- a/erts/emulator/beam/sys.h
+++ b/erts/emulator/beam/sys.h
@@ -994,18 +994,82 @@ erts_refc_read(erts_refc_t *refcp, erts_aint_t min_val)
return val;
}
-#endif /* #if ERTS_GLB_INLINE_INCL_FUNC_DEF */
+#endif /* #if ERTS_GLB_INLINE_INCL_FUNC_DEF */
+
+
+/* Thin wrappers around memcpy and friends, which should always be used in
+ * place of plain memcpy, memset, etc.
+ *
+ * Passing NULL to any of these functions is undefined behavior even though it
+ * may seemingly work when the length (if any) is zero; a compiler can take
+ * this as a hint that the passed operand may *never* be NULL and then optimize
+ * based on that information.
+ */
+ERTS_GLB_INLINE void *sys_memcpy(void *dest, const void *src, size_t n);
+ERTS_GLB_INLINE void *sys_memmove(void *dest, const void *src, size_t n);
+ERTS_GLB_INLINE int sys_memcmp(const void *s1, const void *s2, size_t n);
+ERTS_GLB_INLINE void *sys_memset(void *s, int c, size_t n);
+ERTS_GLB_INLINE void *sys_memzero(void *s, size_t n);
+ERTS_GLB_INLINE int sys_strcmp(const char *s1, const char *s2);
+ERTS_GLB_INLINE int sys_strncmp(const char *s1, const char *s2, size_t n);
+ERTS_GLB_INLINE char *sys_strcpy(char *dest, const char *src);
+ERTS_GLB_INLINE char *sys_strncpy(char *dest, const char *src, size_t n);
+ERTS_GLB_INLINE size_t sys_strlen(const char *s);
+
+#if ERTS_GLB_INLINE_INCL_FUNC_DEF
-#define sys_memcpy(s1,s2,n) memcpy(s1,s2,n)
-#define sys_memmove(s1,s2,n) memmove(s1,s2,n)
-#define sys_memcmp(s1,s2,n) memcmp(s1,s2,n)
-#define sys_memset(s,c,n) memset(s,c,n)
-#define sys_memzero(s, n) memset(s,'\0',n)
-#define sys_strcmp(s1,s2) strcmp(s1,s2)
-#define sys_strncmp(s1,s2,n) strncmp(s1,s2,n)
-#define sys_strcpy(s1,s2) strcpy(s1,s2)
-#define sys_strncpy(s1,s2,n) strncpy(s1,s2,n)
-#define sys_strlen(s) strlen(s)
+ERTS_GLB_INLINE void *sys_memcpy(void *dest, const void *src, size_t n)
+{
+ ASSERT(dest != NULL && src != NULL);
+ return memcpy(dest,src,n);
+}
+ERTS_GLB_INLINE void *sys_memmove(void *dest, const void *src, size_t n)
+{
+ ASSERT(dest != NULL && src != NULL);
+ return memmove(dest,src,n);
+}
+ERTS_GLB_INLINE int sys_memcmp(const void *s1, const void *s2, size_t n)
+{
+ ASSERT(s1 != NULL && s2 != NULL);
+ return memcmp(s1,s2,n);
+}
+ERTS_GLB_INLINE void *sys_memset(void *s, int c, size_t n)
+{
+ ASSERT(s != NULL);
+ return memset(s,c,n);
+}
+ERTS_GLB_INLINE void *sys_memzero(void *s, size_t n)
+{
+ ASSERT(s != NULL);
+ return memset(s,'\0',n);
+}
+ERTS_GLB_INLINE int sys_strcmp(const char *s1, const char *s2)
+{
+ ASSERT(s1 != NULL && s2 != NULL);
+ return strcmp(s1,s2);
+}
+ERTS_GLB_INLINE int sys_strncmp(const char *s1, const char *s2, size_t n)
+{
+ ASSERT(s1 != NULL && s2 != NULL);
+ return strncmp(s1,s2,n);
+}
+ERTS_GLB_INLINE char *sys_strcpy(char *dest, const char *src)
+{
+ ASSERT(dest != NULL && src != NULL);
+ return strcpy(dest,src);
+
+}
+ERTS_GLB_INLINE char *sys_strncpy(char *dest, const char *src, size_t n)
+{
+ ASSERT(dest != NULL && src != NULL);
+ return strncpy(dest,src,n);
+}
+ERTS_GLB_INLINE size_t sys_strlen(const char *s)
+{
+ ASSERT(s != NULL);
+ return strlen(s);
+}
+#endif /* #if ERTS_GLB_INLINE_INCL_FUNC_DEF */
/* define function symbols (needed in sys_drv_api) */
#define sys_fp_alloc sys_alloc
diff --git a/erts/emulator/beam/utils.c b/erts/emulator/beam/utils.c
index fe9f1c7606..4bf60619ba 100644
--- a/erts/emulator/beam/utils.c
+++ b/erts/emulator/beam/utils.c
@@ -3152,6 +3152,9 @@ tailrecur_ne:
int cmp;
byte* a_ptr;
byte* b_ptr;
+ if (eq_only && a_size != b_size) {
+ RETURN_NEQ(a_size - b_size);
+ }
ERTS_GET_BINARY_BYTES(a, a_ptr, a_bitoffs, a_bitsize);
ERTS_GET_BINARY_BYTES(b, b_ptr, b_bitoffs, b_bitsize);
if ((a_bitsize | b_bitsize | a_bitoffs | b_bitoffs) == 0) {
diff --git a/erts/emulator/hipe/hipe_amd64.c b/erts/emulator/hipe/hipe_amd64.c
index e3cff4a4ba..f23f341e6d 100644
--- a/erts/emulator/hipe/hipe_amd64.c
+++ b/erts/emulator/hipe/hipe_amd64.c
@@ -28,6 +28,7 @@
#include "error.h"
#include "bif.h"
#include "big.h" /* term_to_Sint() */
+#include "erl_binary.h"
#include "hipe_arch.h"
#include "hipe_bif0.h"
@@ -38,6 +39,8 @@
#undef ERL_FUN_SIZE
#include "hipe_literals.h"
+static void patch_trampoline(void *trampoline, void *destAddress);
+
const Uint sse2_fnegate_mask[2] = {0x8000000000000000,0};
void hipe_patch_load_fe(Uint64 *address, Uint64 value)
@@ -52,9 +55,9 @@ int hipe_patch_insn(void *address, Uint64 value, Eterm type)
switch (type) {
case am_closure:
case am_constant:
+ case am_c_const:
*(Uint64*)address = value;
break;
- case am_c_const:
case am_atom:
/* check that value fits in an unsigned imm32 */
/* XXX: are we sure it's not really a signed imm32? */
@@ -71,14 +74,18 @@ int hipe_patch_insn(void *address, Uint64 value, Eterm type)
int hipe_patch_call(void *callAddress, void *destAddress, void *trampoline)
{
- Sint rel32;
+ Sint64 destOffset = (Sint64)destAddress - (Sint64)callAddress - 4;
- ASSERT(trampoline == NULL);
+ if ((destOffset < -0x80000000L) || (destOffset >= 0x80000000L)) {
+ destOffset = (Sint64)trampoline - (Sint64)callAddress - 4;
- rel32 = (Sint)destAddress - (Sint)callAddress - 4;
- if ((Sint)(Sint32)rel32 != rel32)
- return -1;
- *(Uint32*)callAddress = (Uint32)rel32;
+ if ((destOffset < -0x80000000L) || (destOffset >= 0x80000000L))
+ return -1;
+
+ patch_trampoline(trampoline, destAddress);
+ }
+
+ *(Uint32*)callAddress = (Uint32)destOffset;
hipe_flush_icache_word(callAddress);
return 0;
}
@@ -96,12 +103,80 @@ static void *alloc_code(unsigned int alloc_bytes)
return erts_alloc(ERTS_ALC_T_HIPE_EXEC, alloc_bytes);
}
+static int check_callees(Eterm callees)
+{
+ Eterm *tuple;
+ Uint arity;
+ Uint i;
+
+ if (is_not_tuple(callees))
+ return -1;
+ tuple = tuple_val(callees);
+ arity = arityval(tuple[0]);
+ for (i = 1; i <= arity; ++i) {
+ Eterm mfa = tuple[i];
+ if (is_atom(mfa))
+ continue;
+ if (is_not_tuple(mfa) ||
+ tuple_val(mfa)[0] != make_arityval(3) ||
+ is_not_atom(tuple_val(mfa)[1]) ||
+ is_not_atom(tuple_val(mfa)[2]) ||
+ is_not_small(tuple_val(mfa)[3]) ||
+ unsigned_val(tuple_val(mfa)[3]) > 255)
+ return -1;
+ }
+ return arity;
+}
+
+#define TRAMPOLINE_BYTES 12
+
+static void generate_trampolines(unsigned char *address,
+ int nrcallees, Eterm callees,
+ unsigned char **trampvec)
+{
+ unsigned char *trampoline = address;
+ int i;
+
+ for(i = 0; i < nrcallees; ++i) {
+ trampoline[0] = 0x48; /* movabsq $..., %rax; */
+ trampoline[1] = 0xb8;
+ *(void**)(trampoline+2) = NULL; /* callee's address */
+ trampoline[10] = 0xff; /* jmpq *%rax */
+ trampoline[11] = 0xe0;
+ trampvec[i] = trampoline;
+ trampoline += TRAMPOLINE_BYTES;
+ }
+ hipe_flush_icache_range(address, nrcallees*TRAMPOLINE_BYTES);
+}
+
+static void patch_trampoline(void *trampoline, void *destAddress)
+{
+ unsigned char *tp = (unsigned char*) trampoline;
+
+ ASSERT(tp[0] == 0x48 && tp[1] == 0xb8);
+
+ *(void**)(tp+2) = destAddress; /* callee's address */
+ hipe_flush_icache_word(tp+2);
+}
+
void *hipe_alloc_code(Uint nrbytes, Eterm callees, Eterm *trampolines, Process *p)
{
- if (is_not_nil(callees))
+ int nrcallees;
+ Eterm trampvecbin;
+ unsigned char **trampvec;
+ unsigned char *address;
+
+ nrcallees = check_callees(callees);
+ if (nrcallees < 0)
return NULL;
- *trampolines = NIL;
- return alloc_code(nrbytes);
+
+ trampvecbin = new_binary(p, NULL, nrcallees*sizeof(unsigned char*));
+ trampvec = (unsigned char **)binary_bytes(trampvecbin);
+
+ address = alloc_code(nrbytes + nrcallees*TRAMPOLINE_BYTES);
+ generate_trampolines(address + nrbytes, nrcallees, callees, trampvec);
+ *trampolines = trampvecbin;
+ return address;
}
void hipe_free_code(void* code, unsigned int bytes)
@@ -129,10 +204,9 @@ void *hipe_make_native_stub(void *callee_exp, unsigned int beamArity)
*/
unsigned int codeSize;
unsigned char *code, *codep;
- unsigned int callEmuOffset;
- codeSize = /* 23, 26, 29, or 32 bytes */
- 23 + /* 23 when all offsets are 8-bit */
+ codeSize = /* 30, 33, 36, or 39 bytes */
+ 30 + /* 30 when all offsets are 8-bit */
(P_CALLEE_EXP >= 128 ? 3 : 0) +
((P_CALLEE_EXP + 4) >= 128 ? 3 : 0) +
(P_ARITY >= 128 ? 3 : 0);
@@ -197,14 +271,15 @@ void *hipe_make_native_stub(void *callee_exp, unsigned int beamArity)
codep[0] = beamArity;
codep += 1;
- /* jmp callemu; 5 bytes */
- callEmuOffset = (unsigned char*)nbif_callemu - (code + codeSize);
- codep[0] = 0xe9;
- codep[1] = callEmuOffset & 0xFF;
- codep[2] = (callEmuOffset >> 8) & 0xFF;
- codep[3] = (callEmuOffset >> 16) & 0xFF;
- codep[4] = (callEmuOffset >> 24) & 0xFF;
- codep += 5;
+ /* jmp callemu; 12 bytes */
+ codep[0] = 0x48;
+ codep[1] = 0xb8;
+ codep += 2;
+ *(Uint64*)codep = (Uint64)nbif_callemu;
+ codep += 8;
+ codep[0] = 0xff;
+ codep[1] = 0xe0;
+ codep += 2;
ASSERT(codep == code + codeSize);
diff --git a/erts/emulator/hipe/hipe_bif0.c b/erts/emulator/hipe/hipe_bif0.c
index e477c4cdea..a8be64e08d 100644
--- a/erts/emulator/hipe/hipe_bif0.c
+++ b/erts/emulator/hipe/hipe_bif0.c
@@ -1112,7 +1112,7 @@ static struct hipe_mfa_info* mod2mfa_put(struct hipe_mfa_info* mfa)
struct hipe_ref {
struct hipe_ref_head head; /* list of refs to same calleee */
void *address;
-#if defined(__arm__) || defined(__powerpc__) || defined(__ppc__) || defined(__powerpc64__)
+#if defined(__x86_64__) || defined(__arm__) || defined(__powerpc__) || defined(__ppc__) || defined(__powerpc64__)
void *trampoline;
#endif
unsigned int flags;
@@ -1543,7 +1543,7 @@ BIF_RETTYPE hipe_bifs_add_ref_2(BIF_ALIST_2)
ref = erts_alloc(ERTS_ALC_T_HIPE_LL, sizeof(struct hipe_ref));
ref->address = address;
-#if defined(__arm__) || defined(__powerpc__) || defined(__ppc__) || defined(__powerpc64__)
+#if defined(__x86_64__) || defined(__arm__) || defined(__powerpc__) || defined(__ppc__) || defined(__powerpc64__)
ref->trampoline = trampoline;
#endif
ref->flags = flags;
@@ -1819,7 +1819,7 @@ void hipe_redirect_to_module(Module* modp)
if (ref->flags & REF_FLAG_IS_LOAD_MFA)
res = hipe_patch_insn(ref->address, (Uint)p->remote_address, am_load_mfa);
else {
-#if defined(__arm__) || defined(__powerpc__) || defined(__ppc__) || defined(__powerpc64__)
+#if defined(__x86_64__) || defined(__arm__) || defined(__powerpc__) || defined(__ppc__) || defined(__powerpc64__)
void* trampoline = ref->trampoline;
#else
void* trampoline = NULL;
diff --git a/erts/emulator/hipe/hipe_gc.c b/erts/emulator/hipe/hipe_gc.c
index 1a4a4c7952..aaedba1afd 100644
--- a/erts/emulator/hipe/hipe_gc.c
+++ b/erts/emulator/hipe/hipe_gc.c
@@ -91,7 +91,7 @@ Eterm *fullsweep_nstack(Process *p, Eterm *n_htop)
ASSERT(is_boxed(val));
*nsp_i = val;
} else if (!erts_is_literal(gval, ptr)) {
- move_boxed(&ptr, val, &n_htop, nsp_i);
+ move_boxed(ptr, val, &n_htop, nsp_i);
}
} else if (is_list(gval)) {
Eterm *ptr = list_val(gval);
@@ -100,7 +100,7 @@ Eterm *fullsweep_nstack(Process *p, Eterm *n_htop)
*nsp_i = ptr[1];
} else if (!erts_is_literal(gval, ptr)) {
ASSERT(erts_dbg_within_proc(ptr, p, NULL));
- move_cons(&ptr, val, &n_htop, nsp_i);
+ move_cons(ptr, val, &n_htop, nsp_i);
}
}
}
@@ -206,10 +206,10 @@ void gensweep_nstack(Process *p, Eterm **ptr_old_htop, Eterm **ptr_n_htop)
ASSERT(is_boxed(val));
*nsp_i = val;
} else if (ErtsInArea(ptr, mature, mature_size)) {
- move_boxed(&ptr, val, &old_htop, nsp_i);
+ move_boxed(ptr, val, &old_htop, nsp_i);
} else if (ErtsInYoungGen(gval, ptr, oh, oh_size)) {
ASSERT(erts_dbg_within_proc(ptr, p, NULL));
- move_boxed(&ptr, val, &n_htop, nsp_i);
+ move_boxed(ptr, val, &n_htop, nsp_i);
}
} else if (is_list(gval)) {
Eterm *ptr = list_val(gval);
@@ -217,10 +217,10 @@ void gensweep_nstack(Process *p, Eterm **ptr_old_htop, Eterm **ptr_n_htop)
if (IS_MOVED_CONS(val)) {
*nsp_i = ptr[1];
} else if (ErtsInArea(ptr, mature, mature_size)) {
- move_cons(&ptr, val, &old_htop, nsp_i);
+ move_cons(ptr, val, &old_htop, nsp_i);
} else if (ErtsInYoungGen(gval, ptr, oh, oh_size)) {
ASSERT(erts_dbg_within_proc(ptr, p, NULL));
- move_cons(&ptr, val, &n_htop, nsp_i);
+ move_cons(ptr, val, &n_htop, nsp_i);
}
}
}
@@ -278,7 +278,7 @@ Eterm *sweep_literals_nstack(Process *p, Eterm *old_htop, char *area,
ASSERT(is_boxed(val));
*nsp_i = val;
} else if (ErtsInArea(ptr, area, area_size)) {
- move_boxed(&ptr, val, &old_htop, nsp_i);
+ move_boxed(ptr, val, &old_htop, nsp_i);
}
} else if (is_list(gval)) {
Eterm *ptr = list_val(gval);
@@ -286,7 +286,7 @@ Eterm *sweep_literals_nstack(Process *p, Eterm *old_htop, char *area,
if (IS_MOVED_CONS(val)) {
*nsp_i = ptr[1];
} else if (ErtsInArea(ptr, area, area_size)) {
- move_cons(&ptr, val, &old_htop, nsp_i);
+ move_cons(ptr, val, &old_htop, nsp_i);
}
}
}
diff --git a/erts/emulator/hipe/hipe_mkliterals.c b/erts/emulator/hipe/hipe_mkliterals.c
index 3323e8640b..74e793577c 100644
--- a/erts/emulator/hipe/hipe_mkliterals.c
+++ b/erts/emulator/hipe/hipe_mkliterals.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2001-2016. All Rights Reserved.
+ * Copyright Ericsson AB 2001-2018. 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.
@@ -519,8 +519,8 @@ static const struct rts_param rts_params[] = {
1, offsetof(struct process, hipe.bif_callee)
#endif
},
- { 49, "P_MSG_FIRST", 1, offsetof(struct process, msg.first) },
- { 50, "P_MSG_SAVE", 1, offsetof(struct process, msg.save) },
+ { 49, "P_MSG_FIRST", 1, offsetof(struct process, sig_qs.first) },
+ { 50, "P_MSG_SAVE", 1, offsetof(struct process, sig_qs.save) },
{ 51, "P_CALLEE_EXP", 1, offsetof(struct process, hipe.u.callee_exp) },
{ 52, "THE_NON_VALUE", 1, (int)THE_NON_VALUE },
@@ -531,8 +531,8 @@ static const struct rts_param rts_params[] = {
#endif
},
- { 54, "P_MSG_LAST", 1, offsetof(struct process, msg.last) },
- { 55, "P_MSG_SAVED_LAST", 1, offsetof(struct process, msg.saved_last) },
+ { 54, "P_MSG_LAST", 1, offsetof(struct process, sig_qs.last) },
+ { 55, "P_MSG_SAVED_LAST", 1, offsetof(struct process, sig_qs.saved_last) },
};
#define NR_PARAMS ARRAY_SIZE(rts_params)
diff --git a/erts/emulator/hipe/hipe_mode_switch.c b/erts/emulator/hipe/hipe_mode_switch.c
index 8b497c9970..bc9a700204 100644
--- a/erts/emulator/hipe/hipe_mode_switch.c
+++ b/erts/emulator/hipe/hipe_mode_switch.c
@@ -496,8 +496,10 @@ Process *hipe_mode_switch(Process *p, unsigned cmd, Eterm reg[])
erts_proc_lock(p, ERTS_PROC_LOCKS_MSG_RECEIVE);
p->i = hipe_beam_pc_resume;
p->arity = 0;
- erts_atomic32_read_band_relb(&p->state,
- ~ERTS_PSFLG_ACTIVE);
+ if (erts_atomic32_read_nob(&p->state) & ERTS_PSFLG_EXITING)
+ ASSERT(erts_atomic32_read_nob(&p->state) & ERTS_PSFLG_ACTIVE);
+ else
+ erts_atomic32_read_band_relb(&p->state, ~ERTS_PSFLG_ACTIVE);
erts_proc_unlock(p, ERTS_PROC_LOCKS_MSG_RECEIVE);
do_schedule:
{
@@ -662,7 +664,8 @@ void hipe_inc_nstack(Process *p)
Eterm *new_nstack = erts_alloc(ERTS_ALC_T_HIPE_STK, new_size*sizeof(Eterm));
unsigned used_size = p->hipe.nstend - p->hipe.nsp;
- sys_memcpy(new_nstack+new_size-used_size, p->hipe.nsp, used_size*sizeof(Eterm));
+ if (used_size)
+ sys_memcpy(new_nstack+new_size-used_size, p->hipe.nsp, used_size*sizeof(Eterm));
if (p->hipe.nstgraylim)
p->hipe.nstgraylim = new_nstack + new_size - (p->hipe.nstend - p->hipe.nstgraylim);
if (p->hipe.nstblacklim)
diff --git a/erts/emulator/hipe/hipe_native_bif.c b/erts/emulator/hipe/hipe_native_bif.c
index 498b43ac6b..cf8c4139be 100644
--- a/erts/emulator/hipe/hipe_native_bif.c
+++ b/erts/emulator/hipe/hipe_native_bif.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2001-2017. All Rights Reserved.
+ * Copyright Ericsson AB 2001-2018. 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.
@@ -35,6 +35,7 @@
#include "hipe_native_bif.h"
#include "hipe_arch.h"
#include "hipe_stack.h"
+#include "erl_proc_sig_queue.h"
/*
* These are wrappers for BIFs that may trigger a native
@@ -254,7 +255,7 @@ void hipe_handle_exception(Process *c_p)
/* Synthesized to avoid having to generate code for it. */
c_p->def_arg_reg[0] = exception_tag[GET_EXC_CLASS(c_p->freason)];
- c_p->msg.saved_last = 0; /* No longer safe to use this position */
+ ERTS_RECV_MARK_CLEAR(c_p); /* No longer safe to use this position */
hipe_find_handler(c_p);
}
@@ -544,38 +545,57 @@ Eterm hipe_check_get_msg(Process *c_p)
msgp = PEEK_MESSAGE(c_p);
if (!msgp) {
- erts_proc_lock(c_p, ERTS_PROC_LOCKS_MSG_RECEIVE);
- /* Make sure messages wont pass exit signals... */
- if (ERTS_PROC_PENDING_EXIT(c_p)) {
- erts_proc_unlock(c_p, ERTS_PROC_LOCKS_MSG_RECEIVE);
- return THE_NON_VALUE; /* Will be rescheduled for exit */
- }
- ERTS_MSGQ_MV_INQ2PRIVQ(c_p);
- msgp = PEEK_MESSAGE(c_p);
- if (msgp)
- erts_proc_unlock(c_p, ERTS_PROC_LOCKS_MSG_RECEIVE);
- else {
- /* XXX: BEAM doesn't need this */
- c_p->hipe_smp.have_receive_locks = 1;
- c_p->flags &= ~F_DELAY_GC;
- return THE_NON_VALUE;
- }
+ int get_out;
+ (void) erts_proc_sig_receive_helper(c_p, CONTEXT_REDS, 0,
+ &msgp, &get_out);
+ /* FIXME: Need to bump reductions... */
+ if (!msgp) {
+ if (get_out) {
+ if (get_out < 0) {
+ /*
+ * FIXME: We should get out yielding
+ * here...
+ */
+ goto next_message;
+ }
+ /* Go exit... */
+ return THE_NON_VALUE;
+ }
+
+ /*
+ * If there are no more messages in queue
+ * (and we are not yielding or exiting)
+ * erts_proc_sig_receive_helper()
+ * returns with message queue lock locked...
+ */
+
+ /* XXX: BEAM doesn't need this */
+ c_p->hipe_smp.have_receive_locks = 1;
+ c_p->flags &= ~F_DELAY_GC;
+ return THE_NON_VALUE;
+ }
}
- if (is_non_value(ERL_MESSAGE_TERM(msgp))
- && !erts_decode_dist_message(c_p, ERTS_PROC_LOCK_MAIN, msgp, 0)) {
- /*
- * A corrupt distribution message that we weren't able to decode;
- * remove it...
- */
- ASSERT(!msgp->data.attached);
- UNLINK_MESSAGE(c_p, msgp);
- msgp->next = NULL;
- erts_cleanup_messages(msgp);
- goto next_message;
+ ASSERT(msgp == PEEK_MESSAGE(c_p));
+ ASSERT(msgp && ERTS_SIG_IS_MSG(msgp));
+
+ if (ERTS_SIG_IS_EXTERNAL_MSG(msgp)) {
+ /* FIXME: bump appropriate amount... */
+ if (!erts_decode_dist_message(c_p, ERTS_PROC_LOCK_MAIN, msgp, 0)) {
+ /*
+ * A corrupt distribution message that we weren't able to decode;
+ * remove it...
+ */
+ /* TODO: Add DTrace probe for this bad message situation? */
+ UNLINK_MESSAGE(c_p, msgp);
+ msgp->next = NULL;
+ erts_cleanup_messages(msgp);
+ goto next_message;
+ }
}
- ASSERT(is_value(ERL_MESSAGE_TERM(msgp)));
+ ASSERT(msgp == PEEK_MESSAGE(c_p));
+ ASSERT(ERTS_SIG_IS_INTERNAL_MSG(msgp));
return ERL_MESSAGE_TERM(msgp);
}
diff --git a/erts/emulator/internal_doc/CarrierMigration.md b/erts/emulator/internal_doc/CarrierMigration.md
index 2a9594db25..3a796d11b7 100644
--- a/erts/emulator/internal_doc/CarrierMigration.md
+++ b/erts/emulator/internal_doc/CarrierMigration.md
@@ -3,17 +3,17 @@ Carrier Migration
The ERTS memory allocators manage memory blocks in two types of raw
memory chunks. We call these chunks of raw memory
-*carriers*. Singleblock carriers which only contain one large block,
-and multiblock carriers which contain multiple blocks. A carrier is
+*carriers*. Single-block carriers which only contain one large block,
+and multi-block carriers which contain multiple blocks. A carrier is
typically created using `mmap()` on unix systems. However, how a
carrier is created is of minor importance. An allocator instance
-typically manages a mixture of single- and multiblock carriers.
+typically manages a mixture of single- and multi-block carriers.
Problem
-------
When a carrier is empty, i.e. contains only one large free block, it
-is deallocated. Since multiblock carriers can contain both allocated
+is deallocated. Since multi-block carriers can contain both allocated
blocks and free blocks at the same time, an allocator instance might
be stuck with a large amount of poorly utilized carriers if the memory
load decreases. After a peak in memory usage it is expected that not
@@ -23,9 +23,9 @@ can usually be reused if the memory load increases again. However,
since each scheduler thread manages its own set of allocator
instances, and memory load is not necessarily correlated to CPU load, we
might get into a situation where there are lots of poorly utilized
-multiblock carriers on some allocator instances while we need to
-allocate new multiblock carriers on other allocator instances. In
-scenarios like this, the demand for multiblock carriers in the system
+multi-block carriers on some allocator instances while we need to
+allocate new multi-block carriers on other allocator instances. In
+scenarios like this, the demand for multi-block carriers in the system
might increase at the same time as the actual memory demand in the
system has decreased which is both unwanted and quite unexpected for
the end user.
@@ -34,7 +34,7 @@ Solution
--------
In order to prevent scenarios like this we've implemented support for
-migration of multiblock carriers between allocator instances of the
+migration of multi-block carriers between allocator instances of the
same type.
### Management of Free Blocks ###
@@ -44,7 +44,7 @@ and add it to another we need to be able to move references to the
free blocks of the carrier between the allocator instances. The
allocator instance specific data structure referring to the free
blocks it manages often refers to the same carrier from multiple
-places. For example, when the address order bestfit strategy is used
+places. For example, when the address order best-fit strategy is used
this data structure is a binary search tree spanning all carriers that
the allocator instance manages. Free blocks in one specific carrier
can be referred to from potentially every other carrier that is
@@ -135,7 +135,7 @@ carriers between scheduler specific allocator instances of the same
allocator type.
Each allocator instance keeps track of the current utilization of its
-multiblock carriers. When the total utilization falls below the "abandon
+multi-block carriers. When the total utilization falls below the "abandon
carrier utilization limit" it starts to inspect the utilization of the
current carrier when deallocations are made. If also the utilization
of the carrier falls below the "abandon carrier utilization limit" it
@@ -144,31 +144,45 @@ and inserts the carrier into the pool.
Since the carrier has been unlinked from the data structure of
available free blocks, no more allocations will be made in the
-carrier. The allocator instance putting the carrier into the pool,
-however, still has the responsibility of performing deallocations in
-it while it remains in the pool. The allocator instance with this
-deallocation responsibility is here called the **employer**.
-
-Each carrier has a flag field containing information about the
-employing allocator instance, a flag indicating if the carrier is in
-the pool or not, and a flag indicating if it is busy or not. When the
-carrier is in the pool, the employing allocator instance needs to mark it
-as busy while operating on it. If another thread inspects it in order
-to try to fetch it from the pool, it will skip it if it is busy. When
-fetching the carrier from the pool, employment will change and further
+carrier.
+
+The allocator instance that created a carrier is called its **owner**.
+Ownership never changes.
+
+The allocator instance that has the responsibility to perform deallocations in a
+carrier is called its **employer**. The employer may also perform allocations if
+the carrier is not in the pool. Employment may change when a carrier is fetched from
+or inserted into the pool.
+
+Deallocations in a carrier, while it remains in the pool, is always performed
+the owner. That is, all pooled carriers are employed by their owners.
+
+Each carrier has an atomic word containing a pointer to the employing allocator
+instance and three bit flags; IN_POOL, BUSY and HOMECOMING.
+
+When fetching a carrier from the pool, employment may change and further
deallocations in the carrier will be redirected to the new
employer using the delayed dealloc functionality.
-If a carrier in the pool becomes empty, it will be withdrawn from the
-pool. All carriers that become empty are also always passed to its
-**owning** allocator instance for deallocation using the delayed
-dealloc functionality. Since carriers this way always will be
-deallocated by the owner that allocated the carrier, the
+When a foreign allocator instance abandons a carrier back into the pool, it will
+also pass it back to its **owner** using the delayed dealloc queue. When doing
+this it will set the HOMECOMING bit flag to mark it as "enqueued". The owner
+will later clear the HOMECOMING bit when the carrier is dequeued. This mechanism
+prevents a carrier from being enqueued again before it has been dequeued.
+
+When a carrier becomes empty, it will be deallocated. Carrier deallocation is
+always done by the owner that allocated the carrier. By doing this, the
underlying functionality of allocating and deallocating carriers can
remain simple and doesn't have to bother about multiple threads. In a
NUMA system we will also not mix carriers originating from multiple
NUMA nodes.
+If a carrier in the pool becomes empty, it will be withdrawn from the
+pool and be deallocated by the owner which already employs it.
+
+If a carrier employed by a foreign allocator becomes empty, it will be passed
+back to the owner for deallocation using the delayed dealloc functionality.
+
In short:
* The allocator instance that created a carrier **owns** it.
@@ -177,34 +191,31 @@ In short:
* The allocator instance that uses a carrier **employs** it.
* An **employer** can abandon a carrier into the pool.
* Pooled carriers are not allocated from.
-* Deallocation in a pooled carrier is still performed by its **employer**.
-* **Employment** can only change when a carrier is fetched from the pool.
+* Pooled carriers are always **employed** by their **owner**.
+* **Employment** can only change from **owner** to a foreign allocator
+ when a carrier is fetched from the pool.
+
### Searching the pool ###
+When an allocator instance needs more carrier space, it inspects the pool. If no
+carrier could be fetched from the pool, it will allocate a new
+carrier. Regardless of where the allocator instance gets the carrier from, it
+just links in the carrier into its data structure of free blocks.
+
To harbor real time characteristics, searching the pool is
limited. We only inspect a limited number of carriers. If none of
those carriers had a free block large enough to satisfy the allocation
-request, the search will fail. A carrier in the pool can also be busy
+request, the search will fail. A carrier in the pool can also be BUSY
if another thread is currently doing block deallocation work on the
-carrier. A busy carrier will also be skipped by the search as it can
+carrier. A BUSY carrier will also be skipped by the search as it can
not satisfy the request. The pool is lock-free and we do not want to
block, waiting for the other thread to finish.
-#### Before OTP 17.4 ####
+### The bad cluster problem ###
-When an allocator instance needs more carrier space, it always begins
-by inspecting its own carriers that are waiting for thread progress
-before they can be deallocated. If no such carrier could be found, it
-then inspects the pool. If no carrier could be fetched from the pool,
-it will allocate a new carrier. Regardless of where the allocator
-instance gets the carrier from it the just links in the carrier into
-its data structure of free blocks.
-
-#### After OTP 17.4 ####
-
-The old search algorithm had a problem as the search always started at
-the same position in the pool, the sentinel. This could lead to
+Before OTP-17.4 the search algorithm had a problem as the search always started
+at the same position in the pool, the sentinel. This could lead to
contention from concurrent searching processes. But even worse, it
could lead to a "bad" state when searches fail with a high rate
leading to new carriers instead being allocated. These new carriers
@@ -236,26 +247,27 @@ The result is that we prefer carriers created by the thread itself,
which is good for NUMA performance. And we get more entry points when
searching the pool, which will ease contention and clustering.
+### Our own pooled tree ###
+
To do the first search among own carriers, every allocator instance
-has two new lists: `pooled_list` and `traitor_list`. These lists are only
-accessed by the allocator itself and they only contain the allocator's
-own carriers. When an owned carrier is abandoned and put in the
-pool, it is also linked into `pooled_list`. When we search our
-`pooled_list` and find a carrier that is no longer in the pool, we
-move that carrier from `pooled_list` to `traitor_list` as it is now
-employed by another allocator. If searching `pooled_list` fails, we
-also do a limited search of `traitor_list`. When finding an abandoned
-carrier in `traitor_list` it is either employed or moved back to
-`pooled_list` if it could not satisfy the allocation request.
-
-When searching `pooled_list` and `traitor_list` we always start at the
-point where the last search ended. This to avoid clustering
-problems and increase the probability to find a "good" carrier. As
-`pooled_list` and `traitor_list` are only accessed by the owning
-allocator instance, they need no thread synchronization at all.
+has a `pooled_tree` of carriers. This tree is only accessed by the allocator
+itself and can only contain its own carriers. When a carrier is
+abandoned and put in the pool, it is also inserted into `pooled_tree`. This is
+either done direct, if the carrier was already employed by its owner, or by
+first passing it back to the owner via the delayed dealloc queue.
+
+When we search our `pooled_tree` and find a carrier that is no longer in the
+pool, we remove that carrier from `pooled_tree` and mark it as TRAITOR, as it is
+now employed by a foreign allocator. We will not find any carriers in
+`pooled_tree` that are marked as BUSY by other threads.
+
+If no carrier in `pooled_tree` had a large enough free block, we search it again
+to find any carrier that may act as an entry point into the shared list of all
+pooled carriers. This in order to, if possible, avoid starting at the sentinel
+and thereby ease the "bad clustering" problem.
Furthermore, the search for own carriers that are scheduled
-for deallocation is now done as the last search option. The idea is
+for deallocation is done as the last search option. The idea is
that it is better to reuse a poorly utilized carrier than to
resurrect an empty carrier that was just about to be released back to
the OS.
@@ -271,14 +283,14 @@ load did not.
When using the `aoffcaobf` or `aoff` strategies compared to `gf` or
`bf`, we loose some performance since we get more modifications in the
data structure of free blocks. This performance penalty is however
-reduced using the `aoffcbf` strategy. A tradeoff between memory
+reduced using the `aoffcbf` strategy. A trade off between memory
consumption and performance is however inevitable, and it is up to
the user to decide what is most important.
Further work
------------
-It would be quite easy to extend this to allow migration of multiblock
+It would be quite easy to extend this to allow migration of multi-block
carriers between all allocator types. More or less the only obstacle
is maintenance of the statistics information.
diff --git a/erts/emulator/nifs/common/prim_file_nif.c b/erts/emulator/nifs/common/prim_file_nif.c
index 6874f41d75..bbd9becb47 100644
--- a/erts/emulator/nifs/common/prim_file_nif.c
+++ b/erts/emulator/nifs/common/prim_file_nif.c
@@ -891,10 +891,10 @@ static ERL_NIF_TERM set_owner_nif(ErlNifEnv *env, int argc, const ERL_NIF_TERM a
posix_errno_t posix_errno;
efile_path_t path;
- Uint32 uid, gid;
+ Sint32 uid, gid;
- if(argc != 3 || !enif_get_uint(env, argv[1], &uid)
- || !enif_get_uint(env, argv[2], &gid)) {
+ if(argc != 3 || !enif_get_int(env, argv[1], &uid)
+ || !enif_get_int(env, argv[2], &gid)) {
return enif_make_badarg(env);
}
diff --git a/erts/emulator/nifs/common/prim_file_nif.h b/erts/emulator/nifs/common/prim_file_nif.h
index cc9bc8f5c3..4194cdc7d9 100644
--- a/erts/emulator/nifs/common/prim_file_nif.h
+++ b/erts/emulator/nifs/common/prim_file_nif.h
@@ -177,7 +177,7 @@ posix_errno_t efile_set_permissions(const efile_path_t *path, Uint32 permissions
/** @brief On Unix, this will set the owner/group to the given values. It will
* do nothing on other platforms. */
-posix_errno_t efile_set_owner(const efile_path_t *path, Uint32 owner, Uint32 group);
+posix_errno_t efile_set_owner(const efile_path_t *path, Sint32 owner, Sint32 group);
/** @brief Resolves the final path of the given link. */
posix_errno_t efile_read_link(ErlNifEnv *env, const efile_path_t *path, ERL_NIF_TERM *result);
diff --git a/erts/emulator/nifs/unix/unix_prim_file.c b/erts/emulator/nifs/unix/unix_prim_file.c
index 57c8ef62e1..1637f9cb71 100644
--- a/erts/emulator/nifs/unix/unix_prim_file.c
+++ b/erts/emulator/nifs/unix/unix_prim_file.c
@@ -125,24 +125,11 @@ static int open_file_type_check(const efile_path_t *path, int fd) {
* immediately in a read within the call, but the new implementation
* never does that. */
return 1;
- } else {
- /* The old driver tolerated opening /dev/null despite the "no devices"
- * limitation. It provided no explanation for this but we still need
- * to match the behavior. We're checking through stat(2) instead of
- * comparing the name to account for links. */
- struct stat null_device_info;
- int is_dev_null;
-
- is_dev_null = (stat("/dev/null", &null_device_info) == 0);
- is_dev_null &= (file_info.st_ino == null_device_info.st_ino);
- is_dev_null &= (file_info.st_dev == null_device_info.st_dev);
-
- if(is_dev_null) {
- return 1;
- }
}
- if(!S_ISREG(file_info.st_mode)) {
+ /* Allow everything that isn't a directory, and error out on the next call
+ * if it's unsupported. */
+ if(S_ISDIR(file_info.st_mode)) {
return 0;
}
@@ -700,7 +687,7 @@ posix_errno_t efile_set_permissions(const efile_path_t *path, Uint32 permissions
return 0;
}
-posix_errno_t efile_set_owner(const efile_path_t *path, Uint32 owner, Uint32 group) {
+posix_errno_t efile_set_owner(const efile_path_t *path, Sint32 owner, Sint32 group) {
if(chown((const char*)path->data, owner, group) < 0) {
return errno;
}
diff --git a/erts/emulator/nifs/win32/win_prim_file.c b/erts/emulator/nifs/win32/win_prim_file.c
index 9b79182f2c..8058350b25 100644
--- a/erts/emulator/nifs/win32/win_prim_file.c
+++ b/erts/emulator/nifs/win32/win_prim_file.c
@@ -801,7 +801,7 @@ posix_errno_t efile_set_permissions(const efile_path_t *path, Uint32 permissions
return windows_to_posix_errno(GetLastError());
}
-posix_errno_t efile_set_owner(const efile_path_t *path, Uint32 owner, Uint32 group) {
+posix_errno_t efile_set_owner(const efile_path_t *path, Sint32 owner, Sint32 group) {
(void)path;
(void)owner;
(void)group;
diff --git a/erts/emulator/sys/common/erl_check_io.c b/erts/emulator/sys/common/erl_check_io.c
index f93d4c1557..6d531fdb76 100644
--- a/erts/emulator/sys/common/erl_check_io.c
+++ b/erts/emulator/sys/common/erl_check_io.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2006-2017. All Rights Reserved.
+ * Copyright Ericsson AB 2006-2018. 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.
@@ -885,7 +885,7 @@ enif_select(ErlNifEnv* env,
ErtsDrvSelectDataState *free_select = NULL;
ErtsNifSelectDataState *free_nif = NULL;
- ASSERT(!(resource->monitors && resource->monitors->is_dying));
+ ASSERT(!resource->monitors);
#ifdef ERTS_SYS_CONTINOUS_FD_NUMBERS
if (!grow_drv_ev_state(fd)) {
diff --git a/erts/emulator/sys/common/erl_mmap.c b/erts/emulator/sys/common/erl_mmap.c
index a9c6e72c5f..5dadd8a5a6 100644
--- a/erts/emulator/sys/common/erl_mmap.c
+++ b/erts/emulator/sys/common/erl_mmap.c
@@ -296,11 +296,10 @@ typedef struct {
}ErtsFreeSegMap;
struct ErtsMemMapper_ {
- int (*reserve_physical)(char *, UWord, int exec);
+ int (*reserve_physical)(char *, UWord);
void (*unreserve_physical)(char *, UWord);
int supercarrier;
int no_os_mmap;
- int executable; /* is client a native code allocator? */
/*
* Super unaligned area is located above super aligned
* area. That is, `sa.bot` is beginning of the super
@@ -358,10 +357,6 @@ char* erts_literals_start;
UWord erts_literals_size;
#endif
-#ifdef ERTS_HAVE_EXEC_MMAPPER
-ErtsMemMapper erts_exec_mmapper;
-#endif
-
#define ERTS_MMAP_SIZE_SC_SA_INC(SZ) \
do { \
@@ -1240,7 +1235,6 @@ Eterm build_free_seg_list(Process* p, ErtsFreeSegMap* map)
#if HAVE_MMAP
# define ERTS_MMAP_PROT (PROT_READ|PROT_WRITE)
-# define ERTS_MMAP_PROT_EXEC (PROT_READ|PROT_WRITE|PROT_EXEC)
# if defined(MAP_ANONYMOUS)
# define ERTS_MMAP_FLAGS (MAP_ANON|MAP_PRIVATE)
# define ERTS_MMAP_FD (-1)
@@ -1254,26 +1248,24 @@ Eterm build_free_seg_list(Process* p, ErtsFreeSegMap* map)
#endif
static ERTS_INLINE void *
-os_mmap(void *hint_ptr, UWord size, int try_superalign, int executable)
+os_mmap(void *hint_ptr, UWord size, int try_superalign)
{
#if HAVE_MMAP
- const int prot = executable ? ERTS_MMAP_PROT_EXEC : ERTS_MMAP_PROT;
void *res;
#ifdef MAP_ALIGN
if (try_superalign)
- res = mmap((void *) ERTS_SUPERALIGNED_SIZE, size, prot,
+ res = mmap((void *) ERTS_SUPERALIGNED_SIZE, size, ERTS_MMAP_PROT,
ERTS_MMAP_FLAGS|MAP_ALIGN, ERTS_MMAP_FD, 0);
else
#endif
- res = mmap((void *) hint_ptr, size, prot,
+ res = mmap((void *) hint_ptr, size, ERTS_MMAP_PROT,
ERTS_MMAP_FLAGS, ERTS_MMAP_FD, 0);
if (res == MAP_FAILED)
return NULL;
return res;
#elif HAVE_VIRTUALALLOC
- const DWORD prot = executable ? PAGE_EXECUTE_READWRITE : PAGE_READWRITE;
return (void *) VirtualAlloc(NULL, (SIZE_T) size,
- MEM_COMMIT|MEM_RESERVE, prot);
+ MEM_COMMIT|MEM_RESERVE, PAGE_READWRITE);
#else
# error "missing mmap() or similar"
#endif
@@ -1330,7 +1322,6 @@ os_mremap(void *ptr, UWord old_size, UWord new_size, int try_superalign)
#if HAVE_MMAP
#define ERTS_MMAP_RESERVE_PROT (ERTS_MMAP_PROT)
-#define ERTS_MMAP_RESERVE_PROT_EXEC (ERTS_MMAP_PROT_EXEC)
#define ERTS_MMAP_RESERVE_FLAGS (ERTS_MMAP_FLAGS|MAP_FIXED)
#define ERTS_MMAP_UNRESERVE_PROT (PROT_NONE)
#if defined(__FreeBSD__)
@@ -1346,10 +1337,9 @@ os_mremap(void *ptr, UWord old_size, UWord new_size, int try_superalign)
#endif /* __FreeBSD__ */
static int
-os_reserve_physical(char *ptr, UWord size, int exec)
+os_reserve_physical(char *ptr, UWord size)
{
- const int prot = exec ? ERTS_MMAP_RESERVE_PROT_EXEC : ERTS_MMAP_RESERVE_PROT;
- void *res = mmap((void *) ptr, (size_t) size, prot,
+ void *res = mmap((void *) ptr, (size_t) size, ERTS_MMAP_RESERVE_PROT,
ERTS_MMAP_RESERVE_FLAGS, ERTS_MMAP_FD, 0);
if (res == (void *) MAP_FAILED)
return 0;
@@ -1366,37 +1356,11 @@ os_unreserve_physical(char *ptr, UWord size)
}
static void *
-os_mmap_virtual(char *ptr, UWord size, int exec)
+os_mmap_virtual(char *ptr, UWord size)
{
int flags = ERTS_MMAP_VIRTUAL_FLAGS;
void* res;
-#ifdef ERTS_ALC_A_EXEC
- if (exec) {
- ASSERT(!ptr);
- /* OTP-19.0: Nice hack below cut-and-pasted from hipe_amd64.c */
-
-# ifdef MAP_32BIT
- /* If we got MAP_32BIT (Linux), then use that to ask for low memory */
- flags |= MAP_32BIT;
-# else
- /* FreeBSD doesn't have MAP_32BIT, and it doesn't respect
- a plain map_hint (returns high mappings even though the
- hint refers to a free area), so we have to use both map_hint
- and MAP_FIXED to get addresses below the 2GB boundary.
- This is even worse than the Linux/ppc64 case.
- Similarly, Solaris 10 doesn't have MAP_32BIT,
- and it doesn't respect a plain map_hint. */
- ptr = (char*)(512*1024*1024); /* 0.5GB */
-
-# if defined(__FreeBSD__) || defined(__sun__)
- flags |= MAP_FIXED;
-# endif
-# endif /* !MAP_32BIT */
- }
-#else /* !ERTS_ALC_A_EXEC */
- ASSERT(!exec);
-#endif
res = mmap((void *) ptr, (size_t) size, ERTS_MMAP_VIRTUAL_PROT,
flags, ERTS_MMAP_FD, 0);
if (res == (void *) MAP_FAILED)
@@ -1411,7 +1375,7 @@ os_mmap_virtual(char *ptr, UWord size, int exec)
#endif /* ERTS_HAVE_OS_MMAP */
-static int reserve_noop(char *ptr, UWord size, int exec)
+static int reserve_noop(char *ptr, UWord size)
{
#ifdef ERTS_MMAP_DEBUG_FILL_AREAS
Uint32 *uip, *end = (Uint32 *) (ptr + size);
@@ -1454,7 +1418,7 @@ alloc_desc_insert_free_seg(ErtsMemMapper* mm,
#if ERTS_HAVE_OS_MMAP
if (!mm->no_os_mmap) {
- ptr = os_mmap(mm->desc.new_area_hint, ERTS_PAGEALIGNED_SIZE, 0, 0);
+ ptr = os_mmap(mm->desc.new_area_hint, ERTS_PAGEALIGNED_SIZE, 0);
if (ptr) {
mm->desc.new_area_hint = ptr+ERTS_PAGEALIGNED_SIZE;
ERTS_MMAP_SIZE_OS_INC(ERTS_PAGEALIGNED_SIZE);
@@ -1473,7 +1437,7 @@ alloc_desc_insert_free_seg(ErtsMemMapper* mm,
da_map = &mm->sua.map;
desc = lookup_free_seg(da_map, ERTS_PAGEALIGNED_SIZE);
if (desc) {
- if (mm->reserve_physical(desc->start, ERTS_PAGEALIGNED_SIZE, 0))
+ if (mm->reserve_physical(desc->start, ERTS_PAGEALIGNED_SIZE))
ERTS_MMAP_SIZE_SC_SUA_INC(ERTS_PAGEALIGNED_SIZE);
else
desc = NULL;
@@ -1483,7 +1447,7 @@ alloc_desc_insert_free_seg(ErtsMemMapper* mm,
da_map = &mm->sa.map;
desc = lookup_free_seg(da_map, ERTS_PAGEALIGNED_SIZE);
if (desc) {
- if (mm->reserve_physical(desc->start, ERTS_PAGEALIGNED_SIZE, 0))
+ if (mm->reserve_physical(desc->start, ERTS_PAGEALIGNED_SIZE))
ERTS_MMAP_SIZE_SC_SA_INC(ERTS_PAGEALIGNED_SIZE);
else
desc = NULL;
@@ -1544,7 +1508,7 @@ erts_mmap(ErtsMemMapper* mm, Uint32 flags, UWord *sizep)
if (desc) {
seg = desc->start;
end = seg+asize;
- if (!mm->reserve_physical(seg, asize, mm->executable))
+ if (!mm->reserve_physical(seg, asize))
goto supercarrier_reserve_failure;
if (desc->end == end) {
delete_free_seg(&mm->sua.map, desc);
@@ -1559,8 +1523,7 @@ erts_mmap(ErtsMemMapper* mm, Uint32 flags, UWord *sizep)
}
if (asize <= mm->sua.bot - mm->sa.top) {
- if (!mm->reserve_physical(mm->sua.bot - asize, asize,
- mm->executable))
+ if (!mm->reserve_physical(mm->sua.bot - asize, asize))
goto supercarrier_reserve_failure;
mm->sua.bot -= asize;
seg = mm->sua.bot;
@@ -1576,8 +1539,7 @@ erts_mmap(ErtsMemMapper* mm, Uint32 flags, UWord *sizep)
char *start = seg = desc->start;
seg = (char *) ERTS_SUPERALIGNED_CEILING(seg);
end = seg+asize;
- if (!mm->reserve_physical(start, (UWord) (end - start),
- mm->executable))
+ if (!mm->reserve_physical(start, (UWord) (end - start)))
goto supercarrier_reserve_failure;
ERTS_MMAP_SIZE_SC_SA_INC(asize);
if (desc->end == end) {
@@ -1609,8 +1571,7 @@ erts_mmap(ErtsMemMapper* mm, Uint32 flags, UWord *sizep)
if (asize + (seg - start) <= mm->sua.bot - start) {
end = seg + asize;
- if (!mm->reserve_physical(start, (UWord) (end - start),
- mm->executable))
+ if (!mm->reserve_physical(start, (UWord) (end - start)))
goto supercarrier_reserve_failure;
mm->sa.top = end;
ERTS_MMAP_SIZE_SC_SA_INC(asize);
@@ -1632,8 +1593,7 @@ erts_mmap(ErtsMemMapper* mm, Uint32 flags, UWord *sizep)
seg = (char *) ERTS_SUPERALIGNED_CEILING(org_start);
end = seg + asize;
- if (!mm->reserve_physical(seg, (UWord) (org_end - seg),
- mm->executable))
+ if (!mm->reserve_physical(seg, (UWord) (org_end - seg)))
goto supercarrier_reserve_failure;
ERTS_MMAP_SIZE_SC_SUA_INC(asize);
if (org_start != seg) {
@@ -1666,13 +1626,13 @@ erts_mmap(ErtsMemMapper* mm, Uint32 flags, UWord *sizep)
/* Map using OS primitives */
if (!(ERTS_MMAPFLG_SUPERCARRIER_ONLY & flags) && !mm->no_os_mmap) {
if (!(ERTS_MMAPFLG_SUPERALIGNED & flags)) {
- seg = os_mmap(NULL, asize, 0, mm->executable);
+ seg = os_mmap(NULL, asize, 0);
if (!seg)
goto failure;
}
else {
asize = ERTS_SUPERALIGNED_CEILING(*sizep);
- seg = os_mmap(NULL, asize, 1, mm->executable);
+ seg = os_mmap(NULL, asize, 1);
if (!seg)
goto failure;
@@ -1682,8 +1642,7 @@ erts_mmap(ErtsMemMapper* mm, Uint32 flags, UWord *sizep)
os_munmap(seg, asize);
- ptr = os_mmap(NULL, asize + ERTS_SUPERALIGNED_SIZE, 1,
- mm->executable);
+ ptr = os_mmap(NULL, asize + ERTS_SUPERALIGNED_SIZE, 1);
if (!ptr)
goto failure;
@@ -2018,8 +1977,7 @@ erts_mremap(ErtsMemMapper* mm,
if (next && new_end <= next->end) {
if (!mm->reserve_physical(((char *) ptr) + old_size,
- asize - old_size,
- mm->executable))
+ asize - old_size))
goto supercarrier_reserve_failure;
if (new_end < next->end)
resize_free_seg(&mm->sua.map, next, new_end, next->end);
@@ -2037,8 +1995,7 @@ erts_mremap(ErtsMemMapper* mm,
if (end == mm->sa.top) {
if (new_end <= mm->sua.bot) {
if (!mm->reserve_physical(((char *) ptr) + old_size,
- asize - old_size,
- mm->executable))
+ asize - old_size))
goto supercarrier_reserve_failure;
mm->sa.top = new_end;
new_ptr = ptr;
@@ -2050,8 +2007,7 @@ erts_mremap(ErtsMemMapper* mm,
adjacent_free_seg(&mm->sa.map, start, end, &prev, &next);
if (next && new_end <= next->end) {
if (!mm->reserve_physical(((char *) ptr) + old_size,
- asize - old_size,
- mm->executable))
+ asize - old_size))
goto supercarrier_reserve_failure;
if (new_end < next->end)
resize_free_seg(&mm->sa.map, next, new_end, next->end);
@@ -2171,7 +2127,7 @@ static void hard_dbg_mseg_init(void);
#endif
void
-erts_mmap_init(ErtsMemMapper* mm, ErtsMMapInit *init, int executable)
+erts_mmap_init(ErtsMemMapper* mm, ErtsMMapInit *init)
{
static int is_first_call = 1;
int virtual_map = 0;
@@ -2203,7 +2159,6 @@ erts_mmap_init(ErtsMemMapper* mm, ErtsMMapInit *init, int executable)
mm->supercarrier = 0;
mm->reserve_physical = reserve_noop;
mm->unreserve_physical = unreserve_noop;
- mm->executable = executable;
#if HAVE_MMAP && !defined(MAP_ANON)
mm->mmap_fd = open("/dev/zero", O_RDWR);
@@ -2225,7 +2180,7 @@ erts_mmap_init(ErtsMemMapper* mm, ErtsMMapInit *init, int executable)
ptr = (char *) ERTS_PAGEALIGNED_CEILING(init->virtual_range.start);
end = (char *) ERTS_PAGEALIGNED_FLOOR(init->virtual_range.end);
sz = end - ptr;
- start = os_mmap_virtual(ptr, sz, executable);
+ start = os_mmap_virtual(ptr, sz);
if (!start || start > ptr || start >= end)
erts_exit(1,
"erts_mmap: Failed to create virtual range for super carrier\n");
@@ -2250,7 +2205,7 @@ erts_mmap_init(ErtsMemMapper* mm, ErtsMMapInit *init, int executable)
sz = ERTS_PAGEALIGNED_CEILING(init->scs);
#ifdef ERTS_HAVE_OS_PHYSICAL_MEMORY_RESERVATION
if (!init->scrpm) {
- start = os_mmap_virtual(NULL, sz, executable);
+ start = os_mmap_virtual(NULL, sz);
mm->reserve_physical = os_reserve_physical;
mm->unreserve_physical = os_unreserve_physical;
virtual_map = 1;
@@ -2262,7 +2217,7 @@ erts_mmap_init(ErtsMemMapper* mm, ErtsMMapInit *init, int executable)
* The whole supercarrier will by physically
* reserved all the time.
*/
- start = os_mmap(NULL, sz, 1, executable);
+ start = os_mmap(NULL, sz, 1);
}
if (!start)
erts_exit(1,
@@ -2336,7 +2291,7 @@ erts_mmap_init(ErtsMemMapper* mm, ErtsMMapInit *init, int executable)
mm->sua.top -= ERTS_PAGEALIGNED_SIZE;
mm->size.supercarrier.used.total += ERTS_PAGEALIGNED_SIZE;
#ifdef ERTS_HAVE_OS_PHYSICAL_MEMORY_RESERVATION
- if (!virtual_map || os_reserve_physical(mm->sua.top, ERTS_PAGEALIGNED_SIZE, 0))
+ if (!virtual_map || os_reserve_physical(mm->sua.top, ERTS_PAGEALIGNED_SIZE))
#endif
add_free_desc_area(mm, mm->sua.top, end);
mm->desc.reserved += (end - mm->sua.top) / sizeof(ErtsFreeSegDesc);
@@ -2349,7 +2304,7 @@ erts_mmap_init(ErtsMemMapper* mm, ErtsMMapInit *init, int executable)
* will be used for free segment descritors.
*/
#ifdef ERTS_HAVE_OS_PHYSICAL_MEMORY_RESERVATION
- if (virtual_map && !os_reserve_physical(start, mm->sa.bot - start, 0))
+ if (virtual_map && !os_reserve_physical(start, mm->sa.bot - start))
erts_exit(1, "erts_mmap: Failed to reserve physical memory for descriptors\n");
#endif
mm->desc.unused_start = start;
diff --git a/erts/emulator/sys/common/erl_mmap.h b/erts/emulator/sys/common/erl_mmap.h
index 2a07d93c8c..c1f9668b18 100644
--- a/erts/emulator/sys/common/erl_mmap.h
+++ b/erts/emulator/sys/common/erl_mmap.h
@@ -93,11 +93,6 @@ typedef struct {
#define ERTS_MMAP_INIT_LITERAL_INITER \
{{NULL, NULL}, {NULL, NULL}, ERTS_LITERAL_VIRTUAL_AREA_SIZE, 1, (1 << 10), 0}
-#define ERTS_HIPE_EXEC_VIRTUAL_AREA_SIZE (UWORD_CONSTANT(512)*1024*1024)
-
-#define ERTS_MMAP_INIT_HIPE_EXEC_INITER \
- {{NULL, NULL}, {NULL, NULL}, ERTS_HIPE_EXEC_VIRTUAL_AREA_SIZE, 1, (1 << 10), 0}
-
#define ERTS_SUPERALIGNED_SIZE \
(1 << ERTS_MMAP_SUPERALIGNED_BITS)
@@ -140,7 +135,7 @@ void *erts_mmap(ErtsMemMapper*, Uint32 flags, UWord *sizep);
void erts_munmap(ErtsMemMapper*, Uint32 flags, void *ptr, UWord size);
void *erts_mremap(ErtsMemMapper*, Uint32 flags, void *ptr, UWord old_size, UWord *sizep);
int erts_mmap_in_supercarrier(ErtsMemMapper*, void *ptr);
-void erts_mmap_init(ErtsMemMapper*, ErtsMMapInit*, int executable);
+void erts_mmap_init(ErtsMemMapper*, ErtsMMapInit*);
struct erts_mmap_info_struct
{
UWord sizes[6];
@@ -165,15 +160,6 @@ extern ErtsMemMapper erts_dflt_mmapper;
extern ErtsMemMapper erts_literal_mmapper;
# endif
-# if defined(ERTS_ALC_A_EXEC) && defined(__x86_64__)
- /*
- * On x86_64, exec_alloc employs its own super carrier 'erts_exec_mmaper'
- * to ensure low memory for HiPE AMD64 small code model.
- */
-# define ERTS_HAVE_EXEC_MMAPPER
-extern ErtsMemMapper erts_exec_mmapper;
-# endif
-
# endif /* ERTS_HAVE_OS_PHYSICAL_MEMORY_RESERVATION */
#endif /* ERTS_WANT_MEM_MAPPERS */
diff --git a/erts/emulator/sys/common/erl_mseg.c b/erts/emulator/sys/common/erl_mseg.c
index bf6de9b13a..ced3d61525 100644
--- a/erts/emulator/sys/common/erl_mseg.c
+++ b/erts/emulator/sys/common/erl_mseg.c
@@ -1406,15 +1406,9 @@ erts_mseg_init(ErtsMsegInit_t *init)
erts_mtx_init(&init_atoms_mutex, "mseg_init_atoms", NIL,
ERTS_LOCK_FLAGS_PROPERTY_STATIC | ERTS_LOCK_FLAGS_CATEGORY_GENERIC);
-#ifdef ERTS_HAVE_EXEC_MMAPPER
- /* Initialize erts_exec_mapper *FIRST*, to increase probability
- * of getting low memory for HiPE AMD64's small code model.
- */
- erts_mmap_init(&erts_exec_mmapper, &init->exec_mmap, 1);
-#endif
- erts_mmap_init(&erts_dflt_mmapper, &init->dflt_mmap, 0);
+ erts_mmap_init(&erts_dflt_mmapper, &init->dflt_mmap);
#if defined(ARCH_64) && defined(ERTS_HAVE_OS_PHYSICAL_MEMORY_RESERVATION)
- erts_mmap_init(&erts_literal_mmapper, &init->literal_mmap, 0);
+ erts_mmap_init(&erts_literal_mmapper, &init->literal_mmap);
#endif
if (!IS_2POW(GET_PAGE_SIZE))
diff --git a/erts/emulator/sys/common/erl_mseg.h b/erts/emulator/sys/common/erl_mseg.h
index bba0dec499..af275c18be 100644
--- a/erts/emulator/sys/common/erl_mseg.h
+++ b/erts/emulator/sys/common/erl_mseg.h
@@ -61,7 +61,6 @@ typedef struct {
Uint nos;
ErtsMMapInit dflt_mmap;
ErtsMMapInit literal_mmap;
- ErtsMMapInit exec_mmap;
} ErtsMsegInit_t;
#define ERTS_MSEG_INIT_DEFAULT_INITIALIZER \
@@ -72,7 +71,6 @@ typedef struct {
1000, /* cci: Cache check interval */ \
ERTS_MMAP_INIT_DEFAULT_INITER, \
ERTS_MMAP_INIT_LITERAL_INITER, \
- ERTS_MMAP_INIT_HIPE_EXEC_INITER \
}
typedef struct {
diff --git a/erts/emulator/sys/unix/erl_child_setup.c b/erts/emulator/sys/unix/erl_child_setup.c
index 69fc6c2879..10601529a4 100644
--- a/erts/emulator/sys/unix/erl_child_setup.c
+++ b/erts/emulator/sys/unix/erl_child_setup.c
@@ -56,6 +56,8 @@
#include <stdio.h>
#include <stdarg.h>
#include <sys/wait.h>
+#include <sys/types.h>
+#include <sys/socket.h>
#define WANT_NONBLOCKING
@@ -437,6 +439,21 @@ main(int argc, char *argv[])
exit(1);
}
+ /* Ignore SIGTERM.
+ Some container environments send SIGTERM to all processes
+ when terminating. We don't want erl_child_setup to terminate
+ in these cases as that will prevent beam from properly
+ cleaning up.
+ */
+ sa.sa_handler = SIG_IGN;
+ sigemptyset(&sa.sa_mask);
+ sa.sa_flags = 0;
+
+ if (sigaction(SIGTERM, &sa, 0) == -1) {
+ perror(NULL);
+ exit(1);
+ }
+
forker_hash_init();
SET_CLOEXEC(uds_fd);
diff --git a/erts/emulator/sys/unix/sys_drivers.c b/erts/emulator/sys/unix/sys_drivers.c
index b7ac89d89a..117855acf0 100644
--- a/erts/emulator/sys/unix/sys_drivers.c
+++ b/erts/emulator/sys/unix/sys_drivers.c
@@ -50,6 +50,9 @@
#include <sys/ioctl.h>
#endif
+#include <sys/types.h>
+#include <sys/socket.h>
+
#define WANT_NONBLOCKING /* must define this to pull in defs from sys.h */
#include "sys.h"
diff --git a/erts/emulator/sys/unix/sys_uds.c b/erts/emulator/sys/unix/sys_uds.c
index dd0a3b03ff..278c6b6ba1 100644
--- a/erts/emulator/sys/unix/sys_uds.c
+++ b/erts/emulator/sys/unix/sys_uds.c
@@ -18,6 +18,42 @@
* %CopyrightEnd%
*/
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#if defined(__sun__) && !defined(_XOPEN_SOURCE)
+#define _XOPEN_SOURCE 500
+#endif
+
+#include <limits.h>
+
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#ifdef HAVE_SYS_SOCKETIO_H
+# include <sys/socketio.h>
+#endif
+#ifdef HAVE_SYS_SOCKIO_H
+# include <sys/sockio.h>
+#endif
+
+#ifdef HAVE_NET_ERRNO_H
+#include <net/errno.h>
+#endif
+
+#ifdef HAVE_DIRENT_H
+# include <dirent.h>
+#endif
+
+#ifdef HAVE_UNISTD_H
+# include <unistd.h>
+#endif
+
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
#include "sys_uds.h"
int
diff --git a/erts/emulator/sys/unix/sys_uds.h b/erts/emulator/sys/unix/sys_uds.h
index a598102d5c..26c91d6a00 100644
--- a/erts/emulator/sys/unix/sys_uds.h
+++ b/erts/emulator/sys/unix/sys_uds.h
@@ -21,18 +21,6 @@
#ifndef _ERL_UNIX_UDS_H
#define _ERL_UNIX_UDS_H
-#ifdef HAVE_CONFIG_H
-# include "config.h"
-#endif
-
-#if defined(__sun__) && !defined(_XOPEN_SOURCE)
-#define _XOPEN_SOURCE 500
-#endif
-
-#include <limits.h>
-
-#include <sys/types.h>
-#include <sys/socket.h>
#include <sys/uio.h>
#if defined IOV_MAX
@@ -43,8 +31,6 @@
#define MAXIOV 16
#endif
-#include "sys.h"
-
int sys_uds_readv(int fd, struct iovec *iov, size_t iov_len,
int *fds, int fd_count, int flags);
int sys_uds_read(int fd, char *buff, size_t len,
diff --git a/erts/emulator/test/alloc_SUITE.erl b/erts/emulator/test/alloc_SUITE.erl
index 5e5cd6f578..88ff2a7a92 100644
--- a/erts/emulator/test/alloc_SUITE.erl
+++ b/erts/emulator/test/alloc_SUITE.erl
@@ -31,6 +31,7 @@
mseg_clear_cache/1,
erts_mmap/1,
cpool/1,
+ set_dyn_param/1,
migration/1]).
-include_lib("common_test/include/ct.hrl").
@@ -41,6 +42,7 @@ suite() ->
all() ->
[basic, coalesce, threads, realloc_copy, bucket_index,
+ set_dyn_param,
bucket_mask, rbtree, mseg_clear_cache, erts_mmap, cpool, migration].
init_per_testcase(Case, Config) when is_list(Config) ->
@@ -65,7 +67,11 @@ mseg_clear_cache(Cfg) -> drv_case(Cfg).
cpool(Cfg) -> drv_case(Cfg).
migration(Cfg) ->
- drv_case(Cfg, concurrent, "+MZe true").
+ %% Enable test_alloc.
+ %% Disable driver_alloc to avoid recursive alloc_util calls
+ %% through enif_mutex_create() in my_creating_mbc().
+ drv_case(Cfg, concurrent, "+MZe true +MRe false"),
+ drv_case(Cfg, concurrent, "+MZe true +MRe false +MZas ageffcbf").
erts_mmap(Config) when is_list(Config) ->
case {os:type(), mmsc_flags()} of
@@ -110,7 +116,7 @@ erts_mmap_do(Config, SCO, SCRPM, SCRFSD) ->
0 -> O1;
_ -> O1 ++ " +MMscrfsd"++integer_to_list(SCRFSD)
end,
- {ok, Node} = start_node(Config, Opts),
+ {ok, Node} = start_node(Config, Opts, []),
Self = self(),
Ref = make_ref(),
F = fun() ->
@@ -140,6 +146,82 @@ erts_mmap_do(Config, SCO, SCRPM, SCRFSD) ->
Result.
+%% Test erlang:system_flag(erts_alloc, ...)
+set_dyn_param(_Config) ->
+ {_, _, _, AlcList} = erlang:system_info(allocator),
+
+ {Enabled, Disabled, Others} =
+ lists:foldl(fun({sys_alloc,_}, {Es, Ds, Os}) ->
+ {Es, [sys_alloc | Ds], Os};
+
+ ({AT, Opts}, {Es, Ds, Os}) when is_list(Opts) ->
+ case lists:keyfind(e, 1, Opts) of
+ {e, true} ->
+ {[AT | Es], Ds, Os};
+ {e, false} ->
+ {Es, [AT | Ds], Os};
+ false ->
+ {Es, Ds, [AT | Os]}
+ end;
+
+ (_, Acc) -> Acc
+ end,
+ {[], [], []},
+ AlcList),
+
+ Param = sbct,
+ lists:foreach(fun(AT) -> set_dyn_param_enabled(AT, Param) end,
+ Enabled),
+
+ lists:foreach(fun(AT) ->
+ Tpl = {AT, Param, 12345},
+ io:format("~p\n", [Tpl]),
+ notsup = erlang:system_flag(erts_alloc, Tpl)
+ end,
+ Disabled),
+
+ lists:foreach(fun(AT) ->
+ Tpl = {AT, Param, 12345},
+ io:format("~p\n", [Tpl]),
+ {'EXIT',{badarg,_}} =
+ (catch erlang:system_flag(erts_alloc, Tpl))
+ end,
+ Others),
+ ok.
+
+set_dyn_param_enabled(AT, Param) ->
+ OldVal = get_alc_param(AT, Param),
+
+ Val1 = OldVal div 2,
+ Tuple = {AT, Param, Val1},
+ io:format("~p\n", [Tuple]),
+ ok = erlang:system_flag(erts_alloc, Tuple),
+ Val1 = get_alc_param(AT, Param),
+
+ ok = erlang:system_flag(erts_alloc, {AT, Param, OldVal}),
+ OldVal = get_alc_param(AT, Param),
+ ok.
+
+get_alc_param(AT, Param) ->
+ lists:foldl(fun({instance,_,Istats}, Acc) ->
+ {options,Opts} = lists:keyfind(options, 1, Istats),
+ {Param,Val} = lists:keyfind(Param, 1, Opts),
+ {as,Strategy} = lists:keyfind(as, 1, Opts),
+
+ case {param_for_strat(Param, Strategy), Acc} of
+ {false, _} -> Acc;
+ {true, undefined} -> Val;
+ {true, _} ->
+ Val = Acc
+ end
+ end,
+ undefined,
+ erlang:system_info({allocator, AT})).
+
+param_for_strat(sbct, gf) -> false;
+param_for_strat(_, _) -> true.
+
+
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% %%
%% Internal functions %%
@@ -151,7 +233,9 @@ drv_case(Config) ->
drv_case(Config, Mode, NodeOpts) when is_list(Config) ->
case os:type() of
{Family, _} when Family == unix; Family == win32 ->
- {ok, Node} = start_node(Config, NodeOpts),
+ %%Prog = {prog,"/my/own/otp/bin/cerl -debug"},
+ Prog = [],
+ {ok, Node} = start_node(Config, NodeOpts, Prog),
Self = self(),
Ref = make_ref(),
spawn_link(Node,
@@ -217,19 +301,35 @@ wait_for_memory_deallocations() ->
end.
print_stats(migration) ->
- {Btot,Ctot} = lists:foldl(fun({instance,Inr,Istats}, {Bacc,Cacc}) ->
- {mbcs,MBCS} = lists:keyfind(mbcs, 1, Istats),
- Btup = lists:keyfind(blocks, 1, MBCS),
- Ctup = lists:keyfind(carriers, 1, MBCS),
- io:format("{instance,~p,~p,~p}\n", [Inr, Btup, Ctup]),
- {tuple_add(Bacc,Btup),tuple_add(Cacc,Ctup)};
- (_, Acc) -> Acc
- end,
- {{blocks,0,0,0},{carriers,0,0,0}},
- erlang:system_info({allocator,test_alloc})),
-
+ IFun = fun({instance,Inr,Istats}, {Bacc,Cacc,Pacc}) ->
+ {mbcs,MBCS} = lists:keyfind(mbcs, 1, Istats),
+ Btup = lists:keyfind(blocks, 1, MBCS),
+ Ctup = lists:keyfind(carriers, 1, MBCS),
+
+ Ptup = case lists:keyfind(mbcs_pool, 1, Istats) of
+ {mbcs_pool,POOL} ->
+ {blocks, Bpool} = lists:keyfind(blocks, 1, POOL),
+ {carriers, Cpool} = lists:keyfind(carriers, 1, POOL),
+ {pool, Bpool, Cpool};
+ false ->
+ {pool, 0, 0}
+ end,
+ io:format("{instance,~p,~p,~p,~p}}\n",
+ [Inr, Btup, Ctup, Ptup]),
+ {tuple_add(Bacc,Btup),tuple_add(Cacc,Ctup),
+ tuple_add(Pacc,Ptup)};
+ (_, Acc) -> Acc
+ end,
+
+ {Btot,Ctot,Ptot} = lists:foldl(IFun,
+ {{blocks,0,0,0},{carriers,0,0,0},{pool,0,0}},
+ erlang:system_info({allocator,test_alloc})),
+
+ {pool, PBtot, PCtot} = Ptot,
io:format("Number of blocks : ~p\n", [Btot]),
- io:format("Number of carriers: ~p\n", [Ctot]);
+ io:format("Number of carriers: ~p\n", [Ctot]),
+ io:format("Number of pooled blocks : ~p\n", [PBtot]),
+ io:format("Number of pooled carriers: ~p\n", [PCtot]);
print_stats(_) -> ok.
tuple_add(T1, T2) ->
@@ -326,13 +426,13 @@ handle_result(_State, Result0) ->
continue
end.
-start_node(Config, Opts) when is_list(Config), is_list(Opts) ->
+start_node(Config, Opts, Prog) when is_list(Config), is_list(Opts) ->
case proplists:get_value(debug,Config) of
true -> {ok, node()};
- _ -> start_node_1(Config, Opts)
+ _ -> start_node_1(Config, Opts, Prog)
end.
-start_node_1(Config, Opts) ->
+start_node_1(Config, Opts, Prog) ->
Pa = filename:dirname(code:which(?MODULE)),
Name = list_to_atom(atom_to_list(?MODULE)
++ "-"
@@ -341,7 +441,11 @@ start_node_1(Config, Opts) ->
++ integer_to_list(erlang:system_time(second))
++ "-"
++ integer_to_list(erlang:unique_integer([positive]))),
- test_server:start_node(Name, slave, [{args, Opts++" -pa "++Pa}]).
+ ErlArg = case Prog of
+ [] -> [];
+ _ -> [{erl,[Prog]}]
+ end,
+ test_server:start_node(Name, slave, [{args, Opts++" -pa "++Pa} | ErlArg]).
stop_node(Node) when Node =:= node() -> ok;
stop_node(Node) ->
diff --git a/erts/emulator/test/alloc_SUITE_data/allocator_test.h b/erts/emulator/test/alloc_SUITE_data/allocator_test.h
index 97ee58cdad..5272f86c98 100644
--- a/erts/emulator/test/alloc_SUITE_data/allocator_test.h
+++ b/erts/emulator/test/alloc_SUITE_data/allocator_test.h
@@ -156,7 +156,8 @@ typedef void* erts_cond;
#define IS_SMP_ENABLED ((int) ALC_TEST0(0xf13))
#define ALLOC_TEST(S) ((void*) ALC_TEST1(0xf14, (S)))
#define FREE_TEST(P) ((void) ALC_TEST1(0xf15, (P)))
-#define SET_TEST_MBC_USER_HEADER(SZ,CMBC,DMBC) ((int)ALC_TEST3(0xf16, (SZ), (CMBC), (DMBC)))
-#define GET_TEST_MBC_SIZE() ((int) ALC_TEST0(0xf17))
+#define REALLOC_TEST(P,S) ((void*) ALC_TEST2(0xf16, (P), (S)))
+#define SET_TEST_MBC_USER_HEADER(SZ,CMBC,DMBC) ((int)ALC_TEST3(0xf17, (SZ), (CMBC), (DMBC)))
+#define GET_TEST_MBC_SIZE() ((int) ALC_TEST0(0xf18))
#endif
diff --git a/erts/emulator/test/alloc_SUITE_data/migration.c b/erts/emulator/test/alloc_SUITE_data/migration.c
index b9a4de03b3..1d974225fc 100644
--- a/erts/emulator/test/alloc_SUITE_data/migration.c
+++ b/erts/emulator/test/alloc_SUITE_data/migration.c
@@ -223,6 +223,42 @@ static int rand_int(MigrationState* state, int low, int high)
return low + (x % (high+1-low));
}
+enum Operation
+{
+ ALLOCATE_OP,
+ FREE_OP,
+ REALLOC_OP,
+ CLEANUP_OP
+};
+
+static enum Operation rand_op(MigrationState* state)
+{
+ int r = rand_int(state, 1, 100);
+ switch (state->phase) {
+ case GROWING:
+ FATAL_ASSERT(state->nblocks < state->max_nblocks);
+ if (r > 10 || state->nblocks == 0)
+ return ALLOCATE_OP;
+ else if (r > 5)
+ return FREE_OP;
+ else
+ return REALLOC_OP;
+
+ case SHRINKING:
+ FATAL_ASSERT(state->nblocks > 0);
+ if (r > 10 || state->nblocks == state->max_nblocks)
+ return FREE_OP;
+ else if (r > 5)
+ return ALLOCATE_OP;
+ else
+ return REALLOC_OP;
+
+ case CLEANUP:
+ return CLEANUP_OP;
+ default:
+ FATAL_ASSERT(!"Invalid op phase");
+ }
+}
static void do_cleanup(TestCaseState_t *tcs, MigrationState* state)
{
@@ -275,53 +311,75 @@ testcase_run(TestCaseState_t *tcs)
state->goal_nblocks = rand_int(state, 1, state->max_nblocks);
}
- switch (state->phase) {
- case GROWING: {
+ switch (rand_op(state)) {
+ case ALLOCATE_OP: {
MyBlock* p;
FATAL_ASSERT(!state->blockv[state->nblocks]);
- p = ALLOC_TEST(rand_int(state, state->block_size/2, state->block_size));
+ p = ALLOC_TEST(rand_int(state, state->block_size/2, state->block_size));
FATAL_ASSERT(p);
add_block(p, state);
- state->blockv[state->nblocks] = p;
- if (++state->nblocks >= state->goal_nblocks) {
- /*testcase_printf(tcs, "%d: Grown to %d blocks", tcs->thr_nr, state->nblocks);*/
- state->phase = SHRINKING;
- state->goal_nblocks = rand_int(state, 0, state->goal_nblocks-1);
- }
- else
- FATAL_ASSERT(!state->blockv[state->nblocks]);
+ state->blockv[state->nblocks++] = p;
break;
}
- case SHRINKING: {
+ case FREE_OP: {
int ix = rand_int(state, 0, state->nblocks-1);
FATAL_ASSERT(state->blockv[ix]);
remove_block(state->blockv[ix]);
FREE_TEST(state->blockv[ix]);
state->blockv[ix] = state->blockv[--state->nblocks];
state->blockv[state->nblocks] = NULL;
-
- if (state->nblocks <= state->goal_nblocks) {
- /*testcase_printf(tcs, "%d: Shrunk to %d blocks", tcs->thr_nr, state->nblocks);*/
- if (++state->round >= MAX_ROUNDS) {
- state->phase = CLEANUP;
- } else {
- state->phase = GROWING;
- state->goal_nblocks = rand_int(state, state->goal_nblocks+1, state->max_nblocks);
- }
- }
break;
}
+ case REALLOC_OP: {
+ int ix = rand_int(state, 0, state->nblocks-1);
+ MyBlock* p;
+ FATAL_ASSERT(state->blockv[ix]);
+ remove_block(state->blockv[ix]);
+ p = REALLOC_TEST(state->blockv[ix], rand_int(state, state->block_size/2, state->block_size));
+ FATAL_ASSERT(p);
+ add_block(p, state);
+ state->blockv[ix] = p;
+ break;
+ }
+ case CLEANUP_OP:
+ do_cleanup(tcs, state);
+ break;
+ default:
+ FATAL_ASSERT(!"Invalid operation");
+ }
+
+ switch (state->phase) {
+ case GROWING: {
+ if (state->nblocks >= state->goal_nblocks) {
+ /*testcase_printf(tcs, "%d: Grown to %d blocks", tcs->thr_nr, state->nblocks);*/
+ state->phase = SHRINKING;
+ state->goal_nblocks = rand_int(state, 0, state->goal_nblocks-1);
+ }
+ else
+ FATAL_ASSERT(!state->blockv[state->nblocks]);
+ break;
+ }
+ case SHRINKING: {
+ if (state->nblocks <= state->goal_nblocks) {
+ /*testcase_printf(tcs, "%d: Shrunk to %d blocks", tcs->thr_nr, state->nblocks);*/
+ if (++state->round >= MAX_ROUNDS) {
+ state->phase = CLEANUP;
+ } else {
+ state->phase = GROWING;
+ state->goal_nblocks = rand_int(state, state->goal_nblocks+1, state->max_nblocks);
+ }
+ }
+ break;
+ }
case CLEANUP:
- do_cleanup(tcs, state);
- break;
+ case DONE:
+ break;
default:
FATAL_ASSERT(!"Invalid phase");
}
- if (state->phase == DONE) {
- }
- else {
+ if (state->phase != DONE) {
testcase_continue(tcs);
}
}
diff --git a/erts/emulator/test/bif_SUITE.erl b/erts/emulator/test/bif_SUITE.erl
index e1b42e5d85..22706ae8b1 100644
--- a/erts/emulator/test/bif_SUITE.erl
+++ b/erts/emulator/test/bif_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2005-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2005-2018. 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.
@@ -33,7 +33,9 @@
atom_to_binary/1,min_max/1, erlang_halt/1,
erl_crash_dump_bytes/1,
is_builtin/1, error_stacktrace/1,
- error_stacktrace_during_call_trace/1]).
+ error_stacktrace_during_call_trace/1,
+ group_leader_prio/1, group_leader_prio_dirty/1,
+ is_process_alive/1]).
suite() ->
[{ct_hooks,[ts_install_cth]},
@@ -46,7 +48,9 @@ all() ->
display, display_string, list_to_utf8_atom,
atom_to_binary, binary_to_atom, binary_to_existing_atom,
erl_crash_dump_bytes, min_max, erlang_halt, is_builtin,
- error_stacktrace, error_stacktrace_during_call_trace].
+ error_stacktrace, error_stacktrace_during_call_trace,
+ group_leader_prio, group_leader_prio_dirty,
+ is_process_alive].
%% Uses erlang:display to test that erts_printf does not do deep recursion
display(Config) when is_list(Config) ->
@@ -825,7 +829,6 @@ error_stacktrace_during_call_trace(Config) when is_list(Config) ->
end
end,
ok.
-
error_stacktrace_test() ->
Types = [apply_const_last, apply_const, apply_last,
@@ -963,9 +966,140 @@ do_error_1(call) ->
erlang:error(id(oops)).
+group_leader_prio(Config) when is_list(Config) ->
+ group_leader_prio_test(false).
+
+group_leader_prio_dirty(Config) when is_list(Config) ->
+ group_leader_prio_test(true).
+
+group_leader_prio_test(Dirty) ->
+ %%
+ %% Unfortunately back in the days node local group_leader/2 was not
+ %% implemented as sending an asynchronous signal to the process to change
+ %% group leader for. Instead it has always been synchronously changed, and
+ %% nothing in the documentation have hinted otherwise... Therefore I do not
+ %% dare the change this.
+ %%
+ %% In order to prevent priority inversion, the priority of the receiver of
+ %% the group leader signal is elevated while handling incoming signals if
+ %% the sender has a higher priority than the receiver. This test tests that
+ %% the priority elevation actually works...
+ %%
+ Tester = self(),
+ Init = erlang:whereis(init),
+ GL = erlang:group_leader(),
+ process_flag(priority, max),
+ {TestProcFun, NTestProcs}
+ = case Dirty of
+ false ->
+ %% These processes will handle all incoming signals
+ %% by them selves...
+ {fun () ->
+ Tester ! {alive, self()},
+ receive after infinity -> ok end
+ end,
+ 100};
+ true ->
+ %% These processes wont handle incoming signals by
+ %% them selves since they are stuck on dirty schedulers
+ %% when we try to change group leader. A dirty process
+ %% signal handler process (system process) will be notified
+ %% of the need to handle incoming signals for these processes,
+ %% and will instead handle the signal for these processes...
+ {fun () ->
+ %% The following sends the message '{alive, self()}'
+ %% to Tester once on a dirty io scheduler, then wait
+ %% there until the process terminates...
+ erts_debug:dirty_io(alive_waitexiting, Tester)
+ end,
+ erlang:system_info(dirty_io_schedulers)}
+ end,
+ TPs = lists:map(fun (_) ->
+ spawn_opt(TestProcFun,
+ [link, {priority, normal}])
+ end, lists:seq(1, NTestProcs)),
+ lists:foreach(fun (TP) -> receive {alive, TP} -> ok end end, TPs),
+ TLs = lists:map(fun (_) ->
+ spawn_opt(fun () -> tok_loop() end,
+ [link, {priority, high}])
+ end,
+ lists:seq(1, 2*erlang:system_info(schedulers))),
+ %% Wait to ensure distribution of high prio processes over schedulers...
+ receive after 1000 -> ok end,
+ %%
+ %% Test that we can get group-leader signals through to normal prio
+ %% processes from a max prio process even though all schedulers are filled
+ %% with executing high prio processes.
+ %%
+ lists:foreach(fun (_) ->
+ lists:foreach(fun (TP) ->
+ erlang:yield(),
+ %% whitebox -- Enqueue some signals on it
+ %% preventing us from hogging its main lock
+ %% and set group-leader directly....
+ erlang:demonitor(erlang:monitor(process, TP)),
+ true = erlang:group_leader(Init, TP),
+ {group_leader, Init} = process_info(TP, group_leader),
+ erlang:demonitor(erlang:monitor(process, TP)),
+ true = erlang:group_leader(GL, TP),
+ {group_leader, GL} = process_info(TP, group_leader)
+ end,
+ TPs)
+ end,
+ lists:seq(1,100)),
+ %%
+ %% Also test when it is exiting...
+ %%
+ lists:foreach(fun (TP) ->
+ erlang:yield(),
+ M = erlang:monitor(process, TP),
+ unlink(TP),
+ exit(TP, bang),
+ badarg = try
+ true = erlang:group_leader(Init, TP)
+ catch
+ error : What -> What
+ end,
+ receive
+ {'DOWN', M, process, TP, Reason} ->
+ bang = Reason
+ end
+ end,
+ TPs),
+ lists:foreach(fun (TL) ->
+ M = erlang:monitor(process, TL),
+ unlink(TL),
+ exit(TL, bang),
+ receive
+ {'DOWN', M, process, TL, Reason} ->
+ bang = Reason
+ end
+ end,
+ TLs),
+ ok.
+is_process_alive(Config) when is_list(Config) ->
+ process_flag(priority, max),
+ Ps = lists:map(fun (_) ->
+ spawn_opt(fun () -> tok_loop() end,
+ [{priority, high}, link])
+ end,
+ lists:seq(1, 2*erlang:system_info(schedulers))),
+ receive after 1000 -> ok end, %% Wait for load to spread
+ lists:foreach(fun (P) ->
+ %% Ensure that signal order is preserved
+ %% and that we are not starved due to
+ %% priority inversion
+ true = erlang:is_process_alive(P),
+ unlink(P),
+ true = erlang:is_process_alive(P),
+ exit(P, kill),
+ false = erlang:is_process_alive(P)
+ end,
+ Ps),
+ ok.
-%% Helpers
+%% helpers
id(I) -> I.
@@ -1005,3 +1139,11 @@ hostname([$@ | Hostname]) ->
list_to_atom(Hostname);
hostname([_C | Cs]) ->
hostname(Cs).
+
+tok_loop() ->
+ tok_loop(hej).
+
+tok_loop(hej) ->
+ tok_loop(hopp);
+tok_loop(hopp) ->
+ tok_loop(hej).
diff --git a/erts/emulator/test/binary_SUITE.erl b/erts/emulator/test/binary_SUITE.erl
index 4bc1838139..a3c3daac15 100644
--- a/erts/emulator/test/binary_SUITE.erl
+++ b/erts/emulator/test/binary_SUITE.erl
@@ -262,6 +262,7 @@ test_deep_bitstr(List) ->
{Bin,bitstring_to_list(Bin)}.
bad_list_to_binary(Config) when is_list(Config) ->
+ test_bad_bin(<<1:1>>),
test_bad_bin(atom),
test_bad_bin(42),
test_bad_bin([1|2]),
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 2a8b999307..a94a2c0b02 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
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2009-2017. All Rights Reserved.
+ * Copyright Ericsson AB 2009-2018. 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.
@@ -112,15 +112,12 @@ static ERL_NIF_TERM send_from_dirty_nif(ErlNifEnv* env, int argc, const ERL_NIF_
{
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);
+ res = enif_send(env, &pid, NULL, result);
if (!res)
return enif_make_badarg(env);
else
@@ -131,15 +128,12 @@ static ERL_NIF_TERM send_wait_from_dirty_nif(ErlNifEnv* env, int argc, const ERL
{
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);
+ res = enif_send(env, &pid, NULL, result);
#ifdef __WIN32__
Sleep(2000);
@@ -211,10 +205,8 @@ dirty_sleeper(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
/* If we get a pid argument, it indicates a process involved in the
test wants a message from us. Prior to the sleep we send a 'ready'
message, and then after the sleep, send a 'done' message. */
- if (argc == 1 && enif_get_local_pid(env, argv[0], &pid)) {
- msg_env = enif_alloc_env();
- enif_send(env, &pid, msg_env, enif_make_atom(msg_env, "ready"));
- }
+ if (argc == 1 && enif_get_local_pid(env, argv[0], &pid))
+ enif_send(env, &pid, NULL, enif_make_atom(env, "ready"));
#ifdef __WIN32__
Sleep(2000);
@@ -222,11 +214,8 @@ dirty_sleeper(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
sleep(2);
#endif
- if (argc == 1) {
- assert(msg_env != NULL);
- enif_send(env, &pid, msg_env, enif_make_atom(msg_env, "done"));
- enif_free_env(msg_env);
- }
+ if (argc == 1)
+ enif_send(env, &pid, NULL, enif_make_atom(env, "done"));
return enif_make_atom(env, "ok");
}
@@ -247,8 +236,8 @@ static ERL_NIF_TERM dirty_call_while_terminated_nif(ErlNifEnv* env, int argc, co
self_term = enif_make_pid(env, &self);
- result = enif_make_tuple2(env, enif_make_atom(env, "dirty_alive"), self_term);
menv = enif_alloc_env();
+ result = enif_make_tuple2(menv, enif_make_atom(menv, "dirty_alive"), self_term);
res = enif_send(env, &to, menv, result);
enif_free_env(menv);
if (!res)
@@ -259,9 +248,7 @@ static ERL_NIF_TERM dirty_call_while_terminated_nif(ErlNifEnv* env, int argc, co
;
result = enif_make_tuple2(env, enif_make_atom(env, "dirty_dead"), self_term);
- menv = enif_alloc_env();
- res = enif_send(env, &to, menv, result);
- enif_free_env(menv);
+ res = enif_send(env, &to, NULL, result);
#ifdef __WIN32__
Sleep(1000);
diff --git a/erts/emulator/test/efile_SUITE.erl b/erts/emulator/test/efile_SUITE.erl
index 16d581a567..821381bf0d 100644
--- a/erts/emulator/test/efile_SUITE.erl
+++ b/erts/emulator/test/efile_SUITE.erl
@@ -46,14 +46,14 @@ iter_max_files_1(Config) ->
DataDir = proplists:get_value(data_dir,Config),
TestFile = filename:join(DataDir, "existing_file"),
N = 10,
- %% Run on a different node in order to set the max ports
+ %% Run on a different node in order to make the test more stable.
Dir = filename:dirname(code:which(?MODULE)),
{ok,Node} = test_server:start_node(test_iter_max_files,slave,
- [{args,"+Q 1524 -pa " ++ Dir}]),
+ [{args,"-pa " ++ Dir}]),
L = rpc:call(Node,?MODULE,do_iter_max_files,[N, TestFile]),
test_server:stop_node(Node),
io:format("Number of files opened in each test:~n~w\n", [L]),
- all_equal(L),
+ verify_max_files(L),
Head = hd(L),
if Head >= 2 -> ok;
true -> ct:fail(too_few_files)
@@ -65,12 +65,15 @@ do_iter_max_files(N, Name) when N > 0 ->
do_iter_max_files(_, _) ->
[].
-all_equal([E, E| T]) ->
- all_equal([E| T]);
-all_equal([_]) ->
- ok;
-all_equal([]) ->
- ok.
+%% The attempts shouldn't vary too much; we used to require that they were all
+%% exactly equal, but after we reimplemented the file driver as a NIF we
+%% noticed that the only reason it was stable on Darwin was because the port
+%% limit was hit before ulimit.
+verify_max_files(Attempts) ->
+ N = length(Attempts),
+ Mean = lists:sum(Attempts) / N,
+ Variance = lists:sum([(X - Mean) * (X - Mean) || X <- Attempts]) / N,
+ true = math:sqrt(Variance) =< 1 + (Mean / 1000).
max_files(Name) ->
Fds = open_files(Name),
diff --git a/erts/emulator/test/erl_link_SUITE.erl b/erts/emulator/test/erl_link_SUITE.erl
index a66ca7a57d..ed444f2599 100644
--- a/erts/emulator/test/erl_link_SUITE.erl
+++ b/erts/emulator/test/erl_link_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2001-2017. All Rights Reserved.
+%% Copyright Ericsson AB 2001-2018. 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.
@@ -53,26 +53,17 @@
-export([test_proc/0]).
--define(LINK_UNDEF, 0).
--define(LINK_PID, 1).
--define(LINK_NODE, 3).
-
-
-% These are to be kept in sync with erl_monitors.h
--define(MON_ORIGIN, 1).
--define(MON_TARGET, 2).
-
-
--record(erl_link, {type = ?LINK_UNDEF,
+-record(erl_link, {type, % process | port | dist_process
pid = [],
- targets = []}).
+ id}).
% This is to be kept in sync with erl_bif_info.c (make_monitor_list)
--record(erl_monitor, {type, % MON_ORIGIN or MON_TARGET
- ref,
+-record(erl_monitor, {type, % process | port | time_offset | dist_process | resource | node | nodes | suspend
+ dir, % origin | target
+ ref, % Reference | []
pid, % Process or nodename
- name = []}). % registered name or []
+ extra = []}). % registered name, integer or, []
suite() ->
@@ -106,7 +97,7 @@ end_per_suite(_Config) ->
links(Config) when is_list(Config) ->
common_link_test(node(), node()),
true = link(self()),
- [] = find_erl_link(self(), ?LINK_PID, self()),
+ [] = find_erl_link(self(), process, self()),
true = unlink(self()),
ok.
@@ -191,6 +182,7 @@ monitor_nodes(Config) when is_list(Config) ->
monitor_node(A, false),
monitor_node(B, true),
monitor_node(C, true),
+ receive after 1000 -> ok end,
monitor_node(C, false),
monitor_node(C, true),
monitor_node(B, true),
@@ -304,7 +296,8 @@ run_common_process_monitors(TP1, TP2) ->
wait_until(fun () -> is_proc_dead(TP2) end),
ok = tp_call(TP1, fun () ->
receive
- {'DOWN',R2,process,TP2O,bye} ->
+ {'DOWN',R2,process,TP2O,Reason1} ->
+ bye = Reason1,
ok
end
end),
@@ -313,7 +306,8 @@ run_common_process_monitors(TP1, TP2) ->
R3 = tp_call(TP1, fun () -> erlang:monitor(process, TP2) end),
ok = tp_call(TP1, fun () ->
receive
- {'DOWN',R3,process,TP2O,noproc} ->
+ {'DOWN',R3,process,TP2O,Reason2} ->
+ noproc = Reason2,
ok
end
end),
@@ -698,22 +692,10 @@ test_proc() ->
end,
test_proc().
-expand_link_list([#erl_link{type = ?LINK_NODE, targets = N} = Rec | T]) ->
- lists:duplicate(N,Rec#erl_link{targets = []}) ++ expand_link_list(T);
-expand_link_list([#erl_link{targets = [#erl_link{pid = Pid}]} = Rec | T]) ->
- [Rec#erl_link{targets = [Pid]} | expand_link_list(T)];
-expand_link_list([#erl_link{targets = [#erl_link{pid = Pid}|TT]} = Rec | T]) ->
- [ Rec#erl_link{targets = [Pid]} | expand_link_list(
- [Rec#erl_link{targets = TT} | T])];
-expand_link_list([#erl_link{targets = []} = Rec | T]) ->
- [Rec | expand_link_list(T)];
-expand_link_list([]) ->
- [].
-
get_local_link_list(Obj) ->
case catch erts_debug:get_internal_state({link_list, Obj}) of
LL when is_list(LL) ->
- expand_link_list(LL);
+ LL;
_ ->
[]
end.
@@ -722,7 +704,7 @@ get_remote_link_list(Node, Obj) ->
case catch rpc:call(Node, erts_debug, get_internal_state,
[{link_list, Obj}]) of
LL when is_list(LL) ->
- expand_link_list(LL);
+ LL;
_ ->
[]
end.
@@ -776,82 +758,106 @@ get_monitor_list(undefined) ->
find_erl_monitor(Pid, Ref) when is_reference(Ref) ->
+ MonitorList = get_monitor_list(Pid),
+ io:format("~p MonitorList: ~p~n", [Pid, MonitorList]),
lists:foldl(fun (#erl_monitor{ref = R} = EL, Acc) when R == Ref ->
[EL|Acc];
(_, Acc) ->
Acc
end,
[],
- get_monitor_list(Pid)).
-
-% find_erl_link(Obj, Ref) when is_reference(Ref) ->
-% lists:foldl(fun (#erl_link{ref = R} = EL, Acc) when R == Ref ->
-% [EL|Acc];
-% (_, Acc) ->
-% Acc
-% end,
-% [],
-% get_link_list(Obj)).
-
-find_erl_link(Obj, Type, [Item, Data]) when is_pid(Item);
- is_port(Item);
- is_atom(Item) ->
- lists:foldl(fun (#erl_link{type = T, pid = I, targets = D} = EL,
+ MonitorList);
+find_erl_monitor(Pid, Item) ->
+ MonitorList = get_monitor_list(Pid),
+ io:format("~p MonitorList: ~p~n", [Pid, MonitorList]),
+ lists:foldl(fun (#erl_monitor{pid = I} = EL, Acc) when I == Item ->
+ [EL|Acc];
+ (_, Acc) ->
+ Acc
+ end,
+ [],
+ MonitorList).
+
+
+find_erl_link(Obj, Type, Item) when is_pid(Item); is_port(Item) ->
+ LinkList = get_link_list(Obj),
+ io:format("~p LinkList: ~p~n", [Obj, LinkList]),
+ lists:foldl(fun (#erl_link{type = T, pid = I} = EL,
Acc) when T == Type, I == Item ->
- case Data of
- D ->
- [EL|Acc];
- [] ->
- [EL|Acc];
- _ ->
- Acc
- end;
+ [EL|Acc];
+ (_, Acc) ->
+ Acc
+ end,
+ [],
+ LinkList);
+find_erl_link(Obj, Type, Id) when is_integer(Id) ->
+ %% Find by Id
+ LinkList = get_link_list(Obj),
+ io:format("~p LinkList: ~p~n", [Obj, LinkList]),
+ lists:foldl(fun (#erl_link{type = T, id = I} = EL,
+ Acc) when T == Type, I == Id ->
+ [EL|Acc];
(_, Acc) ->
Acc
end,
[],
- get_link_list(Obj));
-find_erl_link(Obj, Type, Item) when is_pid(Item); is_port(Item); is_atom(Item) ->
- find_erl_link(Obj, Type, [Item, []]).
+ LinkList).
+get_link_type(A, B) when is_port(A);
+ is_port(B) ->
+ port;
+get_link_type(A, B) when is_pid(A),
+ is_pid(B) ->
+ case node(A) == node(B) of
+ true ->
+ process;
+ false ->
+ dist_process
+ end.
+check_link(A, B) when node(A) == node(B) ->
+ LinkType = get_link_type(A, B),
+ [#erl_link{type = LinkType,
+ pid = B,
+ id = Id}] = find_erl_link(A, LinkType, B),
+ [#erl_link{type = LinkType,
+ pid = A,
+ id = Id}] = find_erl_link(B, LinkType, A),
+ [] = find_erl_link({node(A), node(B)},
+ LinkType,
+ A),
+ [] = find_erl_link({node(B), node(A)},
+ LinkType,
+ B),
+ ok;
check_link(A, B) ->
- [#erl_link{type = ?LINK_PID,
+ [#erl_link{type = dist_process,
pid = B,
- targets = []}] = find_erl_link(A, ?LINK_PID, B),
- [#erl_link{type = ?LINK_PID,
+ id = IdA}] = find_erl_link(A, dist_process, B),
+ [#erl_link{type = dist_process,
pid = A,
- targets = []}] = find_erl_link(B, ?LINK_PID, A),
- case node(A) == node(B) of
- false ->
- [#erl_link{type = ?LINK_PID,
- pid = A,
- targets = [B]}] = find_erl_link({node(A),
- node(B)},
- ?LINK_PID,
- [A, [B]]),
- [#erl_link{type = ?LINK_PID,
- pid = B,
- targets = [A]}] = find_erl_link({node(B),
- node(A)},
- ?LINK_PID,
- [B, [A]]);
- true ->
- [] = find_erl_link({node(A), node(B)},
- ?LINK_PID,
- [A, [B]]),
- [] = find_erl_link({node(B), node(A)},
- ?LINK_PID,
- [B, [A]])
- end,
+ id = IdA}] = find_erl_link({node(A),
+ node(B)},
+ dist_process,
+ IdA),
+ [#erl_link{type = dist_process,
+ pid = A,
+ id = IdB}] = find_erl_link(B, dist_process, A),
+ [#erl_link{type = dist_process,
+ pid = B,
+ id = IdB}] = find_erl_link({node(B),
+ node(A)},
+ dist_process,
+ IdB),
ok.
check_unlink(A, B) ->
- [] = find_erl_link(A, ?LINK_PID, B),
- [] = find_erl_link(B, ?LINK_PID, A),
- [] = find_erl_link({node(A), node(B)}, ?LINK_PID, [A, [B]]),
- [] = find_erl_link({node(B), node(A)}, ?LINK_PID, [B, [A]]),
+ LinkType = get_link_type(A, B),
+ [] = find_erl_link(A, LinkType, B),
+ [] = find_erl_link(B, LinkType, A),
+ [] = find_erl_link({node(A), node(B)}, dist_process, A),
+ [] = find_erl_link({node(B), node(A)}, dist_process, B),
ok.
check_process_monitor(From, {Name, Node}, Ref) when is_pid(From),
@@ -864,22 +870,26 @@ check_process_monitor(From, {Name, Node}, Ref) when is_pid(From),
is_atom(Node),
is_reference(Ref) ->
MonitoredPid = rpc:call(Node, erlang, whereis, [Name]),
- [#erl_monitor{type = ?MON_ORIGIN,
+ [#erl_monitor{type = dist_process,
+ dir = origin,
ref = Ref,
pid = Node,
- name = Name}] = find_erl_monitor(From, Ref),
- [#erl_monitor{type = ?MON_TARGET,
+ extra = Name}] = find_erl_monitor(From, Ref),
+ [#erl_monitor{type = dist_process,
+ dir = target,
ref = Ref,
pid = From,
- name = Name}] = find_erl_monitor({node(From), Node}, Ref),
- [#erl_monitor{type = ?MON_ORIGIN,
+ extra = Name}] = find_erl_monitor({node(From), Node}, Ref),
+ [#erl_monitor{type = dist_process,
+ dir = origin,
ref = Ref,
pid = MonitoredPid,
- name = Name}] = find_erl_monitor({Node, node(From)}, Ref),
- [#erl_monitor{type = ?MON_TARGET,
+ extra = Name}] = find_erl_monitor({Node, node(From)}, Ref),
+ [#erl_monitor{type = dist_process,
+ dir = target,
ref = Ref,
pid = From,
- name = Name}] = find_erl_monitor(MonitoredPid, Ref),
+ extra = Name}] = find_erl_monitor(MonitoredPid, Ref),
ok;
check_process_monitor(From, Name, Ref) when is_pid(From),
is_atom(Name),
@@ -887,27 +897,36 @@ check_process_monitor(From, Name, Ref) when is_pid(From),
is_reference(Ref) ->
MonitoredPid = rpc:call(node(From), erlang, whereis, [Name]),
- [#erl_monitor{type = ?MON_ORIGIN,
+ [#erl_monitor{type = process,
+ dir = origin,
ref = Ref,
pid = MonitoredPid,
- name = Name}] = find_erl_monitor(From, Ref),
+ extra = Name}] = find_erl_monitor(From, Ref),
- [#erl_monitor{type = ?MON_TARGET,
+ [#erl_monitor{type = process,
+ dir = target,
ref = Ref,
pid = From,
- name = Name}] = find_erl_monitor(MonitoredPid,Ref),
+ extra = Name}] = find_erl_monitor(MonitoredPid,Ref),
ok;
check_process_monitor(From, To, Ref) when is_pid(From),
is_pid(To),
is_reference(Ref) ->
- OriMon = [#erl_monitor{type = ?MON_ORIGIN,
+ MonType = case node(From) == node(To) of
+ true -> process;
+ false -> dist_process
+ end,
+
+ OriMon = [#erl_monitor{type = MonType,
+ dir = origin,
ref = Ref,
pid = To}],
OriMon = find_erl_monitor(From, Ref),
- TargMon = [#erl_monitor{type = ?MON_TARGET,
+ TargMon = [#erl_monitor{type = MonType,
+ dir = target,
ref = Ref,
pid = From}],
TargMon = find_erl_monitor(To, Ref),
@@ -915,7 +934,11 @@ check_process_monitor(From, To, Ref) when is_pid(From),
case node(From) == node(To) of
false ->
- TargMon = find_erl_monitor({node(From), node(To)}, Ref),
+ DistTargMon = [#erl_monitor{type = dist_process,
+ dir = target,
+ ref = Ref,
+ pid = From}],
+ DistTargMon = find_erl_monitor({node(From), node(To)}, Ref),
OriMon = find_erl_monitor({node(To), node(From)}, Ref);
true ->
[] = find_erl_monitor({node(From), node(From)}, Ref)
@@ -986,19 +1009,36 @@ check_process_demonitor(From, To, Ref) when is_pid(From),
ok.
no_of_monitor_node(From, Node) when is_pid(From), is_atom(Node) ->
- length(find_erl_link(From, ?LINK_NODE, Node)).
+ case find_erl_monitor(From, Node) of
+ [] -> 0;
+ [#erl_monitor{type = node,
+ dir = origin,
+ pid = Node,
+ extra = N}] -> N
+ end.
+check_monitor_node(From, Node, 0) when is_pid(From),
+ is_atom(Node) ->
+ [] = find_erl_monitor(From, Node),
+ [] = find_erl_monitor({node(From), Node}, From);
check_monitor_node(From, Node, No) when is_pid(From),
is_atom(Node),
is_integer(No),
- No >= 0 ->
- LL = lists:duplicate(No, #erl_link{type = ?LINK_NODE, pid = Node}),
- DLL = lists:duplicate(No, #erl_link{type = ?LINK_NODE, pid = From}),
- LL = find_erl_link(From, ?LINK_NODE, Node),
- DLL = find_erl_link({node(From), Node}, ?LINK_NODE, From),
- ok.
-
-
+ No > 0 ->
+ [#erl_monitor{type = node,
+ dir = origin,
+ pid = Node,
+ extra = No}] = find_erl_monitor(From, Node),
+ [#erl_monitor{type = node,
+ dir = target,
+ pid = From}] = find_erl_monitor({node(From), Node}, From).
+
+connection_id(Node) ->
+ try
+ erts_debug:get_internal_state({connection_id, Node})
+ catch
+ _:_ -> -1
+ end.
hostname() ->
from($@, atom_to_list(node())).
diff --git a/erts/emulator/test/iovec_SUITE.erl b/erts/emulator/test/iovec_SUITE.erl
index 49dc64b0d2..963b7e2501 100644
--- a/erts/emulator/test/iovec_SUITE.erl
+++ b/erts/emulator/test/iovec_SUITE.erl
@@ -25,7 +25,8 @@
-export([integer_lists/1, binary_lists/1, empty_lists/1, empty_binary_lists/1,
mixed_lists/1, improper_lists/1, illegal_lists/1, cons_bomb/1,
sub_binary_lists/1, iolist_to_iovec_idempotence/1,
- iolist_to_iovec_correctness/1]).
+ iolist_to_iovec_correctness/1, unaligned_sub_binaries/1,
+ direct_binary_arg/1]).
-include_lib("common_test/include/ct.hrl").
@@ -36,7 +37,8 @@ suite() ->
all() ->
[integer_lists, binary_lists, empty_lists, empty_binary_lists, mixed_lists,
sub_binary_lists, illegal_lists, improper_lists, cons_bomb,
- iolist_to_iovec_idempotence, iolist_to_iovec_correctness].
+ iolist_to_iovec_idempotence, iolist_to_iovec_correctness,
+ unaligned_sub_binaries, direct_binary_arg].
init_per_suite(Config) ->
Config.
@@ -78,7 +80,7 @@ illegal_lists(Config) when is_list(Config) ->
BitStrs = gen_variations(["gurka", <<1:1>>, "gaffel"]),
BadInts = gen_variations(["gurka", 890, "gaffel"]),
Atoms = gen_variations([gurka, "gaffel"]),
- BadTails = [["test" | 0], ["gurka", gaffel]],
+ BadTails = [["test" | 0], ["gurka" | gaffel], ["gaffel" | <<1:1>>]],
Variations =
BitStrs ++ BadInts ++ Atoms ++ BadTails,
@@ -98,14 +100,7 @@ cons_bomb(Config) when is_list(Config) ->
BinBase = gen_variations([<<I:8>> || I <- lists:seq(1, 255)]),
MixBase = gen_variations([<<12, 45, 78>>, lists:seq(1, 255)]),
- Rounds =
- case system_mem_size() of
- Mem when Mem >= (16 bsl 30) -> 32;
- Mem when Mem >= (3 bsl 30) -> 28;
- _ -> 20
- end,
-
- Variations = gen_variations([IntBase, BinBase, MixBase], Rounds),
+ Variations = gen_variations([IntBase, BinBase, MixBase], 16),
equivalence_test(fun erlang:iolist_to_iovec/1, Variations).
iolist_to_iovec_idempotence(Config) when is_list(Config) ->
@@ -130,6 +125,21 @@ iolist_to_iovec_correctness(Config) when is_list(Config) ->
true = is_iolist_equal(Optimized, Variations),
ok.
+unaligned_sub_binaries(Config) when is_list(Config) ->
+ UnalignedBins = [gen_unaligned_binary(I) || I <- lists:seq(32, 4 bsl 10, 512)],
+ UnalignedVariations = gen_variations(UnalignedBins),
+
+ Optimized = erlang:iolist_to_iovec(UnalignedVariations),
+
+ true = is_iolist_equal(Optimized, UnalignedVariations),
+ ok.
+
+direct_binary_arg(Config) when is_list(Config) ->
+ {'EXIT',{badarg, _}} = (catch erlang:iolist_to_iovec(<<1:1>>)),
+ [<<1>>] = erlang:iolist_to_iovec(<<1>>),
+ [] = erlang:iolist_to_iovec(<<>>),
+ ok.
+
illegality_test(Fun, Variations) ->
[{'EXIT',{badarg, _}} = (catch Fun(Variation)) || Variation <- Variations],
ok.
@@ -145,11 +155,18 @@ equivalence_test(Fun, [Head | _] = Variations) ->
is_iolist_equal(A, B) ->
iolist_to_binary(A) =:= iolist_to_binary(B).
+gen_unaligned_binary(Size) ->
+ Bin0 = << <<I>> || I <- lists:seq(1, Size) >>,
+ <<0:3,Bin:Size/binary,31:5>> = id(<<0:3,Bin0/binary,31:5>>),
+ Bin.
+
+id(I) -> I.
+
%% Generates a bunch of lists whose contents will be equal to Base repeated a
%% few times. The lists only differ by their structure, so their reduction to
%% a simpler format should yield the same result.
gen_variations(Base) ->
- gen_variations(Base, 16).
+ gen_variations(Base, 12).
gen_variations(Base, N) ->
[gen_flat_list(Base, N),
gen_nested_list(Base, N),
@@ -169,8 +186,3 @@ gen_nasty_list_1([Head | Base], Result) when is_list(Head) ->
gen_nasty_list_1(Base, [[Result], [gen_nasty_list_1(Head, [])]]);
gen_nasty_list_1([Head | Base], Result) ->
gen_nasty_list_1(Base, [[Result], [Head]]).
-
-system_mem_size() ->
- application:ensure_all_started(os_mon),
- {Tot,_Used,_} = memsup:get_memory_data(),
- Tot.
diff --git a/erts/emulator/test/module_info_SUITE.erl b/erts/emulator/test/module_info_SUITE.erl
index ba9b564fdc..46a3bba732 100644
--- a/erts/emulator/test/module_info_SUITE.erl
+++ b/erts/emulator/test/module_info_SUITE.erl
@@ -23,7 +23,7 @@
-include_lib("common_test/include/ct.hrl").
-export([all/0, suite/0,
- exports/1,functions/1,deleted/1,native/1,info/1]).
+ exports/1,functions/1,deleted/1,native/1,info/1,nifs/1]).
%%-compile(native).
@@ -38,7 +38,7 @@ all() ->
modules().
modules() ->
- [exports, functions, deleted, native, info].
+ [exports, functions, deleted, native, info, nifs].
%% Should return all functions exported from this module. (local)
all_exported() ->
@@ -62,12 +62,24 @@ exports(Config) when is_list(Config) ->
All = lists:sort(?MODULE:module_info(exports)),
ok.
-%% Test that the list of exported functions from this module is correct.
+%% Test that the list of local and exported functions from this module is
+%% correct.
functions(Config) when is_list(Config) ->
All = all_functions(),
All = lists:sort(?MODULE:module_info(functions)),
ok.
+nifs(Config) when is_list(Config) ->
+ [] = ?MODULE:module_info(nifs),
+
+ %% erl_tracer is guaranteed to be present and contain these NIFs
+ TraceNIFs = erl_tracer:module_info(nifs),
+ true = lists:member({enabled, 3}, TraceNIFs),
+ true = lists:member({trace, 5}, TraceNIFs),
+ 2 = length(TraceNIFs),
+
+ ok.
+
%% Test that deleted modules cause badarg
deleted(Config) when is_list(Config) ->
Data = proplists:get_value(data_dir, Config),
diff --git a/erts/emulator/test/monitor_SUITE.erl b/erts/emulator/test/monitor_SUITE.erl
index 9d772480d9..c7250a9d26 100644
--- a/erts/emulator/test/monitor_SUITE.erl
+++ b/erts/emulator/test/monitor_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1999-2017. All Rights Reserved.
+%% Copyright Ericsson AB 1999-2018. 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.
@@ -86,7 +86,7 @@ case_2(Config) when is_list(Config) ->
R = erlang:monitor(process, B),
B ! R,
receive
- {'EXIT', _} -> ok;
+ true -> ok;
Other ->
ct:fail({rec, Other})
end,
@@ -98,7 +98,7 @@ case_2a(Config) when is_list(Config) ->
{B,R} = spawn_monitor(?MODULE, y2, [self()]),
B ! R,
receive
- {'EXIT', _} -> ok;
+ true -> ok;
Other ->
ct:fail({rec, Other})
end,
@@ -182,7 +182,7 @@ demon_e_1(Config) when is_list(Config) ->
end ),
receive
{P2, ref, R2} ->
- demon_error(R2, badarg),
+ true = erlang:demonitor(R2),
P2 ! {self(), stop};
Other2 ->
ct:fail({rec, Other2})
@@ -729,8 +729,8 @@ named_down(Config) when is_list(Config) ->
end),
?assertEqual(true, register(Name, NamedProc)),
unlink(NamedProc),
- exit(NamedProc, bang),
Mon = erlang:monitor(process, Name),
+ exit(NamedProc, bang),
receive {'DOWN',Mon, _, _, bang} -> ok
after 3000 -> ?assert(false) end,
?assertEqual(true, register(Name, self())),
diff --git a/erts/emulator/test/nif_SUITE.erl b/erts/emulator/test/nif_SUITE.erl
index 85c302e310..a9eb4b2768 100644
--- a/erts/emulator/test/nif_SUITE.erl
+++ b/erts/emulator/test/nif_SUITE.erl
@@ -1170,6 +1170,12 @@ maps(Config) when is_list(Config) ->
{1, M2} = make_map_remove_nif(M2, "key3"),
{0, undefined} = make_map_remove_nif(self(), key),
+ M1 = maps_from_list_nif(maps:to_list(M1)),
+ M2 = maps_from_list_nif(maps:to_list(M2)),
+ M3 = maps_from_list_nif(maps:to_list(M3)),
+
+ has_duplicate_keys = maps_from_list_nif([{1,1},{1,1}]),
+
verify_tmpmem(TmpMem),
ok.
@@ -3049,8 +3055,24 @@ nif_ioq(Config) ->
{peek_head, a},
{peek, a},
- %% Test to enqueue a bunch of refc binaries
+ %% Ensure that enqueued refc binaries are intact after a roundtrip.
+ %%
+ %% This test and the ones immediately following it does not go through
+ %% erlang:iolist_to_iovec/1
{enqv, a, [nif_ioq_payload(refcbin) || _ <- lists:seq(1,20)], 0},
+ {peek, a},
+
+ %% ... heap binaries
+ {enqv, a, [nif_ioq_payload(heapbin) || _ <- lists:seq(1,20)], 0},
+ {peek, a},
+
+ %% ... plain sub-binaries
+ {enqv, a, [nif_ioq_payload(subbin) || _ <- lists:seq(1,20)], 0},
+ {peek, a},
+
+ %% ... unaligned binaries
+ {enqv, a, [nif_ioq_payload(unaligned_bin) || _ <- lists:seq(1,20)], 0},
+ {peek, a},
%% Enq stuff to destroy with data in queue
{enqv, a, 2, 100},
@@ -3206,13 +3228,16 @@ nif_ioq_payload(N) when is_integer(N) ->
Tail = if N > 3 -> nif_ioq_payload(N-3); true -> [] end,
Head = element(1, lists:split(N,[nif_ioq_payload(subbin),
nif_ioq_payload(heapbin),
- nif_ioq_payload(refcbin) | Tail])),
+ nif_ioq_payload(refcbin),
+ nif_ioq_payload(unaligned_bin) | Tail])),
erlang:iolist_to_iovec(Head);
nif_ioq_payload(subbin) ->
Bin = nif_ioq_payload(refcbin),
Sz = size(Bin) - 1,
<<_:8,SubBin:Sz/binary,_/bits>> = Bin,
SubBin;
+nif_ioq_payload(unaligned_bin) ->
+ make_unaligned_binary(<< <<I>> || I <- lists:seq(1, 255) >>);
nif_ioq_payload(heapbin) ->
<<"a literal heap binary">>;
nif_ioq_payload(refcbin) ->
@@ -3220,6 +3245,13 @@ nif_ioq_payload(refcbin) ->
nif_ioq_payload(Else) ->
Else.
+make_unaligned_binary(Bin0) ->
+ Size = byte_size(Bin0),
+ <<0:3,Bin:Size/binary,31:5>> = id(<<0:3,Bin0/binary,31:5>>),
+ Bin.
+
+id(I) -> I.
+
%% The NIFs:
lib_version() -> undefined.
call_history() -> ?nif_stub.
diff --git a/erts/emulator/test/nif_SUITE_data/nif_SUITE.c b/erts/emulator/test/nif_SUITE_data/nif_SUITE.c
index fa9ae1015c..e8d9302505 100644
--- a/erts/emulator/test/nif_SUITE_data/nif_SUITE.c
+++ b/erts/emulator/test/nif_SUITE_data/nif_SUITE.c
@@ -2140,24 +2140,45 @@ static ERL_NIF_TERM make_map_remove_nif(ErlNifEnv* env, int argc, const ERL_NIF_
/* maps */
static ERL_NIF_TERM maps_from_list_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
- ERL_NIF_TERM cell = argv[0];
- ERL_NIF_TERM map = enif_make_new_map(env);
- ERL_NIF_TERM tuple;
- const ERL_NIF_TERM *pair;
- int arity = -1;
+ ERL_NIF_TERM *keys, *values;
+ ERL_NIF_TERM result, cell;
+ unsigned count;
- if (argc != 1 && !enif_is_list(env, cell)) return enif_make_badarg(env);
+ if (argc != 1 || !enif_get_list_length(env, argv[0], &count)) {
+ return enif_make_badarg(env);
+ }
- /* assume sorted keys */
+ keys = enif_alloc(sizeof(ERL_NIF_TERM) * count * 2);
+ values = keys + count;
- while (!enif_is_empty_list(env,cell)) {
- if (!enif_get_list_cell(env, cell, &tuple, &cell)) return enif_make_badarg(env);
- if (enif_get_tuple(env,tuple,&arity,&pair)) {
- enif_make_map_put(env, map, pair[0], pair[1], &map);
- }
+ cell = argv[0];
+ count = 0;
+
+ while (!enif_is_empty_list(env, cell)) {
+ const ERL_NIF_TERM *pair;
+ ERL_NIF_TERM tuple;
+ int arity;
+
+ if (!enif_get_list_cell(env, cell, &tuple, &cell)
+ || !enif_get_tuple(env, tuple, &arity, &pair)
+ || arity != 2) {
+ enif_free(keys);
+ return enif_make_badarg(env);
+ }
+
+ keys[count] = pair[0];
+ values[count] = pair[1];
+
+ count++;
}
- return map;
+ if (!enif_make_map_from_arrays(env, keys, values, count, &result)) {
+ result = enif_make_atom(env, "has_duplicate_keys");
+ }
+
+ enif_free(keys);
+
+ return result;
}
static ERL_NIF_TERM sorted_list_from_maps_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) {
diff --git a/erts/emulator/test/port_SUITE.erl b/erts/emulator/test/port_SUITE.erl
index e46665a881..5b39d05df8 100644
--- a/erts/emulator/test/port_SUITE.erl
+++ b/erts/emulator/test/port_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2017. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2018. 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.
@@ -109,7 +109,6 @@
mon_port_pid_demonitor/1,
mon_port_remote_on_remote/1,
mon_port_driver_die/1,
- mon_port_driver_die_demonitor/1,
mul_basic/1,
mul_slow_writes/1,
name1/1,
@@ -180,8 +179,7 @@ all() ->
mon_port_bad_named,
mon_port_pid_demonitor,
mon_port_name_demonitor,
- mon_port_driver_die,
- mon_port_driver_die_demonitor
+ mon_port_driver_die
].
groups() ->
@@ -2783,7 +2781,7 @@ mon_port_driver_die(Config) ->
end,
ok.
-
+-ifdef(DISABLED_TESTCASE).
%% 1. Spawn a port which will sleep 3 seconds
%% 2. Monitor port
%% 3. Port driver and dies horribly (via C driver_failure call). This should
@@ -2819,6 +2817,7 @@ mon_port_driver_die_demonitor(Config) ->
after 5000 -> ?assert(false)
end,
ok.
+-endif.
%% @doc Makes a controllable port for testing. Underlying mechanism of this
%% port is not important, only important is our ability to close/kill it or
diff --git a/erts/emulator/test/port_trace_SUITE.erl b/erts/emulator/test/port_trace_SUITE.erl
index a1986397a8..eba8f194e0 100644
--- a/erts/emulator/test/port_trace_SUITE.erl
+++ b/erts/emulator/test/port_trace_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1999-2017. All Rights Reserved.
+%% Copyright Ericsson AB 1999-2018. 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.
@@ -202,8 +202,7 @@ ports(_Config) ->
erlang:port_close(Prt),
[{trace,Prt,closed,normal},
- {trace,Prt,unregister,port_trace_SUITE},
- {trace,Prt,unlink,S}] = flush(),
+ {trace,Prt,unregister,port_trace_SUITE}] = flush(),
ok.
@@ -475,8 +474,7 @@ failure_test(Failure, Reason) ->
process_flag(trap_exit, false)
end,
[{trace, Prt, 'receive', {S, {command, Failure}}},
- {trace, Prt, closed, Reason},
- {trace, Prt, unlink, S}] = flush(),
+ {trace, Prt, closed, Reason}] = flush(),
ok.
@@ -599,13 +597,11 @@ close(Prt, Flags) ->
if Recv, Ports ->
[{trace, Prt, 'receive', {S, close}},
- {trace, Prt, closed, normal},
- {trace, Prt, unlink, S}] = flush();
+ {trace, Prt, closed, normal}] = flush();
Recv ->
[{trace, Prt, 'receive', {S, close}}] = flush();
Ports ->
- [{trace, Prt, closed, normal},
- {trace, Prt, unlink, S}] = flush();
+ [{trace, Prt, closed, normal}] = flush();
true ->
[] = flush()
end.
diff --git a/erts/emulator/test/process_SUITE.erl b/erts/emulator/test/process_SUITE.erl
index a8bcfac84d..7eff786e8b 100644
--- a/erts/emulator/test/process_SUITE.erl
+++ b/erts/emulator/test/process_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2017. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2018. 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.
@@ -43,7 +43,6 @@
process_info_lock_reschedule3/1,
process_info_garbage_collection/1,
bump_reductions/1, low_prio/1, binary_owner/1, yield/1, yield2/1,
- process_status_exiting/1,
otp_4725/1, bad_register/1, garbage_collect/1, otp_6237/1,
process_info_messages/1, process_flag_badarg/1, process_flag_heap_size/1,
spawn_opt_heap_size/1, spawn_opt_max_heap_size/1,
@@ -80,7 +79,6 @@ all() ->
process_info_lock_reschedule2,
process_info_lock_reschedule3,
process_info_garbage_collection,
- process_status_exiting,
bump_reductions, low_prio, yield, yield2, otp_4725,
bad_register, garbage_collect, process_info_messages,
process_flag_badarg, process_flag_heap_size,
@@ -841,28 +839,6 @@ process_info_lock_reschedule3(Config) when is_list(Config) ->
ct:fail(BadStatus)
end.
-process_status_exiting(Config) when is_list(Config) ->
- %% Make sure that erts_debug:get_internal_state({process_status,P})
- %% returns exiting if it is in status P_EXITING.
- erts_debug:set_internal_state(available_internal_state,true),
- Prio = process_flag(priority, max),
- P = spawn_opt(fun () -> receive after infinity -> ok end end,
- [{priority, normal}]),
- erlang:yield(),
- %% The tok_loop processes are here to make it hard for the exiting
- %% process to be scheduled in for exit...
- TokLoops = lists:map(fun (_) ->
- spawn_opt(fun tok_loop/0,
- [link,{priority, high}])
- end, lists:seq(1, erlang:system_info(schedulers_online))),
- exit(P, boom),
- wait_until(fun() ->
- exiting =:= erts_debug:get_internal_state({process_status,P})
- end),
- lists:foreach(fun (Tok) -> unlink(Tok), exit(Tok,bang) end, TokLoops),
- process_flag(priority, Prio),
- ok.
-
otp_4725(Config) when is_list(Config) ->
Tester = self(),
Ref1 = make_ref(),
@@ -1000,7 +976,7 @@ gv(Key,List) ->
%% Tests erlang:bump_reductions/1.
bump_reductions(Config) when is_list(Config) ->
erlang:garbage_collect(),
- receive after 1 -> ok end, % Clear reductions.
+ erlang:yield(), % Clear reductions.
{reductions,R1} = process_info(self(), reductions),
true = erlang:bump_reductions(100),
{reductions,R2} = process_info(self(), reductions),
diff --git a/erts/emulator/test/signal_SUITE.erl b/erts/emulator/test/signal_SUITE.erl
index 61a8617165..fab2f45f28 100644
--- a/erts/emulator/test/signal_SUITE.erl
+++ b/erts/emulator/test/signal_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2006-2017. All Rights Reserved.
+%% Copyright Ericsson AB 2006-2018. 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.
@@ -34,23 +34,9 @@
-export([init_per_testcase/2, end_per_testcase/2]).
% Test cases
--export([xm_sig_order/1,
- pending_exit_unlink_process/1,
- pending_exit_unlink_dist_process/1,
- pending_exit_unlink_port/1,
- pending_exit_trap_exit/1,
- pending_exit_receive/1,
- pending_exit_exit/1,
- pending_exit_gc/1,
- pending_exit_is_process_alive/1,
- pending_exit_process_display/1,
- pending_exit_process_info_1/1,
- pending_exit_process_info_2/1,
- pending_exit_group_leader/1,
- exit_before_pending_exit/1]).
+-export([xm_sig_order/1]).
init_per_testcase(Func, Config) when is_atom(Func), is_list(Config) ->
- available_internal_state(true),
[{testcase, Func}|Config].
end_per_testcase(_Func, _Config) ->
@@ -60,24 +46,14 @@ init_per_suite(Config) ->
Config.
end_per_suite(_Config) ->
- available_internal_state(true),
- catch erts_debug:set_internal_state(not_running_optimization, true),
- available_internal_state(false).
+ ok.
suite() ->
[{ct_hooks,[ts_install_cth]},
{timetrap, {minutes, 2}}].
all() ->
- [xm_sig_order, pending_exit_unlink_process,
- pending_exit_unlink_dist_process,
- pending_exit_unlink_port, pending_exit_trap_exit,
- pending_exit_receive, pending_exit_trap_exit,
- pending_exit_gc, pending_exit_is_process_alive,
- pending_exit_process_display,
- pending_exit_process_info_1,
- pending_exit_process_info_2, pending_exit_group_leader,
- exit_before_pending_exit].
+ [xm_sig_order].
%% Test that exit signals and messages are received in correct order
@@ -113,362 +89,9 @@ xm_sig_order_proc() ->
end,
xm_sig_order_proc().
-pending_exit_unlink_process(Config) when is_list(Config) ->
- pending_exit_test(self(), unlink).
-
-pending_exit_unlink_dist_process(Config) when is_list(Config) ->
- {ok, Node} = start_node(Config),
- From = spawn(Node, fun () -> receive after infinity -> ok end end),
- Res = pending_exit_test(From, unlink),
- stop_node(Node),
- Res.
-
-pending_exit_unlink_port(Config) when is_list(Config) ->
- pending_exit_test(hd(erlang:ports()), unlink).
-
-pending_exit_trap_exit(Config) when is_list(Config) ->
- pending_exit_test(self(), trap_exit).
-
-pending_exit_receive(Config) when is_list(Config) ->
- pending_exit_test(self(), 'receive').
-
-pending_exit_exit(Config) when is_list(Config) ->
- pending_exit_test(self(), exit).
-
-pending_exit_gc(Config) when is_list(Config) ->
- pending_exit_test(self(), gc).
-
-pending_exit_test(From, Type) ->
- OTE = process_flag(trap_exit, true),
- Ref = make_ref(),
- Master = self(),
- ExitBySignal = case Type of
- gc ->
- lists:duplicate(10000,
- exit_by_signal);
- _ ->
- exit_by_signal
- end,
- Pid = spawn_link(
- fun () ->
- receive go -> ok end,
- false = have_pending_exit(),
- exit = fake_exit(From,
- self(),
- ExitBySignal),
- true = have_pending_exit(),
- Master ! {self(), Ref, Type},
- case Type of
- gc ->
- force_gc(),
- erlang:yield();
- unlink ->
- unlink(From);
- trap_exit ->
- process_flag(trap_exit, true);
- 'receive' ->
- receive _ -> ok
- after 0 -> ok
- end;
- exit ->
- ok
- end,
- exit(exit_by_myself)
- end),
- Mon = erlang:monitor(process, Pid),
- Pid ! go,
- Reason = receive
- {'DOWN', Mon, process, Pid, R} ->
- receive
- {Pid, Ref, Type} ->
- ok
- after 0 ->
- ct:fail(premature_exit)
- end,
- case Type of
- exit ->
- exit_by_myself = R;
- _ ->
- ExitBySignal = R
- end
- end,
- receive
- {'EXIT', Pid, R2} ->
- Reason = R2
- end,
- process_flag(trap_exit, OTE),
- ok,
- {comment, "Test only valid with current SMP emulator."}.
-
-
-
-exit_before_pending_exit(Config) when is_list(Config) ->
- %% This is a testcase testcase very specific to the smp
- %% implementation as it is of the time of writing.
- %%
- %% The testcase tries to check that a process can
- %% exit by itself even though it has a pending exit.
- OTE = process_flag(trap_exit, true),
- Master = self(),
- Tester = spawn_link(
- fun () ->
- Opts = case {erlang:system_info(run_queues),
- erlang:system_info(schedulers_online)} of
- {RQ, SO} when RQ =:= 1; SO =:= 1 -> [];
- _ ->
- process_flag(scheduler, 1),
- [{scheduler, 2}]
- end,
- P = self(),
- Exiter = spawn_opt(fun () ->
- receive
- {exit_me, P, R} ->
- exit(P, R)
- end
- end, Opts),
- erlang:yield(),
- Exiter ! {exit_me, self(), exited_by_exiter},
- %% We want to get a pending exit
- %% before we exit ourselves. We
- %% don't want to be scheduled out
- %% since we will then see the
- %% pending exit.
- %%
- %% Do something that takes
- %% relatively long time but
- %% consumes few reductions...
- repeat(fun() -> erlang:system_info(procs) end,10),
- %% ... then exit.
- Master ! {self(),
- pending_exit,
- have_pending_exit()},
- exit(exited_by_myself)
- end),
- PendingExit = receive {Tester, pending_exit, PE} -> PE end,
- receive
- {'EXIT', Tester, exited_by_myself} ->
- process_flag(trap_exit, OTE),
- ok;
- Msg ->
- ct:fail({unexpected_message, Msg})
- end,
- NoScheds = integer_to_list(erlang:system_info(schedulers_online)),
- {comment,
- "Was "
- ++ case PendingExit of
- true -> "";
- false ->"*not*"
- end ++ " able to trigger a pending exit. "
- ++ "Running on " ++ NoScheds ++ " scheduler(s). "
- ++ "This test is only interesting with at least two schedulers."}.
-
--define(PE_INFO_REPEAT, 100).
-
-pending_exit_is_process_alive(Config) when is_list(Config) ->
- S = exit_op_test_init(),
- TestFun = fun (P) -> false = is_process_alive(P) end,
- repeated_exit_op_test(TestFun, ?PE_INFO_REPEAT),
- verify_pending_exit_success(S),
- comment().
-
-pending_exit_process_info_1(Config) when is_list(Config) ->
- S = exit_op_test_init(),
- TestFun = fun (P) ->
- undefined = process_info(P)
- end,
- repeated_exit_op_test(TestFun, ?PE_INFO_REPEAT),
- verify_pending_exit_success(S),
- comment().
-
-pending_exit_process_info_2(Config) when is_list(Config) ->
- S0 = exit_op_test_init(),
- repeated_exit_op_test(fun (P) ->
- undefined = process_info(P, messages)
- end, ?PE_INFO_REPEAT),
- S1 = verify_pending_exit_success(S0),
- repeated_exit_op_test(fun (P) ->
- undefined = process_info(P, status)
- end, ?PE_INFO_REPEAT),
- S2 = verify_pending_exit_success(S1),
- repeated_exit_op_test(fun (P) ->
- undefined = process_info(P, links)
- end, ?PE_INFO_REPEAT),
- S3 = verify_pending_exit_success(S2),
- repeated_exit_op_test(fun (P) ->
- undefined = process_info(P, [messages])
- end, ?PE_INFO_REPEAT),
- S4 = verify_pending_exit_success(S3),
- repeated_exit_op_test(fun (P) ->
- undefined = process_info(P, [status])
- end, ?PE_INFO_REPEAT),
- S5 = verify_pending_exit_success(S4),
- repeated_exit_op_test(fun (P) ->
- undefined = process_info(P, [links])
- end, ?PE_INFO_REPEAT),
- S6 = verify_pending_exit_success(S5),
- repeated_exit_op_test(fun (P) ->
- undefined = process_info(P, [status,
- links])
- end, ?PE_INFO_REPEAT),
- S7 = verify_pending_exit_success(S6),
- repeated_exit_op_test(fun (P) ->
- undefined = process_info(P, [messages,
- status])
- end, ?PE_INFO_REPEAT),
- S8 = verify_pending_exit_success(S7),
- repeated_exit_op_test(fun (P) ->
- undefined = process_info(P, [messages,
- links])
- end, ?PE_INFO_REPEAT),
- S9 = verify_pending_exit_success(S8),
- repeated_exit_op_test(
- fun (P) ->
- undefined = process_info(P, [message_queue_len,
- status])
- end, ?PE_INFO_REPEAT),
- S10 = verify_pending_exit_success(S9),
- repeated_exit_op_test(fun (P) ->
- undefined = process_info(P, [messages,
- links,
- status])
- end, ?PE_INFO_REPEAT),
- verify_pending_exit_success(S10),
- comment().
-
-pending_exit_process_display(Config) when is_list(Config) ->
- S = exit_op_test_init(),
- TestFun = fun (P) ->
- badarg = try
- erlang:process_display(P, backtrace)
- catch
- error:badarg -> badarg
- end
- end,
- repeated_exit_op_test(TestFun, ?PE_INFO_REPEAT),
- verify_pending_exit_success(S),
- comment().
-
-pending_exit_group_leader(Config) when is_list(Config) ->
- S = exit_op_test_init(),
- TestFun = fun (P) ->
- badarg = try
- group_leader(self(), P)
- catch
- error:badarg -> badarg
- end
- end,
- repeated_exit_op_test(TestFun, ?PE_INFO_REPEAT),
- verify_pending_exit_success(S),
- comment().
-
%%
%% -- Internal utils --------------------------------------------------------
%%
-exit_op_test_init() ->
- put(no_pending_exit_success, 0),
- put(no_pending_exit_tries, 0),
- {case {erlang:system_info(run_queues),
- erlang:system_info(schedulers_online)} of
- {RQ, SO} when RQ =:= 1; SO =:= 1 -> false;
- _ -> true
- end, 0, 0}.
-
-verify_pending_exit_success({false, _, _} = S) ->
- S;
-verify_pending_exit_success({true, S, T}) ->
- NewS = get(no_pending_exit_success),
- NewT = get(no_pending_exit_tries),
- case NewT =:= T of
- true -> ok;
- _ -> case NewS > S of
- true -> ok;
- _ -> exit(no_pending_exits)
- end
- end,
- {true, NewS, NewT}.
-
-comment() ->
- {comment,
- "Pending exit trigger ratio "
- ++ integer_to_list(get(no_pending_exit_success))
- ++ "/"
- ++ integer_to_list(get(no_pending_exit_tries))
- ++ "."
- ++ case get(not_running_opt_test) of
- true -> " No 'not running optimization' to disable.";
- _ -> ""
- end}.
-
-repeated_exit_op_test(TestFun, N) ->
- WorkFun0 = fun () ->
- lists:sort(lists:reverse(lists:seq(1, 1000)))
- end,
- repeat(fun () -> exit_op_test(TestFun, WorkFun0) end, N),
- try erts_debug:set_internal_state(not_running_optimization, false) of
- Bool when Bool == true; Bool == false ->
- WorkFun1 = fun () ->
- erts_debug:set_internal_state(sleep, 0),
- lists:sort(lists:reverse(lists:seq(1, 1000)))
- end,
- repeat(fun () ->
- exit_op_test(TestFun, WorkFun1)
- end, N)
- catch
- error:notsup -> put(not_running_opt_test, true)
- after
- catch erts_debug:set_internal_state(not_running_optimization, true)
- end.
-
-exit_op_test(TestFun, WorkFun) ->
- Opts = case {erlang:system_info(run_queues),
- erlang:system_info(schedulers_online)} of
- {RQ, SO} when RQ =:= 1; SO =:= 1 -> [];
- _ ->
- process_flag(scheduler, 1),
- [{scheduler, 2}]
- end,
- Master = self(),
- Going = make_ref(),
- P = spawn_opt(fun () ->
- loop(10, WorkFun),
- Master ! Going,
- loop(infinity, WorkFun)
- end, Opts),
- receive Going -> ok end,
- loop(10, WorkFun),
- erlang:yield(),
- exit(P, bang),
- PE0 = have_pending_exit(P),
- TestFun(P),
- PE = case PE0 of
- true -> true;
- _ -> false
- end,
- case {PE, get(no_pending_exit_success), get(no_pending_exit_tries)} of
- {true, undefined, undefined} ->
- put(no_pending_exit_success, 1),
- put(no_pending_exit_tries, 1);
- {false, undefined, undefined} ->
- put(no_pending_exit_success, 0),
- put(no_pending_exit_tries, 1);
- {true, S, T} ->
- put(no_pending_exit_success, S+1),
- put(no_pending_exit_tries, T+1);
- {false, _S, T} ->
- put(no_pending_exit_tries, T+1)
- end,
- ok.
-
-loop(infinity, WorkFun) ->
- do_loop(infinity, WorkFun);
-loop(0, _WorkFun) ->
- ok;
-loop(N, WorkFun) when is_integer(N) ->
- do_loop(N-1, WorkFun).
-
-do_loop(N, WorkFun) ->
- WorkFun(),
- loop(N, WorkFun).
repeat(_Fun, N) when is_integer(N), N =< 0 ->
ok;
@@ -486,30 +109,3 @@ start_node(Config) ->
stop_node(Node) ->
test_server:stop_node(Node).
-
-have_pending_exit() ->
- have_pending_exit(self()).
-
-have_pending_exit(Pid) ->
- erts_debug:get_internal_state({have_pending_exit, Pid}).
-
-force_gc() ->
- erts_debug:set_internal_state(force_gc, self()).
-
-fake_exit(From, To, Reason) ->
- erts_debug:set_internal_state(send_fake_exit_signal, {From, To, Reason}).
-
-available_internal_state(Bool) when Bool == true; Bool == false ->
- case {Bool,
- (catch erts_debug:get_internal_state(available_internal_state))} of
- {true, true} ->
- true;
- {false, true} ->
- erts_debug:set_internal_state(available_internal_state, false),
- true;
- {true, _} ->
- erts_debug:set_internal_state(available_internal_state, true),
- false;
- {false, _} ->
- false
- end.
diff --git a/erts/emulator/test/trace_SUITE.erl b/erts/emulator/test/trace_SUITE.erl
index a81aa64057..def25dba7d 100644
--- a/erts/emulator/test/trace_SUITE.erl
+++ b/erts/emulator/test/trace_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2017. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2018. 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.
@@ -235,7 +235,7 @@ link_receive_call_correlation(Config) when is_list(Config) ->
1 = erlang:trace(Receiver, true, ['receive', procs, call, timestamp, scheduler_id]),
1 = erlang:trace_pattern({?MODULE, receive_msg, '_'}, [], [local]),
- Num = 100000,
+ Num = 100,
(fun F(0) -> [];
F(N) ->
@@ -255,7 +255,7 @@ link_receive_call_correlation(Config) when is_list(Config) ->
Msgs = (fun F() -> receive M -> [M | F()] after 1 -> [] end end)(),
- case check_consistent(Receiver, Num, Num, Num, Msgs) of
+ case check_consistent(Receiver, Num, Num, Num, Msgs, false, undefined) of
ok ->
ok;
{error, Reason} ->
@@ -265,20 +265,63 @@ link_receive_call_correlation(Config) when is_list(Config) ->
-define(schedid, , _).
-check_consistent(_Pid, Recv, Call, _LU, [Msg | _]) when Recv > Call ->
+check_consistent(_Pid, Recv, Call, _LU, [Msg | _], _Received, _LinkedN) when Recv > Call ->
{error, Msg};
-check_consistent(Pid, Recv, Call, LU, [Msg | Msgs]) ->
+check_consistent(Pid, Recv, Call, LU, [Msg | Msgs], false, undefined) ->
case Msg of
{trace, Pid, 'receive', Recv ?schedid} ->
- check_consistent(Pid,Recv - 1, Call, LU, Msgs);
+ check_consistent(Pid,Recv - 1, Call, LU, Msgs, true, undefined);
{trace_ts, Pid, 'receive', Recv ?schedid, _} ->
- check_consistent(Pid,Recv - 1, Call, LU, Msgs);
+ check_consistent(Pid,Recv - 1, Call, LU, Msgs, true, undefined);
{trace, Pid, call, {?MODULE, receive_msg, [Call]} ?schedid} ->
- check_consistent(Pid,Recv, Call - 1, LU, Msgs);
+ check_consistent(Pid,Recv, Call - 1, LU, Msgs, false, undefined);
{trace_ts, Pid, call, {?MODULE, receive_msg, [Call]} ?schedid, _} ->
- check_consistent(Pid,Recv, Call - 1, LU, Msgs);
+ check_consistent(Pid,Recv, Call - 1, LU, Msgs, false, undefined);
+
+ {trace, Pid, _, _Self ?schedid} ->
+ check_consistent(Pid, Recv, Call, LU, Msgs, false, undefined);
+ {trace_ts, Pid, _, _Self ?schedid, _} ->
+ check_consistent(Pid, Recv, Call, LU, Msgs, false, undefined);
+
+ Msg ->
+ {error, Msg}
+ end;
+check_consistent(Pid, Recv, Call, LU, [Msg | Msgs], true, undefined) ->
+
+ case Msg of
+ {trace, Pid, call, {?MODULE, receive_msg, [Call]} ?schedid} ->
+ check_consistent(Pid,Recv, Call - 1, LU, Msgs, true, undefined);
+ {trace_ts, Pid, call, {?MODULE, receive_msg, [Call]} ?schedid, _} ->
+ check_consistent(Pid,Recv, Call - 1, LU, Msgs, true, undefined);
+
+ {trace, Pid, getting_linked, _Self ?schedid} ->
+ check_consistent(Pid, Recv, Call, LU - 1, Msgs, true, Recv rem 2);
+ {trace_ts, Pid, getting_linked, _Self ?schedid, _} ->
+ check_consistent(Pid, Recv, Call, LU - 1, Msgs, true, Recv rem 2);
+
+ {trace, Pid, getting_unlinked, _Self ?schedid} ->
+ check_consistent(Pid, Recv, Call, LU - 1, Msgs, true, (Recv+1) rem 2);
+ {trace_ts, Pid, getting_unlinked, _Self ?schedid, _} ->
+ check_consistent(Pid, Recv, Call, LU - 1, Msgs, true, (Recv+1) rem 2);
+
+ Msg ->
+ {error, Msg}
+ end;
+check_consistent(Pid, Recv, Call, LU, [Msg | Msgs], true, LinkedN) ->
+ UnlinkedN = (LinkedN + 1) rem 2,
+
+ case Msg of
+ {trace, Pid, 'receive', Recv ?schedid} when Recv == LU ->
+ check_consistent(Pid,Recv - 1, Call, LU, Msgs, true, LinkedN);
+ {trace_ts, Pid, 'receive', Recv ?schedid, _} when Recv == LU ->
+ check_consistent(Pid,Recv - 1, Call, LU, Msgs, true, LinkedN);
+
+ {trace, Pid, call, {?MODULE, receive_msg, [Call]} ?schedid} ->
+ check_consistent(Pid,Recv, Call - 1, LU, Msgs, true, LinkedN);
+ {trace_ts, Pid, call, {?MODULE, receive_msg, [Call]} ?schedid, _} ->
+ check_consistent(Pid,Recv, Call - 1, LU, Msgs, true, LinkedN);
%% We check that for each receive we have gotten a
%% getting_linked or getting_unlinked message. Also
@@ -286,38 +329,38 @@ check_consistent(Pid, Recv, Call, LU, [Msg | Msgs]) ->
%% message we expect to receive is an even number
%% and odd number for getting_unlinked.
{trace, Pid, getting_linked, _Self ?schedid}
- when Recv rem 2 == 0, Recv == LU ->
- check_consistent(Pid, Recv, Call, LU - 1, Msgs);
+ when Recv rem 2 == LinkedN ->
+ check_consistent(Pid, Recv, Call, LU - 1, Msgs, true, LinkedN);
{trace_ts, Pid, getting_linked, _Self ?schedid, _}
- when Recv rem 2 == 0, Recv == LU ->
- check_consistent(Pid, Recv, Call, LU - 1, Msgs);
+ when Recv rem 2 == LinkedN ->
+ check_consistent(Pid, Recv, Call, LU - 1, Msgs, true, LinkedN);
{trace, Pid, getting_unlinked, _Self ?schedid}
- when Recv rem 2 == 1, Recv == LU ->
- check_consistent(Pid, Recv, Call, LU - 1, Msgs);
+ when Recv rem 2 == UnlinkedN ->
+ check_consistent(Pid, Recv, Call, LU - 1, Msgs, true, LinkedN);
{trace_ts, Pid, getting_unlinked, _Self ?schedid, _}
- when Recv rem 2 == 1, Recv == LU ->
- check_consistent(Pid, Recv, Call, LU - 1, Msgs);
+ when Recv rem 2 == UnlinkedN ->
+ check_consistent(Pid, Recv, Call, LU - 1, Msgs, true, LinkedN);
{trace,Pid,'receive',Ignore ?schedid}
when Ignore == stop; Ignore == timeout ->
- check_consistent(Pid, Recv, Call, LU, Msgs);
+ check_consistent(Pid, Recv, Call, LU, Msgs, true, LinkedN);
{trace_ts,Pid,'receive',Ignore ?schedid,_}
when Ignore == stop; Ignore == timeout ->
- check_consistent(Pid, Recv, Call, LU, Msgs);
+ check_consistent(Pid, Recv, Call, LU, Msgs, true, LinkedN);
{trace, Pid, exit, normal ?schedid} ->
- check_consistent(Pid, Recv, Call, LU, Msgs);
+ check_consistent(Pid, Recv, Call, LU, Msgs, true, LinkedN);
{trace_ts, Pid, exit, normal ?schedid, _} ->
- check_consistent(Pid, Recv, Call, LU, Msgs);
+ check_consistent(Pid, Recv, Call, LU, Msgs, true, LinkedN);
{'EXIT', Pid, normal} ->
- check_consistent(Pid, Recv, Call, LU, Msgs);
+ check_consistent(Pid, Recv, Call, LU, Msgs, true, LinkedN);
Msg ->
{error, Msg}
end;
-check_consistent(_, 0, 0, 0, []) ->
+check_consistent(_, 0, 0, 1, [], true, _) ->
ok;
-check_consistent(_, Recv, Call, LU, []) ->
+check_consistent(_, Recv, Call, LU, [], _, _) ->
{error,{Recv, Call, LU}}.
receive_msg(M) ->
diff --git a/erts/etc/common/erlexec.c b/erts/etc/common/erlexec.c
index f7c8c27d53..f7774c6e2e 100644
--- a/erts/etc/common/erlexec.c
+++ b/erts/etc/common/erlexec.c
@@ -80,6 +80,8 @@ static char *plusM_au_alloc_switches[] = {
"as",
"asbcst",
"acul",
+ "acnl",
+ "acfml",
"e",
"t",
"lmbcs",
diff --git a/erts/etc/common/inet_gethost.c b/erts/etc/common/inet_gethost.c
index b746487668..4fc7a93348 100644
--- a/erts/etc/common/inet_gethost.c
+++ b/erts/etc/common/inet_gethost.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1998-2017. All Rights Reserved.
+ * Copyright Ericsson AB 1998-2018. 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.
@@ -1770,7 +1770,7 @@ static int worker_loop(void)
}
#elif defined(HAVE_GETIPNODEBYNAME) /*#ifdef HAVE_GETADDRINFO */
DEBUGF(5,("Starting getipnodebyname(%s)",data));
- he = getipnodebyname(data, AF_INET6, AI_DEFAULT, &error_num);
+ he = getipnodebyname(data, AF_INET6, 0, &error_num);
if (he) {
free_he = 1;
error_num = 0;
diff --git a/erts/etc/unix/etp-commands.in b/erts/etc/unix/etp-commands.in
index 95e065b156..bac90cb472 100644
--- a/erts/etc/unix/etp-commands.in
+++ b/erts/etc/unix/etp-commands.in
@@ -2,7 +2,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2005-2017. All Rights Reserved.
+# Copyright Ericsson AB 2005-2018. 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.
@@ -1290,7 +1290,7 @@ document etp-msgq
% Sequential trace tokens are included in comments and
% the current match position in the queue is marked '<='.
%
-% A process's message queue is process_tab[i]->msg.
+% A process's message queue is process_tab[i]->sig_qs.
%---------------------------------------------------------------------------
end
@@ -1693,7 +1693,7 @@ define etp-proc-state-int
printf "dirty-cpu-proc | "
end
if ($arg0 & 0x2000000)
- printf "on-heap-msgq | "
+ printf "sig-q | "
end
if ($arg0 & 0x1000000)
printf "off-heap-msgq | "
@@ -1714,10 +1714,10 @@ define etp-proc-state-int
printf "active-sys | "
end
if ($arg0 & 0x80000)
- printf "trapping-exit | "
+ printf "sig-in-q | "
end
if ($arg0 & 0x40000)
- printf "bound | "
+ printf "sys-tasks | "
end
if ($arg0 & 0x20000)
printf "garbage-collecting | "
@@ -1735,7 +1735,7 @@ define etp-proc-state-int
printf "active | "
end
if ($arg0 & 0x1000)
- printf "pending-exit | "
+ printf "unused | "
end
if ($arg0 & 0x800)
printf "exiting | "
@@ -1819,160 +1819,21 @@ document etp-proc-state
% Print state of process
%---------------------------------------------------------------------------
end
-define etp-proc-state-int
+
+define etp-proc-flags-int
# Args: int
#
- if ($arg0 & 0x80000000)
- printf "GARBAGE<0x80000000> | "
- end
- if ($arg0 & 0x40000000)
- printf "dirty-running-sys | "
- end
- if ($arg0 & 0x20000000)
- printf "dirty-running | "
- end
- if ($arg0 & 0x10000000)
- printf "dirty-active-sys | "
+ if ($arg0 & ~0xfffffff)
+ printf "GARBAGE<%x> ", ($arg0 & ~0x1ffffff)
end
if ($arg0 & 0x8000000)
- printf "dirty-io-proc | "
+ printf "trap-exit "
end
if ($arg0 & 0x4000000)
- printf "dirty-cpu-proc | "
+ printf "local-sigs-only "
end
if ($arg0 & 0x2000000)
- printf "on-heap-msgq | "
- end
- if ($arg0 & 0x1000000)
- printf "off-heap-msgq | "
- end
- if ($arg0 & 0x800000)
- printf "delayed-sys | "
- end
- if ($arg0 & 0x400000)
- printf "proxy | "
- set $proxy_process = 1
- else
- set $proxy_process = 0
- end
- if ($arg0 & 0x200000)
- printf "running-sys | "
- end
- if ($arg0 & 0x100000)
- printf "active-sys | "
- end
- if ($arg0 & 0x80000)
- printf "trapping-exit | "
- end
- if ($arg0 & 0x40000)
- printf "bound | "
- end
- if ($arg0 & 0x20000)
- printf "garbage-collecting | "
- end
- if ($arg0 & 0x10000)
- printf "suspended | "
- end
- if ($arg0 & 0x8000)
- printf "running | "
- end
- if ($arg0 & 0x4000)
- printf "in-run-queue | "
- end
- if ($arg0 & 0x2000)
- printf "active | "
- end
- if ($arg0 & 0x1000)
- printf "pending-exit | "
- end
- if ($arg0 & 0x800)
- printf "exiting | "
- end
- if ($arg0 & 0x400)
- printf "free | "
- end
- if ($arg0 & 0x200)
- printf "in-prq-low | "
- end
- if ($arg0 & 0x100)
- printf "in-prq-normal | "
- end
- if ($arg0 & 0x80)
- printf "in-prq-high | "
- end
- if ($arg0 & 0x40)
- printf "in-prq-max | "
- end
- if ($arg0 & 0x30) == 0x0
- printf "prq-prio-max | "
- else
- if ($arg0 & 0x30) == 0x10
- printf "prq-prio-high | "
- else
- if ($arg0 & 0x30) == 0x20
- printf "prq-prio-normal | "
- else
- printf "prq-prio-low | "
- end
- end
- end
- if ($arg0 & 0xc) == 0x0
- printf "usr-prio-max | "
- else
- if ($arg0 & 0xc) == 0x4
- printf "usr-prio-high | "
- else
- if ($arg0 & 0xc) == 0x8
- printf "usr-prio-normal | "
- else
- printf "usr-prio-low | "
- end
- end
- end
- if ($arg0 & 0x3) == 0x0
- printf "act-prio-max\n"
- else
- if ($arg0 & 0x3) == 0x1
- printf "act-prio-high\n"
- else
- if ($arg0 & 0x3) == 0x2
- printf "act-prio-normal\n"
- else
- printf "act-prio-low\n"
- end
- end
- end
-end
-
-document etp-proc-state-int
-%---------------------------------------------------------------------------
-% etp-proc-state-int int
-%
-% Print state of process state value
-%---------------------------------------------------------------------------
-end
-
-
-define etp-proc-state
-# Args: Process*
-#
- set $state_int = *(((Uint32 *) &(((Process *) $arg0)->state)))
- etp-proc-state-int $state_int
-end
-
-document etp-proc-state
-%---------------------------------------------------------------------------
-% etp-proc-state Process*
-%
-% Print state of process
-%---------------------------------------------------------------------------
-end
-
-define etp-proc-flags-int
-# Args: int
-#
- if ($arg0 & ~0x1ffffff)
- printf "GARBAGE<%x> ", ($arg0 & ~0x1ffffff)
+ printf "hibernated "
end
if ($arg0 & 0x1000000)
printf "dirty-minor-gc "
@@ -2131,9 +1992,9 @@ define etp-process-info
end
printf " Mbuf size: %ld\n", $etp_proc->mbuf_sz
if (etp_smp_compiled)
- printf " Msgq len: %ld (inner=%ld, outer=%ld)\n", ($etp_proc->msg.len + $etp_proc->msg_inq.len), $etp_proc->msg.len, $etp_proc->msg_inq.len
+ printf " Msgq len: %ld (inner=%ld, outer=%ld)\n", ($etp_proc->sig_qs.len + $etp_proc->sig_inq.len), $etp_proc->sig_qs.len, $etp_proc->sig_inq.len
else
- printf " Msgq len: %d\n", $etp_proc->msg.len
+ printf " Msgq len: %d\n", $etp_proc->sig_qs.len
end
printf " Parent: "
etp-1 $etp_proc->parent
@@ -2236,9 +2097,9 @@ define etp-process-memory-info
end
printf "] [Mbuf: %5ld", $etp_pmem_proc->mbuf_sz
if (etp_smp_compiled)
- printf " | %3ld (%3ld | %3ld)", ($etp_pmem_proc->msg.len + $etp_pmem_proc->msg_inq.len), $etp_pmem_proc->msg.len, $etp_pmem_proc->msg_inq.len
+ printf " | %3ld (%3ld | %3ld)", ($etp_pmem_proc->sig_qs.len + $etp_pmem_proc->sig_inq.len), $etp_pmem_proc->sig_qs.len, $etp_pmem_proc->sig_inq.len
else
- printf " | %3ld", $etp_pmem_proc->msg.len
+ printf " | %3ld", $etp_pmem_proc->sig_qs.len
end
printf "] "
if ($etp_pmem_proc->i)
@@ -3696,7 +3557,7 @@ define etp-chart
# Non-reentrant
etp-chart-start ($arg0)
set ($arg0) = ($arg0)
- etp-msgq (($arg0)->msg)
+ etp-msgq (($arg0)->sig_qs)
etp-stackdump ($arg0)
etp-dictdump (($arg0)->dictionary)
etp-dictdump (($arg0)->debug_dictionary)
diff --git a/erts/lib_src/common/erl_printf_format.c b/erts/lib_src/common/erl_printf_format.c
index 3302083288..5a680d6f9d 100644
--- a/erts/lib_src/common/erl_printf_format.c
+++ b/erts/lib_src/common/erl_printf_format.c
@@ -326,7 +326,7 @@ static int fmt_double(fmtfn_t fn,void*arg,double val,
{
int res;
int fi = 0;
- char format_str[7];
+ char format_str[8];
char sbuf[32];
char *bufp = sbuf;
double dexp;
diff --git a/erts/preloaded/ebin/erl_prim_loader.beam b/erts/preloaded/ebin/erl_prim_loader.beam
index 2d86dd3843..02e77bfbb2 100644
--- a/erts/preloaded/ebin/erl_prim_loader.beam
+++ b/erts/preloaded/ebin/erl_prim_loader.beam
Binary files differ
diff --git a/erts/preloaded/ebin/erlang.beam b/erts/preloaded/ebin/erlang.beam
index f0da8b5869..e93f053e01 100644
--- a/erts/preloaded/ebin/erlang.beam
+++ b/erts/preloaded/ebin/erlang.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
deleted file mode 100644
index 2a4cb53d3e..0000000000
--- a/erts/preloaded/ebin/erts_dirty_process_code_checker.beam
+++ /dev/null
Binary files differ
diff --git a/erts/preloaded/ebin/erts_dirty_process_signal_handler.beam b/erts/preloaded/ebin/erts_dirty_process_signal_handler.beam
new file mode 100644
index 0000000000..2df6da7415
--- /dev/null
+++ b/erts/preloaded/ebin/erts_dirty_process_signal_handler.beam
Binary files differ
diff --git a/erts/preloaded/ebin/erts_internal.beam b/erts/preloaded/ebin/erts_internal.beam
index 7d2edd9845..f5967780ad 100644
--- a/erts/preloaded/ebin/erts_internal.beam
+++ b/erts/preloaded/ebin/erts_internal.beam
Binary files differ
diff --git a/erts/preloaded/ebin/prim_file.beam b/erts/preloaded/ebin/prim_file.beam
index 902b0945c6..9cc22222db 100644
--- a/erts/preloaded/ebin/prim_file.beam
+++ b/erts/preloaded/ebin/prim_file.beam
Binary files differ
diff --git a/erts/preloaded/src/Makefile b/erts/preloaded/src/Makefile
index a2872082f2..4333f6643a 100644
--- a/erts/preloaded/src/Makefile
+++ b/erts/preloaded/src/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2008-2016. All Rights Reserved.
+# Copyright Ericsson AB 2008-2018. 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.
@@ -47,7 +47,7 @@ PRE_LOADED_ERL_MODULES = \
erts_internal \
erl_tracer \
erts_literal_area_collector \
- erts_dirty_process_code_checker
+ erts_dirty_process_signal_handler
PRE_LOADED_BEAM_MODULES = \
prim_eval
diff --git a/erts/preloaded/src/erl_prim_loader.erl b/erts/preloaded/src/erl_prim_loader.erl
index d16fe5a0bc..b82b9f5b46 100644
--- a/erts/preloaded/src/erl_prim_loader.erl
+++ b/erts/preloaded/src/erl_prim_loader.erl
@@ -56,7 +56,7 @@
-export([purge_archive_cache/0]).
%% Used by init and the code server.
--export([get_modules/2,get_modules/3]).
+-export([get_modules/2,get_modules/3, is_basename/1]).
-include_lib("kernel/include/file.hrl").
diff --git a/erts/preloaded/src/erlang.erl b/erts/preloaded/src/erlang.erl
index 278a485a01..bffa59338e 100644
--- a/erts/preloaded/src/erlang.erl
+++ b/erts/preloaded/src/erlang.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2017. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2018. 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.
@@ -31,7 +31,7 @@
-export([localtime_to_universaltime/1]).
-export([suspend_process/1]).
-export([min/2, max/2]).
--export([dmonitor_node/3, dmonitor_p/2]).
+-export([dmonitor_node/3]).
-export([delay_trap/2]).
-export([set_cookie/2, get_cookie/0]).
-export([nodes/0]).
@@ -39,7 +39,6 @@
-export([integer_to_list/2]).
-export([integer_to_binary/2]).
-export([set_cpu_topology/1, format_cpu_topology/1]).
--export([await_proc_exit/3]).
-export([memory/0, memory/1]).
-export([alloc_info/1, alloc_sizes/1]).
@@ -122,7 +121,7 @@
-export([delete_element/2]).
-export([delete_module/1, demonitor/1, demonitor/2, display/1]).
-export([display_nl/0, display_string/1, erase/0, erase/1]).
--export([error/1, error/2, exit/1, exit/2, external_size/1]).
+-export([error/1, error/2, exit/1, exit/2, exit_signal/2, external_size/1]).
-export([external_size/2, finish_after_on_load/2, finish_loading/1, float/1]).
-export([float_to_binary/1, float_to_binary/2,
float_to_list/1, float_to_list/2, floor/1]).
@@ -786,6 +785,13 @@ exit(_Reason) ->
exit(_Pid, _Reason) ->
erlang:nif_error(undefined).
+%% exit_signal/2
+-spec erlang:exit_signal(Pid, Reason) -> true when
+ Pid :: pid() | port(),
+ Reason :: term().
+exit_signal(_Pid, _Reason) ->
+ erlang:nif_error(undefined).
+
%% external_size/1
-spec erlang:external_size(Term) -> non_neg_integer() when
Term :: term().
@@ -1012,8 +1018,20 @@ group_leader() ->
-spec group_leader(GroupLeader, Pid) -> true when
GroupLeader :: pid(),
Pid :: pid().
-group_leader(_GroupLeader, _Pid) ->
- erlang:nif_error(undefined).
+group_leader(GroupLeader, Pid) ->
+ case case erts_internal:group_leader(GroupLeader, Pid) of
+ false ->
+ Ref = erlang:make_ref(),
+ erts_internal:group_leader(GroupLeader,
+ Pid,
+ Ref),
+ receive {Ref, MsgRes} -> MsgRes end;
+ Res ->
+ Res
+ end of
+ true -> true;
+ Error -> erlang:error(Error, [GroupLeader, Pid])
+ end.
%% halt/0
%% Shadowed by erl_bif_types: erlang:halt/0
@@ -1904,7 +1922,7 @@ element(_N, _Tuple) ->
%% Not documented
-type module_info_key() :: attributes | compile | exports | functions | md5
- | module | native | native_addresses.
+ | module | native | native_addresses | nifs.
-spec erlang:get_module_info(Module, Item) -> ModuleInfo when
Module :: atom(),
Item :: module_info_key(),
@@ -2403,6 +2421,10 @@ subtract(_,_) ->
OldDirtyCPUSchedulersOnline when
DirtyCPUSchedulersOnline :: pos_integer(),
OldDirtyCPUSchedulersOnline :: pos_integer();
+ (erts_alloc, {Alloc, F, V}) -> ok | notsup when
+ Alloc :: atom(),
+ F :: atom(),
+ V :: integer();
(fullsweep_after, Number) -> OldNumber when
Number :: non_neg_integer(),
OldNumber :: non_neg_integer();
@@ -2559,10 +2581,10 @@ tuple_to_list(_Tuple) ->
Settings :: [{Subsystem :: atom(),
[{Parameter :: atom(),
Value :: term()}]}];
- (alloc_util_allocators) -> [Alloc] when
- Alloc :: atom();
({allocator, Alloc}) -> [_] when %% More or less anything
Alloc :: atom();
+ (alloc_util_allocators) -> [Alloc] when
+ Alloc :: atom();
({allocator_sizes, Alloc}) -> [_] when %% More or less anything
Alloc :: atom();
(atom_count) -> pos_integer();
@@ -2589,6 +2611,7 @@ tuple_to_list(_Tuple) ->
(driver_version) -> string();
(dynamic_trace) -> none | dtrace | systemtap;
(dynamic_trace_probes) -> boolean();
+ (end_time) -> non_neg_integer();
(elib_malloc) -> false;
(eager_check_io) -> boolean();
(ets_limit) -> pos_integer();
@@ -2616,6 +2639,7 @@ tuple_to_list(_Tuple) ->
(otp_release) -> string();
(os_monotonic_time_source) -> [{atom(),term()}];
(os_system_time_source) -> [{atom(),term()}];
+ (port_parallelism) -> boolean();
(port_count) -> non_neg_integer();
(port_limit) -> pos_integer();
(process_count) -> pos_integer();
@@ -2645,7 +2669,8 @@ tuple_to_list(_Tuple) ->
(trace_control_word) -> non_neg_integer();
(update_cpu_info) -> changed | unchanged;
(version) -> string();
- (wordsize | {wordsize, internal} | {wordsize, external}) -> 4 | 8.
+ (wordsize | {wordsize, internal} | {wordsize, external}) -> 4 | 8;
+ (overview) -> boolean().
system_info(_Item) ->
erlang:nif_error(undefined).
@@ -3272,13 +3297,6 @@ dmonitor_node(Node, Flag, Opts) ->
dmonitor_node(Node,Flag,[])
end.
--spec erlang:dmonitor_p('process', pid() | {atom(),atom()}) -> reference().
-dmonitor_p(process, ProcSpec) ->
- %% Only called when auto-connect attempt failed early in VM
- Ref = erlang:make_ref(),
- erlang:self() ! {'DOWN', Ref, process, ProcSpec, noconnection},
- Ref.
-
%%
%% Trap function used when modified timing has been enabled.
%%
@@ -3511,33 +3529,6 @@ rvrs(Xs) -> rvrs(Xs, []).
rvrs([],Ys) -> Ys;
rvrs([X|Xs],Ys) -> rvrs(Xs, [X|Ys]).
-%% erlang:await_proc_exit/3 is for internal use only!
-%%
-%% BIFs that need to await a specific process exit before
-%% returning traps to erlang:await_proc_exit/3.
-%%
-%% NOTE: This function is tightly coupled to
-%% the implementation of the
-%% erts_bif_prep_await_proc_exit_*()
-%% functions in bif.c. Do not make
-%% any changes to it without reading
-%% the comment about them in bif.c!
--spec erlang:await_proc_exit(dst(), 'apply' | 'data' | 'reason', term()) -> term().
-await_proc_exit(Proc, Op, Data) ->
- Mon = erlang:monitor(process, Proc),
- receive
- {'DOWN', Mon, process, _Proc, Reason} ->
- case Op of
- apply ->
- {M, F, A} = Data,
- erlang:apply(M, F, A);
- data ->
- Data;
- reason ->
- Reason
- end
- end.
-
-spec min(Term1, Term2) -> Minimum when
Term1 :: term(),
Term2 :: term(),
@@ -3730,15 +3721,14 @@ memory_is_supported() ->
get_blocks_size([{blocks_size, Sz, _, _} | Rest], Acc) ->
get_blocks_size(Rest, Acc+Sz);
-get_blocks_size([{_, _, _, _} | Rest], Acc) ->
- get_blocks_size(Rest, Acc);
get_blocks_size([{blocks_size, Sz} | Rest], Acc) ->
get_blocks_size(Rest, Acc+Sz);
-get_blocks_size([{_, _} | Rest], Acc) ->
+get_blocks_size([_ | Rest], Acc) ->
get_blocks_size(Rest, Acc);
get_blocks_size([], Acc) ->
Acc.
+
blocks_size([{Carriers, SizeList} | Rest], Acc) when Carriers == mbcs;
Carriers == mbcs_pool;
Carriers == sbcs ->
diff --git a/erts/preloaded/src/erts_dirty_process_code_checker.erl b/erts/preloaded/src/erts_dirty_process_code_checker.erl
deleted file mode 100644
index 7d3fa264be..0000000000
--- a/erts/preloaded/src/erts_dirty_process_code_checker.erl
+++ /dev/null
@@ -1,81 +0,0 @@
-%%
-%% %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} = 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_dirty_process_signal_handler.erl b/erts/preloaded/src/erts_dirty_process_signal_handler.erl
new file mode 100644
index 0000000000..ab71790b9d
--- /dev/null
+++ b/erts/preloaded/src/erts_dirty_process_signal_handler.erl
@@ -0,0 +1,97 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2018. 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_signal_handler).
+
+-export([start/0]).
+
+%%
+%% The erts_dirty_process_signal_handler 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 ->
+ try
+ handle_request(Request)
+ catch
+ _ : _ ->
+ %% Ignore all failures;
+ %% someone passed us garbage...
+ ok
+ end
+ end,
+ msg_loop().
+
+handle_request(Pid) when is_pid(Pid) ->
+ handle_incoming_signals(Pid, 0);
+handle_request({Requester, Target, Prio,
+ {SysTaskOp, ReqId, Arg} = Op} = Request) ->
+ case handle_sys_task(Requester, Target, SysTaskOp, ReqId, Arg) of
+ true ->
+ ok;
+ false ->
+ %% Target has stopped executing dirty since the
+ %% initial request was made. Dispatch the
+ %% request to target and let it handle it itself...
+ case erts_internal:request_system_task(Requester,
+ Target,
+ Prio,
+ Op) of
+ ok ->
+ ok;
+ dirty_execution ->
+ %% Ahh... It began executing dirty again...
+ handle_request(Request)
+ end
+ end;
+handle_request(_Garbage) ->
+ ignore.
+
+%%
+%% ----------------------------------------------------------------------------
+%%
+
+handle_incoming_signals(Pid, 5) ->
+ self() ! Pid; %% Work with other requests for a while...
+handle_incoming_signals(Pid, N) ->
+ case erts_internal:dirty_process_handle_signals(Pid) of
+ more -> handle_incoming_signals(Pid, N+1);
+ _Res -> ok
+ end.
+
+handle_sys_task(Requester, Target, check_process_code, ReqId, Module) ->
+ case erts_internal:is_process_executing_dirty(Target) of
+ false ->
+ false;
+ true ->
+ _ = check_process(Requester, Target, ReqId, Module),
+ true
+ end.
+
+check_process(Requester, Target, ReqId, Module) ->
+ Result = erts_internal:check_dirty_process_code(Target, Module),
+ Requester ! {check_process_code, ReqId, Result}.
diff --git a/erts/preloaded/src/erts_internal.erl b/erts/preloaded/src/erts_internal.erl
index a083e9ac2f..da5c9c68ed 100644
--- a/erts/preloaded/src/erts_internal.erl
+++ b/erts/preloaded/src/erts_internal.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2012-2017. All Rights Reserved.
+%% Copyright Ericsson AB 2012-2018. 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.
@@ -45,6 +45,8 @@
-export([check_process_code/3]).
-export([check_dirty_process_code/2]).
-export([is_process_executing_dirty/1]).
+-export([dirty_process_handle_signals/1]).
+
-export([release_literal_area_switch/0]).
-export([purge_module/2]).
@@ -63,6 +65,7 @@
-export([dist_ctrl_put_data/2]).
+-export([get_dflags/0]).
-export([new_connection/1]).
-export([abort_connection/2]).
@@ -70,9 +73,13 @@
gather_sched_wall_time_result/1,
await_sched_wall_time_modifications/2]).
+-export([group_leader/2, group_leader/3]).
+
%% Auto import name clash
-export([check_process_code/1]).
+-export([is_process_alive/1, is_process_alive/2]).
+
%%
%% Await result of send to port
%%
@@ -305,6 +312,13 @@ check_dirty_process_code(_Pid,_Module) ->
is_process_executing_dirty(_Pid) ->
erlang:nif_error(undefined).
+-spec dirty_process_handle_signals(Pid) -> Res when
+ Pid :: pid(),
+ Res :: 'false' | 'true' | 'noproc' | 'normal' | 'more' | 'ok'.
+
+dirty_process_handle_signals(_Pid) ->
+ erlang:nif_error(undefined).
+
-spec release_literal_area_switch() -> 'true' | 'false'.
release_literal_area_switch() ->
@@ -497,10 +511,10 @@ dist_ctrl_put_data(DHandle, IoList) ->
%% erlang:dist_ctrl_put_data/2 ...
RootST = try erlang:error(Reason)
catch
- error:Reason ->
- case erlang:get_stacktrace() of
+ error:Reason:ST ->
+ case ST of
[] -> [];
- ST -> tl(ST)
+ [_|T] -> T
end
end,
StackTrace = [{erlang, dist_ctrl_put_data,
@@ -510,6 +524,11 @@ dist_ctrl_put_data(DHandle, IoList) ->
end.
+-spec erts_internal:get_dflags() -> {erts_dflags, integer(), integer(),
+ integer(), integer(), integer()}.
+get_dflags() ->
+ erlang:nif_error(undefined).
+
-spec erts_internal:new_connection(Node) -> ConnId when
Node :: atom(),
ConnId :: {integer(), erlang:dist_handle()}.
@@ -568,3 +587,37 @@ sched_wall_time(Ref, N, Acc) ->
{Ref, SWTL} when erlang:is_list(SWTL) -> sched_wall_time(Ref, N-1, Acc ++ SWTL);
{Ref, SWT} -> sched_wall_time(Ref, N-1, [SWT|Acc])
end.
+
+-spec erts_internal:group_leader(GL, Pid) -> true | false | badarg when
+ GL :: pid(),
+ Pid :: pid().
+
+group_leader(_GL, _Pid) ->
+ erlang:nif_error(undefined).
+
+-spec erts_internal:group_leader(GL, Pid, Ref) -> ok when
+ GL :: pid(),
+ Pid :: pid(),
+ Ref :: reference().
+
+group_leader(_GL, _Pid, _Ref) ->
+ erlang:nif_error(undefined).
+
+-spec erts_internal:is_process_alive(Pid, Ref) -> 'ok' when
+ Pid :: pid(),
+ Ref :: reference().
+
+is_process_alive(_Pid, _Ref) ->
+ erlang:nif_error(undefined).
+
+-spec erts_internal:is_process_alive(Pid) -> boolean() when
+ Pid :: pid().
+
+is_process_alive(Pid) ->
+ Ref = make_ref(),
+ erts_internal:is_process_alive(Pid, Ref),
+ receive
+ {Ref, Res} ->
+ Res
+ end.
+
diff --git a/erts/preloaded/src/init.erl b/erts/preloaded/src/init.erl
index 679a2241d2..e0ae6b1656 100644
--- a/erts/preloaded/src/init.erl
+++ b/erts/preloaded/src/init.erl
@@ -32,8 +32,8 @@
%% (Optional - default efile)
%% -hosts [Node] : List of hosts from which we can boot.
%% (Mandatory if -loader inet)
-%% -mode embedded : Load all modules at startup, no automatic loading
-%% -mode interactive : Auto load modules (default system behaviour).
+%% -mode interactive : Auto load modules not needed at startup (default system behaviour).
+%% -mode embedded : Load all modules in the boot script, disable auto loading.
%% -path : Override path in bootfile.
%% -pa Path+ : Add my own paths first.
%% -pz Path+ : Add my own paths last.
diff --git a/erts/preloaded/src/prim_file.erl b/erts/preloaded/src/prim_file.erl
index 35042a7c72..432a8c15cd 100644
--- a/erts/preloaded/src/prim_file.erl
+++ b/erts/preloaded/src/prim_file.erl
@@ -618,8 +618,10 @@ write_file_info_1(Filename, Info, TimeType) ->
error:_ -> {error, badarg}
end.
-set_owner(_EncodedName, undefined, undefined) ->
- ok;
+set_owner(EncodedName, Uid, undefined) ->
+ set_owner(EncodedName, Uid, -1);
+set_owner(EncodedName, undefined, Gid) ->
+ set_owner(EncodedName, -1, Gid);
set_owner(EncodedName, Uid, Gid) ->
set_owner_nif(EncodedName, Uid, Gid).
set_owner_nif(_Path, _Uid, _Gid) ->
diff --git a/erts/preloaded/src/prim_zip.erl b/erts/preloaded/src/prim_zip.erl
index b1ddbbe173..5cc15b7acd 100644
--- a/erts/preloaded/src/prim_zip.erl
+++ b/erts/preloaded/src/prim_zip.erl
@@ -74,8 +74,8 @@ open(FilterFun, FilterAcc, F) when is_function(FilterFun, 2) ->
throw(Reason);
throw:InternalReason ->
{error, InternalReason};
- Class:Reason ->
- erlang:error(erlang:raise(Class, Reason, erlang:get_stacktrace()))
+ Class:Reason:Stk ->
+ erlang:error(erlang:raise(Class, Reason, Stk))
end;
open(_, _, _) ->
{error, einval}.
@@ -89,9 +89,9 @@ do_open(FilterFun, FilterAcc, F) ->
{PrimZip2, FilterAcc2} = get_central_dir(PrimZip, FilterFun, FilterAcc),
{ok, PrimZip2, FilterAcc2}
catch
- Class:Reason ->
+ Class:Reason:Stk ->
_ = close(PrimZip),
- erlang:error(erlang:raise(Class, Reason, erlang:get_stacktrace()))
+ erlang:error(erlang:raise(Class, Reason, Stk))
end.
%% iterate over all files in a zip archive
@@ -106,8 +106,8 @@ foldl(FilterFun, FilterAcc, #primzip{files = Files} = PrimZip)
throw(Reason);
throw:InternalReason ->
{error, InternalReason};
- Class:Reason ->
- erlang:error(erlang:raise(Class, Reason, erlang:get_stacktrace()))
+ Class:Reason:Stk ->
+ erlang:error(erlang:raise(Class, Reason, Stk))
end;
foldl(_, _, _) ->
{error, einval}.
diff --git a/erts/test/z_SUITE.erl b/erts/test/z_SUITE.erl
index b3d0ba20bc..bb1afecafc 100644
--- a/erts/test/z_SUITE.erl
+++ b/erts/test/z_SUITE.erl
@@ -40,7 +40,7 @@
suite() ->
[{ct_hooks,[ts_install_cth]},
- {timetrap, {minutes, 5}}].
+ {timetrap, {minutes, 10}}].
all() ->
[core_files].
diff --git a/erts/vsn.mk b/erts/vsn.mk
index 8b22afea3b..c3a62a5535 100644
--- a/erts/vsn.mk
+++ b/erts/vsn.mk
@@ -18,7 +18,7 @@
# %CopyrightEnd%
#
-VSN = 9.2
+VSN = 9.3
# Port number 4365 in 4.2
# Port number 4366 in 4.3
diff --git a/lib/asn1/doc/src/notes.xml b/lib/asn1/doc/src/notes.xml
index 1abe983221..bb15c9ff5f 100644
--- a/lib/asn1/doc/src/notes.xml
+++ b/lib/asn1/doc/src/notes.xml
@@ -32,6 +32,23 @@
<p>This document describes the changes made to the asn1 application.</p>
+<section><title>Asn1 5.0.5</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Dialyzer suppression has been added for the generated
+ ASN.1 helper function to_bitstring/1 that previously
+ created irrelevant warnings.</p>
+ <p>
+ Own Id: OTP-13882 Aux Id: ERIERL-144 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Asn1 5.0.4</title>
<section><title>Fixed Bugs and Malfunctions</title>
diff --git a/lib/asn1/src/asn1ct_gen_per.erl b/lib/asn1/src/asn1ct_gen_per.erl
index 82e9326294..c09b0f47d1 100644
--- a/lib/asn1/src/asn1ct_gen_per.erl
+++ b/lib/asn1/src/asn1ct_gen_per.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2017. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2018. 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.
@@ -47,14 +47,20 @@ dialyzer_suppressions(#gen{erule=per,aligned=Aligned}) ->
false -> uper;
true -> per
end,
- case asn1ct_func:is_used({Mod,complete,1}) of
+ suppress({Mod,complete,1}),
+ suppress({per_common,to_bitstring,2}),
+ emit([" ok.",nl]).
+
+suppress({M,F,A}=MFA) ->
+ case asn1ct_func:is_used(MFA) of
false ->
ok;
true ->
- emit([" _ = complete(Arg),",nl])
- end,
- emit([" ok.",nl]).
-
+ Args =
+ [lists:concat(["element(",I,", Arg)"])
+ || I <- lists:seq(1, A)],
+ emit([" ",{call,M,F,Args},com,nl])
+ end.
gen_encode(Erules,Type) when is_record(Type,typedef) ->
gen_encode_user(Erules,Type).
diff --git a/lib/asn1/vsn.mk b/lib/asn1/vsn.mk
index 4cd89089e9..39dfe8f4fb 100644
--- a/lib/asn1/vsn.mk
+++ b/lib/asn1/vsn.mk
@@ -1 +1 @@
-ASN1_VSN = 5.0.4
+ASN1_VSN = 5.0.5
diff --git a/lib/common_test/doc/src/notes.xml b/lib/common_test/doc/src/notes.xml
index c6b928bb5d..7e909b24cd 100644
--- a/lib/common_test/doc/src/notes.xml
+++ b/lib/common_test/doc/src/notes.xml
@@ -33,6 +33,22 @@
<file>notes.xml</file>
</header>
+<section><title>Common_Test 1.15.4</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Fixed problem with 'skip_groups' in combination with 'all
+ suites' option in test specification.</p>
+ <p>
+ Own Id: OTP-14953</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Common_Test 1.15.3</title>
<section><title>Improvements and New Features</title>
diff --git a/lib/common_test/src/common_test.app.src b/lib/common_test/src/common_test.app.src
index 0aa4aacf16..9becde110b 100644
--- a/lib/common_test/src/common_test.app.src
+++ b/lib/common_test/src/common_test.app.src
@@ -92,7 +92,7 @@
"sasl-2.4.2",
"snmp-5.1.2",
"ssh-4.0",
- "stdlib-3.4",
+ "stdlib-3.5",
"syntax_tools-1.7",
"tools-2.8",
"xmerl-1.3.8"
diff --git a/lib/common_test/src/ct.erl b/lib/common_test/src/ct.erl
index 69e371a30f..fd7fa07b81 100644
--- a/lib/common_test/src/ct.erl
+++ b/lib/common_test/src/ct.erl
@@ -872,8 +872,8 @@ fail(Reason) ->
try
exit({test_case_failed,Reason})
catch
- Class:R ->
- case erlang:get_stacktrace() of
+ Class:R:S ->
+ case S of
[{?MODULE,fail,1,_}|Stk] -> ok;
Stk -> ok
end,
@@ -894,8 +894,8 @@ fail(Format, Args) ->
try
exit({test_case_failed,lists:flatten(Str)})
catch
- Class:R ->
- case erlang:get_stacktrace() of
+ Class:R:S ->
+ case S of
[{?MODULE,fail,2,_}|Stk] -> ok;
Stk -> ok
end,
diff --git a/lib/common_test/src/ct_hooks.erl b/lib/common_test/src/ct_hooks.erl
index f0592a40be..bb33f0243b 100644
--- a/lib/common_test/src/ct_hooks.erl
+++ b/lib/common_test/src/ct_hooks.erl
@@ -233,8 +233,7 @@ call([{Hook, call_id, NextFun} | Rest], Config, Meta, Hooks) ->
Rest ++ [{NewId, call_init}, {NewId,NextFun}]}
end,
call(resort(NewRest,NewHooks,Meta), Config, Meta, NewHooks)
- catch Error:Reason ->
- Trace = erlang:get_stacktrace(),
+ catch Error:Reason:Trace ->
ct_logs:log("Suite Hook","Failed to start a CTH: ~tp:~tp",
[Error,{Reason,Trace}]),
call([], {fail,"Failed to start CTH"
@@ -422,8 +421,7 @@ catch_apply(M,F,A, Default, Fallback) ->
catch_apply(M,F,A) ->
try
erlang:apply(M,F,A)
- catch _:Reason ->
- Trace = erlang:get_stacktrace(),
+ catch _:Reason:Trace ->
ct_logs:log("Suite Hook","Call to CTH failed: ~w:~tp",
[error,{Reason,Trace}]),
throw({error_in_cth_call,
diff --git a/lib/common_test/src/ct_run.erl b/lib/common_test/src/ct_run.erl
index 8c401cf3f5..afc5fee4f7 100644
--- a/lib/common_test/src/ct_run.erl
+++ b/lib/common_test/src/ct_run.erl
@@ -442,11 +442,9 @@ script_start2(Opts = #opts{vts = undefined,
TestSpecData ->
execute_all_specs(TestSpecData, Opts, Args, [])
catch
- throw:{error,Reason} ->
- StackTrace = erlang:get_stacktrace(),
+ throw:{error,Reason}:StackTrace ->
{error,{invalid_testspec,{Reason,StackTrace}}};
- _:Reason ->
- StackTrace = erlang:get_stacktrace(),
+ _:Reason:StackTrace ->
{error,{invalid_testspec,{Reason,StackTrace}}}
end;
[] ->
@@ -1211,11 +1209,9 @@ run_spec_file(Relaxed,
TestSpecData ->
run_all_specs(TestSpecData, Opts, StartOpts, [])
catch
- throw:{error,CTReason} ->
- StackTrace = erlang:get_stacktrace(),
+ throw:{error,CTReason}:StackTrace ->
exit({error,{invalid_testspec,{CTReason,StackTrace}}});
- _:CTReason ->
- StackTrace = erlang:get_stacktrace(),
+ _:CTReason:StackTrace ->
exit({error,{invalid_testspec,{CTReason,StackTrace}}})
end.
diff --git a/lib/common_test/src/ct_testspec.erl b/lib/common_test/src/ct_testspec.erl
index bb445bb0d2..bd3755722f 100644
--- a/lib/common_test/src/ct_testspec.erl
+++ b/lib/common_test/src/ct_testspec.erl
@@ -1425,7 +1425,12 @@ skip_groups1(Suite,Groups,Cmt,Suites0) ->
GrAndCases1 = GrAndCases0 ++ SkipGroups,
insert_in_order({Suite,GrAndCases1},Suites0,replace);
false ->
- insert_in_order({Suite,SkipGroups},Suites0,replace)
+ case Suites0 of
+ [{all,_}=All|Skips]->
+ [All|Skips++[{Suite,SkipGroups}]];
+ _ ->
+ insert_in_order({Suite,SkipGroups},Suites0,replace)
+ end
end.
skip_cases(Node,Dir,Suite,Cases,Cmt,Tests,false) when is_list(Cases) ->
diff --git a/lib/common_test/src/ct_util.erl b/lib/common_test/src/ct_util.erl
index 10a06d5c88..e904bb1e7c 100644
--- a/lib/common_test/src/ct_util.erl
+++ b/lib/common_test/src/ct_util.erl
@@ -209,11 +209,10 @@ do_start(Parent, Mode, LogDir, Verbosity) ->
self() ! {{stop,{self(),{user_error,CTHReason}}},
{Parent,make_ref()}}
catch
- _:CTHReason ->
+ _:CTHReason:StackTrace ->
ErrorInfo = if is_atom(CTHReason) ->
io_lib:format("{~tp,~tp}",
- [CTHReason,
- erlang:get_stacktrace()]);
+ [CTHReason, StackTrace]);
true ->
CTHReason
end,
diff --git a/lib/common_test/src/ct_webtool.erl b/lib/common_test/src/ct_webtool.erl
index 82aa78fc4b..c7174b13f5 100644
--- a/lib/common_test/src/ct_webtool.erl
+++ b/lib/common_test/src/ct_webtool.erl
@@ -771,7 +771,7 @@ fill_out(Nr)->
%Controls whether the user selected a tool to start
%----------------------------------------------------------------------
get_tools(Input)->
- case httpd:parse_query(Input) of
+ case uri_string:dissect_query(Input) of
[]->
no_tools;
Tools->
diff --git a/lib/common_test/src/test_server.erl b/lib/common_test/src/test_server.erl
index 7250041e13..9469619aa9 100644
--- a/lib/common_test/src/test_server.erl
+++ b/lib/common_test/src/test_server.erl
@@ -1340,13 +1340,12 @@ do_init_per_testcase(Mod, Args) ->
{skip,Reason};
exit:{Skip,Reason} when Skip =:= skip; Skip =:= skipped ->
{skip,Reason};
- throw:Other ->
- set_loc(erlang:get_stacktrace()),
+ throw:Other:Stk ->
+ set_loc(Stk),
Line = get_loc(),
print_init_conf_result(Line,"thrown",Other),
{skip,{failed,{Mod,init_per_testcase,Other}}};
- _:Reason0 ->
- Stk = erlang:get_stacktrace(),
+ _:Reason0:Stk ->
Reason = {Reason0,Stk},
set_loc(Stk),
Line = get_loc(),
@@ -1395,20 +1394,19 @@ do_end_per_testcase(Mod,EndFunc,Func,Conf) ->
_ ->
ok
catch
- throw:Other ->
+ throw:Other:Stk ->
Comment0 = case read_comment() of
"" -> "";
Cmt -> Cmt ++ test_server_ctrl:xhtml("<br>",
"<br />")
end,
- set_loc(erlang:get_stacktrace()),
+ set_loc(Stk),
comment(io_lib:format("~ts<font color=\"red\">"
"WARNING: ~w thrown!"
"</font>\n",[Comment0,EndFunc])),
print_end_tc_warning(EndFunc,Other,"thrown",get_loc()),
{failed,{Mod,end_per_testcase,Other}};
- Class:Reason ->
- Stk = erlang:get_stacktrace(),
+ Class:Reason:Stk ->
set_loc(Stk),
Why = case Class of
exit -> {'EXIT',Reason};
@@ -1550,8 +1548,7 @@ ts_tc(M, F, A) ->
throw:{skipped, Reason} -> {skip, Reason};
exit:{skip, Reason} -> {skip, Reason};
exit:{skipped, Reason} -> {skip, Reason};
- Type:Reason ->
- Stk = erlang:get_stacktrace(),
+ Type:Reason:Stk ->
set_loc(Stk),
case Type of
throw ->
@@ -1740,8 +1737,8 @@ fail(Reason) ->
try
exit({suite_failed,Reason})
catch
- Class:R ->
- case erlang:get_stacktrace() of
+ Class:R:Stacktrace ->
+ case Stacktrace of
[{?MODULE,fail,1,_}|Stk] -> ok;
Stk -> ok
end,
@@ -1763,8 +1760,8 @@ fail() ->
try
exit(suite_failed)
catch
- Class:R ->
- case erlang:get_stacktrace() of
+ Class:R:Stacktrace ->
+ case Stacktrace of
[{?MODULE,fail,0,_}|Stk] -> ok;
Stk -> ok
end,
@@ -2043,15 +2040,15 @@ call_user_timetrap(Func, Sup) when is_function(Func) ->
try Func() of
Result ->
Sup ! {self(),Result}
- catch _:Error ->
- exit({Error,erlang:get_stacktrace()})
+ catch _:Error:Stk ->
+ exit({Error,Stk})
end;
call_user_timetrap({M,F,A}, Sup) ->
try apply(M,F,A) of
Result ->
Sup ! {self(),Result}
- catch _:Error ->
- exit({Error,erlang:get_stacktrace()})
+ catch _:Error:Stk ->
+ exit({Error,Stk})
end.
save_user_timetrap(TCPid, UserTTSup, StartTime) ->
diff --git a/lib/common_test/src/test_server_ctrl.erl b/lib/common_test/src/test_server_ctrl.erl
index 3a454a1e84..1ae6c8c7c7 100644
--- a/lib/common_test/src/test_server_ctrl.erl
+++ b/lib/common_test/src/test_server_ctrl.erl
@@ -2301,7 +2301,7 @@ run_test_cases(TestSpec, Config, TimetrapData) ->
%% test_server_io:print_buffered/1 to print the data. To help with this,
%% two variables in the process dictionary are used:
%% 'test_server_common_io_handler' and 'test_server_queued_io'. The values
-%% are set to as follwing:
+%% are set to as following:
%%
%% Value Meaning
%% ----- -------
@@ -5167,7 +5167,7 @@ display_info([Pid|T], R, M) ->
Other
end,
Reds = fetch(reductions, Info),
- LM = length(fetch(messages, Info)),
+ LM = fetch(message_queue_len, Info),
pformat(io_lib:format("~w", [Pid]),
io_lib:format("~tw", [Call]),
io_lib:format("~tw", [Curr]), Reds, LM),
@@ -5739,7 +5739,7 @@ uri_encode_comp([Char|Chars],Encoding) ->
Reserved = sets:is_element(Char, reserved()),
case (Char>127 andalso Encoding==latin1) orelse Reserved of
true ->
- [ $% | http_util:integer_to_hexlist(Char)] ++
+ [ $% | integer_to_list(Char, 16)] ++
uri_encode_comp(Chars,Encoding);
false ->
[Char | uri_encode_comp(Chars,Encoding)]
diff --git a/lib/common_test/src/test_server_sup.erl b/lib/common_test/src/test_server_sup.erl
index 6ddbf1ad27..139621141d 100644
--- a/lib/common_test/src/test_server_sup.erl
+++ b/lib/common_test/src/test_server_sup.erl
@@ -781,8 +781,8 @@ framework_call(Callback,Func,Args,DefaultReturn) ->
catch
exit:Why ->
EH(Why);
- error:Why ->
- EH({Why,erlang:get_stacktrace()});
+ error:Why:Stacktrace ->
+ EH({Why,Stacktrace});
throw:Why ->
EH(Why)
end;
diff --git a/lib/common_test/src/vts.erl b/lib/common_test/src/vts.erl
index 83fcde2f48..2bf3e82ade 100644
--- a/lib/common_test/src/vts.erl
+++ b/lib/common_test/src/vts.erl
@@ -919,7 +919,7 @@ get_input_data(Input,Key)->
end.
parse(Input) ->
- httpd:parse_query(Input).
+ uri_string:dissect_query(Input).
vts_integer_to_list(X) when is_atom(X) ->
atom_to_list(X);
diff --git a/lib/common_test/test/ct_netconfc_SUITE_data/ns.erl b/lib/common_test/test/ct_netconfc_SUITE_data/ns.erl
index 63bf9be134..975f6cafcb 100644
--- a/lib/common_test/test/ct_netconfc_SUITE_data/ns.erl
+++ b/lib/common_test/test/ct_netconfc_SUITE_data/ns.erl
@@ -231,8 +231,7 @@ data_for_channel(CM, Ch, Data, State) ->
{ok, NewState}
end
catch
- Class:Reason ->
- Stacktrace = erlang:get_stacktrace(),
+ Class:Reason:Stacktrace ->
error_logger:error_report([{?MODULE, data_for_channel},
{request, Data},
{buffer, State#session.buffer},
diff --git a/lib/common_test/test/ct_unicode_SUITE_data/unicode_atoms_SUITE.erl b/lib/common_test/test/ct_unicode_SUITE_data/unicode_atoms_SUITE.erl
index 993452500e..041c13cdbd 100644
--- a/lib/common_test/test/ct_unicode_SUITE_data/unicode_atoms_SUITE.erl
+++ b/lib/common_test/test/ct_unicode_SUITE_data/unicode_atoms_SUITE.erl
@@ -77,7 +77,7 @@ all() ->
'fail_αβ_4'(_Config) ->
ct:log("This is test case ~tw",[?FUNCTION_NAME]),
- S = try throw(ok) catch throw:ok -> erlang:get_stacktrace() end,
+ S = try throw(ok) catch throw:ok:Stacktrace -> Stacktrace end,
erlang:raise(error,{error,testcase,?FUNCTION_NAME},S),
ok.
diff --git a/lib/common_test/test_server/ts_install_cth.erl b/lib/common_test/test_server/ts_install_cth.erl
index 5d325b1115..35a5ec916c 100644
--- a/lib/common_test/test_server/ts_install_cth.erl
+++ b/lib/common_test/test_server/ts_install_cth.erl
@@ -108,8 +108,7 @@ pre_init_per_suite(_Suite,Config,State) ->
{add_node_name(Config, State), State}
catch error:{badmatch,{error,enoent}} ->
{add_node_name(Config, State), State};
- Error:Reason ->
- Stack = erlang:get_stacktrace(),
+ Error:Reason:Stack ->
ct:pal("~p failed! ~p:{~p,~p}",[?MODULE,Error,Reason,Stack]),
{{fail,{?MODULE,{Error,Reason, Stack}}},State}
end.
diff --git a/lib/common_test/vsn.mk b/lib/common_test/vsn.mk
index 96fdc89853..ea3e9871cb 100644
--- a/lib/common_test/vsn.mk
+++ b/lib/common_test/vsn.mk
@@ -1 +1 @@
-COMMON_TEST_VSN = 1.15.3
+COMMON_TEST_VSN = 1.15.4
diff --git a/lib/compiler/doc/src/notes.xml b/lib/compiler/doc/src/notes.xml
index f4a3f9875b..bc1f68337b 100644
--- a/lib/compiler/doc/src/notes.xml
+++ b/lib/compiler/doc/src/notes.xml
@@ -32,6 +32,31 @@
<p>This document describes the changes made to the Compiler
application.</p>
+<section><title>Compiler 7.1.5</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>The internal compiler pass (<c>beam_validator</c>)
+ that validates the generated code has been
+ strengthened.</p>
+ <p>When compiling from BEAM assembly code, the
+ <c>beam_type</c> optimizer pass could make the code
+ unsafe. Corrected.</p>
+ <p>
+ Own Id: OTP-14863</p>
+ </item>
+ <item>
+ <p>Corrected optimizations of integers matched out from
+ binaries and used in bit operations.</p>
+ <p>
+ Own Id: OTP-14898</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Compiler 7.1.4</title>
<section><title>Fixed Bugs and Malfunctions</title>
diff --git a/lib/compiler/src/beam_a.erl b/lib/compiler/src/beam_a.erl
index 7df2edd714..91acb19971 100644
--- a/lib/compiler/src/beam_a.erl
+++ b/lib/compiler/src/beam_a.erl
@@ -61,6 +61,14 @@ rename_instrs([{'%live',_}|Is]) ->
%% Ignore old type of live annotation. Only happens when compiling
%% from very old .S files.
rename_instrs(Is);
+rename_instrs([{get_list,S,D1,D2}|Is]) ->
+ %% Only happens when compiling from old .S files.
+ if
+ D1 =:= S ->
+ [{get_tl,S,D2},{get_hd,S,D1}|rename_instrs(Is)];
+ true ->
+ [{get_hd,S,D1},{get_tl,S,D2}|rename_instrs(Is)]
+ end;
rename_instrs([I|Is]) ->
[rename_instr(I)|rename_instrs(Is)];
rename_instrs([]) -> [].
diff --git a/lib/compiler/src/beam_asm.erl b/lib/compiler/src/beam_asm.erl
index 453e00fce3..5ef340c831 100644
--- a/lib/compiler/src/beam_asm.erl
+++ b/lib/compiler/src/beam_asm.erl
@@ -407,7 +407,17 @@ encode_arg({atom, Atom}, Dict0) when is_atom(Atom) ->
{Index, Dict} = beam_dict:atom(Atom, Dict0),
{encode(?tag_a, Index), Dict};
encode_arg({integer, N}, Dict) ->
- {encode(?tag_i, N), Dict};
+ %% Conservatily assume that all integers whose absolute
+ %% value is greater than 1 bsl 128 will be bignums in
+ %% the runtime system.
+ if
+ N >= 1 bsl 128 ->
+ encode_arg({literal, N}, Dict);
+ N =< -(1 bsl 128) ->
+ encode_arg({literal, N}, Dict);
+ true ->
+ {encode(?tag_i, N), Dict}
+ end;
encode_arg(nil, Dict) ->
{encode(?tag_a, 0), Dict};
encode_arg({f, W}, Dict) ->
@@ -465,7 +475,7 @@ encode_alloc_list_1([{floats,Floats}|T], Dict, Acc0) ->
encode_alloc_list_1([], Dict, Acc) ->
{iolist_to_binary(Acc),Dict}.
--spec encode(non_neg_integer(), pos_integer()) -> iodata().
+-spec encode(non_neg_integer(), integer()) -> iodata().
encode(Tag, N) when N < 0 ->
encode1(Tag, negative_to_bytes(N));
diff --git a/lib/compiler/src/beam_block.erl b/lib/compiler/src/beam_block.erl
index 39ae8d5347..8cd271e1dc 100644
--- a/lib/compiler/src/beam_block.erl
+++ b/lib/compiler/src/beam_block.erl
@@ -36,19 +36,18 @@ module({Mod,Exp,Attr,Fs0,Lc}, Opts) ->
function({function,Name,Arity,CLabel,Is0}, Blockify) ->
try
%% Collect basic blocks and optimize them.
- Is2 = case Blockify of
- true ->
- Is1 = blockify(Is0),
- embed_lines(Is1);
- false ->
- Is0
+ Is1 = case Blockify of
+ false -> Is0;
+ true -> blockify(Is0)
end,
- Is3 = beam_utils:anno_defs(Is2),
- Is4 = move_allocates(Is3),
- Is5 = beam_utils:live_opt(Is4),
- Is6 = opt_blocks(Is5),
- Is7 = beam_utils:delete_annos(Is6),
- Is = opt_allocs(Is7),
+ Is2 = embed_lines(Is1),
+ Is3 = local_cse(Is2),
+ Is4 = beam_utils:anno_defs(Is3),
+ Is5 = move_allocates(Is4),
+ Is6 = beam_utils:live_opt(Is5),
+ Is7 = opt_blocks(Is6),
+ Is8 = beam_utils:delete_annos(Is7),
+ Is = opt_allocs(Is8),
%% Done.
{function,Name,Arity,CLabel,Is}
@@ -109,7 +108,8 @@ collect({put_tuple,A,D}) -> {set,[D],[],{put_tuple,A}};
collect({put,S}) -> {set,[],[S],put};
collect({get_tuple_element,S,I,D}) -> {set,[D],[S],{get_tuple_element,I}};
collect({set_tuple_element,S,D,I}) -> {set,[],[S,D],{set_tuple_element,I}};
-collect({get_list,S,D1,D2}) -> {set,[D1,D2],[S],get_list};
+collect({get_hd,S,D}) -> {set,[D],[S],get_hd};
+collect({get_tl,S,D}) -> {set,[D],[S],get_tl};
collect(remove_message) -> {set,[],[],remove_message};
collect({put_map,F,Op,S,D,R,{list,Puts}}) ->
{set,[D],[S|Puts],{alloc,R,{put_map,Op,F}}};
@@ -137,6 +137,11 @@ embed_lines([{block,B2},{line,_}=Line,{block,B1}|T], Acc) ->
embed_lines([{block,B1},{line,_}=Line|T], Acc) ->
B = {block,[{set,[],[],Line}|B1]},
embed_lines([B|T], Acc);
+embed_lines([{block,B2},{block,B1}|T], Acc) ->
+ %% This can only happen when beam_block is run for
+ %% the second time.
+ B = {block,B1++B2},
+ embed_lines([B|T], Acc);
embed_lines([I|Is], Acc) ->
embed_lines(Is, [I|Acc]);
embed_lines([], Acc) -> Acc.
@@ -204,7 +209,7 @@ move_allocates([]) -> [].
move_allocates_1([{'%anno',_}|Is], Acc) ->
move_allocates_1(Is, Acc);
-move_allocates_1([I|Is], [{set,[],[],{alloc,Live0,Info}}|Acc]=Acc0) ->
+move_allocates_1([I|Is], [{set,[],[],{alloc,Live0,Info0}}|Acc]=Acc0) ->
case alloc_may_pass(I) of
false ->
move_allocates_1(Is, [I|Acc0]);
@@ -213,6 +218,7 @@ move_allocates_1([I|Is], [{set,[],[],{alloc,Live0,Info}}|Acc]=Acc0) ->
not_possible ->
move_allocates_1(Is, [I|Acc0]);
Live when is_integer(Live) ->
+ Info = safe_info(Info0),
A = {set,[],[],{alloc,Live,Info}},
move_allocates_1(Is, [A,I|Acc])
end
@@ -221,17 +227,25 @@ move_allocates_1([I|Is], Acc) ->
move_allocates_1(Is, [I|Acc]);
move_allocates_1([], Acc) -> Acc.
+alloc_may_pass({set,_,[{fr,_}],fmove}) -> false;
alloc_may_pass({set,_,_,{alloc,_,_}}) -> false;
alloc_may_pass({set,_,_,{set_tuple_element,_}}) -> false;
alloc_may_pass({set,_,_,put_list}) -> false;
alloc_may_pass({set,_,_,put}) -> false;
alloc_may_pass({set,_,_,_}) -> true.
+safe_info({nozero,Stack,Heap,_}) ->
+ %% nozero is not safe if the allocation instruction is moved
+ %% upwards past an instruction that may throw an exception
+ %% (such as element/2).
+ {zero,Stack,Heap,[]};
+safe_info(Info) -> Info.
+
%% opt([Instruction]) -> [Instruction]
%% Optimize the instruction stream inside a basic block.
opt([{set,[X],[X],move}|Is]) -> opt(Is);
-opt([{set,[X],_,move},{set,[X],_,move}=I|Is]) ->
+opt([{set,[Dst],_,move},{set,[Dst],[Src],move}=I|Is]) when Dst =/= Src ->
opt([I|Is]);
opt([{set,[{x,0}],[S1],move}=I1,{set,[D2],[{x,0}],move}|Is]) ->
opt([I1,{set,[D2],[S1],move}|Is]);
@@ -250,6 +264,16 @@ opt([{set,[D1],[{integer,Idx1},Reg],{bif,element,{f,L}}}=I1,
{set,[D2],[{integer,Idx2},Reg],{bif,element,{f,L}}}=I2|Is])
when Idx1 < Idx2, D1 =/= D2, D1 =/= Reg, D2 =/= Reg ->
opt([I2,I1|Is]);
+opt([{set,Hd0,Cons,get_hd}=GetHd,
+ {set,Tl0,Cons,get_tl}=GetTl|Is0]) ->
+ case {opt_moves(Hd0, [GetTl|Is0]),opt_moves(Tl0, [GetHd|Is0])} of
+ {{Hd0,Is},{Tl0,_}} ->
+ [GetHd|opt(Is)];
+ {{Hd,Is},{Tl0,_}} ->
+ [{set,Hd,Cons,get_hd}|opt(Is)];
+ {{_,_},{Tl,Is}} ->
+ [{set,Tl,Cons,get_tl}|opt(Is)]
+ end;
opt([{set,Ds0,Ss,Op}|Is0]) ->
{Ds,Is} = opt_moves(Ds0, Is0),
[{set,Ds,Ss,Op}|opt(Is)];
@@ -265,17 +289,6 @@ opt_moves([D0]=Ds, Is0) ->
case opt_move(D0, Is0) of
not_possible -> {Ds,Is0};
{D1,Is} -> {[D1],Is}
- end;
-opt_moves([X0,Y0], Is0) ->
- {X,Is2} = case opt_move(X0, Is0) of
- not_possible -> {X0,Is0};
- {Y0,_} -> {X0,Is0};
- {_X1,_Is1} = XIs1 -> XIs1
- end,
- case opt_move(Y0, Is2) of
- not_possible -> {[X,Y0],Is2};
- {X,_} -> {[X,Y0],Is2};
- {Y,Is} -> {[X,Y],Is}
end.
%% opt_move(Dest, [Instruction]) -> {UpdatedDest,[Instruction]} | not_possible
@@ -289,7 +302,7 @@ opt_move(Dest, Is) ->
opt_move_1(R, [{set,[D],[R],move}|Is0], Acc) ->
%% Provided that the source register is killed by instructions
%% that follow, the optimization is safe.
- case eliminate_use_of_from_reg(Is0, R, D, []) of
+ case eliminate_use_of_from_reg(Is0, R, D) of
{yes,Is} -> opt_move_rev(D, Acc, Is);
no -> not_possible
end;
@@ -347,13 +360,21 @@ opt_tuple_element_1([{set,_,_,{alloc,_,_}}|_], _, _, _) ->
opt_tuple_element_1([{set,_,_,{try_catch,_,_}}|_], _, _, _) ->
no;
opt_tuple_element_1([{set,[D],[S],move}|Is0], I0, {_,S}, Acc) ->
- case eliminate_use_of_from_reg(Is0, S, D, []) of
+ case eliminate_use_of_from_reg(Is0, S, D) of
no ->
no;
- {yes,Is} ->
+ {yes,Is1} ->
{set,[S],Ss,Op} = I0,
I = {set,[D],Ss,Op},
- {yes,reverse(Acc, [I|Is])}
+ case opt_move_rev(S, Acc, [I|Is1]) of
+ not_possible ->
+ %% Not safe because the move of the
+ %% get_tuple_element instruction would cause the
+ %% result of a previous instruction to be ignored.
+ no;
+ {_,Is} ->
+ {yes,Is}
+ end
end;
opt_tuple_element_1([{set,Ds,Ss,_}=I|Is], MovedI, {S,D}=Regs, Acc) ->
case member(S, Ds) orelse member(D, Ss) of
@@ -389,6 +410,14 @@ is_killed_or_used(R, {set,Ss,Ds,_}) ->
%% that FromRegister is still used and that the optimization is not
%% possible.
+eliminate_use_of_from_reg(Is, From, To) ->
+ try
+ eliminate_use_of_from_reg(Is, From, To, [])
+ catch
+ throw:not_possible ->
+ no
+ end.
+
eliminate_use_of_from_reg([{set,_,_,{alloc,Live,_}}|_]=Is0, {x,X}, _, Acc) ->
if
X < Live ->
@@ -397,21 +426,32 @@ eliminate_use_of_from_reg([{set,_,_,{alloc,Live,_}}|_]=Is0, {x,X}, _, Acc) ->
{yes,reverse(Acc, Is0)}
end;
eliminate_use_of_from_reg([{set,Ds,Ss0,Op}=I0|Is], From, To, Acc) ->
+ ensure_safe_tuple(I0, To),
I = case member(From, Ss0) of
- true ->
- Ss = [case S of
- From -> To;
- _ -> S
- end || S <- Ss0],
- {set,Ds,Ss,Op};
- false ->
- I0
- end,
+ true ->
+ Ss = [case S of
+ From -> To;
+ _ -> S
+ end || S <- Ss0],
+ {set,Ds,Ss,Op};
+ false ->
+ I0
+ end,
case member(From, Ds) of
- true ->
- {yes,reverse(Acc, [I|Is])};
- false ->
- eliminate_use_of_from_reg(Is, From, To, [I|Acc])
+ true ->
+ {yes,reverse(Acc, [I|Is])};
+ false ->
+ case member(To, Ds) of
+ true ->
+ case beam_utils:is_killed_block(From, Is) of
+ true ->
+ {yes,reverse(Acc, [I|Is])};
+ false ->
+ no
+ end;
+ false ->
+ eliminate_use_of_from_reg(Is, From, To, [I|Acc])
+ end
end;
eliminate_use_of_from_reg([I]=Is, From, _To, Acc) ->
case beam_utils:is_killed_block(From, [I]) of
@@ -421,6 +461,10 @@ eliminate_use_of_from_reg([I]=Is, From, _To, Acc) ->
no
end.
+ensure_safe_tuple({set,[To],[],{put_tuple,_}}, To) ->
+ throw(not_possible);
+ensure_safe_tuple(_, _) -> ok.
+
%% opt_allocs(Instructions) -> Instructions. Optimize allocate
%% instructions inside blocks. If safe, replace an allocate_zero
%% instruction with the slightly cheaper allocate instruction.
@@ -541,3 +585,109 @@ defined_regs([{set,Ds,_,{alloc,Live,_}}|_], Regs) ->
x_live(Ds, Regs bor ((1 bsl Live) - 1));
defined_regs([{set,Ds,_,_}|Is], Regs) ->
defined_regs(Is, x_live(Ds, Regs)).
+
+%%%
+%%% Do local common sub expression elimination (CSE) in each block.
+%%%
+
+local_cse([{block,Bl0}|Is]) ->
+ Bl = cse_block(Bl0, orddict:new(), []),
+ [{block,Bl}|local_cse(Is)];
+local_cse([I|Is]) ->
+ [I|local_cse(Is)];
+local_cse([]) -> [].
+
+cse_block([I|Is], Es0, Acc0) ->
+ Es1 = cse_clear(I, Es0),
+ case cse_expr(I) of
+ none ->
+ %% Instruction is not suitable for CSE.
+ cse_block(Is, Es1, [I|Acc0]);
+ {ok,D,Expr} ->
+ %% Suitable instruction. First update the dictionary of
+ %% suitable expressions for the next iteration.
+ Es = cse_add(D, Expr, Es1),
+
+ %% Search for a previous identical expression.
+ case cse_find(Expr, Es0) of
+ error ->
+ %% Nothing found
+ cse_block(Is, Es, [I|Acc0]);
+ Src ->
+ %% Use the previously calculated result.
+ %% Also eliminate any line instruction.
+ Move = {set,[D],[Src],move},
+ case Acc0 of
+ [{set,_,_,{line,_}}|Acc] ->
+ cse_block(Is, Es, [Move|Acc]);
+ [_|_] ->
+ cse_block(Is, Es, [Move|Acc0])
+ end
+ end
+ end;
+cse_block([], _, Acc) ->
+ reverse(Acc).
+
+%% cse_find(Expr, Expressions) -> error | Register.
+%% Find a previously evaluated expression whose result can be reused,
+%% or return 'error' if no such expression is found.
+
+cse_find(Expr, Es) ->
+ case orddict:find(Expr, Es) of
+ {ok,{Src,_}} -> Src;
+ error -> error
+ end.
+
+cse_expr({set,[D],Ss,{bif,N,_}}) ->
+ case D of
+ {fr,_} ->
+ %% There are too many things that can go wrong.
+ none;
+ _ ->
+ {ok,D,{{bif,N},Ss}}
+ end;
+cse_expr({set,[D],Ss,{alloc,_,{gc_bif,N,_}}}) ->
+ {ok,D,{{gc_bif,N},Ss}};
+cse_expr({set,[D],Ss,put_list}) ->
+ {ok,D,{put_list,Ss}};
+cse_expr(_) -> none.
+
+%% cse_clear(Instr, Expressions0) -> Expressions.
+%% Remove all previous expressions that will become
+%% invalid when this instruction is executed. Basically,
+%% an expression is no longer safe to reuse when the
+%% register it has been stored to has been modified, killed,
+%% or if any of the source operands have changed.
+
+cse_clear({set,Ds,_,{alloc,Live,_}}, Es) ->
+ cse_clear_1(Es, Live, Ds);
+cse_clear({set,Ds,_,_}, Es) ->
+ cse_clear_1(Es, all, Ds).
+
+cse_clear_1(Es, Live, Ds0) ->
+ Ds = ordsets:from_list(Ds0),
+ [E || E <- Es, cse_is_safe(E, Live, Ds)].
+
+cse_is_safe({_,{Dst,Interfering}}, Live, Ds) ->
+ ordsets:is_disjoint(Interfering, Ds) andalso
+ case Dst of
+ {x,X} ->
+ X < Live;
+ _ ->
+ true
+ end.
+
+%% cse_add(Dest, Expr, Expressions0) -> Expressions.
+%% Provided that it is safe, add a new expression to the dictionary
+%% of already evaluated expressions.
+
+cse_add(D, {_,Ss}=Expr, Es) ->
+ case member(D, Ss) of
+ false ->
+ Interfering = ordsets:from_list([D|Ss]),
+ orddict:store(Expr, {D,Interfering}, Es);
+ true ->
+ %% Unsafe because the instruction overwrites one of
+ %% source operands.
+ Es
+ end.
diff --git a/lib/compiler/src/beam_clean.erl b/lib/compiler/src/beam_clean.erl
index e094c2c320..7ddf9fa2e2 100644
--- a/lib/compiler/src/beam_clean.erl
+++ b/lib/compiler/src/beam_clean.erl
@@ -24,7 +24,7 @@
-export([module/2]).
-export([bs_clean_saves/1]).
-export([clean_labels/1]).
--import(lists, [foldl/3,reverse/1,filter/2]).
+-import(lists, [foldl/3,reverse/1]).
-spec module(beam_utils:module_code(), [compile:option()]) ->
{'ok',beam_utils:module_code()}.
@@ -303,8 +303,21 @@ maybe_remove_lines(Fs, Opts) ->
end.
remove_lines([{function,N,A,Lbl,Is0}|T]) ->
- Is = filter(fun({line,_}) -> false;
- (_) -> true
- end, Is0),
+ Is = remove_lines_fun(Is0),
[{function,N,A,Lbl,Is}|remove_lines(T)];
remove_lines([]) -> [].
+
+remove_lines_fun([{line,_}|Is]) ->
+ remove_lines_fun(Is);
+remove_lines_fun([{block,Bl0}|Is]) ->
+ Bl = remove_lines_block(Bl0),
+ [{block,Bl}|remove_lines_fun(Is)];
+remove_lines_fun([I|Is]) ->
+ [I|remove_lines_fun(Is)];
+remove_lines_fun([]) -> [].
+
+remove_lines_block([{set,_,_,{line,_}}|Is]) ->
+ remove_lines_block(Is);
+remove_lines_block([I|Is]) ->
+ [I|remove_lines_block(Is)];
+remove_lines_block([]) -> [].
diff --git a/lib/compiler/src/beam_dead.erl b/lib/compiler/src/beam_dead.erl
index da944f3ce6..dbbaae05eb 100644
--- a/lib/compiler/src/beam_dead.erl
+++ b/lib/compiler/src/beam_dead.erl
@@ -294,24 +294,25 @@ backward([{jump,{f,To}}=J|[{gc_bif,_,{f,To},_,_,_Dst}|Is]], D, Acc) ->
%% register is initialized, and it is therefore no need to test
%% for liveness of the destination register at label To.
backward([J|Is], D, Acc);
-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) ->
+backward([{test,bs_start_match2,F,Live,[Src,_]=Args,Ctxt}|Is], D, Acc0) ->
{f,To0} = F,
- case beam_utils:is_killed(Ctxt, Acc0, D) of
- true ->
- 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])
+ case test_bs_literal(F, Ctxt, D, Acc0) of
+ {none,Acc} ->
+ %% Ctxt killed immediately after bs_start_match2.
+ To = shortcut_bs_context_to_binary(To0, Src, D),
+ I = {test,is_bitstr,{f,To},[Src]},
+ backward(Is, D, [I|Acc]);
+ {Literal,Acc} ->
+ %% Ctxt killed after matching a literal.
+ To = shortcut_bs_context_to_binary(To0, Src, D),
+ Eq = {test,is_eq_exact,{f,To},[Src,{literal,Literal}]},
+ backward(Is, D, [Eq|Acc]);
+ not_killed ->
+ %% Ctxt not killed. Not much to do.
+ To = shortcut_bs_start_match(To0, Src, D),
+ I = {test,bs_start_match2,{f,To},Live,Args,Ctxt},
+ backward(Is, D, [I|Acc0])
end;
-backward([{test,bs_start_match2,{f,To0},Live,[Src|_]=Info,Dst}|Is], D, Acc) ->
- To = shortcut_bs_start_match(To0, Src, D),
- I = {test,bs_start_match2,{f,To},Live,Info,Dst},
- backward(Is, D, [I|Acc]);
backward([{test,Op,{f,To0},Ops0}|Is], D, Acc) ->
To1 = shortcut_bs_test(To0, Is, D),
To2 = shortcut_label(To1, D),
@@ -511,6 +512,22 @@ remove_from_list(Lit, [Val,{f,_}=Fail|T]) ->
[Val,Fail|remove_from_list(Lit, T)];
remove_from_list(_, []) -> [].
+
+test_bs_literal(F, Ctxt, D,
+ [{test,bs_match_string,F,[Ctxt,Bs]},
+ {test,bs_test_tail2,F,[Ctxt,0]}|Acc]) ->
+ test_bs_literal_1(Ctxt, Acc, D, Bs);
+test_bs_literal(F, Ctxt, D, [{test,bs_test_tail2,F,[Ctxt,0]}|Acc]) ->
+ test_bs_literal_1(Ctxt, Acc, D, <<>>);
+test_bs_literal(_, Ctxt, D, Acc) ->
+ test_bs_literal_1(Ctxt, Acc, D, none).
+
+test_bs_literal_1(Ctxt, Is, D, Literal) ->
+ case beam_utils:is_killed(Ctxt, Is, D) of
+ true -> {Literal,Is};
+ false -> not_killed
+ end.
+
%% shortcut_bs_test(TargetLabel, ReversedInstructions, D) -> TargetLabel'
%% Try to shortcut the failure label for bit syntax matching.
diff --git a/lib/compiler/src/beam_disasm.erl b/lib/compiler/src/beam_disasm.erl
index 50b76d7f29..a68c4b5367 100644
--- a/lib/compiler/src/beam_disasm.erl
+++ b/lib/compiler/src/beam_disasm.erl
@@ -1090,6 +1090,10 @@ resolve_inst({build_stacktrace,[]},_,_,_) ->
build_stacktrace;
resolve_inst({raw_raise,[]},_,_,_) ->
raw_raise;
+resolve_inst({get_hd,[Src,Dst]},_,_,_) ->
+ {get_hd,Src,Dst};
+resolve_inst({get_tl,[Src,Dst]},_,_,_) ->
+ {get_tl,Src,Dst};
%%
%% Catches instructions that are not yet handled.
diff --git a/lib/compiler/src/beam_flatten.erl b/lib/compiler/src/beam_flatten.erl
index a4d45a4ca6..c60211f516 100644
--- a/lib/compiler/src/beam_flatten.erl
+++ b/lib/compiler/src/beam_flatten.erl
@@ -50,6 +50,9 @@ norm_block([{set,[],[],{alloc,R,Alloc}}|Is], Acc0) ->
Acc ->
norm_block(Is, Acc)
end;
+norm_block([{set,[D1],[S],get_hd},{set,[D2],[S],get_tl}|Is], Acc) ->
+ I = {get_list,S,D1,D2},
+ norm_block(Is, [I|Acc]);
norm_block([I|Is], Acc) -> norm_block(Is, [norm(I)|Acc]);
norm_block([], Acc) -> Acc.
@@ -64,12 +67,14 @@ norm({set,[D],[],{put_tuple,A}}) -> {put_tuple,A,D};
norm({set,[],[S],put}) -> {put,S};
norm({set,[D],[S],{get_tuple_element,I}}) -> {get_tuple_element,S,I,D};
norm({set,[],[S,D],{set_tuple_element,I}}) -> {set_tuple_element,S,D,I};
-norm({set,[D1,D2],[S],get_list}) -> {get_list,S,D1,D2};
+norm({set,[D],[S],get_hd}) -> {get_hd,S,D};
+norm({set,[D],[S],get_tl}) -> {get_tl,S,D};
norm({set,[D],[S|Puts],{alloc,R,{put_map,Op,F}}}) ->
{put_map,F,Op,S,D,R,{list,Puts}};
norm({set,[],[],remove_message}) -> remove_message;
norm({set,[],[],fclearerror}) -> fclearerror;
-norm({set,[],[],fcheckerror}) -> {fcheckerror,{f,0}}.
+norm({set,[],[],fcheckerror}) -> {fcheckerror,{f,0}};
+norm({set,[],[],{line,_}=Line}) -> Line.
norm_allocate({_Zero,nostack,Nh,[]}, Regs) ->
[{test_heap,Nh,Regs}];
diff --git a/lib/compiler/src/beam_split.erl b/lib/compiler/src/beam_split.erl
index d041f18806..52dd89b5bb 100644
--- a/lib/compiler/src/beam_split.erl
+++ b/lib/compiler/src/beam_split.erl
@@ -50,8 +50,9 @@ split_block([{set,[R],[_,_,_]=As,{bif,is_record,{f,Lbl}}}|Is], Bl, Acc) ->
split_block(Is, [], [{bif,is_record,{f,Lbl},As,R}|make_block(Bl, Acc)]);
split_block([{set,[R],As,{bif,N,{f,Lbl}=Fail}}|Is], Bl, Acc) when Lbl =/= 0 ->
split_block(Is, [], [{bif,N,Fail,As,R}|make_block(Bl, Acc)]);
-split_block([{set,[R],As,{bif,raise,{f,_}=Fail}}|Is], Bl, Acc) ->
- split_block(Is, [], [{bif,raise,Fail,As,R}|make_block(Bl, Acc)]);
+split_block([{set,[],[],{line,_}=Line},
+ {set,[R],As,{bif,raise,{f,_}=Fail}}|Is], Bl, Acc) ->
+ split_block(Is, [], [{bif,raise,Fail,As,R},Line|make_block(Bl, Acc)]);
split_block([{set,[R],As,{alloc,Live,{gc_bif,N,{f,Lbl}=Fail}}}|Is], Bl, Acc)
when Lbl =/= 0 ->
split_block(Is, [], [{gc_bif,N,Fail,Live,As,R}|make_block(Bl, Acc)]);
@@ -61,8 +62,6 @@ split_block([{set,[D],[S|Puts],{alloc,R,{put_map,Op,{f,Lbl}=Fail}}}|Is],
make_block(Bl, Acc)]);
split_block([{set,[R],[],{try_catch,Op,L}}|Is], Bl, Acc) ->
split_block(Is, [], [{Op,R,L}|make_block(Bl, Acc)]);
-split_block([{set,[],[],{line,_}=Line}|Is], Bl, Acc) ->
- split_block(Is, [], [Line|make_block(Bl, Acc)]);
split_block([I|Is], Bl, Acc) ->
split_block(Is, [I|Bl], Acc);
split_block([], Bl, Acc) -> make_block(Bl, Acc).
diff --git a/lib/compiler/src/beam_type.erl b/lib/compiler/src/beam_type.erl
index 3b6bf49961..28f36db399 100644
--- a/lib/compiler/src/beam_type.erl
+++ b/lib/compiler/src/beam_type.erl
@@ -17,14 +17,15 @@
%%
%% %CopyrightEnd%
%%
-%% Purpose : Type-based optimisations.
+%% Purpose: Type-based optimisations. See the comment for verified_type/1
+%% the very end of this file for a description of the types in the
+%% type database.
-module(beam_type).
-export([module/2]).
--import(lists, [filter/2,foldl/3,keyfind/3,member/2,
- reverse/1,reverse/2,sort/1]).
+-import(lists, [foldl/3,member/2,reverse/1,reverse/2,sort/1]).
-define(UNICODE_INT, {integer,{0,16#10FFFF}}).
@@ -80,96 +81,81 @@ simplify(Is0, TypeDb0) ->
%% Basic simplification, mostly tuples, no floating point optimizations.
simplify_basic(Is, Ts) ->
- simplify_basic_1(Is, Ts, []).
-
-simplify_basic_1([{set,[D],[{integer,Index},Reg],{bif,element,_}}=I0|Is], Ts0, Acc) ->
- I = case max_tuple_size(Reg, Ts0) of
- Sz when 0 < Index, Index =< Sz ->
- {set,[D],[Reg],{get_tuple_element,Index-1}};
- _Other -> I0
- end,
- Ts = update(I, Ts0),
- simplify_basic_1(Is, Ts, [I|Acc]);
-simplify_basic_1([{set,[D],[TupleReg],{get_tuple_element,0}}=I|Is0], Ts0, Acc) ->
- case tdb_find(TupleReg, Ts0) of
- {tuple,_,_,[Contents]} ->
- simplify_basic_1([{set,[D],[Contents],move}|Is0], Ts0, Acc);
- _ ->
- Ts = update(I, Ts0),
- simplify_basic_1(Is0, Ts, [I|Acc])
+ simplify_basic(Is, Ts, []).
+
+simplify_basic([I0|Is], Ts0, Acc) ->
+ case simplify_instr(I0, Ts0) of
+ [] ->
+ simplify_basic(Is, Ts0, Acc);
+ [I] ->
+ Ts = update(I, Ts0),
+ simplify_basic(Is, Ts, [I|Acc])
+ end;
+simplify_basic([], Ts, Acc) ->
+ {reverse(Acc),Ts}.
+
+%% simplify_instr(Instruction, Ts) -> [Instruction].
+
+%% Simplify a simple instruction using type information. Return an
+%% empty list if the instruction should be removed, or a list with
+%% the original or modified instruction.
+
+simplify_instr({set,[D],[{integer,Index},Reg],{bif,element,_}}=I, Ts) ->
+ case max_tuple_size(Reg, Ts) of
+ Sz when 0 < Index, Index =< Sz ->
+ [{set,[D],[Reg],{get_tuple_element,Index-1}}];
+ _ -> [I]
end;
-simplify_basic_1([{set,_,_,{try_catch,_,_}}=I|Is], _Ts, Acc) ->
- simplify_basic_1(Is, tdb_new(), [I|Acc]);
-simplify_basic_1([{test,is_atom,_,[R]}=I|Is], Ts, Acc) ->
+simplify_instr({test,Test,Fail,[R]}=I, Ts) ->
case tdb_find(R, Ts) of
- boolean -> simplify_basic_1(Is, Ts, Acc);
- _ -> simplify_basic_1(Is, Ts, [I|Acc])
+ any ->
+ [I];
+ Type ->
+ case will_succeed(Test, Type) of
+ yes -> [];
+ no -> [{jump,Fail}];
+ maybe -> [I]
+ end
+ end;
+simplify_instr({set,[D],[TupleReg],{get_tuple_element,0}}=I, Ts) ->
+ case tdb_find(TupleReg, Ts) of
+ {tuple,_,_,[Contents]} ->
+ [{set,[D],[Contents],move}];
+ _ ->
+ [I]
end;
-simplify_basic_1([{test,is_integer,_,[R]}=I|Is], Ts, Acc) ->
+simplify_instr({test,test_arity,_,[R,Arity]}=I, Ts) ->
case tdb_find(R, Ts) of
- integer -> simplify_basic_1(Is, Ts, Acc);
- {integer,_} -> simplify_basic_1(Is, Ts, Acc);
- _ -> simplify_basic_1(Is, Ts, [I|Acc])
+ {tuple,exact_size,Arity,_} -> [];
+ _ -> [I]
end;
-simplify_basic_1([{test,is_tuple,_,[R]}=I|Is], Ts, Acc) ->
+simplify_instr({test,is_eq_exact,Fail,[R,{atom,A}=Atom]}=I, Ts) ->
case tdb_find(R, Ts) of
- {tuple,_,_,_} -> simplify_basic_1(Is, Ts, Acc);
- _ -> simplify_basic_1(Is, Ts, [I|Acc])
+ {atom,_}=Atom -> [];
+ boolean when is_boolean(A) -> [I];
+ any -> [I];
+ _ -> [{jump,Fail}]
end;
-simplify_basic_1([{test,test_arity,_,[R,Arity]}=I|Is], Ts0, Acc) ->
- case tdb_find(R, Ts0) of
- {tuple,exact_size,Arity,_} ->
- simplify_basic_1(Is, Ts0, Acc);
- _Other ->
- Ts = update(I, Ts0),
- simplify_basic_1(Is, Ts, [I|Acc])
+simplify_instr({test,is_record,_,[R,{atom,_}=Tag,{integer,Arity}]}=I, Ts) ->
+ case tdb_find(R, Ts) of
+ {tuple,exact_size,Arity,[Tag]} -> [];
+ _ -> [I]
end;
-simplify_basic_1([{test,is_map,_,[R]}=I|Is], Ts0, Acc) ->
- case tdb_find(R, Ts0) of
- map -> simplify_basic_1(Is, Ts0, Acc);
- _Other ->
- Ts = update(I, Ts0),
- simplify_basic_1(Is, Ts, [I|Acc])
+simplify_instr({select,select_val,Reg,_,_}=I, Ts) ->
+ [case tdb_find(Reg, Ts) of
+ {integer,Range} ->
+ simplify_select_val_int(I, Range);
+ boolean ->
+ simplify_select_val_bool(I);
+ _ ->
+ I
+ end];
+simplify_instr({test,bs_test_unit,_,[Src,Unit]}=I, Ts) ->
+ case tdb_find(Src, Ts) of
+ {binary,U} when U rem Unit =:= 0 -> [];
+ _ -> [I]
end;
-simplify_basic_1([{test,is_nonempty_list,_,[R]}=I|Is], Ts0, Acc) ->
- case tdb_find(R, Ts0) of
- nonempty_list -> simplify_basic_1(Is, Ts0, Acc);
- _Other ->
- Ts = update(I, Ts0),
- simplify_basic_1(Is, Ts, [I|Acc])
- end;
-simplify_basic_1([{test,is_eq_exact,Fail,[R,{atom,_}=Atom]}=I|Is0], Ts0, Acc0) ->
- Acc = case tdb_find(R, Ts0) of
- {atom,_}=Atom -> Acc0;
- {atom,_} -> [{jump,Fail}|Acc0];
- _ -> [I|Acc0]
- end,
- Ts = update(I, Ts0),
- simplify_basic_1(Is0, Ts, Acc);
-simplify_basic_1([{test,is_record,_,[R,{atom,_}=Tag,{integer,Arity}]}=I|Is], Ts0, Acc) ->
- case tdb_find(R, Ts0) of
- {tuple,exact_size,Arity,[Tag]} ->
- simplify_basic_1(Is, Ts0, Acc);
- _Other ->
- Ts = update(I, Ts0),
- simplify_basic_1(Is, Ts, [I|Acc])
- end;
-simplify_basic_1([{select,select_val,Reg,_,_}=I0|Is], Ts, Acc) ->
- I = case tdb_find(Reg, Ts) of
- {integer,Range} ->
- simplify_select_val_int(I0, Range);
- boolean ->
- simplify_select_val_bool(I0);
- _ ->
- I0
- end,
- simplify_basic_1(Is, tdb_new(), [I|Acc]);
-simplify_basic_1([I|Is], Ts0, Acc) ->
- Ts = update(I, Ts0),
- simplify_basic_1(Is, Ts, [I|Acc]);
-simplify_basic_1([], Ts, Acc) ->
- Is = reverse(Acc),
- {Is,Ts}.
+simplify_instr(I, _) -> [I].
simplify_select_val_int({select,select_val,R,_,L0}=I, {Min,Max}) ->
Vs = sort([V || {integer,V} <- L0]),
@@ -197,6 +183,53 @@ eq_ranges([H], H, H) -> true;
eq_ranges([H|T], H, Max) -> eq_ranges(T, H+1, Max);
eq_ranges(_, _, _) -> false.
+%% will_succeed(TestOperation, Type) -> yes|no|maybe.
+%% Test whether TestOperation applied to an argument of type Type
+%% will succeed. Return yes, no, or maybe.
+%%
+%% Type is a type as described in the comment for verified_type/1 at
+%% the very end of this file, but it will *never* be 'any'.
+
+will_succeed(is_atom, Type) ->
+ case Type of
+ {atom,_} -> yes;
+ boolean -> yes;
+ _ -> no
+ end;
+will_succeed(is_binary, Type) ->
+ case Type of
+ {binary,U} when U rem 8 =:= 0 -> yes;
+ {binary,_} -> maybe;
+ _ -> no
+ end;
+will_succeed(is_bitstr, Type) ->
+ case Type of
+ {binary,_} -> yes;
+ _ -> no
+ end;
+will_succeed(is_integer, Type) ->
+ case Type of
+ integer -> yes;
+ {integer,_} -> yes;
+ _ -> no
+ end;
+will_succeed(is_map, Type) ->
+ case Type of
+ map -> yes;
+ _ -> no
+ end;
+will_succeed(is_nonempty_list, Type) ->
+ case Type of
+ nonempty_list -> yes;
+ _ -> no
+ end;
+will_succeed(is_tuple, Type) ->
+ case Type of
+ {tuple,_,_,_} -> yes;
+ _ -> no
+ end;
+will_succeed(_, _) -> maybe.
+
%% simplify_float([Instruction], TypeDatabase) ->
%% {[Instruction],TypeDatabase'} | not_possible
%% Simplify floating point operations in blocks.
@@ -226,7 +259,7 @@ simplify_float_1([{set,[D0],[A0],{alloc,_,{gc_bif,'-',{f,0}}}}=I|Is]=Is0,
{D,Rs} = find_dest(D0, Rs1),
Areg = fetch_reg(A, Rs),
Acc = [{set,[D],[Areg],{bif,fnegate,{f,0}}}|clearerror(Acc1)],
- Ts = tdb_update([{D0,float}], Ts0),
+ Ts = tdb_store(D0, float, Ts0),
simplify_float_1(Is, Ts, Rs, Acc);
_Other ->
Ts = update(I, Ts0),
@@ -249,7 +282,7 @@ simplify_float_1([{set,[D0],[A0,B0],{alloc,_,{gc_bif,Op0,{f,0}}}}=I|Is]=Is0,
Areg = fetch_reg(A, Rs),
Breg = fetch_reg(B, Rs),
Acc = [{set,[D],[Areg,Breg],{bif,Op,{f,0}}}|clearerror(Acc2)],
- Ts = tdb_update([{D0,float}], Ts0),
+ Ts = tdb_store(D0, float, Ts0),
simplify_float_1(Is, Ts, Rs, Acc)
end;
simplify_float_1([{set,_,_,{try_catch,_,_}}=I|Is]=Is0, _Ts, Rs0, Acc0) ->
@@ -422,100 +455,100 @@ update({'%anno',_}, Ts) ->
Ts;
update({set,[D],[S],move}, Ts) ->
tdb_copy(S, D, Ts);
-update({set,[D],[{integer,I},Reg],{bif,element,_}}, Ts0) ->
- tdb_update([{Reg,{tuple,min_size,I,[]}},{D,kill}], Ts0);
-update({set,[D],[_Index,Reg],{bif,element,_}}, Ts0) ->
- tdb_update([{Reg,{tuple,min_size,0,[]}},{D,kill}], Ts0);
-update({set,[D],Args,{bif,N,_}}, Ts0) ->
+update({set,[D],[Index,Reg],{bif,element,_}}, Ts0) ->
+ MinSize = case Index of
+ {integer,I} -> I;
+ _ -> 0
+ end,
+ Ts = tdb_meet(Reg, {tuple,min_size,MinSize,[]}, Ts0),
+ tdb_store(D, any, Ts);
+update({set,[D],Args,{bif,N,_}}, Ts) ->
Ar = length(Args),
BoolOp = erl_internal:new_type_test(N, Ar) orelse
erl_internal:comp_op(N, Ar) orelse
erl_internal:bool_op(N, Ar),
- case BoolOp of
- true ->
- tdb_update([{D,boolean}], Ts0);
- false ->
- tdb_update([{D,kill}], Ts0)
+ Type = case BoolOp of
+ true -> boolean;
+ false -> unary_op_type(N)
+ end,
+ tdb_store(D, Type, Ts);
+update({set,[D],[S],{get_tuple_element,0}}, Ts0) ->
+ if
+ D =:= S ->
+ tdb_store(D, any, Ts0);
+ true ->
+ Ts = tdb_store(D, {tuple_element,S,0}, Ts0),
+ tdb_store(S, {tuple,min_size,1,[]}, Ts)
end;
-update({set,[D],[S],{get_tuple_element,0}}, Ts) ->
- tdb_update([{D,{tuple_element,S,0}}], Ts);
update({set,[D],[S],{alloc,_,{gc_bif,float,{f,0}}}}, Ts0) ->
%% Make sure we reject non-numeric literal argument.
case possibly_numeric(S) of
- true -> tdb_update([{D,float}], Ts0);
- false -> Ts0
+ true -> tdb_store(D, float, Ts0);
+ false -> Ts0
end;
update({set,[D],[S1,S2],{alloc,_,{gc_bif,'band',{f,0}}}}, Ts) ->
- case keyfind(integer, 1, [S1,S2]) of
- {integer,N} ->
- update_band(N, D, Ts);
- false ->
- tdb_update([{D,integer}], Ts)
- end;
-update({set,[D],[S1,S2],{alloc,_,{gc_bif,'/',{f,0}}}}, Ts0) ->
+ Type = band_type(S1, S2, Ts),
+ tdb_store(D, Type, Ts);
+update({set,[D],[S1,S2],{alloc,_,{gc_bif,'/',{f,0}}}}, Ts) ->
%% Make sure we reject non-numeric literals.
case possibly_numeric(S1) andalso possibly_numeric(S2) of
- true -> tdb_update([{D,float}], Ts0);
- false -> Ts0
+ true -> tdb_store(D, float, Ts);
+ false -> Ts
end;
update({set,[D],[S1,S2],{alloc,_,{gc_bif,Op,{f,0}}}}, Ts0) ->
case op_type(Op) of
integer ->
- tdb_update([{D,integer}], Ts0);
- {float,_} ->
- case {tdb_find(S1, Ts0),tdb_find(S2, Ts0)} of
- {float,_} -> tdb_update([{D,float}], Ts0);
- {_,float} -> tdb_update([{D,float}], Ts0);
- {_,_} -> tdb_update([{D,kill}], Ts0)
- end;
- unknown ->
- tdb_update([{D,kill}], Ts0)
- end;
-update({set,[],_Src,_Op}, Ts0) -> Ts0;
-update({set,[D],_Src,_Op}, Ts0) ->
- tdb_update([{D,kill}], Ts0);
-update({set,[D1,D2],_Src,_Op}, Ts0) ->
- tdb_update([{D1,kill},{D2,kill}], Ts0);
+ tdb_store(D, integer, Ts0);
+ {float,_} ->
+ case {tdb_find(S1, Ts0),tdb_find(S2, Ts0)} of
+ {float,_} -> tdb_store(D, float, Ts0);
+ {_,float} -> tdb_store(D, float, Ts0);
+ {_,_} -> tdb_store(D, any, Ts0)
+ end;
+ Type ->
+ tdb_store(D, Type, Ts0)
+ end;
+update({set,[D],[_],{alloc,_,{gc_bif,Op,{f,0}}}}, Ts) ->
+ tdb_store(D, unary_op_type(Op), Ts);
+update({set,[],_Src,_Op}, Ts) ->
+ Ts;
+update({set,[D],_Src,_Op}, Ts) ->
+ tdb_store(D, any, Ts);
update({kill,D}, Ts) ->
- tdb_update([{D,kill}], Ts);
+ tdb_store(D, any, Ts);
%% Instructions outside of blocks.
-update({test,is_float,_Fail,[Src]}, Ts0) ->
- tdb_update([{Src,float}], Ts0);
-update({test,test_arity,_Fail,[Src,Arity]}, Ts0) ->
- tdb_update([{Src,{tuple,exact_size,Arity,[]}}], Ts0);
-update({test,is_map,_Fail,[Src]}, Ts0) ->
- tdb_update([{Src,map}], Ts0);
+update({test,test_arity,_Fail,[Src,Arity]}, Ts) ->
+ tdb_meet(Src, {tuple,exact_size,Arity,[]}, Ts);
update({get_map_elements,_,Src,{list,Elems0}}, Ts0) ->
+ Ts1 = tdb_meet(Src, map, Ts0),
{_Ss,Ds} = beam_utils:split_even(Elems0),
- Elems = [{Dst,kill} || Dst <- Ds],
- tdb_update([{Src,map}|Elems], Ts0);
-update({test,is_nonempty_list,_Fail,[Src]}, Ts0) ->
- tdb_update([{Src,nonempty_list}], Ts0);
-update({test,is_eq_exact,_,[Reg,{atom,_}=Atom]}, Ts) ->
- case tdb_find(Reg, Ts) of
- error ->
- Ts;
- {tuple_element,TupleReg,0} ->
- tdb_update([{TupleReg,{tuple,min_size,1,[Atom]}}], Ts);
- _ ->
- Ts
- end;
+ foldl(fun(Dst, A) -> tdb_store(Dst, any, A) end, Ts1, Ds);
+update({test,is_eq_exact,_,[Reg,{atom,_}=Atom]}, Ts0) ->
+ Ts = case tdb_find_source_tuple(Reg, Ts0) of
+ {source_tuple,TupleReg} ->
+ tdb_meet(TupleReg, {tuple,min_size,1,[Atom]}, Ts0);
+ none ->
+ Ts0
+ end,
+ tdb_meet(Reg, Atom, Ts);
update({test,is_record,_Fail,[Src,Tag,{integer,Arity}]}, Ts) ->
- tdb_update([{Src,{tuple,exact_size,Arity,[Tag]}}], Ts);
+ tdb_meet(Src, {tuple,exact_size,Arity,[Tag]}, Ts);
-%% Binary matching
+%% Binaries and binary matching.
update({test,bs_get_integer2,_,_,Args,Dst}, Ts) ->
- tdb_update([{Dst,get_bs_integer_type(Args)}], Ts);
+ tdb_store(Dst, get_bs_integer_type(Args), Ts);
update({test,bs_get_utf8,_,_,_,Dst}, Ts) ->
- tdb_update([{Dst,?UNICODE_INT}], Ts);
+ tdb_store(Dst, ?UNICODE_INT, Ts);
update({test,bs_get_utf16,_,_,_,Dst}, Ts) ->
- tdb_update([{Dst,?UNICODE_INT}], Ts);
+ tdb_store(Dst, ?UNICODE_INT, Ts);
update({test,bs_get_utf32,_,_,_,Dst}, Ts) ->
- tdb_update([{Dst,?UNICODE_INT}], Ts);
+ tdb_store(Dst, ?UNICODE_INT, Ts);
+update({bs_init,_,{bs_init2,_,_},_,_,Dst}, Ts) ->
+ tdb_store(Dst, {binary,8}, Ts);
update({bs_init,_,_,_,_,Dst}, Ts) ->
- tdb_update([{Dst,kill}], Ts);
+ tdb_store(Dst, {binary,1}, Ts);
update({bs_put,_,_,_}, Ts) ->
Ts;
update({bs_save2,_,_}, Ts) ->
@@ -523,14 +556,31 @@ update({bs_save2,_,_}, Ts) ->
update({bs_restore2,_,_}, Ts) ->
Ts;
update({bs_context_to_binary,Dst}, Ts) ->
- tdb_update([{Dst,kill}], Ts);
-update({test,bs_start_match2,_,_,_,Dst}, Ts) ->
- tdb_update([{Dst,kill}], Ts);
-update({test,bs_get_binary2,_,_,_,Dst}, Ts) ->
- tdb_update([{Dst,kill}], Ts);
+ tdb_store(Dst, {binary,1}, Ts);
+update({test,bs_start_match2,_,_,[Src,_],Dst}, Ts0) ->
+ Ts = tdb_meet(Src, {binary,1}, Ts0),
+ tdb_copy(Src, Dst, Ts);
+update({test,bs_get_binary2,_,_,[_,_,Unit,_],Dst}, Ts) ->
+ true = is_integer(Unit), %Assertion.
+ tdb_store(Dst, {binary,Unit}, Ts);
update({test,bs_get_float2,_,_,_,Dst}, Ts) ->
- tdb_update([{Dst,float}], Ts);
-
+ tdb_store(Dst, float, Ts);
+update({test,bs_test_unit,_,[Src,Unit]}, Ts) ->
+ tdb_meet(Src, {binary,Unit}, Ts);
+
+%% Other test instructions
+update({test,Test,_Fail,[Src]}, Ts) ->
+ Type = case Test of
+ is_binary -> {binary,8};
+ is_bitstr -> {binary,1};
+ is_boolean -> boolean;
+ is_float -> float;
+ is_integer -> integer;
+ is_map -> map;
+ is_nonempty_list -> nonempty_list;
+ _ -> any
+ end,
+ tdb_meet(Src, Type, Ts);
update({test,_Test,_Fail,_Other}, Ts) ->
Ts;
@@ -538,7 +588,7 @@ update({test,_Test,_Fail,_Other}, Ts) ->
update({call_ext,Ar,{extfunc,math,Math,Ar}}, Ts) ->
case is_math_bif(Math, Ar) of
- true -> tdb_update([{{x,0},float}], Ts);
+ true -> tdb_store({x,0}, float, Ts);
false -> tdb_kill_xregs(Ts)
end;
update({call_ext,3,{extfunc,erlang,setelement,3}}, Ts0) ->
@@ -555,7 +605,7 @@ update({call_ext,3,{extfunc,erlang,setelement,3}}, Ts0) ->
%% first element of the tuple.
{tuple,SzKind,Sz,[]}
end,
- tdb_update([{{x,0},T}], Ts);
+ tdb_store({x,0}, T, Ts);
_ ->
Ts
end;
@@ -566,24 +616,32 @@ update({call_fun, _}, Ts) -> tdb_kill_xregs(Ts);
update({apply, _}, Ts) -> tdb_kill_xregs(Ts);
update({line,_}, Ts) -> Ts;
+update({'%',_}, Ts) -> Ts;
%% The instruction is unknown. Kill all information.
update(_I, _Ts) -> tdb_new().
-update_band(N, Reg, Ts) ->
- Type = update_band_1(N, 0),
- tdb_update([{Reg,Type}], Ts).
+band_type({integer,Int}, Other, Ts) ->
+ band_type_1(Int, Other, Ts);
+band_type(Other, {integer,Int}, Ts) ->
+ band_type_1(Int, Other, Ts);
+band_type(_, _, _) -> integer.
-update_band_1(N, Bits) when Bits < 64 ->
+band_type_1(Int, OtherSrc, Ts) ->
+ Type = band_type_2(Int, 0),
+ OtherType = tdb_find(OtherSrc, Ts),
+ meet(Type, OtherType).
+
+band_type_2(N, Bits) when Bits < 64 ->
case 1 bsl Bits of
P when P =:= N + 1 ->
{integer,{0,N}};
P when P > N + 1 ->
integer;
_ ->
- update_band_1(N, Bits+1)
+ band_type_2(N, Bits+1)
end;
-update_band_1(_, _) ->
+band_type_2(_, _) ->
%% Negative or large positive number. Give up.
integer.
@@ -707,7 +765,15 @@ op_type('bxor') -> integer;
op_type('bsl') -> integer;
op_type('bsr') -> integer;
op_type('div') -> integer;
-op_type(_) -> unknown.
+op_type(_) -> any.
+
+unary_op_type(bit_size) -> integer;
+unary_op_type(byte_size) -> integer;
+unary_op_type(length) -> integer;
+unary_op_type(map_size) -> integer;
+unary_op_type(size) -> integer;
+unary_op_type(tuple_size) -> integer;
+unary_op_type(_) -> any.
flush(Rs, [{set,[_],[_,_,_],{bif,is_record,_}}|_]=Is0, Acc0) ->
Acc = flush_all(Rs, Is0, Acc0),
@@ -790,38 +856,39 @@ checkerror_1([], OrigIs) -> OrigIs.
checkerror_2(OrigIs) -> [{set,[],[],fcheckerror}|OrigIs].
-%%% Routines for maintaining a type database. The type database
+%%% Routines for maintaining a type database. The type database
%%% associates type information with registers.
%%%
-%%% {tuple,min_size,Size,First} means that the corresponding register contains
-%%% a tuple with *at least* Size elements (conversely, exact_size means that it
-%%% contains a tuple with *exactly* Size elements). An tuple with unknown size
-%%% is represented as {tuple,min_size,0,[]}. First is either [] (meaning that
-%%% the tuple's first element is unknown) or [FirstElement] (the contents of
-%%% the first element).
-%%%
-%%% 'float' means that the register contains a float.
-%%%
-%%% 'integer' or {integer,{Min,Max}} that the register contains an
-%%% integer.
+%%% See the comment for verified_type/1 at the end of module for
+%%% a description of the possible types.
%% tdb_new() -> EmptyDataBase
%% Creates a new, empty type database.
tdb_new() -> [].
-%% tdb_find(Register, Db) -> Information|error
+%% tdb_find(Register, Db) -> Type
%% Returns type information or the atom error if there is no type
%% information available for Register.
+%%
+%% See the comment for verified_type/1 at the end of module for
+%% a description of the possible types.
-tdb_find({x,_}=K, Ts) -> tdb_find_1(K, Ts);
-tdb_find({y,_}=K, Ts) -> tdb_find_1(K, Ts);
-tdb_find(_, _) -> error.
+tdb_find(Reg, Ts) ->
+ case tdb_find_raw(Reg, Ts) of
+ {tuple_element,_,_} -> any;
+ Type -> Type
+ end.
-tdb_find_1(K, Ts) ->
- case orddict:find(K, Ts) of
- {ok,Val} -> Val;
- error -> error
+%% tdb_find_source_tuple(Register, Ts) -> {source_tuple,Register} | 'none'.
+%% Find the tuple whose first element was fetched to the register Register.
+
+tdb_find_source_tuple(Reg, Ts) ->
+ case tdb_find_raw(Reg, Ts) of
+ {tuple_element,Src,0} ->
+ {source_tuple,Src};
+ _ ->
+ none
end.
%% tdb_copy(Source, Dest, Db) -> Db'
@@ -829,9 +896,9 @@ tdb_find_1(K, Ts) ->
%% as the Source.
tdb_copy({Tag,_}=S, D, Ts) when Tag =:= x; Tag =:= y ->
- case tdb_find(S, Ts) of
- error -> orddict:erase(D, Ts);
- Type -> orddict:store(D, Type, Ts)
+ case tdb_find_raw(S, Ts) of
+ any -> orddict:erase(D, Ts);
+ Type -> orddict:store(D, Type, Ts)
end;
tdb_copy(Literal, D, Ts) ->
Type = case Literal of
@@ -843,14 +910,89 @@ tdb_copy(Literal, D, Ts) ->
{literal,Tuple} when tuple_size(Tuple) >= 1 ->
Lit = tag_literal(element(1, Tuple)),
{tuple,exact_size,tuple_size(Tuple),[Lit]};
- _ -> term
+ _ -> any
end,
- if
- Type =:= term ->
- orddict:erase(D, Ts);
- true ->
- verify_type(Type),
- orddict:store(D, Type, Ts)
+ tdb_store(D, verified_type(Type), Ts).
+
+%% tdb_store(Register, Type, Ts0) -> Ts.
+%% Store a new type for register Register. Return the update type
+%% database. Use this function when a new value is assigned to
+%% a register.
+%%
+%% See the comment for verified_type/1 at the end of module for
+%% a description of the possible types.
+
+tdb_store(Reg, any, Ts) ->
+ erase(Reg, Ts);
+tdb_store(Reg, Type, Ts) ->
+ store(Reg, verified_type(Type), Ts).
+
+store(Key, New, [{K,_}|_]=Dict) when Key < K ->
+ [{Key,New}|Dict];
+store(Key, New, [{K,Val}=E|Dict]) when Key > K ->
+ case Val of
+ {tuple_element,Key,_} -> store(Key, New, Dict);
+ _ -> [E|store(Key, New, Dict)]
+ end;
+store(Key, New, [{_K,Old}|Dict]) -> %Key == K
+ case Old of
+ {tuple,_,_,_} ->
+ [{Key,New}|erase_tuple_element(Key, Dict)];
+ _ ->
+ [{Key,New}|Dict]
+ end;
+store(Key, New, []) -> [{Key,New}].
+
+erase(Key, [{K,_}=E|Dict]) when Key < K ->
+ [E|Dict];
+erase(Key, [{K,Val}=E|Dict]) when Key > K ->
+ case Val of
+ {tuple_element,Key,_} -> erase(Key, Dict);
+ _ -> [E|erase(Key, Dict)]
+ end;
+erase(Key, [{_K,Val}|Dict]) -> %Key == K
+ case Val of
+ {tuple,_,_,_} -> erase_tuple_element(Key, Dict);
+ _ -> Dict
+ end;
+erase(_, []) -> [].
+
+erase_tuple_element(Key, [{_,{tuple_element,Key,_}}|Dict]) ->
+ erase_tuple_element(Key, Dict);
+erase_tuple_element(Key, [E|Dict]) ->
+ [E|erase_tuple_element(Key, Dict)];
+erase_tuple_element(_Key, []) -> [].
+
+%% tdb_meet(Register, Type, Ts0) -> Ts.
+%% Update information of a register that is used as the source for an
+%% instruction. The type Type will be combined using the meet operation
+%% with the previous type information for the register, resulting in
+%% narrower (more specific) type.
+%%
+%% For example, if the previous type is {tuple,min_size,2,[]} and the
+%% the new type is {tuple,exact_size,5,[]}, the meet of the types will
+%% be {tuple,exact_size,5,[]}.
+%%
+%% See the comment for verified_type/1 at the end of module for
+%% a description of the possible types.
+
+tdb_meet(Reg, NewType, Ts) ->
+ Update = fun(Type0) -> meet(Type0, NewType) end,
+ orddict:update(Reg, Update, NewType, Ts).
+
+%%%
+%%% Here follows internal helper functions for accessing and
+%%% updating the type database.
+%%%
+
+tdb_find_raw({x,_}=K, Ts) -> tdb_find_raw_1(K, Ts);
+tdb_find_raw({y,_}=K, Ts) -> tdb_find_raw_1(K, Ts);
+tdb_find_raw(_, _) -> any.
+
+tdb_find_raw_1(K, Ts) ->
+ case orddict:find(K, Ts) of
+ {ok,Val} -> Val;
+ error -> any
end.
tag_literal(A) when is_atom(A) -> {atom,A};
@@ -859,45 +1001,6 @@ tag_literal(I) when is_integer(I) -> {integer,I};
tag_literal([]) -> nil;
tag_literal(Lit) -> {literal,Lit}.
-%% tdb_update([UpdateOp], Db) -> NewDb
-%% UpdateOp = {Register,kill}|{Register,NewInfo}
-%% Updates a type database. If a 'kill' operation is given, the type
-%% information for that register will be removed from the database.
-%% A kill operation takes precedence over other operations for the same
-%% register (i.e. [{{x,0},kill},{{x,0},{tuple,min_size,5,[]}}] means that the
-%% the existing type information, if any, will be discarded, and the
-%% the '{tuple,min_size,5,[]}' information ignored.
-%%
-%% If NewInfo information is given and there exists information about
-%% the register, the old and new type information will be merged.
-%% For instance, {tuple,min_size,5,_} and {tuple,min_size,10,_} will be merged
-%% to produce {tuple,min_size,10,_}.
-
-tdb_update(Uis0, Ts0) ->
- Uis1 = filter(fun ({{x,_},_Op}) -> true;
- ({{y,_},_Op}) -> true;
- (_) -> false
- end, Uis0),
- tdb_update1(lists:sort(Uis1), Ts0).
-
-tdb_update1([{Key,kill}|Ops], [{K,_Old}|_]=Db) when Key < K ->
- tdb_update1(remove_key(Key, Ops), Db);
-tdb_update1([{Key,Type}=New|Ops], [{K,_Old}|_]=Db) when Key < K ->
- verify_type(Type),
- [New|tdb_update1(Ops, Db)];
-tdb_update1([{Key,kill}|Ops], [{Key,_}|Db]) ->
- tdb_update1(remove_key(Key, Ops), Db);
-tdb_update1([{Key,NewInfo}|Ops], [{Key,OldInfo}|Db]) ->
- [{Key,merge_type_info(NewInfo, OldInfo)}|tdb_update1(Ops, Db)];
-tdb_update1([{_,_}|_]=Ops, [Old|Db]) ->
- [Old|tdb_update1(Ops, Db)];
-tdb_update1([{Key,kill}|Ops], []) ->
- tdb_update1(remove_key(Key, Ops), []);
-tdb_update1([{_,Type}=New|Ops], []) ->
- verify_type(Type),
- [New|tdb_update1(Ops, [])];
-tdb_update1([], Db) -> Db.
-
%% tdb_kill_xregs(Db) -> NewDb
%% Kill all information about x registers. Also kill all tuple_element
%% dependencies from y registers to x registers.
@@ -906,41 +1009,106 @@ tdb_kill_xregs([{{x,_},_Type}|Db]) -> tdb_kill_xregs(Db);
tdb_kill_xregs([{{y,_},{tuple_element,{x,_},_}}|Db]) -> tdb_kill_xregs(Db);
tdb_kill_xregs([Any|Db]) -> [Any|tdb_kill_xregs(Db)];
tdb_kill_xregs([]) -> [].
-
-remove_key(Key, [{Key,_Op}|Ops]) -> remove_key(Key, Ops);
-remove_key(_, Ops) -> Ops.
-merge_type_info(I, I) -> I;
-merge_type_info({tuple,min_size,Sz1,Same}, {tuple,min_size,Sz2,Same}=Max) when Sz1 < Sz2 ->
+%% meet(Type1, Type2) -> Type
+%% Returns the "meet" of Type1 and Type2. The meet is a narrower
+%% type than Type1 and Type2. For example:
+%%
+%% meet(integer, {integer,{0,3}}) -> {integer,{0,3}}
+%%
+%% The meet for two different types result in 'none', which is
+%% the bottom element for our type lattice:
+%%
+%% meet(integer, map) -> none
+
+meet(T, T) ->
+ T;
+meet({integer,_}=T, integer) ->
+ T;
+meet(integer, {integer,_}=T) ->
+ T;
+meet({integer,{Min1,Max1}}, {integer,{Min2,Max2}}) ->
+ {integer,{max(Min1, Min2),min(Max1, Max2)}};
+meet({tuple,min_size,Sz1,Same}, {tuple,min_size,Sz2,Same}=Max) when Sz1 < Sz2 ->
Max;
-merge_type_info({tuple,min_size,Sz1,Same}=Max, {tuple,min_size,Sz2,Same}) when Sz1 > Sz2 ->
+meet({tuple,min_size,Sz1,Same}=Max, {tuple,min_size,Sz2,Same}) when Sz1 > Sz2 ->
Max;
-merge_type_info({tuple,exact_size,_,Same}=Exact, {tuple,_,_,Same}) ->
+meet({tuple,exact_size,_,Same}=Exact, {tuple,_,_,Same}) ->
Exact;
-merge_type_info({tuple,_,_,Same},{tuple,exact_size,_,Same}=Exact) ->
+meet({tuple,_,_,Same},{tuple,exact_size,_,Same}=Exact) ->
Exact;
-merge_type_info({tuple,SzKind1,Sz1,[]}, {tuple,_SzKind2,_Sz2,First}=Tuple2) ->
- merge_type_info({tuple,SzKind1,Sz1,First}, Tuple2);
-merge_type_info({tuple,_SzKind1,_Sz1,First}=Tuple1, {tuple,SzKind2,Sz2,_}) ->
- merge_type_info(Tuple1, {tuple,SzKind2,Sz2,First});
-merge_type_info(integer, {integer,_}=Int) ->
- Int;
-merge_type_info({integer,_}=Int, integer) ->
- Int;
-merge_type_info({integer,{Min1,Max1}}, {integer,{Min2,Max2}}) ->
- {integer,{max(Min1, Min2),min(Max1, Max2)}};
-merge_type_info(NewType, _) ->
- verify_type(NewType),
- NewType.
-
-verify_type({atom,_}) -> ok;
-verify_type(boolean) -> ok;
-verify_type(integer) -> ok;
-verify_type({integer,{Min,Max}})
- when is_integer(Min), is_integer(Max) -> ok;
-verify_type(map) -> ok;
-verify_type(nonempty_list) -> ok;
-verify_type({tuple,_,Sz,[]}) when is_integer(Sz) -> ok;
-verify_type({tuple,_,Sz,[_]}) when is_integer(Sz) -> ok;
-verify_type({tuple_element,_,_}) -> ok;
-verify_type(float) -> ok.
+meet({tuple,SzKind1,Sz1,[]}, {tuple,_SzKind2,_Sz2,First}=Tuple2) ->
+ meet({tuple,SzKind1,Sz1,First}, Tuple2);
+meet({tuple,_SzKind1,_Sz1,First}=Tuple1, {tuple,SzKind2,Sz2,_}) ->
+ meet(Tuple1, {tuple,SzKind2,Sz2,First});
+meet({binary,U1}, {binary,U2}) ->
+ {binary,max(U1, U2)};
+meet(T1, T2) ->
+ case is_any(T1) of
+ true ->
+ verified_type(T2);
+ false ->
+ case is_any(T2) of
+ true ->
+ verified_type(T1);
+ false ->
+ none %The bottom element.
+ end
+ end.
+
+is_any(any) -> true;
+is_any({tuple_element,_,_}) -> true;
+is_any(_) -> false.
+
+%% verified_type(Type) -> Type
+%% Returns the passed in type if it is one of the defined types.
+%% Crashes if there is anything wrong with the type.
+%%
+%% Here are all possible types:
+%%
+%% any Any Erlang term (top element for the type lattice).
+%%
+%% {atom,Atom} The specific atom Atom.
+%% {binary,Unit} Binary/bitstring aligned to unit Unit.
+%% boolean 'true' | 'false'
+%% float Floating point number.
+%% integer Integer.
+%% {integer,{Min,Max}} Integer in the inclusive range Min through Max.
+%% map Map.
+%% nonempty_list Nonempty list.
+%% {tuple,_,_,_} Tuple (see below).
+%%
+%% none No type (bottom element for the type lattice).
+%%
+%% {tuple,min_size,Size,First} means that the corresponding register
+%% contains a tuple with *at least* Size elements (conversely,
+%% {tuple,exact_size,Size,First} means that it contains a tuple with
+%% *exactly* Size elements). An tuple with unknown size is
+%% represented as {tuple,min_size,0,[]}. First is either [] (meaning
+%% that the tuple's first element is unknown) or [FirstElement] (the
+%% contents of the first element).
+%%
+%% There is also a pseudo-type called {tuple_element,_,_}:
+%%
+%% {tuple_element,SrcTuple,ElementNumber}
+%%
+%% that does not provide any information about the type of the
+%% register itself, but provides a link back to the source tuple that
+%% the register got its value from.
+%%
+%% Note that {tuple_element,_,_} will *never* be returned by tdb_find/2.
+%% Use tdb_find_source_tuple/2 to locate the source tuple for a register.
+
+verified_type(any=T) -> T;
+verified_type({atom,_}=T) -> T;
+verified_type({binary,U}=T) when is_integer(U) -> T;
+verified_type(boolean=T) -> T;
+verified_type(integer=T) -> T;
+verified_type({integer,{Min,Max}}=T)
+ when is_integer(Min), is_integer(Max) -> T;
+verified_type(map=T) -> T;
+verified_type(nonempty_list=T) -> T;
+verified_type({tuple,_,Sz,[]}=T) when is_integer(Sz) -> T;
+verified_type({tuple,_,Sz,[_]}=T) when is_integer(Sz) -> T;
+verified_type({tuple_element,_,_}=T) -> T;
+verified_type(float=T) -> T.
diff --git a/lib/compiler/src/beam_utils.erl b/lib/compiler/src/beam_utils.erl
index 5333925589..047cd5a569 100644
--- a/lib/compiler/src/beam_utils.erl
+++ b/lib/compiler/src/beam_utils.erl
@@ -118,7 +118,7 @@ is_killed(R, Is, D) ->
St = #live{lbl=D,res=gb_trees:empty()},
case check_liveness(R, Is, St) of
{killed,_} -> true;
- {exit_not_used,_} -> true;
+ {exit_not_used,_} -> false;
{_,_} -> false
end.
@@ -131,7 +131,7 @@ is_killed_at(R, Lbl, D) when is_integer(Lbl) ->
St0 = #live{lbl=D,res=gb_trees:empty()},
case check_liveness_at(R, Lbl, St0) of
{killed,_} -> true;
- {exit_not_used,_} -> true;
+ {exit_not_used,_} -> false;
{_,_} -> false
end.
@@ -148,7 +148,7 @@ is_not_used(R, Is, D) ->
St = #live{lbl=D,res=gb_trees:empty()},
case check_liveness(R, Is, St) of
{used,_} -> false;
- {exit_not_used,_} -> false;
+ {exit_not_used,_} -> true;
{_,_} -> true
end.
@@ -440,8 +440,11 @@ check_liveness(R, [{bs_init,_,_,Live,Ss,Dst}|Is], St) ->
case member(R, Ss) of
true -> {used,St};
false ->
+ %% If the exception is taken, the stack may
+ %% be scanned. Therefore the register is not
+ %% guaranteed to be killed.
if
- R =:= Dst -> {killed,St};
+ R =:= Dst -> {not_used,St};
true -> not_used(check_liveness(R, Is, St))
end
end
@@ -602,8 +605,11 @@ check_liveness(R, [{test_heap,N,Live}|Is], St) ->
check_liveness(R, [{allocate_zero,N,Live}|Is], St) ->
I = {block,[{set,[],[],{alloc,Live,{zero,N,0,[]}}}]},
check_liveness(R, [I|Is], St);
-check_liveness(R, [{get_list,S,D1,D2}|Is], St) ->
- I = {block,[{set,[D1,D2],[S],get_list}]},
+check_liveness(R, [{get_hd,S,D}|Is], St) ->
+ I = {block,[{set,[D],[S],get_hd}]},
+ check_liveness(R, [I|Is], St);
+check_liveness(R, [{get_tl,S,D}|Is], St) ->
+ I = {block,[{set,[D],[S],get_tl}]},
check_liveness(R, [I|Is], St);
check_liveness(R, [remove_message|Is], St) ->
check_liveness(R, Is, St);
@@ -732,8 +738,8 @@ check_liveness_block_1(R, Ss, Ds, Op, Is, St0) ->
end
end.
-check_liveness_block_2(R, {gc_bif,_Op,{f,Lbl}}, _Ss, St) ->
- check_liveness_block_3(R, Lbl, St);
+check_liveness_block_2(R, {gc_bif,Op,{f,Lbl}}, Ss, St) ->
+ check_liveness_block_3(R, Lbl, {Op,length(Ss)}, St);
check_liveness_block_2(R, {bif,Op,{f,Lbl}}, Ss, St) ->
Arity = length(Ss),
case erl_internal:comp_op(Op, Arity) orelse
@@ -741,16 +747,23 @@ check_liveness_block_2(R, {bif,Op,{f,Lbl}}, Ss, St) ->
true ->
{killed,St};
false ->
- check_liveness_block_3(R, Lbl, St)
+ check_liveness_block_3(R, Lbl, {Op,length(Ss)}, St)
end;
check_liveness_block_2(R, {put_map,_Op,{f,Lbl}}, _Ss, St) ->
- check_liveness_block_3(R, Lbl, St);
+ check_liveness_block_3(R, Lbl, {unsafe,0}, St);
check_liveness_block_2(_, _, _, St) ->
{killed,St}.
-check_liveness_block_3(_, 0, St) ->
+check_liveness_block_3({x,_}, 0, _FA, St) ->
{killed,St};
-check_liveness_block_3(R, Lbl, St0) ->
+check_liveness_block_3({y,_}, 0, {F,A}, St) ->
+ %% If the exception is thrown, the stack may be scanned,
+ %% thus implicitly using the y register.
+ case erl_bifs:is_safe(erlang, F, A) of
+ true -> {killed,St};
+ false -> {used,St}
+ end;
+check_liveness_block_3(R, Lbl, _FA, St0) ->
check_liveness_at(R, Lbl, St0).
index_labels_1([{label,Lbl}|Is0], Acc) ->
@@ -788,6 +801,10 @@ replace_labels_1([{wait,{f,Lbl}}|Is], Acc, D, Fb) ->
replace_labels_1(Is, [{wait,{f,label(Lbl, D, Fb)}}|Acc], D, Fb);
replace_labels_1([{wait_timeout,{f,Lbl},To}|Is], Acc, D, Fb) ->
replace_labels_1(Is, [{wait_timeout,{f,label(Lbl, D, Fb)},To}|Acc], D, Fb);
+replace_labels_1([{recv_mark=Op,{f,Lbl}}|Is], Acc, D, Fb) ->
+ replace_labels_1(Is, [{Op,{f,label(Lbl, D, Fb)}}|Acc], D, Fb);
+replace_labels_1([{recv_set=Op,{f,Lbl}}|Is], Acc, D, Fb) ->
+ replace_labels_1(Is, [{Op,{f,label(Lbl, D, Fb)}}|Acc], D, Fb);
replace_labels_1([{bif,Name,{f,Lbl},As,R}|Is], Acc, D, Fb) when Lbl =/= 0 ->
replace_labels_1(Is, [{bif,Name,{f,label(Lbl, D, Fb)},As,R}|Acc], D, Fb);
replace_labels_1([{gc_bif,Name,{f,Lbl},Live,As,R}|Is], Acc, D, Fb) when Lbl =/= 0 ->
@@ -991,47 +1008,52 @@ live_opt([{recv_mark,_}=I|Is], Regs, D, Acc) ->
live_opt([], _, _, Acc) -> Acc.
-live_opt_block([{set,Ds,Ss,Op0}|Is], Regs0, D, Acc) ->
- Regs1 = x_live(Ss, x_dead(Ds, Regs0)),
- {Op, Regs} = live_opt_block_op(Op0, Regs1, D),
- I = {set, Ds, Ss, Op},
-
- case Ds of
- [{x,X}] ->
- case (not is_live(X, Regs0)) andalso Op =:= move of
- true ->
- live_opt_block(Is, Regs0, D, Acc);
- false ->
- live_opt_block(Is, Regs, D, [I|Acc])
- end;
- _ ->
- live_opt_block(Is, Regs, D, [I|Acc])
+live_opt_block([{set,[{x,X}]=Ds,Ss,move}=I|Is], Regs0, D, Acc) ->
+ Regs = x_live(Ss, x_dead(Ds, Regs0)),
+ case is_live(X, Regs0) of
+ true ->
+ live_opt_block(Is, Regs, D, [I|Acc]);
+ false ->
+ %% Useless move, will never be used.
+ live_opt_block(Is, Regs, D, Acc)
end;
-live_opt_block([{'%anno',_}|Is], Regs, D, Acc) ->
- live_opt_block(Is, Regs, D, Acc);
-live_opt_block([], Regs, _, Acc) -> {Acc,Regs}.
-
-live_opt_block_op({alloc,Live0,AllocOp}, Regs0, D) ->
- Regs =
- case AllocOp of
- {Kind, _N, Fail} when Kind =:= gc_bif; Kind =:= put_map ->
- live_join_label(Fail, D, Regs0);
- _ ->
- Regs0
- end,
+live_opt_block([{set,Ds,Ss,{alloc,Live0,AllocOp}}|Is], Regs0, D, Acc) ->
+ %% Calculate liveness from the point of view of the GC.
+ %% There will never be a GC if the instruction fails, so we should
+ %% ignore the failure branch.
+ GcRegs1 = x_dead(Ds, Regs0),
+ GcRegs = x_live(Ss, GcRegs1),
+ Live = live_regs(GcRegs),
%% The life-time analysis used by the code generator is sometimes too
%% conservative, so it may be possible to lower the number of live
%% registers based on the exact liveness information. The main benefit is
%% that more optimizations that depend on liveness information (such as the
- %% beam_bool and beam_dead passes) may be applied.
- Live = live_regs(Regs),
- true = Live =< Live0,
- {{alloc,Live,AllocOp}, live_call(Live)};
-live_opt_block_op({bif,_N,Fail} = Op, Regs, D) ->
- {Op, live_join_label(Fail, D, Regs)};
-live_opt_block_op(Op, Regs, _D) ->
- {Op, Regs}.
+ %% beam_dead pass) may be applied.
+ true = Live =< Live0, %Assertion.
+ I = {set,Ds,Ss,{alloc,Live,AllocOp}},
+
+ %% Calculate liveness from the point of view of the preceding instruction.
+ %% The liveness is the union of live registers in the GC and the live
+ %% registers at the failure label.
+ Regs1 = live_call(Live),
+ Regs = live_join_alloc(AllocOp, D, Regs1),
+ live_opt_block(Is, Regs, D, [I|Acc]);
+live_opt_block([{set,Ds,Ss,{bif,_,Fail}}=I|Is], Regs0, D, Acc) ->
+ Regs1 = x_dead(Ds, Regs0),
+ Regs2 = x_live(Ss, Regs1),
+ Regs = live_join_label(Fail, D, Regs2),
+ live_opt_block(Is, Regs, D, [I|Acc]);
+live_opt_block([{set,Ds,Ss,_}=I|Is], Regs0, D, Acc) ->
+ Regs = x_live(Ss, x_dead(Ds, Regs0)),
+ live_opt_block(Is, Regs, D, [I|Acc]);
+live_opt_block([{'%anno',_}|Is], Regs, D, Acc) ->
+ live_opt_block(Is, Regs, D, Acc);
+live_opt_block([], Regs, _, Acc) -> {Acc,Regs}.
+
+live_join_alloc({Kind,_Name,Fail}, D, Regs) when Kind =:= gc_bif; Kind =:= put_map ->
+ live_join_label(Fail, D, Regs);
+live_join_alloc(_, _, Regs) -> Regs.
live_join_labels([{f,L}|T], D, Regs0) when L =/= 0 ->
Regs = gb_trees:get(L, D) bor Regs0,
diff --git a/lib/compiler/src/beam_validator.erl b/lib/compiler/src/beam_validator.erl
index 22ceef097c..c30ab34ac7 100644
--- a/lib/compiler/src/beam_validator.erl
+++ b/lib/compiler/src/beam_validator.erl
@@ -85,8 +85,6 @@ format_error(Error) ->
%%% Things currently not checked. XXX
%%%
%%% - Heap allocation for binaries.
-%%% - That put_tuple is followed by the correct number of
-%%% put instructions.
%%%
%% validate(Module, [Function]) -> [] | [Error]
@@ -148,7 +146,8 @@ validate_0(Module, [{function,Name,Ar,Entry,Code}|Fs], Ft) ->
hf=0, %Available heap size for floats.
fls=undefined, %Floating point state.
ct=[], %List of hot catch/try labels
- setelem=false %Previous instruction was setelement/3.
+ setelem=false, %Previous instruction was setelement/3.
+ puts_left=none %put/1 instructions left.
}).
-type label() :: integer().
@@ -340,11 +339,25 @@ valfun_1({put_list,A,B,Dst}, Vst0) ->
Vst = eat_heap(2, Vst0),
set_type_reg(cons, Dst, Vst);
valfun_1({put_tuple,Sz,Dst}, Vst0) when is_integer(Sz) ->
+ Vst1 = eat_heap(1, Vst0),
+ Vst = set_type_reg(tuple_in_progress, Dst, Vst1),
+ #vst{current=St0} = Vst,
+ St = St0#st{puts_left={Sz,{Dst,{tuple,Sz}}}},
+ Vst#vst{current=St};
+valfun_1({put,Src}, Vst0) ->
+ assert_term(Src, Vst0),
Vst = eat_heap(1, Vst0),
- set_type_reg({tuple,Sz}, Dst, Vst);
-valfun_1({put,Src}, Vst) ->
- assert_term(Src, Vst),
- eat_heap(1, Vst);
+ #vst{current=St0} = Vst,
+ case St0 of
+ #st{puts_left=none} ->
+ error(not_building_a_tuple);
+ #st{puts_left={1,{Dst,Type}}} ->
+ St = St0#st{puts_left=none},
+ set_type_reg(Type, Dst, Vst#vst{current=St});
+ #st{puts_left={PutsLeft,Info}} when is_integer(PutsLeft) ->
+ St = St0#st{puts_left={PutsLeft-1,Info}},
+ Vst#vst{current=St}
+ end;
%% Instructions for optimization of selective receives.
valfun_1({recv_mark,{f,Fail}}, Vst) when is_integer(Fail) ->
Vst;
@@ -578,6 +591,12 @@ valfun_4({get_list,Src,D1,D2}, Vst0) ->
assert_type(cons, Src, Vst0),
Vst = set_type_reg(term, D1, Vst0),
set_type_reg(term, D2, Vst);
+valfun_4({get_hd,Src,Dst}, Vst) ->
+ assert_type(cons, Src, Vst),
+ set_type_reg(term, Dst, Vst);
+valfun_4({get_tl,Src,Dst}, Vst) ->
+ assert_type(cons, Src, Vst),
+ set_type_reg(term, Dst, Vst);
valfun_4({get_tuple_element,Src,I,Dst}, Vst) ->
assert_type({tuple_element,I+1}, Src, Vst),
set_type_reg(term, Dst, Vst);
@@ -1134,6 +1153,7 @@ set_type_y(Type, {y,Y}=Reg, #vst{current=#st{y=Ys0}=St}=Vst)
{value,_} ->
gb_trees:update(Y, Type, Ys0)
end,
+ check_try_catch_tags(Type, Y, Ys0),
Vst#vst{current=St#st{y=Ys}};
set_type_y(Type, Reg, #vst{}) -> error({invalid_store,Reg,Type}).
@@ -1141,6 +1161,29 @@ set_catch_end({y,Y}, #vst{current=#st{y=Ys0}=St}=Vst) ->
Ys = gb_trees:update(Y, initialized, Ys0),
Vst#vst{current=St#st{y=Ys}}.
+check_try_catch_tags(Type, LastY, Ys) ->
+ case is_try_catch_tag(Type) of
+ false ->
+ ok;
+ true ->
+ %% Every catch or try/catch must use a lower Y register
+ %% number than any enclosing catch or try/catch. That will
+ %% ensure that when the stack is scanned when an
+ %% exception occurs, the innermost try/catch tag is found
+ %% first.
+ Bad = [{{y,Y},Tag} || {Y,Tag} <- gb_trees:to_list(Ys),
+ Y < LastY, is_try_catch_tag(Tag)],
+ case Bad of
+ [] ->
+ ok;
+ [_|_] ->
+ error({bad_try_catch_nesting,{y,LastY},Bad})
+ end
+ end.
+
+is_try_catch_tag({catchtag,_}) -> true;
+is_try_catch_tag({trytag,_}) -> true;
+is_try_catch_tag(_) -> false.
is_reg_defined({x,_}=Reg, Vst) -> is_type_defined_x(Reg, Vst);
is_reg_defined({y,_}=Reg, Vst) -> is_type_defined_y(Reg, Vst);
@@ -1274,6 +1317,7 @@ get_move_term_type(Src, Vst) ->
initialized -> error({unassigned,Src});
{catchtag,_} -> error({catchtag,Src});
{trytag,_} -> error({trytag,Src});
+ tuple_in_progress -> error({tuple_in_progress,Src});
Type -> Type
end.
@@ -1282,10 +1326,7 @@ get_move_term_type(Src, Vst) ->
%% a standard Erlang type (no catch/try tags or match contexts).
get_term_type(Src, Vst) ->
- case get_term_type_1(Src, Vst) of
- initialized -> error({unassigned,Src});
- {catchtag,_} -> error({catchtag,Src});
- {trytag,_} -> error({trytag,Src});
+ case get_move_term_type(Src, Vst) of
#ms{} -> error({match_context,Src});
Type -> Type
end.
@@ -1332,7 +1373,12 @@ branch_arities([Sz,{f,L}|T], Tuple, #vst{current=St}=Vst0)
Vst = branch_state(L, Vst1),
branch_arities(T, Tuple, Vst#vst{current=St}).
-branch_state(0, #vst{}=Vst) -> Vst;
+branch_state(0, #vst{}=Vst) ->
+ %% If the instruction fails, the stack may be scanned
+ %% looking for a catch tag. Therefore the Y registers
+ %% must be initialized at this point.
+ verify_y_init(Vst),
+ Vst;
branch_state(L, #vst{current=St,branched=B}=Vst) ->
Vst#vst{
branched=case gb_trees:is_defined(L, B) of
diff --git a/lib/compiler/src/beam_z.erl b/lib/compiler/src/beam_z.erl
index 1c56b95a9e..6c3a6995d7 100644
--- a/lib/compiler/src/beam_z.erl
+++ b/lib/compiler/src/beam_z.erl
@@ -24,18 +24,20 @@
-export([module/2]).
--import(lists, [dropwhile/2]).
+-import(lists, [dropwhile/2,map/2]).
-spec module(beam_utils:module_code(), [compile:option()]) ->
{'ok',beam_asm:module_code()}.
-module({Mod,Exp,Attr,Fs0,Lc}, _Opt) ->
- Fs = [function(F) || F <- Fs0],
+module({Mod,Exp,Attr,Fs0,Lc}, Opts) ->
+ NoGetHdTl = proplists:get_bool(no_get_hd_tl, Opts),
+ Fs = [function(F, NoGetHdTl) || F <- Fs0],
{ok,{Mod,Exp,Attr,Fs,Lc}}.
-function({function,Name,Arity,CLabel,Is0}) ->
+function({function,Name,Arity,CLabel,Is0}, NoGetHdTl) ->
try
- Is = undo_renames(Is0),
+ Is1 = undo_renames(Is0),
+ Is = maybe_eliminate_get_hd_tl(Is1, NoGetHdTl),
{function,Name,Arity,CLabel,Is}
catch
Class:Error:Stack ->
@@ -65,6 +67,10 @@ undo_renames([{bif,raise,_,_,_}=I|Is0]) ->
(_) -> true
end, Is0),
[I|undo_renames(Is)];
+undo_renames([{get_hd,Src,Dst1},{get_tl,Src,Dst2}|Is]) ->
+ [{get_list,Src,Dst1,Dst2}|undo_renames(Is)];
+undo_renames([{get_tl,Src,Dst2},{get_hd,Src,Dst1}|Is]) ->
+ [{get_list,Src,Dst1,Dst2}|undo_renames(Is)];
undo_renames([I|Is]) ->
[undo_rename(I)|undo_renames(Is)];
undo_renames([]) -> [].
@@ -107,3 +113,17 @@ undo_rename({get_map_elements,Fail,Src,{list,List}}) ->
undo_rename({select,I,Reg,Fail,List}) ->
{I,Reg,Fail,{list,List}};
undo_rename(I) -> I.
+
+%%%
+%%% Eliminate get_hd/get_tl instructions if requested by
+%%% the no_get_hd_tl option.
+%%%
+
+maybe_eliminate_get_hd_tl(Is, true) ->
+ map(fun({get_hd,Cons,Hd}) ->
+ {get_list,Cons,Hd,{x,1022}};
+ ({get_tl,Cons,Tl}) ->
+ {get_list,Cons,{x,1022},Tl};
+ (I) -> I
+ end, Is);
+maybe_eliminate_get_hd_tl(Is, false) -> Is.
diff --git a/lib/compiler/src/compile.erl b/lib/compiler/src/compile.erl
index 1409c358c2..c6a0056a70 100644
--- a/lib/compiler/src/compile.erl
+++ b/lib/compiler/src/compile.erl
@@ -219,13 +219,15 @@ expand_opt(report, Os) ->
expand_opt(return, Os) ->
[return_errors,return_warnings|Os];
expand_opt(r16, Os) ->
- [no_record_opt,no_utf8_atoms|Os];
+ [no_get_hd_tl,no_record_opt,no_utf8_atoms|Os];
expand_opt(r17, Os) ->
- [no_record_opt,no_utf8_atoms|Os];
+ [no_get_hd_tl,no_record_opt,no_utf8_atoms|Os];
expand_opt(r18, Os) ->
- [no_record_opt,no_utf8_atoms|Os];
+ [no_get_hd_tl,no_record_opt,no_utf8_atoms|Os];
expand_opt(r19, Os) ->
- [no_record_opt,no_utf8_atoms|Os];
+ [no_get_hd_tl,no_record_opt,no_utf8_atoms|Os];
+expand_opt(r20, Os) ->
+ [no_get_hd_tl,no_record_opt,no_utf8_atoms|Os];
expand_opt({debug_info_key,_}=O, Os) ->
[encrypt_debug_info,O|Os];
expand_opt(no_float_opt, Os) ->
diff --git a/lib/compiler/src/core_parse.yrl b/lib/compiler/src/core_parse.yrl
index 79a7cccd98..85444023c6 100644
--- a/lib/compiler/src/core_parse.yrl
+++ b/lib/compiler/src/core_parse.yrl
@@ -496,7 +496,7 @@ make_lit_bin(Acc, [#c_bitstr{val=I0,size=Sz0,unit=U0,type=Type0,flags=F0}|T]) ->
throw(impossible)
end,
if
- Sz =< 8, T =:= [] ->
+ 0 =< Sz, Sz =< 8, T =:= [] ->
<<Acc/binary,I:Sz>>;
Sz =:= 8 ->
make_lit_bin(<<Acc/binary,I:8>>, T);
diff --git a/lib/compiler/src/genop.tab b/lib/compiler/src/genop.tab
index d59bb241a8..a47d4e8cf7 100755
--- a/lib/compiler/src/genop.tab
+++ b/lib/compiler/src/genop.tab
@@ -564,3 +564,13 @@ BEAM_FORMAT_NUMBER=0
## exception, but store the atom 'badarg' in x(0) and execute the
## next instruction.
161: raw_raise/0
+
+## @spec get_hd Source Head
+## @doc Get the head (or car) part of a list (a cons cell) from Source and
+## put it into the register Head.
+162: get_hd/2
+
+## @spec get_tl Source Tail
+## @doc Get the tail (or cdr) part of a list (a cons cell) from Source and
+## put it into the register Tail.
+163: get_tl/2
diff --git a/lib/compiler/src/v3_codegen.erl b/lib/compiler/src/v3_codegen.erl
index a96d58a903..a8f4926e55 100644
--- a/lib/compiler/src/v3_codegen.erl
+++ b/lib/compiler/src/v3_codegen.erl
@@ -1495,28 +1495,34 @@ select_extract_map(Src, Vs, Fail, I, Vdb, Bef, St) ->
{Code, Aft, St}.
-select_extract_cons(Src, [#k_var{name=Hd}, #k_var{name=Tl}], I, Vdb, Bef, St) ->
- {Es,Aft} = case {vdb_find(Hd, Vdb), vdb_find(Tl, Vdb)} of
- {{_,_,Lhd}, {_,_,Ltl}} when Lhd =< I, Ltl =< I ->
- %% Both head and tail are dead. No need to generate
- %% any instruction.
- {[], Bef};
- _ ->
- %% At least one of head and tail will be used,
- %% but we must always fetch both. We will call
- %% clear_dead/2 to allow reuse of the register
- %% in case only of them is used.
-
- Reg0 = put_reg(Tl, put_reg(Hd, Bef#sr.reg)),
- Int0 = Bef#sr{reg=Reg0},
- Rsrc = fetch_var(Src, Int0),
- Rhd = fetch_reg(Hd, Reg0),
- Rtl = fetch_reg(Tl, Reg0),
- Int1 = clear_dead(Int0, I, Vdb),
- {[{get_list,Rsrc,Rhd,Rtl}], Int1}
- end,
- {Es,Aft,St}.
-
+select_extract_cons(Src, [#k_var{name=Hd},#k_var{name=Tl}], I, Vdb, Bef, St) ->
+ Rsrc = fetch_var(Src, Bef),
+ Int = clear_dead(Bef, I, Vdb),
+ {{_,_,Lhd},{_,_,Ltl}} = {vdb_find(Hd, Vdb),vdb_find(Tl, Vdb)},
+ case {Lhd =< I, Ltl =< I} of
+ {true,true} ->
+ %% Both dead.
+ {[],Bef,St};
+ {true,false} ->
+ %% Head dead.
+ Reg0 = put_reg(Tl, Bef#sr.reg),
+ Aft = Int#sr{reg=Reg0},
+ Rtl = fetch_reg(Tl, Reg0),
+ {[{get_tl,Rsrc,Rtl}],Aft,St};
+ {false,true} ->
+ %% Tail dead.
+ Reg0 = put_reg(Hd, Bef#sr.reg),
+ Aft = Int#sr{reg=Reg0},
+ Rhd = fetch_reg(Hd, Reg0),
+ {[{get_hd,Rsrc,Rhd}],Aft,St};
+ {false,false} ->
+ %% Both used.
+ Reg0 = put_reg(Tl, put_reg(Hd, Bef#sr.reg)),
+ Aft = Bef#sr{reg=Reg0},
+ Rhd = fetch_reg(Hd, Reg0),
+ Rtl = fetch_reg(Tl, Reg0),
+ {[{get_hd,Rsrc,Rhd},{get_tl,Rsrc,Rtl}],Aft,St}
+ end.
guard_clause_cg(#k_guard_clause{anno=#l{vdb=Vdb},guard=G,body=B}, Fail, Bef, St0) ->
{Gis,Int,St1} = guard_cg(G, Fail, Vdb, Bef, St0),
diff --git a/lib/compiler/src/v3_core.erl b/lib/compiler/src/v3_core.erl
index 6029b91cdc..8cf8c69fef 100644
--- a/lib/compiler/src/v3_core.erl
+++ b/lib/compiler/src/v3_core.erl
@@ -1152,7 +1152,7 @@ fun_tq(Cs0, L, St0, NameInfo) ->
%% lc_tq(Line, Exp, [Qualifier], Mc, State) -> {LetRec,[PreExp],State}.
%% This TQ from Simon PJ pp 127-138.
-lc_tq(Line, E, [#igen{anno=GAnno,ceps=Ceps,
+lc_tq(Line, E, [#igen{anno=#a{anno=GA}=GAnno,ceps=Ceps,
acc_pat=AccPat,acc_guard=AccGuard,
skip_pat=SkipPat,tail=Tail,tail_pat=TailPat,
arg={Pre,Arg}}|Qs], Mc, St0) ->
@@ -1162,7 +1162,7 @@ lc_tq(Line, E, [#igen{anno=GAnno,ceps=Ceps,
F = #c_var{anno=LA,name={Name,1}},
Nc = #iapply{anno=GAnno,op=F,args=[Tail]},
{Var,St2} = new_var(St1),
- Fc = function_clause([Var], LA, {Name,1}),
+ Fc = function_clause([Var], GA, {Name,1}),
TailClause = #iclause{anno=LAnno,pats=[TailPat],guard=[],body=[Mc]},
Cs0 = case {AccPat,AccGuard} of
{SkipPat,[]} ->
@@ -1185,9 +1185,9 @@ lc_tq(Line, E, [#igen{anno=GAnno,ceps=Ceps,
body=Lps ++ [Lc]}|Cs0],
St3}
end,
- Fun = #ifun{anno=LAnno,id=[],vars=[Var],clauses=Cs,fc=Fc},
- {#iletrec{anno=LAnno#a{anno=[list_comprehension|LA]},defs=[{{Name,1},Fun}],
- body=Pre ++ [#iapply{anno=LAnno,op=F,args=[Arg]}]},
+ Fun = #ifun{anno=GAnno,id=[],vars=[Var],clauses=Cs,fc=Fc},
+ {#iletrec{anno=GAnno#a{anno=[list_comprehension|GA]},defs=[{{Name,1},Fun}],
+ body=Pre ++ [#iapply{anno=GAnno,op=F,args=[Arg]}]},
Ceps,St4};
lc_tq(Line, E, [#ifilter{}=Filter|Qs], Mc, St) ->
filter_tq(Line, E, Filter, Mc, St, Qs, fun lc_tq/5);
diff --git a/lib/compiler/src/v3_kernel.erl b/lib/compiler/src/v3_kernel.erl
index fd73e5a7dc..dfe8d26afb 100644
--- a/lib/compiler/src/v3_kernel.erl
+++ b/lib/compiler/src/v3_kernel.erl
@@ -2377,12 +2377,11 @@ uexpr(#k_try{anno=A,arg=A0,vars=Vs,body=B0,evars=Evs,handler=H0},
{A1,Au,St2} = ubody(A0, {break,Avs}, St1),
{B1,Bu,St3} = ubody(B0, Br, St2),
{H1,Hu,St4} = ubody(H0, Br, St3),
- {Rs1,St5} = ensure_return_vars(Rs0, St4),
Used = union([Au,subtract(Bu, lit_list_vars(Vs)),
subtract(Hu, lit_list_vars(Evs))]),
- {#k_try{anno=#k{us=Used,ns=lit_list_vars(Rs1),a=A},
- arg=A1,vars=Vs,body=B1,evars=Evs,handler=H1,ret=Rs1},
- Used,St5}
+ {#k_try{anno=#k{us=Used,ns=lit_list_vars(Rs0),a=A},
+ arg=A1,vars=Vs,body=B1,evars=Evs,handler=H1,ret=Rs0},
+ Used,St4}
end;
uexpr(#k_try{anno=A,arg=A0,vars=Vs,body=B0,evars=Evs,handler=H0},
return, St0) ->
@@ -2390,13 +2389,11 @@ uexpr(#k_try{anno=A,arg=A0,vars=Vs,body=B0,evars=Evs,handler=H0},
{A1,Au,St2} = ubody(A0, {break,Avs}, St1), %Must break to clean up here!
{B1,Bu,St3} = ubody(B0, return, St2),
{H1,Hu,St4} = ubody(H0, return, St3),
- NumNew = 1,
- {Ns,St5} = new_vars(NumNew, St4),
Used = union([Au,subtract(Bu, lit_list_vars(Vs)),
subtract(Hu, lit_list_vars(Evs))]),
- {#k_try_enter{anno=#k{us=Used,ns=Ns,a=A},
+ {#k_try_enter{anno=#k{us=Used,ns=[],a=A},
arg=A1,vars=Vs,body=B1,evars=Evs,handler=H1},
- Used,St5};
+ Used,St4};
uexpr(#k_catch{anno=A,body=B0}, {break,Rs0}, St0) ->
{Rb,St1} = new_var(St0),
{B1,Bu,St2} = ubody(B0, {break,[Rb]}, St1),
diff --git a/lib/compiler/src/v3_kernel_pp.erl b/lib/compiler/src/v3_kernel_pp.erl
index ac91039ae0..e9cbe81088 100644
--- a/lib/compiler/src/v3_kernel_pp.erl
+++ b/lib/compiler/src/v3_kernel_pp.erl
@@ -248,7 +248,7 @@ format_1(#k_put{arg=A,ret=Rs}, Ctxt) ->
[format(A, Ctxt),
format_ret(Rs, ctxt_bump_indent(Ctxt, 1))
];
-format_1(#k_try{arg=A,vars=Vs,body=B,evars=Evs,handler=H}, Ctxt) ->
+format_1(#k_try{arg=A,vars=Vs,body=B,evars=Evs,handler=H,ret=Rs}, Ctxt) ->
Ctxt1 = ctxt_bump_indent(Ctxt, Ctxt#ctxt.body_indent),
["try",
nl_indent(Ctxt1),
@@ -264,7 +264,8 @@ format_1(#k_try{arg=A,vars=Vs,body=B,evars=Evs,handler=H}, Ctxt) ->
nl_indent(Ctxt1),
format(H, Ctxt1),
nl_indent(Ctxt),
- "end"
+ "end",
+ format_ret(Rs, Ctxt)
];
format_1(#k_try_enter{arg=A,vars=Vs,body=B,evars=Evs,handler=H}, Ctxt) ->
Ctxt1 = ctxt_bump_indent(Ctxt, Ctxt#ctxt.body_indent),
diff --git a/lib/compiler/test/beam_block_SUITE.erl b/lib/compiler/test/beam_block_SUITE.erl
index 55d5f2dbe8..38ead96cc8 100644
--- a/lib/compiler/test/beam_block_SUITE.erl
+++ b/lib/compiler/test/beam_block_SUITE.erl
@@ -22,7 +22,7 @@
-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,
- erl_202/1,repro/1]).
+ erl_202/1,repro/1,local_cse/1,second_block_pass/1]).
%% The only test for the following functions is that
%% the code compiles and is accepted by beam_validator.
@@ -40,7 +40,9 @@ groups() ->
otp_7345,
move_opt_across_gc_bif,
erl_202,
- repro
+ repro,
+ local_cse,
+ second_block_pass
]}].
init_per_suite(Config) ->
@@ -237,6 +239,72 @@ find_operands(Cfg,XsiGraph,ActiveList,Count) ->
[Count+1, length(NewActiveList), length(digraph:vertices(XsiGraph))],
find_operands(NewCfg,XsiGraph,NewActiveList,Count+1).
+%% Some tests of local common subexpression elimination (CSE).
+
+local_cse(_Config) ->
+ {Self,{ok,Self}} = local_cse_1(),
+
+ local_cse_2([]),
+ local_cse_2(lists:seq(1, 512)),
+ local_cse_2(?MODULE:module_info()),
+
+ {[b],[a,b]} = local_cse_3(a, b),
+
+ {2000,Self,{Self,write_cache}} = local_cse_4(),
+
+ ok.
+
+local_cse_1() ->
+ %% Cover handling of unsafe tuple construction in
+ %% eliminate_use_of_from_reg/4. It became necessary to handle
+ %% unsafe tuples when local CSE was introduced.
+
+ {self(),{ok,self()}}.
+
+local_cse_2(Term) ->
+ case cse_make_binary(Term) of
+ <<Size:8,BinTerm:Size/binary>> ->
+ Term = binary_to_term(BinTerm);
+ <<Size:8,SizeTerm:Size/binary,BinTerm/binary>> ->
+ {'$size',TermSize} = binary_to_term(SizeTerm),
+ TermSize = byte_size(BinTerm),
+ Term = binary_to_term(BinTerm)
+ end.
+
+%% Copy of observer_backend:ttb_make_binary/1. During development of
+%% the local CSE optimization this function was incorrectly optimized.
+
+cse_make_binary(Term) ->
+ B = term_to_binary(Term),
+ SizeB = byte_size(B),
+ if SizeB > 255 ->
+ SB = term_to_binary({'$size',SizeB}),
+ <<(byte_size(SB)):8, SB/binary, B/binary>>;
+ true ->
+ <<SizeB:8, B/binary>>
+ end.
+
+local_cse_3(X, Y) ->
+ %% The following expression was incorrectly transformed to {[X,Y],[X,Y]}
+ %% during development of the local CSE optimization.
+
+ {[Y],[X,Y]}.
+
+local_cse_4() ->
+ do_local_cse_4(2000, self(), {self(), write_cache}).
+
+do_local_cse_4(X, Y, Z) ->
+ {X,Y,Z}.
+
+%% Tests previously found bugs when running beam_block the second time.
+
+second_block_pass(_Config) ->
+ [#{dts:=5.0}] = second_1([#{dts => 10.0}], 2.0),
+ ok.
+
+second_1(Fs, TS) ->
+ [F#{dts=>DTS / TS} || #{dts:=DTS} = F <- Fs].
+
%%%
%%% Common functions.
%%%
diff --git a/lib/compiler/test/beam_type_SUITE.erl b/lib/compiler/test/beam_type_SUITE.erl
index fe856b12b6..541075af2a 100644
--- a/lib/compiler/test/beam_type_SUITE.erl
+++ b/lib/compiler/test/beam_type_SUITE.erl
@@ -23,7 +23,7 @@
init_per_group/2,end_per_group/2,
integers/1,coverage/1,booleans/1,setelement/1,cons/1,
tuple/1,record_float/1,binary_float/1,float_compare/1,
- arity_checks/1]).
+ arity_checks/1,elixir_binaries/1]).
suite() -> [{ct_hooks,[ts_install_cth]}].
@@ -42,7 +42,8 @@ groups() ->
record_float,
binary_float,
float_compare,
- arity_checks
+ arity_checks,
+ elixir_binaries
]}].
init_per_suite(Config) ->
@@ -66,6 +67,15 @@ integers(_Config) ->
college = do_integers_3(),
+ zero = do_integers_4(<<0:1>>, 0),
+ one = do_integers_4(<<1:1>>, 0),
+ other = do_integers_4(<<1:1>>, 2),
+
+ zero = do_integers_5(0, 0),
+ one = do_integers_5(0, 1),
+ two = do_integers_5(0, 2),
+ three = do_integers_5(0, 3),
+
ok.
do_integers_1(B0) ->
@@ -88,7 +98,31 @@ do_integers_3() ->
1 -> 0
end.
-coverage(_Config) ->
+do_integers_4(<<X:1,T/bits>>, C) ->
+ %% Binary matching gives the range 0-1 for X.
+ %% The range for `X bor C` is unknown. It must not be inherited
+ %% from X. (`X bor C` will reuse the register used for X.)
+ case X bor C of
+ 0 -> do_integers_4(T, C, zero);
+ 1 -> do_integers_4(T, C, one);
+ _ -> do_integers_4(T, C, other)
+ end.
+
+do_integers_4(_, _, Res) ->
+ Res.
+
+do_integers_5(X0, Y0) ->
+ %% X and Y will use the same register.
+ X = X0 band 1,
+ Y = Y0 band 3,
+ case Y of
+ 0 -> zero;
+ 1 -> one;
+ 2 -> two;
+ 3 -> three
+ end.
+
+coverage(Config) ->
{'EXIT',{badarith,_}} = (catch id(1) bsl 0.5),
{'EXIT',{badarith,_}} = (catch id(2.0) bsl 2),
{'EXIT',{badarith,_}} = (catch a + 0.5),
@@ -99,6 +133,29 @@ coverage(_Config) ->
id(id(42) band 387439739874298734983787934283479243879),
id(-1 band id(13)),
+ error = if
+ is_map(Config), is_integer(Config) -> ok;
+ true -> error
+ end,
+ error = if
+ is_map(Config), is_atom(Config) -> ok;
+ true -> error
+ end,
+ error = if
+ is_map(Config), is_tuple(Config) -> ok;
+ true -> error
+ end,
+ error = if
+ is_integer(Config), is_bitstring(Config) -> ok;
+ true -> error
+ end,
+
+ ok = case Config of
+ <<_>> when is_binary(Config) ->
+ impossible;
+ [_|_] ->
+ ok
+ end,
ok.
booleans(_Config) ->
@@ -199,5 +256,42 @@ do_tuple_arity_check(RGB) when is_tuple(RGB),
_ -> ok
end.
+elixir_binaries(_Config) ->
+ <<"foo blitzky baz">> = elixir_binary_1(<<"blitzky">>),
+ <<"foo * baz">> = elixir_binary_2($*),
+ <<7:4,755:10>> = elixir_bitstring_3(<<755:10>>),
+ ok.
+
+elixir_binary_1(Bar) when is_binary(Bar) ->
+ <<"foo ",
+ case Bar of
+ Rewrite when is_binary(Rewrite) ->
+ Rewrite;
+ Rewrite ->
+ list_to_binary(Rewrite)
+ end/binary,
+ " baz">>.
+
+elixir_binary_2(Arg) ->
+ Bin = <<Arg>>,
+ <<"foo ",
+ case Bin of
+ Rewrite when is_binary(Rewrite) ->
+ Rewrite;
+ Rewrite ->
+ list_to_binary:to_string(Rewrite)
+ end/binary,
+ " baz">>.
+
+elixir_bitstring_3(Bar) when is_bitstring(Bar) ->
+ <<7:4,
+ case Bar of
+ Rewrite when is_bitstring(Rewrite) ->
+ Rewrite;
+ Rewrite ->
+ list_to_bitstring(Rewrite)
+ end/bitstring>>.
+
+
id(I) ->
I.
diff --git a/lib/compiler/test/beam_validator_SUITE.erl b/lib/compiler/test/beam_validator_SUITE.erl
index 685eb2a72e..b8fff7b100 100644
--- a/lib/compiler/test/beam_validator_SUITE.erl
+++ b/lib/compiler/test/beam_validator_SUITE.erl
@@ -33,8 +33,8 @@
state_after_fault_in_catch/1,no_exception_in_catch/1,
undef_label/1,illegal_instruction/1,failing_gc_guard_bif/1,
map_field_lists/1,cover_bin_opt/1,
- val_dsetel/1]).
-
+ val_dsetel/1,bad_tuples/1,bad_try_catch_nesting/1]).
+
-include_lib("common_test/include/ct.hrl").
init_per_testcase(Case, Config) when is_atom(Case), is_list(Config) ->
@@ -61,7 +61,8 @@ groups() ->
freg_state,bad_bin_match,bad_dsetel,
state_after_fault_in_catch,no_exception_in_catch,
undef_label,illegal_instruction,failing_gc_guard_bif,
- map_field_lists,cover_bin_opt,val_dsetel]}].
+ map_field_lists,cover_bin_opt,val_dsetel,
+ bad_tuples,bad_try_catch_nesting]}].
init_per_suite(Config) ->
Config.
@@ -509,6 +510,27 @@ destroy_reg({Tag,N}) ->
{y,N+1}
end.
+bad_tuples(Config) ->
+ Errors = do_val(bad_tuples, Config),
+ [{{bad_tuples,heap_overflow,1},
+ {{put,{x,0}},8,{heap_overflow,{left,0},{wanted,1}}}},
+ {{bad_tuples,long,2},
+ {{put,{atom,too_long}},8,not_building_a_tuple}},
+ {{bad_tuples,self_referential,1},
+ {{put,{x,1}},7,{tuple_in_progress,{x,1}}}},
+ {{bad_tuples,short,1},
+ {{move,{x,1},{x,0}},7,{tuple_in_progress,{x,1}}}}] = Errors,
+
+ ok.
+
+bad_try_catch_nesting(Config) ->
+ Errors = do_val(bad_try_catch_nesting, Config),
+ [{{bad_try_catch_nesting,main,2},
+ {{'try',{y,2},{f,3}},
+ 7,
+ {bad_try_catch_nesting,{y,2},[{{y,1},{trytag,[5]}}]}}}] = Errors,
+ ok.
+
%%%-------------------------------------------------------------------------
transform_remove(Remove, Module) ->
diff --git a/lib/compiler/test/beam_validator_SUITE_data/bad_try_catch_nesting.S b/lib/compiler/test/beam_validator_SUITE_data/bad_try_catch_nesting.S
new file mode 100644
index 0000000000..9f1b21a17b
--- /dev/null
+++ b/lib/compiler/test/beam_validator_SUITE_data/bad_try_catch_nesting.S
@@ -0,0 +1,64 @@
+{module, bad_try_catch_nesting}. %% version = 0
+
+{exports, [{main,2},{module_info,0},{module_info,1}]}.
+
+{attributes, []}.
+
+{labels, 11}.
+
+
+{function, main, 2, 2}.
+ {label,1}.
+ {line,[{location,"bad_try_catch_nesting.erl",4}]}.
+ {func_info,{atom,bad_try_catch_nesting},{atom,main},2}.
+ {label,2}.
+ {allocate_zero,3,2}.
+ {'try',{y,1},{f,5}}.
+ {move,{x,1},{y,0}}.
+ {'try',{y,2},{f,3}}.
+ {line,[{location,"bad_try_catch_nesting.erl",7}]}.
+ {call_fun,0}.
+ {try_end,{y,2}}.
+ {jump,{f,4}}.
+ {label,3}.
+ {try_case,{y,2}}.
+ {test,is_ne_exact,{f,4},[{x,0},{atom,error}]}.
+ {line,[]}.
+ {bif,raise,{f,0},[{x,2},{x,1}],{x,0}}.
+ {label,4}.
+ {move,{y,0},{x,0}}.
+ {kill,{y,0}}.
+ {line,[{location,"bad_try_catch_nesting.erl",12}]}.
+ {call_fun,0}.
+ {try_end,{y,1}}.
+ {deallocate,3}.
+ return.
+ {label,5}.
+ {try_case,{y,1}}.
+ {test,is_eq_exact,{f,6},[{x,0},{atom,throw}]}.
+ {deallocate,3}.
+ return.
+ {label,6}.
+ {line,[]}.
+ {bif,raise,{f,0},[{x,2},{x,1}],{x,0}}.
+
+
+{function, module_info, 0, 8}.
+ {label,7}.
+ {line,[]}.
+ {func_info,{atom,bad_try_catch_nesting},{atom,module_info},0}.
+ {label,8}.
+ {move,{atom,bad_try_catch_nesting},{x,0}}.
+ {line,[]}.
+ {call_ext_only,1,{extfunc,erlang,get_module_info,1}}.
+
+
+{function, module_info, 1, 10}.
+ {label,9}.
+ {line,[]}.
+ {func_info,{atom,bad_try_catch_nesting},{atom,module_info},1}.
+ {label,10}.
+ {move,{x,0},{x,1}}.
+ {move,{atom,bad_try_catch_nesting},{x,0}}.
+ {line,[]}.
+ {call_ext_only,2,{extfunc,erlang,get_module_info,2}}.
diff --git a/lib/compiler/test/beam_validator_SUITE_data/bad_tuples.S b/lib/compiler/test/beam_validator_SUITE_data/bad_tuples.S
new file mode 100644
index 0000000000..7980241c37
--- /dev/null
+++ b/lib/compiler/test/beam_validator_SUITE_data/bad_tuples.S
@@ -0,0 +1,88 @@
+{module, bad_tuples}. %% version = 0
+
+{exports, [{heap_overflow,1},
+ {long,2},
+ {module_info,0},
+ {module_info,1},
+ {self_referential,1},
+ {short,1}]}.
+
+{attributes, []}.
+
+{labels, 13}.
+
+
+{function, short, 1, 2}.
+ {label,1}.
+ {line,[{location,"bad_tuples.erl",4}]}.
+ {func_info,{atom,bad_tuples},{atom,short},1}.
+ {label,2}.
+ {test_heap,3,1}.
+ {put_tuple,2,{x,1}}.
+ {put,{atom,ok}}.
+ {move,{x,1},{x,0}}.
+ return.
+
+
+{function, long, 2, 4}.
+ {label,3}.
+ {line,[{location,"bad_tuples.erl",7}]}.
+ {func_info,{atom,bad_tuples},{atom,long},2}.
+ {label,4}.
+ {test_heap,6,2}.
+ {put_tuple,2,{x,2}}.
+ {put,{x,0}}.
+ {put,{x,1}}.
+ {put,{atom,too_long}}.
+ {put_tuple,2,{x,0}}.
+ {put,{atom,ok}}.
+ {put,{x,2}}.
+ return.
+
+
+{function, heap_overflow, 1, 6}.
+ {label,5}.
+ {line,[{location,"bad_tuples.erl",10}]}.
+ {func_info,{atom,bad_tuples},{atom,heap_overflow},1}.
+ {label,6}.
+ {test_heap,3,1}.
+ {put_tuple,2,{x,1}}.
+ {put,{atom,ok}}.
+ {put,{x,0}}.
+ {put,{x,0}}.
+ {move,{x,1},{x,0}}.
+ return.
+
+
+{function, self_referential, 1, 8}.
+ {label,7}.
+ {line,[{location,"bad_tuples.erl",13}]}.
+ {func_info,{atom,bad_tuples},{atom,self_referential},1}.
+ {label,8}.
+ {test_heap,3,1}.
+ {put_tuple,2,{x,1}}.
+ {put,{atom,ok}}.
+ {put,{x,1}}.
+ {move,{x,1},{x,0}}.
+ return.
+
+
+{function, module_info, 0, 10}.
+ {label,9}.
+ {line,[]}.
+ {func_info,{atom,bad_tuples},{atom,module_info},0}.
+ {label,10}.
+ {move,{atom,bad_tuples},{x,0}}.
+ {line,[]}.
+ {call_ext_only,1,{extfunc,erlang,get_module_info,1}}.
+
+
+{function, module_info, 1, 12}.
+ {label,11}.
+ {line,[]}.
+ {func_info,{atom,bad_tuples},{atom,module_info},1}.
+ {label,12}.
+ {move,{x,0},{x,1}}.
+ {move,{atom,bad_tuples},{x,0}}.
+ {line,[]}.
+ {call_ext_only,2,{extfunc,erlang,get_module_info,2}}.
diff --git a/lib/compiler/test/bs_construct_SUITE.erl b/lib/compiler/test/bs_construct_SUITE.erl
index da99aba346..7c5ad97f7e 100644
--- a/lib/compiler/test/bs_construct_SUITE.erl
+++ b/lib/compiler/test/bs_construct_SUITE.erl
@@ -303,7 +303,14 @@ fail(Config) when is_list(Config) ->
{'EXIT',{badarg,_}} = (catch <<42.0/integer>>),
{'EXIT',{badarg,_}} = (catch <<42/binary>>),
{'EXIT',{badarg,_}} = (catch <<an_atom/integer>>),
-
+
+ %% Bad literal sizes
+ Bin = i(<<>>),
+ {'EXIT',{badarg,_}} = (catch <<0:(-1)>>),
+ {'EXIT',{badarg,_}} = (catch <<Bin/binary,0:(-1)>>),
+ {'EXIT',{badarg,_}} = (catch <<0:(-(1 bsl 100))>>),
+ {'EXIT',{badarg,_}} = (catch <<Bin/binary,0:(-(1 bsl 100))>>),
+
ok.
float_bin(Config) when is_list(Config) ->
diff --git a/lib/compiler/test/bs_match_SUITE.erl b/lib/compiler/test/bs_match_SUITE.erl
index 4bd5e8e2e1..235956a714 100644
--- a/lib/compiler/test/bs_match_SUITE.erl
+++ b/lib/compiler/test/bs_match_SUITE.erl
@@ -678,6 +678,10 @@ coverage(Config) when is_list(Config) ->
<<>> = coverage_per_key(<<4:32>>),
<<$a,$b,$c>> = coverage_per_key(<<7:32,"abc">>),
+ binary = coverage_bitstring(<<>>),
+ binary = coverage_bitstring(<<7>>),
+ bitstring = coverage_bitstring(<<7:4>>),
+ other = coverage_bitstring([a]),
ok.
coverage_fold(Fun, Acc, <<H,T/binary>>) ->
@@ -768,6 +772,10 @@ coverage_per_key(<<BinSize:32,Bin/binary>> = B) ->
true = (byte_size(B) =:= BinSize),
Bin.
+coverage_bitstring(Bin) when is_binary(Bin) -> binary;
+coverage_bitstring(<<_/bitstring>>) -> bitstring;
+coverage_bitstring(_) -> other.
+
multiple_uses(Config) when is_list(Config) ->
{344,62879,345,<<245,159,1,89>>} = multiple_uses_1(<<1,88,245,159,1,89>>),
true = multiple_uses_2(<<0,0,197,18>>),
diff --git a/lib/compiler/test/compile_SUITE.erl b/lib/compiler/test/compile_SUITE.erl
index 35c11d894d..eee5bc733f 100644
--- a/lib/compiler/test/compile_SUITE.erl
+++ b/lib/compiler/test/compile_SUITE.erl
@@ -1455,19 +1455,21 @@ env_compiler_options(_Config) ->
bc_options(Config) ->
DataDir = proplists:get_value(data_dir, Config),
- 101 = highest_opcode(DataDir, small_float, [no_line_info]),
+ 101 = highest_opcode(DataDir, small_float, [no_get_hd_tl,no_line_info]),
103 = highest_opcode(DataDir, big,
- [no_record_opt,no_line_info,no_stack_trimming]),
+ [no_get_hd_tl,no_record_opt,
+ no_line_info,no_stack_trimming]),
- 125 = highest_opcode(DataDir, small_float, [no_line_info,no_float_opt]),
+ 125 = highest_opcode(DataDir, small_float,
+ [no_get_hd_tl,no_line_info,no_float_opt]),
132 = highest_opcode(DataDir, small,
- [no_record_opt,no_float_opt,no_line_info]),
+ [no_get_hd_tl,no_record_opt,no_float_opt,no_line_info]),
- 136 = highest_opcode(DataDir, big, [no_record_opt,no_line_info]),
+ 136 = highest_opcode(DataDir, big, [no_get_hd_tl,no_record_opt,no_line_info]),
- 153 = highest_opcode(DataDir, big, [no_record_opt]),
+ 153 = highest_opcode(DataDir, big, [no_get_hd_tl,no_record_opt]),
153 = highest_opcode(DataDir, big, [r16]),
153 = highest_opcode(DataDir, big, [r17]),
153 = highest_opcode(DataDir, big, [r18]),
@@ -1478,9 +1480,10 @@ bc_options(Config) ->
158 = highest_opcode(DataDir, small_maps, [r17]),
158 = highest_opcode(DataDir, small_maps, [r18]),
158 = highest_opcode(DataDir, small_maps, [r19]),
+ 158 = highest_opcode(DataDir, small_maps, [r20]),
158 = highest_opcode(DataDir, small_maps, []),
- 159 = highest_opcode(DataDir, big, []),
+ 163 = highest_opcode(DataDir, big, []),
ok.
diff --git a/lib/compiler/test/compile_SUITE_data/big.erl b/lib/compiler/test/compile_SUITE_data/big.erl
index 2e54ee8660..1db07755a1 100644
--- a/lib/compiler/test/compile_SUITE_data/big.erl
+++ b/lib/compiler/test/compile_SUITE_data/big.erl
@@ -741,3 +741,7 @@ snmp_access(suite) ->
debug_support(suite) ->
[ info, schema, schema, kill, lkill ].
+%% Cover translation of get_hd/2 to get_list/3 when option no_get_hd_tl
+%% is given.
+cover_get_hd([Hd|_]) ->
+ Hd.
diff --git a/lib/compiler/test/lc_SUITE.erl b/lib/compiler/test/lc_SUITE.erl
index 9ad417b09b..699081470d 100644
--- a/lib/compiler/test/lc_SUITE.erl
+++ b/lib/compiler/test/lc_SUITE.erl
@@ -107,6 +107,31 @@ basic(Config) when is_list(Config) ->
[] = [X || X <- L1, X+1 < 2],
{'EXIT',_} = (catch [X || X <- L1, odd(X)]),
fc([x], catch [E || E <- id(x)]),
+
+ %% Make sure that line numbers point out the generator.
+ case ?MODULE of
+ lc_inline_SUITE ->
+ ok;
+ _ ->
+ {'EXIT',{function_clause,
+ [{?MODULE,_,_,
+ [{file,"bad_lc.erl"},{line,4}]}|_]}} =
+ (catch bad_generator(a)),
+ {'EXIT',{function_clause,
+ [{?MODULE,_,_,
+ [{file,"bad_lc.erl"},{line,4}]}|_]}} =
+ (catch bad_generator([a|b])),
+ {'EXIT',{badarg,
+ [{erlang,length,_,_},
+ {?MODULE,bad_generator_bc,1,
+ [{file,"bad_lc.erl"},{line,7}]}|_]}} =
+ (catch bad_generator_bc(a)),
+ {'EXIT',{badarg,
+ [{erlang,length,_,_},
+ {?MODULE,bad_generator_bc,1,
+ [{file,"bad_lc.erl"},{line,7}]}|_]}} =
+ (catch bad_generator_bc([a|b]))
+ end,
ok.
tuple_list() ->
@@ -249,3 +274,11 @@ fc(Args, {'EXIT',{function_clause,[{?MODULE,_,Arity,_}|_]}})
fc(Args, {'EXIT',{{case_clause,ActualArgs},_}})
when ?MODULE =:= lc_inline_SUITE ->
Args = tuple_to_list(ActualArgs).
+
+-file("bad_lc.erl", 1).
+bad_generator(List) -> %Line 2
+ [I || %Line 3
+ I <- List]. %Line 4
+bad_generator_bc(List) -> %Line 5
+ << <<I:4>> || %Line 6
+ I <- List>>. %Line 7
diff --git a/lib/compiler/test/misc_SUITE.erl b/lib/compiler/test/misc_SUITE.erl
index d93c5dda1e..4e39f4663e 100644
--- a/lib/compiler/test/misc_SUITE.erl
+++ b/lib/compiler/test/misc_SUITE.erl
@@ -359,9 +359,7 @@ integer_encoding_1(Config) ->
io:put_chars(Src, "t(Last) ->[\n"),
io:put_chars(Data, "[\n"),
- do_integer_encoding(-(id(1) bsl 10000), Src, Data),
- do_integer_encoding(id(1) bsl 10000, Src, Data),
- do_integer_encoding(1024, 0, Src, Data),
+ do_integer_encoding(137, 0, Src, Data),
_ = [begin
B = 1 bsl I,
do_integer_encoding(-B-1, Src, Data),
@@ -370,7 +368,7 @@ integer_encoding_1(Config) ->
do_integer_encoding(B-1, Src, Data),
do_integer_encoding(B, Src, Data),
do_integer_encoding(B+1, Src, Data)
- end || I <- lists:seq(1, 128)],
+ end || I <- lists:seq(1, 130)],
io:put_chars(Src, "Last].\n\n"),
ok = file:close(Src),
io:put_chars(Data, "0].\n\n"),
@@ -384,8 +382,6 @@ integer_encoding_1(Config) ->
%% Compare lists.
List = Mod:t(0),
{ok,[List]} = file:consult(DataFile),
- OneBsl10000 = id(1) bsl 10000,
- [-(1 bsl 10000),OneBsl10000|_] = List,
%% Cleanup.
file:delete(SrcFile),
@@ -404,7 +400,3 @@ do_integer_encoding(I, Src, Data) ->
Str = integer_to_list(I),
io:put_chars(Src, [Str,",\n"]),
io:put_chars(Data, [Str,",\n"]).
-
-
-id(I) -> I.
-
diff --git a/lib/compiler/vsn.mk b/lib/compiler/vsn.mk
index 082786c7d8..ee75ee27fd 100644
--- a/lib/compiler/vsn.mk
+++ b/lib/compiler/vsn.mk
@@ -1 +1 @@
-COMPILER_VSN = 7.1.4
+COMPILER_VSN = 7.1.5
diff --git a/lib/crypto/c_src/crypto.c b/lib/crypto/c_src/crypto.c
index 9a3ea07c97..149387bcee 100644
--- a/lib/crypto/c_src/crypto.c
+++ b/lib/crypto/c_src/crypto.c
@@ -479,8 +479,6 @@ static ERL_NIF_TERM pkey_sign_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM a
static ERL_NIF_TERM pkey_verify_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
static ERL_NIF_TERM pkey_crypt_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
static ERL_NIF_TERM rsa_generate_key_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
-static ERL_NIF_TERM dh_generate_parameters_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
-static ERL_NIF_TERM dh_check(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
static ERL_NIF_TERM dh_generate_key_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
static ERL_NIF_TERM dh_compute_key_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
static ERL_NIF_TERM privkey_to_pubkey_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
@@ -568,8 +566,6 @@ static ErlNifFunc nif_funcs[] = {
{"pkey_verify_nif", 6, pkey_verify_nif},
{"pkey_crypt_nif", 6, pkey_crypt_nif},
{"rsa_generate_key_nif", 2, rsa_generate_key_nif},
- {"dh_generate_parameters_nif", 2, dh_generate_parameters_nif},
- {"dh_check", 1, dh_check},
{"dh_generate_key_nif", 4, dh_generate_key_nif},
{"dh_compute_key_nif", 3, dh_compute_key_nif},
{"privkey_to_pubkey_nif", 2, privkey_to_pubkey_nif},
@@ -3006,70 +3002,6 @@ static ERL_NIF_TERM rsa_generate_key_nif(ErlNifEnv* env, int argc, const ERL_NIF
rsa_generate_key, argc, argv);
}
-static ERL_NIF_TERM dh_generate_parameters_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
-{/* (PrimeLen, Generator) */
- int prime_len, generator;
- DH* dh_params = NULL;
- int p_len, g_len;
- unsigned char *p_ptr, *g_ptr;
- ERL_NIF_TERM ret_p, ret_g;
- const BIGNUM *dh_p, *dh_q, *dh_g;
-
- if (!enif_get_int(env, argv[0], &prime_len)
- || !enif_get_int(env, argv[1], &generator)) {
-
- return enif_make_badarg(env);
- }
-
- if (DH_generate_parameters_ex(dh_params, prime_len, generator, NULL)) {
- return atom_error;
- }
- DH_get0_pqg(dh_params, &dh_p, &dh_q, &dh_g);
- DH_free(dh_params);
- p_len = BN_num_bytes(dh_p);
- g_len = BN_num_bytes(dh_g);
- p_ptr = enif_make_new_binary(env, p_len, &ret_p);
- g_ptr = enif_make_new_binary(env, g_len, &ret_g);
- BN_bn2bin(dh_p, p_ptr);
- BN_bn2bin(dh_g, g_ptr);
- ERL_VALGRIND_MAKE_MEM_DEFINED(p_ptr, p_len);
- ERL_VALGRIND_MAKE_MEM_DEFINED(g_ptr, g_len);
- return enif_make_list2(env, ret_p, ret_g);
-}
-
-static ERL_NIF_TERM dh_check(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
-{/* ([PrimeLen, Generator]) */
- DH* dh_params;
- int i;
- ERL_NIF_TERM ret, head, tail;
- BIGNUM *dh_p, *dh_g;
-
- if (!enif_get_list_cell(env, argv[0], &head, &tail)
- || !get_bn_from_bin(env, head, &dh_p)
- || !enif_get_list_cell(env, tail, &head, &tail)
- || !get_bn_from_bin(env, head, &dh_g)
- || !enif_is_empty_list(env,tail)) {
-
- return enif_make_badarg(env);
- }
-
- dh_params = DH_new();
- DH_set0_pqg(dh_params, dh_p, NULL, dh_g);
- if (DH_check(dh_params, &i)) {
- if (i == 0) ret = atom_ok;
- else if (i & DH_CHECK_P_NOT_PRIME) ret = atom_not_prime;
- else if (i & DH_CHECK_P_NOT_SAFE_PRIME) ret = atom_not_strong_prime;
- else if (i & DH_UNABLE_TO_CHECK_GENERATOR) ret = atom_unable_to_check_generator;
- else if (i & DH_NOT_SUITABLE_GENERATOR) ret = atom_not_suitable_generator;
- else ret = enif_make_tuple2(env, atom_unknown, enif_make_uint(env, i));
- }
- else { /* Check Failed */
- ret = enif_make_tuple2(env, atom_error, atom_check_failed);
- }
- DH_free(dh_params);
- return ret;
-}
-
static ERL_NIF_TERM dh_generate_key_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{/* (PrivKey|undefined, DHParams=[P,G], Mpint, Len|0) */
DH* dh_params;
diff --git a/lib/crypto/doc/src/notes.xml b/lib/crypto/doc/src/notes.xml
index dbeb886d7b..1f788a4e35 100644
--- a/lib/crypto/doc/src/notes.xml
+++ b/lib/crypto/doc/src/notes.xml
@@ -31,6 +31,22 @@
</header>
<p>This document describes the changes made to the Crypto application.</p>
+<section><title>Crypto 4.2.1</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Fix build error caused by removed RSA padding functions
+ in LibreSSL >= 2.6.1</p>
+ <p>
+ Own Id: OTP-14873</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Crypto 4.2</title>
<section><title>Fixed Bugs and Malfunctions</title>
diff --git a/lib/crypto/src/crypto.erl b/lib/crypto/src/crypto.erl
index df259d5419..46775989ae 100644
--- a/lib/crypto/src/crypto.erl
+++ b/lib/crypto/src/crypto.erl
@@ -42,7 +42,6 @@
-export([stream_init/2, stream_init/3, stream_encrypt/2, stream_decrypt/2]).
-export([public_encrypt/4, private_decrypt/4]).
-export([private_encrypt/4, public_decrypt/4]).
--export([dh_generate_parameters/2, dh_check/1]). %% Testing see
-export([privkey_to_pubkey/2]).
-export([ec_curve/1, ec_curves/0]).
-export([rand_seed/1]).
@@ -848,8 +847,13 @@ on_load() ->
case Status of
ok -> ok;
{error, {E, Str}} ->
- error_logger:error_msg("Unable to load crypto library. Failed with error:~n\"~p, ~s\"~n"
- "OpenSSL might not be installed on this system.~n",[E,Str]),
+ Fmt = "Unable to load crypto library. Failed with error:~n\"~p, ~s\"~n~s",
+ Extra = case E of
+ load_failed ->
+ "OpenSSL might not be installed on this system.\n";
+ _ -> ""
+ end,
+ error_logger:error_msg(Fmt, [E,Str,Extra]),
Status
end.
@@ -1090,27 +1094,6 @@ rsa_generate_key_nif(_Bits, _Exp) -> ?nif_stub.
%% DH Diffie-Hellman functions
%%
-%% Generate (and check) Parameters is not documented because they are implemented
-%% for testing (and offline parameter generation) only.
-%% From the openssl doc:
-%% DH_generate_parameters() may run for several hours before finding a suitable prime.
-%% Thus dh_generate_parameters may in this implementation block
-%% the emulator for several hours.
-%%
-%% usage: dh_generate_parameters(1024, 2 or 5) ->
-%% [Prime=mpint(), SharedGenerator=mpint()]
-dh_generate_parameters(PrimeLen, Generator) ->
- case dh_generate_parameters_nif(PrimeLen, Generator) of
- error -> erlang:error(generation_failed, [PrimeLen,Generator]);
- Ret -> Ret
- end.
-
-dh_generate_parameters_nif(_PrimeLen, _Generator) -> ?nif_stub.
-
-%% Checks that the DHParameters are ok.
-%% DHParameters = [P (Prime)= mpint(), G(Generator) = mpint()]
-dh_check([_Prime,_Gen]) -> ?nif_stub.
-
%% DHParameters = [P (Prime)= mpint(), G(Generator) = mpint()]
%% PrivKey = mpint()
dh_generate_key_nif(_PrivateKey, _DHParameters, _Mpint, _Length) -> ?nif_stub.
diff --git a/lib/crypto/test/engine_SUITE.erl b/lib/crypto/test/engine_SUITE.erl
index f206f967c7..f410542f72 100644
--- a/lib/crypto/test/engine_SUITE.erl
+++ b/lib/crypto/test/engine_SUITE.erl
@@ -72,7 +72,12 @@ groups() ->
init_per_suite(Config) ->
try crypto:start() of
ok ->
- Config;
+ case crypto:info_lib() of
+ [{_,_, <<"OpenSSL 1.0.1s-freebsd 1 Mar 2016">>}] ->
+ {skip, "Problem with engine on OpenSSL 1.0.1s-freebsd"};
+ _ ->
+ Config
+ end;
{error,{already_started,crypto}} ->
Config
catch _:_ ->
diff --git a/lib/crypto/vsn.mk b/lib/crypto/vsn.mk
index da3915a4fc..3432f00836 100644
--- a/lib/crypto/vsn.mk
+++ b/lib/crypto/vsn.mk
@@ -1 +1 @@
-CRYPTO_VSN = 4.2
+CRYPTO_VSN = 4.2.1
diff --git a/lib/debugger/src/dbg_debugged.erl b/lib/debugger/src/dbg_debugged.erl
index e142af4ae0..8b64a60a9c 100644
--- a/lib/debugger/src/dbg_debugged.erl
+++ b/lib/debugger/src/dbg_debugged.erl
@@ -31,32 +31,25 @@
%% Called via the error handler.
%%--------------------------------------------------------------------
eval(Mod, Func, Args) ->
- SaveStacktrace = erlang:get_stacktrace(),
Meta = dbg_ieval:eval(Mod, Func, Args),
Mref = erlang:monitor(process, Meta),
- msg_loop(Meta, Mref, SaveStacktrace).
+ msg_loop(Meta, Mref).
%%====================================================================
%% Internal functions
%%====================================================================
-msg_loop(Meta, Mref, SaveStacktrace) ->
+msg_loop(Meta, Mref) ->
receive
%% Evaluated function has returned a value
{sys, Meta, {ready, Val}} ->
erlang:demonitor(Mref, [flush]),
-
- %% Restore original stacktrace and return the value
- try erlang:raise(throw, stack, SaveStacktrace)
- catch
- throw:stack ->
- case Val of
- {dbg_apply,M,F,A} ->
- apply(M, F, A);
- _ ->
- Val
- end
+ case Val of
+ {dbg_apply,M,F,A} ->
+ apply(M, F, A);
+ _ ->
+ Val
end;
%% Evaluated function raised an (uncaught) exception
@@ -74,32 +67,25 @@ msg_loop(Meta, Mref, SaveStacktrace) ->
Meta ! {self(), rec_acked},
ok
end,
- msg_loop(Meta, Mref, SaveStacktrace);
+ msg_loop(Meta, Mref);
%% Meta needs something evaluated within context of real process
{sys, Meta, {command,Command}} ->
Reply = handle_command(Command),
Meta ! {sys, self(), Reply},
- msg_loop(Meta, Mref, SaveStacktrace);
+ msg_loop(Meta, Mref);
%% Meta has terminated
%% Must be due to int:stop() (or -heaven forbid- a debugger bug)
{'DOWN', Mref, _, _, Reason} ->
-
- %% Restore original stacktrace and return a dummy value
- try erlang:raise(throw, stack, SaveStacktrace)
- catch
- throw:stack ->
- {interpreter_terminated, Reason}
- end
+ {interpreter_terminated, Reason}
end.
handle_command(Command) ->
try
reply(Command)
- catch Class:Reason ->
- Stacktrace = stacktrace_f(erlang:get_stacktrace()),
- {exception,{Class,Reason,Stacktrace}}
+ catch Class:Reason:Stacktrace ->
+ {exception,{Class,Reason,stacktrace_f(Stacktrace)}}
end.
reply({apply,M,F,As}) ->
diff --git a/lib/debugger/src/dbg_ieval.erl b/lib/debugger/src/dbg_ieval.erl
index 8009d62629..9840cebc1a 100644
--- a/lib/debugger/src/dbg_ieval.erl
+++ b/lib/debugger/src/dbg_ieval.erl
@@ -924,8 +924,7 @@ expr({dbg,Line,raise,As0}, Bs0, #ieval{level=Le}=Ieval0) ->
trace(return, {Le,Error}),
{value,Error,Bs}
catch
- _:_ ->
- Stk = erlang:get_stacktrace(), %Possibly truncated.
+ _:_:Stk -> %Possibly truncated.
StkFun = fun(_) -> Stk end,
do_exception(Class, Reason, StkFun, Bs, Ieval)
end;
@@ -1034,7 +1033,7 @@ expr({send,Line,To0,Msg0}, Bs0, Ieval0) ->
%% Binary
expr({bin,Line,Fs}, Bs0, Ieval0) ->
- Ieval = Ieval0#ieval{line=Line},
+ Ieval = Ieval0#ieval{line=Line,top=false},
try
eval_bits:expr_grp(Fs, Bs0,
fun (E, B) -> expr(E, B, Ieval) end,
diff --git a/lib/debugger/src/dbg_wx_mon.erl b/lib/debugger/src/dbg_wx_mon.erl
index a32a6894b8..00aee62a87 100644
--- a/lib/debugger/src/dbg_wx_mon.erl
+++ b/lib/debugger/src/dbg_wx_mon.erl
@@ -119,9 +119,9 @@ init(CallingPid, Mode, SFile) ->
init2(CallingPid, Mode, SFile, GS)
catch
exit:stop -> stop;
- Error:Reason ->
+ Error:Reason:Stacktrace ->
io:format("~p: Crashed {~p,~p} in~n ~p",
- [?MODULE, Error, Reason, erlang:get_stacktrace()])
+ [?MODULE, Error, Reason, Stacktrace])
end
end.
diff --git a/lib/debugger/src/dbg_wx_trace.erl b/lib/debugger/src/dbg_wx_trace.erl
index b1e0e03b4c..505d53005f 100644
--- a/lib/debugger/src/dbg_wx_trace.erl
+++ b/lib/debugger/src/dbg_wx_trace.erl
@@ -95,9 +95,9 @@ start(Pid, Env, Parent, TraceWin, BackTrace, Strings) ->
catch
_:stop ->
exit(stop);
- E:R ->
+ E:R:S ->
io:format("TraceWin Crashed ~p~n",[E]),
- io:format(" ~p in ~p~n",[R, erlang:get_stacktrace()]),
+ io:format(" ~p in ~p~n",[R, S]),
exit(R)
end;
error ->
diff --git a/lib/debugger/test/int_eval_SUITE.erl b/lib/debugger/test/int_eval_SUITE.erl
index 27ca4852b5..da1d6734f8 100644
--- a/lib/debugger/test/int_eval_SUITE.erl
+++ b/lib/debugger/test/int_eval_SUITE.erl
@@ -29,7 +29,8 @@
bifs_outside_erlang/1, spawning/1, applying/1,
catch_and_throw/1, external_call/1, test_module_info/1,
apply_interpreted_fun/1, apply_uninterpreted_fun/1,
- interpreted_exit/1, otp_8310/1, stacktrace/1, maps/1]).
+ interpreted_exit/1, otp_8310/1, stacktrace/1, maps/1,
+ call_inside_binary/1]).
%% Helpers.
-export([applier/3]).
@@ -45,7 +46,8 @@ all() ->
[bifs_outside_erlang, spawning, applying,
catch_and_throw, external_call, test_module_info,
apply_interpreted_fun, apply_uninterpreted_fun,
- interpreted_exit, otp_8310, stacktrace, maps].
+ interpreted_exit, otp_8310, stacktrace, maps,
+ call_inside_binary].
groups() ->
[].
@@ -275,6 +277,9 @@ maps(Config) when is_list(Config) ->
[#{hello := 0, price := 0}] = spawn_eval(fun () -> ?IM:update_in_fun() end),
ok.
+call_inside_binary(Config) when is_list(Config) ->
+ <<"1">> = ?IM:call_inside_binary(fun erlang:integer_to_binary/1),
+ ok.
do_eval(Config, Mod) ->
DataDir = proplists:get_value(data_dir, Config),
diff --git a/lib/debugger/test/int_eval_SUITE_data/my_int_eval_module.erl b/lib/debugger/test/int_eval_SUITE_data/my_int_eval_module.erl
index ca7929c10b..384d61f051 100644
--- a/lib/debugger/test/int_eval_SUITE_data/my_int_eval_module.erl
+++ b/lib/debugger/test/int_eval_SUITE_data/my_int_eval_module.erl
@@ -31,6 +31,7 @@
-export([f/1, f_try/1, f_catch/1]).
-export([otp_5837/1, otp_8310/0]).
-export([empty_map_update/1, update_in_fun/0]).
+-export([call_inside_binary/1]).
%% Internal exports.
-export([echo/2,my_subtract/2,catch_a_ball/0,throw_a_ball/0]).
@@ -248,3 +249,6 @@ empty_map_update(Map) -> Map#{}.
update_in_fun() ->
lists:map(fun (X) -> X#{price := 0} end, [#{hello => 0, price => nil}]).
+
+call_inside_binary(Fun) ->
+ <<(Fun(1))/binary>>.
diff --git a/lib/dialyzer/doc/src/notes.xml b/lib/dialyzer/doc/src/notes.xml
index a1eecfb3fe..8d11252bff 100644
--- a/lib/dialyzer/doc/src/notes.xml
+++ b/lib/dialyzer/doc/src/notes.xml
@@ -32,6 +32,32 @@
<p>This document describes the changes made to the Dialyzer
application.</p>
+<section><title>Dialyzer 3.2.4</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p> Fix bugs concerning <c>erlang:abs/1</c> and
+ <c>erlang:bsl/2</c>. </p>
+ <p>
+ Own Id: OTP-14858 Aux Id: ERL-551 </p>
+ </item>
+ <item>
+ <p> Fix a bug that caused Dialyzer to crash instead of
+ emitting a warning. </p>
+ <p>
+ Own Id: OTP-14911</p>
+ </item>
+ <item>
+ <p> Fix a bug concerning parameterized opaque types. </p>
+ <p>
+ Own Id: OTP-14925 Aux Id: ERL-565 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Dialyzer 3.2.3</title>
<section><title>Fixed Bugs and Malfunctions</title>
diff --git a/lib/dialyzer/src/dialyzer_cl_parse.erl b/lib/dialyzer/src/dialyzer_cl_parse.erl
index 80c10183cf..f21eaed087 100644
--- a/lib/dialyzer/src/dialyzer_cl_parse.erl
+++ b/lib/dialyzer/src/dialyzer_cl_parse.erl
@@ -41,8 +41,8 @@ start() ->
Ret
catch
throw:{dialyzer_cl_parse_error, Msg} -> {error, Msg};
- _:R ->
- Msg = io_lib:format("~tp\n~tp\n", [R, erlang:get_stacktrace()]),
+ _:R:S ->
+ Msg = io_lib:format("~tp\n~tp\n", [R, S]),
{error, lists:flatten(Msg)}
end.
diff --git a/lib/dialyzer/src/dialyzer_contracts.erl b/lib/dialyzer/src/dialyzer_contracts.erl
index e72c1aecfc..0df15e55f9 100644
--- a/lib/dialyzer/src/dialyzer_contracts.erl
+++ b/lib/dialyzer/src/dialyzer_contracts.erl
@@ -197,6 +197,10 @@ check_contracts(Contracts, Callgraph, FunTypes, ModOpaques) ->
false ->
[{MFA, Contract}|NewContracts]
end;
+ {error, {extra_range, _, _}} ->
+ %% do not treat extra range as an error in this check
+ %% since that prevents discovering other actual errors
+ [{MFA, Contract}|NewContracts];
{error, _Error} -> NewContracts
end;
error -> NewContracts
diff --git a/lib/dialyzer/src/dialyzer_dataflow.erl b/lib/dialyzer/src/dialyzer_dataflow.erl
index 384912f983..c5f93a3392 100644
--- a/lib/dialyzer/src/dialyzer_dataflow.erl
+++ b/lib/dialyzer/src/dialyzer_dataflow.erl
@@ -299,7 +299,7 @@ traverse(Tree, Map, State) ->
match_fail -> t_none();
raise -> t_none();
bs_init_writable -> t_from_term(<<>>);
- build_stacktrace -> t_list();
+ build_stacktrace -> erl_bif_types:type(erlang, build_stacktrace, 0);
Other -> erlang:error({'Unsupported primop', Other})
end,
{State, Map, Type};
@@ -1236,6 +1236,13 @@ handle_tuple(Tree, Map, State) ->
State2 = state__add_warning(State1, ?WARN_OPAQUE,
Tree, Msg),
{State2, Map1, t_none()};
+ {error, record, ErrorPat, ErrorType, _} ->
+ Msg = {record_match,
+ [format_patterns(ErrorPat),
+ format_type(ErrorType, State1)]},
+ State2 = state__add_warning(State1, ?WARN_MATCHING,
+ Tree, Msg),
+ {State2, Map1, t_none()};
{Map2, ETypes} ->
{State1, Map2, t_tuple(ETypes)}
end
@@ -3437,19 +3444,19 @@ state__fun_info(Fun, #state{callgraph = CG, fun_tab = FunTab, plt = PLT}) ->
{Fun, Sig, Contract, LocalRet}.
forward_args(Fun, ArgTypes, #state{work = Work, fun_tab = FunTab} = State) ->
- {OldArgTypes, OldOut, Fixpoint} =
+ {NewArgTypes, OldOut, Fixpoint} =
case dict:find(Fun, FunTab) of
- {ok, {not_handled, {OldArgTypes0, OldOut0}}} ->
- {OldArgTypes0, OldOut0, false};
+ {ok, {not_handled, {_OldArgTypesAreNone, OldOut0}}} ->
+ {ArgTypes, OldOut0, false};
{ok, {OldArgTypes0, OldOut0}} ->
- {OldArgTypes0, OldOut0,
- t_is_subtype(t_product(ArgTypes), t_product(OldArgTypes0))}
+ NewArgTypes0 = [t_sup(X, Y) ||
+ {X, Y} <- lists:zip(ArgTypes, OldArgTypes0)],
+ {NewArgTypes0, OldOut0,
+ t_is_equal(t_product(NewArgTypes0), t_product(OldArgTypes0))}
end,
case Fixpoint of
true -> State;
false ->
- NewArgTypes = [t_sup(X, Y) ||
- {X, Y} <- lists:zip(ArgTypes, OldArgTypes)],
NewWork = add_work(Fun, Work),
?debug("~tw: forwarding args ~ts\n",
[state__lookup_name(Fun, State),
diff --git a/lib/dialyzer/src/dialyzer_plt.erl b/lib/dialyzer/src/dialyzer_plt.erl
index 95c8b5ebce..2af4534396 100644
--- a/lib/dialyzer/src/dialyzer_plt.erl
+++ b/lib/dialyzer/src/dialyzer_plt.erl
@@ -531,17 +531,19 @@ compute_md5_from_files(Files) ->
lists:keysort(1, [{F, compute_md5_from_file(F)} || F <- Files]).
compute_md5_from_file(File) ->
- case filelib:is_regular(File) of
- false ->
+ case beam_lib:all_chunks(File) of
+ {ok, _, Chunks} ->
+ %% We cannot use beam_lib:md5 because it does not consider
+ %% the debug_info chunk, where typespecs are likely stored.
+ %% So we consider almost all chunks except the useless ones.
+ Filtered = [[ID, Chunk] || {ID, Chunk} <- Chunks, ID =/= "CInf", ID =/= "Docs"],
+ erlang:md5(lists:sort(Filtered));
+ {error, beam_lib, {file_error, _, enoent}} ->
Msg = io_lib:format("Not a regular file: ~ts\n", [File]),
throw({dialyzer_error, Msg});
- true ->
- case dialyzer_utils:get_core_from_beam(File) of
- {error, Error} ->
- throw({dialyzer_error, Error});
- {ok, Core} ->
- erlang:md5(term_to_binary(Core))
- end
+ {error, beam_lib, _} ->
+ Msg = io_lib:format("Could not compute MD5 for .beam: ~ts\n", [File]),
+ throw({dialyzer_error, Msg})
end.
init_diff_list(RemoveFiles, AddFiles) ->
diff --git a/lib/dialyzer/src/dialyzer_typesig.erl b/lib/dialyzer/src/dialyzer_typesig.erl
index b1f6a54503..dede475f98 100644
--- a/lib/dialyzer/src/dialyzer_typesig.erl
+++ b/lib/dialyzer/src/dialyzer_typesig.erl
@@ -418,7 +418,11 @@ traverse(Tree, DefinedVars, State) ->
match_fail -> throw(error);
raise -> throw(error);
bs_init_writable -> {State, t_from_term(<<>>)};
- build_stacktrace -> {State, t_list()};
+ build_stacktrace ->
+ V = mk_var(Tree),
+ Type = erl_bif_types:type(erlang, build_stacktrace, 0),
+ State1 = state__store_conj(V, sub, Type, State),
+ {State1, V};
Other -> erlang:error({'Unsupported primop', Other})
end;
'receive' ->
@@ -1896,9 +1900,8 @@ solver(Solver, SolveFun) ->
?debug("Solver ~w returned unexpected result:\n ~P\n",
[Solver, _R, 60]),
throw(error)
- catch E:R ->
- io:format("Solver ~w failed: ~w:~p\n ~tp\n",
- [Solver, E, R, erlang:get_stacktrace()]),
+ catch E:R:S ->
+ io:format("Solver ~w failed: ~w:~p\n ~tp\n", [Solver, E, R, S]),
throw(error)
end.
diff --git a/lib/dialyzer/src/typer.erl b/lib/dialyzer/src/typer.erl
index 16b9c8a94a..9d3d9ce438 100644
--- a/lib/dialyzer/src/typer.erl
+++ b/lib/dialyzer/src/typer.erl
@@ -164,9 +164,9 @@ get_type_info(#analysis{callgraph = CallGraph,
CodeServer),
Analysis#analysis{callgraph = StrippedCallGraph, trust_plt = NewPlt}
catch
- error:What ->
+ error:What:Stacktrace ->
fatal_error(io_lib:format("Analysis failed with message: ~tp",
- [{What, erlang:get_stacktrace()}]));
+ [{What, Stacktrace}]));
throw:{dialyzer_succ_typing_error, Msg} ->
fatal_error(io_lib:format("Analysis failed with message: ~ts", [Msg]))
end.
diff --git a/lib/dialyzer/test/behaviour_SUITE_data/src/proper/proper_typeserver.erl b/lib/dialyzer/test/behaviour_SUITE_data/src/proper/proper_typeserver.erl
index b16075763f..12f6532c0c 100644
--- a/lib/dialyzer/test/behaviour_SUITE_data/src/proper/proper_typeserver.erl
+++ b/lib/dialyzer/test/behaviour_SUITE_data/src/proper/proper_typeserver.erl
@@ -539,7 +539,7 @@ apply_spec_test({Mod,Fun,_Arity}=MFA, {_Domain,Range}, SpecTimeout, FalsePositiv
try apply(Mod, Fun, Args) of
X -> {ok, X}
catch
- X:Y -> {X, Y}
+ X:Y:S -> {{X, Y}, S}
end,
case Result of
{ok, Z} ->
@@ -551,15 +551,15 @@ apply_spec_test({Mod,Fun,_Arity}=MFA, {_Domain,Range}, SpecTimeout, FalsePositiv
false ->
false
end;
- Exception when is_function(FalsePositiveMFAs) ->
+ {Exception, S2} when is_function(FalsePositiveMFAs) ->
case FalsePositiveMFAs(MFA, Args, Exception) of
true ->
true;
false ->
- error(Exception, erlang:get_stacktrace())
+ error(Exception, S2)
end;
- Exception ->
- error(Exception, erlang:get_stacktrace())
+ {Exception, S3} ->
+ error(Exception, S3)
end
end).
diff --git a/lib/dialyzer/test/opaque_SUITE_data/src/para_bug/same.erl b/lib/dialyzer/test/opaque_SUITE_data/src/para_bug/same.erl
new file mode 100644
index 0000000000..44149f4199
--- /dev/null
+++ b/lib/dialyzer/test/opaque_SUITE_data/src/para_bug/same.erl
@@ -0,0 +1,15 @@
+-module(same).
+
+-export([baz/1]).
+
+-record(bar, {
+ a :: same_type:st(integer()),
+ b :: same_type:st(atom())
+ }).
+
+baz(Bar) ->
+ _ = wrap_find(0, Bar#bar.a),
+ wrap_find(0, Bar#bar.b).
+
+wrap_find(K, D) ->
+ same_type:t(K, D).
diff --git a/lib/dialyzer/test/opaque_SUITE_data/src/para_bug/same_type.erl b/lib/dialyzer/test/opaque_SUITE_data/src/para_bug/same_type.erl
new file mode 100644
index 0000000000..855a5d30be
--- /dev/null
+++ b/lib/dialyzer/test/opaque_SUITE_data/src/para_bug/same_type.erl
@@ -0,0 +1,13 @@
+-module(same_type).
+
+-export([t/2]).
+
+-export_type([st/1]).
+
+%% When unopaqued all specializations of st/1 are equal.
+-opaque st(_A) :: {st, tuple()}.
+
+-spec t(_, st(_)) -> _.
+
+t(K, V) ->
+ {K, V}.
diff --git a/lib/dialyzer/test/opaque_SUITE_data/src/proper/proper_typeserver.erl b/lib/dialyzer/test/opaque_SUITE_data/src/proper/proper_typeserver.erl
index 1677b4efb8..529f9fba72 100644
--- a/lib/dialyzer/test/opaque_SUITE_data/src/proper/proper_typeserver.erl
+++ b/lib/dialyzer/test/opaque_SUITE_data/src/proper/proper_typeserver.erl
@@ -533,7 +533,7 @@ apply_spec_test({Mod,Fun,_Arity}=MFA, {_Domain,Range}, SpecTimeout, FalsePositiv
try apply(Mod,Fun,Args) of
X -> {ok, X}
catch
- X:Y -> {X, Y}
+ X:Y:S -> {{X, Y}, S}
end,
case Result of
{ok, Z} ->
@@ -545,15 +545,15 @@ apply_spec_test({Mod,Fun,_Arity}=MFA, {_Domain,Range}, SpecTimeout, FalsePositiv
false ->
false
end;
- Exception when is_function(FalsePositiveMFAs) ->
+ {Exception, S2} when is_function(FalsePositiveMFAs) ->
case FalsePositiveMFAs(MFA, Args, Exception) of
true ->
true;
false ->
- error(Exception, erlang:get_stacktrace())
+ error(Exception, S2)
end;
- Exception ->
- error(Exception, erlang:get_stacktrace())
+ {Exception, S3} ->
+ error(Exception, S3)
end
end).
diff --git a/lib/dialyzer/test/options1_SUITE_data/src/compiler/beam_validator.erl b/lib/dialyzer/test/options1_SUITE_data/src/compiler/beam_validator.erl
index 8fe43163f6..ea92613781 100644
--- a/lib/dialyzer/test/options1_SUITE_data/src/compiler/beam_validator.erl
+++ b/lib/dialyzer/test/options1_SUITE_data/src/compiler/beam_validator.erl
@@ -174,7 +174,7 @@ validate_error(Error, Name, Ar) ->
-endif.
validate_error_1(Error, Name, Ar) ->
{{'_',Name,Ar},
- {internal_error,'_',{Error,erlang:get_stacktrace()}}}.
+ {internal_error,'_',{Error,[]}}}.
-record(st, %Emulation state
{x=init_regs(0, term), %x register info.
diff --git a/lib/dialyzer/test/r9c_SUITE_data/src/mnesia/mnesia_tm.erl b/lib/dialyzer/test/r9c_SUITE_data/src/mnesia/mnesia_tm.erl
index 09e310530d..af49ceff72 100644
--- a/lib/dialyzer/test/r9c_SUITE_data/src/mnesia/mnesia_tm.erl
+++ b/lib/dialyzer/test/r9c_SUITE_data/src/mnesia/mnesia_tm.erl
@@ -2051,7 +2051,7 @@ display_pid_info(Pid) ->
Other
end,
Reds = fetch(reductions, Info),
- LM = length(fetch(messages, Info)),
+ LM = fetch(message_queue_len, Info),
pformat(io_lib:format("~p", [Pid]),
io_lib:format("~p", [Call]),
io_lib:format("~p", [Curr]), Reds, LM)
diff --git a/lib/dialyzer/test/small_SUITE_data/results/chars b/lib/dialyzer/test/small_SUITE_data/results/chars
index 2c1f8f8d17..72fbdb4528 100644
--- a/lib/dialyzer/test/small_SUITE_data/results/chars
+++ b/lib/dialyzer/test/small_SUITE_data/results/chars
@@ -1,4 +1,4 @@
-chars.erl:29: Invalid type specification for function chars:f/1. The success typing is (#{'b':=50}) -> 'ok'
-chars.erl:32: Function t1/0 has no local return
-chars.erl:32: The call chars:f(#{'b':=50}) breaks the contract (#{'a':=49,'b'=>50,'c'=>51}) -> 'ok'
+chars.erl:37: Invalid type specification for function chars:f/1. The success typing is (#{'b':=50}) -> 'ok'
+chars.erl:40: Function t1/0 has no local return
+chars.erl:40: The call chars:f(#{'b':=50}) breaks the contract (#{'a':=49,'b'=>50,'c'=>51}) -> 'ok'
diff --git a/lib/dialyzer/test/small_SUITE_data/results/extra_range b/lib/dialyzer/test/small_SUITE_data/results/extra_range
new file mode 100644
index 0000000000..ec50c95c4e
--- /dev/null
+++ b/lib/dialyzer/test/small_SUITE_data/results/extra_range
@@ -0,0 +1,4 @@
+
+extra_range.erl:29: The pattern 'ok' can never match the type 'error'
+extra_range.erl:43: The pattern 'no' can never match the type 'maybe' | 'yes'
+extra_range.erl:58: The pattern 'maybe' can never match the type 'no' | 'yes'
diff --git a/lib/dialyzer/test/small_SUITE_data/results/record_match b/lib/dialyzer/test/small_SUITE_data/results/record_match
new file mode 100644
index 0000000000..a0dd6f560a
--- /dev/null
+++ b/lib/dialyzer/test/small_SUITE_data/results/record_match
@@ -0,0 +1,3 @@
+
+record_match.erl:16: Function select/0 has no local return
+record_match.erl:17: Matching of pattern {'b_literal', 'undefined'} tagged with a record name violates the declared type of #b_local{} | #b_remote{}
diff --git a/lib/dialyzer/test/small_SUITE_data/results/stacktrace b/lib/dialyzer/test/small_SUITE_data/results/stacktrace
new file mode 100644
index 0000000000..fd60881953
--- /dev/null
+++ b/lib/dialyzer/test/small_SUITE_data/results/stacktrace
@@ -0,0 +1,5 @@
+
+stacktrace.erl:11: The pattern {'a', 'b'} can never match the type [{atom(),atom(),[any()] | byte(),[{'file',string()} | {'line',pos_integer()}]}]
+stacktrace.erl:19: The pattern ['a', 'b'] can never match the type [{atom(),atom(),[any()] | byte(),[{'file',string()} | {'line',pos_integer()}]}]
+stacktrace.erl:44: The pattern {'a', 'b'} can never match the type [{atom(),atom(),[any()] | byte(),[{'file',string()} | {'line',pos_integer()}]}]
+stacktrace.erl:53: The pattern ['a', 'b'] can never match the type [{atom(),atom(),[any()] | byte(),[{'file',string()} | {'line',pos_integer()}]}]
diff --git a/lib/dialyzer/test/small_SUITE_data/src/chars.erl b/lib/dialyzer/test/small_SUITE_data/src/chars.erl
index 1e9c8ab6b9..62b90cf54d 100644
--- a/lib/dialyzer/test/small_SUITE_data/src/chars.erl
+++ b/lib/dialyzer/test/small_SUITE_data/src/chars.erl
@@ -12,17 +12,25 @@
-spec t() -> $0-$0..$9-$0| $?.
t() ->
- c(#r{f = $z - 3}),
+ r(#r{f = $z - 3}),
+ r(#r{f = 97}),
+ c($/),
c($z - 3),
c($B).
-spec c(cs()) -> $3-$0..$9-$0.
-
-c($A + 1) -> 2;
+c($A + 1) -> $9-$0;
c(C) ->
case C of
- $z - 3 -> 3;
- #r{f = $z - 3} -> 7
+ $z - 3 -> $3-$0;
+ _ -> $7-$0
+ end.
+
+-spec r(#r{f :: $a..$z}) -> ok | error.
+r(R) ->
+ case R of
+ #r{f = $z - 3} -> error;
+ _ -> ok
end.
%% Display contract with character in warning:
diff --git a/lib/dialyzer/test/small_SUITE_data/src/extra_range.erl b/lib/dialyzer/test/small_SUITE_data/src/extra_range.erl
new file mode 100644
index 0000000000..9d6ba89c95
--- /dev/null
+++ b/lib/dialyzer/test/small_SUITE_data/src/extra_range.erl
@@ -0,0 +1,59 @@
+%% Test that a spec containing more items than actually returned
+%% (whether by accident or by benign overspeccing) does not prevent
+%% detection of impossible matches.
+
+-module(extra_range).
+
+-export([t1/2, t2/2, t3/2, t4/2]).
+
+-dialyzer([no_return]).
+
+%% this spec matches the behaviour of the code
+-spec normal(integer()) -> ok | error.
+normal(1) -> ok;
+normal(2) -> error.
+
+t1(X, Y) when is_integer(X), is_integer(Y) ->
+ ok = normal(X),
+ error = normal(Y),
+ ok.
+
+
+%% this spec has a typo, which should cause anyone trying to match on
+%% `ok = typo(X)' to get a warning, because `ok' is not in the spec
+-spec typo(integer()) -> ook | error.
+typo(1) -> ok;
+typo(2) -> error.
+
+t2(X, Y) when is_integer(X), is_integer(Y) ->
+ ok = typo(X), % warning expected - not allowed according to spec
+ error = typo(Y),
+ ok.
+
+
+%% this is overspecified, and should cause a warning for trying
+%% to match on `no = over(X)', because it cannot succeed and either
+%% the spec should be updated or the code should be extended
+-spec over(integer()) -> yes | no | maybe.
+over(1) -> yes;
+over(_) -> maybe.
+
+t3(X, Y) when is_integer(X), is_integer(Y) ->
+ yes = over(X),
+ no = over(Y), % warning expected - spec or code needs fixing
+ maybe = over(X + Y),
+ ok.
+
+
+%% this is underspecified, which should cause anyone trying to match on
+%% `maybe = under(X)' to get a warning, because `maybe' is not in the spec
+-spec under(integer()) -> yes | no.
+under(1) -> yes;
+under(2) -> no;
+under(_) -> maybe.
+
+t4(X, Y) when is_integer(X), is_integer(Y) ->
+ yes = under(X),
+ no = under(Y),
+ maybe = under(X + Y), % warning expected - not in spec
+ ok.
diff --git a/lib/dialyzer/test/small_SUITE_data/src/record_match.erl b/lib/dialyzer/test/small_SUITE_data/src/record_match.erl
new file mode 100644
index 0000000000..8e9b91937f
--- /dev/null
+++ b/lib/dialyzer/test/small_SUITE_data/src/record_match.erl
@@ -0,0 +1,17 @@
+-module(record_match).
+
+-export([select/0]).
+
+-record(b_literal, {val}).
+-record(b_remote, {mod,name,arity}).
+-record(b_local, {name,arity}).
+
+-type b_remote() :: #b_remote{}.
+-type b_local() :: #b_local{}.
+
+-type argument() :: b_remote() | b_local().
+
+-record(b_set, {args=[] :: [argument()]}).
+
+select() ->
+ #b_set{args=[#b_remote{},#b_literal{}]}.
diff --git a/lib/dialyzer/test/small_SUITE_data/src/stacktrace.erl b/lib/dialyzer/test/small_SUITE_data/src/stacktrace.erl
new file mode 100644
index 0000000000..de79e710e9
--- /dev/null
+++ b/lib/dialyzer/test/small_SUITE_data/src/stacktrace.erl
@@ -0,0 +1,73 @@
+-module(stacktrace).
+
+%% Check the stacktrace variable introduced in Erlang/OTP 21.0
+
+-export([t1/0, t2/0, t3/0, t4/0, s1/0, s2/0, s3/0, s4/0]).
+
+t1() ->
+ try foo:bar()
+ catch
+ E:P:S ->
+ {a,b} = S, % can never match
+ {E, P}
+ end.
+
+t2() ->
+ try foo:bar()
+ catch
+ E:P:S ->
+ [a,b] = S, % can never match
+ {E, P}
+ end.
+
+t3() ->
+ try foo:bar()
+ catch
+ E:P:S ->
+ [{m,f,[],[]}] = S,
+ {E, P}
+ end.
+
+t4() ->
+ try foo:bar()
+ catch
+ E:P:S ->
+ [{m,f,1,[{file,"tjo"},{line,95}]}] = S,
+ {E, P}
+ end.
+
+s1() ->
+ try foo:bar()
+ catch
+ E:P ->
+ S = erlang:get_stacktrace(),
+ {a,b} = S, % can never match
+ {E, P}
+ end.
+
+s2() ->
+ try foo:bar()
+ catch
+ E:P ->
+ S = erlang:get_stacktrace(),
+ [a,b] = S, % can never match
+ {E, P}
+ end.
+
+s3() ->
+ try foo:bar()
+ catch
+ E:P ->
+ S = erlang:get_stacktrace(),
+ [{m,f,[],[]}] = S,
+ {E, P}
+ end.
+
+s4() ->
+ try foo:bar()
+ catch
+ E:P ->
+ S = erlang:get_stacktrace(),
+ [{m,f,1,[{file,"tjo"},{line,95}]}] = S,
+ {E, P}
+ end.
diff --git a/lib/dialyzer/vsn.mk b/lib/dialyzer/vsn.mk
index 1b46f66602..fa58adc2db 100644
--- a/lib/dialyzer/vsn.mk
+++ b/lib/dialyzer/vsn.mk
@@ -1 +1 @@
-DIALYZER_VSN = 3.2.3
+DIALYZER_VSN = 3.2.4
diff --git a/lib/diameter/doc/src/diameter.xml b/lib/diameter/doc/src/diameter.xml
index 6b84b22eb5..6bc7d147c0 100644
--- a/lib/diameter/doc/src/diameter.xml
+++ b/lib/diameter/doc/src/diameter.xml
@@ -1865,8 +1865,8 @@ An example return value with for a client service with Origin-Host
{raddr,{127,0,0,1}},
{rport,3868},
{reuseaddr,true}]}]},
- {watchdog,{&lt;0.66.0>,{1346,171491,996448},okay}},
- {peer,{&lt;0.67.0>,{1346,171491,999906}}},
+ {watchdog,{&lt;0.66.0>,-576460736368485571,okay}},
+ {peer,{&lt;0.67.0>,-576460736357885808}},
{apps,[{0,common}]},
{caps,[{origin_host,{"client.example.com","server.example.com"}},
{origin_realm,{"example.com","example.com"}},
@@ -1946,8 +1946,8 @@ connection might look as follows.</p>
{transport_config,[{reuseaddr,true},
{ip,{127,0,0,1}},
{port,3868}]}]},
- {accept,[[{watchdog,{&lt;0.56.0>,{1346,171481,226895},okay}},
- {peer,{&lt;0.58.0>,{1346,171491,999511}}},
+ {accept,[[{watchdog,{&lt;0.56.0>,-576460739249514012,okay}},
+ {peer,{&lt;0.58.0>,-576460638229179167}},
{apps,[{0,common}]},
{caps,[{origin_host,{"server.example.com","client.example.com"}},
{origin_realm,{"example.com","example.com"}},
@@ -1976,7 +1976,7 @@ connection might look as follows.</p>
{send_max,148},
{send_avg,87},
{send_pend,0}]}]}],
- [{watchdog,{&lt;0.72.0>,{1346,171491,998404},initial}}]]},
+ [{watchdog,{&lt;0.72.0>,-576460638229717546,initial}}]]},
{statistics,[{{{0,280,0},recv},7},
{{{0,280,1},send},7},
{{{0,280,0},recv,{'Result-Code',2001}},7},
@@ -2024,8 +2024,8 @@ A return value for the server above might look as follows.</p>
{transport_config,[{reuseaddr,true},
{ip,{127,0,0,1}},
{port,3868}]}]},
- {watchdog,{&lt;0.56.0>,{1346,171481,226895},okay}},
- {peer,{&lt;0.58.0>,{1346,171491,999511}}},
+ {watchdog,{&lt;0.56.0>,-576460739249514012,okay}},
+ {peer,{&lt;0.58.0>,-576460638229179167}},
{apps,[{0,common}]},
{caps,[{origin_host,{"server.example.com","client.example.com"}},
{origin_realm,{"example.com","example.com"}},
diff --git a/lib/diameter/doc/src/notes.xml b/lib/diameter/doc/src/notes.xml
index ba4525fd20..fa1be39b5b 100644
--- a/lib/diameter/doc/src/notes.xml
+++ b/lib/diameter/doc/src/notes.xml
@@ -43,6 +43,32 @@ first.</p>
<!-- ===================================================================== -->
+<section><title>diameter 2.1.4</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Fix close of diameter_tcp/sctp listening socket at
+ diameter:remove_transport/2, that was broken in diameter
+ 2.1. A reconfigured transport could not listen on the
+ same endpoint as a result.</p>
+ <p>
+ Own Id: OTP-14839</p>
+ </item>
+ <item>
+ <p>
+ Fix handling of SUSPECT connections at service
+ termination. A connection with this watchdog state caused
+ diameter_service:terminate/2 to fail.</p>
+ <p>
+ Own Id: OTP-14947 Aux Id: ERIERL-124 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>diameter 2.1.3</title>
<section><title>Fixed Bugs and Malfunctions</title>
diff --git a/lib/diameter/src/base/diameter_reg.erl b/lib/diameter/src/base/diameter_reg.erl
index 5b7cfab31a..c1762a07e3 100644
--- a/lib/diameter/src/base/diameter_reg.erl
+++ b/lib/diameter/src/base/diameter_reg.erl
@@ -246,8 +246,11 @@ handle_call({add, Uniq, Key}, {Pid, _}, S) ->
handle_call({remove, Key}, {Pid, _}, S) ->
Rec = {Key, Pid},
- ets:delete_object(?TABLE, Rec),
- {reply, true, notify(remove, Rec, S)};
+ {reply, true, try
+ notify(remove, Rec, S)
+ after
+ ets:delete_object(?TABLE, Rec)
+ end};
handle_call({wait, Pat}, {Pid, _} = From, S) ->
NS = add_monitor(Pid, S),
@@ -370,10 +373,12 @@ send({_,_} = From, add, Rec) ->
down(Pid, #state{monitors = Ps} = S) ->
Recs = match('_', Pid),
- ets:match_delete(?TABLE, {'_', Pid}),
- lists:foldl(fun(R,NS) -> notify(remove, R, NS) end,
- flush(Pid, S#state{monitors = sets:del_element(Pid, Ps)}),
- Recs).
+ Acc0 = flush(Pid, S#state{monitors = sets:del_element(Pid, Ps)}),
+ try
+ lists:foldl(fun(R,NS) -> notify(remove, R, NS) end, Acc0, Recs)
+ after
+ ets:match_delete(?TABLE, {'_', Pid})
+ end.
%% flush/3
diff --git a/lib/diameter/src/base/diameter_service.erl b/lib/diameter/src/base/diameter_service.erl
index 31dd92f878..cbe66ef27a 100644
--- a/lib/diameter/src/base/diameter_service.erl
+++ b/lib/diameter/src/base/diameter_service.erl
@@ -151,7 +151,7 @@
apps :: match([{0..16#FFFFFFFF, diameter:app_alias()}] %% {Id, Alias}
| [diameter:app_alias()]), %% remote
caps :: match(#diameter_caps{}),
- started = diameter_lib:now(), %% at process start or sharing
+ started = diameter_lib:now(), %% at connection_up
watchdog :: match(pid() %% key into watchdogT
| undefined)}). %% undefined if remote
@@ -554,15 +554,25 @@ terminate(Reason, #state{service_name = Name, local = {PeerT, _, _}} = S) ->
%% wait for watchdog state changes to take care of if. That this
%% takes place after deleting the state entry ensures that the
%% resulting failover by request processes accomplishes nothing.
- ets:foldl(fun(#peer{pid = TPid}, _) ->
- diameter_traffic:peer_down(TPid)
- end,
- ok,
- PeerT),
+ ets:foldl(fun peer_down/2, ok, PeerT),
shutdown == Reason %% application shutdown
andalso shutdown(application, S).
+%% peer_down/1
+%%
+%% Entries with watchdog state SUSPECT are already down: ignore the
+%% expected failure. This assumes the current implementation, but
+%% double the number of lookups (in the typical case) could be the
+%% greater evil if there are many peer connections.
+
+peer_down(#peer{pid = TPid}, _) ->
+ try
+ diameter_traffic:peer_down(TPid)
+ catch
+ error: {badmatch, []} -> ok
+ end.
+
%% ---------------------------------------------------------------------------
%% # code_change/3
%% ---------------------------------------------------------------------------
diff --git a/lib/diameter/src/diameter.appup.src b/lib/diameter/src/diameter.appup.src
index 7da59f8b25..05a8c9378e 100644
--- a/lib/diameter/src/diameter.appup.src
+++ b/lib/diameter/src/diameter.appup.src
@@ -2,7 +2,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010-2017. All Rights Reserved.
+%% Copyright Ericsson AB 2010-2018. 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.
@@ -54,10 +54,10 @@
{"1.12.1", [{restart_application, diameter}]}, %% 19.1
{"1.12.2", [{restart_application, diameter}]}, %% 19.3
{"2.0", [{restart_application, diameter}]}, %% 20.0
- {"2.1", [{load_module, diameter_gen}, %% 20.1
- {update, diameter_reg, {advanced, "2.1"}}]},
- {"2.1.1", [{load_module, diameter_gen}]}, %% 20.1.2
- {"2.1.2", []} %% 20.1.3
+ {"2.1", [{restart_application, diameter}]}, %% 20.1
+ {"2.1.1", [{restart_application, diameter}]}, %% 20.1.2
+ {"2.1.2", [{restart_application, diameter}]}, %% 20.1.3
+ {"2.1.3", [{restart_application, diameter}]} %% 20.2
],
[
{"0.9", [{restart_application, diameter}]},
@@ -94,7 +94,8 @@
{"1.12.2", [{restart_application, diameter}]},
{"2.0", [{restart_application, diameter}]},
{"2.1", [{restart_application, diameter}]},
- {"2.1.1", [{load_module, diameter_gen}]},
- {"2.1.2", []}
+ {"2.1.1", [{restart_application, diameter}]},
+ {"2.1.2", [{restart_application, diameter}]},
+ {"2.1.3", [{restart_application, diameter}]}
]
}.
diff --git a/lib/diameter/vsn.mk b/lib/diameter/vsn.mk
index 0c852d75cd..b0fb4ada28 100644
--- a/lib/diameter/vsn.mk
+++ b/lib/diameter/vsn.mk
@@ -1,6 +1,6 @@
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2010-2017. All Rights Reserved.
+# Copyright Ericsson AB 2010-2018. 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,5 +17,5 @@
# %CopyrightEnd%
APPLICATION = diameter
-DIAMETER_VSN = 2.1.3
+DIAMETER_VSN = 2.1.4
APP_VSN = $(APPLICATION)-$(DIAMETER_VSN)$(PRE_VSN)
diff --git a/lib/et/src/et_wx_contents_viewer.erl b/lib/et/src/et_wx_contents_viewer.erl
index bca517317e..7ca41da3e8 100644
--- a/lib/et/src/et_wx_contents_viewer.erl
+++ b/lib/et/src/et_wx_contents_viewer.erl
@@ -93,8 +93,8 @@ start_link(Options) ->
end,
{ok, Pid}
catch
- error:Reason ->
- {error, {'EXIT', Reason, erlang:get_stacktrace()}}
+ error:Reason:Stacktrace ->
+ {error, {'EXIT', Reason, Stacktrace}}
end;
{error, Reason} ->
{error, Reason}
diff --git a/lib/eunit/src/eunit_lib.erl b/lib/eunit/src/eunit_lib.erl
index d1bd160ea1..771541354c 100644
--- a/lib/eunit/src/eunit_lib.erl
+++ b/lib/eunit/src/eunit_lib.erl
@@ -202,13 +202,13 @@ format_exception_test_() ->
"\nymmud:rorre"++_,
lists:reverse(lists:flatten(
format_exception(try erlang:error(dummy)
- catch C:R -> {C, R, erlang:get_stacktrace()}
+ catch C:R:S -> {C, R, S}
end)))),
?_assertMatch(
"\nymmud:rorre"++_,
lists:reverse(lists:flatten(
format_exception(try erlang:error(dummy, [a])
- catch C:R -> {C, R, erlang:get_stacktrace()}
+ catch C:R:S -> {C, R, S}
end))))].
-endif.
diff --git a/lib/eunit/src/eunit_listener.erl b/lib/eunit/src/eunit_listener.erl
index e652c5b2f6..75aa05c543 100644
--- a/lib/eunit/src/eunit_listener.erl
+++ b/lib/eunit/src/eunit_listener.erl
@@ -137,8 +137,7 @@ call(F, As, St) when is_atom(F) ->
try apply(St#state.callback, F, As) of
Substate -> St#state{state = Substate}
catch
- Class:Term ->
- Trace = erlang:get_stacktrace(),
+ Class:Term:Trace ->
if F =/= terminate ->
call(terminate, [{error, {Class, Term, Trace}},
St#state.state], St);
diff --git a/lib/eunit/src/eunit_proc.erl b/lib/eunit/src/eunit_proc.erl
index e075005238..96bdcf88b6 100644
--- a/lib/eunit/src/eunit_proc.erl
+++ b/lib/eunit/src/eunit_proc.erl
@@ -628,7 +628,7 @@ io_request({put_chars, M, F, As}, Buf) ->
try apply(M, F, As) of
Chars -> {ok, [Chars | Buf]}
catch
- C:T -> {{error, {C,T,erlang:get_stacktrace()}}, Buf}
+ C:T:S -> {{error, {C,T,S}}, Buf}
end;
io_request({put_chars, _Enc, Chars}, Buf) ->
io_request({put_chars, Chars}, Buf);
diff --git a/lib/eunit/src/eunit_test.erl b/lib/eunit/src/eunit_test.erl
index 6036537178..6fe85ae70a 100644
--- a/lib/eunit/src/eunit_test.erl
+++ b/lib/eunit/src/eunit_test.erl
@@ -39,11 +39,11 @@
%% somewhat, but you can't have everything.) Note that we assume that
%% this particular module is the boundary between eunit and user code.
-get_stacktrace() ->
- get_stacktrace([]).
+get_stacktrace(Trace) ->
+ get_stacktrace(Trace, []).
-get_stacktrace(Ts) ->
- eunit_lib:uniq(prune_trace(erlang:get_stacktrace(), Ts)).
+get_stacktrace(Trace, Ts) ->
+ eunit_lib:uniq(prune_trace(Trace, Ts)).
-dialyzer({no_match, prune_trace/2}).
prune_trace([{eunit_data, _, _} | Rest], Tail) ->
@@ -75,8 +75,8 @@ run_testfun(F) ->
{eunit_internal, Term} ->
%% Internally generated: re-throw Term (lose the trace)
throw(Term);
- Class:Reason ->
- {error, {Class, Reason, get_stacktrace()}}
+ Class:Reason:Trace ->
+ {error, {Class, Reason, Trace}}
end.
@@ -272,7 +272,7 @@ mf_wrapper(M, F) ->
fun () ->
try M:F()
catch
- error:undef ->
+ error:undef:Trace ->
%% Check if it was M:F/0 that was undefined
case erlang:module_loaded(M) of
false ->
@@ -282,14 +282,14 @@ mf_wrapper(M, F) ->
false ->
fail({no_such_function, {M,F,0}});
true ->
- rethrow(error, undef, [{M,F,0}])
+ rethrow(error, undef, Trace, [{M,F,0}])
end
end
end
end.
-rethrow(Class, Reason, Trace) ->
- erlang:raise(Class, Reason, get_stacktrace(Trace)).
+rethrow(Class, Reason, Trace, Ts) ->
+ erlang:raise(Class, Reason, get_stacktrace(Trace, Ts)).
fail(Term) ->
throw({eunit_internal, Term}).
@@ -332,12 +332,14 @@ enter_context(Setup, Cleanup, Instantiate, Callback) ->
T ->
case eunit_lib:is_not_test(T) of
true ->
- catch throw(error), % generate a stack trace
+ {_, Stacktrace} =
+ erlang:process_info(self(),
+ current_stacktrace),
{module,M} = erlang:fun_info(Instantiate, module),
{name,N} = erlang:fun_info(Instantiate, name),
{arity,A} = erlang:fun_info(Instantiate, arity),
context_error({bad_instantiator, {{M,N,A},T}},
- error, badarg);
+ error, Stacktrace, badarg);
false ->
ok
end,
@@ -346,21 +348,22 @@ enter_context(Setup, Cleanup, Instantiate, Callback) ->
%% Always run cleanup; client may be an idiot
try Cleanup(R)
catch
- Class:Term ->
- context_error(cleanup_failed, Class, Term)
+ Class:Term:Trace ->
+ context_error(cleanup_failed,
+ Class, Trace, Term)
end
end
catch
- Class:Term ->
- context_error(instantiation_failed, Class, Term)
+ Class:Term:Trace ->
+ context_error(instantiation_failed, Class, Trace, Term)
end
catch
- Class:Term ->
- context_error(setup_failed, Class, Term)
+ Class:Term:Trace ->
+ context_error(setup_failed, Class, Trace, Term)
end.
-context_error(Type, Class, Term) ->
- throw({context_error, Type, {Class, Term, get_stacktrace()}}).
+context_error(Type, Class, Trace, Term) ->
+ throw({context_error, Type, {Class, Term, get_stacktrace(Trace)}}).
%% This generates single setup/cleanup functions from a list of tuples
%% on the form {Tag, Setup, Cleanup}, where the setup function always
@@ -378,8 +381,8 @@ multi_setup([{Tag, S, C} | Es], CleanupPrev) ->
try C(R) of
_ -> CleanupPrev(Rs)
catch
- Class:Term ->
- throw({Tag, {Class, Term, get_stacktrace()}})
+ Class:Term:Trace ->
+ throw({Tag, {Class, Term, Trace}})
end
end,
{SetupRest, CleanupAll} = multi_setup(Es, Cleanup),
@@ -388,9 +391,9 @@ multi_setup([{Tag, S, C} | Es], CleanupPrev) ->
R ->
SetupRest([R|Rs])
catch
- Class:Term ->
+ Class:Term:Trace ->
CleanupPrev(Rs),
- throw({Tag, {Class, Term, get_stacktrace()}})
+ throw({Tag, {Class, Term, Trace}})
end
end,
CleanupAll};
diff --git a/lib/hipe/cerl/Makefile b/lib/hipe/cerl/Makefile
index 9f50d6bf91..b6116c4276 100644
--- a/lib/hipe/cerl/Makefile
+++ b/lib/hipe/cerl/Makefile
@@ -44,7 +44,7 @@ RELSYSDIR = $(RELEASE_PATH)/lib/hipe-$(VSN)
# Target Specs
# ----------------------------------------------------
MODULES = cerl_cconv cerl_closurean cerl_hipeify cerl_lib \
- cerl_messagean cerl_pmatch cerl_prettypr cerl_to_icode \
+ cerl_pmatch cerl_prettypr cerl_to_icode \
cerl_typean erl_bif_types erl_types
HRL_FILES= cerl_hipe_primops.hrl
diff --git a/lib/hipe/cerl/cerl_messagean.erl b/lib/hipe/cerl/cerl_messagean.erl
deleted file mode 100644
index c79e045bd0..0000000000
--- a/lib/hipe/cerl/cerl_messagean.erl
+++ /dev/null
@@ -1,1095 +0,0 @@
-%% 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.
-%%
-%% @copyright 2002 Richard Carlsson
-%% @author Richard Carlsson <[email protected]>
-%% @doc Message analysis of Core Erlang programs.
-
-%% TODO: might need a "top" (`any') element for any-length value lists.
-
--module(cerl_messagean).
-
--export([annotate/1]).
-
--import(cerl, [alias_pat/1, alias_var/1, ann_c_var/2, ann_c_fun/3,
- apply_args/1, apply_op/1, atom_val/1, bitstr_size/1,
- bitstr_val/1, binary_segments/1, c_letrec/2,
- ann_c_tuple/2, c_nil/0, call_args/1, call_module/1,
- call_name/1, case_arg/1, case_clauses/1, catch_body/1,
- clause_body/1, clause_guard/1, clause_pats/1, cons_hd/1,
- cons_tl/1, fun_body/1, fun_vars/1, get_ann/1, int_val/1,
- is_c_atom/1, is_c_int/1, let_arg/1, let_body/1,
- let_vars/1, letrec_body/1, letrec_defs/1, module_defs/1,
- module_defs/1, module_exports/1, pat_vars/1,
- primop_args/1, primop_name/1, receive_action/1,
- receive_clauses/1, receive_timeout/1, seq_arg/1,
- seq_body/1, set_ann/2, try_arg/1, try_body/1, try_vars/1,
- try_evars/1, try_handler/1, tuple_es/1, type/1,
- values_es/1]).
-
--import(cerl_trees, [get_label/1]).
-
--define(DEF_LIMIT, 4).
-
-%% -export([test/1, test1/1, ttest/1]).
-
-%% ttest(F) ->
-%% {T, _} = cerl_trees:label(user_default:read(F)),
-%% {Time0, _} = erlang:statistics(runtime),
-%% analyze(T),
-%% {Time1, _} = erlang:statistics(runtime),
-%% Time1 - Time0.
-
-%% test(F) ->
-%% {T, _} = cerl_trees:label(user_default:read(F)),
-%% {Time0, _} = erlang:statistics(runtime),
-%% {Esc, _Vars} = analyze(T),
-%% {Time1, _} = erlang:statistics(runtime),
-%% io:fwrite("messages: ~p.\n", [Esc]),
-%% Set = sets:from_list(Esc),
-%% H = fun (Node, Ctxt, Cont) ->
-%% Doc = case get_ann(Node) of
-%% [{label, L} | _] ->
-%% B = sets:is_element(L, Set),
-%% bf(Node, Ctxt, Cont, B);
-%% _ ->
-%% bf(Node, Ctxt, Cont, false)
-%% end,
-%% case type(Node) of
-%% cons -> color(Doc);
-%% tuple -> color(Doc);
-%% _ -> Doc
-%% end
-%% end,
-%% {ok, FD} = file:open("out.html",[write]),
-%% Txt = cerl_prettypr:format(T, [{hook, H},{user,false}]),
-%% io:put_chars(FD, "<pre>\n"),
-%% io:put_chars(FD, html(Txt)),
-%% io:put_chars(FD, "</pre>\n"),
-%% file:close(FD),
-%% {ok, Time1 - Time0}.
-
-%% test1(F) ->
-%% {T, _} = cerl_trees:label(user_default:read(F)),
-%% {Time0, _} = erlang:statistics(runtime),
-%% {T1, Esc, Vars} = annotate(T),
-%% {Time1, _} = erlang:statistics(runtime),
-%% io:fwrite("messages: ~p.\n", [Esc]),
-%% %%% io:fwrite("vars: ~p.\n", [[X || X <- dict:to_list(Vars)]]),
-%% T2 = hhl_transform:transform(T1, Vars),
-%% Set = sets:from_list(Esc),
-%% H = fun (Node, Ctxt, Cont) ->
-%% case get_ann(Node) of
-%% [{label, L} | _] ->
-%% B = sets:is_element(L, Set),
-%% bf(Node, Ctxt, Cont, B);
-%% _ ->
-%% bf(Node, Ctxt, Cont, false)
-%% end
-%% end,
-%% {ok, FD} = file:open("out.html",[write]),
-%% Txt = cerl_prettypr:format(T2, [{hook, H},{user,false}]),
-%% io:put_chars(FD, "<pre>\n"),
-%% io:put_chars(FD, html(Txt)),
-%% io:put_chars(FD, "</pre>\n"),
-%% file:close(FD),
-%% {ok, Time1 - Time0}.
-
-%% html(Cs) ->
-%% html(Cs, []).
-
-%% html([$#, $< | Cs], As) ->
-%% html_1(Cs, [$< | As]);
-%% html([$< | Cs], As) ->
-%% html(Cs, ";tl&" ++ As);
-%% html([$> | Cs], As) ->
-%% html(Cs, ";tg&" ++ As);
-%% html([$& | Cs], As) ->
-%% html(Cs, ";pma&" ++ As);
-%% html([C | Cs], As) ->
-%% html(Cs, [C | As]);
-%% html([], As) ->
-%% lists:reverse(As).
-
-%% html_1([$> | Cs], As) ->
-%% html(Cs, [$> | As]);
-%% html_1([C | Cs], As) ->
-%% html_1(Cs, [C | As]).
-
-%% bf(Node, Ctxt, Cont, B) ->
-%% B0 = cerl_prettypr:get_ctxt_user(Ctxt),
-%% if B /= B0 ->
-%% Ctxt1 = cerl_prettypr:set_ctxt_user(Ctxt, B),
-%% Doc = Cont(Node, Ctxt1),
-%% case B of
-%% true ->
-%% Start = "<b>",
-%% End = "</b>";
-%% false ->
-%% Start = "</b>",
-%% End = "<b>"
-%% end,
-%% markup(Doc, Start, End);
-%% true ->
-%% Cont(Node, Ctxt)
-%% end.
-
-%% color(Doc) ->
-%% % Doc.
-%% markup(Doc, "<font color=blue>", "</font>").
-
-%% markup(Doc, Start, End) ->
-%% prettypr:beside(
-%% prettypr:null_text([$# | Start]),
-%% prettypr:beside(Doc,
-%% prettypr:null_text([$# | End]))).
-
-
-%% =====================================================================
-%% annotate(Tree) -> {Tree1, Escapes, Vars}
-%%
-%% Tree = cerl:cerl()
-%%
-%% Analyzes `Tree' (see `analyze') and appends a term 'escapes', to
-%% the annotation list of each constructor expression node and of
-%% `Tree', corresponding to the escape information derived by the
-%% analysis. Any previous such annotations are removed from `Tree'.
-%% `Tree1' is the modified tree; for details on `OutList',
-%% `Outputs' , `Dependencies', `Escapes' and `Parents', see
-%% `analyze'.
-%%
-%% Note: `Tree' must be annotated with labels in order to use this
-%% function; see `analyze' for details.
-
--type label() :: integer() | 'external' | 'top'.
--type ordset(X) :: [X]. % XXX: TAKE ME OUT
-
--spec annotate(cerl:cerl()) -> {cerl:cerl(), ordset(label()), dict:dict()}.
-
-annotate(Tree) ->
- {Esc0, Vars} = analyze(Tree),
- Esc = sets:from_list(Esc0),
- F = fun (T) ->
- case type(T) of
- literal -> T;
-%%% var ->
-%%% L = get_label(T),
-%%% T1 = ann_escape(T, L, Esc),
-%%% X = dict:fetch(L, Vars),
-%%% set_ann(T1, append_ann({s,X}, get_ann(T1)));
- _ ->
- L = get_label(T),
- ann_escape(T, L, Esc)
- end
- end,
- {cerl_trees:map(F, Tree), Esc0, Vars}.
-
-ann_escape(T, L, Esc) ->
- case sets:is_element(L, Esc) of
- true ->
- set_ann(T, append_ann(escapes, get_ann(T)));
- false ->
- T
- end.
-
-append_ann(Tag, [X | Xs]) ->
- if tuple_size(X) >= 1, element(1, X) =:= Tag ->
- append_ann(Tag, Xs);
- true ->
- [X | append_ann(Tag, Xs)]
- end;
-append_ann(Tag, []) ->
- [Tag].
-
-
-%% =====================================================================
-%% analyze(Tree) -> Escapes
-%%
-%% Tree = cerl:cerl()
-%% Escapes = ordset(Label)
-%% Label = integer() | external | top
-%%
-%% Analyzes a module or an expression represented by `Tree'.
-%%
-%% `Escapes' is the set of labels of constructor expressions in
-%% `Tree' such that the created values may be accessed from outside
-%% `Tree'.
-%%
-%% Note: `Tree' must be annotated with labels (as done by the
-%% function `cerl_trees:label/1') in order to use this function.
-%% The label annotation `{label, L}' (where L should be an integer)
-%% must be the first element of the annotation list of each node in
-%% the tree. Instances of variables bound in `Tree' which denote
-%% the same variable must have the same label; apart from this,
-%% labels should be unique. Constant literals do not need to be
-%% labeled.
-
--record(state, {vars, out, dep, work, funs, k}).
-
-%% Note: We assume that all remote calls and primops return a single
-%% value.
-
-%% The analysis determines which objects (identified by the
-%% corresponding "cons-point" labels in the code) are likely to be
-%% passed in a message. (If so, we say that they "escape".) It is always
-%% safe to assume either case, because the send operation will assure
-%% that things are copied if necessary. This analysis tries to
-%% anticipate that copying will be done.
-%%
-%% Rules:
-%% 1) An object passed as message argument (or part of such an
-%% argument) to a known send-operation, will probably be a message.
-%% 2) A received value is always a message (safe).
-%% 3) The external function can return any object (unsafe).
-%% 4) A function called from the external function can receive any
-%% object (unsafe) as argument.
-%% 5) Unknown functions/operations can return any object (unsafe).
-
-%% We wrap the given syntax tree T in a fun-expression labeled `top',
-%% which is initially in the set of escaped labels. `top' will be
-%% visited at least once.
-%%
-%% We create a separate function labeled `external', defined as:
-%% "'external'/1 = fun () -> Any", which will represent any and all
-%% functions outside T, and which returns the 'unsafe' value.
-
-analyze(Tree) ->
- analyze(Tree, ?DEF_LIMIT).
-
-analyze(Tree, Limit) ->
- {_, _, Esc, Dep, _Par} = cerl_closurean:analyze(Tree),
-%%% io:fwrite("dependencies: ~w.\n", [dict:to_list(Dep)]),
- analyze(Tree, Limit, Dep, Esc).
-
-analyze(Tree, Limit, Dep0, Esc0) ->
- %% Note that we use different name spaces for variable labels and
- %% function/call site labels, so we can reuse some names here. We
- %% assume that the labeling of Tree only uses integers, not atoms.
- Any = ann_c_var([{label, any}], 'Any'),
- External = ann_c_var([{label, external}], {external, 1}),
- ExtFun = ann_c_fun([{label, external}], [], Any),
-%%% io:fwrite("external fun:\n~s.\n",
-%%% [cerl_prettypr:format(ExtFun, [noann, {paper, 80}])]),
- Top = ann_c_var([{label, top}], {top, 0}),
- TopFun = ann_c_fun([{label, top}], [], Tree),
-
- %% The "start fun" just makes the initialisation easier. It is not
- %% itself in the call graph.
- StartFun = ann_c_fun([{label, start}], [],
- c_letrec([{External, ExtFun}, {Top, TopFun}],
- c_nil())),
-%%% io:fwrite("start fun:\n~s.\n",
-%%% [cerl_prettypr:format(StartFun, [{paper, 80}])]),
-
- %% Initialise the Any and Escape variables. Gather a database of all
- %% fun-expressions in Tree and initialise their outputs and parameter
- %% variables. All escaping functions can receive any values as
- %% inputs. Bind all module- and letrec-defined variables to their
- %% corresponding labels.
- Esc = sets:from_list(Esc0),
- Unsafe = unsafe(),
- Empty = empty(),
- Funs0 = dict:new(),
- Vars0 = dict:store(escape, empty(),
- dict:store(any, Unsafe, dict:new())),
- Out0 = dict:new(),
- F = fun (T, S = {Fs, Vs, Os}) ->
- case type(T) of
- 'fun' ->
- L = get_label(T),
- As = fun_vars(T),
- X = case sets:is_element(L, Esc) of
- true -> Unsafe;
- false -> Empty
- end,
- {dict:store(L, T, Fs),
- bind_vars_single(As, X, Vs),
- dict:store(L, none, Os)};
- letrec ->
- {Fs, bind_defs(letrec_defs(T), Vs), Os};
- module ->
- {Fs, bind_defs(module_defs(T), Vs), Os};
- _ ->
- S
- end
- end,
- {Funs, Vars, Out} = cerl_trees:fold(F, {Funs0, Vars0, Out0}, StartFun),
-
- %% Add the dependency for the loop in 'external':
- Dep = add_dep(loop, external, Dep0),
-
- %% Enter the fixpoint iteration at the StartFun.
- St = loop(StartFun, start, #state{vars = Vars,
- out = Out,
- dep = Dep,
- work = init_work(),
- funs = Funs,
- k = Limit}),
- Ms = labels(dict:fetch(escape, St#state.vars)),
- {Ms, St#state.vars}.
-
-loop(T, L, St0) ->
-%%% io:fwrite("analyzing: ~w.\n",[L]),
-%%% io:fwrite("work: ~w.\n", [St0#state.work]),
- Xs0 = dict:fetch(L, St0#state.out),
- {Xs1, St1} = visit(fun_body(T), L, St0),
- Xs = limit(Xs1, St1#state.k),
- {W, M} = case equal(Xs0, Xs) of
- true ->
- {St1#state.work, St1#state.out};
- false ->
-%%% io:fwrite("out (~w) changed: ~w <- ~w.\n",
-%%% [L, Xs, Xs0]),
- M1 = dict:store(L, Xs, St1#state.out),
- case dict:find(L, St1#state.dep) of
- {ok, S} ->
- {add_work(set__to_list(S), St1#state.work),
- M1};
- error ->
- {St1#state.work, M1}
- end
- end,
- St2 = St1#state{out = M},
- case take_work(W) of
- {ok, L1, W1} ->
- T1 = dict:fetch(L1, St2#state.funs),
- loop(T1, L1, St2#state{work = W1});
- none ->
- St2
- end.
-
-visit(T, L, St) ->
-%%% io:fwrite("visiting: ~w.\n",[type(T)]),
- case type(T) of
- literal ->
- %% This is (or should be) a constant, even if it's compound,
- %% so it's bugger all whether it is sent or not.
- case cerl:concrete(T) of
- [] -> {[empty()], St};
- X when is_atom(X) -> {[empty()], St};
- X when is_integer(X) -> {[empty()], St};
- X when is_float(X) -> {[empty()], St};
- _ ->
- exit({not_literal, T})
- end;
- var ->
- %% If a variable is not already in the store here, it must
- %% be free in the program.
- L1 = get_label(T),
- Vars = St#state.vars,
- case dict:find(L1, Vars) of
- {ok, X} ->
- {[X], St};
- error ->
-%%% io:fwrite("free var: ~w.\n",[L1]),
- X = unsafe(),
- St1 = St#state{vars = dict:store(L1, X, Vars)},
- {[X], St1}
- end;
- 'fun' ->
- %% Must revisit the fun also, because its environment might
- %% have changed. (We don't keep track of such dependencies.)
- L1 = get_label(T),
- St1 = St#state{work = add_work([L1], St#state.work)},
- %% Currently, lambda expressions can only be locally
- %% allocated, and therefore we have to force copying by
- %% treating them as "unsafe" for now.
- {[unsafe()], St1};
- %% {[singleton(L1)], St1};
- values ->
- visit_list(values_es(T), L, St);
- cons ->
- {[X1, X2], St1} = visit_list([cons_hd(T), cons_tl(T)], L, St),
- L1 = get_label(T),
- X = make_cons(L1, X1, X2),
- %% Also store the values of the elements.
- Hd = get_hd(X),
- Tl = get_tl(X),
- St2 = St1#state{vars = dict:store(L1, [Hd, Tl], St1#state.vars)},
- {[X], St2};
- tuple ->
- {Xs, St1} = visit_list(tuple_es(T), L, St),
- L1 = get_label(T),
- %% Also store the values of the elements.
- St2 = St1#state{vars = dict:store(L1, Xs, St1#state.vars)},
- {[struct(L1, Xs)], St2};
- 'let' ->
- {Xs, St1} = visit(let_arg(T), L, St),
- Vars = bind_vars(let_vars(T), Xs, St1#state.vars),
- visit(let_body(T), L, St1#state{vars = Vars});
- seq ->
- {_, St1} = visit(seq_arg(T), L, St),
- visit(seq_body(T), L, St1);
- apply ->
- {_F, St1} = visit(apply_op(T), L, St),
- {As, St2} = visit_list(apply_args(T), L, St1),
- L1 = get_label(T),
- Ls = get_deps(L1, St#state.dep),
- Out = St2#state.out,
- Xs1 = join_list([dict:fetch(X, Out) || X <- Ls]),
- {Xs1, call_site(Ls, As, St2)};
- call ->
- M = call_module(T),
- F = call_name(T),
- As = call_args(T),
- {_, St1} = visit(M, L, St),
- {_, St2} = visit(F, L, St1),
- {Xs, St3} = visit_list(As, L, St2),
- L1 = get_label(T),
- remote_call(M, F, Xs, As, L1, St3);
- primop ->
- As = primop_args(T),
- {Xs, St1} = visit_list(As, L, St),
- F = atom_val(primop_name(T)),
- primop_call(F, length(Xs), Xs, As, St1);
- 'case' ->
- {Xs, St1} = visit(case_arg(T), L, St),
- visit_clauses(Xs, case_clauses(T), L, St1);
- 'receive' ->
- %% The received value is of course a message, so it
- %% is 'empty()', not 'unsafe()'.
- X = empty(),
- {Xs1, St1} = visit_clauses([X], receive_clauses(T), L, St),
- {_, St2} = visit(receive_timeout(T), L, St1),
- {Xs2, St3} = visit(receive_action(T), L, St2),
- {join(Xs1, Xs2), St3};
- 'try' ->
- {Xs1, St1} = visit(try_arg(T), L, St),
- X = unsafe(),
- Vars = bind_vars(try_vars(T), Xs1, St1#state.vars),
- {Xs2, St2} = visit(try_body(T), L, St1#state{vars = Vars}),
- EVars = bind_vars(try_evars(T), [X, X, X], St2#state.vars),
- {Xs3, St3} = visit(try_handler(T), L, St2#state{vars = EVars}),
- {join(Xs2, Xs3), St3};
- 'catch' ->
- %% If we catch an exception, we can get unsafe data.
- {Xs, St1} = visit(catch_body(T), L, St),
- {join([unsafe()], Xs), St1};
- binary ->
- %% Binaries are heap objects, but we don't have special
- %% shared-heap allocation operators for them at the moment.
- %% They must therefore be treated as unsafe.
- {_, St1} = visit_list(binary_segments(T), L, St),
- {[unsafe()], St1};
- bitstr ->
- %% The other fields are constant literals.
- {_, St1} = visit(bitstr_val(T), L, St),
- {_, St2} = visit(bitstr_size(T), L, St1),
- {none, St2};
- letrec ->
- %% All the bound funs should be revisited, because the
- %% environment might have changed.
- Ls = [get_label(F) || {_, F} <- letrec_defs(T)],
- St1 = St#state{work = add_work(Ls, St#state.work)},
- visit(letrec_body(T), L, St1);
- module ->
- %% We regard a module as a tuple of function variables in
- %% the body of a `letrec'.
- visit(c_letrec(module_defs(T),
- ann_c_tuple([{label, get_label(T)}],
- module_exports(T))),
- L, St)
- end.
-
-visit_clause(T, Xs, L, St) ->
- Vars = bind_pats(clause_pats(T), Xs, St#state.vars),
- {_, St1} = visit(clause_guard(T), L, St#state{vars = Vars}),
- visit(clause_body(T), L, St1).
-
-%% We assume correct value-list typing.
-
-visit_list([T | Ts], L, St) ->
- {Xs, St1} = visit(T, L, St),
- {Xs1, St2} = visit_list(Ts, L, St1),
- X = case Xs of
- [X1] -> X1;
- _ -> empty()
- end,
- {[X | Xs1], St2};
-visit_list([], _L, St) ->
- {[], St}.
-
-visit_clauses(Xs, [T | Ts], L, St) ->
- {Xs1, St1} = visit_clause(T, Xs, L, St),
- {Xs2, St2} = visit_clauses(Xs, Ts, L, St1),
- {join(Xs1, Xs2), St2};
-visit_clauses(_, [], _L, St) ->
- {none, St}.
-
-bind_defs([{V, F} | Ds], Vars) ->
- bind_defs(Ds, dict:store(get_label(V), singleton(get_label(F)), Vars));
-bind_defs([], Vars) ->
- Vars.
-
-bind_pats(Ps, none, Vars) ->
- bind_pats_single(Ps, empty(), Vars);
-bind_pats(Ps, Xs, Vars) ->
- if length(Xs) =:= length(Ps) ->
- bind_pats_list(Ps, Xs, Vars);
- true ->
- bind_pats_single(Ps, empty(), Vars)
- end.
-
-%% The lists might not be of the same length.
-
-bind_pats_list([P | Ps], [X | Xs], Vars) ->
- bind_pats_list(Ps, Xs, bind_pat_vars(P, X, Vars));
-bind_pats_list(Ps, [], Vars) ->
- bind_pats_single(Ps, empty(), Vars);
-bind_pats_list([], _, Vars) ->
- Vars.
-
-bind_pats_single([P | Ps], X, Vars) ->
- bind_pats_single(Ps, X, bind_pat_vars(P, X, Vars));
-bind_pats_single([], _X, Vars) ->
- Vars.
-
-bind_pat_vars(P, X, Vars) ->
- case type(P) of
- var ->
- dict:store(get_label(P), X, Vars);
- literal ->
- Vars;
- cons ->
- bind_pats_list([cons_hd(P), cons_tl(P)],
- [get_hd(X), get_tl(X)], Vars);
- tuple ->
- case elements(X) of
- none ->
- bind_vars_single(pat_vars(P), X, Vars);
- Xs ->
- bind_pats_list(tuple_es(P), Xs, Vars)
- end;
- binary ->
- %% See the handling of binary-expressions.
- bind_pats_single(binary_segments(P), unsafe(), Vars);
- bitstr ->
- %% See the handling of binary-expressions.
- bind_pats_single([bitstr_val(P), bitstr_size(P)],
- unsafe(), Vars);
- alias ->
- P1 = alias_pat(P),
- Vars1 = bind_pat_vars(P1, X, Vars),
- dict:store(get_label(alias_var(P)), X, Vars1)
- end.
-
-%%% %% This is the "exact" version of list representation, which simply
-%%% %% mimics the actual cons, head and tail operations.
-%%% make_cons(L, X1, X2) ->
-%%% struct(L1, [X1, X2]).
-%%% get_hd(X) ->
-%%% case elements(X) of
-%%% none -> X;
-%%% [X1 | _] -> X1;
-%%% _ -> empty()
-%%% end.
-%%% get_tl(X) ->
-%%% case elements(X) of
-%%% none -> X;
-%%% [_, X2 | _] -> X2;
-%%% _ -> empty()
-%%% end.
-
-%% This version does not unnecessarily confuse spine labels with element
-%% labels, and is safe. However, it loses precision if cons cells are
-%% used for other things than proper lists.
-
-make_cons(L, X1, X2) ->
- %% join subtypes and cons locations
- join_single(struct(L, [X1]), X2).
-
-get_hd(X) ->
- case elements(X) of
- none -> X;
- [X1 | _] -> X1; % First element represents list subtype.
- _ -> empty()
- end.
-
-get_tl(X) -> X. % Tail of X has same type as X.
-
-bind_vars(Vs, none, Vars) ->
- bind_vars_single(Vs, empty(), Vars);
-bind_vars(Vs, Xs, Vars) ->
- if length(Vs) =:= length(Xs) ->
- bind_vars_list(Vs, Xs, Vars);
- true ->
- bind_vars_single(Vs, empty(), Vars)
- end.
-
-bind_vars_list([V | Vs], [X | Xs], Vars) ->
- bind_vars_list(Vs, Xs, dict:store(get_label(V), X, Vars));
-bind_vars_list([], [], Vars) ->
- Vars.
-
-bind_vars_single([V | Vs], X, Vars) ->
- bind_vars_single(Vs, X, dict:store(get_label(V), X, Vars));
-bind_vars_single([], _X, Vars) ->
- Vars.
-
-%% This handles a call site, updating parameter variables with respect
-%% to the actual parameters. The 'external' function is handled
-%% specially, since it can get an arbitrary number of arguments. For our
-%% purposes here, calls to the external function can be ignored.
-
-call_site(Ls, Xs, St) ->
-%%% io:fwrite("call site: ~w -> ~w (~w).\n", [L, Ls, Xs]),
- {W, V} = call_site(Ls, Xs, St#state.work, St#state.vars,
- St#state.funs, St#state.k),
- St#state{work = W, vars = V}.
-
-call_site([external | Ls], Xs, W, V, Fs, Limit) ->
- call_site(Ls, Xs, W, V, Fs, Limit);
-call_site([L | Ls], Xs, W, V, Fs, Limit) ->
- Vs = fun_vars(dict:fetch(L, Fs)),
- case bind_args(Vs, Xs, V, Limit) of
- {V1, true} ->
- call_site(Ls, Xs, add_work([L], W), V1, Fs, Limit);
- {V1, false} ->
- call_site(Ls, Xs, W, V1, Fs, Limit)
- end;
-call_site([], _, W, V, _, _) ->
- {W, V}.
-
-add_dep(Source, Target, Deps) ->
- case dict:find(Source, Deps) of
- {ok, X} ->
- case set__is_member(Target, X) of
- true ->
- Deps;
- false ->
-%%% io:fwrite("new dep: ~w <- ~w.\n", [Target, Source]),
- dict:store(Source, set__add(Target, X), Deps)
- end;
- error ->
-%%% io:fwrite("new dep: ~w <- ~w.\n", [Target, Source]),
- dict:store(Source, set__singleton(Target), Deps)
- end.
-
-%% If the arity does not match the call, nothing is done here.
-
-bind_args(Vs, Xs, Vars, Limit) ->
- if length(Vs) =:= length(Xs) ->
- bind_args(Vs, Xs, Vars, Limit, false);
- true ->
- {Vars, false}
- end.
-
-bind_args([V | Vs], [X | Xs], Vars, Limit, Ch) ->
- L = get_label(V),
- {Vars1, Ch1} = bind_arg(L, X, Vars, Limit, Ch),
- bind_args(Vs, Xs, Vars1, Limit, Ch1);
-bind_args([], [], Vars, _Limit, Ch) ->
- {Vars, Ch}.
-
-%% bind_arg(L, X, Vars, Limit) ->
-%% bind_arg(L, X, Vars, Limit, false).
-
-bind_arg(L, X, Vars, Limit, Ch) ->
- X0 = dict:fetch(L, Vars),
- X1 = limit_single(join_single(X, X0), Limit),
- case equal_single(X0, X1) of
- true ->
- {Vars, Ch};
- false ->
-%%% io:fwrite("arg (~w) changed: ~w <- ~w + ~w.\n",
-%%% [L, X1, X0, X]),
- {dict:store(L, X1, Vars), true}
- end.
-
-%% This handles escapes from things like primops and remote calls.
-
-escape(Xs, Ns, St) ->
- escape(Xs, Ns, 1, St).
-
-escape([_ | Xs], Ns=[N1 | _], N, St) when is_integer(N1), N1 > N ->
- escape(Xs, Ns, N + 1, St);
-escape([X | Xs], [N | Ns], N, St) ->
- Vars = St#state.vars,
- X0 = dict:fetch(escape, Vars),
- X1 = join_single(X, X0),
- case equal_single(X0, X1) of
- true ->
- escape(Xs, Ns, N + 1, St);
- false ->
-%%% io:fwrite("escape changed: ~w <- ~w + ~w.\n", [X1, X0, X]),
- Vars1 = dict:store(escape, X1, Vars),
- escape(Xs, Ns, N + 1, St#state{vars = Vars1})
- end;
-escape(Xs, [_ | Ns], N, St) ->
- escape(Xs, Ns, N + 1, St);
-escape(_, _, _, St) ->
- St.
-
-%% Handle primop calls: (At present, we assume that all unknown calls
-%% yield exactly one value. This might have to be changed.)
-
-primop_call(F, A, Xs, _As, St0) ->
- %% St1 = case is_escape_op(F, A) of
- %% [] -> St0;
- %% Ns -> escape(Xs, Ns, St0)
- %% end,
- St1 = St0,
- case is_imm_op(F, A) of
- true ->
- {[empty()], St1};
- false ->
- call_unknown(Xs, St1)
- end.
-
-%% Handle remote-calls: (At present, we assume that all unknown calls
-%% yield exactly one value. This might have to be changed.)
-
-remote_call(M, F, Xs, As, L, St) ->
- case is_c_atom(M) andalso is_c_atom(F) of
- true ->
- remote_call_1(atom_val(M), atom_val(F), length(Xs),
- Xs, As, L, St);
- false ->
- %% Unknown function
- call_unknown(Xs, St)
- end.
-
-%% When calling an unknown function, we assume that the result does
-%% *not* contain any of the constructors in its arguments (but it could
-%% return locally allocated data that we don't know about). Note that
-%% even a "pure" function can still cons up new data.
-
-call_unknown(_Xs, St) ->
- {[unsafe()], St}.
-
-%% We need to handle some important standard functions in order to get
-%% decent precision.
-%% TODO: foldl, map, mapfoldl
-
-remote_call_1(erlang, hd, 1, [X], _As, _L, St) ->
- {[get_hd(X)], St};
-remote_call_1(erlang, tl, 1, [X], _As, _L, St) ->
- {[get_tl(X)], St};
-remote_call_1(erlang, element, 2, [_,X], [N|_], _L, St) ->
- case elements(X) of
- none -> {[X], St};
- Xs ->
- case is_c_int(N) of
- true ->
- N1 = int_val(N),
- if is_integer(N1), 1 =< N1, N1 =< length(Xs) ->
- {[nth(N1, Xs)], St};
- true ->
- {none, St}
- end;
- false ->
- %% Even if we don't know which element is selected,
- %% we know that the top level is never part of the
- %% returned value.
- {[join_single_list(Xs)], St}
- end
- end;
-remote_call_1(erlang, setelement, 3, [_,X, Y], [N|_], L, St) ->
- %% The constructor gets the label of the call operation.
- case elements(X) of
- none -> {[join_single(singleton(L), join_single(X, Y))], St};
- Xs ->
- case is_c_int(N) of
- true ->
- N1 = int_val(N),
- if is_integer(N1), 1 =< N1, N1 =< length(Xs) ->
- Xs1 = set_nth(N1, Y, Xs),
- {[struct(L, Xs1)], St};
- true ->
- {none, St}
- end;
- false ->
- %% Even if we don't know which element is selected,
- %% we know that the top level is never part of the
- %% returned value (a new tuple is always created).
- Xs1 = [join_single(Y, X1) || X1 <- Xs],
- {[struct(L, Xs1)], St}
- end
- end;
-remote_call_1(erlang, '++', 2, [X1,X2], _As, _L, St) ->
- %% Note: this is unsafe for non-proper lists! (See make_cons/3).
- %% No safe version is implemented.
- {[join_single(X1, X2)], St};
-remote_call_1(erlang, '--', 2, [X1,_X2], _As, _L, St) ->
- {[X1], St};
-remote_call_1(lists, append, 2, Xs, As, L, St) ->
- remote_call_1(erlang, '++', 2, Xs, As, L, St);
-remote_call_1(lists, subtract, 2, Xs, As, L, St) ->
- remote_call_1(erlang, '--', 2, Xs, As, L, St);
-remote_call_1(M, F, A, Xs, _As, _L, St0) ->
- St1 = case is_escape_op(M, F, A) of
- [] -> St0;
- Ns -> escape(Xs, Ns, St0)
- end,
- case is_imm_op(M, F, A) of
- true ->
- {[empty()], St1};
- false ->
- call_unknown(Xs, St1)
- end.
-
-%% 1-based n:th-element list selector and update function.
-
-nth(1, [X | _Xs]) -> X;
-nth(N, [_X | Xs]) when N > 1 -> nth(N - 1, Xs).
-
-set_nth(1, Y, [_X | Xs]) -> [Y | Xs];
-set_nth(N, Y, [X | Xs]) when N > 1 -> [X | set_nth(N - 1, Y, Xs)].
-
-%% Domain: none | [V], where V = {S, none} | {S, [V]}, S = set(integer()).
-
-join(none, Xs2) -> Xs2;
-join(Xs1, none) -> Xs1;
-join(Xs1, Xs2) ->
- if length(Xs1) =:= length(Xs2) ->
- join_1(Xs1, Xs2);
- true ->
- none
- end.
-
-join_1([X1 | Xs1], [X2 | Xs2]) ->
- [join_single(X1, X2) | join_1(Xs1, Xs2)];
-join_1([], []) ->
- [].
-
-join_list([Xs | Xss]) ->
- join(Xs, join_list(Xss));
-join_list([]) ->
- none.
-
-empty() -> {set__new(), []}.
-
-singleton(X) -> {set__singleton(X), []}.
-
-struct(X, Xs) -> {set__singleton(X), Xs}.
-
-elements({_, Xs}) -> Xs.
-
-unsafe() -> {set__singleton(unsafe), none}.
-
-equal(none, none) -> true;
-equal(none, _) -> false;
-equal(_, none) -> false;
-equal(X1, X2) -> equal_1(X1, X2).
-
-equal_1([X1 | Xs1], [X2 | Xs2]) ->
- equal_single(X1, X2) andalso equal_1(Xs1, Xs2);
-equal_1([], []) -> true;
-equal_1(_, _) -> false.
-
-equal_single({S1, none}, {S2, none}) ->
- set__equal(S1, S2);
-equal_single({_, none}, _) ->
- false;
-equal_single(_, {_, none}) ->
- false;
-equal_single({S1, Vs1}, {S2, Vs2}) ->
- set__equal(S1, S2) andalso equal_single_lists(Vs1, Vs2).
-
-equal_single_lists([X1 | Xs1], [X2 | Xs2]) ->
- equal_single(X1, X2) andalso equal_single_lists(Xs1, Xs2);
-equal_single_lists([], []) ->
- true;
-equal_single_lists(_, _) ->
- false.
-
-join_single({S, none}, V) ->
- {set__union(S, labels(V)), none};
-join_single(V, {S, none}) ->
- {set__union(S, labels(V)), none};
-join_single({S1, Vs1}, {S2, Vs2}) ->
- {set__union(S1, S2), join_single_lists(Vs1, Vs2)}.
-
-join_single_list([V | Vs]) ->
- join_single(V, join_single_list(Vs));
-join_single_list([]) ->
- empty().
-
-%% If one list has more elements that the other, and N is the length of
-%% the longer list, then the result has N elements.
-
-join_single_lists([V1], [V2]) ->
- [join_single(V1, V2)];
-join_single_lists([V1 | Vs1], [V2 | Vs2]) ->
- [join_single(V1, V2) | join_single_lists(Vs1, Vs2)];
-join_single_lists([], Vs) -> Vs;
-join_single_lists(Vs, []) -> Vs.
-
-collapse(V) ->
- {labels(V), none}.
-
-%% collapse_list([]) ->
-%% empty();
-%% collapse_list(Vs) ->
-%% {labels_list(Vs), none}.
-
-labels({S, none}) -> S;
-labels({S, []}) -> S;
-labels({S, Vs}) -> set__union(S, labels_list(Vs)).
-
-labels_list([V]) ->
- labels(V);
-labels_list([V | Vs]) ->
- set__union(labels(V), labels_list(Vs)).
-
-limit(none, _K) -> none;
-limit(X, K) -> limit_list(X, K).
-
-limit_list([X | Xs], K) ->
- [limit_single(X, K) | limit_list(Xs, K)];
-limit_list([], _) ->
- [].
-
-limit_single({_, none} = V, _K) ->
- V;
-limit_single({_, []} = V, _K) ->
- V;
-limit_single({S, Vs}, K) when K > 0 ->
- {S, limit_list(Vs, K - 1)};
-limit_single(V, _K) ->
- collapse(V).
-
-%% Set abstraction for label sets in the domain.
-
-%% set__is_empty([]) -> true;
-%% set__is_empty(_) -> false.
-
-set__new() -> [].
-
-set__singleton(X) -> [X].
-
-set__to_list(S) -> S.
-
-%% set__from_list(S) -> ordsets:from_list(S).
-
-set__union(X, Y) -> ordsets:union(X, Y).
-
-set__add(X, S) -> ordsets:add_element(X, S).
-
-set__is_member(X, S) -> ordsets:is_element(X, S).
-
-%% set__subtract(X, Y) -> ordsets:subtract(X, Y).
-
-set__equal(X, Y) -> X =:= Y.
-
-%% A simple but efficient functional queue.
-
-queue__new() -> {[], []}.
-
-queue__put(X, {In, Out}) -> {[X | In], Out}.
-
-queue__get({In, [X | Out]}) -> {ok, X, {In, Out}};
-queue__get({[], _}) -> empty;
-queue__get({In, _}) ->
- [X | In1] = lists:reverse(In),
- {ok, X, {[], In1}}.
-
-%% The work list - a queue without repeated elements.
-
-init_work() ->
- {queue__new(), sets:new()}.
-
-add_work(Ls, {Q, Set}) ->
- add_work(Ls, Q, Set).
-
-%% Note that the elements are enqueued in order.
-
-add_work([L | Ls], Q, Set) ->
- case sets:is_element(L, Set) of
- true ->
- add_work(Ls, Q, Set);
- false ->
- add_work(Ls, queue__put(L, Q), sets:add_element(L, Set))
- end;
-add_work([], Q, Set) ->
- {Q, Set}.
-
-take_work({Queue0, Set0}) ->
- case queue__get(Queue0) of
- {ok, L, Queue1} ->
- Set1 = sets:del_element(L, Set0),
- {ok, L, {Queue1, Set1}};
- empty ->
- none
- end.
-
-get_deps(L, Dep) ->
- case dict:find(L, Dep) of
- {ok, Ls} -> Ls;
- error -> []
- end.
-
-%% Escape operators may let their arguments escape. For this analysis,
-%% only send-operations are considered as causing escapement, and only
-%% in specific arguments.
-
-%% is_escape_op(_F, _A) -> [].
-
--spec is_escape_op(atom(), atom(), arity()) -> [arity()].
-
-is_escape_op(erlang, '!', 2) -> [2];
-is_escape_op(erlang, send, 2) -> [2];
-is_escape_op(erlang, spawn, 1) -> [1];
-is_escape_op(erlang, spawn, 3) -> [3];
-is_escape_op(erlang, spawn, 4) -> [4];
-is_escape_op(erlang, spawn_link, 3) -> [3];
-is_escape_op(erlang, spawn_link, 4) -> [4];
-is_escape_op(_M, _F, _A) -> [].
-
-%% "Immediate" operators will never return heap allocated data. This is
-%% of course true for operators that never return, like 'exit/1'. (Note
-%% that floats are always heap allocated objects, and that most integer
-%% arithmetic can return a bignum on the heap.)
-
--spec is_imm_op(atom(), arity()) -> boolean().
-
-is_imm_op(match_fail, 1) -> true;
-is_imm_op(_, _) -> false.
-
--spec is_imm_op(atom(), atom(), arity()) -> boolean().
-
-is_imm_op(erlang, self, 0) -> true;
-is_imm_op(erlang, '=:=', 2) -> true;
-is_imm_op(erlang, '==', 2) -> true;
-is_imm_op(erlang, '=/=', 2) -> true;
-is_imm_op(erlang, '/=', 2) -> true;
-is_imm_op(erlang, '<', 2) -> true;
-is_imm_op(erlang, '=<', 2) -> true;
-is_imm_op(erlang, '>', 2) -> true;
-is_imm_op(erlang, '>=', 2) -> true;
-is_imm_op(erlang, 'and', 2) -> true;
-is_imm_op(erlang, 'or', 2) -> true;
-is_imm_op(erlang, 'xor', 2) -> true;
-is_imm_op(erlang, 'not', 1) -> true;
-is_imm_op(erlang, is_alive, 0) -> true;
-is_imm_op(erlang, is_atom, 1) -> true;
-is_imm_op(erlang, is_binary, 1) -> true;
-is_imm_op(erlang, is_builtin, 3) -> true;
-is_imm_op(erlang, is_float, 1) -> true;
-is_imm_op(erlang, is_function, 1) -> true;
-is_imm_op(erlang, is_integer, 1) -> true;
-is_imm_op(erlang, is_list, 1) -> true;
-is_imm_op(erlang, is_number, 1) -> true;
-is_imm_op(erlang, is_pid, 1) -> true;
-is_imm_op(erlang, is_port, 1) -> true;
-is_imm_op(erlang, is_process_alive, 1) -> true;
-is_imm_op(erlang, is_reference, 1) -> true;
-is_imm_op(erlang, is_tuple, 1) -> true;
-is_imm_op(erlang, length, 1) -> true; % never a bignum
-is_imm_op(erlang, list_to_atom, 1) -> true;
-is_imm_op(erlang, node, 0) -> true;
-is_imm_op(erlang, node, 1) -> true;
-is_imm_op(erlang, throw, 1) -> true;
-is_imm_op(erlang, exit, 1) -> true;
-is_imm_op(erlang, error, 1) -> true;
-is_imm_op(erlang, error, 2) -> true;
-is_imm_op(_M, _F, _A) -> false.
diff --git a/lib/hipe/cerl/erl_bif_types.erl b/lib/hipe/cerl/erl_bif_types.erl
index 5fda857bf1..bfffb8db41 100644
--- a/lib/hipe/cerl/erl_bif_types.erl
+++ b/lib/hipe/cerl/erl_bif_types.erl
@@ -585,6 +585,13 @@ type(erlang, float, 1, Xs, Opaques) ->
%% Guard bif, needs to be here.
type(erlang, floor, 1, Xs, Opaques) ->
strict(erlang, floor, 1, Xs, fun (_) -> t_integer() end, Opaques);
+%% Primop, needs to be somewhere.
+type(erlang, build_stacktrace, 0, _, _Opaques) ->
+ t_list(t_tuple([t_module(),
+ t_atom(),
+ t_sup([t_arity(),t_list()]),
+ t_list(t_sup([t_tuple([t_atom('file'),t_string()]),
+ t_tuple([t_atom('line'),t_pos_integer()])]))]));
%% Guard bif, needs to be here.
type(erlang, hd, 1, Xs, Opaques) ->
strict(erlang, hd, 1, Xs, fun ([X]) -> t_cons_hd(X) end, Opaques);
@@ -2336,6 +2343,9 @@ arg_types(erlang, float, 1) ->
%% Guard bif, needs to be here.
arg_types(erlang, floor, 1) ->
[t_number()];
+%% Primop, needs to be somewhere.
+arg_types(erlang, build_stacktrace, 0) ->
+ [];
%% Guard bif, needs to be here.
arg_types(erlang, hd, 1) ->
[t_cons()];
diff --git a/lib/hipe/doc/src/hipe_app.xml b/lib/hipe/doc/src/hipe_app.xml
index aaeb06193d..fc42ecd97d 100644
--- a/lib/hipe/doc/src/hipe_app.xml
+++ b/lib/hipe/doc/src/hipe_app.xml
@@ -99,6 +99,41 @@
each mode.</p>
</item>
+ <tag>Optimization for <c>receive</c> with unique references</tag>
+ <item>
+ <p>
+ The BEAM compiler can do an optimization when a receive
+ statement is only waiting for messages containing a reference
+ created before the receive. All messages that existed in the
+ queue when the reference was created will be bypassed, as they
+ cannot possibly contain the reference. HiPE currently has an
+ optimization similar this, but it is not guaranteed to
+ bypass all messages. In the worst case scenario it, cannot
+ bypass any messages at all.
+ </p>
+ <p>
+ An example of this is when <c>gen_server:call()</c> waits for
+ the reply message.
+ </p>
+ </item>
+
+ </taglist>
+ </section>
+ <section>
+ <title>Stability Issues</title>
+ <taglist>
+ <tag>Not yielding in <c>receive</c> statements</tag>
+ <item>
+ <p>HiPE will not yield in <c>receive</c> statements where
+ appropriate. If a process have lots of signals in its signal
+ queue and execute a HiPE compiled <c>receive</c> statement,
+ the scheduler thread performing the execution may be stuck
+ in the <c>receive</c> statement for a very long time. This
+ can in turn cause various severe issues such as for example
+ prevent the runtime system from being able to release
+ memory.
+ </p>
+ </item>
</taglist>
</section>
<section>
diff --git a/lib/hipe/doc/src/notes.xml b/lib/hipe/doc/src/notes.xml
index bad0c254ce..c190a89260 100644
--- a/lib/hipe/doc/src/notes.xml
+++ b/lib/hipe/doc/src/notes.xml
@@ -31,6 +31,45 @@
</header>
<p>This document describes the changes made to HiPE.</p>
+<section><title>Hipe 3.17.1</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Fix HiPE bug for binary constructs like
+ <c>&lt;&lt;X/utf8&gt;&gt;</c> which could in rare cases
+ cause faulty results or VM crash.</p>
+ <p>
+ This fix affects both the <c>hipe</c> compiler and
+ <c>erts</c> runtime in an <em>incompatible</em> way. Old
+ hipe compiled files need to be recompiled to load and run
+ properly as native.</p>
+ <p>
+ *** POTENTIAL INCOMPATIBILITY ***</p>
+ <p>
+ Own Id: OTP-14850 Aux Id: PR-1664 </p>
+ </item>
+ <item>
+ <p>The BEAM compiler chooses not to perform tailcall
+ optimisations for some calls in tail position, for
+ example to some built-in functions. However, when the
+ ErLLVM HiPE backend is used, LLVM may choose to perform
+ tailcall optimisation on these calls, breaking the
+ expected semantics.</p>
+ <p>To preserve the precise semantics exhibited by BEAM,
+ the 'notail' marker, present in LLVM since version 3.8,
+ is added to call instructions that BEAM has not turned
+ into tail calls, which inhibits LLVM from performing
+ tail-call optimisation in turn.</p>
+ <p>
+ Own Id: OTP-14886</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Hipe 3.17</title>
<section><title>Fixed Bugs and Malfunctions</title>
diff --git a/lib/hipe/icode/hipe_beam_to_icode.erl b/lib/hipe/icode/hipe_beam_to_icode.erl
index 6e66ec057c..f429d40272 100644
--- a/lib/hipe/icode/hipe_beam_to_icode.erl
+++ b/lib/hipe/icode/hipe_beam_to_icode.erl
@@ -605,6 +605,16 @@ trans_fun([{get_list,List,Head,Tail}|Instructions], Env) ->
?error_msg("hd and tl regs identical in get_list~n",[]),
erlang:error(not_handled)
end;
+%%--- get_hd ---
+trans_fun([{get_hd,List,Head}|Instructions], Env) ->
+ TransList = [trans_arg(List)],
+ I = hipe_icode:mk_primop([mk_var(Head)],unsafe_hd,TransList),
+ [I | trans_fun(Instructions,Env)];
+%%--- get_tl ---
+trans_fun([{get_tl,List,Tail}|Instructions], Env) ->
+ TransList = [trans_arg(List)],
+ I = hipe_icode:mk_primop([mk_var(Tail)],unsafe_tl,TransList),
+ [I | trans_fun(Instructions,Env)];
%%--- get_tuple_element ---
trans_fun([{get_tuple_element,Xreg,Index,Dst}|Instructions], Env) ->
I = hipe_icode:mk_primop([mk_var(Dst)],
diff --git a/lib/hipe/icode/hipe_icode_inline_bifs.erl b/lib/hipe/icode/hipe_icode_inline_bifs.erl
index 7a6947f190..16a95991e7 100644
--- a/lib/hipe/icode/hipe_icode_inline_bifs.erl
+++ b/lib/hipe/icode/hipe_icode_inline_bifs.erl
@@ -24,8 +24,9 @@
%% Currently inlined BIFs:
%% and, or, xor, not, <, >, >=, =<, ==, /=, =/=, =:=
-%% is_atom, is_boolean, is_binary, is_float, is_function,
-%% is_integer, is_list, is_pid, is_port, is_reference, is_tuple
+%% is_atom, is_binary, is_bitstring, is_boolean, is_float,
+%% is_function, is_integer, is_list, is_map, is_number,
+%% is_pid, is_port, is_reference, is_tuple
-module(hipe_icode_inline_bifs).
@@ -116,17 +117,20 @@ try_type_tests(I) -> I.
is_type_test(Name) ->
case Name of
- is_integer -> {true, integer};
+ is_atom -> {true, atom};
+ is_binary -> {true, binary};
+ is_bitstring -> {true, bitstr};
+ is_boolean -> {true, boolean};
is_float -> {true, float};
- is_tuple -> {true, tuple};
- is_binary -> {true, binary};
+ is_function -> {true, function};
+ is_integer -> {true, integer};
is_list -> {true, list};
+ is_map -> {true, map};
+ is_number -> {true, number};
is_pid -> {true, pid};
- is_atom -> {true, atom};
- is_boolean -> {true, boolean};
- is_function -> {true, function};
- is_reference -> {true, reference};
is_port -> {true, port};
+ is_reference -> {true, reference};
+ is_tuple -> {true, tuple};
_ -> false
end.
diff --git a/lib/hipe/main/hipe.app.src b/lib/hipe/main/hipe.app.src
index 1138d72dd2..7350e873aa 100644
--- a/lib/hipe/main/hipe.app.src
+++ b/lib/hipe/main/hipe.app.src
@@ -26,7 +26,6 @@
cerl_closurean,
cerl_hipeify,
cerl_lib,
- cerl_messagean,
cerl_pmatch,
cerl_prettypr,
cerl_to_icode,
diff --git a/lib/hipe/main/hipe_main.erl b/lib/hipe/main/hipe_main.erl
index 4b5eb4c63e..6e48f0cffd 100644
--- a/lib/hipe/main/hipe_main.erl
+++ b/lib/hipe/main/hipe_main.erl
@@ -410,9 +410,9 @@ icode_to_rtl(MFA, Icode, Options, Servers) ->
hipe_llvm_liveness:analyze(RtlCfg4)
end,
pp(RtlCfg5, MFA, rtl, pp_rtl, Options, Servers),
- case proplists:get_bool(verify_gcsafe, Options) of
- false -> ok;
- true ->
+ case proplists:get_bool(no_verify_gcsafe, Options) of
+ true -> ok;
+ false ->
ok = hipe_rtl_verify_gcsafe:check(RtlCfg5)
end,
LinearRTL1 = hipe_rtl_cfg:linearize(RtlCfg5),
diff --git a/lib/hipe/rtl/hipe_rtl_verify_gcsafe.erl b/lib/hipe/rtl/hipe_rtl_verify_gcsafe.erl
index c3f20bfec1..01d7e89ccd 100644
--- a/lib/hipe/rtl/hipe_rtl_verify_gcsafe.erl
+++ b/lib/hipe/rtl/hipe_rtl_verify_gcsafe.erl
@@ -76,6 +76,7 @@ safe_primop(bs_allocate) -> true;
safe_primop(bs_reallocate) -> true;
safe_primop(bs_utf8_size) -> true;
safe_primop(bs_get_utf8) -> true;
+safe_primop(bs_put_utf8) -> true;
safe_primop(bs_utf16_size) -> true;
safe_primop(bs_get_utf16) -> true;
safe_primop(bs_validate_unicode_retract) -> true;
diff --git a/lib/hipe/vsn.mk b/lib/hipe/vsn.mk
index 508ec00548..0c517f9a7a 100644
--- a/lib/hipe/vsn.mk
+++ b/lib/hipe/vsn.mk
@@ -1 +1 @@
-HIPE_VSN = 3.17
+HIPE_VSN = 3.17.1
diff --git a/lib/hipe/x86/hipe_rtl_to_x86.erl b/lib/hipe/x86/hipe_rtl_to_x86.erl
index 31e4f6e4ac..22947da148 100644
--- a/lib/hipe/x86/hipe_rtl_to_x86.erl
+++ b/lib/hipe/x86/hipe_rtl_to_x86.erl
@@ -646,7 +646,7 @@ conv_imm(Opnd, Map) ->
is_imm64(Value) when is_integer(Value) ->
(Value < -(1 bsl (32 - 1))) or (Value > (1 bsl (32 - 1)) - 1);
is_imm64({_,atom}) -> false; % Atoms are 32 bits.
-is_imm64({_,c_const}) -> false; % c_consts are 32 bits.
+is_imm64({_,c_const}) -> true; % c_consts are 64 bits.
is_imm64({_,_}) -> true . % Other relocs are 64 bits.
-else.
conv_imm(Opnd, Map) ->
@@ -777,6 +777,18 @@ conv_fconv(Dst, Src) ->
%%% Finalise the conversion of a 2-address FP operation.
+-ifdef(HIPE_AMD64).
+conv_fp_unary(Dst, Src, 'fchs') ->
+ Tmp = new_untagged_temp(),
+ case same_opnd(Dst, Src) of
+ true ->
+ [];
+ _ ->
+ [hipe_x86:mk_fmove(Src, Dst)]
+ end ++
+ mk_load_address(c_const, hipe_x86:mk_imm({sse2_fnegate_mask, c_const}), Tmp) ++
+ [hipe_x86:mk_fp_binop('xorpd', hipe_x86:mk_mem(Tmp, hipe_x86:mk_imm(0), double), Dst)].
+-else.
conv_fp_unary(Dst, Src, FpUnOp) ->
case same_opnd(Dst, Src) of
true ->
@@ -785,6 +797,7 @@ conv_fp_unary(Dst, Src, FpUnOp) ->
[hipe_x86:mk_fmove(Src, Dst),
hipe_x86:mk_fp_unop(FpUnOp, Dst)]
end.
+-endif.
conv_fp_unop(RtlFpUnOp) ->
case RtlFpUnOp of
@@ -854,13 +867,8 @@ mk_jmp_switch(Index, JTabLab, Labels) ->
%%% Finalise the translation of a load_address instruction.
-ifdef(HIPE_AMD64).
-mk_load_address(Type, Src, Dst) ->
- case Type of
- c_const -> % 32 bits
- [hipe_x86:mk_move(Src, Dst)];
- _ ->
- [hipe_x86:mk_move64(Src, Dst)]
- end.
+mk_load_address(_Type, Src, Dst) ->
+ [hipe_x86:mk_move64(Src, Dst)].
-else.
mk_load_address(_Type, Src, Dst) ->
[hipe_x86:mk_move(Src, Dst)].
diff --git a/lib/hipe/x86/hipe_x86_assemble.erl b/lib/hipe/x86/hipe_x86_assemble.erl
index 50919bdf4e..9d2586a14d 100644
--- a/lib/hipe/x86/hipe_x86_assemble.erl
+++ b/lib/hipe/x86/hipe_x86_assemble.erl
@@ -735,6 +735,7 @@ resolve_sse2_op(Op) ->
fdiv -> divsd;
fmul -> mulsd;
fsub -> subsd;
+ xorpd -> xorpd;
_ -> exit({?MODULE, unknown_sse2_operator, Op})
end.
diff --git a/lib/inets/doc/src/http_client.xml b/lib/inets/doc/src/http_client.xml
index 212958f17f..15e383ec77 100644
--- a/lib/inets/doc/src/http_client.xml
+++ b/lib/inets/doc/src/http_client.xml
@@ -97,27 +97,32 @@
7 > {ok, {{NewVersion, 200, NewReasonPhrase}, NewHeaders, NewBody}} =
httpc:request(get, {"http://www.erlang.org", [{"connection", "close"}]},
[], []).</code>
-
+ <p>This sends an HTTP request over a unix domain socket (experimental):</p>
+ <code type="erl">
+ 8 > httpc:set_options([{ipfamily, local},
+ {unix_socket,"/tmp/unix_socket/consul_http.sock"}]).
+ 9 > {ok, {{NewVersion, 200, NewReasonPhrase}, NewHeaders, NewBody}} =
+ httpc:request(put, {"http:///v1/kv/foo", [], [], "hello"}, [], []).</code>
<p>Start an HTTP client profile:</p>
<code><![CDATA[
- 8 > {ok, Pid} = inets:start(httpc, [{profile, foo}]).
+ 10 > {ok, Pid} = inets:start(httpc, [{profile, foo}]).
{ok, <0.45.0>}
]]></code>
<p>The new profile has no proxy settings, so the connection is refused:</p>
<code type="erl">
- 9 > httpc:request("http://www.erlang.org", foo).
+ 11 > httpc:request("http://www.erlang.org", foo).
{error, econnrefused}</code>
<p>Stop the HTTP client profile:</p>
<code type="erl">
- 10 > inets:stop(httpc, foo).
+ 12 > inets:stop(httpc, foo).
ok</code>
<p>Alternative way to stop the HTTP client profile:</p>
<code type="erl">
- 10 > inets:stop(httpc, Pid).
+ 13 > inets:stop(httpc, Pid).
ok</code>
</section>
diff --git a/lib/inets/doc/src/http_uri.xml b/lib/inets/doc/src/http_uri.xml
index 20c042c202..f57214a7ce 100644
--- a/lib/inets/doc/src/http_uri.xml
+++ b/lib/inets/doc/src/http_uri.xml
@@ -45,7 +45,6 @@
this module:</p>
<p><c>boolean() = true | false</c></p>
<p><c>string()</c> = list of ASCII characters</p>
- <p><c>unicode_binary()</c> = binary() with characters encoded in the UTF-8 coding standard</p>
</section>
@@ -54,22 +53,22 @@
<p>Type definitions that are related to URI:</p>
<taglist>
- <tag><c>uri() = string() | unicode:unicode_binary()</c></tag>
+ <tag><c>uri() = string() | binary()</c></tag>
<item><p>Syntax according to the URI definition in RFC 3986,
for example, "http://www.erlang.org/"</p></item>
- <tag><c>user_info() = string() | unicode:unicode_binary()</c></tag>
+ <tag><c>user_info() = string() | binary()</c></tag>
<item><p></p></item>
<tag><c>scheme() = atom()</c></tag>
<item><p>Example: http, https</p></item>
- <tag><c>host() = string() | unicode:unicode_binary()</c></tag>
+ <tag><c>host() = string() | binary()</c></tag>
<item><p></p></item>
- <tag><c>port() = pos_integer()</c></tag>
+ <tag><c>port() = inet:port_number()</c></tag>
<item><p></p></item>
- <tag><c>path() = string() | unicode:unicode_binary()</c></tag>
+ <tag><c>path() = string() | binary()</c></tag>
<item><p>Represents a file path or directory path</p></item>
- <tag><c>query() = string() | unicode:unicode_binary()</c></tag>
+ <tag><c>query() = string() | binary()</c></tag>
<item><p></p></item>
- <tag><c>fragment() = string() | unicode:unicode_binary()</c></tag>
+ <tag><c>fragment() = string() | binary()</c></tag>
<item><p></p></item>
</taglist>
@@ -84,7 +83,7 @@
<fsummary>Decodes a hexadecimal encoded URI.</fsummary>
<type>
- <v>HexEncodedURI = string() | unicode:unicode_binary() - A possibly hexadecimal encoded URI</v>
+ <v>HexEncodedURI = string() | binary() - A possibly hexadecimal encoded URI</v>
<v>URI = uri()</v>
</type>
@@ -99,7 +98,7 @@
<fsummary>Encodes a hexadecimal encoded URI.</fsummary>
<type>
<v>URI = uri()</v>
- <v>HexEncodedURI = string() | unicode:unicode_binary() - Hexadecimal encoded URI</v>
+ <v>HexEncodedURI = string() | binary() - Hexadecimal encoded URI</v>
</type>
<desc>
@@ -119,12 +118,13 @@
<v>Option = {ipv6_host_with_brackets, boolean()} |
{scheme_defaults, scheme_defaults()} |
{fragment, boolean()} |
- {scheme_validation_fun, fun()}]</v>
+ {scheme_validation_fun, fun()}</v>
<v>Result = {Scheme, UserInfo, Host, Port, Path, Query} |
{Scheme, UserInfo, Host, Port, Path, Query, Fragment}</v>
+ <v>Scheme = scheme()</v>
<v>UserInfo = user_info()</v>
<v>Host = host()</v>
- <v>Port = pos_integer()</v>
+ <v>Port = inet:port_number()</v>
<v>Path = path()</v>
<v>Query = query()</v>
<v>Fragment = fragment()</v>
@@ -146,13 +146,20 @@
<p>Scheme validation fun is to be defined as follows:</p>
<code>
-fun(SchemeStr :: string() | unicode:unicode_binary()) ->
+fun(SchemeStr :: string() | binary()) ->
valid | {error, Reason :: term()}.
</code>
<p>It is called before scheme string gets converted into scheme atom and
thus possible atom leak could be prevented</p>
+ <warning>
+ <p>The scheme portion of the URI gets converted into atom,
+ meaning that atom leak may occur. Specifying a scheme
+ validation fun is recommended unless the URI is already
+ sanitized.</p>
+ </warning>
+
<marker id="encode"></marker>
</desc>
</func>
@@ -162,7 +169,7 @@ fun(SchemeStr :: string() | unicode:unicode_binary()) ->
<fsummary>A list of the scheme and their default ports.</fsummary>
<type>
<v>SchemeDefaults = [{scheme(), default_scheme_port_number()}] </v>
- <v>default_scheme_port_number() = pos_integer()</v>
+ <v>default_scheme_port_number() = inet:port_number()</v>
</type>
<desc>
<p>Provides a list of the scheme and their default
diff --git a/lib/inets/doc/src/httpc.xml b/lib/inets/doc/src/httpc.xml
index 58714328c5..14662f257c 100644
--- a/lib/inets/doc/src/httpc.xml
+++ b/lib/inets/doc/src/httpc.xml
@@ -210,7 +210,8 @@
ip |
port |
socket_opts |
- verbose</v>
+ verbose |
+ unix_socket</v>
<v>Profile = profile() | pid()</v>
<d>When started <c>stand_alone</c> only the pid can used.</d>
<v>Values = [{option_item(), term()}]</v>
@@ -297,8 +298,8 @@
{full_result, boolean()} |
{headers_as_is, boolean() |
{socket_opts, socket_opts()} |
- {receiver, receiver()},
- {ipv6_host_with_brackets, boolean()}}</v>
+ {receiver, receiver()} |
+ {ipv6_host_with_brackets, boolean()}</v>
<v>stream_to() = none | self | {self, once} | filename()</v>
<v>socket_opts() = [socket_opt()]</v>
<v>receiver() = pid() | function()/1 | {Module, Function, Args}</v>
@@ -533,7 +534,8 @@
<v>| {ip, IpAddress}</v>
<v>| {port, Port}</v>
<v>| {socket_opts, socket_opts()}</v>
- <v>| {verbose, VerboseMode}</v>
+ <v>| {verbose, VerboseMode}</v>
+ <v>| {unix_socket, UnixSocket}</v>
<v>Proxy = {Hostname, Port}</v>
<v>Hostname = string()</v>
<d>Example: "localhost" or "foo.bar.se"</d>
@@ -576,7 +578,7 @@
If option <c>verify</c> is used, function <c>store_cookies/2</c>
has to be called for the cookies to be saved.
Default is <c>disabled</c>.</d>
- <v>IpFamily = inet | inet6 </v>
+ <v>IpFamily = inet | inet6 | local</v>
<d>Default is <c>inet</c>.</d>
<v>IpAddress = ip_address()</v>
<d>If the host has several network interfaces, this option specifies
@@ -601,6 +603,12 @@
It is a debug feature.</d>
<v>Profile = profile() | pid()</v>
<d>When started <c>stand_alone</c> only the pid can be used.</d>
+ <v>UnixSocket = path()</v>
+ <d>
+ Experimental option for sending HTTP requests over a unix domain socket. The value
+ of <c>unix_socket</c> shall be the full path to a unix domain socket file with read/write
+ permissions for the erlang process. Default is <c>undefined</c>.
+ </d>
</type>
<desc>
<p>Sets options to be used for subsequent requests.</p>
diff --git a/lib/inets/doc/src/notes.xml b/lib/inets/doc/src/notes.xml
index 70b2811c0e..0417e07de8 100644
--- a/lib/inets/doc/src/notes.xml
+++ b/lib/inets/doc/src/notes.xml
@@ -33,7 +33,37 @@
<file>notes.xml</file>
</header>
- <section><title>Inets 6.4.5</title>
+ <section><title>Inets 6.5</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ httpc_manager crashes when a long running request is sent
+ on a persistent HTTP connection (keep-alive). Fixed
+ httpc_manager to use proper timeouts on keep-alive
+ connections.</p>
+ <p>
+ Own Id: OTP-14908</p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Add support for unix domain sockets in the http client.</p>
+ <p>
+ Own Id: OTP-14854</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Inets 6.4.5</title>
<section><title>Fixed Bugs and Malfunctions</title>
<list>
diff --git a/lib/inets/src/http_client/httpc.erl b/lib/inets/src/http_client/httpc.erl
index 821eb7f02f..a73503a5ce 100644
--- a/lib/inets/src/http_client/httpc.erl
+++ b/lib/inets/src/http_client/httpc.erl
@@ -171,6 +171,7 @@ request(Method,
HTTPOptions, Options, Profile)
when (Method =:= options) orelse
(Method =:= get) orelse
+ (Method =:= put) orelse
(Method =:= head) orelse
(Method =:= delete) orelse
(Method =:= trace) andalso
@@ -531,6 +532,7 @@ handle_request(Method, Url,
Stream = proplists:get_value(stream, Options),
Receiver = proplists:get_value(receiver, Options),
SocketOpts = proplists:get_value(socket_opts, Options),
+ UnixSocket = proplists:get_value(unix_socket, Options),
BracketedHost = proplists:get_value(ipv6_host_with_brackets,
Options),
@@ -558,6 +560,7 @@ handle_request(Method, Url,
headers_as_is = headers_as_is(Headers0, Options),
socket_opts = SocketOpts,
started = Started,
+ unix_socket = UnixSocket,
ipv6_host_with_brackets = BracketedHost},
case httpc_manager:request(Request, profile_name(Profile)) of
{ok, RequestId} ->
@@ -823,7 +826,7 @@ request_options_defaults() ->
error
end,
- VerifyBrackets = VerifyBoolean,
+ VerifyBrackets = VerifyBoolean,
[
{sync, true, VerifySync},
@@ -894,11 +897,36 @@ request_options_sanity_check(Opts) ->
end,
ok.
-validate_options(Options) ->
- (catch validate_options(Options, [])).
-
-validate_options([], ValidateOptions) ->
- {ok, lists:reverse(ValidateOptions)};
+validate_ipfamily_unix_socket(Options0) ->
+ IpFamily = proplists:get_value(ipfamily, Options0, inet),
+ UnixSocket = proplists:get_value(unix_socket, Options0, undefined),
+ Options1 = proplists:delete(ipfamily, Options0),
+ Options2 = proplists:delete(ipfamily, Options1),
+ validate_ipfamily_unix_socket(IpFamily, UnixSocket, Options2,
+ [{ipfamily, IpFamily}, {unix_socket, UnixSocket}]).
+%%
+validate_ipfamily_unix_socket(local, undefined, _Options, _Acc) ->
+ bad_option(unix_socket, undefined);
+validate_ipfamily_unix_socket(IpFamily, UnixSocket, _Options, _Acc)
+ when IpFamily =/= local, UnixSocket =/= undefined ->
+ bad_option(ipfamily, IpFamily);
+validate_ipfamily_unix_socket(IpFamily, UnixSocket, Options, Acc) ->
+ validate_ipfamily(IpFamily),
+ validate_unix_socket(UnixSocket),
+ {Options, Acc}.
+
+
+validate_options(Options0) ->
+ try
+ {Options, Acc} = validate_ipfamily_unix_socket(Options0),
+ validate_options(Options, Acc)
+ catch
+ error:Reason ->
+ {error, Reason}
+ end.
+%%
+validate_options([], ValidOptions) ->
+ {ok, lists:reverse(ValidOptions)};
validate_options([{proxy, Proxy} = Opt| Tail], Acc) ->
validate_proxy(Proxy),
@@ -958,6 +986,10 @@ validate_options([{verbose, Value} = Opt| Tail], Acc) ->
validate_verbose(Value),
validate_options(Tail, [Opt | Acc]);
+validate_options([{unix_socket, Value} = Opt| Tail], Acc) ->
+ validate_unix_socket(Value),
+ validate_options(Tail, [Opt | Acc]);
+
validate_options([{_, _} = Opt| _], _Acc) ->
{error, {not_an_option, Opt}}.
@@ -1026,7 +1058,8 @@ validate_ipv6(BadValue) ->
bad_option(ipv6, BadValue).
validate_ipfamily(Value)
- when (Value =:= inet) orelse (Value =:= inet6) orelse (Value =:= inet6fb4) ->
+ when (Value =:= inet) orelse (Value =:= inet6) orelse
+ (Value =:= inet6fb4) orelse (Value =:= local) ->
Value;
validate_ipfamily(BadValue) ->
bad_option(ipfamily, BadValue).
@@ -1056,6 +1089,15 @@ validate_verbose(Value)
validate_verbose(BadValue) ->
bad_option(verbose, BadValue).
+validate_unix_socket(Value)
+ when (Value =:= undefined) ->
+ Value;
+validate_unix_socket(Value)
+ when is_list(Value) andalso length(Value) > 0 ->
+ Value;
+validate_unix_socket(BadValue) ->
+ bad_option(unix_socket, BadValue).
+
bad_option(Option, BadValue) ->
throw({error, {bad_option, Option, BadValue}}).
diff --git a/lib/inets/src/http_client/httpc_handler.erl b/lib/inets/src/http_client/httpc_handler.erl
index 1482f4f922..9b09832eb8 100644
--- a/lib/inets/src/http_client/httpc_handler.erl
+++ b/lib/inets/src/http_client/httpc_handler.erl
@@ -711,9 +711,9 @@ do_handle_info({'EXIT', _, _}, State = #state{request = undefined}) ->
%% can retry requests in the pipeline.
do_handle_info({'EXIT', _, _}, State) ->
{noreply, State#state{status = close}}.
-
+
call(Msg, Pid) ->
- try gen_server:call(Pid, Msg)
+ try gen_server:call(Pid, Msg, infinity)
catch
exit:{noproc, _} ->
{error, closed};
@@ -754,6 +754,7 @@ connect(SocketType, ToAddress,
#options{ipfamily = IpFamily,
ip = FromAddress,
port = FromPort,
+ unix_socket = UnixSocket,
socket_opts = Opts0}, Timeout) ->
Opts1 =
case FromPort of
@@ -789,6 +790,16 @@ connect(SocketType, ToAddress,
OK ->
OK
end;
+ local ->
+ Opts3 = [IpFamily | Opts2],
+ SocketAddr = {local, UnixSocket},
+ case http_transport:connect(SocketType, {SocketAddr, 0}, Opts3, Timeout) of
+ {error, Reason} ->
+ {error, {failed_connect, [{to_address, SocketAddr},
+ {IpFamily, Opts3, Reason}]}};
+ Else ->
+ Else
+ end;
_ ->
Opts3 = [IpFamily | Opts2],
case http_transport:connect(SocketType, ToAddress, Opts3, Timeout) of
@@ -800,9 +811,23 @@ connect(SocketType, ToAddress,
end
end.
-connect_and_send_first_request(Address, Request, #state{options = Options} = State) ->
+handle_unix_socket_options(#request{unix_socket = UnixSocket}, Options)
+ when UnixSocket =:= undefined ->
+ Options;
+
+handle_unix_socket_options(#request{unix_socket = UnixSocket},
+ Options = #options{ipfamily = IpFamily}) ->
+ case IpFamily of
+ local ->
+ Options#options{unix_socket = UnixSocket};
+ Else ->
+ error({badarg, [{ipfamily, Else}, {unix_socket, UnixSocket}]})
+ end.
+
+connect_and_send_first_request(Address, Request, #state{options = Options0} = State) ->
SocketType = socket_type(Request),
ConnTimeout = (Request#request.settings)#http_options.connect_timeout,
+ Options = handle_unix_socket_options(Request, Options0),
case connect(SocketType, Address, Options, ConnTimeout) of
{ok, Socket} ->
ClientClose =
@@ -841,9 +866,10 @@ connect_and_send_first_request(Address, Request, #state{options = Options} = Sta
{ok, State#state{request = Request}}
end.
-connect_and_send_upgrade_request(Address, Request, #state{options = Options} = State) ->
+connect_and_send_upgrade_request(Address, Request, #state{options = Options0} = State) ->
ConnTimeout = (Request#request.settings)#http_options.connect_timeout,
SocketType = ip_comm,
+ Options = handle_unix_socket_options(Request, Options0),
case connect(SocketType, Address, Options, ConnTimeout) of
{ok, Socket} ->
SessionType = httpc_manager:session_type(Options),
@@ -1685,9 +1711,8 @@ update_session(ProfileName, #session{id = SessionId} = Session, Pos, Value) ->
insert_session(Session2, ProfileName);
error:badarg ->
{stop, normal};
- T:E ->
+ T:E:Stacktrace ->
%% Unexpected this must be an error!
- Stacktrace = erlang:get_stacktrace(),
error_logger:error_msg("Failed updating session: "
"~n ProfileName: ~p"
"~n SessionId: ~p"
diff --git a/lib/inets/src/http_client/httpc_internal.hrl b/lib/inets/src/http_client/httpc_internal.hrl
index 5f8c70f28d..c5fe439722 100644
--- a/lib/inets/src/http_client/httpc_internal.hrl
+++ b/lib/inets/src/http_client/httpc_internal.hrl
@@ -83,10 +83,11 @@
max_sessions = ?HTTP_MAX_TCP_SESSIONS,
cookies = disabled, % enabled | disabled | verify
verbose = false, % boolean(),
- ipfamily = inet, % inet | inet6 | inet6fb4
+ ipfamily = inet, % inet | inet6 | inet6fb4 | local
ip = default, % specify local interface
port = default, % specify local port
- socket_opts = [] % other socket options
+ socket_opts = [], % other socket options
+ unix_socket = undefined % Local unix socket
}
).
-type options() :: #options{}.
@@ -115,6 +116,7 @@
% request
timer :: undefined | reference(),
socket_opts, % undefined | [socket_option()]
+ unix_socket, % undefined | string()
ipv6_host_with_brackets % boolean()
}
).
diff --git a/lib/inets/src/http_client/httpc_manager.erl b/lib/inets/src/http_client/httpc_manager.erl
index ffdf1603b3..7b8d7875de 100644
--- a/lib/inets/src/http_client/httpc_manager.erl
+++ b/lib/inets/src/http_client/httpc_manager.erl
@@ -553,7 +553,8 @@ handle_cast({set_options, Options}, State = #state{options = OldOptions}) ->
ip = get_ip(Options, OldOptions),
port = get_port(Options, OldOptions),
verbose = get_verbose(Options, OldOptions),
- socket_opts = get_socket_opts(Options, OldOptions)
+ socket_opts = get_socket_opts(Options, OldOptions),
+ unix_socket = get_unix_socket_opts(Options, OldOptions)
},
case {OldOptions#options.verbose, NewOptions#options.verbose} of
{Same, Same} ->
@@ -963,7 +964,10 @@ get_option(ip, #options{ip = IP}) ->
get_option(port, #options{port = Port}) ->
Port;
get_option(socket_opts, #options{socket_opts = SocketOpts}) ->
- SocketOpts.
+ SocketOpts;
+get_option(unix_socket, #options{unix_socket = UnixSocket}) ->
+ UnixSocket.
+
get_proxy(Opts, #options{proxy = Default}) ->
proplists:get_value(proxy, Opts, Default).
@@ -1016,6 +1020,8 @@ get_verbose(Opts, #options{verbose = Default}) ->
get_socket_opts(Opts, #options{socket_opts = Default}) ->
proplists:get_value(socket_opts, Opts, Default).
+get_unix_socket_opts(Opts, #options{unix_socket = Default}) ->
+ proplists:get_value(unix_socket, Opts, Default).
handle_verbose(debug) ->
dbg:p(self(), [call]),
diff --git a/lib/inets/src/http_lib/http_uri.erl b/lib/inets/src/http_lib/http_uri.erl
index 7f1ca02014..bc588fd390 100644
--- a/lib/inets/src/http_lib/http_uri.erl
+++ b/lib/inets/src/http_lib/http_uri.erl
@@ -61,19 +61,35 @@
scheme_defaults/0,
encode/1, decode/1]).
--export_type([scheme/0, default_scheme_port_number/0]).
+-export_type([uri/0,
+ user_info/0,
+ scheme/0, default_scheme_port_number/0,
+ host/0,
+ path/0,
+ query/0,
+ fragment/0]).
+-type uri() :: string() | binary().
+-type user_info() :: string() | binary().
+-type scheme() :: atom().
+-type host() :: string() | binary().
+-type path() :: string() | binary().
+-type query() :: string() | binary().
+-type fragment() :: string() | binary().
+-type port_number() :: inet:port_number().
+-type default_scheme_port_number() :: port_number().
+-type hex_uri() :: string() | binary(). %% Hexadecimal encoded URI.
+-type maybe_hex_uri() :: string() | binary(). %% A possibly hexadecimal encoded URI.
+
+-type scheme_defaults() :: [{scheme(), default_scheme_port_number()}].
+-type scheme_validation_fun() :: fun((SchemeStr :: string() | binary()) ->
+ valid | {error, Reason :: term()}).
%%%=========================================================================
%%% API
%%%=========================================================================
--type scheme() :: atom().
--type default_scheme_port_number() :: pos_integer().
-
--spec scheme_defaults() ->
- [{scheme(), default_scheme_port_number()}].
-
+-spec scheme_defaults() -> scheme_defaults().
scheme_defaults() ->
[{http, 80},
{https, 443},
@@ -82,9 +98,20 @@ scheme_defaults() ->
{sftp, 22},
{tftp, 69}].
+-type parse_result() ::
+ {scheme(), user_info(), host(), port_number(), path(), query()} |
+ {scheme(), user_info(), host(), port_number(), path(), query(),
+ fragment()}.
+
+-spec parse(uri()) -> {ok, parse_result()} | {error, term()}.
parse(AbsURI) ->
parse(AbsURI, []).
+-spec parse(uri(), [Option]) -> {ok, parse_result()} | {error, term()} when
+ Option :: {ipv6_host_with_brackets, boolean()} |
+ {scheme_defaults, scheme_defaults()} |
+ {fragment, boolean()} |
+ {scheme_validation_fun, scheme_validation_fun() | none}.
parse(AbsURI, Opts) ->
case parse_scheme(AbsURI, Opts) of
{error, Reason} ->
@@ -105,6 +132,7 @@ reserved() ->
$#, $[, $], $<, $>, $\", ${, $}, $|, %"
$\\, $', $^, $%, $ ]).
+-spec encode(uri()) -> hex_uri().
encode(URI) when is_list(URI) ->
Reserved = reserved(),
lists:append([uri_encode(Char, Reserved) || Char <- URI]);
@@ -112,6 +140,7 @@ encode(URI) when is_binary(URI) ->
Reserved = reserved(),
<< <<(uri_encode_binary(Char, Reserved))/binary>> || <<Char>> <= URI >>.
+-spec decode(maybe_hex_uri()) -> uri().
decode(String) when is_list(String) ->
do_decode(String);
decode(String) when is_binary(String) ->
@@ -168,7 +197,7 @@ extract_scheme(Str, Opts) ->
{value, {scheme_validation_fun, Fun}} when is_function(Fun) ->
case Fun(Str) of
valid ->
- {ok, list_to_atom(http_util:to_lower(Str))};
+ {ok, to_atom(http_util:to_lower(Str))};
{error, Error} ->
{error, Error}
end;
diff --git a/lib/inets/src/http_server/httpd_response.erl b/lib/inets/src/http_server/httpd_response.erl
index 6b9053fda6..57ce162922 100644
--- a/lib/inets/src/http_server/httpd_response.erl
+++ b/lib/inets/src/http_server/httpd_response.erl
@@ -84,14 +84,14 @@ traverse_modules(ModData,[Module|Rest]) ->
{proceed, NewData} ->
traverse_modules(ModData#mod{data = NewData}, Rest)
catch
- T:E ->
+ T:E:Stacktrace ->
String =
lists:flatten(
io_lib:format("module traverse failed: ~p:do => "
"~n Error Type: ~p"
"~n Error: ~p"
"~n Stack trace: ~p",
- [Module, T, E, ?STACK()])),
+ [Module, T, E, Stacktrace])),
httpd_util:error_log(ModData#mod.config_db, String),
send_status(ModData, 500, none),
done
diff --git a/lib/inets/src/inets_app/inets.appup.src b/lib/inets/src/inets_app/inets.appup.src
index fdf4cc6e07..a86413147c 100644
--- a/lib/inets/src/inets_app/inets.appup.src
+++ b/lib/inets/src/inets_app/inets.appup.src
@@ -18,14 +18,10 @@
%% %CopyrightEnd%
{"%VSN%",
[
- {<<"6.4.3">>, [{load_module, httpd_esi,
- soft_purge, soft_purge, []}]},
{<<"6\\..*">>,[{restart_application, inets}]},
{<<"5\\..*">>,[{restart_application, inets}]}
],
[
- {<<"6.4.3">>, [{load_module, httpd_esi,
- soft_purge, soft_purge, []}]},
{<<"6\\..*">>,[{restart_application, inets}]},
{<<"5\\..*">>,[{restart_application, inets}]}
]
diff --git a/lib/inets/src/inets_app/inets_internal.hrl b/lib/inets/src/inets_app/inets_internal.hrl
index 079b415b56..e0f59bba5f 100644
--- a/lib/inets/src/inets_app/inets_internal.hrl
+++ b/lib/inets/src/inets_app/inets_internal.hrl
@@ -22,8 +22,6 @@
-ifndef(inets_internal_hrl).
-define(inets_internal_hrl, true).
--define(STACK(), erlang:get_stacktrace()).
-
%% Various trace macros
-define(report(Severity, Label, Service, Content),
diff --git a/lib/inets/test/http_test_lib.erl b/lib/inets/test/http_test_lib.erl
index 38e9e4976e..4e119cce04 100644
--- a/lib/inets/test/http_test_lib.erl
+++ b/lib/inets/test/http_test_lib.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2015-2015. All Rights Reserved.
+%% Copyright Ericsson AB 2015-2018. 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.
@@ -55,6 +55,25 @@ dummy_server_init(Caller, ip_comm, Inet, Extra) ->
]]},
[], ContentCb, Conf, ListenSocket);
+dummy_server_init(Caller, unix_socket, Inet, Extra) ->
+ ContentCb = proplists:get_value(content_cb, Extra),
+ UnixSocket = proplists:get_value(unix_socket, Extra),
+ SocketAddr = {local, UnixSocket},
+ BaseOpts = [binary, {packet, 0}, {reuseaddr,true}, {active, false}, {nodelay, true},
+ {ifaddr, SocketAddr}],
+ Conf = proplists:get_value(conf, Extra),
+ {ok, ListenSocket} = gen_tcp:listen(0, [Inet | BaseOpts]),
+ {ok, Port} = inet:port(ListenSocket),
+ Caller ! {port, Port},
+ dummy_ipcomm_server_loop({httpd_request, parse, [[{max_uri, ?HTTP_MAX_URI_SIZE},
+ {max_header, ?HTTP_MAX_HEADER_SIZE},
+ {max_version,?HTTP_MAX_VERSION_STRING},
+ {max_method, ?HTTP_MAX_METHOD_STRING},
+ {max_content_length, ?HTTP_MAX_CONTENT_LENGTH},
+ {customize, httpd_custom}
+ ]]},
+ [], ContentCb, Conf, ListenSocket);
+
dummy_server_init(Caller, ssl, Inet, Extra) ->
ContentCb = proplists:get_value(content_cb, Extra),
SSLOptions = proplists:get_value(ssl, Extra),
diff --git a/lib/inets/test/httpc_SUITE.erl b/lib/inets/test/httpc_SUITE.erl
index 0533b9ab70..38705372c9 100644
--- a/lib/inets/test/httpc_SUITE.erl
+++ b/lib/inets/test/httpc_SUITE.erl
@@ -37,6 +37,10 @@
-define(TLS_URL_START, "https://").
-define(NOT_IN_USE_PORT, 8997).
+%% Using hardcoded file path to keep it below 107 charaters
+%% (maximum length supported by erlang)
+-define(UNIX_SOCKET, "/tmp/inets_httpc_SUITE.sock").
+
-record(sslsocket, {fd = nil, pid = nil}).
%%--------------------------------------------------------------------
%% Common Test interface functions -----------------------------------
@@ -50,6 +54,8 @@ all() ->
[
{group, http},
{group, sim_http},
+ {group, http_internal},
+ {group, http_unix_socket},
{group, https},
{group, sim_https},
{group, misc}
@@ -62,6 +68,8 @@ groups() ->
%% and it shall be the last test case in the suite otherwise cookie
%% will fail.
{sim_http, [], only_simulated() ++ [process_leak_on_keepalive]},
+ {http_internal, [], real_requests_esi()},
+ {http_unix_socket, [], simulated_unix_socket()},
{https, [], real_requests()},
{sim_https, [], only_simulated()},
{misc, [], misc()}
@@ -97,6 +105,12 @@ real_requests()->
invalid_body
].
+real_requests_esi() ->
+ [slow_connection].
+
+simulated_unix_socket() ->
+ [unix_domain_socket].
+
only_simulated() ->
[
cookie,
@@ -182,15 +196,29 @@ init_per_group(Group, Config0) when Group =:= sim_https; Group =:= https->
_:_ ->
{skip, "Crypto did not start"}
end;
-
+init_per_group(http_unix_socket = Group, Config0) ->
+ case os:type() of
+ {win32,_} ->
+ {skip, "Unix Domain Sockets are not supported on Windows"};
+ _ ->
+ file:delete(?UNIX_SOCKET),
+ start_apps(Group),
+ Config = proplists:delete(port, Config0),
+ Port = server_start(Group, server_config(Group, Config)),
+ [{port, Port} | Config]
+ end;
init_per_group(Group, Config0) ->
start_apps(Group),
Config = proplists:delete(port, Config0),
Port = server_start(Group, server_config(Group, Config)),
[{port, Port} | Config].
+end_per_group(http_unix_socket,_Config) ->
+ file:delete(?UNIX_SOCKET),
+ ok;
end_per_group(_, _Config) ->
ok.
+
do_init_per_group(Group, Config0) ->
Config = proplists:delete(port, Config0),
Port = server_start(Group, server_config(Group, Config)),
@@ -1245,7 +1273,40 @@ stream_fun_server_close(Config) when is_list(Config) ->
after 13000 ->
ct:fail(did_not_receive_close)
end.
-
+
+%%--------------------------------------------------------------------
+slow_connection() ->
+ [{doc, "Test that a request on a slow keep-alive connection won't crash the httpc_manager"}].
+slow_connection(Config) when is_list(Config) ->
+ BodyFun = fun(0) -> eof;
+ (LenLeft) -> timer:sleep(1000),
+ {ok, lists:duplicate(10, "1"), LenLeft - 10}
+ end,
+ Request = {url(group_name(Config), "/httpc_SUITE:esi_post", Config),
+ [{"content-length", "100"}],
+ "text/plain",
+ {BodyFun, 100}},
+ {ok, _} = httpc:request(post, Request, [], []),
+ %% Second request causes a crash if gen_server timeout is not set to infinity
+ %% in httpc_handler.
+ {ok, _} = httpc:request(post, Request, [], []).
+
+%%-------------------------------------------------------------------------
+unix_domain_socket() ->
+ [{"doc, Test HTTP requests over unix domain sockets"}].
+unix_domain_socket(Config) when is_list(Config) ->
+
+ URL = "http:///v1/kv/foo",
+
+ {ok,[{unix_socket,?UNIX_SOCKET}]} =
+ httpc:get_options([unix_socket]),
+ {ok, {{_,200,_}, [_ | _], _}}
+ = httpc:request(put, {URL, [], [], ""}, [], []),
+ {ok, {{_,200,_}, [_ | _], _}}
+ = httpc:request(get, {URL, []}, [], []).
+
+
+
%%--------------------------------------------------------------------
%% Internal Functions ------------------------------------------------
%%--------------------------------------------------------------------
@@ -1339,6 +1400,8 @@ url(https, End, Config) ->
?TLS_URL_START ++ Host ++ ":" ++ integer_to_list(Port) ++ End;
url(sim_http, End, Config) ->
url(http, End, Config);
+url(http_internal, End, Config) ->
+ url(http, End, Config);
url(sim_https, End, Config) ->
url(https, End, Config).
url(http, UserInfo, End, Config) ->
@@ -1358,19 +1421,28 @@ group_name(Config) ->
server_start(sim_http, _) ->
Inet = inet_version(),
- ok = httpc:set_options([{ipfamily, Inet}]),
+ ok = httpc:set_options([{ipfamily, Inet},{unix_socket, undefined}]),
{_Pid, Port} = http_test_lib:dummy_server(ip_comm, Inet, [{content_cb, ?MODULE}]),
Port;
server_start(sim_https, SslConfig) ->
Inet = inet_version(),
- ok = httpc:set_options([{ipfamily, Inet}]),
+ ok = httpc:set_options([{ipfamily, Inet},{unix_socket, undefined}]),
{_Pid, Port} = http_test_lib:dummy_server(ssl, Inet, [{ssl, SslConfig}, {content_cb, ?MODULE}]),
Port;
+server_start(http_unix_socket, Config) ->
+ Inet = local,
+ Socket = proplists:get_value(unix_socket, Config),
+ ok = httpc:set_options([{ipfamily, Inet},{unix_socket, Socket}]),
+ {_Pid, Port} = http_test_lib:dummy_server(unix_socket, Inet, [{content_cb, ?MODULE},
+ {unix_socket, Socket}]),
+ Port;
+
server_start(_, HttpdConfig) ->
{ok, Pid} = inets:start(httpd, HttpdConfig),
Serv = inets:services_info(),
+ ok = httpc:set_options([{ipfamily, inet_version()},{unix_socket, undefined}]),
{value, {_, _, Info}} = lists:keysearch(Pid, 2, Serv),
proplists:get_value(port, Info).
@@ -1385,14 +1457,31 @@ server_config(http, Config) ->
{mime_type, "text/plain"},
{script_alias, {"/cgi-bin/", filename:join(ServerRoot, "cgi-bin") ++ "/"}}
];
-
+server_config(http_internal, Config) ->
+ ServerRoot = proplists:get_value(server_root, Config),
+ [{port, 0},
+ {server_name,"httpc_test"},
+ {server_root, ServerRoot},
+ {document_root, proplists:get_value(doc_root, Config)},
+ {bind_address, any},
+ {ipfamily, inet_version()},
+ {mime_type, "text/plain"},
+ {erl_script_alias, {"", [httpc_SUITE]}}
+ ];
server_config(https, Config) ->
[{socket_type, {essl, ssl_config(Config)}} | server_config(http, Config)];
server_config(sim_https, Config) ->
ssl_config(Config);
+server_config(http_unix_socket, _Config) ->
+ Socket = ?UNIX_SOCKET,
+ [{unix_socket, Socket}];
+
server_config(_, _) ->
[].
+esi_post(Sid, _Env, _Input) ->
+ mod_esi:deliver(Sid, ["OK"]).
+
start_apps(https) ->
inets_test_lib:start_apps([crypto, public_key, ssl]);
start_apps(sim_https) ->
@@ -2131,6 +2220,19 @@ handle_uri(_,"/delay_close.html",_,_,Socket,_) ->
handle_uri("HEAD",_,_,_,_,_) ->
"HTTP/1.1 200 ok\r\n" ++
"Content-Length:0\r\n\r\n";
+handle_uri("PUT","/v1/kv/foo",_,_,_,_) ->
+ "HTTP/1.1 200 OK\r\n" ++
+ "Date: Tue, 20 Feb 2018 14:39:08 GMT\r\n" ++
+ "Content-Length: 5\r\n\r\n" ++
+ "Content-Type: application/json\r\n\r\n" ++
+ "true\n";
+handle_uri("GET","/v1/kv/foo",_,_,_,_) ->
+ "HTTP/1.1 200 OK\r\n" ++
+ "Date: Tue, 20 Feb 2018 14:39:08 GMT\r\n" ++
+ "Content-Length: 24\r\n" ++
+ "Content-Type: application/json\r\n\r\n" ++
+ "[{\"Value\": \"aGVsbG8=\"}]\n";
+
handle_uri(_,_,_,_,_,DefaultResponse) ->
DefaultResponse.
diff --git a/lib/inets/test/uri_SUITE.erl b/lib/inets/test/uri_SUITE.erl
index f973296af6..8e00e6f565 100644
--- a/lib/inets/test/uri_SUITE.erl
+++ b/lib/inets/test/uri_SUITE.erl
@@ -52,6 +52,7 @@ all() ->
escaped,
hexed_query,
scheme_validation,
+ scheme_validation_bin,
encode_decode
].
@@ -273,6 +274,26 @@ scheme_validation(Config) when is_list(Config) ->
http_uri:parse("https://localhost#fragment",
[{scheme_validation_fun, none}]).
+scheme_validation_bin(Config) when is_list(Config) ->
+ {ok, {http,<<>>,<<"localhost">>,80,<<"/">>,<<>>}} =
+ http_uri:parse(<<"http://localhost#fragment">>),
+
+ ValidationFun =
+ fun(<<"http">>) -> valid;
+ (_) -> {error, bad_scheme}
+ end,
+
+ {ok, {http,<<>>,<<"localhost">>,80,<<"/">>,<<>>}} =
+ http_uri:parse(<<"http://localhost#fragment">>,
+ [{scheme_validation_fun, ValidationFun}]),
+ {error, bad_scheme} =
+ http_uri:parse(<<"https://localhost#fragment">>,
+ [{scheme_validation_fun, ValidationFun}]),
+ %% non-fun scheme_validation_fun works as no option passed
+ {ok, {https,<<>>,<<"localhost">>,443,<<"/">>,<<>>}} =
+ http_uri:parse(<<"https://localhost#fragment">>,
+ [{scheme_validation_fun, none}]).
+
encode_decode(Config) when is_list(Config) ->
?assertEqual("foo%20bar", http_uri:encode("foo bar")),
?assertEqual(<<"foo%20bar">>, http_uri:encode(<<"foo bar">>)),
diff --git a/lib/inets/vsn.mk b/lib/inets/vsn.mk
index 05cf4f6cc3..1fad9afe33 100644
--- a/lib/inets/vsn.mk
+++ b/lib/inets/vsn.mk
@@ -19,6 +19,6 @@
# %CopyrightEnd%
APPLICATION = inets
-INETS_VSN = 6.4.5
+INETS_VSN = 6.5
PRE_VSN =
APP_VSN = "$(APPLICATION)-$(INETS_VSN)$(PRE_VSN)"
diff --git a/lib/kernel/doc/src/code.xml b/lib/kernel/doc/src/code.xml
index c94f612c01..bd95819636 100644
--- a/lib/kernel/doc/src/code.xml
+++ b/lib/kernel/doc/src/code.xml
@@ -34,26 +34,28 @@
<p>This module contains the interface to the Erlang
<em>code server</em>, which deals with the loading of compiled
code into a running Erlang runtime system.</p>
- <p>The runtime system can be started in <em>embedded</em> or
- <em>interactive</em> mode. Which one is decided by command-line
+ <p>The runtime system can be started in <em>interactive</em> or
+ <em>embedded</em> mode. Which one is decided by the command-line
flag <c>-mode</c>:</p>
<pre>
% <input>erl -mode interactive</input></pre>
<p>The modes are as follows:</p>
<list type="bulleted">
<item>
- <p>In embedded mode, all code is loaded during system startup
- according to the boot script. (Code can also be loaded later
- by explicitly ordering the code server to do so).</p>
- </item>
- <item>
<p>In interactive mode, which is default, only some code is loaded
- during system startup, basically the modules needed by the runtime
+ during system startup, basically the modules needed by the runtime
system. Other code is dynamically loaded when first
referenced. When a call to a function in a certain module is
made, and the module is not loaded, the code server searches
for and tries to load the module.</p>
</item>
+ <item>
+ <p>In embedded mode, modules are not auto loaded. Trying to use
+ a module that has not been loaded results in an error. This mode is
+ recommended when the boot script loads all modules, as it is
+ typically done in OTP releases. (Code can still be loaded later
+ by explicitly ordering the code server to do so).</p>
+ </item>
</list>
<p>To prevent accidentally reloading of modules affecting the Erlang
runtime system, directories <c>kernel</c>, <c>stdlib</c>,
diff --git a/lib/kernel/doc/src/file.xml b/lib/kernel/doc/src/file.xml
index 8477b0e148..1b72769ce3 100644
--- a/lib/kernel/doc/src/file.xml
+++ b/lib/kernel/doc/src/file.xml
@@ -981,8 +981,7 @@ f.txt: {person, "kalle", 25}.
</item>
<tag><c>eisdir</c></tag>
<item>
- <p>The named file is not a regular file. It can be a
- directory, a FIFO, or a device.</p>
+ <p>The named file is a directory.</p>
</item>
<tag><c>enotdir</c></tag>
<item>
diff --git a/lib/kernel/doc/src/inet.xml b/lib/kernel/doc/src/inet.xml
index abb045b744..9552332948 100644
--- a/lib/kernel/doc/src/inet.xml
+++ b/lib/kernel/doc/src/inet.xml
@@ -4,7 +4,7 @@
<erlref>
<header>
<copyright>
- <year>1997</year><year>2017</year>
+ <year>1997</year><year>2018</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -277,9 +277,7 @@ fe80::204:acff:fe17:bf38
<p>Returns a <c>hostent</c> record for the host with the specified
hostname.</p>
<p>If resolver option <c>inet6</c> is <c>true</c>,
- an IPv6 address is looked up. If that fails,
- the IPv4 address is looked up and returned on
- IPv6-mapped IPv4 format.</p>
+ an IPv6 address is looked up.</p>
</desc>
</func>
@@ -582,6 +580,19 @@ get_tcpi_sacked(Sock) ->
</func>
<func>
+ <name name="ipv4_mapped_ipv6_address" arity="1" />
+ <fsummary>Convert to and from IPv4-mapped IPv6 address.</fsummary>
+ <desc>
+ <p>
+ Convert an IPv4 address to an IPv4-mapped IPv6 address
+ or the reverse. When converting from an IPv6 address
+ all but the 2 low words are ignored so this function also
+ works on some other types of addresses than IPv4-mapped.
+ </p>
+ </desc>
+ </func>
+
+ <func>
<name name="parse_strict_address" arity="1" />
<fsummary>Parse an IPv4 or IPv6 address strict.</fsummary>
<desc>
diff --git a/lib/kernel/doc/src/inet_res.xml b/lib/kernel/doc/src/inet_res.xml
index 3454e3c6f9..351d86a93a 100644
--- a/lib/kernel/doc/src/inet_res.xml
+++ b/lib/kernel/doc/src/inet_res.xml
@@ -4,7 +4,7 @@
<erlref>
<header>
<copyright>
- <year>2009</year><year>2015</year>
+ <year>2009</year><year>2018</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -230,9 +230,7 @@ inet_dns:record_type(_) -> undefined.</pre>
<seealso marker="#getbyname/2"><c>getbyname/2,3</c></seealso>.
</p>
<p>If resolver option <c>inet6</c> is <c>true</c>,
- an IPv6 address is looked up. If that fails,
- the IPv4 address is looked up and returned on
- IPv6-mapped IPv4 format.</p>
+ an IPv6 address is looked up.</p>
</desc>
</func>
diff --git a/lib/kernel/doc/src/notes.xml b/lib/kernel/doc/src/notes.xml
index d7f224c38e..09844f1502 100644
--- a/lib/kernel/doc/src/notes.xml
+++ b/lib/kernel/doc/src/notes.xml
@@ -31,6 +31,54 @@
</header>
<p>This document describes the changes made to the Kernel application.</p>
+<section><title>Kernel 5.4.3</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p> Correct a few contracts. </p>
+ <p>
+ Own Id: OTP-14889</p>
+ </item>
+ <item>
+ <p>
+ Reject loading modules with names containing directory
+ separators ('/' or '\' on Windows).</p>
+ <p>
+ Own Id: OTP-14933 Aux Id: ERL-564, PR-1716 </p>
+ </item>
+ <item>
+ <p>
+ Fix bug in handling of os:cmd/2 option max_size on
+ windows.</p>
+ <p>
+ Own Id: OTP-14940</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Kernel 5.4.2</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Add <c>os:cmd/2</c> that takes an options map as the
+ second argument.</p>
+ <p>
+ Add <c>max_size</c> as an option to <c>os:cmd/2</c> that
+ control the maximum size of the result that
+ <c>os:cmd/2</c> will return.</p>
+ <p>
+ Own Id: OTP-14823</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Kernel 5.4.1</title>
<section><title>Fixed Bugs and Malfunctions</title>
diff --git a/lib/kernel/doc/src/os.xml b/lib/kernel/doc/src/os.xml
index c27182ff0b..ef416ed233 100644
--- a/lib/kernel/doc/src/os.xml
+++ b/lib/kernel/doc/src/os.xml
@@ -103,7 +103,7 @@
</desc>
</datatype>
<datatype>
- <name name="command_input"/>
+ <name name="os_command"/>
<desc>
<p>All characters needs to be valid characters on the
specific OS using
@@ -116,19 +116,31 @@
</p>
</desc>
</datatype>
+ <datatype>
+ <name name="os_command_opts"/>
+ <desc>
+ <p>Options for <seealso marker="#cmd/2"><c>os:cmd/2</c></seealso></p>
+ <taglist>
+ <tag><c>max_size</c></tag>
+ <item>
+ <p>The maximum size of the data returned by the <c>os:cmd</c> call.
+ See the <seealso marker="#cmd/2"><c>os:cmd/2</c></seealso>
+ documentation for more details.</p>
+ </item>
+ </taglist>
+ </desc>
+ </datatype>
</datatypes>
-
+
<funcs>
<func>
<name name="cmd" arity="1"/>
+ <name name="cmd" arity="2"/>
<fsummary>Execute a command in a shell of the target OS.</fsummary>
<desc>
<p>Executes <c><anno>Command</anno></c> in a command shell of the
- target OS,
- captures the standard output of the command, and returns this
- result as a string. This function is a replacement of
- the previous function <c>unix:cmd/1</c>; they are equivalent on a
- Unix platform.</p>
+ target OS, captures the standard output of the command,
+ and returns this result as a string.</p>
<warning><p>Previous implementation used to allow all characters
as long as they were integer values greater than or equal to zero.
This sometimes lead to unwanted results since null characters
@@ -142,6 +154,21 @@ DirOut = os:cmd("dir"), % on Win32 platform</code>
called from another program (for example, <c>os:cmd/1</c>)
can differ, compared with the standard output of the command
when called directly from an OS command shell.</p>
+ <p><c>os:cmd/2</c> was added in kernel-5.5 (OTP-20.2.1). It makes it
+ possible to pass an options map as the second argument in order to
+ control the behaviour of <c>os:cmd</c>. The possible options are:
+ </p>
+ <taglist>
+ <tag><c>max_size</c></tag>
+ <item>
+ <p>The maximum size of the data returned by the <c>os:cmd</c> call.
+ This option is a safety feature that should be used when the command
+ executed can return a very large, possibly infinite, result.</p>
+ <code type="none">
+> os:cmd("cat /dev/zero", #{ max_size => 20 }).
+[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]</code>
+ </item>
+ </taglist>
</desc>
</func>
diff --git a/lib/kernel/doc/src/rpc.xml b/lib/kernel/doc/src/rpc.xml
index adec2d9520..fab616e630 100644
--- a/lib/kernel/doc/src/rpc.xml
+++ b/lib/kernel/doc/src/rpc.xml
@@ -4,7 +4,7 @@
<erlref>
<header>
<copyright>
- <year>1996</year><year>2016</year>
+ <year>1996</year><year>2018</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -217,7 +217,7 @@
<list type="bulleted">
<item>A list of the nodes that do not exist</item>
<item>A list of the nodes where the server does not exist</item>
- <item>A list of the nodes where the server terminatd before sending
+ <item>A list of the nodes where the server terminated before sending
any reply.</item>
</list>
</desc>
@@ -268,8 +268,9 @@
on the specified nodes and collects the answers. It returns
<c>{<anno>ResL</anno>, <anno>BadNodes</anno>}</c>, where
<c><anno>BadNodes</anno></c> is a list
- of the nodes that terminated or timed out during computation,
- and <c><anno>ResL</anno></c> is a list of the return values.
+ of the nodes that do not exist,
+ and <c><anno>ResL</anno></c> is a list of the return values,
+ or <c>{badrpc, <anno>Reason</anno>}</c> for failing calls.
<c><anno>Timeout</anno></c> is a time (integer) in milliseconds, or
<c>infinity</c>.</p>
<p>The following example is useful when new object code is to
@@ -347,7 +348,7 @@
<func>
<name name="pmap" arity="3"/>
- <fsummary>Parallell evaluation of mapping a function over a
+ <fsummary>Parallel evaluation of mapping a function over a
list.</fsummary>
<desc>
<p>Evaluates <c>apply(<anno>Module</anno>, <anno>Function</anno>,
diff --git a/lib/kernel/include/dist.hrl b/lib/kernel/include/dist.hrl
index db4a5eaebc..b7c35712a6 100644
--- a/lib/kernel/include/dist.hrl
+++ b/lib/kernel/include/dist.hrl
@@ -42,31 +42,5 @@
-define(DFLAG_BIG_CREATION, 16#40000).
-define(DFLAG_SEND_SENDER, 16#80000).
-%% DFLAGs that require strict ordering or:ed together...
--define(DFLAGS_STRICT_ORDER_DELIVERY,
- ?DFLAG_DIST_HDR_ATOM_CACHE).
-
-
%% Also update dflag2str() in ../src/dist_util.erl
%% when adding flags...
-
--define(DFLAGS_ALL,
- (?DFLAG_PUBLISHED
- bor ?DFLAG_ATOM_CACHE
- bor ?DFLAG_EXTENDED_REFERENCES
- bor ?DFLAG_DIST_MONITOR
- bor ?DFLAG_FUN_TAGS
- bor ?DFLAG_DIST_MONITOR_NAME
- bor ?DFLAG_HIDDEN_ATOM_CACHE
- bor ?DFLAG_NEW_FUN_TAGS
- bor ?DFLAG_EXTENDED_PIDS_PORTS
- bor ?DFLAG_EXPORT_PTR_TAG
- bor ?DFLAG_BIT_BINARIES
- bor ?DFLAG_NEW_FLOATS
- bor ?DFLAG_UNICODE_IO
- bor ?DFLAG_DIST_HDR_ATOM_CACHE
- bor ?DFLAG_SMALL_ATOM_TAGS
- bor ?DFLAG_UTF8_ATOMS
- bor ?DFLAG_MAP_TAG
- bor ?DFLAG_BIG_CREATION
- bor ?DFLAG_SEND_SENDER)).
diff --git a/lib/kernel/src/code.erl b/lib/kernel/src/code.erl
index 9969021a6c..f143a49d2f 100644
--- a/lib/kernel/src/code.erl
+++ b/lib/kernel/src/code.erl
@@ -149,8 +149,11 @@ load_file(Mod) when is_atom(Mod) ->
-spec ensure_loaded(Module) -> {module, Module} | {error, What} when
Module :: module(),
What :: embedded | badfile | nofile | on_load_failure.
-ensure_loaded(Mod) when is_atom(Mod) ->
- call({ensure_loaded,Mod}).
+ensure_loaded(Mod) when is_atom(Mod) ->
+ case erlang:module_loaded(Mod) of
+ true -> {module, Mod};
+ false -> call({ensure_loaded,Mod})
+ end.
%% XXX File as an atom is allowed only for backwards compatibility.
-spec load_abs(Filename) -> load_ret() when
diff --git a/lib/kernel/src/code_server.erl b/lib/kernel/src/code_server.erl
index 418b0c50e1..f5a890cb95 100644
--- a/lib/kernel/src/code_server.erl
+++ b/lib/kernel/src/code_server.erl
@@ -340,8 +340,7 @@ handle_call(all_loaded, _From, S) ->
{reply,all_loaded(Db),S};
handle_call({get_object_code,Mod}, _From, St) when is_atom(Mod) ->
- Path = St#state.path,
- case mod_to_bin(Path, Mod) of
+ case get_object_code(St, Mod) of
{_,Bin,FName} -> {reply,{Mod,Bin,FName},St};
Error -> {reply,Error,St}
end;
@@ -1182,19 +1181,28 @@ load_file(Mod, From, St0) ->
end,
handle_pending_on_load(Action, Mod, From, St0).
-load_file_1(Mod, From, #state{path=Path}=St) ->
- case mod_to_bin(Path, Mod) of
+load_file_1(Mod, From, St) ->
+ case get_object_code(St, Mod) of
error ->
{reply,{error,nofile},St};
{Mod,Binary,File} ->
try_load_module_1(File, Mod, Binary, From, St)
end.
-mod_to_bin([Dir|Tail], Mod) ->
- File = filename:append(Dir, atom_to_list(Mod) ++ objfile_extension()),
+get_object_code(#state{path=Path}, Mod) when is_atom(Mod) ->
+ ModStr = atom_to_list(Mod),
+ case erl_prim_loader:is_basename(ModStr) of
+ true ->
+ mod_to_bin(Path, Mod, ModStr ++ objfile_extension());
+ false ->
+ error
+ end.
+
+mod_to_bin([Dir|Tail], Mod, ModFile) ->
+ File = filename:append(Dir, ModFile),
case erl_prim_loader:get_file(File) of
error ->
- mod_to_bin(Tail, Mod);
+ mod_to_bin(Tail, Mod, ModFile);
{ok,Bin,_} ->
case filename:pathtype(File) of
absolute ->
@@ -1203,10 +1211,9 @@ mod_to_bin([Dir|Tail], Mod) ->
{Mod,Bin,absname(File)}
end
end;
-mod_to_bin([], Mod) ->
+mod_to_bin([], Mod, ModFile) ->
%% At last, try also erl_prim_loader's own method
- File = to_list(Mod) ++ objfile_extension(),
- case erl_prim_loader:get_file(File) of
+ case erl_prim_loader:get_file(ModFile) of
error ->
error; % No more alternatives !
{ok,Bin,FName} ->
diff --git a/lib/kernel/src/disk_log_1.erl b/lib/kernel/src/disk_log_1.erl
index 93856aa7b3..b456b53d20 100644
--- a/lib/kernel/src/disk_log_1.erl
+++ b/lib/kernel/src/disk_log_1.erl
@@ -630,7 +630,7 @@ is_head(Bin) when is_binary(Bin) ->
%% Writes MaxB bytes on each file.
%% Creates a file called Name.idx in the Dir. This
%% file contains the last written FileName as one byte, and
-%% follwing that, the sizes of each file (size 0 number of items).
+%% following that, the sizes of each file (size 0 number of items).
%% On startup, this file is read, and the next available
%% filename is used as first log file.
%% Reports can be browsed with Report Browser Tool (rb), or
diff --git a/lib/kernel/src/dist_util.erl b/lib/kernel/src/dist_util.erl
index fb9f7fd7eb..f7a84c14b4 100644
--- a/lib/kernel/src/dist_util.erl
+++ b/lib/kernel/src/dist_util.erl
@@ -27,6 +27,7 @@
%%-compile(export_all).
-export([handshake_we_started/1, handshake_other_started/1,
+ strict_order_flags/0,
start_timer/1, setup_timer/2,
reset_timer/1, cancel_timer/1,
shutdown/3, shutdown/4]).
@@ -116,22 +117,8 @@ dflag2str(_) ->
"UNKNOWN".
-remove_flag(Flag, Flags) ->
- case Flags band Flag of
- 0 ->
- Flags;
- _ ->
- Flags - Flag
- end.
-
-adjust_flags(ThisFlags, OtherFlags, RejectFlags) ->
- case (?DFLAG_PUBLISHED band ThisFlags) band OtherFlags of
- 0 ->
- {remove_flag(?DFLAG_PUBLISHED, ThisFlags),
- remove_flag(?DFLAG_PUBLISHED, OtherFlags)};
- _ ->
- {ThisFlags, OtherFlags band (bnot RejectFlags)}
- end.
+adjust_flags(ThisFlags, OtherFlags) ->
+ ThisFlags band OtherFlags.
publish_flag(hidden, _) ->
0;
@@ -143,50 +130,35 @@ publish_flag(_, OtherNode) ->
0
end.
--define(DFLAGS_REMOVABLE,
- (?DFLAG_DIST_HDR_ATOM_CACHE
- bor ?DFLAG_HIDDEN_ATOM_CACHE
- bor ?DFLAG_ATOM_CACHE)).
-
--define(DFLAGS_ADDABLE,
- (?DFLAGS_ALL
- band (bnot (?DFLAG_PUBLISHED
- bor ?DFLAG_HIDDEN_ATOM_CACHE
- bor ?DFLAG_ATOM_CACHE)))).
-
--define(DFLAGS_THIS_DEFAULT,
- (?DFLAG_EXPORT_PTR_TAG
- bor ?DFLAG_EXTENDED_PIDS_PORTS
- bor ?DFLAG_EXTENDED_REFERENCES
- bor ?DFLAG_DIST_MONITOR
- bor ?DFLAG_FUN_TAGS
- bor ?DFLAG_DIST_MONITOR_NAME
- bor ?DFLAG_NEW_FUN_TAGS
- bor ?DFLAG_BIT_BINARIES
- bor ?DFLAG_NEW_FLOATS
- bor ?DFLAG_UNICODE_IO
- bor ?DFLAG_DIST_HDR_ATOM_CACHE
- bor ?DFLAG_SMALL_ATOM_TAGS
- bor ?DFLAG_UTF8_ATOMS
- bor ?DFLAG_MAP_TAG
- bor ?DFLAG_BIG_CREATION
- bor ?DFLAG_SEND_SENDER)).
-
-make_this_flags(RequestType, AddFlags, RemoveFlags, OtherNode) ->
- case RemoveFlags band (bnot ?DFLAGS_REMOVABLE) of
+
+%% Sync with dist.c
+-record(erts_dflags, {
+ default, % flags erts prefers
+ mandatory, % flags erts needs
+ addable, % flags local dist implementation is allowed to add
+ rejectable, % flags local dist implementation is allowed to reject
+ strict_order % flags for features needing strict order delivery
+}).
+
+-spec strict_order_flags() -> integer().
+strict_order_flags() ->
+ EDF = erts_internal:get_dflags(),
+ EDF#erts_dflags.strict_order.
+
+make_this_flags(RequestType, AddFlags, RejectFlags, OtherNode,
+ #erts_dflags{}=EDF) ->
+ case RejectFlags band (bnot EDF#erts_dflags.rejectable) of
0 -> ok;
Rerror -> exit({"Rejecting non rejectable flags", Rerror})
end,
- case AddFlags band (bnot ?DFLAGS_ADDABLE) of
+ case AddFlags band (bnot EDF#erts_dflags.addable) of
0 -> ok;
Aerror -> exit({"Adding non addable flags", Aerror})
end,
- Flgs0 = ?DFLAGS_THIS_DEFAULT,
+ Flgs0 = EDF#erts_dflags.default,
Flgs1 = Flgs0 bor publish_flag(RequestType, OtherNode),
Flgs2 = Flgs1 bor AddFlags,
- Flgs3 = Flgs2 band (bnot (?DFLAG_HIDDEN_ATOM_CACHE
- bor ?DFLAG_ATOM_CACHE)),
- Flgs3 band (bnot RemoveFlags).
+ Flgs2 band (bnot RejectFlags).
handshake_other_started(#hs_data{request_type=ReqType,
add_flags=AddFlgs0,
@@ -196,19 +168,18 @@ handshake_other_started(#hs_data{request_type=ReqType,
RejFlgs = convert_flags(RejFlgs0),
ReqFlgs = convert_flags(ReqFlgs0),
{PreOtherFlags,Node,Version} = recv_name(HSData0),
- PreThisFlags = make_this_flags(ReqType, AddFlgs, RejFlgs, Node),
- {ThisFlags, OtherFlags} = adjust_flags(PreThisFlags,
- PreOtherFlags,
- RejFlgs),
- HSData = HSData0#hs_data{this_flags=ThisFlags,
- other_flags=OtherFlags,
+ EDF = erts_internal:get_dflags(),
+ PreThisFlags = make_this_flags(ReqType, AddFlgs, RejFlgs, Node, EDF),
+ ChosenFlags = adjust_flags(PreThisFlags, PreOtherFlags),
+ HSData = HSData0#hs_data{this_flags=ChosenFlags,
+ other_flags=ChosenFlags,
other_version=Version,
other_node=Node,
other_started=true,
add_flags=AddFlgs,
reject_flags=RejFlgs,
require_flags=ReqFlgs},
- check_dflags(HSData),
+ check_dflags(HSData, EDF),
is_allowed(HSData),
?debug({"MD5 connection from ~p (V~p)~n",
[Node, HSData#hs_data.other_version]}),
@@ -247,14 +218,11 @@ is_allowed(#hs_data{other_node = Node,
check_dflags(#hs_data{other_node = Node,
other_flags = OtherFlags,
other_started = OtherStarted,
- require_flags = RequiredFlags} = HSData) ->
- Mandatory = ((?DFLAG_EXTENDED_REFERENCES
- bor ?DFLAG_EXTENDED_PIDS_PORTS
- bor ?DFLAG_UTF8_ATOMS
- bor ?DFLAG_NEW_FUN_TAGS)
- bor RequiredFlags),
- Missing = check_mandatory(0, ?DFLAGS_ALL, Mandatory,
- OtherFlags, []),
+ require_flags = RequiredFlags} = HSData,
+ #erts_dflags{}=EDF) ->
+
+ Mandatory = (EDF#erts_dflags.mandatory bor RequiredFlags),
+ Missing = check_mandatory(Mandatory, OtherFlags, []),
case Missing of
[] ->
ok;
@@ -274,21 +242,20 @@ check_dflags(#hs_data{other_node = Node,
?shutdown2(Node, {check_dflags_failed, Missing})
end.
-check_mandatory(_Bit, 0, _Mandatory, _OtherFlags, Missing) ->
+check_mandatory(0, _OtherFlags, Missing) ->
Missing;
-check_mandatory(Bit, Left, Mandatory, OtherFlags, Missing) ->
- DFlag = (1 bsl Bit),
- NewLeft = Left band (bnot DFlag),
- NewMissing = case {DFlag band Mandatory,
- DFlag band OtherFlags} of
- {DFlag, 0} ->
+check_mandatory(Mandatory, OtherFlags, Missing) ->
+ Left = Mandatory band (Mandatory - 1), % clear lowest set bit
+ DFlag = Mandatory bxor Left, % only lowest set bit
+ NewMissing = case DFlag band OtherFlags of
+ 0 ->
%% Mandatory and missing...
[dflag2str(DFlag) | Missing];
_ ->
- %% Not mandatory or present...
+ %% Mandatory and present...
Missing
end,
- check_mandatory(Bit+1, NewLeft, Mandatory, OtherFlags, NewMissing).
+ check_mandatory(Left, OtherFlags, NewMissing).
%% No nodedown will be sent if we fail before this process has
@@ -410,7 +377,8 @@ handshake_we_started(#hs_data{request_type=ReqType,
AddFlgs = convert_flags(AddFlgs0),
RejFlgs = convert_flags(RejFlgs0),
ReqFlgs = convert_flags(ReqFlgs0),
- PreThisFlags = make_this_flags(ReqType, AddFlgs, RejFlgs, Node),
+ EDF = erts_internal:get_dflags(),
+ PreThisFlags = make_this_flags(ReqType, AddFlgs, RejFlgs, Node, EDF),
HSData = PreHSData#hs_data{this_flags = PreThisFlags,
add_flags = AddFlgs,
reject_flags = RejFlgs,
@@ -418,13 +386,11 @@ handshake_we_started(#hs_data{request_type=ReqType,
send_name(HSData),
recv_status(HSData),
{PreOtherFlags,ChallengeA} = recv_challenge(HSData),
- {ThisFlags,OtherFlags} = adjust_flags(PreThisFlags,
- PreOtherFlags,
- RejFlgs),
- NewHSData = HSData#hs_data{this_flags = ThisFlags,
- other_flags = OtherFlags,
+ ChosenFlags = adjust_flags(PreThisFlags, PreOtherFlags),
+ NewHSData = HSData#hs_data{this_flags = ChosenFlags,
+ other_flags = ChosenFlags,
other_started = false},
- check_dflags(NewHSData),
+ check_dflags(NewHSData, EDF),
MyChallenge = gen_challenge(),
{MyCookie,HisCookie} = get_cookies(Node),
send_challenge_reply(NewHSData,MyChallenge,
@@ -539,8 +505,8 @@ do_setnode(#hs_data{other_node = Node, socket = Socket,
"no table space left for node ~w ** ~n",
[Node]),
?shutdown(Node);
- error:Other ->
- exit({Other, erlang:get_stacktrace()})
+ error:Other:Stacktrace ->
+ exit({Other, Stacktrace})
end;
_ ->
error_msg("** Distribution connection error, "
@@ -588,7 +554,7 @@ con_loop({Kernel, Node, Socket, Type, DHandle, MFTick, MFGetstat,
{Kernel, aux_tick} ->
case getstat(DHandle, Socket, MFGetstat) of
{ok, _, _, PendWrite} ->
- send_tick(Socket, PendWrite, MFTick);
+ send_aux_tick(Type, Socket, PendWrite, MFTick);
_ ->
ignore_it
end,
@@ -841,49 +807,56 @@ send_status(#hs_data{socket = Socket, other_node = Node,
%% The detection time interval is thus, by default, 45s < DT < 75s
-%% A HIDDEN node is always (if not a pending write) ticked if
-%% we haven't read anything as a hidden node only ticks when it receives
-%% a TICK !!
+%% A HIDDEN node is always ticked if we haven't read anything
+%% as a (primitive) hidden node only ticks when it receives a TICK !!
send_tick(DHandle, Socket, Tick, Type, MFTick, MFGetstat) ->
#tick{tick = T0,
read = Read,
write = Write,
- ticked = Ticked} = Tick,
+ ticked = Ticked0} = Tick,
T = T0 + 1,
T1 = T rem 4,
case getstat(DHandle, Socket, MFGetstat) of
- {ok, Read, _, _} when Ticked =:= T ->
+ {ok, Read, _, _} when Ticked0 =:= T ->
{error, not_responding};
- {ok, Read, W, Pend} when Type =:= hidden ->
- send_tick(Socket, Pend, MFTick),
- {ok, Tick#tick{write = W + 1,
- tick = T1}};
- {ok, Read, Write, Pend} ->
- send_tick(Socket, Pend, MFTick),
- {ok, Tick#tick{write = Write + 1,
- tick = T1}};
- {ok, R, Write, Pend} ->
- send_tick(Socket, Pend, MFTick),
- {ok, Tick#tick{write = Write + 1,
- read = R,
- tick = T1,
- ticked = T}};
- {ok, Read, W, _} ->
- {ok, Tick#tick{write = W,
- tick = T1}};
- {ok, R, W, _} ->
- {ok, Tick#tick{write = W,
- read = R,
- tick = T1,
- ticked = T}};
+
+ {ok, R, W1, Pend} ->
+ RDiff = R - Read,
+ W2 = case need_to_tick(Type, RDiff, W1-Write, Pend) of
+ true ->
+ MFTick(Socket),
+ W1 + 1;
+ false ->
+ W1
+ end,
+
+ Ticked1 = case RDiff of
+ 0 -> Ticked0;
+ _ -> T
+ end,
+
+ {ok, Tick#tick{write = W2,
+ tick = T1,
+ read = R,
+ ticked = Ticked1}};
+
Error ->
Error
end.
-send_tick(_, Pend, _) when Pend /= false, Pend /= 0 ->
+need_to_tick(_, _, 0, 0) -> % nothing written and empty send queue
+ true;
+need_to_tick(_, _, 0, false) -> % nothing written and empty send queue
+ true;
+need_to_tick(hidden, 0, _, _) -> % nothing read from hidden
+ true;
+need_to_tick(_, _, _, _) ->
+ false.
+
+send_aux_tick(normal, _, Pend, _) when Pend /= false, Pend /= 0 ->
ok; %% Dont send tick if pending write.
-send_tick(Socket, _Pend, MFTick) ->
+send_aux_tick(_Type, Socket, _Pend, MFTick) ->
MFTick(Socket).
%% ------------------------------------------------------------
diff --git a/lib/kernel/src/error_handler.erl b/lib/kernel/src/error_handler.erl
index 59ca8e690d..a9582c6225 100644
--- a/lib/kernel/src/error_handler.erl
+++ b/lib/kernel/src/error_handler.erl
@@ -106,8 +106,8 @@ crash(M, F, A) ->
crash(Tuple) ->
try erlang:error(undef)
catch
- error:undef ->
- Stk = [Tuple|tl(erlang:get_stacktrace())],
+ error:undef:Stacktrace ->
+ Stk = [Tuple|tl(Stacktrace)],
erlang:raise(error, undef, Stk)
end.
diff --git a/lib/kernel/src/erts_debug.erl b/lib/kernel/src/erts_debug.erl
index 3456c8511e..6f248626ca 100644
--- a/lib/kernel/src/erts_debug.erl
+++ b/lib/kernel/src/erts_debug.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1999-2016. All Rights Reserved.
+%% Copyright Ericsson AB 1999-2018. 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.
@@ -32,8 +32,7 @@
%%% BIFs
-export([breakpoint/2, disassemble/1, display/1, dist_ext_to_term/2,
- dump_monitors/1, dump_links/1, flat_size/1,
- get_internal_state/1, instructions/0,
+ flat_size/1, get_internal_state/1, instructions/0,
map_info/1, same/2, set_internal_state/2,
size_shared/1, copy_shared/1, dirty_cpu/2, dirty_io/2, dirty/3,
lcnt_control/1, lcnt_control/2, lcnt_collect/0, lcnt_clear/0]).
@@ -70,18 +69,6 @@ display(_) ->
dist_ext_to_term(_, _) ->
erlang:nif_error(undef).
--spec dump_monitors(Id) -> true when
- Id :: pid() | atom().
-
-dump_monitors(_) ->
- erlang:nif_error(undef).
-
--spec dump_links(Id) -> true when
- Id :: pid() | port() | atom().
-
-dump_links(_) ->
- erlang:nif_error(undef).
-
-spec flat_size(Term) -> non_neg_integer() when
Term :: term().
diff --git a/lib/kernel/src/file.erl b/lib/kernel/src/file.erl
index d05199897f..c2df1ee288 100644
--- a/lib/kernel/src/file.erl
+++ b/lib/kernel/src/file.erl
@@ -1382,8 +1382,8 @@ eval_stream2({ok,Form,EndLine}, Fd, H, Last, E, Bs0) ->
try erl_eval:exprs(Form, Bs0) of
{value,V,Bs} ->
eval_stream(Fd, H, EndLine, {V}, E, Bs)
- catch Class:Reason ->
- Error = {EndLine,?MODULE,{Class,Reason,erlang:get_stacktrace()}},
+ catch Class:Reason:StackTrace ->
+ Error = {EndLine,?MODULE,{Class,Reason,StackTrace}},
eval_stream(Fd, H, EndLine, Last, [Error|E], Bs0)
end;
eval_stream2({error,What,EndLine}, Fd, H, Last, E, Bs) ->
diff --git a/lib/kernel/src/group.erl b/lib/kernel/src/group.erl
index e1198d2587..2c0518ccad 100644
--- a/lib/kernel/src/group.erl
+++ b/lib/kernel/src/group.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2016. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2017. 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.
@@ -114,7 +114,7 @@ server_loop(Drv, Shell, Buf0) ->
{io_request,From,ReplyAs,Req} when is_pid(From) ->
%% This io_request may cause a transition to a couple of
%% selective receive loops elsewhere in this module.
- Buf = io_request(Req, From, ReplyAs, Drv, Buf0),
+ Buf = io_request(Req, From, ReplyAs, Drv, Shell, Buf0),
server_loop(Drv, Shell, Buf);
{reply,{{From,ReplyAs},Reply}} ->
io_reply(From, ReplyAs, Reply),
@@ -135,7 +135,7 @@ server_loop(Drv, Shell, Buf0) ->
exit(R);
%% We want to throw away any term that we don't handle (standard
%% practice in receive loops), but not any {Drv,_} tuples which are
- %% handled in io_request/5.
+ %% handled in io_request/6.
NotDrvTuple when (not is_tuple(NotDrvTuple)) orelse
(tuple_size(NotDrvTuple) =/= 2) orelse
(element(1, NotDrvTuple) =/= Drv) ->
@@ -177,8 +177,8 @@ set_unicode_state(Drv,Bool) ->
end.
-io_request(Req, From, ReplyAs, Drv, Buf0) ->
- case io_request(Req, Drv, {From,ReplyAs}, Buf0) of
+io_request(Req, From, ReplyAs, Drv, Shell, Buf0) ->
+ case io_request(Req, Drv, Shell, {From,ReplyAs}, Buf0) of
{ok,Reply,Buf} ->
io_reply(From, ReplyAs, Reply),
Buf;
@@ -208,7 +208,7 @@ io_request(Req, From, ReplyAs, Drv, Buf0) ->
%%
%% These put requests have to be synchronous to the driver as otherwise
%% there is no guarantee that the data has actually been printed.
-io_request({put_chars,unicode,Chars}, Drv, From, Buf) ->
+io_request({put_chars,unicode,Chars}, Drv, _Shell, From, Buf) ->
case catch unicode:characters_to_binary(Chars,utf8) of
Binary when is_binary(Binary) ->
send_drv(Drv, {put_chars_sync, unicode, Binary, {From,ok}}),
@@ -216,7 +216,7 @@ io_request({put_chars,unicode,Chars}, Drv, From, Buf) ->
_ ->
{error,{error,{put_chars, unicode,Chars}},Buf}
end;
-io_request({put_chars,unicode,M,F,As}, Drv, From, Buf) ->
+io_request({put_chars,unicode,M,F,As}, Drv, _Shell, From, Buf) ->
case catch apply(M, F, As) of
Binary when is_binary(Binary) ->
send_drv(Drv, {put_chars_sync, unicode, Binary, {From,ok}}),
@@ -230,12 +230,12 @@ io_request({put_chars,unicode,M,F,As}, Drv, From, Buf) ->
{error,{error,F},Buf}
end
end;
-io_request({put_chars,latin1,Binary}, Drv, From, Buf) when is_binary(Binary) ->
+io_request({put_chars,latin1,Binary}, Drv, _Shell, From, Buf) when is_binary(Binary) ->
send_drv(Drv, {put_chars_sync, unicode,
unicode:characters_to_binary(Binary,latin1),
{From,ok}}),
{noreply,Buf};
-io_request({put_chars,latin1,Chars}, Drv, From, Buf) ->
+io_request({put_chars,latin1,Chars}, Drv, _Shell, From, Buf) ->
case catch unicode:characters_to_binary(Chars,latin1) of
Binary when is_binary(Binary) ->
send_drv(Drv, {put_chars_sync, unicode, Binary, {From,ok}}),
@@ -243,7 +243,7 @@ io_request({put_chars,latin1,Chars}, Drv, From, Buf) ->
_ ->
{error,{error,{put_chars,latin1,Chars}},Buf}
end;
-io_request({put_chars,latin1,M,F,As}, Drv, From, Buf) ->
+io_request({put_chars,latin1,M,F,As}, Drv, _Shell, From, Buf) ->
case catch apply(M, F, As) of
Binary when is_binary(Binary) ->
send_drv(Drv, {put_chars_sync, unicode,
@@ -260,30 +260,30 @@ io_request({put_chars,latin1,M,F,As}, Drv, From, Buf) ->
end
end;
-io_request({get_chars,Encoding,Prompt,N}, Drv, _From, Buf) ->
- get_chars_n(Prompt, io_lib, collect_chars, N, Drv, Buf, Encoding);
-io_request({get_line,Encoding,Prompt}, Drv, _From, Buf) ->
- get_chars_line(Prompt, io_lib, collect_line, [], Drv, Buf, Encoding);
-io_request({get_until,Encoding, Prompt,M,F,As}, Drv, _From, Buf) ->
- get_chars_line(Prompt, io_lib, get_until, {M,F,As}, Drv, Buf, Encoding);
-io_request({get_password,_Encoding},Drv,_From,Buf) ->
- get_password_chars(Drv, Buf);
-io_request({setopts,Opts}, Drv, _From, Buf) when is_list(Opts) ->
+io_request({get_chars,Encoding,Prompt,N}, Drv, Shell, _From, Buf) ->
+ get_chars_n(Prompt, io_lib, collect_chars, N, Drv, Shell, Buf, Encoding);
+io_request({get_line,Encoding,Prompt}, Drv, Shell, _From, Buf) ->
+ get_chars_line(Prompt, io_lib, collect_line, [], Drv, Shell, Buf, Encoding);
+io_request({get_until,Encoding, Prompt,M,F,As}, Drv, Shell, _From, Buf) ->
+ get_chars_line(Prompt, io_lib, get_until, {M,F,As}, Drv, Shell, Buf, Encoding);
+io_request({get_password,_Encoding},Drv,Shell,_From,Buf) ->
+ get_password_chars(Drv, Shell, Buf);
+io_request({setopts,Opts}, Drv, _Shell, _From, Buf) when is_list(Opts) ->
setopts(Opts, Drv, Buf);
-io_request(getopts, Drv, _From, Buf) ->
+io_request(getopts, Drv, _Shell, _From, Buf) ->
getopts(Drv, Buf);
-io_request({requests,Reqs}, Drv, From, Buf) ->
- io_requests(Reqs, {ok,ok,Buf}, From, Drv);
+io_request({requests,Reqs}, Drv, Shell, From, Buf) ->
+ io_requests(Reqs, {ok,ok,Buf}, From, Drv, Shell);
%% New in R12
-io_request({get_geometry,columns},Drv,_From,Buf) ->
+io_request({get_geometry,columns},Drv,_Shell,_From,Buf) ->
case get_tty_geometry(Drv) of
{W,_H} ->
{ok,W,Buf};
_ ->
{error,{error,enotsup},Buf}
end;
-io_request({get_geometry,rows},Drv,_From,Buf) ->
+io_request({get_geometry,rows},Drv,_Shell,_From,Buf) ->
case get_tty_geometry(Drv) of
{_W,H} ->
{ok,H,Buf};
@@ -292,40 +292,40 @@ io_request({get_geometry,rows},Drv,_From,Buf) ->
end;
%% BC with pre-R13
-io_request({put_chars,Chars}, Drv, From, Buf) ->
- io_request({put_chars,latin1,Chars}, Drv, From, Buf);
-io_request({put_chars,M,F,As}, Drv, From, Buf) ->
- io_request({put_chars,latin1,M,F,As}, Drv, From, Buf);
-io_request({get_chars,Prompt,N}, Drv, From, Buf) ->
- io_request({get_chars,latin1,Prompt,N}, Drv, From, Buf);
-io_request({get_line,Prompt}, Drv, From, Buf) ->
- io_request({get_line,latin1,Prompt}, Drv, From, Buf);
-io_request({get_until, Prompt,M,F,As}, Drv, From, Buf) ->
- io_request({get_until,latin1, Prompt,M,F,As}, Drv, From, Buf);
-io_request(get_password,Drv,From,Buf) ->
- io_request({get_password,latin1},Drv,From,Buf);
-
-
-
-io_request(_, _Drv, _From, Buf) ->
+io_request({put_chars,Chars}, Drv, Shell, From, Buf) ->
+ io_request({put_chars,latin1,Chars}, Drv, Shell, From, Buf);
+io_request({put_chars,M,F,As}, Drv, Shell, From, Buf) ->
+ io_request({put_chars,latin1,M,F,As}, Drv, Shell, From, Buf);
+io_request({get_chars,Prompt,N}, Drv, Shell, From, Buf) ->
+ io_request({get_chars,latin1,Prompt,N}, Drv, Shell, From, Buf);
+io_request({get_line,Prompt}, Drv, Shell, From, Buf) ->
+ io_request({get_line,latin1,Prompt}, Drv, Shell, From, Buf);
+io_request({get_until, Prompt,M,F,As}, Drv, Shell, From, Buf) ->
+ io_request({get_until,latin1, Prompt,M,F,As}, Drv, Shell, From, Buf);
+io_request(get_password,Drv,Shell,From,Buf) ->
+ io_request({get_password,latin1},Drv,Shell,From,Buf);
+
+
+
+io_request(_, _Drv, _Shell, _From, Buf) ->
{error,{error,request},Buf}.
-%% Status = io_requests(RequestList, PrevStat, From, Drv)
+%% Status = io_requests(RequestList, PrevStat, From, Drv, Shell)
%% Process a list of output requests as long as
%% the previous status is 'ok' or noreply.
%%
%% We use undefined as the From for all but the last request
%% in order to discards acknowledgements from those requests.
%%
-io_requests([R|Rs], {noreply,Buf}, From, Drv) ->
+io_requests([R|Rs], {noreply,Buf}, From, Drv, Shell) ->
ReqFrom = if Rs =:= [] -> From; true -> undefined end,
- io_requests(Rs, io_request(R, Drv, ReqFrom, Buf), From, Drv);
-io_requests([R|Rs], {ok,ok,Buf}, From, Drv) ->
+ io_requests(Rs, io_request(R, Drv, Shell, ReqFrom, Buf), From, Drv, Shell);
+io_requests([R|Rs], {ok,ok,Buf}, From, Drv, Shell) ->
ReqFrom = if Rs =:= [] -> From; true -> undefined end,
- io_requests(Rs, io_request(R, Drv, ReqFrom, Buf), From, Drv);
-io_requests([_|_], Error, _From, _Drv) ->
+ io_requests(Rs, io_request(R, Drv, Shell, ReqFrom, Buf), From, Drv, Shell);
+io_requests([_|_], Error, _From, _Drv, _Shell) ->
Error;
-io_requests([], Stat, _From, _) ->
+io_requests([], Stat, _From, _, _Shell) ->
Stat.
%% io_reply(From, ReplyAs, Reply)
@@ -333,7 +333,7 @@ io_requests([], Stat, _From, _) ->
%% The ACK contains the return value.
io_reply(undefined, _ReplyAs, _Reply) ->
- %% Ignore these replies as they are generated from io_requests/4.
+ %% Ignore these replies as they are generated from io_requests/5.
ok;
io_reply(From, ReplyAs, Reply) ->
From ! {io_reply,ReplyAs,Reply},
@@ -442,8 +442,8 @@ getopts(Drv,Buf) ->
%% {Result,NewSaveBuffer}
%% {error,What,NewSaveBuffer}
-get_password_chars(Drv,Buf) ->
- case get_password_line(Buf, Drv) of
+get_password_chars(Drv,Shell,Buf) ->
+ case get_password_line(Buf, Drv, Shell) of
{done, Line, Buf1} ->
{ok, Line, Buf1};
interrupted ->
@@ -452,59 +452,59 @@ get_password_chars(Drv,Buf) ->
{exit, terminated}
end.
-get_chars_n(Prompt, M, F, Xa, Drv, Buf, Encoding) ->
+get_chars_n(Prompt, M, F, Xa, Drv, Shell, Buf, Encoding) ->
Pbs = prompt_bytes(Prompt, Encoding),
case get(echo) of
true ->
- get_chars_loop(Pbs, M, F, Xa, Drv, Buf, start, Encoding);
+ get_chars_loop(Pbs, M, F, Xa, Drv, Shell, Buf, start, Encoding);
false ->
- get_chars_n_loop(Pbs, M, F, Xa, Drv, Buf, start, Encoding)
+ get_chars_n_loop(Pbs, M, F, Xa, Drv, Shell, Buf, start, Encoding)
end.
-get_chars_line(Prompt, M, F, Xa, Drv, Buf, Encoding) ->
+get_chars_line(Prompt, M, F, Xa, Drv, Shell, Buf, Encoding) ->
Pbs = prompt_bytes(Prompt, Encoding),
- get_chars_loop(Pbs, M, F, Xa, Drv, Buf, start, Encoding).
+ get_chars_loop(Pbs, M, F, Xa, Drv, Shell, Buf, start, Encoding).
-get_chars_loop(Pbs, M, F, Xa, Drv, Buf0, State, Encoding) ->
+get_chars_loop(Pbs, M, F, Xa, Drv, Shell, Buf0, State, Encoding) ->
Result = case get(echo) of
true ->
- get_line(Buf0, Pbs, Drv, Encoding);
+ get_line(Buf0, Pbs, Drv, Shell, Encoding);
false ->
% get_line_echo_off only deals with lists
% and does not need encoding...
- get_line_echo_off(Buf0, Pbs, Drv)
+ get_line_echo_off(Buf0, Pbs, Drv, Shell)
end,
case Result of
{done,Line,Buf} ->
- get_chars_apply(Pbs, M, F, Xa, Drv, Buf, State, Line, Encoding);
+ get_chars_apply(Pbs, M, F, Xa, Drv, Shell, Buf, State, Line, Encoding);
interrupted ->
{error,{error,interrupted},[]};
terminated ->
{exit,terminated}
end.
-get_chars_apply(Pbs, M, F, Xa, Drv, Buf, State0, Line, Encoding) ->
+get_chars_apply(Pbs, M, F, Xa, Drv, Shell, Buf, State0, Line, Encoding) ->
case catch M:F(State0, cast(Line,get(read_mode), Encoding), Encoding, Xa) of
{stop,Result,Rest} ->
{ok,Result,append(Rest, Buf, Encoding)};
{'EXIT',_} ->
{error,{error,err_func(M, F, Xa)},[]};
State1 ->
- get_chars_loop(Pbs, M, F, Xa, Drv, Buf, State1, Encoding)
+ get_chars_loop(Pbs, M, F, Xa, Drv, Shell, Buf, State1, Encoding)
end.
-get_chars_n_loop(Pbs, M, F, Xa, Drv, Buf0, State, Encoding) ->
+get_chars_n_loop(Pbs, M, F, Xa, Drv, Shell, Buf0, State, Encoding) ->
try M:F(State, cast(Buf0, get(read_mode), Encoding), Encoding, Xa) of
{stop,Result,Rest} ->
{ok, Result, Rest};
State1 ->
- case get_chars_echo_off(Pbs, Drv) of
+ case get_chars_echo_off(Pbs, Drv, Shell) of
interrupted ->
{error,{error,interrupted},[]};
terminated ->
{exit,terminated};
Buf ->
- get_chars_n_loop(Pbs, M, F, Xa, Drv, Buf, State1, Encoding)
+ get_chars_n_loop(Pbs, M, F, Xa, Drv, Shell, Buf, State1, Encoding)
end
catch _:_ ->
{error,{error,err_func(M, F, Xa)},[]}
@@ -523,24 +523,24 @@ err_func(_, F, _) ->
%% {done,LineChars,RestChars}
%% interrupted
-get_line(Chars, Pbs, Drv, Encoding) ->
+get_line(Chars, Pbs, Drv, Shell, Encoding) ->
{more_chars,Cont,Rs} = edlin:start(Pbs),
send_drv_reqs(Drv, Rs),
- get_line1(edlin:edit_line(Chars, Cont), Drv, new_stack(get(line_buffer)),
+ get_line1(edlin:edit_line(Chars, Cont), Drv, Shell, new_stack(get(line_buffer)),
Encoding).
-get_line1({done,Line,Rest,Rs}, Drv, Ls, _Encoding) ->
+get_line1({done,Line,Rest,Rs}, Drv, _Shell, Ls, _Encoding) ->
send_drv_reqs(Drv, Rs),
save_line_buffer(Line, get_lines(Ls)),
{done,Line,Rest};
-get_line1({undefined,{_A,Mode,Char},Cs,Cont,Rs}, Drv, Ls0, Encoding)
+get_line1({undefined,{_A,Mode,Char},Cs,Cont,Rs}, Drv, Shell, Ls0, Encoding)
when ((Mode =:= none) and (Char =:= $\^P))
or ((Mode =:= meta_left_sq_bracket) and (Char =:= $A)) ->
send_drv_reqs(Drv, Rs),
case up_stack(save_line(Ls0, edlin:current_line(Cont))) of
{none,_Ls} ->
send_drv(Drv, beep),
- get_line1(edlin:edit_line(Cs, Cont), Drv, Ls0, Encoding);
+ get_line1(edlin:edit_line(Cs, Cont), Drv, Shell, Ls0, Encoding);
{Lcs,Ls} ->
send_drv_reqs(Drv, edlin:erase_line(Cont)),
{more_chars,Ncont,Nrs} = edlin:start(edlin:prompt(Cont)),
@@ -548,16 +548,17 @@ get_line1({undefined,{_A,Mode,Char},Cs,Cont,Rs}, Drv, Ls0, Encoding)
get_line1(edlin:edit_line1(lists:sublist(Lcs, 1, length(Lcs)-1),
Ncont),
Drv,
+ Shell,
Ls, Encoding)
end;
-get_line1({undefined,{_A,Mode,Char},Cs,Cont,Rs}, Drv, Ls0, Encoding)
+get_line1({undefined,{_A,Mode,Char},Cs,Cont,Rs}, Drv, Shell, Ls0, Encoding)
when ((Mode =:= none) and (Char =:= $\^N))
or ((Mode =:= meta_left_sq_bracket) and (Char =:= $B)) ->
send_drv_reqs(Drv, Rs),
case down_stack(save_line(Ls0, edlin:current_line(Cont))) of
{none,_Ls} ->
send_drv(Drv, beep),
- get_line1(edlin:edit_line(Cs, Cont), Drv, Ls0, Encoding);
+ get_line1(edlin:edit_line(Cs, Cont), Drv, Shell, Ls0, Encoding);
{Lcs,Ls} ->
send_drv_reqs(Drv, edlin:erase_line(Cont)),
{more_chars,Ncont,Nrs} = edlin:start(edlin:prompt(Cont)),
@@ -565,6 +566,7 @@ get_line1({undefined,{_A,Mode,Char},Cs,Cont,Rs}, Drv, Ls0, Encoding)
get_line1(edlin:edit_line1(lists:sublist(Lcs, 1, length(Lcs)-1),
Ncont),
Drv,
+ Shell,
Ls, Encoding)
end;
%% ^R = backward search, ^S = forward search.
@@ -577,7 +579,7 @@ get_line1({undefined,{_A,Mode,Char},Cs,Cont,Rs}, Drv, Ls0, Encoding)
%% new modes: search, search_quit, search_found. These are added to
%% the regular ones (none, meta_left_sq_bracket) and handle special
%% cases of history search.
-get_line1({undefined,{_A,Mode,Char},Cs,Cont,Rs}, Drv, Ls, Encoding)
+get_line1({undefined,{_A,Mode,Char},Cs,Cont,Rs}, Drv, Shell, Ls, Encoding)
when ((Mode =:= none) and (Char =:= $\^R)) ->
send_drv_reqs(Drv, Rs),
%% drop current line, move to search mode. We store the current
@@ -587,8 +589,8 @@ get_line1({undefined,{_A,Mode,Char},Cs,Cont,Rs}, Drv, Ls, Encoding)
Pbs = prompt_bytes("(search)`': ", Encoding),
{more_chars,Ncont,Nrs} = edlin:start(Pbs, search),
send_drv_reqs(Drv, Nrs),
- get_line1(edlin:edit_line1(Cs, Ncont), Drv, Ls, Encoding);
-get_line1({expand, Before, Cs0, Cont,Rs}, Drv, Ls0, Encoding) ->
+ get_line1(edlin:edit_line1(Cs, Ncont), Drv, Shell, Ls, Encoding);
+get_line1({expand, Before, Cs0, Cont,Rs}, Drv, Shell, Ls0, Encoding) ->
send_drv_reqs(Drv, Rs),
ExpandFun = get(expand_fun),
{Found, Add, Matches} = ExpandFun(Before),
@@ -603,37 +605,37 @@ get_line1({expand, Before, Cs0, Cont,Rs}, Drv, Ls0, Encoding) ->
send_drv(Drv, {put_chars, unicode, unicode:characters_to_binary(MatchStr,unicode)}),
[$\^L | Cs1]
end,
- get_line1(edlin:edit_line(Cs, Cont), Drv, Ls0, Encoding);
-get_line1({undefined,_Char,Cs,Cont,Rs}, Drv, Ls, Encoding) ->
+ get_line1(edlin:edit_line(Cs, Cont), Drv, Shell, Ls0, Encoding);
+get_line1({undefined,_Char,Cs,Cont,Rs}, Drv, Shell, Ls, Encoding) ->
send_drv_reqs(Drv, Rs),
send_drv(Drv, beep),
- get_line1(edlin:edit_line(Cs, Cont), Drv, Ls, Encoding);
+ get_line1(edlin:edit_line(Cs, Cont), Drv, Shell, Ls, Encoding);
%% The search item was found and accepted (new line entered on the exact
%% result found)
-get_line1({_What,Cont={line,_Prompt,_Chars,search_found},Rs}, Drv, Ls0, Encoding) ->
+get_line1({_What,Cont={line,_Prompt,_Chars,search_found},Rs}, Drv, Shell, Ls0, Encoding) ->
Line = edlin:current_line(Cont),
%% this may create duplicate entries.
Ls = save_line(new_stack(get_lines(Ls0)), Line),
- get_line1({done, Line, "", Rs}, Drv, Ls, Encoding);
+ get_line1({done, Line, "", Rs}, Drv, Shell, Ls, Encoding);
%% The search mode has been exited, but the user wants to remain in line
%% editing mode wherever that was, but editing the search result.
-get_line1({What,Cont={line,_Prompt,_Chars,search_quit},Rs}, Drv, Ls, Encoding) ->
+get_line1({What,Cont={line,_Prompt,_Chars,search_quit},Rs}, Drv, Shell, Ls, Encoding) ->
Line = edlin:current_chars(Cont),
%% Load back the old prompt with the correct line number.
case get(search_quit_prompt) of
undefined -> % should not happen. Fallback.
LsFallback = save_line(new_stack(get_lines(Ls)), Line),
- get_line1({done, "\n", Line, Rs}, Drv, LsFallback, Encoding);
+ get_line1({done, "\n", Line, Rs}, Drv, Shell, LsFallback, Encoding);
Prompt -> % redraw the line and keep going with the same stack position
NCont = {line,Prompt,{lists:reverse(Line),[]},none},
send_drv_reqs(Drv, Rs),
send_drv_reqs(Drv, edlin:erase_line(Cont)),
send_drv_reqs(Drv, edlin:redraw_line(NCont)),
- get_line1({What, NCont ,[]}, Drv, pad_stack(Ls), Encoding)
+ get_line1({What, NCont ,[]}, Drv, Shell, pad_stack(Ls), Encoding)
end;
%% Search mode is entered.
get_line1({What,{line,Prompt,{RevCmd0,_Aft},search},Rs},
- Drv, Ls0, Encoding) ->
+ Drv, Shell, Ls0, Encoding) ->
send_drv_reqs(Drv, Rs),
%% Figure out search direction. ^S and ^R are returned through edlin
%% whenever we received a search while being already in search mode.
@@ -655,82 +657,88 @@ get_line1({What,{line,Prompt,{RevCmd0,_Aft},search},Rs},
{Ls2, {RevCmd, "': "++Line}}
end,
Cont = {line,Prompt,NewStack,search},
- more_data(What, Cont, Drv, Ls, Encoding);
-get_line1({What,Cont0,Rs}, Drv, Ls, Encoding) ->
+ more_data(What, Cont, Drv, Shell, Ls, Encoding);
+get_line1({What,Cont0,Rs}, Drv, Shell, Ls, Encoding) ->
send_drv_reqs(Drv, Rs),
- more_data(What, Cont0, Drv, Ls, Encoding).
+ more_data(What, Cont0, Drv, Shell, Ls, Encoding).
-more_data(What, Cont0, Drv, Ls, Encoding) ->
+more_data(What, Cont0, Drv, Shell, Ls, Encoding) ->
receive
{Drv,{data,Cs}} ->
- get_line1(edlin:edit_line(Cs, Cont0), Drv, Ls, Encoding);
+ get_line1(edlin:edit_line(Cs, Cont0), Drv, Shell, Ls, Encoding);
{Drv,eof} ->
- get_line1(edlin:edit_line(eof, Cont0), Drv, Ls, Encoding);
+ get_line1(edlin:edit_line(eof, Cont0), Drv, Shell, Ls, Encoding);
{io_request,From,ReplyAs,Req} when is_pid(From) ->
{more_chars,Cont,_More} = edlin:edit_line([], Cont0),
send_drv_reqs(Drv, edlin:erase_line(Cont)),
- io_request(Req, From, ReplyAs, Drv, []), %WRONG!!!
+ io_request(Req, From, ReplyAs, Drv, Shell, []), %WRONG!!!
send_drv_reqs(Drv, edlin:redraw_line(Cont)),
- get_line1({more_chars,Cont,[]}, Drv, Ls, Encoding);
+ get_line1({more_chars,Cont,[]}, Drv, Shell, Ls, Encoding);
{reply,{{From,ReplyAs},Reply}} ->
%% We take care of replies from puts here as well
io_reply(From, ReplyAs, Reply),
- more_data(What, Cont0, Drv, Ls, Encoding);
+ more_data(What, Cont0, Drv, Shell, Ls, Encoding);
{'EXIT',Drv,interrupt} ->
interrupted;
{'EXIT',Drv,_} ->
- terminated
+ terminated;
+ {'EXIT',Shell,R} ->
+ exit(R)
after
get_line_timeout(What)->
- get_line1(edlin:edit_line([], Cont0), Drv, Ls, Encoding)
+ get_line1(edlin:edit_line([], Cont0), Drv, Shell, Ls, Encoding)
end.
-get_line_echo_off(Chars, Pbs, Drv) ->
+get_line_echo_off(Chars, Pbs, Drv, Shell) ->
send_drv_reqs(Drv, [{put_chars, unicode,Pbs}]),
- get_line_echo_off1(edit_line(Chars,[]), Drv).
+ get_line_echo_off1(edit_line(Chars,[]), Drv, Shell).
-get_line_echo_off1({Chars,[]}, Drv) ->
+get_line_echo_off1({Chars,[]}, Drv, Shell) ->
receive
{Drv,{data,Cs}} ->
- get_line_echo_off1(edit_line(Cs, Chars), Drv);
+ get_line_echo_off1(edit_line(Cs, Chars), Drv, Shell);
{Drv,eof} ->
- get_line_echo_off1(edit_line(eof, Chars), Drv);
+ get_line_echo_off1(edit_line(eof, Chars), Drv, Shell);
{io_request,From,ReplyAs,Req} when is_pid(From) ->
- io_request(Req, From, ReplyAs, Drv, []),
- get_line_echo_off1({Chars,[]}, Drv);
+ io_request(Req, From, ReplyAs, Drv, Shell, []),
+ get_line_echo_off1({Chars,[]}, Drv, Shell);
{reply,{{From,ReplyAs},Reply}} when From =/= undefined ->
%% We take care of replies from puts here as well
io_reply(From, ReplyAs, Reply),
- get_line_echo_off1({Chars,[]},Drv);
+ get_line_echo_off1({Chars,[]},Drv, Shell);
{'EXIT',Drv,interrupt} ->
interrupted;
{'EXIT',Drv,_} ->
- terminated
+ terminated;
+ {'EXIT',Shell,R} ->
+ exit(R)
end;
-get_line_echo_off1({Chars,Rest}, _Drv) ->
+get_line_echo_off1({Chars,Rest}, _Drv, _Shell) ->
{done,lists:reverse(Chars),case Rest of done -> []; _ -> Rest end}.
-get_chars_echo_off(Pbs, Drv) ->
+get_chars_echo_off(Pbs, Drv, Shell) ->
send_drv_reqs(Drv, [{put_chars, unicode,Pbs}]),
- get_chars_echo_off1(Drv).
+ get_chars_echo_off1(Drv, Shell).
-get_chars_echo_off1(Drv) ->
+get_chars_echo_off1(Drv, Shell) ->
receive
{Drv, {data, Cs}} ->
Cs;
{Drv, eof} ->
eof;
{io_request,From,ReplyAs,Req} when is_pid(From) ->
- io_request(Req, From, ReplyAs, Drv, []),
- get_chars_echo_off1(Drv);
+ io_request(Req, From, ReplyAs, Drv, Shell, []),
+ get_chars_echo_off1(Drv, Shell);
{reply,{{From,ReplyAs},Reply}} when From =/= undefined ->
%% We take care of replies from puts here as well
io_reply(From, ReplyAs, Reply),
- get_chars_echo_off1(Drv);
+ get_chars_echo_off1(Drv, Shell);
{'EXIT',Drv,interrupt} ->
interrupted;
{'EXIT',Drv,_} ->
- terminated
+ terminated;
+ {'EXIT',Shell,R} ->
+ exit(R)
end.
%% We support line editing for the ICANON mode except the following
@@ -861,30 +869,32 @@ search_down_stack(Stack, Substr) ->
%% This is get_line without line editing (except for backspace) and
%% without echo.
-get_password_line(Chars, Drv) ->
- get_password1(edit_password(Chars,[]),Drv).
+get_password_line(Chars, Drv, Shell) ->
+ get_password1(edit_password(Chars,[]),Drv,Shell).
-get_password1({Chars,[]}, Drv) ->
+get_password1({Chars,[]}, Drv, Shell) ->
receive
{Drv,{data,Cs}} ->
- get_password1(edit_password(Cs,Chars),Drv);
+ get_password1(edit_password(Cs,Chars),Drv,Shell);
{io_request,From,ReplyAs,Req} when is_pid(From) ->
%send_drv_reqs(Drv, [{delete_chars, -length(Pbs)}]),
- io_request(Req, From, ReplyAs, Drv, []), %WRONG!!!
+ io_request(Req, From, ReplyAs, Drv, Shell, []), %WRONG!!!
%% I guess the reason the above line is wrong is that Buf is
%% set to []. But do we expect anything but plain output?
- get_password1({Chars, []}, Drv);
+ get_password1({Chars, []}, Drv, Shell);
{reply,{{From,ReplyAs},Reply}} ->
%% We take care of replies from puts here as well
io_reply(From, ReplyAs, Reply),
- get_password1({Chars, []},Drv);
+ get_password1({Chars, []},Drv, Shell);
{'EXIT',Drv,interrupt} ->
interrupted;
{'EXIT',Drv,_} ->
- terminated
+ terminated;
+ {'EXIT',Shell,R} ->
+ exit(R)
end;
-get_password1({Chars,Rest},Drv) ->
+get_password1({Chars,Rest},Drv,_Shell) ->
send_drv_reqs(Drv,[{put_chars, unicode, "\n"}]),
{done,lists:reverse(Chars),case Rest of done -> []; _ -> Rest end}.
diff --git a/lib/kernel/src/hipe_unified_loader.erl b/lib/kernel/src/hipe_unified_loader.erl
index f8199fcf71..fd06f0f7d8 100644
--- a/lib/kernel/src/hipe_unified_loader.erl
+++ b/lib/kernel/src/hipe_unified_loader.erl
@@ -275,6 +275,7 @@ needs_trampolines(Architecture) ->
arm -> true;
powerpc -> true;
ppc64 -> true;
+ amd64 -> true;
_ -> false
end.
diff --git a/lib/kernel/src/inet.erl b/lib/kernel/src/inet.erl
index fe91b0d33e..4bad523dff 100644
--- a/lib/kernel/src/inet.erl
+++ b/lib/kernel/src/inet.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2017. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2018. 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.
@@ -34,7 +34,8 @@
ip/1, stats/0, options/0,
pushf/3, popf/1, close/1, gethostname/0, gethostname/1,
parse_ipv4_address/1, parse_ipv6_address/1, parse_ipv4strict_address/1,
- parse_ipv6strict_address/1, parse_address/1, parse_strict_address/1, ntoa/1]).
+ parse_ipv6strict_address/1, parse_address/1, parse_strict_address/1,
+ ntoa/1, ipv4_mapped_ipv6_address/1]).
-export([connect_options/2, listen_options/2, udp_options/2, sctp_options/2]).
-export([udp_module/1, tcp_module/1, tcp_module/2, sctp_module/1]).
@@ -675,6 +676,14 @@ parse_address(Addr) ->
parse_strict_address(Addr) ->
inet_parse:strict_address(Addr).
+-spec ipv4_mapped_ipv6_address(ip_address()) -> ip_address().
+ipv4_mapped_ipv6_address({D1,D2,D3,D4})
+ when (D1 bor D2 bor D3 bor D4) < 256 ->
+ {0,0,0,0,0,16#ffff,(D1 bsl 8) bor D2,(D3 bsl 8) bor D4};
+ipv4_mapped_ipv6_address({D1,D2,D3,D4,D5,D6,D7,D8})
+ when (D1 bor D2 bor D3 bor D4 bor D5 bor D6 bor D7 bor D8) < 65536 ->
+ {D7 bsr 8,D7 band 255,D8 bsr 8,D8 band 255}.
+
%% Return a list of available options
options() ->
[
@@ -1244,9 +1253,7 @@ gethostbyname_string(Name, Type)
inet ->
inet_parse:ipv4_address(Name);
inet6 ->
- %% XXX should we really translate IPv4 addresses here
- %% even if we do not know if this host can do IPv6?
- inet_parse:ipv6_address(Name)
+ inet_parse:ipv6strict_address(Name)
end of
{ok,IP} ->
{ok,make_hostent(Name, [IP], [], Type)};
diff --git a/lib/kernel/src/inet_hosts.erl b/lib/kernel/src/inet_hosts.erl
index 0bdf00ac30..fc653bf0d3 100644
--- a/lib/kernel/src/inet_hosts.erl
+++ b/lib/kernel/src/inet_hosts.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2016. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2018. 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.
@@ -72,9 +72,6 @@ gethostbyname(Name, Type, Byname, Byaddr) ->
gethostbyaddr({A,B,C,D}=IP) when ?ip(A,B,C,D) ->
gethostbyaddr(IP, inet);
-%% ipv4 only ipv6 address
-gethostbyaddr({0,0,0,0,0,16#ffff=F,G,H}) when ?ip6(0,0,0,0,0,F,G,H) ->
- gethostbyaddr({G bsr 8, G band 255, H bsr 8, H band 255});
gethostbyaddr({A,B,C,D,E,F,G,H}=IP) when ?ip6(A,B,C,D,E,F,G,H) ->
gethostbyaddr(IP, inet6);
gethostbyaddr(Addr) when is_list(Addr) ->
diff --git a/lib/kernel/src/inet_res.erl b/lib/kernel/src/inet_res.erl
index 49aa5f8bda..6454802b04 100644
--- a/lib/kernel/src/inet_res.erl
+++ b/lib/kernel/src/inet_res.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2016. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2018. 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.
@@ -349,9 +349,6 @@ gethostbyaddr_tm({A,B,C,D} = IP, Timer) when ?ip(A,B,C,D) ->
{ok, HEnt} -> {ok, HEnt};
_ -> res_gethostbyaddr(dn_in_addr_arpa(A,B,C,D), IP, Timer)
end;
-%% ipv4 only ipv6 address
-gethostbyaddr_tm({0,0,0,0,0,16#ffff,G,H},Timer) when is_integer(G+H) ->
- gethostbyaddr_tm({G div 256, G rem 256, H div 256, H rem 256},Timer);
gethostbyaddr_tm({A,B,C,D,E,F,G,H} = IP, Timer) when ?ip6(A,B,C,D,E,F,G,H) ->
inet_db:res_update_conf(),
case inet_db:gethostbyaddr(IP) of
@@ -431,28 +428,7 @@ gethostbyname(Name,Family,Timeout) ->
gethostbyname_tm(Name,inet,Timer) ->
getbyname_tm(Name,?S_A,Timer);
gethostbyname_tm(Name,inet6,Timer) ->
- case getbyname_tm(Name,?S_AAAA,Timer) of
- {ok,HEnt} -> {ok,HEnt};
- {error,nxdomain} ->
- case getbyname_tm(Name, ?S_A,Timer) of
- {ok, HEnt} ->
- %% rewrite to a ipv4 only ipv6 address
- {ok,
- HEnt#hostent {
- h_addrtype = inet6,
- h_length = 16,
- h_addr_list =
- lists:map(
- fun({A,B,C,D}) ->
- {0,0,0,0,0,16#ffff,A*256+B,C*256+D}
- end, HEnt#hostent.h_addr_list)
- }};
- Error ->
- Error
- end;
- Error ->
- Error
- end;
+ getbyname_tm(Name,?S_AAAA,Timer);
gethostbyname_tm(_Name, _Family, _Timer) ->
{error, einval}.
diff --git a/lib/kernel/src/net_kernel.erl b/lib/kernel/src/net_kernel.erl
index cdb10a7b12..669adefdf8 100644
--- a/lib/kernel/src/net_kernel.erl
+++ b/lib/kernel/src/net_kernel.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2017. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2018. 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.
@@ -222,8 +222,7 @@ get_net_ticktime() ->
Error :: error | {error, term()}.
monitor_nodes(Flag) ->
case catch process_flag(monitor_nodes, Flag) of
- true -> ok;
- false -> ok;
+ N when is_integer(N) -> ok;
_ -> mk_monitor_nodes_error(Flag, [])
end.
@@ -236,8 +235,7 @@ monitor_nodes(Flag) ->
Error :: error | {error, term()}.
monitor_nodes(Flag, Opts) ->
case catch process_flag({monitor_nodes, Opts}, Flag) of
- true -> ok;
- false -> ok;
+ N when is_integer(N) -> ok;
_ -> mk_monitor_nodes_error(Flag, Opts)
end.
@@ -1777,16 +1775,16 @@ async_reply({reply, Msg, State}, From) ->
async_gen_server_reply(From, Msg) ->
{Pid, Tag} = From,
M = {Tag, Msg},
- case catch erlang:send(Pid, M, [nosuspend, noconnect]) of
+ try erlang:send(Pid, M, [nosuspend, noconnect]) of
ok ->
ok;
nosuspend ->
_ = spawn(fun() -> catch erlang:send(Pid, M, [noconnect]) end),
ok;
noconnect ->
- ok; % The gen module takes care of this case.
- {'EXIT', _} ->
- ok
+ ok % The gen module takes care of this case.
+ catch
+ _:_ -> ok
end.
call_owner(Owner, Msg) ->
diff --git a/lib/kernel/src/os.erl b/lib/kernel/src/os.erl
index fbc046c8f9..77c883f57f 100644
--- a/lib/kernel/src/os.erl
+++ b/lib/kernel/src/os.erl
@@ -21,11 +21,11 @@
%% Provides a common operating system interface.
--export([type/0, version/0, cmd/1, find_executable/1, find_executable/2]).
+-export([type/0, version/0, cmd/1, cmd/2, find_executable/1, find_executable/2]).
-include("file.hrl").
--export_type([env_var_name/0, env_var_value/0, env_var_name_value/0, command_input/0]).
+-export_type([env_var_name/0, env_var_value/0, env_var_name_value/0]).
-export([getenv/0, getenv/1, getenv/2, putenv/2, unsetenv/1]).
@@ -35,14 +35,17 @@
perf_counter/1, set_env_var/2, set_signal/2, system_time/0,
system_time/1, timestamp/0, unset_env_var/1]).
+-type os_command() :: atom() | io_lib:chars().
+-type os_command_opts() :: #{ max_size => non_neg_integer() | infinity }.
+
+-export_type([os_command/0, os_command_opts/0]).
+
-type env_var_name() :: nonempty_string().
-type env_var_value() :: string().
-type env_var_name_value() :: nonempty_string().
--type command_input() :: atom() | io_lib:chars().
-
-spec list_env_vars() -> [{env_var_name(), env_var_value()}].
list_env_vars() ->
erlang:nif_error(undef).
@@ -260,14 +263,20 @@ extensions() ->
%% Executes the given command in the default shell for the operating system.
-spec cmd(Command) -> string() when
- Command :: os:command_input().
+ Command :: os_command().
cmd(Cmd) ->
+ cmd(Cmd, #{ }).
+
+-spec cmd(Command, Options) -> string() when
+ Command :: os_command(),
+ Options :: os_command_opts().
+cmd(Cmd, Opts) ->
{SpawnCmd, SpawnOpts, SpawnInput, Eot} = mk_cmd(os:type(), validate(Cmd)),
Port = open_port({spawn, SpawnCmd}, [binary, stderr_to_stdout,
stream, in, hide | SpawnOpts]),
MonRef = erlang:monitor(port, Port),
true = port_command(Port, SpawnInput),
- Bytes = get_data(Port, MonRef, Eot, []),
+ Bytes = get_data(Port, MonRef, Eot, [], 0, maps:get(max_size, Opts, infinity)),
demonitor(MonRef, [flush]),
String = unicode:characters_to_list(Bytes),
if %% Convert to unicode list if possible otherwise return bytes
@@ -332,12 +341,13 @@ validate2([List|Rest]) when is_list(List) ->
validate2(List),
validate2(Rest).
-get_data(Port, MonRef, Eot, Sofar) ->
+get_data(Port, MonRef, Eot, Sofar, Size, Max) ->
receive
{Port, {data, Bytes}} ->
- case eot(Bytes, Eot) of
+ case eot(Bytes, Eot, Size, Max) of
more ->
- get_data(Port, MonRef, Eot, [Sofar,Bytes]);
+ get_data(Port, MonRef, Eot, [Sofar, Bytes],
+ Size + byte_size(Bytes), Max);
Last ->
catch port_close(Port),
flush_until_down(Port, MonRef),
@@ -348,13 +358,16 @@ get_data(Port, MonRef, Eot, Sofar) ->
iolist_to_binary(Sofar)
end.
-eot(_Bs, <<>>) ->
+eot(Bs, <<>>, Size, Max) when Size + byte_size(Bs) < Max ->
more;
-eot(Bs, Eot) ->
+eot(Bs, <<>>, Size, Max) ->
+ binary:part(Bs, {0, Max - Size});
+eot(Bs, Eot, Size, Max) ->
case binary:match(Bs, Eot) of
- nomatch -> more;
- {Pos, _} ->
- binary:part(Bs,{0, Pos})
+ {Pos, _} when Size + Pos < Max ->
+ binary:part(Bs,{0, Pos});
+ _ ->
+ eot(Bs, <<>>, Size, Max)
end.
%% When port_close returns we know that all the
diff --git a/lib/kernel/src/rpc.erl b/lib/kernel/src/rpc.erl
index 0e0b7dffa3..d197de942f 100644
--- a/lib/kernel/src/rpc.erl
+++ b/lib/kernel/src/rpc.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2017. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2018. 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.
@@ -418,10 +418,7 @@ abcast(Name, Mess) ->
abcast([Node|Tail], Name, Mess) ->
Dest = {Name,Node},
- case catch erlang:send(Dest, Mess, [noconnect]) of
- noconnect -> spawn(erlang, send, [Dest,Mess]), ok;
- _ -> ok
- end,
+ try erlang:send(Dest, Mess) catch error:_ -> ok end,
abcast(Tail, Name, Mess);
abcast([], _,_) -> abcast.
@@ -498,7 +495,7 @@ start_monitor(Node, Name) ->
Module :: module(),
Function :: atom(),
Args :: [term()],
- ResL :: [term()],
+ ResL :: [Res :: term() | {'badrpc', Reason :: term()}],
BadNodes :: [node()].
multicall(M, F, A) ->
@@ -509,14 +506,14 @@ multicall(M, F, A) ->
Module :: module(),
Function :: atom(),
Args :: [term()],
- ResL :: [term()],
+ ResL :: [Res :: term() | {'badrpc', Reason :: term()}],
BadNodes :: [node()];
(Module, Function, Args, Timeout) -> {ResL, BadNodes} when
Module :: module(),
Function :: atom(),
Args :: [term()],
Timeout :: timeout(),
- ResL :: [term()],
+ ResL :: [Res :: term() | {'badrpc', Reason :: term()}],
BadNodes :: [node()].
multicall(Nodes, M, F, A) when is_list(Nodes) ->
@@ -531,7 +528,7 @@ multicall(M, F, A, Timeout) ->
Function :: atom(),
Args :: [term()],
Timeout :: timeout(),
- ResL :: [term()],
+ ResL :: [Res :: term() | {'badrpc', Reason :: term()}],
BadNodes :: [node()].
multicall(Nodes, M, F, A, infinity)
diff --git a/lib/kernel/test/code_SUITE.erl b/lib/kernel/test/code_SUITE.erl
index 569753155f..902196def2 100644
--- a/lib/kernel/test/code_SUITE.erl
+++ b/lib/kernel/test/code_SUITE.erl
@@ -931,37 +931,34 @@ purge_stacktrace(Config) when is_list(Config) ->
code:purge(code_b_test),
try code_b_test:call(fun(b) -> ok end, a)
catch
- error:function_clause ->
+ error:function_clause:Stacktrace ->
code:load_file(code_b_test),
- case erlang:get_stacktrace() of
+ case Stacktrace of
[{?MODULE,_,[a],_},
{code_b_test,call,2,_},
{?MODULE,purge_stacktrace,1,_}|_] ->
- false = code:purge(code_b_test),
- [] = erlang:get_stacktrace()
+ false = code:purge(code_b_test)
end
end,
try code_b_test:call(nofun, 2)
catch
- error:function_clause ->
+ error:function_clause:Stacktrace2 ->
code:load_file(code_b_test),
- case erlang:get_stacktrace() of
+ case Stacktrace2 of
[{code_b_test,call,[nofun,2],_},
{?MODULE,purge_stacktrace,1,_}|_] ->
- false = code:purge(code_b_test),
- [] = erlang:get_stacktrace()
+ false = code:purge(code_b_test)
end
end,
Args = [erlang,error,[badarg]],
try code_b_test:call(erlang, error, [badarg,Args])
catch
- error:badarg ->
+ error:badarg:Stacktrace3 ->
code:load_file(code_b_test),
- case erlang:get_stacktrace() of
+ case Stacktrace3 of
[{code_b_test,call,Args,_},
{?MODULE,purge_stacktrace,1,_}|_] ->
- false = code:purge(code_b_test),
- [] = erlang:get_stacktrace()
+ false = code:purge(code_b_test)
end
end,
ok.
diff --git a/lib/kernel/test/erl_distribution_SUITE.erl b/lib/kernel/test/erl_distribution_SUITE.erl
index bbfaa9d147..0470f09f29 100644
--- a/lib/kernel/test/erl_distribution_SUITE.erl
+++ b/lib/kernel/test/erl_distribution_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2017. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2018. 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.
@@ -95,7 +95,11 @@ init_per_group(_GroupName, Config) ->
end_per_group(_GroupName, Config) ->
Config.
-
+init_per_testcase(TC, Config) when TC == hostnames;
+ TC == nodenames ->
+ file:make_dir("hostnames_nodedir"),
+ file:write_file("hostnames_nodedir/ignore_core_files",""),
+ Config;
init_per_testcase(Func, Config) when is_atom(Func), is_list(Config) ->
Config.
@@ -251,7 +255,7 @@ test_node(Name, Illigal) ->
end,
net_kernel:monitor_nodes(true),
BinCommand = unicode:characters_to_binary(Command, utf8),
- Prt = open_port({spawn, BinCommand}, [stream]),
+ Prt = open_port({spawn, BinCommand}, [stream,{cd,"hostnames_nodedir"}]),
Node = list_to_atom(Name),
receive
{nodeup, Node} ->
@@ -459,9 +463,9 @@ run_remote_test([FuncStr, TestNodeStr | Args]) ->
1
end
catch
- C:E ->
+ C:E:S ->
io:format("Node ~p got EXCEPTION ~p:~p\nat ~p\n",
- [node(), C, E, erlang:get_stacktrace()]),
+ [node(), C, E, S]),
2
end,
io:format("Node ~p doing halt(~p).\n",[node(), Status]),
@@ -1140,17 +1144,16 @@ monitor_nodes_otp_6481_test(Config, TestType) when is_list(Config) ->
TestMonNodeState = monitor_node_state(),
%% io:format("~p~n", [TestMonNodeState]),
TestMonNodeState =
- MonNodeState
+ case TestType of
+ nodedown -> [];
+ nodeup -> [{self(), []}]
+ end
+ ++ lists:map(fun (_) -> {MN, []} end, Seq)
++ case TestType of
nodedown -> [{self(), []}];
nodeup -> []
end
- ++ lists:map(fun (_) -> {MN, []} end, Seq)
- ++ case TestType of
- nodedown -> [];
- nodeup -> [{self(), []}]
- end,
-
+ ++ MonNodeState,
{ok, Node} = start_node(Name, "", this),
receive {nodeup, Node} -> ok end,
diff --git a/lib/kernel/test/file_SUITE.erl b/lib/kernel/test/file_SUITE.erl
index 0cb8087a76..ff93f25e25 100644
--- a/lib/kernel/test/file_SUITE.erl
+++ b/lib/kernel/test/file_SUITE.erl
@@ -56,7 +56,8 @@
open1/1,
old_modes/1, new_modes/1, path_open/1, open_errors/1]).
-export([ file_info_basic_file/1, file_info_basic_directory/1,
- file_info_bad/1, file_info_times/1, file_write_file_info/1]).
+ file_info_bad/1, file_info_times/1, file_write_file_info/1,
+ file_wfi_helpers/1]).
-export([rename/1, access/1, truncate/1, datasync/1, sync/1,
read_write/1, pread_write/1, append/1, exclusive/1]).
-export([ e_delete/1, e_rename/1, e_make_dir/1, e_del_dir/1]).
@@ -152,7 +153,8 @@ groups() ->
{pos, [], [pos1, pos2, pos3]},
{file_info, [],
[file_info_basic_file, file_info_basic_directory,
- file_info_bad, file_info_times, file_write_file_info]},
+ file_info_bad, file_info_times, file_write_file_info,
+ file_wfi_helpers]},
{consult, [], [consult1, path_consult]},
{eval, [], [eval1, path_eval]},
{script, [], [script1, path_script]},
@@ -399,11 +401,11 @@ read_write_0(Str, {Func, ReadFun}, Options) ->
io:format("~p:~p: ~p ERROR: ~ts vs~n ~w~n - ~p~n",
[?MODULE, Line, Func, Str, ReadBytes, Options]),
exit({error, ?LINE});
- error:What ->
+ error:What:Stacktrace ->
io:format("~p:??: ~p ERROR: ~p from~n ~w~n ~p~n",
[?MODULE, Func, What, Str, Options]),
- io:format("\t~p~n", [erlang:get_stacktrace()]),
+ io:format("\t~p~n", [Stacktrace]),
exit({error, ?LINE})
end.
@@ -1608,6 +1610,39 @@ file_write_file_info(Config) when is_list(Config) ->
[] = flush(),
ok.
+file_wfi_helpers(Config) when is_list(Config) ->
+ RootDir = get_good_directory(Config),
+ io:format("RootDir = ~p", [RootDir]),
+
+ Name = filename:join(RootDir,
+ atom_to_list(?MODULE) ++ "_wfi_helpers"),
+
+ ok = ?FILE_MODULE:write_file(Name, "hello again"),
+ NewTime = {{1997, 02, 15}, {13, 18, 20}},
+ ok = ?FILE_MODULE:change_time(Name, NewTime, NewTime),
+
+ {ok, #file_info{atime=NewActAtime, mtime=NewTime}} =
+ ?FILE_MODULE:read_file_info(Name),
+
+ NewFilteredAtime = filter_atime(NewTime, Config),
+ NewFilteredAtime = filter_atime(NewActAtime, Config),
+
+ %% Make the file unwritable
+ ok = ?FILE_MODULE:change_mode(Name, 8#400),
+ {error, eacces} = ?FILE_MODULE:write_file(Name, "hello again"),
+
+ %% ... and writable again
+ ok = ?FILE_MODULE:change_mode(Name, 8#600),
+ ok = ?FILE_MODULE:write_file(Name, "hello again"),
+
+ %% We have no idea which users will work, so all we can do is to check
+ %% that it returns enoent instead of crashing.
+ {error, enoent} = ?FILE_MODULE:change_group("bogus file name", 0),
+ {error, enoent} = ?FILE_MODULE:change_owner("bogus file name", 0),
+
+ [] = flush(),
+ ok.
+
%% Returns a directory on a file system that has correct file times.
get_good_directory(Config) ->
@@ -2177,7 +2212,7 @@ e_delete(Config) when is_list(Config) ->
Base, #file_info {mode=0}),
{error, eacces} = ?FILE_MODULE:delete(Afile),
?FILE_MODULE:write_file_info(
- Base, #file_info {mode=8#600})
+ Base, #file_info {mode=8#700})
end,
[] = flush(),
@@ -2308,7 +2343,7 @@ e_make_dir(Config) when is_list(Config) ->
?FILE_MODULE:write_file_info(Base, #file_info {mode=0}),
{error, eacces} = ?FILE_MODULE:make_dir(filename:join(Base, "xxxx")),
?FILE_MODULE:write_file_info(
- Base, #file_info {mode=8#600})
+ Base, #file_info {mode=8#700})
end,
ok.
@@ -2354,7 +2389,7 @@ e_del_dir(Config) when is_list(Config) ->
ok = ?FILE_MODULE:make_dir(ADirectory),
?FILE_MODULE:write_file_info( Base, #file_info {mode=0}),
{error, eacces} = ?FILE_MODULE:del_dir(ADirectory),
- ?FILE_MODULE:write_file_info( Base, #file_info {mode=8#600})
+ ?FILE_MODULE:write_file_info( Base, #file_info {mode=8#700})
end,
[] = flush(),
ok.
diff --git a/lib/kernel/test/gen_sctp_SUITE.erl b/lib/kernel/test/gen_sctp_SUITE.erl
index 620ab235a0..9dde00652c 100644
--- a/lib/kernel/test/gen_sctp_SUITE.erl
+++ b/lib/kernel/test/gen_sctp_SUITE.erl
@@ -1038,8 +1038,7 @@ do_from_other_process(Fun) ->
Result ->
Parent ! {Ref,Result}
catch
- Class:Reason ->
- Stacktrace = erlang:get_stacktrace(),
+ Class:Reason:Stacktrace ->
Parent ! {Ref,Class,Reason,Stacktrace}
end
end),
@@ -1617,8 +1616,7 @@ s_start(Socket, Timeout, Parent) ->
try
s_loop(Socket, Timeout, Parent, Handler, gb_trees:empty())
catch
- Class:Reason ->
- Stacktrace = erlang:get_stacktrace(),
+ Class:Reason:Stacktrace ->
io:format(?MODULE_STRING":socket exception ~w:~w at~n"
"~p.~n", [Class,Reason,Stacktrace]),
erlang:raise(Class, Reason, Stacktrace)
diff --git a/lib/kernel/test/gen_tcp_api_SUITE.erl b/lib/kernel/test/gen_tcp_api_SUITE.erl
index 12d22519ce..0fe44e8a88 100644
--- a/lib/kernel/test/gen_tcp_api_SUITE.erl
+++ b/lib/kernel/test/gen_tcp_api_SUITE.erl
@@ -605,9 +605,9 @@ ok({ok,V}) -> V;
ok(NotOk) ->
try throw(not_ok)
catch
- Thrown ->
+ throw:Thrown:Stacktrace ->
erlang:raise(
- error, {Thrown, NotOk}, tl(erlang:get_stacktrace()))
+ error, {Thrown, NotOk}, tl(Stacktrace))
end.
get_localaddr() ->
diff --git a/lib/kernel/test/gen_udp_SUITE.erl b/lib/kernel/test/gen_udp_SUITE.erl
index 96e495505a..6a50239c2a 100644
--- a/lib/kernel/test/gen_udp_SUITE.erl
+++ b/lib/kernel/test/gen_udp_SUITE.erl
@@ -755,9 +755,9 @@ ok({ok,V}) -> V;
ok(NotOk) ->
try throw(not_ok)
catch
- Thrown ->
+ throw:Thrown:Stacktrace ->
erlang:raise(
- error, {Thrown, NotOk}, tl(erlang:get_stacktrace()))
+ error, {Thrown, NotOk}, tl(Stacktrace))
end.
diff --git a/lib/kernel/test/inet_SUITE.erl b/lib/kernel/test/inet_SUITE.erl
index 3b502be8b8..2e5f8c7d2c 100644
--- a/lib/kernel/test/inet_SUITE.erl
+++ b/lib/kernel/test/inet_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2017. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2018. 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.
@@ -40,7 +40,8 @@
lookup_bad_search_option/1,
getif/1,
getif_ifr_name_overflow/1,getservbyname_overflow/1, getifaddrs/1,
- parse_strict_address/1, simple_netns/1, simple_netns_open/1,
+ parse_strict_address/1, ipv4_mapped_ipv6_address/1,
+ simple_netns/1, simple_netns_open/1,
simple_bind_to_device/1, simple_bind_to_device_open/1]).
-export([get_hosts/1, get_ipv6_hosts/1, parse_hosts/1, parse_address/1,
@@ -667,6 +668,26 @@ parse_strict_address(Config) when is_list(Config) ->
{ok, {3089,3106,23603,50240,0,0,119,136}} =
inet:parse_strict_address("c11:0c22:5c33:c440::077:0088").
+ipv4_mapped_ipv6_address(Config) when is_list(Config) ->
+ {D1,D2,D3,D4} = IPv4Address =
+ {rand:uniform(256) - 1,
+ rand:uniform(256) - 1,
+ rand:uniform(256) - 1,
+ rand:uniform(256) - 1},
+ E7 = (D1 bsl 8) bor D2,
+ E8 = (D3 bsl 8) bor D4,
+ io:format("IPv4Address: ~p.~n", [IPv4Address]),
+ {0,0,0,0,0,65535,E7,E8} = inet:ipv4_mapped_ipv6_address(IPv4Address),
+ IPv6Address =
+ {rand:uniform(65536) - 1,
+ rand:uniform(65536) - 1,
+ rand:uniform(65536) - 1,
+ rand:uniform(65536) - 1,
+ rand:uniform(65536) - 1,
+ rand:uniform(65536) - 1, E7, E8},
+ IPv4Address = inet:ipv4_mapped_ipv6_address(IPv6Address),
+ ok.
+
t_gethostnative(Config) when is_list(Config) ->
%% this will result in 26 bytes sent which causes problem in Windows
%% if the port-program has not assured stdin to be read in BINARY mode
@@ -1083,11 +1104,9 @@ ifaddrs([{If,Opts}|IOs]) ->
#ifopts{flags=F} = Ifopts = check_ifopts(Opts, #ifopts{name=If}),
case F of
{flags,Flags} ->
- case lists:member(up, Flags) of
- true ->
- Ifopts#ifopts.addrs;
- false ->
- []
+ case lists:member(running, Flags) of
+ true -> Ifopts#ifopts.addrs;
+ false -> []
end ++ ifaddrs(IOs);
undefined ->
ifaddrs(IOs)
diff --git a/lib/kernel/test/inet_res_SUITE.erl b/lib/kernel/test/inet_res_SUITE.erl
index 6691ad9c06..2a5b8d0044 100644
--- a/lib/kernel/test/inet_res_SUITE.erl
+++ b/lib/kernel/test/inet_res_SUITE.erl
@@ -217,10 +217,10 @@ proxy_start(TC, {NS,P}) ->
spawn_link(
fun () ->
try proxy_start(TC, NS, P, Parent, Tag)
- catch C:X ->
+ catch C:X:Stacktrace ->
io:format(
"~w: ~w:~p ~p~n",
- [self(),C,X,erlang:get_stacktrace()])
+ [self(),C,X,Stacktrace])
end
end),
receive {started,Tag,Port} ->
diff --git a/lib/kernel/test/init_SUITE.erl b/lib/kernel/test/init_SUITE.erl
index 2b59eb2bfe..c8415b34e5 100644
--- a/lib/kernel/test/init_SUITE.erl
+++ b/lib/kernel/test/init_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2016. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2018. 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.
@@ -365,7 +365,9 @@ restart(Config) when is_list(Config) ->
%% 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,
+ io:format("SysProcs0=~p~n", [SysProcs0]),
+ [InitPid, PurgerPid, LitCollectorPid,
+ DirtySigNPid, DirtySigHPid, DirtySigMPid] = SysProcs0,
InitPid = rpc:call(Node, erlang, whereis, [init]),
PurgerPid = rpc:call(Node, erlang, whereis, [erts_code_purger]),
Procs = rpc:call(Node, erlang, processes, []),
@@ -381,7 +383,9 @@ restart(Config) when is_list(Config) ->
ok = wait_restart(30, Node),
SysProcs1 = rpc:call(Node, ?MODULE, find_system_processes, []),
- [InitPid1, PurgerPid1, LitCollectorPid1, DirtyCodePid1] = SysProcs1,
+ io:format("SysProcs1=~p~n", [SysProcs1]),
+ [InitPid1, PurgerPid1, LitCollectorPid1,
+ DirtySigNPid1, DirtySigHPid1, DirtySigMPid1] = SysProcs1,
%% Still the same init process!
InitPid1 = rpc:call(Node, erlang, whereis, [init]),
@@ -394,20 +398,18 @@ restart(Config) when is_list(Config) ->
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,
+ LitCollectorP = pid_to_list(LitCollectorPid),
+ LitCollectorP = pid_to_list(LitCollectorPid1),
+
+ %% and same normal dirty signal handler process!
+ DirtySigNP = pid_to_list(DirtySigNPid),
+ DirtySigNP = pid_to_list(DirtySigNPid1),
+ %% and same high dirty signal handler process!
+ DirtySigHP = pid_to_list(DirtySigHPid),
+ DirtySigHP = pid_to_list(DirtySigHPid1),
+ %% and same max dirty signal handler process!
+ DirtySigMP = pid_to_list(DirtySigMPid),
+ DirtySigMP = pid_to_list(DirtySigMPid1),
NewProcs0 = rpc:call(Node, erlang, processes, []),
NewProcs = NewProcs0 -- SysProcs1,
@@ -433,7 +435,9 @@ restart(Config) when is_list(Config) ->
-record(sys_procs, {init,
code_purger,
literal_collector,
- dirty_proc_checker}).
+ dirty_sig_handler_normal,
+ dirty_sig_handler_high,
+ dirty_sig_handler_max}).
find_system_processes() ->
find_system_procs(processes(), #sys_procs{}).
@@ -442,21 +446,32 @@ find_system_procs([], SysProcs) ->
[SysProcs#sys_procs.init,
SysProcs#sys_procs.code_purger,
SysProcs#sys_procs.literal_collector,
- SysProcs#sys_procs.dirty_proc_checker];
+ SysProcs#sys_procs.dirty_sig_handler_normal,
+ SysProcs#sys_procs.dirty_sig_handler_high,
+ SysProcs#sys_procs.dirty_sig_handler_max];
find_system_procs([P|Ps], SysProcs) ->
- case process_info(P, initial_call) of
- {initial_call,{otp_ring0,start,2}} ->
+ case process_info(P, [initial_call, priority]) 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}} ->
+ [{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}} ->
+ [{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});
+ [{initial_call,{erts_dirty_process_signal_handler,start,0}},
+ {priority,normal}] ->
+ undefined = SysProcs#sys_procs.dirty_sig_handler_normal,
+ find_system_procs(Ps, SysProcs#sys_procs{dirty_sig_handler_normal = P});
+ [{initial_call,{erts_dirty_process_signal_handler,start,0}},
+ {priority,high}] ->
+ undefined = SysProcs#sys_procs.dirty_sig_handler_high,
+ find_system_procs(Ps, SysProcs#sys_procs{dirty_sig_handler_high = P});
+ [{initial_call,{erts_dirty_process_signal_handler,start,0}},
+ {priority,max}] ->
+ undefined = SysProcs#sys_procs.dirty_sig_handler_max,
+ find_system_procs(Ps, SysProcs#sys_procs{dirty_sig_handler_max = P});
_ ->
find_system_procs(Ps, SysProcs)
end.
diff --git a/lib/kernel/test/os_SUITE.erl b/lib/kernel/test/os_SUITE.erl
index 8056321448..591fbb2125 100644
--- a/lib/kernel/test/os_SUITE.erl
+++ b/lib/kernel/test/os_SUITE.erl
@@ -26,7 +26,8 @@
null_in_command/1, space_in_name/1, bad_command/1,
find_executable/1, unix_comment_in_command/1, deep_list_command/1,
large_output_command/1, background_command/0, background_command/1,
- message_leak/1, close_stdin/0, close_stdin/1, perf_counter_api/1]).
+ message_leak/1, close_stdin/0, close_stdin/1, max_size_command/1,
+ perf_counter_api/1]).
-include_lib("common_test/include/ct.hrl").
@@ -39,7 +40,7 @@ all() ->
space_in_name, bad_command,
find_executable, unix_comment_in_command, deep_list_command,
large_output_command, background_command, message_leak,
- close_stdin, perf_counter_api].
+ close_stdin, max_size_command, perf_counter_api].
groups() ->
[].
@@ -322,6 +323,19 @@ close_stdin(Config) ->
"-1" = os:cmd(Fds).
+max_size_command(_Config) ->
+
+ Res20 = os:cmd("cat /dev/zero", #{ max_size => 20 }),
+ 20 = length(Res20),
+
+ Res0 = os:cmd("cat /dev/zero", #{ max_size => 0 }),
+ 0 = length(Res0),
+
+ Res32768 = os:cmd("cat /dev/zero", #{ max_size => 32768 }),
+ 32768 = length(Res32768),
+
+ ResHello = string:trim(os:cmd("echo hello", #{ max_size => 20 })),
+ 5 = length(ResHello).
%% Test that the os:perf_counter api works as expected
perf_counter_api(_Config) ->
diff --git a/lib/kernel/test/pdict_SUITE.erl b/lib/kernel/test/pdict_SUITE.erl
index d105952df9..a891451c82 100644
--- a/lib/kernel/test/pdict_SUITE.erl
+++ b/lib/kernel/test/pdict_SUITE.erl
@@ -33,6 +33,7 @@
init_per_group/2,end_per_group/2,
mixed/1,
literals/1,
+ destructive/1,
simple/1, complicated/1, heavy/1, simple_all_keys/1, info/1]).
-export([init_per_testcase/2, end_per_testcase/2]).
-export([other_process/2]).
@@ -52,6 +53,7 @@ suite() ->
all() ->
[simple, complicated, heavy, simple_all_keys, info,
literals,
+ destructive,
mixed].
groups() ->
@@ -367,6 +369,36 @@ match_keys(All) ->
ok.
+%% Test destructive put optimization of immed values
+%% does not affect get/0 or process_info.
+destructive(_Config) ->
+ Keys = lists:seq(1,100),
+ [put(Key, 17) || Key <- Keys],
+ Get1 = get(),
+ {dictionary,PI1} = process_info(self(), dictionary),
+
+ [begin
+ {Key, 17} = lists:keyfind(Key, 1, Get1),
+ {Key, 17} = lists:keyfind(Key, 1, PI1)
+ end
+ || Key <- Keys],
+
+ [17 = put(Key, 42) || Key <- Keys], % Mutate
+
+ Get2 = get(),
+ {dictionary,PI2} = process_info(self(), dictionary),
+
+ [begin
+ {Key, 17} = lists:keyfind(Key, 1, Get1),
+ {Key, 17} = lists:keyfind(Key, 1, PI1),
+ {Key, 42} = lists:keyfind(Key, 1, Get2),
+ {Key, 42} = lists:keyfind(Key, 1, PI2)
+
+ end
+ || Key <- Keys],
+
+ ok.
+
%% Do random mixed put/erase to test grow/shrink
%% Written for a temporary bug in gc during shrink
mixed(_Config) ->
diff --git a/lib/kernel/test/prim_file_SUITE.erl b/lib/kernel/test/prim_file_SUITE.erl
index ab62f4dc34..5bb230d1c4 100644
--- a/lib/kernel/test/prim_file_SUITE.erl
+++ b/lib/kernel/test/prim_file_SUITE.erl
@@ -1306,7 +1306,7 @@ e_delete(Config) when is_list(Config) ->
Base, #file_info {mode=0}),
{error, eacces} = ?PRIM_FILE:delete(Afile),
?PRIM_FILE:write_file_info(
- Base, #file_info {mode=8#600})
+ Base, #file_info {mode=8#700})
end,
ok.
@@ -1442,7 +1442,7 @@ e_make_dir(Config) when is_list(Config) ->
?PRIM_FILE:write_file_info(Base, #file_info {mode=0}),
{error, eacces} =
?PRIM_FILE:make_dir(filename:join(Base, "xxxx")),
- ?PRIM_FILE:write_file_info(Base, #file_info {mode=8#600})
+ ?PRIM_FILE:write_file_info(Base, #file_info {mode=8#700})
end,
ok.
@@ -1492,7 +1492,7 @@ e_del_dir(Config) when is_list(Config) ->
?PRIM_FILE:write_file_info(Base, #file_info {mode=0}),
{error, eacces} = ?PRIM_FILE:del_dir(ADirectory),
?PRIM_FILE:write_file_info(
- Base, #file_info {mode=8#600})
+ Base, #file_info {mode=8#700})
end,
ok.
diff --git a/lib/kernel/vsn.mk b/lib/kernel/vsn.mk
index 106bda01ca..60a1b0bff8 100644
--- a/lib/kernel/vsn.mk
+++ b/lib/kernel/vsn.mk
@@ -1 +1 @@
-KERNEL_VSN = 5.4.1
+KERNEL_VSN = 5.4.3
diff --git a/lib/mnesia/src/mnesia.erl b/lib/mnesia/src/mnesia.erl
index 95f7d4afc1..6d87544bd2 100644
--- a/lib/mnesia/src/mnesia.erl
+++ b/lib/mnesia/src/mnesia.erl
@@ -177,8 +177,8 @@
%% Local function in order to avoid external function call
val(Var) ->
- case ?catch_val(Var) of
- {'EXIT', _} -> mnesia_lib:other_val(Var);
+ case ?catch_val_and_stack(Var) of
+ {'EXIT', Stacktrace} -> mnesia_lib:other_val(Var, Stacktrace);
Value -> Value
end.
diff --git a/lib/mnesia/src/mnesia.hrl b/lib/mnesia/src/mnesia.hrl
index da7e662288..e36222d098 100644
--- a/lib/mnesia/src/mnesia.hrl
+++ b/lib/mnesia/src/mnesia.hrl
@@ -47,6 +47,10 @@
-define(catch_val(Var), (try ?ets_lookup_element(mnesia_gvar, Var, 2)
catch error:_ -> {'EXIT', {badarg, []}} end)).
+-define(catch_val_and_stack(Var),
+ (try ?ets_lookup_element(mnesia_gvar, Var, 2)
+ catch error:_:_Stacktrace -> {'EXIT', _Stacktrace} end)).
+
%% It's important that counter is first, since we compare tid's
-record(tid,
diff --git a/lib/mnesia/src/mnesia_bup.erl b/lib/mnesia/src/mnesia_bup.erl
index 34f16f178b..9dda340333 100644
--- a/lib/mnesia/src/mnesia_bup.erl
+++ b/lib/mnesia/src/mnesia_bup.erl
@@ -90,9 +90,9 @@ iterate(Mod, Fun, Opaque, Acc) ->
catch throw:Err ->
close_read(R2),
Err;
- _:Reason ->
+ _:Reason:Stacktrace ->
close_read(R2),
- {error, {Reason, erlang:get_stacktrace()}}
+ {error, {Reason, Stacktrace}}
end
catch throw:{error,_} = Err ->
Err
@@ -198,9 +198,9 @@ do_read_schema_section(R) ->
try
{R3, RawSchema} = safe_apply(R2, read, [R2#restore.bup_data]),
do_read_schema_section(R3, verify_header(RawSchema), [])
- catch T:E ->
+ catch T:E:S ->
close_read(R2),
- erlang:raise(T,E,erlang:get_stacktrace())
+ erlang:raise(T,E,S)
end.
do_read_schema_section(R, {ok, B, C, []}, Acc) ->
diff --git a/lib/mnesia/src/mnesia_checkpoint.erl b/lib/mnesia/src/mnesia_checkpoint.erl
index 8112378ffd..8290237908 100644
--- a/lib/mnesia/src/mnesia_checkpoint.erl
+++ b/lib/mnesia/src/mnesia_checkpoint.erl
@@ -1269,9 +1269,9 @@ system_code_change(Cp, _Module, _OldVsn, _Extra) ->
%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Local function in order to avoid external function call
val(Var) ->
- case ?catch_val(Var) of
- {'EXIT', _} -> mnesia_lib:other_val(Var);
- _VaLuE_ -> _VaLuE_
+ case ?catch_val_and_stack(Var) of
+ {'EXIT', Stacktrace} -> mnesia_lib:other_val(Var, Stacktrace);
+ Value -> Value
end.
-
diff --git a/lib/mnesia/src/mnesia_controller.erl b/lib/mnesia/src/mnesia_controller.erl
index 77013489b3..77e67a59db 100644
--- a/lib/mnesia/src/mnesia_controller.erl
+++ b/lib/mnesia/src/mnesia_controller.erl
@@ -185,9 +185,10 @@ max_loaders() ->
worker_res
}).
+%% Local function in order to avoid external function call
val(Var) ->
- case ?catch_val(Var) of
- {'EXIT', _} -> mnesia_lib:other_val(Var);
+ case ?catch_val_and_stack(Var) of
+ {'EXIT', Stacktrace} -> mnesia_lib:other_val(Var, Stacktrace);
Value -> Value
end.
diff --git a/lib/mnesia/src/mnesia_dumper.erl b/lib/mnesia/src/mnesia_dumper.erl
index f0ed7aef4a..31bcc1451f 100644
--- a/lib/mnesia/src/mnesia_dumper.erl
+++ b/lib/mnesia/src/mnesia_dumper.erl
@@ -191,8 +191,7 @@ do_perform_dump(Cont, InPlace, InitBy, Regulator, OldVersion) ->
try insert_recs(Recs, InPlace, InitBy, Regulator, OldVersion) of
Version ->
do_perform_dump(C2, InPlace, InitBy, Regulator, Version)
- catch _:R when R =/= fatal ->
- ST = erlang:get_stacktrace(),
+ catch _:R:ST when R =/= fatal ->
Reason = {"Transaction log dump error: ~tp~n", [{R, ST}]},
close_files(InPlace, {error, Reason}, InitBy),
exit(Reason)
@@ -325,8 +324,7 @@ perform_update(Tid, SchemaOps, _DumperMode, _UseDir) ->
?eval_debug_fun({?MODULE, post_dump}, [InitBy]),
close_files(InPlace, ok, InitBy),
ok
- catch _:Reason when Reason =/= fatal ->
- ST = erlang:get_stacktrace(),
+ catch _:Reason:ST when Reason =/= fatal ->
Error = {error, {"Schema update error", {Reason, ST}}},
close_files(InPlace, Error, InitBy),
fatal("Schema update error ~tp ~tp", [{Reason,ST}, SchemaOps])
@@ -1471,8 +1469,9 @@ regulate(RegulatorPid) ->
{regulated, RegulatorPid} -> ok
end.
+%% Local function in order to avoid external function call
val(Var) ->
- case ?catch_val(Var) of
- {'EXIT', _} -> mnesia_lib:other_val(Var);
+ case ?catch_val_and_stack(Var) of
+ {'EXIT', Stacktrace} -> mnesia_lib:other_val(Var, Stacktrace);
Value -> Value
end.
diff --git a/lib/mnesia/src/mnesia_frag.erl b/lib/mnesia/src/mnesia_frag.erl
index c39f30e140..63fb4981fe 100644
--- a/lib/mnesia/src/mnesia_frag.erl
+++ b/lib/mnesia/src/mnesia_frag.erl
@@ -1157,9 +1157,10 @@ remove_node(Node, Cs) ->
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Helpers
+%% Local function in order to avoid external function call
val(Var) ->
- case ?catch_val(Var) of
- {'EXIT', _} -> mnesia_lib:other_val(Var);
+ case ?catch_val_and_stack(Var) of
+ {'EXIT', Stacktrace} -> mnesia_lib:other_val(Var, Stacktrace);
Value -> Value
end.
diff --git a/lib/mnesia/src/mnesia_lib.erl b/lib/mnesia/src/mnesia_lib.erl
index 53fdd76de8..56f6926df0 100644
--- a/lib/mnesia/src/mnesia_lib.erl
+++ b/lib/mnesia/src/mnesia_lib.erl
@@ -116,7 +116,7 @@
lock_table/1,
mkcore/1,
not_active_here/1,
- other_val/1,
+ other_val/2,
overload_read/0,
overload_read/1,
overload_set/2,
@@ -435,8 +435,8 @@ validate_record(Tab, Obj) ->
%%
val(Var) ->
- case ?catch_val(Var) of
- {'EXIT', _} -> other_val(Var);
+ case ?catch_val_and_stack(Var) of
+ {'EXIT', Stacktrace} -> other_val(Var, Stacktrace);
_VaLuE_ -> _VaLuE_
end.
@@ -446,9 +446,9 @@ set(Var, Val) ->
unset(Var) ->
?ets_delete(mnesia_gvar, Var).
-other_val(Var) ->
+other_val(Var, Stacktrace) ->
case other_val_1(Var) of
- error -> pr_other(Var);
+ error -> pr_other(Var, Stacktrace);
Val -> Val
end.
@@ -460,8 +460,8 @@ other_val_1(Var) ->
_ -> error
end.
--spec pr_other(_) -> no_return().
-pr_other(Var) ->
+-spec pr_other(_, _) -> no_return().
+pr_other(Var, Stacktrace) ->
Why =
case is_running() of
no -> {node_not_running, node()};
@@ -469,7 +469,7 @@ pr_other(Var) ->
end,
verbose("~p (~tp) val(mnesia_gvar, ~tw) -> ~p ~tp ~n",
[self(), process_info(self(), registered_name),
- Var, Why, erlang:get_stacktrace()]),
+ Var, Why, Stacktrace]),
mnesia:abort(Why).
%% Some functions for list valued variables
diff --git a/lib/mnesia/src/mnesia_loader.erl b/lib/mnesia/src/mnesia_loader.erl
index 4c6336cb73..bbceb9ba66 100644
--- a/lib/mnesia/src/mnesia_loader.erl
+++ b/lib/mnesia/src/mnesia_loader.erl
@@ -34,9 +34,10 @@
-include("mnesia.hrl").
+%% Local function in order to avoid external function call
val(Var) ->
- case ?catch_val(Var) of
- {'EXIT', _} -> mnesia_lib:other_val(Var);
+ case ?catch_val_and_stack(Var) of
+ {'EXIT', Stacktrace} -> mnesia_lib:other_val(Var, Stacktrace);
Value -> Value
end.
@@ -535,7 +536,7 @@ init_table(Tab, _, Fun, _DetsInfo,_) ->
try
true = ets:init_table(Tab, Fun),
ok
- catch _:Else -> {Else, erlang:get_stacktrace()}
+ catch _:Else:Stacktrace -> {Else, Stacktrace}
end.
@@ -777,9 +778,9 @@ do_send_table(Pid, Tab, Storage, RemoteS) ->
throw:receiver_died ->
cleanup_tab_copier(Pid, Storage, Tab),
ok;
- error:Reason -> %% Prepare failed
+ error:Reason:Stacktrace -> %% Prepare failed
cleanup_tab_copier(Pid, Storage, Tab),
- {error, {tab_copier, Tab, {Reason, erlang:get_stacktrace()}}}
+ {error, {tab_copier, Tab, {Reason, Stacktrace}}}
after
unlink(whereis(mnesia_tm))
end.
diff --git a/lib/mnesia/src/mnesia_locker.erl b/lib/mnesia/src/mnesia_locker.erl
index 073b48abc0..710bf38cb5 100644
--- a/lib/mnesia/src/mnesia_locker.erl
+++ b/lib/mnesia/src/mnesia_locker.erl
@@ -97,10 +97,11 @@ init(Parent) ->
end,
loop(#state{supervisor = Parent}).
+%% Local function in order to avoid external function call
val(Var) ->
- case ?catch_val(Var) of
- {'EXIT', _} -> mnesia_lib:other_val(Var);
- _VaLuE_ -> _VaLuE_
+ case ?catch_val_and_stack(Var) of
+ {'EXIT', Stacktrace} -> mnesia_lib:other_val(Var, Stacktrace);
+ Value -> Value
end.
reply(From, R) ->
diff --git a/lib/mnesia/src/mnesia_recover.erl b/lib/mnesia/src/mnesia_recover.erl
index d792070332..475bf3c327 100644
--- a/lib/mnesia/src/mnesia_recover.erl
+++ b/lib/mnesia/src/mnesia_recover.erl
@@ -177,10 +177,10 @@ disconnect(Node) ->
log_decision(D) ->
cast({log_decision, D}).
+%% Local function in order to avoid external function call
val(Var) ->
- case ?catch_val(Var) of
- {'EXIT', _Reason} ->
- mnesia_lib:other_val(Var);
+ case ?catch_val_and_stack(Var) of
+ {'EXIT', Stacktrace} -> mnesia_lib:other_val(Var, Stacktrace);
Value -> Value
end.
diff --git a/lib/mnesia/src/mnesia_schema.erl b/lib/mnesia/src/mnesia_schema.erl
index 71952af31c..de6c3fb4ee 100644
--- a/lib/mnesia/src/mnesia_schema.erl
+++ b/lib/mnesia/src/mnesia_schema.erl
@@ -181,9 +181,10 @@ exit_on_error({error, Reason}) ->
exit_on_error(GoodRes) ->
GoodRes.
+%% Local function in order to avoid external function call
val(Var) ->
- case ?catch_val(Var) of
- {'EXIT', _} -> mnesia_lib:other_val(Var);
+ case ?catch_val_and_stack(Var) of
+ {'EXIT', Stacktrace} -> mnesia_lib:other_val(Var, Stacktrace);
Value -> Value
end.
@@ -2695,10 +2696,10 @@ prepare_op(_Tid, {op, transform, Fun, TabDef}, _WaitFor) ->
Objs ->
mnesia_lib:db_fixtable(Storage, Tab, false),
{true, Objs, mandatory}
- catch _:Reason ->
+ catch _:Reason:Stacktrace ->
mnesia_lib:db_fixtable(Storage, Tab, false),
mnesia_lib:important("Transform function failed: '~tp' in '~tp'",
- [Reason, erlang:get_stacktrace()]),
+ [Reason, Stacktrace]),
exit({"Bad transform function", Tab, Fun, node(), Reason})
end
end;
diff --git a/lib/mnesia/src/mnesia_tm.erl b/lib/mnesia/src/mnesia_tm.erl
index ebf580d09e..3f6f6c98d8 100644
--- a/lib/mnesia/src/mnesia_tm.erl
+++ b/lib/mnesia/src/mnesia_tm.erl
@@ -121,10 +121,11 @@ init(Parent) ->
proc_lib:init_ack(Parent, {ok, self()}),
doit_loop(#state{supervisor = Parent}).
+%% Local function in order to avoid external function call
val(Var) ->
- case ?catch_val(Var) of
- {'EXIT', _} -> mnesia_lib:other_val(Var);
- _VaLuE_ -> _VaLuE_
+ case ?catch_val_and_stack(Var) of
+ {'EXIT', Stacktrace} -> mnesia_lib:other_val(Var, Stacktrace);
+ Value -> Value
end.
reply({From,Ref}, R) ->
@@ -597,9 +598,9 @@ recover_coordinator(Tid, Etabs) ->
false -> %% When killed before store havn't been copied to
ok %% to the new nested trans store.
end
- catch _:Reason ->
+ catch _:Reason:Stacktrace ->
dbg_out("Recovery of coordinator ~p failed: ~tp~n",
- [Tid, {Reason, erlang:get_stacktrace()}]),
+ [Tid, {Reason, Stacktrace}]),
Protocol = asym_trans,
tell_outcome(Tid, Protocol, node(), CheckNodes, TellNodes)
end,
@@ -825,8 +826,7 @@ execute_transaction(Fun, Args, Factor, Retries, Type) ->
catch throw:Value -> %% User called throw
Reason = {aborted, {throw, Value}},
return_abort(Fun, Args, Reason);
- error:Reason ->
- ST = erlang:get_stacktrace(),
+ error:Reason:ST ->
check_exit(Fun, Args, Factor, Retries, {Reason,ST}, Type);
_:Reason ->
check_exit(Fun, Args, Factor, Retries, Reason, Type)
@@ -1796,14 +1796,13 @@ do_update(Tid, Storage, [Op | Ops], OldRes) ->
try do_update_op(Tid, Storage, Op) of
ok -> do_update(Tid, Storage, Ops, OldRes);
NewRes -> do_update(Tid, Storage, Ops, NewRes)
- catch _:Reason ->
+ catch _:Reason:ST ->
%% This may only happen when we recently have
%% deleted our local replica, changed storage_type
%% or transformed table
%% BUGBUG: Updates may be lost if storage_type is changed.
%% Determine actual storage type and try again.
%% BUGBUG: Updates may be lost if table is transformed.
- ST = erlang:get_stacktrace(),
verbose("do_update in ~w failed: ~tp -> {'EXIT', ~tp}~n",
[Tid, Op, {Reason, ST}]),
do_update(Tid, Storage, Ops, OldRes)
@@ -1914,11 +1913,10 @@ commit_clear([H|R], Tid, Storage, Tab, K, Obj)
do_snmp(_, []) -> ok;
do_snmp(Tid, [Head|Tail]) ->
try mnesia_snmp_hook:update(Head)
- catch _:Reason ->
+ catch _:Reason:ST ->
%% This should only happen when we recently have
%% deleted our local replica or recently deattached
%% the snmp table
- ST = erlang:get_stacktrace(),
verbose("do_snmp in ~w failed: ~tp -> {'EXIT', ~tp}~n",
[Tid, Head, {Reason, ST}])
end,
@@ -2212,7 +2210,7 @@ display_pid_info(Pid) ->
Other
end,
Reds = fetch(reductions, Info),
- LM = length(fetch(messages, Info)),
+ LM = fetch(message_queue_len, Info),
pformat(io_lib:format("~p", [Pid]),
io_lib:format("~tp", [Call]),
io_lib:format("~tp", [Curr]), Reds, LM)
diff --git a/lib/mnesia/test/mnesia_test_lib.erl b/lib/mnesia/test/mnesia_test_lib.erl
index 78dbe7ffde..db0e7beac6 100644
--- a/lib/mnesia/test/mnesia_test_lib.erl
+++ b/lib/mnesia/test/mnesia_test_lib.erl
@@ -470,9 +470,9 @@ get_suite(Mod, {group, Suite}) ->
{_, _, TCList} = lists:keyfind(Suite, 1, Groups),
TCList
catch
- _:Reason ->
+ _:Reason:Stacktrace ->
io:format("Not implemented ~p ~p (~p ~p)~n",
- [Mod,Suite,Reason, erlang:get_stacktrace()]),
+ [Mod,Suite,Reason,Stacktrace]),
'NYI'
end;
get_suite(Mod, all) ->
diff --git a/lib/mnesia/test/mnesia_test_lib.hrl b/lib/mnesia/test/mnesia_test_lib.hrl
index ba7eb10ea2..90a3c686b0 100644
--- a/lib/mnesia/test/mnesia_test_lib.hrl
+++ b/lib/mnesia/test/mnesia_test_lib.hrl
@@ -55,25 +55,25 @@
?error("Not Matching Actual result was:~n ~p~n",[_AR_0]),
{fail,_AR_0}
catch
- exit:{aborted, _ER_1} when
+ exit:{aborted, _ER_1}:Stacktrace when
element(1, _ER_1) =:= node_not_running;
element(1, _ER_1) =:= bad_commit;
element(1, _ER_1) =:= cyclic ->
%% Need to re-raise these to restart transaction
- erlang:raise(exit, {aborted, _ER_1}, erlang:get_stacktrace());
- exit:_AR_1 ->
+ erlang:raise(exit, {aborted, _ER_1}, Stacktrace);
+ exit:_AR_1:Stacktrace ->
case fun(_AR_EXIT_) -> {'EXIT', _AR_EXIT_} end(_AR_1) of
_AR_2 = ExpectedRes ->
?verbose("ok, ~n Result as expected:~p~n",[_AR_2]),
{success,_AR_2};
_AR_2 ->
?error("Not Matching Actual result was:~n ~p~n ~p~n",
- [_AR_2, erlang:get_stacktrace()]),
+ [_AR_2, Stacktrace]),
{fail,_AR_2}
end;
- _T1_:_AR_1 ->
+ _T1_:_AR_1:Stacktrace ->
?error("Not Matching Actual result was:~n ~p~n ~p~n",
- [{_T1_,_AR_1}, erlang:get_stacktrace()]),
+ [{_T1_,_AR_1}, Stacktrace]),
{fail,{_T1_,_AR_1}}
end
end()).
diff --git a/lib/observer/doc/src/notes.xml b/lib/observer/doc/src/notes.xml
index 96cd89b375..c0b8309af6 100644
--- a/lib/observer/doc/src/notes.xml
+++ b/lib/observer/doc/src/notes.xml
@@ -32,6 +32,64 @@
<p>This document describes the changes made to the Observer
application.</p>
+<section><title>Observer 2.7</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ etop.hrl used a relative path to include
+ observer_backend.hrl, this is now changed to use
+ include_lib instead. runtime_tools/include is added to
+ the tertiary bootstrap.</p>
+ <p>
+ Own Id: OTP-14842 Aux Id: ERL-534 </p>
+ </item>
+ <item>
+ <p>
+ If a crashdump was truncated in the attributes section
+ for a module, crashdump_viewer would crash when a module
+ view was opened from the GUI. This bug was introduced in
+ OTP-20.2 and is now corrected.</p>
+ <p>
+ Own Id: OTP-14846 Aux Id: ERL-537 </p>
+ </item>
+ <item>
+ <p>
+ Optimized ets and mnesia table view tab in observer gui,
+ listing 10000 tables was previously very slow.</p>
+ <p>
+ Own Id: OTP-14856 Aux Id: ERIERL-117 </p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ When a process has many links and/or monitors, it could
+ earlier take very long time to display the process
+ information window. This is now improved by only showing
+ a few links and monitors, and then an link named
+ "more..." to expand the rest.</p>
+ <p>
+ Own Id: OTP-14725</p>
+ </item>
+ <item>
+ <p>
+ More crash dump info such as: process binary virtual heap
+ stats, full info for process causing out-of-mem during
+ GC, more port related info, and dirty scheduler info.</p>
+ <p>
+ Own Id: OTP-14820</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Observer 2.6</title>
<section><title>Fixed Bugs and Malfunctions</title>
diff --git a/lib/observer/src/cdv_html_wx.erl b/lib/observer/src/cdv_html_wx.erl
index 4b43b6a840..77a9d3207b 100644
--- a/lib/observer/src/cdv_html_wx.erl
+++ b/lib/observer/src/cdv_html_wx.erl
@@ -94,21 +94,21 @@ handle_event(#wx{event=#wxHtmlLink{type=command_html_link_clicked,
case Target of
"#Binary?" ++ BinSpec ->
[{"offset",Off},{"size",Size},{"pos",Pos}] =
- httpd:parse_query(BinSpec),
+ uri_string:dissect_query(BinSpec),
Id = {cdv, {list_to_integer(Off),
list_to_integer(Size),
list_to_integer(Pos)}},
expand(Id,cdv_bin_cb,State);
"#OBSBinary?" ++ BinSpec ->
[{"key1",Preview},{"key2",Size},{"key3",Hash}] =
- httpd:parse_query(BinSpec),
+ uri_string:dissect_query(BinSpec),
Id = {obs, {Tab, {list_to_integer(Preview),
list_to_integer(Size),
list_to_integer(Hash)}}},
expand(Id,cdv_bin_cb,State);
"#Term?" ++ TermKeys ->
[{"key1",Key1},{"key2",Key2},{"key3",Key3}] =
- httpd:parse_query(TermKeys),
+ uri_string:dissect_query(TermKeys),
Id = {cdv, {Tab,{list_to_integer(Key1),
list_to_integer(Key2),
list_to_integer(Key3)}}},
diff --git a/lib/observer/src/cdv_port_cb.erl b/lib/observer/src/cdv_port_cb.erl
index b5cbe8132d..6bb8f07a74 100644
--- a/lib/observer/src/cdv_port_cb.erl
+++ b/lib/observer/src/cdv_port_cb.erl
@@ -34,7 +34,8 @@
-define(COL_CONN, ?COL_ID+1).
-define(COL_NAME, ?COL_CONN+1).
-define(COL_CTRL, ?COL_NAME+1).
--define(COL_SLOT, ?COL_CTRL+1).
+-define(COL_QUEUE, ?COL_CTRL+1).
+-define(COL_SLOT, ?COL_QUEUE+1).
@@ -44,6 +45,7 @@ col_to_elem(?COL_ID) -> #port.id;
col_to_elem(?COL_CONN) -> #port.connected;
col_to_elem(?COL_NAME) -> #port.name;
col_to_elem(?COL_CTRL) -> #port.controls;
+col_to_elem(?COL_QUEUE) -> #port.queue;
col_to_elem(?COL_SLOT) -> #port.slot.
col_spec() ->
@@ -51,6 +53,7 @@ col_spec() ->
{"Connected", ?wxLIST_FORMAT_LEFT, 120},
{"Name", ?wxLIST_FORMAT_LEFT, 150},
{"Controls", ?wxLIST_FORMAT_LEFT, 200},
+ {"Queue", ?wxLIST_FORMAT_RIGHT, 100},
{"Slot", ?wxLIST_FORMAT_RIGHT, 50}].
get_info(_) ->
@@ -96,9 +99,17 @@ format(D) ->
info_fields() ->
[{"Overview",
[{"Name", name},
+ {"State", state},
+ {"Task Flags", task_flags},
{"Connected", {click,connected}},
{"Slot", slot},
- {"Controls", controls}]},
+ {"Controls", controls},
+ {"Input bytes", input},
+ {"Output bytes", output},
+ {"Queue bytes", queue},
+ {"Port data", port_data}]},
{scroll_boxes,
[{"Links",1,{click,links}},
- {"Monitors",1,{click,monitors}}]}].
+ {"Monitors",1,{click,monitors}},
+ {"Suspended",1,{click,suspended}}
+ ]}].
diff --git a/lib/observer/src/cdv_proc_cb.erl b/lib/observer/src/cdv_proc_cb.erl
index f10650bbb7..0ea23dd7cb 100644
--- a/lib/observer/src/cdv_proc_cb.erl
+++ b/lib/observer/src/cdv_proc_cb.erl
@@ -149,6 +149,10 @@ info_fields() ->
{"Old Heap", old_heap},
{"Heap Unused", heap_unused},
{"Old Heap Unused", old_heap_unused},
+ {"Binary vheap", bin_vheap},
+ {"Old Binary vheap", old_bin_vheap},
+ {"Binary vheap unused", bin_vheap_unused},
+ {"Old Binary vheap unused", old_bin_vheap_unused},
{"Number of Heap Fragements", num_heap_frag},
{"Heap Fragment Data",heap_frag_data},
{"New Heap Start", new_heap_start},
diff --git a/lib/observer/src/cdv_sched_cb.erl b/lib/observer/src/cdv_sched_cb.erl
index 192aaf31a7..d2696a276f 100644
--- a/lib/observer/src/cdv_sched_cb.erl
+++ b/lib/observer/src/cdv_sched_cb.erl
@@ -31,7 +31,8 @@
%% Columns
-define(COL_ID, 0).
--define(COL_PROC, ?COL_ID+1).
+-define(COL_TYPE, ?COL_ID+1).
+-define(COL_PROC, ?COL_TYPE+1).
-define(COL_PORT, ?COL_PROC+1).
-define(COL_RQL, ?COL_PORT+1).
-define(COL_PQL, ?COL_RQL+1).
@@ -39,6 +40,7 @@
%% Callbacks for cdv_virtual_list_wx
col_to_elem(id) -> col_to_elem(?COL_ID);
col_to_elem(?COL_ID) -> #sched.name;
+col_to_elem(?COL_TYPE) -> #sched.type;
col_to_elem(?COL_PROC) -> #sched.process;
col_to_elem(?COL_PORT) -> #sched.port;
col_to_elem(?COL_RQL) -> #sched.run_q;
@@ -46,6 +48,7 @@ col_to_elem(?COL_PQL) -> #sched.port_q.
col_spec() ->
[{"Id", ?wxLIST_FORMAT_RIGHT, 50},
+ {"Type", ?wxLIST_FORMAT_CENTER, 100},
{"Current Process", ?wxLIST_FORMAT_CENTER, 130},
{"Current Port", ?wxLIST_FORMAT_CENTER, 130},
{"Run Queue Length", ?wxLIST_FORMAT_RIGHT, 180},
@@ -73,7 +76,8 @@ detail_pages() ->
[{"Scheduler Information", fun init_gen_page/2}].
init_gen_page(Parent, Info0) ->
- Fields = info_fields(),
+ Type = proplists:get_value(type, Info0),
+ Fields = info_fields(Type),
Details = proplists:get_value(details, Info0),
Info = if is_map(Details) -> Info0 ++ maps:to_list(Details);
true -> Info0
@@ -81,15 +85,16 @@ init_gen_page(Parent, Info0) ->
cdv_info_wx:start_link(Parent,{Fields,Info,[]}).
%%% Internal
-info_fields() ->
+info_fields(Type) ->
[{"Scheduler Overview",
[{"Id", id},
+ {"Type", type},
{"Current Process",process},
{"Current Port", port},
{"Sleep Info Flags", sleep_info},
{"Sleep Aux Work", sleep_aux}
]},
- {"Run Queues",
+ {run_queues_header(Type),
[{"Flags", runq_flags},
{"Priority Max Length", runq_max},
{"Priority High Length", runq_high},
@@ -116,3 +121,8 @@ info_fields() ->
{" ", {currp_stack, 11}}
]}
].
+
+run_queues_header(normal) ->
+ "Run Queues";
+run_queues_header(DirtyX) ->
+ "Run Queues (common for all '" ++ atom_to_list(DirtyX) ++ "' schedulers)".
diff --git a/lib/observer/src/crashdump_viewer.erl b/lib/observer/src/crashdump_viewer.erl
index bba97624a7..d0c14db486 100644
--- a/lib/observer/src/crashdump_viewer.erl
+++ b/lib/observer/src/crashdump_viewer.erl
@@ -116,6 +116,10 @@
-define(allocator,allocator).
-define(atoms,atoms).
-define(binary,binary).
+-define(dirty_cpu_scheduler,dirty_cpu_scheduler).
+-define(dirty_cpu_run_queue,dirty_cpu_run_queue).
+-define(dirty_io_scheduler,dirty_io_scheduler).
+-define(dirty_io_run_queue,dirty_io_run_queue).
-define(ende,ende).
-define(erl_crash_dump,erl_crash_dump).
-define(ets,ets).
@@ -1222,6 +1226,18 @@ all_procinfo(Fd,Fun,Proc,WS,LineHead) ->
"OldHeap unused" ->
Bytes = list_to_integer(bytes(Fd))*WS,
get_procinfo(Fd,Fun,Proc#proc{old_heap_unused=Bytes},WS);
+ "BinVHeap" ->
+ Bytes = list_to_integer(bytes(Fd))*WS,
+ get_procinfo(Fd,Fun,Proc#proc{bin_vheap=Bytes},WS);
+ "OldBinVHeap" ->
+ Bytes = list_to_integer(bytes(Fd))*WS,
+ get_procinfo(Fd,Fun,Proc#proc{old_bin_vheap=Bytes},WS);
+ "BinVHeap unused" ->
+ Bytes = list_to_integer(bytes(Fd))*WS,
+ get_procinfo(Fd,Fun,Proc#proc{bin_vheap_unused=Bytes},WS);
+ "OldBinVHeap unused" ->
+ Bytes = list_to_integer(bytes(Fd))*WS,
+ get_procinfo(Fd,Fun,Proc#proc{old_bin_vheap_unused=Bytes},WS);
"New heap start" ->
get_procinfo(Fd,Fun,Proc#proc{new_heap_start=bytes(Fd)},WS);
"New heap top" ->
@@ -1632,6 +1648,10 @@ port_to_tuple("#Port<"++Port) ->
get_portinfo(Fd,Port) ->
case line_head(Fd) of
+ "State" ->
+ get_portinfo(Fd,Port#port{state=bytes(Fd)});
+ "Task Flags" ->
+ get_portinfo(Fd,Port#port{task_flags=bytes(Fd)});
"Slot" ->
%% stored as integer so we can sort on it
get_portinfo(Fd,Port#port{slot=list_to_integer(bytes(Fd))});
@@ -1656,6 +1676,10 @@ get_portinfo(Fd,Port) ->
{Pid,Pid++" ("++Ref++")"}
end || Mon <- Monitors0],
get_portinfo(Fd,Port#port{monitors=Monitors});
+ "Suspended" ->
+ Pids = split_pid_list_no_space(bytes(Fd)),
+ Suspended = [{Pid,Pid} || Pid <- Pids],
+ get_portinfo(Fd,Port#port{suspended=Suspended});
"Port controls linked-in driver" ->
Str = lists:flatten(["Linked in driver: " | string(Fd)]),
get_portinfo(Fd,Port#port{controls=Str});
@@ -1671,6 +1695,15 @@ get_portinfo(Fd,Port) ->
"Port is UNIX fd not opened by emulator" ->
Str = lists:flatten(["UNIX fd not opened by emulator: "| string(Fd)]),
get_portinfo(Fd,Port#port{controls=Str});
+ "Input" ->
+ get_portinfo(Fd,Port#port{input=list_to_integer(bytes(Fd))});
+ "Output" ->
+ get_portinfo(Fd,Port#port{output=list_to_integer(bytes(Fd))});
+ "Queue" ->
+ get_portinfo(Fd,Port#port{queue=list_to_integer(bytes(Fd))});
+ "Port Data" ->
+ get_portinfo(Fd,Port#port{port_data=string(Fd)});
+
"=" ++ _next_tag ->
Port;
Other ->
@@ -2503,73 +2536,142 @@ get_indextableinfo1(Fd,IndexTable) ->
%%-----------------------------------------------------------------
%% Page with scheduler table information
schedulers(File) ->
- case lookup_index(?scheduler) of
- [] ->
- [];
- Schedulers ->
- Fd = open(File),
- R = lists:map(fun({Name,Start}) ->
- get_schedulerinfo(Fd,Name,Start)
- end,
- Schedulers),
- close(Fd),
- R
- end.
+ Fd = open(File),
-get_schedulerinfo(Fd,Name,Start) ->
+ Schds0 = case lookup_index(?scheduler) of
+ [] ->
+ [];
+ Normals ->
+ [{Normals, #sched{type=normal}}]
+ end,
+ Schds1 = case lookup_index(?dirty_cpu_scheduler) of
+ [] ->
+ Schds0;
+ DirtyCpus ->
+ [{DirtyCpus, get_dirty_runqueue(Fd, ?dirty_cpu_run_queue)}
+ | Schds0]
+ end,
+ Schds2 = case lookup_index(?dirty_io_scheduler) of
+ [] ->
+ Schds1;
+ DirtyIos ->
+ [{DirtyIos, get_dirty_runqueue(Fd, ?dirty_io_run_queue)}
+ | Schds1]
+ end,
+
+ R = schedulers1(Fd, Schds2, []),
+ close(Fd),
+ R.
+
+schedulers1(_Fd, [], Acc) ->
+ Acc;
+schedulers1(Fd, [{Scheds,Sched0} | Tail], Acc0) ->
+ Acc1 = lists:foldl(fun({Name,Start}, AccIn) ->
+ [get_schedulerinfo(Fd,Name,Start,Sched0) | AccIn]
+ end,
+ Acc0,
+ Scheds),
+ schedulers1(Fd, Tail, Acc1).
+
+get_schedulerinfo(Fd,Name,Start,Sched0) ->
pos_bof(Fd,Start),
- get_schedulerinfo1(Fd,#sched{name=Name}).
+ get_schedulerinfo1(Fd,Sched0#sched{name=list_to_integer(Name)}).
-get_schedulerinfo1(Fd,Sched=#sched{details=Ds}) ->
+sched_type(?dirty_cpu_run_queue) -> dirty_cpu;
+sched_type(?dirty_io_run_queue) -> dirty_io.
+
+get_schedulerinfo1(Fd, Sched) ->
+ case get_schedulerinfo2(Fd, Sched) of
+ {more, Sched2} ->
+ get_schedulerinfo1(Fd, Sched2);
+ {done, Sched2} ->
+ Sched2
+ end.
+
+get_schedulerinfo2(Fd, Sched=#sched{details=Ds}) ->
case line_head(Fd) of
"Current Process" ->
- get_schedulerinfo1(Fd,Sched#sched{process=bytes(Fd, "None")});
+ {more, Sched#sched{process=bytes(Fd, "None")}};
"Current Port" ->
- get_schedulerinfo1(Fd,Sched#sched{port=bytes(Fd, "None")});
+ {more, Sched#sched{port=bytes(Fd, "None")}};
+
+ "Scheduler Sleep Info Flags" ->
+ {more, Sched#sched{details=Ds#{sleep_info=>bytes(Fd, "None")}}};
+ "Scheduler Sleep Info Aux Work" ->
+ {more, Sched#sched{details=Ds#{sleep_aux=>bytes(Fd, "None")}}};
+
+ "Current Process State" ->
+ {more, Sched#sched{details=Ds#{currp_state=>bytes(Fd)}}};
+ "Current Process Internal State" ->
+ {more, Sched#sched{details=Ds#{currp_int_state=>bytes(Fd)}}};
+ "Current Process Program counter" ->
+ {more, Sched#sched{details=Ds#{currp_prg_cnt=>string(Fd)}}};
+ "Current Process CP" ->
+ {more, Sched#sched{details=Ds#{currp_cp=>string(Fd)}}};
+ "Current Process Limited Stack Trace" ->
+ %% If there shall be last in scheduler information block
+ {done, Sched#sched{details=get_limited_stack(Fd, 0, Ds)}};
+
+ "=" ++ _next_tag ->
+ {done, Sched};
+
+ Other ->
+ case Sched#sched.type of
+ normal ->
+ get_runqueue_info2(Fd, Other, Sched);
+ _ ->
+ unexpected(Fd,Other,"dirty scheduler information"),
+ {done, Sched}
+ end
+ end.
+
+get_dirty_runqueue(Fd, Tag) ->
+ case lookup_index(Tag) of
+ [{_, Start}] ->
+ pos_bof(Fd,Start),
+ get_runqueue_info1(Fd,#sched{type=sched_type(Tag)});
+ [] ->
+ #sched{}
+ end.
+
+get_runqueue_info1(Fd, Sched) ->
+ case get_runqueue_info2(Fd, line_head(Fd), Sched) of
+ {more, Sched2} ->
+ get_runqueue_info1(Fd, Sched2);
+ {done, Sched2} ->
+ Sched2
+ end.
+
+get_runqueue_info2(Fd, LineHead, Sched=#sched{details=Ds}) ->
+ case LineHead of
"Run Queue Max Length" ->
RQMax = list_to_integer(bytes(Fd)),
RQ = RQMax + Sched#sched.run_q,
- get_schedulerinfo1(Fd,Sched#sched{run_q=RQ, details=Ds#{runq_max=>RQMax}});
+ {more, Sched#sched{run_q=RQ, details=Ds#{runq_max=>RQMax}}};
"Run Queue High Length" ->
RQHigh = list_to_integer(bytes(Fd)),
RQ = RQHigh + Sched#sched.run_q,
- get_schedulerinfo1(Fd,Sched#sched{run_q=RQ, details=Ds#{runq_high=>RQHigh}});
+ {more, Sched#sched{run_q=RQ, details=Ds#{runq_high=>RQHigh}}};
"Run Queue Normal Length" ->
RQNorm = list_to_integer(bytes(Fd)),
RQ = RQNorm + Sched#sched.run_q,
- get_schedulerinfo1(Fd,Sched#sched{run_q=RQ, details=Ds#{runq_norm=>RQNorm}});
+ {more, Sched#sched{run_q=RQ, details=Ds#{runq_norm=>RQNorm}}};
"Run Queue Low Length" ->
RQLow = list_to_integer(bytes(Fd)),
RQ = RQLow + Sched#sched.run_q,
- get_schedulerinfo1(Fd,Sched#sched{run_q=RQ, details=Ds#{runq_low=>RQLow}});
+ {more, Sched#sched{run_q=RQ, details=Ds#{runq_low=>RQLow}}};
"Run Queue Port Length" ->
RQ = list_to_integer(bytes(Fd)),
- get_schedulerinfo1(Fd,Sched#sched{port_q=RQ});
-
- "Scheduler Sleep Info Flags" ->
- get_schedulerinfo1(Fd,Sched#sched{details=Ds#{sleep_info=>bytes(Fd, "None")}});
- "Scheduler Sleep Info Aux Work" ->
- get_schedulerinfo1(Fd,Sched#sched{details=Ds#{sleep_aux=>bytes(Fd, "None")}});
+ {more, Sched#sched{port_q=RQ}};
"Run Queue Flags" ->
- get_schedulerinfo1(Fd,Sched#sched{details=Ds#{runq_flags=>bytes(Fd, "None")}});
+ {more, Sched#sched{details=Ds#{runq_flags=>bytes(Fd, "None")}}};
- "Current Process State" ->
- get_schedulerinfo1(Fd,Sched#sched{details=Ds#{currp_state=>bytes(Fd)}});
- "Current Process Internal State" ->
- get_schedulerinfo1(Fd,Sched#sched{details=Ds#{currp_int_state=>bytes(Fd)}});
- "Current Process Program counter" ->
- get_schedulerinfo1(Fd,Sched#sched{details=Ds#{currp_prg_cnt=>string(Fd)}});
- "Current Process CP" ->
- get_schedulerinfo1(Fd,Sched#sched{details=Ds#{currp_cp=>string(Fd)}});
- "Current Process Limited Stack Trace" ->
- %% If there shall be last in scheduler information block
- Sched#sched{details=get_limited_stack(Fd, 0, Ds)};
"=" ++ _next_tag ->
- Sched;
+ {done, Sched};
Other ->
unexpected(Fd,Other,"scheduler information"),
- Sched
+ {done, Sched}
end.
get_limited_stack(Fd, N, Ds) ->
@@ -3000,6 +3102,10 @@ tag_to_atom("allocated_areas") -> ?allocated_areas;
tag_to_atom("allocator") -> ?allocator;
tag_to_atom("atoms") -> ?atoms;
tag_to_atom("binary") -> ?binary;
+tag_to_atom("dirty_cpu_scheduler") -> ?dirty_cpu_scheduler;
+tag_to_atom("dirty_cpu_run_queue") -> ?dirty_cpu_run_queue;
+tag_to_atom("dirty_io_scheduler") -> ?dirty_io_scheduler;
+tag_to_atom("dirty_io_run_queue") -> ?dirty_io_run_queue;
tag_to_atom("end") -> ?ende;
tag_to_atom("erl_crash_dump") -> ?erl_crash_dump;
tag_to_atom("ets") -> ?ets;
diff --git a/lib/observer/src/crashdump_viewer.hrl b/lib/observer/src/crashdump_viewer.hrl
index 6a93a089fd..252e19379d 100644
--- a/lib/observer/src/crashdump_viewer.hrl
+++ b/lib/observer/src/crashdump_viewer.hrl
@@ -80,6 +80,10 @@
old_heap,
heap_unused,
old_heap_unused,
+ bin_vheap,
+ old_bin_vheap,
+ bin_vheap_unused,
+ old_bin_vheap_unused,
new_heap_start,
new_heap_top,
stack_top,
@@ -95,19 +99,27 @@
-record(port,
{id,
+ state,
+ task_flags=0,
slot,
connected,
links,
name,
monitors,
- controls}).
+ suspended,
+ controls,
+ input,
+ output,
+ queue,
+ port_data}).
-record(sched,
{name,
+ type,
process,
port,
run_q=0,
- port_q=0,
+ port_q,
details=#{}
}).
diff --git a/lib/observer/src/observer.app.src b/lib/observer/src/observer.app.src
index f682e3dc7b..cd4aa2e299 100644
--- a/lib/observer/src/observer.app.src
+++ b/lib/observer/src/observer.app.src
@@ -65,8 +65,7 @@
{registered, []},
{applications, [kernel, stdlib]},
{env, []},
- {runtime_dependencies, ["wx-1.2","stdlib-3.4","runtime_tools-1.8.14",
- "kernel-3.0","inets-5.10","et-1.5",
- "erts-7.0"]}]}.
+ {runtime_dependencies, ["wx-1.2","stdlib-3.5","runtime_tools-1.8.14",
+ "kernel-3.0","et-1.5","erts-7.0"]}]}.
diff --git a/lib/observer/src/observer_alloc_wx.erl b/lib/observer/src/observer_alloc_wx.erl
index 7f4b3dd484..e0f319b6cc 100644
--- a/lib/observer/src/observer_alloc_wx.erl
+++ b/lib/observer/src/observer_alloc_wx.erl
@@ -79,8 +79,8 @@ init([Notebook, Parent, Config]) ->
max = #{}
}
}
- catch _:Err ->
- io:format("~p crashed ~tp: ~tp~n",[?MODULE, Err, erlang:get_stacktrace()]),
+ catch _:Err:Stacktrace ->
+ io:format("~p crashed ~tp: ~tp~n",[?MODULE, Err, Stacktrace]),
{stop, Err}
end.
diff --git a/lib/observer/src/observer_perf_wx.erl b/lib/observer/src/observer_perf_wx.erl
index 5adfadb16e..eddb9327f3 100644
--- a/lib/observer/src/observer_perf_wx.erl
+++ b/lib/observer/src/observer_perf_wx.erl
@@ -86,8 +86,8 @@ init([Notebook, Parent, Config]) ->
secs=maps:get(secs, Config, ?DISP_SECONDS)}
},
{Panel, State0}
- catch _:Err ->
- io:format("~p crashed ~tp: ~tp~n",[?MODULE, Err, erlang:get_stacktrace()]),
+ catch _:Err:Stacktrace ->
+ io:format("~p crashed ~tp: ~tp~n",[?MODULE, Err, Stacktrace]),
{stop, Err}
end.
diff --git a/lib/observer/src/observer_procinfo.erl b/lib/observer/src/observer_procinfo.erl
index 6b761cb2ff..6a85d29c1e 100644
--- a/lib/observer/src/observer_procinfo.erl
+++ b/lib/observer/src/observer_procinfo.erl
@@ -148,7 +148,7 @@ handle_event(#wx{event=#wxHtmlLink{linkInfo=#wxHtmlLinkInfo{href=Href}}},
observer ! {open_link, Href},
{noreply, State};
Callback ->
- [{"key1",Key1},{"key2",Key2},{"key3",Key3}] = httpd:parse_query(Rest),
+ [{"key1",Key1},{"key2",Key2},{"key3",Key3}] = uri_string:dissect_query(Rest),
Id = {obs, {T,{list_to_integer(Key1),
list_to_integer(Key2),
list_to_integer(Key3)}}},
diff --git a/lib/observer/src/observer_traceoptions_wx.erl b/lib/observer/src/observer_traceoptions_wx.erl
index fbcf6d7fe9..ff8c40ce77 100644
--- a/lib/observer/src/observer_traceoptions_wx.erl
+++ b/lib/observer/src/observer_traceoptions_wx.erl
@@ -536,7 +536,7 @@ ms_from_string(Str) ->
{error, List} -> throw([[Error, $\n] || {_, Error} <- List])
end
catch error:_Reason ->
- %% io:format("Bad term: ~ts~n ~tp in ~tp~n", [Str, _Reason, erlang:get_stacktrace()]),
+ %% io:format("Bad term: ~ts~n ~tp in ~tp~n", [Str, _Reason, Stacktrace]),
throw("Invalid term")
end.
diff --git a/lib/observer/test/crashdump_viewer_SUITE.erl b/lib/observer/test/crashdump_viewer_SUITE.erl
index 32773a779e..41ca3f3ce9 100644
--- a/lib/observer/test/crashdump_viewer_SUITE.erl
+++ b/lib/observer/test/crashdump_viewer_SUITE.erl
@@ -820,10 +820,10 @@ dump_with_size_limit_reached(DataDir,Rel,DumpName,Max) ->
"-env ERL_CRASH_DUMP_BYTES " ++
integer_to_list(Bytes)),
{ok,#file_info{size=Size}} = file:read_file_info(CD),
- if Size < Bytes ->
+ if Size =< Bytes ->
%% This means that the dump was actually smaller than the
%% randomly selected truncation size, so we'll just do it
- %% again with a smaller numer
+ %% again with a smaller number
ok = file:delete(CD),
dump_with_size_limit_reached(DataDir,Rel,DumpName,Size-3);
true ->
diff --git a/lib/observer/vsn.mk b/lib/observer/vsn.mk
index fc1fca0925..74a6db768e 100644
--- a/lib/observer/vsn.mk
+++ b/lib/observer/vsn.mk
@@ -1 +1 @@
-OBSERVER_VSN = 2.6
+OBSERVER_VSN = 2.7
diff --git a/lib/os_mon/src/disksup.erl b/lib/os_mon/src/disksup.erl
index aeec335ba7..5118d807e1 100644
--- a/lib/os_mon/src/disksup.erl
+++ b/lib/os_mon/src/disksup.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2016. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2018. 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.
@@ -357,7 +357,7 @@ parse_df_take_word_percent(Input) ->
%% and capacity), skip % sign, (optionally for susv3 can also skip IUsed, IFree
%% and ICap% fields) then take remaining characters as the mount path
-spec parse_df(string(), posix | susv3) ->
- {error, parse_df} | {ok, {integer(), integer(), integer()}, string()}.
+ {error, parse_df} | {ok, {integer(), integer(), list()}, string()}.
parse_df(Input0, Flavor) ->
%% Format of Posix/Linux df output looks like Header + Lines
%% Filesystem 1024-blocks Used Available Capacity Mounted on
diff --git a/lib/parsetools/include/yeccpre.hrl b/lib/parsetools/include/yeccpre.hrl
index 91d6cd49a6..53e8124aaa 100644
--- a/lib/parsetools/include/yeccpre.hrl
+++ b/lib/parsetools/include/yeccpre.hrl
@@ -56,8 +56,7 @@ return_error(Line, Message) ->
yeccpars0(Tokens, Tzr, State, States, Vstack) ->
try yeccpars1(Tokens, Tzr, State, States, Vstack)
catch
- error: Error ->
- Stacktrace = erlang:get_stacktrace(),
+ error: Error: Stacktrace ->
try yecc_error_type(Error, Stacktrace) of
Desc ->
erlang:raise(error, {yecc_bug, ?CODE_VERSION, Desc},
diff --git a/lib/parsetools/src/yeccparser.erl b/lib/parsetools/src/yeccparser.erl
index 6f6f66d56c..45d91a83a3 100644
--- a/lib/parsetools/src/yeccparser.erl
+++ b/lib/parsetools/src/yeccparser.erl
@@ -72,8 +72,7 @@ return_error(Line, Message) ->
yeccpars0(Tokens, Tzr, State, States, Vstack) ->
try yeccpars1(Tokens, Tzr, State, States, Vstack)
catch
- error: Error ->
- Stacktrace = erlang:get_stacktrace(),
+ error: Error: Stacktrace ->
try yecc_error_type(Error, Stacktrace) of
Desc ->
erlang:raise(error, {yecc_bug, ?CODE_VERSION, Desc},
@@ -167,21 +166,20 @@ yecctoken_location(Token) ->
end.
-compile({nowarn_unused_function, yecctoken2string/1}).
-yecctoken2string({atom, _, A}) -> io_lib:write(A);
+yecctoken2string({atom, _, A}) -> io_lib:write_atom(A);
yecctoken2string({integer,_,N}) -> io_lib:write(N);
yecctoken2string({float,_,F}) -> io_lib:write(F);
yecctoken2string({char,_,C}) -> io_lib:write_char(C);
yecctoken2string({var,_,V}) -> io_lib:format("~s", [V]);
yecctoken2string({string,_,S}) -> io_lib:write_string(S);
yecctoken2string({reserved_symbol, _, A}) -> io_lib:write(A);
-yecctoken2string({_Cat, _, Val}) -> io_lib:format("~p",[Val]);
+yecctoken2string({_Cat, _, Val}) -> io_lib:format("~tp", [Val]);
yecctoken2string({dot, _}) -> "'.'";
-yecctoken2string({'$end', _}) ->
- [];
+yecctoken2string({'$end', _}) -> [];
yecctoken2string({Other, _}) when is_atom(Other) ->
- io_lib:write(Other);
+ io_lib:write_atom(Other);
yecctoken2string(Other) ->
- io_lib:write(Other).
+ io_lib:format("~tp", [Other]).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
diff --git a/lib/parsetools/test/yecc_SUITE.erl b/lib/parsetools/test/yecc_SUITE.erl
index a7166b91ed..2d3b15326b 100644
--- a/lib/parsetools/test/yecc_SUITE.erl
+++ b/lib/parsetools/test/yecc_SUITE.erl
@@ -1674,8 +1674,7 @@ format_error(Message) ->
yeccpars0(Tokens, MFA) ->
try yeccpars1(Tokens, MFA, 0, [], [])
catch
- error: Error ->
- Stacktrace = erlang:get_stacktrace(),
+ error: Error : Stacktrace ->
try yecc_error_type(Error, Stacktrace) of
{syntax_error, Token} ->
yeccerror(Token);
diff --git a/lib/public_key/src/pubkey_cert.erl b/lib/public_key/src/pubkey_cert.erl
index c433a96585..c0d7b9be8e 100644
--- a/lib/public_key/src/pubkey_cert.erl
+++ b/lib/public_key/src/pubkey_cert.erl
@@ -371,23 +371,23 @@ match_name(directoryName, DirName, [PermittedName | Rest]) ->
match_name(fun is_rdnSeq/2, DirName, PermittedName, Rest);
match_name(uniformResourceIdentifier, URI, [PermittedName | Rest]) ->
- case split_uri(URI) of
- incomplete ->
- false;
- {_, _, Host, _, _} ->
- PN = case split_uri(PermittedName) of
- {_, _, PNhost, _, _} -> PNhost;
+ case uri_string:normalize(URI, [return_map]) of
+ #{host := Host} ->
+ PN = case uri_string:normalize(PermittedName, [return_map]) of
+ #{host := PNhost} -> PNhost;
_X -> PermittedName
end,
- match_name(fun is_valid_host_or_domain/2, Host, PN, Rest)
+ match_name(fun is_valid_host_or_domain/2, Host, PN, Rest);
+ _ ->
+ false
end;
match_name(emailAddress, Name, [PermittedName | Rest]) ->
Fun = fun(Email, PermittedEmail) ->
- is_valid_email_address(Email, PermittedEmail,
- string:tokens(PermittedEmail,"@"))
- end,
- match_name(Fun, Name, PermittedName, Rest);
+ is_valid_email_address(Email, PermittedEmail,
+ string:tokens(PermittedEmail,"@"))
+ end,
+ match_name(Fun, Name, PermittedName, Rest);
match_name(dNSName, Name, [PermittedName | Rest]) ->
Fun = fun(Domain, [$.|Domain]) -> true;
@@ -868,75 +868,12 @@ is_valid_subject_alt_name({otherName, #'AnotherName'{}}) ->
is_valid_subject_alt_name({_, _}) ->
false.
-is_ip_address(Address) ->
- case inet_parse:address(Address) of
- {ok, _} ->
- true;
- _ ->
- false
- end.
-
-is_fully_qualified_name(_Name) ->
- true.
-
is_valid_uri(AbsURI) ->
- case split_uri(AbsURI) of
- incomplete ->
- false;
- {StrScheme, _, Host, _, _} ->
- case string:to_lower(StrScheme) of
- Scheme when Scheme =:= "http"; Scheme =:= "ftp" ->
- is_valid_host(Host);
- _ ->
- false
- end
- end.
-
-is_valid_host(Host) ->
- case is_ip_address(Host) of
- true ->
- true;
- false ->
- is_fully_qualified_name(Host)
- end.
-
-%% Could have a more general split URI in stdlib? Maybe when
-%% regexs are improved. Needed also in inets!
-split_uri(Uri) ->
- case split_uri(Uri, ":", {error, no_scheme}, 1, 1) of
- {error, no_scheme} ->
- incomplete;
- {StrScheme, "//" ++ URIPart} ->
- {Authority, PathQuery} =
- split_auth_path(URIPart),
- {UserInfo, HostPort} =
- split_uri(Authority, "@", {"", Authority}, 1, 1),
- {Host, Port} =
- split_uri(HostPort, ":", {HostPort, dummy_port}, 1, 1),
- {StrScheme, UserInfo, Host, Port, PathQuery}
- end.
-
-split_auth_path(URIPart) ->
- case split_uri(URIPart, "/", URIPart, 1, 0) of
- Split = {_, _} ->
- Split;
- URIPart ->
- case split_uri(URIPart, "\\?", URIPart, 1, 0) of
- Split = {_, _} ->
- Split;
- URIPart ->
- {URIPart,""}
- end
- end.
-
-split_uri(UriPart, SplitChar, NoMatchResult, SkipLeft, SkipRight) ->
- case re:run(UriPart, SplitChar) of
- {match,[{Start, _}]} ->
- StrPos = Start + 1,
- {string:substr(UriPart, 1, StrPos - SkipLeft),
- string:substr(UriPart, StrPos + SkipRight, length(UriPart))};
- nomatch ->
- NoMatchResult
+ case uri_string:normalize(AbsURI, [return_map]) of
+ #{scheme := _} ->
+ true;
+ _ ->
+ false
end.
is_rdnSeq({rdnSequence,[]}, {rdnSequence,[none]}) ->
diff --git a/lib/public_key/src/public_key.app.src b/lib/public_key/src/public_key.app.src
index dbd732c384..5833141e87 100644
--- a/lib/public_key/src/public_key.app.src
+++ b/lib/public_key/src/public_key.app.src
@@ -14,7 +14,7 @@
{applications, [asn1, crypto, kernel, stdlib]},
{registered, []},
{env, []},
- {runtime_dependencies, ["stdlib-2.0","kernel-3.0","erts-6.0","crypto-3.8",
+ {runtime_dependencies, ["stdlib-3.5","kernel-3.0","erts-6.0","crypto-3.8",
"asn1-3.0"]}
]
}.
diff --git a/lib/public_key/src/public_key.erl b/lib/public_key/src/public_key.erl
index 034126655c..931901640a 100644
--- a/lib/public_key/src/public_key.erl
+++ b/lib/public_key/src/public_key.erl
@@ -1456,7 +1456,7 @@ ascii_to_lower(String) ->
verify_hostname_extract_fqdn_default({dns_id,S}) ->
S;
verify_hostname_extract_fqdn_default({uri_id,URI}) ->
- {ok,{https,_,Host,_,_,_}} = http_uri:parse(URI),
+ #{scheme := "https", host := Host} = uri_string:normalize(URI, [return_map]),
Host.
diff --git a/lib/reltool/doc/src/reltool_examples.xml b/lib/reltool/doc/src/reltool_examples.xml
index 30cb3c13b6..2a103119e6 100644
--- a/lib/reltool/doc/src/reltool_examples.xml
+++ b/lib/reltool/doc/src/reltool_examples.xml
@@ -5,7 +5,7 @@
<header>
<copyright>
<year>2009</year>
- <year>2017</year>
+ <year>2018</year>
<holder>Ericsson AB, All Rights Reserved</holder>
</copyright>
<legalnotice>
@@ -150,13 +150,13 @@ Eshell V9.0 (abort with ^G)
{mod_cond,all},
{incl_cond,derived},
{erts,[{app,erts,
- [{vsn,"9.0"},
- {lib_dir,"/usr/local/lib/erlang/lib/erts-9.0"},
+ [{vsn,"10.0"},
+ {lib_dir,"/usr/local/lib/erlang/lib/erts-10.0"},
{mod,erl_prim_loader,[]},
{mod,erl_tracer,[]},
{mod,erlang,[]},
{mod,erts_code_purger,[]},
- {mod,erts_dirty_process_code_checker,[]},
+ {mod,erts_dirty_process_signal_handler,[]},
{mod,erts_internal,[]},
{mod,erts_literal_area_collector,[]},
{mod,init,[]},
@@ -309,9 +309,9 @@ Eshell V9.0 (abort with ^G)
<section>
<title>Generate release and script files</title>
<pre>
-Erlang/OTP 20 [erts-9.0] [source-c13b302] [64-bit] [smp:4:4] [ds:4:4:10] [async-threads:10]
+Erlang/OTP 20 [erts-10.0] [source-c13b302] [64-bit] [smp:4:4] [ds:4:4:10] [async-threads:10]
[hipe] [kernel-poll:false]
-Eshell V9.0 (abort with ^G)
+Eshell V10.0 (abort with ^G)
1&gt;
1&gt; {ok, Server} = reltool:start_server([{config, {sys, [{boot_rel, "NAME"},
{rel, "NAME", "VSN",
@@ -324,13 +324,13 @@ Eshell V9.0 (abort with ^G)
3&gt;
3&gt; reltool:get_rel(Server, "NAME").
{ok,{release,{"NAME","VSN"},
- {erts,"9.0"},
+ {erts,"10.0"},
[{kernel,"5.2"},{stdlib,"3.3"},{sasl,"3.0.3"}]}}
4&gt;
4&gt; reltool:get_script(Server, "NAME").
{ok,{script,{"NAME","VSN"},
[{preLoaded,[erl_prim_loader,erl_tracer,erlang,
- erts_code_purger,erts_dirty_process_code_checker,
+ erts_code_purger,erts_dirty_process_signal_handler,
erts_internal,erts_literal_area_collector,init,otp_ring0,
prim_eval,prim_file,prim_inet,prim_zip,zlib]},
{progress,preloaded},
@@ -374,9 +374,9 @@ ok
<section>
<title>Create a target system</title>
<pre>
-Erlang/OTP 20 [erts-9.0] [source-c13b302] [64-bit] [smp:4:4] [ds:4:4:10] [async-threads:10]
+Erlang/OTP 20 [erts-10.0] [source-c13b302] [64-bit] [smp:4:4] [ds:4:4:10] [async-threads:10]
[hipe] [kernel-poll:false]
-Eshell V9.0 (abort with ^G)
+Eshell V10.0 (abort with ^G)
1&gt;
1&gt; Config = {sys, [{escript, "examples/display_args", [{incl_cond, include}]},
{app, inets, [{incl_cond, include}]},
@@ -393,7 +393,7 @@ Eshell V9.0 (abort with ^G)
2&gt;
2&gt; {ok, Spec} = reltool:get_target_spec([Config]).
{ok,[{create_dir,"releases",
- [{write_file,"start_erl.data","9.0 1.0\n"},
+ [{write_file,"start_erl.data","10.0 1.0\n"},
{create_dir,"1.0",
[{write_file,"start_clean.rel",
[37,37,32,114,101,108,32,103,101,110,101,114,97,116|...]},
@@ -410,17 +410,17 @@ Eshell V9.0 (abort with ^G)
{create_dir,"bin",
[{copy_file,"display_args.escript",
"/usr/local/lib/erlang/lib/reltool-0.7.3/examples/display_args"},
- {copy_file,"display_args","erts-9.0/bin/escript"},
- {copy_file,"start","erts-9.0/bin/start"},
- {copy_file,"ct_run","erts-9.0/bin/ct_run"},
- {copy_file,"dialyzer","erts-9.0/bin/dialyzer"},
- {copy_file,"run_erl","erts-9.0/bin/run_erl"},
- {copy_file,"erl","erts-9.0/bin/dyn_erl"},
- {copy_file,"to_erl","erts-9.0/bin/to_erl"},
- {copy_file,"epmd","erts-9.0/bin/epmd"},
- {copy_file,"erlc","erts-9.0/bin/erlc"},
- {copy_file,"typer","erts-9.0/bin/typer"},
- {copy_file,"escript","erts-9.0/bin/escript"},
+ {copy_file,"display_args","erts-10.0/bin/escript"},
+ {copy_file,"start","erts-10.0/bin/start"},
+ {copy_file,"ct_run","erts-10.0/bin/ct_run"},
+ {copy_file,"dialyzer","erts-10.0/bin/dialyzer"},
+ {copy_file,"run_erl","erts-10.0/bin/run_erl"},
+ {copy_file,"erl","erts-10.0/bin/dyn_erl"},
+ {copy_file,"to_erl","erts-10.0/bin/to_erl"},
+ {copy_file,"epmd","erts-10.0/bin/epmd"},
+ {copy_file,"erlc","erts-10.0/bin/erlc"},
+ {copy_file,"typer","erts-10.0/bin/typer"},
+ {copy_file,"escript","erts-10.0/bin/escript"},
{write_file,"start_clean.boot",&lt;&lt;131,104,3,119,6,115,...&gt;&gt;},
{write_file,"start_sasl.boot",&lt;&lt;131,104,3,119,6,...&gt;&gt;},
{write_file,"start.boot",&lt;&lt;131,104,3,119,...&gt;&gt;}]},
@@ -451,7 +451,7 @@ Eshell V9.0 (abort with ^G)
{copy_file,[...]},
{copy_file,...},
{...}]}]},
- {create_dir,"erts-9.0",
+ {create_dir,"erts-10.0",
[{create_dir,"bin",
[{copy_file,"start"},
{copy_file,"ct_run"},
@@ -459,7 +459,7 @@ Eshell V9.0 (abort with ^G)
{copy_file,"dialyzer"},
{copy_file,"beam.smp"},
{copy_file,"run_erl"},
- {copy_file,"erl","erts-9.0/bin/dyn_erl"},
+ {copy_file,"erl","erts-10.0/bin/dyn_erl"},
{copy_file,"to_erl"},
{copy_file,"epmd"},
{copy_file,"erl_child_setup"},
@@ -511,8 +511,8 @@ Eshell V9.0 (abort with ^G)
[{create_dir,"priv",
[{create_dir,"lib",[{copy_file,[...]},{copy_file,...}]},
{create_dir,"obj",[{copy_file,...},{...}|...]}]}]},
- {archive,"erts-9.0.ez",[],
- [{create_dir,"erts-9.0",
+ {archive,"erts-10.0.ez",[],
+ [{create_dir,"erts-10.0",
[{create_dir,"src",[{...}|...]},
{create_dir,"ebin",[...]}]}]},
{archive,"hipe-3.15.4.ez",[],
@@ -549,14 +549,14 @@ ok
ok
7&gt;
7&gt; file:list_dir(TargetDir).
-{ok,["bin","Install","lib","misc","usr","erts-9.0",
+{ok,["bin","Install","lib","misc","usr","erts-10.0",
"releases"]}
8&gt;
8&gt; file:list_dir(filename:join([TargetDir,"lib"])).
{ok,["tools-2.9.1.ez","kernel-5.2.ez","inets-6.3.9.ez",
"kernel-5.2","sasl-3.0.3.ez","hipe-3.15.4.ez","inets-6.3.9",
"crypto-3.7.4","crypto-3.7.4.ez","stdlib-3.3.ez",
- "erts-9.0.ez","stdlib-3.3","compiler-7.0.4.ez"]}
+ "erts-10.0.ez","stdlib-3.3","compiler-7.0.4.ez"]}
9&gt;
9&gt; file:make_dir("/tmp/yet_another_target_dir").
ok
@@ -565,7 +565,7 @@ ok
ok
11&gt;
11&gt; file:list_dir("/tmp/yet_another_target_dir").
-{ok,["bin","Install","lib","misc","usr","erts-9.0",
+{ok,["bin","Install","lib","misc","usr","erts-10.0",
"releases"]}
</pre>
diff --git a/lib/reltool/src/reltool_app_win.erl b/lib/reltool/src/reltool_app_win.erl
index 663144861f..335b40fa74 100644
--- a/lib/reltool/src/reltool_app_win.erl
+++ b/lib/reltool/src/reltool_app_win.erl
@@ -102,8 +102,8 @@ init(Parent, WxEnv, Xref, C, AppName) ->
try
do_init(Parent, WxEnv, Xref, C, AppName)
catch
- error:Reason ->
- exit({Reason, erlang:get_stacktrace()})
+ error:Reason:Stacktrace ->
+ exit({Reason, Stacktrace})
end.
do_init(Parent, WxEnv, Xref, C, AppName) ->
diff --git a/lib/reltool/src/reltool_mod_win.erl b/lib/reltool/src/reltool_mod_win.erl
index dcd802a5de..8272d864c2 100644
--- a/lib/reltool/src/reltool_mod_win.erl
+++ b/lib/reltool/src/reltool_mod_win.erl
@@ -107,8 +107,8 @@ init(Parent, WxEnv, Xref, RelPid, C, ModName) ->
try
do_init(Parent, WxEnv, Xref, RelPid, C, ModName)
catch
- error:Reason ->
- exit({Reason, erlang:get_stacktrace()})
+ error:Reason:Stacktrace ->
+ exit({Reason, Stacktrace})
end.
do_init(Parent, WxEnv, Xref, RelPid, C, ModName) ->
diff --git a/lib/reltool/src/reltool_server.erl b/lib/reltool/src/reltool_server.erl
index 853191c696..66ee1a3742 100644
--- a/lib/reltool/src/reltool_server.erl
+++ b/lib/reltool/src/reltool_server.erl
@@ -135,8 +135,8 @@ init([{parent,Parent}|_] = Options) ->
catch
throw:{error,Reason} ->
proc_lib:init_ack(Parent,{error,Reason});
- error:Reason ->
- exit({Reason, erlang:get_stacktrace()})
+ error:Reason:Stacktrace ->
+ exit({Reason, Stacktrace})
end.
do_init(Options) ->
diff --git a/lib/reltool/src/reltool_sys_win.erl b/lib/reltool/src/reltool_sys_win.erl
index 92df270752..d87b1dc91f 100644
--- a/lib/reltool/src/reltool_sys_win.erl
+++ b/lib/reltool/src/reltool_sys_win.erl
@@ -135,9 +135,9 @@ init(Options) ->
try
do_init(Options)
catch
- error:Reason ->
- io:format("~tp: ~tp~n",[Reason, erlang:get_stacktrace()]),
- exit({Reason, erlang:get_stacktrace()})
+ error:Reason:Stacktrace ->
+ io:format("~tp: ~tp~n",[Reason, Stacktrace]),
+ exit({Reason, Stacktrace})
end.
do_init([{safe_config, Safe}, {parent, Parent} | Options]) ->
diff --git a/lib/runtime_tools/doc/src/Makefile b/lib/runtime_tools/doc/src/Makefile
index ec19a4ce59..a9b0056a93 100644
--- a/lib/runtime_tools/doc/src/Makefile
+++ b/lib/runtime_tools/doc/src/Makefile
@@ -41,7 +41,13 @@ RELSYSDIR = $(RELEASE_PATH)/lib/$(APPLICATION)-$(VSN)
# Target Specs
# ----------------------------------------------------
XML_APPLICATION_FILES = ref_man.xml
-XML_REF3_FILES = dbg.xml dyntrace.xml erts_alloc_config.xml system_information.xml msacc.xml
+XML_REF3_FILES = \
+ dbg.xml \
+ dyntrace.xml \
+ erts_alloc_config.xml \
+ system_information.xml \
+ msacc.xml \
+ scheduler.xml
XML_REF6_FILES = runtime_tools_app.xml
XML_PART_FILES = part.xml
diff --git a/lib/runtime_tools/doc/src/notes.xml b/lib/runtime_tools/doc/src/notes.xml
index 93e3e26fda..355e3dd40d 100644
--- a/lib/runtime_tools/doc/src/notes.xml
+++ b/lib/runtime_tools/doc/src/notes.xml
@@ -32,6 +32,38 @@
<p>This document describes the changes made to the Runtime_Tools
application.</p>
+<section><title>Runtime_Tools 1.12.5</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p><c>system_information:to_file/1</c> will now use
+ slightly less memory.</p>
+ <p>
+ Own Id: OTP-14816</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Runtime_Tools 1.12.4</title>
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ New family of <c>erts_alloc</c> strategies: Age Order
+ First Fit. Similar to "address order", but instead the
+ oldest possible carrier is always chosen for allocation.</p>
+ <p>
+ Own Id: OTP-14917 Aux Id: ERIERL-88 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Runtime_Tools 1.12.3</title>
<section><title>Fixed Bugs and Malfunctions</title>
diff --git a/lib/runtime_tools/doc/src/ref_man.xml b/lib/runtime_tools/doc/src/ref_man.xml
index d2fb7a29af..eb3a6f0f5c 100644
--- a/lib/runtime_tools/doc/src/ref_man.xml
+++ b/lib/runtime_tools/doc/src/ref_man.xml
@@ -37,6 +37,7 @@
<xi:include href="dyntrace.xml"/>
<xi:include href="erts_alloc_config.xml"/>
<xi:include href="msacc.xml"/>
+ <xi:include href="scheduler.xml"/>
<xi:include href="system_information.xml"/>
</application>
diff --git a/lib/runtime_tools/doc/src/scheduler.xml b/lib/runtime_tools/doc/src/scheduler.xml
new file mode 100644
index 0000000000..dd8bf73bae
--- /dev/null
+++ b/lib/runtime_tools/doc/src/scheduler.xml
@@ -0,0 +1,135 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<!DOCTYPE erlref SYSTEM "erlref.dtd">
+
+<erlref>
+ <header>
+ <copyright>
+ <year>2018</year>
+ <holder>Ericsson AB. All Rights Reserved.</holder>
+ </copyright>
+ <legalnotice>
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+ </legalnotice>
+
+ <title></title>
+ <prepared></prepared>
+ <responsible></responsible>
+ <docno>1</docno>
+ <approved></approved>
+ <checked></checked>
+ <date></date>
+ <rev></rev>
+ <file>scheduler.xml</file>
+ </header>
+ <module>scheduler</module>
+ <modulesummary>Measure scheduler utilization</modulesummary>
+ <description>
+ <p>This module contains utility functions for easier measurement and
+ calculation of scheduler utilization, otherwise obtained from calling the
+ more primitive <seealso marker="erts:erlang#statistics_scheduler_wall_time">
+ <c>statistics(scheduler_wall_time)</c></seealso>.</p>
+ <p>The simplest usage is to call <seealso marker="#utilization-1">
+ <c>scheduler:utilization(Seconds)</c></seealso>.</p>
+ </description>
+
+ <datatypes>
+ <datatype>
+ <name name="sched_sample"/>
+ </datatype>
+ <datatype>
+ <name name="sched_type"/>
+ </datatype>
+ <datatype>
+ <name name="sched_id"/>
+ </datatype>
+ <datatype>
+ <name name="sched_util_result"/>
+ <desc>
+ <p>A list of tuples containing results for individual schedulers
+ as well as aggregated averages. <c>Util</c> is the scheduler utilization
+ as a floating point value between 0.0 and 1.0. <c>Percent</c> is the
+ same utilization as a more human readable string expressed in percent.</p>
+ <taglist>
+ <tag><c>{normal, SchedulerId, Util, Percent}</c></tag>
+ <item>Scheduler utilization of a normal scheduler with number
+ <c>SchedulerId</c>.</item>
+ <tag><c>{cpu, SchedulerId, Util, Percent}</c></tag>
+ <item>Scheduler utilization of a dirty-cpu scheduler with number
+ <c>SchedulerId</c>.</item>
+ <tag><c>{io, SchedulerId, Util, Percent}</c></tag>
+ <item>Scheduler utilization of a dirty-io scheduler with number
+ <c>SchedulerId</c>. This tuple will only exist if both samples were
+ taken with <seealso marker="#sample_all-0"><c>sample_all/0</c></seealso>.</item>
+ <tag><c>{total, Util, Percent}</c></tag>
+ <item>Total utilization of all normal and dirty-cpu schedulers.</item>
+ <tag><c>{weighted, Util, Percent}</c></tag>
+ <item>Total utilization of all normal and dirty-cpu schedulers,
+ weighted against maximum amount of available CPU time.</item>
+ </taglist>
+ </desc>
+ </datatype>
+ </datatypes>
+
+ <funcs>
+
+ <func>
+ <name name="sample" arity="0"/>
+ <fsummary>Get scheduler utilization sample.</fsummary>
+ <desc>
+ <p>Return a scheduler utilization sample for normal and dirty-cpu
+ schedulers.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name name="sample_all" arity="0"/>
+ <fsummary>Get scheduler utilization sample.</fsummary>
+ <desc>
+ <p>Return a scheduler utilization sample for all schedulers,
+ including dirty-io schedulers.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name name="utilization" arity="1" clause_i="1"/>
+ <fsummary>Measure scheduler utilizations during a period of time.</fsummary>
+ <desc>
+ <p>Measure utilization for normal and dirty-cpu schedulers during
+ <c><anno>Seconds</anno></c> seconds, and then return the result.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name name="utilization" arity="1" clause_i="2"/>
+ <fsummary>Measure scheduler utilizations since sample.</fsummary>
+ <desc>
+ <p>Calculate scheduler utilizations for the time interval from when
+ <c><anno>Sample</anno></c> was taken and "now". The same as calling
+ <c>scheduler:utilization(Sample, scheduler:sample_all())</c>.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name name="utilization" arity="2"/>
+ <fsummary>Measure scheduler utilizations between two samples.</fsummary>
+ <desc>
+ <p>Calculates scheduler utilizations for the time interval between
+ the two samples obtained from calling
+ <seealso marker="#sample-0"><c>sample/0</c></seealso> or
+ <seealso marker="#sample_all-0"><c>sample_all/0</c></seealso>.</p>
+ </desc>
+ </func>
+
+ </funcs>
+ </erlref>
diff --git a/lib/runtime_tools/doc/src/specs.xml b/lib/runtime_tools/doc/src/specs.xml
index 978bd39e55..33fe7fa370 100644
--- a/lib/runtime_tools/doc/src/specs.xml
+++ b/lib/runtime_tools/doc/src/specs.xml
@@ -2,4 +2,5 @@
<specs xmlns:xi="http://www.w3.org/2001/XInclude">
<xi:include href="../specs/specs_system_information.xml"/>
<xi:include href="../specs/specs_msacc.xml"/>
+ <xi:include href="../specs/specs_scheduler.xml"/>
</specs>
diff --git a/lib/runtime_tools/src/Makefile b/lib/runtime_tools/src/Makefile
index 5a99c6e240..6faa9c2e35 100644
--- a/lib/runtime_tools/src/Makefile
+++ b/lib/runtime_tools/src/Makefile
@@ -45,6 +45,7 @@ MODULES= \
system_information \
observer_backend \
ttb_autostart\
+ scheduler\
msacc
HRL_FILES= ../include/observer_backend.hrl
diff --git a/lib/runtime_tools/src/erts_alloc_config.erl b/lib/runtime_tools/src/erts_alloc_config.erl
index 342363d08d..6c1a945484 100644
--- a/lib/runtime_tools/src/erts_alloc_config.erl
+++ b/lib/runtime_tools/src/erts_alloc_config.erl
@@ -265,7 +265,13 @@ strategy_str(aoff) ->
strategy_str(aoffcbf) ->
"Address order first fit carrier best fit";
strategy_str(aoffcaobf) ->
- "Address order first fit carrier adress order best fit".
+ "Address order first fit carrier adress order best fit";
+strategy_str(ageffcaoff) ->
+ "Age order first fit carrier address order first fit";
+strategy_str(ageffcbf) ->
+ "Age order first fit carrier best fit";
+strategy_str(ageffcaobf) ->
+ "Age order first fit carrier adress order best fit".
default_acul(A, S) ->
case carrier_migration_support(S) of
diff --git a/lib/runtime_tools/src/observer_backend.erl b/lib/runtime_tools/src/observer_backend.erl
index a1edde8516..7ec3b38930 100644
--- a/lib/runtime_tools/src/observer_backend.erl
+++ b/lib/runtime_tools/src/observer_backend.erl
@@ -279,7 +279,7 @@ get_table_list(mnesia, Opts) ->
end,
[Tab|Acc]
catch _:_What ->
- %% io:format("Skipped ~p: ~p ~p ~n",[Id, _What, erlang:get_stacktrace()]),
+ %% io:format("Skipped ~p: ~p ~p ~n",[Id, _What, Stacktrace]),
Acc
end
end,
diff --git a/lib/runtime_tools/src/runtime_tools.app.src b/lib/runtime_tools/src/runtime_tools.app.src
index 449532e5c4..09a9b447c2 100644
--- a/lib/runtime_tools/src/runtime_tools.app.src
+++ b/lib/runtime_tools/src/runtime_tools.app.src
@@ -23,6 +23,7 @@
{modules, [appmon_info, dbg,observer_backend,runtime_tools,
runtime_tools_sup,erts_alloc_config,
ttb_autostart,dyntrace,system_information,
+ scheduler,
msacc]},
{registered, [runtime_tools_sup]},
{applications, [kernel, stdlib]},
diff --git a/lib/runtime_tools/src/scheduler.erl b/lib/runtime_tools/src/scheduler.erl
new file mode 100644
index 0000000000..c896b671ac
--- /dev/null
+++ b/lib/runtime_tools/src/scheduler.erl
@@ -0,0 +1,152 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2018. 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%
+%%
+
+%% @doc Utility functions for easier measurement of scheduler utilization
+%% using erlang:statistics(scheduler_wall_time).
+
+-module(scheduler).
+
+-export([sample/0,
+ sample_all/0,
+ utilization/1,
+ utilization/2]).
+
+-export_type([sched_sample/0]).
+
+
+-opaque sched_sample() ::
+ {scheduler_wall_time | scheduler_wall_time_all,
+ [{sched_type(), sched_id(), ActiveTime::integer(), TotalTime::integer()}]}.
+
+-type sched_type() :: normal | cpu | io.
+
+-type sched_id() :: integer().
+
+-spec sample() -> sched_sample().
+sample() ->
+ sample(scheduler_wall_time).
+
+-spec sample_all() -> sched_sample().
+sample_all() ->
+ sample(scheduler_wall_time_all).
+
+sample(Stats) ->
+ case erlang:statistics(Stats) of
+ undefined ->
+ erlang:system_flag(scheduler_wall_time, true),
+ sample(Stats);
+
+ List ->
+ Sorted = lists:sort(List),
+ Tagged = lists:map(fun({I, A, T}) -> {sched_tag(I), I, A, T} end,
+ Sorted),
+ {Stats, Tagged}
+ end.
+
+-type sched_util_result() ::
+ [{sched_type(), sched_id(), float(), string()} |
+ {total, float(), string()} |
+ {weighted, float(), string()}].
+
+-spec utilization(Seconds) -> sched_util_result() when
+ Seconds :: pos_integer();
+ (Sample) -> sched_util_result() when
+ Sample :: sched_sample().
+utilization(Seconds) when is_integer(Seconds), Seconds > 0 ->
+ OldFlag = erlang:system_flag(scheduler_wall_time, true),
+ T0 = sample(),
+ receive after Seconds*1000 -> ok end,
+ T1 = sample(),
+ case OldFlag of
+ false ->
+ erlang:system_flag(scheduler_wall_time, OldFlag);
+ true ->
+ ok
+ end,
+ utilization(T0,T1);
+
+utilization({Stats, _}=T0) when Stats =:= scheduler_wall_time;
+ Stats =:= scheduler_wall_time_all ->
+ utilization(T0, sample(Stats)).
+
+-spec utilization(Sample1, Sample2) -> sched_util_result() when
+ Sample1 :: sched_sample(),
+ Sample2 :: sched_sample().
+utilization({Stats, Ts0}, {Stats, Ts1}) ->
+ Diffs = lists:map(fun({{Tag, I, A0, T0}, {Tag, I, A1, T1}}) ->
+ {Tag, I, (A1 - A0), (T1 - T0)}
+ end,
+ lists:zip(Ts0,Ts1)),
+
+ {Lst0, {A, T, N}} = lists:foldl(fun({Tag, I, Adiff, Tdiff}, {Lst, Acc}) ->
+ R = safe_div(Adiff, Tdiff),
+ {[{Tag, I, R, percent(R)} | Lst],
+ acc(Tag, Adiff, Tdiff, Acc)}
+ end,
+ {[], {0, 0, 0}},
+ Diffs),
+
+ Total = safe_div(A, T),
+ Lst1 = lists:reverse(Lst0),
+ Lst2 = case erlang:system_info(logical_processors_available) of
+ unknown -> Lst1;
+ LPA ->
+ Weighted = Total * (N / LPA),
+ [{weighted, Weighted, percent(Weighted)} | Lst1]
+ end,
+ [{total, Total, percent(Total)} | Lst2];
+
+utilization({scheduler_wall_time, _}=T0,
+ {scheduler_wall_time_all, Ts1}) ->
+ utilization(T0, {scheduler_wall_time, remove_io(Ts1)});
+
+utilization({scheduler_wall_time_all, Ts0},
+ {scheduler_wall_time, _}=T1) ->
+ utilization({scheduler_wall_time, remove_io(Ts0)}, T1).
+
+%% Do not include dirty-io in totals
+acc(io, _, _, Acc) ->
+ Acc;
+acc(Tag, Adiff, Tdiff, {Asum, Tsum, N}) when Tag =:= normal; Tag =:= cpu ->
+ {Adiff+Asum, Tdiff+Tsum, N+1}.
+
+
+remove_io(Ts) ->
+ lists:filter(fun({io,_,_,_}) -> false;
+ (_) -> true end,
+ Ts).
+
+safe_div(A, B) ->
+ if B == 0.0 -> 0.0;
+ true -> A / B
+ end.
+
+sched_tag(Nr) ->
+ Normal = erlang:system_info(schedulers),
+ Cpu = Normal + erlang:system_info(dirty_cpu_schedulers),
+ case Nr of
+ _ when Nr =< Normal -> normal;
+ _ when Nr =< Cpu -> cpu;
+ _ -> io
+ end.
+
+
+percent(F) ->
+ float_to_list(F*100, [{decimals,1}]) ++ [$%].
diff --git a/lib/runtime_tools/src/system_information.erl b/lib/runtime_tools/src/system_information.erl
index 119d7cc3d4..136ee55b54 100644
--- a/lib/runtime_tools/src/system_information.erl
+++ b/lib/runtime_tools/src/system_information.erl
@@ -488,10 +488,10 @@ to_fd(Fd) ->
ok = file:write(Fd, io_lib:format(Format, Args))
end,
- EmitChunk("{system_information_version, ~p}.~n"
+ EmitChunk("{system_information_version, ~w}.~n"
"{system_information,["
- "{init_arguments,~p},"
- "{code_paths,~p},",
+ "{init_arguments,~w},"
+ "{code_paths,~w},",
[?REPORT_FILE_VSN,
init:get_arguments(),
code:get_path()]),
@@ -499,12 +499,12 @@ to_fd(Fd) ->
emit_code_info(EmitChunk),
EmitChunk( "," %% Note the leading comma!
- "{system_info,~p},"
- "{erts_compile_info,~p},"
- "{beam_dynamic_libraries,~p},"
- "{environment_erts,~p},"
- "{environment,~p},"
- "{sanity_check,~p}"
+ "{system_info,~w},"
+ "{erts_compile_info,~w},"
+ "{beam_dynamic_libraries,~w},"
+ "{environment_erts,~w},"
+ "{environment,~w},"
+ "{sanity_check,~w}"
"]}.~n",
[erlang_system_info(),
erlang:system_info(compile_info),
@@ -533,11 +533,11 @@ emit_application_info(EmitChunk, Path) ->
Description = proplists:get_value(description, Info, []),
Version = proplists:get_value(vsn, Info, []),
- EmitChunk("{application, {~p,["
- "{description,~p},"
- "{vsn,~p},"
- "{path,~p},"
- "{runtime_dependencies,~p},",
+ EmitChunk("{application, {~w,["
+ "{description,~w},"
+ "{vsn,~w},"
+ "{path,~w},"
+ "{runtime_dependencies,~w},",
[App, Description, Version, Path, RtDeps]),
emit_module_info_from_path(EmitChunk, Path),
EmitChunk("]}}", [])
@@ -545,7 +545,7 @@ emit_application_info(EmitChunk, Path) ->
emit_code_path_info(EmitChunk, Path) ->
EmitChunk("{code, ["
- "{path, ~p},", [Path]),
+ "{path, ~w},", [Path]),
emit_module_info_from_path(EmitChunk, Path),
EmitChunk("]}", []).
@@ -573,11 +573,11 @@ emit_module_info(EmitChunk, Beam) ->
_ -> true
end,
- EmitChunk("{~p,["
- "{loaded,~p},"
- "{native,~p},"
- "{compiler,~p},"
- "{md5,~p}"
+ EmitChunk("{~w,["
+ "{loaded,~w},"
+ "{native,~w},"
+ "{compiler,~w},"
+ "{md5,~w}"
"]}",
[Mod, Loaded, Native, CompilerVersion, hexstring(Md5)]).
diff --git a/lib/runtime_tools/test/Makefile b/lib/runtime_tools/test/Makefile
index 61377ea09e..de37b2570d 100644
--- a/lib/runtime_tools/test/Makefile
+++ b/lib/runtime_tools/test/Makefile
@@ -9,6 +9,7 @@ MODULES = \
system_information_SUITE \
dbg_SUITE \
erts_alloc_config_SUITE \
+ scheduler_SUITE \
msacc_SUITE
ERL_FILES= $(MODULES:%=%.erl)
diff --git a/lib/runtime_tools/test/dbg_SUITE.erl b/lib/runtime_tools/test/dbg_SUITE.erl
index cfe8412e33..c5dcccb887 100644
--- a/lib/runtime_tools/test/dbg_SUITE.erl
+++ b/lib/runtime_tools/test/dbg_SUITE.erl
@@ -478,8 +478,7 @@ port(Config) when is_list(Config) ->
TraceFileDrv = list_to_atom(lists:flatten(["trace_file_drv n ",TestFile])),
[{trace,Port,open,S,TraceFileDrv},
{trace,Port,getting_linked,S},
- {trace,Port,closed,normal},
- {trace,Port,unlink,S}] = flush()
+ {trace,Port,closed,normal}] = flush()
after
dbg:stop()
end,
diff --git a/lib/runtime_tools/test/scheduler_SUITE.erl b/lib/runtime_tools/test/scheduler_SUITE.erl
new file mode 100644
index 0000000000..1c80253371
--- /dev/null
+++ b/lib/runtime_tools/test/scheduler_SUITE.erl
@@ -0,0 +1,104 @@
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2018. 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(scheduler_SUITE).
+
+-export([suite/0, all/0]).
+
+%% Test cases
+-export([basic/1]).
+
+all() -> [basic].
+
+
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+
+basic(_Config) ->
+ S1 = scheduler:sample(),
+ S2 = scheduler:sample_all(),
+
+ check(scheduler:utilization(1)),
+
+ check(scheduler:utilization(S1)),
+ check(scheduler:utilization(S2)),
+ check(scheduler:utilization(S1, scheduler:sample())),
+ check(scheduler:utilization(S2, scheduler:sample())),
+
+ S3 = scheduler:sample_all(),
+ U13 = scheduler:utilization(S1, S3),
+ U13 = scheduler:utilization(S1, remove_io(S3)),
+ check(U13),
+
+ U23all = scheduler:utilization(S2, S3),
+ check(U23all),
+ U23 = scheduler:utilization(S2, remove_io(S3)),
+ U23 = scheduler:utilization(remove_io(S2), S3),
+ U23 = remove_io(U23all),
+ check(U23),
+
+ ok.
+
+
+check([{total, Tf, Ts} | List]=U) ->
+ io:format("\nU = ~p\n", [U]),
+ check_values(Tf, Ts, true),
+
+ SchdList = case hd(List) of
+ {weighted, Wf, Ws} ->
+ check_values(Wf, Ws, false),
+ tl(List);
+ _ ->
+ unknown = erlang:system_info(logical_processors_available),
+ List
+ end,
+
+ lists:foreach(fun({Type, Id, F, S}) when ((Type =:= normal) or (Type =:= cpu) or (Type =:= io)),
+ is_integer(Id) ->
+ check_values(F, S, true)
+ end,
+ SchdList),
+ ok.
+
+check_values(F, S, Max100) ->
+ true = is_float(F),
+ true = F >= 0.0,
+
+ $% = lists:last(S),
+ Sf = list_to_float(lists:droplast(S)),
+ true = Sf >= 0.0,
+ true = case Max100 of
+ true ->
+ true = F =< 1.0,
+ true = Sf =< 100.0;
+ false ->
+ true
+ end,
+ MaxDiff = 0.055555555555555555, %% change to 0.05 when float_to_list/2 is fixed
+ true = abs(F*100 - Sf) =< MaxDiff,
+ ok.
+
+
+remove_io({scheduler_wall_time_all,Lst}) ->
+ {scheduler_wall_time, remove_io(Lst)};
+remove_io(Lst) ->
+ lists:filter(fun({io,_,_,_}) -> false;
+ (_) -> true end,
+ Lst).
diff --git a/lib/runtime_tools/vsn.mk b/lib/runtime_tools/vsn.mk
index 872bd5db1d..26869b9412 100644
--- a/lib/runtime_tools/vsn.mk
+++ b/lib/runtime_tools/vsn.mk
@@ -1 +1 @@
-RUNTIME_TOOLS_VSN = 1.12.3
+RUNTIME_TOOLS_VSN = 1.12.5
diff --git a/lib/sasl/doc/src/release_handler.xml b/lib/sasl/doc/src/release_handler.xml
index 8f073807fb..975188f489 100644
--- a/lib/sasl/doc/src/release_handler.xml
+++ b/lib/sasl/doc/src/release_handler.xml
@@ -61,6 +61,7 @@
<list type="bulleted">
<item>A release upgrade file, <c>relup</c></item>
<item>A system configuration file, <c>sys.config</c></item>
+ <item>A system configuration source file, <c>sys.config.src</c></item>
</list>
<p>The <c>relup</c> file contains instructions for how to upgrade
to, or downgrade from, this version of the release.</p>
@@ -819,4 +820,3 @@ release_handler:set_unpacked(RelFile, [{myapp,"1.0","/home/user"},...]).
<seealso marker="systools"><c>systools(3)</c></seealso></p>
</section>
</erlref>
-
diff --git a/lib/sasl/doc/src/systools.xml b/lib/sasl/doc/src/systools.xml
index e7c3c499da..4842c732b1 100644
--- a/lib/sasl/doc/src/systools.xml
+++ b/lib/sasl/doc/src/systools.xml
@@ -349,10 +349,11 @@ myapp-1/ebin/myapp.app
the release version as specified in <c>Name.rel</c>.</p>
<p><c>releases/RelVsn</c> contains the boot script
<c>Name.boot</c> renamed to <c>start.boot</c> and, if found,
- the files <c>relup</c> and <c>sys.config</c>. These files
+ the files <c>relup</c> and <c>sys.config</c> or <c>sys.config.src</c>. These files
are searched for in the same directory as <c>Name.rel</c>,
in the current working directory, and in any directories
- specified using option <c>path</c>.</p>
+ specified using option <c>path</c>. In the case of <c>sys.config</c>
+ it is not included if <c>sys.config.src</c> is found.</p>
<p>If the release package is to contain a new Erlang runtime
system, the <c>bin</c> directory of the specified runtime
system <c>{erts,Dir}</c> is copied to <c>erts-ErtsVsn/bin</c>.</p>
@@ -397,4 +398,3 @@ myapp-1/ebin/myapp.app
<seealso marker="script"><c>script(4)</c></seealso></p>
</section>
</erlref>
-
diff --git a/lib/sasl/src/systools_make.erl b/lib/sasl/src/systools_make.erl
index 9d960b7361..4c2ad8dfef 100644
--- a/lib/sasl/src/systools_make.erl
+++ b/lib/sasl/src/systools_make.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2017. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2018. 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.
@@ -310,6 +310,7 @@ add_apply_upgrade(Script,Args) ->
%% RelVsn/start.boot
%% relup
%% sys.config
+%% sys.config.src
%% erts-EVsn[/bin]
%%-----------------------------------------------------------------
@@ -1499,7 +1500,7 @@ mandatory_modules() ->
preloaded() ->
%% Sorted
[erl_prim_loader,erl_tracer,erlang,
- erts_code_purger,erts_dirty_process_code_checker,
+ erts_code_purger,erts_dirty_process_signal_handler,
erts_internal,erts_literal_area_collector,
init,otp_ring0,prim_buffer,prim_eval,prim_file,
prim_inet,prim_zip,zlib].
@@ -1552,6 +1553,7 @@ create_kernel_procs(Appls) ->
%% RelVsn/start.boot
%% relup
%% sys.config
+%% sys.config.src
%% erts-EVsn[/bin]
%%
%% The VariableN.tar.gz files can also be stored as own files not
@@ -1707,14 +1709,18 @@ add_system_files(Tar, RelName, Release, Path1) ->
add_to_tar(Tar, Relup, filename:join(RelVsnDir, "relup"))
end,
- case lookup_file("sys.config", Path) of
- false ->
- ignore;
- Sys ->
- check_sys_config(Sys),
- add_to_tar(Tar, Sys, filename:join(RelVsnDir, "sys.config"))
+ case lookup_file("sys.config.src", Path) of
+ false ->
+ case lookup_file("sys.config", Path) of
+ false ->
+ ignore;
+ Sys ->
+ check_sys_config(Sys),
+ add_to_tar(Tar, Sys, filename:join(RelVsnDir, "sys.config"))
+ end;
+ SysSrc ->
+ add_to_tar(Tar, SysSrc, filename:join(RelVsnDir, "sys.config.src"))
end,
-
ok.
lookup_file(Name, [Dir|Path]) ->
diff --git a/lib/sasl/test/release_handler_SUITE.erl b/lib/sasl/test/release_handler_SUITE.erl
index 824820c214..edd2efdf05 100644
--- a/lib/sasl/test/release_handler_SUITE.erl
+++ b/lib/sasl/test/release_handler_SUITE.erl
@@ -2580,15 +2580,15 @@ check_gg_info(Node,OtherAlive,OtherDead,Synced,N) ->
GGI = rpc:call(Node, global_group, info, []),
GI = rpc:call(Node, global, info,[]),
try do_check_gg_info(OtherAlive,OtherDead,Synced,GGI,GI)
- catch _:E when N==0 ->
+ catch _:E:Stacktrace when N==0 ->
?t:format("~nERROR: check_gg_info failed for ~p:~n~p~n"
"when GGI was: ~p~nand GI was: ~p~n",
- [Node,{E,erlang:get_stacktrace()},GGI,GI]),
+ [Node,{E,Stacktrace},GGI,GI]),
?t:fail("check_gg_info failed");
- _:E ->
+ _:E:Stacktrace ->
?t:format("~nWARNING: check_gg_info failed for ~p:~n~p~n"
"when GGI was: ~p~nand GI was: ~p~n",
- [Node,{E,erlang:get_stacktrace()},GGI,GI]),
+ [Node,{E,Stacktrace},GGI,GI]),
timer:sleep(1000),
check_gg_info(Node,OtherAlive,OtherDead,Synced,N-1)
end.
diff --git a/lib/sasl/test/systools_SUITE.erl b/lib/sasl/test/systools_SUITE.erl
index 07748d975f..c8b2f31120 100644
--- a/lib/sasl/test/systools_SUITE.erl
+++ b/lib/sasl/test/systools_SUITE.erl
@@ -67,7 +67,7 @@ groups() ->
otp_3065_circular_dependenies, included_and_used_sort_script]},
{tar, [],
[tar_options, normal_tar, no_mod_vsn_tar, system_files_tar,
- invalid_system_files_tar, variable_tar,
+ system_src_file_tar, invalid_system_files_tar, variable_tar,
src_tests_tar, var_tar, exref_tar, link_tar, no_sasl_tar,
otp_9507_path_ebin]},
{relup, [],
@@ -945,12 +945,47 @@ system_files_tar(Config) ->
ok.
+
system_files_tar(cleanup,Config) ->
Dir = ?privdir,
file:delete(filename:join(Dir,"sys.config")),
file:delete(filename:join(Dir,"relup")),
ok.
+%% make_tar: Check that sys.config.src and not sys.config is included
+system_src_file_tar(Config) ->
+ {ok, OldDir} = file:get_cwd(),
+
+ {LatestDir, LatestName} = create_script(latest,Config),
+
+ DataDir = filename:absname(?copydir),
+ LibDir = fname([DataDir, d_normal, lib]),
+ P = [fname([LibDir, 'db-2.1', ebin]),
+ fname([LibDir, 'fe-3.1', ebin])],
+
+ ok = file:set_cwd(LatestDir),
+
+ %% Add dummy sys.config and sys.config.src
+ ok = file:write_file("sys.config.src","[${SOMETHING}].\n"),
+ ok = file:write_file("sys.config","[].\n"),
+
+ {ok, _, _} = systools:make_script(LatestName, [silent, {path, P}]),
+ ok = systools:make_tar(LatestName, [{path, P}]),
+ ok = check_tar(fname(["releases","LATEST","sys.config.src"]), LatestName),
+ {error, _} = check_tar(fname(["releases","LATEST","sys.config"]), LatestName),
+ {ok, _, _} = systools:make_tar(LatestName, [{path, P}, silent]),
+ ok = check_tar(fname(["releases","LATEST","sys.config.src"]), LatestName),
+ {error, _} = check_tar(fname(["releases","LATEST","sys.config"]), LatestName),
+
+ ok = file:set_cwd(OldDir),
+
+ ok.
+
+system_src_file_tar(cleanup,Config) ->
+ Dir = ?privdir,
+ file:delete(filename:join(Dir,"sys.config")),
+ file:delete(filename:join(Dir,"sys.config.src")),
+ ok.
%% make_tar: Check that make_tar fails if relup or sys.config exist
%% but do not have valid content
diff --git a/lib/snmp/doc/src/notes.xml b/lib/snmp/doc/src/notes.xml
index 1b5f94ed07..8d48cb911d 100644
--- a/lib/snmp/doc/src/notes.xml
+++ b/lib/snmp/doc/src/notes.xml
@@ -34,7 +34,23 @@
</header>
- <section><title>SNMP 5.2.9</title>
+ <section><title>SNMP 5.2.10</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ The example MIB EX1-MIB in the SNMP application has been
+ corrected to match its example.</p>
+ <p>
+ Own Id: OTP-14204 Aux Id: PR-1726 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>SNMP 5.2.9</title>
<section><title>Fixed Bugs and Malfunctions</title>
<list>
diff --git a/lib/snmp/doc/src/snmp_impl_example_agent.xml b/lib/snmp/doc/src/snmp_impl_example_agent.xml
index a86006a0a7..e576fa51f3 100644
--- a/lib/snmp/doc/src/snmp_impl_example_agent.xml
+++ b/lib/snmp/doc/src/snmp_impl_example_agent.xml
@@ -47,6 +47,7 @@
EX1-MIB DEFINITIONS ::= BEGIN
IMPORTS
+ experimental FROM RFC1155-SMI
RowStatus FROM STANDARD-MIB
DisplayString FROM RFC1213-MIB
OBJECT-TYPE FROM RFC-1212
@@ -81,7 +82,7 @@ EX1-MIB DEFINITIONS ::= BEGIN
FriendsEntry ::=
SEQUENCE {
- fIndex
+ fIndex
INTEGER,
fName
DisplayString,
@@ -105,6 +106,7 @@ EX1-MIB DEFINITIONS ::= BEGIN
DESCRIPTION
"Name of friend"
::= { friendsEntry 2 }
+
fAddress OBJECT-TYPE
SYNTAX DisplayString (SIZE (0..255))
ACCESS read-write
@@ -112,6 +114,7 @@ EX1-MIB DEFINITIONS ::= BEGIN
DESCRIPTION
"Address of friend"
::= { friendsEntry 3 }
+
fStatus OBJECT-TYPE
SYNTAX RowStatus
ACCESS read-write
@@ -119,12 +122,13 @@ EX1-MIB DEFINITIONS ::= BEGIN
DESCRIPTION
"The status of this conceptual row."
::= { friendsEntry 4 }
+
fTrap TRAP-TYPE
ENTERPRISE example1
VARIABLES { myName, fIndex }
DESCRIPTION
- "This trap is sent when something happens to
- the friend specified by fIndex."
+ "This trap is sent when something happens to
+ the friend specified by fIndex."
::= 1
END
</code>
diff --git a/lib/snmp/vsn.mk b/lib/snmp/vsn.mk
index c195f9f5d9..2c97683625 100644
--- a/lib/snmp/vsn.mk
+++ b/lib/snmp/vsn.mk
@@ -19,6 +19,6 @@
# %CopyrightEnd%
APPLICATION = snmp
-SNMP_VSN = 5.2.9
+SNMP_VSN = 5.2.10
PRE_VSN =
APP_VSN = "$(APPLICATION)-$(SNMP_VSN)$(PRE_VSN)"
diff --git a/lib/ssh/doc/src/notes.xml b/lib/ssh/doc/src/notes.xml
index 3a2f55a487..db60b4ab6f 100644
--- a/lib/ssh/doc/src/notes.xml
+++ b/lib/ssh/doc/src/notes.xml
@@ -30,6 +30,105 @@
<file>notes.xml</file>
</header>
+<section><title>Ssh 4.6.7</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Fix bad spec in ssh.hrl: <c>double_algs()</c>.</p>
+ <p>
+ Own Id: OTP-14990</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Ssh 4.6.6</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Remove a blocking risk when a channel is closed and an
+ operation is tried on that channel after at least a
+ second's time gap.</p>
+ <p>
+ Own Id: OTP-14939</p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Added ssh_compat_SUITE.</p>
+ <p>
+ This suite contains a number of interoperability tests
+ mainly with OpenSSH. The tests start docker containers
+ with different OpenSSH and OpenSSL/LibreSSLcryptolib
+ versions and performs a number of tests of supported
+ algorithms.</p>
+ <p>
+ All login methods and all user's public key types are
+ tested both for the client and the server.</p>
+ <p>
+ All algorithms for kex, cipher, host key, mac and
+ compressions are tested with a number of exec and sftp
+ tests, both for the client and the server.</p>
+ <p>
+ Own Id: OTP-14194 Aux Id: OTP-12487 </p>
+ </item>
+ <item>
+ <p>
+ Default exec is disabled when a user-defined shell is
+ enabled.</p>
+ <p>
+ Own Id: OTP-14881</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Ssh 4.6.5</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Adjusted supervisor timeouts</p>
+ <p>
+ Own Id: OTP-14907</p>
+ </item>
+ <item>
+ <p>
+ Remove ERROR messages for slow process exits</p>
+ <p>
+ Own Id: OTP-14930</p>
+ </item>
+ </list>
+ </section>
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Add option <c>save_accepted_host</c> to
+ <c>ssh:connection</c>. This option, if set to false,
+ inhibits saving host keys to e.g the file
+ <c>known_hosts</c>.</p>
+ <p>
+ Own Id: OTP-14935</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Ssh 4.6.4</title>
<section><title>Fixed Bugs and Malfunctions</title>
@@ -60,7 +159,6 @@
</list>
</section>
-
<section><title>Improvements and New Features</title>
<list>
<item>
@@ -90,7 +188,6 @@
</section>
<section><title>Ssh 4.6.2</title>
-
<section><title>Fixed Bugs and Malfunctions</title>
<list>
<item>
@@ -370,6 +467,40 @@
</section>
+
+<section><title>Ssh 4.4.2.2</title>
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Default exec is disabled when a user-defined shell is
+ enabled.</p>
+ <p>
+ Own Id: OTP-14881</p>
+ </item>
+ </list>
+ </section>
+</section>
+
+
+<section><title>Ssh 4.4.2.1</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Trailing white space was removed at end of the
+ hello-string. This caused interoperability problems with
+ some other ssh-implementations (e.g OpenSSH 7.3p1 on
+ Solaris 11)</p>
+ <p>
+ Own Id: OTP-14763 Aux Id: ERIERL-74 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Ssh 4.4.2</title>
<section><title>Fixed Bugs and Malfunctions</title>
@@ -740,6 +871,93 @@
</section>
+<section><title>Ssh 4.2.2.5</title>
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Default exec is disabled when a user-defined shell is
+ enabled.</p>
+ <p>
+ Own Id: OTP-14881</p>
+ </item>
+ </list>
+ </section>
+</section>
+
+
+<section><title>Ssh 4.2.2.4</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Trailing white space was removed at end of the
+ hello-string. This caused interoperability problems with
+ some other ssh-implementations (e.g OpenSSH 7.3p1 on
+ Solaris 11)</p>
+ <p>
+ Own Id: OTP-14763 Aux Id: ERIERL-74 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Ssh 4.2.2.3</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ The key exchange algorithm
+ diffie-hellman-group-exchange-sha* has a server-option
+ <c>{dh_gex_limits,{Min,Max}}</c>. There was a hostkey
+ signature validation error on the client side if the
+ option was used and the <c>Min</c> or the <c>Max</c>
+ differed from the corresponding values obtained from the
+ client.</p>
+ <p>
+ This bug is now corrected.</p>
+ <p>
+ Own Id: OTP-14166</p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Key exchange algorithms
+ diffie-hellman-group-exchange-sha* optimized, up to a
+ factor of 11 for the slowest ( = biggest and safest) one.</p>
+ <p>
+ Own Id: OTP-14169 Aux Id: seq-13261 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Ssh 4.2.2.2</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Upgrade of an established client connection could crash
+ because the ssh client supervisors children had wrong
+ type. This is fixed now.</p>
+ <p>
+ Own Id: OTP-13782 Aux Id: seq13158 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Ssh 4.2.2.1</title>
<section><title>Fixed Bugs and Malfunctions</title>
diff --git a/lib/ssh/doc/src/ssh.xml b/lib/ssh/doc/src/ssh.xml
index 337f4094cc..d36be8431c 100644
--- a/lib/ssh/doc/src/ssh.xml
+++ b/lib/ssh/doc/src/ssh.xml
@@ -67,24 +67,41 @@
<taglist>
<tag><c>boolean() =</c></tag>
<item><p><c>true | false</c></p></item>
+
<tag><c>string() =</c></tag>
<item><p><c>[byte()]</c></p></item>
+
<tag><c>ssh_daemon_ref() =</c></tag>
<item><p>opaque() -
as returned by <c>ssh:daemon/[1,2,3]</c></p></item>
+
+ <tag><c>ok_error(OKtype) = </c></tag>
+ <item><p><c>{ok,OKtype} | {error, term()}</c></p></item>
+
+ <tag><c>ok_error() = </c></tag>
+ <item><p><c>ok_error(term())</c></p></item>
+
<tag><c>ssh_connection_ref() =</c></tag>
<item><p>opaque() - as returned by <c>ssh:connect/3</c></p></item>
+
<tag><c>ip_address() =</c></tag>
- <item><p><c>inet::ip_address</c></p></item>
+ <item><p><c>inet::ip_address()</c></p></item>
+
+ <tag><c>port_number() =</c></tag>
+ <item><p><c>inet::port_number()</c></p></item>
+
<tag><c>subsystem_spec() =</c></tag>
<item><p><c>{subsystem_name(),
{channel_callback(), channel_init_args()}}</c></p></item>
+
<tag><c>subsystem_name() =</c></tag>
<item><p><c>string()</c></p></item>
+
<tag><c>channel_callback() =</c></tag>
<item><p><c>atom()</c> - Name of the Erlang module
implementing the subsystem using the <c>ssh_channel</c> behavior, see
<seealso marker="ssh_channel">ssh_channel(3)</seealso></p></item>
+
<tag><c>key_cb() =</c></tag>
<item>
<p><c>atom() | {atom(), list()}</c></p>
@@ -94,6 +111,7 @@
case maybe.</p>
<p><c>list()</c> - List of options that can be passed to the callback module.</p>
</item>
+
<tag><c>channel_init_args() =</c></tag>
<item><p><c>list()</c></p></item>
@@ -227,6 +245,18 @@
</item>
</list>
</item>
+
+ <tag><c><![CDATA[{save_accepted_host, boolean()}]]></c></tag>
+ <item>
+ <p>If <c>true</c>, the client saves an accepted host key to avoid the
+ accept question the next time the same host is connected. If the option
+ <c>key_cb</c> is not present, the key is saved in the file "known_hosts".
+ </p>
+ <p>If <c>false</c>, the key is not saved and the key will still be unknown
+ at the next access of the same host.
+ </p>
+ </item>
+
<tag><c><![CDATA[{user_interaction, boolean()}]]></c></tag>
<item>
<p>If <c>false</c>, disables the client to connect to the server
@@ -466,8 +496,8 @@
<v>Option = client_version | server_version | user | peer | sockname </v>
<v>Value = [option_value()] </v>
<v>option_value() = {{Major::integer(), Minor::integer()}, VersionString::string()} |
- User::string() | Peer::{inet:hostname(), {inet::ip_adress(), inet::port_number()}} |
- Sockname::{inet::ip_adress(), inet::port_number()}</v>
+ User::string() | Peer::{inet:hostname(), {ip_address(), port_number()}} |
+ Sockname::{ip_address(), port_number()}</v>
</type>
<desc>
<p>Retrieves information about a connection.</p>
@@ -529,22 +559,83 @@
The option can be set to the empty list if
you do not want the daemon to run any subsystems.</p>
</item>
- <tag><c><![CDATA[{shell, {Module, Function, Args} |
+
+ <tag><marker id="daemon_opt_shell"/>
+ <c><![CDATA[{shell, {Module, Function, Args} |
fun(string() = User) - > pid() | fun(string() = User,
ip_address() = PeerAddr) -> pid()}]]></c></tag>
<item>
<p>Defines the read-eval-print loop used when a shell is
requested by the client. The default is to use the Erlang shell:
<c><![CDATA[{shell, start, []}]]></c></p>
+ <p>See the option <seealso marker="#daemon_opt_exec"><c>exec</c></seealso>
+ for a description of how the daemon execute exec-requests depending on
+ the shell- and exec-options.</p>
+ </item>
+
+ <tag><marker id="daemon_opt_exec"/>
+ <c><![CDATA[{exec, {direct, exec_spec()}}]]></c>
+ <br/><c>where:</c>
+ <br/><c>exec_spec() = </c>
+ <br/><c> fun(Cmd::string()) -> ok_error()</c>
+ <br/><c> | fun(Cmd::string(), User::string()) -> ok_error()</c>
+ <br/><c> | fun(Cmd::string(), User::string(), ClientAddr::{ip_address(), port_number()}) -> ok_error()</c>
+ </tag>
+ <item>
+ <p>This option changes how the daemon execute exec-requests from clients. The term in <c>ok_error()</c>
+ is formatted to a string if it is a non-string type. No trailing newline is added in the ok-case but in the
+ error case.</p>
+ <p>Error texts are returned on channel-type 1 which usually are piped to <c>stderr</c> on e.g Linux systems.
+ Texts from a successful execution will in similar manner be piped to <c>stdout</c>. The exit-status code
+ is set to 0 for success and -1 for errors. The exact results presented on the client side depends on the
+ client.
+ </p>
+ <p>The option cooperates with the daemon-option <seealso marker="#daemon_opt_shell"><c>shell</c></seealso>
+ in the following way:</p>
+ <taglist>
+ <tag>1. If the exec-option is present (the shell-option may or may not be present):</tag>
+ <item>
+ <p>The exec-option fun is called with the same number of parameters as the arity of the fun,
+ and the result is returned to the client.
+ </p>
+ </item>
+
+ <tag>2. If the exec-option is absent, but a shell-option is present with the default Erlang shell:</tag>
+ <item>
+ <p>The default Erlang evaluator is used and the result is returned to the client.</p>
+ </item>
+
+ <tag>3. If the exec-option is absent, but a shell-option is present that is not the default Erlang shell:</tag>
+ <item>
+ <p>The exec-request is not evaluated and an error message is returned to the client.</p>
+ </item>
+
+ <tag>4. If neither the exec-option nor the shell-option is present:</tag>
+ <item>
+ <p>The default Erlang evaluator is used and the result is returned to the client.</p>
+ </item>
+ </taglist>
+ <p>If a custom CLI is installed (see the option <seealso marker="#daemon_opt_ssh_cli"><c>ssh_cli</c></seealso>)
+ the rules above are replaced by thoose implied by the custom CLI.
+ </p>
+ <note>
+ <p>The exec-option has existed for a long time but has not previously been documented. The old
+ definition and behaviour are retained but obey the rules 1-4 above if conflicting.
+ The old and undocumented style should not be used in new programs.</p>
+ </note>
</item>
- <tag><c><![CDATA[{ssh_cli, {channel_callback(),
+
+ <tag><marker id="daemon_opt_ssh_cli"/>
+ <c><![CDATA[{ssh_cli, {channel_callback(),
channel_init_args()} | no_cli}]]></c></tag>
<item>
<p>Provides your own CLI implementation, that is, a channel callback
module that implements a shell and command execution. The shell
read-eval-print loop can be customized, using the
- option <c>shell</c>. This means less work than implementing
- an own CLI channel. If set to <c>no_cli</c>, the CLI channels
+ option <seealso marker="#daemon_opt_shell"><c>shell</c></seealso>. This means less work than implementing
+ an own CLI channel. If <c>ssh_cli</c> is set to <c>no_cli</c>, the CLI channels
+ like <seealso marker="#daemon_opt_shell"><c>shell</c></seealso>
+ and <seealso marker="#daemon_opt_exec"><c>exec</c></seealso>
are disabled and only subsystem channels are allowed.</p>
</item>
<tag><c><![CDATA[{user_dir, string()}]]></c></tag>
diff --git a/lib/ssh/doc/src/ssh_connection.xml b/lib/ssh/doc/src/ssh_connection.xml
index 150d46a9a2..72830de04d 100644
--- a/lib/ssh/doc/src/ssh_connection.xml
+++ b/lib/ssh/doc/src/ssh_connection.xml
@@ -428,7 +428,7 @@
</func>
<func>
- <name>shell(ConnectionRef, ChannelId) -> ssh_request_status() | {error, closed}
+ <name>shell(ConnectionRef, ChannelId) -> ok | failure | {error, closed}
</name>
<fsummary>Requests that the user default shell (typically defined in
/etc/passwd in Unix systems) is to be executed at the server end.</fsummary>
@@ -440,6 +440,10 @@
<p>Is to be called by a client channel process to request that the user default
shell (typically defined in /etc/passwd in Unix systems) is executed
at the server end.</p>
+ <p>Note: the return value is <c>ok</c> instead of <c>success</c> unlike in other
+ functions in this module. This is a fault that was introduced so long ago that
+ any change would break a large number of existing software.
+ </p>
</desc>
</func>
diff --git a/lib/ssh/doc/src/ssh_sftp.xml b/lib/ssh/doc/src/ssh_sftp.xml
index ed7fbf9cf3..129426a6d5 100644
--- a/lib/ssh/doc/src/ssh_sftp.xml
+++ b/lib/ssh/doc/src/ssh_sftp.xml
@@ -464,11 +464,16 @@
<v>FileInfo = record()</v>
</type>
<desc>
- <p>Returns a <c><![CDATA[file_info]]></c> record from the file specified by
+ <p>Returns a <c><![CDATA[file_info]]></c> record from the file system object specified by
<c><![CDATA[Name]]></c> or <c><![CDATA[Handle]]></c>. See
<seealso marker="kernel:file#read_file_info-2">file:read_file_info/2</seealso>
for information about the record.
</p>
+ <p>
+ Depending on the underlying OS:es links might be followed and info on the final file, directory
+ etc is returned. See <seealso marker="#read_link_info-2">ssh_sftp::read_link_info/2</seealso>
+ on how to get information on links instead.
+ </p>
</desc>
</func>
diff --git a/lib/ssh/src/ssh.erl b/lib/ssh/src/ssh.erl
index 032d87bdad..25d537c624 100644
--- a/lib/ssh/src/ssh.erl
+++ b/lib/ssh/src/ssh.erl
@@ -184,7 +184,6 @@ channel_info(ConnectionRef, ChannelId, Options) ->
daemon(Port) ->
daemon(Port, []).
-
daemon(Socket, UserOptions) when is_port(Socket) ->
try
#{} = Options = ssh_options:handle_options(server, UserOptions),
@@ -267,8 +266,6 @@ daemon(Host0, Port0, UserOptions0) when 0 =< Port0, Port0 =< 65535,
daemon(_, _, _) ->
{error, badarg}.
-
-
%%--------------------------------------------------------------------
-spec daemon_info(daemon_ref()) -> ok_error( [{atom(), term()}] ).
diff --git a/lib/ssh/src/ssh.hrl b/lib/ssh/src/ssh.hrl
index 3dee1c5521..8d950eea3c 100644
--- a/lib/ssh/src/ssh.hrl
+++ b/lib/ssh/src/ssh.hrl
@@ -35,6 +35,8 @@
-define(DEFAULT_TRANSPORT, {tcp, gen_tcp, tcp_closed} ).
+-define(DEFAULT_SHELL, {shell, start, []} ).
+
-define(MAX_RND_PADDING_LEN, 15).
-define(SUPPORTED_AUTH_METHODS, "publickey,keyboard-interactive,password").
@@ -112,7 +114,7 @@
| {mac, double_algs()}
| {compression, double_algs()} .
-type simple_algs() :: list( atom() ) .
--type double_algs() :: list( {client2serverlist,simple_algs()} | {server2client,simple_algs()} )
+-type double_algs() :: list( {client2server,simple_algs()} | {server2client,simple_algs()} )
| simple_algs() .
-type options() :: #{socket_options := socket_options(),
diff --git a/lib/ssh/src/ssh_acceptor.erl b/lib/ssh/src/ssh_acceptor.erl
index d66a34c58a..27d4242dd4 100644
--- a/lib/ssh/src/ssh_acceptor.erl
+++ b/lib/ssh/src/ssh_acceptor.erl
@@ -86,7 +86,8 @@ acceptor_init(Parent, Port, Address, Opts, AcceptTimeout) ->
acceptor_loop(Callback, Port, Address, Opts, LSock, AcceptTimeout);
{error,_} -> % Not open, a restart
- {ok,NewLSock} = listen(Port, Opts),
+ %% Allow gen_tcp:listen to fail 4 times if eaddrinuse:
+ {ok,NewLSock} = try_listen(Port, Opts, 4),
proc_lib:init_ack(Parent, {ok, self()}),
Opts1 = ?DELETE_INTERNAL_OPT(lsocket, Opts),
{_, Callback, _} = ?GET_OPT(transport, Opts1),
@@ -98,6 +99,19 @@ acceptor_init(Parent, Port, Address, Opts, AcceptTimeout) ->
end.
+try_listen(Port, Opts, NtriesLeft) ->
+ try_listen(Port, Opts, 1, NtriesLeft).
+
+try_listen(Port, Opts, N, Nmax) ->
+ case listen(Port, Opts) of
+ {error,eaddrinuse} when N<Nmax ->
+ timer:sleep(10*N), % Sleep 10, 20, 30,... ms
+ try_listen(Port, Opts, N+1, Nmax);
+ Other ->
+ Other
+ end.
+
+
request_ownership(LSock, SockOwner) ->
SockOwner ! {request_control,LSock,self()},
receive
diff --git a/lib/ssh/src/ssh_acceptor_sup.erl b/lib/ssh/src/ssh_acceptor_sup.erl
index a24664793b..fc564a359b 100644
--- a/lib/ssh/src/ssh_acceptor_sup.erl
+++ b/lib/ssh/src/ssh_acceptor_sup.erl
@@ -86,10 +86,7 @@ child_spec(Address, Port, Profile, Options) ->
Timeout = ?GET_INTERNAL_OPT(timeout, Options, ?DEFAULT_TIMEOUT),
#{id => id(Address, Port, Profile),
start => {ssh_acceptor, start_link, [Port, Address, Options, Timeout]},
- restart => transient,
- shutdown => 5500, %brutal_kill,
- type => worker,
- modules => [ssh_acceptor]
+ restart => transient % because a crashed listener could be replaced by a new one
}.
id(Address, Port, Profile) ->
diff --git a/lib/ssh/src/ssh_channel_sup.erl b/lib/ssh/src/ssh_channel_sup.erl
index 6b01dc334d..8444533fd1 100644
--- a/lib/ssh/src/ssh_channel_sup.erl
+++ b/lib/ssh/src/ssh_channel_sup.erl
@@ -26,7 +26,7 @@
-behaviour(supervisor).
--export([start_link/1, start_child/2]).
+-export([start_link/1, start_child/5]).
%% Supervisor callback
-export([init/1]).
@@ -37,7 +37,14 @@
start_link(Args) ->
supervisor:start_link(?MODULE, [Args]).
-start_child(Sup, ChildSpec) ->
+start_child(Sup, Callback, Id, Args, Exec) ->
+ ChildSpec =
+ #{id => make_ref(),
+ start => {ssh_channel, start_link, [self(), Id, Callback, Args, Exec]},
+ restart => temporary,
+ type => worker,
+ modules => [ssh_channel]
+ },
supervisor:start_child(Sup, ChildSpec).
%%%=========================================================================
diff --git a/lib/ssh/src/ssh_cli.erl b/lib/ssh/src/ssh_cli.erl
index 62854346b0..783f2f80c0 100644
--- a/lib/ssh/src/ssh_cli.erl
+++ b/lib/ssh/src/ssh_cli.erl
@@ -118,31 +118,52 @@ handle_ssh_msg({ssh_cm, ConnectionHandler,
write_chars(ConnectionHandler, ChannelId, Chars),
{ok, State#state{pty = Pty, buf = NewBuf}};
-handle_ssh_msg({ssh_cm, ConnectionHandler,
- {shell, ChannelId, WantReply}}, State) ->
+handle_ssh_msg({ssh_cm, ConnectionHandler, {shell, ChannelId, WantReply}}, State) ->
NewState = start_shell(ConnectionHandler, State),
- ssh_connection:reply_request(ConnectionHandler, WantReply,
- success, ChannelId),
+ ssh_connection:reply_request(ConnectionHandler, WantReply, success, ChannelId),
{ok, NewState#state{channel = ChannelId,
cm = ConnectionHandler}};
-handle_ssh_msg({ssh_cm, ConnectionHandler,
- {exec, ChannelId, WantReply, Cmd}}, #state{exec=undefined} = State) ->
- {Reply, Status} = exec(Cmd),
- write_chars(ConnectionHandler,
- ChannelId, io_lib:format("~p\n", [Reply])),
- ssh_connection:reply_request(ConnectionHandler, WantReply,
- success, ChannelId),
- ssh_connection:exit_status(ConnectionHandler, ChannelId, Status),
- ssh_connection:send_eof(ConnectionHandler, ChannelId),
- {stop, ChannelId, State#state{channel = ChannelId, cm = ConnectionHandler}};
-handle_ssh_msg({ssh_cm, ConnectionHandler,
- {exec, ChannelId, WantReply, Cmd}}, State) ->
- NewState = start_shell(ConnectionHandler, Cmd, State),
- ssh_connection:reply_request(ConnectionHandler, WantReply,
- success, ChannelId),
- {ok, NewState#state{channel = ChannelId,
- cm = ConnectionHandler}};
+handle_ssh_msg({ssh_cm, ConnectionHandler, {exec, ChannelId, WantReply, Cmd}}, S0) ->
+ case
+ case S0#state.exec of
+ {direct,F} ->
+ %% Exec called and a Fun or MFA is defined to use. The F returns the
+ %% value to return.
+ exec_direct(ConnectionHandler, F, Cmd);
+
+ undefined when S0#state.shell == ?DEFAULT_SHELL ->
+ %% Exec called and the shell is the default shell (= Erlang shell).
+ %% To be exact, eval the term as an Erlang term (but not using the
+ %% ?DEFAULT_SHELL directly). This disables banner, prompts and such.
+ exec_in_erlang_default_shell(Cmd);
+
+ undefined ->
+ %% Exec called, but the a shell other than the default shell is defined.
+ %% No new exec shell is defined, so don't execute!
+ %% We don't know if it is intended to use the new shell or not.
+ {"Prohibited.", 255, 1};
+
+ _ ->
+ %% Exec called and a Fun or MFA is defined to use. The F communicates via
+ %% standard io:write/read.
+ %% Kept for compatibility.
+ S1 = start_exec_shell(ConnectionHandler, Cmd, S0),
+ ssh_connection:reply_request(ConnectionHandler, WantReply, success, ChannelId),
+ {ok, S1}
+ end
+ of
+ {Reply, Status, Type} ->
+ write_chars(ConnectionHandler, ChannelId, Type, Reply),
+ ssh_connection:reply_request(ConnectionHandler, WantReply, success, ChannelId),
+ ssh_connection:exit_status(ConnectionHandler, ChannelId, Status),
+ ssh_connection:send_eof(ConnectionHandler, ChannelId),
+ {stop, ChannelId, S0#state{channel = ChannelId, cm = ConnectionHandler}};
+
+ {ok, S} ->
+ {ok, S#state{channel = ChannelId,
+ cm = ConnectionHandler}}
+ end;
handle_ssh_msg({ssh_cm, _ConnectionHandler, {eof, _ChannelId}}, State) ->
{ok, State};
@@ -249,35 +270,7 @@ to_group(Data, Group) ->
end,
to_group(Tail, Group).
-exec(Cmd) ->
- case eval(parse(scan(Cmd))) of
- {error, _} ->
- {Cmd, 0}; %% This should be an external call
- Term ->
- Term
- end.
-
-scan(Cmd) ->
- erl_scan:string(Cmd).
-
-parse({ok, Tokens, _}) ->
- erl_parse:parse_exprs(Tokens);
-parse(Error) ->
- Error.
-
-eval({ok, Expr_list}) ->
- case (catch erl_eval:exprs(Expr_list,
- erl_eval:new_bindings())) of
- {value, Value, _NewBindings} ->
- {Value, 0};
- {'EXIT', {Error, _}} ->
- {Error, -1};
- Error ->
- {Error, -1}
- end;
-eval(Error) ->
- {Error, -1}.
-
+%%--------------------------------------------------------------------
%%% io_request, handle io requests from the user process,
%%% Note, this is not the real I/O-protocol, but the mockup version
%%% used between edlin and a user_driver. The protocol tags are
@@ -453,11 +446,14 @@ move_cursor(From, To, #ssh_pty{width=Width, term=Type}) ->
%% %%% make sure that there is data to send
%% %%% before calling ssh_connection:send
write_chars(ConnectionHandler, ChannelId, Chars) ->
+ write_chars(ConnectionHandler, ChannelId, ?SSH_EXTENDED_DATA_DEFAULT, Chars).
+
+write_chars(ConnectionHandler, ChannelId, Type, Chars) ->
case has_chars(Chars) of
false -> ok;
true -> ssh_connection:send(ConnectionHandler,
ChannelId,
- ?SSH_EXTENDED_DATA_DEFAULT,
+ Type,
Chars)
end.
@@ -493,53 +489,130 @@ bin_to_list(L) when is_list(L) ->
bin_to_list(I) when is_integer(I) ->
I.
+
+%%--------------------------------------------------------------------
start_shell(ConnectionHandler, State) ->
- Shell = State#state.shell,
- ConnectionInfo = ssh_connection_handler:connection_info(ConnectionHandler,
- [peer, user]),
- ShellFun = case is_function(Shell) of
- true ->
- User = proplists:get_value(user, ConnectionInfo),
- case erlang:fun_info(Shell, arity) of
- {arity, 1} ->
- fun() -> Shell(User) end;
- {arity, 2} ->
- {_, PeerAddr} = proplists:get_value(peer, ConnectionInfo),
- fun() -> Shell(User, PeerAddr) end;
- _ ->
- Shell
- end;
- _ ->
- Shell
- end,
- Echo = get_echo(State#state.pty),
- Group = group:start(self(), ShellFun, [{echo, Echo}]),
- State#state{group = Group, buf = empty_buf()}.
-
-start_shell(_ConnectionHandler, Cmd, #state{exec={M, F, A}} = State) ->
- Group = group:start(self(), {M, F, A++[Cmd]}, [{echo, false}]),
- State#state{group = Group, buf = empty_buf()};
-start_shell(ConnectionHandler, Cmd, #state{exec=Shell} = State) when is_function(Shell) ->
-
- ConnectionInfo = ssh_connection_handler:connection_info(ConnectionHandler,
- [peer, user]),
- User = proplists:get_value(user, ConnectionInfo),
- ShellFun =
- case erlang:fun_info(Shell, arity) of
- {arity, 1} ->
- fun() -> Shell(Cmd) end;
- {arity, 2} ->
- fun() -> Shell(Cmd, User) end;
- {arity, 3} ->
- {_, PeerAddr} = proplists:get_value(peer, ConnectionInfo),
- fun() -> Shell(Cmd, User, PeerAddr) end;
- _ ->
- Shell
- end,
- Echo = get_echo(State#state.pty),
- Group = group:start(self(), ShellFun, [{echo,Echo}]),
- State#state{group = Group, buf = empty_buf()}.
+ ShellSpawner =
+ case State#state.shell of
+ Shell when is_function(Shell, 1) ->
+ [{user,User}] = ssh_connection_handler:connection_info(ConnectionHandler, [user]),
+ fun() -> Shell(User) end;
+ Shell when is_function(Shell, 2) ->
+ ConnectionInfo =
+ ssh_connection_handler:connection_info(ConnectionHandler, [peer, user]),
+ User = proplists:get_value(user, ConnectionInfo),
+ {_, PeerAddr} = proplists:get_value(peer, ConnectionInfo),
+ fun() -> Shell(User, PeerAddr) end;
+ {_,_,_} = Shell ->
+ Shell
+ end,
+ State#state{group = group:start(self(), ShellSpawner, [{echo, get_echo(State#state.pty)}]),
+ buf = empty_buf()}.
+
+%%--------------------------------------------------------------------
+start_exec_shell(ConnectionHandler, Cmd, State) ->
+ ExecShellSpawner =
+ case State#state.exec of
+ ExecShell when is_function(ExecShell, 1) ->
+ fun() -> ExecShell(Cmd) end;
+ ExecShell when is_function(ExecShell, 2) ->
+ [{user,User}] = ssh_connection_handler:connection_info(ConnectionHandler, [user]),
+ fun() -> ExecShell(Cmd, User) end;
+ ExecShell when is_function(ExecShell, 3) ->
+ ConnectionInfo =
+ ssh_connection_handler:connection_info(ConnectionHandler, [peer, user]),
+ User = proplists:get_value(user, ConnectionInfo),
+ {_, PeerAddr} = proplists:get_value(peer, ConnectionInfo),
+ fun() -> ExecShell(Cmd, User, PeerAddr) end;
+ {M,F,A} ->
+ {M, F, A++[Cmd]}
+ end,
+ State#state{group = group:start(self(), ExecShellSpawner, [{echo,false}]),
+ buf = empty_buf()}.
+
+%%--------------------------------------------------------------------
+exec_in_erlang_default_shell(Cmd) ->
+ case eval(parse(scan(Cmd))) of
+ {ok, Term} ->
+ {io_lib:format("~p\n", [Term]), 0, 0};
+ {error, Error} when is_atom(Error) ->
+ {io_lib:format("Error in ~p: ~p\n", [Cmd,Error]), -1, 1};
+ _ ->
+ {io_lib:format("Error: ~p\n", [Cmd]), -1, 1}
+ end.
+
+scan(Cmd) ->
+ erl_scan:string(Cmd).
+
+parse({ok, Tokens, _}) ->
+ erl_parse:parse_exprs(Tokens);
+parse(Error) ->
+ Error.
+
+eval({ok, Expr_list}) ->
+ case (catch erl_eval:exprs(Expr_list,
+ erl_eval:new_bindings())) of
+ {value, Value, _NewBindings} ->
+ {ok, Value};
+ {'EXIT', {Error, _}} ->
+ {error, Error};
+ {error, Error} ->
+ {error, Error};
+ Error ->
+ {error, Error}
+ end;
+eval({error,Error}) ->
+ {error, Error};
+eval(Error) ->
+ {error, Error}.
+
+%%--------------------------------------------------------------------
+exec_direct(ConnectionHandler, ExecSpec, Cmd) ->
+ try
+ case ExecSpec of
+ _ when is_function(ExecSpec, 1) ->
+ ExecSpec(Cmd);
+ _ when is_function(ExecSpec, 2) ->
+ [{user,User}] = ssh_connection_handler:connection_info(ConnectionHandler, [user]),
+ ExecSpec(Cmd, User);
+ _ when is_function(ExecSpec, 3) ->
+ ConnectionInfo =
+ ssh_connection_handler:connection_info(ConnectionHandler, [peer, user]),
+ User = proplists:get_value(user, ConnectionInfo),
+ {_, PeerAddr} = proplists:get_value(peer, ConnectionInfo),
+ ExecSpec(Cmd, User, PeerAddr)
+ end
+ of
+ Reply ->
+ return_direct_exec_reply(Reply, Cmd)
+ catch
+ C:Error ->
+ {io_lib:format("Error in \"~s\": ~p ~p~n", [Cmd,C,Error]), -1, 1}
+ end.
+
+
+
+return_direct_exec_reply(Reply, Cmd) ->
+ case fmt_exec_repl(Reply) of
+ {ok,S} ->
+ {S, 0, 0};
+ {error,S} ->
+ {io_lib:format("Error in \"~s\": ~s~n", [Cmd,S]), -1, 1}
+ end.
+
+fmt_exec_repl({T,A}) when T==ok ; T==error ->
+ try
+ {T, io_lib:format("~s",[A])}
+ catch
+ error:badarg ->
+ {T, io_lib:format("~p", [A])};
+ C:Err ->
+ {error, io_lib:format("~p:~p~n",[C,Err])}
+ end;
+fmt_exec_repl(Other) ->
+ {error, io_lib:format("Bad exec-plugin return: ~p",[Other])}.
+%%--------------------------------------------------------------------
% Pty can be undefined if the client never sets any pty options before
% starting the shell.
get_echo(undefined) ->
diff --git a/lib/ssh/src/ssh_connection.erl b/lib/ssh/src/ssh_connection.erl
index 7e9ee78fd2..946ae2967b 100644
--- a/lib/ssh/src/ssh_connection.erl
+++ b/lib/ssh/src/ssh_connection.erl
@@ -812,22 +812,20 @@ start_channel(Cb, Id, Args, SubSysSup, Opts) ->
start_channel(Cb, Id, Args, SubSysSup, undefined, Opts).
start_channel(Cb, Id, Args, SubSysSup, Exec, Opts) ->
- ChildSpec = child_spec(Cb, Id, Args, Exec),
ChannelSup = ssh_subsystem_sup:channel_supervisor(SubSysSup),
- assert_limit_num_channels_not_exceeded(ChannelSup, Opts),
- ssh_channel_sup:start_child(ChannelSup, ChildSpec).
+ case max_num_channels_not_exceeded(ChannelSup, Opts) of
+ true ->
+ ssh_channel_sup:start_child(ChannelSup, Cb, Id, Args, Exec);
+ false ->
+ throw(max_num_channels_exceeded)
+ end.
-assert_limit_num_channels_not_exceeded(ChannelSup, Opts) ->
+max_num_channels_not_exceeded(ChannelSup, Opts) ->
MaxNumChannels = ?GET_OPT(max_channels, Opts),
NumChannels = length([x || {_,_,worker,[ssh_channel]} <-
supervisor:which_children(ChannelSup)]),
- if
- %% Note that NumChannels is BEFORE starting a new one
- NumChannels < MaxNumChannels ->
- ok;
- true ->
- throw(max_num_channels_exceeded)
- end.
+ %% Note that NumChannels is BEFORE starting a new one
+ NumChannels < MaxNumChannels.
%%--------------------------------------------------------------------
%%% Internal functions
@@ -874,14 +872,6 @@ check_subsystem(SsName, Options) ->
Value
end.
-child_spec(Callback, Id, Args, Exec) ->
- Name = make_ref(),
- StartFunc = {ssh_channel, start_link, [self(), Id, Callback, Args, Exec]},
- Restart = temporary,
- Shutdown = 3600,
- Type = worker,
- {Name, StartFunc, Restart, Shutdown, Type, [ssh_channel]}.
-
start_cli(#connection{cli_spec = no_cli}, _) ->
{error, cli_disabled};
start_cli(#connection{options = Options,
diff --git a/lib/ssh/src/ssh_connection_handler.erl b/lib/ssh/src/ssh_connection_handler.erl
index 0ca960ef96..852e70d9e2 100644
--- a/lib/ssh/src/ssh_connection_handler.erl
+++ b/lib/ssh/src/ssh_connection_handler.erl
@@ -1168,23 +1168,30 @@ handle_event({call,From}, stop, StateName, D0) ->
{Repls,D} = send_replies(Replies, D0),
{stop_and_reply, normal, [{reply,From,ok}|Repls], D#data{connection_state=Connection}};
-
handle_event({call,_}, _, StateName, _) when not ?CONNECTED(StateName) ->
{keep_state_and_data, [postpone]};
handle_event({call,From}, {request, ChannelPid, ChannelId, Type, Data, Timeout}, StateName, D0)
when ?CONNECTED(StateName) ->
- D = handle_request(ChannelPid, ChannelId, Type, Data, true, From, D0),
- %% Note reply to channel will happen later when reply is recived from peer on the socket
- start_channel_request_timer(ChannelId, From, Timeout),
- {keep_state, cache_request_idle_timer_check(D)};
+ case handle_request(ChannelPid, ChannelId, Type, Data, true, From, D0) of
+ {error,Error} ->
+ {keep_state, D0, {reply,From,{error,Error}}};
+ D ->
+ %% Note reply to channel will happen later when reply is recived from peer on the socket
+ start_channel_request_timer(ChannelId, From, Timeout),
+ {keep_state, cache_request_idle_timer_check(D)}
+ end;
handle_event({call,From}, {request, ChannelId, Type, Data, Timeout}, StateName, D0)
when ?CONNECTED(StateName) ->
- D = handle_request(ChannelId, Type, Data, true, From, D0),
- %% Note reply to channel will happen later when reply is recived from peer on the socket
- start_channel_request_timer(ChannelId, From, Timeout),
- {keep_state, cache_request_idle_timer_check(D)};
+ case handle_request(ChannelId, Type, Data, true, From, D0) of
+ {error,Error} ->
+ {keep_state, D0, {reply,From,{error,Error}}};
+ D ->
+ %% Note reply to channel will happen later when reply is recived from peer on the socket
+ start_channel_request_timer(ChannelId, From, Timeout),
+ {keep_state, cache_request_idle_timer_check(D)}
+ end;
handle_event({call,From}, {data, ChannelId, Type, Data, Timeout}, StateName, D0)
when ?CONNECTED(StateName) ->
@@ -1442,38 +1449,43 @@ handle_event(Type, Ev, StateName, D) ->
-spec terminate(any(),
state_name(),
#data{}
- ) -> finalize_termination_result() .
+ ) -> term().
%% . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
terminate(normal, StateName, State) ->
- finalize_termination(StateName, State);
+ stop_subsystem(State),
+ close_transport(State);
terminate({shutdown,{init,Reason}}, StateName, State) ->
error_logger:info_report(io_lib:format("Erlang ssh in connection handler init: ~p~n",[Reason])),
- finalize_termination(StateName, State);
+ stop_subsystem(State),
+ close_transport(State);
terminate(shutdown, StateName, State0) ->
%% Terminated by supervisor
State = send_msg(#ssh_msg_disconnect{code = ?SSH_DISCONNECT_BY_APPLICATION,
- description = "Application shutdown"},
- State0),
- finalize_termination(StateName, State);
-
-%% terminate({shutdown,Msg}, StateName, State0) when is_record(Msg,ssh_msg_disconnect)->
-%% State = send_msg(Msg, State0),
-%% finalize_termination(StateName, Msg, State);
+ description = "Application shutdown"},
+ State0),
+ close_transport(State);
terminate({shutdown,_R}, StateName, State) ->
- finalize_termination(StateName, State);
+ %% Internal termination
+ stop_subsystem(State),
+ close_transport(State);
+
+terminate(kill, StateName, State) ->
+ stop_subsystem(State),
+ close_transport(State);
terminate(Reason, StateName, State0) ->
%% Others, e.g undef, {badmatch,_}
log_error(Reason),
State = send_msg(#ssh_msg_disconnect{code = ?SSH_DISCONNECT_BY_APPLICATION,
- description = "Internal error"},
+ description = "Internal error"},
State0),
- finalize_termination(StateName, State).
+ stop_subsystem(State),
+ close_transport(State).
%%--------------------------------------------------------------------
@@ -1548,21 +1560,25 @@ start_the_connection_child(UserPid, Role, Socket, Options0) ->
%%--------------------------------------------------------------------
%% Stopping
--type finalize_termination_result() :: ok .
-
-finalize_termination(_StateName, #data{transport_cb = Transport,
- connection_state = Connection,
- socket = Socket}) ->
- case Connection 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)),
+
+stop_subsystem(#data{connection_state =
+ #connection{system_supervisor = SysSup,
+ sub_system_supervisor = SubSysSup}}) when is_pid(SubSysSup) ->
+ ssh_system_sup:stop_subsystem(SysSup, SubSysSup);
+stop_subsystem(_) ->
ok.
+
+close_transport(#data{transport_cb = Transport,
+ socket = Socket}) ->
+ try
+ Transport:close(Socket)
+ of
+ _ -> ok
+ catch
+ _:_ -> ok
+ end.
+
%%--------------------------------------------------------------------
%% "Invert" the Role
peer_role(client) -> server;
@@ -1774,21 +1790,31 @@ is_usable_user_pubkey(A, Ssh) ->
%%%----------------------------------------------------------------
handle_request(ChannelPid, ChannelId, Type, Data, WantReply, From, D) ->
case ssh_channel:cache_lookup(cache(D), ChannelId) of
- #channel{remote_id = Id} = Channel ->
+ #channel{remote_id = Id,
+ sent_close = false} = Channel ->
update_sys(cache(D), Channel, Type, ChannelPid),
send_msg(ssh_connection:channel_request_msg(Id, Type, WantReply, Data),
add_request(WantReply, ChannelId, From, D));
- undefined ->
- D
+
+ _ when WantReply==true ->
+ {error,closed};
+
+ _ ->
+ D
end.
handle_request(ChannelId, Type, Data, WantReply, From, D) ->
case ssh_channel:cache_lookup(cache(D), ChannelId) of
- #channel{remote_id = Id} ->
+ #channel{remote_id = Id,
+ sent_close = false} ->
send_msg(ssh_connection:channel_request_msg(Id, Type, WantReply, Data),
add_request(WantReply, ChannelId, From, D));
- undefined ->
- D
+
+ _ when WantReply==true ->
+ {error,closed};
+
+ _ ->
+ D
end.
%%%----------------------------------------------------------------
diff --git a/lib/ssh/src/ssh_connection_sup.erl b/lib/ssh/src/ssh_connection_sup.erl
index 60ee8b7c73..2e8450090a 100644
--- a/lib/ssh/src/ssh_connection_sup.erl
+++ b/lib/ssh/src/ssh_connection_sup.erl
@@ -52,10 +52,7 @@ init(_) ->
},
ChildSpecs = [#{id => undefined, % As simple_one_for_one is used.
start => {ssh_connection_handler, start_link, []},
- restart => temporary,
- shutdown => 4000,
- type => worker,
- modules => [ssh_connection_handler]
+ restart => temporary % because there is no way to restart a crashed connection
}
],
{ok, {SupFlags,ChildSpecs}}.
diff --git a/lib/ssh/src/ssh_options.erl b/lib/ssh/src/ssh_options.erl
index 68c99743ee..c05293d1ae 100644
--- a/lib/ssh/src/ssh_options.erl
+++ b/lib/ssh/src/ssh_options.erl
@@ -268,17 +268,19 @@ default(server) ->
},
{shell, def} =>
- #{default => {shell, start, []},
+ #{default => ?DEFAULT_SHELL,
chk => fun({M,F,A}) -> is_atom(M) andalso is_atom(F) andalso is_list(A);
(V) -> check_function1(V) orelse check_function2(V)
end,
class => user_options
},
- {exec, def} => % FIXME: need some archeology....
+ {exec, def} =>
#{default => undefined,
- chk => fun({M,F,_}) -> is_atom(M) andalso is_atom(F);
- (V) -> is_function(V)
+ chk => fun({direct, V}) -> check_function1(V) orelse check_function2(V) orelse check_function3(V);
+ %% Compatibility (undocumented):
+ ({M,F,A}) -> is_atom(M) andalso is_atom(F) andalso is_list(A);
+ (V) -> check_function1(V) orelse check_function2(V) orelse check_function3(V)
end,
class => user_options
},
@@ -439,6 +441,12 @@ default(client) ->
class => user_options
},
+ {save_accepted_host, def} =>
+ #{default => true,
+ chk => fun erlang:is_boolean/1,
+ class => user_options
+ },
+
{pref_public_key_algs, def} =>
#{default => ssh_transport:default_algorithms(public_key),
chk => fun check_pref_public_key_algs/1,
diff --git a/lib/ssh/src/ssh_subsystem_sup.erl b/lib/ssh/src/ssh_subsystem_sup.erl
index 8db051095c..77da240a66 100644
--- a/lib/ssh/src/ssh_subsystem_sup.erl
+++ b/lib/ssh/src/ssh_subsystem_sup.erl
@@ -74,18 +74,14 @@ ssh_connection_child_spec(Role, Address, Port, _Profile, Options) ->
#{id => id(Role, ssh_connection_sup, Address, Port),
start => {ssh_connection_sup, start_link, [Options]},
restart => temporary,
- shutdown => 5000,
- type => supervisor,
- modules => [ssh_connection_sup]
+ type => supervisor
}.
ssh_channel_child_spec(Role, Address, Port, _Profile, Options) ->
#{id => id(Role, ssh_channel_sup, Address, Port),
start => {ssh_channel_sup, start_link, [Options]},
restart => temporary,
- shutdown => infinity,
- type => supervisor,
- modules => [ssh_channel_sup]
+ type => supervisor
}.
id(Role, Sup, Address, Port) ->
diff --git a/lib/ssh/src/ssh_sup.erl b/lib/ssh/src/ssh_sup.erl
index eaec7a54e4..8183016ba5 100644
--- a/lib/ssh/src/ssh_sup.erl
+++ b/lib/ssh/src/ssh_sup.erl
@@ -36,15 +36,14 @@ init(_) ->
intensity => 10,
period => 3600
},
- ChildSpecs = [#{id => Module,
- start => {Module, start_link, []},
- restart => permanent,
- shutdown => 4000, %brutal_kill,
- type => supervisor,
- modules => [Module]
+ ChildSpecs = [#{id => sshd_sup,
+ start => {sshd_sup, start_link, []},
+ type => supervisor
+ },
+ #{id => sshc_sup,
+ start => {sshc_sup, start_link, []},
+ type => supervisor
}
- || Module <- [sshd_sup,
- sshc_sup]
],
{ok, {SupFlags,ChildSpecs}}.
diff --git a/lib/ssh/src/ssh_system_sup.erl b/lib/ssh/src/ssh_system_sup.erl
index e70abf59c2..469f9560e9 100644
--- a/lib/ssh/src/ssh_system_sup.erl
+++ b/lib/ssh/src/ssh_system_sup.erl
@@ -63,9 +63,7 @@ init([Address, Port, Profile, Options]) ->
[#{id => id(ssh_acceptor_sup, Address, Port, Profile),
start => {ssh_acceptor_sup, start_link, [Address, Port, Profile, Options]},
restart => transient,
- shutdown => infinity,
- type => supervisor,
- modules => [ssh_acceptor_sup]
+ type => supervisor
}];
_ ->
[]
@@ -90,11 +88,11 @@ stop_listener(Address, Port, Profile) ->
stop_system(SysSup) ->
- spawn(fun() -> sshd_sup:stop_child(SysSup) end),
+ catch sshd_sup:stop_child(SysSup),
ok.
stop_system(Address, Port, Profile) ->
- spawn(fun() -> sshd_sup:stop_child(Address, Port, Profile) end),
+ catch sshd_sup:stop_child(Address, Port, Profile),
ok.
@@ -124,9 +122,8 @@ start_subsystem(SystemSup, Role, Address, Port, Profile, Options) ->
#{id => make_ref(),
start => {ssh_subsystem_sup, start_link, [Role, Address, Port, Profile, Options]},
restart => temporary,
- shutdown => infinity,
- type => supervisor,
- modules => [ssh_subsystem_sup]},
+ type => supervisor
+ },
supervisor:start_child(SystemSup, SubsystemSpec).
stop_subsystem(SystemSup, SubSys) ->
diff --git a/lib/ssh/src/ssh_transport.erl b/lib/ssh/src/ssh_transport.erl
index ad9efc4755..975053d301 100644
--- a/lib/ssh/src/ssh_transport.erl
+++ b/lib/ssh/src/ssh_transport.erl
@@ -889,10 +889,13 @@ known_host_key(#ssh{opts = Opts, key_cb = {KeyCb,KeyCbOpts}, peer = {PeerName,_}
{_,true} ->
ok;
{_,false} ->
+ DoAdd = ?GET_OPT(save_accepted_host, Opts),
case accepted_host(Ssh, PeerName, Public, Opts) of
- true ->
+ true when DoAdd == true ->
{_,R} = add_host_key(KeyCb, PeerName, Public, [{key_cb_private,KeyCbOpts}|UserOpts]),
R;
+ true when DoAdd == false ->
+ ok;
false ->
{error, rejected_by_user};
{error,E} ->
diff --git a/lib/ssh/src/sshc_sup.erl b/lib/ssh/src/sshc_sup.erl
index 133b2c6450..f4b39dbbdc 100644
--- a/lib/ssh/src/sshc_sup.erl
+++ b/lib/ssh/src/sshc_sup.erl
@@ -27,7 +27,7 @@
-behaviour(supervisor).
--export([start_link/0, start_child/1, stop_child/1]).
+-export([start_link/0, start_child/1]).
%% Supervisor callback
-export([init/1]).
@@ -43,13 +43,6 @@ start_link() ->
start_child(Args) ->
supervisor:start_child(?MODULE, Args).
-stop_child(Client) ->
- spawn(fun() ->
- ClientSup = whereis(?SSHC_SUP),
- supervisor:terminate_child(ClientSup, Client)
- end),
- ok.
-
%%%=========================================================================
%%% Supervisor callback
%%%=========================================================================
@@ -60,10 +53,7 @@ init(_) ->
},
ChildSpecs = [#{id => undefined, % As simple_one_for_one is used.
start => {ssh_connection_handler, start_link, []},
- restart => temporary,
- shutdown => 4000,
- type => worker,
- modules => [ssh_connection_handler]
+ restart => temporary % because there is no way to restart a crashed connection
}
],
{ok, {SupFlags,ChildSpecs}}.
diff --git a/lib/ssh/src/sshd_sup.erl b/lib/ssh/src/sshd_sup.erl
index c23e65d955..779a861a54 100644
--- a/lib/ssh/src/sshd_sup.erl
+++ b/lib/ssh/src/sshd_sup.erl
@@ -90,10 +90,8 @@ init(_) ->
child_spec(Address, Port, Profile, Options) ->
#{id => id(Address, Port, Profile),
start => {ssh_system_sup, start_link, [Address, Port, Profile, Options]},
- restart => temporary,
- shutdown => infinity,
- type => supervisor,
- modules => [ssh_system_sup]
+ restart => temporary,
+ type => supervisor
}.
id(Address, Port, Profile) ->
diff --git a/lib/ssh/test/Makefile b/lib/ssh/test/Makefile
index 21359a0386..4d84b6c6b6 100644
--- a/lib/ssh/test/Makefile
+++ b/lib/ssh/test/Makefile
@@ -34,7 +34,6 @@ VSN=$(GS_VSN)
MODULES= \
ssh_algorithms_SUITE \
ssh_options_SUITE \
- ssh_renegotiate_SUITE \
ssh_basic_SUITE \
ssh_bench_SUITE \
ssh_compat_SUITE \
diff --git a/lib/ssh/test/ssh_basic_SUITE.erl b/lib/ssh/test/ssh_basic_SUITE.erl
index 202b0afe57..d3f93c7382 100644
--- a/lib/ssh/test/ssh_basic_SUITE.erl
+++ b/lib/ssh/test/ssh_basic_SUITE.erl
@@ -28,60 +28,12 @@
-include("ssh_test_lib.hrl").
%% Note: This directive should only be used in test suites.
-%%-compile(export_all).
-
-%%% Test cases
--export([
- app_test/1,
- appup_test/1,
- cli/1,
- close/1,
- daemon_already_started/1,
- daemon_opt_fd/1,
- multi_daemon_opt_fd/1,
- double_close/1,
- exec/1,
- exec_compressed/1,
- exec_key_differs1/1,
- exec_key_differs2/1,
- exec_key_differs3/1,
- exec_key_differs_fail/1,
- fail_daemon_start/1,
- idle_time_client/1,
- idle_time_server/1,
- inet6_option/1,
- inet_option/1,
- internal_error/1,
- known_hosts/1,
- login_bad_pwd_no_retry1/1,
- login_bad_pwd_no_retry2/1,
- login_bad_pwd_no_retry3/1,
- login_bad_pwd_no_retry4/1,
- login_bad_pwd_no_retry5/1,
- misc_ssh_options/1,
- openssh_zlib_basic_test/1,
- packet_size_zero/1,
- pass_phrase/1,
- peername_sockname/1,
- send/1,
- shell/1,
- shell_no_unicode/1,
- shell_unicode_string/1,
- ssh_info_print/1,
- key_callback/1,
- key_callback_options/1,
- shell_exit_status/1
- ]).
-
-%%% Common test callbacks
--export([suite/0, all/0, groups/0,
- init_per_suite/1, end_per_suite/1,
- init_per_group/2, end_per_group/2,
- init_per_testcase/2, end_per_testcase/2
- ]).
+-compile(export_all).
-define(NEWLINE, <<"\r\n">>).
+-define(REKEY_DATA_TMO, 65000).
+
%%--------------------------------------------------------------------
%% Common Test interface functions -----------------------------------
%%--------------------------------------------------------------------
@@ -91,76 +43,97 @@ suite() ->
{timetrap,{seconds,40}}].
all() ->
- [app_test,
- appup_test,
- {group, dsa_key},
- {group, rsa_key},
- {group, ecdsa_sha2_nistp256_key},
- {group, ecdsa_sha2_nistp384_key},
- {group, ecdsa_sha2_nistp521_key},
- {group, dsa_pass_key},
- {group, rsa_pass_key},
- {group, ecdsa_sha2_nistp256_pass_key},
- {group, ecdsa_sha2_nistp384_pass_key},
- {group, ecdsa_sha2_nistp521_pass_key},
- {group, host_user_key_differs},
- {group, key_cb},
- {group, internal_error},
- {group, rsa_host_key_is_actualy_ecdsa},
- daemon_already_started,
- double_close,
- daemon_opt_fd,
- multi_daemon_opt_fd,
- packet_size_zero,
- ssh_info_print,
- {group, login_bad_pwd_no_retry},
- shell_exit_status
- ].
+ [{group, all_tests}].
+
groups() ->
- [{dsa_key, [], basic_tests()},
- {rsa_key, [], basic_tests()},
- {ecdsa_sha2_nistp256_key, [], basic_tests()},
- {ecdsa_sha2_nistp384_key, [], basic_tests()},
- {ecdsa_sha2_nistp521_key, [], basic_tests()},
+ [{all_tests, [parallel], [{group, ssh_renegotiate_SUITE},
+ {group, ssh_basic_SUITE}
+ ]},
+ {ssh_basic_SUITE, [], [app_test,
+ appup_test,
+ {group, dsa_key},
+ {group, rsa_key},
+ {group, ecdsa_sha2_nistp256_key},
+ {group, ecdsa_sha2_nistp384_key},
+ {group, ecdsa_sha2_nistp521_key},
+ {group, dsa_pass_key},
+ {group, rsa_pass_key},
+ {group, ecdsa_sha2_nistp256_pass_key},
+ {group, ecdsa_sha2_nistp384_pass_key},
+ {group, ecdsa_sha2_nistp521_pass_key},
+ {group, host_user_key_differs},
+ {group, key_cb},
+ {group, internal_error},
+ {group, rsa_host_key_is_actualy_ecdsa},
+ daemon_already_started,
+ double_close,
+ daemon_opt_fd,
+ multi_daemon_opt_fd,
+ packet_size,
+ ssh_info_print,
+ {group, login_bad_pwd_no_retry},
+ shell_exit_status
+ ]},
+
+ {ssh_renegotiate_SUITE, [parallel], [rekey,
+ rekey_limit,
+ renegotiate1,
+ renegotiate2]},
+
+ {dsa_key, [], [{group, basic}]},
+ {rsa_key, [], [{group, basic}]},
+ {ecdsa_sha2_nistp256_key, [], [{group, basic}]},
+ {ecdsa_sha2_nistp384_key, [], [{group, basic}]},
+ {ecdsa_sha2_nistp521_key, [], [{group, basic}]},
{rsa_host_key_is_actualy_ecdsa, [], [fail_daemon_start]},
- {host_user_key_differs, [], [exec_key_differs1,
- exec_key_differs2,
- exec_key_differs3,
- exec_key_differs_fail]},
+ {host_user_key_differs, [parallel], [exec_key_differs1,
+ exec_key_differs2,
+ exec_key_differs3,
+ exec_key_differs_fail]},
{dsa_pass_key, [], [pass_phrase]},
{rsa_pass_key, [], [pass_phrase]},
{ecdsa_sha2_nistp256_pass_key, [], [pass_phrase]},
{ecdsa_sha2_nistp384_pass_key, [], [pass_phrase]},
{ecdsa_sha2_nistp521_pass_key, [], [pass_phrase]},
- {key_cb, [], [key_callback, key_callback_options]},
+ {key_cb, [parallel], [key_callback, key_callback_options]},
{internal_error, [], [internal_error]},
- {login_bad_pwd_no_retry, [], [login_bad_pwd_no_retry1,
- login_bad_pwd_no_retry2,
- login_bad_pwd_no_retry3,
- login_bad_pwd_no_retry4,
- login_bad_pwd_no_retry5
- ]}
+ {login_bad_pwd_no_retry, [parallel], [login_bad_pwd_no_retry1,
+ login_bad_pwd_no_retry2,
+ login_bad_pwd_no_retry3,
+ login_bad_pwd_no_retry4,
+ login_bad_pwd_no_retry5
+ ]},
+
+ {basic, [], [{group,p_basic},
+ close,
+ known_hosts
+ ]},
+ {p_basic, [parallel], [send, peername_sockname,
+ exec, exec_compressed,
+ shell, shell_no_unicode, shell_unicode_string,
+ cli,
+ idle_time_client, idle_time_server, openssh_zlib_basic_test,
+ misc_ssh_options, inet_option, inet6_option]}
].
-basic_tests() ->
- [send, close, peername_sockname,
- exec, exec_compressed,
- shell, shell_no_unicode, shell_unicode_string,
- cli, known_hosts,
- idle_time_client, idle_time_server, openssh_zlib_basic_test,
- misc_ssh_options, inet_option, inet6_option].
+
%%--------------------------------------------------------------------
init_per_suite(Config) ->
- ?CHECK_CRYPTO(Config).
+ ?CHECK_CRYPTO(begin
+ ssh:start(),
+ Config
+ end).
end_per_suite(_Config) ->
ssh:stop().
%%--------------------------------------------------------------------
+init_per_group(ssh_renegotiate_SUITE, Config) ->
+ [{preferred_algorithms, ssh:default_algorithms()} | Config];
init_per_group(dsa_key, Config) ->
case lists:member('ssh-dss',
ssh_transport:default_algorithms(public_key)) of
@@ -414,7 +387,6 @@ init_per_testcase(TC, Config) when TC==shell_no_unicode ;
PrivDir = proplists:get_value(priv_dir, Config),
UserDir = proplists:get_value(priv_dir, Config),
SysDir = proplists:get_value(data_dir, Config),
- ssh:start(),
Sftpd = {_Pid, _Host, Port} =
ssh_test_lib:daemon([{system_dir, SysDir},
{user_dir, PrivDir},
@@ -437,7 +409,6 @@ init_per_testcase(inet6_option, Config) ->
{skip,"No ipv6 interface address"}
end;
init_per_testcase(_TestCase, Config) ->
- ssh:start(),
Config.
end_per_testcase(TestCase, Config) when TestCase == server_password_option;
@@ -458,7 +429,6 @@ end_per_testcase(_TestCase, Config) ->
end_per_testcase(Config).
end_per_testcase(_Config) ->
- ssh:stop(),
ok.
%%--------------------------------------------------------------------
@@ -480,8 +450,8 @@ misc_ssh_options(Config) when is_list(Config) ->
SystemDir = filename:join(proplists:get_value(priv_dir, Config), system),
UserDir = proplists:get_value(priv_dir, Config),
- CMiscOpt0 = [{connect_timeout, 1000}, {user_dir, UserDir}],
- CMiscOpt1 = [{connect_timeout, infinity}, {user_dir, UserDir}],
+ CMiscOpt0 = [{connect_timeout, 1000}, {user_dir, UserDir}, {silently_accept_hosts, true}],
+ CMiscOpt1 = [{connect_timeout, infinity}, {user_dir, UserDir}, {silently_accept_hosts, true}],
SMiscOpt0 = [{user_dir, UserDir}, {system_dir, SystemDir}],
SMiscOpt1 = [{user_dir, UserDir}, {system_dir, SystemDir}],
@@ -764,11 +734,11 @@ cli(Config) when is_list(Config) ->
{ok, ChannelId} = ssh_connection:session_channel(ConnectionRef, infinity),
ssh_connection:shell(ConnectionRef, ChannelId),
- ok = ssh_connection:send(ConnectionRef, ChannelId, <<"q">>),
+ ssh_connection:send(ConnectionRef, ChannelId, <<"q">>),
receive
{ssh_cm, ConnectionRef,
{data,0,0, <<"\r\nYou are accessing a dummy, type \"q\" to exit\r\n\n">>}} ->
- ok = ssh_connection:send(ConnectionRef, ChannelId, <<"q">>)
+ ssh_connection:send(ConnectionRef, ChannelId, <<"q">>)
after
30000 -> ct:fail("timeout ~p:~p",[?MODULE,?LINE])
end,
@@ -1104,7 +1074,7 @@ multi_daemon_opt_fd(Config) ->
end || {S,Pid,C} <- Tests].
%%--------------------------------------------------------------------
-packet_size_zero(Config) ->
+packet_size(Config) ->
SystemDir = proplists:get_value(data_dir, Config),
PrivDir = proplists:get_value(priv_dir, Config),
UserDir = filename:join(PrivDir, nopubkey), % to make sure we don't use public-key-auth
@@ -1119,21 +1089,36 @@ packet_size_zero(Config) ->
{user_interaction, false},
{user, "vego"},
{password, "morot"}]),
-
- {ok,Chan} = ssh_connection:session_channel(Conn, 1000, _MaxPacketSize=0, 60000),
- ok = ssh_connection:shell(Conn, Chan),
+ lists:foreach(
+ fun(MaxPacketSize) ->
+ ct:log("Try max_packet_size=~p",[MaxPacketSize]),
+ {ok,Ch} = ssh_connection:session_channel(Conn, 1000, MaxPacketSize, 60000),
+ ok = ssh_connection:shell(Conn, Ch),
+ rec(Server, Conn, Ch, MaxPacketSize),
+ ssh_connection:close(Conn, Ch)
+ end, [0, 1, 10, 25]),
ssh:close(Conn),
ssh:stop_daemon(Server),
+ ok.
+
+rec(Server, Conn, Ch, MaxSz) ->
receive
- {ssh_cm,Conn,{data,Chan,_Type,_Msg1}} = M ->
- ct:log("Got ~p",[M]),
- ct:fail(doesnt_obey_max_packet_size_0)
- after 5000 ->
- ok
- end.
-
+ {ssh_cm,Conn,{data,Ch,_,M}} when size(M) =< MaxSz ->
+ ct:log("~p: ~p",[MaxSz,M]),
+ rec(Server, Conn, Ch, MaxSz);
+ {ssh_cm,Conn,{data,Ch,_,_}} = M ->
+ ct:log("Max pkt size=~p. Got ~p",[MaxSz,M]),
+ ssh:close(Conn),
+ ssh:stop_daemon(Server),
+ ct:fail("Does not obey max_packet_size=~p",[MaxSz])
+ after
+ 2000 ->
+ ct:log("~p: ok!",[MaxSz]),
+ ok
+ end.
+
%%--------------------------------------------------------------------
shell_no_unicode(Config) ->
new_do_shell(proplists:get_value(io,Config),
@@ -1340,6 +1325,156 @@ shell_exit_status(Config) when is_list(Config) ->
ssh:stop_daemon(Pid).
+%%% Idle timeout test
+rekey() -> [{timetrap,{seconds,90}}].
+
+rekey(Config) ->
+ {Pid, Host, Port} =
+ ssh_test_lib:std_daemon(Config,
+ [{rekey_limit, 0}]),
+ ConnectionRef =
+ ssh_test_lib:std_connect(Config, Host, Port,
+ [{rekey_limit, 0}]),
+ Kex1 = ssh_test_lib:get_kex_init(ConnectionRef),
+ receive
+ after ?REKEY_DATA_TMO ->
+ %%By this time rekeying would have been done
+ Kex2 = ssh_test_lib:get_kex_init(ConnectionRef),
+ false = (Kex2 == Kex1),
+ ssh:close(ConnectionRef),
+ ssh:stop_daemon(Pid)
+ end.
+
+%%--------------------------------------------------------------------
+
+%%% Test rekeying by data volume
+
+rekey_limit() -> [{timetrap,{seconds,400}}].
+
+rekey_limit(Config) ->
+ UserDir = proplists:get_value(priv_dir, Config),
+ DataFile = filename:join(UserDir, "rekey.data"),
+
+ Algs = proplists:get_value(preferred_algorithms, Config),
+ {Pid, Host, Port} = ssh_test_lib:std_daemon(Config,[{max_random_length_padding,0},
+ {preferred_algorithms,Algs}]),
+
+ ConnectionRef = ssh_test_lib:std_connect(Config, Host, Port, [{rekey_limit, 6000},
+ {max_random_length_padding,0}]),
+ {ok, SftpPid} = ssh_sftp:start_channel(ConnectionRef),
+
+ Kex1 = ssh_test_lib:get_kex_init(ConnectionRef),
+
+ timer:sleep(?REKEY_DATA_TMO),
+ Kex1 = ssh_test_lib:get_kex_init(ConnectionRef),
+
+ Data = lists:duplicate(159000,1),
+ ok = ssh_sftp:write_file(SftpPid, DataFile, Data),
+
+ timer:sleep(?REKEY_DATA_TMO),
+ Kex2 = ssh_test_lib:get_kex_init(ConnectionRef),
+
+ false = (Kex2 == Kex1),
+
+ timer:sleep(?REKEY_DATA_TMO),
+ Kex2 = ssh_test_lib:get_kex_init(ConnectionRef),
+
+ ok = ssh_sftp:write_file(SftpPid, DataFile, "hi\n"),
+
+ timer:sleep(?REKEY_DATA_TMO),
+ Kex2 = ssh_test_lib:get_kex_init(ConnectionRef),
+
+ false = (Kex2 == Kex1),
+
+ timer:sleep(?REKEY_DATA_TMO),
+ Kex2 = ssh_test_lib:get_kex_init(ConnectionRef),
+
+ ssh_sftp:stop_channel(SftpPid),
+ ssh:close(ConnectionRef),
+ ssh:stop_daemon(Pid).
+
+%%--------------------------------------------------------------------
+
+%%% Test rekeying with simulataneous send request
+
+renegotiate1(Config) ->
+ UserDir = proplists:get_value(priv_dir, Config),
+ DataFile = filename:join(UserDir, "renegotiate1.data"),
+
+ Algs = proplists:get_value(preferred_algorithms, Config),
+ {Pid, Host, DPort} = ssh_test_lib:std_daemon(Config,[{max_random_length_padding,0},
+ {preferred_algorithms,Algs}]),
+
+ RPort = ssh_test_lib:inet_port(),
+ {ok,RelayPid} = ssh_relay:start_link({0,0,0,0}, RPort, Host, DPort),
+
+
+ ConnectionRef = ssh_test_lib:std_connect(Config, Host, RPort, [{max_random_length_padding,0}]),
+ {ok, SftpPid} = ssh_sftp:start_channel(ConnectionRef),
+
+ Kex1 = ssh_test_lib:get_kex_init(ConnectionRef),
+
+ {ok, Handle} = ssh_sftp:open(SftpPid, DataFile, [write]),
+
+ ok = ssh_sftp:write(SftpPid, Handle, "hi\n"),
+
+ ssh_relay:hold(RelayPid, rx, 20, 1000),
+ ssh_connection_handler:renegotiate(ConnectionRef),
+ spawn(fun() -> ok=ssh_sftp:write(SftpPid, Handle, "another hi\n") end),
+
+ timer:sleep(2000),
+
+ Kex2 = ssh_test_lib:get_kex_init(ConnectionRef),
+
+ false = (Kex2 == Kex1),
+
+ ssh_relay:stop(RelayPid),
+ ssh_sftp:stop_channel(SftpPid),
+ ssh:close(ConnectionRef),
+ ssh:stop_daemon(Pid).
+
+%%--------------------------------------------------------------------
+
+%%% Test rekeying with inflight messages from peer
+
+renegotiate2(Config) ->
+ UserDir = proplists:get_value(priv_dir, Config),
+ DataFile = filename:join(UserDir, "renegotiate2.data"),
+
+ Algs = proplists:get_value(preferred_algorithms, Config),
+ {Pid, Host, DPort} = ssh_test_lib:std_daemon(Config,[{max_random_length_padding,0},
+ {preferred_algorithms,Algs}]),
+
+ RPort = ssh_test_lib:inet_port(),
+ {ok,RelayPid} = ssh_relay:start_link({0,0,0,0}, RPort, Host, DPort),
+
+ ConnectionRef = ssh_test_lib:std_connect(Config, Host, RPort, [{max_random_length_padding,0}]),
+ {ok, SftpPid} = ssh_sftp:start_channel(ConnectionRef),
+
+ Kex1 = ssh_test_lib:get_kex_init(ConnectionRef),
+
+ {ok, Handle} = ssh_sftp:open(SftpPid, DataFile, [write]),
+
+ ok = ssh_sftp:write(SftpPid, Handle, "hi\n"),
+
+ ssh_relay:hold(RelayPid, rx, 20, infinity),
+ spawn(fun() -> ok=ssh_sftp:write(SftpPid, Handle, "another hi\n") end),
+ %% need a small pause here to ensure ssh_sftp:write is executed
+ ct:sleep(10),
+ ssh_connection_handler:renegotiate(ConnectionRef),
+ ssh_relay:release(RelayPid, rx),
+
+ timer:sleep(2000),
+
+ Kex2 = ssh_test_lib:get_kex_init(ConnectionRef),
+
+ false = (Kex2 == Kex1),
+
+ ssh_relay:stop(RelayPid),
+ ssh_sftp:stop_channel(SftpPid),
+ ssh:close(ConnectionRef),
+ ssh:stop_daemon(Pid).
+
%%--------------------------------------------------------------------
%% Internal functions ------------------------------------------------
%%--------------------------------------------------------------------
@@ -1491,7 +1626,7 @@ new_do_shell(IO, N, Ops=[{Order,Arg}|More]) ->
ct:fail("*** Expected ~p, but got ~p",[string:strip(ExpStr),RecStr])
end
after 30000 ->
- ct:log("Meassage queue of ~p:~n~p",
+ ct:log("Message queue of ~p:~n~p",
[self(), erlang:process_info(self(), messages)]),
case Order of
expect -> ct:fail("timeout, expected ~p",[string:strip(Arg)]);
diff --git a/lib/ssh/test/ssh_compat_SUITE.erl b/lib/ssh/test/ssh_compat_SUITE.erl
index 74ab5aca3a..f7eda1dc08 100644
--- a/lib/ssh/test/ssh_compat_SUITE.erl
+++ b/lib/ssh/test/ssh_compat_SUITE.erl
@@ -32,7 +32,8 @@
-compile(export_all).
-define(USER,"sshtester").
--define(PWD, "foobar").
+-define(PASSWD, "foobar").
+-define(BAD_PASSWD, "NOT-"?PASSWD).
-define(DOCKER_PFX, "ssh_compat_suite-ssh").
%%--------------------------------------------------------------------
@@ -44,25 +45,22 @@ suite() ->
{timetrap,{seconds,40}}].
all() ->
- [{group,G} || G <- vers()].
+%% [check_docker_present] ++
+ [{group,G} || G <- ssh_image_versions()].
groups() ->
- [{G, [], tests()} || G <- vers()].
-
-tests() ->
- [login_with_password_otp_is_client,
- login_with_password_otp_is_server,
- login_with_keyboard_interactive_otp_is_client,
- login_with_keyboard_interactive_otp_is_server,
- login_with_all_public_keys_otp_is_client,
- login_with_all_public_keys_otp_is_server,
- all_algorithms_otp_is_client,
- all_algorithms_otp_is_server
+ [{otp_client, [], [login_otp_is_client,
+ all_algorithms_sftp_exec_reneg_otp_is_client,
+ send_recv_big_with_renegotiate_otp_is_client
+ ]},
+ {otp_server, [], [login_otp_is_server,
+ all_algorithms_sftp_exec_reneg_otp_is_server
+ ]} |
+ [{G, [], [{group,otp_client}, {group,otp_server}]} || G <- ssh_image_versions()]
].
-
-vers() ->
+ssh_image_versions() ->
try
%% Find all useful containers in such a way that undefined command, too low
%% priviliges, no containers and containers found give meaningful result:
@@ -94,28 +92,60 @@ end_per_suite(Config) ->
%%% os:cmd("docker rm $(docker ps -aq -f status=exited)"),
%% Remove dangling images:
%%% os:cmd("docker rmi $(docker images -f dangling=true -q)"),
+ catch ssh:stop(),
Config.
+init_per_group(otp_server, Config) ->
+ case proplists:get_value(common_remote_client_algs, Config) of
+ undefined ->
+ SSHver = proplists:get_value(ssh_version, Config, ""),
+ {skip,"No "++SSHver++ " client found in docker"};
+ _ ->
+ Config
+ end;
+
+init_per_group(otp_client, Config) ->
+ Config;
-init_per_group(G, Config) ->
- case lists:member(G, vers()) of
+init_per_group(G, Config0) ->
+ case lists:member(G, ssh_image_versions()) of
true ->
+ %% This group is for one of the images
+ Vssh = atom_to_list(G),
+ Cmnt = io_lib:format("+++ ~s +++",[Vssh]),
+ ct:comment("~s",[Cmnt]),
try start_docker(G) of
{ok,ID} ->
- ct:log("==> ~p",[G]),
- [Vssh|VsslRest] = string:tokens(atom_to_list(G), "-"),
- Vssl = lists:flatten(lists:join($-,VsslRest)),
- ct:comment("+++ ~s + ~s +++",[Vssh,Vssl]),
+ ct:log("==> ~p started",[G]),
%% Find the algorithms that both client and server supports:
{IP,Port} = ip_port([{id,ID}]),
- try common_algs([{id,ID}|Config], IP, Port) of
- {ok, RemoteServerCommon, RemoteClientCommon} ->
- [{ssh_version,Vssh},{ssl_version,Vssl},
- {id,ID},
- {common_server_algs,RemoteServerCommon},
- {common_client_algs,RemoteClientCommon}
- |Config];
+ ct:log("Try contact ~p:~p",[IP,Port]),
+ Config1 = [{id,ID},
+ {ssh_version,Vssh}
+ | Config0],
+ try common_algs(Config1, IP, Port) of
+ {ok, ServerHello, RemoteServerCommon, ClientHello, RemoteClientCommon} ->
+ case chk_hellos([ServerHello,ClientHello], Cmnt) of
+ Cmnt ->
+ ok;
+ NewCmnt ->
+ ct:comment("~s",[NewCmnt])
+ end,
+ AuthMethods =
+ %% This should be obtained by quering the peer, but that
+ %% is a bit hard. It is possible with ssh_protocol_SUITE
+ %% techniques, but it can wait.
+ case Vssh of
+ "dropbear" ++ _ ->
+ [password, publickey];
+ _ ->
+ [password, 'keyboard-interactive', publickey]
+ end,
+ [{common_remote_server_algs,RemoteServerCommon},
+ {common_remote_client_algs,RemoteClientCommon},
+ {common_authmethods,AuthMethods}
+ |Config1];
Other ->
ct:log("Error in init_per_group: ~p",[Other]),
stop_docker(ID),
@@ -138,188 +168,301 @@ init_per_group(G, Config) ->
end;
false ->
- Config
+ Config0
end.
-end_per_group(_, Config) ->
- catch stop_docker(proplists:get_value(id,Config)),
- Config.
+end_per_group(G, Config) ->
+ case lists:member(G, ssh_image_versions()) of
+ true ->
+ catch stop_docker(proplists:get_value(id,Config));
+ false ->
+ ok
+ end.
%%--------------------------------------------------------------------
%% Test Cases --------------------------------------------------------
%%--------------------------------------------------------------------
-login_with_password_otp_is_client(Config) ->
- {IP,Port} = ip_port(Config),
- {ok,C} = ssh:connect(IP, Port, [{auth_methods,"password"},
- {user,?USER},
- {password,?PWD},
- {user_dir, new_dir(Config)},
- {silently_accept_hosts,true},
- {user_interaction,false}
- ]),
- ssh:close(C).
-
-%%--------------------------------------------------------------------
-login_with_password_otp_is_server(Config) ->
- {Server, Host, HostPort} =
- ssh_test_lib:daemon(0,
- [{auth_methods,"password"},
- {system_dir, setup_local_hostdir('ssh-rsa',Config)},
- {user_dir, new_dir(Config)},
- {user_passwords, [{?USER,?PWD}]},
- {failfun, fun ssh_test_lib:failfun/2}
- ]),
- R = exec_from_docker(Config, Host, HostPort,
- "'lists:concat([\"Answer=\",1+2]).\r\n'",
- [<<"Answer=3">>],
- ""),
- ssh:stop_daemon(Server),
- R.
-
-%%--------------------------------------------------------------------
-login_with_keyboard_interactive_otp_is_client(Config) ->
- {DockerIP,DockerPort} = ip_port(Config),
- {ok,C} = ssh:connect(DockerIP, DockerPort,
- [{auth_methods,"keyboard-interactive"},
- {user,?USER},
- {password,?PWD},
- {user_dir, new_dir(Config)},
- {silently_accept_hosts,true},
- {user_interaction,false}
- ]),
- ssh:close(C).
-
-%%--------------------------------------------------------------------
-login_with_keyboard_interactive_otp_is_server(Config) ->
- {Server, Host, HostPort} =
- ssh_test_lib:daemon(0,
- [{auth_methods,"keyboard-interactive"},
- {system_dir, setup_local_hostdir('ssh-rsa',Config)},
- {user_dir, new_dir(Config)},
- {user_passwords, [{?USER,?PWD}]},
- {failfun, fun ssh_test_lib:failfun/2}
- ]),
- R = exec_from_docker(Config, Host, HostPort,
- "'lists:concat([\"Answer=\",1+3]).\r\n'",
- [<<"Answer=4">>],
- ""),
- ssh:stop_daemon(Server),
- R.
+check_docker_present(_Config) ->
+ ct:log("This testcase is just to show in Monitor that we have a test host with docker installed",[]),
+ {fail, "Test is OK: just showing docker is available"}.
%%--------------------------------------------------------------------
-login_with_all_public_keys_otp_is_client(Config) ->
- CommonAlgs = [{public_key_from_host,A}
- || {public_key,A} <- proplists:get_value(common_server_algs, Config)],
- {DockerIP,DockerPort} = ip_port(Config),
- chk_all_algos(CommonAlgs, Config,
- fun(_Tag,Alg) ->
- ssh:connect(DockerIP, DockerPort,
- [{auth_methods, "publickey"},
- {user, ?USER},
- {user_dir, setup_remote_auth_keys_and_local_priv(Alg, Config)},
- {silently_accept_hosts,true},
- {user_interaction,false}
- ])
+login_otp_is_client(Config) ->
+ {IP,Port} = ip_port(Config),
+ PublicKeyAlgs = [A || {public_key,A} <- proplists:get_value(common_remote_server_algs, Config)],
+ CommonAuths =
+ [{AuthMethod,Alg} || AuthMethod <- proplists:get_value(common_authmethods, Config),
+ Alg <- case AuthMethod of
+ publickey ->
+ PublicKeyAlgs;
+ _ ->
+ [' ']
+ end
+ ],
+
+ chk_all_algos(?FUNCTION_NAME, CommonAuths, Config,
+ fun(AuthMethod,Alg) ->
+ {Opts,Dir} =
+ case AuthMethod of
+ publickey ->
+ {[], setup_remote_auth_keys_and_local_priv(Alg, Config)};
+ _ ->
+ {[{password,?PASSWD}], new_dir(Config)}
+ end,
+ ssh:connect(IP, Port, [{auth_methods, atom_to_list(AuthMethod)},
+ {user,?USER},
+ {user_dir, Dir},
+ {silently_accept_hosts,true},
+ {user_interaction,false}
+ | Opts
+ ])
end).
+
%%--------------------------------------------------------------------
-login_with_all_public_keys_otp_is_server(Config) ->
- CommonAlgs = [{public_key_to_host,A}
- || {public_key,A} <- proplists:get_value(common_client_algs, Config)],
- UserDir = new_dir(Config),
- {Server, Host, HostPort} =
- ssh_test_lib:daemon(0,
- [{auth_methods, "publickey"},
- {system_dir, setup_local_hostdir('ssh-rsa',Config)},
- {user_dir, UserDir},
- {user_passwords, [{?USER,?PWD}]},
- {failfun, fun ssh_test_lib:failfun/2}
- ]),
-
- R = chk_all_algos(CommonAlgs, Config,
- fun(_Tag,Alg) ->
- setup_remote_priv_and_local_auth_keys(Alg, clear_dir(UserDir), Config),
- exec_from_docker(Config, Host, HostPort,
- "'lists:concat([\"Answer=\",1+4]).\r\n'",
- [<<"Answer=5">>],
- "")
- end),
- ssh:stop_daemon(Server),
- R.
+login_otp_is_server(Config) ->
+ PublicKeyAlgs = [A || {public_key,A} <- proplists:get_value(common_remote_client_algs, Config)],
+ CommonAuths =
+ [{AuthMethod,Alg} || AuthMethod <- proplists:get_value(common_authmethods, Config),
+ Alg <- case AuthMethod of
+ publickey ->
+ PublicKeyAlgs;
+ _ ->
+ [' ']
+ end
+ ],
+ SysDir = setup_local_hostdir(hd(PublicKeyAlgs), Config),
+ chk_all_algos(?FUNCTION_NAME, CommonAuths, Config,
+ fun(AuthMethod,Alg) ->
+ {Opts,UsrDir} =
+ case AuthMethod of
+ publickey ->
+ {[{user_passwords, [{?USER,?BAD_PASSWD}]}],
+ setup_remote_priv_and_local_auth_keys(Alg, Config)
+ };
+ _ ->
+ {[{user_passwords, [{?USER,?PASSWD}]}],
+ new_dir(Config)
+ }
+ end,
+ {Server, Host, HostPort} =
+ ssh_test_lib:daemon(0,
+ [{auth_methods, atom_to_list(AuthMethod)},
+ {system_dir, SysDir},
+ {user_dir, UsrDir},
+ {failfun, fun ssh_test_lib:failfun/2}
+ | Opts
+ ]),
+ R = exec_from_docker(Config, Host, HostPort,
+ "'lists:concat([\"Answer=\",1+3]).\r\n'",
+ [<<"Answer=4">>],
+ ""),
+ ssh:stop_daemon(Server),
+ R
+ end).
%%--------------------------------------------------------------------
-all_algorithms_otp_is_client(Config) ->
- CommonAlgs = proplists:get_value(common_server_algs, Config),
+all_algorithms_sftp_exec_reneg_otp_is_client(Config) ->
+ CommonAlgs = proplists:get_value(common_remote_server_algs, Config),
{IP,Port} = ip_port(Config),
- chk_all_algos(CommonAlgs, Config,
+ chk_all_algos(?FUNCTION_NAME, CommonAlgs, Config,
fun(Tag, Alg) ->
- ssh:connect(IP, Port, [{user,?USER},
- {password,?PWD},
- {auth_methods, "password"},
- {user_dir, new_dir(Config)},
- {preferred_algorithms, [{Tag,[Alg]}]},
- {silently_accept_hosts,true},
- {user_interaction,false}
+ ConnRes =
+ ssh:connect(IP, Port,
+ [{user,?USER},
+ {password,?PASSWD},
+ {auth_methods, "password"},
+ {user_dir, new_dir(Config)},
+ {preferred_algorithms, [{Tag,[Alg]}]},
+ {silently_accept_hosts,true},
+ {user_interaction,false}
+ ]) ,
+ test_erl_client_reneg(ConnRes, % Seems that max 10 channels may be open in sshd
+ [{exec,1},
+ {sftp,5},
+ {no_subsyst,1},
+ {setenv, 1},
+ {sftp_async,1}
])
end).
%%--------------------------------------------------------------------
-all_algorithms_otp_is_server(Config) ->
- CommonAlgs = proplists:get_value(common_client_algs, Config),
+all_algorithms_sftp_exec_reneg_otp_is_server(Config) ->
+ CommonAlgs = proplists:get_value(common_remote_client_algs, Config),
UserDir = setup_remote_priv_and_local_auth_keys('ssh-rsa', Config),
- chk_all_algos(CommonAlgs, Config,
+ chk_all_algos(?FUNCTION_NAME, CommonAlgs, Config,
fun(Tag,Alg) ->
HostKeyAlg = case Tag of
public_key -> Alg;
_ -> 'ssh-rsa'
end,
+ SftpRootDir = new_dir(Config),
+ %% ct:log("Rootdir = ~p",[SftpRootDir]),
{Server, Host, HostPort} =
ssh_test_lib:daemon(0,
[{preferred_algorithms, [{Tag,[Alg]}]},
{system_dir, setup_local_hostdir(HostKeyAlg, Config)},
{user_dir, UserDir},
- {user_passwords, [{?USER,?PWD}]},
- {failfun, fun ssh_test_lib:failfun/2}
+ {user_passwords, [{?USER,?PASSWD}]},
+ {failfun, fun ssh_test_lib:failfun/2},
+ {subsystems,
+ [ssh_sftpd:subsystem_spec([{cwd,SftpRootDir},
+ {root,SftpRootDir}]),
+ {"echo_10",{ssh_echo_server,[10,[{dbg,true}]]}}
+ ]}
]),
- R = exec_from_docker(Config, Host, HostPort,
- "hi_there.\r\n",
- [<<"hi_there">>],
- ""),
+ R = do([fun() ->
+ exec_from_docker(Config, Host, HostPort,
+ "hi_there.\r\n",
+ [<<"hi_there">>],
+ "")
+ end,
+ fun() ->
+ sftp_tests_erl_server(Config, Host, HostPort, SftpRootDir, UserDir)
+ end
+ ]),
ssh:stop_daemon(Server),
R
end).
%%--------------------------------------------------------------------
+send_recv_big_with_renegotiate_otp_is_client(Config) ->
+ %% Connect to the remote openssh server:
+ {IP,Port} = ip_port(Config),
+ {ok,C} = ssh:connect(IP, Port, [{user,?USER},
+ {password,?PASSWD},
+ {user_dir, setup_remote_auth_keys_and_local_priv('ssh-rsa', Config)},
+ {silently_accept_hosts,true},
+ {user_interaction,false}
+ ]),
+
+ %% Open a channel and exec the Linux 'cat' command at the openssh side.
+ %% This 'cat' will read stdin and write to stdout until an eof is read from stdin.
+ {ok, Ch1} = ssh_connection:session_channel(C, infinity),
+ success = ssh_connection:exec(C, Ch1, "cat", infinity),
+
+ %% Build big binary
+ HalfSizeBytes = 100*1000*1000,
+ Data = << <<X:32>> || X <- lists:seq(1, HalfSizeBytes div 4)>>,
+
+ %% Send the data. Must spawn a process to avoid deadlock. The client will block
+ %% until all is sent through the send window. But the server will stop receiveing
+ %% when the servers send-window towards the client is full.
+ %% Since the client can't receive before the server has received all but 655k from the client
+ %% ssh_connection:send/4 is blocking...
+ spawn_link(
+ fun() ->
+ ct:comment("Sending ~p Mbytes with renegotiation in the middle",[2*byte_size(Data)/1000000]),
+ %% ct:log("sending first ~p bytes",[byte_size(Data)]),
+ ok = ssh_connection:send(C, Ch1, Data, 10000),
+ %% ct:log("Init renegotiation test",[]),
+ Kex1 = renegotiate_test(init, C),
+ %% ct:log("sending next ~p bytes",[byte_size(Data)]),
+ ok = ssh_connection:send(C, Ch1, Data, 10000),
+ %% ct:log("Finnish renegotiation test",[]),
+ renegotiate_test(Kex1, C),
+ %% ct:log("sending eof",[]),
+ ok = ssh_connection:send_eof(C, Ch1)
+ %%, ct:log("READY, sent ~p bytes",[2*byte_size(Data)])
+ end),
+
+ {eof,ReceivedData} =
+ loop_until(fun({eof,_}) -> true;
+ (_ ) -> false
+ end,
+ fun(Acc) ->
+ %%ct:log("Get more ~p",[ ExpectedSize-byte_size(Acc) ]),
+ receive
+ {ssh_cm, C, {eof,Ch}} when Ch==Ch1 ->
+ %% ct:log("eof received",[]),
+ {eof,Acc};
+
+ {ssh_cm, C, {data,Ch,0,B}} when Ch==Ch1,
+ is_binary(B) ->
+ %% ct:log("(1) Received ~p bytes (total ~p), missing ~p bytes",
+ %% [byte_size(B),
+ %% byte_size(B)+byte_size(Acc),
+ %% 2*byte_size(Data)-(byte_size(B)+byte_size(Acc))]),
+ ssh_connection:adjust_window(C, Ch1, byte_size(B)),
+ <<Acc/binary, B/binary>>
+ end
+ end,
+ <<>>),
+
+ ExpectedData = <<Data/binary, Data/binary>>,
+ case ReceivedData of
+ ExpectedData ->
+ %% ct:log("Correct data returned",[]),
+ %% receive close messages
+ loop_until(fun(Left) -> %% ct:log("Expect: ~p",[Left]),
+ Left == []
+ end,
+ fun([Next|Rest]) ->
+ receive
+ {ssh_cm,C,Next} -> Rest
+ end
+ end,
+ [%% Already received: {eof, Ch1},
+ {exit_status,Ch1,0},
+ {closed,Ch1}]
+ ),
+ ok;
+ _ when is_binary(ReceivedData) ->
+ ct:fail("~p bytes echoed but ~p expected", [byte_size(ReceivedData), 2*byte_size(Data)])
+ end.
+
+%%--------------------------------------------------------------------
%% Utilities ---------------------------------------------------------
%%--------------------------------------------------------------------
-exec_from_docker(WhatEver, {0,0,0,0}, HostPort, Command, Expects, ExtraSshArg) ->
- exec_from_docker(WhatEver, host_ip(), HostPort, Command, Expects, ExtraSshArg);
+%%--------------------------------------------------------------------
+%%
+%% A practical meta function
+%%
+loop_until(CondFun, DoFun, Acc) ->
+ case CondFun(Acc) of
+ true ->
+ Acc;
+ false ->
+ loop_until(CondFun, DoFun, DoFun(Acc))
+ end.
+
+%%--------------------------------------------------------------------
+%%
+%% Exec the Command in the docker. Add the arguments ExtraSshArg in the
+%% ssh command.
+%%
+%% If Expects is returned, then return 'ok', else return {fail,Msg}.
+%%
exec_from_docker(Config, HostIP, HostPort, Command, Expects, ExtraSshArg) when is_binary(hd(Expects)),
is_list(Config) ->
{DockerIP,DockerPort} = ip_port(Config),
{ok,C} = ssh:connect(DockerIP, DockerPort,
[{user,?USER},
- {password,?PWD},
+ {password,?PASSWD},
{user_dir, new_dir(Config)},
{silently_accept_hosts,true},
{user_interaction,false}
]),
- R = exec_from_docker(C, HostIP, HostPort, Command, Expects, ExtraSshArg),
+ R = exec_from_docker(C, HostIP, HostPort, Command, Expects, ExtraSshArg, Config),
ssh:close(C),
- R;
-
-exec_from_docker(C, HostIP, HostPort, Command, Expects, ExtraSshArg) when is_binary(hd(Expects)) ->
- SSH_from_docker =
- lists:concat(["sshpass -p ",?PWD," ",
- "/buildroot/ssh/bin/ssh -p ",HostPort," -o 'CheckHostIP=no' -o 'StrictHostKeyChecking=no' ",
- ExtraSshArg," ",
- inet_parse:ntoa(HostIP)," "
- ]),
- ExecCommand = SSH_from_docker ++ Command,
- R = exec(C, ExecCommand),
- case R of
- {ok,{ExitStatus,Result}} when ExitStatus == 0 ->
+ R.
+
+exec_from_docker(C, DestIP, DestPort, Command, Expects, ExtraSshArg, Config) when is_binary(hd(Expects)) ->
+ ExecCommand =
+ lists:concat(
+ ["sshpass -p ",?PASSWD," "
+ | case proplists:get_value(ssh_version,Config) of
+ "dropbear" ++ _ ->
+ ["dbclient -y -y -p ",DestPort," ",ExtraSshArg," ",iptoa(DestIP)," "];
+
+ _ -> %% OpenSSH or compatible
+ ["/buildroot/ssh/bin/ssh -o 'CheckHostIP=no' -o 'StrictHostKeyChecking=no' ",
+ ExtraSshArg," -p ",DestPort," ",iptoa(DestIP)," "]
+ end]) ++ Command,
+
+ case exec(C, ExecCommand) of
+ {ok,{ExitStatus,Result}} = R when ExitStatus == 0 ->
case binary:match(Result, Expects) of
nomatch ->
ct:log("Result of~n ~s~nis~n ~p",[ExecCommand,R]),
@@ -327,28 +470,26 @@ exec_from_docker(C, HostIP, HostPort, Command, Expects, ExtraSshArg) when is_bin
_ ->
ok
end;
- {ok,_} ->
+ {ok,_} = R ->
ct:log("Result of~n ~s~nis~n ~p",[ExecCommand,R]),
{fail, "Exit status =/= 0"};
- _ ->
+ R ->
ct:log("Result of~n ~s~nis~n ~p",[ExecCommand,R]),
{fail, "Couldn't login to host"}
end.
-
-
exec(C, Cmd) ->
- ct:log("~s",[Cmd]),
+ %% ct:log("~s",[Cmd]),
{ok,Ch} = ssh_connection:session_channel(C, 10000),
success = ssh_connection:exec(C, Ch, Cmd, 10000),
- exec_result(C, Ch).
+ result_of_exec(C, Ch).
-exec_result(C, Ch) ->
- exec_result(C, Ch, undefined, <<>>).
+result_of_exec(C, Ch) ->
+ result_of_exec(C, Ch, undefined, <<>>).
-exec_result(C, Ch, ExitStatus, Acc) ->
+result_of_exec(C, Ch, ExitStatus, Acc) ->
receive
{ssh_cm,C,{closed,Ch}} ->
%%ct:log("CHAN ~p got *closed*",[Ch]),
@@ -356,29 +497,37 @@ exec_result(C, Ch, ExitStatus, Acc) ->
{ssh_cm,C,{exit_status,Ch,ExStat}} when ExitStatus == undefined ->
%%ct:log("CHAN ~p got *exit status ~p*",[Ch,ExStat]),
- exec_result(C, Ch, ExStat, Acc);
+ result_of_exec(C, Ch, ExStat, Acc);
{ssh_cm,C,{data,Ch,_,Data}=_X} when ExitStatus == undefined ->
%%ct:log("CHAN ~p got ~p",[Ch,_X]),
- exec_result(C, Ch, ExitStatus, <<Acc/binary, Data/binary>>);
+ result_of_exec(C, Ch, ExitStatus, <<Acc/binary, Data/binary>>);
_Other ->
%%ct:log("OTHER: ~p",[_Other]),
- exec_result(C, Ch, ExitStatus, Acc)
+ result_of_exec(C, Ch, ExitStatus, Acc)
after 5000 ->
- %%ct:log("NO MORE, received so far:~n~s",[Acc]),
+ ct:log("NO MORE, received so far:~n~s",[Acc]),
{error, timeout}
end.
-chk_all_algos(CommonAlgs, Config, DoTestFun) when is_function(DoTestFun,2) ->
+%%--------------------------------------------------------------------
+%%
+%% Loop through all {Tag,Alg} pairs in CommonAlgs, call DoTestFun(Tag,Alg) which
+%% returns one of {ok,C}, ok, or Other.
+%%
+%% The chk_all_algos returns 'ok' or {fail,FaledAlgosList}
+%%
+
+chk_all_algos(FunctionName, CommonAlgs, Config, DoTestFun) when is_function(DoTestFun,2) ->
ct:comment("~p algorithms",[length(CommonAlgs)]),
%% Check each algorithm
Failed =
lists:foldl(
fun({Tag,Alg}, FailedAlgos) ->
- ct:log("Try ~p",[Alg]),
+ %% ct:log("Try ~p",[Alg]),
case DoTestFun(Tag,Alg) of
{ok,C} ->
ssh:close(C),
@@ -387,10 +536,10 @@ chk_all_algos(CommonAlgs, Config, DoTestFun) when is_function(DoTestFun,2) ->
FailedAlgos;
Other ->
ct:log("FAILED! ~p ~p: ~p",[Tag,Alg,Other]),
- [Alg|FailedAlgos]
+ [{Alg,Other}|FailedAlgos]
end
end, [], CommonAlgs),
- ct:pal("~s", [format_result_table_use_all_algos(Config, CommonAlgs, Failed)]),
+ ct:pal("~s", [format_result_table_use_all_algos(FunctionName, Config, CommonAlgs, Failed)]),
case Failed of
[] ->
ok;
@@ -398,6 +547,41 @@ chk_all_algos(CommonAlgs, Config, DoTestFun) when is_function(DoTestFun,2) ->
{fail, Failed}
end.
+
+
+%%%----------------------------------------------------------------
+%%%
+%%% Call all Funs as Fun() which returns 'ok', {ok,C} or Other.
+%%% do/1 returns 'ok' or the first encountered value that is not
+%%% successful.
+%%%
+
+do(Funs) ->
+ do(Funs, 1).
+
+do([Fun|Funs], N) ->
+ case Fun() of
+ ok ->
+ %% ct:log("Fun ~p ok",[N]),
+ do(Funs, N-1);
+ {ok,C} ->
+ %% ct:log("Fun ~p {ok,C}",[N]),
+ ssh:close(C),
+ do(Funs, N-1);
+ Other ->
+ ct:log("Fun ~p FAILED:~n~p",[N, Other]),
+ Other
+ end;
+
+do([], _) ->
+ %% ct:log("All Funs ok",[]),
+ ok.
+
+%%--------------------------------------------------------------------
+%%
+%% Functions to set up local and remote host's and user's keys and directories
+%%
+
setup_local_hostdir(KeyAlg, Config) ->
setup_local_hostdir(KeyAlg, new_dir(Config), Config).
setup_local_hostdir(KeyAlg, HostDir, Config) ->
@@ -428,7 +612,7 @@ setup_remote_auth_keys_and_local_priv(KeyAlg, IP, Port, UserDir, Config) ->
ok = file:write_file(DstFile++".pub", Publ),
%% Remote auth_methods with public key
{ok,Ch,Cc} = ssh_sftp:start_channel(IP, Port, [{user, ?USER },
- {password, ?PWD },
+ {password, ?PASSWD },
{auth_methods, "password"},
{silently_accept_hosts,true},
{user_interaction,false}
@@ -460,7 +644,7 @@ setup_remote_priv_and_local_auth_keys(KeyAlg, IP, Port, UserDir, Config) ->
ok = file:write_file(AuthKeyFile, Publ),
%% Remote private and public key
{ok,Ch,Cc} = ssh_sftp:start_channel(IP, Port, [{user, ?USER },
- {password, ?PWD },
+ {password, ?PASSWD },
{auth_methods, "password"},
{silently_accept_hosts,true},
{user_interaction,false}
@@ -485,6 +669,7 @@ priv_pub_keys(KeySubDir, Type, Config, KeyAlg) ->
{ok, {Priv,Publ}}.
+%%%---------------- The default filenames
src_filename(user, 'ssh-rsa' ) -> "id_rsa";
src_filename(user, 'rsa-sha2-256' ) -> "id_rsa";
src_filename(user, 'rsa-sha2-512' ) -> "id_rsa";
@@ -516,7 +701,11 @@ dst_filename(host, 'ecdsa-sha2-nistp384') -> "ssh_host_ecdsa_key";
dst_filename(host, 'ecdsa-sha2-nistp521') -> "ssh_host_ecdsa_key".
-format_result_table_use_all_algos(Config, CommonAlgs, Failed) ->
+%%--------------------------------------------------------------------
+%%
+%% Format the result table for chk_all_algos/4
+%%
+format_result_table_use_all_algos(FunctionName, Config, CommonAlgs, Failed) ->
%% Write a nice table with the result
AlgHead = 'Algorithm',
AlgWidth = lists:max([length(atom_to_list(A)) || {_,A} <- CommonAlgs]),
@@ -529,23 +718,25 @@ format_result_table_use_all_algos(Config, CommonAlgs, Failed) ->
end,
{io_lib:format('~s ~*s ~s~n',
[Tag, -AlgWidth, A,
- case lists:member(A,Failed) of
- true -> "<<<< FAIL <<<<";
- false-> "(ok)"
+ case proplists:get_value(A,Failed) of
+ undefined -> "(ok)";
+ Err -> io_lib:format("<<<< FAIL <<<< ~p",[Err])
end]),
T}
end, undefined, CommonAlgs),
Vssh = proplists:get_value(ssh_version,Config,""),
- Vssl = proplists:get_value(ssl_version,Config,""),
- io_lib:format("~nResults, Peer versions: ~s and ~s~n"
+ io_lib:format("~nResults of ~p, Peer version: ~s~n~n"
"Tag ~*s Result~n"
"=====~*..=s=======~n~s"
- ,[Vssh,Vssl,
- -AlgWidth,AlgHead,
+ ,[FunctionName, Vssh,
+ -AlgWidth, AlgHead,
AlgWidth, "", ResultTable]).
-
+%%--------------------------------------------------------------------
+%%
+%% Docker handling: start_docker/1 and stop_docker/1
+%%
start_docker(Ver) ->
Cmnd = lists:concat(["docker run -itd --rm -p 1234 ",?DOCKER_PFX,":",Ver]),
Id0 = os:cmd(Cmnd),
@@ -572,6 +763,10 @@ is_docker_sha(L) ->
(_) -> false
end, L).
+%%--------------------------------------------------------------------
+%%
+%% Misc docker info functions
+
ip_port(Config) ->
{_Ver,{IP,Port},_} = proplists:get_value(id,Config),
{IP,Port}.
@@ -590,6 +785,23 @@ ip(Id) ->
{ok,IP} = inet:parse_address(IPstr),
IP.
+%%--------------------------------------------------------------------
+%%
+%% Normalize the host returned from ssh_test_lib
+
+iptoa({0,0,0,0}) -> inet_parse:ntoa(host_ip());
+iptoa(IP) -> inet_parse:ntoa(IP).
+
+host_ip() ->
+ {ok,Name} = inet:gethostname(),
+ {ok,#hostent{h_addr_list = [IP|_]}} = inet_res:gethostbyname(Name),
+ IP.
+
+%%--------------------------------------------------------------------
+%%
+%% Create a new fresh directory or clear an existing one
+%%
+
new_dir(Config) ->
PrivDir = proplists:get_value(priv_dir, Config),
SubDirName = integer_to_list(erlang:system_time()),
@@ -626,20 +838,34 @@ delete_all_contents(Dir) ->
end
end, Fs).
+%%--------------------------------------------------------------------
+%%
+%% Find the intersection of algoritms for otp ssh and the docker ssh.
+%% Returns {ok, ServerHello, Server, ClientHello, Client} where Server are the algorithms common
+%% with the docker server and analogous for Client.
+%%
+%% Client may be undefined if no usable client is found.
+%%
+%% Both Server and Client are lists of {Tag,AlgName}.
+%%
+
common_algs(Config, IP, Port) ->
case remote_server_algs(IP, Port) of
- {ok, {RemoteHelloBin, RemoteServerKexInit}} ->
+ {ok, {ServerHello, RemoteServerKexInit}} ->
+ RemoteServerAlgs = kexint_msg2default_algorithms(RemoteServerKexInit),
+ Server = find_common_algs(RemoteServerAlgs,
+ use_algorithms(ServerHello)),
+ ct:log("Remote server:~n~p~n~p",[ServerHello, RemoteServerAlgs]),
case remote_client_algs(Config) of
- {ok,{_Hello,RemoteClientKexInit}} ->
- RemoteServerAlgs = kexint_msg2default_algorithms(RemoteServerKexInit),
- Server = find_common_algs(RemoteServerAlgs,
- use_algorithms(RemoteHelloBin)),
+ {ok,{ClientHello,RemoteClientKexInit}} ->
RemoteClientAlgs = kexint_msg2default_algorithms(RemoteClientKexInit),
Client = find_common_algs(RemoteClientAlgs,
- use_algorithms(RemoteHelloBin)),
- ct:log("Docker server algorithms:~n ~p~n~nDocker client algorithms:~n ~p",
- [RemoteServerAlgs,RemoteClientAlgs]),
- {ok, Server, Client};
+ use_algorithms(ClientHello)),
+ ct:log("Remote client:~n~p~n~p",[ClientHello, RemoteClientAlgs]),
+ {ok, ServerHello, Server, ClientHello, Client};
+ {error,_} =TO ->
+ ct:log("Remote client algs can't be found: ~p",[TO]),
+ {ok, ServerHello, Server, undefined, undefined};
Other ->
Other
end;
@@ -648,6 +874,24 @@ common_algs(Config, IP, Port) ->
end.
+chk_hellos(Hs, Str) ->
+ lists:foldl(
+ fun(H, Acc) ->
+ try binary:split(H, <<"-">>, [global])
+ of
+ %% [<<"SSH">>,<<"2.0">>|_] ->
+ %% Acc;
+ [<<"SSH">>,OldVer = <<"1.",_/binary>>|_] ->
+ io_lib:format("~s, Old SSH ver ~s",[Acc,OldVer]);
+ _ ->
+ Acc
+ catch
+ _:_ ->
+ Acc
+ end
+ end, Str, Hs).
+
+
find_common_algs(Remote, Local) ->
[{T,V} || {T,Vs} <- ssh_test_lib:extract_algos(
ssh_test_lib:intersection(Remote,
@@ -685,12 +929,18 @@ kexint_msg2default_algorithms(#ssh_msg_kexinit{kex_algorithms = Kex,
{server2client,ssh_test_lib:to_atoms(CompS2C)}]}].
-
+%%--------------------------------------------------------------------
+%%
+%% Find the algorithms supported by the remote server
+%%
+%% Connect with tcp to the server, send a hello and read the returned
+%% server hello and kexinit message.
+%%
remote_server_algs(IP, Port) ->
case try_gen_tcp_connect(IP, Port, 5) of
{ok,S} ->
ok = gen_tcp:send(S, "SSH-2.0-CheckAlgs\r\n"),
- receive_hello(S, <<>>);
+ receive_hello(S);
{error,Error} ->
{error,Error}
end.
@@ -709,6 +959,13 @@ try_gen_tcp_connect(_, _, _) ->
{error, "No contact"}.
+%%--------------------------------------------------------------------
+%%
+%% Find the algorithms supported by the remote client
+%%
+%% Set up a fake ssh server and make the remote client connect to it. Use
+%% hello message and the kexinit message.
+%%
remote_client_algs(Config) ->
Parent = self(),
Ref = make_ref(),
@@ -719,7 +976,7 @@ remote_client_algs(Config) ->
Parent ! {addr,Ref,IP,Port},
{ok,S} = gen_tcp:accept(Sl),
ok = gen_tcp:send(S, "SSH-2.0-CheckAlgs\r\n"),
- Parent ! {Ref,receive_hello(S, <<>>)}
+ Parent ! {Ref,receive_hello(S)}
end),
receive
{addr,Ref,IP,Port} ->
@@ -732,14 +989,28 @@ remote_client_algs(Config) ->
receive
{Ref, Result} ->
Result
- after 15000 ->
- {error, timeout2}
+ after 5000 ->
+ {error, {timeout,2}}
end
- after 15000 ->
- {error, timeout1}
+ after 5000 ->
+ {error, {timeout,1}}
end.
+%%% Receive a few packets from the remote server or client and find what is supported:
+
+receive_hello(S) ->
+ try
+ receive_hello(S, <<>>)
+ of
+ Result ->
+ Result
+ catch
+ Class:Error ->
+ ST = erlang:get_stacktrace(),
+ {error, {Class,Error,ST}}
+ end.
+
receive_hello(S, Ack) ->
%% The Ack is to collect bytes until the full message is received
@@ -747,20 +1018,19 @@ receive_hello(S, Ack) ->
{tcp, S, Bin0} when is_binary(Bin0) ->
case binary:split(<<Ack/binary, Bin0/binary>>, [<<"\r\n">>,<<"\r">>,<<"\n">>]) of
[Hello = <<"SSH-2.0-",_/binary>>, NextPacket] ->
- ct:log("Got 2.0 hello (~p), ~p bytes to next msg",[Hello,size(NextPacket)]),
+ %% ct:log("Got 2.0 hello (~p), ~p bytes to next msg",[Hello,size(NextPacket)]),
{ok, {Hello, receive_kexinit(S, NextPacket)}};
[Hello = <<"SSH-1.99-",_/binary>>, NextPacket] ->
- ct:comment("Old SSH ~s",["1.99"]),
- ct:log("Got 1.99 hello (~p), ~p bytes to next msg",[Hello,size(NextPacket)]),
+ %% ct:log("Got 1.99 hello (~p), ~p bytes to next msg",[Hello,size(NextPacket)]),
{ok, {Hello, receive_kexinit(S, NextPacket)}};
[Bin] when size(Bin) < 256 ->
- ct:log("Got part of hello (~p chars):~n~s~n~s",[size(Bin),Bin,
- [io_lib:format('~2.16.0b ',[C])
- || C <- binary_to_list(Bin0)
- ]
- ]),
+ %% ct:log("Got part of hello (~p chars):~n~s~n~s",[size(Bin),Bin,
+ %% [io_lib:format('~2.16.0b ',[C])
+ %% || C <- binary_to_list(Bin0)
+ %% ]
+ %% ]),
receive_hello(S, Bin0);
_ ->
@@ -804,11 +1074,326 @@ receive_kexinit(S, Ack) ->
throw(timeout)
end.
+%%%----------------------------------------------------------------
+%%% Test of sftp from the OpenSSH client side
+%%%
+sftp_tests_erl_server(Config, ServerIP, ServerPort, ServerRootDir, UserDir) ->
+ try
+ Cmnds = prepare_local_directory(ServerRootDir),
+ call_sftp_in_docker(Config, ServerIP, ServerPort, Cmnds, UserDir),
+ check_local_directory(ServerRootDir)
+ catch
+ Class:Error ->
+ ST = erlang:get_stacktrace(),
+ {error, {Class,Error,ST}}
+ end.
-host_ip() ->
- {ok,Name} = inet:gethostname(),
- {ok,#hostent{h_addr_list = [IP|_]}} = inet_res:gethostbyname(Name),
- IP.
+prepare_local_directory(ServerRootDir) ->
+ file:write_file(filename:join(ServerRootDir,"tst1"),
+ <<"Some test text">>
+ ),
+ ["get tst1",
+ "put tst1 tst2",
+ "put tst1 tst3",
+ "rename tst1 ex_tst1",
+ "rm tst3",
+ "mkdir mydir",
+ "cd mydir",
+ "put tst1 file_1",
+ "put tst1 unreadable_file",
+ "chmod 222 unreadable_file",
+ "exit"].
+
+check_local_directory(ServerRootDir) ->
+ case lists:sort(ok(file:list_dir(ServerRootDir)) -- [".",".."]) of
+ ["ex_tst1","mydir","tst2"] ->
+ {ok,Expect} = file:read_file(filename:join(ServerRootDir,"ex_tst1")),
+ case file:read_file(filename:join(ServerRootDir,"tst2")) of
+ {ok,Expect} ->
+ case lists:sort(ok(file:list_dir(filename:join(ServerRootDir,"mydir"))) -- [".",".."]) of
+ ["file_1","unreadable_file"] ->
+ case file:read_file(filename:join([ServerRootDir,"mydir","file_1"])) of
+ {ok,Expect} ->
+ case file:read_file(filename:join([ServerRootDir,"mydir","unreadable_file"])) of
+ {error,_} ->
+ ok;
+ {ok,_} ->
+ {error, {could_read_unreadable,"mydir/unreadable_file"}}
+ end;
+ {ok,Other} ->
+ ct:log("file_1:~n~s~nExpected:~n~s",[Other,Expect]),
+ {error, {bad_contents_in_file,"mydir/file_1"}}
+ end;
+ Other ->
+ ct:log("Directory ~s~n~p",[filename:join(ServerRootDir,"mydir"),Other]),
+ {error,{bad_dir_contents,"mydir"}}
+ end;
+ {ok,Other} ->
+ ct:log("tst2:~n~s~nExpected:~n~s",[Other,Expect]),
+ {error, {bad_contents_in_file,"tst2"}}
+ end;
+ ["tst1"] ->
+ {error,{missing_file,"tst2"}};
+ Other ->
+ ct:log("Directory ~s~n~p",[ServerRootDir,Other]),
+ {error,{bad_dir_contents,"/"}}
+ end.
+
+call_sftp_in_docker(Config, ServerIP, ServerPort, Cmnds, UserDir) ->
+ {DockerIP,DockerPort} = ip_port(Config),
+ {ok,C} = ssh:connect(DockerIP, DockerPort,
+ [{user,?USER},
+ {password,?PASSWD},
+ {user_dir, UserDir},
+ {silently_accept_hosts,true},
+ {user_interaction,false}
+ ]),
+
+ %% Make commands for "expect" in the docker:
+ PreExpectCmnds = ["spawn /buildroot/ssh/bin/sftp -oPort="++integer_to_list(ServerPort)++
+ " -oCheckHostIP=no -oStrictHostKeyChecking=no " ++
+ iptoa(ServerIP)++"\n"
+ ],
+ PostExpectCmnds= [],
+ ExpectCmnds =
+ PreExpectCmnds ++
+ ["expect \"sftp>\" {send \""++Cmnd++"\n\"}\n" || Cmnd <- Cmnds] ++
+ PostExpectCmnds,
+
+ %% Make an commands file in the docker
+ {ok,Ch} = ssh_sftp:start_channel(C, [{timeout,10000}]),
+ ok = ssh_sftp:write_file(Ch, "commands", erlang:iolist_to_binary(ExpectCmnds)),
+ ok = ssh_sftp:stop_channel(Ch),
+
+ %% Call expect in the docker
+ {ok, Ch1} = ssh_connection:session_channel(C, infinity),
+ Kex1 = renegotiate_test(init, C),
+ success = ssh_connection:exec(C, Ch1, "expect commands", infinity),
+
+ renegotiate_test(Kex1, C),
+ recv_log_msgs(C, Ch1),
+
+ %% Done.
+ ssh:close(C).
+
+recv_log_msgs(C, Ch) ->
+ receive
+ {ssh_cm,C,{closed,Ch}} ->
+ %% ct:log("Channel closed ~p",[{closed,1}]),
+ ok;
+ {ssh_cm,C,{data,Ch,1,Msg}} ->
+ ct:log("*** ERROR from docker:~n~s",[Msg]),
+ recv_log_msgs(C, Ch);
+ {ssh_cm,C,_Msg} ->
+ %% ct:log("Got ~p",[_Msg]),
+ recv_log_msgs(C, Ch)
+ end.
+%%%----------------------------------------------------------------
+%%%----------------------------------------------------------------
+%%%
+%%% Tests from the Erlang client side
+%%%
+%%%----------------------------------------------------------------
+%%%----------------------------------------------------------------
+test_erl_client_reneg({ok,C}, Spec) ->
+ %% Start the test processes on the connection C:
+ Parent = self(),
+ Pids = [spawn(
+ fun() ->
+ Parent ! {self(), TestType, Id, one_test_erl_client(TestType,Id,C)}
+ end
+ )
+ || {TestType,N} <- Spec,
+ Id <- lists:seq(1,N)],
+
+ Kex1 = renegotiate_test(init, C),
+
+ %% Collect the results:
+ case lists:filter(
+ fun(R) -> R=/=ok end,
+ [receive
+ {Pid,_TestType,_Id,ok} ->
+ %% ct:log("Test ~p:~p passed!", [_TestType,_Id]),
+ ok;
+ {Pid,TestType,Id,OtherResult} ->
+ ct:log("~p:~p ~p ~p~n~p",[?MODULE,?LINE,TestType,Id,OtherResult]),
+ {error,TestType,Id}
+ end || Pid <- Pids])
+ of
+ [] ->
+ renegotiate_test(Kex1, C),
+ {ok,C};
+ Other ->
+ renegotiate_test(Kex1, C),
+ Other
+ end;
+
+test_erl_client_reneg(Error, _) ->
+ Error.
+
+
+one_test_erl_client(exec, Id, C) ->
+ {ok, Ch} = ssh_connection:session_channel(C, infinity),
+ success = ssh_connection:exec(C, Ch, "echo Hi there", 5000),
+ case loop_until(fun({eof,_}) -> true;
+ (_ ) -> false
+ end,
+ fun(Acc) ->
+ receive
+ {ssh_cm, C, {eof,Ch}} ->
+ {eof,Acc};
+ {ssh_cm, C, {data,Ch,0,B}} when is_binary(B) ->
+ <<Acc/binary, B/binary>>
+ end
+ end,
+ <<>>) of
+ {eof,<<"Hi there\n">>} ->
+ ok;
+ Other ->
+ ct:pal("exec Got other ~p", [Other]),
+ {error, {exec,Id,bad_msg,Other,undefined}}
+ end;
+
+one_test_erl_client(no_subsyst, Id, C) ->
+ {ok, Ch} = ssh_connection:session_channel(C, infinity),
+ case ssh_connection:subsystem(C, Ch, "foo", infinity) of
+ failure ->
+ ok;
+ Other ->
+ ct:pal("no_subsyst Got other ~p", [Other]),
+ {error, {no_subsyst,Id,bad_ret,Other,undefined}}
+ end;
+
+one_test_erl_client(setenv, Id, C) ->
+ {ok, Ch} = ssh_connection:session_channel(C, infinity),
+ Var = "ENV_TEST",
+ Value = lists:concat(["env_test_",Id,"_",erlang:system_time()]),
+ Env = case ssh_connection:setenv(C, Ch, Var, Value, infinity) of
+ success -> binary_to_list(Value++"\n");
+ failure -> <<"\n">>
+ end,
+ success = ssh_connection:exec(C, Ch, "echo $"++Var, 5000),
+ case loop_until(fun({eof,_}) -> true;
+ (_ ) -> false
+ end,
+ fun(Acc) ->
+ receive
+ {ssh_cm, C, {eof,Ch}} ->
+ {eof,Acc};
+ {ssh_cm, C, {data,Ch,0,B}} when is_binary(B) ->
+ <<Acc/binary, B/binary>>
+ end
+ end,
+ <<>>) of
+ {eof,Env} ->
+ ok;
+ Other ->
+ ct:pal("setenv Got other ~p", [Other]),
+ {error, {setenv,Id,bad_msg,Other,undefined}}
+ end;
+
+one_test_erl_client(SFTP, Id, C) when SFTP==sftp ; SFTP==sftp_async ->
+ try
+ {ok,Ch} = ssh_sftp:start_channel(C, [{timeout,10000}]),
+ %% A new fresh name of a new file tree:
+ RootDir = lists:concat(["r_",Id,"_",erlang:system_time()]),
+ %% Check that it does not exist:
+ false = lists:member(RootDir, ok(ssh_sftp:list_dir(Ch, "."))),
+ %% Create it:
+ ok = ssh_sftp:make_dir(Ch, RootDir),
+ {ok, #file_info{type=directory, access=read_write}} = ssh_sftp:read_file_info(Ch, RootDir),
+ R = do_sftp_tests_erl_client(SFTP, C, Ch, Id, RootDir),
+ catch ssh_sftp:stop_channel(Ch),
+ R
+ catch
+ Class:Error ->
+ ST = erlang:get_stacktrace(),
+ {error, {SFTP,Id,Class,Error,ST}}
+ end.
+
+
+
+do_sftp_tests_erl_client(sftp_async, _C, Ch, _Id, RootDir) ->
+ FileName1 = "boring_name",
+ F1 = filename:join(RootDir, FileName1),
+ %% Open a new handle and start writing:
+ {ok,Handle1} = ssh_sftp:open(Ch, F1, [write,binary]),
+ {async,Aref1} = ssh_sftp:awrite(Ch, Handle1, <<0:250000/unsigned-unit:8>>),
+ wait_for_async_result(Aref1);
+
+do_sftp_tests_erl_client(sftp, _C, Ch, _Id, RootDir) ->
+ FileName0 = "f0",
+ F0 = filename:join(RootDir, FileName0),
+
+ %% Create and write a file:
+ ok = ssh_sftp:write_file(Ch,
+ F0 = filename:join(RootDir, FileName0),
+ Data0 = mkbin(1234,240)),
+ {ok,Data0} = ssh_sftp:read_file(Ch, F0),
+ {ok, #file_info{type=regular, access=read_write, size=1234}} = ssh_sftp:read_file_info(Ch, F0),
+
+ %% Re-write:
+ {ok,Handle0} = ssh_sftp:open(Ch, F0, [write,read,binary]),
+ ok = ssh_sftp:pwrite(Ch, Handle0, 16, Data0_1=mkbin(10,255)),
+
+ <<B1:16/binary, _:10/binary, B2:(1234-26)/binary>> = Data0,
+ FileContents = <<B1:16/binary, Data0_1:10/binary, B2:(1234-26)/binary>>,
+
+ <<_:1/binary, Part:25/binary, _/binary>> = FileContents,
+ {ok, Part} = ssh_sftp:pread(Ch, Handle0, 1, 25),
+
+ %% Check:
+ {ok, FileContents} = ssh_sftp:pread(Ch, Handle0, 0, 1234),
+ ok = ssh_sftp:close(Ch, Handle0),
+
+ %% Check in another way:
+ {ok, FileContents} = ssh_sftp:read_file(Ch, F0),
+
+ %% Remove write access rights and check that it can't be written:
+ ok = ssh_sftp:write_file_info(Ch, F0, #file_info{mode=8#400}), %read}),
+ {ok, #file_info{type=regular, access=read}} = ssh_sftp:read_file_info(Ch, F0),
+ {error,permission_denied} = ssh_sftp:write_file(Ch, F0, mkbin(10,14)),
+
+ %% Test deletion of file and dir:
+ [FileName0] = ok(ssh_sftp:list_dir(Ch, RootDir)) -- [".", ".."],
+ ok = ssh_sftp:delete(Ch, F0),
+ [] = ok(ssh_sftp:list_dir(Ch, RootDir)) -- [".", ".."],
+ ok = ssh_sftp:del_dir(Ch, RootDir),
+ false = lists:member(RootDir, ok(ssh_sftp:list_dir(Ch, "."))),
+ ok.
+
+
+wait_for_async_result(Aref) ->
+ receive
+ {async_reply, Aref, Result} ->
+ Result
+ after
+ 60000 ->
+ timeout
+ end.
+
+
+mkbin(Size, Byte) ->
+ list_to_binary(lists:duplicate(Size,Byte)).
+
+ok({ok,X}) -> X.
+
+%%%----------------------------------------------------------------
+renegotiate_test(init, ConnectionRef) ->
+ Kex1 = ssh_test_lib:get_kex_init(ConnectionRef),
+ ssh_connection_handler:renegotiate(ConnectionRef),
+ %%ct:log("Renegotiate test initiated!",[]),
+ Kex1;
+
+renegotiate_test(Kex1, ConnectionRef) ->
+ case ssh_test_lib:get_kex_init(ConnectionRef) of
+ Kex1 ->
+ ct:log("Renegotiate test failed, Kex1 == Kex2!",[]),
+ error(renegotiate_failed);
+ _ ->
+ %% ct:log("Renegotiate test passed!",[]),
+ ok
+ end.
diff --git a/lib/ssh/test/ssh_compat_SUITE_data/build_scripts/create-dropbear-ssh b/lib/ssh/test/ssh_compat_SUITE_data/build_scripts/create-dropbear-ssh
new file mode 100755
index 0000000000..85973081d0
--- /dev/null
+++ b/lib/ssh/test/ssh_compat_SUITE_data/build_scripts/create-dropbear-ssh
@@ -0,0 +1,28 @@
+#!/bin/sh
+
+# ./create-dropbear-ssh
+
+# This way of fetching the tar-file separate from the docker commands makes
+# http-proxy handling way easier. The wget command handles the $https_proxy
+# variable while the docker command must have /etc/docker/something changed
+# and the docker server restarted. That is not possible without root access.
+
+# Make a Dockerfile. This method simplifies env variable handling considerably:
+cat - > TempDockerFile <<EOF
+
+ FROM ubuntubuildbase
+
+ WORKDIR /buildroot
+
+ RUN apt-get -y update
+ RUN apt-get -y upgrade
+ RUN apt-get -y install openssh-sftp-server
+%% RUN echo 81 | apt-get -y install dropbear
+
+EOF
+
+# Build the image:
+docker build -t ssh_compat_suite-ssh-dropbear -f ./TempDockerFile .
+
+# Cleaning
+rm -fr ./TempDockerFile $TMP
diff --git a/lib/ssh/test/ssh_compat_SUITE_data/build_scripts/create-dropbear-ssh-run b/lib/ssh/test/ssh_compat_SUITE_data/build_scripts/create-dropbear-ssh-run
new file mode 100755
index 0000000000..d98c0cfaa3
--- /dev/null
+++ b/lib/ssh/test/ssh_compat_SUITE_data/build_scripts/create-dropbear-ssh-run
@@ -0,0 +1,27 @@
+#!/bin/sh
+
+# ./create-dropbear-ssh-run
+
+VER=v2016.72
+
+# This way of fetching the tar-file separate from the docker commands makes
+# http-proxy handling way easier. The wget command handles the $https_proxy
+# variable while the docker command must have /etc/docker/something changed
+# and the docker server restarted. That is not possible without root access.
+
+# Make a Dockerfile. This method simplifies env variable handling considerably:
+cat - > TempDockerFile <<EOF
+
+ FROM ssh_compat_suite-ssh-dropbear-installed:${VER}
+
+ WORKDIR /buildroot
+
+ CMD dropbear -F -p 1234
+
+EOF
+
+# Build the image:
+docker build -t ssh_compat_suite-ssh:dropbear${VER} -f ./TempDockerFile .
+
+# Cleaning
+rm -fr ./TempDockerFile $TMP
diff --git a/lib/ssh/test/ssh_compat_SUITE_data/build_scripts/create-ssh-image b/lib/ssh/test/ssh_compat_SUITE_data/build_scripts/create-ssh-image
index 983c57b18b..2e08408841 100755
--- a/lib/ssh/test/ssh_compat_SUITE_data/build_scripts/create-ssh-image
+++ b/lib/ssh/test/ssh_compat_SUITE_data/build_scripts/create-ssh-image
@@ -47,7 +47,8 @@ cat - > TempDockerFile <<EOF
RUN ./configure --without-pie \
--prefix=/buildroot/ssh \
--with-ssl-dir=/buildroot/ssl \
- --with-pam
+ --with-pam \
+ LDFLAGS=-Wl,-R/buildroot/ssl/lib
RUN make
RUN make install
RUN echo UsePAM yes >> /buildroot/ssh/etc/sshd_config
diff --git a/lib/ssh/test/ssh_compat_SUITE_data/build_scripts/create-ssl-image b/lib/ssh/test/ssh_compat_SUITE_data/build_scripts/create-ssl-image
index 66f8358b8a..4ab2a8bddc 100755
--- a/lib/ssh/test/ssh_compat_SUITE_data/build_scripts/create-ssl-image
+++ b/lib/ssh/test/ssh_compat_SUITE_data/build_scripts/create-ssl-image
@@ -23,6 +23,16 @@ case "$1" in
;;
esac
+case $1$2 in
+ openssl0.9.8[a-l])
+ CONFIG_FLAGS=no-asm
+ ;;
+ *)
+ CONFIG_FLAGS=
+ ;;
+esac
+
+
# This way of fetching the tar-file separate from the docker commands makes
# http-proxy handling way easier. The wget command handles the $https_proxy
# variable while the docker command must have /etc/docker/something changed
@@ -42,10 +52,10 @@ cat - > TempDockerFile <<EOF
WORKDIR ${FAM}-${VER}
- RUN ./config --prefix=/buildroot/ssl
+ RUN ./config --prefix=/buildroot/ssl ${CONFIG_FLAGS}
RUN make
- RUN make install
+ RUN make install_sw
RUN echo Built ${FAM}-${VER}
EOF
diff --git a/lib/ssh/test/ssh_compat_SUITE_data/build_scripts/create_all b/lib/ssh/test/ssh_compat_SUITE_data/build_scripts/create_all
index 16b9c21d9f..0dcf8cb570 100755
--- a/lib/ssh/test/ssh_compat_SUITE_data/build_scripts/create_all
+++ b/lib/ssh/test/ssh_compat_SUITE_data/build_scripts/create_all
@@ -3,19 +3,21 @@
UBUNTU_VERSION=16.04
SSH_SSL_VERSIONS=(\
- openssh 4.4p1 openssl 0.9.8zh \
- openssh 4.5p1 openssl 0.9.8zh \
- openssh 5.0p1 openssl 0.9.8zh \
- openssh 6.2p2 openssl 0.9.8zh \
- openssh 6.3p1 openssl 0.9.8zh \
- \
- openssh 7.1p1 openssl 1.0.0t \
- \
- openssh 7.1p1 openssl 1.0.1p \
- \
- openssh 6.6p1 openssl 1.0.2n \
- openssh 7.1p1 openssl 1.0.2n \
- openssh 7.6p1 openssl 1.0.2n \
+ openssh 4.4p1 openssl 0.9.8c \
+ openssh 4.5p1 openssl 0.9.8m \
+ openssh 5.0p1 openssl 0.9.8za \
+ openssh 6.2p2 openssl 0.9.8c \
+ openssh 6.3p1 openssl 0.9.8zh \
+ \
+ openssh 7.1p1 openssl 1.0.0a \
+ \
+ openssh 7.1p1 openssl 1.0.1p \
+ \
+ openssh 6.6p1 openssl 1.0.2n \
+ openssh 7.1p1 openssl 1.0.2n \
+ openssh 7.6p1 openssl 1.0.2n \
+ \
+ openssh 7.6p1 libressl 2.6.4 \
)
if [ "x$1" == "x-b" ]
diff --git a/lib/ssh/test/ssh_connection_SUITE.erl b/lib/ssh/test/ssh_connection_SUITE.erl
index ba4518cfe6..257f2f70d7 100644
--- a/lib/ssh/test/ssh_connection_SUITE.erl
+++ b/lib/ssh/test/ssh_connection_SUITE.erl
@@ -45,9 +45,18 @@ all() ->
{group, openssh},
small_interrupted_send,
interrupted_send,
+ exec_erlang_term,
+ exec_erlang_term_non_default_shell,
start_shell,
start_shell_exec,
start_shell_exec_fun,
+ start_shell_exec_fun2,
+ start_shell_exec_fun3,
+ start_shell_exec_direct_fun,
+ start_shell_exec_direct_fun2,
+ start_shell_exec_direct_fun3,
+ start_shell_exec_direct_fun1_error,
+ start_shell_exec_direct_fun1_error_type,
start_shell_sock_exec_fun,
start_shell_sock_daemon_exec,
connect_sock_not_tcp,
@@ -85,6 +94,7 @@ init_per_suite(Config) ->
?CHECK_CRYPTO(Config).
end_per_suite(Config) ->
+ catch ssh:stop(),
Config.
%%--------------------------------------------------------------------
@@ -519,7 +529,7 @@ start_shell_exec(Config) when is_list(Config) ->
{Pid, Host, Port} = ssh_test_lib:daemon([{system_dir, SysDir},
{user_dir, UserDir},
{password, "morot"},
- {exec, {?MODULE,ssh_exec,[]}} ]),
+ {exec, {?MODULE,ssh_exec_echo,[]}} ]),
ConnectionRef = ssh_test_lib:connect(Host, Port, [{silently_accept_hosts, true},
{user, "foo"},
@@ -532,7 +542,7 @@ start_shell_exec(Config) when is_list(Config) ->
success = ssh_connection:exec(ConnectionRef, ChannelId0,
"testing", infinity),
receive
- {ssh_cm, ConnectionRef, {data, _ChannelId, 0, <<"testing\r\n">>}} ->
+ {ssh_cm, ConnectionRef, {data, _ChannelId, 0, <<"echo testing\r\n">>}} ->
ok
after 5000 ->
ct:fail("Exec Timeout")
@@ -542,10 +552,42 @@ start_shell_exec(Config) when is_list(Config) ->
ssh:stop_daemon(Pid).
%%--------------------------------------------------------------------
-start_shell_exec_fun() ->
- [{doc, "start shell to exec command"}].
+exec_erlang_term(Config) when is_list(Config) ->
+ PrivDir = proplists:get_value(priv_dir, Config),
+ UserDir = filename:join(PrivDir, nopubkey), % to make sure we don't use public-key-auth
+ file:make_dir(UserDir),
+ SysDir = proplists:get_value(data_dir, Config),
+ {Pid, Host, Port} = ssh_test_lib:daemon([{system_dir, SysDir},
+ {user_dir, UserDir},
+ {password, "morot"}
+ ]),
+
+ ConnectionRef = ssh_test_lib:connect(Host, Port, [{silently_accept_hosts, true},
+ {user, "foo"},
+ {password, "morot"},
+ {user_interaction, true},
+ {user_dir, UserDir}]),
+
+ {ok, ChannelId0} = ssh_connection:session_channel(ConnectionRef, infinity),
+
+ success = ssh_connection:exec(ConnectionRef, ChannelId0,
+ "1+2.", infinity),
+ TestResult =
+ receive
+ {ssh_cm, ConnectionRef, {data, _ChannelId, 0, <<"3",_/binary>>}} = R ->
+ ct:log("Got expected ~p",[R]);
+ Other ->
+ ct:log("Got unexpected ~p",[Other])
+ after 5000 ->
+ {fail,"Exec Timeout"}
+ end,
+
+ ssh:close(ConnectionRef),
+ ssh:stop_daemon(Pid),
+ TestResult.
-start_shell_exec_fun(Config) when is_list(Config) ->
+%%--------------------------------------------------------------------
+exec_erlang_term_non_default_shell(Config) when is_list(Config) ->
PrivDir = proplists:get_value(priv_dir, Config),
UserDir = filename:join(PrivDir, nopubkey), % to make sure we don't use public-key-auth
file:make_dir(UserDir),
@@ -553,24 +595,109 @@ start_shell_exec_fun(Config) when is_list(Config) ->
{Pid, Host, Port} = ssh_test_lib:daemon([{system_dir, SysDir},
{user_dir, UserDir},
{password, "morot"},
- {exec, fun ssh_exec/1}]),
+ {shell, fun(U, H) -> start_our_shell(U, H) end}
+ ]),
ConnectionRef = ssh_test_lib:connect(Host, Port, [{silently_accept_hosts, true},
{user, "foo"},
{password, "morot"},
{user_interaction, true},
- {user_dir, UserDir}]),
+ {user_dir, UserDir}
+ ]),
{ok, ChannelId0} = ssh_connection:session_channel(ConnectionRef, infinity),
success = ssh_connection:exec(ConnectionRef, ChannelId0,
- "testing", infinity),
+ "1+2.", infinity),
+ TestResult =
+ receive
+ {ssh_cm, ConnectionRef, {data, _ChannelId, 0, <<"3",_/binary>>}} = R ->
+ ct:log("Got unexpected ~p",[R]),
+ {fail,"Could exec erlang term although non-erlang shell"};
+ Other ->
+ ct:log("Got expected ~p",[Other])
+ after 5000 ->
+ {fail, "Exec Timeout"}
+ end,
+
+ ssh:close(ConnectionRef),
+ ssh:stop_daemon(Pid),
+ TestResult.
+
+%%--------------------------------------------------------------------
+start_shell_exec_fun(Config) ->
+ do_start_shell_exec_fun(fun ssh_exec_echo/1,
+ "testing", <<"echo testing\r\n">>, 0,
+ Config).
+
+start_shell_exec_fun2(Config) ->
+ do_start_shell_exec_fun(fun ssh_exec_echo/2,
+ "testing", <<"echo foo testing\r\n">>, 0,
+ Config).
+
+start_shell_exec_fun3(Config) ->
+ do_start_shell_exec_fun(fun ssh_exec_echo/3,
+ "testing", <<"echo foo testing\r\n">>, 0,
+ Config).
+
+start_shell_exec_direct_fun(Config) ->
+ do_start_shell_exec_fun({direct, fun ssh_exec_direct_echo/1},
+ "testing", <<"echo testing\n">>, 0,
+ Config).
+
+start_shell_exec_direct_fun2(Config) ->
+ do_start_shell_exec_fun({direct, fun ssh_exec_direct_echo/2},
+ "testing", <<"echo foo testing">>, 0,
+ Config).
+
+start_shell_exec_direct_fun3(Config) ->
+ do_start_shell_exec_fun({direct, fun ssh_exec_direct_echo/3},
+ "testing", <<"echo foo testing">>, 0,
+ Config).
+
+start_shell_exec_direct_fun1_error(Config) ->
+ do_start_shell_exec_fun({direct, fun ssh_exec_direct_echo_error_return/1},
+ "testing", <<"Error in \"testing\": {bad}\n">>, 1,
+ Config).
+
+start_shell_exec_direct_fun1_error_type(Config) ->
+ do_start_shell_exec_fun({direct, fun ssh_exec_direct_echo_error_return_type/1},
+ "testing", <<"Error in \"testing\": Bad exec-plugin return: very_bad\n">>, 1,
+ Config).
+
+
+
+do_start_shell_exec_fun(Fun, Command, Expect, ExpectType, Config) ->
+ PrivDir = proplists:get_value(priv_dir, Config),
+ UserDir = filename:join(PrivDir, nopubkey), % to make sure we don't use public-key-auth
+ file:make_dir(UserDir),
+ SysDir = proplists:get_value(data_dir, Config),
+ {Pid, Host, Port} = ssh_test_lib:daemon([{system_dir, SysDir},
+ {user_dir, UserDir},
+ {password, "morot"},
+ {exec, Fun}]),
+
+ ConnectionRef = ssh_test_lib:connect(Host, Port, [{silently_accept_hosts, true},
+ {user, "foo"},
+ {password, "morot"},
+ {user_interaction, true},
+ {user_dir, UserDir}]),
+
+ {ok, ChannelId0} = ssh_connection:session_channel(ConnectionRef, infinity),
+
+ success = ssh_connection:exec(ConnectionRef, ChannelId0, Command, infinity),
receive
- {ssh_cm, ConnectionRef, {data, _ChannelId, 0, <<"testing\r\n">>}} ->
+ {ssh_cm, ConnectionRef, {data, _ChannelId, ExpectType, Expect}} ->
ok
after 5000 ->
- ct:fail("Exec Timeout")
+ receive
+ Other ->
+ ct:pal("Received other:~n~p",[Other]),
+ ct:fail("Unexpected response")
+ after 0 ->
+ ct:fail("Exec Timeout")
+ end
end,
ssh:close(ConnectionRef),
@@ -588,7 +715,7 @@ start_shell_sock_exec_fun(Config) when is_list(Config) ->
{Pid, HostD, Port} = ssh_test_lib:daemon([{system_dir, SysDir},
{user_dir, UserDir},
{password, "morot"},
- {exec, fun ssh_exec/1}]),
+ {exec, fun ssh_exec_echo/1}]),
Host = ssh_test_lib:ntoa(ssh_test_lib:mangle_connect_address(HostD)),
{ok, Sock} = ssh_test_lib:gen_tcp_connect(Host, Port, [{active,false}]),
@@ -604,7 +731,7 @@ start_shell_sock_exec_fun(Config) when is_list(Config) ->
"testing", infinity),
receive
- {ssh_cm, ConnectionRef, {data, _ChannelId, 0, <<"testing\r\n">>}} ->
+ {ssh_cm, ConnectionRef, {data, _ChannelId, 0, <<"echo testing\r\n">>}} ->
ok
after 5000 ->
ct:fail("Exec Timeout")
@@ -628,7 +755,7 @@ start_shell_sock_daemon_exec(Config) ->
{ok, _Pid} = ssh:daemon(Ss, [{system_dir, SysDir},
{user_dir, UserDir},
{password, "morot"},
- {exec, fun ssh_exec/1}])
+ {exec, fun ssh_exec_echo/1}])
end),
{ok,Sc} = gen_tcp:accept(Sl),
{ok,ConnectionRef} = ssh:connect(Sc, [{silently_accept_hosts, true},
@@ -643,7 +770,7 @@ start_shell_sock_daemon_exec(Config) ->
"testing", infinity),
receive
- {ssh_cm, ConnectionRef, {data, _ChannelId, 0, <<"testing\r\n">>}} ->
+ {ssh_cm, ConnectionRef, {data, _ChannelId, 0, <<"echo testing\r\n">>}} ->
ok
after 5000 ->
ct:fail("Exec Timeout")
@@ -754,7 +881,7 @@ stop_listener(Config) when is_list(Config) ->
{Pid0, Host, Port} = ssh_test_lib:daemon([{system_dir, SysDir},
{user_dir, UserDir},
{password, "morot"},
- {exec, fun ssh_exec/1}]),
+ {exec, fun ssh_exec_echo/1}]),
ConnectionRef0 = ssh_test_lib:connect(Host, Port, [{silently_accept_hosts, true},
{user, "foo"},
@@ -774,7 +901,7 @@ stop_listener(Config) when is_list(Config) ->
success = ssh_connection:exec(ConnectionRef0, ChannelId0,
"testing", infinity),
receive
- {ssh_cm, ConnectionRef0, {data, ChannelId0, 0, <<"testing\r\n">>}} ->
+ {ssh_cm, ConnectionRef0, {data, ChannelId0, 0, <<"echo testing\r\n">>}} ->
ok
after 5000 ->
ct:fail("Exec Timeout")
@@ -783,7 +910,7 @@ stop_listener(Config) when is_list(Config) ->
case ssh_test_lib:daemon(Port, [{system_dir, SysDir},
{user_dir, UserDir},
{password, "potatis"},
- {exec, fun ssh_exec/1}]) of
+ {exec, fun ssh_exec_echo/1}]) of
{Pid1, Host, Port} ->
ConnectionRef1 = ssh_test_lib:connect(Host, Port, [{silently_accept_hosts, true},
{user, "foo"},
@@ -800,6 +927,8 @@ stop_listener(Config) when is_list(Config) ->
ssh:stop_daemon(Pid0),
ssh:stop_daemon(Pid1);
Error ->
+ ssh:close(ConnectionRef0),
+ ssh:stop_daemon(Pid0),
ct:fail({unexpected, Error})
end.
@@ -819,11 +948,22 @@ start_subsystem_on_closed_channel(Config) ->
{user_interaction, false},
{user_dir, UserDir}]),
- {ok, ChannelId} = ssh_connection:session_channel(ConnectionRef, infinity),
- ok = ssh_connection:close(ConnectionRef, ChannelId),
+ {ok, ChannelId1} = ssh_connection:session_channel(ConnectionRef, infinity),
+ ok = ssh_connection:close(ConnectionRef, ChannelId1),
+ {error, closed} = ssh_connection:ptty_alloc(ConnectionRef, ChannelId1, []),
+ {error, closed} = ssh_connection:subsystem(ConnectionRef, ChannelId1, "echo_n", 5000),
+ {error, closed} = ssh_connection:exec(ConnectionRef, ChannelId1, "testing1.\n", 5000),
+ {error, closed} = ssh_connection:send(ConnectionRef, ChannelId1, "exit().\n", 5000),
- {error, closed} = ssh_connection:subsystem(ConnectionRef, ChannelId, "echo_n", infinity),
+ %% Test that there could be a gap between close and an operation (Bugfix OTP-14939):
+ {ok, ChannelId2} = ssh_connection:session_channel(ConnectionRef, infinity),
+ ok = ssh_connection:close(ConnectionRef, ChannelId2),
+ timer:sleep(2000),
+ {error, closed} = ssh_connection:ptty_alloc(ConnectionRef, ChannelId2, []),
+ {error, closed} = ssh_connection:subsystem(ConnectionRef, ChannelId2, "echo_n", 5000),
+ {error, closed} = ssh_connection:exec(ConnectionRef, ChannelId2, "testing1.\n", 5000),
+ {error, closed} = ssh_connection:send(ConnectionRef, ChannelId2, "exit().\n", 5000),
ssh:close(ConnectionRef),
ssh:stop_daemon(Pid).
@@ -981,7 +1121,22 @@ start_our_shell(_User, _Peer) ->
%% Don't actually loop, just exit
end).
-ssh_exec(Cmd) ->
+
+ssh_exec_echo(Cmd) ->
+ spawn(fun() ->
+ io:format("echo "++Cmd ++ "\n")
+ end).
+
+ssh_exec_echo(Cmd, User) ->
spawn(fun() ->
- io:format(Cmd ++ "\n")
+ io:format(io_lib:format("echo ~s ~s\n",[User,Cmd]))
end).
+ssh_exec_echo(Cmd, User, _PeerAddr) ->
+ ssh_exec_echo(Cmd,User).
+
+ssh_exec_direct_echo(Cmd) -> {ok, io_lib:format("echo ~s~n",[Cmd])}.
+ssh_exec_direct_echo(Cmd, User) -> {ok, io_lib:format("echo ~s ~s",[User,Cmd])}.
+ssh_exec_direct_echo(Cmd, User, _PeerAddr) -> ssh_exec_direct_echo(Cmd,User).
+
+ssh_exec_direct_echo_error_return(_Cmd) -> {error, {bad}}.
+ssh_exec_direct_echo_error_return_type(_Cmd) -> very_bad.
diff --git a/lib/ssh/test/ssh_engine_SUITE.erl b/lib/ssh/test/ssh_engine_SUITE.erl
index daf93891e9..c131a70973 100644
--- a/lib/ssh/test/ssh_engine_SUITE.erl
+++ b/lib/ssh/test/ssh_engine_SUITE.erl
@@ -55,16 +55,22 @@ basic_tests() ->
init_per_suite(Config) ->
ssh:start(),
?CHECK_CRYPTO(
- case load_engine() of
- {ok,E} ->
- [{engine,E}|Config];
- {error, notsup} ->
- {skip, "Engine not supported on this OpenSSL version"};
- {error, bad_engine_id} ->
- {skip, "Dynamic Engine not supported"};
- Other ->
- ct:log("Engine load failed: ~p",[Other]),
- {fail, "Engine load failed"}
+ case crypto:info_lib() of
+ [{_,_, <<"OpenSSL 1.0.1s-freebsd 1 Mar 2016">>}] ->
+ {skip, "Strange Engine stuff"};
+
+ _ ->
+ case load_engine() of
+ {ok,E} ->
+ [{engine,E}|Config];
+ {error, notsup} ->
+ {skip, "Engine not supported on this OpenSSL version"};
+ {error, bad_engine_id} ->
+ {skip, "Dynamic Engine not supported"};
+ Other ->
+ ct:log("Engine load failed: ~p",[Other]),
+ {fail, "Engine load failed"}
+ end
end
).
diff --git a/lib/ssh/test/ssh_options_SUITE.erl b/lib/ssh/test/ssh_options_SUITE.erl
index 144ec7f8fd..12a85c40aa 100644
--- a/lib/ssh/test/ssh_options_SUITE.erl
+++ b/lib/ssh/test/ssh_options_SUITE.erl
@@ -70,7 +70,8 @@
hostkey_fingerprint_check_sha256/1,
hostkey_fingerprint_check_sha384/1,
hostkey_fingerprint_check_sha512/1,
- hostkey_fingerprint_check_list/1
+ hostkey_fingerprint_check_list/1,
+ save_accepted_host_option/1
]).
%%% Common test callbacks
@@ -124,6 +125,7 @@ all() ->
id_string_own_string_server,
id_string_own_string_server_trail_space,
id_string_random_server,
+ save_accepted_host_option,
{group, hardening_tests}
].
@@ -206,32 +208,23 @@ end_per_group(_, Config) ->
%%--------------------------------------------------------------------
init_per_testcase(_TestCase, Config) ->
ssh:start(),
- Config.
-
-end_per_testcase(TestCase, Config) when TestCase == server_password_option;
- TestCase == server_userpassword_option;
- TestCase == server_pwdfun_option;
- TestCase == server_pwdfun_4_option ->
+ %% Create a clean user_dir
UserDir = filename:join(proplists:get_value(priv_dir, Config), nopubkey),
ssh_test_lib:del_dirs(UserDir),
- end_per_testcase(Config);
-end_per_testcase(_TestCase, Config) ->
- end_per_testcase(Config).
+ file:make_dir(UserDir),
+ [{user_dir,UserDir}|Config].
-end_per_testcase(_Config) ->
+end_per_testcase(_TestCase, Config) ->
ssh:stop(),
ok.
%%--------------------------------------------------------------------
%% Test Cases --------------------------------------------------------
%%--------------------------------------------------------------------
-%%--------------------------------------------------------------------
%%% validate to server that uses the 'password' option
server_password_option(Config) when is_list(Config) ->
- PrivDir = proplists:get_value(priv_dir, Config),
- UserDir = filename:join(PrivDir, nopubkey), % to make sure we don't use public-key-auth
- file:make_dir(UserDir),
+ UserDir = proplists:get_value(user_dir, Config),
SysDir = proplists:get_value(data_dir, Config),
{Pid, Host, Port} = ssh_test_lib:daemon([{system_dir, SysDir},
{user_dir, UserDir},
@@ -262,12 +255,10 @@ server_password_option(Config) when is_list(Config) ->
%%% validate to server that uses the 'password' option
server_userpassword_option(Config) when is_list(Config) ->
- PrivDir = proplists:get_value(priv_dir, Config),
- UserDir = filename:join(PrivDir, nopubkey), % to make sure we don't use public-key-auth
- file:make_dir(UserDir),
+ UserDir = proplists:get_value(user_dir, Config),
SysDir = proplists:get_value(data_dir, Config),
{Pid, Host, Port} = ssh_test_lib:daemon([{system_dir, SysDir},
- {user_dir, PrivDir},
+ {user_dir, UserDir},
{user_passwords, [{"vego", "morot"}]}]),
ConnectionRef =
@@ -297,15 +288,13 @@ server_userpassword_option(Config) when is_list(Config) ->
%%--------------------------------------------------------------------
%%% validate to server that uses the 'pwdfun' option
server_pwdfun_option(Config) ->
- PrivDir = proplists:get_value(priv_dir, Config),
- UserDir = filename:join(PrivDir, nopubkey), % to make sure we don't use public-key-auth
- file:make_dir(UserDir),
+ UserDir = proplists:get_value(user_dir, Config),
SysDir = proplists:get_value(data_dir, Config),
CHKPWD = fun("foo",Pwd) -> Pwd=="bar";
(_,_) -> false
end,
{Pid, Host, Port} = ssh_test_lib:daemon([{system_dir, SysDir},
- {user_dir, PrivDir},
+ {user_dir, UserDir},
{pwdfun,CHKPWD}]),
ConnectionRef =
ssh_test_lib:connect(Host, Port, [{silently_accept_hosts, true},
@@ -335,9 +324,7 @@ server_pwdfun_option(Config) ->
%%--------------------------------------------------------------------
%%% validate to server that uses the 'pwdfun/4' option
server_pwdfun_4_option(Config) ->
- PrivDir = proplists:get_value(priv_dir, Config),
- UserDir = filename:join(PrivDir, nopubkey), % to make sure we don't use public-key-auth
- file:make_dir(UserDir),
+ UserDir = proplists:get_value(user_dir, Config),
SysDir = proplists:get_value(data_dir, Config),
PWDFUN = fun("foo",Pwd,{_,_},undefined) -> Pwd=="bar";
("fie",Pwd,{_,_},undefined) -> {Pwd=="bar",new_state};
@@ -345,7 +332,7 @@ server_pwdfun_4_option(Config) ->
(_,_,_,_) -> false
end,
{Pid, Host, Port} = ssh_test_lib:daemon([{system_dir, SysDir},
- {user_dir, PrivDir},
+ {user_dir, UserDir},
{pwdfun,PWDFUN}]),
ConnectionRef1 =
ssh_test_lib:connect(Host, Port, [{silently_accept_hosts, true},
@@ -395,9 +382,7 @@ server_pwdfun_4_option(Config) ->
%%--------------------------------------------------------------------
server_pwdfun_4_option_repeat(Config) ->
- PrivDir = proplists:get_value(priv_dir, Config),
- UserDir = filename:join(PrivDir, nopubkey), % to make sure we don't use public-key-auth
- file:make_dir(UserDir),
+ UserDir = proplists:get_value(user_dir, Config),
SysDir = proplists:get_value(data_dir, Config),
%% Test that the state works
Parent = self(),
@@ -406,7 +391,7 @@ server_pwdfun_4_option_repeat(Config) ->
(_,P,_,S) -> Parent!{P,S}, {false,S+1}
end,
{Pid, Host, Port} = ssh_test_lib:daemon([{system_dir, SysDir},
- {user_dir, PrivDir},
+ {user_dir, UserDir},
{auth_methods,"keyboard-interactive"},
{pwdfun,PWDFUN}]),
@@ -490,9 +475,7 @@ user_dir_option(Config) ->
%%--------------------------------------------------------------------
%%% validate client that uses the 'ssh_msg_debug_fun' option
ssh_msg_debug_fun_option_client(Config) ->
- PrivDir = proplists:get_value(priv_dir, Config),
- UserDir = filename:join(PrivDir, nopubkey), % to make sure we don't use public-key-auth
- file:make_dir(UserDir),
+ UserDir = proplists:get_value(user_dir, Config),
SysDir = proplists:get_value(data_dir, Config),
{Pid, Host, Port} = ssh_test_lib:daemon([{system_dir, SysDir},
@@ -530,9 +513,7 @@ ssh_msg_debug_fun_option_client(Config) ->
%%--------------------------------------------------------------------
connectfun_disconnectfun_server(Config) ->
- PrivDir = proplists:get_value(priv_dir, Config),
- UserDir = filename:join(PrivDir, nopubkey), % to make sure we don't use public-key-auth
- file:make_dir(UserDir),
+ UserDir = proplists:get_value(user_dir, Config),
SysDir = proplists:get_value(data_dir, Config),
Parent = self(),
@@ -576,9 +557,7 @@ connectfun_disconnectfun_server(Config) ->
%%--------------------------------------------------------------------
connectfun_disconnectfun_client(Config) ->
- PrivDir = proplists:get_value(priv_dir, Config),
- UserDir = filename:join(PrivDir, nopubkey), % to make sure we don't use public-key-auth
- file:make_dir(UserDir),
+ UserDir = proplists:get_value(user_dir, Config),
SysDir = proplists:get_value(data_dir, Config),
Parent = self(),
@@ -607,9 +586,7 @@ connectfun_disconnectfun_client(Config) ->
%%--------------------------------------------------------------------
%%% validate client that uses the 'ssh_msg_debug_fun' option
ssh_msg_debug_fun_option_server(Config) ->
- PrivDir = proplists:get_value(priv_dir, Config),
- UserDir = filename:join(PrivDir, nopubkey), % to make sure we don't use public-key-auth
- file:make_dir(UserDir),
+ UserDir = proplists:get_value(user_dir, Config),
SysDir = proplists:get_value(data_dir, Config),
Parent = self(),
@@ -651,9 +628,7 @@ ssh_msg_debug_fun_option_server(Config) ->
%%--------------------------------------------------------------------
disconnectfun_option_server(Config) ->
- PrivDir = proplists:get_value(priv_dir, Config),
- UserDir = filename:join(PrivDir, nopubkey), % to make sure we don't use public-key-auth
- file:make_dir(UserDir),
+ UserDir = proplists:get_value(user_dir, Config),
SysDir = proplists:get_value(data_dir, Config),
Parent = self(),
@@ -686,9 +661,7 @@ disconnectfun_option_server(Config) ->
%%--------------------------------------------------------------------
disconnectfun_option_client(Config) ->
- PrivDir = proplists:get_value(priv_dir, Config),
- UserDir = filename:join(PrivDir, nopubkey), % to make sure we don't use public-key-auth
- file:make_dir(UserDir),
+ UserDir = proplists:get_value(user_dir, Config),
SysDir = proplists:get_value(data_dir, Config),
Parent = self(),
@@ -720,9 +693,7 @@ disconnectfun_option_client(Config) ->
%%--------------------------------------------------------------------
unexpectedfun_option_server(Config) ->
- PrivDir = proplists:get_value(priv_dir, Config),
- UserDir = filename:join(PrivDir, nopubkey), % to make sure we don't use public-key-auth
- file:make_dir(UserDir),
+ UserDir = proplists:get_value(user_dir, Config),
SysDir = proplists:get_value(data_dir, Config),
Parent = self(),
@@ -763,9 +734,7 @@ unexpectedfun_option_server(Config) ->
%%--------------------------------------------------------------------
unexpectedfun_option_client(Config) ->
- PrivDir = proplists:get_value(priv_dir, Config),
- UserDir = filename:join(PrivDir, nopubkey), % to make sure we don't use public-key-auth
- file:make_dir(UserDir),
+ UserDir = proplists:get_value(user_dir, Config),
SysDir = proplists:get_value(data_dir, Config),
Parent = self(),
@@ -840,14 +809,9 @@ supported_hash(HashAlg) ->
really_do_hostkey_fingerprint_check(Config, HashAlg) ->
- PrivDir = proplists:get_value(priv_dir, Config),
- UserDirServer = filename:join(PrivDir, nopubkey), % to make sure we don't use public-key-auth
- file:make_dir(UserDirServer),
+ UserDir = proplists:get_value(user_dir, Config),
SysDir = proplists:get_value(data_dir, Config),
- UserDirClient =
- ssh_test_lib:create_random_dir(Config), % Ensure no 'known_hosts' disturbs
-
%% All host key fingerprints. Trust that public_key has checked the ssh_hostkey_fingerprint
%% function since that function is used by the ssh client...
FPs0 = [case HashAlg of
@@ -873,7 +837,7 @@ really_do_hostkey_fingerprint_check(Config, HashAlg) ->
%% Start daemon with the public keys that we got fingerprints from
{Pid, Host0, Port} = ssh_test_lib:daemon([{system_dir, SysDir},
- {user_dir, UserDirServer},
+ {user_dir, UserDir},
{password, "morot"}]),
Host = ssh_test_lib:ntoa(Host0),
FP_check_fun = fun(PeerName, FP) ->
@@ -896,7 +860,8 @@ really_do_hostkey_fingerprint_check(Config, HashAlg) ->
end},
{user, "foo"},
{password, "morot"},
- {user_dir, UserDirClient},
+ {user_dir, UserDir},
+ {save_accepted_host, false}, % Ensure no 'known_hosts' disturbs
{user_interaction, false}]),
ssh:stop_daemon(Pid).
@@ -987,9 +952,7 @@ ms_passed(T0) ->
%%--------------------------------------------------------------------
ssh_daemon_minimal_remote_max_packet_size_option(Config) ->
SystemDir = proplists:get_value(data_dir, Config),
- PrivDir = proplists:get_value(priv_dir, Config),
- UserDir = filename:join(PrivDir, nopubkey), % to make sure we don't use public-key-auth
- file:make_dir(UserDir),
+ UserDir = proplists:get_value(user_dir, Config),
{Server, Host, Port} = ssh_test_lib:daemon([{system_dir, SystemDir},
{user_dir, UserDir},
@@ -1314,6 +1277,33 @@ try_to_connect(Connect, Host, Port, Pid, Tref, N) ->
end.
%%--------------------------------------------------------------------
+save_accepted_host_option(Config) ->
+ UserDir = proplists:get_value(user_dir, Config),
+ KnownHosts = filename:join(UserDir, "known_hosts"),
+ SysDir = proplists:get_value(data_dir, Config),
+ {Pid, Host, Port} = ssh_test_lib:daemon([{system_dir, SysDir},
+ {user_dir, UserDir},
+ {user_passwords, [{"vego", "morot"}]}
+ ]),
+ {error,enoent} = file:read_file(KnownHosts),
+
+ {ok,_C1} = ssh:connect(Host, Port, [{silently_accept_hosts, true},
+ {user, "vego"},
+ {password, "morot"},
+ {user_interaction, false},
+ {save_accepted_host, false},
+ {user_dir, UserDir}]),
+ {error,enoent} = file:read_file(KnownHosts),
+
+ {ok,_C2} = ssh:connect(Host, Port, [{silently_accept_hosts, true},
+ {user, "vego"},
+ {password, "morot"},
+ {user_interaction, false},
+ {user_dir, UserDir}]),
+ {ok,_} = file:read_file(KnownHosts),
+ ssh:stop_daemon(Pid).
+
+%%--------------------------------------------------------------------
%% Internal functions ------------------------------------------------
%%--------------------------------------------------------------------
diff --git a/lib/ssh/test/ssh_renegotiate_SUITE.erl b/lib/ssh/test/ssh_renegotiate_SUITE.erl
deleted file mode 100644
index 74bbc291b2..0000000000
--- a/lib/ssh/test/ssh_renegotiate_SUITE.erl
+++ /dev/null
@@ -1,237 +0,0 @@
-%%
-%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2008-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(ssh_renegotiate_SUITE).
-
--include_lib("common_test/include/ct.hrl").
--include("ssh_test_lib.hrl").
-
-%% Note: This directive should only be used in test suites.
--compile(export_all).
-
--define(REKEY_DATA_TMO, 65000).
-%%--------------------------------------------------------------------
-%% Common Test interface functions -----------------------------------
-%%--------------------------------------------------------------------
-
-suite() -> [{ct_hooks,[ts_install_cth]},
- {timetrap,{seconds,40}}].
-
-all() -> [{group,default_algs},
- {group,aes_gcm}
- ].
-
-groups() -> [{default_algs, [], tests()},
- {aes_gcm, [], tests()}
- ].
-
-tests() -> [rekey, rekey_limit, renegotiate1, renegotiate2].
-
-%%--------------------------------------------------------------------
-init_per_suite(Config) ->
- ?CHECK_CRYPTO(Config).
-
-end_per_suite(_Config) ->
- ssh:stop().
-
-%%--------------------------------------------------------------------
-init_per_group(aes_gcm, Config) ->
- case lists:member({client2server,['[email protected]']},
- ssh_transport:supported_algorithms(cipher)) of
- true ->
- [{preferred_algorithms, [{cipher,[{client2server,['[email protected]']},
- {server2client,['[email protected]']}]}]}
- | Config];
- false ->
- {skip, "aes_gcm not supported"}
- end;
-init_per_group(_, Config) ->
- [{preferred_algorithms, ssh:default_algorithms()} | Config].
-
-
-end_per_group(_, Config) ->
- Config.
-
-%%--------------------------------------------------------------------
-init_per_testcase(_TestCase, Config) ->
- ssh:start(),
- Config.
-
-end_per_testcase(_TestCase, _Config) ->
- ssh:stop(),
- ok.
-
-%%--------------------------------------------------------------------
-%% Test Cases --------------------------------------------------------
-%%--------------------------------------------------------------------
-
-%%% Idle timeout test
-rekey() -> [{timetrap,{seconds,90}}].
-
-rekey(Config) ->
- {Pid, Host, Port} =
- ssh_test_lib:std_daemon(Config,
- [{rekey_limit, 0}]),
- ConnectionRef =
- ssh_test_lib:std_connect(Config, Host, Port,
- [{rekey_limit, 0}]),
- Kex1 = ssh_test_lib:get_kex_init(ConnectionRef),
- receive
- after ?REKEY_DATA_TMO ->
- %%By this time rekeying would have been done
- Kex2 = ssh_test_lib:get_kex_init(ConnectionRef),
- false = (Kex2 == Kex1),
- ssh:close(ConnectionRef),
- ssh:stop_daemon(Pid)
- end.
-
-%%--------------------------------------------------------------------
-
-%%% Test rekeying by data volume
-
-rekey_limit() -> [{timetrap,{seconds,400}}].
-
-rekey_limit(Config) ->
- UserDir = proplists:get_value(priv_dir, Config),
- DataFile = filename:join(UserDir, "rekey.data"),
-
- Algs = proplists:get_value(preferred_algorithms, Config),
- {Pid, Host, Port} = ssh_test_lib:std_daemon(Config,[{max_random_length_padding,0},
- {preferred_algorithms,Algs}]),
-
- ConnectionRef = ssh_test_lib:std_connect(Config, Host, Port, [{rekey_limit, 6000},
- {max_random_length_padding,0}]),
- {ok, SftpPid} = ssh_sftp:start_channel(ConnectionRef),
-
- Kex1 = ssh_test_lib:get_kex_init(ConnectionRef),
-
- timer:sleep(?REKEY_DATA_TMO),
- Kex1 = ssh_test_lib:get_kex_init(ConnectionRef),
-
- Data = lists:duplicate(159000,1),
- ok = ssh_sftp:write_file(SftpPid, DataFile, Data),
-
- timer:sleep(?REKEY_DATA_TMO),
- Kex2 = ssh_test_lib:get_kex_init(ConnectionRef),
-
- false = (Kex2 == Kex1),
-
- timer:sleep(?REKEY_DATA_TMO),
- Kex2 = ssh_test_lib:get_kex_init(ConnectionRef),
-
- ok = ssh_sftp:write_file(SftpPid, DataFile, "hi\n"),
-
- timer:sleep(?REKEY_DATA_TMO),
- Kex2 = ssh_test_lib:get_kex_init(ConnectionRef),
-
- false = (Kex2 == Kex1),
-
- timer:sleep(?REKEY_DATA_TMO),
- Kex2 = ssh_test_lib:get_kex_init(ConnectionRef),
-
- ssh_sftp:stop_channel(SftpPid),
- ssh:close(ConnectionRef),
- ssh:stop_daemon(Pid).
-
-%%--------------------------------------------------------------------
-
-%%% Test rekeying with simulataneous send request
-
-renegotiate1(Config) ->
- UserDir = proplists:get_value(priv_dir, Config),
- DataFile = filename:join(UserDir, "renegotiate1.data"),
-
- Algs = proplists:get_value(preferred_algorithms, Config),
- {Pid, Host, DPort} = ssh_test_lib:std_daemon(Config,[{max_random_length_padding,0},
- {preferred_algorithms,Algs}]),
-
- RPort = ssh_test_lib:inet_port(),
- {ok,RelayPid} = ssh_relay:start_link({0,0,0,0}, RPort, Host, DPort),
-
-
- ConnectionRef = ssh_test_lib:std_connect(Config, Host, RPort, [{max_random_length_padding,0}]),
- {ok, SftpPid} = ssh_sftp:start_channel(ConnectionRef),
-
- Kex1 = ssh_test_lib:get_kex_init(ConnectionRef),
-
- {ok, Handle} = ssh_sftp:open(SftpPid, DataFile, [write]),
-
- ok = ssh_sftp:write(SftpPid, Handle, "hi\n"),
-
- ssh_relay:hold(RelayPid, rx, 20, 1000),
- ssh_connection_handler:renegotiate(ConnectionRef),
- spawn(fun() -> ok=ssh_sftp:write(SftpPid, Handle, "another hi\n") end),
-
- timer:sleep(2000),
-
- Kex2 = ssh_test_lib:get_kex_init(ConnectionRef),
-
- false = (Kex2 == Kex1),
-
- ssh_relay:stop(RelayPid),
- ssh_sftp:stop_channel(SftpPid),
- ssh:close(ConnectionRef),
- ssh:stop_daemon(Pid).
-
-%%--------------------------------------------------------------------
-
-%%% Test rekeying with inflight messages from peer
-
-renegotiate2(Config) ->
- UserDir = proplists:get_value(priv_dir, Config),
- DataFile = filename:join(UserDir, "renegotiate2.data"),
-
- Algs = proplists:get_value(preferred_algorithms, Config),
- {Pid, Host, DPort} = ssh_test_lib:std_daemon(Config,[{max_random_length_padding,0},
- {preferred_algorithms,Algs}]),
-
- RPort = ssh_test_lib:inet_port(),
- {ok,RelayPid} = ssh_relay:start_link({0,0,0,0}, RPort, Host, DPort),
-
- ConnectionRef = ssh_test_lib:std_connect(Config, Host, RPort, [{max_random_length_padding,0}]),
- {ok, SftpPid} = ssh_sftp:start_channel(ConnectionRef),
-
- Kex1 = ssh_test_lib:get_kex_init(ConnectionRef),
-
- {ok, Handle} = ssh_sftp:open(SftpPid, DataFile, [write]),
-
- ok = ssh_sftp:write(SftpPid, Handle, "hi\n"),
-
- ssh_relay:hold(RelayPid, rx, 20, infinity),
- spawn(fun() -> ok=ssh_sftp:write(SftpPid, Handle, "another hi\n") end),
- %% need a small pause here to ensure ssh_sftp:write is executed
- ct:sleep(10),
- ssh_connection_handler:renegotiate(ConnectionRef),
- ssh_relay:release(RelayPid, rx),
-
- timer:sleep(2000),
-
- Kex2 = ssh_test_lib:get_kex_init(ConnectionRef),
-
- false = (Kex2 == Kex1),
-
- ssh_relay:stop(RelayPid),
- ssh_sftp:stop_channel(SftpPid),
- ssh:close(ConnectionRef),
- ssh:stop_daemon(Pid).
-
-%%--------------------------------------------------------------------
-%% Internal functions ------------------------------------------------
-%%--------------------------------------------------------------------
diff --git a/lib/ssh/test/ssh_renegotiate_SUITE_data/id_dsa b/lib/ssh/test/ssh_renegotiate_SUITE_data/id_dsa
deleted file mode 100644
index d306f8b26e..0000000000
--- a/lib/ssh/test/ssh_renegotiate_SUITE_data/id_dsa
+++ /dev/null
@@ -1,13 +0,0 @@
------BEGIN DSA PRIVATE KEY-----
-MIIBvAIBAAKBgQDfi2flSTZZofwT4yQT0NikX/LGNT7UPeB/XEWe/xovEYCElfaQ
-APFixXvEgXwoojmZ5kiQRKzLM39wBP0jPERLbnZXfOOD0PDnw0haMh7dD7XKVMod
-/EigVgHf/qBdM2M8yz1s/rRF7n1UpLSypziKjkzCm7JoSQ2zbWIPdmBIXwIVAMgP
-kpr7Sq3O7sHdb8D601DRjoExAoGAMOQxDfB2Fd8ouz6G96f/UOzRMI/Kdv8kYYKW
-JIGY+pRYrLPyYzUeJznwZreOJgrczAX+luHnKFWJ2Dnk5CyeXk67Wsr7pJ/4MBMD
-OKeIS0S8qoSBN8+Krp79fgA+yS3IfqbkJLtLu4EBaCX4mKQIX4++k44d4U5lc8pt
-+9hlEI8CgYEAznKxx9kyC6bVo7LUYKaGhofRFt0SYFc5PVmT2VUGRs1R6+6DPD+e
-uEO6IhFct7JFSRbP9p0JD4Uk+3zlZF+XX6b2PsZkeV8f/02xlNGUSmEzCSiNg1AX
-Cy/WusYhul0MncWCHMcOZB5rIvU/aP5EJJtn3xrRaz6u0SThF6AnT34CFQC63czE
-ZU8w8Q+H7z0j+a+70x2iAw==
------END DSA PRIVATE KEY-----
-
diff --git a/lib/ssh/test/ssh_renegotiate_SUITE_data/id_rsa b/lib/ssh/test/ssh_renegotiate_SUITE_data/id_rsa
deleted file mode 100644
index 9d7e0dd5fb..0000000000
--- a/lib/ssh/test/ssh_renegotiate_SUITE_data/id_rsa
+++ /dev/null
@@ -1,15 +0,0 @@
------BEGIN RSA PRIVATE KEY-----
-MIICXAIBAAKBgQD1OET+3O/Bvj/dtjxDTXmj1oiJt4sIph5kGy0RfjoPrZfaS+CU
-DhakCmS6t2ivxWFgtpKWaoGMZMJqWj6F6ZsumyFl3FPBtujwY/35cgifrI9Ns4Tl
-zR1uuengNBmV+WRQ5cd9F2qS6Z8aDQihzt0r8JUqLcK+VQbrmNzboCCQQwIDAQAB
-AoGAPQEyqPTt8JUT7mRXuaacjFXiweAXhp9NEDpyi9eLOjtFe9lElZCrsUOkq47V
-TGUeRKEm9qSodfTbKPoqc8YaBJGJPhUaTAcha+7QcDdfHBvIsgxvU7ePVnlpXRp3
-CCUEMPhlnx6xBoTYP+fRU0e3+xJIPVyVCqX1jAdUMkzfRoECQQD6ux7B1QJAIWyK
-SGkbDUbBilNmzCFNgIpOP6PA+bwfi5d16diTpra5AX09keQABAo/KaP1PdV8Vg0p
-z4P3A7G3AkEA+l+AKG6m0kQTTBMJDqOdVPYwe+5GxunMaqmhokpEbuGsrZBl5Dvd
-WpcBjR7jmenrhKZRIuA+Fz5HPo/UQJPl1QJBAKxstDkeED8j/S2XoFhPKAJ+6t39
-sUVICVTIZQeXdmzHJXCcUSkw8+WEhakqw/3SyW0oaK2FSWQJFWJUZ+8eJj8CQEh3
-xeduB5kKnS9CvzdeghZqX6QvVosSdtlUmfUYW/BgH5PpHKTP8wTaeld3XldZTpMJ
-dKiMkUw2+XYROVUrubUCQD+Na1LhULlpn4ISEtIEfqpdlUhxDgO15Wg8USmsng+x
-ICliVOSQtwaZjm8kwaFt0W7XnpnDxbRs37vIEbIMWak=
------END RSA PRIVATE KEY-----
diff --git a/lib/ssh/test/ssh_renegotiate_SUITE_data/ssh_host_dsa_key b/lib/ssh/test/ssh_renegotiate_SUITE_data/ssh_host_dsa_key
deleted file mode 100644
index 51ab6fbd88..0000000000
--- a/lib/ssh/test/ssh_renegotiate_SUITE_data/ssh_host_dsa_key
+++ /dev/null
@@ -1,13 +0,0 @@
------BEGIN DSA PRIVATE KEY-----
-MIIBuwIBAAKBgQCClaHzE2ul0gKSUxah5W0W8UiJLy4hXngKEqpaUq9SSdVdY2LK
-wVfKH1gt5iuaf1FfzOhsIC9G/GLnjYttXZc92cv/Gfe3gR+s0ni2++MX+T++mE/Q
-diltXv/Hp27PybS67SmiFW7I+RWnT2OKlMPtw2oUuKeztCe5UWjaj/y5FQIVAPLA
-l9RpiU30Z87NRAHY3NTRaqtrAoGANMRxw8UfdtNVR0CrQj3AgPaXOGE4d+G4Gp4X
-skvnCHycSVAjtYxebUkzUzt5Q6f/IabuLUdge3gXrc8BetvrcKbp+XZgM0/Vj2CF
-Ymmy3in6kzGZq7Fw1sZaku6AOU8vLa5woBT2vAcHLLT1bLAzj7viL048T6MfjrOP
-ef8nHvACgYBhDWFQJ1mf99sg92LalVq1dHLmVXb3PTJDfCO/Gz5NFmj9EZbAtdah
-/XcF3DeRF+eEoz48wQF/ExVxSMIhLdL+o+ElpVhlM7Yii+T7dPhkQfEul6zZXu+U
-ykSTXYUbtsfTNRFQGBW2/GfnEc0mnIxfn9v10NEWMzlq5z9wT9P0CgIVAN4wtL5W
-Lv62jKcdskxNyz2NQoBx
------END DSA PRIVATE KEY-----
-
diff --git a/lib/ssh/test/ssh_renegotiate_SUITE_data/ssh_host_dsa_key.pub b/lib/ssh/test/ssh_renegotiate_SUITE_data/ssh_host_dsa_key.pub
deleted file mode 100644
index 4dbb1305b0..0000000000
--- a/lib/ssh/test/ssh_renegotiate_SUITE_data/ssh_host_dsa_key.pub
+++ /dev/null
@@ -1,11 +0,0 @@
----- BEGIN SSH2 PUBLIC KEY ----
-AAAAB3NzaC1kc3MAAACBAIKVofMTa6XSApJTFqHlbRbxSIkvLiFeeAoSqlpSr1JJ1V1j
-YsrBV8ofWC3mK5p/UV/M6GwgL0b8YueNi21dlz3Zy/8Z97eBH6zSeLb74xf5P76YT9B2
-KW1e/8enbs/JtLrtKaIVbsj5FadPY4qUw+3DahS4p7O0J7lRaNqP/LkVAAAAFQDywJfU
-aYlN9GfOzUQB2NzU0WqrawAAAIA0xHHDxR9201VHQKtCPcCA9pc4YTh34bganheyS+cI
-fJxJUCO1jF5tSTNTO3lDp/8hpu4tR2B7eBetzwF62+twpun5dmAzT9WPYIViabLeKfqT
-MZmrsXDWxlqS7oA5Ty8trnCgFPa8BwcstPVssDOPu+IvTjxPox+Os495/yce8AAAAIBh
-DWFQJ1mf99sg92LalVq1dHLmVXb3PTJDfCO/Gz5NFmj9EZbAtdah/XcF3DeRF+eEoz48
-wQF/ExVxSMIhLdL+o+ElpVhlM7Yii+T7dPhkQfEul6zZXu+UykSTXYUbtsfTNRFQGBW2
-/GfnEc0mnIxfn9v10NEWMzlq5z9wT9P0Cg==
----- END SSH2 PUBLIC KEY ----
diff --git a/lib/ssh/test/ssh_renegotiate_SUITE_data/ssh_host_rsa_key b/lib/ssh/test/ssh_renegotiate_SUITE_data/ssh_host_rsa_key
deleted file mode 100644
index 79968bdd7d..0000000000
--- a/lib/ssh/test/ssh_renegotiate_SUITE_data/ssh_host_rsa_key
+++ /dev/null
@@ -1,16 +0,0 @@
------BEGIN RSA PRIVATE KEY-----
-MIICXQIBAAKBgQDCZX+4FBDwZIh9y/Uxee1VJnEXlowpz2yDKwj8semM4q843337
-zbNfxHmladB1lpz2NqyxI175xMIJuDxogyZdsOxGnFAzAnthR4dqL/RWRWzjaxSB
-6IAO9SPYVVlrpZ+1hsjLW79fwXK/yc8VdhRuWTeQiRgYY2ek8+OKbOqz4QIDAQAB
-AoGANmvJzJO5hkLuvyDZHKfAnGTtpifcR1wtSa9DjdKUyn8vhKF0mIimnbnYQEmW
-NUUb3gXCZLi9PvkpRSVRrASDOZwcjoU/Kvww163vBUVb2cOZfFhyn6o2Sk88Tt++
-udH3hdjpf9i7jTtUkUe+QYPsia+wgvvrmn4QrahLAH86+kECQQDx5gFeXTME3cnW
-WMpFz3PPumduzjqgqMMWEccX4FtQkMX/gyGa5UC7OHFyh0N/gSWvPbRHa8A6YgIt
-n8DO+fh5AkEAzbqX4DOn8NY6xJIi42q7l/2jIA0RkB6P7YugW5NblhqBZ0XDnpA5
-sMt+rz+K07u9XZtxgh1xi7mNfwY6lEAMqQJBAJBEauCKmRj35Z6OyeQku59SPsnY
-+SJEREVvSNw2lH9SOKQQ4wPsYlTGbvKtNVZgAcen91L5MmYfeckYE/fdIZECQQCt
-64zxsTnM1I8iFxj/gP/OYlJBikrKt8udWmjaghzvLMEw+T2DExJyb9ZNeT53+UMB
-m6O+B/4xzU/djvp+0hbhAkAemIt+rA5kTmYlFndhpvzkSSM8a2EXsO4XIPgGWCTT
-tQKS/tTly0ADMjN/TVy11+9d6zcqadNVuHXHGtR4W0GR
------END RSA PRIVATE KEY-----
-
diff --git a/lib/ssh/test/ssh_renegotiate_SUITE_data/ssh_host_rsa_key.pub b/lib/ssh/test/ssh_renegotiate_SUITE_data/ssh_host_rsa_key.pub
deleted file mode 100644
index 75d2025c71..0000000000
--- a/lib/ssh/test/ssh_renegotiate_SUITE_data/ssh_host_rsa_key.pub
+++ /dev/null
@@ -1,5 +0,0 @@
----- BEGIN SSH2 PUBLIC KEY ----
-AAAAB3NzaC1yc2EAAAADAQABAAAAgQDCZX+4FBDwZIh9y/Uxee1VJnEXlowpz2yDKwj8
-semM4q843337zbNfxHmladB1lpz2NqyxI175xMIJuDxogyZdsOxGnFAzAnthR4dqL/RW
-RWzjaxSB6IAO9SPYVVlrpZ+1hsjLW79fwXK/yc8VdhRuWTeQiRgYY2ek8+OKbOqz4Q==
----- END SSH2 PUBLIC KEY ----
diff --git a/lib/ssh/test/ssh_sup_SUITE.erl b/lib/ssh/test/ssh_sup_SUITE.erl
index 3920a1c592..b145066c36 100644
--- a/lib/ssh/test/ssh_sup_SUITE.erl
+++ b/lib/ssh/test/ssh_sup_SUITE.erl
@@ -42,7 +42,9 @@ suite() ->
all() ->
[default_tree, sshc_subtree, sshd_subtree, sshd_subtree_profile,
- killed_acceptor_restarts].
+ killed_acceptor_restarts,
+ shell_channel_tree
+ ].
groups() ->
[].
@@ -199,8 +201,6 @@ killed_acceptor_restarts(Config) ->
Port2 = ssh_test_lib:daemon_port(DaemonPid2),
true = (Port /= Port2),
- ct:pal("~s",[lists:flatten(ssh_info:string())]),
-
{ok,[{AccPid,ListenAddr,Port}]} = acceptor_pid(DaemonPid),
{ok,[{AccPid2,ListenAddr,Port2}]} = acceptor_pid(DaemonPid2),
@@ -214,37 +214,143 @@ killed_acceptor_restarts(Config) ->
{user_dir, UserDir}]),
[{client_version,_}] = ssh:connection_info(C1,[client_version]),
+ ct:log("~s",[lists:flatten(ssh_info:string())]),
+
%% Make acceptor restart:
exit(AccPid, kill),
+ ?wait_match(undefined, process_info(AccPid)),
- %% Check it is a new acceptor:
- {ok,[{AccPid1,ListenAddr,Port}]} = acceptor_pid(DaemonPid),
- true = (AccPid /= AccPid1),
- true = (AccPid2 /= AccPid1),
+ %% Check it is a new acceptor and wait if it is not:
+ ?wait_match({ok,[{AccPid1,ListenAddr,Port}]}, AccPid1=/=AccPid,
+ acceptor_pid(DaemonPid),
+ AccPid1,
+ 500, 30),
+
+ true = (AccPid1 =/= AccPid2),
%% Connect second client and check it is alive:
- {ok,C2} = ssh:connect("localhost", Port, [{silently_accept_hosts, true},
- {user_interaction, false},
- {user, ?USER},
- {password, ?PASSWD},
- {user_dir, UserDir}]),
+ C2 =
+ case ssh:connect("localhost", Port, [{silently_accept_hosts, true},
+ {user_interaction, false},
+ {user, ?USER},
+ {password, ?PASSWD},
+ {user_dir, UserDir}]) of
+ {ok,_C2} ->
+ _C2;
+ _Other ->
+ ct:log("new connect failed: ~p~n~n~s",[_Other,lists:flatten(ssh_info:string())]),
+ ct:fail("Re-connect failed!", [])
+ end,
+
[{client_version,_}] = ssh:connection_info(C2,[client_version]),
- ct:pal("~s",[lists:flatten(ssh_info:string())]),
+ ct:log("~s",[lists:flatten(ssh_info:string())]),
%% Check first client is still alive:
[{client_version,_}] = ssh:connection_info(C1,[client_version]),
ok = ssh:stop_daemon(DaemonPid2),
- timer:sleep(15000),
+ ?wait_match(undefined, process_info(DaemonPid2), 1000, 30),
[{client_version,_}] = ssh:connection_info(C1,[client_version]),
[{client_version,_}] = ssh:connection_info(C2,[client_version]),
ok = ssh:stop_daemon(DaemonPid),
- timer:sleep(15000),
+ ?wait_match(undefined, process_info(DaemonPid), 1000, 30),
{error,closed} = ssh:connection_info(C1,[client_version]),
{error,closed} = ssh:connection_info(C2,[client_version]).
+
+%%-------------------------------------------------------------------------
+shell_channel_tree(Config) ->
+ PrivDir = proplists:get_value(priv_dir, Config),
+ UserDir = filename:join(PrivDir, nopubkey), % to make sure we don't use public-key-auth
+ file:make_dir(UserDir),
+ SysDir = proplists:get_value(data_dir, Config),
+ TimeoutShell =
+ fun() ->
+ io:format("TimeoutShell started!~n",[]),
+ timer:sleep(5000),
+ ct:log("~p TIMEOUT!",[self()])
+ end,
+ {Daemon, Host, Port} = ssh_test_lib:daemon([{system_dir, SysDir},
+ {user_dir, UserDir},
+ {password, "morot"},
+ {shell, fun(_User) ->
+ spawn(TimeoutShell)
+ end
+ }
+ ]),
+ ConnectionRef = ssh_test_lib:connect(Host, Port, [{silently_accept_hosts, true},
+ {user, "foo"},
+ {password, "morot"},
+ {user_interaction, true},
+ {user_dir, UserDir}]),
+
+ [ChannelSup|_] = Sups0 = chk_empty_con_daemon(Daemon),
+ {ok, ChannelId0} = ssh_connection:session_channel(ConnectionRef, infinity),
+ ok = ssh_connection:shell(ConnectionRef,ChannelId0),
+
+ ?wait_match([{_, GroupPid,worker,[ssh_channel]}],
+ supervisor:which_children(ChannelSup),
+ [GroupPid]),
+ {links,GroupLinks} = erlang:process_info(GroupPid, links),
+ [ShellPid] = GroupLinks--[ChannelSup],
+ ct:log("GroupPid = ~p, ShellPid = ~p",[GroupPid,ShellPid]),
+
+ receive
+ {ssh_cm,ConnectionRef, {data, ChannelId0, 0, <<"TimeoutShell started!\r\n">>}} ->
+ receive
+ %%---- wait for the subsystem to terminate
+ {ssh_cm,ConnectionRef,{closed,ChannelId0}} ->
+ ct:log("Subsystem terminated",[]),
+ case {chk_empty_con_daemon(Daemon),
+ process_info(GroupPid),
+ process_info(ShellPid)} of
+ {Sups0, undefined, undefined} ->
+ %% SUCCESS
+ ssh:stop_daemon(Daemon);
+ {Sups0, _, undefined} ->
+ ssh:stop_daemon(Daemon),
+ ct:fail("Group proc lives!");
+ {Sups0, undefined, _} ->
+ ssh:stop_daemon(Daemon),
+ ct:fail("Shell proc lives!");
+ _ ->
+ ssh:stop_daemon(Daemon),
+ ct:fail("Sup tree changed!")
+ end
+ after 10000 ->
+ ssh:close(ConnectionRef),
+ ssh:stop_daemon(Daemon),
+ ct:fail("CLI Timeout")
+ end
+ after 10000 ->
+ ssh:close(ConnectionRef),
+ ssh:stop_daemon(Daemon),
+ ct:fail("CLI Timeout")
+ end.
+
+
+chk_empty_con_daemon(Daemon) ->
+ ?wait_match([{_,SubSysSup, supervisor,[ssh_subsystem_sup]},
+ {{ssh_acceptor_sup,_,_,_}, AccSup, supervisor,[ssh_acceptor_sup]}],
+ supervisor:which_children(Daemon),
+ [SubSysSup,AccSup]),
+ ?wait_match([{{server,ssh_connection_sup, _,_},
+ ConnectionSup, supervisor,
+ [ssh_connection_sup]},
+ {{server,ssh_channel_sup,_ ,_},
+ ChannelSup,supervisor,
+ [ssh_channel_sup]}],
+ supervisor:which_children(SubSysSup),
+ [ConnectionSup,ChannelSup]),
+ ?wait_match([{{ssh_acceptor_sup,_,_,_},_,worker,[ssh_acceptor]}],
+ supervisor:which_children(AccSup)),
+ ?wait_match([{_, _, worker,[ssh_connection_handler]}],
+ supervisor:which_children(ConnectionSup)),
+ ?wait_match([], supervisor:which_children(ChannelSup)),
+ [ChannelSup, ConnectionSup, SubSysSup, AccSup].
+
%%-------------------------------------------------------------------------
%% Help functions
%%-------------------------------------------------------------------------
diff --git a/lib/ssh/test/ssh_test_lib.erl b/lib/ssh/test/ssh_test_lib.erl
index 2d7bf75847..57ae2dbac2 100644
--- a/lib/ssh/test/ssh_test_lib.erl
+++ b/lib/ssh/test/ssh_test_lib.erl
@@ -56,7 +56,9 @@ daemon(Host, Port, Options) ->
ct:log("~p:~p Calling ssh:daemon(~p, ~p, ~p)",[?MODULE,?LINE,Host,Port,Options]),
case ssh:daemon(Host, Port, Options) of
{ok, Pid} ->
- {ok,L} = ssh:daemon_info(Pid),
+ R = ssh:daemon_info(Pid),
+ ct:log("~p:~p ssh:daemon_info(~p) ->~n ~p",[?MODULE,?LINE,Pid,R]),
+ {ok,L} = R,
ListenPort = proplists:get_value(port, L),
ListenIP = proplists:get_value(ip, L),
{Pid, ListenIP, ListenPort};
@@ -199,15 +201,17 @@ init_io_server(TestCase) ->
loop_io_server(TestCase, Buff0) ->
receive
- {input, TestCase, Line} ->
+ {input, TestCase, Line} = _INP ->
+ %%ct:log("io_server ~p:~p ~p got ~p",[?MODULE,?LINE,self(),_INP]),
loop_io_server(TestCase, Buff0 ++ [Line]);
- {io_request, From, ReplyAs, Request} ->
+ {io_request, From, ReplyAs, Request} = _REQ->
+ %%ct:log("io_server ~p:~p ~p got ~p",[?MODULE,?LINE,self(),_REQ]),
{ok, Reply, Buff} = io_request(Request, TestCase, From,
ReplyAs, Buff0),
io_reply(From, ReplyAs, Reply),
loop_io_server(TestCase, Buff);
{'EXIT',_, _} = _Exit ->
-%% ct:log("ssh_test_lib:loop_io_server/2 got ~p",[_Exit]),
+ ct:log("ssh_test_lib:loop_io_server/2 got ~p",[_Exit]),
ok
after
30000 -> ct:fail("timeout ~p:~p",[?MODULE,?LINE])
diff --git a/lib/ssh/test/ssh_test_lib.hrl b/lib/ssh/test/ssh_test_lib.hrl
index eaf856e6e8..4b6579bd71 100644
--- a/lib/ssh/test/ssh_test_lib.hrl
+++ b/lib/ssh/test/ssh_test_lib.hrl
@@ -16,12 +16,12 @@
%%-------------------------------------------------------------------------
%% Help macro
%%-------------------------------------------------------------------------
--define(wait_match(Pattern, FunctionCall, Bind, Timeout, Ntries),
+-define(wait_match(Pattern, Guard, FunctionCall, Bind, Timeout, Ntries),
Bind =
(fun() ->
F = fun(N, F1) ->
case FunctionCall of
- Pattern -> Bind;
+ Pattern when Guard -> Bind;
_ when N>0 ->
ct:pal("Must sleep ~p ms at ~p:~p",[Timeout,?MODULE,?LINE]),
timer:sleep(Timeout),
@@ -34,6 +34,9 @@
end)()
).
+-define(wait_match(Pattern, FunctionCall, Bind, Timeout, Ntries),
+ ?wait_match(Pattern, true, FunctionCall, Bind, Timeout, Ntries)).
+
-define(wait_match(Pattern, FunctionCall, Timeout, Ntries), ?wait_match(Pattern, FunctionCall, ok, Timeout, Ntries)).
-define(wait_match(Pattern, FunctionCall, Bind), ?wait_match(Pattern, FunctionCall, Bind, 500, 10) ).
diff --git a/lib/ssh/test/ssh_to_openssh_SUITE.erl b/lib/ssh/test/ssh_to_openssh_SUITE.erl
index b20764ce47..9df404d7ed 100644
--- a/lib/ssh/test/ssh_to_openssh_SUITE.erl
+++ b/lib/ssh/test/ssh_to_openssh_SUITE.erl
@@ -48,19 +48,9 @@ all() ->
end.
groups() ->
- [{erlang_client, [], [erlang_shell_client_openssh_server,
- erlang_client_openssh_server_exec_compressed,
- erlang_client_openssh_server_setenv,
- erlang_client_openssh_server_publickey_dsa,
- erlang_client_openssh_server_publickey_rsa,
- erlang_client_openssh_server_password,
- erlang_client_openssh_server_kexs,
- erlang_client_openssh_server_nonexistent_subsystem,
- erlang_client_openssh_server_renegotiate
+ [{erlang_client, [], [erlang_shell_client_openssh_server
]},
- {erlang_server, [], [erlang_server_openssh_client_public_key_dsa,
- erlang_server_openssh_client_public_key_rsa,
- erlang_server_openssh_client_renegotiate
+ {erlang_server, [], [erlang_server_openssh_client_renegotiate
]}
].
@@ -100,15 +90,6 @@ end_per_group(_, Config) ->
Config.
-init_per_testcase(erlang_server_openssh_client_public_key_dsa, Config) ->
- chk_key(sshc, 'ssh-dss', ".ssh/id_dsa", Config);
-init_per_testcase(erlang_server_openssh_client_public_key_rsa, Config) ->
- chk_key(sshc, 'ssh-rsa', ".ssh/id_rsa", Config);
-init_per_testcase(erlang_client_openssh_server_publickey_dsa, Config) ->
- chk_key(sshd, 'ssh-dss', ".ssh/id_dsa", Config);
-init_per_testcase(erlang_client_openssh_server_publickey_rsa, Config) ->
- chk_key(sshd, 'ssh-rsa', ".ssh/id_rsa", Config);
-
init_per_testcase(erlang_server_openssh_client_renegotiate, Config) ->
case os:type() of
{unix,_} -> ssh:start(), Config;
@@ -122,27 +103,6 @@ end_per_testcase(_TestCase, _Config) ->
ssh:stop(),
ok.
-
-chk_key(Pgm, Name, File, Config) ->
- case ssh_test_lib:openssh_supports(Pgm, public_key, Name) of
- false ->
- {skip,lists:concat(["openssh client does not support ",Name])};
- true ->
- {ok,[[Home]]} = init:get_argument(home),
- KeyFile = filename:join(Home, File),
- case file:read_file(KeyFile) of
- {ok, Pem} ->
- case public_key:pem_decode(Pem) of
- [{_,_, not_encrypted}] ->
- init_per_testcase('__default__',Config);
- _ ->
- {skip, {error, "Has pass phrase can not be used by automated test case"}}
- end;
- _ ->
- {skip, lists:concat(["no ~/",File])}
- end
- end.
-
%%--------------------------------------------------------------------
%% Test Cases --------------------------------------------------------
%%--------------------------------------------------------------------
@@ -160,219 +120,6 @@ erlang_shell_client_openssh_server(Config) when is_list(Config) ->
receive_logout(),
receive_normal_exit(Shell).
-%--------------------------------------------------------------------
-erlang_client_openssh_server_exec() ->
- [{doc, "Test api function ssh_connection:exec"}].
-
-erlang_client_openssh_server_exec(Config) when is_list(Config) ->
- ConnectionRef = ssh_test_lib:connect(?SSH_DEFAULT_PORT, [{silently_accept_hosts, true},
- {user_interaction, false}]),
- {ok, ChannelId0} = ssh_connection:session_channel(ConnectionRef, infinity),
- success = ssh_connection:exec(ConnectionRef, ChannelId0,
- "echo testing", infinity),
- Data0 = {ssh_cm, ConnectionRef, {data, ChannelId0, 0, <<"testing\n">>}},
- case ssh_test_lib:receive_exec_result(Data0) of
- expected ->
- ssh_test_lib:receive_exec_end(ConnectionRef, ChannelId0);
- {unexpected_msg,{ssh_cm, ConnectionRef, {exit_status, ChannelId0, 0}}
- = ExitStatus0} ->
- ct:log("0: Collected data ~p", [ExitStatus0]),
- ssh_test_lib:receive_exec_result(Data0,
- ConnectionRef, ChannelId0);
- Other0 ->
- ct:fail(Other0)
- end,
-
- {ok, ChannelId1} = ssh_connection:session_channel(ConnectionRef, infinity),
- success = ssh_connection:exec(ConnectionRef, ChannelId1,
- "echo testing1", infinity),
- Data1 = {ssh_cm, ConnectionRef, {data, ChannelId1, 0, <<"testing1\n">>}},
- case ssh_test_lib:receive_exec_result(Data1) of
- expected ->
- ssh_test_lib:receive_exec_end(ConnectionRef, ChannelId1);
- {unexpected_msg,{ssh_cm, ConnectionRef, {exit_status, ChannelId1, 0}}
- = ExitStatus1} ->
- ct:log("0: Collected data ~p", [ExitStatus1]),
- ssh_test_lib:receive_exec_result(Data1,
- ConnectionRef, ChannelId1);
- Other1 ->
- ct:fail(Other1)
- end.
-
-%%--------------------------------------------------------------------
-erlang_client_openssh_server_exec_compressed() ->
- [{doc, "Test that compression option works"}].
-
-erlang_client_openssh_server_exec_compressed(Config) when is_list(Config) ->
- CompressAlgs = [zlib, '[email protected]',none],
- case ssh_test_lib:ssh_supports(CompressAlgs, compression) of
- {false,L} ->
- {skip, io_lib:format("~p compression is not supported",[L])};
-
- true ->
- ConnectionRef = ssh_test_lib:connect(?SSH_DEFAULT_PORT, [{silently_accept_hosts, true},
- {user_interaction, false},
- {preferred_algorithms,
- [{compression,CompressAlgs}]}]),
- {ok, ChannelId} = ssh_connection:session_channel(ConnectionRef, infinity),
- success = ssh_connection:exec(ConnectionRef, ChannelId,
- "echo testing", infinity),
- Data = {ssh_cm, ConnectionRef, {data, ChannelId, 0, <<"testing\n">>}},
- case ssh_test_lib:receive_exec_result(Data) of
- expected ->
- ssh_test_lib:receive_exec_end(ConnectionRef, ChannelId);
- {unexpected_msg,{ssh_cm, ConnectionRef,
- {exit_status, ChannelId, 0}} = ExitStatus} ->
- ct:log("0: Collected data ~p", [ExitStatus]),
- ssh_test_lib:receive_exec_result(Data, ConnectionRef, ChannelId);
- Other ->
- ct:fail(Other)
- end
- end.
-
-%%--------------------------------------------------------------------
-erlang_client_openssh_server_kexs() ->
- [{doc, "Test that we can connect with different KEXs."}].
-
-erlang_client_openssh_server_kexs(Config) when is_list(Config) ->
- KexAlgos = try proplists:get_value(kex, proplists:get_value(common_algs,Config))
- catch _:_ -> []
- end,
- comment(KexAlgos),
- case KexAlgos of
- [] -> {skip, "No common kex algorithms"};
- _ ->
- Success =
- lists:foldl(
- fun(Kex, Acc) ->
- ConnectionRef =
- ssh_test_lib:connect(?SSH_DEFAULT_PORT, [{silently_accept_hosts, true},
- {user_interaction, false},
- {preferred_algorithms,
- [{kex,[Kex]}]}]),
-
- {ok, ChannelId} =
- ssh_connection:session_channel(ConnectionRef, infinity),
- success =
- ssh_connection:exec(ConnectionRef, ChannelId,
- "echo testing", infinity),
-
- ExpectedData = {ssh_cm, ConnectionRef, {data, ChannelId, 0, <<"testing\n">>}},
- case ssh_test_lib:receive_exec_result(ExpectedData) of
- expected ->
- ssh_test_lib:receive_exec_end(ConnectionRef, ChannelId),
- Acc;
- {unexpected_msg,{ssh_cm, ConnectionRef,
- {exit_status, ChannelId, 0}} = ExitStatus} ->
- ct:log("0: Collected data ~p", [ExitStatus]),
- ssh_test_lib:receive_exec_result(ExpectedData, ConnectionRef, ChannelId),
- Acc;
- Other ->
- ct:log("~p failed: ~p",[Kex,Other]),
- false
- end
- end, true, KexAlgos),
- case Success of
- true ->
- ok;
- false ->
- {fail, "Kex failed for one or more algos"}
- end
- end.
-
-%%--------------------------------------------------------------------
-erlang_client_openssh_server_setenv() ->
- [{doc, "Test api function ssh_connection:setenv"}].
-
-erlang_client_openssh_server_setenv(Config) when is_list(Config) ->
- ConnectionRef =
- ssh_test_lib:connect(?SSH_DEFAULT_PORT, [{silently_accept_hosts, true},
- {user_interaction, false}]),
- {ok, ChannelId} =
- ssh_connection:session_channel(ConnectionRef, infinity),
- Env = case ssh_connection:setenv(ConnectionRef, ChannelId,
- "ENV_TEST", "testing_setenv",
- infinity) of
- success ->
- <<"tesing_setenv\n">>;
- failure ->
- <<"\n">>
- end,
- success = ssh_connection:exec(ConnectionRef, ChannelId,
- "echo $ENV_TEST", infinity),
- Data = {ssh_cm, ConnectionRef, {data, ChannelId, 0, Env}},
- case ssh_test_lib:receive_exec_result(Data) of
- expected ->
- ssh_test_lib:receive_exec_end(ConnectionRef, ChannelId);
- {unexpected_msg,{ssh_cm, ConnectionRef,
- {data,0,1, UnxpectedData}}} ->
- %% Some os may return things as
- %% ENV_TEST: Undefined variable.\n"
- ct:log("UnxpectedData: ~p", [UnxpectedData]),
- ssh_test_lib:receive_exec_end(ConnectionRef, ChannelId);
- {unexpected_msg,{ssh_cm, ConnectionRef, {exit_status, ChannelId, 0}}
- = ExitStatus} ->
- ct:log("0: Collected data ~p", [ExitStatus]),
- ssh_test_lib:receive_exec_result(Data,
- ConnectionRef, ChannelId);
- Other ->
- ct:fail(Other)
- end.
-
-%%--------------------------------------------------------------------
-
-%% setenv not meaningfull on erlang ssh daemon!
-
-%%--------------------------------------------------------------------
-erlang_client_openssh_server_publickey_rsa(Config) ->
- erlang_client_openssh_server_publickey_X(Config, 'ssh-rsa').
-
-erlang_client_openssh_server_publickey_dsa(Config) ->
- erlang_client_openssh_server_publickey_X(Config, 'ssh-dss').
-
-
-erlang_client_openssh_server_publickey_X(_Config, Alg) ->
- ConnectionRef =
- ssh_test_lib:connect(?SSH_DEFAULT_PORT,
- [{pref_public_key_algs, [Alg]},
- {user_interaction, false},
- {auth_methods, "publickey"},
- silently_accept_hosts]),
- {ok, Channel} =
- ssh_connection:session_channel(ConnectionRef, infinity),
- ok = ssh_connection:close(ConnectionRef, Channel),
- ok = ssh:close(ConnectionRef).
-
-%%--------------------------------------------------------------------
-erlang_server_openssh_client_public_key_dsa() ->
- [{timetrap, {seconds,(?TIMEOUT div 1000)+10}}].
-erlang_server_openssh_client_public_key_dsa(Config) when is_list(Config) ->
- erlang_server_openssh_client_public_key_X(Config, 'ssh-dss').
-
-erlang_server_openssh_client_public_key_rsa() ->
- [{timetrap, {seconds,(?TIMEOUT div 1000)+10}}].
-erlang_server_openssh_client_public_key_rsa(Config) when is_list(Config) ->
- erlang_server_openssh_client_public_key_X(Config, 'ssh-rsa').
-
-
-erlang_server_openssh_client_public_key_X(Config, Alg) ->
- SystemDir = proplists:get_value(data_dir, Config),
- PrivDir = proplists:get_value(priv_dir, Config),
- KnownHosts = filename:join(PrivDir, "known_hosts"),
- {Pid, Host, Port} = ssh_test_lib:daemon([{system_dir, SystemDir},
- {preferred_algorithms,[{public_key, [Alg]}]},
- {auth_methods, "publickey"},
- {failfun, fun ssh_test_lib:failfun/2}]),
- ct:sleep(500),
-
- Cmd = ssh_test_lib:open_sshc_cmd(Host, Port,
- [" -o UserKnownHostsFile=", KnownHosts,
- " -o StrictHostKeyChecking=no"],
- "1+1."),
- OpenSsh = ssh_test_lib:open_port({spawn, Cmd}),
- ssh_test_lib:rcv_expected({data,<<"2\n">>}, OpenSsh, ?TIMEOUT),
- ssh:stop_daemon(Pid).
-
%%--------------------------------------------------------------------
%% Test that the Erlang/OTP server can renegotiate with openSSH
erlang_server_openssh_client_renegotiate(Config) ->
@@ -430,108 +177,6 @@ erlang_server_openssh_client_renegotiate(Config) ->
end.
%%--------------------------------------------------------------------
-erlang_client_openssh_server_renegotiate(_Config) ->
- process_flag(trap_exit, true),
- IO = ssh_test_lib:start_io_server(),
- Ref = make_ref(),
- Parent = self(),
-
- Shell =
- spawn_link(
- fun() ->
- Host = ssh_test_lib:hostname(),
- Options = [{user_interaction, false},
- {silently_accept_hosts,true}],
- group_leader(IO, self()),
- {ok, ConnRef} = ssh:connect(Host, ?SSH_DEFAULT_PORT, Options),
- ct:log("Parent = ~p, IO = ~p, Shell = ~p, ConnRef = ~p~n",[Parent, IO, self(), ConnRef]),
- case ssh_connection:session_channel(ConnRef, infinity) of
- {ok,ChannelId} ->
- success = ssh_connection:ptty_alloc(ConnRef, ChannelId, []),
- Args = [{channel_cb, ssh_shell},
- {init_args,[ConnRef, ChannelId]},
- {cm, ConnRef}, {channel_id, ChannelId}],
- {ok, State} = ssh_channel:init([Args]),
- Parent ! {ok, Ref, ConnRef},
- ssh_channel:enter_loop(State);
- Error ->
- Parent ! {error, Ref, Error}
- end,
- receive
- nothing -> ok
- end
- end),
-
- receive
- {error, Ref, Error} ->
- ct:fail("Error=~p",[Error]);
- {ok, Ref, ConnectionRef} ->
- IO ! {input, self(), "echo Hej1\n"},
- receive_data("Hej1", ConnectionRef),
- Kex1 = ssh_test_lib:get_kex_init(ConnectionRef),
- ssh_connection_handler:renegotiate(ConnectionRef),
- IO ! {input, self(), "echo Hej2\n"},
- receive_data("Hej2", ConnectionRef),
- Kex2 = ssh_test_lib:get_kex_init(ConnectionRef),
- IO ! {input, self(), "exit\n"},
- receive_logout(),
- receive_normal_exit(Shell),
- true = (Kex1 =/= Kex2)
- end.
-
-%%--------------------------------------------------------------------
-erlang_client_openssh_server_password() ->
- [{doc, "Test client password option"}].
-erlang_client_openssh_server_password(Config) when is_list(Config) ->
- %% to make sure we don't public-key-auth
- UserDir = proplists:get_value(data_dir, Config),
- {error, Reason0} =
- ssh:connect(any, ?SSH_DEFAULT_PORT, [{silently_accept_hosts, true},
- {user, "foo"},
- {password, "morot"},
- {user_interaction, false},
- {user_dir, UserDir}]),
-
- ct:log("Test of user foo that does not exist. "
- "Error msg: ~p~n", [Reason0]),
-
- User = string:strip(os:cmd("whoami"), right, $\n),
-
- case length(string:tokens(User, " ")) of
- 1 ->
- {error, Reason1} =
- ssh:connect(any, ?SSH_DEFAULT_PORT,
- [{silently_accept_hosts, true},
- {user, User},
- {password, "foo"},
- {user_interaction, false},
- {user_dir, UserDir}]),
- ct:log("Test of wrong Pasword. "
- "Error msg: ~p~n", [Reason1]);
- _ ->
- ct:log("Whoami failed reason: ~n", [])
- end.
-
-%%--------------------------------------------------------------------
-
-erlang_client_openssh_server_nonexistent_subsystem() ->
- [{doc, "Test client password option"}].
-erlang_client_openssh_server_nonexistent_subsystem(Config) when is_list(Config) ->
-
- ConnectionRef = ssh_test_lib:connect(?SSH_DEFAULT_PORT,
- [{user_interaction, false},
- silently_accept_hosts]),
-
- {ok, ChannelId} = ssh_connection:session_channel(ConnectionRef, infinity),
-
- failure = ssh_connection:subsystem(ConnectionRef, ChannelId, "foo", infinity).
-
-%%--------------------------------------------------------------------
-%
-%% Not possible to send password with openssh without user interaction
-%%
-%%--------------------------------------------------------------------
-%%--------------------------------------------------------------------
%%% Internal functions -----------------------------------------------
%%--------------------------------------------------------------------
receive_data(Data, Conn) ->
diff --git a/lib/ssh/vsn.mk b/lib/ssh/vsn.mk
index 004db6e3a2..d5eed0b087 100644
--- a/lib/ssh/vsn.mk
+++ b/lib/ssh/vsn.mk
@@ -1,5 +1,4 @@
#-*-makefile-*- ; force emacs to enter makefile-mode
-SSH_VSN = 4.6.4
-
+SSH_VSN = 4.6.7
APP_VSN = "ssh-$(SSH_VSN)"
diff --git a/lib/ssl/doc/src/notes.xml b/lib/ssl/doc/src/notes.xml
index 79176f5edf..4ad7da9486 100644
--- a/lib/ssl/doc/src/notes.xml
+++ b/lib/ssl/doc/src/notes.xml
@@ -27,6 +27,97 @@
</header>
<p>This document describes the changes made to the SSL application.</p>
+<section><title>SSL 8.2.5</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Fix filter function to not incorrectly exclude AEAD
+ cipher suites</p>
+ <p>
+ Own Id: OTP-14981</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>SSL 8.2.4</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Optimization of bad merge conflict resolution causing
+ dubble decode</p>
+ <p>
+ Own Id: OTP-14843</p>
+ </item>
+ <item>
+ <p>
+ Restore error propagation to OTP-19.3 behaviour, in
+ OTP-20.2 implementation adjustments to gen_statem needed
+ some further adjustments to avoid a race condition. This
+ could cause a TLS server to not always report file path
+ errors correctly.</p>
+ <p>
+ Own Id: OTP-14852</p>
+ </item>
+ <item>
+ <p>
+ Corrected RC4 suites listing function to regard TLS
+ version</p>
+ <p>
+ Own Id: OTP-14871</p>
+ </item>
+ <item>
+ <p>
+ Fix alert handling so that unexpected messages are logged
+ and alerted correctly</p>
+ <p>
+ Own Id: OTP-14919</p>
+ </item>
+ <item>
+ <p>
+ Correct handling of anonymous cipher suites</p>
+ <p>
+ Own Id: OTP-14952</p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Added new API functions to facilitate cipher suite
+ handling</p>
+ <p>
+ Own Id: OTP-14760</p>
+ </item>
+ <item>
+ <p>
+ Correct TLS_FALLBACK_SCSV handling so that this special
+ flag suite is always placed last in the cipher suite list
+ in accordance with the specs. Also make sure this
+ functionality is used in DTLS.</p>
+ <p>
+ Own Id: OTP-14828</p>
+ </item>
+ <item>
+ <p>
+ Add TLS record version sanity check for early as possible
+ error detection and consistency in ALERT codes generated</p>
+ <p>
+ Own Id: OTP-14892</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>SSL 8.2.3</title>
<section><title>Fixed Bugs and Malfunctions</title>
@@ -307,6 +398,21 @@
</section>
</section>
+<section><title>SSL 8.1.3.1.1</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Fix alert handling so that unexpected messages are logged
+ and alerted correctly</p>
+ <p>
+ Own Id: OTP-14929</p>
+ </item>
+ </list>
+ </section>
+</section>
+
<section><title>SSL 8.1.3.1</title>
<section><title>Fixed Bugs and Malfunctions</title>
<list>
diff --git a/lib/ssl/doc/src/ssl.xml b/lib/ssl/doc/src/ssl.xml
index 8fcda78ed5..8c1b1541c7 100644
--- a/lib/ssl/doc/src/ssl.xml
+++ b/lib/ssl/doc/src/ssl.xml
@@ -32,7 +32,7 @@
<modulesummary>Interface Functions for Secure Socket Layer</modulesummary>
<description>
<p>
- This module contains interface functions for the SSL/TLS protocol.
+ This module contains interface functions for the SSL/TLS/DTLS protocol.
For detailed information about the supported standards see
<seealso marker="ssl_app">ssl(6)</seealso>.
</p>
@@ -40,7 +40,7 @@
<section>
<title>DATA TYPES</title>
- <p>The following data types are used in the functions for SSL:</p>
+ <p>The following data types are used in the functions for SSL/TLS/DTLS:</p>
<taglist>
@@ -56,9 +56,11 @@
<p>The default socket options are
<c>[{mode,list},{packet, 0},{header, 0},{active, true}]</c>.</p>
<p>For valid options, see the
- <seealso marker="kernel:inet">inet(3)</seealso> and
- <seealso marker="kernel:gen_tcp">gen_tcp(3)</seealso> manual pages
- in Kernel.</p></item>
+ <seealso marker="kernel:inet">inet(3)</seealso>,
+ <seealso marker="kernel:gen_tcp">gen_tcp(3)</seealso> and
+ <seealso marker="kernel:gen_tcp">gen_udp(3)</seealso>
+ manual pages
+ in Kernel. Note that stream oriented options such as packet are only relevant for SSL/TLS and not DTLS</p></item>
<tag><marker id="type-ssloption"/><c>ssl_option() =</c></tag>
<item>
@@ -95,13 +97,14 @@
<item><p><c>{cb_info, {CallbackModule::atom(), DataTag::atom(),
ClosedTag::atom(), ErrTag:atom()}}</c></p>
- <p>Defaults to <c>{gen_tcp, tcp, tcp_closed, tcp_error}</c>. Can be used
- to customize the transport layer. The callback module must implement a
+ <p>Defaults to <c>{gen_tcp, tcp, tcp_closed, tcp_error}</c> for TLS
+ and <c>{gen_udp, udp, udp_closed, udp_error}</c> for DTLS. Can be used
+ to customize the transport layer. For TLS the callback module must implement a
reliable transport protocol, behave as <c>gen_tcp</c>, and have functions
corresponding to <c>inet:setopts/2</c>, <c>inet:getopts/2</c>,
<c>inet:peername/1</c>, <c>inet:sockname/1</c>, and <c>inet:port/1</c>.
The callback <c>gen_tcp</c> is treated specially and calls <c>inet</c>
- directly.</p>
+ directly. For DTLS this feature must be considered exprimental.</p>
<taglist>
<tag><c>CallbackModule =</c></tag>
<item><p><c>atom()</c></p></item>
@@ -137,18 +140,26 @@
<tag><c>sslsocket() =</c></tag>
<item><p>opaque()</p></item>
-
- <tag><marker id="type-protocol"/><c>protocol() =</c></tag>
+
+ <tag><marker id="type-protocol"/><c> protocol_version() =</c></tag>
+ <item><p><c> ssl_tls_protocol() | dtls_protocol() </c></p></item>
+
<item><p><c>sslv3 | tlsv1 | 'tlsv1.1' | 'tlsv1.2'</c></p></item>
+ <tag><marker id="type-protocol"/><c> dtls_protocol() =</c></tag>
+ <item><p><c>'dtlsv1' | 'dtlsv1.2'</c></p></item>
+
<tag><c>ciphers() =</c></tag>
- <item><p><c>= [ciphersuite()] | string()</c></p>
- <p>According to old API.</p></item>
+ <item><p><c>= [ciphersuite()]</c></p>
+ <p>Tuples and string formats accepted by versions
+ before ssl-8.2.4 will be converted for backwards compatibility</p></item>
<tag><c>ciphersuite() =</c></tag>
-
- <item><p><c>{key_exchange(), cipher(), MAC::hash()} |
- {key_exchange(), cipher(), MAC::hash(), PRF::hash()}</c></p></item>
+ <item><p><c>
+ #{key_exchange := key_exchange(),
+ cipher := cipher(),
+ mac := MAC::hash() | aead,
+ prf := PRF::hash() | default_prf} </c></p></item>
<tag><c>key_exchange()=</c></tag>
<item><p><c>rsa | dhe_dss | dhe_rsa | dh_anon | psk | dhe_psk
@@ -165,6 +176,12 @@
<tag><c>prf_random() =</c></tag>
<item><p><c>client_random | server_random</c></p></item>
+ <tag><c>cipher_filters() =</c></tag>
+ <item><p><c> [{key_exchange | cipher | mac | prf, algo_filter()}])</c></p></item>
+
+ <tag><c>algo_filter() =</c></tag>
+ <item><p>fun(key_exchange() | cipher() | hash() | aead | default_prf) -> true | false </p></item>
+
<tag><c>srp_param_type() =</c></tag>
<item><p><c>srp_1024 | srp_1536 | srp_2048 | srp_3072
| srp_4096 | srp_6144 | srp_8192</c></p></item>
@@ -184,7 +201,7 @@
</section>
<section>
- <title>SSL OPTION DESCRIPTIONS - COMMON for SERVER and CLIENT</title>
+ <title>TLS/DTLS OPTION DESCRIPTIONS - COMMON for SERVER and CLIENT</title>
<p>The following options have the same meaning in the client and
the server:</p>
@@ -247,8 +264,9 @@
<item><p>Specifies if to reject renegotiation attempt that does
not live up to
<url href="http://www.ietf.org/rfc/rfc5746.txt">RFC 5746</url>.
- By default <c>secure_renegotiate</c> is set to <c>false</c>,
- that is, secure renegotiation is used if possible,
+ By default <c>secure_renegotiate</c> is set to <c>true</c>,
+ that is, secure renegotiation is enforced. If set to <c>false</c> secure renegotiation
+ will still be used if possible,
but it falls back to insecure renegotiation if the peer
does not support
<url href="http://www.ietf.org/rfc/rfc5746.txt">RFC 5746</url>.</p>
@@ -289,11 +307,11 @@ atom()}} |
<list type="bulleted">
<item><p>If the verify callback fun returns <c>{fail, Reason}</c>,
the verification process is immediately stopped, an alert is
- sent to the peer, and the TLS/SSL handshake terminates.</p></item>
+ sent to the peer, and the TLS/DTLS handshake terminates.</p></item>
<item><p>If the verify callback fun returns <c>{valid, UserState}</c>,
the verification process continues.</p></item>
<item><p>If the verify callback fun always returns
- <c>{valid, UserState}</c>, the TLS/SSL handshake does not
+ <c>{valid, UserState}</c>, the TLS/DTLS handshake does not
terminate regarding verification failures and the connection is
established.</p></item>
<item><p>If called with an extension unknown to the user application,
@@ -456,15 +474,16 @@ marker="public_key:public_key#pkix_path_validation-3">public_key:pkix_path_valid
marker="public_key:public_key#pkix_path_validation-3">public_key:pkix_path_validation/3</seealso>
with the selected CA as trusted anchor and the rest of the chain.</p></item>
- <tag><c>{versions, [protocol()]}</c></tag>
+ <tag><c>{versions, [protocol_version()]}</c></tag>
+
<item><p>TLS protocol versions supported by started clients and servers.
This option overrides the application environment option
- <c>protocol_version</c>. If the environment option is not set, it defaults
+ <c>protocol_version</c> and <c>dtls_protocol_version</c>. If the environment option is not set, it defaults
to all versions, except SSL-3.0, supported by the SSL application.
See also <seealso marker="ssl:ssl_app">ssl(6).</seealso></p></item>
<tag><c>{hibernate_after, integer()|undefined}</c></tag>
- <item><p>When an integer-value is specified, <c>ssl_connection</c>
+ <item><p>When an integer-value is specified, <c>TLS/DTLS-connection</c>
goes into hibernation after the specified number of milliseconds
of inactivity, thus reducing its memory footprint. When
<c>undefined</c> is specified (this is the default), the process
@@ -524,7 +543,7 @@ fun(srp, Username :: string(), UserState :: term()) ->
</section>
<section>
- <title>SSL OPTION DESCRIPTIONS - CLIENT SIDE</title>
+ <title>TLS/DTLS OPTION DESCRIPTIONS - CLIENT SIDE</title>
<p>The following options are client-specific or have a slightly different
meaning in the client than in the server:</p>
@@ -664,7 +683,7 @@ fun(srp, Username :: string(), UserState :: term()) ->
</section>
<section>
- <title>SSL OPTION DESCRIPTIONS - SERVER SIDE</title>
+ <title>TLS/DTLS OPTION DESCRIPTIONS - SERVER SIDE</title>
<p>The following options are server-specific or have a slightly different
meaning in the server than in the client:</p>
@@ -702,7 +721,7 @@ fun(srp, Username :: string(), UserState :: term()) ->
</p></item>
<tag><c>{fail_if_no_peer_cert, boolean()}</c></tag>
- <item><p>Used together with <c>{verify, verify_peer}</c> by an SSL server.
+ <item><p>Used together with <c>{verify, verify_peer}</c> by an TLS/DTLS server.
If set to <c>true</c>, the server fails if the client does not have
a certificate to send, that is, sends an empty certificate. If set to
<c>false</c>, it fails only if the client sends an invalid
@@ -716,7 +735,7 @@ fun(srp, Username :: string(), UserState :: term()) ->
<tag><c>{reuse_session, fun(SuggestedSessionId,
PeerCert, Compression, CipherSuite) -> boolean()}</c></tag>
- <item><p>Enables the SSL server to have a local policy
+ <item><p>Enables the TLS/DTLS server to have a local policy
for deciding if a session is to be reused or not.
Meaningful only if <c>reuse_sessions</c> is set to <c>true</c>.
<c>SuggestedSessionId</c> is a <c>binary()</c>, <c>PeerCert</c> is
@@ -802,19 +821,13 @@ fun(srp, Username :: string(), UserState :: term()) ->
client certificate is requested. For more details see the <seealso marker="#client_signature_algs">corresponding client option</seealso>.
</p> </item>
- <tag><c>{v2_hello_compatible, boolean()}</c></tag>
- <item>If true, the server accepts clients that send hello messages on SSL-2.0 format but offers
- supported SSL/TLS versions. Defaults to false, that is the server will not interoperate with clients that
- offers SSL-2.0.
- </item>
-
</taglist>
</section>
<section>
<title>General</title>
- <p>When an SSL socket is in active mode (the default), data from the
+ <p>When an TLS/DTLS socket is in active mode (the default), data from the
socket is delivered to the owner of the socket in the form of
messages:</p>
@@ -829,14 +842,34 @@ fun(srp, Username :: string(), UserState :: term()) ->
</section>
<funcs>
+
+ <func>
+ <name>append_cipher_suites(Deferred, Suites) -> ciphers() </name>
+ <fsummary></fsummary>
+ <type>
+ <v>Deferred = ciphers() | cipher_filters() </v>
+ <v>Suites = ciphers() </v>
+ </type>
+ <desc><p>Make <c>Deferred</c> suites become the least preferred
+ suites, that is put them at the end of the cipher suite list
+ <c>Suites</c> after removing them from <c>Suites</c> if
+ present. <c>Deferred</c> may be a list of cipher suits or a
+ list of filters in which case the filters are use on <c>Suites</c> to
+ extract the Deferred cipher list.</p>
+ </desc>
+ </func>
+
<func>
<name>cipher_suites() -></name>
- <name>cipher_suites(Type) -> ciphers()</name>
+ <name>cipher_suites(Type) -> old_ciphers()</name>
<fsummary>Returns a list of supported cipher suites.</fsummary>
<type>
<v>Type = erlang | openssl | all</v>
</type>
- <desc><p>Returns a list of supported cipher suites.
+ <desc>
+ <p>Returns a list of supported cipher suites.
+ This function will become deprecated in OTP 21, and replaced
+ by <seealso marker="#cipher_suites-2">ssl:cipher-suites/2</seealso>
<c>cipher_suites()</c> is equivalent to <c>cipher_suites(erlang).</c>
Type <c>openssl</c> is provided for backwards compatibility with the
old SSL, which used OpenSSL. <c>cipher_suites(all)</c> returns
@@ -844,12 +877,26 @@ fun(srp, Username :: string(), UserState :: term()) ->
in <c>cipher_suites(erlang)</c> but included in
<c>cipher_suites(all)</c> are not used unless explicitly configured
by the user.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name>cipher_suites(Supported, Version) -> ciphers()</name>
+ <fsummary>Returns a list of all default or
+ all supported cipher suites.</fsummary>
+ <type>
+ <v> Supported = default | all | anonymous </v>
+ <v> Version = protocol_version() </v>
+ </type>
+ <desc><p>Returns all default or all supported (except anonymous),
+ or all anonymous cipher suites for a
+ TLS version</p>
</desc>
</func>
<func>
<name>eccs() -></name>
- <name>eccs(protocol()) -> [named_curve()]</name>
+ <name>eccs(protocol_version()) -> [named_curve()]</name>
<fsummary>Returns a list of supported ECCs.</fsummary>
<desc><p>Returns a list of supported ECCs. <c>eccs()</c>
@@ -857,7 +904,7 @@ fun(srp, Username :: string(), UserState :: term()) ->
supported protocols and then deduplicating the output.</p>
</desc>
</func>
-
+
<func>
<name>clear_pem_cache() -> ok </name>
<fsummary> Clears the pem cache</fsummary>
@@ -872,20 +919,20 @@ fun(srp, Username :: string(), UserState :: term()) ->
<func>
<name>connect(Socket, SslOptions) -> </name>
- <name>connect(Socket, SslOptions, Timeout) -> {ok, SslSocket}
+ <name>connect(Socket, SslOptions, Timeout) -> {ok, TLSSocket}
| {error, Reason}</name>
<fsummary>Upgrades a <c>gen_tcp</c>, or
- equivalent, connected socket to an SSL socket.</fsummary>
+ equivalent, connected socket to an TLS socket.</fsummary>
<type>
<v>Socket = socket()</v>
<v>SslOptions = [ssl_option()]</v>
<v>Timeout = integer() | infinity</v>
- <v>SslSocket = sslsocket()</v>
+ <v>TLSSocket = sslsocket()</v>
<v>Reason = term()</v>
</type>
<desc><p>Upgrades a <c>gen_tcp</c>, or equivalent,
- connected socket to an SSL socket, that is, performs the
- client-side ssl handshake.</p>
+ connected socket to an TLS socket, that is, performs the
+ client-side TLS handshake.</p>
<note><p>If the option <c>verify</c> is set to <c>verify_peer</c>
the option <c>server_name_indication</c> shall also be specified,
@@ -899,7 +946,7 @@ fun(srp, Username :: string(), UserState :: term()) ->
<name>connect(Host, Port, Options) -></name>
<name>connect(Host, Port, Options, Timeout) ->
{ok, SslSocket} | {error, Reason}</name>
- <fsummary>Opens an SSL connection to <c>Host</c>, <c>Port</c>.</fsummary>
+ <fsummary>Opens an TLS/DTLS connection to <c>Host</c>, <c>Port</c>.</fsummary>
<type>
<v>Host = host()</v>
<v>Port = integer()</v>
@@ -908,13 +955,13 @@ fun(srp, Username :: string(), UserState :: term()) ->
<v>SslSocket = sslsocket()</v>
<v>Reason = term()</v>
</type>
- <desc><p>Opens an SSL connection to <c>Host</c>, <c>Port</c>.</p>
+ <desc><p>Opens an TLS/DTLS connection to <c>Host</c>, <c>Port</c>.</p>
<p> When the option <c>verify</c> is set to <c>verify_peer</c> the check
<seealso marker="public_key:public_key#pkix_verify_hostname-2">public_key:pkix_verify_hostname/2</seealso>
will be performed in addition to the usual x509-path validation checks. If the check fails the error {bad_cert, hostname_check_failed} will
be propagated to the path validation fun <seealso marker="#verify_fun">verify_fun</seealso>, where it is possible to do customized
- checks by using the full possibilitis of the <seealso marker="public_key:public_key#pkix_verify_hostname-2">public_key:pkix_verify_hostname/2</seealso> API.
+ checks by using the full possibilities of the <seealso marker="public_key:public_key#pkix_verify_hostname-3">public_key:pkix_verify_hostname/3</seealso> API.
When the option <c>server_name_indication</c> is provided, its value (the DNS name) will be used as <c>ReferenceID</c>
to <seealso marker="public_key:public_key#pkix_verify_hostname-2">public_key:pkix_verify_hostname/2</seealso>.
@@ -930,24 +977,24 @@ fun(srp, Username :: string(), UserState :: term()) ->
<func>
<name>close(SslSocket) -> ok | {error, Reason}</name>
- <fsummary>Closes an SSL connection.</fsummary>
+ <fsummary>Closes an TLS/DTLS connection.</fsummary>
<type>
<v>SslSocket = sslsocket()</v>
<v>Reason = term()</v>
</type>
- <desc><p>Closes an SSL connection.</p>
+ <desc><p>Closes an TLS/DTLS connection.</p>
</desc>
</func>
<func>
<name>close(SslSocket, How) -> ok | {ok, port()} | {error, Reason}</name>
- <fsummary>Closes an SSL connection.</fsummary>
+ <fsummary>Closes an TLS connection.</fsummary>
<type>
<v>SslSocket = sslsocket()</v>
<v>How = timeout() | {NewController::pid(), timeout()} </v>
<v>Reason = term()</v>
</type>
- <desc><p>Closes or downgrades an SSL connection. In the latter case the transport
+ <desc><p>Closes or downgrades an TLS connection. In the latter case the transport
connection will be handed over to the <c>NewController</c> process after receiving
the TLS close alert from the peer. The returned transport socket will have
the following options set: <c>[{active, false}, {packet, 0}, {mode, binary}]</c></p>
@@ -958,7 +1005,7 @@ fun(srp, Username :: string(), UserState :: term()) ->
<name>controlling_process(SslSocket, NewOwner) ->
ok | {error, Reason}</name>
<fsummary>Assigns a new controlling process to the
- SSL socket.</fsummary>
+ TLS/DTLS socket.</fsummary>
<type>
<v>SslSocket = sslsocket()</v>
<v>NewOwner = pid()</v>
@@ -1008,6 +1055,21 @@ fun(srp, Username :: string(), UserState :: term()) ->
</desc>
</func>
+ <func>
+ <name>filter_cipher_suites(Suites, Filters) -> ciphers()</name>
+ <fsummary></fsummary>
+ <type>
+ <v> Suites = ciphers()</v>
+ <v> Filters = cipher_filters()</v>
+ </type>
+ <desc><p>Removes cipher suites if any of the filter functions
+ returns false for any part of the cipher suite. This function
+ also calls default filter functions to make sure the cipher
+ suites are supported by crypto. If no filter function is supplied for some
+ part the default behaviour is fun(Algorithm) -> true.</p>
+ </desc>
+ </func>
+
<func>
<name>format_error(Reason) -> string()</name>
<fsummary>Returns an error string.</fsummary>
@@ -1105,6 +1167,22 @@ fun(srp, Username :: string(), UserState :: term()) ->
<p>Returns the address and port number of the peer.</p>
</desc>
</func>
+
+ <func>
+ <name>prepend_cipher_suites(Preferred, Suites) -> ciphers()</name>
+ <fsummary></fsummary>
+ <type>
+ <v>Preferred = ciphers() | cipher_filters() </v>
+ <v>Suites = ciphers() </v>
+ </type>
+ <desc><p>Make <c>Preferred</c> suites become the most preferred
+ suites that is put them at the head of the cipher suite list
+ <c>Suites</c> after removing them from <c>Suites</c> if
+ present. <c>Preferred</c> may be a list of cipher suits or a
+ list of filters in which case the filters are use on <c>Suites</c> to
+ extract the preferred cipher list. </p>
+ </desc>
+ </func>
<func>
<name>prf(Socket, Secret, Label, Seed, WantedLength) -> {ok, binary()} | {error, reason()}</name>
@@ -1121,7 +1199,7 @@ fun(srp, Username :: string(), UserState :: term()) ->
extra key material. It either takes user-generated values for
<c>Secret</c> and <c>Seed</c> or atoms directing it to use a specific
value from the session security parameters.</p>
- <p>Can only be used with TLS connections; <c>{error, undefined}</c>
+ <p>Can only be used with TLS/DTLS connections; <c>{error, undefined}</c>
is returned for SSLv3 connections.</p>
</desc>
</func>
@@ -1221,7 +1299,7 @@ fun(srp, Username :: string(), UserState :: term()) ->
<v>Reason = term()</v>
</type>
<desc>
- <p>Performs the SSL/TLS server-side handshake.</p>
+ <p>Performs the SSL/TLS/DTLS server-side handshake.</p>
<p><c>Socket</c> is a socket as returned by
<seealso marker="#transport_accept-2">ssl:transport_accept/[1,2]</seealso>
</p>
@@ -1231,7 +1309,7 @@ fun(srp, Username :: string(), UserState :: term()) ->
<func>
<name>ssl_accept(Socket, SslOptions) -> </name>
<name>ssl_accept(Socket, SslOptions, Timeout) -> {ok, Socket} | ok | {error, Reason}</name>
- <fsummary>Performs server-side SSL/TLS handshake.</fsummary>
+ <fsummary>Performs server-side SSL/TLS/DTLS handshake.</fsummary>
<type>
<v>Socket = socket() | sslsocket() </v>
<v>SslOptions = [ssl_option()]</v>
@@ -1248,10 +1326,10 @@ fun(srp, Username :: string(), UserState :: term()) ->
by calling this function, else the upgrade succeeds or does not
succeed depending on timing.</p></warning>
- <p>If <c>Socket</c> is an <c>sslsocket()</c>: provides extra SSL/TLS
+ <p>If <c>Socket</c> is an <c>sslsocket()</c>: provides extra SSL/TLS/DTLS
options to those specified in
<seealso marker="#listen-2">ssl:listen/2 </seealso> and then performs
- the SSL/TLS handshake.
+ the SSL/TLS/DTLS handshake.
</p>
</desc>
</func>
@@ -1310,7 +1388,7 @@ fun(srp, Username :: string(), UserState :: term()) ->
The socket returned is to be passed to
<seealso marker="#ssl_accept-2"> ssl:ssl_accept[2,3]</seealso>
to complete handshaking, that is,
- establishing the SSL/TLS connection.</p>
+ establishing the SSL/TLS/DTLS connection.</p>
<warning>
<p>The socket returned can only be used with
<seealso marker="#ssl_accept-2"> ssl:ssl_accept[2,3]</seealso>.
@@ -1332,7 +1410,8 @@ fun(srp, Username :: string(), UserState :: term()) ->
<fsummary>Returns version information relevant for the
SSL application.</fsummary>
<type>
- <v>versions_info() = {app_vsn, string()} | {supported | available, [protocol()] </v>
+ <v>versions_info() = {app_vsn, string()} | {supported | available, [ssl_tls_protocol()]} |
+ {supported_dtls | available_dtls, [dtls_protocol()]} </v>
</type>
<desc>
<p>Returns version information relevant for the SSL
@@ -1342,19 +1421,35 @@ fun(srp, Username :: string(), UserState :: term()) ->
<item>The application version of the SSL application.</item>
<tag><c>supported</c></tag>
- <item>TLS/SSL versions supported by default.
+ <item>SSL/TLS versions supported by default.
Overridden by a version option on
<seealso marker="#connect-2"> connect/[2,3,4]</seealso>,
<seealso marker="#listen-2"> listen/2</seealso>, and <seealso
marker="#ssl_accept-2">ssl_accept/[1,2,3]</seealso>.
- For the negotiated TLS/SSL version, see <seealso
+ For the negotiated SSL/TLS version, see <seealso
marker="#connection_information-1">ssl:connection_information/1
</seealso>.</item>
-
+
+ <tag><c>supported_dtls</c></tag>
+ <item>DTLS versions supported by default.
+ Overridden by a version option on
+ <seealso marker="#connect-2"> connect/[2,3,4]</seealso>,
+ <seealso marker="#listen-2"> listen/2</seealso>, and <seealso
+ marker="#ssl_accept-2">ssl_accept/[1,2,3]</seealso>.
+ For the negotiated DTLS version, see <seealso
+ marker="#connection_information-1">ssl:connection_information/1
+ </seealso>.</item>
+
<tag><c>available</c></tag>
- <item>All TLS/SSL versions supported by the SSL application.
+ <item>All SSL/TLS versions supported by the SSL application.
TLS 1.2 requires sufficient support from the Crypto
application.</item>
+
+ <tag><c>available_dtls</c></tag>
+ <item>All DTLS versions supported by the SSL application.
+ DTLS 1.2 requires sufficient support from the Crypto
+ application.</item>
+
</taglist>
</desc>
</func>
@@ -1365,6 +1460,7 @@ fun(srp, Username :: string(), UserState :: term()) ->
<title>SEE ALSO</title>
<p><seealso marker="kernel:inet">inet(3)</seealso> and
<seealso marker="kernel:gen_tcp">gen_tcp(3)</seealso>
+ <seealso marker="kernel:gen_udp">gen_udp(3)</seealso>
</p>
</section>
diff --git a/lib/ssl/doc/src/ssl_app.xml b/lib/ssl/doc/src/ssl_app.xml
index 3b0f01d1e8..e22d43db0e 100644
--- a/lib/ssl/doc/src/ssl_app.xml
+++ b/lib/ssl/doc/src/ssl_app.xml
@@ -35,12 +35,13 @@
<description>
<p>
- The ssl application is an implementation of the SSL/TLS protocol in Erlang.
+ The ssl application is an implementation of the SSL/TLS/DTLS protocol in Erlang.
</p>
<list type="bulleted">
- <item>Supported SSL/TLS-versions are SSL-3.0, TLS-1.0,
- TLS-1.1, and TLS-1.2.</item>
- <item>For security reasons SSL-2.0 is not supported.</item>
+ <item>Supported SSL/TLS/DTLS-versions are SSL-3.0, TLS-1.0,
+ TLS-1.1, TLS-1.2, DTLS-1.0 (based on TLS-1.1), DTLS-1.2 (based on TLS-1.2)</item>
+ <item>For security reasons SSL-2.0 is not supported.
+ Interoperability with SSL-2.0 enabled clients dropped. (OTP 21) </item>
<item>For security reasons SSL-3.0 is no longer supported by default,
but can be configured. (OTP 19) </item>
<item>For security reasons RSA key exchange cipher suites are no longer supported by default,
@@ -76,7 +77,7 @@
<section>
<title>DEPENDENCIES</title>
- <p>The SSL application uses the <c>public_key</c> and
+ <p>The SSL application uses the <c>public_key</c>, <c>asn1</c> and
Crypto application to handle public keys and encryption, hence
these applications must be loaded for the SSL application to work.
In an embedded environment this means they must be started with
@@ -98,13 +99,20 @@
<p><c>erl -ssl protocol_version "['tlsv1.2', 'tlsv1.1']"</c></p>
<taglist>
- <tag><c>protocol_version = </c><seealso marker="ssl#type-protocol">ssl:protocol()</seealso><c><![CDATA[<optional>]]></c></tag>
+ <tag><c>protocol_version = </c><seealso marker="ssl#type-protocol">ssl:ssl_tls_protocol()</seealso><c><![CDATA[<optional>]]></c></tag>
<item><p>Protocol supported by started clients and
servers. If this option is not set, it defaults to all
- protocols currently supported by the SSL application.
+ TLS protocols currently supported by the SSL application.
This option can be overridden by the version option
to <c>ssl:connect/[2,3]</c> and <c>ssl:listen/2</c>.</p></item>
+ <tag><c>dtls_protocol_version = </c><seealso marker="ssl#type-protocol">ssl:dtls_protocol()</seealso><c><![CDATA[<optional>]]></c></tag>
+ <item><p>Protocol supported by started clients and
+ servers. If this option is not set, it defaults to all
+ DTLS protocols currently supported by the SSL application.
+ This option can be overridden by the version option
+ to <c>ssl:connect/[2,3]</c> and <c>ssl:listen/2</c>.</p></item>
+
<tag><c><![CDATA[session_lifetime = integer() <optional>]]></c></tag>
<item><p>Maximum lifetime of the session data in seconds. Defaults to 24 hours which is the maximum
recommended lifetime by <url href="http://www.ietf.org/rfc/5246rfc.txt">RFC 5246</url>. However
@@ -127,14 +135,14 @@
new client connections. If the maximum number of sessions is
reached, the current cache entries will be invalidated
regardless of their remaining lifetime. Defaults to
- 1000.</p></item>
+ 1000. Recommended ssl-8.2.1 or later for this option to work as intended.</p></item>
<tag> <c><![CDATA[session_cache_server_max = integer() <optional>]]></c></tag>
<item><p>Limits the growth of the servers session cache, that is
how many client sessions are cached by the server. If the
maximum number of sessions is reached, the current cache entries
will be invalidated regardless of their remaining
- lifetime. Defaults to 1000.</p></item>
+ lifetime. Defaults to 1000. Recommended ssl-8.2.1 or later for this option to work as intended.</p></item>
<tag><c><![CDATA[ssl_pem_cache_clean = integer() <optional>]]></c></tag>
<item>
@@ -149,9 +157,8 @@
<tag><c><![CDATA[bypass_pem_cache = boolean() <optional>]]></c></tag>
<item>
<p>Introduced in ssl-8.0.2. Disables the PEM-cache.
- The PEM cache has proven to be a bottleneck, until the
- implementation has been improved this can be used as
- a workaround. Defaults to false.
+ Can be used as a workaround for the PEM-cache bottleneck
+ before ssl-8.1.1. Defaults to false.
</p>
</item>
@@ -171,7 +178,7 @@
<title>ERROR LOGGER AND EVENT HANDLERS</title>
<p>The SSL application uses the default <seealso
marker="kernel:error_logger">OTP error logger</seealso> to log
- unexpected errors and TLS alerts. The logging of TLS alerts may be
+ unexpected errors and TLS/DTLS alerts. The logging of TLS/DTLS alerts may be
turned off with the <c>log_alert</c> option. </p>
</section>
diff --git a/lib/ssl/doc/src/ssl_crl_cache.xml b/lib/ssl/doc/src/ssl_crl_cache.xml
index 7a67de3971..738487759a 100644
--- a/lib/ssl/doc/src/ssl_crl_cache.xml
+++ b/lib/ssl/doc/src/ssl_crl_cache.xml
@@ -40,7 +40,7 @@
<name>delete(Entries) -> ok | {error, Reason} </name>
<fsummary> </fsummary>
<type>
- <v> Entries = <seealso marker="inets:http_uri">http_uri:uri() </seealso> | {file, string()} | {der, [<seealso
+ <v> Entries = <seealso marker="stdlib:uri_string">uri_string:uri_string()</seealso> | {file, string()} | {der, [<seealso
marker="public_key:public_key"> public_key:der_encoded() </seealso>]}</v>
<v> Reason = term()</v>
</type>
@@ -55,7 +55,7 @@
<type>
<v> CRLSrc = {file, string()} | {der, [ <seealso
marker="public_key:public_key"> public_key:der_encoded() </seealso> ]}</v>
- <v> URI = <seealso marker="inets:http_uri">http_uri:uri() </seealso> </v>
+ <v> URI = <seealso marker="stdlib:uri_string">uri_string:uri_string() </seealso> </v>
<v> Reason = term()</v>
</type>
<desc>
@@ -63,4 +63,4 @@
</desc>
</func>
</funcs>
-</erlref> \ No newline at end of file
+</erlref>
diff --git a/lib/ssl/doc/src/ssl_distribution.xml b/lib/ssl/doc/src/ssl_distribution.xml
index 7f8a08f704..e14f3f90dc 100644
--- a/lib/ssl/doc/src/ssl_distribution.xml
+++ b/lib/ssl/doc/src/ssl_distribution.xml
@@ -22,7 +22,7 @@
</legalnotice>
- <title>Using SSL for Erlang Distribution</title>
+ <title>Using TLS for Erlang Distribution</title>
<prepared>P Nyblom</prepared>
<responsible></responsible>
<docno></docno>
@@ -33,7 +33,7 @@
<file>ssl_distribution.xml</file>
</header>
<p>This section describes how the Erlang distribution can use
- SSL to get extra verification and security.</p>
+ TLS to get extra verification and security.</p>
<p>The Erlang distribution can in theory use almost any
connection-based protocol as bearer. However, a module that
@@ -45,16 +45,16 @@
<p>In the SSL application, an extra distribution
module, <c>inet_tls_dist</c>, can be used as an
- alternative. All distribution connections will use SSL and
+ alternative. All distribution connections will use TLS and
all participating Erlang nodes in a distributed system must use
this distribution module.</p>
<p>The security level depends on the parameters provided to the
- SSL connection setup. Erlang node cookies are however always
+ TLS connection setup. Erlang node cookies are however always
used, as they can be used to differentiate between two different
Erlang networks.</p>
- <p>To set up Erlang distribution over SSL:</p>
+ <p>To set up Erlang distribution over TLS:</p>
<list type="bulleted">
<item><em>Step 1:</em> Build boot scripts including the
@@ -63,13 +63,13 @@
<c>net_kernel</c>.</item>
<item><em>Step 3:</em> Specify the security options and other
SSL options.</item>
- <item><em>Step 4:</em> Set up the environment to always use SSL.</item>
+ <item><em>Step 4:</em> Set up the environment to always use TLS.</item>
</list>
<p>The following sections describe these steps.</p>
<section>
- <title>Building Boot Scripts Including the ssl Application</title>
+ <title>Building Boot Scripts Including the SSL Application</title>
<p>Boot scripts are built using the <c>systools</c> utility in the
SASL application. For more information on <c>systools</c>,
see the SASL documentation. This is only an example of
@@ -90,7 +90,7 @@
STDLIB application.</p></item>
</list>
- <p>The following shows an example <c>.rel</c> file with SSL
+ <p>The following shows an example <c>.rel</c> file with TLS
added:</p>
<code type="none">
{release, {"OTP APN 181 01","R15A"}, {erts, "5.9"},
@@ -154,7 +154,7 @@ Eshell V5.0 (abort with ^G)
<section>
<title>Specifying Distribution Module for net_kernel</title>
- <p>The distribution module for SSL is named <c>inet_tls_dist</c>
+ <p>The distribution module for SSL/TLS is named <c>inet_tls_dist</c>
and is specified on the command line with option <c>-proto_dist</c>.
The argument to <c>-proto_dist</c> is to be the module
name without suffix <c>_dist</c>. So, this distribution
@@ -174,21 +174,21 @@ Eshell V5.0 (abort with ^G)
(ssl_test@myhost)1> </code>
<p>However, a node started in this way refuses to talk
- to other nodes, as no SSL parameters are supplied
+ to other nodes, as no TLS parameters are supplied
(see the next section).</p>
</section>
<section>
- <title>Specifying SSL Options</title>
+ <title>Specifying SSL/TLS Options</title>
<p>
- The SSL distribution options can be written into a file
+ The SSL/TLS distribution options can be written into a file
that is consulted when the node is started. This file name
is then specified with the command line argument
<c>-ssl_dist_optfile</c>.
</p>
<p>
- Any available SSL option can be specified in an options file,
+ Any available SSL/TLS option can be specified in an options file,
but note that options that take a <c>fun()</c> has to use
the syntax <c>fun Mod:Func/Arity</c> since a function
body can not be compiled when consulting a file.
@@ -202,7 +202,7 @@ Eshell V5.0 (abort with ^G)
interfere severely, so beware!
</p>
<p>
- For SSL to work, at least a public key and a certificate
+ For SSL/TLS to work, at least a public key and a certificate
must be specified for the server side.
In the following example, the PEM file
<c>"/home/me/ssl/erlserver.pem"</c> contains both
@@ -257,13 +257,13 @@ $ erl -boot /home/me/ssl/start_ssl -proto_dist inet_tls
still be accepted if it does not present any certificate.
</p>
<p>
- A node started in this way is fully functional, using SSL
+ A node started in this way is fully functional, using TLS
as the distribution protocol.
</p>
</section>
<section>
- <title>Specifying SSL Options (Legacy)</title>
+ <title>Specifying SSL/TLS Options (Legacy)</title>
<p>
As in the previous section the PEM file
@@ -272,9 +272,9 @@ $ erl -boot /home/me/ssl/start_ssl -proto_dist inet_tls
</p>
<p>On the <c>erl</c> command line you can specify options that the
- SSL distribution adds when creating a socket.</p>
+ SSL/TLS distribution adds when creating a socket.</p>
- <p>The simplest SSL options in the following list can be specified
+ <p>The simplest SSL/TLS options in the following list can be specified
by adding the
prefix <c>server_</c> or <c>client_</c> to the option name:</p>
<list type="bulleted">
@@ -294,7 +294,7 @@ $ erl -boot /home/me/ssl/start_ssl -proto_dist inet_tls
</list>
<p>Note that <c>verify_fun</c> needs to be written in a different
- form than the corresponding SSL option, since funs are not
+ form than the corresponding SSL/TLS option, since funs are not
accepted on the command line.</p>
<p>The server can also take the options <c>dhfile</c> and
@@ -307,7 +307,7 @@ $ erl -boot /home/me/ssl/start_ssl -proto_dist inet_tls
<p>Raw socket options, such as <c>packet</c> and <c>size</c> must not
be specified on the command line.</p>
- <p>The command-line argument for specifying the SSL options is named
+ <p>The command-line argument for specifying the SSL/TLS options is named
<c>-ssl_dist_opt</c> and is to be followed by pairs of
SSL options and their values. Argument <c>-ssl_dist_opt</c> can
be repeated any number of times.</p>
@@ -331,10 +331,10 @@ Eshell V5.0 (abort with ^G)
</section>
<section>
- <title>Setting up Environment to Always Use SSL (Legacy)</title>
+ <title>Setting up Environment to Always Use SSL/TLS (Legacy)</title>
<p>A convenient way to specify arguments to Erlang is to use environment
variable <c>ERL_FLAGS</c>. All the flags needed to
- use the SSL distribution can be specified in that variable and are
+ use the SSL/TLS distribution can be specified in that variable and are
then interpreted as command-line arguments for all
subsequent invocations of Erlang.</p>
@@ -365,8 +365,8 @@ Eshell V5.0 (abort with ^G)
</section>
<section>
- <title>Using SSL distribution over IPv6</title>
- <p>It is possible to use SSL distribution over IPv6 instead of
+ <title>Using SSL/TLS distribution over IPv6</title>
+ <p>It is possible to use SSL/TLS distribution over IPv6 instead of
IPv4. To do this, pass the option <c>-proto_dist inet6_tls</c>
instead of <c>-proto_dist inet_tls</c> when starting Erlang,
either on the command line or in the <c>ERL_FLAGS</c> environment
@@ -380,6 +380,6 @@ $ erl -boot /home/me/ssl/start_ssl -proto_dist inet6_tls
</code>
<p>A node started in this way will only be able to communicate with
- other nodes using SSL distribution over IPv6.</p>
+ other nodes using SSL/TLS distribution over IPv6.</p>
</section>
</chapter>
diff --git a/lib/ssl/doc/src/ssl_introduction.xml b/lib/ssl/doc/src/ssl_introduction.xml
index 25b05a769d..a416924eb1 100644
--- a/lib/ssl/doc/src/ssl_introduction.xml
+++ b/lib/ssl/doc/src/ssl_introduction.xml
@@ -5,7 +5,7 @@
<header>
<copyright>
<year>2015</year>
- <year>2015</year>
+ <year>2017</year>
<holder>Ericsson AB, All Rights Reserved</holder>
</copyright>
<legalnotice>
@@ -41,14 +41,15 @@
authenticate the counterpart with whom they communicate,
and to exchange a symmetric key for payload encryption. The protocol provides
data/message confidentiality (encryption), integrity (through message authentication code checks)
- and host verification (through certificate path validation).</p>
+ and host verification (through certificate path validation). DTLS (Datagram Transport Layer Security) that
+ is based on TLS but datagram oriented instead of stream oriented.</p>
</section>
<section>
<title>Prerequisites</title>
<p>It is assumed that the reader is familiar with the Erlang
programming language, the concepts of OTP, and has a basic
- understanding of SSL/TLS.</p>
+ understanding of SSL/TLS/DTLS.</p>
</section>
</chapter>
diff --git a/lib/ssl/doc/src/ssl_protocol.xml b/lib/ssl/doc/src/ssl_protocol.xml
index 31a22db58b..0b12dc7dc5 100644
--- a/lib/ssl/doc/src/ssl_protocol.xml
+++ b/lib/ssl/doc/src/ssl_protocol.xml
@@ -22,7 +22,7 @@
</legalnotice>
- <title>TLS and its Predecessor, SSL</title>
+ <title>TLS/DTLS and TLS Predecessor, SSL</title>
<prepared></prepared>
<responsible></responsible>
<docno></docno>
@@ -33,7 +33,7 @@
<file>ssl_protocol.xml</file>
</header>
- <p>The Erlang SSL application implements the SSL/TLS protocol
+ <p>The Erlang SSL application implements the SSL/TLS/DTLS protocol
for the currently supported versions, see the
<seealso marker="ssl">ssl(3)</seealso> manual page.
</p>
@@ -41,20 +41,22 @@
<p>By default SSL/TLS is run over the TCP/IP protocol even
though you can plug in any other reliable transport protocol
with the same Application Programming Interface (API) as the
- <c>gen_tcp</c> module in Kernel.</p>
+ <c>gen_tcp</c> module in Kernel. DTLS is by default run over UDP/IP,
+ which means that application data has no delivery guarentees. Other
+ transports, such as SCTP, may be supported in future releases.</p>
<p>If a client and a server wants to use an upgrade mechanism, such as
- defined by RFC 2817, to upgrade a regular TCP/IP connection to an SSL
+ defined by RFC 2817, to upgrade a regular TCP/IP connection to an TLS
connection, this is supported by the Erlang SSL application API. This can be
useful for, for example, supporting HTTP and HTTPS on the same port and
- implementing virtual hosting.
+ implementing virtual hosting. Note this is a TLS feature only.
</p>
<section>
<title>Security Overview</title>
<p>To achieve authentication and privacy, the client and server
- perform a TLS handshake procedure before transmitting or receiving
+ perform a TLS/DTLS handshake procedure before transmitting or receiving
any data. During the handshake, they agree on a protocol version and
cryptographic algorithms, generate shared secrets using public
key cryptographies, and optionally authenticate each other with
@@ -73,10 +75,10 @@
<p>The keys for the symmetric encryption are generated uniquely
for each connection and are based on a secret negotiated
- in the TLS handshake.</p>
+ in the TLS/DTLS handshake.</p>
- <p>The TLS handshake protocol and data transfer is run on top of
- the TLS Record Protocol, which uses a keyed-hash Message
+ <p>The TLS/DTLS handshake protocol and data transfer is run on top of
+ the TLS/DTLS Record Protocol, which uses a keyed-hash Message
Authenticity Code (MAC), or a Hash-based MAC (HMAC),
to protect the message data
integrity. From the TLS RFC: "A Message Authentication Code is a
@@ -152,8 +154,8 @@
from it was saved, for security reasons. The amount of time the
session data is to be saved can be configured.</p>
- <p>By default the SSL clients try to reuse an available session and
- by default the SSL servers agree to reuse sessions when clients
+ <p>By default the TLS/DTLS clients try to reuse an available session and
+ by default the TLS/DTLS servers agree to reuse sessions when clients
ask for it.</p>
</section>
diff --git a/lib/ssl/doc/src/using_ssl.xml b/lib/ssl/doc/src/using_ssl.xml
index 61918a346d..3ef33df719 100644
--- a/lib/ssl/doc/src/using_ssl.xml
+++ b/lib/ssl/doc/src/using_ssl.xml
@@ -22,7 +22,7 @@
</legalnotice>
- <title>Using SSL API</title>
+ <title>Using SSL application API</title>
<prepared></prepared>
<responsible></responsible>
<docno></docno>
@@ -51,7 +51,7 @@
<section>
<title>Minimal Example</title>
- <note><p> The minimal setup is not the most secure setup of SSL.</p>
+ <note><p> The minimal setup is not the most secure setup of SSL/TLS/DTLS.</p>
</note>
<p>To set up client/server connections:</p>
@@ -60,27 +60,27 @@
<code type="erl">1 server> ssl:start().
ok</code>
- <p><em>Step 2:</em> Create an SSL listen socket:</p>
+ <p><em>Step 2:</em> Create an TLS listen socket: (To run DTLS add the option {protocol, dtls})</p>
<code type="erl">2 server> {ok, ListenSocket} =
ssl:listen(9999, [{certfile, "cert.pem"}, {keyfile, "key.pem"},{reuseaddr, true}]).
{ok,{sslsocket, [...]}}</code>
- <p><em>Step 3:</em> Do a transport accept on the SSL listen socket:</p>
+ <p><em>Step 3:</em> Do a transport accept on the TLS listen socket:</p>
<code type="erl">3 server> {ok, Socket} = ssl:transport_accept(ListenSocket).
{ok,{sslsocket, [...]}}</code>
- <p><em>Step 4:</em> Start the client side:</p>
+ <p><em>Step 4:</em> Start the client side: </p>
<code type="erl">1 client> ssl:start().
ok</code>
-
+ <p> To run DTLS add the option {protocol, dtls} to third argument.</p>
<code type="erl">2 client> {ok, Socket} = ssl:connect("localhost", 9999, [], infinity).
{ok,{sslsocket, [...]}}</code>
- <p><em>Step 5:</em> Do the SSL handshake:</p>
+ <p><em>Step 5:</em> Do the TLS handshake:</p>
<code type="erl">4 server> ok = ssl:ssl_accept(Socket).
ok</code>
- <p><em>Step 6:</em> Send a message over SSL:</p>
+ <p><em>Step 6:</em> Send a message over TLS:</p>
<code type="erl">5 server> ssl:send(Socket, "foo").
ok</code>
@@ -92,7 +92,7 @@ ok</code>
</section>
<section>
- <title>Upgrade Example</title>
+ <title>Upgrade Example - TLS only </title>
<note><p>To upgrade a TCP/IP connection to an SSL connection, the
client and server must agree to do so. The agreement
@@ -125,24 +125,24 @@ ok</code>
<code type="erl">4 server> inet:setopts(Socket, [{active, false}]).
ok</code>
- <p><em>Step 6:</em> Do the SSL handshake:</p>
- <code type="erl">5 server> {ok, SSLSocket} = ssl:ssl_accept(Socket, [{cacertfile, "cacerts.pem"},
+ <p><em>Step 6:</em> Do the TLS handshake:</p>
+ <code type="erl">5 server> {ok, TLSSocket} = ssl:ssl_accept(Socket, [{cacertfile, "cacerts.pem"},
{certfile, "cert.pem"}, {keyfile, "key.pem"}]).
{ok,{sslsocket,[...]}}</code>
- <p><em>Step 7:</em> Upgrade to an SSL connection. The client and server
+ <p><em>Step 7:</em> Upgrade to an TLS connection. The client and server
must agree upon the upgrade. The server must call
<c>ssl:accept/2</c> before the client calls <c>ssl:connect/3.</c></p>
- <code type="erl">3 client>{ok, SSLSocket} = ssl:connect(Socket, [{cacertfile, "cacerts.pem"},
+ <code type="erl">3 client>{ok, TLSSocket} = ssl:connect(Socket, [{cacertfile, "cacerts.pem"},
{certfile, "cert.pem"}, {keyfile, "key.pem"}], infinity).
{ok,{sslsocket,[...]}}</code>
- <p><em>Step 8:</em> Send a message over SSL:</p>
- <code type="erl">4 client> ssl:send(SSLSocket, "foo").
+ <p><em>Step 8:</em> Send a message over TLS:</p>
+ <code type="erl">4 client> ssl:send(TLSSocket, "foo").
ok</code>
- <p><em>Step 9:</em> Set <c>active true</c> on the SSL socket:</p>
- <code type="erl">4 server> ssl:setopts(SSLSocket, [{active, true}]).
+ <p><em>Step 9:</em> Set <c>active true</c> on the TLS socket:</p>
+ <code type="erl">4 server> ssl:setopts(TLSSocket, [{active, true}]).
ok</code>
<p><em>Step 10:</em> Flush the shell message queue to see that the message
@@ -153,7 +153,51 @@ ok</code>
</section>
</section>
- <section>
+ <section>
+ <title>Customizing cipher suits</title>
+
+ <p>Fetch default cipher suite list for an TLS/DTLS version. Change default
+ to all to get all possible cipher suites.</p>
+ <code type="erl">1> Default = ssl:cipher_suites(default, 'tlsv1.2').
+ [#{cipher => aes_256_gcm,key_exchange => ecdhe_ecdsa,
+ mac => aead,prf => sha384}, ....]
+</code>
+
+ <p>In OTP 20 it is desirable to remove all cipher suites
+ that uses rsa kexchange (removed from default in 21) </p>
+ <code type="erl">2> NoRSA =
+ ssl:filter_cipher_suites(Default,
+ [{key_exchange, fun(rsa) -> false;
+ (_) -> true end}]).
+ [...]
+ </code>
+
+ <p> Pick just a few suites </p>
+ <code type="erl"> 3> Suites =
+ ssl:filter_cipher_suites(Default,
+ [{key_exchange, fun(ecdh_ecdsa) -> true;
+ (_) -> false end},
+ {cipher, fun(aes_128_cbc) ->true;
+ (_) ->false end}]).
+ [#{cipher => aes_128_cbc,key_exchange => ecdh_ecdsa,
+ mac => sha256,prf => sha256},
+ #{cipher => aes_128_cbc,key_exchange => ecdh_ecdsa,mac => sha,
+ prf => default_prf}]
+ </code>
+
+ <p> Make some particular suites the most preferred, or least
+ preferred by changing prepend to append.</p>
+ <code type="erl"> 4>ssl:prepend_cipher_suites(Suites, Default).
+ [#{cipher => aes_128_cbc,key_exchange => ecdh_ecdsa,
+ mac => sha256,prf => sha256},
+ #{cipher => aes_128_cbc,key_exchange => ecdh_ecdsa,mac => sha,
+ prf => default_prf},
+ #{cipher => aes_256_cbc,key_exchange => ecdhe_ecdsa,
+ mac => sha384,prf => sha384}, ...]
+ </code>
+ </section>
+
+ <section>
<title>Using an Engine Stored Key</title>
<p>Erlang ssl application is able to use private keys provided
diff --git a/lib/ssl/src/Makefile b/lib/ssl/src/Makefile
index 8eba5cf347..11b3e65912 100644
--- a/lib/ssl/src/Makefile
+++ b/lib/ssl/src/Makefile
@@ -84,7 +84,6 @@ MODULES= \
tls_record \
dtls_record \
ssl_record \
- ssl_v2 \
ssl_v3 \
tls_v1 \
dtls_v1
diff --git a/lib/ssl/src/dtls_connection.erl b/lib/ssl/src/dtls_connection.erl
index fb12a729b1..7bc7fc3fc6 100644
--- a/lib/ssl/src/dtls_connection.erl
+++ b/lib/ssl/src/dtls_connection.erl
@@ -970,8 +970,7 @@ unprocessed_events(Events) ->
update_handshake_history(#hello_verify_request{}, _, Hist) ->
Hist;
update_handshake_history(_, Handshake, Hist) ->
- %% DTLS never needs option "v2_hello_compatible" to be true
- ssl_handshake:update_handshake_history(Hist, iolist_to_binary(Handshake), false).
+ ssl_handshake:update_handshake_history(Hist, iolist_to_binary(Handshake)).
prepare_flight(#state{flight_buffer = Flight,
connection_states = ConnectionStates0,
protocol_buffers =
diff --git a/lib/ssl/src/dtls_record.erl b/lib/ssl/src/dtls_record.erl
index 316de05532..2938fd460f 100644
--- a/lib/ssl/src/dtls_record.erl
+++ b/lib/ssl/src/dtls_record.erl
@@ -440,7 +440,6 @@ get_dtls_records_aux(<<?BYTE(?CHANGE_CIPHER_SPEC),?BYTE(MajVer),?BYTE(MinVer),
version = {MajVer, MinVer},
epoch = Epoch, sequence_number = SequenceNumber,
fragment = Data} | Acc]);
-
get_dtls_records_aux(<<?BYTE(_), ?BYTE(_MajVer), ?BYTE(_MinVer),
?UINT16(Length), _/binary>>,
_Acc) when Length > ?MAX_CIPHER_TEXT_LENGTH ->
diff --git a/lib/ssl/src/dtls_v1.erl b/lib/ssl/src/dtls_v1.erl
index 51ee8ec047..0f6344b6f7 100644
--- a/lib/ssl/src/dtls_v1.erl
+++ b/lib/ssl/src/dtls_v1.erl
@@ -21,7 +21,7 @@
-include("ssl_cipher.hrl").
--export([suites/1, all_suites/1, hmac_hash/3, ecc_curves/1,
+-export([suites/1, all_suites/1, anonymous_suites/1,hmac_hash/3, ecc_curves/1,
corresponding_tls_version/1, corresponding_dtls_version/1,
cookie_secret/0, cookie_timeout/0]).
@@ -40,6 +40,12 @@ all_suites(Version) ->
end,
ssl_cipher:all_suites(corresponding_tls_version(Version))).
+anonymous_suites(Version) ->
+ lists:filter(fun(Cipher) ->
+ is_acceptable_cipher(ssl_cipher:suite_definition(Cipher))
+ end,
+ ssl_cipher:anonymous_suites(corresponding_tls_version(Version))).
+
hmac_hash(MacAlg, MacSecret, Value) ->
tls_v1:hmac_hash(MacAlg, MacSecret, Value).
diff --git a/lib/ssl/src/ssl.app.src b/lib/ssl/src/ssl.app.src
index 3c6cd254c1..2aecb6836e 100644
--- a/lib/ssl/src/ssl.app.src
+++ b/lib/ssl/src/ssl.app.src
@@ -9,7 +9,6 @@
tls_socket,
tls_v1,
ssl_v3,
- ssl_v2,
tls_connection_sup,
%% DTLS
dtls_connection,
@@ -62,5 +61,5 @@
{applications, [crypto, public_key, kernel, stdlib]},
{env, []},
{mod, {ssl_app, []}},
- {runtime_dependencies, ["stdlib-3.2","public_key-1.5","kernel-6.0",
+ {runtime_dependencies, ["stdlib-3.5","public_key-1.5","kernel-6.0",
"erts-10.0","crypto-4.2", "inets-5.10.7"]}]}.
diff --git a/lib/ssl/src/ssl.appup.src b/lib/ssl/src/ssl.appup.src
index bfdd0c205b..4ad2a2f1fd 100644
--- a/lib/ssl/src/ssl.appup.src
+++ b/lib/ssl/src/ssl.appup.src
@@ -1,6 +1,7 @@
%% -*- erlang -*-
{"%VSN%",
[
+ {<<"8.2.4">>, [{load_module, ssl_cipher, soft_purge, soft_purge, []}]},
{<<"8\\..*">>, [{restart_application, ssl}]},
{<<"7\\..*">>, [{restart_application, ssl}]},
{<<"6\\..*">>, [{restart_application, ssl}]},
@@ -9,6 +10,7 @@
{<<"3\\..*">>, [{restart_application, ssl}]}
],
[
+ {<<"8.2.4">>, [{load_module, ssl_cipher, soft_purge, soft_purge, []}]},
{<<"8\\..*">>, [{restart_application, ssl}]},
{<<"7\\..*">>, [{restart_application, ssl}]},
{<<"6\\..*">>, [{restart_application, ssl}]},
diff --git a/lib/ssl/src/ssl.erl b/lib/ssl/src/ssl.erl
index 656ed94ea5..4efd13a6fa 100644
--- a/lib/ssl/src/ssl.erl
+++ b/lib/ssl/src/ssl.erl
@@ -39,7 +39,9 @@
]).
%% SSL/TLS protocol handling
--export([cipher_suites/0, cipher_suites/1, eccs/0, eccs/1, versions/0,
+-export([cipher_suites/0, cipher_suites/1, cipher_suites/2, filter_cipher_suites/2,
+ prepend_cipher_suites/2, append_cipher_suites/2,
+ eccs/0, eccs/1, versions/0,
format_error/1, renegotiate/1, prf/5, negotiated_protocol/1,
connection_information/1, connection_information/2]).
%% Misc
@@ -379,19 +381,93 @@ negotiated_protocol(#sslsocket{pid = Pid}) ->
cipher_suites() ->
cipher_suites(erlang).
%%--------------------------------------------------------------------
--spec cipher_suites(erlang | openssl | all) -> [ssl_cipher:old_erl_cipher_suite() | string()].
+-spec cipher_suites(erlang | openssl | all) ->
+ [ssl_cipher:old_erl_cipher_suite() | string()].
%% Description: Returns all supported cipher suites.
%%--------------------------------------------------------------------
cipher_suites(erlang) ->
[ssl_cipher:erl_suite_definition(Suite) || Suite <- available_suites(default)];
cipher_suites(openssl) ->
- [ssl_cipher:openssl_suite_name(Suite) || Suite <- available_suites(default)];
+ [ssl_cipher:openssl_suite_name(Suite) ||
+ Suite <- available_suites(default)];
cipher_suites(all) ->
[ssl_cipher:erl_suite_definition(Suite) || Suite <- available_suites(all)].
%%--------------------------------------------------------------------
+-spec cipher_suites(default | all | anonymous, tls_record:tls_version() | dtls_record:dtls_version() |
+ tls_record:tls_atom_version() | dtls_record:dtls_atom_version()) ->
+ [ssl_cipher:erl_cipher_suite()].
+%% Description: Returns all default and all supported cipher suites for a
+%% TLS/DTLS version
+%%--------------------------------------------------------------------
+cipher_suites(Base, Version) when Version == 'tlsv1.2';
+ Version == 'tlsv1.1';
+ Version == tlsv1;
+ Version == sslv3 ->
+ cipher_suites(Base, tls_record:protocol_version(Version));
+cipher_suites(Base, Version) when Version == 'dtlsv1.2';
+ Version == 'dtlsv1'->
+ cipher_suites(Base, dtls_record:protocol_version(Version));
+cipher_suites(Base, Version) ->
+ [ssl_cipher:suite_definition(Suite) || Suite <- supported_suites(Base, Version)].
+
+%%--------------------------------------------------------------------
+-spec filter_cipher_suites([ssl_cipher:erl_cipher_suite()],
+ [{key_exchange | cipher | mac | prf, fun()}] | []) ->
+ [ssl_cipher:erl_cipher_suite()].
+%% Description: Removes cipher suites if any of the filter functions returns false
+%% for any part of the cipher suite. This function also calls default filter functions
+%% to make sure the cipher suite are supported by crypto.
+%%--------------------------------------------------------------------
+filter_cipher_suites(Suites, Filters0) ->
+ #{key_exchange_filters := KexF,
+ cipher_filters := CipherF,
+ mac_filters := MacF,
+ prf_filters := PrfF}
+ = ssl_cipher:crypto_support_filters(),
+ Filters = #{key_exchange_filters => add_filter(proplists:get_value(key_exchange, Filters0), KexF),
+ cipher_filters => add_filter(proplists:get_value(cipher, Filters0), CipherF),
+ mac_filters => add_filter(proplists:get_value(mac, Filters0), MacF),
+ prf_filters => add_filter(proplists:get_value(prf, Filters0), PrfF)},
+ ssl_cipher:filter_suites(Suites, Filters).
+%%--------------------------------------------------------------------
+-spec prepend_cipher_suites([ssl_cipher:erl_cipher_suite()] |
+ [{key_exchange | cipher | mac | prf, fun()}],
+ [ssl_cipher:erl_cipher_suite()]) ->
+ [ssl_cipher:erl_cipher_suite()].
+%% Description: Make <Preferred> suites become the most prefered
+%% suites that is put them at the head of the cipher suite list
+%% and remove them from <Suites> if present. <Preferred> may be a
+%% list of cipher suits or a list of filters in which case the
+%% filters are use on Suites to extract the the preferred
+%% cipher list.
+%% --------------------------------------------------------------------
+prepend_cipher_suites([First | _] = Preferred, Suites0) when is_map(First) ->
+ Suites = Preferred ++ (Suites0 -- Preferred),
+ Suites;
+prepend_cipher_suites(Filters, Suites) ->
+ Preferred = filter_cipher_suites(Suites, Filters),
+ Preferred ++ (Suites -- Preferred).
+%%--------------------------------------------------------------------
+-spec append_cipher_suites(Deferred :: [ssl_cipher:erl_cipher_suite()] |
+ [{key_exchange | cipher | mac | prf, fun()}],
+ [ssl_cipher:erl_cipher_suite()]) ->
+ [ssl_cipher:erl_cipher_suite()].
+%% Description: Make <Deferred> suites suites become the
+%% least prefered suites that is put them at the end of the cipher suite list
+%% and removed them from <Suites> if present.
+%%
+%%--------------------------------------------------------------------
+append_cipher_suites([First | _] = Deferred, Suites0) when is_map(First)->
+ Suites = (Suites0 -- Deferred) ++ Deferred,
+ Suites;
+append_cipher_suites(Filters, Suites) ->
+ Deferred = filter_cipher_suites(Suites, Filters),
+ (Suites -- Deferred) ++ Deferred.
+
+%%--------------------------------------------------------------------
-spec eccs() -> tls_v1:curves().
%% Description: returns all supported curves across all versions
%%--------------------------------------------------------------------
@@ -410,6 +486,11 @@ eccs({3,0}) ->
eccs({3,_}) ->
Curves = tls_v1:ecc_curves(all),
eccs_filter_supported(Curves);
+eccs({_,_} = DTLSVersion) ->
+ eccs(dtls_v1:corresponding_tls_version(DTLSVersion));
+eccs(DTLSAtomVersion) when DTLSAtomVersion == 'dtlsv1';
+ DTLSAtomVersion == 'dtlsv2' ->
+ eccs(dtls_record:protocol_version(DTLSAtomVersion));
eccs(AtomVersion) when is_atom(AtomVersion) ->
eccs(tls_record:protocol_version(AtomVersion)).
@@ -542,16 +623,23 @@ sockname(#sslsocket{pid = Pid, fd = {Transport, Socket, _, _}}) when is_pid(Pid)
%%---------------------------------------------------------------
-spec versions() -> [{ssl_app, string()} | {supported, [tls_record:tls_atom_version()]} |
- {available, [tls_record:tls_atom_version()]}].
+ {supported_dtls, [dtls_record:dtls_atom_version()]} |
+ {available, [tls_record:tls_atom_version()]} |
+ {available_dtls, [dtls_record:dtls_atom_version()]}].
%%
%% Description: Returns a list of relevant versions.
%%--------------------------------------------------------------------
versions() ->
- Vsns = tls_record:supported_protocol_versions(),
- SupportedVsns = [tls_record:protocol_version(Vsn) || Vsn <- Vsns],
- AvailableVsns = ?ALL_AVAILABLE_VERSIONS,
- %% TODO Add DTLS versions when supported
- [{ssl_app, ?VSN}, {supported, SupportedVsns}, {available, AvailableVsns}].
+ TLSVsns = tls_record:supported_protocol_versions(),
+ DTLSVsns = dtls_record:supported_protocol_versions(),
+ SupportedTLSVsns = [tls_record:protocol_version(Vsn) || Vsn <- TLSVsns],
+ SupportedDTLSVsns = [dtls_record:protocol_version(Vsn) || Vsn <- DTLSVsns],
+ AvailableTLSVsns = ?ALL_AVAILABLE_VERSIONS,
+ AvailableDTLSVsns = ?ALL_AVAILABLE_DATAGRAM_VERSIONS,
+ [{ssl_app, ?VSN}, {supported, SupportedTLSVsns},
+ {supported_dtls, SupportedDTLSVsns},
+ {available, AvailableTLSVsns},
+ {available_dtls, AvailableDTLSVsns}].
%%---------------------------------------------------------------
@@ -631,16 +719,21 @@ tls_version({254, _} = Version) ->
%%%--------------------------------------------------------------
%%% Internal functions
%%%--------------------------------------------------------------------
-
%% Possible filters out suites not supported by crypto
available_suites(default) ->
Version = tls_record:highest_protocol_version([]),
ssl_cipher:filter_suites(ssl_cipher:suites(Version));
-
available_suites(all) ->
Version = tls_record:highest_protocol_version([]),
ssl_cipher:filter_suites(ssl_cipher:all_suites(Version)).
+supported_suites(default, Version) ->
+ ssl_cipher:suites(Version);
+supported_suites(all, Version) ->
+ ssl_cipher:all_suites(Version);
+supported_suites(anonymous, Version) ->
+ ssl_cipher:anonymous_suites(Version).
+
do_listen(Port, #config{transport_info = {Transport, _, _, _}} = Config, tls_connection) ->
tls_socket:listen(Transport, Port, Config);
@@ -750,7 +843,7 @@ handle_options(Opts0, Role, Host) ->
%% Server side option
reuse_session = handle_option(reuse_session, Opts, ReuseSessionFun),
reuse_sessions = handle_option(reuse_sessions, Opts, true),
- secure_renegotiate = handle_option(secure_renegotiate, Opts, false),
+ secure_renegotiate = handle_option(secure_renegotiate, Opts, true),
client_renegotiation = handle_option(client_renegotiation, Opts,
default_option_role(server, true, Role),
server, Role),
@@ -788,7 +881,6 @@ handle_options(Opts0, Role, Host) ->
client, Role),
crl_check = handle_option(crl_check, Opts, false),
crl_cache = handle_option(crl_cache, Opts, {ssl_crl_cache, {internal, []}}),
- v2_hello_compatible = handle_option(v2_hello_compatible, Opts, false),
max_handshake_size = handle_option(max_handshake_size, Opts, ?DEFAULT_MAX_HANDSHAKE_SIZE)
},
@@ -804,7 +896,7 @@ handle_options(Opts0, Role, Host) ->
alpn_preferred_protocols, next_protocols_advertised,
client_preferred_next_protocols, log_alert,
server_name_indication, honor_cipher_order, padding_check, crl_check, crl_cache,
- fallback, signature_algs, eccs, honor_ecc_order, beast_mitigation, v2_hello_compatible,
+ fallback, signature_algs, eccs, honor_ecc_order, beast_mitigation,
max_handshake_size],
SockOpts = lists:foldl(fun(Key, PropList) ->
@@ -1041,8 +1133,6 @@ validate_option(beast_mitigation, Value) when Value == one_n_minus_one orelse
Value == zero_n orelse
Value == disabled ->
Value;
-validate_option(v2_hello_compatible, Value) when is_boolean(Value) ->
- Value;
validate_option(max_handshake_size, Value) when is_integer(Value) andalso Value =< ?MAX_UNIT24 ->
Value;
validate_option(protocol, Value = tls) ->
@@ -1150,17 +1240,21 @@ handle_cipher_option(Value, Version) when is_list(Value) ->
binary_cipher_suites(Version, []) ->
%% Defaults to all supported suites that does
%% not require explicit configuration
- ssl_cipher:filter_suites(ssl_cipher:suites(tls_version(Version)));
+ default_binary_suites(Version);
+binary_cipher_suites(Version, [Map|_] = Ciphers0) when is_map(Map) ->
+ Ciphers = [ssl_cipher:suite(C) || C <- Ciphers0],
+ binary_cipher_suites(Version, Ciphers);
binary_cipher_suites(Version, [Tuple|_] = Ciphers0) when is_tuple(Tuple) ->
Ciphers = [ssl_cipher:suite(tuple_to_map(C)) || C <- Ciphers0],
binary_cipher_suites(Version, Ciphers);
binary_cipher_suites(Version, [Cipher0 | _] = Ciphers0) when is_binary(Cipher0) ->
- All = ssl_cipher:all_suites(tls_version(Version)),
+ All = ssl_cipher:all_suites(Version) ++
+ ssl_cipher:anonymous_suites(Version),
case [Cipher || Cipher <- Ciphers0, lists:member(Cipher, All)] of
[] ->
%% Defaults to all supported suites that does
%% not require explicit configuration
- ssl_cipher:filter_suites(ssl_cipher:suites(tls_version(Version)));
+ default_binary_suites(Version);
Ciphers ->
Ciphers
end;
@@ -1173,6 +1267,9 @@ binary_cipher_suites(Version, Ciphers0) ->
Ciphers = [ssl_cipher:openssl_suite(C) || C <- string:lexemes(Ciphers0, ":")],
binary_cipher_suites(Version, Ciphers).
+default_binary_suites(Version) ->
+ ssl_cipher:filter_suites(ssl_cipher:suites(Version)).
+
tuple_to_map({Kex, Cipher, Mac}) ->
#{key_exchange => Kex,
cipher => Cipher,
@@ -1181,9 +1278,19 @@ tuple_to_map({Kex, Cipher, Mac}) ->
tuple_to_map({Kex, Cipher, Mac, Prf}) ->
#{key_exchange => Kex,
cipher => Cipher,
- mac => Mac,
+ mac => tuple_to_map_mac(Cipher, Mac),
prf => Prf}.
+%% Backwards compatible
+tuple_to_map_mac(aes_128_gcm, _) ->
+ aead;
+tuple_to_map_mac(aes_256_gcm, _) ->
+ aead;
+tuple_to_map_mac(chacha20_poly1305, _) ->
+ aead;
+tuple_to_map_mac(_, MAC) ->
+ MAC.
+
handle_eccs_option(Value, Version) when is_list(Value) ->
{_Major, Minor} = tls_version(Version),
try tls_v1:ecc_curves(Minor, Value) of
@@ -1462,3 +1569,8 @@ reject_alpn_next_prot_options([Opt| AlpnNextOpts], Opts) ->
false ->
reject_alpn_next_prot_options(AlpnNextOpts, Opts)
end.
+
+add_filter(undefined, Filters) ->
+ Filters;
+add_filter(Filter, Filters) ->
+ [Filter | Filters].
diff --git a/lib/ssl/src/ssl_alert.erl b/lib/ssl/src/ssl_alert.erl
index 95ab955ad0..fc7b1e6d1c 100644
--- a/lib/ssl/src/ssl_alert.erl
+++ b/lib/ssl/src/ssl_alert.erl
@@ -48,7 +48,9 @@ decode(Bin) ->
decode(Bin, [], 0).
%%--------------------------------------------------------------------
--spec reason_code(#alert{}, client | server) -> closed | {essl, string()}.
+-spec reason_code(#alert{}, client | server) ->
+ closed | {tls_alert, unicode:chardata()}.
+%-spec reason_code(#alert{}, client | server) -> closed | {essl, string()}.
%%
%% Description: Returns the error reason that will be returned to the
%% user.
diff --git a/lib/ssl/src/ssl_cipher.erl b/lib/ssl/src/ssl_cipher.erl
index 7c5cff3665..28b26fd358 100644
--- a/lib/ssl/src/ssl_cipher.erl
+++ b/lib/ssl/src/ssl_cipher.erl
@@ -36,9 +36,11 @@
-export([security_parameters/2, security_parameters/3, suite_definition/1,
erl_suite_definition/1,
cipher_init/3, decipher/6, cipher/5, decipher_aead/6, cipher_aead/6,
- suite/1, suites/1, all_suites/1,
- ec_keyed_suites/0, chacha_suites/1, anonymous_suites/1, psk_suites/1, srp_suites/0,
- rc4_suites/1, des_suites/1, rsa_suites/1, openssl_suite/1, openssl_suite_name/1, filter/2, filter_suites/1,
+ suite/1, suites/1, all_suites/1, crypto_support_filters/0,
+ ec_keyed_suites/0, chacha_suites/1, anonymous_suites/1, psk_suites/1, psk_suites_anon/1,
+ srp_suites/0, srp_suites_anon/0,
+ rc4_suites/1, des_suites/1, rsa_suites/1, openssl_suite/1, openssl_suite_name/1,
+ filter/2, filter_suites/1, filter_suites/2,
hash_algorithm/1, sign_algorithm/1, is_acceptable_hash/2, is_fallback/1,
random_bytes/1, calc_mac_hash/4,
is_stream_ciphersuite/1]).
@@ -53,7 +55,7 @@
-type key_algo() :: null | rsa | dhe_rsa | dhe_dss | ecdhe_ecdsa| ecdh_ecdsa | ecdh_rsa| srp_rsa| srp_dss | psk | dhe_psk | rsa_psk | dh_anon | ecdh_anon | srp_anon.
-type erl_cipher_suite() :: #{key_exchange := key_algo(),
cipher := cipher(),
- mac := hash(),
+ mac := hash() | aead,
prf := hash() | default_prf %% Old cipher suites, version dependent
}.
-type old_erl_cipher_suite() :: {key_algo(), cipher(), hash()} % Pre TLS 1.2
@@ -94,7 +96,7 @@ security_parameters(Version, CipherSuite, SecParams) ->
expanded_key_material_length = expanded_key_material(Cipher),
key_material_length = key_material(Cipher),
iv_size = iv_size(Cipher),
- mac_algorithm = hash_algorithm(Hash),
+ mac_algorithm = mac_algorithm(Hash),
prf_algorithm = prf_algorithm(PrfHashAlg, Version),
hash_size = hash_size(Hash)}.
@@ -237,7 +239,7 @@ decipher(?AES_CBC, HashSz, CipherState, Fragment, Version, PaddingCheck) ->
%%--------------------------------------------------------------------
-spec decipher_aead(cipher_enum(), #cipher_state{}, integer(), binary(), binary(), ssl_record:ssl_version()) ->
- {binary(), binary(), #cipher_state{}} | #alert{}.
+ {binary(), #cipher_state{}} | #alert{}.
%%
%% Description: Decrypts the data and checks the associated data (AAD) MAC using
%% cipher described by cipher_enum() and updating the cipher state.
@@ -321,12 +323,12 @@ suites({_, Minor}) ->
all_suites({3, _} = Version) ->
suites(Version)
++ chacha_suites(Version)
- ++ anonymous_suites(Version)
++ psk_suites(Version)
++ srp_suites()
++ rc4_suites(Version)
++ des_suites(Version)
++ rsa_suites(Version);
+
all_suites(Version) ->
dtls_v1:all_suites(Version).
%%--------------------------------------------------------------------
@@ -350,12 +352,12 @@ chacha_suites(_) ->
%% if explicitly set by user. Intended only for testing.
%%--------------------------------------------------------------------
anonymous_suites({3, N}) ->
- anonymous_suites(N);
+ srp_suites_anon() ++ anonymous_suites(N);
anonymous_suites({254, _} = Version) ->
- anonymous_suites(dtls_v1:corresponding_tls_version(Version))
- -- [?TLS_DH_anon_WITH_RC4_128_MD5];
+ dtls_v1:anonymous_suites(Version);
anonymous_suites(N)
when N >= 3 ->
+ psk_suites_anon(N) ++
[?TLS_DH_anon_WITH_AES_128_GCM_SHA256,
?TLS_DH_anon_WITH_AES_256_GCM_SHA384,
?TLS_DH_anon_WITH_AES_128_CBC_SHA256,
@@ -364,20 +366,20 @@ anonymous_suites(N)
?TLS_ECDH_anon_WITH_AES_256_CBC_SHA,
?TLS_ECDH_anon_WITH_3DES_EDE_CBC_SHA,
?TLS_DH_anon_WITH_RC4_128_MD5];
-
-anonymous_suites(2) ->
+anonymous_suites(2 = N) ->
+ psk_suites_anon(N) ++
[?TLS_ECDH_anon_WITH_AES_128_CBC_SHA,
?TLS_ECDH_anon_WITH_AES_256_CBC_SHA,
?TLS_ECDH_anon_WITH_3DES_EDE_CBC_SHA,
?TLS_DH_anon_WITH_DES_CBC_SHA,
?TLS_DH_anon_WITH_RC4_128_MD5];
-
anonymous_suites(N) when N == 0;
N == 1 ->
- [?TLS_DH_anon_WITH_RC4_128_MD5,
- ?TLS_DH_anon_WITH_3DES_EDE_CBC_SHA,
- ?TLS_DH_anon_WITH_DES_CBC_SHA
- ].
+ psk_suites_anon(N) ++
+ [?TLS_DH_anon_WITH_RC4_128_MD5,
+ ?TLS_DH_anon_WITH_3DES_EDE_CBC_SHA,
+ ?TLS_DH_anon_WITH_DES_CBC_SHA
+ ].
%%--------------------------------------------------------------------
-spec psk_suites(ssl_record:ssl_version() | integer()) -> [cipher_suite()].
@@ -390,41 +392,52 @@ psk_suites({3, N}) ->
psk_suites(N)
when N >= 3 ->
[
- ?TLS_ECDHE_PSK_WITH_AES_256_GCM_SHA384,
- ?TLS_DHE_PSK_WITH_AES_256_GCM_SHA384,
?TLS_RSA_PSK_WITH_AES_256_GCM_SHA384,
+ ?TLS_RSA_PSK_WITH_AES_256_CBC_SHA384,
+ ?TLS_RSA_PSK_WITH_AES_128_GCM_SHA256,
+ ?TLS_RSA_PSK_WITH_AES_128_CBC_SHA256
+ ] ++ psk_suites(0);
+psk_suites(_) ->
+ [?TLS_RSA_PSK_WITH_AES_256_CBC_SHA,
+ ?TLS_RSA_PSK_WITH_AES_128_CBC_SHA,
+ ?TLS_RSA_PSK_WITH_3DES_EDE_CBC_SHA,
+ ?TLS_RSA_PSK_WITH_RC4_128_SHA].
+
+%%--------------------------------------------------------------------
+-spec psk_suites_anon(ssl_record:ssl_version() | integer()) -> [cipher_suite()].
+%%
+%% Description: Returns a list of the anonymous PSK cipher suites, only supported
+%% if explicitly set by user.
+%%--------------------------------------------------------------------
+psk_suites_anon({3, N}) ->
+ psk_suites_anon(N);
+psk_suites_anon(N)
+ when N >= 3 ->
+ [
+ ?TLS_DHE_PSK_WITH_AES_256_GCM_SHA384,
?TLS_PSK_WITH_AES_256_GCM_SHA384,
?TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA384,
?TLS_DHE_PSK_WITH_AES_256_CBC_SHA384,
- ?TLS_RSA_PSK_WITH_AES_256_CBC_SHA384,
?TLS_PSK_WITH_AES_256_CBC_SHA384,
?TLS_ECDHE_PSK_WITH_AES_128_GCM_SHA256,
?TLS_DHE_PSK_WITH_AES_128_GCM_SHA256,
- ?TLS_RSA_PSK_WITH_AES_128_GCM_SHA256,
?TLS_PSK_WITH_AES_128_GCM_SHA256,
?TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256,
?TLS_DHE_PSK_WITH_AES_128_CBC_SHA256,
- ?TLS_RSA_PSK_WITH_AES_128_CBC_SHA256,
?TLS_PSK_WITH_AES_128_CBC_SHA256
- ] ++ psk_suites(0);
-psk_suites(_) ->
- [?TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA,
- ?TLS_DHE_PSK_WITH_AES_256_CBC_SHA,
- ?TLS_RSA_PSK_WITH_AES_256_CBC_SHA,
+ ] ++ psk_suites_anon(0);
+psk_suites_anon(_) ->
+ [?TLS_DHE_PSK_WITH_AES_256_CBC_SHA,
?TLS_PSK_WITH_AES_256_CBC_SHA,
?TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA,
?TLS_DHE_PSK_WITH_AES_128_CBC_SHA,
- ?TLS_RSA_PSK_WITH_AES_128_CBC_SHA,
?TLS_PSK_WITH_AES_128_CBC_SHA,
?TLS_ECDHE_PSK_WITH_3DES_EDE_CBC_SHA,
?TLS_DHE_PSK_WITH_3DES_EDE_CBC_SHA,
- ?TLS_RSA_PSK_WITH_3DES_EDE_CBC_SHA,
?TLS_PSK_WITH_3DES_EDE_CBC_SHA,
?TLS_ECDHE_PSK_WITH_RC4_128_SHA,
?TLS_DHE_PSK_WITH_RC4_128_SHA,
- ?TLS_RSA_PSK_WITH_RC4_128_SHA,
?TLS_PSK_WITH_RC4_128_SHA].
-
%%--------------------------------------------------------------------
-spec srp_suites() -> [cipher_suite()].
%%
@@ -432,15 +445,24 @@ psk_suites(_) ->
%% if explicitly set by user.
%%--------------------------------------------------------------------
srp_suites() ->
- [?TLS_SRP_SHA_WITH_3DES_EDE_CBC_SHA,
- ?TLS_SRP_SHA_RSA_WITH_3DES_EDE_CBC_SHA,
+ [?TLS_SRP_SHA_RSA_WITH_3DES_EDE_CBC_SHA,
?TLS_SRP_SHA_DSS_WITH_3DES_EDE_CBC_SHA,
- ?TLS_SRP_SHA_WITH_AES_128_CBC_SHA,
?TLS_SRP_SHA_RSA_WITH_AES_128_CBC_SHA,
?TLS_SRP_SHA_DSS_WITH_AES_128_CBC_SHA,
- ?TLS_SRP_SHA_WITH_AES_256_CBC_SHA,
?TLS_SRP_SHA_RSA_WITH_AES_256_CBC_SHA,
?TLS_SRP_SHA_DSS_WITH_AES_256_CBC_SHA].
+
+%%--------------------------------------------------------------------
+-spec srp_suites_anon() -> [cipher_suite()].
+%%
+%% Description: Returns a list of the SRP anonymous cipher suites, only supported
+%% if explicitly set by user.
+%%--------------------------------------------------------------------
+srp_suites_anon() ->
+ [?TLS_SRP_SHA_WITH_3DES_EDE_CBC_SHA,
+ ?TLS_SRP_SHA_WITH_AES_128_CBC_SHA,
+ ?TLS_SRP_SHA_WITH_AES_256_CBC_SHA].
+
%%--------------------------------------------------------------------
-spec rc4_suites(Version::ssl_record:ssl_version() | integer()) -> [cipher_suite()].
%%
@@ -750,32 +772,32 @@ suite_definition(?TLS_RSA_PSK_WITH_NULL_SHA) ->
suite_definition(?TLS_PSK_WITH_AES_128_GCM_SHA256) ->
#{key_exchange => psk,
cipher => aes_128_gcm,
- mac => null,
+ mac => aead,
prf => sha256};
suite_definition(?TLS_PSK_WITH_AES_256_GCM_SHA384) ->
#{key_exchange => psk,
cipher => aes_256_gcm,
- mac => null,
+ mac => aead,
prf => sha384};
suite_definition(?TLS_DHE_PSK_WITH_AES_128_GCM_SHA256) ->
#{key_exchange => dhe_psk,
cipher => aes_128_gcm,
- mac => null,
+ mac => aead,
prf => sha256};
suite_definition(?TLS_DHE_PSK_WITH_AES_256_GCM_SHA384) ->
#{key_exchange => dhe_psk,
cipher => aes_256_gcm,
- mac => null,
+ mac => aead,
prf => sha384};
suite_definition(?TLS_RSA_PSK_WITH_AES_128_GCM_SHA256) ->
#{key_exchange => rsa_psk,
cipher => aes_128_gcm,
- mac => null,
+ mac => aead,
prf => sha256};
suite_definition(?TLS_RSA_PSK_WITH_AES_256_GCM_SHA384) ->
#{key_exchange => rsa_psk,
cipher => aes_256_gcm,
- mac => null,
+ mac => aead,
prf => sha384};
suite_definition(?TLS_PSK_WITH_AES_128_CBC_SHA256) ->
#{key_exchange => psk,
@@ -1115,42 +1137,42 @@ suite_definition(?TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384) ->
suite_definition(?TLS_RSA_WITH_AES_128_GCM_SHA256) ->
#{key_exchange => rsa,
cipher => aes_128_gcm,
- mac => null,
+ mac => aead,
prf => sha256};
suite_definition(?TLS_RSA_WITH_AES_256_GCM_SHA384) ->
#{key_exchange => rsa,
cipher => aes_256_gcm,
- mac => null,
+ mac => aead,
prf => sha384};
suite_definition(?TLS_DHE_RSA_WITH_AES_128_GCM_SHA256) ->
#{key_exchange => dhe_rsa,
cipher => aes_128_gcm,
- mac => null,
+ mac => aead,
prf => sha256};
suite_definition(?TLS_DHE_RSA_WITH_AES_256_GCM_SHA384) ->
#{key_exchange => dhe_rsa,
cipher => aes_256_gcm,
- mac => null,
+ mac => aead,
prf => sha384};
suite_definition(?TLS_DH_RSA_WITH_AES_128_GCM_SHA256) ->
#{key_exchange => dh_rsa,
cipher => aes_128_gcm,
- mac => null,
+ mac => aead,
prf => sha256};
suite_definition(?TLS_DH_RSA_WITH_AES_256_GCM_SHA384) ->
#{key_exchange => dh_rsa,
cipher => aes_256_gcm,
- mac => null,
+ mac => aead,
prf => sha384};
suite_definition(?TLS_DHE_DSS_WITH_AES_128_GCM_SHA256) ->
#{key_exchange => dhe_dss,
cipher => aes_128_gcm,
- mac => null,
+ mac => aead,
prf => sha256};
suite_definition(?TLS_DHE_DSS_WITH_AES_256_GCM_SHA384) ->
#{key_exchange => dhe_dss,
cipher => aes_256_gcm,
- mac => null,
+ mac => aead,
prf => sha384};
suite_definition(?TLS_DH_DSS_WITH_AES_128_GCM_SHA256) ->
#{key_exchange => dh_dss,
@@ -1160,74 +1182,74 @@ suite_definition(?TLS_DH_DSS_WITH_AES_128_GCM_SHA256) ->
suite_definition(?TLS_DH_DSS_WITH_AES_256_GCM_SHA384) ->
#{key_exchange => dh_dss,
cipher => aes_256_gcm,
- mac => null,
+ mac => aead,
prf => sha384};
suite_definition(?TLS_DH_anon_WITH_AES_128_GCM_SHA256) ->
#{key_exchange => dh_anon,
cipher => aes_128_gcm,
- mac => null,
+ mac => aead,
prf => sha256};
suite_definition(?TLS_DH_anon_WITH_AES_256_GCM_SHA384) ->
#{key_exchange => dh_anon,
cipher => aes_256_gcm,
- mac => null,
+ mac => aead,
prf => sha384};
%% RFC 5289 ECC AES-GCM Cipher Suites
suite_definition(?TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256) ->
#{key_exchange => ecdhe_ecdsa,
cipher => aes_128_gcm,
- mac => null,
+ mac => aead,
prf => sha256};
suite_definition(?TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384) ->
#{key_exchange => ecdhe_ecdsa,
cipher => aes_256_gcm,
- mac => null,
+ mac => aead,
prf => sha384};
suite_definition(?TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256) ->
#{key_exchange => ecdh_ecdsa,
cipher => aes_128_gcm,
- mac => null,
+ mac => aead,
prf => sha256};
suite_definition(?TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384) ->
#{key_exchange => ecdh_ecdsa,
cipher => aes_256_gcm,
- mac => null,
+ mac => aead,
prf => sha384};
suite_definition(?TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256) ->
#{key_exchange => ecdhe_rsa,
cipher => aes_128_gcm,
- mac => null,
+ mac => aead,
prf => sha256};
suite_definition(?TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) ->
#{key_exchange => ecdhe_rsa,
cipher => aes_256_gcm,
- mac => null,
+ mac => aead,
prf => sha384};
suite_definition(?TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256) ->
#{key_exchange => ecdh_rsa,
cipher => aes_128_gcm,
- mac => null,
+ mac => aead,
prf => sha256};
suite_definition(?TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384) ->
#{key_exchange => ecdh_rsa,
cipher => aes_256_gcm,
- mac => null,
+ mac => aead,
prf => sha384};
%% draft-agl-tls-chacha20poly1305-04 Chacha20/Poly1305 Suites
suite_definition(?TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256) ->
#{key_exchange => ecdhe_rsa,
cipher => chacha20_poly1305,
- mac => null,
+ mac => aead,
prf => sha256};
suite_definition(?TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256) ->
#{key_exchange => ecdhe_ecdsa,
cipher => chacha20_poly1305,
- mac => null,
+ mac => aead,
prf => sha256};
suite_definition(?TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256) ->
#{key_exchange => dhe_rsa,
cipher => chacha20_poly1305,
- mac => null,
+ mac => aead,
prf => sha256}.
%%--------------------------------------------------------------------
@@ -1428,32 +1450,32 @@ suite(#{key_exchange := rsa_psk,
%%% TLS 1.2 PSK Cipher Suites RFC 5487
suite(#{key_exchange := psk,
cipher := aes_128_gcm,
- mac := null,
+ mac := aead,
prf := sha256}) ->
?TLS_PSK_WITH_AES_128_GCM_SHA256;
suite(#{key_exchange := psk,
cipher := aes_256_gcm,
- mac := null,
+ mac := aead,
prf := sha384}) ->
?TLS_PSK_WITH_AES_256_GCM_SHA384;
suite(#{key_exchange := dhe_psk,
cipher := aes_128_gcm,
- mac := null,
+ mac := aead,
prf := sha256}) ->
?TLS_DHE_PSK_WITH_AES_128_GCM_SHA256;
suite(#{key_exchange := dhe_psk,
cipher := aes_256_gcm,
- mac := null,
+ mac := aead,
prf := sha384}) ->
?TLS_DHE_PSK_WITH_AES_256_GCM_SHA384;
suite(#{key_exchange := rsa_psk,
cipher := aes_128_gcm,
- mac := null,
+ mac := aead,
prf := sha256}) ->
?TLS_RSA_PSK_WITH_AES_128_GCM_SHA256;
suite(#{key_exchange := rsa_psk,
cipher := aes_256_gcm,
- mac := null,
+ mac := aead,
prf := sha384}) ->
?TLS_RSA_PSK_WITH_AES_256_GCM_SHA384;
suite(#{key_exchange := psk,
@@ -1739,119 +1761,119 @@ suite(#{key_exchange := ecdh_rsa,
%% RFC 5288 AES-GCM Cipher Suites
suite(#{key_exchange := rsa,
cipher := aes_128_gcm,
- mac := null,
+ mac := aead,
prf := sha256}) ->
?TLS_RSA_WITH_AES_128_GCM_SHA256;
suite(#{key_exchange := rsa,
cipher := aes_256_gcm,
- mac := null,
+ mac := aead,
prf := sha384}) ->
?TLS_RSA_WITH_AES_256_GCM_SHA384;
suite(#{key_exchange := dhe_rsa,
cipher := aes_128_gcm,
- mac := null,
+ mac := aead,
prf := sha256}) ->
?TLS_DHE_RSA_WITH_AES_128_GCM_SHA256;
suite(#{key_exchange := dhe_rsa,
cipher := aes_256_gcm,
- mac := null,
+ mac := aead,
prf := sha384}) ->
?TLS_DHE_RSA_WITH_AES_256_GCM_SHA384;
suite(#{key_exchange := dh_rsa,
cipher := aes_128_gcm,
- mac := null,
+ mac := aead,
prf := sha256}) ->
?TLS_DH_RSA_WITH_AES_128_GCM_SHA256;
suite(#{key_exchange := dh_rsa,
cipher := aes_256_gcm,
- mac := null,
+ mac := aead,
prf := sha384}) ->
?TLS_DH_RSA_WITH_AES_256_GCM_SHA384;
suite(#{key_exchange := dhe_dss,
cipher := aes_128_gcm,
- mac := null,
+ mac := aead,
prf := sha256}) ->
?TLS_DHE_DSS_WITH_AES_128_GCM_SHA256;
suite(#{key_exchange := dhe_dss,
cipher := aes_256_gcm,
- mac := null,
+ mac := aead,
prf := sha384}) ->
?TLS_DHE_DSS_WITH_AES_256_GCM_SHA384;
suite(#{key_exchange := dh_dss,
cipher := aes_128_gcm,
- mac := null,
+ mac := aead,
prf := sha256}) ->
?TLS_DH_DSS_WITH_AES_128_GCM_SHA256;
suite(#{key_exchange := dh_dss,
cipher := aes_256_gcm,
- mac := null,
+ mac := aead,
prf := sha384}) ->
?TLS_DH_DSS_WITH_AES_256_GCM_SHA384;
suite(#{key_exchange := dh_anon,
cipher := aes_128_gcm,
- mac := null,
+ mac := aead,
prf := sha256}) ->
?TLS_DH_anon_WITH_AES_128_GCM_SHA256;
suite(#{key_exchange := dh_anon,
cipher := aes_256_gcm,
- mac := null,
+ mac := aead,
prf := sha384}) ->
?TLS_DH_anon_WITH_AES_256_GCM_SHA384;
%% RFC 5289 ECC AES-GCM Cipher Suites
suite(#{key_exchange := ecdhe_ecdsa,
cipher := aes_128_gcm,
- mac := null,
+ mac := aead,
prf := sha256}) ->
?TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256;
suite(#{key_exchange := ecdhe_ecdsa,
cipher := aes_256_gcm,
- mac := null,
+ mac := aead,
prf := sha384}) ->
?TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384;
suite(#{key_exchange := ecdh_ecdsa,
cipher := aes_128_gcm,
- mac := null,
+ mac := aead,
prf := sha256}) ->
?TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256;
suite(#{key_exchange := ecdh_ecdsa,
cipher := aes_256_gcm,
- mac := null,
+ mac := aead,
prf := sha384}) ->
?TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384;
suite(#{key_exchange := ecdhe_rsa,
cipher := aes_128_gcm,
- mac := null,
+ mac := aead,
prf := sha256}) ->
?TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256;
suite(#{key_exchange := ecdhe_rsa,
cipher := aes_256_gcm,
- mac := null,
+ mac := aead,
prf := sha384}) ->
?TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384;
suite(#{key_exchange := ecdh_rsa,
cipher := aes_128_gcm,
- mac := null,
+ mac := aead,
prf := sha256}) ->
?TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256;
suite(#{key_exchange := ecdh_rsa,
cipher := aes_256_gcm,
- mac := null,
+ mac := aead,
prf := sha384}) ->
?TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384;
%% draft-agl-tls-chacha20poly1305-04 Chacha20/Poly1305 Suites
suite(#{key_exchange := ecdhe_rsa,
cipher := chacha20_poly1305,
- mac := null,
+ mac := aead,
prf := sha256}) ->
?TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256;
suite(#{key_exchange := ecdhe_ecdsa,
cipher := chacha20_poly1305,
- mac := null,
+ mac := aead,
prf := sha256}) ->
?TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256;
suite(#{key_exchange := dhe_rsa,
cipher := chacha20_poly1305,
- mac := null,
+ mac := aead,
prf := sha256}) ->
?TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256.
@@ -2011,9 +2033,9 @@ openssl_suite("ECDH-RSA-AES256-GCM-SHA384") ->
?TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384.
%%--------------------------------------------------------------------
--spec openssl_suite_name(cipher_suite()) -> openssl_cipher_suite().
+-spec openssl_suite_name(cipher_suite()) -> openssl_cipher_suite() | erl_cipher_suite().
%%
-%% Description: Return openssl cipher suite name.
+%% Description: Return openssl cipher suite name if possible
%%-------------------------------------------------------------------
openssl_suite_name(?TLS_DHE_RSA_WITH_AES_256_CBC_SHA) ->
"DHE-RSA-AES256-SHA";
@@ -2223,38 +2245,74 @@ filter(DerCert, Ciphers) ->
{_, ecdsa} ->
Ciphers1 -- rsa_signed_suites()
end.
-
%%--------------------------------------------------------------------
--spec filter_suites([cipher_suite()]) -> [cipher_suite()].
+-spec filter_suites([erl_cipher_suite()] | [cipher_suite()], map()) ->
+ [erl_cipher_suite()] | [cipher_suite()].
+%%
+%% Description: Filter suites using supplied filter funs
+%%-------------------------------------------------------------------
+filter_suites(Suites, Filters) ->
+ ApplyFilters = fun(Suite) ->
+ filter_suite(Suite, Filters)
+ end,
+ lists:filter(ApplyFilters, Suites).
+
+filter_suite(#{key_exchange := KeyExchange,
+ cipher := Cipher,
+ mac := Hash,
+ prf := Prf},
+ #{key_exchange_filters := KeyFilters,
+ cipher_filters := CipherFilters,
+ mac_filters := HashFilters,
+ prf_filters := PrfFilters}) ->
+ all_filters(KeyExchange, KeyFilters) andalso
+ all_filters(Cipher, CipherFilters) andalso
+ all_filters(Hash, HashFilters) andalso
+ all_filters(Prf, PrfFilters);
+filter_suite(Suite, Filters) ->
+ filter_suite(suite_definition(Suite), Filters).
+
+%%--------------------------------------------------------------------
+-spec filter_suites([erl_cipher_suite()] | [cipher_suite()]) ->
+ [erl_cipher_suite()] | [cipher_suite()].
%%
%% Description: Filter suites for algorithms supported by crypto.
%%-------------------------------------------------------------------
-filter_suites(Suites = [Value|_]) when is_map(Value) ->
- Algos = crypto:supports(),
- Hashs = proplists:get_value(hashs, Algos),
- lists:filter(fun(#{key_exchange := KeyExchange,
- cipher := Cipher,
- mac := Hash,
- prf := Prf}) ->
- is_acceptable_keyexchange(KeyExchange, proplists:get_value(public_keys, Algos)) andalso
- is_acceptable_cipher(Cipher, proplists:get_value(ciphers, Algos)) andalso
- is_acceptable_hash(Hash, Hashs) andalso
- is_acceptable_prf(Prf, Hashs)
- end, Suites);
-
filter_suites(Suites) ->
+ Filters = crypto_support_filters(),
+ filter_suites(Suites, Filters).
+
+all_filters(_, []) ->
+ true;
+all_filters(Value, [Filter| Rest]) ->
+ case Filter(Value) of
+ true ->
+ all_filters(Value, Rest);
+ false ->
+ false
+ end.
+crypto_support_filters() ->
Algos = crypto:supports(),
Hashs = proplists:get_value(hashs, Algos),
- lists:filter(fun(Suite) ->
- #{key_exchange := KeyExchange,
- cipher := Cipher,
- mac := Hash,
- prf := Prf} = suite_definition(Suite),
- is_acceptable_keyexchange(KeyExchange, proplists:get_value(public_keys, Algos)) andalso
- is_acceptable_cipher(Cipher, proplists:get_value(ciphers, Algos)) andalso
- is_acceptable_hash(Hash, Hashs) andalso
- is_acceptable_prf(Prf, Hashs)
- end, Suites).
+ #{key_exchange_filters =>
+ [fun(KeyExchange) ->
+ is_acceptable_keyexchange(KeyExchange,
+ proplists:get_value(public_keys, Algos))
+ end],
+ cipher_filters =>
+ [fun(Cipher) ->
+ is_acceptable_cipher(Cipher,
+ proplists:get_value(ciphers, Algos))
+ end],
+ mac_filters =>
+ [fun(Hash) ->
+ is_acceptable_hash(Hash, Hashs)
+ end],
+ prf_filters =>
+ [fun(Prf) ->
+ is_acceptable_prf(Prf,
+ proplists:get_value(hashs, Algos))
+ end]}.
is_acceptable_keyexchange(KeyExchange, _Algos) when KeyExchange == psk;
KeyExchange == null ->
@@ -2314,6 +2372,8 @@ is_acceptable_cipher(Cipher, Algos) ->
is_acceptable_hash(null, _Algos) ->
true;
+is_acceptable_hash(aead, _Algos) ->
+ true;
is_acceptable_hash(Hash, Algos) ->
proplists:get_bool(Hash, Algos).
@@ -2473,6 +2533,11 @@ prf_algorithm(default_prf, {3, _}) ->
prf_algorithm(Algo, _) ->
hash_algorithm(Algo).
+mac_algorithm(aead) ->
+ aead;
+mac_algorithm(Algo) ->
+ hash_algorithm(Algo).
+
hash_algorithm(null) -> ?NULL;
hash_algorithm(md5) -> ?MD5;
hash_algorithm(sha) -> ?SHA; %% Only sha always refers to "SHA-1"
@@ -2503,6 +2568,10 @@ sign_algorithm(Other) when is_integer(Other) andalso ((Other >= 224) and (Other
hash_size(null) ->
0;
+%% The AEAD MAC hash size is not used in the context
+%% of calculating the master secret. See RFC 5246 Section 6.2.3.3.
+hash_size(aead) ->
+ 0;
hash_size(md5) ->
16;
hash_size(sha) ->
diff --git a/lib/ssl/src/ssl_connection.erl b/lib/ssl/src/ssl_connection.erl
index d046145dff..f493c93726 100644
--- a/lib/ssl/src/ssl_connection.erl
+++ b/lib/ssl/src/ssl_connection.erl
@@ -1119,8 +1119,7 @@ handle_common_event(internal, {handshake, {#hello_request{}, _}}, StateName, #st
when StateName =/= connection ->
{keep_state_and_data};
handle_common_event(internal, {handshake, {Handshake, Raw}}, StateName,
- #state{tls_handshake_history = Hs0,
- ssl_options = #ssl_options{v2_hello_compatible = V2HComp}} = State0,
+ #state{tls_handshake_history = Hs0} = State0,
Connection) ->
PossibleSNI = Connection:select_sni_extension(Handshake),
@@ -1128,7 +1127,7 @@ handle_common_event(internal, {handshake, {Handshake, Raw}}, StateName,
%% a client_hello, which needs to be determined by the connection callback.
%% In other cases this is a noop
State = handle_sni_extension(PossibleSNI, State0),
- HsHist = ssl_handshake:update_handshake_history(Hs0, iolist_to_binary(Raw), V2HComp),
+ HsHist = ssl_handshake:update_handshake_history(Hs0, iolist_to_binary(Raw)),
{next_state, StateName, State#state{tls_handshake_history = HsHist},
[{next_event, internal, Handshake}]};
handle_common_event(internal, {protocol_record, TLSorDTLSRecord}, StateName, State, Connection) ->
@@ -1148,8 +1147,8 @@ handle_common_event(internal, #change_cipher_spec{type = <<1>>}, StateName,
StateName, State);
handle_common_event(_Type, Msg, StateName, #state{negotiated_version = Version} = State,
_) ->
- Alert = ?ALERT_REC(?FATAL,?UNEXPECTED_MESSAGE),
- handle_own_alert(Alert, Version, {StateName, Msg}, State).
+ Alert = ?ALERT_REC(?FATAL,?UNEXPECTED_MESSAGE, {unexpected_msg, Msg}),
+ handle_own_alert(Alert, Version, StateName, State).
handle_call({application_data, _Data}, _, _, _, _) ->
%% In renegotiation priorities handshake, send data when handshake is finished
diff --git a/lib/ssl/src/ssl_crl_cache.erl b/lib/ssl/src/ssl_crl_cache.erl
index 8817b0c884..66f46da75f 100644
--- a/lib/ssl/src/ssl_crl_cache.erl
+++ b/lib/ssl/src/ssl_crl_cache.erl
@@ -92,8 +92,8 @@ delete({der, CRLs}) ->
ssl_manager:delete_crls({?NO_DIST_POINT, CRLs});
delete(URI) ->
- case http_uri:parse(URI) of
- {ok, {http, _, _ , _, Path,_}} ->
+ case uri_string:normalize(URI, [return_map]) of
+ #{scheme := "http", path := Path} ->
ssl_manager:delete_crls(string:trim(Path, leading, "/"));
_ ->
{error, {only_http_distribution_points_supported, URI}}
@@ -103,8 +103,8 @@ delete(URI) ->
%%% Internal functions
%%--------------------------------------------------------------------
do_insert(URI, CRLs) ->
- case http_uri:parse(URI) of
- {ok, {http, _, _ , _, Path,_}} ->
+ case uri_string:normalize(URI, [return_map]) of
+ #{scheme := "http", path := Path} ->
ssl_manager:insert_crls(string:trim(Path, leading, "/"), CRLs);
_ ->
{error, {only_http_distribution_points_supported, URI}}
@@ -161,7 +161,7 @@ http_get(URL, Rest, CRLDbInfo, Timeout) ->
cache_lookup(_, undefined) ->
[];
cache_lookup(URL, {{Cache, _}, _}) ->
- {ok, {_, _, _ , _, Path,_}} = http_uri:parse(URL),
+ #{path := Path} = uri_string:normalize(URL, [return_map]),
case ssl_pkix_db:lookup(string:trim(Path, leading, "/"), Cache) of
undefined ->
[];
diff --git a/lib/ssl/src/ssl_handshake.erl b/lib/ssl/src/ssl_handshake.erl
index 0c55af9174..e1f813ea95 100644
--- a/lib/ssl/src/ssl_handshake.erl
+++ b/lib/ssl/src/ssl_handshake.erl
@@ -52,7 +52,7 @@
%% Handle handshake messages
-export([certify/7, certificate_verify/6, verify_signature/5,
master_secret/4, server_key_exchange_hash/2, verify_connection/6,
- init_handshake_history/0, update_handshake_history/3, verify_server_key/5,
+ init_handshake_history/0, update_handshake_history/2, verify_server_key/5,
select_version/3
]).
@@ -189,12 +189,18 @@ certificate_request(CipherSuite, CertDbHandle, CertDbRef, HashSigns, Version) ->
{dh, binary()} |
{dh, {binary(), binary()}, #'DHParameter'{}, {HashAlgo::atom(), SignAlgo::atom()},
binary(), binary(), public_key:private_key()} |
+ {ecdh, _, _, _, _, _} |
{ecdh, #'ECPrivateKey'{}} |
+ {psk, _, _, _, _, _} |
{psk, binary()} |
+ {dhe_psk, _, _, _, _, _, _, _} |
{dhe_psk, binary(), binary()} |
+ {ecdhe_psk, _, _, _, _, _, _} |
{ecdhe_psk, binary(), #'ECPrivateKey'{}} |
{srp, {binary(), binary()}, #srp_user{}, {HashAlgo::atom(), SignAlgo::atom()},
- binary(), binary(), public_key:private_key()}) ->
+ binary(), binary(), public_key:private_key()} |
+ {srp, _} |
+ {psk_premaster_secret, _, _, _}) ->
#client_key_exchange{} | #server_key_exchange{}.
%%
@@ -473,24 +479,12 @@ init_handshake_history() ->
{[], []}.
%%--------------------------------------------------------------------
--spec update_handshake_history(ssl_handshake:ssl_handshake_history(), Data ::term(), boolean()) ->
+-spec update_handshake_history(ssl_handshake:ssl_handshake_history(), Data ::term()) ->
ssl_handshake:ssl_handshake_history().
%%
%% Description: Update the handshake history buffer with Data.
%%--------------------------------------------------------------------
-update_handshake_history(Handshake, % special-case SSL2 client hello
- <<?CLIENT_HELLO, ?UINT24(_), ?BYTE(Major), ?BYTE(Minor),
- ?UINT16(CSLength), ?UINT16(0),
- ?UINT16(CDLength),
- CipherSuites:CSLength/binary,
- ChallengeData:CDLength/binary>>, true) ->
- update_handshake_history(Handshake,
- <<?CLIENT_HELLO, ?BYTE(Major), ?BYTE(Minor),
- ?UINT16(CSLength), ?UINT16(0),
- ?UINT16(CDLength),
- CipherSuites:CSLength/binary,
- ChallengeData:CDLength/binary>>, true);
-update_handshake_history({Handshake0, _Prev}, Data, _) ->
+update_handshake_history({Handshake0, _Prev}, Data) ->
{[Data|Handshake0], Handshake0}.
verify_server_key(#server_key_params{params_bin = EncParams,
@@ -774,9 +768,8 @@ decode_suites('3_bytes', Dec) ->
%%====================================================================
available_suites(UserSuites, Version) ->
- lists:filtermap(fun(Suite) ->
- lists:member(Suite, ssl_cipher:all_suites(Version))
- end, UserSuites).
+ VersionSuites = ssl_cipher:all_suites(Version) ++ ssl_cipher:anonymous_suites(Version),
+ lists:filtermap(fun(Suite) -> lists:member(Suite, VersionSuites) end, UserSuites).
available_suites(ServerCert, UserSuites, Version, undefined, Curve) ->
ssl_cipher:filter(ServerCert, available_suites(UserSuites, Version))
@@ -1056,7 +1049,9 @@ select_curve(undefined, _, _) ->
%%
%% Description: Handles signature_algorithms hello extension (server)
%%--------------------------------------------------------------------
-select_hashsign(_, undefined, _, _, _Version) ->
+select_hashsign(_, _, KeyExAlgo, _, _Version) when KeyExAlgo == dh_anon;
+ KeyExAlgo == ecdh_anon;
+ KeyExAlgo == srp_anon ->
{null, anon};
%% The signature_algorithms extension was introduced with TLS 1.2. Ignore it if we have
%% negotiated a lower version.
diff --git a/lib/ssl/src/ssl_internal.hrl b/lib/ssl/src/ssl_internal.hrl
index 9bb1cbaeb0..d354910f33 100644
--- a/lib/ssl/src/ssl_internal.hrl
+++ b/lib/ssl/src/ssl_internal.hrl
@@ -73,6 +73,7 @@
%% sslv3 is considered insecure due to lack of padding check (Poodle attack)
%% Keep as interop with legacy software but do not support as default
-define(ALL_AVAILABLE_VERSIONS, ['tlsv1.2', 'tlsv1.1', tlsv1, sslv3]).
+-define(ALL_AVAILABLE_DATAGRAM_VERSIONS, ['dtlsv1.2', dtlsv1]).
-define(ALL_SUPPORTED_VERSIONS, ['tlsv1.2', 'tlsv1.1', tlsv1]).
-define(MIN_SUPPORTED_VERSIONS, ['tlsv1.1', tlsv1]).
-define(ALL_DATAGRAM_SUPPORTED_VERSIONS, ['dtlsv1.2', dtlsv1]).
@@ -143,7 +144,6 @@
signature_algs,
eccs,
honor_ecc_order :: boolean(),
- v2_hello_compatible :: boolean(),
max_handshake_size :: integer()
}).
diff --git a/lib/ssl/src/ssl_record.erl b/lib/ssl/src/ssl_record.erl
index dd6a3e8521..c0eee466ae 100644
--- a/lib/ssl/src/ssl_record.erl
+++ b/lib/ssl/src/ssl_record.erl
@@ -326,7 +326,7 @@ cipher_aead(Version, Fragment,
%%--------------------------------------------------------------------
-spec decipher(ssl_version(), binary(), connection_state(), boolean()) ->
- {binary(), binary(), connection_state} | #alert{}.
+ {binary(), binary(), connection_state()} | #alert{}.
%%
%% Description: Payload decryption
%%--------------------------------------------------------------------
diff --git a/lib/ssl/src/ssl_v2.erl b/lib/ssl/src/ssl_v2.erl
deleted file mode 100644
index 37134cbe5d..0000000000
--- a/lib/ssl/src/ssl_v2.erl
+++ /dev/null
@@ -1,38 +0,0 @@
-%%
-%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2007-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%
-%%
-
-%%
-%%----------------------------------------------------------------------
-%% Purpose: Handles sslv2 hello as clients supporting sslv2 and higher
-%% will send an sslv2 hello.
-%%----------------------------------------------------------------------
-
--module(ssl_v2).
-
--export([client_random/2]).
-
-client_random(ChallengeData, 32) ->
- ChallengeData;
-client_random(ChallengeData, N) when N > 32 ->
- <<NewChallengeData:32/binary, _/binary>> = ChallengeData,
- NewChallengeData;
-client_random(ChallengeData, N) ->
- Pad = list_to_binary(lists:duplicate(N, 0)),
- <<Pad/binary, ChallengeData/binary>>.
diff --git a/lib/ssl/src/tls_connection.erl b/lib/ssl/src/tls_connection.erl
index 2872ca9fe5..c35378f18f 100644
--- a/lib/ssl/src/tls_connection.erl
+++ b/lib/ssl/src/tls_connection.erl
@@ -266,10 +266,9 @@ send_handshake(Handshake, State) ->
queue_handshake(Handshake, #state{negotiated_version = Version,
tls_handshake_history = Hist0,
flight_buffer = Flight0,
- ssl_options = #ssl_options{v2_hello_compatible = V2HComp},
connection_states = ConnectionStates0} = State0) ->
{BinHandshake, ConnectionStates, Hist} =
- encode_handshake(Handshake, Version, ConnectionStates0, Hist0, V2HComp),
+ encode_handshake(Handshake, Version, ConnectionStates0, Hist0),
State0#state{connection_states = ConnectionStates,
tls_handshake_history = Hist,
flight_buffer = Flight0 ++ [BinHandshake]}.
@@ -400,7 +399,7 @@ getopts(Transport, Socket, Tag) ->
init({call, From}, {start, Timeout},
#state{host = Host, port = Port, role = client,
- ssl_options = #ssl_options{v2_hello_compatible = V2HComp} = SslOpts,
+ ssl_options = SslOpts,
session = #session{own_certificate = Cert} = Session0,
transport_cb = Transport, socket = Socket,
connection_states = ConnectionStates0,
@@ -416,7 +415,7 @@ init({call, From}, {start, Timeout},
HelloVersion = tls_record:hello_version(Version, SslOpts#ssl_options.versions),
Handshake0 = ssl_handshake:init_handshake_history(),
{BinMsg, ConnectionStates, Handshake} =
- encode_handshake(Hello, HelloVersion, ConnectionStates0, Handshake0, V2HComp),
+ encode_handshake(Hello, HelloVersion, ConnectionStates0, Handshake0),
send(Transport, Socket, BinMsg),
State1 = State0#state{connection_states = ConnectionStates,
negotiated_version = Version, %% Requested version
@@ -656,15 +655,11 @@ next_tls_record(Data, StateName, #state{protocol_buffers =
handle_record_alert(Alert, State0)
end.
-acceptable_record_versions(hello, #state{ssl_options = #ssl_options{v2_hello_compatible = true}}) ->
- [tls_record:protocol_version(Vsn) || Vsn <- ?ALL_AVAILABLE_VERSIONS ++ ['sslv2']];
+
acceptable_record_versions(hello, _) ->
[tls_record:protocol_version(Vsn) || Vsn <- ?ALL_AVAILABLE_VERSIONS];
acceptable_record_versions(_, #state{negotiated_version = Version}) ->
[Version].
-handle_record_alert(#alert{description = ?BAD_RECORD_MAC},
- #state{ssl_options = #ssl_options{v2_hello_compatible = true}}) ->
- ?ALERT_REC(?FATAL, ?PROTOCOL_VERSION);
handle_record_alert(Alert, _) ->
Alert.
@@ -727,9 +722,9 @@ handle_alerts([Alert | Alerts], {next_state, StateName, State}) ->
handle_alerts([Alert | Alerts], {next_state, StateName, State, _Actions}) ->
handle_alerts(Alerts, ssl_connection:handle_alert(Alert, StateName, State)).
-encode_handshake(Handshake, Version, ConnectionStates0, Hist0, V2HComp) ->
+encode_handshake(Handshake, Version, ConnectionStates0, Hist0) ->
Frag = tls_handshake:encode_handshake(Handshake, Version),
- Hist = ssl_handshake:update_handshake_history(Hist0, Frag, V2HComp),
+ Hist = ssl_handshake:update_handshake_history(Hist0, Frag),
{Encoded, ConnectionStates} =
tls_record:encode_handshake(Frag, Version, ConnectionStates0),
{Encoded, ConnectionStates, Hist}.
diff --git a/lib/ssl/src/tls_handshake.erl b/lib/ssl/src/tls_handshake.erl
index 8817418fb0..0058b9c8ae 100644
--- a/lib/ssl/src/tls_handshake.erl
+++ b/lib/ssl/src/tls_handshake.erl
@@ -39,7 +39,7 @@
-export([encode_handshake/2]).
%% Handshake decodeing
--export([get_tls_handshake/4, decode_handshake/4]).
+-export([get_tls_handshake/4, decode_handshake/3]).
-type tls_handshake() :: #client_hello{} | ssl_handshake:ssl_handshake().
@@ -268,9 +268,9 @@ enc_handshake(HandshakeMsg, Version) ->
%%--------------------------------------------------------------------
get_tls_handshake_aux(Version, <<?BYTE(Type), ?UINT24(Length),
Body:Length/binary,Rest/binary>>,
- #ssl_options{v2_hello_compatible = V2Hello} = Opts, Acc) ->
+ Opts, Acc) ->
Raw = <<?BYTE(Type), ?UINT24(Length), Body/binary>>,
- try decode_handshake(Version, Type, Body, V2Hello) of
+ try decode_handshake(Version, Type, Body) of
Handshake ->
get_tls_handshake_aux(Version, Rest, Opts, [{Handshake,Raw} | Acc])
catch
@@ -280,29 +280,15 @@ get_tls_handshake_aux(Version, <<?BYTE(Type), ?UINT24(Length),
get_tls_handshake_aux(_Version, Data, _, Acc) ->
{lists:reverse(Acc), Data}.
-decode_handshake(_, ?HELLO_REQUEST, <<>>, _) ->
+decode_handshake(_, ?HELLO_REQUEST, <<>>) ->
#hello_request{};
-
-decode_handshake(_Version, ?CLIENT_HELLO, Bin, true) ->
- try decode_hello(Bin) of
- Hello ->
- Hello
- catch
- _:_ ->
- decode_v2_hello(Bin)
- end;
-decode_handshake(_Version, ?CLIENT_HELLO, Bin, false) ->
- decode_hello(Bin);
-
decode_handshake(_Version, ?CLIENT_HELLO,
<<?BYTE(Major), ?BYTE(Minor), Random:32/binary,
?BYTE(SID_length), Session_ID:SID_length/binary,
?UINT16(Cs_length), CipherSuites:Cs_length/binary,
?BYTE(Cm_length), Comp_methods:Cm_length/binary,
- Extensions/binary>>, _) ->
-
+ Extensions/binary>>) ->
DecodedExtensions = ssl_handshake:decode_hello_extensions({client, Extensions}),
-
#client_hello{
client_version = {Major,Minor},
random = Random,
@@ -311,36 +297,7 @@ decode_handshake(_Version, ?CLIENT_HELLO,
compression_methods = Comp_methods,
extensions = DecodedExtensions
};
-decode_handshake(Version, Tag, Msg, _) ->
+decode_handshake(Version, Tag, Msg) ->
ssl_handshake:decode_handshake(Version, Tag, Msg).
-decode_hello(<<?BYTE(Major), ?BYTE(Minor), Random:32/binary,
- ?BYTE(SID_length), Session_ID:SID_length/binary,
- ?UINT16(Cs_length), CipherSuites:Cs_length/binary,
- ?BYTE(Cm_length), Comp_methods:Cm_length/binary,
- Extensions/binary>>) ->
- DecodedExtensions = ssl_handshake:decode_hello_extensions({client, Extensions}),
-
- #client_hello{
- client_version = {Major,Minor},
- random = Random,
- session_id = Session_ID,
- cipher_suites = ssl_handshake:decode_suites('2_bytes', CipherSuites),
- compression_methods = Comp_methods,
- extensions = DecodedExtensions
- }.
-%% The server must be able to receive such messages, from clients that
-%% are willing to use ssl v3 or higher, but have ssl v2 compatibility.
-decode_v2_hello(<<?BYTE(Major), ?BYTE(Minor),
- ?UINT16(CSLength), ?UINT16(0),
- ?UINT16(CDLength),
- CipherSuites:CSLength/binary,
- ChallengeData:CDLength/binary>>) ->
- #client_hello{client_version = {Major, Minor},
- random = ssl_v2:client_random(ChallengeData, CDLength),
- session_id = 0,
- cipher_suites = ssl_handshake:decode_suites('3_bytes', CipherSuites),
- compression_methods = [?NULL],
- extensions = #hello_extensions{}
- }.
diff --git a/lib/ssl/src/tls_record.erl b/lib/ssl/src/tls_record.erl
index 188ec6809d..aa70508f1e 100644
--- a/lib/ssl/src/tls_record.erl
+++ b/lib/ssl/src/tls_record.erl
@@ -394,16 +394,6 @@ initial_connection_state(ConnectionEnd, BeastMitigation) ->
server_verify_data => undefined
}.
-assert_version(<<1:1, Length0:15, Data0:Length0/binary, _/binary>>, Versions) ->
- case Data0 of
- <<?BYTE(?CLIENT_HELLO), ?BYTE(Major), ?BYTE(Minor), _/binary>> ->
- %% First check v2_hello_compatible mode is active
- lists:member({2,0}, Versions) andalso
- %% andalso we want to negotiate higher version
- lists:member({Major, Minor}, Versions -- [{2,0}]);
- _ ->
- false
- end;
assert_version(<<?BYTE(_), ?BYTE(MajVer), ?BYTE(MinVer), _/binary>>, Versions) ->
is_acceptable_version({MajVer, MinVer}, Versions).
@@ -431,32 +421,10 @@ get_tls_records_aux(<<?BYTE(?CHANGE_CIPHER_SPEC),?BYTE(MajVer),?BYTE(MinVer),
get_tls_records_aux(Rest, [#ssl_tls{type = ?CHANGE_CIPHER_SPEC,
version = {MajVer, MinVer},
fragment = Data} | Acc]);
-%% Matches an ssl v2 client hello message.
-%% The server must be able to receive such messages, from clients that
-%% are willing to use ssl v3 or higher, but have ssl v2 compatibility.
-get_tls_records_aux(<<1:1, Length0:15, Data0:Length0/binary, Rest/binary>>,
- Acc) ->
- case Data0 of
- <<?BYTE(?CLIENT_HELLO), ?BYTE(MajVer), ?BYTE(MinVer), _/binary>> ->
- Length = Length0-1,
- <<?BYTE(_), Data1:Length/binary>> = Data0,
- Data = <<?BYTE(?CLIENT_HELLO), ?UINT24(Length), Data1/binary>>,
- get_tls_records_aux(Rest, [#ssl_tls{type = ?HANDSHAKE,
- version = {MajVer, MinVer},
- fragment = Data} | Acc]);
- _ ->
- ?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE)
-
- end;
-
get_tls_records_aux(<<0:1, _CT:7, ?BYTE(_MajVer), ?BYTE(_MinVer),
?UINT16(Length), _/binary>>,
_Acc) when Length > ?MAX_CIPHER_TEXT_LENGTH ->
?ALERT_REC(?FATAL, ?RECORD_OVERFLOW);
-get_tls_records_aux(<<1:1, Length0:15, _/binary>>,_Acc)
- when Length0 > ?MAX_CIPHER_TEXT_LENGTH ->
- ?ALERT_REC(?FATAL, ?RECORD_OVERFLOW);
-
get_tls_records_aux(Data, Acc) ->
case size(Data) =< ?MAX_CIPHER_TEXT_LENGTH + ?INITIAL_BYTES of
true ->
diff --git a/lib/ssl/test/ssl_basic_SUITE.erl b/lib/ssl/test/ssl_basic_SUITE.erl
index 6d954a1d3f..a9901007db 100644
--- a/lib/ssl/test/ssl_basic_SUITE.erl
+++ b/lib/ssl/test/ssl_basic_SUITE.erl
@@ -163,7 +163,8 @@ api_tests() ->
server_name_indication_option,
accept_pool,
prf,
- socket_options
+ socket_options,
+ cipher_suites
].
api_tests_tls() ->
@@ -193,6 +194,7 @@ renegotiate_tests() ->
[client_renegotiate,
server_renegotiate,
client_secure_renegotiate,
+ client_secure_renegotiate_fallback,
client_renegotiate_reused_session,
server_renegotiate_reused_session,
client_no_wrap_sequence_number,
@@ -207,7 +209,7 @@ tls_cipher_tests() ->
rc4_ecdsa_cipher_suites].
cipher_tests() ->
- [cipher_suites,
+ [old_cipher_suites,
cipher_suites_mix,
ciphers_rsa_signed_certs,
ciphers_rsa_signed_certs_openssl_names,
@@ -704,8 +706,6 @@ secret_connection_info(Config) when is_list(Config) ->
ct:log("Testcase ~p, Client ~p Server ~p ~n",
[self(), Client, Server]),
-
- Version = ssl_test_lib:protocol_version(Config),
ssl_test_lib:check_result(Server, true, Client, true),
@@ -1130,11 +1130,16 @@ fallback(Config) when is_list(Config) ->
%%--------------------------------------------------------------------
cipher_format() ->
- [{doc, "Test that cipher conversion from tuples to binarys works"}].
+ [{doc, "Test that cipher conversion from maps | tuples | stings to binarys works"}].
cipher_format(Config) when is_list(Config) ->
- {ok, Socket} = ssl:listen(0, [{ciphers, ssl:cipher_suites()}]),
- ssl:close(Socket).
-
+ {ok, Socket0} = ssl:listen(0, [{ciphers, ssl:cipher_suites(default, 'tlsv1.2')}]),
+ ssl:close(Socket0),
+ %% Legacy
+ {ok, Socket1} = ssl:listen(0, [{ciphers, ssl:cipher_suites()}]),
+ ssl:close(Socket1),
+ {ok, Socket2} = ssl:listen(0, [{ciphers, ssl:cipher_suites(openssl)}]),
+ ssl:close(Socket2).
+
%%--------------------------------------------------------------------
peername() ->
@@ -1285,20 +1290,76 @@ sockname_result(S) ->
ssl:sockname(S).
%%--------------------------------------------------------------------
+
cipher_suites() ->
- [{doc,"Test API function cipher_suites/0"}].
+ [{doc,"Test API function cipher_suites/2, filter_cipher_suites/2"
+ " and prepend|append_cipher_suites/2"}].
cipher_suites(Config) when is_list(Config) ->
- MandatoryCipherSuiteTLS1_0TLS1_1 = {rsa,'3des_ede_cbc',sha},
- MandatoryCipherSuiteTLS1_0TLS1_2 = {rsa,'aes_128_cbc',sha} ,
- [_|_] = Suites = ssl:cipher_suites(),
- AllSuites = ssl:cipher_suites(all),
- %% The mandantory suites will no longer be supported by default
- %% due to security reasons
- true = lists:member(MandatoryCipherSuiteTLS1_0TLS1_1, AllSuites),
- true = lists:member(MandatoryCipherSuiteTLS1_0TLS1_2, AllSuites),
+ MandatoryCipherSuiteTLS1_0TLS1_1 = #{key_exchange => rsa,
+ cipher => '3des_ede_cbc',
+ mac => sha,
+ prf => default_prf},
+ MandatoryCipherSuiteTLS1_0TLS1_2 = #{key_exchange =>rsa,
+ cipher => 'aes_128_cbc',
+ mac => sha,
+ prf => default_prf},
+ Version = ssl_test_lib:protocol_version(Config),
+ All = [_|_] = ssl:cipher_suites(all, Version),
+ Default = [_|_] = ssl:cipher_suites(default, Version),
+ Anonymous = [_|_] = ssl:cipher_suites(anonymous, Version),
+ true = length(Default) < length(All),
+ Filters = [{key_exchange,
+ fun(dhe_rsa) ->
+ true;
+ (_) ->
+ false
+ end
+ },
+ {cipher,
+ fun(aes_256_cbc) ->
+ true;
+ (_) ->
+ false
+ end
+ },
+ {mac,
+ fun(sha) ->
+ true;
+ (_) ->
+ false
+ end
+ }
+ ],
+ Cipher = #{cipher => aes_256_cbc,
+ key_exchange => dhe_rsa,
+ mac => sha,
+ prf => default_prf},
+ [Cipher] = ssl:filter_cipher_suites(All, Filters),
+ [Cipher | Rest0] = ssl:prepend_cipher_suites([Cipher], Default),
+ [Cipher | Rest0] = ssl:prepend_cipher_suites(Filters, Default),
+ true = lists:member(Cipher, Default),
+ false = lists:member(Cipher, Rest0),
+ [Cipher | Rest1] = lists:reverse(ssl:append_cipher_suites([Cipher], Default)),
+ [Cipher | Rest1] = lists:reverse(ssl:append_cipher_suites(Filters, Default)),
+ true = lists:member(Cipher, Default),
+ false = lists:member(Cipher, Rest1),
+ [] = lists:dropwhile(fun(X) -> not lists:member(X, Default) end, Anonymous),
+ [] = lists:dropwhile(fun(X) -> not lists:member(X, All) end, Anonymous),
+ true = lists:member(MandatoryCipherSuiteTLS1_0TLS1_1, All),
+ true = lists:member(MandatoryCipherSuiteTLS1_0TLS1_2, All).
+
+%%--------------------------------------------------------------------
+
+old_cipher_suites() ->
+ [{doc,"Test API function cipher_suites/0"}].
+
+old_cipher_suites(Config) when is_list(Config) ->
+ MandatoryCipherSuite = {rsa, '3des_ede_cbc', sha},
+ [_|_] = Suites = ssl:cipher_suites(),
Suites = ssl:cipher_suites(erlang),
- [_|_] =ssl:cipher_suites(openssl).
+ [_|_] = ssl:cipher_suites(openssl),
+ true = lists:member(MandatoryCipherSuite, ssl:cipher_suites(all)).
%%--------------------------------------------------------------------
cipher_suites_mix() ->
@@ -2838,6 +2899,36 @@ client_secure_renegotiate(Config) when is_list(Config) ->
ssl_test_lib:close(Server),
ssl_test_lib:close(Client).
+%%--------------------------------------------------------------------
+client_secure_renegotiate_fallback() ->
+ [{doc,"Test that we can set secure_renegotiate to false that is "
+ "fallback option, we however do not have a insecure server to test against!"}].
+client_secure_renegotiate_fallback(Config) when is_list(Config) ->
+ ServerOpts = ssl_test_lib:ssl_options(server_opts, Config),
+ ClientOpts = ssl_test_lib:ssl_options(client_opts, Config),
+
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+
+ Data = "From erlang to erlang",
+
+ Server =
+ ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {mfa, {?MODULE, erlang_ssl_receive, [Data]}},
+ {options, [{secure_renegotiate, false} | ServerOpts]}]),
+ Port = ssl_test_lib:inet_port(Server),
+
+ Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa, {?MODULE,
+ renegotiate, [Data]}},
+ {options, [{reuse_sessions, false},
+ {secure_renegotiate, false}| ClientOpts]}]),
+
+ ssl_test_lib:check_result(Client, ok, Server, ok),
+ ssl_test_lib:close(Server),
+ ssl_test_lib:close(Client).
%%--------------------------------------------------------------------
server_renegotiate() ->
@@ -3171,18 +3262,25 @@ no_reuses_session_server_restart_new_cert_file(Config) when is_list(Config) ->
%%--------------------------------------------------------------------
defaults(Config) when is_list(Config)->
- [_,
- {supported, Supported},
- {available, Available}]
- = ssl:versions(),
- true = lists:member(sslv3, Available),
- false = lists:member(sslv3, Supported),
+ Versions = ssl:versions(),
+ true = lists:member(sslv3, proplists:get_value(available, Versions)),
+ false = lists:member(sslv3, proplists:get_value(supported, Versions)),
+ true = lists:member('tlsv1', proplists:get_value(available, Versions)),
+ true = lists:member('tlsv1', proplists:get_value(supported, Versions)),
+ true = lists:member('tlsv1.1', proplists:get_value(available, Versions)),
+ true = lists:member('tlsv1.1', proplists:get_value(supported, Versions)),
+ true = lists:member('tlsv1.2', proplists:get_value(available, Versions)),
+ true = lists:member('tlsv1.2', proplists:get_value(supported, Versions)),
false = lists:member({rsa,rc4_128,sha}, ssl:cipher_suites()),
true = lists:member({rsa,rc4_128,sha}, ssl:cipher_suites(all)),
false = lists:member({rsa,des_cbc,sha}, ssl:cipher_suites()),
true = lists:member({rsa,des_cbc,sha}, ssl:cipher_suites(all)),
false = lists:member({dhe_rsa,des_cbc,sha}, ssl:cipher_suites()),
- true = lists:member({dhe_rsa,des_cbc,sha}, ssl:cipher_suites(all)).
+ true = lists:member({dhe_rsa,des_cbc,sha}, ssl:cipher_suites(all)),
+ true = lists:member('dtlsv1.2', proplists:get_value(available_dtls, Versions)),
+ true = lists:member('dtlsv1', proplists:get_value(available_dtls, Versions)),
+ true = lists:member('dtlsv1.2', proplists:get_value(supported_dtls, Versions)),
+ true = lists:member('dtlsv1', proplists:get_value(supported_dtls, Versions)).
%%--------------------------------------------------------------------
reuseaddr() ->
@@ -3333,7 +3431,7 @@ tls_ciphersuite_vs_version(Config) when is_list(Config) ->
>>),
{ok, <<22, RecMajor:8, RecMinor:8, _RecLen:16, 2, HelloLen:24>>} = gen_tcp:recv(Socket, 9, 10000),
{ok, <<HelloBin:HelloLen/binary>>} = gen_tcp:recv(Socket, HelloLen, 5000),
- ServerHello = tls_handshake:decode_handshake({RecMajor, RecMinor}, 2, HelloBin, false),
+ ServerHello = tls_handshake:decode_handshake({RecMajor, RecMinor}, 2, HelloBin),
case ServerHello of
#server_hello{server_version = {3,0}, cipher_suite = <<0,57>>} ->
ok;
@@ -3793,9 +3891,23 @@ rizzo() ->
vunrable to Rizzo/Dungon attack"}].
rizzo(Config) when is_list(Config) ->
- Ciphers = [X || X ={_,Y,_} <- ssl:cipher_suites(), Y =/= rc4_128],
Prop = proplists:get_value(tc_group_properties, Config),
Version = proplists:get_value(name, Prop),
+ NVersion = ssl_test_lib:protocol_version(Config, tuple),
+ Ciphers = ssl:filter_cipher_suites(ssl:cipher_suites(all, NVersion),
+ [{key_exchange,
+ fun(Alg) when Alg == ecdh_rsa; Alg == ecdhe_rsa->
+ true;
+ (_) ->
+ false
+ end},
+ {cipher,
+ fun(rc4_128) ->
+ false;
+ (_) ->
+ true
+ end}]),
+
run_send_recv_rizzo(Ciphers, Config, Version,
{?MODULE, send_recv_result_active_rizzo, []}).
%%--------------------------------------------------------------------
@@ -3807,8 +3919,13 @@ no_rizzo_rc4(Config) when is_list(Config) ->
Version = proplists:get_value(name, Prop),
NVersion = ssl_test_lib:protocol_version(Config, tuple),
%% Test uses RSA certs
- Ciphers = ssl_test_lib:rc4_suites(NVersion) -- [{ecdhe_ecdsa,rc4_128,sha},
- {ecdh_ecdsa,rc4_128,sha}],
+ Ciphers = ssl:filter_cipher_suites(ssl_test_lib:rc4_suites(NVersion),
+ [{key_exchange,
+ fun(Alg) when Alg == ecdh_rsa; Alg == ecdhe_rsa->
+ true;
+ (_) ->
+ false
+ end}]),
run_send_recv_rizzo(Ciphers, Config, Version,
{?MODULE, send_recv_result_active_no_rizzo, []}).
@@ -3819,10 +3936,21 @@ rizzo_one_n_minus_one(Config) when is_list(Config) ->
Prop = proplists:get_value(tc_group_properties, Config),
Version = proplists:get_value(name, Prop),
NVersion = ssl_test_lib:protocol_version(Config, tuple),
- AllSuites = ssl_test_lib:available_suites(NVersion),
- Ciphers = [X || X ={_,Y,_} <- AllSuites, Y =/= rc4_128],
+ Ciphers = ssl:filter_cipher_suites(ssl:cipher_suites(all, NVersion),
+ [{key_exchange,
+ fun(Alg) when Alg == ecdh_rsa; Alg == ecdhe_rsa->
+ true;
+ (_) ->
+ false
+ end},
+ {cipher,
+ fun(rc4_128) ->
+ false;
+ (_) ->
+ true
+ end}]),
run_send_recv_rizzo(Ciphers, Config, Version,
- {?MODULE, send_recv_result_active_rizzo, []}).
+ {?MODULE, send_recv_result_active_rizzo, []}).
rizzo_zero_n() ->
[{doc,"Test that the 0/n-split mitigation of Rizzo/Dungon attack can be explicitly selected"}].
@@ -3831,8 +3959,13 @@ rizzo_zero_n(Config) when is_list(Config) ->
Prop = proplists:get_value(tc_group_properties, Config),
Version = proplists:get_value(name, Prop),
NVersion = ssl_test_lib:protocol_version(Config, tuple),
- AllSuites = ssl_test_lib:available_suites(NVersion),
- Ciphers = [X || X ={_,Y,_} <- AllSuites, Y =/= rc4_128],
+ Ciphers = ssl:filter_cipher_suites(ssl:cipher_suites(default, NVersion),
+ [{cipher,
+ fun(rc4_128) ->
+ false;
+ (_) ->
+ true
+ end}]),
run_send_recv_rizzo(Ciphers, Config, Version,
{?MODULE, send_recv_result_active_no_rizzo, []}).
@@ -3840,9 +3973,16 @@ rizzo_disabled() ->
[{doc,"Test that the mitigation of Rizzo/Dungon attack can be explicitly disabled"}].
rizzo_disabled(Config) when is_list(Config) ->
- Ciphers = [X || X ={_,Y,_} <- ssl:cipher_suites(), Y =/= rc4_128],
Prop = proplists:get_value(tc_group_properties, Config),
Version = proplists:get_value(name, Prop),
+ NVersion = ssl_test_lib:protocol_version(Config, tuple),
+ Ciphers = ssl:filter_cipher_suites(ssl:cipher_suites(default, NVersion),
+ [{cipher,
+ fun(rc4_128) ->
+ false;
+ (_) ->
+ true
+ end}]),
run_send_recv_rizzo(Ciphers, Config, Version,
{?MODULE, send_recv_result_active_no_rizzo, []}).
@@ -4617,19 +4757,21 @@ rizzo_test(Cipher, Config, Version, Mfa) ->
[{Cipher, Error}]
end.
-client_server_opts({KeyAlgo,_,_}, Config)
+client_server_opts(#{key_exchange := KeyAlgo}, Config)
when KeyAlgo == rsa orelse
KeyAlgo == dhe_rsa orelse
- KeyAlgo == ecdhe_rsa ->
+ KeyAlgo == ecdhe_rsa orelse
+ KeyAlgo == rsa_psk orelse
+ KeyAlgo == srp_rsa ->
{ssl_test_lib:ssl_options(client_opts, Config),
ssl_test_lib:ssl_options(server_opts, Config)};
-client_server_opts({KeyAlgo,_,_}, Config) when KeyAlgo == dss orelse KeyAlgo == dhe_dss ->
+client_server_opts(#{key_exchange := KeyAlgo}, Config) when KeyAlgo == dss orelse KeyAlgo == dhe_dss ->
{ssl_test_lib:ssl_options(client_dsa_opts, Config),
ssl_test_lib:ssl_options(server_dsa_opts, Config)};
-client_server_opts({KeyAlgo,_,_}, Config) when KeyAlgo == ecdh_ecdsa orelse KeyAlgo == ecdhe_ecdsa ->
+client_server_opts(#{key_exchange := KeyAlgo}, Config) when KeyAlgo == ecdh_ecdsa orelse KeyAlgo == ecdhe_ecdsa ->
{ssl_test_lib:ssl_options(client_opts, Config),
ssl_test_lib:ssl_options(server_ecdsa_opts, Config)};
-client_server_opts({KeyAlgo,_,_}, Config) when KeyAlgo == ecdh_rsa ->
+client_server_opts(#{key_exchange := KeyAlgo}, Config) when KeyAlgo == ecdh_rsa ->
{ssl_test_lib:ssl_options(client_opts, Config),
ssl_test_lib:ssl_options(server_ecdh_rsa_opts, Config)}.
diff --git a/lib/ssl/test/ssl_engine_SUITE.erl b/lib/ssl/test/ssl_engine_SUITE.erl
index bc221d35fd..71891356e8 100644
--- a/lib/ssl/test/ssl_engine_SUITE.erl
+++ b/lib/ssl/test/ssl_engine_SUITE.erl
@@ -39,23 +39,28 @@ init_per_suite(Config) ->
catch crypto:stop(),
try crypto:start() of
ok ->
- ssl_test_lib:clean_start(),
- case crypto:get_test_engine() of
- {ok, EngineName} ->
- try crypto:engine_load(<<"dynamic">>,
- [{<<"SO_PATH">>, EngineName},
- <<"LOAD">>],
- []) of
- {ok, Engine} ->
- [{engine, Engine} |Config];
- {error, Reason} ->
- ct:pal("Reason ~p", [Reason]),
- {skip, "No dynamic engine support"}
- catch error:notsup ->
- {skip, "No engine support in OpenSSL"}
- end;
- {error, notexist} ->
- {skip, "Test engine not found"}
+ case crypto:info_lib() of
+ [{_,_, <<"OpenSSL 1.0.1s-freebsd 1 Mar 2016">>}] ->
+ {skip, "Problem with engine on OpenSSL 1.0.1s-freebsd"};
+ _ ->
+ ssl_test_lib:clean_start(),
+ case crypto:get_test_engine() of
+ {ok, EngineName} ->
+ try crypto:engine_load(<<"dynamic">>,
+ [{<<"SO_PATH">>, EngineName},
+ <<"LOAD">>],
+ []) of
+ {ok, Engine} ->
+ [{engine, Engine} |Config];
+ {error, Reason} ->
+ ct:pal("Reason ~p", [Reason]),
+ {skip, "No dynamic engine support"}
+ catch error:notsup ->
+ {skip, "No engine support in OpenSSL"}
+ end;
+ {error, notexist} ->
+ {skip, "Test engine not found"}
+ end
end
catch _:_ ->
{skip, "Crypto did not start"}
diff --git a/lib/ssl/test/ssl_handshake_SUITE.erl b/lib/ssl/test/ssl_handshake_SUITE.erl
index 9658cb5f56..cd3d972c07 100644
--- a/lib/ssl/test/ssl_handshake_SUITE.erl
+++ b/lib/ssl/test/ssl_handshake_SUITE.erl
@@ -33,7 +33,6 @@
%% Common Test interface functions -----------------------------------
%%--------------------------------------------------------------------
all() -> [decode_hello_handshake,
- decode_hello_handshake_version_confusion,
decode_single_hello_extension_correctly,
decode_supported_elliptic_curves_hello_extension_correctly,
decode_unknown_hello_extension_correctly,
@@ -101,20 +100,13 @@ decode_hello_handshake(_Config) ->
Version = {3, 0},
{Records, _Buffer} = tls_handshake:get_tls_handshake(Version, HelloPacket, <<>>,
- #ssl_options{v2_hello_compatible = false}),
+ #ssl_options{}),
{Hello, _Data} = hd(Records),
#renegotiation_info{renegotiated_connection = <<0>>}
= (Hello#server_hello.extensions)#hello_extensions.renegotiation_info.
-decode_hello_handshake_version_confusion(_) ->
- HelloPacket = <<3,3,0,0,0,0,0,63,210,235,149,6,244,140,108,13,177,74,16,218,33,108,219,41,73,228,3,82,132,123,73,144,118,100,0,0,32,192,4,0,10,192,45,192,38,0,47,192,18,0,163,0,22,0,165,192,29,192,18,192,30,0,103,0,57,192,48,0,47,1,0>>,
- Version = {3,3},
- ClientHello = 1,
- Hello = tls_handshake:decode_handshake({3,3}, ClientHello, HelloPacket, false),
- Hello = tls_handshake:decode_handshake({3,3}, ClientHello, HelloPacket, true).
-
decode_single_hello_extension_correctly(_Config) ->
Renegotiation = <<?UINT16(?RENEGOTIATION_EXT), ?UINT16(1), 0>>,
Extensions = ssl_handshake:decode_hello_extensions(Renegotiation),
diff --git a/lib/ssl/test/ssl_test_lib.erl b/lib/ssl/test/ssl_test_lib.erl
index 94d10b2f9b..26ef311615 100644
--- a/lib/ssl/test/ssl_test_lib.erl
+++ b/lib/ssl/test/ssl_test_lib.erl
@@ -1024,59 +1024,50 @@ string_regex_filter(Str, Search) when is_list(Str) ->
string_regex_filter(_Str, _Search) ->
false.
-anonymous_suites({3,_ } = Version) ->
- [ssl_cipher:erl_suite_definition(S) || S <- ssl_cipher:filter_suites(ssl_cipher:anonymous_suites(Version))];
-anonymous_suites(DTLSVersion) ->
- Version = dtls_v1:corresponding_tls_version(DTLSVersion),
- [ssl_cipher:erl_suite_definition(S) || S <- ssl_cipher:filter_suites(ssl_cipher:anonymous_suites(Version)),
- not ssl_cipher:is_stream_ciphersuite(tuple_to_map(ssl_cipher:erl_suite_definition(S)))].
-
-psk_suites({3,_ } = Version) ->
- [ssl_cipher:erl_suite_definition(S) || S <- ssl_cipher:filter_suites(ssl_cipher:psk_suites(Version))];
-psk_suites(DTLSVersion) ->
- Version = dtls_v1:corresponding_tls_version(DTLSVersion),
- [ssl_cipher:erl_suite_definition(S) || S <- ssl_cipher:filter_suites(ssl_cipher:psk_suites(Version)),
- not ssl_cipher:is_stream_ciphersuite(tuple_to_map(ssl_cipher:erl_suite_definition(S)))].
-
-psk_anon_suites({3,_ } = Version) ->
- [Suite || Suite <- psk_suites(Version), is_psk_anon_suite(Suite)];
-psk_anon_suites(DTLSVersion) ->
- Version = dtls_v1:corresponding_tls_version(DTLSVersion),
- [Suite || Suite <- psk_suites(Version), is_psk_anon_suite(Suite),
- not ssl_cipher:is_stream_ciphersuite(tuple_to_map(Suite))].
+anonymous_suites(Version) ->
+ ssl:filter_cipher_suites([ssl_cipher:suite_definition(S) || S <- ssl_cipher:anonymous_suites(Version)],[]).
+psk_suites(Version) ->
+ ssl:filter_cipher_suites([ssl_cipher:suite_definition(S) || S <- ssl_cipher:psk_suites(Version)], []).
+
+psk_anon_suites(Version) ->
+ ssl:filter_cipher_suites([ssl_cipher:suite_definition(S) || S <- ssl_cipher:psk_suites_anon(Version)],
+ [{key_exchange,
+ fun(psk) ->
+ true;
+ (psk_dhe) ->
+ true;
+ (_) ->
+ false
+ end}]).
+
srp_suites() ->
- [ssl_cipher:erl_suite_definition(Suite) ||
- Suite <-
- ssl_cipher:filter_suites([tuple_to_map(S) ||
- S <- [{srp_anon,'3des_ede_cbc', sha},
- {srp_rsa, '3des_ede_cbc', sha},
- {srp_anon, aes_128_cbc, sha},
- {srp_rsa, aes_128_cbc, sha},
- {srp_anon, aes_256_cbc, sha},
- {srp_rsa, aes_256_cbc, sha}]])].
+ ssl:filter_cipher_suites([ssl_cipher:suite_definition(S) || S <- ssl_cipher:srp_suites()],
+ [{key_exchange,
+ fun(srp_rsa) ->
+ true;
+ (_) ->
+ false
+ end}]).
srp_anon_suites() ->
- [ssl_cipher:erl_suite_definition(Suite) ||
- Suite <-
- ssl_cipher:filter_suites([tuple_to_map(S) ||
- S <-[{srp_anon, '3des_ede_cbc', sha},
- {srp_anon, aes_128_cbc, sha},
- {srp_anon, aes_256_cbc, sha}]])].
+ ssl:filter_cipher_suites([ssl_cipher:suite_definition(S) || S <- ssl_cipher:srp_suites_anon()],
+ []).
srp_dss_suites() ->
- [ssl_cipher:erl_suite_definition(Suite) ||
- Suite <-
- ssl_cipher:filter_suites([tuple_to_map(S) ||
- S <- [{srp_dss, '3des_ede_cbc', sha},
- {srp_dss, aes_128_cbc, sha},
- {srp_dss, aes_256_cbc, sha}]])].
-
+ ssl:filter_cipher_suites([ssl_cipher:suite_definition(S) || S <- ssl_cipher:srp_suites()],
+ [{key_exchange,
+ fun(srp_dss) ->
+ true;
+ (_) ->
+ false
+ end}]).
chacha_suites(Version) ->
[ssl_cipher:erl_suite_definition(S) || S <- ssl_cipher:filter_suites(ssl_cipher:chacha_suites(Version))].
+
rc4_suites(Version) ->
- [ssl_cipher:erl_suite_definition(S) || S <- ssl_cipher:filter_suites(ssl_cipher:rc4_suites(Version))].
+ ssl:filter_cipher_suites([ssl_cipher:suite_definition(S) || S <-ssl_cipher:rc4_suites(Version)], []).
des_suites(Version) ->
- [ssl_cipher:erl_suite_definition(S) || S <- ssl_cipher:filter_suites(ssl_cipher:des_suites(Version))].
+ ssl:filter_cipher_suites([ssl_cipher:suite_definition(S) || S <-ssl_cipher:des_suites(Version)], []).
tuple_to_map({Kex, Cipher, Mac}) ->
#{key_exchange => Kex,
@@ -1313,6 +1304,32 @@ cipher_restriction(Config0) ->
Config0
end.
+openssl_dsa_support() ->
+ case os:cmd("openssl version") of
+ "LibreSSL 2.6.1" ++ _ ->
+ true;
+ "LibreSSL 2.6.2" ++ _ ->
+ true;
+ "LibreSSL 2.6" ++ _ ->
+ false;
+ "LibreSSL 2.4" ++ _ ->
+ true;
+ "LibreSSL 2.3" ++ _ ->
+ true;
+ "LibreSSL 2.2" ++ _ ->
+ true;
+ "LibreSSL 2.1" ++ _ ->
+ true;
+ "LibreSSL 2.0" ++ _ ->
+ true;
+ "LibreSSL" ++ _ ->
+ false;
+ "OpenSSL 1.0.1" ++ Rest ->
+ hd(Rest) >= s;
+ _ ->
+ true
+ end.
+
check_sane_openssl_version(Version) ->
case supports_ssl_tls_version(Version) of
true ->
@@ -1391,7 +1408,9 @@ filter_suites(Ciphers0, AtomVersion) ->
Supported0 = ssl_cipher:suites(Version)
++ ssl_cipher:anonymous_suites(Version)
++ ssl_cipher:psk_suites(Version)
+ ++ ssl_cipher:psk_suites_anon(Version)
++ ssl_cipher:srp_suites()
+ ++ ssl_cipher:srp_suites_anon()
++ ssl_cipher:rc4_suites(Version),
Supported1 = ssl_cipher:filter_suites(Supported0),
Supported2 = [ssl_cipher:erl_suite_definition(S) || S <- Supported1],
diff --git a/lib/ssl/test/ssl_to_openssl_SUITE.erl b/lib/ssl/test/ssl_to_openssl_SUITE.erl
index 33cdc325f4..e8cbf857ef 100644
--- a/lib/ssl/test/ssl_to_openssl_SUITE.erl
+++ b/lib/ssl/test/ssl_to_openssl_SUITE.erl
@@ -51,17 +51,16 @@ groups() ->
[{basic, [], basic_tests()},
{'tlsv1.2', [], all_versions_tests() ++ alpn_tests() ++ npn_tests() ++ sni_server_tests()},
{'tlsv1.1', [], all_versions_tests() ++ alpn_tests() ++ npn_tests() ++ sni_server_tests()},
- {'tlsv1', [], all_versions_tests()++ alpn_tests() ++ npn_tests() ++ sni_server_tests()},
- {'sslv3', [], all_versions_tests()},
- {'dtlsv1.2', [], dtls_all_versions_tests()},
- {'dtlsv1', [], dtls_all_versions_tests()}
- ].
+ {'tlsv1', [], all_versions_tests()++ alpn_tests() ++ npn_tests() ++ sni_server_tests()},
+ {'sslv3', [], all_versions_tests()},
+ {'dtlsv1.2', [], dtls_all_versions_tests()},
+ {'dtlsv1', [], dtls_all_versions_tests()}
+ ].
basic_tests() ->
[basic_erlang_client_openssl_server,
basic_erlang_server_openssl_client,
- expired_session,
- ssl2_erlang_server_openssl_client_comp
+ expired_session
].
all_versions_tests() ->
@@ -70,6 +69,9 @@ all_versions_tests() ->
erlang_server_openssl_client,
erlang_client_openssl_server_dsa_cert,
erlang_server_openssl_client_dsa_cert,
+ erlang_client_openssl_server_anon,
+ erlang_server_openssl_client_anon,
+ erlang_server_openssl_client_anon_with_cert,
erlang_server_openssl_client_reuse_session,
erlang_client_openssl_server_renegotiate,
erlang_client_openssl_server_nowrap_seqnum,
@@ -89,6 +91,9 @@ dtls_all_versions_tests() ->
erlang_server_openssl_client,
erlang_client_openssl_server_dsa_cert,
erlang_server_openssl_client_dsa_cert,
+ erlang_client_openssl_server_anon,
+ erlang_server_openssl_client_anon,
+ erlang_server_openssl_client_anon_with_cert,
erlang_server_openssl_client_reuse_session,
erlang_client_openssl_server_renegotiate,
erlang_client_openssl_server_nowrap_seqnum,
@@ -135,21 +140,26 @@ sni_server_tests() ->
init_per_suite(Config0) ->
case os:find_executable("openssl") of
- false ->
- {skip, "Openssl not found"};
- _ ->
- ct:pal("Version: ~p", [os:cmd("openssl version")]),
- catch crypto:stop(),
- try crypto:start() of
- ok ->
+ false ->
+ {skip, "Openssl not found"};
+ _ ->
+ ct:pal("Version: ~p", [os:cmd("openssl version")]),
+ catch crypto:stop(),
+ try crypto:start() of
+ ok ->
ssl_test_lib:clean_start(),
-
- Config1 = ssl_test_lib:make_rsa_cert(Config0),
- Config2 = ssl_test_lib:make_dsa_cert(Config1),
- ssl_test_lib:cipher_restriction(Config2)
- catch _:_ ->
- {skip, "Crypto did not start"}
- end
+ Config =
+ case ssl_test_lib:openssl_dsa_support() of
+ true ->
+ Config1 = ssl_test_lib:make_rsa_cert(Config0),
+ ssl_test_lib:make_dsa_cert(Config1);
+ false ->
+ ssl_test_lib:make_rsa_cert(Config0)
+ end,
+ ssl_test_lib:cipher_restriction(Config)
+ catch _:_ ->
+ {skip, "Crypto did not start"}
+ end
end.
end_per_suite(_Config) ->
@@ -157,39 +167,34 @@ end_per_suite(_Config) ->
application:stop(crypto).
init_per_group(basic, Config0) ->
- Config = ssl_test_lib:clean_tls_version(Config0),
- case ssl_test_lib:supports_ssl_tls_version(sslv2) of
- true ->
- [{v2_hello_compatible, true} | Config];
- false ->
- [{v2_hello_compatible, false} | Config]
- end;
+ ssl_test_lib:clean_tls_version(Config0);
+
init_per_group(GroupName, Config) ->
case ssl_test_lib:is_tls_version(GroupName) of
- true ->
+ true ->
case ssl_test_lib:supports_ssl_tls_version(GroupName) of
- true ->
+ true ->
case ssl_test_lib:check_sane_openssl_version(GroupName) of
- true ->
+ true ->
ssl_test_lib:init_tls_version(GroupName, Config);
- false ->
+ false ->
{skip, openssl_does_not_support_version}
end;
false ->
{skip, openssl_does_not_support_version}
end;
- _ ->
- ssl:start(),
- Config
+ _ ->
+ ssl:start(),
+ Config
end.
end_per_group(GroupName, Config) ->
- case ssl_test_lib:is_tls_version(GroupName) of
- true ->
- ssl_test_lib:clean_tls_version(Config);
- false ->
- Config
- end.
+ case ssl_test_lib:is_tls_version(GroupName) of
+ true ->
+ ssl_test_lib:clean_tls_version(Config);
+ false ->
+ Config
+ end.
init_per_testcase(expired_session, Config) ->
ct:timetrap(?EXPIRE * 1000 * 5),
@@ -197,83 +202,94 @@ init_per_testcase(expired_session, Config) ->
application:load(ssl),
application:set_env(ssl, session_lifetime, ?EXPIRE),
ssl:start(),
- Config;
-
-init_per_testcase(TestCase, Config) when TestCase == ciphers_rsa_signed_certs;
- TestCase == ciphers_dsa_signed_certs ->
- ct:timetrap({seconds, 90}),
- special_init(TestCase, Config);
-
+ Config;
+
+init_per_testcase(TestCase, Config) when
+ TestCase == ciphers_dsa_signed_certs;
+ TestCase == erlang_client_openssl_server_dsa_cert;
+ TestCase == erlang_server_openssl_client_dsa_cert;
+ TestCase == erlang_client_openssl_server_dsa_cert;
+ TestCase == erlang_server_openssl_client_dsa_cert ->
+ case ssl_test_lib:openssl_dsa_support() of
+ true ->
+ special_init(TestCase, Config);
+ false ->
+ {skip, "DSA not supported by OpenSSL"}
+ end;
init_per_testcase(TestCase, Config) ->
ct:timetrap({seconds, 35}),
special_init(TestCase, Config).
+special_init(TestCase, Config) when
+ TestCase == ciphers_rsa_signed_certs;
+ TestCase == ciphers_dsa_signed_certs->
+ ct:timetrap({seconds, 90}),
+ Config;
special_init(TestCase, Config)
when TestCase == erlang_client_openssl_server_renegotiate;
- TestCase == erlang_client_openssl_server_nowrap_seqnum;
+ TestCase == erlang_client_openssl_server_nowrap_seqnum;
TestCase == erlang_server_openssl_client_nowrap_seqnum
- ->
+ ->
{ok, Version} = application:get_env(ssl, protocol_version),
check_sane_openssl_renegotaite(Config, Version);
-special_init(Case, Config) when Case == ssl2_erlang_server_openssl_client;
- Case == ssl2_erlang_server_openssl_client_comp ->
+special_init(ssl2_erlang_server_openssl_client, Config) ->
case ssl_test_lib:supports_ssl_tls_version(sslv2) of
- true ->
- Config;
- false ->
- {skip, "sslv2 not supported by openssl"}
- end;
+ true ->
+ Config;
+ false ->
+ {skip, "sslv2 not supported by openssl"}
+ end;
special_init(TestCase, Config)
- when TestCase == erlang_client_alpn_openssl_server_alpn;
- TestCase == erlang_server_alpn_openssl_client_alpn;
- TestCase == erlang_client_alpn_openssl_server;
- TestCase == erlang_client_openssl_server_alpn;
- TestCase == erlang_server_alpn_openssl_client;
- TestCase == erlang_server_openssl_client_alpn ->
+ when TestCase == erlang_client_alpn_openssl_server_alpn;
+ TestCase == erlang_server_alpn_openssl_client_alpn;
+ TestCase == erlang_client_alpn_openssl_server;
+ TestCase == erlang_client_openssl_server_alpn;
+ TestCase == erlang_server_alpn_openssl_client;
+ TestCase == erlang_server_openssl_client_alpn ->
check_openssl_alpn_support(Config);
special_init(TestCase, Config)
- when TestCase == erlang_client_alpn_openssl_server_alpn_renegotiate;
- TestCase == erlang_server_alpn_openssl_client_alpn_renegotiate ->
- {ok, Version} = application:get_env(ssl, protocol_version),
- case check_sane_openssl_renegotaite(Config, Version) of
- {skip, _} = Skip ->
- Skip;
- _ ->
- check_openssl_alpn_support(Config)
- end;
+ when TestCase == erlang_client_alpn_openssl_server_alpn_renegotiate;
+ TestCase == erlang_server_alpn_openssl_client_alpn_renegotiate ->
+ {ok, Version} = application:get_env(ssl, protocol_version),
+ case check_sane_openssl_renegotaite(Config, Version) of
+ {skip, _} = Skip ->
+ Skip;
+ _ ->
+ check_openssl_alpn_support(Config)
+ end;
special_init(TestCase, Config)
- when TestCase == erlang_client_alpn_npn_openssl_server_alpn_npn;
- TestCase == erlang_server_alpn_npn_openssl_client_alpn_npn ->
+ when TestCase == erlang_client_alpn_npn_openssl_server_alpn_npn;
+ TestCase == erlang_server_alpn_npn_openssl_client_alpn_npn ->
case check_openssl_alpn_support(Config) of
{skip, _} = Skip ->
Skip;
_ ->
- check_openssl_npn_support(Config)
+ check_openssl_npn_support(Config)
end;
special_init(TestCase, Config)
- when TestCase == erlang_client_openssl_server_npn;
- TestCase == erlang_server_openssl_client_npn;
- TestCase == erlang_server_openssl_client_npn_only_server;
- TestCase == erlang_server_openssl_client_npn_only_client;
- TestCase == erlang_client_openssl_server_npn_only_client;
- TestCase == erlang_client_openssl_server_npn_only_server ->
+ when TestCase == erlang_client_openssl_server_npn;
+ TestCase == erlang_server_openssl_client_npn;
+ TestCase == erlang_server_openssl_client_npn_only_server;
+ TestCase == erlang_server_openssl_client_npn_only_client;
+ TestCase == erlang_client_openssl_server_npn_only_client;
+ TestCase == erlang_client_openssl_server_npn_only_server ->
check_openssl_npn_support(Config);
special_init(TestCase, Config)
when TestCase == erlang_server_openssl_client_npn_renegotiate;
TestCase == erlang_client_openssl_server_npn_renegotiate ->
{ok, Version} = application:get_env(ssl, protocol_version),
- case check_sane_openssl_renegotaite(Config, Version) of
- {skip, _} = Skip ->
- Skip;
- _ ->
- check_openssl_npn_support(Config)
- end;
+ case check_sane_openssl_renegotaite(Config, Version) of
+ {skip, _} = Skip ->
+ Skip;
+ _ ->
+ check_openssl_npn_support(Config)
+ end;
special_init(TestCase, Config0)
when TestCase == erlang_server_openssl_client_sni_match;
@@ -282,25 +298,25 @@ special_init(TestCase, Config0)
TestCase == erlang_server_openssl_client_sni_match_fun;
TestCase == erlang_server_openssl_client_sni_no_match_fun;
TestCase == erlang_server_openssl_client_sni_no_header_fun ->
- RsaOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config0),
+ RsaOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config0),
Config = [{sni_server_opts, [{sni_hosts,
[{"a.server", [
{certfile, proplists:get_value(certfile, RsaOpts)},
{keyfile, proplists:get_value(keyfile, RsaOpts)}
]},
{"b.server", [
- {certfile, proplists:get_value(certfile, RsaOpts)},
+ {certfile, proplists:get_value(certfile, RsaOpts)},
{keyfile, proplists:get_value(keyfile, RsaOpts)}
]}
]}]} | Config0],
check_openssl_sni_support(Config);
special_init(_, Config) ->
- Config.
+ Config.
end_per_testcase(reuse_session_expired, Config) ->
application:unset_env(ssl, session_lifetime),
- Config;
+ Config;
end_per_testcase(_, Config) ->
Config.
@@ -323,8 +339,8 @@ basic_erlang_client_openssl_server(Config) when is_list(Config) ->
KeyFile = proplists:get_value(keyfile, ServerOpts),
Exe = "openssl",
- Args = ["s_server", "-accept", integer_to_list(Port),
- "-cert", CertFile, "-key", KeyFile],
+ Args = ["s_server", "-accept", integer_to_list(Port),
+ "-cert", CertFile, "-key", KeyFile],
OpensslPort = ssl_test_lib:portable_open_port(Exe, Args),
@@ -332,15 +348,15 @@ basic_erlang_client_openssl_server(Config) when is_list(Config) ->
ssl_test_lib:wait_for_openssl_server(Port, tls),
Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
- {host, Hostname},
- {from, self()},
- {mfa, {?MODULE,
- erlang_ssl_receive, [Data]}},
- {options, ClientOpts}]),
+ {host, Hostname},
+ {from, self()},
+ {mfa, {?MODULE,
+ erlang_ssl_receive, [Data]}},
+ {options, ClientOpts}]),
true = port_command(OpensslPort, Data),
ssl_test_lib:check_result(Client, ok),
-
+
%% Clean close down! Server needs to be closed first !!
ssl_test_lib:close_port(OpensslPort),
ssl_test_lib:close(Client),
@@ -352,23 +368,20 @@ basic_erlang_server_openssl_client() ->
basic_erlang_server_openssl_client(Config) when is_list(Config) ->
process_flag(trap_exit, true),
ServerOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config),
- V2Compat = proplists:get_value(v2_hello_compatible, Config),
{_, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
Data = "From openssl to erlang",
- ct:pal("v2_hello_compatible: ~p", [V2Compat]),
-
Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
- {from, self()},
- {mfa, {?MODULE, erlang_ssl_receive, [Data]}},
- {options,[{v2_hello_compatible, V2Compat} | ServerOpts]}]),
+ {from, self()},
+ {mfa, {?MODULE, erlang_ssl_receive, [Data]}},
+ {options,ServerOpts}]),
Port = ssl_test_lib:inet_port(Server),
Exe = "openssl",
Args = ["s_client", "-connect", hostname_format(Hostname) ++
- ":" ++ integer_to_list(Port) | workaround_openssl_s_clinent()],
+ ":" ++ integer_to_list(Port) ++ no_v2_flag() | workaround_openssl_s_clinent()],
OpenSslPort = ssl_test_lib:portable_open_port(Exe, Args),
true = port_command(OpenSslPort, Data),
@@ -398,19 +411,19 @@ erlang_client_openssl_server(Config) when is_list(Config) ->
Version = ssl_test_lib:protocol_version(Config),
Exe = "openssl",
Args = ["s_server", "-accept", integer_to_list(Port),
- ssl_test_lib:version_flag(Version),
- "-cert", CertFile, "-key", KeyFile],
-
+ ssl_test_lib:version_flag(Version),
+ "-cert", CertFile, "-key", KeyFile],
+
OpensslPort = ssl_test_lib:portable_open_port(Exe, Args),
ssl_test_lib:wait_for_openssl_server(Port, proplists:get_value(protocol, Config)),
Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
- {host, Hostname},
- {from, self()},
- {mfa, {?MODULE,
- erlang_ssl_receive, [Data]}},
- {options, ClientOpts}]),
+ {host, Hostname},
+ {from, self()},
+ {mfa, {?MODULE,
+ erlang_ssl_receive, [Data]}},
+ {options, ClientOpts}]),
true = port_command(OpensslPort, Data),
ssl_test_lib:check_result(Client, ok),
@@ -426,24 +439,24 @@ erlang_server_openssl_client() ->
erlang_server_openssl_client(Config) when is_list(Config) ->
process_flag(trap_exit, true),
ServerOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config),
-
- {_, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+ {_, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+
Data = "From openssl to erlang",
Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
- {from, self()},
- {mfa, {?MODULE, erlang_ssl_receive, [Data]}},
- {options, ServerOpts}]),
+ {from, self()},
+ {mfa, {?MODULE, erlang_ssl_receive, [Data]}},
+ {options, ServerOpts}]),
Port = ssl_test_lib:inet_port(Server),
Version = ssl_test_lib:protocol_version(Config),
Exe = "openssl",
Args = ["s_client", "-connect", hostname_format(Hostname) ++":" ++ integer_to_list(Port),
- ssl_test_lib:version_flag(Version)],
-
- OpenSslPort = ssl_test_lib:portable_open_port(Exe, Args),
+ ssl_test_lib:version_flag(Version)],
+ OpenSslPort = ssl_test_lib:portable_open_port(Exe, Args),
+
true = port_command(OpenSslPort, Data),
ssl_test_lib:check_result(Server, ok),
@@ -460,8 +473,8 @@ erlang_client_openssl_server_dsa_cert(Config) when is_list(Config) ->
ClientOpts = ssl_test_lib:ssl_options(client_dsa_opts, Config),
ServerOpts = ssl_test_lib:ssl_options(server_dsa_verify_opts, Config),
- {ClientNode, _, Hostname} = ssl_test_lib:run_where(Config),
-
+ {ClientNode, _, Hostname} = ssl_test_lib:run_where(Config),
+
Data = "From openssl to erlang",
Port = ssl_test_lib:inet_port(node()),
@@ -471,27 +484,27 @@ erlang_client_openssl_server_dsa_cert(Config) when is_list(Config) ->
Version = ssl_test_lib:protocol_version(Config),
Exe = "openssl",
Args = ["s_server", "-accept", integer_to_list(Port),
- ssl_test_lib:version_flag(Version),
- "-cert", CertFile, "-CAfile", CaCertFile,
- "-key", KeyFile, "-Verify", "2", "-msg"],
+ ssl_test_lib:version_flag(Version),
+ "-cert", CertFile, "-CAfile", CaCertFile,
+ "-key", KeyFile, "-Verify", "2", "-msg"],
OpensslPort = ssl_test_lib:portable_open_port(Exe, Args),
ssl_test_lib:wait_for_openssl_server(Port, proplists:get_value(protocol, Config)),
Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
- {host, Hostname},
- {from, self()},
- {mfa, {?MODULE,
- erlang_ssl_receive, [Data]}},
- {options, ClientOpts}]),
+ {host, Hostname},
+ {from, self()},
+ {mfa, {?MODULE,
+ erlang_ssl_receive, [Data]}},
+ {options, ClientOpts}]),
true = port_command(OpensslPort, Data),
- ssl_test_lib:check_result(Client, ok),
-
+ ssl_test_lib:check_result(Client, ok),
+
%% Clean close down! Server needs to be closed first !!
- ssl_test_lib:close_port(OpensslPort),
+ ssl_test_lib:close_port(OpensslPort),
ssl_test_lib:close(Client),
process_flag(trap_exit, false),
ok.
@@ -511,17 +524,17 @@ erlang_server_openssl_client_dsa_cert(Config) when is_list(Config) ->
KeyFile = proplists:get_value(keyfile, ClientOpts),
Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
- {from, self()},
- {mfa, {?MODULE, erlang_ssl_receive, [Data]}},
- {options, ServerOpts}]),
+ {from, self()},
+ {mfa, {?MODULE, erlang_ssl_receive, [Data]}},
+ {options, ServerOpts}]),
Port = ssl_test_lib:inet_port(Server),
Version = ssl_test_lib:protocol_version(Config),
Exe = "openssl",
Args = ["s_client", "-connect", hostname_format(Hostname) ++ ":" ++ integer_to_list(Port),
- ssl_test_lib:version_flag(Version),
- "-cert", CertFile,
- "-CAfile", CaCertFile,
- "-key", KeyFile, "-msg"],
+ ssl_test_lib:version_flag(Version),
+ "-cert", CertFile,
+ "-CAfile", CaCertFile,
+ "-key", KeyFile, "-msg"],
OpenSslPort = ssl_test_lib:portable_open_port(Exe, Args),
true = port_command(OpenSslPort, Data),
@@ -533,11 +546,124 @@ erlang_server_openssl_client_dsa_cert(Config) when is_list(Config) ->
ssl_test_lib:close_port(OpenSslPort),
process_flag(trap_exit, false).
-%%--------------------------------------------------------------------
+ %%--------------------------------------------------------------------
+erlang_client_openssl_server_anon() ->
+ [{doc,"Test erlang client with openssl server, anonymous"}].
+erlang_client_openssl_server_anon(Config) when is_list(Config) ->
+ process_flag(trap_exit, true),
+ %% OpenSSL expects a certificate and key, even if the cipher spec
+ %% is restructed to aNULL, so we use 'server_rsa_opts' here
+ ServerOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config),
+ ClientOpts = ssl_test_lib:ssl_options(client_anon_opts, Config),
+ VersionTuple = ssl_test_lib:protocol_version(Config, tuple),
+ Ciphers = ssl_test_lib:anonymous_suites(VersionTuple),
+
+ {ClientNode, _, Hostname} = ssl_test_lib:run_where(Config),
+
+ Data = "From openssl to erlang",
+
+ Port = ssl_test_lib:inet_port(node()),
+ CertFile = proplists:get_value(certfile, ServerOpts),
+ KeyFile = proplists:get_value(keyfile, ServerOpts),
+ Version = ssl_test_lib:protocol_version(Config),
+ Exe = "openssl",
+ Args = ["s_server", "-accept", integer_to_list(Port),
+ ssl_test_lib:version_flag(Version),
+ "-cert", CertFile, "-key", KeyFile,
+ "-cipher", "aNULL", "-msg"],
+
+ OpensslPort = ssl_test_lib:portable_open_port(Exe, Args),
+
+ ssl_test_lib:wait_for_openssl_server(Port, proplists:get_value(protocol, Config)),
+
+ Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa, {?MODULE,
+ erlang_ssl_receive, [Data]}},
+ {options, [{ciphers, Ciphers} | ClientOpts]}]),
+
+ true = port_command(OpensslPort, Data),
+
+ ssl_test_lib:check_result(Client, ok),
+
+ %% Clean close down! Server needs to be closed first !!
+ ssl_test_lib:close_port(OpensslPort),
+ ssl_test_lib:close(Client),
+ process_flag(trap_exit, false),
+ ok.
+%%--------------------------------------------------------------------
+erlang_server_openssl_client_anon() ->
+ [{doc,"Test erlang server with openssl client, anonymous"}].
+erlang_server_openssl_client_anon(Config) when is_list(Config) ->
+ process_flag(trap_exit, true),
+ ServerOpts = ssl_test_lib:ssl_options(server_anon_opts, Config),
+ VersionTuple = ssl_test_lib:protocol_version(Config, tuple),
+ Ciphers = ssl_test_lib:anonymous_suites(VersionTuple),
+
+ {_, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+
+ Data = "From openssl to erlang",
+
+ Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {mfa, {?MODULE, erlang_ssl_receive, [Data]}},
+ {options, [{ciphers, Ciphers} | ServerOpts]}]),
+ Port = ssl_test_lib:inet_port(Server),
+ Version = ssl_test_lib:protocol_version(Config),
+ Exe = "openssl",
+ Args = ["s_client", "-connect", hostname_format(Hostname) ++ ":" ++ integer_to_list(Port),
+ ssl_test_lib:version_flag(Version),
+ "-cipher", "aNULL", "-msg"],
+
+ OpenSslPort = ssl_test_lib:portable_open_port(Exe, Args),
+ true = port_command(OpenSslPort, Data),
+
+ ssl_test_lib:check_result(Server, ok),
+
+ %% Clean close down! Server needs to be closed first !!
+ ssl_test_lib:close(Server),
+ ssl_test_lib:close_port(OpenSslPort),
+ process_flag(trap_exit, false).
+
+%%--------------------------------------------------------------------
+erlang_server_openssl_client_anon_with_cert() ->
+ [{doc,"Test erlang server with openssl client, anonymous (with cert)"}].
+erlang_server_openssl_client_anon_with_cert(Config) when is_list(Config) ->
+ process_flag(trap_exit, true),
+ ServerOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config),
+ VersionTuple = ssl_test_lib:protocol_version(Config, tuple),
+ Ciphers = ssl_test_lib:anonymous_suites(VersionTuple),
+
+ {_, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+
+ Data = "From openssl to erlang",
+
+ Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {mfa, {?MODULE, erlang_ssl_receive, [Data]}},
+ {options, [{ciphers, Ciphers} | ServerOpts]}]),
+ Port = ssl_test_lib:inet_port(Server),
+ Version = ssl_test_lib:protocol_version(Config),
+ Exe = "openssl",
+ Args = ["s_client", "-connect", hostname_format(Hostname) ++ ":" ++ integer_to_list(Port),
+ ssl_test_lib:version_flag(Version),
+ "-cipher", "aNULL", "-msg"],
+
+ OpenSslPort = ssl_test_lib:portable_open_port(Exe, Args),
+ true = port_command(OpenSslPort, Data),
+
+ ssl_test_lib:check_result(Server, ok),
+
+ %% Clean close down! Server needs to be closed first !!
+ ssl_test_lib:close(Server),
+ ssl_test_lib:close_port(OpenSslPort),
+ process_flag(trap_exit, false).
+ %%--------------------------------------------------------------------
erlang_server_openssl_client_reuse_session() ->
[{doc, "Test erlang server with openssl client that reconnects with the"
- "same session id, to test reusing of sessions."}].
+ "same session id, to test reusing of sessions."}].
erlang_server_openssl_client_reuse_session(Config) when is_list(Config) ->
process_flag(trap_exit, true),
ServerOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config),
@@ -547,18 +673,18 @@ erlang_server_openssl_client_reuse_session(Config) when is_list(Config) ->
Data = "From openssl to erlang",
Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
- {from, self()},
- {mfa, {?MODULE, erlang_ssl_receive, [Data]}},
- {reconnect_times, 5},
- {options, ServerOpts}]),
+ {from, self()},
+ {mfa, {?MODULE, erlang_ssl_receive, [Data]}},
+ {reconnect_times, 5},
+ {options, ServerOpts}]),
Port = ssl_test_lib:inet_port(Server),
Version = ssl_test_lib:protocol_version(Config),
-
+
Exe = "openssl",
Args = ["s_client", "-connect", hostname_format(Hostname)
++ ":" ++ integer_to_list(Port),
- ssl_test_lib:version_flag(Version),
- "-reconnect"],
+ ssl_test_lib:version_flag(Version),
+ "-reconnect"],
OpenSslPort = ssl_test_lib:portable_open_port(Exe, Args),
@@ -569,7 +695,7 @@ erlang_server_openssl_client_reuse_session(Config) when is_list(Config) ->
%% Clean close down! Server needs to be closed first !!
ssl_test_lib:close(Server),
ssl_test_lib:close_port(OpenSslPort),
- process_flag(trap_exit, false),
+ process_flag(trap_exit, false),
ok.
%%--------------------------------------------------------------------
@@ -593,46 +719,46 @@ erlang_client_openssl_server_renegotiate(Config) when is_list(Config) ->
Exe = "openssl",
Args = ["s_server", "-accept", integer_to_list(Port),
- ssl_test_lib:version_flag(Version),
- "-cert", CertFile, "-key", KeyFile, "-msg"],
+ ssl_test_lib:version_flag(Version),
+ "-cert", CertFile, "-key", KeyFile, "-msg"],
OpensslPort = ssl_test_lib:portable_open_port(Exe, Args),
ssl_test_lib:wait_for_openssl_server(Port, proplists:get_value(protocol, Config)),
Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
- {host, Hostname},
- {from, self()},
- {mfa, {?MODULE,
- delayed_send, [[ErlData, OpenSslData]]}},
- {options, ClientOpts}]),
+ {host, Hostname},
+ {from, self()},
+ {mfa, {?MODULE,
+ delayed_send, [[ErlData, OpenSslData]]}},
+ {options, ClientOpts}]),
true = port_command(OpensslPort, ?OPENSSL_RENEGOTIATE),
ct:sleep(?SLEEP),
true = port_command(OpensslPort, OpenSslData),
ssl_test_lib:check_result(Client, ok),
-
- %% Clean close down! Server needs to be closed first !!
+
+ %% Clean close down! Server needs to be closed first !!
ssl_test_lib:close_port(OpensslPort),
ssl_test_lib:close(Client),
- process_flag(trap_exit, false),
+ process_flag(trap_exit, false),
ok.
%%--------------------------------------------------------------------
erlang_client_openssl_server_nowrap_seqnum() ->
[{doc, "Test that erlang client will renegotiate session when",
- "max sequence number celing is about to be reached. Although"
- "in the testcase we use the test option renegotiate_at"
- " to lower treashold substantially."}].
+ "max sequence number celing is about to be reached. Although"
+ "in the testcase we use the test option renegotiate_at"
+ " to lower treashold substantially."}].
erlang_client_openssl_server_nowrap_seqnum(Config) when is_list(Config) ->
process_flag(trap_exit, true),
ServerOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config),
ClientOpts = ssl_test_lib:ssl_options(client_rsa_opts, Config),
{ClientNode, _, Hostname} = ssl_test_lib:run_where(Config),
-
+
ErlData = "From erlang to openssl\n",
N = 10,
@@ -642,21 +768,21 @@ erlang_client_openssl_server_nowrap_seqnum(Config) when is_list(Config) ->
Version = ssl_test_lib:protocol_version(Config),
Exe = "openssl",
Args = ["s_server", "-accept", integer_to_list(Port),
- ssl_test_lib:version_flag(Version),
- "-cert", CertFile, "-key", KeyFile, "-msg"],
-
+ ssl_test_lib:version_flag(Version),
+ "-cert", CertFile, "-key", KeyFile, "-msg"],
+
OpensslPort = ssl_test_lib:portable_open_port(Exe, Args),
ssl_test_lib:wait_for_openssl_server(Port, proplists:get_value(protocol, Config)),
Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
- {host, Hostname},
- {from, self()},
- {mfa, {ssl_test_lib,
- trigger_renegotiate, [[ErlData, N+2]]}},
- {options, [{reuse_sessions, false},
- {renegotiate_at, N} | ClientOpts]}]),
-
+ {host, Hostname},
+ {from, self()},
+ {mfa, {ssl_test_lib,
+ trigger_renegotiate, [[ErlData, N+2]]}},
+ {options, [{reuse_sessions, false},
+ {renegotiate_at, N} | ClientOpts]}]),
+
ssl_test_lib:check_result(Client, ok),
%% Clean close down! Server needs to be closed first !!
@@ -666,37 +792,37 @@ erlang_client_openssl_server_nowrap_seqnum(Config) when is_list(Config) ->
%%--------------------------------------------------------------------
erlang_server_openssl_client_nowrap_seqnum() ->
[{doc, "Test that erlang client will renegotiate session when",
- "max sequence number celing is about to be reached. Although"
- "in the testcase we use the test option renegotiate_at"
- " to lower treashold substantially."}].
+ "max sequence number celing is about to be reached. Although"
+ "in the testcase we use the test option renegotiate_at"
+ " to lower treashold substantially."}].
erlang_server_openssl_client_nowrap_seqnum(Config) when is_list(Config) ->
process_flag(trap_exit, true),
ServerOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config),
{_, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
-
+
Data = "From openssl to erlang",
-
+
N = 10,
Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
- {from, self()},
- {mfa, {ssl_test_lib,
- trigger_renegotiate, [[Data, N+2]]}},
- {options, [{renegotiate_at, N}, {reuse_sessions, false} | ServerOpts]}]),
+ {from, self()},
+ {mfa, {ssl_test_lib,
+ trigger_renegotiate, [[Data, N+2]]}},
+ {options, [{renegotiate_at, N}, {reuse_sessions, false} | ServerOpts]}]),
Port = ssl_test_lib:inet_port(Server),
Version = ssl_test_lib:protocol_version(Config),
Exe = "openssl",
Args = ["s_client","-connect", hostname_format(Hostname) ++ ":" ++ integer_to_list(Port),
- ssl_test_lib:version_flag(Version),
- "-msg"],
-
+ ssl_test_lib:version_flag(Version),
+ "-msg"],
+
OpenSslPort = ssl_test_lib:portable_open_port(Exe, Args),
true = port_command(OpenSslPort, Data),
-
+
ssl_test_lib:check_result(Server, ok),
-
+
%% Clean close down! Server needs to be closed first !!
ssl_test_lib:close(Server),
ssl_test_lib:close_port(OpenSslPort),
@@ -706,15 +832,15 @@ erlang_server_openssl_client_nowrap_seqnum(Config) when is_list(Config) ->
erlang_client_openssl_server_no_server_ca_cert() ->
[{doc, "Test erlang client when openssl server sends a cert chain not"
- "including the ca cert. Explicitly test this even if it is"
- "implicitly tested eleswhere."}].
+ "including the ca cert. Explicitly test this even if it is"
+ "implicitly tested eleswhere."}].
erlang_client_openssl_server_no_server_ca_cert(Config) when is_list(Config) ->
process_flag(trap_exit, true),
ServerOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config),
ClientOpts = ssl_test_lib:ssl_options(client_rsa_opts, Config),
{ClientNode, _, Hostname} = ssl_test_lib:run_where(Config),
-
+
Data = "From openssl to erlang",
Port = ssl_test_lib:inet_port(node()),
@@ -723,22 +849,22 @@ erlang_client_openssl_server_no_server_ca_cert(Config) when is_list(Config) ->
Version = ssl_test_lib:protocol_version(Config),
Exe = "openssl",
Args = ["s_server", "-accept", integer_to_list(Port),
- ssl_test_lib:version_flag(Version),
- "-cert", CertFile, "-key", KeyFile, "-msg"],
-
+ ssl_test_lib:version_flag(Version),
+ "-cert", CertFile, "-key", KeyFile, "-msg"],
+
OpensslPort = ssl_test_lib:portable_open_port(Exe, Args),
-
+
ssl_test_lib:wait_for_openssl_server(Port, proplists:get_value(protocol, Config)),
Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
- {host, Hostname},
- {from, self()},
- {mfa, {?MODULE,
- erlang_ssl_receive, [Data]}},
- {options, ClientOpts}]),
+ {host, Hostname},
+ {from, self()},
+ {mfa, {?MODULE,
+ erlang_ssl_receive, [Data]}},
+ {options, ClientOpts}]),
true = port_command(OpensslPort, Data),
-
+
ssl_test_lib:check_result(Client, ok),
%% Clean close down! Server needs to be closed first !!
@@ -755,9 +881,9 @@ erlang_client_openssl_server_client_cert(Config) when is_list(Config) ->
ClientOpts = ssl_test_lib:ssl_options(client_rsa_verify_opts, Config),
{ClientNode, _, Hostname} = ssl_test_lib:run_where(Config),
-
+
Data = "From openssl to erlang",
-
+
Port = ssl_test_lib:inet_port(node()),
CertFile = proplists:get_value(certfile, ServerOpts),
CaCertFile = proplists:get_value(cacertfile, ServerOpts),
@@ -765,31 +891,30 @@ erlang_client_openssl_server_client_cert(Config) when is_list(Config) ->
Version = ssl_test_lib:protocol_version(Config),
Exe = "openssl",
Args = ["s_server", "-accept", integer_to_list(Port),
- ssl_test_lib:version_flag(Version),
- "-cert", CertFile, "-CAfile", CaCertFile,
- "-key", KeyFile, "-Verify", "2"],
-
+ ssl_test_lib:version_flag(Version),
+ "-cert", CertFile, "-CAfile", CaCertFile,
+ "-key", KeyFile, "-Verify", "2"],
+
OpensslPort = ssl_test_lib:portable_open_port(Exe, Args),
ssl_test_lib:wait_for_openssl_server(Port, proplists:get_value(protocol, Config)),
Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
- {host, Hostname},
- {from, self()},
- {mfa, {?MODULE,
- erlang_ssl_receive, [Data]}},
- {options, ClientOpts}]),
+ {host, Hostname},
+ {from, self()},
+ {mfa, {?MODULE,
+ erlang_ssl_receive, [Data]}},
+ {options, ClientOpts}]),
true = port_command(OpensslPort, Data),
-
+
ssl_test_lib:check_result(Client, ok),
-
+
%% Clean close down! Server needs to be closed first !!
ssl_test_lib:close_port(OpensslPort),
ssl_test_lib:close(Client),
process_flag(trap_exit, false).
%%--------------------------------------------------------------------
-
erlang_server_openssl_client_client_cert() ->
[{doc,"Test erlang server with openssl client when client sends cert"}].
erlang_server_openssl_client_client_cert(Config) when is_list(Config) ->
@@ -798,39 +923,38 @@ erlang_server_openssl_client_client_cert(Config) when is_list(Config) ->
ClientOpts = ssl_test_lib:ssl_options(client_rsa_verify_opts, Config),
{_, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
-
+
Data = "From openssl to erlang",
Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
- {from, self()},
- {mfa, {?MODULE,
- erlang_ssl_receive, [Data]}},
- {options,
- [{verify , verify_peer}
- | ServerOpts]}]),
+ {from, self()},
+ {mfa, {?MODULE,
+ erlang_ssl_receive, [Data]}},
+ {options,
+ [{verify , verify_peer}
+ | ServerOpts]}]),
Port = ssl_test_lib:inet_port(Server),
-
+
CaCertFile = proplists:get_value(cacertfile, ClientOpts),
CertFile = proplists:get_value(certfile, ClientOpts),
KeyFile = proplists:get_value(keyfile, ClientOpts),
Version = ssl_test_lib:protocol_version(Config),
Exe = "openssl",
Args = ["s_client", "-cert", CertFile,
- "-CAfile", CaCertFile,
- "-key", KeyFile,"-connect", hostname_format(Hostname) ++ ":" ++ integer_to_list(Port),
- ssl_test_lib:version_flag(Version)],
+ "-CAfile", CaCertFile,
+ "-key", KeyFile,"-connect", hostname_format(Hostname) ++ ":" ++ integer_to_list(Port),
+ ssl_test_lib:version_flag(Version)],
OpenSslPort = ssl_test_lib:portable_open_port(Exe, Args),
true = port_command(OpenSslPort, Data),
ssl_test_lib:check_result(Server, ok),
-
+
%% Clean close down! Server needs to be closed first !!
ssl_test_lib:close_port(OpenSslPort),
ssl_test_lib:close(Server),
process_flag(trap_exit, false).
%%--------------------------------------------------------------------
-
erlang_server_erlang_client_client_cert() ->
[{doc,"Test erlang server with erlang client when client sends cert"}].
erlang_server_erlang_client_client_cert(Config) when is_list(Config) ->
@@ -839,30 +963,30 @@ erlang_server_erlang_client_client_cert(Config) when is_list(Config) ->
ClientOpts = proplists:get_value(client_rsa_verify_opts, Config),
Version = ssl_test_lib:protocol_version(Config),
{ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
-
+
Data = "From erlang to erlang",
Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
- {from, self()},
- {mfa, {?MODULE,
- erlang_ssl_receive,
- %% Due to 1/n-1 splitting countermeasure Rizzo/Duong-Beast
- [Data]}},
- {options,
- [{verify , verify_peer}
- | ServerOpts]}]),
+ {from, self()},
+ {mfa, {?MODULE,
+ erlang_ssl_receive,
+ %% Due to 1/n-1 splitting countermeasure Rizzo/Duong-Beast
+ [Data]}},
+ {options,
+ [{verify , verify_peer}
+ | ServerOpts]}]),
Port = ssl_test_lib:inet_port(Server),
-
+
Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
- {host, Hostname},
- {from, self()},
- %% Due to 1/n-1 splitting countermeasure Rizzo/Duong-Beast
- {mfa, {ssl, send, [Data]}},
- {options,
- [{versions, [Version]} | ClientOpts]}]),
-
+ {host, Hostname},
+ {from, self()},
+ %% Due to 1/n-1 splitting countermeasure Rizzo/Duong-Beast
+ {mfa, {ssl, send, [Data]}},
+ {options,
+ [{versions, [Version]} | ClientOpts]}]),
+
ssl_test_lib:check_result(Server, ok, Client, ok),
-
+
ssl_test_lib:close(Server),
ssl_test_lib:close(Client),
process_flag(trap_exit, false).
@@ -894,43 +1018,43 @@ erlang_client_bad_openssl_server(Config) when is_list(Config) ->
ClientOpts = ssl_test_lib:ssl_options(client_rsa_opts, Config),
{ClientNode, _, Hostname} = ssl_test_lib:run_where(Config),
-
+
Port = ssl_test_lib:inet_port(node()),
CertFile = proplists:get_value(certfile, ServerOpts),
KeyFile = proplists:get_value(keyfile, ServerOpts),
Version = ssl_test_lib:protocol_version(Config),
Exe = "openssl",
Args = ["s_server", "-accept", integer_to_list(Port), ssl_test_lib:version_flag(Version),
- "-cert", CertFile, "-key", KeyFile],
+ "-cert", CertFile, "-key", KeyFile],
OpensslPort = ssl_test_lib:portable_open_port(Exe, Args),
-
+
ssl_test_lib:wait_for_openssl_server(Port, proplists:get_value(protocol, Config)),
-
+
Client0 = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
- {host, Hostname},
- {from, self()},
- {mfa, {?MODULE, server_sent_garbage, []}},
- {options,
- [{versions, [Version]} | ClientOpts]}]),
-
+ {host, Hostname},
+ {from, self()},
+ {mfa, {?MODULE, server_sent_garbage, []}},
+ {options,
+ [{versions, [Version]} | ClientOpts]}]),
+
%% Send garbage
true = port_command(OpensslPort, ?OPENSSL_GARBAGE),
ct:sleep(?SLEEP),
Client0 ! server_sent_garbage,
-
+
ssl_test_lib:check_result(Client0, true),
-
+
ssl_test_lib:close(Client0),
-
+
%% Make sure openssl does not hang and leave zombie process
Client1 = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
- {host, Hostname},
- {from, self()},
- {mfa, {ssl_test_lib, no_result_msg, []}},
- {options,
- [{versions, [Version]} | ClientOpts]}]),
+ {host, Hostname},
+ {from, self()},
+ {mfa, {ssl_test_lib, no_result_msg, []}},
+ {options,
+ [{versions, [Version]} | ClientOpts]}]),
%% Clean close down! Server needs to be closed first !!
ssl_test_lib:close_port(OpensslPort),
@@ -955,38 +1079,38 @@ expired_session(Config) when is_list(Config) ->
Exe = "openssl",
Args = ["s_server", "-accept", integer_to_list(Port),
- "-cert", CertFile,"-key", KeyFile],
-
+ "-cert", CertFile,"-key", KeyFile],
+
OpensslPort = ssl_test_lib:portable_open_port(Exe, Args),
ssl_test_lib:wait_for_openssl_server(Port, tls),
-
+
Client0 =
- ssl_test_lib:start_client([{node, ClientNode},
- {port, Port}, {host, Hostname},
- {mfa, {ssl_test_lib, no_result, []}},
- {from, self()}, {options, ClientOpts}]),
-
+ ssl_test_lib:start_client([{node, ClientNode},
+ {port, Port}, {host, Hostname},
+ {mfa, {ssl_test_lib, no_result, []}},
+ {from, self()}, {options, ClientOpts}]),
+
ssl_test_lib:close(Client0),
%% Make sure session is registered
ct:sleep(?SLEEP),
Client1 =
- ssl_test_lib:start_client([{node, ClientNode},
- {port, Port}, {host, Hostname},
- {mfa, {ssl_test_lib, no_result, []}},
- {from, self()}, {options, ClientOpts}]),
-
+ ssl_test_lib:start_client([{node, ClientNode},
+ {port, Port}, {host, Hostname},
+ {mfa, {ssl_test_lib, no_result, []}},
+ {from, self()}, {options, ClientOpts}]),
+
ssl_test_lib:close(Client1),
%% Make sure session is unregistered due to expiration
ct:sleep((?EXPIRE+1) * 1000),
-
+
Client2 =
- ssl_test_lib:start_client([{node, ClientNode},
- {port, Port}, {host, Hostname},
- {mfa, {ssl_test_lib, no_result, []}},
- {from, self()}, {options, ClientOpts}]),
+ ssl_test_lib:start_client([{node, ClientNode},
+ {port, Port}, {host, Hostname},
+ {mfa, {ssl_test_lib, no_result, []}},
+ {from, self()}, {options, ClientOpts}]),
%% Clean close down! Server needs to be closed first !!
ssl_test_lib:close_port(OpensslPort),
@@ -1002,52 +1126,21 @@ ssl2_erlang_server_openssl_client(Config) when is_list(Config) ->
ServerOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config),
{_, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
-
- Server = ssl_test_lib:start_server_error([{node, ServerNode}, {port, 0},
- {from, self()},
- {options, ServerOpts}]),
- Port = ssl_test_lib:inet_port(Server),
-
- Exe = "openssl",
- Args = ["s_client", "-connect", hostname_format(Hostname) ++ ":" ++ integer_to_list(Port),
- "-ssl2", "-msg"],
-
- OpenSslPort = ssl_test_lib:portable_open_port(Exe, Args),
-
- ct:log("Ports ~p~n", [[erlang:port_info(P) || P <- erlang:ports()]]),
- consume_port_exit(OpenSslPort),
- ssl_test_lib:check_result(Server, {error, {tls_alert, "bad record mac"}}),
- process_flag(trap_exit, false).
-%%--------------------------------------------------------------------
-ssl2_erlang_server_openssl_client_comp() ->
- [{doc,"Test that ssl v2 clients are rejected"}].
-
-ssl2_erlang_server_openssl_client_comp(Config) when is_list(Config) ->
- process_flag(trap_exit, true),
- ServerOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config),
- V2Compat = proplists:get_value(v2_hello_compatible, Config),
-
- ServerOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config),
-
- {_, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
-
- Data = "From openssl to erlang",
Server = ssl_test_lib:start_server_error([{node, ServerNode}, {port, 0},
- {from, self()},
- {options, [{v2_hello_compatible, V2Compat} | ServerOpts]}]),
+ {from, self()},
+ {options, ServerOpts}]),
Port = ssl_test_lib:inet_port(Server),
-
+
Exe = "openssl",
Args = ["s_client", "-connect", hostname_format(Hostname) ++ ":" ++ integer_to_list(Port),
- "-ssl2", "-msg"],
-
+ "-ssl2", "-msg"],
+
OpenSslPort = ssl_test_lib:portable_open_port(Exe, Args),
- true = port_command(OpenSslPort, Data),
-
+
ct:log("Ports ~p~n", [[erlang:port_info(P) || P <- erlang:ports()]]),
consume_port_exit(OpenSslPort),
- ssl_test_lib:check_result(Server, {error, {tls_alert, "protocol version"}}),
+ ssl_test_lib:check_result(Server, {error, {tls_alert, "bad record mac"}}),
process_flag(trap_exit, false).
%%--------------------------------------------------------------------
@@ -1875,3 +1968,11 @@ hostname_format(Hostname) ->
false ->
"localhost"
end.
+
+no_v2_flag() ->
+ case ssl_test_lib:supports_ssl_tls_version(sslv2) of
+ true ->
+ " -no_ssl2 ";
+ false ->
+ ""
+ end.
diff --git a/lib/ssl/vsn.mk b/lib/ssl/vsn.mk
index 2650399eea..0ff22c5eab 100644
--- a/lib/ssl/vsn.mk
+++ b/lib/ssl/vsn.mk
@@ -1 +1 @@
-SSL_VSN = 8.2.3
+SSL_VSN = 8.2.5
diff --git a/lib/stdlib/doc/src/assert_hrl.xml b/lib/stdlib/doc/src/assert_hrl.xml
index ea23cca2ee..33f29f38da 100644
--- a/lib/stdlib/doc/src/assert_hrl.xml
+++ b/lib/stdlib/doc/src/assert_hrl.xml
@@ -93,7 +93,7 @@ erlc -DNOASSERT=true *.erl</code>
<taglist>
<tag><c>assert(BoolExpr)</c></tag>
<item></item>
- <tag><c>URKAassert(BoolExpr, Comment)</c></tag>
+ <tag><c>assert(BoolExpr, Comment)</c></tag>
<item>
<p>Tests that <c>BoolExpr</c> completes normally returning
<c>true</c>.</p>
diff --git a/lib/stdlib/doc/src/erl_tar.xml b/lib/stdlib/doc/src/erl_tar.xml
index 337028568a..14c543ee2b 100644
--- a/lib/stdlib/doc/src/erl_tar.xml
+++ b/lib/stdlib/doc/src/erl_tar.xml
@@ -4,7 +4,7 @@
<erlref>
<header>
<copyright>
- <year>2003</year><year>2017</year>
+ <year>2003</year><year>2018</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -136,6 +136,9 @@
<v>Filename = filename()|{NameInArchive,FilenameOrBin}</v>
<v>Options = [Option]</v>
<v>Option = dereference|verbose|{chunks,ChunkSize}</v>
+ <v>|{atime,non_neg_integer()}|{mtime,non_neg_integer()}</v>
+ <v>|{ctime,non_neg_integer()}|{uid,non_neg_integer()}</v>
+ <v>|{gid,non_neg_integer()}</v>
<v>ChunkSize = positive_integer()</v>
<v>RetValue = ok|{error,{Filename,Reason}}</v>
<v>Reason = term()</v>
@@ -167,6 +170,42 @@
<seealso marker="ssh:ssh_sftp#open_tar/3">
<c>ssh_sftp:open_tar/3</c></seealso>.</p>
</item>
+ <tag><c>{atime,non_neg_integer()}</c></tag>
+ <item>
+ <p>Sets the last time, as
+ <seealso marker="erts:time_correction#POSIX_Time">
+ POSIX time</seealso>, when the file was read. See also
+ <seealso marker="kernel:file#read_file_info/1">
+ <c>file:read_file_info/1</c></seealso>.</p>
+ </item>
+ <tag><c>{mtime,non_neg_integer()}</c></tag>
+ <item>
+ <p>Sets the last time, as
+ <seealso marker="erts:time_correction#POSIX_Time">
+ POSIX time</seealso>, when the file was written. See also
+ <seealso marker="kernel:file#read_file_info/1">
+ <c>file:read_file_info/1</c></seealso>.</p>
+ </item>
+ <tag><c>{ctime,non_neg_integer()}</c></tag>
+ <item>
+ <p>Sets the time, as
+ <seealso marker="erts:time_correction#POSIX_Time">
+ POSIX time</seealso>, when the file was created. See also
+ <seealso marker="kernel:file#read_file_info/1">
+ <c>file:read_file_info/1</c></seealso>.</p>
+ </item>
+ <tag><c>{uid,non_neg_integer()}</c></tag>
+ <item>
+ <p>Sets the file owner.
+ <seealso marker="kernel:file#read_file_info/1">
+ <c>file:read_file_info/1</c></seealso>.</p>
+ </item>
+ <tag><c>{gid,non_neg_integer()}</c></tag>
+ <item>
+ <p>Sets the group that the file owner belongs to.
+ <seealso marker="kernel:file#read_file_info/1">
+ <c>file:read_file_info/1</c></seealso>.</p>
+ </item>
</taglist>
</desc>
</func>
@@ -378,7 +417,7 @@
<v>Reason = term()</v>
</type>
<desc>
- <p>Cconverts an error reason term to a human-readable error message
+ <p>Converts an error reason term to a human-readable error message
string.</p>
</desc>
</func>
diff --git a/lib/stdlib/doc/src/ets.xml b/lib/stdlib/doc/src/ets.xml
index a0ec22c515..305376a425 100644
--- a/lib/stdlib/doc/src/ets.xml
+++ b/lib/stdlib/doc/src/ets.xml
@@ -487,6 +487,11 @@ Error: fun containing local Erlang function calls
<p>The pid of the heir of the table, or <c>none</c> if no heir
is set.</p>
</item>
+ <tag><c>{id,</c><seealso marker="#type-tid">
+ <c>tid()</c></seealso><c>}</c></tag>
+ <item>
+ <p>The table identifier.</p>
+ </item>
<tag><c>{keypos, integer() >= 1}</c></tag>
<item>
<p>The key position.</p>
@@ -963,11 +968,11 @@ ets:is_compiled_ms(Broken).</code>
<func>
<name name="match_spec_run" arity="2"/>
<fsummary>Perform matching, using a compiled match specification on a
- list of tuples.</fsummary>
+ list of terms.</fsummary>
<desc>
<p>Executes the matching specified in a compiled
<seealso marker="#match_spec">match specification</seealso> on a list
- of tuples. Term <c><anno>CompiledMatchSpec</anno></c> is to be
+ of terms. Term <c><anno>CompiledMatchSpec</anno></c> is to be
the result of a call to <seealso marker="#match_spec_compile/1">
<c>match_spec_compile/1</c></seealso> and is hence the internal
representation of the match specification one wants to use.</p>
@@ -985,7 +990,7 @@ Table = ets:new...
MatchSpec = ...
% The following call...
ets:match_spec_run(ets:tab2list(Table),
-ets:match_spec_compile(MatchSpec)),
+ ets:match_spec_compile(MatchSpec)),
% ...gives the same result as the more common (and more efficient)
ets:select(Table, MatchSpec),</code>
<note>
@@ -1074,10 +1079,13 @@ ets:select(Table, MatchSpec),</code>
</item>
<tag><c>named_table</c></tag>
<item>
- <p>If this option is present, name <c><anno>Name</anno></c> is
- associated with the table identifier. The name can then
- be used instead of the table identifier in subsequent
- operations.</p>
+ <p>If this option is present, the table is registered under its
+ <c><anno>Name</anno></c> which can then be used instead of the
+ table identifier in subsequent operations.</p>
+ <p>The function will also return the <c><anno>Name</anno></c>
+ instead of the table identifier. To get the table identifier of a
+ named table, use
+ <seealso marker="#whereis/1"><c>whereis/1</c></seealso>.</p>
</item>
<tag><c>{keypos,<anno>Pos</anno>}</c></tag>
<item>
@@ -2037,6 +2045,21 @@ true</pre>
</list>
</desc>
</func>
+
+ <func>
+ <name name="whereis" arity="1"/>
+ <fsummary>Retrieves the tid() of a named table.</fsummary>
+ <desc>
+ <p>This function returns the
+ <seealso marker="#type-tid"><c>tid()</c></seealso> of the named table
+ identified by <c><anno>TableName</anno></c>, or <c>undefined</c> if
+ no such table exists. The <c>tid()</c> can be used in place of the
+ table name in all operations, which is slightly faster since the name
+ does not have to be resolved on each call.</p>
+ <p>If the table is deleted, the <c>tid()</c> will be invalid even if
+ another named table is created with the same name.</p>
+ </desc>
+ </func>
</funcs>
</erlref>
diff --git a/lib/stdlib/doc/src/io.xml b/lib/stdlib/doc/src/io.xml
index 72c774e6ef..f1037ec76b 100644
--- a/lib/stdlib/doc/src/io.xml
+++ b/lib/stdlib/doc/src/io.xml
@@ -137,11 +137,11 @@
Hello world!
ok</pre>
<p>The general format of a control sequence is <c>~F.P.PadModC</c>.</p>
- <p>Character <c>C</c> determines the type of control sequence
- to be used, <c>F</c> and <c>P</c> are optional numeric
- arguments. If <c>F</c>, <c>P</c>, or <c>Pad</c> is <c>*</c>,
- the next argument in <c>Data</c> is used as the numeric value
- of <c>F</c> or <c>P</c>.</p>
+ <p>The character <c>C</c> determines the type of control sequence
+ to be used. It is the only required field. All of <c>F</c>,
+ <c>P</c>, <c>Pad</c>, and <c>Mod</c> are optional. For example,
+ to use a <c>#</c> for <c>Pad</c> but use the default values for
+ <c>F</c> and <c>P</c>, you can write <c>~..#C</c>.</p>
<list type="bulleted">
<item>
<p><c>F</c> is the <c>field width</c> of the printed argument. A
@@ -167,13 +167,26 @@ ok</pre>
The default padding character is <c>' '</c> (space).</p>
</item>
<item>
- <p><c>Mod</c> is the control sequence modifier. It is either a
- single character (<c>t</c>, for Unicode
- translation, and <c>l</c>, for stopping <c>p</c> and
- <c>P</c> from detecting printable characters)
- that changes the interpretation of <c>Data</c>.</p>
+ <p><c>Mod</c> is the control sequence modifier. This is
+ one or more characters that change the interpretation of
+ <c>Data</c>. The current modifiers are <c>t</c>, for Unicode
+ translation, and <c>l</c>, for stopping <c>p</c> and <c>P</c>
+ from detecting printable characters.</p>
</item>
</list>
+ <p>If <c>F</c>, <c>P</c>, or <c>Pad</c> is a <c>*</c> character,
+ the next argument in <c>Data</c> is used as the value.
+ For example:</p>
+ <pre>
+1> <input>io:fwrite("~*.*.0f~n",[9, 5, 3.14159265]).</input>
+003.14159
+ok</pre>
+ <p>To use a literal <c>*</c> character as <c>Pad</c>, it must be
+ passed as an argument:</p>
+ <pre>
+2> <input>io:fwrite("~*.*.*f~n",[9, 5, $*, 3.14159265]).</input>
+**3.14159
+ok</pre>
<p><em>Available control sequences:</em></p>
<taglist>
<tag><c>~</c></tag>
@@ -277,10 +290,9 @@ ok
<c>~w</c>, but breaks terms whose printed representation
is longer than one line into many lines and indents each
line sensibly. Left-justification is not supported.
- It also tries to detect lists of
- printable characters and to output these as strings. The
- Unicode translation modifier is used for determining
- what characters are printable, for example:</p>
+ It also tries to detect flat lists of
+ printable characters and output these as strings.
+ For example:</p>
<pre>
1> <input>T = [{attributes,[[{id,age,1.50000},{mode,explicit},</input>
<input>{typename,"INTEGER"}], [{id,cho},{mode,explicit},{typename,'Cho'}]]},</input>
@@ -302,7 +314,7 @@ ok
{mode,implicit}]
ok</pre>
<p>The field width specifies the maximum line length.
- Defaults to 80. The precision specifies the initial
+ It defaults to 80. The precision specifies the initial
indentation of the term. It defaults to the number of
characters printed on this line in the <em>same</em> call to
<seealso marker="#write/1"><c>write/1</c></seealso> or
@@ -332,18 +344,53 @@ ok
[{a,[97]},
{b,[98]}]
ok</pre>
- <p>Binaries that look like UTF-8 encoded strings are
- output with the string syntax if the Unicode translation
- modifier is specified:</p>
+ <p>The Unicode translation modifier <c>t</c> specifies how to treat
+ characters outside the Latin-1 range of codepoints, in
+ atoms, strings, and binaries. For example, printing an atom
+ containing a character &gt; 255:</p>
+ <pre>
+8> <input>io:fwrite("~p~n",[list_to_atom([1024])]).</input>
+'\x{400}'
+ok
+9> <input>io:fwrite("~tp~n",[list_to_atom([1024])]).</input>
+'Ѐ'
+ok</pre>
+ <p>By default, Erlang only detects lists of characters
+ in the Latin-1 range as strings, but the <c>+pc unicode</c>
+ flag can be used to change this (see <seealso
+ marker="#printable_range/0">
+ <c>printable_range/0</c></seealso> for details). For example:</p>
+ <pre>
+10> <input>io:fwrite("~p~n",[[214]]).</input>
+"Ö"
+ok
+11> <input>io:fwrite("~p~n",[[1024]]).</input>
+[1024]
+ok
+12> <input>io:fwrite("~tp~n",[[1024]]).</input>
+[1024]
+ok
+</pre>
+ <p>but if Erlang was started with <c>+pc unicode</c>:</p>
<pre>
-9> <input>io:fwrite("~p~n",[[1024]]).</input>
+13> <input>io:fwrite("~p~n",[[1024]]).</input>
[1024]
-10> <input>io:fwrite("~tp~n",[[1024]]).</input>
-"\x{400}"
-11> <input>io:fwrite("~tp~n", [&lt;&lt;128,128&gt;&gt;]).</input>
+ok
+14> <input>io:fwrite("~tp~n",[[1024]]).</input>
+"Ѐ"
+ok</pre>
+ <p>Similarly, binaries that look like UTF-8 encoded strings
+ are output with the binary string syntax if the <c>t</c>
+ modifier is specified:</p>
+ <pre>
+15> <input>io:fwrite("~p~n", [&lt;&lt;208,128&gt;&gt;]).</input>
+&lt;&lt;208,128&gt;&gt;
+ok
+16> <input>io:fwrite("~tp~n", [&lt;&lt;208,128&gt;&gt;]).</input>
+&lt;&lt;"Ѐ"/utf8&gt;&gt;
+ok
+17> <input>io:fwrite("~tp~n", [&lt;&lt;128,128&gt;&gt;]).</input>
&lt;&lt;128,128&gt;&gt;
-12> <input>io:fwrite("~tp~n", [&lt;&lt;208,128&gt;&gt;]).</input>
-&lt;&lt;"\x{400}"/utf8&gt;&gt;
ok</pre>
</item>
<tag><c>W</c></tag>
diff --git a/lib/stdlib/doc/src/lists.xml b/lib/stdlib/doc/src/lists.xml
index 7efafedc82..c3d5d7e07a 100644
--- a/lib/stdlib/doc/src/lists.xml
+++ b/lib/stdlib/doc/src/lists.xml
@@ -4,7 +4,7 @@
<erlref>
<header>
<copyright>
- <year>1996</year><year>2017</year>
+ <year>1996</year><year>2018</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -771,6 +771,18 @@ length(lists:seq(From, To, Incr)) =:= (To - From + Incr) div Incr</code>
</func>
<func>
+ <name name="search" arity="2"/>
+ <fsummary>Find the first element that satisfies a predicate.</fsummary>
+ <desc>
+ <p>If there is a <c><anno>Value</anno></c> in <c><anno>List</anno></c>
+ such that <c><anno>Pred</anno>(<anno>Value</anno>)</c> returns
+ <c>true</c>, returns <c>{value, <anno>Value</anno>}</c>
+ for the first such <c><anno>Value</anno></c>,
+ otherwise returns <c>false</c>.</p>
+ </desc>
+ </func>
+
+ <func>
<name name="splitwith" arity="2"/>
<fsummary>Split a list into two lists based on a predicate.</fsummary>
<desc>
diff --git a/lib/stdlib/doc/src/notes.xml b/lib/stdlib/doc/src/notes.xml
index b61e5b9b9e..e26c4aba74 100644
--- a/lib/stdlib/doc/src/notes.xml
+++ b/lib/stdlib/doc/src/notes.xml
@@ -31,6 +31,62 @@
</header>
<p>This document describes the changes made to the STDLIB application.</p>
+<section><title>STDLIB 3.4.5</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ The <c>Module:init/1</c> function in <c>gen_statem</c>
+ may return an actions list containing any action, but an
+ erroneous check only allowed state enter actions so e.g
+ <c>{next_event,internal,event}</c> caused a server crash.
+ This bug has been fixed.</p>
+ <p>
+ Own Id: OTP-13995</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>STDLIB 3.4.4</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p> Correct <c>filelib:find_source()</c> and
+ <c>filelib:find_file()</c> to by default also search one
+ level below <c>src</c>. This is in accordance with the
+ Design Principles which states that an application can
+ have Erlang source files one level below the <c>src</c>
+ directory. </p>
+ <p>
+ Own Id: OTP-14832 Aux Id: ERL-527 </p>
+ </item>
+ <item>
+ <p> The contract of <c>erl_tar:table/2</c> is corrected.
+ </p>
+ <p>
+ Own Id: OTP-14860 Aux Id: PR 1670 </p>
+ </item>
+ <item>
+ <p> Correct a few contracts. </p>
+ <p>
+ Own Id: OTP-14889</p>
+ </item>
+ <item>
+ <p>
+ Fix string:prefix/2 to handle an empty string as second
+ argument.</p>
+ <p>
+ Own Id: OTP-14942 Aux Id: PR-1702 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>STDLIB 3.4.3</title>
<section><title>Fixed Bugs and Malfunctions</title>
diff --git a/lib/stdlib/doc/src/timer.xml b/lib/stdlib/doc/src/timer.xml
index fcaccdb2cb..350847bf7d 100644
--- a/lib/stdlib/doc/src/timer.xml
+++ b/lib/stdlib/doc/src/timer.xml
@@ -270,7 +270,7 @@
<item>
<p>Evaluates <c>apply(<anno>Module</anno>, <anno>Function</anno>,
<anno>Arguments</anno>)</c> and measures the elapsed real time as
- reported by <seealso marker="os:timestamp/0">
+ reported by <seealso marker="kernel:os#timestamp/0">
<c>os:timestamp/0</c></seealso>.</p>
<p>Returns <c>{<anno>Time</anno>, <anno>Value</anno>}</c>, where
<c><anno>Time</anno></c> is the elapsed real time in
diff --git a/lib/stdlib/doc/src/uri_string.xml b/lib/stdlib/doc/src/uri_string.xml
index 21f470e763..88d4600611 100644
--- a/lib/stdlib/doc/src/uri_string.xml
+++ b/lib/stdlib/doc/src/uri_string.xml
@@ -4,7 +4,7 @@
<erlref>
<header>
<copyright>
- <year>2017</year><year>2017</year>
+ <year>2017</year><year>2018</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -24,7 +24,7 @@
<title>uri_string</title>
<prepared>Péter Dimitrov</prepared>
<docno>1</docno>
- <date>2017-10-24</date>
+ <date>2018-02-07</date>
<rev>A</rev>
</header>
<module>uri_string</module>
@@ -32,7 +32,11 @@
<description>
<p>This module contains functions for parsing and handling URIs
(<url href="https://www.ietf.org/rfc/rfc3986.txt">RFC 3986</url>) and
- form-urlencoded query strings (<url href="https://www.w3.org/TR/html5/forms.html">HTML5</url>).
+ form-urlencoded query strings (<url href="https://www.w3.org/TR/html52/">HTML 5.2</url>).
+ </p>
+ <p>
+ Parsing and serializing non-UTF-8 form-urlencoded query strings are also supported
+ (<url href="https://www.w3.org/TR/html50/">HTML 5.0</url>).
</p>
<p>A URI is an identifier consisting of a sequence of characters matching the syntax
rule named <em>URI</em> in <url href="https://www.ietf.org/rfc/rfc3986.txt">RFC 3986</url>.
@@ -70,7 +74,8 @@
<seealso marker="#transcode/2"><c>transcode/2</c></seealso>
</item>
<item>Transforming URIs into a normalized form<br></br>
- <seealso marker="#normalize/1"><c>normalize/1</c></seealso>
+ <seealso marker="#normalize/1"><c>normalize/1</c></seealso><br></br>
+ <seealso marker="#normalize/2"><c>normalize/2</c></seealso>
</item>
<item>Composing form-urlencoded query strings from a list of key-value pairs<br></br>
<seealso marker="#compose_query/1"><c>compose_query/1</c></seealso><br></br>
@@ -151,8 +156,10 @@
<p>Composes a form-urlencoded <c><anno>QueryString</anno></c> based on a
<c><anno>QueryList</anno></c>, a list of non-percent-encoded key-value pairs.
Form-urlencoding is defined in section
- 4.10.22.6 of the <url href="https://www.w3.org/TR/html5/forms.html">HTML5</url>
- specification.
+ 4.10.21.6 of the <url href="https://www.w3.org/TR/html52/">HTML 5.2</url>
+ specification and in section 4.10.22.6 of the
+ <url href="https://www.w3.org/TR/html50/">HTML 5.0</url> specification for
+ non-UTF-8 encodings.
</p>
<p>See also the opposite operation <seealso marker="#dissect_query/1">
<c>dissect_query/1</c></seealso>.
@@ -209,12 +216,11 @@
<p>Dissects an urlencoded <c><anno>QueryString</anno></c> and returns a
<c><anno>QueryList</anno></c>, a list of non-percent-encoded key-value pairs.
Form-urlencoding is defined in section
- 4.10.22.6 of the <url href="https://www.w3.org/TR/html5/forms.html">HTML5</url>
- specification.
+ 4.10.21.6 of the <url href="https://www.w3.org/TR/html52/">HTML 5.2</url>
+ specification and in section 4.10.22.6 of the
+ <url href="https://www.w3.org/TR/html50/">HTML 5.0</url> specification for
+ non-UTF-8 encodings.
</p>
- <p>It is not as strict for its input as the decoding algorithm defined by
- <url href="https://www.w3.org/TR/html5/forms.html">HTML5</url>
- and accepts all unicode characters.</p>
<p>See also the opposite operation <seealso marker="#compose_query/1">
<c>compose_query/1</c></seealso>.
</p>
@@ -233,7 +239,7 @@
<name name="normalize" arity="1"/>
<fsummary>Syntax-based normalization.</fsummary>
<desc>
- <p>Transforms <c><anno>URIString</anno></c> into a normalized form
+ <p>Transforms an <c><anno>URI</anno></c> into a normalized form
using Syntax-Based Normalization as defined by
<url href="https://www.ietf.org/rfc/rfc3986.txt">RFC 3986</url>.</p>
<p>This function implements case normalization, percent-encoding
@@ -247,6 +253,33 @@
<![CDATA[<<"mid/6">>]]>
3> uri_string:normalize("http://localhost:80").
"https://localhost/"
+4> <input>uri_string:normalize(#{scheme => "http",port => 80,path => "/a/b/c/./../../g",</input>
+4> host => "localhost-örebro"}).
+"http://localhost-%C3%B6rebro/a/g"
+ </pre>
+ </desc>
+ </func>
+
+ <func>
+ <name name="normalize" arity="2"/>
+ <fsummary>Syntax-based normalization.</fsummary>
+ <desc>
+ <p>Same as <c>normalize/1</c> but with an additional
+ <c><anno>Options</anno></c> parameter, that controls if the normalized URI
+ shall be returned as an uri_map().
+ There is one supported option: <c>return_map</c>.
+ </p>
+ <p><em>Example:</em></p>
+ <pre>
+1> <input>uri_string:normalize("/a/b/c/./../../g", [return_map]).</input>
+#{path => "/a/g"}
+2> <![CDATA[uri_string:normalize(<<"mid/content=5/../6">>, [return_map]).]]>
+<![CDATA[#{path => <<"mid/6">>}]]>
+3> uri_string:normalize("http://localhost:80", [return_map]).
+#{scheme => "http",path => "/",host => "localhost"}
+4> <input>uri_string:normalize(#{scheme => "http",port => 80,path => "/a/b/c/./../../g",</input>
+4> host => "localhost-örebro"}, [return_map]).
+#{scheme => "http",path => "/a/g",host => "localhost-örebro"}
</pre>
</desc>
</func>
diff --git a/lib/stdlib/include/assert.hrl b/lib/stdlib/include/assert.hrl
index 2fbaeba0b2..2ec89e7d8a 100644
--- a/lib/stdlib/include/assert.hrl
+++ b/lib/stdlib/include/assert.hrl
@@ -309,7 +309,7 @@
{unexpected_success, __V}]})
catch
Class:Term -> ok;
- __C:__T ->
+ __C:__T:__S ->
erlang:error({assertException,
[{module, ?MODULE},
{line, ?LINE},
@@ -318,8 +318,7 @@
"{ "++(??Class)++" , "++(??Term)
++" , [...] }"},
{unexpected_exception,
- {__C, __T,
- erlang:get_stacktrace()}}]})
+ {__C, __T, __S}}]})
end
end)())
end).
@@ -338,7 +337,7 @@
{unexpected_success, __V}]})
catch
Class:Term -> ok;
- __C:__T ->
+ __C:__T:__S ->
erlang:error({assertException,
[{module, ?MODULE},
{line, ?LINE},
@@ -348,8 +347,7 @@
"{ "++(??Class)++" , "++(??Term)
++" , [...] }"},
{unexpected_exception,
- {__C, __T,
- erlang:get_stacktrace()}}]})
+ {__C, __T, __S}}]})
end
end)())
end).
@@ -378,7 +376,7 @@
try (Expr) of
_ -> ok
catch
- __C:__T ->
+ __C:__T:__S ->
case __C of
Class ->
case __T of
@@ -391,9 +389,7 @@
"{ "++(??Class)++" , "
++(??Term)++" , [...] }"},
{unexpected_exception,
- {__C, __T,
- erlang:get_stacktrace()
- }}]});
+ {__C, __T, __S}}]});
_ -> ok
end;
_ -> ok
@@ -407,7 +403,7 @@
try (Expr) of
_ -> ok
catch
- __C:__T ->
+ __C:__T:__S ->
case __C of
Class ->
case __T of
@@ -421,9 +417,7 @@
"{ "++(??Class)++" , "
++(??Term)++" , [...] }"},
{unexpected_exception,
- {__C, __T,
- erlang:get_stacktrace()
- }}]});
+ {__C, __T, __S}}]});
_ -> ok
end;
_ -> ok
diff --git a/lib/stdlib/src/beam_lib.erl b/lib/stdlib/src/beam_lib.erl
index 06c15fceda..24349c74e8 100644
--- a/lib/stdlib/src/beam_lib.erl
+++ b/lib/stdlib/src/beam_lib.erl
@@ -148,7 +148,8 @@ chunks(File, Chunks, Options) ->
try read_chunk_data(File, Chunks, Options)
catch Error -> Error end.
--spec all_chunks(beam()) -> {'ok', 'beam_lib', [{chunkid(), dataB()}]}.
+-spec all_chunks(beam()) ->
+ {'ok', 'beam_lib', [{chunkid(), dataB()}]} | {'error', 'beam_lib', info_rsn()}.
all_chunks(File) ->
read_all_chunks(File).
diff --git a/lib/stdlib/src/binary.erl b/lib/stdlib/src/binary.erl
index 6a64133b45..7d0e42489e 100644
--- a/lib/stdlib/src/binary.erl
+++ b/lib/stdlib/src/binary.erl
@@ -47,23 +47,39 @@ at(_, _) ->
-spec bin_to_list(Subject) -> [byte()] when
Subject :: binary().
-bin_to_list(_) ->
- erlang:nif_error(undef).
+bin_to_list(Subject) ->
+ binary_to_list(Subject).
-spec bin_to_list(Subject, PosLen) -> [byte()] when
Subject :: binary(),
PosLen :: part().
-bin_to_list(_, _) ->
- erlang:nif_error(undef).
+bin_to_list(Subject, {Pos, Len}) ->
+ bin_to_list(Subject, Pos, Len);
+bin_to_list(_Subject, _BadArg) ->
+ erlang:error(badarg).
-spec bin_to_list(Subject, Pos, Len) -> [byte()] when
Subject :: binary(),
Pos :: non_neg_integer(),
Len :: integer().
-bin_to_list(_, _, _) ->
- erlang:nif_error(undef).
+bin_to_list(Subject, Pos, Len) when not is_binary(Subject);
+ not is_integer(Pos);
+ not is_integer(Len) ->
+ %% binary_to_list/3 allows bitstrings as long as the slice fits, and we
+ %% want to badarg when Pos/Len aren't integers instead of raising badarith
+ %% when adjusting args for binary_to_list/3.
+ erlang:error(badarg);
+bin_to_list(Subject, Pos, 0) when Pos >= 0, Pos =< byte_size(Subject) ->
+ %% binary_to_list/3 doesn't handle this case.
+ [];
+bin_to_list(_Subject, _Pos, 0) ->
+ erlang:error(badarg);
+bin_to_list(Subject, Pos, Len) when Len < 0 ->
+ bin_to_list(Subject, Pos + Len, -Len);
+bin_to_list(Subject, Pos, Len) when Len > 0 ->
+ binary_to_list(Subject, Pos + 1, Pos + Len).
-spec compile_pattern(Pattern) -> cp() when
Pattern :: binary() | [binary()].
diff --git a/lib/stdlib/src/c.erl b/lib/stdlib/src/c.erl
index 9a447af5b7..13f78841aa 100644
--- a/lib/stdlib/src/c.erl
+++ b/lib/stdlib/src/c.erl
@@ -564,7 +564,7 @@ display_info(Pid) ->
Other
end,
Reds = fetch(reductions, Info),
- LM = length(fetch(messages, Info)),
+ LM = fetch(message_queue_len, Info),
HS = fetch(heap_size, Info),
SS = fetch(stack_size, Info),
iformat(w(Pid), mfa_string(Call),
@@ -886,7 +886,7 @@ portinfo(Id) ->
procline(Name, Info, Pid) ->
Call = initial_call(Info),
Reds = fetch(reductions, Info),
- LM = length(fetch(messages, Info)),
+ LM = fetch(message_queue_len, Info),
procformat(io_lib:format("~tw",[Name]),
io_lib:format("~w",[Pid]),
io_lib:format("~ts",[mfa_string(Call)]),
@@ -1034,8 +1034,8 @@ appcall(App, M, F, Args) ->
try
apply(M, F, Args)
catch
- error:undef ->
- case erlang:get_stacktrace() of
+ error:undef:S ->
+ case S of
[{M,F,Args,_}|_] ->
Arity = length(Args),
io:format("Call to ~w:~w/~w in application ~w failed.\n",
diff --git a/lib/stdlib/src/dets.erl b/lib/stdlib/src/dets.erl
index 4e3fe0e5c1..e1a36abc70 100644
--- a/lib/stdlib/src/dets.erl
+++ b/lib/stdlib/src/dets.erl
@@ -1288,8 +1288,8 @@ init(Parent, Server) ->
catch
exit:normal ->
exit(normal);
- _:Bad ->
- bug_found(no_name, Op, Bad, From),
+ _:Bad:Stacktrace ->
+ bug_found(no_name, Op, Bad, Stacktrace, From),
exit(Bad) % give up
end
end.
@@ -1371,8 +1371,8 @@ do_apply_op(Op, From, Head, N) ->
catch
exit:normal ->
exit(normal);
- _:Bad ->
- bug_found(Head#head.name, Op, Bad, From),
+ _:Bad:Stacktrace ->
+ bug_found(Head#head.name, Op, Bad, Stacktrace, From),
open_file_loop(Head, N)
end.
@@ -1581,7 +1581,7 @@ apply_op(Op, From, Head, N) ->
ok
end.
-bug_found(Name, Op, Bad, From) ->
+bug_found(Name, Op, Bad, Stacktrace, From) ->
case dets_utils:debug_mode() of
true ->
%% If stream_op/5 found more requests, this is not
@@ -1590,7 +1590,7 @@ bug_found(Name, Op, Bad, From) ->
("** dets: Bug was found when accessing table ~tw,~n"
"** dets: operation was ~tp and reply was ~tw.~n"
"** dets: Stacktrace: ~tw~n",
- [Name, Op, Bad, erlang:get_stacktrace()]);
+ [Name, Op, Bad, Stacktrace]);
false ->
error_logger:format
("** dets: Bug was found when accessing table ~tw~n",
diff --git a/lib/stdlib/src/dets_utils.erl b/lib/stdlib/src/dets_utils.erl
index 17f55ebdc2..4c8ea9e82b 100644
--- a/lib/stdlib/src/dets_utils.erl
+++ b/lib/stdlib/src/dets_utils.erl
@@ -377,7 +377,8 @@ corrupt_reason(Head, Reason0) ->
no_disk_map ->
Reason0;
DM ->
- ST = erlang:get_stacktrace(),
+ {current_stacktrace, ST} =
+ erlang:process_info(self(), current_stacktrace),
PD = get(),
{Reason0, ST, PD, DM}
end,
diff --git a/lib/stdlib/src/epp.erl b/lib/stdlib/src/epp.erl
index 00e6a10d8a..77cc88eb08 100644
--- a/lib/stdlib/src/epp.erl
+++ b/lib/stdlib/src/epp.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2017. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2018. 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.
@@ -1197,21 +1197,21 @@ skip_else(_Else, From, St, Sis) ->
%% macro_expansion(Tokens, Anno)
%% Extract the macro parameters and the expansion from a macro definition.
-macro_pars([{')',_Lp}, {',',Ld}|Ex], Args) ->
- {ok, {lists:reverse(Args), macro_expansion(Ex, Ld)}};
-macro_pars([{var,_,Name}, {')',_Lp}, {',',Ld}|Ex], Args) ->
+macro_pars([{')',_Lp}, {',',_Ld}=Comma|Ex], Args) ->
+ {ok, {lists:reverse(Args), macro_expansion(Ex, Comma)}};
+macro_pars([{var,_,Name}, {')',_Lp}, {',',_Ld}=Comma|Ex], Args) ->
false = lists:member(Name, Args), %Prolog is nice
- {ok, {lists:reverse([Name|Args]), macro_expansion(Ex, Ld)}};
+ {ok, {lists:reverse([Name|Args]), macro_expansion(Ex, Comma)}};
macro_pars([{var,_L,Name}, {',',_}|Ts], Args) ->
false = lists:member(Name, Args),
macro_pars(Ts, [Name|Args]).
-macro_expansion([{')',_Lp},{dot,_Ld}], _Anno0) -> [];
-macro_expansion([{dot,_}=Dot], _Anno0) ->
+macro_expansion([{')',_Lp},{dot,_Ld}], _T0) -> [];
+macro_expansion([{dot,_}=Dot], _T0) ->
throw({error,loc(Dot),missing_parenthesis});
-macro_expansion([T|Ts], _Anno0) ->
+macro_expansion([T|Ts], _T0) ->
[T|macro_expansion(Ts, T)];
-macro_expansion([], Anno0) -> throw({error,loc(Anno0),premature_end}).
+macro_expansion([], T0) -> throw({error,loc(T0),premature_end}).
%% expand_macros(Tokens, St)
%% expand_macro(Tokens, MacroToken, RestTokens)
diff --git a/lib/stdlib/src/erl_lint.erl b/lib/stdlib/src/erl_lint.erl
index 1930c462e8..9a62d21d34 100644
--- a/lib/stdlib/src/erl_lint.erl
+++ b/lib/stdlib/src/erl_lint.erl
@@ -3971,6 +3971,8 @@ extract_sequence(3, [$.,_|Fmt], Need) ->
extract_sequence(4, Fmt, Need);
extract_sequence(3, Fmt, Need) ->
extract_sequence(4, Fmt, Need);
+extract_sequence(4, [$t, $l | Fmt], Need) ->
+ extract_sequence(4, [$l, $t | Fmt], Need);
extract_sequence(4, [$t, $c | Fmt], Need) ->
extract_sequence(5, [$c|Fmt], Need);
extract_sequence(4, [$t, $s | Fmt], Need) ->
@@ -3987,8 +3989,14 @@ extract_sequence(4, [$t, C | _Fmt], _Need) ->
{error,"invalid control ~t" ++ [C]};
extract_sequence(4, [$l, $p | Fmt], Need) ->
extract_sequence(5, [$p|Fmt], Need);
+extract_sequence(4, [$l, $t, $p | Fmt], Need) ->
+ extract_sequence(5, [$p|Fmt], Need);
extract_sequence(4, [$l, $P | Fmt], Need) ->
extract_sequence(5, [$P|Fmt], Need);
+extract_sequence(4, [$l, $t, $P | Fmt], Need) ->
+ extract_sequence(5, [$P|Fmt], Need);
+extract_sequence(4, [$l, $t, C | _Fmt], _Need) ->
+ {error,"invalid control ~lt" ++ [C]};
extract_sequence(4, [$l, C | _Fmt], _Need) ->
{error,"invalid control ~l" ++ [C]};
extract_sequence(4, Fmt, Need) ->
diff --git a/lib/stdlib/src/erl_tar.erl b/lib/stdlib/src/erl_tar.erl
index 5ee584d612..d8b8f466b1 100644
--- a/lib/stdlib/src/erl_tar.erl
+++ b/lib/stdlib/src/erl_tar.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2017. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2018. 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.
@@ -457,26 +457,61 @@ add(Reader, NameOrBin, NameInArchive, Options)
do_add(#reader{access=write}=Reader, Name, NameInArchive, Options)
when is_list(NameInArchive), is_list(Options) ->
- RF = fun(F) -> file:read_link_info(F, [{time, posix}]) end,
+ RF = apply_file_info_opts_fun(Options, read_link_info),
Opts = #add_opts{read_info=RF},
- add1(Reader, Name, NameInArchive, add_opts(Options, Opts));
+ add1(Reader, Name, NameInArchive, add_opts(Options, Options, Opts));
do_add(#reader{access=read},_,_,_) ->
{error, eacces};
do_add(Reader,_,_,_) ->
{error, {badarg, Reader}}.
-add_opts([dereference|T], Opts) ->
- RF = fun(F) -> file:read_file_info(F, [{time, posix}]) end,
- add_opts(T, Opts#add_opts{read_info=RF});
-add_opts([verbose|T], Opts) ->
- add_opts(T, Opts#add_opts{verbose=true});
-add_opts([{chunks,N}|T], Opts) ->
- add_opts(T, Opts#add_opts{chunk_size=N});
-add_opts([_|T], Opts) ->
- add_opts(T, Opts);
-add_opts([], Opts) ->
+add_opts([dereference|T], AllOptions, Opts) ->
+ RF = apply_file_info_opts_fun(AllOptions, read_file_info),
+ add_opts(T, AllOptions, Opts#add_opts{read_info=RF});
+add_opts([verbose|T], AllOptions, Opts) ->
+ add_opts(T, AllOptions, Opts#add_opts{verbose=true});
+add_opts([{chunks,N}|T], AllOptions, Opts) ->
+ add_opts(T, AllOptions, Opts#add_opts{chunk_size=N});
+add_opts([{atime,Value}|T], AllOptions, Opts) ->
+ add_opts(T, AllOptions, Opts#add_opts{atime=Value});
+add_opts([{mtime,Value}|T], AllOptions, Opts) ->
+ add_opts(T, AllOptions, Opts#add_opts{mtime=Value});
+add_opts([{ctime,Value}|T], AllOptions, Opts) ->
+ add_opts(T, AllOptions, Opts#add_opts{ctime=Value});
+add_opts([{uid,Value}|T], AllOptions, Opts) ->
+ add_opts(T, AllOptions, Opts#add_opts{uid=Value});
+add_opts([{gid,Value}|T], AllOptions, Opts) ->
+ add_opts(T, AllOptions, Opts#add_opts{gid=Value});
+add_opts([_|T], AllOptions, Opts) ->
+ add_opts(T, AllOptions, Opts);
+add_opts([], _AllOptions, Opts) ->
Opts.
+apply_file_info_opts(Opts, {ok, FileInfo}) ->
+ {ok, do_apply_file_info_opts(Opts, FileInfo)};
+apply_file_info_opts(_Opts, Other) ->
+ Other.
+
+do_apply_file_info_opts([{atime,Value}|T], FileInfo) ->
+ do_apply_file_info_opts(T, FileInfo#file_info{atime=Value});
+do_apply_file_info_opts([{mtime,Value}|T], FileInfo) ->
+ do_apply_file_info_opts(T, FileInfo#file_info{mtime=Value});
+do_apply_file_info_opts([{ctime,Value}|T], FileInfo) ->
+ do_apply_file_info_opts(T, FileInfo#file_info{ctime=Value});
+do_apply_file_info_opts([{uid,Value}|T], FileInfo) ->
+ do_apply_file_info_opts(T, FileInfo#file_info{uid=Value});
+do_apply_file_info_opts([{gid,Value}|T], FileInfo) ->
+ do_apply_file_info_opts(T, FileInfo#file_info{gid=Value});
+do_apply_file_info_opts([_|T], FileInfo) ->
+ do_apply_file_info_opts(T, FileInfo);
+do_apply_file_info_opts([], FileInfo) ->
+ FileInfo.
+
+apply_file_info_opts_fun(Options, InfoFunction) ->
+ fun(F) ->
+ apply_file_info_opts(Options, file:InfoFunction(F, [{time, posix}]))
+ end.
+
add1(#reader{}=Reader, Name, NameInArchive, #add_opts{read_info=ReadInfo}=Opts)
when is_list(Name) ->
Res = case ReadInfo(Name) of
@@ -515,9 +550,11 @@ add1(Reader, Bin, NameInArchive, Opts) when is_binary(Bin) ->
name = NameInArchive,
size = byte_size(Bin),
typeflag = ?TYPE_REGULAR,
- atime = Now,
- mtime = Now,
- ctime = Now,
+ atime = add_opts_time(Opts#add_opts.atime, Now),
+ mtime = add_opts_time(Opts#add_opts.mtime, Now),
+ ctime = add_opts_time(Opts#add_opts.ctime, Now),
+ uid = Opts#add_opts.uid,
+ gid = Opts#add_opts.gid,
mode = 8#100644},
{ok, Reader2} = add_header(Reader, Header, Opts),
Padding = skip_padding(byte_size(Bin)),
@@ -527,6 +564,9 @@ add1(Reader, Bin, NameInArchive, Opts) when is_binary(Bin) ->
{error, Reason} -> {error, {NameInArchive, Reason}}
end.
+add_opts_time(undefined, Now) -> Now;
+add_opts_time(Time, _Now) -> Time.
+
add_directory(Reader, DirName, NameInArchive, Info, Opts) ->
case file:list_dir(DirName) of
{ok, []} ->
@@ -1650,8 +1690,12 @@ write_file(Name, Bin) ->
case file:write_file(Name, Bin) of
ok -> ok;
{error,enoent} ->
- ok = make_dirs(Name, file),
- write_file(Name, Bin);
+ case make_dirs(Name, file) of
+ ok ->
+ write_file(Name, Bin);
+ {error,Reason} ->
+ throw({error, Reason})
+ end;
{error,Reason} ->
throw({error, Reason})
end.
diff --git a/lib/stdlib/src/erl_tar.hrl b/lib/stdlib/src/erl_tar.hrl
index cff0c2f500..5d6cecbb66 100644
--- a/lib/stdlib/src/erl_tar.hrl
+++ b/lib/stdlib/src/erl_tar.hrl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2017. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2018. 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.
@@ -21,7 +21,12 @@
-record(add_opts, {
read_info, %% Fun to use for read file/link info.
chunk_size = 0, %% For file reading when sending to sftp. 0=do not chunk
- verbose = false}). %% Verbose on/off.
+ verbose = false, %% Verbose on/off.
+ atime = undefined,
+ mtime = undefined,
+ ctime = undefined,
+ uid = 0,
+ gid = 0}).
-type add_opts() :: #add_opts{}.
%% Options used when reading a tar archive.
@@ -36,7 +41,12 @@
-type add_opt() :: dereference |
verbose |
- {chunks, pos_integer()}.
+ {chunks, pos_integer()} |
+ {atime, non_neg_integer()} |
+ {mtime, non_neg_integer()} |
+ {ctime, non_neg_integer()} |
+ {uid, non_neg_integer()} |
+ {gid, non_neg_integer()}.
-type extract_opt() :: {cwd, string()} |
{files, [string()]} |
diff --git a/lib/stdlib/src/escript.erl b/lib/stdlib/src/escript.erl
index 132f8efbbe..beea9927d2 100644
--- a/lib/stdlib/src/escript.erl
+++ b/lib/stdlib/src/escript.erl
@@ -283,8 +283,7 @@ start(EscriptOptions) ->
throw:Str ->
io:format("escript: ~ts\n", [Str]),
my_halt(127);
- _:Reason ->
- Stk = erlang:get_stacktrace(),
+ _:Reason:Stk ->
io:format("escript: Internal error: ~tp\n", [Reason]),
io:format("~tp\n", [Stk]),
my_halt(127)
@@ -759,8 +758,8 @@ run(Module, Args) ->
Module:main(Args),
my_halt(0)
catch
- Class:Reason ->
- fatal(format_exception(Class, Reason))
+ Class:Reason:StackTrace ->
+ fatal(format_exception(Class, Reason, StackTrace))
end.
-spec interpret(_, _, _, _) -> no_return().
@@ -793,8 +792,8 @@ interpret(Forms, HasRecs, File, Args) ->
end}),
my_halt(0)
catch
- Class:Reason ->
- fatal(format_exception(Class, Reason))
+ Class:Reason:StackTrace ->
+ fatal(format_exception(Class, Reason, StackTrace))
end.
report_errors(Errors) ->
@@ -873,7 +872,7 @@ eval_exprs([E|Es], Bs0, Lf, Ef, RBs) ->
{value,_V,Bs} = erl_eval:expr(E, Bs0, Lf, Ef, RBs1),
eval_exprs(Es, Bs, Lf, Ef, RBs).
-format_exception(Class, Reason) ->
+format_exception(Class, Reason, StackTrace) ->
Enc = encoding(),
P = case Enc of
latin1 -> "P";
@@ -882,7 +881,6 @@ format_exception(Class, Reason) ->
PF = fun(Term, I) ->
io_lib:format("~." ++ integer_to_list(I) ++ P, [Term, 50])
end,
- StackTrace = erlang:get_stacktrace(),
StackFun = fun(M, _F, _A) -> (M =:= erl_eval) or (M =:= ?MODULE) end,
lib:format_exception(1, Class, Reason, StackTrace, StackFun, PF, Enc).
@@ -916,8 +914,8 @@ hidden_apply(App, M, F, Args) ->
try
apply(fun() -> M end(), F, Args)
catch
- error:undef ->
- case erlang:get_stacktrace() of
+ error:undef:StackTrace ->
+ case StackTrace of
[{M,F,Args,_} | _] ->
Arity = length(Args),
Text = io_lib:format("Call to ~w:~w/~w in application ~w failed.\n",
diff --git a/lib/stdlib/src/ets.erl b/lib/stdlib/src/ets.erl
index b6548626f3..6a559f0be5 100644
--- a/lib/stdlib/src/ets.erl
+++ b/lib/stdlib/src/ets.erl
@@ -73,7 +73,8 @@
select_count/2, select_delete/2, select_replace/2, select_reverse/1,
select_reverse/2, select_reverse/3, setopts/2, slot/2,
take/2,
- update_counter/3, update_counter/4, update_element/3]).
+ update_counter/3, update_counter/4, update_element/3,
+ whereis/1]).
%% internal exports
-export([internal_request_all/0]).
@@ -145,6 +146,7 @@ give_away(_, _, _) ->
InfoList :: [InfoTuple],
InfoTuple :: {compressed, boolean()}
| {heir, pid() | none}
+ | {id, tid()}
| {keypos, pos_integer()}
| {memory, non_neg_integer()}
| {name, atom()}
@@ -162,7 +164,7 @@ info(_) ->
-spec info(Tab, Item) -> Value | undefined when
Tab :: tab(),
- Item :: compressed | fixed | heir | keypos | memory
+ Item :: compressed | fixed | heir | id | keypos | memory
| name | named_table | node | owner | protection
| safe_fixed | safe_fixed_monotonic_time | size | stats | type
| write_concurrency | read_concurrency,
@@ -277,7 +279,7 @@ match_spec_compile(_) ->
erlang:nif_error(undef).
-spec match_spec_run_r(List, CompiledMatchSpec, list()) -> list() when
- List :: [tuple()],
+ List :: [term()],
CompiledMatchSpec :: comp_match_spec().
match_spec_run_r(_, _, _) ->
@@ -512,12 +514,17 @@ update_counter(_, _, _, _) ->
update_element(_, _, _) ->
erlang:nif_error(undef).
+-spec whereis(TableName) -> tid() | undefined when
+ TableName :: atom().
+whereis(_) ->
+ erlang:nif_error(undef).
+
%%% End of BIFs
-opaque comp_match_spec() :: reference().
-spec match_spec_run(List, CompiledMatchSpec) -> list() when
- List :: [tuple()],
+ List :: [term()],
CompiledMatchSpec :: comp_match_spec().
match_spec_run(List, CompiledMS) ->
@@ -882,10 +889,10 @@ tab2file(Tab, File, Options) ->
_ = disk_log:close(Name),
_ = file:delete(File),
exit(ExReason);
- error:ErReason ->
+ error:ErReason:StackTrace ->
_ = disk_log:close(Name),
_ = file:delete(File),
- erlang:raise(error,ErReason,erlang:get_stacktrace())
+ erlang:raise(error,ErReason,StackTrace)
end
catch
throw:TReason2 ->
@@ -1060,9 +1067,9 @@ file2tab(File, Opts) ->
exit:ExReason ->
ets:delete(Tab),
exit(ExReason);
- error:ErReason ->
+ error:ErReason:StackTrace ->
ets:delete(Tab),
- erlang:raise(error,ErReason,erlang:get_stacktrace())
+ erlang:raise(error,ErReason,StackTrace)
end
after
_ = disk_log:close(Name)
diff --git a/lib/stdlib/src/file_sorter.erl b/lib/stdlib/src/file_sorter.erl
index 3aeaff8dc4..7f74e71136 100644
--- a/lib/stdlib/src/file_sorter.erl
+++ b/lib/stdlib/src/file_sorter.erl
@@ -1314,9 +1314,9 @@ infun(W) ->
{cont, W#w{in = NFun}, Objs};
Error ->
error(Error, W1)
- catch Class:Reason ->
+ catch Class:Reason:Stacktrace ->
cleanup(W1),
- erlang:raise(Class, Reason, erlang:get_stacktrace())
+ erlang:raise(Class, Reason, Stacktrace)
end.
outfun(A, #w{inout_value = Val} = W) when Val =/= no_value ->
@@ -1336,9 +1336,9 @@ outfun(A, W) ->
W#w{out = NF};
Error ->
error(Error, W1)
- catch Class:Reason ->
+ catch Class:Reason:Stacktrace ->
cleanup(W1),
- erlang:raise(Class, Reason, erlang:get_stacktrace())
+ erlang:raise(Class, Reason, Stacktrace)
end.
is_keypos(Keypos) when is_integer(Keypos), Keypos > 0 ->
diff --git a/lib/stdlib/src/gen.erl b/lib/stdlib/src/gen.erl
index 0e6f49d99f..2e6223d2bb 100644
--- a/lib/stdlib/src/gen.erl
+++ b/lib/stdlib/src/gen.erl
@@ -157,52 +157,27 @@ call(Process, Label, Request, Timeout)
Fun = fun(Pid) -> do_call(Pid, Label, Request, Timeout) end,
do_for_proc(Process, Fun).
-do_call(Process, Label, Request, Timeout) ->
- try erlang:monitor(process, Process) of
- Mref ->
- %% If the monitor/2 call failed to set up a connection to a
- %% remote node, we don't want the '!' operator to attempt
- %% to set up the connection again. (If the monitor/2 call
- %% failed due to an expired timeout, '!' too would probably
- %% have to wait for the timeout to expire.) Therefore,
- %% use erlang:send/3 with the 'noconnect' option so that it
- %% will fail immediately if there is no connection to the
- %% remote node.
-
- catch erlang:send(Process, {Label, {self(), Mref}, Request},
- [noconnect]),
- receive
- {Mref, Reply} ->
- erlang:demonitor(Mref, [flush]),
- {ok, Reply};
- {'DOWN', Mref, _, _, noconnection} ->
- Node = get_node(Process),
- exit({nodedown, Node});
- {'DOWN', Mref, _, _, Reason} ->
- exit(Reason)
- after Timeout ->
- erlang:demonitor(Mref, [flush]),
- exit(timeout)
- end
- catch
- error:_ ->
- %% Node (C/Java?) is not supporting the monitor.
- %% The other possible case -- this node is not distributed
- %% -- should have been handled earlier.
- %% Do the best possible with monitor_node/2.
- %% This code may hang indefinitely if the Process
- %% does not exist. It is only used for featureweak remote nodes.
- Node = get_node(Process),
- monitor_node(Node, true),
- receive
- {nodedown, Node} ->
- monitor_node(Node, false),
- exit({nodedown, Node})
- after 0 ->
- Tag = make_ref(),
- Process ! {Label, {self(), Tag}, Request},
- wait_resp(Node, Tag, Timeout)
- end
+do_call(Process, Label, Request, Timeout) when is_atom(Process) =:= false ->
+ Mref = erlang:monitor(process, Process),
+
+ %% OTP-21:
+ %% Auto-connect is asynchronous. But we still use 'noconnect' to make sure
+ %% we send on the monitored connection, and not trigger a new auto-connect.
+ %%
+ erlang:send(Process, {Label, {self(), Mref}, Request}, [noconnect]),
+
+ receive
+ {Mref, Reply} ->
+ erlang:demonitor(Mref, [flush]),
+ {ok, Reply};
+ {'DOWN', Mref, _, _, noconnection} ->
+ Node = get_node(Process),
+ exit({nodedown, Node});
+ {'DOWN', Mref, _, _, Reason} ->
+ exit(Reason)
+ after Timeout ->
+ erlang:demonitor(Mref, [flush]),
+ exit(timeout)
end.
get_node(Process) ->
@@ -217,19 +192,6 @@ get_node(Process) ->
node(Process)
end.
-wait_resp(Node, Tag, Timeout) ->
- receive
- {Tag, Reply} ->
- monitor_node(Node, false),
- {ok, Reply};
- {nodedown, Node} ->
- monitor_node(Node, false),
- exit({nodedown, Node})
- after Timeout ->
- monitor_node(Node, false),
- exit(timeout)
- end.
-
%%
%% Send a reply to the client.
%%
diff --git a/lib/stdlib/src/gen_server.erl b/lib/stdlib/src/gen_server.erl
index ac172325b5..f29314d0a2 100644
--- a/lib/stdlib/src/gen_server.erl
+++ b/lib/stdlib/src/gen_server.erl
@@ -109,7 +109,7 @@
-define(
STACKTRACE(),
- try throw(ok) catch _ -> erlang:get_stacktrace() end).
+ element(2, erlang:process_info(self(), current_stacktrace))).
%%%=========================================================================
%%% API
@@ -369,7 +369,7 @@ init_it(Mod, Args) ->
{ok, Mod:init(Args)}
catch
throw:R -> {ok, R};
- Class:R -> {'EXIT', Class, R, erlang:get_stacktrace()}
+ Class:R:S -> {'EXIT', Class, R, S}
end.
%%%========================================================================
@@ -437,12 +437,11 @@ decode_msg(Msg, Parent, Name, State, Mod, Time, HibernateAfterTimeout, Debug, Hi
%%% Send/receive functions
%%% ---------------------------------------------------
do_send(Dest, Msg) ->
- case catch erlang:send(Dest, Msg, [noconnect]) of
- noconnect ->
- spawn(erlang, send, [Dest,Msg]);
- Other ->
- Other
- end.
+ try erlang:send(Dest, Msg)
+ catch
+ error:_ -> ok
+ end,
+ ok.
do_multi_call(Nodes, Name, Req, infinity) ->
Tag = make_ref(),
@@ -634,7 +633,7 @@ try_dispatch(Mod, Func, Msg, State) ->
catch
throw:R ->
{ok, R};
- error:undef = R when Func == handle_info ->
+ error:undef = R:Stacktrace when Func == handle_info ->
case erlang:function_exported(Mod, handle_info, 2) of
false ->
error_logger:warning_msg("** Undefined handle_info in ~p~n"
@@ -642,10 +641,10 @@ try_dispatch(Mod, Func, Msg, State) ->
[Mod, Msg]),
{ok, {noreply, State}};
true ->
- {'EXIT', error, R, erlang:get_stacktrace()}
+ {'EXIT', error, R, Stacktrace}
end;
- Class:R ->
- {'EXIT', Class, R, erlang:get_stacktrace()}
+ Class:R:Stacktrace ->
+ {'EXIT', Class, R, Stacktrace}
end.
try_handle_call(Mod, Msg, From, State) ->
@@ -654,8 +653,8 @@ try_handle_call(Mod, Msg, From, State) ->
catch
throw:R ->
{ok, R};
- Class:R ->
- {'EXIT', Class, R, erlang:get_stacktrace()}
+ Class:R:Stacktrace ->
+ {'EXIT', Class, R, Stacktrace}
end.
try_terminate(Mod, Reason, State) ->
@@ -666,8 +665,8 @@ try_terminate(Mod, Reason, State) ->
catch
throw:R ->
{ok, R};
- Class:R ->
- {'EXIT', Class, R, erlang:get_stacktrace()}
+ Class:R:Stacktrace ->
+ {'EXIT', Class, R, Stacktrace}
end;
false ->
{ok, ok}
diff --git a/lib/stdlib/src/gen_statem.erl b/lib/stdlib/src/gen_statem.erl
index 1a7736fc7e..7f5d82cc21 100644
--- a/lib/stdlib/src/gen_statem.erl
+++ b/lib/stdlib/src/gen_statem.erl
@@ -365,7 +365,7 @@ event_type(Type) ->
-define(
STACKTRACE(),
- try throw(ok) catch _ -> erlang:get_stacktrace() end).
+ element(2, erlang:process_info(self(), current_stacktrace))).
-define(not_sys_debug, []).
%%
@@ -590,11 +590,11 @@ call_dirty(ServerRef, Request, Timeout, T) ->
{ok,Reply} ->
Reply
catch
- Class:Reason ->
+ Class:Reason:Stacktrace ->
erlang:raise(
Class,
{Reason,{?MODULE,call,[ServerRef,Request,Timeout]}},
- erlang:get_stacktrace())
+ Stacktrace)
end.
call_clean(ServerRef, Request, Timeout, T) ->
@@ -608,9 +608,8 @@ call_clean(ServerRef, Request, Timeout, T) ->
ServerRef, '$gen_call', Request, T) of
Result ->
{Ref,Result}
- catch Class:Reason ->
- {Ref,Class,Reason,
- erlang:get_stacktrace()}
+ catch Class:Reason:Stacktrace ->
+ {Ref,Class,Reason,Stacktrace}
end
end),
Mref = monitor(process, Pid),
@@ -642,16 +641,11 @@ replies([]) ->
%% Might actually not send the message in case of caught exception
send(Proc, Msg) ->
- try erlang:send(Proc, Msg, [noconnect]) of
- noconnect ->
- _ = spawn(erlang, send, [Proc,Msg]),
- ok;
- ok ->
- ok
+ try erlang:send(Proc, Msg)
catch
- _:_ ->
- ok
- end.
+ error:_ -> ok
+ end,
+ ok.
%% Here the init_it/6 and enter_loop/5,6,7 functions converge
enter(Module, Opts, State, Data, Server, Actions, Parent) ->
@@ -675,9 +669,9 @@ enter(Module, Opts, State, Data, Server, Actions, Parent) ->
NewDebug = ?sys_debug(Debug, {Name,State}, {enter,Event,State}),
case call_callback_mode(S) of
#state{} = NewS ->
- loop_event_actions(
+ loop_event_actions_list(
Parent, NewDebug, NewS,
- Events, Event, State, Data, #trans_opts{},
+ Events, Event, State, Data, false,
NewActions, CallEnter);
[Class,Reason,Stacktrace] ->
terminate(
@@ -697,8 +691,7 @@ init_it(Starter, Parent, ServerRef, Module, Args, Opts) ->
catch
Result ->
init_result(Starter, Parent, ServerRef, Module, Result, Opts);
- Class:Reason ->
- Stacktrace = erlang:get_stacktrace(),
+ Class:Reason:Stacktrace ->
Name = gen:get_proc_name(ServerRef),
gen:unregister_name(ServerRef),
proc_lib:init_ack(Starter, {error,Reason}),
@@ -1293,7 +1286,7 @@ parse_actions_next_event(
next_events_r = [{Type,Content}|NextEventsR]});
_ ->
[error,
- {bad_action_from_state_function,{next_events,Type,Content}},
+ {bad_action_from_state_function,{next_event,Type,Content}},
?STACKTRACE(),
?not_sys_debug]
end;
@@ -1310,7 +1303,7 @@ parse_actions_next_event(
next_events_r = [{Type,Content}|NextEventsR]});
_ ->
[error,
- {bad_action_from_state_function,{next_events,Type,Content}},
+ {bad_action_from_state_function,{next_event,Type,Content}},
?STACKTRACE(),
Debug]
end.
@@ -1584,8 +1577,8 @@ call_callback_mode(#state{module = Module} = S) ->
catch
CallbackMode ->
callback_mode_result(S, CallbackMode);
- Class:Reason ->
- [Class,Reason,erlang:get_stacktrace()]
+ Class:Reason:Stacktrace ->
+ [Class,Reason,Stacktrace]
end.
callback_mode_result(S, CallbackMode) ->
@@ -1638,8 +1631,8 @@ call_state_function(
catch
Result ->
{Result,S};
- Class:Reason ->
- [Class,Reason,erlang:get_stacktrace()]
+ Class:Reason:Stacktrace ->
+ [Class,Reason,Stacktrace]
end.
@@ -1827,8 +1820,7 @@ terminate(
_ -> ok
catch
_ -> ok;
- C:R ->
- ST = erlang:get_stacktrace(),
+ C:R:ST ->
error_info(
C, R, ST, S, Q,
format_status(terminate, get(), S)),
diff --git a/lib/stdlib/src/io_lib.erl b/lib/stdlib/src/io_lib.erl
index cacd9f2524..e37c13093b 100644
--- a/lib/stdlib/src/io_lib.erl
+++ b/lib/stdlib/src/io_lib.erl
@@ -931,7 +931,7 @@ limit_term(Term, Depth) ->
limit(_, 0) -> '...';
limit([H|T]=L, D) ->
if
- D =:= 1 -> '...';
+ D =:= 1 -> ['...'];
true ->
case printable_list(L) of
true -> L;
@@ -944,7 +944,7 @@ limit(Term, D) when is_map(Term) ->
limit({}=T, _D) -> T;
limit(T, D) when is_tuple(T) ->
if
- D =:= 1 -> '...';
+ D =:= 1 -> {'...'};
true ->
list_to_tuple([limit(element(1, T), D-1)|
limit_tail(tl(tuple_to_list(T)), D-1)])
@@ -961,32 +961,29 @@ limit_tail(Other, D) ->
%% Cannot limit maps properly since there is no guarantee that
%% maps:from_list() creates a map with the same internal ordering of
-%% the selected associations as in Map.
+%% the selected associations as in Map. Instead of subtracting one
+%% from the depth as the map associations are traversed (as is done
+%% for tuples and lists), the same depth is applied to each and every
+%% (returned) association.
limit_map(Map, D) ->
- limit_map(maps:iterator(Map), D, []).
+ %% Keep one extra association to make sure the final ',...' is included.
+ limit_map_body(maps:iterator(Map), D + 1, D, []).
-limit_map(_I, 0, Acc) ->
+limit_map_body(_I, 0, _D0, Acc) ->
maps:from_list(Acc);
-limit_map(I, D, Acc) ->
+limit_map_body(I, D, D0, Acc) ->
case maps:next(I) of
{K, V, NextI} ->
- limit_map(NextI, D-1, [{K,V} | Acc]);
+ limit_map_body(NextI, D-1, D0, [limit_map_assoc(K, V, D0) | Acc]);
none ->
maps:from_list(Acc)
end.
-%% maps:from_list(limit_map_body(erts_internal:maps_to_list(Map, D), D)).
+limit_map_assoc(K, V, D) ->
+ %% Keep keys as are to avoid creating duplicated keys.
+ {K, limit(V, D - 1)}.
-%% limit_map_body(_, 0) -> [{'...', '...'}];
-%% limit_map_body([], _) -> [];
-%% limit_map_body([{K,V}], D) -> [limit_map_assoc(K, V, D)];
-%% limit_map_body([{K,V}|KVs], D) ->
-%% [limit_map_assoc(K, V, D) | limit_map_body(KVs, D-1)].
-
-%% limit_map_assoc(K, V, D) ->
-%% {limit(K, D-1), limit(V, D-1)}.
-
-limit_bitstring(B, _D) -> B. %% Keeps all printable binaries.
+limit_bitstring(B, _D) -> B. % Keeps all printable binaries.
test_limit(_, 0) -> throw(limit);
test_limit([H|T]=L, D) when is_integer(D) ->
@@ -1022,18 +1019,21 @@ test_limit_tuple(T, I, Sz, D) ->
test_limit(element(I, T), D-1),
test_limit_tuple(T, I+1, Sz, D-1).
-test_limit_map(_Map, _D) -> ok.
-%% test_limit_map_body(erts_internal:maps_to_list(Map, D), D).
+test_limit_map(Map, D) ->
+ test_limit_map_body(maps:iterator(Map), D).
-%% test_limit_map_body(_, 0) -> throw(limit);
-%% test_limit_map_body([], _) -> ok;
-%% test_limit_map_body([{K,V}], D) -> test_limit_map_assoc(K, V, D);
-%% test_limit_map_body([{K,V}|KVs], D) ->
-%% test_limit_map_assoc(K, V, D),
-%% test_limit_map_body(KVs, D-1).
+test_limit_map_body(_I, 0) -> throw(limit); % cannot happen
+test_limit_map_body(I, D) ->
+ case maps:next(I) of
+ {K, V, NextI} ->
+ test_limit_map_assoc(K, V, D),
+ test_limit_map_body(NextI, D-1);
+ none ->
+ ok
+ end.
-%% test_limit_map_assoc(K, V, D) ->
-%% test_limit(K, D-1),
-%% test_limit(V, D-1).
+test_limit_map_assoc(K, V, D) ->
+ test_limit(K, D - 1),
+ test_limit(V, D - 1).
test_limit_bitstring(_, _) -> ok.
diff --git a/lib/stdlib/src/io_lib_format.erl b/lib/stdlib/src/io_lib_format.erl
index e345810ca0..64edbf1824 100644
--- a/lib/stdlib/src/io_lib_format.erl
+++ b/lib/stdlib/src/io_lib_format.erl
@@ -95,7 +95,7 @@ print([]) ->
[].
print(C, F, Ad, P, Pad, Encoding, Strings) ->
- [$~] ++ print_field_width(F, Ad) ++ print_precision(P) ++
+ [$~] ++ print_field_width(F, Ad) ++ print_precision(P, Pad) ++
print_pad_char(Pad) ++ print_encoding(Encoding) ++
print_strings(Strings) ++ [C].
@@ -103,8 +103,9 @@ print_field_width(none, _Ad) -> "";
print_field_width(F, left) -> integer_to_list(-F);
print_field_width(F, right) -> integer_to_list(F).
-print_precision(none) -> "";
-print_precision(P) -> [$. | integer_to_list(P)].
+print_precision(none, $\s) -> "";
+print_precision(none, _Pad) -> "."; % pad must be second dot
+print_precision(P, _Pad) -> [$. | integer_to_list(P)].
print_pad_char($\s) -> ""; % default, no need to make explicit
print_pad_char(Pad) -> [$., Pad].
@@ -126,25 +127,23 @@ collect_cseq(Fmt0, Args0) ->
{F,Ad,Fmt1,Args1} = field_width(Fmt0, Args0),
{P,Fmt2,Args2} = precision(Fmt1, Args1),
{Pad,Fmt3,Args3} = pad_char(Fmt2, Args2),
- {Encoding,Fmt4,Args4} = encoding(Fmt3, Args3),
- {Strings,Fmt5,Args5} = strings(Fmt4, Args4),
- {C,As,Fmt6,Args6} = collect_cc(Fmt5, Args5),
- FormatSpec = #{control_char => C, args => As, width => F, adjust => Ad,
- precision => P, pad_char => Pad, encoding => Encoding,
- strings => Strings},
- {FormatSpec,Fmt6,Args6}.
-
-encoding([$t|Fmt],Args) ->
- true = hd(Fmt) =/= $l,
- {unicode,Fmt,Args};
-encoding(Fmt,Args) ->
- {latin1,Fmt,Args}.
-
-strings([$l|Fmt],Args) ->
- true = hd(Fmt) =/= $t,
- {false,Fmt,Args};
-strings(Fmt,Args) ->
- {true,Fmt,Args}.
+ Spec0 = #{width => F,
+ adjust => Ad,
+ precision => P,
+ pad_char => Pad,
+ encoding => latin1,
+ strings => true},
+ {Spec1,Fmt4} = modifiers(Fmt3, Spec0),
+ {C,As,Fmt5,Args4} = collect_cc(Fmt4, Args3),
+ Spec2 = Spec1#{control_char => C, args => As},
+ {Spec2,Fmt5,Args4}.
+
+modifiers([$t|Fmt], Spec) ->
+ modifiers(Fmt, Spec#{encoding => unicode});
+modifiers([$l|Fmt], Spec) ->
+ modifiers(Fmt, Spec#{strings => false});
+modifiers(Fmt, Spec) ->
+ {Spec, Fmt}.
field_width([$-|Fmt0], Args0) ->
{F,Fmt,Args} = field_value(Fmt0, Args0),
diff --git a/lib/stdlib/src/lists.erl b/lib/stdlib/src/lists.erl
index af9d63ddd6..06c90c0280 100644
--- a/lib/stdlib/src/lists.erl
+++ b/lib/stdlib/src/lists.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2016. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2018. 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.
@@ -38,8 +38,8 @@
-export([all/2,any/2,map/2,flatmap/2,foldl/3,foldr/3,filter/2,
partition/2,zf/2,filtermap/2,
- mapfoldl/3,mapfoldr/3,foreach/2,takewhile/2,dropwhile/2,splitwith/2,
- split/2,
+ mapfoldl/3,mapfoldr/3,foreach/2,takewhile/2,dropwhile/2,
+ search/2, splitwith/2,split/2,
join/2]).
%%% BIFs
@@ -1399,6 +1399,19 @@ dropwhile(Pred, [Hd|Tail]=Rest) ->
end;
dropwhile(Pred, []) when is_function(Pred, 1) -> [].
+-spec search(Pred, List) -> {value, Value} | false when
+ Pred :: fun((T) -> boolean()),
+ List :: [T],
+ Value :: T.
+
+search(Pred, [Hd|Tail]) ->
+ case Pred(Hd) of
+ true -> {value, Hd};
+ false -> search(Pred, Tail)
+ end;
+search(Pred, []) when is_function(Pred, 1) ->
+ false.
+
-spec splitwith(Pred, List) -> {List1, List2} when
Pred :: fun((T) -> boolean()),
List :: [T],
diff --git a/lib/stdlib/src/proc_lib.erl b/lib/stdlib/src/proc_lib.erl
index 8e10cbe93b..1991585c13 100644
--- a/lib/stdlib/src/proc_lib.erl
+++ b/lib/stdlib/src/proc_lib.erl
@@ -231,8 +231,8 @@ init_p(Parent, Ancestors, Fun) when is_function(Fun) ->
try
Fun()
catch
- Class:Reason ->
- exit_p(Class, Reason, erlang:get_stacktrace())
+ Class:Reason:Stacktrace ->
+ exit_p(Class, Reason, Stacktrace)
end.
-spec init_p(pid(), [pid()], atom(), atom(), [term()]) -> term().
@@ -246,8 +246,8 @@ init_p_do_apply(M, F, A) ->
try
apply(M, F, A)
catch
- Class:Reason ->
- exit_p(Class, Reason, erlang:get_stacktrace())
+ Class:Reason:Stacktrace ->
+ exit_p(Class, Reason, Stacktrace)
end.
-spec wake_up(atom(), atom(), [term()]) -> term().
@@ -256,8 +256,8 @@ wake_up(M, F, A) when is_atom(M), is_atom(F), is_list(A) ->
try
apply(M, F, A)
catch
- Class:Reason ->
- exit_p(Class, Reason, erlang:get_stacktrace())
+ Class:Reason:Stacktrace ->
+ exit_p(Class, Reason, Stacktrace)
end.
exit_p(Class, Reason, Stacktrace) ->
diff --git a/lib/stdlib/src/qlc.erl b/lib/stdlib/src/qlc.erl
index f11f9d0a0b..3a66f6930b 100644
--- a/lib/stdlib/src/qlc.erl
+++ b/lib/stdlib/src/qlc.erl
@@ -301,11 +301,11 @@ eval(QH, Options) ->
post_funs(Post)
end
end
- catch Term ->
- case erlang:get_stacktrace() of
+ catch throw:Term:Stacktrace ->
+ case Stacktrace of
[?THROWN_ERROR | _] ->
Term;
- Stacktrace ->
+ _ ->
erlang:raise(throw, Term, Stacktrace)
end
end
@@ -359,11 +359,11 @@ fold(Fun, Acc0, QH, Options) ->
post_funs(Post)
end
end
- catch Term ->
- case erlang:get_stacktrace() of
+ catch throw:Term:Stacktrace ->
+ case Stacktrace of
[?THROWN_ERROR | _] ->
Term;
- Stacktrace ->
+ _ ->
erlang:raise(throw, Term, Stacktrace)
end
end
@@ -457,11 +457,11 @@ info(QH, Options) ->
debug -> % Not documented. Intended for testing only.
Info
end
- catch Term ->
- case erlang:get_stacktrace() of
+ catch throw:Term:Stacktrace ->
+ case Stacktrace of
[?THROWN_ERROR | _] ->
Term;
- Stacktrace ->
+ _ ->
erlang:raise(throw, Term, Stacktrace)
end
end
@@ -1056,9 +1056,9 @@ cursor_process(H, GUnique, GCache, TmpDir, SpawnOptions, MaxList, TmpUsage) ->
Prep = prepare_qlc(H, not_a_list, GUnique, GCache,
TmpDir, MaxList, TmpUsage),
setup_qlc(Prep, Setup)
- catch Class:Reason ->
- Parent ! {self(), {caught, Class, Reason,
- erlang:get_stacktrace()}},
+ catch Class:Reason:Stacktrace ->
+ Parent ! {self(),
+ {caught, Class, Reason, Stacktrace}},
exit(normal)
end,
Parent ! {self(), ok},
@@ -1075,8 +1075,8 @@ parent_fun(Pid, Parent) ->
{TPid, {parent_fun, Fun}} ->
V = try
{value, Fun()}
- catch Class:Reason ->
- {parent_fun_caught, Class, Reason, erlang:get_stacktrace()}
+ catch Class:Reason:Stacktrace ->
+ {parent_fun_caught, Class, Reason, Stacktrace}
end,
TPid ! {Parent, V},
parent_fun(Pid, Parent);
@@ -1101,9 +1101,9 @@ reply(Parent, MonRef, Post, Cont) ->
throw_error(Cont)
end
catch
- Class:Reason ->
+ Class:Reason:Stacktrace ->
post_funs(Post),
- Message = {caught, Class, Reason, erlang:get_stacktrace()},
+ Message = {caught, Class, Reason, Stacktrace},
Parent ! {self(), Message},
exit(normal)
end,
@@ -1392,9 +1392,8 @@ next_loop(Pid, L, N) when N =/= 0 ->
{caught, throw, Error, [?THROWN_ERROR | _]} ->
Error;
{caught, Class, Reason, Stacktrace} ->
- CurrentStacktrace = try erlang:error(foo)
- catch error:_ -> erlang:get_stacktrace()
- end,
+ {current_stacktrace, CurrentStacktrace} =
+ erlang:process_info(self(), current_stacktrace),
erlang:raise(Class, Reason, Stacktrace ++ CurrentStacktrace);
error ->
erlang:error({qlc_cursor_pid_no_longer_exists, Pid})
@@ -2627,9 +2626,9 @@ table_handle(#qlc_table{trav_fun = TraverseFun, trav_MS = TravMS,
Parent =:= self() ->
try
ParentFun()
- catch Class:Reason ->
+ catch Class:Reason:Stacktrace ->
post_funs(Post),
- erlang:raise(Class, Reason, erlang:get_stacktrace())
+ erlang:raise(Class, Reason, Stacktrace)
end;
true ->
case monitor_request(Parent, {parent_fun, ParentFun}) of
@@ -3033,9 +3032,9 @@ file_sort_handle(H, Kp, SortOptions, TmpDir, Compressed, Post, LocalPost) ->
{terms, BTerms} ->
try
{[binary_to_term(B) || B <- BTerms], Post, LocalPost}
- catch Class:Reason ->
+ catch Class:Reason:Stacktrace ->
post_funs(Post),
- erlang:raise(Class, Reason, erlang:get_stacktrace())
+ erlang:raise(Class, Reason, Stacktrace)
end
end.
@@ -3045,9 +3044,9 @@ do_sort(In, Out, Sort, SortOptions, Post) ->
{error, Reason} -> throw_reason(Reason);
Reply -> Reply
end
- catch Class:Term ->
+ catch Class:Term:Stacktrace ->
post_funs(Post),
- erlang:raise(Class, Term, erlang:get_stacktrace())
+ erlang:raise(Class, Term, Stacktrace)
end.
do_sort(In, Out, sort, SortOptions) ->
@@ -3797,9 +3796,9 @@ call(undefined, _Arg, Default, _Post) ->
call(Fun, Arg, _Default, Post) ->
try
Fun(Arg)
- catch Class:Reason ->
+ catch Class:Reason:Stacktrace ->
post_funs(Post),
- erlang:raise(Class, Reason, erlang:get_stacktrace())
+ erlang:raise(Class, Reason, Stacktrace)
end.
grd(undefined, _Arg) ->
diff --git a/lib/stdlib/src/shell.erl b/lib/stdlib/src/shell.erl
index ad4984b64c..e4153e7899 100644
--- a/lib/stdlib/src/shell.erl
+++ b/lib/stdlib/src/shell.erl
@@ -645,8 +645,7 @@ eval_exprs(Es, Shell, Bs0, RT, Lf, Ef, W) ->
catch
exit:normal ->
exit(normal);
- Class:Reason ->
- Stacktrace = erlang:get_stacktrace(),
+ Class:Reason:Stacktrace ->
M = {self(),Class,{Reason,Stacktrace}},
case do_catch(Class, Reason) of
true ->
@@ -807,8 +806,8 @@ restrict_handlers(RShMod, Shell, RT) ->
-define(BAD_RETURN(M, F, V),
try erlang:error(reason)
- catch _:_ -> erlang:raise(exit, {restricted_shell_bad_return,V},
- [{M,F,3} | erlang:get_stacktrace()])
+ catch _:_:S -> erlang:raise(exit, {restricted_shell_bad_return,V},
+ [{M,F,3} | S])
end).
local_allowed(F, As, RShMod, Bs, Shell, RT) when is_atom(F) ->
diff --git a/lib/stdlib/src/string.erl b/lib/stdlib/src/string.erl
index e01bb7d85e..4e89819e41 100644
--- a/lib/stdlib/src/string.erl
+++ b/lib/stdlib/src/string.erl
@@ -420,10 +420,12 @@ to_number(_, Number, Rest, _, Tail) ->
%% Return the remaining string with prefix removed or else nomatch
-spec prefix(String::unicode:chardata(), Prefix::unicode:chardata()) ->
'nomatch' | unicode:chardata().
-prefix(Str, []) -> Str;
prefix(Str, Prefix0) ->
- Prefix = unicode:characters_to_list(Prefix0),
- case prefix_1(Str, Prefix) of
+ Result = case unicode:characters_to_list(Prefix0) of
+ [] -> Str;
+ Prefix -> prefix_1(Str, Prefix)
+ end,
+ case Result of
[] when is_binary(Str) -> <<>>;
Res -> Res
end.
diff --git a/lib/stdlib/src/uri_string.erl b/lib/stdlib/src/uri_string.erl
index a84679c595..28d36ea229 100644
--- a/lib/stdlib/src/uri_string.erl
+++ b/lib/stdlib/src/uri_string.erl
@@ -227,7 +227,7 @@
%% External API
%%-------------------------------------------------------------------------
-export([compose_query/1, compose_query/2,
- dissect_query/1, normalize/1, parse/1,
+ dissect_query/1, normalize/1, normalize/2, parse/1,
recompose/1, transcode/2]).
-export_type([error/0, uri_map/0, uri_string/0]).
@@ -292,18 +292,36 @@
%%-------------------------------------------------------------------------
%% Normalize URIs
%%-------------------------------------------------------------------------
--spec normalize(URIString) -> NormalizedURI when
- URIString :: uri_string(),
- NormalizedURI :: uri_string().
-normalize(URIString) ->
- %% Percent-encoding normalization and case normalization for
- %% percent-encoded triplets are achieved by running parse and
- %% recompose on the input URI string.
- recompose(
- normalize_path_segment(
- normalize_scheme_based(
- normalize_case(
- parse(URIString))))).
+-spec normalize(URI) -> NormalizedURI when
+ URI :: uri_string() | uri_map(),
+ NormalizedURI :: uri_string()
+ | error().
+normalize(URIMap) ->
+ normalize(URIMap, []).
+
+
+-spec normalize(URI, Options) -> NormalizedURI when
+ URI :: uri_string() | uri_map(),
+ Options :: [return_map],
+ NormalizedURI :: uri_string() | uri_map().
+normalize(URIMap, []) when is_map(URIMap) ->
+ recompose(normalize_map(URIMap));
+normalize(URIMap, [return_map]) when is_map(URIMap) ->
+ normalize_map(URIMap);
+normalize(URIString, []) ->
+ case parse(URIString) of
+ Value when is_map(Value) ->
+ recompose(normalize_map(Value));
+ Error ->
+ Error
+ end;
+normalize(URIString, [return_map]) ->
+ case parse(URIString) of
+ Value when is_map(Value) ->
+ normalize_map(Value);
+ Error ->
+ Error
+ end.
%%-------------------------------------------------------------------------
@@ -385,7 +403,8 @@ transcode(URIString, Options) when is_list(URIString) ->
%%-------------------------------------------------------------------------
%% Functions for working with the query part of a URI as a list
%% of key/value pairs.
-%% HTML5 - 4.10.22.6 URL-encoded form data
+%% HTML 5.2 - 4.10.21.6 URL-encoded form data - WHATWG URL (10 Jan 2018) - UTF-8
+%% HTML 5.0 - 4.10.22.6 URL-encoded form data - non UTF-8
%%-------------------------------------------------------------------------
%%-------------------------------------------------------------------------
@@ -393,7 +412,7 @@ transcode(URIString, Options) when is_list(URIString) ->
%% (application/x-www-form-urlencoded encoding algorithm)
%%-------------------------------------------------------------------------
-spec compose_query(QueryList) -> QueryString when
- QueryList :: [{uri_string(), uri_string()}],
+ QueryList :: [{unicode:chardata(), unicode:chardata()}],
QueryString :: uri_string()
| error().
compose_query(List) ->
@@ -401,7 +420,7 @@ compose_query(List) ->
-spec compose_query(QueryList, Options) -> QueryString when
- QueryList :: [{uri_string(), uri_string()}],
+ QueryList :: [{unicode:chardata(), unicode:chardata()}],
Options :: [{encoding, atom()}],
QueryString :: uri_string()
| error().
@@ -432,7 +451,7 @@ compose_query([], _Options, IsList, Acc) ->
%%-------------------------------------------------------------------------
-spec dissect_query(QueryString) -> QueryList when
QueryString :: uri_string(),
- QueryList :: [{uri_string(), uri_string()}]
+ QueryList :: [{unicode:chardata(), unicode:chardata()}]
| error().
dissect_query(<<>>) ->
[];
@@ -1755,7 +1774,8 @@ get_separator(_L) ->
<<"&">>.
-%% HTML5 - 4.10.22.6 URL-encoded form data - encoding
+%% HTML 5.2 - 4.10.21.6 URL-encoded form data - WHATWG URL (10 Jan 2018) - UTF-8
+%% HTML 5.0 - 4.10.22.6 URL-encoded form data - encoding (non UTF-8)
form_urlencode(Cs, [{encoding, latin1}]) when is_list(Cs) ->
B = convert_to_binary(Cs, utf8, utf8),
html5_byte_encode(base10_encode(B));
@@ -1850,7 +1870,8 @@ dissect_query_value(<<>>, IsList, Acc, Key, Value) ->
lists:reverse([{K,V}|Acc]).
-%% Form-urldecode input based on RFC 1866 [8.2.1]
+%% HTML 5.2 - 4.10.21.6 URL-encoded form data - WHATWG URL (10 Jan 2018) - UTF-8
+%% HTML 5.0 - 4.10.22.6 URL-encoded form data - decoding (non UTF-8)
form_urldecode(true, B) ->
Result = base10_decode(form_urldecode(B, <<>>)),
convert_to_list(Result, utf8);
@@ -1903,6 +1924,12 @@ base10_decode_unicode(<<H,_/binary>>, _, _) ->
%% Helper functions for normalize
%%-------------------------------------------------------------------------
+normalize_map(URIMap) ->
+ normalize_path_segment(
+ normalize_scheme_based(
+ normalize_case(URIMap))).
+
+
%% 6.2.2.1. Case Normalization
normalize_case(#{scheme := Scheme, host := Host} = Map) ->
Map#{scheme => to_lower(Scheme),
diff --git a/lib/stdlib/src/zip.erl b/lib/stdlib/src/zip.erl
index 81f927f399..39be2abff6 100644
--- a/lib/stdlib/src/zip.erl
+++ b/lib/stdlib/src/zip.erl
@@ -457,8 +457,7 @@ do_zip(F, Files, Options) ->
Out3 = Output({close, F}, Out2),
{ok, Out3}
catch
- C:R ->
- Stk = erlang:get_stacktrace(),
+ C:R:Stk ->
zlib:close(Z),
Output({close, F}, Out0),
erlang:raise(C, R, Stk)
diff --git a/lib/stdlib/test/array_SUITE.erl b/lib/stdlib/test/array_SUITE.erl
index 5836f275ba..956582c4fd 100644
--- a/lib/stdlib/test/array_SUITE.erl
+++ b/lib/stdlib/test/array_SUITE.erl
@@ -141,10 +141,10 @@ t(What) ->
io:format("Test ~p ~n",[T]),
try
?MODULE:T([])
- catch _E:_R ->
+ catch _E:_R:_S ->
Line = get(test_server_loc),
io:format("Failed ~p:~p ~p ~p~n ~p~n",
- [T,Line,_E,_R, erlang:get_stacktrace()])
+ [T,Line,_E,_R,_S])
end
end, What).
@@ -161,8 +161,8 @@ extract_tests() ->
end,
[Call(Test) || Test <- Tests],
io:format("Tests ~p~n", [Tests])
- catch _:Err ->
- io:format("Error: ~p ~p~n", [Err, erlang:get_stacktrace()])
+ catch _:Err:Stacktrace ->
+ io:format("Error: ~p ~p~n", [Err, Stacktrace])
end,
file:close(In),
file:close(Out).
diff --git a/lib/stdlib/test/dets_SUITE.erl b/lib/stdlib/test/dets_SUITE.erl
index d667bd82a2..7d82790b82 100644
--- a/lib/stdlib/test/dets_SUITE.erl
+++ b/lib/stdlib/test/dets_SUITE.erl
@@ -3275,16 +3275,16 @@ otp_8856(Config) when is_list(Config) ->
{ok, _} = dets:open_file(Tab, [{type, bag}, {file, File}]),
spawn(fun()-> Me ! {1, dets:insert(Tab, [])} end),
spawn(fun()-> Me ! {2, dets:insert_new(Tab, [])} end),
- ok = dets:close(Tab),
receive {1, ok} -> ok end,
receive {2, true} -> ok end,
+ ok = dets:close(Tab),
file:delete(File),
{ok, _} = dets:open_file(Tab, [{type, set}, {file, File}]),
spawn(fun() -> dets:delete(Tab, 0) end),
spawn(fun() -> Me ! {3, dets:insert_new(Tab, {0,0})} end),
- ok = dets:close(Tab),
receive {3, true} -> ok end,
+ ok = dets:close(Tab),
file:delete(File),
ok.
diff --git a/lib/stdlib/test/error_logger_h_SUITE.erl b/lib/stdlib/test/error_logger_h_SUITE.erl
index 1f2a9fda0b..9dc04f27a1 100644
--- a/lib/stdlib/test/error_logger_h_SUITE.erl
+++ b/lib/stdlib/test/error_logger_h_SUITE.erl
@@ -257,8 +257,7 @@ match_output([Item|T], Lines0, AtNode, Depth) ->
Lines ->
match_output(T, Lines, AtNode, Depth)
catch
- C:E ->
- Stk = erlang:get_stacktrace(),
+ C:E:Stk ->
io:format("ITEM: ~p", [Item]),
io:format("LINES: ~p", [Lines0]),
erlang:raise(C, E, Stk)
diff --git a/lib/stdlib/test/ets_SUITE.erl b/lib/stdlib/test/ets_SUITE.erl
index 07c8b60cbd..ec4a16b510 100644
--- a/lib/stdlib/test/ets_SUITE.erl
+++ b/lib/stdlib/test/ets_SUITE.erl
@@ -78,6 +78,7 @@
-export([ets_all/1]).
-export([massive_ets_all/1]).
-export([take/1]).
+-export([whereis_table/1]).
-export([init_per_testcase/2, end_per_testcase/2]).
%% Convenience for manual testing
@@ -137,7 +138,8 @@ all() ->
otp_9423,
ets_all,
massive_ets_all,
- take].
+ take,
+ whereis_table].
groups() ->
[{new, [],
@@ -3647,7 +3649,7 @@ verify_rescheduling_exit(Config, ForEachData, Flags, Fix, NOTabs, NOProcs) ->
XScheds = count_exit_sched(TP),
io:format("~p XScheds=~p~n",
[TP, XScheds]),
- true = XScheds >= 5
+ true = XScheds >= 3
end,
TPs),
stop_loopers(LPs),
@@ -4099,6 +4101,7 @@ info_do(Opts) ->
{value, {keypos, 2}} = lists:keysearch(keypos, 1, Res),
{value, {protection, protected}} =
lists:keysearch(protection, 1, Res),
+ {value, {id, Tab}} = lists:keysearch(id, 1, Res),
true = ets:delete(Tab),
undefined = ets:info(non_existing_table_xxyy),
undefined = ets:info(non_existing_table_xxyy,type),
@@ -5892,6 +5895,36 @@ take(Config) when is_list(Config) ->
ets:delete(T3),
ok.
+whereis_table(Config) when is_list(Config) ->
+ %% Do we return 'undefined' when the named table doesn't exist?
+ undefined = ets:whereis(whereis_test),
+
+ %% Does the tid() refer to the same table as the name?
+ whereis_test = ets:new(whereis_test, [named_table]),
+ Tid = ets:whereis(whereis_test),
+
+ ets:insert(whereis_test, [{hello}, {there}]),
+
+ [[{hello}],[{there}]] = ets:match(whereis_test, '$1'),
+ [[{hello}],[{there}]] = ets:match(Tid, '$1'),
+
+ true = ets:delete_all_objects(Tid),
+
+ [] = ets:match(whereis_test, '$1'),
+ [] = ets:match(Tid, '$1'),
+
+ %% Does the name disappear when deleted through the tid()?
+ true = ets:delete(Tid),
+ undefined = ets:info(whereis_test),
+ {'EXIT',{badarg, _}} = (catch ets:match(whereis_test, '$1')),
+
+ %% Is the old tid() broken when the table is re-created with the same
+ %% name?
+ whereis_test = ets:new(whereis_test, [named_table]),
+ [] = ets:match(whereis_test, '$1'),
+ {'EXIT',{badarg, _}} = (catch ets:match(Tid, '$1')),
+
+ ok.
%%
%% Utility functions:
@@ -6023,17 +6056,23 @@ etsmem() ->
end},
{Mem,AllTabs}.
-verify_etsmem({MemInfo,AllTabs}) ->
+
+verify_etsmem(MI) ->
wait_for_test_procs(),
+ verify_etsmem(MI, 1).
+
+verify_etsmem({MemInfo,AllTabs}, Try) ->
case etsmem() of
{MemInfo,_} ->
io:format("Ets mem info: ~p", [MemInfo]),
- case MemInfo of
- {ErlMem,EtsAlloc} when ErlMem == notsup; EtsAlloc == undefined ->
+ case {MemInfo, Try} of
+ {{ErlMem,EtsAlloc},_} when ErlMem == notsup; EtsAlloc == undefined ->
%% Use 'erl +Mea max' to do more complete memory leak testing.
{comment,"Incomplete or no mem leak testing"};
- _ ->
- ok
+ {_, 1} ->
+ ok;
+ _ ->
+ {comment, "Transient memory discrepancy"}
end;
{MemInfo2, AllTabs2} ->
@@ -6041,7 +6080,15 @@ verify_etsmem({MemInfo,AllTabs}) ->
io:format("Actual: ~p", [MemInfo2]),
io:format("Changed tables before: ~p\n",[AllTabs -- AllTabs2]),
io:format("Changed tables after: ~p\n", [AllTabs2 -- AllTabs]),
- ct:fail("Failed memory check")
+ case Try < 2 of
+ true ->
+ io:format("\nThis discrepancy could be caused by an "
+ "inconsistent memory \"snapshot\""
+ "\nTry again...\n", []),
+ verify_etsmem({MemInfo, AllTabs}, Try+1);
+ false ->
+ ct:fail("Failed memory check")
+ end
end.
diff --git a/lib/stdlib/test/gen_statem_SUITE.erl b/lib/stdlib/test/gen_statem_SUITE.erl
index c747db475a..3f48fe1590 100644
--- a/lib/stdlib/test/gen_statem_SUITE.erl
+++ b/lib/stdlib/test/gen_statem_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2016-2017. All Rights Reserved.
+%% Copyright Ericsson AB 2016-2018. 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.
@@ -832,9 +832,14 @@ event_types(_Config) ->
%% Abusing the internal format of From...
#{init =>
fun () ->
- {ok, start, undefined}
+ {ok, start1, undefined,
+ [{next_event,internal,0}]}
end,
- start =>
+ start1 =>
+ fun (internal, 0, undefined) ->
+ {next_state, start2, undefined}
+ end,
+ start2 =>
fun ({call,_} = Call, Req, undefined) ->
{next_state, state1, undefined,
[{next_event,internal,1},
@@ -2040,9 +2045,9 @@ handle_event(Type, Event, State, Data) ->
Result ->
wrap_result(Result)
catch
- throw:Result ->
+ throw:Result:Stacktrace ->
erlang:raise(
- throw, wrap_result(Result), erlang:get_stacktrace())
+ throw, wrap_result(Result), Stacktrace)
end.
unwrap_state([State]) ->
diff --git a/lib/stdlib/test/io_SUITE.erl b/lib/stdlib/test/io_SUITE.erl
index 45363c0592..6f4e7ad7e0 100644
--- a/lib/stdlib/test/io_SUITE.erl
+++ b/lib/stdlib/test/io_SUITE.erl
@@ -1905,29 +1905,61 @@ otp_10836(Suite) when is_list(Suite) ->
%% OTP-10755. The 'l' modifier
otp_10755(Suite) when is_list(Suite) ->
+ %% printing plain ascii characters
S = "string",
"\"string\"" = fmt("~p", [S]),
"[115,116,114,105,110,103]" = fmt("~lp", [S]),
"\"string\"" = fmt("~P", [S, 2]),
"[115|...]" = fmt("~lP", [S, 2]),
- {'EXIT',{badarg,_}} = (catch fmt("~ltp", [S])),
- {'EXIT',{badarg,_}} = (catch fmt("~tlp", [S])),
- {'EXIT',{badarg,_}} = (catch fmt("~ltP", [S])),
- {'EXIT',{badarg,_}} = (catch fmt("~tlP", [S])),
+ %% printing latin1 chars, with and without modifiers
+ T = {[255],list_to_atom([255]),[a,b,c]},
+ "{\"ÿ\",ÿ,[a,b,c]}" = fmt("~p", [T]),
+ "{\"ÿ\",ÿ,[a,b,c]}" = fmt("~tp", [T]),
+ "{[255],ÿ,[a,b,c]}" = fmt("~lp", [T]),
+ "{[255],ÿ,[a,b,c]}" = fmt("~ltp", [T]),
+ "{[255],ÿ,[a,b,c]}" = fmt("~tlp", [T]),
+ "{\"ÿ\",ÿ,...}" = fmt("~P", [T,3]),
+ "{\"ÿ\",ÿ,...}" = fmt("~tP", [T,3]),
+ "{[255],ÿ,...}" = fmt("~lP", [T,3]),
+ "{[255],ÿ,...}" = fmt("~ltP", [T,3]),
+ "{[255],ÿ,...}" = fmt("~tlP", [T,3]),
+ %% printing unicode chars, with and without modifiers
+ U = {[666],list_to_atom([666]),[a,b,c]},
+ "{[666],'\\x{29A}',[a,b,c]}" = fmt("~p", [U]),
+ case io:printable_range() of
+ unicode ->
+ "{\"ʚ\",'ʚ',[a,b,c]}" = fmt("~tp", [U]),
+ "{\"ʚ\",'ʚ',...}" = fmt("~tP", [U,3]);
+ latin1 ->
+ "{[666],'ʚ',[a,b,c]}" = fmt("~tp", [U]),
+ "{[666],'ʚ',...}" = fmt("~tP", [U,3])
+ end,
+ "{[666],'\\x{29A}',[a,b,c]}" = fmt("~lp", [U]),
+ "{[666],'ʚ',[a,b,c]}" = fmt("~ltp", [U]),
+ "{[666],'ʚ',[a,b,c]}" = fmt("~tlp", [U]),
+ "{[666],'\\x{29A}',...}" = fmt("~P", [U,3]),
+ "{[666],'\\x{29A}',...}" = fmt("~lP", [U,3]),
+ "{[666],'ʚ',...}" = fmt("~ltP", [U,3]),
+ "{[666],'ʚ',...}" = fmt("~tlP", [U,3]),
+ %% the compiler should catch uses of ~l with other than pP
Text =
"-module(l_mod).\n"
"-export([t/0]).\n"
"t() ->\n"
" S = \"string\",\n"
- " io:format(\"~ltp\", [S]),\n"
- " io:format(\"~tlp\", [S]),\n"
- " io:format(\"~ltP\", [S, 1]),\n"
- " io:format(\"~tlP\", [S, 1]).\n",
+ " io:format(\"~lw\", [S]),\n"
+ " io:format(\"~lW\", [S, 1]),\n"
+ " io:format(\"~ltw\", [S]),\n"
+ " io:format(\"~tlw\", [S]),\n"
+ " io:format(\"~ltW\", [S, 1]),\n"
+ " io:format(\"~tlW\", [S, 1]).\n",
{ok,l_mod,[{_File,Ws}]} = compile_file("l_mod.erl", Text, Suite),
- ["format string invalid (invalid control ~lt)",
- "format string invalid (invalid control ~tl)",
- "format string invalid (invalid control ~lt)",
- "format string invalid (invalid control ~tl)"] =
+ ["format string invalid (invalid control ~lw)",
+ "format string invalid (invalid control ~lW)",
+ "format string invalid (invalid control ~ltw)",
+ "format string invalid (invalid control ~ltw)",
+ "format string invalid (invalid control ~ltW)",
+ "format string invalid (invalid control ~ltW)"] =
[lists:flatten(M:format_error(E)) || {_L,M,E} <- Ws],
ok.
@@ -2005,6 +2037,7 @@ writes(N, F1) ->
format_string(_Config) ->
%% All but padding is tested by fmt/2.
+ "xxxxxxxsss" = fmt("~10..xs", ["sss"]),
"xxxxxxsssx" = fmt("~10.4.xs", ["sss"]),
"xxxxxxsssx" = fmt("~10.4.*s", [$x, "sss"]),
ok.
@@ -2384,19 +2417,36 @@ limit_term(_Config) ->
{_, 2} = limt({a,b,c,[d,e]}, 2),
{_, 2} = limt({a,b,c,[d,e]}, 3),
{_, 2} = limt({a,b,c,[d,e]}, 4),
+ T0 = [1|{a,b,c}],
+ {_, 2} = limt(T0, 2),
+ {_, 2} = limt(T0, 3),
+ {_, 2} = limt(T0, 4),
{_, 1} = limt(<<"foo">>, 18),
+ {_, 2} = limt({"",[1,2]}, 3),
+ {_, 2} = limt({"",{1,2}}, 3),
+ true = limt_pp({"123456789012345678901234567890",{1,2}}, 3),
ok = blimt(<<"123456789012345678901234567890">>),
+ true = limt_pp(<<"123456789012345678901234567890">>, 3),
+ {_, 2} = limt({<<"kljlkjsl">>,[1,2,3,4]}, 4),
{_, 1} = limt(<<7:3>>, 2),
{_, 1} = limt(<<7:21>>, 2),
{_, 1} = limt([], 2),
{_, 1} = limt({}, 2),
+ {_, 1} = limt({"", ""}, 4),
{_, 1} = limt(#{}, 2),
- {_, 1} = limt(#{[] => {}}, 2),
+ {_, 2} = limt(#{[] => {}}, 1),
+ {_, 2} = limt(#{[] => {}}, 2),
{_, 1} = limt(#{[] => {}}, 3),
T = #{[] => {},[a] => [b]},
- {_, 1} = limt(T, 2),
+ {_, 1} = limt(T, 0),
+ {_, 2} = limt(T, 1),
+ {_, 2} = limt(T, 2),
{_, 1} = limt(T, 3),
{_, 1} = limt(T, 4),
+ T2 = #{[] => {},{} => []},
+ {_, 2} = limt(T2, 1),
+ {_, 2} = limt(T2, 2),
+ {_, 1} = limt(T2, 3),
ok.
blimt(Binary) ->
@@ -2430,3 +2480,12 @@ limt(Term, Depth) when is_integer(Depth) ->
form(Term, Depth) ->
lists:flatten(io_lib:format("~W", [Term, Depth])).
+
+limt_pp(Term, Depth) when is_integer(Depth) ->
+ T1 = io_lib:limit_term(Term, Depth),
+ S = pp(Term, Depth),
+ S1 = pp(T1, Depth),
+ S1 =:= S.
+
+pp(Term, Depth) ->
+ lists:flatten(io_lib:format("~P", [Term, Depth])).
diff --git a/lib/stdlib/test/lists_SUITE.erl b/lib/stdlib/test/lists_SUITE.erl
index 7c99244b36..837ab4e97e 100644
--- a/lib/stdlib/test/lists_SUITE.erl
+++ b/lib/stdlib/test/lists_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2017. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2018. 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.
@@ -57,7 +57,7 @@
filter_partition/1,
join/1,
otp_5939/1, otp_6023/1, otp_6606/1, otp_7230/1,
- suffix/1, subtract/1, droplast/1, hof/1]).
+ suffix/1, subtract/1, droplast/1, search/1, hof/1]).
%% Sort randomized lists until stopped.
%%
@@ -121,7 +121,7 @@ groups() ->
{zip, [parallel], [zip_unzip, zip_unzip3, zipwith, zipwith3]},
{misc, [parallel], [reverse, member, dropwhile, takewhile,
filter_partition, suffix, subtract, join,
- hof, droplast]}
+ hof, droplast, search]}
].
init_per_suite(Config) ->
@@ -2615,6 +2615,20 @@ droplast(Config) when is_list(Config) ->
ok.
+%% Test lists:search/2
+search(Config) when is_list(Config) ->
+ F = fun(I) -> I rem 2 =:= 0 end,
+ F2 = fun(A, B) -> A > B end,
+
+ {value, 2} = lists:search(F, [1,2,3,4]),
+ false = lists:search(F, [1,3,5,7]),
+ false = lists:search(F, []),
+
+ %% Error cases.
+ {'EXIT',{function_clause,_}} = (catch lists:search(badfun, [])),
+ {'EXIT',{function_clause,_}} = (catch lists:search(F2, [])),
+ ok.
+
%% Briefly test the common high-order functions to ensure they
%% are covered.
hof(Config) when is_list(Config) ->
diff --git a/lib/stdlib/test/proc_lib_SUITE.erl b/lib/stdlib/test/proc_lib_SUITE.erl
index 7686889360..fbdcb518b2 100644
--- a/lib/stdlib/test/proc_lib_SUITE.erl
+++ b/lib/stdlib/test/proc_lib_SUITE.erl
@@ -446,8 +446,8 @@ init_dont_hang(Config) when is_list(Config) ->
StartLinkRes = proc_lib:start(?MODULE, init_dont_hang_init, [self()], 1000),
StartLinkRes = proc_lib:start(?MODULE, init_dont_hang_init, [self()], 1000, []),
ok
- catch _:Error ->
- io:format("Error ~p /= ~p ~n",[erlang:get_stacktrace(), StartLinkRes]),
+ catch _:Error:Stacktrace ->
+ io:format("Error ~p /= ~p ~n",[Stacktrace, StartLinkRes]),
exit(Error)
end.
diff --git a/lib/stdlib/test/rand_SUITE.erl b/lib/stdlib/test/rand_SUITE.erl
index 3d3241b33d..d753d929f5 100644
--- a/lib/stdlib/test/rand_SUITE.erl
+++ b/lib/stdlib/test/rand_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2000-2017. All Rights Reserved.
+%% Copyright Ericsson AB 2000-2018. 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.
@@ -52,7 +52,9 @@ all() ->
[seed, interval_int, interval_float,
api_eq,
reference,
- {group, basic_stats}, uniform_real_conv,
+ {group, basic_stats},
+ {group, distr_stats},
+ uniform_real_conv,
plugin, measure,
{group, reference_jump}
].
@@ -60,8 +62,9 @@ all() ->
groups() ->
[{basic_stats, [parallel],
[basic_stats_uniform_1, basic_stats_uniform_2,
- basic_stats_standard_normal,
- stats_standard_normal_box_muller,
+ basic_stats_standard_normal]},
+ {distr_stats, [parallel],
+ [stats_standard_normal_box_muller,
stats_standard_normal_box_muller_2,
stats_standard_normal]},
{reference_jump, [parallel],
@@ -70,6 +73,9 @@ groups() ->
group(basic_stats) ->
%% valgrind needs a lot of time
[{timetrap,{minutes,10}}];
+group(distr_stats) ->
+ %% valgrind needs a lot of time
+ [{timetrap,{minutes,10}}];
group(reference_jump) ->
%% valgrind needs a lot of time
[{timetrap,{minutes,10}}].
@@ -82,9 +88,9 @@ test() ->
try
ok = ?MODULE:Test([]),
io:format("~p: ok~n", [Test])
- catch _:Reason ->
+ catch _:Reason:Stacktrace ->
io:format("Failed: ~p: ~p ~p~n",
- [Test, Reason, erlang:get_stacktrace()])
+ [Test, Reason, Stacktrace])
end
end, Tests).
@@ -98,8 +104,8 @@ seed(Config) when is_list(Config) ->
Algs = algs(),
Test = fun(Alg) ->
try seed_1(Alg)
- catch _:Reason ->
- ct:fail({Alg, Reason, erlang:get_stacktrace()})
+ catch _:Reason:Stacktrace ->
+ ct:fail({Alg, Reason, Stacktrace})
end
end,
[Test(Alg) || Alg <- Algs],
@@ -437,7 +443,7 @@ stats_standard_normal_box_muller(Config) when is_list(Config) ->
{Z, [S]}
end,
State = [rand:seed(exrop)],
- stats_standard_normal(NormalS, State)
+ stats_standard_normal(NormalS, State, 3)
catch error:_ ->
{skip, "math:erfc/1 not supported"}
end.
@@ -462,7 +468,7 @@ stats_standard_normal_box_muller_2(Config) when is_list(Config) ->
{Z, [S]}
end,
State = [rand:seed(exrop)],
- stats_standard_normal(NormalS, State)
+ stats_standard_normal(NormalS, State, 3)
catch error:_ ->
{skip, "math:erfc/1 not supported"}
end.
@@ -472,21 +478,21 @@ stats_standard_normal(Config) when is_list(Config) ->
try math:erfc(1.0) of
_ ->
stats_standard_normal(
- fun rand:normal_s/1, rand:seed_s(exrop))
+ fun rand:normal_s/1, rand:seed_s(exrop), 3)
catch error:_ ->
{skip, "math:erfc/1 not supported"}
end.
%%
-stats_standard_normal(Fun, S) ->
+stats_standard_normal(Fun, S, Retries) ->
%%%
%%% ct config:
-%%% {rand_SUITE, [{stats_standard_normal,[{seconds, 8}, {std_devs, 4.2}]}]}.
+%%% {rand_SUITE, [{stats_standard_normal,[{seconds, 8}, {std_devs, 4.0}]}]}.
%%%
Seconds = ct:get_config({?MODULE, ?FUNCTION_NAME, seconds}, 8),
StdDevs =
ct:get_config(
{?MODULE, ?FUNCTION_NAME, std_devs},
- 4.2), % probability erfc(4.2/sqrt(2)) (1/37465) to fail a bucket
+ 4.0), % probability erfc(4.0/sqrt(2)) (1/15787) to fail a bucket
%%%
ct:timetrap({seconds, Seconds + 120}),
%% Buckets is chosen to get a range where the the probability to land
@@ -505,11 +511,11 @@ stats_standard_normal(Fun, S) ->
P0 = math:erf(1 / W),
Rounds = TargetHits * ceil(1.0 / P0),
Histogram = array:new({default, 0}),
- StopTime = erlang:monotonic_time(second) + Seconds,
ct:pal(
"Running standard normal test against ~w std devs for ~w seconds...",
[StdDevs, Seconds]),
- {PositiveHistogram, NegativeHistogram, Outlier, TotalRounds} =
+ StopTime = erlang:monotonic_time(second) + Seconds,
+ {PositiveHistogram, NegativeHistogram, Outlier, TotalRounds, NewS} =
stats_standard_normal(
InvDelta, Buckets, Histogram, Histogram, 0.0,
Fun, S, Rounds, StopTime, Rounds, 0),
@@ -522,16 +528,33 @@ stats_standard_normal(Fun, S) ->
"Total rounds: ~w, tolerance: 1/~.2f..1/~.2f, "
"outlier: ~.2f, probability 1/~.2f.",
[TotalRounds, Precision, TopPrecision, Outlier, InvOP]),
- {TotalRounds, [], []} =
- {TotalRounds,
+ case
+ {bucket_error, TotalRounds,
check_histogram(
W, TotalRounds, StdDevs, PositiveHistogram, Buckets),
check_histogram(
- W, TotalRounds, StdDevs, NegativeHistogram, Buckets)},
- %% If the probability for getting this Outlier is lower than 1/50,
- %% then this is fishy!
- true = (1/50 =< OutlierProbability),
- {comment, {tp, TopPrecision, op, InvOP}}.
+ W, TotalRounds, StdDevs, NegativeHistogram, Buckets)}
+ of
+ {_, _, [], []} when InvOP < 100 ->
+ {comment, {tp, TopPrecision, op, InvOP}};
+ {_, _, [], []} ->
+ %% If the probability for getting this Outlier is lower than
+ %% 1/100, then this is fishy!
+ stats_standard_normal(
+ Fun, NewS, Retries, {outlier_fishy, InvOP});
+ BucketErrors ->
+ stats_standard_normal(
+ Fun, NewS, Retries, BucketErrors)
+ end.
+%%
+stats_standard_normal(Fun, S, Retries, Failure) ->
+ case Retries - 1 of
+ 0 ->
+ ct:fail(Failure);
+ NewRetries ->
+ ct:pal("Retry due to TC glitch: ~p", [Failure]),
+ stats_standard_normal(Fun, S, NewRetries)
+ end.
%%
stats_standard_normal(
InvDelta, Buckets, PositiveHistogram, NegativeHistogram, Outlier,
@@ -544,7 +567,7 @@ stats_standard_normal(
Fun, S, Rounds, StopTime, Rounds, TotalRounds + Rounds);
_ ->
{PositiveHistogram, NegativeHistogram,
- Outlier, TotalRounds + Rounds}
+ Outlier, TotalRounds + Rounds, S}
end;
stats_standard_normal(
InvDelta, Buckets, PositiveHistogram, NegativeHistogram, Outlier,
@@ -571,9 +594,6 @@ increment_bucket(Bucket, Array) ->
array:set(Bucket, array:get(Bucket, Array) + 1, Array).
check_histogram(W, Rounds, StdDevs, Histogram, Buckets) ->
- %%PrevBucket = 512,
- %%Bucket = PrevBucket - 1,
- %%P = 0.5 * math:erfc(PrevBucket / W),
TargetP = 0.5 * math:erfc(Buckets / W),
P = 0.0,
N = 0,
@@ -592,7 +612,7 @@ check_histogram(
P = 0.5 * math:erfc(Bucket / W),
BucketP = P - PrevP,
if
- TargetP =< BucketP ->
+ BucketP < TargetP ->
check_histogram(
W, Rounds, StdDevs, Histogram, TargetP,
Bucket - 1, PrevBucket, PrevP, N);
@@ -604,7 +624,7 @@ check_histogram(
UpperLimit = ceil(Exp + Threshold),
if
N < LowerLimit; UpperLimit < N ->
- [#{bucket => {Bucket, PrevBucket}, n => N, exp => Exp,
+ [#{bucket => {Bucket, PrevBucket}, n => N,
lower => LowerLimit, upper => UpperLimit} |
check_histogram(
W, Rounds, StdDevs, Histogram, TargetP,
@@ -722,12 +742,12 @@ uniform_real_conv_check(M, E, Gen) ->
[["16#",integer_to_list(G,16),$\s]||G<-Gen]]),
ct:fail({neq, FF, F})
catch
- Error:Reason ->
+ Error:Reason:Stacktrace ->
ct:pal(
"~w:~p ~s: ~s~n",
[Error, Reason, rand:float2str(F),
[["16#",integer_to_list(G,16),$\s]||G<-Gen]]),
- ct:fail({Error, Reason, F, erlang:get_stacktrace()})
+ ct:fail({Error, Reason, F, Stacktrace})
end.
diff --git a/lib/stdlib/test/re_SUITE.erl b/lib/stdlib/test/re_SUITE.erl
index 71f86e32e5..7b82647416 100644
--- a/lib/stdlib/test/re_SUITE.erl
+++ b/lib/stdlib/test/re_SUITE.erl
@@ -894,10 +894,13 @@ match_limit(Config) when is_list(Config) ->
%% Test that we get sub-binaries if subject is a binary and we capture
%% binaries.
sub_binaries(Config) when is_list(Config) ->
- Bin = list_to_binary(lists:seq(1,255)),
- {match,[B,C]}=re:run(Bin,"(a)",[{capture,all,binary}]),
- 255 = binary:referenced_byte_size(B),
- 255 = binary:referenced_byte_size(C),
- {match,[D]}=re:run(Bin,"(a)",[{capture,[1],binary}]),
- 255 = binary:referenced_byte_size(D),
+ %% The GC can auto-convert tiny sub-binaries to heap binaries, so we
+ %% extract large sequences to make the test more stable.
+ Bin = << <<I>> || I <- lists:seq(1, 4096) >>,
+ {match,[B,C]}=re:run(Bin,"a(.+)$",[{capture,all,binary}]),
+ true = byte_size(B) =/= byte_size(C),
+ 4096 = binary:referenced_byte_size(B),
+ 4096 = binary:referenced_byte_size(C),
+ {match,[D]}=re:run(Bin,"a(.+)$",[{capture,[1],binary}]),
+ 4096 = binary:referenced_byte_size(D),
ok.
diff --git a/lib/stdlib/test/stdlib_bench_SUITE.erl b/lib/stdlib/test/stdlib_bench_SUITE.erl
index 294898a932..2364e8376f 100644
--- a/lib/stdlib/test/stdlib_bench_SUITE.erl
+++ b/lib/stdlib/test/stdlib_bench_SUITE.erl
@@ -348,12 +348,16 @@ do_tests(Test, ParamSet, Config) ->
{Parallelism, Message} = bench_params(ParamSet),
Fun = create_clients(Message, ServerMod, Client, Parallelism),
{TotalLoops, AllPidTime} = run_test(Fun),
- PerSecond = ?CALLS_PER_LOOP * round((1000 * TotalLoops) / AllPidTime),
- ct_event:notify(
- #event{
- name = benchmark_data,
- data = [{suite,BenchmarkSuite},{value,PerSecond}]}),
- PerSecond.
+ try ?CALLS_PER_LOOP * round((1000 * TotalLoops) / AllPidTime) of
+ PerSecond ->
+ ct_event:notify(
+ #event{
+ name = benchmark_data,
+ data = [{suite,BenchmarkSuite},{value,PerSecond}]}),
+ PerSecond
+ catch error:badarith ->
+ "Time measurement is not working"
+ end.
-define(COUNTER, n).
diff --git a/lib/stdlib/test/string_SUITE.erl b/lib/stdlib/test/string_SUITE.erl
index d02a6eac0a..fdff2d24b8 100644
--- a/lib/stdlib/test/string_SUITE.erl
+++ b/lib/stdlib/test/string_SUITE.erl
@@ -486,6 +486,10 @@ to_float(_) ->
prefix(_) ->
?TEST("", ["a"], nomatch),
?TEST("a", [""], "a"),
+ ?TEST("a", [[[]]], "a"),
+ ?TEST("a", [<<>>], "a"),
+ ?TEST("a", [[<<>>]], "a"),
+ ?TEST("a", [[[<<>>]]], "a"),
?TEST("b", ["a"], nomatch),
?TEST("a", ["a"], ""),
?TEST("å", ["a"], nomatch),
@@ -877,9 +881,9 @@ test_1(Line, Func, Str, Args, Exp) ->
catch
error:Exp ->
ok;
- error:Reason ->
+ error:Reason:Stacktrace ->
io:format("~p:~p: Crash ~p ~p~n",
- [?MODULE,Line, Reason, erlang:get_stacktrace()]),
+ [?MODULE,Line, Reason, Stacktrace]),
exit({error, Func})
end.
@@ -944,10 +948,10 @@ check_types(Line, Func, [Str|_], Res) ->
io:format("Failed: ~p ~p: ~p ~p~n",[Line, Func, T1, T2]),
io:format(" ~p => ~p~n", [Str, Res]),
error;
- _:Reason ->
- io:format("Crash: ~p in~n ~p~n",[Reason, erlang:get_stacktrace()]),
+ _:Reason:Stacktrace ->
+ io:format("Crash: ~p in~n ~p~n",[Reason, Stacktrace]),
io:format("Failed: ~p ~p: ~p => ~p~n", [Line, Func, Str, Res]),
- exit({Reason, erlang:get_stacktrace()})
+ exit({Reason, Stacktrace})
end.
check_types_1(T, T) ->
diff --git a/lib/stdlib/test/tar_SUITE.erl b/lib/stdlib/test/tar_SUITE.erl
index 4061008812..32a33283d1 100644
--- a/lib/stdlib/test/tar_SUITE.erl
+++ b/lib/stdlib/test/tar_SUITE.erl
@@ -28,7 +28,7 @@
extract_from_open_file/1, symlinks/1, open_add_close/1, cooked_compressed/1,
memory/1,unicode/1,read_other_implementations/1,
sparse/1, init/1, leading_slash/1, dotdot/1,
- roundtrip_metadata/1]).
+ roundtrip_metadata/1, apply_file_info_opts/1]).
-include_lib("common_test/include/ct.hrl").
-include_lib("kernel/include/file.hrl").
@@ -42,7 +42,8 @@ all() ->
extract_filtered,
symlinks, open_add_close, cooked_compressed, memory, unicode,
read_other_implementations,
- sparse,init,leading_slash,dotdot,roundtrip_metadata].
+ sparse,init,leading_slash,dotdot,roundtrip_metadata,
+ apply_file_info_opts].
groups() ->
[].
@@ -989,6 +990,31 @@ do_roundtrip_metadata(Dir, File) ->
ok
end.
+apply_file_info_opts(Config) when is_list(Config) ->
+ ok = file:set_cwd(proplists:get_value(priv_dir, Config)),
+
+ ok = file:make_dir("empty_directory"),
+ ok = file:write_file("file", "contents"),
+
+ Opts = [{atime, 0}, {mtime, 0}, {ctime, 0}, {uid, 0}, {gid, 0}],
+ TarFile = "reproducible.tar",
+ {ok, Tar} = erl_tar:open(TarFile, [write]),
+ ok = erl_tar:add(Tar, "file", Opts),
+ ok = erl_tar:add(Tar, "empty_directory", Opts),
+ ok = erl_tar:add(Tar, <<"contents">>, "memory_file", Opts),
+ erl_tar:close(Tar),
+
+ ok = file:make_dir("extracted"),
+ erl_tar:extract(TarFile, [{cwd, "extracted"}]),
+
+ {ok, #file_info{mtime=0}} =
+ file:read_file_info("extracted/empty_directory", [{time, posix}]),
+ {ok, #file_info{mtime=0}} =
+ file:read_file_info("extracted/file", [{time, posix}]),
+ {ok, #file_info{mtime=0}} =
+ file:read_file_info("extracted/memory_file", [{time, posix}]),
+
+ ok.
%% Delete the given list of files.
delete_files([]) -> ok;
diff --git a/lib/stdlib/test/unicode_util_SUITE.erl b/lib/stdlib/test/unicode_util_SUITE.erl
index 632d9ae6e6..40b1c260a5 100644
--- a/lib/stdlib/test/unicode_util_SUITE.erl
+++ b/lib/stdlib/test/unicode_util_SUITE.erl
@@ -136,10 +136,10 @@ verify_gc(Line0, N, Acc) ->
io:format("Expected: ~p~n", [Res]),
io:format("Got: ~w~n", [Other]),
Acc+1;
- Cl:R ->
+ Cl:R:Stacktrace ->
io:format("~p: ~ts => |~tp|~n",[N, Line, Str]),
io:format("Expected: ~p~n", [Res]),
- erlang:raise(Cl,R,erlang:get_stacktrace())
+ erlang:raise(Cl,R,Stacktrace)
end.
gc_test_data([[247]|Rest], Str, [First|GCs]) ->
@@ -175,29 +175,29 @@ verify_nfd(Data0, LineNo, _Acc) ->
C3GC = fetch(C1, fun unicode_util:nfd/1),
C3GC = fetch(C2, fun unicode_util:nfd/1),
C3GC = fetch(C3, fun unicode_util:nfd/1)
- catch _Cl:{badmatch, Other} = _R->
+ catch _Cl:{badmatch, Other} = _R: Stacktrace ->
io:format("Failed: ~p~nInput: ~ts~n\t=> ~w |~ts|~n",[LineNo, Data1, C1, C1]),
io:format("Expected: ~ts ~w~n", [C3GC, C3GC]),
io:format("Got: ~ts ~w~n", [Other, Other]),
- erlang:raise(_Cl,_R,erlang:get_stacktrace());
- Cl:R ->
+ erlang:raise(_Cl,_R,Stacktrace);
+ Cl:R:Stacktrace ->
io:format("~p: ~ts => |~tp|~n",[LineNo, Data1, C1]),
io:format("Expected: ~p~n", [C3]),
- erlang:raise(Cl,R,erlang:get_stacktrace())
+ erlang:raise(Cl,R,Stacktrace)
end,
C5GC = fetch(C5, fun unicode_util:gc/1),
try
C5GC = fetch(C4, fun unicode_util:nfd/1),
C5GC = fetch(C5, fun unicode_util:nfd/1)
- catch _Cl2:{badmatch, Other2} = _R2->
+ catch _Cl2:{badmatch, Other2} = _R2:Stacktrace2 ->
io:format("Failed: ~p~nInput: ~ts~n\t=> ~w |~ts|~n",[LineNo, Data1, C1, C1]),
io:format("Expected: ~ts ~w~n", [C5GC, C5GC]),
io:format("Got: ~ts ~w~n", [Other2, Other2]),
- erlang:raise(_Cl2,_R2,erlang:get_stacktrace());
- Cl2:R2 ->
+ erlang:raise(_Cl2,_R2,Stacktrace2);
+ Cl2:R2:Stacktrace2 ->
io:format("~p: ~ts => |~tp|~n",[LineNo, Data1, C1]),
io:format("Expected: ~p~n", [C5]),
- erlang:raise(Cl2,R2,erlang:get_stacktrace())
+ erlang:raise(Cl2,R2,Stacktrace2)
end,
ok.
@@ -218,29 +218,29 @@ verify_nfc(Data0, LineNo, _Acc) ->
C2GC = fetch(C1, fun unicode_util:nfc/1),
C2GC = fetch(C2, fun unicode_util:nfc/1),
C2GC = fetch(C3, fun unicode_util:nfc/1)
- catch _Cl:{badmatch, Other} = _R->
+ catch _Cl:{badmatch, Other} = _R:Stacktrace ->
io:format("Failed: ~p~nInput: ~ts~n\t=> ~w |~ts|~n",[LineNo, Data1, C1, C1]),
io:format("Expected: ~ts ~w~n", [C2GC, C2GC]),
io:format("Got: ~ts ~w~n", [Other, Other]),
- erlang:raise(_Cl,_R,erlang:get_stacktrace());
- Cl:R ->
+ erlang:raise(_Cl,_R,Stacktrace);
+ Cl:R:Stacktrace ->
io:format("~p: ~ts => |~tp|~n",[LineNo, Data1, C1]),
io:format("Expected: ~p~n", [C3]),
- erlang:raise(Cl,R,erlang:get_stacktrace())
+ erlang:raise(Cl,R,Stacktrace)
end,
C4GC = fetch(C4, fun unicode_util:gc/1),
try
C4GC = fetch(C4, fun unicode_util:nfc/1),
C4GC = fetch(C5, fun unicode_util:nfc/1)
- catch _Cl2:{badmatch, Other2} = _R2->
+ catch _Cl2:{badmatch, Other2} = _R2:Stacktrace2 ->
io:format("Failed: ~p~nInput: ~ts~n\t=> ~w |~ts|~n",[LineNo, Data1, C1, C1]),
io:format("Expected: ~ts ~w~n", [C4GC, C4GC]),
io:format("Got: ~ts ~w~n", [Other2, Other2]),
- erlang:raise(_Cl2,_R2,erlang:get_stacktrace());
- Cl2:R2 ->
+ erlang:raise(_Cl2,_R2,Stacktrace2);
+ Cl2:R2:Stacktrace2 ->
io:format("~p: ~ts => |~tp|~n",[LineNo, Data1, C1]),
io:format("Expected: ~p~n", [C5]),
- erlang:raise(Cl2,R2,erlang:get_stacktrace())
+ erlang:raise(Cl2,R2,Stacktrace2)
end,
ok.
@@ -263,15 +263,15 @@ verify_nfkd(Data0, LineNo, _Acc) ->
C5GC = lists:flatten(fetch(C3, fun unicode_util:nfkd/1)),
C5GC = lists:flatten(fetch(C4, fun unicode_util:nfkd/1)),
C5GC = lists:flatten(fetch(C5, fun unicode_util:nfkd/1))
- catch _Cl:{badmatch, Other} = _R->
+ catch _Cl:{badmatch, Other} = _R:Stacktrace ->
io:format("Failed: ~p~nInput: ~ts~n\t=> ~w |~ts|~n",[LineNo, Data1, C5, C5]),
io:format("Expected: ~ts ~w~n", [C5GC, C5GC]),
io:format("Got: ~ts ~w~n", [Other, Other]),
- erlang:raise(_Cl,_R,erlang:get_stacktrace());
- Cl:R ->
+ erlang:raise(_Cl,_R,Stacktrace);
+ Cl:R:Stacktrace ->
io:format("~p: ~ts => |~tp|~n",[LineNo, Data1, C1]),
io:format("Expected: ~p~n", [C3]),
- erlang:raise(Cl,R,erlang:get_stacktrace())
+ erlang:raise(Cl,R,Stacktrace)
end,
ok.
@@ -296,15 +296,15 @@ verify_nfkc(Data0, LineNo, _Acc) ->
C4GC = lists:flatten(fetch(C4, fun unicode_util:nfkc/1)),
C4GC = lists:flatten(fetch(C5, fun unicode_util:nfkc/1))
- catch _Cl:{badmatch, Other} = _R->
+ catch _Cl:{badmatch, Other} = _R:Stacktrace ->
io:format("Failed: ~p~nInput: ~ts~n\t=> ~w |~ts|~n",[LineNo, Data1, C4, C4]),
io:format("Expected: ~ts ~w~n", [C4GC, C4GC]),
io:format("Got: ~ts ~w~n", [Other, Other]),
- erlang:raise(_Cl,_R,erlang:get_stacktrace());
- Cl:R ->
+ erlang:raise(_Cl,_R,Stacktrace);
+ Cl:R:Stacktrace ->
io:format("~p: ~ts => |~tp|~n",[LineNo, Data1, C1]),
io:format("Expected: ~p~n", [C3]),
- erlang:raise(Cl,R,erlang:get_stacktrace())
+ erlang:raise(Cl,R,Stacktrace)
end,
ok.
diff --git a/lib/stdlib/test/uri_string_SUITE.erl b/lib/stdlib/test/uri_string_SUITE.erl
index fef356355c..92f8bb3292 100644
--- a/lib/stdlib/test/uri_string_SUITE.erl
+++ b/lib/stdlib/test/uri_string_SUITE.erl
@@ -22,7 +22,7 @@
-include_lib("common_test/include/ct.hrl").
-export([all/0, suite/0,groups/0,
- normalize/1,
+ normalize/1, normalize_map/1, normalize_return_map/1, normalize_negative/1,
parse_binary_fragment/1, parse_binary_host/1, parse_binary_host_ipv4/1,
parse_binary_host_ipv6/1,
parse_binary_path/1, parse_binary_pct_encoded_fragment/1, parse_binary_pct_encoded_query/1,
@@ -68,6 +68,9 @@ suite() ->
all() ->
[
normalize,
+ normalize_map,
+ normalize_return_map,
+ normalize_negative,
parse_binary_scheme,
parse_binary_userinfo,
parse_binary_pct_encoded_userinfo,
@@ -912,6 +915,56 @@ normalize(_Config) ->
<<"tftp://localhost">> =
uri_string:normalize(<<"tftp://localhost:69">>).
+normalize_map(_Config) ->
+ "/a/g" = uri_string:normalize(#{path => "/a/b/c/./../../g"}),
+ <<"mid/6">> = uri_string:normalize(#{path => <<"mid/content=5/../6">>}),
+ "http://localhost-%C3%B6rebro/a/g" =
+ uri_string:normalize(#{scheme => "http",port => 80,path => "/a/b/c/./../../g",
+ host => "localhost-örebro"}),
+ <<"http://localhost-%C3%B6rebro/a/g">> =
+ uri_string:normalize(#{scheme => <<"http">>,port => 80,
+ path => <<"/a/b/c/./../../g">>,
+ host => <<"localhost-örebro"/utf8>>}),
+ <<"https://localhost/">> =
+ uri_string:normalize(#{scheme => <<"https">>,port => 443,path => <<>>,
+ host => <<"localhost">>}),
+ <<"https://localhost:445/">> =
+ uri_string:normalize(#{scheme => <<"https">>,port => 445,path => <<>>,
+ host => <<"localhost">>}),
+ <<"ftp://localhost">> =
+ uri_string:normalize(#{scheme => <<"ftp">>,port => 21,path => <<>>,
+ host => <<"localhost">>}),
+ <<"ssh://localhost">> =
+ uri_string:normalize(#{scheme => <<"ssh">>,port => 22,path => <<>>,
+ host => <<"localhost">>}),
+ <<"sftp://localhost">> =
+ uri_string:normalize(#{scheme => <<"sftp">>,port => 22,path => <<>>,
+ host => <<"localhost">>}),
+ <<"tftp://localhost">> =
+ uri_string:normalize(#{scheme => <<"tftp">>,port => 69,path => <<>>,
+ host => <<"localhost">>}).
+
+normalize_return_map(_Config) ->
+ #{scheme := "http",path := "/a/g",host := "localhost-örebro"} =
+ uri_string:normalize("http://localhos%74-%c3%b6rebro:80/a/b/c/./../../g",
+ [return_map]),
+ #{scheme := <<"http">>,path := <<"/a/g">>, host := <<"localhost-örebro"/utf8>>} =
+ uri_string:normalize(<<"http://localhos%74-%c3%b6rebro:80/a/b/c/./../../g">>,
+ [return_map]),
+ #{scheme := <<"https">>,path := <<"/">>, host := <<"localhost">>} =
+ uri_string:normalize(#{scheme => <<"https">>,port => 443,path => <<>>,
+ host => <<"localhost">>}, [return_map]).
+
+normalize_negative(_Config) ->
+ {error,invalid_uri,":"} =
+ uri_string:normalize("http://local>host"),
+ {error,invalid_uri,":"} =
+ uri_string:normalize(<<"http://local>host">>),
+ {error,invalid_uri,":"} =
+ uri_string:normalize("http://[192.168.0.1]", [return_map]),
+ {error,invalid_uri,":"} =
+ uri_string:normalize(<<"http://[192.168.0.1]">>, [return_map]).
+
interop_query_utf8(_Config) ->
Q = uri_string:compose_query([{"foo bar","1"}, {"合", "2"}]),
Uri = uri_string:recompose(#{path => "/", query => Q}),
diff --git a/lib/stdlib/test/zip_SUITE.erl b/lib/stdlib/test/zip_SUITE.erl
index 1dfcda4ed0..e5ba629c55 100644
--- a/lib/stdlib/test/zip_SUITE.erl
+++ b/lib/stdlib/test/zip_SUITE.erl
@@ -909,8 +909,7 @@ do_fd_leak(Bad, N) ->
ok ->
do_fd_leak(Bad, N + 1)
catch
- C:R ->
- Stk = erlang:get_stacktrace(),
+ C:R:Stk ->
io:format("Bad error after ~p attempts\n", [N]),
erlang:raise(C, R, Stk)
end.
diff --git a/lib/stdlib/vsn.mk b/lib/stdlib/vsn.mk
index 69d258c2f0..09a4d6fb50 100644
--- a/lib/stdlib/vsn.mk
+++ b/lib/stdlib/vsn.mk
@@ -1 +1 @@
-STDLIB_VSN = 3.4.3
+STDLIB_VSN = 3.4.5
diff --git a/lib/tools/doc/src/lcnt.xml b/lib/tools/doc/src/lcnt.xml
index 5bdfc60448..0c24375b91 100644
--- a/lib/tools/doc/src/lcnt.xml
+++ b/lib/tools/doc/src/lcnt.xml
@@ -371,7 +371,7 @@
<v>Serial = integer()</v>
</type>
<desc>
- <p>Creates a process id with creation 0. Example:</p>
+ <p>Creates a process id with creation 0.</p>
</desc>
</func>
diff --git a/lib/tools/doc/src/notes.xml b/lib/tools/doc/src/notes.xml
index 1edc08c9cd..45f276c09e 100644
--- a/lib/tools/doc/src/notes.xml
+++ b/lib/tools/doc/src/notes.xml
@@ -31,6 +31,46 @@
</header>
<p>This document describes the changes made to the Tools application.</p>
+<section><title>Tools 2.11.2</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p> A counting bug is corrected in <c>Cover</c>. The bug
+ was introduced in Erlang/OTP 18.0. </p>
+ <p>
+ Own Id: OTP-14817 Aux Id: PR 1641 </p>
+ </item>
+ <item>
+ <p>The <c>lcnt</c> server will no longer crash if
+ <c>lcnt:information/0</c> is called before
+ <c>lcnt:collect/0</c>.</p>
+ <p>
+ Own Id: OTP-14912</p>
+ </item>
+ <item>
+ <p><c>lcnt:collect</c> will now implicitly start the
+ <c>lcnt</c> server, as per the documentation.</p>
+ <p>
+ Own Id: OTP-14913</p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Improved indentation in emacs and various other updates.</p>
+ <p>
+ Own Id: OTP-14944</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Tools 2.11.1</title>
<section><title>Fixed Bugs and Malfunctions</title>
diff --git a/lib/tools/emacs/Makefile b/lib/tools/emacs/Makefile
index 35c93ba4ed..ea4d6cb723 100644
--- a/lib/tools/emacs/Makefile
+++ b/lib/tools/emacs/Makefile
@@ -54,8 +54,6 @@ EL_FILES = $(EMACS_FILES:%=%.el)
ELC_FILES = $(EMACS_FILES:%=%.elc)
-TEST_FILES = test.erl.indented test.erl.orig
-
# ----------------------------------------------------
# Targets
# ----------------------------------------------------
@@ -75,7 +73,7 @@ include $(ERL_TOP)/make/otp_release_targets.mk
release_spec: opt
$(INSTALL_DIR) "$(RELSYSDIR)/emacs"
- $(INSTALL_DATA) $(EL_FILES) $(README_FILES) $(TEST_FILES) \
+ $(INSTALL_DATA) $(EL_FILES) $(README_FILES) \
"$(RELSYSDIR)/emacs"
ifeq ($(DOCTYPE),pdf)
@@ -89,19 +87,3 @@ release_docs_spec: docs
$(INSTALL_DATA) $(MAN_FILES) "$(RELEASE_PATH)/man/man3"
endif
endif
-
-EMACS ?= emacs
-
-test_indentation:
- @rm -f test.erl
- @rm -f test_indent.el
- @echo '(load "erlang-start")' >> test_indent.el
- @echo '(find-file "test.erl.orig")' >> test_indent.el
- @echo "(require 'cl) ; required with Emacs < 23 for ignore-errors" >> test_indent.el
- @echo '(erlang-mode)' >> test_indent.el
- @echo '(toggle-debug-on-error)' >> test_indent.el
- @echo '(erlang-indent-current-buffer)' >> test_indent.el
- @echo '(write-file "test.erl")' >> test_indent.el
- $(EMACS) --batch -Q -L . -l test_indent.el
- diff -u test.erl.indented test.erl
- @echo "No differences between expected and actual indentation"
diff --git a/lib/tools/emacs/erlang-skels.el b/lib/tools/emacs/erlang-skels.el
index bdb3d9ad4a..534f50ab33 100644
--- a/lib/tools/emacs/erlang-skels.el
+++ b/lib/tools/emacs/erlang-skels.el
@@ -279,7 +279,8 @@ Please see the function `tempo-define-template'.")
'((erlang-skel-include erlang-skel-large-header)
"-behaviour(application)." n n
"%% Application callbacks" n
- "-export([start/2, stop/1])." n n
+ "-export([start/2, start_phase/3, stop/1, prep_stop/1," n>
+ "config_change/3])." n n
(erlang-skel-double-separator-start 3)
"%%% Application callbacks" n
(erlang-skel-double-separator-end 3) n
@@ -291,13 +292,14 @@ Please see the function `tempo-define-template'.")
"%% application. If the application is structured according to the OTP" n
"%% design principles as a supervision tree, this means starting the" n
"%% top supervisor of the tree." n
- "%%" n
- "%% @spec start(StartType, StartArgs) -> {ok, Pid} |" n
- "%% {ok, Pid, State} |" n
- "%% {error, Reason}" n
- "%% StartType = normal | {takeover, Node} | {failover, Node}" n
- "%% StartArgs = term()" n
(erlang-skel-separator-end 2)
+ "-spec start(StartType :: normal |" n>
+ "{takeover, Node :: node()} |" n>
+ "{failover, Node :: node()}," n>
+ "StartArgs :: term()) ->" n>
+ "{ok, Pid :: pid()} |" n>
+ "{ok, Pid :: pid(), State :: term()} |" n>
+ "{error, Reason :: term()}." n
"start(_StartType, _StartArgs) ->" n>
"case 'TopSupervisor':start_link() of" n>
"{ok, Pid} ->" n>
@@ -309,15 +311,52 @@ Please see the function `tempo-define-template'.")
(erlang-skel-separator-start 2)
"%% @private" n
"%% @doc" n
+ "%% top supervisor of the tree." n
+ "%% Starts an application with included applications, when" n
+ "%% synchronization is needed between processes in the different" n
+ "%% applications during startup."
+ (erlang-skel-separator-end 2)
+ "-spec start_phase(Phase :: atom()," n>
+ "StartType :: normal |" n>
+ "{takeover, Node :: node()} |" n>
+ "{failover, Node :: node()}," n>
+ "PhaseArgs :: term()) -> ok | {error, Reason :: term()}." n
+ "start_phase(_Phase, _StartType, _PhaseArgs) ->" n>
+ "ok."n
+ n
+ (erlang-skel-separator-start 2)
+ "%% @private" n
+ "%% @doc" n
"%% This function is called whenever an application has stopped. It" n
"%% is intended to be the opposite of Module:start/2 and should do" n
"%% any necessary cleaning up. The return value is ignored." n
- "%%" n
- "%% @spec stop(State) -> void()" n
(erlang-skel-separator-end 2)
+ "-spec stop(State :: term()) -> any()." n
"stop(_State) ->" n>
"ok." n
n
+ (erlang-skel-separator-start 2)
+ "%% @private" n
+ "%% @doc" n
+ "%% This function is called when an application is about to be stopped," n
+ "%% before shutting down the processes of the application." n
+ (erlang-skel-separator-end 2)
+ "-spec prep_stop(State :: term()) -> NewState :: term()." n
+ "prep_stop(State) ->" n>
+ "State." n
+ n
+ (erlang-skel-separator-start 2)
+ "%% @private" n
+ "%% @doc" n
+ "%% This function is called by an application after a code replacement," n
+ "%% if the configuration parameters have changed." n
+ (erlang-skel-separator-end 2)
+ "-spec config_change(Changed :: [{Par :: atom(), Val :: term()}]," n>
+ "New :: [{Par :: atom(), Val :: term()}]," n>
+ "Removed :: [Par :: atom()]) -> ok." n
+ "config_change(_Changed, _New, _Removed) ->" n>
+ "ok." n
+ n
(erlang-skel-double-separator-start 3)
"%%% Internal functions" n
(erlang-skel-double-separator-end 3)
@@ -343,9 +382,12 @@ Please see the function `tempo-define-template'.")
(erlang-skel-separator-start 2)
"%% @doc" n
"%% Starts the supervisor" n
- "%%" n
- "%% @spec start_link() -> {ok, Pid} | ignore | {error, Error}" n
(erlang-skel-separator-end 2)
+ "-spec start_link() -> {ok, Pid :: pid()} |" n>
+ "{error, {already_started, Pid :: pid()}} |" n>
+ "{error, {shutdown, term()}} |" n>
+ "{error, term()} |" n>
+ "ignore." n
"start_link() ->" n>
"supervisor:start_link({local, ?SERVER}, ?MODULE, [])." n
n
@@ -359,11 +401,11 @@ Please see the function `tempo-define-template'.")
"%% this function is called by the new process to find out about" n
"%% restart strategy, maximum restart intensity, and child" n
"%% specifications." n
- "%%" n
- "%% @spec init(Args) -> {ok, {SupFlags, [ChildSpec]}} |" n
- "%% ignore |" n
- "%% {error, Reason}" n
(erlang-skel-separator-end 2)
+ "-spec init(Args :: term()) ->" n>
+ "{ok, {SupFlags :: supervisor:sup_flags()," n>
+ "[ChildSpec :: supervisor:child_spec()]}} |" n>
+ "ignore." n
"init([]) ->" n
"" n>
"SupFlags = #{strategy => one_for_one," n>
@@ -406,9 +448,11 @@ Please see the function `tempo-define-template'.")
(erlang-skel-separator-start 2)
"%% @doc" n
"%% Starts the supervisor bridge" n
- "%%" n
- "%% @spec start_link() -> {ok, Pid} | ignore | {error, Error}" n
(erlang-skel-separator-end 2)
+ "-spec start_link() -> {ok, Pid :: pid()} |" n>
+ "{error, {already_started, Pid :: pid()}} |" n>
+ "{error, term()} |" n>
+ "ignore." n
"start_link() ->" n>
"supervisor_bridge:start_link({local, ?SERVER}, ?MODULE, [])." n
n
@@ -422,11 +466,10 @@ Please see the function `tempo-define-template'.")
"%% which calls Module:init/1 to start the subsystem. To ensure a" n
"%% synchronized start-up procedure, this function does not return" n
"%% until Module:init/1 has returned." n
- "%%" n
- "%% @spec init(Args) -> {ok, Pid, State} |" n
- "%% ignore |" n
- "%% {error, Reason}" n
(erlang-skel-separator-end 2)
+ "-spec init(Args :: term()) -> {ok, Pid :: pid(), State :: term()} |" n>
+ "{error, Error :: term()} |" n>
+ "ignore." n
"init([]) ->" n>
"case 'AModule':start_link() of" n>
"{ok, Pid} ->" n>
@@ -442,10 +485,9 @@ Please see the function `tempo-define-template'.")
"%% to terminate. It should be the opposite of Module:init/1 and stop" n
"%% the subsystem and do any necessary cleaning up.The return value is" n
"%% ignored." n
- "%%" n
- "%% @spec terminate(Reason, State) -> void()" n
(erlang-skel-separator-end 2)
- "terminate(Reason, State) ->" n>
+ "-spec terminate(Reason :: shutdown | term(), State :: term()) -> any()." n
+ "terminate(_Reason, _State) ->" n>
"'AModule':stop()," n>
"ok." n
n
@@ -464,9 +506,8 @@ Please see the function `tempo-define-template'.")
"-export([start_link/0])." n n
"%% gen_server callbacks" n
- "-export([init/1, handle_call/3, handle_cast/2, "
- "handle_info/2," n>
- "terminate/2, code_change/3])." n n
+ "-export([init/1, handle_call/3, handle_cast/2, handle_info/2," n>
+ "terminate/2, code_change/3, format_status/2])." n n
"-define(SERVER, ?MODULE)." n n
@@ -478,9 +519,11 @@ Please see the function `tempo-define-template'.")
(erlang-skel-separator-start 2)
"%% @doc" n
"%% Starts the server" n
- "%%" n
- "%% @spec start_link() -> {ok, Pid} | ignore | {error, Error}" n
(erlang-skel-separator-end 2)
+ "-spec start_link() -> {ok, Pid :: pid()} |" n>
+ "{error, Error :: {already_started, pid()}} |" n>
+ "{error, Error :: term()} |" n>
+ "ignore." n
"start_link() ->" n>
"gen_server:start_link({local, ?SERVER}, ?MODULE, [], [])." n
n
@@ -492,12 +535,12 @@ Please see the function `tempo-define-template'.")
"%% @private" n
"%% @doc" n
"%% Initializes the server" n
- "%%" n
- "%% @spec init(Args) -> {ok, State} |" n
- "%% {ok, State, Timeout} |" n
- "%% ignore |" n
- "%% {stop, Reason}" n
(erlang-skel-separator-end 2)
+ "-spec init(Args :: term()) -> {ok, State :: term()} |" n>
+ "{ok, State :: term(), Timeout :: timeout()} |" n>
+ "{ok, State :: term(), hibernate} |" n>
+ "{stop, Reason :: term()} |" n>
+ "ignore." n
"init([]) ->" n>
"process_flag(trap_exit, true)," n>
"{ok, #state{}}." n
@@ -506,15 +549,16 @@ Please see the function `tempo-define-template'.")
"%% @private" n
"%% @doc" n
"%% Handling call messages" n
- "%%" n
- "%% @spec handle_call(Request, From, State) ->" n
- "%% {reply, Reply, State} |" n
- "%% {reply, Reply, State, Timeout} |" n
- "%% {noreply, State} |" n
- "%% {noreply, State, Timeout} |" n
- "%% {stop, Reason, Reply, State} |" n
- "%% {stop, Reason, State}" n
(erlang-skel-separator-end 2)
+ "-spec handle_call(Request :: term(), From :: {pid(), term()}, State :: term()) ->" n>
+ "{reply, Reply :: term(), NewState :: term()} |" n>
+ "{reply, Reply :: term(), NewState :: term(), Timeout :: timeout()} |" n>
+ "{reply, Reply :: term(), NewState :: term(), hibernate} |" n>
+ "{noreply, NewState :: term()} |" n>
+ "{noreply, NewState :: term(), Timeout :: timeout()} |" n>
+ "{noreply, NewState :: term(), hibernate} |" n>
+ "{stop, Reason :: term(), Reply :: term(), NewState :: term()} |" n>
+ "{stop, Reason :: term(), NewState :: term()}." n
"handle_call(_Request, _From, State) ->" n>
"Reply = ok," n>
"{reply, Reply, State}." n
@@ -523,23 +567,25 @@ Please see the function `tempo-define-template'.")
"%% @private" n
"%% @doc" n
"%% Handling cast messages" n
- "%%" n
- "%% @spec handle_cast(Msg, State) -> {noreply, State} |" n
- "%% {noreply, State, Timeout} |" n
- "%% {stop, Reason, State}" n
(erlang-skel-separator-end 2)
- "handle_cast(_Msg, State) ->" n>
+ "-spec handle_cast(Request :: term(), State :: term()) ->" n>
+ "{noreply, NewState :: term()} |" n>
+ "{noreply, NewState :: term(), Timeout :: timeout()} |" n>
+ "{noreply, NewState :: term(), hibernate} |" n>
+ "{stop, Reason :: term(), NewState :: term()}." n
+ "handle_cast(_Request, State) ->" n>
"{noreply, State}." n
n
(erlang-skel-separator-start 2)
"%% @private" n
"%% @doc" n
"%% Handling all non call/cast messages" n
- "%%" n
- "%% @spec handle_info(Info, State) -> {noreply, State} |" n
- "%% {noreply, State, Timeout} |" n
- "%% {stop, Reason, State}" n
(erlang-skel-separator-end 2)
+ "-spec handle_info(Info :: timeout() | term(), State :: term()) ->" n>
+ "{noreply, NewState :: term()} |" n>
+ "{noreply, NewState :: term(), Timeout :: timeout()} |" n>
+ "{noreply, NewState :: term(), hibernate} |" n>
+ "{stop, Reason :: normal | term(), NewState :: term()}." n
"handle_info(_Info, State) ->" n>
"{noreply, State}." n
n
@@ -550,9 +596,9 @@ Please see the function `tempo-define-template'.")
"%% terminate. It should be the opposite of Module:init/1 and do any" n
"%% necessary cleaning up. When it returns, the gen_server terminates" n
"%% with Reason. The return value is ignored." n
- "%%" n
- "%% @spec terminate(Reason, State) -> void()" n
(erlang-skel-separator-end 2)
+ "-spec terminate(Reason :: normal | shutdown | {shutdown, term()} | term()," n>
+ "State :: term()) -> any()." n
"terminate(_Reason, _State) ->" n>
"ok." n
n
@@ -560,12 +606,26 @@ Please see the function `tempo-define-template'.")
"%% @private" n
"%% @doc" n
"%% Convert process state when code is changed" n
- "%%" n
- "%% @spec code_change(OldVsn, State, Extra) -> {ok, NewState}" n
(erlang-skel-separator-end 2)
+ "-spec code_change(OldVsn :: term() | {down, term()}," n>
+ "State :: term()," n>
+ "Extra :: term()) -> {ok, NewState :: term()} |" n>
+ "{error, Reason :: term()}." n
"code_change(_OldVsn, State, _Extra) ->" n>
"{ok, State}." n
n
+ (erlang-skel-separator-start 2)
+ "%% @private" n
+ "%% @doc" n
+ "%% This function is called for changing the form and appearance" n
+ "%% of gen_server status when it is returned from sys:get_status/1,2" n
+ "%% or when it appears in termination error logs." n
+ (erlang-skel-separator-end 2)
+ "-spec format_status(Opt :: normal | terminate," n>
+ "Status :: list()) -> Status :: term()." n
+ "format_status(_Opt, Status) ->" n>
+ "Status." n
+ n
(erlang-skel-double-separator-start 3)
"%%% Internal functions" n
(erlang-skel-double-separator-end 3)
@@ -581,8 +641,8 @@ Please see the function `tempo-define-template'.")
"-export([start_link/0, add_handler/0])." n n
"%% gen_event callbacks" n
- "-export([init/1, handle_event/2, handle_call/2, " n>
- "handle_info/2, terminate/2, code_change/3])." n n
+ "-export([init/1, handle_event/2, handle_call/2, handle_info/2," n>
+ "terminate/2, code_change/3, format_status/2])." n n
"-define(SERVER, ?MODULE)." n n
@@ -594,18 +654,17 @@ Please see the function `tempo-define-template'.")
(erlang-skel-separator-start 2)
"%% @doc" n
"%% Creates an event manager" n
- "%%" n
- "%% @spec start_link() -> {ok, Pid} | {error, Error}" n
(erlang-skel-separator-end 2)
+ "-spec start_link() -> {ok, Pid :: pid()} |" n>
+ "{error, Error :: {already_started, pid()} | term()}." n
"start_link() ->" n>
"gen_event:start_link({local, ?SERVER})." n
n
(erlang-skel-separator-start 2)
"%% @doc" n
"%% Adds an event handler" n
- "%%" n
- "%% @spec add_handler() -> ok | {'EXIT', Reason} | term()" n
(erlang-skel-separator-end 2)
+ "-spec add_handler() -> ok | {'EXIT', Reason :: term()} | term()." n
"add_handler() ->" n>
"gen_event:add_handler(?SERVER, ?MODULE, [])." n
n
@@ -617,9 +676,11 @@ Please see the function `tempo-define-template'.")
"%% @doc" n
"%% Whenever a new event handler is added to an event manager," n
"%% this function is called to initialize the event handler." n
- "%%" n
- "%% @spec init(Args) -> {ok, State}" n
(erlang-skel-separator-end 2)
+ "-spec init(Args :: term() | {Args :: term(), Term :: term()}) ->" n>
+ "{ok, State :: term()} |" n>
+ "{ok, State :: term(), hibernate} |" n>
+ "{error, Reason :: term()}." n
"init([]) ->" n>
"{ok, #state{}}." n
n
@@ -629,12 +690,13 @@ Please see the function `tempo-define-template'.")
"%% Whenever an event manager receives an event sent using" n
"%% gen_event:notify/2 or gen_event:sync_notify/2, this function is" n
"%% called for each installed event handler to handle the event." n
- "%%" n
- "%% @spec handle_event(Event, State) ->" n
- "%% {ok, State} |" n
- "%% {swap_handler, Args1, State1, Mod2, Args2} |"n
- "%% remove_handler" n
(erlang-skel-separator-end 2)
+ "-spec handle_event(Event :: term(), State :: term()) ->" n>
+ "{ok, NewState :: term()} |" n>
+ "{ok, NewState :: term(), hibernate} |" n>
+ "remove_handler |" n>
+ "{swap_handler, Args1 :: term(), NewState :: term()," n>
+ "Handler2 :: atom() | {atom(), term()} , Args2 :: term()}." n>
"handle_event(_Event, State) ->" n>
"{ok, State}." n
n
@@ -644,12 +706,13 @@ Please see the function `tempo-define-template'.")
"%% Whenever an event manager receives a request sent using" n
"%% gen_event:call/3,4, this function is called for the specified" n
"%% event handler to handle the request." n
- "%%" n
- "%% @spec handle_call(Request, State) ->" n
- "%% {ok, Reply, State} |" n
- "%% {swap_handler, Reply, Args1, State1, Mod2, Args2} |" n
- "%% {remove_handler, Reply}" n
(erlang-skel-separator-end 2)
+ "-spec handle_call(Request :: term(), State :: term()) ->" n>
+ "{ok, Reply :: term(), NewState :: term()} |" n>
+ "{ok, Reply :: term(), NewState :: term(), hibernate} |" n>
+ "{remove_handler, Reply :: term()} |" n>
+ "{swap_handler, Reply :: term(), Args1 :: term(), NewState :: term()," n>
+ "Handler2 :: atom() | {atom(), term()}, Args2 :: term()}." n
"handle_call(_Request, State) ->" n>
"Reply = ok," n>
"{ok, Reply, State}." n
@@ -660,12 +723,13 @@ Please see the function `tempo-define-template'.")
"%% This function is called for each installed event handler when" n
"%% an event manager receives any other message than an event or a" n
"%% synchronous request (or a system message)." n
- "%%" n
- "%% @spec handle_info(Info, State) ->" n
- "%% {ok, State} |" n
- "%% {swap_handler, Args1, State1, Mod2, Args2} |" n
- "%% remove_handler" n
(erlang-skel-separator-end 2)
+ "-spec handle_info(Info :: term(), State :: term()) ->" n>
+ "{ok, NewState :: term()} |" n>
+ "{ok, NewState :: term(), hibernate} |" n>
+ "remove_handler |" n>
+ "{swap_handler, Args1 :: term(), NewState :: term()," n>
+ "Handler2 :: atom() | {atom(), term()}, Args2 :: term()}." n
"handle_info(_Info, State) ->" n>
"{ok, State}." n
n
@@ -675,22 +739,40 @@ Please see the function `tempo-define-template'.")
"%% Whenever an event handler is deleted from an event manager, this" n
"%% function is called. It should be the opposite of Module:init/1 and" n
"%% do any necessary cleaning up." n
- "%%" n
- "%% @spec terminate(Reason, State) -> void()" n
(erlang-skel-separator-end 2)
- "terminate(_Reason, _State) ->" n>
+ "-spec terminate(Arg :: {stop, Reason :: term()} |" n>
+ "stop |" n>
+ "remove_handler |" n>
+ "{error, {'EXIT', Reason :: term()}} |" n>
+ "{error, Term :: term()} |" n>
+ "term()," n>
+ "State :: term()) -> any()." n
+ "terminate(_Arg, _State) ->" n>
"ok." n
n
(erlang-skel-separator-start 2)
"%% @private" n
"%% @doc" n
"%% Convert process state when code is changed" n
- "%%" n
- "%% @spec code_change(OldVsn, State, Extra) -> {ok, NewState}" n
(erlang-skel-separator-end 2)
+ "-spec code_change(OldVsn :: term() | {down, term()}," n>
+ "State :: term()," n>
+ "Extra :: term()) -> {ok, NewState :: term()}." n
"code_change(_OldVsn, State, _Extra) ->" n>
"{ok, State}." n
n
+ (erlang-skel-separator-start 2)
+ "%% @private" n
+ "%% @doc" n
+ "%% This function is called for changing the form and appearance" n
+ "%% of gen_event status when it is returned from sys:get_status/1,2" n
+ "%% or when it appears in termination error logs." n
+ (erlang-skel-separator-end 2)
+ "-spec format_status(Opt :: normal | terminate," n>
+ "Status :: list()) -> Status :: term()." n
+ "format_status(_Opt, Status) ->" n>
+ "Status." n
+ n
(erlang-skel-double-separator-start 3)
"%%% Internal functions" n
(erlang-skel-double-separator-end 3)
diff --git a/lib/tools/emacs/erlang.el b/lib/tools/emacs/erlang.el
index 6b93d63182..b88f368746 100644
--- a/lib/tools/emacs/erlang.el
+++ b/lib/tools/emacs/erlang.el
@@ -4,7 +4,7 @@
;; Author: Anders Lindgren
;; Keywords: erlang, languages, processes
;; Date: 2011-12-11
-;; Version: 2.8.0
+;; Version: 2.8.1
;; Package-Requires: ((emacs "24.1"))
;; %CopyrightBegin%
@@ -84,7 +84,7 @@
"The Erlang programming language."
:group 'languages)
-(defconst erlang-version "2.8.0"
+(defconst erlang-version "2.8.1"
"The version number of Erlang mode.")
(defcustom erlang-root-dir nil
@@ -884,7 +884,6 @@ resulting regexp is surrounded by \\_< and \\_>."
"alloc_sizes"
"append"
"append_element"
- "await_proc_exit"
"bump_reductions"
"call_on_load_function"
"cancel_timer"
@@ -903,7 +902,6 @@ resulting regexp is surrounded by \\_< and \\_>."
"dist_ctrl_input_handler"
"dist_ctrl_put_data"
"dmonitor_node"
- "dmonitor_p"
"dt_append_vm_tag_data"
"dt_get_tag"
"dt_get_tag_data"
@@ -912,6 +910,7 @@ resulting regexp is surrounded by \\_< and \\_>."
"dt_restore_tag"
"dt_spread_tag"
"convert_time_unit"
+ "exit_signal"
"external_size"
"finish_after_on_load"
"finish_loading"
@@ -2742,7 +2741,7 @@ Return nil if inside string, t if in a comment."
(1+ (nth 2 stack-top)))
((= (char-syntax (following-char)) ?\))
(goto-char (nth 1 stack-top))
- (cond ((looking-at "[({]\\s *\\($\\|%\\)")
+ (cond ((erlang-record-or-function-args-p)
;; Line ends with parenthesis.
(let ((previous (erlang-indent-find-preceding-expr))
(stack-pos (nth 2 stack-top)))
@@ -2752,19 +2751,10 @@ Return nil if inside string, t if in a comment."
(nth 2 stack-top))))
((= (following-char) ?,)
;; a comma at the start of the line: line up with opening parenthesis.
- (nth 2 stack-top))
+ (min (nth 2 stack-top)
+ (erlang-indent-element stack-top indent-point token)))
(t
- (goto-char (nth 1 stack-top))
- (let ((base (cond ((looking-at "[({]\\s *\\($\\|%\\)")
- ;; Line ends with parenthesis.
- (erlang-indent-parenthesis (nth 2 stack-top)))
- (t
- ;; Indent to the same column as the first
- ;; argument.
- (goto-char (1+ (nth 1 stack-top)))
- (skip-chars-forward " \t")
- (current-column)))))
- (erlang-indent-standard indent-point token base 't)))))
+ (erlang-indent-element stack-top indent-point token))))
;;
((eq (car stack-top) '<<)
;; Element of binary (possible comprehension) expression,
@@ -2773,13 +2763,11 @@ Return nil if inside string, t if in a comment."
(+ 2 (nth 2 stack-top)))
((looking-at "\\(>>\\)[^_a-zA-Z0-9]")
(nth 2 stack-top))
+ ((= (following-char) ?,)
+ (min (+ (nth 2 stack-top) 1)
+ (- (erlang-indent-to-first-element stack-top 2) 1)))
(t
- (goto-char (nth 1 stack-top))
- ;; Indent to the same column as the first
- ;; argument.
- (goto-char (+ 2 (nth 1 stack-top)))
- (skip-chars-forward " \t")
- (current-column))))
+ (erlang-indent-to-first-element stack-top 2))))
((memq (car stack-top) '(icr fun spec))
;; The default indentation is the column of the option
@@ -2835,12 +2823,13 @@ Return nil if inside string, t if in a comment."
(let ((base (erlang-indent-find-base stack indent-point off skip)))
;; Special cases
(goto-char indent-point)
- (cond ((looking-at "\\(end\\|after\\)\\($\\|[^_a-zA-Z0-9]\\)")
+ (cond ((looking-at "\\(;\\|end\\|after\\)\\($\\|[^_a-zA-Z0-9]\\)")
(if (eq (car stack-top) '->)
(erlang-pop stack))
- (if stack
- (erlang-caddr (car stack))
- 0))
+ (cond ((and stack (looking-at ";"))
+ (+ (erlang-caddr (car stack)) (- erlang-indent-level 2)))
+ (stack (erlang-caddr (car stack)))
+ (t off)))
((looking-at "catch\\b\\($\\|[^_a-zA-Z0-9]\\)")
;; Are we in a try
(let ((start (if (eq (car stack-top) '->)
@@ -2914,6 +2903,22 @@ Return nil if inside string, t if in a comment."
(current-column))) start-alternativ))))))
)))
+(defun erlang-indent-to-first-element (stack-top extra)
+ ;; Indent to the same column as the first
+ ;; argument. extra should be 1 for lists tuples or 2 for binaries
+ (goto-char (+ (nth 1 stack-top) extra))
+ (skip-chars-forward " \t")
+ (current-column))
+
+(defun erlang-indent-element (stack-top indent-point token)
+ (goto-char (nth 1 stack-top))
+ (let ((base (cond ((erlang-record-or-function-args-p)
+ ;; Line ends with parenthesis.
+ (erlang-indent-parenthesis (nth 2 stack-top)))
+ (t
+ (erlang-indent-to-first-element stack-top 1)))))
+ (erlang-indent-standard indent-point token base 't)))
+
(defun erlang-indent-standard (indent-point token base inside-parenthesis)
"Standard indent when in blocks or tuple or arguments.
Look at last thing to see in what state we are, move relative to the base."
@@ -2939,6 +2944,9 @@ Return nil if inside string, t if in a comment."
;; Avoid treating comments a continued line.
((= (following-char) ?%)
base)
+ ((and (= (following-char) ?,) inside-parenthesis)
+ ;; a comma at the start of the line line up with parenthesis
+ (- base 1))
;; Continued line (e.g. line beginning
;; with an operator.)
(t
@@ -3028,11 +3036,21 @@ This assumes that the preceding expression is either simple
(t col)))
col))))
+(defun erlang-record-or-function-args-p ()
+ (and (looking-at "[({]\\s *\\($\\|%\\)")
+ (or (eq (following-char) ?\( )
+ (save-excursion
+ (ignore-errors (forward-sexp (- 1)))
+ (eq (preceding-char) ?#)))))
+
(defun erlang-indent-parenthesis (stack-position)
(let ((previous (erlang-indent-find-preceding-expr)))
- (if (> previous stack-position)
- (+ stack-position erlang-argument-indent)
- (+ previous erlang-argument-indent))))
+ (cond ((eq previous stack-position) ;; tuple or map not a record
+ (1+ stack-position))
+ ((> previous stack-position)
+ (+ stack-position erlang-argument-indent))
+ (t
+ (+ previous erlang-argument-indent)))))
(defun erlang-skip-blank (&optional lim)
"Skip over whitespace and comments until limit reached."
@@ -5166,7 +5184,6 @@ future, a new shell on an already running host will be started."
;; e.g. it does not assume that we are running an inferior
;; Erlang, there exists a lot of other possibilities.
-
(defvar erlang-shell-buffer-name "*erlang*"
"The name of the Erlang link shell buffer.")
@@ -5177,46 +5194,28 @@ Also see the description of `ielm-prompt-read-only'."
:type 'boolean
:package-version '(erlang . "2.8.0"))
-(defvar erlang-shell-mode-map nil
- "Keymap used by Erlang shells.")
-
-
-(defvar erlang-shell-mode-hook nil
- "User functions to run when an Erlang shell is started.
-
-This hook is used to change the behaviour of Erlang mode. It is
-normally used by the user to personalise the programming environment.
-When used in a site init file, it could be used to customise Erlang
-mode for all users on the system.
-
-The function added to this hook is run every time a new Erlang
-shell is started.
+(defvar erlang-shell-mode-map
+ (let ((map (make-sparse-keymap)))
+ (define-key map "\M-\t" 'erlang-complete-tag)
-See also `erlang-load-hook', a hook which is run once, when Erlang
-mode is loaded, and `erlang-mode-hook' which is run every time a new
-Erlang source file is loaded into Emacs.")
+ ;; Normally the other way around.
+ (define-key map "\C-a" 'comint-bol)
+ (define-key map "\C-c\C-a" 'beginning-of-line)
+ (define-key map "\C-d" nil) ; Was `comint-delchar-or-maybe-eof'
+ (define-key map "\M-\C-m" 'compile-goto-error)
+ map)
+ "Keymap used by Erlang shells.")
(defvar erlang-input-ring-file-name "~/.erlang_history"
"When non-nil, file name used to store Erlang shell history information.")
-
-(defun erlang-shell-mode ()
+(define-derived-mode erlang-shell-mode comint-mode "Erlang Shell"
"Major mode for interacting with an Erlang shell.
-We assume that we already are in Comint mode.
-
The following special commands are available:
\\{erlang-shell-mode-map}"
- (interactive)
- (setq major-mode 'erlang-shell-mode)
- (setq mode-name "Erlang Shell")
(erlang-mode-variables)
- (if erlang-shell-mode-map
- nil
- (setq erlang-shell-mode-map (copy-keymap comint-mode-map))
- (erlang-shell-mode-commands erlang-shell-mode-map))
- (use-local-map erlang-shell-mode-map)
;; Needed when compiling directly from the Erlang shell.
(setq compilation-last-buffer (current-buffer))
(setq comint-prompt-regexp "^[^>=]*> *")
@@ -5230,7 +5229,6 @@ The following special commands are available:
'inferior-erlang-strip-delete nil t)
(add-hook 'comint-output-filter-functions
'inferior-erlang-strip-ctrl-m nil t)
-
(setq comint-input-ring-file-name erlang-input-ring-file-name)
(comint-read-input-ring t)
(make-local-variable 'kill-buffer-hook)
@@ -5249,8 +5247,7 @@ The following special commands are available:
(define-key map [menu-bar compilation]
(cons "Errors" compilation-menu-map)))
map))))
- (erlang-tags-init)
- (run-hooks 'erlang-shell-mode-hook))
+ (erlang-tags-init))
(defun erlang-mouse-2-command (event)
@@ -5272,13 +5269,6 @@ Selects Comint or Compilation mode command as appropriate."
(call-interactively (lookup-key compilation-mode-map "\C-m"))
(call-interactively (lookup-key comint-mode-map "\C-m"))))
-(defun erlang-shell-mode-commands (map)
- (define-key map "\M-\t" 'erlang-complete-tag)
- (define-key map "\C-a" 'comint-bol) ; Normally the other way around.
- (define-key map "\C-c\C-a" 'beginning-of-line)
- (define-key map "\C-d" nil) ; Was `comint-delchar-or-maybe-eof'
- (define-key map "\M-\C-m" 'compile-goto-error))
-
;;;
;;; Inferior Erlang -- Run an Erlang shell as a subprocess.
;;;
diff --git a/lib/tools/emacs/test.erl.indented b/lib/tools/emacs/test.erl.indented
deleted file mode 100644
index 14a4eca7c3..0000000000
--- a/lib/tools/emacs/test.erl.indented
+++ /dev/null
@@ -1,784 +0,0 @@
-%% -*- Mode: erlang; indent-tabs-mode: nil -*-
-%%
-%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2009-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%
-
-%%%-------------------------------------------------------------------
-%%% File : test.erl
-%%% Author : Dan Gudmundsson <[email protected]>
-%%% Description : Test emacs mode indention and font-locking
-%%% this file is intentionally not indented.
-%%% Copy the file and indent it and you should end up with test.erl.indented
-%%% Created : 6 Oct 2009 by Dan Gudmundsson <[email protected]>
-%%%-------------------------------------------------------------------
-
-%% Start off with syntax highlighting you have to verify this by looking here
-%% and see that the code looks alright
-
--module(test).
--compile(export_all).
-
-%% Used to cause an "Unbalanced parentheses" error.
-foo(M) ->
- M#{a :=<<"a">>
- ,b:=1}.
-foo() ->
- #{a =><<"a">>
- ,b=>1}.
-
-%% Module attributes should be highlighted
-
--export([t/1]).
--record(record1, {a,
- b,
- c
- }).
--record(record2, {
- a,
- b
- }).
-
--record(record3, {a = 8#42423 bor
- 8#4234,
- b = 8#5432
- bor 2#1010101
- c = 123 +
- 234,
- d}).
-
--record(record4, {
- a = 8#42423 bor
- 8#4234,
- b = 8#5432
- bor 2#1010101
- c = 123 +
- 234,
- d}).
-
--record(record5, { a = 1 :: integer()
- , b = foobar :: atom()
- }).
-
--define(MACRO_1, macro).
--define(MACRO_2(_), macro).
-
--spec t(integer()) -> any().
-
--type ann() :: Var :: integer().
--type ann2() :: Var ::
- 'return'
- | 'return_white_spaces'
- | 'return_comments'
- | 'text' | ann().
--type paren() ::
- (ann2()).
--type t1() :: atom().
--type t2() :: [t1()].
--type t3(Atom) :: integer(Atom).
--type t4() :: t3(foobar).
--type t5() :: {t1(), t3(foo)}.
--type t6() :: 1 | 2 | 3 |
- 'foo' | 'bar'.
--type t7() :: [].
--type t71() :: [_].
--type t8() :: {any(),none(),pid(),port(),
- reference(),float()}.
--type t9() :: [1|2|3|foo|bar] |
- list(a | b | c) | t71().
--type t10() :: {1|2|3|foo|t9()} | {}.
--type t11() :: 1..2.
--type t13() :: maybe_improper_list(integer(), t11()).
--type t14() :: [erl_scan:foo() |
- %% Should be highlighted
- term() |
- bool() |
- byte() |
- char() |
- non_neg_integer() | nonempty_list() |
- pos_integer() |
- neg_integer() |
- number() |
- list() |
- nonempty_improper_list() | nonempty_maybe_improper_list() |
- maybe_improper_list() | string() | iolist() | byte() |
- module() |
- mfa() |
- node() |
- timeout() |
- no_return() |
- %% Should not be highlighted
- nonempty_() | nonlist() |
- erl_scan:bar(34, 92) | t13() | m:f(integer() | <<_:_*16>>)].
-
-
--type t15() :: {binary(),<<>>,<<_:34>>,<<_:_*42>>,
- <<_:3,_:_*14>>,<<>>} | [<<>>|<<_:34>>|<<_:16>>|
- <<_:3,_:_*1472>>|<<_:19,_:_*14>>| <<_:34>>|
- <<_:34>>|<<_:34>>|<<_:34>>].
--type t16() :: fun().
--type t17() :: fun((...) -> paren()).
--type t18() :: fun(() -> t17() | t16()).
--type t19() :: fun((t18()) -> t16()) |
- fun((nonempty_maybe_improper_list('integer', any())|
- 1|2|3|a|b|<<_:3,_:_*14>>|integer()) ->
- nonempty_maybe_improper_list('integer', any())|
- 1|2|3|a|b|<<_:3,_:_*14>>|integer()).
--type t20() :: [t19(), ...].
--type t21() :: tuple().
--type t21(A) :: A.
--type t22() :: t21(integer()).
--type t23() :: #rec1{}.
--type t24() :: #rec2{a :: t23(), b :: [atom()]}.
--type t25() :: #rec3{f123 :: [t24() |
- 1|2|3|4|a|b|c|d|
- nonempty_maybe_improper_list(integer, any())]}.
--type t26() :: #rec4{ a :: integer()
- , b :: any()
- }.
--type t27() :: { integer()
- , atom()
- }.
--type t99() ::
- {t2(),t4(),t5(),t6(),t7(),t8(),t10(),t14(),
- t15(),t20(),t21(), t22(),t25()}.
--spec t1(FooBar :: t99()) -> t99();
- (t2()) -> t2();
- (t4()) -> t4() when is_subtype(t4(), t24);
- (t23()) -> t23() when is_subtype(t23(), atom()),
- is_subtype(t23(), t14());
- (t24()) -> t24() when is_subtype(t24(), atom()),
- is_subtype(t24(), t14()),
- is_subtype(t24(), t4()).
-
--spec over(I :: integer()) -> R1 :: foo:typen();
- (A :: atom()) -> R2 :: foo:atomen();
- (T :: tuple()) -> R3 :: bar:typen().
-
--spec mod:t2() -> any().
-
--spec handle_cast(Cast :: {'exchange', node(), [[name(),...]]}
- | {'del_member', name(), pid()},
- #state{}) -> {'noreply', #state{}}.
-
--spec handle_cast(Cast ::
- {'exchange', node(), [[name(),...]]}
- | {'del_member', name(), pid()},
- #state{}) -> {'noreply', #state{}}.
-
--spec all(fun((T) -> boolean()), List :: [T]) ->
- boolean() when is_subtype(T, term()). % (*)
-
--spec get_closest_pid(term()) ->
- Return :: pid()
- | {'error', {'no_process', term()}
- | {'no_such_group', term()}}.
-
--spec add( X :: integer()
- , Y :: integer()
- ) -> integer().
-
--opaque attributes_data() ::
- [{'column', column()} | {'line', info_line()} |
- {'text', string()}] | {line(),column()}.
--record(r,{
- f1 :: attributes_data(),
- f222 = foo:bar(34, #rec3{}, 234234234423,
- aassdsfsdfsdf, 2234242323) ::
- [t24() | 1|2|3|4|a|b|c|d|
- nonempty_maybe_improper_list(integer, any())],
- f333 :: [t24() | 1|2|3|4|a|b|c|d|
- nonempty_maybe_improper_list(integer, any())],
- f3 = x:y(),
- f4 = x:z() :: t99(),
- f17 :: 'undefined',
- f18 :: 1 | 2 | 'undefined',
- f19 = 3 :: integer()|undefined,
- f5 = 3 :: undefined|integer()}).
-
--record(state, {
- sequence_number = 1 :: integer()
- }).
-
-
-highlighting(X) % Function definitions should be highlighted
- when is_integer(X) -> % and so should `when' and `is_integer' be
- %% Highlighting
- %% Various characters (we keep an `atom' after to see that highlighting ends)
- $a,atom, % Characters should be marked
- "string",atom, % and strings
- 'asdasd',atom, % quote should be atoms??
- 'VaV',atom,
- 'aVa',atom,
- '\'atom',atom,
- 'atom\'',atom,
- 'at\'om',atom,
- '#1',atom,
-
- $", atom, % atom should be ok
- $', atom,
-
- "string$", atom, "string$", atom, % currently buggy I know...
- "string\$", atom, % workaround for bug above
-
- "char $in string", atom,
-
- 'atom$', atom, 'atom$', atom,
- 'atom\$', atom,
-
- 'char $in atom', atom,
-
- $[, ${, $\\, atom,
- ?MACRO_1,
- ?MACRO_2(foo),
-
- %% Numerical constants
- 16#DD, % AD Should not be highlighted
- 32#dd, % AD Should not be highlighted
- 32#ddAB, % AD Should not be highlighted
- 32#101, % AD Should not be highlighted
- 32#ABTR, % AD Should not be highlighted
-
- %% Variables
- Variables = lists:foo(),
- _Variables = lists:foo(), % AD
- AppSpec = Xyz/2,
- Module42 = Xyz(foo, bar),
- Module:foo(),
- _Module:foo(), % AD
- FooÅÅ = lists:reverse([tl,hd,tl,hd]), % AD Should highlight FooÅÅ
- _FooÅÅ = 42, % AD Should highlight _FooÅÅ
-
- %% Bifs
- erlang:registered(),
- registered(),
- hd(tl(tl(hd([a,b,c])))),
- erlang:anything(lists),
- %% Guards
- is_atom(foo), is_float(2.3), is_integer(32), is_number(4323.3),
- is_function(Fun), is_pid(self()),
- not_a_guard:is_list([]),
- %% Other Types
-
- atom, % not (currently) hightlighted
- 234234,
- 234.43,
-
- [list, are, not, higlighted],
- {nor, is, tuple},
- ok.
-
-%%%
-%%% Indentation
-%%%
-
-%%% Left
-
-%% Indented
-
- % Right
-
-
-indent_basics(X, Y, Z)
- when X > 42,
- Z < 13;
- Y =:= 4711 ->
- %% comments
- % right comments
- case lists:filter(fun(_, AlongName,
- B,
- C) ->
- true
- end,
- [a,v,b])
- of
- [] ->
- Y = 5 * 43,
- ok;
- [_|_] ->
- Y = 5 * 43,
- ok
- end,
- Y,
- %% List, tuples and binaries
- [a,
- b, c
- ],
- [ a,
- b, c
- ],
-
- [
- a,
- b
- ],
- {a,
- b,c
- },
- { a,
- b,c
- },
-
- {
- a,
- b
- },
-
- <<1:8,
- 2:8
- >>,
- <<
- 1:8,
- 2:8
- >>,
- << 1:8,
- 2:8
- >>,
-
- (a,
- b,
- c
- ),
-
- ( a,
- b,
- c
- ),
-
-
- (
- a,
- b,
- c
- ),
-
- call(2#42423 bor
- #4234,
- 2#5432,
- other_arg),
- ok;
-indent_basics(Xlongname,
- #struct{a=Foo,
- b=Bar},
- [X|
- Y]) ->
- testing_next_clause,
- ok;
-indent_basics( % AD added clause
- X, % not sure how this should look
- Y,
- Z)
- when
- X < 42, Z > 13;
- Y =:= 4711 ->
- foo;
-indent_basics(X, Y, Z) when % AD added clause
- X < 42, Z > 13; % testing when indentation
- Y =:= 4711 ->
- foo;
-indent_basics(X, Y, Z) % AD added clause
- when % testing when indentation
- X < 42, Z > 13; % unsure about this one
- Y =:= 4711 ->
- foo.
-
-
-
-indent_nested() ->
- [
- {foo, 2, "string"},
- {bar, 3, "another string"}
- ].
-
-
-indent_icr(Z) -> % icr = if case receive
- %% If
- if Z >= 0 ->
- X = 43 div 4,
- foo(X);
- Z =< 10 ->
- X = 43 div 4,
- foo(X);
- Z == 5 orelse
- Z == 7 ->
- X = 43 div 4,
- foo(X);
- true ->
- if_works
- end,
- %% Case
- case {Z, foo, bar} of
- {Z,_,_} ->
- X = 43 div 4,
- foo(X);
- {Z,_,_} when
- Z =:= 42 -> % AD line should be indented as a when
- X = 43 div 4,
- foo(X);
- {Z,_,_}
- when Z < 10 -> % AD when should be indented
- X = 43 div 4,
- foo(X);
- {Z,_,_}
- when % AD when should be indented
- Z < 10 % and the guards should follow when
- andalso % unsure about how though
- true ->
- X = 43 div 4,
- foo(X)
- end,
- %% begin
- begin
- sune,
- X = 74234 + foo(8456) +
- 345 div 43,
- ok
- end,
-
-
- %% receive
- receive
- {Z,_,_} ->
- X = 43 div 4,
- foo(X);
- Z ->
- X = 43 div 4,
- foo(X)
- end,
- receive
- {Z,_,_} ->
- X = 43 div 4,
- foo(X);
- Z % AD added clause
- when Z =:= 1 -> % This line should be indented by 2
- X = 43 div 4,
- foo(X);
- Z when % AD added clause
- Z =:= 2 -> % This line should be indented by 2
- X = 43 div 4,
- foo(X);
- Z ->
- X = 43 div 4,
- foo(X)
- after infinity ->
- foo(X),
- asd(X),
- 5*43
- end,
- receive
- after 10 ->
- foo(X),
- asd(X),
- 5*43
- end,
- ok.
-
-indent_fun() ->
- %% Changed fun to one indention level
- Var = spawn(fun(X)
- when X == 2;
- X > 10 ->
- hello,
- case Hello() of
- true when is_atom(X) ->
- foo;
- false ->
- bar
- end;
- (Foo) when is_atom(Foo),
- is_integer(X) ->
- X = 6* 45,
- Y = true andalso
- kalle
- end),
- %% check EEP37 named funs
- Fn1 = fun Fact(N) when N > 0 ->
- F = Fact(N-1),
- N * F;
- Fact(0) ->
- 1
- end,
- %% check anonymous funs too
- Fn2 = fun(0) ->
- 1;
- (N) ->
- N
- end,
- ok.
-
-indent_try_catch() ->
- try
- io:format(stdout, "Parsing file ~s, ",
- [St0#leex.xfile]),
- {ok,Line3,REAs,Actions,St3} =
- parse_rules(Xfile, Line2, Macs, St2)
- catch
- exit:{badarg,R} ->
- foo(R),
- io:format(stdout,
- "ERROR reason ~p~n",
- R);
- error:R % AD added clause
- when R =:= 42 -> % when should be indented
- foo(R);
- error:R % AD added clause
- when % when should be indented
- R =:= 42 -> % but unsure about this (maybe 2 more)
- foo(R);
- error:R when % AD added clause
- R =:= foo -> % line should be 2 indented (works)
- foo(R);
- error:R ->
- foo(R),
- io:format(stdout,
- "ERROR reason ~p~n",
- R)
- after
- foo('after'),
- file:close(Xfile)
- end;
-indent_try_catch() ->
- try
- foo(bar)
- of
- X when true andalso
- kalle ->
- io:format(stdout, "Parsing file ~s, ",
- [St0#leex.xfile]),
- {ok,Line3,REAs,Actions,St3} =
- parse_rules(Xfile, Line2, Macs, St2);
- X % AD added clause
- when false andalso % when should be 2 indented
- bengt ->
- gurka();
- X when % AD added clause
- false andalso % line should be 2 indented
- not bengt ->
- gurka();
- X ->
- io:format(stdout, "Parsing file ~s, ",
- [St0#leex.xfile]),
- {ok,Line3,REAs,Actions,St3} =
- parse_rules(Xfile, Line2, Macs, St2)
- catch
- exit:{badarg,R} ->
- foo(R),
- io:format(stdout,
- "ERROR reason ~p~n",
- R);
- error:R ->
- foo(R),
- io:format(stdout,
- "ERROR reason ~p~n",
- R)
- after
- foo('after'),
- file:close(Xfile),
- bar(with_long_arg,
- with_second_arg)
- end;
-indent_try_catch() ->
- try foo()
- after
- foo(),
- bar(with_long_arg,
- with_second_arg)
- end.
-
-indent_catch() ->
- D = B +
- float(43.1),
-
- B = catch oskar(X),
-
- A = catch (baz +
- bax),
- catch foo(),
-
- C = catch B +
- float(43.1),
-
- case catch foo(X) of
- A ->
- B
- end,
-
- case
- catch foo(X)
- of
- A ->
- B
- end,
-
- case
- foo(X)
- of
- A ->
- catch B,
- X
- end,
-
- try sune of
- _ -> foo
- catch _:_ -> baf
- end,
-
- try
- sune
- of
- _ ->
- X = 5,
- (catch foo(X)),
- X + 10
- catch _:_ -> baf
- end,
-
- try
- (catch sune)
- of
- _ ->
- catch foo() %% BUGBUG can't handle catch inside try without parentheses
- catch _:_ ->
- baf
- end,
-
- try
- (catch exit())
- catch
- _ ->
- catch baf()
- end,
- ok.
-
-indent_binary() ->
- X = lists:foldr(fun(M) ->
- <<Ma/binary, " ">>
- end, [], A),
- A = <<X/binary, 0:8>>,
- B.
-
-
-indent_comprehensions() ->
- %% I don't have a good idea how we want to handle this
- %% but they are here to show how they are indented today.
- Result1 = [X ||
- #record{a=X} <- lists:seq(1, 10),
- true = (X rem 2)
- ],
- Result2 = [X || <<X:32,_:32>> <= <<0:512>>,
- true = (X rem 2)
- ],
-
- Binary1 = << <<X:8>> ||
- #record{a=X} <- lists:seq(1, 10),
- true = (X rem 2)
- >>,
-
- Binary2 = << <<X:8>> || <<X:32,_:32>> <= <<0:512>>,
- true = (X rem 2)
- >>,
- ok.
-
-%% This causes an error in earlier erlang-mode versions.
-foo() ->
- [#foo{
- foo = foo}].
-
-%% Record indentation
-some_function_with_a_very_long_name() ->
- #'a-long-record-name-like-it-sometimes-is-with-asn.1-records'{
- field1=a,
- field2=b},
- case dummy_function_with_a_very_very_long_name(x) of
- #'a-long-record-name-like-it-sometimes-is-with-asn.1-records'{
- field1=a,
- field2=b} ->
- ok;
- Var = #'a-long-record-name-like-it-sometimes-is-with-asn.1-records'{
- field1=a,
- field2=b} ->
- Var#'a-long-record-name-like-it-sometimes-is-with-asn.1-records'{
- field1=a,
- field2=b};
- #xyz{
- a=1,
- b=2} ->
- ok
- end.
-
-another_function_with_a_very_very_long_name() ->
- #rec{
- field1=1,
- field2=1}.
-
-some_function_name_xyz(xyzzy, #some_record{
- field1=Field1,
- field2=Field2}) ->
- SomeVariable = f(#'Some-long-record-name'{
- field_a = 1,
- 'inter-xyz-parameters' =
- #'Some-other-very-long-record-name'{
- field2 = Field1,
- field2 = Field2}}),
- {ok, SomeVariable}.
-
-commas_first() ->
- {abc, [ {some_var, 1}
- , {some_other_var, 2}
- , {erlang_ftw, 9}
- , {erlang_cookie, 'cookie'}
- , {cmds,
- [ {one, "sudo ls"}
- , {one, "sudo ls"}
- , {two, "sudo ls"}
- , {three, "sudo ls"}
- , {four, "sudo ls"}
- , {three, "sudo ls"}
- ] }
- , {ssh_username, "yow"}
- , {cluster,
- [ {aaaa, [ {"10.198.55.12" , "" }
- , {"10.198.55.13" , "" }
- ] }
- , {bbbb, [ {"10.198.55.151", "" }
- , {"10.198.55.123", "" }
- , {"10.198.55.34" , "" }
- , {"10.198.55.85" , "" }
- , {"10.198.55.67" , "" }
- ] }
- , {cccc, [ {"10.198.55.68" , "" }
- , {"10.198.55.69" , "" }
- ] }
- ] }
- ]
- }.
-
-
-%% this used to result in a scan-sexp error
-[{
- }].
-
-%% this used to result in 2x the correct indentation within the function
-%% body, due to the function name being mistaken for a keyword
-catcher(N) ->
- try generate_exception(N) of
- Val -> {N, normal, Val}
- catch
- throw:X -> {N, caught, thrown, X};
- exit:X -> {N, caught, exited, X};
- error:X -> {N, caught, error, X}
- end.
diff --git a/lib/tools/emacs/test.erl.orig b/lib/tools/emacs/test.erl.orig
deleted file mode 100644
index c0cf1749b6..0000000000
--- a/lib/tools/emacs/test.erl.orig
+++ /dev/null
@@ -1,784 +0,0 @@
-%% -*- Mode: erlang; indent-tabs-mode: nil -*-
-%%
-%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2009-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%
-
-%%%-------------------------------------------------------------------
-%%% File : test.erl
-%%% Author : Dan Gudmundsson <[email protected]>
-%%% Description : Test emacs mode indention and font-locking
-%%% this file is intentionally not indented.
-%%% Copy the file and indent it and you should end up with test.erl.indented
-%%% Created : 6 Oct 2009 by Dan Gudmundsson <[email protected]>
-%%%-------------------------------------------------------------------
-
-%% Start off with syntax highlighting you have to verify this by looking here
-%% and see that the code looks alright
-
--module(test).
--compile(export_all).
-
-%% Used to cause an "Unbalanced parentheses" error.
-foo(M) ->
-M#{a :=<<"a">>
-,b:=1}.
-foo() ->
-#{a =><<"a">>
-,b=>1}.
-
-%% Module attributes should be highlighted
-
--export([t/1]).
--record(record1, {a,
- b,
- c
-}).
--record(record2, {
- a,
- b
- }).
-
--record(record3, {a = 8#42423 bor
- 8#4234,
- b = 8#5432
- bor 2#1010101
- c = 123 +
-234,
- d}).
-
--record(record4, {
- a = 8#42423 bor
- 8#4234,
- b = 8#5432
- bor 2#1010101
- c = 123 +
- 234,
- d}).
-
--record(record5, { a = 1 :: integer()
-, b = foobar :: atom()
-}).
-
--define(MACRO_1, macro).
--define(MACRO_2(_), macro).
-
--spec t(integer()) -> any().
-
--type ann() :: Var :: integer().
--type ann2() :: Var ::
- 'return'
- | 'return_white_spaces'
- | 'return_comments'
- | 'text' | ann().
--type paren() ::
- (ann2()).
--type t1() :: atom().
--type t2() :: [t1()].
--type t3(Atom) :: integer(Atom).
--type t4() :: t3(foobar).
--type t5() :: {t1(), t3(foo)}.
--type t6() :: 1 | 2 | 3 |
- 'foo' | 'bar'.
--type t7() :: [].
--type t71() :: [_].
--type t8() :: {any(),none(),pid(),port(),
- reference(),float()}.
--type t9() :: [1|2|3|foo|bar] |
- list(a | b | c) | t71().
--type t10() :: {1|2|3|foo|t9()} | {}.
--type t11() :: 1..2.
--type t13() :: maybe_improper_list(integer(), t11()).
--type t14() :: [erl_scan:foo() |
- %% Should be highlighted
- term() |
- bool() |
- byte() |
- char() |
- non_neg_integer() | nonempty_list() |
- pos_integer() |
- neg_integer() |
- number() |
- list() |
- nonempty_improper_list() | nonempty_maybe_improper_list() |
- maybe_improper_list() | string() | iolist() | byte() |
- module() |
- mfa() |
- node() |
- timeout() |
- no_return() |
- %% Should not be highlighted
- nonempty_() | nonlist() |
- erl_scan:bar(34, 92) | t13() | m:f(integer() | <<_:_*16>>)].
-
-
--type t15() :: {binary(),<<>>,<<_:34>>,<<_:_*42>>,
- <<_:3,_:_*14>>,<<>>} | [<<>>|<<_:34>>|<<_:16>>|
-<<_:3,_:_*1472>>|<<_:19,_:_*14>>| <<_:34>>|
-<<_:34>>|<<_:34>>|<<_:34>>].
--type t16() :: fun().
--type t17() :: fun((...) -> paren()).
--type t18() :: fun(() -> t17() | t16()).
--type t19() :: fun((t18()) -> t16()) |
- fun((nonempty_maybe_improper_list('integer', any())|
- 1|2|3|a|b|<<_:3,_:_*14>>|integer()) ->
-nonempty_maybe_improper_list('integer', any())|
-1|2|3|a|b|<<_:3,_:_*14>>|integer()).
--type t20() :: [t19(), ...].
--type t21() :: tuple().
--type t21(A) :: A.
--type t22() :: t21(integer()).
--type t23() :: #rec1{}.
--type t24() :: #rec2{a :: t23(), b :: [atom()]}.
--type t25() :: #rec3{f123 :: [t24() |
-1|2|3|4|a|b|c|d|
-nonempty_maybe_improper_list(integer, any())]}.
--type t26() :: #rec4{ a :: integer()
-, b :: any()
-}.
--type t27() :: { integer()
-, atom()
-}.
--type t99() ::
-{t2(),t4(),t5(),t6(),t7(),t8(),t10(),t14(),
-t15(),t20(),t21(), t22(),t25()}.
--spec t1(FooBar :: t99()) -> t99();
-(t2()) -> t2();
- (t4()) -> t4() when is_subtype(t4(), t24);
-(t23()) -> t23() when is_subtype(t23(), atom()),
- is_subtype(t23(), t14());
-(t24()) -> t24() when is_subtype(t24(), atom()),
- is_subtype(t24(), t14()),
- is_subtype(t24(), t4()).
-
--spec over(I :: integer()) -> R1 :: foo:typen();
- (A :: atom()) -> R2 :: foo:atomen();
- (T :: tuple()) -> R3 :: bar:typen().
-
--spec mod:t2() -> any().
-
--spec handle_cast(Cast :: {'exchange', node(), [[name(),...]]}
- | {'del_member', name(), pid()},
- #state{}) -> {'noreply', #state{}}.
-
--spec handle_cast(Cast ::
- {'exchange', node(), [[name(),...]]}
- | {'del_member', name(), pid()},
- #state{}) -> {'noreply', #state{}}.
-
--spec all(fun((T) -> boolean()), List :: [T]) ->
- boolean() when is_subtype(T, term()). % (*)
-
--spec get_closest_pid(term()) ->
- Return :: pid()
- | {'error', {'no_process', term()}
- | {'no_such_group', term()}}.
-
--spec add( X :: integer()
-, Y :: integer()
-) -> integer().
-
--opaque attributes_data() ::
-[{'column', column()} | {'line', info_line()} |
- {'text', string()}] | {line(),column()}.
--record(r,{
- f1 :: attributes_data(),
-f222 = foo:bar(34, #rec3{}, 234234234423,
- aassdsfsdfsdf, 2234242323) ::
-[t24() | 1|2|3|4|a|b|c|d|
- nonempty_maybe_improper_list(integer, any())],
-f333 :: [t24() | 1|2|3|4|a|b|c|d|
- nonempty_maybe_improper_list(integer, any())],
-f3 = x:y(),
-f4 = x:z() :: t99(),
-f17 :: 'undefined',
-f18 :: 1 | 2 | 'undefined',
-f19 = 3 :: integer()|undefined,
-f5 = 3 :: undefined|integer()}).
-
--record(state, {
- sequence_number = 1 :: integer()
- }).
-
-
-highlighting(X) % Function definitions should be highlighted
- when is_integer(X) -> % and so should `when' and `is_integer' be
- %% Highlighting
- %% Various characters (we keep an `atom' after to see that highlighting ends)
- $a,atom, % Characters should be marked
- "string",atom, % and strings
- 'asdasd',atom, % quote should be atoms??
- 'VaV',atom,
- 'aVa',atom,
- '\'atom',atom,
- 'atom\'',atom,
- 'at\'om',atom,
- '#1',atom,
-
- $", atom, % atom should be ok
- $', atom,
-
- "string$", atom, "string$", atom, % currently buggy I know...
- "string\$", atom, % workaround for bug above
-
- "char $in string", atom,
-
- 'atom$', atom, 'atom$', atom,
- 'atom\$', atom,
-
- 'char $in atom', atom,
-
- $[, ${, $\\, atom,
- ?MACRO_1,
- ?MACRO_2(foo),
-
- %% Numerical constants
- 16#DD, % AD Should not be highlighted
- 32#dd, % AD Should not be highlighted
- 32#ddAB, % AD Should not be highlighted
- 32#101, % AD Should not be highlighted
- 32#ABTR, % AD Should not be highlighted
-
- %% Variables
- Variables = lists:foo(),
- _Variables = lists:foo(), % AD
- AppSpec = Xyz/2,
- Module42 = Xyz(foo, bar),
- Module:foo(),
- _Module:foo(), % AD
- FooÅÅ = lists:reverse([tl,hd,tl,hd]), % AD Should highlight FooÅÅ
- _FooÅÅ = 42, % AD Should highlight _FooÅÅ
-
- %% Bifs
- erlang:registered(),
- registered(),
- hd(tl(tl(hd([a,b,c])))),
- erlang:anything(lists),
- %% Guards
- is_atom(foo), is_float(2.3), is_integer(32), is_number(4323.3),
- is_function(Fun), is_pid(self()),
- not_a_guard:is_list([]),
- %% Other Types
-
- atom, % not (currently) hightlighted
- 234234,
- 234.43,
-
- [list, are, not, higlighted],
- {nor, is, tuple},
- ok.
-
-%%%
-%%% Indentation
-%%%
-
-%%% Left
-
-%% Indented
-
-% Right
-
-
-indent_basics(X, Y, Z)
- when X > 42,
-Z < 13;
-Y =:= 4711 ->
- %% comments
- % right comments
- case lists:filter(fun(_, AlongName,
- B,
- C) ->
- true
- end,
- [a,v,b])
- of
- [] ->
- Y = 5 * 43,
- ok;
- [_|_] ->
- Y = 5 * 43,
- ok
- end,
- Y,
- %% List, tuples and binaries
- [a,
- b, c
- ],
- [ a,
- b, c
- ],
-
- [
- a,
- b
-],
- {a,
- b,c
- },
- { a,
- b,c
- },
-
- {
- a,
- b
- },
-
-<<1:8,
- 2:8
- >>,
- <<
- 1:8,
- 2:8
- >>,
- << 1:8,
- 2:8
- >>,
-
- (a,
- b,
- c
- ),
-
- ( a,
- b,
- c
- ),
-
-
- (
- a,
- b,
- c
- ),
-
- call(2#42423 bor
- #4234,
- 2#5432,
- other_arg),
- ok;
-indent_basics(Xlongname,
- #struct{a=Foo,
- b=Bar},
- [X|
- Y]) ->
- testing_next_clause,
- ok;
-indent_basics( % AD added clause
- X, % not sure how this should look
- Y,
- Z)
- when
- X < 42, Z > 13;
- Y =:= 4711 ->
- foo;
-indent_basics(X, Y, Z) when % AD added clause
- X < 42, Z > 13; % testing when indentation
- Y =:= 4711 ->
- foo;
-indent_basics(X, Y, Z) % AD added clause
- when % testing when indentation
- X < 42, Z > 13; % unsure about this one
- Y =:= 4711 ->
- foo.
-
-
-
-indent_nested() ->
- [
- {foo, 2, "string"},
- {bar, 3, "another string"}
- ].
-
-
-indent_icr(Z) -> % icr = if case receive
- %% If
- if Z >= 0 ->
- X = 43 div 4,
- foo(X);
- Z =< 10 ->
- X = 43 div 4,
- foo(X);
- Z == 5 orelse
- Z == 7 ->
- X = 43 div 4,
- foo(X);
- true ->
- if_works
- end,
- %% Case
- case {Z, foo, bar} of
- {Z,_,_} ->
- X = 43 div 4,
- foo(X);
- {Z,_,_} when
- Z =:= 42 -> % AD line should be indented as a when
- X = 43 div 4,
- foo(X);
- {Z,_,_}
- when Z < 10 -> % AD when should be indented
- X = 43 div 4,
- foo(X);
- {Z,_,_}
- when % AD when should be indented
- Z < 10 % and the guards should follow when
- andalso % unsure about how though
- true ->
- X = 43 div 4,
- foo(X)
- end,
- %% begin
- begin
- sune,
- X = 74234 + foo(8456) +
- 345 div 43,
- ok
- end,
-
-
- %% receive
- receive
- {Z,_,_} ->
- X = 43 div 4,
- foo(X);
- Z ->
- X = 43 div 4,
- foo(X)
- end,
- receive
- {Z,_,_} ->
- X = 43 div 4,
- foo(X);
- Z % AD added clause
- when Z =:= 1 -> % This line should be indented by 2
- X = 43 div 4,
- foo(X);
- Z when % AD added clause
- Z =:= 2 -> % This line should be indented by 2
- X = 43 div 4,
- foo(X);
- Z ->
- X = 43 div 4,
- foo(X)
- after infinity ->
- foo(X),
- asd(X),
- 5*43
- end,
- receive
- after 10 ->
- foo(X),
- asd(X),
- 5*43
- end,
- ok.
-
-indent_fun() ->
- %% Changed fun to one indention level
-Var = spawn(fun(X)
- when X == 2;
- X > 10 ->
- hello,
- case Hello() of
- true when is_atom(X) ->
- foo;
- false ->
- bar
- end;
- (Foo) when is_atom(Foo),
- is_integer(X) ->
- X = 6* 45,
- Y = true andalso
- kalle
- end),
-%% check EEP37 named funs
-Fn1 = fun Fact(N) when N > 0 ->
- F = Fact(N-1),
- N * F;
-Fact(0) ->
- 1
- end,
-%% check anonymous funs too
- Fn2 = fun(0) ->
-1;
- (N) ->
- N
- end,
- ok.
-
-indent_try_catch() ->
- try
- io:format(stdout, "Parsing file ~s, ",
- [St0#leex.xfile]),
- {ok,Line3,REAs,Actions,St3} =
- parse_rules(Xfile, Line2, Macs, St2)
- catch
- exit:{badarg,R} ->
- foo(R),
- io:format(stdout,
- "ERROR reason ~p~n",
- R);
- error:R % AD added clause
- when R =:= 42 -> % when should be indented
- foo(R);
- error:R % AD added clause
- when % when should be indented
- R =:= 42 -> % but unsure about this (maybe 2 more)
- foo(R);
- error:R when % AD added clause
- R =:= foo -> % line should be 2 indented (works)
- foo(R);
- error:R ->
- foo(R),
- io:format(stdout,
- "ERROR reason ~p~n",
- R)
- after
- foo('after'),
- file:close(Xfile)
- end;
-indent_try_catch() ->
- try
- foo(bar)
- of
- X when true andalso
- kalle ->
- io:format(stdout, "Parsing file ~s, ",
- [St0#leex.xfile]),
- {ok,Line3,REAs,Actions,St3} =
- parse_rules(Xfile, Line2, Macs, St2);
- X % AD added clause
- when false andalso % when should be 2 indented
- bengt ->
- gurka();
- X when % AD added clause
- false andalso % line should be 2 indented
- not bengt ->
- gurka();
- X ->
- io:format(stdout, "Parsing file ~s, ",
- [St0#leex.xfile]),
- {ok,Line3,REAs,Actions,St3} =
- parse_rules(Xfile, Line2, Macs, St2)
- catch
- exit:{badarg,R} ->
- foo(R),
- io:format(stdout,
- "ERROR reason ~p~n",
- R);
- error:R ->
- foo(R),
- io:format(stdout,
- "ERROR reason ~p~n",
- R)
- after
- foo('after'),
- file:close(Xfile),
- bar(with_long_arg,
- with_second_arg)
- end;
- indent_try_catch() ->
- try foo()
- after
- foo(),
- bar(with_long_arg,
- with_second_arg)
- end.
-
-indent_catch() ->
- D = B +
- float(43.1),
-
- B = catch oskar(X),
-
- A = catch (baz +
- bax),
- catch foo(),
-
- C = catch B +
- float(43.1),
-
- case catch foo(X) of
- A ->
- B
- end,
-
- case
- catch foo(X)
- of
- A ->
- B
- end,
-
- case
- foo(X)
- of
- A ->
- catch B,
- X
- end,
-
- try sune of
- _ -> foo
- catch _:_ -> baf
- end,
-
- try
-sune
- of
- _ ->
- X = 5,
- (catch foo(X)),
- X + 10
- catch _:_ -> baf
- end,
-
- try
- (catch sune)
- of
- _ ->
- catch foo() %% BUGBUG can't handle catch inside try without parentheses
- catch _:_ ->
- baf
- end,
-
- try
-(catch exit())
- catch
-_ ->
- catch baf()
- end,
- ok.
-
-indent_binary() ->
- X = lists:foldr(fun(M) ->
- <<Ma/binary, " ">>
- end, [], A),
- A = <<X/binary, 0:8>>,
- B.
-
-
-indent_comprehensions() ->
-%% I don't have a good idea how we want to handle this
-%% but they are here to show how they are indented today.
-Result1 = [X ||
- #record{a=X} <- lists:seq(1, 10),
- true = (X rem 2)
- ],
-Result2 = [X || <<X:32,_:32>> <= <<0:512>>,
- true = (X rem 2)
- ],
-
-Binary1 = << <<X:8>> ||
- #record{a=X} <- lists:seq(1, 10),
- true = (X rem 2)
- >>,
-
-Binary2 = << <<X:8>> || <<X:32,_:32>> <= <<0:512>>,
- true = (X rem 2)
- >>,
-ok.
-
-%% This causes an error in earlier erlang-mode versions.
-foo() ->
-[#foo{
-foo = foo}].
-
-%% Record indentation
-some_function_with_a_very_long_name() ->
- #'a-long-record-name-like-it-sometimes-is-with-asn.1-records'{
- field1=a,
- field2=b},
- case dummy_function_with_a_very_very_long_name(x) of
- #'a-long-record-name-like-it-sometimes-is-with-asn.1-records'{
- field1=a,
- field2=b} ->
- ok;
- Var = #'a-long-record-name-like-it-sometimes-is-with-asn.1-records'{
- field1=a,
- field2=b} ->
- Var#'a-long-record-name-like-it-sometimes-is-with-asn.1-records'{
- field1=a,
- field2=b};
- #xyz{
- a=1,
- b=2} ->
- ok
- end.
-
-another_function_with_a_very_very_long_name() ->
- #rec{
- field1=1,
- field2=1}.
-
-some_function_name_xyz(xyzzy, #some_record{
- field1=Field1,
- field2=Field2}) ->
- SomeVariable = f(#'Some-long-record-name'{
- field_a = 1,
- 'inter-xyz-parameters' =
- #'Some-other-very-long-record-name'{
- field2 = Field1,
- field2 = Field2}}),
- {ok, SomeVariable}.
-
-commas_first() ->
- {abc, [ {some_var, 1}
- , {some_other_var, 2}
- , {erlang_ftw, 9}
- , {erlang_cookie, 'cookie'}
- , {cmds,
- [ {one, "sudo ls"}
- , {one, "sudo ls"}
- , {two, "sudo ls"}
- , {three, "sudo ls"}
- , {four, "sudo ls"}
- , {three, "sudo ls"}
- ] }
- , {ssh_username, "yow"}
- , {cluster,
- [ {aaaa, [ {"10.198.55.12" , "" }
- , {"10.198.55.13" , "" }
- ] }
- , {bbbb, [ {"10.198.55.151", "" }
- , {"10.198.55.123", "" }
- , {"10.198.55.34" , "" }
- , {"10.198.55.85" , "" }
- , {"10.198.55.67" , "" }
- ] }
- , {cccc, [ {"10.198.55.68" , "" }
- , {"10.198.55.69" , "" }
- ] }
- ] }
-]
-}.
-
-
-%% this used to result in a scan-sexp error
-[{
-}].
-
-%% this used to result in 2x the correct indentation within the function
-%% body, due to the function name being mistaken for a keyword
-catcher(N) ->
-try generate_exception(N) of
-Val -> {N, normal, Val}
-catch
-throw:X -> {N, caught, thrown, X};
-exit:X -> {N, caught, exited, X};
-error:X -> {N, caught, error, X}
-end.
diff --git a/lib/tools/src/fprof.erl b/lib/tools/src/fprof.erl
index fb657c2928..a3b4bfdddf 100644
--- a/lib/tools/src/fprof.erl
+++ b/lib/tools/src/fprof.erl
@@ -1242,8 +1242,7 @@ spawn_3step(Spawn, FunPrelude, FunAck, FunBody)
catch Child ! {Parent, Ref, Go},
Result
catch
- Class:Reason ->
- Stacktrace = erlang:get_stacktrace(),
+ Class:Reason:Stacktrace ->
catch exit(Child, kill),
erlang:raise(Class, Reason, Stacktrace)
end;
diff --git a/lib/tools/src/lcnt.erl b/lib/tools/src/lcnt.erl
index 139b3d8a4a..d0152a4915 100644
--- a/lib/tools/src/lcnt.erl
+++ b/lib/tools/src/lcnt.erl
@@ -218,9 +218,11 @@ raw() -> call(raw).
set(Option, Value) -> call({set, Option, Value}).
set({Option, Value}) -> call({set, Option, Value}).
save(Filename) -> call({save, Filename}).
-load(Filename) -> ok = start_internal(), call({load, Filename}).
+load(Filename) -> call({load, Filename}).
-call(Msg) -> gen_server:call(?MODULE, Msg, infinity).
+call(Msg) ->
+ ok = start_internal(),
+ gen_server:call(?MODULE, Msg, infinity).
%% -------------------------------------------------------------------- %%
%%
@@ -237,7 +239,6 @@ apply(Fun) when is_function(Fun) ->
lcnt:apply(Fun, []).
apply(Fun, As) when is_function(Fun) ->
- ok = start_internal(),
Opt = lcnt:rt_opt({copy_save, true}),
lcnt:clear(),
Res = erlang:apply(Fun, As),
@@ -943,7 +944,7 @@ print_state_information(#state{locks = Locks} = State) ->
print(kv("#tries", s(Stats#stats.tries))),
print(kv("#colls", s(Stats#stats.colls))),
print(kv("wait time", s(Stats#stats.time) ++ " us" ++ " ( " ++ s(Stats#stats.time/1000000) ++ " s)")),
- print(kv("percent of duration", s(Stats#stats.time/State#state.duration*100) ++ " %")),
+ print(kv("percent of duration", s(percent(Stats#stats.time, State#state.duration)) ++ " %")),
ok.
diff --git a/lib/tools/test/emacs_SUITE.erl b/lib/tools/test/emacs_SUITE.erl
index 77a8813db5..f4e78da667 100644
--- a/lib/tools/test/emacs_SUITE.erl
+++ b/lib/tools/test/emacs_SUITE.erl
@@ -23,10 +23,10 @@
-export([all/0, init_per_testcase/2, end_per_testcase/2]).
--export([bif_highlight/1]).
+-export([bif_highlight/1, indent/1]).
-all() ->
- [bif_highlight].
+all() ->
+ [bif_highlight, indent].
init_per_testcase(_Case, Config) ->
ErlangEl = filename:join([code:lib_dir(tools),"emacs","erlang.el"]),
@@ -74,4 +74,69 @@ check_bif_highlight(Bin, Tag, Compare) ->
[] = Compare -- EmacsIntBifs,
[] = EmacsIntBifs -- Compare.
-
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+indent(Config) ->
+ case emacs_version_ok() of
+ false -> {skip, "Old or no emacs found"};
+ true ->
+ Def = filename:dirname(code:which(?MODULE)) ++ "/" ++ ?MODULE_STRING ++ "_data",
+ Dir = proplists:get_value(data_dir, Config, Def),
+ OrigFs = filelib:wildcard(Dir ++ "/*"),
+ io:format("Dir: ~s~nFs: ~p~n", [Dir, OrigFs]),
+ Fs = [{File, unindent(File)} || File <- OrigFs,
+ filename:extension(File) =:= ""],
+ Indent = fun emacs/1,
+ [Indent(File) || {_, File} <- Fs],
+ Res = [diff(Orig, File) || {Orig, File} <- Fs],
+ [file:delete(File) || {ok, File} <- Res], %% Cleanup
+ [] = [Fail || {fail, Fail} <- Res],
+ ok
+ end.
+
+unindent(Input) ->
+ Output = Input ++ ".erl",
+ {ok, Bin} = file:read_file(Input),
+ Lines0 = string:split(Bin, "\n", all),
+ Lines = [string:trim(Line, leading, [$\s,$\t]) || Line <- Lines0],
+ %% io:format("File: ~s lines: ~w~n", [Input, length(Lines0)]),
+ %% [io:format("~s~n", [L]) || L <- Lines],
+ ok = file:write_file(Output, lists:join("\n", Lines)),
+ Output.
+
+diff(Orig, File) ->
+ case os:cmd(["diff ", Orig, " ", File]) of
+ "" -> {ok, File};
+ Diff ->
+ io:format("Fail: ~s vs ~s~n~s~n~n",[Orig, File, Diff]),
+ {fail, File}
+ end.
+
+emacs_version_ok() ->
+ case os:cmd("emacs --version | head -1") of
+ "GNU Emacs " ++ Ver ->
+ case string:to_float(Ver) of
+ {Vsn, _} when Vsn >= 24.1 ->
+ true;
+ _ ->
+ io:format("Emacs version fail~n~s~n~n",[Ver]),
+ false
+ end;
+ Res ->
+ io:format("Emacs version fail~n~s~n~n",[Res]),
+ false
+ end.
+
+emacs(File) ->
+ EmacsErlDir = filename:join([code:lib_dir(tools), "emacs"]),
+ Cmd = ["emacs ",
+ "--batch --quick ",
+ "--directory ", EmacsErlDir, " ",
+ "--eval \"(require 'erlang-start)\" ",
+ File, " ",
+ "--eval '(indent-region (point-min) (point-max) nil)' ",
+ "--eval '(save-buffer 0)'"
+ ],
+ _Res = os:cmd(Cmd),
+ % io:format("cmd ~s:~n=> ~s~n", [Cmd, _Res]),
+ ok.
diff --git a/lib/tools/test/emacs_SUITE_data/comments b/lib/tools/test/emacs_SUITE_data/comments
new file mode 100644
index 0000000000..ff974ca295
--- /dev/null
+++ b/lib/tools/test/emacs_SUITE_data/comments
@@ -0,0 +1,25 @@
+%% -*- Mode: erlang; indent-tabs-mode: nil -*-
+%% Copyright Ericsson AB 2017. All Rights Reserved.
+
+%%% 3 comment chars: always left indented
+%%% 2 comment chars: Context indented
+%%% 1 comment char: Rigth indented
+
+%%% left
+%% context dependent
+ % rigth
+
+func() ->
+%%% left
+ %% context dependent
+ % right indented
+ case get(foo) of
+ undefined ->
+ %% Testing indention
+ ok;
+ %% Catch all
+ Other ->
+ Other
+ end,
+ ok.
+
diff --git a/lib/tools/test/emacs_SUITE_data/comprehensions b/lib/tools/test/emacs_SUITE_data/comprehensions
new file mode 100644
index 0000000000..45279850a5
--- /dev/null
+++ b/lib/tools/test/emacs_SUITE_data/comprehensions
@@ -0,0 +1,47 @@
+%% -*- Mode: erlang; indent-tabs-mode: nil -*-
+%% Copyright Ericsson AB 2017. All Rights Reserved.
+
+%%% indentation of comprehensions
+
+%%% Not everything in these test are set in stone
+%%% better indentation rules can be added but by having
+%%% these tests we can see what changes in new implementations
+%%% and notice when doing unintentional changes
+
+list() ->
+ %% I don't have a good idea how we want to handle this
+ %% but they are here to show how they are indented today.
+ Result1 = [X ||
+ #record{a=X} <- lists:seq(1, 10),
+ true = (X rem 2)
+ ],
+ Result2 = [X || <<X:32,_:32>> <= <<0:512>>,
+ true = (X rem 2)
+ ],
+ Res = [ func(X,
+ arg2)
+ ||
+ #record{a=X} <- lists:seq(1, 10),
+ true = (X rem 2)
+ ],
+ Result1.
+
+binary(B) ->
+ Binary1 = << <<X:8>> ||
+ #record{a=X} <- lists:seq(1, 10),
+ true = (X rem 2)
+ >>,
+
+ Binary2 = << <<X:8>> || <<X:32,_:32>> <= <<0:512>>,
+ true = (X rem 2)
+ >>,
+
+ Bin3 = <<
+ <<
+ X:8,
+ 34:8
+ >>
+ || <<X:32,_:32>> <= <<0:512>>,
+ true = (X rem 2)
+ >>,
+ ok.
diff --git a/lib/tools/test/emacs_SUITE_data/funcs b/lib/tools/test/emacs_SUITE_data/funcs
new file mode 100644
index 0000000000..877f982005
--- /dev/null
+++ b/lib/tools/test/emacs_SUITE_data/funcs
@@ -0,0 +1,174 @@
+%% -*- Mode: erlang; indent-tabs-mode: nil -*-
+%% Copyright Ericsson AB 2017. All Rights Reserved.
+
+%%% Function (and funs) indentation
+
+%%% Not everything in these test are set in stone
+%%% better indentation rules can be added but by having
+%%% these tests we can see what changes in new implementations
+%%% and notice when doing unintentional changes
+
+-export([
+ func1/0,
+ func2/0,
+ a_function_with_a_very_very_long_name/0,
+ when1/2
+ ]).
+
+-compile([nowarn_unused_functions,
+ {inline, [
+ func2/2,
+ func3/2
+ ]
+ }
+ ]).
+
+func1() ->
+ basic.
+
+func2(A1,
+ A2) ->
+ ok.
+
+func3(
+ A1,
+ A2
+ ) ->
+ ok.
+
+%% Okeefe style
+func4(A1
+ ,A2
+ ,A3
+ ) ->
+ ok.
+
+func5(
+ A41
+ ,A42) ->
+ ok.
+
+a_function_with_a_very_very_long_name() ->
+ A00 = #record{
+ field1=1,
+ field2=1
+ },
+ A00.
+
+when1(W1, W2)
+ when is_number(W1),
+ is_number(W2) ->
+ ok.
+
+when2(W1,W2,W3) when
+ W1 > W2,
+ W2 > W3 ->
+ ok.
+
+when3(W1,W2,W3) when
+ W1 > W2,
+ W2 > W3
+ ->
+ ok.
+
+when4(W1,W2,W3)
+ when
+ W1 > W2,
+ W2 > W3 ->
+ ok.
+
+match1({[H|T],
+ Other},
+ M1A2) ->
+ ok.
+
+match2(
+ {
+ [H|T],
+ Other
+ },
+ M2A2
+ ) ->
+ ok.
+
+match3({
+ M3A1,
+ [
+ H |
+ T
+ ],
+ Other
+ },
+ M3A2
+ ) ->
+ ok.
+
+match4(<<
+ M4A:8,
+ M4B:16/unsigned-integer,
+ _/binary
+ >>,
+ M4C) ->
+ ok.
+
+match5(M5A,
+ #record{
+ b=M5B,
+ c=M5C
+ }
+ ) ->
+ ok.
+
+match6(M6A,
+ #{key6a := a6,
+ key6b := b6
+ }) ->
+ ok.
+
+funs(1)
+ when
+ X ->
+ %% Changed fun to one indention level
+ %% 'when' and several clause forces a depth of '4'
+ Var = spawn(fun(X, _)
+ when X == 2;
+ X > 10 ->
+ hello,
+ case Hello() of
+ true when is_atom(X) ->
+ foo;
+ false ->
+ bar
+ end;
+ (Foo) when is_atom(Foo),
+ is_integer(X) ->
+ X = 6 * 45,
+ Y = true andalso
+ kalle
+ end),
+ Var;
+funs(2) ->
+ %% check EEP37 named funs
+ Fn1 = fun
+ Factory(N) when
+ N > 0 ->
+ F = Fact(N-1),
+ N * F;
+ Factory(0) ->
+ 1
+ end,
+ Fn1;
+funs(3) ->
+ %% check anonymous funs too
+ Fn2 = fun(0) ->
+ 1;
+ (N) ->
+ N
+ end,
+ ok;
+funs(4) ->
+ X = lists:foldr(fun(M) ->
+ <<M/binary, " ">>
+ end, [], Z),
+ A = <<X/binary, 0:8>>,
+ A.
diff --git a/lib/tools/test/emacs_SUITE_data/highlight b/lib/tools/test/emacs_SUITE_data/highlight
new file mode 100644
index 0000000000..0719f6516a
--- /dev/null
+++ b/lib/tools/test/emacs_SUITE_data/highlight
@@ -0,0 +1,78 @@
+%% -*- Mode: erlang; indent-tabs-mode: nil -*-
+%% Copyright Ericsson AB 2017. All Rights Reserved.
+
+%%% Open this file in your editor and manually check the colors of
+%%% different types and calls and builtin words
+
+%%% Not everything in these test are set in stone
+%%% better indentation rules can be added but by having
+%%% these tests we can see what changes in new implementations
+%%% and notice when doing unintentional changes
+
+
+highlighting(X) % Function definitions should be highlighted
+ when is_integer(X) -> % and so should `when' and `is_integer' be
+ %% Highlighting
+ %% Various characters (we keep an `atom' after to see that highlighting ends)
+ $a,atom, % Characters should be marked
+ "string",atom, % and strings
+ 'asdasd',atom, % quote should be atoms??
+ 'VaV',atom,
+ 'aVa',atom,
+ '\'atom',atom,
+ 'atom\'',atom,
+ 'at\'om',atom,
+ '#1',atom,
+
+ $", atom, % atom should be ok
+ $', atom,
+
+ "string$", atom, "string$", atom, % currently buggy I know...
+ "string\$", atom, % workaround for bug above
+
+ "char $in string", atom,
+
+ 'atom$', atom, 'atom$', atom,
+ 'atom\$', atom,
+
+ 'char $in atom', atom,
+
+ $[, ${, $\\, atom,
+ ?MACRO_1,
+ ?MACRO_2(foo),
+
+ %% Numerical constants
+ 16#DD, % Should not be highlighted
+ 32#dd, % Should not be highlighted
+ 32#ddAB, % Should not be highlighted
+ 32#101, % Should not be highlighted
+ 32#ABTR, % Should not be highlighted
+
+ %% Variables
+ Variables = lists:foo(),
+ _Variables = lists:foo(),
+ AppSpec = Xyz/2,
+ Module42 = Xyz(foo, bar),
+ Module:foo(),
+ _Module:foo(), %
+ FooÅÅ = lists:reverse([tl,hd,tl,hd]), % Should highlight FooÅÅ
+ _FooÅÅ = 42, % Should highlight _FooÅÅ
+
+ %% Bifs
+ erlang:registered(),
+ registered(),
+ hd(tl(tl(hd([a,b,c])))),
+ erlang:anything(lists),
+ %% Guards
+ is_atom(foo), is_float(2.3), is_integer(32), is_number(4323.3),
+ is_function(Fun), is_pid(self()),
+ not_a_guard:is_list([]),
+ %% Other Types
+
+ atom, % not (currently) hightlighted
+ 234234,
+ 234.43,
+
+ [list, are, not, higlighted],
+ {nor, is, tuple},
+ ok.
diff --git a/lib/tools/test/emacs_SUITE_data/icr b/lib/tools/test/emacs_SUITE_data/icr
new file mode 100644
index 0000000000..8445c1a74d
--- /dev/null
+++ b/lib/tools/test/emacs_SUITE_data/icr
@@ -0,0 +1,157 @@
+%% -*- Mode: erlang; indent-tabs-mode: nil -*-
+%% Copyright Ericsson AB 2017. All Rights Reserved.
+
+%%% indentation of if case receive statements
+
+%%% Not everything in these test are set in stone
+%%% better indentation rules can be added but by having
+%%% these tests we can see what changes in new implementations
+%%% and notice when doing unintentional changes
+
+indent_if(1, Z) ->
+ %% If
+ if Z >= 0 ->
+ X = 43 div Z,
+ X;
+ Z =< 10 ->
+ X = 43 div Z,
+ X;
+ Z == 5 orelse
+ Z == 7 ->
+ X = 43 div Z,
+ X;
+ is_number(Z),
+ Z < 32 ->
+ Z;
+ is_number(Z);
+ Z < 32 ->
+ Z * 32;
+ true ->
+ if_works
+ end;
+indent_if(2, Z) ->
+ %% If
+ if
+ Z >= 0 ->
+ X = 43 div Z,
+ X
+ ; Z =< 10 ->
+ 43 div Z
+ ; Z == 5 orelse
+ Z == 7 ->
+ X = 43 div Z,
+ X
+ ; is_number(Z),
+ Z < 32 ->
+ Z
+ ; true ->
+ if_works
+ end.
+
+indent_case(1, Z) ->
+ %% Case
+ case {Z, foo, bar} of
+ {Z,_,_} ->
+ X = 43 div 4,
+ foo(X);
+ {Z,_,_} when
+ Z =:= 42 -> % line should be indented as a when
+ X = 43 div 4,
+ foo(X);
+ {Z,_,_}
+ when Z < 10 orelse
+ Z =:= foo -> % Binary op alignment here !!!
+ X = 43 div 4,
+ Bool = Z < 5 orelse % Binary op args align differently after when
+ Z =:= foo, % and elsewhere ???
+ foo(X);
+ {Z,_,_}
+ when % when should be indented
+ Z < 10 % and the guards should follow when
+ andalso % unsure about how though
+ true ->
+ X = 43 div 4,
+ foo(X)
+ end;
+indent_case(2, Z) ->
+ %% Case
+ case {Z, foo, bar} of
+ {Z,_,_} ->
+ X = 43 div 4,
+ foo(X)
+ ; {Z,_,_} when
+ Z =:= 42 -> % line should be indented as a when
+ X = 43 div 4,
+ foo(X)
+ ; {Z,_,_}
+ when Z < 10 -> % when should be indented
+ X = 43 div 4,
+ foo(X)
+ ; {Z,_,_}
+ when % when should be indented
+ Z < 10 % and the guards should follow when
+ andalso % unsure about how though
+ true ->
+ X = 43 div 4,
+ foo(X)
+ end.
+
+indent_begin(Z) ->
+ %% Begin
+ begin
+ sune,
+ Z = 74234 +
+ foo(8456) +
+ 345 div 43,
+ Foo = begin
+ ok,
+ foo(234),
+ begin
+ io:format("Down here\n")
+ end
+ end,
+ {Foo,
+ bar}
+ end.
+
+indent_receive(1) ->
+ %% receive
+ receive
+ {Z,_,_} ->
+ X = 43 div 4,
+ foo(X)
+ ; Z ->
+ X = 43 div 4,
+ foo(X)
+ end,
+ ok;
+indent_receive(2) ->
+ receive
+ {Z,_,_} ->
+ X = 43 div 4,
+ foo(X);
+ Z % added clause
+ when Z =:= 1 -> % This line should be indented by 2
+ X = 43 div 4,
+ foo(X);
+ Z when % added clause
+ Z =:= 2 -> % This line should be indented by 2
+ X = 43 div 4,
+ foo(X);
+ Z ->
+ X = 43 div 4,
+ foo(X)
+ after infinity ->
+ foo(X),
+ asd(X),
+ 5*43
+ end,
+ ok;
+indent_receive() ->
+ receive
+ after 10 ->
+ foo(X),
+ asd(X),
+ 5*43
+ end,
+ ok.
diff --git a/lib/tools/test/emacs_SUITE_data/macros b/lib/tools/test/emacs_SUITE_data/macros
new file mode 100644
index 0000000000..6c874e9187
--- /dev/null
+++ b/lib/tools/test/emacs_SUITE_data/macros
@@ -0,0 +1,31 @@
+%% -*- Mode: erlang; indent-tabs-mode: nil -*-
+%% Copyright Ericsson AB 2017. All Rights Reserved.
+
+%%% Macros should be indented as code
+
+-define(M0, ok).
+
+-define(M1,
+ case X of
+ undefined -> error;
+ _ -> ok
+ end).
+
+-define(M2(M2A1,
+ M2A2),
+ func(M2A1,
+ M2A2)
+ ).
+
+-define(
+ M3,
+ undefined
+ ).
+
+-ifdef(DEBUG).
+-define(LOG,
+ logger:log(?MODULE,?LINE)
+ ).
+-else().
+-define(LOG, ok).
+-endif().
diff --git a/lib/tools/test/emacs_SUITE_data/records b/lib/tools/test/emacs_SUITE_data/records
new file mode 100644
index 0000000000..241582718c
--- /dev/null
+++ b/lib/tools/test/emacs_SUITE_data/records
@@ -0,0 +1,35 @@
+%% -*- Mode: erlang; indent-tabs-mode: nil -*-
+%% Copyright Ericsson AB 2017. All Rights Reserved.
+
+%% Test that records are indented correctly
+
+-record(record0,
+ {
+ r0a,
+ r0b,
+ r0c
+ }).
+
+-record(record1, {r1a,
+ r1b,
+ r1c
+ }).
+
+-record(record2, {
+ r2a,
+ r2b
+ }).
+
+-record(record3, {r3a = 8#42423 bor
+ 8#4234,
+ r3b = 8#5432
+ bor 2#1010101,
+ r3c = 123 +
+ 234,
+ r3d}).
+
+-record(record5,
+ { r5a = 1 :: integer()
+ , r5b = foobar :: atom()
+ }).
+
diff --git a/lib/tools/test/emacs_SUITE_data/terms b/lib/tools/test/emacs_SUITE_data/terms
new file mode 100644
index 0000000000..352364a73c
--- /dev/null
+++ b/lib/tools/test/emacs_SUITE_data/terms
@@ -0,0 +1,174 @@
+%% -*- Mode: erlang; indent-tabs-mode: nil -*-
+%% Copyright Ericsson AB 2017. All Rights Reserved.
+
+%%% indentation of terms contain builtin types
+
+%%% Not everything in these test are set in stone
+%%% better indentation rules can be added but by having
+%%% these tests we can see what changes in new implementations
+%%% and notice when doing unintentional changes
+
+
+list(1) ->
+ [a,
+ b,
+ c
+ ];
+list(2) ->
+ [ a,
+ b, c
+ ];
+list(3) ->
+ [
+ a,
+ b, c
+ ];
+list(4) ->
+ [ a
+ , b
+ , c
+ ].
+
+tuple(1) ->
+ {a,
+ b,c
+ };
+tuple(2) ->
+ { a,
+ b,c
+ };
+tuple(3) ->
+ {
+ a,
+ b,c
+ };
+tuple(4) ->
+ { a
+ , b
+ ,c
+ }.
+
+binary(1) ->
+ <<1:8,
+ 2:8
+ >>;
+binary(2) ->
+ <<
+ 1:8,
+ 2:8
+ >>;
+binary(3) ->
+ << 1:8,
+ 2:8
+ >>;
+binary(4) ->
+ <<
+ 1:8
+ ,2:8
+ >>;
+binary(5) ->
+ << 1:8
+ , 2:8
+ >>.
+
+record(1) ->
+ #record{a=1,
+ b=2
+ };
+record(2) ->
+ #record{ a=1,
+ b=2
+ };
+record(3) ->
+ #record{
+ a=1,
+ b=2
+ };
+record(4) ->
+ #record{
+ a=1
+ ,b=2
+ };
+record(Record) ->
+ Record#record{
+ a=1
+ ,b=2
+ }.
+
+map(1) ->
+ #{a=>1,
+ b=>2
+ };
+map(2) ->
+ #{ a=>1,
+ b=>2
+ };
+map(3) ->
+ #{
+ a=>1,
+ b=>2
+ };
+map(4) ->
+ #{
+ a => <<"a">>
+ ,b => 2
+ };
+map(MapVar) ->
+ MapVar = #{a :=<<"a">>
+ ,b:=1}.
+
+deep(Rec) ->
+ Rec#rec{ atom = 'atom',
+ map = #{ k1 => {v,
+ 1},
+ k2 => [
+ 1,
+ 2,
+ 3
+ ],
+ {key,
+ 3}
+ =>
+ <<
+ 123:8,
+ 255:8
+ >>
+ }
+ }.
+
+%% Record indentation
+some_function_with_a_very_long_name() ->
+ #'a-long-record-name-like-it-sometimes-is-with-asn.1-records'{
+ field1=a,
+ field2=b},
+ case dummy_function_with_a_very_very_long_name(x) of
+ #'a-long-record-name-like-it-sometimes-is-with-asn.1-records'{
+ field1=a,
+ field2=b} ->
+ ok;
+ Var = #'a-long-record-name-like-it-sometimes-is-with-asn.1-records'{
+ field1=a,
+ field2=b} ->
+ Var#'a-long-record-name-like-it-sometimes-is-with-asn.1-records'{
+ field1=a,
+ field2=b};
+ #xyz{
+ a=1,
+ b=2} ->
+ ok
+ end.
+
+some_function_name_xyz(xyzzy, #some_record{
+ field1=Field1,
+ field2=Field2}) ->
+ SomeVariable = f(#'Some-long-record-name'{
+ field_a = 1,
+ 'inter-xyz-parameters' =
+ #'Some-other-very-long-record-name'{
+ field2 = Field1,
+ field2 = Field2}}),
+ {ok, SomeVariable}.
+
+foo() ->
+ [#foo{
+ foo = foo}].
diff --git a/lib/tools/test/emacs_SUITE_data/try_catch b/lib/tools/test/emacs_SUITE_data/try_catch
new file mode 100644
index 0000000000..0005b2003a
--- /dev/null
+++ b/lib/tools/test/emacs_SUITE_data/try_catch
@@ -0,0 +1,166 @@
+%% -*- Mode: erlang; indent-tabs-mode: nil -*-
+%% Copyright Ericsson AB 2017. All Rights Reserved.
+
+%%% Try and catch indentation is hard
+
+%%% Not everything in these test are set in stone
+%%% better indentation rules can be added but by having
+%%% these tests we can see what changes in new implementations
+%%% and notice when doing unintentional changes
+
+try_catch() ->
+ try
+ io:format(stdout, "Parsing file ~s, ",
+ [St0#leex.xfile]),
+ {ok,Line3,REAs,Actions,St3} =
+ parse_rules(Xfile, Line2, Macs, St2)
+ catch
+ exit:{badarg,R} ->
+ foo(R),
+ io:format(stdout,
+ "ERROR reason ~p~n",
+ R);
+ error:R
+ when R =:= 42 -> % when should be indented
+ foo(R);
+ error:R
+ when % when should be indented
+ R =:= 42 -> % but unsure about this (maybe 2 more)
+ foo(R);
+ error:R when
+ R =:= foo -> % line should be 2 indented (works)
+ foo(R);
+ error:R ->
+ foo(R),
+ io:format(stdout,
+ "ERROR reason ~p~n",
+ R)
+ after
+ foo('after'),
+ file:close(Xfile)
+ end;
+try_catch() ->
+ try
+ foo(bar)
+ of
+ X when true andalso
+ kalle ->
+ io:format(stdout, "Parsing file ~s, ",
+ [St0#leex.xfile]),
+ {ok,Line3,REAs,Actions,St3} =
+ parse_rules(Xfile, Line2, Macs, St2);
+ X
+ when false andalso % when should be 2 indented
+ bengt ->
+ gurka();
+ X when
+ false andalso % line should be 2 indented
+ not bengt ->
+ gurka();
+ X ->
+ io:format(stdout, "Parsing file ~s, ",
+ [St0#leex.xfile]),
+ {ok,Line3,REAs,Actions,St3} =
+ parse_rules(Xfile, Line2, Macs, St2)
+ catch
+ exit:{badarg,R} ->
+ foo(R),
+ io:format(stdout,
+ "ERROR reason ~p~n",
+ R);
+ error:R ->
+ foo(R),
+ io:format(stdout,
+ "ERROR reason ~p~n",
+ R)
+ after
+ foo('after'),
+ file:close(Xfile),
+ bar(with_long_arg,
+ with_second_arg)
+ end;
+try_catch() ->
+ try foo()
+ after
+ foo(),
+ bar(with_long_arg,
+ with_second_arg)
+ end.
+
+indent_catch() ->
+ D = B +
+ float(43.1),
+
+ B = catch oskar(X),
+
+ A = catch (baz +
+ bax),
+ catch foo(),
+
+ C = catch B +
+ float(43.1),
+
+ case catch foo(X) of
+ A ->
+ B
+ end,
+
+ case
+ catch foo(X)
+ of
+ A ->
+ B
+ end,
+
+ case
+ foo(X)
+ of
+ A ->
+ catch B,
+ X
+ end,
+
+ try sune of
+ _ -> foo
+ catch _:_ -> baf
+ end,
+
+ Variable = try
+ sune
+ of
+ _ ->
+ X = 5,
+ (catch foo(X)),
+ X + 10
+ catch _:_ -> baf
+ after cleanup()
+ end,
+
+ try
+ (catch sune)
+ of
+ _ ->
+ foo1(),
+ catch foo() %% BUGBUG can't handle catch inside try without parentheses
+ catch _:_ ->
+ baf
+ end,
+
+ try
+ (catch exit())
+ catch
+ _ ->
+ catch baf()
+ end,
+ ok.
+
+%% this used to result in 2x the correct indentation within the function
+%% body, due to the function name being mistaken for a keyword
+catcher(N) ->
+ try generate_exception(N) of
+ Val -> {N, normal, Val}
+ catch
+ throw:X -> {N, caught, thrown, X};
+ exit:X -> {N, caught, exited, X};
+ error:X -> {N, caught, error, X}
+ end.
diff --git a/lib/tools/test/emacs_SUITE_data/type_specs b/lib/tools/test/emacs_SUITE_data/type_specs
new file mode 100644
index 0000000000..e71841cc7a
--- /dev/null
+++ b/lib/tools/test/emacs_SUITE_data/type_specs
@@ -0,0 +1,110 @@
+%% -*- Mode: erlang; indent-tabs-mode: nil -*-
+%% Copyright Ericsson AB 2017. All Rights Reserved.
+
+%% Tests how types and specs are indented (also that the editor can parse them)
+%% May need improvements
+
+
+-type ann() :: Var :: integer().
+-type ann2() ::
+ 'return'
+ | 'return_white_spaces'
+ | 'return_comments'
+ | 'text' | ann().
+-type paren() ::
+ (ann2()).
+
+-type t6() ::
+ 1 | 2 | 3 |
+ 'foo'
+ | 'bar'.
+
+-type t8() :: {any(),none(),pid(),port(),
+ reference(),float()}.
+
+-type t14() :: [erl_scan:foo() |
+ %% Should be highlighted
+ term() |
+ boolean() |
+ byte() |
+ char() |
+ non_neg_integer() | nonempty_list() |
+ pos_integer() |
+ neg_integer() |
+ number() |
+ list() |
+ nonempty_improper_list() | nonempty_maybe_improper_list() |
+ maybe_improper_list() | string() | iolist() | byte() |
+ module() |
+ mfa() |
+ node() |
+ timeout() |
+ no_return() |
+ %% Should not be highlighted
+ nonempty_() | nonlist() |
+ erl_scan:bar(34, 92) | t13() | m:f(integer() | <<_:_*16>>)].
+
+-type t15() :: {binary(),<<>>,<<_:34>>,<<_:_*42>>,
+ <<_:3,_:_*14>>,<<>>} | [<<>>|<<_:34>>|<<_:16>>|
+ <<_:3,_:_*1472>>|<<_:19,_:_*14>>| <<_:34>>|
+ <<_:34>>|<<_:34>>|<<_:34>>].
+
+-type t18() ::
+ fun(() -> t17() | t16()).
+-type t19() ::
+ fun((t18()) -> t16()) |
+ fun((nonempty_maybe_improper_list('integer', any())|
+ 1|2|3|a|b|<<_:3,_:_*14>>|integer())
+ ->
+ nonempty_maybe_improper_list('integer', any())| %% left to col 16?
+ 1|2|3|a|b|<<_:3,_:_*14>>|integer()). %% left to col 16?
+-type t20() :: [t19(), ...].
+-type t25() :: #rec3{f123 :: [t24() |
+ 1|2|3|4|a|b|c|d|
+ nonempty_maybe_improper_list(integer, any())]}.
+-type t26() :: #rec4{ a :: integer()
+ , b :: any()
+ }.
+
+%% Spec
+
+-spec t1(FooBar :: t99()) -> t99();
+ (t2()) -> t2();
+ (t4()) -> t4() when is_subtype(t4(), t24);
+ (t23()) -> t23() when is_subtype(t23(), atom()),
+ is_subtype(t23(), t14());
+ (t24()) -> t24() when is_subtype(t24(), atom()),
+ is_subtype(t24(), t14()),
+ is_subtype(t24(), t4()).
+
+-spec over(I :: integer()) -> R1 :: foo:typen();
+ (A :: atom()) -> R2 :: foo:atomen();
+ (T :: tuple()) -> R3 :: bar:typen().
+
+-spec mod:t2() -> any().
+
+-spec handle_cast(Cast :: {'exchange', node(), [[name(),...]]}
+ | {'del_member', name(), pid()},
+ #state{}) -> {'noreply', #state{}}.
+
+-spec handle_cast(Cast ::
+ {'exchange', node(), [[name(),...]]}
+ | {'del_member', name(), pid()},
+ #state{}) ->
+ {'noreply', #state{}}. %% left to col 10?
+
+-spec all(fun((T) -> boolean()), List :: [T]) ->
+ boolean() when is_subtype(T, term()). % (*)
+
+-spec get_closest_pid(term()) ->
+ Return :: pid() %% left to col 10?
+ | {'error', {'no_process', term()}} %% left to col 10?
+ | {'no_such_group', term()}. %% left to col 10?
+
+-spec add( X :: integer()
+ , Y :: integer()
+ ) -> integer().
+
+-opaque attributes_data() ::
+ [{'column', column()} | {'line', info_line()} |
+ {'text', string()}] | {line(),column()}.
diff --git a/lib/tools/test/lcnt_SUITE.erl b/lib/tools/test/lcnt_SUITE.erl
index 146c915087..a79572a742 100644
--- a/lib/tools/test/lcnt_SUITE.erl
+++ b/lib/tools/test/lcnt_SUITE.erl
@@ -30,6 +30,8 @@
t_conflicts/1,
t_locations/1,
t_swap_keys/1,
+ t_implicit_start/1,
+ t_crash_before_collect/1,
smoke_lcnt/1]).
init_per_testcase(_Case, Config) ->
@@ -44,8 +46,8 @@ suite() ->
{timetrap,{minutes,4}}].
all() ->
- [t_load, t_conflicts, t_locations, t_swap_keys,
- smoke_lcnt].
+ [t_load, t_conflicts, t_locations, t_swap_keys, t_implicit_start,
+ t_crash_before_collect, smoke_lcnt].
%%----------------------------------------------------------------------
%% Tests
@@ -149,6 +151,15 @@ t_swap_keys_file([File|Files]) ->
ok = lcnt:stop(),
t_swap_keys_file(Files).
+%% Prior to OTP-14913 this would crash with 'noproc' as the lcnt server hadn't
+%% been started yet.
+t_implicit_start(Config) when is_list(Config) ->
+ ok = lcnt:conflicts().
+
+t_crash_before_collect(Config) when is_list(Config) ->
+ {ok, _} = lcnt:start(),
+ ok = lcnt:information().
+
%% Simple smoke test of actual lock-counting, if running on
%% a run-time with lock-counting enabled.
smoke_lcnt(Config) ->
diff --git a/lib/tools/vsn.mk b/lib/tools/vsn.mk
index 6cafbca6a7..f9723c0f9b 100644
--- a/lib/tools/vsn.mk
+++ b/lib/tools/vsn.mk
@@ -1 +1 @@
-TOOLS_VSN = 2.11.1
+TOOLS_VSN = 2.11.2
diff --git a/lib/wx/api_gen/gen_util.erl b/lib/wx/api_gen/gen_util.erl
index 5e2f405498..3068a2f4ea 100644
--- a/lib/wx/api_gen/gen_util.erl
+++ b/lib/wx/api_gen/gen_util.erl
@@ -106,8 +106,8 @@ check_diff(Diff) ->
throw:_ -> diff;
error:{badmatch,_} ->
diff;
- _:What ->
- io:format("~p:~p: ~p ~p~n", [?MODULE,?LINE, What, erlang:get_stacktrace()]),
+ _:What:Stacktrace ->
+ io:format("~p:~p: ~p ~p~n", [?MODULE,?LINE, What, Stacktrace]),
diff
end.
diff --git a/lib/wx/api_gen/gl_gen.erl b/lib/wx/api_gen/gl_gen.erl
index 7e3766a43b..6f68517c16 100644
--- a/lib/wx/api_gen/gl_gen.erl
+++ b/lib/wx/api_gen/gl_gen.erl
@@ -47,9 +47,9 @@ safe(What, QuitOnErr) ->
What(),
io:format("Completed successfully~n~n", []),
QuitOnErr andalso gen_util:halt(0)
- catch Err:Reason ->
+ catch Err:Reason:Stacktrace ->
io:format("Error ~p: ~p:~p~n ~p~n",
- [get(current_func),Err,Reason,erlang:get_stacktrace()]),
+ [get(current_func),Err,Reason,Stacktrace]),
(catch gen_util:close()),
timer:sleep(1999),
QuitOnErr andalso gen_util:halt(1)
diff --git a/lib/wx/api_gen/gl_gen_erl.erl b/lib/wx/api_gen/gl_gen_erl.erl
index 45f5fd8f4c..7e673c2987 100644
--- a/lib/wx/api_gen/gl_gen_erl.erl
+++ b/lib/wx/api_gen/gl_gen_erl.erl
@@ -246,8 +246,8 @@ gen_types(Where) ->
gen_export(F) ->
try gen_export_1(F)
- catch E:R ->
- io:format("Crash ~p:~p in ~p ~n",[E,R, erlang:get_stacktrace()]),
+ catch E:R:S ->
+ io:format("Crash ~p:~p in ~p ~n",[E,R,S]),
io:format("Func = ~p~n ~p", [F, get(F)])
end.
@@ -489,8 +489,8 @@ doc_return_types2(T, Ps) ->
doc_arg_type(#arg{name=Name,type=T}) ->
try
erl_arg_name(Name) ++ " :: " ++ doc_arg_type2(T)
- catch _:Error ->
- io:format("Error spec: ~p ~p~n~p~n",[Name, Error, erlang:get_stacktrace()]),
+ catch _:Error:Stacktrace ->
+ io:format("Error spec: ~p ~p~n~p~n",[Name, Error, Stacktrace]),
exit(error)
end.
diff --git a/lib/wx/api_gen/wx_gen.erl b/lib/wx/api_gen/wx_gen.erl
index aadfe4b111..ab70a588ab 100644
--- a/lib/wx/api_gen/wx_gen.erl
+++ b/lib/wx/api_gen/wx_gen.erl
@@ -47,9 +47,9 @@ safe(What, QuitOnErr) ->
What(),
io:format("Completed successfully~n~n", []),
QuitOnErr andalso gen_util:halt(0)
- catch Err:Reason ->
+ catch Err:Reason:Stacktrace ->
io:format("Error in ~p ~p~n", [get(current_class),get(current_func)]),
- erlang:display({Err,Reason, erlang:get_stacktrace()}),
+ erlang:display({Err,Reason,Stacktrace}),
catch gen_util:close(),
QuitOnErr andalso gen_util:halt(1)
end.
diff --git a/lib/wx/examples/demo/ex_aui.erl b/lib/wx/examples/demo/ex_aui.erl
index d8fc0021f1..97805621ed 100644
--- a/lib/wx/examples/demo/ex_aui.erl
+++ b/lib/wx/examples/demo/ex_aui.erl
@@ -87,8 +87,7 @@ do_init(Config) ->
wxAuiManager:update(Manager),
process_flag(trap_exit, true),
{Panel, #state{parent=Panel, config=Config, aui=Manager}}
- catch Class:Reason ->
- ST = erlang:get_stacktrace(),
+ catch Class:Reason:ST ->
io:format("AUI Crashed ~p ~p~n",[Reason, ST]),
wxAuiManager:unInit(Manager),
wxAuiManager:destroy(Manager),
diff --git a/lib/wx/src/wx.erl b/lib/wx/src/wx.erl
index 34bf06cf46..6dd3c148db 100644
--- a/lib/wx/src/wx.erl
+++ b/lib/wx/src/wx.erl
@@ -183,7 +183,7 @@ batch(Fun) ->
ok = wxe_util:cast(?BATCH_BEGIN, <<>>),
try Fun()
catch
- error:W -> erlang:exit({W, erlang:get_stacktrace()});
+ error:W:S -> erlang:exit({W, S});
throw:W -> erlang:throw(W);
exit:W -> erlang:exit(W)
after
@@ -196,7 +196,7 @@ foreach(Fun, List) ->
ok = wxe_util:cast(?BATCH_BEGIN, <<>>),
try lists:foreach(Fun, List)
catch
- error:W -> erlang:exit({W, erlang:get_stacktrace()});
+ error:W:S -> erlang:exit({W, S});
throw:W -> erlang:throw(W);
exit:W -> erlang:exit(W)
after
@@ -209,7 +209,7 @@ map(Fun, List) ->
ok = wxe_util:cast(?BATCH_BEGIN, <<>>),
try lists:map(Fun, List)
catch
- error:W -> erlang:exit({W, erlang:get_stacktrace()});
+ error:W:S -> erlang:exit({W, S});
throw:W -> erlang:throw(W);
exit:W -> erlang:exit(W)
after
@@ -222,7 +222,7 @@ foldl(Fun, Acc, List) ->
ok = wxe_util:cast(?BATCH_BEGIN, <<>>),
try lists:foldl(Fun, Acc, List)
catch
- error:W -> erlang:exit({W, erlang:get_stacktrace()});
+ error:W:S -> erlang:exit({W, S});
throw:W -> erlang:throw(W);
exit:W -> erlang:exit(W)
after
@@ -235,7 +235,7 @@ foldr(Fun, Acc, List) ->
ok = wxe_util:cast(?BATCH_BEGIN, <<>>),
try lists:foldr(Fun, Acc, List)
catch
- error:W -> erlang:exit({W, erlang:get_stacktrace()});
+ error:W:S -> erlang:exit({W, S});
throw:W -> erlang:throw(W);
exit:W -> erlang:exit(W)
after
diff --git a/lib/wx/src/wxe_server.erl b/lib/wx/src/wxe_server.erl
index 58fcaf8f23..d6d9dbd629 100644
--- a/lib/wx/src/wxe_server.erl
+++ b/lib/wx/src/wxe_server.erl
@@ -283,10 +283,10 @@ invoke_callback(Pid, Ev, Ref) ->
Return -> exit({bad_return, Return})
end
end
- catch _:Reason ->
+ catch _:Reason:Stacktrace ->
wxEvent:skip(Ref),
?log("Callback fun crashed with {'EXIT, ~p, ~p}~n",
- [Reason, erlang:get_stacktrace()])
+ [Reason, Stacktrace])
end,
wxe_util:cast(?WXE_CB_RETURN, <<>>)
end,
@@ -299,9 +299,9 @@ invoke_callback_fun(Fun) ->
Return = Fun(),
true = is_binary(Return),
Return
- catch _:Reason ->
+ catch _:Reason:Stacktrace ->
?log("Callback fun crashed with {'EXIT, ~p, ~p}~n",
- [Reason, erlang:get_stacktrace()]),
+ [Reason, Stacktrace]),
<<>>
end,
wxe_util:cast(?WXE_CB_RETURN, Res).
diff --git a/lib/xmerl/src/xmerl_xsd.erl b/lib/xmerl/src/xmerl_xsd.erl
index a89b3159ec..d727084175 100644
--- a/lib/xmerl/src/xmerl_xsd.erl
+++ b/lib/xmerl/src/xmerl_xsd.erl
@@ -18,7 +18,7 @@
%% %CopyrightEnd%
%%
-%% @doc Interface module for XML Schema vlidation.
+%% @doc Interface module for XML Schema validation.
%% It handles the W3.org
%% <a href="http://www.w3.org/XML/Schema#dev">specifications</a>
%% of XML Schema second edition 28 october 2004. For an introduction to
diff --git a/lib/xmerl/test/xmerl_SUITE_data/eventp/CMOM.xml b/lib/xmerl/test/xmerl_SUITE_data/eventp/CMOM.xml
index 7c64046897..c2533248d1 100644
--- a/lib/xmerl/test/xmerl_SUITE_data/eventp/CMOM.xml
+++ b/lib/xmerl/test/xmerl_SUITE_data/eventp/CMOM.xml
@@ -10264,7 +10264,7 @@ Note! This attribute cannot have a value larger than for 'egressAtmPcr'.</descri
<attribute name="ingressAtmMcr">
<description>Ingress minimum desired cell rate (cells/s).
-Only positive vaues allowed. This attribute is mandatory only when serviceCategory is UBR+.
+Only positive values allowed. This attribute is mandatory only when serviceCategory is UBR+.
Note! When 'serviceCategory' is set to CBR or UBR this attribute has no relevance and the value submitted is ignored by the system.
diff --git a/lib/xmerl/test/xmerl_SUITE_data/eventp/CelloMOM.xml b/lib/xmerl/test/xmerl_SUITE_data/eventp/CelloMOM.xml
index 8f8cf54505..3b5d8ae2ad 100644
--- a/lib/xmerl/test/xmerl_SUITE_data/eventp/CelloMOM.xml
+++ b/lib/xmerl/test/xmerl_SUITE_data/eventp/CelloMOM.xml
@@ -10264,7 +10264,7 @@ Note! This attribute cannot have a value larger than for 'egressAtmPcr'.</descri
<attribute name="ingressAtmMcr">
<description>Ingress minimum desired cell rate (cells/s).
-Only positive vaues allowed. This attribute is mandatory only when serviceCategory is UBR+.
+Only positive values allowed. This attribute is mandatory only when serviceCategory is UBR+.
Note! When 'serviceCategory' is set to CBR or UBR this attribute has no relevance and the value submitted is ignored by the system.
diff --git a/otp_versions.table b/otp_versions.table
index 2e1052264d..4a8aa2a806 100644
--- a/otp_versions.table
+++ b/otp_versions.table
@@ -1,3 +1,8 @@
+OTP-20.3.2 : ssh-4.6.7 stdlib-3.4.5 # asn1-5.0.5 common_test-1.15.4 compiler-7.1.5 cosEvent-2.2.2 cosEventDomain-1.2.2 cosFileTransfer-1.2.2 cosNotification-1.2.3 cosProperty-1.2.3 cosTime-1.2.3 cosTransactions-1.3.3 crypto-4.2.1 debugger-4.2.4 dialyzer-3.2.4 diameter-2.1.4 edoc-0.9.2 eldap-1.2.3 erl_docgen-0.7.2 erl_interface-3.10.1 erts-9.3 et-1.6.1 eunit-2.3.5 hipe-3.17.1 ic-4.4.3 inets-6.5 jinterface-1.8.1 kernel-5.4.3 megaco-3.18.3 mnesia-4.15.3 observer-2.7 odbc-2.12.1 orber-3.8.4 os_mon-2.4.4 otp_mibs-1.1.2 parsetools-2.1.6 public_key-1.5.2 reltool-0.7.5 runtime_tools-1.12.5 sasl-3.1.1 snmp-5.2.10 ssl-8.2.5 syntax_tools-2.1.4 tools-2.11.2 wx-1.8.3 xmerl-1.3.16 :
+OTP-20.3.1 : ssl-8.2.5 # asn1-5.0.5 common_test-1.15.4 compiler-7.1.5 cosEvent-2.2.2 cosEventDomain-1.2.2 cosFileTransfer-1.2.2 cosNotification-1.2.3 cosProperty-1.2.3 cosTime-1.2.3 cosTransactions-1.3.3 crypto-4.2.1 debugger-4.2.4 dialyzer-3.2.4 diameter-2.1.4 edoc-0.9.2 eldap-1.2.3 erl_docgen-0.7.2 erl_interface-3.10.1 erts-9.3 et-1.6.1 eunit-2.3.5 hipe-3.17.1 ic-4.4.3 inets-6.5 jinterface-1.8.1 kernel-5.4.3 megaco-3.18.3 mnesia-4.15.3 observer-2.7 odbc-2.12.1 orber-3.8.4 os_mon-2.4.4 otp_mibs-1.1.2 parsetools-2.1.6 public_key-1.5.2 reltool-0.7.5 runtime_tools-1.12.5 sasl-3.1.1 snmp-5.2.10 ssh-4.6.6 stdlib-3.4.4 syntax_tools-2.1.4 tools-2.11.2 wx-1.8.3 xmerl-1.3.16 :
+OTP-20.3 : asn1-5.0.5 common_test-1.15.4 compiler-7.1.5 crypto-4.2.1 dialyzer-3.2.4 diameter-2.1.4 erts-9.3 hipe-3.17.1 inets-6.5 kernel-5.4.3 observer-2.7 runtime_tools-1.12.5 snmp-5.2.10 ssh-4.6.6 ssl-8.2.4 stdlib-3.4.4 tools-2.11.2 # cosEvent-2.2.2 cosEventDomain-1.2.2 cosFileTransfer-1.2.2 cosNotification-1.2.3 cosProperty-1.2.3 cosTime-1.2.3 cosTransactions-1.3.3 debugger-4.2.4 edoc-0.9.2 eldap-1.2.3 erl_docgen-0.7.2 erl_interface-3.10.1 et-1.6.1 eunit-2.3.5 ic-4.4.3 jinterface-1.8.1 megaco-3.18.3 mnesia-4.15.3 odbc-2.12.1 orber-3.8.4 os_mon-2.4.4 otp_mibs-1.1.2 parsetools-2.1.6 public_key-1.5.2 reltool-0.7.5 sasl-3.1.1 syntax_tools-2.1.4 wx-1.8.3 xmerl-1.3.16 :
+OTP-20.2.4 : ssh-4.6.5 # asn1-5.0.4 common_test-1.15.3 compiler-7.1.4 cosEvent-2.2.2 cosEventDomain-1.2.2 cosFileTransfer-1.2.2 cosNotification-1.2.3 cosProperty-1.2.3 cosTime-1.2.3 cosTransactions-1.3.3 crypto-4.2 debugger-4.2.4 dialyzer-3.2.3 diameter-2.1.3 edoc-0.9.2 eldap-1.2.3 erl_docgen-0.7.2 erl_interface-3.10.1 erts-9.2.1 et-1.6.1 eunit-2.3.5 hipe-3.17 ic-4.4.3 inets-6.4.5 jinterface-1.8.1 kernel-5.4.2 megaco-3.18.3 mnesia-4.15.3 observer-2.6 odbc-2.12.1 orber-3.8.4 os_mon-2.4.4 otp_mibs-1.1.2 parsetools-2.1.6 public_key-1.5.2 reltool-0.7.5 runtime_tools-1.12.4 sasl-3.1.1 snmp-5.2.9 ssl-8.2.3 stdlib-3.4.3 syntax_tools-2.1.4 tools-2.11.1 wx-1.8.3 xmerl-1.3.16 :
+OTP-20.2.3 : erts-9.2.1 kernel-5.4.2 runtime_tools-1.12.4 # asn1-5.0.4 common_test-1.15.3 compiler-7.1.4 cosEvent-2.2.2 cosEventDomain-1.2.2 cosFileTransfer-1.2.2 cosNotification-1.2.3 cosProperty-1.2.3 cosTime-1.2.3 cosTransactions-1.3.3 crypto-4.2 debugger-4.2.4 dialyzer-3.2.3 diameter-2.1.3 edoc-0.9.2 eldap-1.2.3 erl_docgen-0.7.2 erl_interface-3.10.1 et-1.6.1 eunit-2.3.5 hipe-3.17 ic-4.4.3 inets-6.4.5 jinterface-1.8.1 megaco-3.18.3 mnesia-4.15.3 observer-2.6 odbc-2.12.1 orber-3.8.4 os_mon-2.4.4 otp_mibs-1.1.2 parsetools-2.1.6 public_key-1.5.2 reltool-0.7.5 sasl-3.1.1 snmp-5.2.9 ssh-4.6.4 ssl-8.2.3 stdlib-3.4.3 syntax_tools-2.1.4 tools-2.11.1 wx-1.8.3 xmerl-1.3.16 :
OTP-20.2.2 : mnesia-4.15.3 # asn1-5.0.4 common_test-1.15.3 compiler-7.1.4 cosEvent-2.2.2 cosEventDomain-1.2.2 cosFileTransfer-1.2.2 cosNotification-1.2.3 cosProperty-1.2.3 cosTime-1.2.3 cosTransactions-1.3.3 crypto-4.2 debugger-4.2.4 dialyzer-3.2.3 diameter-2.1.3 edoc-0.9.2 eldap-1.2.3 erl_docgen-0.7.2 erl_interface-3.10.1 erts-9.2 et-1.6.1 eunit-2.3.5 hipe-3.17 ic-4.4.3 inets-6.4.5 jinterface-1.8.1 kernel-5.4.1 megaco-3.18.3 observer-2.6 odbc-2.12.1 orber-3.8.4 os_mon-2.4.4 otp_mibs-1.1.2 parsetools-2.1.6 public_key-1.5.2 reltool-0.7.5 runtime_tools-1.12.3 sasl-3.1.1 snmp-5.2.9 ssh-4.6.4 ssl-8.2.3 stdlib-3.4.3 syntax_tools-2.1.4 tools-2.11.1 wx-1.8.3 xmerl-1.3.16 :
OTP-20.2.1 : ssh-4.6.4 # asn1-5.0.4 common_test-1.15.3 compiler-7.1.4 cosEvent-2.2.2 cosEventDomain-1.2.2 cosFileTransfer-1.2.2 cosNotification-1.2.3 cosProperty-1.2.3 cosTime-1.2.3 cosTransactions-1.3.3 crypto-4.2 debugger-4.2.4 dialyzer-3.2.3 diameter-2.1.3 edoc-0.9.2 eldap-1.2.3 erl_docgen-0.7.2 erl_interface-3.10.1 erts-9.2 et-1.6.1 eunit-2.3.5 hipe-3.17 ic-4.4.3 inets-6.4.5 jinterface-1.8.1 kernel-5.4.1 megaco-3.18.3 mnesia-4.15.2 observer-2.6 odbc-2.12.1 orber-3.8.4 os_mon-2.4.4 otp_mibs-1.1.2 parsetools-2.1.6 public_key-1.5.2 reltool-0.7.5 runtime_tools-1.12.3 sasl-3.1.1 snmp-5.2.9 ssl-8.2.3 stdlib-3.4.3 syntax_tools-2.1.4 tools-2.11.1 wx-1.8.3 xmerl-1.3.16 :
OTP-20.2 : asn1-5.0.4 common_test-1.15.3 compiler-7.1.4 cosEvent-2.2.2 cosEventDomain-1.2.2 cosFileTransfer-1.2.2 cosNotification-1.2.3 cosProperty-1.2.3 cosTime-1.2.3 cosTransactions-1.3.3 crypto-4.2 debugger-4.2.4 dialyzer-3.2.3 diameter-2.1.3 edoc-0.9.2 eldap-1.2.3 erl_docgen-0.7.2 erl_interface-3.10.1 erts-9.2 eunit-2.3.5 hipe-3.17 ic-4.4.3 inets-6.4.5 jinterface-1.8.1 kernel-5.4.1 megaco-3.18.3 mnesia-4.15.2 observer-2.6 odbc-2.12.1 orber-3.8.4 os_mon-2.4.4 otp_mibs-1.1.2 parsetools-2.1.6 public_key-1.5.2 runtime_tools-1.12.3 sasl-3.1.1 snmp-5.2.9 ssh-4.6.3 ssl-8.2.3 stdlib-3.4.3 syntax_tools-2.1.4 tools-2.11.1 wx-1.8.3 xmerl-1.3.16 # et-1.6.1 reltool-0.7.5 :
@@ -16,6 +21,8 @@ OTP-20.0.3 : asn1-5.0.2 compiler-7.1.1 erts-9.0.3 ssh-4.5.1 # common_test-1.15.1
OTP-20.0.2 : asn1-5.0.1 erts-9.0.2 kernel-5.3.1 # common_test-1.15.1 compiler-7.1 cosEvent-2.2.1 cosEventDomain-1.2.1 cosFileTransfer-1.2.1 cosNotification-1.2.2 cosProperty-1.2.2 cosTime-1.2.2 cosTransactions-1.3.2 crypto-4.0 debugger-4.2.2 dialyzer-3.2 diameter-2.0 edoc-0.9 eldap-1.2.2 erl_docgen-0.7 erl_interface-3.10 et-1.6 eunit-2.3.3 hipe-3.16 ic-4.4.2 inets-6.4 jinterface-1.8 megaco-3.18.2 mnesia-4.15 observer-2.4 odbc-2.12 orber-3.8.3 os_mon-2.4.2 otp_mibs-1.1.1 parsetools-2.1.5 public_key-1.4.1 reltool-0.7.4 runtime_tools-1.12.1 sasl-3.0.4 snmp-5.2.6 ssh-4.5 ssl-8.2 stdlib-3.4.1 syntax_tools-2.1.2 tools-2.10.1 wx-1.8.1 xmerl-1.3.15 :
OTP-20.0.1 : common_test-1.15.1 erts-9.0.1 runtime_tools-1.12.1 stdlib-3.4.1 tools-2.10.1 # asn1-5.0 compiler-7.1 cosEvent-2.2.1 cosEventDomain-1.2.1 cosFileTransfer-1.2.1 cosNotification-1.2.2 cosProperty-1.2.2 cosTime-1.2.2 cosTransactions-1.3.2 crypto-4.0 debugger-4.2.2 dialyzer-3.2 diameter-2.0 edoc-0.9 eldap-1.2.2 erl_docgen-0.7 erl_interface-3.10 et-1.6 eunit-2.3.3 hipe-3.16 ic-4.4.2 inets-6.4 jinterface-1.8 kernel-5.3 megaco-3.18.2 mnesia-4.15 observer-2.4 odbc-2.12 orber-3.8.3 os_mon-2.4.2 otp_mibs-1.1.1 parsetools-2.1.5 public_key-1.4.1 reltool-0.7.4 sasl-3.0.4 snmp-5.2.6 ssh-4.5 ssl-8.2 syntax_tools-2.1.2 wx-1.8.1 xmerl-1.3.15 :
OTP-20.0 : asn1-5.0 common_test-1.15 compiler-7.1 cosProperty-1.2.2 crypto-4.0 debugger-4.2.2 dialyzer-3.2 diameter-2.0 edoc-0.9 erl_docgen-0.7 erl_interface-3.10 erts-9.0 eunit-2.3.3 hipe-3.16 inets-6.4 jinterface-1.8 kernel-5.3 megaco-3.18.2 mnesia-4.15 observer-2.4 orber-3.8.3 parsetools-2.1.5 public_key-1.4.1 reltool-0.7.4 runtime_tools-1.12 sasl-3.0.4 snmp-5.2.6 ssh-4.5 ssl-8.2 stdlib-3.4 syntax_tools-2.1.2 tools-2.10 wx-1.8.1 xmerl-1.3.15 # cosEvent-2.2.1 cosEventDomain-1.2.1 cosFileTransfer-1.2.1 cosNotification-1.2.2 cosTime-1.2.2 cosTransactions-1.3.2 eldap-1.2.2 et-1.6 ic-4.4.2 odbc-2.12 os_mon-2.4.2 otp_mibs-1.1.1 :
+OTP-19.3.6.7 : kernel-5.2.0.1 # asn1-4.0.4 common_test-1.14 compiler-7.0.4.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.4 debugger-4.2.1 dialyzer-3.1.1 diameter-1.12.2 edoc-0.8.1 eldap-1.2.2 erl_docgen-0.6.1 erl_interface-3.9.3 erts-8.3.5.4 et-1.6 eunit-2.3.2 gs-1.6.2 hipe-3.15.4 ic-4.4.2 inets-6.3.9 jinterface-1.7.1 megaco-3.18.1 mnesia-4.14.3.1 observer-2.3.1 odbc-2.12 orber-3.8.2 os_mon-2.4.2 otp_mibs-1.1.1 parsetools-2.1.4 percept-0.9 public_key-1.4 reltool-0.7.3 runtime_tools-1.11.1 sasl-3.0.3 snmp-5.2.5 ssh-4.4.2.2 ssl-8.1.3.1.1 stdlib-3.3 syntax_tools-2.1.1 tools-2.9.1 typer-0.9.12 wx-1.8 xmerl-1.3.14 :
+OTP-19.3.6.6 : ssh-4.4.2.2 ssl-8.1.3.1.1 # asn1-4.0.4 common_test-1.14 compiler-7.0.4.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.4 debugger-4.2.1 dialyzer-3.1.1 diameter-1.12.2 edoc-0.8.1 eldap-1.2.2 erl_docgen-0.6.1 erl_interface-3.9.3 erts-8.3.5.4 et-1.6 eunit-2.3.2 gs-1.6.2 hipe-3.15.4 ic-4.4.2 inets-6.3.9 jinterface-1.7.1 kernel-5.2 megaco-3.18.1 mnesia-4.14.3.1 observer-2.3.1 odbc-2.12 orber-3.8.2 os_mon-2.4.2 otp_mibs-1.1.1 parsetools-2.1.4 percept-0.9 public_key-1.4 reltool-0.7.3 runtime_tools-1.11.1 sasl-3.0.3 snmp-5.2.5 stdlib-3.3 syntax_tools-2.1.1 tools-2.9.1 typer-0.9.12 wx-1.8 xmerl-1.3.14 :
OTP-19.3.6.5 : erts-8.3.5.4 mnesia-4.14.3.1 ssh-4.4.2.1 # asn1-4.0.4 common_test-1.14 compiler-7.0.4.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.4 debugger-4.2.1 dialyzer-3.1.1 diameter-1.12.2 edoc-0.8.1 eldap-1.2.2 erl_docgen-0.6.1 erl_interface-3.9.3 et-1.6 eunit-2.3.2 gs-1.6.2 hipe-3.15.4 ic-4.4.2 inets-6.3.9 jinterface-1.7.1 kernel-5.2 megaco-3.18.1 observer-2.3.1 odbc-2.12 orber-3.8.2 os_mon-2.4.2 otp_mibs-1.1.1 parsetools-2.1.4 percept-0.9 public_key-1.4 reltool-0.7.3 runtime_tools-1.11.1 sasl-3.0.3 snmp-5.2.5 ssl-8.1.3.1 stdlib-3.3 syntax_tools-2.1.1 tools-2.9.1 typer-0.9.12 wx-1.8 xmerl-1.3.14 :
OTP-19.3.6.4 : ssl-8.1.3.1 # asn1-4.0.4 common_test-1.14 compiler-7.0.4.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.4 debugger-4.2.1 dialyzer-3.1.1 diameter-1.12.2 edoc-0.8.1 eldap-1.2.2 erl_docgen-0.6.1 erl_interface-3.9.3 erts-8.3.5.3 et-1.6 eunit-2.3.2 gs-1.6.2 hipe-3.15.4 ic-4.4.2 inets-6.3.9 jinterface-1.7.1 kernel-5.2 megaco-3.18.1 mnesia-4.14.3 observer-2.3.1 odbc-2.12 orber-3.8.2 os_mon-2.4.2 otp_mibs-1.1.1 parsetools-2.1.4 percept-0.9 public_key-1.4 reltool-0.7.3 runtime_tools-1.11.1 sasl-3.0.3 snmp-5.2.5 ssh-4.4.2 stdlib-3.3 syntax_tools-2.1.1 tools-2.9.1 typer-0.9.12 wx-1.8 xmerl-1.3.14 :
OTP-19.3.6.3 : compiler-7.0.4.1 erts-8.3.5.3 # asn1-4.0.4 common_test-1.14 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.4 debugger-4.2.1 dialyzer-3.1.1 diameter-1.12.2 edoc-0.8.1 eldap-1.2.2 erl_docgen-0.6.1 erl_interface-3.9.3 et-1.6 eunit-2.3.2 gs-1.6.2 hipe-3.15.4 ic-4.4.2 inets-6.3.9 jinterface-1.7.1 kernel-5.2 megaco-3.18.1 mnesia-4.14.3 observer-2.3.1 odbc-2.12 orber-3.8.2 os_mon-2.4.2 otp_mibs-1.1.1 parsetools-2.1.4 percept-0.9 public_key-1.4 reltool-0.7.3 runtime_tools-1.11.1 sasl-3.0.3 snmp-5.2.5 ssh-4.4.2 ssl-8.1.3 stdlib-3.3 syntax_tools-2.1.1 tools-2.9.1 typer-0.9.12 wx-1.8 xmerl-1.3.14 :
@@ -49,6 +56,7 @@ 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
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.8 : ssh-4.2.2.5 # asn1-4.0.2 common_test-1.12.1.1 compiler-6.0.3.1 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.1 debugger-4.1.2 dialyzer-2.9 diameter-1.11.2 edoc-0.7.18 eldap-1.2.1.1 erl_docgen-0.4.2 erl_interface-3.8.2 erts-7.3.1.4 et-1.5.1 eunit-2.2.13 gs-1.6 hipe-3.15 ic-4.4 inets-6.2.4.1 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.2 stdlib-2.8 syntax_tools-1.7 test_server-3.10 tools-2.8.3 typer-0.9.10 webtool-0.9.1 wx-1.6.1 xmerl-1.3.10 :
OTP-18.3.4.7 : ssl-7.3.3.2 # asn1-4.0.2 common_test-1.12.1.1 compiler-6.0.3.1 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.1 debugger-4.1.2 dialyzer-2.9 diameter-1.11.2 edoc-0.7.18 eldap-1.2.1.1 erl_docgen-0.4.2 erl_interface-3.8.2 erts-7.3.1.4 et-1.5.1 eunit-2.2.13 gs-1.6 hipe-3.15 ic-4.4 inets-6.2.4.1 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.4 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.6 : compiler-6.0.3.1 eldap-1.2.1.1 erts-7.3.1.4 ssh-4.2.2.4 # asn1-4.0.2 common_test-1.12.1.1 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.1 debugger-4.1.2 dialyzer-2.9 diameter-1.11.2 edoc-0.7.18 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.1 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.5 : crypto-3.6.3.1 erts-7.3.1.3 inets-6.2.4.1 ssh-4.2.2.3 # 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 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 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 :
diff --git a/system/doc/design_principles/release_structure.xml b/system/doc/design_principles/release_structure.xml
index a0ab21c43f..8e62ba845f 100644
--- a/system/doc/design_principles/release_structure.xml
+++ b/system/doc/design_principles/release_structure.xml
@@ -208,8 +208,8 @@ releases/ch_rel-1.rel</pre>
is therefore now instead duplicated in the tar file so no manual
copying is necessary.</p>
<p>If a <c>relup</c> file and/or a system configuration file called
- <c>sys.config</c> is found, these files are also included in
- the release package. See
+ <c>sys.config</c>, or a <c>sys.config.src</c>, is found, these files
+ are also included in the release package. See
<seealso marker="release_handling#req">Release Handling</seealso>.</p>
<p>Options can be set to make the release package include source
code and the ERTS binary as well.</p>
@@ -240,7 +240,7 @@ $ROOT/lib/App1-AVsn1/ebin
<item><c>erts-EVsn/bin</c> - Erlang runtime system executables</item>
<item><c>releases/Vsn</c> - <c>.rel</c> file and boot script
<c>start.boot</c>; if present in the release package, <c>relup</c>
- and/or <c>sys.config</c></item>
+ and/or <c>sys.config</c> or <c>sys.config.src</c></item>
<item><c>bin</c> - Top-level Erlang runtime system executables</item>
</list>
<p>Applications are not required to be located under directory
diff --git a/system/doc/efficiency_guide/advanced.xml b/system/doc/efficiency_guide/advanced.xml
index 896eda5f1c..21d4a66d77 100644
--- a/system/doc/efficiency_guide/advanced.xml
+++ b/system/doc/efficiency_guide/advanced.xml
@@ -151,7 +151,7 @@
<row>
<cell>Processes</cell>
<cell>The maximum number of simultaneously alive Erlang processes
- is by default 32,768. This limit can be configured at startup.
+ is by default 262,144. This limit can be configured at startup.
For more information, see the
<seealso marker="erts:erl#max_processes"><c>+P</c></seealso>
command-line flag in the
@@ -194,10 +194,8 @@
</row>
<row>
<cell>Elements in a tuple</cell>
- <cell>The maximum number of elements in a tuple is 67,108,863
- (26-bit unsigned integer). Clearly, other factors such as the
- available memory can make it difficult to create a tuple of
- that size.</cell>
+ <cell>The maximum number of elements in a tuple is 16,777,215
+ (24-bit unsigned integer).</cell>
</row>
<row>
<cell>Size of binary</cell>
diff --git a/system/doc/reference_manual/modules.xml b/system/doc/reference_manual/modules.xml
index 4a97bfeb7b..7dc71eb307 100644
--- a/system/doc/reference_manual/modules.xml
+++ b/system/doc/reference_manual/modules.xml
@@ -307,6 +307,12 @@ behaviour_info(callbacks) -> Callbacks.</pre>
all functions in the module.</p>
</item>
+ <tag><c>nifs</c></tag>
+ <item>
+ <p>Returns a list of <c>{Name,Arity}</c> tuples with
+ all NIF functions in the module.</p>
+ </item>
+
<tag><c>native</c></tag>
<item>
<p>Return <c>true</c> if the module has native compiled code.
diff --git a/system/doc/system_principles/create_target.xmlsrc b/system/doc/system_principles/create_target.xmlsrc
index f9b27ffc35..dc6cbbe980 100644
--- a/system/doc/system_principles/create_target.xmlsrc
+++ b/system/doc/system_principles/create_target.xmlsrc
@@ -263,6 +263,14 @@ os> <input>/usr/local/erl-target/bin/erl -boot /usr/local/erl-target/releases/FI
current directory create not only the file <c>mysystem.rel</c>,
but also file <c>sys.config</c>, the latter file is tacitly
put in the appropriate directory.</p>
+ <p>However, it can also be convenient to replace variables in
+ within a <c>sys.config</c> on the target after unpacking but
+ before running the release. If you have a <c>sys.config.src</c>
+ it will be included and is not required to be a valid Erlang term
+ file like <c>sys.config</c>. Before running the release you must
+ have a valid <c>sys.config</c> in the same directory, so using
+ <c>sys.config.src</c> requires having some tool to populate what is
+ needed and write <c>sys.config</c> to disk before booting the release.</p>
</section>
<section>