aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.gitignore1
-rw-r--r--bootstrap/bin/no_dot_erlang.bootbin6483 -> 6541 bytes
-rw-r--r--bootstrap/bin/start.bootbin6483 -> 6541 bytes
-rw-r--r--bootstrap/bin/start_clean.bootbin6483 -> 6541 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/beam_a.beambin3192 -> 3200 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/beam_asm.beambin11208 -> 10996 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/beam_block.beambin3508 -> 3444 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/beam_bs.beambin3400 -> 0 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/beam_clean.beambin3568 -> 3516 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/beam_dict.beambin4900 -> 4660 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/beam_disasm.beambin21224 -> 20860 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/beam_except.beambin3780 -> 4228 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/beam_jump.beambin10048 -> 9988 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/beam_kernel_to_ssa.beambin32408 -> 28880 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/beam_peep.beambin3752 -> 3604 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/beam_ssa.beambin12300 -> 12176 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/beam_ssa_bsm.beambin18708 -> 17952 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/beam_ssa_codegen.beambin39156 -> 37708 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/beam_ssa_dead.beambin12444 -> 11876 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/beam_ssa_funs.beambin2556 -> 2572 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/beam_ssa_lint.beambin7672 -> 7536 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/beam_ssa_opt.beambin32308 -> 38664 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/beam_ssa_pp.beambin5500 -> 5500 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/beam_ssa_pre_codegen.beambin43612 -> 45400 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/beam_ssa_recv.beambin4040 -> 3912 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/beam_ssa_share.beambin5608 -> 5348 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/beam_ssa_type.beambin18660 -> 28456 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/beam_trim.beambin9008 -> 8676 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/beam_utils.beambin3548 -> 3548 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/beam_validator.beambin35476 -> 44140 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/beam_z.beambin3240 -> 3620 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/cerl.beambin28604 -> 28252 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/cerl_clauses.beambin2852 -> 2808 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/cerl_inline.beambin36696 -> 34840 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/cerl_trees.beambin21740 -> 20416 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/compile.beambin42628 -> 41276 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/compiler.app4
-rw-r--r--bootstrap/lib/compiler/ebin/compiler.appup2
-rw-r--r--bootstrap/lib/compiler/ebin/core_lib.beambin4088 -> 3720 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/core_lint.beambin12548 -> 12472 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/core_parse.beambin63288 -> 63064 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/core_pp.beambin11832 -> 11472 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/core_scan.beambin6452 -> 6248 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/erl_bifs.beambin2076 -> 2080 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/rec_env.beambin4536 -> 4492 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/sys_core_alias.beambin5800 -> 5588 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/sys_core_bsm.beambin1676 -> 1672 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/sys_core_dsetel.beambin6376 -> 0 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/sys_core_fold.beambin48364 -> 45584 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/sys_core_fold_lists.beambin3900 -> 4020 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/sys_core_inline.beambin3872 -> 3912 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/sys_pre_attributes.beambin2600 -> 2468 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/v3_core.beambin55284 -> 50704 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/v3_kernel.beambin53044 -> 50348 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/v3_kernel_pp.beambin12472 -> 12060 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/application.beambin3736 -> 3732 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/application_controller.beambin30324 -> 29864 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/application_master.beambin6172 -> 6108 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/auth.beambin6196 -> 6164 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/code.beambin12872 -> 12740 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/code_server.beambin23508 -> 22624 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/disk_log.beambin30992 -> 29652 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/disk_log_1.beambin23204 -> 22496 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/disk_log_server.beambin6196 -> 6160 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/dist_ac.beambin23964 -> 23492 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/dist_util.beambin12512 -> 12312 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/erl_boot_server.beambin5632 -> 5588 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/erl_ddll.beambin2800 -> 2752 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/erl_epmd.beambin7096 -> 7060 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/error_handler.beambin1572 -> 1576 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/error_logger.beambin6264 -> 6128 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/erts_debug.beambin9332 -> 9184 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/file.beambin13540 -> 13384 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/file_io_server.beambin15768 -> 15324 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/file_server.beambin4948 -> 4920 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/gen_sctp.beambin3212 -> 3212 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/global.beambin30232 -> 29228 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/global_group.beambin16048 -> 15812 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/group.beambin14388 -> 14240 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/group_history.beambin5656 -> 5536 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/heart.beambin5204 -> 5172 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/hipe_unified_loader.beambin12440 -> 12348 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/inet.beambin23584 -> 23256 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/inet6_tcp.beambin3016 -> 3016 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/inet_config.beambin7368 -> 7252 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/inet_db.beambin25552 -> 25288 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/inet_dns.beambin19172 -> 18604 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/inet_gethost_native.beambin9876 -> 9732 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/inet_hosts.beambin1924 -> 1904 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/inet_parse.beambin13544 -> 13404 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/inet_res.beambin13380 -> 13200 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/inet_sctp.beambin2148 -> 2148 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/inet_tcp.beambin2784 -> 2784 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/inet_tcp_dist.beambin7616 -> 7492 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/inet_udp.beambin1912 -> 1908 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/kernel.app4
-rw-r--r--bootstrap/lib/kernel/ebin/kernel.appup49
-rw-r--r--bootstrap/lib/kernel/ebin/kernel.beambin3588 -> 3572 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/kernel_config.beambin2680 -> 2644 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/kernel_refc.beambin2368 -> 2288 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/local_tcp.beambin2192 -> 2180 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/local_udp.beambin1380 -> 1372 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/logger.beambin11796 -> 15100 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/logger_backend.beambin2560 -> 2552 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/logger_config.beambin2988 -> 3176 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/logger_disk_log_h.beambin9492 -> 3356 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/logger_filters.beambin1780 -> 1748 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/logger_formatter.beambin8796 -> 8568 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/logger_h_common.beambin5592 -> 7668 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/logger_olp.beambin0 -> 8316 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/logger_proxy.beambin0 -> 2884 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/logger_server.beambin11248 -> 11376 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/logger_simple_h.beambin4256 -> 4244 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/logger_std_h.beambin10860 -> 5184 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/logger_sup.beambin576 -> 636 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/net_adm.beambin2900 -> 2824 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/net_kernel.beambin24536 -> 24244 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/os.beambin5172 -> 5128 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/pg2.beambin7612 -> 7568 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/ram_file.beambin6072 -> 6008 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/raw_file_io.beambin1712 -> 1668 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/raw_file_io_compressed.beambin2336 -> 2308 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/raw_file_io_deflate.beambin2608 -> 2600 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/raw_file_io_delayed.beambin5276 -> 5208 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/raw_file_io_inflate.beambin4200 -> 4188 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/raw_file_io_list.beambin2480 -> 2460 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/rpc.beambin7700 -> 7704 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/seq_trace.beambin1584 -> 1600 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/standard_error.beambin3744 -> 3724 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/user.beambin11112 -> 11020 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/user_drv.beambin10940 -> 10908 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/wrap_log_reader.beambin3028 -> 2936 bytes
-rw-r--r--bootstrap/lib/kernel/include/dist.hrl3
-rw-r--r--bootstrap/lib/stdlib/ebin/array.beambin11708 -> 11640 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/beam_lib.beambin19108 -> 18708 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/binary.beambin2900 -> 2844 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/c.beambin17192 -> 17000 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/calendar.beambin7604 -> 7632 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/dets.beambin46376 -> 45236 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/dets_server.beambin6556 -> 6468 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/dets_utils.beambin26280 -> 25788 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/dets_v9.beambin46536 -> 45256 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/dict.beambin9292 -> 8836 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/digraph.beambin7684 -> 7508 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/digraph_utils.beambin6772 -> 6732 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/edlin.beambin10720 -> 10504 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/edlin_expand.beambin3868 -> 3756 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/epp.beambin28976 -> 28276 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/erl_abstract_code.beambin1012 -> 968 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/erl_anno.beambin3568 -> 3484 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/erl_bits.beambin2396 -> 2356 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/erl_compile.beambin7032 -> 6684 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/erl_error.beambin8388 -> 8280 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/erl_eval.beambin35668 -> 35192 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/erl_expand_records.beambin20864 -> 19396 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/erl_internal.beambin6812 -> 6732 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/erl_lint.beambin88436 -> 85148 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/erl_parse.beambin97240 -> 96136 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/erl_pp.beambin25780 -> 25568 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/erl_scan.beambin26012 -> 25720 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/erl_tar.beambin32248 -> 31016 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/error_logger_tty_h.beambin4800 -> 4788 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/escript.beambin16344 -> 15676 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/ets.beambin21976 -> 21536 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/eval_bits.beambin7760 -> 7684 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/file_sorter.beambin28412 -> 27324 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/filelib.beambin10400 -> 10216 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/filename.beambin14920 -> 14796 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/gb_sets.beambin8160 -> 7712 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/gb_trees.beambin5368 -> 5084 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/gen.beambin4916 -> 4912 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/gen_event.beambin13560 -> 13020 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/gen_fsm.beambin11304 -> 12432 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/gen_server.beambin14812 -> 15344 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/gen_statem.beambin20016 -> 20448 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/io.beambin5868 -> 5796 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/io_lib.beambin13612 -> 13532 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/io_lib_format.beambin14360 -> 13892 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/io_lib_fread.beambin7052 -> 6520 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/io_lib_pretty.beambin21520 -> 21068 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/lists.beambin29436 -> 29280 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/log_mf_h.beambin2380 -> 2360 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/maps.beambin3200 -> 3188 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/ms_transform.beambin18912 -> 18460 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/otp_internal.beambin10112 -> 10208 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/pool.beambin3688 -> 3664 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/proc_lib.beambin12852 -> 12628 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/proplists.beambin4612 -> 4596 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/qlc.beambin66332 -> 65192 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/qlc_pt.beambin72744 -> 70568 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/queue.beambin5976 -> 5904 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/rand.beambin29488 -> 29052 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/random.beambin1736 -> 1768 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/re.beambin12648 -> 12344 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/sets.beambin6392 -> 6152 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/shell.beambin28860 -> 28484 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/sofs.beambin35860 -> 35276 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/stdlib.app2
-rw-r--r--bootstrap/lib/stdlib/ebin/stdlib.appup47
-rw-r--r--bootstrap/lib/stdlib/ebin/string.beambin36300 -> 35032 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/supervisor.beambin22372 -> 21700 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/supervisor_bridge.beambin2348 -> 2340 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/sys.beambin8436 -> 9100 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/unicode.beambin14192 -> 14140 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/unicode_util.beambin198916 -> 198716 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/uri_string.beambin26600 -> 24988 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/win32reg.beambin5180 -> 5120 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/zip.beambin25376 -> 24236 bytes
-rw-r--r--erts/doc/src/erl_dist_protocol.xml116
-rw-r--r--erts/doc/src/erl_ext_dist.xml9
-rw-r--r--erts/doc/src/erl_nif.xml93
-rw-r--r--erts/doc/src/notes.xml38
-rw-r--r--erts/doc/src/persistent_term.xml16
-rw-r--r--erts/emulator/beam/beam_emu.c5
-rw-r--r--erts/emulator/beam/beam_load.c3
-rw-r--r--erts/emulator/beam/bif.c205
-rw-r--r--erts/emulator/beam/bif.tab1
-rw-r--r--erts/emulator/beam/big.h6
-rw-r--r--erts/emulator/beam/break.c6
-rw-r--r--erts/emulator/beam/copy.c6
-rw-r--r--erts/emulator/beam/dist.c1582
-rw-r--r--erts/emulator/beam/dist.h270
-rw-r--r--erts/emulator/beam/erl_alloc.types2
-rw-r--r--erts/emulator/beam/erl_alloc_util.c16
-rw-r--r--erts/emulator/beam/erl_bif_binary.c18
-rw-r--r--erts/emulator/beam/erl_bif_info.c41
-rw-r--r--erts/emulator/beam/erl_bif_persistent.c17
-rw-r--r--erts/emulator/beam/erl_db.c22
-rw-r--r--erts/emulator/beam/erl_db.h2
-rw-r--r--erts/emulator/beam/erl_db_util.c2
-rw-r--r--erts/emulator/beam/erl_gc.c24
-rw-r--r--erts/emulator/beam/erl_hl_timer.c39
-rw-r--r--erts/emulator/beam/erl_hl_timer.h2
-rw-r--r--erts/emulator/beam/erl_init.c5
-rw-r--r--erts/emulator/beam/erl_lock_check.c3
-rw-r--r--erts/emulator/beam/erl_map.c2
-rw-r--r--erts/emulator/beam/erl_message.c197
-rw-r--r--erts/emulator/beam/erl_message.h29
-rw-r--r--erts/emulator/beam/erl_monitor_link.c131
-rw-r--r--erts/emulator/beam/erl_monitor_link.h230
-rw-r--r--erts/emulator/beam/erl_nif.c36
-rw-r--r--erts/emulator/beam/erl_nif_api_funcs.h8
-rw-r--r--erts/emulator/beam/erl_node_tables.c240
-rw-r--r--erts/emulator/beam/erl_node_tables.h86
-rw-r--r--erts/emulator/beam/erl_port_task.c2
-rw-r--r--erts/emulator/beam/erl_printf_term.c4
-rw-r--r--erts/emulator/beam/erl_proc_sig_queue.c373
-rw-r--r--erts/emulator/beam/erl_proc_sig_queue.h88
-rw-r--r--erts/emulator/beam/erl_process.c1018
-rw-r--r--erts/emulator/beam/erl_process.h14
-rw-r--r--erts/emulator/beam/erl_process_dump.c33
-rw-r--r--erts/emulator/beam/erl_ptab.h5
-rw-r--r--erts/emulator/beam/erl_rbtree.h280
-rw-r--r--erts/emulator/beam/erl_time_sup.c6
-rw-r--r--erts/emulator/beam/external.c361
-rw-r--r--erts/emulator/beam/external.h76
-rw-r--r--erts/emulator/beam/global.h5
-rw-r--r--erts/emulator/beam/io.c17
-rw-r--r--erts/emulator/beam/msg_instrs.tab4
-rw-r--r--erts/emulator/beam/utils.c5
-rw-r--r--erts/emulator/hipe/hipe_native_bif.c2
-rw-r--r--erts/emulator/sys/common/erl_check_io.c5
-rw-r--r--erts/emulator/sys/common/erl_osenv.c12
-rw-r--r--erts/emulator/sys/common/erl_poll.c4
-rw-r--r--erts/emulator/test/distribution_SUITE.erl336
-rw-r--r--erts/emulator/test/erts_test_utils.erl31
-rw-r--r--erts/emulator/test/nif_SUITE.erl60
-rw-r--r--erts/emulator/test/nif_SUITE_data/nif_SUITE.c90
-rw-r--r--erts/emulator/test/node_container_SUITE.erl23
-rw-r--r--erts/emulator/test/persistent_term_SUITE.erl5
-rw-r--r--erts/etc/common/heart.c38
-rw-r--r--erts/etc/unix/Makefile3
-rw-r--r--erts/etc/unix/cerl.src29
-rw-r--r--erts/etc/unix/etp-commands.in122
-rw-r--r--erts/etc/unix/etp-rr-run-until-beam.py45
-rw-r--r--erts/etc/win32/msys_tools/vc/ld.sh5
-rw-r--r--erts/preloaded/ebin/persistent_term.beambin1692 -> 1836 bytes
-rw-r--r--erts/preloaded/ebin/prim_inet.beambin81176 -> 81012 bytes
-rw-r--r--erts/preloaded/src/persistent_term.erl9
-rw-r--r--erts/preloaded/src/prim_inet.erl16
-rw-r--r--erts/vsn.mk2
-rw-r--r--lib/common_test/doc/src/ct_telnet.xml29
-rw-r--r--lib/common_test/doc/src/notes.xml43
-rw-r--r--lib/common_test/src/ct_cover.erl5
-rw-r--r--lib/common_test/src/ct_netconfc.erl10
-rw-r--r--lib/common_test/src/ct_run.erl8
-rw-r--r--lib/common_test/src/ct_telnet.erl144
-rw-r--r--lib/common_test/src/ct_telnet_client.erl6
-rw-r--r--lib/common_test/src/ct_util.hrl1
-rw-r--r--lib/common_test/src/test_server.erl47
-rw-r--r--lib/common_test/test/ct_hooks_SUITE.erl106
-rw-r--r--lib/common_test/test/ct_hooks_SUITE_data/cth/tests/ct_cth_update_result_post_end_tc_SUITE.erl101
-rw-r--r--lib/common_test/test/ct_hooks_SUITE_data/cth/tests/update_result_post_end_tc_cth.erl98
-rw-r--r--lib/common_test/test/ct_netconfc_SUITE_data/netconfc1_SUITE.erl6
-rw-r--r--lib/common_test/test/ct_telnet_SUITE.erl39
-rw-r--r--lib/common_test/test/ct_telnet_SUITE_data/ct_telnet_faulty_regexp_SUITE.erl79
-rw-r--r--lib/common_test/test/ct_telnet_SUITE_data/ct_telnet_own_server_SUITE.erl11
-rw-r--r--lib/common_test/test_server/configure.in6
-rw-r--r--lib/compiler/src/Makefile2
-rw-r--r--lib/compiler/src/beam_a.erl4
-rw-r--r--lib/compiler/src/beam_except.erl6
-rw-r--r--lib/compiler/src/beam_jump.erl43
-rw-r--r--lib/compiler/src/beam_kernel_to_ssa.erl5
-rw-r--r--lib/compiler/src/beam_ssa.erl78
-rw-r--r--lib/compiler/src/beam_ssa_bsm.erl10
-rw-r--r--lib/compiler/src/beam_ssa_opt.erl222
-rw-r--r--lib/compiler/src/beam_ssa_pre_codegen.erl212
-rw-r--r--lib/compiler/src/beam_ssa_type.erl694
-rw-r--r--lib/compiler/src/beam_validator.erl1546
-rw-r--r--lib/compiler/src/compile.erl32
-rw-r--r--lib/compiler/src/compiler.app.src1
-rw-r--r--lib/compiler/src/erl_bifs.erl16
-rw-r--r--lib/compiler/src/sys_core_dsetel.erl360
-rw-r--r--lib/compiler/src/sys_core_fold.erl153
-rw-r--r--lib/compiler/src/v3_core.erl24
-rw-r--r--lib/compiler/src/v3_kernel.erl1
-rw-r--r--lib/compiler/test/Makefile15
-rw-r--r--lib/compiler/test/beam_validator_SUITE.erl34
-rw-r--r--lib/compiler/test/bif_SUITE.erl7
-rw-r--r--lib/compiler/test/compile_SUITE.erl123
-rw-r--r--lib/compiler/test/compiler.cover5
-rw-r--r--lib/compiler/test/inline_SUITE.erl4
-rw-r--r--lib/compiler/test/match_SUITE.erl15
-rw-r--r--lib/compiler/test/misc_SUITE.erl12
-rw-r--r--lib/compiler/test/test_lib.erl56
-rw-r--r--lib/compiler/test/warnings_SUITE.erl14
-rw-r--r--lib/crypto/c_src/algorithms.c9
-rw-r--r--lib/crypto/c_src/atoms.c9
-rw-r--r--lib/crypto/c_src/atoms.h4
-rw-r--r--lib/crypto/c_src/digest.c14
-rw-r--r--lib/crypto/c_src/openssl_config.h7
-rw-r--r--lib/crypto/doc/src/crypto.xml5
-rw-r--r--lib/crypto/doc/src/engine_keys.xml2
-rw-r--r--lib/crypto/src/crypto.erl7
-rw-r--r--lib/crypto/test/crypto_SUITE.erl77
-rw-r--r--lib/crypto/test/engine_SUITE.erl2
-rw-r--r--lib/debugger/test/int_eval_SUITE.erl5
-rw-r--r--lib/dialyzer/src/dialyzer_codeserver.erl8
-rw-r--r--lib/dialyzer/src/dialyzer_contracts.erl130
-rw-r--r--lib/dialyzer/src/dialyzer_utils.erl37
-rw-r--r--lib/dialyzer/test/small_SUITE_data/results/spec_other_module2
-rw-r--r--lib/dialyzer/test/small_SUITE_data/src/lists_key_bug.erl19
-rw-r--r--lib/dialyzer/test/small_SUITE_data/src/spec_other_module.erl7
-rw-r--r--lib/diameter/doc/src/notes.xml18
-rw-r--r--lib/diameter/src/base/diameter_gen.erl2
-rw-r--r--lib/diameter/src/base/diameter_traffic.erl2
-rw-r--r--lib/diameter/src/diameter.appup.src4
-rw-r--r--lib/diameter/src/transport/diameter_tcp.erl8
-rw-r--r--lib/erl_interface/doc/src/ei.xml15
-rw-r--r--lib/erl_interface/doc/src/ei_users_guide.xml18
-rw-r--r--lib/erl_interface/include/ei.h4
-rw-r--r--lib/erl_interface/src/Makefile.in3
-rw-r--r--lib/erl_interface/src/connect/ei_connect.c90
-rw-r--r--lib/erl_interface/src/connect/ei_resolve.c22
-rw-r--r--lib/erl_interface/src/connect/ei_resolve.h2
-rw-r--r--lib/erl_interface/src/legacy/erl_eterm.c2
-rw-r--r--lib/erl_interface/src/misc/ei_init.c32
-rw-r--r--lib/erl_interface/src/misc/ei_internal.h2
-rw-r--r--lib/erl_interface/src/misc/ei_portio.c13
-rw-r--r--lib/erl_interface/test/ei_accept_SUITE_data/ei_accept_test.c2
-rw-r--r--lib/erl_interface/test/ei_accept_SUITE_data/eiaccnode.c2
-rw-r--r--lib/erl_interface/test/ei_connect_SUITE_data/ei_connect_test.c2
-rw-r--r--lib/erl_interface/test/ei_decode_SUITE_data/ei_decode_test.c15
-rw-r--r--lib/erl_interface/test/ei_decode_encode_SUITE_data/ei_decode_encode_test.c2
-rw-r--r--lib/erl_interface/test/ei_encode_SUITE_data/ei_encode_test.c14
-rw-r--r--lib/erl_interface/test/ei_format_SUITE_data/ei_format_test.c8
-rw-r--r--lib/erl_interface/test/ei_print_SUITE_data/ei_print_test.c8
-rw-r--r--lib/erl_interface/test/ei_tmo_SUITE_data/ei_tmo_test.c8
-rw-r--r--lib/ftp/doc/src/ftp.xml2
-rw-r--r--lib/hipe/cerl/erl_bif_types.erl6
-rw-r--r--lib/inets/doc/src/notes.xml24
-rw-r--r--lib/inets/vsn.mk2
-rw-r--r--lib/kernel/doc/src/logger.xml9
-rw-r--r--lib/kernel/include/dist.hrl3
-rw-r--r--lib/kernel/src/dist_util.erl4
-rw-r--r--lib/kernel/src/gen_tcp.erl6
-rw-r--r--lib/kernel/src/gen_udp.erl6
-rw-r--r--lib/kernel/src/inet_tcp_dist.erl1
-rw-r--r--lib/kernel/src/logger.erl137
-rw-r--r--lib/kernel/src/logger_std_h.erl75
-rw-r--r--lib/kernel/src/net_kernel.erl16
-rw-r--r--lib/kernel/test/logger_SUITE.erl58
-rw-r--r--lib/kernel/test/logger_disk_log_h_SUITE.erl2
-rw-r--r--lib/kernel/test/logger_std_h_SUITE.erl18
-rw-r--r--lib/mnesia/src/mnesia_dumper.erl31
-rw-r--r--lib/observer/src/cdv_detail_wx.erl3
-rw-r--r--lib/observer/src/cdv_table_wx.erl3
-rw-r--r--lib/observer/src/cdv_virtual_list_wx.erl3
-rw-r--r--lib/observer/src/cdv_wx.erl3
-rw-r--r--lib/observer/src/observer_alloc_wx.erl3
-rw-r--r--lib/observer/src/observer_app_wx.erl11
-rw-r--r--lib/observer/src/observer_perf_wx.erl15
-rw-r--r--lib/observer/src/observer_port_wx.erl14
-rw-r--r--lib/observer/src/observer_pro_wx.erl15
-rw-r--r--lib/observer/src/observer_procinfo.erl8
-rw-r--r--lib/observer/src/observer_trace_wx.erl17
-rw-r--r--lib/observer/src/observer_traceoptions_wx.erl9
-rw-r--r--lib/observer/src/observer_tv_table.erl3
-rw-r--r--lib/observer/src/observer_tv_wx.erl13
-rw-r--r--lib/observer/src/observer_wx.erl20
-rw-r--r--lib/observer/test/crashdump_helper.erl2
-rw-r--r--lib/public_key/doc/src/public_key.xml2
-rw-r--r--lib/public_key/src/public_key.erl2
-rw-r--r--lib/reltool/doc/src/reltool.xml2
-rw-r--r--lib/reltool/doc/src/reltool_examples.xml9
-rw-r--r--lib/reltool/src/reltool.hrl3
-rw-r--r--lib/reltool/src/reltool_server.erl12
-rw-r--r--lib/reltool/src/reltool_target.erl27
-rw-r--r--lib/reltool/test/reltool_server_SUITE.erl179
-rw-r--r--lib/reltool/test/reltool_test_lib.erl3
-rw-r--r--lib/reltool/test/reltool_wx_SUITE.erl7
-rw-r--r--lib/sasl/test/test_lib.hrl4
-rw-r--r--lib/ssh/test/ssh_trpt_test_lib.erl99
-rw-r--r--lib/ssl/doc/specs/.gitignore1
-rw-r--r--lib/ssl/doc/src/Makefile8
-rw-r--r--lib/ssl/doc/src/specs.xml9
-rw-r--r--lib/ssl/doc/src/ssl.xml1788
-rw-r--r--lib/ssl/doc/src/ssl_crl_cache.xml25
-rw-r--r--lib/ssl/doc/src/ssl_crl_cache_api.xml61
-rw-r--r--lib/ssl/doc/src/ssl_session_cache_api.xml95
-rw-r--r--lib/ssl/src/dtls_connection.erl214
-rw-r--r--lib/ssl/src/dtls_handshake.erl8
-rw-r--r--lib/ssl/src/dtls_handshake.hrl1
-rw-r--r--lib/ssl/src/dtls_packet_demux.erl4
-rw-r--r--lib/ssl/src/dtls_record.erl51
-rw-r--r--lib/ssl/src/inet_tls_dist.erl25
-rw-r--r--lib/ssl/src/ssl.erl432
-rw-r--r--lib/ssl/src/ssl_alert.erl77
-rw-r--r--lib/ssl/src/ssl_api.hrl49
-rw-r--r--lib/ssl/src/ssl_cipher.erl109
-rw-r--r--lib/ssl/src/ssl_cipher.hrl1
-rw-r--r--lib/ssl/src/ssl_cipher_format.erl42
-rw-r--r--lib/ssl/src/ssl_connection.erl1188
-rw-r--r--lib/ssl/src/ssl_connection.hrl93
-rw-r--r--lib/ssl/src/ssl_crl_cache.erl4
-rw-r--r--lib/ssl/src/ssl_crl_cache_api.erl15
-rw-r--r--lib/ssl/src/ssl_handshake.erl4
-rw-r--r--lib/ssl/src/ssl_internal.hrl14
-rw-r--r--lib/ssl/src/ssl_logger.erl14
-rw-r--r--lib/ssl/src/ssl_manager.erl8
-rw-r--r--lib/ssl/src/ssl_record.erl72
-rw-r--r--lib/ssl/src/ssl_record.hrl4
-rw-r--r--lib/ssl/src/ssl_session.erl5
-rw-r--r--lib/ssl/src/ssl_session_cache_api.erl24
-rw-r--r--lib/ssl/src/tls_connection.erl318
-rw-r--r--lib/ssl/src/tls_connection.hrl1
-rw-r--r--lib/ssl/src/tls_connection_1_3.erl36
-rw-r--r--lib/ssl/src/tls_handshake.erl9
-rw-r--r--lib/ssl/src/tls_handshake_1_3.erl193
-rw-r--r--lib/ssl/src/tls_record.erl431
-rw-r--r--lib/ssl/src/tls_record_1_3.erl17
-rw-r--r--lib/ssl/src/tls_sender.erl294
-rw-r--r--lib/ssl/src/tls_v1.erl10
-rw-r--r--lib/ssl/test/Makefile2
-rw-r--r--lib/ssl/test/ssl_alpn_handshake_SUITE.erl35
-rw-r--r--lib/ssl/test/ssl_basic_SUITE.erl248
-rw-r--r--lib/ssl/test/ssl_bench_SUITE.erl37
-rw-r--r--lib/ssl/test/ssl_certificate_verify_SUITE.erl109
-rw-r--r--lib/ssl/test/ssl_crl_SUITE.erl23
-rw-r--r--lib/ssl/test/ssl_dist_bench_SUITE.erl63
-rw-r--r--lib/ssl/test/ssl_sni_SUITE.erl8
-rw-r--r--lib/ssl/test/ssl_test_lib.erl80
-rw-r--r--lib/ssl/test/ssl_to_openssl_SUITE.erl7
-rw-r--r--lib/stdlib/doc/src/ets.xml20
-rw-r--r--lib/stdlib/doc/src/notes.xml15
-rw-r--r--lib/stdlib/doc/src/proplists.xml5
-rw-r--r--lib/stdlib/src/calendar.erl39
-rw-r--r--lib/stdlib/src/erl_pp.erl4
-rw-r--r--lib/stdlib/src/io_lib.erl18
-rw-r--r--lib/stdlib/src/io_lib_format.erl27
-rw-r--r--lib/stdlib/src/io_lib_pretty.erl20
-rw-r--r--lib/stdlib/src/stdlib.appup.src8
-rw-r--r--lib/stdlib/test/binary_module_SUITE.erl15
-rw-r--r--lib/stdlib/test/calendar_SUITE.erl14
-rw-r--r--lib/stdlib/test/erl_eval_SUITE.erl12
-rw-r--r--lib/stdlib/test/erl_pp_SUITE.erl11
-rw-r--r--lib/stdlib/vsn.mk2
-rw-r--r--lib/tools/doc/src/cover.xml16
-rw-r--r--lib/tools/src/cover.erl490
-rw-r--r--lib/tools/test/cover_SUITE.erl40
-rw-r--r--lib/wx/api_gen/wx_extra/added_func.h6
-rw-r--r--lib/wx/api_gen/wx_gen.erl9
-rw-r--r--lib/wx/api_gen/wx_gen_cpp.erl8
-rw-r--r--lib/wx/api_gen/wxapi.conf36
-rw-r--r--lib/wx/c_src/gen/wxe_derived_dest.h10
-rw-r--r--lib/wx/c_src/gen/wxe_events.cpp4
-rw-r--r--lib/wx/c_src/gen/wxe_funcs.cpp115
-rw-r--r--lib/wx/c_src/gen/wxe_init.cpp66
-rw-r--r--lib/wx/c_src/gen/wxe_macros.h20
-rw-r--r--lib/wx/c_src/wxe_impl.cpp16
-rw-r--r--lib/wx/include/wx.hrl16
-rw-r--r--lib/wx/src/gen/wxDisplay.erl131
-rw-r--r--lib/wx/src/gen/wxGCDC.erl287
-rw-r--r--lib/wx/src/gen/wxe_debug.hrl18
-rw-r--r--lib/wx/src/gen/wxe_funcs.hrl18
-rwxr-xr-xotp_build2
-rw-r--r--otp_versions.table5
497 files changed, 13565 insertions, 7038 deletions
diff --git a/.gitignore b/.gitignore
index ef19ace53c..8dd66f605a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -246,6 +246,7 @@ JAVADOC-GENERATED
/lib/compiler/src/core_parse.erl
/lib/compiler/test/*_no_opt_SUITE.erl
+/lib/compiler/test/*_no_ssa_opt_SUITE.erl
/lib/compiler/test/*_post_opt_SUITE.erl
/lib/compiler/test/*_inline_SUITE.erl
/lib/compiler/test/*_r21_SUITE.erl
diff --git a/bootstrap/bin/no_dot_erlang.boot b/bootstrap/bin/no_dot_erlang.boot
index 0d5ef3920d..496654fcea 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 0d5ef3920d..496654fcea 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 0d5ef3920d..496654fcea 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 addcf3a6de..ceb83ba48b 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 cac4ba468c..232cc74188 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 3f7efe278b..1db56cb5f1 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
deleted file mode 100644
index e59be34306..0000000000
--- a/bootstrap/lib/compiler/ebin/beam_bs.beam
+++ /dev/null
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/beam_clean.beam b/bootstrap/lib/compiler/ebin/beam_clean.beam
index 7ee870f80f..4d567eca88 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_dict.beam b/bootstrap/lib/compiler/ebin/beam_dict.beam
index c1aadafb76..19a05c1276 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 a75223de58..6b6b6d0f3f 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 2a892da335..c76d41c58a 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_jump.beam b/bootstrap/lib/compiler/ebin/beam_jump.beam
index 160958f700..e23ff4e906 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_kernel_to_ssa.beam b/bootstrap/lib/compiler/ebin/beam_kernel_to_ssa.beam
index 40b20fbdd1..b02630aed9 100644
--- a/bootstrap/lib/compiler/ebin/beam_kernel_to_ssa.beam
+++ b/bootstrap/lib/compiler/ebin/beam_kernel_to_ssa.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/beam_peep.beam b/bootstrap/lib/compiler/ebin/beam_peep.beam
index dfcfdfb25a..29016312bc 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_ssa.beam b/bootstrap/lib/compiler/ebin/beam_ssa.beam
index f2732c5c26..4f44f50dff 100644
--- a/bootstrap/lib/compiler/ebin/beam_ssa.beam
+++ b/bootstrap/lib/compiler/ebin/beam_ssa.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/beam_ssa_bsm.beam b/bootstrap/lib/compiler/ebin/beam_ssa_bsm.beam
index 1ed4084574..cdbc969b92 100644
--- a/bootstrap/lib/compiler/ebin/beam_ssa_bsm.beam
+++ b/bootstrap/lib/compiler/ebin/beam_ssa_bsm.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/beam_ssa_codegen.beam b/bootstrap/lib/compiler/ebin/beam_ssa_codegen.beam
index 96a6141b72..7a09803699 100644
--- a/bootstrap/lib/compiler/ebin/beam_ssa_codegen.beam
+++ b/bootstrap/lib/compiler/ebin/beam_ssa_codegen.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/beam_ssa_dead.beam b/bootstrap/lib/compiler/ebin/beam_ssa_dead.beam
index 3491c86049..edc5cc5679 100644
--- a/bootstrap/lib/compiler/ebin/beam_ssa_dead.beam
+++ b/bootstrap/lib/compiler/ebin/beam_ssa_dead.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/beam_ssa_funs.beam b/bootstrap/lib/compiler/ebin/beam_ssa_funs.beam
index bcfaa3da2d..d28c4c6b81 100644
--- a/bootstrap/lib/compiler/ebin/beam_ssa_funs.beam
+++ b/bootstrap/lib/compiler/ebin/beam_ssa_funs.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/beam_ssa_lint.beam b/bootstrap/lib/compiler/ebin/beam_ssa_lint.beam
index 5102ff8dc0..322653dfc7 100644
--- a/bootstrap/lib/compiler/ebin/beam_ssa_lint.beam
+++ b/bootstrap/lib/compiler/ebin/beam_ssa_lint.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/beam_ssa_opt.beam b/bootstrap/lib/compiler/ebin/beam_ssa_opt.beam
index 4c6af72b91..d889add00d 100644
--- a/bootstrap/lib/compiler/ebin/beam_ssa_opt.beam
+++ b/bootstrap/lib/compiler/ebin/beam_ssa_opt.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/beam_ssa_pp.beam b/bootstrap/lib/compiler/ebin/beam_ssa_pp.beam
index 8d60dcecf0..f76f15aa70 100644
--- a/bootstrap/lib/compiler/ebin/beam_ssa_pp.beam
+++ b/bootstrap/lib/compiler/ebin/beam_ssa_pp.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/beam_ssa_pre_codegen.beam b/bootstrap/lib/compiler/ebin/beam_ssa_pre_codegen.beam
index d2fabb3b18..6e8f7ac648 100644
--- a/bootstrap/lib/compiler/ebin/beam_ssa_pre_codegen.beam
+++ b/bootstrap/lib/compiler/ebin/beam_ssa_pre_codegen.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/beam_ssa_recv.beam b/bootstrap/lib/compiler/ebin/beam_ssa_recv.beam
index 97504d7aed..e86fa1831a 100644
--- a/bootstrap/lib/compiler/ebin/beam_ssa_recv.beam
+++ b/bootstrap/lib/compiler/ebin/beam_ssa_recv.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/beam_ssa_share.beam b/bootstrap/lib/compiler/ebin/beam_ssa_share.beam
index d1062609c3..ea8e83d919 100644
--- a/bootstrap/lib/compiler/ebin/beam_ssa_share.beam
+++ b/bootstrap/lib/compiler/ebin/beam_ssa_share.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/beam_ssa_type.beam b/bootstrap/lib/compiler/ebin/beam_ssa_type.beam
index 504a94cb0a..de9929bc92 100644
--- a/bootstrap/lib/compiler/ebin/beam_ssa_type.beam
+++ b/bootstrap/lib/compiler/ebin/beam_ssa_type.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/beam_trim.beam b/bootstrap/lib/compiler/ebin/beam_trim.beam
index 2574f1ec31..00f3224106 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_utils.beam b/bootstrap/lib/compiler/ebin/beam_utils.beam
index cef4e2ec8d..996525efc9 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 72e00f055c..74590b52d7 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 eebfcf4a5c..9a8ca5e718 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 6df0b15731..254f94fd36 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 299b535077..a20a4ca43b 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 c6e0e6e8e0..c20b40541c 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 c4fd19ef3b..63532015b0 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 c1788f31c3..fda55d4035 100644
--- a/bootstrap/lib/compiler/ebin/compile.beam
+++ b/bootstrap/lib/compiler/ebin/compile.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/compiler.app b/bootstrap/lib/compiler/ebin/compiler.app
index 70748f16ff..0a5e6de039 100644
--- a/bootstrap/lib/compiler/ebin/compiler.app
+++ b/bootstrap/lib/compiler/ebin/compiler.app
@@ -19,12 +19,11 @@
{application, compiler,
[{description, "ERTS CXC 138 10"},
- {vsn, "7.2.7"},
+ {vsn, "7.3.1"},
{modules, [
beam_a,
beam_asm,
beam_block,
- beam_bs,
beam_clean,
beam_dict,
beam_disasm,
@@ -66,7 +65,6 @@
rec_env,
sys_core_alias,
sys_core_bsm,
- sys_core_dsetel,
sys_core_fold,
sys_core_fold_lists,
sys_core_inline,
diff --git a/bootstrap/lib/compiler/ebin/compiler.appup b/bootstrap/lib/compiler/ebin/compiler.appup
index 399b1af1bb..8370664167 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.2.4",
+{"7.3.1",
[{<<".*">>,[{restart_application, compiler}]}],
[{<<".*">>,[{restart_application, compiler}]}]
}.
diff --git a/bootstrap/lib/compiler/ebin/core_lib.beam b/bootstrap/lib/compiler/ebin/core_lib.beam
index eb7dca711d..19b0c3dfcb 100644
--- a/bootstrap/lib/compiler/ebin/core_lib.beam
+++ b/bootstrap/lib/compiler/ebin/core_lib.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/core_lint.beam b/bootstrap/lib/compiler/ebin/core_lint.beam
index 75ec19a458..ecad4f33e0 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 84a20817f4..f14357cf02 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 ceff6dbdfa..2423c9b2ea 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 b6dcb0a559..8e44464cd7 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/erl_bifs.beam b/bootstrap/lib/compiler/ebin/erl_bifs.beam
index dc3ede0fce..e626e91f4f 100644
--- a/bootstrap/lib/compiler/ebin/erl_bifs.beam
+++ b/bootstrap/lib/compiler/ebin/erl_bifs.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/rec_env.beam b/bootstrap/lib/compiler/ebin/rec_env.beam
index ddbf799292..329100e682 100644
--- a/bootstrap/lib/compiler/ebin/rec_env.beam
+++ b/bootstrap/lib/compiler/ebin/rec_env.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 d6899a780b..953776ebaa 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 fe04ff54ba..6f4204e0a1 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
deleted file mode 100644
index 344e864891..0000000000
--- a/bootstrap/lib/compiler/ebin/sys_core_dsetel.beam
+++ /dev/null
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/sys_core_fold.beam b/bootstrap/lib/compiler/ebin/sys_core_fold.beam
index bac42707e9..98d24fc57a 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 db9b195aec..f4aac9ac13 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 55869526a4..d06f8d4823 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 348c060875..002b4f2914 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_core.beam b/bootstrap/lib/compiler/ebin/v3_core.beam
index b1e0e21729..a4da69abdf 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 251483be06..8fc867e66b 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 4e157cc305..e60bd7c722 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.beam b/bootstrap/lib/kernel/ebin/application.beam
index 83070b9a8a..faf620850e 100644
--- a/bootstrap/lib/kernel/ebin/application.beam
+++ b/bootstrap/lib/kernel/ebin/application.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/application_controller.beam b/bootstrap/lib/kernel/ebin/application_controller.beam
index 24ceb48536..e0c3790efe 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 f04a61d46f..77bb9544e2 100644
--- a/bootstrap/lib/kernel/ebin/application_master.beam
+++ b/bootstrap/lib/kernel/ebin/application_master.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/auth.beam b/bootstrap/lib/kernel/ebin/auth.beam
index d1a5f2c31e..a470129eff 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 99a05979a0..0a35cf9036 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 500906fe3c..cb1b8374e0 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 db24834614..35d41e0604 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 1859239f78..3884d05e0f 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 e5838e0171..bd40c824a6 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 e1145bd00f..99f5c66998 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 bbf2b22d0a..aec35239f6 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 2b4885199e..00e4aba64d 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_ddll.beam b/bootstrap/lib/kernel/ebin/erl_ddll.beam
index b08f0ed2ab..9708d146f7 100644
--- a/bootstrap/lib/kernel/ebin/erl_ddll.beam
+++ b/bootstrap/lib/kernel/ebin/erl_ddll.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/erl_epmd.beam b/bootstrap/lib/kernel/ebin/erl_epmd.beam
index 1c54ab32a4..5e43e48577 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/error_handler.beam b/bootstrap/lib/kernel/ebin/error_handler.beam
index 40fb8ef4ec..19739b0043 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 02ab05023a..f9b1dd2ed9 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 52aae18464..eff26cf7a2 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 51407568c7..3bf1cbeeaf 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 17473bb2e3..354fce8576 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/file_server.beam b/bootstrap/lib/kernel/ebin/file_server.beam
index 18134e367b..bfbef9341d 100644
--- a/bootstrap/lib/kernel/ebin/file_server.beam
+++ b/bootstrap/lib/kernel/ebin/file_server.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/gen_sctp.beam b/bootstrap/lib/kernel/ebin/gen_sctp.beam
index 6776a74958..9fc5800f0c 100644
--- a/bootstrap/lib/kernel/ebin/gen_sctp.beam
+++ b/bootstrap/lib/kernel/ebin/gen_sctp.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/global.beam b/bootstrap/lib/kernel/ebin/global.beam
index f5a8195f7c..4a9a6cd229 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 c1e229b2da..6252b1b3c2 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 620a9d2974..8fe196f806 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 e0287893c0..5cfc7183f9 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 945b10cd31..d826f4f99d 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 57e58235d4..fc80f2d1bd 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 2cace976ea..29225eb473 100644
--- a/bootstrap/lib/kernel/ebin/inet.beam
+++ b/bootstrap/lib/kernel/ebin/inet.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/inet6_tcp.beam b/bootstrap/lib/kernel/ebin/inet6_tcp.beam
index 6c1c562ded..7cacde3399 100644
--- a/bootstrap/lib/kernel/ebin/inet6_tcp.beam
+++ b/bootstrap/lib/kernel/ebin/inet6_tcp.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/inet_config.beam b/bootstrap/lib/kernel/ebin/inet_config.beam
index 1cf364e231..f06a0d5c68 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 f1f5766692..dfc94974b9 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 3f6e85965f..1af10a472f 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 4034ae6171..70af600524 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 ba989b642d..8fb47e84eb 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 b85540ead1..818c26d776 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 c3230acc89..2a54ed84e8 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_sctp.beam b/bootstrap/lib/kernel/ebin/inet_sctp.beam
index 65cbd8f5ad..0e659bd738 100644
--- a/bootstrap/lib/kernel/ebin/inet_sctp.beam
+++ b/bootstrap/lib/kernel/ebin/inet_sctp.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/inet_tcp.beam b/bootstrap/lib/kernel/ebin/inet_tcp.beam
index 49fdc129f4..c273d47012 100644
--- a/bootstrap/lib/kernel/ebin/inet_tcp.beam
+++ b/bootstrap/lib/kernel/ebin/inet_tcp.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 100ccc01fe..e2122ff2a3 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 35edff2393..62156a3284 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 898ed0a048..1b4665a31c 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, "6.1"},
+ {vsn, "6.2"},
{modules, [application,
application_controller,
application_master,
@@ -68,6 +68,8 @@
logger_formatter,
logger_h_common,
logger_handler_watcher,
+ logger_olp,
+ logger_proxy,
logger_server,
logger_simple_h,
logger_std_h,
diff --git a/bootstrap/lib/kernel/ebin/kernel.appup b/bootstrap/lib/kernel/ebin/kernel.appup
index ac114f0354..59e716b2d0 100644
--- a/bootstrap/lib/kernel/ebin/kernel.appup
+++ b/bootstrap/lib/kernel/ebin/kernel.appup
@@ -16,13 +16,42 @@
%% limitations under the License.
%%
%% %CopyrightEnd%
-{"5.4.3",
- %% Up from - max one major revision back
- [{<<"5\\.3(\\.[0-9]+)*">>,[restart_new_emulator]}, % OTP-20.0
- {<<"5\\.4(\\.[0-9]+)*">>,[restart_new_emulator]}, % OTP-20.1+
- {<<"6\\.0(\\.[0-9]+)*">>,[restart_new_emulator]}], % OTP-21
- %% Down to - max one major revision back
- [{<<"5\\.3(\\.[0-9]+)*">>,[restart_new_emulator]}, % OTP-20.0
- {<<"5\\.4(\\.[0-9]+)*">>,[restart_new_emulator]}, % OTP-20.1+
- {<<"6\\.0(\\.[0-9]+)*">>,[restart_new_emulator]}] % OTP-21
-}.
+%%
+%% We allow upgrade from, and downgrade to all previous
+%% versions from the following OTP releases:
+%% - OTP 20
+%% - OTP 21
+%%
+%% We also allow upgrade from, and downgrade to all
+%% versions that have branched off from the above
+%% stated previous versions.
+%%
+{"6.2",
+ [{<<"^5\\.3$">>,[restart_new_emulator]},
+ {<<"^5\\.3\\.0(?:\\.[0-9]+)+$">>,[restart_new_emulator]},
+ {<<"^5\\.3\\.1(?:\\.[0-9]+)*$">>,[restart_new_emulator]},
+ {<<"^5\\.4$">>,[restart_new_emulator]},
+ {<<"^5\\.4\\.0(?:\\.[0-9]+)+$">>,[restart_new_emulator]},
+ {<<"^5\\.4\\.1(?:\\.[0-9]+)*$">>,[restart_new_emulator]},
+ {<<"^5\\.4\\.2(?:\\.[0-9]+)*$">>,[restart_new_emulator]},
+ {<<"^5\\.4\\.3(?:\\.[0-9]+)*$">>,[restart_new_emulator]},
+ {<<"^6\\.0$">>,[restart_new_emulator]},
+ {<<"^6\\.0\\.0(?:\\.[0-9]+)+$">>,[restart_new_emulator]},
+ {<<"^6\\.0\\.1(?:\\.[0-9]+)*$">>,[restart_new_emulator]},
+ {<<"^6\\.1$">>,[restart_new_emulator]},
+ {<<"^6\\.1\\.0(?:\\.[0-9]+)+$">>,[restart_new_emulator]},
+ {<<"^6\\.1\\.1(?:\\.[0-9]+)*$">>,[restart_new_emulator]}],
+ [{<<"^5\\.3$">>,[restart_new_emulator]},
+ {<<"^5\\.3\\.0(?:\\.[0-9]+)+$">>,[restart_new_emulator]},
+ {<<"^5\\.3\\.1(?:\\.[0-9]+)*$">>,[restart_new_emulator]},
+ {<<"^5\\.4$">>,[restart_new_emulator]},
+ {<<"^5\\.4\\.0(?:\\.[0-9]+)+$">>,[restart_new_emulator]},
+ {<<"^5\\.4\\.1(?:\\.[0-9]+)*$">>,[restart_new_emulator]},
+ {<<"^5\\.4\\.2(?:\\.[0-9]+)*$">>,[restart_new_emulator]},
+ {<<"^5\\.4\\.3(?:\\.[0-9]+)*$">>,[restart_new_emulator]},
+ {<<"^6\\.0$">>,[restart_new_emulator]},
+ {<<"^6\\.0\\.0(?:\\.[0-9]+)+$">>,[restart_new_emulator]},
+ {<<"^6\\.0\\.1(?:\\.[0-9]+)*$">>,[restart_new_emulator]},
+ {<<"^6\\.1$">>,[restart_new_emulator]},
+ {<<"^6\\.1\\.0(?:\\.[0-9]+)+$">>,[restart_new_emulator]},
+ {<<"^6\\.1\\.1(?:\\.[0-9]+)*$">>,[restart_new_emulator]}]}.
diff --git a/bootstrap/lib/kernel/ebin/kernel.beam b/bootstrap/lib/kernel/ebin/kernel.beam
index 10a5f0298a..75ff192576 100644
--- a/bootstrap/lib/kernel/ebin/kernel.beam
+++ b/bootstrap/lib/kernel/ebin/kernel.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/kernel_config.beam b/bootstrap/lib/kernel/ebin/kernel_config.beam
index 9f29de109f..bcadaab5f1 100644
--- a/bootstrap/lib/kernel/ebin/kernel_config.beam
+++ b/bootstrap/lib/kernel/ebin/kernel_config.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/kernel_refc.beam b/bootstrap/lib/kernel/ebin/kernel_refc.beam
index 3ab1e0e46a..04306c6e2c 100644
--- a/bootstrap/lib/kernel/ebin/kernel_refc.beam
+++ b/bootstrap/lib/kernel/ebin/kernel_refc.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/local_tcp.beam b/bootstrap/lib/kernel/ebin/local_tcp.beam
index 17b7c20daa..2958d19524 100644
--- a/bootstrap/lib/kernel/ebin/local_tcp.beam
+++ b/bootstrap/lib/kernel/ebin/local_tcp.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/local_udp.beam b/bootstrap/lib/kernel/ebin/local_udp.beam
index 725a6e2073..cc5f46f746 100644
--- a/bootstrap/lib/kernel/ebin/local_udp.beam
+++ b/bootstrap/lib/kernel/ebin/local_udp.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/logger.beam b/bootstrap/lib/kernel/ebin/logger.beam
index ac70f610cc..3865db9d89 100644
--- a/bootstrap/lib/kernel/ebin/logger.beam
+++ b/bootstrap/lib/kernel/ebin/logger.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/logger_backend.beam b/bootstrap/lib/kernel/ebin/logger_backend.beam
index 1b29546101..efc5d37a12 100644
--- a/bootstrap/lib/kernel/ebin/logger_backend.beam
+++ b/bootstrap/lib/kernel/ebin/logger_backend.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/logger_config.beam b/bootstrap/lib/kernel/ebin/logger_config.beam
index bded41c72d..51c7f78237 100644
--- a/bootstrap/lib/kernel/ebin/logger_config.beam
+++ b/bootstrap/lib/kernel/ebin/logger_config.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/logger_disk_log_h.beam b/bootstrap/lib/kernel/ebin/logger_disk_log_h.beam
index 31b18bde25..2b64b2ca84 100644
--- a/bootstrap/lib/kernel/ebin/logger_disk_log_h.beam
+++ b/bootstrap/lib/kernel/ebin/logger_disk_log_h.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/logger_filters.beam b/bootstrap/lib/kernel/ebin/logger_filters.beam
index 712ebee110..2d8035278d 100644
--- a/bootstrap/lib/kernel/ebin/logger_filters.beam
+++ b/bootstrap/lib/kernel/ebin/logger_filters.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/logger_formatter.beam b/bootstrap/lib/kernel/ebin/logger_formatter.beam
index b2d43763a9..c790d87569 100644
--- a/bootstrap/lib/kernel/ebin/logger_formatter.beam
+++ b/bootstrap/lib/kernel/ebin/logger_formatter.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/logger_h_common.beam b/bootstrap/lib/kernel/ebin/logger_h_common.beam
index 24bdf42273..7361625f7c 100644
--- a/bootstrap/lib/kernel/ebin/logger_h_common.beam
+++ b/bootstrap/lib/kernel/ebin/logger_h_common.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/logger_olp.beam b/bootstrap/lib/kernel/ebin/logger_olp.beam
new file mode 100644
index 0000000000..ea04f7bde8
--- /dev/null
+++ b/bootstrap/lib/kernel/ebin/logger_olp.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/logger_proxy.beam b/bootstrap/lib/kernel/ebin/logger_proxy.beam
new file mode 100644
index 0000000000..ed0ed0f56b
--- /dev/null
+++ b/bootstrap/lib/kernel/ebin/logger_proxy.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/logger_server.beam b/bootstrap/lib/kernel/ebin/logger_server.beam
index 91135f0080..daa22aee33 100644
--- a/bootstrap/lib/kernel/ebin/logger_server.beam
+++ b/bootstrap/lib/kernel/ebin/logger_server.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/logger_simple_h.beam b/bootstrap/lib/kernel/ebin/logger_simple_h.beam
index 73d8d0727c..a6d0c9e17c 100644
--- a/bootstrap/lib/kernel/ebin/logger_simple_h.beam
+++ b/bootstrap/lib/kernel/ebin/logger_simple_h.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/logger_std_h.beam b/bootstrap/lib/kernel/ebin/logger_std_h.beam
index 9577a05b2f..921e853f73 100644
--- a/bootstrap/lib/kernel/ebin/logger_std_h.beam
+++ b/bootstrap/lib/kernel/ebin/logger_std_h.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/logger_sup.beam b/bootstrap/lib/kernel/ebin/logger_sup.beam
index 32eb526a09..60192ef4a1 100644
--- a/bootstrap/lib/kernel/ebin/logger_sup.beam
+++ b/bootstrap/lib/kernel/ebin/logger_sup.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/net_adm.beam b/bootstrap/lib/kernel/ebin/net_adm.beam
index 3ba16fbf3f..5810577c6a 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 cd8db9b45f..7f57ea3d77 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 95da61ed4a..80e3f7b1e2 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 40d216bac9..d293ea18f3 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/ram_file.beam b/bootstrap/lib/kernel/ebin/ram_file.beam
index 8135abcab0..02c4c6454a 100644
--- a/bootstrap/lib/kernel/ebin/ram_file.beam
+++ b/bootstrap/lib/kernel/ebin/ram_file.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/raw_file_io.beam b/bootstrap/lib/kernel/ebin/raw_file_io.beam
index 2e73087b25..7d3cc2e3c3 100644
--- a/bootstrap/lib/kernel/ebin/raw_file_io.beam
+++ b/bootstrap/lib/kernel/ebin/raw_file_io.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/raw_file_io_compressed.beam b/bootstrap/lib/kernel/ebin/raw_file_io_compressed.beam
index ae9db38926..3c41f8528e 100644
--- a/bootstrap/lib/kernel/ebin/raw_file_io_compressed.beam
+++ b/bootstrap/lib/kernel/ebin/raw_file_io_compressed.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 0793ba6f2d..7185506bc5 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_delayed.beam b/bootstrap/lib/kernel/ebin/raw_file_io_delayed.beam
index 9199dac5ba..0ae74c7045 100644
--- a/bootstrap/lib/kernel/ebin/raw_file_io_delayed.beam
+++ b/bootstrap/lib/kernel/ebin/raw_file_io_delayed.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 457cbcc64c..194f5fb507 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/raw_file_io_list.beam b/bootstrap/lib/kernel/ebin/raw_file_io_list.beam
index 73f072fb5f..b544fd394e 100644
--- a/bootstrap/lib/kernel/ebin/raw_file_io_list.beam
+++ b/bootstrap/lib/kernel/ebin/raw_file_io_list.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/rpc.beam b/bootstrap/lib/kernel/ebin/rpc.beam
index 62b972805a..b53525e2ca 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/seq_trace.beam b/bootstrap/lib/kernel/ebin/seq_trace.beam
index 9a325446ed..f81a39ddc0 100644
--- a/bootstrap/lib/kernel/ebin/seq_trace.beam
+++ b/bootstrap/lib/kernel/ebin/seq_trace.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/standard_error.beam b/bootstrap/lib/kernel/ebin/standard_error.beam
index 0ca95ea770..18582d22f2 100644
--- a/bootstrap/lib/kernel/ebin/standard_error.beam
+++ b/bootstrap/lib/kernel/ebin/standard_error.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/user.beam b/bootstrap/lib/kernel/ebin/user.beam
index a30f12417a..3e7be39f5f 100644
--- a/bootstrap/lib/kernel/ebin/user.beam
+++ b/bootstrap/lib/kernel/ebin/user.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/user_drv.beam b/bootstrap/lib/kernel/ebin/user_drv.beam
index 3558dff5be..9232703812 100644
--- a/bootstrap/lib/kernel/ebin/user_drv.beam
+++ b/bootstrap/lib/kernel/ebin/user_drv.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/wrap_log_reader.beam b/bootstrap/lib/kernel/ebin/wrap_log_reader.beam
index 382cc0e618..6cfafbd750 100644
--- a/bootstrap/lib/kernel/ebin/wrap_log_reader.beam
+++ b/bootstrap/lib/kernel/ebin/wrap_log_reader.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/include/dist.hrl b/bootstrap/lib/kernel/include/dist.hrl
index 003852f1b0..f06fc328d7 100644
--- a/bootstrap/lib/kernel/include/dist.hrl
+++ b/bootstrap/lib/kernel/include/dist.hrl
@@ -42,6 +42,9 @@
-define(DFLAG_BIG_CREATION, 16#40000).
-define(DFLAG_SEND_SENDER, 16#80000).
-define(DFLAG_BIG_SEQTRACE_LABELS, 16#100000).
+%% -define(DFLAG_NO_MAGIC, 16#200000). %% Used internally only
+-define(DFLAG_EXIT_PAYLOAD, 16#400000).
+-define(DFLAG_FRAGMENTS, 16#800000).
%% Also update dflag2str() in ../src/dist_util.erl
%% when adding flags...
diff --git a/bootstrap/lib/stdlib/ebin/array.beam b/bootstrap/lib/stdlib/ebin/array.beam
index a132cb5470..ac3193515c 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 a778a2dad4..4782583fd6 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 c88e1a003f..0cedd64883 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 a367eb8897..9d9bf170a2 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 5aa493c638..d0abc17e48 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 d118445f7f..37f31e1945 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 f88f45c809..c5fabc5e98 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 440805c963..b25999333b 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 06356b7063..1e12c7a8b8 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 81b26934eb..dc390024a4 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 27da3f013b..999a3c5b7d 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/digraph_utils.beam b/bootstrap/lib/stdlib/ebin/digraph_utils.beam
index 7a4d347912..772332cd08 100644
--- a/bootstrap/lib/stdlib/ebin/digraph_utils.beam
+++ b/bootstrap/lib/stdlib/ebin/digraph_utils.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/edlin.beam b/bootstrap/lib/stdlib/ebin/edlin.beam
index cf1a6c5e90..9b6ea06df2 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 fb483ec8cd..81d57e38d9 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 5b995408bf..9611419792 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_abstract_code.beam b/bootstrap/lib/stdlib/ebin/erl_abstract_code.beam
index b52efa7445..973ad9fb80 100644
--- a/bootstrap/lib/stdlib/ebin/erl_abstract_code.beam
+++ b/bootstrap/lib/stdlib/ebin/erl_abstract_code.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/erl_anno.beam b/bootstrap/lib/stdlib/ebin/erl_anno.beam
index e74e1a41c0..a40fcb3f92 100644
--- a/bootstrap/lib/stdlib/ebin/erl_anno.beam
+++ b/bootstrap/lib/stdlib/ebin/erl_anno.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/erl_bits.beam b/bootstrap/lib/stdlib/ebin/erl_bits.beam
index 1b0d1d2ceb..508da7ba8a 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 f625088f9c..07a8f1c9ac 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_error.beam b/bootstrap/lib/stdlib/ebin/erl_error.beam
index b19c0b7538..f47b2b2e14 100644
--- a/bootstrap/lib/stdlib/ebin/erl_error.beam
+++ b/bootstrap/lib/stdlib/ebin/erl_error.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/erl_eval.beam b/bootstrap/lib/stdlib/ebin/erl_eval.beam
index 10a62b2b15..ee6df8efcb 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 da0822e9bb..331a946757 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_internal.beam b/bootstrap/lib/stdlib/ebin/erl_internal.beam
index 37d4e09347..9015ad8936 100644
--- a/bootstrap/lib/stdlib/ebin/erl_internal.beam
+++ b/bootstrap/lib/stdlib/ebin/erl_internal.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/erl_lint.beam b/bootstrap/lib/stdlib/ebin/erl_lint.beam
index 8170bfb4cc..e50e44fc4b 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 79194f5283..41369ca147 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 e741577c2f..64b26f2510 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_scan.beam b/bootstrap/lib/stdlib/ebin/erl_scan.beam
index 360233e8f3..8347f97ec1 100644
--- a/bootstrap/lib/stdlib/ebin/erl_scan.beam
+++ b/bootstrap/lib/stdlib/ebin/erl_scan.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/erl_tar.beam b/bootstrap/lib/stdlib/ebin/erl_tar.beam
index 99ef87f432..79cddbdab2 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_tty_h.beam b/bootstrap/lib/stdlib/ebin/error_logger_tty_h.beam
index 4ecfd2d919..bdb80ce1e4 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 b5ed605760..880fc090b2 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 a40a00ab48..8d3538e7a7 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 639cbbfb60..2f36309102 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 dbc990c5b6..65dcc78f23 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 b366827ae7..6efc5e219a 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 068ca59ebe..b882b287c2 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 c402e65233..8d1e2c11e0 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/gb_trees.beam b/bootstrap/lib/stdlib/ebin/gb_trees.beam
index 215704f10e..0aae509cb6 100644
--- a/bootstrap/lib/stdlib/ebin/gb_trees.beam
+++ b/bootstrap/lib/stdlib/ebin/gb_trees.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/gen.beam b/bootstrap/lib/stdlib/ebin/gen.beam
index dfe444ee66..3179f098c9 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 b41dc43257..4727efff99 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 dfa8202179..be5213e76d 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 fb69aaf296..b84877582b 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 b643d2f550..f5185696eb 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 4b4b4d6196..bce83b5a3a 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 1b21c65ba1..c52efe1b44 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 e42ebca4fe..5556dc733b 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_fread.beam b/bootstrap/lib/stdlib/ebin/io_lib_fread.beam
index 3bc7bfd679..b56a9e3eb7 100644
--- a/bootstrap/lib/stdlib/ebin/io_lib_fread.beam
+++ b/bootstrap/lib/stdlib/ebin/io_lib_fread.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 a732e98e3c..09e9be48f8 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/lists.beam b/bootstrap/lib/stdlib/ebin/lists.beam
index 5134be06af..6178717067 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/log_mf_h.beam b/bootstrap/lib/stdlib/ebin/log_mf_h.beam
index 592de58dc5..bc1b5c7552 100644
--- a/bootstrap/lib/stdlib/ebin/log_mf_h.beam
+++ b/bootstrap/lib/stdlib/ebin/log_mf_h.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/maps.beam b/bootstrap/lib/stdlib/ebin/maps.beam
index 623fd0951c..193a06241d 100644
--- a/bootstrap/lib/stdlib/ebin/maps.beam
+++ b/bootstrap/lib/stdlib/ebin/maps.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/ms_transform.beam b/bootstrap/lib/stdlib/ebin/ms_transform.beam
index 65d745a858..aa9d279a9e 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/otp_internal.beam b/bootstrap/lib/stdlib/ebin/otp_internal.beam
index e1785421db..4dbdabbb7d 100644
--- a/bootstrap/lib/stdlib/ebin/otp_internal.beam
+++ b/bootstrap/lib/stdlib/ebin/otp_internal.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/pool.beam b/bootstrap/lib/stdlib/ebin/pool.beam
index 7c310e3eda..408ee3a0c9 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 3f73773e06..c49c165b13 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 94fd9746da..a8ddeb2d57 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 af30cc1864..de61165aae 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 41adc8473e..653fa3e808 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 4a52126c62..571d4a13a5 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/rand.beam b/bootstrap/lib/stdlib/ebin/rand.beam
index e4cbb26417..9636f4fbbf 100644
--- a/bootstrap/lib/stdlib/ebin/rand.beam
+++ b/bootstrap/lib/stdlib/ebin/rand.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/random.beam b/bootstrap/lib/stdlib/ebin/random.beam
index 197b7b8b50..a4bc2b6128 100644
--- a/bootstrap/lib/stdlib/ebin/random.beam
+++ b/bootstrap/lib/stdlib/ebin/random.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/re.beam b/bootstrap/lib/stdlib/ebin/re.beam
index af77423e98..7a010dbdd5 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 cea57f083a..e3609ea527 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 ff11977085..6bd3978bb4 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/sofs.beam b/bootstrap/lib/stdlib/ebin/sofs.beam
index 89529b7df0..3efc50d4a8 100644
--- a/bootstrap/lib/stdlib/ebin/sofs.beam
+++ b/bootstrap/lib/stdlib/ebin/sofs.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/stdlib.app b/bootstrap/lib/stdlib/ebin/stdlib.app
index 46163158bf..2bb0b06f12 100644
--- a/bootstrap/lib/stdlib/ebin/stdlib.app
+++ b/bootstrap/lib/stdlib/ebin/stdlib.app
@@ -20,7 +20,7 @@
%%
{application, stdlib,
[{description, "ERTS CXC 138 10"},
- {vsn, "3.6"},
+ {vsn, "3.7.1"},
{modules, [array,
base64,
beam_lib,
diff --git a/bootstrap/lib/stdlib/ebin/stdlib.appup b/bootstrap/lib/stdlib/ebin/stdlib.appup
index 381b33c833..d22e917574 100644
--- a/bootstrap/lib/stdlib/ebin/stdlib.appup
+++ b/bootstrap/lib/stdlib/ebin/stdlib.appup
@@ -16,11 +16,42 @@
%% limitations under the License.
%%
%% %CopyrightEnd%
-{"3.4.5",
- %% Up from - max one major revision back
- [{<<"3\\.4(\\.[0-9]+)*">>,[restart_new_emulator]}, % OTP-20.*
- {<<"3\\.5(\\.[0-9]+)*">>,[restart_new_emulator]}],% OTP-21.*
- %% Down to - max one major revision back
- [{<<"3\\.4(\\.[0-9]+)*">>,[restart_new_emulator]}, % OTP-20.*
- {<<"3\\.5(\\.[0-9]+)*">>,[restart_new_emulator]}] % OTP-20.*
-}.
+%%
+%% We allow upgrade from, and downgrade to all previous
+%% versions from the following OTP releases:
+%% - OTP 20
+%% - OTP 21
+%%
+%% We also allow upgrade from, and downgrade to all
+%% versions that have branched off from the above
+%% stated previous versions.
+%%
+{"3.7.1",
+ [{<<"^3\\.4$">>,[restart_new_emulator]},
+ {<<"^3\\.4\\.0(?:\\.[0-9]+)+$">>,[restart_new_emulator]},
+ {<<"^3\\.4\\.1(?:\\.[0-9]+)*$">>,[restart_new_emulator]},
+ {<<"^3\\.4\\.2(?:\\.[0-9]+)*$">>,[restart_new_emulator]},
+ {<<"^3\\.4\\.3(?:\\.[0-9]+)*$">>,[restart_new_emulator]},
+ {<<"^3\\.4\\.4(?:\\.[0-9]+)*$">>,[restart_new_emulator]},
+ {<<"^3\\.4\\.5(?:\\.[0-9]+)*$">>,[restart_new_emulator]},
+ {<<"^3\\.5$">>,[restart_new_emulator]},
+ {<<"^3\\.5\\.0(?:\\.[0-9]+)+$">>,[restart_new_emulator]},
+ {<<"^3\\.5\\.1(?:\\.[0-9]+)*$">>,[restart_new_emulator]},
+ {<<"^3\\.6$">>,[restart_new_emulator]},
+ {<<"^3\\.6\\.0(?:\\.[0-9]+)+$">>,[restart_new_emulator]},
+ {<<"^3\\.7$">>,[restart_new_emulator]},
+ {<<"^3\\.7\\.0(?:\\.[0-9]+)+$">>,[restart_new_emulator]}],
+ [{<<"^3\\.4$">>,[restart_new_emulator]},
+ {<<"^3\\.4\\.0(?:\\.[0-9]+)+$">>,[restart_new_emulator]},
+ {<<"^3\\.4\\.1(?:\\.[0-9]+)*$">>,[restart_new_emulator]},
+ {<<"^3\\.4\\.2(?:\\.[0-9]+)*$">>,[restart_new_emulator]},
+ {<<"^3\\.4\\.3(?:\\.[0-9]+)*$">>,[restart_new_emulator]},
+ {<<"^3\\.4\\.4(?:\\.[0-9]+)*$">>,[restart_new_emulator]},
+ {<<"^3\\.4\\.5(?:\\.[0-9]+)*$">>,[restart_new_emulator]},
+ {<<"^3\\.5$">>,[restart_new_emulator]},
+ {<<"^3\\.5\\.0(?:\\.[0-9]+)+$">>,[restart_new_emulator]},
+ {<<"^3\\.5\\.1(?:\\.[0-9]+)*$">>,[restart_new_emulator]},
+ {<<"^3\\.6$">>,[restart_new_emulator]},
+ {<<"^3\\.6\\.0(?:\\.[0-9]+)+$">>,[restart_new_emulator]},
+ {<<"^3\\.7$">>,[restart_new_emulator]},
+ {<<"^3\\.7\\.0(?:\\.[0-9]+)+$">>,[restart_new_emulator]}]}.
diff --git a/bootstrap/lib/stdlib/ebin/string.beam b/bootstrap/lib/stdlib/ebin/string.beam
index dd005daf89..da647da3ca 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 425c946a05..243a67008b 100644
--- a/bootstrap/lib/stdlib/ebin/supervisor.beam
+++ b/bootstrap/lib/stdlib/ebin/supervisor.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/supervisor_bridge.beam b/bootstrap/lib/stdlib/ebin/supervisor_bridge.beam
index 6b410a6bd2..63cfa4ab2e 100644
--- a/bootstrap/lib/stdlib/ebin/supervisor_bridge.beam
+++ b/bootstrap/lib/stdlib/ebin/supervisor_bridge.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/sys.beam b/bootstrap/lib/stdlib/ebin/sys.beam
index 90e7fbfdb2..fb1d52268c 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.beam b/bootstrap/lib/stdlib/ebin/unicode.beam
index 71ee4524a7..8534ddbe22 100644
--- a/bootstrap/lib/stdlib/ebin/unicode.beam
+++ b/bootstrap/lib/stdlib/ebin/unicode.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/unicode_util.beam b/bootstrap/lib/stdlib/ebin/unicode_util.beam
index 93d8257a71..7da23997df 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 0a0dfaff7a..001118f0b9 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 aed66c3559..136b65f9fb 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 a03dd40c1f..84807f7e39 100644
--- a/bootstrap/lib/stdlib/ebin/zip.beam
+++ b/bootstrap/lib/stdlib/ebin/zip.beam
Binary files differ
diff --git a/erts/doc/src/erl_dist_protocol.xml b/erts/doc/src/erl_dist_protocol.xml
index c90c8f9521..185c75fe84 100644
--- a/erts/doc/src/erl_dist_protocol.xml
+++ b/erts/doc/src/erl_dist_protocol.xml
@@ -532,11 +532,7 @@ io:format("old/unused name ~ts at port ~p, fd = ~p ~n",
<marker id="distribution_handshake"/>
<title>Distribution Handshake</title>
<p>This section describes the distribution handshake protocol introduced
- in Erlang/OTP R6. This description was previously located in
- <c>$ERL_TOP/lib/kernel/internal_doc/distribution_handshake.txt</c> and
- has more or less been copied and "formatted" here. It has been almost
- unchanged since 1999, but the handshake has not changed much since then
- either.</p>
+ in Erlang/OTP R6. The handshake has remained almost the same since then.</p>
<section>
<title>General</title>
@@ -847,6 +843,18 @@ DiB == gen_digest(ChA, ICA)?
of the <c>SEND_TT</c> control message.
</p>
</item>
+ <tag><c>-define(DFLAG_BIG_SEQTRACE_LABELS, 16#100000).</c></tag>
+ <item>
+ <p>The node understands any term as the seqtrace label.</p>
+ </item>
+ <tag><c>-define(DFLAG_EXIT_PAYLOAD, 16#400000).</c></tag>
+ <item>
+ <p>Use the <c>PAYLOAD_EXIT</c>, <c>PAYLOAD_EXIT_TT</c>,
+ <c>PAYLOAD_EXIT2</c>, <c>PAYLOAD_EXIT2_TT</c>
+ and <c>PAYLOAD_MONITOR_P_EXIT</c>
+ <seealso marker="#control_message">control message</seealso>s
+ instead of the non-PAYLOAD variants.</p>
+ </item>
</taglist>
<p>
There is also function <c>dist_util:strict_order_flags/0</c>
@@ -859,7 +867,7 @@ DiB == gen_digest(ChA, ICA)?
<section>
<marker id="connected_nodes"/>
<title>Protocol between Connected Nodes</title>
- <p>As from ERTS 5.7.2 the runtime system passes a distribution flag
+ <p>Since ERTS 5.7.2 (OTP R13B) the runtime system passes a distribution flag
in the handshake stage that enables the use of a
<seealso marker="erl_ext_dist#distribution_header">distribution header
</seealso> on all messages passed. Messages passed between nodes have in
@@ -878,7 +886,7 @@ DiB == gen_digest(ChA, ICA)?
<cell align="center"><c>ControlMessage</c></cell>
<cell align="center"><c>Message</c></cell>
</row>
- <tcaption>Format of Messages Passed between Nodes (as from ERTS 5.7.2)
+ <tcaption>Format of Messages Passed between Nodes (as from ERTS 5.7.2 (OTP R13B))
</tcaption>
</table>
@@ -887,15 +895,23 @@ DiB == gen_digest(ChA, ICA)?
<item>
<p>Equal to d + n + m.</p>
</item>
+ <tag><c>DistributionHeader</c></tag>
+ <item>
+ <p>
+ <seealso marker="erl_ext_dist#distribution_header">Distribution header
+ describing the atom cache and fragmented distribution messages.
+ </seealso>
+ </p>
+ </item>
<tag><c>ControlMessage</c></tag>
<item>
<p>A tuple passed using the external format of Erlang.</p>
</item>
<tag><c>Message</c></tag>
<item>
- <p>The message sent to another node using the '!' (in external format).
- Notice that <c>Message</c> is only passed in combination with a
- <c>ControlMessage</c> encoding a send ('!').</p>
+ <p>The message sent to another node using the '!'
+ or the reason for a EXIT, EXIT2 or DOWN signal using
+ the external term format.</p>
</item>
</taglist>
@@ -903,7 +919,7 @@ DiB == gen_digest(ChA, ICA)?
number is omitted from the terms that follow a distribution header
</seealso>.</p>
- <p>Nodes with an ERTS version earlier than 5.7.2 does not pass the
+ <p>Nodes with an ERTS version earlier than 5.7.2 (OTP R13B) does not pass the
distribution flag that enables the distribution header. Messages passed
between nodes have in this case the following format:</p>
@@ -920,7 +936,7 @@ DiB == gen_digest(ChA, ICA)?
<cell align="center"><c>ControlMessage</c></cell>
<cell align="center"><c>Message</c></cell>
</row>
- <tcaption>Format of Messages Passed between Nodes (before ERTS 5.7.2)
+ <tcaption>Format of Messages Passed between Nodes (before ERTS 5.7.2 (OTP R13B))
</tcaption>
</table>
@@ -963,6 +979,7 @@ DiB == gen_digest(ChA, ICA)?
<tag><c>EXIT</c></tag>
<item>
<p><c>{3, FromPid, ToPid, Reason}</c></p>
+ <p>This signal is sent when a link has been broken</p>
</item>
<tag><c>UNLINK</c></tag>
<item>
@@ -985,6 +1002,7 @@ DiB == gen_digest(ChA, ICA)?
<tag><c>EXIT2</c></tag>
<item>
<p><c>{8, FromPid, ToPid, Reason}</c></p>
+ <p>This signal is sent by a call to the erlang:exit/2 bif</p>
</item>
</taglist>
</section>
@@ -1007,7 +1025,7 @@ DiB == gen_digest(ChA, ICA)?
<p><c>{16, FromPid, Unused, ToName, TraceToken}</c></p>
<p>Followed by <c>Message</c>.</p>
<p><c>Unused</c> is kept for backward compatibility.</p>
- </item>
+ </item>
<tag><c>EXIT2_TT</c></tag>
<item>
<p><c>{18, FromPid, ToPid, TraceToken, Reason}</c></p>
@@ -1061,7 +1079,7 @@ DiB == gen_digest(ChA, ICA)?
<p><c>{22, FromPid, ToPid}</c></p>
<p>Followed by <c>Message</c>.</p>
<p>
- This control messages replace the <c>SEND</c> control
+ This control message replaces the <c>SEND</c> control
message and will be sent when the distribution flag
<seealso marker="erl_dist_protocol#dflags"><c>DFLAG_SEND_SENDER</c></seealso>
has been negotiated in the connection setup handshake.
@@ -1080,7 +1098,7 @@ DiB == gen_digest(ChA, ICA)?
<p><c>{23, FromPid, ToPid, TraceToken}</c></p>
<p>Followed by <c>Message</c>.</p>
<p>
- This control messages replace the <c>SEND_TT</c> control
+ This control message replaces the <c>SEND_TT</c> control
message and will be sent when the distribution flag
<seealso marker="erl_dist_protocol#dflags"><c>DFLAG_SEND_SENDER</c></seealso>
has been negotiated in the connection setup handshake.
@@ -1097,4 +1115,72 @@ DiB == gen_digest(ChA, ICA)?
</taglist>
</section>
+ <section>
+ <title>New Ctrlmessages for Erlang/OTP 22</title>
+ <note><p>
+ Messages encoded before the connection has
+ been set up may still use the non-PAYLOAD variant.
+ However, once a PAYLOAD control message has been sent,
+ no more non-PAYLOAD control messages will be sent in
+ the same direction on the connection.
+ </p></note>
+ <taglist>
+ <tag><c>PAYLOAD_EXIT</c></tag>
+ <item>
+ <p><c>{24, FromPid, ToPid}</c></p>
+ <p>Followed by <c>Reason</c>.</p>
+ <p>
+ This control message replaces the <c>EXIT</c> control
+ message and will be sent when the distribution flag
+ <seealso marker="erl_dist_protocol#dflags"><c>DFLAG_EXIT_PAYLOAD</c></seealso>
+ has been negotiated in the connection setup handshake.
+ </p>
+ </item>
+ <tag><c>PAYLOAD_EXIT_TT</c></tag>
+ <item>
+ <p><c>{25, FromPid, ToPid}</c></p>
+ <p>Followed by <c>Reason</c>.</p>
+ <p>
+ This control message replaces the <c>EXIT_TT</c> control
+ message and will be sent when the distribution flag
+ <seealso marker="erl_dist_protocol#dflags"><c>DFLAG_EXIT_PAYLOAD</c></seealso>
+ has been negotiated in the connection setup handshake.
+ </p>
+ </item>
+ <tag><c>PAYLOAD_EXIT2</c></tag>
+ <item>
+ <p><c>{26, FromPid, ToPid}</c></p>
+ <p>Followed by <c>Reason</c>.</p>
+ <p>
+ This control message replaces the <c>EXIT2</c> control
+ message and will be sent when the distribution flag
+ <seealso marker="erl_dist_protocol#dflags"><c>DFLAG_EXIT_PAYLOAD</c></seealso>
+ has been negotiated in the connection setup handshake.
+ </p>
+ </item>
+ <tag><c>PAYLOAD_EXIT2_TT</c></tag>
+ <item>
+ <p><c>{27, FromPid, ToPid}</c></p>
+ <p>Followed by <c>Reason</c>.</p>
+ <p>
+ This control message replaces the <c>EXIT2_TT</c> control
+ message and will be sent when the distribution flag
+ <seealso marker="erl_dist_protocol#dflags"><c>DFLAG_EXIT_PAYLOAD</c></seealso>
+ has been negotiated in the connection setup handshake.
+ </p>
+ </item>
+ <tag><c>PAYLOAD_MONITOR_P_EXIT</c></tag>
+ <item>
+ <p><c>{28, FromPid, ToPid, Ref}</c></p>
+ <p>Followed by <c>Reason</c>.</p>
+ <p>
+ This control message replaces the <c>MONITOR_P_EXIT</c> control
+ message and will be sent when the distribution flag
+ <seealso marker="erl_dist_protocol#dflags"><c>DFLAG_EXIT_PAYLOAD</c></seealso>
+ has been negotiated in the connection setup handshake.
+ </p>
+ </item>
+ </taglist>
+ </section>
+
</chapter>
diff --git a/erts/doc/src/erl_ext_dist.xml b/erts/doc/src/erl_ext_dist.xml
index b7090d0472..29c6694a6b 100644
--- a/erts/doc/src/erl_ext_dist.xml
+++ b/erts/doc/src/erl_ext_dist.xml
@@ -136,16 +136,9 @@
</note>
</section>
- <section>
+ <section>
<title>Distribution Header</title>
<p>
- <marker id="distribution_header"/>
- As from ERTS 5.7.2 the old atom cache protocol was
- dropped and a new one was introduced. This protocol
- introduced the distribution header. Nodes with an ERTS version
- earlier than 5.7.2 can still communicate with new nodes,
- but no distribution header and no atom cache are used.</p>
- <p>
The distribution header only contains an atom cache
reference section, but can in the future contain more
information. The distribution header precedes one or more Erlang
diff --git a/erts/doc/src/erl_nif.xml b/erts/doc/src/erl_nif.xml
index 34042cb4de..cc7452bab5 100644
--- a/erts/doc/src/erl_nif.xml
+++ b/erts/doc/src/erl_nif.xml
@@ -783,14 +783,16 @@ typedef struct {
<p>A process identifier (pid). In contrast to pid terms (instances of
<c>ERL_NIF_TERM</c>), <c>ErlNifPid</c>s are self-contained and not
bound to any <seealso marker="#ErlNifEnv">environment</seealso>.
- <c>ErlNifPid</c> is an opaque type.</p>
+ <c>ErlNifPid</c> is an opaque type. It can be copied, moved
+ in memory, forgotten, and so on.</p>
</item>
<tag><marker id="ErlNifPort"/><c>ErlNifPort</c></tag>
<item>
<p>A port identifier. In contrast to port ID terms (instances of
<c>ERL_NIF_TERM</c>), <c>ErlNifPort</c>s are self-contained and not
bound to any <seealso marker="#ErlNifEnv">environment</seealso>.
- <c>ErlNifPort</c> is an opaque type.</p>
+ <c>ErlNifPort</c> is an opaque type. It can be copied, moved
+ in memory, forgotten, and so on.</p>
</item>
<tag><marker id="ErlNifResourceType"/><c>ErlNifResourceType</c></tag>
<item>
@@ -1088,6 +1090,20 @@ typedef struct {
</func>
<func>
+ <name since="OTP @OTP-15011@"><ret>int</ret>
+ <nametext>enif_compare_pids(const ErlNifPid *pid1, const ErlNifPid *pid2)
+ </nametext></name>
+ <fsummary>Compare two pids.</fsummary>
+ <desc>
+ <p>Compares two <seealso marker="#ErlNifPid"><c>ErlNifPid</c>
+ </seealso>s according to term order.</p>
+ <p>Returns <c>0</c> if <c>pid1</c> and <c>pid2</c> are equal,
+ &lt; <c>0</c> if <c>pid1</c> &lt; <c>pid2</c>, and
+ &gt; <c>0</c> if <c>pid1</c> &gt; <c>pid2</c>.</p>
+ </desc>
+ </func>
+
+ <func>
<name since="OTP R13B04"><ret>void</ret>
<nametext>enif_cond_broadcast(ErlNifCond *cnd)</nametext></name>
<fsummary></fsummary>
@@ -1392,6 +1408,9 @@ enif_free_iovec(iovec);]]></code>
initializes the pid variable <c>*pid</c> from it and returns
<c>true</c>. Otherwise returns <c>false</c>. No check is done to see
if the process is alive.</p>
+ <note><p><c>enif_get_local_pid</c> will return false if argument
+ <c>term</c> is the atom <seealso marker="#enif_make_pid">
+ <c>undefined</c></seealso>.</p></note>
</desc>
</func>
@@ -1871,6 +1890,17 @@ enif_inspect_iovec(env, max_elements, term, &amp;tail, &amp;iovec);
</func>
<func>
+ <name since="OTP @OTP-15011@"><ret>int</ret>
+ <nametext>enif_is_pid_undefined(const ErlNifPid* pid)</nametext></name>
+ <fsummary>Determine if pid is undefined.</fsummary>
+ <desc>
+ <p>Returns <c>true</c> if <c>pid</c> has been set as undefined by
+ <seealso marker="#enif_set_pid_undefined"><c>enif_set_pid_undefined</c>
+ </seealso>.</p>
+ </desc>
+ </func>
+
+ <func>
<name since="OTP R13B04"><ret>int</ret>
<nametext>enif_is_port(ErlNifEnv* env, ERL_NIF_TERM term)</nametext>
</name>
@@ -2217,6 +2247,18 @@ enif_inspect_iovec(env, max_elements, term, &amp;tail, &amp;iovec);
</func>
<func>
+ <name since="OTP @OTP-15362@"><ret>ERL_NIF_TERM</ret>
+ <nametext>enif_make_monitor_term(ErlNifEnv* env, const ErlNifMonitor* mon)</nametext></name>
+ <fsummary>Make monitor term from the given monitor identifier.</fsummary>
+ <desc>
+ <p>Creates a term identifying the given monitor received from
+ <seealso marker="#enif_monitor_process"><c>enif_monitor_process</c>
+ </seealso>.</p>
+ <p>This function is primarily intended for debugging purpose.</p>
+ </desc>
+ </func>
+
+ <func>
<name since="OTP R14B"><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>
@@ -2247,7 +2289,8 @@ enif_inspect_iovec(env, max_elements, term, &amp;tail, &amp;iovec);
</name>
<fsummary>Make a pid term.</fsummary>
<desc>
- <p>Makes a pid term from <c>*pid</c>.</p>
+ <p>Makes a pid term or the atom <seealso marker="#enif_set_pid_undefined">
+ <c>undefined</c></seealso> from <c>*pid</c>.</p>
</desc>
</func>
@@ -2613,7 +2656,9 @@ enif_map_iterator_destroy(env, &amp;iter);</code>
<p>Argument <c>caller_env</c> is the environment of the calling process
or callback. Must only be NULL if calling from a custom thread.</p>
<p>Returns <c>0</c> on success, &lt; 0 if no <c>down</c> callback is
- provided, and &gt; 0 if the process is no longer alive.</p>
+ provided, and &gt; 0 if the process is no longer alive or if
+ <c>target_pid</c> is <seealso marker="#enif_set_pid_undefined">
+ undefined</seealso>.</p>
<p>This function is only thread-safe when the emulator with SMP support
is used. It can only be used in a non-SMP emulator from a NIF-calling
thread.</p>
@@ -3068,7 +3113,9 @@ enif_map_iterator_destroy(env, &amp;iter);</code>
<seealso marker="#enif_select_write"><c>enif_select_write</c></seealso>
introduced in erts-11.0 (OTP-22.0).</p>
</note>
- <p>Argument <c>pid</c> may be <c>NULL</c> to indicate the calling process.</p>
+ <p>Argument <c>pid</c> may be <c>NULL</c> to indicate the calling
+ process. It must not be set as <seealso marker="#enif_set_pid_undefined">
+ undefined</seealso>.</p>
<p>Argument <c>obj</c> is a resource object obtained from
<seealso marker="#enif_alloc_resource"><c>enif_alloc_resource</c></seealso>.
The purpose of the resource objects is as a container of the event object
@@ -3079,16 +3126,18 @@ enif_map_iterator_destroy(env, &amp;iter);</code>
or the atom <c>undefined</c>. It will be passed as <c>Ref</c> in the notifications.
If a selective <c>receive</c> statement is used to wait for the notification
then a reference created just before the <c>receive</c> will exploit a runtime
- optimization that bypasses all earlier received messages in the queue.</p>
+ optimization that bypasses all earlier received messages in the
+ queue.</p>
<p>The notifications are one-shot only. To receive further notifications of the same
type (read or write), repeated calls to <c>enif_select</c> must be made
after receiving each notification.</p>
<p><c>ERL_NIF_SELECT_CANCEL</c> can be used to cancel previously
selected events. It must be used in a bitwise OR combination with
<c>ERL_NIF_SELECT_READ</c> and/or <c>ERL_NIF_SELECT_WRITE</c> to
- indicate which type of event to cancel. The return value will
- tell if the event was actualy cancelled or if a notification may
- already have been sent.</p>
+ indicate which type of event to cancel. Arguments <c>pid</c> and
+ <c>ref</c> are ignored when <c>ERL_NIF_SELECT_CANCEL</c> is specified.
+ The return value will tell if the event was actualy cancelled or if a
+ notification may already have been sent.</p>
<p>Use <c>ERL_NIF_SELECT_STOP</c> as <c>mode</c> in order to safely
close an event object that has been passed to <c>enif_select</c>. The
<seealso marker="#ErlNifResourceStop"><c>stop</c></seealso> callback
@@ -3097,7 +3146,9 @@ enif_map_iterator_destroy(env, &amp;iter);</code>
even if all notifications have been received (or cancelled) and no
further calls to <c>enif_select</c> have been made.
<c>ERL_NIF_SELECT_STOP</c> will first cancel any selected events
- before it calls or schedules the <c>stop</c> callback.</p>
+ before it calls or schedules the <c>stop</c> callback. Arguments
+ <c>pid</c> and <c>ref</c> are ignored when <c>ERL_NIF_SELECT_STOP</c>
+ is specified.</p>
<p>The first call to <c>enif_select</c> for a specific OS <c>event</c> will establish
a relation between the event object and the containing resource. All subsequent calls
for an <c>event</c> must pass its containing resource as argument
@@ -3180,7 +3231,11 @@ if (retval &amp; ERL_NIF_SELECT_STOP_CALLED) {
<c>enif_alloc_env</c></seealso>. If argument <c>msg_env</c> is
<c>NULL</c> the term <c>msg</c> will be copied, otherwise both
<c>msg</c> and <c>msg_env</c> will be invalidated by a successful call
- to <c>enif_select_read</c> or <c>enif_select_write</c>.</p>
+ to <c>enif_select_read</c> or <c>enif_select_write</c>. The environment
+ is then to either be freed with <seealso marker="#enif_free_env">
+ <c>enif_free_env</c></seealso> or cleared for reuse with
+ <seealso marker="#enif_clear_env"><c>enif_clear_env</c></seealso>. An
+ unsuccessful call will leave <c>msg</c> and <c>msg_env</c> still valid.</p>
<p>Apart from the message format <c>enif_select_read</c> and
<c>enif_select_write</c> behaves exactly the same as <seealso
marker="#enif_select">enif_select</seealso> with argument <c>mode</c> as
@@ -3234,8 +3289,9 @@ if (retval &amp; ERL_NIF_SELECT_STOP_CALLED) {
<c>msg</c>) is invalidated by a successful call to <c>enif_send</c>.
The environment is to either be freed with
<seealso marker="#enif_free_env">
- <c>enif_free_env</c></seealso> of cleared for reuse with
- <seealso marker="#enif_clear_env"><c>enif_clear_env</c></seealso>.</p>
+ <c>enif_free_env</c></seealso> or cleared for reuse with
+ <seealso marker="#enif_clear_env"><c>enif_clear_env</c></seealso>. An
+ unsuccessful call will leave <c>msg</c> and <c>msg_env</c> still valid.</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 environment is still valid after
the call.</p>
@@ -3250,6 +3306,17 @@ if (retval &amp; ERL_NIF_SELECT_STOP_CALLED) {
</func>
<func>
+ <name since="OTP @OTP-15011@"><ret>void</ret>
+ <nametext>enif_set_pid_undefined(ErlNifPid* pid)</nametext></name>
+ <fsummary>Set pid as undefined.</fsummary>
+ <desc>
+ <p>Sets an <seealso marker="#ErlNifPid"><c>ErlNifPid</c></seealso>
+ variable as undefined. See <seealso marker="#enif_is_pid_undefined">
+ <c>enif_is_pid_undefined</c></seealso>.</p>
+ </desc>
+ </func>
+
+ <func>
<name since="OTP R13B04"><ret>unsigned</ret>
<nametext>enif_sizeof_resource(void* obj)</nametext></name>
<fsummary>Get the byte size of a resource object.</fsummary>
diff --git a/erts/doc/src/notes.xml b/erts/doc/src/notes.xml
index 2c23456ff1..3473a12526 100644
--- a/erts/doc/src/notes.xml
+++ b/erts/doc/src/notes.xml
@@ -31,6 +31,24 @@
</header>
<p>This document describes the changes made to the ERTS application.</p>
+<section><title>Erts 10.2.4</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ When using the <c>{linger,{true,T}}</c> option;
+ <c>gen_tcp:listen/2</c> used the full linger time before
+ returning for example <c>eaddrinuse</c>. This bug has now
+ been corrected.</p>
+ <p>
+ Own Id: OTP-14728 Aux Id: ERIERL-303 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Erts 10.2.3</title>
<section><title>Fixed Bugs and Malfunctions</title>
@@ -1694,6 +1712,26 @@
</section>
+<section><title>Erts 9.3.3.9</title>
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>Added an optional <c>./configure</c> flag to compile
+ the emulator with spectre mitigation:
+ <c>--with-spectre-mitigation</c></p>
+ <p>Note that this requires a recent version of GCC with
+ support for spectre mitigation and the
+ <c>--mindirect-branch=thunk</c> flag, such as
+ <c>8.1</c>.</p>
+ <p>
+ Own Id: OTP-15430 Aux Id: ERIERL-237 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Erts 9.3.3.8</title>
<section><title>Fixed Bugs and Malfunctions</title>
diff --git a/erts/doc/src/persistent_term.xml b/erts/doc/src/persistent_term.xml
index 1eda7f8d76..9d3c9afd80 100644
--- a/erts/doc/src/persistent_term.xml
+++ b/erts/doc/src/persistent_term.xml
@@ -256,6 +256,22 @@ will be slower as the number of persistent terms increases.</pre>
</func>
<func>
+ <name name="get" arity="2" since="OTP 21.3"/>
+ <fsummary>Get the value for a persistent term.</fsummary>
+ <desc>
+ <p>Retrieve the value for the persistent term associated with
+ the key <c><anno>Key</anno></c>. The lookup will be made in
+ constant time and the value will not be copied to the heap
+ of the calling process.</p>
+ <p>This function returns <c><anno>Default</anno></c> if no
+ term has been stored with the key <c><anno>Key</anno></c>.</p>
+ <p>If the calling process holds on to the value of the
+ persistent term and the persistent term is deleted in the future,
+ the term will be copied to the process.</p>
+ </desc>
+ </func>
+
+ <func>
<name name="info" arity="0" since="OTP 21.2"/>
<fsummary>Get information about persistent terms.</fsummary>
<desc>
diff --git a/erts/emulator/beam/beam_emu.c b/erts/emulator/beam/beam_emu.c
index 4ef06464f4..90162a6543 100644
--- a/erts/emulator/beam/beam_emu.c
+++ b/erts/emulator/beam/beam_emu.c
@@ -885,19 +885,22 @@ void process_main(Eterm * x_reg_array, FloatDef* f_reg_array)
#include "beam_warm.h"
OpCase(normal_exit): {
- SWAPOUT;
+ HEAVY_SWAPOUT;
c_p->freason = EXC_NORMAL;
c_p->arity = 0; /* In case this process will ever be garbed again. */
ERTS_UNREQ_PROC_MAIN_LOCK(c_p);
erts_do_exit_process(c_p, am_normal);
ERTS_REQ_PROC_MAIN_LOCK(c_p);
+ HEAVY_SWAPIN;
goto do_schedule;
}
OpCase(continue_exit): {
+ HEAVY_SWAPOUT;
ERTS_UNREQ_PROC_MAIN_LOCK(c_p);
erts_continue_exit_process(c_p);
ERTS_REQ_PROC_MAIN_LOCK(c_p);
+ HEAVY_SWAPIN;
goto do_schedule;
}
diff --git a/erts/emulator/beam/beam_load.c b/erts/emulator/beam/beam_load.c
index 7ff55e8927..e9e294cd59 100644
--- a/erts/emulator/beam/beam_load.c
+++ b/erts/emulator/beam/beam_load.c
@@ -6108,7 +6108,8 @@ erts_release_literal_area(ErtsLiteralArea* literal_area)
}
default:
ASSERT(is_external_header(oh->thing_word));
- erts_deref_node_entry(((ExternalThing*)oh)->node);
+ erts_deref_node_entry(((ExternalThing*)oh)->node,
+ make_boxed(&oh->thing_word));
}
oh = oh->next;
}
diff --git a/erts/emulator/beam/bif.c b/erts/emulator/beam/bif.c
index ff7db0e742..a4c7af9a24 100644
--- a/erts/emulator/beam/bif.c
+++ b/erts/emulator/beam/bif.c
@@ -184,7 +184,7 @@ BIF_RETTYPE link_1(BIF_ALIST_1)
DistEntry *dep;
ErtsLink *lnk;
int code;
- ErtsDSigData dsd;
+ ErtsDSigSendContext ctx;
dep = external_pid_dist_entry(BIF_ARG_1);
if (dep == erts_this_dist_entry)
@@ -201,9 +201,9 @@ BIF_RETTYPE link_1(BIF_ALIST_1)
ldp = erts_link_to_data(lnk);
- code = erts_dsig_prepare(&dsd, dep, BIF_P,
+ code = erts_dsig_prepare(&ctx, dep, BIF_P,
ERTS_PROC_LOCK_MAIN,
- ERTS_DSP_RLOCK, 0, 1);
+ ERTS_DSP_RLOCK, 0, 1, 1);
switch (code) {
case ERTS_DSIG_PREP_NOT_ALIVE:
case ERTS_DSIG_PREP_NOT_CONNECTED:
@@ -222,9 +222,10 @@ BIF_RETTYPE link_1(BIF_ALIST_1)
ASSERT(inserted); (void)inserted;
erts_de_runlock(dep);
- code = erts_dsig_send_link(&dsd, BIF_P->common.id, BIF_ARG_1);
+ code = erts_dsig_send_link(&ctx, BIF_P->common.id, BIF_ARG_1);
if (code == ERTS_DSIG_SEND_YIELD)
ERTS_BIF_YIELD_RETURN(BIF_P, am_true);
+ ASSERT(code == ERTS_DSIG_SEND_OK);
BIF_RET(am_true);
break;
}
@@ -306,7 +307,7 @@ demonitor(Process *c_p, Eterm ref, Eterm *multip)
DistEntry *dep;
int code = ERTS_DSIG_SEND_OK;
int deleted;
- ErtsDSigData dsd;
+ ErtsDSigSendContext ctx;
ASSERT(is_external_pid(to) || is_node_name_atom(to));
@@ -322,8 +323,8 @@ demonitor(Process *c_p, Eterm ref, Eterm *multip)
}
}
- code = erts_dsig_prepare(&dsd, dep, c_p, ERTS_PROC_LOCK_MAIN,
- ERTS_DSP_RLOCK, 0, 0);
+ code = erts_dsig_prepare(&ctx, dep, c_p, ERTS_PROC_LOCK_MAIN,
+ ERTS_DSP_RLOCK, 0, 1, 0);
deleted = erts_monitor_dist_delete(&mdp->target);
@@ -352,8 +353,8 @@ demonitor(Process *c_p, Eterm ref, Eterm *multip)
* 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);
+ code = erts_dsig_send_demonitor(&ctx, c_p->common.id,
+ watched, mdp->ref);
break;
}
@@ -533,7 +534,7 @@ BIF_RETTYPE monitor_2(BIF_ALIST_2)
}
if (is_external_pid(target)) {
- ErtsDSigData dsd;
+ ErtsDSigSendContext ctx;
int code;
dep = external_pid_dist_entry(target);
@@ -551,9 +552,9 @@ BIF_RETTYPE monitor_2(BIF_ALIST_2)
BIF_P->common.id, id, name);
erts_monitor_tree_insert(&ERTS_P_MONITORS(BIF_P), &mdp->origin);
- code = erts_dsig_prepare(&dsd, dep,
+ code = erts_dsig_prepare(&ctx, dep,
BIF_P, ERTS_PROC_LOCK_MAIN,
- ERTS_DSP_RLOCK, 0, 1);
+ ERTS_DSP_RLOCK, 0, 1, 1);
switch (code) {
case ERTS_DSIG_PREP_NOT_ALIVE:
case ERTS_DSIG_PREP_NOT_CONNECTED:
@@ -568,7 +569,7 @@ BIF_RETTYPE monitor_2(BIF_ALIST_2)
ASSERT(inserted); (void)inserted;
erts_de_runlock(dep);
- code = erts_dsig_send_monitor(&dsd, BIF_P->common.id, target, ref);
+ code = erts_dsig_send_monitor(&ctx, BIF_P->common.id, target, ref);
break;
}
@@ -914,7 +915,7 @@ BIF_RETTYPE unlink_1(BIF_ALIST_1)
ErtsLinkData *ldp;
DistEntry *dep;
int code;
- ErtsDSigData dsd;
+ ErtsDSigSendContext ctx;
dep = external_pid_dist_entry(BIF_ARG_1);
if (dep == erts_this_dist_entry)
@@ -932,15 +933,15 @@ BIF_RETTYPE unlink_1(BIF_ALIST_1)
else
erts_link_release(lnk);
- code = erts_dsig_prepare(&dsd, dep, BIF_P, ERTS_PROC_LOCK_MAIN,
- ERTS_DSP_NO_LOCK, 0, 0);
+ code = erts_dsig_prepare(&ctx, dep, BIF_P, ERTS_PROC_LOCK_MAIN,
+ ERTS_DSP_NO_LOCK, 0, 1, 0);
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_unlink(&dsd, BIF_P->common.id, BIF_ARG_1);
+ code = erts_dsig_send_unlink(&ctx, BIF_P->common.id, BIF_ARG_1);
if (code == ERTS_DSIG_SEND_YIELD)
ERTS_BIF_YIELD_RETURN(BIF_P, am_true);
break;
@@ -1301,10 +1302,11 @@ static BIF_RETTYPE send_exit_signal_bif(Process *c_p, Eterm id, Eterm reason, in
ERTS_BIF_PREP_RET(ret_val, am_true); /* Old incarnation of this node... */
else {
int code;
- ErtsDSigData dsd;
+ ErtsDSigSendContext ctx;
+
+ code = erts_dsig_prepare(&ctx, dep, c_p, ERTS_PROC_LOCK_MAIN,
+ ERTS_DSP_NO_LOCK, 0, 0, 1);
- 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:
@@ -1312,11 +1314,29 @@ static BIF_RETTYPE send_exit_signal_bif(Process *c_p, Eterm id, Eterm reason, in
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)
+ code = erts_dsig_send_exit2(&ctx, c_p->common.id, id, reason);
+ switch (code) {
+ case ERTS_DSIG_SEND_YIELD:
ERTS_BIF_PREP_YIELD_RETURN(ret_val, c_p, am_true);
- else
+ break;
+ case ERTS_DSIG_SEND_CONTINUE:
+ BUMP_ALL_REDS(c_p);
+ erts_set_gc_state(c_p, 0);
+ ERTS_BIF_PREP_TRAP1(ret_val, &dsend_continue_trap_export, c_p,
+ erts_dsend_export_trap_context(c_p, &ctx));
+ break;
+ case ERTS_DSIG_SEND_OK:
ERTS_BIF_PREP_RET(ret_val, am_true);
+ break;
+ case ERTS_DSIG_SEND_TOO_LRG:
+ erts_set_gc_state(c_p, 1);
+ ERTS_BIF_PREP_ERROR(ret_val, c_p, SYSTEM_LIMIT);
+ break;
+ default:
+ ASSERT(! "Invalid dsig send exit2 result");
+ ERTS_BIF_PREP_ERROR(ret_val, c_p, EXC_INTERNAL_ERROR);
+ break;
+ }
break;
default:
ASSERT(! "Invalid dsig prepare result");
@@ -1800,33 +1820,36 @@ ebif_bang_2(BIF_ALIST_2)
static Sint remote_send(Process *p, DistEntry *dep,
- Eterm to, Eterm full_to, Eterm msg,
- ErtsSendContext* ctx)
+ Eterm to, Eterm node, Eterm full_to, Eterm msg,
+ Eterm return_term, Eterm *ctxpp,
+ int connect, int suspend)
{
Sint res;
int code;
+ ErtsDSigSendContext ctx;
ASSERT(is_atom(to) || is_external_pid(to));
- ctx->dep = dep;
- code = erts_dsig_prepare(&ctx->dsd, dep, p, ERTS_PROC_LOCK_MAIN,
+ code = erts_dsig_prepare(&ctx, dep, p, ERTS_PROC_LOCK_MAIN,
ERTS_DSP_NO_LOCK,
- !ctx->suspend, ctx->connect);
+ !suspend, 0, connect);
+ ctx.return_term = return_term;
+ ctx.node = node;
switch (code) {
case ERTS_DSIG_PREP_NOT_ALIVE:
case ERTS_DSIG_PREP_NOT_CONNECTED:
res = SEND_NOCONNECT;
break;
case ERTS_DSIG_PREP_WOULD_SUSPEND:
- ASSERT(!ctx->suspend);
+ ASSERT(!suspend);
res = SEND_YIELD;
break;
case ERTS_DSIG_PREP_PENDING:
case ERTS_DSIG_PREP_CONNECTED: {
if (is_atom(to))
- code = erts_dsig_send_reg_msg(to, msg, ctx);
+ code = erts_dsig_send_reg_msg(&ctx, to, msg);
else
- code = erts_dsig_send_msg(to, msg, ctx);
+ code = erts_dsig_send_msg(&ctx, to, msg);
/*
* Note that reductions have been bumped on calling
* process by erts_dsig_send_reg_msg() or
@@ -1834,9 +1857,19 @@ static Sint remote_send(Process *p, DistEntry *dep,
*/
if (code == ERTS_DSIG_SEND_YIELD)
res = SEND_YIELD_RETURN;
- else if (code == ERTS_DSIG_SEND_CONTINUE)
+ else if (code == ERTS_DSIG_SEND_CONTINUE) {
+ erts_set_gc_state(p, 0);
+
+ /* Keep a reference to the dist entry if the
+ name is an not a pid. */
+ if (is_atom(to)) {
+ erts_ref_dist_entry(ctx.dep);
+ ctx.deref_dep = 1;
+ }
+
+ *ctxpp = erts_dsend_export_trap_context(p, &ctx);
res = SEND_YIELD_CONTINUE;
- else if (code == ERTS_DSIG_SEND_TOO_LRG)
+ } else if (code == ERTS_DSIG_SEND_TOO_LRG)
res = SEND_SYSTEM_LIMIT;
else
res = 0;
@@ -1858,7 +1891,8 @@ static Sint remote_send(Process *p, DistEntry *dep,
}
static Sint
-do_send(Process *p, Eterm to, Eterm msg, Eterm *refp, ErtsSendContext *ctx)
+do_send(Process *p, Eterm to, Eterm msg, Eterm return_term, Eterm *refp,
+ Eterm *dist_ctx, int connect, int suspend)
{
Eterm portid;
Port *pt;
@@ -1890,7 +1924,8 @@ do_send(Process *p, Eterm to, Eterm msg, Eterm *refp, ErtsSendContext *ctx)
erts_send_error_to_logger(p->group_leader, dsbufp);
return 0;
}
- return remote_send(p, dep, to, to, msg, ctx);
+ return remote_send(p, dep, to, dep->sysname, to, msg, return_term,
+ dist_ctx, connect, suspend);
} else if (is_atom(to)) {
Eterm id = erts_whereis_name_to_id(p, to);
@@ -1945,7 +1980,7 @@ do_send(Process *p, Eterm to, Eterm msg, Eterm *refp, ErtsSendContext *ctx)
ret_val = 0;
if (pt) {
- int ps_flags = ctx->suspend ? 0 : ERTS_PORT_SIG_FLG_NOSUSPEND;
+ int ps_flags = suspend ? 0 : ERTS_PORT_SIG_FLG_NOSUSPEND;
*refp = NIL;
if (IS_TRACED_FL(p, F_TRACE_SEND)) /* trace once only !! */
@@ -1960,12 +1995,12 @@ 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_BUSY:
/* Nothing has been sent */
- if (ctx->suspend)
+ if (suspend)
erts_suspend(p, ERTS_PROC_LOCK_MAIN, pt);
return SEND_YIELD;
case ERTS_PORT_OP_BUSY_SCHEDULED:
/* Message was sent */
- if (ctx->suspend) {
+ if (suspend) {
erts_suspend(p, ERTS_PROC_LOCK_MAIN, pt);
ret_val = SEND_YIELD_RETURN;
break;
@@ -2036,13 +2071,10 @@ do_send(Process *p, Eterm to, Eterm msg, Eterm *refp, ErtsSendContext *ctx)
ASSERT(dep != erts_this_dist_entry);
deref_dep = 1;
}
- ctx->dsd.node = tp[2];
- ret = remote_send(p, dep, tp[1], to, msg, ctx);
- if (ret == SEND_YIELD_CONTINUE) {
- erts_ref_dist_entry(ctx->dep);
- ctx->deref_dep = 1;
- }
+ ret = remote_send(p, dep, tp[1], tp[2], to, msg, return_term,
+ dist_ctx, connect, suspend);
+
if (deref_dep)
erts_deref_dist_entry(dep);
return ret;
@@ -2081,25 +2113,16 @@ BIF_RETTYPE send_3(BIF_ALIST_3)
Eterm l = opts;
Sint result;
-
- DeclareTypedTmpHeap(ErtsSendContext, ctx, BIF_P);
+ int connect = 1, suspend = 1;
+ Eterm ctx;
ERTS_MSACC_PUSH_STATE_M_X();
- UseTmpHeap(sizeof(ErtsSendContext)/sizeof(Eterm), BIF_P);
-
- ctx->suspend = !0;
- ctx->connect = !0;
- ctx->deref_dep = 0;
- ctx->return_term = am_ok;
- ctx->dss.reds = (Sint) (ERTS_BIF_REDS_LEFT(p) * TERM_TO_BINARY_LOOP_FACTOR);
- ctx->dss.phase = ERTS_DSIG_SEND_PHASE_INIT;
-
while (is_list(l)) {
if (CAR(list_val(l)) == am_noconnect) {
- ctx->connect = 0;
+ connect = 0;
} else if (CAR(list_val(l)) == am_nosuspend) {
- ctx->suspend = 0;
+ suspend = 0;
} else {
ERTS_BIF_PREP_ERROR(retval, p, BADARG);
goto done;
@@ -2116,7 +2139,7 @@ BIF_RETTYPE send_3(BIF_ALIST_3)
#endif
ERTS_MSACC_SET_STATE_CACHED_M_X(ERTS_MSACC_STATE_SEND);
- result = do_send(p, to, msg, &ref, ctx);
+ result = do_send(p, to, msg, am_ok, &ref, &ctx, connect, suspend);
ERTS_MSACC_POP_STATE_M_X();
if (result >= 0) {
@@ -2129,22 +2152,21 @@ BIF_RETTYPE send_3(BIF_ALIST_3)
switch (result) {
case SEND_NOCONNECT:
- if (ctx->connect) {
+ if (connect) {
ERTS_BIF_PREP_RET(retval, am_ok);
} else {
ERTS_BIF_PREP_RET(retval, am_noconnect);
}
break;
case SEND_YIELD:
- if (ctx->suspend) {
- ERTS_BIF_PREP_YIELD3(retval,
- bif_export[BIF_send_3], p, to, msg, opts);
+ if (suspend) {
+ ERTS_BIF_PREP_YIELD3(retval, bif_export[BIF_send_3], p, to, msg, opts);
} else {
ERTS_BIF_PREP_RET(retval, am_nosuspend);
}
break;
case SEND_YIELD_RETURN:
- if (!ctx->suspend) {
+ if (!suspend) {
ERTS_BIF_PREP_RET(retval, am_nosuspend);
break;
}
@@ -2169,9 +2191,7 @@ BIF_RETTYPE send_3(BIF_ALIST_3)
break;
case SEND_YIELD_CONTINUE:
BUMP_ALL_REDS(p);
- erts_set_gc_state(p, 0);
- ERTS_BIF_PREP_TRAP1(retval, &dsend_continue_trap_export, p,
- erts_dsend_export_trap_context(p, ctx));
+ ERTS_BIF_PREP_TRAP1(retval, &dsend_continue_trap_export, p, ctx);
break;
default:
erts_exit(ERTS_ABORT_EXIT, "send_3 invalid result %d\n", (int)result);
@@ -2179,7 +2199,6 @@ BIF_RETTYPE send_3(BIF_ALIST_3)
}
done:
- UnUseTmpHeap(sizeof(ErtsSendContext)/sizeof(Eterm), BIF_P);
return retval;
}
@@ -2193,14 +2212,14 @@ BIF_RETTYPE send_2(BIF_ALIST_2)
static BIF_RETTYPE dsend_continue_trap_1(BIF_ALIST_1)
{
Binary* bin = erts_magic_ref2bin(BIF_ARG_1);
- ErtsSendContext* ctx = (ErtsSendContext*) ERTS_MAGIC_BIN_DATA(bin);
+ ErtsDSigSendContext *ctx = (ErtsDSigSendContext*) ERTS_MAGIC_BIN_DATA(bin);
Sint initial_reds = (Sint) (ERTS_BIF_REDS_LEFT(BIF_P) * TERM_TO_BINARY_LOOP_FACTOR);
int result;
ASSERT(ERTS_MAGIC_BIN_DESTRUCTOR(bin) == erts_dsend_context_dtor);
- ctx->dss.reds = initial_reds;
- result = erts_dsig_send(&ctx->dsd, &ctx->dss);
+ ctx->reds = initial_reds;
+ result = erts_dsig_send(ctx);
switch (result) {
case ERTS_DSIG_SEND_OK:
@@ -2209,7 +2228,7 @@ static BIF_RETTYPE dsend_continue_trap_1(BIF_ALIST_1)
break;
case ERTS_DSIG_SEND_YIELD: /*SEND_YIELD_RETURN*/
erts_set_gc_state(BIF_P, 1);
- if (!ctx->suspend)
+ if (ctx->no_suspend)
BIF_RET(am_nosuspend);
ERTS_BIF_YIELD_RETURN(BIF_P, ctx->return_term);
@@ -2234,20 +2253,14 @@ Eterm erl_send(Process *p, Eterm to, Eterm msg)
Eterm retval;
Eterm ref;
Sint result;
- DeclareTypedTmpHeap(ErtsSendContext, ctx, p);
+ Eterm ctx;
ERTS_MSACC_PUSH_AND_SET_STATE_M_X(ERTS_MSACC_STATE_SEND);
- UseTmpHeap(sizeof(ErtsSendContext)/sizeof(Eterm), p);
+
#ifdef DEBUG
ref = NIL;
#endif
- ctx->suspend = !0;
- ctx->connect = !0;
- ctx->deref_dep = 0;
- ctx->return_term = msg;
- ctx->dss.reds = (Sint) (ERTS_BIF_REDS_LEFT(p) * TERM_TO_BINARY_LOOP_FACTOR);
- ctx->dss.phase = ERTS_DSIG_SEND_PHASE_INIT;
- result = do_send(p, to, msg, &ref, ctx);
+ result = do_send(p, to, msg, msg, &ref, &ctx, 1, 1);
ERTS_MSACC_POP_STATE_M_X();
@@ -2289,9 +2302,7 @@ Eterm erl_send(Process *p, Eterm to, Eterm msg)
break;
case SEND_YIELD_CONTINUE:
BUMP_ALL_REDS(p);
- erts_set_gc_state(p, 0);
- ERTS_BIF_PREP_TRAP1(retval, &dsend_continue_trap_export, p,
- erts_dsend_export_trap_context(p, ctx));
+ ERTS_BIF_PREP_TRAP1(retval, &dsend_continue_trap_export, p, ctx);
break;
default:
erts_exit(ERTS_ABORT_EXIT, "invalid send result %d\n", (int)result);
@@ -2299,7 +2310,6 @@ Eterm erl_send(Process *p, Eterm to, Eterm msg)
}
done:
- UnUseTmpHeap(sizeof(ErtsSendContext)/sizeof(Eterm), p);
return retval;
}
@@ -4053,10 +4063,12 @@ BIF_RETTYPE list_to_pid_1(BIF_ALIST_1)
if (is_nil(dep->cid))
goto bad;
- enp = erts_find_or_insert_node(dep->sysname, dep->creation);
+ etp = (ExternalThing *) HAlloc(BIF_P, EXTERNAL_THING_HEAD_SIZE + 1);
+
+ enp = erts_find_or_insert_node(dep->sysname, dep->creation,
+ make_boxed(&etp->header));
ASSERT(enp != erts_this_node);
- etp = (ExternalThing *) HAlloc(BIF_P, EXTERNAL_THING_HEAD_SIZE + 1);
etp->header = make_external_pid_header(1);
etp->next = MSO(BIF_P).first;
etp->node = enp;
@@ -4120,10 +4132,11 @@ BIF_RETTYPE list_to_port_1(BIF_ALIST_1)
if (is_nil(dep->cid))
goto bad;
- enp = erts_find_or_insert_node(dep->sysname, dep->creation);
+ etp = (ExternalThing *) HAlloc(BIF_P, EXTERNAL_THING_HEAD_SIZE + 1);
+ enp = erts_find_or_insert_node(dep->sysname, dep->creation,
+ make_boxed(&etp->header));
ASSERT(enp != erts_this_node);
- etp = (ExternalThing *) HAlloc(BIF_P, EXTERNAL_THING_HEAD_SIZE + 1);
etp->header = make_external_port_header(1);
etp->next = MSO(BIF_P).first;
etp->node = enp;
@@ -4226,9 +4239,6 @@ BIF_RETTYPE list_to_ref_1(BIF_ALIST_1)
if (is_nil(dep->cid))
goto bad;
- enp = erts_find_or_insert_node(dep->sysname, dep->creation);
- ASSERT(enp != erts_this_node);
-
hsz = EXTERNAL_THING_HEAD_SIZE;
#if defined(ARCH_64)
hsz += n/2 + 1;
@@ -4237,6 +4247,11 @@ BIF_RETTYPE list_to_ref_1(BIF_ALIST_1)
#endif
etp = (ExternalThing *) HAlloc(BIF_P, hsz);
+
+ enp = erts_find_or_insert_node(dep->sysname, dep->creation,
+ make_boxed(&etp->header));
+ ASSERT(enp != erts_this_node);
+
etp->header = make_external_ref_header(n/2);
etp->next = BIF_P->off_heap.first;
etp->node = enp;
@@ -4356,21 +4371,21 @@ BIF_RETTYPE erts_internal_group_leader_2(BIF_ALIST_2)
if (is_external_pid(BIF_ARG_2)) {
DistEntry *dep;
int code;
- ErtsDSigData dsd;
+ ErtsDSigSendContext ctx;
dep = external_pid_dist_entry(BIF_ARG_2);
ERTS_ASSERT(dep);
if(dep == erts_this_dist_entry)
BIF_ERROR(BIF_P, BADARG);
- code = erts_dsig_prepare(&dsd, dep, BIF_P, ERTS_PROC_LOCK_MAIN,
- ERTS_DSP_NO_LOCK, 0, 1);
+ code = erts_dsig_prepare(&ctx, dep, BIF_P, ERTS_PROC_LOCK_MAIN,
+ ERTS_DSP_NO_LOCK, 0, 1, 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_group_leader(&dsd, BIF_ARG_1, BIF_ARG_2);
+ code = erts_dsig_send_group_leader(&ctx, BIF_ARG_1, BIF_ARG_2);
if (code == ERTS_DSIG_SEND_YIELD)
ERTS_BIF_YIELD_RETURN(BIF_P, am_true);
BIF_RET(am_true);
diff --git a/erts/emulator/beam/bif.tab b/erts/emulator/beam/bif.tab
index 18586b5ede..11941db8cd 100644
--- a/erts/emulator/beam/bif.tab
+++ b/erts/emulator/beam/bif.tab
@@ -737,3 +737,4 @@ bif erts_internal:spawn_system_process/3
bif erlang:integer_to_list/2
bif erlang:integer_to_binary/2
+bif persistent_term:get/2
diff --git a/erts/emulator/beam/big.h b/erts/emulator/beam/big.h
index da4dc84d10..ad19cce395 100644
--- a/erts/emulator/beam/big.h
+++ b/erts/emulator/beam/big.h
@@ -81,7 +81,11 @@ typedef Uint dsize_t; /* Vector size type */
* a Uint64 argument. Therefore, we must test the size of the argument
* to ensure that the cast does not discard the high-order 32 bits.
*/
-#define _IS_SSMALL32(x) (((Uint32) ((((x)) >> (SMALL_BITS-1)) + 1)) < 2)
+#if defined(ARCH_32)
+# define _IS_SSMALL32(x) (((Uint32) ((((x)) >> (SMALL_BITS-1)) + 1)) < 2)
+#else
+# define _IS_SSMALL32(x) (1)
+#endif
#define _IS_SSMALL64(x) (((Uint64) ((((x)) >> (SMALL_BITS-1)) + 1)) < 2)
#define IS_SSMALL(x) (sizeof(x) == sizeof(Uint32) ? _IS_SSMALL32(x) : _IS_SSMALL64(x))
diff --git a/erts/emulator/beam/break.c b/erts/emulator/beam/break.c
index 273b598562..27bf2187c2 100644
--- a/erts/emulator/beam/break.c
+++ b/erts/emulator/beam/break.c
@@ -129,7 +129,7 @@ typedef struct {
void *to_arg;
} PrintMonitorContext;
-static void doit_print_link(ErtsLink *lnk, void *vpcontext)
+static int doit_print_link(ErtsLink *lnk, void *vpcontext, Sint reds)
{
PrintMonitorContext *pcontext = vpcontext;
fmtfn_t to = pcontext->to;
@@ -141,10 +141,11 @@ static void doit_print_link(ErtsLink *lnk, void *vpcontext)
} else {
erts_print(to, to_arg, ", %T", lnk->other.item);
}
+ return 1;
}
-static void doit_print_monitor(ErtsMonitor *mon, void *vpcontext)
+static int doit_print_monitor(ErtsMonitor *mon, void *vpcontext, Sint reds)
{
ErtsMonitorData *mdp;
PrintMonitorContext *pcontext = vpcontext;
@@ -196,6 +197,7 @@ static void doit_print_monitor(ErtsMonitor *mon, void *vpcontext)
/* ignore other monitors... */
break;
}
+ return 1;
}
/* Display info about an individual Erlang process */
diff --git a/erts/emulator/beam/copy.c b/erts/emulator/beam/copy.c
index e7bd046e18..db74b06cc5 100644
--- a/erts/emulator/beam/copy.c
+++ b/erts/emulator/beam/copy.c
@@ -854,7 +854,7 @@ Eterm copy_struct_x(Eterm obj, Uint sz, Eterm** hpp, ErlOffHeap* off_heap, Uint
case EXTERNAL_REF_SUBTAG:
{
ExternalThing *etp = (ExternalThing *) objp;
- erts_refc_inc(&etp->node->refc, 2);
+ erts_ref_node_entry(etp->node, 2, make_boxed(htop));
}
L_off_heap_node_container_common:
{
@@ -1660,7 +1660,7 @@ Uint copy_shared_perform(Eterm obj, Uint size, erts_shcopy_t *info,
case EXTERNAL_REF_SUBTAG:
{
ExternalThing *etp = (ExternalThing *) ptr;
- erts_refc_inc(&etp->node->refc, 2);
+ erts_ref_node_entry(etp->node, 2, make_boxed(hp));
}
off_heap_node_container_common:
{
@@ -1866,7 +1866,7 @@ Eterm copy_shallow(Eterm* ERTS_RESTRICT ptr, Uint sz, Eterm** hpp,
case EXTERNAL_REF_SUBTAG:
{
ExternalThing* etp = (ExternalThing *) (tp-1);
- erts_refc_inc(&etp->node->refc, 2);
+ erts_ref_node_entry(etp->node, 2, make_boxed(hp-1));
}
off_heap_common:
{
diff --git a/erts/emulator/beam/dist.c b/erts/emulator/beam/dist.c
index 15642e1669..b50c8273b1 100644
--- a/erts/emulator/beam/dist.c
+++ b/erts/emulator/beam/dist.c
@@ -50,19 +50,26 @@
#define DIST_CTL_DEFAULT_SIZE 64
/* Turn this on to get printouts of all distribution messages
- * which go on the line
+ * which go on the line. Enabling this may make some testcases
+ * fail. Especially the broken dist testcases in distribution_SUITE.
*/
#if 0
#define ERTS_DIST_MSG_DBG
+FILE *dbg_file;
#endif
#if 0
+/* Enable this to print the dist debug messages to a file instead */
+#define ERTS_DIST_MSG_DBG_FILE "/tmp/dist_dbg.%d"
+#endif
+#if 0
+/* Enable this to print the raw bytes sent and received */
#define ERTS_RAW_DIST_MSG_DBG
#endif
#if defined(ERTS_DIST_MSG_DBG) || defined(ERTS_RAW_DIST_MSG_DBG)
static void bw(byte *buf, ErlDrvSizeT sz)
{
- bin_write(ERTS_PRINT_STDERR, NULL, buf, sz);
+ bin_write(ERTS_PRINT_FILE, dbg_file, buf, sz);
}
#endif
@@ -70,39 +77,93 @@ static void bw(byte *buf, ErlDrvSizeT sz)
static void
dist_msg_dbg(ErtsDistExternal *edep, char *what, byte *buf, int sz)
{
- ErtsHeapFactory factory;
- DeclareTmpHeapNoproc(ctl_default,DIST_CTL_DEFAULT_SIZE);
- Eterm* ctl = ctl_default;
- byte *extp = edep->extp;
+ byte *extp = edep->data->extp;
Eterm msg;
Sint ctl_len;
- Sint size = ctl_len = erts_decode_dist_ext_size(edep);
+ Sint size = ctl_len = erts_decode_dist_ext_size(edep, 0);
if (size < 0) {
- erts_fprintf(stderr,
+ erts_fprintf(dbg_file,
"DIST MSG DEBUG: erts_decode_dist_ext_size(%s) failed:\n",
what);
bw(buf, sz);
}
else {
- ErlHeapFragment *mbuf = new_message_buffer(size);
- erts_factory_static_init(&factory, ctl, ctl_len, &mbuf->off_heap);
- msg = erts_decode_dist_ext(&factory, edep);
+ ErtsHeapFactory factory;
+ ErtsMessage *mbuf = erts_factory_message_create(&factory, NULL, 0, ctl_len);
+ /* Set mbuf msg to NIL as erts_factory_undo will fail otherwise */
+ ERL_MESSAGE_TERM(mbuf) = NIL;
+ msg = erts_decode_dist_ext(&factory, edep, 0);
if (is_value(msg))
- erts_fprintf(stderr, " %s: %T\n", what, msg);
+ erts_fprintf(dbg_file, " %s: %.80T\n", what, msg);
else {
- erts_fprintf(stderr,
+ erts_fprintf(dbg_file,
"DIST MSG DEBUG: erts_decode_dist_ext(%s) failed:\n",
what);
bw(buf, sz);
}
- free_message_buffer(mbuf);
- edep->extp = extp;
+ erts_factory_undo(&factory);
+ edep->data->extp = extp;
}
}
+static char *erts_dop_to_string(enum dop dop) {
+ if (dop == DOP_LINK)
+ return "LINK";
+ if (dop == DOP_SEND)
+ return "SEND";
+ if (dop == DOP_EXIT)
+ return "EXIT";
+ if (dop == DOP_UNLINK)
+ return "UNLINK";
+ if (dop == DOP_REG_SEND)
+ return "REG_SEND";
+ if (dop == DOP_GROUP_LEADER)
+ return "GROUP_LEADER";
+ if (dop == DOP_EXIT2)
+ return "EXIT2";
+ if (dop == DOP_SEND_TT)
+ return "SEND_TT";
+ if (dop == DOP_EXIT_TT)
+ return "EXIT_TT";
+ if (dop == DOP_REG_SEND_TT)
+ return "REG_SEND_TT";
+ if (dop == DOP_EXIT2_TT)
+ return "EXIT2_TT";
+ if (dop == DOP_MONITOR_P)
+ return "MONITOR_P";
+ if (dop == DOP_DEMONITOR_P)
+ return "DEMONITOR_P";
+ if (dop == DOP_MONITOR_P_EXIT)
+ return "MONITOR_P_EXIT";
+ if (dop == DOP_SEND_SENDER)
+ return "SEND_SENDER";
+ if (dop == DOP_SEND_SENDER_TT)
+ return "SEND_SENDER_TT";
+ if (dop == DOP_PAYLOAD_EXIT)
+ return "PAYLOAD_EXIT";
+ if (dop == DOP_PAYLOAD_EXIT_TT)
+ return "PAYLOAD_EXIT_TT";
+ if (dop == DOP_PAYLOAD_EXIT2)
+ return "PAYLOAD_EXIT2";
+ if (dop == DOP_PAYLOAD_EXIT2_TT)
+ return "PAYLOAD_EXIT2_TT";
+ if (dop == DOP_PAYLOAD_MONITOR_P_EXIT)
+ return "PAYLOAD_MONITOR_P_EXIT";
+ ASSERT(0);
+ return "UNKNOWN";
+}
+
#endif
+#if defined(VALGRIND)
+#include <valgrind/valgrind.h>
+#include <valgrind/memcheck.h>
+# define PURIFY_MSG(msg) \
+ VALGRIND_PRINTF("%s, line %d: %s", __FILE__, __LINE__, msg)
+#else
+# define PURIFY_MSG(msg)
+#endif
int erts_is_alive; /* System must be blocked on change */
int erts_dist_buf_busy_limit;
@@ -116,12 +177,17 @@ static Export *dist_ctrl_put_data_trap;
/* forward declarations */
-static int dsig_send_ctl(ErtsDSigData* dsdp, Eterm ctl, int force_busy);
+static void erts_schedule_dist_command(Port *, DistEntry *);
+static int dsig_send_exit(ErtsDSigSendContext *ctx, Eterm ctl, Eterm msg);
+static int dsig_send_ctl(ErtsDSigSendContext *ctx, Eterm ctl);
static void send_nodes_mon_msgs(Process *, Eterm, Eterm, Eterm, Eterm);
static void init_nodes_monitors(void);
static Sint abort_connection(DistEntry* dep, Uint32 conn_id);
static ErtsDistOutputBuf* clear_de_out_queues(DistEntry*);
static void free_de_out_queues(DistEntry*, ErtsDistOutputBuf*);
+int erts_dist_seq_tree_foreach_delete_yielding(DistSeqNode **root,
+ void **vyspp,
+ Sint limit);
static erts_atomic_t no_caches;
static erts_atomic_t no_nodes;
@@ -142,7 +208,6 @@ delete_cache(ErtsAtomCache *cache)
}
}
-
static void
create_cache(DistEntry *dep)
{
@@ -185,32 +250,36 @@ get_suspended_on_de(DistEntry *dep, erts_aint32_t unset_qflgs)
}
}
-#define ERTS_MON_LNK_FIRE_LIMIT 100
+#define ERTS_MON_LNK_FIRE_REDS 40
-static void monitor_connection_down(ErtsMonitor *mon, void *unused)
+static int monitor_connection_down(ErtsMonitor *mon, void *unused, Sint reds)
{
if (erts_monitor_is_origin(mon))
erts_proc_sig_send_demonitor(mon);
else
erts_proc_sig_send_monitor_down(mon, am_noconnection);
+ return ERTS_MON_LNK_FIRE_REDS;
}
-static void link_connection_down(ErtsLink *lnk, void *vdist)
+static int link_connection_down(ErtsLink *lnk, void *vdist, Sint reds)
{
erts_proc_sig_send_link_exit(NULL, THE_NON_VALUE, lnk,
am_noconnection, NIL);
+ return ERTS_MON_LNK_FIRE_REDS;
}
typedef enum {
ERTS_CML_CLEANUP_STATE_LINKS,
ERTS_CML_CLEANUP_STATE_MONITORS,
ERTS_CML_CLEANUP_STATE_ONAME_MONITORS,
+ ERTS_CML_CLEANUP_STATE_SEQUENCES,
ERTS_CML_CLEANUP_STATE_NODE_MONITORS
-} ErtsConMonLnkCleaupState;
+} ErtsConMonLnkSeqCleanupState;
typedef struct {
- ErtsConMonLnkCleaupState state;
+ ErtsConMonLnkSeqCleanupState state;
ErtsMonLnkDist *dist;
+ DistSeqNode *seq;
void *yield_state;
int trigger_node_monitors;
Eterm nodename;
@@ -218,49 +287,58 @@ typedef struct {
Eterm reason;
ErlOffHeap oh;
Eterm heap[1];
-} ErtsConMonLnkCleanup;
+} ErtsConMonLnkSeqCleanup;
static void
-con_monitor_link_cleanup(void *vcmlcp)
+con_monitor_link_seq_cleanup(void *vcmlcp)
{
- ErtsConMonLnkCleanup *cmlcp = vcmlcp;
+ ErtsConMonLnkSeqCleanup *cmlcp = vcmlcp;
ErtsMonLnkDist *dist = cmlcp->dist;
ErtsSchedulerData *esdp;
- int yield;
+ int reds = CONTEXT_REDS;
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)
+ reds = erts_link_list_foreach_delete_yielding(&dist->links,
+ link_connection_down,
+ NULL, &cmlcp->yield_state,
+ reds);
+ if (reds <= 0)
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)
+ reds = erts_monitor_list_foreach_delete_yielding(&dist->monitors,
+ monitor_connection_down,
+ NULL, &cmlcp->yield_state,
+ reds);
+ if (reds <= 0)
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)
+ reds = erts_monitor_tree_foreach_delete_yielding(&dist->orig_name_monitors,
+ monitor_connection_down,
+ NULL, &cmlcp->yield_state,
+ reds);
+ if (reds <= 0)
break;
cmlcp->dist = NULL;
erts_mon_link_dist_dec_refc(dist);
ASSERT(!cmlcp->yield_state);
+ cmlcp->state = ERTS_CML_CLEANUP_STATE_SEQUENCES;
+ case ERTS_CML_CLEANUP_STATE_SEQUENCES:
+ reds = erts_dist_seq_tree_foreach_delete_yielding(&cmlcp->seq,
+ &cmlcp->yield_state,
+ reds);
+ if (reds <= 0)
+ break;
+
+ ASSERT(!cmlcp->yield_state);
cmlcp->state = ERTS_CML_CLEANUP_STATE_NODE_MONITORS;
case ERTS_CML_CLEANUP_STATE_NODE_MONITORS:
if (cmlcp->trigger_node_monitors) {
@@ -280,22 +358,23 @@ con_monitor_link_cleanup(void *vcmlcp)
esdp = erts_get_scheduler_data();
ASSERT(esdp && esdp->type == ERTS_SCHED_NORMAL);
erts_schedule_misc_aux_work((int) esdp->no,
- con_monitor_link_cleanup,
+ con_monitor_link_seq_cleanup,
(void *) cmlcp);
}
static void
-schedule_con_monitor_link_cleanup(ErtsMonLnkDist *dist,
- Eterm nodename,
- Eterm visability,
- Eterm reason)
+schedule_con_monitor_link_seq_cleanup(ErtsMonLnkDist *dist,
+ DistSeqNode *seq,
+ Eterm nodename,
+ Eterm visability,
+ Eterm reason)
{
- if (dist || is_value(nodename)) {
+ if (dist || is_value(nodename) || seq) {
ErtsSchedulerData *esdp;
- ErtsConMonLnkCleanup *cmlcp;
+ ErtsConMonLnkSeqCleanup *cmlcp;
Uint rsz, size;
- size = sizeof(ErtsConMonLnkCleanup);
+ size = sizeof(ErtsConMonLnkSeqCleanup);
if (is_non_value(reason) || is_immed(reason)) {
rsz = 0;
@@ -322,6 +401,8 @@ schedule_con_monitor_link_cleanup(ErtsMonLnkDist *dist,
erts_mtx_unlock(&dist->mtx);
}
+ cmlcp->seq = seq;
+
cmlcp->trigger_node_monitors = is_value(nodename);
cmlcp->nodename = nodename;
cmlcp->visability = visability;
@@ -335,13 +416,13 @@ schedule_con_monitor_link_cleanup(ErtsMonLnkDist *dist,
esdp = erts_get_scheduler_data();
ASSERT(esdp && esdp->type == ERTS_SCHED_NORMAL);
erts_schedule_misc_aux_work((int) esdp->no,
- con_monitor_link_cleanup,
+ con_monitor_link_seq_cleanup,
(void *) cmlcp);
}
}
/*
-** A full node name constists of a "n@h"
+** A full node name consists of a "n@h"
**
** n must be a valid node name: string of ([a-z][A-Z][0-9]_-)+
**
@@ -558,6 +639,7 @@ int erts_do_net_exits(DistEntry *dep, Eterm reason)
}
else { /* Call from distribution controller (port/process) */
ErtsMonLnkDist *mld;
+ DistSeqNode *sequences;
ErtsAtomCache *cache;
ErtsProcList *suspendees;
ErtsDistOutputBuf *obuf;
@@ -587,6 +669,9 @@ int erts_do_net_exits(DistEntry *dep, Eterm reason)
mld = dep->mld;
dep->mld = NULL;
+ sequences = dep->sequences;
+ dep->sequences = NULL;
+
nodename = dep->sysname;
flags = dep->flags;
@@ -610,14 +695,15 @@ int erts_do_net_exits(DistEntry *dep, Eterm reason)
erts_de_rwunlock(dep);
- schedule_con_monitor_link_cleanup(mld,
- nodename,
- (flags & DFLAG_PUBLISHED
- ? am_visible
- : am_hidden),
- (reason == am_normal
- ? am_connection_closed
- : reason));
+ schedule_con_monitor_link_seq_cleanup(mld,
+ sequences,
+ nodename,
+ (flags & DFLAG_PUBLISHED
+ ? am_visible
+ : am_hidden),
+ (reason == am_normal
+ ? am_connection_closed
+ : reason));
erts_resume_processes(suspendees);
@@ -651,6 +737,16 @@ void init_dist(void)
{
init_nodes_monitors();
+#ifdef ERTS_DIST_MSG_DBG_FILE
+ {
+ char buff[255];
+ sprintf(buff, ERTS_DIST_MSG_DBG_FILE, getpid());
+ dbg_file = fopen(buff,"w+");
+ }
+#elif defined (ERTS_DIST_MSG_DBG)
+ dbg_file = stderr;
+#endif
+
nodedown.reason = NIL;
nodedown.bp = NULL;
@@ -674,21 +770,79 @@ void init_dist(void)
}
}
-#define ErtsDistOutputBuf2Binary(OB) \
- ((Binary *) (((char *) (OB)) - offsetof(Binary, orig_bytes)))
+#define ErtsDistOutputBuf2Binary(OB) OB->bin
+
+#ifdef DEBUG
+
+struct obuf_list;
+struct obuf_list {
+ erts_refc_t refc;
+ struct obuf_list *next;
+ struct obuf_list *prev;
+};
+#define obuf_list_size sizeof(struct obuf_list)
+static struct obuf_list *erts_obuf_list = NULL;
+static erts_mtx_t erts_obuf_list_mtx;
+
+static void
+insert_obuf(struct obuf_list *obuf, erts_aint_t initial) {
+ erts_mtx_lock(&erts_obuf_list_mtx);
+ obuf->next = erts_obuf_list;
+ obuf->prev = NULL;
+ erts_refc_init(&obuf->refc, initial);
+ if (erts_obuf_list)
+ erts_obuf_list->prev = obuf;
+ erts_obuf_list = obuf;
+ erts_mtx_unlock(&erts_obuf_list_mtx);
+}
+
+static void
+remove_obuf(struct obuf_list *obuf) {
+ if (erts_refc_dectest(&obuf->refc, 0) == 0) {
+ erts_mtx_lock(&erts_obuf_list_mtx);
+ if (obuf->prev) {
+ obuf->prev->next = obuf->next;
+ } else {
+ erts_obuf_list = obuf->next;
+ }
+ if (obuf->next) obuf->next->prev = obuf->prev;
+ erts_mtx_unlock(&erts_obuf_list_mtx);
+ }
+}
+
+void check_obuf(void);
+void check_obuf(void) {
+ erts_mtx_lock(&erts_obuf_list_mtx);
+ ERTS_ASSERT(erts_obuf_list == NULL);
+ erts_mtx_unlock(&erts_obuf_list_mtx);
+}
+#else
+#define insert_obuf(...)
+#define remove_obuf(...)
+#define obuf_list_size 0
+#endif
static ERTS_INLINE ErtsDistOutputBuf *
-alloc_dist_obuf(Uint size)
+alloc_dist_obuf(Uint size, Uint headers)
{
+ int i;
ErtsDistOutputBuf *obuf;
- Uint obuf_size = sizeof(ErtsDistOutputBuf)+sizeof(byte)*(size-1);
+ Uint obuf_size = sizeof(ErtsDistOutputBuf)*(headers) +
+ sizeof(byte)*size + obuf_list_size;
Binary *bin = erts_bin_drv_alloc(obuf_size);
- obuf = (ErtsDistOutputBuf *) &bin->orig_bytes[0];
+ size += obuf_list_size;
+ obuf = (ErtsDistOutputBuf *) &bin->orig_bytes[size];
+ erts_refc_add(&bin->intern.refc, headers - 1, 1);
+ for (i = 0; i < headers; i++) {
+ obuf[i].bin = bin;
+ obuf[i].extp = (byte *)&bin->orig_bytes[0] + obuf_list_size;
#ifdef DEBUG
- obuf->dbg_pattern = ERTS_DIST_OUTPUT_BUF_DBG_PATTERN;
- obuf->alloc_endp = obuf->data + size;
- ASSERT(bin == ErtsDistOutputBuf2Binary(obuf));
+ obuf[i].dbg_pattern = ERTS_DIST_OUTPUT_BUF_DBG_PATTERN;
+ obuf[i].alloc_endp = obuf->extp + size;
+ ASSERT(bin == ErtsDistOutputBuf2Binary(obuf));
#endif
+ }
+ insert_obuf((struct obuf_list*)&bin->orig_bytes[0], headers);
return obuf;
}
@@ -697,14 +851,17 @@ free_dist_obuf(ErtsDistOutputBuf *obuf)
{
Binary *bin = ErtsDistOutputBuf2Binary(obuf);
ASSERT(obuf->dbg_pattern == ERTS_DIST_OUTPUT_BUF_DBG_PATTERN);
- erts_bin_release(bin);
+ remove_obuf((struct obuf_list*)&bin->orig_bytes[0]);
+ if (erts_refc_dectest(&bin->intern.refc, 0) == 0) {
+ erts_bin_free(bin);
+ }
}
static ERTS_INLINE Sint
size_obuf(ErtsDistOutputBuf *obuf)
{
- Binary *bin = ErtsDistOutputBuf2Binary(obuf);
- return bin->orig_size;
+ return sizeof(ErtsDistOutputBuf) + (obuf->ext_endp - obuf->ext_start)
+ + (obuf->hdr_endp - obuf->hdrp);
}
static ErtsDistOutputBuf* clear_de_out_queues(DistEntry* dep)
@@ -758,18 +915,20 @@ static void free_de_out_queues(DistEntry* dep, ErtsDistOutputBuf *obuf)
int erts_dsend_context_dtor(Binary* ctx_bin)
{
- ErtsSendContext* ctx = ERTS_MAGIC_BIN_DATA(ctx_bin);
- switch (ctx->dss.phase) {
+ ErtsDSigSendContext* ctx = ERTS_MAGIC_BIN_DATA(ctx_bin);
+ switch (ctx->phase) {
case ERTS_DSIG_SEND_PHASE_MSG_SIZE:
- DESTROY_SAVED_WSTACK(&ctx->dss.u.sc.wstack);
+ DESTROY_SAVED_WSTACK(&ctx->u.sc.wstack);
break;
case ERTS_DSIG_SEND_PHASE_MSG_ENCODE:
- DESTROY_SAVED_WSTACK(&ctx->dss.u.ec.wstack);
+ DESTROY_SAVED_WSTACK(&ctx->u.ec.wstack);
break;
default:;
}
- if (ctx->dss.phase >= ERTS_DSIG_SEND_PHASE_ALLOC && ctx->dss.obuf) {
- free_dist_obuf(ctx->dss.obuf);
+ if (ctx->phase >= ERTS_DSIG_SEND_PHASE_ALLOC && ctx->obuf) {
+ int i;
+ for (i = 0; i < ctx->fragments; i++)
+ free_dist_obuf(&ctx->obuf[i]);
}
if (ctx->deref_dep)
erts_deref_dist_entry(ctx->dep);
@@ -777,10 +936,10 @@ int erts_dsend_context_dtor(Binary* ctx_bin)
return 1;
}
-Eterm erts_dsend_export_trap_context(Process* p, ErtsSendContext* ctx)
+Eterm erts_dsend_export_trap_context(Process* p, ErtsDSigSendContext* ctx)
{
struct exported_ctx {
- ErtsSendContext ctx;
+ ErtsDSigSendContext ctx;
ErtsAtomCacheMap acm;
};
Binary* ctx_bin = erts_create_magic_binary(sizeof(struct exported_ctx),
@@ -788,12 +947,12 @@ Eterm erts_dsend_export_trap_context(Process* p, ErtsSendContext* ctx)
struct exported_ctx* dst = ERTS_MAGIC_BIN_DATA(ctx_bin);
Eterm* hp = HAlloc(p, ERTS_MAGIC_REF_THING_SIZE);
- sys_memcpy(&dst->ctx, ctx, sizeof(ErtsSendContext));
- ASSERT(ctx->dss.ctl == make_tuple(ctx->ctl_heap));
- dst->ctx.dss.ctl = make_tuple(dst->ctx.ctl_heap);
- if (ctx->dss.acmp) {
- sys_memcpy(&dst->acm, ctx->dss.acmp, sizeof(ErtsAtomCacheMap));
- dst->ctx.dss.acmp = &dst->acm;
+ sys_memcpy(&dst->ctx, ctx, sizeof(ErtsDSigSendContext));
+ ASSERT(ctx->ctl == make_tuple(ctx->ctl_heap));
+ dst->ctx.ctl = make_tuple(dst->ctx.ctl_heap);
+ if (ctx->acmp) {
+ sys_memcpy(&dst->acm, ctx->acmp, sizeof(ErtsAtomCacheMap));
+ dst->ctx.acmp = &dst->acm;
}
return erts_mk_magic_ref(&hp, &MSO(p), ctx_bin);
}
@@ -814,71 +973,58 @@ Eterm erts_dsend_export_trap_context(Process* p, ErtsSendContext* ctx)
** Send a DOP_LINK link message
*/
int
-erts_dsig_send_link(ErtsDSigData *dsdp, Eterm local, Eterm remote)
+erts_dsig_send_link(ErtsDSigSendContext *ctx, Eterm local, Eterm remote)
{
- DeclareTmpHeapNoproc(ctl_heap,4);
- Eterm ctl = TUPLE3(&ctl_heap[0], make_small(DOP_LINK), local, remote);
- int res;
- UseTmpHeapNoproc(4);
-
- res = dsig_send_ctl(dsdp, ctl, 0);
- UnUseTmpHeapNoproc(4);
- return res;
+ Eterm ctl = TUPLE3(&ctx->ctl_heap[0], make_small(DOP_LINK), local, remote);
+ return dsig_send_ctl(ctx, ctl);
}
int
-erts_dsig_send_unlink(ErtsDSigData *dsdp, Eterm local, Eterm remote)
+erts_dsig_send_unlink(ErtsDSigSendContext *ctx, Eterm local, Eterm remote)
{
- DeclareTmpHeapNoproc(ctl_heap,4);
- Eterm ctl = TUPLE3(&ctl_heap[0], make_small(DOP_UNLINK), local, remote);
- int res;
-
- UseTmpHeapNoproc(4);
- res = dsig_send_ctl(dsdp, ctl, 0);
- UnUseTmpHeapNoproc(4);
- return res;
+ Eterm ctl = TUPLE3(&ctx->ctl_heap[0], make_small(DOP_UNLINK), local, remote);
+ return dsig_send_ctl(ctx, ctl);
}
/* 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} */
int
-erts_dsig_send_m_exit(ErtsDSigData *dsdp, Eterm watcher, Eterm watched,
- Eterm ref, Eterm reason)
+erts_dsig_send_m_exit(ErtsDSigSendContext *ctx, Eterm watcher, Eterm watched,
+ Eterm ref, Eterm reason)
{
- Eterm ctl;
- DeclareTmpHeapNoproc(ctl_heap,6);
- int res;
+ Eterm ctl, msg;
- if (~dsdp->flags & (DFLAG_DIST_MONITOR | DFLAG_DIST_MONITOR_NAME)) {
+ if (~ctx->flags & (DFLAG_DIST_MONITOR | DFLAG_DIST_MONITOR_NAME)) {
/*
* Receiver does not support DOP_MONITOR_P_EXIT (see dsig_send_monitor)
*/
return ERTS_DSIG_SEND_OK;
}
- UseTmpHeapNoproc(6);
-
- ctl = TUPLE5(&ctl_heap[0], make_small(DOP_MONITOR_P_EXIT),
- watched, watcher, ref, reason);
+ if (ctx->dep->flags & DFLAG_EXIT_PAYLOAD) {
+ ctl = TUPLE4(&ctx->ctl_heap[0], make_small(DOP_PAYLOAD_MONITOR_P_EXIT),
+ watched, watcher, ref);
+ msg = reason;
+ } else {
+ ctl = TUPLE5(&ctx->ctl_heap[0], make_small(DOP_MONITOR_P_EXIT),
+ watched, watcher, ref, reason);
+ msg = THE_NON_VALUE;
+ }
- res = dsig_send_ctl(dsdp, ctl, 1);
- UnUseTmpHeapNoproc(6);
- return res;
+ return dsig_send_exit(ctx, ctl, msg);
}
/* We want to monitor a process (named or unnamed) on another node, we send:
{DOP_MONITOR_P, Local pid, Remote pid or name, Ref}, which is exactly what's
needed on the other side... */
int
-erts_dsig_send_monitor(ErtsDSigData *dsdp, Eterm watcher, Eterm watched,
+erts_dsig_send_monitor(ErtsDSigSendContext *ctx, Eterm watcher, Eterm watched,
Eterm ref)
{
Eterm ctl;
- DeclareTmpHeapNoproc(ctl_heap,5);
- int res;
- if (~dsdp->flags & (DFLAG_DIST_MONITOR | DFLAG_DIST_MONITOR_NAME)) {
+ if (~ctx->flags & (DFLAG_DIST_MONITOR | DFLAG_DIST_MONITOR_NAME)) {
/*
* Receiver does not support DOP_MONITOR_P.
* Just avoid sending it and by doing that reduce this monitor
@@ -888,48 +1034,40 @@ erts_dsig_send_monitor(ErtsDSigData *dsdp, Eterm watcher, Eterm watched,
return ERTS_DSIG_SEND_OK;
}
- UseTmpHeapNoproc(5);
- ctl = TUPLE4(&ctl_heap[0],
+ ctl = TUPLE4(&ctx->ctl_heap[0],
make_small(DOP_MONITOR_P),
watcher, watched, ref);
- res = dsig_send_ctl(dsdp, ctl, 0);
- UnUseTmpHeapNoproc(5);
- return res;
+ return dsig_send_ctl(ctx, ctl);
}
/* 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} */
int
-erts_dsig_send_demonitor(ErtsDSigData *dsdp, Eterm watcher,
- Eterm watched, Eterm ref, int force)
+erts_dsig_send_demonitor(ErtsDSigSendContext *ctx, Eterm watcher,
+ Eterm watched, Eterm ref)
{
Eterm ctl;
- DeclareTmpHeapNoproc(ctl_heap,5);
- int res;
- if (~dsdp->flags & (DFLAG_DIST_MONITOR | DFLAG_DIST_MONITOR_NAME)) {
+ if (~ctx->flags & (DFLAG_DIST_MONITOR | DFLAG_DIST_MONITOR_NAME)) {
/*
* Receiver does not support DOP_DEMONITOR_P (see dsig_send_monitor)
*/
return ERTS_DSIG_SEND_OK;
}
- UseTmpHeapNoproc(5);
- ctl = TUPLE4(&ctl_heap[0],
+ ctl = TUPLE4(&ctx->ctl_heap[0],
make_small(DOP_DEMONITOR_P),
watcher, watched, ref);
- res = dsig_send_ctl(dsdp, ctl, force);
- UnUseTmpHeapNoproc(5);
- return res;
+ return dsig_send_ctl(ctx, ctl);
}
-static int can_send_seqtrace_token(ErtsSendContext* ctx, Eterm token) {
+static int can_send_seqtrace_token(ErtsDSigSendContext* ctx, Eterm token) {
Eterm label;
- if (ctx->dsd.flags & DFLAG_BIG_SEQTRACE_LABELS) {
+ if (ctx->flags & DFLAG_BIG_SEQTRACE_LABELS) {
/* The other end is capable of handling arbitrary seq_trace labels. */
return 1;
}
@@ -945,11 +1083,11 @@ static int can_send_seqtrace_token(ErtsSendContext* ctx, Eterm token) {
}
int
-erts_dsig_send_msg(Eterm remote, Eterm message, ErtsSendContext* ctx)
+erts_dsig_send_msg(ErtsDSigSendContext* ctx, Eterm remote, Eterm message)
{
Eterm ctl;
Eterm token = NIL;
- Process *sender = ctx->dsd.proc;
+ Process *sender = ctx->c_p;
int res;
#ifdef USE_VM_PROBES
Sint tok_label = 0;
@@ -970,7 +1108,7 @@ erts_dsig_send_msg(Eterm remote, Eterm message, ErtsSendContext* ctx)
*node_name = *sender_name = *receiver_name = '\0';
if (DTRACE_ENABLED(message_send) || DTRACE_ENABLED(message_send_remote)) {
erts_snprintf(node_name, sizeof(DTRACE_CHARBUF_NAME(node_name)),
- "%T", ctx->dsd.dep->sysname);
+ "%T", ctx->dep->sysname);
erts_snprintf(sender_name, sizeof(DTRACE_CHARBUF_NAME(sender_name)),
"%T", sender->common.id);
erts_snprintf(receiver_name, sizeof(DTRACE_CHARBUF_NAME(receiver_name)),
@@ -990,7 +1128,7 @@ erts_dsig_send_msg(Eterm remote, Eterm message, ErtsSendContext* ctx)
send_token = (token != NIL && can_send_seqtrace_token(ctx, token));
- if (ctx->dsd.flags & DFLAG_SEND_SENDER) {
+ if (ctx->flags & DFLAG_SEND_SENDER) {
dist_op = make_small(send_token ?
DOP_SEND_SENDER_TT :
DOP_SEND_SENDER);
@@ -1013,21 +1151,18 @@ erts_dsig_send_msg(Eterm remote, Eterm message, ErtsSendContext* ctx)
msize, tok_label, tok_lastcnt, tok_serial);
DTRACE7(message_send_remote, sender_name, node_name, receiver_name,
msize, tok_label, tok_lastcnt, tok_serial);
- ctx->dss.ctl = ctl;
- ctx->dss.msg = message;
- ctx->dss.force_busy = 0;
- res = erts_dsig_send(&ctx->dsd, &ctx->dss);
+ ctx->ctl = ctl;
+ ctx->msg = message;
+ res = erts_dsig_send(ctx);
return res;
}
int
-erts_dsig_send_reg_msg(Eterm remote_name, Eterm message,
- ErtsSendContext* ctx)
+erts_dsig_send_reg_msg(ErtsDSigSendContext* ctx, Eterm remote_name, Eterm message)
{
Eterm ctl;
Eterm token = NIL;
- Process *sender = ctx->dsd.proc;
- int res;
+ Process *sender = ctx->c_p;
#ifdef USE_VM_PROBES
Sint tok_label = 0;
Sint tok_lastcnt = 0;
@@ -1047,7 +1182,7 @@ erts_dsig_send_reg_msg(Eterm remote_name, Eterm message,
*node_name = *sender_name = *receiver_name = '\0';
if (DTRACE_ENABLED(message_send) || DTRACE_ENABLED(message_send_remote)) {
erts_snprintf(node_name, sizeof(DTRACE_CHARBUF_NAME(node_name)),
- "%T", ctx->dsd.dep->sysname);
+ "%T", ctx->dep->sysname);
erts_snprintf(sender_name, sizeof(DTRACE_CHARBUF_NAME(sender_name)),
"%T", sender->common.id);
erts_snprintf(receiver_name, sizeof(DTRACE_CHARBUF_NAME(receiver_name)),
@@ -1072,23 +1207,19 @@ erts_dsig_send_reg_msg(Eterm remote_name, Eterm message,
msize, tok_label, tok_lastcnt, tok_serial);
DTRACE7(message_send_remote, sender_name, node_name, receiver_name,
msize, tok_label, tok_lastcnt, tok_serial);
- ctx->dss.ctl = ctl;
- ctx->dss.msg = message;
- ctx->dss.force_busy = 0;
- res = erts_dsig_send(&ctx->dsd, &ctx->dss);
- return res;
+ ctx->ctl = ctl;
+ ctx->msg = message;
+ return erts_dsig_send(ctx);
}
/* local has died, deliver the exit signal to remote */
int
-erts_dsig_send_exit_tt(ErtsDSigData *dsdp, Eterm local, Eterm remote,
+erts_dsig_send_exit_tt(ErtsDSigSendContext *ctx, Eterm local, Eterm remote,
Eterm reason, Eterm token)
{
- Eterm ctl;
- DeclareTmpHeapNoproc(ctl_heap,6);
- int res;
+ Eterm ctl, msg = THE_NON_VALUE;
#ifdef USE_VM_PROBES
- Process *sender = dsdp->proc;
+ Process *sender = ctx->c_p;
Sint tok_label = 0;
Sint tok_lastcnt = 0;
Sint tok_serial = 0;
@@ -1098,20 +1229,29 @@ erts_dsig_send_exit_tt(ErtsDSigData *dsdp, Eterm local, Eterm remote,
DTRACE_CHARBUF(reason_str, 128);
#endif
- UseTmpHeapNoproc(6);
+ if (ctx->dep->flags & DFLAG_EXIT_PAYLOAD)
+ msg = reason;
+
if (have_seqtrace(token)) {
- seq_trace_update_send(dsdp->proc);
+ seq_trace_update_send(ctx->c_p);
seq_trace_output_exit(token, reason, SEQ_TRACE_SEND, remote, local);
- ctl = TUPLE5(&ctl_heap[0],
- make_small(DOP_EXIT_TT), local, remote, token, reason);
+ if (ctx->dep->flags & DFLAG_EXIT_PAYLOAD) {
+ ctl = TUPLE4(&ctx->ctl_heap[0],
+ make_small(DOP_PAYLOAD_EXIT_TT), local, remote, token);
+ } else
+ ctl = TUPLE5(&ctx->ctl_heap[0],
+ make_small(DOP_EXIT_TT), local, remote, token, reason);
} else {
- ctl = TUPLE4(&ctl_heap[0], make_small(DOP_EXIT), local, remote, reason);
+ if (ctx->dep->flags & DFLAG_EXIT_PAYLOAD)
+ ctl = TUPLE3(&ctx->ctl_heap[0], make_small(DOP_PAYLOAD_EXIT), local, remote);
+ else
+ ctl = TUPLE4(&ctx->ctl_heap[0], make_small(DOP_EXIT), local, remote, reason);
}
#ifdef USE_VM_PROBES
*node_name = *sender_name = *remote_name = '\0';
if (DTRACE_ENABLED(process_exit_signal_remote)) {
erts_snprintf(node_name, sizeof(DTRACE_CHARBUF_NAME(node_name)),
- "%T", dsdp->dep->sysname);
+ "%T", ctx->dep->sysname);
erts_snprintf(sender_name, sizeof(DTRACE_CHARBUF_NAME(sender_name)),
"%T", sender->common.id);
erts_snprintf(remote_name, sizeof(DTRACE_CHARBUF_NAME(remote_name)),
@@ -1127,73 +1267,171 @@ erts_dsig_send_exit_tt(ErtsDSigData *dsdp, Eterm local, Eterm remote,
#endif
DTRACE7(process_exit_signal_remote, sender_name, node_name,
remote_name, reason_str, tok_label, tok_lastcnt, tok_serial);
- /* forced, i.e ignore busy */
- res = dsig_send_ctl(dsdp, ctl, 1);
- UnUseTmpHeapNoproc(6);
- return res;
+ return dsig_send_exit(ctx, ctl, msg);
}
int
-erts_dsig_send_exit(ErtsDSigData *dsdp, Eterm local, Eterm remote, Eterm reason)
+erts_dsig_send_exit(ErtsDSigSendContext *ctx, Eterm local, Eterm remote, Eterm reason)
{
- DeclareTmpHeapNoproc(ctl_heap,5);
- int res;
- Eterm ctl;
+ Eterm ctl, msg = ctx->dep->flags & DFLAG_EXIT_PAYLOAD ? reason : THE_NON_VALUE;
- UseTmpHeapNoproc(5);
- ctl = TUPLE4(&ctl_heap[0],
- make_small(DOP_EXIT), local, remote, reason);
- /* forced, i.e ignore busy */
- res = dsig_send_ctl(dsdp, ctl, 1);
- UnUseTmpHeapNoproc(5);
- return res;
+ if (ctx->dep->flags & DFLAG_EXIT_PAYLOAD) {
+ ctl = TUPLE3(&ctx->ctl_heap[0], make_small(DOP_PAYLOAD_EXIT), local, remote);
+ msg = reason;
+ } else {
+ ctl = TUPLE4(&ctx->ctl_heap[0], make_small(DOP_EXIT), local, remote, reason);
+ msg = THE_NON_VALUE;
+ }
+ return dsig_send_exit(ctx, ctl, msg);
}
int
-erts_dsig_send_exit2(ErtsDSigData *dsdp, Eterm local, Eterm remote, Eterm reason)
+erts_dsig_send_exit2(ErtsDSigSendContext *ctx, Eterm local, Eterm remote, Eterm reason)
{
- DeclareTmpHeapNoproc(ctl_heap,5);
- int res;
- Eterm ctl;
+ Eterm ctl, msg;
- UseTmpHeapNoproc(5);
- ctl = TUPLE4(&ctl_heap[0],
- make_small(DOP_EXIT2), local, remote, reason);
+ if (ctx->dep->flags & DFLAG_EXIT_PAYLOAD) {
+ ctl = TUPLE3(&ctx->ctl_heap[0],
+ make_small(DOP_PAYLOAD_EXIT2), local, remote);
+ msg = reason;
+ } else {
+ ctl = TUPLE4(&ctx->ctl_heap[0],
+ make_small(DOP_EXIT2), local, remote, reason);
+ msg = THE_NON_VALUE;
+ }
- res = dsig_send_ctl(dsdp, ctl, 0);
- UnUseTmpHeapNoproc(5);
- return res;
+ return dsig_send_exit(ctx, ctl, msg);
}
int
-erts_dsig_send_group_leader(ErtsDSigData *dsdp, Eterm leader, Eterm remote)
+erts_dsig_send_group_leader(ErtsDSigSendContext *ctx, Eterm leader, Eterm remote)
{
- DeclareTmpHeapNoproc(ctl_heap,4);
- int res;
Eterm ctl;
- UseTmpHeapNoproc(4);
- ctl = TUPLE3(&ctl_heap[0],
+ ctl = TUPLE3(&ctx->ctl_heap[0],
make_small(DOP_GROUP_LEADER), leader, remote);
- res = dsig_send_ctl(dsdp, ctl, 0);
- UnUseTmpHeapNoproc(4);
- return res;
+ return dsig_send_ctl(ctx, ctl);
}
-#if defined(PURIFY)
-# define PURIFY_MSG(msg) \
- purify_printf("%s, line %d: %s", __FILE__, __LINE__, msg)
-#elif defined(VALGRIND)
-#include <valgrind/valgrind.h>
-#include <valgrind/memcheck.h>
+struct dist_sequences {
+ ErlHeapFragment hfrag;
+ struct dist_sequences *parent;
+ struct dist_sequences *left;
+ struct dist_sequences *right;
+ char is_red;
-# define PURIFY_MSG(msg) \
- VALGRIND_PRINTF("%s, line %d: %s", __FILE__, __LINE__, msg)
-#else
-# define PURIFY_MSG(msg)
-#endif
+ Uint64 seq_id;
+ int cnt;
+ Sint ctl_len;
+};
+
+#define ERTS_RBT_PREFIX dist_seq
+#define ERTS_RBT_T DistSeqNode
+#define ERTS_RBT_KEY_T Uint
+#define ERTS_RBT_FLAGS_T int
+#define ERTS_RBT_INIT_EMPTY_TNODE(T) \
+ do { \
+ (T)->parent = NULL; \
+ (T)->left = NULL; \
+ (T)->right = NULL; \
+ (T)->is_red = 0; \
+ } while(0)
+#define ERTS_RBT_IS_RED(T) ((T)->is_red)
+#define ERTS_RBT_SET_RED(T) ((T)->is_red = 1)
+#define ERTS_RBT_IS_BLACK(T) (!ERTS_RBT_IS_RED(T))
+#define ERTS_RBT_SET_BLACK(T) ((T)->is_red = 0)
+#define ERTS_RBT_GET_FLAGS(T) ((T)->is_red)
+#define ERTS_RBT_SET_FLAGS(T, F) ((T)->is_red = F)
+#define ERTS_RBT_GET_PARENT(T) ((T)->parent)
+#define ERTS_RBT_SET_PARENT(T, P) ((T)->parent = P)
+#define ERTS_RBT_GET_RIGHT(T) ((T)->right)
+#define ERTS_RBT_SET_RIGHT(T, R) ((T)->right = (R))
+#define ERTS_RBT_GET_LEFT(T) ((T)->left)
+#define ERTS_RBT_SET_LEFT(T, L) ((T)->left = (L))
+#define ERTS_RBT_GET_KEY(T) ((T)->seq_id)
+#define ERTS_RBT_IS_LT(KX, KY) (KX < KY)
+#define ERTS_RBT_IS_EQ(KX, KY) (KX == KY)
+#define ERTS_RBT_WANT_DELETE
+#define ERTS_RBT_WANT_LOOKUP_INSERT
+#define ERTS_RBT_WANT_LOOKUP
+#define ERTS_RBT_WANT_FOREACH
+#define ERTS_RBT_WANT_FOREACH_DESTROY_YIELDING
+
+#include "erl_rbtree.h"
+
+struct erts_dist_seq_tree_foreach_iter_arg {
+ int (*func)(ErtsDistExternal *, void *, Sint);
+ void *arg;
+};
+
+static int
+erts_dist_seq_tree_foreach_iter(DistSeqNode *seq, void *arg, Sint reds)
+{
+ struct erts_dist_seq_tree_foreach_iter_arg *state = arg;
+ return state->func(erts_get_dist_ext(&seq->hfrag), state->arg, reds);
+}
+
+void
+erts_dist_seq_tree_foreach(DistEntry *dep, int (*func)(ErtsDistExternal *, void *, Sint), void *arg)
+{
+ struct erts_dist_seq_tree_foreach_iter_arg state;
+ state.func = func;
+ state.arg = arg;
+ dist_seq_rbt_foreach(dep->sequences, erts_dist_seq_tree_foreach_iter, &state);
+}
+
+static int dist_seq_cleanup(DistSeqNode *seq, void *unused, Sint reds)
+{
+ erts_free_dist_ext_copy(erts_get_dist_ext(&seq->hfrag));
+ free_message_buffer(&seq->hfrag);
+ return 1;
+}
+
+typedef struct {
+ DistSeqNode *root;
+ dist_seq_rbt_yield_state_t rbt_ystate;
+} DistSeqNodeYieldState;
+
+int
+erts_dist_seq_tree_foreach_delete_yielding(DistSeqNode **root,
+ void **vyspp,
+ Sint limit)
+{
+ DistSeqNodeYieldState ys = {*root, ERTS_RBT_YIELD_STAT_INITER};
+ DistSeqNodeYieldState *ysp;
+ int res;
+
+ ysp = (DistSeqNodeYieldState *) *vyspp;
+ if (!ysp) {
+ *root = NULL;
+ ysp = &ys;
+ }
+ res = dist_seq_rbt_foreach_destroy_yielding(&ysp->root,
+ dist_seq_cleanup,
+ NULL,
+ &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_SEQ_YIELD_STATE,
+ sizeof(DistSeqNodeYieldState));
+ sys_memcpy((void *) ysp, (void *) &ys,
+ sizeof(DistSeqNodeYieldState));
+ }
+
+ *vyspp = (void *) ysp;
+ }
+
+ return res;
+}
/*
** Input from distribution port.
@@ -1210,10 +1448,13 @@ int erts_net_message(Port *prt,
Uint32 conn_id,
byte *hbuf,
ErlDrvSizeT hlen,
+ Binary *bin,
byte *buf,
ErlDrvSizeT len)
{
- ErtsDistExternal ede;
+ ErtsDistExternal ede, *edep = &ede;
+ ErtsDistExternalData ede_data;
+ ErlHeapFragment *ede_hfrag = NULL;
Sint ctl_len;
Eterm arg;
Eterm from, to;
@@ -1225,10 +1466,8 @@ int erts_net_message(Port *prt,
DeclareTmpHeapNoproc(ctl_default,DIST_CTL_DEFAULT_SIZE);
Eterm* ctl = ctl_default;
ErtsHeapFactory factory;
- Eterm* hp;
Sint type;
Eterm token;
- Eterm token_size;
Uint tuple_arity;
int res;
#ifdef ERTS_DIST_MSG_DBG
@@ -1254,69 +1493,182 @@ int erts_net_message(Port *prt,
}
#ifdef ERTS_RAW_DIST_MSG_DBG
- erts_fprintf(stderr, "<< ");
+ erts_fprintf(dbg_file, "RECV: ");
bw(buf, len);
#endif
- res = erts_prepare_dist_ext(&ede, buf, len, dep, conn_id, dep->cache);
+ ede.data = &ede_data;
+
+ res = erts_prepare_dist_ext(&ede, buf, len, bin, dep, conn_id, dep->cache);
switch (res) {
case ERTS_PREP_DIST_EXT_CLOSED:
return 0; /* Connection not alive; ignore signal... */
case ERTS_PREP_DIST_EXT_FAILED:
#ifdef ERTS_DIST_MSG_DBG
- erts_fprintf(stderr, "DIST MSG DEBUG: erts_prepare_dist_ext() failed:\n");
+ erts_fprintf(dbg_file, "DIST MSG DEBUG: erts_prepare_dist_ext() failed:\n");
bw(buf, orig_len);
#endif
goto data_error;
case ERTS_PREP_DIST_EXT_SUCCESS:
- ctl_len = erts_decode_dist_ext_size(&ede);
+ ctl_len = erts_decode_dist_ext_size(&ede, 1);
if (ctl_len < 0) {
#ifdef ERTS_DIST_MSG_DBG
- erts_fprintf(stderr, "DIST MSG DEBUG: erts_decode_dist_ext_size(CTL) failed:\n");
+ erts_fprintf(dbg_file, "DIST MSG DEBUG: erts_decode_dist_ext_size(CTL) failed:\n");
bw(buf, orig_len);
#endif
PURIFY_MSG("data error");
goto data_error;
}
+
+ /* A non-fragmented message */
+ if (!ede.data->seq_id) {
+ if (ctl_len > DIST_CTL_DEFAULT_SIZE) {
+ ctl = erts_alloc(ERTS_ALC_T_DCTRL_BUF, ctl_len * sizeof(Eterm));
+ }
+
+ erts_factory_tmp_init(&factory, ctl, ctl_len, ERTS_ALC_T_DCTRL_BUF);
+ break;
+ } else {
+ DistSeqNode *seq;
+ Uint sz = erts_dist_ext_size(&ede);
+ Uint used_sz = ctl_len * sizeof(Eterm);
+
+ /* We calculate the size of the heap fragment to be allocated.
+ The used_size part has to be larger that the ctl data and the
+ DistSeqNode. */
+ if (used_sz + (sizeof(ErlHeapFragment) - sizeof(Eterm)) < sizeof(DistSeqNode))
+ used_sz = sizeof(DistSeqNode) - (sizeof(ErlHeapFragment) - sizeof(Eterm));
+
+ seq = (DistSeqNode *)new_message_buffer((sz + used_sz) / sizeof(Eterm));
+ seq->hfrag.used_size = used_sz / sizeof(Eterm);
+
+ seq->ctl_len = ctl_len;
+ seq->seq_id = ede.data->seq_id;
+ seq->cnt = ede.data->frag_id;
+ if (dist_seq_rbt_lookup_insert(&dep->sequences, seq) != NULL) {
+ free_message_buffer(&seq->hfrag);
+ goto data_error;
+ }
+
+ erts_make_dist_ext_copy(&ede, erts_get_dist_ext(&seq->hfrag));
+
+ if (ede.data->frag_id > 1) {
+ seq->cnt--;
+ return 0;
+ }
+ }
+
+ /* fall through, the first fragment in the sequence was the last fragment */
+ case ERTS_PREP_DIST_EXT_FRAG_CONT: {
+ DistSeqNode *seq = dist_seq_rbt_lookup(dep->sequences, ede.data->seq_id);
+
+ if (!seq)
+ goto data_error;
+
+ /* If we did a fall-though we already did this */
+ if (res == ERTS_PREP_DIST_EXT_FRAG_CONT)
+ erts_dist_ext_frag(&ede_data, erts_get_dist_ext(&seq->hfrag));
+
+ /* Verify that the fragments have arrived in the correct order */
+ if (seq->cnt != ede.data->frag_id)
+ goto data_error;
+
+ seq->cnt--;
+
+ /* Check if this was the last fragment */
+ if (ede.data->frag_id > 1)
+ return 0;
+
+ /* Last fragment arrived, time to dispatch the signal */
+ dist_seq_rbt_delete(&dep->sequences, seq);
+ ctl_len = seq->ctl_len;
+
+ /* Now that we no longer need the DistSeqNode we re-use the heapfragment
+ to decode the ctl msg into. We don't need the ctl message to be in
+ the heapfragment, but we decode into the heapfragment speculatively
+ in case there is a trace token that we need. */
+ erts_factory_heap_frag_init(&factory, &seq->hfrag);
+ edep = erts_get_dist_ext(&seq->hfrag);
+ ede_hfrag = &seq->hfrag;
+
+ /* If the sequence consisted of more than 1 fragment we create one large
+ binary out of all of the fragments. This because erts_decode_ext
+ cannot handle a segmented buffer.
+ TODO: Move this copy to as late as possible, preferably in in the
+ erts_decode_dist_ext in the receiving process.
+ */
+ if (edep->data->frag_id > 1) {
+ Uint sz = 0;
+ Binary *bin;
+ int i;
+ byte *ep;
+
+ for (i = 0; i < edep->data->frag_id; i++)
+ sz += edep->data[i].ext_endp - edep->data[i].extp;
+
+ bin = erts_bin_nrml_alloc(sz);
+ ep = (byte*)bin->orig_bytes;
+
+ for (i = 0; i < edep->data->frag_id; i++) {
+ sys_memcpy(ep, edep->data[i].extp, edep->data[i].ext_endp - edep->data[i].extp);
+ ep += edep->data[i].ext_endp - edep->data[i].extp;
+ erts_bin_release(edep->data[i].binp);
+ edep->data[i].binp = NULL;
+ edep->data[i].extp = NULL;
+ edep->data[i].ext_endp = NULL;
+ }
+
+ edep->data->frag_id = 1;
+ edep->data->extp = (byte*)bin->orig_bytes;
+ edep->data->ext_endp = ep;
+ edep->data->binp = bin;
+ }
+
break;
+ }
default:
ERTS_INTERNAL_ERROR("Unexpected result from erts_prepare_dist_ext()");
break;
}
- if (ctl_len > DIST_CTL_DEFAULT_SIZE) {
- ctl = erts_alloc(ERTS_ALC_T_DCTRL_BUF, ctl_len * sizeof(Eterm));
- }
- hp = ctl;
-
- erts_factory_tmp_init(&factory, ctl, ctl_len, ERTS_ALC_T_DCTRL_BUF);
- arg = erts_decode_dist_ext(&factory, &ede);
+ arg = erts_decode_dist_ext(&factory, edep, 1);
if (is_non_value(arg)) {
#ifdef ERTS_DIST_MSG_DBG
- erts_fprintf(stderr, "DIST MSG DEBUG: erts_decode_dist_ext(CTL) failed:\n");
+ erts_fprintf(dbg_file, "DIST MSG DEBUG: erts_decode_dist_ext(CTL) failed:\n");
bw(buf, orig_len);
#endif
PURIFY_MSG("data error");
goto decode_error;
}
-#ifdef ERTS_DIST_MSG_DBG
- erts_fprintf(stderr, "<< CTL: %T\n", arg);
-#endif
+ /* Fill the unused part of the hfrag with a bignum header */
+ if (ede_hfrag && ede_hfrag->mem + ede_hfrag->used_size > factory.hp) {
+ Uint slot = factory.hp - ede_hfrag->mem;
+ ede_hfrag->mem[slot] = make_pos_bignum_header(ede_hfrag->used_size - slot - 1);
+ }
if (is_not_tuple(arg) ||
(tuple = tuple_val(arg), (tuple_arity = arityval(*tuple)) < 1) ||
is_not_small(tuple[1])) {
+#ifdef ERTS_DIST_MSG_DBG
+ if (is_tuple(arg) && arityval(*tuple) > 1)
+ erts_fprintf(dbg_file, "RECV: CTL: %s: %.80T\n",
+ erts_dop_to_string(unsigned_val(tuple[1])), arg);
+#endif
goto invalid_message;
}
- token_size = 0;
+#ifdef ERTS_DIST_MSG_DBG
+ erts_fprintf(dbg_file, "RECV: CTL: %s: %.80T\n",
+ erts_dop_to_string(unsigned_val(tuple[1])), arg);
+#endif
+
token = NIL;
switch (type = unsigned_val(tuple[1])) {
case DOP_LINK: {
- ErtsDSigData dsd;
+ ErtsDSigSendContext ctx;
int code;
if (tuple_arity != 3) {
@@ -1354,9 +1706,9 @@ int erts_net_message(Port *prt,
erts_link_release_both(ldp);
}
- code = erts_dsig_prepare(&dsd, dep, NULL, 0, ERTS_DSP_NO_LOCK, 0, 0);
+ code = erts_dsig_prepare(&ctx, dep, NULL, 0, ERTS_DSP_NO_LOCK, 1, 1, 0);
if (code == ERTS_DSIG_PREP_CONNECTED) {
- code = erts_dsig_send_exit(&dsd, to, from, am_noproc);
+ code = erts_dsig_send_exit(&ctx, to, from, am_noproc);
ASSERT(code == ERTS_DSIG_SEND_OK);
}
@@ -1389,7 +1741,7 @@ int erts_net_message(Port *prt,
/* A remote process wants to monitor us, we get:
{DOP_MONITOR_P, Remote pid, local pid or name, ref} */
Eterm pid, name;
- ErtsDSigData dsd;
+ ErtsDSigSendContext ctx;
int code;
if (tuple_arity != 4) {
@@ -1444,10 +1796,9 @@ int erts_net_message(Port *prt,
}
- code = erts_dsig_prepare(&dsd, dep, NULL, 0, ERTS_DSP_NO_LOCK, 0, 0);
+ code = erts_dsig_prepare(&ctx, dep, NULL, 0, ERTS_DSP_NO_LOCK, 1, 1, 0);
if (code == ERTS_DSIG_PREP_CONNECTED) {
- code = erts_dsig_send_m_exit(&dsd, watcher, watched, ref,
- am_noproc);
+ code = erts_dsig_send_m_exit(&ctx, watcher, watched, ref, am_noproc);
ASSERT(code == ERTS_DSIG_SEND_OK);
}
@@ -1506,7 +1857,6 @@ int erts_net_message(Port *prt,
goto invalid_message;
}
- token_size = size_object(tuple[5]);
/* Fall through ... */
case DOP_REG_SEND:
/* {DOP_REG_SEND, From, Cookie, ToName} -- Message */
@@ -1521,7 +1871,7 @@ int erts_net_message(Port *prt,
}
#ifdef ERTS_DIST_MSG_DBG
- dist_msg_dbg(&ede, "MSG", buf, orig_len);
+ dist_msg_dbg(edep, "MSG", buf, orig_len);
#endif
from = tuple[2];
@@ -1531,35 +1881,25 @@ int erts_net_message(Port *prt,
}
rp = erts_whereis_process(NULL, 0, to, 0, 0);
if (rp) {
- Uint xsize = (type == DOP_REG_SEND
- ? 0
- : ERTS_HEAP_FRAG_SIZE(token_size));
ErtsProcLocks locks = 0;
- ErtsDistExternal *ede_copy;
- ede_copy = erts_make_dist_ext_copy(&ede, xsize);
if (type == DOP_REG_SEND) {
token = NIL;
} else {
- ErlHeapFragment *heap_frag;
- ErlOffHeap *ohp;
- ASSERT(xsize);
- heap_frag = erts_dist_ext_trailer(ede_copy);
- ERTS_INIT_HEAP_FRAG(heap_frag, token_size, token_size);
- hp = heap_frag->mem;
- ohp = &heap_frag->off_heap;
token = tuple[5];
- token = copy_struct(token, token_size, &hp, ohp);
}
- erts_queue_dist_message(rp, locks, ede_copy, token, from);
+ erts_queue_dist_message(rp, locks, edep, ede_hfrag, token, from);
+
if (locks)
erts_proc_unlock(rp, locks);
- }
+ } else if (ede_hfrag) {
+ erts_free_dist_ext_copy(erts_get_dist_ext(ede_hfrag));
+ free_message_buffer(ede_hfrag);
+ }
break;
case DOP_SEND_SENDER_TT: {
- Uint xsize;
case DOP_SEND_TT:
if (tuple_arity != 4) {
@@ -1567,15 +1907,12 @@ int erts_net_message(Port *prt,
}
token = tuple[4];
- token_size = size_object(token);
- xsize = ERTS_HEAP_FRAG_SIZE(token_size);
goto send_common;
case DOP_SEND_SENDER:
case DOP_SEND:
token = NIL;
- xsize = 0;
if (tuple_arity != 3)
goto invalid_message;
@@ -1591,7 +1928,7 @@ int erts_net_message(Port *prt,
: tuple[2] == am_Empty);
#ifdef ERTS_DIST_MSG_DBG
- dist_msg_dbg(&ede, "MSG", buf, orig_len);
+ dist_msg_dbg(edep, "MSG", buf, orig_len);
#endif
to = tuple[3];
if (is_not_pid(to)) {
@@ -1600,39 +1937,38 @@ int erts_net_message(Port *prt,
rp = erts_proc_lookup(to);
if (rp) {
ErtsProcLocks locks = 0;
- ErtsDistExternal *ede_copy;
-
- ede_copy = erts_make_dist_ext_copy(&ede, xsize);
- if (is_not_nil(token)) {
- ErlHeapFragment *heap_frag;
- ErlOffHeap *ohp;
- ASSERT(xsize);
- heap_frag = erts_dist_ext_trailer(ede_copy);
- ERTS_INIT_HEAP_FRAG(heap_frag, token_size, token_size);
- hp = heap_frag->mem;
- ohp = &heap_frag->off_heap;
- token = copy_struct(token, token_size, &hp, ohp);
- }
- erts_queue_dist_message(rp, locks, ede_copy, token, am_Empty);
+ erts_queue_dist_message(rp, locks, edep, ede_hfrag, token, am_Empty);
if (locks)
erts_proc_unlock(rp, locks);
- }
+ } else if (ede_hfrag) {
+ erts_free_dist_ext_copy(erts_get_dist_ext(ede_hfrag));
+ free_message_buffer(ede_hfrag);
+ }
break;
}
+ case DOP_PAYLOAD_MONITOR_P_EXIT:
case DOP_MONITOR_P_EXIT: {
+
/* 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} */
-
- if (tuple_arity != 5) {
- goto invalid_message;
- }
- watched = tuple[2]; /* remote proc or name which died */
- watcher = tuple[3];
+ watched = tuple[2]; /* remote proc or name which died */
+ watcher = tuple[3];
ref = tuple[4];
- reason = tuple[5];
+
+ if (type == DOP_PAYLOAD_MONITOR_P_EXIT) {
+ if (tuple_arity != 4) {
+ goto invalid_message;
+ }
+ reason = THE_NON_VALUE;
+ } else {
+ if (tuple_arity != 5) {
+ goto invalid_message;
+ }
+ reason = tuple[5];
+ }
if (is_not_ref(ref))
goto invalid_message;
@@ -1648,70 +1984,125 @@ int erts_net_message(Port *prt,
goto invalid_message;
}
- erts_proc_sig_send_dist_monitor_down(dep, ref, watched,
- watcher, reason);
+ if (!erts_proc_lookup(watcher)) break; /* Process not alive */
+
+ if (reason == THE_NON_VALUE) {
+
+#ifdef ERTS_DIST_MSG_DBG
+ dist_msg_dbg(edep, "MSG", buf, orig_len);
+#endif
+
+ }
+
+ erts_proc_sig_send_dist_monitor_down(
+ dep, ref, watched, watcher, edep, ede_hfrag, reason);
break;
}
+ case DOP_PAYLOAD_EXIT:
+ case DOP_PAYLOAD_EXIT_TT:
case DOP_EXIT_TT:
case DOP_EXIT: {
+
/* 'from', which 'to' is linked to, died */
+ from = tuple[2];
+ to = tuple[3];
+
if (type == DOP_EXIT) {
if (tuple_arity != 4) {
goto invalid_message;
}
-
- from = tuple[2];
- to = tuple[3];
- reason = tuple[4];
token = NIL;
- } else {
+ reason = tuple[4];
+ } else if (type == DOP_EXIT_TT){
if (tuple_arity != 5) {
goto invalid_message;
}
- from = tuple[2];
- to = tuple[3];
token = tuple[4];
reason = tuple[5];
- }
+ } else if (type == DOP_PAYLOAD_EXIT) {
+ if (tuple_arity != 3) {
+ goto invalid_message;
+ }
+ token = NIL;
+ reason = THE_NON_VALUE;
+ } else {
+ if (tuple_arity != 4) {
+ goto invalid_message;
+ }
+ token = tuple[4];
+ reason = THE_NON_VALUE;
+ }
if (is_not_external_pid(from)
|| dep != external_pid_dist_entry(from)
|| is_not_internal_pid(to)) {
goto invalid_message;
}
+ if (!erts_proc_lookup(to)) break; /* Process not alive */
+
+ if (reason == THE_NON_VALUE) {
+#ifdef ERTS_DIST_MSG_DBG
+ dist_msg_dbg(edep, "MSG", buf, orig_len);
+#endif
+ }
+
erts_proc_sig_send_dist_link_exit(dep,
- from, to,
+ from, to, edep, ede_hfrag,
reason, token);
break;
}
+ case DOP_PAYLOAD_EXIT2_TT:
+ case DOP_PAYLOAD_EXIT2:
case DOP_EXIT2_TT:
- case DOP_EXIT2:
+ case DOP_EXIT2: {
+
/* 'from' is send an exit signal to 'to' */
+ from = tuple[2];
+ to = tuple[3];
+
if (type == DOP_EXIT2) {
if (tuple_arity != 4) {
goto invalid_message;
}
- from = tuple[2];
- to = tuple[3];
reason = tuple[4];
token = NIL;
- } else {
+ } else if (type == DOP_EXIT2_TT) {
if (tuple_arity != 5) {
goto invalid_message;
}
- from = tuple[2];
- to = tuple[3];
token = tuple[4];
reason = tuple[5];
- }
- if (is_not_pid(from) || is_not_internal_pid(to)) {
+ } else if (type == DOP_PAYLOAD_EXIT2) {
+ if (tuple_arity != 3) {
+ goto invalid_message;
+ }
+ reason = THE_NON_VALUE;
+ token = NIL;
+ } else {
+ if (tuple_arity != 4) {
+ goto invalid_message;
+ }
+ reason = THE_NON_VALUE;
+ token = tuple[4];
+ }
+ if (is_not_pid(from)
+ || dep != external_pid_dist_entry(from)
+ || is_not_internal_pid(to)) {
goto invalid_message;
}
- erts_proc_sig_send_exit(NULL, from, to, reason, token, 0);
- break;
+ if (!erts_proc_lookup(to)) break; /* Process not alive */
+ if (reason == THE_NON_VALUE) {
+#ifdef ERTS_DIST_MSG_DBG
+ dist_msg_dbg(edep, "MSG", buf, orig_len);
+#endif
+ }
+
+ erts_proc_sig_send_dist_exit(dep, from, to, edep, ede_hfrag, reason, token);
+ break;
+ }
case DOP_GROUP_LEADER:
if (tuple_arity != 3) {
goto invalid_message;
@@ -1725,13 +2116,15 @@ int erts_net_message(Port *prt,
(void) erts_proc_sig_send_group_leader(NULL, to, from, NIL);
break;
- default:
+ default:
goto invalid_message;
}
- erts_factory_close(&factory);
- if (ctl != ctl_default) {
- erts_free(ERTS_ALC_T_DCTRL_BUF, (void *) ctl);
+ if (ede_hfrag == NULL) {
+ erts_factory_close(&factory);
+ if (ctl != ctl_default) {
+ erts_free(ERTS_ALC_T_DCTRL_BUF, (void *) ctl);
+ }
}
UnUseTmpHeapNoproc(DIST_CTL_DEFAULT_SIZE);
ERTS_CHK_NO_PROC_LOCKS;
@@ -1744,9 +2137,14 @@ int erts_net_message(Port *prt,
}
decode_error:
PURIFY_MSG("data error");
- erts_factory_close(&factory);
- if (ctl != ctl_default) {
- erts_free(ERTS_ALC_T_DCTRL_BUF, (void *) ctl);
+ if (ede_hfrag == NULL) {
+ erts_factory_close(&factory);
+ if (ctl != ctl_default) {
+ erts_free(ERTS_ALC_T_DCTRL_BUF, (void *) ctl);
+ }
+ } else {
+ erts_free_dist_ext_copy(erts_get_dist_ext(ede_hfrag));
+ free_message_buffer(ede_hfrag);
}
data_error:
UnUseTmpHeapNoproc(DIST_CTL_DEFAULT_SIZE);
@@ -1755,18 +2153,21 @@ data_error:
return -1;
}
-static int dsig_send_ctl(ErtsDSigData* dsdp, Eterm ctl, int force_busy)
+static int dsig_send_exit(ErtsDSigSendContext *ctx, Eterm ctl, Eterm msg)
+{
+ ctx->ctl = ctl;
+ ctx->msg = msg;
+ return erts_dsig_send(ctx);
+}
+
+static int dsig_send_ctl(ErtsDSigSendContext *ctx, Eterm ctl)
{
- struct erts_dsig_send_context ctx;
int ret;
- ctx.ctl = ctl;
- ctx.msg = THE_NON_VALUE;
- ctx.force_busy = force_busy;
- ctx.phase = ERTS_DSIG_SEND_PHASE_INIT;
-#ifdef DEBUG
- ctx.reds = 1; /* provoke assert below (no reduction count without msg) */
-#endif
- ret = erts_dsig_send(dsdp, &ctx);
+ ctx->ctl = ctl;
+ ctx->msg = THE_NON_VALUE;
+ ctx->from = THE_NON_VALUE;
+ ctx->reds = 1; /* provoke assert below (no reduction count without msg) */
+ ret = erts_dsig_send(ctx);
ASSERT(ret != ERTS_DSIG_SEND_CONTINUE);
return ret;
}
@@ -1797,7 +2198,117 @@ notify_dist_data(Process *c_p, Eterm pid)
}
int
-erts_dsig_send(ErtsDSigData *dsdp, struct erts_dsig_send_context* ctx)
+erts_dsig_prepare(ErtsDSigSendContext *ctx,
+ DistEntry *dep,
+ Process *proc,
+ ErtsProcLocks proc_locks,
+ ErtsDSigPrepLock dspl,
+ int no_suspend,
+ int no_trap,
+ int connect)
+{
+ int res;
+
+ if (!erts_is_alive)
+ return ERTS_DSIG_PREP_NOT_ALIVE;
+ if (!dep) {
+ ASSERT(!connect);
+ return ERTS_DSIG_PREP_NOT_CONNECTED;
+ }
+
+#ifdef ERTS_ENABLE_LOCK_CHECK
+ if (connect) {
+ erts_proc_lc_might_unlock(proc, proc_locks);
+ }
+#endif
+
+retry:
+ erts_de_rlock(dep);
+
+ if (dep->state == ERTS_DE_STATE_CONNECTED) {
+ res = ERTS_DSIG_PREP_CONNECTED;
+ }
+ else if (dep->state == ERTS_DE_STATE_PENDING) {
+ res = ERTS_DSIG_PREP_PENDING;
+ }
+ else if (dep->state == ERTS_DE_STATE_EXITING) {
+ res = ERTS_DSIG_PREP_NOT_CONNECTED;
+ goto fail;
+ }
+ else if (connect) {
+ 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;
+ }
+ goto retry;
+ }
+ else {
+ ASSERT(dep->state == ERTS_DE_STATE_IDLE);
+ res = ERTS_DSIG_PREP_NOT_CONNECTED;
+ goto fail;
+ }
+
+ if (no_suspend) {
+ if (erts_atomic32_read_acqb(&dep->qflgs) & ERTS_DE_QFLG_BUSY) {
+ res = ERTS_DSIG_PREP_WOULD_SUSPEND;
+ goto fail;
+ }
+ }
+
+ ctx->c_p = proc;
+ ctx->dep = dep;
+ ctx->deref_dep = 0;
+ ctx->cid = dep->cid;
+ ctx->connection_id = dep->connection_id;
+ ctx->no_suspend = no_suspend;
+ ctx->no_trap = no_trap;
+ ctx->flags = dep->flags;
+ ctx->return_term = am_true;
+ ctx->phase = ERTS_DSIG_SEND_PHASE_INIT;
+ ctx->from = proc ? proc->common.id : am_undefined;
+ ctx->reds = no_trap ? 1 : (Sint) (ERTS_BIF_REDS_LEFT(proc) * TERM_TO_BINARY_LOOP_FACTOR);
+ if (dspl == ERTS_DSP_NO_LOCK)
+ erts_de_runlock(dep);
+ return res;
+
+ fail:
+ erts_de_runlock(dep);
+ return res;
+}
+
+static
+void erts_schedule_dist_command(Port *prt, DistEntry *dist_entry)
+{
+ DistEntry *dep;
+ Eterm id;
+
+ if (prt) {
+ ERTS_LC_ASSERT(erts_lc_is_port_locked(prt));
+ ASSERT((erts_atomic32_read_nob(&prt->state)
+ & ERTS_PORT_SFLGS_DEAD) == 0);
+
+ dep = (DistEntry*) erts_prtsd_get(prt, ERTS_PRTSD_DIST_ENTRY);
+ ASSERT(dep);
+ id = prt->common.id;
+ }
+ else {
+ ASSERT(dist_entry);
+ ERTS_LC_ASSERT(erts_lc_rwmtx_is_rlocked(&dist_entry->rwmtx)
+ || erts_lc_rwmtx_is_rwlocked(&dist_entry->rwmtx));
+ ASSERT(is_internal_port(dist_entry->cid));
+
+ dep = dist_entry;
+ id = dep->cid;
+ }
+
+ if (!erts_atomic_xchg_mb(&dep->dist_cmd_scheduled, 1))
+ erts_port_task_schedule(id, &dep->dist_cmd, ERTS_PORT_TASK_DIST_CMD);
+}
+
+
+int
+erts_dsig_send(ErtsDSigSendContext *ctx)
{
int retval;
Sint initial_reds = ctx->reds;
@@ -1806,11 +2317,13 @@ erts_dsig_send(ErtsDSigData *dsdp, struct erts_dsig_send_context* ctx)
while (1) {
switch (ctx->phase) {
case ERTS_DSIG_SEND_PHASE_INIT:
- ctx->flags = dsdp->flags;
- ctx->c_p = dsdp->proc;
+ ctx->flags = ctx->flags;
+ ctx->c_p = ctx->c_p;
- if (!ctx->c_p || dsdp->no_suspend)
- ctx->force_busy = 1;
+ if (!ctx->c_p) {
+ ctx->no_trap = 1;
+ ctx->no_suspend = 1;
+ }
ERTS_LC_ASSERT(!ctx->c_p
|| (ERTS_PROC_LOCK_MAIN
@@ -1829,9 +2342,11 @@ erts_dsig_send(ErtsDSigData *dsdp, struct erts_dsig_send_context* ctx)
}
#ifdef ERTS_DIST_MSG_DBG
- erts_fprintf(stderr, ">> CTL: %T\n", ctx->ctl);
+ erts_fprintf(dbg_file, "SEND: CTL: %s: %.80T\n",
+ erts_dop_to_string(unsigned_val(tuple_val(ctx->ctl)[1])),
+ ctx->ctl);
if (is_value(ctx->msg))
- erts_fprintf(stderr, " MSG: %T\n", ctx->msg);
+ erts_fprintf(dbg_file, " MSG: %.160T\n", ctx->msg);
#endif
ctx->data_size = ctx->max_finalize_prepend;
@@ -1848,25 +2363,45 @@ erts_dsig_send(ErtsDSigData *dsdp, struct erts_dsig_send_context* ctx)
ctx->phase = ERTS_DSIG_SEND_PHASE_MSG_SIZE;
case ERTS_DSIG_SEND_PHASE_MSG_SIZE:
- if (erts_encode_dist_ext_size_int(ctx->msg, ctx, &ctx->data_size)) {
- retval = ERTS_DSIG_SEND_CONTINUE;
- goto done;
- }
+ if (!ctx->no_trap) {
+ if (erts_encode_dist_ext_size_int(ctx->msg, ctx, &ctx->data_size)) {
+ retval = ERTS_DSIG_SEND_CONTINUE;
+ goto done;
+ }
+ } else {
+ erts_encode_dist_ext_size(ctx->msg, ctx->flags, ctx->acmp, &ctx->data_size);
+ }
ctx->phase = ERTS_DSIG_SEND_PHASE_ALLOC;
case ERTS_DSIG_SEND_PHASE_ALLOC:
erts_finalize_atom_cache_map(ctx->acmp, ctx->flags);
- ctx->dhdr_ext_size = erts_encode_ext_dist_header_size(ctx->acmp);
- ctx->data_size += ctx->dhdr_ext_size;
+ if (ctx->flags & DFLAG_FRAGMENTS && is_value(ctx->msg) && is_not_immed(ctx->msg)) {
+ /* Calculate the max number of fragments that are needed */
+ ASSERT(is_pid(ctx->from) &&
+ "from has to be a pid because it is used as sequence id");
+ ctx->fragments = ctx->data_size / ERTS_DIST_FRAGMENT_SIZE + 1;
+ } else
+ ctx->fragments = 1;
+
+ ctx->dhdr_ext_size = erts_encode_ext_dist_header_size(ctx->acmp, ctx->fragments);
- ctx->obuf = alloc_dist_obuf(ctx->data_size);
- ctx->obuf->ext_endp = &ctx->obuf->data[0] + ctx->max_finalize_prepend + ctx->dhdr_ext_size;
+ ctx->obuf = alloc_dist_obuf(
+ ctx->dhdr_ext_size + ctx->data_size +
+ (ctx->fragments-1) * ERTS_DIST_FRAGMENT_HEADER_SIZE,
+ ctx->fragments);
+ ctx->obuf->ext_start = &ctx->obuf->extp[0];
+ ctx->obuf->ext_endp = &ctx->obuf->extp[0] + ctx->max_finalize_prepend + ctx->dhdr_ext_size;
/* Encode internal version of dist header */
- ctx->obuf->extp = erts_encode_ext_dist_header_setup(ctx->obuf->ext_endp, ctx->acmp);
+ ctx->obuf->extp = erts_encode_ext_dist_header_setup(
+ ctx->obuf->ext_endp, ctx->acmp, ctx->fragments, ctx->from);
/* Encode control message */
erts_encode_dist_ext(ctx->ctl, &ctx->obuf->ext_endp, ctx->flags, ctx->acmp, NULL, NULL);
+
+ ctx->obuf->hdrp = NULL;
+ ctx->obuf->hdr_endp = NULL;
+
if (is_non_value(ctx->msg)) {
ctx->obuf->msg_start = NULL;
ctx->phase = ERTS_DSIG_SEND_PHASE_FIN;
@@ -1880,50 +2415,137 @@ erts_dsig_send(ErtsDSigData *dsdp, struct erts_dsig_send_context* ctx)
ctx->phase = ERTS_DSIG_SEND_PHASE_MSG_ENCODE;
case ERTS_DSIG_SEND_PHASE_MSG_ENCODE:
- if (erts_encode_dist_ext(ctx->msg, &ctx->obuf->ext_endp, ctx->flags, ctx->acmp, &ctx->u.ec, &ctx->reds)) {
- retval = ERTS_DSIG_SEND_CONTINUE;
- goto done;
- }
+ if (!ctx->no_trap) {
+ if (erts_encode_dist_ext(ctx->msg, &ctx->obuf->ext_endp, ctx->flags,
+ ctx->acmp, &ctx->u.ec, &ctx->reds)) {
+ retval = ERTS_DSIG_SEND_CONTINUE;
+ goto done;
+ }
+ } else {
+ erts_encode_dist_ext(ctx->msg, &ctx->obuf->ext_endp, ctx->flags,
+ ctx->acmp, NULL, NULL);
+ }
- ctx->phase = ERTS_DSIG_SEND_PHASE_FIN;
+ ctx->phase = ERTS_DSIG_SEND_PHASE_FIN;
case ERTS_DSIG_SEND_PHASE_FIN: {
- DistEntry *dep = dsdp->dep;
- int suspended = 0;
- int resume = 0;
ASSERT(ctx->obuf->extp < ctx->obuf->ext_endp);
- ASSERT(&ctx->obuf->data[0] <= ctx->obuf->extp - ctx->max_finalize_prepend);
- ASSERT(ctx->obuf->ext_endp <= &ctx->obuf->data[0] + ctx->data_size);
+ ASSERT(((byte*)&ctx->obuf->bin->orig_bytes[0]+obuf_list_size) <= ctx->obuf->extp - ctx->max_finalize_prepend);
+ ASSERT(ctx->obuf->ext_endp <= ((byte*)ctx->obuf->bin->orig_bytes+obuf_list_size) + ctx->data_size + ctx->dhdr_ext_size);
ctx->data_size = ctx->obuf->ext_endp - ctx->obuf->extp;
- if (ctx->data_size > (Uint) INT_MAX) {
- free_dist_obuf(ctx->obuf);
- ctx->obuf = NULL;
- retval = ERTS_DSIG_SEND_TOO_LRG;
- goto done;
- }
ctx->obuf->hopefull_flags = ctx->u.ec.hopefull_flags;
- /*
+
+ if (ctx->fragments > 1) {
+ int fin_fragments;
+ int i;
+ byte *msg = ctx->obuf->msg_start,
+ *msg_end = ctx->obuf->ext_endp,
+ *hdrp = msg_end;
+
+ ASSERT((ctx->obuf->hopefull_flags & ctx->flags) == ctx->obuf->hopefull_flags);
+ ASSERT(get_int64(ctx->obuf->extp + 1 + 1 + 8) == ctx->fragments);
+
+ /* Now that encoding is done we know how large the term will
+ be so we adjust the number of fragments to send. Note that
+ this can mean that only 1 fragment is sent. */
+ fin_fragments = (ctx->obuf->ext_endp - ctx->obuf->msg_start + ERTS_DIST_FRAGMENT_SIZE-1) /
+ ERTS_DIST_FRAGMENT_SIZE - 1;
+
+ /* Update the frag_id in the DIST_FRAG_HEADER */
+ put_int64(fin_fragments+1, ctx->obuf->extp + 1 + 1 + 8);
+
+ if (fin_fragments > 0)
+ msg += ERTS_DIST_FRAGMENT_SIZE;
+ else
+ msg = msg_end;
+ ctx->obuf->next = &ctx->obuf[1];
+ ctx->obuf->ext_endp = msg;
+
+ /* Loop through all fragments, updating the output buffers
+ to be correct and also writing the DIST_FRAG_CONT header. */
+ for (i = 1; i < fin_fragments + 1; i++) {
+ ctx->obuf[i].hopefull_flags = 0;
+ ctx->obuf[i].extp = msg;
+ ctx->obuf[i].ext_start = msg;
+ if (msg + ERTS_DIST_FRAGMENT_SIZE > msg_end)
+ ctx->obuf[i].ext_endp = msg_end;
+ else {
+ msg += ERTS_DIST_FRAGMENT_SIZE;
+ ctx->obuf[i].ext_endp = msg;
+ }
+ ASSERT(ctx->obuf[i].ext_endp > ctx->obuf[i].extp);
+ ctx->obuf[i].hdrp = erts_encode_ext_dist_header_fragment(
+ &hdrp, fin_fragments - i + 1, ctx->from);
+ ctx->obuf[i].hdr_endp = hdrp;
+ ctx->obuf[i].next = &ctx->obuf[i+1];
+ }
+ /* If the initial fragment calculation was incorrect we free the
+ remaining output buffers. */
+ for (; i < ctx->fragments; i++) {
+ free_dist_obuf(&ctx->obuf[i]);
+ }
+ if (!ctx->no_trap && !ctx->no_suspend)
+ ctx->reds -= ctx->fragments;
+ ctx->fragments = fin_fragments + 1;
+ }
+
+ ctx->phase = ERTS_DSIG_SEND_PHASE_SEND;
+
+ if (ctx->reds <= 0) {
+ retval = ERTS_DSIG_SEND_CONTINUE;
+ goto done;
+ }
+ }
+ case ERTS_DSIG_SEND_PHASE_SEND: {
+ /*
* Signal encoded; now verify that the connection still exists,
* and if so enqueue the signal and schedule it for send.
*/
- ctx->obuf->next = NULL;
+ DistEntry *dep = ctx->dep;
+ int suspended = 0;
+ int resume = 0;
+ int i;
erts_de_rlock(dep);
cid = dep->cid;
if (dep->state == ERTS_DE_STATE_EXITING
|| dep->state == ERTS_DE_STATE_IDLE
- || dep->connection_id != dsdp->connection_id) {
+ || dep->connection_id != ctx->connection_id) {
/* Not the same connection as when we started; drop message... */
erts_de_runlock(dep);
- free_dist_obuf(ctx->obuf);
+ for (i = 0; i < ctx->fragments; i++)
+ free_dist_obuf(&ctx->obuf[i]);
+ ctx->fragments = 0;
}
else {
- Sint qsize;
+ Sint qsize = erts_atomic_read_nob(&dep->qsize);
erts_aint32_t qflgs;
ErtsProcList *plp = NULL;
Eterm notify_proc = NIL;
- Sint obsz = size_obuf(ctx->obuf);
+ Sint obsz;
+ int fragments;
+
+ /* Calculate how many fragments to send. This depends on
+ the available space in the distr queue and the amount
+ of remaining reductions. */
+ for (fragments = 0, obsz = 0;
+ fragments < ctx->fragments &&
+ ((ctx->reds > 0 && (qsize + obsz) < erts_dist_buf_busy_limit) ||
+ ctx->no_trap || ctx->no_suspend);
+ fragments++) {
+#ifdef DEBUG
+ int reds = 100;
+#else
+ int reds = 10;
+#endif
+ if (!ctx->no_trap && !ctx->no_suspend)
+ ctx->reds -= reds;
+ obsz += size_obuf(&ctx->obuf[fragments]);
+ }
+
+ ASSERT(fragments == ctx->fragments ||
+ (!ctx->no_trap && !ctx->no_suspend));
erts_mtx_lock(&dep->qlock);
qsize = erts_atomic_add_read_nob(&dep->qsize, (erts_aint_t) obsz);
@@ -1944,7 +2566,7 @@ erts_dsig_send(ErtsDSigData *dsdp, struct erts_dsig_send_context* ctx)
/* else: requester will send itself the message... */
qflgs &= ~ERTS_DE_QFLG_REQ_INFO;
}
- if (!ctx->force_busy && (qflgs & ERTS_DE_QFLG_BUSY)) {
+ if (!ctx->no_suspend && (qflgs & ERTS_DE_QFLG_BUSY)) {
erts_mtx_unlock(&dep->qlock);
plp = erts_proclist_create(ctx->c_p);
@@ -1953,14 +2575,27 @@ erts_dsig_send(ErtsDSigData *dsdp, struct erts_dsig_send_context* ctx)
erts_mtx_lock(&dep->qlock);
}
- /* Enqueue obuf on dist entry */
- if (dep->out_queue.last)
- dep->out_queue.last->next = ctx->obuf;
- else
- dep->out_queue.first = ctx->obuf;
- dep->out_queue.last = ctx->obuf;
+ if (fragments > 1) {
+ if (!ctx->obuf->hdrp) {
+ ASSERT(get_int64(ctx->obuf->extp + 10) == ctx->fragments);
+ } else {
+ ASSERT(get_int64(ctx->obuf->hdrp + 10) == ctx->fragments);
+ }
+ }
+
+ if (fragments) {
+ ctx->obuf[fragments-1].next = NULL;
+ if (dep->out_queue.last)
+ dep->out_queue.last->next = ctx->obuf;
+ else
+ dep->out_queue.first = ctx->obuf;
+ dep->out_queue.last = &ctx->obuf[fragments-1];
- if (!ctx->force_busy) {
+ ctx->fragments -= fragments;
+ ctx->obuf = &ctx->obuf[fragments];
+ }
+
+ if (!ctx->no_suspend) {
qflgs = erts_atomic32_read_nob(&dep->qflgs);
if (!(qflgs & ERTS_DE_QFLG_BUSY)) {
if (suspended)
@@ -2007,6 +2642,13 @@ erts_dsig_send(ErtsDSigData *dsdp, struct erts_dsig_send_context* ctx)
* erroneously scheduled when it shouldn't be.
*/
}
+ /* More fragments left to be sent, yield and re-schedule */
+ if (ctx->fragments) {
+ retval = ERTS_DSIG_SEND_CONTINUE;
+ if (!resume && erts_system_monitor_flags.busy_dist_port)
+ monitor_generic(ctx->c_p, am_busy_dist_port, cid);
+ goto done;
+ }
}
ctx->obuf = NULL;
@@ -2091,9 +2733,9 @@ static Uint
dist_port_commandv(Port *prt, ErtsDistOutputBuf *obuf)
{
int fpe_was_unmasked;
- ErlDrvSizeT size;
- SysIOVec iov[2];
- ErlDrvBinary* bv[2];
+ ErlDrvSizeT size = 0;
+ SysIOVec iov[3];
+ ErlDrvBinary* bv[3];
ErlIOVec eiov;
ERTS_CHK_NO_PROC_LOCKS;
@@ -2108,12 +2750,31 @@ dist_port_commandv(Port *prt, ErtsDistOutputBuf *obuf)
eiov.vsize = 1;
}
else {
- size = obuf->ext_endp - obuf->extp;
+ int i = 1;
eiov.vsize = 2;
- iov[1].iov_base = obuf->extp;
- iov[1].iov_len = size;
- bv[1] = Binary2ErlDrvBinary(ErtsDistOutputBuf2Binary(obuf));
+ if (obuf->hdrp) {
+ eiov.vsize = 3;
+ iov[i].iov_base = obuf->hdrp;
+ iov[i].iov_len = obuf->hdr_endp - obuf->hdrp;
+ size += iov[i].iov_len;
+ bv[i] = Binary2ErlDrvBinary(ErtsDistOutputBuf2Binary(obuf));
+#ifdef ERTS_RAW_DIST_MSG_DBG
+ erts_fprintf(dbg_file, "SEND: ");
+ bw(iov[i].iov_base, iov[i].iov_len);
+#endif
+ i++;
+
+ }
+
+ iov[i].iov_base = obuf->extp;
+ iov[i].iov_len = obuf->ext_endp - obuf->extp;
+#ifdef ERTS_RAW_DIST_MSG_DBG
+ erts_fprintf(dbg_file, "SEND: ");
+ bw(iov[i].iov_base, iov[i].iov_len);
+#endif
+ size += iov[i].iov_len;
+ bv[i] = Binary2ErlDrvBinary(ErtsDistOutputBuf2Binary(obuf));
}
eiov.size = size;
@@ -2220,6 +2881,23 @@ erts_dist_command(Port *prt, int initial_reds)
dep->finalized_out_queue.first = NULL;
dep->finalized_out_queue.last = NULL;
+#ifdef DEBUG
+ {
+ Uint sz = 0;
+ ErtsDistOutputBuf *curr = oq.first;
+ while (curr) {
+ sz += size_obuf(curr);
+ curr = curr->next;
+ }
+ curr = foq.first;
+ while (curr) {
+ sz += size_obuf(curr);
+ curr = curr->next;
+ }
+ ASSERT(sz <= erts_atomic_read_nob(&dep->qsize));
+ }
+#endif
+
sched_flags = erts_atomic32_read_nob(&prt->sched.flags);
if (reds < 0)
@@ -2233,10 +2911,6 @@ erts_dist_command(Port *prt, int initial_reds)
size = (*send)(prt, foq.first);
erts_atomic64_inc_nob(&dep->out);
esdp->io.out += (Uint64) size;
-#ifdef ERTS_RAW_DIST_MSG_DBG
- erts_fprintf(stderr, ">> ");
- bw(foq.first->extp, size);
-#endif
reds -= ERTS_PORT_REDS_DIST_CMD_DATA(size);
fob = foq.first;
obufsize += size_obuf(fob);
@@ -2305,15 +2979,11 @@ erts_dist_command(Port *prt, int initial_reds)
preempt = 1;
break;
}
- ASSERT(&oq.first->data[0] <= oq.first->extp
- && oq.first->extp <= oq.first->ext_endp);
+ ASSERT(oq.first->bin->orig_bytes <= (char*)oq.first->extp
+ && oq.first->extp <= oq.first->ext_endp);
size = (*send)(prt, oq.first);
erts_atomic64_inc_nob(&dep->out);
esdp->io.out += (Uint64) size;
-#ifdef ERTS_RAW_DIST_MSG_DBG
- erts_fprintf(stderr, ">> ");
- bw(oq.first->extp, size);
-#endif
reds -= ERTS_PORT_REDS_DIST_CMD_DATA(size);
fob = oq.first;
obufsize += size_obuf(fob);
@@ -2450,100 +3120,6 @@ erts_dist_command(Port *prt, int initial_reds)
goto done;
}
-#if 0
-
-int
-dist_data_finalize(Process *c_p, int reds_limit)
-{
- int reds = 5;
- DistEntry *dep = ;
- ErtsDistOutputQueue oq, foq;
- ErtsDistOutputBuf *ob;
- int preempt;
-
-
- erts_mtx_lock(&dep->qlock);
- flags = dep->flags;
- oq.first = dep->out_queue.first;
- oq.last = dep->out_queue.last;
- dep->out_queue.first = NULL;
- dep->out_queue.last = NULL;
- erts_mtx_unlock(&dep->qlock);
-
- if (!oq.first) {
- ASSERT(!oq.last);
- oq.first = dep->tmp_out_queue.first;
- oq.last = dep->tmp_out_queue.last;
- }
- else {
- ErtsDistOutputBuf *f, *l;
- ASSERT(oq.last);
- if (dep->tmp_out_queue.last) {
- dep->tmp_out_queue.last->next = oq.first;
- oq.first = dep->tmp_out_queue.first;
- }
- }
-
- if (!oq.first) {
- /* Nothing to do... */
- ASSERT(!oq.last);
- return reds;
- }
-
- foq.first = dep->finalized_out_queue.first;
- foq.last = dep->finalized_out_queue.last;
-
- preempt = 0;
- ob = oq.first;
- ASSERT(ob);
-
- do {
- ob->extp = erts_encode_ext_dist_header_finalize(ob->extp,
- dep->cache,
- flags);
- if (!(flags & DFLAG_DIST_HDR_ATOM_CACHE))
- *--ob->extp = PASS_THROUGH; /* Old node; 'pass through'
- needed */
- ASSERT(&ob->data[0] <= ob->extp && ob->extp < ob->ext_endp);
- reds += ERTS_PORT_REDS_DIST_CMD_FINALIZE;
- preempt = reds > reds_limit;
- if (preempt)
- break;
- ob = ob->next;
- } while (ob);
- /*
- * At least one buffer was finalized; if we got preempted,
- * ob points to the last buffer that we finalized.
- */
- if (foq.last)
- foq.last->next = oq.first;
- else
- foq.first = oq.first;
- if (!preempt) {
- /* All buffers finalized */
- foq.last = oq.last;
- oq.first = oq.last = NULL;
- }
- else {
- /* Not all buffers finalized; split oq. */
- foq.last = ob;
- oq.first = ob->next;
- if (oq.first)
- ob->next = NULL;
- else
- oq.last = NULL;
- }
-
- dep->finalized_out_queue.first = foq.first;
- dep->finalized_out_queue.last = foq.last;
- dep->tmp_out_queue.first = oq.first;
- dep->tmp_out_queue.last = oq.last;
-
- return reds;
-}
-
-#endif
-
BIF_RETTYPE
dist_ctrl_get_data_notification_1(BIF_ALIST_1)
{
@@ -2612,6 +3188,7 @@ dist_ctrl_put_data_2(BIF_ALIST_2)
ErlDrvSizeT size;
Eterm input_handler;
Uint32 conn_id;
+ Binary *bin = NULL;
if (is_binary(BIF_ARG_2))
size = binary_size(BIF_ARG_2);
@@ -2637,13 +3214,27 @@ dist_ctrl_put_data_2(BIF_ALIST_2)
if (size != 0) {
byte *data, *temp_alloc = NULL;
- data = (byte *) erts_get_aligned_binary_bytes(BIF_ARG_2, &temp_alloc);
+ if (binary_bitoffset(BIF_ARG_2))
+ data = (byte *) erts_get_aligned_binary_bytes(BIF_ARG_2, &temp_alloc);
+ else {
+ Eterm real_bin;
+ ProcBin *proc_bin;
+ Uint offset, bitoffs, bitsize;
+
+ ERTS_GET_REAL_BIN(BIF_ARG_2, real_bin, offset, bitoffs, bitsize);
+ ASSERT(bitoffs == 0);
+ data = binary_bytes(real_bin) + offset;
+ proc_bin = (ProcBin *)binary_val(real_bin);
+ if (proc_bin->thing_word == HEADER_PROC_BIN)
+ bin = proc_bin->val;
+ }
+
if (!data)
BIF_ERROR(BIF_P, BADARG);
erts_proc_unlock(BIF_P, ERTS_PROC_LOCK_MAIN);
- (void) erts_net_message(NULL, dep, conn_id, NULL, 0, data, size);
+ (void) erts_net_message(NULL, dep, conn_id, NULL, 0, bin, data, size);
/*
* We ignore any decode failures. On fatal failures the
* connection will be taken down by killing the
@@ -2877,6 +3468,10 @@ static void kill_connection(DistEntry *dep)
void
erts_kill_dist_connection(DistEntry *dep, Uint32 conn_id)
{
+#ifdef ERTS_DIST_MSG_DBG
+ erts_fprintf(dbg_file, "INTR: kill dist conn to %T:%u\n",
+ dep->sysname, conn_id);
+#endif
erts_de_rwlock(dep);
if (conn_id == dep->connection_id
&& dep->state == ERTS_DE_STATE_CONNECTED) {
@@ -2891,7 +3486,7 @@ struct print_to_data {
void *arg;
};
-static void doit_print_monitor_info(ErtsMonitor *mon, void *vptdp)
+static int doit_print_monitor_info(ErtsMonitor *mon, void *vptdp, Sint reds)
{
fmtfn_t to = ((struct print_to_data *) vptdp)->to;
void *arg = ((struct print_to_data *) vptdp)->arg;
@@ -2914,6 +3509,7 @@ static void doit_print_monitor_info(ErtsMonitor *mon, void *vptdp)
else
erts_print(to, arg, "%T\n", mdep->md.origin.other.item);
}
+ return 1;
}
static void print_monitor_info(fmtfn_t to, void *arg, DistEntry *dep)
@@ -2929,12 +3525,13 @@ static void print_monitor_info(fmtfn_t to, void *arg, DistEntry *dep)
}
}
-static void doit_print_link_info(ErtsLink *lnk, void *vptdp)
+static int doit_print_link_info(ErtsLink *lnk, void *vptdp, Sint reds)
{
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);
+ return 1;
}
static void print_link_info(fmtfn_t to, void *arg, DistEntry *dep)
@@ -3536,8 +4133,9 @@ Sint erts_abort_connection_rwunlock(DistEntry* dep)
erts_set_dist_entry_not_connected(dep);
erts_de_rwunlock(dep);
- schedule_con_monitor_link_cleanup(mld, THE_NON_VALUE,
- THE_NON_VALUE, THE_NON_VALUE);
+ schedule_con_monitor_link_seq_cleanup(
+ mld, NULL, THE_NON_VALUE,
+ THE_NON_VALUE, THE_NON_VALUE);
if (resume_procs) {
int resumed = erts_resume_processes(resume_procs);
@@ -3843,16 +4441,16 @@ monitor_node(Process* p, Eterm Node, Eterm Bool, Eterm Options)
}
case am_true: {
- ErtsDSigData dsd;
- dsd.node = Node;
+ ErtsDSigSendContext ctx;
+ ctx.node = Node;
dep = erts_find_or_insert_dist_entry(Node);
if (dep == erts_this_dist_entry)
break;
- switch (erts_dsig_prepare(&dsd, dep, p,
+ switch (erts_dsig_prepare(&ctx, dep, p,
ERTS_PROC_LOCK_MAIN,
- ERTS_DSP_RLOCK, 0, async_connect)) {
+ ERTS_DSP_RLOCK, 0, 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 */
@@ -4009,6 +4607,10 @@ init_nodes_monitors(void)
{
erts_mtx_init(&nodes_monitors_mtx, "nodes_monitors", NIL,
ERTS_LOCK_FLAGS_PROPERTY_STATIC | ERTS_LOCK_FLAGS_CATEGORY_DISTRIBUTION);
+#ifdef DEBUG
+ erts_mtx_init(&erts_obuf_list_mtx, "sad", NIL,
+ ERTS_LOCK_FLAGS_PROPERTY_STATIC | ERTS_LOCK_FLAGS_CATEGORY_DISTRIBUTION);
+#endif
nodes_monitors = NULL;
no_nodes_monitors = 0;
}
@@ -4150,8 +4752,8 @@ typedef struct {
Uint i;
} ErtsNodesMonitorContext;
-static void
-save_nodes_monitor(ErtsMonitor *mon, void *vctxt)
+static int
+save_nodes_monitor(ErtsMonitor *mon, void *vctxt, Sint reds)
{
ErtsNodesMonitorContext *ctxt = vctxt;
ErtsMonitorData *mdp = erts_monitor_to_data(mon);
@@ -4163,6 +4765,7 @@ save_nodes_monitor(ErtsMonitor *mon, void *vctxt)
ctxt->nmdp[ctxt->i].options = mdp->origin.other.item;
ctxt->i++;
+ return 1;
}
static void
@@ -4295,8 +4898,8 @@ typedef struct {
} ErtsNodesMonitorInfoContext;
-static void
-nodes_monitor_info(ErtsMonitor *mon, void *vctxt)
+static int
+nodes_monitor_info(ErtsMonitor *mon, void *vctxt, Sint reds)
{
ErtsMonitorDataExtended *mdep;
ErtsNodesMonitorInfoContext *ctxt = vctxt;
@@ -4343,6 +4946,7 @@ nodes_monitor_info(ErtsMonitor *mon, void *vctxt)
ctxt->hpp = hpp;
ctxt->szp = szp;
ctxt->res = res;
+ return 1;
}
Eterm
diff --git a/erts/emulator/beam/dist.h b/erts/emulator/beam/dist.h
index 845fab229a..c4bb967592 100644
--- a/erts/emulator/beam/dist.h
+++ b/erts/emulator/beam/dist.h
@@ -47,6 +47,8 @@
#define DFLAG_SEND_SENDER 0x80000
#define DFLAG_BIG_SEQTRACE_LABELS 0x100000
#define DFLAG_NO_MAGIC 0x200000 /* internal for pending connection */
+#define DFLAG_EXIT_PAYLOAD 0x400000
+#define DFLAG_FRAGMENTS 0x800000
/* Mandatory flags for distribution */
#define DFLAG_DIST_MANDATORY (DFLAG_EXTENDED_REFERENCES \
@@ -75,7 +77,9 @@
| DFLAG_MAP_TAG \
| DFLAG_BIG_CREATION \
| DFLAG_SEND_SENDER \
- | DFLAG_BIG_SEQTRACE_LABELS)
+ | DFLAG_BIG_SEQTRACE_LABELS \
+ | DFLAG_EXIT_PAYLOAD \
+ | DFLAG_FRAGMENTS)
/* Flags addable by local distr implementations */
#define DFLAG_DIST_ADDABLE DFLAG_DIST_DEFAULT
@@ -99,26 +103,35 @@
| DFLAG_BIG_CREATION)
/* opcodes used in distribution messages */
-#define DOP_LINK 1
-#define DOP_SEND 2
-#define DOP_EXIT 3
-#define DOP_UNLINK 4
+enum dop {
+ DOP_LINK = 1,
+ DOP_SEND = 2,
+ DOP_EXIT = 3,
+ DOP_UNLINK = 4,
/* Ancient DOP_NODE_LINK (5) was here, can be reused */
-#define DOP_REG_SEND 6
-#define DOP_GROUP_LEADER 7
-#define DOP_EXIT2 8
-
-#define DOP_SEND_TT 12
-#define DOP_EXIT_TT 13
-#define DOP_REG_SEND_TT 16
-#define DOP_EXIT2_TT 18
-
-#define DOP_MONITOR_P 19
-#define DOP_DEMONITOR_P 20
-#define DOP_MONITOR_P_EXIT 21
-
-#define DOP_SEND_SENDER 22
-#define DOP_SEND_SENDER_TT 23
+ DOP_REG_SEND = 6,
+ DOP_GROUP_LEADER = 7,
+ DOP_EXIT2 = 8,
+
+ DOP_SEND_TT = 12,
+ DOP_EXIT_TT = 13,
+ DOP_REG_SEND_TT = 16,
+ DOP_EXIT2_TT = 18,
+
+ DOP_MONITOR_P = 19,
+ DOP_DEMONITOR_P = 20,
+ DOP_MONITOR_P_EXIT = 21,
+
+ DOP_SEND_SENDER = 22,
+ DOP_SEND_SENDER_TT = 23,
+
+ /* These are used when DFLAG_EXIT_PAYLOAD is detected */
+ DOP_PAYLOAD_EXIT = 24,
+ DOP_PAYLOAD_EXIT_TT = 25,
+ DOP_PAYLOAD_EXIT2 = 26,
+ DOP_PAYLOAD_EXIT2_TT = 27,
+ DOP_PAYLOAD_MONITOR_P_EXIT = 28
+};
/* distribution trap functions */
extern Export* dmonitor_node_trap;
@@ -129,15 +142,15 @@ typedef enum {
} ErtsDSigPrepLock;
-typedef struct {
- Process *proc;
- DistEntry *dep;
- Eterm node; /* used if dep == NULL */
- Eterm cid;
- Eterm connection_id;
- int no_suspend;
- Uint32 flags;
-} ErtsDSigData;
+/* Must be larger or equal to 16 */
+#ifdef DEBUG
+#define ERTS_DIST_FRAGMENT_SIZE 16
+#else
+/* This should be made configurable */
+#define ERTS_DIST_FRAGMENT_SIZE (64 * 1024)
+#endif
+
+#define ERTS_DIST_FRAGMENT_HEADER_SIZE (1 + 1 + 8 + 8) /* magic, header, seq id, frag id*/
#define ERTS_DE_BUSY_LIMIT (1024*1024)
extern int erts_dist_buf_busy_limit;
@@ -159,124 +172,6 @@ extern int erts_is_alive;
/* Pending connection; signals can be enqueued */
#define ERTS_DSIG_PREP_PENDING 4
-ERTS_GLB_INLINE int erts_dsig_prepare(ErtsDSigData *,
- DistEntry*,
- Process *,
- ErtsProcLocks,
- ErtsDSigPrepLock,
- int,
- int);
-
-ERTS_GLB_INLINE
-void erts_schedule_dist_command(Port *, DistEntry *);
-
-int erts_auto_connect(DistEntry* dep, Process *proc, ErtsProcLocks proc_locks);
-
-#if ERTS_GLB_INLINE_INCL_FUNC_DEF
-
-ERTS_GLB_INLINE int
-erts_dsig_prepare(ErtsDSigData *dsdp,
- DistEntry *dep,
- Process *proc,
- ErtsProcLocks proc_locks,
- ErtsDSigPrepLock dspl,
- int no_suspend,
- int connect)
-{
- int res;
-
- if (!erts_is_alive)
- return ERTS_DSIG_PREP_NOT_ALIVE;
- if (!dep) {
- ASSERT(!connect);
- return ERTS_DSIG_PREP_NOT_CONNECTED;
- }
-
-#ifdef ERTS_ENABLE_LOCK_CHECK
- if (connect) {
- erts_proc_lc_might_unlock(proc, proc_locks);
- }
-#endif
-
-retry:
- erts_de_rlock(dep);
-
- if (dep->state == ERTS_DE_STATE_CONNECTED) {
- res = ERTS_DSIG_PREP_CONNECTED;
- }
- else if (dep->state == ERTS_DE_STATE_PENDING) {
- res = ERTS_DSIG_PREP_PENDING;
- }
- else if (dep->state == ERTS_DE_STATE_EXITING) {
- res = ERTS_DSIG_PREP_NOT_CONNECTED;
- goto fail;
- }
- else if (connect) {
- 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;
- }
- goto retry;
- }
- else {
- ASSERT(dep->state == ERTS_DE_STATE_IDLE);
- res = ERTS_DSIG_PREP_NOT_CONNECTED;
- goto fail;
- }
-
- if (no_suspend) {
- if (erts_atomic32_read_acqb(&dep->qflgs) & ERTS_DE_QFLG_BUSY) {
- res = ERTS_DSIG_PREP_WOULD_SUSPEND;
- goto fail;
- }
- }
- dsdp->proc = proc;
- dsdp->dep = dep;
- dsdp->cid = dep->cid;
- dsdp->connection_id = dep->connection_id;
- dsdp->no_suspend = no_suspend;
- dsdp->flags = dep->flags;
- if (dspl == ERTS_DSP_NO_LOCK)
- erts_de_runlock(dep);
- return res;
-
- fail:
- erts_de_runlock(dep);
- return res;
-}
-
-ERTS_GLB_INLINE
-void erts_schedule_dist_command(Port *prt, DistEntry *dist_entry)
-{
- DistEntry *dep;
- Eterm id;
-
- if (prt) {
- ERTS_LC_ASSERT(erts_lc_is_port_locked(prt));
- ASSERT((erts_atomic32_read_nob(&prt->state)
- & ERTS_PORT_SFLGS_DEAD) == 0);
-
- dep = (DistEntry*) erts_prtsd_get(prt, ERTS_PRTSD_DIST_ENTRY);
- ASSERT(dep);
- id = prt->common.id;
- }
- else {
- ASSERT(dist_entry);
- ERTS_LC_ASSERT(erts_lc_rwmtx_is_rlocked(&dist_entry->rwmtx)
- || erts_lc_rwmtx_is_rwlocked(&dist_entry->rwmtx));
- ASSERT(is_internal_port(dist_entry->cid));
-
- dep = dist_entry;
- id = dep->cid;
- }
-
- if (!erts_atomic_xchg_mb(&dep->dist_cmd_scheduled, 1))
- erts_port_task_schedule(id, &dep->dist_cmd, ERTS_PORT_TASK_DIST_CMD);
-}
-
-#endif
-
#ifdef DEBUG
#define ERTS_DBG_CHK_NO_DIST_LNK(D, R, L) \
erts_dbg_chk_no_dist_proc_link((D), (R), (L))
@@ -336,41 +231,45 @@ enum erts_dsig_send_phase {
ERTS_DSIG_SEND_PHASE_MSG_SIZE,
ERTS_DSIG_SEND_PHASE_ALLOC,
ERTS_DSIG_SEND_PHASE_MSG_ENCODE,
- ERTS_DSIG_SEND_PHASE_FIN
+ ERTS_DSIG_SEND_PHASE_FIN,
+ ERTS_DSIG_SEND_PHASE_SEND
};
-struct erts_dsig_send_context {
- enum erts_dsig_send_phase phase;
- Sint reds;
+typedef struct erts_dsig_send_context {
+ int connect;
+ int no_suspend;
+ int no_trap;
Eterm ctl;
Eterm msg;
- int force_busy;
+ Eterm from;
+ Eterm ctl_heap[6];
+ Eterm return_term;
+
+ DistEntry *dep;
+ Eterm node; /* used if dep == NULL */
+ Eterm cid;
+ Eterm connection_id;
+ int deref_dep;
+
+ enum erts_dsig_send_phase phase;
+ Sint reds;
+
Uint32 max_finalize_prepend;
Uint data_size, dhdr_ext_size;
ErtsAtomCacheMap *acmp;
ErtsDistOutputBuf *obuf;
+ Uint fragments;
Uint32 flags;
Process *c_p;
union {
TTBSizeContext sc;
TTBEncodeContext ec;
}u;
-};
-typedef struct {
- int suspend;
- int connect;
-
- Eterm ctl_heap[6];
- ErtsDSigData dsd;
- DistEntry *dep;
- int deref_dep;
- struct erts_dsig_send_context dss;
-
- Eterm return_term;
-}ErtsSendContext;
+} ErtsDSigSendContext;
+typedef struct dist_sequences DistSeqNode;
/*
* erts_dsig_send_* return values.
@@ -380,21 +279,21 @@ typedef struct {
#define ERTS_DSIG_SEND_CONTINUE 2
#define ERTS_DSIG_SEND_TOO_LRG 3
-extern int erts_dsig_send_link(ErtsDSigData *, Eterm, Eterm);
-extern int erts_dsig_send_msg(Eterm, Eterm, ErtsSendContext*);
-extern int erts_dsig_send_exit_tt(ErtsDSigData *, Eterm, Eterm, Eterm, Eterm);
-extern int erts_dsig_send_unlink(ErtsDSigData *, Eterm, Eterm);
-extern int erts_dsig_send_reg_msg(Eterm, Eterm, ErtsSendContext*);
-extern int erts_dsig_send_group_leader(ErtsDSigData *, Eterm, Eterm);
-extern int erts_dsig_send_exit(ErtsDSigData *, Eterm, Eterm, Eterm);
-extern int erts_dsig_send_exit2(ErtsDSigData *, Eterm, Eterm, Eterm);
-extern int erts_dsig_send_demonitor(ErtsDSigData *, Eterm, Eterm, Eterm, int);
-extern int erts_dsig_send_monitor(ErtsDSigData *, Eterm, Eterm, Eterm);
-extern int erts_dsig_send_m_exit(ErtsDSigData *, Eterm, Eterm, Eterm, Eterm);
-
-extern int erts_dsig_send(ErtsDSigData *dsdp, struct erts_dsig_send_context* ctx);
+extern int erts_dsig_send_msg(ErtsDSigSendContext*, Eterm, Eterm);
+extern int erts_dsig_send_reg_msg(ErtsDSigSendContext*, Eterm, Eterm);
+extern int erts_dsig_send_link(ErtsDSigSendContext *, Eterm, Eterm);
+extern int erts_dsig_send_exit_tt(ErtsDSigSendContext *, Eterm, Eterm, Eterm, Eterm);
+extern int erts_dsig_send_unlink(ErtsDSigSendContext *, Eterm, Eterm);
+extern int erts_dsig_send_group_leader(ErtsDSigSendContext *, Eterm, Eterm);
+extern int erts_dsig_send_exit(ErtsDSigSendContext *, Eterm, Eterm, Eterm);
+extern int erts_dsig_send_exit2(ErtsDSigSendContext *, Eterm, Eterm, Eterm);
+extern int erts_dsig_send_demonitor(ErtsDSigSendContext *, Eterm, Eterm, Eterm);
+extern int erts_dsig_send_monitor(ErtsDSigSendContext *, Eterm, Eterm, Eterm);
+extern int erts_dsig_send_m_exit(ErtsDSigSendContext *, Eterm, Eterm, Eterm, Eterm);
+
+extern int erts_dsig_send(ErtsDSigSendContext *dsdp);
extern int erts_dsend_context_dtor(Binary*);
-extern Eterm erts_dsend_export_trap_context(Process* p, ErtsSendContext* ctx);
+extern Eterm erts_dsend_export_trap_context(Process* p, ErtsDSigSendContext* ctx);
extern int erts_dist_command(Port *prt, int reds);
extern void erts_dist_port_not_busy(Port *prt);
@@ -404,5 +303,18 @@ extern Uint erts_dist_cache_size(void);
extern Sint erts_abort_connection_rwunlock(DistEntry *dep);
+extern void erts_dist_seq_tree_foreach(
+ DistEntry *dep,
+ int (*func)(ErtsDistExternal *, void*, Sint), void *args);
+
+extern int erts_dsig_prepare(ErtsDSigSendContext *,
+ DistEntry*,
+ Process *,
+ ErtsProcLocks,
+ ErtsDSigPrepLock,
+ int,
+ int,
+ int);
+int erts_auto_connect(DistEntry* dep, Process *proc, ErtsProcLocks proc_locks);
#endif
diff --git a/erts/emulator/beam/erl_alloc.types b/erts/emulator/beam/erl_alloc.types
index 08dcc5ea9a..490a033b8a 100644
--- a/erts/emulator/beam/erl_alloc.types
+++ b/erts/emulator/beam/erl_alloc.types
@@ -275,6 +275,8 @@ type ML_DIST STANDARD SYSTEM monitor_link_dist
type PF3_ARGS SHORT_LIVED PROCESSES process_flag_3_arguments
type SETUP_CONN_ARG SHORT_LIVED PROCESSES setup_connection_argument
type LIST_TRAP SHORT_LIVED PROCESSES list_bif_trap_state
+type CONT_EXIT_TRAP SHORT_LIVED PROCESSES continue_exit_trap_state
+type SEQ_YIELD_STATE SHORT_LIVED SYSTEM dist_seq_yield_state
type ENVIRONMENT SYSTEM SYSTEM environment
diff --git a/erts/emulator/beam/erl_alloc_util.c b/erts/emulator/beam/erl_alloc_util.c
index d238d38d27..8d4464969a 100644
--- a/erts/emulator/beam/erl_alloc_util.c
+++ b/erts/emulator/beam/erl_alloc_util.c
@@ -7503,7 +7503,7 @@ static int gather_ahist_scan(Allctr_t *allocator,
return blocks_scanned;
}
-static void gather_ahist_append_result(hist_tree_t *node, void *arg)
+static int gather_ahist_append_result(hist_tree_t *node, void *arg, Sint reds)
{
gather_ahist_t *state = (gather_ahist_t*)arg;
@@ -7537,6 +7537,7 @@ static void gather_ahist_append_result(hist_tree_t *node, void *arg)
/* Plain free is intentional. */
free(node);
+ return 1;
}
static void gather_ahist_send(gather_ahist_t *state)
@@ -7595,11 +7596,11 @@ static int gather_ahist_finish(void *arg)
state->building_result = 1;
}
- if (hist_tree_rbt_foreach_destroy_yielding(&state->hist_tree,
- &gather_ahist_append_result,
- state,
- &state->hist_tree_yield,
- BLOCKSCAN_REDUCTIONS)) {
+ if (!hist_tree_rbt_foreach_destroy_yielding(&state->hist_tree,
+ &gather_ahist_append_result,
+ state,
+ &state->hist_tree_yield,
+ BLOCKSCAN_REDUCTIONS)) {
return 1;
}
@@ -7608,10 +7609,11 @@ static int gather_ahist_finish(void *arg)
return 0;
}
-static void gather_ahist_destroy_result(hist_tree_t *node, void *arg)
+static int gather_ahist_destroy_result(hist_tree_t *node, void *arg, Sint reds)
{
(void)arg;
free(node);
+ return 1;
}
static void gather_ahist_abort(void *arg)
diff --git a/erts/emulator/beam/erl_bif_binary.c b/erts/emulator/beam/erl_bif_binary.c
index ca1ba55b22..4d6d31cd76 100644
--- a/erts/emulator/beam/erl_bif_binary.c
+++ b/erts/emulator/beam/erl_bif_binary.c
@@ -2735,7 +2735,7 @@ static BIF_RETTYPE do_encode_unsigned(Process *p, Eterm uns, Eterm endianess)
dsize_t num_parts = BIG_SIZE(bigp);
Eterm res;
byte *b;
- ErtsDigit d;
+ ErtsDigit d = 0;
if(BIG_SIGN(bigp)) {
goto badarg;
@@ -2751,26 +2751,22 @@ static BIF_RETTYPE do_encode_unsigned(Process *p, Eterm uns, Eterm endianess)
if (endianess == am_big) {
Sint i,j;
j = 0;
- d = BIG_DIGIT(bigp,0);
for (i=n-1;i>=0;--i) {
- b[i] = d & 0xFF;
- if (!((++j) % sizeof(ErtsDigit))) {
+ if (!((j++) % sizeof(ErtsDigit))) {
d = BIG_DIGIT(bigp,j / sizeof(ErtsDigit));
- } else {
- d >>= 8;
}
+ b[i] = d & 0xFF;
+ d >>= 8;
}
} else {
Sint i,j;
j = 0;
- d = BIG_DIGIT(bigp,0);
for (i=0;i<n;++i) {
- b[i] = d & 0xFF;
- if (!((++j) % sizeof(ErtsDigit))) {
+ if (!((j++) % sizeof(ErtsDigit))) {
d = BIG_DIGIT(bigp,j / sizeof(ErtsDigit));
- } else {
- d >>= 8;
}
+ b[i] = d & 0xFF;
+ d >>= 8;
}
}
diff --git a/erts/emulator/beam/erl_bif_info.c b/erts/emulator/beam/erl_bif_info.c
index 6f4e34e1a8..74708b2caa 100644
--- a/erts/emulator/beam/erl_bif_info.c
+++ b/erts/emulator/beam/erl_bif_info.c
@@ -227,7 +227,7 @@ bld_magic_ref_bin_list(Uint **hpp, Uint *szp, ErlOffHeap* oh)
}).
*/
-static void do_calc_mon_size(ErtsMonitor *mon, void *vpsz)
+static int do_calc_mon_size(ErtsMonitor *mon, void *vpsz, Sint reds)
{
ErtsMonitorData *mdp = erts_monitor_to_data(mon);
Uint *psz = vpsz;
@@ -238,7 +238,8 @@ static void do_calc_mon_size(ErtsMonitor *mon, void *vpsz)
else
*psz += is_immed(mon->other.item) ? 0 : NC_HEAP_SIZE(mon->other.item);
- *psz += 9; /* CONS + 6-tuple */
+ *psz += 9; /* CONS + 6-tuple */
+ return 1;
}
typedef struct {
@@ -248,7 +249,7 @@ typedef struct {
Eterm tag;
} MonListContext;
-static void do_make_one_mon_element(ErtsMonitor *mon, void * vpmlc)
+static int do_make_one_mon_element(ErtsMonitor *mon, void * vpmlc, Sint reds)
{
ErtsMonitorData *mdp = erts_monitor_to_data(mon);
MonListContext *pmlc = vpmlc;
@@ -319,6 +320,7 @@ static void do_make_one_mon_element(ErtsMonitor *mon, void * vpmlc)
pmlc->hp += 7;
pmlc->res = CONS(pmlc->hp, tup, pmlc->res);
pmlc->hp += 2;
+ return 1;
}
static Eterm
@@ -328,7 +330,7 @@ make_monitor_list(Process *p, int tree, ErtsMonitor *root, Eterm tail)
Uint sz = 0;
MonListContext mlc;
void (*foreach)(ErtsMonitor *,
- void (*)(ErtsMonitor *, void *),
+ ErtsMonitorFunc,
void *);
foreach = tree ? erts_monitor_tree_foreach : erts_monitor_list_foreach;
@@ -354,7 +356,7 @@ make_monitor_list(Process *p, int tree, ErtsMonitor *root, Eterm tail)
}).
*/
-static void calc_lnk_size(ErtsLink *lnk, void *vpsz)
+static int calc_lnk_size(ErtsLink *lnk, void *vpsz, Sint reds)
{
Uint *psz = vpsz;
Uint sz = 0;
@@ -364,7 +366,8 @@ static void calc_lnk_size(ErtsLink *lnk, void *vpsz)
*psz += sz;
*psz += is_immed(lnk->other.item) ? 0 : size_object(lnk->other.item);
- *psz += 7; /* CONS + 4-tuple */
+ *psz += 7; /* CONS + 4-tuple */
+ return 1;
}
typedef struct {
@@ -374,7 +377,7 @@ typedef struct {
Eterm tag;
} LnkListContext;
-static void make_one_lnk_element(ErtsLink *lnk, void * vpllc)
+static int make_one_lnk_element(ErtsLink *lnk, void * vpllc, Sint reds)
{
LnkListContext *pllc = vpllc;
Eterm tup, t, pid, id;
@@ -411,6 +414,7 @@ static void make_one_lnk_element(ErtsLink *lnk, void * vpllc)
pllc->hp += 5;
pllc->res = CONS(pllc->hp, tup, pllc->res);
pllc->hp += 2;
+ return 1;
}
static Eterm
@@ -420,7 +424,7 @@ make_link_list(Process *p, int tree, ErtsLink *root, Eterm tail)
Uint sz = 0;
LnkListContext llc;
void (*foreach)(ErtsLink *,
- void (*)(ErtsLink *, void *),
+ ErtsLinkFunc,
void *);
foreach = tree ? erts_link_tree_foreach : erts_link_list_foreach;
@@ -519,16 +523,17 @@ do { \
} \
} while (0)
-static void collect_one_link(ErtsLink *lnk, void *vmicp)
+static int collect_one_link(ErtsLink *lnk, void *vmicp, Sint reds)
{
MonitorInfoCollection *micp = vmicp;
EXTEND_MONITOR_INFOS(micp);
micp->mi[micp->mi_i].entity.term = lnk->other.item;
micp->sz += 2 + NC_HEAP_SIZE(lnk->other.item);
micp->mi_i++;
+ return 1;
}
-static void collect_one_origin_monitor(ErtsMonitor *mon, void *vmicp)
+static int collect_one_origin_monitor(ErtsMonitor *mon, void *vmicp, Sint reds)
{
if (erts_monitor_is_origin(mon)) {
MonitorInfoCollection *micp = vmicp;
@@ -573,9 +578,10 @@ static void collect_one_origin_monitor(ErtsMonitor *mon, void *vmicp)
break;
}
}
+ return 1;
}
-static void collect_one_target_monitor(ErtsMonitor *mon, void *vmicp)
+static int collect_one_target_monitor(ErtsMonitor *mon, void *vmicp, Sint reds)
{
MonitorInfoCollection *micp = vmicp;
@@ -612,8 +618,8 @@ static void collect_one_target_monitor(ErtsMonitor *mon, void *vmicp)
default:
break;
}
-
}
+ return 1;
}
typedef struct {
@@ -653,8 +659,8 @@ do { \
} \
} while (0)
-static void
-collect_one_suspend_monitor(ErtsMonitor *mon, void *vsmicp)
+static int
+collect_one_suspend_monitor(ErtsMonitor *mon, void *vsmicp, Sint reds)
{
if (mon->type == ERTS_MON_TYPE_SUSPEND) {
Sint count;
@@ -678,6 +684,7 @@ collect_one_suspend_monitor(ErtsMonitor *mon, void *vsmicp)
smicp->smi_i++;
}
+ return 1;
}
/*
@@ -3144,14 +3151,16 @@ BIF_RETTYPE system_info_1(BIF_ALIST_1)
BIF_ERROR(BIF_P, BADARG);
}
-static void monitor_size(ErtsMonitor *mon, void *vsz)
+static int monitor_size(ErtsMonitor *mon, void *vsz, Sint reds)
{
*((Uint *) vsz) = erts_monitor_size(mon);
+ return 1;
}
-static void link_size(ErtsMonitor *lnk, void *vsz)
+static int link_size(ErtsMonitor *lnk, void *vsz, Sint reds)
{
*((Uint *) vsz) = erts_link_size(lnk);
+ return 1;
}
/**********************************************************************/
diff --git a/erts/emulator/beam/erl_bif_persistent.c b/erts/emulator/beam/erl_bif_persistent.c
index 9dca768a18..5a78a043ce 100644
--- a/erts/emulator/beam/erl_bif_persistent.c
+++ b/erts/emulator/beam/erl_bif_persistent.c
@@ -332,6 +332,23 @@ BIF_RETTYPE persistent_term_get_1(BIF_ALIST_1)
BIF_ERROR(BIF_P, BADARG);
}
+BIF_RETTYPE persistent_term_get_2(BIF_ALIST_2)
+{
+ Eterm key = BIF_ARG_1;
+ Eterm result = BIF_ARG_2;
+ HashTable* hash_table = (HashTable *) erts_atomic_read_nob(&the_hash_table);
+ Uint entry_index;
+ Eterm term;
+
+ entry_index = lookup(hash_table, key);
+ term = hash_table->term[entry_index];
+ if (is_boxed(term)) {
+ ASSERT(is_tuple_arity(term, 2));
+ result = tuple_val(term)[2];
+ }
+ BIF_RET(result);
+}
+
BIF_RETTYPE persistent_term_erase_1(BIF_ALIST_1)
{
Eterm key = BIF_ARG_1;
diff --git a/erts/emulator/beam/erl_db.c b/erts/emulator/beam/erl_db.c
index df6f42edd3..4132a54934 100644
--- a/erts/emulator/beam/erl_db.c
+++ b/erts/emulator/beam/erl_db.c
@@ -3701,7 +3701,7 @@ static SWord proc_cleanup_fixed_table(Process* p, DbFixation* fix)
/*
* erts_db_process_exiting() is called when a process terminates.
* It returns 0 when completely done, and !0 when it wants to
- * yield. c_p->u.terminate can hold a pointer to a state while
+ * yield. *yield_state can hold a pointer to a state while
* yielding.
*/
#define ERTS_DB_INTERNAL_ERROR(LSTR) \
@@ -3709,7 +3709,7 @@ static SWord proc_cleanup_fixed_table(Process* p, DbFixation* fix)
__FILE__, __LINE__)
int
-erts_db_process_exiting(Process *c_p, ErtsProcLocks c_p_locks)
+erts_db_process_exiting(Process *c_p, ErtsProcLocks c_p_locks, void **yield_state)
{
typedef struct {
enum {
@@ -3719,7 +3719,7 @@ erts_db_process_exiting(Process *c_p, ErtsProcLocks c_p_locks)
}op;
DbTable *tb;
} CleanupState;
- CleanupState *state = (CleanupState *) c_p->u.terminate;
+ CleanupState *state = (CleanupState *) *yield_state;
Eterm pid = c_p->common.id;
CleanupState default_state;
SWord initial_reds = ERTS_BIF_REDS_LEFT(c_p);
@@ -3792,7 +3792,7 @@ erts_db_process_exiting(Process *c_p, ErtsProcLocks c_p_locks)
if (state != &default_state)
erts_free(ERTS_ALC_T_DB_PROC_CLEANUP, state);
- c_p->u.terminate = NULL;
+ *yield_state = NULL;
BUMP_REDS(c_p, (initial_reds - reds));
return 0;
@@ -3812,12 +3812,12 @@ erts_db_process_exiting(Process *c_p, ErtsProcLocks c_p_locks)
yield:
if (state == &default_state) {
- c_p->u.terminate = erts_alloc(ERTS_ALC_T_DB_PROC_CLEANUP,
- sizeof(CleanupState));
- sys_memcpy(c_p->u.terminate, (void*) state, sizeof(CleanupState));
+ *yield_state = erts_alloc(ERTS_ALC_T_DB_PROC_CLEANUP,
+ sizeof(CleanupState));
+ sys_memcpy(*yield_state, (void*) state, sizeof(CleanupState));
}
else
- ASSERT(state == c_p->u.terminate);
+ ASSERT(state == *yield_state);
return !0;
}
@@ -3912,7 +3912,7 @@ struct free_fixations_ctx
SWord cnt;
};
-static void free_fixations_op(DbFixation* fix, void* vctx)
+static int free_fixations_op(DbFixation* fix, void* vctx, Sint reds)
{
struct free_fixations_ctx* ctx = (struct free_fixations_ctx*) vctx;
erts_aint_t diff;
@@ -3949,6 +3949,7 @@ static void free_fixations_op(DbFixation* fix, void* vctx)
ERTS_ETS_MISC_MEM_ADD(-sizeof(DbFixation));
}
ctx->cnt++;
+ return 1;
}
int erts_db_execute_free_fixation(Process* p, DbFixation* fix)
@@ -4093,7 +4094,7 @@ struct fixing_procs_info_ctx
Eterm list;
};
-static void fixing_procs_info_op(DbFixation* fix, void* vctx)
+static int fixing_procs_info_op(DbFixation* fix, void* vctx, Sint reds)
{
struct fixing_procs_info_ctx* ctx = (struct fixing_procs_info_ctx*) vctx;
Eterm* hp;
@@ -4103,6 +4104,7 @@ static void fixing_procs_info_op(DbFixation* fix, void* vctx)
tpl = TUPLE2(hp, fix->procs.p->common.id, make_small(fix->counter));
hp += 3;
ctx->list = CONS(hp, tpl, ctx->list);
+ return 1;
}
static Eterm table_info(Process* p, DbTable* tb, Eterm What)
diff --git a/erts/emulator/beam/erl_db.h b/erts/emulator/beam/erl_db.h
index 5955d42aae..dc77fbb60c 100644
--- a/erts/emulator/beam/erl_db.h
+++ b/erts/emulator/beam/erl_db.h
@@ -111,7 +111,7 @@ typedef enum {
} ErtsDbSpinCount;
void init_db(ErtsDbSpinCount);
-int erts_db_process_exiting(Process *, ErtsProcLocks);
+int erts_db_process_exiting(Process *, ErtsProcLocks, void **);
int erts_db_execute_free_fixation(Process*, DbFixation*);
void db_info(fmtfn_t, void *, int);
void erts_db_foreach_table(void (*)(DbTable *, void *), void *);
diff --git a/erts/emulator/beam/erl_db_util.c b/erts/emulator/beam/erl_db_util.c
index ec7442aeeb..1ea7074d21 100644
--- a/erts/emulator/beam/erl_db_util.c
+++ b/erts/emulator/beam/erl_db_util.c
@@ -3312,7 +3312,7 @@ void db_cleanup_offheap_comp(DbTerm* obj)
default:
ASSERT(is_external_header(u.hdr->thing_word));
ASSERT(u.pb != &tmp);
- erts_deref_node_entry(u.ext->node);
+ erts_deref_node_entry(u.ext->node, make_boxed(u.ep));
break;
}
}
diff --git a/erts/emulator/beam/erl_gc.c b/erts/emulator/beam/erl_gc.c
index 3a50b294d1..9317850d96 100644
--- a/erts/emulator/beam/erl_gc.c
+++ b/erts/emulator/beam/erl_gc.c
@@ -1303,7 +1303,8 @@ erts_garbage_collect_literals(Process* p, Eterm* literals,
ExternalThing *etp;
ASSERT(is_external_header(ptr->thing_word));
etp = (ExternalThing *) ptr;
- erts_refc_inc(&etp->node->refc, 1);
+ erts_ref_node_entry(etp->node, 1,
+ make_boxed(&oh->thing_word));
break;
}
}
@@ -2836,7 +2837,11 @@ sweep_off_heap(Process *p, int fullsweep)
while (ptr) {
if (IS_MOVED_BOXED(ptr->thing_word)) {
ASSERT(!ErtsInArea(ptr, oheap, oheap_sz));
- *prev = ptr = (struct erl_off_heap_header*) boxed_val(ptr->thing_word);
+ if (is_external_header(((struct erl_off_heap_header*) boxed_val(ptr->thing_word))->thing_word))
+ erts_node_bookkeep(((ExternalThing*)ptr)->node,
+ make_boxed(&ptr->thing_word),
+ ERL_NODE_DEC);
+ *prev = ptr = (struct erl_off_heap_header*) boxed_val(ptr->thing_word);
ASSERT(!IS_MOVED_BOXED(ptr->thing_word));
switch (ptr->thing_word) {
case HEADER_PROC_BIN: {
@@ -2863,6 +2868,11 @@ sweep_off_heap(Process *p, int fullsweep)
/* fall through... */
}
default:
+ if (is_external_header(ptr->thing_word)) {
+ erts_node_bookkeep(((ExternalThing*)ptr)->node,
+ make_boxed(&ptr->thing_word),
+ ERL_NODE_INC);
+ }
prev = &ptr->next;
ptr = ptr->next;
}
@@ -2896,7 +2906,8 @@ sweep_off_heap(Process *p, int fullsweep)
}
default:
ASSERT(is_external_header(ptr->thing_word));
- erts_deref_node_entry(((ExternalThing*)ptr)->node);
+ erts_deref_node_entry(((ExternalThing*)ptr)->node,
+ make_boxed(&ptr->thing_word));
}
*prev = ptr = ptr->next;
}
@@ -3029,6 +3040,13 @@ offset_heap(Eterm* hp, Uint sz, Sint offs, char* area, Uint area_size)
{
struct erl_off_heap_header* oh = (struct erl_off_heap_header*) hp;
+ if (is_external_header(oh->thing_word)) {
+ erts_node_bookkeep(((ExternalThing*)oh)->node,
+ make_boxed(((Eterm*)oh)-offs), ERL_NODE_DEC);
+ erts_node_bookkeep(((ExternalThing*)oh)->node,
+ make_boxed((Eterm*)oh), ERL_NODE_INC);
+ }
+
if (ErtsInArea(oh->next, area, area_size)) {
Eterm** uptr = (Eterm **) (void *) &oh->next;
*uptr += offs; /* Patch the mso chain */
diff --git a/erts/emulator/beam/erl_hl_timer.c b/erts/emulator/beam/erl_hl_timer.c
index 75ad6de2c9..b0eb0e85c0 100644
--- a/erts/emulator/beam/erl_hl_timer.c
+++ b/erts/emulator/beam/erl_hl_timer.c
@@ -2263,9 +2263,10 @@ parse_bif_timer_options(Eterm option_list, int *async,
return 1;
}
-static void
-exit_cancel_bif_timer(ErtsBifTimer *tmr, void *vesdp)
+static int
+exit_cancel_bif_timer(ErtsBifTimer *tmr, void *vesdp, Sint reds)
{
+#define ERTS_BTM_CANCEL_REDS 80
ErtsSchedulerData *esdp = (ErtsSchedulerData *) vesdp;
Uint32 sid, roflgs;
erts_aint_t state;
@@ -2290,7 +2291,7 @@ exit_cancel_bif_timer(ErtsBifTimer *tmr, void *vesdp)
if (sid != (Uint32) esdp->no) {
queue_canceled_timer(esdp, sid, (ErtsTimer *) tmr);
- return;
+ return ERTS_BTM_CANCEL_REDS;
}
if (tmr->btm.tree.parent != ERTS_HLT_PFIELD_NOT_IN_TABLE) {
@@ -2306,14 +2307,9 @@ exit_cancel_bif_timer(ErtsBifTimer *tmr, void *vesdp)
hl_timer_dec_refc(&tmr->type.hlt, roflgs);
else
tw_timer_dec_refc(&tmr->type.twt);
+ return ERTS_BTM_CANCEL_REDS;
}
-#ifdef ERTS_HLT_DEBUG
-# define ERTS_BTM_MAX_DESTROY_LIMIT 2
-#else
-# define ERTS_BTM_MAX_DESTROY_LIMIT 50
-#endif
-
typedef struct {
ErtsBifTimers *bif_timers;
union {
@@ -2321,10 +2317,9 @@ typedef struct {
} u;
} ErtsBifTimerYieldState;
-int erts_cancel_bif_timers(Process *p, ErtsBifTimers **btm, void **vyspp)
+int erts_cancel_bif_timers(Process *p, ErtsBifTimers **btm, void **vyspp, int reds)
{
ErtsSchedulerData *esdp = erts_proc_sched_data(p);
-
ErtsBifTimerYieldState ys = {*btm, {ERTS_RBT_YIELD_STAT_INITER}};
ErtsBifTimerYieldState *ysp;
int res;
@@ -2337,9 +2332,9 @@ int erts_cancel_bif_timers(Process *p, ErtsBifTimers **btm, void **vyspp)
exit_cancel_bif_timer,
(void *) esdp,
&ysp->u.proc_btm_yield_state,
- ERTS_BTM_MAX_DESTROY_LIMIT);
+ reds);
- if (res == 0) {
+ if (res > 0) {
if (ysp != &ys)
erts_free(ERTS_ALC_T_BTM_YIELD_STATE, ysp);
*vyspp = NULL;
@@ -2819,8 +2814,8 @@ btm_print(ErtsBifTimer *tmr, void *vbtmp, ErtsMonotonicTime tpos, int is_hlt)
(Sint64) left);
}
-static void
-btm_tree_print(ErtsBifTimer *tmr, void *vbtmp)
+static int
+btm_tree_print(ErtsBifTimer *tmr, void *vbtmp, Sint reds)
{
int is_hlt = !!(tmr->type.head.roflgs & ERTS_TMR_ROFLG_HLT);
ErtsMonotonicTime tpos;
@@ -2829,6 +2824,7 @@ btm_tree_print(ErtsBifTimer *tmr, void *vbtmp)
else
tpos = erts_tweel_read_timeout(&tmr->type.twt.u.tw_tmr);
btm_print(tmr, vbtmp, tpos, is_hlt);
+ return 1;
}
void
@@ -2860,8 +2856,8 @@ typedef struct {
void *arg;
} ErtsBTMForeachDebug;
-static void
-debug_btm_foreach(ErtsBifTimer *tmr, void *vbtmfd)
+static int
+debug_btm_foreach(ErtsBifTimer *tmr, void *vbtmfd, Sint reds)
{
if (erts_atomic32_read_nob(&tmr->btm.state) == ERTS_TMR_STATE_ACTIVE) {
ErtsBTMForeachDebug *btmfd = (ErtsBTMForeachDebug *) vbtmfd;
@@ -2870,6 +2866,7 @@ debug_btm_foreach(ErtsBifTimer *tmr, void *vbtmfd)
: tmr->type.head.receiver.proc->common.id);
(*btmfd->func)(id, tmr->btm.message, tmr->btm.bp, btmfd->arg);
}
+ return 1;
}
void
@@ -2918,8 +2915,8 @@ debug_callback_timer_foreach_list(ErtsHLTimer *tmr, void *vdfct)
tmr->head.u.arg);
}
-static void
-debug_callback_timer_foreach(ErtsHLTimer *tmr, void *vdfct)
+static int
+debug_callback_timer_foreach(ErtsHLTimer *tmr, void *vdfct, Sint reds)
{
ErtsDebugForeachCallbackTimer *dfct
= (ErtsDebugForeachCallbackTimer *) vdfct;
@@ -2934,6 +2931,7 @@ debug_callback_timer_foreach(ErtsHLTimer *tmr, void *vdfct)
(*dfct->func)(dfct->arg,
tmr->timeout,
tmr->head.u.arg);
+ return 1;
}
static void
@@ -2981,7 +2979,8 @@ erts_debug_callback_timer_foreach(void (*tclbk)(void *),
if (srv->yield.root)
debug_callback_timer_foreach(srv->yield.root,
- (void *) &dfct);
+ (void *) &dfct,
+ -1);
time_rbt_foreach(srv->time_tree,
debug_callback_timer_foreach,
diff --git a/erts/emulator/beam/erl_hl_timer.h b/erts/emulator/beam/erl_hl_timer.h
index e6f5e8b67d..29c873868b 100644
--- a/erts/emulator/beam/erl_hl_timer.h
+++ b/erts/emulator/beam/erl_hl_timer.h
@@ -56,7 +56,7 @@ void erts_cancel_proc_timer(Process *);
void erts_set_port_timer(Port *, Sint64);
void erts_cancel_port_timer(Port *);
Sint64 erts_read_port_timer(Port *);
-int erts_cancel_bif_timers(Process *, ErtsBifTimers **, void **);
+int erts_cancel_bif_timers(Process *, ErtsBifTimers **, void **, int);
int erts_detach_accessor_bif_timers(Process *, ErtsBifTimers *, void **);
ErtsHLTimerService *erts_create_timer_service(void);
void erts_hl_timer_init(void);
diff --git a/erts/emulator/beam/erl_init.c b/erts/emulator/beam/erl_init.c
index 12750b9aa6..82d5140d1c 100644
--- a/erts/emulator/beam/erl_init.c
+++ b/erts/emulator/beam/erl_init.c
@@ -2417,12 +2417,17 @@ erts_exit_vv(int n, int flush_async, char *fmt, va_list args1, va_list args2)
erts_exit_epilogue();
}
+void check_obuf(void);
__decl_noreturn void __noreturn erts_exit_epilogue(void)
{
int n = erts_exit_code;
sys_tty_reset(n);
+#ifdef DEBUG
+ check_obuf();
+#endif
+
if (n == ERTS_INTR_EXIT)
exit(0);
else if (n == ERTS_DUMP_EXIT)
diff --git a/erts/emulator/beam/erl_lock_check.c b/erts/emulator/beam/erl_lock_check.c
index 3aab4828cc..39eabb6710 100644
--- a/erts/emulator/beam/erl_lock_check.c
+++ b/erts/emulator/beam/erl_lock_check.c
@@ -164,7 +164,8 @@ static erts_lc_lock_order_t erts_lock_order[] = {
{ "os_monotonic_time", NULL },
{ "erts_alloc_hard_debug", NULL },
{ "hard_dbg_mseg", NULL },
- { "erts_mmap", NULL }
+ { "erts_mmap", NULL },
+ { "sad", NULL}
};
#define ERTS_LOCK_ORDER_SIZE \
diff --git a/erts/emulator/beam/erl_map.c b/erts/emulator/beam/erl_map.c
index 93816542cd..62dd85e425 100644
--- a/erts/emulator/beam/erl_map.c
+++ b/erts/emulator/beam/erl_map.c
@@ -480,7 +480,7 @@ Eterm erts_hashmap_from_array(ErtsHeapFactory* factory, Eterm *leafs, Uint n,
Eterm erts_map_from_ks_and_vs(ErtsHeapFactory *factory, Eterm *ks0, Eterm *vs0, Uint n)
{
- if (n < MAP_SMALL_MAP_LIMIT) {
+ if (n <= MAP_SMALL_MAP_LIMIT) {
Eterm *ks, *vs, *hp;
flatmap_t *mp;
Eterm keys;
diff --git a/erts/emulator/beam/erl_message.c b/erts/emulator/beam/erl_message.c
index 942bec84cf..9d40754d2d 100644
--- a/erts/emulator/beam/erl_message.c
+++ b/erts/emulator/beam/erl_message.c
@@ -181,7 +181,7 @@ erts_cleanup_offheap(ErlOffHeap *offheap)
break;
default:
ASSERT(is_external_header(u.hdr->thing_word));
- erts_deref_node_entry(u.ext->node);
+ erts_deref_node_entry(u.ext->node, make_boxed(u.ep));
break;
}
}
@@ -201,34 +201,44 @@ free_message_buffer(ErlHeapFragment* bp)
}while (bp != NULL);
}
+static void
+erts_cleanup_message(ErtsMessage *mp)
+{
+ ErlHeapFragment *bp;
+ if (ERTS_SIG_IS_EXTERNAL_MSG(mp) || ERTS_SIG_IS_NON_MSG(mp)) {
+ ErtsDistExternal *edep = erts_proc_sig_get_external(mp);
+ if (edep) {
+ erts_free_dist_ext_copy(edep);
+ if (mp->data.heap_frag == &mp->hfrag) {
+ ASSERT(ERTS_SIG_IS_EXTERNAL_MSG(mp));
+ mp->data.heap_frag = ERTS_MSG_COMBINED_HFRAG;
+ }
+ }
+ }
+
+ if (ERTS_SIG_IS_MSG(mp) && mp->data.attached != ERTS_MSG_COMBINED_HFRAG) {
+ bp = mp->data.heap_frag;
+ } else {
+ /* All non msg signals are combined HFRAG messages,
+ but we overwrite the mp->data field with the
+ nm_signal queue ptr so have to fix that here
+ before freeing it. */
+ mp->data.attached = ERTS_MSG_COMBINED_HFRAG;
+ bp = mp->hfrag.next;
+ erts_cleanup_offheap(&mp->hfrag.off_heap);
+ }
+
+ if (bp)
+ free_message_buffer(bp);
+}
+
void
erts_cleanup_messages(ErtsMessage *msgp)
{
ErtsMessage *mp = msgp;
while (mp) {
ErtsMessage *fmp;
- ErlHeapFragment *bp;
- 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);
- }
- if (mp->data.dist_ext)
- erts_free_dist_ext_copy(mp->data.dist_ext);
- }
- 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);
- }
- if (bp)
- free_message_buffer(bp);
- }
+ erts_cleanup_message(mp);
fmp = mp;
mp = mp->next;
erts_free_message(fmp);
@@ -260,6 +270,7 @@ void
erts_queue_dist_message(Process *rcvr,
ErtsProcLocks rcvr_locks,
ErtsDistExternal *dist_ext,
+ ErlHeapFragment *hfrag,
Eterm token,
Eterm from)
{
@@ -268,8 +279,26 @@ erts_queue_dist_message(Process *rcvr,
ERTS_LC_ASSERT(rcvr_locks == erts_proc_lc_my_proc_locks(rcvr));
- mp = erts_alloc_message(0, NULL);
- mp->data.dist_ext = dist_ext;
+ if (hfrag) {
+ /* Fragmented message, allocate a message reference */
+ mp = erts_alloc_message(0, NULL);
+ mp->data.heap_frag = hfrag;
+ } else {
+ /* Un-fragmented message, allocate space for
+ token and dist_ext in message. */
+ Uint dist_ext_sz = erts_dist_ext_size(dist_ext) / sizeof(Eterm);
+ Uint token_sz = size_object(token);
+ Uint sz = token_sz + dist_ext_sz;
+ Eterm *hp;
+
+ mp = erts_alloc_message(sz, &hp);
+ mp->data.heap_frag = &mp->hfrag;
+ mp->hfrag.used_size = token_sz;
+
+ erts_make_dist_ext_copy(dist_ext, erts_get_dist_ext(mp->data.heap_frag));
+
+ token = copy_struct(token, token_sz, &hp, &mp->data.heap_frag->off_heap);
+ }
ERL_MESSAGE_FROM(mp) = dist_ext->dep->sysname;
ERL_MESSAGE_TERM(mp) = THE_NON_VALUE;
@@ -493,25 +522,27 @@ Uint
erts_msg_attached_data_size_aux(ErtsMessage *msg)
{
Sint sz;
- ASSERT(is_non_value(ERL_MESSAGE_TERM(msg)));
- ASSERT(msg->data.dist_ext);
- ASSERT(msg->data.dist_ext->heap_size < 0);
-
- sz = erts_decode_dist_ext_size(msg->data.dist_ext);
- if (sz < 0) {
- /* Bad external
- * We leave the message intact in this case as it's not worth the trouble
- * to make all callers remove it from queue. It will be detected again
- * and removed from message queue later anyway.
- */
- return 0;
- }
+ ErtsDistExternal *edep = erts_get_dist_ext(msg->data.heap_frag);
+ ASSERT(ERTS_SIG_IS_EXTERNAL_MSG(msg));
- msg->data.dist_ext->heap_size = sz;
- if (is_not_nil(msg->m[1])) {
- ErlHeapFragment *heap_frag;
- heap_frag = erts_dist_ext_trailer(msg->data.dist_ext);
- sz += heap_frag->used_size;
+ if (edep->heap_size < 0) {
+
+ sz = erts_decode_dist_ext_size(edep, 1);
+ if (sz < 0) {
+ /* Bad external
+ * We leave the message intact in this case as it's not worth the trouble
+ * to make all callers remove it from queue. It will be detected again
+ * and removed from message queue later anyway.
+ */
+ return 0;
+ }
+
+ edep->heap_size = sz;
+ } else {
+ sz = edep->heap_size;
+ }
+ if (is_not_nil(ERL_MESSAGE_TOKEN(msg))) {
+ sz += msg->data.heap_frag->used_size;
}
return sz;
}
@@ -1099,80 +1130,6 @@ change_to_off_heap:
return res;
}
-int
-erts_decode_dist_message(Process *proc, ErtsProcLocks proc_locks,
- ErtsMessage *msgp, int force_off_heap)
-{
- ErtsHeapFactory factory;
- Eterm msg;
- ErlHeapFragment *bp;
- Sint need;
- int decode_in_heap_frag;
-
- decode_in_heap_frag = (force_off_heap
- || !(proc_locks & ERTS_PROC_LOCK_MAIN)
- || (proc->flags & F_OFF_HEAP_MSGQ));
-
- if (msgp->data.dist_ext->heap_size >= 0)
- need = msgp->data.dist_ext->heap_size;
- else {
- need = erts_decode_dist_ext_size(msgp->data.dist_ext);
- if (need < 0) {
- /* bad msg; remove it... */
- if (is_not_immed(ERL_MESSAGE_TOKEN(msgp))) {
- bp = erts_dist_ext_trailer(msgp->data.dist_ext);
- erts_cleanup_offheap(&bp->off_heap);
- }
- erts_free_dist_ext_copy(msgp->data.dist_ext);
- msgp->data.dist_ext = NULL;
- return 0;
- }
-
- msgp->data.dist_ext->heap_size = need;
- }
-
- if (is_not_immed(ERL_MESSAGE_TOKEN(msgp))) {
- bp = erts_dist_ext_trailer(msgp->data.dist_ext);
- need += bp->used_size;
- }
-
- if (decode_in_heap_frag)
- erts_factory_heap_frag_init(&factory, new_message_buffer(need));
- else
- erts_factory_proc_prealloc_init(&factory, proc, need);
-
- ASSERT(msgp->data.dist_ext->heap_size >= 0);
- if (is_not_immed(ERL_MESSAGE_TOKEN(msgp))) {
- ErlHeapFragment *heap_frag;
- heap_frag = erts_dist_ext_trailer(msgp->data.dist_ext);
- ERL_MESSAGE_TOKEN(msgp) = copy_struct(ERL_MESSAGE_TOKEN(msgp),
- heap_frag->used_size,
- &factory.hp,
- factory.off_heap);
- erts_cleanup_offheap(&heap_frag->off_heap);
- }
-
- msg = erts_decode_dist_ext(&factory, msgp->data.dist_ext);
- ERL_MESSAGE_TERM(msgp) = msg;
- erts_free_dist_ext_copy(msgp->data.dist_ext);
- msgp->data.attached = NULL;
-
- if (is_non_value(msg)) {
- erts_factory_undo(&factory);
- return 0;
- }
-
- erts_factory_trim_and_close(&factory, msgp->m,
- ERL_MESSAGE_REF_ARRAY_SZ);
-
- ASSERT(!msgp->data.heap_frag);
-
- if (decode_in_heap_frag)
- msgp->data.heap_frag = factory.heap_frags;
-
- return 1;
-}
-
void erts_factory_proc_init(ErtsHeapFactory* factory,
Process* p)
{
@@ -1233,7 +1190,7 @@ erts_factory_message_create(ErtsHeapFactory* factory,
int on_heap;
erts_aint32_t state;
- state = proc ? erts_atomic32_read_nob(&proc->state) : 0;
+ state = proc ? erts_atomic32_read_nob(&proc->state) : ERTS_PSFLG_OFF_HEAP_MSGQ;
if (state & ERTS_PSFLG_OFF_HEAP_MSGQ) {
msgp = erts_alloc_message(sz, &hp);
@@ -1466,8 +1423,8 @@ void erts_factory_close(ErtsHeapFactory* factory)
else
factory->message->data.heap_frag = factory->heap_frags;
- /* Fall through */
- case FACTORY_HEAP_FRAGS:
+ /* Fall through */
+ case FACTORY_HEAP_FRAGS:
bp = factory->heap_frags;
}
diff --git a/erts/emulator/beam/erl_message.h b/erts/emulator/beam/erl_message.h
index b2550814fd..4c2674394e 100644
--- a/erts/emulator/beam/erl_message.h
+++ b/erts/emulator/beam/erl_message.h
@@ -138,7 +138,7 @@ typedef struct erl_heap_fragment ErlHeapFragment;
struct erl_heap_fragment {
ErlHeapFragment* next; /* Next heap fragment */
ErlOffHeap off_heap; /* Offset heap data. */
- Uint alloc_size; /* Size in (half)words of mem */
+ Uint alloc_size; /* Size in words of mem */
Uint used_size; /* With terms to be moved to heap by GC */
Eterm mem[1]; /* Data */
};
@@ -167,7 +167,6 @@ struct erl_heap_fragment {
#define ERL_MESSAGE_REF_FIELDS__ \
ErtsMessage *next; /* Next message */ \
union { \
- ErtsDistExternal *dist_ext; \
ErlHeapFragment *heap_frag; \
void *attached; \
} data; \
@@ -438,7 +437,8 @@ ErlHeapFragment* new_message_buffer(Uint);
ErlHeapFragment* erts_resize_message_buffer(ErlHeapFragment *, Uint,
Eterm *, Uint);
void free_message_buffer(ErlHeapFragment *);
-void erts_queue_dist_message(Process*, ErtsProcLocks, ErtsDistExternal *, Eterm, Eterm);
+void erts_queue_dist_message(Process*, ErtsProcLocks, ErtsDistExternal *,
+ ErlHeapFragment *, Eterm, Eterm);
void erts_queue_message(Process*, ErtsProcLocks,ErtsMessage*, Eterm, Eterm);
void erts_queue_proc_message(Process* from,Process* to, ErtsProcLocks,ErtsMessage*, Eterm);
void erts_queue_proc_messages(Process* from, Process* to, ErtsProcLocks,
@@ -455,8 +455,6 @@ Sint erts_move_messages_off_heap(Process *c_p);
Sint erts_complete_off_heap_message_queue_change(Process *c_p);
Eterm erts_change_message_queue_management(Process *c_p, Eterm new_state);
-int erts_decode_dist_message(Process *, ErtsProcLocks, ErtsMessage *, int);
-
void erts_cleanup_messages(ErtsMessage *mp);
void *erts_alloc_message_ref(void);
@@ -585,22 +583,11 @@ ERTS_GLB_INLINE Uint erts_used_frag_sz(const ErlHeapFragment* bp)
ERTS_GLB_INLINE Uint erts_msg_attached_data_size(ErtsMessage *msg)
{
ASSERT(msg->data.attached);
- if (is_value(ERL_MESSAGE_TERM(msg))) {
- ErlHeapFragment *bp;
- bp = erts_message_to_heap_frag(msg);
- return erts_used_frag_sz(bp);
- }
- else if (msg->data.dist_ext->heap_size < 0)
- return erts_msg_attached_data_size_aux(msg);
- else {
- Uint sz = msg->data.dist_ext->heap_size;
- if (is_not_nil(ERL_MESSAGE_TOKEN(msg))) {
- ErlHeapFragment *heap_frag;
- heap_frag = erts_dist_ext_trailer(msg->data.dist_ext);
- sz += heap_frag->used_size;
- }
- return sz;
- }
+
+ if (ERTS_SIG_IS_INTERNAL_MSG(msg))
+ return erts_used_frag_sz(erts_message_to_heap_frag(msg));
+
+ return erts_msg_attached_data_size_aux(msg);
}
#endif
diff --git a/erts/emulator/beam/erl_monitor_link.c b/erts/emulator/beam/erl_monitor_link.c
index 48d9bd4ca5..1c6b4afaa3 100644
--- a/erts/emulator/beam/erl_monitor_link.c
+++ b/erts/emulator/beam/erl_monitor_link.c
@@ -191,7 +191,8 @@ ml_cmp_keys(Eterm key1, Eterm key2)
if (n1->sysname != n2->sysname)
return n1->sysname < n2->sysname ? -1 : 1;
ASSERT(n1->creation != n2->creation);
- return n1->creation < n2->creation ? -1 : 1;
+ if (n1->creation != 0 && n2->creation != 0)
+ return n1->creation < n2->creation ? -1 : 1;
}
ndw1 = external_thing_data_words(et1);
@@ -335,7 +336,7 @@ ml_rbt_delete(ErtsMonLnkNode **root, ErtsMonLnkNode *ml)
static void
ml_rbt_foreach(ErtsMonLnkNode *root,
- void (*func)(ErtsMonLnkNode *, void *),
+ ErtsMonLnkNodeFunc func,
void *arg)
{
mon_lnk_rbt_foreach(root, func, arg);
@@ -348,7 +349,7 @@ typedef struct {
static int
ml_rbt_foreach_yielding(ErtsMonLnkNode *root,
- void (*func)(ErtsMonLnkNode *, void *),
+ ErtsMonLnkNodeFunc func,
void *arg,
void **vyspp,
Sint limit)
@@ -362,7 +363,7 @@ ml_rbt_foreach_yielding(ErtsMonLnkNode *root,
ysp = &ys;
res = mon_lnk_rbt_foreach_yielding(ysp->root, func, arg,
&ysp->rbt_ystate, limit);
- if (res == 0) {
+ if (res > 0) {
if (ysp != &ys)
erts_free(ERTS_ALC_T_ML_YIELD_STATE, ysp);
*vyspp = NULL;
@@ -383,22 +384,22 @@ ml_rbt_foreach_yielding(ErtsMonLnkNode *root,
}
typedef struct {
- void (*func)(ErtsMonLnkNode *, void *);
+ ErtsMonLnkNodeFunc func;
void *arg;
} ErtsMonLnkForeachDeleteContext;
-static void
-rbt_wrap_foreach_delete(ErtsMonLnkNode *ml, void *vctxt)
+static int
+rbt_wrap_foreach_delete(ErtsMonLnkNode *ml, void *vctxt, Sint reds)
{
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);
+ return ctxt->func(ml, ctxt->arg, reds);
}
static void
ml_rbt_foreach_delete(ErtsMonLnkNode **root,
- void (*func)(ErtsMonLnkNode *, void *),
+ ErtsMonLnkNodeFunc func,
void *arg)
{
ErtsMonLnkForeachDeleteContext ctxt;
@@ -411,7 +412,7 @@ ml_rbt_foreach_delete(ErtsMonLnkNode **root,
static int
ml_rbt_foreach_delete_yielding(ErtsMonLnkNode **root,
- void (*func)(ErtsMonLnkNode *, void *),
+ ErtsMonLnkNodeFunc func,
void *arg,
void **vyspp,
Sint limit)
@@ -433,7 +434,7 @@ ml_rbt_foreach_delete_yielding(ErtsMonLnkNode **root,
(void *) &ctxt,
&ysp->rbt_ystate,
limit);
- if (res == 0) {
+ if (res > 0) {
if (ysp != &ys)
erts_free(ERTS_ALC_T_ML_YIELD_STATE, ysp);
*vyspp = NULL;
@@ -459,12 +460,11 @@ ml_rbt_foreach_delete_yielding(ErtsMonLnkNode **root,
static int
ml_dl_list_foreach_yielding(ErtsMonLnkNode *list,
- void (*func)(ErtsMonLnkNode *, void *),
+ ErtsMonLnkNodeFunc func,
void *arg,
void **vyspp,
- Sint limit)
+ Sint reds)
{
- Sint cnt = 0;
ErtsMonLnkNode *ml = (ErtsMonLnkNode *) *vyspp;
ERTS_ML_ASSERT(!ml || list);
@@ -475,28 +475,26 @@ ml_dl_list_foreach_yielding(ErtsMonLnkNode *list,
if (ml) {
do {
ERTS_ML_ASSERT(ml->flags & ERTS_ML_FLG_IN_TABLE);
- func(ml, arg);
+ reds -= func(ml, arg, reds);
ml = ml->node.list.next;
- cnt++;
- } while (ml != list && cnt < limit);
+ } while (ml != list && reds > 0);
if (ml != list) {
*vyspp = (void *) ml;
- return 1; /* yield */
+ return 0; /* yield */
}
}
*vyspp = NULL;
- return 0; /* done */
+ return reds <= 0 ? 1 : reds; /* done */
}
static int
ml_dl_list_foreach_delete_yielding(ErtsMonLnkNode **list,
- void (*func)(ErtsMonLnkNode *, void *),
+ ErtsMonLnkNodeFunc func,
void *arg,
void **vyspp,
- Sint limit)
+ Sint reds)
{
- Sint cnt = 0;
ErtsMonLnkNode *first = *list;
ErtsMonLnkNode *ml = (ErtsMonLnkNode *) *vyspp;
@@ -510,19 +508,18 @@ ml_dl_list_foreach_delete_yielding(ErtsMonLnkNode **list,
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);
+ reds -= func(ml, arg, reds);
ml = next;
- cnt++;
- } while (ml != first && cnt < limit);
+ } while (ml != first && reds > 0);
if (ml != first) {
*vyspp = (void *) ml;
- return 1; /* yield */
+ return 0; /* yield */
}
}
*vyspp = NULL;
*list = NULL;
- return 0; /* done */
+ return reds <= 0 ? 1 : reds; /* done */
}
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\
@@ -666,91 +663,91 @@ erts_monitor_tree_delete(ErtsMonitor **root, ErtsMonitor *mon)
void
erts_monitor_tree_foreach(ErtsMonitor *root,
- void (*func)(ErtsMonitor *, void *),
+ ErtsMonitorFunc func,
void *arg)
{
ml_rbt_foreach((ErtsMonLnkNode *) root,
- (void (*)(ErtsMonLnkNode*, void*)) func,
+ (ErtsMonLnkNodeFunc) func,
arg);
}
int
erts_monitor_tree_foreach_yielding(ErtsMonitor *root,
- void (*func)(ErtsMonitor *, void *),
+ ErtsMonitorFunc func,
void *arg,
void **vyspp,
Sint limit)
{
return ml_rbt_foreach_yielding((ErtsMonLnkNode *) root,
- (void (*)(ErtsMonLnkNode*, void*)) func,
+ (int (*)(ErtsMonLnkNode*, void*, Sint)) func,
arg, vyspp, limit);
}
void
erts_monitor_tree_foreach_delete(ErtsMonitor **root,
- void (*func)(ErtsMonitor *, void *),
+ ErtsMonitorFunc func,
void *arg)
{
ml_rbt_foreach_delete((ErtsMonLnkNode **) root,
- (void (*)(ErtsMonLnkNode*, void*)) func,
+ (int (*)(ErtsMonLnkNode*, void*, Sint)) func,
arg);
}
int
erts_monitor_tree_foreach_delete_yielding(ErtsMonitor **root,
- void (*func)(ErtsMonitor *, void *),
+ ErtsMonitorFunc func,
void *arg,
void **vyspp,
Sint limit)
{
return ml_rbt_foreach_delete_yielding((ErtsMonLnkNode **) root,
- (void (*)(ErtsMonLnkNode*, void*)) func,
+ (int (*)(ErtsMonLnkNode*, void*, Sint)) func,
arg, vyspp, limit);
}
void
erts_monitor_list_foreach(ErtsMonitor *list,
- void (*func)(ErtsMonitor *, void *),
+ ErtsMonitorFunc func,
void *arg)
{
void *ystate = NULL;
- while (ml_dl_list_foreach_yielding((ErtsMonLnkNode *) list,
- (void (*)(ErtsMonLnkNode *, void *)) func,
- arg, &ystate, (Sint) INT_MAX));
+ while (!ml_dl_list_foreach_yielding((ErtsMonLnkNode *) list,
+ (int (*)(ErtsMonLnkNode *, void *, Sint)) func,
+ arg, &ystate, (Sint) INT_MAX));
}
int
erts_monitor_list_foreach_yielding(ErtsMonitor *list,
- void (*func)(ErtsMonitor *, void *),
+ ErtsMonitorFunc func,
void *arg,
void **vyspp,
Sint limit)
{
return ml_dl_list_foreach_yielding((ErtsMonLnkNode *) list,
- (void (*)(ErtsMonLnkNode *, void *)) func,
+ (int (*)(ErtsMonLnkNode *, void *, Sint)) func,
arg, vyspp, limit);
}
void
erts_monitor_list_foreach_delete(ErtsMonitor **list,
- void (*func)(ErtsMonitor *, void *),
+ ErtsMonitorFunc func,
void *arg)
{
void *ystate = NULL;
- while (ml_dl_list_foreach_delete_yielding((ErtsMonLnkNode **) list,
- (void (*)(ErtsMonLnkNode*, void*)) func,
- arg, &ystate, (Sint) INT_MAX));
+ while (!ml_dl_list_foreach_delete_yielding((ErtsMonLnkNode **) list,
+ (int (*)(ErtsMonLnkNode*, void*, Sint)) func,
+ arg, &ystate, (Sint) INT_MAX));
}
int
erts_monitor_list_foreach_delete_yielding(ErtsMonitor **list,
- void (*func)(ErtsMonitor *, void *),
+ ErtsMonitorFunc func,
void *arg,
void **vyspp,
Sint limit)
{
return ml_dl_list_foreach_delete_yielding((ErtsMonLnkNode **) list,
- (void (*)(ErtsMonLnkNode*, void*)) func,
+ (int (*)(ErtsMonLnkNode*, void*, Sint)) func,
arg, vyspp, limit);
}
@@ -1074,92 +1071,92 @@ erts_link_tree_delete(ErtsLink **root, ErtsLink *lnk)
void
erts_link_tree_foreach(ErtsLink *root,
- void (*func)(ErtsLink *, void *),
+ ErtsLinkFunc func,
void *arg)
{
ml_rbt_foreach((ErtsMonLnkNode *) root,
- (void (*)(ErtsMonLnkNode*, void*)) func,
+ (ErtsMonLnkNodeFunc) func,
arg);
}
int
erts_link_tree_foreach_yielding(ErtsLink *root,
- void (*func)(ErtsLink *, void *),
+ ErtsLinkFunc func,
void *arg,
void **vyspp,
Sint limit)
{
return ml_rbt_foreach_yielding((ErtsMonLnkNode *) root,
- (void (*)(ErtsMonLnkNode*, void*)) func,
+ (ErtsMonLnkNodeFunc) func,
arg, vyspp, limit);
}
void
erts_link_tree_foreach_delete(ErtsLink **root,
- void (*func)(ErtsLink *, void *),
+ ErtsLinkFunc func,
void *arg)
{
ml_rbt_foreach_delete((ErtsMonLnkNode **) root,
- (void (*)(ErtsMonLnkNode*, void*)) func,
+ (ErtsMonLnkNodeFunc) func,
arg);
}
int
erts_link_tree_foreach_delete_yielding(ErtsLink **root,
- void (*func)(ErtsLink *, void *),
+ ErtsLinkFunc func,
void *arg,
void **vyspp,
Sint limit)
{
return ml_rbt_foreach_delete_yielding((ErtsMonLnkNode **) root,
- (void (*)(ErtsMonLnkNode*, void*)) func,
+ (ErtsMonLnkNodeFunc) func,
arg, vyspp, limit);
}
void
erts_link_list_foreach(ErtsLink *list,
- void (*func)(ErtsLink *, void *),
+ ErtsLinkFunc func,
void *arg)
{
void *ystate = NULL;
- while (ml_dl_list_foreach_yielding((ErtsMonLnkNode *) list,
- (void (*)(ErtsMonLnkNode *, void *)) func,
- arg, &ystate, (Sint) INT_MAX));
+ while (!ml_dl_list_foreach_yielding((ErtsMonLnkNode *) list,
+ (ErtsMonLnkNodeFunc) func,
+ arg, &ystate, (Sint) INT_MAX));
}
int
erts_link_list_foreach_yielding(ErtsLink *list,
- void (*func)(ErtsLink *, void *),
+ ErtsLinkFunc func,
void *arg,
void **vyspp,
Sint limit)
{
return ml_dl_list_foreach_yielding((ErtsMonLnkNode *) list,
- (void (*)(ErtsMonLnkNode *, void *)) func,
+ (ErtsMonLnkNodeFunc) func,
arg, vyspp, limit);
}
void
erts_link_list_foreach_delete(ErtsLink **list,
- void (*func)(ErtsLink *, void *),
+ ErtsLinkFunc func,
void *arg)
{
void *ystate = NULL;
- while (ml_dl_list_foreach_delete_yielding((ErtsMonLnkNode **) list,
- (void (*)(ErtsMonLnkNode*, void*)) func,
- arg, &ystate, (Sint) INT_MAX));
+ while (!ml_dl_list_foreach_delete_yielding((ErtsMonLnkNode **) list,
+ (ErtsMonLnkNodeFunc) func,
+ arg, &ystate, (Sint) INT_MAX));
}
int
erts_link_list_foreach_delete_yielding(ErtsLink **list,
- void (*func)(ErtsLink *, void *),
+ int (*func)(ErtsLink *, void *, Sint),
void *arg,
void **vyspp,
Sint limit)
{
return ml_dl_list_foreach_delete_yielding((ErtsMonLnkNode **) list,
- (void (*)(ErtsMonLnkNode*, void*)) func,
+ (ErtsMonLnkNodeFunc) func,
arg, vyspp, limit);
}
diff --git a/erts/emulator/beam/erl_monitor_link.h b/erts/emulator/beam/erl_monitor_link.h
index ed7bf7d54a..eff861fce8 100644
--- a/erts/emulator/beam/erl_monitor_link.h
+++ b/erts/emulator/beam/erl_monitor_link.h
@@ -439,6 +439,7 @@
(ERTS_ML_FLG_EXTENDED|ERTS_ML_FLG_NAME)
typedef struct ErtsMonLnkNode__ ErtsMonLnkNode;
+typedef int (*ErtsMonLnkNodeFunc)(ErtsMonLnkNode *, void *, Sint);
typedef struct {
UWord parent; /* Parent ptr and flags... */
@@ -622,6 +623,7 @@ erts_ml_dl_list_last__(ErtsMonLnkNode *list)
typedef struct ErtsMonLnkNode__ ErtsMonitor;
+typedef int (*ErtsMonitorFunc)(ErtsMonitor *, void *, Sint);
typedef struct {
ErtsMonitor origin;
@@ -653,6 +655,7 @@ struct ErtsMonitorDataExtended__ {
typedef struct ErtsMonitorSuspend__ ErtsMonitorSuspend;
+
struct ErtsMonitorSuspend__ {
ErtsMonitorData md; /* origin = suspender; target = suspendee */
ErtsMonitorSuspend *next;
@@ -685,7 +688,7 @@ 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:
+ * When the function 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.
*
@@ -711,7 +714,7 @@ ErtsMonitor *erts_monotor_tree_lookup_insert(ErtsMonitor **root,
* 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:
+ * When the function 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.
*
@@ -738,7 +741,7 @@ ErtsMonitor *erts_monitor_tree_lookup_create(ErtsMonitor **root, int *created,
*
* @brief Insert a monitor in a monitor tree
*
- * When the funcion is called it is assumed that:
+ * When the function 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.
@@ -754,7 +757,7 @@ 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:
+ * When the function 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
@@ -774,7 +777,7 @@ void erts_monitor_tree_replace(ErtsMonitor **root, ErtsMonitor *old,
*
* @brief Delete a monitor from a monitor tree
*
- * When the funcion is called it is assumed that:
+ * When the function is called it is assumed that:
* - 'mon' monitor is part of the tree
* If the above is not true, bad things will happen.
*
@@ -789,7 +792,7 @@ 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
+ * The function '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'.
*
@@ -802,7 +805,7 @@ void erts_monitor_tree_delete(ErtsMonitor **root, ErtsMonitor *mon);
*
*/
void erts_monitor_tree_foreach(ErtsMonitor *root,
- void (*func)(ErtsMonitor *, void *),
+ ErtsMonitorFunc func,
void *arg);
/**
@@ -810,9 +813,10 @@ void erts_monitor_tree_foreach(ErtsMonitor *root,
* @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
+ * The function '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'.
+ * in the tree referred to by 'root'. It should return the number of
+ * reductions the operator took to perform.
*
* It is assumed that:
* - *yspp equals NULL on first call
@@ -835,27 +839,28 @@ void erts_monitor_tree_foreach(ErtsMonitor *root,
* *yspp should be NULL. When done *yspp
* will be NULL.
*
- * @param[in] limit Maximum amount of monitors to process
- * before yielding.
+ * @param[in] reds Reductions available to execute before yielding.
*
- * @returns A non-zero value when all monitors has been
- * processed, and zero when more work is needed.
+ * @returns The unconsumed reductions when all monitors
+ * have been processed, and zero when more work
+ * is needed.
*
*/
int erts_monitor_tree_foreach_yielding(ErtsMonitor *root,
- void (*func)(ErtsMonitor *, void *),
+ ErtsMonitorFunc func,
void *arg,
void **vyspp,
- Sint limit);
+ Sint reds);
/**
*
* @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
+ * The function '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'.
+ * in the tree referred to by 'root'. It should return the number of
+ * reductions the operator took to perform.
*
* @param[in,out] root Pointer to pointer to root of monitor tree
*
@@ -866,7 +871,7 @@ int erts_monitor_tree_foreach_yielding(ErtsMonitor *root,
*
*/
void erts_monitor_tree_foreach_delete(ErtsMonitor **root,
- void (*func)(ErtsMonitor *, void *),
+ ErtsMonitorFunc func,
void *arg);
/**
@@ -874,9 +879,10 @@ void erts_monitor_tree_foreach_delete(ErtsMonitor **root,
* @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
+ * The function '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'.
+ * in the tree referred to by 'root'. It should return the number of
+ * reductions the operator took to perform.
*
* It is assumed that:
* - *yspp equals NULL on first call
@@ -899,18 +905,18 @@ void erts_monitor_tree_foreach_delete(ErtsMonitor **root,
* *yspp should be NULL. When done *yspp
* will be NULL.
*
- * @param[in] limit Maximum amount of monitors to process
- * before yielding.
+ * @param[in] reds Reductions available to execute before yielding.
*
- * @returns A non-zero value when all monitors has been
- * processed, and zero when more work is needed.
+ * @returns The unconsumed reductions when all monitors
+ * have been processed, and zero when more work
+ * is needed.
*
*/
int erts_monitor_tree_foreach_delete_yielding(ErtsMonitor **root,
- void (*func)(ErtsMonitor *, void *),
+ ErtsMonitorFunc func,
void *arg,
void **vyspp,
- Sint limit);
+ Sint reds);
/*
* --- Monitor list operations --
@@ -920,7 +926,7 @@ int erts_monitor_tree_foreach_delete_yielding(ErtsMonitor **root,
*
* @brief Insert a monitor in a monitor list
*
- * When the funcion is called it is assumed that:
+ * When the function 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.
*
@@ -935,7 +941,7 @@ ERTS_GLB_INLINE void erts_monitor_list_insert(ErtsMonitor **list, ErtsMonitor *m
*
* @brief Delete a monitor from a monitor list
*
- * When the funcion is called it is assumed that:
+ * When the function is called it is assumed that:
* - 'mon' monitor is part of the list
* If the above is not true, bad things will happen.
*
@@ -980,7 +986,7 @@ 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
+ * The function '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'.
*
@@ -993,7 +999,7 @@ ERTS_GLB_INLINE ErtsMonitor *erts_monitor_list_last(ErtsMonitor *list);
*
*/
void erts_monitor_list_foreach(ErtsMonitor *list,
- void (*func)(ErtsMonitor *, void *),
+ ErtsMonitorFunc func,
void *arg);
/**
@@ -1001,9 +1007,10 @@ void erts_monitor_list_foreach(ErtsMonitor *list,
* @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
+ * The function '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'.
+ * in the tree referred to by 'root'. It should return the number of
+ * reductions the operator took to perform.
*
* It is assumed that:
* - *yspp equals NULL on first call
@@ -1026,25 +1033,25 @@ void erts_monitor_list_foreach(ErtsMonitor *list,
* *yspp should be NULL. When done *yspp
* will be NULL.
*
- * @param[in] limit Maximum amount of monitors to process
- * before yielding.
+ * @param[in] reds Reductions available to execute before yielding.
*
- * @returns A non-zero value when all monitors has been
- * processed, and zero when more work is needed.
+ * @returns The unconsumed reductions when all monitors
+ * have been processed, and zero when more work
+ * is needed.
*
*/
int erts_monitor_list_foreach_yielding(ErtsMonitor *list,
- void (*func)(ErtsMonitor *, void *),
+ ErtsMonitorFunc func,
void *arg,
void **vyspp,
- Sint limit);
+ Sint reds);
/**
*
* @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
+ * The function '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'.
*
@@ -1057,7 +1064,7 @@ int erts_monitor_list_foreach_yielding(ErtsMonitor *list,
*
*/
void erts_monitor_list_foreach_delete(ErtsMonitor **list,
- void (*func)(ErtsMonitor *, void *),
+ ErtsMonitorFunc func,
void *arg);
/**
@@ -1065,9 +1072,10 @@ void erts_monitor_list_foreach_delete(ErtsMonitor **list,
* @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
+ * The function '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'.
+ * in the tree referred to by 'root'. It should return the number of
+ * reductions the operator took to perform.
*
* It is assumed that:
* - *yspp equals NULL on first call
@@ -1090,18 +1098,18 @@ void erts_monitor_list_foreach_delete(ErtsMonitor **list,
* *yspp should be NULL. When done *yspp
* will be NULL.
*
- * @param[in] limit Maximum amount of monitors to process
- * before yielding.
+ * @param[in] reds Reductions available to execute before yielding.
*
- * @returns A non-zero value when all monitors has been
- * processed, and zero when more work is needed.
+ * @returns The unconsumed reductions when all monitors
+ * have been processed, and zero when more work
+ * is needed.
*
*/
int erts_monitor_list_foreach_delete_yielding(ErtsMonitor **list,
- void (*func)(ErtsMonitor *, void *),
+ ErtsMonitorFunc func,
void *arg,
void **vyspp,
- Sint limit);
+ Sint reds);
/*
* --- Misc monitor operations ---
@@ -1113,7 +1121,7 @@ int erts_monitor_list_foreach_delete_yielding(ErtsMonitor **list,
*
* Can create all types of monitors
*
- * When the funcion is called it is assumed that:
+ * When the function 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, ERTS_MON_TYPE_NODES, or
@@ -1199,7 +1207,7 @@ ERTS_GLB_INLINE int erts_monitor_is_in_table(ErtsMonitor *mon);
* 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:
+ * When the function 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.
@@ -1216,7 +1224,7 @@ ERTS_GLB_INLINE void erts_monitor_release(ErtsMonitor *mon);
* Release both the origin and target parts of the monitor
* simultaneously and deallocate the structure.
*
- * When the funcion is called it is assumed that:
+ * When the function 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
@@ -1232,7 +1240,7 @@ 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:
+ * When the function 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.
*
@@ -1253,7 +1261,7 @@ ERTS_GLB_INLINE int erts_monitor_dist_insert(ErtsMonitor *mon, ErtsMonLnkDist *d
*
* @brief Delete monitor from dist monitor tree or list
*
- * When the funcion is called it is assumed that:
+ * When the function is called it is assumed that:
* - 'mon' monitor earler has been inserted into 'dist'
* If the above is not true, bad things will happen.
*
@@ -1291,7 +1299,7 @@ erts_monitor_set_dead_dist(ErtsMonitor *mon, Eterm nodename);
* 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:
+ * When the function is called it is assumed that:
* - 'mon' has not been released
* If the above is not true, bad things will happen.
*
@@ -1507,6 +1515,8 @@ ERTS_GLB_INLINE ErtsMonitorSuspend *erts_monitor_suspend(ErtsMonitor *mon)
typedef struct ErtsMonLnkNode__ ErtsLink;
+typedef int (*ErtsLinkFunc)(ErtsLink *, void *, Sint);
+
typedef struct {
ErtsLink a;
ErtsLink b;
@@ -1544,7 +1554,7 @@ 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:
+ * When the function 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.
*
@@ -1590,7 +1600,7 @@ ErtsLink *erts_link_tree_lookup_create(ErtsLink **root, int *created,
*
* @brief Insert a link in a link tree
*
- * When the funcion is called it is assumed that:
+ * When the function 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.
@@ -1606,7 +1616,7 @@ 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:
+ * When the function 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
@@ -1630,7 +1640,7 @@ void erts_link_tree_replace(ErtsLink **root, ErtsLink *old, ErtsLink *new);
* 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:
+ * When the function 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.
*
@@ -1649,7 +1659,7 @@ ERTS_GLB_INLINE ErtsLink *erts_link_tree_insert_addr_replace(ErtsLink **root,
*
* @brief Delete a link from a link tree
*
- * When the funcion is called it is assumed that:
+ * When the function is called it is assumed that:
* - 'lnk' link is part of the tree
* If the above is not true, bad things will happen.
*
@@ -1668,7 +1678,7 @@ void erts_link_tree_delete(ErtsLink **root, ErtsLink *lnk);
* 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:
+ * When the function 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.
*
@@ -1687,7 +1697,7 @@ ERTS_GLB_INLINE ErtsLink *erts_link_tree_key_delete(ErtsLink **root, ErtsLink *l
*
* @brief Call a function for each link in a link tree
*
- * The funcion 'func' will be called with a pointer to a link
+ * The function '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'.
*
@@ -1700,7 +1710,7 @@ ERTS_GLB_INLINE ErtsLink *erts_link_tree_key_delete(ErtsLink **root, ErtsLink *l
*
*/
void erts_link_tree_foreach(ErtsLink *root,
- void (*func)(ErtsLink *, void *),
+ ErtsLinkFunc,
void *arg);
/**
@@ -1708,9 +1718,10 @@ void erts_link_tree_foreach(ErtsLink *root,
* @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
+ * The function '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'.
+ * in the tree referred to by 'root'. It should return the number of
+ * reductions the operator took to perform.
*
* It is assumed that:
* - *yspp equals NULL on first call
@@ -1733,25 +1744,25 @@ void erts_link_tree_foreach(ErtsLink *root,
* *yspp should be NULL. When done *yspp
* will be NULL.
*
- * @param[in] limit Maximum amount of links to process
- * before yielding.
+ * @param[in] reds Reductions available to execute before yielding.
*
- * @returns A non-zero value when all links has been
- * processed, and zero when more work is needed.
+ * @returns The unconsumed reductions when all links
+ * have been processed, and zero when more work
+ * is needed.
*
*/
int erts_link_tree_foreach_yielding(ErtsLink *root,
- void (*func)(ErtsLink *, void *),
+ ErtsLinkFunc func,
void *arg,
void **vyspp,
- Sint limit);
+ Sint reds);
/**
*
* @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
+ * The function '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'.
*
@@ -1764,7 +1775,7 @@ int erts_link_tree_foreach_yielding(ErtsLink *root,
*
*/
void erts_link_tree_foreach_delete(ErtsLink **root,
- void (*func)(ErtsLink *, void *),
+ ErtsLinkFunc func,
void *arg);
/**
@@ -1772,9 +1783,10 @@ void erts_link_tree_foreach_delete(ErtsLink **root,
* @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
+ * The function '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'.
+ * in the tree referred to by 'root'. It should return the number of
+ * reductions the operator took to perform.
*
* It is assumed that:
* - *yspp equals NULL on first call
@@ -1797,18 +1809,18 @@ void erts_link_tree_foreach_delete(ErtsLink **root,
* *yspp should be NULL. When done *yspp
* will be NULL.
*
- * @param[in] limit Maximum amount of links to process
- * before yielding.
+ * @param[in] reds Reductions available to execute before yielding.
*
- * @returns A non-zero value when all links has been
- * processed, and zero when more work is needed.
+ * @returns The unconsumed reductions when all links
+ * have been processed, and zero when more work
+ * is needed.
*
*/
int erts_link_tree_foreach_delete_yielding(ErtsLink **root,
- void (*func)(ErtsLink *, void *),
+ ErtsLinkFunc func,
void *arg,
void **vyspp,
- Sint limit);
+ Sint reds);
/*
* --- Link list operations ---
@@ -1818,7 +1830,7 @@ int erts_link_tree_foreach_delete_yielding(ErtsLink **root,
*
* @brief Insert a link in a link list
*
- * When the funcion is called it is assumed that:
+ * When the function 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.
*
@@ -1833,7 +1845,7 @@ 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:
+ * When the function is called it is assumed that:
* - 'lnk' link is part of the list
* If the above is not true, bad things will happen.
*
@@ -1878,7 +1890,7 @@ 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
+ * The function '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'.
*
@@ -1891,7 +1903,7 @@ ERTS_GLB_INLINE ErtsLink *erts_link_list_last(ErtsLink *list);
*
*/
void erts_link_list_foreach(ErtsLink *list,
- void (*func)(ErtsLink *, void *),
+ ErtsLinkFunc func,
void *arg);
/**
@@ -1899,9 +1911,10 @@ void erts_link_list_foreach(ErtsLink *list,
* @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
+ * The function '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'.
+ * in the tree referred to by 'root'. It should return the number of
+ * reductions the operator took to perform.
*
* It is assumed that:
* - *yspp equals NULL on first call
@@ -1924,25 +1937,25 @@ void erts_link_list_foreach(ErtsLink *list,
* *yspp should be NULL. When done *yspp
* will be NULL.
*
- * @param[in] limit Maximum amount of links to process
- * before yielding.
+ * @param[in] reds Reductions available to execute before yielding.
*
- * @returns A non-zero value when all links has been
- * processed, and zero when more work is needed.
+ * @returns The unconsumed reductions when all links
+ * have been processed, and zero when more work
+ * is needed.
*
*/
int erts_link_list_foreach_yielding(ErtsLink *list,
- void (*func)(ErtsLink *, void *),
+ ErtsLinkFunc func,
void *arg,
void **vyspp,
- Sint limit);
+ Sint reds);
/**
*
* @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
+ * The function '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'.
*
@@ -1955,7 +1968,7 @@ int erts_link_list_foreach_yielding(ErtsLink *list,
*
*/
void erts_link_list_foreach_delete(ErtsLink **list,
- void (*func)(ErtsLink *, void *),
+ ErtsLinkFunc func,
void *arg);
/**
@@ -1963,9 +1976,10 @@ void erts_link_list_foreach_delete(ErtsLink **list,
* @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
+ * The function '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'.
+ * in the tree referred to by 'root'. It should return the number of
+ * reductions the operator took to perform.
*
* It is assumed that:
* - *yspp equals NULL on first call
@@ -1988,18 +2002,18 @@ void erts_link_list_foreach_delete(ErtsLink **list,
* *yspp should be NULL. When done *yspp
* will be NULL.
*
- * @param[in] limit Maximum amount of links to process
- * before yielding.
+ * @param[in] reds Reductions available to execute before yielding.
*
- * @returns A non-zero value when all links has been
- * processed, and zero when more work is needed.
+ * @returns The unconsumed reductions when all links
+ * have been processed, and zero when more work
+ * is needed.
*
*/
int erts_link_list_foreach_delete_yielding(ErtsLink **list,
- void (*func)(ErtsLink *, void *),
+ ErtsLinkFunc func,
void *arg,
void **vyspp,
- Sint limit);
+ Sint reds);
/*
* --- Misc link operations ---
@@ -2011,7 +2025,7 @@ int erts_link_list_foreach_delete_yielding(ErtsLink **list,
*
* Can create all types of links
*
- * When the funcion is called it is assumed that:
+ * When the function 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
@@ -2081,7 +2095,7 @@ ERTS_GLB_INLINE int erts_link_is_in_table(ErtsLink *lnk);
* 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:
+ * When the function 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.
@@ -2098,7 +2112,7 @@ ERTS_GLB_INLINE void erts_link_release(ErtsLink *lnk);
* Release both halves of a link simultaneously and deallocate
* the structure.
*
- * When the funcion is called it is assumed that:
+ * When the function 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
@@ -2113,7 +2127,7 @@ 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:
+ * When the function 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.
*
@@ -2134,7 +2148,7 @@ 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:
+ * When the function is called it is assumed that:
* - 'lnk' link earler has been inserted into 'dist'
* If the above is not true, bad things will happen.
*
@@ -2172,7 +2186,7 @@ erts_link_set_dead_dist(ErtsLink *lnk, Eterm nodename);
* 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:
+ * When the function is called it is assumed that:
* - 'lnk' has not been released
* If the above is not true, bad things will happen.
*
diff --git a/erts/emulator/beam/erl_nif.c b/erts/emulator/beam/erl_nif.c
index a48d0391f6..349d9bf13a 100644
--- a/erts/emulator/beam/erl_nif.c
+++ b/erts/emulator/beam/erl_nif.c
@@ -1083,6 +1083,17 @@ int enif_get_local_pid(ErlNifEnv* env, ERL_NIF_TERM term, ErlNifPid* pid)
return 0;
}
+void enif_set_pid_undefined(ErlNifPid* pid)
+{
+ pid->pid = am_undefined;
+}
+
+int enif_is_pid_undefined(const ErlNifPid* pid)
+{
+ ASSERT(pid->pid == am_undefined || is_internal_pid(pid->pid));
+ return pid->pid == am_undefined;
+}
+
int enif_get_local_port(ErlNifEnv* env, ERL_NIF_TERM term, ErlNifPort* port)
{
if (is_internal_port(term)) {
@@ -2357,13 +2368,21 @@ rmon_refc_read(ErtsResourceMonitors *rms)
return rms->refc & ERTS_RESOURCE_REFC_MASK;
}
-static void dtor_demonitor(ErtsMonitor* mon, void* context)
+static int dtor_demonitor(ErtsMonitor* mon, void* context, Sint reds)
{
ASSERT(erts_monitor_is_origin(mon));
ASSERT(is_internal_pid(mon->other.item));
erts_proc_sig_send_demonitor(mon);
+ return 1;
+}
+
+#ifdef DEBUG
+int erts_dbg_is_resource_dying(ErtsResource* resource)
+{
+ return resource->monitors && rmon_is_dying(resource->monitors);
}
+#endif
# define NIF_RESOURCE_DTOR &nif_resource_dtor
@@ -2705,8 +2724,12 @@ int enif_consume_timeslice(ErlNifEnv* env, int percent)
{
Process *proc;
Sint reds;
+ int sched;
- execution_state(env, &proc, NULL);
+ execution_state(env, &proc, &sched);
+
+ if (sched < 0)
+ return 0; /* no-op on dirty scheduler */
ASSERT(is_proc_bound(env) && percent >= 1 && percent <= 100);
if (percent < 1) percent = 1;
@@ -3329,6 +3352,9 @@ int enif_monitor_process(ErlNifEnv* env, void* obj, const ErlNifPid* target_pid,
}
ASSERT(rsrc->type->down);
+ if (target_pid->pid == am_undefined)
+ return 1;
+
ref = erts_make_ref_in_buffer(tmp);
mdp = erts_monitor_create(ERTS_MON_TYPE_RESOURCE, ref,
@@ -3362,6 +3388,12 @@ int enif_monitor_process(ErlNifEnv* env, void* obj, const ErlNifPid* target_pid,
return 0;
}
+ERL_NIF_TERM enif_make_monitor_term(ErlNifEnv* env, const ErlNifMonitor* monitor)
+{
+ Eterm* hp = alloc_heap(env, ERTS_REF_THING_SIZE);
+ return erts_driver_monitor_to_ref(hp, monitor);
+}
+
int enif_demonitor_process(ErlNifEnv* env, void* obj, const ErlNifMonitor* monitor)
{
ErtsResource* rsrc = DATA_TO_RESOURCE(obj);
diff --git a/erts/emulator/beam/erl_nif_api_funcs.h b/erts/emulator/beam/erl_nif_api_funcs.h
index 129166562d..8ab454c8dd 100644
--- a/erts/emulator/beam/erl_nif_api_funcs.h
+++ b/erts/emulator/beam/erl_nif_api_funcs.h
@@ -211,7 +211,9 @@ ERL_NIF_API_FUNC_DECL(int,enif_vsnprintf,(char*, size_t, const char *fmt, va_lis
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));
ERL_NIF_API_FUNC_DECL(int,enif_select_x,(ErlNifEnv* env, ErlNifEvent e, enum ErlNifSelectFlags flags, void* obj, const ErlNifPid* pid, ERL_NIF_TERM msg, ErlNifEnv* msg_env));
-
+ERL_NIF_API_FUNC_DECL(ERL_NIF_TERM,enif_make_monitor_term,(ErlNifEnv* env, const ErlNifMonitor*));
+ERL_NIF_API_FUNC_DECL(void,enif_set_pid_undefined,(ErlNifPid* pid));
+ERL_NIF_API_FUNC_DECL(int,enif_is_pid_undefined,(const ErlNifPid* pid));
/*
** ADD NEW ENTRIES HERE (before this comment) !!!
@@ -396,6 +398,9 @@ ERL_NIF_API_FUNC_DECL(int,enif_select_x,(ErlNifEnv* env, ErlNifEvent e, enum Erl
# 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)
# define enif_select_x ERL_NIF_API_FUNC_MACRO(enif_select_x)
+# define enif_make_monitor_term ERL_NIF_API_FUNC_MACRO(enif_make_monitor_term)
+# define enif_set_pid_undefined ERL_NIF_API_FUNC_MACRO(enif_set_pid_undefined)
+# define enif_is_pid_undefined ERL_NIF_API_FUNC_MACRO(enif_is_pid_undefined)
/*
** ADD NEW ENTRIES HERE (before this comment)
@@ -627,6 +632,7 @@ static ERL_NIF_INLINE ERL_NIF_TERM enif_make_list9(ErlNifEnv* env,
#ifndef enif_make_pid
# define enif_make_pid(ENV, PID) ((void)(ENV),(const ERL_NIF_TERM)((PID)->pid))
+# define enif_compare_pids(A, B) (enif_compare((A)->pid,(B)->pid))
# define enif_select_read(ENV, E, OBJ, PID, MSG, MSG_ENV) \
enif_select_x(ENV, E, ERL_NIF_SELECT_READ | ERL_NIF_SELECT_CUSTOM_MSG, \
OBJ, PID, MSG, MSG_ENV)
diff --git a/erts/emulator/beam/erl_node_tables.c b/erts/emulator/beam/erl_node_tables.c
index 18ed782ae3..afafaf48dc 100644
--- a/erts/emulator/beam/erl_node_tables.c
+++ b/erts/emulator/beam/erl_node_tables.c
@@ -201,6 +201,7 @@ dist_table_alloc(void *dep_tmpl)
dep->send = NULL;
dep->cache = NULL;
dep->transcode_ctx = NULL;
+ dep->sequences = NULL;
/* Link in */
@@ -801,8 +802,9 @@ node_table_hash(void *venp)
static int
node_table_cmp(void *venp1, void *venp2)
{
- return ((((ErlNode *) venp1)->sysname == ((ErlNode *) venp2)->sysname
- && ((ErlNode *) venp1)->creation == ((ErlNode *) venp2)->creation)
+ return ((((ErlNode *) venp1)->sysname == ((ErlNode *) venp2)->sysname) &&
+ ((((ErlNode *) venp1)->creation == ((ErlNode *) venp2)->creation) ||
+ (((ErlNode *) venp1)->creation == 0 || ((ErlNode *) venp2)->creation == 0))
? 0
: 1);
}
@@ -816,11 +818,16 @@ node_table_alloc(void *venp_tmpl)
node_entries++;
- erts_refc_init(&enp->refc, -1);
+ erts_init_node_entry(enp, -1);
enp->creation = ((ErlNode *) venp_tmpl)->creation;
enp->sysname = ((ErlNode *) venp_tmpl)->sysname;
enp->dist_entry = erts_find_or_insert_dist_entry(((ErlNode *) venp_tmpl)->sysname);
+#ifdef ERL_NODE_BOOKKEEP
+ erts_atomic_init_nob(&enp->slot, 0);
+ sys_memzero(enp->books, sizeof(struct erl_node_bookkeeping) * 1024);
+#endif
+
return (void *) enp;
}
@@ -873,7 +880,7 @@ erts_node_table_info(fmtfn_t to, void *to_arg)
}
-ErlNode *erts_find_or_insert_node(Eterm sysname, Uint32 creation)
+ErlNode *erts_find_or_insert_node(Eterm sysname, Uint32 creation, Eterm book)
{
ErlNode *res;
ErlNode ne;
@@ -883,9 +890,9 @@ ErlNode *erts_find_or_insert_node(Eterm sysname, Uint32 creation)
erts_rwmtx_rlock(&erts_node_table_rwmtx);
res = hash_get(&erts_node_table, (void *) &ne);
if (res && res != erts_this_node) {
- erts_aint_t refc = erts_refc_inctest(&res->refc, 0);
+ erts_aint_t refc = erts_ref_node_entry(res, 0, book);
if (refc < 2) /* New or pending delete */
- erts_refc_inc(&res->refc, 1);
+ erts_ref_node_entry(res, 1, THE_NON_VALUE);
}
erts_rwmtx_runlock(&erts_node_table_rwmtx);
if (res)
@@ -895,9 +902,9 @@ ErlNode *erts_find_or_insert_node(Eterm sysname, Uint32 creation)
res = hash_put(&erts_node_table, (void *) &ne);
ASSERT(res);
if (res != erts_this_node) {
- erts_aint_t refc = erts_refc_inctest(&res->refc, 0);
+ erts_aint_t refc = erts_ref_node_entry(res, 0, book);
if (refc < 2) /* New or pending delete */
- erts_refc_inc(&res->refc, 1);
+ erts_ref_node_entry(res, 1, THE_NON_VALUE);
}
erts_rwmtx_rwunlock(&erts_node_table_rwmtx);
return res;
@@ -924,6 +931,7 @@ static void try_delete_node(void *venp)
*
* If refc > 0, the entry is in use. Keep the entry.
*/
+ erts_node_bookkeep(enp, THE_NON_VALUE, ERL_NODE_DEC);
refc = erts_refc_dectest(&enp->refc, -1);
if (refc == -1)
(void) hash_erase(&erts_node_table, (void *) enp);
@@ -1021,7 +1029,7 @@ erts_set_this_node(Eterm sysname, Uint creation)
erts_deref_dist_entry(erts_this_dist_entry);
erts_this_node = NULL; /* to make sure refc is bumped for this node */
- erts_this_node = erts_find_or_insert_node(sysname, creation);
+ erts_this_node = erts_find_or_insert_node(sysname, creation, THE_NON_VALUE);
erts_this_dist_entry = erts_this_node->dist_entry;
erts_ref_dist_entry(erts_this_dist_entry);
@@ -1090,7 +1098,7 @@ void erts_init_node_tables(int dd_sec)
node_tmpl.creation = 0;
erts_this_node = hash_put(&erts_node_table, &node_tmpl);
/* +1 for erts_this_node */
- erts_refc_init(&erts_this_node->refc, 1);
+ erts_init_node_entry(erts_this_node, 1);
ASSERT(erts_this_node->dist_entry != NULL);
erts_this_dist_entry = erts_this_node->dist_entry;
@@ -1177,6 +1185,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_sequence;
static Eterm AM_signal;
static void setup_reference_table(void);
@@ -1218,6 +1227,7 @@ typedef struct dist_referrer_ {
int ctrl_ref;
int system_ref;
int signal_ref;
+ int sequence_ref;
Eterm id;
Uint creation;
Uint id_heap[ID_HEAP_SIZE];
@@ -1272,6 +1282,7 @@ erts_get_node_and_dist_references(struct process *proc)
INIT_AM(delayed_delete_timer);
INIT_AM(thread_progress_delete_timer);
INIT_AM(signal);
+ INIT_AM(sequence);
references_atoms_need_init = 0;
}
@@ -1309,8 +1320,9 @@ erts_get_node_and_dist_references(struct process *proc)
#define TIMER_REF 8
#define SYSTEM_REF 9
#define SIGNAL_REF 10
+#define SEQUENCE_REF 11
-#define INC_TAB_SZ 10
+#define INC_TAB_SZ 11
static void
insert_dist_referrer(ReferredDist *referred_dist,
@@ -1344,6 +1356,7 @@ insert_dist_referrer(ReferredDist *referred_dist,
drp->ctrl_ref = 0;
drp->system_ref = 0;
drp->signal_ref = 0;
+ drp->sequence_ref = 0;
}
switch (type) {
@@ -1353,6 +1366,7 @@ insert_dist_referrer(ReferredDist *referred_dist,
case ETS_REF: drp->ets_ref++; break;
case SYSTEM_REF: drp->system_ref++; break;
case SIGNAL_REF: drp->signal_ref++; break;
+ case SEQUENCE_REF: drp->sequence_ref++; break;
default: ASSERT(0);
}
}
@@ -1509,7 +1523,7 @@ insert_offheap(ErlOffHeap *oh, int type, Eterm id)
}
}
else if (IsSendCtxBinary(u.mref->mb)) {
- ErtsSendContext* ctx = ERTS_MAGIC_BIN_DATA(u.mref->mb);
+ ErtsDSigSendContext* ctx = ERTS_MAGIC_BIN_DATA(u.mref->mb);
if (ctx->deref_dep)
insert_dist_entry(ctx->dep, type, id, 0);
}
@@ -1544,16 +1558,18 @@ static void insert_monitor_data(ErtsMonitor *mon, int type, Eterm id)
mdp->origin.flags |= ERTS_ML_FLG_DBG_VISITED;
}
-static void insert_monitor(ErtsMonitor *mon, void *idp)
+static int insert_monitor(ErtsMonitor *mon, void *idp, Sint reds)
{
Eterm id = *((Eterm *) idp);
insert_monitor_data(mon, MONITOR_REF, id);
+ return 1;
}
-static void clear_visited_monitor(ErtsMonitor *mon, void *p)
+static int clear_visited_monitor(ErtsMonitor *mon, void *p, Sint reds)
{
ErtsMonitorData *mdp = erts_monitor_to_data(mon);
mdp->origin.flags &= ~ERTS_ML_FLG_DBG_VISITED;
+ return 1;
}
static void
@@ -1581,6 +1597,20 @@ insert_dist_monitors(DistEntry *dep)
}
}
+
+static int
+insert_sequence(ErtsDistExternal *edep, void *arg, Sint reds)
+{
+ insert_dist_entry(edep->dep, SEQUENCE_REF, *(Eterm*)arg, 0);
+ return 1;
+}
+
+static void
+insert_dist_sequences(DistEntry *dep)
+{
+ erts_dist_seq_tree_foreach(dep, insert_sequence, (void *) &dep->sysname);
+}
+
static void
clear_visited_p_monitors(ErtsPTabElementCommon *p)
{
@@ -1621,16 +1651,18 @@ static void insert_link_data(ErtsLink *lnk, int type, Eterm id)
ldp->a.flags |= ERTS_ML_FLG_DBG_VISITED;
}
-static void insert_link(ErtsLink *lnk, void *idp)
+static int insert_link(ErtsLink *lnk, void *idp, Sint reds)
{
Eterm id = *((Eterm *) idp);
insert_link_data(lnk, LINK_REF, id);
+ return 1;
}
-static void clear_visited_link(ErtsLink *lnk, void *p)
+static int clear_visited_link(ErtsLink *lnk, void *p, Sint reds)
{
ErtsLinkData *ldp = erts_link_to_data(lnk);
ldp->a.flags &= ~ERTS_ML_FLG_DBG_VISITED;
+ return 1;
}
static void
@@ -1770,11 +1802,9 @@ insert_message(ErtsMessage *msg, int type, Process *proc)
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);
+ heap_frag = msg->data.heap_frag;
+ insert_dist_entry(erts_get_dist_ext(heap_frag)->dep,
+ type, proc->common.id, 0);
}
}
while (heap_frag) {
@@ -1798,24 +1828,115 @@ insert_sig_offheap(ErlOffHeap *ohp, void *arg)
insert_offheap(ohp, SIGNAL_REF, proc->common.id);
}
-static void
-insert_sig_monitor(ErtsMonitor *mon, void *arg)
+static int
+insert_sig_monitor(ErtsMonitor *mon, void *arg, Sint reds)
{
Process *proc = arg;
insert_monitor_data(mon, SIGNAL_REF, proc->common.id);
+ return 1;
}
-static void
-insert_sig_link(ErtsLink *lnk, void *arg)
+static int
+insert_sig_link(ErtsLink *lnk, void *arg, Sint reds)
{
Process *proc = arg;
insert_link_data(lnk, SIGNAL_REF, proc->common.id);
+ return 1;
}
static void
-setup_reference_table(void)
+insert_sig_ext(ErtsDistExternal *edep, void *arg)
{
+ Process *proc = arg;
+ insert_dist_entry(edep->dep, SIGNAL_REF, proc->common.id, 0);
+}
+
+static void
+insert_process(Process *proc)
+{
+ int mli;
+ ErtsMessage *msg_list[] = {proc->msg_frag};
ErlHeapFragment *hfp;
+
+ /* Insert Heap */
+ insert_offheap(&(proc->off_heap),
+ HEAP_REF,
+ proc->common.id);
+ /* Insert heap fragments buffers */
+ for(hfp = proc->mbuf; hfp; hfp = hfp->next)
+ insert_offheap(&(hfp->off_heap),
+ HEAP_REF,
+ proc->common.id);
+
+ /* 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)
+ 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,
+ insert_sig_ext,
+ (void *) proc);
+
+ /* If the process is FREE, the proc->common field has been
+ re-used by the ptab delete, so we cannot trust it. */
+ if (!(erts_atomic32_read_nob(&proc->state) & ERTS_PSFLG_FREE)) {
+ /* Insert links */
+ insert_p_links(&proc->common);
+
+ /* Insert monitors */
+ insert_p_monitors(&proc->common);
+ }
+
+ {
+ DistEntry *dep = ERTS_PROC_GET_DIST_ENTRY(proc);
+ if (dep)
+ insert_dist_entry(dep,
+ CTRL_REF,
+ proc->common.id,
+ 0);
+ }
+}
+
+static void
+insert_dist_suspended_procs(DistEntry *dep)
+{
+ ErtsProcList *plist = erts_proclist_peek_first(dep->suspended);
+ while (plist) {
+ if (is_not_immed(plist->u.pid))
+ insert_process(plist->u.p);
+ plist = erts_proclist_peek_next(dep->suspended, plist);
+ }
+}
+
+#ifdef ERL_NODE_BOOKKEEP
+void
+erts_node_bookkeep(ErlNode *np, Eterm term, int what)
+{
+ erts_aint_t slot = (erts_atomic_inc_read_nob(&np->slot) - 1) % 1024;
+ ErtsSchedulerData *esdp = erts_get_scheduler_data();
+ Eterm who = THE_NON_VALUE;
+ ASSERT(np);
+ np->books[slot].what = what;
+ np->books[slot].term = term;
+ if (esdp->current_process) {
+ who = esdp->current_process->common.id;
+ } else if (esdp->current_port) {
+ who = esdp->current_port->common.id;
+ }
+ np->books[slot].who = who;
+}
+#endif
+
+static void
+setup_reference_table(void)
+{
DistEntry *dep;
HashInfo hi;
int i, max;
@@ -1865,52 +1986,10 @@ setup_reference_table(void)
/* Insert all processes */
for (i = 0; i < max; i++) {
Process *proc = erts_pix2proc(i);
- if (proc) {
- int mli;
- ErtsMessage *msg_list[] = {proc->msg_frag};
-
- /* Insert Heap */
- insert_offheap(&(proc->off_heap),
- HEAP_REF,
- proc->common.id);
- /* Insert heap fragments buffers */
- for(hfp = proc->mbuf; hfp; hfp = hfp->next)
- insert_offheap(&(hfp->off_heap),
- HEAP_REF,
- proc->common.id);
-
- /* 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)
- 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 */
- insert_p_links(&proc->common);
-
- /* Insert monitors */
- insert_p_monitors(&proc->common);
-
- {
- DistEntry *dep = ERTS_PROC_GET_DIST_ENTRY(proc);
- if (dep)
- insert_dist_entry(dep,
- CTRL_REF,
- proc->common.id,
- 0);
- }
- }
+ if (proc)
+ insert_process(proc);
}
-
+
erts_foreach_sys_msg_in_q(insert_sys_msg);
/* Insert all ports */
@@ -1983,16 +2062,22 @@ setup_reference_table(void)
for(dep = erts_visible_dist_entries; dep; dep = dep->next) {
insert_dist_links(dep);
insert_dist_monitors(dep);
+ insert_dist_sequences(dep);
+ insert_dist_suspended_procs(dep);
}
for(dep = erts_hidden_dist_entries; dep; dep = dep->next) {
insert_dist_links(dep);
insert_dist_monitors(dep);
+ insert_dist_sequences(dep);
+ insert_dist_suspended_procs(dep);
}
for(dep = erts_pending_dist_entries; dep; dep = dep->next) {
insert_dist_links(dep);
insert_dist_monitors(dep);
+ insert_dist_sequences(dep);
+ insert_dist_suspended_procs(dep);
}
/* Not connected dist entries should not have any links,
@@ -2000,6 +2085,8 @@ setup_reference_table(void)
for(dep = erts_not_connected_dist_entries; dep; dep = dep->next) {
insert_dist_links(dep);
insert_dist_monitors(dep);
+ insert_dist_sequences(dep);
+ insert_dist_suspended_procs(dep);
}
/* Insert all ets tables */
@@ -2173,6 +2260,10 @@ 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->sequence_ref) {
+ tup = MK_2TUP(AM_sequence, MK_UINT(drp->sequence_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);
@@ -2247,6 +2338,12 @@ static void noop_sig_offheap(ErlOffHeap *oh, void *arg)
}
+static void noop_sig_ext(ErtsDistExternal *ext, void *arg)
+{
+
+}
+
+
static void
delete_reference_table(void)
{
@@ -2297,6 +2394,7 @@ delete_reference_table(void)
noop_sig_offheap,
clear_visited_monitor,
clear_visited_link,
+ noop_sig_ext,
(void *) proc);
}
}
diff --git a/erts/emulator/beam/erl_node_tables.h b/erts/emulator/beam/erl_node_tables.h
index c44f1f8991..d5daf0c2df 100644
--- a/erts/emulator/beam/erl_node_tables.h
+++ b/erts/emulator/beam/erl_node_tables.h
@@ -98,11 +98,22 @@ struct ErtsDistOutputBuf_ {
byte *alloc_endp;
#endif
ErtsDistOutputBuf *next;
- Uint hopefull_flags;
+ Binary *bin;
+ /* Pointers to the distribution header,
+ if NULL the distr header is in the extp */
+ byte *hdrp;
+ byte *hdr_endp;
+ /* Pointers to the ctl + payload */
byte *extp;
byte *ext_endp;
+ /* Start of payload and hopefull_flags, used by transcode */
+ Uint hopefull_flags;
byte *msg_start;
- byte data[1];
+ /* start of the ext buffer, this is not always the same as extp
+ as the atom cache handling can use less then the allotted buffer.
+ This value is needed to calculate the size of this output buffer.*/
+ byte *ext_start;
+
};
typedef struct {
@@ -161,7 +172,46 @@ struct dist_entry_ {
ErtsThrPrgrLaterOp later_op;
struct transcode_context* transcode_ctx;
+
+ struct dist_sequences *sequences; /* Ongoing distribution sequences */
+};
+
+/*
+#define ERL_NODE_BOOKKEEP
+ * Bookkeeping of ErlNode inc and dec operations to help debug refc problems.
+ * This is best used together with cerl -rr. Type the below into gdb:
+ * gdb:
+set pagination off
+set $i = 0
+set $node = referred_nodes[$node_ix].node
+while $i < $node->slot.counter
+ printf "%p: ", $node->books[$i].term
+ etp-1 $node->books[$i].who
+ printf " "
+ p $node->books[$i].what
+ set $i++
+end
+
+ * Then save that into a file called test.txt and run the below in
+ * an erlang shell in order to get all inc/dec that do not have a
+ * match.
+
+f(), {ok, B} = file:read_file("test.txt").
+Vs = [begin [Val, _, _, _, What] = All = string:lexemes(Ln, " "),{Val,What,All} end || Ln <- string:lexemes(B,"\n")].
+Accs = lists:foldl(fun({V,<<"ERL_NODE_INC">>,_},M) -> Val = maps:get(V,M,0), M#{ V => Val + 1 }; ({V,<<"ERL_NODE_DEC">>,_},M) -> Val = maps:get(V,M,0), M#{ V => Val - 1 } end, #{}, Vs).
+lists:usort(lists:filter(fun({V,N}) -> N /= 0 end, maps:to_list(Accs))).
+
+ * There are bound to be bugs in the the instrumentation code, but
+ * atleast this is a place to start when hunting refc bugs.
+ *
+ */
+#ifdef ERL_NODE_BOOKKEEP
+struct erl_node_bookkeeping {
+ Eterm who;
+ Eterm term;
+ enum { ERL_NODE_INC, ERL_NODE_DEC } what;
};
+#endif
typedef struct erl_node_ {
HashBucket hash_bucket; /* Hash bucket */
@@ -169,6 +219,10 @@ typedef struct erl_node_ {
Eterm sysname; /* name@host atom for efficiency */
Uint32 creation; /* Creation */
DistEntry *dist_entry; /* Corresponding dist entry */
+#ifdef ERL_NODE_BOOKKEEP
+ struct erl_node_bookkeeping books[1024];
+ erts_atomic_t slot;
+#endif
} ErlNode;
@@ -201,7 +255,7 @@ void erts_dist_table_info(fmtfn_t, void *);
void erts_set_dist_entry_not_connected(DistEntry *);
void erts_set_dist_entry_pending(DistEntry *);
void erts_set_dist_entry_connected(DistEntry *, Eterm, Uint);
-ErlNode *erts_find_or_insert_node(Eterm, Uint32);
+ErlNode *erts_find_or_insert_node(Eterm, Uint32, Eterm);
void erts_schedule_delete_node(ErlNode *);
void erts_set_this_node(Eterm, Uint);
Uint erts_node_table_size(void);
@@ -219,18 +273,38 @@ DistEntry *erts_dhandle_to_dist_entry(Eterm dhandle, Uint32* connection_id);
Eterm erts_build_dhandle(Eterm **hpp, ErlOffHeap*, DistEntry*, Uint32 conn_id);
Eterm erts_make_dhandle(Process *c_p, DistEntry*, Uint32 conn_id);
-ERTS_GLB_INLINE void erts_deref_node_entry(ErlNode *np);
+ERTS_GLB_INLINE void erts_init_node_entry(ErlNode *np, erts_aint_t val);
+ERTS_GLB_INLINE erts_aint_t erts_ref_node_entry(ErlNode *np, int min_val, Eterm term);
+ERTS_GLB_INLINE void erts_deref_node_entry(ErlNode *np, Eterm term);
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);
+#ifdef ERL_NODE_BOOKKEEP
+void erts_node_bookkeep(ErlNode *, Eterm , int);
+#else
+#define erts_node_bookkeep(...)
+#endif
#if ERTS_GLB_INLINE_INCL_FUNC_DEF
ERTS_GLB_INLINE void
-erts_deref_node_entry(ErlNode *np)
+erts_init_node_entry(ErlNode *np, erts_aint_t val)
+{
+ erts_refc_init(&np->refc, val);
+}
+
+ERTS_GLB_INLINE erts_aint_t
+erts_ref_node_entry(ErlNode *np, int min_val, Eterm term)
+{
+ erts_node_bookkeep(np, term, ERL_NODE_INC);
+ return erts_refc_inctest(&np->refc, min_val);
+}
+
+ERTS_GLB_INLINE void
+erts_deref_node_entry(ErlNode *np, Eterm term)
{
- ASSERT(np);
+ erts_node_bookkeep(np, term, ERL_NODE_DEC);
if (erts_refc_dectest(&np->refc, 0) == 0)
erts_schedule_delete_node(np);
}
diff --git a/erts/emulator/beam/erl_port_task.c b/erts/emulator/beam/erl_port_task.c
index c8f2e88127..30a7875387 100644
--- a/erts/emulator/beam/erl_port_task.c
+++ b/erts/emulator/beam/erl_port_task.c
@@ -2094,7 +2094,7 @@ begin_port_cleanup(Port *pp, ErtsPortTask **execqp, int *processing_busy_q_p)
erts_snprintf(port_str, sizeof(DTRACE_CHARBUF_NAME(port_str)), "%T", pp->common.id);
while (plp2 != NULL) {
- erts_snprintf(pid_str, sizeof(DTRACE_CHARBUF_NAME(pid_str)), "%T", plp2->pid);
+ erts_snprintf(pid_str, sizeof(DTRACE_CHARBUF_NAME(pid_str)), "%T", plp2->u.pid);
DTRACE2(process_port_unblocked, pid_str, port_str);
}
}
diff --git a/erts/emulator/beam/erl_printf_term.c b/erts/emulator/beam/erl_printf_term.c
index 9cfb7fc681..2e33a8a782 100644
--- a/erts/emulator/beam/erl_printf_term.c
+++ b/erts/emulator/beam/erl_printf_term.c
@@ -487,6 +487,8 @@ print_term(fmtfn_t fn, void* arg, Eterm obj, long *dcount) {
PRINT_UWORD(res, fn, arg, 'u', 0, 1, octet);
++bytep;
--bytesize;
+ if ((*dcount)-- <= 0)
+ goto L_done;
}
if (bitsize) {
Uint bits = bitoffs + bitsize;
@@ -521,6 +523,8 @@ print_term(fmtfn_t fn, void* arg, Eterm obj, long *dcount) {
PRINT_CHAR(res, fn, arg, octet);
++bytep;
--bytesize;
+ if ((*dcount)-- <= 0)
+ goto L_done;
}
PRINT_STRING(res, fn, arg, "\">>");
}
diff --git a/erts/emulator/beam/erl_proc_sig_queue.c b/erts/emulator/beam/erl_proc_sig_queue.c
index 18418a76e1..9c74a2c355 100644
--- a/erts/emulator/beam/erl_proc_sig_queue.c
+++ b/erts/emulator/beam/erl_proc_sig_queue.c
@@ -36,6 +36,7 @@
#include "erl_port_task.h"
#include "erl_trace.h"
#include "beam_bp.h"
+#include "erl_binary.h"
#include "big.h"
#include "erl_gc.h"
#include "bif.h"
@@ -80,6 +81,11 @@
#define ERTS_SIG_Q_TYPE_ADJUST_TRACE_INFO \
ERTS_SIG_Q_TYPE_MAX
+#define ERTS_SIG_IS_GEN_EXIT(sig) \
+ (ERTS_PROC_SIG_TYPE(((ErtsSignal *) sig)->common.tag) == ERTS_SIG_Q_TYPE_GEN_EXIT)
+#define ERTS_SIG_IS_GEN_EXIT_EXTERNAL(sig) \
+ (ASSERT(ERTS_SIG_IS_GEN_EXIT(sig)),is_non_value(get_exit_signal_data(sig)->reason))
+
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);
@@ -259,7 +265,7 @@ 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_deref_node_entry(etp->node, ref);
}
erts_free(ERTS_ALC_T_DIST_DEMONITOR, dmon);
}
@@ -294,7 +300,8 @@ 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_deref_node_entry(((ExternalThing *) &sdlnk->heap[0])->node,
+ make_boxed(&sdlnk->heap[0]));
erts_free(ERTS_ALC_T_SIG_DATA, sdlnk);
}
@@ -936,29 +943,54 @@ erts_proc_sig_privqs_len(Process *c_p)
return proc_sig_privqs_len(c_p, 0);
}
+ErtsDistExternal *
+erts_proc_sig_get_external(ErtsMessage *msgp)
+{
+ if (ERTS_SIG_IS_EXTERNAL_MSG(msgp)) {
+ return erts_get_dist_ext(msgp->data.heap_frag);
+ } else if (ERTS_SIG_IS_NON_MSG(msgp) &&
+ ERTS_SIG_IS_GEN_EXIT(msgp) &&
+ ERTS_SIG_IS_GEN_EXIT_EXTERNAL(msgp)) {
+ ErtsDistExternal *edep;
+ ErtsExitSignalData *xsigd = get_exit_signal_data(msgp);
+ ASSERT(ERTS_PROC_SIG_TYPE(((ErtsSignal *) msgp)->common.tag) == ERTS_SIG_Q_TYPE_GEN_EXIT);
+ ASSERT(is_non_value(xsigd->reason));
+ if (msgp->hfrag.next == NULL)
+ edep = (ErtsDistExternal*)(xsigd + 1);
+ else
+ edep = erts_get_dist_ext(msgp->hfrag.next);
+ return edep;
+ }
+ return NULL;
+}
+
static 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)
+ Sint16 op, Eterm reason, ErtsDistExternal *dist_ext,
+ ErlHeapFragment *dist_ext_hfrag,
+ 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;
+ Uint hsz, from_sz, reason_sz, ref_sz, token_sz, dist_ext_sz;
int seq_trace;
#ifdef USE_VM_PROBES
Eterm s_utag, utag;
Uint utag_sz;
#endif
+ ASSERT((is_value(reason) && dist_ext == NULL) ||
+ (is_non_value(reason) && dist_ext != NULL));
+
ASSERT(is_immed(from_tag));
- hsz = sizeof(ErtsExitSignalData)/sizeof(Uint);
+ hsz = sizeof(ErtsExitSignalData)/sizeof(Eterm);
seq_trace = c_p && have_seqtrace(token);
if (seq_trace)
@@ -977,33 +1009,42 @@ send_gen_exit_signal(Process *c_p, Eterm from_tag,
hsz += utag_sz;
#endif
- token_sz = is_immed(token) ? 0 : size_object(token);
+ token_sz = size_object(token);
hsz += token_sz;
- from_sz = is_immed(from) ? 0 : size_object(from);
+ from_sz = size_object(from);
hsz += from_sz;
- reason_sz = is_immed(reason) ? 0 : size_object(reason);
- hsz += reason_sz;
+ ref_sz = size_object(ref);
+ hsz += ref_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;
+ /* The reason was part of the control message,
+ just use copy it into the xsigd */
+ if (is_value(reason)) {
+ reason_sz = 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 */
+ break;
+ }
+ case ERTS_SIG_Q_OP_MONITOR_DOWN: {
+ /* {'DOWN', Ref, process, From, Reason} */
+ hsz += 6; /* 5-tuple */
+ break;
+ }
+ default:
+ ERTS_INTERNAL_ERROR("Invalid exit signal op");
+ break;
+ }
+ } else if (dist_ext != NULL && dist_ext_hfrag == NULL) {
+ /* The message was not fragmented so we need to create space
+ for a single dist_ext element */
+ dist_ext_sz = erts_dist_ext_size(dist_ext) / sizeof(Eterm);
+ hsz += dist_ext_sz;
}
/*
@@ -1015,35 +1056,33 @@ send_gen_exit_signal(Process *c_p, Eterm from_tag,
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_token = copy_struct(token, token_sz, &hp, ohp);
+ s_from = copy_struct(from, from_sz, &hp, ohp);
+ s_ref = copy_struct(ref, ref_sz, &hp, ohp);
- s_from = (is_immed(from)
- ? from
- : copy_struct(from, from_sz, &hp, ohp));
+ if (is_value(reason)) {
+ s_reason = copy_struct(reason, reason_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;
+ 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;
+ default:
+ /* This cannot happen, used to silence gcc warning */
+ s_message = THE_NON_VALUE;
+ break;
+ }
+ } else {
+ s_message = THE_NON_VALUE;
+ s_reason = THE_NON_VALUE;
}
#ifdef USE_VM_PROBES
@@ -1061,11 +1100,13 @@ send_gen_exit_signal(Process *c_p, Eterm from_tag,
hfrag->used_size = hp - start_hp;
- xsigd = (ErtsExitSignalData *) (char *) hp;
+ xsigd = (ErtsExitSignalData *) hp;
xsigd->message = s_message;
xsigd->from = s_from;
xsigd->reason = s_reason;
+ hfrag->next = dist_ext_hfrag;
+
if (is_nil(s_ref))
xsigd->u.normal_kills = normal_kills;
else {
@@ -1073,6 +1114,15 @@ send_gen_exit_signal(Process *c_p, Eterm from_tag,
xsigd->u.ref = s_ref;
}
+ hp += sizeof(ErtsExitSignalData)/sizeof(Eterm);
+
+ if (dist_ext != NULL && dist_ext_hfrag == NULL && is_non_value(reason)) {
+ erts_make_dist_ext_copy(dist_ext, (ErtsDistExternal *) hp);
+ hp += dist_ext_sz;
+ }
+
+ ASSERT(hp == mp->hfrag.mem + mp->hfrag.alloc_size);
+
if (seq_trace)
do_seq_trace_output(to, s_token, s_message);
@@ -1205,7 +1255,19 @@ erts_proc_sig_send_exit(Process *c_p, Eterm from, Eterm to,
from_tag = dep->sysname;
}
send_gen_exit_signal(c_p, from_tag, from, to, ERTS_SIG_Q_OP_EXIT,
- reason, NIL, token, normal_kills);
+ reason, NULL, NULL, NIL, token, normal_kills);
+}
+
+void
+erts_proc_sig_send_dist_exit(DistEntry *dep,
+ Eterm from, Eterm to,
+ ErtsDistExternal *dist_ext,
+ ErlHeapFragment *hfrag,
+ Eterm reason, Eterm token)
+{
+ send_gen_exit_signal(NULL, dep->sysname, from, to, ERTS_SIG_Q_OP_EXIT,
+ reason, dist_ext, hfrag, NIL, token, 0);
+
}
void
@@ -1219,7 +1281,7 @@ erts_proc_sig_send_link_exit(Process *c_p, Eterm from, ErtsLink *lnk,
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);
+ reason, NULL, NULL, NIL, token, 0);
}
else {
/* Pass signal using old link structure... */
@@ -1274,10 +1336,13 @@ erts_proc_sig_send_unlink(Process *c_p, ErtsLink *lnk)
void
erts_proc_sig_send_dist_link_exit(DistEntry *dep,
Eterm from, Eterm to,
+ ErtsDistExternal *dist_ext,
+ ErlHeapFragment *hfrag,
Eterm reason, Eterm token)
{
send_gen_exit_signal(NULL, dep->sysname, from, to, ERTS_SIG_Q_OP_EXIT_LINKED,
- reason, NIL, token, 0);
+ reason, dist_ext, hfrag, NIL, token, 0);
+
}
void
@@ -1299,16 +1364,18 @@ erts_proc_sig_send_dist_unlink(DistEntry *dep, Eterm from, Eterm to)
void
erts_proc_sig_send_dist_monitor_down(DistEntry *dep, Eterm ref,
Eterm from, Eterm to,
+ ErtsDistExternal *dist_ext,
+ ErlHeapFragment *hfrag,
Eterm reason)
{
Eterm monitored, heap[3];
- if (is_atom(from))
+ 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);
+ reason, dist_ext, hfrag, ref, NIL, 0);
}
void
@@ -1376,10 +1443,10 @@ erts_proc_sig_send_monitor_down(ErtsMonitor *mon, Eterm reason)
|| 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);
+ reason, NULL, NULL, mdp->ref, NIL, 0);
}
erts_monitor_release(mon);
}
@@ -2037,7 +2104,6 @@ handle_exit_signal(Process *c_p, ErtsSigRecvTracing *tracing,
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 {
@@ -2062,6 +2128,18 @@ handle_exit_signal(Process *c_p, ErtsSigRecvTracing *tracing,
}
}
+ /* This GEN_EXIT was received from another node, decode the exit reason */
+ if (ERTS_SIG_IS_GEN_EXIT_EXTERNAL(sig))
+ erts_proc_sig_decode_dist(c_p, ERTS_PROC_LOCK_MAIN, sig, 1);
+
+ reason = xsigd->reason;
+
+ if (is_non_value(reason)) {
+ /* Bad distribution message; remove it from queue... */
+ ignore = !0;
+ destroy = !0;
+ }
+
if (!ignore) {
if ((op != ERTS_SIG_Q_OP_EXIT || reason != am_kill)
@@ -2929,6 +3007,104 @@ handle_sync_suspend(Process *c_p, ErtsMessage *mp)
}
}
+int
+erts_proc_sig_decode_dist(Process *proc, ErtsProcLocks proc_locks,
+ ErtsMessage *msgp, int force_off_heap)
+{
+ ErtsHeapFactory factory;
+ ErlHeapFragment *hfrag;
+ Eterm msg;
+ Sint need;
+ ErtsDistExternal *edep;
+ ErtsExitSignalData *xsigd = NULL;
+
+ edep = erts_proc_sig_get_external(msgp);
+ if (!ERTS_SIG_IS_EXTERNAL_MSG(msgp))
+ xsigd = get_exit_signal_data(msgp);
+
+ if (edep->heap_size >= 0)
+ need = edep->heap_size;
+ else {
+ need = erts_decode_dist_ext_size(edep, 1);
+ if (need < 0) {
+ /* bad signal; remove it... */
+ return 0;
+ }
+
+ edep->heap_size = need;
+ }
+
+ if (ERTS_SIG_IS_NON_MSG(msgp)) {
+ switch (ERTS_PROC_SIG_OP(ERL_MESSAGE_TERM(msgp))) {
+ case ERTS_SIG_Q_OP_EXIT:
+ case ERTS_SIG_Q_OP_EXIT_LINKED:
+ /* {'EXIT', From, Reason} */
+ need += 4;
+ break;
+ case ERTS_SIG_Q_OP_MONITOR_DOWN:
+ /* {'DOWN', Ref, process, From, Reason} */
+ need += 6; /* 5-tuple */
+ break;
+ default:
+ ERTS_INTERNAL_ERROR("Invalid exit signal op");
+ break;
+ }
+ }
+
+ hfrag = new_message_buffer(need);
+ erts_factory_heap_frag_init(&factory, hfrag);
+
+ ASSERT(edep->heap_size >= 0);
+
+ msg = erts_decode_dist_ext(&factory, edep, 1);
+
+ if (is_non_value(msg)) {
+ erts_factory_undo(&factory);
+ return 0;
+ }
+
+ if (ERTS_SIG_IS_MSG(msgp)) {
+ ERL_MESSAGE_TERM(msgp) = msg;
+ if (msgp->data.heap_frag == &msgp->hfrag)
+ msgp->data.heap_frag = ERTS_MSG_COMBINED_HFRAG;
+ } else {
+ switch (ERTS_PROC_SIG_OP(ERL_MESSAGE_TERM(msgp))) {
+ case ERTS_SIG_Q_OP_EXIT:
+ case ERTS_SIG_Q_OP_EXIT_LINKED:
+ /* {'EXIT', From, Reason} */
+ erts_reserve_heap(&factory, 4);
+ xsigd->message = TUPLE3(factory.hp, am_EXIT, xsigd->from, msg);
+ factory.hp += 4;
+ break;
+ case ERTS_SIG_Q_OP_MONITOR_DOWN:
+ /* {'DOWN', Ref, process, From, Reason} */
+ erts_reserve_heap(&factory, 6);
+ xsigd->message = TUPLE5(factory.hp, am_DOWN, xsigd->u.ref, am_process, xsigd->from, msg);
+ factory.hp += 6;
+ break;
+ }
+ xsigd->reason = msg;
+ }
+
+ erts_free_dist_ext_copy(edep);
+
+ erts_factory_close(&factory);
+
+ hfrag = factory.heap_frags;
+ while (hfrag->next)
+ hfrag = hfrag->next;
+
+ if (ERTS_SIG_IS_MSG(msgp) && msgp->data.heap_frag != ERTS_MSG_COMBINED_HFRAG) {
+ hfrag->next = msgp->data.heap_frag;
+ msgp->data.heap_frag = factory.heap_frags;
+ } else {
+ hfrag->next = msgp->hfrag.next;
+ msgp->hfrag.next = factory.heap_frags;
+ }
+
+ return 1;
+}
+
void
erts_proc_sig_handle_pending_suspend(Process *c_p)
{
@@ -3045,7 +3221,7 @@ erts_proc_sig_handle_incoming(Process *c_p, erts_aint32_t *statep,
ASSERT(ERTS_SIG_IS_NON_MSG(sig));
tag = ((ErtsSignal *) sig)->common.tag;
-
+
switch (ERTS_PROC_SIG_OP(tag)) {
case ERTS_SIG_Q_OP_EXIT:
@@ -3091,6 +3267,12 @@ erts_proc_sig_handle_incoming(Process *c_p, erts_aint32_t *statep,
break;
case ERTS_SIG_Q_TYPE_GEN_EXIT:
xsigd = get_exit_signal_data(sig);
+
+ /* This GEN_EXIT was received from another node, decode the exit reason */
+ if (ERTS_SIG_IS_GEN_EXIT_EXTERNAL(sig))
+ if (!erts_proc_sig_decode_dist(c_p, ERTS_PROC_LOCK_MAIN, sig, 1))
+ break; /* Decode failed, just remove signal */
+
omon = erts_monitor_tree_lookup(ERTS_P_MONITORS(c_p),
xsigd->u.ref);
if (omon) {
@@ -3590,9 +3772,10 @@ stretch_limit(Process *c_p, ErtsSigRecvTracing *tp,
int
-erts_proc_sig_handle_exit(Process *c_p, int *redsp)
+erts_proc_sig_handle_exit(Process *c_p, Sint *redsp)
{
- int cnt, limit;
+ int cnt;
+ Sint limit;
ErtsMessage *sig, ***next_nm_sig;
ERTS_HDBG_CHECK_SIGNAL_PRIV_QUEUE(c_p, 0);
@@ -3671,9 +3854,9 @@ erts_proc_sig_handle_exit(Process *c_p, int *redsp)
break;
case ERTS_SIG_Q_OP_MONITOR: {
- ErtsProcExitContext pectxt = {c_p, am_noproc};
+ ErtsProcExitContext pectxt = {c_p, am_noproc, NULL, NULL, NIL};
erts_proc_exit_handle_monitor((ErtsMonitor *) sig,
- (void *) &pectxt);
+ (void *) &pectxt, -1);
cnt += 4;
break;
}
@@ -3687,7 +3870,7 @@ erts_proc_sig_handle_exit(Process *c_p, int *redsp)
case ERTS_SIG_Q_OP_LINK: {
ErtsProcExitContext pectxt = {c_p, am_noproc};
- erts_proc_exit_handle_link((ErtsLink *) sig, (void *) &pectxt);
+ erts_proc_exit_handle_link((ErtsLink *) sig, (void *) &pectxt, -1);
break;
}
@@ -4188,11 +4371,13 @@ handle_msg_tracing(Process *c_p, ErtsSigRecvTracing *tracing,
return -1; /* Yield... */
}
if (ERTS_SIG_IS_EXTERNAL_MSG(sig)) {
- cnt++;
- if (!erts_decode_dist_message(c_p, ERTS_PROC_LOCK_MAIN,
- sig, 0)) {
+ cnt += 50; /* Decode is expensive... */
+ if (!erts_proc_sig_decode_dist(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 = NULL;
+ erts_cleanup_messages(sig);
sig = *next_sig;
continue;
}
@@ -4264,18 +4449,12 @@ erts_proc_sig_prep_msgq_for_inspection(Process *c_p,
if (ERTS_SIG_IS_EXTERNAL_MSG(mp)) {
/* decode it... */
- if (mp->data.attached)
- erts_decode_dist_message(rp, rp_locks, mp, !0);
-
- msg = ERL_MESSAGE_TERM(mp);
-
- if (is_non_value(msg)) {
+ if (!erts_proc_sig_decode_dist(rp, rp_locks, mp, !0)) {
ErtsMessage *bad_mp = mp;
/*
* Bad distribution message; remove
* it from the queue...
*/
- ASSERT(!mp->data.attached);
ASSERT(*mpp == bad_mp);
@@ -4287,6 +4466,8 @@ erts_proc_sig_prep_msgq_for_inspection(Process *c_p,
erts_cleanup_messages(bad_mp);
continue;
}
+
+ msg = ERL_MESSAGE_TERM(mp);
}
ASSERT(is_value(msg));
@@ -4445,12 +4626,21 @@ debug_foreach_sig_fake_oh(Eterm term,
}
+static void
+debug_foreach_sig_external(ErtsMessage *msgp,
+ void (*ext_func)(ErtsDistExternal *, void *),
+ void *arg)
+{
+ ext_func(erts_proc_sig_get_external(msgp), 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 *),
+ ErtsMonitorFunc mon_func,
+ ErtsLinkFunc lnk_func,
+ void (*ext_func)(ErtsDistExternal *, void *),
void *arg)
{
ErtsMessage *queue[] = {c_p->sig_qs.first, c_p->sig_qs.cont, c_p->sig_inq.first};
@@ -4459,10 +4649,10 @@ erts_proc_sig_debug_foreach_sig(Process *c_p,
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))
+
+ if (ERTS_SIG_IS_MSG(sig)) {
msg_func(sig, arg);
- else {
+ } else {
Eterm tag;
Uint16 type;
int op;
@@ -4481,18 +4671,21 @@ erts_proc_sig_debug_foreach_sig(Process *c_p,
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);
+ if (ERTS_SIG_IS_GEN_EXIT_EXTERNAL(sig))
+ debug_foreach_sig_external(sig, ext_func, arg);
+ else
+ 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);
+ lnk_func((ErtsLink *) sig, arg, -1);
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);
+ mon_func((ErtsMonitor *) sig, arg, -1);
break;
default:
ERTS_INTERNAL_ERROR("Unexpected sig type");
@@ -4513,7 +4706,7 @@ erts_proc_sig_debug_foreach_sig(Process *c_p,
/* Fall through... */
case ERTS_SIG_Q_OP_MONITOR:
- mon_func((ErtsMonitor *) sig, arg);
+ mon_func((ErtsMonitor *) sig, arg, -1);
break;
case ERTS_SIG_Q_OP_UNLINK:
@@ -4525,7 +4718,7 @@ erts_proc_sig_debug_foreach_sig(Process *c_p,
/* Fall through... */
case ERTS_SIG_Q_OP_LINK:
- lnk_func((ErtsLink *) sig, arg);
+ lnk_func((ErtsLink *) sig, arg, -1);
break;
case ERTS_SIG_Q_OP_GROUP_LEADER: {
diff --git a/erts/emulator/beam/erl_proc_sig_queue.h b/erts/emulator/beam/erl_proc_sig_queue.h
index 6b065a7add..2b055e73bc 100644
--- a/erts/emulator/beam/erl_proc_sig_queue.h
+++ b/erts/emulator/beam/erl_proc_sig_queue.h
@@ -89,6 +89,7 @@
#endif
struct erl_mesg;
+struct erl_dist_external;
typedef struct {
struct erl_mesg *next;
@@ -212,6 +213,38 @@ erts_proc_sig_send_exit(Process *c_p, Eterm from, Eterm to,
/**
*
+ * @brief Send an exit signal to a process.
+ *
+ * This function is used instead of erts_proc_sig_send_link_exit()
+ * when the signal arrives via the distribution and
+ * therefore 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] dist_ext The exit reason in external term format
+ *
+ * @param[in] hfrag Heap frag with trace token and dist_ext
+ * iff available, otherwise NULL.
+ *
+ * @param[in] reason Exit reason.
+ *
+ * @param[in] token Seq trace token.
+ *
+ */
+void
+erts_proc_sig_send_dist_exit(DistEntry *dep,
+ Eterm from, Eterm to,
+ ErtsDistExternal *dist_ext,
+ ErlHeapFragment *hfrag,
+ Eterm reason, Eterm token);
+
+/**
+ *
* @brief Send an exit signal due to broken link to a process.
*
*
@@ -282,7 +315,7 @@ erts_proc_sig_send_unlink(Process *c_p, ErtsLink *lnk);
*
* 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.
+ * therefore no link structure is available.
*
* @param[in] dep Distribution entry of channel
* that the signal arrived on.
@@ -291,6 +324,11 @@ erts_proc_sig_send_unlink(Process *c_p, ErtsLink *lnk);
*
* @param[in] to Identifier of receiver.
*
+ * @param[in] dist_ext The exit reason in external term format
+ *
+ * @param[in] hfrag Heap frag with trace token and dist_ext
+ * iff available, otherwise NULL.
+ *
* @param[in] reason Exit reason.
*
* @param[in] token Seq trace token.
@@ -299,6 +337,8 @@ erts_proc_sig_send_unlink(Process *c_p, ErtsLink *lnk);
void
erts_proc_sig_send_dist_link_exit(struct dist_entry_ *dep,
Eterm from, Eterm to,
+ ErtsDistExternal *dist_ext,
+ ErlHeapFragment *hfrag,
Eterm reason, Eterm token);
/**
@@ -307,7 +347,7 @@ erts_proc_sig_send_dist_link_exit(struct dist_entry_ *dep,
*
* This function is used instead of erts_proc_sig_send_unlink()
* when the signal arrives via the distribution and
- * no link structure is available.
+ * therefore no link structure is available.
*
* @param[in] dep Distribution entry of channel
* that the signal arrived on.
@@ -380,7 +420,7 @@ erts_proc_sig_send_monitor(ErtsMonitor *mon, Eterm to);
*
* 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.
+ * therefore no monitor structure is available.
*
* @param[in] dep Pointer to distribution entry
* of channel that the signal
@@ -392,12 +432,19 @@ erts_proc_sig_send_monitor(ErtsMonitor *mon, Eterm to);
*
* @param[in] to Identifier of receiver.
*
+ * @param[in] dist_ext The exit reason in external term format
+ *
+ * @param[in] hfrag Heap frag with trace token and dist_ext
+ * iff available, otherwise NULL.
+ *
* @param[in] reason Exit reason.
*
*/
void
erts_proc_sig_send_dist_monitor_down(DistEntry *dep, Eterm ref,
Eterm from, Eterm to,
+ ErtsDistExternal *dist_ext,
+ ErlHeapFragment *hfrag,
Eterm reason);
/**
@@ -740,7 +787,7 @@ erts_proc_sig_handle_incoming(Process *c_p, erts_aint32_t *statep,
* queue.
*/
int
-erts_proc_sig_handle_exit(Process *c_p, int *redsp);
+erts_proc_sig_handle_exit(Process *c_p, Sint *redsp);
/**
*
@@ -962,6 +1009,34 @@ void
erts_proc_sig_handle_pending_suspend(Process *c_p);
/**
+ *
+ * @brief Decode the reason term in an external signal
+ *
+ * Any distributed signal with a payload only has the control
+ * message decoded by the dist entry. The final decode of the
+ * payload is done by the process when it inspects the signal
+ * by calling this function.
+ *
+ * This functions handles both messages and link/monitor exits.
+ *
+ * Return true if the decode was successful, false otherwise.
+ *
+ * @param[in] c_p Pointer to executing process
+ *
+ * @param[in] proc_lock Locks held by process. Should always be MAIN.
+ *
+ * @param[in] msgp The signal to decode
+ *
+ * @param[in] force_off_heap If the term should be forced to be off-heap
+ */
+int
+erts_proc_sig_decode_dist(Process *proc, ErtsProcLocks proc_locks,
+ ErtsMessage *msgp, int force_off_heap);
+
+ErtsDistExternal *
+erts_proc_sig_get_external(ErtsMessage *msgp);
+
+/**
* @brief Initialize this functionality
*/
void erts_proc_sig_queue_init(void);
@@ -970,8 +1045,9 @@ 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 *),
+ ErtsMonitorFunc mon_func,
+ ErtsLinkFunc lnk_func,
+ void (*ext_func)(ErtsDistExternal *, void *),
void *arg);
extern Process *erts_dirty_process_signal_handler;
diff --git a/erts/emulator/beam/erl_process.c b/erts/emulator/beam/erl_process.c
index c2799f6612..0a099e69bb 100644
--- a/erts/emulator/beam/erl_process.c
+++ b/erts/emulator/beam/erl_process.c
@@ -735,12 +735,6 @@ erts_pre_init_process(void)
#endif
}
-static void
-release_process(void *vproc)
-{
- erts_proc_dec_refc((Process *) vproc);
-}
-
/* initialize the scheduler */
void
erts_init_process(int ncpu, int proc_tab_size, int legacy_proc_tab)
@@ -752,7 +746,7 @@ erts_init_process(int ncpu, int proc_tab_size, int legacy_proc_tab)
erts_ptab_init_table(&erts_proc,
ERTS_ALC_T_PROC_TABLE,
- release_process,
+ NULL,
(ErtsPTabElementCommon *) &erts_invalid_process.common,
proc_tab_size,
sizeof(Process),
@@ -1476,7 +1470,10 @@ proclist_create(Process *p)
{
ErtsProcList *plp = proclist_alloc();
ensure_later_proc_interval(p->common.u.alive.started_interval);
- plp->pid = p->common.id;
+ if (erts_atomic32_read_nob(&p->state) & ERTS_PSFLG_FREE)
+ plp->u.p = p;
+ else
+ plp->u.pid = p->common.id;
plp->started_interval = p->common.u.alive.started_interval;
return plp;
}
@@ -1485,7 +1482,7 @@ static ERTS_INLINE ErtsProcList *
proclist_copy(ErtsProcList *plp0)
{
ErtsProcList *plp1 = proclist_alloc();
- plp1->pid = plp0->pid;
+ plp1->u.pid = plp0->u.pid;
plp1->started_interval = plp0->started_interval;
return plp1;
}
@@ -1520,7 +1517,10 @@ erts_proclist_dump(fmtfn_t to, void *to_arg, ErtsProcList *plp)
ErtsProcList *first = plp;
while (plp) {
- erts_print(to, to_arg, "%T", plp->pid);
+ if (is_pid(plp->u.pid))
+ erts_print(to, to_arg, "%T", plp->u.pid);
+ else
+ erts_print(to, to_arg, "%T", plp->u.p->common.id);
plp = plp->next;
if (plp == first)
break;
@@ -2462,6 +2462,13 @@ handle_reap_ports(ErtsAuxWorkData *awdp, erts_aint32_t aux_work, int waiting)
erts_port_lock(prt);
+ if (prt->common.u.alive.reg &&
+ prt->common.u.alive.reg->name == am_heart_port) {
+ /* Leave heart port to not get killed before flushing is done*/
+ erts_port_release(prt);
+ continue;
+ }
+
state = erts_atomic32_read_nob(&prt->state);
if (!(state & (ERTS_PORT_SFLGS_INVALID_DRIVER_LOOKUP
| ERTS_PORT_SFLG_HALT))) {
@@ -6519,8 +6526,7 @@ schedule_out_process(ErtsRunQueue *c_rq, erts_aint32_t state, Process *p,
n &= ~running_flgs;
if ((!!(a & (ERTS_PSFLG_ACTIVE_SYS|ERTS_PSFLG_DIRTY_ACTIVE_SYS))
- | ((a & (ERTS_PSFLG_ACTIVE|ERTS_PSFLG_SUSPENDED)) == ERTS_PSFLG_ACTIVE))
- & !(a & ERTS_PSFLG_FREE)) {
+ | ((a & (ERTS_PSFLG_ACTIVE|ERTS_PSFLG_SUSPENDED)) == ERTS_PSFLG_ACTIVE))) {
enqueue = check_enqueue_in_prio_queue(p, &enq_prio, &n, a);
}
a = erts_atomic32_cmpxchg_mb(&p->state, n, e);
@@ -6555,7 +6561,6 @@ schedule_out_process(ErtsRunQueue *c_rq, erts_aint32_t state, Process *p,
else {
Process* sched_p;
- ASSERT(!(n & ERTS_PSFLG_FREE));
ASSERT(!(n & ERTS_PSFLG_SUSPENDED) || (n & (ERTS_PSFLG_ACTIVE_SYS
| ERTS_PSFLG_DIRTY_ACTIVE_SYS)));
@@ -6685,8 +6690,8 @@ change_proc_schedule_state(Process *p,
enqueue = ERTS_ENQUEUE_NOT;
- if (a & ERTS_PSFLG_FREE)
- break; /* We don't want to schedule free processes... */
+ if ((a & (ERTS_PSFLG_FREE|ERTS_PSFLG_ACTIVE)) == ERTS_PSFLG_FREE)
+ break; /* If free and not active, do not schedule */
if (clear_state_flags)
n &= ~clear_state_flags;
@@ -7212,8 +7217,7 @@ schdlr_sspnd_resume_procs(ErtsSchedType sched_type,
while (resume->msb.chngrs) {
ErtsProcList *plp = resume->msb.chngrs;
resume->msb.chngrs = plp->next;
- schdlr_sspnd_resume_proc(sched_type,
- plp->pid);
+ schdlr_sspnd_resume_proc(sched_type, plp->u.pid);
proclist_destroy(plp);
}
}
@@ -7645,7 +7649,7 @@ suspend_scheduler(ErtsSchedulerData *esdp)
else {
schdlr_sspnd.changer = am_true; /* change right in transit */
/* resume process that is queued for next change... */
- resume.onln.nxt = plp->pid;
+ resume.onln.nxt = plp->u.pid;
ASSERT(is_internal_pid(resume.onln.nxt));
}
}
@@ -7865,7 +7869,7 @@ abort_sched_onln_chng_waitq(Process *p)
proclist_destroy(plp);
plp = erts_proclist_peek_first(schdlr_sspnd.chngq);
if (plp)
- resume = plp->pid;
+ resume = plp->u.pid;
else
schdlr_sspnd.changer = am_false;
}
@@ -8131,7 +8135,8 @@ done:
ErtsSchedSuspendResult
erts_block_multi_scheduling(Process *p, ErtsProcLocks plocks, int on, int normal, int all)
{
- int resume_proc, ix, res, have_unlocked_plocks = 0;
+ ErtsSchedSuspendResult res;
+ int resume_proc, ix, have_unlocked_plocks = 0;
ErtsProcList *plp;
ErtsMultiSchedulingBlock *msbp;
erts_aint32_t chng_flg;
@@ -8364,10 +8369,10 @@ erts_multi_scheduling_blockers(Process *p, int normal)
plp1;
plp1 = erts_proclist_peek_next(msbp->blckrs, plp1)) {
for (plp2 = erts_proclist_peek_first(msbp->blckrs);
- plp2->pid != plp1->pid;
+ plp2->u.pid != plp1->u.pid;
plp2 = erts_proclist_peek_next(msbp->blckrs, plp2));
if (plp2 == plp1) {
- res = CONS(hp, plp1->pid, res);
+ res = CONS(hp, plp1->u.pid, res);
hp += 2;
}
/* else: already in result list */
@@ -9036,8 +9041,13 @@ erts_resume_processes(ErtsProcList *list)
while (plp) {
Process *proc;
ErtsProcList *fplp;
- ASSERT(is_internal_pid(plp->pid));
- proc = erts_pid2proc(NULL, 0, plp->pid, ERTS_PROC_LOCK_STATUS);
+ ASSERT(is_internal_pid(plp->u.pid) || is_CP((Eterm)plp->u.p));
+ if (is_internal_pid(plp->u.pid))
+ proc = erts_pid2proc(NULL, 0, plp->u.pid, ERTS_PROC_LOCK_STATUS);
+ else {
+ proc = plp->u.p;
+ erts_proc_lock(proc, ERTS_PROC_LOCK_STATUS);
+ }
if (proc) {
if (erts_proclist_same(plp, proc)) {
resume_process(proc, ERTS_PROC_LOCK_STATUS);
@@ -9629,7 +9639,8 @@ Process *erts_schedule(ErtsSchedulerData *esdp, Process *p, int calls)
while (1) {
erts_aint32_t exp, new;
- int run_process;
+ int run_process, not_running, exiting_on_normal_sched,
+ not_suspended, not_exiting_on_dirty_sched;
new = exp = state;
new &= psflg_band_mask;
/*
@@ -9638,29 +9649,33 @@ Process *erts_schedule(ErtsSchedulerData *esdp, Process *p, int calls)
* scheduler, and not suspended (and not in a
* state where suspend should be ignored).
*/
- run_process = (((!(state & (ERTS_PSFLG_RUNNING
- | ERTS_PSFLG_RUNNING_SYS
- | ERTS_PSFLG_DIRTY_RUNNING
- | ERTS_PSFLG_DIRTY_RUNNING_SYS
- | ERTS_PSFLG_FREE)))
- | (((state & (ERTS_PSFLG_RUNNING
-
- | ERTS_PSFLG_FREE
- | ERTS_PSFLG_RUNNING_SYS
- | ERTS_PSFLG_DIRTY_RUNNING_SYS
- | ERTS_PSFLG_EXITING))
- == ERTS_PSFLG_EXITING)
- & (!!is_normal_sched))
- )
- & ((state & (ERTS_PSFLG_SUSPENDED
- | ERTS_PSFLG_EXITING
- | ERTS_PSFLG_FREE
- | ERTS_PSFLG_ACTIVE_SYS
- | ERTS_PSFLG_DIRTY_ACTIVE_SYS))
- != ERTS_PSFLG_SUSPENDED)
- & (!(state & ERTS_PSFLG_EXITING)
- | (!!is_normal_sched))
- );
+ not_running = !(state & (ERTS_PSFLG_RUNNING
+ | ERTS_PSFLG_RUNNING_SYS
+ | ERTS_PSFLG_DIRTY_RUNNING
+ | ERTS_PSFLG_DIRTY_RUNNING_SYS
+ | ERTS_PSFLG_FREE));
+ exiting_on_normal_sched =
+ ((state & (ERTS_PSFLG_RUNNING
+ | ERTS_PSFLG_ACTIVE
+ | ERTS_PSFLG_RUNNING_SYS
+ | ERTS_PSFLG_DIRTY_RUNNING_SYS
+ | ERTS_PSFLG_EXITING))
+ == (ERTS_PSFLG_EXITING|ERTS_PSFLG_ACTIVE))
+ & (!!is_normal_sched);
+
+
+ not_suspended = ((state & (ERTS_PSFLG_SUSPENDED
+ | ERTS_PSFLG_EXITING
+ | ERTS_PSFLG_FREE
+ | ERTS_PSFLG_ACTIVE_SYS
+ | ERTS_PSFLG_DIRTY_ACTIVE_SYS))
+ != ERTS_PSFLG_SUSPENDED);
+
+ not_exiting_on_dirty_sched = !(state & ERTS_PSFLG_EXITING) | (!!is_normal_sched);
+
+ run_process = (not_running | exiting_on_normal_sched)
+ & not_suspended
+ & not_exiting_on_dirty_sched;
if (run_process) {
if (state & (ERTS_PSFLG_ACTIVE_SYS
@@ -9951,7 +9966,7 @@ trace_schedule_in(Process *p, erts_aint32_t state)
/* Clear tracer if it has been removed */
if (erts_is_tracer_proc_enabled(p, ERTS_PROC_LOCK_MAIN, &p->common)) {
- if (state & ERTS_PSFLG_EXITING) {
+ if (state & ERTS_PSFLG_EXITING && p->u.terminate) {
if (ARE_TRACE_FLAGS_ON(p, F_TRACE_SCHED_EXIT))
trace_sched(p, ERTS_PROC_LOCK_MAIN, am_in_exiting);
}
@@ -9975,12 +9990,9 @@ trace_schedule_out(Process *p, erts_aint32_t state)
if (IS_TRACED_FL(p, F_TRACE_CALLS) && !(state & ERTS_PSFLG_FREE))
erts_schedule_time_break(p, ERTS_BP_CALL_TIME_SCHEDULE_OUT);
- if ((state & (ERTS_PSFLG_FREE|ERTS_PSFLG_EXITING)) == ERTS_PSFLG_EXITING) {
+ if (state & ERTS_PSFLG_EXITING && p->u.terminate) {
if (ARE_TRACE_FLAGS_ON(p, F_TRACE_SCHED_EXIT))
- trace_sched(p, ERTS_PROC_LOCK_MAIN,
- ((state & ERTS_PSFLG_FREE)
- ? am_out_exited
- : am_out_exiting));
+ trace_sched(p, ERTS_PROC_LOCK_MAIN, am_out_exiting);
}
else {
if (ARE_TRACE_FLAGS_ON(p, F_TRACE_SCHED) ||
@@ -12055,12 +12067,91 @@ erts_set_self_exiting(Process *c_p, Eterm reason)
add2runq(enqueue, enq_prio, c_p, state, NULL);
}
-void
-erts_proc_exit_handle_monitor(ErtsMonitor *mon, void *vctxt)
+static int
+erts_proc_exit_handle_dist_monitor(ErtsMonitor *mon, void *vctxt, Sint reds)
+{
+ ErtsProcExitContext *ctxt = (ErtsProcExitContext *) vctxt;
+ Process *c_p = ctxt->c_p;
+ Eterm reason = ctxt->reason;
+ int code;
+ ErtsDSigSendContext ctx;
+ ErtsMonLnkDist *dist;
+ DistEntry *dep;
+ Eterm watcher;
+ ErtsMonitorData *mdp = NULL;
+ Eterm watched;
+
+ ASSERT(erts_monitor_is_target(mon) && mon->type == ERTS_MON_TYPE_DIST_PROC);
+
+ 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(&ctx, dep, c_p, ERTS_PROC_LOCK_MAIN,
+ ERTS_DSP_NO_LOCK, 0, 0, 1);
+
+ ctx.reds = (Sint) (reds * TERM_TO_BINARY_LOOP_FACTOR);
+
+ 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:
+ if (dist->connection_id != ctx.connection_id)
+ break;
+ code = erts_dsig_send_m_exit(&ctx,
+ watcher,
+ watched,
+ mdp->ref,
+ reason);
+ switch (code) {
+ case ERTS_DSIG_SEND_CONTINUE:
+ erts_set_gc_state(c_p, 0);
+ ctxt->dist_state = erts_dsend_export_trap_context(c_p, &ctx);
+ /* fall-through */
+ case ERTS_DSIG_SEND_YIELD:
+ break;
+ case ERTS_DSIG_SEND_OK:
+ break;
+ case ERTS_DSIG_SEND_TOO_LRG:
+ erts_set_gc_state(c_p, 1);
+ break;
+ default:
+ ASSERT(! "Invalid dsig send exit monitor result");
+ break;
+ }
+ break;
+ default:
+ ASSERT(! "Invalid dsig prep exit monitor result");
+ break;
+ }
+ if (!erts_monitor_dist_delete(&mdp->origin))
+ erts_monitor_release(mon);
+ else
+ erts_monitor_release_both(mdp);
+ return reds - (ctx.reds / TERM_TO_BINARY_LOOP_FACTOR);
+}
+
+int
+erts_proc_exit_handle_monitor(ErtsMonitor *mon, void *vctxt, Sint reds)
{
- Process *c_p = ((ErtsProcExitContext *) vctxt)->c_p;
- Eterm reason = ((ErtsProcExitContext *) vctxt)->reason;
+ ErtsProcExitContext *ctxt = (ErtsProcExitContext *) vctxt;
+ Process *c_p = ctxt->c_p;
+ Eterm reason = ctxt->reason;
ErtsMonitorData *mdp = NULL;
+ int res = 1;
if (erts_monitor_is_target(mon)) {
/* We are being watched... */
@@ -12090,43 +12181,48 @@ erts_proc_exit_handle_monitor(ErtsMonitor *mon, void *vctxt)
case ERTS_MON_TYPE_DIST_PROC: {
ErtsMonLnkDist *dist;
DistEntry *dep;
- ErtsDSigData dsd;
+ ErtsDSigSendContext ctx;
int code;
Eterm watcher;
Eterm watched;
- mdp = erts_monitor_to_data(mon);
+ if (is_immed(reason)) {
+ 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);
+ 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(&ctx, dep, NULL, 0,
+ ERTS_DSP_NO_LOCK, 1, 1, 0);
+ switch (code) {
+ case ERTS_DSIG_PREP_CONNECTED:
+ case ERTS_DSIG_PREP_PENDING:
+ if (dist->connection_id == ctx.connection_id) {
+ code = erts_dsig_send_m_exit(&ctx,
+ watcher,
+ watched,
+ mdp->ref,
+ reason);
+ ASSERT(code == ERTS_DSIG_SEND_OK);
+ }
+ default:
+ break;
}
- default:
- break;
+ if (!erts_monitor_dist_delete(&mdp->origin))
+ mdp = NULL;
+ } else {
+ erts_monitor_tree_insert(&ctxt->dist_monitors, mon);
+ return 1;
}
- if (!erts_monitor_dist_delete(&mdp->origin))
- mdp = NULL;
break;
}
default:
@@ -12168,7 +12264,7 @@ erts_proc_exit_handle_monitor(ErtsMonitor *mon, void *vctxt)
case ERTS_MON_TYPE_DIST_PROC: {
ErtsMonLnkDist *dist;
DistEntry *dep;
- ErtsDSigData dsd;
+ ErtsDSigSendContext ctx;
int code;
Eterm watched;
@@ -12185,17 +12281,16 @@ erts_proc_exit_handle_monitor(ErtsMonitor *mon, void *vctxt)
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);
+ code = erts_dsig_prepare(&ctx, dep, NULL, 0,
+ ERTS_DSP_NO_LOCK, 1, 1, 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,
+ if (dist->connection_id == ctx.connection_id) {
+ code = erts_dsig_send_demonitor(&ctx,
c_p->common.id,
watched,
- mdp->ref,
- 1);
+ mdp->ref);
ASSERT(code == ERTS_DSIG_SEND_OK);
}
default:
@@ -12203,6 +12298,7 @@ erts_proc_exit_handle_monitor(ErtsMonitor *mon, void *vctxt)
}
if (!erts_monitor_dist_delete(&mdp->target))
mdp = NULL;
+ res = 100;
break;
}
default:
@@ -12215,11 +12311,84 @@ erts_proc_exit_handle_monitor(ErtsMonitor *mon, void *vctxt)
erts_monitor_release_both(mdp);
else if (mon)
erts_monitor_release(mon);
+ return res;
}
-void
-erts_proc_exit_handle_link(ErtsLink *lnk, void *vctxt)
+static int
+erts_proc_exit_handle_dist_link(ErtsLink *lnk, void *vctxt, Sint reds)
+{
+ ErtsProcExitContext *ctxt = (ErtsProcExitContext *) vctxt;
+ Process *c_p = ctxt->c_p;
+ Eterm reason = ctxt->reason;
+ int code;
+ ErtsDSigSendContext ctx;
+ ErtsMonLnkDist *dist;
+ DistEntry *dep;
+ ErtsLink *dlnk;
+ ErtsLinkData *ldp = NULL;
+
+ ASSERT(lnk->type == ERTS_LNK_TYPE_DIST_PROC);
+ 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;
+
+ code = erts_dsig_prepare(&ctx, dep, c_p, ERTS_PROC_LOCK_MAIN,
+ ERTS_DSP_NO_LOCK, 0, 0, 0);
+
+ ctx.reds = (Sint) (reds * TERM_TO_BINARY_LOOP_FACTOR);
+
+ 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:
+ if (dist->connection_id != ctx.connection_id)
+ break;
+ code = erts_dsig_send_exit_tt(&ctx,
+ c_p->common.id,
+ lnk->other.item,
+ reason,
+ SEQ_TRACE_TOKEN(c_p));
+ switch (code) {
+ case ERTS_DSIG_SEND_CONTINUE:
+ erts_set_gc_state(c_p, 0);
+ ctxt->dist_state = erts_dsend_export_trap_context(c_p, &ctx);
+ /* fall-through */
+ case ERTS_DSIG_SEND_YIELD:
+ break;
+ case ERTS_DSIG_SEND_OK:
+ break;
+ case ERTS_DSIG_SEND_TOO_LRG:
+ erts_set_gc_state(c_p, 1);
+ break;
+ default:
+ ASSERT(! "Invalid dsig send exit monitor result");
+ break;
+ }
+ break;
+ default:
+ ASSERT(! "Invalid dsig prep exit monitor result");
+ break;
+ }
+ if (ldp)
+ erts_link_release_both(ldp);
+ else if (lnk)
+ erts_link_release(lnk);
+ return reds - (ctx.reds / TERM_TO_BINARY_LOOP_FACTOR);
+}
+
+int
+erts_proc_exit_handle_link(ErtsLink *lnk, void *vctxt, Sint reds)
{
+ ErtsProcExitContext *ctxt = (ErtsProcExitContext *) vctxt;
Process *c_p = ((ErtsProcExitContext *) vctxt)->c_p;
Eterm reason = ((ErtsProcExitContext *) vctxt)->reason;
ErtsLinkData *ldp = NULL;
@@ -12250,32 +12419,40 @@ erts_proc_exit_handle_link(ErtsLink *lnk, void *vctxt)
DistEntry *dep;
ErtsMonLnkDist *dist;
ErtsLink *dlnk;
- ErtsDSigData dsd;
+ ErtsDSigSendContext ctx;
int code;
- dlnk = erts_link_to_other(lnk, &ldp);
- dist = ((ErtsLinkDataExtended *) ldp)->dist;
+ if (is_immed(reason)) {
+ 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(is_external_pid(lnk->other.item));
+ dep = external_pid_dist_entry(lnk->other.item);
- ASSERT(dep != erts_this_dist_entry);
+ ASSERT(dep != erts_this_dist_entry);
- if (!erts_link_dist_delete(dlnk))
- ldp = NULL;
+ if (!erts_link_dist_delete(dlnk))
+ ldp = NULL;
- 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);
+ code = erts_dsig_prepare(&ctx, dep, c_p, 0, ERTS_DSP_NO_LOCK, 1, 1, 0);
+ switch (code) {
+ case ERTS_DSIG_PREP_CONNECTED:
+ case ERTS_DSIG_PREP_PENDING:
+ if (dist->connection_id == ctx.connection_id) {
+ code = erts_dsig_send_exit_tt(&ctx,
+ c_p->common.id,
+ lnk->other.item,
+ reason,
+ SEQ_TRACE_TOKEN(c_p));
+ ASSERT(code == ERTS_DSIG_SEND_OK);
+ }
+ break;
+ default:
+ break;
}
+ } else {
+ erts_link_tree_insert(&ctxt->dist_links, lnk);
+ return 1;
}
break;
}
@@ -12288,6 +12465,7 @@ erts_proc_exit_handle_link(ErtsLink *lnk, void *vctxt)
erts_link_release_both(ldp);
else if (lnk)
erts_link_release(lnk);
+ return 1;
}
/* this function fishishes a process and propagates exit messages - called
@@ -12321,11 +12499,8 @@ erts_do_exit_process(Process* p, Eterm reason)
set_self_exiting(p, reason, NULL, NULL, NULL);
- if (IS_TRACED(p)) {
- if (IS_TRACED_FL(p, F_TRACE_CALLS))
- erts_schedule_time_break(p, ERTS_BP_CALL_TIME_SCHEDULE_EXITING);
-
- }
+ if (IS_TRACED_FL(p, F_TRACE_CALLS))
+ erts_schedule_time_break(p, ERTS_BP_CALL_TIME_SCHEDULE_EXITING);
erts_trace_check_exiting(p->common.id);
@@ -12340,288 +12515,444 @@ erts_do_exit_process(Process* p, Eterm reason)
erts_proc_unlock(p, ERTS_PROC_LOCKS_ALL_MINOR);
- if (IS_TRACED_FL(p,F_TRACE_PROCS))
+ if (IS_TRACED_FL(p, F_TRACE_PROCS))
trace_proc(p, ERTS_PROC_LOCK_MAIN, p, am_exit, reason);
-
/*
* p->u.initial of this process can *not* be used anymore;
* will be overwritten by misc termination data.
*/
p->u.terminate = NULL;
+ BUMP_REDS(p, 100);
+
erts_continue_exit_process(p);
}
-void
-erts_continue_exit_process(Process *p)
-{
+enum continue_exit_phase {
+ ERTS_CONTINUE_EXIT_TIMERS,
+ ERTS_CONTINUE_EXIT_BLCKD_MSHED,
+ ERTS_CONTINUE_EXIT_BLCKD_NMSHED,
+ ERTS_CONTINUE_EXIT_USING_DB,
+ ERTS_CONTINUE_EXIT_CLEAN_SYS_TASKS,
+ ERTS_CONTINUE_EXIT_FREE,
+ ERTS_CONTINUE_EXIT_CLEAN_SYS_TASKS_AFTER,
+ ERTS_CONTINUE_EXIT_LINKS,
+ ERTS_CONTINUE_EXIT_MONITORS,
+ ERTS_CONTINUE_EXIT_LT_MONITORS,
+ ERTS_CONTINUE_EXIT_HANDLE_PROC_SIG,
+ ERTS_CONTINUE_EXIT_DIST_LINKS,
+ ERTS_CONTINUE_EXIT_DIST_MONITORS,
+ ERTS_CONTINUE_EXIT_DONE,
+};
+
+struct continue_exit_state {
+ enum continue_exit_phase phase;
ErtsLink *links;
ErtsMonitor *monitors;
ErtsMonitor *lt_monitors;
+ Eterm reason;
+ ErtsProcExitContext pectxt;
+ DistEntry *dep;
+ void *yield_state;
+};
+
+void
+erts_continue_exit_process(Process *p)
+{
+ struct continue_exit_state static_state, *trap_state = &static_state;
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;
-
+ Sint reds = ERTS_BIF_REDS_LEFT(p);
#ifdef DEBUG
int yield_allowed = 1;
#endif
+ if (p->u.terminate) {
+ trap_state = p->u.terminate;
+ } else {
+ trap_state->phase = ERTS_CONTINUE_EXIT_TIMERS;
+ trap_state->reason = p->fvalue;
+ trap_state->dep = NULL;
+ trap_state->yield_state = NULL;
+ }
+
ERTS_LC_ASSERT(ERTS_PROC_LOCK_MAIN == erts_proc_lc_my_proc_locks(p));
ASSERT(ERTS_PROC_IS_EXITING(p));
ASSERT(erts_proc_read_refc(p) > 0);
- if (p->bif_timers) {
- if (erts_cancel_bif_timers(p, &p->bif_timers, &p->u.terminate)) {
- ASSERT(erts_proc_read_refc(p) > 0);
- goto yield;
- }
- ASSERT(erts_proc_read_refc(p) > 0);
- p->bif_timers = NULL;
- }
-
- if (p->flags & F_SCHDLR_ONLN_WAITQ)
- abort_sched_onln_chng_waitq(p);
-
- if (p->flags & F_HAVE_BLCKD_MSCHED) {
- ErtsSchedSuspendResult ssr;
- ssr = erts_block_multi_scheduling(p, ERTS_PROC_LOCK_MAIN, 0, 0, 1);
- switch (ssr) {
- case ERTS_SCHDLR_SSPND_YIELD_RESTART:
- goto yield;
- case ERTS_SCHDLR_SSPND_DONE_MSCHED_BLOCKED:
- case ERTS_SCHDLR_SSPND_DONE_NMSCHED_BLOCKED:
- case ERTS_SCHDLR_SSPND_YIELD_DONE_MSCHED_BLOCKED:
- case ERTS_SCHDLR_SSPND_YIELD_DONE_NMSCHED_BLOCKED:
- case ERTS_SCHDLR_SSPND_DONE:
- case ERTS_SCHDLR_SSPND_YIELD_DONE:
- p->flags &= ~F_HAVE_BLCKD_MSCHED;
- break;
- case ERTS_SCHDLR_SSPND_EINVAL:
- default:
- erts_exit(ERTS_ABORT_EXIT, "%s:%d: Internal error: %d\n",
- __FILE__, __LINE__, (int) ssr);
- }
- }
- if (p->flags & F_HAVE_BLCKD_NMSCHED) {
- ErtsSchedSuspendResult ssr;
- ssr = erts_block_multi_scheduling(p, ERTS_PROC_LOCK_MAIN, 0, 1, 1);
- switch (ssr) {
- case ERTS_SCHDLR_SSPND_YIELD_RESTART:
- goto yield;
- case ERTS_SCHDLR_SSPND_DONE_MSCHED_BLOCKED:
- case ERTS_SCHDLR_SSPND_DONE_NMSCHED_BLOCKED:
- case ERTS_SCHDLR_SSPND_YIELD_DONE_MSCHED_BLOCKED:
- case ERTS_SCHDLR_SSPND_YIELD_DONE_NMSCHED_BLOCKED:
- case ERTS_SCHDLR_SSPND_DONE:
- case ERTS_SCHDLR_SSPND_YIELD_DONE:
- p->flags &= ~F_HAVE_BLCKD_MSCHED;
- break;
- case ERTS_SCHDLR_SSPND_EINVAL:
- default:
- erts_exit(ERTS_ABORT_EXIT, "%s:%d: Internal error: %d\n",
- __FILE__, __LINE__, (int) ssr);
- }
- }
+restart:
+ switch (trap_state->phase) {
+ case ERTS_CONTINUE_EXIT_TIMERS:
+ if (p->bif_timers) {
+ reds = erts_cancel_bif_timers(p, &p->bif_timers, &trap_state->yield_state, reds);
+ if (reds <= 0) goto yield;
+ p->bif_timers = NULL;
+ }
- if (p->flags & F_USING_DB) {
- if (erts_db_process_exiting(p, ERTS_PROC_LOCK_MAIN))
- goto yield;
- p->flags &= ~F_USING_DB;
- }
+ if (p->flags & F_SCHDLR_ONLN_WAITQ) {
+ abort_sched_onln_chng_waitq(p);
+ reds -= 100;
+ }
- erts_set_gc_state(p, 1);
- state = erts_atomic32_read_acqb(&p->state);
- if ((state & ERTS_PSFLG_SYS_TASKS) || p->dirty_sys_tasks) {
- if (cleanup_sys_tasks(p, state, CONTEXT_REDS) >= CONTEXT_REDS/2)
- goto yield;
- }
+ trap_state->phase = ERTS_CONTINUE_EXIT_BLCKD_MSHED;
+ if (reds <= 0) goto yield;
+ case ERTS_CONTINUE_EXIT_BLCKD_MSHED:
+
+ if (p->flags & F_HAVE_BLCKD_MSCHED) {
+ ErtsSchedSuspendResult ssr;
+ ssr = erts_block_multi_scheduling(p, ERTS_PROC_LOCK_MAIN, 0, 0, 1);
+ switch (ssr) {
+ case ERTS_SCHDLR_SSPND_DONE:
+ case ERTS_SCHDLR_SSPND_DONE_MSCHED_BLOCKED:
+ case ERTS_SCHDLR_SSPND_DONE_NMSCHED_BLOCKED:
+ p->flags &= ~F_HAVE_BLCKD_MSCHED;
+ break;
+ default:
+ erts_exit(ERTS_ABORT_EXIT, "%s:%d: Internal error: %d\n",
+ __FILE__, __LINE__, (int) ssr);
+ }
+ reds -= 100;
+ }
-#ifdef DEBUG
- erts_proc_lock(p, ERTS_PROC_LOCK_STATUS);
- ASSERT(ERTS_PROC_GET_DELAYED_GC_TASK_QS(p) == NULL);
- ASSERT(p->dirty_sys_tasks == NULL);
- erts_proc_unlock(p, ERTS_PROC_LOCK_STATUS);
-#endif
+ trap_state->phase = ERTS_CONTINUE_EXIT_BLCKD_NMSHED;
+ if (reds <= 0) goto yield;
+ case ERTS_CONTINUE_EXIT_BLCKD_NMSHED:
+
+ if (p->flags & F_HAVE_BLCKD_NMSCHED) {
+ ErtsSchedSuspendResult ssr;
+ ssr = erts_block_multi_scheduling(p, ERTS_PROC_LOCK_MAIN, 0, 1, 1);
+ switch (ssr) {
+ case ERTS_SCHDLR_SSPND_DONE:
+ case ERTS_SCHDLR_SSPND_DONE_MSCHED_BLOCKED:
+ case ERTS_SCHDLR_SSPND_DONE_NMSCHED_BLOCKED:
+ p->flags &= ~F_HAVE_BLCKD_MSCHED;
+ break;
+ default:
+ erts_exit(ERTS_ABORT_EXIT, "%s:%d: Internal error: %d\n",
+ __FILE__, __LINE__, (int) ssr);
+ }
+ reds -= 100;
+ }
- if (p->flags & F_USING_DDLL) {
- erts_ddll_proc_dead(p, ERTS_PROC_LOCK_MAIN);
- p->flags &= ~F_USING_DDLL;
- }
+ trap_state->yield_state = NULL;
+ trap_state->phase = ERTS_CONTINUE_EXIT_USING_DB;
+ if (reds <= 0) goto yield;
+ case ERTS_CONTINUE_EXIT_USING_DB:
- /*
- * The registered name *should* be the last "erlang resource" to
- * cleanup.
- */
- if (p->common.u.alive.reg) {
- (void) erts_unregister_name(p, ERTS_PROC_LOCK_MAIN, NULL, THE_NON_VALUE);
- ASSERT(!p->common.u.alive.reg);
- }
+ if (p->flags & F_USING_DB) {
+ if (erts_db_process_exiting(p, ERTS_PROC_LOCK_MAIN, &trap_state->yield_state))
+ goto yield;
+ p->flags &= ~F_USING_DB;
+ }
- if (IS_TRACED_FL(p, F_TRACE_SCHED_EXIT))
- trace_sched(p, curr_locks, am_out_exited);
+ erts_set_gc_state(p, 1);
- erts_proc_lock(p, ERTS_PROC_LOCKS_ALL_MINOR);
- curr_locks = ERTS_PROC_LOCKS_ALL;
+ trap_state->phase = ERTS_CONTINUE_EXIT_CLEAN_SYS_TASKS;
+ case ERTS_CONTINUE_EXIT_CLEAN_SYS_TASKS:
+
+ state = erts_atomic32_read_acqb(&p->state);
+ if ((state & ERTS_PSFLG_SYS_TASKS) || p->dirty_sys_tasks) {
+ reds -= cleanup_sys_tasks(p, state, reds);
+ if (reds <= 0) goto yield;
+ }
+
+ trap_state->phase = ERTS_CONTINUE_EXIT_FREE;
+ case ERTS_CONTINUE_EXIT_FREE:
- /*
- * From this point on we are no longer allowed to yield
- * this process.
- */
#ifdef DEBUG
- yield_allowed = 0;
+ erts_proc_lock(p, ERTS_PROC_LOCK_STATUS);
+ ASSERT(ERTS_PROC_GET_DELAYED_GC_TASK_QS(p) == NULL);
+ ASSERT(p->dirty_sys_tasks == NULL);
+ erts_proc_unlock(p, ERTS_PROC_LOCK_STATUS);
#endif
- /*
- * Note! The monitor and link fields will be overwritten
- * by erts_ptab_delete_element() below.
- */
- links = ERTS_P_LINKS(p);
- monitors = ERTS_P_MONITORS(p);
- lt_monitors = ERTS_P_LT_MONITORS(p);
+ if (p->flags & F_USING_DDLL) {
+ erts_ddll_proc_dead(p, ERTS_PROC_LOCK_MAIN);
+ p->flags &= ~F_USING_DDLL;
+ }
- {
- /* Do *not* use erts_get_runq_proc() */
- ErtsRunQueue *rq;
- rq = erts_get_runq_current(erts_proc_sched_data(p));
+ /*
+ * The registered name *should* be the last "erlang resource" to
+ * cleanup.
+ */
+ if (p->common.u.alive.reg) {
+ (void) erts_unregister_name(p, ERTS_PROC_LOCK_MAIN, NULL, THE_NON_VALUE);
+ ASSERT(!p->common.u.alive.reg);
+ }
- erts_runq_lock(rq);
+ erts_proc_lock(p, ERTS_PROC_LOCKS_ALL_MINOR);
+ curr_locks = ERTS_PROC_LOCKS_ALL;
- ASSERT(p->scheduler_data);
- ASSERT(p->scheduler_data->current_process == p);
- ASSERT(p->scheduler_data->free_process == NULL);
+ /*
+ * Note! The monitor and link fields will be overwritten
+ * by erts_ptab_delete_element() below.
+ */
+ trap_state->links = ERTS_P_LINKS(p);
+ trap_state->monitors = ERTS_P_MONITORS(p);
+ trap_state->lt_monitors = ERTS_P_LT_MONITORS(p);
- p->scheduler_data->current_process = NULL;
- p->scheduler_data->free_process = p;
+ {
+ /* Do *not* use erts_get_runq_proc() */
+ ErtsRunQueue *rq;
+ rq = erts_get_runq_current(erts_proc_sched_data(p));
- /* Time of death! */
- erts_ptab_delete_element(&erts_proc, &p->common);
+ erts_runq_lock(rq);
- erts_runq_unlock(rq);
- }
+ ASSERT(p->scheduler_data);
+ ASSERT(p->scheduler_data->current_process == p);
+ ASSERT(p->scheduler_data->free_process == NULL);
- /*
- * All "erlang resources" have to be deallocated before this point,
- * e.g. registered name, so monitoring and linked processes can
- * be sure that all interesting resources have been deallocated
- * when the monitors and/or links hit.
- */
+ /* Time of death! */
+ erts_ptab_delete_element(&erts_proc, &p->common);
- {
- /* Inactivate and notify free */
- erts_aint32_t n, e, a = erts_atomic32_read_nob(&p->state);
- int refc_inced = 0;
- while (1) {
- n = e = a;
- ASSERT(a & ERTS_PSFLG_EXITING);
- n |= ERTS_PSFLG_FREE;
- 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;
- }
- a = erts_atomic32_cmpxchg_mb(&p->state, n, e);
- if (a == e)
- break;
- }
+ erts_runq_unlock(rq);
+ }
- if (a & (ERTS_PSFLG_DIRTY_RUNNING
- | ERTS_PSFLG_DIRTY_RUNNING_SYS)) {
- p->flags |= F_DELAYED_DEL_PROC;
- delay_del_proc = 1;
- /*
- * The dirty scheduler decrease refc
- * when done with the process...
- */
- }
+ /*
+ * All "erlang resources" have to be deallocated before this point,
+ * e.g. registered name, so monitoring and linked processes can
+ * be sure that all interesting resources have been deallocated
+ * when the monitors and/or links hit.
+ */
- if (refc_inced && !(n & ERTS_PSFLG_IN_RUNQ))
- erts_proc_dec_refc(p);
- }
+ {
+ /* Inactivate and notify free */
+ erts_aint32_t n, e, a = erts_atomic32_read_nob(&p->state);
+ int refc_inced = 0;
+ while (1) {
+ n = e = a;
+ ASSERT(a & ERTS_PSFLG_EXITING);
+ n |= ERTS_PSFLG_FREE;
+ if ((n & ERTS_PSFLG_IN_RUNQ) && !refc_inced) {
+ erts_proc_inc_refc(p);
+ refc_inced = 1;
+ }
+ a = erts_atomic32_cmpxchg_mb(&p->state, n, e);
+ if (a == e)
+ break;
+ }
- dep = ((p->flags & F_DISTRIBUTION)
- ? ERTS_PROC_SET_DIST_ENTRY(p, NULL)
- : NULL);
+ if (refc_inced && !(n & ERTS_PSFLG_IN_RUNQ))
+ erts_proc_dec_refc(p);
+ }
+ trap_state->dep = ((p->flags & F_DISTRIBUTION)
+ ? ERTS_PROC_SET_DIST_ENTRY(p, NULL)
+ : NULL);
+
+ reds -= 50;
- /*
- * 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);
+ curr_locks = ERTS_PROC_LOCK_MAIN;
+ trap_state->phase = ERTS_CONTINUE_EXIT_CLEAN_SYS_TASKS_AFTER;
+ case ERTS_CONTINUE_EXIT_CLEAN_SYS_TASKS_AFTER:
+ /*
+ * It might show up signal prio elevation tasks until we
+ * have entered free state. Cleanup such tasks now.
+ */
- 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);
- }
+ state = erts_atomic32_read_acqb(&p->state);
+ if ((state & ERTS_PSFLG_SYS_TASKS) || p->dirty_sys_tasks) {
+ reds -= cleanup_sys_tasks(p, state, reds);
+ if (reds <= 0) goto yield;
+ }
+
+ /* Needs to be unlocked for erts_do_net_exits to work?!? */
+ // 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);
+ 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);
- }
+ if (trap_state->dep) {
+ erts_do_net_exits(trap_state->dep,
+ (trap_state->reason == am_kill) ? am_killed : trap_state->reason);
+ erts_deref_dist_entry(trap_state->dep);
+ }
- pectxt.c_p = p;
- pectxt.reason = reason;
+ trap_state->pectxt.c_p = p;
+ trap_state->pectxt.reason = trap_state->reason;
+ trap_state->pectxt.dist_links = NULL;
+ trap_state->pectxt.dist_monitors = NULL;
+ trap_state->pectxt.dist_state = NIL;
+
+ erts_proc_lock(p, ERTS_PROC_LOCK_MSGQ);
+
+ erts_proc_sig_fetch(p);
+
+ erts_proc_unlock(p, ERTS_PROC_LOCK_MSGQ);
+
+ trap_state->yield_state = NULL;
+ trap_state->phase = ERTS_CONTINUE_EXIT_LINKS;
+ if (reds <= 0) goto yield;
+ case ERTS_CONTINUE_EXIT_LINKS:
+
+ reds = erts_link_tree_foreach_delete_yielding(
+ &trap_state->links,
+ erts_proc_exit_handle_link,
+ (void *) &trap_state->pectxt,
+ &trap_state->yield_state,
+ reds);
+ if (reds <= 0)
+ goto yield;
+
+ ASSERT(!trap_state->links);
+ trap_state->yield_state = NULL;
+ trap_state->phase = ERTS_CONTINUE_EXIT_MONITORS;
+ case ERTS_CONTINUE_EXIT_MONITORS:
+
+ reds = erts_monitor_tree_foreach_delete_yielding(
+ &trap_state->monitors,
+ erts_proc_exit_handle_monitor,
+ (void *) &trap_state->pectxt,
+ &trap_state->yield_state,
+ reds);
+ if (reds <= 0)
+ goto yield;
+
+ ASSERT(!trap_state->monitors);
+ trap_state->yield_state = NULL;
+ trap_state->phase = ERTS_CONTINUE_EXIT_LT_MONITORS;
+ case ERTS_CONTINUE_EXIT_LT_MONITORS:
+
+ reds = erts_monitor_list_foreach_delete_yielding(
+ &trap_state->lt_monitors,
+ erts_proc_exit_handle_monitor,
+ (void *) &trap_state->pectxt,
+ &trap_state->yield_state,
+ reds);
+ if (reds <= 0)
+ goto yield;
+
+ ASSERT(!trap_state->lt_monitors);
+ trap_state->phase = ERTS_CONTINUE_EXIT_HANDLE_PROC_SIG;
+ case ERTS_CONTINUE_EXIT_HANDLE_PROC_SIG: {
+ Sint r = reds;
+
+ if (!erts_proc_sig_handle_exit(p, &r))
+ goto yield;
+
+ reds -= r;
+
+ trap_state->phase = ERTS_CONTINUE_EXIT_DIST_LINKS;
+ }
+ case ERTS_CONTINUE_EXIT_DIST_LINKS: {
+
+ continue_dist_send:
+ if (is_not_nil(trap_state->pectxt.dist_state)) {
+ Binary* bin = erts_magic_ref2bin(trap_state->pectxt.dist_state);
+ ErtsDSigSendContext* ctx = (ErtsDSigSendContext*) ERTS_MAGIC_BIN_DATA(bin);
+ Sint initial_reds = (Sint) (ERTS_BIF_REDS_LEFT(p) * TERM_TO_BINARY_LOOP_FACTOR);
+ int result;
+
+ ctx->reds = initial_reds;
+ result = erts_dsig_send(ctx);
+
+ /* erts_dsig_send bumps reductions on the process in the ctx */
+ reds = ERTS_BIF_REDS_LEFT(p);
+
+ switch (result) {
+ case ERTS_DSIG_SEND_OK:
+ case ERTS_DSIG_SEND_TOO_LRG: /*SEND_SYSTEM_LIMIT*/
+ case ERTS_DSIG_SEND_YIELD: /*SEND_YIELD_RETURN*/
+ break;
+ case ERTS_DSIG_SEND_CONTINUE: { /*SEND_YIELD_CONTINUE*/
+ goto yield;
+ }
+ }
+ erts_set_gc_state(p, 1);
+ trap_state->pectxt.dist_state = NIL;
+ if (reds <= 0)
+ goto yield;
+ goto restart;
+ }
- erts_proc_lock(p, ERTS_PROC_LOCK_MAIN|ERTS_PROC_LOCK_MSGQ);
+ reds = erts_link_tree_foreach_delete_yielding(
+ &trap_state->pectxt.dist_links,
+ erts_proc_exit_handle_dist_link,
+ (void *) &trap_state->pectxt,
+ &trap_state->yield_state,
+ reds);
+ if (reds <= 0 || is_not_nil(trap_state->pectxt.dist_state))
+ goto yield;
+ trap_state->phase = ERTS_CONTINUE_EXIT_DIST_MONITORS;
+ }
+ case ERTS_CONTINUE_EXIT_DIST_MONITORS: {
+
+ if (is_not_nil(trap_state->pectxt.dist_state))
+ goto continue_dist_send;
+
+ reds = erts_monitor_tree_foreach_delete_yielding(
+ &trap_state->pectxt.dist_monitors,
+ erts_proc_exit_handle_dist_monitor,
+ (void *) &trap_state->pectxt,
+ &trap_state->yield_state,
+ reds);
+ if (reds <= 0 || is_not_nil(trap_state->pectxt.dist_state))
+ goto yield;
+
+ trap_state->phase = ERTS_CONTINUE_EXIT_DONE;
+ }
+ case ERTS_CONTINUE_EXIT_DONE: {
+ erts_aint_t state;
+ /*
+ * From this point on we are no longer allowed to yield
+ * this process.
+ */
- erts_proc_sig_fetch(p);
+#ifdef DEBUG
+ yield_allowed = 0;
+#endif
- erts_proc_unlock(p, ERTS_PROC_LOCK_MSGQ);
+ /* Set state to not active as we don't want this process
+ to be scheduled in again after this. */
+ state = erts_atomic32_read_band_relb(&p->state,
+ ~(ERTS_PSFLG_ACTIVE
+ | ERTS_PSFLG_ACTIVE_SYS
+ | ERTS_PSFLG_DIRTY_ACTIVE_SYS));
- if (links) {
- erts_link_tree_foreach_delete(&links,
- erts_proc_exit_handle_link,
- (void *) &pectxt);
- ASSERT(!links);
- }
+ ASSERT(p->scheduler_data);
+ ASSERT(p->scheduler_data->current_process == p);
+ ASSERT(p->scheduler_data->free_process == NULL);
- if (monitors) {
- erts_monitor_tree_foreach_delete(&monitors,
- erts_proc_exit_handle_monitor,
- (void *) &pectxt);
- ASSERT(!monitors);
- }
+ p->scheduler_data->current_process = NULL;
+ p->scheduler_data->free_process = p;
- if (lt_monitors) {
- erts_monitor_list_foreach_delete(&lt_monitors,
- erts_proc_exit_handle_monitor,
- (void *) &pectxt);
- ASSERT(!lt_monitors);
- }
+ if (state & (ERTS_PSFLG_DIRTY_RUNNING
+ | ERTS_PSFLG_DIRTY_RUNNING_SYS)) {
+ p->flags |= F_DELAYED_DEL_PROC;
+ delay_del_proc = 1;
+ /*
+ * The dirty scheduler decrease refc
+ * when done with the process...
+ */
+ }
- /*
- * 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_schedule_thr_prgr_later_cleanup_op(
+ (void (*)(void*))erts_proc_dec_refc,
+ (void *) &p->common,
+ &p->common.u.release,
+ sizeof(Process));
+
+ break;
+ }
}
+ if (trap_state != &static_state) {
+ erts_free(ERTS_ALC_T_CONT_EXIT_TRAP, trap_state);
+ p->u.terminate = NULL;
+ }
+
ERTS_CHK_HAVE_ONLY_MAIN_PROC_LOCK(p);
+ if (IS_TRACED_FL(p, F_TRACE_SCHED_EXIT))
+ trace_sched(p, curr_locks, am_out_exited);
+
erts_flush_trace_messages(p, ERTS_PROC_LOCK_MAIN);
ERTS_TRACER_CLEAR(&ERTS_TRACER(p));
@@ -12637,9 +12968,26 @@ erts_continue_exit_process(Process *p)
ERTS_LC_ASSERT(curr_locks == erts_proc_lc_my_proc_locks(p));
ERTS_LC_ASSERT(ERTS_PROC_LOCK_MAIN & curr_locks);
+ ASSERT(erts_proc_read_refc(p) > 0);
+
+ if (trap_state == &static_state) {
+ trap_state = erts_alloc(ERTS_ALC_T_CONT_EXIT_TRAP, sizeof(*trap_state));
+ sys_memcpy(trap_state, &static_state, sizeof(*trap_state));
+ p->u.terminate = trap_state;
+ }
+
+ ASSERT(p->scheduler_data);
+ ASSERT(p->scheduler_data->current_process == p);
+ ASSERT(p->scheduler_data->free_process == NULL);
+
+ if (trap_state->phase >= ERTS_CONTINUE_EXIT_FREE) {
+ p->scheduler_data->current_process = NULL;
+ p->scheduler_data->free_process = p;
+ }
p->i = (BeamInstr *) beam_continue_exit;
+ /* Why is this lock take??? */
if (!(curr_locks & ERTS_PROC_LOCK_STATUS)) {
erts_proc_lock(p, ERTS_PROC_LOCK_STATUS);
curr_locks |= ERTS_PROC_LOCK_STATUS;
diff --git a/erts/emulator/beam/erl_process.h b/erts/emulator/beam/erl_process.h
index 43937f216c..3b593bce02 100644
--- a/erts/emulator/beam/erl_process.h
+++ b/erts/emulator/beam/erl_process.h
@@ -381,7 +381,10 @@ struct ErtsSchedulerSleepInfo_ {
typedef struct ErtsProcList_ ErtsProcList;
struct ErtsProcList_ {
- Eterm pid;
+ union {
+ Eterm pid;
+ Process *p;
+ } u;
Uint64 started_interval;
ErtsProcList* next;
ErtsProcList* prev;
@@ -1580,7 +1583,7 @@ ERTS_GLB_INLINE int erts_proclist_is_last(ErtsProcList *, ErtsProcList *);
ERTS_GLB_INLINE int
erts_proclist_same(ErtsProcList *plp, Process *p)
{
- return (plp->pid == p->common.id
+ return ((plp->u.pid == p->common.id || plp->u.p == p)
&& (plp->started_interval
== p->common.u.alive.started_interval));
}
@@ -1819,9 +1822,12 @@ Eterm erts_process_info(Process *c_p, ErtsHeapFactory *hfact,
typedef struct {
Process *c_p;
Eterm reason;
+ ErtsLink *dist_links;
+ ErtsMonitor *dist_monitors;
+ Eterm dist_state;
} ErtsProcExitContext;
-void erts_proc_exit_handle_monitor(ErtsMonitor *mon, void *vctxt);
-void erts_proc_exit_handle_link(ErtsLink *lnk, void *vctxt);
+int erts_proc_exit_handle_monitor(ErtsMonitor *mon, void *vctxt, Sint reds);
+int erts_proc_exit_handle_link(ErtsLink *lnk, void *vctxt, Sint reds);
Eterm erts_get_process_priority(erts_aint32_t state);
Eterm erts_set_process_priority(Process *p, Eterm prio);
diff --git a/erts/emulator/beam/erl_process_dump.c b/erts/emulator/beam/erl_process_dump.c
index 10ea401022..a164ed543e 100644
--- a/erts/emulator/beam/erl_process_dump.c
+++ b/erts/emulator/beam/erl_process_dump.c
@@ -100,16 +100,18 @@ 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)
+static int
+monitor_size(ErtsMonitor *mon, void *vsize, Sint reds)
{
*((Uint *) vsize) += erts_monitor_size(mon);
+ return 1;
}
-static void
-link_size(ErtsMonitor *lnk, void *vsize)
+static int
+link_size(ErtsMonitor *lnk, void *vsize, Sint reds)
{
*((Uint *) vsize) += erts_link_size(lnk);
+ return 1;
}
Uint erts_process_memory(Process *p, int include_sigs_in_transit)
@@ -189,11 +191,11 @@ 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);
+ Eterm mesg;
+ if (ERTS_SIG_IS_INTERNAL_MSG(mp))
+ dump_element(to, to_arg, ERL_MESSAGE_TERM(mp));
else
- dump_dist_ext(to, to_arg, mp->data.dist_ext);
+ dump_dist_ext(to, to_arg, erts_get_dist_ext(mp->data.heap_frag));
mesg = ERL_MESSAGE_TOKEN(mp);
erts_print(to, to_arg, ":");
dump_element(to, to_arg, mesg);
@@ -265,6 +267,7 @@ dump_dist_ext(fmtfn_t to, void *to_arg, ErtsDistExternal *edep)
else {
byte *e;
size_t sz;
+ int i;
if (!(edep->flags & ERTS_DIST_EXT_ATOM_TRANS_TAB))
erts_print(to, to_arg, "D0:");
@@ -274,8 +277,8 @@ dump_dist_ext(fmtfn_t to, void *to_arg, ErtsDistExternal *edep)
for (i = 0; i < edep->attab.size; i++)
dump_element(to, to_arg, edep->attab.atom[i]);
}
- sz = edep->ext_endp - edep->extp;
- e = edep->extp;
+ sz = edep->data->ext_endp - edep->data->extp;
+ e = edep->data->extp;
if (edep->flags & ERTS_DIST_EXT_DFLAG_HDR) {
ASSERT(*e != VERSION_MAGIC);
sz++;
@@ -286,15 +289,19 @@ dump_dist_ext(fmtfn_t to, void *to_arg, ErtsDistExternal *edep)
erts_print(to, to_arg, "E%X:", sz);
if (edep->flags & ERTS_DIST_EXT_DFLAG_HDR) {
byte sbuf[3];
- int i = 0;
+
+ i = 0;
sbuf[i++] = VERSION_MAGIC;
- while (i < sizeof(sbuf) && e < edep->ext_endp) {
+ while (i < sizeof(sbuf) && e < edep->data->ext_endp) {
sbuf[i++] = *e++;
}
erts_print_base64(to, to_arg, sbuf, i);
}
- erts_print_base64(to, to_arg, e, edep->ext_endp - e);
+ erts_print_base64(to, to_arg, e, edep->data->ext_endp - e);
+ for (i = 1; i < edep->data->frag_id; i++)
+ erts_print_base64(to, to_arg, edep->data[i].extp,
+ edep->data[i].ext_endp - edep->data[i].extp);
}
}
diff --git a/erts/emulator/beam/erl_ptab.h b/erts/emulator/beam/erl_ptab.h
index 94f0247492..c30a684002 100644
--- a/erts/emulator/beam/erl_ptab.h
+++ b/erts/emulator/beam/erl_ptab.h
@@ -68,8 +68,11 @@ typedef struct {
Uint64 started_interval;
struct reg_proc *reg;
ErtsLink *links;
- ErtsMonitor *monitors;
+ /* Local target monitors, double linked list
+ contains the remote part of local monitors */
ErtsMonitor *lt_monitors;
+ /* other monitors, rb tree */
+ ErtsMonitor *monitors;
} alive;
/* --- While being released --- */
diff --git a/erts/emulator/beam/erl_rbtree.h b/erts/emulator/beam/erl_rbtree.h
index e50abf5cec..ce401fa7e7 100644
--- a/erts/emulator/beam/erl_rbtree.h
+++ b/erts/emulator/beam/erl_rbtree.h
@@ -161,7 +161,7 @@
*
* - void <ERTS_RBT_PREFIX>_rbt_foreach(
* ERTS_RBT_T *tree,
- * void (*op)(ERTS_RBT_T *, void *),
+ * int (*op)(ERTS_RBT_T *, void *),
* void *arg);
* Operate by calling the operator 'op' on each element.
* Order is undefined.
@@ -170,7 +170,7 @@
*
* - void <ERTS_RBT_PREFIX>_rbt_foreach_destroy(
* ERTS_RBT_T *tree,
- * void (*op)(ERTS_RBT_T *, void *),
+ * int (*op)(ERTS_RBT_T *, void *),
* void *arg);
* Operate by calling the operator 'op' on each element.
* Order is undefined. Each element should be destroyed
@@ -180,39 +180,46 @@
*
* - int <ERTS_RBT_PREFIX>_rbt_foreach_yielding(
* ERTS_RBT_T *tree,
- * void (*op)(ERTS_RBT_T *, void *),
+ * int (*op)(ERTS_RBT_T *, void *),
* void *arg,
* <ERTS_RBT_PREFIX>_rbt_yield_state_t *ystate,
- * Sint ylimit);
+ * Sint reds);
* Operate by calling the operator 'op' on each element.
* Order is undefined.
*
- * Yield when 'ylimit' elements has been processed. True is
- * returned when yielding, and false is returned when
- * the whole tree has been processed. The tree should not be
- * modified until all of it has been processed.
+ * Yield when 'reds' reductions has been processed. The 'op'
+ * function return the number of reductions that each element
+ * took to process. The number of reductions remaining is returned,
+ * meaning that if 0 is returned, there are more elements to be
+ * processed. If a value greater than 0 is returned the foreach has
+ * ended. The tree should not be modified until all of it has been
+ * processed.
*
* 'arg' is passed as argument to 'op'.
*
* - int <ERTS_RBT_PREFIX>_rbt_foreach_destroy_yielding(
* ERTS_RBT_T *tree,
- * void (*op)(ERTS_RBT_T *, void *),
+ * int (*op)(ERTS_RBT_T *, void *),
* void *arg,
* <ERTS_RBT_PREFIX>_rbt_yield_state_t *ystate,
- * Sint ylimit);
+ * Sint reds);
* Operate by calling the operator 'op' on each element.
* Order is undefined. Each element should be destroyed
* by 'op'.
*
- * Yield when 'ylimit' elements has been processed. True is
- * returned when yielding, and false is returned when
- * the whole tree has been processed.
+ * Yield when 'reds' reductions has been processed. The 'op'
+ * function return the number of reductions that each element
+ * took to process. The number of reductions remaining is returned,
+ * meaning that if 0 is returned, there are more elements to be
+ * processed. If a value greater than 0 is returned the foreach has
+ * ended. The tree should not be modified until all of it has been
+ * processed.
*
* 'arg' is passed as argument to 'op'.
*
* - void <ERTS_RBT_PREFIX>_rbt_foreach_small(
* ERTS_RBT_T *tree,
- * void (*op)(ERTS_RBT_T *, void *),
+ * int (*op)(ERTS_RBT_T *, void *),
* void *arg);
* Operate by calling the operator 'op' on each element from
* smallest towards larger elements.
@@ -221,7 +228,7 @@
*
* - void <ERTS_RBT_PREFIX>_rbt_foreach_large(
* ERTS_RBT_T *tree,
- * void (*op)(ERTS_RBT_T *, void *),
+ * int (*op)(ERTS_RBT_T *, void *),
* void *arg);
* Operate by calling the operator 'op' on each element from
* largest towards smaller elements.
@@ -230,40 +237,46 @@
*
* - int <ERTS_RBT_PREFIX>_rbt_foreach_small_yielding(
* ERTS_RBT_T *tree,
- * void (*op)(ERTS_RBT_T *, void *),
+ * int (*op)(ERTS_RBT_T *, void *),
* void *arg,
* <ERTS_RBT_PREFIX>_rbt_yield_state_t *ystate,
- * Sint ylimit);
+ * Sint reds);
* Operate by calling the operator 'op' on each element from
* smallest towards larger elements.
*
- * Yield when 'ylimit' elements has been processed. True is
- * returned when yielding, and false is returned when
- * the whole tree has been processed. The tree should not be
- * modified until all of it has been processed.
+ * Yield when 'reds' reductions has been processed. The 'op'
+ * function return the number of reductions that each element
+ * took to process. The number of reductions remaining is returned,
+ * meaning that if 0 is returned, there are more elements to be
+ * processed. If a value greater than 0 is returned the foreach has
+ * ended. The tree should not be modified until all of it has been
+ * processed.
*
* 'arg' is passed as argument to 'op'.
*
* - int <ERTS_RBT_PREFIX>_rbt_foreach_large_yielding(
* ERTS_RBT_T *tree,
- * void (*op)(ERTS_RBT_T *, void *),
+ * int (*op)(ERTS_RBT_T *, void *),
* void *arg,
* <ERTS_RBT_PREFIX>_rbt_yield_state_t *ystate,
- * Sint ylimit);
+ * Sint reds);
* Operate by calling the operator 'op' on each element from
* largest towards smaller elements.
*
- * Yield when 'ylimit' elements has been processed. True is
- * returned when yielding, and false is returned when
- * the whole tree has been processed. The tree should not be
- * modified until all of it has been processed.
+ * Yield when 'reds' reductions has been processed. The 'op'
+ * function return the number of reductions that each element
+ * took to process. The number of reductions remaining is returned,
+ * meaning that if 0 is returned, there are more elements to be
+ * processed. If a value greater than 0 is returned the foreach has
+ * ended. The tree should not be modified until all of it has been
+ * processed.
*
* 'arg' is passed as argument to 'op'.
*
* - void <ERTS_RBT_PREFIX>_rbt_foreach_small_destroy(
* ERTS_RBT_T **tree,
- * void (*op)(ERTS_RBT_T *, void *),
- * void (*destr)(ERTS_RBT_T *, void *),
+ * int (*op)(ERTS_RBT_T *, void *),
+ * int (*destr)(ERTS_RBT_T *, void *),
* void *arg);
* Operate by calling the operator 'op' on each element from
* smallest towards larger elements.
@@ -277,8 +290,8 @@
*
* - void <ERTS_RBT_PREFIX>_rbt_foreach_large_destroy(
* ERTS_RBT_T **tree,
- * void (*op)(ERTS_RBT_T *, void *),
- * void (*destr)(ERTS_RBT_T *, void *),
+ * int (*op)(ERTS_RBT_T *, void *),
+ * int (*destr)(ERTS_RBT_T *, void *),
* void *arg);
* Operate by calling the operator 'op' on each element from
* largest towards smaller elements.
@@ -292,11 +305,11 @@
*
* - int <ERTS_RBT_PREFIX>_rbt_foreach_small_destroy_yielding(
* ERTS_RBT_T **tree,
- * void (*op)(ERTS_RBT_T *, void *),
- * void (*destr)(ERTS_RBT_T *, void *),
+ * int (*op)(ERTS_RBT_T *, void *),
+ * int (*destr)(ERTS_RBT_T *, void *),
* void *arg,
* <ERTS_RBT_PREFIX>_rbt_yield_state_t *ystate,
- * Sint ylimit);
+ * Sint reds);
* Operate by calling the operator 'op' on each element from
* smallest towards larger elements.
*
@@ -305,20 +318,23 @@
* Note that elements are often destroyed in another order
* than the order that the elements are operated on.
*
- * Yield when 'ylimit' elements has been processed. True is
- * returned when yielding, and false is returned when
- * the whole tree has been processed. The tree should not be
- * modified until all of it has been processed.
+ * Yield when 'reds' reductions has been processed. The 'op' and
+ * 'destr' functions return the number of reductions that each element
+ * took to process. The number of reductions remaining is returned,
+ * meaning that if 0 is returned, there are more elements to be
+ * processed. If a value greater than 0 is returned the foreach has
+ * ended. The tree should not be modified until all of it has been
+ * processed.
*
* 'arg' is passed as argument to 'op' and 'destroy'.
*
* - int <ERTS_RBT_PREFIX>_rbt_foreach_large_destroy_yielding(
* ERTS_RBT_T **tree,
- * void (*op)(ERTS_RBT_T *, void *),
- * void (*destr)(ERTS_RBT_T *, void *),
+ * int (*op)(ERTS_RBT_T *, void *),
+ * int (*destr)(ERTS_RBT_T *, void *),
* void *arg,
* <ERTS_RBT_PREFIX>_rbt_yield_state_t *ystate,
- * Sint ylimit);
+ * Sint reds);
* Operate by calling the operator 'op' on each element from
* largest towards smaller elements.
*
@@ -327,10 +343,13 @@
* Note that elements are often destroyed in another order
* than the order that the elements are operated on.
*
- * Yield when 'ylimit' elements has been processed. True is
- * returned when yielding, and false is returned when
- * the whole tree has been processed. The tree should not be
- * modified until all of it has been processed.
+ * Yield when 'reds' reductions has been processed. The 'op' and
+ * 'destr' functions return the number of reductions that each element
+ * took to process. The number of reductions remaining is returned,
+ * meaning that if 0 is returned, there are more elements to be
+ * processed. If a value greater than 0 is returned the foreach has
+ * ended. The tree should not be modified until all of it has been
+ * processed.
*
* 'arg' is passed as argument to 'op' and 'destroy'.
*
@@ -447,17 +466,6 @@
# define ERTS_RBT_API_INLINE__ ERTS_INLINE
#endif
-#ifndef ERTS_RBT_YIELD_STAT_INITER
-# define ERTS_RBT_YIELD_STAT_INITER {NULL, 0}
-#endif
-#ifndef ERTS_RBT_YIELD_STAT_INIT
-# define ERTS_RBT_YIELD_STAT_INIT(YS) \
- do { \
- (YS)->x = NULL; \
- (YS)->up = 0; \
- } while (0)
-#endif
-
#define ERTS_RBT_CONCAT_MACRO_VALUES___(X, Y) \
X ## Y
#define ERTS_RBT_CONCAT_MACRO_VALUES__(X, Y) \
@@ -470,8 +478,38 @@
typedef struct {
ERTS_RBT_T *x;
int up;
+#ifdef DEBUG
+ int debug_red_adj;
+#endif
} ERTS_RBT_YIELD_STATE_T__;
+#define ERTS_RBT_CALLBACK_FOREACH_FUNC(NAME) int (*NAME)(ERTS_RBT_T *, void *, Sint)
+
+#ifndef ERTS_RBT_YIELD_STAT_INITER
+# ifdef DEBUG
+# define ERTS_RBT_YIELD_STAT_INITER {NULL, 0, CONTEXT_REDS}
+# else
+# define ERTS_RBT_YIELD_STAT_INITER {NULL, 0}
+# endif
+#endif
+#ifndef ERTS_RBT_YIELD_STAT_INIT
+# define ERTS_RBT_YIELD_STAT_INIT__(YS) \
+ do { \
+ (YS)->x = NULL; \
+ (YS)->up = 0; \
+ } while (0)
+# ifdef DEBUG
+# define ERTS_RBT_YIELD_STAT_INIT(YS) \
+ do { \
+ ERTS_RBT_YIELD_STAT_INIT__(YS); \
+ (YS)->debug_red_adj = CONTEXT_REDS; \
+ } while(0)
+# else
+# define ERTS_RBT_YIELD_STAT_INIT(YS) ERTS_RBT_YIELD_STAT_INIT__(YS)
+# endif
+#endif
+
+
#define ERTS_RBT_FUNC__(Name) \
ERTS_RBT_CONCAT_MACRO_VALUES__(ERTS_RBT_PREFIX, _rbt_ ## Name)
@@ -1302,11 +1340,11 @@ ERTS_RBT_FUNC__(largest)(ERTS_RBT_T *root)
static ERTS_INLINE int
ERTS_RBT_FUNC__(foreach_unordered__)(ERTS_RBT_T **root,
int destroying,
- void (*op)(ERTS_RBT_T *, void *),
+ ERTS_RBT_CALLBACK_FOREACH_FUNC(op),
void *arg,
- int yielding,
+ int yielding,
ERTS_RBT_YIELD_STATE_T__ *ystate,
- Sint ylimit)
+ Sint reds)
{
ERTS_RBT_T *c, *p, *x;
@@ -1314,13 +1352,17 @@ ERTS_RBT_FUNC__(foreach_unordered__)(ERTS_RBT_T **root,
if (yielding && ystate->x) {
x = ystate->x;
+#ifdef DEBUG
+ if (ystate->debug_red_adj > 0)
+ ystate->debug_red_adj -= 100;
+#endif
ERTS_RBT_ASSERT(ystate->up);
goto restart_up;
}
else {
x = *root;
if (!x)
- return 0;
+ return reds;
if (destroying)
*root = NULL;
}
@@ -1346,10 +1388,10 @@ ERTS_RBT_FUNC__(foreach_unordered__)(ERTS_RBT_T **root,
#ifdef ERTS_RBT_DEBUG
int cdir;
#endif
- if (yielding && ylimit-- <= 0) {
+ if (yielding && reds <= 0) {
ystate->x = x;
ystate->up = 1;
- return 1;
+ return 0;
}
restart_up:
@@ -1375,14 +1417,20 @@ ERTS_RBT_FUNC__(foreach_unordered__)(ERTS_RBT_T **root,
}
#endif
- (*op)(x, arg);
+ reds -= (*op)(x, arg, reds);
+#ifdef DEBUG
+ if (yielding)
+ reds -= ystate->debug_red_adj;
+#endif
if (!p) {
+ /* Done */
if (yielding) {
ystate->x = NULL;
ystate->up = 0;
+ return reds <= 0 ? 1 : reds;
}
- return 0; /* Done */
+ return 1;
}
c = ERTS_RBT_GET_RIGHT(p);
@@ -1407,20 +1455,26 @@ static ERTS_INLINE int
ERTS_RBT_FUNC__(foreach_ordered__)(ERTS_RBT_T **root,
int from_small,
int destroying,
- void (*op)(ERTS_RBT_T *, void *),
- void (*destroy)(ERTS_RBT_T *, void *),
+ ERTS_RBT_CALLBACK_FOREACH_FUNC(op),
+ ERTS_RBT_CALLBACK_FOREACH_FUNC(destroy),
void *arg,
- int yielding,
+ int yielding,
ERTS_RBT_YIELD_STATE_T__ *ystate,
- Sint ylimit)
+ Sint reds)
{
ERTS_RBT_T *c, *p, *x;
ERTS_RBT_ASSERT(!yielding || ystate);
ERTS_RBT_ASSERT(!destroying || destroy);
+ ERTS_RBT_ASSERT(!yielding || yop);
+ ERTS_RBT_ASSERT(yielding || op);
if (yielding && ystate->x) {
x = ystate->x;
+#ifdef DEBUG
+ if (ystate->debug_red_adj > 0)
+ ystate->debug_red_adj -= 100;
+#endif
if (ystate->up)
goto restart_up;
else
@@ -1429,7 +1483,7 @@ ERTS_RBT_FUNC__(foreach_ordered__)(ERTS_RBT_T **root,
else {
x = *root;
if (!x)
- return 0;
+ return reds;
if (destroying)
*root = NULL;
}
@@ -1445,12 +1499,16 @@ ERTS_RBT_FUNC__(foreach_ordered__)(ERTS_RBT_T **root,
x = c;
}
- (*op)(x, arg);
+ reds -= (*op)(x, arg, reds);
+#ifdef DEBUG
+ if (yielding)
+ reds -= ystate->debug_red_adj;
+#endif
- if (yielding && --ylimit <= 0) {
+ if (yielding && reds <= 0) {
ystate->x = x;
ystate->up = 0;
- return 1;
+ return 0;
}
restart_down:
@@ -1472,12 +1530,16 @@ ERTS_RBT_FUNC__(foreach_ordered__)(ERTS_RBT_T **root,
? ERTS_RBT_GET_LEFT(p)
: ERTS_RBT_GET_RIGHT(p)) == x);
- (*op)(p, arg);
+ reds -= (*op)(p, arg, reds);
+#ifdef DEBUG
+ if (yielding)
+ reds -= ystate->debug_red_adj;
+#endif
- if (yielding && --ylimit <= 0) {
+ if (yielding && reds <= 0) {
ystate->x = x;
ystate->up = 1;
- return 1;
+ return 0;
restart_up:
p = ERTS_RBT_GET_PARENT(x);
}
@@ -1510,15 +1572,20 @@ ERTS_RBT_FUNC__(foreach_ordered__)(ERTS_RBT_T **root,
}
#endif
- (*destroy)(x, arg);
+ reds -= (*destroy)(x, arg, reds);
+#ifdef DEBUG
+ if (yielding)
+ reds -= ystate->debug_red_adj;
+#endif
}
if (!p) {
if (yielding) {
ystate->x = NULL;
ystate->up = 0;
+ return reds <= 0 ? 1 : reds;
}
- return 0; /* Done */
+ return 1; /* Done */
}
x = p;
}
@@ -1531,7 +1598,7 @@ ERTS_RBT_FUNC__(foreach_ordered__)(ERTS_RBT_T **root,
static ERTS_RBT_API_INLINE__ void
ERTS_RBT_FUNC__(foreach)(ERTS_RBT_T *root,
- void (*op)(ERTS_RBT_T *, void *),
+ ERTS_RBT_CALLBACK_FOREACH_FUNC(op),
void *arg)
{
(void) ERTS_RBT_FUNC__(foreach_unordered__)(&root, 0, op, arg,
@@ -1544,7 +1611,7 @@ ERTS_RBT_FUNC__(foreach)(ERTS_RBT_T *root,
static ERTS_RBT_API_INLINE__ void
ERTS_RBT_FUNC__(foreach_small)(ERTS_RBT_T *root,
- void (*op)(ERTS_RBT_T *, void *),
+ ERTS_RBT_CALLBACK_FOREACH_FUNC(op),
void *arg)
{
(void) ERTS_RBT_FUNC__(foreach_ordered__)(&root, 1, 0,
@@ -1558,7 +1625,7 @@ ERTS_RBT_FUNC__(foreach_small)(ERTS_RBT_T *root,
static ERTS_RBT_API_INLINE__ void
ERTS_RBT_FUNC__(foreach_large)(ERTS_RBT_T *root,
- void (*op)(ERTS_RBT_T *, void *),
+ ERTS_RBT_CALLBACK_FOREACH_FUNC(op),
void *arg)
{
(void) ERTS_RBT_FUNC__(foreach_ordered__)(&root, 0, 0,
@@ -1572,13 +1639,13 @@ ERTS_RBT_FUNC__(foreach_large)(ERTS_RBT_T *root,
static ERTS_RBT_API_INLINE__ int
ERTS_RBT_FUNC__(foreach_yielding)(ERTS_RBT_T *root,
- void (*op)(ERTS_RBT_T *, void *),
+ ERTS_RBT_CALLBACK_FOREACH_FUNC(op),
void *arg,
ERTS_RBT_YIELD_STATE_T__ *ystate,
- Sint ylimit)
+ Sint reds)
{
return ERTS_RBT_FUNC__(foreach_unordered__)(&root, 0, op, arg,
- 1, ystate, ylimit);
+ 1, ystate, reds);
}
#endif /* ERTS_RBT_WANT_FOREACH_YIELDING */
@@ -1587,14 +1654,14 @@ ERTS_RBT_FUNC__(foreach_yielding)(ERTS_RBT_T *root,
static ERTS_RBT_API_INLINE__ int
ERTS_RBT_FUNC__(foreach_small_yielding)(ERTS_RBT_T *root,
- void (*op)(ERTS_RBT_T *, void *),
+ ERTS_RBT_CALLBACK_FOREACH_FUNC(op),
void *arg,
ERTS_RBT_YIELD_STATE_T__ *ystate,
- Sint ylimit)
+ Sint reds)
{
return ERTS_RBT_FUNC__(foreach_ordered__)(&root, 1, 0,
op, NULL, arg,
- 1, ystate, ylimit);
+ 1, ystate, reds);
}
#endif /* ERTS_RBT_WANT_FOREACH_SMALL_YIELDING */
@@ -1603,14 +1670,14 @@ ERTS_RBT_FUNC__(foreach_small_yielding)(ERTS_RBT_T *root,
static ERTS_RBT_API_INLINE__ int
ERTS_RBT_FUNC__(foreach_large_yielding)(ERTS_RBT_T *root,
- void (*op)(ERTS_RBT_T *, void *),
+ ERTS_RBT_CALLBACK_FOREACH_FUNC(op),
void *arg,
ERTS_RBT_YIELD_STATE_T__ *ystate,
- Sint ylimit)
+ Sint reds)
{
return ERTS_RBT_FUNC__(foreach_ordered__)(&root, 0, 0,
op, NULL, arg,
- 1, ystate, ylimit);
+ 1, ystate, reds);
}
#endif /* ERTS_RBT_WANT_FOREACH_LARGE_YIELDING */
@@ -1619,11 +1686,11 @@ ERTS_RBT_FUNC__(foreach_large_yielding)(ERTS_RBT_T *root,
static ERTS_RBT_API_INLINE__ void
ERTS_RBT_FUNC__(foreach_destroy)(ERTS_RBT_T **root,
- void (*op)(ERTS_RBT_T *, void *),
+ ERTS_RBT_CALLBACK_FOREACH_FUNC(op),
void *arg)
{
(void) ERTS_RBT_FUNC__(foreach_unordered__)(root, 1, op, arg,
- 0, NULL, 0);
+ 0, NULL, 0);
}
#endif /* ERTS_RBT_WANT_FOREACH_DESTROY */
@@ -1632,8 +1699,8 @@ ERTS_RBT_FUNC__(foreach_destroy)(ERTS_RBT_T **root,
static ERTS_RBT_API_INLINE__ void
ERTS_RBT_FUNC__(foreach_small_destroy)(ERTS_RBT_T **root,
- void (*op)(ERTS_RBT_T *, void *),
- void (*destr)(ERTS_RBT_T *, void *),
+ ERTS_RBT_CALLBACK_FOREACH_FUNC(op),
+ ERTS_RBT_CALLBACK_FOREACH_FUNC(destr),
void *arg)
{
(void) ERTS_RBT_FUNC__(foreach_ordered__)(root, 1, 1,
@@ -1647,8 +1714,8 @@ ERTS_RBT_FUNC__(foreach_small_destroy)(ERTS_RBT_T **root,
static ERTS_RBT_API_INLINE__ void
ERTS_RBT_FUNC__(foreach_large_destroy)(ERTS_RBT_T **root,
- void (*op)(ERTS_RBT_T *, void *),
- void (*destr)(ERTS_RBT_T *, void *),
+ ERTS_RBT_CALLBACK_FOREACH_FUNC(op),
+ ERTS_RBT_CALLBACK_FOREACH_FUNC(destr),
void *arg)
{
(void) ERTS_RBT_FUNC__(foreach_ordered__)(root, 0, 1,
@@ -1662,13 +1729,13 @@ ERTS_RBT_FUNC__(foreach_large_destroy)(ERTS_RBT_T **root,
static ERTS_RBT_API_INLINE__ int
ERTS_RBT_FUNC__(foreach_destroy_yielding)(ERTS_RBT_T **root,
- void (*op)(ERTS_RBT_T *, void *),
+ ERTS_RBT_CALLBACK_FOREACH_FUNC(op),
void *arg,
ERTS_RBT_YIELD_STATE_T__ *ystate,
- Sint ylimit)
+ Sint reds)
{
return ERTS_RBT_FUNC__(foreach_unordered__)(root, 1, op, arg,
- 1, ystate, ylimit);
+ 1, ystate, reds);
}
#endif /* ERTS_RBT_WANT_FOREACH_DESTROY_YIELDING */
@@ -1677,15 +1744,15 @@ ERTS_RBT_FUNC__(foreach_destroy_yielding)(ERTS_RBT_T **root,
static ERTS_RBT_API_INLINE__ int
ERTS_RBT_FUNC__(foreach_small_destroy_yielding)(ERTS_RBT_T **root,
- void (*op)(ERTS_RBT_T *, void *),
- void (*destr)(ERTS_RBT_T *, void *),
+ ERTS_RBT_CALLBACK_FOREACH_FUNC(op),
+ ERTS_RBT_CALLBACK_FOREACH_FUNC(destr),
void *arg,
ERTS_RBT_YIELD_STATE_T__ *ystate,
- Sint ylimit)
+ Sint reds)
{
return ERTS_RBT_FUNC__(foreach_ordered__)(root, 1, 1,
op, destr, arg,
- 1, ystate, ylimit);
+ 1, ystate, reds);
}
#endif /* ERTS_RBT_WANT_FOREACH_SMALL_DESTROY_YIELDING */
@@ -1694,15 +1761,15 @@ ERTS_RBT_FUNC__(foreach_small_destroy_yielding)(ERTS_RBT_T **root,
static ERTS_RBT_API_INLINE__ int
ERTS_RBT_FUNC__(foreach_large_destroy_yielding)(ERTS_RBT_T **root,
- void (*op)(ERTS_RBT_T *, void *),
- void (*destr)(ERTS_RBT_T *, void *),
+ ERTS_RBT_CALLBACK_FOREACH_FUNC(op),
+ ERTS_RBT_CALLBACK_FOREACH_FUNC(destr),
void *arg,
ERTS_RBT_YIELD_STATE_T__ *ystate,
- Sint ylimit)
+ Sint reds)
{
return ERTS_RBT_FUNC__(foreach_ordered__)(root, 0, 1,
op, destr, arg,
- 1, ystate, ylimit);
+ 1, ystate, reds);
}
#endif /* ERTS_RBT_WANT_FOREACH_LARGE_DESTROY_YIELDING */
@@ -1855,6 +1922,7 @@ ERTS_RBT_FUNC__(hdbg_check_tree)(ERTS_RBT_T *root, ERTS_RBT_T *n)
#ifdef ERTS_RBT_UNDEF
# undef ERTS_RBT_PREFIX
# undef ERTS_RBT_T
+# undef ERTS_RBT_CALLBACK_FOREACH_FUNC
# undef ERTS_RBT_KEY_T
# undef ERTS_RBT_FLAGS_T
# undef ERTS_RBT_INIT_EMPTY_TNODE
diff --git a/erts/emulator/beam/erl_time_sup.c b/erts/emulator/beam/erl_time_sup.c
index 29c698e34f..d26ea19494 100644
--- a/erts/emulator/beam/erl_time_sup.c
+++ b/erts/emulator/beam/erl_time_sup.c
@@ -1911,8 +1911,8 @@ typedef struct {
ErtsTimeOffsetMonitorInfo *to_mon_info;
} ErtsTimeOffsetMonitorContext;
-static void
-save_time_offset_monitor(ErtsMonitor *mon, void *vcntxt)
+static int
+save_time_offset_monitor(ErtsMonitor *mon, void *vcntxt, Sint reds)
{
ErtsTimeOffsetMonitorContext *cntxt;
ErtsMonitorData *mdp = erts_monitor_to_data(mon);
@@ -1935,7 +1935,7 @@ save_time_offset_monitor(ErtsMonitor *mon, void *vcntxt)
cntxt->to_mon_info[mix].ref
= make_internal_ref(&cntxt->to_mon_info[mix].heap[0]);
-
+ return 1;
}
static void
diff --git a/erts/emulator/beam/external.c b/erts/emulator/beam/external.c
index 9a66e491f3..73eae614fa 100644
--- a/erts/emulator/beam/external.c
+++ b/erts/emulator/beam/external.c
@@ -102,7 +102,7 @@ static byte* enc_term(ErtsAtomCacheMap *, Eterm, byte*, Uint32, struct erl_off_h
struct TTBEncodeContext_;
static int enc_term_int(struct TTBEncodeContext_*,ErtsAtomCacheMap *acmp, Eterm obj, byte* ep, Uint32 dflags,
struct erl_off_heap_header** off_heap, Sint *reds, byte **res);
-static Uint is_external_string(Eterm obj, int* p_is_string);
+static int is_external_string(Eterm obj, Uint* lenp);
static byte* enc_atom(ErtsAtomCacheMap *, Eterm, byte*, Uint32);
static byte* enc_pid(ErtsAtomCacheMap *, Eterm, byte*, Uint32);
struct B2TContext_t;
@@ -262,19 +262,12 @@ erts_finalize_atom_cache_map(ErtsAtomCacheMap *acmp, Uint32 dflags)
if (acmp) {
int long_atoms = 0; /* !0 if one or more atoms are longer than 255. */
int i;
- int sz;
- int fix_sz
- = 1 /* VERSION_MAGIC */
- + 1 /* DIST_HEADER */
- + 1 /* dist header flags */
- + 1 /* number of internal cache entries */
- ;
+ int sz = 0;
int min_sz;
ASSERT(dflags & DFLAG_UTF8_ATOMS);
ASSERT(acmp->hdr_sz < 0);
/* Make sure cache update instructions fit */
- min_sz = fix_sz+(2+4)*acmp->sz;
- sz = fix_sz;
+ min_sz = (2+4)*acmp->sz;
for (i = 0; i < acmp->sz; i++) {
Atom *a;
Eterm atom;
@@ -302,17 +295,28 @@ erts_finalize_atom_cache_map(ErtsAtomCacheMap *acmp, Uint32 dflags)
}
Uint
-erts_encode_ext_dist_header_size(ErtsAtomCacheMap *acmp)
+erts_encode_ext_dist_header_size(ErtsAtomCacheMap *acmp, Uint fragments)
{
if (!acmp)
return 0;
else {
+ int fix_sz
+ = 1 /* VERSION_MAGIC */
+ + 1 /* DIST_HEADER */
+ + 1 /* dist header flags */
+ + 1 /* number of internal cache entries */
+ ;
ASSERT(acmp->hdr_sz >= 0);
- return acmp->hdr_sz;
+ if (fragments > 1)
+ fix_sz += 8 /* sequence id */
+ + 8 /* number of fragments */
+ ;
+ return fix_sz + acmp->hdr_sz;
}
}
-byte *erts_encode_ext_dist_header_setup(byte *ctl_ext, ErtsAtomCacheMap *acmp)
+byte *erts_encode_ext_dist_header_setup(byte *ctl_ext, ErtsAtomCacheMap *acmp,
+ Uint fragments, Eterm from)
{
/* Maximum number of atom must be less than the maximum of a 32 bits
unsigned integer. Check is done in erl_init.c, erl_start function. */
@@ -346,12 +350,37 @@ byte *erts_encode_ext_dist_header_setup(byte *ctl_ext, ErtsAtomCacheMap *acmp)
put_int8(acmp->sz, ep);
--ep;
put_int8(dist_hdr_flags, ep);
- *--ep = DIST_HEADER;
- *--ep = VERSION_MAGIC;
+ if (fragments > 1) {
+ ASSERT(is_pid(from));
+ ep -= 8;
+ put_int64(fragments, ep);
+ ep -= 8;
+ put_int64(from, ep);
+ *--ep = DIST_FRAG_HEADER;
+ } else {
+ *--ep = DIST_HEADER;
+ }
+ *--ep = VERSION_MAGIC;
return ep;
}
}
+byte *erts_encode_ext_dist_header_fragment(byte **hdrpp,
+ Uint fragment,
+ Eterm from)
+{
+ byte *ep = *hdrpp, *start = ep;
+ ASSERT(is_pid(from));
+ *ep++ = VERSION_MAGIC;
+ *ep++ = DIST_FRAG_CONT;
+ put_int64(from, ep);
+ ep += 8;
+ put_int64(fragment, ep);
+ ep += 8;
+ *hdrpp = ep;
+ return start;
+}
+
#define PASS_THROUGH 'p'
@@ -365,7 +394,8 @@ Sint erts_encode_ext_dist_header_finalize(ErtsDistOutputBuf* ob,
int ci, sz;
byte dist_hdr_flags;
int long_atoms;
- register byte *ep = ob->extp;
+ Uint64 seq_id = 0, frag_id = 0;
+ register byte *ep = ob->hdrp ? ob->hdrp : ob->extp;
ASSERT(dflags & DFLAG_UTF8_ATOMS);
/*
@@ -416,7 +446,7 @@ Sint erts_encode_ext_dist_header_finalize(ErtsDistOutputBuf* ob,
}
goto done;
}
- else if (ep[1] != DIST_HEADER) {
+ else if (ep[1] != DIST_HEADER && ep[1] != DIST_FRAG_HEADER && ep[1] != DIST_FRAG_CONT) {
ASSERT(ep[1] == SMALL_TUPLE_EXT || ep[1] == LARGE_TUPLE_EXT);
ASSERT(!(dflags & DFLAG_DIST_HDR_ATOM_CACHE));
/* Node without atom cache, 'pass through' needed */
@@ -424,6 +454,17 @@ Sint erts_encode_ext_dist_header_finalize(ErtsDistOutputBuf* ob,
goto done;
}
+ if (ep[1] == DIST_FRAG_CONT) {
+ ep = ob->extp;
+ goto done;
+ } else if (ep[1] == DIST_FRAG_HEADER) {
+ /* skip the seq id and frag id */
+ seq_id = get_int64(&ep[2]);
+ ep += 8;
+ frag_id = get_int64(&ep[2]);
+ ep += 8;
+ }
+
dist_hdr_flags = ep[2];
long_atoms = ERTS_DIST_HDR_LONG_ATOMS_FLG & ((int) dist_hdr_flags);
@@ -546,11 +587,19 @@ Sint erts_encode_ext_dist_header_finalize(ErtsDistOutputBuf* ob,
}
--ep;
put_int8(ci, ep);
- *--ep = DIST_HEADER;
+ if (seq_id) {
+ ep -= 8;
+ put_int64(frag_id, ep);
+ ep -= 8;
+ put_int64(seq_id, ep);
+ *--ep = DIST_FRAG_HEADER;
+ } else {
+ *--ep = DIST_HEADER;
+ }
*--ep = VERSION_MAGIC;
done:
ob->extp = ep;
- ASSERT(&ob->data[0] <= ob->extp && ob->extp < ob->ext_endp);
+ ASSERT((byte*)ob->bin->orig_bytes <= ob->extp && ob->extp < ob->ext_endp);
return reds < 0 ? 0 : reds;
}
@@ -571,7 +620,7 @@ int erts_encode_dist_ext_size(Eterm term, Uint32 flags, ErtsAtomCacheMap *acmp,
}
}
-int erts_encode_dist_ext_size_int(Eterm term, struct erts_dsig_send_context* ctx, Uint* szp)
+int erts_encode_dist_ext_size_int(Eterm term, ErtsDSigSendContext *ctx, Uint* szp)
{
Uint sz;
if (encode_size_struct_int(&ctx->u.sc, ctx->acmp, term, ctx->flags, &ctx->reds, &sz)) {
@@ -635,56 +684,106 @@ byte* erts_encode_ext_ets(Eterm term, byte *ep, struct erl_off_heap_header** off
off_heap);
}
-ErtsDistExternal *
-erts_make_dist_ext_copy(ErtsDistExternal *edep, Uint xsize)
+
+static Uint
+dist_ext_size(ErtsDistExternal *edep)
{
- size_t align_sz;
- size_t dist_ext_sz;
- size_t ext_sz;
- byte *ep;
- ErtsDistExternal *new_edep;
+ Uint sz = sizeof(ErtsDistExternal);
+
+ ASSERT(edep->data->ext_endp && edep->data->extp);
+ ASSERT(edep->data->ext_endp >= edep->data->extp);
+
+ if (edep->flags & ERTS_DIST_EXT_ATOM_TRANS_TAB) {
+ ASSERT(0 <= edep->attab.size \
+ && edep->attab.size <= ERTS_ATOM_CACHE_SIZE);
+ sz -= sizeof(Eterm)*(ERTS_ATOM_CACHE_SIZE - edep->attab.size);
+ } else {
+ sz -= sizeof(ErtsAtomTranslationTable);
+ }
+ return sz;
+}
+
+Uint
+erts_dist_ext_size(ErtsDistExternal *edep)
+{
+ Uint sz = dist_ext_size(edep);
+ sz += edep->data[0].frag_id * sizeof(ErtsDistExternalData);
+ return sz + ERTS_EXTRA_DATA_ALIGN_SZ(sz);
+}
- dist_ext_sz = ERTS_DIST_EXT_SIZE(edep);
- ASSERT(edep->ext_endp && edep->extp);
- ASSERT(edep->ext_endp >= edep->extp);
- ext_sz = edep->ext_endp - edep->extp;
+Uint
+erts_dist_ext_data_size(ErtsDistExternal *edep)
+{
+ Uint sz = 0, i;
+ for (i = 0; i < edep->data->frag_id; i++)
+ sz += edep->data[i].ext_endp - edep->data[i].extp;
+ return sz;
+}
- align_sz = ERTS_EXTRA_DATA_ALIGN_SZ(dist_ext_sz + ext_sz);
+void
+erts_dist_ext_frag(ErtsDistExternalData *ede_datap, ErtsDistExternal *edep)
+{
+ ErtsDistExternalData *new_ede_datap = &edep->data[edep->data->frag_id - ede_datap->frag_id];
+ sys_memcpy(new_ede_datap, ede_datap, sizeof(ErtsDistExternalData));
+
+ /* If the data is not backed by a binary, we create one here to keep
+ things simple. Only custom distribution drivers should use lists. */
+ if (new_ede_datap->binp == NULL) {
+ size_t ext_sz = ede_datap->ext_endp - ede_datap->extp;
+ new_ede_datap->binp = erts_bin_nrml_alloc(ext_sz);
+ sys_memcpy(new_ede_datap->binp->orig_bytes, (void *) ede_datap->extp, ext_sz);
+ new_ede_datap->extp = (byte*)new_ede_datap->binp->orig_bytes;
+ new_ede_datap->ext_endp = (byte*)new_ede_datap->binp->orig_bytes + ext_sz;
+ } else {
+ erts_refc_inc(&new_ede_datap->binp->intern.refc, 2);
+ }
+}
- new_edep = erts_alloc(ERTS_ALC_T_EXT_TERM_DATA,
- dist_ext_sz + ext_sz + align_sz + xsize);
+void
+erts_make_dist_ext_copy(ErtsDistExternal *edep, ErtsDistExternal *new_edep)
+{
+ size_t dist_ext_sz = dist_ext_size(edep);
+ byte *ep;
ep = (byte *) new_edep;
sys_memcpy((void *) ep, (void *) edep, dist_ext_sz);
+ erts_ref_dist_entry(new_edep->dep);
+
ep += dist_ext_sz;
- if (new_edep->dep)
- erts_ref_dist_entry(new_edep->dep);
- new_edep->extp = ep;
- new_edep->ext_endp = ep + ext_sz;
- new_edep->heap_size = -1;
- sys_memcpy((void *) ep, (void *) edep->extp, ext_sz);
- return new_edep;
+
+ new_edep->data = (ErtsDistExternalData*)ep;
+ sys_memzero(new_edep->data, sizeof(ErtsDistExternalData) * edep->data->frag_id);
+ new_edep->data->frag_id = edep->data->frag_id;
+ erts_dist_ext_frag(edep->data, new_edep);
}
-int
+void
+erts_free_dist_ext_copy(ErtsDistExternal *edep)
+{
+ int i;
+ erts_deref_dist_entry(edep->dep);
+ for (i = 0; i < edep->data->frag_id; i++)
+ if (edep->data[i].binp)
+ erts_bin_release(edep->data[i].binp);
+}
+
+ErtsPrepDistExtRes
erts_prepare_dist_ext(ErtsDistExternal *edep,
byte *ext,
Uint size,
+ Binary *binp,
DistEntry *dep,
Uint32 conn_id,
ErtsAtomCache *cache)
{
register byte *ep;
- edep->heap_size = -1;
- edep->flags = 0;
- edep->dep = dep;
-
ASSERT(dep);
erts_de_rlock(dep);
ASSERT(dep->flags & DFLAG_UTF8_ATOMS);
+
if ((dep->state != ERTS_DE_STATE_CONNECTED &&
dep->state != ERTS_DE_STATE_PENDING)
|| dep->connection_id != conn_id) {
@@ -697,7 +796,7 @@ erts_prepare_dist_ext(ErtsDistExternal *edep,
ext++;
size--;
}
- edep->ext_endp = ext + size;
+
ep = ext;
if (size < 2)
@@ -713,16 +812,33 @@ erts_prepare_dist_ext(ErtsDistExternal *edep,
goto fail;
}
+ edep->heap_size = -1;
+ edep->flags = 0;
+ edep->dep = dep;
+ edep->connection_id = conn_id;
+ edep->data->ext_endp = ext+size;
+ edep->data->binp = binp;
+ edep->data->seq_id = 0;
+ edep->data->frag_id = 1;
+
if (dep->flags & DFLAG_DIST_HDR_ATOM_CACHE)
edep->flags |= ERTS_DIST_EXT_DFLAG_HDR;
- edep->connection_id = dep->connection_id;
-
- if (ep[1] != DIST_HEADER) {
+ if (ep[1] != DIST_HEADER && ep[1] != DIST_FRAG_HEADER && ep[1] != DIST_FRAG_CONT) {
if (edep->flags & ERTS_DIST_EXT_DFLAG_HDR)
goto bad_hdr;
edep->attab.size = 0;
- edep->extp = ext;
+ edep->data->extp = ext;
+ }
+ else if (ep[1] == DIST_FRAG_CONT) {
+ if (!(dep->flags & DFLAG_FRAGMENTS))
+ goto bad_hdr;
+ edep->attab.size = 0;
+ edep->data->extp = ext + 1 + 1 + 8 + 8;
+ edep->data->seq_id = get_int64(&ep[2]);
+ edep->data->frag_id = get_int64(&ep[2+8]);
+ erts_de_runlock(dep);
+ return ERTS_PREP_DIST_EXT_FRAG_CONT;
}
else {
int tix;
@@ -731,9 +847,17 @@ erts_prepare_dist_ext(ErtsDistExternal *edep,
if (!(edep->flags & ERTS_DIST_EXT_DFLAG_HDR))
goto bad_hdr;
+ if (ep[1] == DIST_FRAG_HEADER) {
+ if (!(dep->flags & DFLAG_FRAGMENTS))
+ goto bad_hdr;
+ edep->data->seq_id = get_int64(&ep[2]);
+ edep->data->frag_id = get_int64(&ep[2+8]);
+ ep += 16;
+ }
+
#undef CHKSIZE
#define CHKSIZE(SZ) \
- do { if ((SZ) > edep->ext_endp - ep) goto bad_hdr; } while(0)
+ do { if ((SZ) > edep->data->ext_endp - ep) goto bad_hdr; } while(0)
CHKSIZE(1+1+1);
ep += 2;
@@ -863,7 +987,7 @@ erts_prepare_dist_ext(ErtsDistExternal *edep,
#endif
}
}
- edep->extp = ep;
+ edep->data->extp = ep;
#ifdef ERTS_DEBUG_USE_DIST_SEP
if (*ep != VERSION_MAGIC)
goto bad_hdr;
@@ -888,7 +1012,7 @@ erts_prepare_dist_ext(ErtsDistExternal *edep,
erts_this_node->sysname,
edep->dep->sysname,
dist_entry_channel_no(edep->dep));
- for (ep = ext; ep < edep->ext_endp; ep++)
+ for (ep = ext; ep < edep->data->ext_endp; ep++)
erts_dsprintf(dsbufp, ep != ext ? ",%b8u" : "<<%b8u", *ep);
erts_dsprintf(dsbufp, ">>");
erts_send_warning_to_logger_nogl(dsbufp);
@@ -913,9 +1037,9 @@ bad_dist_ext(ErtsDistExternal *edep)
erts_this_node->sysname,
dep->sysname,
dist_entry_channel_no(dep));
- for (ep = edep->extp; ep < edep->ext_endp; ep++)
+ for (ep = edep->data->extp; ep < edep->data->ext_endp; ep++)
erts_dsprintf(dsbufp,
- ep != edep->extp ? ",%b8u" : "<<...,%b8u",
+ ep != edep->data->extp ? ",%b8u" : "<<...,%b8u",
*ep);
erts_dsprintf(dsbufp, ">>\n");
erts_dsprintf(dsbufp, "ATOM_CACHE_REF translations: ");
@@ -933,30 +1057,32 @@ bad_dist_ext(ErtsDistExternal *edep)
}
Sint
-erts_decode_dist_ext_size(ErtsDistExternal *edep)
+erts_decode_dist_ext_size(ErtsDistExternal *edep, int kill_connection)
{
Sint res;
byte *ep;
- if (edep->extp >= edep->ext_endp)
+
+ if (edep->data->extp >= edep->data->ext_endp)
goto fail;
#ifndef ERTS_DEBUG_USE_DIST_SEP
if (edep->flags & ERTS_DIST_EXT_DFLAG_HDR) {
- if (*edep->extp == VERSION_MAGIC)
+ if (*edep->data->extp == VERSION_MAGIC)
goto fail;
- ep = edep->extp;
+ ep = edep->data->extp;
}
else
#endif
{
- if (*edep->extp != VERSION_MAGIC)
+ if (*edep->data->extp != VERSION_MAGIC)
goto fail;
- ep = edep->extp+1;
+ ep = edep->data->extp+1;
}
- res = decoded_size(ep, edep->ext_endp, 0, NULL);
+ res = decoded_size(ep, edep->data->ext_endp, 0, NULL);
if (res >= 0)
return res;
fail:
- bad_dist_ext(edep);
+ if (kill_connection)
+ bad_dist_ext(edep);
return -1;
}
@@ -982,12 +1108,15 @@ Sint erts_decode_ext_size_ets(byte *ext, Uint size)
*/
Eterm
erts_decode_dist_ext(ErtsHeapFactory* factory,
- ErtsDistExternal *edep)
+ ErtsDistExternal *edep,
+ int kill_connection)
{
Eterm obj;
- byte* ep = edep->extp;
+ byte* ep;
- if (ep >= edep->ext_endp)
+ ep = edep->data->extp;
+
+ if (ep >= edep->data->ext_endp)
goto error;
#ifndef ERTS_DEBUG_USE_DIST_SEP
if (edep->flags & ERTS_DIST_EXT_DFLAG_HDR) {
@@ -1005,14 +1134,15 @@ erts_decode_dist_ext(ErtsHeapFactory* factory,
if (!ep)
goto error;
- edep->extp = ep;
+ edep->data->extp = ep;
return obj;
error:
erts_factory_undo(factory);
- bad_dist_ext(edep);
+ if (kill_connection)
+ bad_dist_ext(edep);
return THE_NON_VALUE;
}
@@ -1057,6 +1187,7 @@ BIF_RETTYPE erts_debug_dist_ext_to_term_2(BIF_ALIST_2)
Eterm res;
Sint hsz;
ErtsDistExternal ede;
+ ErtsDistExternalData ede_data;
Eterm *tp;
Eterm real_bin;
Uint offset;
@@ -1069,7 +1200,8 @@ BIF_RETTYPE erts_debug_dist_ext_to_term_2(BIF_ALIST_2)
ede.flags = ERTS_DIST_EXT_ATOM_TRANS_TAB;
ede.dep = NULL;
ede.heap_size = -1;
-
+ ede.data = &ede_data;
+
if (is_not_tuple(BIF_ARG_1))
goto badarg;
tp = tuple_val(BIF_ARG_1);
@@ -1094,15 +1226,15 @@ BIF_RETTYPE erts_debug_dist_ext_to_term_2(BIF_ALIST_2)
if (bitsize != 0)
goto badarg;
- ede.extp = binary_bytes(real_bin)+offset;
- ede.ext_endp = ede.extp + size;
+ ede.data->extp = binary_bytes(real_bin)+offset;
+ ede.data->ext_endp = ede.data->extp + size;
- hsz = erts_decode_dist_ext_size(&ede);
+ hsz = erts_decode_dist_ext_size(&ede, 1);
if (hsz < 0)
goto badarg;
erts_factory_proc_prealloc_init(&factory, BIF_P, hsz);
- res = erts_decode_dist_ext(&factory, &ede);
+ res = erts_decode_dist_ext(&factory, &ede, 1);
erts_factory_close(&factory);
if (is_value(res))
@@ -2348,7 +2480,7 @@ dec_atom(ErtsDistExternal *edep, byte* ep, Eterm* objp)
return ep;
}
-static ERTS_INLINE ErlNode* dec_get_node(Eterm sysname, Uint32 creation)
+static ERTS_INLINE ErlNode* dec_get_node(Eterm sysname, Uint32 creation, Eterm book)
{
if (sysname == INTERNAL_LOCAL_SYSNAME) /* && DFLAG_INTERNAL_TAGS */
return erts_this_node;
@@ -2357,7 +2489,7 @@ static ERTS_INLINE ErlNode* dec_get_node(Eterm sysname, Uint32 creation)
&& (creation == erts_this_node->creation || creation == ORIG_CREATION))
return erts_this_node;
- return erts_find_or_insert_node(sysname,creation);
+ return erts_find_or_insert_node(sysname,creation,book);
}
static byte*
@@ -2403,7 +2535,7 @@ dec_pid(ErtsDistExternal *edep, ErtsHeapFactory* factory, byte* ep,
* We are careful to create the node entry only after all
* validity tests are done.
*/
- node = dec_get_node(sysname, cre);
+ node = dec_get_node(sysname, cre, make_boxed(factory->hp));
if(node == erts_this_node) {
*objp = make_internal_pid(data);
@@ -2481,11 +2613,21 @@ enc_term_int(TTBEncodeContext* ctx, ErtsAtomCacheMap *acmp, Eterm obj, byte* ep,
{
Eterm* cons = list_val(obj);
Eterm tl;
+ Uint len_cnt = WSTACK_POP(s);
obj = CAR(cons);
tl = CDR(cons);
- WSTACK_PUSH2(s, (is_list(tl) ? ENC_ONE_CONS : ENC_TERM),
- tl);
+ if (is_list(tl)) {
+ len_cnt++;
+ WSTACK_PUSH3(s, len_cnt, ENC_ONE_CONS, tl);
+ }
+ else {
+ byte* list_lenp = (byte*) WSTACK_POP(s);
+ ASSERT(list_lenp[-1] == LIST_EXT);
+ put_int32(len_cnt, list_lenp);
+
+ WSTACK_PUSH2(s, ENC_TERM, tl);
+ }
}
break;
case ENC_PATCH_FUN_SIZE:
@@ -2689,10 +2831,7 @@ enc_term_int(TTBEncodeContext* ctx, ErtsAtomCacheMap *acmp, Eterm obj, byte* ep,
}
case LIST_DEF:
{
- int is_str;
-
- i = is_external_string(obj, &is_str);
- if (is_str) {
+ if (is_external_string(obj, &i)) {
*ep++ = STRING_EXT;
put_int16(i, ep);
ep += 2;
@@ -2701,9 +2840,12 @@ enc_term_int(TTBEncodeContext* ctx, ErtsAtomCacheMap *acmp, Eterm obj, byte* ep,
*ep++ = unsigned_val(CAR(cons));
obj = CDR(cons);
}
+ r -= i;
} else {
+ r -= i/2;
*ep++ = LIST_EXT;
- put_int32(i, ep);
+ /* Patch list length when we find end of list */
+ WSTACK_PUSH2(s, (UWord)ep, 1);
ep += 4;
goto encode_one_cons;
}
@@ -2961,9 +3103,13 @@ enc_term_int(TTBEncodeContext* ctx, ErtsAtomCacheMap *acmp, Eterm obj, byte* ep,
return 0;
}
+/** @brief Is it a list of bytes not longer than MAX_STRING_LEN?
+ * @param lenp out: string length or number of list cells traversed
+ * @return true/false
+ */
static
-Uint
-is_external_string(Eterm list, int* p_is_string)
+int
+is_external_string(Eterm list, Uint* lenp)
{
Uint len = 0;
@@ -2975,29 +3121,15 @@ is_external_string(Eterm list, int* p_is_string)
Eterm* consp = list_val(list);
Eterm hd = CAR(consp);
- if (!is_byte(hd)) {
- break;
+ if (!is_byte(hd) || ++len > MAX_STRING_LEN) {
+ *lenp = len;
+ return 0;
}
- len++;
list = CDR(consp);
}
- /*
- * If we have reached the end of the list, and we have
- * not exceeded the maximum length of a string, this
- * is a string.
- */
- *p_is_string = is_nil(list) && len < MAX_STRING_LEN;
-
- /*
- * Continue to calculate the length.
- */
- while (is_list(list)) {
- Eterm* consp = list_val(list);
- len++;
- list = CDR(consp);
- }
- return len;
+ *lenp = len;
+ return is_nil(list);
}
@@ -3397,7 +3529,7 @@ dec_term_atom_common:
cre = get_int32(ep);
ep += 4;
}
- node = dec_get_node(sysname, cre);
+ node = dec_get_node(sysname, cre, make_boxed(hp));
if(node == erts_this_node) {
*objp = make_internal_port(num);
}
@@ -3477,7 +3609,7 @@ dec_term_atom_common:
if (ref_words > ERTS_MAX_REF_NUMBERS)
goto error;
- node = dec_get_node(sysname, cre);
+ node = dec_get_node(sysname, cre, make_boxed(hp));
if(node == erts_this_node) {
rtp = (ErtsORefThing *) hp;
@@ -4075,8 +4207,8 @@ encode_size_struct_int(TTBSizeContext* ctx, ErtsAtomCacheMap *acmp, Eterm obj,
for (;;) {
ASSERT(!is_header(obj));
- if (ctx && --r == 0) {
- *reds = r;
+ if (ctx && --r <= 0) {
+ *reds = 0;
ctx->obj = obj;
ctx->result = result;
WSTACK_SAVE(s, &ctx->wstack);
@@ -4166,8 +4298,10 @@ encode_size_struct_int(TTBSizeContext* ctx, ErtsAtomCacheMap *acmp, Eterm obj,
result += (1 + encode_size_struct2(acmp, port_node_name(obj), dflags) +
4 + 1);
break;
- case LIST_DEF:
- if ((m = is_string(obj)) && (m < MAX_STRING_LEN)) {
+ case LIST_DEF: {
+ int is_str = is_external_string(obj, &m);
+ r -= m/2;
+ if (is_str) {
result += m + 2 + 1;
} else {
result += 5;
@@ -4176,6 +4310,7 @@ encode_size_struct_int(TTBSizeContext* ctx, ErtsAtomCacheMap *acmp, Eterm obj,
continue; /* big loop */
}
break;
+ }
case TUPLE_DEF:
{
Eterm* ptr = tuple_val(obj);
@@ -4317,7 +4452,7 @@ encode_size_struct_int(TTBSizeContext* ctx, ErtsAtomCacheMap *acmp, Eterm obj,
if (is_header(obj)) {
switch (obj) {
- case LIST_TAIL_OP:
+ case LIST_TAIL_OP:
obj = (Eterm) WSTACK_POP(s);
if (is_list(obj)) {
Eterm* cons = list_val(obj);
@@ -4343,7 +4478,7 @@ encode_size_struct_int(TTBSizeContext* ctx, ErtsAtomCacheMap *acmp, Eterm obj,
WSTACK_DESTROY(s);
if (ctx) {
ASSERT(ctx->wstack.wstart == NULL);
- *reds = r;
+ *reds = r < 0 ? 0 : r;
}
*res = result;
return 0;
diff --git a/erts/emulator/beam/external.h b/erts/emulator/beam/external.h
index edac177cc6..396cd9f802 100644
--- a/erts/emulator/beam/external.h
+++ b/erts/emulator/beam/external.h
@@ -58,6 +58,8 @@
#define SMALL_ATOM_UTF8_EXT 'w'
#define DIST_HEADER 'D'
+#define DIST_FRAG_HEADER 'E'
+#define DIST_FRAG_CONT 'F'
#define ATOM_CACHE_REF 'R'
#define ATOM_INTERNAL_REF2 'I'
#define ATOM_INTERNAL_REF3 'K'
@@ -122,13 +124,23 @@ typedef struct {
#define ERTS_DIST_CON_ID_MASK ((Uint32) 0x00ffffff) /* also in net_kernel.erl */
-typedef struct {
- DistEntry *dep;
+struct binary;
+typedef struct erl_dist_external_data ErtsDistExternalData;
+
+struct erl_dist_external_data {
+ Uint64 seq_id;
+ Uint64 frag_id;
byte *extp;
byte *ext_endp;
+ struct binary *binp;
+};
+
+typedef struct erl_dist_external {
Sint heap_size;
- Uint32 connection_id;
+ DistEntry *dep;
Uint32 flags;
+ Uint32 connection_id;
+ ErtsDistExternalData *data;
ErtsAtomTranslationTable attab;
} ErtsDistExternal;
@@ -155,8 +167,9 @@ void erts_reset_atom_cache_map(ErtsAtomCacheMap *);
void erts_destroy_atom_cache_map(ErtsAtomCacheMap *);
void erts_finalize_atom_cache_map(ErtsAtomCacheMap *, Uint32);
-Uint erts_encode_ext_dist_header_size(ErtsAtomCacheMap *);
-byte *erts_encode_ext_dist_header_setup(byte *, ErtsAtomCacheMap *);
+Uint erts_encode_ext_dist_header_size(ErtsAtomCacheMap *, Uint);
+byte *erts_encode_ext_dist_header_setup(byte *, ErtsAtomCacheMap *, Uint, Eterm);
+byte *erts_encode_ext_dist_header_fragment(byte **, Uint, Eterm);
Sint erts_encode_ext_dist_header_finalize(ErtsDistOutputBuf*, DistEntry *, Uint32 dflags, Sint reds);
struct erts_dsig_send_context;
int erts_encode_dist_ext_size(Eterm, Uint32, ErtsAtomCacheMap*, Uint* szp);
@@ -171,20 +184,24 @@ Uint erts_encode_ext_size_ets(Eterm);
void erts_encode_ext(Eterm, byte **);
byte* erts_encode_ext_ets(Eterm, byte *, struct erl_off_heap_header** ext_off_heap);
-ERTS_GLB_INLINE void erts_free_dist_ext_copy(ErtsDistExternal *);
-ERTS_GLB_INLINE void *erts_dist_ext_trailer(ErtsDistExternal *);
-ErtsDistExternal *erts_make_dist_ext_copy(ErtsDistExternal *, Uint);
-void *erts_dist_ext_trailer(ErtsDistExternal *);
-void erts_destroy_dist_ext_copy(ErtsDistExternal *);
-
-#define ERTS_PREP_DIST_EXT_FAILED (-1)
-#define ERTS_PREP_DIST_EXT_SUCCESS (0)
-#define ERTS_PREP_DIST_EXT_CLOSED (1)
-
-int erts_prepare_dist_ext(ErtsDistExternal *, byte *, Uint,
- DistEntry *, Uint32 conn_id, ErtsAtomCache *);
-Sint erts_decode_dist_ext_size(ErtsDistExternal *);
-Eterm erts_decode_dist_ext(ErtsHeapFactory* factory, ErtsDistExternal *);
+Uint erts_dist_ext_size(ErtsDistExternal *);
+Uint erts_dist_ext_data_size(ErtsDistExternal *);
+void erts_free_dist_ext_copy(ErtsDistExternal *);
+void erts_make_dist_ext_copy(ErtsDistExternal *, ErtsDistExternal *);
+void erts_dist_ext_frag(ErtsDistExternalData *, ErtsDistExternal *);
+#define erts_get_dist_ext(HFRAG) ((ErtsDistExternal*)((HFRAG)->mem + (HFRAG)->used_size))
+
+typedef enum {
+ ERTS_PREP_DIST_EXT_FAILED,
+ ERTS_PREP_DIST_EXT_SUCCESS,
+ ERTS_PREP_DIST_EXT_FRAG_CONT,
+ ERTS_PREP_DIST_EXT_CLOSED
+} ErtsPrepDistExtRes;
+
+ErtsPrepDistExtRes erts_prepare_dist_ext(ErtsDistExternal *, byte *, Uint, struct binary *,
+ DistEntry *, Uint32, ErtsAtomCache *);
+Sint erts_decode_dist_ext_size(ErtsDistExternal *, int);
+Eterm erts_decode_dist_ext(ErtsHeapFactory*, ErtsDistExternal *, int);
Sint erts_decode_ext_size(byte*, Uint);
Sint erts_decode_ext_size_ets(byte*, Uint);
@@ -200,25 +217,4 @@ int erts_debug_max_atom_out_cache_index(void);
int erts_debug_atom_to_out_cache_index(Eterm);
void transcode_free_ctx(DistEntry* dep);
-#if ERTS_GLB_INLINE_INCL_FUNC_DEF
-
-ERTS_GLB_INLINE void
-erts_free_dist_ext_copy(ErtsDistExternal *edep)
-{
- if (edep->dep)
- erts_deref_dist_entry(edep->dep);
- erts_free(ERTS_ALC_T_EXT_TERM_DATA, edep);
-}
-
-ERTS_GLB_INLINE void *
-erts_dist_ext_trailer(ErtsDistExternal *edep)
-{
- void *res = (void *) (edep->ext_endp
- + ERTS_EXTRA_DATA_ALIGN_SZ(edep->ext_endp));
- ASSERT((((UWord) res) % sizeof(Uint)) == 0);
- return res;
-}
-
-#endif
-
#endif /* ERL_EXTERNAL_H__ */
diff --git a/erts/emulator/beam/global.h b/erts/emulator/beam/global.h
index 29de162b42..f9bbe4167f 100644
--- a/erts/emulator/beam/global.h
+++ b/erts/emulator/beam/global.h
@@ -113,6 +113,9 @@ extern Eterm erts_bld_resource_ref(Eterm** hp, ErlOffHeap*, ErtsResource*);
extern void erts_pre_nif(struct enif_environment_t*, Process*,
struct erl_module_nif*, Process* tracee);
extern void erts_post_nif(struct enif_environment_t* env);
+#ifdef DEBUG
+int erts_dbg_is_resource_dying(ErtsResource*);
+#endif
extern void erts_resource_stop(ErtsResource*, ErlNifEvent, int is_direct_call);
void erts_fire_nif_monitor(ErtsMonitor *tmon);
void erts_nif_demonitored(ErtsResource* resource);
@@ -1093,7 +1096,7 @@ extern int distribution_info(fmtfn_t, void *);
extern int is_node_name_atom(Eterm a);
extern int erts_net_message(Port *, DistEntry *, Uint32 conn_id,
- byte *, ErlDrvSizeT, byte *, ErlDrvSizeT);
+ byte *, ErlDrvSizeT, Binary *, byte *, ErlDrvSizeT);
extern void init_dist(void);
extern int stop_dist(void);
diff --git a/erts/emulator/beam/io.c b/erts/emulator/beam/io.c
index 7322239a73..b961c639f5 100644
--- a/erts/emulator/beam/io.c
+++ b/erts/emulator/beam/io.c
@@ -3644,20 +3644,22 @@ typedef struct {
Eterm reason;
} ErtsPortExitContext;
-static void link_port_exit(ErtsLink *lnk, void *vpectxt)
+static int link_port_exit(ErtsLink *lnk, void *vpectxt, Sint reds)
{
ErtsPortExitContext *pectxt = vpectxt;
erts_proc_sig_send_link_exit(NULL, pectxt->port_id,
lnk, pectxt->reason, NIL);
+ return 1;
}
-static void monitor_port_exit(ErtsMonitor *mon, void *vpectxt)
+static int monitor_port_exit(ErtsMonitor *mon, void *vpectxt, Sint reds)
{
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);
+ return 1;
}
/* 'from' is sending 'this_port' an exit signal, (this_port must be internal).
@@ -4836,7 +4838,7 @@ typedef struct {
void *arg;
} prt_one_lnk_data;
-static void prt_one_monitor(ErtsMonitor *mon, void *vprtd)
+static int prt_one_monitor(ErtsMonitor *mon, void *vprtd, Sint reds)
{
ErtsMonitorData *mdp = erts_monitor_to_data(mon);
prt_one_lnk_data *prtd = (prt_one_lnk_data *) vprtd;
@@ -4844,12 +4846,14 @@ static void prt_one_monitor(ErtsMonitor *mon, void *vprtd)
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);
+ return 1;
}
-static void prt_one_lnk(ErtsLink *lnk, void *vprtd)
+static int prt_one_lnk(ErtsLink *lnk, void *vprtd, Sint reds)
{
prt_one_lnk_data *prtd = (prt_one_lnk_data *) vprtd;
erts_print(prtd->to, prtd->arg, "%T", lnk->other.item);
+ return 1;
}
static void dump_port_state(fmtfn_t to, void *arg, erts_aint32_t state)
@@ -5100,7 +5104,7 @@ erts_port_resume_procs(Port *prt)
erts_snprintf(port_str, sizeof(DTRACE_CHARBUF_NAME(port_str)), "%T", prt->common.id);
while (plp2 != NULL) {
- erts_snprintf(pid_str, sizeof(DTRACE_CHARBUF_NAME(pid_str)), "%T", plp2->pid);
+ erts_snprintf(pid_str, sizeof(DTRACE_CHARBUF_NAME(pid_str)), "%T", plp2->u.pid);
DTRACE2(process_port_unblocked, pid_str, port_str);
}
}
@@ -6177,6 +6181,7 @@ int driver_output_binary(ErlDrvPort ix, char* hbuf, ErlDrvSizeT hlen,
dep,
conn_id,
(byte*) hbuf, hlen,
+ ErlDrvBinary2Binary(bin),
(byte*) (bin->orig_bytes+offs), len);
}
else
@@ -6222,12 +6227,14 @@ int driver_output2(ErlDrvPort ix, char* hbuf, ErlDrvSizeT hlen,
dep,
conn_id,
NULL, 0,
+ NULL,
(byte*) hbuf, hlen);
else
return erts_net_message(prt,
dep,
conn_id,
(byte*) hbuf, hlen,
+ NULL,
(byte*) buf, len);
}
else if (state & ERTS_PORT_SFLG_LINEBUF_IO)
diff --git a/erts/emulator/beam/msg_instrs.tab b/erts/emulator/beam/msg_instrs.tab
index 9bf3aefaca..6f8d1469ef 100644
--- a/erts/emulator/beam/msg_instrs.tab
+++ b/erts/emulator/beam/msg_instrs.tab
@@ -137,8 +137,8 @@ i_loop_rec(Dest) {
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)) {
+ SWAPOUT; /* erts_proc_sig_decode_dist() may write to heap... */
+ if (!erts_proc_sig_decode_dist(c_p, ERTS_PROC_LOCK_MAIN, msgp, 0)) {
/*
* A corrupt distribution message that we weren't able to decode;
* remove it...
diff --git a/erts/emulator/beam/utils.c b/erts/emulator/beam/utils.c
index 1e7b494a91..36a492f00d 100644
--- a/erts/emulator/beam/utils.c
+++ b/erts/emulator/beam/utils.c
@@ -2701,7 +2701,8 @@ Sint erts_cmp_compound(Eterm a, Eterm b, int exact, int eq_only)
if((AN)->sysname != (BN)->sysname) \
RETURN_NEQ(erts_cmp_atoms((AN)->sysname, (BN)->sysname)); \
ASSERT((AN)->creation != (BN)->creation); \
- RETURN_NEQ(((AN)->creation < (BN)->creation) ? -1 : 1); \
+ if ((AN)->creation != 0 && (BN)->creation != 0) \
+ RETURN_NEQ(((AN)->creation < (BN)->creation) ? -1 : 1); \
} \
} while (0)
@@ -3486,7 +3487,7 @@ store_external_or_ref_(Uint **hpp, ErlOffHeap* oh, Eterm ns)
if (is_external_header(*from_hp)) {
ExternalThing *etp = (ExternalThing *) from_hp;
ASSERT(is_external(ns));
- erts_refc_inc(&etp->node->refc, 2);
+ erts_ref_node_entry(etp->node, 2, make_boxed(to_hp));
}
else if (is_ordinary_ref_thing(from_hp))
return make_internal_ref(to_hp);
diff --git a/erts/emulator/hipe/hipe_native_bif.c b/erts/emulator/hipe/hipe_native_bif.c
index 211ce0492a..80e5d81023 100644
--- a/erts/emulator/hipe/hipe_native_bif.c
+++ b/erts/emulator/hipe/hipe_native_bif.c
@@ -579,7 +579,7 @@ Eterm hipe_check_get_msg(Process *c_p)
if (ERTS_SIG_IS_EXTERNAL_MSG(msgp)) {
/* FIXME: bump appropriate amount... */
- if (!erts_decode_dist_message(c_p, ERTS_PROC_LOCK_MAIN, msgp, 0)) {
+ if (!erts_proc_sig_decode_dist(c_p, ERTS_PROC_LOCK_MAIN, msgp, 0)) {
/*
* A corrupt distribution message that we weren't able to decode;
* remove it...
diff --git a/erts/emulator/sys/common/erl_check_io.c b/erts/emulator/sys/common/erl_check_io.c
index d413659f81..80e8030d74 100644
--- a/erts/emulator/sys/common/erl_check_io.c
+++ b/erts/emulator/sys/common/erl_check_io.c
@@ -44,7 +44,7 @@
#include "erl_time.h"
#if 0
-#define DEBUG_PRINT(FMT, ...) erts_printf(FMT "\r\n", ##__VA_ARGS__)
+#define DEBUG_PRINT(FMT, ...) do { erts_printf(FMT "\r\n", ##__VA_ARGS__); fflush(stdout); } while(0)
#define DEBUG_PRINT_FD(FMT, STATE, ...) \
DEBUG_PRINT("%d: " FMT " (ev=%s, ac=%s, flg=%s)", \
(STATE) ? (STATE)->fd : (ErtsSysFdType)-1, ##__VA_ARGS__, \
@@ -1061,7 +1061,7 @@ enif_select_x(ErlNifEnv* env,
ErtsDrvSelectDataState *free_select = NULL;
ErtsNifSelectDataState *free_nif = NULL;
- ASSERT(!resource->monitors);
+ ASSERT(!erts_dbg_is_resource_dying(resource));
#ifdef ERTS_SYS_CONTINOUS_FD_NUMBERS
if (!grow_drv_ev_state(fd)) {
@@ -1181,6 +1181,7 @@ enif_select_x(ErlNifEnv* env,
if (on) {
const Eterm recipient = pid ? pid->pid : env->proc->common.id;
+ ASSERT(is_internal_pid(recipient));
if (!state->driver.nif)
state->driver.nif = alloc_nif_select_data();
if (state->type == ERTS_EV_TYPE_NONE) {
diff --git a/erts/emulator/sys/common/erl_osenv.c b/erts/emulator/sys/common/erl_osenv.c
index 6a16377736..f055c5f854 100644
--- a/erts/emulator/sys/common/erl_osenv.c
+++ b/erts/emulator/sys/common/erl_osenv.c
@@ -167,9 +167,10 @@ void erts_osenv_init(erts_osenv_t *env) {
env->tree = NULL;
}
-static void destroy_foreach(env_rbtnode_t *node, void *_state) {
+static int destroy_foreach(env_rbtnode_t *node, void *_state, Sint reds) {
erts_free(ERTS_ALC_T_ENVIRONMENT, node);
(void)_state;
+ return 1;
}
void erts_osenv_clear(erts_osenv_t *env) {
@@ -182,7 +183,7 @@ struct __env_merge {
erts_osenv_t *env;
};
-static void merge_foreach(env_rbtnode_t *node, void *_state) {
+static int merge_foreach(env_rbtnode_t *node, void *_state, Sint reds) {
struct __env_merge *state = (struct __env_merge*)(_state);
env_rbtnode_t *existing_node;
@@ -191,6 +192,7 @@ static void merge_foreach(env_rbtnode_t *node, void *_state) {
if(existing_node == NULL || state->overwrite_existing) {
erts_osenv_put_native(state->env, &node->key, &node->value);
}
+ return 1;
}
void erts_osenv_merge(erts_osenv_t *env, const erts_osenv_t *with, int overwrite) {
@@ -208,7 +210,7 @@ struct __env_foreach_term {
void *user_state;
};
-static void foreach_term_wrapper(env_rbtnode_t *node, void *_state) {
+static int foreach_term_wrapper(env_rbtnode_t *node, void *_state, Sint reds) {
struct __env_foreach_term *state = (struct __env_foreach_term*)_state;
Eterm key, value;
@@ -218,6 +220,7 @@ static void foreach_term_wrapper(env_rbtnode_t *node, void *_state) {
node->value.length, (byte*)node->value.data);
state->user_callback(state->process, state->user_state, key, value);
+ return 1;
}
void erts_osenv_foreach_term(const erts_osenv_t *env, struct process *process,
@@ -314,10 +317,11 @@ struct __env_foreach_native {
void *user_state;
};
-static void foreach_native_wrapper(env_rbtnode_t *node, void *_state) {
+static int foreach_native_wrapper(env_rbtnode_t *node, void *_state, Sint reds) {
struct __env_foreach_native *state = (struct __env_foreach_native*)_state;
state->user_callback(state->user_state, &node->key, &node->value);
+ return 1;
}
void erts_osenv_foreach_native(const erts_osenv_t *env, void *state,
diff --git a/erts/emulator/sys/common/erl_poll.c b/erts/emulator/sys/common/erl_poll.c
index 27ffba58bd..c71d23f58c 100644
--- a/erts/emulator/sys/common/erl_poll.c
+++ b/erts/emulator/sys/common/erl_poll.c
@@ -872,8 +872,8 @@ update_pollset(ErtsPollSet *ps, int fd, ErtsPollOp op, ErtsPollEvents events)
}
}
-#if defined(EV_DISPATCH) && !defined(__OpenBSD__)
- /* If we have EV_DISPATCH we use it, unless we are on OpenBSD as the
+#if defined(EV_DISPATCH) && !(defined(__OpenBSD__) || defined(__NetBSD__))
+ /* If we have EV_DISPATCH we use it, unless we are on OpenBSD/NetBSD as the
behavior of EV_EOF seems to be edge triggered there and we need it
to be level triggered.
diff --git a/erts/emulator/test/distribution_SUITE.erl b/erts/emulator/test/distribution_SUITE.erl
index 885c66331c..4f70b51aa0 100644
--- a/erts/emulator/test/distribution_SUITE.erl
+++ b/erts/emulator/test/distribution_SUITE.erl
@@ -62,7 +62,12 @@
bad_dist_ext_control/1,
bad_dist_ext_connection_id/1,
bad_dist_ext_size/1,
- start_epmd_false/1, epmd_module/1]).
+ start_epmd_false/1, epmd_module/1,
+ bad_dist_fragments/1,
+ message_latency_large_message/1,
+ message_latency_large_link_exit/1,
+ message_latency_large_monitor_exit/1,
+ message_latency_large_exit2/1]).
%% Internal exports.
-export([sender/3, receiver2/2, dummy_waiter/0, dead_process/0,
@@ -90,7 +95,8 @@ all() ->
dist_parallel_send, atom_roundtrip, unicode_atom_roundtrip,
atom_roundtrip_r16b,
contended_atom_cache_entry, contended_unicode_atom_cache_entry,
- bad_dist_structure, {group, bad_dist_ext},
+ {group, message_latency},
+ {group, bad_dist}, {group, bad_dist_ext},
start_epmd_false, epmd_module].
groups() ->
@@ -100,10 +106,18 @@ groups() ->
{trap_bif, [], [trap_bif_1, trap_bif_2, trap_bif_3]},
{dist_auto_connect, [],
[dist_auto_connect_never, dist_auto_connect_once]},
+ {bad_dist, [],
+ [bad_dist_structure, bad_dist_fragments]},
{bad_dist_ext, [],
[bad_dist_ext_receive, bad_dist_ext_process_info,
bad_dist_ext_size,
- bad_dist_ext_control, bad_dist_ext_connection_id]}].
+ bad_dist_ext_control, bad_dist_ext_connection_id]},
+ {message_latency, [],
+ [message_latency_large_message,
+ message_latency_large_link_exit,
+ message_latency_large_monitor_exit,
+ message_latency_large_exit2]}
+ ].
%% Tests pinging a node in different ways.
ping(Config) when is_list(Config) ->
@@ -568,10 +582,20 @@ do_busy_test(Node, Fun) ->
%% Don't match arity; it is different in debug and
%% optimized emulator
[{status, suspended},
- {current_function, {erlang, bif_return_trap, _}}] = Pinfo,
+ {current_function, {Mod, Func, _}}] = Pinfo,
+ if
+ Mod =:= erlang andalso Func =:= bif_return_trap ->
+ true;
+ Mod =:= erts_internal andalso Func =:= dsend_continue_trap ->
+ true;
+ true ->
+ ct:fail({incorrect, pinfo, Pinfo})
+ end,
receive
{'DOWN', M, process, P, Reason} ->
io:format("~p died with exit reason ~p~n", [P, Reason]),
+ verify_nc(node()),
+ verify_nc(Node),
normal = Reason
end
end.
@@ -931,7 +955,9 @@ dist_auto_connect_never(Config) when is_list(Config) ->
ok;
{do_dist_auto_connect, Error} ->
{error, Error};
- Other ->
+ %% The io:formats in dos_dist_auto_connect will
+ %% generate port output messages that are ok
+ Other when not is_port(element(1, Other))->
{error, Other}
after 32000 ->
timeout
@@ -1364,6 +1390,131 @@ get_conflicting_unicode_atoms(CIX, N) ->
get_conflicting_unicode_atoms(CIX, N)
end.
+
+%% The message_latency_large tests that small distribution messages are
+%% not blocked by other large distribution messages. Basically it tests
+%% that fragmentation of distribution messages works.
+message_latency_large_message(Config) when is_list(Config) ->
+ measure_latency_large_message(?FUNCTION_NAME, fun(Dropper, Payload) -> Dropper ! Payload end).
+
+message_latency_large_exit2(Config) when is_list(Config) ->
+ measure_latency_large_message(?FUNCTION_NAME, fun erlang:exit/2).
+
+message_latency_large_link_exit(Config) when is_list(Config) ->
+ message_latency_large_exit(?FUNCTION_NAME, fun erlang:link/1).
+
+message_latency_large_monitor_exit(Config) when is_list(Config) ->
+ message_latency_large_exit(?FUNCTION_NAME, fun(Dropper) ->
+ Dropper ! {monitor, self()},
+ receive ok -> ok end
+ end).
+
+message_latency_large_exit(Nodename, ReasonFun) ->
+ measure_latency_large_message(
+ Nodename,
+ fun(Dropper, Payload) ->
+ Pid = spawn(fun() ->
+ receive go -> ok end,
+ ReasonFun(Dropper),
+ exit(Payload)
+ end),
+
+ FlushTrace = fun F() ->
+ receive
+ {trace, Pid, _, _} = M ->
+ F()
+ after 0 ->
+ ok
+ end
+ end,
+
+ erlang:trace(Pid, true, [exiting]),
+ Pid ! go,
+ receive
+ {trace, Pid, out_exited, 0} ->
+ FlushTrace()
+ end
+ end).
+
+measure_latency_large_message(Nodename, DataFun) ->
+
+ erlang:system_monitor(self(), [busy_dist_port]),
+
+ {ok, N} = start_node(Nodename),
+
+ Dropper = spawn(N, fun F() ->
+ process_flag(trap_exit, true),
+ receive
+ {monitor,Pid} ->
+ erlang:monitor(process, Pid),
+ Pid ! ok;
+ _ -> ok
+ end,
+ F()
+ end),
+
+ Echo = spawn(N, fun F() -> receive {From, Msg} -> From ! Msg, F() end end),
+
+ %% Test 32 MB and 320 MB and test the latency difference of sent messages
+ Payloads = [{I, <<0:(I * 32 * 1024 * 1024 * 8)>>} || I <- [1,10]],
+
+ IndexTimes = [{I, measure_latency(DataFun, Dropper, Echo, P)}
+ || {I, P} <- Payloads],
+
+ Times = [ Time || {_I, Time} <- IndexTimes],
+
+ ct:pal("~p",[IndexTimes]),
+
+ case {lists:max(Times), lists:min(Times)} of
+ {Max, Min} when Max * 0.25 > Min ->
+ ct:fail({incorrect_latency, IndexTimes});
+ _ ->
+ ok
+ end.
+
+measure_latency(DataFun, Dropper, Echo, Payload) ->
+
+ flush(),
+
+ Senders = [spawn_monitor(
+ fun F() ->
+ DataFun(Dropper, Payload),
+ receive
+ die -> ok
+ after 0 ->
+ F()
+ end
+ end) || _ <- lists:seq(1,2)],
+
+ [receive
+ {monitor, _Sender, busy_dist_port, _Info} = M ->
+ ok
+ end || _ <- lists:seq(1,10)],
+
+ {TS, _} =
+ timer:tc(fun() ->
+ [begin
+ Echo ! {self(), hello},
+ receive hello -> ok end
+ end || _ <- lists:seq(1,100)]
+ end),
+ [begin
+ Sender ! die,
+ receive
+ {'DOWN', Ref, process, _, _} ->
+ ok
+ end
+ end || {Sender, Ref} <- Senders],
+ TS.
+
+flush() ->
+ receive
+ _ ->
+ flush()
+ after 0 ->
+ ok
+ end.
+
-define(COOKIE, '').
-define(DOP_LINK, 1).
-define(DOP_SEND, 2).
@@ -1382,6 +1533,15 @@ get_conflicting_unicode_atoms(CIX, N) ->
-define(DOP_DEMONITOR_P, 20).
-define(DOP_MONITOR_P_EXIT, 21).
+-define(DOP_SEND_SENDER, 22).
+-define(DOP_SEND_SENDER_TT, 23).
+
+-define(DOP_PAYLOAD_EXIT, 24).
+-define(DOP_PAYLOAD_EXIT_TT, 25).
+-define(DOP_PAYLOAD_EXIT2, 26).
+-define(DOP_PAYLOAD_EXIT2_TT, 27).
+-define(DOP_PAYLOAD_MONITOR_P_EXIT, 28).
+
start_monitor(Offender,P) ->
Parent = self(),
Q = spawn(Offender,
@@ -1515,7 +1675,145 @@ bad_dist_structure(Config) when is_list(Config) ->
stop_node(Victim),
ok.
+%% Test various dist fragmentation errors
+bad_dist_fragments(Config) when is_list(Config) ->
+ ct:timetrap({seconds, 15}),
+
+ {ok, Offender} = start_node(bad_dist_fragment_offender),
+ {ok, Victim} = start_node(bad_dist_fragment_victim),
+
+ Msg = iolist_to_binary(dmsg_ext(lists:duplicate(255,255))),
+
+ start_node_monitors([Offender,Victim]),
+ Parent = self(),
+ P = spawn(Victim,
+ fun () ->
+ process_flag(trap_exit,true),
+ Parent ! {self(), started},
+ receive check_msgs -> ok end,
+ bad_dist_struct_check_msgs([one,
+ two]),
+ Parent ! {self(), messages_checked},
+ receive done -> ok end
+ end),
+ receive {P, started} -> ok end,
+ pong = rpc:call(Victim, net_adm, ping, [Offender]),
+ verify_up(Offender, Victim),
+ true = lists:member(Offender, rpc:call(Victim, erlang, nodes, [])),
+ start_monitor(Offender,P),
+ P ! one,
+
+ start_monitor(Offender,P),
+ send_bad_fragments(Offender, Victim, P,{?DOP_SEND,?COOKIE,P},3,
+ [{frg, 1, binary:part(Msg, 10,byte_size(Msg)-10)}]),
+ start_monitor(Offender,P),
+ send_bad_fragments(Offender, Victim, P,{?DOP_SEND,?COOKIE,P},3,
+ [{hdr, 3, binary:part(Msg, 0,10)},
+ {frg, 1, binary:part(Msg, 10,byte_size(Msg)-10)}]),
+
+ start_monitor(Offender,P),
+ send_bad_fragments(Offender, Victim, P,{?DOP_SEND,?COOKIE,P},3,
+ [{hdr, 3, binary:part(Msg, 0,10)},
+ {hdr, 3, binary:part(Msg, 0,10)}]),
+
+ start_monitor(Offender,P),
+ send_bad_fragments(Offender, Victim, P,{?DOP_SEND,?COOKIE,P,broken},3,
+ [{hdr, 1, binary:part(Msg, 10,byte_size(Msg)-10)}]),
+
+ start_monitor(Offender,P),
+ send_bad_fragments(Offender, Victim, P,{?DOP_SEND,?COOKIE,P},3,
+ [{hdr, 3, binary:part(Msg, 10,byte_size(Msg)-10)},
+ close]),
+
+ start_monitor(Offender,P),
+ ExitVictim = spawn(Victim, fun() -> receive ok -> ok end end),
+ send_bad_fragments(Offender, Victim, P,{?DOP_PAYLOAD_EXIT,P,ExitVictim},2,
+ [{hdr, 1, [131]}]),
+
+ start_monitor(Offender,P),
+ Exit2Victim = spawn(Victim, fun() -> receive ok -> ok end end),
+ send_bad_fragments(Offender, Victim, P,{?DOP_PAYLOAD_EXIT2,P,ExitVictim},2,
+ [{hdr, 1, [132]}]),
+
+ start_monitor(Offender,P),
+ DownVictim = spawn(Victim, fun() -> receive ok -> ok end end),
+ DownRef = erlang:monitor(process, DownVictim),
+ send_bad_fragments(Offender, Victim, P,{?DOP_PAYLOAD_MONITOR_P_EXIT,P,DownVictim,DownRef},2,
+ [{hdr, 1, [133]}]),
+
+ P ! two,
+ P ! check_msgs,
+ receive
+ {P, messages_checked} -> ok
+ after 5000 ->
+ exit(victim_is_dead)
+ end,
+
+ {message_queue_len, 0}
+ = rpc:call(Victim, erlang, process_info, [P, message_queue_len]),
+
+ unlink(P),
+ P ! done,
+ stop_node(Offender),
+ stop_node(Victim),
+ ok.
+
+dmsg_frag_hdr(Frag) ->
+ dmsg_frag_hdr(erlang:phash2(self()), Frag).
+dmsg_frag_hdr(Seq, Frag) ->
+ [131, $E, uint64_be(Seq), uint64_be(Frag), 0].
+
+dmsg_frag(Frag) ->
+ dmsg_frag(erlang:phash2(self()), Frag).
+dmsg_frag(Seq, Frag) ->
+ [131, $F, uint64_be(Seq), uint64_be(Frag)].
+
+send_bad_fragments(Offender,VictimNode,Victim,Ctrl,WhereToPutSelf,Fragments) ->
+ Parent = self(),
+ Done = make_ref(),
+ ct:pal("Send: ~p",[Fragments]),
+ spawn_link(Offender,
+ fun () ->
+ Node = node(Victim),
+ pong = net_adm:ping(Node),
+ erlang:monitor_node(Node, true),
+ DCtrl = dctrl(Node),
+ Ctrl1 = case WhereToPutSelf of
+ 0 ->
+ Ctrl;
+ N when N > 0 ->
+ setelement(N,Ctrl,self())
+ end,
+
+ FragData = [case Type of
+ hdr ->
+ [dmsg_frag_hdr(FragId),
+ dmsg_ext(Ctrl1), FragPayload];
+ frg ->
+ [dmsg_frag(FragId), FragPayload]
+ end || {Type, FragId, FragPayload} <- Fragments],
+
+ receive {nodedown, Node} -> exit("premature nodedown")
+ after 10 -> ok
+ end,
+
+ [ dctrl_send(DCtrl, D) || D <- FragData ],
+ [ erlang:port_close(DCtrl) || close <- Fragments],
+
+ receive {nodedown, Node} -> ok
+ after 5000 -> exit("missing nodedown")
+ end,
+ Parent ! {FragData,Done}
+ end),
+ receive
+ {WhatSent,Done} ->
+ io:format("Offender sent ~p~n",[WhatSent]),
+ verify_nc(VictimNode),
+ ok
+ after 7000 ->
+ exit(unable_to_send)
+ end.
bad_dist_ext_receive(Config) when is_list(Config) ->
{ok, Offender} = start_node(bad_dist_ext_receive_offender),
@@ -2124,8 +2422,25 @@ start_node(Config, Args, Rel) when is_list(Config), is_list(Rel) ->
start_node(Name, Args, Rel).
stop_node(Node) ->
+ verify_nc(Node),
test_server:stop_node(Node).
+verify_nc(Node) ->
+ P = self(),
+ Ref = make_ref(),
+ spawn(Node,
+ fun() ->
+ R = erts_test_utils:check_node_dist(fun(E) -> E end),
+ P ! {Ref, R}
+ end),
+ receive
+ {Ref, ok} ->
+ ok;
+ {Ref, Error} ->
+ ct:log("~s",[Error]),
+ ct:fail(failed_nc_refc_check)
+ end.
+
freeze_node(Node, MS) ->
Own = 300,
DoingIt = make_ref(),
@@ -2485,6 +2800,17 @@ mk_ref({NodeNameExt, Creation}, Numbers) when is_integer(Creation),
exit({unexpected_binary_to_term_result, Other})
end.
+uint64_be(Uint) when is_integer(Uint), 0 =< Uint, Uint < 1 bsl 64 ->
+ [(Uint bsr 56) band 16#ff,
+ (Uint bsr 48) band 16#ff,
+ (Uint bsr 40) band 16#ff,
+ (Uint bsr 32) band 16#ff,
+ (Uint bsr 24) band 16#ff,
+ (Uint bsr 16) band 16#ff,
+ (Uint bsr 8) band 16#ff,
+ Uint band 16#ff];
+uint64_be(Uint) ->
+ exit({badarg, uint64_be, [Uint]}).
uint32_be(Uint) when is_integer(Uint), 0 =< Uint, Uint < 1 bsl 32 ->
[(Uint bsr 24) band 16#ff,
diff --git a/erts/emulator/test/erts_test_utils.erl b/erts/emulator/test/erts_test_utils.erl
index ac2f2435be..0c3ef3e0fc 100644
--- a/erts/emulator/test/erts_test_utils.erl
+++ b/erts/emulator/test/erts_test_utils.erl
@@ -27,6 +27,7 @@
-export([mk_ext_pid/3,
mk_ext_port/2,
mk_ext_ref/2,
+ available_internal_state/1,
check_node_dist/0, check_node_dist/1, check_node_dist/3]).
@@ -157,6 +158,21 @@ mk_ext_ref({NodeName, Creation}, Numbers) when is_list(NodeName),
end.
+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.
+
%%
%% Check reference counters for node- and dist entries.
@@ -168,16 +184,21 @@ check_node_dist() ->
end).
check_node_dist(Fail) ->
+ AIS = available_internal_state(true),
+ [erlang:garbage_collect(P) || P <- erlang:processes()],
{{node_references, NodeRefs},
{dist_references, DistRefs}} =
erts_debug:get_internal_state(node_and_dist_references),
- check_node_dist(Fail, NodeRefs, DistRefs).
-
-
+ R = check_node_dist(Fail, NodeRefs, DistRefs),
+ available_internal_state(AIS),
+ R.
check_node_dist(Fail, NodeRefs, DistRefs) ->
- check_nd_refc({node(),erlang:system_info(creation)},
- NodeRefs, DistRefs, Fail).
+ AIS = available_internal_state(true),
+ R = check_nd_refc({node(),erlang:system_info(creation)},
+ NodeRefs, DistRefs, Fail),
+ available_internal_state(AIS),
+ R.
check_nd_refc({ThisNodeName, ThisCreation}, NodeRefs, DistRefs, Fail) ->
diff --git a/erts/emulator/test/nif_SUITE.erl b/erts/emulator/test/nif_SUITE.erl
index 0d0930e124..4a0ad9c1d5 100644
--- a/erts/emulator/test/nif_SUITE.erl
+++ b/erts/emulator/test/nif_SUITE.erl
@@ -64,7 +64,8 @@
nif_phash2/1,
nif_whereis/1, nif_whereis_parallel/1,
nif_whereis_threaded/1, nif_whereis_proxy/1,
- nif_ioq/1
+ nif_ioq/1,
+ pid/1
]).
-export([many_args_100/100]).
@@ -103,7 +104,8 @@ all() ->
nif_internal_hash_salted,
nif_phash2,
nif_whereis, nif_whereis_parallel, nif_whereis_threaded,
- nif_ioq].
+ nif_ioq,
+ pid].
groups() ->
[{G, [], api_repeaters()} || G <- api_groups()]
@@ -822,8 +824,11 @@ demonitor_process(Config) ->
end),
R_ptr = alloc_monitor_resource_nif(),
{0,MonBin1} = monitor_process_nif(R_ptr, Pid, true, self()),
+ MonTerm1 = make_monitor_term_nif(MonBin1),
[R_ptr] = monitored_by(Pid),
{0,MonBin2} = monitor_process_nif(R_ptr, Pid, true, self()),
+ MonTerm2 = make_monitor_term_nif(MonBin2),
+ true = (MonTerm1 =/= MonTerm2),
[R_ptr, R_ptr] = monitored_by(Pid),
0 = demonitor_process_nif(R_ptr, MonBin1),
[R_ptr] = monitored_by(Pid),
@@ -837,6 +842,10 @@ demonitor_process(Config) ->
{R_ptr, _, 1} = last_resource_dtor_call(),
[] = monitored_by(Pid),
Pid ! return,
+
+ erlang:garbage_collect(),
+ true = (MonTerm1 =/= MonTerm2),
+ io:format("MonTerm1 = ~p\nMonTerm2 = ~p\n", [MonTerm1, MonTerm2]),
ok.
@@ -1208,6 +1217,15 @@ maps(Config) when is_list(Config) ->
M2 = maps_from_list_nif(maps:to_list(M2)),
M3 = maps_from_list_nif(maps:to_list(M3)),
+ %% Test different map sizes (OTP-15567)
+ repeat_while(fun({35,_}) -> false;
+ ({K,Map}) ->
+ Map = maps_from_list_nif(maps:to_list(Map)),
+ Map = maps:filter(fun(K,V) -> V =:= K*100 end, Map),
+ {K+1, maps:put(K,K*100,Map)}
+ end,
+ {1,#{}}),
+
has_duplicate_keys = maps_from_list_nif([{1,1},{1,1}]),
verify_tmpmem(TmpMem),
@@ -2504,6 +2522,13 @@ repeat(0, _, Arg) ->
repeat(N, Fun, Arg0) ->
repeat(N-1, Fun, Fun(Arg0)).
+repeat_while(Fun, Acc0) ->
+ case Fun(Acc0) of
+ false -> ok;
+ Acc1 ->
+ repeat_while(Fun, Acc1)
+ end.
+
check(Exp,Got,Line) ->
case Got of
Exp -> Exp;
@@ -3344,6 +3369,30 @@ make_unaligned_binary(Bin0) ->
<<0:3,Bin:Size/binary,31:5>> = id(<<0:3,Bin0/binary,31:5>>),
Bin.
+pid(Config) ->
+ ensure_lib_loaded(Config),
+ Self = self(),
+ {true, ErlNifPid} = get_local_pid_nif(Self),
+ false = is_pid_undefined_nif(ErlNifPid),
+ Self = make_pid_nif(ErlNifPid),
+
+ UndefPid = set_pid_undefined_nif(),
+ true = is_pid_undefined_nif(UndefPid),
+ undefined = make_pid_nif(UndefPid),
+ 0 = send_term(UndefPid, message),
+
+ Other = spawn(fun() -> ok end),
+ {true,OtherNifPid} = get_local_pid_nif(Other),
+ Cmp = compare_pids_nif(ErlNifPid, OtherNifPid),
+ true = if Cmp < 0 -> Self < Other;
+ Cmp > 0 -> Self > Other
+ end,
+ 0 = compare_pids_nif(ErlNifPid, ErlNifPid),
+
+ {false, _} = get_local_pid_nif(undefined),
+ ok.
+
+
id(I) -> I.
%% The NIFs:
@@ -3421,6 +3470,7 @@ alloc_monitor_resource_nif() -> ?nif_stub.
monitor_process_nif(_,_,_,_) -> ?nif_stub.
demonitor_process_nif(_,_) -> ?nif_stub.
compare_monitors_nif(_,_) -> ?nif_stub.
+make_monitor_term_nif(_) -> ?nif_stub.
monitor_frenzy_nif(_,_,_,_) -> ?nif_stub.
ioq_nif(_) -> ?nif_stub.
ioq_nif(_,_) -> ?nif_stub.
@@ -3451,5 +3501,11 @@ convert_time_unit(_,_,_) -> ?nif_stub.
now_time() -> ?nif_stub.
cpu_time() -> ?nif_stub.
+get_local_pid_nif(_) -> ?nif_stub.
+make_pid_nif(_) -> ?nif_stub.
+set_pid_undefined_nif() -> ?nif_stub.
+is_pid_undefined_nif(_) -> ?nif_stub.
+compare_pids_nif(_, _) -> ?nif_stub.
+
nif_stub_error(Line) ->
exit({nif_not_loaded,module,?MODULE,line,Line}).
diff --git a/erts/emulator/test/nif_SUITE_data/nif_SUITE.c b/erts/emulator/test/nif_SUITE_data/nif_SUITE.c
index 21af4b05b3..0d5d900d31 100644
--- a/erts/emulator/test/nif_SUITE_data/nif_SUITE.c
+++ b/erts/emulator/test/nif_SUITE_data/nif_SUITE.c
@@ -1882,12 +1882,23 @@ static ERL_NIF_TERM copy_blob(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[
return enif_make_copy(env, mti.p->blob);
}
+static int get_pidbin(ErlNifEnv* env, ERL_NIF_TERM pidbin, ErlNifPid* pid)
+{
+ ErlNifBinary bin;
+
+ if (!enif_inspect_binary(env, pidbin, &bin) || bin.size != sizeof(ErlNifPid))
+ return 0;
+
+ memcpy(pid, bin.data, bin.size);
+ return 1;
+}
+
static ERL_NIF_TERM send_term(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
ErlNifEnv* menv;
ErlNifPid pid;
int ret;
- if (!enif_get_local_pid(env, argv[0], &pid)) {
+ if (!enif_get_local_pid(env, argv[0], &pid) && !get_pidbin(env, argv[0], &pid)) {
return enif_make_badarg(env);
}
menv = enif_alloc_env();
@@ -2847,6 +2858,16 @@ static ERL_NIF_TERM compare_monitors_nif(ErlNifEnv* env, int argc, const ERL_NIF
return enif_make_int(env, enif_compare_monitors(&m1, &m2));
}
+static ERL_NIF_TERM make_monitor_term_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
+{
+ ErlNifMonitor m;
+ if (!get_monitor(env, argv[0], &m)) {
+ return enif_make_badarg(env);
+ }
+
+ return enif_make_monitor_term(env, &m);
+}
+
/*********** monitor_frenzy ************/
@@ -3503,6 +3524,65 @@ static ERL_NIF_TERM ioq(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
return enif_make_badarg(env);
}
+static ERL_NIF_TERM make_bool(ErlNifEnv* env, int bool)
+{
+ return bool ? atom_true : atom_false;
+}
+
+static ERL_NIF_TERM get_local_pid_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
+{
+ ErlNifPid pid;
+ ERL_NIF_TERM pid_bin;
+ int ret = enif_get_local_pid(env, argv[0], &pid);
+
+ memcpy(enif_make_new_binary(env, sizeof(ErlNifPid), &pid_bin),
+ &pid, sizeof(ErlNifPid));
+
+ return enif_make_tuple2(env, make_bool(env, ret), pid_bin);
+}
+
+static ERL_NIF_TERM make_pid_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
+{
+ ErlNifPid pid;
+
+ if (!get_pidbin(env, argv[0], &pid))
+ return enif_make_badarg(env);
+
+ return enif_make_pid(env, &pid);
+}
+
+static ERL_NIF_TERM set_pid_undefined_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
+{
+ ErlNifPid pid;
+ ERL_NIF_TERM pid_bin;
+
+ enif_set_pid_undefined(&pid);
+ memcpy(enif_make_new_binary(env, sizeof(ErlNifPid), &pid_bin),
+ &pid, sizeof(ErlNifPid));
+
+ return pid_bin;
+}
+
+static ERL_NIF_TERM is_pid_undefined_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
+{
+ ErlNifPid pid;
+
+ if (!get_pidbin(env, argv[0], &pid))
+ return enif_make_badarg(env);
+
+ return make_bool(env, enif_is_pid_undefined(&pid));
+}
+
+static ERL_NIF_TERM compare_pids_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
+{
+ ErlNifPid a, b;
+
+ if (!get_pidbin(env, argv[0], &a) || !get_pidbin(env, argv[1], &b))
+ return enif_make_badarg(env);
+
+ return enif_make_int(env, enif_compare_pids(&a, &b));
+}
+
static ErlNifFunc nif_funcs[] =
{
{"lib_version", 0, lib_version},
@@ -3596,6 +3676,7 @@ static ErlNifFunc nif_funcs[] =
{"monitor_process_nif", 4, monitor_process_nif},
{"demonitor_process_nif", 2, demonitor_process_nif},
{"compare_monitors_nif", 2, compare_monitors_nif},
+ {"make_monitor_term_nif", 1, make_monitor_term_nif},
{"monitor_frenzy_nif", 4, monitor_frenzy_nif},
{"whereis_send", 3, whereis_send},
{"whereis_term", 2, whereis_term},
@@ -3604,7 +3685,12 @@ static ErlNifFunc nif_funcs[] =
{"ioq_nif", 1, ioq},
{"ioq_nif", 2, ioq},
{"ioq_nif", 3, ioq},
- {"ioq_nif", 4, ioq}
+ {"ioq_nif", 4, ioq},
+ {"get_local_pid_nif", 1, get_local_pid_nif},
+ {"make_pid_nif", 1, make_pid_nif},
+ {"set_pid_undefined_nif", 0, set_pid_undefined_nif},
+ {"is_pid_undefined_nif", 1, is_pid_undefined_nif},
+ {"compare_pids_nif", 2, compare_pids_nif}
};
ERL_NIF_INIT(nif_SUITE,nif_funcs,load,NULL,upgrade,unload)
diff --git a/erts/emulator/test/node_container_SUITE.erl b/erts/emulator/test/node_container_SUITE.erl
index b3d8f9584d..ef4635a6f5 100644
--- a/erts/emulator/test/node_container_SUITE.erl
+++ b/erts/emulator/test/node_container_SUITE.erl
@@ -71,25 +71,10 @@ init_per_suite(Config) ->
end_per_suite(_Config) ->
erts_debug:set_internal_state(available_internal_state, true),
erts_debug:set_internal_state(node_tab_delayed_delete, -1), %% restore original value
- available_internal_state(false).
-
-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.
+ erts_test_utils:available_internal_state(false).
init_per_testcase(_Case, Config) when is_list(Config) ->
- available_internal_state(true),
+ erts_test_utils:available_internal_state(true),
Config.
end_per_testcase(_Case, Config) when is_list(Config) ->
@@ -928,9 +913,9 @@ id(X) ->
-define(ND_REFS, erts_debug:get_internal_state(node_and_dist_references)).
node_container_refc_check(Node) when is_atom(Node) ->
- AIS = available_internal_state(true),
+ AIS = erts_test_utils:available_internal_state(true),
nc_refc_check(Node),
- available_internal_state(AIS).
+ erts_test_utils:available_internal_state(AIS).
nc_refc_check(Node) when is_atom(Node) ->
Ref = make_ref(),
diff --git a/erts/emulator/test/persistent_term_SUITE.erl b/erts/emulator/test/persistent_term_SUITE.erl
index 58038e24b7..93eb026ced 100644
--- a/erts/emulator/test/persistent_term_SUITE.erl
+++ b/erts/emulator/test/persistent_term_SUITE.erl
@@ -6,7 +6,7 @@
%% 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
-%5
+%%
%% http://www.apache.org/licenses/LICENSE-2.0
%%
%% Unless required by applicable law or agreed to in writing, software
@@ -60,7 +60,8 @@ basic(_Config) ->
Key = {?MODULE,{key,I}},
true = persistent_term:erase(Key),
false = persistent_term:erase(Key),
- {'EXIT',{badarg,_}} = (catch persistent_term:get(Key))
+ {'EXIT',{badarg,_}} = (catch persistent_term:get(Key)),
+ {not_present,Key} = persistent_term:get(Key, {not_present,Key})
end || I <- Seq],
[] = [P || {{?MODULE,_},_}=P <- pget(Chk)],
chk(Chk).
diff --git a/erts/etc/common/heart.c b/erts/etc/common/heart.c
index bd218ff725..bb843a616b 100644
--- a/erts/etc/common/heart.c
+++ b/erts/etc/common/heart.c
@@ -500,7 +500,7 @@ message_loop(erlin_fd, erlout_fd)
#if defined(__WIN32__)
static void
-kill_old_erlang(void){
+kill_old_erlang(int reason){
HANDLE erlh;
DWORD exit_code;
char* envvar = NULL;
@@ -536,7 +536,8 @@ kill_old_erlang(void){
}
#else
static void
-kill_old_erlang(void){
+kill_old_erlang(int reason)
+{
pid_t pid;
int i, res;
int sig = SIGKILL;
@@ -546,14 +547,25 @@ kill_old_erlang(void){
if (envvar && strcmp(envvar, "TRUE") == 0)
return;
- envvar = get_env(HEART_KILL_SIGNAL);
- if (envvar && strcmp(envvar, "SIGABRT") == 0) {
- print_error("kill signal SIGABRT requested");
- sig = SIGABRT;
- }
-
if(heart_beat_kill_pid != 0){
- pid = (pid_t) heart_beat_kill_pid;
+ pid = (pid_t) heart_beat_kill_pid;
+ if (reason == R_CLOSED) {
+ print_error("Wait 5 seconds for Erlang to terminate nicely");
+ for (i=0; i < 5; ++i) {
+ res = kill(pid, 0); /* check if alive */
+ if (res < 0 && errno == ESRCH)
+ return;
+ sleep(1);
+ }
+ print_error("Erlang still alive, kill it");
+ }
+
+ envvar = get_env(HEART_KILL_SIGNAL);
+ if (envvar && strcmp(envvar, "SIGABRT") == 0) {
+ print_error("kill signal SIGABRT requested");
+ sig = SIGABRT;
+ }
+
res = kill(pid,sig);
for(i=0; i < 5 && res == 0; ++i){
sleep(1);
@@ -677,7 +689,7 @@ do_terminate(int erlin_fd, int reason) {
if(!command)
print_error("Would reboot. Terminating.");
else {
- kill_old_erlang();
+ kill_old_erlang(reason);
/* High prio combined with system() works badly indeed... */
SetPriorityClass(GetCurrentProcess(), NORMAL_PRIORITY_CLASS);
win_system(command);
@@ -685,7 +697,7 @@ do_terminate(int erlin_fd, int reason) {
}
free_env_val(command);
} else {
- kill_old_erlang();
+ kill_old_erlang(reason);
/* High prio combined with system() works badly indeed... */
SetPriorityClass(GetCurrentProcess(), NORMAL_PRIORITY_CLASS);
win_system(&cmd[0]);
@@ -697,13 +709,13 @@ do_terminate(int erlin_fd, int reason) {
if(!command)
print_error("Would reboot. Terminating.");
else {
- kill_old_erlang();
+ kill_old_erlang(reason);
ret = system(command);
print_error("Executed \"%s\" -> %d. Terminating.",command, ret);
}
free_env_val(command);
} else {
- kill_old_erlang();
+ kill_old_erlang(reason);
ret = system((char*)&cmd[0]);
print_error("Executed \"%s\" -> %d. Terminating.",cmd, ret);
}
diff --git a/erts/etc/unix/Makefile b/erts/etc/unix/Makefile
index 83c64d35fd..21a725cb88 100644
--- a/erts/etc/unix/Makefile
+++ b/erts/etc/unix/Makefile
@@ -30,7 +30,8 @@ opt debug lcnt: etc
etc: etp-commands
etp-commands: etp-commands.in
- $(gen_verbose)sed 's:@ERL_TOP@:${ERL_TOP}:g' etp-commands.in > etp-commands
+ $(gen_verbose)sed -e 's:@ERL_TOP@:${ERL_TOP}:g' \
+ etp-commands.in > etp-commands
.PHONY: docs
docs:
diff --git a/erts/etc/unix/cerl.src b/erts/etc/unix/cerl.src
index 2e034513b0..bcd64d242e 100644
--- a/erts/etc/unix/cerl.src
+++ b/erts/etc/unix/cerl.src
@@ -224,7 +224,13 @@ while [ $# -gt 0 ]; do
shift
cargs="$cargs -rr"
run_rr=yes
- skip_erlexec=yes
+ case "$1" in
+ "replay"|"ps")
+ ;;
+ *)
+ skip_erlexec=yes
+ ;;
+ esac
;;
*)
break
@@ -307,7 +313,26 @@ if [ "x$GDB" = "x" ]; then
exec $taskset1 valgrind $valgrind_xml $valgrind_log $valgrind_misc_flags $BINDIR/$EMU_NAME $sched_arg $emu_xargs "$@"
elif [ $run_rr = yes ]; then
- exec rr record --ignore-nested $BINDIR/$EMU_NAME $emu_xargs "$@"
+ if [ $1 = replay ]; then
+ shift
+ cmdfile="/tmp/.cerlgdb.$$"
+ echo "set \$etp_beam_executable = \"$BINDIR/$EMU_NAME\"" > $cmdfile
+ if [ "$1" = "-p" ]; then
+ echo 'set $etp_rr_run_until_beam = 1' >> $cmdfile
+ fi
+ cat $ROOTDIR/erts/etc/unix/etp-commands.in >> $cmdfile
+ exec rr replay -x $cmdfile $*
+ elif [ $1 = ps ]; then
+ shift
+ rr ps $* | head -1
+ ChildSetup=`rr ps $* | grep 'erl_child_setup' | awk '{ print $2 }'`
+ for CS in $ChildSetup; do
+ rr ps $* | grep -E "^$CS"
+ done
+ exit 0
+ else
+ exec rr record --ignore-nested $BINDIR/$EMU_NAME $emu_xargs "$@"
+ fi
else
exec $EXEC $xargs ${1+"$@"}
fi
diff --git a/erts/etc/unix/etp-commands.in b/erts/etc/unix/etp-commands.in
index b12a205ba7..e8dc59156f 100644
--- a/erts/etc/unix/etp-commands.in
+++ b/erts/etc/unix/etp-commands.in
@@ -149,7 +149,7 @@ define etp-1
else
# (($arg0) & 0x3) == 0
if (($arg0) == etp_the_non_value)
- printf "<the non-value>"
+ printf "<the-non-value>"
else
etp-cp-1 ($arg0)
end
@@ -1241,7 +1241,7 @@ define etp-sig-int
if $etp_sig_tag != etp_the_non_value
etp-1 $etp_sig_tag 0
else
- print "!ENCODED-DIST-MSG"
+ printf "!ENCODED-DIST-MSG"
end
if ($arg0)->m[1] != $etp_nil
printf " @token= "
@@ -1251,7 +1251,7 @@ define etp-sig-int
etp-1 ($arg0)->m[2] 0
else
if ($etp_sig_tag & 0x3f) != 0x30
- print "!INVALID-SIGNAL"
+ printf "!INVALID-SIGNAL"
else
set $etp_sig_op = (($etp_sig_tag >> 6) & 0xff)
set $etp_sig_type = (($etp_sig_tag >> 14) & 0xff)
@@ -1789,7 +1789,7 @@ define etp-pix2proc
# Args: Eterm
#
set $proc = (Process *) *((UWord *) &erts_proc.r.o.tab[((int) $arg0)])
- printf "(Process *) %p\n", $proc
+ printf "(Process*)%p\n", $proc
end
define etp-pid2proc-1
@@ -1803,7 +1803,7 @@ define etp-pid2proc
# Args: Eterm
#
etp-pid2proc-1 $arg0
- printf "(Process *) %p\n", $proc
+ printf "(Process*)%p\n", $proc
end
define etp-proc-state-int
@@ -2083,7 +2083,7 @@ define etp-process-info-int
printf "\n Flags: "
etp-proc-flags $etp_proc
if $proxy_process != 0
- printf " Pointer: (Process *) %p\n", $etp_proc
+ printf " Pointer: (Process*)%p\n", $etp_proc
printf " *** PROXY process struct *** refer to: \n"
etp-pid2proc-1 $etp_proc->common.id
etp-process-info $proc
@@ -2096,7 +2096,7 @@ define etp-process-info-int
end
end
printf " Current function: "
- if ($etp_proc->current)
+ if ($etp_proc->current && !($etp_proc->state.counter & 0x800))
etp-1 $etp_proc->current->module
printf ":"
etp-1 $etp_proc->current->function
@@ -2133,7 +2133,7 @@ define etp-process-info-int
end
printf " Parent: "
etp-1 ((Eterm)($etp_proc->parent))
- printf "\n Pointer: (Process *) %p\n", $etp_proc
+ printf "\n Pointer: (Process*)%p\n", $etp_proc
end
if ($arg1)
etp-sigqs $etp_proc
@@ -2156,6 +2156,43 @@ document etp-process-info
%---------------------------------------------------------------------------
end
+define etp-processes-free-runq-int
+ set $runq_prio = 0
+ while $runq_prio < 3
+ set $runq_proc = ($arg0)->procs.prio[$runq_prio].first
+ while $runq_proc != 0
+ if $runq_proc->state.counter & 0x400
+ printf "---\n"
+ printf " Pix: FREE -> run_queue %p\n", ($arg0)
+ etp-process-info-int $runq_proc ($arg1)
+ end
+ set $runq_proc = $runq_proc->next
+ end
+ set $runq_prio++
+ end
+end
+
+define etp-processes-free-de-int
+ set $de_ix = 0
+ while $de_ix < ($arg1)
+ set $de = ($arg0)+$de_ix
+ set $susp = $de->suspended
+ set $susp_curr = $susp
+ set $first_loop = 1
+ while $susp_curr != 0 && (($susp_curr != $susp) || $first_loop)
+ if ($susp_curr->u.pid & 0x3) == 0
+ printf "---\n"
+ printf " Pix: FREE "
+ etp $de->sysname
+ etp-process-info-int $susp_curr->u.pid ($arg2)
+ end
+ set $first_loop = 0
+ set $susp_curr = $susp_curr->next
+ end
+ set $de_ix++
+ end
+end
+
define etp-processes-int
if (!erts_initialized)
printf "No processes, since system isn't initialized!\n"
@@ -2176,11 +2213,36 @@ define etp-processes-int
set $proc_cnt--
end
if $proc_ix == $proc_printile
- printf "--- %d%% (%d / %d) searched\n", $proc_printile / $proc_decentile * 10, $proc_ix, $proc_max_ix
+ printf "--- %d%% (%d / %d) searched, looking for %d more\n", $proc_printile / $proc_decentile * 10, $proc_ix, $proc_max_ix, $proc_cnt
set $proc_printile += $proc_decentile
end
set $proc_ix++
end
+
+ ## We should also check for any FREE processes that are running
+ ## They can be found in esdp->current_process, dep->suspendees and
+ ## runq. Running FREE processes are processes that either are yielding
+ ## when exiting or running on a dirty scheduler while having exited.
+ set $sched_ix = 0
+ while $sched_ix < erts_no_schedulers
+ set $sched_data = &erts_aligned_scheduler_data[$sched_ix].esd
+ if $sched_data->current_process != 0
+ if $sched_data->current_process.state.counter & 0x400
+ printf "---\n"
+ printf " Pix: FREE -> scheduler %d\n", $sched_ix+1
+ etp-process-info-int $sched_data->current_process ($arg0)
+ end
+ end
+ etp-processes-free-runq-int $sched_data->run_queue ($arg0)
+ set $sched_ix++
+ end
+ etp-processes-free-runq-int &erts_aligned_run_queues[erts_no_run_queues].runq ($arg0)
+ etp-processes-free-runq-int &erts_aligned_run_queues[erts_no_run_queues+1].runq ($arg0)
+ etp-processes-free-de-int erts_hidden_dist_entries erts_no_of_hidden_dist_entries ($arg0)
+ etp-processes-free-de-int erts_visible_dist_entries erts_no_of_visible_dist_entries ($arg0)
+ etp-processes-free-de-int erts_pending_dist_entries erts_no_of_pending_dist_entries ($arg0)
+ etp-processes-free-de-int erts_not_connected_dist_entries erts_no_of_not_connected_dist_entries ($arg0)
+ etp-processes-free-de-int erts_this_dist_entry 1 ($arg0)
printf "---\n",
end
end
@@ -2237,9 +2299,9 @@ define etp-process-memory-info
end
printf " "
etp-1 $etp_pmem_proc->common.id
- printf ": (Process *) %p ", $etp_pmem_proc
+ printf ": (Process*)%p ", $etp_pmem_proc
if $proxy_process != 0
- printf "(Process *) %p ", $etp_pmem_proc
+ printf "(Process*)%p ", $etp_pmem_proc
printf " *** PROXY process struct *** refer to next: \n"
etp-pid2proc-1 $etp_pmem_proc->common.id
printf " -"
@@ -2313,7 +2375,7 @@ define etp-pix2port
# Args: Eterm
#
set $port = (Port *) *((UWord *) &erts_port.r.o.tab[((int) $arg0)])
- printf "(Port *) %p\n", $port
+ printf "(Port*)%p\n", $port
end
define etp-id2port-1
@@ -2327,7 +2389,7 @@ define etp-id2port
# Args: Eterm
#
etp-id2port-1 $arg0
- printf "(Port *) %p\n", $port
+ printf "(Port*)%p\n", $port
end
define etp-port-sched-flags-int
@@ -2501,7 +2563,7 @@ define etp-port-info
printf " Connected: "
set $connected = *(((Eterm *) &(((Port *) $etp_pinfo_port)->connected)))
etp-1 $connected
- printf "\n Pointer: (Port *) %p\n", $etp_pinfo_port
+ printf "\n Pointer: (Port*)%p\n", $etp_pinfo_port
end
document etp-port-info
@@ -2851,7 +2913,7 @@ define etp-scheduler-info-internal
printf " Sleep Info Flags:"
set $ssi_flags = *((Uint32 *) &$sched_data->ssi->flags)
etp-ssi-flags $ssi_flags
- printf " Pointer: (ErtsSchedulerData *) %p\n", $sched_data
+ printf " Pointer: (ErtsSchedulerData*)%p\n", $sched_data
end
define etp-run-queue-info-internal
@@ -2884,7 +2946,7 @@ define etp-run-queue-info-internal
end
set $rq_flags = *((Uint32 *) &($runq->flags))
etp-rq-flags-int $rq_flags
- printf " Pointer: (ErtsRunQueue *) %p\n", $runq
+ printf " Pointer: (ErtsRunQueue*)%p\n", $runq
end
define etp-fds
@@ -2961,7 +3023,7 @@ define etp-timer-wheel
printf "\n"
while 1
printf "- Timeout pos: %ld\n", $tmr->timeout_pos
- printf " Pointer: (ErtsTWheelTimer *) %p\n", $tmr
+ printf " Pointer: (ErtsTWheelTimer*)%p\n", $tmr
set $tmr = $tmr->next
if ($tmr == $tiw->w[$ix])
loop_break
@@ -2991,7 +3053,7 @@ define etp-timer-wheel
printf "\n"
while 1
printf "- Timeout pos: %ld\n", $tmr->timeout_pos
- printf " Pointer: (ErtsTWheelTimer *) %p\n", $tmr
+ printf " Pointer: (ErtsTWheelTimer*)%p\n", $tmr
set $tmr = $tmr->next
if ($tmr == $tiw->w[$ix])
loop_break
@@ -4326,6 +4388,20 @@ document etp-show
%---------------------------------------------------------------------------
end
+define etp-rr-run-until-beam
+ source @ERL_TOP@/erts/etc/unix/etp-rr-run-until-beam.py
+end
+
+document etp-rr-run-until-beam
+%---------------------------------------------------------------------------
+% etp-rr-run-until-beam
+%
+% Use this gdb macro to make cerl -rr replay -p PID walk until
+% the correct execute has been made. You may have to change the
+% file that is used to debug with.
+%---------------------------------------------------------------------------
+end
+
############################################################################
# Init
#
@@ -4363,7 +4439,13 @@ define hook-run
set $_exitsignal = -1
end
+handle SIGPIPE nostop
+
etp-init
help etp-init
-etp-show
-etp-system-info
+if $etp_rr_run_until_beam
+ help etp-rr-run-until-beam
+else
+ etp-show
+ etp-system-info
+end
diff --git a/erts/etc/unix/etp-rr-run-until-beam.py b/erts/etc/unix/etp-rr-run-until-beam.py
new file mode 100644
index 0000000000..078998b910
--- /dev/null
+++ b/erts/etc/unix/etp-rr-run-until-beam.py
@@ -0,0 +1,45 @@
+#
+# %CopyrightBegin%
+#
+# Copyright Ericsson AB 2013-2016. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# 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%
+#
+
+has_exited = False
+
+def stop_handler (event):
+ global has_exited
+ if isinstance(event, gdb.SignalEvent):
+ print("exit code: %s" % (event.stop_signal))
+ has_exited = True
+
+gdb.events.stop.connect (stop_handler)
+
+gdb.execute('continue')
+
+while not has_exited:
+ r = gdb.execute('when', to_string=True)
+ m = re.match("[^0-9]*([0-9]+)", r)
+ if m:
+ event = int(m.group(1));
+ gdb.execute('start ' + str(event + 1));
+ gdb.execute('continue')
+
+gdb.events.stop.disconnect (stop_handler)
+
+gdb.execute('file ' + str(gdb.parse_and_eval("$etp_beam_executable")))
+gdb.execute('break main')
+gdb.execute('reverse-continue')
diff --git a/erts/etc/win32/msys_tools/vc/ld.sh b/erts/etc/win32/msys_tools/vc/ld.sh
index 8917251f51..22184faf76 100644
--- a/erts/etc/win32/msys_tools/vc/ld.sh
+++ b/erts/etc/win32/msys_tools/vc/ld.sh
@@ -178,7 +178,10 @@ if [ "$RES" = "0" -a -f "$CMANIFEST" ]; then
RES=$?
if [ "$RES" != "0" ]; then
REMOVE=`echo "$OUTPUTRES" | sed 's,\\\;[12]$,,g'`
- CREMOVE=`cygpath $REMOVE`
+ CREMOVE=`win2msys_path.sh $REMOVE`
+ ## If Defender or Search are enabled, they will lock just created files
+ ## and then mt will fail :/
+ echo "If you get this error, make sure Windows Defender AND Windows Search is disabled">>/tmp/link.exe.${p}.1
rm -f "$CREMOVE"
fi
rm -f "$CMANIFEST"
diff --git a/erts/preloaded/ebin/persistent_term.beam b/erts/preloaded/ebin/persistent_term.beam
index e94ef983be..c882e4fad4 100644
--- a/erts/preloaded/ebin/persistent_term.beam
+++ b/erts/preloaded/ebin/persistent_term.beam
Binary files differ
diff --git a/erts/preloaded/ebin/prim_inet.beam b/erts/preloaded/ebin/prim_inet.beam
index f211971529..ff9268ad38 100644
--- a/erts/preloaded/ebin/prim_inet.beam
+++ b/erts/preloaded/ebin/prim_inet.beam
Binary files differ
diff --git a/erts/preloaded/src/persistent_term.erl b/erts/preloaded/src/persistent_term.erl
index 5d0c266127..ee7e49b6cb 100644
--- a/erts/preloaded/src/persistent_term.erl
+++ b/erts/preloaded/src/persistent_term.erl
@@ -19,7 +19,7 @@
%%
-module(persistent_term).
--export([erase/1,get/0,get/1,info/0,put/2]).
+-export([erase/1,get/0,get/1,get/2,info/0,put/2]).
-type key() :: term().
-type value() :: term().
@@ -41,6 +41,13 @@ get() ->
get(_Key) ->
erlang:nif_error(undef).
+-spec get(Key, Default) -> Value when
+ Key :: key(),
+ Default :: value(),
+ Value :: value().
+get(_Key, _Default) ->
+ erlang:nif_error(undef).
+
-spec info() -> Info when
Info :: #{'count':=Count,'memory':=Memory},
Count :: non_neg_integer(),
diff --git a/erts/preloaded/src/prim_inet.erl b/erts/preloaded/src/prim_inet.erl
index 4fe570ec53..d5abdd2483 100644
--- a/erts/preloaded/src/prim_inet.erl
+++ b/erts/preloaded/src/prim_inet.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2000-2018. All Rights Reserved.
+%% Copyright Ericsson AB 2000-2019. 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.
@@ -172,8 +172,18 @@ close(S) when is_port(S) ->
%% and is a contradiction in itself.
%% We have hereby done our best...
%%
- Tref = erlang:start_timer(T * 1000, self(), close_port),
- close_pend_loop(S, Tref, undefined);
+ case subscribe(S, [subs_empty_out_q]) of
+ {ok, [{subs_empty_out_q,0}]} ->
+ close_port(S);
+ {ok, [{subs_empty_out_q,N}]} when N > 0 ->
+ %% Wait for pending output to be sent
+ Tref = erlang:start_timer(T * 1000, self(), close_port),
+ close_pend_loop(S, Tref, N);
+ _ ->
+ %% Subscribe failed - wait full time
+ Tref = erlang:start_timer(T * 1000, self(), close_port),
+ close_pend_loop(S, Tref, undefined)
+ end;
_ -> % Regard this as {ok,{false,_}}
case subscribe(S, [subs_empty_out_q]) of
{ok, [{subs_empty_out_q,N}]} when N > 0 ->
diff --git a/erts/vsn.mk b/erts/vsn.mk
index 9c912a422b..e4bdb1a8eb 100644
--- a/erts/vsn.mk
+++ b/erts/vsn.mk
@@ -18,7 +18,7 @@
# %CopyrightEnd%
#
-VSN = 10.2.3
+VSN = 10.2.4
# Port number 4365 in 4.2
# Port number 4366 in 4.3
diff --git a/lib/common_test/doc/src/ct_telnet.xml b/lib/common_test/doc/src/ct_telnet.xml
index 9a12ce79ed..76f5305c46 100644
--- a/lib/common_test/doc/src/ct_telnet.xml
+++ b/lib/common_test/doc/src/ct_telnet.xml
@@ -239,18 +239,21 @@
<v>Connection = connection()</v>
<v>Cmd = string()</v>
<v>Opts = [Opt]</v>
- <v>Opt = {timeout, timeout()} | {newline, boolean()}</v>
+ <v>Opt = {timeout, timeout()} | {newline, boolean() | string()}</v>
<v>Data = [string()]</v>
<v>Reason = term()</v>
</type>
<desc><marker id="cmd-3"/>
<p>Sends a command through Telnet and waits for prompt.</p>
- <p>By default, this function adds a new line to the end of the
+ <p>By default, this function adds "\n" to the end of the
specified command. If this is not desired, use option
<c>{newline,false}</c>. This is necessary, for example, when
sending Telnet command sequences prefixed with character
- Interprete As Command (IAC).</p>
+ Interpret As Command (IAC). Option <c>{newline,string()}</c>
+ can also be used if a different line end than "\n" is
+ required, for instance <c>{newline,"\r\n"}</c>, to add both
+ carriage return and newline characters.</p>
<p>Option <c>timeout</c> specifies how long the client must wait
for prompt. If the time expires, the function returns
@@ -280,7 +283,7 @@
<v>CmdFormat = string()</v>
<v>Args = list()</v>
<v>Opts = [Opt]</v>
- <v>Opt = {timeout, timeout()} | {newline, boolean()}</v>
+ <v>Opt = {timeout, timeout()} | {newline, boolean() | string()}</v>
<v>Data = [string()]</v>
<v>Reason = term()</v>
</type>
@@ -339,7 +342,7 @@
subexpression number <c>N</c>. Subexpressions are denoted with
<c>'(' ')'</c> in the regular expression.</p>
- <p>If a <c>Tag</c> is speciifed, the returned <c>Match</c> also
+ <p>If a <c>Tag</c> is specified, the returned <c>Match</c> also
includes the matched <c>Tag</c>. Otherwise, only <c>RxMatch</c>
is returned.</p>
@@ -382,7 +385,7 @@
can abort the operation of waiting for prompt.</p></item>
<tag><c>repeat | repeat, N</c></tag>
<item><p>The pattern(s) must be matched multiple times. If <c>N</c>
- is speciified, the pattern(s) are matched <c>N</c> times, and
+ is specified, the pattern(s) are matched <c>N</c> times, and
the function returns <c>HaltReason = done</c>. This option can be
interrupted by one or more <c>HaltPatterns</c>. <c>MatchList</c>
is always returned, that is, a list of <c>Match</c> instead of
@@ -547,17 +550,20 @@
<v>Connection = connection()</v>
<v>Cmd = string()</v>
<v>Opts = [Opt]</v>
- <v>Opt = {newline, boolean()}</v>
+ <v>Opt = {newline, boolean() | string()}</v>
<v>Reason = term()</v>
</type>
<desc><marker id="send-3"/>
<p>Sends a Telnet command and returns immediately.</p>
- <p>By default, this function adds a newline to the end of the
+ <p>By default, this function adds "\n" to the end of the
specified command. If this is not desired, option
<c>{newline,false}</c> can be used. This is necessary, for example,
when sending Telnet command sequences prefixed with character
- Interprete As Command (IAC).</p>
+ Interpret As Command (IAC). Option <c>{newline,string()}</c>
+ can also be used if a different line end than "\n" is
+ required, for instance <c>{newline,"\r\n"}</c>, to add both
+ carriage return and newline characters.</p>
<p>The resulting output from the command can be read with
<seealso marker="#get_data-1"><c>ct_telnet:get_data/2</c></seealso> or
@@ -584,12 +590,15 @@
<v>CmdFormat = string()</v>
<v>Args = list()</v>
<v>Opts = [Opt]</v>
- <v>Opt = {newline, boolean()}</v>
+ <v>Opt = {newline, boolean() | string()}</v>
<v>Reason = term()</v>
</type>
<desc><marker id="sendf-4"/>
<p>Sends a Telnet command and returns immediately (uses a format
string and a list of arguments to build the command).</p>
+
+ <p>For details, see
+ <seealso marker="#send-3"><c>ct_telnet:send/3</c></seealso>.</p>
</desc>
</func>
</funcs>
diff --git a/lib/common_test/doc/src/notes.xml b/lib/common_test/doc/src/notes.xml
index dc18def838..2f53f1c29e 100644
--- a/lib/common_test/doc/src/notes.xml
+++ b/lib/common_test/doc/src/notes.xml
@@ -75,6 +75,44 @@
</section>
+<section><title>Common_Test 1.15.4.1</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ The status of a test case which failed with timetrap
+ timeout in <c>end_per_testcase</c> could not be modified
+ by returning <c>{fail,Reason}</c> from a
+ <c>post_end_per_testcase</c> hook function. This is now
+ corrected.</p>
+ <p>
+ Own Id: OTP-15584 Aux Id: ERIERL-282 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Common_Test 1.15.4.0.1</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ The status of a test case which failed with timetrap
+ timeout in <c>end_per_testcase</c> could not be modified
+ by returning <c>{fail,Reason}</c> from a
+ <c>post_end_per_testcase</c> hook function. This is now
+ corrected.</p>
+ <p>
+ Own Id: OTP-15584 Aux Id: ERIERL-282 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Common_Test 1.15.4</title>
<section><title>Fixed Bugs and Malfunctions</title>
@@ -4026,8 +4064,3 @@
<section><title>common_test 1.3.0</title>
</section>
</chapter>
-
-
-
-
-
diff --git a/lib/common_test/src/ct_cover.erl b/lib/common_test/src/ct_cover.erl
index d286f20a4d..bcd98dcc58 100644
--- a/lib/common_test/src/ct_cover.erl
+++ b/lib/common_test/src/ct_cover.erl
@@ -262,6 +262,11 @@ get_app_info(App=#cover{app=Name}, [{src_files,Name,Src1}|Terms], Dir) ->
Src = App#cover.src,
get_app_info(App#cover{src=Src++Src1},Terms,Dir);
+get_app_info(App=#cover{app=none}, [{local_only,Bool}|Terms], Dir) ->
+ get_app_info(App, [{local_only,none,Bool}|Terms], Dir);
+get_app_info(App=#cover{app=Name}, [{local_only,Name,Bool}|Terms], Dir) ->
+ get_app_info(App#cover{local_only=Bool},Terms,Dir);
+
get_app_info(App, [_|Terms], Dir) ->
get_app_info(App, Terms, Dir);
diff --git a/lib/common_test/src/ct_netconfc.erl b/lib/common_test/src/ct_netconfc.erl
index 29188a648e..6a758c4ea3 100644
--- a/lib/common_test/src/ct_netconfc.erl
+++ b/lib/common_test/src/ct_netconfc.erl
@@ -583,7 +583,7 @@ get_config(Client, Source, Filter, Timeout) ->
-spec edit_config(Client, Target, Config) -> Result when
Client :: client(),
Target :: netconf_db(),
- Config :: simple_xml(),
+ Config :: simple_xml() | [simple_xml()],
Result :: ok | {error,error_reason()}.
edit_config(Client, Target, Config) ->
edit_config(Client, Target, Config, ?DEFAULT_TIMEOUT).
@@ -591,7 +591,7 @@ edit_config(Client, Target, Config) ->
-spec edit_config(Client, Target, Config, OptParams) -> Result when
Client :: client(),
Target :: netconf_db(),
- Config :: simple_xml(),
+ Config :: simple_xml() | [simple_xml()],
OptParams :: [simple_xml()],
Result :: ok | {error,error_reason()};
(Client, Target, Config, Timeout) -> Result when
@@ -608,10 +608,12 @@ edit_config(Client, Target, Config, OptParams) when is_list(OptParams) ->
-spec edit_config(Client, Target, Config, OptParams, Timeout) -> Result when
Client :: client(),
Target :: netconf_db(),
- Config :: simple_xml(),
+ Config :: simple_xml() | [simple_xml()],
OptParams :: [simple_xml()],
Timeout :: timeout(),
Result :: ok | {error,error_reason()}.
+edit_config(Client, Target, Config, OptParams, Timeout) when not is_list(Config)->
+ edit_config(Client, Target, [Config], OptParams, Timeout);
edit_config(Client, Target, Config, OptParams, Timeout) ->
call(Client, {send_rpc_op, edit_config, [Target,Config,OptParams], Timeout}).
@@ -1113,7 +1115,7 @@ encode_rpc_operation(get,[Filter]) ->
encode_rpc_operation(get_config,[Source,Filter]) ->
{'get-config',[{source,[Source]}] ++ filter(Filter)};
encode_rpc_operation(edit_config,[Target,Config,OptParams]) ->
- {'edit-config',[{target,[Target]}] ++ OptParams ++ [{config,[Config]}]};
+ {'edit-config',[{target,[Target]}] ++ OptParams ++ [{config,Config}]};
encode_rpc_operation(delete_config,[Target]) ->
{'delete-config',[{target,[Target]}]};
encode_rpc_operation(copy_config,[Target,Source]) ->
diff --git a/lib/common_test/src/ct_run.erl b/lib/common_test/src/ct_run.erl
index c9d406f1fd..960252a6fe 100644
--- a/lib/common_test/src/ct_run.erl
+++ b/lib/common_test/src/ct_run.erl
@@ -2345,18 +2345,24 @@ start_cover(Opts=#opts{coverspec=CovData,cover_stop=CovStop},LogDir) ->
CovImport,
_CovExport,
#cover{app = CovApp,
+ local_only = LocalOnly,
level = CovLevel,
excl_mods = CovExcl,
incl_mods = CovIncl,
cross = CovCross,
src = _CovSrc}} = CovData,
+ case LocalOnly of
+ true -> cover:local_only();
+ false -> ok
+ end,
ct_logs:log("COVER INFO",
"Using cover specification file: ~ts~n"
"App: ~w~n"
+ "Local only: ~w~n"
"Cross cover: ~w~n"
"Including ~w modules~n"
"Excluding ~w modules",
- [CovFile,CovApp,CovCross,
+ [CovFile,CovApp,LocalOnly,CovCross,
length(CovIncl),length(CovExcl)]),
%% Tell test_server to print a link in its coverlog
diff --git a/lib/common_test/src/ct_telnet.erl b/lib/common_test/src/ct_telnet.erl
index 58a29edace..219f58dcf5 100644
--- a/lib/common_test/src/ct_telnet.erl
+++ b/lib/common_test/src/ct_telnet.erl
@@ -194,6 +194,15 @@ send(Connection,Cmd,Opts) ->
check_send_opts([{newline,Bool}|Opts]) when is_boolean(Bool) ->
check_send_opts(Opts);
+check_send_opts([{newline,String}|Opts]) when is_list(String) ->
+ case lists:all(fun(I) when is_integer(I), I>=0, I=<127 -> true;
+ (_) -> false
+ end, String) of
+ true ->
+ check_send_opts(Opts);
+ false ->
+ {error,{invalid_option,{newline,String}}}
+ end;
check_send_opts([Invalid|_]) ->
{error,{invalid_option,Invalid}};
check_send_opts([]) ->
@@ -211,10 +220,16 @@ expect(Connection,Patterns) ->
expect(Connection,Patterns,Opts) ->
case get_handle(Connection) of
- {ok,Pid} ->
- call(Pid,{expect,Patterns,Opts});
- Error ->
- Error
+ {ok,Pid} ->
+ case call(Pid,{expect,Patterns,Opts}) of
+ {error,Reason} when element(1,Reason)==bad_pattern ->
+ %% Faulty user input - should fail the test case
+ exit({Reason,{?MODULE,?FUNCTION_NAME,3}});
+ Other ->
+ Other
+ end;
+ Error ->
+ Error
end.
%%%=================================================================
@@ -674,60 +689,68 @@ silent_teln_expect(Name,Pid,Data,Pattern,Prx,Opts) ->
%% 3b) Repeat (sequence): 2) is repeated either N times or until a
%% halt condition is fulfilled.
teln_expect(Name,Pid,Data,Pattern0,Prx,Opts) ->
- HaltPatterns =
+ HaltPatterns0 =
case get_ignore_prompt(Opts) of
true ->
get_haltpatterns(Opts);
false ->
[prompt | get_haltpatterns(Opts)]
end,
-
- PromptCheck = get_prompt_check(Opts),
-
- {WaitForPrompt,Pattern1,Opts1} = wait_for_prompt(Pattern0,Opts),
-
- Seq = get_seq(Opts1),
- Pattern2 = convert_pattern(Pattern1,Seq),
- {IdleTimeout,TotalTimeout} = get_timeouts(Opts1),
-
- EO = #eo{teln_pid=Pid,
- prx=Prx,
- idle_timeout=IdleTimeout,
- total_timeout=TotalTimeout,
- seq=Seq,
- haltpatterns=HaltPatterns,
- prompt_check=PromptCheck},
+ case convert_pattern(HaltPatterns0,false) of
+ {ok,HaltPatterns} ->
+ {WaitForPrompt,Pattern1,Opts1} = wait_for_prompt(Pattern0,Opts),
+ Seq = get_seq(Opts1),
+ case convert_pattern(Pattern1,Seq) of
+ {ok,Pattern2} ->
+ {IdleTimeout,TotalTimeout} = get_timeouts(Opts1),
+ PromptCheck = get_prompt_check(Opts1),
+
+ EO = #eo{teln_pid=Pid,
+ prx=Prx,
+ idle_timeout=IdleTimeout,
+ total_timeout=TotalTimeout,
+ seq=Seq,
+ haltpatterns=HaltPatterns,
+ prompt_check=PromptCheck},
- case get_repeat(Opts1) of
- false ->
- case teln_expect1(Name,Pid,Data,Pattern2,[],EO) of
- {ok,Matched,Rest} when WaitForPrompt ->
- case lists:reverse(Matched) of
- [{prompt,_},Matched1] ->
- {ok,Matched1,Rest};
- [{prompt,_}|Matched1] ->
- {ok,lists:reverse(Matched1),Rest}
- end;
- {ok,Matched,Rest} ->
- {ok,Matched,Rest};
- {halt,Why,Rest} ->
- {error,Why,Rest};
- {error,Reason} ->
- {error,Reason}
- end;
- N ->
- EO1 = EO#eo{repeat=N},
- repeat_expect(Name,Pid,Data,Pattern2,[],EO1)
+ case get_repeat(Opts1) of
+ false ->
+ case teln_expect1(Name,Pid,Data,Pattern2,[],EO) of
+ {ok,Matched,Rest} when WaitForPrompt ->
+ case lists:reverse(Matched) of
+ [{prompt,_},Matched1] ->
+ {ok,Matched1,Rest};
+ [{prompt,_}|Matched1] ->
+ {ok,lists:reverse(Matched1),Rest}
+ end;
+ {ok,Matched,Rest} ->
+ {ok,Matched,Rest};
+ {halt,Why,Rest} ->
+ {error,Why,Rest};
+ {error,Reason} ->
+ {error,Reason}
+ end;
+ N ->
+ EO1 = EO#eo{repeat=N},
+ repeat_expect(Name,Pid,Data,Pattern2,[],EO1)
+ end;
+ Error ->
+ Error
+ end;
+ Error ->
+ Error
end.
-convert_pattern(Pattern,Seq)
- when is_list(Pattern) and not is_integer(hd(Pattern)) ->
- case Seq of
- true -> Pattern;
- false -> rm_dupl(Pattern,[])
- end;
+convert_pattern(Pattern0,Seq)
+ when Pattern0==[] orelse (is_list(Pattern0) and not is_integer(hd(Pattern0))) ->
+ Pattern =
+ case Seq of
+ true -> Pattern0;
+ false -> rm_dupl(Pattern0,[])
+ end,
+ compile_pattern(Pattern,[]);
convert_pattern(Pattern,_Seq) ->
- [Pattern].
+ compile_pattern([Pattern],[]).
rm_dupl([P|Ps],Acc) ->
case lists:member(P,Acc) of
@@ -739,6 +762,25 @@ rm_dupl([P|Ps],Acc) ->
rm_dupl([],Acc) ->
lists:reverse(Acc).
+compile_pattern([prompt|Patterns],Acc) ->
+ compile_pattern(Patterns,[prompt|Acc]);
+compile_pattern([{prompt,_}=P|Patterns],Acc) ->
+ compile_pattern(Patterns,[P|Acc]);
+compile_pattern([{Tag,Pattern}|Patterns],Acc) ->
+ try re:compile(Pattern,[unicode]) of
+ {ok,MP} -> compile_pattern(Patterns,[{Tag,MP}|Acc]);
+ {error,Error} -> {error,{bad_pattern,{Tag,Pattern},Error}}
+ catch error:badarg -> {error,{bad_pattern,{Tag,Pattern}}}
+ end;
+compile_pattern([Pattern|Patterns],Acc) ->
+ try re:compile(Pattern,[unicode]) of
+ {ok,MP} -> compile_pattern(Patterns,[MP|Acc]);
+ {error,Error} -> {error,{bad_pattern,Pattern,Error}}
+ catch error:badarg -> {error,{bad_pattern,Pattern}}
+ end;
+compile_pattern([],Acc) ->
+ {ok,lists:reverse(Acc)}.
+
get_timeouts(Opts) ->
{case lists:keysearch(idle_timeout,1,Opts) of
{value,{_,T}} ->
@@ -772,7 +814,7 @@ get_seq(Opts) ->
get_haltpatterns(Opts) ->
case lists:keysearch(halt,1,Opts) of
{value,{halt,HaltPatterns}} ->
- convert_pattern(HaltPatterns,false);
+ HaltPatterns;
false ->
[]
end.
@@ -1068,7 +1110,7 @@ match_line(Name,Pid,Line,[{prompt,PromptType}|Patterns],FoundPrompt,Term,
when PromptType=/=FoundPrompt ->
match_line(Name,Pid,Line,Patterns,FoundPrompt,Term,EO,RetTag);
match_line(Name,Pid,Line,[{Tag,Pattern}|Patterns],FoundPrompt,Term,EO,RetTag) ->
- case re:run(Line,Pattern,[{capture,all,list},unicode]) of
+ case re:run(Line,Pattern,[{capture,all,list}]) of
nomatch ->
match_line(Name,Pid,Line,Patterns,FoundPrompt,Term,EO,RetTag);
{match,Match} ->
@@ -1076,7 +1118,7 @@ match_line(Name,Pid,Line,[{Tag,Pattern}|Patterns],FoundPrompt,Term,EO,RetTag) ->
{RetTag,{Tag,Match}}
end;
match_line(Name,Pid,Line,[Pattern|Patterns],FoundPrompt,Term,EO,RetTag) ->
- case re:run(Line,Pattern,[{capture,all,list},unicode]) of
+ case re:run(Line,Pattern,[{capture,all,list}]) of
nomatch ->
match_line(Name,Pid,Line,Patterns,FoundPrompt,Term,EO,RetTag);
{match,Match} ->
diff --git a/lib/common_test/src/ct_telnet_client.erl b/lib/common_test/src/ct_telnet_client.erl
index 76e4b9ea70..007477c855 100644
--- a/lib/common_test/src/ct_telnet_client.erl
+++ b/lib/common_test/src/ct_telnet_client.erl
@@ -101,9 +101,11 @@ close(Pid) ->
end.
send_data(Pid, Data) ->
- send_data(Pid, Data, true).
+ send_data(Pid, Data, "\n").
send_data(Pid, Data, true) ->
- send_data(Pid, Data++"\n", false);
+ send_data(Pid, Data, "\n");
+send_data(Pid, Data, Newline) when is_list(Newline) ->
+ send_data(Pid, Data++Newline, false);
send_data(Pid, Data, false) ->
Pid ! {send_data, Data},
ok.
diff --git a/lib/common_test/src/ct_util.hrl b/lib/common_test/src/ct_util.hrl
index 039c8168ec..d5c93d05ba 100644
--- a/lib/common_test/src/ct_util.hrl
+++ b/lib/common_test/src/ct_util.hrl
@@ -62,6 +62,7 @@
merge_tests=true}).
-record(cover, {app=none,
+ local_only=false,
level=details,
excl_mods=[],
incl_mods=[],
diff --git a/lib/common_test/src/test_server.erl b/lib/common_test/src/test_server.erl
index a896a0551b..9eda3f2152 100644
--- a/lib/common_test/src/test_server.erl
+++ b/lib/common_test/src/test_server.erl
@@ -850,17 +850,23 @@ spawn_fw_call(Mod,EPTC={end_per_testcase,Func},EndConf,Pid,
"WARNING: end_per_testcase failed!</font>",
{died,W}
end,
- try do_end_tc_call(Mod,EPTC,{Pid,Report,[EndConf]}, Why) of
- _ -> ok
- catch
- _:FwEndTCErr ->
- exit({fw_notify_done,end_tc,FwEndTCErr})
- end,
- FailLoc = proplists:get_value(tc_fail_loc, EndConf),
+ FailLoc0 = proplists:get_value(tc_fail_loc, EndConf),
+ {RetVal1,FailLoc} =
+ try do_end_tc_call(Mod,EPTC,{Pid,Report,[EndConf]}, Why) of
+ Why ->
+ {RetVal,FailLoc0};
+ {failed,_} = R ->
+ {R,[{Mod,Func}]};
+ R ->
+ {R,FailLoc0}
+ catch
+ _:FwEndTCErr ->
+ exit({fw_notify_done,end_tc,FwEndTCErr})
+ end,
%% finished, report back (if end_per_testcase fails, a warning
%% should be printed as part of the comment)
SendTo ! {self(),fw_notify_done,
- {Time,RetVal,FailLoc,[],Warn}}
+ {Time,RetVal1,FailLoc,[],Warn}}
end,
spawn_link(FwCall);
@@ -902,14 +908,25 @@ spawn_fw_call(Mod,Func,CurrConf,Pid,Error,Loc,SendTo) ->
FwErrorNotifyErr})
end,
Conf = [{tc_status,{failed,Error}}|CurrConf],
- try do_end_tc_call(Mod,EndTCFunc,{Pid,Error,[Conf]},Error) of
- _ -> ok
- catch
- _:FwEndTCErr ->
- exit({fw_notify_done,end_tc,FwEndTCErr})
- end,
+ {Time,RetVal,Loc1} =
+ try do_end_tc_call(Mod,EndTCFunc,{Pid,Error,[Conf]},Error) of
+ Error ->
+ {died, Error, Loc};
+ {failed,Reason} = NewReturn ->
+ fw_error_notify(Mod,Func1,Conf,Reason),
+ {died, NewReturn, [{Mod,Func}]};
+ NewReturn ->
+ T = case Error of
+ {timetrap_timeout,TT} -> TT;
+ _ -> 0
+ end,
+ {T, NewReturn, Loc}
+ catch
+ _:FwEndTCErr ->
+ exit({fw_notify_done,end_tc,FwEndTCErr})
+ end,
%% finished, report back
- SendTo ! {self(),fw_notify_done,{died,Error,Loc,[],undefined}}
+ SendTo ! {self(),fw_notify_done,{Time,RetVal,Loc1,[],undefined}}
end,
spawn_link(FwCall).
diff --git a/lib/common_test/test/ct_hooks_SUITE.erl b/lib/common_test/test/ct_hooks_SUITE.erl
index 0f5636a789..44b86b1dfe 100644
--- a/lib/common_test/test/ct_hooks_SUITE.erl
+++ b/lib/common_test/test/ct_hooks_SUITE.erl
@@ -84,7 +84,7 @@ all(suite) ->
fail_post_suite_cth, skip_pre_suite_cth, skip_pre_end_cth,
skip_pre_init_tc_cth,
skip_post_suite_cth, recover_post_suite_cth, update_config_cth,
- state_update_cth, options_cth, same_id_cth,
+ state_update_cth, update_result_cth, options_cth, same_id_cth,
fail_n_skip_with_minimal_cth, prio_cth, no_config,
no_init_suite_config, no_init_config, no_end_config,
failed_sequence, repeat_force_stop, config_clash,
@@ -209,6 +209,10 @@ state_update_cth(Config) when is_list(Config) ->
do_test(state_update_cth, "ct_cth_fail_one_skip_one_SUITE.erl",
[state_update_cth,state_update_cth],Config).
+update_result_cth(Config) ->
+ do_test(update_result_cth, "ct_cth_update_result_post_end_tc_SUITE.erl",
+ [update_result_post_end_tc_cth],Config).
+
options_cth(Config) when is_list(Config) ->
do_test(options_cth, "ct_cth_empty_SUITE.erl",
[{empty_cth,[test]}],Config).
@@ -1099,6 +1103,106 @@ test_events(state_update_cth) ->
{?eh,stop_logging,[]}
];
+test_events(update_result_cth) ->
+ Suite = ct_cth_update_result_post_end_tc_SUITE,
+ [
+ {?eh,start_logging,'_'},
+ {?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}},
+ {?eh,cth,{'_',init,['_',[]]}},
+ {?eh,tc_start,{Suite,init_per_suite}},
+ {?eh,tc_done,{Suite,init_per_suite,ok}},
+
+ {?eh,tc_start,{Suite,tc_ok_to_fail}},
+ {?eh,cth,{'_',post_end_per_testcase,[Suite,tc_ok_to_fail,'_',ok,[]]}},
+ {?eh,tc_done,{Suite,tc_ok_to_fail,{failed,{error,"Test failure"}}}},
+ {?eh,cth,{'_',on_tc_fail,'_'}},
+ {?eh,test_stats,{0,1,{0,0}}},
+
+ {?eh,tc_start,{Suite,tc_ok_to_skip}},
+ {?eh,cth,{'_',post_end_per_testcase,[Suite,tc_ok_to_skip,'_',ok,[]]}},
+ {?eh,tc_done,{Suite,tc_ok_to_skip,{skipped,"Test skipped"}}},
+ {?eh,cth,{'_',on_tc_skip,'_'}},
+ {?eh,test_stats,{0,1,{1,0}}},
+
+ {?eh,tc_start,{Suite,tc_fail_to_ok}},
+ {?eh,cth,{'_',post_end_per_testcase,
+ [Suite,tc_fail_to_ok,'_',
+ {error,{test_case_failed,"should be changed to ok"}},[]]}},
+ {?eh,tc_done,{Suite,tc_fail_to_ok,ok}},
+ {?eh,test_stats,{1,1,{1,0}}},
+
+ {?eh,tc_start,{Suite,tc_fail_to_skip}},
+ {?eh,cth,{'_',post_end_per_testcase,
+ [Suite,tc_fail_to_skip,'_',
+ {error,{test_case_failed,"should be changed to skip"}},[]]}},
+ {?eh,tc_done,{Suite,tc_fail_to_skip,{skipped,"Test skipped"}}},
+ {?eh,cth,{'_',on_tc_skip,'_'}},
+ {?eh,test_stats,{1,1,{2,0}}},
+
+ {?eh,tc_start,{Suite,tc_timetrap_to_ok}},
+ {?eh,cth,{'_',post_end_per_testcase,
+ [Suite,tc_timetrap_to_ok,'_',{timetrap_timeout,3000},[]]}},
+ {?eh,tc_done,{Suite,tc_timetrap_to_ok,ok}},
+ {?eh,test_stats,{2,1,{2,0}}},
+
+ {?eh,tc_start,{Suite,tc_timetrap_to_skip}},
+ {?eh,cth,{'_',post_end_per_testcase,
+ [Suite,tc_timetrap_to_skip,'_',{timetrap_timeout,3000},[]]}},
+ {?eh,tc_done,{Suite,tc_timetrap_to_skip,{skipped,"Test skipped"}}},
+ {?eh,cth,{'_',on_tc_skip,'_'}},
+ {?eh,test_stats,{2,1,{3,0}}},
+
+ {?eh,tc_start,{Suite,tc_skip_to_fail}},
+ {?eh,cth,{'_',post_end_per_testcase,
+ [Suite,tc_skip_to_fail,'_',
+ {skip,"should be changed to fail"},[]]}},
+ {?eh,tc_done,{Suite,tc_skip_to_fail,{failed,{error,"Test failure"}}}},
+ {?eh,cth,{'_',on_tc_fail,'_'}},
+ {?eh,test_stats,{2,2,{3,0}}},
+
+ {?eh,tc_start,{Suite,end_fail_to_fail}},
+ {?eh,cth,{'_',post_end_per_testcase,
+ [Suite,end_fail_to_fail,'_',
+ {failed,
+ {Suite,end_per_testcase,
+ {'EXIT',{test_case_failed,"change result when end fails"}}}},[]]}},
+ {?eh,tc_done,{Suite,end_fail_to_fail,{failed,{error,"Test failure"}}}},
+ {?eh,cth,{'_',on_tc_fail,'_'}},
+ {?eh,test_stats,{2,3,{3,0}}},
+
+ {?eh,tc_start,{Suite,end_fail_to_skip}},
+ {?eh,cth,{'_',post_end_per_testcase,
+ [Suite,end_fail_to_skip,'_',
+ {failed,
+ {Suite,end_per_testcase,
+ {'EXIT',{test_case_failed,"change result when end fails"}}}},[]]}},
+ {?eh,tc_done,{Suite,end_fail_to_skip,{skipped,"Test skipped"}}},
+ {?eh,cth,{'_',on_tc_skip,'_'}},
+ {?eh,test_stats,{2,3,{4,0}}},
+
+ {?eh,tc_start,{Suite,end_timetrap_to_fail}},
+ {?eh,cth,{'_',post_end_per_testcase,
+ [Suite,end_timetrap_to_fail,'_',
+ {failed,{Suite,end_per_testcase,{timetrap_timeout,3000}}},[]]}},
+ {?eh,tc_done,{Suite,end_timetrap_to_fail,{failed,{error,"Test failure"}}}},
+ {?eh,cth,{'_',on_tc_fail,'_'}},
+ {?eh,test_stats,{2,4,{4,0}}},
+
+ {?eh,tc_start,{Suite,end_timetrap_to_skip}},
+ {?eh,cth,{'_',post_end_per_testcase,
+ [Suite,end_timetrap_to_skip,'_',
+ {failed,{Suite,end_per_testcase,{timetrap_timeout,3000}}},[]]}},
+ {?eh,tc_done,{Suite,end_timetrap_to_skip,{skipped,"Test skipped"}}},
+ {?eh,cth,{'_',on_tc_skip,'_'}},
+ {?eh,test_stats,{2,4,{5,0}}},
+
+ {?eh,tc_start,{Suite,end_per_suite}},
+ {?eh,tc_done,{Suite,end_per_suite,ok}},
+ {?eh,test_done,{'DEF','STOP_TIME'}},
+ {?eh,cth,{'_',terminate,[[]]}},
+ {?eh,stop_logging,[]}
+ ];
+
test_events(options_cth) ->
[
{?eh,start_logging,{'DEF','RUNDIR'}},
diff --git a/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/ct_cth_update_result_post_end_tc_SUITE.erl b/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/ct_cth_update_result_post_end_tc_SUITE.erl
new file mode 100644
index 0000000000..a16138ce6f
--- /dev/null
+++ b/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/ct_cth_update_result_post_end_tc_SUITE.erl
@@ -0,0 +1,101 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2010-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%
+%%
+
+-module(ct_cth_update_result_post_end_tc_SUITE).
+
+-compile(export_all).
+
+-include("ct.hrl").
+
+suite() ->
+ [{timetrap,{seconds,3}}].
+
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(Config) ->
+ ok.
+
+init_per_group(_,Config) ->
+ Config.
+
+end_per_group(_,_) ->
+ ok.
+
+init_per_testcase(_,Config) ->
+ Config.
+
+end_per_testcase(EndTimetrap,_) when EndTimetrap==end_timetrap_to_fail;
+ EndTimetrap==end_timetrap_to_skip->
+ timer:sleep(10000);
+end_per_testcase(EndFail,_) when EndFail==end_fail_to_fail;
+ EndFail==end_fail_to_skip->
+ ct:fail("change result when end fails");
+end_per_testcase(_,_) ->
+ ok.
+
+all() ->
+ [tc_ok_to_fail,
+ tc_ok_to_skip,
+ tc_fail_to_ok,
+ tc_fail_to_skip,
+ tc_timetrap_to_ok,
+ tc_timetrap_to_skip,
+ tc_skip_to_fail,
+ end_fail_to_fail,
+ end_fail_to_skip,
+ end_timetrap_to_fail,
+ end_timetrap_to_skip].
+
+%% Test cases starts here.
+tc_ok_to_fail(_Config) ->
+ ok.
+
+tc_ok_to_skip(_Config) ->
+ ok.
+
+tc_fail_to_ok(_Config) ->
+ ct:fail("should be changed to ok").
+
+tc_fail_to_skip(_Config) ->
+ ct:fail("should be changed to skip").
+
+tc_timetrap_to_ok(_Config) ->
+ timer:sleep(10000), % will time out after 3 sek
+ ok.
+
+tc_timetrap_to_skip(_Config) ->
+ timer:sleep(10000), % will time out after 3 sek
+ ok.
+
+tc_skip_to_fail(_Config) ->
+ {skip,"should be changed to fail"}.
+
+end_fail_to_fail(_Config) ->
+ ok.
+
+end_fail_to_skip(_Config) ->
+ ok.
+
+end_timetrap_to_fail(_Config) ->
+ ok.
+
+end_timetrap_to_skip(_Config) ->
+ ok.
diff --git a/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/update_result_post_end_tc_cth.erl b/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/update_result_post_end_tc_cth.erl
new file mode 100644
index 0000000000..7afb3d8781
--- /dev/null
+++ b/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/update_result_post_end_tc_cth.erl
@@ -0,0 +1,98 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2010-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%
+%%
+
+
+-module(update_result_post_end_tc_cth).
+
+
+-include_lib("common_test/src/ct_util.hrl").
+-include_lib("common_test/include/ct_event.hrl").
+
+
+%% CT Hooks
+-compile(export_all).
+
+init(Id, Opts) ->
+ empty_cth:init(Id, Opts).
+
+pre_init_per_suite(Suite, Config, State) ->
+ empty_cth:pre_init_per_suite(Suite,Config,State).
+
+post_init_per_suite(Suite,Config,Return,State) ->
+ empty_cth:post_init_per_suite(Suite,Config,Return,State).
+
+pre_end_per_suite(Suite,Config,State) ->
+ empty_cth:pre_end_per_suite(Suite,Config,State).
+
+post_end_per_suite(Suite,Config,Return,State) ->
+ empty_cth:post_end_per_suite(Suite,Config,Return,State).
+
+pre_init_per_group(Suite,Group,Config,State) ->
+ empty_cth:pre_init_per_group(Suite,Group,Config,State).
+
+post_init_per_group(Suite,Group,Config,Return,State) ->
+ empty_cth:post_init_per_group(Suite,Group,Config,Return,State).
+
+pre_end_per_group(Suite,Group,Config,State) ->
+ empty_cth:pre_end_per_group(Suite,Group,Config,State).
+
+post_end_per_group(Suite,Group,Config,Return,State) ->
+ empty_cth:post_end_per_group(Suite,Group,Config,Return,State).
+
+pre_init_per_testcase(Suite,TC,Config,State) ->
+ empty_cth:pre_init_per_testcase(Suite,TC,Config,State).
+
+post_end_per_testcase(Suite,TC,Config,Return,State) ->
+ empty_cth:post_end_per_testcase(Suite,TC,Config,Return,State),
+ change_result(TC,Config,State).
+
+on_tc_fail(Suite,TC, Reason, State) ->
+ empty_cth:on_tc_fail(Suite,TC,Reason,State).
+
+on_tc_skip(Suite,TC, Reason, State) ->
+ empty_cth:on_tc_skip(Suite,TC,Reason,State).
+
+terminate(State) ->
+ empty_cth:terminate(State).
+
+%%%-----------------------------------------------------------------
+%%%
+change_result(tc_ok_to_fail,_Config,State) ->
+ {{fail, "Test failure"}, State};
+change_result(tc_ok_to_skip,_Config,State) ->
+ {{skip, "Test skipped"}, State};
+change_result(tc_fail_to_ok,Config,State) ->
+ {lists:keydelete(tc_status,1,Config),State};
+change_result(tc_fail_to_skip,Config,State) ->
+ {{skip,"Test skipped"},State};
+change_result(tc_timetrap_to_ok,Config,State) ->
+ {lists:keydelete(tc_status,1,Config),State};
+change_result(tc_timetrap_to_skip,Config,State) ->
+ {{skip,"Test skipped"},State};
+change_result(tc_skip_to_fail,_Config,State) ->
+ {{fail, "Test failure"}, State};
+change_result(end_fail_to_fail,_Config,State) ->
+ {{fail, "Test failure"}, State};
+change_result(end_fail_to_skip,_Config,State) ->
+ {{skip, "Test skipped"}, State};
+change_result(end_timetrap_to_fail,_Config,State) ->
+ {{fail, "Test failure"}, State};
+change_result(end_timetrap_to_skip,_Config,State) ->
+ {{skip, "Test skipped"}, State}.
diff --git a/lib/common_test/test/ct_netconfc_SUITE_data/netconfc1_SUITE.erl b/lib/common_test/test/ct_netconfc_SUITE_data/netconfc1_SUITE.erl
index 0a374d7404..7aaf33839f 100644
--- a/lib/common_test/test/ct_netconfc_SUITE_data/netconfc1_SUITE.erl
+++ b/lib/common_test/test/ct_netconfc_SUITE_data/netconfc1_SUITE.erl
@@ -440,6 +440,12 @@ edit_config(Config) ->
?ok = ct_netconfc:edit_config(Client,running,
{server,[{xmlns,"myns"}],
[{name,["myserver"]}]}),
+ ?NS:expect_reply('edit-config',ok),
+ ?ok = ct_netconfc:edit_config(Client,running,
+ [{server,[{xmlns,"myns"}],
+ [{name,["server1"]}]},
+ {server,[{xmlns,"myns"}],
+ [{name,["server2"]}]}]),
?NS:expect_do_reply('close-session',close,ok),
?ok = ct_netconfc:close_session(Client),
ok.
diff --git a/lib/common_test/test/ct_telnet_SUITE.erl b/lib/common_test/test/ct_telnet_SUITE.erl
index a0089c9bc9..f71b7c370f 100644
--- a/lib/common_test/test/ct_telnet_SUITE.erl
+++ b/lib/common_test/test/ct_telnet_SUITE.erl
@@ -50,10 +50,10 @@
suite() -> [{ct_hooks,[ts_install_cth]}].
groups() ->
- [{legacy, [], [unix_telnet,own_server,timetrap]},
- {raw, [], [unix_telnet,own_server,timetrap]},
- {html, [], [unix_telnet,own_server]},
- {silent, [], [unix_telnet,own_server]}].
+ [{legacy, [], [unix_telnet,own_server,faulty_regexp,timetrap]},
+ {raw, [], [unix_telnet,own_server,faulty_regexp,timetrap]},
+ {html, [], [unix_telnet,own_server,faulty_regexp]},
+ {silent, [], [unix_telnet,own_server,faulty_regexp]}].
all() ->
[
@@ -119,6 +119,12 @@ own_server(Config) ->
all_tests_in_suite(own_server,"ct_telnet_own_server_SUITE",
CfgFile,Config).
+faulty_regexp(Config) ->
+ CfgFile = "telnet.faulty_regexp." ++
+ atom_to_list(groupname(Config)) ++ ".cfg",
+ all_tests_in_suite(faulty_regexp,"ct_telnet_faulty_regexp_SUITE",
+ CfgFile,Config).
+
timetrap(Config) ->
CfgFile = "telnet.timetrap." ++
atom_to_list(groupname(Config)) ++ ".cfg",
@@ -225,6 +231,31 @@ events_to_check(unix_telnet,Config) ->
all_cases(ct_telnet_basic_SUITE,Config);
events_to_check(own_server,Config) ->
all_cases(ct_telnet_own_server_SUITE,Config);
+events_to_check(faulty_regexp,_Config) ->
+ [{?eh,start_logging,{'DEF','RUNDIR'}},
+ {?eh,tc_done,
+ {ct_telnet_faulty_regexp_SUITE,expect_pattern,
+ {failed,
+ {error,{{bad_pattern,"invalid(pattern",{"missing )",15}},
+ {ct_telnet,expect,3}}}}}},
+ {?eh,tc_done,
+ {ct_telnet_faulty_regexp_SUITE,expect_pattern_no_string,
+ {failed,
+ {error,{{bad_pattern,invalid_pattern},
+ {ct_telnet,expect,3}}}}}},
+ {?eh,tc_done,
+ {ct_telnet_faulty_regexp_SUITE,expect_tag_pattern,
+ {failed,
+ {error,{{bad_pattern,{tag,"invalid(pattern"},{"missing )",15}},
+ {ct_telnet,expect,3}}}}}},
+ {?eh,tc_done,
+ {ct_telnet_faulty_regexp_SUITE,expect_tag_pattern_no_string,
+ {failed,
+ {error,{{bad_pattern,{tag,invalid_pattern}},
+ {ct_telnet,expect,3}}}}}},
+ {?eh,tc_done,{ct_telnet_faulty_regexp_SUITE,expect_pattern_unicode,ok}},
+ {?eh,tc_done,{ct_telnet_faulty_regexp_SUITE,expect_tag_pattern_unicode,ok}},
+ {?eh,stop_logging,[]}];
events_to_check(timetrap,_Config) ->
[{?eh,start_logging,{'DEF','RUNDIR'}},
{?eh,tc_done,{ct_telnet_timetrap_SUITE,expect_timetrap,
diff --git a/lib/common_test/test/ct_telnet_SUITE_data/ct_telnet_faulty_regexp_SUITE.erl b/lib/common_test/test/ct_telnet_SUITE_data/ct_telnet_faulty_regexp_SUITE.erl
new file mode 100644
index 0000000000..a5c9451a9c
--- /dev/null
+++ b/lib/common_test/test/ct_telnet_SUITE_data/ct_telnet_faulty_regexp_SUITE.erl
@@ -0,0 +1,79 @@
+-module(ct_telnet_faulty_regexp_SUITE).
+
+-compile(export_all).
+
+-include_lib("common_test/include/ct.hrl").
+
+-define(name, telnet_server_conn1).
+
+%%--------------------------------------------------------------------
+%% TEST SERVER CALLBACK FUNCTIONS
+%%--------------------------------------------------------------------
+
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(_Config) ->
+ ok.
+
+suite() -> [{require,?name,{unix,[telnet]}},
+ {require,ct_conn_log},
+ {ct_hooks, [{cth_conn_log,[]}]}].
+
+all() ->
+ [expect_pattern,
+ expect_pattern_no_string,
+ expect_tag_pattern,
+ expect_tag_pattern_no_string,
+ expect_pattern_unicode,
+ expect_tag_pattern_unicode].
+
+groups() ->
+ [].
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
+init_per_testcase(_,Config) ->
+ ct:log("init_per_testcase: opening telnet connection...",[]),
+ {ok,_} = ct_telnet:open(?name),
+ ct:log("...done",[]),
+ Config.
+
+end_per_testcase(_,_Config) ->
+ ct:log("end_per_testcase: closing telnet connection...",[]),
+ _ = ct_telnet:close(?name),
+ ct:log("...done",[]),
+ ok.
+
+expect_pattern(_) ->
+ ok = ct_telnet:send(?name, "echo ayt"),
+ ok = ct_telnet:expect(?name, "invalid(pattern").
+
+expect_pattern_no_string(_) ->
+ ok = ct_telnet:send(?name, "echo ayt"),
+ ok = ct_telnet:expect(?name, invalid_pattern).
+
+expect_tag_pattern(_) ->
+ ok = ct_telnet:send(?name, "echo ayt"),
+ ok = ct_telnet:expect(?name, {tag,"invalid(pattern"}).
+
+expect_tag_pattern_no_string(_) ->
+ ok = ct_telnet:send(?name, "echo ayt"),
+ ok = ct_telnet:expect(?name, {tag,invalid_pattern}).
+
+%% Test that a unicode pattern can be given without the testcase
+%% failing. Do however notice that there is no real unicode support
+%% in ct_telnet yet, that is, the telnet binary mode is not supported.
+expect_pattern_unicode(_) ->
+ ok = ct_telnet:send(?name, "echo ayt"),
+ {error,{prompt,_}} = ct_telnet:expect(?name, "pattern_with_unicode_αβ"),
+ ok.
+
+expect_tag_pattern_unicode(_) ->
+ ok = ct_telnet:send(?name, "echo ayt"),
+ {error,{prompt,_}} = ct_telnet:expect(?name, "pattern_with_unicode_αβ"),
+ ok.
diff --git a/lib/common_test/test/ct_telnet_SUITE_data/ct_telnet_own_server_SUITE.erl b/lib/common_test/test/ct_telnet_SUITE_data/ct_telnet_own_server_SUITE.erl
index 985fa40ad2..34df57027e 100644
--- a/lib/common_test/test/ct_telnet_SUITE_data/ct_telnet_own_server_SUITE.erl
+++ b/lib/common_test/test/ct_telnet_SUITE_data/ct_telnet_own_server_SUITE.erl
@@ -58,7 +58,8 @@ all() ->
server_speaks,
server_disconnects,
newline_ayt,
- newline_break
+ newline_break,
+ newline_string
].
groups() ->
@@ -393,3 +394,11 @@ newline_break(_) ->
"> " = lists:flatten(R),
ok = ct_telnet:close(Handle),
ok.
+
+%% Test option {newline,String} to specify an own newline, e.g. "\r\n"
+newline_string(_) ->
+ {ok, Handle} = ct_telnet:open(telnet_server_conn1),
+ ok = ct_telnet:send(Handle, "echo hello-", [{newline,"own_nl\n"}]),
+ {ok,["hello-own_nl"]} = ct_telnet:expect(Handle, ["hello-own_nl"]),
+ ok = ct_telnet:close(Handle),
+ ok.
diff --git a/lib/common_test/test_server/configure.in b/lib/common_test/test_server/configure.in
index 0511d126b4..e07bd4c2aa 100644
--- a/lib/common_test/test_server/configure.in
+++ b/lib/common_test/test_server/configure.in
@@ -459,11 +459,11 @@ dnl Freely inspired by AC_TRY_LINK. (Maybe better to create a
dnl AC_LANG_JAVA instead...)
AC_DEFUN(ERL_TRY_LINK_JAVA,
[java_link='$JAVAC conftest.java 1>&AC_FD_CC'
-changequote(�, �)dnl
+changequote(, )dnl
cat > conftest.java <<EOF
-�$1�
+$1
class conftest { public static void main(String[] args) {
- �$2�
+ $2
; return; }}
EOF
changequote([, ])dnl
diff --git a/lib/compiler/src/Makefile b/lib/compiler/src/Makefile
index 97c73d0e07..c971e8844d 100644
--- a/lib/compiler/src/Makefile
+++ b/lib/compiler/src/Makefile
@@ -90,7 +90,6 @@ MODULES = \
rec_env \
sys_core_alias \
sys_core_bsm \
- sys_core_dsetel \
sys_core_fold \
sys_core_fold_lists \
sys_core_inline \
@@ -209,7 +208,6 @@ $(EBIN)/core_lint.beam: core_parse.hrl
$(EBIN)/core_parse.beam: core_parse.hrl $(EGEN)/core_parse.erl
$(EBIN)/core_pp.beam: core_parse.hrl
$(EBIN)/sys_core_alias.beam: core_parse.hrl
-$(EBIN)/sys_core_dsetel.beam: core_parse.hrl
$(EBIN)/sys_core_fold.beam: core_parse.hrl
$(EBIN)/sys_core_fold_lists.beam: core_parse.hrl
$(EBIN)/sys_core_inline.beam: core_parse.hrl
diff --git a/lib/compiler/src/beam_a.erl b/lib/compiler/src/beam_a.erl
index 1ac892a8f1..0bccad1ecd 100644
--- a/lib/compiler/src/beam_a.erl
+++ b/lib/compiler/src/beam_a.erl
@@ -122,10 +122,6 @@ rename_instr({bs_private_append=I,F,Sz,U,Src,Flags,Dst}) ->
{bs_init,F,{I,U,Flags},none,[Sz,Src],Dst};
rename_instr(bs_init_writable=I) ->
{bs_init,{f,0},I,1,[{x,0}],{x,0}};
-rename_instr({test,bs_match_string=Op,F,[Ctx,Bits,{string,Str}]}) when is_list(Str) ->
- %% When compiling from an old .S file. Starting from OTP 22, Str is a binary.
- <<Bs:Bits/bits,_/bits>> = list_to_binary(Str),
- {test,Op,F,[Ctx,Bs]};
rename_instr({put_map_assoc,Fail,S,D,R,L}) ->
{put_map,Fail,assoc,S,D,R,L};
rename_instr({put_map_exact,Fail,S,D,R,L}) ->
diff --git a/lib/compiler/src/beam_except.erl b/lib/compiler/src/beam_except.erl
index 09925b2872..28c89782c9 100644
--- a/lib/compiler/src/beam_except.erl
+++ b/lib/compiler/src/beam_except.erl
@@ -225,7 +225,11 @@ moves_from_stack(nil, I, Acc) ->
{reverse(Acc),I};
moves_from_stack({literal,[H|T]}, I, Acc) ->
Cons = {cons,tag_literal(H),tag_literal(T)},
- moves_from_stack(Cons, I, Acc).
+ moves_from_stack(Cons, I, Acc);
+moves_from_stack(_, _, _) ->
+ %% Not understood. Give up.
+ {[],-1}.
+
get_reg(R, Regs) ->
case Regs of
diff --git a/lib/compiler/src/beam_jump.erl b/lib/compiler/src/beam_jump.erl
index 8b0e3e32f8..6f50bfdb9c 100644
--- a/lib/compiler/src/beam_jump.erl
+++ b/lib/compiler/src/beam_jump.erl
@@ -182,18 +182,20 @@ eliminate_moves(Is) ->
eliminate_moves([{select,select_val,Reg,_,List}=I|Is], D0, Acc) ->
D = update_value_dict(List, Reg, D0),
eliminate_moves(Is, D, [I|Acc]);
-eliminate_moves([{label,Lbl},{block,[{set,[Dst],[Lit],move}|BlkIs]}=Blk0|Is],
- D, Acc0) ->
+eliminate_moves([{test,is_eq_exact,_,[Reg,Val]}=I,
+ {block,BlkIs0}|Is], D0, Acc) ->
+ D = update_unsafe_labels(I, D0),
+ RegVal = {Reg,Val},
+ BlkIs = eliminate_moves_blk(BlkIs0, RegVal),
+ eliminate_moves([{block,BlkIs}|Is], D, [I|Acc]);
+eliminate_moves([{label,Lbl},{block,BlkIs0}=Blk|Is], D, Acc0) ->
Acc = [{label,Lbl}|Acc0],
- case already_has_value(Lit, Lbl, Dst, D) andalso
- no_fallthrough(Acc0) of
- true ->
- %% Remove redundant 'move' instruction.
- Blk = {block,BlkIs},
- eliminate_moves([Blk|Is], D, Acc);
- false ->
- %% Keep 'move' instruction.
- eliminate_moves([Blk0|Is], D, Acc)
+ case {no_fallthrough(Acc0),D} of
+ {true,#{Lbl:={_,_}=RegVal}} ->
+ BlkIs = eliminate_moves_blk(BlkIs0, RegVal),
+ eliminate_moves([{block,BlkIs}|Is], D, Acc);
+ {_,_} ->
+ eliminate_moves([Blk|Is], D, Acc)
end;
eliminate_moves([{block,[]}|Is], D, Acc) ->
%% Empty blocks can prevent further jump optimizations.
@@ -203,17 +205,20 @@ eliminate_moves([I|Is], D0, Acc) ->
eliminate_moves(Is, D, [I|Acc]);
eliminate_moves([], _, Acc) -> reverse(Acc).
+eliminate_moves_blk([{set,[Dst],[_],move}|_]=Is, {_,Dst}) ->
+ Is;
+eliminate_moves_blk([{set,[Dst],[Lit],move}|Is], {Dst,Lit}) ->
+ %% Remove redundant 'move' instruction.
+ Is;
+eliminate_moves_blk([{set,[Dst],[_],move}|_]=Is, {Dst,_}) ->
+ Is;
+eliminate_moves_blk([{set,[_],[_],move}=I|Is], {_,_}=RegVal) ->
+ [I|eliminate_moves_blk(Is, RegVal)];
+eliminate_moves_blk(Is, _) -> Is.
+
no_fallthrough([I|_]) ->
is_unreachable_after(I).
-already_has_value(Lit, Lbl, Reg, D) ->
- case D of
- #{Lbl:={Reg,Lit}} ->
- true;
- #{} ->
- false
- end.
-
update_value_dict([Lit,{f,Lbl}|T], Reg, D0) ->
D = case D0 of
#{Lbl:=unsafe} -> D0;
diff --git a/lib/compiler/src/beam_kernel_to_ssa.erl b/lib/compiler/src/beam_kernel_to_ssa.erl
index d6e675ae72..410bafe0bb 100644
--- a/lib/compiler/src/beam_kernel_to_ssa.erl
+++ b/lib/compiler/src/beam_kernel_to_ssa.erl
@@ -707,11 +707,6 @@ bif_cg(#k_bif{op=#k_remote{mod=#k_atom{val=erlang},name=#k_atom{val=Name}},
%% internal_cg(Bif, [Arg], [Ret], Le, State) ->
%% {[Ainstr],State}.
-internal_cg(dsetelement, [Index0,Tuple0,New0], _Rs, _Le, St) ->
- [New,Tuple,#b_literal{val=Index1}] = ssa_args([New0,Tuple0,Index0], St),
- Index = #b_literal{val=Index1-1},
- Set = #b_set{op=set_tuple_element,args=[New,Tuple,Index]},
- {[Set],St};
internal_cg(make_fun, [Name0,Arity0|As], Rs, _Le, St0) ->
#k_atom{val=Name} = Name0,
#k_int{val=Arity} = Arity0,
diff --git a/lib/compiler/src/beam_ssa.erl b/lib/compiler/src/beam_ssa.erl
index 9c29c98064..a9977b0b1d 100644
--- a/lib/compiler/src/beam_ssa.erl
+++ b/lib/compiler/src/beam_ssa.erl
@@ -23,7 +23,7 @@
-export([add_anno/3,get_anno/2,get_anno/3,
clobbers_xregs/1,def/2,def_used/2,
definitions/1,
- dominators/1,
+ dominators/1,common_dominators/3,
flatmapfold_instrs_rpo/4,
fold_po/3,fold_po/4,fold_rpo/3,fold_rpo/4,
fold_instrs_rpo/4,
@@ -85,7 +85,8 @@
-type anno() :: #{atom() := any()}.
-type block_map() :: #{label():=b_blk()}.
--type dominator_map() :: #{label():=ordsets:ordset(label())}.
+-type dominator_map() :: #{label():=[label()]}.
+-type numbering_map() :: #{label():=non_neg_integer()}.
-type usage_map() :: #{b_var():=[{label(),b_set() | terminator()}]}.
-type definition_map() :: #{b_var():=b_set()}.
-type rename_map() :: #{b_var():=value()}.
@@ -108,7 +109,7 @@
'make_fun' | 'new_try_tag' |
'peek_message' | 'phi' | 'put_list' | 'put_map' | 'put_tuple' |
'raw_raise' | 'recv_next' | 'remove_message' | 'resume' |
- 'set_tuple_element' | 'succeeded' |
+ 'succeeded' |
'timeout' |
'wait' | 'wait_timeout'.
@@ -117,7 +118,8 @@
%% Primops only used internally during code generation.
-type cg_prim_op() :: 'bs_get' | 'bs_match_string' | 'bs_restore' | 'bs_skip' |
- 'copy' | 'put_tuple_arity' | 'put_tuple_element'.
+ 'copy' | 'put_tuple_arity' | 'put_tuple_element' |
+ 'set_tuple_element'.
-import(lists, [foldl/3,keyfind/3,mapfoldl/3,member/2,reverse/1]).
@@ -327,18 +329,41 @@ def_used(Ls, Blocks) ->
Preds = cerl_sets:from_list(Top),
def_used_1(Blks, Preds, [], []).
+%% dominators(BlockMap) -> {Dominators,Numbering}.
+%% Calculate the dominator tree, returning a map where each entry
+%% in the map is a list that gives the path from that block to
+%% the top of the dominator tree. (Note that the suffixes of the
+%% paths are shared with each other, which make the representation
+%% of the dominator tree highly memory-efficient.)
+%%
+%% The implementation is based on:
+%%
+%% http://www.hipersoft.rice.edu/grads/publications/dom14.pdf
+%% Cooper, Keith D.; Harvey, Timothy J; Kennedy, Ken (2001).
+%% A Simple, Fast Dominance Algorithm.
+
-spec dominators(Blocks) -> Result when
Blocks :: block_map(),
- Result :: dominator_map().
-
+ Result :: {dominator_map(), numbering_map()}.
dominators(Blocks) ->
Preds = predecessors(Blocks),
Top0 = rpo(Blocks),
- Top = [{L,map_get(L, Preds)} || L <- Top0],
+ Df = maps:from_list(number(Top0, 0)),
+ [{0,[]}|Top] = [{L,map_get(L, Preds)} || L <- Top0],
%% The flow graph for an Erlang function is reducible, and
%% therefore one traversal in reverse postorder is sufficient.
- iter_dominators(Top, #{}).
+ Acc = #{0=>[0]},
+ {dominators_1(Top, Df, Acc),Df}.
+
+%% common_dominators([Label], Dominators, Numbering) -> [Label].
+%% Calculate the common dominators for the given list of blocks
+%% and Dominators and Numbering as returned from dominators/1.
+
+-spec common_dominators([label()], dominator_map(), numbering_map()) -> [label()].
+common_dominators(Ls, Dom, Numbering) ->
+ Doms = [map_get(L, Dom) || L <- Ls],
+ dom_intersection(Doms, Numbering).
-spec fold_instrs_rpo(Fun, From, Acc0, Blocks) -> any() when
Fun :: fun((b_blk()|terminator(), any()) -> any()),
@@ -657,14 +682,37 @@ def_is([#b_set{dst=Dst}|Is], Def) ->
def_is(Is, [Dst|Def]);
def_is([], Def) -> Def.
-iter_dominators([{0,[]}|Ls], _Doms) ->
- Dom = [0],
- iter_dominators(Ls, #{0=>Dom});
-iter_dominators([{L,Preds}|Ls], Doms) ->
+dominators_1([{L,Preds}|Ls], Df, Doms) ->
DomPreds = [map_get(P, Doms) || P <- Preds, is_map_key(P, Doms)],
- Dom = ordsets:add_element(L, ordsets:intersection(DomPreds)),
- iter_dominators(Ls, Doms#{L=>Dom});
-iter_dominators([], Doms) -> Doms.
+ Dom = [L|dom_intersection(DomPreds, Df)],
+ dominators_1(Ls, Df, Doms#{L=>Dom});
+dominators_1([], _Df, Doms) -> Doms.
+
+dom_intersection([S], _Df) ->
+ S;
+dom_intersection([S|Ss], Df) ->
+ dom_intersection(S, Ss, Df).
+
+dom_intersection(S1, [S2|Ss], Df) ->
+ dom_intersection(dom_intersection_1(S1, S2, Df), Ss, Df);
+dom_intersection(S, [], _Df) -> S.
+
+dom_intersection_1([E1|Es1]=Set1, [E2|Es2]=Set2, Df) ->
+ %% Blocks are numbered in the order they are found in
+ %% reverse postorder.
+ #{E1:=Df1,E2:=Df2} = Df,
+ if Df1 > Df2 ->
+ dom_intersection_1(Es1, Set2, Df);
+ Df2 > Df1 ->
+ dom_intersection_1(Es2, Set1, Df); %switch arguments!
+ true -> %Set1 == Set2
+ %% The common suffix of the sets is the intersection.
+ Set1
+ end.
+
+number([L|Ls], N) ->
+ [{L,N}|number(Ls, N+1)];
+number([], _) -> [].
fold_rpo_1([L|Ls], Fun, Blocks, Acc0) ->
Block = map_get(L, Blocks),
diff --git a/lib/compiler/src/beam_ssa_bsm.erl b/lib/compiler/src/beam_ssa_bsm.erl
index 466337db0e..382e6f635e 100644
--- a/lib/compiler/src/beam_ssa_bsm.erl
+++ b/lib/compiler/src/beam_ssa_bsm.erl
@@ -300,7 +300,8 @@ get_fa(#b_function{ anno = Anno }) ->
promotions = #{} :: promotion_map() }).
alias_matched_binaries(Blocks0, Counter, AliasMap) when AliasMap =/= #{} ->
- State0 = #amb{ dominators = beam_ssa:dominators(Blocks0),
+ {Dominators, _} = beam_ssa:dominators(Blocks0),
+ State0 = #amb{ dominators = Dominators,
match_aliases = AliasMap,
cnt = Counter },
{Blocks, State} = beam_ssa:mapfold_blocks_rpo(fun amb_1/3, [0], State0,
@@ -347,7 +348,7 @@ amb_get_alias(#b_var{}=Arg, Lbl, State) ->
%% Our context may not have been created yet, so we skip assigning
%% an alias unless the given block is among our dominators.
Dominators = maps:get(Lbl, State#amb.dominators),
- case ordsets:is_element(AliasAfter, Dominators) of
+ case member(AliasAfter, Dominators) of
true -> amb_create_alias(Arg, Context, Lbl, State);
false -> {Arg, State}
end;
@@ -444,6 +445,7 @@ combine_matches({Fs0, ModInfo}) ->
combine_matches(#b_function{bs=Blocks0,cnt=Counter0}=F, ModInfo) ->
case funcinfo_get(F, has_bsm_ops, ModInfo) of
true ->
+ {Dominators, _} = beam_ssa:dominators(Blocks0),
{Blocks1, State} =
beam_ssa:mapfold_blocks_rpo(
fun(Lbl, #b_blk{is=Is0}=Block0, State0) ->
@@ -451,7 +453,7 @@ combine_matches(#b_function{bs=Blocks0,cnt=Counter0}=F, ModInfo) ->
{Block0#b_blk{is=Is}, State}
end, [0],
#cm{ definitions = beam_ssa:definitions(Blocks0),
- dominators = beam_ssa:dominators(Blocks0),
+ dominators = Dominators,
blocks = Blocks0 },
Blocks0),
@@ -491,7 +493,7 @@ cm_handle_priors(Src, DstCtx, Bool, Acc, MatchSeq, Lbl, State0) ->
%% dominate us.
Dominators = maps:get(Lbl, State0#cm.dominators, []),
[Ctx || {ValidAfter, Ctx} <- Priors,
- ordsets:is_element(ValidAfter, Dominators)];
+ member(ValidAfter, Dominators)];
error ->
[]
end,
diff --git a/lib/compiler/src/beam_ssa_opt.erl b/lib/compiler/src/beam_ssa_opt.erl
index ca5eefe4fc..6e548dd529 100644
--- a/lib/compiler/src/beam_ssa_opt.erl
+++ b/lib/compiler/src/beam_ssa_opt.erl
@@ -84,7 +84,7 @@ phase([FuncId | Ids], Ps, StMap, FuncDb0) ->
phase(Ids, Ps, StMap#{ FuncId => St }, FuncDb)
catch
Class:Error:Stack ->
- #b_local{name=Name,arity=Arity} = FuncId,
+ #b_local{name=#b_literal{val=Name},arity=Arity} = FuncId,
io:fwrite("Function: ~w/~w\n", [Name,Arity]),
erlang:raise(Class, Error, Stack)
end;
@@ -164,12 +164,14 @@ repeated_passes(Opts) ->
epilogue_passes(Opts) ->
Ps = [?PASS(ssa_opt_type_finish),
?PASS(ssa_opt_float),
- ?PASS(ssa_opt_live), %One last time to clean up the
- %mess left by the float pass.
+ ?PASS(ssa_opt_sw),
+
+ %% Run live one more time to clean up after the float and sw
+ %% passes.
+ ?PASS(ssa_opt_live),
?PASS(ssa_opt_bsm),
?PASS(ssa_opt_bsm_units),
?PASS(ssa_opt_bsm_shortcut),
- ?PASS(ssa_opt_sw),
?PASS(ssa_opt_blockify),
?PASS(ssa_opt_sink),
?PASS(ssa_opt_merge_blocks),
@@ -249,22 +251,14 @@ fdb_update(Caller, Callee, FuncDb) ->
FuncDb#{ Caller => CallerVertex#func_info{out=Calls},
Callee => CalleeVertex#func_info{in=CalledBy} }.
-%% Returns the post-order of all local calls in this module. That is, it starts
-%% with the functions that don't call any others and then walks up the call
-%% chain.
+%% Returns the post-order of all local calls in this module. That is,
+%% called functions will be ordered before the functions calling them.
%%
%% Functions where module-level optimization is disabled are added last in
%% arbitrary order.
get_call_order_po(StMap, FuncDb) ->
- Leaves = maps:fold(fun(Id, #func_info{out=[]}, Acc) ->
- [Id | Acc];
- (_, _, Acc) ->
- Acc
- end, [], FuncDb),
-
- Order = gco_po_1(sort(Leaves), FuncDb, [], #{}),
-
+ Order = gco_po(FuncDb),
Order ++ maps:fold(fun(K, _V, Acc) ->
case is_map_key(K, FuncDb) of
false -> [K | Acc];
@@ -272,20 +266,23 @@ get_call_order_po(StMap, FuncDb) ->
end
end, [], StMap).
-gco_po_1([Id | Ids], FuncDb, Children, Seen) when not is_map_key(Id, Seen) ->
- [Id | gco_po_1(Ids, FuncDb, [Id | Children], Seen#{ Id => true })];
-gco_po_1([_Id | Ids], FuncDb, Children, Seen) ->
- gco_po_1(Ids, FuncDb, Children, Seen);
-gco_po_1([], FuncDb, [_|_]=Children, Seen) ->
- gco_po_1(gco_po_parents(Children, FuncDb), FuncDb, [], Seen);
-gco_po_1([], _FuncDb, [], _Seen) ->
- [].
+gco_po(FuncDb) ->
+ All = sort(maps:keys(FuncDb)),
+ {RPO,_} = gco_rpo(All, FuncDb, cerl_sets:new(), []),
+ reverse(RPO).
-gco_po_parents([Child | Children], FuncDb) ->
- #{ Child := #func_info{in=Parents}} = FuncDb,
- Parents ++ gco_po_parents(Children, FuncDb);
-gco_po_parents([], _FuncDb) ->
- [].
+gco_rpo([Id|Ids], FuncDb, Seen0, Acc0) ->
+ case cerl_sets:is_element(Id, Seen0) of
+ true ->
+ gco_rpo(Ids, FuncDb, Seen0, Acc0);
+ false ->
+ #func_info{out=Successors} = map_get(Id, FuncDb),
+ Seen1 = cerl_sets:add_element(Id, Seen0),
+ {Acc,Seen} = gco_rpo(Successors, FuncDb, Seen1, Acc0),
+ gco_rpo(Ids, FuncDb, Seen, [Id|Acc])
+ end;
+gco_rpo([], _, Seen, Acc) ->
+ {Acc,Seen}.
%%%
%%% Trivial sub passes.
@@ -852,6 +849,7 @@ cse_expr(#b_set{op=Op,args=Args}=I) ->
cse_suitable(#b_set{op=get_hd}) -> true;
cse_suitable(#b_set{op=get_tl}) -> true;
cse_suitable(#b_set{op=put_list}) -> true;
+cse_suitable(#b_set{op=get_tuple_element}) -> true;
cse_suitable(#b_set{op=put_tuple}) -> true;
cse_suitable(#b_set{op={bif,tuple_size}}) ->
%% Doing CSE for tuple_size/1 can prevent the
@@ -1835,12 +1833,16 @@ opt_tup_size_is([], _, _, _Acc) -> none.
%%%
ssa_opt_sw({#st{ssa=Linear0,cnt=Count0}=St, FuncDb}) ->
- {Linear,Count} = opt_sw(Linear0, #{}, Count0, []),
+ {Linear,Count} = opt_sw(Linear0, Count0, []),
{St#st{ssa=Linear,cnt=Count}, FuncDb}.
-opt_sw([{L,#b_blk{is=Is,last=#b_switch{}=Last0}=Blk0}|Bs], Phis0, Count0, Acc) ->
- Phis = opt_sw_phis(Is, Phis0),
- case opt_sw_last(Last0, Phis) of
+opt_sw([{L,#b_blk{is=Is,last=#b_switch{}=Sw0}=Blk0}|Bs], Count0, Acc) ->
+ %% Ensure that no label in the switch list is the same
+ %% as the failure label.
+ #b_switch{fail=Fail,list=List0} = Sw0,
+ List = [{Val,Lbl} || {Val,Lbl} <- List0, Lbl =/= Fail],
+ Sw1 = beam_ssa:normalize(Sw0#b_switch{list=List}),
+ case Sw1 of
#b_switch{arg=Arg,fail=Fail,list=[{Lit,Lbl}]} ->
%% Rewrite a single value switch to a br.
Bool = #b_var{name={'@ssa_bool',Count0}},
@@ -1848,7 +1850,7 @@ opt_sw([{L,#b_blk{is=Is,last=#b_switch{}=Last0}=Blk0}|Bs], Phis0, Count0, Acc) -
IsEq = #b_set{op={bif,'=:='},dst=Bool,args=[Arg,Lit]},
Br = #b_br{bool=Bool,succ=Lbl,fail=Fail},
Blk = Blk0#b_blk{is=Is++[IsEq],last=Br},
- opt_sw(Bs, Phis, Count, [{L,Blk}|Acc]);
+ opt_sw(Bs, Count, [{L,Blk}|Acc]);
#b_switch{arg=Arg,fail=Fail,
list=[{#b_literal{val=B1},Lbl},{#b_literal{val=B2},Lbl}]}
when B1 =:= not B2 ->
@@ -1858,71 +1860,18 @@ opt_sw([{L,#b_blk{is=Is,last=#b_switch{}=Last0}=Blk0}|Bs], Phis0, Count0, Acc) -
IsBool = #b_set{op={bif,is_boolean},dst=Bool,args=[Arg]},
Br = #b_br{bool=Bool,succ=Lbl,fail=Fail},
Blk = Blk0#b_blk{is=Is++[IsBool],last=Br},
- opt_sw(Bs, Phis, Count, [{L,Blk}|Acc]);
- Last0 ->
- opt_sw(Bs, Phis, Count0, [{L,Blk0}|Acc]);
- Last ->
- Blk = Blk0#b_blk{last=Last},
- opt_sw(Bs, Phis, Count0, [{L,Blk}|Acc])
+ opt_sw(Bs, Count, [{L,Blk}|Acc]);
+ Sw0 ->
+ opt_sw(Bs, Count0, [{L,Blk0}|Acc]);
+ Sw ->
+ Blk = Blk0#b_blk{last=Sw},
+ opt_sw(Bs, Count0, [{L,Blk}|Acc])
end;
-opt_sw([{L,#b_blk{is=Is}=Blk}|Bs], Phis0, Count, Acc) ->
- Phis = opt_sw_phis(Is, Phis0),
- opt_sw(Bs, Phis, Count, [{L,Blk}|Acc]);
-opt_sw([], _Phis, Count, Acc) ->
+opt_sw([{L,#b_blk{}=Blk}|Bs], Count, Acc) ->
+ opt_sw(Bs, Count, [{L,Blk}|Acc]);
+opt_sw([], Count, Acc) ->
{reverse(Acc),Count}.
-opt_sw_phis([#b_set{op=phi,dst=Dst,args=Args}|Is], Phis) ->
- case opt_sw_literals(Args, []) of
- error ->
- opt_sw_phis(Is, Phis);
- Literals ->
- opt_sw_phis(Is, Phis#{Dst=>Literals})
- end;
-opt_sw_phis(_, Phis) -> Phis.
-
-opt_sw_last(#b_switch{arg=Arg,fail=Fail,list=List0}=Sw0, Phis) ->
- case Phis of
- #{Arg:=Values0} ->
- Values = gb_sets:from_list(Values0),
-
- %% Prune the switch list to only contain the possible values.
- List1 = [P || {Lit,_}=P <- List0, gb_sets:is_member(Lit, Values)],
-
- %% Now test whether the failure label can ever be reached.
- Sw = case gb_sets:size(Values) =:= length(List1) of
- true ->
- %% The switch list has the same number of values as the phi node.
- %% The values must be the same, because the values that were not
- %% possible were pruned from the switch list. Therefore, the
- %% failure label can't possibly be reached, and we can choose a
- %% a new failure label by picking a value from the list.
- case List1 of
- [{#b_literal{},Lbl}|List] ->
- Sw0#b_switch{fail=Lbl,list=List};
- [] ->
- Sw0#b_switch{list=List1}
- end;
- false ->
- %% There are some values in the phi node that are not in the
- %% switch list; thus, the failure label can still be reached.
- Sw0
- end,
- beam_ssa:normalize(Sw);
- #{} ->
- %% Ensure that no label in the switch list is the same
- %% as the failure label.
- List = [{Val,Lbl} || {Val,Lbl} <- List0, Lbl =/= Fail],
- Sw = Sw0#b_switch{list=List},
- beam_ssa:normalize(Sw)
- end.
-
-opt_sw_literals([{#b_literal{}=Lit,_}|T], Acc) ->
- opt_sw_literals(T, [Lit|Acc]);
-opt_sw_literals([_|_], _Acc) ->
- error;
-opt_sw_literals([], Acc) -> Acc.
-
-
%%%
%%% Merge blocks.
%%%
@@ -2021,7 +1970,7 @@ do_ssa_opt_sink(Linear, Defs, #st{ssa=Blocks0}=St) ->
Used = used_blocks(Linear, Defs, []),
%% Calculate dominators.
- Dom0 = beam_ssa:dominators(Blocks0),
+ {Dom,Numbering} = beam_ssa:dominators(Blocks0),
%% It is not safe to move get_tuple_element instructions to blocks
%% that begin with certain instructions. It is also unsafe to move
@@ -2029,20 +1978,10 @@ do_ssa_opt_sink(Linear, Defs, #st{ssa=Blocks0}=St) ->
%% unsafe moves, pretend that the unsuitable blocks are not
%% dominators.
Unsuitable = unsuitable(Linear, Blocks0),
- Dom = case gb_sets:is_empty(Unsuitable) of
- true ->
- Dom0;
- false ->
- F = fun(_, DomBy) ->
- [L || L <- DomBy,
- not gb_sets:is_element(L, Unsuitable)]
- end,
- maps:map(F, Dom0)
- end,
%% Calculate new positions for get_tuple_element instructions. The new
%% position is a block that dominates all uses of the variable.
- DefLoc = new_def_locations(Used, Defs, Dom),
+ DefLoc = new_def_locations(Used, Defs, Dom, Numbering, Unsuitable),
%% Now move all suitable get_tuple_element instructions to their
%% new blocks.
@@ -2136,50 +2075,42 @@ unsuitable_loop_1([P|Ps], Blocks, Predecessors, Acc0) ->
end;
unsuitable_loop_1([], _, _, Acc) -> Acc.
-%% new_def_locations([{Variable,[UsedInBlock]}|Vs], Defs, Dominators) ->
-%% [{Variable,NewDefinitionBlock}]
-%% Calculate new locations for get_tuple_element instructions. For each
-%% variable, the new location is a block that dominates all uses of
-%% variable and as near to the uses of as possible. If no such block
-%% distinct from the block where the instruction currently is, the
-%% variable will not be included in the result list.
+%% new_def_locations([{Variable,[UsedInBlock]}|Vs], Defs,
+%% Dominators, Numbering, Unsuitable) ->
+%% [{Variable,NewDefinitionBlock}]
+%%
+%% Calculate new locations for get_tuple_element instructions. For
+%% each variable, the new location is a block that dominates all uses
+%% of the variable and as near to the uses of as possible.
-new_def_locations([{V,UsedIn}|Vs], Defs, Dom) ->
+new_def_locations([{V,UsedIn}|Vs], Defs, Dom, Numbering, Unsuitable) ->
DefIn = map_get(V, Defs),
- case common_dom(UsedIn, DefIn, Dom) of
- [] ->
- new_def_locations(Vs, Defs, Dom);
- [_|_]=BetterDef ->
- L = most_dominated(BetterDef, Dom),
- [{V,L}|new_def_locations(Vs, Defs, Dom)]
- end;
-new_def_locations([], _, _) -> [].
-
-common_dom([L|Ls], DefIn, Dom) ->
- DomBy0 = map_get(L, Dom),
- DomBy = ordsets:subtract(DomBy0, map_get(DefIn, Dom)),
- common_dom_1(Ls, Dom, DomBy).
-
-common_dom_1(_, _, []) ->
- [];
-common_dom_1([L|Ls], Dom, [_|_]=DomBy0) ->
- DomBy1 = map_get(L, Dom),
- DomBy = ordsets:intersection(DomBy0, DomBy1),
- common_dom_1(Ls, Dom, DomBy);
-common_dom_1([], _, DomBy) -> DomBy.
-
-most_dominated([L|Ls], Dom) ->
- most_dominated(Ls, L, map_get(L, Dom), Dom).
-
-most_dominated([L|Ls], L0, DomBy, Dom) ->
- case member(L, DomBy) of
+ Common = common_dominator(UsedIn, Dom, Numbering, Unsuitable),
+ case member(Common, map_get(DefIn, Dom)) of
true ->
- most_dominated(Ls, L0, DomBy, Dom);
+ %% The common dominator is either DefIn or an
+ %% ancestor of DefIn.
+ new_def_locations(Vs, Defs, Dom, Numbering, Unsuitable);
false ->
- most_dominated(Ls, L, map_get(L, Dom), Dom)
+ %% We have found a suitable descendant of DefIn,
+ %% to which the get_tuple_element instruction can
+ %% be sunk.
+ [{V,Common}|new_def_locations(Vs, Defs, Dom, Numbering, Unsuitable)]
end;
-most_dominated([], L, _, _) -> L.
+new_def_locations([], _, _, _, _) -> [].
+common_dominator(Ls0, Dom, Numbering, Unsuitable) ->
+ [Common|_] = beam_ssa:common_dominators(Ls0, Dom, Numbering),
+ case gb_sets:is_member(Common, Unsuitable) of
+ true ->
+ %% It is not allowed to place the instruction here. Try
+ %% to find another suitable dominating block by going up
+ %% one step in the dominator tree.
+ [Common,OneUp|_] = map_get(Common, Dom),
+ common_dominator([OneUp], Dom, Numbering, Unsuitable);
+ false ->
+ Common
+ end.
%% Move get_tuple_element instructions to their new locations.
@@ -2219,7 +2150,6 @@ insert_def_is([#b_set{op=Op}=I|Is]=Is0, V, Def) ->
Action0 = case Op of
call -> beyond;
'catch_end' -> beyond;
- set_tuple_element -> beyond;
timeout -> beyond;
_ -> here
end,
diff --git a/lib/compiler/src/beam_ssa_pre_codegen.erl b/lib/compiler/src/beam_ssa_pre_codegen.erl
index 274f78052d..bad43a9c4e 100644
--- a/lib/compiler/src/beam_ssa_pre_codegen.erl
+++ b/lib/compiler/src/beam_ssa_pre_codegen.erl
@@ -124,6 +124,7 @@ passes(Opts) ->
false -> ignore;
true -> ?PASS(fix_tuples)
end,
+ ?PASS(use_set_tuple_element),
?PASS(place_frames),
?PASS(fix_receives),
@@ -857,6 +858,202 @@ fix_tuples(#st{ssa=Blocks0,cnt=Count0}=St) ->
St#st{ssa=Blocks,cnt=Count}.
%%%
+%%% Introduce the set_tuple_element instructions to make
+%%% multiple-field record updates faster.
+%%%
+%%% The expansion of record field updates, when more than one field is
+%%% updated, but not a majority of the fields, will create a sequence of
+%%% calls to `erlang:setelement(Index, Value, Tuple)` where Tuple in the
+%%% first call is the original record tuple, and in the subsequent calls
+%%% Tuple is the result of the previous call. Furthermore, all Index
+%%% values are constant positive integers, and the first call to
+%%% `setelement` will have the greatest index. Thus all the following
+%%% calls do not actually need to test at run-time whether Tuple has type
+%%% tuple, nor that the index is within the tuple bounds.
+%%%
+%%% Since this optimization introduces destructive updates, it used to
+%%% be done as the very last Core Erlang pass before going to
+%%% lower-level code. However, it turns out that this kind of destructive
+%%% updates are awkward also in SSA code and can prevent or complicate
+%%% type analysis and aggressive optimizations.
+%%%
+%%% NOTE: Because there no write barriers in the system, this kind of
+%%% optimization can only be done when we are sure that garbage
+%%% collection will not be triggered between the creation of the tuple
+%%% and the destructive updates - otherwise we might insert pointers
+%%% from an older generation to a newer.
+%%%
+
+use_set_tuple_element(#st{ssa=Blocks0}=St) ->
+ Uses = count_uses(Blocks0),
+ RPO = reverse(beam_ssa:rpo(Blocks0)),
+ Blocks = use_ste_1(RPO, Uses, Blocks0),
+ St#st{ssa=Blocks}.
+
+use_ste_1([L|Ls], Uses, Blocks0) ->
+ {Blk0,Blocks} = use_ste_across(L, Uses, Blocks0),
+ #b_blk{is=Is0} = Blk0,
+ case use_ste_is(Is0, Uses) of
+ Is0 ->
+ use_ste_1(Ls, Uses, Blocks);
+ Is ->
+ Blk = Blk0#b_blk{is=Is},
+ use_ste_1(Ls, Uses, Blocks#{L:=Blk})
+ end;
+use_ste_1([], _, Blocks) -> Blocks.
+
+%%% Optimize within a single block.
+
+use_ste_is([#b_set{}=I|Is0], Uses) ->
+ Is = use_ste_is(Is0, Uses),
+ case extract_ste(I) of
+ none ->
+ [I|Is];
+ Extracted ->
+ use_ste_call(Extracted, I, Is, Uses)
+ end;
+use_ste_is([], _Uses) -> [].
+
+use_ste_call({Dst0,Pos0,_Var0,_Val0}, Call1, Is0, Uses) ->
+ case get_ste_call(Is0, []) of
+ {Prefix,{Dst1,Pos1,Dst0,Val1},Call2,Is}
+ when Pos1 > 0, Pos0 > Pos1 ->
+ case is_single_use(Dst0, Uses) of
+ true ->
+ Call = Call1#b_set{dst=Dst1},
+ Args = [Val1,Dst1,#b_literal{val=Pos1-1}],
+ Dsetel = Call2#b_set{op=set_tuple_element,
+ dst=Dst0,
+ args=Args},
+ [Call|Prefix] ++ [Dsetel|Is];
+ false ->
+ [Call1|Is0]
+ end;
+ _ ->
+ [Call1|Is0]
+ end.
+
+get_ste_call([#b_set{op=get_tuple_element}=I|Is], Acc) ->
+ get_ste_call(Is, [I|Acc]);
+get_ste_call([#b_set{op=call}=I|Is], Acc) ->
+ case extract_ste(I) of
+ none ->
+ none;
+ Extracted ->
+ {reverse(Acc),Extracted,I,Is}
+ end;
+get_ste_call(_, _) -> none.
+
+extract_ste(#b_set{op=call,dst=Dst,
+ args=[#b_remote{mod=#b_literal{val=M},
+ name=#b_literal{val=F}}|Args]}) ->
+ case {M,F,Args} of
+ {erlang,setelement,[#b_literal{val=Pos},Tuple,Val]} ->
+ {Dst,Pos,Tuple,Val};
+ {_,_,_} ->
+ none
+ end;
+extract_ste(#b_set{}) -> none.
+
+%%% Optimize accross blocks within a try/catch block.
+
+use_ste_across(L, Uses, Blocks) ->
+ case map_get(L, Blocks) of
+ #b_blk{last=#b_br{bool=#b_var{}}}=Blk ->
+ try
+ use_ste_across_1(L, Blk, Uses, Blocks)
+ catch
+ throw:not_possible ->
+ {Blk,Blocks}
+ end;
+ #b_blk{}=Blk ->
+ {Blk,Blocks}
+ end.
+
+use_ste_across_1(L, Blk0, Uses, Blocks0) ->
+ #b_blk{is=IsThis,last=#b_br{bool=Bool,succ=Next}} = Blk0,
+ case reverse(IsThis) of
+ [#b_set{op=succeeded,dst=Bool,args=[Result]}=Succ0,
+ #b_set{op=call,args=[#b_remote{}|_],dst=Result}=Call1|Prefix] ->
+ case is_single_use(Bool, Uses) andalso
+ is_n_uses(2, Result, Uses) of
+ true -> ok;
+ false -> throw(not_possible)
+ end,
+ Call2 = use_ste_across_next(Next, Uses, Blocks0),
+ Is = [Call1,Call2],
+ case use_ste_is(Is, decrement_uses(Result, Uses)) of
+ [#b_set{}=Call,#b_set{op=set_tuple_element}=Ste] ->
+ Blocks1 = use_ste_fix_next(Ste, Next, Blocks0),
+ Succ = Succ0#b_set{args=[Call#b_set.dst]},
+ Blk = Blk0#b_blk{is=reverse(Prefix, [Call,Succ])},
+ Blocks = Blocks1#{L:=Blk},
+ {Blk,Blocks};
+ _ ->
+ throw(not_possible)
+ end;
+ _ ->
+ throw(not_possible)
+ end.
+
+use_ste_across_next(Next, Uses, Blocks) ->
+ case map_get(Next, Blocks) of
+ #b_blk{is=[#b_set{op=call,dst=Result,args=[#b_remote{}|_]}=Call,
+ #b_set{op=succeeded,dst=Bool,args=[Result]}],
+ last=#b_br{bool=Bool}} ->
+ case is_single_use(Bool, Uses) andalso
+ is_n_uses(2, Result, Uses) of
+ true -> ok;
+ false -> throw(not_possible)
+ end,
+ Call;
+ #b_blk{} ->
+ throw(not_possible)
+ end.
+
+use_ste_fix_next(Ste, Next, Blocks) ->
+ Blk0 = map_get(Next, Blocks),
+ #b_blk{is=[#b_set{op=call},#b_set{op=succeeded}],last=Br0} = Blk0,
+ Br = beam_ssa:normalize(Br0#b_br{bool=#b_literal{val=true}}),
+ Blk = Blk0#b_blk{is=[Ste],last=Br},
+ Blocks#{Next:=Blk}.
+
+%% Count how many times each variable is used.
+
+count_uses(Blocks) ->
+ count_uses_blk(maps:values(Blocks), #{}).
+
+count_uses_blk([#b_blk{is=Is,last=Last}|Bs], CountMap0) ->
+ F = fun(I, CountMap) ->
+ foldl(fun(Var, Acc) ->
+ case Acc of
+ #{Var:=3} -> Acc;
+ #{Var:=C} -> Acc#{Var:=C+1};
+ #{} -> Acc#{Var=>1}
+ end
+ end, CountMap, beam_ssa:used(I))
+ end,
+ CountMap = F(Last, foldl(F, CountMap0, Is)),
+ count_uses_blk(Bs, CountMap);
+count_uses_blk([], CountMap) -> CountMap.
+
+decrement_uses(V, Uses) ->
+ #{V:=C} = Uses,
+ Uses#{V:=C-1}.
+
+is_n_uses(N, V, Uses) ->
+ case Uses of
+ #{V:=N} -> true;
+ #{} -> false
+ end.
+
+is_single_use(V, Uses) ->
+ case Uses of
+ #{V:=1} -> true;
+ #{} -> false
+ end.
+
+%%%
%%% Find out where frames should be placed.
%%%
@@ -874,7 +1071,7 @@ fix_tuples(#st{ssa=Blocks0,cnt=Count0}=St) ->
%% a stack frame or set up a stack frame with a different size.
place_frames(#st{ssa=Blocks}=St) ->
- Doms = beam_ssa:dominators(Blocks),
+ {Doms,_} = beam_ssa:dominators(Blocks),
Ls = beam_ssa:rpo(Blocks),
Tried = gb_sets:empty(),
Frames0 = [],
@@ -1001,7 +1198,7 @@ phi_predecessors(L, Blocks) ->
is_dominated_by(L, DomBy, Doms) ->
DominatedBy = map_get(L, Doms),
- ordsets:is_element(DomBy, DominatedBy).
+ member(DomBy, DominatedBy).
%% need_frame(#b_blk{}) -> true|false.
%% Test whether any of the instructions in the block requires a stack frame.
@@ -1993,11 +2190,12 @@ reserve_zregs(Blocks, Intervals, Res) ->
end,
beam_ssa:fold_rpo(F, [0], Res, Blocks).
-reserve_zreg([#b_set{op=call,dst=Dst}],
- #b_br{bool=Dst}, _ShortLived, A) ->
- %% If type optimization has determined that the result of a call can be
- %% used directly in a branch, we must avoid reserving a z register or code
- %% generation will fail.
+reserve_zreg([#b_set{op=Op,dst=Dst}],
+ #b_br{bool=Dst}, _ShortLived, A) when Op =:= call;
+ Op =:= get_tuple_element ->
+ %% If type optimization has determined that the result of these
+ %% instructions can be used directly in a branch, we must avoid reserving a
+ %% z register or code generation will fail.
A;
reserve_zreg([#b_set{op={bif,tuple_size},dst=Dst},
#b_set{op={bif,'=:='},args=[Dst,Val]}], Last, ShortLived, A0) ->
diff --git a/lib/compiler/src/beam_ssa_type.erl b/lib/compiler/src/beam_ssa_type.erl
index 32583f5abf..aa4720d222 100644
--- a/lib/compiler/src/beam_ssa_type.erl
+++ b/lib/compiler/src/beam_ssa_type.erl
@@ -23,7 +23,8 @@
-include("beam_ssa_opt.hrl").
-import(lists, [all/2,any/2,droplast/1,foldl/3,last/1,member/2,
- partition/2,reverse/1,sort/1]).
+ keyfind/3,partition/2,reverse/1,reverse/2,
+ seq/2,sort/1,split/2]).
-define(UNICODE_INT, #t_integer{elements={0,16#10FFFF}}).
@@ -44,12 +45,13 @@
-record(t_bs_match, {type :: type()}).
-record(t_tuple, {size=0 :: integer(),
exact=false :: boolean(),
- elements=[] :: [any()]
- }).
+ %% Known element types (1-based index), unknown elements are
+ %% are assumed to be 'any'.
+ elements=#{} :: #{ non_neg_integer() => type() }}).
-type type() :: 'any' | 'none' |
#t_atom{} | #t_integer{} | #t_bs_match{} | #t_tuple{} |
- {'binary',pos_integer()} | 'cons' | 'float' | 'list' | 'map' | 'nil' |'number'.
+ {'binary',pos_integer()} | 'cons' | 'float' | 'list' | 'map' | 'nil' | 'number'.
-type type_db() :: #{beam_ssa:var_name():=type()}.
-spec opt_start(Linear, Args, Anno, FuncDb) -> {Linear, FuncDb} when
@@ -123,7 +125,7 @@ opt_continue_1(Linear0, Args, Id, Ts, FuncDb0) ->
ls=#{0=>Ts,?BADARG_BLOCK=>#{}},
once=UsedOnce },
- {Linear, FuncDb, NewRet} = opt_1(Linear0, D, []),
+ {Linear, FuncDb, NewRet} = opt(Linear0, D, []),
case FuncDb of
#{ Id := Entry0 } ->
@@ -166,8 +168,11 @@ opt_finish_1([Arg | Args], [TypeMap | TypeMaps], ParamInfo0) ->
opt_finish_1([], [], ParamInfo) ->
ParamInfo.
-validator_anno(#t_tuple{size=Size,exact=Exact}) ->
- beam_validator:type_anno(tuple, Size, Exact);
+validator_anno(#t_tuple{size=Size,exact=Exact,elements=Elements0}) ->
+ Elements = maps:fold(fun(Index, Type, Acc) ->
+ Acc#{ Index => validator_anno(Type) }
+ end, #{}, Elements0),
+ beam_validator:type_anno(tuple, Size, Exact, Elements);
validator_anno(#t_integer{elements={Same,Same}}) ->
beam_validator:type_anno(integer, Same);
validator_anno(#t_integer{}) ->
@@ -188,57 +193,42 @@ get_func_id(Anno) ->
#{func_info:={_Mod, Name, Arity}} = Anno,
#b_local{name=#b_literal{val=Name}, arity=Arity}.
-opt_1([{L,Blk}|Bs], #d{ls=Ls}=D, Acc) ->
+opt([{L,Blk}|Bs], #d{ls=Ls}=D, Acc) ->
case Ls of
#{L:=Ts} ->
- opt_2(L, Blk, Bs, Ts, D, Acc);
+ opt_1(L, Blk, Bs, Ts, D, Acc);
#{} ->
%% This block is never reached. Discard it.
- opt_1(Bs, D, Acc)
+ opt(Bs, D, Acc)
end;
-opt_1([], D, Acc) ->
+opt([], D, Acc) ->
#d{func_db=FuncDb,ret_type=NewRet} = D,
{reverse(Acc), FuncDb, NewRet}.
-opt_2(L, #b_blk{is=Is0}=Blk0, Bs, Ts, #d{sub=Sub}=D0, Acc) ->
- case Is0 of
- [#b_set{op=call,dst=Dst,
- args=[#b_remote{mod=#b_literal{val=Mod},
- name=#b_literal{val=Name}}=Rem|Args0]}=I0] ->
- case erl_bifs:is_exit_bif(Mod, Name, length(Args0)) of
- true ->
- %% This call will never reach the successor block.
- %% Rewrite the terminator to a 'ret', and remove
- %% all type information for this label. That will
- %% simplify the phi node in the former successor.
- Args = simplify_args(Args0, Sub, Ts),
- I = I0#b_set{args=[Rem|Args]},
- Ret = #b_ret{arg=Dst},
- Blk = Blk0#b_blk{is=[I],last=Ret},
- Ls = maps:remove(L, D0#d.ls),
-
- %% We potentially lack a return value.
- RetType = join([none | D0#d.ret_type]),
-
- D = D0#d{ls=Ls,ret_type=[RetType]},
- opt_1(Bs, D, [{L,Blk} | Acc]);
- false ->
- opt_3(L, Blk0, Bs, Ts, D0, Acc)
- end;
- _ ->
- opt_3(L, Blk0, Bs, Ts, D0, Acc)
+opt_1(L, #b_blk{is=Is0,last=Last0}=Blk0, Bs, Ts0,
+ #d{ds=Ds0,sub=Sub0,func_db=Fdb0}=D0, Acc) ->
+ case opt_is(Is0, Ts0, Ds0, Fdb0, D0, Sub0, []) of
+ {Is,Ts,Ds,Fdb,Sub} ->
+ D1 = D0#d{ds=Ds,sub=Sub,func_db=Fdb},
+ Last1 = simplify_terminator(Last0, Sub, Ts, Ds),
+ Last = opt_terminator(Last1, Ts, Ds),
+ D = update_successors(Last, Ts, D1),
+ Blk = Blk0#b_blk{is=Is,last=Last},
+ opt(Bs, D, [{L,Blk}|Acc]);
+ {no_return,Ret,Is,Ds,Fdb,Sub} ->
+ %% This call will never reach the successor block.
+ %% Rewrite the terminator to a 'ret', and remove
+ %% all type information for this label. That can
+ %% potentially narrow the type of the phi node
+ %% in the former successor.
+ Ls = maps:remove(L, D0#d.ls),
+ RetType = join([none|D0#d.ret_type]),
+ D = D0#d{ds=Ds,ls=Ls,sub=Sub,
+ func_db=Fdb,ret_type=[RetType]},
+ Blk = Blk0#b_blk{is=Is,last=Ret},
+ opt(Bs, D, [{L,Blk}|Acc])
end.
-opt_3(L, #b_blk{is=Is0,last=Last0}=Blk0, Bs, Ts0,
- #d{ds=Ds0,ls=Ls0,sub=Sub0,func_db=Fdb0}=D0, Acc) ->
- {Is,Ts,Ds,Fdb,Sub} = opt_is(Is0, Ts0, Ds0, Fdb0, Ls0, D0, Sub0, []),
- D1 = D0#d{ds=Ds,sub=Sub,func_db=Fdb},
- Last1 = simplify_terminator(Last0, Sub, Ts, Ds),
- Last = opt_terminator(Last1, Ts, Ds),
- D = update_successors(Last, Ts, D1),
- Blk = Blk0#b_blk{is=Is,last=Last},
- opt_1(Bs, D, [{L,Blk} | Acc]).
-
simplify_terminator(#b_br{bool=Bool}=Br, Sub, Ts, _Ds) ->
Br#b_br{bool=simplify_arg(Bool, Sub, Ts)};
simplify_terminator(#b_switch{arg=Arg}=Sw, Sub, Ts, _Ds) ->
@@ -252,7 +242,7 @@ simplify_terminator(#b_ret{arg=Arg}=Ret, Sub, Ts, Ds) ->
end.
opt_is([#b_set{op=phi,dst=Dst,args=Args0}=I0|Is],
- Ts0, Ds0, Fdb, Ls, D, Sub0, Acc) ->
+ Ts0, Ds0, Fdb, #d{ls=Ls}=D, Sub0, Acc) ->
%% Simplify the phi node by removing all predecessor blocks that no
%% longer exists or no longer branches to this block.
Args = [{simplify_arg(Arg, Sub0, Ts0),From} ||
@@ -263,37 +253,44 @@ opt_is([#b_set{op=phi,dst=Dst,args=Args0}=I0|Is],
%% value or if the values are identical.
[{Val,_}|_] = Args,
Sub = Sub0#{Dst=>Val},
- opt_is(Is, Ts0, Ds0, Fdb, Ls, D, Sub, Acc);
+ opt_is(Is, Ts0, Ds0, Fdb, D, Sub, Acc);
false ->
I = I0#b_set{args=Args},
Ts = update_types(I, Ts0, Ds0),
Ds = Ds0#{Dst=>I},
- opt_is(Is, Ts, Ds, Fdb, Ls, D, Sub0, [I|Acc])
+ opt_is(Is, Ts, Ds, Fdb, D, Sub0, [I|Acc])
end;
-opt_is([#b_set{op=call,args=Args0,dst=Dst}=I0 | Is],
- Ts0, Ds0, Fdb0, Ls, D, Sub, Acc) ->
- Args = simplify_args(Args0, Sub, Ts0),
+opt_is([#b_set{op=call,args=Args0,dst=Dst}=I0|Is],
+ Ts0, Ds0, Fdb0, D, Sub0, Acc) ->
+ Args = simplify_args(Args0, Sub0, Ts0),
I1 = beam_ssa:normalize(I0#b_set{args=Args}),
-
- %% This is a bit of a kludge; we know that any instruction whose return
- %% type is 'none' will fail at runtime, but we don't yet have a way to cut
- %% a block short so we move on like nothing nothing happened.
- %%
- %% This complicates argument type optimization as unreachable calls can
- %% add types that will never occur, so we skip optimizing this call if
- %% the type of any of its arguments is 'none'.
- [_Callee | Rest] = Args,
- case all(fun(Arg) -> get_type(Arg, Ts0) =/= none end, Rest) of
- true ->
- {Ts, Ds, Fdb, I} = opt_call(I1, D, Ts0, Ds0, Fdb0),
- opt_is(Is, Ts, Ds, Fdb, Ls, D, Sub, [I|Acc]);
- false ->
- Ts = Ts0#{ Dst => any },
- Ds = Ds0#{ Dst => I1 },
- opt_is(Is, Ts, Ds, Fdb0, Ls, D, Sub, [I1|Acc])
+ {Ts1,Ds,Fdb,I2} = opt_call(I1, D, Ts0, Ds0, Fdb0),
+ case {map_get(Dst, Ts1),Is} of
+ {_,[#b_set{op=succeeded}]} ->
+ %% This call instruction is inside a try/catch
+ %% block. Don't attempt to optimize it.
+ opt_is(Is, Ts1, Ds, Fdb, D, Sub0, [I2|Acc]);
+ {none,_} ->
+ %% This call never returns. The rest of the
+ %% instructions will not be executed.
+ Ret = #b_ret{arg=Dst},
+ {no_return,Ret,reverse(Acc, [I2]),Ds,Fdb,Sub0};
+ {_,_} ->
+ case simplify_call(I2) of
+ #b_set{}=I ->
+ opt_is(Is, Ts1, Ds, Fdb, D, Sub0, [I|Acc]);
+ #b_literal{}=Lit ->
+ Sub = Sub0#{Dst=>Lit},
+ Ts = maps:remove(Dst, Ts1),
+ opt_is(Is, Ts, Ds0, Fdb, D, Sub, Acc);
+ #b_var{}=Var ->
+ Ts = maps:remove(Dst, Ts1),
+ Sub = Sub0#{Dst=>Var},
+ opt_is(Is, Ts, Ds0, Fdb, D, Sub, Acc)
+ end
end;
opt_is([#b_set{op=succeeded,args=[Arg],dst=Dst}=I],
- Ts0, Ds0, Fdb, Ls, D, Sub0, Acc) ->
+ Ts0, Ds0, Fdb, D, Sub0, Acc) ->
case Ds0 of
#{ Arg := #b_set{op=call} } ->
%% The success check of a call is part of exception handling and
@@ -302,22 +299,22 @@ opt_is([#b_set{op=succeeded,args=[Arg],dst=Dst}=I],
Ts = update_types(I, Ts0, Ds0),
Ds = Ds0#{Dst=>I},
- opt_is([], Ts, Ds, Fdb, Ls, D, Sub0, [I|Acc]);
+ opt_is([], Ts, Ds, Fdb, D, Sub0, [I|Acc]);
#{} ->
Args = simplify_args([Arg], Sub0, Ts0),
Type = type(succeeded, Args, Ts0, Ds0),
case get_literal_from_type(Type) of
#b_literal{}=Lit ->
Sub = Sub0#{Dst=>Lit},
- opt_is([], Ts0, Ds0, Fdb, Ls, D, Sub, Acc);
+ opt_is([], Ts0, Ds0, Fdb, D, Sub, Acc);
none ->
Ts = Ts0#{Dst=>Type},
Ds = Ds0#{Dst=>I},
- opt_is([], Ts, Ds, Fdb, Ls, D, Sub0, [I|Acc])
+ opt_is([], Ts, Ds, Fdb, D, Sub0, [I|Acc])
end
end;
opt_is([#b_set{args=Args0,dst=Dst}=I0|Is],
- Ts0, Ds0, Fdb, Ls, D, Sub0, Acc) ->
+ Ts0, Ds0, Fdb, D, Sub0, Acc) ->
Args = simplify_args(Args0, Sub0, Ts0),
I1 = beam_ssa:normalize(I0#b_set{args=Args}),
case simplify(I1, Ts0) of
@@ -325,24 +322,77 @@ opt_is([#b_set{args=Args0,dst=Dst}=I0|Is],
I = beam_ssa:normalize(I2),
Ts = update_types(I, Ts0, Ds0),
Ds = Ds0#{Dst=>I},
- opt_is(Is, Ts, Ds, Fdb, Ls, D, Sub0, [I|Acc]);
+ opt_is(Is, Ts, Ds, Fdb, D, Sub0, [I|Acc]);
#b_literal{}=Lit ->
Sub = Sub0#{Dst=>Lit},
- opt_is(Is, Ts0, Ds0, Fdb, Ls, D, Sub, Acc);
+ opt_is(Is, Ts0, Ds0, Fdb, D, Sub, Acc);
#b_var{}=Var ->
case Is of
[#b_set{op=succeeded,dst=SuccDst,args=[Dst]}] ->
%% We must remove this 'succeeded' instruction.
Sub = Sub0#{Dst=>Var,SuccDst=>#b_literal{val=true}},
- opt_is([], Ts0, Ds0, Fdb, Ls, D, Sub, Acc);
+ opt_is([], Ts0, Ds0, Fdb, D, Sub, Acc);
_ ->
Sub = Sub0#{Dst=>Var},
- opt_is(Is, Ts0, Ds0, Fdb, Ls, D, Sub, Acc)
+ opt_is(Is, Ts0, Ds0, Fdb, D, Sub, Acc)
end
end;
-opt_is([], Ts, Ds, Fdb, _Ls, _D, Sub, Acc) ->
+opt_is([], Ts, Ds, Fdb, _D, Sub, Acc) ->
{reverse(Acc), Ts, Ds, Fdb, Sub}.
+simplify_call(#b_set{op=call,args=[#b_remote{}=Rem|Args]}=I) ->
+ case Rem of
+ #b_remote{mod=#b_literal{val=Mod},
+ name=#b_literal{val=Name}} ->
+ case erl_bifs:is_pure(Mod, Name, length(Args)) of
+ true ->
+ simplify_remote_call(Mod, Name, Args, I);
+ false ->
+ I
+ end;
+ #b_remote{} ->
+ I
+ end;
+simplify_call(I) -> I.
+
+%% Simplify a remote call to a pure BIF.
+simplify_remote_call(erlang, '++', [#b_literal{val=[]},Tl], _I) ->
+ Tl;
+simplify_remote_call(erlang, setelement,
+ [#b_literal{val=Pos},
+ #b_literal{val=Tuple},
+ #b_var{}=Value], I)
+ when is_integer(Pos), 1 =< Pos, Pos =< tuple_size(Tuple) ->
+ %% Position is a literal integer and the shape of the
+ %% tuple is known.
+ Els0 = [#b_literal{val=El} || El <- tuple_to_list(Tuple)],
+ {Bef,[_|Aft]} = split(Pos - 1, Els0),
+ Els = Bef ++ [Value|Aft],
+ I#b_set{op=put_tuple,args=Els};
+simplify_remote_call(Mod, Name, Args0, I) ->
+ case make_literal_list(Args0) of
+ none ->
+ I;
+ Args ->
+ %% The arguments are literals. Try to evaluate the BIF.
+ try apply(Mod, Name, Args) of
+ Val ->
+ case cerl:is_literal_term(Val) of
+ true ->
+ #b_literal{val=Val};
+ false ->
+ %% The value can't be expressed as a literal
+ %% (e.g. a pid).
+ I
+ end
+ catch
+ _:_ ->
+ %% Failed. Don't bother trying to optimize
+ %% the call.
+ I
+ end
+ end.
+
opt_call(#b_set{dst=Dst,args=[#b_local{}=Callee|Args]}=I0, D, Ts0, Ds0, Fdb0) ->
{Ts, Ds, I} = opt_local_call(I0, Ts0, Ds0, Fdb0),
case Fdb0 of
@@ -365,14 +415,13 @@ opt_call(#b_set{dst=Dst}=I, _D, Ts0, Ds0, Fdb) ->
{Ts, Ds, Fdb, I}.
opt_local_call(#b_set{dst=Dst,args=[Id|_]}=I0, Ts0, Ds0, Fdb) ->
- %% We skip propagating 'none' as we don't yet have a good way to cut a
- %% block short.
Type = case Fdb of
- #{ Id := #func_info{ret_type=[T]} } when T =/= none -> T;
+ #{ Id := #func_info{ret_type=[T]} } -> T;
#{} -> any
end,
I = case Type of
any -> I0;
+ none -> I0;
_ -> beam_ssa:add_anno(result_type, validator_anno(Type), I0)
end,
Ts = Ts0#{ Dst => Type },
@@ -386,11 +435,6 @@ update_arg_types([Arg | Args], [TypeMap0 | TypeMaps], CallId, Ts) ->
#t_bs_match{} -> {binary, 1};
Type -> Type
end,
- PrevType = maps:get(CallId, TypeMap0, NewType),
-
- %% The new type must be narrower than the old one.
- true = meet(NewType, PrevType) =/= none, %Assertion.
-
TypeMap = TypeMap0#{ CallId => NewType },
[TypeMap | update_arg_types(Args, TypeMaps, CallId, Ts)];
update_arg_types([], [], _CallId, _Ts) ->
@@ -418,12 +462,14 @@ simplify(#b_set{op={bif,'or'},args=Args}=I, Ts) ->
false ->
I
end;
-simplify(#b_set{op={bif,element},args=[#b_literal{val=Index},Tuple]}=I, Ts) ->
+simplify(#b_set{op={bif,element},args=[#b_literal{val=Index},Tuple]}=I0, Ts) ->
case t_tuple_size(get_type(Tuple, Ts)) of
{_,Size} when is_integer(Index), 1 =< Index, Index =< Size ->
- I#b_set{op=get_tuple_element,args=[Tuple,#b_literal{val=Index-1}]};
+ I = I0#b_set{op=get_tuple_element,
+ args=[Tuple,#b_literal{val=Index-1}]},
+ simplify(I, Ts);
_ ->
- eval_bif(I, Ts)
+ eval_bif(I0, Ts)
end;
simplify(#b_set{op={bif,hd},args=[List]}=I, Ts) ->
case get_type(List, Ts) of
@@ -471,10 +517,19 @@ simplify(#b_set{op={bif,'=='},args=Args}=I, Ts) ->
end;
simplify(#b_set{op={bif,'=:='},args=[Same,Same]}, _Ts) ->
#b_literal{val=true};
-simplify(#b_set{op={bif,'=:='},args=Args}=I, Ts) ->
- case meet(get_types(Args, Ts)) of
- none -> #b_literal{val=false};
- _ -> eval_bif(I, Ts)
+simplify(#b_set{op={bif,'=:='},args=[A1,_A2]=Args}=I, Ts) ->
+ [T1,T2] = get_types(Args, Ts),
+ case meet(T1, T2) of
+ none ->
+ #b_literal{val=false};
+ _ ->
+ case {t_is_boolean(T1),T2} of
+ {true,#t_atom{elements=[true]}} ->
+ %% Bool =:= true ==> Bool
+ A1;
+ {_,_} ->
+ eval_bif(I, Ts)
+ end
end;
simplify(#b_set{op={bif,Op},args=Args}=I, Ts) ->
Types = get_types(Args, Ts),
@@ -485,11 +540,17 @@ simplify(#b_set{op={bif,Op},args=Args}=I, Ts) ->
AnnoArgs = [anno_float_arg(A) || A <- Types],
eval_bif(beam_ssa:add_anno(float_op, AnnoArgs, I), Ts)
end;
-simplify(#b_set{op=get_tuple_element,args=[Tuple,#b_literal{val=0}]}=I, Ts) ->
+simplify(#b_set{op=get_tuple_element,args=[Tuple,#b_literal{val=N}]}=I, Ts) ->
case get_type(Tuple, Ts) of
- #t_tuple{elements=[First]} ->
- #b_literal{val=First};
- #t_tuple{} ->
+ #t_tuple{size=Size,elements=Es} when Size > N ->
+ ElemType = get_element_type(N + 1, Es),
+ case get_literal_from_type(ElemType) of
+ #b_literal{}=Lit -> Lit;
+ none -> I
+ end;
+ none ->
+ %% Will never be executed because of type conflict.
+ %% #b_literal{val=ignored};
I
end;
simplify(#b_set{op=is_nonempty_list,args=[Src]}=I, Ts) ->
@@ -500,24 +561,8 @@ simplify(#b_set{op=is_nonempty_list,args=[Src]}=I, Ts) ->
_ -> #b_literal{val=false}
end;
simplify(#b_set{op=is_tagged_tuple,
- args=[Src,#b_literal{val=Size},#b_literal{val=Tag}]}=I, Ts) ->
- case get_type(Src, Ts) of
- #t_tuple{exact=true,size=Size,elements=[Tag]} ->
- #b_literal{val=true};
- #t_tuple{exact=true,size=ActualSize,elements=[]} ->
- if
- Size =/= ActualSize ->
- #b_literal{val=false};
- true ->
- I
- end;
- #t_tuple{exact=false} ->
- I;
- any ->
- I;
- _ ->
- #b_literal{val=false}
- end;
+ args=[Src,#b_literal{val=Size},#b_literal{}=Tag]}=I, Ts) ->
+ simplify_is_record(I, get_type(Src, Ts), Size, Tag, Ts);
simplify(#b_set{op=put_list,args=[#b_literal{val=H},
#b_literal{val=T}]}, _Ts) ->
#b_literal{val=[H|T]};
@@ -627,41 +672,49 @@ anno_float_arg(_) -> convert.
opt_terminator(#b_br{bool=#b_literal{}}=Br, _Ts, _Ds) ->
beam_ssa:normalize(Br);
-opt_terminator(#b_br{bool=#b_var{}=V}=Br, Ts, Ds) ->
- #{V:=Set} = Ds,
- case Set of
- #b_set{op={bif,'=:='},args=[Bool,#b_literal{val=true}]} ->
- case t_is_boolean(get_type(Bool, Ts)) of
- true ->
- %% Bool =:= true ==> Bool
- simplify_not(Br#b_br{bool=Bool}, Ts, Ds);
- false ->
- Br
- end;
- #b_set{} ->
- simplify_not(Br, Ts, Ds)
- end;
+opt_terminator(#b_br{bool=#b_var{}}=Br, Ts, Ds) ->
+ simplify_not(Br, Ts, Ds);
opt_terminator(#b_switch{arg=#b_literal{}}=Sw, _Ts, _Ds) ->
beam_ssa:normalize(Sw);
-opt_terminator(#b_switch{arg=#b_var{}=V}=Sw0, Ts, Ds) ->
- Type = get_type(V, Ts),
+opt_terminator(#b_switch{arg=#b_var{}=V}=Sw, Ts, Ds) ->
+ case get_type(V, Ts) of
+ any ->
+ beam_ssa:normalize(Sw);
+ Type ->
+ beam_ssa:normalize(opt_switch(Sw, Type, Ts, Ds))
+ end;
+opt_terminator(#b_ret{}=Ret, _Ts, _Ds) -> Ret.
+
+
+opt_switch(#b_switch{fail=Fail,list=List0}=Sw0, Type, Ts, Ds) ->
+ List = prune_switch_list(List0, Fail, Type, Ts),
+ Sw1 = Sw0#b_switch{list=List},
case Type of
#t_integer{elements={_,_}=Range} ->
- simplify_switch_int(Sw0, Range);
- _ ->
+ simplify_switch_int(Sw1, Range);
+ #t_atom{elements=[_|_]} ->
case t_is_boolean(Type) of
true ->
- case simplify_switch_bool(Sw0, Ts, Ds) of
- #b_br{}=Br ->
- opt_terminator(Br, Ts, Ds);
- Sw ->
- beam_ssa:normalize(Sw)
- end;
+ #b_br{} = Br = simplify_switch_bool(Sw1, Ts, Ds),
+ opt_terminator(Br, Ts, Ds);
false ->
- beam_ssa:normalize(Sw0)
- end
+ simplify_switch_atom(Type, Sw1)
+ end;
+ _ ->
+ Sw1
+ end.
+
+prune_switch_list([{_,Fail}|T], Fail, Type, Ts) ->
+ prune_switch_list(T, Fail, Type, Ts);
+prune_switch_list([{Arg,_}=Pair|T], Fail, Type, Ts) ->
+ case meet(get_type(Arg, Ts), Type) of
+ none ->
+ %% Different types. This value can never match.
+ prune_switch_list(T, Fail, Type, Ts);
+ _ ->
+ [Pair|prune_switch_list(T, Fail, Type, Ts)]
end;
-opt_terminator(#b_ret{}=Ret, _Ts, _Ds) -> Ret.
+prune_switch_list([], _, _, _) -> [].
update_successors(#b_br{bool=#b_literal{val=true},succ=S}, Ts, D) ->
update_successor(S, Ts, D);
@@ -670,38 +723,39 @@ update_successors(#b_br{bool=#b_var{}=Bool,succ=Succ,fail=Fail}, Ts0, D0) ->
true ->
%% This variable is defined in this block and is only
%% referenced by this br terminator. Therefore, there is
- %% no need to include the type database passed on to the
- %% successors of this block.
+ %% no need to include it in the type database passed on to
+ %% the successors of this block.
Ts = maps:remove(Bool, Ts0),
- {SuccTs,FailTs} = infer_types(Bool, Ts, D0),
+ {SuccTs,FailTs} = infer_types_br(Bool, Ts, D0),
D = update_successor(Fail, FailTs, D0),
update_successor(Succ, SuccTs, D);
false ->
- {SuccTs,FailTs} = infer_types(Bool, Ts0, D0),
+ {SuccTs,FailTs} = infer_types_br(Bool, Ts0, D0),
D = update_successor_bool(Bool, false, Fail, FailTs, D0),
update_successor_bool(Bool, true, Succ, SuccTs, D)
end;
-update_successors(#b_switch{arg=#b_var{}=V,fail=Fail,list=List}, Ts0, D0) ->
+update_successors(#b_switch{arg=#b_var{}=V,fail=Fail,list=List}, Ts, D0) ->
case cerl_sets:is_element(V, D0#d.once) of
true ->
%% This variable is defined in this block and is only
%% referenced by this switch terminator. Therefore, there is
- %% no need to include the type database passed on to the
- %% successors of this block.
- Ts = maps:remove(V, Ts0),
+ %% no need to include it in the type database passed on to
+ %% the successors of this block.
D = update_successor(Fail, Ts, D0),
- F = fun({_Val,S}, A) ->
- update_successor(S, Ts, A)
+ F = fun({Val,S}, A) ->
+ SuccTs0 = infer_types_switch(V, Val, Ts, D),
+ SuccTs = maps:remove(V, SuccTs0),
+ update_successor(S, SuccTs, A)
end,
foldl(F, D, List);
false ->
%% V can not be equal to any of the values in List at the fail
%% block.
- FailTs = subtract_sw_list(V, List, Ts0),
+ FailTs = subtract_sw_list(V, List, Ts),
D = update_successor(Fail, FailTs, D0),
F = fun({Val,S}, A) ->
- T = get_type(Val, Ts0),
- update_successor(S, Ts0#{V=>T}, A)
+ SuccTs = infer_types_switch(V, Val, Ts, D),
+ update_successor(S, SuccTs, A)
end,
foldl(F, D, List)
end;
@@ -785,19 +839,40 @@ type(bs_get_tail, _Args, _Ts, _Ds) ->
type(call, [#b_remote{mod=#b_literal{val=Mod},
name=#b_literal{val=Name}}|Args], Ts, _Ds) ->
case {Mod,Name,Args} of
- {erlang,setelement,[Pos,Tuple,_]} ->
+ {erlang,setelement,[Pos,Tuple,Arg]} ->
case {get_type(Pos, Ts),get_type(Tuple, Ts)} of
- {#t_integer{elements={MinIndex,_}},#t_tuple{}=T}
- when MinIndex > 1 ->
- %% First element is not updated. The result
- %% will have the same type.
- T;
+ {#t_integer{elements={Index,Index}},
+ #t_tuple{elements=Es0,size=Size}=T} ->
+ %% This is an exact index, update the type of said element
+ %% or return 'none' if it's known to be out of bounds.
+ Es = set_element_type(Index, get_type(Arg, Ts), Es0),
+ case T#t_tuple.exact of
+ false ->
+ T#t_tuple{size=max(Index, Size),elements=Es};
+ true when Index =< Size ->
+ T#t_tuple{elements=Es};
+ true ->
+ none
+ end;
+ {#t_integer{elements={Min,Max}},
+ #t_tuple{elements=Es0,size=Size}=T} ->
+ %% We know this will land between Min and Max, so kill the
+ %% types for those indexes.
+ Es = maps:without(seq(Min, Max), Es0),
+ case T#t_tuple.exact of
+ false ->
+ T#t_tuple{elements=Es,size=max(Min, Size)};
+ true when Min =< Size ->
+ T#t_tuple{elements=Es,size=Size};
+ true ->
+ none
+ end;
{_,#t_tuple{}=T} ->
- %% Position is 1 or unknown. May update the first
- %% element of the tuple.
- T#t_tuple{elements=[]};
- {#t_integer{elements={MinIndex,_}},_} ->
- #t_tuple{size=MinIndex};
+ %% Position unknown, so we have to discard all element
+ %% information.
+ T#t_tuple{elements=#{}};
+ {#t_integer{elements={Min,_Max}},_} ->
+ #t_tuple{size=Min};
{_,_} ->
#t_tuple{}
end;
@@ -809,6 +884,9 @@ type(call, [#b_remote{mod=#b_literal{val=Mod},
end;
{erlang,'--',[_,_]} ->
list;
+ {lists,F,Args} ->
+ Types = get_types(Args, Ts),
+ lists_function_type(F, Types);
{math,_,_} ->
case is_math_bif(Name, length(Args)) of
false -> any;
@@ -820,6 +898,11 @@ type(call, [#b_remote{mod=#b_literal{val=Mod},
false -> any
end
end;
+type(get_tuple_element, [Tuple, Offset], Ts, _Ds) ->
+ #t_tuple{size=Size,elements=Es} = get_type(Tuple, Ts),
+ #b_literal{val=N} = Offset,
+ true = Size > N, %Assertion.
+ get_element_type(N + 1, Es);
type(is_nonempty_list, [_], _Ts, _Ds) ->
t_boolean();
type(is_tagged_tuple, [_,#b_literal{},#b_literal{}], _Ts, _Ds) ->
@@ -828,13 +911,13 @@ type(put_map, _Args, _Ts, _Ds) ->
map;
type(put_list, _Args, _Ts, _Ds) ->
cons;
-type(put_tuple, Args, _Ts, _Ds) ->
- case Args of
- [#b_literal{val=First}|_] ->
- #t_tuple{exact=true,size=length(Args),elements=[First]};
- _ ->
- #t_tuple{exact=true,size=length(Args)}
- end;
+type(put_tuple, Args, Ts, _Ds) ->
+ {Es, _} = foldl(fun(Arg, {Es0, Index}) ->
+ Type = get_type(Arg, Ts),
+ Es = set_element_type(Index, Type, Es0),
+ {Es, Index + 1}
+ end, {#{}, 1}, Args),
+ #t_tuple{exact=true,size=length(Args),elements=Es};
type(succeeded, [#b_var{}=Src], Ts, Ds) ->
case maps:get(Src, Ds) of
#b_set{op={bif,Bif},args=BifArgs} ->
@@ -895,6 +978,70 @@ arith_op_type(Args, Ts) ->
(_, _) -> none
end, unknown, Types).
+lists_function_type(F, Types) ->
+ case {F,Types} of
+ %% Functions that return booleans.
+ {all,[_,_]} ->
+ t_boolean();
+ {any,[_,_]} ->
+ t_boolean();
+ {keymember,[_,_,_]} ->
+ t_boolean();
+ {member,[_,_]} ->
+ t_boolean();
+ {prefix,[_,_]} ->
+ t_boolean();
+ {suffix,[_,_]} ->
+ t_boolean();
+
+ %% Functions that return lists.
+ {dropwhile,[_,_]} ->
+ list;
+ {duplicate,[_,_]} ->
+ list;
+ {filter,[_,_]} ->
+ list;
+ {flatten,[_]} ->
+ list;
+ {map,[_Fun,List]} ->
+ same_length_type(List);
+ {MapFold,[_Fun,_Acc,List]} when MapFold =:= mapfoldl;
+ MapFold =:= mapfoldr ->
+ #t_tuple{size=2,exact=true,
+ elements=#{1=>same_length_type(List)}};
+ {partition,[_,_]} ->
+ t_two_tuple(list, list);
+ {reverse,[List]} ->
+ same_length_type(List);
+ {sort,[List]} ->
+ same_length_type(List);
+ {splitwith,[_,_]} ->
+ t_two_tuple(list, list);
+ {takewhile,[_,_]} ->
+ list;
+ {unzip,[List]} ->
+ ListType = same_length_type(List),
+ t_two_tuple(ListType, ListType);
+ {usort,[List]} ->
+ same_length_type(List);
+ {zip,[_,_]} ->
+ list;
+ {zipwith,[_,_,_]} ->
+ list;
+ {_,_} ->
+ any
+ end.
+
+%% For a lists function that return a list of the same
+%% length as the input list, return the type of the list.
+same_length_type(cons) -> cons;
+same_length_type(nil) -> nil;
+same_length_type(_) -> list.
+
+t_two_tuple(Type1, Type2) ->
+ #t_tuple{size=2,exact=true,
+ elements=#{1=>Type1,2=>Type2}}.
+
%% will_succeed(TestOperation, Type) -> yes|no|maybe.
%% Test whether TestOperation applied to an argument of type Type
%% will succeed. Return yes, no, or maybe.
@@ -1031,6 +1178,17 @@ bs_match_type(utf16, _) ->
bs_match_type(utf32, _) ->
?UNICODE_INT.
+simplify_switch_atom(#t_atom{elements=Atoms}, #b_switch{list=List0}=Sw) ->
+ case sort([A || {#b_literal{val=A},_} <- List0]) of
+ Atoms ->
+ %% All possible atoms are included in the list. The
+ %% failure label will never be used.
+ [{_,Fail}|List] = List0,
+ Sw#b_switch{fail=Fail,list=List};
+ _ ->
+ Sw
+ end.
+
simplify_switch_int(#b_switch{list=List0}=Sw, {Min,Max}) ->
List1 = sort(List0),
Vs = [V || {#b_literal{val=V},_} <- List1],
@@ -1047,14 +1205,42 @@ eq_ranges([H], H, H) -> true;
eq_ranges([H|T], H, Max) -> eq_ranges(T, H+1, Max);
eq_ranges(_, _, _) -> false.
-simplify_switch_bool(#b_switch{arg=B,list=List0}=Sw, Ts, Ds) ->
- List = sort(List0),
- case List of
- [{#b_literal{val=false},Fail},{#b_literal{val=true},Succ}] ->
- simplify_not(#b_br{bool=B,succ=Succ,fail=Fail}, Ts, Ds);
- [_|_] ->
- Sw
- end.
+simplify_is_record(I, #t_tuple{exact=Exact,
+ size=Size,
+ elements=Es},
+ RecSize, RecTag, Ts) ->
+ TagType = maps:get(1, Es, any),
+ TagMatch = case get_literal_from_type(TagType) of
+ #b_literal{}=RecTag -> yes;
+ #b_literal{} -> no;
+ none ->
+ %% Is it at all possible for the tag to match?
+ case meet(get_type(RecTag, Ts), TagType) of
+ none -> no;
+ _ -> maybe
+ end
+ end,
+ if
+ Size =/= RecSize, Exact; Size > RecSize; TagMatch =:= no ->
+ #b_literal{val=false};
+ Size =:= RecSize, Exact, TagMatch =:= yes ->
+ #b_literal{val=true};
+ true ->
+ I
+ end;
+simplify_is_record(I, any, _Size, _Tag, _Ts) ->
+ I;
+simplify_is_record(_I, _Type, _Size, _Tag, _Ts) ->
+ #b_literal{val=false}.
+
+simplify_switch_bool(#b_switch{arg=B,fail=Fail,list=List0}, Ts, Ds) ->
+ FalseVal = #b_literal{val=false},
+ TrueVal = #b_literal{val=true},
+ List1 = List0 ++ [{FalseVal,Fail},{TrueVal,Fail}],
+ {_,FalseLbl} = keyfind(FalseVal, 1, List1),
+ {_,TrueLbl} = keyfind(TrueVal, 1, List1),
+ Br = beam_ssa:normalize(#b_br{bool=B,succ=TrueLbl,fail=FalseLbl}),
+ simplify_not(Br, Ts, Ds).
simplify_not(#b_br{bool=#b_var{}=V,succ=Succ,fail=Fail}=Br0, Ts, Ds) ->
case Ds of
@@ -1068,7 +1254,8 @@ simplify_not(#b_br{bool=#b_var{}=V,succ=Succ,fail=Fail}=Br0, Ts, Ds) ->
end;
#{} ->
Br0
- end.
+ end;
+simplify_not(#b_br{bool=#b_literal{}}=Br, _Ts, _Ds) -> Br.
%%%
%%% Calculate the set of variables that are only used once in the
@@ -1149,8 +1336,12 @@ get_type(#b_literal{val=Val}, _Ts) ->
Val =:= {} ->
#t_tuple{exact=true};
is_tuple(Val) ->
- #t_tuple{exact=true,size=tuple_size(Val),
- elements=[element(1, Val)]};
+ {Es, _} = foldl(fun(E, {Es0, Index}) ->
+ Type = get_type(#b_literal{val=E}, #{}),
+ Es = set_element_type(Index, Type, Es0),
+ {Es, Index + 1}
+ end, {#{}, 1}, tuple_to_list(Val)),
+ #t_tuple{exact=true,size=tuple_size(Val),elements=Es};
Val =:= [] ->
nil;
true ->
@@ -1192,7 +1383,7 @@ get_type(#b_literal{val=Val}, _Ts) ->
%% failed and that L is not 'cons'. 'cons' can be subtracted from the
%% previously known type for L and the result put in FailTypes.
-infer_types(#b_var{}=V, Ts, #d{ds=Ds}) ->
+infer_types_br(#b_var{}=V, Ts, #d{ds=Ds}) ->
#{V:=#b_set{op=Op,args=Args}} = Ds,
Types0 = infer_type(Op, Args, Ds),
@@ -1213,11 +1404,14 @@ infer_types(#b_var{}=V, Ts, #d{ds=Ds}) ->
Types = Types1 ++ Types0,
{meet_types(EqTypes++Types, Ts),subtract_types(Types, Ts)}.
+infer_types_switch(V, Lit, Ts, #d{ds=Ds}) ->
+ Types = infer_eq_type({bif,'=:='}, [V, Lit], Ts, Ds),
+ meet_types(Types, Ts).
+
infer_eq_type({bif,'=:='}, [#b_var{}=Src,#b_literal{}=Lit], Ts, Ds) ->
Def = maps:get(Src, Ds),
Type = get_type(Lit, Ts),
- [{Src,Type}|infer_tuple_size(Def, Lit) ++
- infer_first_element(Def, Lit)];
+ [{Src,Type} | infer_eq_lit(Def, Lit)];
infer_eq_type({bif,'=:='}, [#b_var{}=Arg0,#b_var{}=Arg1], Ts, _Ds) ->
%% As an example, assume that L1 is known to be 'list', and L2 is
%% known to be 'cons'. Then if 'L1 =:= L2' evaluates to 'true', it can
@@ -1232,6 +1426,17 @@ infer_eq_type({bif,'=:='}, [#b_var{}=Arg0,#b_var{}=Arg1], Ts, _Ds) ->
infer_eq_type(_Op, _Args, _Ts, _Ds) ->
[].
+infer_eq_lit(#b_set{op={bif,tuple_size},args=[#b_var{}=Tuple]},
+ #b_literal{val=Size}) when is_integer(Size) ->
+ [{Tuple,#t_tuple{exact=true,size=Size}}];
+infer_eq_lit(#b_set{op=get_tuple_element,
+ args=[#b_var{}=Tuple,#b_literal{val=N}]},
+ #b_literal{}=Lit) ->
+ Index = N + 1,
+ Es = set_element_type(Index, get_type(Lit, #{}), #{}),
+ [{Tuple,#t_tuple{size=Index,elements=Es}}];
+infer_eq_lit(_, _) -> [].
+
infer_type({bif,element}, [#b_literal{val=Pos},#b_var{}=Tuple], _Ds) ->
if
is_integer(Pos), 1 =< Pos ->
@@ -1265,8 +1470,9 @@ infer_type(bs_start_match, [#b_var{}=Bin], _Ds) ->
infer_type(is_nonempty_list, [#b_var{}=Src], _Ds) ->
[{Src,cons}];
infer_type(is_tagged_tuple, [#b_var{}=Src,#b_literal{val=Size},
- #b_literal{val=Tag}], _Ds) ->
- [{Src,#t_tuple{exact=true,size=Size,elements=[Tag]}}];
+ #b_literal{}=Tag], _Ds) ->
+ Es = set_element_type(1, get_type(Tag, #{}), #{}),
+ [{Src,#t_tuple{exact=true,size=Size,elements=Es}}];
infer_type(succeeded, [#b_var{}=Src], Ds) ->
#b_set{op=Op,args=Args} = maps:get(Src, Ds),
infer_type(Op, Args, Ds);
@@ -1359,17 +1565,6 @@ inferred_bif_type('*', [_,_]) -> number;
inferred_bif_type('/', [_,_]) -> number;
inferred_bif_type(_, _) -> any.
-infer_tuple_size(#b_set{op={bif,tuple_size},args=[#b_var{}=Tuple]},
- #b_literal{val=Size}) when is_integer(Size) ->
- [{Tuple,#t_tuple{exact=true,size=Size}}];
-infer_tuple_size(_, _) -> [].
-
-infer_first_element(#b_set{op=get_tuple_element,
- args=[#b_var{}=Tuple,#b_literal{val=0}]},
- #b_literal{val=First}) ->
- [{Tuple,#t_tuple{size=1,elements=[First]}}];
-infer_first_element(_, _) -> [].
-
is_math_bif(cos, 1) -> true;
is_math_bif(cosh, 1) -> true;
is_math_bif(sin, 1) -> true;
@@ -1468,6 +1663,19 @@ t_tuple_size(_) ->
is_singleton_type(Type) ->
get_literal_from_type(Type) =/= none.
+get_element_type(Index, Es) ->
+ case Es of
+ #{ Index := T } -> T;
+ #{} -> any
+ end.
+
+set_element_type(_Key, none, Es) ->
+ Es;
+set_element_type(Key, any, Es) ->
+ maps:remove(Key, Es);
+set_element_type(Key, Type, Es) ->
+ Es#{ Key => Type }.
+
%% join(Type1, Type2) -> Type
%% Return the "join" of Type1 and Type2. The join is a more general
%% type than Type1 and Type2. For example:
@@ -1515,15 +1723,41 @@ join(#t_integer{}, number) -> number;
join(number, #t_integer{}) -> number;
join(float, number) -> number;
join(number, float) -> number;
-join(#t_tuple{size=Sz,exact=Exact1}, #t_tuple{size=Sz,exact=Exact2}) ->
- Exact = Exact1 and Exact2,
- #t_tuple{size=Sz,exact=Exact};
-join(#t_tuple{size=Sz1}, #t_tuple{size=Sz2}) ->
- #t_tuple{size=min(Sz1, Sz2)};
+join(#t_tuple{size=Sz,exact=ExactA,elements=EsA},
+ #t_tuple{size=Sz,exact=ExactB,elements=EsB}) ->
+ Exact = ExactA and ExactB,
+ Es = join_tuple_elements(Sz, EsA, EsB),
+ #t_tuple{size=Sz,exact=Exact,elements=Es};
+join(#t_tuple{size=SzA,elements=EsA}, #t_tuple{size=SzB,elements=EsB}) ->
+ Sz = min(SzA, SzB),
+ Es = join_tuple_elements(Sz, EsA, EsB),
+ #t_tuple{size=Sz,elements=Es};
join(_T1, _T2) ->
%%io:format("~p ~p\n", [_T1,_T2]),
any.
+join_tuple_elements(MinSize, EsA, EsB) ->
+ Es0 = join_elements(EsA, EsB),
+ maps:filter(fun(Index, _Type) -> Index =< MinSize end, Es0).
+
+join_elements(Es1, Es2) ->
+ Keys = if
+ map_size(Es1) =< map_size(Es2) -> maps:keys(Es1);
+ map_size(Es1) > map_size(Es2) -> maps:keys(Es2)
+ end,
+ join_elements_1(Keys, Es1, Es2, #{}).
+
+join_elements_1([Key | Keys], Es1, Es2, Acc0) ->
+ case {Es1, Es2} of
+ {#{ Key := Type1 }, #{ Key := Type2 }} ->
+ Acc = set_element_type(Key, join(Type1, Type2), Acc0),
+ join_elements_1(Keys, Es1, Es2, Acc);
+ {#{}, #{}} ->
+ join_elements_1(Keys, Es1, Es2, Acc0)
+ end;
+join_elements_1([], _Es1, _Es2, Acc) ->
+ Acc.
+
gcd(A, B) ->
case A rem B of
0 -> B;
@@ -1620,9 +1854,6 @@ meet(_, _) ->
%% Inconsistent types. There will be an exception at runtime.
none.
-meet_tuples(#t_tuple{elements=[E1]}, #t_tuple{elements=[E2]})
- when E1 =/= E2 ->
- none;
meet_tuples(#t_tuple{size=Sz1,exact=true},
#t_tuple{size=Sz2,exact=true}) when Sz1 =/= Sz2 ->
none;
@@ -1630,12 +1861,31 @@ meet_tuples(#t_tuple{size=Sz1,exact=Ex1,elements=Es1},
#t_tuple{size=Sz2,exact=Ex2,elements=Es2}) ->
Size = max(Sz1, Sz2),
Exact = Ex1 or Ex2,
- Es = case {Es1,Es2} of
- {[],[_|_]} -> Es2;
- {[_|_],[]} -> Es1;
- {_,_} -> Es1
- end,
- #t_tuple{size=Size,exact=Exact,elements=Es}.
+ case meet_elements(Es1, Es2) of
+ none ->
+ none;
+ Es ->
+ #t_tuple{size=Size,exact=Exact,elements=Es}
+ end.
+
+meet_elements(Es1, Es2) ->
+ Keys = maps:keys(Es1) ++ maps:keys(Es2),
+ meet_elements_1(Keys, Es1, Es2, #{}).
+
+meet_elements_1([Key | Keys], Es1, Es2, Acc) ->
+ case {Es1, Es2} of
+ {#{ Key := Type1 }, #{ Key := Type2 }} ->
+ case meet(Type1, Type2) of
+ none -> none;
+ Type -> meet_elements_1(Keys, Es1, Es2, Acc#{ Key => Type })
+ end;
+ {#{ Key := Type1 }, _} ->
+ meet_elements_1(Keys, Es1, Es2, Acc#{ Key => Type1 });
+ {_, #{ Key := Type2 }} ->
+ meet_elements_1(Keys, Es1, Es2, Acc#{ Key => Type2 })
+ end;
+meet_elements_1([], _Es1, _Es2, Acc) ->
+ Acc.
%% verified_type(Type) -> Type
%% Returns the passed in type if it is one of the defined types.
@@ -1674,5 +1924,13 @@ verified_type(map=T) -> T;
verified_type(nil=T) -> T;
verified_type(cons=T) -> T;
verified_type(number=T) -> T;
-verified_type(#t_tuple{}=T) -> T;
+verified_type(#t_tuple{size=Size,elements=Es}=T) ->
+ %% All known elements must have a valid index and type. 'any' is prohibited
+ %% since it's implicit and should never be present in the map.
+ maps:fold(fun(Index, Element, _) when is_integer(Index),
+ 1 =< Index, Index =< Size,
+ Element =/= any, Element =/= none ->
+ verified_type(Element)
+ end, [], Es),
+ T;
verified_type(float=T) -> T.
diff --git a/lib/compiler/src/beam_validator.erl b/lib/compiler/src/beam_validator.erl
index b56d53d4ce..5175be3ad5 100644
--- a/lib/compiler/src/beam_validator.erl
+++ b/lib/compiler/src/beam_validator.erl
@@ -26,9 +26,10 @@
%% Interface for compiler.
-export([module/2, format_error/1]).
--export([type_anno/1, type_anno/2, type_anno/3]).
+-export([type_anno/1, type_anno/2, type_anno/4]).
--import(lists, [any/2,dropwhile/2,foldl/3,map/2,foreach/2,reverse/1]).
+-import(lists, [any/2,dropwhile/2,foldl/3,map/2,member/2,reverse/1,
+ seq/2,sort/1,zip/2]).
%% To be called by the compiler.
@@ -65,11 +66,12 @@ type_anno(atom, Value) -> {atom, Value};
type_anno(float, Value) -> {float, Value};
type_anno(integer, Value) -> {integer, Value}.
--spec type_anno(term(), term(), term()) -> term().
-type_anno(tuple, Size, Exact) when is_integer(Size) ->
+-spec type_anno(term(), term(), term(), term()) -> term().
+type_anno(tuple, Size, Exact, Elements) when is_integer(Size), Size >= 0,
+ is_map(Elements) ->
case Exact of
- true -> {tuple, Size};
- false -> {tuple, [Size]}
+ true -> {tuple, Size, Elements};
+ false -> {tuple, [Size], Elements}
end.
-spec format_error(term()) -> iolist().
@@ -139,8 +141,8 @@ validate_0(Module, [{function,Name,Ar,Entry,Code}|Fs], Ft) ->
-type reg_tab() :: gb_trees:tree(index(), 'none' | {'value', _}).
-record(st, %Emulation state
- {x=init_regs(0, term) :: reg_tab(),%x register info.
- y=init_regs(0, initialized) :: reg_tab(),%y register info.
+ {x :: reg_tab(), %x register info.
+ y :: reg_tab(), %y register info.
f=init_fregs(), %
numy=none, %Number of y registers.
h=0, %Available heap size.
@@ -187,7 +189,7 @@ index_parameter_types([{function,_,_,Entry,Code0}|Fs], Acc0) ->
index_parameter_types(Fs, Acc0)
end;
index_parameter_types([], Acc) ->
- gb_trees:from_orddict(lists:sort(Acc)).
+ gb_trees:from_orddict(sort(Acc)).
index_parameter_types_1([{'%', {type_info, Reg, Type0}} | Is], Entry, Acc) ->
Type = case Type0 of
@@ -210,14 +212,10 @@ validate_2({Ls1,Is}, Name, Arity, _Entry, _Ft) ->
validate_3({Ls2,Is}, Name, Arity, Entry, Mod, Ls1, Ft) ->
Offset = 1 + length(Ls1) + 1 + length(Ls2),
- EntryOK = lists:member(Entry, Ls2),
+ EntryOK = member(Entry, Ls2),
if
EntryOK ->
- St = init_state(Arity),
- Vst0 = #vst{current=St,
- branched=gb_trees_from_list([{L,St} || L <- Ls1]),
- labels=gb_sets:from_list(Ls1++Ls2),
- ft=Ft},
+ Vst0 = init_vst(Arity, Ls1, Ls2, Ft),
MFA = {Mod,Name,Arity},
Vst = valfun(Is, MFA, Offset, Vst0),
validate_fun_info_branches(Ls1, MFA, Vst);
@@ -261,10 +259,16 @@ labels_1([{line,_}|Is], R) ->
labels_1(Is, R) ->
{reverse(R),Is}.
-init_state(Arity) ->
+init_vst(Arity, Ls1, Ls2, Ft) ->
Xs = init_regs(Arity, term),
Ys = init_regs(0, initialized),
- kill_heap_allocation(#st{x=Xs,y=Ys,numy=none,ct=[]}).
+ St = #st{x=Xs,y=Ys},
+ Branches = gb_trees_from_list([{L,St} || L <- Ls1]),
+ Labels = gb_sets:from_list(Ls1++Ls2),
+ #vst{branched=Branches,
+ current=St,
+ labels=Labels,
+ ft=Ft}.
kill_heap_allocation(St) ->
St#st{h=0,hf=0}.
@@ -272,7 +276,7 @@ kill_heap_allocation(St) ->
init_regs(0, _) ->
gb_trees:empty();
init_regs(N, Type) ->
- gb_trees_from_list([{R,Type} || R <- lists:seq(0, N-1)]).
+ gb_trees_from_list([{R,Type} || R <- seq(0, N-1)]).
valfun([], MFA, _Offset, #vst{branched=Targets0,labels=Labels0}=Vst) ->
Targets = gb_trees:keys(Targets0),
@@ -323,7 +327,7 @@ valfun_1({bs_get_tail,Ctx,Dst,Live}, Vst0) ->
verify_live(Live, Vst0),
verify_y_init(Vst0),
Vst = prune_x_regs(Live, Vst0),
- extract_term(binary, [Ctx], Dst, Vst, Vst0);
+ extract_term(binary, bs_get_tail, [Ctx], Dst, Vst, Vst0);
valfun_1(bs_init_writable=I, Vst) ->
call(I, 1, Vst);
valfun_1(build_stacktrace=I, Vst) ->
@@ -337,15 +341,15 @@ valfun_1({fmove,{fr,_}=Src,Dst}, Vst0) ->
assert_freg_set(Src, Vst0),
assert_fls(checked, Vst0),
Vst = eat_heap_float(Vst0),
- create_term({float,[]}, Dst, Vst);
-valfun_1({kill,{y,_}=Reg}, Vst) ->
- set_type_y(initialized, Reg, Vst);
-valfun_1({init,{y,_}=Reg}, Vst) ->
- set_type_y(initialized, Reg, Vst);
+ create_term({float,[]}, fmove, [], Dst, Vst);
+valfun_1({kill,Reg}, Vst) ->
+ create_tag(initialized, kill, [], Reg, Vst);
+valfun_1({init,Reg}, Vst) ->
+ create_tag(initialized, init, [], Reg, Vst);
valfun_1({test_heap,Heap,Live}, Vst) ->
test_heap(Heap, Live, Vst);
-valfun_1({bif,Op,{f,_},Src,Dst}=I, Vst) ->
- case is_bif_safe(Op, length(Src)) of
+valfun_1({bif,Op,{f,_},Ss,Dst}=I, Vst) ->
+ case is_bif_safe(Op, length(Ss)) of
false ->
%% Since the BIF can fail, make sure that any catch state
%% is updated.
@@ -353,27 +357,32 @@ valfun_1({bif,Op,{f,_},Src,Dst}=I, Vst) ->
true ->
%% It can't fail, so we finish handling it here (not updating
%% catch state).
- validate_src(Src, Vst),
- Type = bif_type(Op, Src, Vst),
- set_type_reg_expr(Type, I, Dst, Vst)
+ validate_src(Ss, Vst),
+ Type = bif_return_type(Op, Ss, Vst),
+ extract_term(Type, {bif,Op}, Ss, Dst, Vst)
end;
%% Put instructions.
valfun_1({put_list,A,B,Dst}, Vst0) ->
assert_not_fragile(A, Vst0),
assert_not_fragile(B, Vst0),
Vst = eat_heap(2, Vst0),
- create_term(cons, Dst, Vst);
+ create_term(cons, put_list, [A, B], Dst, Vst);
valfun_1({put_tuple2,Dst,{list,Elements}}, Vst0) ->
_ = [assert_not_fragile(El, Vst0) || El <- Elements],
Size = length(Elements),
Vst = eat_heap(Size+1, Vst0),
- Type = {tuple,Size},
- create_term(Type, Dst, Vst);
+ {Es,_} = foldl(fun(Val, {Es0, Index}) ->
+ Type = get_term_type(Val, Vst0),
+ Es = set_element_type(Index, Type, Es0),
+ {Es, Index + 1}
+ end, {#{}, 1}, Elements),
+ Type = {tuple,Size,Es},
+ create_term(Type, put_tuple2, [], Dst, Vst);
valfun_1({put_tuple,Sz,Dst}, Vst0) when is_integer(Sz) ->
Vst1 = eat_heap(1, Vst0),
- Vst = create_term(tuple_in_progress, Dst, Vst1),
+ Vst = create_term(tuple_in_progress, put_tuple, [], Dst, Vst1),
#vst{current=St0} = Vst,
- St = St0#st{puts_left={Sz,{Dst,{tuple,Sz}}}},
+ St = St0#st{puts_left={Sz,{Dst,Sz,#{}}}},
Vst#vst{current=St};
valfun_1({put,Src}, Vst0) ->
assert_not_fragile(Src, Vst0),
@@ -382,11 +391,13 @@ valfun_1({put,Src}, Vst0) ->
case St0 of
#st{puts_left=none} ->
error(not_building_a_tuple);
- #st{puts_left={1,{Dst,Type}}} ->
+ #st{puts_left={1,{Dst,Sz,Es}}} ->
St = St0#st{puts_left=none},
- create_term(Type, Dst, Vst#vst{current=St});
- #st{puts_left={PutsLeft,Info}} when is_integer(PutsLeft) ->
- St = St0#st{puts_left={PutsLeft-1,Info}},
+ create_term({tuple,Sz,Es}, put_tuple, [], Dst, Vst#vst{current=St});
+ #st{puts_left={PutsLeft,{Dst,Sz,Es0}}} when is_integer(PutsLeft) ->
+ Index = Sz - PutsLeft + 1,
+ Es = Es0#{ Index => get_term_type(Src, Vst0) },
+ St = St0#st{puts_left={PutsLeft-1,{Dst,Sz,Es}}},
Vst#vst{current=St}
end;
%% Instructions for optimization of selective receives.
@@ -412,7 +423,7 @@ valfun_1({line,_}, Vst) ->
Vst;
%% Exception generating calls
valfun_1({call_ext,Live,Func}=I, Vst) ->
- case return_type(Func, Vst) of
+ case call_return_type(Func, Vst) of
exception ->
verify_live(Live, Vst),
%% The stack will be scanned, so Y registers
@@ -439,70 +450,73 @@ valfun_1({deallocate,StkSize}, #vst{current=#st{numy=StkSize}}=Vst) ->
deallocate(Vst);
valfun_1({deallocate,_}, #vst{current=#st{numy=NumY}}) ->
error({allocated,NumY});
-valfun_1({trim,N,Remaining}, #vst{current=#st{y=Yregs0,numy=NumY}=St}=Vst) ->
+valfun_1({trim,N,Remaining}, #vst{current=St0}=Vst) ->
+ #st{numy=NumY} = St0,
if
- N =< NumY, N+Remaining =:= NumY ->
- Yregs1 = [{Y-N,Type} || {Y,Type} <- gb_trees:to_list(Yregs0), Y >= N],
- Yregs = gb_trees_from_list(Yregs1),
- Vst#vst{current=St#st{y=Yregs,numy=NumY-N,aliases=#{}}};
- true ->
- error({trim,N,Remaining,allocated,NumY})
+ N =< NumY, N+Remaining =:= NumY ->
+ Vst#vst{current=trim_stack(N, 0, NumY, St0)};
+ N > NumY; N+Remaining =/= NumY ->
+ error({trim,N,Remaining,allocated,NumY})
end;
%% Catch & try.
valfun_1({'catch',Dst,{f,Fail}}, Vst) when Fail =/= none ->
init_try_catch_branch(catchtag, Dst, Fail, Vst);
valfun_1({'try',Dst,{f,Fail}}, Vst) when Fail =/= none ->
init_try_catch_branch(trytag, Dst, Fail, Vst);
-valfun_1({catch_end,Reg}, #vst{current=#st{ct=[Fail|Fails]}}=Vst0) ->
- case get_special_y_type(Reg, Vst0) of
- {catchtag,Fail} ->
- Vst = #vst{current=St} = set_catch_end(Reg, Vst0),
- Xregs = gb_trees:enter(0, term, St#st.x),
- Vst#vst{current=St#st{x=Xregs,ct=Fails,fls=undefined,aliases=#{}}};
- Type ->
- error({bad_type,Type})
+valfun_1({catch_end,Reg}, #vst{current=#st{ct=[Fail|_]}}=Vst0) ->
+ case get_tag_type(Reg, Vst0) of
+ {catchtag,Fail} ->
+ %% {x,0} contains the caught term, if any.
+ create_term(term, catch_end, [], {x,0}, kill_catch_tag(Reg, Vst0));
+ Type ->
+ error({wrong_tag_type,Type})
end;
-valfun_1({try_end,Reg}, #vst{current=#st{ct=[Fail|Fails]}=St0}=Vst) ->
- case get_special_y_type(Reg, Vst) of
- {trytag,Fail} ->
- St = St0#st{ct=Fails,fls=undefined},
- set_catch_end(Reg, Vst#vst{current=St});
- Type ->
- error({bad_type,Type})
+valfun_1({try_end,Reg}, #vst{current=#st{ct=[Fail|_]}}=Vst) ->
+ case get_tag_type(Reg, Vst) of
+ {trytag,Fail} ->
+ %% Kill the catch tag, note that x registers are unaffected.
+ kill_catch_tag(Reg, Vst);
+ Type ->
+ error({wrong_tag_type,Type})
end;
-valfun_1({try_case,Reg}, #vst{current=#st{ct=[Fail|Fails]}}=Vst0) ->
- case get_special_y_type(Reg, Vst0) of
- {trytag,Fail} ->
- Vst = #vst{current=St} = set_catch_end(Reg, Vst0),
- Xs = gb_trees_from_list([{0,{atom,[]}},{1,term},{2,term}]),
- Vst#vst{current=St#st{x=Xs,ct=Fails,fls=undefined,aliases=#{}}};
- Type ->
- error({bad_type,Type})
+valfun_1({try_case,Reg}, #vst{current=#st{ct=[Fail|_]}}=Vst0) ->
+ case get_tag_type(Reg, Vst0) of
+ {trytag,Fail} ->
+ %% Kill the catch tag and all x registers.
+ Vst1 = prune_x_regs(0, kill_catch_tag(Reg, Vst0)),
+
+ %% Class:Error:Stacktrace
+ Vst2 = create_term({atom,[]}, try_case, [], {x,0}, Vst1),
+ Vst = create_term(term, try_case, [], {x,1}, Vst2),
+ create_term(term, try_case, [], {x,2}, Vst);
+ Type ->
+ error({wrong_tag_type,Type})
end;
valfun_1({get_list,Src,D1,D2}, Vst0) ->
assert_not_literal(Src),
assert_type(cons, Src, Vst0),
- Vst = extract_term(term, [Src], D1, Vst0),
- extract_term(term, [Src], D2, Vst);
+ Vst = extract_term(term, get_hd, [Src], D1, Vst0),
+ extract_term(term, get_tl, [Src], D2, Vst);
valfun_1({get_hd,Src,Dst}, Vst) ->
assert_not_literal(Src),
assert_type(cons, Src, Vst),
- extract_term(term, [Src], Dst, Vst);
+ extract_term(term, get_hd, [Src], Dst, Vst);
valfun_1({get_tl,Src,Dst}, Vst) ->
assert_not_literal(Src),
assert_type(cons, Src, Vst),
- extract_term(term, [Src], Dst, Vst);
-valfun_1({get_tuple_element,Src,I,Dst}, Vst) ->
+ extract_term(term, get_tl, [Src], Dst, Vst);
+valfun_1({get_tuple_element,Src,N,Dst}, Vst) ->
assert_not_literal(Src),
- assert_type({tuple_element,I+1}, Src, Vst),
- extract_term(term, [Src], Dst, Vst);
+ assert_type({tuple_element,N+1}, Src, Vst),
+ Type = get_element_type(N+1, Src, Vst),
+ extract_term(Type, get_tuple_element, [Src], Dst, Vst);
valfun_1({jump,{f,Lbl}}, Vst) ->
kill_state(branch_state(Lbl, Vst));
valfun_1(I, Vst) ->
valfun_2(I, Vst).
init_try_catch_branch(Tag, Dst, Fail, Vst0) ->
- Vst1 = set_type_y({Tag,[Fail]}, Dst, Vst0),
+ Vst1 = create_tag({Tag,[Fail]}, 'try_catch', [], Dst, Vst0),
#vst{current=#st{ct=Fails}=St0} = Vst1,
CurrentSt = St0#st{ct=[[Fail]|Fails]},
@@ -530,19 +544,20 @@ valfun_2(_, _) ->
%% Handle the remaining floating point instructions here.
%% Floating point.
-valfun_3({fconv,Src,{fr,_}=Dst}, Vst) ->
- assert_term(Src, Vst),
+valfun_3({fconv,Src,{fr,_}=Dst}, Vst0) ->
+ assert_term(Src, Vst0),
+ Vst = update_type(fun meet/2, number, Src, Vst0),
set_freg(Dst, Vst);
-valfun_3({bif,fadd,_,[_,_]=Src,Dst}, Vst) ->
- float_op(Src, Dst, Vst);
-valfun_3({bif,fdiv,_,[_,_]=Src,Dst}, Vst) ->
- float_op(Src, Dst, Vst);
-valfun_3({bif,fmul,_,[_,_]=Src,Dst}, Vst) ->
- float_op(Src, Dst, Vst);
-valfun_3({bif,fnegate,_,[_]=Src,Dst}, Vst) ->
- float_op(Src, Dst, Vst);
-valfun_3({bif,fsub,_,[_,_]=Src,Dst}, Vst) ->
- float_op(Src, Dst, Vst);
+valfun_3({bif,fadd,_,[_,_]=Ss,Dst}, Vst) ->
+ float_op(Ss, Dst, Vst);
+valfun_3({bif,fdiv,_,[_,_]=Ss,Dst}, Vst) ->
+ float_op(Ss, Dst, Vst);
+valfun_3({bif,fmul,_,[_,_]=Ss,Dst}, Vst) ->
+ float_op(Ss, Dst, Vst);
+valfun_3({bif,fnegate,_,[_]=Ss,Dst}, Vst) ->
+ float_op(Ss, Dst, Vst);
+valfun_3({bif,fsub,_,[_,_]=Ss,Dst}, Vst) ->
+ float_op(Ss, Dst, Vst);
valfun_3(fclearerror, Vst) ->
case get_fls(Vst) of
undefined -> ok;
@@ -593,43 +608,40 @@ valfun_4({call_ext_last,_,_,_}, #vst{current=#st{numy=NumY}}) ->
valfun_4({make_fun2,_,_,_,Live}, Vst) ->
call(make_fun, Live, Vst);
%% Other BIFs
-valfun_4({bif,tuple_size,{f,Fail},[Tuple],Dst}=I, Vst0) ->
- Vst1 = branch_state(Fail, Vst0),
- Vst = update_type(fun meet/2, {tuple,[0]}, Tuple, Vst1),
- set_type_reg_expr({integer,[]}, I, Dst, Vst);
valfun_4({bif,element,{f,Fail},[Pos,Tuple],Dst}, Vst0) ->
PosType = get_durable_term_type(Pos, Vst0),
+ ElementType = case PosType of
+ {integer,I} -> get_element_type(I, Tuple, Vst0);
+ _ -> term
+ end,
+ InferredType = {tuple,[get_tuple_size(PosType)],#{}},
Vst1 = branch_state(Fail, Vst0),
- Type = {tuple,[get_tuple_size(PosType)]},
- Vst = update_type(fun meet/2, Type, Tuple, Vst1),
- extract_term(term, [Tuple], Dst, Vst);
+ Vst = update_type(fun meet/2, InferredType, Tuple, Vst1),
+ extract_term(ElementType, {bif,element}, [Tuple], Dst, Vst);
valfun_4({bif,raise,{f,0},Src,_Dst}, Vst) ->
validate_src(Src, Vst),
kill_state(Vst);
valfun_4(raw_raise=I, Vst) ->
call(I, 3, Vst);
-valfun_4({bif,map_get,{f,Fail},[_Key,Map]=Ss,Dst}, Vst0) ->
- validate_src(Ss, Vst0),
- Vst1 = branch_state(Fail, Vst0),
- Vst = update_type(fun meet/2, map, Map, Vst1),
- extract_term(term, Ss, Dst, Vst);
-valfun_4({bif,is_map_key,{f,Fail},[_Key,Map]=Ss,Dst}, Vst0) ->
- validate_src(Ss, Vst0),
- Vst1 = branch_state(Fail, Vst0),
- Vst = update_type(fun meet/2, map, Map, Vst1),
- extract_term(bool, Ss, Dst, Vst);
valfun_4({bif,Op,{f,Fail},[Cons]=Ss,Dst}, Vst0)
when Op =:= hd; Op =:= tl ->
validate_src(Ss, Vst0),
- Vst1 = branch_state(Fail, Vst0),
- Vst = update_type(fun meet/2, cons, Cons, Vst1),
- Type = bif_type(Op, Ss, Vst),
- extract_term(Type, Ss, Dst, Vst);
+ Vst = type_test(Fail, cons, Cons, Vst0),
+ Type = bif_return_type(Op, Ss, Vst),
+ extract_term(Type, {bif,Op}, Ss, Dst, Vst);
valfun_4({bif,Op,{f,Fail},Ss,Dst}, Vst0) ->
validate_src(Ss, Vst0),
- Vst = branch_state(Fail, Vst0),
- Type = bif_type(Op, Ss, Vst),
- extract_term(Type, Ss, Dst, Vst);
+ Vst1 = branch_state(Fail, Vst0),
+
+ %% Infer argument types. Note that we can't type_test in the general case
+ %% as the BIF could fail for reasons other than bad arguments.
+ ArgTypes = bif_arg_types(Op, Ss),
+ Vst = foldl(fun({Arg, T}, Vsti) ->
+ update_type(fun meet/2, T, Arg, Vsti)
+ end, Vst1, zip(Ss, ArgTypes)),
+
+ Type = bif_return_type(Op, Ss, Vst),
+ extract_term(Type, {bif,Op}, Ss, Dst, Vst);
valfun_4({gc_bif,Op,{f,Fail},Live,Ss,Dst}, #vst{current=St0}=Vst0) ->
validate_src(Ss, Vst0),
verify_live(Live, Vst0),
@@ -637,14 +649,15 @@ valfun_4({gc_bif,Op,{f,Fail},Live,Ss,Dst}, #vst{current=St0}=Vst0) ->
St = kill_heap_allocation(St0),
Vst1 = Vst0#vst{current=St},
Vst2 = branch_state(Fail, Vst1),
- Vst3 = case Op of
- length -> update_type(fun meet/2, list, hd(Ss), Vst2);
- map_size -> update_type(fun meet/2, map, hd(Ss), Vst2);
- _ -> Vst2
- end,
- Type = bif_type(Op, Ss, Vst3),
+
+ ArgTypes = bif_arg_types(Op, Ss),
+ Vst3 = foldl(fun({Arg, T}, Vsti) ->
+ update_type(fun meet/2, T, Arg, Vsti)
+ end, Vst2, zip(Ss, ArgTypes)),
+
+ Type = bif_return_type(Op, Ss, Vst3),
Vst = prune_x_regs(Live, Vst3),
- extract_term(Type, Ss, Dst, Vst, Vst0);
+ extract_term(Type, {gc_bif,Op}, Ss, Dst, Vst, Vst0);
valfun_4(return, #vst{current=#st{numy=none}}=Vst) ->
assert_not_fragile({x,0}, Vst),
kill_state(Vst);
@@ -656,7 +669,7 @@ valfun_4({loop_rec,{f,Fail},Dst}, Vst0) ->
%% remove_message/0 is executed. If control transfers
%% to the loop_rec_end/1 instruction, no part of
%% this term must be stored in a Y register.
- create_term({fragile,term}, Dst, Vst);
+ create_term({fragile,term}, loop_rec, [], Dst, Vst);
valfun_4({wait,_}, Vst) ->
verify_y_init(Vst),
kill_state(Vst);
@@ -671,22 +684,26 @@ valfun_4(timeout, #vst{current=St}=Vst) ->
Vst#vst{current=St#st{x=init_regs(0, term)}};
valfun_4(send, Vst) ->
call(send, 2, Vst);
-valfun_4({set_tuple_element,Src,Tuple,I}, Vst) ->
+valfun_4({set_tuple_element,Src,Tuple,N}, Vst) ->
+ I = N + 1,
assert_not_fragile(Src, Vst),
- assert_type({tuple_element,I+1}, Tuple, Vst),
- Vst;
+ assert_type({tuple_element,I}, Tuple, Vst),
+ %% Manually update the tuple type; we can't rely on the ordinary update
+ %% helpers as we must support overwriting (rather than just widening or
+ %% narrowing) known elements, and we can't use extract_term either since
+ %% the source tuple may be aliased.
+ {tuple, Sz, Es0} = get_term_type(Tuple, Vst),
+ Es = set_element_type(I, get_term_type(Src, Vst), Es0),
+ override_type({tuple, Sz, Es}, Tuple, Vst);
%% Match instructions.
valfun_4({select_val,Src,{f,Fail},{list,Choices}}, Vst0) ->
assert_term(Src, Vst0),
assert_choices(Choices),
- Vst = branch_state(Fail, Vst0),
- kill_state(select_val_branches(Src, Choices, Vst));
+ select_val_branches(Fail, Src, Choices, Vst0);
valfun_4({select_tuple_arity,Tuple,{f,Fail},{list,Choices}}, Vst) ->
assert_type(tuple, Tuple, Vst),
assert_arities(Choices),
- TupleType = get_durable_term_type(Tuple, Vst),
- kill_state(branch_arities(Choices, Tuple, TupleType,
- branch_state(Fail, Vst)));
+ select_arity_branches(Fail, Choices, Tuple, Vst);
%% New bit syntax matching instructions.
valfun_4({test,bs_start_match3,{f,Fail},Live,[Src],Dst}, Vst) ->
@@ -712,19 +729,18 @@ valfun_4({test,bs_skip_utf16,{f,Fail},[Ctx,Live,_]}, Vst) ->
validate_bs_skip_utf(Fail, Ctx, Live, Vst);
valfun_4({test,bs_skip_utf32,{f,Fail},[Ctx,Live,_]}, Vst) ->
validate_bs_skip_utf(Fail, Ctx, Live, Vst);
-valfun_4({test,bs_get_integer2,{f,Fail},Live,[Ctx,_,_,_],Dst}, Vst) ->
- validate_bs_get(Fail, Ctx, Live, {integer, []}, Dst, Vst);
-valfun_4({test,bs_get_float2,{f,Fail},Live,[Ctx,_,_,_],Dst}, Vst) ->
- validate_bs_get(Fail, Ctx, Live, {float, []}, Dst, Vst);
-valfun_4({test,bs_get_binary2,{f,Fail},Live,[Ctx,_,_,_],Dst}, Vst) ->
- Type = propagate_fragility(term, [Ctx], Vst),
- validate_bs_get(Fail, Ctx, Live, Type, Dst, Vst);
-valfun_4({test,bs_get_utf8,{f,Fail},Live,[Ctx,_],Dst}, Vst) ->
- validate_bs_get(Fail, Ctx, Live, {integer, []}, Dst, Vst);
-valfun_4({test,bs_get_utf16,{f,Fail},Live,[Ctx,_],Dst}, Vst) ->
- validate_bs_get(Fail, Ctx, Live, {integer, []}, Dst, Vst);
-valfun_4({test,bs_get_utf32,{f,Fail},Live,[Ctx,_],Dst}, Vst) ->
- validate_bs_get(Fail, Ctx, Live, {integer, []}, Dst, Vst);
+valfun_4({test,bs_get_integer2=Op,{f,Fail},Live,[Ctx,_,_,_],Dst}, Vst) ->
+ validate_bs_get(Op, Fail, Ctx, Live, {integer, []}, Dst, Vst);
+valfun_4({test,bs_get_float2=Op,{f,Fail},Live,[Ctx,_,_,_],Dst}, Vst) ->
+ validate_bs_get(Op, Fail, Ctx, Live, {float, []}, Dst, Vst);
+valfun_4({test,bs_get_binary2=Op,{f,Fail},Live,[Ctx,_,_,_],Dst}, Vst) ->
+ validate_bs_get(Op, Fail, Ctx, Live, binary, Dst, Vst);
+valfun_4({test,bs_get_utf8=Op,{f,Fail},Live,[Ctx,_],Dst}, Vst) ->
+ validate_bs_get(Op, Fail, Ctx, Live, {integer, []}, Dst, Vst);
+valfun_4({test,bs_get_utf16=Op,{f,Fail},Live,[Ctx,_],Dst}, Vst) ->
+ validate_bs_get(Op, Fail, Ctx, Live, {integer, []}, Dst, Vst);
+valfun_4({test,bs_get_utf32=Op,{f,Fail},Live,[Ctx,_],Dst}, Vst) ->
+ validate_bs_get(Op, Fail, Ctx, Live, {integer, []}, Dst, Vst);
valfun_4({bs_save2,Ctx,SavePoint}, Vst) ->
bsm_save(Ctx, SavePoint, Vst);
valfun_4({bs_restore2,Ctx,SavePoint}, Vst) ->
@@ -734,7 +750,7 @@ valfun_4({bs_get_position, Ctx, Dst, Live}, Vst0) ->
verify_live(Live, Vst0),
verify_y_init(Vst0),
Vst = prune_x_regs(Live, Vst0),
- create_term(bs_position, Dst, Vst);
+ create_term(bs_position, bs_get_position, [Ctx], Dst, Vst);
valfun_4({bs_set_position, Ctx, Pos}, Vst) ->
bsm_validate_context(Ctx, Vst),
assert_type(bs_position, Pos, Vst),
@@ -748,7 +764,7 @@ valfun_4({test,is_boolean,{f,Lbl},[Src]}, Vst) ->
valfun_4({test,is_float,{f,Lbl},[Src]}, Vst) ->
type_test(Lbl, {float,[]}, Src, Vst);
valfun_4({test,is_tuple,{f,Lbl},[Src]}, Vst) ->
- type_test(Lbl, {tuple,[0]}, Src, Vst);
+ type_test(Lbl, {tuple,[0],#{}}, Src, Vst);
valfun_4({test,is_integer,{f,Lbl},[Src]}, Vst) ->
type_test(Lbl, {integer,[]}, Src, Vst);
valfun_4({test,is_nonempty_list,{f,Lbl},[Src]}, Vst) ->
@@ -767,43 +783,49 @@ valfun_4({test,is_map,{f,Lbl},[Src]}, Vst) ->
assert_term(Src, Vst),
kill_state(Vst)
end;
-valfun_4({test,test_arity,{f,Lbl},[Tuple,Sz]}, Vst) when is_integer(Sz) ->
- assert_type(tuple, Tuple, Vst),
- update_type(fun meet/2, {tuple,Sz}, Tuple, branch_state(Lbl, Vst));
-valfun_4({test,is_tagged_tuple,{f,Lbl},[Src,Sz,_Atom]}, Vst) ->
- assert_term(Src, Vst),
- update_type(fun meet/2, {tuple,Sz}, Src, branch_state(Lbl, Vst));
+valfun_4({test,test_arity,{f,Lbl},[Tuple,Sz]}, Vst0) when is_integer(Sz) ->
+ assert_type(tuple, Tuple, Vst0),
+ Vst = branch_state(Lbl, Vst0),
+ update_type(fun meet/2, {tuple,Sz,#{}}, Tuple, Vst);
+valfun_4({test,is_tagged_tuple,{f,Lbl},[Src,Sz,Atom]}, Vst0) ->
+ assert_term(Src, Vst0),
+ Vst = branch_state(Lbl, Vst0),
+ update_type(fun meet/2, {tuple,Sz,#{ 1 => Atom }}, Src, Vst);
valfun_4({test,has_map_fields,{f,Lbl},Src,{list,List}}, Vst) ->
assert_type(map, Src, Vst),
assert_unique_map_keys(List),
branch_state(Lbl, Vst);
-valfun_4({test,is_eq_exact,{f,Lbl},[Src,Val]=Ss}, Vst0) ->
- validate_src(Ss, Vst0),
- Infer = infer_types(Src, Vst0),
- Vst1 = Infer(Val, Vst0),
- Vst2 = update_ne_types(Src, Val, Vst1),
- Vst3 = branch_state(Lbl, Vst2),
- Vst = Vst3#vst{current=Vst1#vst.current},
- update_eq_types(Src, Val, Vst);
-valfun_4({test,is_ne_exact,{f,Lbl},[Src,Val]=Ss}, Vst0) ->
- validate_src(Ss, Vst0),
- Vst1 = update_eq_types(Src, Val, Vst0),
- Vst2 = branch_state(Lbl, Vst1),
- Vst = Vst2#vst{current=Vst0#vst.current},
- update_ne_types(Src, Val, Vst);
+valfun_4({test,is_eq_exact,{f,Lbl},[Src,Val]=Ss}, Vst) ->
+ validate_src(Ss, Vst),
+ complex_test(Lbl,
+ fun(FailVst) ->
+ update_ne_types(Src, Val, FailVst)
+ end,
+ fun(SuccVst) ->
+ update_eq_types(Src, Val, SuccVst)
+ end, Vst);
+valfun_4({test,is_ne_exact,{f,Lbl},[Src,Val]=Ss}, Vst) ->
+ validate_src(Ss, Vst),
+ complex_test(Lbl,
+ fun(FailVst) ->
+ update_eq_types(Src, Val, FailVst)
+ end,
+ fun(SuccVst) ->
+ update_ne_types(Src, Val, SuccVst)
+ end, Vst);
valfun_4({test,_Op,{f,Lbl},Src}, Vst) ->
validate_src(Src, Vst),
branch_state(Lbl, Vst);
valfun_4({bs_add,{f,Fail},[A,B,_],Dst}, Vst) ->
assert_not_fragile(A, Vst),
assert_not_fragile(B, Vst),
- create_term({integer,[]}, Dst, branch_state(Fail, Vst));
+ create_term({integer,[]}, bs_add, [A, B], Dst, branch_state(Fail, Vst));
valfun_4({bs_utf8_size,{f,Fail},A,Dst}, Vst) ->
assert_term(A, Vst),
- create_term({integer,[]}, Dst, branch_state(Fail, Vst));
+ create_term({integer,[]}, bs_utf8_size, [A], Dst, branch_state(Fail, Vst));
valfun_4({bs_utf16_size,{f,Fail},A,Dst}, Vst) ->
assert_term(A, Vst),
- create_term({integer,[]}, Dst, branch_state(Fail, Vst));
+ create_term({integer,[]}, bs_utf16_size, [A], Dst, branch_state(Fail, Vst));
valfun_4({bs_init2,{f,Fail},Sz,Heap,Live,_,Dst}, Vst0) ->
verify_live(Live, Vst0),
verify_y_init(Vst0),
@@ -816,7 +838,7 @@ valfun_4({bs_init2,{f,Fail},Sz,Heap,Live,_,Dst}, Vst0) ->
Vst1 = heap_alloc(Heap, Vst0),
Vst2 = branch_state(Fail, Vst1),
Vst = prune_x_regs(Live, Vst2),
- create_term(binary, Dst, Vst);
+ create_term(binary, bs_init2, [], Dst, Vst);
valfun_4({bs_init_bits,{f,Fail},Sz,Heap,Live,_,Dst}, Vst0) ->
verify_live(Live, Vst0),
verify_y_init(Vst0),
@@ -829,7 +851,7 @@ valfun_4({bs_init_bits,{f,Fail},Sz,Heap,Live,_,Dst}, Vst0) ->
Vst1 = heap_alloc(Heap, Vst0),
Vst2 = branch_state(Fail, Vst1),
Vst = prune_x_regs(Live, Vst2),
- create_term(binary, Dst, Vst);
+ create_term(binary, bs_init_bits, [], Dst, Vst);
valfun_4({bs_append,{f,Fail},Bits,Heap,Live,_Unit,Bin,_Flags,Dst}, Vst0) ->
verify_live(Live, Vst0),
verify_y_init(Vst0),
@@ -838,12 +860,12 @@ valfun_4({bs_append,{f,Fail},Bits,Heap,Live,_Unit,Bin,_Flags,Dst}, Vst0) ->
Vst1 = heap_alloc(Heap, Vst0),
Vst2 = branch_state(Fail, Vst1),
Vst = prune_x_regs(Live, Vst2),
- create_term(binary, Dst, Vst);
+ create_term(binary, bs_append, [Bin], Dst, Vst);
valfun_4({bs_private_append,{f,Fail},Bits,_Unit,Bin,_Flags,Dst}, Vst0) ->
assert_not_fragile(Bits, Vst0),
assert_not_fragile(Bin, Vst0),
Vst = branch_state(Fail, Vst0),
- create_term(binary, Dst, Vst);
+ create_term(binary, bs_private_append, [Bin], Dst, Vst);
valfun_4({bs_put_string,Sz,_}, Vst) when is_integer(Sz) ->
Vst;
valfun_4({bs_put_binary,{f,Fail},Sz,_,_,Src}, Vst) ->
@@ -868,10 +890,10 @@ valfun_4({bs_put_utf32,{f,Fail},_,Src}, Vst) ->
assert_not_fragile(Src, Vst),
branch_state(Fail, Vst);
%% Map instructions.
-valfun_4({put_map_assoc,{f,Fail},Src,Dst,Live,{list,List}}, Vst) ->
- verify_put_map(Fail, Src, Dst, Live, List, Vst);
-valfun_4({put_map_exact,{f,Fail},Src,Dst,Live,{list,List}}, Vst) ->
- verify_put_map(Fail, Src, Dst, Live, List, Vst);
+valfun_4({put_map_assoc=Op,{f,Fail},Src,Dst,Live,{list,List}}, Vst) ->
+ verify_put_map(Op, Fail, Src, Dst, Live, List, Vst);
+valfun_4({put_map_exact=Op,{f,Fail},Src,Dst,Live,{list,List}}, Vst) ->
+ verify_put_map(Op, Fail, Src, Dst, Live, List, Vst);
valfun_4({get_map_elements,{f,Fail},Src,{list,List}}, Vst) ->
verify_get_map(Fail, Src, List, Vst);
valfun_4(_, _) ->
@@ -880,69 +902,90 @@ valfun_4(_, _) ->
verify_get_map(Fail, Src, List, Vst0) ->
assert_not_literal(Src), %OTP 22.
assert_type(map, Src, Vst0),
- Vst1 = foldl(fun(D, Vsti) ->
- case is_reg_defined(D,Vsti) of
- true -> create_term(term, D, Vsti);
- false -> Vsti
- end
- end, Vst0, extract_map_vals(List)),
- Vst2 = branch_state(Fail, Vst1),
- Keys = extract_map_keys(List),
- assert_unique_map_keys(Keys),
- verify_get_map_pair(List, Src, Vst0, Vst2).
-extract_map_vals([_Key,Val|T]) ->
- [Val|extract_map_vals(T)];
-extract_map_vals([]) -> [].
+ complex_test(Fail,
+ fun(FailVst) ->
+ clobber_map_vals(List, Src, FailVst)
+ end,
+ fun(SuccVst) ->
+ Keys = extract_map_keys(List),
+ assert_unique_map_keys(Keys),
+ extract_map_vals(List, Src, SuccVst, SuccVst)
+ end, Vst0).
+
+%% get_map_elements may leave its destinations in an inconsistent state when
+%% the fail label is taken. Consider the following:
+%%
+%% {get_map_elements,{f,7},{x,1},{list,[{atom,a},{x,1},{atom,b},{x,2}]}}.
+%%
+%% If 'a' exists but not 'b', {x,1} is overwritten when we jump to {f,7}.
+clobber_map_vals([Key,Dst|T], Map, Vst0) ->
+ case is_reg_defined(Dst, Vst0) of
+ true ->
+ Vst = extract_term(term, {bif,map_get}, [Key, Map], Dst, Vst0),
+ clobber_map_vals(T, Map, Vst);
+ false ->
+ clobber_map_vals(T, Map, Vst0)
+ end;
+clobber_map_vals([], _Map, Vst) ->
+ Vst.
extract_map_keys([Key,_Val|T]) ->
[Key|extract_map_keys(T)];
extract_map_keys([]) -> [].
-verify_get_map_pair([Src,Dst|Vs], Map, Vst0, Vsti0) ->
- assert_term(Src, Vst0),
- Vsti = extract_term(term, [Map], Dst, Vsti0),
- verify_get_map_pair(Vs, Map, Vst0, Vsti);
-verify_get_map_pair([], _Map, _Vst0, Vst) -> Vst.
+extract_map_vals([Key,Dst|Vs], Map, Vst0, Vsti0) ->
+ assert_term(Key, Vst0),
+ Vsti = extract_term(term, {bif,map_get}, [Key, Map], Dst, Vsti0),
+ extract_map_vals(Vs, Map, Vst0, Vsti);
+extract_map_vals([], _Map, _Vst0, Vst) ->
+ Vst.
-verify_put_map(Fail, Src, Dst, Live, List, Vst0) ->
+verify_put_map(Op, Fail, Src, Dst, Live, List, Vst0) ->
assert_type(map, Src, Vst0),
verify_live(Live, Vst0),
verify_y_init(Vst0),
- foreach(fun (Term) -> assert_not_fragile(Term, Vst0) end, List),
+ [assert_not_fragile(Term, Vst0) || Term <- List],
Vst1 = heap_alloc(0, Vst0),
Vst2 = branch_state(Fail, Vst1),
Vst = prune_x_regs(Live, Vst2),
Keys = extract_map_keys(List),
assert_unique_map_keys(Keys),
- create_term(map, Dst, Vst).
+ create_term(map, Op, [Src], Dst, Vst).
%%
%% Common code for validating bs_start_match* instructions.
%%
-validate_bs_start_match(Fail, Live, Type, Src, Dst, Vst0) ->
- verify_live(Live, Vst0),
- verify_y_init(Vst0),
+validate_bs_start_match(Fail, Live, Type, Src, Dst, Vst) ->
+ verify_live(Live, Vst),
+ verify_y_init(Vst),
%% #ms{} can represent either a match context or a term, so we have to mark
%% the source as a term if it fails, and retain the incoming type if it
%% succeeds (match context or not).
- Vst1 = set_aliased_type(term, Src, Vst0),
- Vst2 = prune_x_regs(Live, Vst1),
- Vst3 = branch_state(Fail, Vst2),
- extract_term(Type, [Src], Dst, Vst3, Vst0).
+ %%
+ %% The override_type hack is only needed until we get proper union types.
+ complex_test(Fail,
+ fun(FailVst) ->
+ override_type(term, Src, FailVst)
+ end,
+ fun(SuccVst0) ->
+ SuccVst = prune_x_regs(Live, SuccVst0),
+ extract_term(Type, bs_start_match, [Src], Dst,
+ SuccVst, Vst)
+ end, Vst).
%%
%% Common code for validating bs_get* instructions.
%%
-validate_bs_get(Fail, Ctx, Live, Type, Dst, Vst0) ->
+validate_bs_get(Op, Fail, Ctx, Live, Type, Dst, Vst0) ->
bsm_validate_context(Ctx, Vst0),
verify_live(Live, Vst0),
verify_y_init(Vst0),
Vst1 = prune_x_regs(Live, Vst0),
Vst = branch_state(Fail, Vst1),
- create_term(Type, Dst, Vst).
+ extract_term(Type, Op, [Ctx], Dst, Vst).
%%
%% Common code for validating bs_skip_utf* instructions.
@@ -983,14 +1026,15 @@ kill_state(Vst) ->
%% A "plain" call.
%% The stackframe must be initialized.
%% The instruction will return to the instruction following the call.
-call(Name, Live, #vst{current=St}=Vst) ->
- verify_call_args(Name, Live, Vst),
- verify_y_init(Vst),
- case return_type(Name, Vst) of
- Type when Type =/= exception ->
- %% Type is never 'exception' because it has been handled earlier.
- Xs = gb_trees_from_list([{0,Type}]),
- Vst#vst{current=St#st{x=Xs,f=init_fregs(),aliases=#{}}}
+call(Name, Live, #vst{current=St0}=Vst0) ->
+ verify_call_args(Name, Live, Vst0),
+ verify_y_init(Vst0),
+ case call_return_type(Name, Vst0) of
+ Type when Type =/= exception ->
+ %% Type is never 'exception' because it has been handled earlier.
+ St = St0#st{f=init_fregs(),aliases=#{}},
+ Vst = prune_x_regs(0, Vst0#vst{current=St}),
+ create_term(Type, call, [], {x,0}, Vst)
end.
%% Tail call.
@@ -1006,42 +1050,36 @@ tail_call(Name, Live, Vst0) ->
verify_call_args(_, 0, #vst{}) ->
ok;
verify_call_args({f,Lbl}, Live, Vst) when is_integer(Live)->
- verify_local_call(Lbl, Live, Vst);
+ verify_local_args(Live - 1, Lbl, #{}, Vst);
verify_call_args(_, Live, Vst) when is_integer(Live)->
- verify_call_args_1(Live, Vst);
+ verify_remote_args_1(Live - 1, Vst);
verify_call_args(_, Live, _) ->
error({bad_number_of_live_regs,Live}).
-verify_call_args_1(0, _) -> ok;
-verify_call_args_1(N, Vst) ->
- X = N - 1,
- assert_not_fragile({x,X}, Vst),
- verify_call_args_1(X, Vst).
+verify_remote_args_1(-1, _) ->
+ ok;
+verify_remote_args_1(X, Vst) ->
+ assert_not_fragile({x, X}, Vst),
+ verify_remote_args_1(X - 1, Vst).
-verify_local_call(Lbl, Live, Vst) ->
- F = fun({R, Type}) ->
- verify_arg_type(Lbl, R, Type, Vst)
- end,
- TRegs = typed_call_regs(Live, Vst),
- verify_no_ms_aliases(TRegs),
- foreach(F, TRegs).
-
-typed_call_regs(0, _Vst) ->
- [];
-typed_call_regs(Live0, Vst) ->
- Live = Live0 - 1,
- R = {x,Live},
- [{R, get_move_term_type(R, Vst)} | typed_call_regs(Live, Vst)].
-
-%% Verifies that the same match context isn't present twice.
-verify_no_ms_aliases(Regs) ->
- CtxIds = [Id || {_, #ms{id=Id}} <- Regs],
- UniqueCtxIds = ordsets:from_list(CtxIds),
- if
- length(UniqueCtxIds) < length(CtxIds) ->
- error({multiple_match_contexts, Regs});
- length(UniqueCtxIds) =:= length(CtxIds) ->
- ok
+verify_local_args(-1, _Lbl, _CtxIds, _Vst) ->
+ ok;
+verify_local_args(X, Lbl, CtxIds, Vst) ->
+ Reg = {x, X},
+ case get_raw_type(Reg, Vst) of
+ #ms{id=Id}=Type ->
+ case CtxIds of
+ #{ Id := Other } ->
+ error({multiple_match_contexts, [Reg, Other]});
+ #{} ->
+ verify_arg_type(Lbl, Reg, Type, Vst),
+ verify_local_args(X - 1, Lbl, CtxIds#{ Id => Reg }, Vst)
+ end;
+ {fragile,_} ->
+ error({fragile_message_reference, Reg});
+ Type ->
+ verify_arg_type(Lbl, Reg, Type, Vst),
+ verify_local_args(X - 1, Lbl, CtxIds, Vst)
end.
%% Verifies that the given argument narrows to what the function expects.
@@ -1087,6 +1125,25 @@ allocate(_, _, _, _, #vst{current=#st{numy=Numy}}) ->
deallocate(#vst{current=St}=Vst) ->
Vst#vst{current=St#st{y=init_regs(0, initialized),numy=none}}.
+trim_stack(From, To, Top, #st{y=Ys0}=St) when From =:= Top ->
+ Ys = foldl(fun(Y, Acc) ->
+ gb_trees:delete(Y, Acc)
+ end, Ys0, seq(To, From - 1)),
+ %% Note that all aliases and defs are wiped. This is perhaps a bit too
+ %% conservative, but preserving them won't be easy until type management
+ %% is refactored.
+ St#st{aliases=#{},defs=#{},numy=To,y=Ys};
+trim_stack(From, To, Top, St0) ->
+ #st{y=Ys0} = St0,
+
+ Ys = case gb_trees:lookup(From, Ys0) of
+ none -> error({invalid_shift,{y,From},{y,To}});
+ {value,Type} -> gb_trees:enter(To, Type, Ys0)
+ end,
+
+ St = St0#st{y=Ys},
+ trim_stack(From + 1, To + 1, Top, St).
+
test_heap(Heap, Live, Vst0) ->
verify_live(Live, Vst0),
verify_y_init(Vst0),
@@ -1173,8 +1230,8 @@ assert_arities(_) -> error(bad_tuple_arity_list).
%%% fmove Src {fr,_} %% Move INTO floating point register.
%%%
-float_op(Src, Dst, Vst0) ->
- foreach (fun(S) -> assert_freg_set(S, Vst0) end, Src),
+float_op(Ss, Dst, Vst0) ->
+ [assert_freg_set(S, Vst0) || S <- Ss],
assert_fls(cleared, Vst0),
Vst = set_fls(cleared, Vst0),
set_freg(Dst, Vst).
@@ -1229,7 +1286,10 @@ assert_unique_map_keys([]) ->
assert_unique_map_keys([_]) ->
ok;
assert_unique_map_keys([_,_|_]=Ls) ->
- Vs = [get_literal(L) || L <- Ls],
+ Vs = [begin
+ assert_literal(L),
+ L
+ end || L <- Ls],
case length(Vs) =:= sets:size(sets:from_list(Vs)) of
true -> ok;
false -> error(keys_not_unique)
@@ -1271,7 +1331,7 @@ bsm_save(Reg, SavePoint, Vst) ->
case bsm_get_context(Reg, Vst) of
#ms{valid=Bits,slots=Slots}=Ctxt0 when SavePoint < Slots ->
Ctx = Ctxt0#ms{valid=Bits bor (1 bsl SavePoint),slots=Slots},
- set_type_reg(Ctx, Reg, Vst);
+ override_type(Ctx, Reg, Vst);
_ -> error({illegal_save,SavePoint})
end.
@@ -1290,37 +1350,89 @@ bsm_restore(Reg, SavePoint, Vst) ->
_ -> error({illegal_restore,SavePoint,range})
end.
-select_val_branches(Src, Choices, Vst) ->
- Infer = infer_types(Src, Vst),
- select_val_branches_1(Choices, Src, Infer, Vst).
-
-select_val_branches_1([Val,{f,L}|T], Src, Infer, Vst0) ->
- Vst1 = set_aliased_type(Val, Src, Infer(Val, Vst0)),
- Vst = branch_state(L, Vst1),
- select_val_branches_1(T, Src, Infer, Vst);
-select_val_branches_1([], _, _, Vst) -> Vst.
+select_val_branches(Fail, Src, Choices, Vst0) ->
+ Vst = svb_1(Choices, Src, Vst0),
+ kill_state(branch_state(Fail, Vst)).
+
+svb_1([Val,{f,L}|T], Src, Vst0) ->
+ Vst = complex_test(L,
+ fun(BranchVst) ->
+ update_eq_types(Val, Src, BranchVst)
+ end,
+ fun(FailVst) ->
+ update_ne_types(Val, Src, FailVst)
+ end, Vst0),
+ svb_1(T, Src, Vst);
+svb_1([], _, Vst) ->
+ Vst.
+
+select_arity_branches(Fail, List, Tuple, Vst0) ->
+ Type = get_durable_term_type(Tuple, Vst0),
+ Vst = sab_1(List, Tuple, Type, Vst0),
+ kill_state(branch_state(Fail, Vst)).
+
+sab_1([Sz,{f,L}|T], Tuple, {tuple,[_],Es}=Type0, Vst0) ->
+ #vst{current=St0} = Vst0,
+ Vst1 = update_type(fun meet/2, {tuple,Sz,Es}, Tuple, Vst0),
+ Vst2 = branch_state(L, Vst1),
+ Vst = Vst2#vst{current=St0},
+
+ sab_1(T, Tuple, Type0, Vst);
+sab_1([Sz,{f,L}|T], Tuple, {tuple,Sz,_Es}=Type, Vst0) ->
+ %% The type is already correct. (This test is redundant.)
+ Vst = branch_state(L, Vst0),
+ sab_1(T, Tuple, Type, Vst);
+sab_1([_,{f,_}|T], Tuple, Type, Vst) ->
+ %% We already have an established different exact size for the tuple.
+ %% This label can't possibly be reached.
+ sab_1(T, Tuple, Type, Vst);
+sab_1([], _, _, #vst{}=Vst) ->
+ Vst.
infer_types(Src, Vst) ->
case get_def(Src, Vst) of
- {bif,is_map,{f,_},[Map],_} ->
- fun({atom,true}, S) -> update_type(fun meet/2, map, Map, S);
- (_, S) -> S
- end;
- {bif,tuple_size,{f,_},[Tuple],_} ->
+ {{bif,tuple_size}, [Tuple]} ->
fun({integer,Arity}, S) ->
- update_type(fun meet/2, {tuple,Arity}, Tuple, S);
+ update_type(fun meet/2, {tuple,Arity,#{}}, Tuple, S);
(_, S) -> S
end;
- {bif,'=:=',{f,_},[ArityReg,{integer,_}=Val],_} when ArityReg =/= Src ->
+ {{bif,'=:='},[ArityReg,{integer,_}=Val]} when ArityReg =/= Src ->
fun({atom,true}, S) ->
Infer = infer_types(ArityReg, S),
Infer(Val, S);
(_, S) -> S
end;
+ {{bif,is_atom},[Src]} ->
+ infer_type_test_bif({atom,[]}, Src);
+ {{bif,is_boolean},[Src]} ->
+ infer_type_test_bif(bool, Src);
+ {{bif,is_binary},[Src]} ->
+ infer_type_test_bif(binary, Src);
+ {{bif,is_bitstring},[Src]} ->
+ infer_type_test_bif(binary, Src);
+ {{bif,is_float},[Src]} ->
+ infer_type_test_bif(float, Src);
+ {{bif,is_integer},[Src]} ->
+ infer_type_test_bif({integer,{}}, Src);
+ {{bif,is_list},[Src]} ->
+ infer_type_test_bif(list, Src);
+ {{bif,is_map},[Src]} ->
+ infer_type_test_bif(map, Src);
+ {{bif,is_number},[Src]} ->
+ infer_type_test_bif(number, Src);
+ {{bif,is_tuple},[Src]} ->
+ infer_type_test_bif({tuple,[0],#{}}, Src);
_ ->
fun(_, S) -> S end
end.
+infer_type_test_bif(Type, Src) ->
+ fun({atom,true}, S) ->
+ update_type(fun meet/2, Type, Src, S);
+ (_, S) ->
+ S
+ end.
+
%%%
%%% Keeping track of types.
%%%
@@ -1329,34 +1441,69 @@ infer_types(Src, Vst) ->
assign({y,_}=Src, {y,_}=Dst, Vst) ->
%% The stack trimming optimization may generate a move from an initialized
%% but unassigned Y register to another Y register.
- case get_term_type_1(Src, Vst) of
- initialized -> set_type_reg(initialized, Dst, Vst);
+ case get_raw_type(Src, Vst) of
+ initialized -> create_tag(initialized, init, [], Dst, Vst);
_ -> assign_1(Src, Dst, Vst)
end;
assign({Kind,_}=Reg, Dst, Vst) when Kind =:= x; Kind =:= y ->
assign_1(Reg, Dst, Vst);
assign(Literal, Dst, Vst) ->
- create_term(get_term_type(Literal, Vst), Dst, Vst).
+ Type = get_term_type(Literal, Vst),
+ create_term(Type, move, [Literal], Dst, Vst).
+
+%% Creates a special tag value that isn't a regular term, such as
+%% 'initialized' or 'catchtag'
+create_tag(Type, Op, Ss, {y,_}=Dst, Vst) ->
+ set_type_reg_expr(Type, {Op, Ss}, Dst, Vst);
+create_tag(_Type, _Op, _Ss, Dst, _Vst) ->
+ error({invalid_tag_register, Dst}).
+
+%% Wipes a special tag, leaving the register initialized but empty.
+kill_tag({y,Y}=Reg, #vst{current=#st{y=Ys0}=St0}=Vst) ->
+ _ = get_tag_type(Reg, Vst), %Assertion.
+ Ys = gb_trees:update(Y, initialized, Ys0),
+ Vst#vst{current=St0#st{y=Ys}}.
%% Creates a completely new term with the given type.
-create_term(Type, Dst, Vst) ->
- set_type_reg(Type, Dst, Vst).
+create_term(Type, Op, Ss, Dst, Vst) ->
+ set_type_reg_expr(Type, {Op, Ss}, Dst, Vst).
%% Extracts a term from Ss, propagating fragility.
-extract_term(Type, Ss, Dst, Vst) ->
- extract_term(Type, Ss, Dst, Vst, Vst).
+extract_term(Type, Op, Ss, Dst, Vst) ->
+ extract_term(Type, Op, Ss, Dst, Vst, Vst).
%% As extract_term/4, but uses the incoming Vst for fragility in case x-regs
%% have been pruned and the sources can no longer be found.
-extract_term(Type0, Ss, Dst, Vst, OrigVst) ->
+extract_term(Type0, Op, Ss, Dst, Vst, OrigVst) ->
Type = propagate_fragility(Type0, Ss, OrigVst),
- set_type_reg(Type, Dst, Vst).
+ set_type_reg_expr(Type, {Op, Ss}, Dst, Vst).
+
+%% Helper functions for tests that alter state on both the success and fail
+%% branches, keeping the states from tainting each other.
+complex_test(Fail, FailFun, SuccFun, Vst0) ->
+ #vst{current=St0} = Vst0,
+ Vst1 = FailFun(Vst0),
+ Vst2 = branch_state(Fail, Vst1),
+ Vst = Vst2#vst{current=St0},
+ SuccFun(Vst).
%% Helper function for simple "is_type" tests.
-type_test(Fail, Type, Reg, Vst0) ->
- assert_term(Reg, Vst0),
- Vst = branch_state(Fail, update_type(fun subtract/2, Type, Reg, Vst0)),
- update_type(fun meet/2, Type, Reg, Vst).
+type_test(Fail, Type, Reg, Vst) ->
+ assert_term(Reg, Vst),
+ complex_test(Fail,
+ fun(FailVst) ->
+ update_type(fun subtract/2, Type, Reg, FailVst)
+ end,
+ fun(SuccVst) ->
+ update_type(fun meet/2, Type, Reg, SuccVst)
+ end, Vst).
+
+%% Overrides the type of Reg. This is ugly but a necessity for certain
+%% destructive operations.
+override_type(Type, Reg, Vst) ->
+ %% Once the new type format is in, this should be expressed as:
+ %% update_type(fun(_, T) -> T end, Type, Reg, Vst).
+ set_aliased_type(Type, Reg, Vst).
%% This is used when linear code finds out more and more information about a
%% type, so that the type gets more specialized.
@@ -1375,48 +1522,36 @@ update_type(Merge, Type0, Reg, Vst) ->
none -> Type0;
T -> T
end,
- set_aliased_type(propagate_fragility(Type, [Reg], Vst), Reg, Vst).
+ set_aliased_type(Type, Reg, Vst).
update_ne_types(LHS, RHS, Vst) ->
- T1 = get_durable_term_type(LHS, Vst),
- T2 = get_durable_term_type(RHS, Vst),
- Type = propagate_fragility(subtract(T1, T2), [LHS], Vst),
- set_aliased_type(Type, LHS, Vst).
+ update_type(fun subtract/2, get_durable_term_type(RHS, Vst), LHS, Vst).
update_eq_types(LHS, RHS, Vst0) ->
- T1 = get_durable_term_type(LHS, Vst0),
- T2 = get_durable_term_type(RHS, Vst0),
- Meet = meet(T1, T2),
- Vst = case T1 =/= Meet of
- true ->
- LType = propagate_fragility(Meet, [LHS], Vst0),
- set_aliased_type(LType, LHS, Vst0);
- false ->
- Vst0
- end,
- case T2 =/= Meet of
- true ->
- RType = propagate_fragility(Meet, [RHS], Vst0),
- set_aliased_type(RType, RHS, Vst);
- false ->
- Vst
- end.
+ Infer = infer_types(LHS, Vst0),
+ Vst1 = Infer(RHS, Vst0),
+
+ T1 = get_durable_term_type(LHS, Vst1),
+ T2 = get_durable_term_type(RHS, Vst1),
+
+ Vst = update_type(fun meet/2, T2, LHS, Vst1),
+ update_type(fun meet/2, T1, RHS, Vst).
%% Helper functions for the above.
assign_1(Src, Dst, Vst0) ->
Type = get_move_term_type(Src, Vst0),
- Vst = set_type_reg(Type, Dst, Vst0),
- case Src of
- {Kind,_} when Kind =:= x; Kind =:= y ->
- #vst{current=St0} = Vst,
- #st{aliases=Aliases0} = St0,
- Aliases = Aliases0#{Src=>Dst,Dst=>Src},
- St = St0#st{aliases=Aliases},
- Vst#vst{current=St};
- _ ->
- Vst
- end.
+ Def = get_def(Src, Vst0),
+
+ Vst = set_type_reg_expr(Type, Def, Dst, Vst0),
+
+ #vst{current=St0} = Vst,
+ #st{aliases=Aliases0} = St0,
+
+ Aliases = Aliases0#{Src=>Dst,Dst=>Src},
+
+ St = St0#st{aliases=Aliases},
+ Vst#vst{current=St}.
set_aliased_type(Type, Reg, #vst{current=#st{aliases=Aliases}}=Vst0) ->
Vst1 = set_type(Type, Reg, Vst0),
@@ -1445,7 +1580,9 @@ set_type(Type, {y,_}=Reg, Vst) ->
set_type(_, _, #vst{}=Vst) -> Vst.
set_type_reg(Type, Src, Dst, Vst) ->
- case get_term_type_1(Src, Vst) of
+ case get_raw_type(Src, Vst) of
+ uninitialized ->
+ error({uninitialized_reg, Src});
{fragile,_} ->
set_type_reg(make_fragile(Type), Dst, Vst);
_ ->
@@ -1460,9 +1597,6 @@ set_type_reg_expr(Type, Expr, {x,_}=Reg, Vst) ->
set_type_reg_expr(Type, Expr, Reg, Vst) ->
set_type_y(Type, Expr, Reg, Vst).
-set_type_y(Type, Reg, Vst) ->
- set_type_y(Type, none, Reg, Vst).
-
set_type_x(Type, Expr, {x,X}=Reg, #vst{current=#st{x=Xs0,defs=Defs0}=St0}=Vst)
when is_integer(X), 0 =< X ->
check_limit(Reg),
@@ -1493,7 +1627,7 @@ set_type_y(Type, Expr, {y,Y}=Reg, #vst{current=#st{y=Ys0,defs=Defs0}=St0}=Vst)
{value,_} ->
gb_trees:update(Y, Type, Ys0)
end,
- check_try_catch_tags(Type, Y, Ys0),
+ check_try_catch_tags(Type, Reg, Vst),
Defs = Defs0#{Reg=>Expr},
St = kill_aliases(Reg, St0),
Vst#vst{current=St#st{y=Ys,defs=Defs}};
@@ -1503,34 +1637,26 @@ set_type_y(Type, _Expr, Reg, #vst{}) ->
make_fragile({fragile,_}=Type) -> Type;
make_fragile(Type) -> {fragile,Type}.
-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}}.
+kill_catch_tag(Reg, #vst{current=#st{ct=[Fail|Fails]}=St}=Vst0) ->
+ Vst = Vst0#vst{current=St#st{ct=Fails,fls=undefined}},
+ {_, Fail} = get_tag_type(Reg, Vst), %Assertion.
+ kill_tag(Reg, Vst).
-check_try_catch_tags(Type, LastY, Ys) ->
+check_try_catch_tags(Type, {y,N}=Reg, Vst) ->
+ %% 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.
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
+ case collect_try_catch_tags(N - 1, Vst, []) of
+ [_|_]=Bad -> error({bad_try_catch_nesting, Reg, Bad});
+ [] -> ok
+ end;
+ false ->
+ ok
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);
is_reg_defined(V, #vst{}) -> error({not_a_register, V}).
@@ -1551,6 +1677,13 @@ assert_not_fragile(Src, Vst) ->
_ -> ok
end.
+assert_literal(nil) -> ok;
+assert_literal({atom,A}) when is_atom(A) -> ok;
+assert_literal({float,F}) when is_float(F) -> ok;
+assert_literal({integer,I}) when is_integer(I) -> ok;
+assert_literal({literal,_L}) -> ok;
+assert_literal(T) -> error({literal_required,T}).
+
assert_not_literal({x,_}) -> ok;
assert_not_literal({y,_}) -> ok;
assert_not_literal(Literal) -> error({literal_not_allowed,Literal}).
@@ -1573,10 +1706,10 @@ assert_not_literal(Literal) -> error({literal_not_allowed,Literal}).
%% used by the catch instructions; NOT safe to use in other
%% instructions.
%%
-%% exception Can only be used as a type returned by return_type/2
-%% (which gives the type of the value returned by a BIF).
-%% Thus 'exception' is never stored as type descriptor
-%% for a register.
+%% exception Can only be used as a type returned by
+%% call_return_type/2 (which gives the type of the value
+%% returned by a call). Thus 'exception' is never stored
+%% as type descriptor for a register.
%%
%% #ms{} A match context for bit syntax matching. We do allow
%% it to moved/to from stack, but otherwise it must only
@@ -1597,11 +1730,12 @@ assert_not_literal(Literal) -> error({literal_not_allowed,Literal}).
%%
%% list List: [] or [_|_]
%%
-%% {tuple,[Sz]} Tuple. An element has been accessed using
-%% element/2 or setelement/3 so that it is known that
-%% the type is a tuple of size at least Sz.
+%% {tuple,[Sz],Es} Tuple. An element has been accessed using
+%% element/2 or setelement/3 so that it is known that
+%% the type is a tuple of size at least Sz. Es is a map
+%% containing known types by tuple index.
%%
-%% {tuple,Sz} Tuple. A test_arity instruction has been seen
+%% {tuple,Sz,Es} Tuple. A test_arity instruction has been seen
%% so that it is known that the size is exactly Sz.
%%
%% {atom,[]} Atom.
@@ -1636,6 +1770,10 @@ assert_not_literal(Literal) -> error({literal_not_allowed,Literal}).
meet(Same, Same) ->
Same;
+meet({literal,_}=T1, T2) ->
+ meet_literal(T1, T2);
+meet(T1, {literal,_}=T2) ->
+ meet_literal(T2, T1);
meet(term, Other) ->
Other;
meet(Other, term) ->
@@ -1651,18 +1789,59 @@ meet(T1, T2) ->
{list,nil} -> nil;
{number,{integer,_}=T} -> T;
{number,{float,_}=T} -> T;
- {{tuple,Size1},{tuple,Size2}} ->
- case {Size1,Size2} of
- {[Sz1],[Sz2]} ->
- {tuple,[erlang:max(Sz1, Sz2)]};
- {Sz1,[Sz2]} when Sz2 =< Sz1 ->
- {tuple,Sz1};
- {_,_} ->
+ {{tuple,Size1,Es1},{tuple,Size2,Es2}} ->
+ Es = meet_elements(Es1, Es2),
+ case {Size1,Size2,Es} of
+ {_, _, none} ->
+ none;
+ {[Sz1],[Sz2],_} ->
+ Sz = erlang:max(Sz1, Sz2),
+ assert_tuple_elements(Sz, Es),
+ {tuple,[Sz],Es};
+ {Sz1,[Sz2],_} when Sz2 =< Sz1 ->
+ assert_tuple_elements(Sz1, Es),
+ {tuple,Sz1,Es};
+ {Sz,Sz,_} ->
+ assert_tuple_elements(Sz, Es),
+ {tuple,Sz,Es};
+ {_,_,_} ->
none
end;
{_,_} -> none
end.
+%% Meets types of literals.
+meet_literal({literal,_}=Lit, T) ->
+ meet_literal(T, get_literal_type(Lit));
+meet_literal(T1, T2) ->
+ %% We're done extracting the types, try merging them again.
+ meet(T1, T2).
+
+meet_elements(Es1, Es2) ->
+ Keys = maps:keys(Es1) ++ maps:keys(Es2),
+ meet_elements_1(Keys, Es1, Es2, #{}).
+
+meet_elements_1([Key | Keys], Es1, Es2, Acc) ->
+ case {Es1, Es2} of
+ {#{ Key := Type1 }, #{ Key := Type2 }} ->
+ case meet(Type1, Type2) of
+ none -> none;
+ Type -> meet_elements_1(Keys, Es1, Es2, Acc#{ Key => Type })
+ end;
+ {#{ Key := Type1 }, _} ->
+ meet_elements_1(Keys, Es1, Es2, Acc#{ Key => Type1 });
+ {_, #{ Key := Type2 }} ->
+ meet_elements_1(Keys, Es1, Es2, Acc#{ Key => Type2 })
+ end;
+meet_elements_1([], _Es1, _Es2, Acc) ->
+ Acc.
+
+%% No tuple elements may have an index above the known size.
+assert_tuple_elements(Limit, Es) ->
+ true = maps:fold(fun(Index, _T, true) ->
+ Index =< Limit
+ end, true, Es). %Assertion.
+
%% subtract(Type1, Type2) -> Type
%% Subtract Type2 from Type2. Example:
%% subtract(list, nil) -> cons
@@ -1681,12 +1860,12 @@ assert_type(WantedType, Term, Vst) ->
assert_type(Correct, Correct) -> ok;
assert_type(float, {float,_}) -> ok;
-assert_type(tuple, {tuple,_}) -> ok;
+assert_type(tuple, {tuple,_,_}) -> ok;
assert_type(tuple, {literal,Tuple}) when is_tuple(Tuple) -> ok;
-assert_type({tuple_element,I}, {tuple,[Sz]})
+assert_type({tuple_element,I}, {tuple,[Sz],_})
when 1 =< I, I =< Sz ->
ok;
-assert_type({tuple_element,I}, {tuple,Sz})
+assert_type({tuple_element,I}, {tuple,Sz,_})
when is_integer(Sz), 1 =< I, I =< Sz ->
ok;
assert_type({tuple_element,I}, {literal,Lit}) when I =< tuple_size(Lit) ->
@@ -1696,12 +1875,42 @@ assert_type(cons, {literal,[_|_]}) ->
assert_type(Needed, Actual) ->
error({bad_type,{needed,Needed},{actual,Actual}}).
+get_element_type(Key, Src, Vst) ->
+ get_element_type_1(Key, get_durable_term_type(Src, Vst)).
+
+get_element_type_1(Index, {tuple,Sz,Es}) ->
+ case Es of
+ #{ Index := Type } -> Type;
+ #{} when Index =< Sz -> term;
+ #{} -> none
+ end;
+get_element_type_1(_Index, _Type) ->
+ term.
+
+set_element_type(_Key, none, Es) ->
+ Es;
+set_element_type(Key, term, Es) ->
+ maps:remove(Key, Es);
+set_element_type(Key, Type, Es) ->
+ Es#{ Key => Type }.
+
get_tuple_size({integer,[]}) -> 0;
get_tuple_size({integer,Sz}) -> Sz;
get_tuple_size(_) -> 0.
validate_src(Ss, Vst) when is_list(Ss) ->
- foreach(fun(S) -> get_term_type(S, Vst) end, Ss).
+ [assert_term(S, Vst) || S <- Ss],
+ ok.
+
+%% get_term_type(Src, ValidatorState) -> Type
+%% Get the type of the source Src. The returned type Type will be
+%% a standard Erlang type (no catch/try tags or match contexts).
+
+get_term_type(Src, Vst) ->
+ case get_move_term_type(Src, Vst) of
+ #ms{} -> error({match_context,Src});
+ Type -> Type
+ end.
%% get_durable_term_type(Src, ValidatorState) -> Type
%% Get the type of the source Src. The returned type Type will be
@@ -1719,52 +1928,43 @@ get_durable_term_type(Src, Vst) ->
%% a standard Erlang type (no catch/try tags). Match contexts are OK.
get_move_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_raw_type(Src, Vst) of
+ initialized -> error({unassigned,Src});
+ uninitialized -> error({uninitialized_reg,Src});
+ {catchtag,_} -> error({catchtag,Src});
+ {trytag,_} -> error({trytag,Src});
tuple_in_progress -> error({tuple_in_progress,Src});
- Type -> Type
+ Type -> Type
end.
-%% get_term_type(Src, ValidatorState) -> Type
-%% Get the type of the source Src. The returned type Type will be
-%% a standard Erlang type (no catch/try tags or match contexts).
+%% get_tag_type(Src, ValidatorState) -> Type
+%% Return the tag type of a Y register, erroring out if it contains a term.
-get_term_type(Src, Vst) ->
- case get_move_term_type(Src, Vst) of
- #ms{} -> error({match_context,Src});
- Type -> Type
- end.
+get_tag_type({y,_}=Src, Vst) ->
+ case get_raw_type(Src, Vst) of
+ {catchtag, _}=Tag -> Tag;
+ {trytag, _}=Tag -> Tag;
+ uninitialized=Tag -> Tag;
+ initialized=Tag -> Tag;
+ Other -> error({invalid_tag,Src,Other})
+ end;
+get_tag_type(Src, _) ->
+ error({invalid_tag_register,Src}).
-%% get_special_y_type(Src, ValidatorState) -> Type
-%% Return the type for the Y register without doing any validity checks.
-
-get_special_y_type({y,_}=Reg, Vst) -> get_term_type_1(Reg, Vst);
-get_special_y_type(Src, _) -> error({source_not_y_reg,Src}).
-
-get_term_type_1(nil=T, _) -> T;
-get_term_type_1({atom,A}=T, _) when is_atom(A) -> T;
-get_term_type_1({float,F}=T, _) when is_float(F) -> T;
-get_term_type_1({integer,I}=T, _) when is_integer(I) -> T;
-get_term_type_1({literal,[_|_]}, _) -> cons;
-get_term_type_1({literal,Bitstring}, _) when is_bitstring(Bitstring) -> binary;
-get_term_type_1({literal,Map}, _) when is_map(Map) -> map;
-get_term_type_1({literal,Tuple}, _) when is_tuple(Tuple) ->
- {tuple,tuple_size(Tuple)};
-get_term_type_1({literal,_}=T, _) -> T;
-get_term_type_1({x,X}=Reg, #vst{current=#st{x=Xs}}) when is_integer(X) ->
+%% get_raw_type(Src, ValidatorState) -> Type
+%% Return the type of a register without doing any validity checks.
+get_raw_type({x,X}, #vst{current=#st{x=Xs}}) when is_integer(X) ->
case gb_trees:lookup(X, Xs) of
- {value,Type} -> Type;
- none -> error({uninitialized_reg,Reg})
+ {value,Type} -> Type;
+ none -> uninitialized
end;
-get_term_type_1({y,Y}=Reg, #vst{current=#st{y=Ys}}) when is_integer(Y) ->
+get_raw_type({y,Y}, #vst{current=#st{y=Ys}}) when is_integer(Y) ->
case gb_trees:lookup(Y, Ys) of
- none -> error({uninitialized_reg,Reg});
- {value,uninitialized} -> error({uninitialized_reg,Reg});
- {value,Type} -> Type
+ {value,Type} -> Type;
+ none -> uninitialized
end;
-get_term_type_1(Src, _) -> error({bad_source,Src}).
+get_raw_type(Src, #vst{}) ->
+ get_literal_type(Src).
get_def(Src, #vst{current=#st{defs=Defs}}) ->
case Defs of
@@ -1772,28 +1972,29 @@ get_def(Src, #vst{current=#st{defs=Defs}}) ->
#{} -> none
end.
-%% get_literal(Src) -> literal_value().
-get_literal(nil) -> [];
-get_literal({atom,A}) when is_atom(A) -> A;
-get_literal({float,F}) when is_float(F) -> F;
-get_literal({integer,I}) when is_integer(I) -> I;
-get_literal({literal,L}) -> L;
-get_literal(T) -> error({not_literal,T}).
-
-branch_arities([Sz,{f,L}|T], Tuple, {tuple,[_]}=Type0, Vst0) when is_integer(Sz) ->
- Vst1 = set_aliased_type({tuple,Sz}, Tuple, Vst0),
- Vst = branch_state(L, Vst1),
- branch_arities(T, Tuple, Type0, Vst);
-branch_arities([Sz,{f,L}|T], Tuple, {tuple,Sz}=Type, Vst0) when is_integer(Sz) ->
- %% The type is already correct. (This test is redundant.)
- Vst = branch_state(L, Vst0),
- branch_arities(T, Tuple, Type, Vst);
-branch_arities([Sz0,{f,_}|T], Tuple, {tuple,Sz}=Type, Vst)
- when is_integer(Sz), Sz0 =/= Sz ->
- %% We already have an established different exact size for the tuple.
- %% This label can't possibly be reached.
- branch_arities(T, Tuple, Type, Vst);
-branch_arities([], _, _, #vst{}=Vst) -> Vst.
+get_literal_type(nil=T) -> T;
+get_literal_type({atom,A}=T) when is_atom(A) -> T;
+get_literal_type({float,F}=T) when is_float(F) -> T;
+get_literal_type({integer,I}=T) when is_integer(I) -> T;
+get_literal_type({literal,[_|_]}) -> cons;
+get_literal_type({literal,Bitstring}) when is_bitstring(Bitstring) -> binary;
+get_literal_type({literal,Map}) when is_map(Map) -> map;
+get_literal_type({literal,Tuple}) when is_tuple(Tuple) -> value_to_type(Tuple);
+get_literal_type({literal,_}) -> term;
+get_literal_type(T) -> error({not_literal,T}).
+
+value_to_type([]) -> nil;
+value_to_type(A) when is_atom(A) -> {atom, A};
+value_to_type(F) when is_float(F) -> {float, F};
+value_to_type(I) when is_integer(I) -> {integer, I};
+value_to_type(T) when is_tuple(T) ->
+ {Es,_} = foldl(fun(Val, {Es0, Index}) ->
+ Type = value_to_type(Val),
+ Es = set_element_type(Index, Type, Es0),
+ {Es, Index + 1}
+ end, {#{}, 1}, tuple_to_list(T)),
+ {tuple, tuple_size(T), Es};
+value_to_type(L) -> {literal, L}.
branch_state(0, #vst{}=Vst) ->
%% If the instruction fails, the stack may be scanned
@@ -1902,9 +2103,14 @@ join({catchtag,T0},{catchtag,T1}) ->
{catchtag,ordsets:from_list(T0++T1)};
join({trytag,T0},{trytag,T1}) ->
{trytag,ordsets:from_list(T0++T1)};
-join({tuple,A}, {tuple,B}) ->
- {tuple,[min(tuple_sz(A), tuple_sz(B))]};
-join({Type,A}, {Type,B})
+join({tuple,Size,EsA}, {tuple,Size,EsB}) ->
+ Es = join_tuple_elements(tuple_sz(Size), EsA, EsB),
+ {tuple, Size, Es};
+join({tuple,A,EsA}, {tuple,B,EsB}) ->
+ Size = min(tuple_sz(A), tuple_sz(B)),
+ Es = join_tuple_elements(Size, EsA, EsB),
+ {tuple, [Size], Es};
+join({Type,A}, {Type,B})
when Type =:= atom; Type =:= integer; Type =:= float ->
if A =:= B -> {Type,A};
true -> {Type,[]}
@@ -1916,9 +2122,9 @@ join(number, {Type,_})
when Type =:= integer; Type =:= float ->
number;
join(bool, {atom,A}) ->
- merge_bool(A);
+ join_bool(A);
join({atom,A}, bool) ->
- merge_bool(A);
+ join_bool(A);
join({atom,_}, {atom,_}) ->
{atom,[]};
join(#ms{id=Id1,valid=B1,slots=Slots1},
@@ -1933,19 +2139,34 @@ join(T1, T2) when T1 =/= T2 ->
%% a 'term'.
join_list(T1, T2).
-%% Merges types of literals. Note that the left argument must either be a
+join_tuple_elements(Limit, EsA, EsB) ->
+ Es0 = join_elements(EsA, EsB),
+ maps:filter(fun(Index, _Type) -> Index =< Limit end, Es0).
+
+join_elements(Es1, Es2) ->
+ Keys = if
+ map_size(Es1) =< map_size(Es2) -> maps:keys(Es1);
+ map_size(Es1) > map_size(Es2) -> maps:keys(Es2)
+ end,
+ join_elements_1(Keys, Es1, Es2, #{}).
+
+join_elements_1([Key | Keys], Es1, Es2, Acc0) ->
+ Type = case {Es1, Es2} of
+ {#{ Key := Same }, #{ Key := Same }} -> Same;
+ {#{ Key := Type1 }, #{ Key := Type2 }} -> join(Type1, Type2);
+ {#{}, #{}} -> term
+ end,
+ Acc = set_element_type(Key, Type, Acc0),
+ join_elements_1(Keys, Es1, Es2, Acc);
+join_elements_1([], _Es1, _Es2, Acc) ->
+ Acc.
+
+%% Joins types of literals; note that the left argument must either be a
%% literal or exactly equal to the second argument.
join_literal(Same, Same) ->
Same;
-join_literal({literal,[_|_]}, T) ->
- join_literal(T, cons);
-join_literal({literal,#{}}, T) ->
- join_literal(T, map);
-join_literal({literal,Tuple}, T) when is_tuple(Tuple) ->
- join_literal(T, {tuple, tuple_size(Tuple)});
-join_literal({literal,_}, T) ->
- %% Bitstring, fun, or similar.
- join_literal(T, term);
+join_literal({literal,_}=Lit, T) ->
+ join_literal(T, get_literal_type(Lit));
join_literal(T1, T2) ->
%% We're done extracting the types, try merging them again.
join(T1, T2).
@@ -1959,14 +2180,14 @@ join_list(_, _) ->
%% Not a list, so it must be a term.
term.
+join_bool([]) -> {atom,[]};
+join_bool(true) -> bool;
+join_bool(false) -> bool;
+join_bool(_) -> {atom,[]}.
+
tuple_sz([Sz]) -> Sz;
tuple_sz(Sz) -> Sz.
-merge_bool([]) -> {atom,[]};
-merge_bool(true) -> bool;
-merge_bool(false) -> bool;
-merge_bool(_) -> {atom,[]}.
-
merge_aliases(Al0, Al1) when map_size(Al0) =< map_size(Al1) ->
maps:filter(fun(K, V) ->
case Al1 of
@@ -1977,44 +2198,78 @@ merge_aliases(Al0, Al1) when map_size(Al0) =< map_size(Al1) ->
merge_aliases(Al0, Al1) ->
merge_aliases(Al1, Al0).
-verify_y_init(#vst{current=#st{y=Ys}}) ->
- verify_y_init_1(gb_trees:to_list(Ys)).
-
-verify_y_init_1([]) -> ok;
-verify_y_init_1([{Y,uninitialized}|_]) ->
- error({uninitialized_reg,{y,Y}});
-verify_y_init_1([{Y,{fragile,_}}|_]) ->
- %% Unsafe. This term may be outside any heap belonging
- %% to the process and would be corrupted by a GC.
- error({fragile_message_reference,{y,Y}});
-verify_y_init_1([{_,_}|Ys]) ->
- verify_y_init_1(Ys).
-
-verify_live(0, #vst{}) -> ok;
-verify_live(N, #vst{current=#st{x=Xs}}) ->
- verify_live_1(N, Xs).
-
-verify_live_1(0, _) -> ok;
-verify_live_1(N, Xs) when is_integer(N) ->
- X = N-1,
- case gb_trees:is_defined(X, Xs) of
- false -> error({{x,X},not_live});
- true -> verify_live_1(X, Xs)
+verify_y_init(#vst{current=#st{numy=NumY,y=Ys}}=Vst)
+ when is_integer(NumY), NumY > 0 ->
+ {HighestY, _} = gb_trees:largest(Ys),
+ true = NumY > HighestY, %Assertion.
+ verify_y_init_1(NumY - 1, Vst),
+ ok;
+verify_y_init(#vst{current=#st{numy=undecided,y=Ys}}=Vst) ->
+ case gb_trees:is_empty(Ys) of
+ true ->
+ ok;
+ false ->
+ {HighestY, _} = gb_trees:largest(Ys),
+ verify_y_init_1(HighestY, Vst)
end;
-verify_live_1(N, _) -> error({bad_number_of_live_regs,N}).
+verify_y_init(#vst{}) ->
+ ok.
+
+verify_y_init_1(-1, _Vst) ->
+ ok;
+verify_y_init_1(Y, Vst) ->
+ Reg = {y, Y},
+ case get_raw_type(Reg, Vst) of
+ uninitialized ->
+ error({uninitialized_reg,Reg});
+ {fragile, _} ->
+ %% Unsafe. This term may be outside any heap belonging to the
+ %% process and would be corrupted by a GC.
+ error({fragile_message_reference,Reg});
+ _ ->
+ verify_y_init_1(Y - 1, Vst)
+ end.
+
+verify_live(0, _Vst) ->
+ ok;
+verify_live(Live, Vst) when is_integer(Live), 0 < Live, Live =< 1023 ->
+ verify_live_1(Live - 1, Vst);
+verify_live(Live, _Vst) ->
+ error({bad_number_of_live_regs,Live}).
-verify_no_ct(#vst{current=#st{numy=none}}) -> ok;
+verify_live_1(-1, _) ->
+ ok;
+verify_live_1(X, Vst) when is_integer(X) ->
+ Reg = {x, X},
+ case get_raw_type(Reg, Vst) of
+ uninitialized -> error({Reg, not_live});
+ _ -> verify_live_1(X - 1, Vst)
+ end.
+
+verify_no_ct(#vst{current=#st{numy=none}}) ->
+ ok;
verify_no_ct(#vst{current=#st{numy=undecided}}) ->
error(unknown_size_of_stackframe);
-verify_no_ct(#vst{current=#st{y=Ys}}) ->
- case [Y || Y <- gb_trees:to_list(Ys), verify_no_ct_1(Y)] of
- [] -> ok;
- CT -> error({unfinished_catch_try,CT})
+verify_no_ct(#vst{current=St}=Vst) ->
+ case collect_try_catch_tags(St#st.numy - 1, Vst, []) of
+ [_|_]=Bad -> error({unfinished_catch_try,Bad});
+ [] -> ok
end.
-verify_no_ct_1({_, {catchtag, _}}) -> true;
-verify_no_ct_1({_, {trytag, _}}) -> true;
-verify_no_ct_1({_, _}) -> false.
+%% Collects all try/catch tags, walking down from the Nth stack position.
+collect_try_catch_tags(-1, _Vst, Acc) ->
+ Acc;
+collect_try_catch_tags(Y, Vst, Acc0) ->
+ Tag = get_raw_type({y, Y}, Vst),
+ Acc = case is_try_catch_tag(Tag) of
+ true -> [{{y, Y}, Tag} | Acc0];
+ false -> Acc0
+ end,
+ collect_try_catch_tags(Y - 1, Vst, Acc).
+
+is_try_catch_tag({catchtag,_}) -> true;
+is_try_catch_tag({trytag,_}) -> true;
+is_try_catch_tag(_) -> false.
eat_heap(N, #vst{current=#st{h=Heap0}=St}=Vst) ->
case Heap0-N of
@@ -2043,7 +2298,7 @@ remove_fragility(#vst{current=#st{x=Xs0,y=Ys0}=St0}=Vst) ->
propagate_fragility(Type, Ss, Vst) ->
F = fun(S) ->
- case get_term_type_1(S, Vst) of
+ case get_raw_type(S, Vst) of
{fragile,_} -> true;
_ -> false
end
@@ -2053,72 +2308,114 @@ propagate_fragility(Type, Ss, Vst) ->
false -> Type
end.
-bif_type('-', Src, Vst) ->
- arith_type(Src, Vst);
-bif_type('+', Src, Vst) ->
- arith_type(Src, Vst);
-bif_type('*', Src, Vst) ->
- arith_type(Src, Vst);
-bif_type(abs, [Num], Vst) ->
+%%%
+%%% Return/argument types of BIFs
+%%%
+
+bif_return_type('-', Src, Vst) ->
+ arith_return_type(Src, Vst);
+bif_return_type('+', Src, Vst) ->
+ arith_return_type(Src, Vst);
+bif_return_type('*', Src, Vst) ->
+ arith_return_type(Src, Vst);
+bif_return_type(abs, [Num], Vst) ->
case get_durable_term_type(Num, Vst) of
- {float,_}=T -> T;
- {integer,_}=T -> T;
- _ -> number
+ {float,_}=T -> T;
+ {integer,_}=T -> T;
+ _ -> number
end;
-bif_type(float, _, _) -> {float,[]};
-bif_type('/', _, _) -> {float,[]};
+bif_return_type(float, _, _) -> {float,[]};
+bif_return_type('/', _, _) -> {float,[]};
%% Binary operations
-bif_type('byte_size', _, _) -> {integer,[]};
-bif_type('bit_size', _, _) -> {integer,[]};
+bif_return_type('byte_size', _, _) -> {integer,[]};
+bif_return_type('bit_size', _, _) -> {integer,[]};
%% Integer operations.
-bif_type(ceil, [_], _) -> {integer,[]};
-bif_type('div', [_,_], _) -> {integer,[]};
-bif_type(floor, [_], _) -> {integer,[]};
-bif_type('rem', [_,_], _) -> {integer,[]};
-bif_type(length, [_], _) -> {integer,[]};
-bif_type(size, [_], _) -> {integer,[]};
-bif_type(trunc, [_], _) -> {integer,[]};
-bif_type(round, [_], _) -> {integer,[]};
-bif_type('band', [_,_], _) -> {integer,[]};
-bif_type('bor', [_,_], _) -> {integer,[]};
-bif_type('bxor', [_,_], _) -> {integer,[]};
-bif_type('bnot', [_], _) -> {integer,[]};
-bif_type('bsl', [_,_], _) -> {integer,[]};
-bif_type('bsr', [_,_], _) -> {integer,[]};
+bif_return_type(ceil, [_], _) -> {integer,[]};
+bif_return_type('div', [_,_], _) -> {integer,[]};
+bif_return_type(floor, [_], _) -> {integer,[]};
+bif_return_type('rem', [_,_], _) -> {integer,[]};
+bif_return_type(length, [_], _) -> {integer,[]};
+bif_return_type(size, [_], _) -> {integer,[]};
+bif_return_type(trunc, [_], _) -> {integer,[]};
+bif_return_type(round, [_], _) -> {integer,[]};
+bif_return_type('band', [_,_], _) -> {integer,[]};
+bif_return_type('bor', [_,_], _) -> {integer,[]};
+bif_return_type('bxor', [_,_], _) -> {integer,[]};
+bif_return_type('bnot', [_], _) -> {integer,[]};
+bif_return_type('bsl', [_,_], _) -> {integer,[]};
+bif_return_type('bsr', [_,_], _) -> {integer,[]};
%% Booleans.
-bif_type('==', [_,_], _) -> bool;
-bif_type('/=', [_,_], _) -> bool;
-bif_type('=<', [_,_], _) -> bool;
-bif_type('<', [_,_], _) -> bool;
-bif_type('>=', [_,_], _) -> bool;
-bif_type('>', [_,_], _) -> bool;
-bif_type('=:=', [_,_], _) -> bool;
-bif_type('=/=', [_,_], _) -> bool;
-bif_type('not', [_], _) -> bool;
-bif_type('and', [_,_], _) -> bool;
-bif_type('or', [_,_], _) -> bool;
-bif_type('xor', [_,_], _) -> bool;
-bif_type(is_atom, [_], _) -> bool;
-bif_type(is_boolean, [_], _) -> bool;
-bif_type(is_binary, [_], _) -> bool;
-bif_type(is_float, [_], _) -> bool;
-bif_type(is_function, [_], _) -> bool;
-bif_type(is_integer, [_], _) -> bool;
-bif_type(is_list, [_], _) -> bool;
-bif_type(is_map, [_], _) -> bool;
-bif_type(is_number, [_], _) -> bool;
-bif_type(is_pid, [_], _) -> bool;
-bif_type(is_port, [_], _) -> bool;
-bif_type(is_reference, [_], _) -> bool;
-bif_type(is_tuple, [_], _) -> bool;
+bif_return_type('==', [_,_], _) -> bool;
+bif_return_type('/=', [_,_], _) -> bool;
+bif_return_type('=<', [_,_], _) -> bool;
+bif_return_type('<', [_,_], _) -> bool;
+bif_return_type('>=', [_,_], _) -> bool;
+bif_return_type('>', [_,_], _) -> bool;
+bif_return_type('=:=', [_,_], _) -> bool;
+bif_return_type('=/=', [_,_], _) -> bool;
+bif_return_type('not', [_], _) -> bool;
+bif_return_type('and', [_,_], _) -> bool;
+bif_return_type('or', [_,_], _) -> bool;
+bif_return_type('xor', [_,_], _) -> bool;
+bif_return_type(is_atom, [_], _) -> bool;
+bif_return_type(is_boolean, [_], _) -> bool;
+bif_return_type(is_binary, [_], _) -> bool;
+bif_return_type(is_float, [_], _) -> bool;
+bif_return_type(is_function, [_], _) -> bool;
+bif_return_type(is_integer, [_], _) -> bool;
+bif_return_type(is_list, [_], _) -> bool;
+bif_return_type(is_map, [_], _) -> bool;
+bif_return_type(is_number, [_], _) -> bool;
+bif_return_type(is_pid, [_], _) -> bool;
+bif_return_type(is_port, [_], _) -> bool;
+bif_return_type(is_reference, [_], _) -> bool;
+bif_return_type(is_tuple, [_], _) -> bool;
%% Misc.
-bif_type(tuple_size, [_], _) -> {integer,[]};
-bif_type(node, [], _) -> {atom,[]};
-bif_type(node, [_], _) -> {atom,[]};
-bif_type(hd, [_], _) -> term;
-bif_type(tl, [_], _) -> term;
-bif_type(get, [_], _) -> term;
-bif_type(Bif, _, _) when is_atom(Bif) -> term.
+bif_return_type(tuple_size, [_], _) -> {integer,[]};
+bif_return_type(node, [], _) -> {atom,[]};
+bif_return_type(node, [_], _) -> {atom,[]};
+bif_return_type(hd, [_], _) -> term;
+bif_return_type(tl, [_], _) -> term;
+bif_return_type(get, [_], _) -> term;
+bif_return_type(Bif, _, _) when is_atom(Bif) -> term.
+
+%% Generic
+bif_arg_types(tuple_size, [_]) -> [{tuple,[0],#{}}];
+bif_arg_types(map_size, [_]) -> [map];
+bif_arg_types(is_map_key, [_,_]) -> [term, map];
+bif_arg_types(map_get, [_,_]) -> [term, map];
+bif_arg_types(length, [_]) -> [list];
+bif_arg_types(hd, [_]) -> [cons];
+bif_arg_types(tl, [_]) -> [cons];
+%% Boolean
+bif_arg_types('not', [_]) -> [bool];
+bif_arg_types('and', [_,_]) -> [bool, bool];
+bif_arg_types('or', [_,_]) -> [bool, bool];
+bif_arg_types('xor', [_,_]) -> [bool, bool];
+%% Binary
+bif_arg_types('byte_size', [_]) -> [binary];
+bif_arg_types('bit_size', [_]) -> [binary];
+%% Numerical
+bif_arg_types('-', [_]) -> [number];
+bif_arg_types('+', [_]) -> [number];
+bif_arg_types('*', [_,_]) -> [number, number];
+bif_arg_types('/', [_,_]) -> [number, number];
+bif_arg_types(ceil, [_]) -> [number];
+bif_arg_types(floor, [_]) -> [number];
+bif_arg_types(trunc, [_]) -> [number];
+bif_arg_types(round, [_]) -> [number];
+%% Integer-specific
+bif_arg_types('div', [_,_]) -> [{integer,[]}, {integer,[]}];
+bif_arg_types('rem', [_,_]) -> [{integer,[]}, {integer,[]}];
+bif_arg_types('band', [_,_]) -> [{integer,[]}, {integer,[]}];
+bif_arg_types('bor', [_,_]) -> [{integer,[]}, {integer,[]}];
+bif_arg_types('bxor', [_,_]) -> [{integer,[]}, {integer,[]}];
+bif_arg_types('bnot', [_]) -> [{integer,[]}];
+bif_arg_types('bsl', [_,_]) -> [{integer,[]}, {integer,[]}];
+bif_arg_types('bsr', [_,_]) -> [{integer,[]}, {integer,[]}];
+%% Unsafe type tests that may fail if an argument doesn't have the right type.
+bif_arg_types(is_function, [_,_]) -> [term, {integer,[]}];
+bif_arg_types(_, Args) -> [term || _Arg <- Args].
is_bif_safe('/=', 2) -> true;
is_bif_safe('<', 2) -> true;
@@ -2147,14 +2444,14 @@ is_bif_safe(self, 0) -> true;
is_bif_safe(node, 0) -> true;
is_bif_safe(_, _) -> false.
-arith_type([A], Vst) ->
+arith_return_type([A], Vst) ->
%% Unary '+' or '-'.
case get_durable_term_type(A, Vst) of
{integer,_} -> {integer,[]};
{float,_} -> {float,[]};
_ -> number
end;
-arith_type([A,B], Vst) ->
+arith_return_type([A,B], Vst) ->
TypeA = get_durable_term_type(A, Vst),
TypeB = get_durable_term_type(B, Vst),
case {TypeA, TypeB} of
@@ -2163,80 +2460,145 @@ arith_type([A,B], Vst) ->
{_,{float,_}} -> {float,[]};
{_,_} -> number
end;
-arith_type(_, _) -> number.
+arith_return_type(_, _) -> number.
+
+%%%
+%%% Return/argument types of calls
+%%%
-return_type({extfunc,M,F,A}, Vst) -> return_type_1(M, F, A, Vst);
-return_type(_, _) -> term.
+call_return_type({extfunc,M,F,A}, Vst) -> call_return_type_1(M, F, A, Vst);
+call_return_type(_, _) -> term.
-return_type_1(erlang, setelement, 3, Vst) ->
- Tuple = {x,1},
+call_return_type_1(erlang, setelement, 3, Vst) ->
+ IndexType = get_term_type({x,0}, Vst),
TupleType =
- case get_term_type(Tuple, Vst) of
- {tuple,_}=TT ->
- TT;
- {literal,Lit} when is_tuple(Lit) ->
- {tuple,tuple_size(Lit)};
- _ ->
- {tuple,[0]}
- end,
- case get_term_type({x,0}, Vst) of
- {integer,[]} ->
- TupleType;
- {integer,I} ->
- case meet({tuple,[I]}, TupleType) of
- none -> TupleType;
- T -> T
+ case get_term_type({x,1}, Vst) of
+ {literal,Tuple}=Lit when is_tuple(Tuple) -> get_literal_type(Lit);
+ {tuple,_,_}=TT -> TT;
+ _ -> {tuple,[0],#{}}
+ end,
+ case IndexType of
+ {integer,I} when is_integer(I) ->
+ case meet({tuple,[I],#{}}, TupleType) of
+ {tuple, Sz, Es0} ->
+ ValueType = get_term_type({x,2}, Vst),
+ Es = set_element_type(I, ValueType, Es0),
+ {tuple, Sz, Es};
+ none ->
+ TupleType
end;
_ ->
- TupleType
+ %% The index could point anywhere, so we must discard all element
+ %% information.
+ setelement(3, TupleType, #{})
end;
-return_type_1(erlang, '++', 2, Vst) ->
+call_return_type_1(erlang, '++', 2, Vst) ->
case get_term_type({x,0}, Vst) =:= cons orelse
get_term_type({x,1}, Vst) =:= cons of
true -> cons;
false -> list
end;
-return_type_1(erlang, '--', 2, _Vst) ->
+call_return_type_1(erlang, '--', 2, _Vst) ->
list;
-return_type_1(erlang, F, A, _) ->
- return_type_erl(F, A);
-return_type_1(math, F, A, _) ->
- return_type_math(F, A);
-return_type_1(M, F, A, _) when is_atom(M), is_atom(F), is_integer(A), A >= 0 ->
+call_return_type_1(erlang, F, A, _) ->
+ erlang_mod_return_type(F, A);
+call_return_type_1(lists, F, A, Vst) ->
+ lists_mod_return_type(F, A, Vst);
+call_return_type_1(math, F, A, _) ->
+ math_mod_return_type(F, A);
+call_return_type_1(M, F, A, _) when is_atom(M), is_atom(F), is_integer(A), A >= 0 ->
term.
-return_type_erl(exit, 1) -> exception;
-return_type_erl(throw, 1) -> exception;
-return_type_erl(error, 1) -> exception;
-return_type_erl(error, 2) -> exception;
-return_type_erl(F, A) when is_atom(F), is_integer(A), A >= 0 -> term.
-
-return_type_math(cos, 1) -> {float,[]};
-return_type_math(cosh, 1) -> {float,[]};
-return_type_math(sin, 1) -> {float,[]};
-return_type_math(sinh, 1) -> {float,[]};
-return_type_math(tan, 1) -> {float,[]};
-return_type_math(tanh, 1) -> {float,[]};
-return_type_math(acos, 1) -> {float,[]};
-return_type_math(acosh, 1) -> {float,[]};
-return_type_math(asin, 1) -> {float,[]};
-return_type_math(asinh, 1) -> {float,[]};
-return_type_math(atan, 1) -> {float,[]};
-return_type_math(atanh, 1) -> {float,[]};
-return_type_math(erf, 1) -> {float,[]};
-return_type_math(erfc, 1) -> {float,[]};
-return_type_math(exp, 1) -> {float,[]};
-return_type_math(log, 1) -> {float,[]};
-return_type_math(log2, 1) -> {float,[]};
-return_type_math(log10, 1) -> {float,[]};
-return_type_math(sqrt, 1) -> {float,[]};
-return_type_math(atan2, 2) -> {float,[]};
-return_type_math(pow, 2) -> {float,[]};
-return_type_math(ceil, 1) -> {float,[]};
-return_type_math(floor, 1) -> {float,[]};
-return_type_math(fmod, 2) -> {float,[]};
-return_type_math(pi, 0) -> {float,[]};
-return_type_math(F, A) when is_atom(F), is_integer(A), A >= 0 -> term.
+erlang_mod_return_type(exit, 1) -> exception;
+erlang_mod_return_type(throw, 1) -> exception;
+erlang_mod_return_type(error, 1) -> exception;
+erlang_mod_return_type(error, 2) -> exception;
+erlang_mod_return_type(F, A) when is_atom(F), is_integer(A), A >= 0 -> term.
+
+math_mod_return_type(cos, 1) -> {float,[]};
+math_mod_return_type(cosh, 1) -> {float,[]};
+math_mod_return_type(sin, 1) -> {float,[]};
+math_mod_return_type(sinh, 1) -> {float,[]};
+math_mod_return_type(tan, 1) -> {float,[]};
+math_mod_return_type(tanh, 1) -> {float,[]};
+math_mod_return_type(acos, 1) -> {float,[]};
+math_mod_return_type(acosh, 1) -> {float,[]};
+math_mod_return_type(asin, 1) -> {float,[]};
+math_mod_return_type(asinh, 1) -> {float,[]};
+math_mod_return_type(atan, 1) -> {float,[]};
+math_mod_return_type(atanh, 1) -> {float,[]};
+math_mod_return_type(erf, 1) -> {float,[]};
+math_mod_return_type(erfc, 1) -> {float,[]};
+math_mod_return_type(exp, 1) -> {float,[]};
+math_mod_return_type(log, 1) -> {float,[]};
+math_mod_return_type(log2, 1) -> {float,[]};
+math_mod_return_type(log10, 1) -> {float,[]};
+math_mod_return_type(sqrt, 1) -> {float,[]};
+math_mod_return_type(atan2, 2) -> {float,[]};
+math_mod_return_type(pow, 2) -> {float,[]};
+math_mod_return_type(ceil, 1) -> {float,[]};
+math_mod_return_type(floor, 1) -> {float,[]};
+math_mod_return_type(fmod, 2) -> {float,[]};
+math_mod_return_type(pi, 0) -> {float,[]};
+math_mod_return_type(F, A) when is_atom(F), is_integer(A), A >= 0 -> term.
+
+lists_mod_return_type(dropwhile, 2, _Vst) ->
+ list;
+lists_mod_return_type(duplicate, 2, _Vst) ->
+ list;
+lists_mod_return_type(filter, 2, _Vst) ->
+ list;
+lists_mod_return_type(flatten, 2, _Vst) ->
+ list;
+lists_mod_return_type(map, 2, Vst) ->
+ same_length_type({x,1}, Vst);
+lists_mod_return_type(MF, 3, Vst) when MF =:= mapfoldl; MF =:= mapfoldr ->
+ ListType = same_length_type({x,2}, Vst),
+ {tuple,2,#{1=>ListType}};
+lists_mod_return_type(partition, 2, _Vst) ->
+ two_tuple(list, list);
+lists_mod_return_type(reverse, 1, Vst) ->
+ same_length_type({x,0}, Vst);
+lists_mod_return_type(seq, 2, _Vst) ->
+ list;
+lists_mod_return_type(seq, 3, _Vst) ->
+ list;
+lists_mod_return_type(sort, 1, Vst) ->
+ same_length_type({x,0}, Vst);
+lists_mod_return_type(sort, 2, Vst) ->
+ same_length_type({x,1}, Vst);
+lists_mod_return_type(splitwith, 2, _Vst) ->
+ two_tuple(list, list);
+lists_mod_return_type(takewhile, 2, _Vst) ->
+ list;
+lists_mod_return_type(unzip, 1, Vst) ->
+ ListType = same_length_type({x,0}, Vst),
+ two_tuple(ListType, ListType);
+lists_mod_return_type(usort, 1, Vst) ->
+ same_length_type({x,0}, Vst);
+lists_mod_return_type(usort, 2, Vst) ->
+ same_length_type({x,1}, Vst);
+lists_mod_return_type(zip, 2, _Vst) ->
+ list;
+lists_mod_return_type(zip3, 3, _Vst) ->
+ list;
+lists_mod_return_type(zipwith, 3, _Vst) ->
+ list;
+lists_mod_return_type(zipwith3, 4, _Vst) ->
+ list;
+lists_mod_return_type(_, _, _) ->
+ term.
+
+two_tuple(Type1, Type2) ->
+ {tuple,2,#{1=>Type1,2=>Type2}}.
+
+same_length_type(Reg, Vst) ->
+ case get_term_type(Reg, Vst) of
+ {literal,[_|_]} -> cons;
+ cons -> cons;
+ nil -> nil;
+ _ -> list
+ end.
check_limit({x,X}) when is_integer(X), X < 1023 ->
%% Note: x(1023) is reserved for use by the BEAM loader.
@@ -2251,6 +2613,6 @@ check_limit(_) ->
min(A, B) when is_integer(A), is_integer(B), A < B -> A;
min(A, B) when is_integer(A), is_integer(B) -> B.
-gb_trees_from_list(L) -> gb_trees:from_orddict(lists:sort(L)).
+gb_trees_from_list(L) -> gb_trees:from_orddict(sort(L)).
error(Error) -> throw(Error).
diff --git a/lib/compiler/src/compile.erl b/lib/compiler/src/compile.erl
index 53d3cec2d7..11dea9524b 100644
--- a/lib/compiler/src/compile.erl
+++ b/lib/compiler/src/compile.erl
@@ -814,8 +814,6 @@ kernel_passes() ->
%% Optimizations that must be done after all other optimizations.
[{pass,sys_core_bsm},
{iff,dcbsm,{listing,"core_bsm"}},
- {pass,sys_core_dsetel},
- {iff,dsetel,{listing,"dsetel"}},
{iff,clint,?pass(core_lint_module)},
{iff,core,?pass(save_core_code)},
@@ -827,20 +825,21 @@ kernel_passes() ->
{pass,beam_kernel_to_ssa},
{iff,dssa,{listing,"ssa"}},
{iff,ssalint,{pass,beam_ssa_lint}},
- {unless,no_share_opt,{pass,beam_ssa_share}},
- {iff,dssashare,{listing,"ssashare"}},
- {iff,ssalint,{pass,beam_ssa_lint}},
- {unless,no_bsm_opt,{pass,beam_ssa_bsm}},
- {iff,dssabsm,{listing,"ssabsm"}},
- {iff,ssalint,{pass,beam_ssa_lint}},
- {unless,no_fun_opt,{pass,beam_ssa_funs}},
- {iff,dssafuns,{listing,"ssafuns"}},
- {iff,ssalint,{pass,beam_ssa_lint}},
- {unless,no_ssa_opt,{pass,beam_ssa_opt}},
- {iff,dssaopt,{listing,"ssaopt"}},
- {iff,ssalint,{pass,beam_ssa_lint}},
- {unless,no_recv_opt,{pass,beam_ssa_recv}},
- {iff,drecv,{listing,"recv"}},
+ {delay,
+ [{unless,no_share_opt,{pass,beam_ssa_share}},
+ {iff,dssashare,{listing,"ssashare"}},
+ {iff,ssalint,{pass,beam_ssa_lint}},
+ {unless,no_bsm_opt,{pass,beam_ssa_bsm}},
+ {iff,dssabsm,{listing,"ssabsm"}},
+ {iff,ssalint,{pass,beam_ssa_lint}},
+ {unless,no_fun_opt,{pass,beam_ssa_funs}},
+ {iff,dssafuns,{listing,"ssafuns"}},
+ {iff,ssalint,{pass,beam_ssa_lint}},
+ {unless,no_ssa_opt,{pass,beam_ssa_opt}},
+ {iff,dssaopt,{listing,"ssaopt"}},
+ {iff,ssalint,{pass,beam_ssa_lint}},
+ {unless,no_recv_opt,{pass,beam_ssa_recv}},
+ {iff,drecv,{listing,"recv"}}]},
{pass,beam_ssa_pre_codegen},
{iff,dprecg,{listing,"precodegen"}},
{iff,ssalint,{pass,beam_ssa_lint}},
@@ -2121,7 +2120,6 @@ pre_load() ->
erl_scan,
sys_core_alias,
sys_core_bsm,
- sys_core_dsetel,
sys_core_fold,
v3_core,
v3_kernel],
diff --git a/lib/compiler/src/compiler.app.src b/lib/compiler/src/compiler.app.src
index 108a0ca100..a086a3a8d3 100644
--- a/lib/compiler/src/compiler.app.src
+++ b/lib/compiler/src/compiler.app.src
@@ -65,7 +65,6 @@
rec_env,
sys_core_alias,
sys_core_bsm,
- sys_core_dsetel,
sys_core_fold,
sys_core_fold_lists,
sys_core_inline,
diff --git a/lib/compiler/src/erl_bifs.erl b/lib/compiler/src/erl_bifs.erl
index d925decce6..94a5dfe012 100644
--- a/lib/compiler/src/erl_bifs.erl
+++ b/lib/compiler/src/erl_bifs.erl
@@ -32,6 +32,22 @@
%% Returns `true' if the function `Module:Name/Arity' does not
%% affect the state, nor depend on the state, although its
%% evaluation is not guaranteed to complete normally for all input.
+%%
+%% NOTE: There is no need to include every new pure BIF
+%% here. Including it here means that the value of the function
+%% will be evaluated at compile-time if the arguments are
+%% constant. If that optimization is not useful/desired, there is
+%% no need to include the new BIF here.
+%%
+%% Functions whose return value could conceivably change in a
+%% future version of the runtime system must NOT be included here.
+%%
+%% Here are some example of functions that should not be
+%% included: `term_to_binary/1', hashing functions, non-trivial
+%% encode/decode functions.
+%%
+%% When unsure whether a new BIF should be included here, the
+%% conservative safe choice is NOT to include it.
-spec is_pure(atom(), atom(), arity()) -> boolean().
diff --git a/lib/compiler/src/sys_core_dsetel.erl b/lib/compiler/src/sys_core_dsetel.erl
deleted file mode 100644
index 9ab83c210f..0000000000
--- a/lib/compiler/src/sys_core_dsetel.erl
+++ /dev/null
@@ -1,360 +0,0 @@
-%%
-%% %CopyrightBegin%
-%%
-%% 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.
-%% 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 : Using dsetelement to make multiple-field record updates
-%% faster.
-
-%% The expansion of record field updates, when more than one field is
-%% updated, but not a majority of the fields, will create a sequence of
-%% calls to 'erlang:setelement(Index, Value, Tuple)' where Tuple in the
-%% first call is the original record tuple, and in the subsequent calls
-%% Tuple is the result of the previous call. Furthermore, all Index
-%% values are constant positive integers, and the first call to
-%% 'setelement' will have the greatest index. Thus all the following
-%% calls do not actually need to test at run-time whether Tuple has type
-%% tuple, nor that the index is within the tuple bounds.
-%%
-%% Since this introduces destructive updates in the Core Erlang code, it
-%% must be done as a last stage before going to lower-level code.
-%%
-%% NOTE: Because there are currently no write barriers in the system,
-%% this kind of optimization can only be done when we are sure that
-%% garbage collection will not be triggered between the creation of the
-%% tuple and the destructive updates - otherwise we might insert
-%% pointers from an older generation to a newer.
-%%
-%% The rewriting is done as follows:
-%%
-%% let X1 = call 'erlang':'setelement(5, Tuple, Value1)
-%% in call 'erlang':'setelement(3, X1, Value2)
-%% =>
-%% let X1 = call 'erlang':'setelement(5, Tuple, Value1)
-%% in do primop dsetelement(3, X1, Value2)
-%% X1
-%% and
-%% let X1 = call 'erlang':'setelement(5, Tuple, Value1)
-%% in let X2 = call 'erlang':'setelement(3, X1, Value2)
-%% in ...
-%% =>
-%% let X2 = call 'erlang':'setelement(5, Tuple, Value1)
-%% in do primop 'dsetelement(3, X2, Value2)
-%% ...
-%% if X1 is used exactly once.
-%% Thus, we need to track variable usage.
-%%
-
--module(sys_core_dsetel).
-
--export([module/2]).
-
--include("core_parse.hrl").
-
--spec module(cerl:c_module(), [compile:option()]) -> {'ok', cerl:c_module()}.
-
-module(M0, _Options) ->
- M = visit_module(M0),
- {ok,M}.
-
-visit_module(#c_module{defs=Ds0}=R) ->
- Env = #{},
- Ds = visit_module_1(Ds0, Env, []),
- R#c_module{defs=Ds}.
-
-visit_module_1([{Name,F0}|Fs], Env, Acc) ->
- try visit(Env, F0) of
- {F,_} ->
- visit_module_1(Fs, Env, [{Name,F}|Acc])
- catch
- Class:Error:Stack ->
- #c_var{name={Func,Arity}} = Name,
- io:fwrite("Function: ~w/~w\n", [Func,Arity]),
- erlang:raise(Class, Error, Stack)
- end;
-visit_module_1([], _, Acc) ->
- lists:reverse(Acc).
-
-visit(Env, #c_var{name={_,_}}=R) ->
- %% Ignore local function name.
- {R, Env};
-visit(Env0, #c_var{name=X}=R) ->
- %% There should not be any free variables. If there are,
- %% the case will fail with an exception.
- case Env0 of
- #{X:=N} ->
- {R, Env0#{X:=N+1}}
- end;
-visit(Env, #c_literal{}=R) ->
- {R, Env};
-visit(Env0, #c_tuple{es=Es0}=R) ->
- {Es1,Env1} = visit_list(Env0, Es0),
- {R#c_tuple{es=Es1}, Env1};
-visit(Env0, #c_map{es=Es0}=R) ->
- {Es1,Env1} = visit_list(Env0, Es0),
- {R#c_map{es=Es1}, Env1};
-visit(Env0, #c_map_pair{key=K0,val=V0}=R) ->
- {K,Env1} = visit(Env0, K0),
- {V,Env2} = visit(Env1, V0),
- {R#c_map_pair{key=K,val=V}, Env2};
-visit(Env0, #c_cons{hd=H0,tl=T0}=R) ->
- {H1,Env1} = visit(Env0, H0),
- {T1,Env2} = visit(Env1, T0),
- {R#c_cons{hd=H1,tl=T1}, Env2};
-visit(Env0, #c_binary{segments=Segs}=R) ->
- Env = visit_bin_segs(Env0, Segs),
- {R, Env};
-visit(Env0, #c_values{es=Es0}=R) ->
- {Es1,Env1} = visit_list(Env0, Es0),
- {R#c_values{es=Es1}, Env1};
-visit(Env0, #c_fun{vars=Vs, body=B0}=R) ->
- {Xs, Env1} = bind_vars(Vs, Env0),
- {B1,Env2} = visit(Env1, B0),
- {R#c_fun{body=B1}, restore_vars(Xs, Env0, Env2)};
-visit(Env0, #c_let{vars=Vs, arg=A0, body=B0}=R) ->
- {A1,Env1} = visit(Env0, A0),
- {Xs,Env2} = bind_vars(Vs, Env1),
- {B1,Env3} = visit(Env2, B0),
- rewrite(R#c_let{arg=A1,body=B1}, Env3, restore_vars(Xs, Env1, Env3));
-visit(Env0, #c_seq{arg=A0, body=B0}=R) ->
- {A1,Env1} = visit(Env0, A0),
- {B1,Env2} = visit(Env1, B0),
- {R#c_seq{arg=A1,body=B1}, Env2};
-visit(Env0, #c_case{arg=A0,clauses=Cs0}=R) ->
- {A1,Env1} = visit(Env0, A0),
- {Cs1,Env2} = visit_list(Env1, Cs0),
- {R#c_case{arg=A1,clauses=Cs1}, Env2};
-visit(Env0, #c_clause{pats=Ps,guard=G0,body=B0}=R) ->
- {Vs, Env1} = visit_pats(Ps, Env0),
- {G1,Env2} = visit(Env1, G0),
- {B1,Env3} = visit(Env2, B0),
- {R#c_clause{guard=G1,body=B1}, restore_vars(Vs, Env0, Env3)};
-visit(Env0, #c_receive{clauses=Cs0,timeout=T0,action=A0}=R) ->
- {T1,Env1} = visit(Env0, T0),
- {Cs1,Env2} = visit_list(Env1, Cs0),
- {A1,Env3} = visit(Env2, A0),
- {R#c_receive{clauses=Cs1,timeout=T1,action=A1}, Env3};
-visit(Env0, #c_apply{op=Op0, args=As0}=R) ->
- {Op1,Env1} = visit(Env0, Op0),
- {As1,Env2} = visit_list(Env1, As0),
- {R#c_apply{op=Op1,args=As1}, Env2};
-visit(Env0, #c_call{module=M0,name=N0,args=As0}=R) ->
- {M1,Env1} = visit(Env0, M0),
- {N1,Env2} = visit(Env1, N0),
- {As1,Env3} = visit_list(Env2, As0),
- {R#c_call{module=M1,name=N1,args=As1}, Env3};
-visit(Env0, #c_primop{name=N0, args=As0}=R) ->
- {N1,Env1} = visit(Env0, N0),
- {As1,Env2} = visit_list(Env1, As0),
- {R#c_primop{name=N1,args=As1}, Env2};
-visit(Env0, #c_try{arg=E0, vars=Vs, body=B0, evars=Evs, handler=H0}=R) ->
- {E1,Env1} = visit(Env0, E0),
- {Xs, Env2} = bind_vars(Vs, Env1),
- {B1,Env3} = visit(Env2, B0),
- Env4 = restore_vars(Xs, Env1, Env3),
- {Ys, Env5} = bind_vars(Evs, Env4),
- {H1,Env6} = visit(Env5, H0),
- {R#c_try{arg=E1,body=B1,handler=H1}, restore_vars(Ys, Env4, Env6)};
-visit(Env0, #c_catch{body=B0}=R) ->
- {B1,Env1} = visit(Env0, B0),
- {R#c_catch{body=B1}, Env1};
-visit(Env0, #c_letrec{defs=Ds0,body=B0}=R) ->
- {Xs, Env1} = bind_vars([V || {V,_} <- Ds0], Env0),
- {Ds1,Env2} = visit_def_list(Env1, Ds0),
- {B1,Env3} = visit(Env2, B0),
- {R#c_letrec{defs=Ds1,body=B1}, restore_vars(Xs, Env0, Env3)}.
-%% The following general code for handling modules is slow if a module
-%% contains very many functions. There is special code in visit_module/1
-%% which is much faster.
-%% visit(Env0, #c_module{defs=D0}=R) ->
-%% {R1,Env1} = visit(Env0, #c_letrec{defs=D0,body=#c_nil{}}),
-%% {R#c_module{defs=R1#c_letrec.defs}, Env1};
-
-visit_list(Env, L) ->
- lists:mapfoldl(fun (E, A) -> visit(A, E) end, Env, L).
-
-visit_def_list(Env, L) ->
- lists:mapfoldl(fun ({Name,V0}, E0) ->
- {V1,E1} = visit(E0, V0),
- {{Name,V1}, E1}
- end, Env, L).
-
-visit_bin_segs(Env, Segs) ->
- lists:foldl(fun (#c_bitstr{val=Val,size=Sz}, E0) ->
- {_, E1} = visit(E0, Val),
- {_, E2} = visit(E1, Sz),
- E2
- end, Env, Segs).
-
-bind_vars(Vs, Env) ->
- bind_vars(Vs, Env, []).
-
-bind_vars([#c_var{name=X}|Vs], Env0, Xs)->
- bind_vars(Vs, Env0#{X=>0}, [X|Xs]);
-bind_vars([], Env,Xs) ->
- {Xs, Env}.
-
-visit_pats(Ps, Env) ->
- visit_pats(Ps, Env, []).
-
-visit_pats([P|Ps], Env0, Vs0) ->
- {Vs1, Env1} = visit_pat(Env0, P, Vs0),
- visit_pats(Ps, Env1, Vs1);
-visit_pats([], Env, Vs) ->
- {Vs, Env}.
-
-visit_pat(Env0, #c_var{name=V}, Vs) ->
- {[V|Vs], Env0#{V=>0}};
-visit_pat(Env0, #c_tuple{es=Es}, Vs) ->
- visit_pats(Es, Env0, Vs);
-visit_pat(Env0, #c_map{es=Es}, Vs) ->
- visit_pats(Es, Env0, Vs);
-visit_pat(Env0, #c_map_pair{op=#c_literal{val=exact},key=V,val=K}, Vs0) ->
- {Vs1, Env1} = visit_pat(Env0, V, Vs0),
- visit_pat(Env1, K, Vs1);
-visit_pat(Env0, #c_cons{hd=H,tl=T}, Vs0) ->
- {Vs1, Env1} = visit_pat(Env0, H, Vs0),
- visit_pat(Env1, T, Vs1);
-visit_pat(Env0, #c_binary{segments=Segs}, Vs) ->
- visit_pats(Segs, Env0, Vs);
-visit_pat(Env0, #c_bitstr{val=Val,size=Sz}, Vs0) ->
- {Vs1, Env1} =
- case Sz of
- #c_var{name=V} ->
- %% We don't tolerate free variables.
- case Env0 of
- #{V:=N} ->
- {Vs0, Env0#{V:=N+1}}
- end;
- _ ->
- visit_pat(Env0, Sz, Vs0)
- end,
- visit_pat(Env1, Val, Vs1);
-visit_pat(Env0, #c_alias{pat=P,var=#c_var{name=V}}, Vs) ->
- visit_pat(Env0#{V=>0}, P, [V|Vs]);
-visit_pat(Env, #c_literal{}, Vs) ->
- {Vs, Env}.
-
-restore_vars([V|Vs], Env0, Env1) ->
- case Env0 of
- #{V:=N} ->
- restore_vars(Vs, Env0, Env1#{V=>N});
- _ ->
- restore_vars(Vs, Env0, maps:remove(V, Env1))
- end;
-restore_vars([], _, Env1) ->
- Env1.
-
-
-%% let X1 = call 'erlang':'setelement(5, Tuple, Value1)
-%% in call 'erlang':'setelement(3, X1, Value2)
-%% =>
-%% let X1 = call 'erlang':'setelement(5, Tuple, Value1)
-%% in do primop dsetelement(3, X1, Value2)
-%% X1
-
-rewrite(#c_let{vars=[#c_var{name=X}=V]=Vs,
- arg=#c_call{module=#c_literal{val='erlang'},
- name=#c_literal{val='setelement'},
- args=[#c_literal{val=Index1}, _Tuple, _Val1]
- }=A,
- body=#c_call{anno=Banno,module=#c_literal{val='erlang'},
- name=#c_literal{val='setelement'},
- args=[#c_literal{val=Index2},
- #c_var{name=X},
- Val2]
- }
- }=R,
- _BodyEnv, FinalEnv)
- when is_integer(Index1), is_integer(Index2), Index2 > 0, Index1 > Index2 ->
- case is_safe(Val2) of
- true ->
- {R#c_let{vars=Vs,
- arg=A,
- body=#c_seq{arg=#c_primop{
- anno=Banno,
- name=#c_literal{val='dsetelement'},
- args=[#c_literal{val=Index2},
- V,
- Val2]},
- body=V}
- },
- FinalEnv};
- false ->
- {R, FinalEnv}
- end;
-
-%% let X1 = call 'erlang':'setelement(5, Tuple, Value1)
-%% in let X2 = 'erlang':'setelement(3, X1, Value2)
-%% in ...
-%% =>
-%% let X2 = call 'erlang':'setelement(5, Tuple, Value1)
-%% in do primop dsetelement(3, X2, Value2)
-%% ...
-%% if X1 is used exactly once.
-
-rewrite(#c_let{vars=[#c_var{name=X1}],
- arg=#c_call{module=#c_literal{val='erlang'},
- name=#c_literal{val='setelement'},
- args=[#c_literal{val=Index1}, _Tuple, _Val1]
- }=A,
- body=#c_let{vars=[#c_var{}=V]=Vs,
- arg=#c_call{anno=Banno,
- module=#c_literal{val='erlang'},
- name=#c_literal{val='setelement'},
- args=[#c_literal{val=Index2},
- #c_var{name=X1},
- Val2]},
- body=B}
- }=R,
- BodyEnv, FinalEnv)
- when is_integer(Index1), is_integer(Index2), Index2 > 0, Index1 > Index2 ->
- case is_single_use(X1, BodyEnv) andalso is_safe(Val2) of
- true ->
- {R#c_let{vars=Vs,
- arg=A,
- body=#c_seq{arg=#c_primop{
- anno=Banno,
- name=#c_literal{val='dsetelement'},
- args=[#c_literal{val=Index2},
- V,
- Val2]},
- body=B}
- },
- FinalEnv};
- false ->
- {R, FinalEnv}
- end;
-
-rewrite(R, _, FinalEnv) ->
- {R, FinalEnv}.
-
-%% is_safe(CoreExpr) -> true|false
-%% Determines whether the Core expression can cause a GC collection at run-time.
-%% Note: Assumes that the constant pool is turned on.
-
-is_safe(#c_var{}) -> true;
-is_safe(#c_literal{}) -> true;
-is_safe(_) -> false.
-
-is_single_use(V, Env) ->
- case Env of
- #{V:=1} ->
- true;
- _ ->
- false
- end.
diff --git a/lib/compiler/src/sys_core_fold.erl b/lib/compiler/src/sys_core_fold.erl
index 43c99be982..7e219da0af 100644
--- a/lib/compiler/src/sys_core_fold.erl
+++ b/lib/compiler/src/sys_core_fold.erl
@@ -961,18 +961,12 @@ fold_lit_args(Call, Module, Name, Args0) ->
%%
fold_non_lit_args(Call, erlang, is_boolean, [Arg], Sub) ->
eval_is_boolean(Call, Arg, Sub);
-fold_non_lit_args(Call, erlang, element, [Arg1,Arg2], Sub) ->
- eval_element(Call, Arg1, Arg2, Sub);
fold_non_lit_args(Call, erlang, length, [Arg], _) ->
eval_length(Call, Arg);
fold_non_lit_args(Call, erlang, '++', [Arg1,Arg2], _) ->
eval_append(Call, Arg1, Arg2);
fold_non_lit_args(Call, lists, append, [Arg1,Arg2], _) ->
eval_append(Call, Arg1, Arg2);
-fold_non_lit_args(Call, erlang, setelement, [Arg1,Arg2,Arg3], _) ->
- eval_setelement(Call, Arg1, Arg2, Arg3);
-fold_non_lit_args(Call, erlang, is_record, [Arg1,Arg2,Arg3], Sub) ->
- eval_is_record(Call, Arg1, Arg2, Arg3, Sub);
fold_non_lit_args(Call, erlang, is_function, [Arg1], Sub) ->
eval_is_function_1(Call, Arg1, Sub);
fold_non_lit_args(Call, erlang, is_function, [Arg1,Arg2], Sub) ->
@@ -1141,96 +1135,6 @@ eval_append(Call, #c_cons{anno=Anno,hd=H,tl=T}, List) ->
eval_append(Call, X, Y) ->
Call#c_call{args=[X,Y]}. %Rebuild call arguments.
-%% eval_element(Call, Pos, Tuple, Types) -> Val.
-%% Evaluates element/2 if the position Pos is a literal and
-%% the shape of the tuple Tuple is known.
-%%
-eval_element(Call, #c_literal{val=Pos}, Tuple, Types)
- when is_integer(Pos) ->
- case get_type(Tuple, Types) of
- none ->
- Call;
- Type ->
- Es = case cerl:is_c_tuple(Type) of
- false -> [];
- true -> cerl:tuple_es(Type)
- end,
- if
- 1 =< Pos, Pos =< length(Es) ->
- El = lists:nth(Pos, Es),
- try
- cerl:set_ann(pat_to_expr(El), [compiler_generated])
- catch
- throw:impossible ->
- Call
- end;
- true ->
- %% Index outside tuple or not a tuple.
- eval_failure(Call, badarg)
- end
- end;
-eval_element(Call, Pos, Tuple, Sub) ->
- case is_int_type(Pos, Sub) =:= no orelse
- is_tuple_type(Tuple, Sub) =:= no of
- true ->
- eval_failure(Call, badarg);
- false ->
- Call
- end.
-
-%% eval_is_record(Call, Var, Tag, Size, Types) -> Val.
-%% Evaluates is_record/3 using type information.
-%%
-eval_is_record(Call, Term, #c_literal{val=NeededTag},
- #c_literal{val=Size}, Types) ->
- case get_type(Term, Types) of
- none ->
- Call;
- Type ->
- Es = case cerl:is_c_tuple(Type) of
- false -> [];
- true -> cerl:tuple_es(Type)
- end,
- case Es of
- [#c_literal{val=Tag}|_] ->
- Bool = Tag =:= NeededTag andalso
- length(Es) =:= Size,
- #c_literal{val=Bool};
- _ ->
- #c_literal{val=false}
- end
- end;
-eval_is_record(Call, _, _, _, _) -> Call.
-
-%% eval_setelement(Call, Pos, Tuple, NewVal) -> Core.
-%% Evaluates setelement/3 if position Pos is an integer
-%% and the shape of the tuple Tuple is known.
-%%
-eval_setelement(Call, #c_literal{val=Pos}, Tuple, NewVal)
- when is_integer(Pos) ->
- case cerl:is_data(Tuple) of
- false ->
- Call;
- true ->
- Es0 = case cerl:is_c_tuple(Tuple) of
- false -> [];
- true -> cerl:tuple_es(Tuple)
- end,
- if
- 1 =< Pos, Pos =< length(Es0) ->
- Es = eval_setelement_1(Pos, Es0, NewVal),
- cerl:update_c_tuple(Tuple, Es);
- true ->
- eval_failure(Call, badarg)
- end
- end;
-eval_setelement(Call, _, _, _) -> Call.
-
-eval_setelement_1(1, [_|T], NewVal) ->
- [NewVal|T];
-eval_setelement_1(Pos, [H|T], NewVal) when Pos > 1 ->
- [H|eval_setelement_1(Pos-1, T, NewVal)].
-
%% eval_failure(Call, Reason) -> Core.
%% Warn for a call that will fail and replace the call with
%% a call to erlang:error(Reason).
@@ -1290,16 +1194,15 @@ clause(#c_clause{pats=Ps0}=Cl, Cexpr, Ctxt, Sub0) ->
end.
clause_1(#c_clause{guard=G0,body=B0}=Cl, Ps1, Cexpr, Ctxt, Sub1) ->
- Sub2 = update_types(Cexpr, Ps1, Sub1),
GSub = case {Cexpr,Ps1,G0} of
{_,_,#c_literal{}} ->
%% No need for substitution tricks when the guard
%% does not contain any variables.
- Sub2;
+ Sub1;
{#c_var{name='_'},_,_} ->
%% In a 'receive', Cexpr is the variable '_', which represents the
%% message being matched. We must NOT do any extra substiutions.
- Sub2;
+ Sub1;
{#c_var{},[#c_var{}=Var],_} ->
%% The idea here is to optimize expressions such as
%%
@@ -1321,16 +1224,16 @@ clause_1(#c_clause{guard=G0,body=B0}=Cl, Ps1, Cexpr, Ctxt, Sub1) ->
%%
case cerl:is_c_fname(Cexpr) of
false ->
- sub_set_var(Var, Cexpr, Sub2);
+ sub_set_var(Var, Cexpr, Sub1);
true ->
%% We must not copy funs, and especially not into guards.
- Sub2
+ Sub1
end;
_ ->
- Sub2
+ Sub1
end,
G1 = guard(G0, GSub),
- B1 = body(B0, Ctxt, Sub2),
+ B1 = body(B0, Ctxt, Sub1),
Cl#c_clause{pats=Ps1,guard=G1,body=B1}.
%% let_substs(LetVars, LetArg, Sub) -> {[Var],[Val],Sub}.
@@ -1414,8 +1317,7 @@ pattern(#c_binary{segments=V0}=Pat, Isub, Osub0) ->
{Pat#c_binary{segments=V1},Osub1};
pattern(#c_alias{var=V0,pat=P0}=Pat, Isub, Osub0) ->
{V1,Osub1} = pattern(V0, Isub, Osub0),
- {P1,Osub2} = pattern(P0, Isub, Osub1),
- Osub = update_types(V1, [P1], Osub2),
+ {P1,Osub} = pattern(P0, Isub, Osub1),
{Pat#c_alias{var=V1,pat=P1},Osub}.
map_pair_pattern_list(Ps0, Isub, Osub0) ->
@@ -2137,14 +2039,9 @@ case_expand_var(E, #sub{t=Tdb}) ->
%% encountered.
coerce_to_data(C) ->
- case cerl:is_c_alias(C) of
- false ->
- case cerl:is_data(C) orelse cerl:is_c_var(C) of
- true -> C;
- false -> throw(impossible)
- end;
- true ->
- coerce_to_data(cerl:alias_pat(C))
+ case cerl:is_data(C) orelse cerl:is_c_var(C) of
+ true -> C;
+ false -> throw(impossible)
end.
%% case_opt_nomatch(E, Clauses, LitExpr) -> Clauses'
@@ -3140,14 +3037,6 @@ is_int_type(Var, Sub) ->
C -> yes_no(cerl:is_c_int(C))
end.
--spec is_tuple_type(cerl:cerl(), sub()) -> yes_no_maybe().
-
-is_tuple_type(Var, Sub) ->
- case get_type(Var, Sub) of
- none -> maybe;
- C -> yes_no(cerl:is_c_tuple(C))
- end.
-
yes_no(true) -> yes;
yes_no(false) -> no.
@@ -3209,27 +3098,23 @@ returns_integer(_, _) -> false.
%% update_types(Expr, Pattern, Sub) -> Sub'
%% Update the type database.
--spec update_types(cerl:cerl(), [type_info()], sub()) -> sub().
+-spec update_types(cerl:c_var(), [type_info()], sub()) -> sub().
-update_types(Expr, Pat, #sub{t=Tdb0}=Sub) ->
- Tdb = update_types_1(Expr, Pat, Tdb0),
+update_types(#c_var{name=V}, Pat, #sub{t=Tdb0}=Sub) ->
+ Tdb = update_types_1(V, Pat, Tdb0),
Sub#sub{t=Tdb}.
-update_types_1(#c_var{name=V}, Pat, Types) ->
- update_types_2(V, Pat, Types);
-update_types_1(_, _, Types) -> Types.
-
-update_types_2(V, [#c_tuple{}=P], Types) ->
+update_types_1(V, [#c_tuple{}=P], Types) ->
Types#{V=>P};
-update_types_2(V, [#c_literal{val=Bool}], Types) when is_boolean(Bool) ->
+update_types_1(V, [#c_literal{val=Bool}], Types) when is_boolean(Bool) ->
Types#{V=>bool};
-update_types_2(V, [#c_fun{vars=Vars}], Types) ->
+update_types_1(V, [#c_fun{vars=Vars}], Types) ->
Types#{V=>{'fun',length(Vars)}};
-update_types_2(V, [#c_var{name={_,Arity}}], Types) ->
+update_types_1(V, [#c_var{name={_,Arity}}], Types) ->
Types#{V=>{'fun',Arity}};
-update_types_2(V, [Type], Types) when is_atom(Type) ->
+update_types_1(V, [Type], Types) when is_atom(Type) ->
Types#{V=>Type};
-update_types_2(_, _, Types) -> Types.
+update_types_1(_, _, Types) -> Types.
%% kill_types(V, Tdb) -> Tdb'
%% Kill any entries that references the variable,
diff --git a/lib/compiler/src/v3_core.erl b/lib/compiler/src/v3_core.erl
index 34930c3afe..3699c9d22e 100644
--- a/lib/compiler/src/v3_core.erl
+++ b/lib/compiler/src/v3_core.erl
@@ -330,7 +330,7 @@ gexpr({protect,Line,Arg}, Bools0, St0) ->
{#iprotect{anno=#a{anno=Anno},body=Eps++[E]},[],Bools0,St}
end;
gexpr({op,_,'andalso',_,_}=E0, Bools, St0) ->
- {op,L,'andalso',E1,E2} = right_assoc(E0, 'andalso', St0),
+ {op,L,'andalso',E1,E2} = right_assoc(E0, 'andalso'),
Anno = lineno_anno(L, St0),
{#c_var{name=V0},St} = new_var(Anno, St0),
V = {var,L,V0},
@@ -338,7 +338,7 @@ gexpr({op,_,'andalso',_,_}=E0, Bools, St0) ->
E = make_bool_switch_guard(L, E1, V, E2, False),
gexpr(E, Bools, St);
gexpr({op,_,'orelse',_,_}=E0, Bools, St0) ->
- {op,L,'orelse',E1,E2} = right_assoc(E0, 'orelse', St0),
+ {op,L,'orelse',E1,E2} = right_assoc(E0, 'orelse'),
Anno = lineno_anno(L, St0),
{#c_var{name=V0},St} = new_var(Anno, St0),
V = {var,L,V0},
@@ -767,14 +767,16 @@ expr({op,_,'++',{lc,Llc,E,Qs0},More}, St0) ->
{Qs,St2} = preprocess_quals(Llc, Qs0, St1),
{Y,Yps,St} = lc_tq(Llc, E, Qs, Mc, St2),
{Y,Mps++Yps,St};
-expr({op,L,'andalso',E1,E2}, St0) ->
+expr({op,_,'andalso',_,_}=E0, St0) ->
+ {op,L,'andalso',E1,E2} = right_assoc(E0, 'andalso'),
Anno = lineno_anno(L, St0),
{#c_var{name=V0},St} = new_var(Anno, St0),
V = {var,L,V0},
False = {atom,L,false},
E = make_bool_switch(L, E1, V, E2, False, St0),
expr(E, St);
-expr({op,L,'orelse',E1,E2}, St0) ->
+expr({op,_,'orelse',_,_}=E0, St0) ->
+ {op,L,'orelse',E1,E2} = right_assoc(E0, 'orelse'),
Anno = lineno_anno(L, St0),
{#c_var{name=V0},St} = new_var(Anno, St0),
V = {var,L,V0},
@@ -2058,17 +2060,9 @@ fail_clause(Pats, Anno, Arg) ->
args=[Arg]}]}.
%% Optimization for Dialyzer.
-right_assoc(E, Op, St) ->
- case member(dialyzer, St#core.opts) of
- true ->
- right_assoc2(E, Op);
- false ->
- E
- end.
-
-right_assoc2({op,L1,Op,{op,L2,Op,E1,E2},E3}, Op) ->
- right_assoc2({op,L2,Op,E1,{op,L1,Op,E2,E3}}, Op);
-right_assoc2(E, _Op) -> E.
+right_assoc({op,L1,Op,{op,L2,Op,E1,E2},E3}, Op) ->
+ right_assoc({op,L2,Op,E1,{op,L1,Op,E2,E3}}, Op);
+right_assoc(E, _Op) -> E.
annotate_tuple(A, Es, St) ->
case member(dialyzer, St#core.opts) of
diff --git a/lib/compiler/src/v3_kernel.erl b/lib/compiler/src/v3_kernel.erl
index f7ca66b1da..86351bc0c5 100644
--- a/lib/compiler/src/v3_kernel.erl
+++ b/lib/compiler/src/v3_kernel.erl
@@ -1414,7 +1414,6 @@ is_remote_bif(_, _, _) -> false.
%% return multiple values. Only used in bodies where a BIF may be
%% called for effect only.
-bif_vals(dsetelement, 3) -> 0;
bif_vals(_, _) -> 1.
bif_vals(_, _, _) -> 1.
diff --git a/lib/compiler/test/Makefile b/lib/compiler/test/Makefile
index f042a5cb51..db8eb7e2e1 100644
--- a/lib/compiler/test/Makefile
+++ b/lib/compiler/test/Makefile
@@ -107,6 +107,8 @@ CORE_MODULES = \
NO_MOD_OPT = $(NO_OPT)
+NO_SSA_OPT = $(NO_OPT)
+
NO_OPT_MODULES= $(NO_OPT:%=%_no_opt_SUITE)
NO_OPT_ERL_FILES= $(NO_OPT_MODULES:%=%.erl)
POST_OPT_MODULES= $(NO_OPT:%=%_post_opt_SUITE)
@@ -117,6 +119,8 @@ R21_MODULES= $(R21:%=%_r21_SUITE)
R21_ERL_FILES= $(R21_MODULES:%=%.erl)
NO_MOD_OPT_MODULES= $(NO_MOD_OPT:%=%_no_module_opt_SUITE)
NO_MOD_OPT_ERL_FILES= $(NO_MOD_OPT_MODULES:%=%.erl)
+NO_SSA_OPT_MODULES= $(NO_SSA_OPT:%=%_no_ssa_opt_SUITE)
+NO_SSA_OPT_ERL_FILES= $(NO_SSA_OPT_MODULES:%=%.erl)
ERL_FILES= $(MODULES:%=%.erl)
CORE_FILES= $(CORE_MODULES:%=%.core)
@@ -145,13 +149,16 @@ EBIN = .
# Targets
# ----------------------------------------------------
-make_emakefile: $(NO_OPT_ERL_FILES) $(POST_OPT_ERL_FILES) \
+make_emakefile: $(NO_OPT_ERL_FILES) $(POST_OPT_ERL_FILES) $(NO_SSA_OPT_ERL_FILES) \
$(INLINE_ERL_FILES) $(R21_ERL_FILES) $(NO_MOD_OPT_ERL_FILES)
$(ERL_TOP)/make/make_emakefile $(ERL_COMPILE_FLAGS) -o$(EBIN) $(MODULES) \
> $(EMAKEFILE)
$(ERL_TOP)/make/make_emakefile +no_copt +no_postopt \
+no_ssa_opt +no_recv_opt $(ERL_COMPILE_FLAGS) \
-o$(EBIN) $(NO_OPT_MODULES) >> $(EMAKEFILE)
+ $(ERL_TOP)/make/make_emakefile +no_share_opt +no_bsm_opt +no_fun_opt \
+ +no_ssa_opt +no_recv_opt $(ERL_COMPILE_FLAGS) \
+ -o$(EBIN) $(NO_SSA_OPT_MODULES) >> $(EMAKEFILE)
$(ERL_TOP)/make/make_emakefile +no_copt $(ERL_COMPILE_FLAGS) \
-o$(EBIN) $(POST_OPT_MODULES) >> $(EMAKEFILE)
$(ERL_TOP)/make/make_emakefile +inline $(ERL_COMPILE_FLAGS) \
@@ -180,6 +187,9 @@ docs:
%_no_opt_SUITE.erl: %_SUITE.erl
sed -e 's;-module($(basename $<));-module($(basename $@));' $< > $@
+%_no_ssa_opt_SUITE.erl: %_SUITE.erl
+ sed -e 's;-module($(basename $<));-module($(basename $@));' $< > $@
+
%_post_opt_SUITE.erl: %_SUITE.erl
sed -e 's;-module($(basename $<));-module($(basename $@));' $< > $@
@@ -205,7 +215,8 @@ release_tests_spec: make_emakefile
$(EMAKEFILE) $(ERL_FILES) "$(RELSYSDIR)"
$(INSTALL_DATA) $(NO_OPT_ERL_FILES) $(POST_OPT_ERL_FILES) \
$(INLINE_ERL_FILES) $(R21_ERL_FILES) \
- $(NO_MOD_OPT_ERL_FILES) "$(RELSYSDIR)"
+ $(NO_MOD_OPT_ERL_FILES) \
+ $(NO_SSA_OPT_ERL_FILES) "$(RELSYSDIR)"
$(INSTALL_DATA) $(CORE_FILES) "$(RELSYSDIR)"
for file in $(ERL_DUMMY_FILES); do \
module=`basename $$file .erl`; \
diff --git a/lib/compiler/test/beam_validator_SUITE.erl b/lib/compiler/test/beam_validator_SUITE.erl
index 585d0e7191..2660bf222c 100644
--- a/lib/compiler/test/beam_validator_SUITE.erl
+++ b/lib/compiler/test/beam_validator_SUITE.erl
@@ -159,7 +159,7 @@ merge_undefined(Config) when is_list(Config) ->
[{{t,handle_call,2},
{{call_ext,1,{extfunc,erlang,exit,1}},
10,
- {uninitialized_reg,{y,0}}}}] = Errors,
+ {uninitialized_reg,{y,_}}}}] = Errors,
ok.
uninit(Config) when is_list(Config) ->
@@ -211,16 +211,16 @@ bad_catch_try(Config) when is_list(Config) ->
Errors = do_val(bad_catch_try, Config),
[{{bad_catch_try,bad_1,1},
{{'catch',{x,0},{f,3}},
- 5,{invalid_store,{x,0},{catchtag,[3]}}}},
+ 5,{invalid_tag_register,{x,0}}}},
{{bad_catch_try,bad_2,1},
{{catch_end,{x,9}},
- 8,{source_not_y_reg,{x,9}}}},
+ 8,{invalid_tag_register,{x,9}}}},
{{bad_catch_try,bad_3,1},
- {{catch_end,{y,1}},9,{bad_type,{atom,kalle}}}},
+ {{catch_end,{y,1}},9,{invalid_tag,{y,1},{atom,kalle}}}},
{{bad_catch_try,bad_4,1},
- {{'try',{x,0},{f,15}},5,{invalid_store,{x,0},{trytag,[15]}}}},
+ {{'try',{x,0},{f,15}},5,{invalid_tag_register,{x,0}}}},
{{bad_catch_try,bad_5,1},
- {{try_case,{y,1}},12,{bad_type,term}}},
+ {{try_case,{y,1}},12,{invalid_tag,{y,1},term}}},
{{bad_catch_try,bad_6,1},
{{move,{integer,1},{y,1}},7,
{invalid_store,{y,1},{integer,1}}}}] = Errors,
@@ -539,37 +539,37 @@ receive_stacked(Config) ->
[{{receive_stacked,f1,0},
{{loop_rec_end,{f,3}},
17,
- {fragile_message_reference,{y,0}}}},
+ {fragile_message_reference,{y,_}}}},
{{receive_stacked,f2,0},
- {{test_heap,3,0},10,{fragile_message_reference,{y,1}}}},
+ {{test_heap,3,0},10,{fragile_message_reference,{y,_}}}},
{{receive_stacked,f3,0},
- {{test_heap,3,0},10,{fragile_message_reference,{y,1}}}},
+ {{test_heap,3,0},10,{fragile_message_reference,{y,_}}}},
{{receive_stacked,f4,0},
- {{test_heap,3,0},10,{fragile_message_reference,{y,1}}}},
+ {{test_heap,3,0},10,{fragile_message_reference,{y,_}}}},
{{receive_stacked,f5,0},
{{loop_rec_end,{f,23}},
23,
- {fragile_message_reference,{y,1}}}},
+ {fragile_message_reference,{y,_}}}},
{{receive_stacked,f6,0},
- {{gc_bif,byte_size,{f,29},0,[{y,0}],{x,0}},
+ {{gc_bif,byte_size,{f,29},0,[{y,_}],{x,0}},
12,
- {fragile_message_reference,{y,0}}}},
+ {fragile_message_reference,{y,_}}}},
{{receive_stacked,f7,0},
{{loop_rec_end,{f,33}},
20,
- {fragile_message_reference,{y,0}}}},
+ {fragile_message_reference,{y,_}}}},
{{receive_stacked,f8,0},
{{loop_rec_end,{f,38}},
20,
- {fragile_message_reference,{y,0}}}},
+ {fragile_message_reference,{y,_}}}},
{{receive_stacked,m1,0},
{{loop_rec_end,{f,43}},
19,
- {fragile_message_reference,{y,0}}}},
+ {fragile_message_reference,{y,_}}}},
{{receive_stacked,m2,0},
{{loop_rec_end,{f,48}},
33,
- {fragile_message_reference,{y,0}}}}] = Errors,
+ {fragile_message_reference,{y,_}}}}] = Errors,
%% Compile the original source code as a smoke test.
Data = proplists:get_value(data_dir, Config),
diff --git a/lib/compiler/test/bif_SUITE.erl b/lib/compiler/test/bif_SUITE.erl
index 42ba5d5365..423a7666af 100644
--- a/lib/compiler/test/bif_SUITE.erl
+++ b/lib/compiler/test/bif_SUITE.erl
@@ -23,7 +23,7 @@
-export([all/0,suite/0,groups/0,init_per_suite/1,end_per_suite/1,
init_per_group/2,end_per_group/2,
- beam_validator/1,trunc_and_friends/1,cover_safe_bifs/1]).
+ beam_validator/1,trunc_and_friends/1,cover_safe_and_pure_bifs/1]).
suite() ->
[{ct_hooks,[ts_install_cth]}].
@@ -35,7 +35,7 @@ groups() ->
[{p,[parallel],
[beam_validator,
trunc_and_friends,
- cover_safe_bifs
+ cover_safe_and_pure_bifs
]}].
init_per_suite(Config) ->
@@ -106,7 +106,7 @@ trunc_template(Func, Bif) ->
catch error:badarg -> ok end,
ok.").
-cover_safe_bifs(Config) ->
+cover_safe_and_pure_bifs(Config) ->
_ = get(),
_ = get_keys(a),
_ = group_leader(),
@@ -118,5 +118,6 @@ cover_safe_bifs(Config) ->
_ = processes(),
_ = registered(),
_ = term_to_binary(Config),
+ 42 = list_to_integer("2A", 16),
ok.
diff --git a/lib/compiler/test/compile_SUITE.erl b/lib/compiler/test/compile_SUITE.erl
index dade5d20d5..408af80dd9 100644
--- a/lib/compiler/test/compile_SUITE.erl
+++ b/lib/compiler/test/compile_SUITE.erl
@@ -28,7 +28,7 @@
init_per_group/2,end_per_group/2,
app_test/1,appup_test/1,
debug_info/4, custom_debug_info/1, custom_compile_info/1,
- file_1/1, forms_2/1, module_mismatch/1, big_file/1, outdir/1,
+ file_1/1, forms_2/1, module_mismatch/1, outdir/1,
binary/1, makedep/1, cond_and_ifdef/1, listings/1, listings_big/1,
other_output/1, kernel_listing/1, encrypted_abstr/1,
strict_record/1, utf8_atoms/1, utf8_functions/1, extra_chunks/1,
@@ -46,7 +46,7 @@ suite() -> [{ct_hooks,[ts_install_cth]}].
-spec all() -> all_return_type().
all() ->
- [app_test, appup_test, file_1, forms_2, module_mismatch, big_file, outdir,
+ [app_test, appup_test, file_1, forms_2, module_mismatch, outdir,
binary, makedep, cond_and_ifdef, listings, listings_big,
other_output, kernel_listing, encrypted_abstr, tuple_calls,
strict_record, utf8_atoms, utf8_functions, extra_chunks,
@@ -104,6 +104,7 @@ file_1(Config) when is_list(Config) ->
compile_and_verify(Simple, Target, []),
compile_and_verify(Simple, Target, [native]),
compile_and_verify(Simple, Target, [debug_info]),
+ compile_and_verify(Simple, Target, [no_postopt]),
{ok,simple} = compile:file(Simple, [no_line_info]), %Coverage
{ok,simple} = compile:file(Simple, [{eprof,beam_z}]), %Coverage
@@ -231,17 +232,6 @@ module_mismatch(Config) when is_list(Config) ->
ok.
-big_file(Config) when is_list(Config) ->
- {Big,Target} = get_files(Config, big, "big_file"),
- ok = file:set_cwd(filename:dirname(Target)),
- compile_and_verify(Big, Target, []),
- compile_and_verify(Big, Target, [debug_info]),
- compile_and_verify(Big, Target, [no_postopt]),
-
- %% Cleanup.
- ok = file:delete(Target),
- ok.
-
%% Tests that the {outdir, Dir} option works.
outdir(Config) when is_list(Config) ->
@@ -370,42 +360,37 @@ do_file_listings(DataDir, PrivDir, [File|Files]) ->
TargetDir = filename:join(PrivDir, listings),
ok = file:make_dir(TargetDir),
- %% Test all dedicated listing options.
- do_listing(Simple, TargetDir, 'S'),
- do_listing(Simple, TargetDir, 'E'),
- do_listing(Simple, TargetDir, 'P'),
- do_listing(Simple, TargetDir, dpp, ".pp"),
- do_listing(Simple, TargetDir, dabstr, ".abstr"),
- do_listing(Simple, TargetDir, dexp, ".expand"),
- do_listing(Simple, TargetDir, dcore, ".core"),
- do_listing(Simple, TargetDir, doldinline, ".oldinline"),
- do_listing(Simple, TargetDir, dinline, ".inline"),
- do_listing(Simple, TargetDir, dcore, ".core"),
- do_listing(Simple, TargetDir, dcopt, ".copt"),
- do_listing(Simple, TargetDir, dcbsm, ".core_bsm"),
- do_listing(Simple, TargetDir, dsetel, ".dsetel"),
- do_listing(Simple, TargetDir, dkern, ".kernel"),
- do_listing(Simple, TargetDir, dssa, ".ssa"),
- do_listing(Simple, TargetDir, dssaopt, ".ssaopt"),
- do_listing(Simple, TargetDir, dprecg, ".precodegen"),
- do_listing(Simple, TargetDir, dcg, ".codegen"),
- do_listing(Simple, TargetDir, dblk, ".block"),
- do_listing(Simple, TargetDir, dexcept, ".except"),
- do_listing(Simple, TargetDir, djmp, ".jump"),
- do_listing(Simple, TargetDir, dclean, ".clean"),
- do_listing(Simple, TargetDir, dpeep, ".peep"),
- do_listing(Simple, TargetDir, dopt, ".optimize"),
- do_listing(Simple, TargetDir, diffable, ".S"),
-
- %% First clean up.
- Listings = filename:join(PrivDir, listings),
- lists:foreach(fun(F) -> ok = file:delete(F) end,
- filelib:wildcard(filename:join(Listings, "*"))),
+ List = [{'S',".S"},
+ {'E',".E"},
+ {'P',".P"},
+ {dpp, ".pp"},
+ {dabstr, ".abstr"},
+ {dexp, ".expand"},
+ {dcore, ".core"},
+ {doldinline, ".oldinline"},
+ {dinline, ".inline"},
+ {dcore, ".core"},
+ {dcopt, ".copt"},
+ {dcbsm, ".core_bsm"},
+ {dkern, ".kernel"},
+ {dssa, ".ssa"},
+ {dssaopt, ".ssaopt"},
+ {dprecg, ".precodegen"},
+ {dcg, ".codegen"},
+ {dblk, ".block"},
+ {dexcept, ".except"},
+ {djmp, ".jump"},
+ {dclean, ".clean"},
+ {dpeep, ".peep"},
+ {dopt, ".optimize"},
+ {diffable, ".S"}],
+ p_listings(List, Simple, TargetDir),
%% Test options that produce a listing file if 'binary' is not given.
do_listing(Simple, TargetDir, to_pp, ".P"),
do_listing(Simple, TargetDir, to_exp, ".E"),
do_listing(Simple, TargetDir, to_core0, ".core"),
+ Listings = filename:join(PrivDir, listings),
ok = file:delete(filename:join(Listings, File ++ ".core")),
do_listing(Simple, TargetDir, to_core, ".core"),
do_listing(Simple, TargetDir, to_kernel, ".kernel"),
@@ -421,24 +406,35 @@ do_file_listings(DataDir, PrivDir, [File|Files]) ->
listings_big(Config) when is_list(Config) ->
{Big,Target} = get_files(Config, big, listings_big),
TargetDir = filename:dirname(Target),
- do_listing(Big, TargetDir, 'S'),
- do_listing(Big, TargetDir, 'E'),
- do_listing(Big, TargetDir, 'P'),
- do_listing(Big, TargetDir, dkern, ".kernel"),
- do_listing(Big, TargetDir, dssa, ".ssa"),
- do_listing(Big, TargetDir, dssaopt, ".ssaopt"),
- do_listing(Big, TargetDir, dprecg, ".precodegen"),
- do_listing(Big, TargetDir, to_dis, ".dis"),
-
- TargetNoext = filename:rootname(Target, code:objfile_extension()),
- {ok,big} = compile:file(TargetNoext, [from_asm,{outdir,TargetDir}]),
-
- %% Cleanup.
- ok = file:delete(Target),
- lists:foreach(fun(F) -> ok = file:delete(F) end,
- filelib:wildcard(filename:join(TargetDir, "*"))),
- ok = file:del_dir(TargetDir),
- ok.
+ List = [{'S',".S"},
+ {'E',".E"},
+ {'P',".P"},
+ {dkern, ".kernel"},
+ {dssa, ".ssa"},
+ {dssaopt, ".ssaopt"},
+ {dprecg, ".precodegen"},
+ {to_dis, ".dis"}],
+ p_listings(List, Big, TargetDir).
+
+p_listings(List, File, BaseDir) ->
+ Run = fun({Option,Extension}) ->
+ Uniq = erlang:unique_integer([positive]),
+ Dir = filename:join(BaseDir, integer_to_list(Uniq)),
+ ok = file:make_dir(Dir),
+ try
+ do_listing(File, Dir, Option, Extension),
+ ok
+ catch
+ Class:Error:Stk ->
+ io:format("~p:~p\n~p\n", [Class,Error,Stk]),
+ error
+ after
+ _ = [ok = file:delete(F) ||
+ F <- filelib:wildcard(filename:join(Dir, "*"))],
+ ok = file:del_dir(Dir)
+ end
+ end,
+ test_lib:p_run(Run, List).
other_output(Config) when is_list(Config) ->
{Simple,_Target} = get_files(Config, simple, "other_output"),
@@ -685,9 +681,6 @@ cover(Config) when is_list(Config) ->
io:format("~p\n", [compile:options()]),
ok.
-do_listing(Source, TargetDir, Type) ->
- do_listing(Source, TargetDir, Type, "." ++ atom_to_list(Type)).
-
do_listing(Source, TargetDir, Type, Ext) ->
io:format("Source: ~p TargetDir: ~p\n Type: ~p Ext: ~p\n",
[Source, TargetDir, Type, Ext]),
diff --git a/lib/compiler/test/compiler.cover b/lib/compiler/test/compiler.cover
index 3fd7fc1937..fac0f9947c 100644
--- a/lib/compiler/test/compiler.cover
+++ b/lib/compiler/test/compiler.cover
@@ -1,5 +1,4 @@
-{incl_app,compiler,details}.
-
%% -*- erlang -*-
+{local_only,compiler,true}.
+{incl_app,compiler,details}.
{excl_mods,compiler,[core_scan,core_parse]}.
-
diff --git a/lib/compiler/test/inline_SUITE.erl b/lib/compiler/test/inline_SUITE.erl
index f700059d20..aff1a56c47 100644
--- a/lib/compiler/test/inline_SUITE.erl
+++ b/lib/compiler/test/inline_SUITE.erl
@@ -344,10 +344,8 @@ otp_7223_2({a}) ->
1.
coverage(Config) when is_list(Config) ->
- Mod = bsdecode,
+ Mod = attribute,
Src = filename:join(proplists:get_value(data_dir, Config), Mod),
{ok,Mod,_} = compile:file(Src, [binary,report,{inline,0},
clint,ssalint]),
- {ok,Mod,_} = compile:file(Src, [binary,report,{inline,20},
- verbose,clint,ssalint]),
ok.
diff --git a/lib/compiler/test/match_SUITE.erl b/lib/compiler/test/match_SUITE.erl
index 60ab969929..94bfbb0efe 100644
--- a/lib/compiler/test/match_SUITE.erl
+++ b/lib/compiler/test/match_SUITE.erl
@@ -25,7 +25,7 @@
match_in_call/1,untuplify/1,shortcut_boolean/1,letify_guard/1,
selectify/1,deselectify/1,underscore/1,match_map/1,map_vars_used/1,
coverage/1,grab_bag/1,literal_binary/1,
- unary_op/1,eq_types/1]).
+ unary_op/1,eq_types/1,match_after_return/1]).
-include_lib("common_test/include/ct.hrl").
@@ -40,7 +40,8 @@ groups() ->
match_in_call,untuplify,
shortcut_boolean,letify_guard,selectify,deselectify,
underscore,match_map,map_vars_used,coverage,
- grab_bag,literal_binary,unary_op,eq_types]}].
+ grab_bag,literal_binary,unary_op,eq_types,
+ match_after_return]}].
init_per_suite(Config) ->
@@ -890,5 +891,15 @@ eq_types(A, B) ->
Ref22.
+match_after_return(Config) when is_list(Config) ->
+ %% The return type of the following call will never match the 'wont_happen'
+ %% clauses below, and the beam_ssa_type was clever enough to see that but
+ %% didn't remove the blocks, so it crashed when trying to extract A.
+ ok = case mar_test_tuple(erlang:unique_integer()) of
+ {gurka, never_matches, A} -> {wont_happen, A};
+ _ -> ok
+ end.
+
+mar_test_tuple(I) -> {gurka, I}.
id(I) -> I.
diff --git a/lib/compiler/test/misc_SUITE.erl b/lib/compiler/test/misc_SUITE.erl
index e999c8ffae..a0b415ceaa 100644
--- a/lib/compiler/test/misc_SUITE.erl
+++ b/lib/compiler/test/misc_SUITE.erl
@@ -161,14 +161,13 @@ md5_1(Beam) ->
%% Cover some code that handles internal errors.
silly_coverage(Config) when is_list(Config) ->
- %% sys_core_fold, sys_core_alias, sys_core_bsm, sys_core_setel, v3_kernel
+ %% sys_core_fold, sys_core_alias, sys_core_bsm, v3_kernel
BadCoreErlang = {c_module,[],
name,[],[],
[{{c_var,[],{foo,2}},seriously_bad_body}]},
expect_error(fun() -> sys_core_fold:module(BadCoreErlang, []) end),
expect_error(fun() -> sys_core_alias:module(BadCoreErlang, []) end),
expect_error(fun() -> sys_core_bsm:module(BadCoreErlang, []) end),
- expect_error(fun() -> sys_core_dsetel:module(BadCoreErlang, []) end),
expect_error(fun() -> v3_kernel:module(BadCoreErlang, []) end),
%% beam_kernel_to_ssa
@@ -185,7 +184,6 @@ silly_coverage(Config) when is_list(Config) ->
%% beam_ssa_recv
%% beam_ssa_share
%% beam_ssa_pre_codegen
- %% beam_ssa_opt
%% beam_ssa_codegen
BadSSA = {b_module,#{},a,b,c,
[{b_function,#{func_info=>{mod,foo,0}},args,bad_blocks,0}]},
@@ -193,9 +191,15 @@ silly_coverage(Config) when is_list(Config) ->
expect_error(fun() -> beam_ssa_recv:module(BadSSA, []) end),
expect_error(fun() -> beam_ssa_share:module(BadSSA, []) end),
expect_error(fun() -> beam_ssa_pre_codegen:module(BadSSA, []) end),
- expect_error(fun() -> beam_ssa_opt:module(BadSSA, []) end),
expect_error(fun() -> beam_ssa_codegen:module(BadSSA, []) end),
+ %% beam_ssa_opt
+ BadSSABlocks = #{0 => {b_blk,#{},[bad_code],{b_ret,#{},arg}}},
+ BadSSAOpt = {b_module,#{},a,[],c,
+ [{b_function,#{func_info=>{mod,foo,0}},[],
+ BadSSABlocks,0}]},
+ expect_error(fun() -> beam_ssa_opt:module(BadSSAOpt, []) end),
+
%% beam_ssa_lint, beam_ssa_pp
{error,[{_,Errors}]} = beam_ssa_lint:module(bad_ssa_lint_input(), []),
_ = [io:put_chars(Mod:format_error(Reason)) ||
diff --git a/lib/compiler/test/test_lib.erl b/lib/compiler/test/test_lib.erl
index 7fb4751b42..39c26c6142 100644
--- a/lib/compiler/test/test_lib.erl
+++ b/lib/compiler/test/test_lib.erl
@@ -50,12 +50,8 @@ smoke_disasm(File) when is_list(File) ->
Res = beam_disasm:file(File),
{beam_file,_Mod} = {element(1, Res),element(2, Res)}.
-%% If we are running cover, we don't want to run test cases that
-%% invokes the compiler in parallel, as doing so would probably
-%% be slower than running them sequentially.
-
parallel() ->
- case test_server:is_cover() orelse erlang:system_info(schedulers) =:= 1 of
+ case erlang:system_info(schedulers) =:= 1 of
true -> [];
false -> [parallel]
end.
@@ -70,21 +66,24 @@ uniq() ->
opt_opts(Mod) ->
Comp = Mod:module_info(compile),
{options,Opts} = lists:keyfind(options, 1, Comp),
- lists:filter(fun(no_copt) -> true;
- (no_postopt) -> true;
- (no_ssa_opt) -> true;
- (no_recv_opt) -> true;
- (no_ssa_float) -> true;
- (no_stack_trimming) -> true;
- (debug_info) -> true;
- (inline) -> true;
- (no_put_tuple2) -> true;
- (no_bsm3) -> true;
- (no_bsm_opt) -> true;
- (no_module_opt) -> true;
- (no_type_opt) -> true;
- (_) -> false
- end, Opts).
+ lists:filter(fun
+ (debug_info) -> true;
+ (inline) -> true;
+ (no_bsm3) -> true;
+ (no_bsm_opt) -> true;
+ (no_copt) -> true;
+ (no_fun_opt) -> true;
+ (no_module_opt) -> true;
+ (no_postopt) -> true;
+ (no_put_tuple2) -> true;
+ (no_recv_opt) -> true;
+ (no_share_opt) -> true;
+ (no_ssa_float) -> true;
+ (no_ssa_opt) -> true;
+ (no_stack_trimming) -> true;
+ (no_type_opt) -> true;
+ (_) -> false
+ end, Opts).
%% Some test suites gets cloned (e.g. to "record_SUITE" to
%% "record_no_opt_SUITE"), but the data directory is not cloned.
@@ -97,7 +96,8 @@ get_data_dir(Config) ->
Data2 = re:replace(Data1, "_post_opt_SUITE", "_SUITE", Opts),
Data3 = re:replace(Data2, "_inline_SUITE", "_SUITE", Opts),
Data4 = re:replace(Data3, "_r21_SUITE", "_SUITE", Opts),
- re:replace(Data4, "_no_module_opt_SUITE", "_SUITE", Opts).
+ Data = re:replace(Data4, "_no_module_opt_SUITE", "_SUITE", Opts),
+ re:replace(Data, "_no_ssa_opt_SUITE", "_SUITE", Opts).
is_cloned_mod(Mod) ->
is_cloned_mod_1(atom_to_list(Mod)).
@@ -105,6 +105,7 @@ is_cloned_mod(Mod) ->
%% Test whether Mod is a cloned module.
is_cloned_mod_1("_no_opt_SUITE") -> true;
+is_cloned_mod_1("_no_ssa_opt_SUITE") -> true;
is_cloned_mod_1("_post_opt_SUITE") -> true;
is_cloned_mod_1("_inline_SUITE") -> true;
is_cloned_mod_1("_21_SUITE") -> true;
@@ -117,18 +118,7 @@ is_cloned_mod_1([]) -> false.
p_run(Test, List) ->
S = erlang:system_info(schedulers),
- N = case test_server:is_cover() of
- false ->
- S + 1;
- true ->
- %% Cover is running. Using too many processes
- %% could slow us down. Measurements on my computer
- %% showed that using 4 parallel processes was
- %% slightly faster than using 3. Using more than
- %% 4 would not buy us much and could actually be
- %% slower.
- min(S, 4)
- end,
+ N = S + 1,
io:format("p_run: ~p parallel processes\n", [N]),
p_run_loop(Test, List, N, [], 0, 0).
diff --git a/lib/compiler/test/warnings_SUITE.erl b/lib/compiler/test/warnings_SUITE.erl
index c5d0bf8420..70b7100451 100644
--- a/lib/compiler/test/warnings_SUITE.erl
+++ b/lib/compiler/test/warnings_SUITE.erl
@@ -240,19 +240,7 @@ guard(Config) when is_list(Config) ->
{4,sys_core_fold,nomatch_guard},
{6,sys_core_fold,no_clause_match},
{6,sys_core_fold,nomatch_guard},
- {6,sys_core_fold,{eval_failure,badarg}},
- {8,sys_core_fold,no_clause_match},
- {8,sys_core_fold,nomatch_guard},
- {8,sys_core_fold,{eval_failure,badarg}},
- {9,sys_core_fold,no_clause_match},
- {9,sys_core_fold,nomatch_guard},
- {9,sys_core_fold,{eval_failure,badarg}},
- {10,sys_core_fold,no_clause_match},
- {10,sys_core_fold,nomatch_guard},
- {10,sys_core_fold,{eval_failure,badarg}},
- {11,sys_core_fold,no_clause_match},
- {11,sys_core_fold,nomatch_guard},
- {11,sys_core_fold,{eval_failure,badarg}}
+ {6,sys_core_fold,{eval_failure,badarg}}
]}}],
[] = run(Config, Ts),
diff --git a/lib/crypto/c_src/algorithms.c b/lib/crypto/c_src/algorithms.c
index 6318c8ad5a..a5bf248ea0 100644
--- a/lib/crypto/c_src/algorithms.c
+++ b/lib/crypto/c_src/algorithms.c
@@ -21,7 +21,7 @@
#include "algorithms.h"
static unsigned int algo_hash_cnt, algo_hash_fips_cnt;
-static ERL_NIF_TERM algo_hash[12]; /* increase when extending the list */
+static ERL_NIF_TERM algo_hash[14]; /* increase when extending the list */
static unsigned int algo_pubkey_cnt, algo_pubkey_fips_cnt;
static ERL_NIF_TERM algo_pubkey[12]; /* increase when extending the list */
static unsigned int algo_cipher_cnt, algo_cipher_fips_cnt;
@@ -62,6 +62,11 @@ void init_algorithms_types(ErlNifEnv* env)
#ifdef HAVE_SHA3_512
algo_hash[algo_hash_cnt++] = enif_make_atom(env, "sha3_512");
#endif
+#ifdef HAVE_BLAKE2
+ algo_hash[algo_hash_cnt++] = enif_make_atom(env, "blake2b");
+ algo_hash[algo_hash_cnt++] = enif_make_atom(env, "blake2s");
+#endif
+
// Non-validated algorithms follow
algo_hash_fips_cnt = algo_hash_cnt;
algo_hash[algo_hash_cnt++] = enif_make_atom(env, "md4");
@@ -136,7 +141,7 @@ void init_algorithms_types(ErlNifEnv* env)
#if defined(HAVE_CHACHA20)
algo_cipher[algo_cipher_cnt++] = enif_make_atom(env,"chacha20");
#endif
-
+
// Validated algorithms first
algo_mac_cnt = 0;
algo_mac[algo_mac_cnt++] = enif_make_atom(env,"hmac");
diff --git a/lib/crypto/c_src/atoms.c b/lib/crypto/c_src/atoms.c
index 3a028b9a67..5f19327197 100644
--- a/lib/crypto/c_src/atoms.c
+++ b/lib/crypto/c_src/atoms.c
@@ -110,6 +110,11 @@ ERL_NIF_TERM atom_sha3_512;
ERL_NIF_TERM atom_md5;
ERL_NIF_TERM atom_ripemd160;
+#ifdef HAVE_BLAKE2
+ERL_NIF_TERM atom_blake2b;
+ERL_NIF_TERM atom_blake2s;
+#endif
+
#ifdef HAS_ENGINE_SUPPORT
ERL_NIF_TERM atom_bad_engine_method;
ERL_NIF_TERM atom_bad_engine_id;
@@ -239,6 +244,10 @@ int init_atoms(ErlNifEnv *env, const ERL_NIF_TERM fips_mode, const ERL_NIF_TERM
atom_sha3_512 = enif_make_atom(env,"sha3_512");
atom_md5 = enif_make_atom(env,"md5");
atom_ripemd160 = enif_make_atom(env,"ripemd160");
+#ifdef HAVE_BLAKE2
+ atom_blake2b = enif_make_atom(env,"blake2b");
+ atom_blake2s = enif_make_atom(env,"blake2s");
+#endif
#ifdef HAS_ENGINE_SUPPORT
atom_bad_engine_method = enif_make_atom(env,"bad_engine_method");
diff --git a/lib/crypto/c_src/atoms.h b/lib/crypto/c_src/atoms.h
index 9ddf0131ac..32f5ec856c 100644
--- a/lib/crypto/c_src/atoms.h
+++ b/lib/crypto/c_src/atoms.h
@@ -113,6 +113,10 @@ extern ERL_NIF_TERM atom_sha3_384;
extern ERL_NIF_TERM atom_sha3_512;
extern ERL_NIF_TERM atom_md5;
extern ERL_NIF_TERM atom_ripemd160;
+#ifdef HAVE_BLAKE2
+extern ERL_NIF_TERM atom_blake2b;
+extern ERL_NIF_TERM atom_blake2s;
+#endif
#ifdef HAS_ENGINE_SUPPORT
extern ERL_NIF_TERM atom_bad_engine_method;
diff --git a/lib/crypto/c_src/digest.c b/lib/crypto/c_src/digest.c
index 00ba65bf54..fec286c000 100644
--- a/lib/crypto/c_src/digest.c
+++ b/lib/crypto/c_src/digest.c
@@ -82,6 +82,20 @@ static struct digest_type_t digest_types[] =
{NULL}
#endif
},
+ {{"blake2b"},
+#ifdef HAVE_BLAKE2
+ {&EVP_blake2b512}
+#else
+ {NULL}
+#endif
+ },
+ {{"blake2s"},
+#ifdef HAVE_BLAKE2
+ {&EVP_blake2s256}
+#else
+ {NULL}
+#endif
+ },
{{NULL}, {NULL}}
};
diff --git a/lib/crypto/c_src/openssl_config.h b/lib/crypto/c_src/openssl_config.h
index c0ce1a59fe..45144a0c25 100644
--- a/lib/crypto/c_src/openssl_config.h
+++ b/lib/crypto/c_src/openssl_config.h
@@ -158,6 +158,13 @@
# define HAVE_SHA3_512
# endif
+// BLAKE2:
+#if OPENSSL_VERSION_NUMBER >= PACKED_OPENSSL_VERSION_PLAIN(1,1,1) \
+ && !defined(HAS_LIBRESSL) \
+ && !defined(OPENSSL_NO_BLAKE2)
+# define HAVE_BLAKE2
+#endif
+
#if OPENSSL_VERSION_NUMBER >= PACKED_OPENSSL_VERSION(0,9,8,'o') \
&& !defined(OPENSSL_NO_EC) \
&& !defined(OPENSSL_NO_ECDH) \
diff --git a/lib/crypto/doc/src/crypto.xml b/lib/crypto/doc/src/crypto.xml
index 3306fe3d16..e0794a080e 100644
--- a/lib/crypto/doc/src/crypto.xml
+++ b/lib/crypto/doc/src/crypto.xml
@@ -44,6 +44,10 @@
SHA-3 Standard: Permutation-Based Hash and Extendable-Output Functions [FIPS PUB 202]
</url>
</item>
+ <tag>BLAKE2</tag>
+ <item>
+ <url href="https://blake2.net/">BLAKE2 — fast secure hashing</url>
+ </item>
<tag>MD5</tag>
<item>
<url href="http://www.ietf.org/rfc/rfc1321.txt">The MD5 Message Digest Algorithm [RFC 1321]</url>
@@ -235,6 +239,7 @@
<name name="sha1"/>
<name name="sha2"/>
<name name="sha3"/>
+ <name name="blake2"/>
<desc>
</desc>
</datatype>
diff --git a/lib/crypto/doc/src/engine_keys.xml b/lib/crypto/doc/src/engine_keys.xml
index b28606fb4e..f78bb81bba 100644
--- a/lib/crypto/doc/src/engine_keys.xml
+++ b/lib/crypto/doc/src/engine_keys.xml
@@ -51,7 +51,7 @@
<p>
OTP/Crypto requires that the user provides two or three items of information about the key. The application used
by the user is usually on a higher level, for example in
- <seealso marker="ssl:ssl#key_option_def">SSL</seealso>. If using
+ <seealso marker="ssl:ssl#type-key">SSL</seealso>. If using
the crypto application directly, it is required that:
</p>
<list>
diff --git a/lib/crypto/src/crypto.erl b/lib/crypto/src/crypto.erl
index 987bc3fe0f..de8cfac9a2 100644
--- a/lib/crypto/src/crypto.erl
+++ b/lib/crypto/src/crypto.erl
@@ -287,6 +287,7 @@
-type sha1() :: sha .
-type sha2() :: sha224 | sha256 | sha384 | sha512 .
-type sha3() :: sha3_224 | sha3_256 | sha3_384 | sha3_512 .
+-type blake2() :: blake2b | blake2s .
-type compatibility_only_hash() :: md5 | md4 .
@@ -329,11 +330,11 @@ stop() ->
| {macs, Macs}
| {curves, Curves}
| {rsa_opts, RSAopts},
- Hashs :: [sha1() | sha2() | sha3() | ripemd160 | compatibility_only_hash()],
+ Hashs :: [sha1() | sha2() | sha3() | blake2() | ripemd160 | compatibility_only_hash()],
Ciphers :: [stream_cipher()
| block_cipher_with_iv() | block_cipher_without_iv()
| aead_cipher()
- ],
+ ],
PKs :: [rsa | dss | ecdsa | dh | ecdh | ec_gf2m],
Macs :: [hmac | cmac | poly1305],
Curves :: [ec_named_curve() | edwards_curve_dh() | edwards_curve_ed()],
@@ -367,7 +368,7 @@ enable_fips_mode(_) -> ?nif_stub.
%%%
%%%================================================================
--define(HASH_HASH_ALGORITHM, sha1() | sha2() | sha3() | ripemd160 | compatibility_only_hash() ).
+-define(HASH_HASH_ALGORITHM, sha1() | sha2() | sha3() | blake2() | ripemd160 | compatibility_only_hash() ).
-spec hash(Type, Data) -> Digest when Type :: ?HASH_HASH_ALGORITHM,
Data :: iodata(),
diff --git a/lib/crypto/test/crypto_SUITE.erl b/lib/crypto/test/crypto_SUITE.erl
index 003e0c58b1..c4323de83f 100644
--- a/lib/crypto/test/crypto_SUITE.erl
+++ b/lib/crypto/test/crypto_SUITE.erl
@@ -56,6 +56,8 @@ groups() ->
{group, sha3_256},
{group, sha3_384},
{group, sha3_512},
+ {group, blake2b},
+ {group, blake2s},
{group, rsa},
{group, dss},
{group, ecdsa},
@@ -137,6 +139,8 @@ groups() ->
{sha3_256, [], [hash, hmac]},
{sha3_384, [], [hash, hmac]},
{sha3_512, [], [hash, hmac]},
+ {blake2b, [], [hash, hmac]},
+ {blake2s, [], [hash, hmac]},
{rsa, [], [sign_verify,
public_encrypt,
private_encrypt,
@@ -587,7 +591,7 @@ use_all_elliptic_curves(_Config) ->
{C,E}
end}
|| Curve <- Curves -- [ed25519, ed448, x25519, x448, ipsec3, ipsec4],
- Hash <- Hashs -- [md4, md5, ripemd160, sha3_224, sha3_256, sha3_384, sha3_512]
+ Hash <- Hashs -- [md4, md5, ripemd160, sha3_224, sha3_256, sha3_384, sha3_512, blake2b, blake2s]
],
Fails =
lists:filter(fun({_,true}) -> false;
@@ -1438,6 +1442,12 @@ group_config(sha3_384 = Type, Config) ->
group_config(sha3_512 = Type, Config) ->
{Msgs,Digests} = sha3_test_vectors(Type),
[{hash, {Type, Msgs, Digests}}, {hmac, hmac_sha3(Type)} | Config];
+group_config(blake2b = Type, Config) ->
+ {Msgs, Digests} = blake2_test_vectors(Type),
+ [{hash, {Type, Msgs, Digests}}, {hmac, blake2_hmac(Type)} | Config];
+group_config(blake2s = Type, Config) ->
+ {Msgs, Digests} = blake2_test_vectors(Type),
+ [{hash, {Type, Msgs, Digests}}, {hmac, blake2_hmac(Type)} | Config];
group_config(rsa, Config) ->
Msg = rsa_plain(),
Public = rsa_public(),
@@ -1704,6 +1714,71 @@ rfc_1321_md5_digests() ->
hexstr2bin("d174ab98d277d9f5a5611c2c9f419d9f"),
hexstr2bin("57edf4a22be3c955ac49da2e2107b67a")].
+
+%% BLAKE2 re-use SHA3 test vectors.
+blake2_test_vectors(blake2b) ->
+ {sha3_msgs(),
+ [ <<186,128,165,63,152,28,77,13,106,39,151,182,159,18,246,233,76,33,47,20,104,90,196,183,75,18,187,111,219,255,162,209,125,135,197,57,42,171,121,45,194,82,213,222,69,51,204,149,24,211,138,168,219,241,146,90,185,35,134,237,212,0,153,35>>
+ , <<120,106,2,247,66,1,89,3,198,198,253,133,37,82,210,114,145,47,71,64,225,88,71,97,138,134,226,23,247,31,84,25,210,94,16,49,175,238,88,83,19,137,100,68,147,78,176,75,144,58,104,91,20,72,183,85,213,111,112,26,254,155,226,206>>
+ , <<114,133,255,62,139,215,104,214,155,230,43,59,241,135,101,163,37,145,127,169,116,74,194,245,130,162,8,80,188,43,17,65,237,27,62,69,40,89,90,204,144,119,43,223,45,55,220,138,71,19,11,68,243,58,2,232,115,14,90,216,225,102,232,136>>
+ , <<206,116,26,197,147,15,227,70,129,17,117,197,34,123,183,191,205,71,244,38,18,250,228,108,8,9,81,79,158,14,58,17,238,23,115,40,113,71,205,234,238,223,245,7,9,170,113,99,65,254,101,36,15,74,214,119,125,107,250,249,114,110,94,82>>
+ , <<152,251,62,251,114,6,253,25,235,246,155,111,49,44,247,182,78,59,148,219,225,161,113,7,145,57,117,167,147,241,119,225,208,119,96,157,127,186,54,60,187,160,13,5,247,170,78,79,168,113,93,100,40,16,76,10,117,100,59,15,243,253,62,175>>
+ ]};
+blake2_test_vectors(blake2s) ->
+ {sha3_msgs(),
+ [ <<80,140,94,140,50,124,20,226,225,167,43,163,78,235,69,47,55,69,139,32,158,214,58,41,77,153,155,76,134,103,89,130>>
+ , <<105,33,122,48,121,144,128,148,225,17,33,208,66,53,74,124,31,85,182,72,44,161,165,30,27,37,13,253,30,208,238,249>>
+ , <<111,77,245,17,106,111,51,46,218,177,217,225,14,232,125,246,85,123,234,182,37,157,118,99,243,188,213,114,44,19,241,137>>
+ , <<53,141,210,237,7,128,212,5,78,118,203,111,58,91,206,40,65,232,226,245,71,67,29,77,9,219,33,182,109,148,31,199>>
+ , <<190,192,192,230,205,229,182,122,203,115,184,31,121,166,122,64,121,174,28,96,218,201,210,102,26,241,142,159,139,80,223,165>>
+ ]}.
+
+blake2_hmac(Type) ->
+ {Ks, Ds, Hs} = lists:unzip3(
+ [ {hexstr2bin(K), hexstr2bin(D), H}
+ || {{K, D}, H} <- lists:zip(blake2_hmac_key_data(), blake2_hmac_hmac(Type)) ]),
+ {Type, Ks, Ds, Hs}.
+
+blake2_hmac_key_data() ->
+ [ {"0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b 0b0b0b0b",
+ "4869205468657265"}
+ , {"4a656665",
+ "7768617420646f2079612077616e7420 666f72206e6f7468696e673f"}
+ , {"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaa",
+ "dddddddddddddddddddddddddddddddd dddddddddddddddddddddddddddddddd dddddddddddddddddddddddddddddddd dddd"}
+ , {"0102030405060708090a0b0c0d0e0f10 111213141516171819",
+ "cdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd cdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd cdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd cdcd"}
+ , {"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaa",
+ "54657374205573696e67204c61726765 72205468616e20426c6f636b2d53697a 65204b6579202d2048617368204b6579 204669727374"}
+ , {"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaa",
+ "54657374205573696e67204c61726765 72205468616e20426c6f636b2d53697a 65204b6579202d2048617368204b6579 204669727374"}
+ , {"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaa",
+ "54686973206973206120746573742075 73696e672061206c6172676572207468 616e20626c6f636b2d73697a65206b65 7920616e642061206c61726765722074 68616e20626c6f636b2d73697a652064 6174612e20546865206b6579206e6565 647320746f2062652068617368656420 6265666f7265206265696e6720757365 642062792074686520484d414320616c 676f726974686d2e"}
+ , {"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaa",
+ "54686973206973206120746573742075 73696e672061206c6172676572207468 616e20626c6f636b2d73697a65206b65 7920616e642061206c61726765722074 68616e20626c6f636b2d73697a652064 6174612e20546865206b6579206e6565 647320746f2062652068617368656420 6265666f7265206265696e6720757365 642062792074686520484d414320616c 676f726974686d2e"}
+ ].
+
+blake2_hmac_hmac(blake2b) ->
+ [ <<53,138,106,24,73,36,137,79,195,75,238,86,128,238,223,87,216,74,55,187,56,131,47,40,142,59,39,220,99,169,140,200,201,30,118,218,71,107,80,139,198,178,212,8,162,72,133,116,82,144,110,74,32,180,140,107,75,85,210,223,15,225,221,36>>
+ , <<111,248,132,248,221,194,166,88,107,60,152,164,205,110,189,241,78,193,2,4,182,113,0,115,235,88,101,173,227,122,38,67,184,128,124,19,53,209,7,236,219,159,254,174,182,130,140,70,37,186,23,44,102,55,158,252,210,34,194,222,17,114,122,180>>
+ , <<244,59,198,44,122,153,53,60,59,44,96,232,239,36,251,189,66,233,84,120,102,220,156,91,228,237,198,244,167,212,188,10,198,32,194,198,0,52,208,64,240,219,175,134,249,233,205,120,145,160,149,89,94,237,85,226,169,150,33,95,12,21,192,24>>
+ , <<229,219,182,222,47,238,66,161,202,160,110,78,123,132,206,64,143,250,92,74,157,226,99,46,202,118,156,222,136,117,1,76,114,208,114,15,234,245,63,118,230,161,128,53,127,82,141,123,244,132,250,58,20,232,204,31,15,59,173,167,23,180,52,145>>
+ , <<165,75,41,67,178,162,2,39,212,28,164,108,9,69,175,9,188,31,174,251,47,73,137,76,35,174,188,85,127,183,156,72,137,220,167,68,8,220,134,80,134,102,122,237,238,74,49,133,197,58,73,200,11,129,76,76,88,19,234,12,139,56,168,248>>
+ , <<180,214,140,139,182,82,151,170,52,132,168,110,29,51,183,138,70,159,33,234,170,158,212,218,159,236,145,218,71,23,34,61,44,15,163,134,170,47,209,241,255,207,89,23,178,103,84,96,53,237,48,238,164,178,19,162,133,148,211,211,169,179,140,170>>
+ , <<171,52,121,128,166,75,94,130,93,209,14,125,50,253,67,160,26,142,109,234,38,122,185,173,125,145,53,36,82,102,24,146,83,17,175,188,176,196,149,25,203,235,221,112,149,64,168,215,37,251,145,26,194,174,233,178,163,170,67,215,150,18,51,147>>
+ , <<97,220,242,140,166,12,169,92,130,89,147,39,171,215,169,161,152,111,242,219,211,199,73,69,198,227,35,186,203,76,159,26,94,103,82,93,20,186,141,98,36,177,98,229,102,23,21,37,83,3,69,169,178,86,8,178,125,251,163,180,146,115,213,6>>
+ ];
+blake2_hmac_hmac(blake2s) ->
+ [ <<101,168,183,197,204,145,54,212,36,232,44,55,226,112,126,116,233,19,192,101,91,153,199,95,64,237,243,135,69,58,50,96>>
+ , <<144,182,40,30,47,48,56,201,5,106,240,180,167,231,99,202,230,254,93,158,180,56,106,14,201,82,55,137,12,16,79,240>>
+ , <<252,196,245,149,41,80,46,52,195,216,218,63,253,171,130,150,106,44,182,55,255,94,155,215,1,19,92,46,148,105,231,144>>
+ , <<70,68,52,220,190,206,9,93,69,106,29,98,214,236,86,248,152,230,37,163,158,92,82,189,249,77,175,17,27,173,131,170>>
+ , <<210,61,121,57,79,83,213,54,160,150,230,81,68,71,238,170,187,5,222,208,27,227,44,25,55,218,106,143,113,3,188,78>>
+ , <<92,76,83,46,110,69,89,83,133,78,21,16,149,38,110,224,127,213,88,129,190,223,139,57,8,217,95,13,190,54,159,234>>
+ , <<203,96,246,167,145,241,64,191,138,162,229,31,243,88,205,178,204,92,3,51,4,91,127,183,122,186,122,179,176,207,178,55>>
+ , <<190,53,233,217,99,171,215,108,1,184,171,181,22,36,240,209,16,96,16,92,213,22,16,58,114,241,117,214,211,189,30,202>>
+ ].
+
%%% https://www.di-mgt.com.au/sha_testvectors.html
sha3_msgs() ->
["abc",
diff --git a/lib/crypto/test/engine_SUITE.erl b/lib/crypto/test/engine_SUITE.erl
index 2a89fa71a3..3416fbd78d 100644
--- a/lib/crypto/test/engine_SUITE.erl
+++ b/lib/crypto/test/engine_SUITE.erl
@@ -345,13 +345,13 @@ engine_list(Config) when is_list(Config) ->
{skip, "OTP Test engine not found"};
{ok, Engine} ->
try
- EngineList0 = crypto:engine_list(),
case crypto:engine_load(<<"dynamic">>,
[{<<"SO_PATH">>, Engine},
<<"LOAD">>],
[]) of
{ok, E} ->
EngineList0 = crypto:engine_list(),
+ false = lists:member(<<"MD5">>, EngineList0),
ok = crypto:engine_add(E),
[<<"MD5">>] = lists:subtract(crypto:engine_list(), EngineList0),
ok = crypto:engine_remove(E),
diff --git a/lib/debugger/test/int_eval_SUITE.erl b/lib/debugger/test/int_eval_SUITE.erl
index 0542e45142..324a44bad8 100644
--- a/lib/debugger/test/int_eval_SUITE.erl
+++ b/lib/debugger/test/int_eval_SUITE.erl
@@ -285,7 +285,10 @@ do_eval(Config, Mod) ->
DataDir = proplists:get_value(data_dir, Config),
ok = file:set_cwd(DataDir),
- {ok,Mod} = compile:file(Mod, [report,debug_info]),
+ %% Turn off type-based optimizations across function calls, as it
+ %% would turn many body-recursive calls into tail-recursive calls,
+ %% which would change the stacktrace.
+ {ok,Mod} = compile:file(Mod, [no_module_opt,report,debug_info]),
{module,Mod} = code:load_file(Mod),
CompiledRes = Mod:Mod(),
ok = io:format("Compiled:\n~p", [CompiledRes]),
diff --git a/lib/dialyzer/src/dialyzer_codeserver.erl b/lib/dialyzer/src/dialyzer_codeserver.erl
index 5587cf2bdf..c4e3c322e5 100644
--- a/lib/dialyzer/src/dialyzer_codeserver.erl
+++ b/lib/dialyzer/src/dialyzer_codeserver.erl
@@ -347,13 +347,11 @@ get_file_contract(Key, ContDict) ->
lookup_mfa_contract(MFA, #codeserver{contracts = ContDict}) ->
ets_dict_find(MFA, ContDict).
--spec lookup_meta_info(module() | mfa(), codeserver()) -> meta_info().
+-spec lookup_meta_info(module() | mfa(), codeserver()) ->
+ {'ok', meta_info()} | 'error'.
lookup_meta_info(MorMFA, #codeserver{fun_meta_info = FunMetaInfo}) ->
- case ets_dict_find(MorMFA, FunMetaInfo) of
- error -> [];
- {ok, PropList} -> PropList
- end.
+ ets_dict_find(MorMFA, FunMetaInfo).
-spec get_contracts(codeserver()) ->
dict:dict(mfa(), dialyzer_contracts:file_contract()).
diff --git a/lib/dialyzer/src/dialyzer_contracts.erl b/lib/dialyzer/src/dialyzer_contracts.erl
index af7f4385ad..9c36d745c3 100644
--- a/lib/dialyzer/src/dialyzer_contracts.erl
+++ b/lib/dialyzer/src/dialyzer_contracts.erl
@@ -25,7 +25,7 @@
%% get_contract_signature/1,
is_overloaded/1,
process_contract_remote_types/1,
- store_tmp_contract/5]).
+ store_tmp_contract/6]).
-export_type([file_contract/0, plt_contracts/0]).
@@ -146,18 +146,18 @@ process_contract_remote_types(CodeServer) ->
Mods = dialyzer_codeserver:all_temp_modules(CodeServer),
RecordTable = dialyzer_codeserver:get_records_table(CodeServer),
ExpTypes = dialyzer_codeserver:get_exported_types(CodeServer),
- ContractFun =
- fun({{_M, _F, _A}=MFA, {File, TmpContract, Xtra}}, C0) ->
- #tmp_contract{contract_funs = CFuns, forms = Forms} = TmpContract,
- {NewCs, C2} = lists:mapfoldl(fun(CFun, C1) ->
- CFun(ExpTypes, RecordTable, C1)
- end, C0, CFuns),
- Args = general_domain(NewCs),
- Contract = #contract{contracts = NewCs, args = Args, forms = Forms},
- {{MFA, {File, Contract, Xtra}}, C2}
- end,
ModuleFun =
fun(ModuleName) ->
+ ContractFun =
+ fun({MFA, {File, TmpContract, Xtra}}, C0) ->
+ #tmp_contract{contract_funs = CFuns, forms = Forms} = TmpContract,
+ {NewCs, C2} = lists:mapfoldl(fun(CFun, C1) ->
+ CFun(ExpTypes, RecordTable, C1)
+ end, C0, CFuns),
+ Args = general_domain(NewCs),
+ Contract = #contract{contracts = NewCs, args = Args, forms = Forms},
+ {{MFA, {File, Contract, Xtra}}, C2}
+ end,
Cache = erl_types:cache__new(),
{ContractMap, CallbackMap} =
dialyzer_codeserver:get_temp_contracts(ModuleName, CodeServer),
@@ -474,26 +474,29 @@ insert_constraints([], Map) -> Map.
-type spec_data() :: {TypeSpec :: [_], Xtra:: [_]}.
--spec store_tmp_contract(mfa(), file_line(), spec_data(), contracts(), types()) ->
- contracts().
+-spec store_tmp_contract(module(), mfa(), file_line(), spec_data(),
+ contracts(), types()) -> contracts().
-store_tmp_contract(MFA, FileLine, {TypeSpec, Xtra}, SpecMap, RecordsDict) ->
+store_tmp_contract(Module, MFA, FileLine, {TypeSpec, Xtra}, SpecMap,
+ RecordsDict) ->
%% io:format("contract from form: ~tp\n", [TypeSpec]),
- TmpContract = contract_from_form(TypeSpec, MFA, RecordsDict, FileLine),
+ TmpContract = contract_from_form(TypeSpec, Module, MFA, RecordsDict, FileLine),
%% io:format("contract: ~tp\n", [TmpContract]),
maps:put(MFA, {FileLine, TmpContract, Xtra}, SpecMap).
-contract_from_form(Forms, MFA, RecDict, FileLine) ->
- {CFuns, Forms1} = contract_from_form(Forms, MFA, RecDict, FileLine, [], []),
+contract_from_form(Forms, Module, MFA, RecDict, FileLine) ->
+ {CFuns, Forms1} =
+ contract_from_form(Forms, Module, MFA, RecDict, FileLine, [], []),
#tmp_contract{contract_funs = CFuns, forms = Forms1}.
-contract_from_form([{type, _, 'fun', [_, _]} = Form | Left], MFA, RecDict,
- FileLine, TypeAcc, FormAcc) ->
+contract_from_form([{type, _, 'fun', [_, _]} = Form | Left], Module, MFA,
+ RecDict, FileLine, TypeAcc, FormAcc) ->
TypeFun =
fun(ExpTypes, RecordTable, Cache) ->
{NewType, NewCache} =
try
- from_form_with_check(Form, ExpTypes, MFA, RecordTable, Cache)
+ from_form_with_check(Form, ExpTypes, Module, MFA, RecordTable,
+ Cache)
catch
throw:{error, Msg} ->
{File, Line} = FileLine,
@@ -506,68 +509,74 @@ contract_from_form([{type, _, 'fun', [_, _]} = Form | Left], MFA, RecDict,
end,
NewTypeAcc = [TypeFun | TypeAcc],
NewFormAcc = [{Form, []} | FormAcc],
- contract_from_form(Left, MFA, RecDict, FileLine, NewTypeAcc, NewFormAcc);
+ contract_from_form(Left, Module, MFA, RecDict, FileLine, NewTypeAcc,
+ NewFormAcc);
contract_from_form([{type, _L1, bounded_fun,
[{type, _L2, 'fun', [_, _]} = Form, Constr]}| Left],
- MFA, RecDict, FileLine, TypeAcc, FormAcc) ->
+ Module, MFA, RecDict, FileLine, TypeAcc, FormAcc) ->
TypeFun =
fun(ExpTypes, RecordTable, Cache) ->
{Constr1, VarTable, Cache1} =
- process_constraints(Constr, MFA, RecDict, ExpTypes, RecordTable,
- Cache),
+ process_constraints(Constr, Module, MFA, RecDict, ExpTypes,
+ RecordTable, Cache),
{NewType, NewCache} =
- from_form_with_check(Form, ExpTypes, MFA, RecordTable,
+ from_form_with_check(Form, ExpTypes, Module, MFA, RecordTable,
VarTable, Cache1),
NewTypeNoVars = erl_types:subst_all_vars_to_any(NewType),
{{NewTypeNoVars, Constr1}, NewCache}
end,
NewTypeAcc = [TypeFun | TypeAcc],
NewFormAcc = [{Form, Constr} | FormAcc],
- contract_from_form(Left, MFA, RecDict, FileLine, NewTypeAcc, NewFormAcc);
-contract_from_form([], _MFA, _RecDict, _FileLine, TypeAcc, FormAcc) ->
+ contract_from_form(Left, Module, MFA, RecDict, FileLine, NewTypeAcc,
+ NewFormAcc);
+contract_from_form([], _Mod, _MFA, _RecDict, _FileLine, TypeAcc, FormAcc) ->
{lists:reverse(TypeAcc), lists:reverse(FormAcc)}.
-process_constraints(Constrs, MFA, RecDict, ExpTypes, RecordTable, Cache) ->
- {Init0, NewCache} = initialize_constraints(Constrs, MFA, RecDict, ExpTypes,
- RecordTable, Cache),
+process_constraints(Constrs, Module, MFA, RecDict, ExpTypes, RecordTable,
+ Cache) ->
+ {Init0, NewCache} = initialize_constraints(Constrs, Module, MFA, RecDict,
+ ExpTypes, RecordTable, Cache),
Init = remove_cycles(Init0),
- constraints_fixpoint(Init, MFA, RecDict, ExpTypes, RecordTable, NewCache).
+ constraints_fixpoint(Init, Module, MFA, RecDict, ExpTypes, RecordTable,
+ NewCache).
-initialize_constraints(Constrs, MFA, RecDict, ExpTypes, RecordTable, Cache) ->
- initialize_constraints(Constrs, MFA, RecDict, ExpTypes, RecordTable,
+initialize_constraints(Constrs, Module, MFA, RecDict, ExpTypes, RecordTable,
+ Cache) ->
+ initialize_constraints(Constrs, Module, MFA, RecDict, ExpTypes, RecordTable,
Cache, []).
-initialize_constraints([], _MFA, _RecDict, _ExpTypes, _RecordTable,
+initialize_constraints([], _Module, _MFA, _RecDict, _ExpTypes, _RecordTable,
Cache, Acc) ->
{Acc, Cache};
-initialize_constraints([Constr|Rest], MFA, RecDict, ExpTypes, RecordTable,
- Cache, Acc) ->
+initialize_constraints([Constr|Rest], Module, MFA, RecDict, ExpTypes,
+ RecordTable, Cache, Acc) ->
case Constr of
{type, _, constraint, [{atom, _, is_subtype}, [Type1, Type2]]} ->
VarTable = erl_types:var_table__new(),
{T1, NewCache} =
- final_form(Type1, ExpTypes, MFA, RecordTable, VarTable, Cache),
+ final_form(Type1, ExpTypes, Module, MFA, RecordTable, VarTable, Cache),
Entry = {T1, Type2},
- initialize_constraints(Rest, MFA, RecDict, ExpTypes, RecordTable,
- NewCache, [Entry|Acc]);
+ initialize_constraints(Rest, Module, MFA, RecDict, ExpTypes,
+ RecordTable, NewCache, [Entry|Acc]);
{type, _, constraint, [{atom,_,Name}, List]} ->
N = length(List),
throw({error,
io_lib:format("Unsupported type guard ~tw/~w\n", [Name, N])})
end.
-constraints_fixpoint(Constrs, MFA, RecDict, ExpTypes, RecordTable, Cache) ->
+constraints_fixpoint(Constrs, Module, MFA, RecDict, ExpTypes, RecordTable,
+ Cache) ->
VarTable = erl_types:var_table__new(),
{VarTab, NewCache} =
- constraints_to_dict(Constrs, MFA, RecDict, ExpTypes, RecordTable,
+ constraints_to_dict(Constrs, Module, MFA, RecDict, ExpTypes, RecordTable,
VarTable, Cache),
- constraints_fixpoint(VarTab, MFA, Constrs, RecDict, ExpTypes,
+ constraints_fixpoint(VarTab, Module, MFA, Constrs, RecDict, ExpTypes,
RecordTable, NewCache).
-constraints_fixpoint(OldVarTab, MFA, Constrs, RecDict, ExpTypes,
+constraints_fixpoint(OldVarTab, Module, MFA, Constrs, RecDict, ExpTypes,
RecordTable, Cache) ->
{NewVarTab, NewCache} =
- constraints_to_dict(Constrs, MFA, RecDict, ExpTypes, RecordTable,
+ constraints_to_dict(Constrs, Module, MFA, RecDict, ExpTypes, RecordTable,
OldVarTab, Cache),
case NewVarTab of
OldVarTab ->
@@ -578,19 +587,23 @@ constraints_fixpoint(OldVarTab, MFA, Constrs, RecDict, ExpTypes,
FinalConstrs = maps:fold(Fun, [], NewVarTab),
{FinalConstrs, NewVarTab, NewCache};
_Other ->
- constraints_fixpoint(NewVarTab, MFA, Constrs, RecDict, ExpTypes,
+ constraints_fixpoint(NewVarTab, Module, MFA, Constrs, RecDict, ExpTypes,
RecordTable, NewCache)
end.
-final_form(Form, ExpTypes, MFA, RecordTable, VarTable, Cache) ->
- from_form_with_check(Form, ExpTypes, MFA, RecordTable, VarTable, Cache).
+final_form(Form, ExpTypes, Module, MFA, RecordTable, VarTable, Cache) ->
+ from_form_with_check(Form, ExpTypes, Module, MFA, RecordTable, VarTable,
+ Cache).
-from_form_with_check(Form, ExpTypes, MFA, RecordTable, Cache) ->
+from_form_with_check(Form, ExpTypes, Module, MFA, RecordTable, Cache) ->
VarTable = erl_types:var_table__new(),
- from_form_with_check(Form, ExpTypes, MFA, RecordTable, VarTable, Cache).
+ from_form_with_check(Form, ExpTypes, Module, MFA, RecordTable, VarTable,
+ Cache).
-from_form_with_check(Form, ExpTypes, MFA, RecordTable, VarTable, Cache) ->
- Site = {spec, MFA},
+from_form_with_check(Form, ExpTypes, Module, MFA, RecordTable, VarTable,
+ Cache) ->
+ {_, F, A} = MFA,
+ Site = {spec, {Module, F, A}},
C1 = erl_types:t_check_record_fields(Form, ExpTypes, Site, RecordTable,
VarTable, Cache),
%% The check costs some time, and with the assumption that contracts
@@ -598,22 +611,22 @@ from_form_with_check(Form, ExpTypes, MFA, RecordTable, VarTable, Cache) ->
%% erl_types:t_from_form_check_remote(Form, ExpTypes, MFA, RecordTable),
erl_types:t_from_form(Form, ExpTypes, Site, RecordTable, VarTable, C1).
-constraints_to_dict(Constrs, MFA, RecDict, ExpTypes, RecordTable,
+constraints_to_dict(Constrs, Module, MFA, RecDict, ExpTypes, RecordTable,
VarTab, Cache) ->
{Subtypes, NewCache} =
- constraints_to_subs(Constrs, MFA, RecDict, ExpTypes, RecordTable,
+ constraints_to_subs(Constrs, Module, MFA, RecDict, ExpTypes, RecordTable,
VarTab, Cache, []),
{insert_constraints(Subtypes), NewCache}.
-constraints_to_subs([], _MFA, _RecDict, _ExpTypes, _RecordTable,
+constraints_to_subs([], _Module, _MFA, _RecDict, _ExpTypes, _RecordTable,
_VarTab, Cache, Acc) ->
{Acc, Cache};
-constraints_to_subs([{T1, Form2}|Rest], MFA, RecDict, ExpTypes, RecordTable,
- VarTab, Cache, Acc) ->
+constraints_to_subs([{T1, Form2}|Rest], Module, MFA, RecDict, ExpTypes,
+ RecordTable, VarTab, Cache, Acc) ->
{T2, NewCache} =
- final_form(Form2, ExpTypes, MFA, RecordTable, VarTab, Cache),
+ final_form(Form2, ExpTypes, Module, MFA, RecordTable, VarTab, Cache),
NewAcc = [{subtype, T1, T2}|Acc],
- constraints_to_subs(Rest, MFA, RecDict, ExpTypes, RecordTable,
+ constraints_to_subs(Rest, Module, MFA, RecDict, ExpTypes, RecordTable,
VarTab, NewCache, NewAcc).
%% Replaces variables with '_' when necessary to break up cycles among
@@ -898,6 +911,7 @@ is_remote_types_related(Contract, CSig, Sig, MFA, RecDict) ->
t_from_forms_without_remote([{FType, []}], MFA, RecDict) ->
Site = {spec, MFA},
+ %% FIXME
Type1 = erl_types:t_from_form_without_remote(FType, Site, RecDict),
{ok, erl_types:subst_all_vars_to_any(Type1)};
t_from_forms_without_remote([{_FType, _Constrs}], _MFA, _RecDict) ->
diff --git a/lib/dialyzer/src/dialyzer_utils.erl b/lib/dialyzer/src/dialyzer_utils.erl
index ebe4040c34..3fe026b096 100644
--- a/lib/dialyzer/src/dialyzer_utils.erl
+++ b/lib/dialyzer/src/dialyzer_utils.erl
@@ -450,8 +450,9 @@ get_spec_info([{Contract, Ln, [{Id, TypeSpec}]}|Left],
error ->
SpecData = {TypeSpec, Xtra},
NewActiveMap =
- dialyzer_contracts:store_tmp_contract(MFA, {File, Ln}, SpecData,
- ActiveMap, RecordsMap),
+ dialyzer_contracts:store_tmp_contract(ModName, MFA, {File, Ln},
+ SpecData, ActiveMap,
+ RecordsMap),
{NewSpecMap, NewCallbackMap} =
case Contract of
spec -> {NewActiveMap, CallbackMap};
@@ -599,24 +600,32 @@ collect_attribute([], _Tag, _File) ->
-spec is_suppressed_fun(mfa(), codeserver()) -> boolean().
is_suppressed_fun(MFA, CodeServer) ->
- lookup_fun_property(MFA, nowarn_function, CodeServer).
+ lookup_fun_property(MFA, nowarn_function, CodeServer, false).
-spec is_suppressed_tag(mfa() | module(), dial_warn_tag(), codeserver()) ->
boolean().
is_suppressed_tag(MorMFA, Tag, Codeserver) ->
- not lookup_fun_property(MorMFA, Tag, Codeserver).
-
-lookup_fun_property({M, _F, _A}=MFA, Property, CodeServer) ->
- MFAPropList = dialyzer_codeserver:lookup_meta_info(MFA, CodeServer),
- case proplists:get_value(Property, MFAPropList, no) of
- mod -> false; % suppressed in function
- func -> true; % requested in function
- no -> lookup_fun_property(M, Property, CodeServer)
+ not lookup_fun_property(MorMFA, Tag, Codeserver, true).
+
+lookup_fun_property({M, _F, _A}=MFA, Property, CodeServer, NoInfoReturn) ->
+ case dialyzer_codeserver:lookup_meta_info(MFA, CodeServer) of
+ error ->
+ lookup_fun_property(M, Property, CodeServer, NoInfoReturn);
+ {ok, MFAPropList} ->
+ case proplists:get_value(Property, MFAPropList, no) of
+ mod -> false; % suppressed in function
+ func -> true; % requested in function
+ no -> lookup_fun_property(M, Property, CodeServer, NoInfoReturn)
+ end
end;
-lookup_fun_property(M, Property, CodeServer) when is_atom(M) ->
- MPropList = dialyzer_codeserver:lookup_meta_info(M, CodeServer),
- proplists:is_defined(Property, MPropList).
+lookup_fun_property(M, Property, CodeServer, NoInfoReturn) when is_atom(M) ->
+ case dialyzer_codeserver:lookup_meta_info(M, CodeServer) of
+ error ->
+ NoInfoReturn;
+ {ok, MPropList} ->
+ proplists:is_defined(Property, MPropList)
+ end.
%% ============================================================================
%%
diff --git a/lib/dialyzer/test/small_SUITE_data/results/spec_other_module b/lib/dialyzer/test/small_SUITE_data/results/spec_other_module
new file mode 100644
index 0000000000..ab2e35cf55
--- /dev/null
+++ b/lib/dialyzer/test/small_SUITE_data/results/spec_other_module
@@ -0,0 +1,2 @@
+
+spec_other_module.erl:7: Contract for function that does not exist: lists:flatten/1
diff --git a/lib/dialyzer/test/small_SUITE_data/src/lists_key_bug.erl b/lib/dialyzer/test/small_SUITE_data/src/lists_key_bug.erl
new file mode 100644
index 0000000000..d7cbc27a4d
--- /dev/null
+++ b/lib/dialyzer/test/small_SUITE_data/src/lists_key_bug.erl
@@ -0,0 +1,19 @@
+-module(lists_key_bug).
+
+%% OTP-15570
+
+-export([t/1]).
+
+t(V) ->
+ K = key(V),
+ case lists:keyfind(K, 1, [{<<"foo">>, bar}]) of
+ false ->
+ a;
+ {_, _} ->
+ b
+ end.
+
+key(1) ->
+ 3;
+key(2) ->
+ <<"foo">>.
diff --git a/lib/dialyzer/test/small_SUITE_data/src/spec_other_module.erl b/lib/dialyzer/test/small_SUITE_data/src/spec_other_module.erl
new file mode 100644
index 0000000000..b36742b1bd
--- /dev/null
+++ b/lib/dialyzer/test/small_SUITE_data/src/spec_other_module.erl
@@ -0,0 +1,7 @@
+-module(spec_other_module).
+
+%% OTP-15562 and ERL-845. Example provided by Kostis.
+
+-type deep_list(A) :: [A | deep_list(A)].
+
+-spec lists:flatten(deep_list(A)) -> [A].
diff --git a/lib/diameter/doc/src/notes.xml b/lib/diameter/doc/src/notes.xml
index 4bfc98de40..cc92bd99f0 100644
--- a/lib/diameter/doc/src/notes.xml
+++ b/lib/diameter/doc/src/notes.xml
@@ -78,6 +78,24 @@ first.</p>
</section>
+<section><title>diameter 2.1.4.1</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Fix failure of incoming answer message with faulty
+ Experimental-Result-Code. Failure to decode the AVP
+ resulted in an uncaught exception, with no no
+ handle_answer/error callback as a consequence.</p>
+ <p>
+ Own Id: OTP-15569 Aux Id: ERIERL-302 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>diameter 2.1.4</title>
<section><title>Fixed Bugs and Malfunctions</title>
diff --git a/lib/diameter/src/base/diameter_gen.erl b/lib/diameter/src/base/diameter_gen.erl
index d110a3015e..564448de48 100644
--- a/lib/diameter/src/base/diameter_gen.erl
+++ b/lib/diameter/src/base/diameter_gen.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010-2018. All Rights Reserved.
+%% Copyright Ericsson AB 2010-2019. 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.
diff --git a/lib/diameter/src/base/diameter_traffic.erl b/lib/diameter/src/base/diameter_traffic.erl
index d2856ae530..2d3e4a2ac9 100644
--- a/lib/diameter/src/base/diameter_traffic.erl
+++ b/lib/diameter/src/base/diameter_traffic.erl
@@ -1925,6 +1925,8 @@ get_avp(Dict, Name, [#diameter_header{} | Avps]) ->
A = find_avp(Code, Vid, Avps),
avp_decode(Dict, Name, ungroup(A))
catch
+ {diameter_gen, _} -> %% faulty Grouped AVP
+ undefined;
error: _ ->
undefined
end;
diff --git a/lib/diameter/src/diameter.appup.src b/lib/diameter/src/diameter.appup.src
index 51830f5276..4e6b983bac 100644
--- a/lib/diameter/src/diameter.appup.src
+++ b/lib/diameter/src/diameter.appup.src
@@ -2,7 +2,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010-2018. All Rights Reserved.
+%% Copyright Ericsson AB 2010-2019. 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.
@@ -59,6 +59,7 @@
{"2.1.2", [{restart_application, diameter}]}, %% 20.1.3
{"2.1.3", [{restart_application, diameter}]}, %% 20.2
{"2.1.4", [{restart_application, diameter}]}, %% 20.3
+ {"2.1.4.1", [{restart_application, diameter}]}, %% 20.3.8.19
{"2.1.5", [{update, diameter_peer_fsm}]} %% 21.0
],
[
@@ -100,6 +101,7 @@
{"2.1.2", [{restart_application, diameter}]},
{"2.1.3", [{restart_application, diameter}]},
{"2.1.4", [{restart_application, diameter}]},
+ {"2.1.4.1", [{restart_application, diameter}]},
{"2.1.5", [{update, diameter_peer_fsm}]}
]
}.
diff --git a/lib/diameter/src/transport/diameter_tcp.erl b/lib/diameter/src/transport/diameter_tcp.erl
index da059fa7d6..e5e766d2a0 100644
--- a/lib/diameter/src/transport/diameter_tcp.erl
+++ b/lib/diameter/src/transport/diameter_tcp.erl
@@ -92,9 +92,9 @@
-type connect_option() :: {raddr, inet:ip_address()}
| {rport, pos_integer()}
- | {ssl_options, true | [ssl:connect_option()]}
+ | {ssl_options, true | [ssl:tls_client_option()]}
| option()
- | ssl:connect_option()
+ | ssl:tls_client_option()
| gen_tcp:connect_option().
-type match() :: inet:ip_address()
@@ -102,9 +102,9 @@
| [match()].
-type listen_option() :: {accept, match()}
- | {ssl_options, true | [ssl:listen_option()]}
+ | {ssl_options, true | [ssl:tls_server_option()]}
| option()
- | ssl:listen_option()
+ | ssl:tls_server_option()
| gen_tcp:listen_option().
-type option() :: {port, non_neg_integer()}
diff --git a/lib/erl_interface/doc/src/ei.xml b/lib/erl_interface/doc/src/ei.xml
index 16f4e18637..ae322255ad 100644
--- a/lib/erl_interface/doc/src/ei.xml
+++ b/lib/erl_interface/doc/src/ei.xml
@@ -733,6 +733,21 @@ ei_encode_tuple_header(buf, &amp;i, 0);</pre>
</func>
<func>
+ <name since="OTP @OTP-15442@"><ret>int</ret><nametext>ei_init(void)</nametext></name>
+ <fsummary>Initialize the ei library.</fsummary>
+ <desc>
+ <p>Initialize the <c>ei</c> library. This function should be called once
+ (and only once) before calling any other functionality in the <c>ei</c>
+ library. However, note the exception below.</p>
+ <p>If the <c>ei</c> library is used together with the <c>erl_interface</c>
+ library, this function should <em>not</em> be called directly. It will be
+ called by the <c>erl_init()</c> function which should be used to initialize
+ the combination of the two libraries instead.</p>
+ <p>On success zero is returned. On failure a posix error code is returned.</p>
+ </desc>
+ </func>
+
+ <func>
<name since=""><ret>int</ret><nametext>ei_print_term(FILE* fp, const char* buf, int* index)</nametext></name>
<name since=""><ret>int</ret><nametext>ei_s_print_term(char** s, const char* buf, int* index)</nametext></name>
<fsummary>Print a term in clear text.</fsummary>
diff --git a/lib/erl_interface/doc/src/ei_users_guide.xml b/lib/erl_interface/doc/src/ei_users_guide.xml
index 0eed50b50b..2dfd99e35a 100644
--- a/lib/erl_interface/doc/src/ei_users_guide.xml
+++ b/lib/erl_interface/doc/src/ei_users_guide.xml
@@ -162,12 +162,20 @@ $ ld -L/usr/local/otp/lib/erl_interface-3.2.3/
</section>
<section>
- <title>Initializing the Erl_Interface Library</title>
- <p>Before calling any of the other <c>Erl_Interface</c> functions, call
- <c>erl_init()</c> exactly once to initialize the library.
+ <title>Initializing the Libraries</title>
+ <p>
+ Before calling any of the other functions in the <c>erl_interface</c>
+ and <c>ei</c> libraries, call <c>erl_init()</c> exactly once to initialize
+ both libraries.
<c>erl_init()</c> takes two arguments. However, the arguments
- are no longer used by <c>Erl_Interface</c> and are therefore to be
- specified as <c>erl_init(NULL,0)</c>.</p>
+ are no longer used by <c>erl_interface</c> and are therefore to be
+ specified as <c>erl_init(NULL,0)</c>.
+ </p>
+ <p>
+ If you only use the <c>ei</c> library, instead initialize it by calling
+ <c>ei_init()</c> exactly once before calling any other functions in
+ the <c>ei</c> library.
+ </p>
</section>
<section>
diff --git a/lib/erl_interface/include/ei.h b/lib/erl_interface/include/ei.h
index 92674571e2..ca4960b252 100644
--- a/lib/erl_interface/include/ei.h
+++ b/lib/erl_interface/include/ei.h
@@ -36,6 +36,8 @@
#include <windows.h>
#include <winbase.h>
typedef LONG_PTR ssize_t; /* Sigh... */
+#else
+#include <sys/types.h> /* ssize_t */
#endif
#include <stdio.h> /* Need type FILE */
@@ -667,6 +669,8 @@ struct ei_reg_tabstat {
};
+int ei_init(void);
+
/* -------------------------------------------------------------------- */
/* XXXXXXXXXXX */
/* -------------------------------------------------------------------- */
diff --git a/lib/erl_interface/src/Makefile.in b/lib/erl_interface/src/Makefile.in
index bf2d821b97..b05d494884 100644
--- a/lib/erl_interface/src/Makefile.in
+++ b/lib/erl_interface/src/Makefile.in
@@ -418,7 +418,8 @@ MISCSRC = \
misc/eimd5.c \
misc/get_type.c \
misc/show_msg.c \
- misc/ei_compat.c
+ misc/ei_compat.c \
+ misc/ei_init.c
REGISTRYSRC = \
registry/hash_dohash.c \
diff --git a/lib/erl_interface/src/connect/ei_connect.c b/lib/erl_interface/src/connect/ei_connect.c
index 1132c9fc23..7a304e6d4f 100644
--- a/lib/erl_interface/src/connect/ei_connect.c
+++ b/lib/erl_interface/src/connect/ei_connect.c
@@ -21,6 +21,8 @@
* Purpose: Connect to any node at any host. (EI version)
*/
+#include "eidef.h"
+
#include <stdlib.h>
#include <sys/types.h>
#include <fcntl.h>
@@ -40,10 +42,8 @@
#include <inetLib.h>
#include <unistd.h>
-#include <sys/types.h>
#include <sys/times.h>
#include <unistd.h>
-#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
@@ -53,7 +53,6 @@
#else /* some other unix */
#include <unistd.h>
-#include <sys/types.h>
#include <sys/times.h>
#if TIME_WITH_SYS_TIME
@@ -84,7 +83,6 @@
#include <ctype.h>
#include <stddef.h>
-#include "eidef.h"
#include "eiext.h"
#include "ei_portio.h"
#include "ei_internal.h"
@@ -98,6 +96,7 @@
#include "ei_epmd.h"
#include "ei_internal.h"
+static int ei_connect_initialized = 0;
int ei_tracelevel = 0;
#define COOKIE_FILE "/.erlang.cookie"
@@ -244,7 +243,7 @@ typedef struct {
static ei_socket_info_data__ *socket_info_data = NULL;
-static int init_socket_info(void)
+static int init_socket_info(int late)
{
int max_fds;
int i;
@@ -252,7 +251,7 @@ static int init_socket_info(void)
ei_socket_info_data__ *info_data, *xchg;
if (EI_ATOMIC_LOAD_ACQ(&socket_info_data) != NULL)
- return !0; /* Already initialized... */
+ return 0; /* Already initialized... */
#if defined(HAVE_SYSCONF) && defined(_SC_OPEN_MAX)
max_fds = sysconf(_SC_OPEN_MAX);
@@ -261,14 +260,14 @@ static int init_socket_info(void)
#endif
if (max_fds < 0)
- return 0;
+ return EIO;
segments_len = ((max_fds-1)/EI_SOCKET_INFO_SEG_SIZE + 1);
info_data = malloc(sizeof(ei_socket_info_data__)
+ (sizeof(ei_socket_info *)*(segments_len-1)));
if (!info_data)
- return 0;
+ return ENOMEM;
info_data->max_fds = max_fds;
for (i = 0; i < segments_len; i++)
@@ -278,7 +277,7 @@ static int init_socket_info(void)
if (!EI_ATOMIC_CMPXCHG_ACQ_REL(&socket_info_data, &xchg, info_data))
free(info_data); /* Already initialized... */
- return !0;
+ return 0;
}
static int put_ei_socket_info(int fd, int dist_version, char* cookie, ei_cnode *ec,
@@ -362,14 +361,16 @@ ei_socket_info *ei_sockets = NULL;
ei_mutex_t* ei_sockets_lock = NULL;
#endif /* _REENTRANT */
-static int init_socket_info(void)
+static int init_socket_info(int late)
{
#ifdef _REENTRANT
- if (ei_sockets_lock == NULL) {
- ei_sockets_lock = ei_mutex_create();
- }
+ if (late)
+ return ENOTSUP; /* Refuse doing unsafe initialization... */
+ ei_sockets_lock = ei_mutex_create();
+ if (!ei_sockets_lock)
+ return ENOMEM;
#endif /* _REENTRANT */
- return !0;
+ return 0;
}
static int put_ei_socket_info(int fd, int dist_version, char* cookie, ei_cnode *ec,
@@ -602,6 +603,38 @@ static int initWinSock(void)
}
#endif
+static int init_connect(int late)
+{
+ int error;
+
+ /*
+ * 'late' is non-zero when not called via ei_init(). Such a
+ * call is not supported, but we for now save the day if
+ * it easy to do so; otherwise, return ENOTSUP.
+ */
+
+#ifdef __WIN32__
+ if (!initWinSock()) {
+ EI_TRACE_ERR0("ei_init_connect","can't initiate winsock");
+ return EIO;
+ }
+#endif /* win32 */
+
+ error = init_socket_info(late);
+ if (error) {
+ EI_TRACE_ERR0("ei_init_connect","can't initiate socket info");
+ return error;
+ }
+
+ ei_connect_initialized = !0;
+ return 0;
+}
+
+int ei_init_connect(void)
+{
+ return init_connect(0);
+}
+
/*
* Perhaps run this routine instead of ei_connect_init/2 ?
* Initailize by setting:
@@ -615,24 +648,12 @@ int ei_connect_xinit_ussi(ei_cnode* ec, const char *thishostname,
{
char *dbglevel;
+ if (!ei_connect_initialized)
+ init_connect(!0);
+
if (cbs != &ei_default_socket_callbacks)
EI_SET_HAVE_PLUGIN_SOCKET_IMPL__;
-/* FIXME this code was enabled for 'erl'_connect_xinit(), why not here? */
-#if 0
-#ifdef __WIN32__
- if (!initWinSock()) {
- EI_TRACE_ERR0("ei_connect_xinit","can't initiate winsock");
- return ERL_ERROR;
- }
-#endif
-#endif
-
- if (!init_socket_info()) {
- EI_TRACE_ERR0("ei_connect_xinit","can't initiate socket info");
- return ERL_ERROR;
- }
-
if (cbs_sz < EI_SOCKET_CALLBACKS_SZ_V1) {
EI_TRACE_ERR0("ei_connect_xinit","invalid size of ei_socket_callbacks struct");
return ERL_ERROR;
@@ -720,13 +741,9 @@ int ei_connect_init_ussi(ei_cnode* ec, const char* this_node_name,
int ei_h_errno;
int res;
-#ifdef __WIN32__
- if (!initWinSock()) {
- EI_TRACE_ERR0("ei_connect_xinit","can't initiate winsock");
- return ERL_ERROR;
- }
-#endif /* win32 */
-
+ if (!ei_connect_initialized)
+ init_connect(!0);
+
/* gethostname requires len to be max(hostname) + 1 */
if (gethostname(thishostname, EI_MAXHOSTNAMELEN+1) == -1) {
#ifdef __WIN32__
@@ -1279,7 +1296,6 @@ int ei_accept_tmo(ei_cnode* ec, int lfd, ErlConnect *conp, unsigned ms)
}
if (conp) {
memcpy((void *) conp->ipadr, (void *) &addr.sin_addr, sizeof(conp->ipadr));
- strcpy(&conp->nodename[0], her_name);
}
if (cbs->accept_handshake_complete) {
diff --git a/lib/erl_interface/src/connect/ei_resolve.c b/lib/erl_interface/src/connect/ei_resolve.c
index 022a43d255..225fddc784 100644
--- a/lib/erl_interface/src/connect/ei_resolve.c
+++ b/lib/erl_interface/src/connect/ei_resolve.c
@@ -57,9 +57,9 @@
#ifdef HAVE_GETHOSTBYNAME_R
-void ei_init_resolve(void)
+int ei_init_resolve(void)
{
- return; /* Do nothing */
+ return 0; /* Do nothing */
}
#else /* !HAVE_GETHOSTBYNAME_R */
@@ -103,7 +103,7 @@ static int verify_dns_configuration(void);
* our own, which are just wrappers around hostGetByName() and
* hostGetByAddr(). Here we look up the functions.
*/
-void ei_init_resolve(void)
+int ei_init_resolve(void)
{
#ifdef VXWORKS
@@ -134,9 +134,12 @@ void ei_init_resolve(void)
#ifdef _REENTRANT
ei_gethost_sem = ei_mutex_create();
+ if (!ei_gethost_sem)
+ return ENOMEM;
#endif /* _REENTRANT */
ei_resolve_initialized = 1;
+ return 0;
}
#ifdef VXWORKS
@@ -312,9 +315,11 @@ static struct hostent *my_gethostbyname_r(const char *name,
struct hostent *src;
struct hostent *rval = NULL;
- /* FIXME this should have been done in 'erl'_init()? */
- if (!ei_resolve_initialized) ei_init_resolve();
-
+ if (!ei_resolve_initialized) {
+ *h_errnop = NO_RECOVERY;
+ return NULL;
+ }
+
#ifdef _REENTRANT
/* === BEGIN critical section === */
if (ei_mutex_lock(ei_gethost_sem,0) != 0) {
@@ -377,7 +382,10 @@ static struct hostent *my_gethostbyaddr_r(const char *addr,
struct hostent *rval = NULL;
/* FIXME this should have been done in 'erl'_init()? */
- if (!ei_resolve_initialized) ei_init_resolve();
+ if (!ei_resolve_initialized) {
+ *h_errnop = NO_RECOVERY;
+ return NULL;
+ }
#ifdef _REENTRANT
/* === BEGIN critical section === */
diff --git a/lib/erl_interface/src/connect/ei_resolve.h b/lib/erl_interface/src/connect/ei_resolve.h
index 10a49ffbc6..5711d7da76 100644
--- a/lib/erl_interface/src/connect/ei_resolve.h
+++ b/lib/erl_interface/src/connect/ei_resolve.h
@@ -20,6 +20,6 @@
#ifndef _EI_RESOLVE_H
#define _EI_RESOLVE_H
-void ei_init_resolve(void);
+int ei_init_resolve(void);
#endif /* _EI_RESOLVE_H */
diff --git a/lib/erl_interface/src/legacy/erl_eterm.c b/lib/erl_interface/src/legacy/erl_eterm.c
index 9ad92121f4..7ed2bdbc93 100644
--- a/lib/erl_interface/src/legacy/erl_eterm.c
+++ b/lib/erl_interface/src/legacy/erl_eterm.c
@@ -65,7 +65,7 @@ void erl_init(void *hp,long heap_size)
{
erl_init_malloc(hp, heap_size);
erl_init_marshal();
- ei_init_resolve();
+ (void) ei_init();
}
void erl_set_compat_rel(unsigned rel)
diff --git a/lib/erl_interface/src/misc/ei_init.c b/lib/erl_interface/src/misc/ei_init.c
new file mode 100644
index 0000000000..5357968657
--- /dev/null
+++ b/lib/erl_interface/src/misc/ei_init.c
@@ -0,0 +1,32 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB 2019. 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%
+ */
+
+#include "ei.h"
+#include "ei_resolve.h"
+#include "ei_internal.h"
+
+int
+ei_init(void)
+{
+ int error = ei_init_connect();
+ if (error)
+ return error;
+ return ei_init_resolve();
+}
diff --git a/lib/erl_interface/src/misc/ei_internal.h b/lib/erl_interface/src/misc/ei_internal.h
index 0c58245c0a..f28dd6d668 100644
--- a/lib/erl_interface/src/misc/ei_internal.h
+++ b/lib/erl_interface/src/misc/ei_internal.h
@@ -153,6 +153,8 @@
extern int ei_tracelevel;
+int ei_init_connect(void);
+
void ei_trace_printf(const char *name, int level, const char *format, ...);
int ei_internal_use_r9_pids_ports(void);
diff --git a/lib/erl_interface/src/misc/ei_portio.c b/lib/erl_interface/src/misc/ei_portio.c
index 726b1af82d..bccc86c1b1 100644
--- a/lib/erl_interface/src/misc/ei_portio.c
+++ b/lib/erl_interface/src/misc/ei_portio.c
@@ -19,6 +19,9 @@
*
*/
+
+#include "eidef.h"
+
#ifdef __WIN32__
#include <winsock2.h>
#include <windows.h>
@@ -47,10 +50,8 @@ static unsigned long param_one = 1;
#include <taskLib.h>
#include <inetLib.h>
#include <selectLib.h>
-#include <sys/types.h>
#include <ioLib.h>
#include <unistd.h>
-#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
@@ -65,7 +66,6 @@ static unsigned long param_one = 1;
#else /* other unix */
#include <stdlib.h>
-#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <fcntl.h>
@@ -86,6 +86,7 @@ static unsigned long param_one = 1;
/* common includes */
+#include <sys/types.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@@ -94,7 +95,9 @@ static unsigned long param_one = 1;
#else
#include <time.h>
#endif
-#include "eidef.h"
+#ifdef HAVE_SYS_SELECT_H
+#include <sys/select.h>
+#endif
#include "ei_portio.h"
#include "ei_internal.h"
@@ -246,7 +249,7 @@ static int tcp_accept(void **ctx, void *addr, int *len, unsigned unused)
if (res)
return res;
- res = accept(fd, (struct sockaddr*) &addr, &addr_len);
+ res = accept(fd, (struct sockaddr*) addr, &addr_len);
if (MEANS_SOCKET_ERROR(res))
return get_error();
diff --git a/lib/erl_interface/test/ei_accept_SUITE_data/ei_accept_test.c b/lib/erl_interface/test/ei_accept_SUITE_data/ei_accept_test.c
index f41d741609..c209f506b1 100644
--- a/lib/erl_interface/test/ei_accept_SUITE_data/ei_accept_test.c
+++ b/lib/erl_interface/test/ei_accept_SUITE_data/ei_accept_test.c
@@ -74,6 +74,8 @@ TESTCASE(interpret)
int i;
ei_term term;
+ ei_init();
+
ei_x_new(&x);
while (get_bin_term(&x, &term) == 0) {
char* buf = x.buff, func[MAXATOMLEN];
diff --git a/lib/erl_interface/test/ei_accept_SUITE_data/eiaccnode.c b/lib/erl_interface/test/ei_accept_SUITE_data/eiaccnode.c
index c850d20f3c..90c7a2259f 100644
--- a/lib/erl_interface/test/ei_accept_SUITE_data/eiaccnode.c
+++ b/lib/erl_interface/test/ei_accept_SUITE_data/eiaccnode.c
@@ -178,6 +178,8 @@ MAIN(int argc, char *argv[])
no_threads = 1;
#endif
+ ei_init();
+
for (i = 0; i < n; ++i) {
if (!no_threads) {
#ifndef VXWORKS
diff --git a/lib/erl_interface/test/ei_connect_SUITE_data/ei_connect_test.c b/lib/erl_interface/test/ei_connect_SUITE_data/ei_connect_test.c
index 29c03d7604..58c0c7f8d8 100644
--- a/lib/erl_interface/test/ei_connect_SUITE_data/ei_connect_test.c
+++ b/lib/erl_interface/test/ei_connect_SUITE_data/ei_connect_test.c
@@ -73,6 +73,8 @@ TESTCASE(interpret)
int i;
ei_term term;
+ ei_init();
+
ei_x_new(&x);
while (get_bin_term(&x, &term) == 0) {
char* buf = x.buff, func[MAXATOMLEN];
diff --git a/lib/erl_interface/test/ei_decode_SUITE_data/ei_decode_test.c b/lib/erl_interface/test/ei_decode_SUITE_data/ei_decode_test.c
index f945a7d378..e516f310b6 100644
--- a/lib/erl_interface/test/ei_decode_SUITE_data/ei_decode_test.c
+++ b/lib/erl_interface/test/ei_decode_SUITE_data/ei_decode_test.c
@@ -321,6 +321,8 @@ int ei_decode_my_string(const char *buf, int *index, char *to,
TESTCASE(test_ei_decode_long)
{
+ ei_init();
+
EI_DECODE_2 (decode_long, 2, long, 0);
EI_DECODE_2 (decode_long, 2, long, 255);
EI_DECODE_2 (decode_long, 5, long, 256);
@@ -363,6 +365,8 @@ TESTCASE(test_ei_decode_long)
TESTCASE(test_ei_decode_ulong)
{
+ ei_init();
+
EI_DECODE_2 (decode_ulong, 2, unsigned long, 0);
EI_DECODE_2 (decode_ulong, 2, unsigned long, 255);
EI_DECODE_2 (decode_ulong, 5, unsigned long, 256);
@@ -409,6 +413,8 @@ TESTCASE(test_ei_decode_ulong)
TESTCASE(test_ei_decode_longlong)
{
+ ei_init();
+
#ifndef VXWORKS
EI_DECODE_2 (decode_longlong, 2, EI_LONGLONG, 0);
EI_DECODE_2 (decode_longlong, 2, EI_LONGLONG, 255);
@@ -443,6 +449,8 @@ TESTCASE(test_ei_decode_longlong)
TESTCASE(test_ei_decode_ulonglong)
{
+ ei_init();
+
#ifndef VXWORKS
EI_DECODE_2 (decode_ulonglong, 2, EI_ULONGLONG, 0);
EI_DECODE_2 (decode_ulonglong, 2, EI_ULONGLONG, 255);
@@ -478,6 +486,8 @@ TESTCASE(test_ei_decode_ulonglong)
TESTCASE(test_ei_decode_char)
{
+ ei_init();
+
EI_DECODE_2(decode_char, 2, char, 0);
EI_DECODE_2(decode_char, 2, char, 0x7f);
EI_DECODE_2(decode_char, 2, char, 0xff);
@@ -491,6 +501,8 @@ TESTCASE(test_ei_decode_char)
TESTCASE(test_ei_decode_nonoptimal)
{
+ ei_init();
+
EI_DECODE_2(decode_char, 2, char, 42);
EI_DECODE_2(decode_char, 5, char, 42);
EI_DECODE_2(decode_char, 4, char, 42);
@@ -612,6 +624,8 @@ TESTCASE(test_ei_decode_nonoptimal)
TESTCASE(test_ei_decode_misc)
{
+ ei_init();
+
/*
EI_DECODE_0(decode_version);
*/
@@ -647,6 +661,7 @@ TESTCASE(test_ei_decode_misc)
TESTCASE(test_ei_decode_utf8_atom)
{
+ ei_init();
EI_DECODE_STRING_4(decode_my_atom_as, 4, P99({229,0}), /* LATIN1 "�" */
P99({ERLANG_ANY,ERLANG_LATIN1,ERLANG_LATIN1}));
diff --git a/lib/erl_interface/test/ei_decode_encode_SUITE_data/ei_decode_encode_test.c b/lib/erl_interface/test/ei_decode_encode_SUITE_data/ei_decode_encode_test.c
index 9977683d59..55d9ed1b1a 100644
--- a/lib/erl_interface/test/ei_decode_encode_SUITE_data/ei_decode_encode_test.c
+++ b/lib/erl_interface/test/ei_decode_encode_SUITE_data/ei_decode_encode_test.c
@@ -477,6 +477,8 @@ TESTCASE(test_ei_decode_encode)
{
int i;
+ ei_init();
+
decode_encode_one(&fun_type);
decode_encode_one(&pid_type);
decode_encode_one(&port_type);
diff --git a/lib/erl_interface/test/ei_encode_SUITE_data/ei_encode_test.c b/lib/erl_interface/test/ei_encode_SUITE_data/ei_encode_test.c
index 32811fdf22..6f63cc5d7e 100644
--- a/lib/erl_interface/test/ei_encode_SUITE_data/ei_encode_test.c
+++ b/lib/erl_interface/test/ei_encode_SUITE_data/ei_encode_test.c
@@ -403,6 +403,8 @@
TESTCASE(test_ei_encode_long)
{
+ ei_init();
+
EI_ENCODE_1(encode_long, 0);
EI_ENCODE_1(encode_long, 255);
@@ -430,6 +432,8 @@ TESTCASE(test_ei_encode_long)
TESTCASE(test_ei_encode_ulong)
{
+ ei_init();
+
EI_ENCODE_1(encode_ulong, 0);
EI_ENCODE_1(encode_ulong, 255);
@@ -454,6 +458,7 @@ TESTCASE(test_ei_encode_ulong)
TESTCASE(test_ei_encode_longlong)
{
+ ei_init();
#ifndef VXWORKS
@@ -494,6 +499,7 @@ TESTCASE(test_ei_encode_longlong)
TESTCASE(test_ei_encode_ulonglong)
{
+ ei_init();
#ifndef VXWORKS
@@ -527,6 +533,8 @@ TESTCASE(test_ei_encode_ulonglong)
TESTCASE(test_ei_encode_char)
{
+ ei_init();
+
EI_ENCODE_1(encode_char, 0);
EI_ENCODE_1(encode_char, 0x7f);
@@ -540,6 +548,8 @@ TESTCASE(test_ei_encode_char)
TESTCASE(test_ei_encode_misc)
{
+ ei_init();
+
EI_ENCODE_0(encode_version);
EI_ENCODE_1(encode_double, 0.0);
@@ -594,6 +604,8 @@ TESTCASE(test_ei_encode_fails)
char buf[1024];
int index;
+ ei_init();
+
/* FIXME the ei_x versions are not tested */
index = 0;
@@ -660,6 +672,7 @@ TESTCASE(test_ei_encode_fails)
TESTCASE(test_ei_encode_utf8_atom)
{
+ ei_init();
EI_ENCODE_3(encode_atom_as, "�", ERLANG_LATIN1, ERLANG_UTF8);
EI_ENCODE_3(encode_atom_as, "�", ERLANG_LATIN1, ERLANG_LATIN1);
@@ -686,6 +699,7 @@ TESTCASE(test_ei_encode_utf8_atom)
TESTCASE(test_ei_encode_utf8_atom_len)
{
+ ei_init();
EI_ENCODE_4(encode_atom_len_as, "���", 1, ERLANG_LATIN1, ERLANG_UTF8);
EI_ENCODE_4(encode_atom_len_as, "���", 2, ERLANG_LATIN1, ERLANG_LATIN1);
diff --git a/lib/erl_interface/test/ei_format_SUITE_data/ei_format_test.c b/lib/erl_interface/test/ei_format_SUITE_data/ei_format_test.c
index 8450332b28..1c0443c0f4 100644
--- a/lib/erl_interface/test/ei_format_SUITE_data/ei_format_test.c
+++ b/lib/erl_interface/test/ei_format_SUITE_data/ei_format_test.c
@@ -48,6 +48,8 @@ send_format(char* format)
TESTCASE(atoms)
{
+ ei_init();
+
send_format("''");
send_format("'a'");
send_format("'A'");
@@ -82,6 +84,8 @@ TESTCASE(atoms)
TESTCASE(tuples)
{
+ ei_init();
+
send_format("{}");
send_format("{a}");
send_format("{a, b}");
@@ -108,6 +112,8 @@ TESTCASE(lists)
ei_x_buff x;
static char str[65537];
+ ei_init();
+
send_format("[]");
send_format("[a]");
send_format("[a, b]");
@@ -177,6 +183,8 @@ TESTCASE(format_wo_ver) {
*/
ei_x_buff x;
+ ei_init();
+
ei_x_new (&x);
ei_x_format(&x, "[-1, +2, ~c, {~a,~s},{~a,~i}]", 'c', "a", "b", "c", 10);
send_bin_term(&x);
diff --git a/lib/erl_interface/test/ei_print_SUITE_data/ei_print_test.c b/lib/erl_interface/test/ei_print_SUITE_data/ei_print_test.c
index 15cfbcae34..80be3016e6 100644
--- a/lib/erl_interface/test/ei_print_SUITE_data/ei_print_test.c
+++ b/lib/erl_interface/test/ei_print_SUITE_data/ei_print_test.c
@@ -84,6 +84,8 @@ static void send_printed3f(char* format, float f1, float f2)
TESTCASE(atoms)
{
+ ei_init();
+
send_printed("''");
send_printed("'a'");
send_printed("'A'");
@@ -118,6 +120,8 @@ TESTCASE(atoms)
TESTCASE(tuples)
{
+ ei_init();
+
send_printed("{}");
send_printed("{a}");
send_printed("{a, b}");
@@ -138,6 +142,8 @@ TESTCASE(lists)
{
ei_x_buff x;
+ ei_init();
+
send_printed("[]");
send_printed("[a]");
send_printed("[a, b]");
@@ -164,6 +170,8 @@ TESTCASE(strings)
{
ei_x_buff x;
+ ei_init();
+
send_printed("\"\n\"");
send_printed("\"\r\n\"");
send_printed("\"a\"");
diff --git a/lib/erl_interface/test/ei_tmo_SUITE_data/ei_tmo_test.c b/lib/erl_interface/test/ei_tmo_SUITE_data/ei_tmo_test.c
index 39846e4a58..693e405f75 100644
--- a/lib/erl_interface/test/ei_tmo_SUITE_data/ei_tmo_test.c
+++ b/lib/erl_interface/test/ei_tmo_SUITE_data/ei_tmo_test.c
@@ -96,6 +96,8 @@ TESTCASE(framework_check)
int i;
#endif
+ ei_init();
+
OPEN_DEBUGFILE(1);
DEBUGF(("B�rjar... \n"));
@@ -340,6 +342,7 @@ TESTCASE(recv_tmo)
int com_sock = -1;
ei_cnode nodeinfo;
+ ei_init();
OPEN_DEBUGFILE(5);
@@ -450,6 +453,7 @@ TESTCASE(send_tmo)
int com_sock = -1;
ei_cnode nodeinfo;
+ ei_init();
OPEN_DEBUGFILE(4);
@@ -591,7 +595,7 @@ TESTCASE(connect_tmo)
int com_sock = -1;
ei_cnode nodeinfo;
-
+ ei_init();
OPEN_DEBUGFILE(3);
@@ -680,7 +684,7 @@ TESTCASE(accept_tmo)
ErlConnect peer;
ei_cnode nodeinfo;
-
+ ei_init();
OPEN_DEBUGFILE(2);
diff --git a/lib/ftp/doc/src/ftp.xml b/lib/ftp/doc/src/ftp.xml
index ead2367925..9645b03364 100644
--- a/lib/ftp/doc/src/ftp.xml
+++ b/lib/ftp/doc/src/ftp.xml
@@ -550,7 +550,7 @@
<v>ipfamily() = inet | inet6 | inet6fb4 (default is inet)</v>
<v>port() = integer() > 0 (default is 21)</v>
<v>mode() = active | passive (default is passive)</v>
- <v>tls_options() = [<seealso marker="ssl:ssl#type-ssloption">ssl:ssloption()</seealso>]</v>
+ <v>tls_options() = [<seealso marker="ssl:ssl#type-tls_option">ssl:tls_option()</seealso>]</v>
<v>sock_opts() = [<seealso marker="kernel:gen_tcp#type-option">gen_tcp:option()</seealso> except for ipv6_v6only, active, packet, mode, packet_size and header</v>
<v>timeout() = integer() > 0 (default is 60000 milliseconds)</v>
<v>dtimeout() = integer() > 0 | infinity (default is infinity)</v>
diff --git a/lib/hipe/cerl/erl_bif_types.erl b/lib/hipe/cerl/erl_bif_types.erl
index 48ce641ab9..799957dfdc 100644
--- a/lib/hipe/cerl/erl_bif_types.erl
+++ b/lib/hipe/cerl/erl_bif_types.erl
@@ -2224,11 +2224,7 @@ type_order() ->
[t_number(), t_atom(), t_reference(), t_fun(), t_port(), t_pid(), t_tuple(),
t_map(), t_list(), t_bitstr()].
-key_comparisons_fail(X0, KeyPos, TupleList, Opaques) ->
- X = case t_is_number(t_inf(X0, t_number(), Opaques), Opaques) of
- false -> X0;
- true -> t_number()
- end,
+key_comparisons_fail(X, KeyPos, TupleList, Opaques) ->
lists:all(fun(Tuple) ->
Key = type(erlang, element, 2, [KeyPos, Tuple]),
t_is_none(t_inf(Key, X, Opaques))
diff --git a/lib/inets/doc/src/notes.xml b/lib/inets/doc/src/notes.xml
index 2faa49e541..31dae6317e 100644
--- a/lib/inets/doc/src/notes.xml
+++ b/lib/inets/doc/src/notes.xml
@@ -33,7 +33,29 @@
<file>notes.xml</file>
</header>
- <section><title>Inets 7.0.4</title>
+ <section><title>Inets 7.0.5</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Fixed bug that causes a crash in http client when using
+ hostnames (e.g. localhost) with the the option
+ ipv6_host_with_brackets set to true.</p>
+ <p>
+ This change also fixes a regression: httpc:request fails
+ with connection error (nxdomain) if option
+ ipv6_host_with_brackets set to true and host component of
+ the URI is an IPv6 address.</p>
+ <p>
+ Own Id: OTP-15554 Aux Id: ERIERL-289 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Inets 7.0.4</title>
<section><title>Fixed Bugs and Malfunctions</title>
<list>
diff --git a/lib/inets/vsn.mk b/lib/inets/vsn.mk
index 1d1560213e..921161dce1 100644
--- a/lib/inets/vsn.mk
+++ b/lib/inets/vsn.mk
@@ -19,6 +19,6 @@
# %CopyrightEnd%
APPLICATION = inets
-INETS_VSN = 7.0.4
+INETS_VSN = 7.0.5
PRE_VSN =
APP_VSN = "$(APPLICATION)-$(INETS_VSN)$(PRE_VSN)"
diff --git a/lib/kernel/doc/src/logger.xml b/lib/kernel/doc/src/logger.xml
index 0668676096..df2d081d76 100644
--- a/lib/kernel/doc/src/logger.xml
+++ b/lib/kernel/doc/src/logger.xml
@@ -689,6 +689,15 @@ start(_, []) ->
</func>
<func>
+ <name name="i" arity="0" since="OTP 21.3"/>
+ <name name="i" arity="1" since="OTP 21.3"/>
+ <fsummary>Pretty print the Logger configuration.</fsummary>
+ <desc>
+ <p>Pretty print the Logger configuration.</p>
+ </desc>
+ </func>
+
+ <func>
<name name="remove_handler" arity="1" since="OTP 21.0"/>
<fsummary>Remove the handler with the specified identity.</fsummary>
<desc>
diff --git a/lib/kernel/include/dist.hrl b/lib/kernel/include/dist.hrl
index 003852f1b0..f06fc328d7 100644
--- a/lib/kernel/include/dist.hrl
+++ b/lib/kernel/include/dist.hrl
@@ -42,6 +42,9 @@
-define(DFLAG_BIG_CREATION, 16#40000).
-define(DFLAG_SEND_SENDER, 16#80000).
-define(DFLAG_BIG_SEQTRACE_LABELS, 16#100000).
+%% -define(DFLAG_NO_MAGIC, 16#200000). %% Used internally only
+-define(DFLAG_EXIT_PAYLOAD, 16#400000).
+-define(DFLAG_FRAGMENTS, 16#800000).
%% Also update dflag2str() in ../src/dist_util.erl
%% when adding flags...
diff --git a/lib/kernel/src/dist_util.erl b/lib/kernel/src/dist_util.erl
index ecc022b28d..09ed31f10c 100644
--- a/lib/kernel/src/dist_util.erl
+++ b/lib/kernel/src/dist_util.erl
@@ -116,6 +116,10 @@ dflag2str(?DFLAG_SEND_SENDER) ->
"SEND_SENDER";
dflag2str(?DFLAG_BIG_SEQTRACE_LABELS) ->
"BIG_SEQTRACE_LABELS";
+dflag2str(?DFLAG_EXIT_PAYLOAD) ->
+ "EXIT_PAYLOAD";
+dflag2str(?DFLAG_FRAGMENTS) ->
+ "FRAGMENTS";
dflag2str(_) ->
"UNKNOWN".
diff --git a/lib/kernel/src/gen_tcp.erl b/lib/kernel/src/gen_tcp.erl
index 7f7833ec23..5d4764f8ff 100644
--- a/lib/kernel/src/gen_tcp.erl
+++ b/lib/kernel/src/gen_tcp.erl
@@ -156,7 +156,7 @@ connect(Address, Port, Opts) ->
Options :: [connect_option()],
Timeout :: timeout(),
Socket :: socket(),
- Reason :: inet:posix().
+ Reason :: timeout | inet:posix().
connect(Address, Port, Opts, Time) ->
Timer = inet:start_timer(Time),
@@ -220,7 +220,7 @@ listen(Port, Opts0) ->
-spec accept(ListenSocket) -> {ok, Socket} | {error, Reason} when
ListenSocket :: socket(),
Socket :: socket(),
- Reason :: closed | timeout | system_limit | inet:posix().
+ Reason :: closed | system_limit | inet:posix().
accept(S) ->
case inet_db:lookup_socket(S) of
@@ -312,7 +312,7 @@ recv(S, Length) when is_port(S) ->
Length :: non_neg_integer(),
Timeout :: timeout(),
Packet :: string() | binary() | HttpPacket,
- Reason :: closed | inet:posix(),
+ Reason :: closed | timeout | inet:posix(),
HttpPacket :: term().
recv(S, Length, Time) when is_port(S) ->
diff --git a/lib/kernel/src/gen_udp.erl b/lib/kernel/src/gen_udp.erl
index d6e8652e77..fad7b2f887 100644
--- a/lib/kernel/src/gen_udp.erl
+++ b/lib/kernel/src/gen_udp.erl
@@ -95,7 +95,7 @@
-spec open(Port) -> {ok, Socket} | {error, Reason} when
Port :: inet:port_number(),
Socket :: socket(),
- Reason :: inet:posix().
+ Reason :: system_limit | inet:posix().
open(Port) ->
open(Port, []).
@@ -112,7 +112,7 @@ open(Port) ->
| {bind_to_device, binary()}
| option(),
Socket :: socket(),
- Reason :: inet:posix().
+ Reason :: system_limit | inet:posix().
open(Port, Opts0) ->
{Mod, Opts} = inet:udp_module(Opts0),
@@ -186,7 +186,7 @@ recv(S,Len) when is_port(S), is_integer(Len) ->
Port :: inet:port_number(),
AncData :: inet:ancillary_data(),
Packet :: string() | binary(),
- Reason :: not_owner | inet:posix().
+ Reason :: not_owner | timeout | inet:posix().
recv(S,Len,Time) when is_port(S) ->
case inet_db:lookup_socket(S) of
diff --git a/lib/kernel/src/inet_tcp_dist.erl b/lib/kernel/src/inet_tcp_dist.erl
index c37212b0f9..c5a114a9ef 100644
--- a/lib/kernel/src/inet_tcp_dist.erl
+++ b/lib/kernel/src/inet_tcp_dist.erl
@@ -212,6 +212,7 @@ do_accept(Driver, Kernel, AcceptPid, Socket, MyNode, Allowed, SetupTime) ->
[{active, true},
{deliver, port},
{packet, 4},
+ binary,
nodelay()])
end,
f_getll = fun(S) ->
diff --git a/lib/kernel/src/logger.erl b/lib/kernel/src/logger.erl
index abdd9a9ceb..7d36640f52 100644
--- a/lib/kernel/src/logger.erl
+++ b/lib/kernel/src/logger.erl
@@ -60,6 +60,7 @@
-export([compare_levels/2]).
-export([set_process_metadata/1, update_process_metadata/1,
unset_process_metadata/0, get_process_metadata/0]).
+-export([i/0, i/1]).
%% Basic report formatting
-export([format_report/1, format_otp_report/1]).
@@ -647,6 +648,142 @@ get_config() ->
proxy=>get_proxy_config(),
module_levels=>lists:keysort(1,get_module_level())}.
+-spec i() -> ok.
+i() ->
+ #{primary := Primary,
+ handlers := HandlerConfigs,
+ proxy := Proxy,
+ module_levels := Modules} = get_config(),
+ M = modifier(),
+ i_primary(Primary,M),
+ i_handlers(HandlerConfigs,M),
+ i_proxy(Proxy,M),
+ i_modules(Modules,M).
+
+-spec i(What) -> ok when
+ What :: primary | handlers | proxy | modules | handler_id().
+i(primary) ->
+ i_primary(get_primary_config(),modifier());
+i(handlers) ->
+ i_handlers(get_handler_config(),modifier());
+i(proxy) ->
+ i_proxy(get_proxy_config(),modifier());
+i(modules) ->
+ i_modules(get_module_level(),modifier());
+i(HandlerId) when is_atom(HandlerId) ->
+ case get_handler_config(HandlerId) of
+ {ok,HandlerConfig} ->
+ i_handlers([HandlerConfig],modifier());
+ Error ->
+ Error
+ end;
+i(What) ->
+ erlang:error(badarg,[What]).
+
+
+i_primary(#{level := Level,
+ filters := Filters,
+ filter_default := FilterDefault},
+ M) ->
+ io:format("Primary configuration: ~n",[]),
+ io:format(" Level: ~p~n",[Level]),
+ io:format(" Filter Default: ~p~n", [FilterDefault]),
+ io:format(" Filters: ~n", []),
+ print_filters(" ",Filters,M).
+
+i_handlers(HandlerConfigs,M) ->
+ io:format("Handler configuration: ~n", []),
+ print_handlers(HandlerConfigs,M).
+
+i_proxy(Proxy,M) ->
+ io:format("Proxy configuration: ~n", []),
+ print_custom(" ",Proxy,M).
+
+i_modules(Modules,M) ->
+ io:format("Level set per module: ~n", []),
+ print_module_levels(Modules,M).
+
+encoding() ->
+ case lists:keyfind(encoding, 1, io:getopts()) of
+ false -> latin1;
+ {encoding, Enc} -> Enc
+ end.
+
+modifier() ->
+ modifier(encoding()).
+
+modifier(latin1) -> "";
+modifier(_) -> "t".
+
+print_filters(Indent, {Id, {Fun, Arg}}, M) ->
+ io:format("~sId: ~"++M++"p~n"
+ "~s Fun: ~"++M++"p~n"
+ "~s Arg: ~"++M++"p~n",
+ [Indent, Id, Indent, Fun, Indent, Arg]);
+print_filters(Indent,[],_M) ->
+ io:format("~s(none)~n",[Indent]);
+print_filters(Indent,Filters,M) ->
+ [print_filters(Indent,Filter,M) || Filter <- Filters],
+ ok.
+
+print_handlers(#{id := Id,
+ module := Module,
+ level := Level,
+ filters := Filters, filter_default := FilterDefault,
+ formatter := {FormatterModule,FormatterConfig}} = Config, M) ->
+ io:format(" Id: ~"++M++"p~n"
+ " Module: ~p~n"
+ " Level: ~p~n"
+ " Formatter:~n"
+ " Module: ~p~n"
+ " Config:~n",
+ [Id, Module, Level, FormatterModule]),
+ print_custom(" ",FormatterConfig,M),
+ io:format(" Filter Default: ~p~n"
+ " Filters:~n",
+ [FilterDefault]),
+ print_filters(" ",Filters,M),
+ case maps:find(config,Config) of
+ {ok,HandlerConfig} ->
+ io:format(" Handler Config:~n"),
+ print_custom(" ",HandlerConfig,M);
+ error ->
+ ok
+ end,
+ MyKeys = [filter_default, filters, formatter, level, module, id, config],
+ case maps:without(MyKeys,Config) of
+ Empty when Empty==#{} ->
+ ok;
+ Unhandled ->
+ io:format(" Custom Config:~n"),
+ print_custom(" ",Unhandled,M)
+ end;
+print_handlers([], _M) ->
+ io:format(" (none)~n");
+print_handlers(HandlerConfigs, M) ->
+ [print_handlers(HandlerConfig, M) || HandlerConfig <- HandlerConfigs],
+ ok.
+
+print_custom(Indent, {Key, Value}, M) ->
+ io:format("~s~"++M++"p: ~"++M++"p~n",[Indent,Key,Value]);
+print_custom(Indent, Map, M) when is_map(Map) ->
+ print_custom(Indent,lists:keysort(1,maps:to_list(Map)), M);
+print_custom(Indent, List, M) when is_list(List), is_tuple(hd(List)) ->
+ [print_custom(Indent, X, M) || X <- List],
+ ok;
+print_custom(Indent, Value, M) ->
+ io:format("~s~"++M++"p~n",[Indent,Value]).
+
+print_module_levels({Module,Level},M) ->
+ io:format(" Module: ~"++M++"p~n"
+ " Level: ~p~n",
+ [Module,Level]);
+print_module_levels([],_M) ->
+ io:format(" (none)~n");
+print_module_levels(Modules,M) ->
+ [print_module_levels(Module,M) || Module <- Modules],
+ ok.
+
-spec internal_init_logger() -> ok | {error,term()}.
%% This function is responsible for config of the logger
%% This is done before add_handlers because we want the
diff --git a/lib/kernel/src/logger_std_h.erl b/lib/kernel/src/logger_std_h.erl
index 0669164bb6..65f5b3876e 100644
--- a/lib/kernel/src/logger_std_h.erl
+++ b/lib/kernel/src/logger_std_h.erl
@@ -217,17 +217,24 @@ open_log_file(HandlerName, FileInfo) ->
Error -> Error
end.
-do_open_log_file({file,File}) ->
- do_open_log_file({file,File,[raw,append,delayed_write]});
+do_open_log_file({file,FileName}) ->
+ do_open_log_file({file,FileName,[raw,append,delayed_write]});
-do_open_log_file({file,File,[]}) ->
- do_open_log_file({file,File,[raw,append,delayed_write]});
+do_open_log_file({file,FileName,[]}) ->
+ do_open_log_file({file,FileName,[raw,append,delayed_write]});
-do_open_log_file({file,File,Modes}) ->
+do_open_log_file({file,FileName,Modes}) ->
try
- case filelib:ensure_dir(File) of
+ case filelib:ensure_dir(FileName) of
ok ->
- file:open(File, Modes);
+ case file:open(FileName, Modes) of
+ {ok, Fd} ->
+ {ok,#file_info{inode=INode}} =
+ file:read_file_info(FileName),
+ {ok, {Fd, INode}};
+ Error ->
+ Error
+ end;
Error ->
Error
end
@@ -237,7 +244,7 @@ do_open_log_file({file,File,Modes}) ->
close_log_file(Std) when Std == standard_io; Std == standard_error ->
ok;
-close_log_file(Fd) ->
+close_log_file({Fd,_}) ->
_ = file:datasync(Fd),
_ = file:close(Fd).
@@ -296,9 +303,9 @@ file_ctrl_init(HandlerName, FileInfo, Starter) when is_tuple(FileInfo) ->
process_flag(message_queue_data, off_heap),
FileName = element(2, FileInfo),
case do_open_log_file(FileInfo) of
- {ok,Fd} ->
+ {ok,File} ->
Starter ! {self(),ok},
- file_ctrl_loop(Fd, FileName, false, ok, ok, HandlerName);
+ file_ctrl_loop(File, FileName, false, ok, ok, HandlerName);
{error,Reason} ->
Starter ! {self(),{error,{open_failed,FileName,Reason}}}
end;
@@ -306,39 +313,43 @@ file_ctrl_init(HandlerName, StdDev, Starter) ->
Starter ! {self(),ok},
file_ctrl_loop(StdDev, StdDev, false, ok, ok, HandlerName).
-file_ctrl_loop(Fd, DevName, Synced,
+file_ctrl_loop(File, DevName, Synced,
PrevWriteResult, PrevSyncResult, HandlerName) ->
receive
%% asynchronous event
{log,Bin} ->
- Fd1 = ensure(Fd, DevName),
- Result = write_to_dev(Fd1, Bin, DevName, PrevWriteResult, HandlerName),
- file_ctrl_loop(Fd1, DevName, false,
+ File1 = ensure(File, DevName),
+ Result = write_to_dev(File1, Bin, DevName,
+ PrevWriteResult, HandlerName),
+ file_ctrl_loop(File1, DevName, false,
Result, PrevSyncResult, HandlerName);
%% synchronous event
{{log,Bin},{From,MRef}} ->
- Fd1 = ensure(Fd, DevName),
- Result = write_to_dev(Fd1, Bin, DevName, PrevWriteResult, HandlerName),
+ File1 = ensure(File, DevName),
+ Result = write_to_dev(File1, Bin, DevName,
+ PrevWriteResult, HandlerName),
From ! {MRef,ok},
- file_ctrl_loop(Fd1, DevName, false,
+ file_ctrl_loop(File1, DevName, false,
Result, PrevSyncResult, HandlerName);
filesync ->
- Fd1 = ensure(Fd, DevName),
- Result = sync_dev(Fd1, DevName, Synced, PrevSyncResult, HandlerName),
- file_ctrl_loop(Fd1, DevName, true,
+ File1 = ensure(File, DevName),
+ Result = sync_dev(File1, DevName, Synced,
+ PrevSyncResult, HandlerName),
+ file_ctrl_loop(File1, DevName, true,
PrevWriteResult, Result, HandlerName);
{filesync,{From,MRef}} ->
- Fd1 = ensure(Fd, DevName),
- Result = sync_dev(Fd1, DevName, Synced, PrevSyncResult, HandlerName),
+ File1 = ensure(File, DevName),
+ Result = sync_dev(File1, DevName, Synced,
+ PrevSyncResult, HandlerName),
From ! {MRef,ok},
- file_ctrl_loop(Fd1, DevName, true,
+ file_ctrl_loop(File1, DevName, true,
PrevWriteResult, Result, HandlerName);
stop ->
- _ = close_log_file(Fd),
+ _ = close_log_file(File),
stopped
end.
@@ -347,16 +358,16 @@ file_ctrl_loop(Fd, DevName, Synced,
%% logrotate)
ensure(Fd,DevName) when is_atom(DevName) ->
Fd;
-ensure(Fd,FileName) ->
+ensure({Fd,INode},FileName) ->
case file:read_file_info(FileName) of
- {ok,_} ->
- Fd;
+ {ok,#file_info{inode=INode}} ->
+ {Fd,INode};
_ ->
_ = file:close(Fd),
_ = file:close(Fd), % delayed_write cause close not to close
case do_open_log_file({file,FileName}) of
- {ok,Fd1} ->
- Fd1;
+ {ok,File} ->
+ File;
Error ->
exit({could_not_reopen_file,Error})
end
@@ -365,13 +376,13 @@ ensure(Fd,FileName) ->
write_to_dev(DevName, Bin, _DevName, _PrevWriteResult, _HandlerName)
when is_atom(DevName) ->
io:put_chars(DevName, Bin);
-write_to_dev(Fd, Bin, FileName, PrevWriteResult, HandlerName) ->
+write_to_dev({Fd,_}, Bin, FileName, PrevWriteResult, HandlerName) ->
Result = ?file_write(Fd, Bin),
maybe_notify_error(write,Result,PrevWriteResult,FileName,HandlerName).
-sync_dev(_Fd, _FileName, true, PrevSyncResult, _HandlerName) ->
+sync_dev(_, _FileName, true, PrevSyncResult, _HandlerName) ->
PrevSyncResult;
-sync_dev(Fd, FileName, false, PrevSyncResult, HandlerName) ->
+sync_dev({Fd,_}, FileName, false, PrevSyncResult, HandlerName) ->
Result = ?file_datasync(Fd),
maybe_notify_error(filesync,Result,PrevSyncResult,FileName,HandlerName).
diff --git a/lib/kernel/src/net_kernel.erl b/lib/kernel/src/net_kernel.erl
index 4915193196..83d3b4b5e1 100644
--- a/lib/kernel/src/net_kernel.erl
+++ b/lib/kernel/src/net_kernel.erl
@@ -1126,14 +1126,22 @@ do_disconnect(Node, State) ->
{false, State}
end.
-
disconnect_pid(Pid, State) ->
exit(Pid, disconnect),
+
+ %% This code used to only use exit + recv 'EXIT' to sync,
+ %% but since OTP-22 links are no longer broken atomically
+ %% so the exit message below can arrive before any remaining
+ %% exit messages have killed the distribution port
+ Ref = erlang:monitor(process, Pid),
%% Sync wait for connection to die!!!
receive
- {'EXIT',Pid,Reason} ->
- {_,State1} = handle_exit(Pid, Reason, State),
- {true, State1}
+ {'DOWN',Ref,_,_,_} ->
+ receive
+ {'EXIT',Pid,Reason} ->
+ {_,State1} = handle_exit(Pid, Reason, State),
+ {true, State1}
+ end
end.
%%
diff --git a/lib/kernel/test/logger_SUITE.erl b/lib/kernel/test/logger_SUITE.erl
index d831d0d108..2dad651f9c 100644
--- a/lib/kernel/test/logger_SUITE.erl
+++ b/lib/kernel/test/logger_SUITE.erl
@@ -101,7 +101,8 @@ all() ->
compare_levels,
process_metadata,
app_config,
- kernel_config].
+ kernel_config,
+ pretty_print].
start_stop(_Config) ->
S = whereis(logger),
@@ -1141,6 +1142,61 @@ kernel_config(Config) ->
ok.
+pretty_print(Config) ->
+ ok = logger:add_handler(?FUNCTION_NAME,logger_std_h,#{}),
+ ok = logger:set_module_level([module1,module2],debug),
+
+ ct:capture_start(),
+ logger:i(),
+ ct:capture_stop(),
+ I0 = ct:capture_get(),
+
+ ct:capture_start(),
+ logger:i(primary),
+ ct:capture_stop(),
+ IPrim = ct:capture_get(),
+
+ ct:capture_start(),
+ logger:i(handlers),
+ ct:capture_stop(),
+ IHs = ct:capture_get(),
+
+ ct:capture_start(),
+ logger:i(proxy),
+ ct:capture_stop(),
+ IProxy = ct:capture_get(),
+
+ ct:capture_start(),
+ logger:i(modules),
+ ct:capture_stop(),
+ IMs = ct:capture_get(),
+
+ I02 = lists:append([IPrim,IHs,IProxy,IMs]),
+ %% ct:log("~p~n",[I0]),
+ %% ct:log("~p~n",[I02]),
+ I0 = I02,
+
+ ct:capture_start(),
+ logger:i(handlers),
+ ct:capture_stop(),
+ IHs = ct:capture_get(),
+
+ Ids = logger:get_handler_ids(),
+ IHs2 =
+ lists:append(
+ [begin
+ ct:capture_start(),
+ logger:i(Id),
+ ct:capture_stop(),
+ [_|IH] = ct:capture_get(),
+ IH
+ end || Id <- Ids]),
+
+ %% ct:log("~p~n",[IHs]),
+ %% ct:log("~p~n",[["Handler configuration: \n"|IHs2]]),
+ IHs = ["Handler configuration: \n"|IHs2],
+ ok.
+
%%%-----------------------------------------------------------------
%%% Internal
check_logged(Level,Format,Args,Meta) ->
diff --git a/lib/kernel/test/logger_disk_log_h_SUITE.erl b/lib/kernel/test/logger_disk_log_h_SUITE.erl
index 9bbec42de8..13b30835a1 100644
--- a/lib/kernel/test/logger_disk_log_h_SUITE.erl
+++ b/lib/kernel/test/logger_disk_log_h_SUITE.erl
@@ -293,7 +293,7 @@ logging(Config) ->
ok = start_and_add(Name, #{filter_default=>log,
formatter=>{?MODULE,self()}},
#{file => LogFile}),
- MsgFormatter = fun(Term) -> {io_lib:format("Term:~p",[Term]),[]} end,
+ MsgFormatter = fun(Term) -> {"Term:~p",[Term]} end,
logger:notice([{x,y}], #{report_cb => MsgFormatter}),
logger:notice([{x,y}], #{}),
ct:pal("Checking contents of ~p", [?log_no(LogFile,1)]),
diff --git a/lib/kernel/test/logger_std_h_SUITE.erl b/lib/kernel/test/logger_std_h_SUITE.erl
index 484d914ec3..b2c2c8ba67 100644
--- a/lib/kernel/test/logger_std_h_SUITE.erl
+++ b/lib/kernel/test/logger_std_h_SUITE.erl
@@ -141,7 +141,8 @@ all() ->
mem_kill_std,
restart_after,
handler_requests_under_load,
- recreate_deleted_log
+ recreate_deleted_log,
+ reopen_changed_log
].
add_remove_instance_tty(_Config) ->
@@ -1269,6 +1270,21 @@ recreate_deleted_log(Config) ->
recreate_deleted_log(cleanup, _Config) ->
ok = stop_handler(?MODULE).
+reopen_changed_log(Config) ->
+ {Log,_HConfig,_StdHConfig} =
+ start_handler(?MODULE, ?FUNCTION_NAME, Config),
+ logger:notice("first",?domain),
+ logger_std_h:filesync(?MODULE),
+ ok = file:rename(Log,Log++".old"),
+ ok = file:write_file(Log,""),
+ logger:notice("second",?domain),
+ logger_std_h:filesync(?MODULE),
+ {ok,<<"first\n">>} = file:read_file(Log++".old"),
+ {ok,<<"second\n">>} = file:read_file(Log),
+ ok.
+reopen_changed_log(cleanup, _Config) ->
+ ok = stop_handler(?MODULE).
+
%%%-----------------------------------------------------------------
%%%
send_requests(TO, Reqs = [{Mod,Func,Args,Res}|Rs]) ->
diff --git a/lib/mnesia/src/mnesia_dumper.erl b/lib/mnesia/src/mnesia_dumper.erl
index a2880d6cf4..964f1f5b7d 100644
--- a/lib/mnesia/src/mnesia_dumper.erl
+++ b/lib/mnesia/src/mnesia_dumper.erl
@@ -272,17 +272,12 @@ do_insert_rec(Tid, Rec, InPlace, InitBy, LogV) ->
end
end,
D = Rec#commit.disc_copies,
- ExtOps = commit_ext(Rec),
insert_ops(Tid, disc_copies, D, InPlace, InitBy, LogV),
- [insert_ops(Tid, Ext, Ops, InPlace, InitBy, LogV) ||
- {Ext, Ops} <- ExtOps,
- storage_semantics(Ext) == disc_copies],
+ insert_ext_ops(Tid, commit_ext(Rec), InPlace, InitBy),
case InitBy of
startup ->
DO = Rec#commit.disc_only_copies,
- insert_ops(Tid, disc_only_copies, DO, InPlace, InitBy, LogV),
- [insert_ops(Tid, Ext, Ops, InPlace, InitBy, LogV) ||
- {Ext, Ops} <- ExtOps, storage_semantics(Ext) == disc_only_copies];
+ insert_ops(Tid, disc_only_copies, DO, InPlace, InitBy, LogV);
_ ->
ignore
end.
@@ -290,11 +285,8 @@ do_insert_rec(Tid, Rec, InPlace, InitBy, LogV) ->
commit_ext(#commit{ext = []}) -> [];
commit_ext(#commit{ext = Ext}) ->
case lists:keyfind(ext_copies, 1, Ext) of
- {_, C} ->
- lists:foldl(fun({Ext0, Op}, D) ->
- orddict:append(Ext0, Op, D)
- end, orddict:new(), C);
- false -> []
+ {_, C} -> C;
+ false -> []
end.
update(_Tid, [], _DumperMode) ->
@@ -330,6 +322,21 @@ perform_update(Tid, SchemaOps, _DumperMode, _UseDir) ->
fatal("Schema update error ~tp ~tp", [{Reason,ST}, SchemaOps])
end.
+insert_ext_ops(Tid, ExtOps, InPlace, InitBy) ->
+ %% Note: ext ops cannot be part of pre-4.3 logs, so there's no need
+ %% to support the old operation order, as in `insert_ops'
+ lists:foreach(
+ fun ({Ext, Op}) ->
+ case storage_semantics(Ext) of
+ Semantics when Semantics == disc_copies;
+ Semantics == disc_only_copies, InitBy == startup ->
+ insert_op(Tid, Ext, Op, InPlace, InitBy);
+ _Other ->
+ ok
+ end
+ end,
+ ExtOps).
+
insert_ops(_Tid, _Storage, [], _InPlace, _InitBy, _) -> ok;
insert_ops(Tid, Storage, [Op], InPlace, InitBy, Ver) when Ver >= "4.3"->
insert_op(Tid, Storage, Op, InPlace, InitBy),
diff --git a/lib/observer/src/cdv_detail_wx.erl b/lib/observer/src/cdv_detail_wx.erl
index 4b1984c394..5e1137511a 100644
--- a/lib/observer/src/cdv_detail_wx.erl
+++ b/lib/observer/src/cdv_detail_wx.erl
@@ -84,8 +84,9 @@ destroy_progress(_) ->
ok.
init(Id,ParentFrame,Callback,App,Parent,{Title,Info,TW}) ->
+ Scale = observer_wx:get_scale(),
Frame=wxFrame:new(ParentFrame, ?wxID_ANY, [Title],
- [{style, ?wxDEFAULT_FRAME_STYLE}, {size, {850,600}}]),
+ [{style, ?wxDEFAULT_FRAME_STYLE}, {size, {Scale*850,Scale*600}}]),
MenuBar = wxMenuBar:new(),
create_menus(MenuBar),
wxFrame:setMenuBar(Frame, MenuBar),
diff --git a/lib/observer/src/cdv_table_wx.erl b/lib/observer/src/cdv_table_wx.erl
index 0f28a51017..0cad272262 100644
--- a/lib/observer/src/cdv_table_wx.erl
+++ b/lib/observer/src/cdv_table_wx.erl
@@ -50,11 +50,12 @@ init([ParentWin, {ColumnSpec,Info,TW}]) ->
end,
Grid = wxListCtrl:new(ParentWin, [{style, Style}]),
Li = wxListItem:new(),
+ Scale = observer_wx:get_scale(),
AddListEntry = fun({Name, Align, DefSize}, Col) ->
wxListItem:setText(Li, Name),
wxListItem:setAlign(Li, Align),
wxListCtrl:insertColumn(Grid, Col, Li),
- wxListCtrl:setColumnWidth(Grid, Col, DefSize),
+ wxListCtrl:setColumnWidth(Grid, Col, DefSize*Scale),
Col + 1
end,
lists:foldl(AddListEntry, 0, ColumnSpec),
diff --git a/lib/observer/src/cdv_virtual_list_wx.erl b/lib/observer/src/cdv_virtual_list_wx.erl
index 2702301021..14877b7eab 100644
--- a/lib/observer/src/cdv_virtual_list_wx.erl
+++ b/lib/observer/src/cdv_virtual_list_wx.erl
@@ -132,11 +132,12 @@ create_list_box(Panel, Holder, Callback, Owner) ->
end}
]),
Li = wxListItem:new(),
+ Scale = observer_wx:get_scale(),
AddListEntry = fun({Name, Align, DefSize}, Col) ->
wxListItem:setText(Li, Name),
wxListItem:setAlign(Li, Align),
wxListCtrl:insertColumn(ListCtrl, Col, Li),
- wxListCtrl:setColumnWidth(ListCtrl, Col, DefSize),
+ wxListCtrl:setColumnWidth(ListCtrl, Col, DefSize*Scale),
Col + 1
end,
ListItems = Callback:col_spec(),
diff --git a/lib/observer/src/cdv_wx.erl b/lib/observer/src/cdv_wx.erl
index f64a278a64..7100cc8790 100644
--- a/lib/observer/src/cdv_wx.erl
+++ b/lib/observer/src/cdv_wx.erl
@@ -101,8 +101,9 @@ init(File0) ->
{ok,CdvServer} = crashdump_viewer:start_link(),
catch wxSystemOptions:setOption("mac.listctrl.always_use_generic", 1),
+ Scale = observer_wx:get_scale(),
Frame = wxFrame:new(wx:null(), ?wxID_ANY, "Crashdump Viewer",
- [{size, {850, 600}}, {style, ?wxDEFAULT_FRAME_STYLE}]),
+ [{size, {Scale*850, Scale*600}}, {style, ?wxDEFAULT_FRAME_STYLE}]),
IconFile = filename:join(code:priv_dir(observer), "erlang_observer.png"),
Icon = wxIcon:new(IconFile, [{type,?wxBITMAP_TYPE_PNG}]),
wxFrame:setIcon(Frame, Icon),
diff --git a/lib/observer/src/observer_alloc_wx.erl b/lib/observer/src/observer_alloc_wx.erl
index 54e246f247..da47a30fb1 100644
--- a/lib/observer/src/observer_alloc_wx.erl
+++ b/lib/observer/src/observer_alloc_wx.erl
@@ -282,11 +282,12 @@ create_mem_info(Parent) ->
Grid = wxListCtrl:new(Parent, [{style, Style}]),
Li = wxListItem:new(),
+ Scale = observer_wx:get_scale(),
AddListEntry = fun({Name, Align, DefSize}, Col) ->
wxListItem:setText(Li, Name),
wxListItem:setAlign(Li, Align),
wxListCtrl:insertColumn(Grid, Col, Li),
- wxListCtrl:setColumnWidth(Grid, Col, DefSize),
+ wxListCtrl:setColumnWidth(Grid, Col, DefSize*Scale),
Col + 1
end,
ListItems = [{"Allocator Type", ?wxLIST_FORMAT_LEFT, 200},
diff --git a/lib/observer/src/observer_app_wx.erl b/lib/observer/src/observer_app_wx.erl
index 2a481966da..8c3eef5411 100644
--- a/lib/observer/src/observer_app_wx.erl
+++ b/lib/observer/src/observer_app_wx.erl
@@ -117,16 +117,19 @@ init([Notebook, Parent, _Config]) ->
UseGC = haveGC(),
Version28 = ?wxMAJOR_VERSION =:= 2 andalso ?wxMINOR_VERSION =:= 8,
+ Scale = observer_wx:get_scale(),
Font = case os:type() of
{unix,_} when UseGC, Version28 ->
- wxFont:new(12,?wxFONTFAMILY_DECORATIVE,?wxFONTSTYLE_NORMAL,?wxFONTWEIGHT_NORMAL);
+ wxFont:new(Scale * 12,?wxFONTFAMILY_DECORATIVE,?wxFONTSTYLE_NORMAL,?wxFONTWEIGHT_NORMAL);
_ ->
- wxSystemSettings:getFont(?wxSYS_DEFAULT_GUI_FONT)
+ Font0 = wxSystemSettings:getFont(?wxSYS_DEFAULT_GUI_FONT),
+ wxFont:setPointSize(Font0, Scale * wxFont:getPointSize(Font0)),
+ Font0
end,
SelCol = wxSystemSettings:getColour(?wxSYS_COLOUR_HIGHLIGHT),
GreyBrush = wxBrush:new({230,230,240}),
SelBrush = wxBrush:new(SelCol),
- LinkPen = wxPen:new(SelCol, [{width, 2}]),
+ LinkPen = wxPen:new(SelCol, [{width, Scale * 2}]),
process_flag(trap_exit, true),
{Panel, #state{parent=Parent,
panel =Panel,
@@ -134,7 +137,7 @@ init([Notebook, Parent, _Config]) ->
app_w =DrawingArea,
usegc = UseGC,
paint=#paint{font = Font,
- pen = wxPen:new({80,80,80}, [{width, 2}]),
+ pen = wxPen:new({80,80,80}, [{width, Scale * 2}]),
brush= GreyBrush,
sel = SelBrush,
links= LinkPen
diff --git a/lib/observer/src/observer_perf_wx.erl b/lib/observer/src/observer_perf_wx.erl
index 21c6d26f49..79271addf2 100644
--- a/lib/observer/src/observer_perf_wx.erl
+++ b/lib/observer/src/observer_perf_wx.erl
@@ -110,25 +110,26 @@ setup_graph_drawing(Panels) ->
_ = [Do(Panel) || Panel <- Panels],
UseGC = haveGC(),
Version28 = ?wxMAJOR_VERSION =:= 2 andalso ?wxMINOR_VERSION =:= 8,
+ Scale = observer_wx:get_scale(),
{Font, SmallFont}
= if UseGC, Version28 ->
%% Def font is really small when using Graphics contexts in 2.8
%% Hardcode it
- F = wxFont:new(12,?wxFONTFAMILY_DECORATIVE,?wxFONTSTYLE_NORMAL,?wxFONTWEIGHT_BOLD),
- SF = wxFont:new(10, ?wxFONTFAMILY_DECORATIVE, ?wxFONTSTYLE_NORMAL, ?wxFONTWEIGHT_NORMAL),
+ F = wxFont:new(Scale * 12,?wxFONTFAMILY_DECORATIVE,?wxFONTSTYLE_NORMAL,?wxFONTWEIGHT_BOLD),
+ SF = wxFont:new(Scale * 10, ?wxFONTFAMILY_DECORATIVE, ?wxFONTSTYLE_NORMAL, ?wxFONTWEIGHT_NORMAL),
{F, SF};
true ->
DefFont = wxSystemSettings:getFont(?wxSYS_DEFAULT_GUI_FONT),
DefSize = wxFont:getPointSize(DefFont),
DefFamily = wxFont:getFamily(DefFont),
- F = wxFont:new(DefSize-1, DefFamily, ?wxFONTSTYLE_NORMAL, ?wxFONTWEIGHT_BOLD),
- SF = wxFont:new(DefSize-2, DefFamily, ?wxFONTSTYLE_NORMAL, ?wxFONTWEIGHT_NORMAL),
+ F = wxFont:new(Scale * (DefSize-1), DefFamily, ?wxFONTSTYLE_NORMAL, ?wxFONTWEIGHT_BOLD),
+ SF = wxFont:new(Scale * (DefSize-2), DefFamily, ?wxFONTSTYLE_NORMAL, ?wxFONTWEIGHT_NORMAL),
{F, SF}
end,
- BlackPen = wxPen:new({0,0,0}, [{width, 1}]),
- Pens = [wxPen:new(Col, [{width, 1}, {style, ?wxSOLID}])
+ BlackPen = wxPen:new({0,0,0}, [{width, Scale}]),
+ Pens = [wxPen:new(Col, [{width, Scale}, {style, ?wxSOLID}])
|| Col <- tuple_to_list(colors())],
- DotPens = [wxPen:new(Col, [{width, 1}, {style, ?wxDOT}])
+ DotPens = [wxPen:new(Col, [{width, Scale}, {style, ?wxDOT}])
|| Col <- tuple_to_list(colors())],
#paint{usegc = UseGC,
font = Font,
diff --git a/lib/observer/src/observer_port_wx.erl b/lib/observer/src/observer_port_wx.erl
index 445f3dd6b1..00cf1b5fba 100644
--- a/lib/observer/src/observer_port_wx.erl
+++ b/lib/observer/src/observer_port_wx.erl
@@ -96,11 +96,12 @@ init([Notebook, Parent, Config]) ->
wxListCtrl:setColumnWidth(Grid, Col, DefSize),
Col + 1
end,
- ListItems = [{"Id", ?wxLIST_FORMAT_LEFT, 150},
- {"Connected", ?wxLIST_FORMAT_LEFT, 150},
- {"Name", ?wxLIST_FORMAT_LEFT, 150},
- {"Controls", ?wxLIST_FORMAT_LEFT, 200},
- {"Slot", ?wxLIST_FORMAT_RIGHT, 50}],
+ Scale = observer_wx:get_scale(),
+ ListItems = [{"Id", ?wxLIST_FORMAT_LEFT, Scale*150},
+ {"Connected", ?wxLIST_FORMAT_LEFT, Scale*150},
+ {"Name", ?wxLIST_FORMAT_LEFT, Scale*150},
+ {"Controls", ?wxLIST_FORMAT_LEFT, Scale*200},
+ {"Slot", ?wxLIST_FORMAT_RIGHT, Scale*50}],
lists:foldl(AddListEntry, 0, ListItems),
wxListItem:destroy(Li),
@@ -461,10 +462,11 @@ display_port_info(Parent, PortRec, Opened) ->
do_display_port_info(Parent0, PortRec) ->
Parent = observer_lib:get_wx_parent(Parent0),
Title = "Port Info: " ++ PortRec#port.id_str,
+ Scale = observer_wx:get_scale(),
Frame = wxMiniFrame:new(Parent, ?wxID_ANY, Title,
[{style, ?wxSYSTEM_MENU bor ?wxCAPTION
bor ?wxCLOSE_BOX bor ?wxRESIZE_BORDER},
- {size,{600,400}}]),
+ {size,{Scale * 600, Scale * 400}}]),
ScrolledWin = wxScrolledWindow:new(Frame,[{style,?wxHSCROLL bor ?wxVSCROLL}]),
wxScrolledWindow:enableScrolling(ScrolledWin,true,true),
wxScrolledWindow:setScrollbars(ScrolledWin,20,20,0,0),
diff --git a/lib/observer/src/observer_pro_wx.erl b/lib/observer/src/observer_pro_wx.erl
index 04e654a37e..4ab4a78462 100644
--- a/lib/observer/src/observer_pro_wx.erl
+++ b/lib/observer/src/observer_pro_wx.erl
@@ -163,13 +163,14 @@ create_list_box(Panel, Holder) ->
wxListCtrl:setColumnWidth(ListCtrl, Col, DefSize),
Col + 1
end,
- ListItems = [{"Pid", ?wxLIST_FORMAT_CENTRE, 120},
- {"Name or Initial Func", ?wxLIST_FORMAT_LEFT, 200},
-%% {"Time", ?wxLIST_FORMAT_CENTRE, 50},
- {"Reds", ?wxLIST_FORMAT_RIGHT, 100},
- {"Memory", ?wxLIST_FORMAT_RIGHT, 100},
- {"MsgQ", ?wxLIST_FORMAT_RIGHT, 50},
- {"Current Function", ?wxLIST_FORMAT_LEFT, 200}],
+ Scale = observer_wx:get_scale(),
+ ListItems = [{"Pid", ?wxLIST_FORMAT_CENTRE, Scale*120},
+ {"Name or Initial Func", ?wxLIST_FORMAT_LEFT, Scale*200},
+%% {"Time", ?wxLIST_FORMAT_CENTRE, Scale*50},
+ {"Reds", ?wxLIST_FORMAT_RIGHT, Scale*100},
+ {"Memory", ?wxLIST_FORMAT_RIGHT, Scale*100},
+ {"MsgQ", ?wxLIST_FORMAT_RIGHT, Scale*50},
+ {"Current Function", ?wxLIST_FORMAT_LEFT, Scale*200}],
lists:foldl(AddListEntry, 0, ListItems),
wxListItem:destroy(Li),
diff --git a/lib/observer/src/observer_procinfo.erl b/lib/observer/src/observer_procinfo.erl
index f436886735..bd5fed0951 100644
--- a/lib/observer/src/observer_procinfo.erl
+++ b/lib/observer/src/observer_procinfo.erl
@@ -59,8 +59,9 @@ init([Pid, ParentFrame, Parent]) ->
{registered_name, Registered} -> io_lib:format("~tp (~p)",[Registered, Pid]);
undefined -> throw(process_undefined)
end,
+ Scale = observer_wx:get_scale(),
Frame=wxFrame:new(ParentFrame, ?wxID_ANY, [atom_to_list(node(Pid)), $:, Title],
- [{style, ?wxDEFAULT_FRAME_STYLE}, {size, {850,600}}]),
+ [{style, ?wxDEFAULT_FRAME_STYLE}, {size, {Scale * 850, Scale * 600}}]),
MenuBar = wxMenuBar:new(),
create_menus(MenuBar),
wxFrame:setMenuBar(Frame, MenuBar),
@@ -245,12 +246,13 @@ init_dict_page(Parent, Pid, Table) ->
init_stack_page(Parent, Pid) ->
LCtrl = wxListCtrl:new(Parent, [{style, ?wxLC_REPORT bor ?wxLC_HRULES}]),
Li = wxListItem:new(),
+ Scale = observer_wx:get_scale(),
wxListItem:setText(Li, "Module:Function/Arg"),
wxListCtrl:insertColumn(LCtrl, 0, Li),
- wxListCtrl:setColumnWidth(LCtrl, 0, 300),
+ wxListCtrl:setColumnWidth(LCtrl, 0, Scale * 300),
wxListItem:setText(Li, "File:LineNumber"),
wxListCtrl:insertColumn(LCtrl, 1, Li),
- wxListCtrl:setColumnWidth(LCtrl, 1, 300),
+ wxListCtrl:setColumnWidth(LCtrl, 1, Scale * 300),
wxListItem:destroy(Li),
Update = fun() ->
case observer_wx:try_rpc(node(Pid), erlang, process_info,
diff --git a/lib/observer/src/observer_trace_wx.erl b/lib/observer/src/observer_trace_wx.erl
index 2c3b46a3a1..f458c8c34a 100644
--- a/lib/observer/src/observer_trace_wx.erl
+++ b/lib/observer/src/observer_trace_wx.erl
@@ -188,8 +188,9 @@ create_proc_port_view(Parent) ->
wxListCtrl:setColumnWidth(Procs, Col, DefSize),
Col + 1
end,
- ProcListItems = [{"Process Id", ?wxLIST_FORMAT_CENTER, 120},
- {"Trace Options", ?wxLIST_FORMAT_LEFT, 300}],
+ Scale = observer_wx:get_scale(),
+ ProcListItems = [{"Process Id", ?wxLIST_FORMAT_CENTER, Scale*120},
+ {"Trace Options", ?wxLIST_FORMAT_LEFT, Scale*300}],
lists:foldl(AddProc, 0, ProcListItems),
AddPort = fun({Name, Align, DefSize}, Col) ->
@@ -199,8 +200,8 @@ create_proc_port_view(Parent) ->
wxListCtrl:setColumnWidth(Ports, Col, DefSize),
Col + 1
end,
- PortListItems = [{"Port Id", ?wxLIST_FORMAT_CENTER, 120},
- {"Trace Options", ?wxLIST_FORMAT_LEFT, 300}],
+ PortListItems = [{"Port Id", ?wxLIST_FORMAT_CENTER, Scale*120},
+ {"Trace Options", ?wxLIST_FORMAT_LEFT, Scale*300}],
lists:foldl(AddPort, 0, PortListItems),
wxListItem:destroy(Li),
@@ -242,14 +243,15 @@ create_matchspec_view(Parent) ->
Funcs = wxListCtrl:new(Splitter, [{winid, ?FUNCS_WIN}, {style, Style}]),
Li = wxListItem:new(),
+ Scale = observer_wx:get_scale(),
wxListItem:setText(Li, "Modules"),
wxListCtrl:insertColumn(Modules, 0, Li),
wxListItem:setText(Li, "Functions"),
wxListCtrl:insertColumn(Funcs, 0, Li),
- wxListCtrl:setColumnWidth(Funcs, 0, 150),
+ wxListCtrl:setColumnWidth(Funcs, 0, Scale*150),
wxListItem:setText(Li, "Match Spec"),
wxListCtrl:insertColumn(Funcs, 1, Li),
- wxListCtrl:setColumnWidth(Funcs, 1, 300),
+ wxListCtrl:setColumnWidth(Funcs, 1, Scale*300),
wxListItem:destroy(Li),
wxSplitterWindow:setSashGravity(Splitter, 0.0),
@@ -969,7 +971,8 @@ output_file(true, true, Opts) ->
create_logwindow(_Parent, false) -> {false, false};
create_logwindow(Parent, true) ->
- LogWin = wxFrame:new(Parent, ?LOG_WIN, "Trace Log", [{size, {750, 800}}]),
+ Scale = observer_wx:get_scale(),
+ LogWin = wxFrame:new(Parent, ?LOG_WIN, "Trace Log", [{size, {750*Scale, 800*Scale}}]),
MB = wxMenuBar:new(),
File = wxMenu:new(),
wxMenu:append(File, ?LOG_CLEAR, "Clear Log\tCtrl-C"),
diff --git a/lib/observer/src/observer_traceoptions_wx.erl b/lib/observer/src/observer_traceoptions_wx.erl
index ea292b92af..514d55ff24 100644
--- a/lib/observer/src/observer_traceoptions_wx.erl
+++ b/lib/observer/src/observer_traceoptions_wx.erl
@@ -167,9 +167,10 @@ select_nodes(Parent, Nodes) ->
check_selector(Parent, Choices).
module_selector(Parent, Node) ->
+ Scale = observer_wx:get_scale(),
Dialog = wxDialog:new(Parent, ?wxID_ANY, "Select Module or Event",
[{style, ?wxDEFAULT_DIALOG_STYLE bor ?wxRESIZE_BORDER},
- {size, {400, 400}}]),
+ {size, {400*Scale, 400*Scale}}]),
Panel = wxPanel:new(Dialog),
PanelSz = wxBoxSizer:new(?wxVERTICAL),
MainSz = wxBoxSizer:new(?wxVERTICAL),
@@ -237,9 +238,10 @@ function_selector(Parent, Node, Module) ->
end.
check_selector(Parent, ParsedChoices) ->
+ Scale = observer_wx:get_scale(),
Dialog = wxDialog:new(Parent, ?wxID_ANY, "Trace Functions",
[{style, ?wxDEFAULT_DIALOG_STYLE bor ?wxRESIZE_BORDER},
- {size, {400, 400}}]),
+ {size, {400*Scale, 400*Scale}}]),
Panel = wxPanel:new(Dialog),
PanelSz = wxBoxSizer:new(?wxVERTICAL),
@@ -331,9 +333,10 @@ select_matchspec(Pid, Parent, AllMatchSpecs, Key) ->
{value,{Key,MSs0},Rest} -> {MSs0,Rest};
false -> {[],AllMatchSpecs}
end,
+ Scale = observer_wx:get_scale(),
Dialog = wxDialog:new(Parent, ?wxID_ANY, "Trace Match Specifications",
[{style, ?wxDEFAULT_DIALOG_STYLE bor ?wxRESIZE_BORDER},
- {size, {400, 400}}]),
+ {size, {400*Scale, 400*Scale}}]),
Panel = wxPanel:new(Dialog),
PanelSz = wxBoxSizer:new(?wxVERTICAL),
diff --git a/lib/observer/src/observer_tv_table.erl b/lib/observer/src/observer_tv_table.erl
index d6dcee2cda..7bd67a0f0b 100644
--- a/lib/observer/src/observer_tv_table.erl
+++ b/lib/observer/src/observer_tv_table.erl
@@ -99,7 +99,8 @@ init([Parent, Opts]) ->
ets -> "TV Ets: " ++ Title0;
mnesia -> "TV Mnesia: " ++ Title0
end,
- Frame = wxFrame:new(Parent, ?wxID_ANY, Title, [{size, {800, 600}}]),
+ Scale = observer_wx:get_scale(),
+ Frame = wxFrame:new(Parent, ?wxID_ANY, Title, [{size, {Scale * 800, Scale * 600}}]),
IconFile = filename:join(code:priv_dir(observer), "erlang_observer.png"),
Icon = wxIcon:new(IconFile, [{type,?wxBITMAP_TYPE_PNG}]),
wxFrame:setIcon(Frame, Icon),
diff --git a/lib/observer/src/observer_tv_wx.erl b/lib/observer/src/observer_tv_wx.erl
index dfd19569fd..9743a6ed42 100644
--- a/lib/observer/src/observer_tv_wx.erl
+++ b/lib/observer/src/observer_tv_wx.erl
@@ -87,12 +87,13 @@ init([Notebook, Parent, Config]) ->
wxListCtrl:setColumnWidth(Grid, Col, DefSize),
Col + 1
end,
- ListItems = [{"Table Name", ?wxLIST_FORMAT_LEFT, 200},
- {"Objects", ?wxLIST_FORMAT_RIGHT, 100},
- {"Size (kB)", ?wxLIST_FORMAT_RIGHT, 100},
- {"Owner Pid", ?wxLIST_FORMAT_CENTER, 150},
- {"Owner Name", ?wxLIST_FORMAT_LEFT, 200},
- {"Table Id", ?wxLIST_FORMAT_LEFT, 250}
+ Scale = observer_wx:get_scale(),
+ ListItems = [{"Table Name", ?wxLIST_FORMAT_LEFT, Scale*200},
+ {"Objects", ?wxLIST_FORMAT_RIGHT, Scale*100},
+ {"Size (kB)", ?wxLIST_FORMAT_RIGHT, Scale*100},
+ {"Owner Pid", ?wxLIST_FORMAT_CENTER, Scale*150},
+ {"Owner Name", ?wxLIST_FORMAT_LEFT, Scale*200},
+ {"Table Id", ?wxLIST_FORMAT_LEFT, Scale*250}
],
lists:foldl(AddListEntry, 0, ListItems),
wxListItem:destroy(Li),
diff --git a/lib/observer/src/observer_wx.erl b/lib/observer/src/observer_wx.erl
index 1f748cb8d2..de7d821030 100644
--- a/lib/observer/src/observer_wx.erl
+++ b/lib/observer/src/observer_wx.erl
@@ -22,7 +22,7 @@
-export([start/0, stop/0]).
-export([create_menus/2, get_attrib/1, get_tracer/0, get_active_node/0, get_menubar/0,
- set_status/1, create_txt_dialog/4, try_rpc/4, return_to_localnode/2]).
+ get_scale/0, set_status/1, create_txt_dialog/4, try_rpc/4, return_to_localnode/2]).
-export([init/1, handle_event/2, handle_cast/2, terminate/2, code_change/3,
handle_call/3, handle_info/2, check_page_title/1]).
@@ -91,14 +91,24 @@ get_active_node() ->
get_menubar() ->
wx_object:call(observer, get_menubar).
+get_scale() ->
+ ScaleStr = os:getenv("OBSERVER_SCALE", "1"),
+ try list_to_integer(ScaleStr) of
+ Scale when Scale < 1 -> 1;
+ Scale -> Scale
+ catch _:_ ->
+ 1
+ end.
+
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
init(_Args) ->
register(observer, self()),
wx:new(),
catch wxSystemOptions:setOption("mac.listctrl.always_use_generic", 1),
+ Scale = get_scale(),
Frame = wxFrame:new(wx:null(), ?wxID_ANY, "Observer",
- [{size, {850, 600}}, {style, ?wxDEFAULT_FRAME_STYLE}]),
+ [{size, {Scale * 850, Scale * 600}}, {style, ?wxDEFAULT_FRAME_STYLE}]),
IconFile = filename:join(code:priv_dir(observer), "erlang_observer.png"),
Icon = wxIcon:new(IconFile, [{type,?wxBITMAP_TYPE_PNG}]),
wxFrame:setIcon(Frame, Icon),
@@ -771,7 +781,11 @@ ensure_sasl_started(Node) ->
ensure_mf_h_handler_used(Node) ->
%% is log_mf_h used ?
- Handlers = rpc:block_call(Node, gen_event, which_handlers, [error_logger]),
+ Handlers =
+ case rpc:block_call(Node, gen_event, which_handlers, [error_logger]) of
+ {badrpc,{'EXIT',noproc}} -> []; % OTP-21+ and no event handler exists
+ Hs -> Hs
+ end,
case lists:any(fun(L)-> L == log_mf_h end, Handlers) of
false -> throw("Error: log_mf_h handler not used in sasl."),
error;
diff --git a/lib/observer/test/crashdump_helper.erl b/lib/observer/test/crashdump_helper.erl
index d6b5eff9b5..84ed99afa5 100644
--- a/lib/observer/test/crashdump_helper.erl
+++ b/lib/observer/test/crashdump_helper.erl
@@ -24,7 +24,7 @@
create_binaries/0,create_sub_binaries/1,
dump_persistent_terms/0,
create_persistent_terms/0]).
--compile(r18).
+-compile(r20).
-include_lib("common_test/include/ct.hrl").
n1_proc(N2,Creator) ->
diff --git a/lib/public_key/doc/src/public_key.xml b/lib/public_key/doc/src/public_key.xml
index 4c61139197..9fcedf6ef9 100644
--- a/lib/public_key/doc/src/public_key.xml
+++ b/lib/public_key/doc/src/public_key.xml
@@ -644,7 +644,7 @@ fun(#'DistributionPoint'{}, #'CertificateList'{},
<v>conf_opt() = {cert, der_encoded()} | {key, PrivateKey} |{cacerts, [der_encoded()]}</v>
<d>
This is a subset of the type
- <seealso marker="ssl:ssl#type-ssloption"> ssl:ssl_option()</seealso>.
+ <seealso marker="ssl:ssl#type-tls_option"> ssl:tls_option()</seealso>.
<c>PrivateKey</c> is what
<seealso marker="#generate_key-1">generate_key/1</seealso>
returns.
diff --git a/lib/public_key/src/public_key.erl b/lib/public_key/src/public_key.erl
index 75d40d2e8a..47c5dbb95a 100644
--- a/lib/public_key/src/public_key.erl
+++ b/lib/public_key/src/public_key.erl
@@ -66,7 +66,7 @@
-export_type([public_key/0, private_key/0, pem_entry/0,
pki_asn1_type/0, asn1_type/0, ssh_file/0, der_encoded/0,
- key_params/0, digest_type/0]).
+ key_params/0, digest_type/0, issuer_name/0, oid/0]).
-type public_key() :: rsa_public_key() | dsa_public_key() | ec_public_key() | ed_public_key() .
-type private_key() :: rsa_private_key() | dsa_private_key() | ec_private_key() | ed_private_key() .
diff --git a/lib/reltool/doc/src/reltool.xml b/lib/reltool/doc/src/reltool.xml
index 5afbad0ba8..136ec2ed69 100644
--- a/lib/reltool/doc/src/reltool.xml
+++ b/lib/reltool/doc/src/reltool.xml
@@ -503,6 +503,7 @@ sys() = {root_dir, root_dir()}
| {incl_cond, incl_cond()}
| {boot_rel, boot_rel()}
| {rel, rel_name(), rel_vsn(), [rel_app()]}
+ | {rel, rel_name(), rel_vsn(), [rel_app()], [rel_opt()]}
| {relocatable, relocatable()}
| {app_file, app_file()}
| {debug_info, debug_info()}
@@ -534,6 +535,7 @@ rel_app() = app_name()
| {app_name(), app_type()}
| {app_name(), [incl_app()]}
| {app_name(), app_type(), [incl_app()]}
+rel_opt() = {load_dot_erlang, boolean()}
app_name() = atom()
app_type() = permanent | transient | temporary | load | none
app_vsn() = string()
diff --git a/lib/reltool/doc/src/reltool_examples.xml b/lib/reltool/doc/src/reltool_examples.xml
index f9a6fcf342..3888b643a2 100644
--- a/lib/reltool/doc/src/reltool_examples.xml
+++ b/lib/reltool/doc/src/reltool_examples.xml
@@ -313,9 +313,12 @@ Erlang/OTP 20 [erts-10.0] [source-c13b302] [64-bit] [smp:4:4] [ds:4:4:10] [async
[hipe] [kernel-poll:false]
Eshell V10.0 (abort with ^G)
1&gt;
-1&gt; {ok, Server} = reltool:start_server([{config, {sys, [{boot_rel, "NAME"},
- {rel, "NAME", "VSN",
- [sasl]}]}}]).
+1&gt; {ok, Server} = reltool:start_server([{config,
+ {sys,
+ [{boot_rel, "NAME"},
+ {rel, "NAME", "VSN",
+ [sasl],
+ [{load_dot_erlang, false}]}]}}]).
{ok,&lt;0.1288.0&gt;}
2&gt;
2&gt; reltool:get_config(Server).
diff --git a/lib/reltool/src/reltool.hrl b/lib/reltool/src/reltool.hrl
index d133762818..892aaf8649 100644
--- a/lib/reltool/src/reltool.hrl
+++ b/lib/reltool/src/reltool.hrl
@@ -61,6 +61,7 @@
| {app_name(), app_type()}
| {app_name(), [incl_app()]}
| {app_name(), app_type(), [incl_app()]}.
+-type rel_opt() :: {load_dot_erlang, boolean()}.
-type mod() :: {incl_cond, incl_cond()}
| {debug_info, debug_info()}.
-type app() :: {vsn, app_vsn()}
@@ -92,6 +93,8 @@
| {lib_dirs, [lib_dir()]}
| {boot_rel, boot_rel()}
| {rel, rel_name(), rel_vsn(), [rel_app()]}
+ | {rel, rel_name(), rel_vsn(),
+ [rel_app()], [rel_opt()]}
| {relocatable, relocatable()}
| {erts, app()}
| {escript, escript_file(), [escript()]}
diff --git a/lib/reltool/src/reltool_server.erl b/lib/reltool/src/reltool_server.erl
index 47aba77835..2de8000fd8 100644
--- a/lib/reltool/src/reltool_server.erl
+++ b/lib/reltool/src/reltool_server.erl
@@ -1483,6 +1483,18 @@ decode(#sys{rels = Rels} = Sys, [{rel, Name, Vsn, RelApps} | SysKeyVals])
Rel = #rel{name = Name, vsn = Vsn, rel_apps = []},
Rel2 = decode(Rel, RelApps),
decode(Sys#sys{rels = [Rel2 | Rels]}, SysKeyVals);
+decode(#sys{rels = Rels} = Sys, [{rel, Name, Vsn, RelApps, Opts} | SysKeyVals])
+ when is_list(Name), is_list(Vsn), is_list(RelApps), is_list(Opts) ->
+ Rel1 = lists:foldl(fun(Opt, Rel0) ->
+ case Opt of
+ {load_dot_erlang, Value} when is_boolean(Value) ->
+ Rel0#rel{load_dot_erlang = Value};
+ _ ->
+ reltool_utils:throw_error("Illegal rel option: ~tp", [Opt])
+ end
+ end, #rel{name = Name, vsn = Vsn, rel_apps = []}, Opts),
+ Rel2 = decode(Rel1, RelApps),
+ decode(Sys#sys{rels = [Rel2 | Rels]}, SysKeyVals);
decode(#sys{} = Sys, [{Key, Val} | KeyVals]) ->
Sys3 =
case Key of
diff --git a/lib/reltool/src/reltool_target.erl b/lib/reltool/src/reltool_target.erl
index 64834ecc1d..dfa62479a0 100644
--- a/lib/reltool/src/reltool_target.erl
+++ b/lib/reltool/src/reltool_target.erl
@@ -108,12 +108,8 @@ do_gen_config(#sys{root_dir = RootDir,
emit(incl_cond, A#app.incl_cond, undefined, InclDefs)}
|| A <- Apps, A#app.is_escript],
DefaultRels = reltool_utils:default_rels(),
- RelsItems =
- [{rel, R#rel.name, R#rel.vsn, do_gen_config(R, InclDefs)} ||
- R <- Rels],
- DefaultRelsItems =
- [{rel, R#rel.name, R#rel.vsn, do_gen_config(R, InclDefs)} ||
- R <- DefaultRels],
+ RelsItems = [do_gen_config(R, InclDefs) || R <- Rels],
+ DefaultRelsItems = [do_gen_config(R, InclDefs) || R <- DefaultRels],
RelsItems2 =
case InclDefs of
true -> RelsItems;
@@ -201,11 +197,20 @@ do_gen_config(#mod{name = Name,
_ ->
[]
end;
-do_gen_config(#rel{name = _Name,
- vsn = _Vsn,
- rel_apps = RelApps},
- InclDefs) ->
- [do_gen_config(RA, InclDefs) || RA <- RelApps];
+do_gen_config(#rel{name = Name,
+ vsn = Vsn,
+ rel_apps = RelApps,
+ load_dot_erlang = LoadDotErlang},
+ InclDefs) ->
+ RelAppsConfig = [do_gen_config(RA, InclDefs) || RA <- RelApps],
+ if
+ LoadDotErlang =:= false ->
+ {rel, Name, Vsn, RelAppsConfig, [{load_dot_erlang, false}]};
+ InclDefs =:= true ->
+ {rel, Name, Vsn, RelAppsConfig, [{load_dot_erlang, true}]};
+ LoadDotErlang =:= true ->
+ {rel, Name, Vsn, RelAppsConfig}
+ end;
do_gen_config(#rel_app{name = Name,
app_type = Type,
incl_apps = InclApps},
diff --git a/lib/reltool/test/reltool_server_SUITE.erl b/lib/reltool/test/reltool_server_SUITE.erl
index 990bd5c217..bb092e8bbf 100644
--- a/lib/reltool/test/reltool_server_SUITE.erl
+++ b/lib/reltool/test/reltool_server_SUITE.erl
@@ -102,6 +102,7 @@ all() ->
create_release,
create_release_sort,
create_script,
+ create_script_without_dot_erlang,
create_script_sort,
create_target,
create_target_unicode,
@@ -171,7 +172,7 @@ break(_Config) ->
start_server(_Config) ->
{ok, Pid} = ?msym({ok, _}, reltool:start_server([])),
- Libs = lists:sort(erl_libs()),
+ Libs = reltool_test_lib:erl_libs(),
StrippedDefault =
case Libs of
[] -> {sys, []};
@@ -185,7 +186,7 @@ start_server(_Config) ->
%% Start a server process and check that it does not crash
set_config(_Config) ->
- Libs = lists:sort(erl_libs()),
+ Libs = reltool_test_lib:erl_libs(),
Default =
{sys,
[
@@ -219,7 +220,15 @@ get_config(_Config) ->
StdLibDir = filename:join(LibDir,"stdlib-"++StdVsn),
SaslLibDir = filename:join(LibDir,"sasl-"++SaslVsn),
- Sys = {sys,[{incl_cond, exclude},
+ Libs = reltool_test_lib:erl_libs(),
+ LibDirs =
+ case Libs of
+ [] -> [];
+ _ -> [{lib_dirs,Libs}]
+ end,
+
+ Sys = {sys,LibDirs ++
+ [{incl_cond, exclude},
{app,kernel,[{incl_cond,include}]},
{app,sasl,[{incl_cond,include},{vsn,SaslVsn}]},
{app,stdlib,[{incl_cond,include},{lib_dir,StdLibDir}]}]},
@@ -228,13 +237,27 @@ get_config(_Config) ->
?m({ok, Sys}, reltool:get_config(Pid,false,false)),
%% Include derived info
- ?msym({ok,{sys,[{incl_cond, exclude},
- {erts,[]},
- {app,kernel,[{incl_cond,include},{mod,_,[]}|_]},
- {app,sasl,[{incl_cond,include},{vsn,SaslVsn},{mod,_,[]}|_]},
- {app,stdlib,[{incl_cond,include},{lib_dir,StdLibDir},
- {mod,_,[]}|_]}]}},
- reltool:get_config(Pid,false,true)),
+ case Libs of
+ [] ->
+ ?msym({ok,{sys,[{incl_cond, exclude},
+ {erts,[]},
+ {app,kernel,[{incl_cond,include},{mod,_,[]}|_]},
+ {app,sasl,[{incl_cond,include},{vsn,SaslVsn},
+ {mod,_,[]}|_]},
+ {app,stdlib,[{incl_cond,include},{lib_dir,StdLibDir},
+ {mod,_,[]}|_]}]}},
+ reltool:get_config(Pid,false,true));
+ _ ->
+ ?msym({ok,{sys,[{lib_dirs,Libs},
+ {incl_cond, exclude},
+ {erts,[]},
+ {app,kernel,[{incl_cond,include},{mod,_,[]}|_]},
+ {app,sasl,[{incl_cond,include},{vsn,SaslVsn},
+ {mod,_,[]}|_]},
+ {app,stdlib,[{incl_cond,include},{lib_dir,StdLibDir},
+ {mod,_,[]}|_]}]}},
+ reltool:get_config(Pid,false,true))
+ end,
%% Include defaults
?msym({ok,{sys,[{root_dir,_},
@@ -248,9 +271,9 @@ get_config(_Config) ->
{app,stdlib,[{incl_cond,include},{vsn,undefined},
{lib_dir,StdLibDir}]},
{boot_rel,"start_clean"},
- {rel,"no_dot_erlang","1.0",[]},
- {rel,"start_clean","1.0",[]},
- {rel,"start_sasl","1.0",[sasl]},
+ {rel,"no_dot_erlang","1.0",[],[{load_dot_erlang,false}]},
+ {rel,"start_clean","1.0",[],[{load_dot_erlang,true}]},
+ {rel,"start_sasl","1.0",[sasl],[{load_dot_erlang,true}]},
{emu_name,"beam"},
{relocatable,true},
{profile,development},
@@ -279,9 +302,9 @@ get_config(_Config) ->
{app,stdlib,[{incl_cond,include},{vsn,StdVsn},
{lib_dir,StdLibDir},{mod,_,[]}|_]},
{boot_rel,"start_clean"},
- {rel,"no_dot_erlang","1.0",[]},
- {rel,"start_clean","1.0",[]},
- {rel,"start_sasl","1.0",[sasl]},
+ {rel,"no_dot_erlang","1.0",[],[{load_dot_erlang,false}]},
+ {rel,"start_clean","1.0",[],[{load_dot_erlang,true}]},
+ {rel,"start_sasl","1.0",[sasl],[{load_dot_erlang,true}]},
{emu_name,"beam"},
{relocatable,true},
{profile,development},
@@ -305,11 +328,11 @@ get_config(_Config) ->
%% OTP-9135, test that app_file option can be set to all | keep | strip
otp_9135(_Config) ->
- Libs = lists:sort(erl_libs()),
+ Libs = reltool_test_lib:erl_libs(),
StrippedDefaultSys =
case Libs of
[] -> [];
- _ -> {lib_dirs, Libs}
+ _ -> [{lib_dirs, Libs}]
end,
Config1 = {sys,[{app_file, keep}]}, % this is the default
@@ -549,6 +572,32 @@ create_script(_Config) ->
?m(equal, diff_script(OrigScript, Script)),
+ %% A release defaults to load_dot_erlang == true
+ {script, {RelName, RelVsn}, ScriptInstructions} = Script,
+ ?m(true, lists:member({apply,{c,erlangrc,[]}}, ScriptInstructions)),
+
+ %% Stop server
+ ?m(ok, reltool:stop(Pid)),
+ ok.
+
+create_script_without_dot_erlang(_Config) ->
+ %% Configure the server
+ RelName = "Just testing",
+ RelVsn = "1.0",
+ Config =
+ {sys,
+ [
+ {lib_dirs, []},
+ {boot_rel, RelName},
+ {rel, RelName, RelVsn, [stdlib, kernel], [{load_dot_erlang, false}]}
+ ]},
+ {ok, Pid} = ?msym({ok, _}, reltool:start_server([{config, Config}])),
+
+ %% Confirm that load_dot_erlang == false was used
+ {ok, Script} = ?msym({ok, _}, reltool:get_script(Pid, RelName)),
+ {script, {RelName, RelVsn}, ScriptInstructions} = Script,
+ ?m(false, lists:member({apply,{c,erlangrc,[]}}, ScriptInstructions)),
+
%% Stop server
?m(ok, reltool:stop(Pid)),
ok.
@@ -1719,13 +1768,19 @@ set_sys_and_undo(Config) ->
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
load_config_and_undo(Config) ->
- Sys1 = {sys,[{incl_cond, exclude},
- {app,kernel,[{incl_cond,include}]},
- {app,sasl,[{incl_cond,include}]},
- {app,stdlib,[{incl_cond,include}]},
- {app,tools,[{incl_cond,include}]}]},
+ Sys1 = {sys,Cfg1=[{incl_cond, exclude},
+ {app,kernel,[{incl_cond,include}]},
+ {app,sasl,[{incl_cond,include}]},
+ {app,stdlib,[{incl_cond,include}]},
+ {app,tools,[{incl_cond,include}]}]},
{ok, Pid} = ?msym({ok, _}, reltool:start_server([{config, Sys1}])),
- ?m({ok, Sys1}, reltool:get_config(Pid)),
+ Libs = reltool_test_lib:erl_libs(),
+ Sys11 =
+ case Libs of
+ [] -> Sys1;
+ _ -> {sys, [{lib_dirs, Libs}|Cfg1]}
+ end,
+ ?m({ok, Sys11}, reltool:get_config(Pid)),
?m({ok,[]}, reltool_server:get_status(Pid)),
%% Get app and mod
@@ -1780,13 +1835,19 @@ load_config_and_undo(Config) ->
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Test that load_config is properly rolled back if it fails
load_config_fail(_Config) ->
- Sys1 = {sys,[{incl_cond, exclude},
- {app,kernel,[{incl_cond,include}]},
- {app,sasl,[{incl_cond,include}]},
- {app,stdlib,[{incl_cond,include}]},
- {app,tools,[{incl_cond,include}]}]},
+ Sys1 = {sys,Cfg1=[{incl_cond, exclude},
+ {app,kernel,[{incl_cond,include}]},
+ {app,sasl,[{incl_cond,include}]},
+ {app,stdlib,[{incl_cond,include}]},
+ {app,tools,[{incl_cond,include}]}]},
{ok, Pid} = ?msym({ok, _}, reltool:start_server([{config, Sys1}])),
- ?m({ok, Sys1}, reltool:get_config(Pid)),
+ Libs = reltool_test_lib:erl_libs(),
+ Sys11 =
+ case Libs of
+ [] -> Sys1;
+ _ -> {sys, [{lib_dirs, Libs}|Cfg1]}
+ end,
+ ?m({ok, Sys11}, reltool:get_config(Pid)),
?m({ok,[]}, reltool_server:get_status(Pid)),
%% Get app and mod
@@ -1804,7 +1865,7 @@ load_config_fail(_Config) ->
reltool_server:load_config(Pid,Sys2)),
%% Check that a rollback is done to the old configuration
- ?m({ok, Sys1}, reltool:get_config(Pid,false,false)),
+ ?m({ok, Sys11}, reltool:get_config(Pid,false,false)),
%% and that tools is not changed (i.e. that the new configuration
%% is not applied)
@@ -2074,25 +2135,42 @@ gen_rel_files(_Config) ->
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
save_config(Config) ->
PrivDir = ?config(priv_dir,Config),
- Sys = {sys,[{incl_cond, exclude},
- {app,kernel,[{incl_cond,include}]},
- {app,sasl,[{incl_cond,include}]},
- {app,stdlib,[{incl_cond,include}]}]},
+ Sys = {sys,Cfg=[{incl_cond, exclude},
+ {app,kernel,[{incl_cond,include}]},
+ {app,sasl,[{incl_cond,include}]},
+ {app,stdlib,[{incl_cond,include}]}]},
{ok, Pid} = ?msym({ok, _}, reltool:start_server([{config, Sys}])),
- ?m({ok, Sys}, reltool:get_config(Pid)),
+ Libs = reltool_test_lib:erl_libs(),
+ Sys1 =
+ case Libs of
+ [] -> Sys;
+ _ -> {sys, [{lib_dirs, Libs}|Cfg]}
+ end,
+ ?m({ok, Sys1}, reltool:get_config(Pid)),
Simple = filename:join(PrivDir,"save_simple.reltool"),
?m(ok, reltool_server:save_config(Pid,Simple,false,false)),
- ?m({ok,[Sys]}, file:consult(Simple)),
+ ?m({ok,[Sys1]}, file:consult(Simple)),
Derivates = filename:join(PrivDir,"save_derivates.reltool"),
?m(ok, reltool_server:save_config(Pid,Derivates,false,true)),
- ?msym({ok,[{sys,[{incl_cond, exclude},
- {erts,[]},
- {app,kernel,[{incl_cond,include},{mod,_,[]}|_]},
- {app,sasl,[{incl_cond,include},{mod,_,[]}|_]},
- {app,stdlib,[{incl_cond,include},{mod,_,[]}|_]}]}]},
- file:consult(Derivates)),
+ case Libs of
+ [] ->
+ ?msym({ok,[{sys,[{incl_cond, exclude},
+ {erts,[]},
+ {app,kernel,[{incl_cond,include},{mod,_,[]}|_]},
+ {app,sasl,[{incl_cond,include},{mod,_,[]}|_]},
+ {app,stdlib,[{incl_cond,include},{mod,_,[]}|_]}]}]},
+ file:consult(Derivates));
+ _ ->
+ ?msym({ok,[{sys,[{lib_dirs,Libs},
+ {incl_cond, exclude},
+ {erts,[]},
+ {app,kernel,[{incl_cond,include},{mod,_,[]}|_]},
+ {app,sasl,[{incl_cond,include},{mod,_,[]}|_]},
+ {app,stdlib,[{incl_cond,include},{mod,_,[]}|_]}]}]},
+ file:consult(Derivates))
+ end,
Defaults = filename:join(PrivDir,"save_defaults.reltool"),
?m(ok, reltool_server:save_config(Pid,Defaults,true,false)),
@@ -2107,9 +2185,9 @@ save_config(Config) ->
{app,stdlib,[{incl_cond,include},{vsn,undefined},
{lib_dir,undefined}]},
{boot_rel,"start_clean"},
- {rel,"no_dot_erlang","1.0",[]},
- {rel,"start_clean","1.0",[]},
- {rel,"start_sasl","1.0",[sasl]},
+ {rel,"no_dot_erlang","1.0",[],[{load_dot_erlang,false}]},
+ {rel,"start_clean","1.0",[],[{load_dot_erlang,true}]},
+ {rel,"start_sasl","1.0",[sasl],[{load_dot_erlang,true}]},
{emu_name,"beam"},
{relocatable,true},
{profile,development},
@@ -2148,9 +2226,9 @@ save_config(Config) ->
{app,stdlib,[{incl_cond,include},{vsn,StdVsn},
{lib_dir,StdLibDir},{mod,_,[]}|_]},
{boot_rel,"start_clean"},
- {rel,"no_dot_erlang","1.0",[]},
- {rel,"start_clean","1.0",[]},
- {rel,"start_sasl","1.0",[sasl]},
+ {rel,"no_dot_erlang","1.0",[],[{load_dot_erlang,false}]},
+ {rel,"start_clean","1.0",[],[{load_dot_erlang,true}]},
+ {rel,"start_sasl","1.0",[sasl],[{load_dot_erlang,true}]},
{emu_name,"beam"},
{relocatable,true},
{profile,development},
@@ -2560,9 +2638,6 @@ windows_erl_libs(_Config) ->
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Library functions
-erl_libs() ->
- reltool_utils:erl_libs().
-
datadir(Config) ->
%% Removes the trailing slash...
filename:nativename(?config(data_dir,Config)).
diff --git a/lib/reltool/test/reltool_test_lib.erl b/lib/reltool/test/reltool_test_lib.erl
index be48ea4726..033d952d0a 100644
--- a/lib/reltool/test/reltool_test_lib.erl
+++ b/lib/reltool/test/reltool_test_lib.erl
@@ -237,7 +237,8 @@ wait_for_close() ->
wait_for_close()
end.
-
+erl_libs() ->
+ lists:sort([filename:absname(P) || P<-reltool_utils:erl_libs()]).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% A small test server, which can be run standalone in a shell
diff --git a/lib/reltool/test/reltool_wx_SUITE.erl b/lib/reltool/test/reltool_wx_SUITE.erl
index f6f7721762..983c8f6c52 100644
--- a/lib/reltool/test/reltool_wx_SUITE.erl
+++ b/lib/reltool/test/reltool_wx_SUITE.erl
@@ -74,7 +74,12 @@ start_all_windows(_Config) ->
%% Test that server pid can be fetched, and that server is alive
{ok, Server} = ?msym({ok,_}, reltool:get_server(SysPid)),
?m(true, erlang:is_process_alive(Server)),
- ?m({ok,{sys,[]}}, reltool:get_config(Server)),
+ Sys =
+ case reltool_test_lib:erl_libs() of
+ [] -> [];
+ Libs -> [{lib_dirs,Libs}]
+ end,
+ ?m({ok,{sys,Sys}}, reltool:get_config(Server)),
%% Terminate
check_no_win_crash(),
diff --git a/lib/sasl/test/test_lib.hrl b/lib/sasl/test/test_lib.hrl
index f5210d4f27..7867d3da39 100644
--- a/lib/sasl/test/test_lib.hrl
+++ b/lib/sasl/test/test_lib.hrl
@@ -1,3 +1,3 @@
-define(ertsvsn,"4.4").
--define(kernelvsn,"5.3").
--define(stdlibvsn,"3.4").
+-define(kernelvsn,"6.0").
+-define(stdlibvsn,"3.5").
diff --git a/lib/ssh/test/ssh_trpt_test_lib.erl b/lib/ssh/test/ssh_trpt_test_lib.erl
index 8de550af15..f2c9892f95 100644
--- a/lib/ssh/test/ssh_trpt_test_lib.erl
+++ b/lib/ssh/test/ssh_trpt_test_lib.erl
@@ -41,15 +41,20 @@
opts = [],
timeout = 5000, % ms
seen_hello = false,
- enc = <<>>,
ssh = #ssh{}, % #ssh{}
alg_neg = {undefined,undefined}, % {own_kexinit, peer_kexinit}
alg, % #alg{}
vars = dict:new(),
reply = [], % Some repy msgs are generated hidden in ssh_transport :[
prints = [],
- return_value
- }).
+ return_value,
+
+ %% Packet retrival and decryption
+ decrypted_data_buffer = <<>>,
+ encrypted_data_buffer = <<>>,
+ aead_data = <<>>,
+ undecrypted_packet_length
+ }).
-define(role(S), ((S#s.ssh)#ssh.role) ).
@@ -475,11 +480,11 @@ recv(S0 = #s{}) ->
%%%================================================================
try_find_crlf(Seen, S0) ->
- case erlang:decode_packet(line,S0#s.enc,[]) of
+ case erlang:decode_packet(line,S0#s.encrypted_data_buffer,[]) of
{more,_} ->
- Line = <<Seen/binary,(S0#s.enc)/binary>>,
+ Line = <<Seen/binary,(S0#s.encrypted_data_buffer)/binary>>,
S0#s{seen_hello = {more,Line},
- enc = <<>>, % didn't find a complete line
+ encrypted_data_buffer = <<>>, % didn't find a complete line
% -> no more characters to test
return_value = {more,Line}
};
@@ -490,13 +495,13 @@ try_find_crlf(Seen, S0) ->
S = opt(print_messages, S0,
fun(X) when X==true;X==detail -> {"Recv info~n~p~n",[Line]} end),
S#s{seen_hello = false,
- enc = Rest,
+ encrypted_data_buffer = Rest,
return_value = {info,Line}};
S1=#s{} ->
S = opt(print_messages, S1,
fun(X) when X==true;X==detail -> {"Recv hello~n~p~n",[Line]} end),
S#s{seen_hello = true,
- enc = Rest,
+ encrypted_data_buffer = Rest,
return_value = {hello,Line}}
end
end.
@@ -511,19 +516,73 @@ handle_hello(Bin, S=#s{ssh=C}) ->
{{Vp,Vs}, server} -> S#s{ssh = C#ssh{c_vsn=Vp, c_version=Vs}}
end.
-receive_binary_msg(S0=#s{ssh=C0=#ssh{decrypt_block_size = BlockSize,
+receive_binary_msg(S0=#s{}) ->
+ case ssh_transport:handle_packet_part(
+ S0#s.decrypted_data_buffer,
+ S0#s.encrypted_data_buffer,
+ S0#s.aead_data,
+ S0#s.undecrypted_packet_length,
+ S0#s.ssh)
+ of
+ {packet_decrypted, DecryptedBytes, EncryptedDataRest, Ssh1} ->
+ S1 = S0#s{ssh = Ssh1#ssh{recv_sequence = ssh_transport:next_seqnum(Ssh1#ssh.recv_sequence)},
+ decrypted_data_buffer = <<>>,
+ undecrypted_packet_length = undefined,
+ aead_data = <<>>,
+ encrypted_data_buffer = EncryptedDataRest},
+ case
+ catch ssh_message:decode(set_prefix_if_trouble(DecryptedBytes,S1))
+ of
+ {'EXIT',_} -> fail(decode_failed,S1);
+
+ Msg ->
+ Ssh2 = case Msg of
+ #ssh_msg_kexinit{} ->
+ ssh_transport:key_init(opposite_role(Ssh1), Ssh1, DecryptedBytes);
+ _ ->
+ Ssh1
+ end,
+ S2 = opt(print_messages, S1,
+ fun(X) when X==true;X==detail -> {"Recv~n~s~n",[format_msg(Msg)]} end),
+ S3 = opt(print_messages, S2,
+ fun(detail) -> {"decrypted bytes ~p~n",[DecryptedBytes]} end),
+ S3#s{ssh = inc_recv_seq_num(Ssh2),
+ return_value = Msg
+ }
+ end;
+
+ {get_more, DecryptedBytes, EncryptedDataRest, AeadData, TotalNeeded, Ssh1} ->
+ %% Here we know that there are not enough bytes in
+ %% EncryptedDataRest to use. We must wait for more.
+ Remaining = case TotalNeeded of
+ undefined -> 8;
+ _ -> TotalNeeded - size(DecryptedBytes) - size(EncryptedDataRest)
+ end,
+ receive_binary_msg(
+ receive_wait(Remaining,
+ S0#s{encrypted_data_buffer = EncryptedDataRest,
+ decrypted_data_buffer = DecryptedBytes,
+ undecrypted_packet_length = TotalNeeded,
+ aead_data = AeadData,
+ ssh = Ssh1}
+ ))
+ end.
+
+
+
+old_receive_binary_msg(S0=#s{ssh=C0=#ssh{decrypt_block_size = BlockSize,
recv_mac_size = MacSize
}
}) ->
- case size(S0#s.enc) >= max(8,BlockSize) of
+ case size(S0#s.encrypted_data_buffer) >= max(8,BlockSize) of
false ->
%% Need more bytes to decode the packet_length field
- Remaining = max(8,BlockSize) - size(S0#s.enc),
+ Remaining = max(8,BlockSize) - size(S0#s.encrypted_data_buffer),
receive_binary_msg( receive_wait(Remaining, S0) );
true ->
%% Has enough bytes to decode the packet_length field
{_, <<?UINT32(PacketLen), _/binary>>, _} =
- ssh_transport:decrypt_blocks(S0#s.enc, BlockSize, C0), % FIXME: BlockSize should be at least 4
+ ssh_transport:decrypt_blocks(S0#s.encrypted_data_buffer, BlockSize, C0), % FIXME: BlockSize should be at least 4
%% FIXME: Check that ((4+PacketLen) rem BlockSize) == 0 ?
@@ -534,19 +593,19 @@ receive_binary_msg(S0=#s{ssh=C0=#ssh{decrypt_block_size = BlockSize,
((4+PacketLen) rem BlockSize) =/= 0 ->
fail(bad_packet_length_modulo, S0); % FIXME: disconnect
- size(S0#s.enc) >= (4 + PacketLen + MacSize) ->
+ size(S0#s.encrypted_data_buffer) >= (4 + PacketLen + MacSize) ->
%% has the whole packet
S0;
true ->
%% need more bytes to get have the whole packet
- Remaining = (4 + PacketLen + MacSize) - size(S0#s.enc),
+ Remaining = (4 + PacketLen + MacSize) - size(S0#s.encrypted_data_buffer),
receive_wait(Remaining, S0)
end,
%% Decrypt all, including the packet_length part (re-use the initial #ssh{})
{C1, SshPacket = <<?UINT32(_),?BYTE(PadLen),Tail/binary>>, EncRest} =
- ssh_transport:decrypt_blocks(S1#s.enc, PacketLen+4, C0),
+ ssh_transport:decrypt_blocks(S1#s.encrypted_data_buffer, PacketLen+4, C0),
PayloadLen = PacketLen - 1 - PadLen,
<<CompressedPayload:PayloadLen/binary, _Padding:PadLen/binary>> = Tail,
@@ -573,7 +632,7 @@ receive_binary_msg(S0=#s{ssh=C0=#ssh{decrypt_block_size = BlockSize,
S3 = opt(print_messages, S2,
fun(detail) -> {"decrypted bytes ~p~n",[SshPacket]} end),
S3#s{ssh = inc_recv_seq_num(C3),
- enc = Rest,
+ encrypted_data_buffer = Rest,
return_value = Msg
}
end
@@ -602,7 +661,7 @@ receive_poll(S=#s{socket=Sock}) ->
inet:setopts(Sock, [{active,once}]),
receive
{tcp,Sock,Data} ->
- receive_poll( S#s{enc = <<(S#s.enc)/binary,Data/binary>>} );
+ receive_poll( S#s{encrypted_data_buffer = <<(S#s.encrypted_data_buffer)/binary,Data/binary>>} );
{tcp_closed,Sock} ->
throw({tcp,tcp_closed});
{tcp_error, Sock, Reason} ->
@@ -616,7 +675,7 @@ receive_wait(S=#s{socket=Sock,
inet:setopts(Sock, [{active,once}]),
receive
{tcp,Sock,Data} ->
- S#s{enc = <<(S#s.enc)/binary,Data/binary>>};
+ S#s{encrypted_data_buffer = <<(S#s.encrypted_data_buffer)/binary,Data/binary>>};
{tcp_closed,Sock} ->
throw({tcp,tcp_closed});
{tcp_error, Sock, Reason} ->
@@ -627,11 +686,11 @@ receive_wait(S=#s{socket=Sock,
receive_wait(N, S=#s{socket=Sock,
timeout=Timeout,
- enc=Enc0}) when N>0 ->
+ encrypted_data_buffer=Enc0}) when N>0 ->
inet:setopts(Sock, [{active,once}]),
receive
{tcp,Sock,Data} ->
- receive_wait(N-size(Data), S#s{enc = <<Enc0/binary,Data/binary>>});
+ receive_wait(N-size(Data), S#s{encrypted_data_buffer = <<Enc0/binary,Data/binary>>});
{tcp_closed,Sock} ->
throw({tcp,tcp_closed});
{tcp_error, Sock, Reason} ->
diff --git a/lib/ssl/doc/specs/.gitignore b/lib/ssl/doc/specs/.gitignore
new file mode 100644
index 0000000000..322eebcb06
--- /dev/null
+++ b/lib/ssl/doc/specs/.gitignore
@@ -0,0 +1 @@
+specs_*.xml
diff --git a/lib/ssl/doc/src/Makefile b/lib/ssl/doc/src/Makefile
index c72b6d6cc4..7cf251d8f9 100644
--- a/lib/ssl/doc/src/Makefile
+++ b/lib/ssl/doc/src/Makefile
@@ -80,11 +80,16 @@ HTML_REF_MAN_FILE = $(HTMLDIR)/index.html
TOP_PDF_FILE = $(PDFDIR)/$(APPLICATION)-$(VSN).pdf
+SPECS_FILES = $(XML_REF3_FILES:%.xml=$(SPECDIR)/specs_%.xml)
+
+TOP_SPECS_FILE = specs.xml
+
# ----------------------------------------------------
# FLAGS
# ----------------------------------------------------
XML_FLAGS +=
DVIPS_FLAGS +=
+SPECS_FLAGS = -I../../../public_key/include -I../../../public_key/src -I../../..
# ----------------------------------------------------
# Targets
@@ -92,7 +97,7 @@ DVIPS_FLAGS +=
$(HTMLDIR)/%.gif: %.gif
$(INSTALL_DATA) $< $@
-docs: pdf html man
+docs: html pdf man
$(TOP_PDF_FILE): $(XML_FILES)
@@ -105,6 +110,7 @@ clean clean_docs:
rm -rf $(XMLDIR)
rm -f $(MAN3DIR)/*
rm -f $(TOP_PDF_FILE) $(TOP_PDF_FILE:%.pdf=%.fo)
+ rm -f $(SPECS_FILES)
rm -f errs core *~
man: $(MAN3_FILES) $(MAN6_FILES)
diff --git a/lib/ssl/doc/src/specs.xml b/lib/ssl/doc/src/specs.xml
new file mode 100644
index 0000000000..50e9428fec
--- /dev/null
+++ b/lib/ssl/doc/src/specs.xml
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<specs xmlns:xi="http://www.w3.org/2001/XInclude">
+ <xi:include href="../specs/specs_ssl_crl_cache_api.xml"/>
+ <xi:include href="../specs/specs_ssl_crl_cache.xml"/>
+ <xi:include href="../specs/specs_ssl_session_cache_api.xml"/>
+ <xi:include href="../specs/specs_ssl.xml"/>
+</specs>
+
+
diff --git a/lib/ssl/doc/src/ssl.xml b/lib/ssl/doc/src/ssl.xml
index 200fb89a4d..bd963e8148 100644
--- a/lib/ssl/doc/src/ssl.xml
+++ b/lib/ssl/doc/src/ssl.xml
@@ -37,296 +37,361 @@
<seealso marker="ssl_app">ssl(6)</seealso>.
</p>
</description>
-
- <section>
- <title>DATA TYPES</title>
- <p>The following data types are used in the functions for SSL/TLS/DTLS:</p>
-
- <taglist>
-
- <tag><c>boolean() =</c></tag>
- <item><p><c>true | false</c></p></item>
-
- <tag><c>option() =</c></tag>
- <item><p><c>socketoption() | ssl_option() | transport_option()</c></p>
- </item>
-
- <tag><c>socketoption() =</c></tag>
- <item><p><c>proplists:property()</c></p>
- <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>,
- <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>
- <p><c>{verify, verify_type()}</c></p>
- <p><c>| {verify_fun, {fun(), term()}}</c></p>
- <p><c>| {fail_if_no_peer_cert, boolean()}</c></p>
- <p><c>| {depth, integer()}</c></p>
- <p><c>| {cert, public_key:der_encoded()}</c></p>
- <p><c>| {certfile, path()}</c></p>
- <p><c>| {key, {'RSAPrivateKey'| 'DSAPrivateKey' | 'ECPrivateKey'
- | 'PrivateKeyInfo', public_key:der_encoded()} |
- #{algorithm := rsa | dss | ecdsa,
- engine := crypto:engine_ref(), key_id := crypto:key_id(), password => crypto:password()}</c></p>
- <p><c>| {keyfile, path()}</c></p>
- <p><c>| {password, string()}</c></p>
- <p><c>| {cacerts, [public_key:der_encoded()]}</c></p>
- <p><c>| {cacertfile, path()}</c></p>
- <p><c>| {dh, public_key:der_encoded()}</c></p>
- <p><c>| {dhfile, path()}</c></p>
- <p><c>| {ciphers, ciphers()}</c></p>
- <p><c>| {user_lookup_fun, {fun(), term()}}, {psk_identity, string()},
- {srp_identity, {string(), string()}}</c></p>
- <p><c>| {reuse_sessions, boolean() | save()}</c></p>
- <p><c>| {reuse_session, fun() | binary()} </c></p>
- <p><c>| {next_protocols_advertised, [binary()]}</c></p>
- <p><c>| {client_preferred_next_protocols, {client | server,
- [binary()]} | {client | server, [binary()], binary()}}</c></p>
- <p><c>| {log_alert, boolean()}</c></p>
- <p><c>| {log_level, atom()}</c></p>
- <p><c>| {server_name_indication, hostname() | disable}</c></p>
- <p><c>| {customize_hostname_check, list()}</c></p>
- <p><c>| {sni_hosts, [{hostname(), [ssl_option()]}]}</c></p>
- <p><c>| {sni_fun, SNIfun::fun()}</c></p>
- </item>
-
- <tag><c>transport_option() =</c></tag>
- <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> 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. For DTLS this feature must be considered exprimental.</p>
- <taglist>
- <tag><c>CallbackModule =</c></tag>
- <item><p><c>atom()</c></p></item>
- <tag><c>DataTag =</c></tag>
- <item><p><c>atom()</c></p>
- <p>Used in socket data message.</p></item>
- <tag><c>ClosedTag =</c></tag>
- <item><p><c>atom()</c></p>
- <p>Used in socket close message.</p></item>
- </taglist>
- </item>
-
- <tag><c>verify_type() =</c></tag>
- <item><p><c>verify_none | verify_peer</c></p></item>
-
- <tag><c>path() =</c></tag>
- <item><p><c>string()</c></p>
- <p>Represents a file path.</p></item>
-
- <tag><c>public_key:der_encoded() =</c></tag>
- <item><p><c>binary()</c></p>
- <p>ASN.1 DER-encoded entity as an Erlang binary.</p></item>
- <tag><c>host() =</c></tag>
- <item><p><c>hostname() | ipaddress()</c></p></item>
+ <!--
+ ================================================================
+ = Data types =
+ ================================================================
+ -->
- <tag><c>hostname() =</c></tag>
- <item><p><c>string() - DNS hostname</c></p></item>
+ <datatypes>
+ <datatype_title>Types used in SSL/TLS/DTLS</datatype_title>
- <tag><c>ip_address() =</c></tag>
- <item><p><c>{N1,N2,N3,N4} % IPv4 | {K1,K2,K3,K4,K5,K6,K7,K8} % IPv6
- </c></p></item>
+
+ <datatype>
+ <name name="socket"/>
+ </datatype>
+
+ <datatype>
+ <name name="sslsocket"/>
+ <desc>
+ <p>An opaque reference to the TLS/DTLS connection, may be used for equality matching.</p>
+ </desc>
+ </datatype>
+
+ <datatype>
+ <name name="tls_option"/>
+ </datatype>
+
+ <datatype>
+ <name name="tls_client_option"/>
+ </datatype>
+
+ <datatype>
+ <name name="tls_server_option"/>
+ </datatype>
+
+
+ <datatype>
+ <name name="socket_option"/>
+ <desc>
+ <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>,
+ <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>
+ </desc>
+ </datatype>
- <tag><c>sslsocket() =</c></tag>
- <item><p>opaque()</p></item>
-
- <tag><marker id="type-protocol"/><c> protocol_version() =</c></tag>
- <item><p><c> ssl_tls_protocol() | dtls_protocol() </c></p></item>
+ <datatype>
+ <name name="active_msgs"/>
+ <desc>
+ <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 as described above.</p>
+ </desc>
+ </datatype>
- <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()]</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 := 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
- | rsa_psk | srp_anon | srp_dss | srp_rsa | ecdh_anon | ecdh_ecdsa
- | ecdhe_ecdsa | ecdh_rsa | ecdhe_rsa</c></p></item>
-
- <tag><c>cipher() =</c></tag>
- <item><p><c>rc4_128 | des_cbc | '3des_ede_cbc'
- | aes_128_cbc | aes_256_cbc | aes_128_gcm | aes_256_gcm | chacha20_poly1305</c></p></item>
-
- <tag><c>hash() =</c></tag>
- <item><p><c>md5 | sha | sha224 | sha256 | sha348 | sha512</c></p></item>
-
- <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>
-
- <tag><c>SNIfun::fun()</c></tag>
- <item><p><c>= fun(ServerName :: string()) -> [ssl_option()]</c></p></item>
-
- <tag><c>named_curve() =</c></tag>
- <item><p><c>sect571r1 | sect571k1 | secp521r1 | brainpoolP512r1
- | sect409k1 | sect409r1 | brainpoolP384r1 | secp384r1
- | sect283k1 | sect283r1 | brainpoolP256r1 | secp256k1 | secp256r1
- | sect239k1 | sect233k1 | sect233r1 | secp224k1 | secp224r1
- | sect193r1 | sect193r2 | secp192k1 | secp192r1 | sect163k1
- | sect163r1 | sect163r2 | secp160k1 | secp160r1 | secp160r2</c></p></item>
-
- <tag><c>hello_extensions() =</c></tag>
- <item><p><c>#{renegotiation_info => binary() | undefined,
- signature_algs => [{hash(), ecsda| rsa| dsa}] | undefined
- alpn => binary() | undefined,
- next_protocol_negotiation => binary() | undefined,
- srp => string() | undefined,
- ec_point_formats => list() | undefined,
- elliptic_curves => [oid] | undefined,
- sni => string() | undefined}
- }</c></p></item>
-
- <tag><c>signature_scheme() =</c></tag>
- <item>
- <p><c>rsa_pkcs1_sha256</c></p>
- <p><c>| rsa_pkcs1_sha384</c></p>
- <p><c>| rsa_pkcs1_sha512</c></p>
- <p><c>| ecdsa_secp256r1_sha256</c></p>
- <p><c>| ecdsa_secp384r1_sha384</c></p>
- <p><c>| ecdsa_secp521r1_sha512</c></p>
- <p><c>| rsa_pss_rsae_sha256</c></p>
- <p><c>| rsa_pss_rsae_sha384</c></p>
- <p><c>| rsa_pss_rsae_sha512</c></p>
- <p><c>| rsa_pss_pss_sha256</c></p>
- <p><c>| rsa_pss_pss_sha384</c></p>
- <p><c>| rsa_pss_pss_sha512</c></p>
- <p><c>| rsa_pkcs1_sha1</c></p>
- <p><c>| ecdsa_sha1</c></p>
- </item>
-
- </taglist>
- </section>
-
- <section>
- <title>TLS/DTLS OPTION DESCRIPTIONS - COMMON for SERVER and CLIENT</title>
+ <datatype>
+ <name name="transport_option"/>
+ <desc>
+ <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. The tag
+ values should be the values used by the underlying transport
+ in its active mode messages. 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. For DTLS this feature must be considered exprimental.
+ </p>
+ </desc>
+ </datatype>
+
+ <datatype>
+ <name name="host"/>
+ </datatype>
+
+ <datatype>
+ <name name="hostname"/>
+ </datatype>
+
+ <datatype>
+ <name name="ip_address"/>
+ </datatype>
+
+ <datatype>
+ <name name="protocol_version"/>
+ </datatype>
+
+ <datatype>
+ <name name="tls_version"/>
+ </datatype>
+
+ <datatype>
+ <name name="dtls_version"/>
+ </datatype>
+
+ <datatype>
+ <name name="legacy_version"/>
+ </datatype>
+
+ <datatype>
+ <name name="prf_random"/>
+ </datatype>
+
+ <datatype>
+ <name name="verify_type"/>
+ </datatype>
+
+ <datatype>
+ <name name="ciphers"/>
+ </datatype>
+
+ <datatype>
+ <name name="erl_cipher_suite"/>
+ </datatype>
+
+ <datatype>
+ <name name="cipher"/>
+ </datatype>
+
+ <datatype>
+ <name name="legacy_cipher"/>
+ </datatype>
+
+ <datatype>
+ <name name="cipher_filters"/>
+ </datatype>
+
+ <datatype>
+ <name name="hash"/>
+ </datatype>
+
+ <datatype>
+ <name name="sha2"/>
+ </datatype>
+
+ <datatype>
+ <name name="legacy_hash"/>
+ </datatype>
+
+ <datatype>
+ <name name="old_cipher_suite"/>
+ </datatype>
+
+ <datatype>
+ <name name="signature_algs"/>
+ </datatype>
+
+ <datatype>
+ <name name="sign_algo"/>
+ </datatype>
+
+ <datatype>
+ <name name="sign_scheme"/>
+ </datatype>
+
+ <datatype>
+ <name name="kex_algo"/>
+ </datatype>
+
+ <datatype>
+ <name name="algo_filter"/>
+ </datatype>
+
+ <datatype>
+ <name name="eccs"/>
+ </datatype>
+
+ <datatype>
+ <name name="named_curve"/>
+ </datatype>
+
+ <datatype>
+ <name name="psk_identity"/>
+ </datatype>
+
+ <datatype>
+ <name name="srp_identity"/>
+ </datatype>
+
+ <datatype>
+ <name name="srp_param_type"/>
+ </datatype>
+
+ <datatype>
+ <name name="app_level_protocol"/>
+ </datatype>
+
+ <datatype>
+ <name name="protocol_extensions"/>
+ </datatype>
+
+ <datatype>
+ <name name="error_alert"/>
+ </datatype>
+
+ <datatype>
+ <name name="tls_alert"/>
+ </datatype>
+
+ <datatype_title>TLS/DTLS OPTION DESCRIPTIONS - COMMON for SERVER and CLIENT</datatype_title>
+
+ <datatype>
+ <name name="common_option"/>
+ </datatype>
+
+ <datatype>
+ <name since="OTP 20" name="protocol"/>
+ <desc>
+ <p>Choose TLS or DTLS protocol for the transport layer security.
+ Defaults to <c>tls</c>. For DTLS other transports than UDP are not yet supported.</p>
+ </desc>
+ </datatype>
+
+ <datatype>
+ <name name="handshake_completion"/>
+ <desc>
+ <p>Defaults to <c>full</c>. If hello is specified the handshake will
+ pause after the hello message and give the user a possibility make decisions
+ based on hello extensions before continuing or aborting the handshake by calling
+ <seealso marker="#handshake_continue-3"> handshake_continue/3</seealso> or
+ <seealso marker="#handshake_cancel-1"> handshake_cancel/1</seealso></p>
+ </desc>
+ </datatype>
+
+ <datatype>
+ <name name="cert"/>
+ <desc>
+ <p>The DER-encoded users certificate. If this option
+ is supplied, it overrides option <c>certfile</c>.</p>
+ </desc>
+ </datatype>
+
+ <datatype>
+ <name name="cert_pem"/>
+ <desc>
+ <p>Path to a file containing the user certificate on PEM format.</p>
+ </desc>
+ </datatype>
+
+ <datatype>
+ <name name="key"/>
+ <desc>
+ <p>The DER-encoded user's private key or a map refering to a crypto
+ engine and its key reference that optionally can be password protected,
+ seealso <seealso marker="crypto:crypto#engine_load-4"> crypto:engine_load/4
+ </seealso> and <seealso marker="crypto:engine_load"> Crypto's Users Guide</seealso>. If this option
+ is supplied, it overrides option <c>keyfile</c>.</p>
+ </desc>
+ </datatype>
+
+ <datatype>
+ <name name="key_pem"/>
+ <desc>
+ <p>Path to the file containing the user's
+ private PEM-encoded key. As PEM-files can contain several
+ entries, this option defaults to the same file as given by
+ option <c>certfile</c>.</p>
+ </desc>
+ </datatype>
+
+ <datatype>
+ <name name="key_password"/>
+ <desc>
+ <p>String containing the user's password. Only used if the
+ private keyfile is password-protected.</p>
+ </desc>
+ </datatype>
+
+ <datatype>
+ <name name="cipher_suites"/>
+ <desc>
+ <p>Supported cipher suites. The function
+ <c>cipher_suites/2</c> can be used to find all ciphers that
+ are supported by default. <c>cipher_suites(all, 'tlsv1.2')</c> can be
+ called to find all available cipher suites. Pre-Shared Key
+ (<url href="http://www.ietf.org/rfc/rfc4279.txt">RFC
+ 4279</url> and <url
+ href="http://www.ietf.org/rfc/rfc5487.txt">RFC 5487</url>),
+ Secure Remote Password (<url
+ href="http://www.ietf.org/rfc/rfc5054.txt">RFC 5054</url>),
+ RC4, 3DES, DES cipher suites, and anonymous cipher suites only work if
+ explicitly enabled by this option; they are supported/enabled
+ by the peer also. Anonymous cipher suites are supported for
+ testing purposes only and are not be used when security
+ matters.</p>
+ </desc>
+ </datatype>
+
+ <datatype>
+ <name name="eccs"/>
+ <desc><p> Allows to specify the order of preference for named curves
+ and to restrict their usage when using a cipher suite supporting them.</p>
+ </desc>
+ </datatype>
- <p>The following options have the same meaning in the client and
- the server:</p>
+ <datatype>
+ <name name="signature_schemes"/>
+ <desc>
+ <p>
+ In addition to the signature_algorithms extension from TLS 1.2,
+ <url href="http://www.ietf.org/rfc/rfc8446.txt#section-4.2.3">TLS 1.3
+ (RFC 5246 Section 4.2.3)</url>adds the signature_algorithms_cert extension
+ which enables having special requirements on the signatures used in the
+ certificates that differs from the requirements on digital signatures as a whole.
+ If this is not required this extension is not needed.
+ </p>
+ <p>
+ The client will send a signature_algorithms_cert extension (ClientHello),
+ if TLS version 1.3 or later is used, and the signature_algs_cert option is
+ explicitly specified. By default, only the signature_algs extension is sent.
+ </p>
+ <p>
+ The signature schemes shall be ordered according to the client's preference
+ (favorite choice first).
+ </p>
+ </desc>
+ </datatype>
+
+ <datatype>
+ <name name="secure_renegotiation"/>
+ <desc><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>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>
+ </desc>
+ </datatype>
- <taglist>
-
- <tag><c>{protocol, tls | dtls}</c></tag>
- <item><p>Choose TLS or DTLS protocol for the transport layer security.
- Defaults to <c>tls</c> Introduced in OTP 20, DTLS support is considered
- experimental in this release. Other transports than UDP are not yet supported.</p></item>
-
- <tag><c>{handshake, hello | full}</c></tag>
- <item><p> Defaults to <c>full</c>. If hello is specified the handshake will
- pause after the hello message and give the user a possibility make decisions
- based on hello extensions before continuing or aborting the handshake by calling
- <seealso marker="#handshake_continue-3"> handshake_continue/3</seealso> or
- <seealso marker="#handshake_cancel-1"> handshake_cancel/1</seealso>
- </p></item>
-
- <tag><c>{cert, public_key:der_encoded()}</c></tag>
- <item><p>The DER-encoded users certificate. If this option
- is supplied, it overrides option <c>certfile</c>.</p></item>
-
- <tag><c>{certfile, path()}</c></tag>
- <item><p>Path to a file containing the user certificate.</p></item>
-
- <tag>
- <marker id="key_option_def"/>
- <c>{key, {'RSAPrivateKey'| 'DSAPrivateKey' | 'ECPrivateKey'
- |'PrivateKeyInfo', public_key:der_encoded()} | #{algorithm := rsa | dss | ecdsa,
- engine := crypto:engine_ref(), key_id := crypto:key_id(), password => crypto:password()}</c></tag>
- <item><p>The DER-encoded user's private key or a map refering to a crypto
- engine and its key reference that optionally can be password protected,
- seealso <seealso marker="crypto:crypto#engine_load-4"> crypto:engine_load/4
- </seealso> and <seealso marker="crypto:engine_load"> Crypto's Users Guide</seealso>. If this option
- is supplied, it overrides option <c>keyfile</c>.</p></item>
-
- <tag><c>{keyfile, path()}</c></tag>
- <item><p>Path to the file containing the user's
- private PEM-encoded key. As PEM-files can contain several
- entries, this option defaults to the same file as given by
- option <c>certfile</c>.</p></item>
-
- <tag><c>{password, string()}</c></tag>
- <item><p>String containing the user's password. Only used if the
- private keyfile is password-protected.</p></item>
-
- <tag><c>{ciphers, ciphers()}</c></tag>
- <item><p>Supported cipher suites. The function
- <c>cipher_suites/0</c> can be used to find all ciphers that are
- supported by default. <c>cipher_suites(all)</c> can be called
- to find all available cipher suites. Pre-Shared Key
- (<url href="http://www.ietf.org/rfc/rfc4279.txt">RFC 4279</url> and
- <url href="http://www.ietf.org/rfc/rfc5487.txt">RFC 5487</url>),
- Secure Remote Password
- (<url href="http://www.ietf.org/rfc/rfc5054.txt">RFC 5054</url>), RC4 cipher suites,
- and anonymous cipher suites only work if explicitly enabled by
- this option; they are supported/enabled by the peer also.
- Anonymous cipher suites are supported for testing purposes
- only and are not be used when security matters.</p></item>
-
- <tag><c>{eccs, [named_curve()]}</c></tag>
- <item><p> Allows to specify the order of preference for named curves
- and to restrict their usage when using a cipher suite supporting them.
- </p></item>
-
- <tag><c>{secure_renegotiate, boolean()}</c></tag>
- <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>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>
- </item>
-
- <tag><c>{depth, integer()}</c></tag>
- <item><p>Maximum number of non-self-issued
+ <datatype>
+ <name name="allowed_cert_chain_length"/>
+ <desc><p>Maximum number of non-self-issued
intermediate certificates that can follow the peer certificate
in a valid certification path. So, if depth is 0 the PEER must
be signed by the trusted ROOT-CA directly; if 1 the path can
be PEER, CA, ROOT-CA; if 2 the path can be PEER, CA, CA,
- ROOT-CA, and so on. The default value is 1.</p></item>
-
- <tag><marker id="verify_fun"/><c>{verify_fun, {Verifyfun :: fun(), InitialUserState ::
- term()}}</c></tag>
- <item><p>The verification fun is to be defined as follows:</p>
+ ROOT-CA, and so on. The default value is 1.</p>
+ </desc>
+ </datatype>
+
+ <datatype>
+ <name name="custom_verify"/>
+ <desc>
+ <p>The verification fun is to be defined as follows:</p>
<code>
-fun(OtpCert :: #'OTPCertificate'{}, Event :: {bad_cert, Reason :: atom() | {revoked,
-atom()}} |
+fun(OtpCert :: #'OTPCertificate'{}, Event :: {bad_cert, Reason :: atom() |
+ {revoked, atom()}} |
{extension, #'Extension'{}}, InitialUserState :: term()) ->
{valid, UserState :: term()} | {valid_peer, UserState :: term()} |
{fail, Reason :: term()} | {unknown, UserState :: term()}.
@@ -334,20 +399,21 @@ atom()}} |
<p>The verification fun is called during the X509-path
validation when an error or an extension unknown to the SSL
- application is encountered. It is also called
- when a certificate is considered valid by the path validation
- to allow access to each certificate in the path to the user
- application. It differentiates between the peer
- certificate and the CA certificates by using <c>valid_peer</c> or
- <c>valid</c> as second argument to the verification fun. See the
- <seealso marker="public_key:public_key_records">public_key User's
- Guide</seealso> for definition of <c>#'OTPCertificate'{}</c> and
- <c>#'Extension'{}</c>.</p>
+ application is encountered. It is also called when a
+ certificate is considered valid by the path validation to
+ allow access to each certificate in the path to the user
+ application. It differentiates between the peer certificate
+ and the CA certificates by using <c>valid_peer</c> or
+ <c>valid</c> as second argument to the verification fun. See
+ the <seealso marker="public_key:public_key_records">public_key
+ User's Guide</seealso> for definition of
+ <c>#'OTPCertificate'{}</c> and <c>#'Extension'{}</c>.</p>
<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/DTLS handshake terminates.</p></item>
+ <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/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
@@ -397,10 +463,12 @@ atom()}} |
<taglist>
<tag><c>unknown_ca</c></tag>
- <item><p>No trusted CA was found in the trusted store. The trusted CA is
- normally a so called ROOT CA, which is a self-signed certificate. Trust can
- be claimed for an intermediate CA (trusted anchor does not have to be
- self-signed according to X-509) by using option <c>partial_chain</c>.</p>
+ <item><p>No trusted CA was found in the trusted store. The
+ trusted CA is normally a so called ROOT CA, which is a
+ self-signed certificate. Trust can be claimed for an
+ intermediate CA (trusted anchor does not have to be
+ self-signed according to X-509) by using option
+ <c>partial_chain</c>.</p>
</item>
<tag><c>selfsigned_peer</c></tag>
@@ -411,15 +479,17 @@ atom()}} |
marker="public_key:public_key#pkix_path_validation-3">public_key:pkix_path_validation/3</seealso>
</p></item>
</taglist>
- </item>
-
- <tag><c>{crl_check, boolean() | peer | best_effort }</c></tag>
- <item>
+ </desc>
+ </datatype>
+
+ <datatype>
+ <name name="crl_check"/>
+ <desc>
<p>Perform CRL (Certificate Revocation List) verification
<seealso marker="public_key:public_key#pkix_crls_validate-3">
- (public_key:pkix_crls_validate/3)</seealso> on all the certificates during the path validation
- <seealso
- marker="public_key:public_key#pkix_path_validation-3">(public_key:pkix_path_validation/3)
+ (public_key:pkix_crls_validate/3)</seealso> on all the
+ certificates during the path validation <seealso
+ marker="public_key:public_key#pkix_path_validation-3">(public_key:pkix_path_validation/3)
</seealso>
of the certificate chain. Defaults to <c>false</c>.</p>
@@ -431,112 +501,111 @@ marker="public_key:public_key#pkix_path_validation-3">public_key:pkix_path_valid
<item>if certificate revocation status cannot be determined
it will be accepted as valid.</item>
</taglist>
-
+
<p>The CA certificates specified for the connection will be used to
construct the certificate chain validating the CRLs.</p>
<p>The CRLs will be fetched from a local or external cache. See
<seealso marker="ssl:ssl_crl_cache_api">ssl_crl_cache_api(3)</seealso>.</p>
- </item>
-
- <tag><c>{crl_cache, {Module :: atom(), {DbHandle :: internal | term(), Args :: list()}}}</c></tag>
- <item>
- <p>Specify how to perform lookup and caching of certificate revocation lists.
- <c>Module</c> defaults to <seealso marker="ssl:ssl_crl_cache">ssl_crl_cache</seealso>
- with <c> DbHandle </c> being <c>internal</c> and an
- empty argument list.</p>
-
- <p>There are two implementations available:</p>
-
- <taglist>
- <tag><c>ssl_crl_cache</c></tag>
- <item>
- <p>This module maintains a cache of CRLs. CRLs can be
- added to the cache using the function <seealso
- marker="ssl:ssl_crl_cache#insert-1">ssl_crl_cache:insert/1</seealso>,
- and optionally automatically fetched through HTTP if the
- following argument is specified:</p>
-
- <taglist>
- <tag><c>{http, timeout()}</c></tag>
- <item><p>
- Enables fetching of CRLs specified as http URIs in<seealso
- marker="public_key:public_key_records">X509 certificate extensions</seealso>.
- Requires the OTP inets application.</p>
- </item>
- </taglist>
- </item>
-
- <tag><c>ssl_crl_hash_dir</c></tag>
- <item>
- <p>This module makes use of a directory where CRLs are
- stored in files named by the hash of the issuer name.</p>
-
- <p>The file names consist of eight hexadecimal digits
- followed by <c>.rN</c>, where <c>N</c> is an integer,
- e.g. <c>1a2b3c4d.r0</c>. For the first version of the
- CRL, <c>N</c> starts at zero, and for each new version,
- <c>N</c> is incremented by one. The OpenSSL utility
- <c>c_rehash</c> creates symlinks according to this
- pattern.</p>
-
- <p>For a given hash value, this module finds all
- consecutive <c>.r*</c> files starting from zero, and those
- files taken together make up the revocation list. CRL
- files whose <c>nextUpdate</c> fields are in the past, or
- that are issued by a different CA that happens to have the
- same name hash, are excluded.</p>
-
- <p>The following argument is required:</p>
-
- <taglist>
- <tag><c>{dir, string()}</c></tag>
- <item><p>Specifies the directory in which the CRLs can be found.</p></item>
- </taglist>
-
- </item>
-
- <tag><c>max_handshake_size</c></tag>
- <item>
- <p>Integer (24 bits unsigned). Used to limit the size of
- valid TLS handshake packets to avoid DoS attacks.
- Defaults to 256*1024.</p>
- </item>
-
- </taglist>
-
- </item>
+ </desc>
+ </datatype>
- <tag><c>{partial_chain, fun(Chain::[DerCert]) -> {trusted_ca, DerCert} |
- unknown_ca }</c></tag>
- <item><p>Claim an intermediate CA in the chain as trusted. TLS then
- performs <seealso
- 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>
+ <datatype>
+ <name name="crl_cache_opts"/>
+ <desc>
+ <p>Specify how to perform lookup and caching of certificate revocation lists.
+ <c>Module</c> defaults to <seealso marker="ssl:ssl_crl_cache">ssl_crl_cache</seealso>
+ with <c> DbHandle </c> being <c>internal</c> and an
+ empty argument list.</p>
+
+ <p>There are two implementations available:</p>
+
+ <taglist>
+ <tag><c>ssl_crl_cache</c></tag>
+ <item>
+ <p>This module maintains a cache of CRLs. CRLs can be
+ added to the cache using the function <seealso
+ marker="ssl:ssl_crl_cache#insert-1">ssl_crl_cache:insert/1</seealso>,
+ and optionally automatically fetched through HTTP if the
+ following argument is specified:</p>
+
+ <taglist>
+ <tag><c>{http, timeout()}</c></tag>
+ <item><p>
+ Enables fetching of CRLs specified as http URIs in<seealso
+ marker="public_key:public_key_records">X509 certificate extensions</seealso>.
+ Requires the OTP inets application.</p>
+ </item>
+ </taglist>
+ </item>
+
+ <tag><c>ssl_crl_hash_dir</c></tag>
+ <item>
+ <p>This module makes use of a directory where CRLs are
+ stored in files named by the hash of the issuer name.</p>
+
+ <p>The file names consist of eight hexadecimal digits
+ followed by <c>.rN</c>, where <c>N</c> is an integer,
+ e.g. <c>1a2b3c4d.r0</c>. For the first version of the
+ CRL, <c>N</c> starts at zero, and for each new version,
+ <c>N</c> is incremented by one. The OpenSSL utility
+ <c>c_rehash</c> creates symlinks according to this
+ pattern.</p>
+
+ <p>For a given hash value, this module finds all
+ consecutive <c>.r*</c> files starting from zero, and those
+ files taken together make up the revocation list. CRL
+ files whose <c>nextUpdate</c> fields are in the past, or
+ that are issued by a different CA that happens to have the
+ same name hash, are excluded.</p>
+
+ <p>The following argument is required:</p>
+
+ <taglist>
+ <tag><c>{dir, string()}</c></tag>
+ <item><p>Specifies the directory in which the CRLs can be found.</p></item>
+ </taglist>
+ </item>
+ </taglist>
+ </desc>
+ </datatype>
+
+ <datatype>
+ <name name="root_fun"/>
+ <desc>
+ <code>
+fun(Chain::[public_key:der_encoded()]) ->
+ {trusted_ca, DerCert::public_key:der_encoded()} | unknown_ca}
+ </code>
+ <p>Claim an intermediate CA in the chain as trusted. TLS then
+ performs <seealso
+ 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>
+ </desc>
+ </datatype>
- <tag><c>{versions, [protocol_version()]}</c></tag>
- <item><p>TLS protocol versions supported by started clients and servers.
+ <datatype>
+ <name name="protocol_versions"/>
+ <desc><p>TLS protocol versions supported by started clients and servers.
This option overrides the application environment option
<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>
+ See also <seealso marker="ssl:ssl_app">ssl(6).</seealso></p>
+ </desc>
+ </datatype>
- <tag><c>{hibernate_after, integer()|undefined}</c></tag>
- <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
- never goes into hibernation.</p></item>
- <tag><c>{user_lookup_fun, {Lookupfun :: fun(), UserState :: term()}}</c></tag>
- <item><p>The lookup fun is to defined as follows:</p>
+ <datatype>
+ <name name="custom_user_lookup"/>
+ <desc><p>The lookup fun is to defined as follows:</p>
<code>
fun(psk, PSKIdentity ::string(), UserState :: term()) ->
{ok, SharedSecret :: binary()} | error;
fun(srp, Username :: string(), UserState :: term()) ->
- {ok, {SRPParams :: srp_param_type(), Salt :: binary(), DerivedKey :: binary()}} | error.
+ {ok, {SRPParams :: srp_param_type(), Salt :: binary(),
+ DerivedKey :: binary()}} | error.
</code>
<p>For Pre-Shared Key (PSK) cipher suites, the lookup fun is
@@ -552,20 +621,63 @@ fun(srp, Username :: string(), UserState :: term()) ->
<url href="http://tools.ietf.org/html/rfc5054#section-2.4"> RFC 5054</url>:
<c>crypto:sha([Salt, crypto:sha([Username, &lt;&lt;$:&gt;&gt;, Password])])</c>
</p>
- </item>
+ </desc>
+ </datatype>
- <tag><c>{padding_check, boolean()}</c></tag>
- <item><p>Affects TLS-1.0 connections only.
+ <datatype>
+ <name name="session_id"/>
+ <desc>
+ <p>Identifies a TLS session.</p>
+ </desc>
+ </datatype>
+
+ <datatype>
+ <name name="log_alert"/>
+ <desc><p>If set to <c>false</c>, error reports are not displayed.
+ Deprecated in OTP 22, use {log_level, <seealso marker="#type-logging_level">logging_level()</seealso>} instead.</p>
+ </desc>
+ </datatype>
+
+ <datatype>
+ <name name="logging_level"/>
+ <desc><p>Specifies the log level for TLS/DTLS. At verbosity level <c>notice</c> and above error reports are
+ displayed in TLS. The level <c>debug</c> triggers verbose logging of TLS protocol
+ messages and logging of ignored alerts in DTLS.</p>
+ </desc>
+ </datatype>
+
+ <datatype>
+ <name name="hibernate_after"/>
+ <desc><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
+ never goes into hibernation.</p>
+ </desc>
+ </datatype>
+
+ <datatype>
+ <name name="handshake_size"/>
+ <desc>
+ <p>Integer (24 bits unsigned). Used to limit the size of
+ valid TLS handshake packets to avoid DoS attacks.
+ Defaults to 256*1024.</p>
+ </desc>
+ </datatype>
+
+ <datatype>
+ <name name="padding_check"/>
+ <desc><p>Affects TLS-1.0 connections only.
If set to <c>false</c>, it disables the block cipher padding check
to be able to interoperate with legacy software.</p>
<warning><p>Using <c>{padding_check, boolean()}</c> makes TLS
vulnerable to the Poodle attack.</p></warning>
- </item>
-
-
+ </desc>
+ </datatype>
- <tag><c>{beast_mitigation, one_n_minus_one | zero_n | disabled}</c></tag>
- <item><p>Affects SSL-3.0 and TLS-1.0 connections only. Used to change the BEAST
+ <datatype>
+ <name name="beast_mitigation"/>
+ <desc><p>Affects SSL-3.0 and TLS-1.0 connections only. Used to change the BEAST
mitigation strategy to interoperate with legacy software.
Defaults to <c>one_n_minus_one</c>.</p>
@@ -575,139 +687,170 @@ fun(srp, Username :: string(), UserState :: term()) ->
<p><c>disabled</c> - Disable BEAST mitigation.</p>
- <warning><p>Using <c>{beast_mitigation, disabled}</c> makes SSL or TLS
+ <warning><p>Using <c>{beast_mitigation, disabled}</c> makes SSL-3.0 or TLS-1.0
vulnerable to the BEAST attack.</p></warning>
- </item>
- </taglist>
-
- </section>
-
- <section>
- <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>
-
- <taglist>
+ </desc>
+ </datatype>
+
+ <datatype>
+ <name name="ssl_imp"/>
+ <desc><p>Deprecated since OTP-17, has no affect.</p></desc>
+ </datatype>
+
+ <datatype_title>TLS/DTLS OPTION DESCRIPTIONS - CLIENT</datatype_title>
+
+ <datatype>
+ <name name="client_option"/>
+ </datatype>
+
+ <datatype>
+ <name name="client_verify_type"/>
+ <desc><p>In mode <c>verify_none</c> the default behavior is to allow
+ all x509-path validation errors. See also option <seealso marker="#type-custom_verify">verify_fun</seealso>.</p>
+ </desc>
+ </datatype>
- <tag><c>{verify, verify_type()}</c></tag>
- <item><p>In mode <c>verify_none</c> the default behavior is to allow
- all x509-path validation errors. See also option <c>verify_fun</c>.</p>
- </item>
-
- <tag><marker id="client_reuse_session"/><c>{reuse_session, binary()}</c></tag>
- <item><p>Reuses a specific session earlier saved with the option
- <c>{reuse_sessions, save} since ssl-9.2</c>
- </p></item>
+ <datatype>
+ <name name="client_reuse_session"/>
+ <desc>
+ <p>Reuses a specific session earlier saved with the option
+ <c>{reuse_sessions, save} since OTP-21.3 </c>
+ </p>
+ </desc>
+ </datatype>
- <tag><c>{reuse_sessions, boolean() | save}</c></tag>
- <item><p>When <c>save</c> is specified a new connection will be negotiated
+ <datatype>
+ <name name="client_reuse_sessions"/>
+ <desc>
+ <p>When <c>save</c> is specified a new connection will be negotiated
and saved for later reuse. The session ID can be fetched with
- <seealso marker="#connection_information">connection_information/2</seealso>
- and used with the client option <seealso marker="#client_reuse_session">reuse_session</seealso>
+ <seealso marker="#connection_information-2">connection_information/2</seealso>
+ and used with the client option <seealso marker="#type-client_reuse_session">reuse_session</seealso>
The boolean value true specifies that if possible, automatized session reuse will
be performed. If a new session is created, and is unique in regard
- to previous stored sessions, it will be saved for possible later reuse.
- Value <c>save</c> since ssl-9.2
- </p></item>
-
- <tag><c>{cacerts, [public_key:der_encoded()]}</c></tag>
- <item><p>The DER-encoded trusted certificates. If this option
- is supplied it overrides option <c>cacertfile</c>.</p></item>
-
- <tag><c>{cacertfile, path()}</c></tag>
- <item><p>Path to a file containing PEM-encoded CA certificates. The CA
+ to previous stored sessions, it will be saved for possible later reuse. Since OTP-21.3</p>
+ </desc>
+ </datatype>
+
+ <datatype>
+ <name name="client_cacerts"/>
+ <desc>
+ <p>The DER-encoded trusted certificates. If this option
+ is supplied it overrides option <c>cacertfile</c>.</p>
+ </desc>
+ </datatype>
+
+ <datatype>
+ <name name="client_cafile"/>
+ <desc>
+ <p>Path to a file containing PEM-encoded CA certificates. The CA
certificates are used during server authentication and when building the
client certificate chain.</p>
- </item>
-
- <tag><c>{alpn_advertised_protocols, [binary()]}</c></tag>
- <item>
- <p>The list of protocols supported by the client to be sent to the
- server to be used for an Application-Layer Protocol Negotiation (ALPN).
- If the server supports ALPN then it will choose a protocol from this
- list; otherwise it will fail the connection with a "no_application_protocol"
- alert. A server that does not support ALPN will ignore this value.</p>
-
- <p>The list of protocols must not contain an empty binary.</p>
-
- <p>The negotiated protocol can be retrieved using the <c>negotiated_protocol/1</c> function.</p>
- </item>
-
- <tag><c>{client_preferred_next_protocols, {Precedence :: server | client, ClientPrefs :: [binary()]}}</c><br/>
- <c>{client_preferred_next_protocols, {Precedence :: server | client, ClientPrefs :: [binary()], Default :: binary()}}</c></tag>
- <item>
- <p>Indicates that the client is to try to perform Next Protocol
- Negotiation.</p>
-
- <p>If precedence is server, the negotiated protocol is the
- first protocol to be shown on the server advertised list, which is
- also on the client preference list.</p>
-
- <p>If precedence is client, the negotiated protocol is the
- first protocol to be shown on the client preference list, which is
- also on the server advertised list.</p>
-
- <p>If the client does not support any of the server advertised
- protocols or the server does not advertise any protocols, the
- client falls back to the first protocol in its list or to the
- default protocol (if a default is supplied). If the
- server does not support Next Protocol Negotiation, the
- connection terminates if no default protocol is supplied.</p>
- </item>
-
- <tag><c>{psk_identity, string()}</c></tag>
- <item><p>Specifies the identity the client presents to the server.
- The matching secret is found by calling <c>user_lookup_fun</c>.</p>
- </item>
-
- <tag><c>{srp_identity, {Username :: string(), Password :: string()}
- </c></tag>
- <item><p>Specifies the username and password to use to authenticate
- to the server.</p></item>
-
- <tag><c>{server_name_indication, HostName :: hostname()}</c></tag>
- <item><p>Specify the hostname to be used in TLS Server Name Indication extension.
- If not specified it will default to the <c>Host</c> argument of <seealso marker="#connect-3">connect/[3,4]</seealso>
- unless it is of type inet:ipaddress().</p>
- <p>
- The <c>HostName</c> will also be used in the hostname verification of the peer certificate using
- <seealso marker="public_key:public_key#pkix_verify_hostname-2">public_key:pkix_verify_hostname/2</seealso>.
- </p>
- </item>
- <tag><c>{server_name_indication, disable}</c></tag>
- <item>
- <p> Prevents the Server Name Indication extension from being sent and
- disables the hostname verification check
- <seealso marker="public_key:public_key#pkix_verify_hostname-2">public_key:pkix_verify_hostname/2</seealso> </p>
- </item>
-
- <tag><c>{customize_hostname_check, Options::list()}</c></tag>
- <item>
- <p> Customizes the hostname verification of the peer certificate, as different protocols that use
+ </desc>
+ </datatype>
+
+ <datatype>
+ <name name="client_alpn"/>
+ <desc>
+ <p>The list of protocols supported by the client to be sent to the
+ server to be used for an Application-Layer Protocol Negotiation (ALPN).
+ If the server supports ALPN then it will choose a protocol from this
+ list; otherwise it will fail the connection with a "no_application_protocol"
+ alert. A server that does not support ALPN will ignore this value.</p>
+
+ <p>The list of protocols must not contain an empty binary.</p>
+
+ <p>The negotiated protocol can be retrieved using the <c>negotiated_protocol/1</c> function.</p>
+ </desc>
+ </datatype>
+
+ <datatype>
+ <name name="client_preferred_next_protocols"/>
+ <desc>
+ <p>Indicates that the client is to try to perform Next Protocol
+ Negotiation.</p>
+
+ <p>If precedence is server, the negotiated protocol is the
+ first protocol to be shown on the server advertised list, which is
+ also on the client preference list.</p>
+
+ <p>If precedence is client, the negotiated protocol is the
+ first protocol to be shown on the client preference list, which is
+ also on the server advertised list.</p>
+
+ <p>If the client does not support any of the server advertised
+ protocols or the server does not advertise any protocols, the
+ client falls back to the first protocol in its list or to the
+ default protocol (if a default is supplied). If the
+ server does not support Next Protocol Negotiation, the
+ connection terminates if no default protocol is supplied.</p>
+ </desc>
+ </datatype>
+
+ <datatype>
+ <name name="client_psk_identity"/>
+ <desc>
+ <p>Specifies the identity the client presents to the server.
+ The matching secret is found by calling <c>user_lookup_fun</c></p>
+ </desc>
+ </datatype>
+
+ <datatype>
+ <name name="client_srp_identity"/>
+ <desc>
+ <p>Specifies the username and password to use to authenticate
+ to the server.</p>
+ </desc>
+ </datatype>
+
+ <datatype>
+ <name name="sni"/>
+ <desc>
+ <p>Specify the hostname to be used in TLS Server Name Indication extension.
+ If not specified it will default to the <c>Host</c> argument of <seealso marker="#connect-3">connect/[3,4]</seealso>
+ unless it is of type inet:ipaddress().</p>
+ <p>
+ The <c>HostName</c> will also be used in the hostname verification of the peer certificate using
+ <seealso marker="public_key:public_key#pkix_verify_hostname-2">public_key:pkix_verify_hostname/2</seealso>.
+ </p>
+ <p> The special value <c>disable</c> prevents the Server Name Indication extension from being sent and
+ disables the hostname verification check
+ <seealso marker="public_key:public_key#pkix_verify_hostname-2">public_key:pkix_verify_hostname/2</seealso> </p>
+ </desc>
+ </datatype>
+
+ <datatype>
+ <name name="customize_hostname_check"/>
+ <desc>
+ <p> Customizes the hostname verification of the peer certificate, as different protocols that use
TLS such as HTTP or LDAP may want to do it differently, for possible options see
<seealso marker="public_key:public_key#pkix_verify_hostname-3">public_key:pkix_verify_hostname/3</seealso> </p>
- </item>
-
- <tag><c>{fallback, boolean()}</c></tag>
- <item>
- <p> Send special cipher suite TLS_FALLBACK_SCSV to avoid undesired TLS version downgrade.
- Defaults to false</p>
- <warning><p>Note this option is not needed in normal TLS usage and should not be used
- to implement new clients. But legacy clients that retries connections in the following manner</p>
-
- <p><c> ssl:connect(Host, Port, [...{versions, ['tlsv2', 'tlsv1.1', 'tlsv1', 'sslv3']}])</c></p>
- <p><c> ssl:connect(Host, Port, [...{versions, [tlsv1.1', 'tlsv1', 'sslv3']}, {fallback, true}])</c></p>
- <p><c> ssl:connect(Host, Port, [...{versions, ['tlsv1', 'sslv3']}, {fallback, true}]) </c></p>
- <p><c> ssl:connect(Host, Port, [...{versions, ['sslv3']}, {fallback, true}]) </c></p>
-
- <p>may use it to avoid undesired TLS version downgrade. Note that TLS_FALLBACK_SCSV must also
- be supported by the server for the prevention to work.
- </p></warning>
- </item>
- <tag><marker id="client_signature_algs"/><c>{signature_algs, [{hash(), ecdsa | rsa | dsa}]}</c></tag>
- <item>
- <p>In addition to the algorithms negotiated by the cipher
+ </desc>
+ </datatype>
+
+ <datatype>
+ <name name="fallback"/>
+ <desc>
+ <p> Send special cipher suite TLS_FALLBACK_SCSV to avoid undesired TLS version downgrade.
+ Defaults to false</p>
+ <warning><p>Note this option is not needed in normal TLS usage and should not be used
+ to implement new clients. But legacy clients that retries connections in the following manner</p>
+
+ <p><c> ssl:connect(Host, Port, [...{versions, ['tlsv2', 'tlsv1.1', 'tlsv1', 'sslv3']}])</c></p>
+ <p><c> ssl:connect(Host, Port, [...{versions, [tlsv1.1', 'tlsv1', 'sslv3']}, {fallback, true}])</c></p>
+ <p><c> ssl:connect(Host, Port, [...{versions, ['tlsv1', 'sslv3']}, {fallback, true}]) </c></p>
+ <p><c> ssl:connect(Host, Port, [...{versions, ['sslv3']}, {fallback, true}]) </c></p>
+
+ <p>may use it to avoid undesired TLS version downgrade. Note that TLS_FALLBACK_SCSV must also
+ be supported by the server for the prevention to work.
+ </p></warning>
+ </desc>
+ </datatype>
+
+ <datatype>
+ <name name="client_signature_algs"/>
+ <desc>
+ <p>In addition to the algorithms negotiated by the cipher
suite used for key exchange, payload encryption, message
authentication and pseudo random calculation, the TLS signature
algorithm extension <url
@@ -738,209 +881,227 @@ fun(srp, Username :: string(), UserState :: term()) ->
Selected signature algorithm can restrict which hash functions
that may be selected. Default support for {md5, rsa} removed in ssl-8.0
</p>
- </item>
- <tag><marker id="signature_algs_cert"/><c>{signature_algs_cert, [signature_scheme()]}</c></tag>
- <item>
- <p>
- In addition to the signature_algorithms extension from TLS 1.2,
- <url href="http://www.ietf.org/rfc/rfc8446.txt#section-4.2.3">TLS 1.3
- (RFC 5246 Section 4.2.3)</url>adds the signature_algorithms_cert extension
- which enables having special requirements on the signatures used in the
- certificates that differs from the requirements on digital signatures as a whole.
- If this is not required this extension is not needed.
- </p>
- <p>
- The client will send a signature_algorithms_cert extension (ClientHello),
- if TLS version 1.3 or later is used, and the signature_algs_cert option is
- explicitly specified. By default, only the signature_algs extension is sent.
- </p>
- <p>
- The signature schemes shall be ordered according to the client's preference
- (favorite choice first).
- </p>
- </item>
- </taglist>
- </section>
-
- <section>
- <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>
-
- <taglist>
-
- <tag><c>{cacerts, [public_key:der_encoded()]}</c></tag>
- <item><p>The DER-encoded trusted certificates. If this option
- is supplied it overrides option <c>cacertfile</c>.</p></item>
+ </desc>
+ </datatype>
- <tag><c>{cacertfile, path()}</c></tag>
- <item><p>Path to a file containing PEM-encoded CA
- certificates. The CA certificates are used to build the server
- certificate chain and for client authentication. The CAs are
- also used in the list of acceptable client CAs passed to the
- client when a certificate is requested. Can be omitted if there
- is no need to verify the client and if there are no
- intermediate CAs for the server certificate.</p></item>
-
- <tag><c>{dh, public_key:der_encoded()}</c></tag>
- <item><p>The DER-encoded Diffie-Hellman parameters. If specified,
- it overrides option <c>dhfile</c>.</p></item>
-
- <tag><c>{dhfile, path()}</c></tag>
- <item><p>Path to a file containing PEM-encoded Diffie Hellman parameters
- to be used by the server if a cipher suite using Diffie Hellman key
- exchange is negotiated. If not specified, default parameters are used.
- </p></item>
-
- <tag><c>{verify, verify_type()}</c></tag>
- <item><p>A server only does x509-path validation in mode <c>verify_peer</c>,
- as it then sends a certificate request to the client
- (this message is not sent if the verify option is <c>verify_none</c>).
- You can then also want to specify option <c>fail_if_no_peer_cert</c>.
- </p></item>
-
- <tag><c>{fail_if_no_peer_cert, boolean()}</c></tag>
- <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
- certificate (an empty certificate is considered valid). Defaults to false.</p>
- </item>
-
- <tag><c>{reuse_sessions, boolean()}</c></tag>
- <item><p>The boolean value true specifies that the server will
- agree to reuse sessions. Setting it to false will result in an empty
- session table, that is no sessions will be reused.
- See also option <seealso marker="#server_reuse_session">reuse_session</seealso>
- </p></item>
-
- <tag><marker id="server_reuse_session"/>
- <c>{reuse_session, fun(SuggestedSessionId,
- PeerCert, Compression, CipherSuite) -> boolean()}</c></tag>
- <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
- a DER-encoded certificate, <c>Compression</c> is an enumeration integer,
- and <c>CipherSuite</c> is of type <c>ciphersuite()</c>.</p></item>
-
- <tag><c>{alpn_preferred_protocols, [binary()]}</c></tag>
- <item>
- <p>Indicates the server will try to perform Application-Layer
- Protocol Negotiation (ALPN).</p>
-
- <p>The list of protocols is in order of preference. The protocol
- negotiated will be the first in the list that matches one of the
- protocols advertised by the client. If no protocol matches, the
- server will fail the connection with a "no_application_protocol" alert.</p>
-
- <p>The negotiated protocol can be retrieved using the <c>negotiated_protocol/1</c> function.</p>
- </item>
-
- <tag><c>{next_protocols_advertised, Protocols :: [binary()]}</c></tag>
- <item><p>List of protocols to send to the client if the client indicates that
- it supports the Next Protocol extension. The client can select a protocol
- that is not on this list. The list of protocols must not contain an empty
- binary. If the server negotiates a Next Protocol, it can be accessed
- using the <c>negotiated_next_protocol/1</c> method.</p></item>
-
- <tag><c>{psk_identity, string()}</c></tag>
- <item><p>Specifies the server identity hint, which the server presents to
- the client.</p></item>
-
- <tag><c>{log_alert, boolean()}</c></tag>
- <item><p>If set to <c>false</c>, error reports are not displayed.</p>
- <p>Deprecated in OTP 22, use <seealso marker="#log_level">log_level</seealso> instead.</p>
- </item>
-
- <tag><marker id="log_level"/><c>{log_level, atom()}</c></tag>
- <item><p>Specifies the log level for TLS/DTLS. It can take the following
- values (ordered by increasing verbosity level): <c>emergency, alert, critical, error,
- warning, notice, info, debug.</c></p>
- <p>At verbosity level <c>notice</c> and above error reports are
- displayed in TLS. The level <c>debug</c> triggers verbose logging of TLS protocol
- messages and logging of ignored alerts in DTLS.</p></item>
-
- <tag><c>{honor_cipher_order, boolean()}</c></tag>
- <item><p>If set to <c>true</c>, use the server preference for cipher
- selection. If set to <c>false</c> (the default), use the client
- preference.</p></item>
-
- <tag><c>{sni_hosts, [{hostname(), [ssl_option()]}]}</c></tag>
- <item><p>If the server receives a SNI (Server Name Indication) from the client
- matching a host listed in the <c>sni_hosts</c> option, the specific options for
- that host will override previously specified options.
-
- The option <c>sni_fun</c>, and <c>sni_hosts</c> are mutually exclusive.</p></item>
-
- <tag><c>{sni_fun, SNIfun::fun()}</c></tag>
- <item><p>If the server receives a SNI (Server Name Indication) from the client,
- the given function will be called to retrieve <c>[ssl_option()]</c> for the indicated server.
- These options will be merged into predefined <c>[ssl_option()]</c>.
-
- The function should be defined as:
- <c>fun(ServerName :: string()) -> [ssl_option()]</c>
- and can be specified as a fun or as named <c>fun module:function/1</c>
-
- The option <c>sni_fun</c>, and <c>sni_hosts</c> are mutually exclusive.</p></item>
-
- <tag><c>{client_renegotiation, boolean()}</c></tag>
- <item>In protocols that support client-initiated renegotiation, the cost
- of resources of such an operation is higher for the server than the
- client. This can act as a vector for denial of service attacks. The SSL
- application already takes measures to counter-act such attempts,
- but client-initiated renegotiation can be strictly disabled by setting
- this option to <c>false</c>. The default value is <c>true</c>.
- Note that disabling renegotiation can result in long-lived connections
- becoming unusable due to limits on the number of messages the underlying
- cipher suite can encipher.
- </item>
-
- <tag><c>{honor_cipher_order, boolean()}</c></tag>
- <item>If true, use the server's preference for cipher selection. If false
- (the default), use the client's preference.
- </item>
+ <datatype_title>TLS/DTLS OPTION DESCRIPTIONS - SERVER </datatype_title>
+
+
+ <datatype>
+ <name name="server_option"/>
+ </datatype>
+
+ <datatype>
+ <name name="server_cacerts"/>
+ <desc><p>The DER-encoded trusted certificates. If this option
+ is supplied it overrides option <c>cacertfile</c>.</p>
+ </desc>
+ </datatype>
+
+ <datatype>
+ <name name="server_cafile"/>
+ <desc><p>Path to a file containing PEM-encoded CA
+ certificates. The CA certificates are used to build the server
+ certificate chain and for client authentication. The CAs are
+ also used in the list of acceptable client CAs passed to the
+ client when a certificate is requested. Can be omitted if
+ there is no need to verify the client and if there are no
+ intermediate CAs for the server certificate.</p>
+ </desc>
+ </datatype>
+
+ <datatype>
+ <name name="dh_der"/>
+ <desc><p>The DER-encoded Diffie-Hellman parameters. If
+ specified, it overrides option <c>dhfile</c>.</p>
+ </desc>
+ </datatype>
+
+ <datatype>
+ <name name="dh_file"/>
+ <desc><p>Path to a file containing PEM-encoded Diffie Hellman
+ parameters to be used by the server if a cipher suite using
+ Diffie Hellman key exchange is negotiated. If not specified,
+ default parameters are used.</p>
+ </desc>
+ </datatype>
+
+ <datatype>
+ <name name="server_verify_type"/>
+ <desc><p>A server only does x509-path validation in mode
+ <c>verify_peer</c>, as it then sends a certificate request to
+ the client (this message is not sent if the verify option is
+ <c>verify_none</c>). You can then also want to specify option
+ <c>fail_if_no_peer_cert</c>. </p>
+ </desc>
+ </datatype>
+
+ <datatype>
+ <name name="fail_if_no_peer_cert"/>
+ <desc><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 certificate (an empty certificate
+ is considered valid). Defaults to false.</p>
+ </desc>
+ </datatype>
- <tag><c>{honor_ecc_order, boolean()}</c></tag>
- <item>If true, use the server's preference for ECC curve selection. If false
- (the default), use the client's preference.
- </item>
-
- <tag><c>{signature_algs, [{hash(), ecdsa | rsa | dsa}]}</c></tag>
- <item><p> The algorithms specified by
- this option will be the ones accepted by the server in a signature algorithm
- negotiation, introduced in TLS-1.2. The algorithms will also be offered to the client if a
- client certificate is requested. For more details see the <seealso marker="#client_signature_algs">corresponding client option</seealso>.
- </p> </item>
- </taglist>
- </section>
-
- <section>
- <title>General</title>
+ <datatype>
+ <name name="server_reuse_sessions"/>
+ <desc><p>The boolean value true specifies that the server will
+ agree to reuse sessions. Setting it to false will result in an empty
+ session table, that is no sessions will be reused.
+ See also option <seealso marker="#type-server_reuse_session">reuse_session</seealso>
+ </p>
+ </desc>
+ </datatype>
- <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>
+ <datatype>
+ <name name="server_reuse_session"/>
+ <desc><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 a DER-encoded certificate,
+ <c>Compression</c> is an enumeration integer, and
+ <c>CipherSuite</c> is of type <c>ciphersuite()</c>.</p>
+ </desc>
+ </datatype>
+
+ <datatype>
+ <name name="server_alpn"/>
+ <desc>
+ <p>Indicates the server will try to perform
+ Application-Layer Protocol Negotiation (ALPN).</p>
+
+ <p>The list of protocols is in order of preference. The
+ protocol negotiated will be the first in the list that
+ matches one of the protocols advertised by the client. If no
+ protocol matches, the server will fail the connection with a
+ "no_application_protocol" alert.</p>
+
+ <p>The negotiated protocol can be retrieved using the
+ <c>negotiated_protocol/1</c> function.</p>
+ </desc>
+ </datatype>
+
+ <datatype>
+ <name name="server_next_protocol"/>
+ <desc><p>List of protocols to send to the client if the client
+ indicates that it supports the Next Protocol extension. The
+ client can select a protocol that is not on this list. The
+ list of protocols must not contain an empty binary. If the
+ server negotiates a Next Protocol, it can be accessed using
+ the <c>negotiated_next_protocol/1</c> method.</p>
+ </desc>
+ </datatype>
+
+ <datatype>
+ <name name="server_psk_identity"/>
+ <desc>
+ <p>Specifies the server identity hint, which the server presents to
+ the client.</p>
+ </desc>
+ </datatype>
+
+ <datatype>
+ <name name="honor_cipher_order"/>
+ <desc>
+ <p>If set to <c>true</c>, use the server preference for cipher
+ selection. If set to <c>false</c> (the default), use the client
+ preference.</p>
+ </desc>
+ </datatype>
+
+ <datatype>
+ <name name="sni_hosts"/>
+ <desc><p>If the server receives a SNI (Server Name Indication) from the client
+ matching a host listed in the <c>sni_hosts</c> option, the specific options for
+ that host will override previously specified options.
+
+ The option <c>sni_fun</c>, and <c>sni_hosts</c> are mutually exclusive.</p>
+ </desc>
+ </datatype>
+
+ <datatype>
+ <name name="sni_fun"/>
+ <desc>
+ <p>If the server receives a SNI (Server Name Indication)
+ from the client, the given function will be called to
+ retrieve <seealso marker="#type-server_option">[server_option()] </seealso> for the indicated server.
+ These options will be merged into predefined
+ <seealso marker="#type-server_option">[server_option()] </seealso> list.
+
+ The function should be defined as:
+ fun(ServerName :: string()) -> <seealso marker="#type-server_option">[server_option()] </seealso>
+ and can be specified as a fun or as named <c>fun module:function/1</c>
+
+ The option <c>sni_fun</c>, and <c>sni_hosts</c> are mutually exclusive.</p>
+ </desc>
+ </datatype>
+
+ <datatype>
+ <name name="client_renegotiation"/>
+ <desc><p>In protocols that support client-initiated
+ renegotiation, the cost of resources of such an operation is
+ higher for the server than the client. This can act as a
+ vector for denial of service attacks. The SSL application
+ already takes measures to counter-act such attempts, but
+ client-initiated renegotiation can be strictly disabled by
+ setting this option to <c>false</c>. The default value is
+ <c>true</c>. Note that disabling renegotiation can result in
+ long-lived connections becoming unusable due to limits on the
+ number of messages the underlying cipher suite can
+ encipher.</p>
+ </desc>
+ </datatype>
+
+ <datatype>
+ <name name="honor_cipher_order"/>
+ <desc><p>If true, use the server's preference for cipher
+ selection. If false (the default), use the client's
+ preference.</p>
+ </desc>
+ </datatype>
+
+ <datatype>
+ <name name="honor_ecc_order"/>
+ <desc><p>If true, use the server's preference for ECC curve
+ selection. If false (the default), use the client's
+ preference.</p>
+ </desc>
+ </datatype>
+
+ <datatype>
+ <name name="server_signature_algs"/>
+ <desc><p> The algorithms specified by this option will be the
+ ones accepted by the server in a signature algorithm
+ negotiation, introduced in TLS-1.2. The algorithms will also
+ be offered to the client if a client certificate is
+ requested. For more details see the <seealso
+ marker="#type-client_signature_algs">corresponding client
+ option</seealso>.
+ </p>
+ </desc>
+ </datatype>
+ </datatypes>
- <list type="bulleted">
- <item><p><c>{ssl, Socket, Data}</c></p></item>
- <item><p><c>{ssl_closed, Socket}</c></p></item>
- <item><p><c>{ssl_error, Socket, Reason}</c></p></item>
- </list>
+<!--
+ ================================================================
+ = Function definitions =
+ ================================================================
+-->
- <p>A <c>Timeout</c> argument specifies a time-out in milliseconds. The
- default value for argument <c>Timeout</c> is <c>infinity</c>.</p>
- </section>
-
<funcs>
<func>
<name since="OTP 20.3">append_cipher_suites(Deferred, Suites) -> ciphers() </name>
<fsummary></fsummary>
<type>
- <v>Deferred = ciphers() | cipher_filters() </v>
- <v>Suites = ciphers() </v>
+ <v>Deferred = <seealso marker="#type-ciphers">ciphers()</seealso> |
+ <seealso marker="#type-cipher_filters">cipher_filters()</seealso></v>
+ <v>Suites = <seealso marker="#type-ciphers">ciphers()</seealso></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
@@ -953,7 +1114,7 @@ fun(srp, Username :: string(), UserState :: term()) ->
<func>
<name since="OTP R14B">cipher_suites() -></name>
- <name since="OTP R14B">cipher_suites(Type) -> old_ciphers()</name>
+ <name since="OTP R14B">cipher_suites(Type) -> [old_cipher_suite()]</name>
<fsummary>Returns a list of supported cipher suites.</fsummary>
<type>
<v>Type = erlang | openssl | all</v>
@@ -969,7 +1130,7 @@ fun(srp, Username :: string(), UserState :: term()) ->
all supported cipher suites.</fsummary>
<type>
<v> Supported = default | all | anonymous </v>
- <v> Version = protocol_version() </v>
+ <v> Version = <seealso marker="#type-protocol_version">protocol_version() </seealso></v>
</type>
<desc><p>Returns all default or all supported (except anonymous),
or all anonymous cipher suites for a
@@ -979,9 +1140,15 @@ fun(srp, Username :: string(), UserState :: term()) ->
<func>
<name since="OTP 19.2">eccs() -></name>
- <name since="OTP 19.2">eccs(protocol_version()) -> [named_curve()]</name>
+ <name since="OTP 19.2">eccs(Version) -> NamedCurves</name>
<fsummary>Returns a list of supported ECCs.</fsummary>
+ <type>
+ <v> Version = <seealso marker="#type-protocol_version">protocol_version() </seealso></v>
+ <v> NamedCurves = <seealso marker="#type-named_curve">[named_curve()] </seealso></v>
+
+ </type>
+
<desc><p>Returns a list of supported ECCs. <c>eccs()</c>
is equivalent to calling <c>eccs(Protocol)</c> with all
supported protocols and then deduplicating the output.</p>
@@ -1001,39 +1168,46 @@ fun(srp, Username :: string(), UserState :: term()) ->
</func>
<func>
- <name since="OTP R14B">connect(Socket, SslOptions) -> </name>
- <name since="">connect(Socket, SslOptions, Timeout) -> {ok, SslSocket} | {ok, SslSocket, Ext}
+ <name since="OTP R14B">connect(Socket, Options) -> </name>
+ <name since="">connect(Socket, Options, Timeout) -> {ok, SslSocket} | {ok, SslSocket, Ext}
| {error, Reason}</name>
<fsummary>Upgrades a <c>gen_tcp</c>, or
equivalent, connected socket to an TLS socket.</fsummary>
<type>
- <v>Socket = socket()</v>
- <v>SslOptions = [{handshake, hello| full} | ssl_option()]</v>
- <v>Timeout = integer() | infinity</v>
- <v>SslSocket = sslsocket()</v>
- <v>Ext = hello_extensions()</v>
- <v>Reason = term()</v>
+ <v>Socket = <seealso marker="#type-socket"> socket() </seealso></v>
+ <v>Options = <seealso marker="#type-tls_client_option"> [tls_client_option()] </seealso></v>
+ <v>Timeout = timeout()</v>
+ <v>SslSocket = <seealso marker="#type-sslsocket"> sslsocket() </seealso></v>
+ <v>Ext = <seealso marker="#type-protocol_extensions">protocol_extensions()</seealso></v>
+ <v>Reason = closed | timeout | <seealso marker="#type-error_alert"> error_alert() </seealso></v>
</type>
<desc><p>Upgrades a <c>gen_tcp</c>, or equivalent,
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,
- if it is not no Server Name Indication extension will be sent,
- and <seealso marker="public_key:public_key#pkix_verify_hostname-2">public_key:pkix_verify_hostname/2</seealso>
- will be called with the IP-address of the connection as <c>ReferenceID</c>, which is proably not what you want.</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, if it is not no Server Name
+ Indication extension will be sent, and <seealso
+ marker="public_key:public_key#pkix_verify_hostname-2">public_key:pkix_verify_hostname/2</seealso>
+ will be called with the IP-address of the connection as
+ <c>ReferenceID</c>, which is proably not what you want.</p>
</note>
<p> If the option <c>{handshake, hello}</c> is used the
handshake is paused after receiving the server hello message
and the success response is <c>{ok, SslSocket, Ext}</c>
- instead of <c>{ok, SslSocket}</c>. Thereafter the handshake is continued or
- canceled by calling <seealso marker="#handshake_continue-3">
+ instead of <c>{ok, SslSocket}</c>. Thereafter the handshake
+ is continued or canceled by calling <seealso
+ marker="#handshake_continue-3">
<c>handshake_continue/3</c></seealso> or <seealso
- marker="#handshake_cancel-1"><c>handshake_cancel/1</c></seealso>.
+ marker="#handshake_cancel-1"><c>handshake_cancel/1</c></seealso>.
</p>
+ <p> If the option <c>active</c> is set to <c>once</c> or <c>true</c> the
+ process owning the sslsocket will receive messages of type
+ <seealso marker="#type-active_msgs"> active_msgs() </seealso>
+ </p>
</desc>
</func>
@@ -1043,19 +1217,19 @@ fun(srp, Username :: string(), UserState :: term()) ->
{ok, SslSocket}| {ok, SslSocket, Ext} | {error, Reason}</name>
<fsummary>Opens an TLS/DTLS connection to <c>Host</c>, <c>Port</c>.</fsummary>
<type>
- <v>Host = host()</v>
- <v>Port = integer()</v>
- <v>Options = [option()]</v>
- <v>Timeout = integer() | infinity</v>
- <v>SslSocket = sslsocket()</v>
- <v>Reason = term()</v>
+ <v>Host =<seealso marker="#type-host"> host() </seealso> </v>
+ <v>Port = <seealso marker="kernel:inet#type-port_number">inet:port_number()</seealso></v>
+ <v>Options = <seealso marker="#type-tls_client_option"> [tls_client_option()]</seealso></v>
+ <v>Timeout = timeout()</v>
+ <v>SslSocket = <seealso marker="#type-sslsocket"> sslsocket() </seealso></v>
+ <v>Reason = closed | timeout | <seealso marker="#type-error_alert"> error_alert() </seealso></v>
</type>
<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
+ be propagated to the path validation fun <seealso marker="#type-custom_verify">verify_fun</seealso>, where it is possible to do customized
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>
@@ -1077,6 +1251,11 @@ fun(srp, Username :: string(), UserState :: term()) ->
<c>handshake_continue/3</c></seealso> or <seealso
marker="#handshake_cancel-1"><c>handshake_cancel/1</c></seealso>.
</p>
+
+ <p> If the option <c>active</c> is set to <c>once</c> or <c>true</c> the
+ process owning the sslsocket will receive messages of type
+ <seealso marker="#type-active_msgs"> active_msgs() </seealso>
+ </p>
</desc>
</func>
@@ -1084,7 +1263,7 @@ fun(srp, Username :: string(), UserState :: term()) ->
<name since="">close(SslSocket) -> ok | {error, Reason}</name>
<fsummary>Closes an TLS/DTLS connection.</fsummary>
<type>
- <v>SslSocket = sslsocket()</v>
+ <v>SslSocket = <seealso marker="#type-sslsocket"> sslsocket() </seealso></v>
<v>Reason = term()</v>
</type>
<desc><p>Closes an TLS/DTLS connection.</p>
@@ -1095,7 +1274,7 @@ fun(srp, Username :: string(), UserState :: term()) ->
<name since="OTP 18.1">close(SslSocket, How) -> ok | {ok, port()} | {error, Reason}</name>
<fsummary>Closes an TLS connection.</fsummary>
<type>
- <v>SslSocket = sslsocket()</v>
+ <v>SslSocket = <seealso marker="#type-sslsocket"> sslsocket() </seealso></v>
<v>How = timeout() | {NewController::pid(), timeout()} </v>
<v>Reason = term()</v>
</type>
@@ -1112,7 +1291,7 @@ fun(srp, Username :: string(), UserState :: term()) ->
<fsummary>Assigns a new controlling process to the
TLS/DTLS socket.</fsummary>
<type>
- <v>SslSocket = sslsocket()</v>
+ <v>SslSocket = <seealso marker="#type-sslsocket"> sslsocket() </seealso></v>
<v>NewOwner = pid()</v>
<v>Reason = term()</v>
</type>
@@ -1128,7 +1307,7 @@ fun(srp, Username :: string(), UserState :: term()) ->
<fsummary>Returns all the connection information.
</fsummary>
<type>
- <v>SslSocket = sslsocket()</v>
+ <v>SslSocket = <seealso marker="#type-sslsocket"> sslsocket() </seealso></v>
<v>Item = protocol | selected_cipher_suite | sni_hostname | ecc | session_id | atom()</v>
<d>Meaningful atoms, not specified above, are the ssl option names.</d>
<v>Result = [{Item::atom(), Value::term()}]</v>
@@ -1149,7 +1328,7 @@ fun(srp, Username :: string(), UserState :: term()) ->
<fsummary>Returns the requested connection information.
</fsummary>
<type>
- <v>SslSocket = sslsocket()</v>
+ <v>SslSocket = <seealso marker="#type-sslsocket"> sslsocket() </seealso></v>
<v>Items = [Item]</v>
<v>Item = protocol | cipher_suite | sni_hostname | ecc | session_id | client_random
| server_random | master_secret | atom()</v>
@@ -1169,8 +1348,8 @@ fun(srp, Username :: string(), UserState :: term()) ->
<name since="OTP 20.3">filter_cipher_suites(Suites, Filters) -> ciphers()</name>
<fsummary></fsummary>
<type>
- <v> Suites = ciphers()</v>
- <v> Filters = cipher_filters()</v>
+ <v> Suites = <seealso marker="#type-ciphers"> ciphers() </seealso></v>
+ <v> Filters = <seealso marker="#type-cipher_filters"> cipher_filters() </seealso></v>
</type>
<desc><p>Removes cipher suites if any of the filter functions
returns false for any part of the cipher suite. This function
@@ -1196,7 +1375,7 @@ fun(srp, Username :: string(), UserState :: term()) ->
{ok, [socketoption()]} | {error, Reason}</name>
<fsummary>Gets the values of the specified options.</fsummary>
<type>
- <v>Socket = sslsocket()</v>
+ <v>Socket = <seealso marker="#type-sslsocket"> sslsocket() </seealso></v>
<v>OptionNames = [atom()]</v>
</type>
<desc>
@@ -1212,7 +1391,7 @@ fun(srp, Username :: string(), UserState :: term()) ->
{ok, OptionValues} | {error, inet:posix()}</name>
<fsummary>Get one or more statistic options for a socket</fsummary>
<type>
- <v>SslSocket = sslsocket()</v>
+ <v>SslSocket = <seealso marker="#type-sslsocket"> sslsocket() </seealso></v>
<v>OptionNames = [atom()]</v>
<v>OptionValues = [{inet:stat_option(), integer()}]</v>
</type>
@@ -1227,27 +1406,32 @@ fun(srp, Username :: string(), UserState :: term()) ->
<name since="OTP 21.0">handshake(HsSocket, Timeout) -> {ok, SslSocket} | {error, Reason}</name>
<fsummary>Performs server-side SSL/TLS handshake.</fsummary>
<type>
- <v>HsSocket = SslSocket = sslsocket()</v>
- <v>Timeout = integer()</v>
- <v>Reason = term()</v>
+ <v>HsSocket = SslSocket = <seealso marker="#type-sslsocket"> sslsocket() </seealso></v>
+ <v>Timeout = timeout()</v>
+ <v>Reason = closed | timeout | <seealso marker="#type-error_alert"> error_alert() </seealso></v>
</type>
<desc>
<p>Performs the SSL/TLS/DTLS server-side handshake.</p>
<p>Returns a new TLS/DTLS socket if the handshake is successful.</p>
+
+ <p> If the option <c>active</c> is set to <c>once</c> or <c>true</c> the
+ process owning the sslsocket will receive messages of type
+ <seealso marker="#type-active_msgs"> active_msgs() </seealso>
+ </p>
</desc>
</func>
<func>
- <name since="OTP 21.0">handshake(Socket, SslOptions) -> </name>
- <name since="OTP 21.0">handshake(Socket, SslOptions, Timeout) -> {ok, SslSocket} | {ok, SslSocket, Ext} | {error, Reason}</name>
+ <name since="OTP 21.0">handshake(Socket, Options) -> </name>
+ <name since="OTP 21.0">handshake(Socket, Options, Timeout) -> {ok, SslSocket} | {ok, SslSocket, Ext} | {error, Reason}</name>
<fsummary>Performs server-side SSL/TLS/DTLS handshake.</fsummary>
<type>
- <v>Socket = socket() | sslsocket() </v>
- <v>SslSocket = sslsocket() </v>
- <v>Ext = hello_extensions()</v>
- <v>SslOptions = [{handshake, hello| full} | ssl_option()]</v>
- <v>Timeout = integer()</v>
- <v>Reason = term()</v>
+ <v>Socket = socket() | <seealso marker="#type-sslsocket"> socket() </seealso> </v>
+ <v>SslSocket = <seealso marker="#type-sslsocket"> sslsocket() </seealso> </v>
+ <v>Ext = <seealso marker="#type-protocol_extensions">protocol_extensions()</seealso></v>
+ <v>Options = <seealso marker="#type-tls_server_option"> [server_option()] </seealso> </v>
+ <v>Timeout = timeout()</v>
+ <v>Reason = closed | timeout | <seealso marker="#type-error_alert"> error_alert() </seealso></v>
</type>
<desc>
<p>If <c>Socket</c> is a ordinary <c>socket()</c>: upgrades a <c>gen_tcp</c>,
@@ -1259,7 +1443,8 @@ fun(srp, Username :: string(), UserState :: term()) ->
is undefined.
</p></warning>
- <p>If <c>Socket</c> is an <c>sslsocket()</c>: provides extra SSL/TLS/DTLS
+ <p>If <c>Socket</c> is an
+ <seealso marker="#type-sslsocket"> sslsocket() </seealso>: provides extra SSL/TLS/DTLS
options to those specified in
<seealso marker="#listen-2">listen/2 </seealso> and then performs
the SSL/TLS/DTLS handshake. Returns a new TLS/DTLS socket if the handshake is successful.</p>
@@ -1273,6 +1458,12 @@ fun(srp, Username :: string(), UserState :: term()) ->
<c>handshake_continue/3</c></seealso> or <seealso
marker="#handshake_cancel-1"><c>handshake_cancel/1</c></seealso>.
</p>
+
+ <p> If the option <c>active</c> is set to <c>once</c> or <c>true</c> the
+ process owning the sslsocket will receive messages of type
+ <seealso marker="#type-active_msgs"> active_msgs() </seealso>
+ </p>
+
</desc>
</func>
@@ -1280,7 +1471,7 @@ fun(srp, Username :: string(), UserState :: term()) ->
<name since="OTP 21.0">handshake_cancel(SslSocket) -> ok </name>
<fsummary>Cancel handshake with a fatal alert</fsummary>
<type>
- <v>SslSocket = sslsocket()</v>
+ <v>SslSocket = <seealso marker="#type-sslsocket"> sslsocket() </seealso></v>
</type>
<desc>
<p>Cancel the handshake with a fatal <c>USER_CANCELED</c> alert.</p>
@@ -1288,14 +1479,14 @@ fun(srp, Username :: string(), UserState :: term()) ->
</func>
<func>
- <name since="OTP 21.0">handshake_continue(HsSocket, SSLOptions) -> {ok, SslSocket} | {error, Reason}</name>
- <name since="OTP 21.0">handshake_continue(HsSocket, SSLOptions, Timeout) -> {ok, SslSocket} | {error, Reason}</name>
+ <name since="OTP 21.0">handshake_continue(HsSocket, Options) -> {ok, SslSocket} | {error, Reason}</name>
+ <name since="OTP 21.0">handshake_continue(HsSocket, Options, Timeout) -> {ok, SslSocket} | {error, Reason}</name>
<fsummary>Continue the SSL/TLS handshake.</fsummary>
<type>
- <v>HsSocket = SslSocket = sslsocket()</v>
- <v>SslOptions = [ssl_option()]</v>
- <v>Timeout = integer()</v>
- <v>Reason = term()</v>
+ <v>HsSocket = SslSocket = <seealso marker="#type-sslsocket"> sslsocket() </seealso></v>
+ <v>Options = <seealso marker="#type-tls_option"> tls_option() </seealso> </v>
+ <v>Timeout = timeout()</v>
+ <v>Reason = closed | timeout | <seealso marker="#type-error_alert"> error_alert() </seealso></v>
</type>
<desc>
<p>Continue the SSL/TLS handshake possiby with new, additional or changed options.</p>
@@ -1307,9 +1498,9 @@ fun(srp, Username :: string(), UserState :: term()) ->
{ok, ListenSocket} | {error, Reason}</name>
<fsummary>Creates an SSL listen socket.</fsummary>
<type>
- <v>Port = integer()</v>
- <v>Options = options()</v>
- <v>ListenSocket = sslsocket()</v>
+ <v>Port = <seealso marker="kernel:inet#type-port_number">inet:port_number()</seealso></v>
+ <v>Options = <seealso marker="#type-tls_server_option"> [server_option()] </seealso></v>
+ <v>ListenSocket = <seealso marker="#type-sslsocket"> sslsocket() </seealso></v>
</type>
<desc>
<p>Creates an SSL listen socket.</p>
@@ -1320,7 +1511,7 @@ fun(srp, Username :: string(), UserState :: term()) ->
<name since="OTP 18.0">negotiated_protocol(SslSocket) -> {ok, Protocol} | {error, protocol_not_negotiated}</name>
<fsummary>Returns the protocol negotiated through ALPN or NPN extensions.</fsummary>
<type>
- <v>SslSocket = sslsocket()</v>
+ <v>SslSocket = <seealso marker="#type-sslsocket"> sslsocket() </seealso></v>
<v>Protocol = binary()</v>
</type>
<desc>
@@ -1334,7 +1525,7 @@ fun(srp, Username :: string(), UserState :: term()) ->
<name since="">peercert(SslSocket) -> {ok, Cert} | {error, Reason}</name>
<fsummary>Returns the peer certificate.</fsummary>
<type>
- <v>SslSocket = sslsocket()</v>
+ <v>SslSocket = <seealso marker="#type-sslsocket"> sslsocket() </seealso></v>
<v>Cert = binary()</v>
</type>
<desc>
@@ -1350,9 +1541,9 @@ fun(srp, Username :: string(), UserState :: term()) ->
{error, Reason}</name>
<fsummary>Returns the peer address and port.</fsummary>
<type>
- <v>SslSocket = sslsocket()</v>
+ <v>SslSocket = <seealso marker="#type-sslsocket"> sslsocket() </seealso></v>
<v>Address = ipaddress()</v>
- <v>Port = integer()</v>
+ <v>Port = <seealso marker="kernel:inet#type-port_number">inet:port_number()</seealso></v>
</type>
<desc>
<p>Returns the address and port number of the peer.</p>
@@ -1363,8 +1554,9 @@ fun(srp, Username :: string(), UserState :: term()) ->
<name since="OTP 20.3">prepend_cipher_suites(Preferred, Suites) -> ciphers()</name>
<fsummary></fsummary>
<type>
- <v>Preferred = ciphers() | cipher_filters() </v>
- <v>Suites = ciphers() </v>
+ <v>Preferred = <seealso marker="#type-ciphers">ciphers()</seealso> |
+ <seealso marker="#type-cipher_filters">cipher_filters()</seealso></v>
+ <v>Suites = <seealso marker="#type-ciphers">ciphers()</seealso></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
@@ -1379,10 +1571,10 @@ fun(srp, Username :: string(), UserState :: term()) ->
<name since="OTP R15B01">prf(Socket, Secret, Label, Seed, WantedLength) -> {ok, binary()} | {error, reason()}</name>
<fsummary>Uses a session Pseudo-Random Function to generate key material.</fsummary>
<type>
- <v>Socket = sslsocket()</v>
+ <v>Socket = <seealso marker="#type-sslsocket"> sslsocket() </seealso></v>
<v>Secret = binary() | master_secret</v>
<v>Label = binary()</v>
- <v>Seed = [binary() | prf_random()]</v>
+ <v>Seed = [binary() | <seealso marker="#type-prf_random"> prf_random()</seealso>]</v>
<v>WantedLength = non_neg_integer()</v>
</type>
<desc>
@@ -1401,9 +1593,9 @@ fun(srp, Username :: string(), UserState :: term()) ->
Reason}</name>
<fsummary>Receives data on a socket.</fsummary>
<type>
- <v>SslSocket = sslsocket()</v>
+ <v>SslSocket = <seealso marker="#type-sslsocket"> sslsocket() </seealso></v>
<v>Length = integer()</v>
- <v>Timeout = integer()</v>
+ <v>Timeout = timeout()</v>
<v>Data = [char()] | binary()</v>
</type>
<desc>
@@ -1426,7 +1618,7 @@ fun(srp, Username :: string(), UserState :: term()) ->
<name since="OTP R14B">renegotiate(SslSocket) -> ok | {error, Reason}</name>
<fsummary>Initiates a new handshake.</fsummary>
<type>
- <v>SslSocket = sslsocket()</v>
+ <v>SslSocket = <seealso marker="#type-sslsocket"> sslsocket() </seealso></v>
</type>
<desc><p>Initiates a new handshake. A notable return value is
<c>{error, renegotiation_rejected}</c> indicating that the peer
@@ -1439,7 +1631,7 @@ fun(srp, Username :: string(), UserState :: term()) ->
<name since="">send(SslSocket, Data) -> ok | {error, Reason}</name>
<fsummary>Writes data to a socket.</fsummary>
<type>
- <v>SslSocket = sslsocket()</v>
+ <v>SslSocket = <seealso marker="#type-sslsocket"> sslsocket() </seealso></v>
<v>Data = iodata()</v>
</type>
<desc>
@@ -1453,8 +1645,8 @@ fun(srp, Username :: string(), UserState :: term()) ->
<name since="">setopts(SslSocket, Options) -> ok | {error, Reason}</name>
<fsummary>Sets socket options.</fsummary>
<type>
- <v>SslSocket = sslsocket()</v>
- <v>Options = [socketoption]()</v>
+ <v>SslSocket = <seealso marker="#type-sslsocket"> sslsocket() </seealso></v>
+ <v>Options = <seealso marker="#type-socket_option"> [socket_option()] </seealso></v>
</type>
<desc>
<p>Sets options according to <c>Options</c> for socket
@@ -1477,7 +1669,7 @@ fun(srp, Username :: string(), UserState :: term()) ->
<name since="OTP R14B">shutdown(SslSocket, How) -> ok | {error, Reason}</name>
<fsummary>Immediately closes a socket.</fsummary>
<type>
- <v>SslSocket = sslsocket()</v>
+ <v>SslSocket = <seealso marker="#type-sslsocket"> sslsocket() </seealso></v>
<v>How = read | write | read_write</v>
<v>Reason = reason()</v>
</type>
@@ -1496,9 +1688,9 @@ fun(srp, Username :: string(), UserState :: term()) ->
<name since="">ssl_accept(SslSocket, Timeout) -> ok | {error, Reason}</name>
<fsummary>Performs server-side SSL/TLS handshake.</fsummary>
<type>
- <v>SslSocket = sslsocket()</v>
- <v>Timeout = integer()</v>
- <v>Reason = term()</v>
+ <v>SslSocket = <seealso marker="#type-sslsocket"> sslsocket() </seealso></v>
+ <v>Timeout = timeout()</v>
+ <v>Reason = closed | timeout | <seealso marker="#type-error_alert"> error_alert() </seealso></v>
</type>
<desc>
<p>Deprecated in OTP 21, use <seealso marker="#handshake-1">handshake/[1,2]</seealso> instead.</p>
@@ -1507,14 +1699,14 @@ fun(srp, Username :: string(), UserState :: term()) ->
</func>
<func>
- <name since="">ssl_accept(Socket, SslOptions) -> </name>
- <name since="OTP R14B">ssl_accept(Socket, SslOptions, Timeout) -> {ok, Socket} | ok | {error, Reason}</name>
+ <name since="">ssl_accept(Socket, Options) -> </name>
+ <name since="OTP R14B">ssl_accept(Socket, Options, Timeout) -> {ok, Socket} | ok | {error, Reason}</name>
<fsummary>Performs server-side SSL/TLS/DTLS handshake.</fsummary>
<type>
- <v>Socket = socket() | sslsocket() </v>
- <v>SslOptions = [ssl_option()]</v>
- <v>Timeout = integer()</v>
- <v>Reason = term()</v>
+ <v>Socket = socket() | <seealso marker="#type-sslsocket"> sslsocket() </seealso> </v>
+ <v>Options = <seealso marker="#type-tls_server_option"> [server_option()] </seealso> </v>
+ <v>Timeout = timeout()</v>
+ <v>Reason = closed | timeout | <seealso marker="#type-error_alert"> error_alert() </seealso></v>
</type>
<desc>
<p>Deprecated in OTP 21, use <seealso marker="#handshake-3">handshake/[2,3]</seealso> instead.</p>
@@ -1527,9 +1719,9 @@ fun(srp, Username :: string(), UserState :: term()) ->
{error, Reason}</name>
<fsummary>Returns the local address and port.</fsummary>
<type>
- <v>SslSocket = sslsocket()</v>
- <v>Address = ipaddress()</v>
- <v>Port = integer()</v>
+ <v>SslSocket = <seealso marker="#type-sslsocket"> sslsocket() </seealso></v>
+ <v>Address = <seealso marker="#type-ip_address">ip_address()</seealso></v>
+ <v>Port = <seealso marker="kernel:inet#type-port_number">inet:port_number()</seealso></v>
</type>
<desc>
<p>Returns the local address and port number of socket
@@ -1562,7 +1754,7 @@ fun(srp, Username :: string(), UserState :: term()) ->
<name since="OTP 21.0">suite_to_str(CipherSuite) -> String</name>
<fsummary>Returns the string representation of a cipher suite.</fsummary>
<type>
- <v>CipherSuite = erl_cipher_suite()</v>
+ <v>CipherSuite = <seealso marker="#type-erl_cipher_suite"> erl_cipher_suite() </seealso></v>
<v>String = string()</v>
</type>
<desc>
@@ -1577,8 +1769,8 @@ fun(srp, Username :: string(), UserState :: term()) ->
<fsummary>Accepts an incoming connection and
prepares for <c>ssl_accept</c>.</fsummary>
<type>
- <v>ListenSocket = SslSocket = sslsocket()</v>
- <v>Timeout = integer()</v>
+ <v>ListenSocket = SslSocket = <seealso marker="#type-sslsocket"> sslsocket() </seealso></v>
+ <v>Timeout = timeout()</v>
<v>Reason = reason()</v>
</type>
<desc>
diff --git a/lib/ssl/doc/src/ssl_crl_cache.xml b/lib/ssl/doc/src/ssl_crl_cache.xml
index b766cfd2d9..a33aec62a7 100644
--- a/lib/ssl/doc/src/ssl_crl_cache.xml
+++ b/lib/ssl/doc/src/ssl_crl_cache.xml
@@ -34,15 +34,27 @@
the following functions are available.
</p>
</description>
+
+ <datatypes>
+ <datatype_title>DATA TYPES</datatype_title>
+
+ <datatype>
+ <name name="crl_src"/>
+ </datatype>
+
+ <datatype>
+ <name name="uri"/>
+ </datatype>
+
+ </datatypes>
<funcs>
<func>
<name since="OTP 18.0">delete(Entries) -> ok | {error, Reason} </name>
<fsummary> </fsummary>
<type>
- <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>
+ <v> Entries = <seealso marker="#type-crl_src">crl_src()</seealso>]}</v>
+ <v> Reason = crl_reason()</v>
</type>
<desc>
<p>Delete CRLs from the ssl applications local cache. </p>
@@ -53,13 +65,12 @@
<name since="OTP 18.0">insert(URI, CRLSrc) -> ok | {error, Reason}</name>
<fsummary> </fsummary>
<type>
- <v> CRLSrc = {file, string()} | {der, [ <seealso
- marker="public_key:public_key"> public_key:der_encoded() </seealso> ]}</v>
- <v> URI = <seealso marker="stdlib:uri_string">uri_string:uri_string() </seealso> </v>
+ <v> CRLSrc = <seealso marker="#type-crl_src">crl_src()</seealso>]}</v>
+ <v> URI = <seealso marker="#type-uri">uri()</seealso> </v>
<v> Reason = term()</v>
</type>
<desc>
- <p>Insert CRLs into the ssl applications local cache. </p>
+ <p>Insert CRLs, available to fetch on DER format from <c>URI</c>, into the ssl applications local cache. </p>
</desc>
</func>
</funcs>
diff --git a/lib/ssl/doc/src/ssl_crl_cache_api.xml b/lib/ssl/doc/src/ssl_crl_cache_api.xml
index c7e501867f..4cba4e1de1 100644
--- a/lib/ssl/doc/src/ssl_crl_cache_api.xml
+++ b/lib/ssl/doc/src/ssl_crl_cache_api.xml
@@ -39,35 +39,44 @@
a CRL cache.
</p>
</description>
-
- <section>
- <title>DATA TYPES</title>
-
- <p>The following data types are used in the functions below:
- </p>
-
- <taglist>
-
- <tag><c>cache_ref() =</c></tag>
- <item>opaque()</item>
- <tag><c>dist_point() =</c></tag>
- <item><p>#'DistributionPoint'{} see <seealso
- marker="public_key:public_key_records"> X509 certificates records</seealso></p></item>
-
- </taglist>
+
+
+ <!--
+ ================================================================
+ = Data types =
+ ================================================================
+ -->
+
+ <datatypes>
- </section>
+ <datatype>
+ <name name="crl_cache_ref"/>
+ <desc>
+ <p>Reference to the CRL cache.</p>
+ </desc>
+ </datatype>
+
+
+ <datatype>
+ <name name="dist_point"/>
+ <desc>
+ <p>For description see <seealso
+ marker="public_key:public_key_records"> X509 certificates records</seealso></p>
+ </desc>
+ </datatype>
+ </datatypes>
+
<funcs>
<func>
<name since="OTP 18.0">fresh_crl(DistributionPoint, CRL) -> FreshCRL</name>
<fsummary> <c>fun fresh_crl/2 </c> will be used as input option <c>update_crl</c> to
public_key:pkix_crls_validate/3 </fsummary>
<type>
- <v> DistributionPoint = dist_point() </v>
+ <v> DistributionPoint = <seealso marker="#type-dist_point"> dist_point() </seealso> </v>
<v> CRL = [<seealso
- marker="public_key:public_key">public_key:der_encoded()</seealso>] </v>
+ marker="public_key:public_key#type-der_encoded">public_key:der_encoded()</seealso>] </v>
<v> FreshCRL = [<seealso
- marker="public_key:public_key">public_key:der_encoded()</seealso>] </v>
+ marker="public_key:public_key#type-der_encoded">public_key:der_encoded()</seealso>] </v>
</type>
<desc>
<p> <c>fun fresh_crl/2 </c> will be used as input option <c>update_crl</c> to
@@ -80,12 +89,12 @@
<name since="OTP 18.0">lookup(DistributionPoint, DbHandle) -> not_available | CRLs </name>
<fsummary> </fsummary>
<type>
- <v> DistributionPoint = dist_point() </v>
+ <v> DistributionPoint = <seealso marker="#type-dist_point"> dist_point() </seealso> </v>
<v> Issuer = <seealso
- marker="public_key:public_key">public_key:issuer_name()</seealso> </v>
- <v> DbHandle = cache_ref() </v>
+ marker="public_key:public_key#type-issuer_name">public_key:issuer_name()</seealso> </v>
+ <v> DbHandle = <seealso marker="#type-crl_cache_ref"> crl_cache_ref() </seealso></v>
<v> CRLs = [<seealso
- marker="public_key:public_key">public_key:der_encoded()</seealso>] </v>
+ marker="public_key:public_key#type-der_encoded">public_key:der_encoded()</seealso>] </v>
</type>
<desc> <p>Lookup the CRLs belonging to the distribution point <c> Distributionpoint</c>.
This function may choose to only look in the cache or to follow distribution point
@@ -110,8 +119,8 @@
<fsummary>Select the CRLs in the cache that are issued by <c>Issuer</c></fsummary>
<type>
<v> Issuer = <seealso
- marker="public_key:public_key">public_key:issuer_name()</seealso></v>
- <v> DbHandle = cache_ref() </v>
+ marker="public_key:public_key#type-issuer_name">public_key:issuer_name()</seealso></v>
+ <v> DbHandle = <seealso marker="#type-crl_cache_ref"> cache_ref() </seealso></v>
</type>
<desc>
<p>Select the CRLs in the cache that are issued by <c>Issuer</c> </p>
diff --git a/lib/ssl/doc/src/ssl_session_cache_api.xml b/lib/ssl/doc/src/ssl_session_cache_api.xml
index 463cf15309..e841729e57 100644
--- a/lib/ssl/doc/src/ssl_session_cache_api.xml
+++ b/lib/ssl/doc/src/ssl_session_cache_api.xml
@@ -38,30 +38,41 @@
defining a new callback module implementing this API.
</p>
</description>
- <section>
- <title>DATA TYPES</title>
- <p>The following data types are used in the functions for
- <c>ssl_session_cache_api</c>:</p>
-
- <taglist>
- <tag><c>cache_ref() =</c></tag>
- <item><p><c>opaque()</c></p></item>
-
- <tag><c>key() =</c></tag>
- <item><p><c>{partialkey(), session_id()}</c></p></item>
-
- <tag><c>partialkey() =</c></tag>
- <item><p><c>opaque()</c></p></item>
-
- <tag><c>session_id() =</c></tag>
- <item><p><c>binary()</c></p></item>
-
- <tag><c>session()</c> =</tag>
- <item><p><c>opaque()</c></p></item>
- </taglist>
-
- </section>
+ <!--
+ ================================================================
+ = Data types =
+ ================================================================
+ -->
+
+ <datatypes>
+
+ <datatype>
+ <name name="session_cache_ref"/>
+ </datatype>
+
+ <datatype>
+ <name name="session_cache_key"/>
+ <desc>
+ <p>A key to an entry in the session cache.</p>
+ </desc>
+ </datatype>
+
+ <datatype>
+ <name name="partial_key"/>
+ <desc>
+ <p>The opaque part of the key. Does not need to be handled
+ by the callback.</p>
+ </desc>
+ </datatype>
+
+ <datatype>
+ <name name="session"/>
+ <desc>
+ <p>The session data that is stored for each session.</p>
+ </desc>
+ </datatype>
+ </datatypes>
<funcs>
@@ -69,8 +80,8 @@
<name since="OTP R14B">delete(Cache, Key) -> _</name>
<fsummary>Deletes a cache entry.</fsummary>
<type>
- <v>Cache = cache_ref()</v>
- <v>Key = key()</v>
+ <v>Cache = <seealso marker="#type-session_cache_ref"> session_cache_ref() </seealso></v>
+ <v>Key = <seealso marker="#type-session_cache_key">session_cache_key() </seealso> </v>
</type>
<desc>
<p>Deletes a cache entry. Is only called from the cache
@@ -83,7 +94,9 @@
<name since="OTP R14B">foldl(Fun, Acc0, Cache) -> Acc</name>
<fsummary></fsummary>
<type>
- <v></v>
+ <v>Fun = fun()</v>
+ <v>Acc0 = Acc = term()</v>
+ <v>Cache = <seealso marker="#type-session_cache_ref"> session_cache_ref() </seealso></v>
</type>
<desc>
<p>Calls <c>Fun(Elem, AccIn)</c> on successive elements of the
@@ -96,10 +109,11 @@
</func>
<func>
- <name since="OTP 18.0">init(Args) -> opaque() </name>
+ <name since="OTP 18.0">init(Args) -> Cache </name>
<fsummary>Returns cache reference.</fsummary>
<type>
- <v>Args = proplists:proplist()</v>
+ <v>Cache = <seealso marker="#type-session_cache_ref"> session_cache_ref() </seealso></v>
+ <v>Args = <seealso marker="stdlib:proplists#type-proplist">proplists:proplist()</seealso></v>
</type>
<desc>
<p>Includes property <c>{role, client | server}</c>.
@@ -124,9 +138,9 @@
<name since="OTP R14B">lookup(Cache, Key) -> Entry</name>
<fsummary>Looks up a cache entry.</fsummary>
<type>
- <v>Cache = cache_ref()</v>
- <v>Key = key()</v>
- <v>Entry = session() | undefined</v>
+ <v>Cache = <seealso marker="#type-session_cache_ref"> session_cache_ref() </seealso></v>
+ <v>Key = <seealso marker="#type-session_cache_key">session_cache_key()</seealso> </v>
+ <v>Session = <seealso marker="#type-session">session()</seealso> | undefined</v>
</type>
<desc>
<p>Looks up a cache entry. Is to be callable from any
@@ -136,12 +150,12 @@
</func>
<func>
- <name since="OTP R14B">select_session(Cache, PartialKey) -> [session()]</name>
+ <name since="OTP R14B">select_session(Cache, PartialKey) -> [Session]</name>
<fsummary>Selects sessions that can be reused.</fsummary>
<type>
- <v>Cache = cache_ref()</v>
- <v>PartialKey = partialkey()</v>
- <v>Session = session()</v>
+ <v>Cache = <seealso marker="#type-session_cache_ref"> session_cache_ref() </seealso></v>
+ <v>PartialKey = <seealso marker="#type-partial_key"> partial_key() </seealso></v>
+ <v>Session = <seealso marker="#type-session">session()</seealso></v>
</type>
<desc>
<p>Selects sessions that can be reused. Is to be callable
@@ -154,7 +168,7 @@
<name since="OTP 19.3">size(Cache) -> integer()</name>
<fsummary>Returns the number of sessions in the cache.</fsummary>
<type>
- <v>Cache = cache_ref()</v>
+ <v>Cache = <seealso marker="#type-session_cache_ref"> session_cache_ref() </seealso></v>
</type>
<desc>
<p>Returns the number of sessions in the cache. If size
@@ -170,7 +184,8 @@
<fsummary>Called by the process that handles the cache when it
is about to terminate.</fsummary>
<type>
- <v>Cache = term() - as returned by init/0</v>
+ <v>Cache = <seealso marker="#type-session_cache_ref"> session_cache_ref() </seealso></v>
+ <d>As returned by init/0</d>
</type>
<desc>
<p>Takes care of possible cleanup that is needed when the
@@ -183,9 +198,9 @@
<name since="OTP R14B">update(Cache, Key, Session) -> _</name>
<fsummary>Caches a new session or updates an already cached one.</fsummary>
<type>
- <v>Cache = cache_ref()</v>
- <v>Key = key()</v>
- <v>Session = session()</v>
+ <v>Cache = <seealso marker="#type-session_cache_ref"> session_cache_ref() </seealso></v>
+ <v>Key = <seealso marker="#type-session_cache_key">session_cache_key()</seealso> </v>
+ <v>Session = <seealso marker="#type-session">session()</seealso></v>
</type>
<desc>
<p>Caches a new session or updates an already cached one. Is
diff --git a/lib/ssl/src/dtls_connection.erl b/lib/ssl/src/dtls_connection.erl
index cbd5c8e0a9..6b5a311efc 100644
--- a/lib/ssl/src/dtls_connection.erl
+++ b/lib/ssl/src/dtls_connection.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2013-2018. All Rights Reserved.
+%% Copyright Ericsson AB 2013-2019. 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,8 +51,7 @@
-export([encode_alert/3, send_alert/2, send_alert_in_connection/2, close/5, protocol_name/0]).
%% Data handling
--export([encode_data/3, next_record/1,
- send/3, socket/5, setopts/3, getopts/3]).
+-export([next_record/1, socket/4, setopts/3, getopts/3]).
%% gen_statem state functions
-export([init/3, error/3, downgrade/3, %% Initiation and take down states
@@ -81,7 +80,7 @@ start_fsm(Role, Host, Port, Socket, {#ssl_options{erl_dist = false},_, Tracker}
end.
%%--------------------------------------------------------------------
--spec start_link(atom(), host(), inet:port_number(), port(), list(), pid(), tuple()) ->
+-spec start_link(atom(), ssl:host(), inet:port_number(), port(), list(), pid(), tuple()) ->
{ok, pid()} | ignore | {error, reason()}.
%%
%% Description: Creates a gen_statem process which calls Module:init/1 to
@@ -252,7 +251,7 @@ handle_protocol_record(#ssl_tls{type = ?HANDSHAKE,
fragment = Data},
StateName,
#state{protocol_buffers = Buffers0,
- negotiated_version = Version} = State) ->
+ connection_env = #connection_env{negotiated_version = Version}} = State) ->
try
case dtls_handshake:get_dtls_handshake(Version, Data, Buffers0) of
{[], Buffers} ->
@@ -274,7 +273,7 @@ handle_protocol_record(#ssl_tls{type = ?CHANGE_CIPHER_SPEC, fragment = Data}, St
{next_state, StateName, State, [{next_event, internal, #change_cipher_spec{type = Data}}]};
%%% DTLS record protocol level Alert messages
handle_protocol_record(#ssl_tls{type = ?ALERT, fragment = EncAlerts}, StateName,
- #state{negotiated_version = Version} = State) ->
+ #state{connection_env = #connection_env{negotiated_version = Version}} = State) ->
case decode_alerts(EncAlerts) of
Alerts = [_|_] ->
handle_alerts(Alerts, {next_state, StateName, State});
@@ -306,7 +305,7 @@ send_handshake(Handshake, #state{connection_states = ConnectionStates} = State)
send_handshake_flight(queue_handshake(Handshake, State), Epoch).
queue_handshake(Handshake0, #state{handshake_env = #handshake_env{tls_handshake_history = Hist0} = HsEnv,
- negotiated_version = Version,
+ connection_env = #connection_env{negotiated_version = Version},
flight_buffer = #{handshakes := HsBuffer0,
change_cipher_spec := undefined,
next_sequence := Seq} = Flight0} = State) ->
@@ -317,7 +316,7 @@ queue_handshake(Handshake0, #state{handshake_env = #handshake_env{tls_handshake_
handshake_env = HsEnv#handshake_env{tls_handshake_history = Hist}};
queue_handshake(Handshake0, #state{handshake_env = #handshake_env{tls_handshake_history = Hist0} = HsEnv,
- negotiated_version = Version,
+ connection_env = #connection_env{negotiated_version = Version},
flight_buffer = #{handshakes_after_change_cipher_spec := Buffer0,
next_sequence := Seq} = Flight0} = State) ->
Handshake = dtls_handshake:encode_handshake(Handshake0, Version, Seq),
@@ -336,12 +335,14 @@ queue_change_cipher(ChangeCipher, #state{flight_buffer = Flight,
reinit(State) ->
%% To be API compatible with TLS NOOP here
reinit_handshake_data(State).
-reinit_handshake_data(#state{protocol_buffers = Buffers,
+reinit_handshake_data(#state{static_env = #static_env{data_tag = DataTag},
+ protocol_buffers = Buffers,
+ protocol_specific = PS,
handshake_env = HsEnv} = State) ->
- State#state{premaster_secret = undefined,
- public_key_info = undefined,
- handshake_env = HsEnv#handshake_env{tls_handshake_history = ssl_handshake:init_handshake_history()},
- flight_state = {retransmit, ?INITIAL_RETRANSMIT_TIMEOUT},
+ State#state{handshake_env = HsEnv#handshake_env{tls_handshake_history = ssl_handshake:init_handshake_history(),
+ public_key_info = undefined,
+ premaster_secret = undefined},
+ protocol_specific = PS#{flight_state => initial_flight_state(DataTag)},
flight_buffer = new_flight(),
protocol_buffers =
Buffers#protocol_buffers{
@@ -365,9 +366,9 @@ empty_connection_state(ConnectionEnd, BeastMitigation) ->
encode_alert(#alert{} = Alert, Version, ConnectionStates) ->
dtls_record:encode_alert_record(Alert, Version, ConnectionStates).
-send_alert(Alert, #state{negotiated_version = Version,
- static_env = #static_env{socket = Socket,
+send_alert(Alert, #state{static_env = #static_env{socket = Socket,
transport_cb = Transport},
+ connection_env = #connection_env{negotiated_version = Version},
connection_states = ConnectionStates0} = State0) ->
{BinMsg, ConnectionStates} =
encode_alert(Alert, Version, ConnectionStates0),
@@ -391,16 +392,13 @@ protocol_name() ->
%% Data handling
%%====================================================================
-encode_data(Data, Version, ConnectionStates0)->
- dtls_record:encode_data(Data, Version, ConnectionStates0).
+send(Transport, {Listener, Socket}, Data) when is_pid(Listener) -> % Server socket
+ dtls_socket:send(Transport, Socket, Data);
+send(Transport, Socket, Data) -> % Client socket
+ dtls_socket:send(Transport, Socket, Data).
-send(Transport, {_, {{_,_}, _} = Socket}, Data) ->
- send(Transport, Socket, Data);
-send(Transport, Socket, Data) ->
- dtls_socket:send(Transport, Socket, Data).
-
-socket(Pid, Transport, Socket, Connection, _) ->
- dtls_socket:socket(Pid, Transport, Socket, Connection).
+socket(Pid, Transport, Socket, _Tracker) ->
+ dtls_socket:socket(Pid, Transport, Socket, ?MODULE).
setopts(Transport, Socket, Other) ->
dtls_socket:setopts(Transport, Socket, Other).
@@ -425,40 +423,33 @@ init({call, From}, {start, Timeout},
session_cache = Cache,
session_cache_cb = CacheCb},
handshake_env = #handshake_env{renegotiation = {Renegotiation, _}},
+ connection_env = CEnv,
ssl_options = SslOpts,
session = #session{own_certificate = Cert} = Session0,
connection_states = ConnectionStates0
} = State0) ->
- Timer = ssl_connection:start_or_recv_cancel_timer(Timeout, From),
Hello = dtls_handshake:client_hello(Host, Port, ConnectionStates0, SslOpts,
Cache, CacheCb, Renegotiation, Cert),
Version = Hello#client_hello.client_version,
HelloVersion = dtls_record:hello_version(Version, SslOpts#ssl_options.versions),
- State1 = prepare_flight(State0#state{negotiated_version = Version}),
- {State2, Actions} = send_handshake(Hello, State1#state{negotiated_version = HelloVersion}),
- State3 = State2#state{negotiated_version = Version, %% Requested version
+ State1 = prepare_flight(State0#state{connection_env = CEnv#connection_env{negotiated_version = Version}}),
+ {State2, Actions} = send_handshake(Hello, State1#state{connection_env = CEnv#connection_env{negotiated_version = HelloVersion}}),
+ State3 = State2#state{connection_env = CEnv#connection_env{negotiated_version = Version}, %% RequestedVersion
session =
Session0#session{session_id = Hello#client_hello.session_id},
- start_or_recv_from = From,
- timer = Timer,
- flight_state = {retransmit, ?INITIAL_RETRANSMIT_TIMEOUT}
- },
+ start_or_recv_from = From},
{Record, State} = next_record(State3),
- next_event(hello, Record, State, Actions);
-init({call, _} = Type, Event, #state{static_env = #static_env{role = server,
- data_tag = udp}} = State) ->
+ next_event(hello, Record, State, [{{timeout, handshake}, Timeout, close} | Actions]);
+init({call, _} = Type, Event, #state{static_env = #static_env{role = server},
+ protocol_specific = PS} = State) ->
Result = gen_handshake(?FUNCTION_NAME, Type, Event,
- State#state{flight_state = {retransmit, ?INITIAL_RETRANSMIT_TIMEOUT},
- protocol_specific = #{current_cookie_secret => dtls_v1:cookie_secret(),
- previous_cookie_secret => <<>>,
- ignored_alerts => 0,
- max_ignored_alerts => 10}}),
+ State#state{protocol_specific = PS#{current_cookie_secret => dtls_v1:cookie_secret(),
+ previous_cookie_secret => <<>>,
+ ignored_alerts => 0,
+ max_ignored_alerts => 10}}),
erlang:send_after(dtls_v1:cookie_timeout(), self(), new_cookie_secret),
Result;
-init({call, _} = Type, Event, #state{static_env = #static_env{role = server}} = State) ->
- %% I.E. DTLS over sctp
- gen_handshake(?FUNCTION_NAME, Type, Event, State#state{flight_state = reliable});
init(Type, Event, State) ->
gen_handshake(?FUNCTION_NAME, Type, Event, State).
@@ -495,6 +486,7 @@ hello(internal, #client_hello{cookie = <<>>,
transport_cb = Transport,
socket = Socket},
handshake_env = HsEnv,
+ connection_env = CEnv,
protocol_specific = #{current_cookie_secret := Secret}} = State0) ->
{ok, {IP, Port}} = dtls_socket:peername(Transport, Socket),
Cookie = dtls_handshake:cookie(Secret, IP, Port, Hello),
@@ -505,7 +497,7 @@ hello(internal, #client_hello{cookie = <<>>,
%% version 1.0 regardless of the version of TLS that is expected to be
%% negotiated.
VerifyRequest = dtls_handshake:hello_verify_request(Cookie, ?HELLO_VERIFY_REQUEST_VERSION),
- State1 = prepare_flight(State0#state{negotiated_version = Version}),
+ State1 = prepare_flight(State0#state{connection_env = CEnv#connection_env{negotiated_version = Version}}),
{State2, Actions} = send_handshake(VerifyRequest, State1),
{Record, State} = next_record(State2),
next_event(?FUNCTION_NAME, Record,
@@ -519,6 +511,7 @@ hello(internal, #hello_verify_request{cookie = Cookie}, #state{static_env = #sta
session_cache = Cache,
session_cache_cb = CacheCb},
handshake_env = #handshake_env{renegotiation = {Renegotiation, _}} = HsEnv,
+ connection_env = CEnv,
ssl_options = SslOpts,
session = #session{own_certificate = OwnCert}
= Session0,
@@ -534,22 +527,26 @@ hello(internal, #hello_verify_request{cookie = Cookie}, #state{static_env = #sta
= ssl_handshake:init_handshake_history()}}),
{State2, Actions} = send_handshake(Hello, State1),
- State = State2#state{negotiated_version = Version, %% Requested version
+ State = State2#state{connection_env = CEnv#connection_env{negotiated_version = Version}, %% Requested version
session =
Session0#session{session_id =
Hello#client_hello.session_id}},
next_event(?FUNCTION_NAME, no_record, State, Actions);
hello(internal, #client_hello{extensions = Extensions} = Hello,
#state{ssl_options = #ssl_options{handshake = hello},
+ handshake_env = HsEnv,
start_or_recv_from = From} = State) ->
{next_state, user_hello, State#state{start_or_recv_from = undefined,
- hello = Hello},
+ handshake_env = HsEnv#handshake_env{hello = Hello}},
[{reply, From, {ok, Extensions}}]};
-hello(internal, #server_hello{extensions = Extensions} = Hello, #state{ssl_options = #ssl_options{handshake = hello},
- start_or_recv_from = From} = State) ->
+hello(internal, #server_hello{extensions = Extensions} = Hello,
+ #state{ssl_options = #ssl_options{handshake = hello},
+ handshake_env = HsEnv,
+ start_or_recv_from = From} = State) ->
{next_state, user_hello, State#state{start_or_recv_from = undefined,
- hello = Hello},
+ handshake_env = HsEnv#handshake_env{hello = Hello}},
[{reply, From, {ok, Extensions}}]};
+
hello(internal, #client_hello{cookie = Cookie} = Hello, #state{static_env = #static_env{role = server,
transport_cb = Transport,
socket = Socket},
@@ -573,8 +570,8 @@ hello(internal, #server_hello{} = Hello,
#state{
static_env = #static_env{role = client},
handshake_env = #handshake_env{renegotiation = {Renegotiation, _}},
+ connection_env = #connection_env{negotiated_version = ReqVersion},
connection_states = ConnectionStates0,
- negotiated_version = ReqVersion,
ssl_options = SslOptions} = State) ->
case dtls_handshake:hello(Hello, SslOptions, ConnectionStates0, Renegotiation) of
#alert{} = Alert ->
@@ -621,10 +618,11 @@ abbreviated(internal = Type,
ConnectionStates1 = dtls_record:save_current_connection_state(ConnectionStates0, read),
ConnectionStates = dtls_record:next_epoch(ConnectionStates1, read),
gen_handshake(?FUNCTION_NAME, Type, Event, State#state{connection_states = ConnectionStates});
-abbreviated(internal = Type, #finished{} = Event, #state{connection_states = ConnectionStates} = State) ->
+abbreviated(internal = Type, #finished{} = Event, #state{connection_states = ConnectionStates,
+ protocol_specific = PS} = State) ->
gen_handshake(?FUNCTION_NAME, Type, Event,
prepare_flight(State#state{connection_states = ConnectionStates,
- flight_state = connection}));
+ protocol_specific = PS#{flight_state => connection}}));
abbreviated(state_timeout, Event, State) ->
handle_state_timeout(Event, ?FUNCTION_NAME, State);
abbreviated(Type, Event, State) ->
@@ -664,10 +662,11 @@ cipher(internal = Type, #change_cipher_spec{type = <<1>>} = Event,
ConnectionStates1 = dtls_record:save_current_connection_state(ConnectionStates0, read),
ConnectionStates = dtls_record:next_epoch(ConnectionStates1, read),
ssl_connection:?FUNCTION_NAME(Type, Event, State#state{connection_states = ConnectionStates}, ?MODULE);
-cipher(internal = Type, #finished{} = Event, #state{connection_states = ConnectionStates} = State) ->
+cipher(internal = Type, #finished{} = Event, #state{connection_states = ConnectionStates,
+ protocol_specific = PS} = State) ->
ssl_connection:?FUNCTION_NAME(Type, Event,
prepare_flight(State#state{connection_states = ConnectionStates,
- flight_state = connection}),
+ protocol_specific = PS#{flight_state => connection}}),
?MODULE);
cipher(state_timeout, Event, State) ->
handle_state_timeout(Event, ?FUNCTION_NAME, State);
@@ -685,14 +684,16 @@ connection(info, Event, State) ->
gen_info(Event, ?FUNCTION_NAME, State);
connection(internal, #hello_request{}, #state{static_env = #static_env{host = Host,
port = Port,
+ data_tag = DataTag,
session_cache = Cache,
session_cache_cb = CacheCb
},
handshake_env = #handshake_env{ renegotiation = {Renegotiation, _}},
+ connection_env = CEnv,
session = #session{own_certificate = Cert} = Session0,
-
ssl_options = SslOpts,
- connection_states = ConnectionStates0
+ connection_states = ConnectionStates0,
+ protocol_specific = PS
} = State0) ->
Hello = dtls_handshake:client_hello(Host, Port, ConnectionStates0, SslOpts,
@@ -700,26 +701,26 @@ connection(internal, #hello_request{}, #state{static_env = #static_env{host = Ho
Version = Hello#client_hello.client_version,
HelloVersion = dtls_record:hello_version(Version, SslOpts#ssl_options.versions),
State1 = prepare_flight(State0),
- {State2, Actions} = send_handshake(Hello, State1#state{negotiated_version = HelloVersion}),
+ {State2, Actions} = send_handshake(Hello, State1#state{connection_env = CEnv#connection_env{negotiated_version = HelloVersion}}),
{Record, State} =
next_record(
- State2#state{flight_state = {retransmit, ?INITIAL_RETRANSMIT_TIMEOUT},
+ State2#state{protocol_specific = PS#{flight_state => initial_flight_state(DataTag)},
session = Session0#session{session_id
- = Hello#client_hello.session_id}}),
+ = Hello#client_hello.session_id}}),
next_event(hello, Record, State, Actions);
connection(internal, #client_hello{} = Hello, #state{static_env = #static_env{role = server},
- allow_renegotiate = true} = State) ->
+ handshake_env = #handshake_env{allow_renegotiate = true} = HsEnv} = State) ->
%% Mitigate Computational DoS attack
%% http://www.educatedguesswork.org/2011/10/ssltls_and_computational_dos.html
%% http://www.thc.org/thc-ssl-dos/ Rather than disabling client
%% initiated renegotiation we will disallow many client initiated
%% renegotiations immediately after each other.
erlang:send_after(?WAIT_TO_ALLOW_RENEGOTIATION, self(), allow_renegotiate),
- {next_state, hello, State#state{allow_renegotiate = false,
- handshake_env = #handshake_env{renegotiation = {true, peer}}},
+ {next_state, hello, State#state{handshake_env = HsEnv#handshake_env{renegotiation = {true, peer},
+ allow_renegotiate = false}},
[{next_event, internal, Hello}]};
connection(internal, #client_hello{}, #state{static_env = #static_env{role = server},
- allow_renegotiate = false} = State0) ->
+ handshake_env = #handshake_env{allow_renegotiate = false}} = State0) ->
Alert = ?ALERT_REC(?WARNING, ?NO_RENEGOTIATION),
State1 = send_alert(Alert, State0),
{Record, State} = ssl_connection:prepare_connection(State1, ?MODULE),
@@ -790,8 +791,10 @@ initial_state(Role, Host, Port, Socket, {SSLOptions, SocketOptions, _}, User,
#state{static_env = InitStatEnv,
handshake_env = #handshake_env{
tls_handshake_history = ssl_handshake:init_handshake_history(),
- renegotiation = {false, first}
+ renegotiation = {false, first},
+ allow_renegotiate = SSLOptions#ssl_options.client_renegotiation
},
+ connection_env = #connection_env{user_application = {Monitor, User}},
socket_options = SocketOptions,
%% We do not want to save the password in the state so that
%% could be written in the clear into error logs.
@@ -799,14 +802,17 @@ initial_state(Role, Host, Port, Socket, {SSLOptions, SocketOptions, _}, User,
session = #session{is_resumable = new},
connection_states = ConnectionStates,
protocol_buffers = #protocol_buffers{},
- user_application = {Monitor, User},
- user_data_buffer = <<>>,
- allow_renegotiate = SSLOptions#ssl_options.client_renegotiation,
+ user_data_buffer = {[],0,[]},
start_or_recv_from = undefined,
flight_buffer = new_flight(),
- flight_state = {retransmit, ?INITIAL_RETRANSMIT_TIMEOUT}
+ protocol_specific = #{flight_state => initial_flight_state(DataTag)}
}.
+initial_flight_state(udp)->
+ {retransmit, ?INITIAL_RETRANSMIT_TIMEOUT};
+initial_flight_state(_) ->
+ reliable.
+
next_dtls_record(Data, StateName, #state{protocol_buffers = #protocol_buffers{
dtls_record_buffer = Buf0,
dtls_cipher_texts = CT0} = Buffers} = State0) ->
@@ -824,7 +830,7 @@ next_dtls_record(Data, StateName, #state{protocol_buffers = #protocol_buffers{
acceptable_record_versions(hello, _) ->
[dtls_record:protocol_version(Vsn) || Vsn <- ?ALL_DATAGRAM_SUPPORTED_VERSIONS];
-acceptable_record_versions(_, #state{negotiated_version = Version}) ->
+acceptable_record_versions(_, #state{connection_env = #connection_env{negotiated_version = Version}}) ->
[Version].
dtls_handshake_events(Packets) ->
@@ -843,8 +849,9 @@ decode_cipher_text(#state{protocol_buffers = #protocol_buffers{dtls_cipher_texts
{Alert, State}
end.
-dtls_version(hello, Version, #state{static_env = #static_env{role = server}} = State) ->
- State#state{negotiated_version = Version}; %%Inital version
+dtls_version(hello, Version, #state{static_env = #static_env{role = server},
+ connection_env = CEnv} = State) ->
+ State#state{connection_env = CEnv#connection_env{negotiated_version = Version}}; %%Inital version
dtls_version(_,_, State) ->
State.
@@ -853,10 +860,11 @@ handle_client_hello(#client_hello{client_version = ClientVersion} = Hello,
static_env = #static_env{port = Port,
session_cache = Cache,
session_cache_cb = CacheCb},
- handshake_env = #handshake_env{renegotiation = {Renegotiation, _}} = HsEnv,
+ handshake_env = #handshake_env{kex_algorithm = KeyExAlg,
+ renegotiation = {Renegotiation, _},
+ negotiated_protocol = CurrentProtocol} = HsEnv,
+ connection_env = CEnv,
session = #session{own_certificate = Cert} = Session0,
- negotiated_protocol = CurrentProtocol,
- key_algorithm = KeyExAlg,
ssl_options = SslOpts} = State0) ->
case dtls_handshake:hello(Hello, SslOpts, {Port, Session0, Cache, CacheCb,
@@ -871,11 +879,12 @@ handle_client_hello(#client_hello{client_version = ClientVersion} = Hello,
end,
State = prepare_flight(State0#state{connection_states = ConnectionStates,
- negotiated_version = Version,
- hashsign_algorithm = HashSign,
- handshake_env = HsEnv#handshake_env{client_hello_version = ClientVersion},
- session = Session,
- negotiated_protocol = Protocol}),
+ connection_env = CEnv#connection_env{negotiated_version = Version},
+ handshake_env = HsEnv#handshake_env{
+ hashsign_algorithm = HashSign,
+ client_hello_version = ClientVersion,
+ negotiated_protocol = Protocol},
+ session = Session}),
ssl_connection:hello(internal, {common_client_hello, Type, ServerHelloExt},
State, ?MODULE)
@@ -895,9 +904,9 @@ handle_info({Protocol, _, _, _, Data}, StateName,
handle_info({CloseTag, Socket}, StateName,
#state{static_env = #static_env{socket = Socket,
close_tag = CloseTag},
+ connection_env = #connection_env{negotiated_version = Version},
socket_options = #socket_options{active = Active},
- protocol_buffers = #protocol_buffers{dtls_cipher_texts = CTs},
- negotiated_version = Version} = State) ->
+ protocol_buffers = #protocol_buffers{dtls_cipher_texts = CTs}} = State) ->
%% Note that as of DTLS 1.2 (TLS 1.1),
%% failure to properly close a connection no longer requires that a
%% session not be resumed. This is a change from DTLS 1.0 to conform
@@ -933,9 +942,10 @@ handle_info(Msg, StateName, State) ->
ssl_connection:StateName(info, Msg, State, ?MODULE).
handle_state_timeout(flight_retransmission_timeout, StateName,
- #state{flight_state = {retransmit, NextTimeout}} = State0) ->
- {State1, Actions0} = send_handshake_flight(State0#state{flight_state = {retransmit, NextTimeout}},
- retransmit_epoch(StateName, State0)),
+ #state{protocol_specific =
+ #{flight_state := {retransmit, _NextTimeout}}} = State0) ->
+ {State1, Actions0} = send_handshake_flight(State0,
+ retransmit_epoch(StateName, State0)),
{next_state, StateName, State, Actions} = next_event(StateName, no_record, State1, Actions0),
%% This will reset the retransmission timer by repeating the enter state event
{repeat_state, State, Actions}.
@@ -975,7 +985,7 @@ decode_alerts(Bin) ->
ssl_alert:decode(Bin).
gen_handshake(StateName, Type, Event,
- #state{negotiated_version = Version} = State) ->
+ #state{connection_env = #connection_env{negotiated_version = Version}} = State) ->
try ssl_connection:StateName(Type, Event, State, ?MODULE) of
Result ->
Result
@@ -986,7 +996,7 @@ gen_handshake(StateName, Type, Event,
Version, StateName, State)
end.
-gen_info(Event, connection = StateName, #state{negotiated_version = Version} = State) ->
+gen_info(Event, connection = StateName, #state{connection_env = #connection_env{negotiated_version = Version}} = State) ->
try handle_info(Event, StateName, State) of
Result ->
Result
@@ -997,7 +1007,7 @@ gen_info(Event, connection = StateName, #state{negotiated_version = Version} =
Version, StateName, State)
end;
-gen_info(Event, StateName, #state{negotiated_version = Version} = State) ->
+gen_info(Event, StateName, #state{connection_env = #connection_env{negotiated_version = Version}} = State) ->
try handle_info(Event, StateName, State) of
Result ->
Result
@@ -1041,17 +1051,17 @@ next_flight(Flight) ->
handshakes_after_change_cipher_spec => []}.
handle_flight_timer(#state{static_env = #static_env{data_tag = udp},
- flight_state = {retransmit, Timeout}} = State) ->
+ protocol_specific = #{flight_state := {retransmit, Timeout}}} = State) ->
start_retransmision_timer(Timeout, State);
handle_flight_timer(#state{static_env = #static_env{data_tag = udp},
- flight_state = connection} = State) ->
+ protocol_specific = #{flight_state := connection}} = State) ->
{State, []};
-handle_flight_timer(State) ->
+handle_flight_timer(#state{protocol_specific = #{flight_state := reliable}} = State) ->
%% No retransmision needed i.e DTLS over SCTP
- {State#state{flight_state = reliable}, []}.
+ {State, []}.
-start_retransmision_timer(Timeout, State) ->
- {State#state{flight_state = {retransmit, new_timeout(Timeout)}},
+start_retransmision_timer(Timeout, #state{protocol_specific = PS} = State) ->
+ {State#state{protocol_specific = PS#{flight_state => {retransmit, new_timeout(Timeout)}}},
[{state_timeout, Timeout, flight_retransmission_timeout}]}.
new_timeout(N) when N =< 30 ->
@@ -1061,9 +1071,9 @@ new_timeout(_) ->
send_handshake_flight(#state{static_env = #static_env{socket = Socket,
transport_cb = Transport},
- flight_buffer = #{handshakes := Flight,
+ connection_env = #connection_env{negotiated_version = Version},
+ flight_buffer = #{handshakes := Flight,
change_cipher_spec := undefined},
- negotiated_version = Version,
connection_states = ConnectionStates0} = State0, Epoch) ->
%% TODO remove hardcoded Max size
{Encoded, ConnectionStates} =
@@ -1073,10 +1083,10 @@ send_handshake_flight(#state{static_env = #static_env{socket = Socket,
send_handshake_flight(#state{static_env = #static_env{socket = Socket,
transport_cb = Transport},
+ connection_env = #connection_env{negotiated_version = Version},
flight_buffer = #{handshakes := [_|_] = Flight0,
change_cipher_spec := ChangeCipher,
handshakes_after_change_cipher_spec := []},
- negotiated_version = Version,
connection_states = ConnectionStates0} = State0, Epoch) ->
{HsBefore, ConnectionStates1} =
encode_handshake_flight(lists:reverse(Flight0), Version, 1400, Epoch, ConnectionStates0),
@@ -1087,10 +1097,10 @@ send_handshake_flight(#state{static_env = #static_env{socket = Socket,
send_handshake_flight(#state{static_env = #static_env{socket = Socket,
transport_cb = Transport},
+ connection_env = #connection_env{negotiated_version = Version},
flight_buffer = #{handshakes := [_|_] = Flight0,
change_cipher_spec := ChangeCipher,
handshakes_after_change_cipher_spec := Flight1},
- negotiated_version = Version,
connection_states = ConnectionStates0} = State0, Epoch) ->
{HsBefore, ConnectionStates1} =
encode_handshake_flight(lists:reverse(Flight0), Version, 1400, Epoch-1, ConnectionStates0),
@@ -1103,10 +1113,10 @@ send_handshake_flight(#state{static_env = #static_env{socket = Socket,
send_handshake_flight(#state{static_env = #static_env{socket = Socket,
transport_cb = Transport},
+ connection_env = #connection_env{negotiated_version = Version},
flight_buffer = #{handshakes := [],
change_cipher_spec := ChangeCipher,
handshakes_after_change_cipher_spec := Flight1},
- negotiated_version = Version,
connection_states = ConnectionStates0} = State0, Epoch) ->
{EncChangeCipher, ConnectionStates1} =
encode_change_cipher(ChangeCipher, Version, Epoch-1, ConnectionStates0),
@@ -1160,10 +1170,9 @@ log_ignore_alert(_, _, _, _) ->
send_application_data(Data, From, _StateName,
#state{static_env = #static_env{socket = Socket,
- protocol_cb = Connection,
transport_cb = Transport},
+ connection_env = #connection_env{negotiated_version = Version},
handshake_env = HsEnv,
- negotiated_version = Version,
connection_states = ConnectionStates0,
ssl_options = #ssl_options{renegotiate_at = RenegotiateAt}} = State0) ->
@@ -1173,9 +1182,9 @@ send_application_data(Data, From, _StateName,
[{next_event, {call, From}, {application_data, Data}}]);
false ->
{Msgs, ConnectionStates} =
- Connection:encode_data(Data, Version, ConnectionStates0),
+ dtls_record:encode_data(Data, Version, ConnectionStates0),
State = State0#state{connection_states = ConnectionStates},
- case Connection:send(Transport, Socket, Msgs) of
+ case send(Transport, Socket, Msgs) of
ok ->
ssl_connection:hibernate_after(connection, State, [{reply, From, ok}]);
Result ->
@@ -1197,3 +1206,4 @@ is_time_to_renegotiate(N, M) when N < M->
false;
is_time_to_renegotiate(_,_) ->
true.
+
diff --git a/lib/ssl/src/dtls_handshake.erl b/lib/ssl/src/dtls_handshake.erl
index eb0f742e70..8e749e65b8 100644
--- a/lib/ssl/src/dtls_handshake.erl
+++ b/lib/ssl/src/dtls_handshake.erl
@@ -46,7 +46,7 @@
%% Handshake handling
%%====================================================================
%%--------------------------------------------------------------------
--spec client_hello(host(), inet:port_number(), ssl_record:connection_states(),
+-spec client_hello(ssl:host(), inet:port_number(), ssl_record:connection_states(),
#ssl_options{}, integer(), atom(), boolean(), der_cert()) ->
#client_hello{}.
%%
@@ -59,7 +59,7 @@ client_hello(Host, Port, ConnectionStates, SslOpts,
Cache, CacheCb, Renegotiation, OwnCert).
%%--------------------------------------------------------------------
--spec client_hello(host(), inet:port_number(), term(), ssl_record:connection_states(),
+-spec client_hello(ssl:host(), inet:port_number(), term(), ssl_record:connection_states(),
#ssl_options{}, integer(), atom(), boolean(), der_cert()) ->
#client_hello{}.
%%
@@ -123,7 +123,7 @@ cookie(Key, Address, Port, #client_hello{client_version = {Major, Minor},
Random, SessionId, CipherSuites, CompressionMethods],
crypto:hmac(sha, Key, CookieData).
%%--------------------------------------------------------------------
--spec hello_verify_request(binary(), dtls_record:dtls_version()) -> #hello_verify_request{}.
+-spec hello_verify_request(binary(), ssl_record:ssl_version()) -> #hello_verify_request{}.
%%
%% Description: Creates a hello verify request message sent by server to
%% verify client
@@ -151,7 +151,7 @@ encode_handshake(Handshake, Version, Seq) ->
%%--------------------------------------------------------------------
%%--------------------------------------------------------------------
--spec get_dtls_handshake(dtls_record:dtls_version(), binary(), #protocol_buffers{}) ->
+-spec get_dtls_handshake(ssl_record:ssl_version(), binary(), #protocol_buffers{}) ->
{[dtls_handshake()], #protocol_buffers{}}.
%%
%% Description: Given buffered and new data from dtls_record, collects
diff --git a/lib/ssl/src/dtls_handshake.hrl b/lib/ssl/src/dtls_handshake.hrl
index a16489bbd1..dab4038762 100644
--- a/lib/ssl/src/dtls_handshake.hrl
+++ b/lib/ssl/src/dtls_handshake.hrl
@@ -27,6 +27,7 @@
-define(dtls_handshake, true).
-include("ssl_handshake.hrl"). %% Common TLS and DTLS records and Constantes
+-include("ssl_api.hrl").
-define(HELLO_VERIFY_REQUEST, 3).
-define(HELLO_VERIFY_REQUEST_VERSION, {254, 255}).
diff --git a/lib/ssl/src/dtls_packet_demux.erl b/lib/ssl/src/dtls_packet_demux.erl
index e03a4e9cb9..afcd4af000 100644
--- a/lib/ssl/src/dtls_packet_demux.erl
+++ b/lib/ssl/src/dtls_packet_demux.erl
@@ -145,11 +145,11 @@ handle_info({Transport, Socket, IP, InPortNo, _} = Msg, #state{listener = Socket
%% UDP socket does not have a connection and should not receive an econnreset
%% This does however happens on some windows versions. Just ignoring it
%% appears to make things work as expected!
-handle_info({Error, Socket, econnreset = Error}, #state{listener = Socket, transport = {_,_,_, udp_error}} = State) ->
+handle_info({udp_error, Socket, econnreset = Error}, #state{listener = Socket, transport = {_,_,_, udp_error}} = State) ->
Report = io_lib:format("Ignore SSL UDP Listener: Socket error: ~p ~n", [Error]),
?LOG_NOTICE(Report),
{noreply, State};
-handle_info({Error, Socket, Error}, #state{listener = Socket, transport = {_,_,_, Error}} = State) ->
+handle_info({ErrorTag, Socket, Error}, #state{listener = Socket, transport = {_,_,_, ErrorTag}} = State) ->
Report = io_lib:format("SSL Packet muliplxer shutdown: Socket error: ~p ~n", [Error]),
?LOG_NOTICE(Report),
{noreply, State#state{close=true}};
diff --git a/lib/ssl/src/dtls_record.erl b/lib/ssl/src/dtls_record.erl
index b7346d3ec8..2fe875da31 100644
--- a/lib/ssl/src/dtls_record.erl
+++ b/lib/ssl/src/dtls_record.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2013-2018. All Rights Reserved.
+%% Copyright Ericsson AB 2013-2019. 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.
@@ -49,9 +49,8 @@
is_acceptable_version/2, hello_version/2]).
--export_type([dtls_version/0, dtls_atom_version/0]).
+-export_type([dtls_atom_version/0]).
--type dtls_version() :: ssl_record:ssl_version().
-type dtls_atom_version() :: dtlsv1 | 'dtlsv1.2'.
-define(REPLAY_WINDOW_SIZE, 64).
@@ -135,7 +134,7 @@ set_connection_state_by_epoch(ReadState, Epoch, #{saved_read := #{epoch := Epoch
States#{saved_read := ReadState}.
%%--------------------------------------------------------------------
--spec init_connection_state_seq(dtls_version(), ssl_record:connection_states()) ->
+-spec init_connection_state_seq(ssl_record:ssl_version(), ssl_record:connection_states()) ->
ssl_record:connection_state().
%%
%% Description: Copy the read sequence number to the write sequence number
@@ -163,7 +162,7 @@ current_connection_state_epoch(#{current_write := #{epoch := Epoch}},
Epoch.
%%--------------------------------------------------------------------
--spec get_dtls_records(binary(), [dtls_version()], binary()) -> {[binary()], binary()} | #alert{}.
+-spec get_dtls_records(binary(), [ssl_record:ssl_version()], binary()) -> {[binary()], binary()} | #alert{}.
%%
%% Description: Given old buffer and new data from UDP/SCTP, packs up a records
%% and returns it as a list of tls_compressed binaries also returns leftover
@@ -188,7 +187,7 @@ get_dtls_records(Data, Versions, Buffer) ->
%%====================================================================
%%--------------------------------------------------------------------
--spec encode_handshake(iolist(), dtls_version(), integer(), ssl_record:connection_states()) ->
+-spec encode_handshake(iolist(), ssl_record:ssl_version(), integer(), ssl_record:connection_states()) ->
{iolist(), ssl_record:connection_states()}.
%
%% Description: Encodes a handshake message to send on the ssl-socket.
@@ -198,7 +197,7 @@ encode_handshake(Frag, Version, Epoch, ConnectionStates) ->
%%--------------------------------------------------------------------
--spec encode_alert_record(#alert{}, dtls_version(), ssl_record:connection_states()) ->
+-spec encode_alert_record(#alert{}, ssl_record:ssl_version(), ssl_record:connection_states()) ->
{iolist(), ssl_record:connection_states()}.
%%
%% Description: Encodes an alert message to send on the ssl-socket.
@@ -210,7 +209,7 @@ encode_alert_record(#alert{level = Level, description = Description},
ConnectionStates).
%%--------------------------------------------------------------------
--spec encode_change_cipher_spec(dtls_version(), integer(), ssl_record:connection_states()) ->
+-spec encode_change_cipher_spec(ssl_record:ssl_version(), integer(), ssl_record:connection_states()) ->
{iolist(), ssl_record:connection_states()}.
%%
%% Description: Encodes a change_cipher_spec-message to send on the ssl socket.
@@ -219,7 +218,7 @@ encode_change_cipher_spec(Version, Epoch, ConnectionStates) ->
encode_plain_text(?CHANGE_CIPHER_SPEC, Version, Epoch, ?byte(?CHANGE_CIPHER_SPEC_PROTO), ConnectionStates).
%%--------------------------------------------------------------------
--spec encode_data(binary(), dtls_version(), ssl_record:connection_states()) ->
+-spec encode_data(binary(), ssl_record:ssl_version(), ssl_record:connection_states()) ->
{iolist(),ssl_record:connection_states()}.
%%
%% Description: Encodes data to send on the ssl-socket.
@@ -248,8 +247,8 @@ decode_cipher_text(#ssl_tls{epoch = Epoch} = CipherText, ConnnectionStates0) ->
%%====================================================================
%%--------------------------------------------------------------------
--spec protocol_version(dtls_atom_version() | dtls_version()) ->
- dtls_version() | dtls_atom_version().
+-spec protocol_version(dtls_atom_version() | ssl_record:ssl_version()) ->
+ ssl_record:ssl_version() | dtls_atom_version().
%%
%% Description: Creates a protocol version record from a version atom
%% or vice versa.
@@ -263,7 +262,7 @@ protocol_version({254, 253}) ->
protocol_version({254, 255}) ->
dtlsv1.
%%--------------------------------------------------------------------
--spec lowest_protocol_version(dtls_version(), dtls_version()) -> dtls_version().
+-spec lowest_protocol_version(ssl_record:ssl_version(), ssl_record:ssl_version()) -> ssl_record:ssl_version().
%%
%% Description: Lowes protocol version of two given versions
%%--------------------------------------------------------------------
@@ -277,7 +276,7 @@ lowest_protocol_version(_,Version) ->
Version.
%%--------------------------------------------------------------------
--spec lowest_protocol_version([dtls_version()]) -> dtls_version().
+-spec lowest_protocol_version([ssl_record:ssl_version()]) -> ssl_record:ssl_version().
%%
%% Description: Lowest protocol version present in a list
%%--------------------------------------------------------------------
@@ -288,7 +287,7 @@ lowest_protocol_version(Versions) ->
lowest_list_protocol_version(Ver, Vers).
%%--------------------------------------------------------------------
--spec highest_protocol_version([dtls_version()]) -> dtls_version().
+-spec highest_protocol_version([ssl_record:ssl_version()]) -> ssl_record:ssl_version().
%%
%% Description: Highest protocol version present in a list
%%--------------------------------------------------------------------
@@ -299,7 +298,7 @@ highest_protocol_version(Versions) ->
highest_list_protocol_version(Ver, Vers).
%%--------------------------------------------------------------------
--spec highest_protocol_version(dtls_version(), dtls_version()) -> dtls_version().
+-spec highest_protocol_version(ssl_record:ssl_version(), ssl_record:ssl_version()) -> ssl_record:ssl_version().
%%
%% Description: Highest protocol version of two given versions
%%--------------------------------------------------------------------
@@ -315,7 +314,7 @@ highest_protocol_version(_,Version) ->
Version.
%%--------------------------------------------------------------------
--spec is_higher(V1 :: dtls_version(), V2::dtls_version()) -> boolean().
+-spec is_higher(V1 :: ssl_record:ssl_version(), V2::ssl_record:ssl_version()) -> boolean().
%%
%% Description: Is V1 > V2
%%--------------------------------------------------------------------
@@ -327,7 +326,7 @@ is_higher(_, _) ->
false.
%%--------------------------------------------------------------------
--spec supported_protocol_versions() -> [dtls_version()].
+-spec supported_protocol_versions() -> [ssl_record:ssl_version()].
%%
%% Description: Protocol versions supported
%%--------------------------------------------------------------------
@@ -370,7 +369,7 @@ supported_protocol_versions([_|_] = Vsns) ->
end.
%%--------------------------------------------------------------------
--spec is_acceptable_version(dtls_version(), Supported :: [dtls_version()]) -> boolean().
+-spec is_acceptable_version(ssl_record:ssl_version(), Supported :: [ssl_record:ssl_version()]) -> boolean().
%%
%% Description: ssl version 2 is not acceptable security risks are too big.
%%
@@ -378,7 +377,7 @@ supported_protocol_versions([_|_] = Vsns) ->
is_acceptable_version(Version, Versions) ->
lists:member(Version, Versions).
--spec hello_version(dtls_version(), [dtls_version()]) -> dtls_version().
+-spec hello_version(ssl_record:ssl_version(), [ssl_record:ssl_version()]) -> ssl_record:ssl_version().
hello_version(Version, Versions) ->
case dtls_v1:corresponding_tls_version(Version) of
TLSVersion when TLSVersion >= {3, 3} ->
@@ -547,15 +546,15 @@ decode_cipher_text(#ssl_tls{type = Type, version = Version,
compression_algorithm = CompAlg}} = ReadState0,
ConnnectionStates0) ->
AAD = start_additional_data(Type, Version, Epoch, Seq),
- CipherS1 = ssl_record:nonce_seed(BulkCipherAlgo, <<?UINT16(Epoch), ?UINT48(Seq)>>, CipherS0),
+ CipherS = ssl_record:nonce_seed(BulkCipherAlgo, <<?UINT16(Epoch), ?UINT48(Seq)>>, CipherS0),
TLSVersion = dtls_v1:corresponding_tls_version(Version),
- case ssl_record:decipher_aead(BulkCipherAlgo, CipherS1, AAD, CipherFragment, TLSVersion) of
- {PlainFragment, CipherState} ->
- {Plain, CompressionS1} = ssl_record:uncompress(CompAlg,
+ case ssl_record:decipher_aead(BulkCipherAlgo, CipherS, AAD, CipherFragment, TLSVersion) of
+ PlainFragment when is_binary(PlainFragment) ->
+ {Plain, CompressionS} = ssl_record:uncompress(CompAlg,
PlainFragment, CompressionS0),
- ReadState0 = ReadState0#{compression_state => CompressionS1,
- cipher_state => CipherState},
- ReadState = update_replay_window(Seq, ReadState0),
+ ReadState1 = ReadState0#{compression_state := CompressionS,
+ cipher_state := CipherS},
+ ReadState = update_replay_window(Seq, ReadState1),
ConnnectionStates = set_connection_state_by_epoch(ReadState, Epoch, ConnnectionStates0, read),
{CipherText#ssl_tls{fragment = Plain}, ConnnectionStates};
#alert{} = Alert ->
diff --git a/lib/ssl/src/inet_tls_dist.erl b/lib/ssl/src/inet_tls_dist.erl
index ce771343fe..e7fab7ebc5 100644
--- a/lib/ssl/src/inet_tls_dist.erl
+++ b/lib/ssl/src/inet_tls_dist.erl
@@ -481,22 +481,25 @@ allowed_nodes(PeerCert, Allowed, PeerIP, Node, Host) ->
allowed_nodes(PeerCert, Allowed, PeerIP)
end.
-
-
setup(Node, Type, MyNode, LongOrShortNames, SetupTime) ->
gen_setup(inet_tcp, Node, Type, MyNode, LongOrShortNames, SetupTime).
gen_setup(Driver, Node, Type, MyNode, LongOrShortNames, SetupTime) ->
Kernel = self(),
monitor_pid(
- spawn_opt(
- fun() ->
- do_setup(
- Driver, Kernel, Node, Type,
- MyNode, LongOrShortNames, SetupTime)
- end,
- [link, {priority, max}])).
+ spawn_opt(setup_fun(Driver, Kernel, Node, Type, MyNode, LongOrShortNames, SetupTime),
+ [link, {priority, max}])).
+
+-spec setup_fun(_,_,_,_,_,_,_) -> fun(() -> no_return()).
+setup_fun(Driver, Kernel, Node, Type, MyNode, LongOrShortNames, SetupTime) ->
+ fun() ->
+ do_setup(
+ Driver, Kernel, Node, Type,
+ MyNode, LongOrShortNames, SetupTime)
+ end.
+
+-spec do_setup(_,_,_,_,_,_,_) -> no_return().
do_setup(Driver, Kernel, Node, Type, MyNode, LongOrShortNames, SetupTime) ->
{Name, Address} = split_node(Driver, Node, LongOrShortNames),
ErlEpmd = net_kernel:epmd_module(),
@@ -521,6 +524,8 @@ do_setup(Driver, Kernel, Node, Type, MyNode, LongOrShortNames, SetupTime) ->
trace({getaddr_failed, Driver, Address, Other}))
end.
+-spec do_setup_connect(_,_,_,_,_,_,_,_,_,_) -> no_return().
+
do_setup_connect(Driver, Kernel, Node, Address, Ip, TcpPort, Version, Type, MyNode, Timer) ->
Opts = trace(connect_options(get_ssl_options(client))),
dist_util:reset_timer(Timer),
@@ -565,7 +570,7 @@ gen_close(Driver, Socket) ->
%% Determine if EPMD module supports address resolving. Default
%% is to use inet_tcp:getaddr/2.
%% ------------------------------------------------------------
-get_address_resolver(EpmdModule, Driver) ->
+get_address_resolver(EpmdModule, _Driver) ->
case erlang:function_exported(EpmdModule, address_please, 3) of
true -> {EpmdModule, address_please};
_ -> {erl_epmd, address_please}
diff --git a/lib/ssl/src/ssl.erl b/lib/ssl/src/ssl.erl
index 616e9e26e7..3516bd6d49 100644
--- a/lib/ssl/src/ssl.erl
+++ b/lib/ssl/src/ssl.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1999-2018. All Rights Reserved.
+%% Copyright Ericsson AB 1999-2019. 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,16 +62,338 @@
-deprecated({ssl_accept, 2, eventually}).
-deprecated({ssl_accept, 3, eventually}).
+-export_type([socket/0,
+ sslsocket/0,
+ socket_option/0,
+ active_msgs/0,
+ host/0,
+ tls_option/0,
+ tls_client_option/0,
+ tls_server_option/0,
+ erl_cipher_suite/0,
+ old_cipher_suite/0,
+ ciphers/0,
+ cipher/0,
+ hash/0,
+ key/0,
+ kex_algo/0,
+ prf_random/0,
+ cipher_filters/0,
+ sign_algo/0,
+ protocol_version/0,
+ protocol_extensions/0,
+ session_id/0,
+ error_alert/0,
+ srp_param_type/0]).
+
+%% -------------------------------------------------------------------------------------------------------
+-type socket() :: gen_tcp:socket().
+-type socket_option() :: gen_tcp:connect_option() | gen_tcp:listen_option() | gen_udp:option().
+-type sslsocket() :: any().
+-type tls_option() :: tls_client_option() | tls_server_option().
+-type tls_client_option() :: client_option() | common_option() | socket_option() | transport_option().
+-type tls_server_option() :: server_option() | common_option() | socket_option() | transport_option().
+-type active_msgs() :: {ssl, sslsocket(), Data::binary() | list()} | {ssl_closed, sslsocket()} |
+ {ssl_error, sslsocket(), Reason::term()}.
+-type transport_option() :: {cb_info, {CallbackModule::atom(), DataTag::atom(),
+ ClosedTag::atom(), ErrTag::atom()}}.
+-type host() :: hostname() | ip_address().
+-type hostname() :: string().
+-type ip_address() :: inet:ip_address().
+-type session_id() :: binary().
+-type protocol_version() :: tls_version() | dtls_version().
+-type tls_version() :: tlsv1 | 'tlsv1.1' | 'tlsv1.2' | 'tlsv1.3' | legacy_version().
+-type dtls_version() :: 'dtlsv1' | 'dtlsv1.2'.
+-type legacy_version() :: sslv3.
+-type verify_type() :: verify_none | verify_peer.
+-type cipher() :: aes_128_cbc |
+ aes_256_cbc |
+ aes_128_gcm |
+ aes_256_gcm |
+ chacha20_poly1305 |
+ legacy_cipher().
+-type legacy_cipher() :: rc4_128 |
+ des_cbc |
+ '3des_ede_cbc'.
+
+-type hash() :: sha |
+ sha2() |
+ legacy_hash().
+
+-type sha2() :: sha224 |
+ sha256 |
+ sha384 |
+ sha512.
+
+-type legacy_hash() :: md5.
+
+-type sign_algo() :: rsa | dsa | ecdsa.
+
+-type sign_scheme() :: rsa_pkcs1_sha256
+ | rsa_pkcs1_sha384
+ | rsa_pkcs1_sha512
+ | ecdsa_secp256r1_sha256
+ | ecdsa_secp384r1_sha384
+ | ecdsa_secp521r1_sha512
+ | rsa_pss_rsae_sha256
+ | rsa_pss_rsae_sha384
+ | rsa_pss_rsae_sha512
+ | rsa_pss_pss_sha256
+ | rsa_pss_pss_sha384
+ | rsa_pss_pss_sha512
+ | rsa_pkcs1_sha1
+ | ecdsa_sha1.
+-type kex_algo() :: 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 |
+ any. %% TLS 1.3
+-type erl_cipher_suite() :: #{key_exchange := kex_algo(),
+ cipher := cipher(),
+ mac := hash() | aead,
+ prf := hash() | default_prf %% Old cipher suites, version dependent
+ }.
+
+-type old_cipher_suite() :: {kex_algo(), cipher(), hash()} % Pre TLS 1.2
+ %% TLS 1.2, internally PRE TLS 1.2 will use default_prf
+ | {kex_algo(), cipher(), hash() | aead, hash()}.
+
+-type named_curve() :: sect571r1 |
+ sect571k1 |
+ secp521r1 |
+ brainpoolP512r1 |
+ sect409k1 |
+ sect409r1 |
+ brainpoolP384r1 |
+ secp384r1 |
+ sect283k1 |
+ sect283r1 |
+ brainpoolP256r1 |
+ secp256k1 |
+ secp256r1 |
+ sect239k1 |
+ sect233k1 |
+ sect233r1 |
+ secp224k1 |
+ secp224r1 |
+ sect193r1 |
+ sect193r2 |
+ secp192k1 |
+ secp192r1 |
+ sect163k1 |
+ sect163r1 |
+ sect163r2 |
+ secp160k1 |
+ secp160r1 |
+ secp160r2.
+
+-type srp_param_type() :: srp_1024 |
+ srp_1536 |
+ srp_2048 |
+ srp_3072 |
+ srp_4096 |
+ srp_6144 |
+ srp_8192.
+
+-type error_alert() :: {tls_alert, {tls_alert(), Description::string()}}.
+
+-type tls_alert() :: close_notify |
+ unexpected_message |
+ bad_record_mac |
+ record_overflow |
+ handshake_failure |
+ bad_certificate |
+ unsupported_certificate |
+ certificate_revoked |
+ certificate_expired |
+ certificate_unknown |
+ illegal_parameter |
+ unknown_ca |
+ access_denied |
+ decode_error |
+ decrypt_error |
+ export_restriction|
+ protocol_version |
+ insufficient_security |
+ internal_error |
+ inappropriate_fallback |
+ user_canceled |
+ no_renegotiation |
+ unsupported_extension |
+ certificate_unobtainable |
+ unrecognized_name |
+ bad_certificate_status_response |
+ bad_certificate_hash_value |
+ unknown_psk_identity |
+ no_application_protocol.
+%% -------------------------------------------------------------------------------------------------------
+-type common_option() :: {protocol, protocol()} |
+ {handshake, handshake_completion()} |
+ {cert, cert()} |
+ {certfile, cert_pem()} |
+ {key, key()} |
+ {keyfile, key_pem()} |
+ {password, key_password()} |
+ {ciphers, cipher_suites()} |
+ {eccs, eccs()} |
+ {signature_algs_cert, signature_schemes()} |
+ {secure_renegotiate, secure_renegotiation()} |
+ {depth, allowed_cert_chain_length()} |
+ {verify_fun, custom_verify()} |
+ {crl_check, crl_check()} |
+ {crl_cache, crl_cache_opts()} |
+ {max_handshake_size, handshake_size()} |
+ {partial_chain, root_fun()} |
+ {versions, protocol_versions()} |
+ {user_lookup_fun, custom_user_lookup()} |
+ {log_level, logging_level()} |
+ {log_alert, log_alert()} |
+ {hibernate_after, hibernate_after()} |
+ {padding_check, padding_check()} |
+ {beast_mitigation, beast_mitigation()} |
+ {ssl_imp, ssl_imp()}.
+
+-type protocol() :: tls | dtls.
+-type handshake_completion() :: hello | full.
+-type cert() :: public_key:der_encoded().
+-type cert_pem() :: file:filename().
+-type key() :: {'RSAPrivateKey'| 'DSAPrivateKey' | 'ECPrivateKey' |'PrivateKeyInfo',
+ public_key:der_encoded()} |
+ #{algorithm := rsa | dss | ecdsa,
+ engine := crypto:engine_ref(),
+ key_id := crypto:key_id(),
+ password => crypto:password()}.
+-type key_pem() :: file:filename().
+-type key_password() :: string().
+-type cipher_suites() :: ciphers().
+-type ciphers() :: [erl_cipher_suite()] |
+ string(). % (according to old API)
+-type cipher_filters() :: list({key_exchange | cipher | mac | prf,
+ algo_filter()}).
+-type algo_filter() :: fun((kex_algo()|cipher()|hash()|aead|default_prf) -> true | false).
+-type eccs() :: [named_curve()].
+-type secure_renegotiation() :: boolean().
+-type allowed_cert_chain_length() :: integer().
+
+-type custom_verify() :: {Verifyfun :: fun(), InitialUserState :: term()}.
+-type crl_check() :: boolean() | peer | best_effort.
+-type crl_cache_opts() :: [term()].
+-type handshake_size() :: integer().
+-type hibernate_after() :: timeout().
+-type root_fun() :: fun().
+-type protocol_versions() :: [protocol_version()].
+-type signature_algs() :: [{hash(), sign_algo()}].
+-type signature_schemes() :: [sign_scheme()].
+-type custom_user_lookup() :: {Lookupfun :: fun(), UserState :: term()}.
+-type padding_check() :: boolean().
+-type beast_mitigation() :: one_n_minus_one | zero_n | disabled.
+-type srp_identity() :: {Username :: string(), Password :: string()}.
+-type psk_identity() :: string().
+-type log_alert() :: boolean().
+-type logging_level() :: logger:level().
+
+%% -------------------------------------------------------------------------------------------------------
+
+-type client_option() :: {verify, client_verify_type()} |
+ {reuse_session, client_reuse_session()} |
+ {reuse_sessions, client_reuse_sessions()} |
+ {cacerts, client_cacerts()} |
+ {cacertfile, client_cafile()} |
+ {alpn_advertised_protocols, client_alpn()} |
+ {client_preferred_next_protocols, client_preferred_next_protocols()} |
+ {psk_identity, client_psk_identity()} |
+ {srp_identity, client_srp_identity()} |
+ {server_name_indication, sni()} |
+ {customize_hostname_check, customize_hostname_check()} |
+ {signature_algs, client_signature_algs()} |
+ {fallback, fallback()}.
+
+-type client_verify_type() :: verify_type().
+-type client_reuse_session() :: session_id().
+-type client_reuse_sessions() :: boolean() | save.
+-type client_cacerts() :: [public_key:der_encoded()].
+-type client_cafile() :: file:filename().
+-type app_level_protocol() :: binary().
+-type client_alpn() :: [app_level_protocol()].
+-type client_preferred_next_protocols() :: {Precedence :: server | client,
+ ClientPrefs :: [app_level_protocol()]} |
+ {Precedence :: server | client,
+ ClientPrefs :: [app_level_protocol()],
+ Default::app_level_protocol()}.
+-type client_psk_identity() :: psk_identity().
+-type client_srp_identity() :: srp_identity().
+-type customize_hostname_check() :: list().
+-type sni() :: HostName :: hostname() | disable.
+-type client_signature_algs() :: signature_algs().
+-type fallback() :: boolean().
+-type ssl_imp() :: new | old.
+
+%% -------------------------------------------------------------------------------------------------------
+
+-type server_option() :: {cacerts, server_cacerts()} |
+ {cacertfile, server_cafile()} |
+ {dh, dh_der()} |
+ {dhfile, dh_file()} |
+ {verify, server_verify_type()} |
+ {fail_if_no_peer_cert, fail_if_no_peer_cert()} |
+ {reuse_sessions, server_reuse_sessions()} |
+ {reuse_session, server_reuse_session()} |
+ {alpn_preferred_protocols, server_alpn()} |
+ {next_protocols_advertised, server_next_protocol()} |
+ {psk_identity, server_psk_identity()} |
+ {honor_cipher_order, boolean()} |
+ {sni_hosts, sni_hosts()} |
+ {sni_fun, sni_fun()} |
+ {honor_cipher_order, honor_cipher_order()} |
+ {honor_ecc_order, honor_ecc_order()} |
+ {client_renegotiation, client_renegotiation()}|
+ {signature_algs, server_signature_algs()}.
+
+-type server_cacerts() :: [public_key:der_encoded()].
+-type server_cafile() :: file:filename().
+-type server_alpn() :: [app_level_protocol()].
+-type server_next_protocol() :: [app_level_protocol()].
+-type server_psk_identity() :: psk_identity().
+-type dh_der() :: binary().
+-type dh_file() :: file:filename().
+-type server_verify_type() :: verify_type().
+-type fail_if_no_peer_cert() :: boolean().
+-type server_signature_algs() :: signature_algs().
+-type server_reuse_session() :: fun().
+-type server_reuse_sessions() :: boolean().
+-type sni_hosts() :: [{hostname(), [server_option() | common_option()]}].
+-type sni_fun() :: fun().
+-type honor_cipher_order() :: boolean().
+-type honor_ecc_order() :: boolean().
+-type client_renegotiation() :: boolean().
+%% -------------------------------------------------------------------------------------------------------
+-type prf_random() :: client_random | server_random.
+-type protocol_extensions() :: #{renegotiation_info => binary(),
+ signature_algs => signature_algs(),
+ alpn => app_level_protocol(),
+ srp => binary(),
+ next_protocol => app_level_protocol(),
+ ec_point_formats => [0..2],
+ elliptic_curves => [public_key:oid()],
+ sni => hostname()}.
+%% -------------------------------------------------------------------------------------------------------
+
+%%%--------------------------------------------------------------------
+%%% API
+%%%--------------------------------------------------------------------
+
%%--------------------------------------------------------------------
--spec start() -> ok | {error, reason()}.
--spec start(permanent | transient | temporary) -> ok | {error, reason()}.
%%
%% Description: Utility function that starts the ssl and applications
%% that it depends on.
%% see application(3)
%%--------------------------------------------------------------------
+-spec start() -> ok | {error, reason()}.
start() ->
start(temporary).
+-spec start(permanent | transient | temporary) -> ok | {error, reason()}.
start(Type) ->
case application:ensure_all_started(ssl, Type) of
{ok, _} ->
@@ -88,21 +410,17 @@ stop() ->
application:stop(ssl).
%%--------------------------------------------------------------------
-
--spec connect(host() | port(), [connect_option()]) -> {ok, #sslsocket{}} |
- {error, reason()}.
--spec connect(host() | port(), [connect_option()] | inet:port_number(),
- timeout() | list()) ->
- {ok, #sslsocket{}} | {error, reason()}.
--spec connect(host() | port(), inet:port_number(), list(), timeout()) ->
- {ok, #sslsocket{}} | {error, reason()}.
-
%%
%% Description: Connect to an ssl server.
%%--------------------------------------------------------------------
+-spec connect(host() | port(), [tls_client_option()]) -> {ok, #sslsocket{}} |
+ {error, reason()}.
connect(Socket, SslOptions) when is_port(Socket) ->
connect(Socket, SslOptions, infinity).
+-spec connect(host() | port(), [tls_client_option()] | inet:port_number(),
+ timeout() | list()) ->
+ {ok, #sslsocket{}} | {error, reason()}.
connect(Socket, SslOptions0, Timeout) when is_port(Socket),
(is_integer(Timeout) andalso Timeout >= 0) or (Timeout == infinity) ->
{Transport,_,_,_} = proplists:get_value(cb_info, SslOptions0,
@@ -119,6 +437,9 @@ connect(Socket, SslOptions0, Timeout) when is_port(Socket),
connect(Host, Port, Options) ->
connect(Host, Port, Options, infinity).
+-spec connect(host() | port(), inet:port_number(), list(), timeout()) ->
+ {ok, #sslsocket{}} | {error, reason()}.
+
connect(Host, Port, Options, Timeout) when (is_integer(Timeout) andalso Timeout >= 0) or (Timeout == infinity) ->
try
{ok, Config} = handle_options(Options, client, Host),
@@ -134,7 +455,7 @@ connect(Host, Port, Options, Timeout) when (is_integer(Timeout) andalso Timeout
end.
%%--------------------------------------------------------------------
--spec listen(inet:port_number(), [listen_option()]) ->{ok, #sslsocket{}} | {error, reason()}.
+-spec listen(inet:port_number(), [tls_server_option()]) ->{ok, #sslsocket{}} | {error, reason()}.
%%
%% Description: Creates an ssl listen socket.
@@ -150,16 +471,16 @@ listen(Port, Options0) ->
Error
end.
%%--------------------------------------------------------------------
--spec transport_accept(#sslsocket{}) -> {ok, #sslsocket{}} |
- {error, reason()}.
--spec transport_accept(#sslsocket{}, timeout()) -> {ok, #sslsocket{}} |
- {error, reason()}.
%%
%% Description: Performs transport accept on an ssl listen socket
%%--------------------------------------------------------------------
+-spec transport_accept(#sslsocket{}) -> {ok, #sslsocket{}} |
+ {error, reason()}.
transport_accept(ListenSocket) ->
transport_accept(ListenSocket, infinity).
+-spec transport_accept(#sslsocket{}, timeout()) -> {ok, #sslsocket{}} |
+ {error, reason()}.
transport_accept(#sslsocket{pid = {ListenSocket,
#config{connection_cb = ConnectionCb} = Config}}, Timeout)
when (is_integer(Timeout) andalso Timeout >= 0) or (Timeout == infinity) ->
@@ -171,25 +492,25 @@ transport_accept(#sslsocket{pid = {ListenSocket,
end.
%%--------------------------------------------------------------------
--spec ssl_accept(#sslsocket{}) -> ok | {error, reason()}.
--spec ssl_accept(#sslsocket{} | port(), timeout()| [ssl_option()
- | transport_option()]) ->
- ok | {ok, #sslsocket{}} | {error, reason()}.
-
--spec ssl_accept(#sslsocket{} | port(), [ssl_option()] | [ssl_option()| transport_option()], timeout()) ->
- ok | {ok, #sslsocket{}} | {error, reason()}.
%%
%% Description: Performs accept on an ssl listen socket. e.i. performs
%% ssl handshake.
%%--------------------------------------------------------------------
+-spec ssl_accept(#sslsocket{}) -> ok | {error, timeout | closed | {options, any()}| error_alert()}.
ssl_accept(ListenSocket) ->
ssl_accept(ListenSocket, [], infinity).
+
+-spec ssl_accept(#sslsocket{} | port(), timeout()| [tls_server_option()]) ->
+ ok | {ok, #sslsocket{}} | {error, timeout | closed | {options, any()}| error_alert()}.
ssl_accept(Socket, Timeout) when (is_integer(Timeout) andalso Timeout >= 0) or (Timeout == infinity) ->
ssl_accept(Socket, [], Timeout);
ssl_accept(ListenSocket, SslOptions) when is_port(ListenSocket) ->
ssl_accept(ListenSocket, SslOptions, infinity);
ssl_accept(Socket, Timeout) ->
ssl_accept(Socket, [], Timeout).
+
+-spec ssl_accept(#sslsocket{} | port(), [tls_server_option()], timeout()) ->
+ ok | {ok, #sslsocket{}} | {error, timeout | closed | {options, any()}| error_alert()}.
ssl_accept(Socket, SslOptions, Timeout) when is_port(Socket) ->
handshake(Socket, SslOptions, Timeout);
ssl_accept(Socket, SslOptions, Timeout) ->
@@ -200,22 +521,19 @@ ssl_accept(Socket, SslOptions, Timeout) ->
Error
end.
%%--------------------------------------------------------------------
--spec handshake(#sslsocket{}) -> {ok, #sslsocket{}} | {error, reason()}.
--spec handshake(#sslsocket{} | port(), timeout()| [ssl_option()
- | transport_option()]) ->
- {ok, #sslsocket{}} | {error, reason()}.
-
--spec handshake(#sslsocket{} | port(), [ssl_option()] | [ssl_option()| transport_option()], timeout()) ->
- {ok, #sslsocket{}} | {error, reason()}.
%%
%% Description: Performs accept on an ssl listen socket. e.i. performs
%% ssl handshake.
%%--------------------------------------------------------------------
%% Performs the SSL/TLS/DTLS server-side handshake.
+-spec handshake(#sslsocket{}) -> {ok, #sslsocket{}} | {error, timeout | closed | {options, any()} | error_alert()}.
+
handshake(ListenSocket) ->
handshake(ListenSocket, infinity).
+-spec handshake(#sslsocket{} | port(), timeout()| [tls_server_option()]) ->
+ {ok, #sslsocket{}} | {error, timeout | closed | {options, any()} | error_alert()}.
handshake(#sslsocket{} = Socket, Timeout) when (is_integer(Timeout) andalso Timeout >= 0) or
(Timeout == infinity) ->
ssl_connection:handshake(Socket, Timeout);
@@ -229,6 +547,8 @@ handshake(#sslsocket{} = Socket, Timeout) when (is_integer(Timeout) andalso Tim
handshake(ListenSocket, SslOptions) when is_port(ListenSocket) ->
handshake(ListenSocket, SslOptions, infinity).
+-spec handshake(#sslsocket{} | port(), [tls_server_option()], timeout()) ->
+ {ok, #sslsocket{}} | {error, timeout | closed | {options, any()} | error_alert()}.
handshake(#sslsocket{} = Socket, [], Timeout) when (is_integer(Timeout) andalso Timeout >= 0) or
(Timeout == infinity)->
handshake(Socket, Timeout);
@@ -271,7 +591,7 @@ handshake(Socket, SslOptions, Timeout) when is_port(Socket),
%%--------------------------------------------------------------------
--spec handshake_continue(#sslsocket{}, [ssl_option()]) ->
+-spec handshake_continue(#sslsocket{}, [tls_client_option() | tls_server_option()]) ->
{ok, #sslsocket{}} | {error, reason()}.
%%
%%
@@ -280,7 +600,7 @@ handshake(Socket, SslOptions, Timeout) when is_port(Socket),
handshake_continue(Socket, SSLOptions) ->
handshake_continue(Socket, SSLOptions, infinity).
%%--------------------------------------------------------------------
--spec handshake_continue(#sslsocket{}, [ssl_option()], timeout()) ->
+-spec handshake_continue(#sslsocket{}, [tls_client_option() | tls_server_option()], timeout()) ->
{ok, #sslsocket{}} | {error, reason()}.
%%
%%
@@ -332,7 +652,7 @@ close(#sslsocket{pid = {ListenSocket, #config{transport_info={Transport,_, _, _}
send(#sslsocket{pid = [Pid]}, Data) when is_pid(Pid) ->
ssl_connection:send(Pid, Data);
send(#sslsocket{pid = [_, Pid]}, Data) when is_pid(Pid) ->
- tls_sender:send_data(Pid, erlang:iolist_to_binary(Data));
+ tls_sender:send_data(Pid, erlang:iolist_to_iovec(Data));
send(#sslsocket{pid = {_, #config{transport_info={_, udp, _, _}}}}, _) ->
{error,enotconn}; %% Emulate connection behaviour
send(#sslsocket{pid = {dtls,_}}, _) ->
@@ -341,13 +661,14 @@ send(#sslsocket{pid = {ListenSocket, #config{transport_info={Transport, _, _, _}
Transport:send(ListenSocket, Data). %% {error,enotconn}
%%--------------------------------------------------------------------
--spec recv(#sslsocket{}, integer()) -> {ok, binary()| list()} | {error, reason()}.
--spec recv(#sslsocket{}, integer(), timeout()) -> {ok, binary()| list()} | {error, reason()}.
%%
%% Description: Receives data when active = false
%%--------------------------------------------------------------------
+-spec recv(#sslsocket{}, integer()) -> {ok, binary()| list()} | {error, reason()}.
recv(Socket, Length) ->
recv(Socket, Length, infinity).
+
+-spec recv(#sslsocket{}, integer(), timeout()) -> {ok, binary()| list()} | {error, reason()}.
recv(#sslsocket{pid = [Pid|_]}, Length, Timeout) when is_pid(Pid),
(is_integer(Timeout) andalso Timeout >= 0) or (Timeout == infinity)->
ssl_connection:recv(Pid, Length, Timeout);
@@ -450,13 +771,13 @@ negotiated_protocol(#sslsocket{pid = [Pid|_]}) when is_pid(Pid) ->
ssl_connection:negotiated_protocol(Pid).
%%--------------------------------------------------------------------
--spec cipher_suites() -> [ssl_cipher_format:old_erl_cipher_suite()] | [string()].
+-spec cipher_suites() -> [old_cipher_suite()] | [string()].
%%--------------------------------------------------------------------
cipher_suites() ->
cipher_suites(erlang).
%%--------------------------------------------------------------------
-spec cipher_suites(erlang | openssl | all) ->
- [ssl_cipher_format:old_erl_cipher_suite() | string()].
+ [old_cipher_suite() | string()].
%% Description: Returns all supported cipher suites.
%%--------------------------------------------------------------------
cipher_suites(erlang) ->
@@ -470,9 +791,9 @@ cipher_suites(all) ->
[ssl_cipher_format:erl_suite_definition(Suite) || Suite <- available_suites(all)].
%%--------------------------------------------------------------------
--spec cipher_suites(default | all | anonymous, tls_record:tls_version() | dtls_record:dtls_version() |
+-spec cipher_suites(default | all | anonymous, ssl_record:ssl_version() |
tls_record:tls_atom_version() | dtls_record:dtls_atom_version()) ->
- [ssl_cipher_format:erl_cipher_suite()].
+ [erl_cipher_suite()].
%% Description: Returns all default and all supported cipher suites for a
%% TLS/DTLS version
%%--------------------------------------------------------------------
@@ -488,9 +809,10 @@ cipher_suites(Base, Version) ->
[ssl_cipher_format:suite_definition(Suite) || Suite <- supported_suites(Base, Version)].
%%--------------------------------------------------------------------
--spec filter_cipher_suites([ssl_cipher_format:erl_cipher_suite()] | [ssl_cipher_format:cipher_suite()],
+-spec filter_cipher_suites([erl_cipher_suite()] | [ssl_cipher_format:cipher_suite()] ,
[{key_exchange | cipher | mac | prf, fun()}] | []) ->
- [ssl_cipher_format:erl_cipher_suite() ] | [ssl_cipher_format:cipher_suite()].
+ [erl_cipher_suite()] | [ssl_cipher_format: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.
@@ -507,10 +829,10 @@ filter_cipher_suites(Suites, Filters0) ->
prf_filters => add_filter(proplists:get_value(prf, Filters0), PrfF)},
ssl_cipher:filter_suites(Suites, Filters).
%%--------------------------------------------------------------------
--spec prepend_cipher_suites([ssl_cipher_format:erl_cipher_suite()] |
+-spec prepend_cipher_suites([erl_cipher_suite()] |
[{key_exchange | cipher | mac | prf, fun()}],
- [ssl_cipher_format:erl_cipher_suite()]) ->
- [ssl_cipher_format:erl_cipher_suite()].
+ [erl_cipher_suite()]) ->
+ [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
@@ -525,10 +847,10 @@ prepend_cipher_suites(Filters, Suites) ->
Preferred = filter_cipher_suites(Suites, Filters),
Preferred ++ (Suites -- Preferred).
%%--------------------------------------------------------------------
--spec append_cipher_suites(Deferred :: [ssl_cipher_format:erl_cipher_suite()] |
+-spec append_cipher_suites(Deferred :: [erl_cipher_suite()] |
[{key_exchange | cipher | mac | prf, fun()}],
- [ssl_cipher_format:erl_cipher_suite()]) ->
- [ssl_cipher_format:erl_cipher_suite()].
+ [erl_cipher_suite()]) ->
+ [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.
@@ -550,8 +872,8 @@ eccs() ->
eccs_filter_supported(Curves).
%%--------------------------------------------------------------------
--spec eccs(tls_record:tls_version() | tls_record:tls_atom_version() |
- dtls_record:dtls_version() | dtls_record:dtls_atom_version()) ->
+-spec eccs(tls_record:tls_atom_version() |
+ ssl_record:ssl_version() | dtls_record:dtls_atom_version()) ->
tls_v1:curves().
%% Description: returns the curves supported for a given version of
%% ssl/tls.
@@ -747,7 +1069,7 @@ versions() ->
SupportedDTLSVsns = [dtls_record:protocol_version(Vsn) || Vsn <- DTLSVsns],
AvailableTLSVsns = ?ALL_AVAILABLE_VERSIONS,
AvailableDTLSVsns = ?ALL_AVAILABLE_DATAGRAM_VERSIONS,
- [{ssl_app, ?VSN}, {supported, SupportedTLSVsns},
+ [{ssl_app, "9.2"}, {supported, SupportedTLSVsns},
{supported_dtls, SupportedDTLSVsns},
{available, AvailableTLSVsns},
{available_dtls, AvailableDTLSVsns}].
@@ -807,8 +1129,8 @@ format_error(Reason) when is_list(Reason) ->
Reason;
format_error(closed) ->
"TLS connection is closed";
-format_error({tls_alert, Description}) ->
- "TLS Alert: " ++ Description;
+format_error({tls_alert, {_, Description}}) ->
+ Description;
format_error({options,{FileType, File, Reason}}) when FileType == cacertfile;
FileType == certfile;
FileType == keyfile;
@@ -837,7 +1159,7 @@ tls_version({254, _} = Version) ->
%%--------------------------------------------------------------------
--spec suite_to_str(ssl_cipher_format:erl_cipher_suite()) -> string().
+-spec suite_to_str(erl_cipher_suite()) -> string().
%%
%% Description: Return the string representation of a cipher suite.
%%--------------------------------------------------------------------
@@ -948,7 +1270,6 @@ handle_options(Opts0, Role, Host) ->
handle_verify_options(Opts, CaCerts),
CertFile = handle_option(certfile, Opts, <<>>),
- RecordCb = record_cb(Opts),
[HighestVersion|_] = Versions =
case handle_option(versions, Opts, []) of
@@ -1075,6 +1396,7 @@ handle_options(Opts0, Role, Host) ->
fallback, signature_algs, signature_algs_cert, eccs, honor_ecc_order,
beast_mitigation, max_handshake_size, handshake, customize_hostname_check,
supported_groups],
+
SockOpts = lists:foldl(fun(Key, PropList) ->
proplists:delete(Key, PropList)
end, Opts, SslOptions),
diff --git a/lib/ssl/src/ssl_alert.erl b/lib/ssl/src/ssl_alert.erl
index ed8156e0be..e17476f33b 100644
--- a/lib/ssl/src/ssl_alert.erl
+++ b/lib/ssl/src/ssl_alert.erl
@@ -48,8 +48,8 @@ decode(Bin) ->
decode(Bin, [], 0).
%%--------------------------------------------------------------------
--spec reason_code(#alert{}, client | server) ->
- closed | {tls_alert, unicode:chardata()}.
+%% -spec reason_code(#alert{}, client | server) ->
+%% {tls_alert, unicode:chardata()} | closed.
%-spec reason_code(#alert{}, client | server) -> closed | {essl, string()}.
%%
%% Description: Returns the error reason that will be returned to the
@@ -58,8 +58,10 @@ decode(Bin) ->
reason_code(#alert{description = ?CLOSE_NOTIFY}, _) ->
closed;
-reason_code(#alert{description = Description}, _) ->
- {tls_alert, string:casefold(description_txt(Description))}.
+reason_code(#alert{description = Description, role = Role} = Alert, Role) ->
+ {tls_alert, {description_atom(Description), own_alert_txt(Alert)}};
+reason_code(#alert{description = Description} = Alert, Role) ->
+ {tls_alert, {description_atom(Description), alert_txt(Alert#alert{role = Role})}}.
%%--------------------------------------------------------------------
-spec own_alert_txt(#alert{}) -> string().
@@ -185,3 +187,70 @@ description_txt(?NO_APPLICATION_PROTOCOL) ->
"No application protocol";
description_txt(Enum) ->
lists:flatten(io_lib:format("unsupported/unknown alert: ~p", [Enum])).
+
+description_atom(?CLOSE_NOTIFY) ->
+ close_notify;
+description_atom(?UNEXPECTED_MESSAGE) ->
+ unexpected_message;
+description_atom(?BAD_RECORD_MAC) ->
+ bad_record_mac;
+description_atom(?DECRYPTION_FAILED_RESERVED) ->
+ decryption_failed_reserved;
+description_atom(?RECORD_OVERFLOW) ->
+ record_overflow;
+description_atom(?DECOMPRESSION_FAILURE) ->
+ decompression_failure;
+description_atom(?HANDSHAKE_FAILURE) ->
+ handshake_failure;
+description_atom(?NO_CERTIFICATE_RESERVED) ->
+ no_certificate_reserved;
+description_atom(?BAD_CERTIFICATE) ->
+ bad_certificate;
+description_atom(?UNSUPPORTED_CERTIFICATE) ->
+ unsupported_certificate;
+description_atom(?CERTIFICATE_REVOKED) ->
+ certificate_revoked;
+description_atom(?CERTIFICATE_EXPIRED) ->
+ certificate_expired;
+description_atom(?CERTIFICATE_UNKNOWN) ->
+ certificate_unknown;
+description_atom(?ILLEGAL_PARAMETER) ->
+ illegal_parameter;
+description_atom(?UNKNOWN_CA) ->
+ unknown_ca;
+description_atom(?ACCESS_DENIED) ->
+ access_denied;
+description_atom(?DECODE_ERROR) ->
+ decode_error;
+description_atom(?DECRYPT_ERROR) ->
+ decrypt_error;
+description_atom(?EXPORT_RESTRICTION) ->
+ export_restriction;
+description_atom(?PROTOCOL_VERSION) ->
+ protocol_version;
+description_atom(?INSUFFICIENT_SECURITY) ->
+ insufficient_security;
+description_atom(?INTERNAL_ERROR) ->
+ internal_error;
+description_atom(?USER_CANCELED) ->
+ user_canceled;
+description_atom(?NO_RENEGOTIATION) ->
+ no_renegotiation;
+description_atom(?UNSUPPORTED_EXTENSION) ->
+ unsupported_extension;
+description_atom(?CERTIFICATE_UNOBTAINABLE) ->
+ certificate_unobtainable;
+description_atom(?UNRECOGNISED_NAME) ->
+ unrecognised_name;
+description_atom(?BAD_CERTIFICATE_STATUS_RESPONSE) ->
+ bad_certificate_status_response;
+description_atom(?BAD_CERTIFICATE_HASH_VALUE) ->
+ bad_certificate_hash_value;
+description_atom(?UNKNOWN_PSK_IDENTITY) ->
+ unknown_psk_identity;
+description_atom(?INAPPROPRIATE_FALLBACK) ->
+ inappropriate_fallback;
+description_atom(?NO_APPLICATION_PROTOCOL) ->
+ no_application_protocol;
+description_atom(_) ->
+ 'unsupported/unkonwn_alert'.
diff --git a/lib/ssl/src/ssl_api.hrl b/lib/ssl/src/ssl_api.hrl
index 7b7b1cbcd9..f4594912bd 100644
--- a/lib/ssl/src/ssl_api.hrl
+++ b/lib/ssl/src/ssl_api.hrl
@@ -21,56 +21,7 @@
-ifndef(ssl_api).
-define(ssl_api, true).
--include("ssl_cipher.hrl").
-
-%% Visible in API
--export_type([connect_option/0, listen_option/0, ssl_option/0, transport_option/0,
- prf_random/0, sslsocket/0]).
-
-
%% Looks like it does for backwards compatibility reasons
-record(sslsocket, {fd = nil, pid = nil}).
-
--type sslsocket() :: #sslsocket{}.
--type connect_option() :: socket_connect_option() | ssl_option() | transport_option().
--type socket_connect_option() :: gen_tcp:connect_option().
--type listen_option() :: socket_listen_option() | ssl_option() | transport_option().
--type socket_listen_option() :: gen_tcp:listen_option().
-
--type ssl_option() :: {versions, ssl_record:ssl_atom_version()} |
- {verify, verify_type()} |
- {verify_fun, {fun(), InitialUserState::term()}} |
- {fail_if_no_peer_cert, boolean()} | {depth, integer()} |
- {cert, Der::binary()} | {certfile, path()} |
- {key, {private_key_type(), Der::binary()}} |
- {keyfile, path()} | {password, string()} | {cacerts, [Der::binary()]} |
- {cacertfile, path()} | {dh, Der::binary()} | {dhfile, path()} |
- {user_lookup_fun, {fun(), InitialUserState::term()}} |
- {psk_identity, string()} |
- {srp_identity, {string(), string()}} |
- {ciphers, ciphers()} | {ssl_imp, ssl_imp()} | {reuse_sessions, boolean()} |
- {reuse_session, fun()} | {hibernate_after, integer()|undefined} |
- {alpn_advertised_protocols, [binary()]} |
- {alpn_preferred_protocols, [binary()]} |
- {next_protocols_advertised, list(binary())} |
- {client_preferred_next_protocols, binary(), client | server, list(binary())}.
-
--type verify_type() :: verify_none | verify_peer.
--type path() :: string().
--type ciphers() :: [ssl_cipher_format:erl_cipher_suite()] |
- string(). % (according to old API)
--type ssl_imp() :: new | old.
-
--type transport_option() :: {cb_info, {CallbackModule::atom(), DataTag::atom(),
- ClosedTag::atom(), ErrTag::atom()}}.
--type prf_random() :: client_random | server_random.
-
--type private_key_type() :: rsa | %% Backwards compatibility
- dsa | %% Backwards compatibility
- 'RSAPrivateKey' |
- 'DSAPrivateKey' |
- 'ECPrivateKey' |
- 'PrivateKeyInfo'.
-
-endif. % -ifdef(ssl_api).
diff --git a/lib/ssl/src/ssl_cipher.erl b/lib/ssl/src/ssl_cipher.erl
index d08b2cc7ad..6e751f9ceb 100644
--- a/lib/ssl/src/ssl_cipher.erl
+++ b/lib/ssl/src/ssl_cipher.erl
@@ -1,7 +1,7 @@
%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2007-2018. All Rights Reserved.
+%% Copyright Ericsson AB 2007-2019. 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.
@@ -42,7 +42,7 @@
rc4_suites/1, des_suites/1, rsa_suites/1,
filter/3, 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,
+ random_bytes/1, calc_mac_hash/4, calc_mac_hash/6,
is_stream_ciphersuite/1, signature_scheme/1,
scheme_to_components/1, hash_size/1, effective_key_bits/1,
key_material/1]).
@@ -112,7 +112,8 @@ cipher_init(?AES_GCM, IV, Key) ->
cipher_init(?CHACHA20_POLY1305, IV, Key) ->
#cipher_state{iv = IV, key = Key, tag_len = 16};
cipher_init(_BCA, IV, Key) ->
- #cipher_state{iv = IV, key = Key}.
+ %% Initialize random IV cache, not used for aead ciphers
+ #cipher_state{iv = IV, key = Key, state = <<>>}.
nonce_seed(Seed, CipherState) ->
CipherState#cipher_state{nonce = Seed}.
@@ -127,12 +128,11 @@ nonce_seed(Seed, CipherState) ->
%% data is calculated and the data plus the HMAC is ecncrypted.
%%-------------------------------------------------------------------
cipher(?NULL, CipherState, <<>>, Fragment, _Version) ->
- GenStreamCipherList = [Fragment, <<>>],
- {GenStreamCipherList, CipherState};
+ {iolist_to_binary(Fragment), CipherState};
cipher(?RC4, CipherState = #cipher_state{state = State0}, Mac, Fragment, _Version) ->
GenStreamCipherList = [Fragment, Mac],
{State1, T} = crypto:stream_encrypt(State0, GenStreamCipherList),
- {T, CipherState#cipher_state{state = State1}};
+ {iolist_to_binary(T), CipherState#cipher_state{state = State1}};
cipher(?DES, CipherState, Mac, Fragment, Version) ->
block_cipher(fun(Key, IV, T) ->
crypto:block_encrypt(des_cbc, Key, IV, T)
@@ -161,8 +161,7 @@ aead_type(?CHACHA20_POLY1305) ->
build_cipher_block(BlockSz, Mac, Fragment) ->
TotSz = byte_size(Mac) + erlang:iolist_size(Fragment) + 1,
- {PaddingLength, Padding} = get_padding(TotSz, BlockSz),
- [Fragment, Mac, PaddingLength, Padding].
+ [Fragment, Mac, padding_with_len(TotSz, BlockSz)].
block_cipher(Fun, BlockSz, #cipher_state{key=Key, iv=IV} = CS0,
Mac, Fragment, {3, N})
@@ -172,14 +171,21 @@ block_cipher(Fun, BlockSz, #cipher_state{key=Key, iv=IV} = CS0,
NextIV = next_iv(T, IV),
{T, CS0#cipher_state{iv=NextIV}};
-block_cipher(Fun, BlockSz, #cipher_state{key=Key, iv=IV} = CS0,
+block_cipher(Fun, BlockSz, #cipher_state{key=Key, iv=IV, state = IV_Cache0} = CS0,
Mac, Fragment, {3, N})
when N == 2; N == 3; N == 4 ->
- NextIV = random_iv(IV),
+ IV_Size = byte_size(IV),
+ <<NextIV:IV_Size/binary, IV_Cache/binary>> =
+ case IV_Cache0 of
+ <<>> ->
+ random_bytes(IV_Size bsl 5); % 32 IVs
+ _ ->
+ IV_Cache0
+ end,
L0 = build_cipher_block(BlockSz, Mac, Fragment),
L = [NextIV|L0],
T = Fun(Key, IV, L),
- {T, CS0#cipher_state{iv=NextIV}}.
+ {T, CS0#cipher_state{iv=NextIV, state = IV_Cache}}.
%%--------------------------------------------------------------------
-spec decipher(cipher_enum(), integer(), #cipher_state{}, binary(),
@@ -501,8 +507,8 @@ filter(DerCert, Ciphers0, Version) ->
filter_suites_signature(Sign, Ciphers, Version).
%%--------------------------------------------------------------------
--spec filter_suites([ssl_cipher_format:erl_cipher_suite()] | [ssl_cipher_format:cipher_suite()], map()) ->
- [ssl_cipher_format:erl_cipher_suite()] | [ssl_cipher_format:cipher_suite()].
+-spec filter_suites([ssl:erl_cipher_suite()] | [ssl_cipher_format:cipher_suite()], map()) ->
+ [ssl:erl_cipher_suite()] | [ssl_cipher_format:cipher_suite()].
%%
%% Description: Filter suites using supplied filter funs
%%-------------------------------------------------------------------
@@ -528,8 +534,8 @@ filter_suite(Suite, Filters) ->
filter_suite(ssl_cipher_format:suite_definition(Suite), Filters).
%%--------------------------------------------------------------------
--spec filter_suites([ssl_cipher_format:erl_cipher_suite()] | [ssl_cipher_format:cipher_suite()]) ->
- [ssl_cipher_format:erl_cipher_suite()] | [ssl_cipher_format:cipher_suite()].
+-spec filter_suites([ssl:erl_cipher_suite()] | [ssl_cipher_format:cipher_suite()]) ->
+ [ssl:erl_cipher_suite()] | [ssl_cipher_format:cipher_suite()].
%%
%% Description: Filter suites for algorithms supported by crypto.
%%-------------------------------------------------------------------
@@ -654,12 +660,13 @@ random_bytes(N) ->
calc_mac_hash(Type, Version,
PlainFragment, #{sequence_number := SeqNo,
mac_secret := MacSecret,
- security_parameters:=
- SecPars}) ->
+ security_parameters :=
+ #security_parameters{mac_algorithm = MacAlgorithm}}) ->
+ calc_mac_hash(Type, Version, PlainFragment, MacAlgorithm, MacSecret, SeqNo).
+%%
+calc_mac_hash(Type, Version, PlainFragment, MacAlgorithm, MacSecret, SeqNo) ->
Length = erlang:iolist_size(PlainFragment),
- mac_hash(Version, SecPars#security_parameters.mac_algorithm,
- MacSecret, SeqNo, Type,
- Length, PlainFragment).
+ mac_hash(Version, MacAlgorithm, MacSecret, SeqNo, Type, Length, PlainFragment).
is_stream_ciphersuite(#{cipher := rc4_128}) ->
true;
@@ -765,7 +772,6 @@ expanded_key_material(Cipher) when Cipher == aes_128_cbc;
Cipher == chacha20_poly1305 ->
unknown.
-
effective_key_bits(null) ->
0;
effective_key_bits(des_cbc) ->
@@ -785,18 +791,15 @@ iv_size(Cipher) when Cipher == null;
Cipher == rc4_128;
Cipher == chacha20_poly1305->
0;
-
iv_size(Cipher) when Cipher == aes_128_gcm;
Cipher == aes_256_gcm ->
4;
-
iv_size(Cipher) ->
block_size(Cipher).
block_size(Cipher) when Cipher == des_cbc;
Cipher == '3des_ede_cbc' ->
8;
-
block_size(Cipher) when Cipher == aes_128_cbc;
Cipher == aes_256_cbc;
Cipher == aes_128_gcm;
@@ -963,21 +966,51 @@ is_correct_padding(GenBlockCipher, {3, 1}, false) ->
%% Padding must be checked in TLS 1.1 and after
is_correct_padding(#generic_block_cipher{padding_length = Len,
padding = Padding}, _, _) ->
- Len == byte_size(Padding) andalso
- binary:copy(?byte(Len), Len) == Padding.
-
-get_padding(Length, BlockSize) ->
- get_padding_aux(BlockSize, Length rem BlockSize).
-
-get_padding_aux(_, 0) ->
- {0, <<>>};
-get_padding_aux(BlockSize, PadLength) ->
- N = BlockSize - PadLength,
- {N, binary:copy(?byte(N), N)}.
+ (Len == byte_size(Padding)) andalso (padding(Len) == Padding).
+
+padding(PadLen) ->
+ case PadLen of
+ 0 -> <<>>;
+ 1 -> <<1>>;
+ 2 -> <<2,2>>;
+ 3 -> <<3,3,3>>;
+ 4 -> <<4,4,4,4>>;
+ 5 -> <<5,5,5,5,5>>;
+ 6 -> <<6,6,6,6,6,6>>;
+ 7 -> <<7,7,7,7,7,7,7>>;
+ 8 -> <<8,8,8,8,8,8,8,8>>;
+ 9 -> <<9,9,9,9,9,9,9,9,9>>;
+ 10 -> <<10,10,10,10,10,10,10,10,10,10>>;
+ 11 -> <<11,11,11,11,11,11,11,11,11,11,11>>;
+ 12 -> <<12,12,12,12,12,12,12,12,12,12,12,12>>;
+ 13 -> <<13,13,13,13,13,13,13,13,13,13,13,13,13>>;
+ 14 -> <<14,14,14,14,14,14,14,14,14,14,14,14,14,14>>;
+ 15 -> <<15,15,15,15,15,15,15,15,15,15,15,15,15,15,15>>;
+ _ ->
+ binary:copy(<<PadLen>>, PadLen)
+ end.
-random_iv(IV) ->
- IVSz = byte_size(IV),
- random_bytes(IVSz).
+padding_with_len(TextLen, BlockSize) ->
+ case BlockSize - (TextLen rem BlockSize) of
+ 0 -> <<0>>;
+ 1 -> <<1,1>>;
+ 2 -> <<2,2,2>>;
+ 3 -> <<3,3,3,3>>;
+ 4 -> <<4,4,4,4,4>>;
+ 5 -> <<5,5,5,5,5,5>>;
+ 6 -> <<6,6,6,6,6,6,6>>;
+ 7 -> <<7,7,7,7,7,7,7,7>>;
+ 8 -> <<8,8,8,8,8,8,8,8,8>>;
+ 9 -> <<9,9,9,9,9,9,9,9,9,9>>;
+ 10 -> <<10,10,10,10,10,10,10,10,10,10,10>>;
+ 11 -> <<11,11,11,11,11,11,11,11,11,11,11,11>>;
+ 12 -> <<12,12,12,12,12,12,12,12,12,12,12,12,12>>;
+ 13 -> <<13,13,13,13,13,13,13,13,13,13,13,13,13,13>>;
+ 14 -> <<14,14,14,14,14,14,14,14,14,14,14,14,14,14,14>>;
+ 15 -> <<15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15>>;
+ PadLen ->
+ binary:copy(<<PadLen>>, PadLen + 1)
+ end.
next_iv(Bin, IV) ->
BinSz = byte_size(Bin),
diff --git a/lib/ssl/src/ssl_cipher.hrl b/lib/ssl/src/ssl_cipher.hrl
index 5891f3a7cc..00822ad9de 100644
--- a/lib/ssl/src/ssl_cipher.hrl
+++ b/lib/ssl/src/ssl_cipher.hrl
@@ -47,6 +47,7 @@
-record(cipher_state, {
iv,
key,
+ finished_key,
state,
nonce,
tag_len
diff --git a/lib/ssl/src/ssl_cipher_format.erl b/lib/ssl/src/ssl_cipher_format.erl
index 6e480eef45..b592295d56 100644
--- a/lib/ssl/src/ssl_cipher_format.erl
+++ b/lib/ssl/src/ssl_cipher_format.erl
@@ -25,33 +25,25 @@
%%----------------------------------------------------------------------
-module(ssl_cipher_format).
+-include("ssl_api.hrl").
-include("ssl_cipher.hrl").
-include("ssl_internal.hrl").
-include_lib("public_key/include/public_key.hrl").
--export_type([cipher_suite/0,
- erl_cipher_suite/0, old_erl_cipher_suite/0, openssl_cipher_suite/0,
- hash/0, key_algo/0, sign_algo/0]).
+-export_type([old_erl_cipher_suite/0, openssl_cipher_suite/0, cipher_suite/0]).
--type cipher() :: null |rc4_128 | des_cbc | '3des_ede_cbc' | aes_128_cbc | aes_256_cbc | aes_128_gcm | aes_256_gcm | chacha20_poly1305.
--type hash() :: null | md5 | sha | sha224 | sha256 | sha384 | sha512.
--type sign_algo() :: rsa | dsa | ecdsa.
--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 |
- any. %% TLS 1.3
--type erl_cipher_suite() :: #{key_exchange := key_algo(),
- cipher := cipher(),
- mac := hash() | aead,
- prf := hash() | default_prf %% Old cipher suites, version dependent
+-type internal_cipher() :: null | ssl:cipher().
+-type internal_hash() :: null | ssl:hash().
+-type internal_kex_algo() :: null | ssl:kex_algo().
+-type internal_erl_cipher_suite() :: #{key_exchange := internal_kex_algo(),
+ cipher := internal_cipher(),
+ mac := internal_hash() | aead,
+ prf := internal_hash() | default_prf %% Old cipher suites, version dependent
}.
--type old_erl_cipher_suite() :: {key_algo(), cipher(), hash()} % Pre TLS 1.2
+-type old_erl_cipher_suite() :: {ssl:kex_algo(), internal_cipher(), internal_hash()} % Pre TLS 1.2
%% TLS 1.2, internally PRE TLS 1.2 will use default_prf
- | {key_algo(), cipher(), hash(), hash() | default_prf}.
+ | {ssl:kex_algo(), internal_cipher(), internal_hash(),
+ internal_hash() | default_prf}.
-type cipher_suite() :: binary().
-type openssl_cipher_suite() :: string().
@@ -60,7 +52,7 @@
openssl_suite/1, openssl_suite_name/1]).
%%--------------------------------------------------------------------
--spec suite_to_str(erl_cipher_suite()) -> string().
+-spec suite_to_str(internal_erl_cipher_suite()) -> string().
%%
%% Description: Return the string representation of a cipher suite.
%%--------------------------------------------------------------------
@@ -90,7 +82,7 @@ suite_to_str(#{key_exchange := Kex,
"_" ++ string:to_upper(atom_to_list(Mac)).
%%--------------------------------------------------------------------
--spec suite_definition(cipher_suite()) -> erl_cipher_suite().
+-spec suite_definition(cipher_suite()) -> internal_erl_cipher_suite().
%%
%% Description: Return erlang cipher suite definition.
%% Note: Currently not supported suites are commented away.
@@ -845,7 +837,7 @@ suite_definition(?TLS_CHACHA20_POLY1305_SHA256) ->
%%--------------------------------------------------------------------
--spec erl_suite_definition(cipher_suite() | erl_cipher_suite()) -> old_erl_cipher_suite().
+-spec erl_suite_definition(cipher_suite() | internal_erl_cipher_suite()) -> old_erl_cipher_suite().
%%
%% Description: Return erlang cipher suite definition. Filters last value
%% for now (compatibility reasons).
@@ -862,7 +854,7 @@ erl_suite_definition(#{key_exchange := KeyExchange, cipher := Cipher,
end.
%%--------------------------------------------------------------------
--spec suite(erl_cipher_suite()) -> cipher_suite().
+-spec suite(internal_erl_cipher_suite()) -> cipher_suite().
%%
%% Description: Return TLS cipher suite definition.
%%--------------------------------------------------------------------
@@ -1663,7 +1655,7 @@ openssl_suite("TLS_CHACHA20_POLY1305_SHA256") ->
%%--------------------------------------------------------------------
--spec openssl_suite_name(cipher_suite()) -> openssl_cipher_suite() | erl_cipher_suite().
+-spec openssl_suite_name(cipher_suite()) -> openssl_cipher_suite() | internal_erl_cipher_suite().
%%
%% Description: Return openssl cipher suite name if possible
%%-------------------------------------------------------------------
diff --git a/lib/ssl/src/ssl_connection.erl b/lib/ssl/src/ssl_connection.erl
index af18ceb322..f194610d72 100644
--- a/lib/ssl/src/ssl_connection.erl
+++ b/lib/ssl/src/ssl_connection.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2013-2018. All Rights Reserved.
+%% Copyright Ericsson AB 2013-2019. 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,9 +39,9 @@
%% Setup
--export([connect/8, handshake/7, handshake/2, handshake/3,
+-export([connect/8, handshake/7, handshake/2, handshake/3, handle_common_event/5,
handshake_continue/3, handshake_cancel/1,
- socket_control/4, socket_control/5, start_or_recv_cancel_timer/2]).
+ socket_control/4, socket_control/5]).
%% User Events
-export([send/2, recv/3, close/2, shutdown/2,
@@ -71,14 +71,14 @@
-export([terminate/3, format_status/2]).
%% Erlang Distribution export
--export([get_sslsocket/1, dist_handshake_complete/2]).
+-export([dist_handshake_complete/2]).
%%====================================================================
%% Setup
%%====================================================================
%%--------------------------------------------------------------------
-spec connect(tls_connection | dtls_connection,
- host(), inet:port_number(),
+ ssl:host(), inet:port_number(),
port() | {tuple(), port()}, %% TLS | DTLS
{#ssl_options{}, #socket_options{},
%% Tracker only needed on server side
@@ -144,7 +144,7 @@ handshake(#sslsocket{pid = [Pid|_]} = Socket, SslOptions, Timeout) ->
end.
%%--------------------------------------------------------------------
--spec handshake_continue(#sslsocket{}, [ssl_option()],
+-spec handshake_continue(#sslsocket{}, [ssl:tls_server_option()],
timeout()) -> {ok, #sslsocket{}}| {error, reason()}.
%%
%% Description: Continues handshake with new options
@@ -183,27 +183,23 @@ socket_control(Connection, Socket, Pid, Transport) ->
%%--------------------------------------------------------------------
socket_control(Connection, Socket, Pids, Transport, udp_listener) ->
%% dtls listener process must have the socket control
- {ok, Connection:socket(Pids, Transport, Socket, Connection, undefined)};
+ {ok, Connection:socket(Pids, Transport, Socket, undefined)};
socket_control(tls_connection = Connection, Socket, [Pid|_] = Pids, Transport, ListenTracker) ->
case Transport:controlling_process(Socket, Pid) of
ok ->
- {ok, Connection:socket(Pids, Transport, Socket, Connection, ListenTracker)};
+ {ok, Connection:socket(Pids, Transport, Socket, ListenTracker)};
{error, Reason} ->
{error, Reason}
end;
socket_control(dtls_connection = Connection, {_, Socket}, [Pid|_] = Pids, Transport, ListenTracker) ->
case Transport:controlling_process(Socket, Pid) of
ok ->
- {ok, Connection:socket(Pids, Transport, Socket, Connection, ListenTracker)};
+ {ok, Connection:socket(Pids, Transport, Socket, ListenTracker)};
{error, Reason} ->
{error, Reason}
end.
-start_or_recv_cancel_timer(infinity, _RecvFrom) ->
- undefined;
-start_or_recv_cancel_timer(Timeout, RecvFrom) ->
- erlang:send_after(Timeout, self(), {cancel_start_or_recv, RecvFrom}).
%%====================================================================
%% User events
@@ -216,9 +212,9 @@ start_or_recv_cancel_timer(Timeout, RecvFrom) ->
%%--------------------------------------------------------------------
send(Pid, Data) ->
call(Pid, {application_data,
- %% iolist_to_binary should really
- %% be called iodata_to_binary()
- erlang:iolist_to_binary(Data)}).
+ %% iolist_to_iovec should really
+ %% be called iodata_to_iovec()
+ erlang:iolist_to_iovec(Data)}).
%%--------------------------------------------------------------------
-spec recv(pid(), integer(), timeout()) ->
@@ -316,9 +312,6 @@ renegotiation(ConnectionPid) ->
internal_renegotiation(ConnectionPid, #{current_write := WriteState}) ->
gen_statem:cast(ConnectionPid, {internal_renegotiate, WriteState}).
-get_sslsocket(ConnectionPid) ->
- call(ConnectionPid, get_sslsocket).
-
dist_handshake_complete(ConnectionPid, DHandle) ->
gen_statem:cast(ConnectionPid, {dist_handshake_complete, DHandle}).
@@ -369,8 +362,8 @@ handle_normal_shutdown(Alert, StateName, #state{static_env = #static_env{role =
transport_cb = Transport,
protocol_cb = Connection,
tracker = Tracker},
- socket_options = Opts,
- user_application = {_Mon, Pid},
+ connection_env = #connection_env{user_application = {_Mon, Pid}},
+ socket_options = Opts,
start_or_recv_from = RecvFrom} = State) ->
Pids = Connection:pids(State),
alert_user(Pids, Transport, Tracker, Socket, StateName, Opts, Pid, RecvFrom, Alert, Role, Connection).
@@ -383,9 +376,10 @@ handle_alert(#alert{level = ?FATAL} = Alert, StateName,
tracker = Tracker,
transport_cb = Transport,
protocol_cb = Connection},
+ connection_env = #connection_env{user_application = {_Mon, Pid}},
ssl_options = SslOpts,
start_or_recv_from = From,
- session = Session, user_application = {_Mon, Pid},
+ session = Session,
socket_options = Opts} = State) ->
invalidate_session(Role, Host, Port, Session),
log_alert(SslOpts#ssl_options.log_level, Role, Connection:protocol_name(),
@@ -449,106 +443,112 @@ handle_alert(#alert{level = ?WARNING} = Alert, StateName,
%%====================================================================
%% Data handling
%%====================================================================
-passive_receive(State0 = #state{user_data_buffer = Buffer}, StateName, Connection) ->
- case Buffer of
- <<>> ->
- {Record, State} = Connection:next_record(State0),
- Connection:next_event(StateName, Record, State);
+passive_receive(State0 = #state{user_data_buffer = {_,BufferSize,_}}, StateName, Connection, StartTimerAction) ->
+ case BufferSize of
+ 0 ->
+ Connection:next_event(StateName, no_record, State0, StartTimerAction);
_ ->
case read_application_data(<<>>, State0) of
{stop, _, _} = ShutdownError ->
ShutdownError;
{Record, State} ->
- Connection:next_event(StateName, Record, State)
+ case State#state.start_or_recv_from of
+ undefined ->
+ %% Cancel recv timeout as data has been delivered
+ Connection:next_event(StateName, Record, State,
+ [{{timeout, recv}, infinity, timeout}]);
+ _ ->
+ Connection:next_event(StateName, Record, State, StartTimerAction)
+ end
end
end.
read_application_data(
Data,
#state{
- user_data_buffer = Buffer0,
- erl_dist_handle = DHandle} = State) ->
+ user_data_buffer = {Front0,BufferSize0,Rear0},
+ connection_env = #connection_env{erl_dist_handle = DHandle}} = State) ->
%%
- Buffer = bincat(Buffer0, Data),
+ Front = Front0,
+ BufferSize = BufferSize0 + byte_size(Data),
+ Rear = [Data|Rear0],
case DHandle of
undefined ->
- #state{
- socket_options = SocketOpts,
- bytes_to_read = BytesToRead,
- start_or_recv_from = RecvFrom,
- timer = Timer} = State,
- read_application_data(
- Buffer, State, SocketOpts, RecvFrom, Timer, BytesToRead);
+ read_application_data(State, Front, BufferSize, Rear);
_ ->
- try read_application_dist_data(Buffer, State, DHandle)
+ try read_application_dist_data(DHandle, Front, BufferSize, Rear) of
+ Buffer ->
+ {no_record, State#state{user_data_buffer = Buffer}}
catch error:_ ->
{stop,disconnect,
- State#state{
- user_data_buffer = Buffer,
- bytes_to_read = undefined}}
+ State#state{user_data_buffer = {Front,BufferSize,Rear}}}
end
end.
-read_application_dist_data(Buffer, State, DHandle) ->
- case Buffer of
- <<Size:32,Data:Size/binary>> ->
- erlang:dist_ctrl_put_data(DHandle, Data),
- {no_record,
- State#state{
- user_data_buffer = <<>>,
- bytes_to_read = undefined}};
- <<Size:32,Data:Size/binary,Rest/binary>> ->
- erlang:dist_ctrl_put_data(DHandle, Data),
- read_application_dist_data(Rest, State, DHandle);
- _ ->
- {no_record,
- State#state{
- user_data_buffer = Buffer,
- bytes_to_read = undefined}}
- end.
-read_application_data(
- Buffer0, State, SocketOpts0, RecvFrom, Timer, BytesToRead) ->
- %%
- case get_data(SocketOpts0, BytesToRead, Buffer0) of
- {ok, ClientData, Buffer} -> % Send data
- #state{
- static_env =
- #static_env{
- socket = Socket,
- protocol_cb = Connection,
- transport_cb = Transport,
- tracker = Tracker},
- user_application = {_Mon, Pid}} = State,
- SocketOpts =
- deliver_app_data(
- Connection:pids(State),
- Transport, Socket, SocketOpts0,
- ClientData, Pid, RecvFrom, Tracker, Connection),
- cancel_timer(Timer),
+read_application_data(#state{
+ socket_options = SocketOpts,
+ bytes_to_read = BytesToRead,
+ start_or_recv_from = RecvFrom} = State, Front, BufferSize, Rear) ->
+ read_application_data(State, Front, BufferSize, Rear, SocketOpts, RecvFrom, BytesToRead).
+
+%% Pick binary from queue front, if empty wait for more data
+read_application_data(State, [Bin|Front], BufferSize, Rear, SocketOpts, RecvFrom, BytesToRead) ->
+ read_application_data_bin(State, Front, BufferSize, Rear, SocketOpts, RecvFrom, BytesToRead, Bin);
+read_application_data(State, [] = Front, BufferSize, [] = Rear, SocketOpts, RecvFrom, BytesToRead) ->
+ 0 = BufferSize, % Assert
+ {no_record, State#state{socket_options = SocketOpts,
+ bytes_to_read = BytesToRead,
+ start_or_recv_from = RecvFrom,
+ user_data_buffer = {Front,BufferSize,Rear}}};
+read_application_data(State, [], BufferSize, Rear, SocketOpts, RecvFrom, BytesToRead) ->
+ [Bin|Front] = lists:reverse(Rear),
+ read_application_data_bin(State, Front, BufferSize, [], SocketOpts, RecvFrom, BytesToRead, Bin).
+
+read_application_data_bin(State, Front, BufferSize, Rear, SocketOpts, RecvFrom, BytesToRead, <<>>) ->
+ %% Done with this binary - get next
+ read_application_data(State, Front, BufferSize, Rear, SocketOpts, RecvFrom, BytesToRead);
+read_application_data_bin(State, Front0, BufferSize0, Rear0, SocketOpts0, RecvFrom, BytesToRead, Bin0) ->
+ %% Decode one packet from a binary
+ case get_data(SocketOpts0, BytesToRead, Bin0) of
+ {ok, Data, Bin} -> % Send data
+ BufferSize = BufferSize0 - (byte_size(Bin0) - byte_size(Bin)),
+ read_application_data_deliver(
+ State, [Bin|Front0], BufferSize, Rear0, SocketOpts0, RecvFrom, Data);
+ {more, undefined} ->
+ %% We need more data, do not know how much
if
- SocketOpts#socket_options.active =:= false;
- Buffer =:= <<>> ->
- %% Passive mode, wait for active once or recv
- %% Active and empty, get more data
- {no_record,
- State#state{
- user_data_buffer = Buffer,
- start_or_recv_from = undefined,
- timer = undefined,
- bytes_to_read = undefined,
- socket_options = SocketOpts
- }};
- true -> %% We have more data
- read_application_data(
- Buffer, State, SocketOpts,
- undefined, undefined, undefined)
+ byte_size(Bin0) < BufferSize0 ->
+ %% We have more data in the buffer besides the first binary - concatenate all and retry
+ Bin = iolist_to_binary([Bin0,Front0|lists:reverse(Rear0)]),
+ read_application_data_bin(
+ State, [], BufferSize0, [], SocketOpts0, RecvFrom, BytesToRead, Bin);
+ true ->
+ %% All data is in the first binary, no use to retry - wait for more
+ {no_record, State#state{socket_options = SocketOpts0,
+ bytes_to_read = BytesToRead,
+ start_or_recv_from = RecvFrom,
+ user_data_buffer = {[Bin0|Front0],BufferSize0,Rear0}}}
end;
- {more, Buffer} -> % no reply, we need more data
- {no_record, State#state{user_data_buffer = Buffer}};
- {passive, Buffer} ->
- {no_record, State#state{user_data_buffer = Buffer}};
- {error,_Reason} -> %% Invalid packet in packet mode
+ {more, Size} when Size =< BufferSize0 ->
+ %% We have a packet in the buffer - collect it in a binary and decode
+ {Data,Front,Rear} = iovec_from_front(Size - byte_size(Bin0), Front0, Rear0, [Bin0]),
+ Bin = iolist_to_binary(Data),
+ read_application_data_bin(
+ State, Front, BufferSize0, Rear, SocketOpts0, RecvFrom, BytesToRead, Bin);
+ {more, _Size} ->
+ %% We do not have a packet in the buffer - wait for more
+ {no_record, State#state{socket_options = SocketOpts0,
+ bytes_to_read = BytesToRead,
+ start_or_recv_from = RecvFrom,
+ user_data_buffer = {[Bin0|Front0],BufferSize0,Rear0}}};
+ passive ->
+ {no_record, State#state{socket_options = SocketOpts0,
+ bytes_to_read = BytesToRead,
+ start_or_recv_from = RecvFrom,
+ user_data_buffer = {[Bin0|Front0],BufferSize0,Rear0}}};
+ {error,_Reason} ->
+ %% Invalid packet in packet mode
#state{
static_env =
#static_env{
@@ -556,13 +556,137 @@ read_application_data(
protocol_cb = Connection,
transport_cb = Transport,
tracker = Tracker},
- user_application = {_Mon, Pid}} = State,
+ connection_env =
+ #connection_env{user_application = {_Mon, Pid}}} = State,
+ Buffer = iolist_to_binary([Bin0,Front0|lists:reverse(Rear0)]),
deliver_packet_error(
Connection:pids(State), Transport, Socket, SocketOpts0,
- Buffer0, Pid, RecvFrom, Tracker, Connection),
- {stop, {shutdown, normal}, State}
+ Buffer, Pid, RecvFrom, Tracker, Connection),
+ {stop, {shutdown, normal}, State#state{socket_options = SocketOpts0,
+ bytes_to_read = BytesToRead,
+ start_or_recv_from = RecvFrom,
+ user_data_buffer = {[Buffer],BufferSize0,[]}}}
end.
+read_application_data_deliver(State, Front, BufferSize, Rear, SocketOpts0, RecvFrom, Data) ->
+ #state{
+ static_env =
+ #static_env{
+ socket = Socket,
+ protocol_cb = Connection,
+ transport_cb = Transport,
+ tracker = Tracker},
+ connection_env =
+ #connection_env{user_application = {_Mon, Pid}}} = State,
+ SocketOpts =
+ deliver_app_data(
+ Connection:pids(State), Transport, Socket, SocketOpts0, Data, Pid, RecvFrom, Tracker, Connection),
+ if
+ SocketOpts#socket_options.active =:= false ->
+ %% Passive mode, wait for active once or recv
+ {no_record,
+ State#state{
+ user_data_buffer = {Front,BufferSize,Rear},
+ start_or_recv_from = undefined,
+ bytes_to_read = undefined,
+ socket_options = SocketOpts
+ }};
+ true -> %% Try to deliver more data
+ read_application_data(State, Front, BufferSize, Rear, SocketOpts, undefined, undefined)
+ end.
+
+
+read_application_dist_data(DHandle, [Bin|Front], BufferSize, Rear) ->
+ read_application_dist_data(DHandle, Front, BufferSize, Rear, Bin);
+read_application_dist_data(_DHandle, [] = Front, BufferSize, [] = Rear) ->
+ BufferSize = 0,
+ {Front,BufferSize,Rear};
+read_application_dist_data(DHandle, [], BufferSize, Rear) ->
+ [Bin|Front] = lists:reverse(Rear),
+ read_application_dist_data(DHandle, Front, BufferSize, [], Bin).
+%%
+read_application_dist_data(DHandle, Front0, BufferSize, Rear0, Bin0) ->
+ case Bin0 of
+ %%
+ %% START Optimization
+ %% It is cheaper to match out several packets in one match operation than to loop for each
+ <<SizeA:32, DataA:SizeA/binary,
+ SizeB:32, DataB:SizeB/binary,
+ SizeC:32, DataC:SizeC/binary,
+ SizeD:32, DataD:SizeD/binary, Rest/binary>> ->
+ %% We have 4 complete packets in the first binary
+ erlang:dist_ctrl_put_data(DHandle, DataA),
+ erlang:dist_ctrl_put_data(DHandle, DataB),
+ erlang:dist_ctrl_put_data(DHandle, DataC),
+ erlang:dist_ctrl_put_data(DHandle, DataD),
+ read_application_dist_data(
+ DHandle, Front0, BufferSize - (4*4+SizeA+SizeB+SizeC+SizeD), Rear0, Rest);
+ <<SizeA:32, DataA:SizeA/binary,
+ SizeB:32, DataB:SizeB/binary,
+ SizeC:32, DataC:SizeC/binary, Rest/binary>> ->
+ %% We have 3 complete packets in the first binary
+ erlang:dist_ctrl_put_data(DHandle, DataA),
+ erlang:dist_ctrl_put_data(DHandle, DataB),
+ erlang:dist_ctrl_put_data(DHandle, DataC),
+ read_application_dist_data(
+ DHandle, Front0, BufferSize - (3*4+SizeA+SizeB+SizeC), Rear0, Rest);
+ <<SizeA:32, DataA:SizeA/binary,
+ SizeB:32, DataB:SizeB/binary, Rest/binary>> ->
+ %% We have 2 complete packets in the first binary
+ erlang:dist_ctrl_put_data(DHandle, DataA),
+ erlang:dist_ctrl_put_data(DHandle, DataB),
+ read_application_dist_data(
+ DHandle, Front0, BufferSize - (2*4+SizeA+SizeB), Rear0, Rest);
+ %% END Optimization
+ %%
+ %% Basic one packet code path
+ <<Size:32, Data:Size/binary, Rest/binary>> ->
+ %% We have a complete packet in the first binary
+ erlang:dist_ctrl_put_data(DHandle, Data),
+ read_application_dist_data(DHandle, Front0, BufferSize - (4+Size), Rear0, Rest);
+ <<Size:32, FirstData/binary>> when 4+Size =< BufferSize ->
+ %% We have a complete packet in the buffer
+ %% - fetch the missing content from the buffer front
+ {Data,Front,Rear} = iovec_from_front(Size - byte_size(FirstData), Front0, Rear0, [FirstData]),
+ erlang:dist_ctrl_put_data(DHandle, Data),
+ read_application_dist_data(DHandle, Front, BufferSize - (4+Size), Rear);
+ <<Bin/binary>> ->
+ %% In OTP-21 the match context reuse optimization fails if we use Bin0 in recursion, so here we
+ %% match out the whole binary which will trick the optimization into keeping the match context
+ %% for the first binary contains complete packet code above
+ case Bin of
+ <<_Size:32, _InsufficientData/binary>> ->
+ %% We have a length field in the first binary but there is not enough data
+ %% in the buffer to form a complete packet - await more data
+ {[Bin|Front0],BufferSize,Rear0};
+ <<IncompleteLengthField/binary>> when 4 < BufferSize ->
+ %% We do not have a length field in the first binary but the buffer
+ %% contains enough data to maybe form a packet
+ %% - fetch a tiny binary from the buffer front to complete the length field
+ {LengthField,Front,Rear} =
+ iovec_from_front(4 - byte_size(IncompleteLengthField), Front0, Rear0, [IncompleteLengthField]),
+ LengthBin = iolist_to_binary(LengthField),
+ read_application_dist_data(DHandle, Front, BufferSize, Rear, LengthBin);
+ <<IncompleteLengthField/binary>> ->
+ %% We do not have enough data in the buffer to even form a length field - await more data
+ {[IncompleteLengthField|Front0],BufferSize,Rear0}
+ end
+ end.
+
+iovec_from_front(Size, [], Rear, Acc) ->
+ iovec_from_front(Size, lists:reverse(Rear), [], Acc);
+iovec_from_front(Size, [Bin|Front], Rear, Acc) ->
+ case Bin of
+ <<Last:Size/binary>> -> % Just enough
+ {lists:reverse(Acc, [Last]),Front,Rear};
+ <<Last:Size/binary, Rest/binary>> -> % More than enough, split here
+ {lists:reverse(Acc, [Last]),[Rest|Front],Rear};
+ <<_/binary>> -> % Not enough
+ BinSize = byte_size(Bin),
+ iovec_from_front(Size - BinSize, Front, Rear, [Bin|Acc])
+ end.
+
+
%%====================================================================
%% Help functions for tls|dtls_connection.erl
%%====================================================================
@@ -575,8 +699,8 @@ handle_session(#server_hello{cipher_suite = CipherSuite,
compression_method = Compression},
Version, NewId, ConnectionStates, ProtoExt, Protocol0,
#state{session = #session{session_id = OldId},
- negotiated_version = ReqVersion,
- negotiated_protocol = CurrentProtocol} = State0) ->
+ handshake_env = #handshake_env{negotiated_protocol = CurrentProtocol} = HsEnv,
+ connection_env = #connection_env{negotiated_version = ReqVersion} = CEnv} = State0) ->
#{key_exchange := KeyAlgorithm} =
ssl_cipher_format:suite_definition(CipherSuite),
@@ -589,12 +713,12 @@ handle_session(#server_hello{cipher_suite = CipherSuite,
{ProtoExt =:= npn, Protocol0}
end,
- State = State0#state{key_algorithm = KeyAlgorithm,
- negotiated_version = Version,
- connection_states = ConnectionStates,
- premaster_secret = PremasterSecret,
- expecting_next_protocol_negotiation = ExpectNPN,
- negotiated_protocol = Protocol},
+ State = State0#state{connection_states = ConnectionStates,
+ handshake_env = HsEnv#handshake_env{kex_algorithm = KeyAlgorithm,
+ premaster_secret = PremasterSecret,
+ expecting_next_protocol_negotiation = ExpectNPN,
+ negotiated_protocol = Protocol},
+ connection_env = CEnv#connection_env{negotiated_version = Version}},
case ssl_session:is_new(OldId, NewId) of
true ->
@@ -608,11 +732,9 @@ handle_session(#server_hello{cipher_suite = CipherSuite,
%%--------------------------------------------------------------------
-spec ssl_config(#ssl_options{}, client | server, #state{}) -> #state{}.
%%--------------------------------------------------------------------
-ssl_config(Opts, Role, State) ->
- ssl_config(Opts, Role, State, new).
-
-ssl_config(Opts, Role, #state{static_env = InitStatEnv0,
- handshake_env = HsEnv} = State0, Type) ->
+ssl_config(Opts, Role, #state{static_env = InitStatEnv0,
+ handshake_env = HsEnv,
+ connection_env = CEnv} = State0) ->
{ok, #{cert_db_ref := Ref,
cert_db_handle := CertDbHandle,
fileref_db_handle := FileRefHandle,
@@ -624,27 +746,19 @@ ssl_config(Opts, Role, #state{static_env = InitStatEnv0,
ssl_config:init(Opts, Role),
TimeStamp = erlang:monotonic_time(),
Session = State0#state.session,
-
- State = State0#state{session = Session#session{own_certificate = OwnCert,
- time_stamp = TimeStamp},
- static_env = InitStatEnv0#static_env{
- file_ref_db = FileRefHandle,
- cert_db_ref = Ref,
- cert_db = CertDbHandle,
- crl_db = CRLDbHandle,
- session_cache = CacheHandle
- },
- private_key = Key,
- diffie_hellman_params = DHParams,
- ssl_options = Opts},
- case Type of
- new ->
- Hist = ssl_handshake:init_handshake_history(),
- State#state{handshake_env = HsEnv#handshake_env{tls_handshake_history = Hist}};
- continue ->
- State
- end.
-
+
+ State0#state{session = Session#session{own_certificate = OwnCert,
+ time_stamp = TimeStamp},
+ static_env = InitStatEnv0#static_env{
+ file_ref_db = FileRefHandle,
+ cert_db_ref = Ref,
+ cert_db = CertDbHandle,
+ crl_db = CRLDbHandle,
+ session_cache = CacheHandle
+ },
+ handshake_env = HsEnv#handshake_env{diffie_hellman_params = DHParams},
+ connection_env = CEnv#connection_env{private_key = Key},
+ ssl_options = Opts}.
%%====================================================================
%% gen_statem general state functions with connection cb argument
@@ -657,8 +771,8 @@ ssl_config(Opts, Role, #state{static_env = InitStatEnv0,
%%--------------------------------------------------------------------
init({call, From}, {start, Timeout}, State0, Connection) ->
- Timer = start_or_recv_cancel_timer(Timeout, From),
- Connection:next_event(hello, no_record, State0#state{start_or_recv_from = From, timer = Timer});
+ Connection:next_event(hello, no_record, State0#state{start_or_recv_from = From},
+ [{{timeout, handshake}, Timeout, close}]);
init({call, From}, {start, {Opts, EmOpts}, Timeout},
#state{static_env = #static_env{role = Role},
ssl_options = OrigSSLOptions,
@@ -705,21 +819,18 @@ hello(info, Msg, State, _) ->
hello(Type, Msg, State, Connection) ->
handle_common_event(Type, Msg, ?FUNCTION_NAME, State, Connection).
-user_hello({call, From}, cancel, #state{negotiated_version = Version} = State, _) ->
+user_hello({call, From}, cancel, #state{connection_env = #connection_env{negotiated_version = Version}} = State, _) ->
gen_statem:reply(From, ok),
handle_own_alert(?ALERT_REC(?FATAL, ?USER_CANCELED, user_canceled),
Version, ?FUNCTION_NAME, State);
user_hello({call, From}, {handshake_continue, NewOptions, Timeout},
- #state{hello = Hello,
- static_env = #static_env{role = Role},
- start_or_recv_from = RecvFrom,
+ #state{static_env = #static_env{role = Role},
+ handshake_env = #handshake_env{hello = Hello},
ssl_options = Options0} = State0, _Connection) ->
- Timer = start_or_recv_cancel_timer(Timeout, RecvFrom),
Options = ssl:handle_options(NewOptions, Options0#ssl_options{handshake = full}),
- State = ssl_config(Options, Role, State0, continue),
- {next_state, hello, State#state{start_or_recv_from = From,
- timer = Timer},
- [{next_event, internal, Hello}]};
+ State = ssl_config(Options, Role, State0),
+ {next_state, hello, State#state{start_or_recv_from = From},
+ [{next_event, internal, Hello}, {{timeout, handshake}, Timeout, close}]};
user_hello(_, _, _, _) ->
{keep_state_and_data, [postpone]}.
@@ -733,9 +844,9 @@ abbreviated({call, From}, Msg, State, Connection) ->
handle_call(Msg, From, ?FUNCTION_NAME, State, Connection);
abbreviated(internal, #finished{verify_data = Data} = Finished,
#state{static_env = #static_env{role = server},
- handshake_env = #handshake_env{tls_handshake_history = Hist},
- negotiated_version = Version,
- expecting_finished = true,
+ handshake_env = #handshake_env{tls_handshake_history = Hist,
+ expecting_finished = true} = HsEnv,
+ connection_env = #connection_env{negotiated_version = Version},
session = #session{master_secret = MasterSecret},
connection_states = ConnectionStates0} =
State0, Connection) ->
@@ -746,16 +857,16 @@ abbreviated(internal, #finished{verify_data = Data} = Finished,
ConnectionStates =
ssl_record:set_client_verify_data(current_both, Data, ConnectionStates0),
{Record, State} = prepare_connection(State0#state{connection_states = ConnectionStates,
- expecting_finished = false}, Connection),
- Connection:next_event(connection, Record, State);
+ handshake_env = HsEnv#handshake_env{expecting_finished = false}}, Connection),
+ Connection:next_event(connection, Record, State, [{{timeout, handshake}, infinity, close}]);
#alert{} = Alert ->
handle_own_alert(Alert, Version, ?FUNCTION_NAME, State0)
end;
abbreviated(internal, #finished{verify_data = Data} = Finished,
#state{static_env = #static_env{role = client},
handshake_env = #handshake_env{tls_handshake_history = Hist0},
+ connection_env = #connection_env{negotiated_version = Version},
session = #session{master_secret = MasterSecret},
- negotiated_version = Version,
connection_states = ConnectionStates0} = State0, Connection) ->
case ssl_handshake:verify_connection(ssl:tls_version(Version), Finished, server,
get_pending_prf(ConnectionStates0, write),
@@ -763,11 +874,11 @@ abbreviated(internal, #finished{verify_data = Data} = Finished,
verified ->
ConnectionStates1 =
ssl_record:set_server_verify_data(current_read, Data, ConnectionStates0),
- {State1, Actions} =
+ {#state{handshake_env = HsEnv} = State1, Actions} =
finalize_handshake(State0#state{connection_states = ConnectionStates1},
?FUNCTION_NAME, Connection),
- {Record, State} = prepare_connection(State1#state{expecting_finished = false}, Connection),
- Connection:next_event(connection, Record, State, Actions);
+ {Record, State} = prepare_connection(State1#state{handshake_env = HsEnv#handshake_env{expecting_finished = false}}, Connection),
+ Connection:next_event(connection, Record, State, [{{timeout, handshake}, infinity, close} | Actions]);
#alert{} = Alert ->
handle_own_alert(Alert, Version, ?FUNCTION_NAME, State0)
end;
@@ -775,19 +886,20 @@ abbreviated(internal, #finished{verify_data = Data} = Finished,
%% & before finished message and it is not allowed during renegotiation
abbreviated(internal, #next_protocol{selected_protocol = SelectedProtocol},
#state{static_env = #static_env{role = server},
- expecting_next_protocol_negotiation = true} = State,
+ handshake_env = #handshake_env{expecting_next_protocol_negotiation = true} = HsEnv} = State,
Connection) ->
Connection:next_event(?FUNCTION_NAME, no_record,
- State#state{negotiated_protocol = SelectedProtocol,
- expecting_next_protocol_negotiation = false});
+ State#state{handshake_env = HsEnv#handshake_env{negotiated_protocol = SelectedProtocol,
+ expecting_next_protocol_negotiation = false}});
abbreviated(internal,
#change_cipher_spec{type = <<1>>},
- #state{connection_states = ConnectionStates0} = State, Connection) ->
+ #state{connection_states = ConnectionStates0,
+ handshake_env = HsEnv} = State, Connection) ->
ConnectionStates1 =
ssl_record:activate_pending_connection_state(ConnectionStates0, read, Connection),
Connection:next_event(?FUNCTION_NAME, no_record, State#state{connection_states =
ConnectionStates1,
- expecting_finished = true});
+ handshake_env = HsEnv#handshake_env{expecting_finished = true}});
abbreviated(info, Msg, State, _) ->
handle_info(Msg, ?FUNCTION_NAME, State);
abbreviated(Type, Msg, State, Connection) ->
@@ -806,7 +918,7 @@ certify(info, Msg, State, _) ->
handle_info(Msg, ?FUNCTION_NAME, State);
certify(internal, #certificate{asn1_certificates = []},
#state{static_env = #static_env{role = server},
- negotiated_version = Version,
+ connection_env = #connection_env{negotiated_version = Version},
ssl_options = #ssl_options{verify = verify_peer,
fail_if_no_peer_cert = true}} =
State, _) ->
@@ -820,7 +932,7 @@ certify(internal, #certificate{asn1_certificates = []},
Connection:next_event(?FUNCTION_NAME, no_record, State0#state{client_certificate_requested = false});
certify(internal, #certificate{},
#state{static_env = #static_env{role = server},
- negotiated_version = Version,
+ connection_env = #connection_env{negotiated_version = Version},
ssl_options = #ssl_options{verify = verify_none}} =
State, _) ->
Alert = ?ALERT_REC(?FATAL,?UNEXPECTED_MESSAGE, unrequested_certificate),
@@ -832,7 +944,7 @@ certify(internal, #certificate{} = Cert,
cert_db = CertDbHandle,
cert_db_ref = CertDbRef,
crl_db = CRLDbInfo},
- negotiated_version = Version,
+ connection_env = #connection_env{negotiated_version = Version},
ssl_options = Opts} = State, Connection) ->
case ssl_handshake:certify(Cert, CertDbHandle, CertDbRef,
Opts, CRLDbInfo, Role, Host) of
@@ -844,34 +956,42 @@ certify(internal, #certificate{} = Cert,
end;
certify(internal, #server_key_exchange{exchange_keys = Keys},
#state{static_env = #static_env{role = client},
- negotiated_version = Version,
- key_algorithm = Alg,
- public_key_info = PubKeyInfo,
+ handshake_env = #handshake_env{kex_algorithm = KexAlg,
+ public_key_info = PubKeyInfo} = HsEnv,
+ connection_env = #connection_env{negotiated_version = Version},
session = Session,
connection_states = ConnectionStates} = State, Connection)
- when Alg == dhe_dss; Alg == dhe_rsa;
- Alg == ecdhe_rsa; Alg == ecdhe_ecdsa;
- Alg == dh_anon; Alg == ecdh_anon;
- Alg == psk; Alg == dhe_psk; Alg == ecdhe_psk; Alg == rsa_psk;
- Alg == srp_dss; Alg == srp_rsa; Alg == srp_anon ->
-
- Params = ssl_handshake:decode_server_key(Keys, Alg, ssl:tls_version(Version)),
+ when KexAlg == dhe_dss;
+ KexAlg == dhe_rsa;
+ KexAlg == ecdhe_rsa;
+ KexAlg == ecdhe_ecdsa;
+ KexAlg == dh_anon;
+ KexAlg == ecdh_anon;
+ KexAlg == psk;
+ KexAlg == dhe_psk;
+ KexAlg == ecdhe_psk;
+ KexAlg == rsa_psk;
+ KexAlg == srp_dss;
+ KexAlg == srp_rsa;
+ KexAlg == srp_anon ->
+
+ Params = ssl_handshake:decode_server_key(Keys, KexAlg, ssl:tls_version(Version)),
%% Use negotiated value if TLS-1.2 otherwhise return default
- HashSign = negotiated_hashsign(Params#server_key_params.hashsign, Alg, PubKeyInfo, ssl:tls_version(Version)),
+ HashSign = negotiated_hashsign(Params#server_key_params.hashsign, KexAlg, PubKeyInfo, ssl:tls_version(Version)),
- case is_anonymous(Alg) of
+ case is_anonymous(KexAlg) of
true ->
calculate_secret(Params#server_key_params.params,
- State#state{hashsign_algorithm = HashSign}, Connection);
+ State#state{handshake_env = HsEnv#handshake_env{hashsign_algorithm = HashSign}}, Connection);
false ->
case ssl_handshake:verify_server_key(Params, HashSign,
ConnectionStates, ssl:tls_version(Version), PubKeyInfo) of
true ->
calculate_secret(Params#server_key_params.params,
- State#state{hashsign_algorithm = HashSign,
- session = session_handle_params(Params#server_key_params.params, Session)},
- Connection);
+ State#state{handshake_env = HsEnv#handshake_env{hashsign_algorithm = HashSign},
+ session = session_handle_params(Params#server_key_params.params, Session)},
+ Connection);
false ->
handle_own_alert(?ALERT_REC(?FATAL, ?DECRYPT_ERROR),
Version, ?FUNCTION_NAME, State)
@@ -879,11 +999,17 @@ certify(internal, #server_key_exchange{exchange_keys = Keys},
end;
certify(internal, #certificate_request{},
#state{static_env = #static_env{role = client},
- negotiated_version = Version,
- key_algorithm = Alg} = State, _)
- when Alg == dh_anon; Alg == ecdh_anon;
- Alg == psk; Alg == dhe_psk; Alg == ecdhe_psk; Alg == rsa_psk;
- Alg == srp_dss; Alg == srp_rsa; Alg == srp_anon ->
+ handshake_env = #handshake_env{kex_algorithm = KexAlg},
+ connection_env = #connection_env{negotiated_version = Version}} = State, _)
+ when KexAlg == dh_anon;
+ KexAlg == ecdh_anon;
+ KexAlg == psk;
+ KexAlg == dhe_psk;
+ KexAlg == ecdhe_psk;
+ KexAlg == rsa_psk;
+ KexAlg == srp_dss;
+ KexAlg == srp_rsa;
+ KexAlg == srp_anon ->
handle_own_alert(?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE),
Version, ?FUNCTION_NAME, State);
certify(internal, #certificate_request{},
@@ -895,9 +1021,10 @@ certify(internal, #certificate_request{},
Connection:next_event(?FUNCTION_NAME, no_record, State#state{client_certificate_requested = true});
certify(internal, #certificate_request{} = CertRequest,
#state{static_env = #static_env{role = client},
+ handshake_env = HsEnv,
+ connection_env = #connection_env{negotiated_version = Version},
session = #session{own_certificate = Cert},
- ssl_options = #ssl_options{signature_algs = SupportedHashSigns},
- negotiated_version = Version} = State, Connection) ->
+ ssl_options = #ssl_options{signature_algs = SupportedHashSigns}} = State, Connection) ->
case ssl_handshake:select_hashsign(CertRequest, Cert,
SupportedHashSigns, ssl:tls_version(Version)) of
#alert {} = Alert ->
@@ -905,53 +1032,55 @@ certify(internal, #certificate_request{} = CertRequest,
NegotiatedHashSign ->
Connection:next_event(?FUNCTION_NAME, no_record,
State#state{client_certificate_requested = true,
- cert_hashsign_algorithm = NegotiatedHashSign})
+ handshake_env = HsEnv#handshake_env{cert_hashsign_algorithm = NegotiatedHashSign}})
end;
%% PSK and RSA_PSK might bypass the Server-Key-Exchange
certify(internal, #server_hello_done{},
#state{static_env = #static_env{role = client},
session = #session{master_secret = undefined},
- negotiated_version = Version,
- psk_identity = PSKIdentity,
- ssl_options = #ssl_options{user_lookup_fun = PSKLookup},
- premaster_secret = undefined,
- key_algorithm = Alg} = State0, Connection)
- when Alg == psk ->
- case ssl_handshake:premaster_secret({Alg, PSKIdentity}, PSKLookup) of
+ connection_env = #connection_env{negotiated_version = Version},
+ handshake_env = #handshake_env{kex_algorithm = KexAlg,
+ premaster_secret = undefined,
+ server_psk_identity = PSKIdentity} = HsEnv,
+ ssl_options = #ssl_options{user_lookup_fun = PSKLookup}} = State0, Connection)
+ when KexAlg == psk ->
+ case ssl_handshake:premaster_secret({KexAlg, PSKIdentity}, PSKLookup) of
#alert{} = Alert ->
handle_own_alert(Alert, Version, ?FUNCTION_NAME, State0);
PremasterSecret ->
State = master_secret(PremasterSecret,
- State0#state{premaster_secret = PremasterSecret}),
- client_certify_and_key_exchange(State, Connection)
+ State0#state{handshake_env =
+ HsEnv#handshake_env{premaster_secret = PremasterSecret}}),
+ client_certify_and_key_exchange(State, Connection)
end;
certify(internal, #server_hello_done{},
#state{static_env = #static_env{role = client},
+ connection_env = #connection_env{negotiated_version = {Major, Minor}} = Version,
+ handshake_env = #handshake_env{kex_algorithm = KexAlg,
+ premaster_secret = undefined,
+ server_psk_identity = PSKIdentity} = HsEnv,
session = #session{master_secret = undefined},
- ssl_options = #ssl_options{user_lookup_fun = PSKLookup},
- negotiated_version = {Major, Minor} = Version,
- psk_identity = PSKIdentity,
- premaster_secret = undefined,
- key_algorithm = Alg} = State0, Connection)
- when Alg == rsa_psk ->
+ ssl_options = #ssl_options{user_lookup_fun = PSKLookup}} = State0, Connection)
+ when KexAlg == rsa_psk ->
Rand = ssl_cipher:random_bytes(?NUM_OF_PREMASTERSECRET_BYTES-2),
RSAPremasterSecret = <<?BYTE(Major), ?BYTE(Minor), Rand/binary>>,
- case ssl_handshake:premaster_secret({Alg, PSKIdentity}, PSKLookup,
+ case ssl_handshake:premaster_secret({KexAlg, PSKIdentity}, PSKLookup,
RSAPremasterSecret) of
#alert{} = Alert ->
handle_own_alert(Alert, Version, ?FUNCTION_NAME, State0);
PremasterSecret ->
State = master_secret(PremasterSecret,
- State0#state{premaster_secret = RSAPremasterSecret}),
+ State0#state{handshake_env =
+ HsEnv#handshake_env{premaster_secret = RSAPremasterSecret}}),
client_certify_and_key_exchange(State, Connection)
end;
%% Master secret was determined with help of server-key exchange msg
certify(internal, #server_hello_done{},
#state{static_env = #static_env{role = client},
- session = #session{master_secret = MasterSecret} = Session,
- connection_states = ConnectionStates0,
- negotiated_version = Version,
- premaster_secret = undefined} = State0, Connection) ->
+ connection_env = #connection_env{negotiated_version = Version},
+ handshake_env = #handshake_env{premaster_secret = undefined},
+ session = #session{master_secret = MasterSecret} = Session,
+ connection_states = ConnectionStates0} = State0, Connection) ->
case ssl_handshake:master_secret(ssl:tls_version(Version), Session,
ConnectionStates0, client) of
{MasterSecret, ConnectionStates} ->
@@ -963,10 +1092,10 @@ certify(internal, #server_hello_done{},
%% Master secret is calculated from premaster_secret
certify(internal, #server_hello_done{},
#state{static_env = #static_env{role = client},
+ connection_env = #connection_env{negotiated_version = Version},
+ handshake_env = #handshake_env{premaster_secret = PremasterSecret},
session = Session0,
- connection_states = ConnectionStates0,
- negotiated_version = Version,
- premaster_secret = PremasterSecret} = State0, Connection) ->
+ connection_states = ConnectionStates0} = State0, Connection) ->
case ssl_handshake:master_secret(ssl:tls_version(Version), PremasterSecret,
ConnectionStates0, client) of
{MasterSecret, ConnectionStates} ->
@@ -985,7 +1114,8 @@ certify(internal = Type, #client_key_exchange{} = Msg,
%% We expect a certificate here
handle_common_event(Type, Msg, ?FUNCTION_NAME, State, Connection);
certify(internal, #client_key_exchange{exchange_keys = Keys},
- State = #state{key_algorithm = KeyAlg, negotiated_version = Version}, Connection) ->
+ State = #state{handshake_env = #handshake_env{kex_algorithm = KeyAlg},
+ connection_env = #connection_env{negotiated_version = Version}}, Connection) ->
try
certify_client_key_exchange(ssl_handshake:decode_client_key(Keys, KeyAlg, ssl:tls_version(Version)),
State, Connection)
@@ -1009,42 +1139,43 @@ cipher(info, Msg, State, _) ->
cipher(internal, #certificate_verify{signature = Signature,
hashsign_algorithm = CertHashSign},
#state{static_env = #static_env{role = server},
- handshake_env = #handshake_env{tls_handshake_history = Hist},
- key_algorithm = KexAlg,
- public_key_info = PublicKeyInfo,
- negotiated_version = Version,
+ handshake_env = #handshake_env{tls_handshake_history = Hist,
+ kex_algorithm = KexAlg,
+ public_key_info = PubKeyInfo} = HsEnv,
+ connection_env = #connection_env{negotiated_version = Version},
session = #session{master_secret = MasterSecret}
} = State, Connection) ->
TLSVersion = ssl:tls_version(Version),
%% Use negotiated value if TLS-1.2 otherwhise return default
- HashSign = negotiated_hashsign(CertHashSign, KexAlg, PublicKeyInfo, TLSVersion),
- case ssl_handshake:certificate_verify(Signature, PublicKeyInfo,
+ HashSign = negotiated_hashsign(CertHashSign, KexAlg, PubKeyInfo, TLSVersion),
+ case ssl_handshake:certificate_verify(Signature, PubKeyInfo,
TLSVersion, HashSign, MasterSecret, Hist) of
valid ->
Connection:next_event(?FUNCTION_NAME, no_record,
- State#state{cert_hashsign_algorithm = HashSign});
+ State#state{handshake_env = HsEnv#handshake_env{cert_hashsign_algorithm = HashSign}});
#alert{} = Alert ->
handle_own_alert(Alert, Version, ?FUNCTION_NAME, State)
end;
%% client must send a next protocol message if we are expecting it
cipher(internal, #finished{},
#state{static_env = #static_env{role = server},
- expecting_next_protocol_negotiation = true,
- negotiated_protocol = undefined, negotiated_version = Version} = State0,
+ handshake_env = #handshake_env{expecting_next_protocol_negotiation = true,
+ negotiated_protocol = undefined},
+ connection_env = #connection_env{negotiated_version = Version}} = State0,
_Connection) ->
handle_own_alert(?ALERT_REC(?FATAL,?UNEXPECTED_MESSAGE), Version, ?FUNCTION_NAME, State0);
cipher(internal, #finished{verify_data = Data} = Finished,
#state{static_env = #static_env{role = Role,
host = Host,
port = Port},
- negotiated_version = Version,
- expecting_finished = true,
+ handshake_env = #handshake_env{tls_handshake_history = Hist,
+ expecting_finished = true} = HsEnv,
+ connection_env = #connection_env{negotiated_version = Version},
session = #session{master_secret = MasterSecret}
= Session0,
ssl_options = SslOpts,
- connection_states = ConnectionStates0,
- handshake_env = #handshake_env{tls_handshake_history = Hist}} = State, Connection) ->
+ connection_states = ConnectionStates0} = State, Connection) ->
case ssl_handshake:verify_connection(ssl:tls_version(Version), Finished,
opposite_role(Role),
get_current_prf(ConnectionStates0, read),
@@ -1052,7 +1183,7 @@ cipher(internal, #finished{verify_data = Data} = Finished,
verified ->
Session = handle_session(Role, SslOpts, Host, Port, Session0),
cipher_role(Role, Data, Session,
- State#state{expecting_finished = false}, Connection);
+ State#state{handshake_env = HsEnv#handshake_env{expecting_finished = false}}, Connection);
#alert{} = Alert ->
handle_own_alert(Alert, Version, ?FUNCTION_NAME, State)
end;
@@ -1060,19 +1191,17 @@ cipher(internal, #finished{verify_data = Data} = Finished,
%% & before finished message and it is not allowed during renegotiation
cipher(internal, #next_protocol{selected_protocol = SelectedProtocol},
#state{static_env = #static_env{role = server},
- expecting_next_protocol_negotiation = true,
- expecting_finished = true} = State0, Connection) ->
- {Record, State} =
- Connection:next_record(State0#state{negotiated_protocol = SelectedProtocol}),
- Connection:next_event(?FUNCTION_NAME, Record,
- State#state{expecting_next_protocol_negotiation = false});
-cipher(internal, #change_cipher_spec{type = <<1>>}, #state{connection_states = ConnectionStates0} =
+ handshake_env = #handshake_env{expecting_finished = true,
+ expecting_next_protocol_negotiation = true} = HsEnv} = State, Connection) ->
+ Connection:next_event(?FUNCTION_NAME, no_record,
+ State#state{handshake_env = HsEnv#handshake_env{negotiated_protocol = SelectedProtocol,
+ expecting_next_protocol_negotiation = false}});
+cipher(internal, #change_cipher_spec{type = <<1>>}, #state{handshake_env = HsEnv, connection_states = ConnectionStates0} =
State, Connection) ->
ConnectionStates =
ssl_record:activate_pending_connection_state(ConnectionStates0, read, Connection),
- Connection:next_event(?FUNCTION_NAME, no_record, State#state{connection_states =
- ConnectionStates,
- expecting_finished = true});
+ Connection:next_event(?FUNCTION_NAME, no_record, State#state{handshake_env = HsEnv#handshake_env{expecting_finished = true},
+ connection_states = ConnectionStates});
cipher(Type, Msg, State, Connection) ->
handle_common_event(Type, Msg, ?FUNCTION_NAME, State, Connection).
@@ -1085,10 +1214,9 @@ connection({call, RecvFrom}, {recv, N, Timeout},
#state{static_env = #static_env{protocol_cb = Connection},
socket_options =
#socket_options{active = false}} = State0, Connection) ->
- Timer = start_or_recv_cancel_timer(Timeout, RecvFrom),
passive_receive(State0#state{bytes_to_read = N,
- start_or_recv_from = RecvFrom,
- timer = Timer}, ?FUNCTION_NAME, Connection);
+ start_or_recv_from = RecvFrom}, ?FUNCTION_NAME, Connection,
+ [{{timeout, recv}, Timeout, timeout}]);
connection({call, From}, renegotiate, #state{static_env = #static_env{protocol_cb = Connection},
handshake_env = HsEnv} = State,
@@ -1104,10 +1232,10 @@ connection({call, From}, {connection_information, false}, State, _) ->
Info = connection_info(State),
hibernate_after(?FUNCTION_NAME, State, [{reply, From, {ok, Info}}]);
connection({call, From}, negotiated_protocol,
- #state{negotiated_protocol = undefined} = State, _) ->
+ #state{handshake_env = #handshake_env{negotiated_protocol = undefined}} = State, _) ->
hibernate_after(?FUNCTION_NAME, State, [{reply, From, {error, protocol_not_negotiated}}]);
connection({call, From}, negotiated_protocol,
- #state{negotiated_protocol = SelectedProtocol} = State, _) ->
+ #state{handshake_env = #handshake_env{negotiated_protocol = SelectedProtocol}} = State, _) ->
hibernate_after(?FUNCTION_NAME, State,
[{reply, From, {ok, SelectedProtocol}}]);
connection({call, From}, Msg, State, Connection) ->
@@ -1120,19 +1248,20 @@ connection(cast, {internal_renegotiate, WriteState}, #state{static_env = #static
connection_states = ConnectionStates#{current_write => WriteState}}, []);
connection(cast, {dist_handshake_complete, DHandle},
#state{ssl_options = #ssl_options{erl_dist = true},
+ connection_env = CEnv,
socket_options = SockOpts} = State0, Connection) ->
process_flag(priority, normal),
State1 =
State0#state{
socket_options = SockOpts#socket_options{active = true},
- erl_dist_handle = DHandle,
+ connection_env = CEnv#connection_env{erl_dist_handle = DHandle},
bytes_to_read = undefined},
{Record, State} = read_application_data(<<>>, State1),
Connection:next_event(connection, Record, State);
connection(info, Msg, State, _) ->
handle_info(Msg, ?FUNCTION_NAME, State);
-connection(internal, {recv, _}, State, Connection) ->
- passive_receive(State, ?FUNCTION_NAME, Connection);
+connection(internal, {recv, Timeout}, State, Connection) ->
+ passive_receive(State, ?FUNCTION_NAME, Connection, [{{timeout, recv}, Timeout, timeout}]);
connection(Type, Msg, State, Connection) ->
handle_common_event(Type, Msg, ?FUNCTION_NAME, State, Connection).
@@ -1159,14 +1288,14 @@ handle_common_event(internal, {handshake, {#hello_request{}, _}}, StateName,
when StateName =/= connection ->
keep_state_and_data;
handle_common_event(internal, {handshake, {Handshake, Raw}}, StateName,
- #state{handshake_env = #handshake_env{tls_handshake_history = Hist0} = HsEnv} = State0,
+ #state{handshake_env = #handshake_env{tls_handshake_history = Hist0}} = State0,
Connection) ->
PossibleSNI = Connection:select_sni_extension(Handshake),
%% This function handles client SNI hello extension when Handshake is
%% a client_hello, which needs to be determined by the connection callback.
%% In other cases this is a noop
- State = handle_sni_extension(PossibleSNI, State0),
+ State = #state{handshake_env = HsEnv} = handle_sni_extension(PossibleSNI, State0),
Hist = ssl_handshake:update_handshake_history(Hist0, Raw),
{next_state, StateName, State#state{handshake_env = HsEnv#handshake_env{tls_handshake_history = Hist}},
@@ -1176,35 +1305,47 @@ handle_common_event(internal, {protocol_record, TLSorDTLSRecord}, StateName, Sta
handle_common_event(timeout, hibernate, _, _, _) ->
{keep_state_and_data, [hibernate]};
handle_common_event(internal, #change_cipher_spec{type = <<1>>}, StateName,
- #state{negotiated_version = Version} = State, _) ->
+ #state{connection_env = #connection_env{negotiated_version = Version}} = State, _) ->
handle_own_alert(?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE), Version,
StateName, State);
-handle_common_event(_Type, Msg, StateName, #state{negotiated_version = Version} = State,
+handle_common_event({timeout, handshake}, close, _StateName, #state{start_or_recv_from = StartFrom} = State, _) ->
+ {stop_and_reply,
+ {shutdown, user_timeout},
+ {reply, StartFrom, {error, timeout}}, State#state{start_or_recv_from = undefined}};
+handle_common_event({timeout, recv}, timeout, StateName, #state{start_or_recv_from = RecvFrom} = State, _) ->
+ {next_state, StateName, State#state{start_or_recv_from = undefined,
+ bytes_to_read = undefined}, [{reply, RecvFrom, {error, timeout}}]};
+handle_common_event(Type, Msg, StateName, #state{connection_env =
+ #connection_env{negotiated_version = Version}} = State,
_) ->
- Alert = ?ALERT_REC(?FATAL,?UNEXPECTED_MESSAGE, {unexpected_msg, Msg}),
+ Alert = ?ALERT_REC(?FATAL,?UNEXPECTED_MESSAGE, {unexpected_msg, {Type,Msg}}),
handle_own_alert(Alert, Version, StateName, State).
handle_call({application_data, _Data}, _, _, _, _) ->
%% In renegotiation priorities handshake, send data when handshake is finished
{keep_state_and_data, [postpone]};
-handle_call({close, _} = Close, From, StateName, State, _Connection) ->
+handle_call({close, _} = Close, From, StateName, #state{connection_env = CEnv} = State, _Connection) ->
%% Run terminate before returning so that the reuseaddr
%% inet-option works properly
Result = terminate(Close, StateName, State),
{stop_and_reply,
{shutdown, normal},
- {reply, From, Result}, State#state{terminated = true}};
+ {reply, From, Result}, State#state{connection_env = CEnv#connection_env{terminated = true}}};
handle_call({shutdown, read_write = How}, From, StateName,
#state{static_env = #static_env{transport_cb = Transport,
- socket = Socket}} = State, _) ->
+ socket = Socket},
+ connection_env = CEnv} = State, _) ->
try send_alert(?ALERT_REC(?WARNING, ?CLOSE_NOTIFY),
StateName, State) of
_ ->
case Transport:shutdown(Socket, How) of
ok ->
- {next_state, StateName, State#state{terminated = true}, [{reply, From, ok}]};
+ {next_state, StateName, State#state{connection_env =
+ CEnv#connection_env{terminated = true}},
+ [{reply, From, ok}]};
Error ->
- {stop_and_reply, {shutdown, normal}, {reply, From, Error}, State#state{terminated = true}}
+ {stop_and_reply, {shutdown, normal}, {reply, From, Error},
+ State#state{connection_env = CEnv#connection_env{terminated = true}}}
end
catch
throw:Return ->
@@ -1226,15 +1367,13 @@ handle_call({recv, _N, _Timeout}, From, _,
handle_call({recv, N, Timeout}, RecvFrom, StateName, State, _) ->
%% Doing renegotiate wait with handling request until renegotiate is
%% finished.
- Timer = start_or_recv_cancel_timer(Timeout, RecvFrom),
- {next_state, StateName, State#state{bytes_to_read = N, start_or_recv_from = RecvFrom,
- timer = Timer},
- [{next_event, internal, {recv, RecvFrom}}]};
+ {next_state, StateName, State#state{bytes_to_read = N, start_or_recv_from = RecvFrom},
+ [{next_event, internal, {recv, RecvFrom}} , {{timeout, recv}, Timeout, timeout}]};
handle_call({new_user, User}, From, StateName,
- State =#state{user_application = {OldMon, _}}, _) ->
+ State = #state{connection_env = #connection_env{user_application = {OldMon, _}} = CEnv}, _) ->
NewMon = erlang:monitor(process, User),
erlang:demonitor(OldMon, [flush]),
- {next_state, StateName, State#state{user_application = {NewMon,User}},
+ {next_state, StateName, State#state{connection_env = CEnv#connection_env{user_application = {NewMon, User}}},
[{reply, From, ok}]};
handle_call({get_opts, OptTags}, From, _,
#state{static_env = #static_env{socket = Socket,
@@ -1254,13 +1393,9 @@ handle_call({set_opts, Opts0}, From, StateName,
handle_call(renegotiate, From, StateName, _, _) when StateName =/= connection ->
{keep_state_and_data, [{reply, From, {error, already_renegotiating}}]};
-handle_call(get_sslsocket, From, _StateName, State, Connection) ->
- SslSocket = Connection:socket(State),
- {keep_state_and_data, [{reply, From, SslSocket}]};
-
handle_call({prf, Secret, Label, Seed, WantedLength}, From, _,
#state{connection_states = ConnectionStates,
- negotiated_version = Version}, _) ->
+ connection_env = #connection_env{negotiated_version = Version}}, _) ->
#{security_parameters := SecParams} =
ssl_record:current_connection_state(ConnectionStates, read),
#security_parameters{master_secret = MasterSecret,
@@ -1308,14 +1443,14 @@ handle_info({ErrorTag, Socket, Reason}, StateName, #state{static_env = #static_e
{stop, {shutdown,normal}, State};
handle_info({'DOWN', MonitorRef, _, _, Reason}, _,
- #state{user_application = {MonitorRef, _Pid},
+ #state{connection_env = #connection_env{user_application = {MonitorRef, _Pid}},
ssl_options = #ssl_options{erl_dist = true}}) ->
{stop, {shutdown, Reason}};
handle_info({'DOWN', MonitorRef, _, _, _}, _,
- #state{user_application = {MonitorRef, _Pid}}) ->
+ #state{connection_env = #connection_env{user_application = {MonitorRef, _Pid}}}) ->
{stop, {shutdown, normal}};
handle_info({'EXIT', Pid, _Reason}, StateName,
- #state{user_application = {_MonitorRef, Pid}} = State) ->
+ #state{connection_env = #connection_env{user_application = {_MonitorRef, Pid}}} = State) ->
%% It seems the user application has linked to us
%% - ignore that and let the monitor handle this
{next_state, StateName, State};
@@ -1328,22 +1463,8 @@ handle_info({'EXIT', Socket, normal}, _StateName, #state{static_env = #static_en
handle_info({'EXIT', Socket, Reason}, _StateName, #state{static_env = #static_env{socket = Socket}} = State) ->
{stop,{shutdown, Reason}, State};
-handle_info(allow_renegotiate, StateName, State) ->
- {next_state, StateName, State#state{allow_renegotiate = true}};
-
-handle_info({cancel_start_or_recv, StartFrom}, StateName,
- #state{handshake_env = #handshake_env{renegotiation = {false, first}}} = State) when StateName =/= connection ->
- {stop_and_reply,
- {shutdown, user_timeout},
- {reply, StartFrom, {error, timeout}},
- State#state{timer = undefined}};
-handle_info({cancel_start_or_recv, RecvFrom}, StateName,
- #state{start_or_recv_from = RecvFrom} = State) when RecvFrom =/= undefined ->
- {next_state, StateName, State#state{start_or_recv_from = undefined,
- bytes_to_read = undefined,
- timer = undefined}, [{reply, RecvFrom, {error, timeout}}]};
-handle_info({cancel_start_or_recv, _RecvFrom}, StateName, State) ->
- {next_state, StateName, State#state{timer = undefined}};
+handle_info(allow_renegotiate, StateName, #state{handshake_env = HsEnv} = State) ->
+ {next_state, StateName, State#state{handshake_env = HsEnv#handshake_env{allow_renegotiate = true}}};
handle_info(Msg, StateName, #state{static_env = #static_env{socket = Socket, error_tag = Tag}} = State) ->
Report = io_lib:format("SSL: Got unexpected info: ~p ~n", [{Msg, Tag, Socket}]),
@@ -1353,7 +1474,7 @@ handle_info(Msg, StateName, #state{static_env = #static_env{socket = Socket, err
%%====================================================================
%% general gen_statem callbacks
%%====================================================================
-terminate(_, _, #state{terminated = true}) ->
+terminate(_, _, #state{connection_env = #connection_env{terminated = true}}) ->
%% Happens when user closes the connection using ssl:close/1
%% we want to guarantee that Transport:close has been called
%% when ssl:close/1 returns unless it is a downgrade where
@@ -1418,13 +1539,8 @@ format_status(terminate, [_, StateName, State]) ->
protocol_buffers = ?SECRET_PRINTOUT,
user_data_buffer = ?SECRET_PRINTOUT,
handshake_env = ?SECRET_PRINTOUT,
+ connection_env = ?SECRET_PRINTOUT,
session = ?SECRET_PRINTOUT,
- private_key = ?SECRET_PRINTOUT,
- diffie_hellman_params = ?SECRET_PRINTOUT,
- diffie_hellman_keys = ?SECRET_PRINTOUT,
- srp_params = ?SECRET_PRINTOUT,
- srp_keys = ?SECRET_PRINTOUT,
- premaster_secret = ?SECRET_PRINTOUT,
ssl_options = NewOptions,
flight_buffer = ?SECRET_PRINTOUT}
}}]}].
@@ -1438,10 +1554,10 @@ send_alert(Alert, _, #state{static_env = #static_env{protocol_cb = Connection}}
Connection:send_alert(Alert, State).
connection_info(#state{static_env = #static_env{protocol_cb = Connection},
- sni_hostname = SNIHostname,
+ handshake_env = #handshake_env{sni_hostname = SNIHostname},
session = #session{session_id = SessionId,
cipher_suite = CipherSuite, ecc = ECCCurve},
- negotiated_version = {_,_} = Version,
+ connection_env = #connection_env{negotiated_version = {_,_} = Version},
ssl_options = Opts}) ->
RecordCB = record_cb(Connection),
CipherSuiteDef = #{key_exchange := KexAlg} = ssl_cipher_format:suite_definition(CipherSuite),
@@ -1469,7 +1585,8 @@ security_info(#state{connection_states = ConnectionStates}) ->
do_server_hello(Type, #{next_protocol_negotiation := NextProtocols} =
ServerHelloExt,
- #state{negotiated_version = Version,
+ #state{connection_env = #connection_env{negotiated_version = Version},
+ handshake_env = HsEnv,
session = #session{session_id = SessId},
connection_states = ConnectionStates0,
ssl_options = #ssl_options{versions = [HighestVersion|_]}}
@@ -1482,8 +1599,8 @@ do_server_hello(Type, #{next_protocol_negotiation := NextProtocols} =
ssl_handshake:server_hello(SessId, ssl:tls_version(Version),
ConnectionStates1, ServerHelloExt),
State = server_hello(ServerHello,
- State1#state{expecting_next_protocol_negotiation =
- NextProtocols =/= undefined}, Connection),
+ State1#state{handshake_env = HsEnv#handshake_env{expecting_next_protocol_negotiation =
+ NextProtocols =/= undefined}}, Connection),
case Type of
new ->
new_server_hello(ServerHello, State, Connection);
@@ -1548,8 +1665,8 @@ override_server_random(Random, _, _) ->
new_server_hello(#server_hello{cipher_suite = CipherSuite,
compression_method = Compression,
session_id = SessionId},
- #state{session = Session0,
- negotiated_version = Version} = State0, Connection) ->
+ #state{session = Session0,
+ connection_env = #connection_env{negotiated_version = Version}} = State0, Connection) ->
try server_certify_and_key_exchange(State0, Connection) of
#state{} = State1 ->
{State, Actions} = server_hello_done(State1, Connection),
@@ -1565,7 +1682,7 @@ new_server_hello(#server_hello{cipher_suite = CipherSuite,
resumed_server_hello(#state{session = Session,
connection_states = ConnectionStates0,
- negotiated_version = Version} = State0, Connection) ->
+ connection_env = #connection_env{negotiated_version = Version}} = State0, Connection) ->
case ssl_handshake:master_secret(ssl:tls_version(Version), Session,
ConnectionStates0, server) of
@@ -1582,19 +1699,20 @@ resumed_server_hello(#state{session = Session,
server_hello(ServerHello, State0, Connection) ->
CipherSuite = ServerHello#server_hello.cipher_suite,
#{key_exchange := KeyAlgorithm} = ssl_cipher_format:suite_definition(CipherSuite),
- State = Connection:queue_handshake(ServerHello, State0),
- State#state{key_algorithm = KeyAlgorithm}.
+ #state{handshake_env = HsEnv} = State = Connection:queue_handshake(ServerHello, State0),
+ State#state{handshake_env = HsEnv#handshake_env{kex_algorithm = KeyAlgorithm}}.
server_hello_done(State, Connection) ->
HelloDone = ssl_handshake:server_hello_done(),
Connection:send_handshake(HelloDone, State).
handle_peer_cert(Role, PeerCert, PublicKeyInfo,
- #state{session = #session{cipher_suite = CipherSuite} = Session} = State0,
+ #state{handshake_env = HsEnv,
+ session = #session{cipher_suite = CipherSuite} = Session} = State0,
Connection) ->
- State1 = State0#state{session =
- Session#session{peer_certificate = PeerCert},
- public_key_info = PublicKeyInfo},
+ State1 = State0#state{handshake_env = HsEnv#handshake_env{public_key_info = PublicKeyInfo},
+ session =
+ Session#session{peer_certificate = PeerCert}},
#{key_exchange := KeyAlgorithm} = ssl_cipher_format:suite_definition(CipherSuite),
State = handle_peer_cert_key(Role, PeerCert, PublicKeyInfo, KeyAlgorithm, State1),
Connection:next_event(certify, no_record, State).
@@ -1602,21 +1720,13 @@ handle_peer_cert(Role, PeerCert, PublicKeyInfo,
handle_peer_cert_key(client, _,
{?'id-ecPublicKey', #'ECPoint'{point = _ECPoint} = PublicKey,
PublicKeyParams},
- KeyAlg, #state{session = Session} = State) when KeyAlg == ecdh_rsa;
+ KeyAlg, #state{handshake_env = HsEnv,
+ session = Session} = State) when KeyAlg == ecdh_rsa;
KeyAlg == ecdh_ecdsa ->
ECDHKey = public_key:generate_key(PublicKeyParams),
PremasterSecret = ssl_handshake:premaster_secret(PublicKey, ECDHKey),
- master_secret(PremasterSecret, State#state{diffie_hellman_keys = ECDHKey,
+ master_secret(PremasterSecret, State#state{handshake_env = HsEnv#handshake_env{kex_keys = ECDHKey},
session = Session#session{ecc = PublicKeyParams}});
-%% We do currently not support cipher suites that use fixed DH.
-%% If we want to implement that the following clause can be used
-%% to extract DH parameters form cert.
-%% handle_peer_cert_key(client, _PeerCert, {?dhpublicnumber, PublicKey, PublicKeyParams},
-%% {_,SignAlg},
-%% #state{diffie_hellman_keys = {_, MyPrivatKey}} = State) when
-%% SignAlg == dh_rsa;
-%% SignAlg == dh_dss ->
-%% dh_master_secret(PublicKeyParams, PublicKey, MyPrivatKey, State);
handle_peer_cert_key(_, _, _, _, State) ->
State.
@@ -1632,13 +1742,13 @@ certify_client(#state{client_certificate_requested = false} = State, _) ->
State.
verify_client_cert(#state{static_env = #static_env{role = client},
- handshake_env = #handshake_env{tls_handshake_history = Hist},
+ handshake_env = #handshake_env{tls_handshake_history = Hist,
+ cert_hashsign_algorithm = HashSign},
+ connection_env = #connection_env{negotiated_version = Version,
+ private_key = PrivateKey},
client_certificate_requested = true,
- negotiated_version = Version,
- private_key = PrivateKey,
session = #session{master_secret = MasterSecret,
- own_certificate = OwnCert},
- cert_hashsign_algorithm = HashSign} = State, Connection) ->
+ own_certificate = OwnCert}} = State, Connection) ->
case ssl_handshake:client_certificate_verify(OwnCert, MasterSecret,
ssl:tls_version(Version), HashSign, PrivateKey, Hist) of
@@ -1652,7 +1762,7 @@ verify_client_cert(#state{static_env = #static_env{role = client},
verify_client_cert(#state{client_certificate_requested = false} = State, _) ->
State.
-client_certify_and_key_exchange(#state{negotiated_version = Version} =
+client_certify_and_key_exchange(#state{connection_env = #connection_env{negotiated_version = Version}} =
State0, Connection) ->
try do_client_certify_and_key_exchange(State0, Connection) of
State1 = #state{} ->
@@ -1677,7 +1787,7 @@ server_certify_and_key_exchange(State0, Connection) ->
request_client_cert(State2, Connection).
certify_client_key_exchange(#encrypted_premaster_secret{premaster_secret= EncPMS},
- #state{private_key = Key,
+ #state{connection_env = #connection_env{private_key = Key},
handshake_env = #handshake_env{client_hello_version = {Major, Minor} = Version}}
= State, Connection) ->
FakeSecret = make_premaster_secret(Version, rsa),
@@ -1700,14 +1810,15 @@ certify_client_key_exchange(#encrypted_premaster_secret{premaster_secret= EncPMS
end,
calculate_master_secret(PremasterSecret, State, Connection, certify, cipher);
certify_client_key_exchange(#client_diffie_hellman_public{dh_public = ClientPublicDhKey},
- #state{diffie_hellman_params = #'DHParameter'{} = Params,
- diffie_hellman_keys = {_, ServerDhPrivateKey}} = State,
+ #state{handshake_env = #handshake_env{diffie_hellman_params = #'DHParameter'{} = Params,
+ kex_keys = {_, ServerDhPrivateKey}}
+ } = State,
Connection) ->
PremasterSecret = ssl_handshake:premaster_secret(ClientPublicDhKey, ServerDhPrivateKey, Params),
calculate_master_secret(PremasterSecret, State, Connection, certify, cipher);
certify_client_key_exchange(#client_ec_diffie_hellman_public{dh_public = ClientPublicEcDhPoint},
- #state{diffie_hellman_keys = ECDHKey} = State, Connection) ->
+ #state{handshake_env = #handshake_env{kex_keys = ECDHKey}} = State, Connection) ->
PremasterSecret = ssl_handshake:premaster_secret(#'ECPoint'{point = ClientPublicEcDhPoint}, ECDHKey),
calculate_master_secret(PremasterSecret, State, Connection, certify, cipher);
certify_client_key_exchange(#client_psk_identity{} = ClientKey,
@@ -1717,8 +1828,8 @@ certify_client_key_exchange(#client_psk_identity{} = ClientKey,
PremasterSecret = ssl_handshake:premaster_secret(ClientKey, PSKLookup),
calculate_master_secret(PremasterSecret, State0, Connection, certify, cipher);
certify_client_key_exchange(#client_dhe_psk_identity{} = ClientKey,
- #state{diffie_hellman_params = #'DHParameter'{} = Params,
- diffie_hellman_keys = {_, ServerDhPrivateKey},
+ #state{handshake_env = #handshake_env{diffie_hellman_params = #'DHParameter'{} = Params,
+ kex_keys = {_, ServerDhPrivateKey}},
ssl_options =
#ssl_options{user_lookup_fun = PSKLookup}} = State0,
Connection) ->
@@ -1726,7 +1837,7 @@ certify_client_key_exchange(#client_dhe_psk_identity{} = ClientKey,
ssl_handshake:premaster_secret(ClientKey, ServerDhPrivateKey, Params, PSKLookup),
calculate_master_secret(PremasterSecret, State0, Connection, certify, cipher);
certify_client_key_exchange(#client_ecdhe_psk_identity{} = ClientKey,
- #state{diffie_hellman_keys = ServerEcDhPrivateKey,
+ #state{handshake_env = #handshake_env{kex_keys = ServerEcDhPrivateKey},
ssl_options =
#ssl_options{user_lookup_fun = PSKLookup}} = State,
Connection) ->
@@ -1734,25 +1845,26 @@ certify_client_key_exchange(#client_ecdhe_psk_identity{} = ClientKey,
ssl_handshake:premaster_secret(ClientKey, ServerEcDhPrivateKey, PSKLookup),
calculate_master_secret(PremasterSecret, State, Connection, certify, cipher);
certify_client_key_exchange(#client_rsa_psk_identity{} = ClientKey,
- #state{private_key = Key,
+ #state{connection_env = #connection_env{private_key = Key},
ssl_options =
#ssl_options{user_lookup_fun = PSKLookup}} = State0,
Connection) ->
PremasterSecret = ssl_handshake:premaster_secret(ClientKey, Key, PSKLookup),
calculate_master_secret(PremasterSecret, State0, Connection, certify, cipher);
certify_client_key_exchange(#client_srp_public{} = ClientKey,
- #state{srp_params = Params,
- srp_keys = Key
+ #state{handshake_env = #handshake_env{srp_params = Params,
+ kex_keys = Key}
} = State0, Connection) ->
PremasterSecret = ssl_handshake:premaster_secret(ClientKey, Key, Params),
calculate_master_secret(PremasterSecret, State0, Connection, certify, cipher).
-certify_server(#state{key_algorithm = Algo} = State, _) when Algo == dh_anon;
- Algo == ecdh_anon;
- Algo == psk;
- Algo == dhe_psk;
- Algo == ecdhe_psk;
- Algo == srp_anon ->
+certify_server(#state{handshake_env = #handshake_env{kex_algorithm = KexAlg}} =
+ State, _) when KexAlg == dh_anon;
+ KexAlg == ecdh_anon;
+ KexAlg == psk;
+ KexAlg == dhe_psk;
+ KexAlg == ecdhe_psk;
+ KexAlg == srp_anon ->
State;
certify_server(#state{static_env = #static_env{cert_db = CertDbHandle,
cert_db_ref = CertDbRef},
@@ -1764,18 +1876,19 @@ certify_server(#state{static_env = #static_env{cert_db = CertDbHandle,
throw(Alert)
end.
-key_exchange(#state{static_env = #static_env{role = server}, key_algorithm = rsa} = State,_) ->
+key_exchange(#state{static_env = #static_env{role = server},
+ handshake_env = #handshake_env{kex_algorithm = rsa}} = State,_) ->
State;
-key_exchange(#state{static_env = #static_env{role = server}, key_algorithm = Algo,
- hashsign_algorithm = HashSignAlgo,
- diffie_hellman_params = #'DHParameter'{} = Params,
- private_key = PrivateKey,
- connection_states = ConnectionStates0,
- negotiated_version = Version
- } = State0, Connection)
- when Algo == dhe_dss;
- Algo == dhe_rsa;
- Algo == dh_anon ->
+key_exchange(#state{static_env = #static_env{role = server},
+ handshake_env = #handshake_env{kex_algorithm = KexAlg,
+ diffie_hellman_params = #'DHParameter'{} = Params,
+ hashsign_algorithm = HashSignAlgo},
+ connection_env = #connection_env{negotiated_version = Version,
+ private_key = PrivateKey},
+ connection_states = ConnectionStates0} = State0, Connection)
+ when KexAlg == dhe_dss;
+ KexAlg == dhe_rsa;
+ KexAlg == dh_anon ->
DHKeys = public_key:generate_key(Params),
#{security_parameters := SecParams} =
ssl_record:pending_connection_state(ConnectionStates0, read),
@@ -1785,24 +1898,26 @@ key_exchange(#state{static_env = #static_env{role = server}, key_algorithm = Alg
HashSignAlgo, ClientRandom,
ServerRandom,
PrivateKey}),
- State = Connection:queue_handshake(Msg, State0),
- State#state{diffie_hellman_keys = DHKeys};
+ #state{handshake_env = HsEnv} = State = Connection:queue_handshake(Msg, State0),
+ State#state{handshake_env = HsEnv#handshake_env{kex_keys = DHKeys}};
key_exchange(#state{static_env = #static_env{role = server},
- private_key = #'ECPrivateKey'{parameters = ECCurve} = Key,
- key_algorithm = Algo,
+ handshake_env = #handshake_env{kex_algorithm = KexAlg} = HsEnv,
+ connection_env = #connection_env{private_key = #'ECPrivateKey'{parameters = ECCurve} = Key},
session = Session} = State, _)
- when Algo == ecdh_ecdsa; Algo == ecdh_rsa ->
- State#state{diffie_hellman_keys = Key,
+ when KexAlg == ecdh_ecdsa;
+ KexAlg == ecdh_rsa ->
+ State#state{handshake_env = HsEnv#handshake_env{kex_keys = Key},
session = Session#session{ecc = ECCurve}};
-key_exchange(#state{static_env = #static_env{role = server}, key_algorithm = Algo,
- hashsign_algorithm = HashSignAlgo,
- private_key = PrivateKey,
+key_exchange(#state{static_env = #static_env{role = server},
+ handshake_env = #handshake_env{kex_algorithm = KexAlg,
+ hashsign_algorithm = HashSignAlgo},
+ connection_env = #connection_env{negotiated_version = Version,
+ private_key = PrivateKey},
session = #session{ecc = ECCCurve},
- connection_states = ConnectionStates0,
- negotiated_version = Version
- } = State0, Connection)
- when Algo == ecdhe_ecdsa; Algo == ecdhe_rsa;
- Algo == ecdh_anon ->
+ connection_states = ConnectionStates0} = State0, Connection)
+ when KexAlg == ecdhe_ecdsa;
+ KexAlg == ecdhe_rsa;
+ KexAlg == ecdh_anon ->
ECDHKeys = public_key:generate_key(ECCCurve),
#{security_parameters := SecParams} =
@@ -1814,18 +1929,19 @@ key_exchange(#state{static_env = #static_env{role = server}, key_algorithm = Alg
HashSignAlgo, ClientRandom,
ServerRandom,
PrivateKey}),
- State = Connection:queue_handshake(Msg, State0),
- State#state{diffie_hellman_keys = ECDHKeys};
-key_exchange(#state{static_env = #static_env{role = server}, key_algorithm = psk,
+ #state{handshake_env = HsEnv} = State = Connection:queue_handshake(Msg, State0),
+ State#state{handshake_env = HsEnv#handshake_env{kex_keys = ECDHKeys}};
+key_exchange(#state{static_env = #static_env{role = server},
+ handshake_env = #handshake_env{kex_algorithm = psk},
ssl_options = #ssl_options{psk_identity = undefined}} = State, _) ->
State;
-key_exchange(#state{static_env = #static_env{role = server}, key_algorithm = psk,
+key_exchange(#state{static_env = #static_env{role = server},
ssl_options = #ssl_options{psk_identity = PskIdentityHint},
- hashsign_algorithm = HashSignAlgo,
- private_key = PrivateKey,
- connection_states = ConnectionStates0,
- negotiated_version = Version
- } = State0, Connection) ->
+ handshake_env = #handshake_env{kex_algorithm = psk,
+ hashsign_algorithm = HashSignAlgo},
+ connection_env = #connection_env{negotiated_version = Version,
+ private_key = PrivateKey},
+ connection_states = ConnectionStates0} = State0, Connection) ->
#{security_parameters := SecParams} =
ssl_record:pending_connection_state(ConnectionStates0, read),
#security_parameters{client_random = ClientRandom,
@@ -1834,15 +1950,16 @@ key_exchange(#state{static_env = #static_env{role = server}, key_algorithm = psk
{psk, PskIdentityHint,
HashSignAlgo, ClientRandom,
ServerRandom,
- PrivateKey}),
+ PrivateKey}),
Connection:queue_handshake(Msg, State0);
-key_exchange(#state{static_env = #static_env{role = server}, key_algorithm = dhe_psk,
+key_exchange(#state{static_env = #static_env{role = server},
ssl_options = #ssl_options{psk_identity = PskIdentityHint},
- hashsign_algorithm = HashSignAlgo,
- diffie_hellman_params = #'DHParameter'{} = Params,
- private_key = PrivateKey,
- connection_states = ConnectionStates0,
- negotiated_version = Version
+ handshake_env = #handshake_env{kex_algorithm = dhe_psk,
+ diffie_hellman_params = #'DHParameter'{} = Params,
+ hashsign_algorithm = HashSignAlgo},
+ connection_env = #connection_env{negotiated_version = Version,
+ private_key = PrivateKey},
+ connection_states = ConnectionStates0
} = State0, Connection) ->
DHKeys = public_key:generate_key(Params),
#{security_parameters := SecParams} =
@@ -1855,15 +1972,16 @@ key_exchange(#state{static_env = #static_env{role = server}, key_algorithm = dhe
HashSignAlgo, ClientRandom,
ServerRandom,
PrivateKey}),
- State = Connection:queue_handshake(Msg, State0),
- State#state{diffie_hellman_keys = DHKeys};
-key_exchange(#state{static_env = #static_env{role = server}, key_algorithm = ecdhe_psk,
+ #state{handshake_env = HsEnv} = State = Connection:queue_handshake(Msg, State0),
+ State#state{handshake_env = HsEnv#handshake_env{kex_keys = DHKeys}};
+key_exchange(#state{static_env = #static_env{role = server},
ssl_options = #ssl_options{psk_identity = PskIdentityHint},
- hashsign_algorithm = HashSignAlgo,
- private_key = PrivateKey,
+ handshake_env = #handshake_env{kex_algorithm = ecdhe_psk,
+ hashsign_algorithm = HashSignAlgo},
+ connection_env = #connection_env{negotiated_version = Version,
+ private_key = PrivateKey},
session = #session{ecc = ECCCurve},
- connection_states = ConnectionStates0,
- negotiated_version = Version
+ connection_states = ConnectionStates0
} = State0, Connection) ->
ECDHKeys = public_key:generate_key(ECCCurve),
#{security_parameters := SecParams} =
@@ -1876,17 +1994,19 @@ key_exchange(#state{static_env = #static_env{role = server}, key_algorithm = ecd
HashSignAlgo, ClientRandom,
ServerRandom,
PrivateKey}),
- State = Connection:queue_handshake(Msg, State0),
- State#state{diffie_hellman_keys = ECDHKeys};
-key_exchange(#state{static_env = #static_env{role = server}, key_algorithm = rsa_psk,
+ #state{handshake_env = HsEnv} = State = Connection:queue_handshake(Msg, State0),
+ State#state{handshake_env = HsEnv#handshake_env{kex_keys = ECDHKeys}};
+key_exchange(#state{static_env = #static_env{role = server},
+ handshake_env = #handshake_env{kex_algorithm = rsa_psk},
ssl_options = #ssl_options{psk_identity = undefined}} = State, _) ->
State;
-key_exchange(#state{static_env = #static_env{role = server}, key_algorithm = rsa_psk,
+key_exchange(#state{static_env = #static_env{role = server},
ssl_options = #ssl_options{psk_identity = PskIdentityHint},
- hashsign_algorithm = HashSignAlgo,
- private_key = PrivateKey,
- connection_states = ConnectionStates0,
- negotiated_version = Version
+ handshake_env = #handshake_env{kex_algorithm = rsa_psk,
+ hashsign_algorithm = HashSignAlgo},
+ connection_env = #connection_env{negotiated_version = Version,
+ private_key = PrivateKey},
+ connection_states = ConnectionStates0
} = State0, Connection) ->
#{security_parameters := SecParams} =
ssl_record:pending_connection_state(ConnectionStates0, read),
@@ -1898,17 +2018,18 @@ key_exchange(#state{static_env = #static_env{role = server}, key_algorithm = rsa
ServerRandom,
PrivateKey}),
Connection:queue_handshake(Msg, State0);
-key_exchange(#state{static_env = #static_env{role = server}, key_algorithm = Algo,
+key_exchange(#state{static_env = #static_env{role = server},
ssl_options = #ssl_options{user_lookup_fun = LookupFun},
- hashsign_algorithm = HashSignAlgo,
+ handshake_env = #handshake_env{kex_algorithm = KexAlg,
+ hashsign_algorithm = HashSignAlgo},
+ connection_env = #connection_env{negotiated_version = Version,
+ private_key = PrivateKey},
session = #session{srp_username = Username},
- private_key = PrivateKey,
- connection_states = ConnectionStates0,
- negotiated_version = Version
+ connection_states = ConnectionStates0
} = State0, Connection)
- when Algo == srp_dss;
- Algo == srp_rsa;
- Algo == srp_anon ->
+ when KexAlg == srp_dss;
+ KexAlg == srp_rsa;
+ KexAlg == srp_anon ->
SrpParams = handle_srp_identity(Username, LookupFun),
Keys = case generate_srp_server_keys(SrpParams, 0) of
Alert = #alert{} ->
@@ -1925,82 +2046,86 @@ key_exchange(#state{static_env = #static_env{role = server}, key_algorithm = Alg
HashSignAlgo, ClientRandom,
ServerRandom,
PrivateKey}),
- State = Connection:queue_handshake(Msg, State0),
- State#state{srp_params = SrpParams,
- srp_keys = Keys};
+ #state{handshake_env = HsEnv} = State = Connection:queue_handshake(Msg, State0),
+ State#state{handshake_env = HsEnv#handshake_env{srp_params = SrpParams,
+ kex_keys = Keys}};
key_exchange(#state{static_env = #static_env{role = client},
- key_algorithm = rsa,
- public_key_info = PublicKeyInfo,
- negotiated_version = Version,
- premaster_secret = PremasterSecret} = State0, Connection) ->
+ handshake_env = #handshake_env{kex_algorithm = rsa,
+ public_key_info = PublicKeyInfo,
+ premaster_secret = PremasterSecret},
+ connection_env = #connection_env{negotiated_version = Version}
+ } = State0, Connection) ->
Msg = rsa_key_exchange(ssl:tls_version(Version), PremasterSecret, PublicKeyInfo),
Connection:queue_handshake(Msg, State0);
key_exchange(#state{static_env = #static_env{role = client},
- key_algorithm = Algorithm,
- negotiated_version = Version,
- diffie_hellman_keys = {DhPubKey, _}
- } = State0, Connection)
- when Algorithm == dhe_dss;
- Algorithm == dhe_rsa;
- Algorithm == dh_anon ->
+ handshake_env = #handshake_env{kex_algorithm = KexAlg,
+ kex_keys = {DhPubKey, _}},
+ connection_env = #connection_env{negotiated_version = Version}
+ } = State0, Connection)
+ when KexAlg == dhe_dss;
+ KexAlg == dhe_rsa;
+ KexAlg == dh_anon ->
Msg = ssl_handshake:key_exchange(client, ssl:tls_version(Version), {dh, DhPubKey}),
Connection:queue_handshake(Msg, State0);
key_exchange(#state{static_env = #static_env{role = client},
- key_algorithm = Algorithm,
- negotiated_version = Version,
- session = Session,
- diffie_hellman_keys = #'ECPrivateKey'{parameters = ECCurve} = Key} = State0, Connection)
- when Algorithm == ecdhe_ecdsa; Algorithm == ecdhe_rsa;
- Algorithm == ecdh_ecdsa; Algorithm == ecdh_rsa;
- Algorithm == ecdh_anon ->
+ handshake_env = #handshake_env{kex_algorithm = KexAlg,
+ kex_keys = #'ECPrivateKey'{parameters = ECCurve} = Key},
+ connection_env = #connection_env{negotiated_version = Version},
+ session = Session
+ } = State0, Connection)
+ when KexAlg == ecdhe_ecdsa;
+ KexAlg == ecdhe_rsa;
+ KexAlg == ecdh_ecdsa;
+ KexAlg == ecdh_rsa;
+ KexAlg == ecdh_anon ->
Msg = ssl_handshake:key_exchange(client, ssl:tls_version(Version), {ecdh, Key}),
Connection:queue_handshake(Msg, State0#state{session = Session#session{ecc = ECCurve}});
key_exchange(#state{static_env = #static_env{role = client},
- ssl_options = SslOpts,
- key_algorithm = psk,
- negotiated_version = Version} = State0, Connection) ->
+ handshake_env = #handshake_env{kex_algorithm = psk},
+ connection_env = #connection_env{negotiated_version = Version},
+ ssl_options = SslOpts} = State0, Connection) ->
Msg = ssl_handshake:key_exchange(client, ssl:tls_version(Version),
{psk, SslOpts#ssl_options.psk_identity}),
Connection:queue_handshake(Msg, State0);
key_exchange(#state{static_env = #static_env{role = client},
- ssl_options = SslOpts,
- key_algorithm = dhe_psk,
- negotiated_version = Version,
- diffie_hellman_keys = {DhPubKey, _}} = State0, Connection) ->
+ handshake_env = #handshake_env{kex_algorithm = dhe_psk,
+ kex_keys = {DhPubKey, _}},
+ connection_env = #connection_env{negotiated_version = Version},
+ ssl_options = SslOpts} = State0, Connection) ->
Msg = ssl_handshake:key_exchange(client, ssl:tls_version(Version),
{dhe_psk,
SslOpts#ssl_options.psk_identity, DhPubKey}),
Connection:queue_handshake(Msg, State0);
key_exchange(#state{static_env = #static_env{role = client},
- ssl_options = SslOpts,
- key_algorithm = ecdhe_psk,
- negotiated_version = Version,
- diffie_hellman_keys = ECDHKeys} = State0, Connection) ->
+ handshake_env = #handshake_env{kex_algorithm = ecdhe_psk,
+ kex_keys = ECDHKeys},
+ connection_env = #connection_env{negotiated_version = Version},
+ ssl_options = SslOpts} = State0, Connection) ->
Msg = ssl_handshake:key_exchange(client, ssl:tls_version(Version),
{ecdhe_psk,
SslOpts#ssl_options.psk_identity, ECDHKeys}),
Connection:queue_handshake(Msg, State0);
key_exchange(#state{static_env = #static_env{role = client},
- ssl_options = SslOpts,
- key_algorithm = rsa_psk,
- public_key_info = PublicKeyInfo,
- negotiated_version = Version,
- premaster_secret = PremasterSecret}
+ handshake_env = #handshake_env{kex_algorithm = rsa_psk,
+ public_key_info = PublicKeyInfo,
+ premaster_secret = PremasterSecret},
+ connection_env = #connection_env{negotiated_version = Version},
+ ssl_options = SslOpts}
= State0, Connection) ->
Msg = rsa_psk_key_exchange(ssl:tls_version(Version), SslOpts#ssl_options.psk_identity,
PremasterSecret, PublicKeyInfo),
Connection:queue_handshake(Msg, State0);
key_exchange(#state{static_env = #static_env{role = client},
- key_algorithm = Algorithm,
- negotiated_version = Version,
- srp_keys = {ClientPubKey, _}}
+ handshake_env = #handshake_env{kex_algorithm = KexAlg,
+ kex_keys = {ClientPubKey, _}},
+ connection_env = #connection_env{negotiated_version = Version}}
= State0, Connection)
- when Algorithm == srp_dss;
- Algorithm == srp_rsa;
- Algorithm == srp_anon ->
+ when KexAlg == srp_dss;
+ KexAlg == srp_rsa;
+ KexAlg == srp_anon ->
Msg = ssl_handshake:key_exchange(client, ssl:tls_version(Version), {srp, ClientPubKey}),
Connection:queue_handshake(Msg, State0).
@@ -2037,18 +2162,24 @@ rsa_psk_key_exchange(Version, PskIdentity, PremasterSecret,
rsa_psk_key_exchange(_, _, _, _) ->
throw (?ALERT_REC(?FATAL,?HANDSHAKE_FAILURE, pub_key_is_not_rsa)).
-request_client_cert(#state{key_algorithm = Alg} = State, _)
- when Alg == dh_anon; Alg == ecdh_anon;
- Alg == psk; Alg == dhe_psk; Alg == ecdhe_psk; Alg == rsa_psk;
- Alg == srp_dss; Alg == srp_rsa; Alg == srp_anon ->
+request_client_cert(#state{handshake_env = #handshake_env{kex_algorithm = Alg}} = State, _)
+ when Alg == dh_anon;
+ Alg == ecdh_anon;
+ Alg == psk;
+ Alg == dhe_psk;
+ Alg == ecdhe_psk;
+ Alg == rsa_psk;
+ Alg == srp_dss;
+ Alg == srp_rsa;
+ Alg == srp_anon ->
State;
request_client_cert(#state{static_env = #static_env{cert_db = CertDbHandle,
cert_db_ref = CertDbRef},
+ connection_env = #connection_env{negotiated_version = Version},
ssl_options = #ssl_options{verify = verify_peer,
signature_algs = SupportedHashSigns},
- connection_states = ConnectionStates0,
- negotiated_version = Version} = State0, Connection) ->
+ connection_states = ConnectionStates0} = State0, Connection) ->
#{security_parameters :=
#security_parameters{cipher_suite = CipherSuite}} =
ssl_record:pending_connection_state(ConnectionStates0, read),
@@ -2065,7 +2196,7 @@ request_client_cert(#state{ssl_options = #ssl_options{verify = verify_none}} =
State.
calculate_master_secret(PremasterSecret,
- #state{negotiated_version = Version,
+ #state{connection_env = #connection_env{negotiated_version = Version},
connection_states = ConnectionStates0,
session = Session0} = State0, Connection,
_Current, Next) ->
@@ -2094,11 +2225,11 @@ finalize_handshake(State0, StateName, Connection) ->
next_protocol(#state{static_env = #static_env{role = server}} = State, _) ->
State;
-next_protocol(#state{negotiated_protocol = undefined} = State, _) ->
+next_protocol(#state{handshake_env = #handshake_env{negotiated_protocol = undefined}} = State, _) ->
State;
-next_protocol(#state{expecting_next_protocol_negotiation = false} = State, _) ->
+next_protocol(#state{handshake_env = #handshake_env{expecting_next_protocol_negotiation = false}} = State, _) ->
State;
-next_protocol(#state{negotiated_protocol = NextProtocol} = State0, Connection) ->
+next_protocol(#state{handshake_env = #handshake_env{negotiated_protocol = NextProtocol}} = State0, Connection) ->
NextProtocolMessage = ssl_handshake:next_protocol(NextProtocol),
Connection:queue_handshake(NextProtocolMessage, State0).
@@ -2107,7 +2238,7 @@ cipher_protocol(State, Connection) ->
finished(#state{static_env = #static_env{role = Role},
handshake_env = #handshake_env{tls_handshake_history = Hist},
- negotiated_version = Version,
+ connection_env = #connection_env{negotiated_version = Version},
session = Session,
connection_states = ConnectionStates0} = State0,
StateName, Connection) ->
@@ -2130,65 +2261,71 @@ save_verify_data(server, #finished{verify_data = Data}, ConnectionStates, abbrev
calculate_secret(#server_dh_params{dh_p = Prime, dh_g = Base,
dh_y = ServerPublicDhKey} = Params,
- State, Connection) ->
+ #state{handshake_env = HsEnv} = State, Connection) ->
Keys = {_, PrivateDhKey} = crypto:generate_key(dh, [Prime, Base]),
PremasterSecret =
ssl_handshake:premaster_secret(ServerPublicDhKey, PrivateDhKey, Params),
calculate_master_secret(PremasterSecret,
- State#state{diffie_hellman_keys = Keys},
+ State#state{handshake_env = HsEnv#handshake_env{kex_keys = Keys}},
Connection, certify, certify);
calculate_secret(#server_ecdh_params{curve = ECCurve, public = ECServerPubKey},
- State=#state{session=Session}, Connection) ->
+ #state{handshake_env = HsEnv,
+ session = Session} = State, Connection) ->
ECDHKeys = public_key:generate_key(ECCurve),
PremasterSecret =
ssl_handshake:premaster_secret(#'ECPoint'{point = ECServerPubKey}, ECDHKeys),
calculate_master_secret(PremasterSecret,
- State#state{diffie_hellman_keys = ECDHKeys,
+ State#state{handshake_env = HsEnv#handshake_env{kex_keys = ECDHKeys},
session = Session#session{ecc = ECCurve}},
Connection, certify, certify);
calculate_secret(#server_psk_params{
hint = IdentityHint},
- State, Connection) ->
+ #state{handshake_env = HsEnv} = State, Connection) ->
%% store for later use
- Connection:next_event(certify, no_record, State#state{psk_identity = IdentityHint});
+ Connection:next_event(certify, no_record,
+ State#state{handshake_env =
+ HsEnv#handshake_env{server_psk_identity = IdentityHint}});
calculate_secret(#server_dhe_psk_params{
dh_params = #server_dh_params{dh_p = Prime, dh_g = Base}} = ServerKey,
- #state{ssl_options = #ssl_options{user_lookup_fun = PSKLookup}} =
+ #state{handshake_env = HsEnv,
+ ssl_options = #ssl_options{user_lookup_fun = PSKLookup}} =
State, Connection) ->
Keys = {_, PrivateDhKey} =
crypto:generate_key(dh, [Prime, Base]),
PremasterSecret = ssl_handshake:premaster_secret(ServerKey, PrivateDhKey, PSKLookup),
- calculate_master_secret(PremasterSecret, State#state{diffie_hellman_keys = Keys},
+ calculate_master_secret(PremasterSecret, State#state{handshake_env = HsEnv#handshake_env{kex_keys = Keys}},
Connection, certify, certify);
calculate_secret(#server_ecdhe_psk_params{
dh_params = #server_ecdh_params{curve = ECCurve}} = ServerKey,
#state{ssl_options = #ssl_options{user_lookup_fun = PSKLookup}} =
- State=#state{session=Session}, Connection) ->
+ #state{handshake_env = HsEnv,
+ session = Session} = State, Connection) ->
ECDHKeys = public_key:generate_key(ECCurve),
PremasterSecret = ssl_handshake:premaster_secret(ServerKey, ECDHKeys, PSKLookup),
calculate_master_secret(PremasterSecret,
- State#state{diffie_hellman_keys = ECDHKeys,
+ State#state{handshake_env = HsEnv#handshake_env{kex_keys = ECDHKeys},
session = Session#session{ecc = ECCurve}},
Connection, certify, certify);
calculate_secret(#server_srp_params{srp_n = Prime, srp_g = Generator} = ServerKey,
- #state{ssl_options = #ssl_options{srp_identity = SRPId}} = State,
+ #state{handshake_env = HsEnv,
+ ssl_options = #ssl_options{srp_identity = SRPId}} = State,
Connection) ->
Keys = generate_srp_client_keys(Generator, Prime, 0),
PremasterSecret = ssl_handshake:premaster_secret(ServerKey, Keys, SRPId),
- calculate_master_secret(PremasterSecret, State#state{srp_keys = Keys}, Connection,
+ calculate_master_secret(PremasterSecret, State#state{handshake_env = HsEnv#handshake_env{kex_keys = Keys}}, Connection,
certify, certify).
master_secret(#alert{} = Alert, _) ->
Alert;
master_secret(PremasterSecret, #state{static_env = #static_env{role = Role},
+ connection_env = #connection_env{negotiated_version = Version},
session = Session,
- negotiated_version = Version,
connection_states = ConnectionStates0} = State) ->
case ssl_handshake:master_secret(ssl:tls_version(Version), PremasterSecret,
ConnectionStates0, Role) of
@@ -2248,7 +2385,7 @@ cipher_role(client, Data, Session, #state{connection_states = ConnectionStates0}
{Record, State} = prepare_connection(State0#state{session = Session,
connection_states = ConnectionStates},
Connection),
- Connection:next_event(connection, Record, State);
+ Connection:next_event(connection, Record, State, [{{timeout, handshake}, infinity, close}]);
cipher_role(server, Data, Session, #state{connection_states = ConnectionStates0} = State0,
Connection) ->
ConnectionStates1 = ssl_record:set_client_verify_data(current_read, Data,
@@ -2257,15 +2394,15 @@ cipher_role(server, Data, Session, #state{connection_states = ConnectionStates0
finalize_handshake(State0#state{connection_states = ConnectionStates1,
session = Session}, cipher, Connection),
{Record, State} = prepare_connection(State1, Connection),
- Connection:next_event(connection, Record, State, Actions).
-
-is_anonymous(Algo) when Algo == dh_anon;
- Algo == ecdh_anon;
- Algo == psk;
- Algo == dhe_psk;
- Algo == ecdhe_psk;
- Algo == rsa_psk;
- Algo == srp_anon ->
+ Connection:next_event(connection, Record, State, [{{timeout, handshake}, infinity, close} | Actions]).
+
+is_anonymous(KexAlg) when KexAlg == dh_anon;
+ KexAlg == ecdh_anon;
+ KexAlg == psk;
+ KexAlg == dhe_psk;
+ KexAlg == ecdhe_psk;
+ KexAlg == rsa_psk;
+ KexAlg == srp_anon ->
true;
is_anonymous(_) ->
false.
@@ -2443,21 +2580,13 @@ ack_connection(#state{handshake_env = #handshake_env{renegotiation = {true, From
gen_statem:reply(From, ok),
State#state{handshake_env = HsEnv#handshake_env{renegotiation = undefined}};
ack_connection(#state{handshake_env = #handshake_env{renegotiation = {false, first}} = HsEnv,
- start_or_recv_from = StartFrom,
- timer = Timer} = State) when StartFrom =/= undefined ->
+ start_or_recv_from = StartFrom} = State) when StartFrom =/= undefined ->
gen_statem:reply(StartFrom, connected),
- cancel_timer(Timer),
State#state{handshake_env = HsEnv#handshake_env{renegotiation = undefined},
- start_or_recv_from = undefined, timer = undefined};
+ start_or_recv_from = undefined};
ack_connection(State) ->
State.
-cancel_timer(undefined) ->
- ok;
-cancel_timer(Timer) ->
- erlang:cancel_timer(Timer),
- ok.
-
session_handle_params(#server_ecdh_params{curve = ECCurve}, Session) ->
Session#session{ecc = ECCurve};
session_handle_params(_, Session) ->
@@ -2513,9 +2642,8 @@ handle_resumed_session(SessId, #state{static_env = #static_env{host = Host,
protocol_cb = Connection,
session_cache = Cache,
session_cache_cb = CacheCb},
- connection_states = ConnectionStates0,
- negotiated_version = Version
- } = State) ->
+ connection_env = #connection_env{negotiated_version = Version},
+ connection_states = ConnectionStates0} = State) ->
Session = CacheCb:lookup(Cache, {{Host, Port}, SessId}),
case ssl_handshake:master_secret(ssl:tls_version(Version), Session,
ConnectionStates0, client) of
@@ -2573,7 +2701,7 @@ handle_active_option(false, connection = StateName, To, Reply, State) ->
hibernate_after(StateName, State, [{reply, To, Reply}]);
handle_active_option(_, connection = StateName0, To, Reply, #state{static_env = #static_env{protocol_cb = Connection},
- user_data_buffer = <<>>} = State0) ->
+ user_data_buffer = {_,0,_}} = State0) ->
case Connection:next_event(StateName0, no_record, State0) of
{next_state, StateName, State} ->
hibernate_after(StateName, State, [{reply, To, Reply}]);
@@ -2582,11 +2710,11 @@ handle_active_option(_, connection = StateName0, To, Reply, #state{static_env =
{stop, _, _} = Stop ->
Stop
end;
-handle_active_option(_, StateName, To, Reply, #state{user_data_buffer = <<>>} = State) ->
+handle_active_option(_, StateName, To, Reply, #state{user_data_buffer = {_,0,_}} = State) ->
%% Active once already set
{next_state, StateName, State, [{reply, To, Reply}]};
-%% user_data_buffer =/= <<>>
+%% user_data_buffer nonempty
handle_active_option(_, StateName0, To, Reply,
#state{static_env = #static_env{protocol_cb = Connection}} = State0) ->
case read_application_data(<<>>, State0) of
@@ -2606,33 +2734,25 @@ handle_active_option(_, StateName0, To, Reply,
%% Picks ClientData
-get_data(_, _, <<>>) ->
- {more, <<>>};
-%% Recv timed out save buffer data until next recv
-get_data(#socket_options{active=false}, undefined, Buffer) ->
- {passive, Buffer};
-get_data(#socket_options{active=Active, packet=Raw}, BytesToRead, Buffer)
+get_data(#socket_options{active=false}, undefined, _Bin) ->
+ %% Recv timed out save buffer data until next recv
+ passive;
+get_data(#socket_options{active=Active, packet=Raw}, BytesToRead, Bin)
when Raw =:= raw; Raw =:= 0 -> %% Raw Mode
- if
- Active =/= false orelse BytesToRead =:= 0 ->
+ case Bin of
+ <<_/binary>> when Active =/= false orelse BytesToRead =:= 0 ->
%% Active true or once, or passive mode recv(0)
- {ok, Buffer, <<>>};
- byte_size(Buffer) >= BytesToRead ->
+ {ok, Bin, <<>>};
+ <<Data:BytesToRead/binary, Rest/binary>> ->
%% Passive Mode, recv(Bytes)
- <<Data:BytesToRead/binary, Rest/binary>> = Buffer,
- {ok, Data, Rest};
- true ->
+ {ok, Data, Rest};
+ <<_/binary>> ->
%% Passive Mode not enough data
- {more, Buffer}
+ {more, BytesToRead}
end;
-get_data(#socket_options{packet=Type, packet_size=Size}, _, Buffer) ->
+get_data(#socket_options{packet=Type, packet_size=Size}, _, Bin) ->
PacketOpts = [{packet_size, Size}],
- case decode_packet(Type, Buffer, PacketOpts) of
- {more, _} ->
- {more, Buffer};
- Decoded ->
- Decoded
- end.
+ decode_packet(Type, Bin, PacketOpts).
decode_packet({http, headers}, Buffer, PacketOpts) ->
decode_packet(httph, Buffer, PacketOpts);
@@ -2684,7 +2804,7 @@ format_reply(_, _, _,#socket_options{active = false, mode = Mode, packet = Packe
{ok, do_format_reply(Mode, Packet, Header, Data)};
format_reply(CPids, Transport, Socket, #socket_options{active = _, mode = Mode, packet = Packet,
header = Header}, Data, Tracker, Connection) ->
- {ssl, Connection:socket(CPids, Transport, Socket, Connection, Tracker),
+ {ssl, Connection:socket(CPids, Transport, Socket, Tracker),
do_format_reply(Mode, Packet, Header, Data)}.
deliver_packet_error(CPids, Transport, Socket,
@@ -2696,7 +2816,7 @@ format_packet_error(_, _, _,#socket_options{active = false, mode = Mode}, Data,
{error, {invalid_packet, do_format_reply(Mode, raw, 0, Data)}};
format_packet_error(CPids, Transport, Socket, #socket_options{active = _, mode = Mode},
Data, Tracker, Connection) ->
- {ssl_error, Connection:socket(CPids, Transport, Socket, Connection, Tracker),
+ {ssl_error, Connection:socket(CPids, Transport, Socket, Tracker),
{invalid_packet, do_format_reply(Mode, raw, 0, Data)}}.
do_format_reply(binary, _, N, Data) when N > 0 -> % Header mode
@@ -2752,12 +2872,10 @@ alert_user(Pids, Transport, Tracker, Socket, Active, Pid, From, Alert, Role, Con
case ssl_alert:reason_code(Alert, Role) of
closed ->
send_or_reply(Active, Pid, From,
- {ssl_closed, Connection:socket(Pids,
- Transport, Socket, Connection, Tracker)});
+ {ssl_closed, Connection:socket(Pids, Transport, Socket, Tracker)});
ReasonCode ->
send_or_reply(Active, Pid, From,
- {ssl_error, Connection:socket(Pids,
- Transport, Socket, Connection, Tracker), ReasonCode})
+ {ssl_error, Connection:socket(Pids, Transport, Socket, Tracker), ReasonCode})
end.
log_alert(Level, Role, ProtocolName, StateName, #alert{role = Role} = Alert) ->
@@ -2776,7 +2894,9 @@ invalidate_session(server, _, Port, Session) ->
handle_sni_extension(undefined, State) ->
State;
-handle_sni_extension(#sni{hostname = Hostname}, #state{static_env = #static_env{role = Role} = InitStatEnv0} = State0) ->
+handle_sni_extension(#sni{hostname = Hostname}, #state{static_env = #static_env{role = Role} = InitStatEnv0,
+ handshake_env = HsEnv,
+ connection_env = CEnv} = State0) ->
NewOptions = update_ssl_options_from_sni(State0#state.ssl_options, Hostname),
case NewOptions of
undefined ->
@@ -2799,12 +2919,12 @@ handle_sni_extension(#sni{hostname = Hostname}, #state{static_env = #static_env{
cert_db = CertDbHandle,
crl_db = CRLDbHandle,
session_cache = CacheHandle
- },
- private_key = Key,
- diffie_hellman_params = DHParams,
- ssl_options = NewOptions,
- sni_hostname = Hostname
- }
+ },
+ connection_env = CEnv#connection_env{private_key = Key},
+ ssl_options = NewOptions,
+ handshake_env = HsEnv#handshake_env{sni_hostname = Hostname,
+ diffie_hellman_params = DHParams}
+ }
end.
update_ssl_options_from_sni(OrigSSLOptions, SNIHostname) ->
@@ -2827,11 +2947,3 @@ new_emulated([], EmOpts) ->
EmOpts;
new_emulated(NewEmOpts, _) ->
NewEmOpts.
-
--compile({inline, [bincat/2]}).
-bincat(<<>>, B) ->
- B;
-bincat(A, <<>>) ->
- A;
-bincat(A, B) ->
- <<A/binary, B/binary>>.
diff --git a/lib/ssl/src/ssl_connection.hrl b/lib/ssl/src/ssl_connection.hrl
index dd3bdd7478..201164949a 100644
--- a/lib/ssl/src/ssl_connection.hrl
+++ b/lib/ssl/src/ssl_connection.hrl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2013-2018. All Rights Reserved.
+%% Copyright Ericsson AB 2013-2019. 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,59 +57,64 @@
unprocessed_handshake_events = 0 :: integer(),
tls_handshake_history :: ssl_handshake:ssl_handshake_history() | secret_printout()
| 'undefined',
- renegotiation :: undefined | {boolean(), From::term() | internal | peer}
+ expecting_finished = false ::boolean(),
+ renegotiation :: undefined | {boolean(), From::term() | internal | peer},
+ allow_renegotiate = true ::boolean(),
+ %% Ext handling
+ hello, %%:: #client_hello{} | #server_hello{}
+ sni_hostname = undefined,
+ expecting_next_protocol_negotiation = false ::boolean(),
+ next_protocol = undefined :: undefined | binary(),
+ negotiated_protocol,
+ hashsign_algorithm = {undefined, undefined},
+ cert_hashsign_algorithm = {undefined, undefined},
+ %% key exchange
+ kex_algorithm :: ssl:kex_algo(),
+ kex_keys :: {PublicKey :: binary(), PrivateKey :: binary()} | #'ECPrivateKey'{} | undefined | secret_printout(),
+ diffie_hellman_params:: #'DHParameter'{} | undefined | secret_printout(),
+ srp_params :: #srp_user{} | secret_printout() | 'undefined',
+ public_key_info :: ssl_handshake:public_key_info() | 'undefined',
+ premaster_secret :: binary() | secret_printout() | 'undefined',
+ server_psk_identity :: binary() | 'undefined' % server psk identity hint
}).
+-record(connection_env, {
+ user_application :: {Monitor::reference(), User::pid()},
+ downgrade,
+ terminated = false ::boolean() | closed,
+ negotiated_version :: ssl_record:ssl_version() | 'undefined',
+ erl_dist_handle = undefined :: erlang:dist_handle() | 'undefined',
+ private_key :: public_key:private_key() | secret_printout() | 'undefined'
+ }).
+
-record(state, {
static_env :: #static_env{},
- handshake_env :: #handshake_env{} | secret_printout(),
- %% Change seldome
- user_application :: {Monitor::reference(), User::pid()},
+ connection_env :: #connection_env{} | secret_printout(),
ssl_options :: #ssl_options{},
socket_options :: #socket_options{},
- session :: #session{} | secret_printout(),
- allow_renegotiate = true ::boolean(),
- terminated = false ::boolean() | closed,
- negotiated_version :: ssl_record:ssl_version() | 'undefined',
- bytes_to_read :: undefined | integer(), %% bytes to read in passive mode
- downgrade,
- %% Changed often
+ %% Hanshake %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+ handshake_env :: #handshake_env{} | secret_printout(),
+ %% Buffer of TLS/DTLS records, used during the TLS
+ %% handshake to when possible pack more than one TLS
+ %% record into the underlaying packet
+ %% format. Introduced by DTLS - RFC 4347. The
+ %% mecahnism is also usefull in TLS although we do not
+ %% need to worry about packet loss in TLS. In DTLS we
+ %% need to track DTLS handshake seqnr
+ flight_buffer = [] :: list() | map(),
+ client_certificate_requested = false :: boolean(),
+ protocol_specific = #{} :: map(),
+ session :: #session{} | secret_printout(),
+ key_share,
+ %% Data shuffling %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
connection_states :: ssl_record:connection_states() | secret_printout(),
protocol_buffers :: term() | secret_printout() , %% #protocol_buffers{} from tls_record.hrl or dtls_recor.hr
- user_data_buffer :: undefined | binary() | secret_printout(),
-
- %% Used only in HS
-
- client_certificate_requested = false :: boolean(),
- key_algorithm :: ssl_cipher_format:key_algo(),
- hashsign_algorithm = {undefined, undefined},
- cert_hashsign_algorithm = {undefined, undefined},
- public_key_info :: ssl_handshake:public_key_info() | 'undefined',
- private_key :: public_key:private_key() | secret_printout() | 'undefined',
- diffie_hellman_params:: #'DHParameter'{} | undefined | secret_printout(),
- diffie_hellman_keys :: {PublicKey :: binary(), PrivateKey :: binary()} | #'ECPrivateKey'{} | undefined | secret_printout(),
- psk_identity :: binary() | 'undefined', % server psk identity hint
- srp_params :: #srp_user{} | secret_printout() | 'undefined',
- srp_keys ::{PublicKey :: binary(), PrivateKey :: binary()} | secret_printout() | 'undefined',
- premaster_secret :: binary() | secret_printout() | 'undefined',
+ user_data_buffer :: undefined | {[binary()],non_neg_integer(),[binary()]} | secret_printout(),
+ bytes_to_read :: undefined | integer(), %% bytes to read in passive mode
+ %% recv and start handling
start_or_recv_from :: term(),
- timer :: undefined | reference(), % start_or_recive_timer
- hello, %%:: #client_hello{} | #server_hello{},
- expecting_next_protocol_negotiation = false ::boolean(),
- expecting_finished = false ::boolean(),
- next_protocol = undefined :: undefined | binary(),
- negotiated_protocol,
- sni_hostname = undefined,
- flight_buffer = [] :: list() | map(), %% Buffer of TLS/DTLS records, used during the TLS handshake
- %% to when possible pack more than one TLS record into the
- %% underlaying packet format. Introduced by DTLS - RFC 4347.
- %% The mecahnism is also usefull in TLS although we do not
- %% need to worry about packet loss in TLS. In DTLS we need to track DTLS handshake seqnr
- flight_state = reliable, %% reliable | {retransmit, integer()}| {waiting, ref(), integer()} - last two is used in DTLS over udp.
- erl_dist_handle = undefined :: erlang:dist_handle() | undefined,
- protocol_specific = #{} :: map(),
- key_share
+ log_level
}).
-define(DEFAULT_DIFFIE_HELLMAN_PARAMS,
diff --git a/lib/ssl/src/ssl_crl_cache.erl b/lib/ssl/src/ssl_crl_cache.erl
index 9c1af86eeb..841620ce57 100644
--- a/lib/ssl/src/ssl_crl_cache.erl
+++ b/lib/ssl/src/ssl_crl_cache.erl
@@ -28,6 +28,10 @@
-behaviour(ssl_crl_cache_api).
+-export_type([crl_src/0, uri/0]).
+-type crl_src() :: {file, file:filename()} | {der, public_key:der_encoded()}.
+-type uri() :: uri_string:uri_string().
+
-export([lookup/3, select/2, fresh_crl/2]).
-export([insert/1, insert/2, delete/1]).
diff --git a/lib/ssl/src/ssl_crl_cache_api.erl b/lib/ssl/src/ssl_crl_cache_api.erl
index d5380583e7..8a750b3929 100644
--- a/lib/ssl/src/ssl_crl_cache_api.erl
+++ b/lib/ssl/src/ssl_crl_cache_api.erl
@@ -21,12 +21,15 @@
%%
-module(ssl_crl_cache_api).
-
-include_lib("public_key/include/public_key.hrl").
--type db_handle() :: term().
--type issuer_name() :: {rdnSequence, [#'AttributeTypeAndValue'{}]}.
+-export_type([dist_point/0, crl_cache_ref/0]).
+
+-type crl_cache_ref() :: any().
+-type issuer_name() :: {rdnSequence,[#'AttributeTypeAndValue'{}]}.
+-type dist_point() :: #'DistributionPoint'{}.
--callback lookup(#'DistributionPoint'{}, issuer_name(), db_handle()) -> not_available | [public_key:der_encoded()].
--callback select(issuer_name(), db_handle()) -> [public_key:der_encoded()].
--callback fresh_crl(#'DistributionPoint'{}, public_key:der_encoded()) -> public_key:der_encoded().
+
+-callback lookup(dist_point(), issuer_name(), crl_cache_ref()) -> not_available | [public_key:der_encoded()].
+-callback select(issuer_name(), crl_cache_ref()) -> [public_key:der_encoded()].
+-callback fresh_crl(dist_point(), public_key:der_encoded()) -> public_key:der_encoded().
diff --git a/lib/ssl/src/ssl_handshake.erl b/lib/ssl/src/ssl_handshake.erl
index a28f4add1b..6b1e3b6e07 100644
--- a/lib/ssl/src/ssl_handshake.erl
+++ b/lib/ssl/src/ssl_handshake.erl
@@ -833,7 +833,7 @@ decode_extensions(Extensions, Version, MessageType) ->
decode_extensions(Extensions, Version, MessageType, empty_extensions()).
%%--------------------------------------------------------------------
--spec decode_server_key(binary(), ssl_cipher_format:key_algo(), ssl_record:ssl_version()) ->
+-spec decode_server_key(binary(), ssl:kex_algo(), ssl_record:ssl_version()) ->
#server_key_params{}.
%%
%% Description: Decode server_key data and return appropriate type
@@ -842,7 +842,7 @@ decode_server_key(ServerKey, Type, Version) ->
dec_server_key(ServerKey, key_exchange_alg(Type), Version).
%%--------------------------------------------------------------------
--spec decode_client_key(binary(), ssl_cipher_format:key_algo(), ssl_record:ssl_version()) ->
+-spec decode_client_key(binary(), ssl:kex_algo(), ssl_record:ssl_version()) ->
#encrypted_premaster_secret{}
| #client_diffie_hellman_public{}
| #client_ec_diffie_hellman_public{}
diff --git a/lib/ssl/src/ssl_internal.hrl b/lib/ssl/src/ssl_internal.hrl
index 57b72366d3..3d117a655f 100644
--- a/lib/ssl/src/ssl_internal.hrl
+++ b/lib/ssl/src/ssl_internal.hrl
@@ -32,8 +32,6 @@
-type reply() :: term().
-type msg() :: term().
-type from() :: term().
--type host() :: inet:ip_address() | inet:hostname().
--type session_id() :: 0 | binary().
-type certdb_ref() :: reference().
-type db_handle() :: term().
-type der_cert() :: binary().
@@ -124,7 +122,8 @@
cert :: public_key:der_encoded() | secret_printout() | 'undefined',
keyfile :: binary(),
key :: {'RSAPrivateKey' | 'DSAPrivateKey' | 'ECPrivateKey' | 'PrivateKeyInfo',
- public_key:der_encoded()} | key_map() | secret_printout() | 'undefined',
+ public_key:der_encoded()} | map() %%map() -> ssl:key() how to handle dialyzer?
+ | secret_printout() | 'undefined',
password :: string() | secret_printout() | 'undefined',
cacerts :: [public_key:der_encoded()] | secret_printout() | 'undefined',
cacertfile :: binary(),
@@ -198,15 +197,6 @@
connection_cb
}).
--type key_map() :: #{algorithm := rsa | dss | ecdsa,
- %% engine and key_id ought to
- %% be :=, but putting it in
- %% the spec gives dialyzer warning
- %% of correct code!
- engine => crypto:engine_ref(),
- key_id => crypto:key_id(),
- password => crypto:password()
- }.
-type state_name() :: hello | abbreviated | certify | cipher | connection.
-type gen_fsm_state_return() :: {next_state, state_name(), term()} |
{next_state, state_name(), term(), timeout()} |
diff --git a/lib/ssl/src/ssl_logger.erl b/lib/ssl/src/ssl_logger.erl
index c4dd2dad60..d59a0dfda2 100644
--- a/lib/ssl/src/ssl_logger.erl
+++ b/lib/ssl/src/ssl_logger.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1999-2018. All Rights Reserved.
+%% Copyright Ericsson AB 1999-2019. 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,13 +46,19 @@
format(#{level:= _Level, msg:= {report, Msg}, meta:= _Meta}, _Config0) ->
#{direction := Direction,
protocol := Protocol,
- message := BinMsg0} = Msg,
+ message := Content} = Msg,
case Protocol of
'tls_record' ->
- BinMsg = lists:flatten(BinMsg0),
+ BinMsg =
+ case Content of
+ #ssl_tls{} ->
+ [tls_record:build_tls_record(Content)];
+ _ when is_list(Content) ->
+ lists:flatten(Content)
+ end,
format_tls_record(Direction, BinMsg);
'handshake' ->
- format_handshake(Direction, BinMsg0);
+ format_handshake(Direction, Content);
_Other ->
[]
end.
diff --git a/lib/ssl/src/ssl_manager.erl b/lib/ssl/src/ssl_manager.erl
index b1f080b0fe..456a560bf6 100644
--- a/lib/ssl/src/ssl_manager.erl
+++ b/lib/ssl/src/ssl_manager.erl
@@ -42,6 +42,8 @@
-include("ssl_handshake.hrl").
-include("ssl_internal.hrl").
+-include("ssl_api.hrl").
+
-include_lib("kernel/include/file.hrl").
-record(state, {
@@ -148,7 +150,7 @@ lookup_trusted_cert(DbHandle, Ref, SerialNumber, Issuer) ->
ssl_pkix_db:lookup_trusted_cert(DbHandle, Ref, SerialNumber, Issuer).
%%--------------------------------------------------------------------
--spec new_session_id(integer()) -> session_id().
+-spec new_session_id(integer()) -> ssl:session_id().
%%
%% Description: Creates a session id for the server.
%%--------------------------------------------------------------------
@@ -170,7 +172,7 @@ clean_cert_db(Ref, File) ->
%%
%% Description: Make the session available for reuse.
%%--------------------------------------------------------------------
--spec register_session(host(), inet:port_number(), #session{}, unique | true) -> ok.
+-spec register_session(ssl:host(), inet:port_number(), #session{}, unique | true) -> ok.
register_session(Host, Port, Session, true) ->
call({register_session, Host, Port, Session});
register_session(Host, Port, Session, unique = Save) ->
@@ -185,7 +187,7 @@ register_session(Port, Session) ->
%% a the session has been marked "is_resumable = false" for some while
%% it will be safe to remove the data from the session database.
%%--------------------------------------------------------------------
--spec invalidate_session(host(), inet:port_number(), #session{}) -> ok.
+-spec invalidate_session(ssl:host(), inet:port_number(), #session{}) -> ok.
invalidate_session(Host, Port, Session) ->
load_mitigation(),
cast({invalidate_session, Host, Port, Session}).
diff --git a/lib/ssl/src/ssl_record.erl b/lib/ssl/src/ssl_record.erl
index d0a72ce51f..91f1876980 100644
--- a/lib/ssl/src/ssl_record.erl
+++ b/lib/ssl/src/ssl_record.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2013-2018. All Rights Reserved.
+%% Copyright Ericsson AB 2013-2019. 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,16 @@
-export([compress/3, uncompress/3, compressions/0]).
%% Payload encryption/decryption
--export([cipher/4, decipher/4, cipher_aead/4, decipher_aead/5, is_correct_mac/2, nonce_seed/3]).
+-export([cipher/4, cipher/5, decipher/4,
+ cipher_aead/4, cipher_aead/5, decipher_aead/5,
+ is_correct_mac/2, nonce_seed/3]).
-export_type([ssl_version/0, ssl_atom_version/0, connection_states/0, connection_state/0]).
-type ssl_version() :: {integer(), integer()}.
-type ssl_atom_version() :: tls_record:tls_atom_version().
--type connection_states() :: term(). %% Map
--type connection_state() :: term(). %% Map
+-type connection_states() :: map(). %% Map
+-type connection_state() :: map(). %% Map
%%====================================================================
%% Connection state handling
@@ -120,7 +122,7 @@ activate_pending_connection_state(#{current_write := Current,
}.
%%--------------------------------------------------------------------
--spec step_encryption_state(connection_states()) -> connection_states().
+-spec step_encryption_state(#state{}) -> #state{}.
%%
%% Description: Activates the next encyrption state (e.g. handshake
%% encryption).
@@ -319,27 +321,49 @@ cipher(Version, Fragment,
#security_parameters{bulk_cipher_algorithm =
BulkCipherAlgo}
} = WriteState0, MacHash) ->
-
+ %%
{CipherFragment, CipherS1} =
ssl_cipher:cipher(BulkCipherAlgo, CipherS0, MacHash, Fragment, Version),
{CipherFragment, WriteState0#{cipher_state => CipherS1}}.
+
+%%--------------------------------------------------------------------
+-spec cipher(ssl_version(), iodata(), #cipher_state{}, MacHash::binary(), #security_parameters{}) ->
+ {CipherFragment::binary(), #cipher_state{}}.
+%%
+%% Description: Payload encryption
+%%--------------------------------------------------------------------
+cipher(Version, Fragment, CipherS0, MacHash,
+ #security_parameters{bulk_cipher_algorithm = BulkCipherAlgo}) ->
+ %%
+ ssl_cipher:cipher(BulkCipherAlgo, CipherS0, MacHash, Fragment, Version).
+
%%--------------------------------------------------------------------
-spec cipher_aead(ssl_version(), iodata(), connection_state(), AAD::binary()) ->
{CipherFragment::binary(), connection_state()}.
%% Description: Payload encryption
%% %%--------------------------------------------------------------------
-cipher_aead(Version, Fragment,
+cipher_aead(_Version, Fragment,
#{cipher_state := CipherS0,
security_parameters :=
#security_parameters{bulk_cipher_algorithm =
BulkCipherAlgo}
} = WriteState0, AAD) ->
{CipherFragment, CipherS1} =
- cipher_aead(BulkCipherAlgo, CipherS0, AAD, Fragment, Version),
+ do_cipher_aead(BulkCipherAlgo, Fragment, CipherS0, AAD),
{CipherFragment, WriteState0#{cipher_state => CipherS1}}.
%%--------------------------------------------------------------------
+-spec cipher_aead(ssl_version(), iodata(), #cipher_state{}, AAD::binary(), #security_parameters{}) ->
+ {CipherFragment::binary(), #cipher_state{}}.
+
+%% Description: Payload encryption
+%% %%--------------------------------------------------------------------
+cipher_aead(_Version, Fragment, CipherS, AAD,
+ #security_parameters{bulk_cipher_algorithm = BulkCipherAlgo}) ->
+ do_cipher_aead(BulkCipherAlgo, Fragment, CipherS, AAD).
+
+%%--------------------------------------------------------------------
-spec decipher(ssl_version(), binary(), connection_state(), boolean()) ->
{binary(), binary(), connection_state()} | #alert{}.
%%
@@ -360,9 +384,8 @@ decipher(Version, CipherFragment,
Alert
end.
%%--------------------------------------------------------------------
--spec decipher_aead(ssl_cipher:cipher_enum(), #cipher_state{},
- binary(), binary(), ssl_record:ssl_version()) ->
- {binary(), #cipher_state{}} | #alert{}.
+-spec decipher_aead(ssl_cipher:cipher_enum(), #cipher_state{}, binary(), binary(), ssl_record:ssl_version()) ->
+ binary() | #alert{}.
%%
%% Description: Decrypts the data and checks the associated data (AAD) MAC using
%% cipher described by cipher_enum() and updating the cipher state.
@@ -374,7 +397,7 @@ decipher_aead(Type, #cipher_state{key = Key} = CipherState, AAD0, CipherFragment
{AAD, CipherText, CipherTag} = aead_ciphertext_split(Type, CipherState, CipherFragment, AAD0),
case ssl_cipher:aead_decrypt(Type, Key, Nonce, CipherText, CipherTag, AAD) of
Content when is_binary(Content) ->
- {Content, CipherState};
+ Content;
_ ->
?ALERT_REC(?FATAL, ?BAD_RECORD_MAC, decryption_failed)
end
@@ -416,11 +439,13 @@ random() ->
Random_28_bytes = ssl_cipher:random_bytes(28),
<<?UINT32(Secs_since_1970), Random_28_bytes/binary>>.
+-compile({inline, [is_correct_mac/2]}).
is_correct_mac(Mac, Mac) ->
true;
is_correct_mac(_M,_H) ->
false.
+-compile({inline, [record_protocol_role/1]}).
record_protocol_role(client) ->
?CLIENT;
record_protocol_role(server) ->
@@ -444,13 +469,15 @@ initial_security_params(ConnectionEnd) ->
compression_algorithm = ?NULL},
ssl_cipher:security_parameters(?TLS_NULL_WITH_NULL_NULL, SecParams).
-cipher_aead(?CHACHA20_POLY1305 = Type, #cipher_state{key=Key} = CipherState, AAD0, Fragment, _Version) ->
- AAD = end_additional_data(AAD0, erlang:iolist_size(Fragment)),
+-define(end_additional_data(AAD, Len), << (begin(AAD)end)/binary, ?UINT16(begin(Len)end) >>).
+
+do_cipher_aead(?CHACHA20_POLY1305 = Type, Fragment, #cipher_state{key=Key} = CipherState, AAD0) ->
+ AAD = ?end_additional_data(AAD0, erlang:iolist_size(Fragment)),
Nonce = encrypt_nonce(Type, CipherState),
{Content, CipherTag} = ssl_cipher:aead_encrypt(Type, Key, Nonce, Fragment, AAD),
{<<Content/binary, CipherTag/binary>>, CipherState};
-cipher_aead(Type, #cipher_state{key=Key, nonce = ExplicitNonce} = CipherState, AAD0, Fragment, _Version) ->
- AAD = end_additional_data(AAD0, erlang:iolist_size(Fragment)),
+do_cipher_aead(Type, Fragment, #cipher_state{key=Key, nonce = ExplicitNonce} = CipherState, AAD0) ->
+ AAD = ?end_additional_data(AAD0, erlang:iolist_size(Fragment)),
Nonce = encrypt_nonce(Type, CipherState),
{Content, CipherTag} = ssl_cipher:aead_encrypt(Type, Key, Nonce, Fragment, AAD),
{<<ExplicitNonce:64/integer, Content/binary, CipherTag/binary>>, CipherState#cipher_state{nonce = ExplicitNonce + 1}}.
@@ -466,15 +493,12 @@ decrypt_nonce(?CHACHA20_POLY1305, #cipher_state{nonce = Nonce, iv = IV}, _) ->
decrypt_nonce(?AES_GCM, #cipher_state{iv = <<Salt:4/bytes, _/binary>>}, <<ExplicitNonce:8/bytes, _/binary>>) ->
<<Salt/binary, ExplicitNonce/binary>>.
+-compile({inline, [aead_ciphertext_split/4]}).
aead_ciphertext_split(?CHACHA20_POLY1305, #cipher_state{tag_len = Len}, CipherTextFragment, AAD) ->
- CipherLen = size(CipherTextFragment) - Len,
+ CipherLen = byte_size(CipherTextFragment) - Len,
<<CipherText:CipherLen/bytes, CipherTag:Len/bytes>> = CipherTextFragment,
- {end_additional_data(AAD, CipherLen), CipherText, CipherTag};
+ {?end_additional_data(AAD, CipherLen), CipherText, CipherTag};
aead_ciphertext_split(?AES_GCM, #cipher_state{tag_len = Len}, CipherTextFragment, AAD) ->
- CipherLen = size(CipherTextFragment) - (Len + 8), %% 8 is length of explicit Nonce
+ CipherLen = byte_size(CipherTextFragment) - (Len + 8), %% 8 is length of explicit Nonce
<< _:8/bytes, CipherText:CipherLen/bytes, CipherTag:Len/bytes>> = CipherTextFragment,
- {end_additional_data(AAD, CipherLen), CipherText, CipherTag}.
-
-end_additional_data(AAD, Len) ->
- <<AAD/binary, ?UINT16(Len)>>.
-
+ {?end_additional_data(AAD, CipherLen), CipherText, CipherTag}.
diff --git a/lib/ssl/src/ssl_record.hrl b/lib/ssl/src/ssl_record.hrl
index 4cb19d9d0d..eb718fd20c 100644
--- a/lib/ssl/src/ssl_record.hrl
+++ b/lib/ssl/src/ssl_record.hrl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2007-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2007-2019. 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.
@@ -141,6 +141,8 @@
-define(HANDSHAKE, 22).
-define(APPLICATION_DATA, 23).
-define(HEARTBEAT, 24).
+-define(KNOWN_RECORD_TYPE(Type),
+ (is_integer(Type) andalso (20 =< (Type)) andalso ((Type) =< 23))).
-define(MAX_PLAIN_TEXT_LENGTH, 16384).
-define(MAX_COMPRESSED_LENGTH, (?MAX_PLAIN_TEXT_LENGTH+1024)).
-define(MAX_CIPHER_TEXT_LENGTH, (?MAX_PLAIN_TEXT_LENGTH+2048)).
diff --git a/lib/ssl/src/ssl_session.erl b/lib/ssl/src/ssl_session.erl
index a9759c9b43..44305c65fe 100644
--- a/lib/ssl/src/ssl_session.erl
+++ b/lib/ssl/src/ssl_session.erl
@@ -27,6 +27,7 @@
-include("ssl_handshake.hrl").
-include("ssl_internal.hrl").
+-include("ssl_api.hrl").
%% Internal application API
-export([is_new/2, client_id/4, server_id/6, valid_session/2]).
@@ -34,7 +35,7 @@
-type seconds() :: integer().
%%--------------------------------------------------------------------
--spec is_new(session_id(), session_id()) -> boolean().
+-spec is_new(ssl:session_id(), ssl:session_id()) -> boolean().
%%
%% Description: Checks if the session id decided by the server is a
%% new or resumed sesion id.
@@ -47,7 +48,7 @@ is_new(_ClientSuggestion, _ServerDecision) ->
true.
%%--------------------------------------------------------------------
--spec client_id({host(), inet:port_number(), #ssl_options{}}, db_handle(), atom(),
+-spec client_id({ssl:host(), inet:port_number(), #ssl_options{}}, db_handle(), atom(),
undefined | binary()) -> binary().
%%
%% Description: Should be called by the client side to get an id
diff --git a/lib/ssl/src/ssl_session_cache_api.erl b/lib/ssl/src/ssl_session_cache_api.erl
index b68c75a09b..5f96f905b1 100644
--- a/lib/ssl/src/ssl_session_cache_api.erl
+++ b/lib/ssl/src/ssl_session_cache_api.erl
@@ -23,14 +23,20 @@
-module(ssl_session_cache_api).
-include("ssl_handshake.hrl").
-include("ssl_internal.hrl").
+-include("ssl_api.hrl").
--type key() :: {{host(), inet:port_number()}, session_id()} | {inet:port_number(), session_id()}.
+-export_type([session_cache_key/0, session/0, partial_key/0, session_cache_ref/0]).
--callback init(list()) -> db_handle().
--callback terminate(db_handle()) -> any().
--callback lookup(db_handle(), key()) -> #session{} | undefined.
--callback update(db_handle(), key(), #session{}) -> any().
--callback delete(db_handle(), key()) -> any().
--callback foldl(fun(), term(), db_handle()) -> term().
--callback select_session(db_handle(), {host(), inet:port_number()} | inet:port_number()) -> [#session{}].
--callback size(db_handle()) -> integer().
+-type session_cache_ref() :: any().
+-type session_cache_key() :: {partial_key(), ssl:session_id()}.
+-opaque session() :: #session{}.
+-opaque partial_key() :: {ssl:host(), inet:port_number()} | inet:port_number().
+
+-callback init(list()) -> session_cache_ref().
+-callback terminate(session_cache_ref()) -> any().
+-callback lookup(session_cache_ref(), session_cache_key()) -> #session{} | undefined.
+-callback update(session_cache_ref(), session_cache_key(), #session{}) -> any().
+-callback delete(session_cache_ref(), session_cache_key()) -> any().
+-callback foldl(fun(), term(), session_cache_ref()) -> term().
+-callback select_session(session_cache_ref(), {ssl:host(), inet:port_number()} | inet:port_number()) -> [#session{}].
+-callback size(session_cache_ref()) -> integer().
diff --git a/lib/ssl/src/tls_connection.erl b/lib/ssl/src/tls_connection.erl
index 01e378702c..9a896971ef 100644
--- a/lib/ssl/src/tls_connection.erl
+++ b/lib/ssl/src/tls_connection.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2007-2018. All Rights Reserved.
+%% Copyright Ericsson AB 2007-2019. 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,7 +50,8 @@
handle_protocol_record/3]).
%% Handshake handling
--export([renegotiation/2, renegotiate/2, send_handshake/2,
+-export([renegotiation/2, renegotiate/2, send_handshake/2,
+ send_handshake_flight/1,
queue_handshake/2, queue_change_cipher/2,
reinit/1, reinit_handshake_data/1, select_sni_extension/1,
empty_connection_state/2]).
@@ -58,11 +59,10 @@
%% Alert and close handling
-export([send_alert/2, send_alert_in_connection/2,
send_sync_alert/2,
- encode_alert/3, close/5, protocol_name/0]).
+ close/5, protocol_name/0]).
%% Data handling
--export([encode_data/3, next_record/1,
- send/3, socket/5, setopts/3, getopts/3]).
+-export([next_record/1, socket/4, setopts/3, getopts/3]).
%% gen_statem state functions
-export([init/3, error/3, downgrade/3, %% Initiation and take down states
@@ -126,7 +126,7 @@ start_fsm(Role, Host, Port, Socket, {#ssl_options{erl_dist = true},_, Tracker} =
end.
%%--------------------------------------------------------------------
--spec start_link(atom(), pid(), host(), inet:port_number(), port(), list(), pid(), tuple()) ->
+-spec start_link(atom(), pid(), ssl:host(), inet:port_number(), port(), list(), pid(), tuple()) ->
{ok, pid()} | ignore | {error, reason()}.
%%
%% Description: Creates a gen_statem process which calls Module:init/1 to
@@ -167,20 +167,11 @@ next_record(#state{handshake_env =
{no_record, State#state{handshake_env =
HsEnv#handshake_env{unprocessed_handshake_events = N-1}}};
next_record(#state{protocol_buffers =
- #protocol_buffers{tls_packets = [], tls_cipher_texts = [#ssl_tls{type = Type}| _] = CipherTexts0}
- = Buffers,
- connection_states = ConnectionStates0,
- negotiated_version = Version,
+ #protocol_buffers{tls_cipher_texts = [_|_] = CipherTexts},
+ connection_states = ConnectionStates,
ssl_options = #ssl_options{padding_check = Check}} = State) ->
- case decode_cipher_texts(Version, Type, CipherTexts0, ConnectionStates0, Check, <<>>) of
- {#ssl_tls{} = Record, ConnectionStates, CipherTexts} ->
- {Record, State#state{protocol_buffers = Buffers#protocol_buffers{tls_cipher_texts = CipherTexts},
- connection_states = ConnectionStates}};
- {#alert{} = Alert, ConnectionStates, CipherTexts} ->
- {Alert, State#state{protocol_buffers = Buffers#protocol_buffers{tls_cipher_texts = CipherTexts},
- connection_states = ConnectionStates}}
- end;
-next_record(#state{protocol_buffers = #protocol_buffers{tls_packets = [], tls_cipher_texts = []},
+ next_record(State, CipherTexts, ConnectionStates, Check);
+next_record(#state{protocol_buffers = #protocol_buffers{tls_cipher_texts = []},
protocol_specific = #{active_n_toggle := true, active_n := N} = ProtocolSpec,
static_env = #static_env{socket = Socket,
close_tag = CloseTag,
@@ -196,16 +187,56 @@ next_record(#state{protocol_buffers = #protocol_buffers{tls_packets = [], tls_ci
next_record(State) ->
{no_record, State}.
+%% Decipher next record and concatenate consecutive ?APPLICATION_DATA records into one
+%%
+next_record(State, CipherTexts, ConnectionStates, Check) ->
+ next_record(State, CipherTexts, ConnectionStates, Check, []).
+%%
+next_record(#state{connection_env = #connection_env{negotiated_version = Version}} = State,
+ [CT|CipherTexts], ConnectionStates0, Check, Acc) ->
+ case tls_record:decode_cipher_text(Version, CT, ConnectionStates0, Check) of
+ {#ssl_tls{type = ?APPLICATION_DATA, fragment = Fragment}, ConnectionStates} ->
+ case CipherTexts of
+ [] ->
+ %% End of cipher texts - build and deliver an ?APPLICATION_DATA record
+ %% from the accumulated fragments
+ next_record_done(State, [], ConnectionStates,
+ #ssl_tls{type = ?APPLICATION_DATA,
+ fragment = iolist_to_binary(lists:reverse(Acc, [Fragment]))});
+ [_|_] ->
+ next_record(State, CipherTexts, ConnectionStates, Check, [Fragment|Acc])
+ end;
+ {Record, ConnectionStates} when Acc =:= [] ->
+ %% Singelton non-?APPLICATION_DATA record - deliver
+ next_record_done(State, CipherTexts, ConnectionStates, Record);
+ {_Record, _ConnectionStates_to_forget} ->
+ %% Not ?APPLICATION_DATA but we have accumulated fragments
+ %% -> build an ?APPLICATION_DATA record with concatenated fragments
+ %% and forget about decrypting this record - we'll decrypt it again next time
+ next_record_done(State, [CT|CipherTexts], ConnectionStates0,
+ #ssl_tls{type = ?APPLICATION_DATA, fragment = iolist_to_binary(lists:reverse(Acc))});
+ #alert{} = Alert ->
+ Alert
+ end.
+
+next_record_done(#state{protocol_buffers = Buffers} = State, CipherTexts, ConnectionStates, Record) ->
+ {Record,
+ State#state{protocol_buffers = Buffers#protocol_buffers{tls_cipher_texts = CipherTexts},
+ connection_states = ConnectionStates}}.
+
+
next_event(StateName, Record, State) ->
next_event(StateName, Record, State, []).
+%%
next_event(StateName, no_record, State0, Actions) ->
case next_record(State0) of
{no_record, State} ->
{next_state, StateName, State, Actions};
{#ssl_tls{} = Record, State} ->
{next_state, StateName, State, [{next_event, internal, {protocol_record, Record}} | Actions]};
- {#alert{} = Alert, State} ->
- {next_state, StateName, State, [{next_event, internal, Alert} | Actions]}
+ #alert{} = Alert ->
+ Version = State0#state.connection_env#connection_env.negotiated_version,
+ ssl_connection:handle_own_alert(Alert, Version, StateName, State0)
end;
next_event(StateName, Record, State, Actions) ->
case Record of
@@ -214,40 +245,30 @@ next_event(StateName, Record, State, Actions) ->
#ssl_tls{} = Record ->
{next_state, StateName, State, [{next_event, internal, {protocol_record, Record}} | Actions]};
#alert{} = Alert ->
- {next_state, StateName, State, [{next_event, internal, Alert} | Actions]}
+ Version = State#state.connection_env#connection_env.negotiated_version,
+ ssl_connection:handle_own_alert(Alert, Version, StateName, State)
end.
-decode_cipher_texts(_, Type, [] = CipherTexts, ConnectionStates, _, Acc) ->
- {#ssl_tls{type = Type, fragment = Acc}, ConnectionStates, CipherTexts};
-decode_cipher_texts(Version, Type,
- [#ssl_tls{type = Type} = CT | CipherTexts], ConnectionStates0, Check, Acc) ->
- case tls_record:decode_cipher_text(Version, CT, ConnectionStates0, Check) of
- {#ssl_tls{type = ?APPLICATION_DATA, fragment = Plain}, ConnectionStates} ->
- decode_cipher_texts(Version, Type, CipherTexts,
- ConnectionStates, Check, <<Acc/binary, Plain/binary>>);
- {#ssl_tls{type = Type, fragment = Plain}, ConnectionStates} ->
- {#ssl_tls{type = Type, fragment = Plain}, ConnectionStates, CipherTexts};
- #alert{} = Alert ->
- {Alert, ConnectionStates0, CipherTexts}
- end;
-decode_cipher_texts(_, Type, CipherTexts, ConnectionStates, _, Acc) ->
- {#ssl_tls{type = Type, fragment = Acc}, ConnectionStates, CipherTexts}.
%%% TLS record protocol level application data messages
-handle_protocol_record(#ssl_tls{type = ?APPLICATION_DATA, fragment = Data}, StateName, State0) ->
+handle_protocol_record(#ssl_tls{type = ?APPLICATION_DATA, fragment = Data}, StateName0, State0) ->
case ssl_connection:read_application_data(Data, State0) of
{stop, _, _} = Stop->
Stop;
{Record, State1} ->
- {next_state, StateName, State, Actions} = next_event(StateName, Record, State1),
- ssl_connection:hibernate_after(StateName, State, Actions)
+ case next_event(StateName0, Record, State1) of
+ {next_state, StateName, State, Actions} ->
+ ssl_connection:hibernate_after(StateName, State, Actions);
+ {stop, _, _} = Stop ->
+ Stop
+ end
end;
%%% TLS record protocol level handshake messages
handle_protocol_record(#ssl_tls{type = ?HANDSHAKE, fragment = Data},
StateName, #state{protocol_buffers =
#protocol_buffers{tls_handshake_buffer = Buf0} = Buffers,
- negotiated_version = Version,
+ connection_env = #connection_env{negotiated_version = Version},
ssl_options = Options} = State0) ->
try
EffectiveVersion = effective_version(Version, Options),
@@ -281,7 +302,7 @@ handle_protocol_record(#ssl_tls{type = ?CHANGE_CIPHER_SPEC, fragment = Data}, St
{next_state, StateName, State, [{next_event, internal, #change_cipher_spec{type = Data}}]};
%%% TLS record protocol level Alert messages
handle_protocol_record(#ssl_tls{type = ?ALERT, fragment = EncAlerts}, StateName,
- #state{negotiated_version = Version} = State) ->
+ #state{connection_env = #connection_env{negotiated_version = Version}} = State) ->
try decode_alerts(EncAlerts) of
Alerts = [_|_] ->
handle_alerts(Alerts, {next_state, StateName, State});
@@ -316,14 +337,14 @@ renegotiate(#state{static_env = #static_env{role = server,
socket = Socket,
transport_cb = Transport},
handshake_env = HsEnv,
- negotiated_version = Version,
+ connection_env = #connection_env{negotiated_version = Version},
connection_states = ConnectionStates0} = State0, Actions) ->
HelloRequest = ssl_handshake:hello_request(),
Frag = tls_handshake:encode_handshake(HelloRequest, Version),
Hs0 = ssl_handshake:init_handshake_history(),
{BinMsg, ConnectionStates} =
tls_record:encode_handshake(Frag, Version, ConnectionStates0),
- send(Transport, Socket, BinMsg),
+ tls_socket:send(Transport, Socket, BinMsg),
State = State0#state{connection_states =
ConnectionStates,
handshake_env = HsEnv#handshake_env{tls_handshake_history = Hs0}},
@@ -332,11 +353,12 @@ renegotiate(#state{static_env = #static_env{role = server,
send_handshake(Handshake, State) ->
send_handshake_flight(queue_handshake(Handshake, State)).
-queue_handshake(Handshake, #state{negotiated_version = Version,
- handshake_env = #handshake_env{tls_handshake_history = Hist0} = HsEnv,
- flight_buffer = Flight0,
- connection_states = ConnectionStates0,
- ssl_options = SslOpts} = State0) ->
+
+queue_handshake(Handshake, #state{handshake_env = #handshake_env{tls_handshake_history = Hist0} = HsEnv,
+ connection_env = #connection_env{negotiated_version = Version},
+ flight_buffer = Flight0,
+ ssl_options = SslOpts,
+ connection_states = ConnectionStates0} = State0) ->
{BinHandshake, ConnectionStates, Hist} =
encode_handshake(Handshake, Version, ConnectionStates0, Hist0),
ssl_logger:debug(SslOpts#ssl_options.log_level, outbound, 'handshake', Handshake),
@@ -349,13 +371,14 @@ queue_handshake(Handshake, #state{negotiated_version = Version,
send_handshake_flight(#state{static_env = #static_env{socket = Socket,
transport_cb = Transport},
flight_buffer = Flight} = State0) ->
- send(Transport, Socket, Flight),
+ tls_socket:send(Transport, Socket, Flight),
{State0#state{flight_buffer = []}, []}.
-queue_change_cipher(Msg, #state{negotiated_version = Version,
+
+queue_change_cipher(Msg, #state{connection_env = #connection_env{negotiated_version = Version},
flight_buffer = Flight0,
- connection_states = ConnectionStates0,
- ssl_options = SslOpts} = State0) ->
+ ssl_options = SslOpts,
+ connection_states = ConnectionStates0} = State0) ->
{BinChangeCipher, ConnectionStates} =
encode_change_cipher(Msg, Version, ConnectionStates0),
ssl_logger:debug(SslOpts#ssl_options.log_level, outbound, 'tls_record', BinChangeCipher),
@@ -363,7 +386,7 @@ queue_change_cipher(Msg, #state{negotiated_version = Version,
flight_buffer = Flight0 ++ [BinChangeCipher]}.
reinit(#state{protocol_specific = #{sender := Sender},
- negotiated_version = Version,
+ connection_env = #connection_env{negotiated_version = Version},
connection_states = #{current_write := Write}} = State) ->
tls_sender:update_connection_state(Sender, Write, Version),
reinit_handshake_data(State).
@@ -373,9 +396,9 @@ reinit_handshake_data(#state{handshake_env = HsEnv} =State) ->
%% are only needed during the handshake phase.
%% To reduce memory foot print of a connection reinitialize them.
State#state{
- premaster_secret = undefined,
- public_key_info = undefined,
- handshake_env = HsEnv#handshake_env{tls_handshake_history = ssl_handshake:init_handshake_history()}
+ handshake_env = HsEnv#handshake_env{tls_handshake_history = ssl_handshake:init_handshake_history(),
+ public_key_info = undefined,
+ premaster_secret = undefined}
}.
select_sni_extension(#client_hello{extensions = #{sni := SNI}}) ->
@@ -399,14 +422,14 @@ empty_connection_state(ConnectionEnd, BeastMitigation) ->
encode_alert(#alert{} = Alert, Version, ConnectionStates) ->
tls_record:encode_alert_record(Alert, Version, ConnectionStates).
-send_alert(Alert, #state{negotiated_version = Version,
- static_env = #static_env{socket = Socket,
+send_alert(Alert, #state{static_env = #static_env{socket = Socket,
transport_cb = Transport},
+ connection_env = #connection_env{negotiated_version = Version},
ssl_options = SslOpts,
connection_states = ConnectionStates0} = StateData0) ->
{BinMsg, ConnectionStates} =
encode_alert(Alert, Version, ConnectionStates0),
- send(Transport, Socket, BinMsg),
+ tls_socket:send(Transport, Socket, BinMsg),
ssl_logger:debug(SslOpts#ssl_options.log_level, outbound, 'tls_record', BinMsg),
StateData0#state{connection_states = ConnectionStates}.
@@ -461,14 +484,9 @@ protocol_name() ->
%%====================================================================
%% Data handling
%%====================================================================
-encode_data(Data, Version, ConnectionStates0)->
- tls_record:encode_data(Data, Version, ConnectionStates0).
-
-send(Transport, Socket, Data) ->
- tls_socket:send(Transport, Socket, Data).
-socket(Pids, Transport, Socket, Connection, Tracker) ->
- tls_socket:socket(Pids, Transport, Socket, Connection, Tracker).
+socket(Pids, Transport, Socket, Tracker) ->
+ tls_socket:socket(Pids, Transport, Socket, ?MODULE, Tracker).
setopts(Transport, Socket, Other) ->
tls_socket:setopts(Transport, Socket, Other).
@@ -494,12 +512,12 @@ init({call, From}, {start, Timeout},
session_cache = Cache,
session_cache_cb = CacheCb},
handshake_env = #handshake_env{renegotiation = {Renegotiation, _}} = HsEnv,
+ connection_env = CEnv,
ssl_options = SslOpts,
session = #session{own_certificate = Cert} = Session0,
connection_states = ConnectionStates0
} = State0) ->
KeyShare = maybe_generate_client_shares(SslOpts),
- Timer = ssl_connection:start_or_recv_cancel_timer(Timeout, From),
Hello = tls_handshake:client_hello(Host, Port, ConnectionStates0, SslOpts,
Cache, CacheCb, Renegotiation, Cert, KeyShare),
@@ -507,19 +525,18 @@ init({call, From}, {start, Timeout},
Handshake0 = ssl_handshake:init_handshake_history(),
{BinMsg, ConnectionStates, Handshake} =
encode_handshake(Hello, HelloVersion, ConnectionStates0, Handshake0),
- send(Transport, Socket, BinMsg),
+ tls_socket:send(Transport, Socket, BinMsg),
ssl_logger:debug(SslOpts#ssl_options.log_level, outbound, 'handshake', Hello),
ssl_logger:debug(SslOpts#ssl_options.log_level, outbound, 'tls_record', BinMsg),
State = State0#state{connection_states = ConnectionStates,
- negotiated_version = HelloVersion, %% Requested version
+ connection_env = CEnv#connection_env{negotiated_version = HelloVersion}, %% Requested version
session =
Session0#session{session_id = Hello#client_hello.session_id},
handshake_env = HsEnv#handshake_env{tls_handshake_history = Handshake},
start_or_recv_from = From,
- timer = Timer,
- key_share = KeyShare},
- next_event(hello, no_record, State);
+ key_share = KeyShare},
+ next_event(hello, no_record, State, [{{timeout, handshake}, Timeout, close}]);
init(Type, Event, State) ->
gen_handshake(?FUNCTION_NAME, Type, Event, State).
@@ -547,26 +564,30 @@ error(_, _, _) ->
%%--------------------------------------------------------------------
hello(internal, #client_hello{extensions = Extensions} = Hello,
#state{ssl_options = #ssl_options{handshake = hello},
+ handshake_env = HsEnv,
start_or_recv_from = From} = State) ->
{next_state, user_hello, State#state{start_or_recv_from = undefined,
- hello = Hello},
+ handshake_env = HsEnv#handshake_env{hello = Hello}},
[{reply, From, {ok, Extensions}}]};
hello(internal, #server_hello{extensions = Extensions} = Hello,
#state{ssl_options = #ssl_options{handshake = hello},
+ handshake_env = HsEnv,
start_or_recv_from = From} = State) ->
{next_state, user_hello, State#state{start_or_recv_from = undefined,
- hello = Hello},
+ handshake_env = HsEnv#handshake_env{hello = Hello}},
[{reply, From, {ok, Extensions}}]};
+
hello(internal, #client_hello{client_version = ClientVersion} = Hello,
#state{connection_states = ConnectionStates0,
static_env = #static_env{
port = Port,
session_cache = Cache,
session_cache_cb = CacheCb},
- handshake_env = #handshake_env{renegotiation = {Renegotiation, _}} = HsEnv,
+ handshake_env = #handshake_env{kex_algorithm = KeyExAlg,
+ renegotiation = {Renegotiation, _},
+ negotiated_protocol = CurrentProtocol} = HsEnv,
+ connection_env = CEnv,
session = #session{own_certificate = Cert} = Session0,
- negotiated_protocol = CurrentProtocol,
- key_algorithm = KeyExAlg,
ssl_options = SslOpts} = State) ->
case choose_tls_version(SslOpts, Hello) of
@@ -581,8 +602,8 @@ hello(internal, #client_hello{client_version = ClientVersion} = Hello,
Renegotiation) of
#alert{} = Alert ->
ssl_connection:handle_own_alert(Alert, ClientVersion, hello,
- State#state{negotiated_version
- = ClientVersion});
+ State#state{connection_env = CEnv#connection_env{negotiated_version
+ = ClientVersion}});
{Version, {Type, Session},
ConnectionStates, Protocol0, ServerHelloExt, HashSign} ->
Protocol = case Protocol0 of
@@ -593,24 +614,26 @@ hello(internal, #client_hello{client_version = ClientVersion} = Hello,
internal,
{common_client_hello, Type, ServerHelloExt},
State#state{connection_states = ConnectionStates,
- negotiated_version = Version,
- hashsign_algorithm = HashSign,
- handshake_env = HsEnv#handshake_env{client_hello_version =
- ClientVersion},
- session = Session,
- negotiated_protocol = Protocol})
+ connection_env = CEnv#connection_env{negotiated_version = Version},
+ handshake_env = HsEnv#handshake_env{
+ hashsign_algorithm = HashSign,
+ client_hello_version = ClientVersion,
+ negotiated_protocol = Protocol},
+ session = Session
+ })
end
+
end;
hello(internal, #server_hello{} = Hello,
#state{connection_states = ConnectionStates0,
- negotiated_version = ReqVersion,
+ connection_env = #connection_env{negotiated_version = ReqVersion} = CEnv,
static_env = #static_env{role = client},
handshake_env = #handshake_env{renegotiation = {Renegotiation, _}},
ssl_options = SslOptions} = State) ->
case tls_handshake:hello(Hello, SslOptions, ConnectionStates0, Renegotiation) of
- #alert{} = Alert ->
+ #alert{} = Alert -> %%TODO
ssl_connection:handle_own_alert(Alert, ReqVersion, hello,
- State#state{negotiated_version = ReqVersion});
+ State#state{connection_env = CEnv#connection_env{negotiated_version = ReqVersion}});
{Version, NewId, ConnectionStates, ProtoExt, Protocol} ->
ssl_connection:handle_session(Hello,
Version, NewId, ConnectionStates, ProtoExt, Protocol, State)
@@ -663,13 +686,16 @@ connection({call, From}, {user_renegotiate, WriteState},
[{next_event,{call, From}, renegotiate}]};
connection({call, From},
{close, {Pid, _Timeout}},
- #state{terminated = closed} = State) ->
- {next_state, downgrade, State#state{terminated = true, downgrade = {Pid, From}},
+ #state{connection_env = #connection_env{terminated = closed} =CEnv} = State) ->
+ {next_state, downgrade, State#state{connection_env =
+ CEnv#connection_env{terminated = true,
+ downgrade = {Pid, From}}},
[{next_event, internal, ?ALERT_REC(?WARNING, ?CLOSE_NOTIFY)}]};
connection({call, From},
{close,{Pid, Timeout}},
#state{connection_states = ConnectionStates,
- protocol_specific = #{sender := Sender}
+ protocol_specific = #{sender := Sender},
+ connection_env = CEnv
} = State0) ->
case tls_sender:downgrade(Sender, Timeout) of
{ok, Write} ->
@@ -680,8 +706,10 @@ connection({call, From},
State = send_alert(?ALERT_REC(?WARNING, ?CLOSE_NOTIFY),
State0#state{connection_states =
ConnectionStates#{current_write => Write}}),
- {next_state, downgrade, State#state{downgrade = {Pid, From},
- terminated = true}, [{timeout, Timeout, downgrade}]};
+ {next_state, downgrade, State#state{connection_env =
+ CEnv#connection_env{downgrade = {Pid, From},
+ terminated = true}},
+ [{timeout, Timeout, downgrade}]};
{error, timeout} ->
{stop_and_reply, {shutdown, downgrade_fail}, [{reply, From, {error, timeout}}]}
end;
@@ -725,8 +753,7 @@ connection(internal, #hello_request{},
= Hello#client_hello.session_id}}, Actions);
connection(internal, #client_hello{} = Hello,
#state{static_env = #static_env{role = server},
- handshake_env = HsEnv,
- allow_renegotiate = true,
+ handshake_env = #handshake_env{allow_renegotiate = true}= HsEnv,
connection_states = CS,
protocol_specific = #{sender := Sender}
} = State) ->
@@ -738,17 +765,16 @@ connection(internal, #client_hello{} = Hello,
erlang:send_after(?WAIT_TO_ALLOW_RENEGOTIATION, self(), allow_renegotiate),
{ok, Write} = tls_sender:renegotiate(Sender),
next_event(hello, no_record, State#state{connection_states = CS#{current_write => Write},
- allow_renegotiate = false,
- handshake_env = HsEnv#handshake_env{renegotiation = {true, peer}}
+ handshake_env = HsEnv#handshake_env{renegotiation = {true, peer},
+ allow_renegotiate = false}
},
[{next_event, internal, Hello}]);
connection(internal, #client_hello{},
- #state{static_env = #static_env{role = server,
- protocol_cb = Connection},
- allow_renegotiate = false} = State0) ->
+ #state{static_env = #static_env{role = server},
+ handshake_env = #handshake_env{allow_renegotiate = false}} = State0) ->
Alert = ?ALERT_REC(?WARNING, ?NO_RENEGOTIATION),
send_alert_in_connection(Alert, State0),
- State = Connection:reinit_handshake_data(State0),
+ State = reinit_handshake_data(State0),
next_event(?FUNCTION_NAME, no_record, State);
connection(Type, Event, State) ->
@@ -761,15 +787,16 @@ connection(Type, Event, State) ->
downgrade(internal, #alert{description = ?CLOSE_NOTIFY},
#state{static_env = #static_env{transport_cb = Transport,
socket = Socket},
- downgrade = {Pid, From}} = State) ->
+ connection_env = #connection_env{downgrade = {Pid, From}}} = State) ->
tls_socket:setopts(Transport, Socket, [{active, false}, {packet, 0}, {mode, binary}]),
Transport:controlling_process(Socket, Pid),
{stop_and_reply, {shutdown, downgrade},[{reply, From, {ok, Socket}}], State};
-downgrade(timeout, downgrade, #state{downgrade = {_, From}} = State) ->
+downgrade(timeout, downgrade, #state{ connection_env = #connection_env{downgrade = {_, From}}} = State) ->
{stop_and_reply, {shutdown, normal},[{reply, From, {error, timeout}}], State};
downgrade(info, {CloseTag, Socket},
#state{static_env = #static_env{socket = Socket,
- close_tag = CloseTag}, downgrade = {_, From}} =
+ close_tag = CloseTag},
+ connection_env = #connection_env{downgrade = {_, From}}} =
State) ->
{stop_and_reply, {shutdown, normal},[{reply, From, {error, CloseTag}}], State};
downgrade(info, Info, State) ->
@@ -948,16 +975,16 @@ initial_state(Role, Sender, Host, Port, Socket, {SSLOptions, SocketOptions, Trac
static_env = InitStatEnv,
handshake_env = #handshake_env{
tls_handshake_history = ssl_handshake:init_handshake_history(),
- renegotiation = {false, first}
+ renegotiation = {false, first},
+ allow_renegotiate = SSLOptions#ssl_options.client_renegotiation
},
+ connection_env = #connection_env{user_application = {UserMonitor, User}},
socket_options = SocketOptions,
ssl_options = SSLOptions,
session = #session{is_resumable = new},
connection_states = ConnectionStates,
protocol_buffers = #protocol_buffers{},
- user_application = {UserMonitor, User},
- user_data_buffer = <<>>,
- allow_renegotiate = SSLOptions#ssl_options.client_renegotiation,
+ user_data_buffer = {[],0,[]},
start_or_recv_from = undefined,
flight_buffer = [],
protocol_specific = #{sender => Sender,
@@ -969,12 +996,11 @@ initial_state(Role, Sender, Host, Port, Socket, {SSLOptions, SocketOptions, Trac
initialize_tls_sender(#state{static_env = #static_env{
role = Role,
transport_cb = Transport,
- protocol_cb = Connection,
socket = Socket,
tracker = Tracker
},
- socket_options = SockOpts,
- negotiated_version = Version,
+ connection_env = #connection_env{negotiated_version = Version},
+ socket_options = SockOpts,
ssl_options = #ssl_options{renegotiate_at = RenegotiateAt,
log_level = LogLevel},
connection_states = #{current_write := ConnectionWriteState},
@@ -984,20 +1010,29 @@ initialize_tls_sender(#state{static_env = #static_env{
socket => Socket,
socket_options => SockOpts,
tracker => Tracker,
- protocol_cb => Connection,
transport_cb => Transport,
negotiated_version => Version,
renegotiate_at => RenegotiateAt,
log_level => LogLevel},
tls_sender:initialize(Sender, Init).
-
-next_tls_record(Data, StateName, #state{protocol_buffers =
- #protocol_buffers{tls_record_buffer = Buf0,
- tls_cipher_texts = CT0} = Buffers,
- ssl_options = SslOpts} = State0) ->
- case tls_record:get_tls_records(Data,
- acceptable_record_versions(StateName, State0),
- Buf0, SslOpts) of
+
+next_tls_record(Data, StateName,
+ #state{protocol_buffers =
+ #protocol_buffers{tls_record_buffer = Buf0,
+ tls_cipher_texts = CT0} = Buffers,
+ ssl_options = SslOpts} = State0) ->
+ Versions =
+ %% TLS 1.3 Client/Server
+ %% - Ignore TLSPlaintext.legacy_record_version
+ %% - Verify that TLSCiphertext.legacy_record_version is set to 0x0303 for all records
+ %% other than an initial ClientHello, where it MAY also be 0x0301.
+ case StateName of
+ hello ->
+ [tls_record:protocol_version(Vsn) || Vsn <- ?ALL_AVAILABLE_VERSIONS];
+ _ ->
+ State0#state.connection_env#connection_env.negotiated_version
+ end,
+ case tls_record:get_tls_records(Data, Versions, Buf0, SslOpts) of
{Records, Buf1} ->
CT1 = CT0 ++ Records,
next_record(State0#state{protocol_buffers =
@@ -1007,14 +1042,6 @@ next_tls_record(Data, StateName, #state{protocol_buffers =
handle_record_alert(Alert, State0)
end.
-%% TLS 1.3 Client/Server
-%% - Ignore TLSPlaintext.legacy_record_version
-%% - Verify that TLSCiphertext.legacy_record_version is set to 0x0303 for all records
-%% other than an initial ClientHello, where it MAY also be 0x0301.
-acceptable_record_versions(StateName, #state{negotiated_version = Version}) when StateName =/= hello->
- Version;
-acceptable_record_versions(hello, _) ->
- [tls_record:protocol_version(Vsn) || Vsn <- ?ALL_AVAILABLE_VERSIONS].
handle_record_alert(Alert, _) ->
Alert.
@@ -1042,18 +1069,18 @@ handle_info({tcp_passive, Socket}, StateName,
State#state{protocol_specific = PS#{active_n_toggle => true}});
handle_info({CloseTag, Socket}, StateName,
#state{static_env = #static_env{socket = Socket, close_tag = CloseTag},
+ connection_env = #connection_env{negotiated_version = Version},
socket_options = #socket_options{active = Active},
protocol_buffers = #protocol_buffers{tls_cipher_texts = CTs},
- user_data_buffer = Buffer,
- protocol_specific = PS,
- negotiated_version = Version} = State) ->
+ user_data_buffer = {_,BufferSize,_},
+ protocol_specific = PS} = State) ->
%% Note that as of TLS 1.1,
%% failure to properly close a connection no longer requires that a
%% session not be resumed. This is a change from TLS 1.0 to conform
%% with widespread implementation practice.
- case (Active == false) andalso ((CTs =/= []) or (Buffer =/= <<>>)) of
+ case (Active == false) andalso ((CTs =/= []) or (BufferSize =/= 0)) of
false ->
case Version of
{1, N} when N >= 1 ->
@@ -1086,12 +1113,13 @@ handle_alerts([], Result) ->
handle_alerts(_, {stop, _, _} = Stop) ->
Stop;
handle_alerts([#alert{level = ?WARNING, description = ?CLOSE_NOTIFY} | _Alerts],
- {next_state, connection = StateName, #state{user_data_buffer = Buffer,
+ {next_state, connection = StateName, #state{connection_env = CEnv,
socket_options = #socket_options{active = false},
+ user_data_buffer = {_,BufferSize,_},
protocol_buffers = #protocol_buffers{tls_cipher_texts = CTs}} =
- State}) when (Buffer =/= <<>>) orelse
+ State}) when (BufferSize =/= 0) orelse
(CTs =/= []) ->
- {next_state, StateName, State#state{terminated = true}};
+ {next_state, StateName, State#state{connection_env = CEnv#connection_env{terminated = true}}};
handle_alerts([Alert | Alerts], {next_state, StateName, State}) ->
handle_alerts(Alerts, ssl_connection:handle_alert(Alert, StateName, State));
handle_alerts([Alert | Alerts], {next_state, StateName, State, _Actions}) ->
@@ -1111,7 +1139,7 @@ decode_alerts(Bin) ->
ssl_alert:decode(Bin).
gen_handshake(StateName, Type, Event,
- #state{negotiated_version = Version} = State) ->
+ #state{connection_env = #connection_env{negotiated_version = Version}} = State) ->
try ssl_connection:StateName(Type, Event, State, ?MODULE) of
Result ->
Result
@@ -1122,8 +1150,9 @@ gen_handshake(StateName, Type, Event,
Version, StateName, State)
end.
+
gen_handshake_1_3(StateName, Type, Event,
- #state{negotiated_version = Version} = State) ->
+ #state{connection_env = #connection_env{negotiated_version = Version}} = State) ->
try tls_connection_1_3:StateName(Type, Event, State, ?MODULE) of
Result ->
Result
@@ -1134,18 +1163,19 @@ gen_handshake_1_3(StateName, Type, Event,
Version, StateName, State)
end.
-gen_info(Event, connection = StateName, #state{negotiated_version = Version} = State) ->
+
+gen_info(Event, connection = StateName, #state{connection_env = #connection_env{negotiated_version = Version}} = State) ->
try handle_info(Event, StateName, State) of
Result ->
Result
catch
- _:_ ->
+ _:_ ->
ssl_connection:handle_own_alert(?ALERT_REC(?FATAL, ?INTERNAL_ERROR,
malformed_data),
Version, StateName, State)
end;
-gen_info(Event, StateName, #state{negotiated_version = Version} = State) ->
+gen_info(Event, StateName, #state{connection_env = #connection_env{negotiated_version = Version}} = State) ->
try handle_info(Event, StateName, State) of
Result ->
Result
@@ -1156,18 +1186,18 @@ gen_info(Event, StateName, #state{negotiated_version = Version} = State) ->
Version, StateName, State)
end.
-gen_info_1_3(Event, connected = StateName, #state{negotiated_version = Version} = State) ->
+gen_info_1_3(Event, connected = StateName, #state{connection_env = #connection_env{negotiated_version = Version}} = State) ->
try handle_info(Event, StateName, State) of
Result ->
Result
catch
- _:_ ->
+ _:_ ->
ssl_connection:handle_own_alert(?ALERT_REC(?FATAL, ?INTERNAL_ERROR,
malformed_data),
Version, StateName, State)
end;
-gen_info_1_3(Event, StateName, #state{negotiated_version = Version} = State) ->
+gen_info_1_3(Event, StateName, #state{connection_env = #connection_env{negotiated_version = Version}} = State) ->
try handle_info(Event, StateName, State) of
Result ->
Result
diff --git a/lib/ssl/src/tls_connection.hrl b/lib/ssl/src/tls_connection.hrl
index 0af2258932..9063b1b736 100644
--- a/lib/ssl/src/tls_connection.hrl
+++ b/lib/ssl/src/tls_connection.hrl
@@ -30,7 +30,6 @@
-include("tls_record.hrl").
-record(protocol_buffers, {
- tls_packets = [], %% :: [#ssl_tls{}], % Not yet handled decode SSL/TLS packets.
tls_record_buffer = <<>>, %% :: binary(), % Buffer of incomplete records
tls_handshake_buffer = <<>>, %% :: binary(), % Buffer of incomplete handshakes
tls_cipher_texts = [] %%:: [binary()]
diff --git a/lib/ssl/src/tls_connection_1_3.erl b/lib/ssl/src/tls_connection_1_3.erl
index 48b3ff0d97..de786d0875 100644
--- a/lib/ssl/src/tls_connection_1_3.erl
+++ b/lib/ssl/src/tls_connection_1_3.erl
@@ -109,7 +109,8 @@
%% gen_statem helper functions
-export([start/4,
- negotiated/4
+ negotiated/4,
+ wait_finished/4
]).
start(internal,
@@ -135,21 +136,40 @@ start(internal,
end.
-%% TODO: remove suppression when function implemented!
--dialyzer([{nowarn_function, [negotiated/4]}, no_match]).
negotiated(internal, Map, State0, _Module) ->
case tls_handshake_1_3:do_negotiated(Map, State0) of
#alert{} = Alert ->
ssl_connection:handle_own_alert(Alert, {3,4}, negotiated, State0);
- M ->
- %% TODO: implement update_state
- %% State = update_state(State0, M),
- {next_state, wait_flight2, State0, [{next_event, internal, M}]}
+ State ->
+ {next_state, wait_finished, State, []}
end.
+wait_finished(internal,
+ #change_cipher_spec{} = ChangeCipherSpec, State0, _Module) ->
+ case tls_handshake_1_3:do_wait_finished(ChangeCipherSpec, State0) of
+ #alert{} = Alert ->
+ ssl_connection:handle_own_alert(Alert, {3,4}, wait_finished, State0);
+ State1 ->
+ {Record, State} = tls_connection:next_record(State1),
+ tls_connection:next_event(?FUNCTION_NAME, Record, State)
+ end;
+wait_finished(internal,
+ #finished{} = Finished, State0, Module) ->
+ case tls_handshake_1_3:do_wait_finished(Finished, State0) of
+ #alert{} = Alert ->
+ ssl_connection:handle_own_alert(Alert, {3,4}, finished, State0);
+ State1 ->
+ {Record, State} = ssl_connection:prepare_connection(State1, Module),
+ tls_connection:next_event(connection, Record, State)
+ end;
+wait_finished(Type, Msg, State, Connection) ->
+ ssl_connection:handle_common_event(Type, Msg, ?FUNCTION_NAME, State, Connection).
+
+
update_state(#state{connection_states = ConnectionStates0,
+ connection_env = CEnv,
session = Session} = State,
#{cipher := Cipher,
key_share := KeyShare,
@@ -166,4 +186,4 @@ update_state(#state{connection_states = ConnectionStates0,
State#state{connection_states = ConnectionStates,
key_share = KeyShare,
session = Session#session{session_id = SessionId},
- negotiated_version = {3,4}}.
+ connection_env = CEnv#connection_env{negotiated_version = {3,4}}}.
diff --git a/lib/ssl/src/tls_handshake.erl b/lib/ssl/src/tls_handshake.erl
index eee2437bfd..e7cee1956b 100644
--- a/lib/ssl/src/tls_handshake.erl
+++ b/lib/ssl/src/tls_handshake.erl
@@ -31,6 +31,7 @@
-include("ssl_alert.hrl").
-include("ssl_internal.hrl").
-include("ssl_cipher.hrl").
+-include("ssl_api.hrl").
-include_lib("public_key/include/public_key.hrl").
-include_lib("kernel/include/logger.hrl").
@@ -49,7 +50,7 @@
%% Handshake handling
%%====================================================================
%%--------------------------------------------------------------------
--spec client_hello(host(), inet:port_number(), ssl_record:connection_states(),
+-spec client_hello(ssl:host(), inet:port_number(), ssl_record:connection_states(),
#ssl_options{}, integer(), atom(), boolean(), der_cert(),
#key_share_client_hello{} | undefined) ->
#client_hello{}.
@@ -97,13 +98,13 @@ client_hello(Host, Port, ConnectionStates,
-spec hello(#server_hello{} | #client_hello{}, #ssl_options{},
ssl_record:connection_states() | {inet:port_number(), #session{}, db_handle(),
atom(), ssl_record:connection_states(),
- binary() | undefined, ssl_cipher_format:key_algo()},
+ binary() | undefined, ssl:kex_algo()},
boolean()) ->
- {tls_record:tls_version(), session_id(),
+ {tls_record:tls_version(), ssl:session_id(),
ssl_record:connection_states(), alpn | npn, binary() | undefined}|
{tls_record:tls_version(), {resumed | new, #session{}},
ssl_record:connection_states(), binary() | undefined,
- HelloExt::map(), {ssl_cipher_format:hash(), ssl_cipher_format:sign_algo()} |
+ HelloExt::map(), {ssl:hash(), ssl:sign_algo()} |
undefined} | #alert{}.
%%
%% Description: Handles a received hello message
diff --git a/lib/ssl/src/tls_handshake_1_3.erl b/lib/ssl/src/tls_handshake_1_3.erl
index f92c54dc53..6a6de4b988 100644
--- a/lib/ssl/src/tls_handshake_1_3.erl
+++ b/lib/ssl/src/tls_handshake_1_3.erl
@@ -45,7 +45,8 @@
encrypted_extensions/0,
server_hello/4]).
--export([do_negotiated/2]).
+-export([do_negotiated/2,
+ do_wait_finished/2]).
%%====================================================================
%% Create handshake messages
@@ -148,12 +149,11 @@ finished(#state{connection_states = ConnectionStates,
handshake_env =
#handshake_env{
tls_handshake_history = {Messages, _}}}) ->
- #{security_parameters := SecParamsR} =
+ #{security_parameters := SecParamsR,
+ cipher_state := #cipher_state{finished_key = FinishedKey}} =
ssl_record:current_connection_state(ConnectionStates, write),
- #security_parameters{prf_algorithm = HKDFAlgo,
- master_secret = SHTS} = SecParamsR,
+ #security_parameters{prf_algorithm = HKDFAlgo} = SecParamsR,
- FinishedKey = tls_v1:finished_key(SHTS, HKDFAlgo),
VerifyData = tls_v1:finished_verify_data(FinishedKey, HKDFAlgo, Messages),
#finished{
@@ -452,7 +452,7 @@ do_negotiated(#{client_share := ClientKey,
ssl_options = #ssl_options{} = _SslOpts,
key_share = KeyShare,
handshake_env = #handshake_env{tls_handshake_history = _HHistory0},
- private_key = CertPrivateKey,
+ connection_env = #connection_env{private_key = CertPrivateKey},
static_env = #static_env{
cert_db = CertDbHandle,
cert_db_ref = CertDbRef,
@@ -468,12 +468,8 @@ do_negotiated(#{client_share := ClientKey,
{State1, _} = tls_connection:send_handshake(ServerHello, State0),
- {HandshakeSecret, ReadKey, ReadIV, WriteKey, WriteIV} =
- calculate_security_parameters(ClientKey, SelectedGroup, KeyShare, State1),
-
State2 =
- update_pending_connection_states(State1, HandshakeSecret,
- ReadKey, ReadIV, WriteKey, WriteIV),
+ calculate_handshake_secrets(ClientKey, SelectedGroup, KeyShare, State1),
State3 = ssl_record:step_encryption_state(State2),
@@ -498,33 +494,114 @@ do_negotiated(#{client_share := ClientKey,
%% Create Finished
Finished = finished(State6),
- %% Encode Certificate, CertifricateVerify
- {_State7, _} = tls_connection:send_handshake(Finished, State6),
+ %% Encode Finished
+ State7 = tls_connection:queue_handshake(Finished, State6),
+
+ %% Send first flight
+ {State8, _} = tls_connection:send_handshake_flight(State7),
+
+ State8
+
+ catch
+ {Ref, {state_not_implemented, State}} ->
+ %% TODO
+ ?ALERT_REC(?FATAL, ?INTERNAL_ERROR, {state_not_implemented, State})
+ end.
- %% Send finished
- %% Next record/Next event
+do_wait_finished(#change_cipher_spec{},
+ #state{connection_states = _ConnectionStates0,
+ session = #session{session_id = _SessionId,
+ own_certificate = _OwnCert},
+ ssl_options = #ssl_options{} = _SslOpts,
+ key_share = _KeyShare,
+ handshake_env = #handshake_env{tls_handshake_history = _HHistory0},
+ static_env = #static_env{
+ cert_db = _CertDbHandle,
+ cert_db_ref = _CertDbRef,
+ socket = _Socket,
+ transport_cb = _Transport}
+ } = State0) ->
+ %% {Ref,Maybe} = maybe(),
- Maybe(not_implemented(negotiated))
+ try
+ State0
catch
- {Ref, {state_not_implemented, State}} ->
+ {_Ref, {state_not_implemented, State}} ->
+ ?ALERT_REC(?FATAL, ?INTERNAL_ERROR, {state_not_implemented, State})
+ end;
+do_wait_finished(#finished{verify_data = VerifyData},
+ #state{connection_states = _ConnectionStates0,
+ session = #session{session_id = _SessionId,
+ own_certificate = _OwnCert},
+ ssl_options = #ssl_options{} = _SslOpts,
+ key_share = _KeyShare,
+ handshake_env = #handshake_env{tls_handshake_history = _HHistory0},
+ static_env = #static_env{
+ cert_db = _CertDbHandle,
+ cert_db_ref = _CertDbRef,
+ socket = _Socket,
+ transport_cb = _Transport}
+ } = State0) ->
+
+ {Ref,Maybe} = maybe(),
+
+ try
+ Maybe(validate_client_finished(State0, VerifyData)),
+
+ State1 = calculate_traffic_secrets(State0),
+
+ %% Configure traffic keys
+ ssl_record:step_encryption_state(State1)
+
+
+ catch
+ {Ref, decrypt_error} ->
+ ?ALERT_REC(?FATAL, ?DECRYPT_ERROR, decrypt_error);
+ {_, {state_not_implemented, State}} ->
%% TODO
?ALERT_REC(?FATAL, ?INTERNAL_ERROR, {state_not_implemented, State})
end.
%% TODO: Remove this function!
-not_implemented(State) ->
- {error, {state_not_implemented, State}}.
+%% not_implemented(State) ->
+%% {error, {state_not_implemented, State}}.
+
+
+%% Recipients of Finished messages MUST verify that the contents are
+%% correct and if incorrect MUST terminate the connection with a
+%% "decrypt_error" alert.
+validate_client_finished(#state{connection_states = ConnectionStates,
+ handshake_env =
+ #handshake_env{
+ tls_handshake_history = {Messages0, _}}}, VerifyData) ->
+ #{security_parameters := SecParamsR,
+ cipher_state := #cipher_state{finished_key = FinishedKey}} =
+ ssl_record:current_connection_state(ConnectionStates, read),
+ #security_parameters{prf_algorithm = HKDFAlgo} = SecParamsR,
+
+ %% Drop the client's finished message, it is not part of the handshake context
+ %% when the client calculates its finished message.
+ [_|Messages] = Messages0,
+
+ ControlData = tls_v1:finished_verify_data(FinishedKey, HKDFAlgo, Messages),
+ compare_verify_data(ControlData, VerifyData).
+
+
+compare_verify_data(Data, Data) ->
+ ok;
+compare_verify_data(_, _) ->
+ {error, decrypt_error}.
-calculate_security_parameters(ClientKey, SelectedGroup, KeyShare,
+calculate_handshake_secrets(ClientKey, SelectedGroup, KeyShare,
#state{connection_states = ConnectionStates,
handshake_env =
#handshake_env{
- tls_handshake_history = HHistory}}) ->
+ tls_handshake_history = HHistory}} = State0) ->
#{security_parameters := SecParamsR} =
ssl_record:pending_connection_state(ConnectionStates, read),
#security_parameters{prf_algorithm = HKDFAlgo,
@@ -550,23 +627,46 @@ calculate_security_parameters(ClientKey, SelectedGroup, KeyShare,
{ReadKey, ReadIV} = tls_v1:calculate_traffic_keys(HKDFAlgo, Cipher, ClientHSTrafficSecret),
{WriteKey, WriteIV} = tls_v1:calculate_traffic_keys(HKDFAlgo, Cipher, ServerHSTrafficSecret),
- %% TODO: store all relevant secrets in state!
- {ServerHSTrafficSecret, ReadKey, ReadIV, WriteKey, WriteIV}.
+ %% Calculate Finished Keys
+ ReadFinishedKey = tls_v1:finished_key(ClientHSTrafficSecret, HKDFAlgo),
+ WriteFinishedKey = tls_v1:finished_key(ServerHSTrafficSecret, HKDFAlgo),
+
+ update_pending_connection_states(State0, HandshakeSecret,
+ ReadKey, ReadIV, ReadFinishedKey,
+ WriteKey, WriteIV, WriteFinishedKey).
+
+calculate_traffic_secrets(#state{connection_states = ConnectionStates,
+ handshake_env =
+ #handshake_env{
+ tls_handshake_history = HHistory}} = State0) ->
+ #{security_parameters := SecParamsR} =
+ ssl_record:pending_connection_state(ConnectionStates, read),
+ #security_parameters{prf_algorithm = HKDFAlgo,
+ cipher_suite = CipherSuite,
+ master_secret = HandshakeSecret} = SecParamsR,
- %% %% Update pending connection state
- %% PendingRead0 = ssl_record:pending_connection_state(ConnectionStates, read),
- %% PendingWrite0 = ssl_record:pending_connection_state(ConnectionStates, write),
+ MasterSecret =
+ tls_v1:key_schedule(master_secret, HKDFAlgo, HandshakeSecret),
- %% PendingRead = update_conn_state(PendingRead0, HandshakeSecret, ReadKey, ReadIV),
- %% PendingWrite = update_conn_state(PendingWrite0, HandshakeSecret, WriteKey, WriteIV),
+ {Messages0, _} = HHistory,
+
+ %% Drop Client Finish
+ [_|Messages] = Messages0,
+
+ %% Calculate [sender]_application_traffic_secret_0
+ ClientAppTrafficSecret0 =
+ tls_v1:client_application_traffic_secret_0(HKDFAlgo, MasterSecret, lists:reverse(Messages)),
+ ServerAppTrafficSecret0 =
+ tls_v1:server_application_traffic_secret_0(HKDFAlgo, MasterSecret, lists:reverse(Messages)),
+
+ %% Calculate traffic keys
+ #{cipher := Cipher} = ssl_cipher_format:suite_definition(CipherSuite),
+ {ReadKey, ReadIV} = tls_v1:calculate_traffic_keys(HKDFAlgo, Cipher, ClientAppTrafficSecret0),
+ {WriteKey, WriteIV} = tls_v1:calculate_traffic_keys(HKDFAlgo, Cipher, ServerAppTrafficSecret0),
- %% %% Update pending and copy to current (activate)
- %% %% All subsequent handshake messages are encrypted
- %% %% ([sender]_handshake_traffic_secret)
- %% #{current_read => PendingRead,
- %% current_write => PendingWrite,
- %% pending_read => PendingRead,
- %% pending_write => PendingWrite}.
+ update_pending_connection_states(State0, MasterSecret,
+ ReadKey, ReadIV, undefined,
+ WriteKey, WriteIV, undefined).
get_server_private_key(#key_share_server_hello{server_share = ServerShare}) ->
@@ -602,24 +702,31 @@ calculate_shared_secret(OthersKey, MyKey = #'ECPrivateKey'{}, _Group)
update_pending_connection_states(#state{connection_states =
CS = #{pending_read := PendingRead0,
pending_write := PendingWrite0}} = State,
- HandshakeSecret, ReadKey, ReadIV, WriteKey, WriteIV) ->
- PendingRead = update_connection_state(PendingRead0, HandshakeSecret, ReadKey, ReadIV),
- PendingWrite = update_connection_state(PendingWrite0, HandshakeSecret, WriteKey, WriteIV),
+ HandshakeSecret,
+ ReadKey, ReadIV, ReadFinishedKey,
+ WriteKey, WriteIV, WriteFinishedKey) ->
+ PendingRead = update_connection_state(PendingRead0, HandshakeSecret,
+ ReadKey, ReadIV, ReadFinishedKey),
+ PendingWrite = update_connection_state(PendingWrite0, HandshakeSecret,
+ WriteKey, WriteIV, WriteFinishedKey),
State#state{connection_states = CS#{pending_read => PendingRead,
- pending_write => PendingWrite}}.
+ pending_write => PendingWrite}}.
update_connection_state(ConnectionState = #{security_parameters := SecurityParameters0},
- HandshakeSecret, Key, IV) ->
+ HandshakeSecret, Key, IV, FinishedKey) ->
%% Store secret
SecurityParameters = SecurityParameters0#security_parameters{
master_secret = HandshakeSecret},
ConnectionState#{security_parameters => SecurityParameters,
- cipher_state => cipher_init(Key, IV)}.
+ cipher_state => cipher_init(Key, IV, FinishedKey)}.
-cipher_init(Key, IV) ->
- #cipher_state{key = Key, iv = IV, tag_len = 16}.
+cipher_init(Key, IV, FinishedKey) ->
+ #cipher_state{key = Key,
+ iv = IV,
+ finished_key = FinishedKey,
+ tag_len = 16}.
%% If there is no overlap between the received
diff --git a/lib/ssl/src/tls_record.erl b/lib/ssl/src/tls_record.erl
index ad2bfb7a5c..94506b8edc 100644
--- a/lib/ssl/src/tls_record.erl
+++ b/lib/ssl/src/tls_record.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2007-2018. All Rights Reserved.
+%% Copyright Ericsson AB 2007-2019. 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,9 @@
%% Decoding
-export([decode_cipher_text/4]).
+%% Logging helper
+-export([build_tls_record/1]).
+
%% Protocol version handling
-export([protocol_version/1, lowest_protocol_version/1, lowest_protocol_version/2,
highest_protocol_version/1, highest_protocol_version/2,
@@ -76,15 +79,23 @@ init_connection_states(Role, BeastMitigation) ->
pending_write => Pending}.
%%--------------------------------------------------------------------
--spec get_tls_records(binary(), [tls_version()] | tls_version(), binary(),
- #ssl_options{}) -> {[binary()], binary()} | #alert{}.
+-spec get_tls_records(
+ binary(),
+ [tls_version()] | tls_version(),
+ Buffer0 :: binary() | {'undefined' | #ssl_tls{}, {[binary()],non_neg_integer(),[binary()]}},
+ #ssl_options{}) ->
+ {Records :: [#ssl_tls{}],
+ Buffer :: {'undefined' | #ssl_tls{}, {[binary()],non_neg_integer(),[binary()]}}} |
+ #alert{}.
%%
%% and returns it as a list of tls_compressed binaries also returns leftover
%% Description: Given old buffer and new data from TCP, packs up a records
%% data
%%--------------------------------------------------------------------
-get_tls_records(Data, Version, Buffer, SslOpts) ->
- get_tls_records_aux(Version, <<Buffer/binary, Data/binary>>, [], SslOpts).
+get_tls_records(Data, Versions, Buffer, SslOpts) when is_binary(Buffer) ->
+ parse_tls_records(Versions, {[Data],byte_size(Data),[]}, SslOpts, undefined);
+get_tls_records(Data, Versions, {Hdr, {Front,Size,Rear}}, SslOpts) ->
+ parse_tls_records(Versions, {Front,Size + byte_size(Data),[Data|Rear]}, SslOpts, Hdr).
%%====================================================================
%% Encoding
@@ -106,8 +117,8 @@ encode_handshake(Frag, Version,
ConnectionStates) ->
case iolist_size(Frag) of
N when N > ?MAX_PLAIN_TEXT_LENGTH ->
- Data = split_bin(iolist_to_binary(Frag), Version, BCA, BeastMitigation),
- encode_iolist(?HANDSHAKE, Data, Version, ConnectionStates);
+ Data = split_iovec(erlang:iolist_to_iovec(Frag), Version, BCA, BeastMitigation),
+ encode_fragments(?HANDSHAKE, Version, Data, ConnectionStates);
_ ->
encode_plain_text(?HANDSHAKE, Version, Frag, ConnectionStates)
end.
@@ -119,7 +130,7 @@ encode_handshake(Frag, Version,
%% Description: Encodes an alert message to send on the ssl-socket.
%%--------------------------------------------------------------------
encode_alert_record(Alert, {3, 4}, ConnectionStates) ->
- tls_record_1_3:encode_handshake(Alert, ConnectionStates);
+ tls_record_1_3:encode_alert_record(Alert, ConnectionStates);
encode_alert_record(#alert{level = Level, description = Description},
Version, ConnectionStates) ->
encode_plain_text(?ALERT, Version, <<?BYTE(Level), ?BYTE(Description)>>,
@@ -135,20 +146,20 @@ encode_change_cipher_spec(Version, ConnectionStates) ->
encode_plain_text(?CHANGE_CIPHER_SPEC, Version, ?byte(?CHANGE_CIPHER_SPEC_PROTO), ConnectionStates).
%%--------------------------------------------------------------------
--spec encode_data(binary(), tls_version(), ssl_record:connection_states()) ->
- {iolist(), ssl_record:connection_states()}.
+-spec encode_data([binary()], tls_version(), ssl_record:connection_states()) ->
+ {[[binary()]], ssl_record:connection_states()}.
%%
%% Description: Encodes data to send on the ssl-socket.
%%--------------------------------------------------------------------
encode_data(Data, {3, 4}, ConnectionStates) ->
tls_record_1_3:encode_data(Data, ConnectionStates);
-encode_data(Frag, Version,
+encode_data(Data, Version,
#{current_write := #{beast_mitigation := BeastMitigation,
security_parameters :=
#security_parameters{bulk_cipher_algorithm = BCA}}} =
ConnectionStates) ->
- Data = split_bin(Frag, Version, BCA, BeastMitigation),
- encode_iolist(?APPLICATION_DATA, Data, Version, ConnectionStates).
+ Fragments = split_iovec(Data, Version, BCA, BeastMitigation),
+ encode_fragments(?APPLICATION_DATA, Version, Fragments, ConnectionStates).
%%====================================================================
%% Decoding
@@ -162,57 +173,59 @@ encode_data(Frag, Version,
%%--------------------------------------------------------------------
decode_cipher_text({3,4}, CipherTextRecord, ConnectionStates, _) ->
tls_record_1_3:decode_cipher_text(CipherTextRecord, ConnectionStates);
-decode_cipher_text(_, #ssl_tls{type = Type, version = Version,
- fragment = CipherFragment} = CipherText,
+decode_cipher_text(_, CipherTextRecord,
#{current_read :=
- #{compression_state := CompressionS0,
- sequence_number := Seq,
- cipher_state := CipherS0,
+ #{sequence_number := Seq,
security_parameters :=
- #security_parameters{
- cipher_type = ?AEAD,
- bulk_cipher_algorithm =
- BulkCipherAlgo,
- compression_algorithm = CompAlg}
- } = ReadState0} = ConnnectionStates0, _) ->
- AAD = start_additional_data(Type, Version, ReadState0),
- CipherS1 = ssl_record:nonce_seed(BulkCipherAlgo, <<?UINT64(Seq)>>, CipherS0),
- case ssl_record:decipher_aead(BulkCipherAlgo, CipherS1, AAD, CipherFragment, Version) of
- {PlainFragment, CipherState} ->
- {Plain, CompressionS1} = ssl_record:uncompress(CompAlg,
- PlainFragment, CompressionS0),
- ConnnectionStates = ConnnectionStates0#{
+ #security_parameters{cipher_type = ?AEAD,
+ bulk_cipher_algorithm = BulkCipherAlgo},
+ cipher_state := CipherS0
+ }
+ } = ConnectionStates0, _) ->
+ SeqBin = <<?UINT64(Seq)>>,
+ #ssl_tls{type = Type, version = {MajVer,MinVer} = Version, fragment = Fragment} = CipherTextRecord,
+ StartAdditionalData = <<SeqBin/binary, ?BYTE(Type), ?BYTE(MajVer), ?BYTE(MinVer)>>,
+ CipherS = ssl_record:nonce_seed(BulkCipherAlgo, SeqBin, CipherS0),
+ case ssl_record:decipher_aead(
+ BulkCipherAlgo, CipherS, StartAdditionalData, Fragment, Version)
+ of
+ PlainFragment when is_binary(PlainFragment) ->
+ #{current_read :=
+ #{security_parameters := SecParams,
+ compression_state := CompressionS0} = ReadState0} = ConnectionStates0,
+ {Plain, CompressionS} = ssl_record:uncompress(SecParams#security_parameters.compression_algorithm,
+ PlainFragment, CompressionS0),
+ ConnectionStates = ConnectionStates0#{
current_read => ReadState0#{
- cipher_state => CipherState,
+ cipher_state => CipherS,
sequence_number => Seq + 1,
- compression_state => CompressionS1}},
- {CipherText#ssl_tls{fragment = Plain}, ConnnectionStates};
+ compression_state => CompressionS}},
+ {CipherTextRecord#ssl_tls{fragment = Plain}, ConnectionStates};
#alert{} = Alert ->
Alert
end;
-decode_cipher_text(_, #ssl_tls{type = Type, version = Version,
- fragment = CipherFragment} = CipherText,
- #{current_read :=
- #{compression_state := CompressionS0,
- sequence_number := Seq,
- security_parameters :=
- #security_parameters{compression_algorithm = CompAlg}
- } = ReadState0} = ConnnectionStates0, PaddingCheck) ->
+decode_cipher_text(_, #ssl_tls{version = Version,
+ fragment = CipherFragment} = CipherTextRecord,
+ #{current_read := ReadState0} = ConnnectionStates0, PaddingCheck) ->
case ssl_record:decipher(Version, CipherFragment, ReadState0, PaddingCheck) of
{PlainFragment, Mac, ReadState1} ->
- MacHash = ssl_cipher:calc_mac_hash(Type, Version, PlainFragment, ReadState1),
+ MacHash = ssl_cipher:calc_mac_hash(CipherTextRecord#ssl_tls.type, Version, PlainFragment, ReadState1),
case ssl_record:is_correct_mac(Mac, MacHash) of
true ->
+ #{sequence_number := Seq,
+ compression_state := CompressionS0,
+ security_parameters :=
+ #security_parameters{compression_algorithm = CompAlg}} = ReadState0,
{Plain, CompressionS1} = ssl_record:uncompress(CompAlg,
PlainFragment, CompressionS0),
- ConnnectionStates = ConnnectionStates0#{
- current_read => ReadState1#{
- sequence_number => Seq + 1,
- compression_state => CompressionS1}},
- {CipherText#ssl_tls{fragment = Plain}, ConnnectionStates};
+ ConnnectionStates =
+ ConnnectionStates0#{current_read =>
+ ReadState1#{sequence_number => Seq + 1,
+ compression_state => CompressionS1}},
+ {CipherTextRecord#ssl_tls{fragment = Plain}, ConnnectionStates};
false ->
- ?ALERT_REC(?FATAL, ?BAD_RECORD_MAC)
+ ?ALERT_REC(?FATAL, ?BAD_RECORD_MAC)
end;
#alert{} = Alert ->
Alert
@@ -398,126 +411,230 @@ initial_connection_state(ConnectionEnd, BeastMitigation) ->
server_verify_data => undefined
}.
-get_tls_records_aux({MajVer, MinVer} = Version, <<?BYTE(Type),?BYTE(MajVer),?BYTE(MinVer),
- ?UINT16(Length), Data:Length/binary, Rest/binary>> = RawTLSRecord,
- Acc, SslOpts) when Type == ?APPLICATION_DATA;
- Type == ?HANDSHAKE;
- Type == ?ALERT;
- Type == ?CHANGE_CIPHER_SPEC ->
- ssl_logger:debug(SslOpts#ssl_options.log_level, inbound, 'tls_record', [RawTLSRecord]),
- get_tls_records_aux(Version, Rest, [#ssl_tls{type = Type,
- version = Version,
- fragment = Data} | Acc], SslOpts);
-get_tls_records_aux(Versions, <<?BYTE(Type),?BYTE(MajVer),?BYTE(MinVer),
- ?UINT16(Length), Data:Length/binary, Rest/binary>> = RawTLSRecord,
- Acc, SslOpts) when is_list(Versions) andalso
- ((Type == ?APPLICATION_DATA)
- orelse
- (Type == ?HANDSHAKE)
- orelse
- (Type == ?ALERT)
- orelse
- (Type == ?CHANGE_CIPHER_SPEC)) ->
- case is_acceptable_version({MajVer, MinVer}, Versions) of
+%% Used by logging to recreate the received bytes
+build_tls_record(#ssl_tls{type = Type, version = {MajVer, MinVer}, fragment = Fragment}) ->
+ Length = byte_size(Fragment),
+ <<?BYTE(Type),?BYTE(MajVer),?BYTE(MinVer),?UINT16(Length), Fragment/binary>>.
+
+
+parse_tls_records(Versions, Q, SslOpts, undefined) ->
+ decode_tls_records(Versions, Q, SslOpts, [], undefined, undefined, undefined);
+parse_tls_records(Versions, Q, SslOpts, #ssl_tls{type = Type, version = Version, fragment = Length}) ->
+ decode_tls_records(Versions, Q, SslOpts, [], Type, Version, Length).
+
+%% Generic code path
+decode_tls_records(Versions, {_,Size,_} = Q0, SslOpts, Acc, undefined, _Version, _Length) ->
+ if
+ 5 =< Size ->
+ {<<?BYTE(Type),?BYTE(MajVer),?BYTE(MinVer), ?UINT16(Length)>>, Q} = binary_from_front(5, Q0),
+ validate_tls_records_type(Versions, Q, SslOpts, Acc, Type, {MajVer,MinVer}, Length);
+ 3 =< Size ->
+ {<<?BYTE(Type),?BYTE(MajVer),?BYTE(MinVer)>>, Q} = binary_from_front(3, Q0),
+ validate_tls_records_type(Versions, Q, SslOpts, Acc, Type, {MajVer,MinVer}, undefined);
+ 1 =< Size ->
+ {<<?BYTE(Type)>>, Q} = binary_from_front(1, Q0),
+ validate_tls_records_type(Versions, Q, SslOpts, Acc, Type, undefined, undefined);
+ true ->
+ validate_tls_records_type(Versions, Q0, SslOpts, Acc, undefined, undefined, undefined)
+ end;
+decode_tls_records(Versions, {_,Size,_} = Q0, SslOpts, Acc, Type, undefined, _Length) ->
+ if
+ 4 =< Size ->
+ {<<?BYTE(MajVer),?BYTE(MinVer), ?UINT16(Length)>>, Q} = binary_from_front(4, Q0),
+ validate_tls_record_version(Versions, Q, SslOpts, Acc, Type, {MajVer,MinVer}, Length);
+ 2 =< Size ->
+ {<<?BYTE(MajVer),?BYTE(MinVer)>>, Q} = binary_from_front(2, Q0),
+ validate_tls_record_version(Versions, Q, SslOpts, Acc, Type, {MajVer,MinVer}, undefined);
+ true ->
+ validate_tls_record_version(Versions, Q0, SslOpts, Acc, Type, undefined, undefined)
+ end;
+decode_tls_records(Versions, {_,Size,_} = Q0, SslOpts, Acc, Type, Version, undefined) ->
+ if
+ 2 =< Size ->
+ {<<?UINT16(Length)>>, Q} = binary_from_front(2, Q0),
+ validate_tls_record_length(Versions, Q, SslOpts, Acc, Type, Version, Length);
true ->
- ssl_logger:debug(SslOpts#ssl_options.log_level, inbound, 'tls_record', [RawTLSRecord]),
- get_tls_records_aux(Versions, Rest, [#ssl_tls{type = Type,
- version = {MajVer, MinVer},
- fragment = Data} | Acc], SslOpts);
- false ->
+ validate_tls_record_length(Versions, Q0, SslOpts, Acc, Type, Version, undefined)
+ end;
+decode_tls_records(Versions, Q, SslOpts, Acc, Type, Version, Length) ->
+ validate_tls_record_length(Versions, Q, SslOpts, Acc, Type, Version, Length).
+
+validate_tls_records_type(_Versions, Q, _SslOpts, Acc, undefined, _Version, _Length) ->
+ {lists:reverse(Acc),
+ {undefined, Q}};
+validate_tls_records_type(Versions, Q, SslOpts, Acc, Type, Version, Length) ->
+ if
+ ?KNOWN_RECORD_TYPE(Type) ->
+ validate_tls_record_version(Versions, Q, SslOpts, Acc, Type, Version, Length);
+ true ->
+ %% Not ?KNOWN_RECORD_TYPE(Type)
+ ?ALERT_REC(?FATAL, ?UNEXPECTED_MESSAGE)
+ end.
+
+validate_tls_record_version(_Versions, Q, _SslOpts, Acc, Type, undefined, _Length) ->
+ {lists:reverse(Acc),
+ {#ssl_tls{type = Type, version = undefined, fragment = undefined}, Q}};
+validate_tls_record_version(Versions, Q, SslOpts, Acc, Type, Version, Length) ->
+ case Versions of
+ _ when is_list(Versions) ->
+ case is_acceptable_version(Version, Versions) of
+ true ->
+ validate_tls_record_length(Versions, Q, SslOpts, Acc, Type, Version, Length);
+ false ->
+ ?ALERT_REC(?FATAL, ?BAD_RECORD_MAC)
+ end;
+ {3, 4} when Version =:= {3, 3} ->
+ validate_tls_record_length(Versions, Q, SslOpts, Acc, Type, Version, Length);
+ Version ->
+ %% Exact version match
+ validate_tls_record_length(Versions, Q, SslOpts, Acc, Type, Version, Length);
+ _ ->
?ALERT_REC(?FATAL, ?BAD_RECORD_MAC)
+ end.
+
+validate_tls_record_length(_Versions, Q, _SslOpts, Acc, Type, Version, undefined) ->
+ {lists:reverse(Acc),
+ {#ssl_tls{type = Type, version = Version, fragment = undefined}, Q}};
+validate_tls_record_length(Versions, {_,Size0,_} = Q0, SslOpts, Acc, Type, Version, Length) ->
+ if
+ Length =< ?MAX_CIPHER_TEXT_LENGTH ->
+ if
+ Length =< Size0 ->
+ %% Complete record
+ {Fragment, Q} = binary_from_front(Length, Q0),
+ Record = #ssl_tls{type = Type, version = Version, fragment = Fragment},
+ ssl_logger:debug(SslOpts#ssl_options.log_level, inbound, 'tls_record', Record),
+ decode_tls_records(Versions, Q, SslOpts, [Record|Acc], undefined, undefined, undefined);
+ true ->
+ {lists:reverse(Acc),
+ {#ssl_tls{type = Type, version = Version, fragment = Length}, Q0}}
+ end;
+ true ->
+ ?ALERT_REC(?FATAL, ?RECORD_OVERFLOW)
+ end.
+
+
+binary_from_front(SplitSize, {Front,Size,Rear}) ->
+ binary_from_front(SplitSize, Front, Size, Rear, []).
+%%
+binary_from_front(SplitSize, [], Size, [_] = Rear, Acc) ->
+ %% Optimize a simple case
+ binary_from_front(SplitSize, Rear, Size, [], Acc);
+binary_from_front(SplitSize, [], Size, Rear, Acc) ->
+ binary_from_front(SplitSize, lists:reverse(Rear), Size, [], Acc);
+binary_from_front(SplitSize, [Bin|Front], Size, Rear, []) ->
+ %% Optimize a frequent case
+ BinSize = byte_size(Bin),
+ if
+ SplitSize < BinSize ->
+ {RetBin, Rest} = erlang:split_binary(Bin, SplitSize),
+ {RetBin, {[Rest|Front],Size - SplitSize,Rear}};
+ BinSize < SplitSize ->
+ binary_from_front(SplitSize - BinSize, Front, Size, Rear, [Bin]);
+ true -> % Perfect fit
+ {Bin, {Front,Size - SplitSize,Rear}}
end;
-get_tls_records_aux(_, <<?BYTE(Type),?BYTE(_MajVer),?BYTE(_MinVer),
- ?UINT16(Length), _:Length/binary, _Rest/binary>>,
- _, _) when Type == ?APPLICATION_DATA;
- Type == ?HANDSHAKE;
- Type == ?ALERT;
- Type == ?CHANGE_CIPHER_SPEC ->
- ?ALERT_REC(?FATAL, ?BAD_RECORD_MAC);
-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(_, Data, Acc, _) ->
- case size(Data) =< ?MAX_CIPHER_TEXT_LENGTH + ?INITIAL_BYTES of
- true ->
- {lists:reverse(Acc), Data};
- false ->
- ?ALERT_REC(?FATAL, ?UNEXPECTED_MESSAGE)
+binary_from_front(SplitSize, [Bin|Front], Size, Rear, Acc) ->
+ BinSize = byte_size(Bin),
+ if
+ SplitSize < BinSize ->
+ {Last, Rest} = erlang:split_binary(Bin, SplitSize),
+ RetBin = iolist_to_binary(lists:reverse(Acc, [Last])),
+ {RetBin, {[Rest|Front],Size - byte_size(RetBin),Rear}};
+ BinSize < SplitSize ->
+ binary_from_front(SplitSize - BinSize, Front, Size, Rear, [Bin|Acc]);
+ true -> % Perfect fit
+ RetBin = iolist_to_binary(lists:reverse(Acc, [Bin])),
+ {RetBin, {Front,Size - byte_size(RetBin),Rear}}
end.
+
%%--------------------------------------------------------------------
-encode_plain_text(Type, Version, Data, #{current_write := Write0} = ConnectionStates) ->
- {CipherFragment, Write1} = do_encode_plain_text(Type, Version, Data, Write0),
- {CipherText, Write} = encode_tls_cipher_text(Type, Version, CipherFragment, Write1),
- {CipherText, ConnectionStates#{current_write => Write}}.
-
-encode_tls_cipher_text(Type, {MajVer, MinVer}, Fragment, #{sequence_number := Seq} = Write) ->
- Length = erlang:iolist_size(Fragment),
- {[<<?BYTE(Type), ?BYTE(MajVer), ?BYTE(MinVer), ?UINT16(Length)>>, Fragment],
- Write#{sequence_number => Seq +1}}.
-
-encode_iolist(Type, Data, Version, ConnectionStates0) ->
- {ConnectionStates, EncodedMsg} =
- lists:foldl(fun(Text, {CS0, Encoded}) ->
- {Enc, CS1} =
- encode_plain_text(Type, Version, Text, CS0),
- {CS1, [Enc | Encoded]}
- end, {ConnectionStates0, []}, Data),
- {lists:reverse(EncodedMsg), ConnectionStates}.
-%%--------------------------------------------------------------------
-do_encode_plain_text(Type, Version, Data, #{compression_state := CompS0,
- cipher_state := CipherS0,
- sequence_number := Seq,
- security_parameters :=
- #security_parameters{
- cipher_type = ?AEAD,
- bulk_cipher_algorithm = BCAlg,
- compression_algorithm = CompAlg}
- } = WriteState0) ->
- {Comp, CompS1} = ssl_record:compress(CompAlg, Data, CompS0),
- CipherS = ssl_record:nonce_seed(BCAlg, <<?UINT64(Seq)>>, CipherS0),
- WriteState = WriteState0#{compression_state => CompS1,
- cipher_state => CipherS},
- AAD = start_additional_data(Type, Version, WriteState),
- ssl_record:cipher_aead(Version, Comp, WriteState, AAD);
-do_encode_plain_text(Type, Version, Data, #{compression_state := CompS0,
- security_parameters :=
- #security_parameters{compression_algorithm = CompAlg}
- }= WriteState0) ->
- {Comp, CompS1} = ssl_record:compress(CompAlg, Data, CompS0),
- WriteState1 = WriteState0#{compression_state => CompS1},
- MacHash = ssl_cipher:calc_mac_hash(Type, Version, Comp, WriteState1),
- ssl_record:cipher(Version, Comp, WriteState1, MacHash);
-do_encode_plain_text(_,_,_,CS) ->
+encode_plain_text(Type, Version, Data, ConnectionStates0) ->
+ {[CipherText],ConnectionStates} = encode_fragments(Type, Version, [Data], ConnectionStates0),
+ {CipherText,ConnectionStates}.
+%%--------------------------------------------------------------------
+encode_fragments(Type, Version, Data,
+ #{current_write := #{compression_state := CompS,
+ cipher_state := CipherS,
+ sequence_number := Seq}} = ConnectionStates) ->
+ encode_fragments(Type, Version, Data, ConnectionStates, CompS, CipherS, Seq, []).
+%%
+encode_fragments(_Type, _Version, [], #{current_write := WriteS} = CS,
+ CompS, CipherS, Seq, CipherFragments) ->
+ {lists:reverse(CipherFragments),
+ CS#{current_write := WriteS#{compression_state := CompS,
+ cipher_state := CipherS,
+ sequence_number := Seq}}};
+encode_fragments(Type, Version, [Text|Data],
+ #{current_write := #{security_parameters :=
+ #security_parameters{cipher_type = ?AEAD,
+ bulk_cipher_algorithm = BCAlg,
+ compression_algorithm = CompAlg} = SecPars}} = CS,
+ CompS0, CipherS0, Seq, CipherFragments) ->
+ {CompText, CompS} = ssl_record:compress(CompAlg, Text, CompS0),
+ SeqBin = <<?UINT64(Seq)>>,
+ CipherS1 = ssl_record:nonce_seed(BCAlg, SeqBin, CipherS0),
+ {MajVer, MinVer} = Version,
+ VersionBin = <<?BYTE(MajVer), ?BYTE(MinVer)>>,
+ StartAdditionalData = <<SeqBin/binary, ?BYTE(Type), VersionBin/binary>>,
+ {CipherFragment,CipherS} = ssl_record:cipher_aead(Version, CompText, CipherS1, StartAdditionalData, SecPars),
+ Length = byte_size(CipherFragment),
+ CipherHeader = <<?BYTE(Type), VersionBin/binary, ?UINT16(Length)>>,
+ encode_fragments(Type, Version, Data, CS, CompS, CipherS, Seq + 1,
+ [[CipherHeader, CipherFragment] | CipherFragments]);
+encode_fragments(Type, Version, [Text|Data],
+ #{current_write := #{security_parameters :=
+ #security_parameters{compression_algorithm = CompAlg,
+ mac_algorithm = MacAlgorithm} = SecPars,
+ mac_secret := MacSecret}} = CS,
+ CompS0, CipherS0, Seq, CipherFragments) ->
+ {CompText, CompS} = ssl_record:compress(CompAlg, Text, CompS0),
+ MacHash = ssl_cipher:calc_mac_hash(Type, Version, CompText, MacAlgorithm, MacSecret, Seq),
+ {CipherFragment,CipherS} = ssl_record:cipher(Version, CompText, CipherS0, MacHash, SecPars),
+ Length = byte_size(CipherFragment),
+ {MajVer, MinVer} = Version,
+ CipherHeader = <<?BYTE(Type), ?BYTE(MajVer), ?BYTE(MinVer), ?UINT16(Length)>>,
+ encode_fragments(Type, Version, Data, CS, CompS, CipherS, Seq + 1,
+ [[CipherHeader, CipherFragment] | CipherFragments]);
+encode_fragments(_Type, _Version, _Data, CS, _CompS, _CipherS, _Seq, _CipherFragments) ->
exit({cs, CS}).
%%--------------------------------------------------------------------
-start_additional_data(Type, {MajVer, MinVer},
- #{sequence_number := SeqNo}) ->
- <<?UINT64(SeqNo), ?BYTE(Type), ?BYTE(MajVer), ?BYTE(MinVer)>>.
%% 1/n-1 splitting countermeasure Rizzo/Duong-Beast, RC4 chiphers are
%% not vulnerable to this attack.
-split_bin(<<FirstByte:8, Rest/binary>>, Version, BCA, one_n_minus_one) when
- BCA =/= ?RC4 andalso ({3, 1} == Version orelse
- {3, 0} == Version) ->
- [[FirstByte]|do_split_bin(Rest)];
+split_iovec([<<FirstByte:8, Rest/binary>>|Data], Version, BCA, one_n_minus_one)
+ when (BCA =/= ?RC4) andalso ({3, 1} == Version orelse
+ {3, 0} == Version) ->
+ [[FirstByte]|split_iovec([Rest|Data])];
%% 0/n splitting countermeasure for clients that are incompatible with 1/n-1
%% splitting.
-split_bin(Bin, Version, BCA, zero_n) when
- BCA =/= ?RC4 andalso ({3, 1} == Version orelse
- {3, 0} == Version) ->
- [<<>>|do_split_bin(Bin)];
-split_bin(Bin, _, _, _) ->
- do_split_bin(Bin).
-
-do_split_bin(<<>>) -> [];
-do_split_bin(Bin) ->
- case Bin of
- <<Chunk:?MAX_PLAIN_TEXT_LENGTH/binary, Rest/binary>> ->
- [Chunk|do_split_bin(Rest)];
- _ ->
- [Bin]
- end.
+split_iovec(Data, Version, BCA, zero_n)
+ when (BCA =/= ?RC4) andalso ({3, 1} == Version orelse
+ {3, 0} == Version) ->
+ [<<>>|split_iovec(Data)];
+split_iovec(Data, _Version, _BCA, _BeatMitigation) ->
+ split_iovec(Data).
+
+split_iovec([]) ->
+ [];
+split_iovec(Data) ->
+ {Part,Rest} = split_iovec(Data, ?MAX_PLAIN_TEXT_LENGTH, []),
+ [Part|split_iovec(Rest)].
+%%
+split_iovec([Bin|Data], SplitSize, Acc) ->
+ BinSize = byte_size(Bin),
+ if
+ SplitSize < BinSize ->
+ {Last, Rest} = erlang:split_binary(Bin, SplitSize),
+ {lists:reverse(Acc, [Last]), [Rest|Data]};
+ BinSize < SplitSize ->
+ split_iovec(Data, SplitSize - BinSize, [Bin|Acc]);
+ true -> % Perfect match
+ {lists:reverse(Acc, [Bin]), Data}
+ end;
+split_iovec([], _SplitSize, Acc) ->
+ {lists:reverse(Acc),[]}.
+
%%--------------------------------------------------------------------
lowest_list_protocol_version(Ver, []) ->
Ver;
diff --git a/lib/ssl/src/tls_record_1_3.erl b/lib/ssl/src/tls_record_1_3.erl
index 1681babed9..05acc08392 100644
--- a/lib/ssl/src/tls_record_1_3.erl
+++ b/lib/ssl/src/tls_record_1_3.erl
@@ -123,6 +123,23 @@ decode_cipher_text(#ssl_tls{type = ?OPAQUE_TYPE,
ReadState0#{sequence_number => Seq + 1}},
{decode_inner_plaintext(PlainFragment), ConnectionStates}
end;
+
+%% RFC8446 - TLS 1.3
+%% D.4. Middlebox Compatibility Mode
+%% - If not offering early data, the client sends a dummy
+%% change_cipher_spec record (see the third paragraph of Section 5)
+%% immediately before its second flight. This may either be before
+%% its second ClientHello or before its encrypted handshake flight.
+%% If offering early data, the record is placed immediately after the
+%% first ClientHello.
+decode_cipher_text(#ssl_tls{type = ?CHANGE_CIPHER_SPEC,
+ version = ?LEGACY_VERSION,
+ fragment = <<1>>},
+ ConnectionStates0) ->
+ {#ssl_tls{type = ?CHANGE_CIPHER_SPEC,
+ version = {3,4}, %% Internally use real version
+ fragment = <<1>>}, ConnectionStates0};
+
decode_cipher_text(#ssl_tls{type = Type,
version = ?LEGACY_VERSION,
fragment = CipherFragment},
diff --git a/lib/ssl/src/tls_sender.erl b/lib/ssl/src/tls_sender.erl
index 1f34f9a420..ba73eddf0b 100644
--- a/lib/ssl/src/tls_sender.erl
+++ b/lib/ssl/src/tls_sender.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2018-2018. All Rights Reserved.
+%% Copyright Ericsson AB 2018-2019. 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,20 +38,24 @@
-define(SERVER, ?MODULE).
--record(data, {connection_pid,
- connection_states = #{},
- role,
- socket,
- socket_options,
- tracker,
- protocol_cb,
- transport_cb,
- negotiated_version,
- renegotiate_at,
- connection_monitor,
- dist_handle,
- log_level
- }).
+-record(static,
+ {connection_pid,
+ role,
+ socket,
+ socket_options,
+ tracker,
+ transport_cb,
+ negotiated_version,
+ renegotiate_at,
+ connection_monitor,
+ dist_handle,
+ log_level
+ }).
+
+-record(data,
+ {static = #static{},
+ connection_states = #{}
+ }).
%%%===================================================================
%%% API
@@ -172,6 +176,10 @@ dist_tls_socket(Pid) ->
callback_mode() ->
state_functions.
+
+-define(HANDLE_COMMON,
+ ?FUNCTION_NAME(Type, Msg, StateData) ->
+ handle_common(Type, Msg, StateData)).
%%--------------------------------------------------------------------
-spec init(Args :: term()) ->
gen_statem:init_result(atom()).
@@ -193,41 +201,37 @@ init({call, From}, {Pid, #{current_write := WriteState,
socket := Socket,
socket_options := SockOpts,
tracker := Tracker,
- protocol_cb := Connection,
transport_cb := Transport,
negotiated_version := Version,
renegotiate_at := RenegotiateAt,
log_level := LogLevel}},
- #data{connection_states = ConnectionStates} = StateData0) ->
+ #data{connection_states = ConnectionStates, static = Static0} = StateData0) ->
Monitor = erlang:monitor(process, Pid),
StateData =
- StateData0#data{connection_pid = Pid,
- connection_monitor = Monitor,
- connection_states =
- ConnectionStates#{current_write => WriteState},
- role = Role,
- socket = Socket,
- socket_options = SockOpts,
- tracker = Tracker,
- protocol_cb = Connection,
- transport_cb = Transport,
- negotiated_version = Version,
- renegotiate_at = RenegotiateAt,
- log_level = LogLevel},
+ StateData0#data{connection_states = ConnectionStates#{current_write => WriteState},
+ static = Static0#static{connection_pid = Pid,
+ connection_monitor = Monitor,
+ role = Role,
+ socket = Socket,
+ socket_options = SockOpts,
+ tracker = Tracker,
+ transport_cb = Transport,
+ negotiated_version = Version,
+ renegotiate_at = RenegotiateAt,
+ log_level = LogLevel}},
{next_state, handshake, StateData, [{reply, From, ok}]};
-init(info, Msg, StateData) ->
- handle_info(Msg, ?FUNCTION_NAME, StateData).
+init(_, _, _) ->
+ %% Just in case anything else sneeks through
+ {keep_state_and_data, [postpone]}.
+
%%--------------------------------------------------------------------
-spec connection(gen_statem:event_type(),
Msg :: term(),
StateData :: term()) ->
gen_statem:event_handler_result(atom()).
%%--------------------------------------------------------------------
-connection({call, From}, renegotiate,
- #data{connection_states = #{current_write := Write}} = StateData) ->
- {next_state, handshake, StateData, [{reply, From, {ok, Write}}]};
connection({call, From}, {application_data, AppData},
- #data{socket_options = #socket_options{packet = Packet}} =
+ #data{static = #static{socket_options = #socket_options{packet = Packet}}} =
StateData) ->
case encode_packet(Packet, AppData) of
{error, _} = Error ->
@@ -235,40 +239,40 @@ connection({call, From}, {application_data, AppData},
Data ->
send_application_data(Data, From, ?FUNCTION_NAME, StateData)
end;
-connection({call, From}, {set_opts, _} = Call, StateData) ->
- handle_call(From, Call, ?FUNCTION_NAME, StateData);
+connection({call, From}, {ack_alert, #alert{} = Alert}, StateData0) ->
+ StateData = send_tls_alert(Alert, StateData0),
+ {next_state, ?FUNCTION_NAME, StateData,
+ [{reply,From,ok}]};
+connection({call, From}, renegotiate,
+ #data{connection_states = #{current_write := Write}} = StateData) ->
+ {next_state, handshake, StateData, [{reply, From, {ok, Write}}]};
+connection({call, From}, downgrade, #data{connection_states =
+ #{current_write := Write}} = StateData) ->
+ {next_state, death_row, StateData, [{reply,From, {ok, Write}}]};
+connection({call, From}, {set_opts, Opts}, StateData) ->
+ handle_set_opts(From, Opts, StateData);
connection({call, From}, dist_get_tls_socket,
- #data{protocol_cb = Connection,
- transport_cb = Transport,
- socket = Socket,
- connection_pid = Pid,
- tracker = Tracker} = StateData) ->
- TLSSocket = Connection:socket([Pid, self()], Transport, Socket, Connection, Tracker),
+ #data{static = #static{transport_cb = Transport,
+ socket = Socket,
+ connection_pid = Pid,
+ tracker = Tracker}} = StateData) ->
+ TLSSocket = tls_connection:socket([Pid, self()], Transport, Socket, Tracker),
{next_state, ?FUNCTION_NAME, StateData, [{reply, From, {ok, TLSSocket}}]};
connection({call, From}, {dist_handshake_complete, _Node, DHandle},
- #data{connection_pid = Pid,
- socket_options = #socket_options{packet = Packet}} =
- StateData) ->
+ #data{static = #static{connection_pid = Pid} = Static} = StateData) ->
ok = erlang:dist_ctrl_input_handler(DHandle, Pid),
ok = ssl_connection:dist_handshake_complete(Pid, DHandle),
%% From now on we execute on normal priority
process_flag(priority, normal),
- {next_state, ?FUNCTION_NAME, StateData#data{dist_handle = DHandle},
- [{reply, From, ok}
- | case dist_data(DHandle, Packet) of
- [] ->
- [];
- Data ->
- [{next_event, internal,
- {application_packets,{self(),undefined},Data}}]
- end]};
-connection({call, From}, {ack_alert, #alert{} = Alert}, StateData0) ->
- StateData = send_tls_alert(Alert, StateData0),
- {next_state, ?FUNCTION_NAME, StateData,
- [{reply,From,ok}]};
-connection({call, From}, downgrade, #data{connection_states =
- #{current_write := Write}} = StateData) ->
- {next_state, death_row, StateData, [{reply,From, {ok, Write}}]};
+ {keep_state, StateData#data{static = Static#static{dist_handle = DHandle}},
+ [{reply,From,ok}|
+ case dist_data(DHandle) of
+ [] ->
+ [];
+ Data ->
+ [{next_event, internal,
+ {application_packets,{self(),undefined},erlang:iolist_to_iovec(Data)}}]
+ end]};
connection(internal, {application_packets, From, Data}, StateData) ->
send_application_data(Data, From, ?FUNCTION_NAME, StateData);
%%
@@ -276,29 +280,26 @@ connection(cast, #alert{} = Alert, StateData0) ->
StateData = send_tls_alert(Alert, StateData0),
{next_state, ?FUNCTION_NAME, StateData};
connection(cast, {new_write, WritesState, Version},
- #data{connection_states = ConnectionStates0} = StateData) ->
+ #data{connection_states = ConnectionStates, static = Static} = StateData) ->
{next_state, connection,
StateData#data{connection_states =
- ConnectionStates0#{current_write => WritesState},
- negotiated_version = Version}};
+ ConnectionStates#{current_write => WritesState},
+ static = Static#static{negotiated_version = Version}}};
%%
-connection(info, dist_data,
- #data{dist_handle = DHandle,
- socket_options = #socket_options{packet = Packet}} =
- StateData) ->
- {next_state, ?FUNCTION_NAME, StateData,
- case dist_data(DHandle, Packet) of
+connection(info, dist_data, #data{static = #static{dist_handle = DHandle}}) ->
+ {keep_state_and_data,
+ case dist_data(DHandle) of
[] ->
[];
Data ->
[{next_event, internal,
- {application_packets,{self(),undefined},Data}}]
+ {application_packets,{self(),undefined},erlang:iolist_to_iovec(Data)}}]
end};
connection(info, tick, StateData) ->
consume_ticks(),
- {next_state, ?FUNCTION_NAME, StateData,
- [{next_event, {call, {self(), undefined}},
- {application_data, <<>>}}]};
+ Data = [<<0:32>>], % encode_packet(4, <<>>)
+ From = {self(), undefined},
+ send_application_data(Data, From, ?FUNCTION_NAME, StateData);
connection(info, {send, From, Ref, Data}, _StateData) ->
%% This is for testing only!
%%
@@ -307,29 +308,37 @@ connection(info, {send, From, Ref, Data}, _StateData) ->
From ! {Ref, ok},
{keep_state_and_data,
[{next_event, {call, {self(), undefined}},
- {application_data, iolist_to_binary(Data)}}]};
-connection(info, Msg, StateData) ->
- handle_info(Msg, ?FUNCTION_NAME, StateData).
+ {application_data, erlang:iolist_to_iovec(Data)}}]};
+?HANDLE_COMMON.
+
%%--------------------------------------------------------------------
-spec handshake(gen_statem:event_type(),
Msg :: term(),
StateData :: term()) ->
gen_statem:event_handler_result(atom()).
%%--------------------------------------------------------------------
-handshake({call, From}, {set_opts, _} = Call, StateData) ->
- handle_call(From, Call, ?FUNCTION_NAME, StateData);
+handshake({call, From}, {set_opts, Opts}, StateData) ->
+ handle_set_opts(From, Opts, StateData);
handshake({call, _}, _, _) ->
+ %% Postpone all calls to the connection state
+ {keep_state_and_data, [postpone]};
+handshake(internal, {application_packets,_,_}, _) ->
{keep_state_and_data, [postpone]};
handshake(cast, {new_write, WritesState, Version},
- #data{connection_states = ConnectionStates0} = StateData) ->
+ #data{connection_states = ConnectionStates, static = Static} = StateData) ->
{next_state, connection,
- StateData#data{connection_states =
- ConnectionStates0#{current_write => WritesState},
- negotiated_version = Version}};
-handshake(internal, {application_packets,_,_}, _) ->
+ StateData#data{connection_states = ConnectionStates#{current_write => WritesState},
+ static = Static#static{negotiated_version = Version}}};
+handshake(info, dist_data, _) ->
{keep_state_and_data, [postpone]};
-handshake(info, Msg, StateData) ->
- handle_info(Msg, ?FUNCTION_NAME, StateData).
+handshake(info, tick, _) ->
+ %% Ignore - data is sent anyway during handshake
+ consume_ticks(),
+ keep_state_and_data;
+handshake(info, {send, _, _, _}, _) ->
+ %% Testing only, OTP distribution test suites...
+ {keep_state_and_data, [postpone]};
+?HANDLE_COMMON.
%%--------------------------------------------------------------------
-spec death_row(gen_statem:event_type(),
@@ -364,52 +373,69 @@ code_change(_OldVsn, State, Data, _Extra) ->
%%%===================================================================
%%% Internal functions
%%%===================================================================
-handle_call(From, {set_opts, Opts}, StateName, #data{socket_options = SockOpts} = StateData) ->
- {next_state, StateName, StateData#data{socket_options = set_opts(SockOpts, Opts)}, [{reply, From, ok}]}.
-
-handle_info({'DOWN', Monitor, _, _, Reason}, _,
- #data{connection_monitor = Monitor,
- dist_handle = Handle} = StateData) when Handle =/= undefined->
- {next_state, death_row, StateData, [{state_timeout, 5000, Reason}]};
-handle_info({'DOWN', Monitor, _, _, _}, _,
- #data{connection_monitor = Monitor} = StateData) ->
+
+handle_set_opts(
+ From, Opts, #data{static = #static{socket_options = SockOpts} = Static} = StateData) ->
+ {keep_state, StateData#data{static = Static#static{socket_options = set_opts(SockOpts, Opts)}},
+ [{reply, From, ok}]}.
+
+handle_common(
+ {call, From}, {set_opts, Opts},
+ #data{static = #static{socket_options = SockOpts} = Static} = StateData) ->
+ {keep_state, StateData#data{static = Static#static{socket_options = set_opts(SockOpts, Opts)}},
+ [{reply, From, ok}]};
+handle_common(
+ info, {'DOWN', Monitor, _, _, Reason},
+ #data{static = #static{connection_monitor = Monitor,
+ dist_handle = Handle}} = StateData) when Handle =/= undefined ->
+ {next_state, death_row, StateData,
+ [{state_timeout, 5000, Reason}]};
+handle_common(
+ info, {'DOWN', Monitor, _, _, _},
+ #data{static = #static{connection_monitor = Monitor}} = StateData) ->
{stop, normal, StateData};
-handle_info(_,_,_) ->
+handle_common(info, Msg, _) ->
+ Report =
+ io_lib:format("TLS sender: Got unexpected info: ~p ~n", [Msg]),
+ error_logger:info_report(Report),
+ keep_state_and_data;
+handle_common(Type, Msg, _) ->
+ Report =
+ io_lib:format(
+ "TLS sender: Got unexpected event: ~p ~n", [{Type,Msg}]),
+ error_logger:error_report(Report),
keep_state_and_data.
-send_tls_alert(Alert, #data{negotiated_version = Version,
- socket = Socket,
- protocol_cb = Connection,
- transport_cb = Transport,
- connection_states = ConnectionStates0,
- log_level = LogLevel} = StateData0) ->
+send_tls_alert(#alert{} = Alert,
+ #data{static = #static{negotiated_version = Version,
+ socket = Socket,
+ transport_cb = Transport,
+ log_level = LogLevel},
+ connection_states = ConnectionStates0} = StateData0) ->
{BinMsg, ConnectionStates} =
- Connection:encode_alert(Alert, Version, ConnectionStates0),
- Connection:send(Transport, Socket, BinMsg),
+ tls_record:encode_alert_record(Alert, Version, ConnectionStates0),
+ tls_socket:send(Transport, Socket, BinMsg),
ssl_logger:debug(LogLevel, outbound, 'tls_record', BinMsg),
StateData0#data{connection_states = ConnectionStates}.
send_application_data(Data, From, StateName,
- #data{connection_pid = Pid,
- socket = Socket,
- dist_handle = DistHandle,
- negotiated_version = Version,
- protocol_cb = Connection,
- transport_cb = Transport,
- connection_states = ConnectionStates0,
- renegotiate_at = RenegotiateAt,
- log_level = LogLevel} = StateData0) ->
+ #data{static = #static{connection_pid = Pid,
+ socket = Socket,
+ dist_handle = DistHandle,
+ negotiated_version = Version,
+ transport_cb = Transport,
+ renegotiate_at = RenegotiateAt,
+ log_level = LogLevel},
+ connection_states = ConnectionStates0} = StateData0) ->
case time_to_renegotiate(Data, ConnectionStates0, RenegotiateAt) of
true ->
ssl_connection:internal_renegotiation(Pid, ConnectionStates0),
{next_state, handshake, StateData0,
[{next_event, internal, {application_packets, From, Data}}]};
false ->
- {Msgs, ConnectionStates} =
- Connection:encode_data(
- iolist_to_binary(Data), Version, ConnectionStates0),
+ {Msgs, ConnectionStates} = tls_record:encode_data(Data, Version, ConnectionStates0),
StateData = StateData0#data{connection_states = ConnectionStates},
- case Connection:send(Transport, Socket, Msgs) of
+ case tls_socket:send(Transport, Socket, Msgs) of
ok when DistHandle =/= undefined ->
ssl_logger:debug(LogLevel, outbound, 'tls_record', Msgs),
{next_state, StateName, StateData, []};
@@ -427,9 +453,9 @@ send_application_data(Data, From, StateName,
encode_packet(Packet, Data) ->
Len = iolist_size(Data),
case Packet of
- 1 when Len < (1 bsl 8) -> [<<Len:8>>,Data];
- 2 when Len < (1 bsl 16) -> [<<Len:16>>,Data];
- 4 when Len < (1 bsl 32) -> [<<Len:32>>,Data];
+ 1 when Len < (1 bsl 8) -> [<<Len:8>>|Data];
+ 2 when Len < (1 bsl 16) -> [<<Len:16>>|Data];
+ 4 when Len < (1 bsl 32) -> [<<Len:32>>|Data];
N when N =:= 1; N =:= 2; N =:= 4 ->
{error,
{badarg, {packet_to_large, Len, (1 bsl (Packet bsl 3)) - 1}}};
@@ -466,22 +492,30 @@ call(FsmPid, Event) ->
{error, closed}
end.
-%%---------------Erlang distribution --------------------------------------
+%%-------------- Erlang distribution helpers ------------------------------
-dist_data(DHandle, Packet) ->
+dist_data(DHandle) ->
case erlang:dist_ctrl_get_data(DHandle) of
none ->
erlang:dist_ctrl_get_data_notification(DHandle),
[];
- Data ->
- %% This is encode_packet(4, Data) without Len check
- %% since the emulator will always deliver a Data
- %% smaller than 4 GB, and the distribution will
- %% therefore always have to use {packet,4}
+ %% This is encode_packet(4, Data) without Len check
+ %% since the emulator will always deliver a Data
+ %% smaller than 4 GB, and the distribution will
+ %% therefore always have to use {packet,4}
+ Data when is_binary(Data) ->
+ Len = byte_size(Data),
+ [[<<Len:32>>,Data]|dist_data(DHandle)];
+ [BA,BB] = Data ->
+ Len = byte_size(BA) + byte_size(BB),
+ [[<<Len:32>>|Data]|dist_data(DHandle)];
+ Data when is_list(Data) ->
Len = iolist_size(Data),
- [<<Len:32>>,Data|dist_data(DHandle, Packet)]
+ [[<<Len:32>>|Data]|dist_data(DHandle)]
end.
+
+%% Empty the inbox from distribution ticks - do not let them accumulate
consume_ticks() ->
receive tick ->
consume_ticks()
diff --git a/lib/ssl/src/tls_v1.erl b/lib/ssl/src/tls_v1.erl
index 5c023bd2d8..f103f3218b 100644
--- a/lib/ssl/src/tls_v1.erl
+++ b/lib/ssl/src/tls_v1.erl
@@ -64,7 +64,7 @@
%% TLS 1.3 ---------------------------------------------------
-spec derive_secret(Secret::binary(), Label::binary(),
- Messages::iodata(), Algo::ssl_cipher_format:hash()) -> Key::binary().
+ Messages::iodata(), Algo::ssl:hash()) -> Key::binary().
derive_secret(Secret, Label, Messages, Algo) ->
Hash = crypto:hash(mac_algo(Algo), Messages),
hkdf_expand_label(Secret, Label,
@@ -72,7 +72,7 @@ derive_secret(Secret, Label, Messages, Algo) ->
-spec hkdf_expand_label(Secret::binary(), Label0::binary(),
Context::binary(), Length::integer(),
- Algo::ssl_cipher_format:hash()) -> KeyingMaterial::binary().
+ Algo::ssl:hash()) -> KeyingMaterial::binary().
hkdf_expand_label(Secret, Label0, Context, Length, Algo) ->
HkdfLabel = create_info(Label0, Context, Length),
hkdf_expand(Secret, HkdfLabel, Length, Algo).
@@ -93,7 +93,7 @@ create_info(Label0, Context0, Length) ->
Content = <<Label/binary, Context/binary>>,
<<?UINT16(Length), Content/binary>>.
--spec hkdf_extract(MacAlg::ssl_cipher_format:hash(), Salt::binary(),
+-spec hkdf_extract(MacAlg::ssl:hash(), Salt::binary(),
KeyingMaterial::binary()) -> PseudoRandKey::binary().
hkdf_extract(MacAlg, Salt, KeyingMaterial) ->
@@ -101,14 +101,14 @@ hkdf_extract(MacAlg, Salt, KeyingMaterial) ->
-spec hkdf_expand(PseudoRandKey::binary(), ContextInfo::binary(),
- Length::integer(), Algo::ssl_cipher_format:hash()) -> KeyingMaterial::binary().
+ Length::integer(), Algo::ssl:hash()) -> KeyingMaterial::binary().
hkdf_expand(PseudoRandKey, ContextInfo, Length, Algo) ->
Iterations = erlang:ceil(Length / ssl_cipher:hash_size(Algo)),
hkdf_expand(Algo, PseudoRandKey, ContextInfo, Length, 1, Iterations, <<>>, <<>>).
--spec transcript_hash(Messages::iodata(), Algo::ssl_cipher_format:hash()) -> Hash::binary().
+-spec transcript_hash(Messages::iodata(), Algo::ssl:hash()) -> Hash::binary().
transcript_hash(Messages, Algo) ->
crypto:hash(mac_algo(Algo), Messages).
diff --git a/lib/ssl/test/Makefile b/lib/ssl/test/Makefile
index a4adc7561b..57b74115ed 100644
--- a/lib/ssl/test/Makefile
+++ b/lib/ssl/test/Makefile
@@ -29,7 +29,7 @@ include $(ERL_TOP)/make/$(TARGET)/otp.mk
# Application version
# ----------------------------------------------------
include ../vsn.mk
-VSN=$(GS_VSN)
+VSN=$(SSL_VSN)
# ----------------------------------------------------
# Target Specs
diff --git a/lib/ssl/test/ssl_alpn_handshake_SUITE.erl b/lib/ssl/test/ssl_alpn_handshake_SUITE.erl
index 7f7c3da5ab..dfc780479e 100644
--- a/lib/ssl/test/ssl_alpn_handshake_SUITE.erl
+++ b/lib/ssl/test/ssl_alpn_handshake_SUITE.erl
@@ -153,41 +153,41 @@ protocols_must_be_a_binary_list(Config) when is_list(Config) ->
empty_client(Config) when is_list(Config) ->
run_failing_handshake(Config,
- [{alpn_advertised_protocols, []}],
- [{alpn_preferred_protocols, [<<"spdy/2">>, <<"spdy/3">>, <<"http/2">>]}],
- {error,{tls_alert,"no application protocol"}}).
+ [{alpn_advertised_protocols, []}],
+ [{alpn_preferred_protocols, [<<"spdy/2">>, <<"spdy/3">>, <<"http/2">>]}],
+ no_application_protocol).
%--------------------------------------------------------------------------------
empty_server(Config) when is_list(Config) ->
run_failing_handshake(Config,
- [{alpn_advertised_protocols, [<<"http/1.0">>, <<"http/1.1">>]}],
- [{alpn_preferred_protocols, []}],
- {error,{tls_alert,"no application protocol"}}).
+ [{alpn_advertised_protocols, [<<"http/1.0">>, <<"http/1.1">>]}],
+ [{alpn_preferred_protocols, []}],
+ no_application_protocol).
%--------------------------------------------------------------------------------
empty_client_empty_server(Config) when is_list(Config) ->
run_failing_handshake(Config,
- [{alpn_advertised_protocols, []}],
- [{alpn_preferred_protocols, []}],
- {error,{tls_alert,"no application protocol"}}).
+ [{alpn_advertised_protocols, []}],
+ [{alpn_preferred_protocols, []}],
+ no_application_protocol).
%--------------------------------------------------------------------------------
no_matching_protocol(Config) when is_list(Config) ->
run_failing_handshake(Config,
- [{alpn_advertised_protocols, [<<"http/1.0">>, <<"http/1.1">>]}],
- [{alpn_preferred_protocols, [<<"spdy/2">>, <<"spdy/3">>, <<"http/2">>]}],
- {error,{tls_alert,"no application protocol"}}).
+ [{alpn_advertised_protocols, [<<"http/1.0">>, <<"http/1.1">>]}],
+ [{alpn_preferred_protocols, [<<"spdy/2">>, <<"spdy/3">>, <<"http/2">>]}],
+ no_application_protocol).
%--------------------------------------------------------------------------------
client_alpn_and_server_alpn(Config) when is_list(Config) ->
run_handshake(Config,
- [{alpn_advertised_protocols, [<<"http/1.0">>, <<"http/1.1">>]}],
- [{alpn_preferred_protocols, [<<"spdy/2">>, <<"http/1.1">>, <<"http/1.0">>]}],
- {ok, <<"http/1.1">>}).
+ [{alpn_advertised_protocols, [<<"http/1.0">>, <<"http/1.1">>]}],
+ [{alpn_preferred_protocols, [<<"spdy/2">>, <<"http/1.1">>, <<"http/1.0">>]}],
+ {ok, <<"http/1.1">>}).
%--------------------------------------------------------------------------------
@@ -297,7 +297,7 @@ alpn_not_supported_server(Config) when is_list(Config)->
%% Internal functions ------------------------------------------------
%%--------------------------------------------------------------------
-run_failing_handshake(Config, ClientExtraOpts, ServerExtraOpts, ExpectedResult) ->
+run_failing_handshake(Config, ClientExtraOpts, ServerExtraOpts, ExpectedAlert) ->
ClientOpts = ClientExtraOpts ++ ssl_test_lib:ssl_options(client_rsa_opts, Config),
ServerOpts = ServerExtraOpts ++ ssl_test_lib:ssl_options(server_rsa_opts, Config),
@@ -313,8 +313,7 @@ run_failing_handshake(Config, ClientExtraOpts, ServerExtraOpts, ExpectedResult)
{from, self()},
{mfa, {?MODULE, placeholder, []}},
{options, ClientOpts}]),
- ssl_test_lib:check_result(Server, ExpectedResult,
- Client, ExpectedResult).
+ ssl_test_lib:check_client_alert(Server, Client, ExpectedAlert).
run_handshake(Config, ClientExtraOpts, ServerExtraOpts, ExpectedProtocol) ->
Data = "hello world",
diff --git a/lib/ssl/test/ssl_basic_SUITE.erl b/lib/ssl/test/ssl_basic_SUITE.erl
index f4ecc4dc33..f109d85e49 100644
--- a/lib/ssl/test/ssl_basic_SUITE.erl
+++ b/lib/ssl/test/ssl_basic_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2007-2018. All Rights Reserved.
+%% Copyright Ericsson AB 2007-2019. 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.
@@ -29,6 +29,7 @@
-include_lib("public_key/include/public_key.hrl").
-include("ssl_api.hrl").
+-include("ssl_cipher.hrl").
-include("ssl_internal.hrl").
-include("ssl_alert.hrl").
-include("ssl_internal.hrl").
@@ -168,6 +169,7 @@ api_tests() ->
socket_options,
cipher_suites,
handshake_continue,
+ handshake_continue_timeout,
hello_client_cancel,
hello_server_cancel
].
@@ -274,7 +276,8 @@ tls13_test_group() ->
tls13_enable_server_side,
tls_record_1_3_encode_decode,
tls13_finished_verify_data,
- tls13_1_RTT_handshake].
+ tls13_1_RTT_handshake,
+ tls13_basic_ssl_s_client].
%%--------------------------------------------------------------------
init_per_suite(Config0) ->
@@ -692,6 +695,34 @@ handshake_continue(Config) when is_list(Config) ->
ssl_test_lib:close(Server),
ssl_test_lib:close(Client).
+%%------------------------------------------------------------------
+handshake_continue_timeout() ->
+ [{doc, "Test API function ssl:handshake_continue/3 with short timeout"}].
+handshake_continue_timeout(Config) when is_list(Config) ->
+ ClientOpts = ssl_test_lib:ssl_options(client_verification_opts, Config),
+ ServerOpts = ssl_test_lib:ssl_options(server_verification_opts, Config),
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+
+ Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {timeout, 1},
+ {options, ssl_test_lib:ssl_options([{reuseaddr, true}, {handshake, hello}],
+ Config)},
+ {continue_options, proplists:delete(reuseaddr, ServerOpts)}
+ ]),
+
+ Port = ssl_test_lib:inet_port(Server),
+
+
+ {connect_failed, _} = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {options, ClientOpts}]),
+
+ ssl_test_lib:check_result(Server, {error,timeout}),
+ ssl_test_lib:close(Server).
+
+
%%--------------------------------------------------------------------
hello_client_cancel() ->
[{doc, "Test API function ssl:handshake_cancel/1 on the client side"}].
@@ -713,14 +744,7 @@ hello_client_cancel(Config) when is_list(Config) ->
{from, self()},
{options, ssl_test_lib:ssl_options([{handshake, hello}], Config)},
{continue_options, cancel}]),
- receive
- {Server, {error, {tls_alert, "user canceled"}}} ->
- ok;
- {Server, {error, closed}} ->
- ct:pal("Did not receive the ALERT"),
- ok
- end.
-
+ ssl_test_lib:check_server_alert(Server, user_canceled).
%%--------------------------------------------------------------------
hello_server_cancel() ->
[{doc, "Test API function ssl:handshake_cancel/1 on the server side"}].
@@ -1194,9 +1218,8 @@ fallback(Config) when is_list(Config) ->
[{fallback, true},
{versions, ['tlsv1']}
| ClientOpts]}]),
-
- ssl_test_lib:check_result(Server, {error,{tls_alert,"inappropriate fallback"}},
- Client, {error,{tls_alert,"inappropriate fallback"}}).
+ ssl_test_lib:check_server_alert(Server, Client, inappropriate_fallback).
+
%%--------------------------------------------------------------------
cipher_format() ->
@@ -2662,8 +2685,7 @@ default_reject_anonymous(Config) when is_list(Config) ->
[{ciphers,[CipherSuite]} |
ClientOpts]}]),
- ssl_test_lib:check_result(Server, {error, {tls_alert, "insufficient security"}},
- Client, {error, {tls_alert, "insufficient security"}}).
+ ssl_test_lib:check_server_alert(Server, Client, insufficient_security).
%%--------------------------------------------------------------------
ciphers_ecdsa_signed_certs() ->
@@ -3515,8 +3537,7 @@ no_common_signature_algs(Config) when is_list(Config) ->
{options, [{signature_algs, [{sha384, rsa}]}
| ClientOpts]}]),
- ssl_test_lib:check_result(Server, {error, {tls_alert, "insufficient security"}},
- Client, {error, {tls_alert, "insufficient security"}}).
+ ssl_test_lib:check_server_alert(Server, Client, insufficient_security).
%%--------------------------------------------------------------------
@@ -3547,7 +3568,7 @@ tls_dont_crash_on_handshake_garbage(Config) ->
<<22, 3,3, 5:16, 92,64,37,228,209>> % garbage
]),
% Send unexpected change_cipher_spec
- ok = gen_tcp:send(Socket, <<20, 0,0,12, 111,40,244,7,137,224,16,109,197,110,249,152>>),
+ ok = gen_tcp:send(Socket, <<20, 3,3, 12:16, 111,40,244,7,137,224,16,109,197,110,249,152>>),
% Ensure we receive an alert, not sudden disconnect
{ok, <<21, _/binary>>} = drop_handshakes(Socket, 1000).
@@ -4075,6 +4096,9 @@ rizzo_one_n_minus_one(Config) when is_list(Config) ->
{cipher,
fun(rc4_128) ->
false;
+ %% TODO: remove this clause when chacha is fixed!
+ (chacha20_poly1305) ->
+ false;
(_) ->
true
end}]),
@@ -4216,8 +4240,7 @@ tls_versions_option(Config) when is_list(Config) ->
{Server, _} ->
ok
end,
-
- ssl_test_lib:check_result(ErrClient, {error, {tls_alert, "protocol version"}}).
+ ssl_test_lib:check_client_alert(ErrClient, protocol_version).
%%--------------------------------------------------------------------
@@ -4445,7 +4468,7 @@ tls_record_1_3_encode_decode(_Config) ->
15,117,155,48,24,112,61,15,113,208,127,51,179,227,194,232>>,
<<197,54,168,218,54,91,157,58,30,201,197,142,51,58,53,231,228,
131,57,122,170,78,82,196,30,48,23,16,95,255,185,236>>,
- undefined,undefined,16},
+ undefined,undefined,undefined,16},
client_verify_data => undefined,compression_state => undefined,
mac_secret => undefined,secure_renegotiation => undefined,
security_parameters =>
@@ -4471,7 +4494,7 @@ tls_record_1_3_encode_decode(_Config) ->
15,117,155,48,24,112,61,15,113,208,127,51,179,227,194,232>>,
<<197,54,168,218,54,91,157,58,30,201,197,142,51,58,53,231,228,
131,57,122,170,78,82,196,30,48,23,16,95,255,185,236>>,
- undefined,undefined,16},
+ undefined,undefined,undefined,16},
client_verify_data => undefined,compression_state => undefined,
mac_secret => undefined,secure_renegotiation => undefined,
security_parameters =>
@@ -5036,7 +5059,164 @@ tls13_1_RTT_handshake(_Config) ->
FinishedHS = #finished{verify_data = FinishedVerifyData},
FinishedIOList = tls_handshake:encode_handshake(FinishedHS, {3,4}),
- FinishedHSBin = iolist_to_binary(FinishedIOList).
+ FinishedHSBin = iolist_to_binary(FinishedIOList),
+
+ %% {server} derive secret "tls13 c ap traffic":
+ %%
+ %% PRK (32 octets): 18 df 06 84 3d 13 a0 8b f2 a4 49 84 4c 5f 8a 47
+ %% 80 01 bc 4d 4c 62 79 84 d5 a4 1d a8 d0 40 29 19
+ %%
+ %% hash (32 octets): 96 08 10 2a 0f 1c cc 6d b6 25 0b 7b 7e 41 7b 1a
+ %% 00 0e aa da 3d aa e4 77 7a 76 86 c9 ff 83 df 13
+ %%
+ %% info (54 octets): 00 20 12 74 6c 73 31 33 20 63 20 61 70 20 74 72
+ %% 61 66 66 69 63 20 96 08 10 2a 0f 1c cc 6d b6 25 0b 7b 7e 41 7b
+ %% 1a 00 0e aa da 3d aa e4 77 7a 76 86 c9 ff 83 df 13
+ %%
+ %% expanded (32 octets): 9e 40 64 6c e7 9a 7f 9d c0 5a f8 88 9b ce
+ %% 65 52 87 5a fa 0b 06 df 00 87 f7 92 eb b7 c1 75 04 a5
+
+ %% PRK = MasterSecret
+ CAPTHash =
+ hexstr2bin("96 08 10 2a 0f 1c cc 6d b6 25 0b 7b 7e 41 7b 1a
+ 00 0e aa da 3d aa e4 77 7a 76 86 c9 ff 83 df 13"),
+ CAPTInfo =
+ hexstr2bin("00 20 12 74 6c 73 31 33 20 63 20 61 70 20 74 72
+ 61 66 66 69 63 20 96 08 10 2a 0f 1c cc 6d b6 25 0b 7b 7e 41 7b
+ 1a 00 0e aa da 3d aa e4 77 7a 76 86 c9 ff 83 df 13"),
+
+ CAPTrafficSecret =
+ hexstr2bin("9e 40 64 6c e7 9a 7f 9d c0 5a f8 88 9b ce
+ 65 52 87 5a fa 0b 06 df 00 87 f7 92 eb b7 c1 75 04 a5"),
+
+ CHSF = <<ClientHello/binary,
+ ServerHello/binary,
+ EncryptedExtensions/binary,
+ Certificate/binary,
+ CertificateVerify/binary,
+ FinishedHSBin/binary>>,
+
+ CAPTHash = crypto:hash(HKDFAlgo, CHSF),
+
+ CAPTInfo =
+ tls_v1:create_info(<<"c ap traffic">>, CAPTHash, ssl_cipher:hash_size(HKDFAlgo)),
+
+ CAPTrafficSecret =
+ tls_v1:client_application_traffic_secret_0(HKDFAlgo, {master_secret, MasterSecret}, CHSF),
+
+ %% {server} derive secret "tls13 s ap traffic":
+ %%
+ %% PRK (32 octets): 18 df 06 84 3d 13 a0 8b f2 a4 49 84 4c 5f 8a 47
+ %% 80 01 bc 4d 4c 62 79 84 d5 a4 1d a8 d0 40 29 19
+ %%
+ %% hash (32 octets): 96 08 10 2a 0f 1c cc 6d b6 25 0b 7b 7e 41 7b 1a
+ %% 00 0e aa da 3d aa e4 77 7a 76 86 c9 ff 83 df 13
+ %%
+ %% info (54 octets): 00 20 12 74 6c 73 31 33 20 73 20 61 70 20 74 72
+ %% 61 66 66 69 63 20 96 08 10 2a 0f 1c cc 6d b6 25 0b 7b 7e 41 7b
+ %% 1a 00 0e aa da 3d aa e4 77 7a 76 86 c9 ff 83 df 13
+ %%
+ %% expanded (32 octets): a1 1a f9 f0 55 31 f8 56 ad 47 11 6b 45 a9
+ %% 50 32 82 04 b4 f4 4b fb 6b 3a 4b 4f 1f 3f cb 63 16 43
+
+ %% PRK = MasterSecret
+ %% hash = CAPTHash
+ SAPTInfo =
+ hexstr2bin(" 00 20 12 74 6c 73 31 33 20 73 20 61 70 20 74 72
+ 61 66 66 69 63 20 96 08 10 2a 0f 1c cc 6d b6 25 0b 7b 7e 41 7b
+ 1a 00 0e aa da 3d aa e4 77 7a 76 86 c9 ff 83 df 13"),
+
+ SAPTrafficSecret =
+ hexstr2bin("a1 1a f9 f0 55 31 f8 56 ad 47 11 6b 45 a9
+ 50 32 82 04 b4 f4 4b fb 6b 3a 4b 4f 1f 3f cb 63 16 43"),
+
+ SAPTInfo =
+ tls_v1:create_info(<<"s ap traffic">>, CAPTHash, ssl_cipher:hash_size(HKDFAlgo)),
+
+ SAPTrafficSecret =
+ tls_v1:server_application_traffic_secret_0(HKDFAlgo, {master_secret, MasterSecret}, CHSF),
+
+ %% {server} derive secret "tls13 exp master":
+ %%
+ %% PRK (32 octets): 18 df 06 84 3d 13 a0 8b f2 a4 49 84 4c 5f 8a 47
+ %% 80 01 bc 4d 4c 62 79 84 d5 a4 1d a8 d0 40 29 19
+ %%
+ %% hash (32 octets): 96 08 10 2a 0f 1c cc 6d b6 25 0b 7b 7e 41 7b 1a
+ %% 00 0e aa da 3d aa e4 77 7a 76 86 c9 ff 83 df 13
+ %%
+ %% info (52 octets): 00 20 10 74 6c 73 31 33 20 65 78 70 20 6d 61 73
+ %% 74 65 72 20 96 08 10 2a 0f 1c cc 6d b6 25 0b 7b 7e 41 7b 1a 00
+ %% 0e aa da 3d aa e4 77 7a 76 86 c9 ff 83 df 13
+ %%
+ %% expanded (32 octets): fe 22 f8 81 17 6e da 18 eb 8f 44 52 9e 67
+ %% 92 c5 0c 9a 3f 89 45 2f 68 d8 ae 31 1b 43 09 d3 cf 50
+
+ %% PRK = MasterSecret
+ %% hash = CAPTHash
+ ExporterInfo =
+ hexstr2bin("00 20 10 74 6c 73 31 33 20 65 78 70 20 6d 61 73
+ 74 65 72 20 96 08 10 2a 0f 1c cc 6d b6 25 0b 7b 7e 41 7b 1a 00
+ 0e aa da 3d aa e4 77 7a 76 86 c9 ff 83 df 13"),
+
+ ExporterMasterSecret =
+ hexstr2bin("fe 22 f8 81 17 6e da 18 eb 8f 44 52 9e 67
+ 92 c5 0c 9a 3f 89 45 2f 68 d8 ae 31 1b 43 09 d3 cf 50"),
+
+ ExporterInfo =
+ tls_v1:create_info(<<"exp master">>, CAPTHash, ssl_cipher:hash_size(HKDFAlgo)),
+
+ ExporterMasterSecret =
+ tls_v1:exporter_master_secret(HKDFAlgo, {master_secret, MasterSecret}, CHSF),
+
+ %% {server} derive write traffic keys for application data:
+ %%
+ %% PRK (32 octets): a1 1a f9 f0 55 31 f8 56 ad 47 11 6b 45 a9 50 32
+ %% 82 04 b4 f4 4b fb 6b 3a 4b 4f 1f 3f cb 63 16 43
+ %%
+ %% key info (13 octets): 00 10 09 74 6c 73 31 33 20 6b 65 79 00
+ %%
+ %% key expanded (16 octets): 9f 02 28 3b 6c 9c 07 ef c2 6b b9 f2 ac
+ %% 92 e3 56
+ %%
+ %% iv info (12 octets): 00 0c 08 74 6c 73 31 33 20 69 76 00
+ %%
+ %% iv expanded (12 octets): cf 78 2b 88 dd 83 54 9a ad f1 e9 84
+
+ %% PRK = SAPTrafficsecret
+ %% key info = WriteKeyInfo
+ %% iv info = WrtieIVInfo
+ SWKey =
+ hexstr2bin("9f 02 28 3b 6c 9c 07 ef c2 6b b9 f2 ac 92 e3 56"),
+
+ SWIV =
+ hexstr2bin("cf 78 2b 88 dd 83 54 9a ad f1 e9 84"),
+
+ {SWKey, SWIV} = tls_v1:calculate_traffic_keys(HKDFAlgo, Cipher, SAPTrafficSecret),
+
+ %% {server} derive read traffic keys for handshake data:
+ %%
+ %% PRK (32 octets): b3 ed db 12 6e 06 7f 35 a7 80 b3 ab f4 5e 2d 8f
+ %% 3b 1a 95 07 38 f5 2e 96 00 74 6a 0e 27 a5 5a 21
+ %%
+ %% key info (13 octets): 00 10 09 74 6c 73 31 33 20 6b 65 79 00
+ %%
+ %% key expanded (16 octets): db fa a6 93 d1 76 2c 5b 66 6a f5 d9 50
+ %% 25 8d 01
+ %%
+ %% iv info (12 octets): 00 0c 08 74 6c 73 31 33 20 69 76 00
+ %%
+ %% iv expanded (12 octets): 5b d3 c7 1b 83 6e 0b 76 bb 73 26 5f
+
+ %% PRK = CHSTrafficsecret
+ %% key info = WriteKeyInfo
+ %% iv info = WrtieIVInfo
+ SRKey =
+ hexstr2bin("db fa a6 93 d1 76 2c 5b 66 6a f5 d9 50 25 8d 01"),
+
+ SRIV =
+ hexstr2bin("5b d3 c7 1b 83 6e 0b 76 bb 73 26 5f"),
+
+ {SRKey, SRIV} = tls_v1:calculate_traffic_keys(HKDFAlgo, Cipher, CHSTrafficSecret).
tls13_finished_verify_data() ->
@@ -5161,6 +5341,28 @@ tls13_finished_verify_data(_Config) ->
FinishedKey = tls_v1:finished_key(BaseKey, sha256),
VerifyData = tls_v1:finished_verify_data(FinishedKey, sha256, Messages).
+tls13_basic_ssl_s_client() ->
+ [{doc,"Test TLS 1.3 basic connection between ssl server and openssl s_client"}].
+
+tls13_basic_ssl_s_client(Config) ->
+ ClientOpts = ssl_test_lib:ssl_options(client_rsa_opts, Config),
+ ServerOpts0 = ssl_test_lib:ssl_options(server_rsa_opts, Config),
+ %% Set versions
+ ServerOpts = [{versions, ['tlsv1.2','tlsv1.3']}|ServerOpts0],
+ {_ClientNode, ServerNode, _Hostname} = ssl_test_lib:run_where(Config),
+
+ Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {mfa, {ssl_test_lib, send_recv_result_active, []}},
+ {options, ServerOpts}]),
+ Port = ssl_test_lib:inet_port(Server),
+
+ Client = ssl_test_lib:start_basic_client(openssl, 'tlsv1.3', Port, ClientOpts),
+
+ ssl_test_lib:check_result(Server, ok),
+ ssl_test_lib:close(Server),
+ ssl_test_lib:close_port(Client).
+
%%--------------------------------------------------------------------
%% Internal functions ------------------------------------------------
diff --git a/lib/ssl/test/ssl_bench_SUITE.erl b/lib/ssl/test/ssl_bench_SUITE.erl
index 0b2011a627..35efa2b8a3 100644
--- a/lib/ssl/test/ssl_bench_SUITE.erl
+++ b/lib/ssl/test/ssl_bench_SUITE.erl
@@ -41,6 +41,7 @@ end_per_group(_GroupName, _Config) ->
ok.
init_per_suite(Config) ->
+ ct:timetrap({minutes, 1}),
case node() of
nonode@nohost ->
{skipped, "Node not distributed"};
@@ -163,7 +164,7 @@ do_test(Type, TC, Loop, ParallellConnections, Server) ->
end
end,
Spawn = fun(Id) ->
- Pid = spawn(fun() -> Test(Id) end),
+ Pid = spawn_link(fun() -> Test(Id) end),
receive {Pid, init} -> Pid end
end,
Pids = [Spawn(Id) || Id <- lists:seq(ParallellConnections, 1, -1)],
@@ -180,42 +181,42 @@ do_test(Type, TC, Loop, ParallellConnections, Server) ->
{ok, TestPerSecond}.
server_init(ssl, setup_connection, _, _, Server) ->
- {ok, Socket} = ssl:listen(0, ssl_opts(listen)),
- {ok, {_Host, Port}} = ssl:sockname(Socket),
+ {ok, LSocket} = ssl:listen(0, ssl_opts(listen)),
+ {ok, {_Host, Port}} = ssl:sockname(LSocket),
{ok, Host} = inet:gethostname(),
?FPROF_SERVER andalso start_profile(fprof, [whereis(ssl_manager), new]),
%%?EPROF_SERVER andalso start_profile(eprof, [ssl_connection_sup, ssl_manager]),
?EPROF_SERVER andalso start_profile(eprof, [ssl_manager]),
Server ! {self(), {init, Host, Port}},
Test = fun(TSocket) ->
- ok = ssl:ssl_accept(TSocket),
- ssl:close(TSocket)
+ {ok, Socket} = ssl:handshake(TSocket),
+ ssl:close(Socket)
end,
- setup_server_connection(Socket, Test);
+ setup_server_connection(LSocket, Test);
server_init(ssl, payload, Loop, _, Server) ->
- {ok, Socket} = ssl:listen(0, ssl_opts(listen)),
- {ok, {_Host, Port}} = ssl:sockname(Socket),
+ {ok, LSocket} = ssl:listen(0, ssl_opts(listen)),
+ {ok, {_Host, Port}} = ssl:sockname(LSocket),
{ok, Host} = inet:gethostname(),
Server ! {self(), {init, Host, Port}},
Test = fun(TSocket) ->
- ok = ssl:ssl_accept(TSocket),
+ {ok, Socket} = ssl:handshake(TSocket),
Size = byte_size(msg()),
- server_echo(TSocket, Size, Loop),
- ssl:close(TSocket)
+ server_echo(Socket, Size, Loop),
+ ssl:close(Socket)
end,
- setup_server_connection(Socket, Test);
+ setup_server_connection(LSocket, Test);
server_init(ssl, pem_cache, Loop, _, Server) ->
- {ok, Socket} = ssl:listen(0, ssl_opts(listen_der)),
- {ok, {_Host, Port}} = ssl:sockname(Socket),
+ {ok, LSocket} = ssl:listen(0, ssl_opts(listen_der)),
+ {ok, {_Host, Port}} = ssl:sockname(LSocket),
{ok, Host} = inet:gethostname(),
Server ! {self(), {init, Host, Port}},
Test = fun(TSocket) ->
- ok = ssl:ssl_accept(TSocket),
+ {ok, Socket} = ssl:handshake(TSocket),
Size = byte_size(msg()),
- server_echo(TSocket, Size, Loop),
- ssl:close(TSocket)
+ server_echo(Socket, Size, Loop),
+ ssl:close(Socket)
end,
- setup_server_connection(Socket, Test);
+ setup_server_connection(LSocket, Test);
server_init(Type, Tc, _, _, Server) ->
io:format("No server init code for ~p ~p~n",[Type, Tc]),
diff --git a/lib/ssl/test/ssl_certificate_verify_SUITE.erl b/lib/ssl/test/ssl_certificate_verify_SUITE.erl
index bddcc2514d..8690faed54 100644
--- a/lib/ssl/test/ssl_certificate_verify_SUITE.erl
+++ b/lib/ssl/test/ssl_certificate_verify_SUITE.erl
@@ -298,15 +298,8 @@ server_require_peer_cert_fail(Config) when is_list(Config) ->
{host, Hostname},
{from, self()},
{options, [{active, Active} | BadClientOpts]}]),
- receive
- {Server, {error, {tls_alert, "handshake failure"}}} ->
- receive
- {Client, {error, {tls_alert, "handshake failure"}}} ->
- ok;
- {Client, {error, closed}} ->
- ok
- end
- end.
+
+ ssl_test_lib:check_server_alert(Server, Client, handshake_failure).
%%--------------------------------------------------------------------
server_require_peer_cert_empty_ok() ->
@@ -365,15 +358,8 @@ server_require_peer_cert_partial_chain(Config) when is_list(Config) ->
{options, [{active, Active},
{cacerts, [RootCA]} |
proplists:delete(cacertfile, ClientOpts)]}]),
- receive
- {Server, {error, {tls_alert, "unknown ca"}}} ->
- receive
- {Client, {error, {tls_alert, "unknown ca"}}} ->
- ok;
- {Client, {error, closed}} ->
- ok
- end
- end.
+ ssl_test_lib:check_server_alert(Server, Client, unknown_ca).
+
%%--------------------------------------------------------------------
server_require_peer_cert_allow_partial_chain() ->
[{doc, "Server trusts intermediat CA and accepts a partial chain. (partial_chain option)"}].
@@ -446,17 +432,7 @@ server_require_peer_cert_do_not_allow_partial_chain(Config) when is_list(Config)
{from, self()},
{mfa, {ssl_test_lib, no_result, []}},
{options, ClientOpts}]),
-
- receive
- {Server, {error, {tls_alert, "unknown ca"}}} ->
- receive
- {Client, {error, {tls_alert, "unknown ca"}}} ->
- ok;
- {Client, {error, closed}} ->
- ok
- end
- end.
-
+ ssl_test_lib:check_server_alert(Server, Client, unknown_ca).
%%--------------------------------------------------------------------
server_require_peer_cert_partial_chain_fun_fail() ->
[{doc, "If parial_chain fun crashes, treat it as if it returned unkown_ca"}].
@@ -487,16 +463,7 @@ server_require_peer_cert_partial_chain_fun_fail(Config) when is_list(Config) ->
{from, self()},
{mfa, {ssl_test_lib, no_result, []}},
{options, ClientOpts}]),
-
- receive
- {Server, {error, {tls_alert, "unknown ca"}}} ->
- receive
- {Client, {error, {tls_alert, "unknown ca"}}} ->
- ok;
- {Client, {error, closed}} ->
- ok
- end
- end.
+ ssl_test_lib:check_server_alert(Server, Client, unknown_ca).
%%--------------------------------------------------------------------
verify_fun_always_run_client() ->
@@ -535,14 +502,8 @@ verify_fun_always_run_client(Config) when is_list(Config) ->
[{verify, verify_peer},
{verify_fun, FunAndState}
| ClientOpts]}]),
- %% Server error may be {tls_alert,"handshake failure"} or closed depending on timing
- %% this is not a bug it is a circumstance of how tcp works!
- receive
- {Server, ServerError} ->
- ct:log("Server Error ~p~n", [ServerError])
- end,
- ssl_test_lib:check_result(Client, {error, {tls_alert, "handshake failure"}}).
+ ssl_test_lib:check_client_alert(Server, Client, handshake_failure).
%%--------------------------------------------------------------------
verify_fun_always_run_server() ->
@@ -581,16 +542,8 @@ verify_fun_always_run_server(Config) when is_list(Config) ->
{mfa, {ssl_test_lib,
no_result, []}},
{options, ClientOpts}]),
-
- %% Client error may be {tls_alert, "handshake failure" } or closed depending on timing
- %% this is not a bug it is a circumstance of how tcp works!
- receive
- {Client, ClientError} ->
- ct:log("Client Error ~p~n", [ClientError])
- end,
-
- ssl_test_lib:check_result(Server, {error, {tls_alert, "handshake failure"}}).
-
+
+ ssl_test_lib:check_client_alert(Server, Client, handshake_failure).
%%--------------------------------------------------------------------
cert_expired() ->
@@ -620,8 +573,7 @@ cert_expired(Config) when is_list(Config) ->
{from, self()},
{options, [{verify, verify_peer}, {active, Active} | ClientOpts]}]),
- ssl_test_lib:check_result(Server, {error, {tls_alert, "certificate expired"}},
- Client, {error, {tls_alert, "certificate expired"}}).
+ ssl_test_lib:check_client_alert(Server, Client, certificate_expired).
two_digits_str(N) when N < 10 ->
lists:flatten(io_lib:format("0~p", [N]));
@@ -727,12 +679,8 @@ critical_extension_verify_server(Config) when is_list(Config) ->
{options, [{verify, verify_none}, {active, Active} | ClientOpts]}]),
%% This certificate has a critical extension that we don't
- %% understand. Therefore, verification should fail.
-
- ssl_test_lib:check_result(Server, {error, {tls_alert, "unsupported certificate"}},
- Client, {error, {tls_alert, "unsupported certificate"}}),
-
- ssl_test_lib:close(Server).
+ %% understand. Therefore, verification should fail.
+ ssl_test_lib:check_server_alert(Server, Client, unsupported_certificate).
%%--------------------------------------------------------------------
critical_extension_verify_client() ->
@@ -763,12 +711,7 @@ critical_extension_verify_client(Config) when is_list(Config) ->
{mfa, {ssl_test_lib, ReceiveFunction, []}},
{options, [{verify, verify_peer}, {active, Active} | ClientOpts]}]),
- %% This certificate has a critical extension that we don't
- %% understand. Therefore, verification should fail.
- ssl_test_lib:check_result(Server, {error, {tls_alert, "unsupported certificate"}},
- Client, {error, {tls_alert, "unsupported certificate"}}),
-
- ssl_test_lib:close(Server).
+ ssl_test_lib:check_client_alert(Server, Client, unsupported_certificate).
%%--------------------------------------------------------------------
critical_extension_verify_none() ->
@@ -908,10 +851,7 @@ invalid_signature_server(Config) when is_list(Config) ->
{host, Hostname},
{from, self()},
{options, [{verify, verify_peer} | ClientOpts]}]),
-
- ssl_test_lib:check_result(Server, {error, {tls_alert, "unknown ca"}},
- Client, {error, {tls_alert, "unknown ca"}}).
-
+ ssl_test_lib:check_server_alert(Server, Client, unknown_ca).
%%--------------------------------------------------------------------
invalid_signature_client() ->
@@ -946,9 +886,7 @@ invalid_signature_client(Config) when is_list(Config) ->
{from, self()},
{options, NewClientOpts}]),
- ssl_test_lib:check_result(Server, {error, {tls_alert, "unknown ca"}},
- Client, {error, {tls_alert, "unknown ca"}}).
-
+ ssl_test_lib:check_client_alert(Server, Client, unknown_ca).
%%--------------------------------------------------------------------
@@ -1034,16 +972,7 @@ unknown_server_ca_fail(Config) when is_list(Config) ->
[{verify, verify_peer},
{verify_fun, FunAndState}
| ClientOpts]}]),
- receive
- {Client, {error, {tls_alert, "unknown ca"}}} ->
- receive
- {Server, {error, {tls_alert, "unknown ca"}}} ->
- ok;
- {Server, {error, closed}} ->
- ok
- end
- end.
-
+ ssl_test_lib:check_client_alert(Server, Client, unknown_ca).
%%--------------------------------------------------------------------
unknown_server_ca_accept_verify_none() ->
@@ -1193,11 +1122,7 @@ customize_hostname_check(Config) when is_list(Config) ->
{mfa, {ssl_test_lib, no_result, []}},
{options, ClientOpts}
]),
- ssl_test_lib:check_result(Client1, {error, {tls_alert, "handshake failure"}},
- Server, {error, {tls_alert, "handshake failure"}}),
-
- ssl_test_lib:close(Server),
- ssl_test_lib:close(Client).
+ ssl_test_lib:check_client_alert(Server, Client1, handshake_failure).
incomplete_chain() ->
[{doc,"Test option verify_peer"}].
diff --git a/lib/ssl/test/ssl_crl_SUITE.erl b/lib/ssl/test/ssl_crl_SUITE.erl
index c61039b5da..b2fd3874a8 100644
--- a/lib/ssl/test/ssl_crl_SUITE.erl
+++ b/lib/ssl/test/ssl_crl_SUITE.erl
@@ -238,7 +238,7 @@ crl_verify_revoked(Config) when is_list(Config) ->
end,
crl_verify_error(Hostname, ServerNode, ServerOpts, ClientNode, ClientOpts,
- "certificate revoked").
+ certificate_revoked).
crl_verify_no_crl() ->
[{doc,"Verify a simple CRL chain when the CRL is missing"}].
@@ -277,10 +277,10 @@ crl_verify_no_crl(Config) when is_list(Config) ->
%% The error "revocation status undetermined" gets turned
%% into "bad certificate".
crl_verify_error(Hostname, ServerNode, ServerOpts, ClientNode, ClientOpts,
- "bad certificate");
+ bad_certificate);
peer ->
crl_verify_error(Hostname, ServerNode, ServerOpts, ClientNode, ClientOpts,
- "bad certificate");
+ bad_certificate);
best_effort ->
%% In "best effort" mode, we consider the certificate not
%% to be revoked if we can't find the appropriate CRL.
@@ -341,7 +341,7 @@ crl_hash_dir_collision(Config) when is_list(Config) ->
%% First certificate revoked; first fails, second succeeds.
crl_verify_error(Hostname, ServerNode, ServerOpts1, ClientNode, ClientOpts,
- "certificate revoked"),
+ certificate_revoked),
crl_verify_valid(Hostname, ServerNode, ServerOpts2, ClientNode, ClientOpts),
make_certs:revoke(PrivDir, CA2, "collision-client-2", CertsConfig),
@@ -352,9 +352,9 @@ crl_hash_dir_collision(Config) when is_list(Config) ->
%% Second certificate revoked; both fail.
crl_verify_error(Hostname, ServerNode, ServerOpts1, ClientNode, ClientOpts,
- "certificate revoked"),
+ certificate_revoked),
crl_verify_error(Hostname, ServerNode, ServerOpts2, ClientNode, ClientOpts,
- "certificate revoked"),
+ certificate_revoked),
ok.
@@ -400,10 +400,10 @@ crl_hash_dir_expired(Config) when is_list(Config) ->
%% The error "revocation status undetermined" gets turned
%% into "bad certificate".
crl_verify_error(Hostname, ServerNode, ServerOpts, ClientNode, ClientOpts,
- "bad certificate");
+ bad_certificate);
peer ->
crl_verify_error(Hostname, ServerNode, ServerOpts, ClientNode, ClientOpts,
- "bad certificate");
+ bad_certificate);
best_effort ->
%% In "best effort" mode, we consider the certificate not
%% to be revoked if we can't find the appropriate CRL.
@@ -451,11 +451,8 @@ crl_verify_error(Hostname, ServerNode, ServerOpts, ClientNode, ClientOpts, Expec
{host, Hostname},
{from, self()},
{options, ClientOpts}]),
- receive
- {Server, AlertOrClose} ->
- ct:pal("Server Alert or Close ~p", [AlertOrClose])
- end,
- ssl_test_lib:check_result(Client, {error, {tls_alert, ExpectedAlert}}).
+
+ ssl_test_lib:check_client_alert(Server, Client, ExpectedAlert).
%%--------------------------------------------------------------------
%% Internal functions ------------------------------------------------
diff --git a/lib/ssl/test/ssl_dist_bench_SUITE.erl b/lib/ssl/test/ssl_dist_bench_SUITE.erl
index d9d9c4e473..7e7de5c9bf 100644
--- a/lib/ssl/test/ssl_dist_bench_SUITE.erl
+++ b/lib/ssl/test/ssl_dist_bench_SUITE.erl
@@ -1,7 +1,7 @@
%%%-------------------------------------------------------------------
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2017-2018. All Rights Reserved.
+%% Copyright Ericsson AB 2017-2019. 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,7 @@
throughput_1048576/1]).
%% Debug
--export([payload/1]).
+-export([payload/1, roundtrip_runner/3, setup_runner/3, throughput_runner/4]).
%%%-------------------------------------------------------------------
@@ -504,17 +504,19 @@ throughput(A, B, Prefix, HA, HB, Packets, Size) ->
[] = ssl_apply(HA, erlang, nodes, []),
[] = ssl_apply(HB, erlang, nodes, []),
#{time := Time,
- dist_stats := DistStats,
+ client_dist_stats := ClientDistStats,
client_msacc_stats := ClientMsaccStats,
client_prof := ClientProf,
server_msacc_stats := ServerMsaccStats,
- server_prof := ServerProf} =
+ server_prof := ServerProf,
+ server_gc_before := Server_GC_Before,
+ server_gc_after := Server_GC_After} =
ssl_apply(HA, fun () -> throughput_runner(A, B, Packets, Size) end),
[B] = ssl_apply(HA, erlang, nodes, []),
[A] = ssl_apply(HB, erlang, nodes, []),
ClientMsaccStats =:= undefined orelse
msacc:print(ClientMsaccStats),
- io:format("DistStats: ~p~n", [DistStats]),
+ io:format("ClientDistStats: ~p~n", [ClientDistStats]),
Overhead =
50 % Distribution protocol headers (empirical) (TLS+=54)
+ byte_size(erlang:term_to_binary([0|<<>>])), % Benchmark overhead
@@ -533,6 +535,8 @@ throughput(A, B, Prefix, HA, HB, Packets, Size) ->
end,
io:format("******* ClientProf:~n", []), prof_print(ClientProf),
io:format("******* ServerProf:~n", []), prof_print(ServerProf),
+ io:format("******* Server GC Before:~n~p~n", [Server_GC_Before]),
+ io:format("******* Server GC After:~n~p~n", [Server_GC_After]),
Speed = round((Bytes * 1000000) / (1024 * Time)),
report(Prefix++" Throughput_"++integer_to_list(Size), Speed, "kB/s").
@@ -554,10 +558,10 @@ throughput_runner(A, B, Rounds, Size) ->
ok
end,
prof_start(),
- {Time,ServerMsaccStats,ServerProf} =
+ #{time := Time} = Result =
throughput_client(ServerPid, ServerMon, Payload, Rounds),
prof_stop(),
- ClientMsaccStats =
+ MsaccStats =
case msacc:available() of
true ->
MStats = msacc:stats(),
@@ -566,15 +570,13 @@ throughput_runner(A, B, Rounds, Size) ->
false ->
undefined
end,
- ClientProf = prof_end(),
+ Prof = prof_end(),
[{_Node,Socket}] = dig_dist_node_sockets(),
DistStats = inet:getstat(Socket),
- #{time => microseconds(Time),
- dist_stats => DistStats,
- client_msacc_stats => ClientMsaccStats,
- client_prof => ClientProf,
- server_msacc_stats => ServerMsaccStats,
- server_prof => ServerProf}.
+ Result#{time := microseconds(Time),
+ client_dist_stats => DistStats,
+ client_msacc_stats => MsaccStats,
+ client_prof => Prof}.
dig_dist_node_sockets() ->
[case DistCtrl of
@@ -597,6 +599,9 @@ dig_dist_node_sockets() ->
throughput_server(Pid, N) ->
+ GC_Before = get_server_gc_info(),
+ %% dbg:tracer(port, dbg:trace_port(file, "throughput_server_gc.log")),
+ %% dbg:p(TLSDistReceiver, garbage_collection),
msacc:available() andalso
begin
msacc:stop(),
@@ -605,9 +610,9 @@ throughput_server(Pid, N) ->
ok
end,
prof_start(),
- throughput_server_loop(Pid, N).
+ throughput_server_loop(Pid, GC_Before, N).
-throughput_server_loop(_Pid, 0) ->
+throughput_server_loop(_Pid, GC_Before, 0) ->
prof_stop(),
MsaccStats =
case msacc:available() of
@@ -620,11 +625,26 @@ throughput_server_loop(_Pid, 0) ->
undefined
end,
Prof = prof_end(),
- exit({ok,MsaccStats,Prof});
-throughput_server_loop(Pid, N) ->
+ %% dbg:flush_trace_port(),
+ exit(#{server_msacc_stats => MsaccStats,
+ server_prof => Prof,
+ server_gc_before => GC_Before,
+ server_gc_after => get_server_gc_info()});
+throughput_server_loop(Pid, GC_Before, N) ->
receive
{Pid, N, _} ->
- throughput_server_loop(Pid, N-1)
+ throughput_server_loop(Pid, GC_Before, N-1)
+ end.
+
+get_server_gc_info() ->
+ case whereis(ssl_connection_sup_dist) of
+ undefined ->
+ undefined;
+ SupPid ->
+ [{_Id,TLSDistReceiver,_Type,_Modules}|_] =
+ supervisor:which_children(SupPid),
+ erlang:process_info(
+ TLSDistReceiver, [garbage_collection,garbage_collection_info])
end.
throughput_client(Pid, Mon, Payload, N) ->
@@ -632,8 +652,8 @@ throughput_client(Pid, Mon, Payload, N) ->
throughput_client_loop(_Pid, Mon, _Payload, 0, StartTime) ->
receive
- {'DOWN', Mon, _, _, {ok,MsaccStats,Prof}} ->
- {elapsed_time(StartTime),MsaccStats,Prof};
+ {'DOWN', Mon, _, _, #{} = Result} ->
+ Result#{time => elapsed_time(StartTime)};
{'DOWN', Mon, _, _, Other} ->
exit(Other)
end;
@@ -651,6 +671,7 @@ prof_start() ->
ok.
-elif(?prof =:= eprof).
prof_start() ->
+ catch eprof:stop(),
{ok,_} = eprof:start(),
profiling = eprof:start_profiling(processes()),
ok.
diff --git a/lib/ssl/test/ssl_sni_SUITE.erl b/lib/ssl/test/ssl_sni_SUITE.erl
index 251b6a2639..7629d75100 100644
--- a/lib/ssl/test/ssl_sni_SUITE.erl
+++ b/lib/ssl/test/ssl_sni_SUITE.erl
@@ -236,8 +236,8 @@ dns_name_reuse(Config) ->
{mfa, {ssl_test_lib, session_info_result, []}},
{from, self()}, {options, [{verify, verify_peer} | ClientConf]}]),
- ssl_test_lib:check_result(Client1, {error, {tls_alert, "handshake failure"}}),
- ssl_test_lib:close(Client0).
+ ssl_test_lib:check_client_alert(Client1, handshake_failure).
+
%%--------------------------------------------------------------------
%% Internal Functions ------------------------------------------------
%%--------------------------------------------------------------------
@@ -370,8 +370,8 @@ unsuccessfull_connect(ServerOptions, ClientOptions, Hostname0, Config) ->
{from, self()},
{options, ClientOptions}]),
- ssl_test_lib:check_result(Server, {error, {tls_alert, "handshake failure"}},
- Client, {error, {tls_alert, "handshake failure"}}).
+ ssl_test_lib:check_server_alert(Server, Client, handshake_failure).
+
host_name(undefined, Hostname) ->
Hostname;
host_name(Hostname, _) ->
diff --git a/lib/ssl/test/ssl_test_lib.erl b/lib/ssl/test/ssl_test_lib.erl
index fdbd39d016..c921dcae4c 100644
--- a/lib/ssl/test/ssl_test_lib.erl
+++ b/lib/ssl/test/ssl_test_lib.erl
@@ -438,6 +438,37 @@ check_result(Pid, Msg) ->
{got, Unexpected}},
ct:fail(Reason)
end.
+check_server_alert(Pid, Alert) ->
+ receive
+ {Pid, {error, {tls_alert, {Alert, _}}}} ->
+ ok
+ end.
+check_server_alert(Server, Client, Alert) ->
+ receive
+ {Server, {error, {tls_alert, {Alert, _}}}} ->
+ receive
+ {Client, {error, {tls_alert, {Alert, _}}}} ->
+ ok;
+ {Client, {error, closed}} ->
+ ok
+ end
+ end.
+check_client_alert(Pid, Alert) ->
+ receive
+ {Pid, {error, {tls_alert, {Alert, _}}}} ->
+ ok
+ end.
+check_client_alert(Server, Client, Alert) ->
+ receive
+ {Client, {error, {tls_alert, {Alert, _}}}} ->
+ receive
+ {Server, {error, {tls_alert, {Alert, _}}}} ->
+ ok;
+ {Server, {error, closed}} ->
+ ok
+ end
+ end.
+
wait_for_result(Server, ServerMsg, Client, ClientMsg) ->
receive
@@ -833,7 +864,8 @@ make_rsa_cert(Config) ->
Config
end.
appropriate_sha(CryptoSupport) ->
- case proplists:get_bool(sha256, CryptoSupport) of
+ Hashes = proplists:get_value(hashs, CryptoSupport),
+ case lists:member(sha256, Hashes) of
true ->
sha256;
false ->
@@ -1072,20 +1104,33 @@ ecc_test(Expect, COpts, SOpts, CECCOpts, SECCOpts, Config) ->
ecc_test_error(COpts, SOpts, CECCOpts, SECCOpts, Config) ->
{Server, Port} = start_server_ecc_error(erlang, SOpts, SECCOpts, Config),
Client = start_client_ecc_error(erlang, Port, COpts, CECCOpts, Config),
- Error = {error, {tls_alert, "insufficient security"}},
- check_result(Server, Error, Client, Error).
+ check_server_alert(Server, Client, insufficient_security).
-start_client(openssl, Port, ClientOpts, Config) ->
+start_basic_client(openssl, Version, Port, ClientOpts) ->
Cert = proplists:get_value(certfile, ClientOpts),
Key = proplists:get_value(keyfile, ClientOpts),
CA = proplists:get_value(cacertfile, ClientOpts),
- Version = ssl_test_lib:protocol_version(Config),
Exe = "openssl",
Args = ["s_client", "-verify", "2", "-port", integer_to_list(Port),
ssl_test_lib:version_flag(Version),
"-cert", Cert, "-CAfile", CA,
"-key", Key, "-host","localhost", "-msg", "-debug"],
+ OpenSslPort = ssl_test_lib:portable_open_port(Exe, Args),
+ true = port_command(OpenSslPort, "Hello world"),
+ OpenSslPort.
+
+start_client(openssl, Port, ClientOpts, Config) ->
+ Cert = proplists:get_value(certfile, ClientOpts),
+ Key = proplists:get_value(keyfile, ClientOpts),
+ CA = proplists:get_value(cacertfile, ClientOpts),
+ Version = ssl_test_lib:protocol_version(Config),
+ Exe = "openssl",
+ Args0 = ["s_client", "-verify", "2", "-port", integer_to_list(Port),
+ ssl_test_lib:version_flag(Version),
+ "-cert", Cert, "-CAfile", CA,
+ "-key", Key, "-host","localhost", "-msg", "-debug"],
+ Args = maybe_force_ipv4(Args0),
OpenSslPort = ssl_test_lib:portable_open_port(Exe, Args),
true = port_command(OpenSslPort, "Hello world"),
OpenSslPort;
@@ -1099,6 +1144,18 @@ start_client(erlang, Port, ClientOpts, Config) ->
{mfa, {ssl_test_lib, check_key_exchange_send_active, [KeyEx]}},
{options, [{verify, verify_peer} | ClientOpts]}]).
+%% Workaround for running tests on machines where openssl
+%% s_client would use an IPv6 address with localhost. As
+%% this test suite and the ssl application is not prepared
+%% for that we have to force s_client to use IPv4 if
+%% OpenSSL supports IPv6.
+maybe_force_ipv4(Args0) ->
+ case is_ipv6_supported() of
+ true ->
+ Args0 ++ ["-4"];
+ false ->
+ Args0
+ end.
start_client_ecc(erlang, Port, ClientOpts, Expect, ECCOpts, Config) ->
{ClientNode, _, Hostname} = ssl_test_lib:run_where(Config),
@@ -1662,6 +1719,17 @@ active_once_disregard(Socket, N) ->
ssl:setopts(Socket, [{active, once}]),
active_once_disregard(Socket, N-byte_size(Bytes))
end.
+
+is_ipv6_supported() ->
+ case os:cmd("openssl version") of
+ "OpenSSL 0.9.8" ++ _ -> % Does not support IPv6
+ false;
+ "OpenSSL 1.0" ++ _ -> % Does not support IPv6
+ false;
+ _ ->
+ true
+ end.
+
is_sane_ecc(openssl) ->
case os:cmd("openssl version") of
"OpenSSL 1.0.0a" ++ _ -> % Known bug in openssl
@@ -1857,6 +1925,8 @@ version_flag('tlsv1.1') ->
"-tls1_1";
version_flag('tlsv1.2') ->
"-tls1_2";
+version_flag('tlsv1.3') ->
+ "-tls1_3";
version_flag(sslv3) ->
"-ssl3";
version_flag(sslv2) ->
diff --git a/lib/ssl/test/ssl_to_openssl_SUITE.erl b/lib/ssl/test/ssl_to_openssl_SUITE.erl
index d180021439..df84411b6d 100644
--- a/lib/ssl/test/ssl_to_openssl_SUITE.erl
+++ b/lib/ssl/test/ssl_to_openssl_SUITE.erl
@@ -1249,7 +1249,7 @@ ssl2_erlang_server_openssl_client(Config) when is_list(Config) ->
ct:log("Ports ~p~n", [[erlang:port_info(P) || P <- erlang:ports()]]),
ssl_test_lib:consume_port_exit(OpenSslPort),
- ssl_test_lib:check_result(Server, {error, {tls_alert, "bad record mac"}}),
+ ssl_test_lib:check_server_alert(Server, bad_record_mac),
process_flag(trap_exit, false).
%%--------------------------------------------------------------------
@@ -1946,6 +1946,11 @@ erlang_ssl_receive(Socket, Data) ->
ct:log("Connection info: ~p~n",
[ssl:connection_information(Socket)]),
receive
+ {ssl, Socket, "R\n"} ->
+ %% Swallow s_client renegotiation command.
+ %% openssl s_client connected commands can appear on
+ %% server side with some openssl versions.
+ erlang_ssl_receive(Socket,Data);
{ssl, Socket, Data} ->
io:format("Received ~p~n",[Data]),
%% open_ssl server sometimes hangs waiting in blocking read
diff --git a/lib/stdlib/doc/src/ets.xml b/lib/stdlib/doc/src/ets.xml
index 7594514b29..d2ac6a75b1 100644
--- a/lib/stdlib/doc/src/ets.xml
+++ b/lib/stdlib/doc/src/ets.xml
@@ -213,6 +213,21 @@ ets:insert(t, {3}),
is not enabled. This can also only be a potential concern for
<c>ordered_set</c> where the traversal order is defined.</p>
</note>
+ <p>Traversals using <c>match</c> and <c>select</c> functions may not need to
+ scan the entire table depending on how the key is specified. A match
+ pattern with a <em>fully bound key</em> (without any match variables) will
+ optimize the operation to a single key lookup without any table traversal
+ at all. For <c>ordered_set</c> a <em>partially bound key</em> will limit the
+ traversal to only scan a subset of the table based on term order. A
+ partially bound key is either a list or a tuple with a prefix that is fully
+ bound. Example:</p>
+<pre>
+1> <input>T = ets:new(t,[ordered_set]), ets:insert(T, {"555-1234", "John Smith"}).</input>
+true
+2> <input>%% Efficient search of all with area code 555</input>
+2> <input>ets:match(T,{[$5,$5,$5,$- |'$1'],'$2'}).</input>
+[["1234","John Smith"]]
+</pre>
</section>
<section>
@@ -2023,9 +2038,8 @@ true</pre>
<p>This function provides an efficient way to update one or more
counters, without the trouble of having to look up an object, update
the object by incrementing an element, and insert the resulting
- object into the table again. (The update is done atomically,
- that is, no process
- can access the ETS table in the middle of the operation.)</p>
+ object into the table again. The operation is guaranteed to be
+ <seealso marker="#concurrency">atomic and isolated</seealso>.</p>
<p>This function destructively update the object with key
<c><anno>Key</anno></c> in table <c><anno>Tab</anno></c> by adding
<c><anno>Incr</anno></c> to the element at position
diff --git a/lib/stdlib/doc/src/notes.xml b/lib/stdlib/doc/src/notes.xml
index a682811c6d..dee7136eb1 100644
--- a/lib/stdlib/doc/src/notes.xml
+++ b/lib/stdlib/doc/src/notes.xml
@@ -31,6 +31,21 @@
</header>
<p>This document describes the changes made to the STDLIB application.</p>
+<section><title>STDLIB 3.7.1</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p> Optimize pretty printing of terms. The slower
+ behaviour was introduced in Erlang/OTP 20. </p>
+ <p>
+ Own Id: OTP-15573 Aux Id: ERIERL-306 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>STDLIB 3.7</title>
<section><title>Fixed Bugs and Malfunctions</title>
diff --git a/lib/stdlib/doc/src/proplists.xml b/lib/stdlib/doc/src/proplists.xml
index 9d7eb55a7e..4465103469 100644
--- a/lib/stdlib/doc/src/proplists.xml
+++ b/lib/stdlib/doc/src/proplists.xml
@@ -57,6 +57,11 @@
<datatype>
<name name="property"/>
</datatype>
+
+ <datatype>
+ <name name="proplist"/>
+ </datatype>
+
</datatypes>
<funcs>
diff --git a/lib/stdlib/src/calendar.erl b/lib/stdlib/src/calendar.erl
index bb5d450cd6..3a083d9fda 100644
--- a/lib/stdlib/src/calendar.erl
+++ b/lib/stdlib/src/calendar.erl
@@ -529,24 +529,41 @@ valid_date({Y, M, D}) ->
%% day_to_year(DayOfEpoch) = {Year, DayOfYear}
%%
-%% The idea here is to first guess a year, and then adjust. Although
-%% the implementation is recursive, at most 1 or 2 recursive steps
+%% The idea here is to first set the upper and lower bounds for a year,
+%% and then adjust a range by interpolation search. Although complexity
+%% of the algorithm is log(log(n)), at most 1 or 2 recursive steps
%% are taken.
-%% If DayOfEpoch is very large, we need far more than 1 or 2 iterations,
-%% since we just subtract a yearful of days at a time until we're there.
%%
-spec day_to_year(non_neg_integer()) -> {year(), day_of_year()}.
day_to_year(DayOfEpoch) when DayOfEpoch >= 0 ->
- Y0 = DayOfEpoch div ?DAYS_PER_YEAR,
- {Y1, D1} = dty(Y0, DayOfEpoch, dy(Y0)),
+ YMax = DayOfEpoch div ?DAYS_PER_YEAR,
+ YMin = DayOfEpoch div ?DAYS_PER_LEAP_YEAR,
+ {Y1, D1} = dty(YMin, YMax, DayOfEpoch, dy(YMin), dy(YMax)),
{Y1, DayOfEpoch - D1}.
--spec dty(year(), non_neg_integer(), non_neg_integer()) ->
+-spec dty(year(), year(), non_neg_integer(), non_neg_integer(),
+ non_neg_integer()) ->
{year(), non_neg_integer()}.
-dty(Y, D1, D2) when D1 < D2 ->
- dty(Y-1, D1, dy(Y-1));
-dty(Y, _D1, D2) ->
- {Y, D2}.
+dty(Min, Max, _D1, DMin, _DMax) when Min == Max ->
+ {Min, DMin};
+dty(Min, Max, D1, DMin, DMax) ->
+ Diff = Max - Min,
+ Mid = Min + (Diff * (D1 - DMin)) div (DMax - DMin),
+ MidLength =
+ case is_leap_year(Mid) of
+ true -> ?DAYS_PER_LEAP_YEAR;
+ false -> ?DAYS_PER_YEAR
+ end,
+ case dy(Mid) of
+ D2 when D1 < D2 ->
+ NewMax = Mid - 1,
+ dty(Min, NewMax, D1, DMin, dy(NewMax));
+ D2 when D1 - D2 >= MidLength ->
+ NewMin = Mid + 1,
+ dty(NewMin, Max, D1, dy(NewMin), DMax);
+ D2 ->
+ {Mid, D2}
+ end.
%%
%% The Gregorian days of the iso week 01 day 1 for a given year.
diff --git a/lib/stdlib/src/erl_pp.erl b/lib/stdlib/src/erl_pp.erl
index dd302a2880..ada3ff5de3 100644
--- a/lib/stdlib/src/erl_pp.erl
+++ b/lib/stdlib/src/erl_pp.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2018. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2019. 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.
@@ -697,6 +697,8 @@ fun_info(Extra) ->
%% BITS:
+bit_grp([], _Opts) ->
+ leaf("<<>>");
bit_grp(Fs, Opts) ->
append([['<<'], [bit_elems(Fs, Opts)], ['>>']]).
diff --git a/lib/stdlib/src/io_lib.erl b/lib/stdlib/src/io_lib.erl
index 8223a52873..2b5a374cf2 100644
--- a/lib/stdlib/src/io_lib.erl
+++ b/lib/stdlib/src/io_lib.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2018. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2019. 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,6 +87,8 @@
-export([limit_term/2]).
+-export([chars_length/1]).
+
-export_type([chars/0, latin1_string/0, continuation/0,
fread_error/0, fread_item/0, format_spec/0, chars_limit/0]).
@@ -1131,3 +1133,17 @@ test_limit_map_assoc(K, V, D) ->
test_limit(V, D - 1).
test_limit_bitstring(_, _) -> ok.
+
+-spec chars_length(chars()) -> non_neg_integer().
+%% Optimized for deep lists S such that deep_latin1_char_list(S) is
+%% true. No binaries allowed! It is assumed that $\r is never followed
+%% by $\n if S is an iolist() (string:length() assigns such a
+%% sub-sequence length 1).
+chars_length(S) ->
+ try
+ %% true = deep_latin1_char_list(S),
+ iolist_size(S)
+ catch
+ _:_ ->
+ string:length(S)
+ end.
diff --git a/lib/stdlib/src/io_lib_format.erl b/lib/stdlib/src/io_lib_format.erl
index ab9031573b..d1aa4cd157 100644
--- a/lib/stdlib/src/io_lib_format.erl
+++ b/lib/stdlib/src/io_lib_format.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2018. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2019. 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.
@@ -248,7 +248,7 @@ count_small([#{control_char := $s}|Cs], #{w := W} = Cnts) ->
count_small(Cs, Cnts#{w := W + 1});
count_small([S|Cs], #{other := Other} = Cnts) when is_list(S);
is_binary(S) ->
- count_small(Cs, Cnts#{other := Other + string:length(S)});
+ count_small(Cs, Cnts#{other := Other + io_lib:chars_length(S)});
count_small([C|Cs], #{other := Other} = Cnts) when is_integer(C) ->
count_small(Cs, Cnts#{other := Other + 1});
count_small([], #{p := P, s := S, w := W, other := Other}) ->
@@ -280,10 +280,15 @@ build_limited([#{control_char := C, args := As, width := F, adjust := Ad,
true -> MaxLen0 div Count0
end,
S = control_limited(C, As, F, Ad, P, Pad, Enc, Str, MaxChars, I),
- Len = string:length(S),
NumOfPs = decr_pc(C, NumOfPs0),
Count = Count0 - 1,
- MaxLen = sub(MaxLen0, Len),
+ MaxLen = if
+ MaxLen0 < 0 -> % optimization
+ MaxLen0;
+ true ->
+ Len = io_lib:chars_length(S),
+ sub(MaxLen0, Len)
+ end,
if
NumOfPs > 0 -> [S|build_limited(Cs, NumOfPs, Count,
MaxLen, indentation(S, I))];
@@ -406,7 +411,7 @@ base(B) when is_integer(B) ->
term(T, none, _Adj, none, _Pad) -> T;
term(T, none, Adj, P, Pad) -> term(T, P, Adj, P, Pad);
term(T, F, Adj, P0, Pad) ->
- L = string:length(T),
+ L = io_lib:chars_length(T),
P = erlang:min(L, case P0 of none -> F; _ -> min(P0, F) end),
if
L > P ->
@@ -713,7 +718,7 @@ fwrite_g(Fl, F, Adj, P, Pad) when P >= 1 ->
end.
-%% iolist_to_chars(iolist()) -> deep_char_list()
+%% iolist_to_chars(iolist()) -> io_lib:chars()
iolist_to_chars([C|Cs]) when is_integer(C), C >= $\000, C =< $\377 ->
[C | iolist_to_chars(Cs)];
@@ -729,7 +734,7 @@ iolist_to_chars(B) when is_binary(B) ->
%% cbinary() | nil())
%% cbinary() :: unicode:unicode_binary() | unicode:latin1_binary()
-%% cdata_to_chars(cdata()) -> io_lib:deep_char_list()
+%% cdata_to_chars(cdata()) -> io_lib:chars()
cdata_to_chars([C|Cs]) when is_integer(C), C >= $\000 ->
[C | cdata_to_chars(Cs)];
@@ -745,7 +750,7 @@ cdata_to_chars(B) when is_binary(B) ->
limit_string(S, F, CharsLimit) when CharsLimit < 0; CharsLimit >= F -> S;
limit_string(S, _F, CharsLimit) ->
- case string:length(S) =< CharsLimit of
+ case io_lib:chars_length(S) =< CharsLimit of
true -> S;
false -> [string:slice(S, 0, sub(CharsLimit, 3)), "..."]
end.
@@ -759,11 +764,11 @@ limit_field(F, CharsLimit) ->
string(S, none, _Adj, none, _Pad) -> S;
string(S, F, Adj, none, Pad) ->
- string_field(S, F, Adj, string:length(S), Pad);
+ string_field(S, F, Adj, io_lib:chars_length(S), Pad);
string(S, none, _Adj, P, Pad) ->
- string_field(S, P, left, string:length(S), Pad);
+ string_field(S, P, left, io_lib:chars_length(S), Pad);
string(S, F, Adj, P, Pad) when F >= P ->
- N = string:length(S),
+ N = io_lib:chars_length(S),
if F > P ->
if N > P ->
adjust(flat_trunc(S, P), chars(Pad, F-P), Adj);
diff --git a/lib/stdlib/src/io_lib_pretty.erl b/lib/stdlib/src/io_lib_pretty.erl
index ba9d9e8434..5483ea87b5 100644
--- a/lib/stdlib/src/io_lib_pretty.erl
+++ b/lib/stdlib/src/io_lib_pretty.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2018. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2019. 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.
@@ -507,20 +507,20 @@ print_length(#{}=M, _D, _T, _RF, _Enc, _Str) when map_size(M) =:= 0 ->
{"#{}", 3, 0, no_more};
print_length(Atom, _D, _T, _RF, Enc, _Str) when is_atom(Atom) ->
S = write_atom(Atom, Enc),
- {S, string:length(S), 0, no_more};
+ {S, io_lib:chars_length(S), 0, no_more};
print_length(List, D, T, RF, Enc, Str) when is_list(List) ->
%% only flat lists are "printable"
case Str andalso printable_list(List, D, T, Enc) of
true ->
%% print as string, escaping double-quotes in the list
S = write_string(List, Enc),
- {S, string:length(S), 0, no_more};
+ {S, io_lib:chars_length(S), 0, no_more};
{true, Prefix} ->
%% Truncated lists when T < 0 could break some existing code.
S = write_string(Prefix, Enc),
%% NumOfDots = 0 to avoid looping--increasing the depth
%% does not make Prefix longer.
- {[S | "..."], 3 + string:length(S), 0, no_more};
+ {[S | "..."], 3 + io_lib:chars_length(S), 0, no_more};
false ->
case print_length_list(List, D, T, RF, Enc, Str) of
{What, Len, Dots, _More} when Dots > 0 ->
@@ -564,7 +564,7 @@ print_length(<<_/bitstring>> = Bin, D, T, RF, Enc, Str) ->
{[$<,$<,S,$>,$>], 4 + length(S), 0, no_more};
{false, List} when is_list(List) ->
S = io_lib:write_string(List, $"), %"
- {[$<,$<,S,"/utf8>>"], 9 + string:length(S), 0, no_more};
+ {[$<,$<,S,"/utf8>>"], 9 + io_lib:chars_length(S), 0, no_more};
{true, true, Prefix} ->
S = io_lib:write_string(Prefix, $"), %"
More = fun(T1, Dd) ->
@@ -576,7 +576,7 @@ print_length(<<_/bitstring>> = Bin, D, T, RF, Enc, Str) ->
More = fun(T1, Dd) ->
?FUNCTION_NAME(Bin, D+Dd, T1, RF, Enc, Str)
end,
- {[$<,$<,S|"/utf8...>>"], 12 + string:length(S), 3, More};
+ {[$<,$<,S|"/utf8...>>"], 12 + io_lib:chars_length(S), 3, More};
false ->
case io_lib:write_binary(Bin, D, T) of
{S, <<>>} ->
@@ -591,7 +591,7 @@ print_length(<<_/bitstring>> = Bin, D, T, RF, Enc, Str) ->
print_length(Term, _D, _T, _RF, _Enc, _Str) ->
S = io_lib:write(Term),
%% S can contain unicode, so iolist_size(S) cannot be used here
- {S, string:length(S), 0, no_more}.
+ {S, io_lib:chars_length(S), 0, no_more}.
print_length_map(Map, 1, _T, RF, Enc, Str) ->
More = fun(T1, Dd) -> ?FUNCTION_NAME(Map, 1+Dd, T1, RF, Enc, Str) end,
@@ -651,7 +651,7 @@ print_length_record(Tuple, 1, _T, RF, RDefs, Enc, Str) ->
{"{...}", 5, 3, More};
print_length_record(Tuple, D, T, RF, RDefs, Enc, Str) ->
Name = [$# | write_atom(element(1, Tuple), Enc)],
- NameL = string:length(Name),
+ NameL = io_lib:chars_length(Name),
T1 = tsub(T, NameL+2),
L = print_length_fields(RDefs, D - 1, T1, Tuple, 2, RF, Enc, Str),
{Len, Dots} = list_length(L, NameL + 2, 0),
@@ -677,7 +677,7 @@ print_length_fields([Def | Defs], D, T, Tuple, I, RF, Enc, Str) ->
print_length_field(Def, D, T, E, RF, Enc, Str) ->
Name = write_atom(Def, Enc),
- NameL = string:length(Name) + 3,
+ NameL = io_lib:chars_length(Name) + 3,
{_, Len, Dots, _} =
Field = print_length(E, D, tsub(T, NameL), RF, Enc, Str),
{{field, Name, NameL, Field}, NameL + Len, Dots, no_more}.
@@ -738,7 +738,7 @@ printable_list(L, _D, T, _Uni) when T < 0->
io_lib:printable_list(L).
slice(L, N) ->
- try string:length(L) =< N of
+ try io_lib:chars_length(L) =< N of
true ->
all;
false ->
diff --git a/lib/stdlib/src/stdlib.appup.src b/lib/stdlib/src/stdlib.appup.src
index 2a324aef82..9e5d6a3bd8 100644
--- a/lib/stdlib/src/stdlib.appup.src
+++ b/lib/stdlib/src/stdlib.appup.src
@@ -38,7 +38,9 @@
{<<"^3\\.5\\.0(?:\\.[0-9]+)+$">>,[restart_new_emulator]},
{<<"^3\\.5\\.1(?:\\.[0-9]+)*$">>,[restart_new_emulator]},
{<<"^3\\.6$">>,[restart_new_emulator]},
- {<<"^3\\.6\\.0(?:\\.[0-9]+)+$">>,[restart_new_emulator]}],
+ {<<"^3\\.6\\.0(?:\\.[0-9]+)+$">>,[restart_new_emulator]},
+ {<<"^3\\.7$">>,[restart_new_emulator]},
+ {<<"^3\\.7\\.0(?:\\.[0-9]+)+$">>,[restart_new_emulator]}],
[{<<"^3\\.4$">>,[restart_new_emulator]},
{<<"^3\\.4\\.0(?:\\.[0-9]+)+$">>,[restart_new_emulator]},
{<<"^3\\.4\\.1(?:\\.[0-9]+)*$">>,[restart_new_emulator]},
@@ -50,4 +52,6 @@
{<<"^3\\.5\\.0(?:\\.[0-9]+)+$">>,[restart_new_emulator]},
{<<"^3\\.5\\.1(?:\\.[0-9]+)*$">>,[restart_new_emulator]},
{<<"^3\\.6$">>,[restart_new_emulator]},
- {<<"^3\\.6\\.0(?:\\.[0-9]+)+$">>,[restart_new_emulator]}]}.
+ {<<"^3\\.6\\.0(?:\\.[0-9]+)+$">>,[restart_new_emulator]},
+ {<<"^3\\.7$">>,[restart_new_emulator]},
+ {<<"^3\\.7\\.0(?:\\.[0-9]+)+$">>,[restart_new_emulator]}]}.
diff --git a/lib/stdlib/test/binary_module_SUITE.erl b/lib/stdlib/test/binary_module_SUITE.erl
index c5cfea5e9e..e0811f19cf 100644
--- a/lib/stdlib/test/binary_module_SUITE.erl
+++ b/lib/stdlib/test/binary_module_SUITE.erl
@@ -22,7 +22,8 @@
-export([all/0, suite/0,
interesting/1,scope_return/1,random_ref_comp/1,random_ref_sr_comp/1,
random_ref_fla_comp/1,parts/1, bin_to_list/1, list_to_bin/1,
- copy/1, referenced/1,guard/1,encode_decode/1,badargs/1,longest_common_trap/1]).
+ copy/1, referenced/1,guard/1,encode_decode/1,badargs/1,longest_common_trap/1,
+ check_no_invalid_read_bug/1]).
-export([random_number/1, make_unaligned/1]).
@@ -36,7 +37,7 @@ all() ->
[scope_return,interesting, random_ref_fla_comp, random_ref_sr_comp,
random_ref_comp, parts, bin_to_list, list_to_bin, copy,
referenced, guard, encode_decode, badargs,
- longest_common_trap].
+ longest_common_trap, check_no_invalid_read_bug].
-define(MASK_ERROR(EXPR),mask_error((catch (EXPR)))).
@@ -1361,3 +1362,13 @@ make_unaligned2(Bin0) when is_binary(Bin0) ->
Bin.
id(I) -> I.
+
+check_no_invalid_read_bug(Config) when is_list(Config) ->
+ check_no_invalid_read_bug(24);
+check_no_invalid_read_bug(60) ->
+ ok;
+check_no_invalid_read_bug(I) ->
+ N = 1 bsl I,
+ binary:encode_unsigned(N+N),
+ binary:encode_unsigned(N+N, little),
+ check_no_invalid_read_bug(I+1).
diff --git a/lib/stdlib/test/calendar_SUITE.erl b/lib/stdlib/test/calendar_SUITE.erl
index df62c0921d..c6d9dbca4a 100644
--- a/lib/stdlib/test/calendar_SUITE.erl
+++ b/lib/stdlib/test/calendar_SUITE.erl
@@ -24,6 +24,7 @@
-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
init_per_group/2,end_per_group/2,
gregorian_days/1,
+ big_gregorian_days/1,
gregorian_seconds/1,
day_of_the_week/1,
day_of_the_week_calibrate/1,
@@ -36,13 +37,16 @@
-define(START_YEAR, 1947).
-define(END_YEAR, 2012).
+-define(BIG_START_YEAR, 20000000).
+-define(BIG_END_YEAR, 20000020).
+
suite() -> [{ct_hooks,[ts_install_cth]}].
all() ->
[gregorian_days, gregorian_seconds, day_of_the_week,
day_of_the_week_calibrate, leap_years,
last_day_of_the_month, local_time_to_universal_time_dst,
- iso_week_number, system_time, rfc3339].
+ iso_week_number, system_time, rfc3339, big_gregorian_days].
groups() ->
[].
@@ -67,6 +71,14 @@ gregorian_days(Config) when is_list(Config) ->
MaxDays = calendar:date_to_gregorian_days({?END_YEAR, 1, 1}),
check_gregorian_days(Days, MaxDays).
+%% Tests that date_to_gregorian_days and gregorian_days_to_date
+%% are each others inverses from ?BIG_START_YEAR-01-01 up to ?BIG_END_YEAR-01-01.
+%% At the same time valid_date is tested.
+big_gregorian_days(Config) when is_list(Config) ->
+ Days = calendar:date_to_gregorian_days({?BIG_START_YEAR, 1, 1}),
+ MaxDays = calendar:date_to_gregorian_days({?BIG_END_YEAR, 1, 1}),
+ check_gregorian_days(Days, MaxDays).
+
%% Tests that datetime_to_gregorian_seconds and
%% gregorian_seconds_to_date are each others inverses for a sampled
%% number of seconds from ?START_YEAR-01-01 up to ?END_YEAR-01-01: We check
diff --git a/lib/stdlib/test/erl_eval_SUITE.erl b/lib/stdlib/test/erl_eval_SUITE.erl
index f4019d477b..2436c8091c 100644
--- a/lib/stdlib/test/erl_eval_SUITE.erl
+++ b/lib/stdlib/test/erl_eval_SUITE.erl
@@ -1156,7 +1156,17 @@ simple() ->
{A}.
simple1() ->
- erlang:error(simple).
+ %% If the compiler could see that this function would always
+ %% throw an error exception, it would rewrite simple() like this:
+ %%
+ %% simple() -> simple1().
+ %%
+ %% That would change the stacktrace. To prevent the compiler from
+ %% doing that optimization, we must obfuscate the code.
+ case get(a_key_that_is_not_defined) of
+ undefined -> erlang:error(simple);
+ WillNeverHappen -> WillNeverHappen
+ end.
%% Simple cases, just to cover some code.
funs(Config) when is_list(Config) ->
diff --git a/lib/stdlib/test/erl_pp_SUITE.erl b/lib/stdlib/test/erl_pp_SUITE.erl
index dda8d0a12e..f5d80e7e68 100644
--- a/lib/stdlib/test/erl_pp_SUITE.erl
+++ b/lib/stdlib/test/erl_pp_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2006-2018. All Rights Reserved.
+%% Copyright Ericsson AB 2006-2019. 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 @@
otp_6321/1, otp_6911/1, otp_6914/1, otp_8150/1, otp_8238/1,
otp_8473/1, otp_8522/1, otp_8567/1, otp_8664/1, otp_9147/1,
otp_10302/1, otp_10820/1, otp_11100/1, otp_11861/1, pr_1014/1,
- otp_13662/1, otp_14285/1]).
+ otp_13662/1, otp_14285/1, otp_15592/1]).
%% Internal export.
-export([ehook/6]).
@@ -81,7 +81,7 @@ groups() ->
[otp_6321, otp_6911, otp_6914, otp_8150, otp_8238,
otp_8473, otp_8522, otp_8567, otp_8664, otp_9147,
otp_10302, otp_10820, otp_11100, otp_11861, pr_1014, otp_13662,
- otp_14285]}].
+ otp_14285, otp_15592]}].
init_per_suite(Config) ->
Config.
@@ -1167,6 +1167,11 @@ otp_14285(_Config) ->
[{encoding,latin1}])),
ok.
+otp_15592(_Config) ->
+ ok = pp_expr(<<"long12345678901234567890123456789012345678901234"
+ "56789012345678901234:f(<<>>)">>),
+ ok.
+
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
compile(Config, Tests) ->
diff --git a/lib/stdlib/vsn.mk b/lib/stdlib/vsn.mk
index e0217418fe..d46173497b 100644
--- a/lib/stdlib/vsn.mk
+++ b/lib/stdlib/vsn.mk
@@ -1 +1 @@
-STDLIB_VSN = 3.7
+STDLIB_VSN = 3.7.1
diff --git a/lib/tools/doc/src/cover.xml b/lib/tools/doc/src/cover.xml
index 31bf7266c7..e9f782977d 100644
--- a/lib/tools/doc/src/cover.xml
+++ b/lib/tools/doc/src/cover.xml
@@ -128,14 +128,26 @@
</desc>
</func>
<func>
- <name since="">start(Nodes) -> {ok,StartedNodes} | {error,not_main_node}</name>
+ <name since="OTP 22.0">local_only() -> ok | {error,too_late}</name>
+ <fsummary>Only support running Cover on the local node.</fsummary>
+ <desc>
+ <p>Only support running Cover on the local node. This function
+ must be called before any modules have been compiled or any
+ nodes added. When running in this mode, modules will be Cover
+ compiled in a more efficient way, but the resulting code will
+ only work on the same node they were compiled on.</p>
+ </desc>
+ </func>
+ <func>
+ <name since="">start(Nodes) -> {ok,StartedNodes} | {error,not_main_node} | {error,local_only}</name>
<fsummary>Start Cover on remote nodes.</fsummary>
<type>
<v>Nodes = StartedNodes = [atom()]</v>
</type>
<desc>
<p>Starts a Cover server on the each of given nodes, and loads
- all cover compiled modules.</p>
+ all cover compiled modules. This call will fail if
+ <c>cover:local_only/0</c> has been called.</p>
</desc>
</func>
<func>
diff --git a/lib/tools/src/cover.erl b/lib/tools/src/cover.erl
index 8d4561ca9e..8fe866cb69 100644
--- a/lib/tools/src/cover.erl
+++ b/lib/tools/src/cover.erl
@@ -23,6 +23,7 @@
%% This module implements the Erlang coverage tool.
%%
%% ARCHITECTURE
+%%
%% The coverage tool consists of one process on each node involved in
%% coverage analysis. The process is registered as 'cover_server'
%% (?SERVER). The cover_server on the 'main' node is in charge, and
@@ -30,45 +31,62 @@
%% 'DOWN' message for another cover_server, it marks the node as
%% 'lost'. If a nodeup is received for a lost node the main node
%% ensures that the cover compiled modules are loaded again. If the
-%% remote node was alive during the disconnected periode, cover data
-%% for this periode will also be included in the analysis.
+%% remote node was alive during the disconnected period, cover data
+%% for this period will also be included in the analysis.
%%
%% The cover_server process on the main node is implemented by the
%% functions init_main/1 and main_process_loop/1. The cover_server on
%% the remote nodes are implemented by the functions init_remote/2 and
%% remote_process_loop/1.
%%
+%% COUNTERS
+%%
+%% The 'counters' modules is used for counting how many time each line
+%% executed. Each cover-compiled module will have its own array of
+%% counters.
+%%
+%% The counter reference for module Module is stored in a persistent
+%% term with the key {cover,Module}.
+%%
+%% When the cover:local_only/0 function has been called, the reference
+%% for the counter array will be compiled into each cover-compiled
+%% module directly (instead of retrieving it from a persistent term).
+%% That will be faster, but the resulting code can be only be used on
+%% the main node.
+%%
%% TABLES
-%% Each nodes has two tables: cover_internal_data_table (?COVER_TABLE) and.
-%% cover_internal_clause_table (?COVER_CLAUSE_TABLE).
-%% ?COVER_TABLE contains the bump data i.e. the data about which lines
-%% have been executed how many times.
+%%
+%% Each node has two tables: ?COVER_MAPPING_TABLE and ?COVER_CLAUSE_TABLE.
+%% ?COVER_MAPPING_TABLE maps from a #bump{} record to an index in the
+%% counter array for the module. It is used both during instrumentation
+%% of cover-compiled modules and when collecting the counter values.
+%%
%% ?COVER_CLAUSE_TABLE contains information about which clauses in which modules
%% cover is currently collecting statistics.
-%%
-%% The main node owns tables named
-%% 'cover_collected_remote_data_table' (?COLLECTION_TABLE) and
-%% 'cover_collected_remote_clause_table' (?COLLECTION_CLAUSE_TABLE).
-%% These tables contain data which is collected from remote nodes (either when a
-%% remote node is stopped with cover:stop/1 or when analysing). When
-%% analysing, data is even moved from the COVER tables on the main
-%% node to the COLLECTION tables.
%%
-%% The main node also has a table named 'cover_binary_code_table'
-%% (?BINARY_TABLE). This table contains the binary code for each cover
-%% compiled module. This is necessary so that the code can be loaded
-%% on remote nodes that are started after the compilation.
+%% The main node owns the tables ?COLLECTION_TABLE and
+%% ?COLLECTION_CLAUSE_TABLE. The counter data is consolidated into those
+%% tables from the counters on both the main node and from remote nodes.
+%% This consolidation is done when a remote node is stopped with
+%% cover:stop/1 or just before starting an analysis.
+%%
+%% The main node also has a table named ?BINARY_TABLE. This table
+%% contains the abstract code code for each cover-compiled
+%% module. This is necessary so that the code can be loaded on remote
+%% nodes that are started after the compilation.
%%
%% PARALLELISM
+%%
%% To take advantage of SMP when doing the cover analysis both the data
%% collection and analysis has been parallelized. One process is spawned for
%% each node when collecting data, and on the remote node when collecting data
%% one process is spawned per module.
%%
-%% When analyzing data it is possible to issue multiple analyse(_to_file)/X
-%% calls at once. They are however all calls (for backwards compatibility
-%% reasons) so the user of cover will have to spawn several processes to to the
-%% calls ( or use async_analyse_to_file ).
+%% When analyzing data it is possible to issue multiple
+%% analyse(_to_file)/X calls at once. They are, however, all calls
+%% (for backwards compatibility reasons), so the user of cover will
+%% have to spawn several processes to to the calls (or use
+%% async_analyse_to_file/X).
%%
%% External exports
@@ -89,7 +107,8 @@
modules/0, imported/0, imported_modules/0, which_nodes/0, is_compiled/1,
reset/1, reset/0,
flush/1,
- stop/0, stop/1]).
+ stop/0, stop/1,
+ local_only/0]).
-export([remote_start/1,get_main_node/0]).
%% Used internally to ensure we upgrade the code to the latest version.
@@ -98,9 +117,16 @@
-record(main_state, {compiled=[], % [{Module,File}]
imported=[], % [{Module,File,ImportFile}]
stopper, % undefined | pid()
+ local_only=false, % true | false
nodes=[], % [Node]
lost_nodes=[]}). % [Node]
+-record(remote_data, {module,
+ file,
+ code,
+ mapping,
+ clauses}).
+
-record(remote_state, {compiled=[], % [{Module,File}]
main_node}). % atom()
@@ -126,11 +152,12 @@
is_guard=false % boolean
}).
--define(COVER_TABLE, 'cover_internal_data_table').
+-define(COVER_MAPPING_TABLE, 'cover_internal_mapping_table').
-define(COVER_CLAUSE_TABLE, 'cover_internal_clause_table').
-define(BINARY_TABLE, 'cover_binary_code_table').
-define(COLLECTION_TABLE, 'cover_collected_remote_data_table').
-define(COLLECTION_CLAUSE_TABLE, 'cover_collected_remote_clause_table').
+
-define(TAG, cover_compiled).
-define(SERVER, cover_server).
@@ -186,6 +213,11 @@ start(Node) when is_atom(Node) ->
start(Nodes) ->
call({start_nodes,remove_myself(Nodes,[])}).
+%% local_only() -> ok | {error,too_late}
+
+local_only() ->
+ call(local_only).
+
%% compile(ModFiles) ->
%% compile(ModFiles, Options) ->
%% compile_module(ModFiles) -> Result
@@ -255,15 +287,8 @@ compile_directory(Dir, Options) when is_list(Dir), is_list(Options) ->
compile_modules(Files,Options) ->
Options2 = filter_options(Options),
- %% compile_modules(Files,Options2,[]).
call({compile, Files, Options2}).
-%% compile_modules([File|Files], Options, Result) ->
-%% R = call({compile, File, Options}),
-%% compile_modules(Files,Options,[R|Result]);
-%% compile_modules([],_Opts,Result) ->
-%% lists:reverse(Result).
-
filter_options(Options) ->
lists:filter(fun(Option) ->
case Option of
@@ -561,16 +586,6 @@ flush(Nodes) ->
get_main_node() ->
call(get_main_node).
-%% bump(Module, Function, Arity, Clause, Line)
-%% Module = Function = atom()
-%% Arity = Clause = Line = integer()
-%% This function is inserted into Cover compiled modules, once for each
-%% executable line.
-%bump(Module, Function, Arity, Clause, Line) ->
-% Key = #bump{module=Module, function=Function, arity=Arity, clause=Clause,
-% line=Line},
-% ets:update_counter(?COVER_TABLE, Key, 1).
-
call(Request) ->
Ref = erlang:monitor(process,?SERVER),
receive {'DOWN', Ref, _Type, _Object, noproc} ->
@@ -631,10 +646,8 @@ remote_reply(MainNode,Reply) ->
init_main(Starter) ->
register(?SERVER,self()),
- %% Having write concurrancy here gives a 40% performance boost
- %% when collect/1 is called.
- ?COVER_TABLE = ets:new(?COVER_TABLE, [set, public, named_table,
- {write_concurrency, true}]),
+ ?COVER_MAPPING_TABLE = ets:new(?COVER_MAPPING_TABLE,
+ [ordered_set, public, named_table]),
?COVER_CLAUSE_TABLE = ets:new(?COVER_CLAUSE_TABLE, [set, public,
named_table]),
?BINARY_TABLE = ets:new(?BINARY_TABLE, [set, public, named_table]),
@@ -648,10 +661,26 @@ init_main(Starter) ->
main_process_loop(State) ->
receive
+ {From, local_only} ->
+ case State of
+ #main_state{compiled=[],nodes=[]} ->
+ reply(From, ok),
+ main_process_loop(State#main_state{local_only=true});
+ #main_state{} ->
+ reply(From, {error,too_late}),
+ main_process_loop(State)
+ end;
+
{From, {start_nodes,Nodes}} ->
- {StartedNodes,State1} = do_start_nodes(Nodes, State),
- reply(From, {ok,StartedNodes}),
- main_process_loop(State1);
+ case State#main_state.local_only of
+ false ->
+ {StartedNodes,State1} = do_start_nodes(Nodes, State),
+ reply(From, {ok,StartedNodes}),
+ main_process_loop(State1);
+ true ->
+ reply(From, {error,local_only}),
+ main_process_loop(State)
+ end;
{From, {compile, Files, Options}} ->
{R,S} = do_compile(Files, Options, State),
@@ -742,11 +771,12 @@ main_process_loop(State) ->
end,
State#main_state.nodes),
reload_originals(State#main_state.compiled),
- ets:delete(?COVER_TABLE),
+ ets:delete(?COVER_MAPPING_TABLE),
ets:delete(?COVER_CLAUSE_TABLE),
ets:delete(?BINARY_TABLE),
ets:delete(?COLLECTION_TABLE),
ets:delete(?COLLECTION_CLAUSE_TABLE),
+ delete_all_counters(),
unregister(?SERVER),
reply(From, ok);
@@ -878,10 +908,8 @@ main_process_loop(State) ->
init_remote(Starter,MainNode) ->
register(?SERVER,self()),
- %% write_concurrency here makes otp_8270 break :(
- ?COVER_TABLE = ets:new(?COVER_TABLE, [set, public, named_table
- %,{write_concurrency, true}
- ]),
+ ?COVER_MAPPING_TABLE = ets:new(?COVER_MAPPING_TABLE,
+ [ordered_set, public, named_table]),
?COVER_CLAUSE_TABLE = ets:new(?COVER_CLAUSE_TABLE, [set, public,
named_table]),
Starter ! {self(),started},
@@ -904,7 +932,7 @@ remote_process_loop(State) ->
remote_process_loop(State#remote_state{compiled=Compiled});
{remote,reset,Module} ->
- do_reset(Module),
+ reset_counters(Module),
remote_reply(State#remote_state.main_node, ok),
remote_process_loop(State);
@@ -925,8 +953,9 @@ remote_process_loop(State) ->
{remote,stop} ->
reload_originals(State#remote_state.compiled),
- ets:delete(?COVER_TABLE),
+ ets:delete(?COVER_MAPPING_TABLE),
ets:delete(?COVER_CLAUSE_TABLE),
+ delete_all_counters(),
unregister(?SERVER),
ok; % not replying since 'DOWN' message will be received anyway
@@ -961,28 +990,12 @@ remote_process_loop(State) ->
end.
do_collect(Modules, CollectorPid, From) ->
- _ = pmap(
- fun(Module) ->
- Pattern = {#bump{module=Module, _='_'}, '$1'},
- MatchSpec = [{Pattern,[{'=/=','$1',0}],['$_']}],
- Match = ets:select(?COVER_TABLE,MatchSpec,?CHUNK_SIZE),
- send_chunks(Match, CollectorPid, [])
- end,Modules),
+ _ = pmap(fun(Module) ->
+ send_counters(Module, CollectorPid)
+ end, Modules),
CollectorPid ! done,
remote_reply(From, ok).
-send_chunks('$end_of_table', _CollectorPid, Mons) ->
- get_downs(Mons);
-send_chunks({Chunk,Continuation}, CollectorPid, Mons) ->
- Mon = spawn_monitor(
- fun() ->
- lists:foreach(fun({Bump,_N}) ->
- ets:insert(?COVER_TABLE, {Bump,0})
- end,
- Chunk) end),
- send_chunk(CollectorPid,Chunk),
- send_chunks(ets:select(Continuation), CollectorPid, [Mon|Mons]).
-
send_chunk(CollectorPid,Chunk) ->
CollectorPid ! {chunk,Chunk,self()},
receive continue -> ok end.
@@ -1021,10 +1034,15 @@ do_reload_original(Module) ->
ignore
end.
-load_compiled([{Module,File,Binary,InitialTable}|Compiled],Acc) ->
- %% Make sure the #bump{} records are available *before* the
- %% module is loaded.
- insert_initial_data(InitialTable),
+load_compiled([Data|Compiled],Acc) ->
+ %% Make sure the #bump{} records and counters are available *before*
+ %% compiling and loading the code.
+ #remote_data{module=Module,file=File,code=Beam,
+ mapping=InitialMapping,clauses=InitialClauses} = Data,
+ ets:insert(?COVER_MAPPING_TABLE, InitialMapping),
+ ets:insert(?COVER_CLAUSE_TABLE, InitialClauses),
+ maybe_create_counters(Module, true),
+
Sticky = case code:is_sticky(Module) of
true ->
code:unstick_mod(Module),
@@ -1032,7 +1050,7 @@ load_compiled([{Module,File,Binary,InitialTable}|Compiled],Acc) ->
false ->
false
end,
- NewAcc = case code:load_binary(Module, ?TAG, Binary) of
+ NewAcc = case code:load_binary(Module, ?TAG, Beam) of
{module,Module} ->
add_compiled(Module, File, Acc);
_ ->
@@ -1047,16 +1065,6 @@ load_compiled([{Module,File,Binary,InitialTable}|Compiled],Acc) ->
load_compiled([],Acc) ->
Acc.
-insert_initial_data([Item|Items]) when is_atom(element(1,Item)) ->
- ets:insert(?COVER_CLAUSE_TABLE, Item),
- insert_initial_data(Items);
-insert_initial_data([Item|Items]) ->
- ets:insert(?COVER_TABLE, Item),
- insert_initial_data(Items);
-insert_initial_data([]) ->
- ok.
-
-
unload([Module|Modules]) ->
do_clear(Module),
do_reload_original(Module),
@@ -1177,7 +1185,7 @@ get_downs_r([]) ->
[];
get_downs_r(Mons) ->
receive
- {'DOWN', Ref, _Type, Pid, R={_,_,_,_}} ->
+ {'DOWN', Ref, _Type, Pid, #remote_data{}=R} ->
[R|get_downs_r(lists:delete({Pid,Ref},Mons))];
{'DOWN', Ref, _Type, Pid, Reason} = Down ->
case lists:member({Pid,Ref},Mons) of
@@ -1196,19 +1204,13 @@ get_downs_r(Mons) ->
%% Binary is the beam code for the module and InitialTable is the initial
%% data to insert in ?COVER_TABLE.
get_data_for_remote_loading({Module,File}) ->
- [{Module,Binary}] = ets:lookup(?BINARY_TABLE,Module),
+ [{Module,Code}] = ets:lookup(?BINARY_TABLE, Module),
%%! The InitialTable list will be long if the module is big - what to do??
- InitialBumps = ets:select(?COVER_TABLE,ms(Module)),
+ Mapping = counters_mapping_table(Module),
InitialClauses = ets:lookup(?COVER_CLAUSE_TABLE,Module),
- {Module,File,Binary,InitialBumps ++ InitialClauses}.
-
-%% Create a match spec which returns the clause info {Module,InitInfo} and
-%% all #bump keys for the given module with 0 number of calls.
-ms(Module) ->
- ets:fun2ms(fun({Key,_}) when Key#bump.module=:=Module ->
- {Key,0}
- end).
+ #remote_data{module=Module,file=File,code=Code,
+ mapping=Mapping,clauses=InitialClauses}.
%% Unload modules on remote nodes
remote_unload(Nodes,UnloadedModules) ->
@@ -1464,7 +1466,7 @@ get_compiled_still_loaded(Nodes,Compiled0) ->
do_compile_beams(ModsAndFiles, State) ->
Result0 = pmap(fun({ok,Module,File}) ->
- do_compile_beam(Module,File,State);
+ do_compile_beam(Module, File, State);
(Error) ->
Error
end,
@@ -1476,8 +1478,10 @@ do_compile_beams(ModsAndFiles, State) ->
do_compile_beam(Module,BeamFile0,State) ->
case get_beam_file(Module,BeamFile0,State#main_state.compiled) of
{ok,BeamFile} ->
+ LocalOnly = State#main_state.local_only,
UserOptions = get_compile_options(Module,BeamFile),
- case do_compile_beam1(Module,BeamFile,UserOptions) of
+ case do_compile_beam1(Module,BeamFile,
+ UserOptions,LocalOnly) of
{ok, Module} ->
{ok,Module,BeamFile};
error ->
@@ -1503,41 +1507,39 @@ fix_state_and_result([],State,Acc) ->
do_compile(Files, Options, State) ->
+ LocalOnly = State#main_state.local_only,
Result0 = pmap(fun(File) ->
- do_compile(File, Options)
+ do_compile1(File, Options, LocalOnly)
end,
Files),
Compiled = [{M,F} || {ok,M,F} <- Result0],
remote_load_compiled(State#main_state.nodes,Compiled),
fix_state_and_result(Result0,State,[]).
-do_compile(File, Options) ->
- case do_compile1(File, Options) of
+do_compile1(File, Options, LocalOnly) ->
+ case do_compile2(File, Options, LocalOnly) of
{ok, Module} ->
{ok,Module,File};
error ->
{error,File}
end.
-%% do_compile1(File, Options) -> {ok,Module} | error
-do_compile1(File, UserOptions) ->
+%% do_compile2(File, Options) -> {ok,Module} | error
+do_compile2(File, UserOptions, LocalOnly) ->
Options = [debug_info,binary,report_errors,report_warnings] ++ UserOptions,
case compile:file(File, Options) of
{ok, Module, Binary} ->
- do_compile_beam1(Module,Binary,UserOptions);
+ do_compile_beam1(Module,Binary,UserOptions,LocalOnly);
error ->
error
end.
%% Beam is a binary or a .beam file name
-do_compile_beam1(Module,Beam,UserOptions) ->
+do_compile_beam1(Module,Beam,UserOptions,LocalOnly) ->
%% Clear database
do_clear(Module),
- %% Extract the abstract format and insert calls to bump/6 at
- %% every executable line and, as a side effect, initiate
- %% the database
-
+ %% Extract the abstract format.
case get_abstract_code(Module, Beam) of
no_abstract_code=E ->
{error,E};
@@ -1547,7 +1549,8 @@ do_compile_beam1(Module,Beam,UserOptions) ->
Forms0 = epp:interpret_file_attribute(Code),
case find_main_filename(Forms0) of
{ok,MainFile} ->
- do_compile_beam2(Module,Beam,UserOptions,Forms0,MainFile);
+ do_compile_beam2(Module,Beam,UserOptions,
+ Forms0,MainFile,LocalOnly);
Error ->
Error
end;
@@ -1566,26 +1569,35 @@ get_abstract_code(Module, Beam) ->
Error -> Error
end.
-do_compile_beam2(Module,Beam,UserOptions,Forms0,MainFile) ->
- {Forms,Vars} = transform(Forms0, Module, MainFile),
+do_compile_beam2(Module,Beam,UserOptions,Forms0,MainFile,LocalOnly) ->
+ init_counter_mapping(Module),
+
+ %% Instrument the abstract code by inserting
+ %% calls to update the counters.
+ {Forms,Vars} = transform(Forms0, Module, MainFile, LocalOnly),
+
+ %% Create counters.
+ maybe_create_counters(Module, not LocalOnly),
%% We need to recover the source from the compilation
%% info otherwise the newly compiled module will have
%% source pointing to the current directory
SourceInfo = get_source_info(Module, Beam),
- %% Compile and load the result
+ %% Compile and load the result.
%% It's necessary to check the result of loading since it may
- %% fail, for example if Module resides in a sticky directory
- {ok, Module, Binary} = compile:forms(Forms, SourceInfo ++ UserOptions),
+ %% fail, for example if Module resides in a sticky directory.
+ Options = SourceInfo ++ UserOptions,
+ {ok, Module, Binary} = compile:forms(Forms, Options),
+
case code:load_binary(Module, ?TAG, Binary) of
{module, Module} ->
- %% Store info about all function clauses in database
+ %% Store info about all function clauses in database.
InitInfo = lists:reverse(Vars#vars.init_info),
ets:insert(?COVER_CLAUSE_TABLE, {Module, InitInfo}),
- %% Store binary code so it can be loaded on remote nodes
+ %% Store binary code so it can be loaded on remote nodes.
ets:insert(?BINARY_TABLE, {Module, Binary}),
{ok, Module};
@@ -1617,11 +1629,12 @@ get_compile_info(Module, Beam) ->
[]
end.
-transform(Code, Module, MainFile) ->
+transform(Code, Module, MainFile, LocalOnly) ->
Vars0 = #vars{module=Module},
- {ok,MungedForms,Vars} = transform_2(Code,[],Vars0,MainFile,on),
+ {ok,MungedForms0,Vars} = transform_2(Code, [], Vars0, MainFile, on),
+ MungedForms = patch_code(Module, MungedForms0, LocalOnly),
{MungedForms,Vars}.
-
+
%% Helpfunction which returns the first found file-attribute, which can
%% be interpreted as the name of the main erlang source file.
find_main_filename([{attribute,_,file,{MainFile,_}}|_]) ->
@@ -1788,19 +1801,7 @@ munge_body([Expr|Body], Vars, MungedBody, LastExprBumpLines) ->
MungedExprs1 = [MungedExpr|MungedBody1],
munge_body(Body, Vars3, MungedExprs1, NewBumps);
false ->
- ets:insert(?COVER_TABLE, {#bump{module = Vars#vars.module,
- function = Vars#vars.function,
- arity = Vars#vars.arity,
- clause = Vars#vars.clause,
- line = Line},
- 0}),
Bump = bump_call(Vars, Line),
-% Bump = {call, 0, {remote, 0, {atom,0,cover}, {atom,0,bump}},
-% [{atom, 0, Vars#vars.module},
-% {atom, 0, Vars#vars.function},
-% {integer, 0, Vars#vars.arity},
-% {integer, 0, Vars#vars.clause},
-% {integer, 0, Line}]},
Lines2 = [Line|Lines],
{MungedExpr, Vars2} = munge_expr(Expr, Vars#vars{lines=Lines2}),
NewBumps = new_bumps(Vars2, Vars),
@@ -1855,8 +1856,10 @@ maybe_fix_last_expr(MungedExprs, Vars, LastExprBumpLines) ->
last_expr_needs_fixing(Vars, LastExprBumpLines) ->
case common_elems(Vars#vars.no_bump_lines, LastExprBumpLines) of
- [Line] -> {yes, Line};
- _ -> no
+ [Line] ->
+ {yes, Line};
+ _ ->
+ no
end.
fix_last_expr([MungedExpr|MungedExprs], Line, Vars) ->
@@ -1921,9 +1924,7 @@ fix_cls([Cl | Cls], Line, Bump) ->
bumps_line(E, L) ->
try bumps_line1(E, L) catch true -> true end.
-bumps_line1({call,_,{remote,_,{atom,_,ets},{atom,_,update_counter}},
- [{atom,_,?COVER_TABLE},{tuple,_,[_,_,_,_,_,{integer,_,Line}]},_]},
- Line) ->
+bumps_line1({'BUMP',Line,_}, Line) ->
throw(true);
bumps_line1([E | Es], Line) ->
bumps_line1(E, Line),
@@ -1933,19 +1934,12 @@ bumps_line1(T, Line) when is_tuple(T) ->
bumps_line1(_, _) ->
false.
-%%% End of fix of last expression.
-
+%% Insert a place holder for the call to counters:add/3 in the
+%% abstract code.
bump_call(Vars, Line) ->
- A = erl_anno:new(0),
- {call,A,{remote,A,{atom,A,ets},{atom,A,update_counter}},
- [{atom,A,?COVER_TABLE},
- {tuple,A,[{atom,A,?BUMP_REC_NAME},
- {atom,A,Vars#vars.module},
- {atom,A,Vars#vars.function},
- {integer,A,Vars#vars.arity},
- {integer,A,Vars#vars.clause},
- {integer,A,Line}]},
- {integer,A,1}]}.
+ {'BUMP',Line,counter_index(Vars, Line)}.
+
+%%% End of fix of last expression.
munge_expr({match,Line,ExprL,ExprR}, Vars) ->
{MungedExprL, Vars2} = munge_expr(ExprL, Vars),
@@ -2105,6 +2099,159 @@ subtract(L1, L2) ->
common_elems(L1, L2) ->
[E || E <- L1, lists:member(E, L2)].
+%%%--Counters------------------------------------------------------------
+
+init_counter_mapping(Mod) ->
+ true = ets:insert_new(?COVER_MAPPING_TABLE, {Mod,0}),
+ ok.
+
+counter_index(Vars, Line) ->
+ #vars{module=Mod,function=F,arity=A,clause=C} = Vars,
+ Key = #bump{module=Mod,function=F,arity=A,
+ clause=C,line=Line},
+ case ets:lookup(?COVER_MAPPING_TABLE, Key) of
+ [] ->
+ Index = ets:update_counter(?COVER_MAPPING_TABLE,
+ Mod, {2,1}),
+ true = ets:insert(?COVER_MAPPING_TABLE, {Key,Index}),
+ Index;
+ [{Key,Index}] ->
+ Index
+ end.
+
+%% Create the counter array and store as a persistent term.
+maybe_create_counters(Mod, true) ->
+ Cref = create_counters(Mod),
+ Key = {?MODULE,Mod},
+ persistent_term:put(Key, Cref),
+ ok;
+maybe_create_counters(_Mod, false) ->
+ ok.
+
+create_counters(Mod) ->
+ Size0 = ets:lookup_element(?COVER_MAPPING_TABLE, Mod, 2),
+ Size = max(1, Size0), %Size must not be 0.
+ Cref = counters:new(Size, [write_concurrency]),
+ ets:insert(?COVER_MAPPING_TABLE, {{counters,Mod},Cref}),
+ Cref.
+
+patch_code(Mod, Forms, false) ->
+ A = erl_anno:new(0),
+ AbstrKey = {tuple,A,[{atom,A,?MODULE},{atom,A,Mod}]},
+ patch_code1(Forms, {distributed,AbstrKey});
+patch_code(Mod, Forms, true) ->
+ Cref = create_counters(Mod),
+ AbstrCref = cid_to_abstract(Cref),
+ patch_code1(Forms, {local_only,AbstrCref}).
+
+%% Go through the abstract code and replace 'BUMP' forms
+%% with the actual code to increment the counters.
+patch_code1({'BUMP',_Line,Index}, {distributed,AbstrKey}) ->
+ %% Replace with counters:add(persistent_term:get(Key), Index, 1).
+ %% This code will work on any node.
+ A = element(2, AbstrKey),
+ GetCref = {call,A,{remote,A,{atom,A,persistent_term},{atom,A,get}},
+ [AbstrKey]},
+ {call,A,{remote,A,{atom,A,counters},{atom,A,add}},
+ [GetCref,{integer,A,Index},{integer,A,1}]};
+patch_code1({'BUMP',_Line,Index}, {local_only,AbstrCref}) ->
+ %% Replace with counters:add(Cref, Index, 1). This code
+ %% will only work on the local node.
+ A = element(2, AbstrCref),
+ {call,A,{remote,A,{atom,A,counters},{atom,A,add}},
+ [AbstrCref,{integer,A,Index},{integer,A,1}]};
+patch_code1({clauses,Cs}, Key) ->
+ {clauses,[patch_code1(El, Key) || El <- Cs]};
+patch_code1([_|_]=List, Key) ->
+ [patch_code1(El, Key) || El <- List];
+patch_code1(Tuple, Key) when tuple_size(Tuple) >= 3 ->
+ Acc = [element(2, Tuple),element(1, Tuple)],
+ patch_code_tuple(3, tuple_size(Tuple), Tuple, Key, Acc);
+patch_code1(Other, _Key) ->
+ Other.
+
+patch_code_tuple(I, Size, Tuple, Key, Acc) when I =< Size ->
+ El = patch_code1(element(I, Tuple), Key),
+ patch_code_tuple(I + 1, Size, Tuple, Key, [El|Acc]);
+patch_code_tuple(_I, _Size, _Tuple, _Key, Acc) ->
+ list_to_tuple(lists:reverse(Acc)).
+
+%% Don't try this at home! Assumes knowledge of the internal
+%% representation of a counter ref.
+cid_to_abstract(Cref0) ->
+ A = erl_anno:new(0),
+ %% Disable dialyzer warning for breaking opacity.
+ Cref = binary_to_term(term_to_binary(Cref0)),
+ {write_concurrency,Ref} = Cref,
+ {tuple,A,[{atom,A,write_concurrency},{integer,A,Ref}]}.
+
+%% Called on the remote node. Collect and send counters to
+%% the main node. Also zero the counters.
+send_counters(Mod, CollectorPid) ->
+ Process = fun(Chunk) -> send_chunk(CollectorPid, Chunk) end,
+ move_counters(Mod, Process).
+
+%% Called on the main node. Collect the counters and consolidate
+%% them into the collection table. Also zero the counters.
+move_counters(Mod) ->
+ move_counters(Mod, fun insert_in_collection_table/1).
+
+move_counters(Mod, Process) ->
+ Pattern = {#bump{module=Mod,_='_'},'_'},
+ Matches = ets:match_object(?COVER_MAPPING_TABLE, Pattern, ?CHUNK_SIZE),
+ Cref = get_counters_ref(Mod),
+ move_counters1(Matches, Cref, Process).
+
+move_counters1({Mappings,Continuation}, Cref, Process) ->
+ Move = fun({Key,Index}) ->
+ Count = counters:get(Cref, Index),
+ ok = counters:sub(Cref, Index, Count),
+ {Key,Count}
+ end,
+ Process(lists:map(Move, Mappings)),
+ move_counters1(ets:match_object(Continuation), Cref, Process);
+move_counters1('$end_of_table', _Cref, _Process) ->
+ ok.
+
+counters_mapping_table(Mod) ->
+ Mapping = counters_mapping(Mod),
+ Cref = get_counters_ref(Mod),
+ #{size:=Size} = counters:info(Cref),
+ [{Mod,Size}|Mapping].
+
+get_counters_ref(Mod) ->
+ ets:lookup_element(?COVER_MAPPING_TABLE, {counters,Mod}, 2).
+
+counters_mapping(Mod) ->
+ Pattern = {#bump{module=Mod,_='_'},'_'},
+ ets:match_object(?COVER_MAPPING_TABLE, Pattern).
+
+clear_counters(Mod) ->
+ _ = persistent_term:erase({?MODULE,Mod}),
+ ets:delete(?COVER_MAPPING_TABLE, Mod),
+ Pattern = {#bump{module=Mod,_='_'},'_'},
+ _ = ets:match_delete(?COVER_MAPPING_TABLE, Pattern),
+ ok.
+
+%% Reset counters (set counters to 0).
+reset_counters(Mod) ->
+ Pattern = {#bump{module=Mod,_='_'},'$1'},
+ MatchSpec = [{Pattern,[],['$1']}],
+ Matches = ets:select(?COVER_MAPPING_TABLE,
+ MatchSpec, ?CHUNK_SIZE),
+ Cref = get_counters_ref(Mod),
+ reset_counters1(Matches, Cref).
+
+reset_counters1({Indices,Continuation}, Cref) ->
+ _ = [counters:put(Cref, N, 0) || N <- Indices],
+ reset_counters1(ets:select(Continuation), Cref);
+reset_counters1('$end_of_table', _Cref) ->
+ ok.
+
+delete_all_counters() ->
+ _ = [persistent_term:erase(Key) || {?MODULE,_}=Key <- persistent_term:get()],
+ ok.
+
%%%--Analysis------------------------------------------------------------
%% Collect data for all modules
@@ -2140,20 +2287,7 @@ collect(Module,Clauses,Nodes) ->
%% ?COLLECTION_TABLE. Resetting data in ?COVER_TABLE
move_modules({Module,Clauses}) ->
ets:insert(?COLLECTION_CLAUSE_TABLE,{Module,Clauses}),
- Pattern = {#bump{module=Module, _='_'}, '_'},
- MatchSpec = [{Pattern,[],['$_']}],
- Match = ets:select(?COVER_TABLE,MatchSpec,?CHUNK_SIZE),
- do_move_module(Match).
-
-do_move_module({Bumps,Continuation}) ->
- lists:foreach(fun({Key,Val}) ->
- ets:insert(?COVER_TABLE, {Key,0}),
- insert_in_collection_table(Key,Val)
- end,
- Bumps),
- do_move_module(ets:select(Continuation));
-do_move_module('$end_of_table') ->
- ok.
+ move_counters(Module).
%% Given a .beam file, find the .erl file. Look first in same directory as
%% the .beam file, then in ../src, then in compile info.
@@ -2709,7 +2843,7 @@ get_term(Fd) ->
%% Reset main node and all remote nodes
do_reset_main_node(Module,Nodes) ->
- do_reset(Module),
+ reset_counters(Module),
do_reset_collection_table(Module),
remote_reset(Module,Nodes).
@@ -2717,27 +2851,9 @@ do_reset_collection_table(Module) ->
ets:delete(?COLLECTION_CLAUSE_TABLE,Module),
ets:match_delete(?COLLECTION_TABLE, {#bump{module=Module},'_'}).
-%% do_reset(Module) -> ok
-%% The reset is done on ?CHUNK_SIZE number of bumps to avoid building
-%% long lists in the case of very large modules
-do_reset(Module) ->
- Pattern = {#bump{module=Module, _='_'}, '$1'},
- MatchSpec = [{Pattern,[{'=/=','$1',0}],['$_']}],
- Match = ets:select(?COVER_TABLE,MatchSpec,?CHUNK_SIZE),
- do_reset2(Match).
-
-do_reset2({Bumps,Continuation}) ->
- lists:foreach(fun({Bump,_N}) ->
- ets:insert(?COVER_TABLE, {Bump,0})
- end,
- Bumps),
- do_reset2(ets:select(Continuation));
-do_reset2('$end_of_table') ->
- ok.
-
do_clear(Module) ->
ets:match_delete(?COVER_CLAUSE_TABLE, {Module,'_'}),
- ets:match_delete(?COVER_TABLE, {#bump{module=Module},'_'}),
+ clear_counters(Module),
case lists:member(?COLLECTION_TABLE, ets:all()) of
true ->
%% We're on the main node
diff --git a/lib/tools/test/cover_SUITE.erl b/lib/tools/test/cover_SUITE.erl
index 161b0105b9..ee58fd7a10 100644
--- a/lib/tools/test/cover_SUITE.erl
+++ b/lib/tools/test/cover_SUITE.erl
@@ -24,7 +24,8 @@
-include_lib("common_test/include/ct.hrl").
suite() ->
- [{ct_hooks,[ts_install_cth]}].
+ [{ct_hooks,[ts_install_cth]},
+ {timetrap,{minutes,5}}].
all() ->
NoStartStop = [eif,otp_5305,otp_5418,otp_7095,otp_8273,
@@ -35,7 +36,8 @@ all() ->
distribution, reconnect, die_and_reconnect,
dont_reconnect_after_stop, stop_node_after_disconnect,
export_import, otp_5031, otp_6115,
- otp_8270, otp_10979_hanging_node, otp_14817],
+ otp_8270, otp_10979_hanging_node, otp_14817,
+ local_only],
case whereis(cover_server) of
undefined ->
[coverage,StartStop ++ NoStartStop];
@@ -1742,6 +1744,40 @@ otp_13289(Config) ->
ok = file:delete(File),
ok.
+local_only(Config) ->
+ ok = file:set_cwd(proplists:get_value(data_dir, Config)),
+
+ %% Trying restricting to local nodes too late.
+ cover:start(),
+ {ok,a} = cover:compile(a),
+ [a] = cover:modules(),
+ {error,too_late} = cover:local_only(),
+ cover:stop(),
+
+ %% Now test local only mode.
+ cover:start(),
+ ok = cover:local_only(),
+ [] = cover:modules(),
+ {ok,a} = cover:compile(a),
+ [a] = cover:modules(),
+ done = a:start(5),
+ {ok, {a,{17,2}}} = cover:analyse(a, coverage, module),
+ {ok, [{{a,exit_kalle,0},{1,0}},
+ {{a,loop,3},{5,1}},
+ {{a,pong,1},{1,0}},
+ {{a,start,1},{6,0}},
+ {{a,stop,1},{0,1}},
+ {{a,trycatch,1},{4,0}}]} =
+ cover:analyse(a, coverage, function),
+
+ %% Make sure that it is not possible to run cover on
+ %% slave nodes.
+ {ok,Name} = test_server:start_node(?FUNCTION_NAME, slave, []),
+ {error,local_only} = cover:start([Name]),
+ test_server:stop_node(Name),
+
+ ok.
+
%%--Auxiliary------------------------------------------------------------
analyse_expr(Expr, Config) ->
diff --git a/lib/wx/api_gen/wx_extra/added_func.h b/lib/wx/api_gen/wx_extra/added_func.h
index bffe391140..28fecbf454 100644
--- a/lib/wx/api_gen/wx_extra/added_func.h
+++ b/lib/wx/api_gen/wx_extra/added_func.h
@@ -44,3 +44,9 @@ class wxWindowGTK {
public:
double GetContentScaleFactor();
};
+
+class wxDisplay {
+ public:
+ // get the resolution of this monitor in pixels per inch
+ wxSize GetPPI() const;
+};
diff --git a/lib/wx/api_gen/wx_gen.erl b/lib/wx/api_gen/wx_gen.erl
index cec6ac9ccf..8a00498319 100644
--- a/lib/wx/api_gen/wx_gen.erl
+++ b/lib/wx/api_gen/wx_gen.erl
@@ -701,8 +701,13 @@ parse_type2(["wxe_cb"|R],Info,Opts, T) ->
parse_type2(R,Info,Opts,T#type{name=int,base=wxe_cb});
parse_type2([const|R],Info,Opts,T=#type{mod=Mod}) ->
parse_type2(R,Info,Opts,T#type{mod=[const|Mod]});
-parse_type2(["unsigned"|R],Info,Opts,T=#type{mod=Mod}) ->
- parse_type2(R,Info,Opts,T#type{mod=[unsigned|Mod]});
+parse_type2(["unsigned"|R],Info,Opts,T=#type{mod=Mod}) ->
+ case T#type.base of
+ undefined ->
+ parse_type2(R,Info,Opts,T#type{name=int, base=int, mod=[unsigned|Mod]});
+ _ ->
+ parse_type2(R,Info,Opts,T#type{mod=[unsigned|Mod]})
+ end;
parse_type2(["int"|R],Info,Opts, T) ->
parse_type2(R,Info,Opts,T#type{name=int,base=int});
parse_type2(["wxByte"|R],Info,Opts, T) ->
diff --git a/lib/wx/api_gen/wx_gen_cpp.erl b/lib/wx/api_gen/wx_gen_cpp.erl
index f13d5873a0..c6f2534380 100644
--- a/lib/wx/api_gen/wx_gen_cpp.erl
+++ b/lib/wx/api_gen/wx_gen_cpp.erl
@@ -1165,6 +1165,7 @@ gen_macros() ->
w("#include <wx/fontdlg.h>~n"),
w("#include <wx/progdlg.h>~n"),
w("#include <wx/printdlg.h>~n"),
+ w("#include <wx/display.h>~n"),
w("#include <wx/dcbuffer.h>~n"),
w("#include <wx/dcmirror.h>~n"),
w("#include <wx/glcanvas.h>~n"),
@@ -1176,6 +1177,7 @@ gen_macros() ->
w("#include <wx/sashwin.h>~n"),
w("#include <wx/laywin.h>~n"),
w("#include <wx/graphics.h>~n"),
+ w("#include <wx/dcgraph.h>~n"),
w("#include <wx/aui/aui.h>~n"),
w("#include <wx/datectrl.h>~n"),
w("#include <wx/filepicker.h>~n"),
@@ -1330,8 +1332,10 @@ encode_events(Evs) ->
w(" } else {~n"),
w(" send_res = rt.send();~n"),
w(" if(cb->skip) event->Skip();~n"),
- #class{id=MouseId} = lists:keyfind("wxMouseEvent", #class.name, Evs),
- w(" if(app->recurse_level < 1 && Etype->cID != ~p) {~n", [MouseId]),
+ #class{id=SizeId} = lists:keyfind("wxSizeEvent", #class.name, Evs),
+ #class{id=MoveId} = lists:keyfind("wxMoveEvent", #class.name, Evs),
+ w(" if(app->recurse_level < 1 && (Etype->cID == ~w || Etype->cID == ~w)) {~n",
+ [SizeId, MoveId]),
w(" app->recurse_level++;~n"),
w(" app->dispatch_cmds();~n"),
w(" app->recurse_level--;~n"),
diff --git a/lib/wx/api_gen/wxapi.conf b/lib/wx/api_gen/wxapi.conf
index c1b55b6875..9707fedf67 100644
--- a/lib/wx/api_gen/wxapi.conf
+++ b/lib/wx/api_gen/wxapi.conf
@@ -27,7 +27,7 @@
{not_const, [wxHAS_INT64,wxBYTE_ORDER,wxRETAINED,
wxFONTENCODING_UTF32,wxFONTENCODING_UTF16,
wxDEFAULT_CONTROL_BORDER,wxMOD_CMD,
- wxMAJOR_VERSION, wxMINOR_VERSION,
+ wxMAJOR_VERSION, wxMINOR_VERSION,
wxRELEASE_NUMBER,wxSUBRELEASE_NUMBER,wxBETA_NUMBER,
%%
wxALWAYS_NATIVE_DOUBLE_BUFFER,
@@ -37,16 +37,30 @@
wxCURSOR_DEFAULT,
wxCURSOR_ARROWWAIT,
wxCURSOR_MAX,
- wxLanguage
+ wxLanguage,
+ wxFONTWEIGHT_NORMAL,
+ wxFONTWEIGHT_LIGHT,
+ wxFONTWEIGHT_BOLD,
+ wxFONTWEIGHT_MAX
]}.
-{gvars,
+{gvars,
[
{wxITALIC_FONT, wxFont},
{wxNORMAL_FONT, wxFont},
{wxSMALL_FONT, wxFont},
{wxSWISS_FONT, wxFont},
-
+
+ %% Added (enum) values in 3.1.2
+ {wxFONTWEIGHT_INVALID, {test_if, "wxCHECK_VERSION(3,1,2)"}},
+ {wxFONTWEIGHT_THIN, {test_if, "wxCHECK_VERSION(3,1,2)"}},
+ {wxFONTWEIGHT_EXTRALIGHT, {test_if, "wxCHECK_VERSION(3,1,2)"}},
+ {wxFONTWEIGHT_MEDIUM, {test_if, "wxCHECK_VERSION(3,1,2)"}},
+ {wxFONTWEIGHT_SEMIBOLD, {test_if, "wxCHECK_VERSION(3,1,2)"}},
+ {wxFONTWEIGHT_EXTRABOLD, {test_if, "wxCHECK_VERSION(3,1,2)"}},
+ {wxFONTWEIGHT_HEAVY, {test_if, "wxCHECK_VERSION(3,1,2)"}},
+ {wxFONTWEIGHT_EXTRAHEAVY, {test_if, "wxCHECK_VERSION(3,1,2)"}},
+
{wxBLACK_DASHED_PEN, wxPen},
{wxBLACK_PEN, wxPen},
{wxCYAN_PEN, wxPen},
@@ -2016,3 +2030,17 @@
['GetPosition', 'GetNumberOfFiles',
{'GetFiles', [{return, [{single, {list, 'm_noFiles'}}]}]}
]}.
+
+
+{class, wxDisplay, root, [{ifdef, wxUSE_DISPLAY}],
+ ['wxDisplay', '~wxDisplay',
+ 'IsOk',
+ {'GetClientArea', [{test_if, "wxCHECK_VERSION(2,8,12)"}]},
+ 'GetGeometry', 'GetName', 'IsPrimary',
+ 'GetCount', 'GetFromPoint', 'GetFromWindow',
+ {'GetPPI', [{test_if, "wxCHECK_VERSION(3,1,2)"}]}
+ ]}.
+
+{class, wxGCDC, wxDC, [{ifdef, wxUSE_GRAPHICS_CONTEXT}],
+ ['wxGCDC', '~wxGCDC', 'GetGraphicsContext', 'SetGraphicsContext'
+ ]}.
diff --git a/lib/wx/c_src/gen/wxe_derived_dest.h b/lib/wx/c_src/gen/wxe_derived_dest.h
index fc0ae0d9fc..a7114eb188 100644
--- a/lib/wx/c_src/gen/wxe_derived_dest.h
+++ b/lib/wx/c_src/gen/wxe_derived_dest.h
@@ -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.
@@ -799,3 +799,11 @@ class EwxDCOverlay : public wxDCOverlay {
EwxDCOverlay(wxOverlay& overlay,wxWindowDC * dc) : wxDCOverlay(overlay,dc) {};
};
+#if wxUSE_GRAPHICS_CONTEXT
+class EwxGCDC : public wxGCDC {
+ public: ~EwxGCDC() {((WxeApp *)wxTheApp)->clearPtr(this);};
+ EwxGCDC(const wxWindowDC& dc) : wxGCDC(dc) {};
+ EwxGCDC() : wxGCDC() {};
+};
+#endif // wxUSE_GRAPHICS_CONTEXT
+
diff --git a/lib/wx/c_src/gen/wxe_events.cpp b/lib/wx/c_src/gen/wxe_events.cpp
index 01787c8a64..8c3283a670 100644
--- a/lib/wx/c_src/gen/wxe_events.cpp
+++ b/lib/wx/c_src/gen/wxe_events.cpp
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2008-2016. All Rights Reserved.
+ * Copyright Ericsson AB 2008-2019. 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.
@@ -910,7 +910,7 @@ case 238: {// wxDropFilesEvent
} else {
send_res = rt.send();
if(cb->skip) event->Skip();
- if(app->recurse_level < 1 && Etype->cID != 168) {
+ if(app->recurse_level < 1 && (Etype->cID == 171 || Etype->cID == 172)) {
app->recurse_level++;
app->dispatch_cmds();
app->recurse_level--;
diff --git a/lib/wx/c_src/gen/wxe_funcs.cpp b/lib/wx/c_src/gen/wxe_funcs.cpp
index 74961b2e5e..32e4bf855b 100644
--- a/lib/wx/c_src/gen/wxe_funcs.cpp
+++ b/lib/wx/c_src/gen/wxe_funcs.cpp
@@ -32113,6 +32113,120 @@ case wxDropFilesEvent_GetFiles: { // wxDropFilesEvent::GetFiles
rt.add(tmpArrayStr);
break;
}
+#if wxUSE_DISPLAY
+case wxDisplay_new: { // wxDisplay::wxDisplay
+ int n=0;
+ while( * (int*) bp) { switch (* (int*) bp) {
+ case 1: {bp += 4;
+ n = (int)*(unsigned int *) bp; bp += 4;
+ } break;
+ }};
+ wxDisplay * Result = new wxDisplay(n);
+ newPtr((void *) Result, 239, memenv);
+ rt.addRef(getRef((void *)Result,memenv), "wxDisplay");
+ break;
+}
+case wxDisplay_destruct: { // wxDisplay::~wxDisplay
+ wxDisplay *This = (wxDisplay *) getPtr(bp,memenv); bp += 4;
+ if(This) { ((WxeApp *) wxTheApp)->clearPtr((void *) This);
+ delete This;}
+ break;
+}
+case wxDisplay_IsOk: { // wxDisplay::IsOk
+ wxDisplay *This = (wxDisplay *) getPtr(bp,memenv); bp += 4;
+ if(!This) throw wxe_badarg(0);
+ bool Result = This->IsOk();
+ rt.addBool(Result);
+ break;
+}
+#if wxCHECK_VERSION(2,8,12)
+case wxDisplay_GetClientArea: { // wxDisplay::GetClientArea
+ wxDisplay *This = (wxDisplay *) getPtr(bp,memenv); bp += 4;
+ if(!This) throw wxe_badarg(0);
+ wxRect Result = This->GetClientArea();
+ rt.add(Result);
+ break;
+}
+#endif
+case wxDisplay_GetGeometry: { // wxDisplay::GetGeometry
+ wxDisplay *This = (wxDisplay *) getPtr(bp,memenv); bp += 4;
+ if(!This) throw wxe_badarg(0);
+ wxRect Result = This->GetGeometry();
+ rt.add(Result);
+ break;
+}
+case wxDisplay_GetName: { // wxDisplay::GetName
+ wxDisplay *This = (wxDisplay *) getPtr(bp,memenv); bp += 4;
+ if(!This) throw wxe_badarg(0);
+ wxString Result = This->GetName();
+ rt.add(Result);
+ break;
+}
+case wxDisplay_IsPrimary: { // wxDisplay::IsPrimary
+ wxDisplay *This = (wxDisplay *) getPtr(bp,memenv); bp += 4;
+ if(!This) throw wxe_badarg(0);
+ bool Result = This->IsPrimary();
+ rt.addBool(Result);
+ break;
+}
+case wxDisplay_GetCount: { // wxDisplay::GetCount
+ int Result = wxDisplay::GetCount();
+ rt.addUint(Result);
+ break;
+}
+case wxDisplay_GetFromPoint: { // wxDisplay::GetFromPoint
+ int * ptX = (int *) bp; bp += 4;
+ int * ptY = (int *) bp; bp += 4;
+ wxPoint pt = wxPoint(*ptX,*ptY);
+ int Result = wxDisplay::GetFromPoint(pt);
+ rt.addInt(Result);
+ break;
+}
+case wxDisplay_GetFromWindow: { // wxDisplay::GetFromWindow
+ wxWindow *window = (wxWindow *) getPtr(bp,memenv); bp += 4;
+ int Result = wxDisplay::GetFromWindow(window);
+ rt.addInt(Result);
+ break;
+}
+#if wxCHECK_VERSION(3,1,2)
+case wxDisplay_GetPPI: { // wxDisplay::GetPPI
+ wxDisplay *This = (wxDisplay *) getPtr(bp,memenv); bp += 4;
+ if(!This) throw wxe_badarg(0);
+ wxSize Result = This->GetPPI();
+ rt.add(Result);
+ break;
+}
+#endif
+#endif // wxUSE_DISPLAY
+#if wxUSE_GRAPHICS_CONTEXT
+case wxGCDC_new_1: { // wxGCDC::wxGCDC
+ wxWindowDC *dc = (wxWindowDC *) getPtr(bp,memenv); bp += 4;
+ wxGCDC * Result = new EwxGCDC(*dc);
+ newPtr((void *) Result, 8, memenv);
+ rt.addRef(getRef((void *)Result,memenv), "wxGCDC");
+ break;
+}
+case wxGCDC_new_0: { // wxGCDC::wxGCDC
+ wxGCDC * Result = new EwxGCDC();
+ newPtr((void *) Result, 8, memenv);
+ rt.addRef(getRef((void *)Result,memenv), "wxGCDC");
+ break;
+}
+case wxGCDC_GetGraphicsContext: { // wxGCDC::GetGraphicsContext
+ wxGCDC *This = (wxGCDC *) getPtr(bp,memenv); bp += 4;
+ if(!This) throw wxe_badarg(0);
+ wxGraphicsContext * Result = (wxGraphicsContext*)This->GetGraphicsContext();
+ rt.addRef(getRef((void *)Result,memenv,8), "wxGraphicsContext");
+ break;
+}
+case wxGCDC_SetGraphicsContext: { // wxGCDC::SetGraphicsContext
+ wxGCDC *This = (wxGCDC *) getPtr(bp,memenv); bp += 4;
+ wxGraphicsContext *ctx = (wxGraphicsContext *) getPtr(bp,memenv); bp += 4;
+ if(!This) throw wxe_badarg(0);
+ This->SetGraphicsContext(ctx);
+ break;
+}
+#endif // wxUSE_GRAPHICS_CONTEXT
default: {
wxeReturn error = wxeReturn(WXE_DRV_PORT, Ecmd.caller, false); error.addAtom("_wxe_error_");
error.addInt((int) op);
@@ -32174,6 +32288,7 @@ bool WxeApp::delete_object(void *ptr, wxeRefData *refd) {
case 231: delete (EwxLocale *) ptr; return false;
case 236: delete (wxOverlay *) ptr; break;
case 237: delete (EwxDCOverlay *) ptr; return false;
+ case 239: delete (wxDisplay *) ptr; break;
default: delete (wxObject *) ptr; return false;
}
return true;
diff --git a/lib/wx/c_src/gen/wxe_init.cpp b/lib/wx/c_src/gen/wxe_init.cpp
index 6ce33a5449..5a52d69003 100644
--- a/lib/wx/c_src/gen/wxe_init.cpp
+++ b/lib/wx/c_src/gen/wxe_init.cpp
@@ -55,6 +55,14 @@ void WxeApp::init_nonconsts(wxeMemEnv *memenv, ErlDrvTermData caller) {
rt.addTupleCount(2);
rt.addAtom("wxFONTENCODING_UTF32"); rt.addInt(wxFONTENCODING_UTF32);
rt.addTupleCount(2);
+ rt.addAtom("wxFONTWEIGHT_BOLD"); rt.addInt(wxFONTWEIGHT_BOLD);
+ rt.addTupleCount(2);
+ rt.addAtom("wxFONTWEIGHT_LIGHT"); rt.addInt(wxFONTWEIGHT_LIGHT);
+ rt.addTupleCount(2);
+ rt.addAtom("wxFONTWEIGHT_MAX"); rt.addInt(wxFONTWEIGHT_MAX);
+ rt.addTupleCount(2);
+ rt.addAtom("wxFONTWEIGHT_NORMAL"); rt.addInt(wxFONTWEIGHT_NORMAL);
+ rt.addTupleCount(2);
rt.addAtom("wxMOD_CMD"); rt.addInt(wxMOD_CMD);
rt.addTupleCount(2);
rt.addAtom("wxLANGUAGE_ABKHAZIAN"); rt.addInt(wxLANGUAGE_ABKHAZIAN);
@@ -654,6 +662,62 @@ void WxeApp::init_nonconsts(wxeMemEnv *memenv, ErlDrvTermData caller) {
rt.addTupleCount(2);
rt.addAtom("wxCYAN_PEN"); rt.addRef(getRef((void *)wxCYAN_PEN,memenv),"wxPen");
rt.addTupleCount(2);
+#if wxCHECK_VERSION(3,1,2)
+ rt.addAtom("wxFONTWEIGHT_EXTRABOLD"); rt.addInt(wxFONTWEIGHT_EXTRABOLD);
+ rt.addTupleCount(2);
+#else
+ rt.addAtom("wxFONTWEIGHT_EXTRABOLD"); rt.addAtom("undefined");
+ rt.addTupleCount(2);
+#endif
+#if wxCHECK_VERSION(3,1,2)
+ rt.addAtom("wxFONTWEIGHT_EXTRAHEAVY"); rt.addInt(wxFONTWEIGHT_EXTRAHEAVY);
+ rt.addTupleCount(2);
+#else
+ rt.addAtom("wxFONTWEIGHT_EXTRAHEAVY"); rt.addAtom("undefined");
+ rt.addTupleCount(2);
+#endif
+#if wxCHECK_VERSION(3,1,2)
+ rt.addAtom("wxFONTWEIGHT_EXTRALIGHT"); rt.addInt(wxFONTWEIGHT_EXTRALIGHT);
+ rt.addTupleCount(2);
+#else
+ rt.addAtom("wxFONTWEIGHT_EXTRALIGHT"); rt.addAtom("undefined");
+ rt.addTupleCount(2);
+#endif
+#if wxCHECK_VERSION(3,1,2)
+ rt.addAtom("wxFONTWEIGHT_HEAVY"); rt.addInt(wxFONTWEIGHT_HEAVY);
+ rt.addTupleCount(2);
+#else
+ rt.addAtom("wxFONTWEIGHT_HEAVY"); rt.addAtom("undefined");
+ rt.addTupleCount(2);
+#endif
+#if wxCHECK_VERSION(3,1,2)
+ rt.addAtom("wxFONTWEIGHT_INVALID"); rt.addInt(wxFONTWEIGHT_INVALID);
+ rt.addTupleCount(2);
+#else
+ rt.addAtom("wxFONTWEIGHT_INVALID"); rt.addAtom("undefined");
+ rt.addTupleCount(2);
+#endif
+#if wxCHECK_VERSION(3,1,2)
+ rt.addAtom("wxFONTWEIGHT_MEDIUM"); rt.addInt(wxFONTWEIGHT_MEDIUM);
+ rt.addTupleCount(2);
+#else
+ rt.addAtom("wxFONTWEIGHT_MEDIUM"); rt.addAtom("undefined");
+ rt.addTupleCount(2);
+#endif
+#if wxCHECK_VERSION(3,1,2)
+ rt.addAtom("wxFONTWEIGHT_SEMIBOLD"); rt.addInt(wxFONTWEIGHT_SEMIBOLD);
+ rt.addTupleCount(2);
+#else
+ rt.addAtom("wxFONTWEIGHT_SEMIBOLD"); rt.addAtom("undefined");
+ rt.addTupleCount(2);
+#endif
+#if wxCHECK_VERSION(3,1,2)
+ rt.addAtom("wxFONTWEIGHT_THIN"); rt.addInt(wxFONTWEIGHT_THIN);
+ rt.addTupleCount(2);
+#else
+ rt.addAtom("wxFONTWEIGHT_THIN"); rt.addAtom("undefined");
+ rt.addTupleCount(2);
+#endif
rt.addAtom("wxGREEN"); rt.add(*(wxGREEN));
rt.addTupleCount(2);
rt.addAtom("wxGREEN_BRUSH"); rt.addRef(getRef((void *)wxGREEN_BRUSH,memenv),"wxBrush");
@@ -723,7 +787,7 @@ void WxeApp::init_nonconsts(wxeMemEnv *memenv, ErlDrvTermData caller) {
rt.addAtom("wx_GL_COMPAT_PROFILE"); rt.addAtom("undefined");
rt.addTupleCount(2);
#endif
- rt.endList(309);
+ rt.endList(321);
rt.addTupleCount(2);
rt.send();
}
diff --git a/lib/wx/c_src/gen/wxe_macros.h b/lib/wx/c_src/gen/wxe_macros.h
index 4c8e52def2..c23e8a83bd 100644
--- a/lib/wx/c_src/gen/wxe_macros.h
+++ b/lib/wx/c_src/gen/wxe_macros.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2008-2017. 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.
@@ -35,6 +35,7 @@
#include <wx/fontdlg.h>
#include <wx/progdlg.h>
#include <wx/printdlg.h>
+#include <wx/display.h>
#include <wx/dcbuffer.h>
#include <wx/dcmirror.h>
#include <wx/glcanvas.h>
@@ -46,6 +47,7 @@
#include <wx/sashwin.h>
#include <wx/laywin.h>
#include <wx/graphics.h>
+#include <wx/dcgraph.h>
#include <wx/aui/aui.h>
#include <wx/datectrl.h>
#include <wx/filepicker.h>
@@ -3426,5 +3428,21 @@
#define wxDropFilesEvent_GetPosition 3597
#define wxDropFilesEvent_GetNumberOfFiles 3598
#define wxDropFilesEvent_GetFiles 3599
+#define wxDisplay_new 3600
+#define wxDisplay_destruct 3601
+#define wxDisplay_IsOk 3602
+#define wxDisplay_GetClientArea 3603
+#define wxDisplay_GetGeometry 3604
+#define wxDisplay_GetName 3605
+#define wxDisplay_IsPrimary 3606
+#define wxDisplay_GetCount 3607
+#define wxDisplay_GetFromPoint 3608
+#define wxDisplay_GetFromWindow 3609
+#define wxDisplay_GetPPI 3610
+#define wxGCDC_new_1 3611
+#define wxGCDC_new_0 3612
+#define wxGCDC_destruct 3613
+#define wxGCDC_GetGraphicsContext 3614
+#define wxGCDC_SetGraphicsContext 3615
diff --git a/lib/wx/c_src/wxe_impl.cpp b/lib/wx/c_src/wxe_impl.cpp
index bd22502d00..43b5476073 100644
--- a/lib/wx/c_src/wxe_impl.cpp
+++ b/lib/wx/c_src/wxe_impl.cpp
@@ -267,7 +267,7 @@ int WxeApp::dispatch_cmds()
return more;
}
-#define BREAK_BATCH 10000
+#define CHECK_EVENTS 10000
int WxeApp::dispatch(wxeFifo * batch)
{
@@ -278,13 +278,14 @@ int WxeApp::dispatch(wxeFifo * batch)
erl_drv_mutex_lock(wxe_batch_locker_m);
while(true) {
while((event = batch->Get()) != NULL) {
+ wait += 1;
erl_drv_mutex_unlock(wxe_batch_locker_m);
switch(event->op) {
case WXE_BATCH_END:
if(blevel>0) {
blevel--;
if(blevel==0)
- wait += BREAK_BATCH/4;
+ wait += CHECK_EVENTS/4;
}
break;
case WXE_BATCH_BEGIN:
@@ -314,21 +315,18 @@ int WxeApp::dispatch(wxeFifo * batch)
break;
}
event->Delete();
+ if(wait > CHECK_EVENTS)
+ return 1; // Let wx check for events
erl_drv_mutex_lock(wxe_batch_locker_m);
batch->Cleanup();
}
- if(blevel <= 0 || wait >= BREAK_BATCH) {
+ if(blevel <= 0) {
erl_drv_mutex_unlock(wxe_batch_locker_m);
- if(blevel > 0) {
- return 1; // We are still in a batch but we can let wx check for events
- } else {
- return 0;
- }
+ return 0;
}
// sleep until something happens
// fprintf(stderr, "%s:%d sleep %d %d %d\r\n", __FILE__, __LINE__, batch->m_n, blevel, wait);fflush(stderr);
wxe_needs_signal = 1;
- wait += 1;
while(batch->m_n == 0) {
erl_drv_cond_wait(wxe_batch_locker_c, wxe_batch_locker_m);
}
diff --git a/lib/wx/include/wx.hrl b/lib/wx/include/wx.hrl
index 23f3b95403..2c145595ee 100644
--- a/lib/wx/include/wx.hrl
+++ b/lib/wx/include/wx.hrl
@@ -398,6 +398,14 @@
-define(wxCYAN, wxe_util:get_const(wxCYAN)).
-define(wxCYAN_BRUSH, wxe_util:get_const(wxCYAN_BRUSH)).
-define(wxCYAN_PEN, wxe_util:get_const(wxCYAN_PEN)).
+-define(wxFONTWEIGHT_EXTRABOLD, wxe_util:get_const(wxFONTWEIGHT_EXTRABOLD)).
+-define(wxFONTWEIGHT_EXTRAHEAVY, wxe_util:get_const(wxFONTWEIGHT_EXTRAHEAVY)).
+-define(wxFONTWEIGHT_EXTRALIGHT, wxe_util:get_const(wxFONTWEIGHT_EXTRALIGHT)).
+-define(wxFONTWEIGHT_HEAVY, wxe_util:get_const(wxFONTWEIGHT_HEAVY)).
+-define(wxFONTWEIGHT_INVALID, wxe_util:get_const(wxFONTWEIGHT_INVALID)).
+-define(wxFONTWEIGHT_MEDIUM, wxe_util:get_const(wxFONTWEIGHT_MEDIUM)).
+-define(wxFONTWEIGHT_SEMIBOLD, wxe_util:get_const(wxFONTWEIGHT_SEMIBOLD)).
+-define(wxFONTWEIGHT_THIN, wxe_util:get_const(wxFONTWEIGHT_THIN)).
-define(wxGREEN, wxe_util:get_const(wxGREEN)).
-define(wxGREEN_BRUSH, wxe_util:get_const(wxGREEN_BRUSH)).
-define(wxGREEN_PEN, wxe_util:get_const(wxGREEN_PEN)).
@@ -1685,10 +1693,10 @@
-define(wxFONTSTYLE_SLANT, ?wxSLANT).
-define(wxFONTSTYLE_MAX, (?wxSLANT+1)).
% From "font.h": wxFontWeight
--define(wxFONTWEIGHT_NORMAL, ?wxNORMAL).
--define(wxFONTWEIGHT_LIGHT, ?wxLIGHT).
--define(wxFONTWEIGHT_BOLD, ?wxBOLD).
--define(wxFONTWEIGHT_MAX, (?wxBOLD+1)).
+-define(wxFONTWEIGHT_NORMAL, wxe_util:get_const(wxFONTWEIGHT_NORMAL)).
+-define(wxFONTWEIGHT_LIGHT, wxe_util:get_const(wxFONTWEIGHT_LIGHT)).
+-define(wxFONTWEIGHT_BOLD, wxe_util:get_const(wxFONTWEIGHT_BOLD)).
+-define(wxFONTWEIGHT_MAX, wxe_util:get_const(wxFONTWEIGHT_MAX)).
% From "fontenc.h": wxFontEncoding
-define(wxFONTENCODING_SYSTEM, -1).
-define(wxFONTENCODING_DEFAULT, 0).
diff --git a/lib/wx/src/gen/wxDisplay.erl b/lib/wx/src/gen/wxDisplay.erl
new file mode 100644
index 0000000000..b6a2bf22ac
--- /dev/null
+++ b/lib/wx/src/gen/wxDisplay.erl
@@ -0,0 +1,131 @@
+%%
+%% %CopyrightBegin%
+%%
+%% 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.
+%% 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%
+%% This file is generated DO NOT EDIT
+
+%% @doc See external documentation: <a href="http://www.wxwidgets.org/manuals/2.8.12/wx_wxdisplay.html">wxDisplay</a>.
+%% @type wxDisplay(). An object reference, The representation is internal
+%% and can be changed without notice. It can't be used for comparsion
+%% stored on disc or distributed for use on other nodes.
+
+-module(wxDisplay).
+-include("wxe.hrl").
+-export([destroy/1,getClientArea/1,getCount/0,getFromPoint/1,getFromWindow/1,
+ getGeometry/1,getName/1,getPPI/1,isOk/1,isPrimary/1,new/0,new/1]).
+
+%% inherited exports
+-export([parent_class/1]).
+
+-export_type([wxDisplay/0]).
+%% @hidden
+parent_class(_Class) -> erlang:error({badtype, ?MODULE}).
+
+-type wxDisplay() :: wx:wx_object().
+%% @equiv new([])
+-spec new() -> wxDisplay().
+
+new() ->
+ new([]).
+
+%% @doc See <a href="http://www.wxwidgets.org/manuals/2.8.12/wx_wxdisplay.html#wxdisplaywxdisplay">external documentation</a>.
+-spec new([Option]) -> wxDisplay() when
+ Option :: {'n', integer()}.
+new(Options)
+ when is_list(Options) ->
+ MOpts = fun({n, N}, Acc) -> [<<1:32/?UI,N:32/?UI>>|Acc];
+ (BadOpt, _) -> erlang:error({badoption, BadOpt}) end,
+ BinOpt = list_to_binary(lists:foldl(MOpts, [<<0:32>>], Options)),
+ wxe_util:construct(?wxDisplay_new,
+ <<BinOpt/binary>>).
+
+%% @doc See <a href="http://www.wxwidgets.org/manuals/2.8.12/wx_wxdisplay.html#wxdisplayisok">external documentation</a>.
+-spec isOk(This) -> boolean() when
+ This::wxDisplay().
+isOk(#wx_ref{type=ThisT,ref=ThisRef}) ->
+ ?CLASS(ThisT,wxDisplay),
+ wxe_util:call(?wxDisplay_IsOk,
+ <<ThisRef:32/?UI>>).
+
+%% @doc See <a href="http://www.wxwidgets.org/manuals/2.8.12/wx_wxdisplay.html#wxdisplaygetclientarea">external documentation</a>.
+-spec getClientArea(This) -> {X::integer(), Y::integer(), W::integer(), H::integer()} when
+ This::wxDisplay().
+getClientArea(#wx_ref{type=ThisT,ref=ThisRef}) ->
+ ?CLASS(ThisT,wxDisplay),
+ wxe_util:call(?wxDisplay_GetClientArea,
+ <<ThisRef:32/?UI>>).
+
+%% @doc See <a href="http://www.wxwidgets.org/manuals/2.8.12/wx_wxdisplay.html#wxdisplaygetgeometry">external documentation</a>.
+-spec getGeometry(This) -> {X::integer(), Y::integer(), W::integer(), H::integer()} when
+ This::wxDisplay().
+getGeometry(#wx_ref{type=ThisT,ref=ThisRef}) ->
+ ?CLASS(ThisT,wxDisplay),
+ wxe_util:call(?wxDisplay_GetGeometry,
+ <<ThisRef:32/?UI>>).
+
+%% @doc See <a href="http://www.wxwidgets.org/manuals/2.8.12/wx_wxdisplay.html#wxdisplaygetname">external documentation</a>.
+-spec getName(This) -> unicode:charlist() when
+ This::wxDisplay().
+getName(#wx_ref{type=ThisT,ref=ThisRef}) ->
+ ?CLASS(ThisT,wxDisplay),
+ wxe_util:call(?wxDisplay_GetName,
+ <<ThisRef:32/?UI>>).
+
+%% @doc See <a href="http://www.wxwidgets.org/manuals/2.8.12/wx_wxdisplay.html#wxdisplayisprimary">external documentation</a>.
+-spec isPrimary(This) -> boolean() when
+ This::wxDisplay().
+isPrimary(#wx_ref{type=ThisT,ref=ThisRef}) ->
+ ?CLASS(ThisT,wxDisplay),
+ wxe_util:call(?wxDisplay_IsPrimary,
+ <<ThisRef:32/?UI>>).
+
+%% @doc See <a href="http://www.wxwidgets.org/manuals/2.8.12/wx_wxdisplay.html#wxdisplaygetcount">external documentation</a>.
+-spec getCount() -> integer().
+getCount() ->
+ wxe_util:call(?wxDisplay_GetCount,
+ <<>>).
+
+%% @doc See <a href="http://www.wxwidgets.org/manuals/2.8.12/wx_wxdisplay.html#wxdisplaygetfrompoint">external documentation</a>.
+-spec getFromPoint(Pt) -> integer() when
+ Pt::{X::integer(), Y::integer()}.
+getFromPoint({PtX,PtY})
+ when is_integer(PtX),is_integer(PtY) ->
+ wxe_util:call(?wxDisplay_GetFromPoint,
+ <<PtX:32/?UI,PtY:32/?UI>>).
+
+%% @doc See <a href="http://www.wxwidgets.org/manuals/2.8.12/wx_wxdisplay.html#wxdisplaygetfromwindow">external documentation</a>.
+-spec getFromWindow(Window) -> integer() when
+ Window::wxWindow:wxWindow().
+getFromWindow(#wx_ref{type=WindowT,ref=WindowRef}) ->
+ ?CLASS(WindowT,wxWindow),
+ wxe_util:call(?wxDisplay_GetFromWindow,
+ <<WindowRef:32/?UI>>).
+
+%% @doc See <a href="http://www.wxwidgets.org/manuals/2.8.12/wx_wxdisplay.html#wxdisplaygetppi">external documentation</a>.
+-spec getPPI(This) -> {W::integer(), H::integer()} when
+ This::wxDisplay().
+getPPI(#wx_ref{type=ThisT,ref=ThisRef}) ->
+ ?CLASS(ThisT,wxDisplay),
+ wxe_util:call(?wxDisplay_GetPPI,
+ <<ThisRef:32/?UI>>).
+
+%% @doc Destroys this object, do not use object again
+-spec destroy(This::wxDisplay()) -> 'ok'.
+destroy(Obj=#wx_ref{type=Type}) ->
+ ?CLASS(Type,wxDisplay),
+ wxe_util:destroy(?wxDisplay_destruct,Obj),
+ ok.
diff --git a/lib/wx/src/gen/wxGCDC.erl b/lib/wx/src/gen/wxGCDC.erl
new file mode 100644
index 0000000000..467013b14e
--- /dev/null
+++ b/lib/wx/src/gen/wxGCDC.erl
@@ -0,0 +1,287 @@
+%%
+%% %CopyrightBegin%
+%%
+%% 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.
+%% 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%
+%% This file is generated DO NOT EDIT
+
+%% @doc See external documentation: <a href="http://www.wxwidgets.org/manuals/2.8.12/wx_wxgcdc.html">wxGCDC</a>.
+%% <p>This class is derived (and can use functions) from:
+%% <br />{@link wxDC}
+%% </p>
+%% @type wxGCDC(). An object reference, The representation is internal
+%% and can be changed without notice. It can't be used for comparsion
+%% stored on disc or distributed for use on other nodes.
+
+-module(wxGCDC).
+-include("wxe.hrl").
+-export([destroy/1,getGraphicsContext/1,new/0,new/1,setGraphicsContext/2]).
+
+%% inherited exports
+-export([blit/5,blit/6,calcBoundingBox/3,clear/1,computeScaleAndOrigin/1,crossHair/2,
+ destroyClippingRegion/1,deviceToLogicalX/2,deviceToLogicalXRel/2,
+ deviceToLogicalY/2,deviceToLogicalYRel/2,drawArc/4,drawBitmap/3,drawBitmap/4,
+ drawCheckMark/2,drawCircle/3,drawEllipse/2,drawEllipse/3,drawEllipticArc/5,
+ drawIcon/3,drawLabel/3,drawLabel/4,drawLine/3,drawLines/2,drawLines/3,
+ drawPoint/2,drawPolygon/2,drawPolygon/3,drawRectangle/2,drawRectangle/3,
+ drawRotatedText/4,drawRoundedRectangle/3,drawRoundedRectangle/4,
+ drawText/3,endDoc/1,endPage/1,floodFill/3,floodFill/4,getBackground/1,
+ getBackgroundMode/1,getBrush/1,getCharHeight/1,getCharWidth/1,getClippingBox/1,
+ getFont/1,getLayoutDirection/1,getLogicalFunction/1,getMapMode/1,
+ getMultiLineTextExtent/2,getMultiLineTextExtent/3,getPPI/1,getPartialTextExtents/2,
+ getPen/1,getPixel/2,getSize/1,getSizeMM/1,getTextBackground/1,getTextExtent/2,
+ getTextExtent/3,getTextForeground/1,getUserScale/1,gradientFillConcentric/4,
+ gradientFillConcentric/5,gradientFillLinear/4,gradientFillLinear/5,
+ isOk/1,logicalToDeviceX/2,logicalToDeviceXRel/2,logicalToDeviceY/2,
+ logicalToDeviceYRel/2,maxX/1,maxY/1,minX/1,minY/1,parent_class/1,resetBoundingBox/1,
+ setAxisOrientation/3,setBackground/2,setBackgroundMode/2,setBrush/2,
+ setClippingRegion/2,setClippingRegion/3,setDeviceOrigin/3,setFont/2,
+ setLayoutDirection/2,setLogicalFunction/2,setMapMode/2,setPalette/2,
+ setPen/2,setTextBackground/2,setTextForeground/2,setUserScale/3,startDoc/2,
+ startPage/1]).
+
+-export_type([wxGCDC/0]).
+-compile([{nowarn_deprecated_function, {wxDC,computeScaleAndOrigin,1}}]).
+
+%% @hidden
+parent_class(wxDC) -> true;
+parent_class(_Class) -> erlang:error({badtype, ?MODULE}).
+
+-type wxGCDC() :: wx:wx_object().
+%% @doc See <a href="http://www.wxwidgets.org/manuals/2.8.12/wx_wxgcdc.html#wxgcdcwxgcdc">external documentation</a>.
+-spec new() -> wxGCDC().
+new() ->
+ wxe_util:construct(?wxGCDC_new_0,
+ <<>>).
+
+%% @doc See <a href="http://www.wxwidgets.org/manuals/2.8.12/wx_wxgcdc.html#wxgcdcwxgcdc">external documentation</a>.
+-spec new(Dc) -> wxGCDC() when
+ Dc::wxWindowDC:wxWindowDC().
+new(#wx_ref{type=DcT,ref=DcRef}) ->
+ ?CLASS(DcT,wxWindowDC),
+ wxe_util:construct(?wxGCDC_new_1,
+ <<DcRef:32/?UI>>).
+
+%% @doc See <a href="http://www.wxwidgets.org/manuals/2.8.12/wx_wxgcdc.html#wxgcdcgetgraphicscontext">external documentation</a>.
+-spec getGraphicsContext(This) -> wxGraphicsContext:wxGraphicsContext() when
+ This::wxGCDC().
+getGraphicsContext(#wx_ref{type=ThisT,ref=ThisRef}) ->
+ ?CLASS(ThisT,wxGCDC),
+ wxe_util:call(?wxGCDC_GetGraphicsContext,
+ <<ThisRef:32/?UI>>).
+
+%% @doc See <a href="http://www.wxwidgets.org/manuals/2.8.12/wx_wxgcdc.html#wxgcdcsetgraphicscontext">external documentation</a>.
+-spec setGraphicsContext(This, Ctx) -> 'ok' when
+ This::wxGCDC(), Ctx::wxGraphicsContext:wxGraphicsContext().
+setGraphicsContext(#wx_ref{type=ThisT,ref=ThisRef},#wx_ref{type=CtxT,ref=CtxRef}) ->
+ ?CLASS(ThisT,wxGCDC),
+ ?CLASS(CtxT,wxGraphicsContext),
+ wxe_util:cast(?wxGCDC_SetGraphicsContext,
+ <<ThisRef:32/?UI,CtxRef:32/?UI>>).
+
+%% @doc Destroys this object, do not use object again
+-spec destroy(This::wxGCDC()) -> 'ok'.
+destroy(Obj=#wx_ref{type=Type}) ->
+ ?CLASS(Type,wxGCDC),
+ wxe_util:destroy(?DESTROY_OBJECT,Obj),
+ ok.
+ %% From wxDC
+%% @hidden
+startPage(This) -> wxDC:startPage(This).
+%% @hidden
+startDoc(This,Message) -> wxDC:startDoc(This,Message).
+%% @hidden
+setUserScale(This,X,Y) -> wxDC:setUserScale(This,X,Y).
+%% @hidden
+setTextForeground(This,Colour) -> wxDC:setTextForeground(This,Colour).
+%% @hidden
+setTextBackground(This,Colour) -> wxDC:setTextBackground(This,Colour).
+%% @hidden
+setPen(This,Pen) -> wxDC:setPen(This,Pen).
+%% @hidden
+setPalette(This,Palette) -> wxDC:setPalette(This,Palette).
+%% @hidden
+setMapMode(This,Mode) -> wxDC:setMapMode(This,Mode).
+%% @hidden
+setLogicalFunction(This,Function) -> wxDC:setLogicalFunction(This,Function).
+%% @hidden
+setLayoutDirection(This,Dir) -> wxDC:setLayoutDirection(This,Dir).
+%% @hidden
+setFont(This,Font) -> wxDC:setFont(This,Font).
+%% @hidden
+setDeviceOrigin(This,X,Y) -> wxDC:setDeviceOrigin(This,X,Y).
+%% @hidden
+setClippingRegion(This,Pt,Sz) -> wxDC:setClippingRegion(This,Pt,Sz).
+%% @hidden
+setClippingRegion(This,Region) -> wxDC:setClippingRegion(This,Region).
+%% @hidden
+setBrush(This,Brush) -> wxDC:setBrush(This,Brush).
+%% @hidden
+setBackgroundMode(This,Mode) -> wxDC:setBackgroundMode(This,Mode).
+%% @hidden
+setBackground(This,Brush) -> wxDC:setBackground(This,Brush).
+%% @hidden
+setAxisOrientation(This,XLeftRight,YBottomUp) -> wxDC:setAxisOrientation(This,XLeftRight,YBottomUp).
+%% @hidden
+resetBoundingBox(This) -> wxDC:resetBoundingBox(This).
+%% @hidden
+isOk(This) -> wxDC:isOk(This).
+%% @hidden
+minY(This) -> wxDC:minY(This).
+%% @hidden
+minX(This) -> wxDC:minX(This).
+%% @hidden
+maxY(This) -> wxDC:maxY(This).
+%% @hidden
+maxX(This) -> wxDC:maxX(This).
+%% @hidden
+logicalToDeviceYRel(This,Y) -> wxDC:logicalToDeviceYRel(This,Y).
+%% @hidden
+logicalToDeviceY(This,Y) -> wxDC:logicalToDeviceY(This,Y).
+%% @hidden
+logicalToDeviceXRel(This,X) -> wxDC:logicalToDeviceXRel(This,X).
+%% @hidden
+logicalToDeviceX(This,X) -> wxDC:logicalToDeviceX(This,X).
+%% @hidden
+gradientFillLinear(This,Rect,InitialColour,DestColour, Options) -> wxDC:gradientFillLinear(This,Rect,InitialColour,DestColour, Options).
+%% @hidden
+gradientFillLinear(This,Rect,InitialColour,DestColour) -> wxDC:gradientFillLinear(This,Rect,InitialColour,DestColour).
+%% @hidden
+gradientFillConcentric(This,Rect,InitialColour,DestColour,CircleCenter) -> wxDC:gradientFillConcentric(This,Rect,InitialColour,DestColour,CircleCenter).
+%% @hidden
+gradientFillConcentric(This,Rect,InitialColour,DestColour) -> wxDC:gradientFillConcentric(This,Rect,InitialColour,DestColour).
+%% @hidden
+getUserScale(This) -> wxDC:getUserScale(This).
+%% @hidden
+getTextForeground(This) -> wxDC:getTextForeground(This).
+%% @hidden
+getTextExtent(This,String, Options) -> wxDC:getTextExtent(This,String, Options).
+%% @hidden
+getTextExtent(This,String) -> wxDC:getTextExtent(This,String).
+%% @hidden
+getTextBackground(This) -> wxDC:getTextBackground(This).
+%% @hidden
+getSizeMM(This) -> wxDC:getSizeMM(This).
+%% @hidden
+getSize(This) -> wxDC:getSize(This).
+%% @hidden
+getPPI(This) -> wxDC:getPPI(This).
+%% @hidden
+getPixel(This,Pt) -> wxDC:getPixel(This,Pt).
+%% @hidden
+getPen(This) -> wxDC:getPen(This).
+%% @hidden
+getPartialTextExtents(This,Text) -> wxDC:getPartialTextExtents(This,Text).
+%% @hidden
+getMultiLineTextExtent(This,String, Options) -> wxDC:getMultiLineTextExtent(This,String, Options).
+%% @hidden
+getMultiLineTextExtent(This,String) -> wxDC:getMultiLineTextExtent(This,String).
+%% @hidden
+getMapMode(This) -> wxDC:getMapMode(This).
+%% @hidden
+getLogicalFunction(This) -> wxDC:getLogicalFunction(This).
+%% @hidden
+getLayoutDirection(This) -> wxDC:getLayoutDirection(This).
+%% @hidden
+getFont(This) -> wxDC:getFont(This).
+%% @hidden
+getClippingBox(This) -> wxDC:getClippingBox(This).
+%% @hidden
+getCharWidth(This) -> wxDC:getCharWidth(This).
+%% @hidden
+getCharHeight(This) -> wxDC:getCharHeight(This).
+%% @hidden
+getBrush(This) -> wxDC:getBrush(This).
+%% @hidden
+getBackgroundMode(This) -> wxDC:getBackgroundMode(This).
+%% @hidden
+getBackground(This) -> wxDC:getBackground(This).
+%% @hidden
+floodFill(This,Pt,Col, Options) -> wxDC:floodFill(This,Pt,Col, Options).
+%% @hidden
+floodFill(This,Pt,Col) -> wxDC:floodFill(This,Pt,Col).
+%% @hidden
+endPage(This) -> wxDC:endPage(This).
+%% @hidden
+endDoc(This) -> wxDC:endDoc(This).
+%% @hidden
+drawText(This,Text,Pt) -> wxDC:drawText(This,Text,Pt).
+%% @hidden
+drawRoundedRectangle(This,Pt,Sz,Radius) -> wxDC:drawRoundedRectangle(This,Pt,Sz,Radius).
+%% @hidden
+drawRoundedRectangle(This,R,Radius) -> wxDC:drawRoundedRectangle(This,R,Radius).
+%% @hidden
+drawRotatedText(This,Text,Pt,Angle) -> wxDC:drawRotatedText(This,Text,Pt,Angle).
+%% @hidden
+drawRectangle(This,Pt,Sz) -> wxDC:drawRectangle(This,Pt,Sz).
+%% @hidden
+drawRectangle(This,Rect) -> wxDC:drawRectangle(This,Rect).
+%% @hidden
+drawPoint(This,Pt) -> wxDC:drawPoint(This,Pt).
+%% @hidden
+drawPolygon(This,Points, Options) -> wxDC:drawPolygon(This,Points, Options).
+%% @hidden
+drawPolygon(This,Points) -> wxDC:drawPolygon(This,Points).
+%% @hidden
+drawLines(This,Points, Options) -> wxDC:drawLines(This,Points, Options).
+%% @hidden
+drawLines(This,Points) -> wxDC:drawLines(This,Points).
+%% @hidden
+drawLine(This,Pt1,Pt2) -> wxDC:drawLine(This,Pt1,Pt2).
+%% @hidden
+drawLabel(This,Text,Rect, Options) -> wxDC:drawLabel(This,Text,Rect, Options).
+%% @hidden
+drawLabel(This,Text,Rect) -> wxDC:drawLabel(This,Text,Rect).
+%% @hidden
+drawIcon(This,Icon,Pt) -> wxDC:drawIcon(This,Icon,Pt).
+%% @hidden
+drawEllipticArc(This,Pt,Sz,Sa,Ea) -> wxDC:drawEllipticArc(This,Pt,Sz,Sa,Ea).
+%% @hidden
+drawEllipse(This,Pt,Sz) -> wxDC:drawEllipse(This,Pt,Sz).
+%% @hidden
+drawEllipse(This,Rect) -> wxDC:drawEllipse(This,Rect).
+%% @hidden
+drawCircle(This,Pt,Radius) -> wxDC:drawCircle(This,Pt,Radius).
+%% @hidden
+drawCheckMark(This,Rect) -> wxDC:drawCheckMark(This,Rect).
+%% @hidden
+drawBitmap(This,Bmp,Pt, Options) -> wxDC:drawBitmap(This,Bmp,Pt, Options).
+%% @hidden
+drawBitmap(This,Bmp,Pt) -> wxDC:drawBitmap(This,Bmp,Pt).
+%% @hidden
+drawArc(This,Pt1,Pt2,Centre) -> wxDC:drawArc(This,Pt1,Pt2,Centre).
+%% @hidden
+deviceToLogicalYRel(This,Y) -> wxDC:deviceToLogicalYRel(This,Y).
+%% @hidden
+deviceToLogicalY(This,Y) -> wxDC:deviceToLogicalY(This,Y).
+%% @hidden
+deviceToLogicalXRel(This,X) -> wxDC:deviceToLogicalXRel(This,X).
+%% @hidden
+deviceToLogicalX(This,X) -> wxDC:deviceToLogicalX(This,X).
+%% @hidden
+destroyClippingRegion(This) -> wxDC:destroyClippingRegion(This).
+%% @hidden
+crossHair(This,Pt) -> wxDC:crossHair(This,Pt).
+%% @hidden
+computeScaleAndOrigin(This) -> wxDC:computeScaleAndOrigin(This).
+%% @hidden
+clear(This) -> wxDC:clear(This).
+%% @hidden
+calcBoundingBox(This,X,Y) -> wxDC:calcBoundingBox(This,X,Y).
+%% @hidden
+blit(This,DestPt,Sz,Source,SrcPt, Options) -> wxDC:blit(This,DestPt,Sz,Source,SrcPt, Options).
+%% @hidden
+blit(This,DestPt,Sz,Source,SrcPt) -> wxDC:blit(This,DestPt,Sz,Source,SrcPt).
diff --git a/lib/wx/src/gen/wxe_debug.hrl b/lib/wx/src/gen/wxe_debug.hrl
index 533f9f2df0..b64a1b4c61 100644
--- a/lib/wx/src/gen/wxe_debug.hrl
+++ b/lib/wx/src/gen/wxe_debug.hrl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2008-2017. 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.
@@ -3377,6 +3377,22 @@ wxdebug_table() ->
{3597, {wxDropFilesEvent, getPosition, 0}},
{3598, {wxDropFilesEvent, getNumberOfFiles, 0}},
{3599, {wxDropFilesEvent, getFiles, 0}},
+ {3600, {wxDisplay, new, 1}},
+ {3601, {wxDisplay, destruct, 0}},
+ {3602, {wxDisplay, isOk, 0}},
+ {3603, {wxDisplay, getClientArea, 0}},
+ {3604, {wxDisplay, getGeometry, 0}},
+ {3605, {wxDisplay, getName, 0}},
+ {3606, {wxDisplay, isPrimary, 0}},
+ {3607, {wxDisplay, getCount, 0}},
+ {3608, {wxDisplay, getFromPoint, 1}},
+ {3609, {wxDisplay, getFromWindow, 1}},
+ {3610, {wxDisplay, getPPI, 0}},
+ {3611, {wxGCDC, new_1, 1}},
+ {3612, {wxGCDC, new_0, 0}},
+ {3613, {wxGCDC, destruct, 0}},
+ {3614, {wxGCDC, getGraphicsContext, 0}},
+ {3615, {wxGCDC, setGraphicsContext, 1}},
{-1, {mod, func, -1}}
].
diff --git a/lib/wx/src/gen/wxe_funcs.hrl b/lib/wx/src/gen/wxe_funcs.hrl
index 14b5545676..030f7f117d 100644
--- a/lib/wx/src/gen/wxe_funcs.hrl
+++ b/lib/wx/src/gen/wxe_funcs.hrl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2008-2017. 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.
@@ -3374,3 +3374,19 @@
-define(wxDropFilesEvent_GetPosition, 3597).
-define(wxDropFilesEvent_GetNumberOfFiles, 3598).
-define(wxDropFilesEvent_GetFiles, 3599).
+-define(wxDisplay_new, 3600).
+-define(wxDisplay_destruct, 3601).
+-define(wxDisplay_IsOk, 3602).
+-define(wxDisplay_GetClientArea, 3603).
+-define(wxDisplay_GetGeometry, 3604).
+-define(wxDisplay_GetName, 3605).
+-define(wxDisplay_IsPrimary, 3606).
+-define(wxDisplay_GetCount, 3607).
+-define(wxDisplay_GetFromPoint, 3608).
+-define(wxDisplay_GetFromWindow, 3609).
+-define(wxDisplay_GetPPI, 3610).
+-define(wxGCDC_new_1, 3611).
+-define(wxGCDC_new_0, 3612).
+-define(wxGCDC_destruct, 3613).
+-define(wxGCDC_GetGraphicsContext, 3614).
+-define(wxGCDC_SetGraphicsContext, 3615).
diff --git a/otp_build b/otp_build
index 21d520e101..b1c5bcc939 100755
--- a/otp_build
+++ b/otp_build
@@ -1018,7 +1018,7 @@ do_debuginfo_win32 ()
fi
BINDIR="$ERL_TOP/bin/$TARGET"
EVSN=`grep '^VSN' erts/vsn.mk | sed 's,^VSN.*=[^0-9]*\([0-9].*\)$,@\1,g;s,^[^@].*,,g;s,^@,,g'`
- for f in beam.debug.dll beam.debug.smp.dll beam.pdb beam.smp.pdb erl.pdb werl.pdb erlexec.pdb; do
+ for f in beam.debug.smp.dll beam.smp.pdb erl.pdb werl.pdb erlexec.pdb; do
if [ -f $BINDIR/$f ]; then
rm -f $RELDIR/erts-$EVSN/bin/$f
cp $BINDIR/$f $RELDIR/erts-$EVSN/bin/$f
diff --git a/otp_versions.table b/otp_versions.table
index cd64800534..aea1e8d5cd 100644
--- a/otp_versions.table
+++ b/otp_versions.table
@@ -1,3 +1,5 @@
+OTP-21.2.6 : erts-10.2.4 stdlib-3.7.1 # asn1-5.0.8 common_test-1.16.1 compiler-7.3.1 crypto-4.4 debugger-4.2.6 dialyzer-3.3.1 diameter-2.1.6 edoc-0.9.4 eldap-1.2.6 erl_docgen-0.8.1 erl_interface-3.10.4 et-1.6.4 eunit-2.3.7 ftp-1.0.1 hipe-3.18.2 inets-7.0.5 jinterface-1.9.1 kernel-6.2 megaco-3.18.4 mnesia-4.15.5 observer-2.8.2 odbc-2.12.2 os_mon-2.4.7 otp_mibs-1.2.1 parsetools-2.1.8 public_key-1.6.4 reltool-0.7.8 runtime_tools-1.13.1 sasl-3.3 snmp-5.2.12 ssh-4.7.3 ssl-9.1.2 syntax_tools-2.1.6 tftp-1.0.1 tools-3.0.2 wx-1.8.6 xmerl-1.3.19 :
+OTP-21.2.5 : inets-7.0.5 # asn1-5.0.8 common_test-1.16.1 compiler-7.3.1 crypto-4.4 debugger-4.2.6 dialyzer-3.3.1 diameter-2.1.6 edoc-0.9.4 eldap-1.2.6 erl_docgen-0.8.1 erl_interface-3.10.4 erts-10.2.3 et-1.6.4 eunit-2.3.7 ftp-1.0.1 hipe-3.18.2 jinterface-1.9.1 kernel-6.2 megaco-3.18.4 mnesia-4.15.5 observer-2.8.2 odbc-2.12.2 os_mon-2.4.7 otp_mibs-1.2.1 parsetools-2.1.8 public_key-1.6.4 reltool-0.7.8 runtime_tools-1.13.1 sasl-3.3 snmp-5.2.12 ssh-4.7.3 ssl-9.1.2 stdlib-3.7 syntax_tools-2.1.6 tftp-1.0.1 tools-3.0.2 wx-1.8.6 xmerl-1.3.19 :
OTP-21.2.4 : erts-10.2.3 inets-7.0.4 # asn1-5.0.8 common_test-1.16.1 compiler-7.3.1 crypto-4.4 debugger-4.2.6 dialyzer-3.3.1 diameter-2.1.6 edoc-0.9.4 eldap-1.2.6 erl_docgen-0.8.1 erl_interface-3.10.4 et-1.6.4 eunit-2.3.7 ftp-1.0.1 hipe-3.18.2 jinterface-1.9.1 kernel-6.2 megaco-3.18.4 mnesia-4.15.5 observer-2.8.2 odbc-2.12.2 os_mon-2.4.7 otp_mibs-1.2.1 parsetools-2.1.8 public_key-1.6.4 reltool-0.7.8 runtime_tools-1.13.1 sasl-3.3 snmp-5.2.12 ssh-4.7.3 ssl-9.1.2 stdlib-3.7 syntax_tools-2.1.6 tftp-1.0.1 tools-3.0.2 wx-1.8.6 xmerl-1.3.19 :
OTP-21.2.3 : compiler-7.3.1 erts-10.2.2 ssl-9.1.2 xmerl-1.3.19 # asn1-5.0.8 common_test-1.16.1 crypto-4.4 debugger-4.2.6 dialyzer-3.3.1 diameter-2.1.6 edoc-0.9.4 eldap-1.2.6 erl_docgen-0.8.1 erl_interface-3.10.4 et-1.6.4 eunit-2.3.7 ftp-1.0.1 hipe-3.18.2 inets-7.0.3 jinterface-1.9.1 kernel-6.2 megaco-3.18.4 mnesia-4.15.5 observer-2.8.2 odbc-2.12.2 os_mon-2.4.7 otp_mibs-1.2.1 parsetools-2.1.8 public_key-1.6.4 reltool-0.7.8 runtime_tools-1.13.1 sasl-3.3 snmp-5.2.12 ssh-4.7.3 stdlib-3.7 syntax_tools-2.1.6 tftp-1.0.1 tools-3.0.2 wx-1.8.6 :
OTP-21.2.2 : ssh-4.7.3 # asn1-5.0.8 common_test-1.16.1 compiler-7.3 crypto-4.4 debugger-4.2.6 dialyzer-3.3.1 diameter-2.1.6 edoc-0.9.4 eldap-1.2.6 erl_docgen-0.8.1 erl_interface-3.10.4 erts-10.2.1 et-1.6.4 eunit-2.3.7 ftp-1.0.1 hipe-3.18.2 inets-7.0.3 jinterface-1.9.1 kernel-6.2 megaco-3.18.4 mnesia-4.15.5 observer-2.8.2 odbc-2.12.2 os_mon-2.4.7 otp_mibs-1.2.1 parsetools-2.1.8 public_key-1.6.4 reltool-0.7.8 runtime_tools-1.13.1 sasl-3.3 snmp-5.2.12 ssl-9.1.1 stdlib-3.7 syntax_tools-2.1.6 tftp-1.0.1 tools-3.0.2 wx-1.8.6 xmerl-1.3.18 :
@@ -18,6 +20,8 @@ OTP-21.0.3 : erts-10.0.3 # asn1-5.0.6 common_test-1.16 compiler-7.2.2 crypto-4.3
OTP-21.0.2 : compiler-7.2.2 erts-10.0.2 public_key-1.6.1 stdlib-3.5.1 # asn1-5.0.6 common_test-1.16 crypto-4.3 debugger-4.2.5 dialyzer-3.3 diameter-2.1.5 edoc-0.9.3 eldap-1.2.4 erl_docgen-0.8 erl_interface-3.10.3 et-1.6.2 eunit-2.3.6 ftp-1.0 hipe-3.18 inets-7.0 jinterface-1.9 kernel-6.0 megaco-3.18.3 mnesia-4.15.4 observer-2.8 odbc-2.12.1 os_mon-2.4.5 otp_mibs-1.2 parsetools-2.1.7 reltool-0.7.6 runtime_tools-1.13 sasl-3.2 snmp-5.2.11 ssh-4.7 ssl-9.0 syntax_tools-2.1.5 tftp-1.0 tools-3.0 wx-1.8.4 xmerl-1.3.17 :
OTP-21.0.1 : compiler-7.2.1 erts-10.0.1 # asn1-5.0.6 common_test-1.16 crypto-4.3 debugger-4.2.5 dialyzer-3.3 diameter-2.1.5 edoc-0.9.3 eldap-1.2.4 erl_docgen-0.8 erl_interface-3.10.3 et-1.6.2 eunit-2.3.6 ftp-1.0 hipe-3.18 inets-7.0 jinterface-1.9 kernel-6.0 megaco-3.18.3 mnesia-4.15.4 observer-2.8 odbc-2.12.1 os_mon-2.4.5 otp_mibs-1.2 parsetools-2.1.7 public_key-1.6 reltool-0.7.6 runtime_tools-1.13 sasl-3.2 snmp-5.2.11 ssh-4.7 ssl-9.0 stdlib-3.5 syntax_tools-2.1.5 tftp-1.0 tools-3.0 wx-1.8.4 xmerl-1.3.17 :
OTP-21.0 : asn1-5.0.6 common_test-1.16 compiler-7.2 crypto-4.3 debugger-4.2.5 dialyzer-3.3 diameter-2.1.5 edoc-0.9.3 eldap-1.2.4 erl_docgen-0.8 erl_interface-3.10.3 erts-10.0 et-1.6.2 eunit-2.3.6 ftp-1.0 hipe-3.18 inets-7.0 jinterface-1.9 kernel-6.0 mnesia-4.15.4 observer-2.8 os_mon-2.4.5 otp_mibs-1.2 parsetools-2.1.7 public_key-1.6 reltool-0.7.6 runtime_tools-1.13 sasl-3.2 ssh-4.7 ssl-9.0 stdlib-3.5 syntax_tools-2.1.5 tftp-1.0 tools-3.0 wx-1.8.4 xmerl-1.3.17 # megaco-3.18.3 odbc-2.12.1 snmp-5.2.11 :
+OTP-20.3.8.20 : common_test-1.15.4.1 # asn1-5.0.5.2 compiler-7.1.5.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 crypto-4.2.2.2 debugger-4.2.4 dialyzer-3.2.4 diameter-2.1.4.1 edoc-0.9.2 eldap-1.2.3.1 erl_docgen-0.7.3 erl_interface-3.10.2.1 erts-9.3.3.9 et-1.6.1 eunit-2.3.5 hipe-3.17.1 ic-4.4.4.2 inets-6.5.2.4 jinterface-1.8.1 kernel-5.4.3.2 megaco-3.18.3 mnesia-4.15.3.2 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.2 snmp-5.2.11 ssh-4.6.9.3 ssl-8.2.6.4 stdlib-3.4.5.1 syntax_tools-2.1.4.1 tools-2.11.2 wx-1.8.3 xmerl-1.3.16.1 :
+OTP-20.3.8.19 : diameter-2.1.4.1 erts-9.3.3.9 # asn1-5.0.5.2 common_test-1.15.4 compiler-7.1.5.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 crypto-4.2.2.2 debugger-4.2.4 dialyzer-3.2.4 edoc-0.9.2 eldap-1.2.3.1 erl_docgen-0.7.3 erl_interface-3.10.2.1 et-1.6.1 eunit-2.3.5 hipe-3.17.1 ic-4.4.4.2 inets-6.5.2.4 jinterface-1.8.1 kernel-5.4.3.2 megaco-3.18.3 mnesia-4.15.3.2 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.2 snmp-5.2.11 ssh-4.6.9.3 ssl-8.2.6.4 stdlib-3.4.5.1 syntax_tools-2.1.4.1 tools-2.11.2 wx-1.8.3 xmerl-1.3.16.1 :
OTP-20.3.8.18 : erts-9.3.3.8 # asn1-5.0.5.2 common_test-1.15.4 compiler-7.1.5.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 crypto-4.2.2.2 debugger-4.2.4 dialyzer-3.2.4 diameter-2.1.4 edoc-0.9.2 eldap-1.2.3.1 erl_docgen-0.7.3 erl_interface-3.10.2.1 et-1.6.1 eunit-2.3.5 hipe-3.17.1 ic-4.4.4.2 inets-6.5.2.4 jinterface-1.8.1 kernel-5.4.3.2 megaco-3.18.3 mnesia-4.15.3.2 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.2 snmp-5.2.11 ssh-4.6.9.3 ssl-8.2.6.4 stdlib-3.4.5.1 syntax_tools-2.1.4.1 tools-2.11.2 wx-1.8.3 xmerl-1.3.16.1 :
OTP-20.3.8.17 : xmerl-1.3.16.1 # asn1-5.0.5.2 common_test-1.15.4 compiler-7.1.5.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 crypto-4.2.2.2 debugger-4.2.4 dialyzer-3.2.4 diameter-2.1.4 edoc-0.9.2 eldap-1.2.3.1 erl_docgen-0.7.3 erl_interface-3.10.2.1 erts-9.3.3.7 et-1.6.1 eunit-2.3.5 hipe-3.17.1 ic-4.4.4.2 inets-6.5.2.4 jinterface-1.8.1 kernel-5.4.3.2 megaco-3.18.3 mnesia-4.15.3.2 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.2 snmp-5.2.11 ssh-4.6.9.3 ssl-8.2.6.4 stdlib-3.4.5.1 syntax_tools-2.1.4.1 tools-2.11.2 wx-1.8.3 :
OTP-20.3.8.16 : erts-9.3.3.7 ssh-4.6.9.3 # asn1-5.0.5.2 common_test-1.15.4 compiler-7.1.5.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 crypto-4.2.2.2 debugger-4.2.4 dialyzer-3.2.4 diameter-2.1.4 edoc-0.9.2 eldap-1.2.3.1 erl_docgen-0.7.3 erl_interface-3.10.2.1 et-1.6.1 eunit-2.3.5 hipe-3.17.1 ic-4.4.4.2 inets-6.5.2.4 jinterface-1.8.1 kernel-5.4.3.2 megaco-3.18.3 mnesia-4.15.3.2 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.2 snmp-5.2.11 ssl-8.2.6.4 stdlib-3.4.5.1 syntax_tools-2.1.4.1 tools-2.11.2 wx-1.8.3 xmerl-1.3.16 :
@@ -42,6 +46,7 @@ OTP-20.3.6 : crypto-4.2.2 ssh-4.6.9 # asn1-5.0.5 common_test-1.15.4 compiler-7.1
OTP-20.3.5 : erts-9.3.1 ssl-8.2.6 # 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.2 et-1.6.1 eunit-2.3.5 hipe-3.17.1 ic-4.4.4 inets-6.5.1 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.2 snmp-5.2.10 ssh-4.6.8 stdlib-3.4.5 syntax_tools-2.1.4 tools-2.11.2 wx-1.8.3 xmerl-1.3.16 :
OTP-20.3.4 : erl_interface-3.10.2 ic-4.4.4 inets-6.5.1 ssh-4.6.8 # 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 erts-9.3 et-1.6.1 eunit-2.3.5 hipe-3.17.1 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.2 snmp-5.2.10 ssl-8.2.5 stdlib-3.4.5 syntax_tools-2.1.4 tools-2.11.2 wx-1.8.3 xmerl-1.3.16 :
OTP-20.3.3 : sasl-3.1.2 # 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 snmp-5.2.10 ssh-4.6.7 ssl-8.2.5 stdlib-3.4.5 syntax_tools-2.1.4 tools-2.11.2 wx-1.8.3 xmerl-1.3.16 :
+OTP-20.3.2.1 : common_test-1.15.4.0.1 # asn1-5.0.5 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.7 ssl-8.2.5 stdlib-3.4.5 syntax_tools-2.1.4 tools-2.11.2 wx-1.8.3 xmerl-1.3.16 :
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 :