summaryrefslogtreecommitdiff
path: root/erts
diff options
context:
space:
mode:
Diffstat (limited to 'erts')
-rw-r--r--erts/.gitignore1
-rw-r--r--erts/Makefile2
-rw-r--r--erts/aclocal.m485
-rwxr-xr-xerts/autoconf/configure.vxworks142
-rw-r--r--erts/autoconf/vxworks/sed.general132
-rw-r--r--erts/autoconf/vxworks/sed.vxworks_cpu3247
-rw-r--r--erts/autoconf/vxworks/sed.vxworks_ppc3258
-rw-r--r--erts/autoconf/vxworks/sed.vxworks_ppc60353
-rw-r--r--erts/autoconf/vxworks/sed.vxworks_ppc603_nolongcall53
-rw-r--r--erts/autoconf/vxworks/sed.vxworks_ppc86052
-rw-r--r--erts/autoconf/vxworks/sed.vxworks_simlinux66
-rw-r--r--erts/autoconf/vxworks/sed.vxworks_simso70
-rw-r--r--erts/autoconf/vxworks/sed.vxworks_sparc40
-rw-r--r--erts/configure.in34
-rw-r--r--erts/doc/src/.gitignore3
-rw-r--r--erts/doc/src/Makefile113
-rw-r--r--erts/doc/src/alt_dist.xml6
-rw-r--r--erts/doc/src/epmd_cmd.xml (renamed from erts/doc/src/epmd.xml)13
-rw-r--r--erts/doc/src/erl_cmd.xml (renamed from erts/doc/src/erl.xml)37
-rw-r--r--erts/doc/src/erl_dist_protocol.xml458
-rw-r--r--erts/doc/src/erl_ext_dist.xml48
-rw-r--r--erts/doc/src/erl_ext_fig.gifbin3834 -> 3840 bytes
-rw-r--r--erts/doc/src/erlang.xml736
-rw-r--r--erts/doc/src/erlc_cmd.xml (renamed from erts/doc/src/erlc.xml)3
-rw-r--r--erts/doc/src/erlsrv_cmd.xml (renamed from erts/doc/src/erlsrv.xml)13
-rw-r--r--erts/doc/src/escript_cmd.xml (renamed from erts/doc/src/escript.xml)3
-rw-r--r--erts/doc/src/init.xml25
-rw-r--r--erts/doc/src/ref_man.xml.src18
-rw-r--r--erts/doc/src/run_erl_cmd.xml (renamed from erts/doc/src/run_erl.xml)5
-rw-r--r--erts/doc/src/socket.xml68
-rw-r--r--erts/doc/src/start_cmd.xml (renamed from erts/doc/src/start.xml)5
-rw-r--r--erts/doc/src/start_erl_cmd.xml (renamed from erts/doc/src/start_erl.xml)7
-rw-r--r--erts/doc/src/werl_cmd.xml (renamed from erts/doc/src/werl.xml)9
-rw-r--r--erts/emulator/Makefile.in44
-rw-r--r--erts/emulator/beam/arith_instrs.tab18
-rw-r--r--erts/emulator/beam/atom.names21
-rw-r--r--erts/emulator/beam/beam_bif_load.c102
-rw-r--r--erts/emulator/beam/beam_bp.c395
-rw-r--r--erts/emulator/beam/beam_bp.h16
-rw-r--r--erts/emulator/beam/beam_debug.c5
-rw-r--r--erts/emulator/beam/beam_emu.c582
-rw-r--r--erts/emulator/beam/beam_load.c475
-rw-r--r--erts/emulator/beam/beam_load.h2
-rw-r--r--erts/emulator/beam/bif.c414
-rw-r--r--erts/emulator/beam/bif.tab39
-rw-r--r--erts/emulator/beam/bif_instrs.tab159
-rw-r--r--erts/emulator/beam/big.c18
-rw-r--r--erts/emulator/beam/big.h2
-rw-r--r--erts/emulator/beam/binary.c12
-rw-r--r--erts/emulator/beam/code_ix.c74
-rw-r--r--erts/emulator/beam/code_ix.h9
-rw-r--r--erts/emulator/beam/dist.c1846
-rw-r--r--erts/emulator/beam/dist.h158
-rw-r--r--erts/emulator/beam/erl_alloc.c6
-rw-r--r--erts/emulator/beam/erl_alloc.types8
-rw-r--r--erts/emulator/beam/erl_alloc_util.c1204
-rw-r--r--erts/emulator/beam/erl_alloc_util.h43
-rw-r--r--erts/emulator/beam/erl_async.c38
-rw-r--r--erts/emulator/beam/erl_bif_binary.c2
-rw-r--r--erts/emulator/beam/erl_bif_chksum.c10
-rw-r--r--erts/emulator/beam/erl_bif_guard.c2
-rw-r--r--erts/emulator/beam/erl_bif_info.c327
-rw-r--r--erts/emulator/beam/erl_bif_lists.c31
-rw-r--r--erts/emulator/beam/erl_bif_persistent.c590
-rw-r--r--erts/emulator/beam/erl_bif_port.c17
-rw-r--r--erts/emulator/beam/erl_bif_trace.c193
-rw-r--r--erts/emulator/beam/erl_cpu_topology.c13
-rw-r--r--erts/emulator/beam/erl_cpu_topology.h5
-rw-r--r--erts/emulator/beam/erl_db.c807
-rw-r--r--erts/emulator/beam/erl_db.h19
-rw-r--r--erts/emulator/beam/erl_db_catree.c43
-rw-r--r--erts/emulator/beam/erl_db_hash.c783
-rw-r--r--erts/emulator/beam/erl_db_hash.h12
-rw-r--r--erts/emulator/beam/erl_db_tree.c232
-rw-r--r--erts/emulator/beam/erl_db_tree_util.h7
-rw-r--r--erts/emulator/beam/erl_db_util.c70
-rw-r--r--erts/emulator/beam/erl_db_util.h54
-rw-r--r--erts/emulator/beam/erl_flxctr.c80
-rw-r--r--erts/emulator/beam/erl_flxctr.h18
-rw-r--r--erts/emulator/beam/erl_fun.c145
-rw-r--r--erts/emulator/beam/erl_fun.h1
-rw-r--r--erts/emulator/beam/erl_gc.c27
-rw-r--r--erts/emulator/beam/erl_init.c27
-rw-r--r--erts/emulator/beam/erl_io_queue.c2
-rw-r--r--erts/emulator/beam/erl_lock_check.c2
-rw-r--r--erts/emulator/beam/erl_lock_count.h11
-rw-r--r--erts/emulator/beam/erl_lock_flags.h4
-rw-r--r--erts/emulator/beam/erl_map.c12
-rw-r--r--erts/emulator/beam/erl_message.c20
-rw-r--r--erts/emulator/beam/erl_message.h1
-rw-r--r--erts/emulator/beam/erl_monitor_link.c95
-rw-r--r--erts/emulator/beam/erl_monitor_link.h18
-rw-r--r--erts/emulator/beam/erl_nfunc_sched.c89
-rw-r--r--erts/emulator/beam/erl_nfunc_sched.h144
-rw-r--r--erts/emulator/beam/erl_nif.c763
-rw-r--r--erts/emulator/beam/erl_node_tables.c21
-rw-r--r--erts/emulator/beam/erl_node_tables.h39
-rw-r--r--erts/emulator/beam/erl_proc_sig_queue.c684
-rw-r--r--erts/emulator/beam/erl_proc_sig_queue.h12
-rw-r--r--erts/emulator/beam/erl_process.c862
-rw-r--r--erts/emulator/beam/erl_process.h110
-rw-r--r--erts/emulator/beam/erl_trace.c17
-rw-r--r--erts/emulator/beam/erl_trace.h11
-rw-r--r--erts/emulator/beam/erl_utils.h47
-rw-r--r--erts/emulator/beam/erlang_dtrace.d39
-rw-r--r--erts/emulator/beam/erlang_lttng.h44
-rw-r--r--erts/emulator/beam/error.h16
-rw-r--r--erts/emulator/beam/export.c66
-rw-r--r--erts/emulator/beam/export.h83
-rw-r--r--erts/emulator/beam/external.c2057
-rw-r--r--erts/emulator/beam/external.h30
-rw-r--r--erts/emulator/beam/global.h22
-rw-r--r--erts/emulator/beam/hash.c107
-rw-r--r--erts/emulator/beam/hash.h66
-rw-r--r--erts/emulator/beam/index.c45
-rw-r--r--erts/emulator/beam/instrs.tab248
-rw-r--r--erts/emulator/beam/macros.tab134
-rw-r--r--erts/emulator/beam/msg_instrs.tab3
-rw-r--r--erts/emulator/beam/ops.tab347
-rw-r--r--erts/emulator/beam/packet_parser.c7
-rw-r--r--erts/emulator/beam/packet_parser.h6
-rw-r--r--erts/emulator/beam/register.c118
-rw-r--r--erts/emulator/beam/register.h1
-rw-r--r--erts/emulator/beam/sys.h55
-rw-r--r--erts/emulator/beam/trace_instrs.tab22
-rw-r--r--erts/emulator/beam/utils.c937
-rw-r--r--erts/emulator/drivers/common/inet_drv.c18
-rw-r--r--erts/emulator/drivers/win32/win_con.c58
-rw-r--r--erts/emulator/hipe/hipe_bif2.c1
-rw-r--r--erts/emulator/hipe/hipe_bif_list.m42
-rw-r--r--erts/emulator/hipe/hipe_debug.c1
-rw-r--r--erts/emulator/hipe/hipe_instrs.tab9
-rw-r--r--erts/emulator/hipe/hipe_mode_switch.c8
-rw-r--r--erts/emulator/internal_doc/AutomaticYieldingOfCCode.md62
-rw-r--r--erts/emulator/internal_doc/beam_makeops.md24
-rw-r--r--erts/emulator/nifs/common/prim_file_nif.c56
-rw-r--r--erts/emulator/nifs/common/prim_file_nif.h15
-rw-r--r--erts/emulator/nifs/common/prim_net_nif.c27
-rw-r--r--erts/emulator/nifs/common/socket_nif.c715
-rw-r--r--erts/emulator/nifs/common/socket_util.c115
-rw-r--r--erts/emulator/nifs/common/socket_util.h20
-rw-r--r--erts/emulator/nifs/unix/unix_prim_file.c153
-rw-r--r--erts/emulator/nifs/win32/win_prim_file.c233
-rw-r--r--erts/emulator/sys/common/erl_check_io.c2
-rw-r--r--erts/emulator/sys/common/erl_osenv.h28
-rw-r--r--erts/emulator/sys/common/erl_poll.h11
-rw-r--r--erts/emulator/sys/unix/erl_child_setup.c34
-rw-r--r--erts/emulator/sys/unix/sys.c11
-rw-r--r--erts/emulator/sys/unix/sys_drivers.c1
-rw-r--r--erts/emulator/sys/unix/sys_uds.c2
-rw-r--r--erts/emulator/sys/win32/sys.c1
-rw-r--r--erts/emulator/sys/win32/sys_env.c1
-rw-r--r--erts/emulator/test/Makefile4
-rw-r--r--erts/emulator/test/alloc_SUITE.erl79
-rw-r--r--erts/emulator/test/beam_SUITE.erl3
-rw-r--r--erts/emulator/test/bif_SUITE.erl83
-rw-r--r--erts/emulator/test/binary_SUITE.erl92
-rw-r--r--erts/emulator/test/call_trace_SUITE.erl72
-rw-r--r--erts/emulator/test/code_SUITE_data/call_purged_fun_tester.erl3
-rw-r--r--erts/emulator/test/decode_packet_SUITE.erl74
-rw-r--r--erts/emulator/test/dirty_bif_SUITE.erl4
-rw-r--r--erts/emulator/test/distribution_SUITE.erl137
-rw-r--r--erts/emulator/test/emulator.spec1
-rw-r--r--erts/emulator/test/emulator_bench.spec1
-rw-r--r--erts/emulator/test/erts_test_destructor.erl41
-rw-r--r--erts/emulator/test/exception_SUITE.erl138
-rw-r--r--erts/emulator/test/hash_SUITE.erl618
-rw-r--r--erts/emulator/test/hash_property_test_SUITE.erl103
-rw-r--r--erts/emulator/test/hibernate_SUITE.erl9
-rw-r--r--erts/emulator/test/hipe_SUITE.erl47
-rw-r--r--erts/emulator/test/hipe_SUITE_data/trycatch_1.erl5
-rw-r--r--erts/emulator/test/list_bif_SUITE.erl68
-rw-r--r--erts/emulator/test/lttng_SUITE.erl70
-rw-r--r--erts/emulator/test/map_SUITE.erl16
-rw-r--r--erts/emulator/test/match_spec_SUITE.erl3
-rw-r--r--erts/emulator/test/mtx_SUITE_data/Makefile.src4
-rw-r--r--erts/emulator/test/nif_SUITE.erl64
-rw-r--r--erts/emulator/test/nif_SUITE_data/Makefile.src7
-rw-r--r--erts/emulator/test/nif_SUITE_data/nif_SUITE.c1
-rw-r--r--erts/emulator/test/nif_SUITE_data/nif_api_2_0/erl_nif.h4
-rw-r--r--erts/emulator/test/nofrag_SUITE.erl5
-rw-r--r--erts/emulator/test/persistent_term_SUITE.erl137
-rw-r--r--erts/emulator/test/persistent_term_SUITE_data/Makefile.src8
-rw-r--r--erts/emulator/test/persistent_term_SUITE_data/erts_test_destructor.c83
-rw-r--r--erts/emulator/test/port_SUITE.erl8
-rw-r--r--erts/emulator/test/process_SUITE.erl1031
-rw-r--r--erts/emulator/test/property_test/phash2_properties.erl63
-rw-r--r--erts/emulator/test/scheduler_SUITE.erl191
-rw-r--r--erts/emulator/test/send_term_SUITE.erl24
-rw-r--r--erts/emulator/test/small_SUITE.erl12
-rw-r--r--erts/emulator/test/socket_SUITE.erl1926
-rw-r--r--erts/emulator/test/trace_SUITE.erl7
-rw-r--r--erts/emulator/test/trace_SUITE_data/Makefile.src3
-rw-r--r--erts/emulator/test/trace_SUITE_data/slow_drv.c102
-rw-r--r--erts/emulator/test/trace_call_time_SUITE.erl91
-rw-r--r--erts/emulator/test/trace_local_SUITE.erl9
-rwxr-xr-xerts/emulator/utils/beam_makeops293
-rwxr-xr-xerts/emulator/utils/make_alloc_types3
-rwxr-xr-xerts/emulator/utils/make_preload10
-rwxr-xr-xerts/emulator/utils/make_tables78
-rw-r--r--erts/epmd/Makefile2
-rw-r--r--erts/epmd/epmd.mk2
-rw-r--r--erts/epmd/src/Makefile.in4
-rw-r--r--erts/epmd/src/epmd.c51
-rw-r--r--erts/epmd/src/epmd.h1
-rw-r--r--erts/epmd/src/epmd_int.h34
-rw-r--r--erts/epmd/src/epmd_srv.c115
-rw-r--r--erts/epmd/test/Makefile2
-rw-r--r--erts/etc/common/Makefile.in3
-rw-r--r--erts/etc/common/erlexec.c153
-rw-r--r--erts/etc/unix/cerl.src6
-rw-r--r--erts/etc/unix/etp-commands.in32
-rw-r--r--erts/etc/unix/run_erl.c12
-rw-r--r--erts/etc/win32/cygwin_tools/reg_query.sh24
-rwxr-xr-xerts/etc/win32/cygwin_tools/w32_path.sh59
-rw-r--r--erts/etc/win32/erl.c21
-rw-r--r--erts/etc/win32/msys_tools/reg_query.sh2
-rwxr-xr-xerts/etc/win32/msys_tools/w32_path.sh59
-rw-r--r--erts/etc/win32/nsis/Makefile49
-rwxr-xr-xerts/etc/win32/nsis/dll_version_helper.sh19
-rwxr-xr-xerts/etc/win32/nsis/find_redist.sh50
-rw-r--r--erts/etc/win32/wsl_tools/SetupWSLcross.bat66
-rwxr-xr-xerts/etc/win32/wsl_tools/erl45
-rwxr-xr-xerts/etc/win32/wsl_tools/erlc60
-rwxr-xr-xerts/etc/win32/wsl_tools/javac.sh53
-rwxr-xr-xerts/etc/win32/wsl_tools/make_bootstrap_ini.sh43
-rwxr-xr-x[-rw-r--r--]erts/etc/win32/wsl_tools/make_local_ini.sh (renamed from erts/doc/Makefile)45
-rwxr-xr-xerts/etc/win32/wsl_tools/reg_query.sh19
-rwxr-xr-xerts/etc/win32/wsl_tools/vc/ar.sh48
-rwxr-xr-xerts/etc/win32/wsl_tools/vc/cc.sh382
-rwxr-xr-xerts/etc/win32/wsl_tools/vc/coffix.c161
-rwxr-xr-xerts/etc/win32/wsl_tools/vc/emu_cc.sh100
-rwxr-xr-xerts/etc/win32/wsl_tools/vc/ld.sh210
-rwxr-xr-xerts/etc/win32/wsl_tools/vc/mc.sh96
-rwxr-xr-xerts/etc/win32/wsl_tools/vc/rc.sh94
-rwxr-xr-xerts/etc/win32/wsl_tools/w32_path.sh70
-rw-r--r--erts/include/internal/erl_misc_utils.h1
-rw-r--r--erts/include/internal/erl_printf_format.h4
-rw-r--r--erts/include/internal/ethread.h3
-rw-r--r--erts/lib_src/Makefile.in3
-rw-r--r--erts/lib_src/common/erl_misc_utils.c260
-rw-r--r--erts/lib_src/common/erl_printf.c5
-rw-r--r--erts/lib_src/common/erl_printf_format.c5
-rw-r--r--erts/lib_src/common/ethr_aux.c3
-rw-r--r--erts/lib_src/yielding_c_fun/.gitignore16
-rw-r--r--erts/lib_src/yielding_c_fun/GNUmakefile212
-rw-r--r--erts/lib_src/yielding_c_fun/Makefile54
-rw-r--r--erts/lib_src/yielding_c_fun/README.md610
-rw-r--r--erts/lib_src/yielding_c_fun/TODO4
-rwxr-xr-xerts/lib_src/yielding_c_fun/bin/yielding_c_fun42
-rw-r--r--erts/lib_src/yielding_c_fun/doc/thread_tutorial.md222
-rw-r--r--erts/lib_src/yielding_c_fun/lib/simple_c_gc/.gitignore11
-rw-r--r--erts/lib_src/yielding_c_fun/lib/simple_c_gc/.misc/clang_blacklist.txt10
-rw-r--r--erts/lib_src/yielding_c_fun/lib/simple_c_gc/GIT_VERSION73
-rw-r--r--erts/lib_src/yielding_c_fun/lib/simple_c_gc/LICENSE201
-rw-r--r--erts/lib_src/yielding_c_fun/lib/simple_c_gc/Makefile121
-rw-r--r--erts/lib_src/yielding_c_fun/lib/simple_c_gc/README.md69
-rw-r--r--erts/lib_src/yielding_c_fun/lib/simple_c_gc/chained_hash_set.h307
-rw-r--r--erts/lib_src/yielding_c_fun/lib/simple_c_gc/simple_c_gc.c310
-rw-r--r--erts/lib_src/yielding_c_fun/lib/simple_c_gc/simple_c_gc.h57
-rw-r--r--erts/lib_src/yielding_c_fun/lib/simple_c_gc/sorted_list_set.h370
-rw-r--r--erts/lib_src/yielding_c_fun/lib/simple_c_gc/test.c53
-rw-r--r--erts/lib_src/yielding_c_fun/lib/tiny_regex_c/GIT_VERSION468
-rw-r--r--erts/lib_src/yielding_c_fun/lib/tiny_regex_c/LICENSE24
-rw-r--r--erts/lib_src/yielding_c_fun/lib/tiny_regex_c/Makefile106
-rw-r--r--erts/lib_src/yielding_c_fun/lib/tiny_regex_c/README.md138
-rw-r--r--erts/lib_src/yielding_c_fun/lib/tiny_regex_c/re.c470
-rw-r--r--erts/lib_src/yielding_c_fun/lib/tiny_regex_c/re.h54
-rw-r--r--erts/lib_src/yielding_c_fun/lib/tiny_regex_c/scripts/exrex.py307
-rw-r--r--erts/lib_src/yielding_c_fun/lib/tiny_regex_c/scripts/regex_test.py77
-rw-r--r--erts/lib_src/yielding_c_fun/lib/tiny_regex_c/scripts/regex_test_neg.py82
-rw-r--r--erts/lib_src/yielding_c_fun/lib/tiny_regex_c/tests/test1.c141
-rw-r--r--erts/lib_src/yielding_c_fun/lib/tiny_regex_c/tests/test2.c2097
-rw-r--r--erts/lib_src/yielding_c_fun/lib/tiny_regex_c/tests/test_print.c21
-rw-r--r--erts/lib_src/yielding_c_fun/lib/tiny_regex_c/tests/test_rand.c28
-rw-r--r--erts/lib_src/yielding_c_fun/lib/tiny_regex_c/tests/test_rand_neg.c29
-rw-r--r--erts/lib_src/yielding_c_fun/main_target.mk26
-rw-r--r--erts/lib_src/yielding_c_fun/test/examples/auto_yield.c124
-rw-r--r--erts/lib_src/yielding_c_fun/test/examples/auto_yield.c.out173
-rw-r--r--erts/lib_src/yielding_c_fun/test/examples/const_defenition.c54
-rw-r--r--erts/lib_src/yielding_c_fun/test/examples/const_defenition.c.out2
-rw-r--r--erts/lib_src/yielding_c_fun/test/examples/consume_reds.c85
-rw-r--r--erts/lib_src/yielding_c_fun/test/examples/consume_reds.c.out11
-rw-r--r--erts/lib_src/yielding_c_fun/test/examples/control_statements.c289
-rw-r--r--erts/lib_src/yielding_c_fun/test/examples/control_statements.c.out45
-rw-r--r--erts/lib_src/yielding_c_fun/test/examples/custom_code_save_restore_yield_state.c129
-rw-r--r--erts/lib_src/yielding_c_fun/test/examples/custom_code_save_restore_yield_state.c.out11
-rw-r--r--erts/lib_src/yielding_c_fun/test/examples/custom_code_save_restore_yield_state_alt_syntax.c131
-rw-r--r--erts/lib_src/yielding_c_fun/test/examples/custom_code_save_restore_yield_state_alt_syntax.c.out11
-rw-r--r--erts/lib_src/yielding_c_fun/test/examples/debug_ptr_to_stack.c54
-rw-r--r--erts/lib_src/yielding_c_fun/test/examples/debug_ptr_to_stack.c.out2
-rw-r--r--erts/lib_src/yielding_c_fun/test/examples/declarations.c91
-rw-r--r--erts/lib_src/yielding_c_fun/test/examples/declarations.c.out2
-rw-r--r--erts/lib_src/yielding_c_fun/test/examples/declarations_in_for_loops.c83
-rw-r--r--erts/lib_src/yielding_c_fun/test/examples/declarations_in_for_loops.c.out211
-rw-r--r--erts/lib_src/yielding_c_fun/test/examples/destroy_while_yielded.c92
-rw-r--r--erts/lib_src/yielding_c_fun/test/examples/destroy_while_yielded.c.out33
-rw-r--r--erts/lib_src/yielding_c_fun/test/examples/in_code_var_declaration.c54
-rw-r--r--erts/lib_src/yielding_c_fun/test/examples/in_code_var_declaration.c.out2
-rw-r--r--erts/lib_src/yielding_c_fun/test/examples/multi_scope_yield.c93
-rw-r--r--erts/lib_src/yielding_c_fun/test/examples/multi_scope_yield.c.out9
-rw-r--r--erts/lib_src/yielding_c_fun/test/examples/nested_call_consume_reds.c91
-rw-r--r--erts/lib_src/yielding_c_fun/test/examples/nested_call_consume_reds.c.out11
-rw-r--r--erts/lib_src/yielding_c_fun/test/examples/nested_loop_yield.c104
-rw-r--r--erts/lib_src/yielding_c_fun/test/examples/nested_loop_yield.c.out74
-rw-r--r--erts/lib_src/yielding_c_fun/test/examples/sha256_erlang_nif/.gitignore25
-rw-r--r--erts/lib_src/yielding_c_fun/test/examples/sha256_erlang_nif/LICENSE191
-rw-r--r--erts/lib_src/yielding_c_fun/test/examples/sha256_erlang_nif/Makefile34
-rw-r--r--erts/lib_src/yielding_c_fun/test/examples/sha256_erlang_nif/README.md23
-rw-r--r--erts/lib_src/yielding_c_fun/test/examples/sha256_erlang_nif/c_src/Makefile83
-rw-r--r--erts/lib_src/yielding_c_fun/test/examples/sha256_erlang_nif/c_src/sha-2/.gitignore3
-rw-r--r--erts/lib_src/yielding_c_fun/test/examples/sha256_erlang_nif/c_src/sha-2/.travis.yml2
-rw-r--r--erts/lib_src/yielding_c_fun/test/examples/sha256_erlang_nif/c_src/sha-2/GIT_VERSION265
-rw-r--r--erts/lib_src/yielding_c_fun/test/examples/sha256_erlang_nif/c_src/sha-2/LICENSE24
-rw-r--r--erts/lib_src/yielding_c_fun/test/examples/sha256_erlang_nif/c_src/sha-2/Makefile11
-rw-r--r--erts/lib_src/yielding_c_fun/test/examples/sha256_erlang_nif/c_src/sha-2/README.md97
-rw-r--r--erts/lib_src/yielding_c_fun/test/examples/sha256_erlang_nif/c_src/sha-2/sha-256.c224
-rw-r--r--erts/lib_src/yielding_c_fun/test/examples/sha256_erlang_nif/c_src/sha-2/sha-256.h4
-rw-r--r--erts/lib_src/yielding_c_fun/test/examples/sha256_erlang_nif/c_src/sha-2/sha-256_orginal.c210
-rw-r--r--erts/lib_src/yielding_c_fun/test/examples/sha256_erlang_nif/c_src/sha-2/test.c238
-rw-r--r--erts/lib_src/yielding_c_fun/test/examples/sha256_erlang_nif/c_src/sha256_nif.c173
-rw-r--r--erts/lib_src/yielding_c_fun/test/examples/sha256_erlang_nif/rebar.config12
-rw-r--r--erts/lib_src/yielding_c_fun/test/examples/sha256_erlang_nif/src/sha256_nif.app.src14
-rw-r--r--erts/lib_src/yielding_c_fun/test/examples/sha256_erlang_nif/src/sha256_nif.erl27
-rw-r--r--erts/lib_src/yielding_c_fun/test/examples/sha256_erlang_nif/test/basic_SUITE.erl32
-rw-r--r--erts/lib_src/yielding_c_fun/test/examples/simple_fun_call.c92
-rw-r--r--erts/lib_src/yielding_c_fun/test/examples/simple_fun_call.c.out2
-rw-r--r--erts/lib_src/yielding_c_fun/test/examples/simple_yield.c58
-rw-r--r--erts/lib_src/yielding_c_fun/test/examples/simple_yield.c.out2
-rw-r--r--erts/lib_src/yielding_c_fun/test/examples/simple_yield_no_reds.c61
-rw-r--r--erts/lib_src/yielding_c_fun/test/examples/simple_yield_no_reds.c.out2
-rw-r--r--erts/lib_src/yielding_c_fun/test/examples/simple_yielding_fun_call.c111
-rw-r--r--erts/lib_src/yielding_c_fun/test/examples/simple_yielding_fun_call.c.out18
-rw-r--r--erts/lib_src/yielding_c_fun/test/examples/stack_array.c135
-rw-r--r--erts/lib_src/yielding_c_fun/test/examples/stack_array.c.out4
-rw-r--r--erts/lib_src/yielding_c_fun/test/examples/static_inline_function.c71
-rw-r--r--erts/lib_src/yielding_c_fun/test/examples/static_inline_function.c.out2
-rw-r--r--erts/lib_src/yielding_c_fun/test/examples/test_generated_header_file_code.c66
-rw-r--r--erts/lib_src/yielding_c_fun/test/examples/test_generated_header_file_main.c71
-rw-r--r--erts/lib_src/yielding_c_fun/test/examples/test_generated_header_file_main.c.out302
-rw-r--r--erts/lib_src/yielding_c_fun/test/examples/test_only_output_yielding_funs.c60
-rw-r--r--erts/lib_src/yielding_c_fun/test/examples/test_only_output_yielding_funs.c.out2
-rw-r--r--erts/lib_src/yielding_c_fun/test/examples/test_trap.c42
-rw-r--r--erts/lib_src/yielding_c_fun/test/examples/thread_example.c88
-rw-r--r--erts/lib_src/yielding_c_fun/test/examples/thread_example.c.out47
-rw-r--r--erts/lib_src/yielding_c_fun/test/examples/void_param.c79
-rw-r--r--erts/lib_src/yielding_c_fun/test/examples/void_param.c.out2
-rw-r--r--erts/lib_src/yielding_c_fun/test/examples/void_ret_fun.c85
-rw-r--r--erts/lib_src/yielding_c_fun/test/examples/void_ret_fun.c.out2
-rw-r--r--erts/lib_src/yielding_c_fun/test/examples/ycf_stack_alloc.c126
-rw-r--r--erts/lib_src/yielding_c_fun/test/examples/ycf_stack_alloc.c.out15
-rw-r--r--erts/lib_src/yielding_c_fun/test/examples/yield_with_struct.c67
-rw-r--r--erts/lib_src/yielding_c_fun/test/examples/yield_with_struct.c.out2
-rw-r--r--erts/lib_src/yielding_c_fun/test/examples/yielding_fun_in_control.c68
-rw-r--r--erts/lib_src/yielding_c_fun/test/examples/yielding_fun_in_control.c.out10
-rw-r--r--erts/lib_src/yielding_c_fun/test/examples/yielding_mutual_recursion.c104
-rw-r--r--erts/lib_src/yielding_c_fun/test/examples/yielding_mutual_recursion.c.out302
-rwxr-xr-xerts/lib_src/yielding_c_fun/test/test.sh176
-rw-r--r--erts/lib_src/yielding_c_fun/ycf_helpers.h40
-rw-r--r--erts/lib_src/yielding_c_fun/ycf_lexer.c483
-rw-r--r--erts/lib_src/yielding_c_fun/ycf_lists.h316
-rw-r--r--erts/lib_src/yielding_c_fun/ycf_main.c330
-rw-r--r--erts/lib_src/yielding_c_fun/ycf_node.c915
-rw-r--r--erts/lib_src/yielding_c_fun/ycf_node.h455
-rw-r--r--erts/lib_src/yielding_c_fun/ycf_parser.c1498
-rw-r--r--erts/lib_src/yielding_c_fun/ycf_parser.h27
-rw-r--r--erts/lib_src/yielding_c_fun/ycf_printers.c493
-rw-r--r--erts/lib_src/yielding_c_fun/ycf_printers.h30
-rw-r--r--erts/lib_src/yielding_c_fun/ycf_string.c111
-rw-r--r--erts/lib_src/yielding_c_fun/ycf_string.h89
-rw-r--r--erts/lib_src/yielding_c_fun/ycf_symbol.c166
-rw-r--r--erts/lib_src/yielding_c_fun/ycf_symbol.h110
-rw-r--r--erts/lib_src/yielding_c_fun/ycf_utils.c106
-rw-r--r--erts/lib_src/yielding_c_fun/ycf_utils.h40
-rw-r--r--erts/lib_src/yielding_c_fun/ycf_yield_fun.c1625
-rw-r--r--erts/lib_src/yielding_c_fun/ycf_yield_fun.h116
-rw-r--r--erts/preloaded/ebin/atomics.beambin3316 -> 3316 bytes
-rw-r--r--erts/preloaded/ebin/counters.beambin3116 -> 3112 bytes
-rw-r--r--erts/preloaded/ebin/erl_init.beambin2312 -> 2304 bytes
-rw-r--r--erts/preloaded/ebin/erl_prim_loader.beambin52732 -> 52304 bytes
-rw-r--r--erts/preloaded/ebin/erl_tracer.beambin2228 -> 2228 bytes
-rw-r--r--erts/preloaded/ebin/erlang.beambin100440 -> 108992 bytes
-rw-r--r--erts/preloaded/ebin/erts_code_purger.beambin10996 -> 10904 bytes
-rw-r--r--erts/preloaded/ebin/erts_dirty_process_signal_handler.beambin2784 -> 2760 bytes
-rw-r--r--erts/preloaded/ebin/erts_internal.beambin20984 -> 22900 bytes
-rw-r--r--erts/preloaded/ebin/erts_literal_area_collector.beambin3272 -> 3260 bytes
-rw-r--r--erts/preloaded/ebin/init.beambin50236 -> 50828 bytes
-rw-r--r--erts/preloaded/ebin/persistent_term.beambin1864 -> 1864 bytes
-rw-r--r--erts/preloaded/ebin/prim_buffer.beambin3620 -> 3620 bytes
-rw-r--r--erts/preloaded/ebin/prim_eval.beambin1544 -> 1544 bytes
-rw-r--r--erts/preloaded/ebin/prim_file.beambin28016 -> 28740 bytes
-rw-r--r--erts/preloaded/ebin/prim_inet.beambin81480 -> 81252 bytes
-rw-r--r--erts/preloaded/ebin/prim_net.beambin5144 -> 5144 bytes
-rw-r--r--erts/preloaded/ebin/prim_zip.beambin22460 -> 22308 bytes
-rw-r--r--erts/preloaded/ebin/socket.beambin79020 -> 80780 bytes
-rw-r--r--erts/preloaded/ebin/socket_registry.beambin5676 -> 5592 bytes
-rw-r--r--erts/preloaded/ebin/zlib.beambin19732 -> 19680 bytes
-rw-r--r--erts/preloaded/src/erl_prim_loader.erl24
-rw-r--r--erts/preloaded/src/erlang.erl686
-rw-r--r--erts/preloaded/src/erts.app.src2
-rw-r--r--erts/preloaded/src/erts_internal.erl98
-rw-r--r--erts/preloaded/src/init.erl37
-rw-r--r--erts/preloaded/src/prim_file.erl38
-rw-r--r--erts/preloaded/src/socket.erl102
-rw-r--r--erts/test/erl_print_SUITE.erl41
-rw-r--r--erts/test/erlexec_SUITE.erl40
-rw-r--r--erts/test/erlexec_SUITE_data/erlexec_tests.c2
-rw-r--r--erts/test/nt_SUITE_data/nt_info.c7
-rw-r--r--erts/test/otp_SUITE.erl27
-rw-r--r--erts/test/upgrade_SUITE.erl11
-rw-r--r--erts/test/z_SUITE.erl99
411 files changed, 41919 insertions, 8038 deletions
diff --git a/erts/.gitignore b/erts/.gitignore
index e515dc8811..6631fc883e 100644
--- a/erts/.gitignore
+++ b/erts/.gitignore
@@ -21,3 +21,4 @@
/emulator/test/*_no_opt_SUITE.erl
/emulator/pcre/pcre_exec_loop_break_cases.inc
+/emulator/beam/erl_db_insert_list.ycf.h
diff --git a/erts/Makefile b/erts/Makefile
index e62c896170..12ac50f7d5 100644
--- a/erts/Makefile
+++ b/erts/Makefile
@@ -153,3 +153,5 @@ release_docs:
.PHONY: xmllint
xmllint:
$(MAKE) -C doc/src $@
+
+include $(ERL_TOP)/make/app_targets.mk
diff --git a/erts/aclocal.m4 b/erts/aclocal.m4
index 5d274e69c3..022e22fa62 100644
--- a/erts/aclocal.m4
+++ b/erts/aclocal.m4
@@ -116,8 +116,8 @@ dnl
dnl LM_WINDOWS_ENVIRONMENT
dnl
dnl
-dnl Tries to determine thw windows build environment, i.e.
-dnl MIXED_CYGWIN_VC or MIXED_MSYS_VC
+dnl Tries to determine the windows build environment, i.e.
+dnl MIXED_VC or MIXED_MINGW
dnl
AC_DEFUN(LM_WINDOWS_ENVIRONMENT,
@@ -127,36 +127,39 @@ if test "X$windows_environment_" != "Xchecked"; then
windows_environment_=checked
MIXED_CYGWIN=no
MIXED_MSYS=no
+MIXED_VSL=no
-AC_MSG_CHECKING(for mixed cygwin or msys and native VC++ environment)
+dnl MIXED_VC is Microsoft Visual C++ used as standard compiler
+MIXED_VC=no
+dnl MIXED_MINGW is mingw(32|64) used as standard compiler
+MIXED_MINGW=no
+
+AC_MSG_CHECKING(for mixed mingw-gcc and native VC++ environment)
if test "X$host" = "Xwin32" -a "x$GCC" != "xyes"; then
if test -x /usr/bin/msys-?.0.dll; then
CFLAGS="$CFLAGS -O2"
MIXED_MSYS=yes
AC_MSG_RESULT([MSYS and VC])
- MIXED_MSYS_VC=yes
- CPPFLAGS="$CPPFLAGS -DERTS_MIXED_MSYS_VC"
+ MIXED_VC=yes
+ CPPFLAGS="$CPPFLAGS -DERTS_MIXED_VC"
elif test -x /usr/bin/cygpath; then
CFLAGS="$CFLAGS -O2"
MIXED_CYGWIN=yes
AC_MSG_RESULT([Cygwin and VC])
- MIXED_CYGWIN_VC=yes
- CPPFLAGS="$CPPFLAGS -DERTS_MIXED_CYGWIN_VC"
- else
+ MIXED_VC=yes
+ CPPFLAGS="$CPPFLAGS -DERTS_MIXED_VC"
+ elif test -x /bin/wslpath; then
+ CFLAGS="$CFLAGS -O2"
+ MIXED_WSL=yes
+ AC_MSG_RESULT([WSL and VC])
+ MIXED_VC=yes
+ CPPFLAGS="$CPPFLAGS -DERTS_MIXED_VC"
+ else
AC_MSG_RESULT([undeterminable])
- AC_MSG_ERROR(Seems to be mixed windows but not with cygwin, cannot handle this!)
+ AC_MSG_ERROR(Seems to be mixed windows but not within any known env, cannot handle this!)
fi
else
AC_MSG_RESULT([no])
- MIXED_CYGWIN_VC=no
- MIXED_MSYS_VC=no
-fi
-AC_SUBST(MIXED_CYGWIN_VC)
-AC_SUBST(MIXED_MSYS_VC)
-
-MIXED_VC=no
-if test "x$MIXED_MSYS_VC" = "xyes" -o "x$MIXED_CYGWIN_VC" = "xyes" ; then
- MIXED_VC=yes
fi
AC_SUBST(MIXED_VC)
@@ -166,44 +169,58 @@ if test "x$MIXED_MSYS" != "xyes"; then
if test "X$host" = "Xwin32" -a "x$GCC" = x"yes"; then
if test -x /usr/bin/cygpath; then
CFLAGS="$CFLAGS -O2"
- MIXED_CYGWIN=yes
AC_MSG_RESULT([yes])
- MIXED_CYGWIN_MINGW=yes
- CPPFLAGS="$CPPFLAGS -DERTS_MIXED_CYGWIN_MINGW"
+ MIXED_MINGW=yes
+ CPPFLAGS="$CPPFLAGS -DERTS_MIXED_MINGW"
else
AC_MSG_RESULT([undeterminable])
AC_MSG_ERROR(Seems to be mixed windows but not with cygwin, cannot handle this!)
fi
else
AC_MSG_RESULT([no])
- MIXED_CYGWIN_MINGW=no
fi
else
- MIXED_CYGWIN_MINGW=no
-fi
-AC_SUBST(MIXED_CYGWIN_MINGW)
+ AC_MSG_CHECKING(for mixed MSYS and native MinGW environment)
+ if test "x$GCC" = x"yes"; then
+ if test -x /usr/bin/msys-=.0.dll; then
+ CFLAGS="$CFLAGS -O2"
+ AC_MSG_RESULT([yes])
+ MIXED_MINGW=yes
+ CPPFLAGS="$CPPFLAGS -DERTS_MIXED_MINGW"
+ else
+ AC_MSG_RESULT([undeterminable])
+ AC_MSG_ERROR(Seems to be mixed windows but not with msys, cannot handle this!)
+ fi
+ else
+ AC_MSG_RESULT([no])
+ fi
+fi
+AC_SUBST(MIXED_MINGW)
AC_MSG_CHECKING(if we mix cygwin with any native compiler)
if test "X$MIXED_CYGWIN" = "Xyes"; then
- AC_MSG_RESULT([yes])
+ AC_MSG_RESULT([yes])
else
AC_MSG_RESULT([no])
fi
-AC_SUBST(MIXED_CYGWIN)
-
AC_MSG_CHECKING(if we mix msys with another native compiler)
if test "X$MIXED_MSYS" = "Xyes" ; then
- AC_MSG_RESULT([yes])
+ AC_MSG_RESULT([yes])
else
AC_MSG_RESULT([no])
fi
-AC_SUBST(MIXED_MSYS)
+AC_MSG_CHECKING(if we mix WSL with another native compiler)
+if test "X$MIXED_WSL" = "Xyes" ; then
+ AC_MSG_RESULT([yes])
+else
+ AC_MSG_RESULT([no])
+fi
fi
-])
-
+])
+
dnl ----------------------------------------------------------------------
dnl
dnl LM_FIND_EMU_CC
@@ -2943,6 +2960,8 @@ if test "x$GCC" = xyes; then
DED_STATIC_CFLAGS="$DED_CFLAGS"
DED_CFLAGS="$DED_CFLAGS -fPIC"
+ # Remove -fPIE and -fno-PIE
+ DED_CFLAGS=`echo $DED_CFLAGS | sed 's/-f\(no-\)\?PIE//g'`
fi
DED_EXT=so
@@ -2993,7 +3012,7 @@ case $host_os in
DED_LDFLAGS="-64 $DED_LDFLAGS"
fi
;;
- aix4*)
+ aix*|os400*)
DED_LDFLAGS="-G -bnoentry -bexpall"
;;
freebsd2*)
diff --git a/erts/autoconf/configure.vxworks b/erts/autoconf/configure.vxworks
deleted file mode 100755
index c3bdfd0095..0000000000
--- a/erts/autoconf/configure.vxworks
+++ /dev/null
@@ -1,142 +0,0 @@
-#!/bin/sh
-#
-# %CopyrightBegin%
-#
-# Copyright Ericsson AB 1997-2018. All Rights Reserved.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# 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%
-#
-# Author:
-# Patrik Winroth
-#
-
-
-# vxworks_ppc860 vxworks_ppc603 vxworks_ppc603_longcall vxworks_cpu32 vxworks_sparc
-# vxworks_ppc750 vxworks_simso
-
-case $# in
-1) host=$1 ;;
-*) echo "usage: configure.vxworks host-configuration"; exit 1 ;;
-esac
-
-case $1 in
-vxworks_cpu32) ;;
-vxworks_ppc750) ;;
-vxworks_ppc860) ;;
-vxworks_ppc603) ;;
-vxworks_ppc603_nolongcall) ;;
-vxworks_sparc) ;;
-vxworks_simso) ;;
-vxworks_simlinux) ;;
-vxworks_ppc32) ;;
-*) echo "usage: configure.vxworks TARGET";
- echo "where TARGET is one of vxworks_cpu32, vxworks_ppc750, vxworks_ppc860, vxworks_ppc603, vxworks_ppc603_nolongcall, vxworks_sparc, vxworks_simso, vxworks_simlinux, vxworks_ppc32"; exit 1;;
-esac
-
-if [ "x$ERL_TOP" = x ]; then
- echo "You need to set ERL_TOP!"
- exit 1
-fi
-
-
-target=$host
-
-# Find out the HOST and WIND_BASE environment
-HOST_TYPE=${HOST_TYPE:=sun4-solaris2}
-case $1 in
-vxworks_ppc750) VXTOP=Tornado2.2 ;;
-vxworks_simso) VXTOP=WindRiver ;;
-vxworks_simlinux) VXTOP=WindRiver ;;
-vxworks_ppc32) VXTOP=WindRiver ;;
-*) VXTOP=wind ;;
-esac
-
-WIND_BASE=${WIND_BASE:=`ypmatch tornado passwd | awk -F: '{print $6}'`/$VXTOP}
-
-# These are created by autoconf.
-MKDIRS="${ERL_TOP}/lib/os_mon/priv/bin/$target
- ${ERL_TOP}/lib/os_mon/priv/obj/$target
- ${ERL_TOP}/lib/asn1/priv/lib/$target
- ${ERL_TOP}/lib/asn1/priv/obj/$target
- ${ERL_TOP}/lib/erl_interface/obj/$target
- ${ERL_TOP}/lib/erl_interface/obj.debug/$target
- ${ERL_TOP}/lib/erl_interface/bin/$target
- ${ERL_TOP}/lib/runtime_tools/priv/lib/$target
- ${ERL_TOP}/lib/runtime_tools/priv/obj/$target
- ${ERL_TOP}/erts/obj/$target
- ${ERL_TOP}/erts/obj.debug/$target
- ${ERL_TOP}/bin/$target"
-
-for dir in $MKDIRS; do
- test ! -d "$dir" && mkdir -p "$dir"
-done
-
-#
-# Create Makefiles for vxWorks.
-#
-my_root=${ERL_TOP}/erts/emulator
-emu_test=$my_root/test
-beam=$my_root/beam
-erts_lib_src=${ERL_TOP}/erts/lib_src
-erts_incl=${ERL_TOP}/erts/include
-erts_incl_intrnl=${ERL_TOP}/erts/include/internal
-etcdir=${ERL_TOP}/erts/etc/common
-erlint_incl_dir=${ERL_TOP}/lib/erl_interface/include
-erlint_dir=${ERL_TOP}/lib/erl_interface/src
-epmd_dir=${ERL_TOP}/erts/epmd/src
-os_mon_dir=${ERL_TOP}/lib/os_mon/c_src
-internal_tools_dir=${ERL_TOP}
-libdir=${ERL_TOP}/lib
-tsdir=$libdir/test_server/src
-zlibdir=${ERL_TOP}/erts/emulator/zlib
-runtime_tools_dir=${ERL_TOP}/lib/runtime_tools/c_src
-tools_dir=${ERL_TOP}/lib/tools/c_src
-
-CONFIG_FILES="${ERL_TOP}/erts/emulator/$host/Makefile
- $erts_lib_src/$host/Makefile
- $erts_incl/$host/erl_int_sizes_config.h
- $erts_incl_intrnl/$host/ethread.mk
- $erts_incl_intrnl/$host/ethread_header_config.h
- $etcdir/$host/Makefile
- $erlint_incl_dir/$host/ei_config.h
- $erlint_dir/$host/Makefile
- $erlint_dir/$host/eidefs.mk
- $epmd_dir/$host/Makefile
- $internal_tools_dir/make/$host/otp.mk
- $internal_tools_dir/make/$host/otp_ded.mk
- $os_mon_dir/$host/Makefile
- $zlibdir/$host/Makefile
- $runtime_tools_dir/$host/Makefile
- $tools_dir/$host/Makefile"
-
-for file in $CONFIG_FILES; do
- new_name=`echo $file|sed "s%/$host/%/$target/%"`
- dir=`echo $new_name|sed 's%/[^/][^/]*$%%'`
- if test "$dir" != "$new_name" && test "$dir" != .; then
- test ! -d "$dir" && mkdir "$dir"
- fi
-
- sole_name=`echo $file|sed "s%.*$target/%%"`
- in_file=`echo $dir|sed "s%/[^/][^/]*$%/$sole_name.in%"`
- echo "creating $new_name"
- sed -f vxworks/sed.$target -f vxworks/sed.general \
- -e "s,@HOST_TYPE@,$HOST_TYPE,g" \
- -e "s,@WIND_BASE@,$WIND_BASE,g" \
- -e "s,@TARGET@,$target,g" \
- -e "s,@ENV_CFLAGS@,$CFLAGS,g" \
- $in_file > $new_name
-done
-
-
diff --git a/erts/autoconf/vxworks/sed.general b/erts/autoconf/vxworks/sed.general
deleted file mode 100644
index ffd5a8133c..0000000000
--- a/erts/autoconf/vxworks/sed.general
+++ /dev/null
@@ -1,132 +0,0 @@
-#
-# %CopyrightBegin%
-#
-# Copyright Ericsson AB 1997-2018. All Rights Reserved.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# 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%
-#
-# Author: Patrik Winroth
-#
-# This sed program file is intended to be used when creating Makefiles
-# for vxworks from the generic Makefile.in that is found in a number
-# of directories (see configure.vxworks).
-#
-# This is the general part that is common for all architectures.
-#
-
-# Size of data types.
-s|^#undef SIZEOF_CHAR|#define SIZEOF_CHAR 1|
-s|^#undef SIZEOF_SHORT|#define SIZEOF_SHORT 2|
-s|^#undef SIZEOF_INT|#define SIZEOF_INT 4|
-s|^#undef SIZEOF_LONG_LONG|#define SIZEOF_LONG_LONG 8|
-s|^#undef SIZEOF_LONG$|#define SIZEOF_LONG 4|
-s|^#undef SIZEOF_VOID_P$|#define SIZEOF_VOID_P 4|
-
-# General stuff.
-s|@erts_rootdir@|/clearcase/otp/erts|
-
-s|@LIBOBJS@|$(OBJDIR)/elib_malloc.o|
-s|@DLOAD_LIB@||
-s|@LDFLAGS@||
-# FIXME: A bit strange to clear out remaining DED_*
-s|@DED_LDFLAGS@||
-s|@DED_CFLAGS@||
-s|@DED_EMU_THR_DEFS@||
-s|@DED_THR_DEFS@||
-s|@DED_SYS_INCLUDE@||
-s|@WERRORFLAGS@||
-s|@DED_STATIC_CFLAGS@||
-s|@STATIC_CFLAGS@||
-s|@GCCLIB@|libgcc.a|
-s|@DEFS@||
-s|@DEXPORT@||
-s|@DCFLAGS@||
-s|@THR_DEFS@||
-s|@THR_LIBS@||
-s|@THR_LIB_NAME@||
-s|@THR_X_LIBS@||
-s|@ETHR_X_LIBS@||
-s|@ETHR_LIBS@||
-s|@ETHR_LIB_NAME@||
-s|@ETHR_DEFS@||
-s|@ETHR_THR_LIB_BASE@||
-s|@ETHR_THR_LIB_BASE_DIR@||
-s|@SYSTEMD_DAEMON_LIBS@||
-s|@EMU_THR_DEFS@||
-s|@EMU_THR_LIBS@||
-s|@EMU_THR_LIB_NAME@|ethread|
-s|@ERTS_ENABLE_KERNEL_POLL@|no|
-s|@ERTS_INTERNAL_X_LIBS@||
-s|@cc_root@|/clearcase/otp/|
-# Define VxWorks even though cross-compiling.
-s|@CROSS_COMPILING|yes|
-
-s|@HCFLAGS@|-DVXWORKS|
-s|@HCLIBS@||
-s|@ENABLE_ALLOC_TYPE_VARS@||
-s|@TERMCAP_LIB@||
-s|@ERTS_BUILD_SMP_EMU@|no|
-s|@HAVE_VALGRIND@|no|
-s|@EXEEXT@||
-s|@WITH_SCTP@||
-
-# HiPE
-s|@HIPE_ENABLED@||
-
-# m4
-s|@OPSYS@|noopsys|
-
-# Conditional inclusion of applications
-s|@HIPE_APP@||
-s|@SSL_APP@|ssl|
-s|@CRYPTO_APP@|crypto|
-s|@SSH_APP@|ssh|
-
-# The target tools prefix, prepended to all cc,ld,as etc commands
-s|@TTPREFIX@|GCC_EXEC_PREFIX=@WIND_BASE@/host/@HOST_TYPE@/lib/gcc-lib/ @WIND_BASE@/host/@HOST_TYPE@/bin/|
-
-# Install programs etc
-s|@PERL@|perl|
-s|@INSTALL_PROGRAM@|${INSTALL}|
-s|@INSTALL_SCRIPT@|${INSTALL}|
-s|@INSTALL_DATA@|${INSTALL} -m 644|
-s|@INSTALL_DIR@|$(INSTALL) -d|
-s|@MKDIR@|/bin/mkdir|
-s|@ERLANG_OSTYPE@|vxworks|
-s|@vxworks_reclaim@|reclaim.h|
-s|@os_mon_programs@||
-s|@erlexec@|erl.exec|
-s|@EMU_LIBOBJS@||
-
-# General CFLAGS
-s|@GENERAL_CFLAGS@|@ENV_CFLAGS@ -DHAVE_LOCALTIME_R -DHAVE_GMTIME_R -DENABLE_ELIB_MALLOC -DELIB_HEAP_USER -DELIB_SORTED_BLOCKS -DWORDS_BIGENDIAN -DELIB_DONT_INITIALIZE -DSIZEOF_CHAR=1 -DSIZEOF_SHORT=2 -DSIZEOF_INT=4 -DSIZEOF_LONG=4 -DSIZEOF_LONG_LONG=8 -DSIZEOF_VOID_P=4 -DERTS_USE_PORT_TASKS=1|g
-s|@WFLAGS@||
-
-# Thread flags for eidefs.mk (erl_interface)
-s|@EI_THREADS@|false|
-
-# Make java code compile although we don't test it on VxWorks (no license)
-s|@JAVAC@|javac|
-
-# What is this anyway?
-# Disable it and see what breaks.
-#s|@ded_soname@||
-
-# Only variable substituted directly
-s|$(LDFLAGS)|-r -d|
-s|@LIBRT@||
-# XXX What is EFFLAGS? Not used in the emulator Makefile.in anyway.
-s|$(EFLAGS)|-DENABLE_ELIB_MALLOC -DELIB_HEAP_USER -DELIB_SORTED_BLOCKS|
-
diff --git a/erts/autoconf/vxworks/sed.vxworks_cpu32 b/erts/autoconf/vxworks/sed.vxworks_cpu32
deleted file mode 100644
index 26e4f4c7ad..0000000000
--- a/erts/autoconf/vxworks/sed.vxworks_cpu32
+++ /dev/null
@@ -1,47 +0,0 @@
-#
-# %CopyrightBegin%
-#
-# Copyright Ericsson AB 1997-2018. All Rights Reserved.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# 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%
-#
-# Author: Patrik Winroth
-#
-# This sed program file is intended to be used when creating Makefiles for vxworks
-# from the generic Makefile.in that is found in a number of directories (see configure.vxworks)
-#
-#
-#
-s|@host@|vxworks_cpu32|
-s|@system_type@|vxworks_cpu32|
-s|@CC@|@TTPREFIX@cc68k|
-s|@HCC@|gcc|
-s|@GCC@|yes|
-s|@LD@|@TTPREFIX@ld68k|
-s|@LIBS@||
-s|@DED_LD@|@TTPREFIX@ld68k|
-s|@DED_CFLAGS@|@CFLAGS@|
-s|@DEBUG_FLAGS@|-g|
-s|@GCCLIB_PATH@|@WIND_BASE@/host/@HOST_TYPE@/lib/gcc-lib/m68k-wrs-vxworks/cygnus-2.7.2-960126/m68000/msoft-float/libgcc.a|
-s|@RANLIB@|@TTPREFIX@ranlib68k|
-s|@AR@|@TTPREFIX@ar68k|
-s|@STRIP@|@TTPREFIX@strip68k|
-s|@SYMPREFIX@|_|
-s|@GCCLIBFLAGS@|-L@WIND_BASE@/host/@HOST_TYPE@/lib/gcc-lib/m68k-wrs-vxworks/cygnus-2.7.2-960126/m68000/msoft-float -lgcc|
-
-s|@LIB_CFLAGS@|@CFLAGS@|
-
-s|@CFLAGS@|@GENERAL_CFLAGS@ -DCPU=CPU32 -mnobitfield -DWANT_NONBLOCKING -DHAVE_MEMMOVE -DVXWORKS -I@WIND_BASE@/target/h -fno-builtin -nostdinc -fvolatile -msoft-float|
-#s|@LIB_CFLAGS@|@GENERAL_CFLAGS@ -DCPU=CPU32 -mnobitfield -DWANT_NONBLOCKING -DHAVE_MEMMOVE -DVXWORKS -I@WIND_BASE@/target/h -fno-builtin -nostdinc -fvolatile -msoft-float|
diff --git a/erts/autoconf/vxworks/sed.vxworks_ppc32 b/erts/autoconf/vxworks/sed.vxworks_ppc32
deleted file mode 100644
index 44697aadc2..0000000000
--- a/erts/autoconf/vxworks/sed.vxworks_ppc32
+++ /dev/null
@@ -1,58 +0,0 @@
-#
-# %CopyrightBegin%
-#
-# Copyright Ericsson AB 2006-2018. All Rights Reserved.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# 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%
-#
-# Author: Peter Andersson
-#
-# This sed program file is intended to be used when creating Makefiles for vxworks
-# from the generic Makefile.in that is found in a number of directories (see configure.vxworks)
-#
-#
-#
-# Install programs etc
-s|@INSTALL@|/usr/bin/install -c|
-
-# other
-s|@host@|vxworks_ppc32|
-s|@system_type@|vxworks_ppc32|
-s|@ARCH@|ppc32|
-s|@CC@|GCC_EXEC_PREFIX=@WIND_BASE@/gnu/3.4.4-vxworks-6.3/@HOST_TYPE@/lib/gcc-lib/ @WIND_BASE@/gnu/3.4.4-vxworks-6.3/@HOST_TYPE@/bin/ccppc -mlongcall|
-s|@HCC@|gcc|
-s|@GCC@|yes|
-s|@LD@|GCC_EXEC_PREFIX=@WIND_BASE@/gnu/3.4.4-vxworks-6.3/@HOST_TYPE@/lib/gcc-lib/ @WIND_BASE@/gnu/3.4.4-vxworks-6.3/@HOST_TYPE@/bin/ldppc|
-s|@STRIP@|GCC_EXEC_PREFIX=@WIND_BASE@/gnu/3.4.4-vxworks-6.3/@HOST_TYPE@/lib/gcc-lib/ @WIND_BASE@/workbench-2.3/@HOST_TYPE@/bin/stripppc|
-s|@SYMPREFIX@||
-s|@LIBS@||
-s|@GCCLIBFLAGS@|-L@WIND_BASE@/vxworks-6.3/target/lib/ppc/PPC32/common -lgcc|
-s|@DED_LD@|GCC_EXEC_PREFIX=@WIND_BASE@/gnu/3.4.4-vxworks-6.3/@HOST_TYPE@/lib/gcc-lib/ @WIND_BASE@/gnu/3.4.4-vxworks-6.3/@HOST_TYPE@/bin/ldppc|
-s|@DED_CFLAGS@|@CFLAGS@|
-s|@DEBUG_CFLAGS@|@CFLAGS@|
-# generate dwarf debug code on PPC ..
-s|@DEBUG_FLAGS@|-gdwarf|
-# remove -g option
-s|TYPE_FLAGS = -g |TYPE_FLAGS = |
-s|@GCCLIB_PATH@|@WIND_BASE@/vxworks-6.3/target/lib/ppc/PPC32/common/libgcc.a|
-s|@RANLIB@|GCC_EXEC_PREFIX=@WIND_BASE@/gnu/3.4.4-vxworks-6.3/@HOST_TYPE@/lib/gcc-lib/ @WIND_BASE@/gnu/3.4.4-vxworks-6.3/@HOST_TYPE@/bin/ranlibppc|
-s|@AR@|GCC_EXEC_PREFIX=@WIND_BASE@/gnu/3.4.4-vxworks-6.3/@HOST_TYPE@/lib/gcc-lib/ @WIND_BASE@/gnu/3.4.4-vxworks-6.3/@HOST_TYPE@/bin/arppc|
-# -Dasm(X)= is for beam
-
-s|@LIB_CFLAGS@|@CFLAGS@|
-
-s|@CFLAGS@|@GENERAL_CFLAGS@ -DCPU=PPC32 -DTOOL_FAMILY=gnu -DTOOL=gnu -DWANT_NONBLOCKING -DHAVE_SENS -DHAVE_MEMMOVE -DVXWORKS -I@WIND_BASE@/vxworks-6.3/target/h -I@WIND_BASE@/gnu/3.4.4-vxworks-6.3/@HOST_TYPE@/lib/gcc/powerpc-wrs-vxworks/3.4.4/include -I@WIND_BASE@/vxworks-6.3/target/h/wrn/coreip -mstrict-align -fvolatile -fno-builtin |
-
-#s|@LIB_CFLAGS@|@GENERAL_CFLAGS@ -DCPU=PPC32 -DTOOL_FAMILY=gnu -DTOOL=gnu -DWANT_NONBLOCKING -DHAVE_SENS -DHAVE_MEMMOVE -DVXWORKS -I@WIND_BASE@/vxworks-6.3/target/h/wrn/coreip -I@WIND_BASE@/vxworks-6.3/target/h -mstrict-align -fvolatile -fno-builtin |
diff --git a/erts/autoconf/vxworks/sed.vxworks_ppc603 b/erts/autoconf/vxworks/sed.vxworks_ppc603
deleted file mode 100644
index 4fdfd51273..0000000000
--- a/erts/autoconf/vxworks/sed.vxworks_ppc603
+++ /dev/null
@@ -1,53 +0,0 @@
-#
-# %CopyrightBegin%
-#
-# Copyright Ericsson AB 2000-2018. All Rights Reserved.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# 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%
-#
-# Author: Patrik Winroth
-#
-# This sed program file is intended to be used when creating Makefiles for vxworks
-# from the generic Makefile.in that is found in a number of directories (see configure.vxworks)
-#
-#
-#
-s|@host@|vxworks_ppc603|
-s|@system_type@|vxworks_ppc603|
-s|@ARCH@|ppc603|
-s|@CC@|@TTPREFIX@ccppc -mlongcall|
-s|@HCC@|gcc|
-s|@GCC@|yes|
-s|@LD@|@TTPREFIX@ldppc|
-s|@STRIP@|@TTPREFIX@stripppc|
-s|@SYMPREFIX@||
-s|@LIBS@||
-s|@GCCLIBFLAGS@|-L@WIND_BASE@/host/@HOST_TYPE@/lib/gcc-lib/powerpc-wrs-vxworks/cygnus-2.7.2-960126 -lgcc|
-s|@DED_LD@|@TTPREFIX@ldppc|
-s|@DED_CFLAGS@|@CFLAGS@|
-s|@DEBUG_CFLAGS@|@CFLAGS@|
-# generate dwarf debug code on PPC ..
-s|@DEBUG_FLAGS@|-gdwarf|
-# remove -g option
-s|TYPE_FLAGS = -g |TYPE_FLAGS = |
-s|@GCCLIB_PATH@|@WIND_BASE@/host/@HOST_TYPE@/lib/gcc-lib/powerpc-wrs-vxworks/cygnus-2.7.2-960126/libgcc.a|
-s|@RANLIB@|@TTPREFIX@ranlibppc|
-s|@AR@|@TTPREFIX@arppc|
-# -Dasm(X)= is for beam
-
-s|@LIB_CFLAGS@|@CFLAGS@|
-
-s|@CFLAGS@|@GENERAL_CFLAGS@ -DCPU=PPC603 -DWANT_NONBLOCKING -DHAVE_MEMMOVE -DVXWORKS -I@WIND_BASE@/target/h -mstrict-align -fvolatile -fno-builtin -fno-for-scope -D_GNU_TOOL|
-#s|@LIB_CFLAGS@|@GENERAL_CFLAGS@ -DCPU=PPC603 -DWANT_NONBLOCKING -DHAVE_MEMMOVE -DVXWORKS -I@WIND_BASE@/target/h -mstrict-align -fvolatile -fno-builtin -fno-for-scope -D_GNU_TOOL|
diff --git a/erts/autoconf/vxworks/sed.vxworks_ppc603_nolongcall b/erts/autoconf/vxworks/sed.vxworks_ppc603_nolongcall
deleted file mode 100644
index d86876e90e..0000000000
--- a/erts/autoconf/vxworks/sed.vxworks_ppc603_nolongcall
+++ /dev/null
@@ -1,53 +0,0 @@
-#
-# %CopyrightBegin%
-#
-# Copyright Ericsson AB 1997-2018. All Rights Reserved.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# 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%
-#
-# Author: Patrik Winroth
-#
-# This sed program file is intended to be used when creating Makefiles for vxworks
-# from the generic Makefile.in that is found in a number of directories (see configure.vxworks)
-#
-#
-#
-s|@host@|vxworks_ppc603|
-s|@system_type@|vxworks_ppc603|
-s|@ARCH@|ppc603|
-s|@CC@|@TTPREFIX@ccppc|
-s|@HCC@|gcc|
-s|@GCC@|yes|
-s|@LD@|@TTPREFIX@ldppc|
-s|@STRIP@|@TTPREFIX@stripppc|
-s|@SYMPREFIX@||
-s|@LIBS@||
-s|@GCCLIBFLAGS@|-L@WIND_BASE@/host/@HOST_TYPE@/lib/gcc-lib/powerpc-wrs-vxworks/cygnus-2.7.2-960126 -lgcc|
-s|@DED_LD@|@TTPREFIX@ldppc|
-s|@DED_CFLAGS@|@CFLAGS@|
-# generate dwarf debug code on PPC ..
-s|@DEBUG_FLAGS@|-gdwarf|
-# remove -g option
-s|TYPE_FLAGS = -g |TYPE_FLAGS = |
-s|@GCCLIB_PATH@|@WIND_BASE@/host/@HOST_TYPE@/lib/gcc-lib/powerpc-wrs-vxworks/cygnus-2.7.2-960126/libgcc.a|
-s|@RANLIB@|@TTPREFIX@ranlibppc|
-s|@AR@|@TTPREFIX@arppc|
-# -Dasm(X)= is for beam
-
-s|@LIB_CFLAGS@|@CFLAGS@|
-
-s|@CFLAGS@|@GENERAL_CFLAGS@ -DCPU=PPC603 -DWANT_NONBLOCKING -DHAVE_MEMMOVE -DVXWORKS -I@WIND_BASE@/target/h -mstrict-align -fvolatile -fno-builtin -fno-for-scope -D_GNU_TOOL|
-#s|@LIB_CFLAGS@|@GENERAL_CFLAGS@ -DCPU=PPC603 -DWANT_NONBLOCKING -DHAVE_MEMMOVE -DVXWORKS -I@WIND_BASE@/target/h -mstrict-align -fvolatile -fno-builtin -fno-for-scope -D_GNU_TOOL|
-
diff --git a/erts/autoconf/vxworks/sed.vxworks_ppc860 b/erts/autoconf/vxworks/sed.vxworks_ppc860
deleted file mode 100644
index a5c4c2d5c3..0000000000
--- a/erts/autoconf/vxworks/sed.vxworks_ppc860
+++ /dev/null
@@ -1,52 +0,0 @@
-#
-# %CopyrightBegin%
-#
-# Copyright Ericsson AB 1997-2018. All Rights Reserved.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# 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%
-#
-# Author: Patrik Winroth
-#
-# This sed program file is intended to be used when creating Makefiles for vxworks
-# from the generic Makefile.in that is found in a number of directories (see configure.vxworks)
-#
-#
-#
-s|@host@|vxworks_ppc860|
-s|@system_type@|vxworks_ppc860|
-s|@ARCH@|ppc860|
-s|@CC@|@TTPREFIX@ccppc -mlongcall|
-s|@HCC@|gcc|
-s|@GCC@|yes|
-s|@LD@|@TTPREFIX@ldppc|
-s|@STRIP@|@TTPREFIX@stripppc|
-s|@SYMPREFIX@||
-s|@LIBS@||
-s|@GCCLIBFLAGS@|-L@WIND_BASE@/host/@HOST_TYPE@/lib/gcc-lib/powerpc-wrs-vxworks/cygnus-2.7.2-960126/soft-float -lgcc|
-s|@DED_LD@|@TTPREFIX@ldppc|
-s|@DED_CFLAGS@|@CFLAGS@|
-s|@DEBUG_CFLAGS@|@CFLAGS@|
-# generate dwarf debug code on PPC ..
-s|@DEBUG_FLAGS@|-gdwarf|
-# remove -g option (go for dwarf)
-s|TYPE_FLAGS = -g |TYPE_FLAGS = |
-s|@GCCLIB_PATH@|@WIND_BASE@/host/@HOST_TYPE@/lib/gcc-lib/powerpc-wrs-vxworks/cygnus-2.7.2-960126/soft-float/libgcc.a|
-s|@RANLIB@|@TTPREFIX@ranlibppc|
-s|@AR@|@TTPREFIX@arppc|
-
-s|@LIB_CFLAGS@|@CFLAGS@|
-
-s|@CFLAGS@|@GENERAL_CFLAGS@ -DCPU=PPC860 -DWANT_NONBLOCKING -DHAVE_MEMMOVE -DVXWORKS -I@WIND_BASE@/target/h -mcpu=860 -fvolatile -fno-builtin -fno-for-scope -msoft-float -D_GNU_TOOL -nostdinc|
-#s|@LIB_CFLAGS@|@GENERAL_CFLAGS@ -DCPU=PPC860 -DWANT_NONBLOCKING -DHAVE_MEMMOVE -DVXWORKS -I@WIND_BASE@/target/h -mcpu=powerpc -fvolatile -fno-builtin -fno-for-scope -msoft-float -D_GNU_TOOL -nostdinc|
diff --git a/erts/autoconf/vxworks/sed.vxworks_simlinux b/erts/autoconf/vxworks/sed.vxworks_simlinux
deleted file mode 100644
index 1a2bbd6236..0000000000
--- a/erts/autoconf/vxworks/sed.vxworks_simlinux
+++ /dev/null
@@ -1,66 +0,0 @@
-#
-# %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%
-#
-# Author: Peter Andersson
-#
-# This sed program file is intended to be used when creating Makefiles for vxworks
-# from the generic Makefile.in that is found in a number of directories (see configure.vxworks)
-#
-#
-#
-#
-# Install programs etc
-s|@INSTALL@|/usr/bin/install -c|
-
-# other
-s|@host@|vxworks_simlinux|
-s|@system_type@|vxworks_simlinux|
-s|@ARCH@|simlinux|
-
-s|@CC@|GCC_EXEC_PREFIX=@WIND_BASE@/gnu/3.4.4-vxworks-6.3/@HOST_TYPE@/lib/gcc-lib/ @WIND_BASE@/gnu/3.4.4-vxworks-6.3/@HOST_TYPE@/bin/ccpentium|
-
-s|@HCC@|gcc|
-s|@GCC@|yes|
-
-s|@LD@|GCC_EXEC_PREFIX=@WIND_BASE@/gnu/3.4.4-vxworks-6.3/@HOST_TYPE@/lib/gcc-lib/ @WIND_BASE@/gnu/3.4.4-vxworks-6.3/@HOST_TYPE@/bin/ldpentium|
-
-#s|@STRIP@|GCC_EXEC_PREFIX=@WIND_BASE@/gnu/3.4.4-vxworks-6.3/@HOST_TYPE@/lib/gcc-lib/ @WIND_BASE@/gnu/3.4.4-vxworks-6.3/@HOST_TYPE@/bin/strip|
-s|@STRIP@||
-
-s|@SYMPREFIX@||
-s|@LIBS@||
-s|@GCCLIBFLAGS@|-L@WIND_BASE@/vxworks-6.3/target/lib/simlinux/SIMLINUX/common -lgcc|
-
-s|@DED_LD@|GCC_EXEC_PREFIX=@WIND_BASE@/gnu/3.4.4-vxworks-6.3/@HOST_TYPE@/lib/gcc-lib/ @WIND_BASE@/gnu/3.4.4-vxworks-6.3/@HOST_TYPE@/bin/ldpentium|
-
-s|@DED_CFLAGS@|@CFLAGS@|
-s|@DEBUG_CFLAGS@|@CFLAGS@|
-# remove -g option
-s|TYPE_FLAGS = -g |TYPE_FLAGS = |
-s|@GCCLIB_PATH@|@WIND_BASE@/vxworks-6.3/target/lib/simlinux/SIMLINUX/common/libgcc.a|
-
-s|@RANLIB@|GCC_EXEC_PREFIX=@WIND_BASE@/gnu/3.4.4-vxworks-6.3/@HOST_TYPE@/lib/gcc-lib/ @WIND_BASE@/gnu/3.4.4-vxworks-6.3/@HOST_TYPE@/bin/ranlibpentium|
-
-s|@AR@|GCC_EXEC_PREFIX=@WIND_BASE@/gnu/3.4.4-vxworks-6.3/@HOST_TYPE@/lib/gcc-lib/ @WIND_BASE@/gnu/3.4.4-vxworks-6.3/@HOST_TYPE@/bin/arpentium|
-
-# -Dasm(X)= is for beam
-s|@LIB_CFLAGS@|@CFLAGS@|
-
-s|@CFLAGS@|@GENERAL_CFLAGS@ -DCPU=SIMLINUX -DTOOL_FAMILY=gnu -DTOOL=gnu -DWANT_NONBLOCKING -DHAVE_SENS -DHAVE_MEMMOVE -DVXWORKS -DDEBUG -I@WIND_BASE@/vxworks-6.3/target/h -I@WIND_BASE@/gnu/3.4.4-vxworks-6.3/@HOST_TYPE@/lib/gcc/i586-wrs-vxworks/3.4.4/include -I@WIND_BASE@/vxworks-6.3/target/h/wrn/coreip -fvolatile -fno-builtin |
-#s|@LIB_CFLAGS@|@GENERAL_CFLAGS@ -DCPU=SIMLINUX -DTOOL_FAMILY=gnu -DTOOL=gnu -DWANT_NONBLOCKING -DHAVE_SENS -DHAVE_MEMMOVE -DVXWORKS -DDEBUG -I@WIND_BASE@/vxworks-6.3/target/h -I@WIND_BASE@/vxworks-6.3/target/h/wrn/coreip -mstrict-align -fvolatile -fno-builtin |
diff --git a/erts/autoconf/vxworks/sed.vxworks_simso b/erts/autoconf/vxworks/sed.vxworks_simso
deleted file mode 100644
index 8d15e87a1b..0000000000
--- a/erts/autoconf/vxworks/sed.vxworks_simso
+++ /dev/null
@@ -1,70 +0,0 @@
-#
-# %CopyrightBegin%
-#
-# Copyright Ericsson AB 2005-2018. All Rights Reserved.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# 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%
-#
-# Author: Peter Andersson
-#
-# This sed program file is intended to be used when creating Makefiles for vxworks
-# from the generic Makefile.in that is found in a number of directories (see configure.vxworks)
-#
-#
-#
-# Install programs etc
-s|@INSTALL@|/usr/ucb/install -c|
-
-# other
-s|@host@|vxworks_simso|
-s|@system_type@|vxworks_simso|
-s|@ARCH@|simso|
-
-# Tornado2.2: s|@CC@|@TTPREFIX@ccsimso|
-s|@CC@|GCC_EXEC_PREFIX=@WIND_BASE@/gnu/3.4.4-vxworks-6.3/@HOST_TYPE@/lib/gcc-lib/ @WIND_BASE@/gnu/3.4.4-vxworks-6.3/@HOST_TYPE@/bin/ccsparc|
-
-s|@HCC@|gcc|
-s|@GCC@|yes|
-
-# Tornado2.2: s|@LD@|@TTPREFIX@ldsimso|
-s|@LD@|GCC_EXEC_PREFIX=@WIND_BASE@/gnu/3.4.4-vxworks-6.3/@HOST_TYPE@/lib/gcc-lib/ @WIND_BASE@/gnu/3.4.4-vxworks-6.3/@HOST_TYPE@/bin/ldsparc|
-
-# Tornado2.2: s|@STRIP@|@TTPREFIX@stripsimso|
-s|@STRIP@|GCC_EXEC_PREFIX=@WIND_BASE@/gnu/3.4.4-vxworks-6.3/@HOST_TYPE@/lib/gcc-lib/ @WIND_BASE@/gnu/3.4.4-vxworks-6.3/@HOST_TYPE@/bin/stripsparc|
-
-s|@SYMPREFIX@||
-s|@LIBS@||
-s|@GCCLIBFLAGS@|-L@WIND_BASE@/vxworks-6.3/target/lib/simso/SIMSPARCSOLARIS/common -lgcc|
-
-# Tornado2.2: s|@DED_LD@|@TTPREFIX@ldsimso|
-s|@DED_LD@|GCC_EXEC_PREFIX=@WIND_BASE@/gnu/3.4.4-vxworks-6.3/@HOST_TYPE@/lib/gcc-lib/ @WIND_BASE@/gnu/3.4.4-vxworks-6.3/@HOST_TYPE@/bin/ldsparc|
-
-s|@DED_CFLAGS@|@CFLAGS@|
-s|@DEBUG_CFLAGS@|@CFLAGS@|
-# remove -g option
-s|TYPE_FLAGS = -g |TYPE_FLAGS = |
-s|@GCCLIB_PATH@|@WIND_BASE@/vxworks-6.3/target/lib/simso/SIMSPARCSOLARIS/common/libgcc.a|
-
-# Tornado2.2: s|@RANLIB@|@TTPREFIX@ranlibsimso|
-s|@RANLIB@|GCC_EXEC_PREFIX=@WIND_BASE@/gnu/3.4.4-vxworks-6.3/@HOST_TYPE@/lib/gcc-lib/ @WIND_BASE@/gnu/3.4.4-vxworks-6.3/@HOST_TYPE@/bin/ranlibsparc|
-
-# Tornado2.2: s|@AR@|arsimso|
-s|@AR@|GCC_EXEC_PREFIX=@WIND_BASE@/gnu/3.4.4-vxworks-6.3/@HOST_TYPE@/lib/gcc-lib/ @WIND_BASE@/gnu/3.4.4-vxworks-6.3/@HOST_TYPE@/bin/arsparc|
-
-# -Dasm(X)= is for beam
-s|@LIB_CFLAGS@|@CFLAGS@|
-
-s|@CFLAGS@|@GENERAL_CFLAGS@ -DCPU=SIMSPARCSOLARIS -DTOOL_FAMILY=gnu -DTOOL=gnu -DWANT_NONBLOCKING -DHAVE_SENS -DHAVE_MEMMOVE -DVXWORKS -DDEBUG -I@WIND_BASE@/vxworks-6.3/target/h -I@WIND_BASE@/gnu/3.4.4-vxworks-6.3/@HOST_TYPE@/lib/gcc/sparc-wrs-vxworks/3.4.4/include -I@WIND_BASE@/vxworks-6.3/target/h/wrn/coreip -fvolatile -fno-builtin |
-#s|@LIB_CFLAGS@|@GENERAL_CFLAGS@ -DCPU=SIMSPARCSOLARIS -DTOOL_FAMILY=gnu -DTOOL=gnu -DWANT_NONBLOCKING -DHAVE_SENS -DHAVE_MEMMOVE -DVXWORKS -DDEBUG -I@WIND_BASE@/vxworks-6.3/target/h -I@WIND_BASE@/vxworks-6.3/target/h/wrn/coreip -mstrict-align -fvolatile -fno-builtin |
diff --git a/erts/autoconf/vxworks/sed.vxworks_sparc b/erts/autoconf/vxworks/sed.vxworks_sparc
deleted file mode 100644
index 118b01d16d..0000000000
--- a/erts/autoconf/vxworks/sed.vxworks_sparc
+++ /dev/null
@@ -1,40 +0,0 @@
-#
-# %CopyrightBegin%
-#
-# Copyright Ericsson AB 1997-2018. All Rights Reserved.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# 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%
-#
-# Author: Patrik Winroth
-#
-# This sed program file is intended to be used when creating Makefiles for vxworks
-# from the generic Makefile.in that is found in a number of directories (see configure.vxworks)
-#
-
-# ccsparc -O2 doesn't work when compiling "rundir"/gc.c - signal 11 is generated when trying
-# therefore it is compiled with -O1 instead, which works - get a new ccsparc !
-s/\$(COMPILE\.emu) -o \$@ -c gc\.c/$(CC) @CFLAGS@ @DEFS@ -O1 $(BEAM_MODE) -I$(SYSDIR) -I$(EMUDIR) -I. $(CPPFLAGS) -c -o $@ -c gc.c/
-s/@host@/vxworks_sparc/
-s/@system_type@/vxworks_sparc/
-s/@CC@/\/home\/gandalf\/bsproj\/tools\/vw-gnu\/solaris.sparc\/bin\/ccsparc/
-s/@HCC@/gcc/
-s/@GCC@/yes/
-s/@LD@/\/home\/gandalf\/bsproj\/tools\/vw-gnu\/solaris.sparc\/bin\/ldsparc/
-s/@DEBUG_FLAGS@/-g/
-s/@GCCLIB_PATH@/\/home\/gandalf\/bsproj\/tools\/vw-gnu\/solaris.sparc\/lib\/gcc-lib\/sparc-wrs-vxworks\/cygnus-2.2.3.1\/libgcc.a/
-s/@RANLIB@/ranlibsparc/
-s/@AR@/arsparc/
-s/@CFLAGS@/-I\/home\/gandalf\/bsproj\/BS.2\/UOS\/vw\/5.2\/h -DWANT_NONBLOCKING -DHAVE_MEMMOVE -DCPU=SPARC -DVXWORKS -fno-builtin -nostdinc/
-
diff --git a/erts/configure.in b/erts/configure.in
index 609c457393..46c90a152a 100644
--- a/erts/configure.in
+++ b/erts/configure.in
@@ -447,6 +447,12 @@ dnl ---------------------------------------------------------------------
dnl NOTE: CPPFLAGS will be included in CFLAGS at the end
case $host_os in
linux*) CPPFLAGS="$CPPFLAGS -D_GNU_SOURCE";;
+ aix*|os400*)
+ # * _ALL_SOURCE: Required to get the winsize structure for TIOCSWINSZ.
+ # * _LINUX_SOURCE_COMPAT: Not required, but makes some libc functions
+ # behave closer to glibc assumptions.
+ CPPFLAGS="$CPPFLAGS -D_ALL_SOURCE -D_LINUX_SOURCE_COMPAT"
+ ;;
win32)
# The ethread library requires _WIN32_WINNT of at least 0x0403.
# -D_WIN32_WINNT=* from CPPFLAGS is saved in ETHR_DEFS.
@@ -577,6 +583,17 @@ else
WERRORFLAGS=""
fi
+AC_MSG_CHECKING([C99 support])
+
+AC_COMPILE_IFELSE([AC_LANG_PROGRAM([],[
+#if __STDC_VERSION__ < 199901L
+ #error "Not C99"
+#endif])],
+ [AC_MSG_RESULT([yes])],
+ [AC_MSG_RESULT([no])
+ CFLAGS="-std=gnu99 $CFLAGS"
+ DEBUG_CFLAGS="-std=gnu99 $DEBUG_CFLAGS"])
+
AC_MSG_CHECKING([CFLAGS for -O switch])
case "$CFLAGS" in
*-O*) AC_MSG_RESULT([yes]) ;;
@@ -918,13 +935,10 @@ dnl
HCC='$(CC)' AC_SUBST(HCC)
HCFLAGS="" AC_SUBST(HCFLAGS)
HCFLAGS="$HCFLAGS -I${ERL_TOP}/erts/$host"
-vxworks_reclaim="" AC_SUBST(vxworks_reclaim)
dnl We want to use $(CC) as linker for the emulator regardless of
dnl what the user say. This might not be the right way to do it, but
dnl for now that is the way we do it.
-USER_LD=$LD
-USER_LDFLAGS="$LDFLAGS"
LD='$(CC)'
case $host_os in
darwin*)
@@ -949,7 +963,7 @@ dnl AC_CYGWIN is deprecated
AC_EXEEXT
AC_OBJEXT
-dnl This is the os flavour, should be unix, ose, vxworks or win32
+dnl This is the os flavour, should be unix or win32
case $host in
win32)
ERLANG_OSTYPE=win32 ;;
@@ -972,7 +986,7 @@ AC_SUBST(ERLANG_OSTYPE)
AC_MSG_CHECKING(for extra flags needed to export symbols)
DEXPORT=""
case $host_os in
- aix4*)
+ aix*|os400*)
DEXPORT=-Wl,-bexpall,-brtl
;;
bsdi*)
@@ -1479,7 +1493,7 @@ if test "$have_gethostbyname_r" = yes; then
AC_DEFINE(HAVE_GETHOSTBYNAME_R, GHBN_R_SOLARIS,
[Define to flavour of gethostbyname_r])
;;
- aix4*)
+ aix*|os400*)
# AIX version also needs "struct hostent_data" defn
AC_TRY_COMPILE([#include <netdb.h>],
[struct hostent_data hd;],
@@ -1598,21 +1612,15 @@ AC_CHECK_HEADERS(fcntl.h limits.h unistd.h syslog.h dlfcn.h ieeefp.h \
AC_CHECK_MEMBERS([struct ifreq.ifr_hwaddr], [], [],
[#ifdef __WIN32__
#else
- #ifdef VXWORKS
- #else
#include <net/if.h>
#endif
- #endif
])
AC_CHECK_MEMBERS([struct ifreq.ifr_enaddr], [], [],
[#ifdef __WIN32__
#else
- #ifdef VXWORKS
- #else
#include <net/if.h>
#endif
- #endif
])
dnl ----------------------------------------------------------------------
@@ -2098,7 +2106,7 @@ AC_CHECK_FUNCS([ieee_handler fpsetmask finite isnan isinf res_gethostbyname dlop
gethrtime localtime_r gmtime_r inet_pton mprotect \
mmap mremap memcpy mallopt sbrk _sbrk __sbrk brk _brk __brk \
flockfile fstat strlcpy strlcat setsid posix2time time2posix \
- setlocale nl_langinfo poll mlockall ppoll])
+ setlocale nl_langinfo poll mlockall ppoll vsyslog])
AC_MSG_CHECKING([for isfinite])
AC_TRY_LINK([#include <math.h>],
diff --git a/erts/doc/src/.gitignore b/erts/doc/src/.gitignore
new file mode 100644
index 0000000000..abe9a7d858
--- /dev/null
+++ b/erts/doc/src/.gitignore
@@ -0,0 +1,3 @@
+ref_man.xml
+specs.xml
+part.xml \ No newline at end of file
diff --git a/erts/doc/src/Makefile b/erts/doc/src/Makefile
index bb96293947..3e2eb80b50 100644
--- a/erts/doc/src/Makefile
+++ b/erts/doc/src/Makefile
@@ -37,15 +37,15 @@ RELSYSDIR = $(RELEASE_PATH)/$(APPLICATION)-$(VSN)
# Target Specs
# ----------------------------------------------------
XML_APPLICATION_FILES = ref_man.xml
-XML_REF1_FILES = epmd.xml \
- erl.xml \
- erlc.xml \
- escript.xml \
- werl.xml \
- erlsrv.xml \
- start_erl.xml \
- run_erl.xml \
- start.xml
+XML_REF1_FILES = epmd_cmd.xml \
+ erl_cmd.xml \
+ erlc_cmd.xml \
+ escript_cmd.xml \
+ werl_cmd.xml \
+ erlsrv_cmd.xml \
+ start_erl_cmd.xml \
+ run_erl_cmd.xml \
+ start_cmd.xml
ifeq ($(USE_ESOCK), yes)
XML_REF3_ESOCK_EFILES = socket.xml
@@ -72,13 +72,16 @@ XML_REF3_EFILES = \
zlib.xml \
$(XML_REF3_ESOCK_EFILES)
-XML_REF3_FILES = \
- $(XML_REF3_EFILES) \
+XML_REF3_CREF = \
driver_entry.xml \
erl_nif.xml \
erl_driver.xml \
erts_alloc.xml
+XML_REF3_FILES = \
+ $(XML_REF3_EFILES) \
+ $(XML_REF3_CREF)
+
XML_PART_FILES = \
part.xml internal.xml
@@ -96,7 +99,6 @@ XML_INTERNAL_FILES = \
SuperCarrier.xml \
CountingInstructions.xml
-
XML_CHAPTER_FILES = \
introduction.xml \
tty.xml \
@@ -118,32 +120,24 @@ TOPDOCDIR=../../../doc
BOOK_FILES = book.xml
-GIF_FILES = \
+IMAGE_FILES = \
erl_ext_fig.gif
XML_FILES = \
$(BOOK_FILES) $(XML_CHAPTER_FILES) \
$(XML_PART_FILES) $(XML_REF3_FILES) $(XML_REF1_FILES) $(XML_APPLICATION_FILES)
+HTML_EXTRA_FILES = $(ERL_TOP)/erts/example/time_compat.erl \
+ $(ERL_TOP)/lib/kernel/examples/gen_tcp_dist/src/gen_tcp_dist.erl
+
XML_GEN_FILES = $(XML_INTERNAL_FILES:%=$(XMLDIR)/%)
-# ----------------------------------------------------
+NO_CHUNKS = $(XML_REF3_CREF) erl_tracer.xml
-HTML_FILES = $(XML_APPLICATION_FILES:%.xml=$(HTMLDIR)/%.html) \
- $(XML_PART_FILES:%.xml=$(HTMLDIR)/%.html)
+# ----------------------------------------------------
-INFO_FILE = ../../info
INFO_FILE_SRC = ../../info.src
-MAN1_FILES = $(XML_REF1_FILES:%.xml=$(MAN1DIR)/%.1)
-MAN3_FILES = $(XML_REF3_FILES:%.xml=$(MAN3DIR)/%.3)
-
-HTML_REF_MAN_FILE = $(HTMLDIR)/index.html
-
-TOP_PDF_FILE = $(PDFDIR)/$(APPLICATION)-$(VSN).pdf
-
-SPECS_FILES = $(XML_REF3_EFILES:%.xml=$(SPECDIR)/specs_%.xml)
-
TOP_SPECS_FILE = specs.xml
XML_FIGURE_DIR = $(XMLDIR)/figures
@@ -153,60 +147,24 @@ PNG_FILES = $(notdir $(INTERNAL_DOC_PNG_FILES))
XMLDIR_PNG_FILES = $(PNG_FILES:%=$(XML_FIGURE_DIR)/%)
# ----------------------------------------------------
-# FLAGS
-# ----------------------------------------------------
-XML_FLAGS +=
-
-KERNEL_SRC=$(ERL_TOP)/lib/kernel/src
-KERNEL_INCLUDE=$(ERL_TOP)/lib/kernel/include
-SPECS_FLAGS = -I$(KERNEL_SRC) -I$(KERNEL_INCLUDE)
-
-# ----------------------------------------------------
# Targets
# ----------------------------------------------------
-_create_dirs := $(shell mkdir -p $(XML_FIGURE_DIR))
+include $(ERL_TOP)/make/doc.mk
-$(HTMLDIR)/%.gif: %.gif
- $(INSTALL_DATA) $< $@
+_create_dirs := $(shell mkdir -p $(XML_FIGURE_DIR))
$(XML_FIGURE_DIR)/%.png: ../../emulator/internal_doc/figures/%.png
$(INSTALL_DATA) $< $@
-docs: part ref_man specs figures man pdf html $(INFO_FILE)
-
-$(TOP_PDF_FILE): $(XML_FILES)
-
-pdf: $(TOP_PDF_FILE)
-
-html: gifs $(HTML_REF_MAN_FILE)
-
-man: $(MAN1_FILES) $(MAN3_FILES)
-
-ref_man: ref_man.xml
-part: part.xml
-specs: specs.xml
-
-gifs: $(GIF_FILES:%=$(HTMLDIR)/%)
+html: figures
$(INFO_FILE): $(INFO_FILE_SRC) $(ERL_TOP)/make/$(TARGET)/otp.mk
sed -e 's;%RELEASE%;$(SYSTEM_VSN);' $(INFO_FILE_SRC) > $(INFO_FILE)
figures: $(XMLDIR_PNG_FILES)
-debug opt:
-
-ldocs: xmllint local_docs
-
-clean:
- rm -rf $(HTMLDIR)/*
- rm -rf $(XMLDIR)
- rm -f $(MAN1DIR)/*
- rm -f $(MAN3DIR)/*
- rm -f $(TOP_PDF_FILE) $(TOP_PDF_FILE:%.pdf=%.fo)
- rm -f $(SPECDIR)/*
- rm -f errs core *~
-
-$(SPECDIR)/specs_%.xml:
+## This rule generate dummy specs for all XML_REF3_CREF's
+$(XML_REF3_CREF:%.xml=$(SPECDIR)/specs_%.xml): $(@:%.xml=%.xml)
$(gen_verbose)escript $(SPECS_EXTRACTOR) $(SPECS_FLAGS) \
-o$(dir $@) -module $(patsubst $(SPECDIR)/specs_%.xml,%,$@)
@@ -229,25 +187,10 @@ specs.xml: specs.xml.src
# ----------------------------------------------------
# Release Target
# ----------------------------------------------------
-include $(ERL_TOP)/make/otp_release_targets.mk
-release_docs_spec: docs
- $(INSTALL_DIR) "$(RELSYSDIR)/doc/pdf"
- $(INSTALL_DATA) $(TOP_PDF_FILE) "$(RELSYSDIR)/doc/pdf"
- $(INSTALL_DIR) "$(RELSYSDIR)/doc/html"
+release_html_spec: release_figures
+
+release_figures:
$(INSTALL_DIR) "$(RELSYSDIR)/doc/html/figures"
- $(INSTALL_DATA) $(HTMLDIR)/* \
- "$(RELSYSDIR)/doc/html"
$(INSTALL_DATA) $(XMLDIR)/figures/* \
"$(RELSYSDIR)/doc/html/figures"
- $(INSTALL_DATA) $(ERL_TOP)/erts/example/time_compat.erl \
- "$(RELSYSDIR)/doc/html"
- $(INSTALL_DATA) $(ERL_TOP)/lib/kernel/examples/gen_tcp_dist/src/gen_tcp_dist.erl \
- "$(RELSYSDIR)/doc/html"
- $(INSTALL_DATA) $(INFO_FILE) "$(RELSYSDIR)"
- $(INSTALL_DIR) "$(RELEASE_PATH)/man/man3"
- $(INSTALL_DATA) $(MAN3DIR)/* "$(RELEASE_PATH)/man/man3"
- $(INSTALL_DIR) "$(RELEASE_PATH)/man/man1"
- $(INSTALL_DATA) $(MAN1_FILES) "$(RELEASE_PATH)/man/man1"
-
-release_spec:
diff --git a/erts/doc/src/alt_dist.xml b/erts/doc/src/alt_dist.xml
index f72e8acd2c..f2155bcc4d 100644
--- a/erts/doc/src/alt_dist.xml
+++ b/erts/doc/src/alt_dist.xml
@@ -785,12 +785,14 @@
<taglist>
<tag><c>DFLAG_DIST_HDR_ATOM_CACHE</c></tag>
<item>Do not use atom cache over this connection.</item>
+ <tag><c>DFLAG_FRAGMENTS</c></tag>
+ <item>Split large distribution messages into multiple fragments.</item>
</taglist>
- <p>Use function <c>dist_util:strict_order_flags/0</c> to get all flags
- for features that require strict order delivery.</p>
<p>
This flag field is optional.
</p>
+ <p>See also <seealso marker="#distribution_data_delivery">
+ Distribution Data Delivery</seealso></p>
</item>
<tag><marker id="hs_data_require_flags"/><c>require_flags</c></tag>
diff --git a/erts/doc/src/epmd.xml b/erts/doc/src/epmd_cmd.xml
index 75353cbc07..ee886bd68e 100644
--- a/erts/doc/src/epmd.xml
+++ b/erts/doc/src/epmd_cmd.xml
@@ -11,7 +11,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
-
+
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
@@ -19,11 +19,11 @@
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-
+
</legalnotice>
<title>epmd</title>
- <prepared>Claes Wikstr&ouml;m</prepared>
+ <prepared>Claes Wikstr&ouml;m</prepared>
<responsible></responsible>
<docno>1</docno>
<approved></approved>
@@ -295,9 +295,8 @@
<title>Logging</title>
<p>On some operating systems <em>syslog</em> will be used for
error reporting when <c>epmd</c> runs as a daemon. To enable
- the error logging, you must edit the
- <path unix="" windows="">/etc/syslog.conf</path> file and add an
- entry:</p>
+ the error logging, you must edit the /etc/syslog.conf file and
+ add an entry:</p>
<code type="none"><![CDATA[
!epmd
@@ -334,5 +333,3 @@
<p>To restrict access further, firewall software must be used.</p>
</section>
</comref>
-
-
diff --git a/erts/doc/src/erl.xml b/erts/doc/src/erl_cmd.xml
index a37707f7f9..64663ce95e 100644
--- a/erts/doc/src/erl.xml
+++ b/erts/doc/src/erl_cmd.xml
@@ -11,7 +11,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
-
+
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
@@ -181,7 +181,7 @@
which is used to start the system; see
<seealso marker="init"><c>init(3)</c></seealso>. Unless
<c><![CDATA[File]]></c> contains an absolute path, the system searches
- for <c><![CDATA[File.boot]]></c> in the current and
+ for <c><![CDATA[File.boot]]></c> in the current and
<c><![CDATA[$ROOT/bin]]></c> directories.</p>
<p>Defaults to <c><![CDATA[$ROOT/bin/start.boot]]></c>.</p>
</item>
@@ -207,9 +207,9 @@
<p>Not recommended; use <seealso marker="erlc"><c>erlc</c></seealso>
instead.</p>
</item>
- <tag><c><![CDATA[-config Config]]></c></tag>
+ <tag><c><![CDATA[-config Config [Config]]]></c></tag>
<item>
- <p>Specifies the name of a configuration file,
+ <p>Specifies the name of one or more configuration files,
<c><![CDATA[Config.config]]></c>, which is used to configure
applications; see
<seealso marker="kernel:app"><c>app(4)</c></seealso> and
@@ -575,8 +575,8 @@
<tag><marker id="async_thread_pool_size"/><c><![CDATA[+A size]]></c></tag>
<item>
<p>Sets the number of threads in async thread pool. Valid range
- is 0-1024. The async thread pool is used by linked-in drivers to
- handle work that may take a very long time. Since OTP-21 there are
+ is 1-1024. The async thread pool is used by linked-in drivers to
+ handle work that may take a very long time. Since OTP 21 there are
very few linked-in drivers in the default Erlang/OTP distribution
that uses the async thread pool. Most of them have been migrated to
dirty IO schedulers. Defaults to 1.</p>
@@ -852,7 +852,7 @@
<p>Sets the range of characters that the system considers printable in
heuristic detection of strings. This typically affects the shell,
debugger, and <c>io:format</c> functions (when <c>~tp</c> is used in
- the format string).</p>
+ the format string).</p>
<p>Two values are supported for <c>Range</c>:</p>
<taglist>
<tag><c>latin1</c></tag>
@@ -948,14 +948,19 @@
<c><![CDATA[+S Schedulers:SchedulerOnline]]></c></tag>
<item>
<p>Sets the number of scheduler threads to create and scheduler threads
- to set online. The maximum for both
- values is 1024. If the Erlang runtime system is able to determine the
- number of logical processors configured and logical processors
- available, <c>Schedulers</c> defaults to logical processors
- configured, and <c>SchedulersOnline</c> defaults to logical processors
- available; otherwise the default values are 1. <c>Schedulers</c> can
- be omitted if <c>:SchedulerOnline</c> is not and conversely. The
- number of schedulers online can be changed at runtime through
+ to set online. The maximum for both values is 1024. If the Erlang
+ runtime system is able to determine the number of logical processors
+ configured and logical processors available, <c>Schedulers</c>
+ defaults to logical processors configured, and
+ <c>SchedulersOnline</c> defaults to logical processors available;
+ otherwise the default values are 1. If the emulator detects that it
+ is subject to a <seealso marker="erlang#system_info_cpu_quota">CPU
+ quota</seealso>, the default value for <c>SchedulersOnline</c> will
+ be limited accordingly.</p>
+ <p>
+ <c>Schedulers</c> can be omitted if <c>:SchedulerOnline</c> is not
+ and conversely. The number of schedulers online can be changed at
+ runtime through
<seealso marker="erlang#system_flag_schedulers_online">
<c>erlang:system_flag(schedulers_online,
SchedulersOnline)</c></seealso>.</p>
@@ -1270,7 +1275,7 @@
node identifiers can be omitted. If omitted, the thread ID
defaults to <c>t0</c>, the core ID defaults to <c>c0</c>,
the processor ID defaults to <c>p0</c>, and the node ID is
- left undefined. Either each logical processor must
+ left undefined. Either each logical processor must
belong to only one NUMA node, or no logical
processors must belong to any NUMA nodes.</p>
<p>Both increasing and decreasing <c><![CDATA[<IdRange>]]></c>s
diff --git a/erts/doc/src/erl_dist_protocol.xml b/erts/doc/src/erl_dist_protocol.xml
index f924c8a70b..eeb0049f4f 100644
--- a/erts/doc/src/erl_dist_protocol.xml
+++ b/erts/doc/src/erl_dist_protocol.xml
@@ -109,7 +109,8 @@
<title>Register a Node in EPMD</title>
<p>When a distributed node is started it registers itself in the EPMD.
The message <c>ALIVE2_REQ</c> described below is sent from the node to
- the EPMD. The response from the EPMD is <c>ALIVE2_RESP</c>.</p>
+ the EPMD. The response from the EPMD is <c>ALIVE2_X_RESP</c> (or
+ <c>ALIVE2_RESP</c>).</p>
<table align="left">
<row>
@@ -154,13 +155,13 @@
</item>
<tag><c>HighestVersion</c></tag>
<item>
- <p>The highest distribution version that this node can handle.
- The value in Erlang/OTP R6B and later is 5.</p>
+ <p>The highest distribution protocol version this node can handle.
+ The value in OTP 23 and later is 6. Older nodes only support version 5.</p>
</item>
<tag><c>LowestVersion</c></tag>
<item>
<p>The lowest distribution version that this node can handle.
- The value in Erlang/OTP R6B and later is 5.</p>
+ Should be 5 to support connections to nodes older than OTP 23.</p>
</item>
<tag><c>Nlen</c></tag>
<item>
@@ -184,7 +185,24 @@
node is a distributed node. When the connection is closed,
the node is automatically unregistered from the EPMD.</p>
- <p>The response message <c>ALIVE2_RESP</c> is as follows:</p>
+ <p>The response message is either <c>ALIVE2_X_RESP</c> or
+ <c>ALIVE2_RESP</c> depending on distribution version. If both the node
+ and EPMD support distribution version 6 then the response is
+ <c>ALIVE2_X_RESP</c> otherwise it is the older <c>ALIVE2_RESP</c>:</p>
+
+ <table align="left">
+ <row>
+ <cell align="center">1</cell>
+ <cell align="center">1</cell>
+ <cell align="center">4</cell>
+ </row>
+ <row>
+ <cell align="center"><c>118</c></cell>
+ <cell align="center"><c>Result</c></cell>
+ <cell align="center"><c>Creation</c></cell>
+ </row>
+ <tcaption>ALIVE2_X_RESP (118) with 32 bit creation</tcaption>
+ </table>
<table align="left">
<row>
@@ -197,7 +215,7 @@
<cell align="center"><c>Result</c></cell>
<cell align="center"><c>Creation</c></cell>
</row>
- <tcaption>ALIVE2_RESP (121)</tcaption>
+ <tcaption>ALIVE2_RESP (121) with 16-bit creation</tcaption>
</table>
<p>Result = 0 -> ok, result &gt; 0 -> error.</p>
@@ -531,8 +549,14 @@ io:format("old/unused name ~ts at port ~p, fd = ~p ~n",
<section>
<marker id="distribution_handshake"/>
<title>Distribution Handshake</title>
- <p>This section describes the distribution handshake protocol introduced
- in Erlang/OTP R6. The handshake has remained almost the same since then.</p>
+ <p>
+ This section describes the distribution handshake protocol used between
+ nodes to establishing a connection. The protocol was introduced in
+ Erlang/OTP R6 and has remained unchanged until OTP 23. The changes made in
+ OTP 23 were designed to be compatible with the older protocol
+ version. That is an old node can still connect toward a new node and vice
+ versa.
+ </p>
<section>
<title>General</title>
@@ -599,19 +623,68 @@ io:format("old/unused name ~ts at port ~p, fd = ~p ~n",
<tag>2) <c>send_name</c>/<c>receive_name</c></tag>
<item>
<p><c>A</c> sends an initial identification to <c>B</c>, which
- receives the message. The message looks as follows (every "square"
- is one byte and the packet header is removed):</p>
- <pre>
-+---+--------+--------+-----+-----+-----+-----+-----+-----+-...-+-----+
-|'n'|Version0|Version1|Flag0|Flag1|Flag2|Flag3|Name0|Name1| ... |NameN|
-+---+--------+--------+-----+-----+-----+-----+-----+-----+-... +-----+</pre>
- <p>'n' is the message tag. 'Version0' and 'Version1' is the
- distribution version selected by <c>A</c>, based on information
- from the EPMD. (16-bit big-endian) 'Flag0' ... 'Flag3' are
- capability flags, the capabilities are defined in
- <c>$ERL_TOP/lib/kernel/include/dist.hrl</c>. (32-bit big-endian)
- 'Name0' ... 'NameN' is the full node name of <c>A</c>, as a string
- of bytes (the packet length denotes how long it is).</p>
+ receives the message. The message can have two different formats
+ which looks as follows (the packet headers are removed):
+ </p>
+ <table align="left">
+ <row>
+ <cell align="center">1</cell>
+ <cell align="center">2</cell>
+ <cell align="center">4</cell>
+ <cell align="center">Nlen</cell>
+ </row>
+ <row>
+ <cell align="center"><c>'n'</c></cell>
+ <cell align="center"><c>Version=5</c></cell>
+ <cell align="center"><c>Flags</c></cell>
+ <cell align="center"><c>Name</c></cell>
+ </row>
+ <tcaption>Old send_name ('n') for protocol version 5</tcaption>
+ </table>
+
+ <table align="left">
+ <row>
+ <cell align="center">1</cell>
+ <cell align="center">8</cell>
+ <cell align="center">4</cell>
+ <cell align="center">2</cell>
+ <cell align="center">Nlen</cell>
+ </row>
+ <row>
+ <cell align="center"><c>'N'</c></cell>
+ <cell align="center"><c>Flags</c></cell>
+ <cell align="center"><c>Creation</c></cell>
+ <cell align="center"><c>Nlen</c></cell>
+ <cell align="center"><c>Name</c></cell>
+ </row>
+ <tcaption>New send_name ('N') for protocol version 6</tcaption>
+ </table>
+
+ <p>
+ The old <c>send_name</c> format is sent from nodes only supporting version 5
+ or to nodes that might only support version 5. The <c>Version</c> is
+ a 16-bit big endian integer and <em>must</em> always have the value 5, even
+ if node <c>A</c> supports version 6. <c>Flags</c> are the
+ <seealso marker="#dflags">capability flags</seealso>
+ of node <c>A</c> in 32-bit big endian. The flag bit
+ <seealso marker="#DFLAG_HANDSHAKE_23"><c>DFLAG_HANDSHAKE_23</c></seealso>
+ should be set if node <c>A</c> supports version 6.
+ <c>Name</c> is the full node name of <c>A</c>, as a string of bytes
+ (the packet length denotes how long it is).
+ </p>
+ <p>
+ The new <c>send_name</c> is only sent from nodes supporting version 6 to
+ nodes known to support version 6. <c>Flags</c> are the
+ <seealso marker="#dflags">capability flags</seealso> of node
+ <c>A</c> in 64-bit big endian. The flag bit
+ <seealso marker="#DFLAG_HANDSHAKE_23"><c>DFLAG_HANDSHAKE_23</c></seealso>
+ must always be set. <c>Creation</c> is the node incarnation
+ identifier used by node <c>A</c> to create its pids, ports and
+ references. <c>Name</c> is the full node name of <c>A</c>, as a
+ string of bytes. <c>Nlen</c> is the byte length of the node name in
+ 16-bit big endian. Any extra data after the node <c>Name</c> must be
+ accepted and ignored.
+ </p>
</item>
<tag>3) <c>recv_status</c>/<c>send_status</c></tag>
<item>
@@ -648,13 +721,19 @@ io:format("old/unused name ~ts at port ~p, fd = ~p ~n",
node <c>B</c>. See step 3B below.</p>
</item>
</taglist>
- <p>The format of the status message is as follows:</p>
- <pre>
-+---+-------+-------+-...-+-------+
-|'s'|Status0|Status1| ... |StatusN|
-+---+-------+-------+-...-+-------+</pre>
- <p>'s' is the message tag. 'Status0' ... 'StatusN' is the status as a
- string (not terminated).</p>
+ <table align="left">
+ <row>
+ <cell align="center">1</cell>
+ <cell align="center">Slen</cell>
+ </row>
+ <row>
+ <cell align="center"><c>'s'</c></cell>
+ <cell align="center"><c>Status</c></cell>
+ </row>
+ <tcaption>The format of the status message</tcaption>
+ </table>
+ <p>'s' is the message tag. <c>Status</c> is the status as a
+ string (not null terminated).</p>
</item>
<tag>3B) <c>send_status</c>/<c>recv_status</c></tag>
<item>
@@ -670,39 +749,136 @@ io:format("old/unused name ~ts at port ~p, fd = ~p ~n",
handshake continues with <c>B</c> sending <c>A</c> another message,
the challenge. The challenge contains the same type of information
as the "name" message initially sent from <c>A</c> to <c>B</c>, plus
- a 32-bit challenge:</p>
- <pre>
-+---+--------+--------+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-...-+-----+
-|'n'|Version0|Version1|Flag0|Flag1|Flag2|Flag3|Chal0|Chal1|Chal2|Chal3|Name0|Name1| ... |NameN|
-+---+--------+--------+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-... +-----+</pre>
- <p>'Chal0' ... 'Chal3' is the challenge as a 32-bit big-endian integer
- and the other fields are <c>B</c>'s version, flags, and full node
- name.</p>
+ a 32-bit challenge. The challenge message can have two different
+ formats:
+ </p>
+ <table align="left">
+ <row>
+ <cell align="center">1</cell>
+ <cell align="center">2</cell>
+ <cell align="center">4</cell>
+ <cell align="center">4</cell>
+ <cell align="center">Nlen</cell>
+ </row>
+ <row>
+ <cell align="center"><c>'n'</c></cell>
+ <cell align="center"><c>Version=5</c></cell>
+ <cell align="center"><c>Flags</c></cell>
+ <cell align="center"><c>Challenge</c></cell>
+ <cell align="center"><c>Name</c></cell>
+ </row>
+ <tcaption>The old challenge message format (version 5)</tcaption>
+ </table>
+ <table align="left">
+ <row>
+ <cell align="center">1</cell>
+ <cell align="center">8</cell>
+ <cell align="center">4</cell>
+ <cell align="center">4</cell>
+ <cell align="center">2</cell>
+ <cell align="center">Nlen</cell>
+ </row>
+ <row>
+ <cell align="center"><c>'N'</c></cell>
+ <cell align="center"><c>Flags</c></cell>
+ <cell align="center"><c>Challenge</c></cell>
+ <cell align="center"><c>Creation</c></cell>
+ <cell align="center"><c>Nlen</c></cell>
+ <cell align="center"><c>Name</c></cell>
+ </row>
+ <tcaption>The new challenge message format (version 6)</tcaption>
+ </table>
+ <p>
+ The old challenge message is sent from old <c>B</c> nodes
+ (supporting only version 5) or if node <c>A</c> had not capability flag
+ <seealso marker="#DFLAG_HANDSHAKE_23"><c>DFLAG_HANDSHAKE_23</c></seealso>
+ set. The <c>Version</c> is a 16-bit big endian integer and
+ <c>must</c> always have the value 5.
+ </p>
+ <p>
+ The new challenge message is sent from new <c>B</c> nodes if node
+ <c>A</c> had capability flag <seealso marker="#DFLAG_HANDSHAKE_23">
+ <c>DFLAG_HANDSHAKE_23</c></seealso> set. Any extra data after the
+ node <c>Name</c> must be accepted and ignored.
+ </p>
+ <p>
+ <c>Challenge</c> is a 32-bit big-endian integer. The other fields
+ are node <c>B</c>'s flags, creation and full node name, similar to
+ the <c>send_name</c> message.
+ </p>
+ </item>
+
+ <tag>4B) <c>send_complement</c>/<c>recv_complement</c></tag>
+ <item>
+ <p>
+ The complement message, from <c>A</c> to <c>B</c>, is only sent if
+ node <c>A</c> initially sent an old name message and received back a
+ new challenge message from node <c>B</c>. It contains complementary
+ information missing in the initial old name message from node <c>A</c>.
+ </p>
+ <table align="left">
+ <row>
+ <cell align="center">1</cell>
+ <cell align="center">4</cell>
+ <cell align="center">4</cell>
+ </row>
+ <row>
+ <cell align="center"><c>'c'</c></cell>
+ <cell align="center"><c>FlagsHigh</c></cell>
+ <cell align="center"><c>Creation</c></cell>
+ </row>
+ <tcaption>The complement message</tcaption>
+ </table>
+ <p>
+ <c>FlagsHigh</c> are the high capability flags (bit 33-64) of node
+ <c>A</c> as a 32-bit big endian integer. <c>Creation</c> is the
+ incarnation identifier of node <c>A</c>.
+ </p>
</item>
+
<tag>5) <c>send_challenge_reply</c>/<c>recv_challenge_reply</c></tag>
<item>
<p>Now <c>A</c> has generated a digest and its own challenge. Those
are sent together in a package to <c>B</c>:</p>
- <pre>
-+---+-----+-----+-----+-----+-----+-----+-----+-----+-...-+------+
-|'r'|Chal0|Chal1|Chal2|Chal3|Dige0|Dige1|Dige2|Dige3| ... |Dige15|
-+---+-----+-----+-----+-----+-----+-----+-----+-----+-...-+------+</pre>
- <p>'r' is the tag. 'Chal0' ... 'Chal3' is <c>A</c>'s challenge for
- <c>B</c> to handle. 'Dige0' ... 'Dige15' is the digest that <c>A</c>
- constructed from the challenge <c>B</c> sent in the previous
- step.</p>
+ <table align="left">
+ <row>
+ <cell align="center">1</cell>
+ <cell align="center">4</cell>
+ <cell align="center">16</cell>
+ </row>
+ <row>
+ <cell align="center"><c>'r'</c></cell>
+ <cell align="center"><c>Challenge</c></cell>
+ <cell align="center"><c>Digest</c></cell>
+ </row>
+ <tcaption>The challenge_reply message</tcaption>
+ </table>
+ <p>
+ <c>Challenge</c> is <c>A</c>'s challenge for <c>B</c> to
+ handle. <c>Digest</c> is the MD5 digest that <c>A</c> constructed
+ from the challenge <c>B</c> sent in the previous step.
+ </p>
</item>
<tag>6) <c>recv_challenge_ack</c>/<c>send_challenge_ack</c></tag>
<item>
<p><c>B</c> checks that the digest received from <c>A</c> is correct
and generates a digest from the challenge received from <c>A</c>.
The digest is then sent to <c>A</c>. The message is as follows:</p>
- <pre>
-+---+-----+-----+-----+-----+-...-+------+
-|'a'|Dige0|Dige1|Dige2|Dige3| ... |Dige15|
-+---+-----+-----+-----+-----+-...-+------+</pre>
- <p>'a' is the tag. 'Dige0' ... 'Dige15' is the digest calculated by
- <c>B</c> for <c>A</c>'s challenge.</p>
+ <table align="left">
+ <row>
+ <cell align="center">1</cell>
+ <cell align="center">16</cell>
+ </row>
+ <row>
+ <cell align="center"><c>'a'</c></cell>
+ <cell align="center"><c>Digest</c></cell>
+ </row>
+ <tcaption>The challenge_ack message</tcaption>
+ </table>
+ <p>
+ <c>Digest</c> is the digest calculated by <c>B</c> for <c>A</c>'s
+ challenge.
+ </p>
</item>
<tag>7) check</tag>
<item>
@@ -728,10 +904,15 @@ recv_status
(if status was 'alive'
send_status - - - - - - - - - - - - - - - - - -&gt;
recv_status)
- ChB = gen_challenge()
- (ChB)
+
+ (ChB) ChB = gen_challenge()
&lt;---------------------------------------------- send_challenge
recv_challenge
+
+(if old send_name and new recv_challenge
+ send_complement - - - - - - - - - - - - - - - -&gt;
+ recv_complement)
+
ChA = gen_challenge(),
OCA = out_cookie(B),
DiA = gen_digest(ChB, OCA)
@@ -793,7 +974,8 @@ DiB == gen_digest(ChA, ICA)?
</item>
<tag><c>-define(DFLAG_NEW_FUN_TAGS,16#80).</c></tag>
<item>
- <p>The node understand new fun tags.</p>
+ <p>The node understands the <seealso marker="erl_ext_dist#NEW_FUN_EXT">
+ <c>NEW_FUN_EXT</c></seealso> tag.</p>
</item>
<tag><c>-define(DFLAG_EXTENDED_PIDS_PORTS,16#100).</c></tag>
<item>
@@ -802,13 +984,18 @@ DiB == gen_digest(ChA, ICA)?
</item>
<tag><c>-define(DFLAG_EXPORT_PTR_TAG,16#200).</c></tag>
<item>
+ <p>The node understands the <seealso marker="erl_ext_dist#EXPORT_EXT">
+ <c>EXPORT_EXT</c></seealso> tag.</p>
</item>
<tag><c>-define(DFLAG_BIT_BINARIES,16#400).</c></tag>
<item>
+ <p>The node understands the <seealso marker="erl_ext_dist#BIT_BINARY_EXT">
+ <c>BIT_BINARY_EXT</c></seealso> tag.</p>
</item>
<tag><c>-define(DFLAG_NEW_FLOATS,16#800).</c></tag>
<item>
- <p>The node understands new float format.</p>
+ <p>The node understands the <seealso marker="erl_ext_dist#NEW_FLOAT_EXT">
+ <c>NEW_FLOAT_EXT</c></seealso> tag.</p>
</item>
<tag><c>-define(DFLAG_UNICODE_IO,16#1000).</c></tag>
<item>
@@ -817,21 +1004,34 @@ DiB == gen_digest(ChA, ICA)?
<item>
<p>The node implements atom cache in distribution header.</p>
</item>
+ <marker id="DFLAG_SMALL_ATOM_TAGS"/>
<tag><c>-define(DFLAG_SMALL_ATOM_TAGS, 16#4000).</c></tag>
<item>
- <p>The node understand the <c>SMALL_ATOM_EXT</c> tag.</p>
+ <p>The node understands the <seealso marker="erl_ext_dist#SMALL_ATOM_EXT">
+ <c>SMALL_ATOM_EXT</c></seealso> tag.</p>
</item>
+ <marker id="DFLAG_UTF8_ATOMS"/>
<tag><c>-define(DFLAG_UTF8_ATOMS, 16#10000).</c></tag>
<item>
- <p>The node understand UTF-8 encoded atoms.</p>
+ <p>The node understands UTF-8 atoms encoded with
+ <seealso marker="erl_ext_dist#ATOM_UTF8_EXT">
+ <c>ATOM_UTF8_EXT</c></seealso> and
+ <seealso marker="erl_ext_dist#SMALL_ATOM_UTF8_EXT">
+ <c>SMALL ATOM_UTF8_EXT</c></seealso>.</p>
</item>
<tag><c>-define(DFLAG_MAP_TAG, 16#20000).</c></tag>
<item>
- <p>The node understand the map tag.</p>
+ <p>The node understands the map tag
+ <seealso marker="erl_ext_dist#MAP_EXT"><c>MAP_EXT</c></seealso>.</p>
</item>
+ <marker id="DFLAG_BIG_CREATION"/>
<tag><c>-define(DFLAG_BIG_CREATION, 16#40000).</c></tag>
<item>
- <p>The node understand big node creation.</p>
+ <p>The node understands big node creation tags
+ <seealso marker="erl_ext_dist#NEW_PID_EXT"><c>NEW_PID_EXT</c></seealso>,
+ <seealso marker="erl_ext_dist#NEW_PORT_EXT"><c>NEW_PORT_EXT</c></seealso> and
+ <seealso marker="erl_ext_dist#NEWER_REFERENCE_EXT"><c>NEWER_REFERENCE_EXT</c></seealso>.
+ </p>
</item>
<tag><c>-define(DFLAG_SEND_SENDER, 16#80000).</c></tag>
<item>
@@ -855,11 +1055,26 @@ DiB == gen_digest(ChA, ICA)?
<seealso marker="#control_message">control message</seealso>s
instead of the non-PAYLOAD variants.</p>
</item>
+ <marker id="DFLAG_FRAGMENTS"/>
<tag><c>-define(DFLAG_FRAGMENTS, 16#800000).</c></tag>
<item>
<p>Use <seealso marker="erl_ext_dist#fragments">fragmented</seealso>
distribution messages to send large messages.</p>
</item>
+ <marker id="DFLAG_HANDSHAKE_23"/>
+ <tag><c>-define(DFLAG_HANDSHAKE_23, 16#1000000).</c></tag>
+ <item>
+ <p>The node supports the new connection setup handshake (version 6)
+ introduced in OTP 23.</p>
+ </item>
+ <tag><marker id="DFLAG_SPAWN"/><c>-define(DFLAG_SPAWN, 16#100000000).</c></tag>
+ <item>
+ <p>Set if the <seealso marker="#SPAWN_REQUEST"><c>SPAWN_REQUEST</c></seealso>,
+ <seealso marker="#SPAWN_REQUEST_TT"><c>SPAWN_REQUEST_TT</c></seealso>,
+ <seealso marker="#SPAWN_REPLY"><c>SPAWN_REPLY</c></seealso>,
+ <seealso marker="#SPAWN_REPLY_TT"><c>SPAWN_REPLY_TT</c></seealso>
+ control messages are supported.</p>
+ </item>
</taglist>
<p>
There is also function <c>dist_util:strict_order_flags/0</c>
@@ -1009,12 +1224,6 @@ DiB == gen_digest(ChA, ICA)?
<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>
-
- <section>
- <title>New Ctrlmessages for distrvsn = 1 (Erlang/OTP R4)</title>
- <taglist>
<tag><c>SEND_TT</c></tag>
<item>
<p><c>{12, Unused, ToPid, TraceToken}</c></p>
@@ -1035,24 +1244,6 @@ DiB == gen_digest(ChA, ICA)?
<item>
<p><c>{18, FromPid, ToPid, TraceToken, Reason}</c></p>
</item>
- </taglist>
- </section>
-
- <section>
- <title>New Ctrlmessages for distrvsn = 2</title>
- <p><c>distrvsn</c> 2 was never used.</p>
- </section>
-
- <section>
- <title>New Ctrlmessages for distrvsn = 3 (Erlang/OTP R5C)</title>
- <p>None, but the version number was increased anyway.</p>
- </section>
-
- <section>
- <title>New Ctrlmessages for distrvsn = 4 (Erlang/OTP R6)</title>
- <p>These are only recognized by Erlang nodes, not by hidden nodes.</p>
-
- <taglist>
<tag><c>MONITOR_P</c></tag>
<item>
<p><c>{19, FromPid, ToProc, Ref}</c>, where
@@ -1185,7 +1376,108 @@ DiB == gen_digest(ChA, ICA)?
has been negotiated in the connection setup handshake.
</p>
</item>
+
+ </taglist>
+ </section>
+ <section>
+ <title>New Ctrlmessages for Erlang/OTP 23</title>
+ <taglist>
+ <tag><marker id="SPAWN_REQUEST"/><c>SPAWN_REQUEST</c></tag>
+ <item>
+ <p><c>{29, ReqId, From, GroupLeader, {Module, Function, Arity}, OptList}</c></p>
+ <p>Followed by <c>ArgList</c>.</p>
+ <p>This signal is sent by the
+ <seealso marker="erlang#spawn_request/5"><c>spawn_request()</c></seealso> BIF.</p>
+ <taglist>
+ <tag><c>ReqId :: reference()</c></tag>
+ <item><p>Request identifier. Also used as monitor
+ reference in case the <c>monitor</c> option has been
+ passed.</p></item>
+ <tag><c>From :: pid()</c></tag>
+ <item><p>Process identifier of the process making the
+ request. That is, the parent process to be.</p></item>
+ <tag><c>GroupLeader :: pid()</c></tag>
+ <item><p>Process identifier of the group leader of the
+ newly created process.</p></item>
+ <tag><c>{Module :: atom(), Function :: atom(), Arity :: integer() >= 0}</c></tag>
+ <item><p>Entry point for the the new process.</p></item>
+ <tag><c>OptList :: [term()]</c></tag>
+ <item><p>A proper list of spawn options to use when spawning.</p></item>
+ <tag><c>ArgList :: [term()]</c></tag>
+ <item><p>A proper list of arguments to use in the call to the entry point.</p></item>
+ </taglist>
+ <p>
+ Only supported when the
+ <seealso marker="erl_dist_protocol#DFLAG_SPAWN"><c>DFLAG_SPAWN</c></seealso>
+ <seealso marker="erl_dist_protocol#dflags">distribution flag</seealso>
+ has been passed.
+ </p>
+ </item>
+ <tag><marker id="SPAWN_REQUEST_TT"/><c>SPAWN_REQUEST_TT</c></tag>
+ <item>
+ <p><c>{30, ReqId, From, GroupLeader, {Module, Function, Arity}, OptList, Token}</c></p>
+ <p>Followed by <c>ArgList</c>.</p>
+ <p>Same as <seealso marker="#SPAWN_REQUEST"><c>SPAWN_REQUEST</c></seealso>, but also
+ with a sequential trace <c>Token</c>.
+ </p>
+ <p>
+ Only supported when the
+ <seealso marker="erl_dist_protocol#DFLAG_SPAWN"><c>DFLAG_SPAWN</c></seealso>
+ <seealso marker="erl_dist_protocol#dflags">distribution flag</seealso>
+ has been passed.
+ </p>
+ </item>
+ <tag><marker id="SPAWN_REPLY"/><c>SPAWN_REPLY</c></tag>
+ <item>
+ <p><c>{31, ReqId, To, Flags, Result}</c></p>
+ <p>This signal is sent as a reply to a process previously sending
+ a <seealso marker="#SPAWN_REQUEST"><c>SPAWN_REQUEST</c></seealso> signal.</p>
+ <taglist>
+ <tag><c>ReqId :: reference()</c></tag>
+ <item><p>Request identifier. Also used as monitor
+ reference in case the <c>monitor</c> option has been
+ passed.</p></item>
+ <tag><c>To :: pid()</c></tag>
+ <item><p>Process identifier of the process making the
+ spawn request.</p></item>
+ <tag><c>Flags :: integer() >= 0</c></tag>
+ <item><p>A bit flag field of bit flags bitwise or:ed together. Currently the
+ following flags are defined:</p>
+ <taglist>
+ <tag><c>1</c></tag>
+ <item><p>A link between <c>To</c> and <c>Result</c> was set up on
+ the node where <c>Result</c> resides.</p></item>
+ <tag><c>2</c></tag>
+ <item><p>A monitor from <c>To</c> to <c>Result</c> was set up on
+ the node where <c>Result</c> resides.</p></item>
+ </taglist>
+ </item>
+ <tag><c>Result :: pid() | atom()</c></tag>
+ <item><p>Result of the operation. If <c>Result</c> is a process
+ identifier, the operation succeeded and the process identifier
+ is the identifier of the newly created process. If <c>Result</c>
+ is an atom, the operation failed and the atom identifies failure
+ reason.</p></item>
+ </taglist>
+ <p>
+ Only supported when the
+ <seealso marker="erl_dist_protocol#DFLAG_SPAWN"><c>DFLAG_SPAWN</c></seealso>
+ <seealso marker="erl_dist_protocol#dflags">distribution flag</seealso>
+ has been passed.
+ </p>
+ </item>
+ <tag><marker id="SPAWN_REPLY_TT"/><c>SPAWN_REPLY_TT</c></tag>
+ <item>
+ <p><c>{32, ReqId, To, Flags, Result, Token}</c></p>
+ <p>Same as <seealso marker="#SPAWN_REPLY"><c>SPAWN_REPLY</c></seealso>, but also
+ with a sequential trace <c>Token</c>.</p>
+ <p>
+ Only supported when the
+ <seealso marker="erl_dist_protocol#DFLAG_SPAWN"><c>DFLAG_SPAWN</c></seealso>
+ <seealso marker="erl_dist_protocol#dflags">distribution flag</seealso>
+ has been passed.
+ </p>
+ </item>
</taglist>
</section>
-
</chapter>
diff --git a/erts/doc/src/erl_ext_dist.xml b/erts/doc/src/erl_ext_dist.xml
index 2ba5994557..c5b2ce1a0a 100644
--- a/erts/doc/src/erl_ext_dist.xml
+++ b/erts/doc/src/erl_ext_dist.xml
@@ -264,7 +264,7 @@
consists of. Length is a 2 byte big-endian integer
if flag <c>LongAtoms</c> has been set, otherwise a 1 byte
integer. When distribution flag
- <seealso marker="erl_dist_protocol#dflags">
+ <seealso marker="erl_dist_protocol#DFLAG_UTF8_ATOMS">
<c>DFLAG_UTF8_ATOMS</c></seealso>
has been exchanged between both nodes in the
<seealso marker="erl_dist_protocol#distribution_handshake">
@@ -316,8 +316,8 @@
</p>
<p>Fragmented distribution messages are only used if the receiving node
signals that it supports them via the
- <seealso marker="erl_dist_protocol#dflags">DFLAG_FRAGMENTS</seealso> distribution
- flag.</p>
+ <seealso marker="erl_dist_protocol#DFLAG_FRAGMENTS">DFLAG_FRAGMENTS</seealso>
+ distribution flag.</p>
<p>A process must complete the sending of a fragmented message before it
can start sending any other message on the same distribution channel.</p>
@@ -637,11 +637,14 @@
<seealso marker="#NEW_PID_EXT"><c>NEW_PID_EXT</c></seealso>.
Port operations are not allowed across node boundaries.
</p>
- <p>Introduced in OTP 19, but only to be decoded and echoed back. Not
- encoded for local ports. Planned to supersede <seealso marker="#PORT_EXT">
- <c>PORT_EXT</c></seealso> in OTP 23 when
- <seealso marker="erl_dist_protocol#dflags"><c>DFLAG_BIG_CREATON</c></seealso>
- becomes mandatory.
+ <p><c>NEW_PORT_EXT</c> was introduced in OTP 19, but only to be decoded
+ and echoed back. Not encoded for local ports.
+ </p>
+ <p>In OTP 23 distribution flag
+ <seealso marker="erl_dist_protocol#DFLAG_BIG_CREATION"><c>DFLAG_BIG_CREATION</c></seealso>
+ became mandatory. All ports are now
+ encoded using <c>NEW_PORT_EXT</c>, even external ports received as <seealso
+ marker="#PORT_EXT"><c>PORT_EXT</c></seealso> from older nodes.
</p>
</section>
@@ -719,11 +722,14 @@
erlang:list_to_pid/1</seealso>).</p>
</item>
</taglist>
- <p>Introduced in OTP 19, but only to be decoded and echoed back. Not
- encoded for local processes. Planned to supersede <seealso marker="#PID_EXT">
- <c>PID_EXT</c></seealso> in OTP 23 when
- <seealso marker="erl_dist_protocol#dflags"><c>DFLAG_BIG_CREATON</c></seealso>
- becomes mandatory.
+ <p><c>NEW_PID_EXT</c> was introduced in OTP 19, but only to be decoded
+ and echoed back. Not encoded for local processes.
+ </p>
+ <p>In OTP 23 distribution flag
+ <seealso marker="erl_dist_protocol#DFLAG_BIG_CREATION"><c>DFLAG_BIG_CREATION</c></seealso>
+ became mandatory. All pids are now encoded using <c>NEW_PID_EXT</c>,
+ even external pids received as
+ <seealso marker="#PID_EXT"><c>PID_EXT</c></seealso> from older nodes.
</p>
</section>
@@ -1047,11 +1053,15 @@
<seealso marker="#NEW_PID_EXT"><c>NEW_PID_EXT</c></seealso>.</p>
</item>
</taglist>
- <p>Introduced in OTP 19, but only to be decoded and echoed back. Not
- encoded for local references. Planned to supersede <seealso marker="#NEW_REFERENCE_EXT">
- <c>NEW_REFERENCE_EXT</c></seealso> in OTP 23 when
- <seealso marker="erl_dist_protocol#dflags"><c>DFLAG_BIG_CREATON</c></seealso>
- becomes mandatory.
+ <p><c>NEWER_REFERENCE_EXT</c> was introduced in OTP 19, but only to be decoded
+ and echoed back. Not encoded for local references.
+ </p>
+ <p>In OTP 23 distribution flag
+ <seealso marker="erl_dist_protocol#DFLAG_BIG_CREATION"><c>DFLAG_BIG_CREATION</c></seealso>
+ became mandatory. All references are now encoded using
+ <c>NEWER_REFERENCE_EXT</c>, even external references received as
+ <seealso marker="#NEW_REFERENCE_EXT"><c>NEW_REFERENCE_EXT</c></seealso>
+ from older nodes.
</p>
</section>
@@ -1408,7 +1418,7 @@
<p>
<c>SMALL_ATOM_EXT</c> was introduced in ERTS 5.7.2 and
require an exchange of distribution flag
- <seealso marker="erl_dist_protocol#dflags">
+ <seealso marker="erl_dist_protocol#DFLAG_SMALL_ATOM_TAGS">
<c>DFLAG_SMALL_ATOM_TAGS</c></seealso> in the
<seealso marker="erl_dist_protocol#distribution_handshake">
distribution handshake</seealso>.
diff --git a/erts/doc/src/erl_ext_fig.gif b/erts/doc/src/erl_ext_fig.gif
index 14d6bbc871..40dd17bd5e 100644
--- a/erts/doc/src/erl_ext_fig.gif
+++ b/erts/doc/src/erl_ext_fig.gif
Binary files differ
diff --git a/erts/doc/src/erlang.xml b/erts/doc/src/erlang.xml
index 2f3d2f9624..7c8591f719 100644
--- a/erts/doc/src/erlang.xml
+++ b/erts/doc/src/erlang.xml
@@ -60,6 +60,13 @@
</desc>
</datatype>
<datatype>
+ <name name="ext_iovec"/>
+ <desc>
+ <p>A term of type <seealso marker="#type-iovec"><c>iovec()</c></seealso>,
+ structured according to the Erlang external term format.</p>
+ </desc>
+ </datatype>
+ <datatype>
<name name="iovec"/>
<desc>
<p>A list of binaries. This datatype is useful to use
@@ -205,6 +212,37 @@
</seealso>.</p>
</desc>
</datatype>
+ <datatype>
+ <name name="spawn_opt_option"></name>
+ <desc>
+ <p>Options for <seealso marker="#spawn_opt/4"><c>spawn_opt()</c></seealso>.</p>
+ </desc>
+ </datatype>
+ <datatype>
+ <name name="priority_level"></name>
+ <desc>
+ <p>Process priority level. For more info see
+ <seealso marker="#process_flag_priority"><c>process_flag(priority,
+ Level)</c></seealso> </p>
+ </desc>
+ </datatype>
+ <datatype>
+ <name name="max_heap_size"></name>
+ <desc>
+ <p>Process max heap size configuration. For more info see
+ <seealso marker="#process_flag_max_heap_size"><c>process_flag(max_heap_size,
+ MaxHeapSize)</c></seealso> </p>
+ </desc>
+ </datatype>
+ <datatype>
+ <name name="message_queue_data"></name>
+ <desc>
+ <p>Process message queue data configuration. For more info see
+ <seealso marker="#process_flag_message_queue_data"><c>process_flag(message_queue_data,
+ MQD)</c></seealso> </p>
+ </desc>
+ </datatype>
+
</datatypes>
@@ -337,6 +375,16 @@ Z = erlang:adler32_combine(X,Y,iolist_size(Data2)).</code>
</func>
<func>
+ <name name="atom_to_binary" arity="1" since="OTP @OTP-15995@"/>
+ <fsummary>Return the binary representation of an atom.</fsummary>
+ <desc>
+ <p>
+ The same as <seealso marker="#atom_to_binary/2"><c>atom_to_binary</c>
+ </seealso><c>(<anno>Atom</anno>, utf8)</c>.</p>
+ </desc>
+ </func>
+
+ <func>
<name name="atom_to_binary" arity="2" since=""/>
<fsummary>Return the binary representation of an atom.</fsummary>
<desc>
@@ -409,14 +457,21 @@ Z = erlang:adler32_combine(X,Y,iolist_size(Data2)).</code>
</func>
<func>
+ <name name="binary_to_atom" arity="1" since="OTP @OTP-15995@"/>
+ <fsummary>Convert from text representation to an atom.</fsummary>
+ <desc>
+ <p>
+ The same as <seealso marker="#binary_to_atom/2"><c>binary_to_atom</c>
+ </seealso><c>(<anno>Binary</anno>, utf8)</c>.</p>
+ </desc>
+ </func>
+
+ <func>
<name name="binary_to_atom" arity="2" since=""/>
<fsummary>Convert from text representation to an atom.</fsummary>
<desc>
<p>Returns the atom whose text representation is
- <c><anno>Binary</anno></c>.
- If <c><anno>Encoding</anno></c> is <c>latin1</c>, no
- translation of bytes in the binary is done.
- If <c><anno>Encoding</anno></c>
+ <c><anno>Binary</anno></c>. If <c><anno>Encoding</anno></c>
is <c>utf8</c> or <c>unicode</c>, the binary must contain
valid UTF-8 sequences.</p>
<note>
@@ -438,6 +493,17 @@ Z = erlang:adler32_combine(X,Y,iolist_size(Data2)).</code>
</func>
<func>
+ <name name="binary_to_existing_atom" arity="1" since="OTP @OTP-15995@"/>
+ <fsummary>Convert from text representation to an atom.</fsummary>
+ <desc>
+ <p>
+ The same as <seealso marker="#binary_to_existing_atom/2">
+ <c>binary_to_existing_atom</c></seealso>
+ <c>(<anno>Binary</anno>, utf8)</c>.</p>
+ </desc>
+ </func>
+
+ <func>
<name name="binary_to_existing_atom" arity="2" since=""/>
<fsummary>Convert from text representation to an atom.</fsummary>
<desc>
@@ -1045,7 +1111,9 @@ Z = erlang:crc32_combine(X,Y,iolist_size(Data2)).</code>
as atoms. Others are returned as strings. Strings of
unrecognized header fields are formatted with only
capital letters first and after hyphen characters, for
- example, <c>"Sec-Websocket-Key"</c>.</p>
+ example, <c>"Sec-Websocket-Key"</c>. Header field names
+ are also returned in <c><anno>UnmodifiedField</anno></c>
+ as strings, without any conversion or formatting.</p>
<p>The protocol type <c>http</c> is only to be used for
the first line when an <c><anno>HttpRequest</anno></c> or an
<c><anno>HttpResponse</anno></c> is expected.
@@ -1270,9 +1338,10 @@ end</code>
<seealso marker="erlang#dist_ctrl_get_data_notification/1"><c>erlang:dist_ctrl_get_data_notification(DHandle)</c></seealso>.
</p>
<p>The returned value when there are data available depends
- on the value of the <c>get_size</c> option configured on the
- distribution channel identified by <c><anno>DHandle</anno></c>.
- For more information see the documentation of the <c>get_size</c>
+ on the value of the <c>get_size</c>
+ option configured on the distribution channel identified
+ by <c><anno>DHandle</anno></c>. For more information see
+ the documentation of the <c>get_size</c>
option for the
<seealso marker="#dist_ctrl_set_opt/3"><c>erlang:dist_ctrl_set_opt/3</c></seealso>
function.</p>
@@ -1839,7 +1908,7 @@ true</pre>
<p>It might point to the <c>init</c> process if the
<c>Fun</c> was statically allocated when module was
loaded (this optimisation is performed for local
- functions that do not capture the enviornment).</p>
+ functions that do not capture the environment).</p>
</item>
<tag><c>{index, Index}</c></tag>
<item>
@@ -2082,8 +2151,9 @@ true</pre>
<fsummary>Get the call stack back-trace of the last exception.</fsummary>
<type name="stack_item"/>
<desc>
- <warning><p><c>erlang:get_stacktrace/0</c> is deprecated and will stop working
- in a future release.</p></warning>
+ <warning><p><c>erlang:get_stacktrace/0</c> is deprecated and
+ will be removed in OTP 24. Starting from OTP 23,
+ <c>erlang:get_stacktrace/0</c> returns <c>[]</c>.</p></warning>
<p>Instead of using <c>erlang:get_stacktrace/0</c> to retrieve
the call stack back-trace, use the following syntax:</p>
<pre>
@@ -2092,53 +2162,6 @@ catch
Class:Reason:Stacktrace ->
{Class,Reason,Stacktrace}
end</pre>
- <p><c>erlang:get_stacktrace/0</c> retrieves the call stack back-trace
- (<em>stacktrace</em>) for an exception that has just been
- caught in the calling process as a list of
- <c>{<anno>Module</anno>,<anno>Function</anno>,<anno>Arity</anno>,<anno>Location</anno>}</c>
- tuples. Field <c><anno>Arity</anno></c> in the first tuple can
- be the argument list of that function call instead of an arity
- integer, depending on the exception.</p>
- <p>If there has not been any exceptions in a process, the
- stacktrace is <c>[]</c>. After a code change for the process,
- the stacktrace can also be reset to <c>[]</c>.</p>
- <p>The stacktrace is the same data as operator <c>catch</c>
- returns, for example:</p>
- <pre>
-{'EXIT',{badarg,Stacktrace}} = catch abs(x)</pre>
- <p><c><anno>Location</anno></c> is a (possibly empty) list
- of two-tuples that
- can indicate the location in the source code of the function.
- The first element is an atom describing the type of
- information in the second element. The following
- items can occur:</p>
- <taglist>
- <tag><c>file</c></tag>
- <item>The second element of the tuple is a string (list of
- characters) representing the filename of the source file
- of the function.
- </item>
- <tag><c>line</c></tag>
- <item>The second element of the tuple is the line number
- (an integer &gt; 0) in the source file
- where the exception occurred or the function was called.
- </item>
- </taglist>
- <warning><p>Developers should rely on stacktrace entries only for
- debugging purposes.</p>
- <p>The VM performs tail call optimization, which
- does not add new entries to the stacktrace, and also limits stacktraces
- to a certain depth. Furthermore, compiler options, optimizations and
- future changes may add or remove stacktrace entries, causing any code
- that expects the stacktrace to be in a certain order or contain specific
- items to fail.</p>
- <p>The only exception to this rule is <c>error:undef</c> which
- guarantees to include the <anno>Module</anno>, <anno>Function</anno> and <anno>Arity</anno>
- of the attempted function as the first stacktrace entry.</p>
- </warning>
- <p>See also
- <seealso marker="#error/1"><c>error/1</c></seealso> and
- <seealso marker="#error/2"><c>error/2</c></seealso>.</p>
</desc>
</func>
@@ -2675,26 +2698,37 @@ false</code>
<name name="link" arity="1" since=""/>
<fsummary>Create a link to another process (or port).</fsummary>
<desc>
- <p>Creates a link between the calling process and another
- process (or port) <c><anno>PidOrPort</anno></c>, if there is
- not such a link
- already. If a process attempts to create a link to itself,
- nothing is done. Returns <c>true</c>.</p>
- <p>If <c><anno>PidOrPort</anno></c> does not exist, the behavior
- of the BIF
- depends on if the calling process is trapping exits or not (see
- <seealso marker="#process_flag/2">
- <c>process_flag/2</c></seealso>):</p>
+
+ <p>Creates a link between the calling process and another process (or
+ port) <c><anno>PidOrPort</anno></c>. If the link already exists or a
+ process attempts to create a link to itself, nothing is done. Returns
+ <c>true</c> if the link is set up.</p>
+
+ <p>If <c><anno>PidOrPort</anno></c> does not exist and checking it is
+ cheap, a <c>noproc</c> error is raised. Currently, checking is cheap
+ if the <c><anno>PidOrPort</anno></c> is local and the caller does not
+ trap exits (see <seealso marker="#process_flag/2"><c>process_flag/2
+ </c></seealso>).</p>
+
+ <p>Apart from any exit signals from the linked process itself, two
+ special exit signals may be sent to the calling process:</p>
+
<list type="bulleted">
- <item><p>If the calling process is not trapping exits, and
- checking <c><anno>PidOrPort</anno></c> is cheap
- (that is, if <c><anno>PidOrPort</anno></c>
- is local), <c>link/1</c> fails with reason <c>noproc</c>.</p></item>
- <item><p>Otherwise, if the calling process is trapping exits,
- and/or <c><anno>PidOrPort</anno></c> is remote, <c>link/1</c>
- returns <c>true</c>, but an exit signal with reason <c>noproc</c>
- is sent to the calling process.</p></item>
+
+ <item><p><c>noproc</c> is sent immediately if
+ <c><anno>PidOrPort</anno></c> does not exist at the time of linking
+ (if the caller is trapping exits or <c><anno>PidOrPort</anno></c> is
+ remote).</p></item>
+
+ <item><p><c>noconnection</c> if <c><anno>PidOrPort</anno></c> is
+ remote and a connection between the nodes could not be established
+ or was severed.</p></item>
+
</list>
+
+ <p>See <seealso marker="doc/reference_manual:processes#links">Processes
+ ➜ Links</seealso> in the Erlang Reference Manual for more details.</p>
+
</desc>
</func>
@@ -5383,12 +5417,13 @@ RealSystem = system + MissedSystem</code>
</item>
<tag><c>{current_stacktrace, <anno>Stack</anno>}</c></tag>
<item>
- <p>Returns the current call stack back-trace (<em>stacktrace</em>)
- of the process. The stack has the same format as returned by
- <seealso marker="#get_stacktrace/0">
- <c>erlang:get_stacktrace/0</c></seealso>. The depth of the
- stacktrace is truncated according to the <c>backtrace_depth</c>
- system flag setting.</p>
+ <p>Returns the current call stack back-trace
+ (<em>stacktrace</em>) of the process. The stack has the
+ same format as in the <c>catch</c> part of a
+ <c>try</c>. See <seealso
+ marker="doc/reference_manual:errors#stacktrace">The call-stack back trace (stacktrace)</seealso>.
+ The depth of the stacktrace is truncated according to the
+ <c>backtrace_depth</c> system flag setting.</p>
</item>
<tag><c>{dictionary, <anno>Dictionary</anno>}</c></tag>
<item>
@@ -6280,6 +6315,23 @@ true</pre>
</func>
<func>
+ <name name="spawn_monitor" arity="2" since="OTP @OTP-15251@"/>
+ <fsummary>Create and monitor a new process with a fun as entry point.
+ </fsummary>
+ <desc>
+ <p>Returns the process identifier of a new process, started by
+ the application of <c><anno>Fun</anno></c> to the empty list
+ <c>[]</c> on the node <c><anno>Node</anno></c>,
+ and a reference for a monitor created to the new process.
+ Otherwise works like
+ <seealso marker="#spawn/3"><c>spawn/3</c></seealso>.</p>
+ <p>If the node identified by <c><anno>Node</anno></c> does not
+ support distributed <c>spawn_monitor()</c>, the call will fail
+ with a <c>notsup</c> exception.</p>
+ </desc>
+ </func>
+
+ <func>
<name name="spawn_monitor" arity="3" since=""/>
<fsummary>Create and monitor a new process with a function as entry point.
</fsummary>
@@ -6294,6 +6346,23 @@ true</pre>
</func>
<func>
+ <name name="spawn_monitor" arity="4" since="OTP @OTP-15251@"/>
+ <fsummary>Create and monitor a new process with a function as entry point.
+ </fsummary>
+ <desc>
+ <p>A new process is started by the application
+ of <c><anno>Module</anno>:<anno>Function</anno></c>
+ to <c><anno>Args</anno></c> on the node <c><anno>Node</anno></c>.
+ The process is monitored at the same time. Returns the process
+ identifier and a reference for the monitor. Otherwise works like
+ <seealso marker="#spawn/3"><c>spawn/3</c></seealso>.</p>
+ <p>If the node identified by <c><anno>Node</anno></c> does not
+ support distributed <c>spawn_monitor()</c>, the call will fail
+ with a <c>notsup</c> exception.</p>
+ </desc>
+ </func>
+
+ <func>
<name name="spawn_opt" arity="2" since=""/>
<fsummary>Create a new process with a fun as entry point.</fsummary>
<type name="priority_level"/>
@@ -6315,17 +6384,22 @@ true</pre>
<name name="spawn_opt" arity="3" since=""/>
<fsummary>Create a new process with a fun as entry point on a specified
node.</fsummary>
- <type name="priority_level"/>
- <type name="max_heap_size"/>
- <type name="message_queue_data"/>
- <type name="spawn_opt_option"/>
<desc>
<p>Returns the process identifier (pid) of a new process started
by the application of <c><anno>Fun</anno></c> to the
empty list <c>[]</c> on <c><anno>Node</anno></c>. If
<c><anno>Node</anno></c> does not exist, a useless pid is
returned. Otherwise works like
- <seealso marker="#spawn_opt/4"><c>spawn_opt/4</c></seealso>.</p>
+ <seealso marker="#spawn_opt/4"><c>spawn_opt/4</c></seealso>.</p>
+ <p>
+ Valid options depends on what options are
+ supported by the node identified by
+ <c><anno>Node</anno></c>. A description of
+ valid <c>Option</c>s for the local node
+ of current OTP version can be found in the
+ documentation of
+ <seealso marker="#spawn_opt/4"><c>spawn_opt/4</c></seealso>.
+ </p>
</desc>
</func>
@@ -6469,10 +6543,6 @@ true</pre>
<name name="spawn_opt" arity="5" since=""/>
<fsummary>Create a new process with a function as entry point on a
specified node.</fsummary>
- <type name="priority_level"/>
- <type name="max_heap_size"/>
- <type name="message_queue_data"/>
- <type name="spawn_opt_option"/>
<desc>
<p>Returns the process identifier (pid) of a new process started
by the application
@@ -6480,15 +6550,417 @@ true</pre>
<c><anno>Args</anno></c> on <c><anno>Node</anno></c>. If
<c><anno>Node</anno></c> does not exist, a useless pid is returned.
Otherwise works like
- <seealso marker="#spawn_opt/4"><c>spawn_opt/4</c></seealso>.</p>
- <note>
- <p>Option <c>monitor</c> is not supported by
- <c>spawn_opt/5</c>.</p>
- </note>
+ <seealso marker="#spawn_opt/4"><c>spawn_opt/4</c></seealso>.</p>
+ <p>
+ Valid options depends on what options are
+ supported by the node identified by
+ <c><anno>Node</anno></c>. A description of
+ valid <c>Option</c>s for the local node
+ of current OTP version can be found in the
+ documentation of
+ <seealso marker="#spawn_opt/4"><c>spawn_opt/4</c></seealso>.
+ </p>
+ </desc>
+ </func>
+
+ <func>
+ <name name="spawn_request" arity="1" since="OTP @OTP-15251@"/>
+ <fsummary>Asynchronously send a request to spawn a new process.</fsummary>
+ <desc>
+ <p>
+ The same as the call
+ <seealso marker="#spawn_request/3"><c>spawn_request(node(),<anno>Fun</anno>,[])</c></seealso>.
+ That is, a spawn request on the local node with no options.
+ </p>
+ </desc>
+ </func>
+
+ <func>
+ <name name="spawn_request" arity="2" clause_i="1" since="OTP @OTP-15251@"/>
+ <fsummary>Asynchronously send a request to spawn a new process.</fsummary>
+ <desc>
+ <p>
+ The same as the call
+ <seealso marker="#spawn_request/3"><c>spawn_request(node(),<anno>Fun</anno>,<anno>Options</anno>)</c></seealso>.
+ That is, a spawn request on the local node.
+ </p>
</desc>
</func>
<func>
+ <name name="spawn_request" arity="2" clause_i="2" since="OTP @OTP-15251@"/>
+ <fsummary>Asynchronously send a request to spawn a new process.</fsummary>
+ <desc>
+ <p>
+ The same as the call
+ <seealso marker="#spawn_request/3"><c>spawn_request(<anno>Node</anno>,<anno>Fun</anno>,[])</c></seealso>.
+ That is, a spawn request with no options.
+ </p>
+ </desc>
+ </func>
+
+ <func>
+ <name name="spawn_request" arity="3" clause_i="1" since="OTP @OTP-15251@"/>
+ <fsummary>Asynchronously send a request to spawn a new process.</fsummary>
+ <desc>
+ <p>
+ The same as
+ <seealso marker="#spawn_request/5"><c>spawn_request(<anno>Node</anno>,erlang,apply,[<anno>Fun</anno>,[]],<anno>Options</anno>)</c></seealso>.
+ That is, a spawn request using the fun <c><anno>Fun</anno></c> of
+ arity zero as entry point.
+ </p>
+ <p>This function will fail with a <c>badarg</c> exception if:</p>
+ <list>
+ <item><p><c><anno>Node</anno></c> is not a valid
+ node name atom.</p></item>
+ <item><p><c><anno>Fun</anno></c> is not a fun of arity zero.</p></item>
+ <item><p><c><anno>Options</anno></c> is not a proper list
+ of terms.</p></item>
+ </list>
+ </desc>
+ </func>
+
+ <func>
+ <name name="spawn_request" arity="3" clause_i="2" since="OTP @OTP-15251@"/>
+ <fsummary>Asynchronously send a request to spawn a new process.</fsummary>
+ <desc>
+ <p>
+ The same as the call
+ <seealso marker="#spawn_request/5"><c>spawn_request(node(),<anno>Module</anno>,<anno>Function</anno>,<anno>Args</anno>,[])</c></seealso>.
+ That is, a spawn request on the local node with no options.
+ </p>
+ </desc>
+ </func>
+
+ <func>
+ <name name="spawn_request" arity="4" clause_i="1" since="OTP @OTP-15251@"/>
+ <fsummary>Asynchronously send a request to spawn a new process.</fsummary>
+ <desc>
+ <p>
+ The same as the call
+ <seealso marker="#spawn_request/5"><c>spawn_request(<anno>Node</anno>,<anno>Module</anno>,<anno>Function</anno>,<anno>Args</anno>,[])</c></seealso>.
+ That is, a spawn request with no options.
+ </p>
+ </desc>
+ </func>
+
+ <func>
+ <name name="spawn_request" arity="4" clause_i="2" since="OTP @OTP-15251@"/>
+ <fsummary>Asynchronously send a request to spawn a new process.</fsummary>
+ <desc>
+ <p>
+ The same as the call
+ <seealso marker="#spawn_request/5"><c>spawn_request(node(),<anno>Module</anno>,<anno>Function</anno>,<anno>Args</anno>,<anno>Options</anno>)</c></seealso>.
+ That is, a spawn request on the local node.
+ </p>
+ </desc>
+ </func>
+
+ <func>
+ <name name="spawn_request" arity="5" since="OTP @OTP-15251@"/>
+ <fsummary>Asynchronously send a request to spawn a new process.</fsummary>
+ <desc>
+ <p>
+ Asynchronously send a spawn request. Returns a request
+ identifier <c><anno>ReqId</anno></c>.
+ </p>
+
+ <marker id="spawn_request_success_message"/>
+ <p>
+ If the spawn operation succeeds, a new process is
+ created on the node identified by <c><anno>Node</anno></c>.
+ When a spawn operation succeeds, the caller will by
+ default be sent a message on the form
+ <c>{<anno>ReplyTag</anno>, <anno>ReqId</anno>, ok, Pid}</c>
+ where <c>Pid</c> is the process identifier of the
+ newly created process. Such a message is referred to as a
+ <i>success message</i> below in the text.
+ <c><anno>ReplyTag</anno></c> is by default the atom
+ <c>spawn_reply</c> unless modified by
+ the <c>{reply_tag, <anno>ReplyTag</anno>}</c> option. The
+ new process is started by the application of
+ <c><anno>Module</anno>:<anno>Function</anno></c>
+ to <c><anno>Args</anno></c>.
+ </p>
+
+ <marker id="spawn_request_error_message"/>
+ <p>The spawn operation fails either if creation of a new process
+ failed or if the spawn operation was interrupted by a connection
+ failure. When a spawn operation fails, the caller will by default
+ be sent a message on the form
+ <c>{<anno>ReplyTag</anno>, <anno>ReqId</anno>, error, Reason}</c>
+ where <c>Reason</c> is the error reason. Such a message is
+ referred to as an <i>error message</i> below in the text.
+ Currently the following spawn error <c>Reason</c>s are defined,
+ but other reasons can appear at any time without prior notice:</p>
+ <taglist>
+ <tag><c>badopt</c></tag>
+ <item>
+ <p>
+ An invalid <c><anno>Option</anno></c> was passed as
+ argument. Note that different runtime systems may
+ support different options.
+ </p>
+ </item>
+ <tag><c>notsup</c></tag>
+ <item>
+ <p>
+ The node identified by <c><anno>Node</anno></c> does
+ not support spawn operations issued by
+ <c>spawn_request()</c>.
+ </p>
+ </item>
+ <tag><c>noconnection</c></tag>
+ <item>
+ <p>
+ Failure to set up a connection to the node
+ identified by <c><anno>Node</anno></c> or
+ the connection to that node was lost during
+ the spawn operation. In the case the
+ connection was lost, a process may or may
+ not have been created.
+ </p>
+ </item>
+ <tag><c>system_limit</c></tag>
+ <item>
+ <p>
+ Could not create a new process due to that some
+ system limit was reached. Typically the process table
+ was full.
+ </p>
+ </item>
+ </taglist>
+ <p>Valid <c><anno>Option</anno></c>s:</p>
+ <taglist>
+ <tag><c>monitor</c></tag>
+ <item>
+ <p>
+ In the absence of spawn operation failures, atomically
+ sets up a monitor to the newly created process. That
+ is, as if the calling process had called
+ <seealso marker="#monitor/2"><c>monitor(process, Pid)</c></seealso>
+ where <c>Pid</c> is the process identifier of the
+ newly created process. The <c><anno>ReqId</anno></c>
+ returned by <c>spawn_request()</c> is also used as
+ monitor reference as if it was returned from
+ <c>monitor(process, Pid)</c>.
+ </p>
+ <p>
+ The monitor will not be activated for the calling
+ process until the spawn operation has succeeded.
+ The monitor can not be
+ <seealso marker="#demonitor/1">demonitored</seealso>
+ before the operation has succeeded. A <c>'DOWN'</c>
+ message for the corresponding monitor is guaranteed
+ not to be delivered before a
+ <seealso marker="#spawn_request_success_message"><i>success
+ message</i></seealso> that corresponds to the spawn
+ operation. If the spawn operation fails, no
+ <c>'DOWN'</c> message will be delivered.
+ </p>
+ <p>
+ If the connection between the nodes involved in
+ the spawn operation is lost during the spawn
+ operation, the spawn operation will fail with an
+ error reason of <c>noconnection</c>. A new process
+ may or may not have been created.
+ </p>
+ </item>
+ <tag><c>link</c></tag>
+ <item>
+ <p>
+ In absence of spawn operation failures, atomically
+ sets up a link between the calling process and the
+ newly created process. That is, as if the calling
+ process had called
+ <seealso marker="#link/1"><c>link(Pid)</c></seealso>
+ where <c>Pid</c> is the process identifier of the
+ newly created process.
+ </p>
+ <p>
+ The link will not be activated for the calling
+ process until the spawn operation has succeeded.
+ The link can not be removed before the operation
+ has succeeded. An exit signal due to the link is
+ guaranteed not to be delivered before a
+ <seealso marker="#spawn_request_success_message"><i>success
+ message</i></seealso> that corresponds to the spawn
+ operation. If the spawn operation fails, no
+ exit signal due to the link will be delivered to
+ the caller of <c>spawn_request()</c>.
+ </p>
+ <p>
+ If the connection between the nodes involved in
+ the spawn operation is lost during the spawn
+ operation, the spawn operation will fail with
+ an error reason of <c>noconnection</c>. A new
+ process may or may not have been created. If it
+ has been created, it will be delivered an exit
+ signal with an exit reason of <c>noconnection</c>.
+ </p>
+ </item>
+ <tag><c>{reply, <anno>Reply</anno>}</c></tag>
+ <item>
+ <p>Valid <c>Reply</c> values:</p>
+ <taglist>
+ <tag><c>yes</c></tag>
+ <item><p>
+ A spawn reply message will be sent to the caller
+ regardless of whether the operation succeeds or
+ not. If the call to <c>spawn_request()</c> returns
+ without raising an exception and the <c>reply</c>
+ option is set to <c>yes</c>, the caller is
+ guaranteed to be delivered either a
+ <seealso marker="#spawn_request_success_message"><i>success
+ message</i></seealso> or an
+ <seealso marker="#spawn_request_error_message"><i>error
+ message</i></seealso>. The <c>reply</c> option is by
+ default set to <c>yes</c>.
+ </p></item>
+ <tag><c>no</c></tag>
+ <item><p>
+ No spawn reply message will be sent to the caller
+ when the spawn operation completes. This regardless of
+ whether the operation succeeds or not.
+ </p></item>
+ <tag><c>error_only</c></tag>
+ <item><p>
+ No spawn reply message will be sent to the caller
+ if the spawn operation succeeds, but an
+ <seealso marker="#spawn_request_error_message"><i>error
+ message</i></seealso> will be sent to the caller
+ if the operation fails.
+ </p></item>
+ <tag><c>success_only</c></tag>
+ <item><p>
+ No spawn reply message will be sent to the caller
+ if the spawn operation fails, but a
+ <seealso marker="#spawn_request_success_message"><i>success
+ message</i></seealso> will be sent to the caller
+ if the operation succeeds.
+ </p></item>
+ </taglist>
+ </item>
+ <tag><c>{reply_tag, <anno>ReplyTag</anno>}</c></tag>
+ <item>
+ <p>
+ Sets the reply tag to <c><anno>ReplyTag</anno></c> in
+ the reply message. That is, in the
+ <seealso marker="#spawn_request_success_message"><i>success</i></seealso>
+ or <seealso marker="#spawn_request_error_message"><i>error</i></seealso>
+ message that is sent to the caller due to the
+ spawn operation. The default reply tag is the atom
+ <c>spawn_reply</c>.
+ </p>
+ </item>
+ <tag><c><anno>OtherOption</anno></c></tag>
+ <item>
+ <p>
+ Other valid options depends on what options are
+ supported by the node identified by
+ <c><anno>Node</anno></c>. A description of other
+ valid <c><anno>Option</anno></c>s for the local node
+ of current OTP version can be found in the
+ documentation of
+ <seealso marker="#spawn_opt/4"><c>spawn_opt/4</c></seealso>.
+ </p>
+ </item>
+ </taglist>
+ <p>This function will fail with a <c>badarg</c> exception if:
+ </p>
+ <list>
+ <item><p><c><anno>Node</anno></c> is not a valid
+ node name atom.</p></item>
+ <item><p><c><anno>Module</anno></c> is not an atom.</p></item>
+ <item><p><c><anno>Function</anno></c> is not an atom.</p></item>
+ <item><p><c><anno>Args</anno></c> is not a proper list
+ of terms.</p></item>
+ <item><p><c><anno>Options</anno></c> is not a proper list
+ of terms.</p></item>
+ </list>
+ <p>
+ Note that not all individual <c><anno>Option</anno></c>s
+ are checked when the spawn request is sent. Some
+ <c><anno>Option</anno></c>s can only be checked on
+ reception of the request. Therefore an invalid option
+ does <em>not</em> cause a <c>badarg</c> exception, but
+ will cause the spawn operation to fail with an error
+ reason of <c>badopt</c>.
+ </p>
+ <p>
+ A spawn request can be abandoned by calling
+ <seealso marker="#spawn_request_abandon/1"><c>spawn_request_abandon/1</c></seealso>.
+ </p>
+ </desc>
+ </func>
+
+ <func>
+ <name name="spawn_request_abandon" arity="1" since="OTP @OTP-15251@"/>
+ <fsummary>Abandon a previously issued spawn request.</fsummary>
+ <desc>
+ <p>
+ Abandon a previously issued spawn request. <c><anno>ReqId</anno></c>
+ corresponds to a request identifier previously returned by
+ <seealso marker="#spawn_request/5"><c>spawn_request()</c></seealso>
+ in a call from current process. That is, only the process that
+ has made the request can abandon the request.
+ </p>
+ <p>
+ A spawn request can only be successfully abandoned until the
+ spawn request has completed. When a spawn request has been
+ successfully abandoned, the caller will not be effected by
+ future direct effects of the spawn request itself.
+ For example, it will not receive a spawn reply message. The
+ request is however not withdrawn, so a new process may or may
+ not be created due to the request. If a new process is created
+ after the spawn request was abandoned, no monitors nor links
+ will be set up to the caller of <c>spawn_request_abandon/1</c>
+ due to the spawn request. If the spawn request included the
+ <c>link</c> option, the process created due to this request
+ will be sent an exit signal from its parent with the exit
+ reason <c>abandoned</c> when it is detected that the
+ spawn operation has succeeded.
+ </p>
+ <note><p>
+ A process created due to a spawn request that has been
+ abandoned may communicate with its parent as any other
+ process. It is <em>only</em> the direct effects on the
+ parent of the actual spawn request, that will be canceled
+ by abandoning a spawn request.
+ </p></note>
+ <p>Return values:</p>
+ <taglist>
+ <tag><c>true</c></tag>
+ <item><p>
+ The spawn request was successfully abandoned.
+ </p></item>
+ <tag><c>false</c></tag>
+ <item><p>
+ No spawn request was abandoned. The <c><anno>ReqId</anno></c>
+ request identifier did not correspond to an outstanding
+ spawn request issued by the calling process. The reason for
+ this is either:</p>
+ <list>
+ <item><p>
+ <c><anno>ReqId</anno></c> corresponds to a spawn request
+ previoulsy made by the calling process. The spawn operation
+ has completed and a spawn reply has already been delivered
+ to the calling process unless the spawn reply was disabled
+ in the request.
+ </p></item>
+ <item><p>
+ <c><anno>ReqId</anno></c> does not correspond to a spawn
+ request that has been made by the calling process.
+ </p></item>
+ </list>
+ </item>
+ </taglist>
+ <p>This function fail with a <c>badarg</c> exception if
+ <c><anno>ReqId</anno></c> is not a reference.</p>
+ </desc>
+ </func>
+
+ <func>
<name name="split_binary" arity="2" since=""/>
<fsummary>Split a binary into two.</fsummary>
<type_desc variable="Pos">0..byte_size(Bin)</type_desc>
@@ -7799,7 +8271,7 @@ Metadata = #{ pid => pid(),
</func>
<func>
- <name name="system_info" arity="1" clause_i="76" since=""/>
+ <name name="system_info" arity="1" clause_i="77" since=""/>
<fsummary>System info overview.</fsummary>
<desc>
<p>Returns information about the current system.
@@ -8218,6 +8690,15 @@ Metadata = #{ pid => pid(),
<seealso marker="#system_info_logical_processors">logical processors
configured</seealso>.</p>
</item>
+ <tag><marker id="system_info_cpu_quota"/>
+ <c>cpu_quota</c></tag>
+ <item>
+ <p>Returns the detected CPU quota the emulator is limited by. The
+ return value is an integer saying how many processors' worth of
+ runtime we get (between 1 and the number of logical processors),
+ or the atom <c>unknown</c> if the emulator cannot detect a
+ quota.</p>
+ </item>
<tag><marker id="system_info_update_cpu_info"/>
<c>update_cpu_info</c></tag>
<item>
@@ -8227,8 +8708,9 @@ Metadata = #{ pid => pid(),
CPU topology</seealso> and the number of logical processors
<seealso marker="#system_info_logical_processors">configured</seealso>,
<seealso marker="#system_info_logical_processors_online">online</seealso>,
- and <seealso marker="#system_info_logical_processors_available">
- available</seealso>.</p>
+ <seealso marker="#system_info_logical_processors_available">available</seealso>,
+ and <seealso marker="#system_info_cpu_quota">cpu
+ quota</seealso>.</p>
<p>If the CPU information has changed since the last time
it was read, the atom <c>changed</c> is returned, otherwise
the atom <c>unchanged</c>. If the CPU information has changed,
@@ -9120,6 +9602,7 @@ Metadata = #{ pid => pid(),
<name name="system_info" arity="1" clause_i="75" since=""/> <!-- version -->
<name name="system_info" arity="1" clause_i="76" since=""/> <!-- wordsize -->
<!-- <name name="system_info" arity="1" clause_i="77"/> overview -->
+ <!-- When adding any entry, make sure to update the overview clause_i -->
<fsummary>Information about the system.</fsummary>
<desc>
<marker id="system_info_misc_tags"/>
@@ -9672,7 +10155,7 @@ hello
<func>
<name name="term_to_binary" arity="2" since=""/>
- <fsummary>Encode a term to en Erlang external term format binary.
+ <fsummary>Encode a term to an Erlang external term format binary.
</fsummary>
<desc>
<p>Returns a binary data object that is the result of encoding
@@ -9736,6 +10219,59 @@ hello
</func>
<func>
+ <name name="term_to_iovec" arity="1" since="OTP @OTP-15618@"/>
+ <fsummary>Encode a term to an Erlang external term format as iovec().
+ </fsummary>
+ <desc>
+ <p>Returns the encoding of <c><anno>Term</anno></c> according to
+ the Erlang external term format as
+ <seealso marker="#type-ext_iovec"><c>ext_iovec()</c></seealso>.</p>
+
+ <p>This function produce the same encoding as
+ <seealso marker="#term_to_binary/1"><c>term_to_binary/1</c></seealso>,
+ but with another return type. The call
+ <c>iolist_to_binary(term_to_iovec(Term))</c> will produce
+ exactly the same result as the call <c>term_to_binary(Term)</c>.</p>
+
+ <p><c>term_to_iovec()</c> is a pure optimization of the functionality
+ <c>term_to_binary()</c> provide. <c>term_to_iovec()</c> can for example
+ refer directly to off heap binaries instead of copying the binary
+ data into the result.</p>
+
+ <p>See also
+ <seealso marker="#term_to_binary/1"><c>term_to_binary/1</c></seealso>.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name name="term_to_iovec" arity="2" since="OTP @OTP-15618@"/>
+ <fsummary>Encode a term to en Erlang external term format as iovec().
+ </fsummary>
+ <desc>
+ <p>Returns the encoding of <c><anno>Term</anno></c> according to
+ the Erlang external term format as
+ <seealso marker="#type-ext_iovec"><c>ext_iovec()</c></seealso>.</p>
+
+ <p>This function produce the same encoding as
+ <seealso marker="#term_to_binary/2"><c>term_to_binary/2</c></seealso>,
+ but with another return type. The call
+ <c>iolist_to_binary(term_to_iovec(Term, Opts))</c> will produce
+ exactly the same result as <c>term_to_binary(Term, Opts)</c>.</p>
+
+ <p>Currently recognised options are all options recognised by
+ <seealso marker="#term_to_binary/2"><c>term_to_binary/2</c></seealso>.</p>
+
+ <p><c>term_to_iovec()</c> is a pure optimization of the functionality
+ <c>term_to_binary()</c> provide. <c>term_to_iovec()</c> can for example
+ refer directly to off heap binaries instead of copying the binary
+ data into the result.</p>
+
+ <p>See also
+ <seealso marker="#term_to_binary/2"><c>term_to_binary/2</c></seealso>.</p>
+ </desc>
+ </func>
+
+ <func>
<name name="throw" arity="1" since=""/>
<fsummary>Throw an exception.</fsummary>
<desc>
diff --git a/erts/doc/src/erlc.xml b/erts/doc/src/erlc_cmd.xml
index f1f2c786da..55a4712b25 100644
--- a/erts/doc/src/erlc.xml
+++ b/erts/doc/src/erlc_cmd.xml
@@ -11,7 +11,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
-
+
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
@@ -387,4 +387,3 @@ erlc +export_all file.erl</pre>
<seealso marker="snmp:snmp"><c>snmp(3)</c></seealso></p>
</section>
</comref>
-
diff --git a/erts/doc/src/erlsrv.xml b/erts/doc/src/erlsrv_cmd.xml
index 6c08b25220..1db5fcd9db 100644
--- a/erts/doc/src/erlsrv.xml
+++ b/erts/doc/src/erlsrv_cmd.xml
@@ -11,7 +11,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
-
+
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
@@ -43,7 +43,7 @@
Windows services applet in a manner similar to other services.</p>
<p>Notice that <c>erlsrv</c> is not a general service utility for Windows,
- but designed for embedded Erlang systems.</p>
+ but designed for embedded Erlang systems.</p>
<p><c>erlsrv</c> also provides a command-line interface for registering,
changing, starting, and stopping services.</p>
@@ -471,10 +471,10 @@
<code type="none"><![CDATA[
#include <windows.h>
-/*
+/*
** A Console control handler that ignores the log off events,
** and lets the default handler take care of other events.
-*/
+*/
BOOL WINAPI service_aware_handler(DWORD ctrl){
if(ctrl == CTRL_LOGOFF_EVENT)
return TRUE;
@@ -485,8 +485,8 @@ BOOL WINAPI service_aware_handler(DWORD ctrl){
void initialize_handler(void){
char buffer[2];
- /*
- * We assume we are running as a service if this
+ /*
+ * We assume we are running as a service if this
* environment variable is defined.
*/
if(GetEnvironmentVariable("ERLSRV_SERVICE_NAME",buffer,
@@ -530,4 +530,3 @@ void initialize_handler(void){
<c>release_handler(3)</c></seealso></p>
</section>
</comref>
-
diff --git a/erts/doc/src/escript.xml b/erts/doc/src/escript_cmd.xml
index be1664b39f..4ca7e46e6e 100644
--- a/erts/doc/src/escript.xml
+++ b/erts/doc/src/escript_cmd.xml
@@ -11,7 +11,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
-
+
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
@@ -448,4 +448,3 @@ ok
</taglist>
</section>
</comref>
-
diff --git a/erts/doc/src/init.xml b/erts/doc/src/init.xml
index d6c73a02ec..c4b1478c18 100644
--- a/erts/doc/src/init.xml
+++ b/erts/doc/src/init.xml
@@ -48,6 +48,15 @@
the system.</p>
</description>
+ <datatypes>
+ <datatype>
+ <name name="mode"/>
+ <desc>
+ <p>Code loading mode.</p>
+ </desc>
+ </datatype>
+ </datatypes>
+
<funcs>
<func>
<name name="boot" arity="1" since=""/>
@@ -165,12 +174,24 @@
<name name="restart" arity="0" since=""/>
<fsummary>Restart the running Erlang node.</fsummary>
<desc>
+ <p>The same as
+ <seealso marker="#restart/1"><c>restart([])</c></seealso>.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name name="restart" arity="1" since="OTP 23.0"/>
+ <fsummary>Restart the running Erlang node.</fsummary>
+ <desc>
<p>The system is restarted <em>inside</em> the running Erlang
node, which means that the emulator is not restarted. All
applications are taken down smoothly, all code is unloaded,
and all ports are closed before the system is booted again in
- the same way as initially started. The same <c>BootArgs</c>
- are used again.</p>
+ the same way as initially started.</p>
+ <p>The same <c>BootArgs</c> are used when restarting the
+ system unless the <c>mode</c> option is given, allowing the
+ code loading mode to be set to either <c>embedded</c> or
+ <c>interactive</c>. All other <c>BootArgs</c> remain the same.</p>
<p>To limit the shutdown time, the time <c>init</c> is allowed
to spend taking down applications, command-line flag
<c>-shutdown_time</c> is to be used.</p>
diff --git a/erts/doc/src/ref_man.xml.src b/erts/doc/src/ref_man.xml.src
index 7dd003763c..5b327165d4 100644
--- a/erts/doc/src/ref_man.xml.src
+++ b/erts/doc/src/ref_man.xml.src
@@ -34,24 +34,24 @@
<xi:include href="atomics.xml"/>
<xi:include href="counters.xml"/>
<xi:include href="driver_entry.xml"/>
- <xi:include href="epmd.xml"/>
- <xi:include href="erl.xml"/>
+ <xi:include href="epmd_cmd.xml"/>
+ <xi:include href="erl_cmd.xml"/>
<xi:include href="erlang.xml"/>
- <xi:include href="erlc.xml"/>
+ <xi:include href="erlc_cmd.xml"/>
<xi:include href="erl_driver.xml"/>
<xi:include href="erl_nif.xml"/>
<xi:include href="erl_prim_loader.xml"/>
- <xi:include href="erlsrv.xml"/>
+ <xi:include href="erlsrv_cmd.xml"/>
<xi:include href="erl_tracer.xml"/>
<xi:include href="erts_alloc.xml"/>
- <xi:include href="escript.xml"/>
+ <xi:include href="escript_cmd.xml"/>
<xi:include href="init.xml"/>
<xi:include href="persistent_term.xml"/>
- <xi:include href="run_erl.xml"/>
+ <xi:include href="run_erl_cmd.xml"/>
%ESOCK_USE_SOCKET_XML%
- <xi:include href="start.xml"/>
- <xi:include href="start_erl.xml"/>
- <xi:include href="werl.xml"/>
+ <xi:include href="start_cmd.xml"/>
+ <xi:include href="start_erl_cmd.xml"/>
+ <xi:include href="werl_cmd.xml"/>
<xi:include href="zlib.xml"/>
</application>
diff --git a/erts/doc/src/run_erl.xml b/erts/doc/src/run_erl_cmd.xml
index fa36457489..9f1984f784 100644
--- a/erts/doc/src/run_erl.xml
+++ b/erts/doc/src/run_erl_cmd.xml
@@ -11,7 +11,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
-
+
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
@@ -19,7 +19,7 @@
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-
+
</legalnotice>
<title>run_erl</title>
@@ -213,4 +213,3 @@
<seealso marker="start_erl"><c>start_erl(1)</c></seealso></p>
</section>
</comref>
-
diff --git a/erts/doc/src/socket.xml b/erts/doc/src/socket.xml
index 2d16ff2074..980479bd99 100644
--- a/erts/doc/src/socket.xml
+++ b/erts/doc/src/socket.xml
@@ -4,7 +4,7 @@
<erlref>
<header>
<copyright>
- <year>2018</year><year>2019</year>
+ <year>2018</year><year>2020</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -94,7 +94,7 @@
<datatype>
<name>socket()</name>
<desc><p>As returned by
- <seealso marker="#open/2"><c>open/2,3,4</c></seealso> and
+ <seealso marker="#open/1"><c>open/1,2,3,4</c></seealso> and
<seealso marker="#accept/1"><c>accept/1,2</c></seealso>.</p>
</desc>
</datatype>
@@ -513,7 +513,58 @@
</func>
<func>
- <name name="open" arity="2" since="OTP 22.0"/>
+ <name name="open" arity="1" since="OTP @OTP-16398@"/>
+ <name name="open" arity="2" clause_i="1" since="OTP @OTP-16398@"/>
+ <fsummary>Create an endpoint for communication.</fsummary>
+ <desc>
+ <p>Create an endpoint (socket) for communication based on an
+ already existing file descriptor.
+ The function attempts to retrieve domain, type and protocol from
+ the system. This is however not possible on all platforms, and
+ in those cases it expects it in <c>Opts</c>. </p>
+
+ <p>The <c>Opts</c> argument is intended for providing extra
+ information for the open call:</p>
+ <taglist>
+ <tag><c><![CDATA[dup: boolean()]]></c></tag>
+ <item>
+ <p>Shall the provided descriptor be duplicated (dup) or not.
+ <br/>Defaults to <c>true</c>. </p>
+ </item>
+
+ <tag><c><![CDATA[debug: boolean()]]></c></tag>
+ <item>
+ <p>Enable or disable debug during the open call.
+ <br/>Defaults to <c>false</c>. </p>
+ </item>
+
+ <tag><c><![CDATA[domain: socket:domain()]]></c></tag>
+ <item>
+ <p>Which domain is the descriptor of. </p>
+ </item>
+
+ <tag><c><![CDATA[type: socket:type()]]></c></tag>
+ <item>
+ <p>Which type is the descriptor of. </p>
+ </item>
+
+ <tag><c><![CDATA[protocol: socket:protocol()]]></c></tag>
+ <item>
+ <p>Which protocol is the descriptor of. </p>
+ </item>
+
+ </taglist>
+
+ <note>
+ <p>This function should be used with care! </p>
+ <p>On some platforms its <em>necessary</em> to provide the
+ <c>protocol</c> as its impossible to retrieve it. </p>
+ </note>
+ </desc>
+ </func>
+
+ <func>
+ <name name="open" arity="2" clause_i="2" since="OTP 22.0"/>
<name name="open" arity="3" since="OTP 22.0"/>
<name name="open" arity="4" since="OTP 22.0"/>
<fsummary>Create an endpoint for communication.</fsummary>
@@ -526,9 +577,10 @@
And for <c>Domain = local</c>, if a protocol <em>is</em> pecified,
it <em>must</em> be <c>default</c>. </p>
- <p>The <c>Extra</c> argument is intended for "obscure" options.
- Currently the only supported option is <c>netns</c>, which
- is only supported on the linux platform.</p>
+ <p>The <c>Opts</c> argument is intended for "other" options.
+ Currently the only supported option(s) are <c>netns</c>, which
+ is only supported on the linux platform and <c>debug</c> (controls debug
+ printouts during the open call).</p>
<note>
<p>It may not be possible to specify the default protocol (except
@@ -911,7 +963,8 @@
<name name="supports" arity="1" clause_i="4" since="OTP 22.0"/>
<name name="supports" arity="1" clause_i="5" since="OTP @OTP-16153@"/>
<name name="supports" arity="1" clause_i="6" since="OTP @OTP-16153@"/>
- <name name="supports" arity="1" clause_i="7" since="OTP 22.0"/>
+ <name name="supports" arity="1" clause_i="7" since="OTP @OTP-16432@"/>
+ <name name="supports" arity="1" clause_i="8" since="OTP 22.0"/>
<name name="supports" arity="2" clause_i="1" since="OTP 22.0"/>
<name name="supports" arity="2" clause_i="2" since="OTP 22.0"/>
<name name="supports" arity="2" clause_i="3" since="OTP 22.0"/>
@@ -1014,4 +1067,3 @@ server(Addr, Port) ->
</code>
</section>
</erlref>
-
diff --git a/erts/doc/src/start.xml b/erts/doc/src/start_cmd.xml
index 6eac47fe94..9f208d43bb 100644
--- a/erts/doc/src/start.xml
+++ b/erts/doc/src/start_cmd.xml
@@ -11,7 +11,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
-
+
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
@@ -19,7 +19,7 @@
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-
+
</legalnotice>
<title>start</title>
@@ -70,4 +70,3 @@
<seealso marker="start_erl"><c>start_erl(1)</c></seealso></p>
</section>
</comref>
-
diff --git a/erts/doc/src/start_erl.xml b/erts/doc/src/start_erl_cmd.xml
index 4887d4606e..e9cb248da4 100644
--- a/erts/doc/src/start_erl.xml
+++ b/erts/doc/src/start_erl_cmd.xml
@@ -11,7 +11,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
-
+
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
@@ -19,7 +19,7 @@
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-
+
</legalnotice>
<title>start_erl</title>
@@ -89,7 +89,7 @@
only option <c>-rootdir</c> is specified, the directory is
assumed to be &lt;Erlang root&gt;\\releases.</p>
</item>
- <tag><c>-rootdir &lt;Erlang root directory&gt;</c></tag>
+ <tag><c>-rootdir &lt;Erlang root directory&gt;</c></tag>
<item>
<p>Mandatory if <c>-reldir</c> is not specified and no
<c><![CDATA[RELDIR]]></c> exists in the environment. This
@@ -166,4 +166,3 @@
<c>release_handler(3)</c></seealso></p>
</section>
</comref>
-
diff --git a/erts/doc/src/werl.xml b/erts/doc/src/werl_cmd.xml
index 792fe204e8..4f25565c64 100644
--- a/erts/doc/src/werl.xml
+++ b/erts/doc/src/werl_cmd.xml
@@ -11,7 +11,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
-
+
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
@@ -19,7 +19,7 @@
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-
+
</legalnotice>
<title>werl</title>
@@ -62,7 +62,7 @@
<p>In cases where you want to redirect standard input and/or
standard output or use Erlang in a pipeline, <c>werl</c> is
not suitable, and the <c>erl</c> program is to be used instead.</p>
-
+
<p>The <c>werl</c> window is in many ways modeled after the <c>xterm</c>
window present on other platforms, as the <c>xterm</c> model
fits well with line-oriented command-based interaction. This
@@ -102,7 +102,7 @@
use <c>Ctrl-P</c>.</p>
</item>
</list>
-
+
<p>A drop-down box in the toolbar contains the command
history. Selecting a command in the drop-down box inserts the command
at the prompt, as if you used the keyboard to retrieve the
@@ -115,4 +115,3 @@
</list>
</description>
</comref>
-
diff --git a/erts/emulator/Makefile.in b/erts/emulator/Makefile.in
index fb56eadf39..e0fca8b082 100644
--- a/erts/emulator/Makefile.in
+++ b/erts/emulator/Makefile.in
@@ -269,15 +269,16 @@ STRIP = strip
PERL = @PERL@
MKDIR = @MKDIR@
-USING_MINGW=@MIXED_CYGWIN_MINGW@
-MIXED_MSYS=@MIXED_MSYS@
+USING_MINGW=@MIXED_MINGW@
ifeq ($(TARGET),win32)
LIB_PREFIX=
LIB_SUFFIX=.lib
+EXE_SUFFIX=.exe
else
LIB_PREFIX=lib
LIB_SUFFIX=.a
+EXE_SUFFIX=
endif
ifeq (@EMU_LOCK_CHECKING@,yes)
@@ -308,9 +309,6 @@ ifeq ($(TARGET), win32)
GEN_OPT_FLGS = $(OPT_LEVEL)
UNROLL_FLG =
RC=rc.sh
-ifeq ($(MIXED_MSYS), yes)
-MAKE_PRELOAD_EXTRA = -msys
-endif
ifeq ($(USING_MINGW), yes)
RES_EXT = @OBJEXT@
MAKE_PRELOAD_EXTRA += " -windres"
@@ -461,6 +459,8 @@ endif
include zlib/zlib.mk
include pcre/pcre.mk
+YCF_SOURCE_DIR = $(ERL_TOP)/erts/lib_src/yielding_c_fun
+include $(ERL_TOP)/erts/lib_src/yielding_c_fun/main_target.mk
$(ERTS_LIB):
$(V_at)cd $(ERTS_LIB_DIR) && $(MAKE) $(TYPE)
@@ -474,6 +474,10 @@ clean:
$(RM) -r pcre/obj/$(TARGET) $(PCRE_GENINC)
$(RM) -r zlib/obj/$(TARGET)
$(RM) -r bin/$(TARGET)
+ $(RM) $(YCF_EXECUTABLE)
+ $(RM) $(YCF_SOURCE_DIR)/*.o
+ $(RM) $(YCF_SOURCE_DIR)/lib/tiny_regex_c/*.o
+ $(RM) $(YCF_SOURCE_DIR)/lib/simple_c_gc/*.o
cd $(ERTS_LIB_DIR) && $(MAKE) clean
.PHONY: docs
@@ -599,7 +603,6 @@ endif
$(TTF_DIR)/erl_bif_table.c \
$(TTF_DIR)/erl_bif_table.h \
-$(TTF_DIR)/erl_bif_wrap.c \
$(TTF_DIR)/erl_bif_list.h \
$(TTF_DIR)/erl_atom_table.c \
$(TTF_DIR)/erl_atom_table.h \
@@ -626,7 +629,24 @@ $(TTF_DIR)/driver_tab.c: Makefile.in utils/make_driver_tab
$(gen_verbose)LANG=C $(PERL) utils/make_driver_tab -o $@ -nifs $(NIF_OBJS) $(STATIC_NIF_LIBS) -drivers $(DRV_OBJS) $(STATIC_DRIVER_LIBS)
GENERATE += $(TTF_DIR)/driver_tab.c
-
+beam/erl_db_insert_list.ycf.h: $(YCF_EXECUTABLE) beam/erl_db.c
+ $(gen_verbose)$(YCF_EXECUTABLE) \
+ -yield \
+ -static_aux_funs \
+ -only_yielding_funs \
+ -output_file_name beam/erl_db_insert_list.ycf.h \
+ -f ets_insert_2_list_check \
+ -f ets_insert_new_2_list_has_member \
+ -f ets_insert_2_list_from_p_heap \
+ -f ets_insert_2_list_destroy_copied_dbterms \
+ -f ets_insert_2_list_copy_term_list \
+ -f ets_insert_new_2_dbterm_list_has_member \
+ -f ets_insert_2_list_insert_db_term_list \
+ -f ets_insert_2_list \
+ -fnoauto ets_insert_2_list_lock_tbl \
+ beam/erl_db.c
+
+GENERATE += beam/erl_db_insert_list.ycf.h
# Preloaded code.
#
@@ -886,7 +906,6 @@ RUN_OBJS += \
$(OBJDIR)/erl_bif_persistent.o \
$(OBJDIR)/erl_bif_atomics.o $(OBJDIR)/erl_bif_counters.o \
$(OBJDIR)/erl_bif_trace.o $(OBJDIR)/erl_bif_unique.o \
- $(OBJDIR)/erl_bif_wrap.o $(OBJDIR)/erl_nfunc_sched.o \
$(OBJDIR)/erl_guard_bifs.o $(OBJDIR)/erl_dirty_bif_wrap.o \
$(OBJDIR)/erl_trace.o $(OBJDIR)/copy.o \
$(OBJDIR)/utils.o $(OBJDIR)/bif.o \
@@ -922,7 +941,8 @@ RUN_OBJS += \
$(OBJDIR)/erl_ptab.o $(OBJDIR)/erl_map.o \
$(OBJDIR)/erl_msacc.o $(OBJDIR)/erl_lock_flags.o \
$(OBJDIR)/erl_io_queue.o $(OBJDIR)/erl_db_catree.o \
- $(ESOCK_RUN_OBJS) $(OBJDIR)/erl_flxctr.o
+ $(ESOCK_RUN_OBJS) $(OBJDIR)/erl_flxctr.o \
+ $(OBJDIR)/erl_nfunc_sched.o
LTTNG_OBJS = $(OBJDIR)/erlang_lttng.o
@@ -1218,7 +1238,7 @@ DEP_FLAGS=-MM $(subst -O2,,$(CFLAGS)) $(INCLUDES) -I../etc/win32 \
-Idrivers/common -Idrivers/$(ERLANG_OSTYPE) \
-Inifs/common -Inifs/$(ERLANG_OSTYPE)
-# ifeq (@MIXED_CYGWIN_VC@,yes)
+# ifeq (@MIXED_VC@,yes)
# VC++ used for compiling. If __GNUC__ is defined we will include
# other headers then when compiling which will result in faulty
# dependencies.
@@ -1285,6 +1305,7 @@ endif
$(V_at)cd $(ERTS_LIB_DIR) && $(MAKE) depend
endif
+ifneq ($(ERTS_SKIP_DEPEND),true)
ifneq ($(MAKECMDGOALS),clean)
ifneq ($(MAKECMDGOALS),generate)
ifndef VOID_EMULATOR
@@ -1292,3 +1313,6 @@ ifndef VOID_EMULATOR
endif
endif
endif
+endif
+
+include $(ERL_TOP)/make/app_targets.mk
diff --git a/erts/emulator/beam/arith_instrs.tab b/erts/emulator/beam/arith_instrs.tab
index f14b376419..335b121ad8 100644
--- a/erts/emulator/beam/arith_instrs.tab
+++ b/erts/emulator/beam/arith_instrs.tab
@@ -217,6 +217,11 @@ i_int_div(Fail, Op1, Op2, Dst) {
$BIF_ERROR_ARITY_2($Fail, BIF_intdiv_2, op1, op2);
} else if (ERTS_LIKELY(is_both_small(op1, op2))) {
Sint ires = signed_val(op1) / signed_val(op2);
+
+ /* We could skip this check if it weren't for the fact that dividing
+ * MIN_SMALL by -1 causes an overflow, and we have nothing to gain from
+ * fixed-point optimizing this instruction since there's no
+ * __builtin_div_overflow. */
if (ERTS_LIKELY(IS_SSMALL(ires))) {
$Dst = make_small(ires);
$NEXT0();
@@ -241,7 +246,14 @@ rem.execute(Fail, Dst) {
c_p->freason = BADARITH;
$BIF_ERROR_ARITY_2($Fail, BIF_rem_2, RemOp1, RemOp2);
} else if (ERTS_LIKELY(is_both_small(RemOp1, RemOp2))) {
- $Dst = make_small(signed_val(RemOp1) % signed_val(RemOp2));
+ Sint lhs_untagged, rhs_untagged, untagged_result;
+
+ /* See plus.execute */
+ lhs_untagged = (RemOp1 & ~_TAG_IMMED1_MASK);
+ rhs_untagged = (RemOp2 & ~_TAG_IMMED1_MASK);
+ untagged_result = lhs_untagged % rhs_untagged;
+
+ $Dst = untagged_result | _TAG_IMMED1_SMALL;
$NEXT0();
} else {
$OUTLINED_ARITH_2($Fail, int_rem, BIF_rem_2, RemOp1, RemOp2, $Dst);
@@ -447,10 +459,10 @@ shift.execute(Fail, Dst) {
reg[1] = Op2;
SWAPOUT;
if (IsOpCode(I[0], i_bsl_ssjd)) {
- I = handle_error(c_p, I, reg, &bif_export[BIF_bsl_2]->info.mfa);
+ I = handle_error(c_p, I, reg, &bif_trap_export[BIF_bsl_2].info.mfa);
} else {
ASSERT(IsOpCode(I[0], i_bsr_ssjd));
- I = handle_error(c_p, I, reg, &bif_export[BIF_bsr_2]->info.mfa);
+ I = handle_error(c_p, I, reg, &bif_trap_export[BIF_bsr_2].info.mfa);
}
goto post_error_handling;
}
diff --git a/erts/emulator/beam/atom.names b/erts/emulator/beam/atom.names
index 7046eb5e65..3ea7677b6d 100644
--- a/erts/emulator/beam/atom.names
+++ b/erts/emulator/beam/atom.names
@@ -68,6 +68,7 @@ atom ErtsSecretAtom='3RT$'
atom DOWN='DOWN'
atom UP='UP'
atom EXIT='EXIT'
+atom abandoned
atom abort
atom abs_path
atom absoluteURI
@@ -116,6 +117,7 @@ atom awaiting_load
atom awaiting_unload
atom backtrace backtrace_depth
atom badarg badarith badarity badfile badfun badkey badmap badmatch badsig
+atom badopt
atom bag
atom band
atom big
@@ -195,6 +197,7 @@ atom current_location
atom current_stacktrace
atom data
atom debug_flags
+atom decentralized_counters
atom decimals
atom default
atom delay_trap
@@ -216,6 +219,7 @@ atom dist_cmd
atom dist_ctrl_put_data
atom dist_ctrlr
atom dist_data
+atom dist_spawn_init
atom Div='/'
atom div
atom dmonitor_node
@@ -246,6 +250,7 @@ atom erlang
atom erl_signal_server
atom error_handler
atom error_logger
+atom error_only
atom erts_code_purger
atom erts_debug
atom erts_dflags
@@ -293,6 +298,7 @@ atom gc_minor_start
atom Ge='>='
atom generational
atom get_all_trap
+atom get_iovec
atom get_internal_state_blocked
atom get_seq_token
atom get_size
@@ -345,6 +351,8 @@ atom is_constant
atom is_seq_trace
atom iterator
atom io
+atom iodata
+atom iovec
atom keypos
atom kill
atom killed
@@ -408,6 +416,7 @@ atom min_bin_vheap_size
atom minor
atom minor_version
atom Minus='-'
+atom MinusMinus='--'
atom module
atom module_info
atom monitored_by
@@ -453,6 +462,7 @@ atom noeol
atom noproc
atom normal
atom nosuspend
+atom no_fail
atom no_float
atom no_integer
atom no_network
@@ -498,6 +508,7 @@ atom packet
atom packet_size
atom parallelism
atom Plus='+'
+atom PlusPlus='++'
atom pause
atom pending
atom pending_driver
@@ -550,10 +561,13 @@ atom register
atom registered_name
atom reload
atom rem
+atom reply
+atom reply_tag
atom report_errors
atom reset
atom reset_seq_trace
atom restart
+atom resume
atom return_from
atom return_to
atom return_trace
@@ -612,6 +626,10 @@ atom silent
atom size
atom spawn_executable
atom spawn_driver
+atom spawn_init
+atom spawn_reply
+atom spawn_request
+atom spawn_request_yield
atom spawned
atom ssl_tls
atom stack_size
@@ -622,6 +640,7 @@ atom stop
atom stream
atom strict_monotonic
atom strict_monotonic_timestamp
+atom success_only
atom sunrm
atom suspend
atom suspended
@@ -689,4 +708,4 @@ atom xor
atom x86
atom yes
atom yield
-atom nifs
+atom nifs \ No newline at end of file
diff --git a/erts/emulator/beam/beam_bif_load.c b/erts/emulator/beam/beam_bif_load.c
index c530063fa1..e130baa1c0 100644
--- a/erts/emulator/beam/beam_bif_load.c
+++ b/erts/emulator/beam/beam_bif_load.c
@@ -144,7 +144,7 @@ BIF_RETTYPE code_make_stub_module_3(BIF_ALIST_3)
BIF_ERROR(BIF_P, BADARG);
if (!erts_try_seize_code_write_permission(BIF_P)) {
- ERTS_BIF_YIELD3(bif_export[BIF_code_make_stub_module_3],
+ ERTS_BIF_YIELD3(&bif_trap_export[BIF_code_make_stub_module_3],
BIF_P, BIF_ARG_1, BIF_ARG_2, BIF_ARG_3);
}
@@ -279,7 +279,7 @@ finish_loading_1(BIF_ALIST_1)
int do_commit = 0;
if (!erts_try_seize_code_write_permission(BIF_P)) {
- ERTS_BIF_YIELD1(bif_export[BIF_finish_loading_1], BIF_P, BIF_ARG_1);
+ ERTS_BIF_YIELD1(&bif_trap_export[BIF_finish_loading_1], BIF_P, BIF_ARG_1);
}
/*
@@ -641,7 +641,7 @@ BIF_RETTYPE delete_module_1(BIF_ALIST_1)
}
if (!erts_try_seize_code_write_permission(BIF_P)) {
- ERTS_BIF_YIELD1(bif_export[BIF_delete_module_1], BIF_P, BIF_ARG_1);
+ ERTS_BIF_YIELD1(&bif_trap_export[BIF_delete_module_1], BIF_P, BIF_ARG_1);
}
{
@@ -768,7 +768,7 @@ BIF_RETTYPE finish_after_on_load_2(BIF_ALIST_2)
}
if (!erts_try_seize_code_write_permission(BIF_P)) {
- ERTS_BIF_YIELD2(bif_export[BIF_finish_after_on_load_2],
+ ERTS_BIF_YIELD2(&bif_trap_export[BIF_finish_after_on_load_2],
BIF_P, BIF_ARG_1, BIF_ARG_2);
}
@@ -819,21 +819,25 @@ BIF_RETTYPE finish_after_on_load_2(BIF_ALIST_2)
*/
num_exps = export_list_size(code_ix);
for (i = 0; i < num_exps; i++) {
- Export *ep = export_list(i,code_ix);
- if (ep == NULL || ep->info.mfa.module != BIF_ARG_1) {
- continue;
- }
- if (ep->beam[1] != 0) {
- ep->addressv[code_ix] = (void *) ep->beam[1];
- ep->beam[1] = 0;
- } else {
- if (ep->addressv[code_ix] == ep->beam &&
- BeamIsOpCode(ep->beam[0], op_apply_bif)) {
- continue;
- }
- ep->addressv[code_ix] = ep->beam;
- ep->beam[0] = BeamOpCodeAddr(op_call_error_handler);
- }
+ Export *ep = export_list(i, code_ix);
+
+ if (ep == NULL || ep->info.mfa.module != BIF_ARG_1) {
+ continue;
+ }
+
+ DBG_CHECK_EXPORT(ep, code_ix);
+
+ if (ep->trampoline.not_loaded.deferred != 0) {
+ ep->addressv[code_ix] = (void*)ep->trampoline.not_loaded.deferred;
+ ep->trampoline.not_loaded.deferred = 0;
+ } else {
+ if (ep->bif_number != -1) {
+ continue;
+ }
+
+ ep->addressv[code_ix] = ep->trampoline.raw;
+ ep->trampoline.op = BeamOpCodeAddr(op_call_error_handler);
+ }
}
modp->curr.code_hdr->on_load_function_ptr = NULL;
@@ -856,10 +860,11 @@ BIF_RETTYPE finish_after_on_load_2(BIF_ALIST_2)
if (ep == NULL || ep->info.mfa.module != BIF_ARG_1) {
continue;
}
- if (BeamIsOpCode(ep->beam[0], op_apply_bif)) {
+ if (ep->bif_number != -1) {
continue;
}
- ep->beam[1] = 0;
+
+ ep->trampoline.not_loaded.deferred = 0;
}
}
erts_release_code_write_permission();
@@ -1109,16 +1114,15 @@ check_process_code(Process* rp, Module* modp, int *redsp, int fcalls)
mod_size = modp->old.code_length;
/*
- * Check if current instruction or continuation pointer points into module.
+ * Check if the instruction pointer points into module.
*/
- if (ErtsInArea(rp->i, mod_start, mod_size)
- || ErtsInArea(rp->cp, mod_start, mod_size)) {
+ if (ErtsInArea(rp->i, mod_start, mod_size)) {
return am_true;
}
-
+
*redsp += 1;
- if (erts_check_nif_export_in_area(rp, mod_start, mod_size))
+ if (erts_check_nfunc_in_area(rp, mod_start, mod_size))
return am_true;
*redsp += (STACK_START(rp) - rp->stop) / 32;
@@ -1657,7 +1661,7 @@ BIF_RETTYPE erts_internal_release_literal_area_switch_0(BIF_ALIST_0)
ASSERT(old_area);
ERTS_VBUMP_ALL_REDS(BIF_P);
- BIF_TRAP0(bif_export[BIF_erts_internal_release_literal_area_switch_0],
+ BIF_TRAP0(&bif_trap_export[BIF_erts_internal_release_literal_area_switch_0],
BIF_P);
}
@@ -2019,7 +2023,7 @@ BIF_RETTYPE erts_internal_purge_module_2(BIF_ALIST_2)
BIF_ERROR(BIF_P, BADARG);
if (!erts_try_seize_code_write_permission(BIF_P)) {
- ERTS_BIF_YIELD2(bif_export[BIF_erts_internal_purge_module_2],
+ ERTS_BIF_YIELD2(&bif_trap_export[BIF_erts_internal_purge_module_2],
BIF_P, BIF_ARG_1, BIF_ARG_2);
}
@@ -2043,23 +2047,22 @@ BIF_RETTYPE erts_internal_purge_module_2(BIF_ALIST_2)
ERTS_BIF_PREP_RET(ret, am_false);
}
else {
- /*
- * Unload any NIF library
- */
- if (modp->old.nif != NULL
- || IF_HIPE(hipe_purge_need_blocking(modp))) {
- /* ToDo: Do unload nif without blocking */
+ if (IF_HIPE(hipe_purge_need_blocking(modp))) {
erts_rwunlock_old_code(code_ix);
erts_proc_unlock(BIF_P, ERTS_PROC_LOCK_MAIN);
erts_thr_progress_block();
is_blocking = 1;
erts_rwlock_old_code(code_ix);
- if (modp->old.nif) {
- erts_unload_nif(modp->old.nif);
- modp->old.nif = NULL;
- }
}
+ /*
+ * Unload any NIF library
+ */
+ if (modp->old.nif) {
+ erts_unload_nif(modp->old.nif);
+ modp->old.nif = NULL;
+ }
+
/*
* Remove the old code.
*/
@@ -2191,25 +2194,28 @@ delete_code(Module* modp)
for (i = 0; i < num_exps; i++) {
Export *ep = export_list(i, code_ix);
if (ep != NULL && (ep->info.mfa.module == module)) {
- if (ep->addressv[code_ix] == ep->beam) {
- if (BeamIsOpCode(ep->beam[0], op_apply_bif)) {
- continue;
- }
- else if (BeamIsOpCode(ep->beam[0], op_i_generic_breakpoint)) {
+ if (ep->addressv[code_ix] == ep->trampoline.raw) {
+ if (BeamIsOpCode(ep->trampoline.op, op_i_generic_breakpoint)) {
ERTS_LC_ASSERT(erts_thr_progress_is_blocking());
ASSERT(modp->curr.num_traced_exports > 0);
DBG_TRACE_MFA_P(&ep->info.mfa,
"export trace cleared, code_ix=%d", code_ix);
- erts_clear_export_break(modp, &ep->info);
+ erts_clear_export_break(modp, ep);
}
else {
- ASSERT(BeamIsOpCode(ep->beam[0], op_call_error_handler) ||
+ ASSERT(BeamIsOpCode(ep->trampoline.op, op_call_error_handler) ||
!erts_initialized);
}
}
- ep->addressv[code_ix] = ep->beam;
- ep->beam[0] = BeamOpCodeAddr(op_call_error_handler);
- ep->beam[1] = 0;
+
+ if (ep->bif_number != -1 && ep->is_bif_traced) {
+ /* Code unloading kills both global and local call tracing. */
+ ep->is_bif_traced = 0;
+ }
+
+ ep->addressv[code_ix] = ep->trampoline.raw;
+ ep->trampoline.op = BeamOpCodeAddr(op_call_error_handler);
+ ep->trampoline.not_loaded.deferred = 0;
DBG_TRACE_MFA_P(&ep->info.mfa,
"export invalidation, code_ix=%d", code_ix);
}
diff --git a/erts/emulator/beam/beam_bp.c b/erts/emulator/beam/beam_bp.c
index da43f65780..4144b0e751 100644
--- a/erts/emulator/beam/beam_bp.c
+++ b/erts/emulator/beam/beam_bp.c
@@ -207,9 +207,6 @@ erts_bp_match_functions(BpFunctions* f, ErtsCodeMFA *mfa, int specified)
if (erts_is_function_native(ci)) {
continue;
}
- if (is_nil(ci->mfa.module)) { /* Ignore BIF stub */
- continue;
- }
switch (specified) {
case 3:
if (ci->mfa.arity != mfa->arity)
@@ -244,8 +241,10 @@ erts_bp_match_export(BpFunctions* f, ErtsCodeMFA *mfa, int specified)
f->matching = (BpFunction *) Alloc(num_exps*sizeof(BpFunction));
ne = 0;
for (i = 0; i < num_exps; i++) {
- Export* ep = export_list(i, code_ix);
- BeamInstr* pc;
+ BeamInstr *func;
+ Export* ep;
+
+ ep = export_list(i, code_ix);
switch (specified) {
case 3:
@@ -263,19 +262,20 @@ erts_bp_match_export(BpFunctions* f, ErtsCodeMFA *mfa, int specified)
ASSERT(0);
}
- pc = ep->beam;
- if (ep->addressv[code_ix] == pc) {
- if (BeamIsOpCode(*pc, op_apply_bif) ||
- BeamIsOpCode(*pc, op_call_error_handler)) {
- continue;
- }
- ASSERT(BeamIsOpCode(*pc, op_i_generic_breakpoint));
- } else if (erts_is_function_native(erts_code_to_codeinfo(ep->addressv[code_ix]))) {
- continue;
- }
+ func = ep->addressv[code_ix];
+
+ if (func == ep->trampoline.raw) {
+ if (BeamIsOpCode(*func, op_call_error_handler)) {
+ continue;
+ }
+ ASSERT(BeamIsOpCode(*func, op_i_generic_breakpoint));
+ } else if (erts_is_function_native(erts_code_to_codeinfo(func))) {
+ continue;
+ }
f->matching[ne].ci = &ep->info;
f->matching[ne].mod = erts_get_module(ep->info.mfa.module, code_ix);
+
ne++;
}
@@ -305,18 +305,6 @@ erts_consolidate_bp_data(BpFunctions* f, int local)
}
}
-void
-erts_consolidate_bif_bp_data(void)
-{
- int i;
-
- ERTS_LC_ASSERT(erts_has_code_write_permission());
- for (i = 0; i < BIF_SIZE; i++) {
- Export *ep = bif_export[i];
- consolidate_bp_data(0, &ep->info, 0);
- }
-}
-
static void
consolidate_bp_data(Module* modp, ErtsCodeInfo *ci, int local)
{
@@ -495,7 +483,7 @@ erts_set_mtrace_break(BpFunctions* f, Binary *match_spec, ErtsTracer tracer)
}
void
-erts_set_call_trace_bif(ErtsCodeInfo *ci, Binary *match_spec, int local)
+erts_set_export_trace(ErtsCodeInfo *ci, Binary *match_spec, int local)
{
Uint flags = local ? ERTS_BPF_LOCAL_TRACE : ERTS_BPF_GLOBAL_TRACE;
@@ -503,25 +491,6 @@ erts_set_call_trace_bif(ErtsCodeInfo *ci, Binary *match_spec, int local)
}
void
-erts_set_mtrace_bif(ErtsCodeInfo *ci, Binary *match_spec, ErtsTracer tracer)
-{
- set_function_break(ci, match_spec, ERTS_BPF_META_TRACE, 0, tracer);
-}
-
-void
-erts_set_time_trace_bif(ErtsCodeInfo *ci, enum erts_break_op count_op)
-{
- set_function_break(ci, NULL,
- ERTS_BPF_TIME_TRACE|ERTS_BPF_TIME_TRACE_ACTIVE,
- count_op, erts_tracer_nil);
-}
-
-void
-erts_clear_time_trace_bif(ErtsCodeInfo *ci) {
- clear_function_break(ci, ERTS_BPF_TIME_TRACE|ERTS_BPF_TIME_TRACE_ACTIVE);
-}
-
-void
erts_set_debug_break(BpFunctions* f) {
set_break(f, NULL, ERTS_BPF_DEBUG, 0, erts_tracer_nil);
}
@@ -547,7 +516,7 @@ erts_clear_trace_break(BpFunctions* f)
}
void
-erts_clear_call_trace_bif(ErtsCodeInfo *ci, int local)
+erts_clear_export_trace(ErtsCodeInfo *ci, int local)
{
GenericBp* g = ci->u.gen_bp;
@@ -566,12 +535,6 @@ erts_clear_mtrace_break(BpFunctions* f)
}
void
-erts_clear_mtrace_bif(ErtsCodeInfo *ci)
-{
- clear_function_break(ci, ERTS_BPF_META_TRACE);
-}
-
-void
erts_clear_debug_break(BpFunctions* f)
{
ERTS_LC_ASSERT(erts_thr_progress_is_blocking());
@@ -630,58 +593,56 @@ erts_clear_module_break(Module *modp) {
}
void
-erts_clear_export_break(Module* modp, ErtsCodeInfo *ci)
+erts_clear_export_break(Module* modp, Export *ep)
{
+ ErtsCodeInfo *ci;
+
ERTS_LC_ASSERT(erts_thr_progress_is_blocking());
+ ci = &ep->info;
+
+ ASSERT(erts_codeinfo_to_code(ci) == ep->trampoline.raw);
+
+ ASSERT(BeamIsOpCode(ep->trampoline.op, op_i_generic_breakpoint));
+ ep->trampoline.op = 0;
+
clear_function_break(ci, ERTS_BPF_ALL);
erts_commit_staged_bp();
- *erts_codeinfo_to_code(ci) = (BeamInstr) 0;
+
consolidate_bp_data(modp, ci, 0);
ASSERT(ci->u.gen_bp == NULL);
}
/*
- * If c_p->cp is a trace return instruction, we set cp
- * to be the place where we again start to execute code.
+ * If the topmost continuation pointer on the stack is a trace return
+ * instruction, we modify it to be the place where we again start to
+ * execute code.
*
- * cp is used by match spec {caller} to get the calling
- * function, and if we don't do this fixup it will be
- * 'undefined'. This has the odd side effect of {caller}
- * not really being which function is the caller, but
- * rather which function we are about to return to.
+ * This continuation pointer is used by match spec {caller} to get the
+ * calling function, and if we don't do this fixup it will be
+ * 'undefined'. This has the odd side effect of {caller} not really
+ * being the function which is the caller, but rather the function
+ * which we are about to return to.
*/
static void fixup_cp_before_trace(Process *c_p, int *return_to_trace)
{
- Eterm *cpp, *E = c_p->stop;
- BeamInstr w = *c_p->cp;
- if (BeamIsOpCode(w, op_return_trace)) {
- cpp = &E[2];
- } else if (BeamIsOpCode(w, op_i_return_to_trace)) {
- *return_to_trace = 1;
- cpp = &E[0];
- } else if (BeamIsOpCode(w, op_i_return_time_trace)) {
- cpp = &E[0];
- } else {
- cpp = NULL;
- }
- if (cpp) {
- for (;;) {
- BeamInstr w = *cp_val(*cpp);
- if (BeamIsOpCode(w, op_return_trace)) {
- cpp += 3;
- } else if (BeamIsOpCode(w, op_i_return_to_trace)) {
- *return_to_trace = 1;
- cpp += 1;
- } else if (BeamIsOpCode(w, op_i_return_time_trace)) {
- cpp += 2;
- } else {
- break;
- }
+ Eterm *cpp = c_p->stop;
+
+ for (;;) {
+ BeamInstr w = *cp_val(*cpp);
+ if (BeamIsOpCode(w, op_return_trace)) {
+ cpp += 3;
+ } else if (BeamIsOpCode(w, op_i_return_to_trace)) {
+ *return_to_trace = 1;
+ cpp += 1;
+ } else if (BeamIsOpCode(w, op_i_return_time_trace)) {
+ cpp += 2;
+ } else {
+ break;
}
- c_p->cp = (BeamInstr *) cp_val(*cpp);
- ASSERT(is_CP(*cpp));
}
+ c_p->stop[0] = (Eterm) cp_val(*cpp);
+ ASSERT(is_CP(*cpp));
}
BeamInstr
@@ -742,13 +703,14 @@ erts_generic_breakpoint(Process* c_p, ErtsCodeInfo *info, Eterm* reg)
}
if (bp_flags & ERTS_BPF_TIME_TRACE_ACTIVE) {
- Eterm w;
+ BeamInstr w;
+ Eterm* E;
ErtsCodeInfo* prev_info = erts_trace_time_call(c_p, info, bp->time);
- w = (BeamInstr) *c_p->cp;
+ E = c_p->stop;
+ w = *(BeamInstr*) E[0];
if (! (BeamIsOpCode(w, op_i_return_time_trace) ||
BeamIsOpCode(w, op_return_trace) ||
BeamIsOpCode(w, op_i_return_to_trace)) ) {
- Eterm* E = c_p->stop;
ASSERT(c_p->htop <= E && E <= c_p->hend);
if (E - 2 < c_p->htop) {
(void) erts_garbage_collect(c_p, 2, reg, info->mfa.arity);
@@ -759,9 +721,8 @@ erts_generic_breakpoint(Process* c_p, ErtsCodeInfo *info, Eterm* reg)
ASSERT(c_p->htop <= E && E <= c_p->hend);
E -= 2;
- E[0] = prev_info ? make_cp(erts_codeinfo_to_code(prev_info)) : NIL;
- E[1] = make_cp(c_p->cp); /* original return address */
- c_p->cp = beam_return_time_trace;
+ E[1] = prev_info ? make_cp(erts_codeinfo_to_code(prev_info)) : NIL;
+ E[0] = (Eterm) beam_return_time_trace;
c_p->stop = E;
}
}
@@ -773,237 +734,24 @@ erts_generic_breakpoint(Process* c_p, ErtsCodeInfo *info, Eterm* reg)
}
}
-/*
- * Entry point called by the trace wrap functions in erl_bif_wrap.c
- *
- * The trace wrap functions are themselves called through the export
- * entries instead of the original BIF functions.
- */
-Eterm
-erts_bif_trace(int bif_index, Process* p, Eterm* args, BeamInstr* I)
-{
- Eterm result;
- Eterm (*func)(Process*, Eterm*, BeamInstr*);
- Export* ep = bif_export[bif_index];
- Uint32 flags = 0, flags_meta = 0;
- ErtsTracer meta_tracer = erts_tracer_nil;
- int applying = (I == ep->beam); /* Yup, the apply code for a bif
- * is actually in the
- * export entry */
- BeamInstr *cp = p->cp;
- GenericBp* g;
- GenericBpData* bp = NULL;
- Uint bp_flags = 0;
- int return_to_trace = 0;
-
- ERTS_CHK_HAVE_ONLY_MAIN_PROC_LOCK(p);
-
- g = ep->info.u.gen_bp;
- if (g) {
- bp = &g->data[erts_active_bp_ix()];
- bp_flags = bp->flags;
- }
-
- /*
- * Make continuation pointer OK, it is not during direct BIF calls,
- * but it is correct during apply of bif.
- */
- if (!applying) {
- p->cp = I;
- } else {
- fixup_cp_before_trace(p, &return_to_trace);
- }
- if (bp_flags & (ERTS_BPF_LOCAL_TRACE|ERTS_BPF_GLOBAL_TRACE) &&
- IS_TRACED_FL(p, F_TRACE_CALLS)) {
- int local = !!(bp_flags & ERTS_BPF_LOCAL_TRACE);
- flags = erts_call_trace(p, &ep->info, bp->local_ms, args,
- local, &ERTS_TRACER(p));
- }
- if (bp_flags & ERTS_BPF_META_TRACE) {
- ErtsTracer old_tracer;
-
- meta_tracer = erts_atomic_read_nob(&bp->meta_tracer->tracer);
- old_tracer = meta_tracer;
- flags_meta = erts_call_trace(p, &ep->info, bp->meta_ms, args,
- 0, &meta_tracer);
-
- if (!ERTS_TRACER_COMPARE(old_tracer, meta_tracer)) {
- ErtsTracer new_tracer = erts_tracer_nil;
- erts_tracer_update(&new_tracer, meta_tracer);
- if (old_tracer == erts_atomic_cmpxchg_acqb(
- &bp->meta_tracer->tracer,
- (erts_aint_t)new_tracer,
- (erts_aint_t)old_tracer)) {
- ERTS_TRACER_CLEAR(&old_tracer);
- } else {
- ERTS_TRACER_CLEAR(&new_tracer);
- }
- }
- }
- if (bp_flags & ERTS_BPF_TIME_TRACE_ACTIVE &&
- IS_TRACED_FL(p, F_TRACE_CALLS)) {
- erts_trace_time_call(p, &ep->info, bp->time);
- }
-
- /* Restore original continuation pointer (if changed). */
- p->cp = cp;
-
- func = bif_table[bif_index].f;
-
- result = func(p, args, I);
-
- if (erts_nif_export_check_save_trace(p, result,
- applying, ep,
- cp, flags,
- flags_meta, I,
- meta_tracer)) {
- /*
- * erts_bif_trace_epilogue() will be called
- * later when appropriate via the NIF export
- * scheduling functionality...
- */
- return result;
- }
-
- return erts_bif_trace_epilogue(p, result, applying, ep, cp,
- flags, flags_meta, I,
- meta_tracer);
-}
-
-Eterm
-erts_bif_trace_epilogue(Process *p, Eterm result, int applying,
- Export* ep, BeamInstr *cp, Uint32 flags,
- Uint32 flags_meta, BeamInstr* I,
- ErtsTracer meta_tracer)
-{
- if (applying && (flags & MATCH_SET_RETURN_TO_TRACE)) {
- BeamInstr i_return_trace = beam_return_trace[0];
- BeamInstr i_return_to_trace = beam_return_to_trace[0];
- BeamInstr i_return_time_trace = beam_return_time_trace[0];
- Eterm *cpp;
- /* Maybe advance cp to skip trace stack frames */
- for (cpp = p->stop; ; cp = cp_val(*cpp++)) {
- if (*cp == i_return_trace) {
- /* Skip stack frame variables */
- while (is_not_CP(*cpp)) cpp++;
- cpp += 2; /* Skip return_trace parameters */
- } else if (*cp == i_return_time_trace) {
- /* Skip stack frame variables */
- while (is_not_CP(*cpp)) cpp++;
- cpp += 1; /* Skip return_time_trace parameters */
- } else if (*cp == i_return_to_trace) {
- /* A return_to trace message is going to be generated
- * by normal means, so we do not have to.
- */
- cp = NULL;
- break;
- } else break;
- }
- }
-
- /* Try to get these in the order
- * they usually appear in normal code... */
- if (is_non_value(result)) {
- Uint reason = p->freason;
- if (reason != TRAP) {
- Eterm class;
- Eterm value = p->fvalue;
- /* Expand error value like in handle_error() */
- if (reason & EXF_ARGLIST) {
- Eterm *tp;
- ASSERT(is_tuple(value));
- tp = tuple_val(value);
- value = tp[1];
- }
- if ((reason & EXF_THROWN) && (p->catches <= 0)) {
- Eterm *hp = HAlloc(p, 3);
- value = TUPLE2(hp, am_nocatch, value);
- reason = EXC_ERROR;
- }
- /* Note: expand_error_value() could theoretically
- * allocate on the heap, but not for any error
- * returned by a BIF, and it would do no harm,
- * just be annoying.
- */
- value = expand_error_value(p, reason, value);
- class = exception_tag[GET_EXC_CLASS(reason)];
-
- if (flags_meta & MATCH_SET_EXCEPTION_TRACE) {
- erts_trace_exception(p, &ep->info.mfa, class, value,
- &meta_tracer);
- }
- if (flags & MATCH_SET_EXCEPTION_TRACE) {
- erts_trace_exception(p, &ep->info.mfa, class, value,
- &ERTS_TRACER(p));
- }
- if ((flags & MATCH_SET_RETURN_TO_TRACE) && p->catches > 0) {
- /* can only happen if(local)*/
- Eterm *ptr = p->stop;
- ASSERT(is_CP(*ptr));
- ASSERT(ptr <= STACK_START(p));
- /* Search the nearest stack frame for a catch */
- while (++ptr < STACK_START(p)) {
- if (is_CP(*ptr)) break;
- if (is_catch(*ptr)) {
- if (applying) {
- /* Apply of BIF, cp is in calling function */
- if (cp) erts_trace_return_to(p, cp);
- } else {
- /* Direct bif call, I points into
- * calling function */
- erts_trace_return_to(p, I);
- }
- }
- }
- }
- if ((flags_meta|flags) & MATCH_SET_EXCEPTION_TRACE) {
- erts_proc_lock(p, ERTS_PROC_LOCKS_ALL_MINOR);
- ERTS_TRACE_FLAGS(p) |= F_EXCEPTION_TRACE;
- erts_proc_unlock(p, ERTS_PROC_LOCKS_ALL_MINOR);
- }
- }
- } else {
- if (flags_meta & MATCH_SET_RX_TRACE) {
- erts_trace_return(p, &ep->info.mfa, result, &meta_tracer);
- }
- /* MATCH_SET_RETURN_TO_TRACE cannot occur if(meta) */
- if (flags & MATCH_SET_RX_TRACE) {
- erts_trace_return(p, &ep->info.mfa, result, &ERTS_TRACER(p));
- }
- if (flags & MATCH_SET_RETURN_TO_TRACE &&
- IS_TRACED_FL(p, F_TRACE_RETURN_TO)) {
- /* can only happen if(local)*/
- if (applying) {
- /* Apply of BIF, cp is in calling function */
- if (cp) erts_trace_return_to(p, cp);
- } else {
- /* Direct bif call, I points into calling function */
- erts_trace_return_to(p, I);
- }
- }
- }
- ERTS_CHK_HAVE_ONLY_MAIN_PROC_LOCK(p);
- return result;
-}
-
static ErtsTracer
do_call_trace(Process* c_p, ErtsCodeInfo* info, Eterm* reg,
int local, Binary* ms, ErtsTracer tracer)
{
int return_to_trace = 0;
- BeamInstr *cp_save = c_p->cp;
Uint32 flags;
Uint need = 0;
+ Eterm cp_save;
Eterm* E = c_p->stop;
- fixup_cp_before_trace(c_p, &return_to_trace);
+ cp_save = E[0];
+ fixup_cp_before_trace(c_p, &return_to_trace);
ERTS_UNREQ_PROC_MAIN_LOCK(c_p);
flags = erts_call_trace(c_p, info, ms, reg, local, &tracer);
ERTS_REQ_PROC_MAIN_LOCK(c_p);
- /* restore cp after potential fixup */
- c_p->cp = cp_save;
+ E[0] = cp_save;
ASSERT(!ERTS_PROC_IS_EXITING(c_p));
if ((flags & MATCH_SET_RETURN_TO_TRACE) && !return_to_trace) {
@@ -1023,28 +771,23 @@ do_call_trace(Process* c_p, ErtsCodeInfo* info, Eterm* reg,
if (flags & MATCH_SET_RETURN_TO_TRACE && !return_to_trace) {
E -= 1;
ASSERT(c_p->htop <= E && E <= c_p->hend);
- E[0] = make_cp(c_p->cp);
- c_p->cp = beam_return_to_trace;
+ E[0] = (Eterm) beam_return_to_trace;
+ c_p->stop = E;
}
- if (flags & MATCH_SET_RX_TRACE)
- {
+ if (flags & MATCH_SET_RX_TRACE) {
E -= 3;
c_p->stop = E;
ASSERT(c_p->htop <= E && E <= c_p->hend);
ASSERT(is_CP((Eterm) (UWord) (&info->mfa.module)));
ASSERT(IS_TRACER_VALID(tracer));
- E[2] = make_cp(c_p->cp);
- E[1] = copy_object(tracer, c_p);
- E[0] = make_cp(&info->mfa.module);
- /* We ARE at the beginning of an instruction,
- the funcinfo is above i. */
- c_p->cp = (flags & MATCH_SET_EXCEPTION_TRACE) ?
- beam_exception_trace : beam_return_trace;
+ E[2] = copy_object(tracer, c_p);
+ E[1] = make_cp(&info->mfa.module);
+ E[0] = (Eterm) ((flags & MATCH_SET_EXCEPTION_TRACE) ?
+ beam_exception_trace : beam_return_trace);
erts_proc_lock(c_p, ERTS_PROC_LOCKS_ALL_MINOR);
ERTS_TRACE_FLAGS(c_p) |= F_EXCEPTION_TRACE;
erts_proc_unlock(c_p, ERTS_PROC_LOCKS_ALL_MINOR);
- } else
- c_p->stop = E;
+ }
return tracer;
}
diff --git a/erts/emulator/beam/beam_bp.h b/erts/emulator/beam/beam_bp.h
index a10965191e..827302b9b5 100644
--- a/erts/emulator/beam/beam_bp.h
+++ b/erts/emulator/beam/beam_bp.h
@@ -119,20 +119,16 @@ void erts_bp_free_matched_functions(BpFunctions* f);
void erts_install_breakpoints(BpFunctions* f);
void erts_uninstall_breakpoints(BpFunctions* f);
void erts_consolidate_bp_data(BpFunctions* f, int local);
-void erts_consolidate_bif_bp_data(void);
void erts_set_trace_break(BpFunctions *f, Binary *match_spec);
void erts_clear_trace_break(BpFunctions *f);
-void erts_set_call_trace_bif(ErtsCodeInfo *ci, Binary *match_spec, int local);
-void erts_clear_call_trace_bif(ErtsCodeInfo *ci, int local);
+void erts_set_export_trace(ErtsCodeInfo *ci, Binary *match_spec, int local);
+void erts_clear_export_trace(ErtsCodeInfo *ci, int local);
void erts_set_mtrace_break(BpFunctions *f, Binary *match_spec,
ErtsTracer tracer);
void erts_clear_mtrace_break(BpFunctions *f);
-void erts_set_mtrace_bif(ErtsCodeInfo *ci, Binary *match_spec,
- ErtsTracer tracer);
-void erts_clear_mtrace_bif(ErtsCodeInfo *ci);
void erts_set_debug_break(BpFunctions *f);
void erts_clear_debug_break(BpFunctions *f);
@@ -142,7 +138,7 @@ void erts_clear_count_break(BpFunctions *f);
void erts_clear_all_breaks(BpFunctions* f);
int erts_clear_module_break(Module *modp);
-void erts_clear_export_break(Module *modp, ErtsCodeInfo* ci);
+void erts_clear_export_break(Module *modp, Export *ep);
BeamInstr erts_generic_breakpoint(Process* c_p, ErtsCodeInfo *ci, Eterm* reg);
BeamInstr erts_trace_break(Process *p, ErtsCodeInfo *ci, Eterm *args,
@@ -151,8 +147,6 @@ BeamInstr erts_trace_break(Process *p, ErtsCodeInfo *ci, Eterm *args,
int erts_is_trace_break(ErtsCodeInfo *ci, Binary **match_spec_ret, int local);
int erts_is_mtrace_break(ErtsCodeInfo *ci, Binary **match_spec_ret,
ErtsTracer *tracer_ret);
-int erts_is_mtrace_bif(ErtsCodeInfo *ci, Binary **match_spec_ret,
- ErtsTracer *tracer_ret);
int erts_is_native_break(ErtsCodeInfo *ci);
int erts_is_count_break(ErtsCodeInfo *ci, Uint *count_ret);
int erts_is_time_break(Process *p, ErtsCodeInfo *ci, Eterm *call_time);
@@ -163,10 +157,6 @@ void erts_schedule_time_break(Process *p, Uint out);
void erts_set_time_break(BpFunctions *f, enum erts_break_op);
void erts_clear_time_break(BpFunctions *f);
-int erts_is_time_trace_bif(Process *p, ErtsCodeInfo *ci, Eterm *call_time);
-void erts_set_time_trace_bif(ErtsCodeInfo *ci, enum erts_break_op);
-void erts_clear_time_trace_bif(ErtsCodeInfo *ci);
-
ErtsCodeInfo *erts_find_local_func(ErtsCodeMFA *mfa);
#if ERTS_GLB_INLINE_INCL_FUNC_DEF
diff --git a/erts/emulator/beam/beam_debug.c b/erts/emulator/beam/beam_debug.c
index 4d52435139..eeb3a465d0 100644
--- a/erts/emulator/beam/beam_debug.c
+++ b/erts/emulator/beam/beam_debug.c
@@ -47,7 +47,6 @@
#else
# define HEXF "%08bpX"
#endif
-#define TermWords(t) (((t) / (sizeof(BeamInstr)/sizeof(Eterm))) + !!((t) % (sizeof(BeamInstr)/sizeof(Eterm))))
void dbg_bt(Process* p, Eterm* sp);
void dbg_where(BeamInstr* addr, Eterm x0, Eterm* reg);
@@ -158,7 +157,7 @@ erts_debug_breakpoint_2(BIF_ALIST_2)
}
if (!erts_try_seize_code_write_permission(BIF_P)) {
- ERTS_BIF_YIELD2(bif_export[BIF_erts_debug_breakpoint_2],
+ ERTS_BIF_YIELD2(&bif_trap_export[BIF_erts_debug_breakpoint_2],
BIF_P, BIF_ARG_1, BIF_ARG_2);
}
erts_proc_unlock(p, ERTS_PROC_LOCK_MAIN);
@@ -332,7 +331,7 @@ erts_debug_disassemble_1(BIF_ALIST_1)
"unknown " HEXF "\n", instr);
code_ptr++;
}
- if (i == op_call_nif) {
+ if (i == op_call_nif_WWW) {
/*
* The rest of the code will not be executed. Don't disassemble any
* more code in this function.
diff --git a/erts/emulator/beam/beam_emu.c b/erts/emulator/beam/beam_emu.c
index 07c16e3415..1a250470cb 100644
--- a/erts/emulator/beam/beam_emu.c
+++ b/erts/emulator/beam/beam_emu.c
@@ -109,14 +109,9 @@ do { \
# define CHECK_ARGS(T)
#endif
-#define CHECK_ALIGNED(Dst) ASSERT((((Uint)&Dst) & (sizeof(Uint)-1)) == 0)
-
-#define GET_BIF_MODULE(p) (p->info.mfa.module)
-#define GET_BIF_FUNCTION(p) (p->info.mfa.function)
-#define GET_BIF_ARITY(p) (p->info.mfa.arity)
-#define GET_BIF_ADDRESS(p) ((BifFunction) (p->beam[1]))
-#define TermWords(t) (((t) / (sizeof(BeamInstr)/sizeof(Eterm))) + !!((t) % (sizeof(BeamInstr)/sizeof(Eterm))))
-
+#define GET_EXPORT_MODULE(p) ((p)->info.mfa.module)
+#define GET_EXPORT_FUNCTION(p) ((p)->info.mfa.function)
+#define GET_EXPORT_ARITY(p) ((p)->info.mfa.arity)
/*
* We reuse some of fields in the save area in the process structure.
@@ -141,10 +136,6 @@ do { \
BeamCodeAddr(IP) < (BeamInstr)LabelAddr(end_emulator_loop))
#endif /* NO_JUMP_TABLE */
-#define SET_CP(p, ip) \
- ASSERT(VALID_INSTR(*(ip))); \
- (p)->cp = (ip)
-
#define SET_I(ip) \
ASSERT(VALID_INSTR(* (Eterm *)(ip))); \
I = (ip)
@@ -165,7 +156,7 @@ BeamInstr beam_continue_exit[1];
/* NOTE These should be the only variables containing trace instructions.
-** Sometimes tests are form the instruction value, and sometimes
+** Sometimes tests are for the instruction value, and sometimes
** for the referring variable (one of these), and rouge references
** will most likely cause chaos.
*/
@@ -254,72 +245,6 @@ void** beam_ops;
#define Q(N) (N*sizeof(Eterm *))
#define l(N) (freg[N].fd)
-/*
- * Check that we haven't used the reductions and jump to function pointed to by
- * the I register. If we are out of reductions, do a context switch.
- */
-
-#define DispatchMacro() \
- do { \
- BeamInstr dis_next; \
- dis_next = *I; \
- CHECK_ARGS(I); \
- if (FCALLS > 0 || FCALLS > neg_o_reds) { \
- FCALLS--; \
- Goto(dis_next); \
- } else { \
- goto context_switch; \
- } \
- } while (0) \
-
-#define DispatchMacroFun() \
- do { \
- BeamInstr dis_next; \
- dis_next = *I; \
- CHECK_ARGS(I); \
- if (FCALLS > 0 || FCALLS > neg_o_reds) { \
- FCALLS--; \
- Goto(dis_next); \
- } else { \
- goto context_switch_fun; \
- } \
- } while (0)
-
-#define DispatchMacrox() \
- do { \
- if (FCALLS > 0) { \
- BeamInstr dis_next; \
- SET_I(((Export *) Arg(0))->addressv[erts_active_code_ix()]); \
- dis_next = *I; \
- FCALLS--; \
- CHECK_ARGS(I); \
- Goto(dis_next); \
- } else if (ERTS_PROC_GET_SAVED_CALLS_BUF(c_p) \
- && FCALLS > neg_o_reds) { \
- goto save_calls1; \
- } else { \
- SET_I(((Export *) Arg(0))->addressv[erts_active_code_ix()]); \
- CHECK_ARGS(I); \
- goto context_switch; \
- } \
- } while (0)
-
-#ifdef DEBUG
-/*
- * To simplify breakpoint setting, put the code in one place only and jump to it.
- */
-# define Dispatch() goto do_dispatch
-# define Dispatchx() goto do_dispatchx
-# define Dispatchfun() goto do_dispatchfun
-#else
-/*
- * Inline for speed.
- */
-# define Dispatch() DispatchMacro()
-# define Dispatchx() DispatchMacrox()
-# define Dispatchfun() DispatchMacroFun()
-#endif
-
#define Arg(N) I[(N)+1]
#define GetSource(raw, dst) \
@@ -352,19 +277,6 @@ do { \
} \
} while(0)
-#define DispatchReturn \
-do { \
- if (FCALLS > 0 || FCALLS > neg_o_reds) { \
- FCALLS--; \
- Goto(*I); \
- } \
- else { \
- c_p->current = NULL; \
- c_p->arity = 1; \
- goto context_switch3; \
- } \
-} while (0)
-
#ifdef DEBUG
/* Better static type testing by the C compiler */
# define BEAM_IS_TUPLE(Src) is_tuple(Src)
@@ -521,10 +433,10 @@ init_emulator(void)
} \
} while(0)
-#define DTRACE_RETURN_FROM_PC(p) \
+#define DTRACE_RETURN_FROM_PC(p, i) \
do { \
ErtsCodeMFA* cmfa; \
- if (DTRACE_ENABLED(function_return) && (cmfa = find_function_from_pc((p)->cp))) { \
+ if (DTRACE_ENABLED(function_return) && (cmfa = find_function_from_pc(i))) { \
DTRACE_RETURN((p), cmfa); \
} \
} while(0)
@@ -534,7 +446,7 @@ init_emulator(void)
#define DTRACE_GLOBAL_CALL(p, mfa) do {} while (0)
#define DTRACE_GLOBAL_CALL_FROM_EXPORT(p, e) do {} while (0)
#define DTRACE_RETURN(p, mfa) do {} while (0)
-#define DTRACE_RETURN_FROM_PC(p) do {} while (0)
+#define DTRACE_RETURN_FROM_PC(p, i) do {} while (0)
#define DTRACE_BIF_ENTRY(p, mfa) do {} while (0)
#define DTRACE_BIF_RETURN(p, mfa) do {} while (0)
#define DTRACE_NIF_ENTRY(p, mfa) do {} while (0)
@@ -772,27 +684,9 @@ void process_main(Eterm * x_reg_array, FloatDef* f_reg_array)
#endif
#include "beam_hot.h"
-
-#ifdef DEBUG
- /*
- * Set a breakpoint here to get control just after a call instruction.
- * I points to the first instruction in the called function.
- *
- * In gdb, use 'call dis(I-5, 1)' to show the name of the function.
- */
- do_dispatch:
- DispatchMacro();
-
- do_dispatchx:
- DispatchMacrox();
-
- do_dispatchfun:
- DispatchMacroFun();
-
-#endif
-
/*
- * Jumped to from the Dispatch() macro when the reductions are used up.
+ * The labels are jumped to from the $DISPATCH() macros when the reductions
+ * are used up.
*
* Since the I register points just beyond the FuncBegin instruction, we
* can get the module, function, and arity for the function being
@@ -986,18 +880,46 @@ void process_main(Eterm * x_reg_array, FloatDef* f_reg_array)
}
#endif
return; /* Never executed */
+}
- save_calls1:
- {
- BeamInstr dis_next;
+/*
+ * Enter all BIFs into the export table.
+ *
+ * Note that they will all call the error_handler until their modules have been
+ * loaded, which may prevent the system from booting if BIFs from non-preloaded
+ * modules are apply/3'd while loading code. Ordinary BIF calls will work fine
+ * however since they won't go through export entries.
+ */
+static void install_bifs(void) {
+ int i;
- save_calls(c_p, (Export *) Arg(0));
+ for (i = 0; i < BIF_SIZE; i++) {
+ BifEntry *entry;
+ Export *ep;
+ int j;
- SET_I(((Export *) Arg(0))->addressv[erts_active_code_ix()]);
+ entry = &bif_table[i];
- dis_next = *I;
- FCALLS--;
- Goto(dis_next);
+ ep = erts_export_put(entry->module, entry->name, entry->arity);
+
+ ep->info.op = BeamOpCodeAddr(op_i_func_info_IaaI);
+ ep->info.mfa.module = entry->module;
+ ep->info.mfa.function = entry->name;
+ ep->info.mfa.arity = entry->arity;
+ ep->bif_number = i;
+
+ memset(&ep->trampoline, 0, sizeof(ep->trampoline));
+ ep->trampoline.op = BeamOpCodeAddr(op_call_error_handler);
+
+ for (j = 0; j < ERTS_NUM_CODE_IX; j++) {
+ ep->addressv[j] = ep->trampoline.raw;
+ }
+
+ /* Set up a hidden export entry so we can trap to this BIF without
+ * it being seen when tracing. */
+ erts_init_trap_export(&bif_trap_export[i],
+ entry->module, entry->name, entry->arity,
+ entry->f);
}
}
@@ -1008,43 +930,30 @@ void process_main(Eterm * x_reg_array, FloatDef* f_reg_array)
static void
init_emulator_finish(void)
{
- int i;
- Export* ep;
-
#if defined(ARCH_64) && defined(CODE_MODEL_SMALL)
- for (i = 0; i < NUMBER_OF_OPCODES; i++) {
- BeamInstr instr = BeamOpCodeAddr(i);
- if (instr >= (1ull << 32)) {
- erts_exit(ERTS_ERROR_EXIT,
- "This run-time was supposed be compiled with all code below 2Gb,\n"
- "but the instruction '%s' is located at %016lx.\n",
- opc[i].name, instr);
- }
- }
+ int i;
+
+ for (i = 0; i < NUMBER_OF_OPCODES; i++) {
+ BeamInstr instr = BeamOpCodeAddr(i);
+ if (instr >= (1ull << 32)) {
+ erts_exit(ERTS_ERROR_EXIT,
+ "This run-time was supposed be compiled with all code below 2Gb,\n"
+ "but the instruction '%s' is located at %016lx.\n",
+ opc[i].name, instr);
+ }
+ }
#endif
- beam_apply[0] = BeamOpCodeAddr(op_i_apply);
- beam_apply[1] = BeamOpCodeAddr(op_normal_exit);
- beam_exit[0] = BeamOpCodeAddr(op_error_action_code);
- beam_continue_exit[0] = BeamOpCodeAddr(op_continue_exit);
- beam_return_to_trace[0] = BeamOpCodeAddr(op_i_return_to_trace);
- beam_return_trace[0] = BeamOpCodeAddr(op_return_trace);
- beam_exception_trace[0] = BeamOpCodeAddr(op_return_trace); /* UGLY */
- beam_return_time_trace[0] = BeamOpCodeAddr(op_i_return_time_trace);
+ beam_apply[0] = BeamOpCodeAddr(op_i_apply);
+ beam_apply[1] = BeamOpCodeAddr(op_normal_exit);
+ beam_exit[0] = BeamOpCodeAddr(op_error_action_code);
+ beam_continue_exit[0] = BeamOpCodeAddr(op_continue_exit);
+ beam_return_to_trace[0] = BeamOpCodeAddr(op_i_return_to_trace);
+ beam_return_trace[0] = BeamOpCodeAddr(op_return_trace);
+ beam_exception_trace[0] = BeamOpCodeAddr(op_return_trace); /* UGLY */
+ beam_return_time_trace[0] = BeamOpCodeAddr(op_i_return_time_trace);
- /*
- * Enter all BIFs into the export table.
- */
- for (i = 0; i < BIF_SIZE; i++) {
- ep = erts_export_put(bif_table[i].module,
- bif_table[i].name,
- bif_table[i].arity);
- bif_export[i] = ep;
- ep->beam[0] = BeamOpCodeAddr(op_apply_bif);
- ep->beam[1] = (BeamInstr) bif_table[i].f;
- /* XXX: set func info for bifs */
- ep->info.op = BeamOpCodeAddr(op_i_func_info_IaaI);
- }
+ install_bifs();
}
/*
@@ -1257,7 +1166,7 @@ void erts_dirty_process_main(ErtsSchedulerData *esdp)
* I[2]: Pointer to erl_module_nif
* I[3]: Function pointer to dirty NIF
*
- * This layout is determined by the NifExport struct
+ * This layout is determined by the ErtsNativeFunc struct
*/
ERTS_MSACC_SET_STATE_CACHED_M_X(ERTS_MSACC_STATE_NIF);
@@ -1271,11 +1180,11 @@ void erts_dirty_process_main(ErtsSchedulerData *esdp)
ERTS_UNREQ_PROC_MAIN_LOCK(c_p);
ASSERT(!ERTS_PROC_IS_EXITING(c_p));
- if (BeamIsOpCode(*I, op_apply_bif)) {
+ if (BeamIsOpCode(*I, op_call_bif_W)) {
exiting = erts_call_dirty_bif(esdp, c_p, I, reg);
}
else {
- ASSERT(BeamIsOpCode(*I, op_call_nif));
+ ASSERT(BeamIsOpCode(*I, op_call_nif_WWW));
exiting = erts_call_dirty_nif(esdp, c_p, I, reg);
}
@@ -1303,7 +1212,7 @@ ubif2mfa(void* uf)
int i;
for (i = 0; erts_u_bifs[i].bif; i++) {
if (erts_u_bifs[i].bif == uf)
- return &bif_export[erts_u_bifs[i].exp_ix]->info.mfa;
+ return &bif_trap_export[erts_u_bifs[i].exp_ix].info.mfa;
}
erts_exit(ERTS_ERROR_EXIT, "bad u bif: %p\n", uf);
return NULL;
@@ -1344,6 +1253,33 @@ Eterm error_atom[NUMBER_EXIT_CODES] = {
am_badkey, /* 19 */
};
+/* Returns the return address at E[0] in printable form, skipping tracing in
+ * the same manner as gather_stacktrace.
+ *
+ * This is needed to generate correct stacktraces when throwing errors from
+ * instructions that return like an ordinary function, such as call_nif. */
+BeamInstr *erts_printable_return_address(Process* p, Eterm *E) {
+ Eterm *ptr = E;
+
+ ASSERT(is_CP(*ptr));
+
+ while (ptr < STACK_START(p)) {
+ BeamInstr *cp = cp_val(*ptr);
+
+ if (cp == beam_exception_trace || cp == beam_return_trace) {
+ ptr += 3;
+ } else if (cp == beam_return_time_trace) {
+ ptr += 2;
+ } else if (cp == beam_return_to_trace) {
+ ptr += 1;
+ } else {
+ return cp;
+ }
+ }
+
+ ERTS_ASSERT(!"No continuation pointer on stack");
+}
+
/*
* To fully understand the error handling, one must keep in mind that
* when an exception is thrown, the search for a handler can jump back
@@ -1373,14 +1309,14 @@ handle_error(Process* c_p, BeamInstr* pc, Eterm* reg, ErtsCodeMFA *bif_mfa)
ASSERT(c_p->freason != TRAP); /* Should have been handled earlier. */
- if (c_p->freason & EXF_RESTORE_NIF)
- erts_nif_export_restore_error(c_p, &pc, reg, &bif_mfa);
+ if (c_p->freason & EXF_RESTORE_NFUNC)
+ erts_nfunc_restore_error(c_p, &pc, reg, &bif_mfa);
#ifdef DEBUG
if (bif_mfa) {
- /* Verify that bif_mfa does not point into our nif export */
- NifExport *nep = ERTS_PROC_GET_NIF_TRAP_EXPORT(c_p);
- ASSERT(!nep || !ErtsInArea(bif_mfa, (char *)nep, sizeof(NifExport)));
+ /* Verify that bif_mfa does not point into our native function wrapper */
+ ErtsNativeFunc *nep = ERTS_PROC_GET_NFUNC_TRAP_WRAPPER(c_p);
+ ASSERT(!nep || !ErtsInArea(bif_mfa, (char *)nep, sizeof(ErtsNativeFunc)));
}
#endif
@@ -1443,7 +1379,7 @@ handle_error(Process* c_p, BeamInstr* pc, Eterm* reg, ErtsCodeMFA *bif_mfa)
reg[2] = Value;
reg[3] = c_p->ftrace;
if ((new_pc = next_catch(c_p, reg))) {
- c_p->cp = 0; /* To avoid keeping stale references. */
+ c_p->stop[0] = NIL; /* To avoid keeping stale references. */
ERTS_RECV_MARK_CLEAR(c_p); /* No longer safe to use this position */
return new_pc;
}
@@ -1481,35 +1417,6 @@ next_catch(Process* c_p, Eterm *reg) {
return NULL;
}
- /*
- * Better safe than sorry here. In debug builds, produce a core
- * dump if the top of the stack doesn't point to a continuation
- * pointer. In other builds, ignore a non-CP at the top of stack.
- */
- ASSERT(is_CP(*ptr));
- if ((is_not_CP(*ptr) || (*cp_val(*ptr) != i_return_trace &&
- *cp_val(*ptr) != i_return_to_trace &&
- *cp_val(*ptr) != i_return_time_trace ))
- && c_p->cp) {
- /* Can not follow cp here - code may be unloaded */
- BeamInstr *cpp = c_p->cp;
- if (cpp == beam_exception_trace) {
- ErtsCodeMFA *mfa = (ErtsCodeMFA*)cp_val(ptr[0]);
- erts_trace_exception(c_p, mfa,
- reg[1], reg[2],
- ERTS_TRACER_FROM_ETERM(ptr+1));
- /* Skip return_trace parameters */
- ptr += 2;
- } else if (cpp == beam_return_trace) {
- /* Skip return_trace parameters */
- ptr += 2;
- } else if (cpp == beam_return_time_trace) {
- /* Skip return_trace parameters */
- ptr += 1;
- } else if (cpp == beam_return_to_trace) {
- have_return_to_trace = !0; /* Record next cp */
- }
- }
while (ptr < STACK_START(c_p)) {
if (is_catch(*ptr)) {
if (active_catches) goto found_catch;
@@ -1583,6 +1490,8 @@ terminate_proc(Process* c_p, Eterm Value)
if (GET_EXC_CLASS(c_p->freason) == EXTAG_ERROR) {
Value = add_stacktrace(c_p, Value, c_p->ftrace);
}
+ c_p->ftrace = NIL;
+
/* EXF_LOG is a primary exception flag */
if (c_p->freason & EXF_LOG) {
int alive = erts_is_alive;
@@ -1664,6 +1573,54 @@ expand_error_value(Process* c_p, Uint freason, Eterm Value) {
return Value;
}
+
+static void
+gather_stacktrace(Process* p, struct StackTrace* s, int depth)
+{
+ BeamInstr *prev;
+ Eterm *ptr;
+
+ if (depth == 0) {
+ return;
+ }
+
+ prev = s->depth ? s->trace[s->depth - 1] : s->pc;
+ ptr = p->stop;
+
+ /*
+ * Traverse the stack backwards and add all unique continuation
+ * pointers to the buffer, up to the maximum stack trace size.
+ *
+ * Skip trace stack frames.
+ */
+
+ ASSERT(ptr >= STACK_TOP(p) && ptr <= STACK_START(p));
+
+ while (ptr < STACK_START(p) && depth > 0) {
+ if (is_CP(*ptr)) {
+ BeamInstr *cp = cp_val(*ptr);
+
+ if (cp == beam_exception_trace || cp == beam_return_trace) {
+ ptr += 3;
+ } else if (cp == beam_return_time_trace) {
+ ptr += 2;
+ } else if (cp == beam_return_to_trace) {
+ ptr += 1;
+ } else {
+ if (cp != prev) {
+ /* Record non-duplicates only */
+ prev = cp;
+ s->trace[s->depth++] = cp - 1;
+ depth--;
+ }
+ ptr++;
+ }
+ } else {
+ ptr++;
+ }
+ }
+}
+
/*
* Quick-saving the stack trace in an internal form on the heap. Note
* that c_p->ftrace will point to a cons cell which holds the given args
@@ -1750,11 +1707,6 @@ save_stacktrace(Process* c_p, BeamInstr* pc, Eterm* reg,
s->trace[s->depth++] = pc;
depth--;
}
- /* Save second stack entry if CP is valid and different from pc */
- if (depth > 0 && c_p->cp != 0 && c_p->cp != pc) {
- s->trace[s->depth++] = c_p->cp - 1;
- depth--;
- }
s->pc = NULL;
args = make_arglist(c_p, reg, bif_mfa->arity); /* Overwrite CAR(c_p->ftrace) */
} else {
@@ -1762,9 +1714,9 @@ save_stacktrace(Process* c_p, BeamInstr* pc, Eterm* reg,
non_bif_stacktrace:
s->current = c_p->current;
- /*
+ /*
* For a function_clause error, the arguments are in the beam
- * registers, c_p->cp is valid, and c_p->current is set.
+ * registers and c_p->current is set.
*/
if ( (GET_EXC_INDEX(s->freason)) ==
(GET_EXC_INDEX(EXC_FUNCTION_CLAUSE)) ) {
@@ -1772,18 +1724,8 @@ save_stacktrace(Process* c_p, BeamInstr* pc, Eterm* reg,
ASSERT(s->current);
a = s->current->arity;
args = make_arglist(c_p, reg, a); /* Overwrite CAR(c_p->ftrace) */
- /* Save first stack entry */
- ASSERT(c_p->cp);
- if (depth > 0) {
- s->trace[s->depth++] = c_p->cp - 1;
- depth--;
- }
s->pc = NULL; /* Ignore pc */
} else {
- if (depth > 0 && c_p->cp != 0 && c_p->cp != pc) {
- s->trace[s->depth++] = c_p->cp - 1;
- depth--;
- }
s->pc = pc;
}
}
@@ -1796,80 +1738,13 @@ save_stacktrace(Process* c_p, BeamInstr* pc, Eterm* reg,
}
/* Save the actual stack trace */
- erts_save_stacktrace(c_p, s, depth);
+ gather_stacktrace(c_p, s, depth);
}
void
erts_save_stacktrace(Process* p, struct StackTrace* s, int depth)
{
- if (depth > 0) {
- Eterm *ptr;
- BeamInstr *prev = s->depth ? s->trace[s->depth-1] : NULL;
- BeamInstr i_return_trace = beam_return_trace[0];
- BeamInstr i_return_to_trace = beam_return_to_trace[0];
-
- /*
- * Traverse the stack backwards and add all unique continuation
- * pointers to the buffer, up to the maximum stack trace size.
- *
- * Skip trace stack frames.
- */
- ptr = p->stop;
- if (ptr < STACK_START(p) &&
- (is_not_CP(*ptr)|| (*cp_val(*ptr) != i_return_trace &&
- *cp_val(*ptr) != i_return_to_trace)) &&
- p->cp) {
- /* Cannot follow cp here - code may be unloaded */
- BeamInstr *cpp = p->cp;
- int trace_cp;
- if (cpp == beam_exception_trace || cpp == beam_return_trace) {
- /* Skip return_trace parameters */
- ptr += 2;
- trace_cp = 1;
- } else if (cpp == beam_return_to_trace) {
- /* Skip return_to_trace parameters */
- ptr += 1;
- trace_cp = 1;
- }
- else {
- trace_cp = 0;
- }
- if (trace_cp && s->pc == cpp) {
- /*
- * If process 'cp' points to a return/exception trace
- * instruction and 'cp' has been saved as 'pc' in
- * stacktrace, we need to update 'pc' in stacktrace
- * with the actual 'cp' located on the top of the
- * stack; otherwise, we will lose the top stackframe
- * when building the stack trace.
- */
- ASSERT(is_CP(p->stop[0]));
- s->pc = cp_val(p->stop[0]);
- }
- }
- while (ptr < STACK_START(p) && depth > 0) {
- if (is_CP(*ptr)) {
- if (*cp_val(*ptr) == i_return_trace) {
- /* Skip stack frame variables */
- do ++ptr; while (is_not_CP(*ptr));
- /* Skip return_trace parameters */
- ptr += 2;
- } else if (*cp_val(*ptr) == i_return_to_trace) {
- /* Skip stack frame variables */
- do ++ptr; while (is_not_CP(*ptr));
- } else {
- BeamInstr *cp = cp_val(*ptr);
- if (cp != prev) {
- /* Record non-duplicates only */
- prev = cp;
- s->trace[s->depth++] = cp - 1;
- depth--;
- }
- ptr++;
- }
- } else ptr++;
- }
- }
+ gather_stacktrace(p, s, depth);
}
/*
@@ -2128,95 +2003,66 @@ apply_bif_error_adjustment(Process *p, Export *ep,
Eterm *reg, Uint arity,
BeamInstr *I, Uint stack_offset)
{
+ int apply_only;
+ Uint need;
+
+ need = stack_offset /* bytes */ / sizeof(Eterm);
+ apply_only = stack_offset == 0;
+
/*
* I is only set when the apply is a tail call, i.e.,
* from the instructions i_apply_only, i_apply_last_P,
* and apply_last_IP.
*/
- if (I
- && BeamIsOpCode(ep->beam[0], op_apply_bif)
- && (ep == bif_export[BIF_error_1]
- || ep == bif_export[BIF_error_2]
- || ep == bif_export[BIF_exit_1]
- || ep == bif_export[BIF_throw_1])) {
- /*
- * We are about to tail apply one of the BIFs
- * erlang:error/1, erlang:error/2, erlang:exit/1,
- * or erlang:throw/1. Error handling of these BIFs is
- * special!
- *
- * We need 'p->cp' to point into the calling
- * function when handling the error after the BIF has
- * been applied. This in order to get the topmost
- * stackframe correct. Without the following adjustment,
- * 'p->cp' will point into the function that called
- * current function when handling the error. We add a
- * dummy stackframe in order to achieve this.
- *
- * Note that these BIFs unconditionally will cause
- * an exception to be raised. That is, our modifications
- * of 'p->cp' as well as the stack will be corrected by
- * the error handling code.
- *
- * If we find an exception/return-to trace continuation
- * pointer as the topmost continuation pointer, we do not
- * need to do anything since the information already will
- * be available for generation of the stacktrace.
- */
- int apply_only = stack_offset == 0;
- BeamInstr *cpp;
+ if (!(I && (ep->bif_number == BIF_error_1 ||
+ ep->bif_number == BIF_error_2 ||
+ ep->bif_number == BIF_exit_1 ||
+ ep->bif_number == BIF_throw_1))) {
+ return;
+ }
- if (apply_only) {
- ASSERT(p->cp != NULL);
- cpp = p->cp;
- }
- else {
- ASSERT(is_CP(p->stop[0]));
- cpp = cp_val(p->stop[0]);
- }
+ /*
+ * We are about to tail apply one of the BIFs erlang:error/1,
+ * erlang:error/2, erlang:exit/1, or erlang:throw/1. Error handling of
+ * these BIFs is special!
+ *
+ * We need the topmost continuation pointer to point into the calling
+ * function when handling the error after the BIF has been applied. This in
+ * order to get the topmost stackframe correct.
+ *
+ * Note that these BIFs will unconditionally cause an exception to be
+ * raised. That is, our modifications of the stack will be corrected by the
+ * error handling code.
+ */
+ if (need == 0) {
+ need = 1; /* i_apply_only */
+ }
- if (cpp != beam_exception_trace
- && cpp != beam_return_trace
- && cpp != beam_return_to_trace) {
- Uint need = stack_offset /* bytes */ / sizeof(Eterm);
- if (need == 0)
- need = 1; /* i_apply_only */
- if (p->stop - p->htop < need)
- erts_garbage_collect(p, (int) need, reg, arity+1);
- p->stop -= need;
-
- if (apply_only) {
- /*
- * Called from the i_apply_only instruction.
- *
- * 'p->cp' contains continuation pointer pointing
- * into the function that called current function.
- * We push that continuation pointer onto the stack,
- * and set 'p->cp' to point into current function.
- */
+ if (p->stop - p->htop < need) {
+ erts_garbage_collect(p, (int) need, reg, arity+1);
+ }
- p->stop[0] = make_cp(p->cp);
- p->cp = I;
- }
- else {
- /*
- * Called from an i_apply_last_p, or apply_last_IP,
- * instruction.
- *
- * Calling instruction will after we return read
- * a continuation pointer from the stack and write
- * it to 'p->cp', and then remove the topmost
- * stackframe of size 'stack_offset'.
- *
- * We have sized the dummy-stackframe so that it
- * will be removed by the instruction we currently
- * are executing, and leave the stackframe that
- * normally would have been removed intact.
- *
- */
- p->stop[0] = make_cp(I);
- }
- }
+ if (apply_only) {
+ /*
+ * Called from the i_apply_only instruction.
+ *
+ * Push the continuation pointer for the current function to the stack.
+ */
+ p->stop -= need;
+ p->stop[0] = make_cp(I);
+ } else {
+ /*
+ * Called from an i_apply_last_* instruction.
+ *
+ * The calling instruction will deallocate a stack frame of size
+ * 'stack_offset'.
+ *
+ * Push the continuation pointer for the current function to the stack,
+ * and then add a dummy stackframe for the i_apply_last* instruction
+ * to discard.
+ */
+ p->stop[0] = make_cp(I);
+ p->stop -= need;
}
}
@@ -2437,10 +2283,10 @@ erts_hibernate(Process* c_p, Eterm* reg)
c_p->arg_reg[0] = module;
c_p->arg_reg[1] = function;
c_p->arg_reg[2] = args;
- c_p->stop = STACK_START(c_p);
+ c_p->stop = c_p->hend - 1; /* Keep first continuation pointer */
+ ASSERT(c_p->stop[0] == make_cp(beam_apply+1));
c_p->catches = 0;
c_p->i = beam_apply;
- c_p->cp = (BeamInstr *) beam_apply+1;
/*
* If there are no waiting messages, garbage collect and
@@ -2460,7 +2306,7 @@ erts_hibernate(Process* c_p, Eterm* reg)
ASSERT(!ERTS_PROC_IS_EXITING(c_p));
}
erts_proc_unlock(c_p, ERTS_PROC_LOCK_MSGQ|ERTS_PROC_LOCK_STATUS);
- c_p->current = &bif_export[BIF_hibernate_3]->info.mfa;
+ c_p->current = &bif_trap_export[BIF_hibernate_3].info.mfa;
c_p->flags |= F_HIBERNATE_SCHED; /* Needed also when woken! */
return 1;
}
@@ -3268,10 +3114,10 @@ erts_is_builtin(Eterm Mod, Eterm Name, int arity)
e.info.mfa.arity = arity;
if ((ep = export_get(&e)) == NULL) {
- return 0;
+ return 0;
}
- return ep->addressv[erts_active_code_ix()] == ep->beam &&
- BeamIsOpCode(ep->beam[0], op_apply_bif);
+
+ return ep->bif_number != -1;
}
diff --git a/erts/emulator/beam/beam_load.c b/erts/emulator/beam/beam_load.c
index 35f2ea6688..3fc3b8168e 100644
--- a/erts/emulator/beam/beam_load.c
+++ b/erts/emulator/beam/beam_load.c
@@ -141,7 +141,7 @@ typedef struct {
* eventually patch with a pointer into
* the export entry.
*/
- BifFunction bf; /* Pointer to BIF function if BIF;
+ Export *bif; /* Pointer to export entry if BIF;
* NULL otherwise.
*/
} ImportEntry;
@@ -315,6 +315,7 @@ typedef struct LoaderState {
* (or 0 if there is no on_load function)
*/
int otp_20_or_higher; /* Compiled with OTP 20 or higher */
+ unsigned max_opcode; /* Highest opcode used in module */
/*
* Atom table.
@@ -844,17 +845,23 @@ erts_finish_loading(Binary* magic, Process* c_p,
if (ep == NULL || ep->info.mfa.module != module) {
continue;
}
- if (ep->addressv[code_ix] == ep->beam) {
- if (BeamIsOpCode(ep->beam[0], op_apply_bif)) {
- continue;
- } else if (BeamIsOpCode(ep->beam[0], op_i_generic_breakpoint)) {
+
+ DBG_CHECK_EXPORT(ep, code_ix);
+
+ if (ep->addressv[code_ix] == ep->trampoline.raw) {
+ if (BeamIsOpCode(ep->trampoline.op, op_i_generic_breakpoint)) {
ERTS_LC_ASSERT(erts_thr_progress_is_blocking());
ASSERT(mod_tab_p->curr.num_traced_exports > 0);
- erts_clear_export_break(mod_tab_p, &ep->info);
- ep->addressv[code_ix] = (BeamInstr *) ep->beam[1];
- ep->beam[1] = 0;
+
+ erts_clear_export_break(mod_tab_p, ep);
+
+ ep->addressv[code_ix] =
+ (BeamInstr*)ep->trampoline.breakpoint.address;
+ ep->trampoline.breakpoint.address = 0;
+
+ ASSERT(ep->addressv[code_ix] != ep->trampoline.raw);
}
- ASSERT(ep->beam[1] == 0);
+ ASSERT(ep->trampoline.breakpoint.address == 0);
}
}
ASSERT(mod_tab_p->curr.num_breakpoints == 0);
@@ -1470,15 +1477,14 @@ load_import_table(LoaderState* stp)
}
stp->import[i].arity = arity;
stp->import[i].patches = 0;
- stp->import[i].bf = NULL;
+ stp->import[i].bif = NULL;
/*
- * If the export entry refers to a BIF, get the pointer to
- * the BIF function.
+ * If the export entry refers to a BIF, save a pointer to the BIF entry.
*/
if ((e = erts_active_export_entry(mod, func, arity)) != NULL) {
- if (BeamIsOpCode(e->beam[0], op_apply_bif)) {
- stp->import[i].bf = (BifFunction) e->beam[1];
+ if (e->bif_number != -1) {
+ stp->import[i].bif = e;
if (func == am_load_nif && mod == am_erlang && arity == 2) {
stp->may_load_nif = 1;
}
@@ -1529,33 +1535,6 @@ read_export_table(LoaderState* stp)
LoadError2(stp, "export table entry %u: label %u not resolved", i, n);
}
stp->export[i].address = address = stp->codev + value;
-
- /*
- * Find out if there is a BIF with the same name.
- */
-
- if (!is_bif(stp->module, func, arity)) {
- continue;
- }
-
- /*
- * This is a stub for a BIF.
- *
- * It should not be exported, and the information in its
- * func_info instruction should be invalidated so that it
- * can be filtered out by module_info(functions) and by
- * any other functions that walk through all local functions.
- */
-
- if (stp->labels[n].num_patches > 0) {
- LoadError3(stp, "there are local calls to the stub for "
- "the BIF %T:%T/%d",
- stp->module, func, arity);
- }
- stp->export[i].address = NULL;
- address[-1] = 0;
- address[-2] = NIL;
- address[-3] = NIL;
}
return 1;
@@ -1563,31 +1542,33 @@ read_export_table(LoaderState* stp)
return 0;
}
-
static int
is_bif(Eterm mod, Eterm func, unsigned arity)
{
- Export* e = erts_active_export_entry(mod, func, arity);
- if (e == NULL) {
- return 0;
- }
- if (! BeamIsOpCode(e->beam[0], op_apply_bif)) {
- return 0;
- }
- if (mod == am_erlang && func == am_apply && arity == 3) {
- /*
- * erlang:apply/3 is a special case -- it is implemented
- * as an instruction and it is OK to redefine it.
- */
- return 0;
+ Export *e = erts_active_export_entry(mod, func, arity);
+
+ if (e != NULL) {
+ return e->bif_number != -1;
}
- return 1;
+
+ return 0;
}
static int
read_lambda_table(LoaderState* stp)
{
unsigned int i;
+ unsigned int otp_22_or_lower;
+
+ /*
+ * Determine whether this module was compiled with OTP 22 or lower
+ * by looking at the max opcode number. The compiler in OTP 23 will
+ * always set the max opcode to the opcode for `swap` (whether
+ * actually used or not) so that a module compiled for OTP 23
+ * cannot be loaded in earlier versions.
+ */
+
+ otp_22_or_lower = stp->max_opcode < genop_swap_2;
GetInt(stp, 4, stp->num_lambdas);
if (stp->num_lambdas > stp->lambdas_allocated) {
@@ -1619,6 +1600,29 @@ read_lambda_table(LoaderState* stp)
GetInt(stp, 4, Index);
GetInt(stp, 4, stp->lambdas[i].num_free);
GetInt(stp, 4, OldUniq);
+
+ /*
+ * Fun entries are now keyed by the explicit ("new") index in
+ * the fun entry. That allows multiple make_fun2 instructions
+ * to share the same fun entry (when the `fun F/A` syntax is
+ * used). Before OTP 23, fun entries were keyed by the old
+ * index, which is the order of the entries in the fun
+ * chunk. Each make_fun2 needed to refer to its own fun entry.
+ *
+ * Modules compiled before OTP 23 can safely be loaded if the
+ * old index and the new index are equal. That is true for all
+ * modules compiled with OTP R15 and later.
+ */
+ if (otp_22_or_lower && i != Index) {
+ /*
+ * Compiled with a compiler before OTP R15B. The new indices
+ * are not reliable, so it is not safe to load this module.
+ */
+ LoadError2(stp, "please re-compile this module with an "
+ ERLANG_OTP_RELEASE " compiler "
+ "(old-style fun with indices: %d/%d)",
+ i, Index);
+ }
fe = erts_put_fun_entry2(stp->module, OldUniq, i, stp->mod_md5,
Index, arity-stp->lambdas[i].num_free);
stp->lambdas[i].fe = fe;
@@ -1839,7 +1843,6 @@ read_code_header(LoaderState* stp)
{
unsigned head_size;
unsigned version;
- unsigned opcode_max;
int i;
/*
@@ -1871,8 +1874,8 @@ read_code_header(LoaderState* stp)
/*
* Verify the number of the highest opcode used.
*/
- GetInt(stp, 4, opcode_max);
- if (opcode_max > MAX_GENERIC_OPCODE) {
+ GetInt(stp, 4, stp->max_opcode);
+ if (stp->max_opcode > MAX_GENERIC_OPCODE) {
LoadError2(stp,
"This BEAM file was compiled for a later version"
" of the run-time system than " ERLANG_OTP_RELEASE ".\n"
@@ -1880,7 +1883,7 @@ read_code_header(LoaderState* stp)
ERLANG_OTP_RELEASE " compiler.\n"
" (Use of opcode %d; this emulator supports "
"only up to %d.)",
- opcode_max, MAX_GENERIC_OPCODE);
+ stp->max_opcode, MAX_GENERIC_OPCODE);
}
GetInt(stp, 4, stp->num_labels);
@@ -1919,8 +1922,6 @@ read_code_header(LoaderState* stp)
code = stp->codev = (BeamInstr*) &stp->hdr->functions; \
} \
} while (0)
-
-#define TermWords(t) (((t) / (sizeof(BeamInstr)/sizeof(Eterm))) + !!((t) % (sizeof(BeamInstr)/sizeof(Eterm))))
static void init_label(Label* lp)
{
@@ -2500,10 +2501,14 @@ load_code(LoaderState* stp)
if (i >= stp->num_imports) {
LoadError1(stp, "invalid import table index %d", i);
}
- if (stp->import[i].bf == NULL) {
+ if (stp->import[i].bif == NULL) {
LoadError1(stp, "not a BIF: import table index %d", i);
}
- code[ci++] = (BeamInstr) stp->import[i].bf;
+ {
+ int bif_index = stp->import[i].bif->bif_number;
+ BifEntry *bif_entry = &bif_table[bif_index];
+ code[ci++] = (BeamInstr) bif_entry->f;
+ }
break;
case 'P': /* Byte offset into tuple or stack */
case 'Q': /* Like 'P', but packable */
@@ -2711,36 +2716,30 @@ load_code(LoaderState* stp)
num_trailing_f = 0;
}
#endif
+ CodeNeed(1);
switch (tmp_op->a[arg].type) {
case TAG_i:
- CodeNeed(1);
code[ci++] = make_small(tmp_op->a[arg].val);
break;
case TAG_u:
case TAG_a:
case TAG_v:
- CodeNeed(1);
code[ci++] = tmp_op->a[arg].val;
break;
case TAG_f:
- CodeNeed(1);
register_label_patch(stp, tmp_op->a[arg].val, ci, -last_instr_start);
ci++;
break;
case TAG_x:
- CodeNeed(1);
code[ci++] = make_loader_x_reg(tmp_op->a[arg].val);
break;
case TAG_y:
- CodeNeed(1);
code[ci++] = make_loader_y_reg(tmp_op->a[arg].val);
break;
case TAG_n:
- CodeNeed(1);
code[ci++] = NIL;
break;
case TAG_q:
- CodeNeed(1);
new_literal_patch(stp, ci);
code[ci++] = tmp_op->a[arg].val;
break;
@@ -2811,18 +2810,43 @@ load_code(LoaderState* stp)
switch (stp->specific_op) {
case op_i_func_info_IaaI:
{
+ int padding_required;
Sint offset;
+
if (function_number >= stp->num_functions) {
LoadError1(stp, "too many functions in module (header said %u)",
stp->num_functions);
}
- if (stp->may_load_nif) {
+ /* Native function calls may be larger than their stubs, so
+ * we'll need to make sure any potentially-native function stub
+ * is padded with enough room.
+ *
+ * Note that the padding is applied for the previous function,
+ * not the current one, so we check whether the old F/A is
+ * a BIF. */
+ padding_required = last_func_start && (stp->may_load_nif ||
+ is_bif(stp->module, stp->function, stp->arity));
+
+ /*
+ * Save context for error messages.
+ */
+ stp->function = code[ci-2];
+ stp->arity = code[ci-1];
+
+ /*
+ * Save current offset of into the line instruction array.
+ */
+ if (stp->func_line) {
+ stp->func_line[function_number] = stp->current_li;
+ }
+
+ if (padding_required) {
const int finfo_ix = ci - FUNC_INFO_SZ;
- if (finfo_ix - last_func_start < BEAM_NIF_MIN_FUNC_SZ && last_func_start) {
+ if (finfo_ix - last_func_start < BEAM_NATIVE_MIN_FUNC_SZ) {
/* Must make room for call_nif op */
- int pad = BEAM_NIF_MIN_FUNC_SZ - (finfo_ix - last_func_start);
- ASSERT(pad > 0 && pad < BEAM_NIF_MIN_FUNC_SZ);
+ int pad = BEAM_NATIVE_MIN_FUNC_SZ - (finfo_ix - last_func_start);
+ ASSERT(pad > 0 && pad < BEAM_NATIVE_MIN_FUNC_SZ);
CodeNeed(pad);
sys_memmove(&code[finfo_ix+pad], &code[finfo_ix],
FUNC_INFO_SZ*sizeof(BeamInstr));
@@ -2833,20 +2857,6 @@ load_code(LoaderState* stp)
}
last_func_start = ci;
- /*
- * Save current offset of into the line instruction array.
- */
-
- if (stp->func_line) {
- stp->func_line[function_number] = stp->current_li;
- }
-
- /*
- * Save context for error messages.
- */
- stp->function = code[ci-2];
- stp->arity = code[ci-1];
-
/* When this assert is triggered, it is normally a sign that
the size of the ops.tab i_func_info instruction is not
the same as FUNC_INFO_SZ */
@@ -2876,7 +2886,6 @@ load_code(LoaderState* stp)
case op_i_bs_match_string_yfWW:
new_string_patch(stp, ci-1);
break;
-
case op_catch_yf:
/* code[ci-3] &&lb_catch_yf
* code[ci-2] y-register offset in E
@@ -2977,6 +2986,7 @@ load_code(LoaderState* stp)
#define succ3(St, X, Y) ((X).type == (Y).type && (X).val + 3 == (Y).val)
#define succ4(St, X, Y) ((X).type == (Y).type && (X).val + 4 == (Y).val)
+#define offset(St, X, Y, Offset) ((X).type == (Y).type && (X).val + Offset == (Y).val)
#ifdef NO_FPE_SIGNALS
#define no_fpe_signals(St) 1
@@ -3131,27 +3141,6 @@ mixed_types(LoaderState* stp, GenOpArg Size, GenOpArg* Rest)
return 0;
}
-static int
-is_killed_apply(LoaderState* stp, GenOpArg Reg, GenOpArg Live)
-{
- return Reg.type == TAG_x && Live.type == TAG_u &&
- Live.val+2 <= Reg.val;
-}
-
-static int
-is_killed(LoaderState* stp, GenOpArg Reg, GenOpArg Live)
-{
- return Reg.type == TAG_x && Live.type == TAG_u &&
- Live.val <= Reg.val;
-}
-
-static int
-is_killed_by_call_fun(LoaderState* stp, GenOpArg Reg, GenOpArg Live)
-{
- return Reg.type == TAG_x && Live.type == TAG_u &&
- Live.val+1 <= Reg.val;
-}
-
/*
* Test whether register Reg is killed by make_fun instruction that
* creates the fun given by index idx.
@@ -3171,14 +3160,23 @@ is_killed_by_make_fun(LoaderState* stp, GenOpArg Reg, GenOpArg idx)
}
}
-/*
- * Test whether register Reg is killed by the send instruction that follows.
- */
-
+/* Test whether Bif is "heavy" and should always go through its export entry */
static int
-is_killed_by_send(LoaderState* stp, GenOpArg Reg)
+is_heavy_bif(LoaderState* stp, GenOpArg Bif)
{
- return Reg.type == TAG_x && 2 <= Reg.val;
+ Export *ep;
+
+ if (Bif.type != TAG_u || Bif.val >= stp->num_imports) {
+ return 0;
+ }
+
+ ep = stp->import[Bif.val].bif;
+
+ if (ep) {
+ return bif_table[ep->bif_number].kind == BIF_KIND_HEAVY;
+ }
+
+ return 0;
}
/*
@@ -3324,8 +3322,8 @@ gen_get_integer2(LoaderState* stp, GenOpArg Fail, GenOpArg Ms, GenOpArg Live,
}
goto generic;
}
- } else {
- GENOP_NAME_ARITY(op, i_bs_get_integer, 6);
+ } else if (Size.type == TAG_x || Size.type == TAG_y) {
+ GENOP_NAME_ARITY(op, i_bs_get_integer, 6);
op->a[0] = Ms;
op->a[1] = Fail;
op->a[2] = Live;
@@ -3335,6 +3333,9 @@ gen_get_integer2(LoaderState* stp, GenOpArg Fail, GenOpArg Ms, GenOpArg Live,
op->a[5] = Dst;
op->next = NULL;
return op;
+ } else {
+ /* Invalid literal size. */
+ goto error;
}
op->next = NULL;
return op;
@@ -3391,7 +3392,7 @@ gen_get_binary2(LoaderState* stp, GenOpArg Fail, GenOpArg Ms, GenOpArg Live,
op->a[4] = Flags;
op->a[5] = Dst;
}
- } else {
+ } else if (Size.type == TAG_x || Size.type == TAG_y) {
GENOP_NAME_ARITY(op, i_bs_get_binary2, 6);
op->a[0] = Ms;
op->a[1] = Fail;
@@ -3400,6 +3401,9 @@ gen_get_binary2(LoaderState* stp, GenOpArg Fail, GenOpArg Ms, GenOpArg Live,
op->a[4].type = TAG_u;
op->a[4].val = (Unit.val << 3) | Flags.val;
op->a[5] = Dst;
+ } else {
+ /* Invalid literal size. */
+ goto error;
}
op->next = NULL;
return op;
@@ -3636,12 +3640,20 @@ gen_skip_bits2(LoaderState* stp, GenOpArg Fail, GenOpArg Ms,
goto error;
}
}
- } else {
+ } else if (Size.type == TAG_x || Size.type == TAG_y) {
GENOP_NAME_ARITY(op, i_bs_skip_bits2, 4);
op->a[0] = Ms;
op->a[1] = Size;
op->a[2] = Fail;
op->a[3] = Unit;
+ } else {
+ /*
+ * Invalid literal size. Can only happen if compiler
+ * optimizations are selectively disabled. For example,
+ * at the time of writing, [no_copt, no_type_opt] will allow
+ * skip instructions with invalid sizes to slip through.
+ */
+ goto error;
}
op->next = NULL;
return op;
@@ -5208,27 +5220,52 @@ final_touch(LoaderState* stp, struct erl_module_instance* inst_p)
*/
for (i = 0; i < stp->num_exps; i++) {
- Export* ep;
- BeamInstr* address = stp->export[i].address;
+ Export* ep;
+ BeamInstr* address = stp->export[i].address;
- if (address == NULL) {
- /* Skip stub for a BIF */
- continue;
- }
- ep = erts_export_put(stp->module, stp->export[i].function,
- stp->export[i].arity);
- if (on_load) {
- /*
- * on_load: Don't make any of the exported functions
- * callable yet. Keep any function in the current
- * code callable.
- */
- ep->beam[1] = (BeamInstr) address;
- }
- else
+ ep = erts_export_put(stp->module,
+ stp->export[i].function,
+ stp->export[i].arity);
+
+ /* Fill in BIF stubs with a proper call to said BIF. */
+ if (ep->bif_number != -1) {
+ erts_write_bif_wrapper(ep, address);
+ }
+
+ if (on_load) {
+ /*
+ * on_load: Don't make any of the exported functions
+ * callable yet. Keep any function in the current
+ * code callable.
+ */
+ ep->trampoline.not_loaded.deferred = (BeamInstr) address;
+ } else {
ep->addressv[erts_staging_code_ix()] = address;
+ }
}
+#ifdef DEBUG
+ /* Ensure that we've loaded stubs for all BIFs in this module. */
+ for (i = 0; i < BIF_SIZE; i++) {
+ BifEntry *entry = &bif_table[i];
+
+ if (stp->module == entry->module) {
+ Export *ep = erts_export_put(entry->module,
+ entry->name,
+ entry->arity);
+ BeamInstr *addr = ep->addressv[erts_staging_code_ix()];
+
+ if (!ErtsInArea(addr, stp->codev, stp->ci * sizeof(BeamInstr))) {
+ erts_exit(ERTS_ABORT_EXIT,
+ "Module %T doesn't export BIF %T/%i\n",
+ entry->module,
+ entry->name,
+ entry->arity);
+ }
+ }
+ }
+#endif
+
/*
* Import functions and patch all callers.
*/
@@ -5333,6 +5370,16 @@ transform_engine(LoaderState* st)
if (((1 << instr->a[ap].type) & mask) == 0)
goto restart;
break;
+#if defined(TOP_is_type_next_arg)
+ case TOP_is_type_next_arg:
+ mask = *pc++;
+ ASSERT(ap < instr->arity);
+ ASSERT(instr->a[ap].type < BEAM_NUM_TAGS);
+ if (((1 << instr->a[ap].type) & mask) == 0)
+ goto restart;
+ ap++;
+ break;
+#endif
case TOP_pred:
i = *pc++;
switch (i) {
@@ -5362,6 +5409,18 @@ transform_engine(LoaderState* st)
if (*pc++ != instr->a[ap].val)
goto restart;
break;
+#if defined(TOP_is_type_eq_next_arg)
+ case TOP_is_type_eq_next_arg:
+ mask = *pc++;
+ ASSERT(ap < instr->arity);
+ ASSERT(instr->a[ap].type < BEAM_NUM_TAGS);
+ if (((1 << instr->a[ap].type) & mask) == 0)
+ goto restart;
+ if (*pc++ != instr->a[ap].val)
+ goto restart;
+ ap++;
+ break;
+#endif
case TOP_is_same_var:
ASSERT(ap < instr->arity);
i = *pc++;
@@ -5400,15 +5459,16 @@ transform_engine(LoaderState* st)
i = instr->a[ap].val;
ASSERT(i < st->num_imports);
- if (i >= st->num_imports || st->import[i].bf == NULL)
- goto restart;
- if (bif_number != -1 &&
- bif_export[bif_number]->beam[1] != (BeamInstr) st->import[i].bf) {
+ if (i >= st->num_imports || st->import[i].bif == NULL)
goto restart;
- }
+ if (bif_number != -1) {
+ Export *bif = st->import[i].bif;
+ if (bif->bif_number != bif_number) {
+ goto restart;
+ }
+ }
}
break;
-
#endif
#if defined(TOP_is_not_bif)
case TOP_is_not_bif:
@@ -5438,7 +5498,7 @@ transform_engine(LoaderState* st)
* they are special.
*/
if (i < st->num_imports) {
- if (st->import[i].bf != NULL ||
+ if (st->import[i].bif != NULL ||
(st->import[i].module == am_erlang &&
st->import[i].function == am_apply &&
(st->import[i].arity == 2 || st->import[i].arity == 3))) {
@@ -5478,7 +5538,42 @@ transform_engine(LoaderState* st)
var[i].val = instr->a[ap].val;
ap++;
break;
-
+#if defined(TOP_is_type_set_var_next_arg)
+ case TOP_is_type_set_var_next_arg:
+ mask = pc[0];
+ i = pc[1];
+ ASSERT(i < TE_MAX_VARS);
+ ASSERT(ap < instr->arity);
+ ASSERT(instr->a[ap].type < BEAM_NUM_TAGS);
+ if (((1 << instr->a[ap].type) & mask) == 0)
+ goto restart;
+ ASSERT(i < TE_MAX_VARS);
+ var[i] = instr->a[ap];
+ ap++;
+ pc += 2;
+ break;
+#endif
+#if defined(TOP_is_type_eq_set_var_next_arg)
+ case TOP_is_type_eq_set_var_next_arg:
+ {
+ Eterm val;
+ mask = pc[0];
+ val = pc[1];
+ i = pc[2];
+ ASSERT(i < TE_MAX_VARS);
+ ASSERT(ap < instr->arity);
+ ASSERT(instr->a[ap].type < BEAM_NUM_TAGS);
+ if (((1 << instr->a[ap].type) & mask) == 0)
+ goto restart;
+ if (val != instr->a[ap].val)
+ goto restart;
+ ASSERT(i < TE_MAX_VARS);
+ var[i] = instr->a[ap];
+ ap++;
+ pc += 3;
+ }
+ break;
+#endif
#if defined(TOP_rest_args)
case TOP_rest_args:
{
@@ -5494,19 +5589,27 @@ transform_engine(LoaderState* st)
case TOP_commit:
instr = instr->next; /* The next_instr was optimized away. */
keep = instr;
- st->genop = instr;
-#ifdef DEBUG
- instr = 0;
-#endif
break;
+#if defined(TOP_commit_new_instr)
+ case TOP_commit_new_instr:
+ /*
+ * Reuse the last instruction on the left side instead of
+ * allocating a new instruction. Note that this is not
+ * safe if TOP_rest_args has been executed; therefore,
+ * this combined instruction is never used when that is
+ * the case.
+ */
+ ASSERT(instr->a == instr->def_args);
+ keep = instr;
+ instr->op = op = *pc++;
+ instr->arity = gen_opc[op].arity;
+ ap = 0;
+ break;
+#endif
#if defined(TOP_keep)
case TOP_keep:
/* Keep the current instruction unchanged. */
keep = instr;
- st->genop = instr;
-#ifdef DEBUG
- instr = 0;
-#endif
break;
#endif
#if defined(TOP_call_end)
@@ -5535,11 +5638,12 @@ transform_engine(LoaderState* st)
keep = instr->next; /* The next_instr was optimized away. */
*lastp = keep;
- st->genop = new_instr;
+ instr = new_instr;
}
/* FALLTHROUGH */
#endif
case TOP_end:
+ st->genop = instr;
while (first != keep) {
GenOp* next = first->next;
FREE_GENOP(st, first);
@@ -5550,28 +5654,28 @@ transform_engine(LoaderState* st)
/*
* Note that the instructions are generated in reverse order.
*/
- NEW_GENOP(st, instr);
- instr->next = st->genop;
- st->genop = instr;
- instr->op = op = *pc++;
- instr->arity = gen_opc[op].arity;
- ap = 0;
- break;
+ {
+ GenOp* new_instr;
+ NEW_GENOP(st, new_instr);
+ new_instr->next = instr;
+ instr = new_instr;
+ instr->op = op = *pc++;
+ instr->arity = gen_opc[op].arity;
+ ap = 0;
+ }
+ break;
#ifdef TOP_rename
case TOP_rename:
instr->op = op = *pc++;
instr->arity = gen_opc[op].arity;
return TE_OK;
#endif
- case TOP_store_type:
- i = *pc++;
- instr->a[ap].type = i;
- instr->a[ap].val = 0;
- break;
- case TOP_store_val:
- i = *pc++;
- instr->a[ap].val = i;
- break;
+ case TOP_store_val_next_arg:
+ instr->a[ap].type = pc[0];
+ instr->a[ap].val = pc[1];
+ ap++;
+ pc += 2;
+ break;
case TOP_store_var_next_arg:
i = *pc++;
ASSERT(i < TE_MAX_VARS);
@@ -5599,6 +5703,23 @@ transform_engine(LoaderState* st)
break;
case TOP_fail:
return TE_FAIL;
+#if defined(TOP_skip_unless)
+ case TOP_skip_unless:
+ /*
+ * Note that the caller of transform_engine() guarantees that
+ * there is always a second instruction available.
+ */
+ ASSERT(instr);
+ if (instr->next->op != pc[0]) {
+ /* The second instruction is wrong. Skip ahead. */
+ pc += pc[1] + 2;
+ ASSERT(*pc < NUM_TOPS); /* Valid instruction? */
+ } else {
+ /* Correct second instruction. */
+ pc += 2;
+ }
+ break;
+#endif
default:
ASSERT(0);
}
@@ -6283,12 +6404,12 @@ exported_from_module(Process* p, /* Process whose heap to use. */
if (ep->info.mfa.module == mod) {
Eterm tuple;
-
- if (ep->addressv[code_ix] == ep->beam &&
- BeamIsOpCode(ep->beam[0], op_call_error_handler)) {
- /* There is a call to the function, but it does not exist. */
- continue;
- }
+
+ if (ep->addressv[code_ix] == ep->trampoline.raw &&
+ BeamIsOpCode(ep->trampoline.op, op_call_error_handler)) {
+ /* There is a call to the function, but it does not exist. */
+ continue;
+ }
if (hp == hend) {
int need = 10 * 5;
diff --git a/erts/emulator/beam/beam_load.h b/erts/emulator/beam/beam_load.h
index 156c3c45e2..e7127c5b08 100644
--- a/erts/emulator/beam/beam_load.h
+++ b/erts/emulator/beam/beam_load.h
@@ -106,7 +106,7 @@ typedef struct beam_code_header {
}BeamCodeHeader;
-# define BEAM_NIF_MIN_FUNC_SZ 4
+# define BEAM_NATIVE_MIN_FUNC_SZ 4
void erts_release_literal_area(struct ErtsLiteralArea_* literal_area);
int erts_is_module_native(BeamCodeHeader* code);
diff --git a/erts/emulator/beam/bif.c b/erts/emulator/beam/bif.c
index 04e9db1f8e..9fc6ba4ac6 100644
--- a/erts/emulator/beam/bif.c
+++ b/erts/emulator/beam/bif.c
@@ -77,6 +77,8 @@ BIF_RETTYPE spawn_3(BIF_ALIST_3)
Eterm pid;
so.flags = erts_default_spo_flags;
+ so.opts = NIL;
+ so.tag = am_spawn_reply;
pid = erl_create_process(BIF_P, BIF_ARG_1, BIF_ARG_2, BIF_ARG_3, &so);
if (is_non_value(pid)) {
BIF_ERROR(BIF_P, so.error_code);
@@ -309,6 +311,15 @@ demonitor(Process *c_p, Eterm ref, Eterm *multip)
int deleted;
ErtsDSigSendContext ctx;
+ if (mon->flags & ERTS_ML_FLG_SPAWN_PENDING) {
+ /*
+ * Not allowed to remove this until spawn
+ * operation has succeeded; restore monitor...
+ */
+ erts_monitor_tree_insert(&ERTS_P_MONITORS(c_p), mon);
+ return am_false;
+ }
+
ASSERT(is_external_pid(to) || is_node_name_atom(to));
if (is_external_pid(to))
@@ -701,8 +712,11 @@ BIF_RETTYPE spawn_link_3(BIF_ALIST_3)
{
ErlSpawnOpts so;
Eterm pid;
+ Eterm tmp_heap[2];
so.flags = erts_default_spo_flags|SPO_LINK;
+ so.opts = CONS(&tmp_heap[0], am_link, NIL);
+ so.tag = am_spawn_reply;
pid = erl_create_process(BIF_P, BIF_ARG_1, BIF_ARG_2, BIF_ARG_3, &so);
if (is_non_value(pid)) {
BIF_ERROR(BIF_P, so.error_code);
@@ -716,132 +730,39 @@ BIF_RETTYPE spawn_link_3(BIF_ALIST_3)
/**********************************************************************/
-BIF_RETTYPE spawn_opt_1(BIF_ALIST_1)
+BIF_RETTYPE spawn_opt_4(BIF_ALIST_4)
{
ErlSpawnOpts so;
Eterm pid;
- Eterm* tp;
- Eterm ap;
- Eterm arg;
Eterm res;
+ int opts_error;
/*
- * Check that the first argument is a tuple of four elements.
+ * Fail order:
+ * - Bad types
+ * - Bad options
*/
- if (is_not_tuple(BIF_ARG_1)) {
- error:
- BIF_ERROR(BIF_P, BADARG);
- }
- tp = tuple_val(BIF_ARG_1);
- if (*tp != make_arityval(4))
- goto error;
-
- /*
- * Store default values for options.
- */
- so.flags = erts_default_spo_flags|SPO_USE_ARGS;
- so.min_heap_size = H_MIN_SIZE;
- so.min_vheap_size = BIN_VH_MIN_SIZE;
- so.max_heap_size = H_MAX_SIZE;
- so.max_heap_flags = H_MAX_FLAGS;
- so.priority = PRIORITY_NORMAL;
- so.max_gen_gcs = (Uint16) erts_atomic32_read_nob(&erts_max_gen_gcs);
- so.scheduler = 0;
-
- /*
- * Walk through the option list.
- */
- ap = tp[4];
- while (is_list(ap)) {
- arg = CAR(list_val(ap));
- if (arg == am_link) {
- so.flags |= SPO_LINK;
- } else if (arg == am_monitor) {
- so.flags |= SPO_MONITOR;
- } else if (is_tuple(arg)) {
- Eterm* tp2 = tuple_val(arg);
- Eterm val;
- if (*tp2 != make_arityval(2))
- goto error;
- arg = tp2[1];
- val = tp2[2];
- if (arg == am_priority) {
- if (val == am_max)
- so.priority = PRIORITY_MAX;
- else if (val == am_high)
- so.priority = PRIORITY_HIGH;
- else if (val == am_normal)
- so.priority = PRIORITY_NORMAL;
- else if (val == am_low)
- so.priority = PRIORITY_LOW;
- else
- goto error;
- } else if (arg == am_message_queue_data) {
- switch (val) {
- case am_on_heap:
- so.flags &= ~SPO_OFF_HEAP_MSGQ;
- so.flags |= SPO_ON_HEAP_MSGQ;
- break;
- case am_off_heap:
- so.flags &= ~SPO_ON_HEAP_MSGQ;
- so.flags |= SPO_OFF_HEAP_MSGQ;
- break;
- default:
- goto error;
- }
- } else if (arg == am_min_heap_size && is_small(val)) {
- Sint min_heap_size = signed_val(val);
- if (min_heap_size < 0) {
- goto error;
- } else if (min_heap_size < H_MIN_SIZE) {
- so.min_heap_size = H_MIN_SIZE;
- } else {
- so.min_heap_size = erts_next_heap_size(min_heap_size, 0);
- }
- } else if (arg == am_max_heap_size) {
- if (!erts_max_heap_size(val, &so.max_heap_size, &so.max_heap_flags))
- goto error;
- } else if (arg == am_min_bin_vheap_size && is_small(val)) {
- Sint min_vheap_size = signed_val(val);
- if (min_vheap_size < 0) {
- goto error;
- } else if (min_vheap_size < BIN_VH_MIN_SIZE) {
- so.min_vheap_size = BIN_VH_MIN_SIZE;
- } else {
- so.min_vheap_size = erts_next_heap_size(min_vheap_size, 0);
- }
- } else if (arg == am_fullsweep_after && is_small(val)) {
- Sint max_gen_gcs = signed_val(val);
- if (max_gen_gcs < 0) {
- goto error;
- } else {
- so.max_gen_gcs = max_gen_gcs;
- }
- } else if (arg == am_scheduler && is_small(val)) {
- Sint scheduler = signed_val(val);
- if (scheduler < 0 || erts_no_schedulers < scheduler)
- goto error;
- so.scheduler = (int) scheduler;
- } else {
- goto error;
- }
- } else {
- goto error;
- }
- ap = CDR(list_val(ap));
- }
- if (is_not_nil(ap)) {
- goto error;
- }
-
- if (so.max_heap_size != 0 && so.max_heap_size < so.min_heap_size) {
- goto error;
+ opts_error = erts_parse_spawn_opts(&so, BIF_ARG_4, NULL, 0);
+ if (opts_error) {
+ Sint arity;
+ if (is_not_atom(BIF_ARG_1) || is_not_atom(BIF_ARG_2))
+ BIF_ERROR(BIF_P, BADARG);
+ arity = erts_list_length(BIF_ARG_3);
+ if (arity < 0)
+ BIF_ERROR(BIF_P, BADARG);
+ if (arity > MAX_SMALL)
+ BIF_ERROR(BIF_P, SYSTEM_LIMIT);
+ if (opts_error > 0)
+ BIF_ERROR(BIF_P, BADARG);
+ BIF_ERROR(BIF_P, BADARG);
}
-
+
/*
* Spawn the process.
*/
- pid = erl_create_process(BIF_P, tp[1], tp[2], tp[3], &so);
+ so.opts = BIF_ARG_4;
+ so.tag = am_spawn_reply;
+ pid = erl_create_process(BIF_P, BIF_ARG_1, BIF_ARG_2, BIF_ARG_3, &so);
if (is_non_value(pid)) {
BIF_ERROR(BIF_P, so.error_code);
} else if (so.flags & SPO_MONITOR) {
@@ -859,6 +780,130 @@ BIF_RETTYPE spawn_opt_1(BIF_ALIST_1)
}
}
+/**********************************************************************/
+
+BIF_RETTYPE erts_internal_spawn_request_4(BIF_ALIST_4)
+{
+ ErlSpawnOpts so;
+ Eterm tmp_heap_mfna[4];
+ Eterm tmp_heap_alist[4 + 2];
+ Sint arity;
+ int opts_error;
+ Eterm tag, tmp, error;
+
+ if (!is_atom(BIF_ARG_1))
+ goto badarg;
+ if (!is_atom(BIF_ARG_2))
+ goto badarg;
+ arity = erts_list_length(BIF_ARG_3);
+ if (arity < 0)
+ goto badarg;
+
+ /*
+ * Fail order:
+ * - Bad types
+ * - Bad options
+ */
+ opts_error = erts_parse_spawn_opts(&so, BIF_ARG_4, &tag, !0);
+ if (arity > MAX_SMALL)
+ goto system_limit;
+ if (opts_error) {
+ if (opts_error > 0)
+ goto badarg;
+ goto badopt;
+ }
+
+ /* Make argument list for erts_internal:spawn_init/1 */
+ tmp = TUPLE3(&tmp_heap_alist[0], BIF_ARG_1, BIF_ARG_2, BIF_ARG_3);
+ tmp = CONS(&tmp_heap_alist[4], tmp, NIL);
+
+ so.mfa = TUPLE3(&tmp_heap_mfna[0], BIF_ARG_1, BIF_ARG_2, make_small(arity));
+ so.flags |= SPO_ASYNC;
+ so.mref = THE_NON_VALUE;
+ so.tag = tag;
+ so.opts = BIF_ARG_4;
+
+ /*
+ * Spawn the process.
+ */
+ tmp = erl_create_process(BIF_P, am_erts_internal, am_spawn_init, tmp, &so);
+ if (is_non_value(tmp)) {
+ switch (so.error_code) {
+ case SYSTEM_LIMIT:
+ goto system_limit;
+ case BADARG:
+ default:
+ ERTS_INTERNAL_ERROR("Unexpected error from erl_create_process()");
+ BIF_ERROR(BIF_P, EXC_INTERNAL_ERROR);
+ }
+ }
+
+ ASSERT(is_internal_pid(tmp));
+
+ if (ERTS_USE_MODIFIED_TIMING()) {
+ BIF_TRAP2(erts_delay_trap, BIF_P, so.mref, ERTS_MODIFIED_TIMING_DELAY);
+ }
+ else {
+ BIF_RET(so.mref);
+ }
+
+badarg:
+ BIF_RET(am_badarg);
+system_limit:
+ error = am_system_limit;
+ goto send_error;
+badopt:
+ error = am_badopt;
+ /* fall through... */
+send_error: {
+ Eterm ref = erts_make_ref(BIF_P);
+ if (!(so.flags & SPO_NO_EMSG))
+ erts_send_local_spawn_reply(BIF_P, ERTS_PROC_LOCK_MAIN, NULL,
+ tag, ref, error, am_undefined);
+ BIF_RET(ref);
+ }
+
+}
+
+BIF_RETTYPE spawn_request_abandon_1(BIF_ALIST_1)
+{
+ ErtsMonitor *omon;
+
+ if (is_not_internal_ref(BIF_ARG_1)) {
+ if (is_not_ref(BIF_ARG_1))
+ BIF_ERROR(BIF_P, BADARG);
+ /* Not an outstanding spawn_request of this process... */
+ BIF_RET(am_false);
+ }
+
+ omon = erts_monitor_tree_lookup(ERTS_P_MONITORS(BIF_P), BIF_ARG_1);
+ if (!omon
+ || ((omon->flags & (ERTS_ML_FLG_SPAWN_PENDING
+ | ERTS_ML_FLG_SPAWN_ABANDONED))
+ != ERTS_ML_FLG_SPAWN_PENDING)) {
+ /* Not an outstanding spawn_request of this process... */
+ BIF_RET(am_false);
+ }
+
+ ASSERT(erts_monitor_is_origin(omon));
+
+ if (omon->flags & ERTS_ML_FLG_SPAWN_LINK) {
+ /* Leave it for reply... */
+ omon->flags |= ERTS_ML_FLG_SPAWN_ABANDONED;
+ }
+ else {
+ /* We don't need it anymore; remove it... */
+ ErtsMonitorData *mdp;
+ erts_monitor_tree_delete(&ERTS_P_MONITORS(BIF_P), omon);
+ mdp = erts_monitor_to_data(omon);
+ if (erts_monitor_dist_delete(&mdp->target))
+ erts_monitor_release_both(mdp);
+ else
+ erts_monitor_release(omon);
+ }
+ BIF_RET(am_true);
+}
+
/**********************************************************************/
/* remove a link from a process */
@@ -990,8 +1035,7 @@ BIF_RETTYPE hibernate_3(BIF_ALIST_3)
BIF_RETTYPE get_stacktrace_0(BIF_ALIST_0)
{
- Eterm t = build_stacktrace(BIF_P, BIF_P->ftrace);
- BIF_RET(t);
+ BIF_RET(NIL);
}
/**********************************************************************/
@@ -1816,7 +1860,7 @@ ebif_bang_2(BIF_ALIST_2)
#define SEND_INTERNAL_ERROR (-6)
#define SEND_AWAIT_RESULT (-7)
#define SEND_YIELD_CONTINUE (-8)
-#define SEND_SYSTEM_LIMIT (-9)
+#define SEND_SYSTEM_LIMIT (-9)
static Sint remote_send(Process *p, DistEntry *dep,
@@ -1915,7 +1959,7 @@ do_send(Process *p, Eterm to, Eterm msg, Eterm return_term, Eterm *refp,
erts_dsprintf_buf_t *dsbufp = erts_create_logger_dsbuf();
erts_dsprintf(dsbufp,
"Discarding message %T from %T to %T in an old "
- "incarnation (%d) of this node (%d)\n",
+ "incarnation (%u) of this node (%u)\n",
msg,
p->common.id,
to,
@@ -1959,7 +2003,7 @@ do_send(Process *p, Eterm to, Eterm msg, Eterm return_term, Eterm *refp,
erts_dsprintf_buf_t *dsbufp = erts_create_logger_dsbuf();
erts_dsprintf(dsbufp,
"Discarding message %T from %T to %T in an old "
- "incarnation (%d) of this node (%d)\n",
+ "incarnation (%u) of this node (%u)\n",
msg,
p->common.id,
to,
@@ -1987,7 +2031,7 @@ do_send(Process *p, Eterm to, Eterm msg, Eterm return_term, Eterm *refp,
trace_send(p, portid, msg);
if (have_seqtrace(SEQ_TRACE_TOKEN(p))) {
- seq_trace_update_send(p);
+ seq_trace_update_serial(p);
seq_trace_output(SEQ_TRACE_TOKEN(p), msg,
SEQ_TRACE_SEND, portid, p);
}
@@ -2160,7 +2204,7 @@ BIF_RETTYPE send_3(BIF_ALIST_3)
break;
case SEND_YIELD:
if (suspend) {
- ERTS_BIF_PREP_YIELD3(retval, bif_export[BIF_send_3], p, to, msg, opts);
+ ERTS_BIF_PREP_YIELD3(retval, &bif_trap_export[BIF_send_3], p, to, msg, opts);
} else {
ERTS_BIF_PREP_RET(retval, am_nosuspend);
}
@@ -2277,7 +2321,7 @@ Eterm erl_send(Process *p, Eterm to, Eterm msg)
ERTS_BIF_PREP_RET(retval, msg);
break;
case SEND_YIELD:
- ERTS_BIF_PREP_YIELD2(retval, bif_export[BIF_send_2], p, to, msg);
+ ERTS_BIF_PREP_YIELD2(retval, &bif_trap_export[BIF_send_2], p, to, msg);
break;
case SEND_YIELD_RETURN:
yield_return:
@@ -2584,7 +2628,7 @@ BIF_RETTYPE iolist_size_1(BIF_ALIST_1)
} else {
ERTS_BIF_ERROR_TRAPPED1(BIF_P,
BADARG,
- bif_export[BIF_iolist_size_1],
+ &bif_trap_export[BIF_iolist_size_1],
input_list);
}
@@ -2604,7 +2648,7 @@ BIF_RETTYPE iolist_size_1(BIF_ALIST_1)
ESTACK_SAVE(s, &context->stack);
erts_set_gc_state(BIF_P, 0);
BUMP_ALL_REDS(BIF_P);
- BIF_TRAP1(bif_export[BIF_iolist_size_1], BIF_P, state_mref);
+ BIF_TRAP1(&bif_trap_export[BIF_iolist_size_1], BIF_P, state_mref);
}
/**********************************************************************/
@@ -3942,7 +3986,7 @@ BIF_RETTYPE halt_2(BIF_ALIST_2)
("System halted by BIF halt(%T, %T)\n", BIF_ARG_1, BIF_ARG_2));
if (flush) {
erts_halt(pos_int_code);
- ERTS_BIF_YIELD2(bif_export[BIF_halt_2], BIF_P, am_undefined, am_undefined);
+ ERTS_BIF_YIELD2(&bif_trap_export[BIF_halt_2], BIF_P, am_undefined, am_undefined);
}
else {
erts_proc_unlock(BIF_P, ERTS_PROC_LOCK_MAIN);
@@ -4531,7 +4575,7 @@ BIF_RETTYPE system_flag_2(BIF_ALIST_2)
BIF_RET(am_enabled);
case ERTS_SCHDLR_SSPND_YIELD_RESTART:
ERTS_VBUMP_ALL_REDS(BIF_P);
- BIF_TRAP2(bif_export[BIF_system_flag_2],
+ BIF_TRAP2(&bif_trap_export[BIF_system_flag_2],
BIF_P, BIF_ARG_1, BIF_ARG_2);
case ERTS_SCHDLR_SSPND_YIELD_DONE:
ERTS_BIF_YIELD_RETURN_X(BIF_P, am_enabled,
@@ -4556,7 +4600,7 @@ BIF_RETTYPE system_flag_2(BIF_ALIST_2)
BIF_RET(make_small(old_no));
case ERTS_SCHDLR_SSPND_YIELD_RESTART:
ERTS_VBUMP_ALL_REDS(BIF_P);
- BIF_TRAP2(bif_export[BIF_system_flag_2],
+ BIF_TRAP2(&bif_trap_export[BIF_system_flag_2],
BIF_P, BIF_ARG_1, BIF_ARG_2);
case ERTS_SCHDLR_SSPND_YIELD_DONE:
ERTS_BIF_YIELD_RETURN_X(BIF_P, make_small(old_no),
@@ -4720,7 +4764,7 @@ BIF_RETTYPE system_flag_2(BIF_ALIST_2)
BIF_RET(make_small(old_no));
case ERTS_SCHDLR_SSPND_YIELD_RESTART:
ERTS_VBUMP_ALL_REDS(BIF_P);
- BIF_TRAP2(bif_export[BIF_system_flag_2],
+ BIF_TRAP2(&bif_trap_export[BIF_system_flag_2],
BIF_P, BIF_ARG_1, BIF_ARG_2);
case ERTS_SCHDLR_SSPND_YIELD_DONE:
ERTS_BIF_YIELD_RETURN_X(BIF_P, make_small(old_no),
@@ -4870,9 +4914,13 @@ BIF_RETTYPE phash_2(BIF_ALIST_2)
BIF_RETTYPE phash2_1(BIF_ALIST_1)
{
Uint32 hash;
-
- hash = make_hash2(BIF_ARG_1);
- BIF_RET(make_small(hash & ((1L << 27) - 1)));
+ Eterm trap_state = THE_NON_VALUE;
+ hash = trapping_make_hash2(BIF_ARG_1, &trap_state, BIF_P);
+ if (trap_state == THE_NON_VALUE) {
+ BIF_RET(make_small(hash & ((1L << 27) - 1)));
+ } else {
+ BIF_TRAP1(&bif_trap_export[BIF_phash2_1], BIF_P, trap_state);
+ }
}
BIF_RETTYPE phash2_2(BIF_ALIST_2)
@@ -4880,6 +4928,7 @@ BIF_RETTYPE phash2_2(BIF_ALIST_2)
Uint32 hash;
Uint32 final_hash;
Uint32 range;
+ Eterm trap_state = THE_NON_VALUE;
/* Check for special case 2^32 */
if (term_equals_2pow32(BIF_ARG_2)) {
@@ -4891,7 +4940,10 @@ BIF_RETTYPE phash2_2(BIF_ALIST_2)
}
range = (Uint32) u;
}
- hash = make_hash2(BIF_ARG_1);
+ hash = trapping_make_hash2(BIF_ARG_1, &trap_state, BIF_P);
+ if (trap_state != THE_NON_VALUE) {
+ BIF_TRAP2(&bif_trap_export[BIF_phash2_2], BIF_P, trap_state, BIF_ARG_2);
+ }
if (range) {
final_hash = hash % range; /* [0..range-1] */
} else {
@@ -4967,15 +5019,32 @@ void erts_init_trap_export(Export* ep, Eterm m, Eterm f, Uint a,
Eterm (*bif)(BIF_ALIST))
{
int i;
+
sys_memset((void *) ep, 0, sizeof(Export));
+
for (i=0; i<ERTS_NUM_CODE_IX; i++) {
- ep->addressv[i] = ep->beam;
+ ep->addressv[i] = ep->trampoline.raw;
}
+
+ ep->bif_number = -1;
+
+ ep->info.op = op_i_func_info_IaaI;
ep->info.mfa.module = m;
ep->info.mfa.function = f;
ep->info.mfa.arity = a;
- ep->beam[0] = BeamOpCodeAddr(op_apply_bif);
- ep->beam[1] = (BeamInstr) bif;
+
+ ep->trampoline.op = BeamOpCodeAddr(op_call_bif_W);
+ ep->trampoline.raw[1] = (BeamInstr)bif;
+}
+
+/*
+ * Writes a BIF call wrapper to the given address.
+ */
+void erts_write_bif_wrapper(Export *export, BeamInstr *address) {
+ BifEntry *entry = &bif_table[export->bif_number];
+
+ address[0] = BeamOpCodeAddr(op_call_bif_W);
+ address[1] = (BeamInstr)entry->f;
}
void erts_init_bif(void)
@@ -5027,7 +5096,7 @@ void erts_init_bif(void)
}
/*
- * Scheduling of BIFs via NifExport...
+ * Scheduling of BIFs via ErtsNativeFunc...
*/
#define ERTS_WANT_NFUNC_SCHED_INTERNALS__
#include "erl_nfunc_sched.h"
@@ -5042,8 +5111,8 @@ schedule(Process *c_p, Process *dirty_shadow_proc,
int argc, Eterm *argv)
{
ERTS_LC_ASSERT(ERTS_PROC_LOCK_MAIN & erts_proc_lc_my_proc_locks(c_p));
- (void) erts_nif_export_schedule(c_p, dirty_shadow_proc,
- mfa, pc, BeamOpCodeAddr(op_apply_bif),
+ (void) erts_nfunc_schedule(c_p, dirty_shadow_proc,
+ mfa, pc, BeamOpCodeAddr(op_call_bif_W),
dfunc, ifunc,
module, function,
argc, argv);
@@ -5052,23 +5121,23 @@ schedule(Process *c_p, Process *dirty_shadow_proc,
static BIF_RETTYPE dirty_bif_result(BIF_ALIST_1)
{
- NifExport *nep = (NifExport *) ERTS_PROC_GET_NIF_TRAP_EXPORT(BIF_P);
- erts_nif_export_restore(BIF_P, nep, BIF_ARG_1);
+ ErtsNativeFunc *nep = (ErtsNativeFunc *) ERTS_PROC_GET_NFUNC_TRAP_WRAPPER(BIF_P);
+ erts_nfunc_restore(BIF_P, nep, BIF_ARG_1);
BIF_RET(BIF_ARG_1);
}
static BIF_RETTYPE dirty_bif_trap(BIF_ALIST)
{
- NifExport *nep = (NifExport *) ERTS_PROC_GET_NIF_TRAP_EXPORT(BIF_P);
+ ErtsNativeFunc *nep = (ErtsNativeFunc *) ERTS_PROC_GET_NFUNC_TRAP_WRAPPER(BIF_P);
/*
* Arity and argument registers already set
* correct by call to dirty_bif_trap()...
*/
- ASSERT(BIF_P->arity == nep->exp.info.mfa.arity);
+ ASSERT(BIF_P->arity == nep->trampoline.info.mfa.arity);
- erts_nif_export_restore(BIF_P, nep, THE_NON_VALUE);
+ erts_nfunc_restore(BIF_P, nep, THE_NON_VALUE);
BIF_P->i = (BeamInstr *) nep->func;
BIF_P->freason = TRAP;
@@ -5083,8 +5152,8 @@ static BIF_RETTYPE dirty_bif_exception(BIF_ALIST_2)
freason = signed_val(BIF_ARG_1);
- /* Restore orig info for error and clear nif export in handle_error() */
- freason |= EXF_RESTORE_NIF;
+ /* Restore orig info for error and clear nif wrapper in handle_error() */
+ freason |= EXF_RESTORE_NFUNC;
BIF_P->fvalue = BIF_ARG_2;
@@ -5122,6 +5191,7 @@ erts_schedule_bif(Process *proc,
if (!ERTS_PROC_IS_EXITING(c_p)) {
Export *exp;
BifFunction dbif, ibif;
+ BeamInstr call_instr;
BeamInstr *pc;
/*
@@ -5156,29 +5226,41 @@ erts_schedule_bif(Process *proc,
if (i == NULL) {
ERTS_INTERNAL_ERROR("Missing instruction pointer");
}
+
+ if (BeamIsOpCode(*i, op_i_generic_breakpoint)) {
+ ErtsCodeInfo *ci;
+ GenericBp *bp;
+
+ ci = erts_code_to_codeinfo(i);
+ bp = ci->u.gen_bp;
+
+ call_instr = bp->orig_instr;
+ } else {
+ call_instr = *i;
+ }
+
#ifdef HIPE
- else if (proc->flags & F_HIPE_MODE) {
+ if (proc->flags & F_HIPE_MODE) {
/* Pointer to bif export in i */
exp = (Export *) i;
- pc = c_p->cp;
+ pc = cp_val(c_p->stop[0]);
mfa = &exp->info.mfa;
- }
+ } else /* !! This is part of the if clause below !! */
#endif
- else if (BeamIsOpCode(*i, op_call_bif_e)) {
- /* Pointer to bif export in i+1 */
- exp = (Export *) i[1];
+ if (BeamIsOpCode(call_instr, op_call_light_bif_be)) {
+ /* Pointer to bif export in i+2 */
+ exp = (Export *) i[2];
pc = i;
mfa = &exp->info.mfa;
}
- else if (BeamIsOpCode(*i, op_call_bif_only_e)) {
- /* Pointer to bif export in i+1 */
- exp = (Export *) i[1];
+ else if (BeamIsOpCode(call_instr, op_call_light_bif_only_be)) {
+ /* Pointer to bif export in i+2 */
+ exp = (Export *) i[2];
pc = i;
mfa = &exp->info.mfa;
}
- else if (BeamIsOpCode(*i, op_apply_bif)) {
- /* Pointer to bif in i+1, and mfa in i-3 */
- pc = c_p->cp;
+ else if (BeamIsOpCode(call_instr, op_call_bif_W)) {
+ pc = cp_val(c_p->stop[0]);
mfa = erts_code_to_codemfa(i);
}
else {
@@ -5206,7 +5288,7 @@ erts_schedule_bif(Process *proc,
static BIF_RETTYPE
call_bif(Process *c_p, Eterm *reg, BeamInstr *I)
{
- NifExport *nep = ERTS_I_BEAM_OP_TO_NIF_EXPORT(I);
+ ErtsNativeFunc *nep = ERTS_I_BEAM_OP_TO_NFUNC(I);
ErtsBifFunc bif = (ErtsBifFunc) nep->func;
BIF_RETTYPE ret;
@@ -5219,12 +5301,12 @@ call_bif(Process *c_p, Eterm *reg, BeamInstr *I)
ret = (*bif)(c_p, reg, I);
if (is_value(ret))
- erts_nif_export_restore(c_p, nep, ret);
+ erts_nfunc_restore(c_p, nep, ret);
else if (c_p->freason != TRAP)
- c_p->freason |= EXF_RESTORE_NIF; /* restore in handle_error() */
+ c_p->freason |= EXF_RESTORE_NFUNC; /* restore in handle_error() */
else if (nep->func == ERTS_SCHED_BIF_TRAP_MARKER) {
/* BIF did an ordinary trap... */
- erts_nif_export_restore(c_p, nep, ret);
+ erts_nfunc_restore(c_p, nep, ret);
}
/* else:
* BIF rescheduled itself using erts_schedule_bif().
@@ -5241,7 +5323,7 @@ erts_call_dirty_bif(ErtsSchedulerData *esdp, Process *c_p, BeamInstr *I, Eterm *
int exiting;
Process *dirty_shadow_proc;
ErtsBifFunc bf;
- NifExport *nep;
+ ErtsNativeFunc *nep;
#ifdef DEBUG
Eterm *c_p_htop;
erts_aint32_t state;
@@ -5254,8 +5336,8 @@ erts_call_dirty_bif(ErtsSchedulerData *esdp, Process *c_p, BeamInstr *I, Eterm *
#endif
- nep = ERTS_I_BEAM_OP_TO_NIF_EXPORT(I);
- ASSERT(nep == ERTS_PROC_GET_NIF_TRAP_EXPORT(c_p));
+ nep = ERTS_I_BEAM_OP_TO_NFUNC(I);
+ ASSERT(nep == ERTS_PROC_GET_NFUNC_TRAP_WRAPPER(c_p));
nep->func = ERTS_SCHED_BIF_TRAP_MARKER;
@@ -5269,7 +5351,6 @@ erts_call_dirty_bif(ErtsSchedulerData *esdp, Process *c_p, BeamInstr *I, Eterm *
dirty_shadow_proc->freason = c_p->freason;
dirty_shadow_proc->fvalue = c_p->fvalue;
dirty_shadow_proc->ftrace = c_p->ftrace;
- dirty_shadow_proc->cp = c_p->cp;
dirty_shadow_proc->i = c_p->i;
#ifdef DEBUG
@@ -5316,7 +5397,6 @@ erts_call_dirty_bif(ErtsSchedulerData *esdp, Process *c_p, BeamInstr *I, Eterm *
c_p->freason = dirty_shadow_proc->freason;
c_p->fvalue = dirty_shadow_proc->fvalue;
c_p->ftrace = dirty_shadow_proc->ftrace;
- c_p->cp = dirty_shadow_proc->cp;
c_p->i = dirty_shadow_proc->i;
c_p->arity = dirty_shadow_proc->arity;
}
diff --git a/erts/emulator/beam/bif.tab b/erts/emulator/beam/bif.tab
index a5f46c4264..63c9fc23d5 100644
--- a/erts/emulator/beam/bif.tab
+++ b/erts/emulator/beam/bif.tab
@@ -25,13 +25,14 @@
#
# <bif-decl> ::= "bif" <bif> <C-name>* |
# "ubif" <bif> <C-name>* |
-# "gcbif" <bif> <C-name>*
+# "hbif" <bif> <C-name>*
# <bif> ::= <module> ":" <name> "/" <arity>
#
-# ubif: Use for operators and guard BIFs that never build anything
-# on the heap (such as tuple_size/1) and operators.
+# ubif: Use for operators and guard BIFs.
#
-# gcbif: Use for guard BIFs that may build on the heap (such as abs/1).
+# hbif: Use for BIFs that perform garbage collection or need up-to-date
+# information on where they were called from. These must be called
+# through the export entry.
#
# bif: Use for all other BIFs.
#
@@ -60,7 +61,7 @@ bif erlang:display_string/1
bif erlang:display_nl/0
ubif erlang:element/2
bif erlang:erase/0
-bif erlang:erase/1
+hbif erlang:erase/1
bif erlang:exit/1
bif erlang:exit/2
bif erlang:exit_signal/2
@@ -70,7 +71,7 @@ ubif erlang:float/1
bif erlang:float_to_list/1
bif erlang:float_to_list/2
bif erlang:fun_info/2
-bif erts_internal:garbage_collect/1
+hbif erts_internal:garbage_collect/1
bif erlang:get/0
bif erlang:get/1
bif erlang:get_keys/1
@@ -127,10 +128,10 @@ bif erlang:ports/0
bif erlang:pre_loaded/0
bif erlang:process_flag/2
bif erts_internal:process_flag/3
-bif erlang:process_info/1
-bif erlang:process_info/2
+hbif erlang:process_info/1
+hbif erlang:process_info/2
bif erlang:processes/0
-bif erlang:put/2
+hbif erlang:put/2
bif erlang:register/2
bif erlang:registered/0
ubif erlang:round/1
@@ -143,6 +144,8 @@ bif erlang:split_binary/2
bif erlang:statistics/1
bif erlang:term_to_binary/1
bif erlang:term_to_binary/2
+bif erlang:term_to_iovec/1
+bif erlang:term_to_iovec/2
bif erlang:throw/1
bif erlang:time/0
ubif erlang:tl/1
@@ -153,7 +156,7 @@ bif erlang:universaltime_to_localtime/1
bif erlang:unlink/1
bif erlang:unregister/1
bif erlang:whereis/1
-bif erlang:spawn_opt/1
+bif erlang:spawn_opt/4
bif erlang:setnode/2
bif erlang:dist_get_stat/1
bif erlang:dist_ctrl_input_handler/2
@@ -174,7 +177,7 @@ bif erts_internal:port_connect/2
bif erts_internal:request_system_task/3
bif erts_internal:request_system_task/4
-bif erts_internal:check_process_code/1
+hbif erts_internal:check_process_code/1
bif erts_internal:map_to_tuple_keys/1
bif erts_internal:term_type/1
@@ -193,10 +196,14 @@ bif erts_internal:scheduler_wall_time/1
bif erts_internal:dirty_process_handle_signals/1
-bif erts_internal:create_dist_channel/4
+bif erts_internal:create_dist_channel/3
bif erts_internal:ets_super_user/1
+bif erts_internal:spawn_request/4
+bif erts_internal:dist_spawn_request/4
+bif erlang:spawn_request_abandon/1
+
# inet_db support
bif erlang:port_set_data/2
bif erlang:port_get_data/1
@@ -466,7 +473,7 @@ bif code:is_module_native/1
# New Bifs in R9C.
#
-bif erlang:hibernate/3
+hbif erlang:hibernate/3
bif error_logger:warning_map/0
#
@@ -758,3 +765,9 @@ bif erts_internal:ets_raw_next/2
bif erts_internal:abort_pending_connection/2
+
+#
+# New in 23
+#
+
+bif erts_internal:get_creation/0
diff --git a/erts/emulator/beam/bif_instrs.tab b/erts/emulator/beam/bif_instrs.tab
index 8e0caa38a3..9c5f9e0c51 100644
--- a/erts/emulator/beam/bif_instrs.tab
+++ b/erts/emulator/beam/bif_instrs.tab
@@ -212,26 +212,32 @@ i_length.execute(Fail, Live, Dst) {
// Call a BIF, store the result in x(0) and transfer control to the
// next instruction.
//
-call_bif(Exp) {
+call_light_bif(Bif, Exp) {
+ Export *export;
ErtsBifFunc bf;
+
Eterm result;
ErlHeapFragment *live_hf_end;
- Export *export = (Export*) $Exp;
+
+ bf = (ErtsBifFunc) $Bif;
+ export = (Export*) $Exp;
if (!((FCALLS - 1) > 0 || (FCALLS-1) > neg_o_reds)) {
/*
* If we have run out of reductions, do a context
* switch before calling the BIF.
*/
- c_p->arity = GET_BIF_ARITY(export);
+ c_p->arity = GET_EXPORT_ARITY(export);
c_p->current = &export->info.mfa;
goto context_switch3;
}
- ERTS_MSACC_SET_BIF_STATE_CACHED_X(GET_BIF_MODULE(export),
- GET_BIF_ADDRESS(export));
+ if (ERTS_UNLIKELY(export->is_bif_traced)) {
+ $SAVE_CONTINUATION_POINTER($NEXT_INSTRUCTION);
+ $DISPATCH_EXPORT(export);
+ }
- bf = GET_BIF_ADDRESS(export);
+ ERTS_MSACC_SET_BIF_STATE_CACHED_X(GET_EXPORT_MODULE(export), bf);
PRE_BIF_SWAPOUT(c_p);
ERTS_DBG_CHK_REDS(c_p, FCALLS);
@@ -243,21 +249,26 @@ call_bif(Exp) {
ERTS_VERIFY_UNUSED_TEMP_ALLOC(c_p);
live_hf_end = c_p->mbuf;
ERTS_CHK_MBUF_SZ(c_p);
+
result = (*bf)(c_p, reg, I);
+
+ /* Only heavy BIFs may GC. */
+ ASSERT(E == c_p->stop);
+
ERTS_CHK_MBUF_SZ(c_p);
ASSERT(!ERTS_PROC_IS_EXITING(c_p) || is_non_value(result));
ERTS_VERIFY_UNUSED_TEMP_ALLOC(c_p);
ERTS_HOLE_CHECK(c_p);
ERTS_REQ_PROC_MAIN_LOCK(c_p);
if (ERTS_IS_GC_DESIRED(c_p)) {
- Uint arity = GET_BIF_ARITY(export);
+ Uint arity = GET_EXPORT_ARITY(export);
result = erts_gc_after_bif_call_lhf(c_p, live_hf_end, result,
reg, arity);
E = c_p->stop;
}
- PROCESS_MAIN_CHK_LOCKS(c_p);
HTOP = HEAP_TOP(c_p);
FCALLS = c_p->fcalls;
+ PROCESS_MAIN_CHK_LOCKS(c_p);
ERTS_DBG_CHK_REDS(c_p, FCALLS);
/*
@@ -280,10 +291,9 @@ call_bif(Exp) {
* erlang code or by nif_bif.epilogue() when the BIF
* is done).
*/
- SET_CP(c_p, $NEXT_INSTRUCTION);
+ $SAVE_CONTINUATION_POINTER($NEXT_INSTRUCTION);
SET_I(c_p->i);
- SWAPIN;
- Dispatch();
+ $DISPATCH();
}
/*
@@ -297,29 +307,38 @@ call_bif(Exp) {
//
// Call a BIF tail-recursively, storing the result in x(0) and doing
-// a return to the continuation poiner (c_p->cp).
+// a return to the continuation poiner.
//
-
-call_bif_only(Exp) {
+call_light_bif_only(Bif, Exp) {
+ ErlHeapFragment *live_hf_end;
ErtsBifFunc bf;
+ Export *export;
Eterm result;
- ErlHeapFragment *live_hf_end;
- Export *export = (Export*) $Exp;
+
+ bf = (ErtsBifFunc) $Bif;
+ export = (Export*) $Exp;
if (!((FCALLS - 1) > 0 || (FCALLS-1) > neg_o_reds)) {
/*
* If we have run out of reductions, do a context
* switch before calling the BIF.
*/
- c_p->arity = GET_BIF_ARITY(export);
+ c_p->arity = GET_EXPORT_ARITY(export);
c_p->current = &export->info.mfa;
goto context_switch3;
}
- ERTS_MSACC_SET_BIF_STATE_CACHED_X(GET_BIF_MODULE(export),
- GET_BIF_ADDRESS(export));
+ if (ERTS_UNLIKELY(export->is_bif_traced)) {
+ /* Set up a dummy stack frame so we can perform a normal call. Loader
+ * transformations ensure that the next instruction after this is
+ * 'deallocate_return 0'. */
+ $AH(0, 0, GET_EXPORT_ARITY(export));
+
+ $SAVE_CONTINUATION_POINTER($NEXT_INSTRUCTION);
+ $DISPATCH_EXPORT(export);
+ }
- bf = GET_BIF_ADDRESS(export);
+ ERTS_MSACC_SET_BIF_STATE_CACHED_X(GET_EXPORT_MODULE(export), bf);
PRE_BIF_SWAPOUT(c_p);
ERTS_DBG_CHK_REDS(c_p, FCALLS);
@@ -331,21 +350,26 @@ call_bif_only(Exp) {
ERTS_VERIFY_UNUSED_TEMP_ALLOC(c_p);
live_hf_end = c_p->mbuf;
ERTS_CHK_MBUF_SZ(c_p);
+
result = (*bf)(c_p, reg, I);
+
+ /* Only heavy BIFs may GC. */
+ ASSERT(E == c_p->stop);
+
ERTS_CHK_MBUF_SZ(c_p);
ASSERT(!ERTS_PROC_IS_EXITING(c_p) || is_non_value(result));
ERTS_VERIFY_UNUSED_TEMP_ALLOC(c_p);
ERTS_HOLE_CHECK(c_p);
ERTS_REQ_PROC_MAIN_LOCK(c_p);
if (ERTS_IS_GC_DESIRED(c_p)) {
- Uint arity = GET_BIF_ARITY(export);
+ Uint arity = GET_EXPORT_ARITY(export);
result = erts_gc_after_bif_call_lhf(c_p, live_hf_end, result,
reg, arity);
E = c_p->stop;
}
- PROCESS_MAIN_CHK_LOCKS(c_p);
HTOP = HEAP_TOP(c_p);
FCALLS = c_p->fcalls;
+ PROCESS_MAIN_CHK_LOCKS(c_p);
ERTS_DBG_CHK_REDS(c_p, FCALLS);
/*
@@ -367,11 +391,10 @@ call_bif_only(Exp) {
} else if (c_p->freason == TRAP) {
/*
* Dispatch to a trap. When the trap is done, a jump
- * to the continuation pointer (c_p->cp) will be done.
+ * to the continuation pointer on the stack will be done.
*/
SET_I(c_p->i);
- SWAPIN;
- Dispatch();
+ $DISPATCH();
}
/*
@@ -413,17 +436,26 @@ send() {
r(0) = result;
CHECK_TERM(r(0));
} else if (c_p->freason == TRAP) {
- SET_CP(c_p, $NEXT_INSTRUCTION);
+ $SAVE_CONTINUATION_POINTER($NEXT_INSTRUCTION);
SET_I(c_p->i);
SWAPIN;
- Dispatch();
+ $DISPATCH();
} else {
goto find_func_info;
}
}
+call_nif_early() {
+ HEAVY_SWAPOUT;
+ I = erts_call_nif_early(c_p, erts_code_to_codeinfo(I));
+ HEAVY_SWAPIN;
+ ASSERT(VALID_INSTR(*I));
+ Goto(*I);
+ //| -no_next
+}
+
call_nif := nif_bif.call_nif.epilogue;
-apply_bif := nif_bif.apply_bif.epilogue;
+call_bif := nif_bif.call_bif.epilogue;
nif_bif.head() {
Eterm nif_bif_result;
@@ -433,7 +465,7 @@ nif_bif.head() {
ErtsCodeMFA *codemfa;
}
-nif_bif.call_nif() {
+nif_bif.call_nif(Func, NifMod, DirtyFunc) {
/*
* call_nif is always first instruction in function:
*
@@ -443,11 +475,14 @@ nif_bif.call_nif() {
* I[0]: &&call_nif
* I[1]: Function pointer to NIF function
* I[2]: Pointer to erl_module_nif
- * I[3]: Function pointer to dirty NIF
+ * I[3]: Function pointer to dirty NIF. This is not used in this
+ * instruction, but dirty schedulers look at it.
*
- * This layout is determined by the NifExport struct
+ * This layout is determined by the ErtsNativeFunc struct
*/
+ (void)$DirtyFunc;
+
ERTS_MSACC_SET_STATE_CACHED_M_X(ERTS_MSACC_STATE_NIF);
codemfa = erts_code_to_codemfa(I);
@@ -465,12 +500,12 @@ nif_bif.call_nif() {
ASSERT(!ERTS_PROC_IS_EXITING(c_p));
{
typedef Eterm NifF(struct enif_environment_t*, int argc, Eterm argv[]);
- NifF* fp = vbf = (NifF*) I[1];
+ NifF* fp = vbf = (NifF*) $Func;
struct enif_environment_t env;
ASSERT(c_p->scheduler_data);
live_hf_end = c_p->mbuf;
ERTS_CHK_MBUF_SZ(c_p);
- erts_pre_nif(&env, c_p, (struct erl_module_nif*)I[2], NULL);
+ erts_pre_nif(&env, c_p, (struct erl_module_nif*)$NifMod, NULL);
ASSERT((c_p->scheduler_data)->current_nif == NULL);
(c_p->scheduler_data)->current_nif = &env;
@@ -495,15 +530,15 @@ nif_bif.call_nif() {
DTRACE_NIF_RETURN(c_p, codemfa);
}
-nif_bif.apply_bif() {
+nif_bif.call_bif(Func) {
/*
- * At this point, I points to the code[0] in the export entry for
- * the BIF:
+ * At this point, I points to the code[0] in the native function wrapper
+ * for the BIF:
*
* code[-3]: Module
* code[-2]: Function
* code[-1]: Arity
- * code[0]: &&apply_bif
+ * code[0]: &&call_bif
* code[1]: Function pointer to BIF function
*/
@@ -515,21 +550,19 @@ nif_bif.apply_bif() {
codemfa = erts_code_to_codemfa(I);
- ERTS_MSACC_SET_BIF_STATE_CACHED_X(codemfa->module, (BifFunction)Arg(0));
-
+ ERTS_MSACC_SET_BIF_STATE_CACHED_X(codemfa->module, (BifFunction)$Func);
/* In case we apply process_info/1,2 or load_nif/1 */
c_p->current = codemfa;
$SET_CP_I_ABS(I); /* In case we apply check_process_code/2. */
c_p->arity = 0; /* To allow garbage collection on ourselves
- * (check_process_code/2).
- */
+ * (check_process_code/2, put/2, etc). */
DTRACE_BIF_ENTRY(c_p, codemfa);
SWAPOUT;
ERTS_DBG_CHK_REDS(c_p, FCALLS - 1);
c_p->fcalls = FCALLS - 1;
- vbf = (BifFunction) Arg(0);
+ vbf = (BifFunction)$Func;
PROCESS_MAIN_CHK_LOCKS(c_p);
bif_nif_arity = codemfa->arity;
ASSERT(bif_nif_arity <= 4);
@@ -557,6 +590,7 @@ nif_bif.apply_bif() {
}
nif_bif.epilogue() {
+ //| -no_next
ERTS_REQ_PROC_MAIN_LOCK(c_p);
ERTS_HOLE_CHECK(c_p);
if (ERTS_IS_GC_DESIRED(c_p)) {
@@ -570,8 +604,7 @@ nif_bif.epilogue() {
if (ERTS_LIKELY(is_value(nif_bif_result))) {
r(0) = nif_bif_result;
CHECK_TERM(r(0));
- SET_I(c_p->cp);
- c_p->cp = 0;
+ $RETURN();
Goto(*I);
} else if (c_p->freason == TRAP) {
SET_I(c_p->i);
@@ -579,8 +612,42 @@ nif_bif.epilogue() {
c_p->flags &= ~F_HIBERNATE_SCHED;
goto do_schedule;
}
- Dispatch();
+ $DISPATCH();
+ }
+ {
+ BeamInstr *cp = erts_printable_return_address(c_p, E);
+ ASSERT(VALID_INSTR(*cp));
+ I = handle_error(c_p, cp, reg, c_p->current);
}
- I = handle_error(c_p, c_p->cp, reg, c_p->current);
goto post_error_handling;
}
+
+i_load_nif() {
+ //| -no_next
+ if (erts_try_seize_code_write_permission(c_p)) {
+ Eterm result;
+
+ PRE_BIF_SWAPOUT(c_p);
+ result = erts_load_nif(c_p, I, r(0), r(1));
+ erts_release_code_write_permission();
+ ERTS_REQ_PROC_MAIN_LOCK(c_p);
+ SWAPIN;
+
+ if (ERTS_LIKELY(is_value(result))) {
+ r(0) = result;
+ $NEXT0();
+ } else {
+ static ErtsCodeMFA mfa = {am_erlang, am_load_nif, 2};
+ c_p->freason = BADARG;
+ I = handle_error(c_p, I, reg, &mfa);
+ goto post_error_handling;
+ }
+ } else {
+ /* Yield and try again */
+ $SET_CP_I_ABS(I);
+ SWAPOUT;
+ c_p->current = NULL;
+ c_p->arity = 2;
+ goto do_schedule;
+ }
+}
diff --git a/erts/emulator/beam/big.c b/erts/emulator/beam/big.c
index 522f50287a..7666f23a4f 100644
--- a/erts/emulator/beam/big.c
+++ b/erts/emulator/beam/big.c
@@ -2176,6 +2176,24 @@ term_to_Uint64(Eterm term, Uint64 *up)
#endif
}
+int
+term_to_Uint32(Eterm term, Uint32 *up)
+{
+#if ERTS_SIZEOF_ETERM == 4
+ return term_to_Uint(term,up);
+#else
+ if (is_small(term)) {
+ Sint i = signed_val(term);
+ if (i >= 0) {
+ *up = (Uint32) i;
+ return 1;
+ }
+ }
+ *up = BADARG;
+ return 0;
+#endif
+}
+
int term_to_Sint(Eterm term, Sint *sp)
{
diff --git a/erts/emulator/beam/big.h b/erts/emulator/beam/big.h
index ad19cce395..3fed076419 100644
--- a/erts/emulator/beam/big.h
+++ b/erts/emulator/beam/big.h
@@ -168,6 +168,8 @@ Eterm erts_uint64_array_to_big(Uint **, int, int, Uint64 *);
int term_to_Uint64(Eterm, Uint64*);
int term_to_Sint64(Eterm, Sint64*);
#endif
+int term_to_Uint32(Eterm, Uint32*);
+
Uint32 big_to_uint32(Eterm b);
int term_equals_2pow32(Eterm);
diff --git a/erts/emulator/beam/binary.c b/erts/emulator/beam/binary.c
index 4ddf59092a..0ae4bc1e60 100644
--- a/erts/emulator/beam/binary.c
+++ b/erts/emulator/beam/binary.c
@@ -567,7 +567,7 @@ BIF_RETTYPE binary_to_list_1(BIF_ALIST_1)
if (size < L2B_B2L_MIN_EXEC_REDS*ERTS_B2L_BYTES_PER_REDUCTION) {
if (reds_left <= L2B_B2L_RESCHED_REDS) {
/* Yield and do it with full context reds... */
- ERTS_BIF_YIELD1(bif_export[BIF_binary_to_list_1],
+ ERTS_BIF_YIELD1(&bif_trap_export[BIF_binary_to_list_1],
BIF_P, BIF_ARG_1);
}
/* Allow a bit more reductions... */
@@ -621,7 +621,7 @@ BIF_RETTYPE binary_to_list_3(BIF_ALIST_3)
if (size < L2B_B2L_MIN_EXEC_REDS*ERTS_B2L_BYTES_PER_REDUCTION) {
if (reds_left <= L2B_B2L_RESCHED_REDS) {
/* Yield and do it with full context reds... */
- ERTS_BIF_YIELD3(bif_export[BIF_binary_to_list_3],
+ ERTS_BIF_YIELD3(&bif_trap_export[BIF_binary_to_list_3],
BIF_P, BIF_ARG_1, BIF_ARG_2, BIF_ARG_3);
}
/* Allow a bit more reductions... */
@@ -668,7 +668,7 @@ BIF_RETTYPE bitstring_to_list_1(BIF_ALIST_1)
if (size < L2B_B2L_MIN_EXEC_REDS*ERTS_B2L_BYTES_PER_REDUCTION) {
if (reds_left <= L2B_B2L_RESCHED_REDS) {
/* Yield and do it with full context reds... */
- ERTS_BIF_YIELD1(bif_export[BIF_bitstring_to_list_1],
+ ERTS_BIF_YIELD1(&bif_trap_export[BIF_bitstring_to_list_1],
BIF_P, BIF_ARG_1);
}
/* Allow a bit more reductions... */
@@ -1041,7 +1041,7 @@ HIPE_WRAPPER_BIF_DISABLE_GC(list_to_binary, 1)
BIF_RETTYPE list_to_binary_1(BIF_ALIST_1)
{
- return erts_list_to_binary_bif(BIF_P, BIF_ARG_1, bif_export[BIF_list_to_binary_1]);
+ return erts_list_to_binary_bif(BIF_P, BIF_ARG_1, &bif_trap_export[BIF_list_to_binary_1]);
}
HIPE_WRAPPER_BIF_DISABLE_GC(iolist_to_binary, 1)
@@ -1054,7 +1054,7 @@ BIF_RETTYPE iolist_to_binary_1(BIF_ALIST_1)
}
BIF_ERROR(BIF_P, BADARG);
}
- return erts_list_to_binary_bif(BIF_P, BIF_ARG_1, bif_export[BIF_iolist_to_binary_1]);
+ return erts_list_to_binary_bif(BIF_P, BIF_ARG_1, &bif_trap_export[BIF_iolist_to_binary_1]);
}
static int bitstr_list_len(ErtsIOListState *);
@@ -1081,7 +1081,7 @@ BIF_RETTYPE list_to_bitstring_1(BIF_ALIST_1)
else {
ErtsL2BState state = ERTS_L2B_STATE_INITER(BIF_P,
BIF_ARG_1,
- bif_export[BIF_list_to_bitstring_1],
+ &bif_trap_export[BIF_list_to_bitstring_1],
bitstr_list_len,
list_to_bitstr_buf_yielding);
int orig_reds_left = ERTS_BIF_REDS_LEFT(BIF_P);
diff --git a/erts/emulator/beam/code_ix.c b/erts/emulator/beam/code_ix.c
index 50352b4084..eab9d51b5e 100644
--- a/erts/emulator/beam/code_ix.c
+++ b/erts/emulator/beam/code_ix.c
@@ -37,9 +37,12 @@
erts_atomic32_t the_active_code_index;
erts_atomic32_t the_staging_code_index;
+static int code_writing_seized = 0;
static Process* code_writing_process = NULL;
struct code_write_queue_item {
Process *p;
+ void (*aux_func)(void *);
+ void *aux_arg;
struct code_write_queue_item* next;
};
static struct code_write_queue_item* code_write_queue = NULL;
@@ -108,19 +111,37 @@ void erts_abort_staging_code_ix(void)
CIX_TRACE("abort");
}
+static int try_seize_cwp(Process* c_p,
+ void (*aux_func)(void *),
+ void *aux_arg);
/*
* Calller _must_ yield if we return 0
*/
int erts_try_seize_code_write_permission(Process* c_p)
{
+ ASSERT(c_p != NULL);
+ return try_seize_cwp(c_p, NULL, NULL);
+}
+
+int erts_try_seize_code_write_permission_aux(void (*aux_func)(void *),
+ void *aux_arg)
+{
+ ASSERT(aux_func != NULL);
+ return try_seize_cwp(NULL, aux_func, aux_arg);
+}
+
+static int try_seize_cwp(Process* c_p,
+ void (*aux_func)(void *),
+ void *aux_arg)
+{
int success;
ASSERT(!erts_thr_progress_is_blocking()); /* to avoid deadlock */
- ASSERT(c_p != NULL);
erts_mtx_lock(&code_write_permission_mtx);
- success = (code_writing_process == NULL);
+ success = !code_writing_seized;
if (success) {
+ code_writing_seized = 1;
code_writing_process = c_p;
#ifdef ERTS_ENABLE_LOCK_CHECK
erts_tsd_set(has_code_write_permission, (void *) 1);
@@ -128,13 +149,22 @@ int erts_try_seize_code_write_permission(Process* c_p)
}
else { /* Already locked */
struct code_write_queue_item* qitem;
- ASSERT(code_writing_process != c_p);
- qitem = erts_alloc(ERTS_ALC_T_CODE_IX_LOCK_Q, sizeof(*qitem));
- qitem->p = c_p;
- erts_proc_inc_refc(c_p);
- qitem->next = code_write_queue;
- code_write_queue = qitem;
- erts_suspend(c_p, ERTS_PROC_LOCK_MAIN, NULL);
+ qitem = erts_alloc(ERTS_ALC_T_CODE_IX_LOCK_Q, sizeof(*qitem));
+ if (c_p) {
+ ASSERT(code_writing_process != c_p);
+ qitem->p = c_p;
+ qitem->aux_func = NULL;
+ qitem->aux_arg = NULL;
+ erts_proc_inc_refc(c_p);
+ erts_suspend(c_p, ERTS_PROC_LOCK_MAIN, NULL);
+ }
+ else {
+ qitem->p = NULL;
+ qitem->aux_func = aux_func;
+ qitem->aux_arg = aux_arg;
+ }
+ qitem->next = code_write_queue;
+ code_write_queue = qitem;
}
erts_mtx_unlock(&code_write_permission_mtx);
return success;
@@ -146,15 +176,25 @@ void erts_release_code_write_permission(void)
ERTS_LC_ASSERT(erts_has_code_write_permission());
while (code_write_queue != NULL) { /* unleash the entire herd */
struct code_write_queue_item* qitem = code_write_queue;
- erts_proc_lock(qitem->p, ERTS_PROC_LOCK_STATUS);
- if (!ERTS_PROC_IS_EXITING(qitem->p)) {
- erts_resume(qitem->p, ERTS_PROC_LOCK_STATUS);
- }
- erts_proc_unlock(qitem->p, ERTS_PROC_LOCK_STATUS);
- code_write_queue = qitem->next;
- erts_proc_dec_refc(qitem->p);
+ if (qitem->p) {
+ erts_proc_lock(qitem->p, ERTS_PROC_LOCK_STATUS);
+ if (!ERTS_PROC_IS_EXITING(qitem->p)) {
+ erts_resume(qitem->p, ERTS_PROC_LOCK_STATUS);
+ }
+ erts_proc_unlock(qitem->p, ERTS_PROC_LOCK_STATUS);
+ erts_proc_dec_refc(qitem->p);
+ }
+ else { /* aux work*/
+ ErtsSchedulerData *esdp = erts_get_scheduler_data();
+ ASSERT(esdp && esdp->type == ERTS_SCHED_NORMAL);
+ erts_schedule_misc_aux_work((int) esdp->no,
+ qitem->aux_func,
+ qitem->aux_arg);
+ }
+ code_write_queue = qitem->next;
erts_free(ERTS_ALC_T_CODE_IX_LOCK_Q, qitem);
}
+ code_writing_seized = 0;
code_writing_process = NULL;
#ifdef ERTS_ENABLE_LOCK_CHECK
erts_tsd_set(has_code_write_permission, (void *) 0);
@@ -165,6 +205,6 @@ void erts_release_code_write_permission(void)
#ifdef ERTS_ENABLE_LOCK_CHECK
int erts_has_code_write_permission(void)
{
- return (code_writing_process != NULL) && erts_tsd_get(has_code_write_permission);
+ return code_writing_seized && erts_tsd_get(has_code_write_permission);
}
#endif
diff --git a/erts/emulator/beam/code_ix.h b/erts/emulator/beam/code_ix.h
index 42976d2301..64a9c95979 100644
--- a/erts/emulator/beam/code_ix.h
+++ b/erts/emulator/beam/code_ix.h
@@ -133,6 +133,15 @@ ErtsCodeIndex erts_staging_code_ix(void);
*/
int erts_try_seize_code_write_permission(struct process* c_p);
+/* Try seize exclusive code write permission for aux work.
+ * System thread progress must not be blocked.
+ * On success return true.
+ * On failure return false and aux work func(arg) will be scheduled when
+ * permission is released. .
+ */
+int erts_try_seize_code_write_permission_aux(void (*func)(void *),
+ void *arg);
+
/* Release code write permission.
* Will resume any suspended waiters.
*/
diff --git a/erts/emulator/beam/dist.c b/erts/emulator/beam/dist.c
index 1293ad2d83..3091e322bc 100644
--- a/erts/emulator/beam/dist.c
+++ b/erts/emulator/beam/dist.c
@@ -149,6 +149,12 @@ static char *erts_dop_to_string(enum dop dop) {
return "PAYLOAD_EXIT2_TT";
if (dop == DOP_PAYLOAD_MONITOR_P_EXIT)
return "PAYLOAD_MONITOR_P_EXIT";
+ if (dop == DOP_SPAWN_REQUEST)
+ return "SPAWN_REQUEST";
+ if (dop == DOP_SPAWN_REQUEST_TT)
+ return "SPAWN_REQUEST_TT";
+ if (dop == DOP_SPAWN_REPLY)
+ return "SPAWN_REPLY";
ASSERT(0);
return "UNKNOWN";
}
@@ -168,6 +174,9 @@ static char *erts_dop_to_string(enum dop dop) {
int erts_is_alive; /* System must be blocked on change */
int erts_dist_buf_busy_limit;
+int erts_dflags_test_remove_hopefull_flags;
+
+Export spawn_request_yield_export;
/* distribution trap functions */
Export* dmonitor_node_trap = NULL;
@@ -262,6 +271,12 @@ static int monitor_connection_down(ErtsMonitor *mon, void *unused, Sint reds)
return ERTS_MON_LNK_FIRE_REDS;
}
+static int dist_pend_spawn_exit_connection_down(ErtsMonitor *mon, void *unused, Sint reds)
+{
+ erts_monitor_release(mon);
+ return 1;
+}
+
static int link_connection_down(ErtsLink *lnk, void *vdist, Sint reds)
{
erts_proc_sig_send_link_exit(NULL, THE_NON_VALUE, lnk,
@@ -273,12 +288,14 @@ typedef enum {
ERTS_CML_CLEANUP_STATE_LINKS,
ERTS_CML_CLEANUP_STATE_MONITORS,
ERTS_CML_CLEANUP_STATE_ONAME_MONITORS,
+ ERTS_CML_CLEANUP_STATE_PEND_SPAWN_EXIT_MONITORS,
ERTS_CML_CLEANUP_STATE_SEQUENCES,
ERTS_CML_CLEANUP_STATE_NODE_MONITORS
} ErtsConMonLnkSeqCleanupState;
typedef struct {
ErtsConMonLnkSeqCleanupState state;
+ DistEntry* dep;
ErtsMonLnkDist *dist;
DistSeqNode *seq;
void *yield_state;
@@ -327,6 +344,16 @@ con_monitor_link_seq_cleanup(void *vcmlcp)
if (reds <= 0)
break;
+ ASSERT(!cmlcp->yield_state);
+ cmlcp->state = ERTS_CML_CLEANUP_STATE_PEND_SPAWN_EXIT_MONITORS;
+ case ERTS_CML_CLEANUP_STATE_PEND_SPAWN_EXIT_MONITORS:
+ reds = erts_monitor_tree_foreach_delete_yielding(&dist->dist_pend_spawn_exit,
+ dist_pend_spawn_exit_connection_down,
+ NULL, &cmlcp->yield_state,
+ reds);
+ if (reds <= 0)
+ break;
+
cmlcp->dist = NULL;
erts_mon_link_dist_dec_refc(dist);
@@ -343,11 +370,30 @@ con_monitor_link_seq_cleanup(void *vcmlcp)
cmlcp->state = ERTS_CML_CLEANUP_STATE_NODE_MONITORS;
case ERTS_CML_CLEANUP_STATE_NODE_MONITORS:
if (cmlcp->trigger_node_monitors) {
+ Process* waiter;
send_nodes_mon_msgs(NULL,
am_nodedown,
cmlcp->nodename,
cmlcp->visability,
cmlcp->reason);
+ erts_de_rwlock(cmlcp->dep);
+ ASSERT(cmlcp->dep->state == ERTS_DE_STATE_IDLE ||
+ cmlcp->dep->state == ERTS_DE_STATE_PENDING);
+ ASSERT(cmlcp->dep->pending_nodedown);
+ waiter = cmlcp->dep->suspended_nodeup;
+ cmlcp->dep->suspended_nodeup = NULL;
+ cmlcp->dep->pending_nodedown = 0;
+ erts_de_rwunlock(cmlcp->dep);
+ erts_deref_dist_entry(cmlcp->dep);
+
+ if (waiter) {
+ erts_proc_lock(waiter, ERTS_PROC_LOCK_STATUS);
+ if (!ERTS_PROC_IS_EXITING(waiter)) {
+ erts_resume(waiter, ERTS_PROC_LOCK_STATUS);
+ }
+ erts_proc_unlock(waiter, ERTS_PROC_LOCK_STATUS);
+ erts_proc_dec_refc(waiter);
+ }
}
erts_cleanup_offheap(&cmlcp->oh);
erts_free(ERTS_ALC_T_CML_CLEANUP, vcmlcp);
@@ -364,7 +410,8 @@ con_monitor_link_seq_cleanup(void *vcmlcp)
}
static void
-schedule_con_monitor_link_seq_cleanup(ErtsMonLnkDist *dist,
+schedule_con_monitor_link_seq_cleanup(DistEntry* dep,
+ ErtsMonLnkDist *dist,
DistSeqNode *seq,
Eterm nodename,
Eterm visability,
@@ -404,7 +451,16 @@ schedule_con_monitor_link_seq_cleanup(ErtsMonLnkDist *dist,
cmlcp->seq = seq;
- cmlcp->trigger_node_monitors = is_value(nodename);
+ if (is_value(nodename)) {
+ ASSERT(dep);
+ cmlcp->trigger_node_monitors = 1;
+ cmlcp->dep = dep;
+ erts_ref_dist_entry(dep);
+ }
+ else {
+ cmlcp->trigger_node_monitors = 0;
+ cmlcp->dep = NULL;
+ }
cmlcp->nodename = nodename;
cmlcp->visability = visability;
if (rsz == 0)
@@ -422,6 +478,255 @@ schedule_con_monitor_link_seq_cleanup(ErtsMonLnkDist *dist,
}
}
+static void
+dist_pend_spawn_exit_save_child_result(Eterm result, Eterm ref, ErtsMonLnkDist *dist)
+{
+ ErtsMonitorData *new_mdp = NULL;
+ Process *proc = NULL;
+ int done = 0;
+
+ while (1) {
+ erts_mtx_lock(&dist->mtx);
+
+ if (!dist->alive)
+ done = !0;
+ else {
+ ErtsMonitor *mon;
+ mon = erts_monitor_tree_lookup(dist->dist_pend_spawn_exit, ref);
+ if (!mon) {
+ if (new_mdp) {
+ /*
+ * Save info so parent can get child pid when handling
+ * links during termination
+ */
+ erts_monitor_tree_insert(&dist->dist_pend_spawn_exit,
+ &new_mdp->target);
+ done = !0;
+ new_mdp = NULL;
+ }
+ }
+ else {
+ ErtsMonitorDataExtended *mdep;
+ /*
+ * The terminating parent is waiting for this signal.
+ * Store childs pid and resume parent if it has
+ * suspended (i.e, mon->other.ptr != NULL)...
+ */
+ proc = mon->other.ptr;
+ mdep = (ErtsMonitorDataExtended *) erts_monitor_to_data(mon);
+ if (is_atom(result))
+ mdep->md.origin.other.item = result;
+ else {
+ Eterm *hp;
+ ErlOffHeap oh;
+#ifdef DEBUG
+ int i;
+ for (i = 0; i < EXTERNAL_THING_HEAD_SIZE + 1; i++) {
+ ASSERT(is_non_value(mdep->heap[i]));
+ }
+#endif
+ hp = &(mdep)->heap[0];
+ ERTS_INIT_OFF_HEAP(&oh);
+ oh.first = mdep->uptr.ohhp;
+ mdep->md.origin.other.item
+ = copy_struct(result,
+ EXTERNAL_THING_HEAD_SIZE + 1,
+ &hp, &oh);
+ mdep->uptr.ohhp = oh.first;
+ }
+ done = !0;
+ }
+ }
+
+ erts_mtx_unlock(&dist->mtx);
+
+ if (done)
+ break;
+
+ /*
+ * No monitor previously saved by parent; create one
+ * and store child pid...
+ */
+ new_mdp = erts_monitor_create(ERTS_MON_TYPE_DIST_PROC, ref,
+ am_undefined, result, NIL);
+ ASSERT(new_mdp->target.other.item == am_undefined);
+ new_mdp->target.other.ptr = NULL;
+
+ ((ErtsMonitorDataExtended *) new_mdp)->dist = dist;
+ erts_mon_link_dist_inc_refc(dist);
+ erts_monitor_release(&new_mdp->origin);
+ }
+
+ if (proc)
+ erts_resume(proc, 0);
+
+ if (new_mdp)
+ erts_monitor_release(&new_mdp->target);
+
+}
+
+int
+erts_dist_pend_spawn_exit_delete(ErtsMonitor *mon)
+{
+ ErtsMonitorDataExtended *mdep;
+ int delete;
+ ErtsMonLnkDist *dist;
+ Uint16 flags;
+
+ mdep = (ErtsMonitorDataExtended *) erts_monitor_to_data(mon);
+ dist = mdep->dist;
+
+ erts_mtx_lock(&dist->mtx);
+
+ flags = mon->flags;
+ delete = !!dist->alive & !!(flags & ERTS_ML_FLG_IN_TABLE);
+
+ if (delete)
+ erts_monitor_tree_delete(&dist->dist_pend_spawn_exit, mon);
+
+ erts_mtx_unlock(&dist->mtx);
+
+ return delete;
+}
+
+int
+erts_dist_pend_spawn_exit_parent_setup(ErtsMonitor *mon)
+{
+ /*
+ * Return:
+ * 0 -> connection is closing
+ * !0 -> moved target part of monitor to dist_pend_spawn_exit
+ */
+ ErtsMonitorData *mdp;
+ ErtsMonLnkDist *dist;
+ int res;
+
+ ASSERT(erts_monitor_is_origin(mon));
+
+ mdp = (ErtsMonitorData *) erts_monitor_to_data(mon);
+
+ if (!erts_monitor_dist_delete(&mdp->target))
+ return 0;
+
+ dist = ((ErtsMonitorDataExtended *) mdp)->dist;
+
+ while (1) {
+ ErtsMonitor *tmp_mon;
+
+ erts_mtx_lock(&dist->mtx);
+ mdp->target.other.ptr = NULL;
+
+ if (!dist->alive) {
+ res = 0;
+ tmp_mon = NULL;
+ }
+ else {
+ res = !0;
+ tmp_mon = erts_monitor_tree_lookup(dist->dist_pend_spawn_exit,
+ mdp->ref);
+ if (!tmp_mon)
+ erts_monitor_tree_insert(&dist->dist_pend_spawn_exit,
+ &mdp->target);
+ else
+ erts_monitor_tree_delete(&dist->dist_pend_spawn_exit, tmp_mon);
+ }
+
+ erts_mtx_unlock(&dist->mtx);
+
+ if (!tmp_mon) {
+ if (!res)
+ erts_monitor_release(&mdp->target);
+ return res;
+ }
+ else {
+ /*
+ * Child had responded; copy its pid then store
+ * original target end in 'dist_pend_spawn_exit'
+ */
+ ErtsMonitorData *tmp_mdp = erts_monitor_to_data(tmp_mon);
+
+ if (is_atom(tmp_mdp->origin.other.item)) {
+ erts_monitor_release(tmp_mon);
+ erts_monitor_release(&mdp->target);
+ return 0; /* Spawn failed; drop it... */
+ }
+
+ /*
+ * If mdp->origin.other.item != am_pending, the other
+ * end is behaving badly by sending multiple
+ * responses...
+ */
+ if (mdp->origin.other.item == am_pending) {
+ ErtsMonitorDataExtended *mdep = (ErtsMonitorDataExtended *) mdp;
+ ErlOffHeap oh;
+ Eterm *hp;
+#ifdef DEBUG
+ int i;
+
+ ASSERT(is_external_pid(tmp_mdp->origin.other.item));
+ for (i = 0; i < EXTERNAL_THING_HEAD_SIZE + 1; i++) {
+ ASSERT(is_non_value(mdep->heap[i]));
+ }
+#endif
+ hp = &(mdep)->heap[0];
+ ERTS_INIT_OFF_HEAP(&oh);
+ oh.first = mdep->uptr.ohhp;
+ mdep->md.origin.other.item
+ = copy_struct(tmp_mdp->origin.other.item,
+ EXTERNAL_THING_HEAD_SIZE + 1,
+ &hp, &oh);
+ mdep->uptr.ohhp = oh.first;
+ }
+ erts_monitor_release(tmp_mon);
+ }
+ }
+}
+
+int
+erts_dist_pend_spawn_exit_parent_wait(Process *c_p,
+ ErtsProcLocks locks,
+ ErtsMonitor *mon)
+{
+ /*
+ * return value of
+ * > 0 --> Child pid can now be found in monitor
+ * 0 --> Connection not alive; drop it
+ * < 0 --> Setup completed; later need to wait for child pid
+ */
+ ErtsMonitorData *mdp;
+ ErtsMonLnkDist *dist;
+ int res;
+
+ ASSERT(erts_monitor_is_origin(mon));
+
+ mdp = (ErtsMonitorData *) erts_monitor_to_data(mon);
+ dist = ((ErtsMonitorDataExtended *) mdp)->dist;
+
+ erts_mtx_lock(&dist->mtx);
+
+ if (!dist->alive) {
+ res = 0;
+ }
+ else {
+ ASSERT(&mdp->target ==
+ erts_monitor_tree_lookup(dist->dist_pend_spawn_exit, mdp->ref));
+ if (mdp->origin.other.item != am_pending) {
+ erts_monitor_tree_delete(&dist->dist_pend_spawn_exit, &mdp->target);
+ res = 1;
+ }
+ else {
+ /* We need to suspend wait and wait for the result... */
+ mdp->target.other.ptr = (void *) c_p;
+ erts_suspend(c_p, locks, NULL);
+ res = -1;
+ }
+ }
+
+ erts_mtx_unlock(&dist->mtx);
+
+ return res;
+}
+
/*
** A full node name consists of a "n@h"
**
@@ -644,7 +949,7 @@ int erts_do_net_exits(DistEntry *dep, Eterm reason)
ErtsAtomCache *cache;
ErtsProcList *suspendees;
ErtsDistOutputBuf *obuf;
- Uint32 flags;
+ Uint64 flags;
erts_atomic_set_mb(&dep->dist_cmd_scheduled, 1);
erts_de_rwlock(dep);
@@ -674,7 +979,7 @@ int erts_do_net_exits(DistEntry *dep, Eterm reason)
dep->sequences = NULL;
nodename = dep->sysname;
- flags = dep->flags;
+ flags = dep->dflags;
erts_atomic_set_nob(&dep->input_handler, (erts_aint_t) NIL);
cache = dep->cache;
@@ -693,10 +998,11 @@ int erts_do_net_exits(DistEntry *dep, Eterm reason)
dep->send = NULL;
erts_set_dist_entry_not_connected(dep);
-
+ dep->pending_nodedown = 1;
erts_de_rwunlock(dep);
- schedule_con_monitor_link_seq_cleanup(mld,
+ schedule_con_monitor_link_seq_cleanup(dep,
+ mld,
sequences,
nodename,
(flags & DFLAG_PUBLISHED
@@ -711,8 +1017,6 @@ int erts_do_net_exits(DistEntry *dep, Eterm reason)
delete_cache(cache);
free_de_out_queues(dep, obuf);
- if (dep->transcode_ctx)
- transcode_free_ctx(dep);
}
dec_no_nodes();
@@ -734,6 +1038,8 @@ trap_function(Eterm func, int arity)
*/
static Eterm erts_dflags_record;
+static BIF_RETTYPE spawn_request_yield_3(BIF_ALIST_3);
+
void init_dist(void)
{
init_nodes_monitors();
@@ -759,61 +1065,114 @@ void init_dist(void)
dist_ctrl_put_data_trap = erts_export_put(am_erts_internal,
am_dist_ctrl_put_data,
2);
+ erts_init_trap_export(&spawn_request_yield_export,
+ am_erts_internal, am_spawn_request_yield,
+ 3, spawn_request_yield_3);
{
- Eterm* hp = erts_alloc(ERTS_ALC_T_LITERAL, (1+6)*sizeof(Eterm));
- erts_dflags_record = TUPLE6(hp, am_erts_dflags,
- make_small(DFLAG_DIST_DEFAULT),
- make_small(DFLAG_DIST_MANDATORY),
- make_small(DFLAG_DIST_ADDABLE),
- make_small(DFLAG_DIST_REJECTABLE),
- make_small(DFLAG_DIST_STRICT_ORDER));
- erts_set_literal_tag(&erts_dflags_record, hp, (1+6));
+ Eterm *hp_start, *hp, **hpp = NULL;
+ Uint sz = 0, *szp = &sz;
+ while (1) {
+ erts_dflags_record =
+ erts_bld_tuple(hpp, szp, 6,
+ am_erts_dflags,
+ erts_bld_uint64(hpp, szp, DFLAG_DIST_DEFAULT),
+ erts_bld_uint64(hpp, szp, DFLAG_DIST_MANDATORY),
+ erts_bld_uint64(hpp, szp, DFLAG_DIST_ADDABLE),
+ erts_bld_uint64(hpp, szp, DFLAG_DIST_REJECTABLE),
+ erts_bld_uint64(hpp, szp, DFLAG_DIST_STRICT_ORDER));
+ if (hpp) {
+ ASSERT(is_value(erts_dflags_record));
+ ASSERT(hp == hp_start + sz);
+ erts_set_literal_tag(&erts_dflags_record, hp_start, sz);
+ break;
+ }
+ hp = hp_start = erts_alloc(ERTS_ALC_T_LITERAL, sz*sizeof(Eterm));
+ hpp = &hp;
+ szp = NULL;
+ }
}
}
-#define ErtsDistOutputBuf2Binary(OB) OB->bin
-
static ERTS_INLINE ErtsDistOutputBuf *
-alloc_dist_obuf(Uint size, Uint headers)
+alloc_dist_obufs(byte **extp, TTBEncodeContext *ctx,
+ Uint data_size, Uint fragments, Uint vlen)
{
- Uint obuf_size = sizeof(ErtsDistOutputBuf)*(headers);
+ int ix;
ErtsDistOutputBuf *obuf;
+ char *ptr;
+ Uint iov_sz, obsz;
Binary *bin;
- byte *extp;
- int i;
+ ErlIOVec **feiov;
+ Uint fragment_size;
- bin = erts_bin_drv_alloc(obuf_size + size);
- erts_refc_add(&bin->intern.refc, headers - 1, 1);
+ obsz = sizeof(ErtsDistOutputBuf)*fragments;
+ if (obsz % sizeof(void *) != 0)
+ obsz += sizeof(void *) - (obsz % sizeof(void *));
- obuf = (ErtsDistOutputBuf *)&bin->orig_bytes[0];
- extp = (byte *)&bin->orig_bytes[obuf_size];
+ iov_sz = erts_ttb_iov_size(0, vlen, fragments);
+
+ bin = erts_bin_drv_alloc(obsz + iov_sz + data_size);
+ ctx->result_bin = bin;
+ ptr = (char *) &bin->orig_bytes[0];
+
+ obuf = (ErtsDistOutputBuf *) ptr;
+ ptr += obsz;
+
+ if (fragments > 1)
+ fragment_size = ERTS_DIST_FRAGMENT_SIZE;
+ else
+ fragment_size = ~((Uint) 0);
+
+ feiov = erts_ttb_iov_init(ctx, 0, ptr, vlen,
+ fragments, fragment_size);
+ ptr += iov_sz;
- for (i = 0; i < headers; i++) {
- obuf[i].bin = bin;
- obuf[i].extp = extp;
+ erts_refc_add(&bin->intern.refc, fragments - 1, 1);
+
+ for (ix = 0; ix < fragments; ix++) {
+ obuf[ix].bin = bin;
+ obuf[ix].eiov = feiov[ix];
#ifdef DEBUG
- obuf[i].dbg_pattern = ERTS_DIST_OUTPUT_BUF_DBG_PATTERN;
- obuf[i].ext_startp = extp;
- obuf[i].alloc_endp = &extp[size];
- ASSERT(bin == ErtsDistOutputBuf2Binary(obuf));
+ obuf[ix].dbg_pattern = ERTS_DIST_OUTPUT_BUF_DBG_PATTERN;
#endif
}
+ *extp = (byte *) ptr;
return obuf;
}
static ERTS_INLINE void
-free_dist_obuf(ErtsDistOutputBuf *obuf)
+free_dist_obuf(ErtsDistOutputBuf *obuf, int free_binv)
{
- Binary *bin = ErtsDistOutputBuf2Binary(obuf);
ASSERT(obuf->dbg_pattern == ERTS_DIST_OUTPUT_BUF_DBG_PATTERN);
- erts_bin_release(bin);
+
+ if (free_binv) {
+ int i;
+ int vlen = obuf->eiov->vsize;
+ ErlDrvBinary **binv = obuf->eiov->binv;
+ for (i = 0; i < vlen; i++) {
+ if (binv[i])
+ driver_free_binary(binv[i]);
+ }
+ }
+ erts_bin_release(obuf->bin);
}
static ERTS_INLINE Sint
size_obuf(ErtsDistOutputBuf *obuf)
{
- return sizeof(ErtsDistOutputBuf) + (obuf->ext_endp - obuf->ext_start)
- + (obuf->hdr_endp - obuf->hdrp);
+ Sint vlen = obuf->eiov->vsize;
+ Sint sz;
+#ifdef DEBUG
+ Sint i;
+ for (i = 0, sz = 0; i < vlen; i++)
+ sz += obuf->eiov->iov[i].iov_len;
+ ASSERT(sz == obuf->eiov->size);
+#endif
+ sz = sizeof(ErtsDistOutputBuf) + sizeof(ErlIOVec);
+ sz += obuf->eiov->size;
+ sz += sizeof(SysIOVec)*vlen;
+ sz += sizeof(ErlDrvBinary*)*vlen;
+ return sz;
}
static ErtsDistOutputBuf* clear_de_out_queues(DistEntry* dep)
@@ -853,7 +1212,7 @@ static void free_de_out_queues(DistEntry* dep, ErtsDistOutputBuf *obuf)
fobuf = obuf;
obuf = obuf->next;
obufsize += size_obuf(fobuf);
- free_dist_obuf(fobuf);
+ free_dist_obuf(fobuf, !0);
}
if (obufsize) {
@@ -880,7 +1239,7 @@ int erts_dsend_context_dtor(Binary* ctx_bin)
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]);
+ free_dist_obuf(&ctx->obuf[i], !0);
}
if (ctx->deref_dep)
erts_deref_dist_entry(ctx->dep);
@@ -947,14 +1306,14 @@ erts_dsig_send_m_exit(ErtsDSigSendContext *ctx, Eterm watcher, Eterm watched,
{
Eterm ctl, msg;
- if (~ctx->flags & (DFLAG_DIST_MONITOR | DFLAG_DIST_MONITOR_NAME)) {
+ if (~ctx->dflags & (DFLAG_DIST_MONITOR | DFLAG_DIST_MONITOR_NAME)) {
/*
* Receiver does not support DOP_MONITOR_P_EXIT (see dsig_send_monitor)
*/
return ERTS_DSIG_SEND_OK;
}
- if (ctx->dep->flags & DFLAG_EXIT_PAYLOAD) {
+ if (ctx->dep->dflags & DFLAG_EXIT_PAYLOAD) {
ctl = TUPLE4(&ctx->ctl_heap[0], make_small(DOP_PAYLOAD_MONITOR_P_EXIT),
watched, watcher, ref);
msg = reason;
@@ -976,7 +1335,7 @@ erts_dsig_send_monitor(ErtsDSigSendContext *ctx, Eterm watcher, Eterm watched,
{
Eterm ctl;
- if (~ctx->flags & (DFLAG_DIST_MONITOR | DFLAG_DIST_MONITOR_NAME)) {
+ if (~ctx->dflags & (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
@@ -1002,7 +1361,7 @@ erts_dsig_send_demonitor(ErtsDSigSendContext *ctx, Eterm watcher,
{
Eterm ctl;
- if (~ctx->flags & (DFLAG_DIST_MONITOR | DFLAG_DIST_MONITOR_NAME)) {
+ if (~ctx->dflags & (DFLAG_DIST_MONITOR | DFLAG_DIST_MONITOR_NAME)) {
/*
* Receiver does not support DOP_DEMONITOR_P (see dsig_send_monitor)
*/
@@ -1019,7 +1378,7 @@ erts_dsig_send_demonitor(ErtsDSigSendContext *ctx, Eterm watcher,
static int can_send_seqtrace_token(ErtsDSigSendContext* ctx, Eterm token) {
Eterm label;
- if (ctx->flags & DFLAG_BIG_SEQTRACE_LABELS) {
+ if (ctx->dflags & DFLAG_BIG_SEQTRACE_LABELS) {
/* The other end is capable of handling arbitrary seq_trace labels. */
return 1;
}
@@ -1052,7 +1411,7 @@ erts_dsig_send_msg(ErtsDSigSendContext* ctx, Eterm remote, Eterm message)
#endif
if (have_seqtrace(SEQ_TRACE_TOKEN(sender))) {
- seq_trace_update_send(sender);
+ seq_trace_update_serial(sender);
token = SEQ_TRACE_TOKEN(sender);
seq_trace_output(token, message, SEQ_TRACE_SEND, remote, sender);
}
@@ -1080,7 +1439,7 @@ erts_dsig_send_msg(ErtsDSigSendContext* ctx, Eterm remote, Eterm message)
send_token = (token != NIL && can_send_seqtrace_token(ctx, token));
- if (ctx->flags & DFLAG_SEND_SENDER) {
+ if (ctx->dflags & DFLAG_SEND_SENDER) {
dist_op = make_small(send_token ?
DOP_SEND_SENDER_TT :
DOP_SEND_SENDER);
@@ -1127,7 +1486,7 @@ erts_dsig_send_reg_msg(ErtsDSigSendContext* ctx, Eterm remote_name,
#endif
if (have_seqtrace(SEQ_TRACE_TOKEN(sender))) {
- seq_trace_update_send(sender);
+ seq_trace_update_serial(sender);
token = SEQ_TRACE_TOKEN(sender);
seq_trace_output(token, message, SEQ_TRACE_SEND, full_to, sender);
}
@@ -1182,20 +1541,20 @@ erts_dsig_send_exit_tt(ErtsDSigSendContext *ctx, Eterm local, Eterm remote,
DTRACE_CHARBUF(reason_str, 128);
#endif
- if (ctx->dep->flags & DFLAG_EXIT_PAYLOAD)
+ if (ctx->dep->dflags & DFLAG_EXIT_PAYLOAD)
msg = reason;
if (have_seqtrace(token)) {
- seq_trace_update_send(ctx->c_p);
+ seq_trace_update_serial(ctx->c_p);
seq_trace_output_exit(token, reason, SEQ_TRACE_SEND, remote, local);
- if (ctx->dep->flags & DFLAG_EXIT_PAYLOAD) {
+ if (ctx->dep->dflags & 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 {
- if (ctx->dep->flags & DFLAG_EXIT_PAYLOAD)
+ if (ctx->dep->dflags & 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);
@@ -1226,9 +1585,9 @@ erts_dsig_send_exit_tt(ErtsDSigSendContext *ctx, Eterm local, Eterm remote,
int
erts_dsig_send_exit(ErtsDSigSendContext *ctx, Eterm local, Eterm remote, Eterm reason)
{
- Eterm ctl, msg = ctx->dep->flags & DFLAG_EXIT_PAYLOAD ? reason : THE_NON_VALUE;
+ Eterm ctl, msg = ctx->dep->dflags & DFLAG_EXIT_PAYLOAD ? reason : THE_NON_VALUE;
- if (ctx->dep->flags & DFLAG_EXIT_PAYLOAD) {
+ if (ctx->dep->dflags & DFLAG_EXIT_PAYLOAD) {
ctl = TUPLE3(&ctx->ctl_heap[0], make_small(DOP_PAYLOAD_EXIT), local, remote);
msg = reason;
} else {
@@ -1243,7 +1602,7 @@ erts_dsig_send_exit2(ErtsDSigSendContext *ctx, Eterm local, Eterm remote, Eterm
{
Eterm ctl, msg;
- if (ctx->dep->flags & DFLAG_EXIT_PAYLOAD) {
+ if (ctx->dep->dflags & DFLAG_EXIT_PAYLOAD) {
ctl = TUPLE3(&ctx->ctl_heap[0],
make_small(DOP_PAYLOAD_EXIT2), local, remote);
msg = reason;
@@ -1268,6 +1627,65 @@ erts_dsig_send_group_leader(ErtsDSigSendContext *ctx, Eterm leader, Eterm remote
return dsig_send_ctl(ctx, ctl);
}
+static int
+dsig_send_spawn_request(ErtsDSigSendContext *ctx, Eterm ref, Eterm from,
+ Eterm gl, Eterm mfa, Eterm alist, Eterm opts)
+{
+ Process *sender = ctx->c_p;
+ if (!have_seqtrace(SEQ_TRACE_TOKEN(sender))) {
+ ctx->ctl = TUPLE6(&ctx->ctl_heap[0], make_small(DOP_SPAWN_REQUEST),
+ ref, from, gl, mfa, opts);
+ }
+ else {
+ Eterm tmp_heap[8];
+ Eterm node = ctx->dep ? ctx->dep->sysname : ctx->node;
+ Eterm msg;
+ Eterm token;
+ /*
+ * Present this as two messages for the sequence tracing.
+ * All data except the argument list in the first message
+ * and then the argument list as second message (which
+ * willl become an actual message). For more info see
+ * handling of seq-trace token in erl_create_process().
+ */
+
+ seq_trace_update_serial(sender);
+ token = SEQ_TRACE_TOKEN(sender);
+ msg = TUPLE6(&tmp_heap[0], am_spawn_request,
+ ref, from, gl, mfa, opts);
+ seq_trace_output(token, msg, SEQ_TRACE_SEND, node, sender);
+
+ seq_trace_update_serial(sender);
+ token = SEQ_TRACE_TOKEN(sender);
+ seq_trace_output(token, alist, SEQ_TRACE_SEND, node, sender);
+
+ ctx->ctl = TUPLE7(&ctx->ctl_heap[0], make_small(DOP_SPAWN_REQUEST_TT),
+ ref, from, gl, mfa, opts, token);
+ }
+ ctx->msg = alist;
+ return erts_dsig_send(ctx);
+}
+
+int
+erts_dsig_send_spawn_reply(ErtsDSigSendContext *ctx,
+ Eterm ref,
+ Eterm to,
+ Eterm flags,
+ Eterm result,
+ Eterm token)
+{
+ if (!have_seqtrace(token)) {
+ ctx->ctl = TUPLE5(&ctx->ctl_heap[0], make_small(DOP_SPAWN_REPLY),
+ ref, to, flags, result);
+ }
+ else {
+ ctx->ctl = TUPLE6(&ctx->ctl_heap[0], make_small(DOP_SPAWN_REPLY_TT),
+ ref, to, flags, result, token);
+ }
+ ctx->msg = THE_NON_VALUE;
+ return erts_dsig_send(ctx);
+}
+
#define ERTS_RBT_PREFIX dist_seq
#define ERTS_RBT_T DistSeqNode
#define ERTS_RBT_KEY_T Uint
@@ -1832,7 +2250,7 @@ int erts_net_message(Port *prt,
* the atom '' (empty cookie).
*/
ASSERT((type == DOP_SEND_SENDER || type == DOP_SEND_SENDER_TT)
- ? (is_pid(tuple[2]) && (dep->flags & DFLAG_SEND_SENDER))
+ ? (is_pid(tuple[2]) && (dep->dflags & DFLAG_SEND_SENDER))
: tuple[2] == am_Empty);
#ifdef ERTS_DIST_MSG_DBG
@@ -2054,6 +2472,247 @@ int erts_net_message(Port *prt,
(void) erts_proc_sig_send_group_leader(NULL, to, from, NIL);
break;
+ case DOP_SPAWN_REQUEST_TT: {
+ Eterm tmp_heap[2];
+ ErlSpawnOpts so;
+ int code, opts_error;
+ Eterm pid, error, ref, from, gl, mfa, opts, token, args;
+ /* {DOP_SPAWN_REQUEST_TT, Ref, From, GL, MFA, Opts, Token} */
+
+ if (tuple_arity != 7)
+ goto invalid_message;
+
+ token = tuple[7];
+
+ if (0) {
+
+ case DOP_SPAWN_REQUEST:
+ /* {DOP_SPAWN_REQUEST, Ref, From, GL, MFA, Opts} */
+ if (tuple_arity != 6)
+ goto invalid_message;
+
+ token = NIL;
+ }
+
+ ref = tuple[2];
+ from = tuple[3];
+ gl = tuple[4];
+ mfa = tuple[5];
+ opts = tuple[6];
+
+ if (is_not_external_ref(ref))
+ goto invalid_message;
+ if (is_not_external_pid(from))
+ goto invalid_message;
+ if (is_not_pid(gl))
+ goto invalid_message;
+ if (is_not_tuple_arity(mfa, 3))
+ goto invalid_message;
+ else {
+ Eterm *tp = tuple_val(tuple[5]);
+ if (is_not_atom(tp[1]))
+ goto invalid_message;
+ if (is_not_atom(tp[2]))
+ goto invalid_message;
+ if (is_not_small(tp[3]))
+ goto invalid_message;
+ }
+
+ opts_error = erts_parse_spawn_opts(&so, opts, NULL, 0);
+ if (opts_error) {
+ ErtsDSigSendContext ctx;
+ if (opts_error > 1)
+ goto invalid_message;
+ error = am_badopt;
+ dist_spawn_error:
+ if (ede_hfrag) {
+ erts_free_dist_ext_copy(erts_get_dist_ext(ede_hfrag));
+ free_message_buffer(ede_hfrag);
+ }
+ if (have_seqtrace(token)) {
+ /*
+ * See erl_create_process() for why we do this
+ * serial trickery...
+ */
+ Eterm tmp_heap[7];
+ Eterm seq_msg;
+ Eterm serial;
+ Uint serial_num;
+ /* Receiver spawn_request... */
+ serial = SEQ_TRACE_T_SERIAL(token);
+ serial_num = unsigned_val(serial);
+ serial_num--;
+ serial = make_small(serial_num);
+ SEQ_TRACE_T_SERIAL(token) = serial;
+ seq_msg = TUPLE6(&tmp_heap[0], am_spawn_request,
+ ref, from, gl, mfa, opts);
+ seq_trace_output(token, seq_msg, SEQ_TRACE_RECEIVE,
+ erts_this_dist_entry->sysname, NULL);
+ SEQ_TRACE_T_LASTCNT(token) = serial;
+ /* Send spawn_reply... */
+ erts_seq_trace_update_node_token(token);
+ seq_msg = TUPLE4(&tmp_heap[0],
+ am_spawn_reply, ref, am_error, error);
+ seq_trace_output(token, seq_msg, SEQ_TRACE_SEND, from, NULL);
+ }
+ 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_spawn_reply(&ctx,
+ tuple[2],
+ tuple[3],
+ make_small(0),
+ error,
+ token);
+ ASSERT(code == ERTS_DSIG_SEND_OK);
+ }
+ break;
+ }
+
+ so.mref = ref;
+ so.tag = am_spawn_reply;
+ so.parent_id = from;
+ so.group_leader = gl;
+ so.mfa = mfa;
+ so.dist_entry = dep;
+ so.edep = edep;
+ so.ede_hfrag = ede_hfrag;
+ so.token = token;
+ so.opts = opts;
+
+ args = CONS(&tmp_heap[0], mfa, NIL);
+ pid = erl_create_process(NULL,
+ am_erts_internal,
+ am_dist_spawn_init,
+ args,
+ &so);
+ if (is_non_value(pid)) {
+ if (so.error_code == SYSTEM_LIMIT)
+ error = am_system_limit;
+ else
+ goto invalid_message; /* should not happen */
+ goto dist_spawn_error;
+ }
+
+ break;
+ }
+
+ case DOP_SPAWN_REPLY_TT: {
+ ErtsLinkData *ldp;
+ ErtsLink *lnk;
+ int monitor;
+ Eterm ref, result, flags_term, parent, token;
+ Uint flags;
+
+ /* {DOP_SPAWN_REPLY_TT, Ref, To, Flags, From, Token} */
+ if (tuple_arity != 6)
+ goto invalid_message;
+
+ token = tuple[6];
+
+ if (0) {
+ case DOP_SPAWN_REPLY:
+
+ /* {DOP_SPAWN_REPLY, Ref, To, Flags, From} */
+ if (tuple_arity != 5)
+ goto invalid_message;
+
+ token = NIL;
+ }
+
+ ldp = NULL;
+ lnk = NULL;
+ monitor = 0;
+
+ ref = tuple[2];
+ parent = tuple[3];
+ flags_term = tuple[4];
+ result = tuple[5];
+
+ if (is_not_internal_pid(parent)) {
+ if (is_external_pid(parent)) {
+ DistEntry *dep = external_pid_dist_entry(parent);
+ if (dep == erts_this_dist_entry)
+ break; /* Old incarnation of this node... */
+ }
+ goto invalid_message;
+ }
+
+ if (is_not_internal_ref(ref)) {
+ if (is_external_ref(ref)) {
+ DistEntry *dep = external_ref_dist_entry(ref);
+ if (dep == erts_this_dist_entry)
+ break; /* Old incarnation of this node... */
+ }
+ goto invalid_message;
+ }
+
+ if (is_not_small(flags_term))
+ goto invalid_message;
+
+ flags = unsigned_val(flags_term);
+ if (flags >= (1 << 27))
+ goto invalid_message;
+
+ if (is_not_external_pid(result) && is_not_atom(result))
+ goto invalid_message;
+
+ if (is_external_pid(result)) {
+
+ monitor = !!(flags & ERTS_DIST_SPAWN_FLAG_MONITOR);
+
+ if (flags & ERTS_DIST_SPAWN_FLAG_LINK) {
+ /* Successful spawn-link... */
+ int code;
+
+ ldp = erts_link_create(ERTS_LNK_TYPE_DIST_PROC,
+ result, parent);
+ ASSERT(ldp->a.other.item == parent);
+ ASSERT(eq(ldp->b.other.item, result));
+ code = erts_link_dist_insert(&ldp->a, dep->mld);
+ ASSERT(code); (void)code;
+
+ lnk = &ldp->b;
+ }
+ }
+
+ if (!erts_proc_sig_send_dist_spawn_reply(dep->sysname, ref,
+ parent, lnk, result,
+ token)) {
+ ErtsDSigSendContext ctx;
+ int code;
+
+ if (monitor) {
+ 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_demonitor(&ctx, parent,
+ result, ref);
+ ASSERT(code == ERTS_DSIG_SEND_OK);
+ }
+ }
+
+ if (lnk) {
+
+ code = erts_link_dist_delete(&ldp->a);
+ ASSERT(code);
+ erts_link_release_both(ldp);
+ }
+
+ if (flags & ERTS_DIST_SPAWN_FLAG_LINK) {
+ /*
+ * Save info so the terminating parent can send us
+ * an exit signal with the correct exit reason...
+ */
+ dist_pend_spawn_exit_save_child_result(result,
+ ref,
+ dep->mld);
+ }
+ }
+
+ break;
+ }
+
default:
goto invalid_message;
}
@@ -2201,7 +2860,7 @@ retry:
ctx->connection_id = dep->connection_id;
ctx->no_suspend = no_suspend;
ctx->no_trap = no_trap;
- ctx->flags = dep->flags;
+ ctx->dflags = dep->dflags;
ctx->return_term = am_true;
ctx->phase = ERTS_DSIG_SEND_PHASE_INIT;
ctx->from = proc ? proc->common.id : am_undefined;
@@ -2255,8 +2914,6 @@ erts_dsig_send(ErtsDSigSendContext *ctx)
while (1) {
switch (ctx->phase) {
case ERTS_DSIG_SEND_PHASE_INIT:
- ctx->flags = ctx->flags;
- ctx->c_p = ctx->c_p;
if (!ctx->c_p) {
ctx->no_trap = 1;
@@ -2270,13 +2927,11 @@ erts_dsig_send(ErtsDSigSendContext *ctx)
if (!erts_is_alive)
return ERTS_DSIG_SEND_OK;
- if (ctx->flags & DFLAG_DIST_HDR_ATOM_CACHE) {
+ if (ctx->dflags & DFLAG_DIST_HDR_ATOM_CACHE) {
ctx->acmp = erts_get_atom_cache_map(ctx->c_p);
- ctx->max_finalize_prepend = 0;
}
else {
ctx->acmp = NULL;
- ctx->max_finalize_prepend = 3;
}
#ifdef ERTS_DIST_MSG_DBG
@@ -2287,176 +2942,212 @@ erts_dsig_send(ErtsDSigSendContext *ctx)
erts_fprintf(dbg_file, " MSG: %.160T\n", ctx->msg);
#endif
- ctx->data_size = ctx->max_finalize_prepend;
+ ctx->data_size = 0;
erts_reset_atom_cache_map(ctx->acmp);
- switch (erts_encode_dist_ext_size(ctx->ctl, ctx->flags,
- ctx->acmp, &ctx->data_size)) {
- case ERTS_EXT_SZ_OK:
- break;
- case ERTS_EXT_SZ_SYSTEM_LIMIT:
- retval = ERTS_DSIG_SEND_TOO_LRG;
- goto done;
- case ERTS_EXT_SZ_YIELD:
- ERTS_INTERNAL_ERROR("Unexpected yield result");
- break;
+ ERTS_INIT_TTBSizeContext(&ctx->u.sc, ctx->dflags);
+
+ while (1) {
+ ErtsExtSzRes sz_res;
+ Sint reds = CONTEXT_REDS;
+ sz_res = erts_encode_dist_ext_size(ctx->ctl,
+ ctx->acmp,
+ &ctx->u.sc,
+ &ctx->data_size,
+ &reds,
+ &ctx->vlen,
+ &ctx->fragments);
+ ctx->reds -= CONTEXT_REDS - reds;
+ if (sz_res == ERTS_EXT_SZ_OK)
+ break;
+ if (sz_res == ERTS_EXT_SZ_SYSTEM_LIMIT) {
+ retval = ERTS_DSIG_SEND_TOO_LRG;
+ goto done;
+ }
}
+
if (is_non_value(ctx->msg)) {
ctx->phase = ERTS_DSIG_SEND_PHASE_ALLOC;
break;
}
- ctx->u.sc.wstack.wstart = NULL;
- ctx->u.sc.flags = ctx->flags;
- ctx->u.sc.level = 0;
ctx->phase = ERTS_DSIG_SEND_PHASE_MSG_SIZE;
case ERTS_DSIG_SEND_PHASE_MSG_SIZE: {
- ErtsExtSzRes sz_res;
- sz_res = (!ctx->no_trap
- ? erts_encode_dist_ext_size_ctx(ctx->msg,
- ctx,
- &ctx->data_size)
- : erts_encode_dist_ext_size(ctx->msg,
- ctx->flags,
- ctx->acmp,
- &ctx->data_size));
- switch (sz_res) {
- case ERTS_EXT_SZ_OK:
- break;
- case ERTS_EXT_SZ_SYSTEM_LIMIT:
- retval = ERTS_DSIG_SEND_TOO_LRG;
- goto done;
- case ERTS_EXT_SZ_YIELD:
- if (ctx->no_trap)
- ERTS_INTERNAL_ERROR("Unexpected yield result");
+ Sint reds, *redsp;
+ if (!ctx->no_trap)
+ redsp = &ctx->reds;
+ else {
+ reds = CONTEXT_REDS;
+ redsp = &reds;
+ }
+ while (1) {
+ ErtsExtSzRes sz_res;
+ sz_res = erts_encode_dist_ext_size(ctx->msg,
+ ctx->acmp,
+ &ctx->u.sc,
+ &ctx->data_size,
+ redsp,
+ &ctx->vlen,
+ &ctx->fragments);
+ if (ctx->no_trap) {
+ ctx->reds -= CONTEXT_REDS - reds;
+ if (sz_res == ERTS_EXT_SZ_YIELD) {
+ reds = CONTEXT_REDS;
+ continue;
+ }
+ }
+ if (sz_res == ERTS_EXT_SZ_OK)
+ break;
+ if (sz_res == ERTS_EXT_SZ_SYSTEM_LIMIT) {
+ retval = ERTS_DSIG_SEND_TOO_LRG;
+ goto done;
+ }
+ ASSERT(sz_res == ERTS_EXT_SZ_YIELD);
retval = ERTS_DSIG_SEND_CONTINUE;
goto done;
}
ctx->phase = ERTS_DSIG_SEND_PHASE_ALLOC;
}
- case ERTS_DSIG_SEND_PHASE_ALLOC:
- erts_finalize_atom_cache_map(ctx->acmp, ctx->flags);
-
- 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->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;
-
+ case ERTS_DSIG_SEND_PHASE_ALLOC: {
+
+ erts_finalize_atom_cache_map(ctx->acmp, ctx->dflags);
+
+ ERTS_INIT_TTBEncodeContext(&ctx->u.ec, ctx->dflags);
+ ctx->dhdr_ext_size = erts_encode_ext_dist_header_size(&ctx->u.ec,
+ ctx->acmp,
+ ctx->fragments);
+ ctx->obuf = alloc_dist_obufs(&ctx->extp,
+ &ctx->u.ec,
+ ctx->dhdr_ext_size + ctx->data_size
+ + ((ctx->fragments - 1)
+ * ERTS_DIST_FRAGMENT_HEADER_SIZE),
+ ctx->fragments,
+ ctx->vlen);
+ ctx->alloced_fragments = ctx->fragments;
/* Encode internal version of dist header */
- 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;
+ ctx->dhdrp = ctx->extp;
+ ctx->extp += ctx->dhdr_ext_size;
+ ctx->dhdrp = erts_encode_ext_dist_header_setup(&ctx->u.ec,
+ ctx->extp,
+ ctx->acmp,
+ ctx->fragments,
+ ctx->from);
+ ctx->dhdr_ext_size = ctx->extp - ctx->dhdrp;
+ while (1) {
+ Sint reds = CONTEXT_REDS;
+ /* Encode control message */
+ int res = erts_encode_dist_ext(ctx->ctl, &ctx->extp,
+ ctx->dflags, ctx->acmp,
+ &ctx->u.ec, &ctx->fragments,
+ &reds);
+ ctx->reds -= CONTEXT_REDS - reds;
+ if (res == 0)
+ break;
+ }
if (is_non_value(ctx->msg)) {
- ctx->obuf->msg_start = NULL;
ctx->phase = ERTS_DSIG_SEND_PHASE_FIN;
break;
}
- ctx->u.ec.flags = ctx->flags;
- ctx->u.ec.hopefull_flags = 0;
- ctx->u.ec.level = 0;
- ctx->u.ec.wstack.wstart = NULL;
- ctx->obuf->msg_start = ctx->obuf->ext_endp;
ctx->phase = ERTS_DSIG_SEND_PHASE_MSG_ENCODE;
- case ERTS_DSIG_SEND_PHASE_MSG_ENCODE:
- if (!ctx->no_trap) {
- if (erts_encode_dist_ext(ctx->msg, &ctx->obuf->ext_endp, ctx->flags,
- ctx->acmp, &ctx->u.ec, &ctx->reds)) {
+ }
+ case ERTS_DSIG_SEND_PHASE_MSG_ENCODE: {
+ Sint reds, *redsp;
+ if (!ctx->no_trap)
+ redsp = &ctx->reds;
+ else {
+ reds = CONTEXT_REDS;
+ redsp = &reds;
+ }
+ while (1) {
+ int res = erts_encode_dist_ext(ctx->msg, &ctx->extp,
+ ctx->dflags, ctx->acmp,
+ &ctx->u.ec,
+ &ctx->fragments,
+ redsp);
+ if (!ctx->no_trap) {
+ if (res == 0)
+ break;
retval = ERTS_DSIG_SEND_CONTINUE;
goto done;
}
- } else {
- erts_encode_dist_ext(ctx->msg, &ctx->obuf->ext_endp, ctx->flags,
- ctx->acmp, NULL, NULL);
+ else {
+ ctx->reds -= CONTEXT_REDS - reds;
+ if (res == 0)
+ break;
+ reds = CONTEXT_REDS;
+ }
}
ctx->phase = ERTS_DSIG_SEND_PHASE_FIN;
+ }
case ERTS_DSIG_SEND_PHASE_FIN: {
-
- ASSERT(ctx->obuf->extp < ctx->obuf->ext_endp);
- ASSERT(ctx->obuf->ext_startp <= ctx->obuf->extp - ctx->max_finalize_prepend);
- ASSERT(ctx->obuf->ext_endp <= (byte*)ctx->obuf->ext_startp + ctx->data_size + ctx->dhdr_ext_size);
-
- ctx->data_size = ctx->obuf->ext_endp - ctx->obuf->extp;
-
- 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;
-
+ Uint fid = ctx->fragments;
+ ErtsDistOutputBuf *obuf = ctx->obuf;
+ ErlIOVec *eiov;
+ Sint fix;
+
+ ASSERT(fid >= 1);
+ ASSERT(ctx->alloced_fragments >= ctx->fragments);
+
+ eiov = obuf[0].eiov;
+ ASSERT(eiov);
+ ASSERT(eiov->vsize >= 3);
+ ASSERT(!eiov->iov[0].iov_base);
+ ASSERT(!eiov->iov[0].iov_len);
+ ASSERT(!eiov->binv[0]);
+ ASSERT(!eiov->iov[1].iov_base);
+ ASSERT(!eiov->iov[1].iov_len);
+ ASSERT(!eiov->binv[1]);
+
+ if (ctx->alloced_fragments > 1) {
+ ASSERT(get_int64(ctx->dhdrp + 1 + 1 + 8) == ctx->alloced_fragments);
/* 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;
+ put_int64(ctx->fragments, ctx->dhdrp + 1 + 1 + 8);
+ }
+
+ eiov->size += ctx->dhdr_ext_size;
+ eiov->iov[1].iov_base = ctx->dhdrp;
+ eiov->iov[1].iov_len = ctx->dhdr_ext_size;
+ erts_refc_inc(&obuf[0].bin->intern.refc, 2);
+ eiov->binv[1] = Binary2ErlDrvBinary(obuf[0].bin);
+ obuf[0].next = &obuf[1];
+
+ for (fix = 1; fix < ctx->fragments; fix++) {
+ byte *hdr = erts_encode_ext_dist_header_fragment(&ctx->extp,
+ --fid, ctx->from);
+ Uint sz = ctx->extp - hdr;
+
+ eiov = obuf[fix].eiov;
+ ASSERT(eiov);
+ ASSERT(eiov->vsize >= 3);
+ ASSERT(!eiov->iov[0].iov_base);
+ ASSERT(!eiov->iov[0].iov_len);
+ ASSERT(!eiov->binv[0]);
+ ASSERT(!eiov->iov[1].iov_base);
+ ASSERT(!eiov->iov[1].iov_len);
+ ASSERT(!eiov->binv[1]);
+
+ eiov->size += sz;
+ eiov->iov[1].iov_base = hdr;
+ eiov->iov[1].iov_len = sz;
+ erts_refc_inc(&obuf[fix].bin->intern.refc, 2);
+ eiov->binv[1] = Binary2ErlDrvBinary(obuf[fix].bin);
+ obuf[fix].next = &obuf[fix+1];
+ }
+ obuf[fix-1].next = NULL;
+ ASSERT(fid == 1);
+ /* If the initial fragment calculation was incorrect we free the
+ remaining output buffers. */
+ for (; fix < ctx->alloced_fragments; fix++) {
+ free_dist_obuf(&ctx->obuf[fix], 0);
}
ctx->phase = ERTS_DSIG_SEND_PHASE_SEND;
- if (ctx->reds <= 0) {
+ if (ctx->reds <= 0 && !ctx->no_trap) {
retval = ERTS_DSIG_SEND_CONTINUE;
goto done;
}
@@ -2478,7 +3169,7 @@ erts_dsig_send(ErtsDSigSendContext *ctx)
/* Not the same connection as when we started; drop message... */
erts_de_runlock(dep);
for (i = 0; i < ctx->fragments; i++)
- free_dist_obuf(&ctx->obuf[i]);
+ free_dist_obuf(&ctx->obuf[i], !0);
ctx->fragments = 0;
}
else {
@@ -2538,13 +3229,9 @@ erts_dsig_send(ErtsDSigSendContext *ctx)
erts_mtx_lock(&dep->qlock);
}
- 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);
- }
- }
+ ASSERT(fragments < 2
+ || (get_int64(ctx->obuf->eiov->iov[1].iov_base + 10)
+ == ctx->fragments));
if (fragments) {
ctx->obuf[fragments-1].next = NULL;
@@ -2666,8 +3353,19 @@ dist_port_command(Port *prt, ErtsDistOutputBuf *obuf)
bufp = NULL;
}
else {
- size = obuf->ext_endp - obuf->extp;
- bufp = (char*) obuf->extp;
+ char *ptr;
+ Sint i, vlen = obuf->eiov->vsize;
+ SysIOVec *iov = obuf->eiov->iov;
+ size = obuf->eiov->size;
+ bufp = ptr = erts_alloc(ERTS_ALC_T_TMP, size);
+ for (i = 0; i < vlen; i++) {
+ Uint len = iov[i].iov_len;
+ if (len) {
+ sys_memcpy((void *) ptr, (void *) iov[i].iov_base, len);
+ ptr += len;
+ }
+ }
+ ASSERT(size == ptr - bufp);
}
#ifdef USE_VM_PROBES
@@ -2689,6 +3387,7 @@ dist_port_command(Port *prt, ErtsDistOutputBuf *obuf)
fpe_was_unmasked = erts_block_fpe();
(*prt->drv_ptr->output)((ErlDrvData) prt->drv_data, bufp, size);
erts_unblock_fpe(fpe_was_unmasked);
+ erts_free(ERTS_ALC_T_TMP, bufp);
return size;
}
@@ -2696,59 +3395,53 @@ static Uint
dist_port_commandv(Port *prt, ErtsDistOutputBuf *obuf)
{
int fpe_was_unmasked;
- ErlDrvSizeT size = 0;
- SysIOVec iov[3];
- ErlDrvBinary* bv[3];
- ErlIOVec eiov;
+ SysIOVec iov[1];
+ Uint size;
+ ErlDrvBinary* bv[1];
+ ErlIOVec eiov, *eiovp;
ERTS_CHK_NO_PROC_LOCKS;
ERTS_LC_ASSERT(erts_lc_is_port_locked(prt));
- iov[0].iov_base = NULL;
- iov[0].iov_len = 0;
- bv[0] = NULL;
-
- if (!obuf) {
- size = 0;
+ if (obuf)
+ eiovp = obuf->eiov;
+ else {
+ iov[0].iov_base = NULL;
+ iov[0].iov_len = 0;
+ bv[0] = NULL;
+ eiov.size = 0;
eiov.vsize = 1;
+ eiov.iov = iov;
+ eiov.binv = bv;
+ eiovp = &eiov;
}
- else {
- int i = 1;
- eiov.vsize = 2;
-
- 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++;
- }
+#ifdef DEBUG
+ {
+ Uint sz;
+ Sint i;
+ for (i = 0, sz = 0; i < eiovp->vsize; i++)
+ sz += eiovp->iov[i].iov_len;
+ ASSERT(sz == eiovp->size);
+ }
+#endif
- 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));
+ {
+ Sint i;
+ erts_fprintf(dbg_file, "SEND: ");
+ for (i = 0; i < eiovp->vsize; i++) {
+ if (eiovp->iov[i].iov_len)
+ bw(eiovp->iov[i].iov_base, eiovp->iov[i].iov_len);
+ }
}
+#endif
- eiov.size = size;
- eiov.iov = iov;
- eiov.binv = bv;
-
- if (size > (Uint) INT_MAX)
+ if (eiovp->size > (Uint) INT_MAX)
erts_exit(ERTS_DUMP_EXIT,
"Absurdly large distribution output data buffer "
"(%beu bytes) passed.\n",
- size);
+ eiovp->size);
ASSERT(prt->drv_ptr->outputv);
@@ -2768,9 +3461,14 @@ dist_port_commandv(Port *prt, ErtsDistOutputBuf *obuf)
#endif
prt->caller = NIL;
fpe_was_unmasked = erts_block_fpe();
- (*prt->drv_ptr->outputv)((ErlDrvData) prt->drv_data, &eiov);
+ (*prt->drv_ptr->outputv)((ErlDrvData) prt->drv_data, eiovp);
erts_unblock_fpe(fpe_was_unmasked);
+ size = (Uint) eiovp->size;
+ /* Remove header used by driver... */
+ eiovp->size -= eiovp->iov[0].iov_len;
+ eiovp->iov[0].iov_base = NULL;
+ eiovp->iov[0].iov_len = 0;
return size;
}
@@ -2796,7 +3494,7 @@ erts_dist_command(Port *prt, int initial_reds)
{
Sint reds = initial_reds - ERTS_PORT_REDS_DIST_CMD_START;
enum dist_entry_state state;
- Uint32 flags;
+ Uint64 flags;
Sint qsize, obufsize = 0;
ErtsDistOutputQueue oq, foq;
DistEntry *dep = (DistEntry*) erts_prtsd_get(prt, ERTS_PRTSD_DIST_ENTRY);
@@ -2809,7 +3507,7 @@ erts_dist_command(Port *prt, int initial_reds)
erts_atomic_set_mb(&dep->dist_cmd_scheduled, 0);
erts_de_rlock(dep);
- flags = dep->flags;
+ flags = dep->dflags;
state = dep->state;
send = dep->send;
erts_de_runlock(dep);
@@ -2871,14 +3569,14 @@ erts_dist_command(Port *prt, int initial_reds)
do {
Uint size;
ErtsDistOutputBuf *fob;
+ obufsize += size_obuf(foq.first);
size = (*send)(prt, foq.first);
erts_atomic64_inc_nob(&dep->out);
esdp->io.out += (Uint64) size;
reds -= ERTS_PORT_REDS_DIST_CMD_DATA(size);
fob = foq.first;
- obufsize += size_obuf(fob);
foq.first = foq.first->next;
- free_dist_obuf(fob);
+ free_dist_obuf(fob, !0);
sched_flags = erts_atomic32_read_nob(&prt->sched.flags);
preempt = reds < 0 || (sched_flags & ERTS_PTS_FLG_EXIT);
if (sched_flags & ERTS_PTS_FLG_BUSY_PORT)
@@ -2902,7 +3600,7 @@ erts_dist_command(Port *prt, int initial_reds)
reds = erts_encode_ext_dist_header_finalize(ob, dep, flags, reds);
obufsize -= size_obuf(ob);
if (reds < 0)
- break;
+ break; /* finalize needs to be restarted... */
last_finalized = ob;
ob = ob->next;
} while (ob);
@@ -2938,24 +3636,23 @@ erts_dist_command(Port *prt, int initial_reds)
int preempt = 0;
while (oq.first && !preempt) {
ErtsDistOutputBuf *fob;
- Uint size;
+ Uint size, obsz;
obufsize += size_obuf(oq.first);
reds = erts_encode_ext_dist_header_finalize(oq.first, dep, flags, reds);
- obufsize -= size_obuf(oq.first);
- if (reds < 0) {
+ obsz = size_obuf(oq.first);
+ obufsize -= obsz;
+ if (reds < 0) { /* finalize needs to be restarted... */
preempt = 1;
break;
}
- 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;
reds -= ERTS_PORT_REDS_DIST_CMD_DATA(size);
fob = oq.first;
- obufsize += size_obuf(fob);
+ obufsize += obsz;
oq.first = oq.first->next;
- free_dist_obuf(fob);
+ free_dist_obuf(fob, !0);
sched_flags = erts_atomic32_read_nob(&prt->sched.flags);
preempt = reds <= 0 || (sched_flags & ERTS_PTS_FLG_EXIT);
if ((sched_flags & ERTS_PTS_FLG_BUSY_PORT) && oq.first && !preempt)
@@ -3008,7 +3705,6 @@ erts_dist_command(Port *prt, int initial_reds)
done:
if (obufsize != 0) {
- ASSERT(obufsize > 0);
erts_mtx_lock(&dep->qlock);
#ifdef DEBUG
qsize = (Sint) erts_atomic_add_read_nob(&dep->qsize,
@@ -3060,7 +3756,7 @@ erts_dist_command(Port *prt, int initial_reds)
ErtsDistOutputBuf *fob = oq.first;
oq.first = oq.first->next;
obufsize += size_obuf(fob);
- free_dist_obuf(fob);
+ free_dist_obuf(fob, !0);
}
foq.first = NULL;
@@ -3374,13 +4070,17 @@ dist_ctrl_get_data_1(BIF_ALIST_1)
{
DistEntry *dep = ERTS_PROC_GET_DIST_ENTRY(BIF_P);
const Sint initial_reds = ERTS_BIF_REDS_LEFT(BIF_P);
- Sint reds = initial_reds, obufsize = 0;
+ Sint reds = initial_reds, obufsize = 0, ix, vlen;
ErtsDistOutputBuf *obuf;
Eterm *hp, res;
- ProcBin *pb;
erts_aint_t qsize;
Uint32 conn_id, get_size;
- Uint hsz = 0, bin_sz;
+ Uint hsz = 0, data_sz;
+ SysIOVec *iov;
+ ErlDrvBinary **binv;
+#ifdef DEBUG
+ Eterm *hendp;
+#endif
if (!dep)
BIF_ERROR(BIF_P, EXC_NOTSUP);
@@ -3412,7 +4112,6 @@ dist_ctrl_get_data_1(BIF_ALIST_1)
{
if (!dep->tmp_out_queue.first) {
ASSERT(!dep->tmp_out_queue.last);
- ASSERT(!dep->transcode_ctx);
qsize = erts_atomic_read_acqb(&dep->qsize);
if (qsize > 0) {
erts_mtx_lock(&dep->qlock);
@@ -3433,13 +4132,13 @@ dist_ctrl_get_data_1(BIF_ALIST_1)
obuf = dep->tmp_out_queue.first;
obufsize += size_obuf(obuf);
- reds = erts_encode_ext_dist_header_finalize(obuf, dep, dep->flags, reds);
+ reds = erts_encode_ext_dist_header_finalize(obuf, dep, dep->dflags, reds);
obufsize -= size_obuf(obuf);
- if (reds < 0) {
+ if (reds < 0) { /* finalize needs to be restarted... */
erts_de_runlock(dep);
if (obufsize)
erts_atomic_add_nob(&dep->qsize, (erts_aint_t) -obufsize);
- ERTS_BIF_YIELD1(bif_export[BIF_dist_ctrl_get_data_1],
+ ERTS_BIF_YIELD1(&bif_trap_export[BIF_dist_ctrl_get_data_1],
BIF_P, BIF_ARG_1);
}
@@ -3452,53 +4151,70 @@ dist_ctrl_get_data_1(BIF_ALIST_1)
erts_de_runlock(dep);
- bin_sz = obuf->ext_endp - obuf->extp + obuf->hdr_endp - obuf->hdrp;
+ vlen = obuf->eiov->vsize;
+ data_sz = obuf->eiov->size;
+ iov = obuf->eiov->iov;
+ binv = obuf->eiov->binv;
+
+#ifdef DEBUG
+ {
+ Uint dbg_sz;
+ for (ix = 0, dbg_sz = 0; ix < vlen; ix++)
+ dbg_sz += iov[ix].iov_len;
+ ASSERT(data_sz == dbg_sz);
+ }
+#endif
+
+ ASSERT(vlen >= 1);
+ ASSERT(iov[0].iov_len == 0);
+ ASSERT(!binv[0]);
+
+ hsz = 2 /* cons */ + PROC_BIN_SIZE;
+ hsz *= vlen - 1;
get_size = dep->opts & ERTS_DIST_CTRL_OPT_GET_SIZE;
if (get_size) {
hsz += 3; /* 2 tuple */
- if (!IS_USMALL(0, bin_sz))
+ if (!IS_USMALL(0, data_sz))
hsz += BIG_UINT_HEAP_SIZE;
}
- if (!obuf->hdrp) {
- hp = HAlloc(BIF_P, PROC_BIN_SIZE + hsz);
- pb = (ProcBin *) (char *) hp;
- pb->thing_word = HEADER_PROC_BIN;
- pb->size = obuf->ext_endp - obuf->extp;
- pb->next = MSO(BIF_P).first;
- MSO(BIF_P).first = (struct erl_off_heap_header*) pb;
- pb->val = ErtsDistOutputBuf2Binary(obuf);
- pb->bytes = (byte*) obuf->extp;
- pb->flags = 0;
- res = make_binary(pb);
- hp += PROC_BIN_SIZE;
- } else {
- hp = HAlloc(BIF_P, PROC_BIN_SIZE * 2 + 4 + hsz);
- pb = (ProcBin *) (char *) hp;
- pb->thing_word = HEADER_PROC_BIN;
- pb->size = obuf->ext_endp - obuf->extp;
- pb->next = MSO(BIF_P).first;
- MSO(BIF_P).first = (struct erl_off_heap_header*) pb;
- pb->val = ErtsDistOutputBuf2Binary(obuf);
- pb->bytes = (byte*) obuf->extp;
- pb->flags = 0;
- hp += PROC_BIN_SIZE;
+ hp = HAlloc(BIF_P, hsz);
+#ifdef DEBUG
+ hendp = hp + hsz;
+#endif
- res = CONS(hp, make_binary(pb), NIL);
- hp += 2;
+ res = NIL;
+
+ for (ix = vlen - 1; ix > 0; ix--) {
+ Binary *bin;
+ ProcBin *pb;
+ Eterm bin_term;
+ ASSERT(binv[ix]);
+
+ /*
+ * We intentionally avoid using sub binaries
+ * since the GC might convert those to heap
+ * binaries and by this ruin the nice preparation
+ * for usage of this data as I/O vector in
+ * nifs/drivers.
+ */
+
+ bin = ErlDrvBinary2Binary(binv[ix]);
pb = (ProcBin *) (char *) hp;
+ hp += PROC_BIN_SIZE;
pb->thing_word = HEADER_PROC_BIN;
- pb->size = obuf->hdr_endp - obuf->hdrp;
+ pb->size = (Uint) iov[ix].iov_len;
pb->next = MSO(BIF_P).first;
MSO(BIF_P).first = (struct erl_off_heap_header*) pb;
- pb->val = ErtsDistOutputBuf2Binary(obuf);
- erts_refc_inc(&pb->val->intern.refc, 1);
- pb->bytes = (byte*) obuf->hdrp;
+ pb->val = bin;
+ pb->bytes = (byte*) iov[ix].iov_base;
pb->flags = 0;
- hp += PROC_BIN_SIZE;
- res = CONS(hp, make_binary(pb), res);
+ OH_OVERHEAD(&MSO(BIF_P), pb->size / sizeof(Eterm));
+ bin_term = make_binary(pb);
+
+ res = CONS(hp, bin_term, res);
hp += 2;
}
@@ -3522,15 +4238,20 @@ dist_ctrl_get_data_1(BIF_ALIST_1)
if (get_size) {
Eterm sz_term;
- if (IS_USMALL(0, bin_sz))
- sz_term = make_small(bin_sz);
+ if (IS_USMALL(0, data_sz))
+ sz_term = make_small(data_sz);
else {
- sz_term = uint_to_big(bin_sz, hp);
+ sz_term = uint_to_big(data_sz, hp);
hp += BIG_UINT_HEAP_SIZE;
}
res = TUPLE2(hp, sz_term, res);
+ hp += 3;
}
+ ASSERT(hendp == hp);
+
+ free_dist_obuf(obuf, 0);
+
BIF_RET2(res, (initial_reds - reds));
}
@@ -3764,13 +4485,11 @@ int distribution_info(fmtfn_t to, void *arg) /* Called by break handler */
BIF_RETTYPE setnode_2(BIF_ALIST_2)
{
Process *net_kernel = NULL;
- Uint creation;
+ Uint32 creation;
int success;
/* valid creation ? */
- if(!term_to_Uint(BIF_ARG_2, &creation))
- goto error;
- if(creation > 3)
+ if(!term_to_Uint32(BIF_ARG_2, &creation))
goto error;
/* valid node name ? */
@@ -3810,6 +4529,7 @@ BIF_RETTYPE setnode_2(BIF_ALIST_2)
if (success) {
inc_no_nodes();
erts_set_this_node(BIF_ARG_1, (Uint32) creation);
+ erts_this_dist_entry->creation = creation;
erts_is_alive = 1;
send_nodes_mon_msgs(NULL, am_nodeup, BIF_ARG_1, am_visible, NIL);
erts_proc_lock(net_kernel, ERTS_PROC_LOCKS_ALL);
@@ -3850,7 +4570,9 @@ BIF_RETTYPE setnode_2(BIF_ALIST_2)
typedef struct {
DistEntry *dep;
- Uint flags;
+ int de_locked;
+ Uint64 dflags;
+ Uint32 creation;
Uint version;
Eterm setup_pid;
Process *net_kernel;
@@ -3858,24 +4580,26 @@ typedef struct {
static int
setup_connection_epiloge_rwunlock(Process *c_p, DistEntry *dep,
- Eterm ctrlr, Uint flags,
- Uint version, Eterm setup_pid,
+ Eterm ctrlr, Uint64 flags,
+ Uint32 creation, Eterm setup_pid,
Process *net_kernel);
static Eterm
setup_connection_distctrl(Process *c_p, void *arg,
int *redsp, ErlHeapFragment **bpp);
-BIF_RETTYPE erts_internal_create_dist_channel_4(BIF_ALIST_4)
+BIF_RETTYPE erts_internal_create_dist_channel_3(BIF_ALIST_3)
{
BIF_RETTYPE ret;
- Uint flags;
+ Uint64 flags;
Uint version;
+ Uint32 creation;
Eterm *hp, res_tag = THE_NON_VALUE, res = THE_NON_VALUE;
DistEntry *dep = NULL;
int de_locked = 0;
Port *pp = NULL;
int true_nk;
+ Eterm *tpl;
Process *net_kernel = erts_whereis_process(BIF_P, ERTS_PROC_LOCK_MAIN,
am_net_kernel,
ERTS_PROC_LOCK_STATUS,
@@ -3901,19 +4625,27 @@ BIF_RETTYPE erts_internal_create_dist_channel_4(BIF_ALIST_4)
if (!is_internal_port(BIF_ARG_2) && !is_internal_pid(BIF_ARG_2))
goto badarg;
+ if (!is_tuple_arity(BIF_ARG_3, 3))
+ goto badarg;
+
+ tpl = tuple_val(BIF_ARG_3);
+
/* Dist flags... */
- if (!is_small(BIF_ARG_3))
+ if (!term_to_Uint64(tpl[1], &flags))
goto badarg;
- flags = unsigned_val(BIF_ARG_3);
/* Version... */
- if (!is_small(BIF_ARG_4))
+ if (!is_small(tpl[2]))
goto badarg;
- version = unsigned_val(BIF_ARG_4);
+ version = unsigned_val(tpl[2]);
if (version == 0)
goto badarg;
+ /* Creation... */
+ if (!term_to_Uint32(tpl[3], &creation))
+ goto badarg;
+
if (~flags & DFLAG_DIST_MANDATORY) {
erts_dsprintf_buf_t *dsbufp = erts_create_logger_dsbuf();
erts_dsprintf(dsbufp, "%T", BIF_P->common.id);
@@ -3944,11 +4676,18 @@ BIF_RETTYPE erts_internal_create_dist_channel_4(BIF_ALIST_4)
goto system_limit; /* Should never happen!!! */
if (is_internal_pid(BIF_ARG_2)) {
+ erts_de_rwlock(dep);
+ de_locked = 1;
+ if (dep->pending_nodedown)
+ goto suspend;
+
if (BIF_P->common.id == BIF_ARG_2) {
ErtsSetupConnDistCtrl scdc;
scdc.dep = dep;
- scdc.flags = flags;
+ scdc.de_locked = 1;
+ scdc.dflags = flags;
+ scdc.creation = creation;
scdc.version = version;
scdc.setup_pid = BIF_P->common.id;
scdc.net_kernel = net_kernel;
@@ -3956,6 +4695,7 @@ BIF_RETTYPE erts_internal_create_dist_channel_4(BIF_ALIST_4)
res = setup_connection_distctrl(BIF_P, &scdc, NULL, NULL);
/* Dec of refc on net_kernel by setup_connection_distctrl() */
net_kernel = NULL;
+ de_locked = 0;
BUMP_REDS(BIF_P, 5);
dep = NULL;
@@ -3968,11 +4708,16 @@ BIF_RETTYPE erts_internal_create_dist_channel_4(BIF_ALIST_4)
else {
ErtsSetupConnDistCtrl *scdcp;
+ erts_de_rwunlock(dep);
+ de_locked = 0;
+
scdcp = erts_alloc(ERTS_ALC_T_SETUP_CONN_ARG,
sizeof(ErtsSetupConnDistCtrl));
scdcp->dep = dep;
- scdcp->flags = flags;
+ scdcp->de_locked = 0;
+ scdcp->dflags = flags;
+ scdcp->creation = creation;
scdcp->version = version;
scdcp->setup_pid = BIF_P->common.id;
scdcp->net_kernel = net_kernel;
@@ -4021,6 +4766,9 @@ BIF_RETTYPE erts_internal_create_dist_channel_4(BIF_ALIST_4)
|| is_not_nil(dep->cid))
goto badarg;
+ if(dep->pending_nodedown)
+ goto suspend;
+
erts_atomic32_read_bor_nob(&pp->state, ERTS_PORT_SFLG_DISTRIBUTION);
erts_prtsd_set(pp, ERTS_PRTSD_DIST_ENTRY, dep);
@@ -4044,7 +4792,7 @@ BIF_RETTYPE erts_internal_create_dist_channel_4(BIF_ALIST_4)
conn_id = dep->connection_id;
set_res = setup_connection_epiloge_rwunlock(BIF_P, dep, BIF_ARG_2, flags,
- version, BIF_P->common.id,
+ creation, BIF_P->common.id,
net_kernel);
/* Dec of refc on net_kernel by setup_connection_epiloge_rwunlock() */
net_kernel = NULL;
@@ -4084,6 +4832,17 @@ BIF_RETTYPE erts_internal_create_dist_channel_4(BIF_ALIST_4)
return ret;
+ suspend:
+ ASSERT(de_locked);
+ ASSERT(!dep->suspended_nodeup);
+ dep->suspended_nodeup = BIF_P;
+ erts_proc_inc_refc(BIF_P);
+ erts_suspend(BIF_P, ERTS_PROC_LOCK_MAIN, NULL);
+ ERTS_BIF_PREP_YIELD3(ret,
+ &bif_trap_export[BIF_erts_internal_create_dist_channel_3],
+ BIF_P, BIF_ARG_1, BIF_ARG_2, BIF_ARG_3);
+ goto done;
+
badarg:
ERTS_BIF_PREP_RET(ret, am_badarg);
goto done;
@@ -4095,8 +4854,8 @@ BIF_RETTYPE erts_internal_create_dist_channel_4(BIF_ALIST_4)
static int
setup_connection_epiloge_rwunlock(Process *c_p, DistEntry *dep,
- Eterm ctrlr, Uint flags,
- Uint version, Eterm setup_pid,
+ Eterm ctrlr, Uint64 flags,
+ Uint32 creation, Eterm setup_pid,
Process *net_kernel)
{
Eterm notify_proc = NIL;
@@ -4125,8 +4884,7 @@ setup_connection_epiloge_rwunlock(Process *c_p, DistEntry *dep,
if (!success)
return 0;
- dep->version = version;
- dep->creation = 0;
+ dep->creation = creation;
ASSERT(is_internal_port(ctrlr) || is_internal_pid(ctrlr));
ASSERT(dep->state == ERTS_DE_STATE_PENDING);
@@ -4175,7 +4933,6 @@ setup_connection_distctrl(Process *c_p, void *arg, int *redsp, ErlHeapFragment *
{
ErtsSetupConnDistCtrl *scdcp = (ErtsSetupConnDistCtrl *) arg;
DistEntry *dep = scdcp->dep;
- int dep_locked = 0;
Eterm *hp;
Uint32 conn_id;
int dec_net_kernel_on_error = !0;
@@ -4186,8 +4943,10 @@ setup_connection_distctrl(Process *c_p, void *arg, int *redsp, ErlHeapFragment *
if (ERTS_PROC_IS_EXITING(c_p))
goto badarg;
- erts_de_rwlock(dep);
- dep_locked = !0;
+ if (!scdcp->de_locked) {
+ erts_de_rwlock(dep);
+ scdcp->de_locked = !0;
+ }
if (dep->state != ERTS_DE_STATE_PENDING)
goto badarg;
@@ -4211,7 +4970,7 @@ setup_connection_distctrl(Process *c_p, void *arg, int *redsp, ErlHeapFragment *
*redsp = 5;
if (!setup_connection_epiloge_rwunlock(c_p, dep, c_p->common.id,
- scdcp->flags, scdcp->version,
+ scdcp->dflags, scdcp->creation,
scdcp->setup_pid,
scdcp->net_kernel)) {
erts_proc_lock(c_p, ERTS_PROC_LOCKS_ALL_MINOR);
@@ -4241,7 +5000,7 @@ badarg:
if (bpp) /* not called directly */
erts_free(ERTS_ALC_T_SETUP_CONN_ARG, arg);
- if (dep_locked)
+ if (scdcp->de_locked)
erts_de_rwunlock(dep);
erts_deref_dist_entry(dep);
@@ -4252,7 +5011,40 @@ badarg:
BIF_RETTYPE erts_internal_get_dflags_0(BIF_ALIST_0)
{
+ if (erts_dflags_test_remove_hopefull_flags) {
+ /* For internal emulator tests only! */
+ Eterm *hp, **hpp = NULL;
+ Uint sz = 0, *szp = &sz;
+ Eterm res;
+ while (1) {
+ res = erts_bld_tuple(hpp, szp, 6,
+ am_erts_dflags,
+ erts_bld_uint64(hpp, szp, DFLAG_DIST_DEFAULT & ~DFLAG_DIST_HOPEFULLY),
+ erts_bld_uint64(hpp, szp, DFLAG_DIST_MANDATORY & ~DFLAG_DIST_HOPEFULLY),
+ erts_bld_uint64(hpp, szp, DFLAG_DIST_ADDABLE & ~DFLAG_DIST_HOPEFULLY),
+ erts_bld_uint64(hpp, szp, DFLAG_DIST_REJECTABLE & ~DFLAG_DIST_HOPEFULLY),
+ erts_bld_uint64(hpp, szp, DFLAG_DIST_STRICT_ORDER & ~DFLAG_DIST_HOPEFULLY));
+ if (hpp) {
+ ASSERT(is_value(res));
+ return res;
+ }
+ hp = HAlloc(BIF_P, sz);
+ hpp = &hp;
+ szp = NULL;
+ }
+ }
return erts_dflags_record;
+
+}
+
+BIF_RETTYPE erts_internal_get_creation_0(BIF_ALIST_0)
+{
+ Eterm *hp;
+ Uint hsz = 0;
+
+ erts_bld_uint(NULL, &hsz, erts_this_dist_entry->creation);
+ hp = HAlloc(BIF_P, hsz);
+ return erts_bld_uint(&hp, NULL, erts_this_dist_entry->creation);
}
BIF_RETTYPE erts_internal_new_connection_1(BIF_ALIST_1)
@@ -4331,7 +5123,7 @@ Sint erts_abort_pending_connection_rwunlock(DistEntry* dep,
erts_de_rwunlock(dep);
schedule_con_monitor_link_seq_cleanup(
- mld, NULL, THE_NON_VALUE,
+ NULL, mld, NULL, THE_NON_VALUE,
THE_NON_VALUE, THE_NON_VALUE);
if (resume_procs) {
@@ -4421,6 +5213,398 @@ int erts_auto_connect(DistEntry* dep, Process *proc, ErtsProcLocks proc_locks)
return 1;
}
+static BIF_RETTYPE spawn_request_yield_3(BIF_ALIST_3)
+{
+ Binary* bin = erts_magic_ref2bin(BIF_ARG_1);
+ ErtsDSigSendContext *ctx = (ErtsDSigSendContext*) ERTS_MAGIC_BIN_DATA(bin);
+ Sint initial_reds = (Sint) (ERTS_BIF_REDS_LEFT(BIF_P) * TERM_TO_BINARY_LOOP_FACTOR);
+ int code;
+
+ ASSERT(ERTS_MAGIC_BIN_DESTRUCTOR(bin) == erts_dsend_context_dtor);
+
+ ctx->reds = initial_reds;
+ code = erts_dsig_send(ctx);
+
+ switch (code) {
+ case ERTS_DSIG_SEND_OK:
+ erts_set_gc_state(BIF_P, 1);
+ BIF_RET(BIF_ARG_2);
+
+ case ERTS_DSIG_SEND_YIELD:
+ erts_set_gc_state(BIF_P, 1);
+ ERTS_BIF_YIELD_RETURN(BIF_P, BIF_ARG_2);
+
+ case ERTS_DSIG_SEND_CONTINUE: {
+ BUMP_ALL_REDS(BIF_P);
+ BIF_TRAP3(&spawn_request_yield_export, BIF_P,
+ BIF_ARG_1, BIF_ARG_2, BIF_ARG_3);
+ }
+
+ case ERTS_DSIG_SEND_TOO_LRG: {
+ ErtsMonitor *mon;
+ ErtsMonitorData *mdp;
+ Eterm ref;
+
+ erts_set_gc_state(BIF_P, 1);
+
+ if (is_internal_ordinary_ref(BIF_ARG_2))
+ ref = BIF_ARG_2;
+ else {
+ Eterm *tp;
+ ASSERT(is_tuple_arity(BIF_ARG_2, 2));
+ tp = tuple_val(BIF_ARG_2);
+ ref = tp[1];
+ ASSERT(is_internal_ordinary_ref(ref));
+ }
+
+ mon = erts_monitor_tree_lookup(ERTS_P_MONITORS(BIF_P), ref);
+ ASSERT(mon);
+ erts_monitor_tree_delete(&ERTS_P_MONITORS(BIF_P), mon);
+ mdp = erts_monitor_to_data(mon);
+ if (erts_monitor_dist_delete(&mdp->target))
+ erts_monitor_release_both(mdp);
+ else
+ erts_monitor_release(mon);
+
+ erts_send_local_spawn_reply(BIF_P, ERTS_PROC_LOCK_MAIN, NULL,
+ BIF_ARG_3, ref, am_system_limit,
+ am_undefined);
+ BIF_RET(BIF_ARG_2);
+ }
+
+ default:
+ ERTS_INTERNAL_ERROR("Invalid spawn request result");
+ BIF_ERROR(BIF_P, EXC_INTERNAL_ERROR);
+ }
+}
+
+BIF_RETTYPE erts_internal_dist_spawn_request_4(BIF_ALIST_4)
+{
+ BIF_RETTYPE ret_val;
+ Eterm node = BIF_ARG_1;
+ Eterm mfa = BIF_ARG_2;
+ Eterm opts = BIF_ARG_3;
+ Eterm tag = am_spawn_reply;
+ Eterm mod, func, alist, new_opts, error, ref,
+ ok_result;
+ Uint nargs, nopts, rm_opts, rebuild_opts;
+ DistEntry *dep = NULL;
+ Eterm list;
+ ErtsDSigSendContext ctx;
+ int code, link = 0, monitor = 0, success_message, error_message;
+
+ ok_result = THE_NON_VALUE;
+ success_message = error_message = !0;
+
+ if (!is_node_name_atom(node))
+ goto badarg;
+ dep = erts_find_or_insert_dist_entry(node);
+ if (dep == erts_this_dist_entry)
+ goto badarg;
+ if (!is_tuple_arity(mfa, 3))
+ goto badarg;
+ else {
+ Eterm *tp = tuple_val(mfa);
+ mod = tp[1];
+ func = tp[2];
+ alist = tp[3];
+ if (!is_atom(mod))
+ goto badarg;
+ if (!is_atom(func))
+ goto badarg;
+ nargs = 0;
+ list = alist;
+ while (is_list(list)) {
+ Eterm *cp = list_val(list);
+ list = CDR(cp);
+ nargs++;
+ }
+ if (!is_nil(list))
+ goto badarg;
+ }
+
+ new_opts = list = opts;
+ nopts = 0;
+ rm_opts = 0;
+ rebuild_opts = 0;
+
+ while (is_list(list)) {
+ Eterm *cp = list_val(list);
+ Eterm car = CAR(cp);
+ list = CDR(cp);
+ nopts++;
+ switch (car) {
+ case am_link:
+ link = !0;
+ break;
+ case am_monitor:
+ monitor = !0;
+ break;
+ default:
+ if (is_tuple_arity(car, 2)) {
+ Eterm *tp = tuple_val(car);
+ switch (tp[1]) {
+
+ case am_reply_tag:
+ tag = tp[2];
+
+ if (0) {
+ case am_reply:
+ switch (tp[2]) {
+ case am_error_only:
+ success_message = 0;
+ error_message = !0;
+ break;
+ case am_success_only:
+ success_message = !0;
+ error_message = 0;
+ break;
+ case am_no:
+ success_message = 0;
+ error_message = 0;
+ break;
+ case am_yes:
+ success_message = !0;
+ error_message = !0;
+ break;
+ default:
+ goto badarg;
+ }
+ }
+
+ if (BIF_ARG_4 != am_spawn_request)
+ goto badarg;
+
+ rm_opts++;
+ new_opts = list;
+ rebuild_opts = nopts - rm_opts;
+ break;
+
+ default:
+ break;
+ }
+ }
+ break;
+ }
+ }
+ if (!is_nil(list))
+ goto badarg;
+
+ if (rm_opts) {
+ /*
+ * If no 'rebuild_opts', all options to drop were in
+ * the head of the 'opts' list. 'new_opts' now contain
+ * the tail of original option list without the dropped
+ * options.
+ */
+ if (rebuild_opts) {
+#ifdef DEBUG
+ Eterm reusable_tail, *hp_start;
+#endif
+ Uint rm_cnt;
+ Eterm *hp, *prev_cp;
+ /*
+ * Remove 'reply_tag' option in option list.
+ * This options are mixed with other options.
+ *
+ * We build the list backwards and reuse tail
+ * without options to remove, if such exist.
+ */
+
+ hp = HAlloc(BIF_P, 2*rebuild_opts);
+
+#ifdef DEBUG
+ hp_start = hp;
+ reusable_tail = new_opts;
+#endif
+
+ hp += 2*(rebuild_opts - 1);
+ new_opts = make_list(hp);
+ prev_cp = NULL;
+ list = opts;
+ rm_cnt = 0;
+
+ while (is_list(list)) {
+ Eterm *cp = list_val(list);
+ Eterm car = CAR(cp);
+ list = CDR(cp);
+ if (is_tuple_arity(car, 2)) {
+ Eterm *tp = tuple_val(car);
+ if (am_reply_tag == tp[1]
+ || am_reply == tp[1]) {
+ rm_cnt++;
+ /* skip option */
+ if (rm_cnt == rm_opts) {
+ ASSERT(prev_cp);
+ ASSERT(list == reusable_tail);
+ CDR(prev_cp) = list;
+ break; /* last option to skip */
+ }
+ continue;
+ }
+ }
+#ifdef DEBUG
+ rebuild_opts--;
+#endif
+ CAR(hp) = car;
+ prev_cp = hp;
+ hp -= 2;
+ CDR(prev_cp) = make_list(hp);
+ }
+ ASSERT(hp == hp_start - 2);
+ ASSERT(rebuild_opts == 0);
+ }
+
+ opts = new_opts;
+ }
+
+ /* Arguments checked; do it... */
+
+ ref = erts_make_ref(BIF_P);
+ if (BIF_ARG_4 == am_spawn_request)
+ ok_result = ref;
+ else {
+ Eterm *hp = HAlloc(BIF_P, 3);
+ ASSERT(BIF_ARG_4 == am_spawn_opt);
+ ok_result = TUPLE2(hp, ref, monitor ? am_true : am_false);
+ }
+
+ code = erts_dsig_prepare(&ctx, dep,
+ BIF_P, ERTS_PROC_LOCK_MAIN,
+ ERTS_DSP_RLOCK, 0, 0, 1);
+ switch (code) {
+ case ERTS_DSIG_PREP_NOT_ALIVE:
+ case ERTS_DSIG_PREP_NOT_CONNECTED:
+ goto noconnection;
+
+ case ERTS_DSIG_PREP_CONNECTED:
+ if (!(dep->dflags & DFLAG_SPAWN)) {
+ erts_de_runlock(dep);
+ goto notsup;
+ }
+ /* Fall through... */
+ case ERTS_DSIG_PREP_PENDING: {
+ int inserted;
+ ErtsMonitorData *mdp;
+ Eterm nargs_term, mfna, *hp;
+
+ if (IS_USMALL(0, nargs)) {
+ hp = HAlloc(BIF_P, 4);
+ nargs_term = make_small(nargs);
+ }
+ else {
+ hp = HAlloc(BIF_P, 4+BIG_UINT_HEAP_SIZE);
+ nargs_term = uint_to_big(nargs, hp);
+ hp += BIG_UINT_HEAP_SIZE;
+ }
+
+ mfna = TUPLE3(hp, mod, func, nargs_term);
+
+ mdp = erts_monitor_create(ERTS_MON_TYPE_DIST_PROC, ref,
+ BIF_P->common.id, am_pending,
+ tag);
+ if (monitor)
+ mdp->origin.flags |= ERTS_ML_FLG_SPAWN_MONITOR;
+ if (link)
+ mdp->origin.flags |= ERTS_ML_FLG_SPAWN_LINK;
+ if (!success_message)
+ mdp->origin.flags |= ERTS_ML_FLG_SPAWN_NO_SMSG;
+ if (!error_message)
+ mdp->origin.flags |= ERTS_ML_FLG_SPAWN_NO_EMSG;
+
+ erts_monitor_tree_insert(&ERTS_P_MONITORS(BIF_P),
+ &mdp->origin);
+ inserted = erts_monitor_dist_insert(&mdp->target, dep->mld);
+ ASSERT(inserted); (void)inserted;
+
+ erts_de_runlock(dep);
+
+ ctx.reds = (Sint) (ERTS_BIF_REDS_LEFT(BIF_P) * TERM_TO_BINARY_LOOP_FACTOR);
+
+ code = dsig_send_spawn_request(&ctx, ref, BIF_P->common.id,
+ BIF_P->group_leader, mfna,
+ alist, opts);
+ switch (code) {
+ case ERTS_DSIG_SEND_OK:
+ ERTS_BIF_PREP_RET(ret_val, ok_result);
+ break;
+
+ case ERTS_DSIG_SEND_YIELD:
+ ERTS_BIF_PREP_YIELD_RETURN(ret_val, BIF_P, ok_result);
+ break;
+
+ case ERTS_DSIG_SEND_CONTINUE: {
+ Eterm ctx_term;
+ /* Keep dist entry alive over trap... */
+ ctx.deref_dep = 1;
+ dep = NULL;
+
+ erts_set_gc_state(BIF_P, 0);
+ ctx_term = erts_dsend_export_trap_context(BIF_P, &ctx);
+ BUMP_ALL_REDS(BIF_P);
+ ERTS_BIF_PREP_TRAP3(ret_val, &spawn_request_yield_export,
+ BIF_P, ctx_term, ok_result, tag);
+ break;
+ }
+
+ case ERTS_DSIG_SEND_TOO_LRG: {
+ ErtsMonitor *mon;
+ ErtsMonitorData *mdp;
+
+ mon = erts_monitor_tree_lookup(ERTS_P_MONITORS(BIF_P), ref);
+ ASSERT(mon);
+ erts_monitor_tree_delete(&ERTS_P_MONITORS(BIF_P), mon);
+ mdp = erts_monitor_to_data(mon);
+ if (erts_monitor_dist_delete(&mdp->target))
+ erts_monitor_release_both(mdp);
+ else
+ erts_monitor_release(mon);
+
+ goto system_limit;
+ }
+
+ default:
+ ERTS_INTERNAL_ERROR("Invalid spawn request result");
+ ERTS_BIF_PREP_RET(ret_val, am_internal_error);
+ }
+ break;
+ }
+ default:
+ ERTS_INTERNAL_ERROR("Invalid dsig prepare result");
+ ERTS_BIF_PREP_RET(ret_val, am_internal_error);
+ break;
+ }
+
+do_return:
+
+ if (dep)
+ erts_deref_dist_entry(dep);
+
+ return ret_val;
+
+badarg:
+ ERTS_BIF_PREP_RET(ret_val, am_badarg);
+ goto do_return;
+
+system_limit:
+ error = am_system_limit;
+ goto send_error;
+noconnection:
+ error = am_noconnection;
+ goto send_error;
+notsup:
+ error = am_notsup;
+ /* fall through... */
+send_error:
+ ASSERT(is_value(ok_result));
+ if (error_message)
+ erts_send_local_spawn_reply(BIF_P, ERTS_PROC_LOCK_MAIN, NULL,
+ tag, ref, error, am_undefined);
+ ERTS_BIF_PREP_RET(ret_val, ok_result);
+ goto do_return;
+}
+
+
/**********************************************************************/
/* node(Object) -> Node */
@@ -4756,7 +5940,7 @@ BIF_RETTYPE monitor_node_2(BIF_ALIST_2)
BIF_RETTYPE net_kernel_dflag_unicode_io_1(BIF_ALIST_1)
{
DistEntry *de;
- Uint32 f;
+ Uint64 f;
if (is_not_pid(BIF_ARG_1)) {
BIF_ERROR(BIF_P,BADARG);
}
@@ -4766,7 +5950,7 @@ BIF_RETTYPE net_kernel_dflag_unicode_io_1(BIF_ALIST_1)
BIF_RET(am_true);
}
erts_de_rlock(de);
- f = de->flags;
+ f = de->dflags;
erts_de_runlock(de);
BIF_RET(((f & DFLAG_UNICODE_IO) ? am_true : am_false));
}
diff --git a/erts/emulator/beam/dist.h b/erts/emulator/beam/dist.h
index f6cb79472f..65c29caeb3 100644
--- a/erts/emulator/beam/dist.h
+++ b/erts/emulator/beam/dist.h
@@ -25,46 +25,57 @@
#include "erl_node_tables.h"
#include "zlib.h"
-#define DFLAG_PUBLISHED 0x01
-#define DFLAG_ATOM_CACHE 0x02
-#define DFLAG_EXTENDED_REFERENCES 0x04
-#define DFLAG_DIST_MONITOR 0x08
-#define DFLAG_FUN_TAGS 0x10
-#define DFLAG_DIST_MONITOR_NAME 0x20
-#define DFLAG_HIDDEN_ATOM_CACHE 0x40
-#define DFLAG_NEW_FUN_TAGS 0x80
-#define DFLAG_EXTENDED_PIDS_PORTS 0x100
-#define DFLAG_EXPORT_PTR_TAG 0x200
-#define DFLAG_BIT_BINARIES 0x400
-#define DFLAG_NEW_FLOATS 0x800
-#define DFLAG_UNICODE_IO 0x1000
-#define DFLAG_DIST_HDR_ATOM_CACHE 0x2000
-#define DFLAG_SMALL_ATOM_TAGS 0x4000
-#define DFLAG_INTERNAL_TAGS 0x8000 /* used by ETS 'compressed' option */
-#define DFLAG_UTF8_ATOMS 0x10000
-#define DFLAG_MAP_TAG 0x20000
-#define DFLAG_BIG_CREATION 0x40000
-#define DFLAG_SEND_SENDER 0x80000
-#define DFLAG_BIG_SEQTRACE_LABELS 0x100000
-#define DFLAG_NO_MAGIC 0x200000 /* internal for pending connection */
-#define DFLAG_EXIT_PAYLOAD 0x400000
-#define DFLAG_FRAGMENTS 0x800000
+#define DFLAG_PUBLISHED ((Uint64)0x01)
+#define DFLAG_ATOM_CACHE ((Uint64)0x02)
+#define DFLAG_EXTENDED_REFERENCES ((Uint64)0x04)
+#define DFLAG_DIST_MONITOR ((Uint64)0x08)
+#define DFLAG_FUN_TAGS ((Uint64)0x10)
+#define DFLAG_DIST_MONITOR_NAME ((Uint64)0x20)
+#define DFLAG_HIDDEN_ATOM_CACHE ((Uint64)0x40)
+#define DFLAG_NEW_FUN_TAGS ((Uint64)0x80)
+#define DFLAG_EXTENDED_PIDS_PORTS ((Uint64)0x100)
+#define DFLAG_EXPORT_PTR_TAG ((Uint64)0x200)
+#define DFLAG_BIT_BINARIES ((Uint64)0x400)
+#define DFLAG_NEW_FLOATS ((Uint64)0x800)
+#define DFLAG_UNICODE_IO ((Uint64)0x1000)
+#define DFLAG_DIST_HDR_ATOM_CACHE ((Uint64)0x2000)
+#define DFLAG_SMALL_ATOM_TAGS ((Uint64)0x4000)
+#define DFLAG_ETS_COMPRESSED ((Uint64)0x8000) /* internal */
+#define DFLAG_UTF8_ATOMS ((Uint64)0x10000)
+#define DFLAG_MAP_TAG ((Uint64)0x20000)
+#define DFLAG_BIG_CREATION ((Uint64)0x40000)
+#define DFLAG_SEND_SENDER ((Uint64)0x80000)
+#define DFLAG_BIG_SEQTRACE_LABELS ((Uint64)0x100000)
+#define DFLAG_PENDING_CONNECT ((Uint64)0x200000) /* internal */
+#define DFLAG_EXIT_PAYLOAD ((Uint64)0x400000)
+#define DFLAG_FRAGMENTS ((Uint64)0x800000)
+#define DFLAG_HANDSHAKE_23 ((Uint64)0x1000000)
+#define DFLAG_RESERVED 0xfe000000
+/*
+ * As the old handshake only support 32 flag bits, we reserve the remainding
+ * bits in the lower 32 for changes in the handshake protocol or potentially
+ * new capabilities that we also want to backport to OTP-22 or older.
+ */
+#define DFLAG_SPAWN ((Uint64)0x100000000)
+
/* Mandatory flags for distribution */
#define DFLAG_DIST_MANDATORY (DFLAG_EXTENDED_REFERENCES \
| DFLAG_EXTENDED_PIDS_PORTS \
| DFLAG_UTF8_ATOMS \
- | DFLAG_NEW_FUN_TAGS)
+ | DFLAG_NEW_FUN_TAGS \
+ | DFLAG_BIG_CREATION)
/*
* Additional optimistic flags when encoding toward pending connection.
- * If remote node (erl_interface) does not supporting these then we may need
+ * If remote node (erl_interface) does not support these then we may need
* to transcode messages enqueued before connection setup was finished.
*/
#define DFLAG_DIST_HOPEFULLY (DFLAG_EXPORT_PTR_TAG \
| DFLAG_BIT_BINARIES \
| DFLAG_DIST_MONITOR \
- | DFLAG_DIST_MONITOR_NAME)
+ | DFLAG_DIST_MONITOR_NAME \
+ | DFLAG_SPAWN)
/* Our preferred set of flags. Used for connection setup handshake */
#define DFLAG_DIST_DEFAULT (DFLAG_DIST_MANDATORY | DFLAG_DIST_HOPEFULLY \
@@ -75,11 +86,12 @@
| DFLAG_SMALL_ATOM_TAGS \
| DFLAG_UTF8_ATOMS \
| DFLAG_MAP_TAG \
- | DFLAG_BIG_CREATION \
| DFLAG_SEND_SENDER \
| DFLAG_BIG_SEQTRACE_LABELS \
| DFLAG_EXIT_PAYLOAD \
- | DFLAG_FRAGMENTS)
+ | DFLAG_FRAGMENTS \
+ | DFLAG_HANDSHAKE_23 \
+ | DFLAG_SPAWN)
/* Flags addable by local distr implementations */
#define DFLAG_DIST_ADDABLE DFLAG_DIST_DEFAULT
@@ -87,6 +99,7 @@
/* Flags rejectable by local distr implementation */
#define DFLAG_DIST_REJECTABLE (DFLAG_DIST_HDR_ATOM_CACHE \
| DFLAG_HIDDEN_ATOM_CACHE \
+ | DFLAG_FRAGMENTS \
| DFLAG_ATOM_CACHE)
/* Flags for all features needing strict order delivery */
@@ -130,9 +143,17 @@ enum dop {
DOP_PAYLOAD_EXIT_TT = 25,
DOP_PAYLOAD_EXIT2 = 26,
DOP_PAYLOAD_EXIT2_TT = 27,
- DOP_PAYLOAD_MONITOR_P_EXIT = 28
+ DOP_PAYLOAD_MONITOR_P_EXIT = 28,
+
+ DOP_SPAWN_REQUEST = 29,
+ DOP_SPAWN_REQUEST_TT = 30,
+ DOP_SPAWN_REPLY = 31,
+ DOP_SPAWN_REPLY_TT = 32
};
+#define ERTS_DIST_SPAWN_FLAG_LINK (1 << 0)
+#define ERTS_DIST_SPAWN_FLAG_MONITOR (1 << 1)
+
/* distribution trap functions */
extern Export* dmonitor_node_trap;
@@ -144,7 +165,7 @@ typedef enum {
/* Must be larger or equal to 16 */
#ifdef DEBUG
-#define ERTS_DIST_FRAGMENT_SIZE 16
+#define ERTS_DIST_FRAGMENT_SIZE 1024
#else
/* This should be made configurable */
#define ERTS_DIST_FRAGMENT_SIZE (64 * 1024)
@@ -175,6 +196,9 @@ extern int erts_is_alive;
/* dist_ctrl_{g,s}et_option/2 */
#define ERTS_DIST_CTRL_OPT_GET_SIZE ((Uint32) (1 << 0))
+/* for emulator internal testing... */
+extern int erts_dflags_test_remove_hopefull_flags;
+
#ifdef DEBUG
#define ERTS_DBG_CHK_NO_DIST_LNK(D, R, L) \
erts_dbg_chk_no_dist_proc_link((D), (R), (L))
@@ -193,23 +217,75 @@ extern int erts_is_alive;
typedef enum { TTBSize, TTBEncode, TTBCompress } TTBState;
typedef struct TTBSizeContext_ {
- Uint flags;
+ Uint64 dflags;
int level;
+ Sint vlen;
+ int iovec;
+ Uint fragment_size;
+ Uint last_result;
+ Uint extra_size;
Uint result;
Eterm obj;
ErtsWStack wstack;
} TTBSizeContext;
+#define ERTS_INIT_TTBSizeContext(Ctx, Flags) \
+ do { \
+ (Ctx)->wstack.wstart = NULL; \
+ (Ctx)->dflags = (Flags); \
+ (Ctx)->level = 0; \
+ (Ctx)->vlen = -1; \
+ (Ctx)->fragment_size = ~((Uint) 0); \
+ (Ctx)->extra_size = 0; \
+ (Ctx)->last_result = 0; \
+ } while (0)
+
typedef struct TTBEncodeContext_ {
- Uint flags;
- Uint hopefull_flags;
+ Uint64 dflags;
+ Uint64 hopefull_flags;
+ byte *hopefull_flagsp;
int level;
byte* ep;
Eterm obj;
ErtsWStack wstack;
Binary *result_bin;
+ byte *cptr;
+ Sint vlen;
+ Uint size;
+ byte *payload_ixp;
+ byte *hopefull_ixp;
+ SysIOVec* iov;
+ ErlDrvBinary** binv;
+ Eterm *termv;
+ int iovec;
+ Uint fragment_size;
+ Sint frag_ix;
+ ErlIOVec **fragment_eiovs;
+#ifdef DEBUG
+ int debug_fragments;
+ int debug_vlen;
+#endif
} TTBEncodeContext;
+#define ERTS_INIT_TTBEncodeContext(Ctx, Flags) \
+ do { \
+ (Ctx)->wstack.wstart = NULL; \
+ (Ctx)->dflags = (Flags); \
+ (Ctx)->level = 0; \
+ (Ctx)->vlen = 0; \
+ (Ctx)->size = 0; \
+ (Ctx)->termv = NULL; \
+ (Ctx)->iov = NULL; \
+ (Ctx)->binv = NULL; \
+ (Ctx)->fragment_size = ~((Uint) 0); \
+ if ((Flags) & DFLAG_PENDING_CONNECT) { \
+ (Ctx)->hopefull_flags = 0; \
+ (Ctx)->hopefull_flagsp = NULL; \
+ (Ctx)->hopefull_ixp = NULL; \
+ (Ctx)->payload_ixp = NULL; \
+ } \
+ } while (0)
+
typedef struct {
Uint real_size;
Uint dest_len;
@@ -246,7 +322,7 @@ typedef struct erts_dsig_send_context {
Eterm ctl;
Eterm msg;
Eterm from;
- Eterm ctl_heap[6];
+ Eterm ctl_heap[8]; /* 7-tuple (SPAWN_REQUEST_TT) */
Eterm return_term;
DistEntry *dep;
@@ -258,12 +334,13 @@ typedef struct erts_dsig_send_context {
enum erts_dsig_send_phase phase;
Sint reds;
- Uint32 max_finalize_prepend;
Uint data_size, dhdr_ext_size;
+ byte *dhdrp, *extp;
ErtsAtomCacheMap *acmp;
ErtsDistOutputBuf *obuf;
- Uint fragments;
- Uint32 flags;
+ Uint alloced_fragments, fragments;
+ Sint vlen;
+ Uint64 dflags;
Process *c_p;
union {
TTBSizeContext sc;
@@ -305,6 +382,7 @@ 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_spawn_reply(ErtsDSigSendContext *, Eterm, Eterm, Eterm, Eterm, Eterm);
extern int erts_dsig_send(ErtsDSigSendContext *dsdp);
extern int erts_dsend_context_dtor(Binary*);
@@ -333,4 +411,8 @@ extern int erts_dsig_prepare(ErtsDSigSendContext *,
void erts_dist_print_procs_suspended_on_de(fmtfn_t to, void *to_arg);
int erts_auto_connect(DistEntry* dep, Process *proc, ErtsProcLocks proc_locks);
+
+Uint erts_ttb_iov_size(int use_termv, Sint vlen, Uint fragments);
+ErlIOVec **erts_ttb_iov_init(TTBEncodeContext *ctx, int use_termv, char *ptr,
+ Sint vlen, Uint fragments, Uint fragments_size);
#endif
diff --git a/erts/emulator/beam/erl_alloc.c b/erts/emulator/beam/erl_alloc.c
index b9f0334172..1bbc7d7f1e 100644
--- a/erts/emulator/beam/erl_alloc.c
+++ b/erts/emulator/beam/erl_alloc.c
@@ -653,8 +653,6 @@ erts_alloc_init(int *argc, char **argv, ErtsAllocInitOpts *eaiop)
= erts_timer_type_size(ERTS_ALC_T_HL_PTIMER);
fix_type_sizes[ERTS_ALC_FIX_TYPE_IX(ERTS_ALC_T_BIF_TIMER)]
= erts_timer_type_size(ERTS_ALC_T_BIF_TIMER);
- fix_type_sizes[ERTS_ALC_FIX_TYPE_IX(ERTS_ALC_T_NIF_EXP_TRACE)]
- = sizeof(NifExportTrace);
fix_type_sizes[ERTS_ALC_FIX_TYPE_IX(ERTS_ALC_T_MREF_NSCHED_ENT)]
= sizeof(ErtsNSchedMagicRefTableEntry);
fix_type_sizes[ERTS_ALC_FIX_TYPE_IX(ERTS_ALC_T_MINDIRECTION)]
@@ -2392,10 +2390,6 @@ erts_memory(fmtfn_t *print_to_p, void *print_to_arg, void *proc, Eterm earg)
&size.processes_used,
fi,
ERTS_ALC_T_BIF_TIMER);
- add_fix_values(&size.processes,
- &size.processes_used,
- fi,
- ERTS_ALC_T_NIF_EXP_TRACE);
}
if (want.atom || want.atom_used) {
diff --git a/erts/emulator/beam/erl_alloc.types b/erts/emulator/beam/erl_alloc.types
index b40f5d0622..3d14e86445 100644
--- a/erts/emulator/beam/erl_alloc.types
+++ b/erts/emulator/beam/erl_alloc.types
@@ -195,6 +195,8 @@ type DB_DMC_ERR_INFO ETS ETS db_dmc_error_info
type DB_TERM ETS ETS db_term
type DB_PROC_CLEANUP SHORT_LIVED ETS db_proc_cleanup_state
type ETS_ALL_REQ SHORT_LIVED ETS ets_all_request
+type ETS_CTRS ETS ETS ets_decentralized_ctrs
+type ETS_I_LST_TRAP SHORT_LIVED ETS ets_insert_list_bif_trap_state
type LOGGER_DSBUF TEMPORARY SYSTEM logger_dsbuf
type TMP_DSBUF TEMPORARY SYSTEM tmp_dsbuf
type INFO_DSBUF SYSTEM SYSTEM info_dsbuf
@@ -279,6 +281,7 @@ 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 PHASH2_TRAP SHORT_LIVED PROCESSES phash2_trap_state
type ENVIRONMENT SYSTEM SYSTEM environment
@@ -286,6 +289,8 @@ type PERSISTENT_TERM LONG_LIVED CODE persisten_term
type PERSISTENT_LOCK_Q SHORT_LIVED SYSTEM persistent_lock_q
type PERSISTENT_TERM_TMP SHORT_LIVED SYSTEM persistent_term_tmp_table
+type T2B_VEC SHORT_LIVED PROCESSES term_to_binary_vector
+
#
# Types used for special emulators
#
@@ -331,8 +336,7 @@ type DB_HEIR_DATA STANDARD ETS db_heir_data
type DB_MS_PSDO_PROC LONG_LIVED ETS db_match_pseudo_proc
type SCHDLR_DATA LONG_LIVED SYSTEM scheduler_data
-type NIF_TRAP_EXPORT STANDARD PROCESSES nif_trap_export_entry
-type NIF_EXP_TRACE FIXED_SIZE PROCESSES nif_export_trace
+type NFUNC_TRAP_WRAPPER STANDARD PROCESSES nfunc_trap_wrapper
type EXPORT LONG_LIVED CODE export_entry
type MONITOR FIXED_SIZE PROCESSES monitor
type MONITOR_SUSPEND STANDARD PROCESSES monitor_suspend
diff --git a/erts/emulator/beam/erl_alloc_util.c b/erts/emulator/beam/erl_alloc_util.c
index bfc2f5992c..8123f99375 100644
--- a/erts/emulator/beam/erl_alloc_util.c
+++ b/erts/emulator/beam/erl_alloc_util.c
@@ -481,244 +481,265 @@ static void check_blk_carrier(Allctr_t *, Block_t *);
/* Statistics updating ... */
#ifdef DEBUG
-#define DEBUG_CHECK_CARRIER_NO_SZ(AP) \
- ASSERT(((AP)->sbcs.curr.norm.mseg.no \
- && (AP)->sbcs.curr.norm.mseg.size) \
- || (!(AP)->sbcs.curr.norm.mseg.no \
- && !(AP)->sbcs.curr.norm.mseg.size)); \
- ASSERT(((AP)->sbcs.curr.norm.sys_alloc.no \
- && (AP)->sbcs.curr.norm.sys_alloc.size) \
- || (!(AP)->sbcs.curr.norm.sys_alloc.no \
- && !(AP)->sbcs.curr.norm.sys_alloc.size)); \
- ASSERT(((AP)->mbcs.curr.norm.mseg.no \
- && (AP)->mbcs.curr.norm.mseg.size) \
- || (!(AP)->mbcs.curr.norm.mseg.no \
- && !(AP)->mbcs.curr.norm.mseg.size)); \
- ASSERT(((AP)->mbcs.curr.norm.sys_alloc.no \
- && (AP)->mbcs.curr.norm.sys_alloc.size) \
- || (!(AP)->mbcs.curr.norm.sys_alloc.no \
- && !(AP)->mbcs.curr.norm.sys_alloc.size));
+
+#define DEBUG_CHECK_CARRIER_NO_SZ_1(CSTATS) \
+ do { \
+ int ix__; \
+ for (ix__ = ERTS_CRR_ALLOC_MIN; ix__ <= ERTS_CRR_ALLOC_MAX; ix__++) { \
+ UWord no__ = (CSTATS)->carriers[ix__].no; \
+ UWord size__= (CSTATS)->carriers[ix__].size; \
+ ASSERT((no__ > 0 && size__ > 0) || (no__ == 0 && size__ == 0)); \
+ } \
+ } while (0)
+
+#define DEBUG_CHECK_CARRIER_NO_SZ(AP) \
+ do { \
+ DEBUG_CHECK_CARRIER_NO_SZ_1(&(AP)->sbcs); \
+ DEBUG_CHECK_CARRIER_NO_SZ_1(&(AP)->mbcs); \
+ } while (0)
#else
#define DEBUG_CHECK_CARRIER_NO_SZ(AP)
#endif
-#define STAT_SBC_ALLOC(AP, BSZ) \
- (AP)->sbcs.blocks.curr.size += (BSZ); \
- if ((AP)->sbcs.blocks.max.size < (AP)->sbcs.blocks.curr.size) \
- (AP)->sbcs.blocks.max.size = (AP)->sbcs.blocks.curr.size; \
- if ((AP)->sbcs.max.no < ((AP)->sbcs.curr.norm.mseg.no \
- + (AP)->sbcs.curr.norm.sys_alloc.no)) \
- (AP)->sbcs.max.no = ((AP)->sbcs.curr.norm.mseg.no \
- + (AP)->sbcs.curr.norm.sys_alloc.no); \
- if ((AP)->sbcs.max.size < ((AP)->sbcs.curr.norm.mseg.size \
- + (AP)->sbcs.curr.norm.sys_alloc.size)) \
- (AP)->sbcs.max.size = ((AP)->sbcs.curr.norm.mseg.size \
- + (AP)->sbcs.curr.norm.sys_alloc.size)
-
-#define STAT_MSEG_SBC_ALLOC(AP, CSZ, BSZ) \
-do { \
- (AP)->sbcs.curr.norm.mseg.no++; \
- (AP)->sbcs.curr.norm.mseg.size += (CSZ); \
- STAT_SBC_ALLOC((AP), (BSZ)); \
- DEBUG_CHECK_CARRIER_NO_SZ((AP)); \
-} while (0)
-
-#define STAT_SYS_ALLOC_SBC_ALLOC(AP, CSZ, BSZ) \
-do { \
- (AP)->sbcs.curr.norm.sys_alloc.no++; \
- (AP)->sbcs.curr.norm.sys_alloc.size += (CSZ); \
- STAT_SBC_ALLOC((AP), (BSZ)); \
- DEBUG_CHECK_CARRIER_NO_SZ((AP)); \
-} while (0)
-
-
-#define STAT_SBC_FREE(AP, BSZ) \
- ASSERT((AP)->sbcs.blocks.curr.size >= (BSZ)); \
- (AP)->sbcs.blocks.curr.size -= (BSZ)
-
-#define STAT_MSEG_SBC_FREE(AP, CSZ, BSZ) \
-do { \
- ASSERT((AP)->sbcs.curr.norm.mseg.no > 0); \
- (AP)->sbcs.curr.norm.mseg.no--; \
- ASSERT((AP)->sbcs.curr.norm.mseg.size >= (CSZ)); \
- (AP)->sbcs.curr.norm.mseg.size -= (CSZ); \
- STAT_SBC_FREE((AP), (BSZ)); \
- DEBUG_CHECK_CARRIER_NO_SZ((AP)); \
-} while (0)
-
-#define STAT_SYS_ALLOC_SBC_FREE(AP, CSZ, BSZ) \
-do { \
- ASSERT((AP)->sbcs.curr.norm.sys_alloc.no > 0); \
- (AP)->sbcs.curr.norm.sys_alloc.no--; \
- ASSERT((AP)->sbcs.curr.norm.sys_alloc.size >= (CSZ)); \
- (AP)->sbcs.curr.norm.sys_alloc.size -= (CSZ); \
- STAT_SBC_FREE((AP), (BSZ)); \
- DEBUG_CHECK_CARRIER_NO_SZ((AP)); \
-} while (0)
-
-#define STAT_MBC_ALLOC(AP) \
- if ((AP)->mbcs.max.no < ((AP)->mbcs.curr.norm.mseg.no \
- + (AP)->mbcs.curr.norm.sys_alloc.no)) \
- (AP)->mbcs.max.no = ((AP)->mbcs.curr.norm.mseg.no \
- + (AP)->mbcs.curr.norm.sys_alloc.no); \
- if ((AP)->mbcs.max.size < ((AP)->mbcs.curr.norm.mseg.size \
- + (AP)->mbcs.curr.norm.sys_alloc.size)) \
- (AP)->mbcs.max.size = ((AP)->mbcs.curr.norm.mseg.size \
- + (AP)->mbcs.curr.norm.sys_alloc.size)
-
-
-#define STAT_MSEG_MBC_ALLOC(AP, CSZ) \
-do { \
- (AP)->mbcs.curr.norm.mseg.no++; \
- (AP)->mbcs.curr.norm.mseg.size += (CSZ); \
- STAT_MBC_ALLOC((AP)); \
- DEBUG_CHECK_CARRIER_NO_SZ((AP)); \
-} while (0)
-
-#define STAT_SYS_ALLOC_MBC_ALLOC(AP, CSZ) \
-do { \
- (AP)->mbcs.curr.norm.sys_alloc.no++; \
- (AP)->mbcs.curr.norm.sys_alloc.size += (CSZ); \
- STAT_MBC_ALLOC((AP)); \
- DEBUG_CHECK_CARRIER_NO_SZ((AP)); \
-} while (0)
-
-#define STAT_MBC_CPOOL_FETCH(AP, CRR) \
-do { \
- UWord csz__ = CARRIER_SZ((CRR)); \
- if (IS_MSEG_CARRIER((CRR))) \
- STAT_MSEG_MBC_ALLOC((AP), csz__); \
- else \
- STAT_SYS_ALLOC_MBC_ALLOC((AP), csz__); \
- set_new_allctr_abandon_limit(AP); \
- (AP)->mbcs.blocks.curr.no += (CRR)->cpool.blocks[(AP)->alloc_no]; \
- if ((AP)->mbcs.blocks.max.no < (AP)->mbcs.blocks.curr.no) \
- (AP)->mbcs.blocks.max.no = (AP)->mbcs.blocks.curr.no; \
- (AP)->mbcs.blocks.curr.size += \
- (CRR)->cpool.blocks_size[(AP)->alloc_no]; \
- if ((AP)->mbcs.blocks.max.size < (AP)->mbcs.blocks.curr.size) \
- (AP)->mbcs.blocks.max.size = (AP)->mbcs.blocks.curr.size; \
-} while (0)
-
-#define STAT_MSEG_MBC_FREE(AP, CSZ) \
-do { \
- ASSERT((AP)->mbcs.curr.norm.mseg.no > 0); \
- (AP)->mbcs.curr.norm.mseg.no--; \
- ASSERT((AP)->mbcs.curr.norm.mseg.size >= (CSZ)); \
- (AP)->mbcs.curr.norm.mseg.size -= (CSZ); \
- DEBUG_CHECK_CARRIER_NO_SZ((AP)); \
-} while (0)
-
-#define STAT_SYS_ALLOC_MBC_FREE(AP, CSZ) \
-do { \
- ASSERT((AP)->mbcs.curr.norm.sys_alloc.no > 0); \
- (AP)->mbcs.curr.norm.sys_alloc.no--; \
- ASSERT((AP)->mbcs.curr.norm.sys_alloc.size >= (CSZ)); \
- (AP)->mbcs.curr.norm.sys_alloc.size -= (CSZ); \
- DEBUG_CHECK_CARRIER_NO_SZ((AP)); \
-} while (0)
-
-#define STAT_MBC_FREE(AP, CRR) \
-do { \
- UWord csz__ = CARRIER_SZ((CRR)); \
- if (IS_MSEG_CARRIER((CRR))) { \
- STAT_MSEG_MBC_FREE((AP), csz__); \
- } else { \
- STAT_SYS_ALLOC_MBC_FREE((AP), csz__); \
- } \
- set_new_allctr_abandon_limit(AP); \
-} while (0)
-
-#define STAT_MBC_ABANDON(AP, CRR) \
-do { \
- STAT_MBC_FREE(AP, CRR); \
- ERTS_ALC_CPOOL_ASSERT((AP)->mbcs.blocks.curr.no \
- >= (CRR)->cpool.blocks[(AP)->alloc_no]); \
- (AP)->mbcs.blocks.curr.no -= (CRR)->cpool.blocks[(AP)->alloc_no]; \
- ERTS_ALC_CPOOL_ASSERT((AP)->mbcs.blocks.curr.size \
- >= (CRR)->cpool.blocks_size[(AP)->alloc_no]); \
- (AP)->mbcs.blocks.curr.size -= (CRR)->cpool.blocks_size[(AP)->alloc_no]; \
-} while (0)
-
-#define STAT_MBC_BLK_ALLOC_CRR(AP, CRR, BSZ) \
-do { \
- (CRR)->cpool.blocks[(AP)->alloc_no]++; \
- (CRR)->cpool.blocks_size[(AP)->alloc_no] += (BSZ); \
- (CRR)->cpool.total_blocks_size += (BSZ); \
-} while (0)
-
-#define STAT_MBC_BLK_ALLOC(AP, CRR, BSZ, FLGS) \
-do { \
- CarriersStats_t *cstats__ = &(AP)->mbcs; \
- cstats__->blocks.curr.no++; \
- if (cstats__->blocks.max.no < cstats__->blocks.curr.no) \
- cstats__->blocks.max.no = cstats__->blocks.curr.no; \
- cstats__->blocks.curr.size += (BSZ); \
- if (cstats__->blocks.max.size < cstats__->blocks.curr.size) \
- cstats__->blocks.max.size = cstats__->blocks.curr.size; \
- STAT_MBC_BLK_ALLOC_CRR((AP), (CRR), (BSZ)); \
-} while (0)
+/* Carrier statistics */
+
+#define STAT_CRR_UPDATED_1(CSTATS) \
+ do { \
+ UWord no_sum__, size_sum__; \
+ int i__; \
+ no_sum__ = size_sum__ = 0; \
+ for (i__ = ERTS_CRR_ALLOC_MIN; i__ <= ERTS_CRR_ALLOC_MAX; i__++) { \
+ ASSERT(ERTS_UWORD_MAX - no_sum__ > (CSTATS)->carriers[i__].no); \
+ ASSERT(ERTS_UWORD_MAX - size_sum__ > (CSTATS)->carriers[i__].size); \
+ no_sum__ += (CSTATS)->carriers[i__].no; \
+ size_sum__ += (CSTATS)->carriers[i__].size; \
+ } \
+ if ((CSTATS)->max.no < no_sum__) { \
+ (CSTATS)->max.no = no_sum__; \
+ } \
+ if ((CSTATS)->max.size < size_sum__) { \
+ (CSTATS)->max.size = size_sum__; \
+ } \
+ } while (0)
+
+#define STAT_CRR_ALLOC_1(TYPE, CSTATS, SIZE) \
+ do { \
+ ASSERT(ERTS_UWORD_MAX - (CSTATS)->carriers[(TYPE)].no > 1); \
+ ASSERT(ERTS_UWORD_MAX - (CSTATS)->carriers[(TYPE)].size > (SIZE)); \
+ (CSTATS)->carriers[(TYPE)].no += 1; \
+ (CSTATS)->carriers[(TYPE)].size += (SIZE); \
+ STAT_CRR_UPDATED_1(CSTATS); \
+ } while (0)
+
+#define STAT_CRR_FREE_1(TYPE, CSTATS, SIZE) \
+ do { \
+ ASSERT((CSTATS)->carriers[(TYPE)].no >= 1); \
+ ASSERT((CSTATS)->carriers[(TYPE)].size >= (SIZE)); \
+ (CSTATS)->carriers[(TYPE)].no -= 1; \
+ (CSTATS)->carriers[(TYPE)].size -= (SIZE); \
+ STAT_CRR_UPDATED_1(CSTATS); \
+ } while (0)
+
+#define STAT_MSEG_SBC_ALLOC(AP, CSZ, BSZ) \
+ do { \
+ CarriersStats_t *cstats__ = &(AP)->sbcs; \
+ STAT_CRR_ALLOC_1(ERTS_CRR_ALLOC_MSEG, cstats__, CSZ); \
+ STAT_BLK_ALLOC_1(cstats__, (AP)->alloc_no, 1, (BSZ)); \
+ DEBUG_CHECK_CARRIER_NO_SZ((AP)); \
+ } while (0)
+
+#define STAT_SYS_ALLOC_SBC_ALLOC(AP, CSZ, BSZ) \
+ do { \
+ CarriersStats_t *cstats__ = &(AP)->sbcs; \
+ STAT_CRR_ALLOC_1(ERTS_CRR_ALLOC_SYS, cstats__, CSZ); \
+ STAT_BLK_ALLOC_1(cstats__, (AP)->alloc_no, 1, (BSZ)); \
+ DEBUG_CHECK_CARRIER_NO_SZ((AP)); \
+ } while (0)
+
+#define STAT_MSEG_SBC_FREE(AP, CSZ, BSZ) \
+ do { \
+ CarriersStats_t *cstats__ = &(AP)->sbcs; \
+ STAT_CRR_FREE_1(ERTS_CRR_ALLOC_MSEG, cstats__, CSZ); \
+ STAT_BLK_FREE_1(cstats__, (AP)->alloc_no, 1, (BSZ)); \
+ DEBUG_CHECK_CARRIER_NO_SZ((AP)); \
+ } while (0)
+
+#define STAT_SYS_ALLOC_SBC_FREE(AP, CSZ, BSZ) \
+ do { \
+ CarriersStats_t *cstats__ = &(AP)->sbcs; \
+ STAT_CRR_FREE_1(ERTS_CRR_ALLOC_SYS, cstats__, CSZ); \
+ STAT_BLK_FREE_1(cstats__, (AP)->alloc_no, 1, (BSZ)); \
+ DEBUG_CHECK_CARRIER_NO_SZ((AP)); \
+ } while (0)
+
+#define STAT_MSEG_MBC_ALLOC(AP, CSZ) \
+ do { \
+ CarriersStats_t *cstats__ = &(AP)->mbcs; \
+ STAT_CRR_ALLOC_1(ERTS_CRR_ALLOC_MSEG, cstats__, CSZ); \
+ DEBUG_CHECK_CARRIER_NO_SZ((AP)); \
+ } while (0)
+
+#define STAT_SYS_ALLOC_MBC_ALLOC(AP, CSZ) \
+ do { \
+ CarriersStats_t *cstats__ = &(AP)->mbcs; \
+ STAT_CRR_ALLOC_1(ERTS_CRR_ALLOC_SYS, cstats__, CSZ); \
+ DEBUG_CHECK_CARRIER_NO_SZ((AP)); \
+ } while (0)
+
+#define STAT_MSEG_MBC_FREE(AP, CSZ) \
+ do { \
+ CarriersStats_t *cstats__ = &(AP)->mbcs; \
+ STAT_CRR_FREE_1(ERTS_CRR_ALLOC_MSEG, cstats__, CSZ); \
+ DEBUG_CHECK_CARRIER_NO_SZ((AP)); \
+ } while (0)
+
+#define STAT_SYS_ALLOC_MBC_FREE(AP, CSZ) \
+ do { \
+ CarriersStats_t *cstats__ = &(AP)->mbcs; \
+ STAT_CRR_FREE_1(ERTS_CRR_ALLOC_SYS, cstats__, CSZ); \
+ DEBUG_CHECK_CARRIER_NO_SZ((AP)); \
+ } while (0)
+
+#define STAT_MBC_FREE(AP, CRR) \
+ do { \
+ UWord csz__ = CARRIER_SZ((CRR)); \
+ if (IS_MSEG_CARRIER((CRR))) { \
+ STAT_MSEG_MBC_FREE((AP), csz__); \
+ } else { \
+ STAT_SYS_ALLOC_MBC_FREE((AP), csz__); \
+ } \
+ set_new_allctr_abandon_limit(AP); \
+ } while (0)
+
+/* Block statistics */
+
+#define STAT_BLK_UPDATED_1(BSTATS) \
+ do { \
+ if ((BSTATS)->max.no < (BSTATS)->curr.no) { \
+ (BSTATS)->max.no = (BSTATS)->curr.no; \
+ } \
+ if ((BSTATS)->max.size < (BSTATS)->curr.size) { \
+ (BSTATS)->max.size = (BSTATS)->curr.size; \
+ } \
+ } while (0)
+
+#define STAT_BLK_ALLOC_1(CSTATS, ALLOC_NO, COUNT, SIZE) \
+ do { \
+ BlockStats_t *bstats__ = \
+ &(CSTATS)->blocks[(ALLOC_NO) - ERTS_ALC_A_MIN]; \
+ ASSERT(ERTS_UWORD_MAX - bstats__->curr.no > (COUNT)); \
+ ASSERT(ERTS_UWORD_MAX - bstats__->curr.size > (SIZE)); \
+ bstats__->curr.no += (COUNT); \
+ bstats__->curr.size += (SIZE); \
+ STAT_BLK_UPDATED_1(bstats__); \
+ } while (0)
+
+#define STAT_BLK_FREE_1(CSTATS, ALLOC_NO, COUNT, SIZE) \
+ do { \
+ BlockStats_t *bstats__ = \
+ &(CSTATS)->blocks[(ALLOC_NO) - ERTS_ALC_A_MIN]; \
+ ASSERT(bstats__->curr.no >= (COUNT)); \
+ ASSERT(bstats__->curr.size >= (SIZE)); \
+ bstats__->curr.no -= (COUNT); \
+ bstats__->curr.size -= (SIZE); \
+ STAT_BLK_UPDATED_1(bstats__); \
+ } while (0)
+
+#define STAT_MBC_CPOOL_FETCH(AP, CRR) \
+ do { \
+ CarriersStats_t *cstats__ = &(AP)->mbcs; \
+ UWord csz__ = CARRIER_SZ((CRR)); \
+ int alloc_no__; \
+ if (IS_MSEG_CARRIER((CRR))) { \
+ STAT_MSEG_MBC_ALLOC((AP), csz__); \
+ } else { \
+ STAT_SYS_ALLOC_MBC_ALLOC((AP), csz__); \
+ } \
+ set_new_allctr_abandon_limit(AP); \
+ for (alloc_no__ = ERTS_ALC_A_MIN; \
+ alloc_no__ <= ERTS_ALC_A_MAX; \
+ alloc_no__++) { \
+ int ix__ = alloc_no__ - ERTS_ALC_A_MIN; \
+ UWord count = (CRR)->cpool.blocks[ix__]; \
+ UWord size = (CRR)->cpool.blocks_size[ix__]; \
+ STAT_BLK_ALLOC_1(cstats__, alloc_no__, count, size); \
+ } \
+ } while (0)
+
+#define STAT_MBC_CPOOL_ABANDON(AP, CRR) \
+ do { \
+ CarriersStats_t *cstats__ = &(AP)->mbcs; \
+ int alloc_no__; \
+ STAT_MBC_FREE(AP, CRR); \
+ for (alloc_no__ = ERTS_ALC_A_MIN; \
+ alloc_no__ <= ERTS_ALC_A_MAX; \
+ alloc_no__++) { \
+ int ix__ = alloc_no__ - ERTS_ALC_A_MIN; \
+ UWord count = (CRR)->cpool.blocks[ix__]; \
+ UWord size = (CRR)->cpool.blocks_size[ix__]; \
+ STAT_BLK_FREE_1(cstats__, alloc_no__, count, size); \
+ } \
+ } while (0)
-static ERTS_INLINE int
+static ERTS_INLINE void
stat_cpool_mbc_blk_free(Allctr_t *allctr,
- ErtsAlcType_t type,
- Carrier_t *crr,
- Carrier_t **busy_pcrr_pp,
- UWord blksz)
+ int alloc_no,
+ Carrier_t *crr,
+ Carrier_t **busy_pcrr_pp,
+ UWord blksz)
{
- Allctr_t *orig_allctr;
- int alloc_no;
-
- alloc_no = ERTS_ALC_T2A(type);
-
- ERTS_ALC_CPOOL_ASSERT(crr->cpool.blocks[alloc_no] > 0);
- crr->cpool.blocks[alloc_no]--;
- ERTS_ALC_CPOOL_ASSERT(crr->cpool.blocks_size[alloc_no] >= blksz);
- crr->cpool.blocks_size[alloc_no] -= blksz;
- ERTS_ALC_CPOOL_ASSERT(crr->cpool.total_blocks_size >= blksz);
- crr->cpool.total_blocks_size -= blksz;
-
- if (allctr->alloc_no == alloc_no && (!busy_pcrr_pp || !*busy_pcrr_pp)) {
+ if (!busy_pcrr_pp || !*busy_pcrr_pp) {
/* This is a local block, so we should not update the pool
* statistics. */
- return 0;
- }
-
- /* This is either a foreign block that's been fetched from the pool, or any
- * block that's in the pool. The carrier's owner keeps the statistics for
- * both pooled and foreign blocks. */
-
- orig_allctr = crr->cpool.orig_allctr;
+ CarriersStats_t *cstats__ = &allctr->mbcs;
+ STAT_BLK_FREE_1(cstats__, alloc_no, 1, blksz);
+ } else {
+ Allctr_t *orig_allctr = crr->cpool.orig_allctr;
+ int ix = alloc_no - ERTS_ALC_A_MIN;
- ERTS_ALC_CPOOL_ASSERT(alloc_no != allctr->alloc_no ||
- (crr == *busy_pcrr_pp && allctr == orig_allctr));
+ ERTS_ALC_CPOOL_ASSERT(crr == *busy_pcrr_pp && allctr == orig_allctr);
#ifdef ERTS_ALC_CPOOL_DEBUG
- ERTS_ALC_CPOOL_ASSERT(
- erts_atomic_dec_read_nob(&orig_allctr->cpool.stat.no_blocks[alloc_no]) >= 0);
- ERTS_ALC_CPOOL_ASSERT(
- erts_atomic_add_read_nob(&orig_allctr->cpool.stat.blocks_size[alloc_no],
- -((erts_aint_t) blksz)) >= 0);
+ ERTS_ALC_CPOOL_ASSERT(
+ erts_atomic_dec_read_nob(
+ &orig_allctr->cpool.stat.no_blocks[ix]) >= 0);
+ ERTS_ALC_CPOOL_ASSERT(
+ erts_atomic_add_read_nob(&orig_allctr->cpool.stat.blocks_size[ix],
+ -((erts_aint_t) blksz)) >= 0);
#else
- erts_atomic_dec_nob(&orig_allctr->cpool.stat.no_blocks[alloc_no]);
- erts_atomic_add_nob(&orig_allctr->cpool.stat.blocks_size[alloc_no],
- -((erts_aint_t) blksz));
+ erts_atomic_dec_nob(&orig_allctr->cpool.stat.no_blocks[ix]);
+ erts_atomic_add_nob(&orig_allctr->cpool.stat.blocks_size[ix],
+ -((erts_aint_t) blksz));
#endif
-
- return 1;
+ }
}
-#define STAT_MBC_BLK_FREE(AP, TYPE, CRR, BPCRRPP, BSZ, FLGS) \
-do { \
- if (!stat_cpool_mbc_blk_free((AP), (TYPE), (CRR), (BPCRRPP), (BSZ))) { \
- CarriersStats_t *cstats__ = &(AP)->mbcs; \
- ASSERT(cstats__->blocks.curr.no > 0); \
- cstats__->blocks.curr.no--; \
- ASSERT(cstats__->blocks.curr.size >= (BSZ)); \
- cstats__->blocks.curr.size -= (BSZ); \
- } \
-} while (0)
+#define STAT_MBC_BLK_ALLOC(AP, CRR, BSZ) \
+ do { \
+ int alloc_no__ = (AP)->alloc_no; \
+ int ix__ = alloc_no__ - ERTS_ALC_A_MIN; \
+ ASSERT(ERTS_UWORD_MAX - (CRR)->cpool.blocks[ix__] > 1); \
+ ASSERT(ERTS_UWORD_MAX - (CRR)->cpool.blocks_size[ix__] > (BSZ)); \
+ ASSERT(ERTS_UWORD_MAX - (CRR)->cpool.total_blocks_size > (BSZ)); \
+ (CRR)->cpool.blocks[ix__] += 1; \
+ (CRR)->cpool.blocks_size[ix__] += (BSZ); \
+ (CRR)->cpool.total_blocks_size += (BSZ); \
+ STAT_BLK_ALLOC_1(&(AP)->mbcs, alloc_no__, 1, (BSZ)); \
+ } while (0)
+
+#define STAT_MBC_BLK_FREE(AP, TYPE, CRR, BPCRRPP, BSZ) \
+ do { \
+ int alloc_no__ = ERTS_ALC_T2A(TYPE); \
+ int ix__ = (alloc_no__) - ERTS_ALC_A_MIN; \
+ ASSERT((CRR)->cpool.blocks[ix__] >= 1); \
+ ASSERT((CRR)->cpool.blocks_size[ix__] >= (BSZ)); \
+ ASSERT((CRR)->cpool.total_blocks_size >= (BSZ)); \
+ (CRR)->cpool.blocks[ix__] -= 1; \
+ (CRR)->cpool.blocks_size[ix__] -= (BSZ); \
+ (CRR)->cpool.total_blocks_size -= (BSZ); \
+ stat_cpool_mbc_blk_free((AP), alloc_no__, (CRR), (BPCRRPP), (BSZ)); \
+ } while (0)
/* Debug stuff... */
#ifdef DEBUG
@@ -1211,9 +1232,14 @@ static Uint
get_next_mbc_size(Allctr_t *allctr)
{
Uint size;
- int cs = (allctr->mbcs.curr.norm.mseg.no
- + allctr->mbcs.curr.norm.sys_alloc.no
- - (allctr->main_carrier ? 1 : 0));
+ int cs;
+ int i;
+
+ cs = 0;
+ for (i = ERTS_CRR_ALLOC_MIN; i <= ERTS_CRR_ALLOC_MAX; i++) {
+ cs += allctr->mbcs.carriers[i].no;
+ }
+ cs -= (allctr->main_carrier ? 1 : 0);
ASSERT(cs >= 0);
ASSERT(allctr->largest_mbc_size >= allctr->smallest_mbc_size);
@@ -2285,8 +2311,14 @@ check_abandon_carrier(Allctr_t *allctr, Block_t *fblk, Carrier_t **busy_pcrr_pp)
if (allctr->cpool.disable_abandon)
return;
- if (allctr->mbcs.blocks.curr.size > allctr->cpool.abandon_limit)
- return;
+ {
+ int ix = allctr->alloc_no - ERTS_ALC_A_MIN;
+
+ /* We only consider the current allocation type; . */
+ if (allctr->mbcs.blocks[ix].curr.size > allctr->cpool.abandon_limit) {
+ return;
+ }
+ }
ncrr_in_pool = erts_atomic_read_nob(&allctr->cpool.stat.no_carriers);
if (ncrr_in_pool >= allctr->cpool.in_pool_limit)
@@ -2515,7 +2547,7 @@ mbc_alloc_finalize(Allctr_t *allctr,
}
ERTS_ALC_CPOOL_ALLOC_OP(allctr);
- STAT_MBC_BLK_ALLOC(allctr, crr, blk_sz, alcu_flgs);
+ STAT_MBC_BLK_ALLOC(allctr, crr, blk_sz);
ASSERT(IS_ALLOCED_BLK(blk));
ASSERT(blk_sz == MBC_BLK_SZ(blk));
@@ -2734,7 +2766,7 @@ mbc_free(Allctr_t *allctr, ErtsAlcType_t type, void *p, Carrier_t **busy_pcrr_pp
ERTS_ALC_CPOOL_FREE_OP(allctr);
- STAT_MBC_BLK_FREE(allctr, type, crr, busy_pcrr_pp, blk_sz, alcu_flgs);
+ STAT_MBC_BLK_FREE(allctr, type, crr, busy_pcrr_pp, blk_sz);
is_first_blk = IS_MBC_FIRST_ABLK(allctr, blk);
is_last_blk = IS_LAST_BLK(blk);
@@ -2934,8 +2966,8 @@ mbc_realloc(Allctr_t *allctr, ErtsAlcType_t type, void *p, Uint size,
crr = ABLK_TO_MBC(blk);
ERTS_ALC_CPOOL_REALLOC_OP(allctr);
- STAT_MBC_BLK_FREE(allctr, type, crr, NULL, old_blk_sz, alcu_flgs);
- STAT_MBC_BLK_ALLOC(allctr, crr, blk_sz, alcu_flgs);
+ STAT_MBC_BLK_FREE(allctr, type, crr, NULL, old_blk_sz);
+ STAT_MBC_BLK_ALLOC(allctr, crr, blk_sz);
ASSERT(MBC_BLK_SZ(blk) >= allctr->min_block_size);
@@ -3038,8 +3070,8 @@ mbc_realloc(Allctr_t *allctr, ErtsAlcType_t type, void *p, Uint size,
}
ERTS_ALC_CPOOL_REALLOC_OP(allctr);
- STAT_MBC_BLK_FREE(allctr, type, crr, NULL, old_blk_sz, alcu_flgs);
- STAT_MBC_BLK_ALLOC(allctr, crr, blk_sz, alcu_flgs);
+ STAT_MBC_BLK_FREE(allctr, type, crr, NULL, old_blk_sz);
+ STAT_MBC_BLK_ALLOC(allctr, crr, blk_sz);
ASSERT(IS_ALLOCED_BLK(blk));
ASSERT(blk_sz == MBC_BLK_SZ(blk));
@@ -3186,7 +3218,7 @@ mbc_realloc(Allctr_t *allctr, ErtsAlcType_t type, void *p, Uint size,
0);
ERTS_ALC_CPOOL_FREE_OP(allctr);
- STAT_MBC_BLK_FREE(allctr, type, crr, NULL, old_blk_sz, alcu_flgs);
+ STAT_MBC_BLK_FREE(allctr, type, crr, NULL, old_blk_sz);
return new_p;
}
@@ -3357,27 +3389,17 @@ cpool_insert(Allctr_t *allctr, Carrier_t *crr)
erts_aint_t val;
ErtsAlcCPoolData_t *sentinel = allctr->cpool.sentinel;
Allctr_t *orig_allctr = crr->cpool.orig_allctr;
+ int ix;
ERTS_ALC_CPOOL_ASSERT(allctr->alloc_no == ERTS_ALC_A_TEST /* testcase */
|| erts_thr_progress_is_managed_thread());
- {
- int alloc_no = allctr->alloc_no;
-
- ERTS_ALC_CPOOL_ASSERT(
- erts_atomic_read_nob(&orig_allctr->cpool.stat.blocks_size[alloc_no]) >= 0 &&
- crr->cpool.blocks_size[alloc_no] >= 0);
-
- ERTS_ALC_CPOOL_ASSERT(
- erts_atomic_read_nob(&orig_allctr->cpool.stat.no_blocks[alloc_no]) >= 0 &&
- crr->cpool.blocks[alloc_no] >= 0);
-
- /* We only modify the counter for our current type since the others are
- * conceptually still in the pool. */
- erts_atomic_add_nob(&orig_allctr->cpool.stat.blocks_size[alloc_no],
- ((erts_aint_t) crr->cpool.blocks_size[alloc_no]));
- erts_atomic_add_nob(&orig_allctr->cpool.stat.no_blocks[alloc_no],
- ((erts_aint_t) crr->cpool.blocks[alloc_no]));
+ /* Add the carrier's block statistics to the pool. */
+ for (ix = 0; ix < ERTS_ALC_A_COUNT; ix++) {
+ erts_atomic_add_nob(&orig_allctr->cpool.stat.blocks_size[ix],
+ ((erts_aint_t) crr->cpool.blocks_size[ix]));
+ erts_atomic_add_nob(&orig_allctr->cpool.stat.no_blocks[ix],
+ ((erts_aint_t) crr->cpool.blocks[ix]));
}
erts_atomic_add_nob(&orig_allctr->cpool.stat.carriers_size,
@@ -3454,11 +3476,15 @@ cpool_delete(Allctr_t *allctr, Allctr_t *prev_allctr, Carrier_t *crr)
#ifdef ERTS_ALC_CPOOL_DEBUG
ErtsAlcCPoolData_t *sentinel = allctr->cpool.sentinel;
#endif
+ Allctr_t *orig_allctr = crr->cpool.orig_allctr;
+ int ix;
ERTS_ALC_CPOOL_ASSERT(allctr->alloc_no == ERTS_ALC_A_TEST /* testcase */
|| erts_thr_progress_is_managed_thread());
ERTS_ALC_CPOOL_ASSERT(sentinel != &crr->cpool);
+ ERTS_ALC_CPOOL_ASSERT(orig_allctr == prev_allctr);
+
/* Set mod marker on next ptr of our predecessor */
val = (erts_aint_t) &crr->cpool;
@@ -3531,30 +3557,31 @@ cpool_delete(Allctr_t *allctr, Allctr_t *prev_allctr, Carrier_t *crr)
crr->cpool.thr_prgr = erts_thr_progress_later(NULL);
- {
- Allctr_t *orig_allctr = crr->cpool.orig_allctr;
- int alloc_no = allctr->alloc_no;
-
- ERTS_ALC_CPOOL_ASSERT(orig_allctr == prev_allctr);
-
- ERTS_ALC_CPOOL_ASSERT(crr->cpool.blocks_size[alloc_no] <=
- erts_atomic_read_nob(&orig_allctr->cpool.stat.blocks_size[alloc_no]));
-
- ERTS_ALC_CPOOL_ASSERT(crr->cpool.blocks[alloc_no] <=
- erts_atomic_read_nob(&orig_allctr->cpool.stat.no_blocks[alloc_no]));
+ /* Subtract the carrier's block statistics from the pool. */
+ for (ix = 0; ix < ERTS_ALC_A_COUNT; ix++) {
+#ifdef ERTS_ALC_CPOOL_DEBUG
+ SWord new_blk_sz, new_blk_no;
- /* We only modify the counters for our current type since the others
- * were, conceptually, never taken out of the pool. */
- erts_atomic_add_nob(&orig_allctr->cpool.stat.blocks_size[alloc_no],
- -((erts_aint_t) crr->cpool.blocks_size[alloc_no]));
- erts_atomic_add_nob(&orig_allctr->cpool.stat.no_blocks[alloc_no],
- -((erts_aint_t) crr->cpool.blocks[alloc_no]));
+ new_blk_sz =
+ erts_atomic_add_read_nob(&orig_allctr->cpool.stat.blocks_size[ix],
+ -((erts_aint_t)crr->cpool.blocks_size[ix]));
+ new_blk_no =
+ erts_atomic_add_read_nob(&orig_allctr->cpool.stat.no_blocks[ix],
+ -((erts_aint_t)crr->cpool.blocks[ix]));
- erts_atomic_add_nob(&orig_allctr->cpool.stat.carriers_size,
- -((erts_aint_t) CARRIER_SZ(crr)));
- erts_atomic_dec_wb(&orig_allctr->cpool.stat.no_carriers);
+ ERTS_ALC_CPOOL_ASSERT(new_blk_sz >= 0);
+ ERTS_ALC_CPOOL_ASSERT(new_blk_no >= 0);
+#else
+ erts_atomic_add_nob(&orig_allctr->cpool.stat.blocks_size[ix],
+ -((erts_aint_t) crr->cpool.blocks_size[ix]));
+ erts_atomic_add_nob(&orig_allctr->cpool.stat.no_blocks[ix],
+ -((erts_aint_t) crr->cpool.blocks[ix]));
+#endif
}
+ erts_atomic_add_nob(&orig_allctr->cpool.stat.carriers_size,
+ -((erts_aint_t) CARRIER_SZ(crr)));
+ erts_atomic_dec_wb(&orig_allctr->cpool.stat.no_carriers);
}
static Carrier_t *
@@ -3937,9 +3964,12 @@ allctr_abandon_limit(Allctr_t *allctr)
{
UWord limit;
UWord csz;
+ int i;
- csz = allctr->mbcs.curr.norm.mseg.size;
- csz += allctr->mbcs.curr.norm.sys_alloc.size;
+ csz = 0;
+ for (i = ERTS_CRR_ALLOC_MIN; i <= ERTS_CRR_ALLOC_MAX; i++) {
+ csz += allctr->mbcs.carriers[i].size;
+ }
limit = csz*allctr->cpool.util_limit;
if (limit > csz)
@@ -3961,7 +3991,7 @@ abandon_carrier(Allctr_t *allctr, Carrier_t *crr)
{
erts_aint_t iallctr;
- STAT_MBC_ABANDON(allctr, crr);
+ STAT_MBC_CPOOL_ABANDON(allctr, crr);
unlink_carrier(&allctr->mbc_list, crr);
allctr->remove_mbc(allctr, crr);
@@ -4023,9 +4053,12 @@ static void
cpool_read_stat(Allctr_t *allctr, int alloc_no,
UWord *nocp, UWord *cszp, UWord *nobp, UWord *bszp)
{
+ int block_ix;
int i;
UWord noc = 0, csz = 0, nob = 0, bsz = 0;
+ block_ix = alloc_no - ERTS_ALC_A_MIN;
+
/*
* We try to get consistent values, but after
* ERTS_ALC_CPOOL_MAX_FAILED_STAT_READS failed
@@ -4041,10 +4074,10 @@ cpool_read_stat(Allctr_t *allctr, int alloc_no,
? erts_atomic_read_nob(&allctr->cpool.stat.carriers_size)
: 0);
tnob = (UWord) (nobp
- ? erts_atomic_read_nob(&allctr->cpool.stat.no_blocks[alloc_no])
+ ? erts_atomic_read_nob(&allctr->cpool.stat.no_blocks[block_ix])
: 0);
tbsz = (UWord) (bszp
- ? erts_atomic_read_nob(&allctr->cpool.stat.blocks_size[alloc_no])
+ ? erts_atomic_read_nob(&allctr->cpool.stat.blocks_size[block_ix])
: 0);
if (tnoc == noc && tcsz == csz && tnob == nob && tbsz == bsz)
break;
@@ -4192,12 +4225,12 @@ create_carrier(Allctr_t *allctr, Uint umem_sz, UWord flags)
if (erts_mseg_no(&allctr->mseg_opt) >= max_mseg_carriers)
goto try_sys_alloc;
if (flags & CFLG_SBC) {
- if (allctr->sbcs.curr.norm.mseg.no >= allctr->max_mseg_sbcs)
+ if (allctr->sbcs.carriers[ERTS_CRR_ALLOC_MSEG].no >= allctr->max_mseg_sbcs)
goto try_sys_alloc;
}
#if !ERTS_SUPER_ALIGNED_MSEG_ONLY
else {
- if (allctr->mbcs.curr.norm.mseg.no >= allctr->max_mseg_mbcs)
+ if (allctr->mbcs.carriers[ERTS_CRR_ALLOC_MSEG].no >= allctr->max_mseg_mbcs)
goto try_sys_alloc;
}
#endif
@@ -4638,10 +4671,10 @@ static struct {
Eterm mseg_alloc_carriers;
#endif
Eterm carriers;
- Eterm blocks_size;
- Eterm blocks;
- Eterm foreign_blocks;
+ Eterm blocks;
+ Eterm count;
+ Eterm size;
Eterm calls;
Eterm sys_alloc;
@@ -4658,6 +4691,7 @@ static struct {
} am;
static Eterm alloc_type_atoms[ERTS_ALC_N_MAX + 1];
+static Eterm alloc_num_atoms[ERTS_ALC_A_MAX + 1];
static ERTS_INLINE void atom_init(Eterm *atom, char *name)
{
@@ -4741,9 +4775,9 @@ init_atoms(Allctr_t *allctr)
AM_INIT(mseg_alloc_carriers);
#endif
AM_INIT(carriers);
- AM_INIT(blocks_size);
AM_INIT(blocks);
- AM_INIT(foreign_blocks);
+ AM_INIT(count);
+ AM_INIT(size);
AM_INIT(calls);
AM_INIT(sys_alloc);
@@ -4767,6 +4801,13 @@ init_atoms(Allctr_t *allctr)
alloc_type_atoms[ix] = am_atom_put(name, len);
}
+
+ for (ix = ERTS_ALC_A_MIN; ix <= ERTS_ALC_A_MAX; ix++) {
+ const char *name = ERTS_ALC_A2AD(ix);
+ size_t len = sys_strlen(name);
+
+ alloc_num_atoms[ix] = am_atom_put(name, len);
+ }
}
if (allctr && !allctr->atoms_initialized) {
@@ -4939,39 +4980,81 @@ sz_info_carriers(Allctr_t *allctr,
Uint *szp)
{
Eterm res = THE_NON_VALUE;
- UWord curr_size = cs->curr.norm.mseg.size + cs->curr.norm.sys_alloc.size;
+ UWord curr_size;
+ int i;
+
+ curr_size = 0;
+ for (i = ERTS_CRR_ALLOC_MIN; i <= ERTS_CRR_ALLOC_MAX; i++) {
+ curr_size += cs->carriers[i].size;
+ }
if (print_to_p) {
- fmtfn_t to = *print_to_p;
- void *arg = print_to_arg;
- erts_print(to,
- arg,
- "%sblocks size: %bpu %bpu %bpu\n",
- prefix,
- cs->blocks.curr.size,
- cs->blocks.max.size,
- cs->blocks.max_ever.size);
- erts_print(to,
- arg,
- "%scarriers size: %bpu %bpu %bpu\n",
- prefix,
- curr_size,
- cs->max.size,
- cs->max_ever.size);
+ fmtfn_t to = *print_to_p;
+ void *arg = print_to_arg;
+ int alloc_no;
+
+ for (alloc_no = ERTS_ALC_A_MIN;
+ alloc_no <= ERTS_ALC_A_MAX;
+ alloc_no++) {
+ int ix = alloc_no - ERTS_ALC_A_MIN;
+
+ erts_print(to,
+ arg,
+ "%sblocks[%s] size: %bpu %bpu %bpu\n",
+ prefix,
+ erts_alc_a2ad[alloc_no],
+ cs->blocks[ix].curr.size,
+ cs->blocks[ix].max.size,
+ cs->blocks[ix].max_ever.size);
+ }
+
+ erts_print(to,
+ arg,
+ "%scarriers size: %bpu %bpu %bpu\n",
+ prefix,
+ curr_size,
+ cs->max.size,
+ cs->max_ever.size);
}
if (hpp || szp) {
- res = NIL;
- add_4tup(hpp, szp, &res,
- am.carriers_size,
- bld_unstable_uint(hpp, szp, curr_size),
- bld_unstable_uint(hpp, szp, cs->max.size),
- bld_unstable_uint(hpp, szp, cs->max_ever.size));
- add_4tup(hpp, szp, &res,
- am.blocks_size,
- bld_unstable_uint(hpp, szp, cs->blocks.curr.size),
- bld_unstable_uint(hpp, szp, cs->blocks.max.size),
- bld_unstable_uint(hpp, szp, cs->blocks.max_ever.size));
+ Eterm blocks;
+ int alloc_no;
+
+ res = NIL;
+
+ add_4tup(hpp, szp, &res,
+ am.carriers_size,
+ bld_unstable_uint(hpp, szp, curr_size),
+ bld_unstable_uint(hpp, szp, cs->max.size),
+ bld_unstable_uint(hpp, szp, cs->max_ever.size));
+
+ blocks = NIL;
+ for (alloc_no = ERTS_ALC_A_MIN;
+ alloc_no <= ERTS_ALC_A_MAX;
+ alloc_no++) {
+ int ix = alloc_no - ERTS_ALC_A_MIN;
+ UWord curr, max, max_ever;
+ Eterm info = NIL;
+
+ curr = cs->blocks[ix].curr.size;
+ max = cs->blocks[ix].max.size;
+ max_ever = cs->blocks[ix].max_ever.size;
+
+ if (curr == 0 && max == 0 && max_ever == 0) {
+ continue;
+ }
+
+ add_4tup(hpp, szp, &info,
+ am.size,
+ bld_unstable_uint(hpp, szp, curr),
+ bld_unstable_uint(hpp, szp, max),
+ bld_unstable_uint(hpp, szp, max_ever));
+
+ add_2tup(hpp, szp, &blocks, alloc_num_atoms[alloc_no], info);
+ }
+
+ add_2tup(hpp, szp, &res, am.blocks, blocks);
}
return res;
@@ -4988,33 +5071,43 @@ info_cpool(Allctr_t *allctr,
Uint *szp)
{
Eterm res = THE_NON_VALUE;
- UWord noc, csz, nob, bsz;
+ UWord noc, csz;
- noc = csz = nob = bsz = ~0;
+ noc = csz = ~0;
if (print_to_p || hpp) {
- if (sz_only)
- cpool_read_stat(allctr, allctr->alloc_no, NULL, &csz, NULL, &bsz);
- else
- cpool_read_stat(allctr, allctr->alloc_no, &noc, &csz, &nob, &bsz);
+ cpool_read_stat(allctr, allctr->alloc_no, &noc, &csz, NULL, NULL);
}
if (print_to_p) {
- fmtfn_t to = *print_to_p;
- void *arg = print_to_arg;
- if (!sz_only)
- erts_print(to, arg, "%sblocks: %bpu\n", prefix, nob);
- erts_print(to, arg, "%sblocks size: %bpu\n", prefix, bsz);
- if (!sz_only)
- erts_print(to, arg, "%scarriers: %bpu\n", prefix, noc);
- erts_print(to, arg, "%scarriers size: %bpu\n", prefix, csz);
+ fmtfn_t to = *print_to_p;
+ void *arg = print_to_arg;
+ int alloc_no;
+
+ for (alloc_no = ERTS_ALC_A_MIN;
+ alloc_no <= ERTS_ALC_A_MAX;
+ alloc_no++) {
+ UWord nob, bsz;
+
+ nob = bsz = ~0;
+ cpool_read_stat(allctr, alloc_no, NULL, NULL, &nob, &bsz);
+
+ if (!sz_only)
+ erts_print(to, arg, "%sblocks[%s] count: %bpu\n",
+ prefix, erts_alc_a2ad[alloc_no], nob);
+ erts_print(to, arg, "%sblocks[%s] size: %bpu\n",
+ prefix, erts_alc_a2ad[alloc_no], bsz);
+ }
+
+ if (!sz_only)
+ erts_print(to, arg, "%scarriers: %bpu\n", prefix, noc);
+ erts_print(to, arg, "%scarriers size: %bpu\n", prefix, csz);
}
if (hpp || szp) {
- Eterm foreign_blocks;
- int i;
+ Eterm blocks;
+ int alloc_no;
- foreign_blocks = NIL;
- res = NIL;
+ res = NIL;
if (!sz_only) {
add_3tup(hpp, szp, &res, am.fail_pooled,
@@ -5072,49 +5165,36 @@ info_cpool(Allctr_t *allctr,
bld_unstable_uint(hpp, szp, noc));
}
- add_2tup(hpp, szp, &res,
- am.blocks_size,
- bld_unstable_uint(hpp, szp, bsz));
-
- if (!sz_only) {
- add_2tup(hpp, szp, &res,
- am.blocks,
- bld_unstable_uint(hpp, szp, nob));
- }
-
- for (i = ERTS_ALC_A_MIN; i <= ERTS_ALC_A_MAX; i++) {
- const char *name_str;
- Eterm name, info;
-
- if (i == allctr->alloc_no) {
- continue;
- }
+ blocks = NIL;
+ for (alloc_no = ERTS_ALC_A_MIN;
+ alloc_no <= ERTS_ALC_A_MAX;
+ alloc_no++) {
+ UWord nob, bsz;
+ Eterm info;
- cpool_read_stat(allctr, i, NULL, NULL, &nob, &bsz);
+ nob = bsz = ~0;
+ cpool_read_stat(allctr, alloc_no, NULL, NULL, &nob, &bsz);
if (bsz == 0 && (nob == 0 || sz_only)) {
continue;
}
- name_str = ERTS_ALC_A2AD(i);
info = NIL;
add_2tup(hpp, szp, &info,
- am.blocks_size,
+ am.size,
bld_unstable_uint(hpp, szp, bsz));
if (!sz_only) {
add_2tup(hpp, szp, &info,
- am.blocks,
+ am.count,
bld_unstable_uint(hpp, szp, nob));
}
- name = am_atom_put(name_str, sys_strlen(name_str));
-
- add_2tup(hpp, szp, &foreign_blocks, name, info);
+ add_2tup(hpp, szp, &blocks, alloc_num_atoms[alloc_no], info);
}
- add_2tup(hpp, szp, &res, am.foreign_blocks, foreign_blocks);
+ add_2tup(hpp, szp, &res, am.blocks, blocks);
}
return res;
@@ -5132,27 +5212,41 @@ info_carriers(Allctr_t *allctr,
{
Eterm res = THE_NON_VALUE;
UWord curr_no, curr_size;
-
- curr_no = cs->curr.norm.mseg.no + cs->curr.norm.sys_alloc.no;
- curr_size = cs->curr.norm.mseg.size + cs->curr.norm.sys_alloc.size;
+ int i;
+
+ curr_no = curr_size = 0;
+ for (i = ERTS_CRR_ALLOC_MIN; i <= ERTS_CRR_ALLOC_MAX; i++) {
+ curr_no += cs->carriers[i].no;
+ curr_size += cs->carriers[i].size;
+ }
if (print_to_p) {
- fmtfn_t to = *print_to_p;
- void *arg = print_to_arg;
- erts_print(to,
- arg,
- "%sblocks: %bpu %bpu %bpu\n",
- prefix,
- cs->blocks.curr.no,
- cs->blocks.max.no,
- cs->blocks.max_ever.no);
- erts_print(to,
- arg,
- "%sblocks size: %bpu %bpu %bpu\n",
- prefix,
- cs->blocks.curr.size,
- cs->blocks.max.size,
- cs->blocks.max_ever.size);
+ fmtfn_t to = *print_to_p;
+ void *arg = print_to_arg;
+ int alloc_no;
+
+ for (alloc_no = ERTS_ALC_A_MIN;
+ alloc_no <= ERTS_ALC_A_MAX;
+ alloc_no++) {
+ int ix = alloc_no - ERTS_ALC_A_MIN;
+ erts_print(to,
+ arg,
+ "%sblocks[%s] count: %bpu %bpu %bpu\n",
+ prefix,
+ erts_alc_a2ad[alloc_no],
+ cs->blocks[ix].curr.no,
+ cs->blocks[ix].max.no,
+ cs->blocks[ix].max_ever.no);
+ erts_print(to,
+ arg,
+ "%sblocks[%s] size: %bpu %bpu %bpu\n",
+ prefix,
+ erts_alc_a2ad[alloc_no],
+ cs->blocks[ix].curr.size,
+ cs->blocks[ix].max.size,
+ cs->blocks[ix].max_ever.size);
+ }
+
erts_print(to,
arg,
"%scarriers: %bpu %bpu %bpu\n",
@@ -5165,13 +5259,13 @@ info_carriers(Allctr_t *allctr,
arg,
"%smseg carriers: %bpu\n",
prefix,
- cs->curr.norm.mseg.no);
+ cs->carriers[ERTS_CRR_ALLOC_MSEG].no);
#endif
erts_print(to,
arg,
"%ssys_alloc carriers: %bpu\n",
prefix,
- cs->curr.norm.sys_alloc.no);
+ cs->carriers[ERTS_CRR_ALLOC_SYS].no);
erts_print(to,
arg,
"%scarriers size: %bpu %bpu %bpu\n",
@@ -5184,24 +5278,27 @@ info_carriers(Allctr_t *allctr,
arg,
"%smseg carriers size: %bpu\n",
prefix,
- cs->curr.norm.mseg.size);
+ cs->carriers[ERTS_CRR_ALLOC_MSEG].size);
#endif
erts_print(to,
arg,
"%ssys_alloc carriers size: %bpu\n",
prefix,
- cs->curr.norm.sys_alloc.size);
+ cs->carriers[ERTS_CRR_ALLOC_SYS].size);
}
if (hpp || szp) {
+ Eterm blocks;
+ int alloc_no;
+
res = NIL;
add_2tup(hpp, szp, &res,
am.sys_alloc_carriers_size,
- bld_unstable_uint(hpp, szp, cs->curr.norm.sys_alloc.size));
+ bld_unstable_uint(hpp, szp, cs->carriers[ERTS_CRR_ALLOC_SYS].size));
#if HAVE_ERTS_MSEG
add_2tup(hpp, szp, &res,
am.mseg_alloc_carriers_size,
- bld_unstable_uint(hpp, szp, cs->curr.norm.mseg.size));
+ bld_unstable_uint(hpp, szp, cs->carriers[ERTS_CRR_ALLOC_MSEG].size));
#endif
add_4tup(hpp, szp, &res,
am.carriers_size,
@@ -5210,27 +5307,57 @@ info_carriers(Allctr_t *allctr,
bld_unstable_uint(hpp, szp, cs->max_ever.size));
add_2tup(hpp, szp, &res,
am.sys_alloc_carriers,
- bld_unstable_uint(hpp, szp, cs->curr.norm.sys_alloc.no));
+ bld_unstable_uint(hpp, szp, cs->carriers[ERTS_CRR_ALLOC_SYS].no));
#if HAVE_ERTS_MSEG
add_2tup(hpp, szp, &res,
am.mseg_alloc_carriers,
- bld_unstable_uint(hpp, szp, cs->curr.norm.mseg.no));
+ bld_unstable_uint(hpp, szp, cs->carriers[ERTS_CRR_ALLOC_MSEG].no));
#endif
add_4tup(hpp, szp, &res,
am.carriers,
bld_unstable_uint(hpp, szp, curr_no),
bld_unstable_uint(hpp, szp, cs->max.no),
bld_unstable_uint(hpp, szp, cs->max_ever.no));
- add_4tup(hpp, szp, &res,
- am.blocks_size,
- bld_unstable_uint(hpp, szp, cs->blocks.curr.size),
- bld_unstable_uint(hpp, szp, cs->blocks.max.size),
- bld_unstable_uint(hpp, szp, cs->blocks.max_ever.size));
- add_4tup(hpp, szp, &res,
- am.blocks,
- bld_unstable_uint(hpp, szp, cs->blocks.curr.no),
- bld_unstable_uint(hpp, szp, cs->blocks.max.no),
- bld_unstable_uint(hpp, szp, cs->blocks.max_ever.no));
+
+ blocks = NIL;
+ for (alloc_no = ERTS_ALC_A_MIN;
+ alloc_no <= ERTS_ALC_A_MAX;
+ alloc_no++) {
+ int ix = alloc_no - ERTS_ALC_A_MIN;
+ UWord curr_size, max_size, max_ever_size;
+ UWord curr_no, max_no, max_ever_no;
+ Eterm info;
+
+ curr_size = cs->blocks[ix].curr.size;
+ max_size = cs->blocks[ix].max.size;
+ max_ever_size = cs->blocks[ix].max_ever.size;
+
+ curr_no = cs->blocks[ix].curr.no;
+ max_no = cs->blocks[ix].max.no;
+ max_ever_no = cs->blocks[ix].max_ever.no;
+
+ if (max_ever_no == 0) {
+ continue;
+ }
+
+ info = NIL;
+
+ add_4tup(hpp, szp, &info,
+ am.size,
+ bld_unstable_uint(hpp, szp, curr_size),
+ bld_unstable_uint(hpp, szp, max_size),
+ bld_unstable_uint(hpp, szp, max_ever_size));
+
+ add_4tup(hpp, szp, &info,
+ am.count,
+ bld_unstable_uint(hpp, szp, curr_no),
+ bld_unstable_uint(hpp, szp, max_no),
+ bld_unstable_uint(hpp, szp, max_ever_no));
+
+ add_2tup(hpp, szp, &blocks, alloc_num_atoms[alloc_no], info);
+ }
+
+ add_2tup(hpp, szp, &res, am.blocks, blocks);
}
return res;
@@ -5490,23 +5617,50 @@ info_options(Allctr_t *allctr,
static ERTS_INLINE void
update_max_ever_values(CarriersStats_t *cs)
{
- if (cs->max_ever.no < cs->max.no)
- cs->max_ever.no = cs->max.no;
- if (cs->max_ever.size < cs->max.size)
- cs->max_ever.size = cs->max.size;
- if (cs->blocks.max_ever.no < cs->blocks.max.no)
- cs->blocks.max_ever.no = cs->blocks.max.no;
- if (cs->blocks.max_ever.size < cs->blocks.max.size)
- cs->blocks.max_ever.size = cs->blocks.max.size;
+ int ix;
+
+ for (ix = 0; ix < ERTS_ALC_A_COUNT; ix++) {
+ BlockStats_t *bstats = &cs->blocks[ix];
+
+ if (bstats->max_ever.no < bstats->max.no) {
+ bstats->max_ever.no = bstats->max.no;
+ }
+
+ if (bstats->max_ever.size < bstats->max.size) {
+ bstats->max_ever.size = bstats->max.size;
+ }
+ }
+
+ if (cs->max_ever.no < cs->max.no) {
+ cs->max_ever.no = cs->max.no;
+ }
+
+ if (cs->max_ever.size < cs->max.size) {
+ cs->max_ever.size = cs->max.size;
+ }
}
static ERTS_INLINE void
reset_max_values(CarriersStats_t *cs)
{
- cs->max.no = cs->curr.norm.mseg.no + cs->curr.norm.sys_alloc.no;
- cs->max.size = cs->curr.norm.mseg.size + cs->curr.norm.sys_alloc.size;
- cs->blocks.max.no = cs->blocks.curr.no;
- cs->blocks.max.size = cs->blocks.curr.size;
+ UWord curr_no, curr_size;
+ int ix;
+
+ for (ix = 0; ix < ERTS_ALC_A_COUNT; ix++) {
+ BlockStats_t *bstats = &cs->blocks[ix];
+
+ bstats->max.no = bstats->curr.no;
+ bstats->max.size = bstats->curr.size;
+ }
+
+ curr_no = curr_size = 0;
+ for (ix = ERTS_CRR_ALLOC_MIN; ix <= ERTS_CRR_ALLOC_MAX; ix++) {
+ curr_no += cs->carriers[ix].no;
+ curr_size += cs->carriers[ix].size;
+ }
+
+ cs->max.no = curr_no;
+ cs->max.size = curr_size;
}
@@ -5614,11 +5768,6 @@ erts_alcu_sz_info(Allctr_t *allctr,
ERTS_ALCU_DBG_CHK_THR_ACCESS(allctr);
- /* Update sbc values not continuously updated */
- allctr->sbcs.blocks.curr.no
- = allctr->sbcs.curr.norm.mseg.no + allctr->sbcs.curr.norm.sys_alloc.no;
- allctr->sbcs.blocks.max.no = allctr->sbcs.max.no;
-
update_max_ever_values(&allctr->mbcs);
update_max_ever_values(&allctr->sbcs);
@@ -5690,11 +5839,6 @@ erts_alcu_info(Allctr_t *allctr,
ERTS_ALCU_DBG_CHK_THR_ACCESS(allctr);
- /* Update sbc values not continuously updated */
- allctr->sbcs.blocks.curr.no
- = allctr->sbcs.curr.norm.mseg.no + allctr->sbcs.curr.norm.sys_alloc.no;
- allctr->sbcs.blocks.max.no = allctr->sbcs.max.no;
-
update_max_ever_values(&allctr->mbcs);
update_max_ever_values(&allctr->sbcs);
@@ -5753,61 +5897,66 @@ erts_alcu_info(Allctr_t *allctr,
void
erts_alcu_foreign_size(Allctr_t *allctr, ErtsAlcType_t alloc_no, AllctrSize_t *size)
{
+ int ix;
+
+ sys_memset(size, 0, sizeof(*size));
+
+ if (allctr->thread_safe)
+ erts_mtx_lock(&allctr->mutex);
+
+ for (ix = ERTS_CRR_ALLOC_MIN; ix <= ERTS_CRR_ALLOC_MAX; ix++) {
+ size->carriers += allctr->mbcs.carriers[ix].size;
+ size->carriers += allctr->sbcs.carriers[ix].size;
+ }
+
+ ix = alloc_no - ERTS_ALC_A_MIN;
+ size->blocks += allctr->mbcs.blocks[ix].curr.size;
+ size->blocks += allctr->sbcs.blocks[ix].curr.size;
+
if (ERTS_ALC_IS_CPOOL_ENABLED(allctr)) {
UWord csz, bsz;
+
+ csz = bsz = 0;
cpool_read_stat(allctr, alloc_no, NULL, &csz, NULL, &bsz);
- size->carriers = csz;
- size->blocks = bsz;
- } else {
- size->carriers = 0;
- size->blocks = 0;
+
+ size->carriers += csz;
+ size->blocks += bsz;
}
+
+ if (allctr->thread_safe)
+ erts_mtx_unlock(&allctr->mutex);
}
void
erts_alcu_current_size(Allctr_t *allctr, AllctrSize_t *size, ErtsAlcUFixInfo_t *fi, int fisz)
{
-
- if (allctr->thread_safe)
- erts_mtx_lock(&allctr->mutex);
-
- size->carriers = allctr->mbcs.curr.norm.mseg.size;
- size->carriers += allctr->mbcs.curr.norm.sys_alloc.size;
- size->carriers += allctr->sbcs.curr.norm.mseg.size;
- size->carriers += allctr->sbcs.curr.norm.sys_alloc.size;
-
- size->blocks = allctr->mbcs.blocks.curr.size;
- size->blocks += allctr->sbcs.blocks.curr.size;
-
- if (ERTS_ALC_IS_CPOOL_ENABLED(allctr)) {
- UWord csz, bsz;
- cpool_read_stat(allctr, allctr->alloc_no, NULL, &csz, NULL, &bsz);
- size->blocks += bsz;
- size->carriers += csz;
- }
+ erts_alcu_foreign_size(allctr, allctr->alloc_no, size);
if (fi) {
- int ix;
- for (ix = 0; ix < fisz; ix++) {
- if (allctr->fix) {
- if (ERTS_ALC_IS_CPOOL_ENABLED(allctr)) {
- fi[ix].allocated += (allctr->fix[ix].type_size
- * allctr->fix[ix].u.cpool.allocated);
- fi[ix].used += (allctr->fix[ix].type_size
- * allctr->fix[ix].u.cpool.used);
- }
- else {
- fi[ix].allocated += (allctr->fix[ix].type_size
- * allctr->fix[ix].u.nocpool.allocated);
- fi[ix].used += (allctr->fix[ix].type_size
- * allctr->fix[ix].u.nocpool.used);
- }
- }
- }
- }
+ int ix;
+
+ if (allctr->thread_safe)
+ erts_mtx_lock(&allctr->mutex);
+
+ for (ix = 0; ix < fisz; ix++) {
+ if (allctr->fix) {
+ if (ERTS_ALC_IS_CPOOL_ENABLED(allctr)) {
+ fi[ix].allocated += (allctr->fix[ix].type_size
+ * allctr->fix[ix].u.cpool.allocated);
+ fi[ix].used += (allctr->fix[ix].type_size
+ * allctr->fix[ix].u.cpool.used);
+ } else {
+ fi[ix].allocated += (allctr->fix[ix].type_size
+ * allctr->fix[ix].u.nocpool.allocated);
+ fi[ix].used += (allctr->fix[ix].type_size
+ * allctr->fix[ix].u.nocpool.used);
+ }
+ }
+ }
- if (allctr->thread_safe)
- erts_mtx_unlock(&allctr->mutex);
+ if (allctr->thread_safe)
+ erts_mtx_unlock(&allctr->mutex);
+ }
}
/* ----------------------------------------------------------------------- */
@@ -6660,10 +6809,12 @@ erts_alcu_start(Allctr_t *allctr, AllctrInit_t *init)
allctr->cpool.dc_list.last = NULL;
allctr->cpool.abandon_limit = 0;
allctr->cpool.disable_abandon = 0;
- for (i = ERTS_ALC_A_MIN; i <= ERTS_ALC_A_MAX; i++) {
+
+ for (i = 0; i < ERTS_ALC_A_COUNT; i++) {
erts_atomic_init_nob(&allctr->cpool.stat.blocks_size[i], 0);
erts_atomic_init_nob(&allctr->cpool.stat.no_blocks[i], 0);
}
+
erts_atomic_init_nob(&allctr->cpool.stat.carriers_size, 0);
erts_atomic_init_nob(&allctr->cpool.stat.no_carriers, 0);
if (!init->ts && init->acul && init->acnl) {
@@ -7696,14 +7847,15 @@ typedef struct chist_node__ {
UWord carrier_size;
UWord unscanned_size;
- UWord allocated_size;
- /* BLOCKSCAN_BAILOUT_THRESHOLD guarantees we won't overflow this or the
- * counters in the free block histogram. */
- int allocated_count;
int flags;
- int histogram[1];
+ /* A mirror of the block counters in the carrier's ErtsAlcCPoolData_t. */
+ UWord alloc_count[ERTS_ALC_A_COUNT];
+ UWord alloc_size[ERTS_ALC_A_COUNT];
+
+ /* BLOCKSCAN_BAILOUT_THRESHOLD guarantees we won't overflow. */
+ int free_histogram[1];
} chist_node_t;
typedef struct {
@@ -7712,7 +7864,7 @@ typedef struct {
ErtsIRefStorage iref;
Process *process;
- Eterm allocator_desc;
+ int allocator_number;
chist_node_t *info_list;
UWord info_count;
@@ -7737,7 +7889,7 @@ static int gather_cinfo_scan(Allctr_t *allocator,
state = (gather_cinfo_t*)user_data;
node = calloc(1, sizeof(chist_node_t) +
(state->hist_slot_count - 1) *
- sizeof(node->histogram[0]));
+ sizeof(node->free_histogram[0]));
blocks_scanned = 1;
/* ERTS_CRR_ALCTR_FLG_BUSY is ignored since we've set it ourselves and it
@@ -7747,16 +7899,20 @@ static int gather_cinfo_scan(Allctr_t *allocator,
node->carrier_size = CARRIER_SZ(carrier);
if (IS_SB_CARRIER(carrier)) {
- UWord block_size;
-
- block = SBC2BLK(allocator, carrier);
- block_size = SBC_BLK_SZ(block);
+ int ix = allocator->alloc_no - ERTS_ALC_A_MIN;
- node->allocated_size = block_size;
- node->allocated_count = 1;
+ node->alloc_count[ix] = 1;
+ node->alloc_size[ix] = node->carrier_size;
} else {
UWord scanned_bytes = MBC_HEADER_SIZE(allocator);
+ sys_memcpy(&node->alloc_count[0],
+ &carrier->cpool.blocks[0],
+ sizeof(UWord) * ERTS_ALC_A_COUNT);
+ sys_memcpy(&node->alloc_size[0],
+ &carrier->cpool.blocks_size[0],
+ sizeof(UWord) * ERTS_ALC_A_COUNT);
+
block = MBC_TO_FIRST_BLK(allocator, carrier);
while (1) {
@@ -7764,10 +7920,7 @@ static int gather_cinfo_scan(Allctr_t *allocator,
scanned_bytes += block_size;
- if (IS_ALLOCED_BLK(block)) {
- node->allocated_size += block_size;
- node->allocated_count++;
- } else {
+ if (IS_FREE_BLK(block)) {
UWord size_interval;
int hist_slot;
@@ -7776,7 +7929,7 @@ static int gather_cinfo_scan(Allctr_t *allocator,
hist_slot = MIN(size_interval, state->hist_slot_count - 1);
- node->histogram[hist_slot]++;
+ node->free_histogram[hist_slot]++;
}
if (blocks_scanned >= BLOCKSCAN_BAILOUT_THRESHOLD) {
@@ -7801,46 +7954,89 @@ static int gather_cinfo_scan(Allctr_t *allocator,
static void gather_cinfo_append_result(gather_cinfo_t *state,
chist_node_t *info)
{
- Eterm carrier_size, unscanned_size, allocated_size;
- Eterm histogram_tuple, carrier_tuple;
+ Eterm carrier_tuple, block_list, histogram_tuple;
+ Eterm carrier_size, unscanned_size;
Uint term_size;
Eterm *hp;
int ix;
ASSERT(state->building_result);
+ term_size = 0;
+
+ /* Free block histogram. */
+ term_size += 1 + state->hist_slot_count;
+
+ /* Per-type block list. */
+ for (ix = ERTS_ALC_A_MIN; ix <= ERTS_ALC_A_MAX; ix++) {
+ UWord count = info->alloc_count[ix - ERTS_ALC_A_MIN];
+ UWord size = info->alloc_size[ix - ERTS_ALC_A_MIN];
+
+ if (count > 0) {
+ /* We have at least one block of this type, so we'll need a cons
+ * cell and a 3-tuple; {Type, Count, Size}. */
+ term_size += 2 + 4;
+ term_size += IS_USMALL(0, count) ? 0 : BIG_UINT_HEAP_SIZE;
+ term_size += IS_USMALL(0, size) ? 0 : BIG_UINT_HEAP_SIZE;
+ }
+ }
- term_size = 11 + state->hist_slot_count;
+ /* Carrier tuple and its fields. */
+ term_size += 7;
term_size += IS_USMALL(0, info->carrier_size) ? 0 : BIG_UINT_HEAP_SIZE;
term_size += IS_USMALL(0, info->unscanned_size) ? 0 : BIG_UINT_HEAP_SIZE;
- term_size += IS_USMALL(0, info->allocated_size) ? 0 : BIG_UINT_HEAP_SIZE;
+ /* ... and a finally a cons cell to keep the result in. */
+ term_size += 2;
+
+ /* * * */
hp = erts_produce_heap(&state->msg_factory, term_size, 0);
- hp[0] = make_arityval(state->hist_slot_count);
+ block_list = NIL;
+ for (ix = ERTS_ALC_A_MIN; ix <= ERTS_ALC_A_MAX; ix++) {
+ UWord count = info->alloc_count[ix - ERTS_ALC_A_MIN];
+ UWord size = info->alloc_size[ix - ERTS_ALC_A_MIN];
- for (ix = 0; ix < state->hist_slot_count; ix++) {
- hp[1 + ix] = make_small(info->histogram[ix]);
+ if (count > 0) {
+ Eterm block_count, block_size;
+ Eterm alloc_tuple;
+
+ block_count = bld_unstable_uint(&hp, NULL, count);
+ block_size = bld_unstable_uint(&hp, NULL, size);
+
+ hp[0] = make_arityval(3);
+ hp[1] = alloc_num_atoms[ix];
+ hp[2] = block_count;
+ hp[3] = block_size;
+
+ alloc_tuple = make_tuple(hp);
+ hp += 4;
+
+ block_list = CONS(hp, alloc_tuple, block_list);
+ hp += 2;
+ }
}
+ hp[0] = make_arityval(state->hist_slot_count);
+ for (ix = 0; ix < state->hist_slot_count; ix++) {
+ hp[1 + ix] = make_small(info->free_histogram[ix]);
+ }
histogram_tuple = make_tuple(hp);
hp += 1 + state->hist_slot_count;
carrier_size = bld_unstable_uint(&hp, NULL, info->carrier_size);
unscanned_size = bld_unstable_uint(&hp, NULL, info->unscanned_size);
- allocated_size = bld_unstable_uint(&hp, NULL, info->allocated_size);
- hp[0] = make_arityval(7);
- hp[1] = state->allocator_desc;
- hp[2] = carrier_size;
- hp[3] = unscanned_size;
- hp[4] = allocated_size;
- hp[5] = make_small(info->allocated_count);
- hp[6] = (info->flags & ERTS_CRR_ALCTR_FLG_IN_POOL) ? am_true : am_false;
- hp[7] = histogram_tuple;
+ hp[0] = make_arityval(6);
+ hp[1] = alloc_num_atoms[state->allocator_number];
+ hp[2] = (info->flags & ERTS_CRR_ALCTR_FLG_IN_POOL) ? am_true : am_false;
+ hp[3] = carrier_size;
+ hp[4] = unscanned_size;
+ hp[5] = block_list;
+ hp[6] = histogram_tuple;
carrier_tuple = make_tuple(hp);
- hp += 8;
+ hp += 7;
state->result_list = CONS(hp, carrier_tuple, state->result_list);
@@ -7936,8 +8132,6 @@ int erts_alcu_gather_carrier_info(struct process *p, int allocator_num,
{
gather_cinfo_t *gather_state;
blockscan_t *scanner;
-
- const char *allocator_desc;
Allctr_t *allocator;
ASSERT(is_internal_ref(ref));
@@ -7948,7 +8142,7 @@ int erts_alcu_gather_carrier_info(struct process *p, int allocator_num,
return 0;
}
- allocator_desc = ERTS_ALC_A2AD(allocator_num);
+ ensure_atoms_initialized(allocator);
/* Plain calloc is intentional. */
gather_state = (gather_cinfo_t*)calloc(1, sizeof(gather_cinfo_t));
@@ -7959,9 +8153,7 @@ int erts_alcu_gather_carrier_info(struct process *p, int allocator_num,
scanner->finish = gather_cinfo_finish;
scanner->user_data = gather_state;
- gather_state->allocator_desc = erts_atom_put((byte *)allocator_desc,
- sys_strlen(allocator_desc),
- ERTS_ATOM_ENC_LATIN1, 1);
+ gather_state->allocator_number = allocator_num;
erts_iref_storage_save(&gather_state->iref, ref);
gather_state->hist_slot_start = hist_start * 2;
gather_state->hist_slot_count = hist_width;
@@ -8069,14 +8261,22 @@ void
erts_alcu_verify_unused(Allctr_t *allctr)
{
UWord no;
+ int ix;
- no = allctr->sbcs.curr.norm.mseg.no;
- no += allctr->sbcs.curr.norm.sys_alloc.no;
- no += allctr->mbcs.blocks.curr.no;
+ no = 0;
+ for (ix = ERTS_CRR_ALLOC_MIN; ix <= ERTS_CRR_ALLOC_MAX; ix++) {
+ no += allctr->sbcs.carriers[ix].no;
+ }
+
+ ix = allctr->alloc_no - ERTS_ALC_A_MIN;
+ no += allctr->mbcs.blocks[ix].curr.no;
if (no) {
- UWord sz = allctr->sbcs.blocks.curr.size;
- sz += allctr->mbcs.blocks.curr.size;
+ UWord sz = 0;
+
+ sz += allctr->sbcs.blocks[ix].curr.size;
+ sz += allctr->mbcs.blocks[ix].curr.size;
+
erts_exit(ERTS_ABORT_EXIT,
"%salloc() used when expected to be unused!\n"
"Total amount of blocks allocated: %bpu\n"
diff --git a/erts/emulator/beam/erl_alloc_util.h b/erts/emulator/beam/erl_alloc_util.h
index b46b311c59..07cbd8470e 100644
--- a/erts/emulator/beam/erl_alloc_util.h
+++ b/erts/emulator/beam/erl_alloc_util.h
@@ -456,8 +456,8 @@ typedef struct {
ErtsThrPrgrVal thr_prgr;
erts_atomic_t max_size;
UWord abandon_limit;
- UWord blocks[ERTS_ALC_A_MAX + 1];
- UWord blocks_size[ERTS_ALC_A_MAX + 1];
+ UWord blocks[ERTS_ALC_A_COUNT];
+ UWord blocks_size[ERTS_ALC_A_COUNT];
UWord total_blocks_size;
enum {
ERTS_MBC_IS_HOME,
@@ -492,19 +492,28 @@ typedef struct {
} StatValues_t;
typedef struct {
- union {
- struct {
- StatValues_t mseg;
- StatValues_t sys_alloc;
- } norm;
- } curr;
- StatValues_t max;
- StatValues_t max_ever;
- struct {
- StatValues_t curr;
- StatValues_t max;
- StatValues_t max_ever;
- } blocks;
+ StatValues_t curr;
+ StatValues_t max;
+ StatValues_t max_ever;
+} BlockStats_t;
+
+enum {
+ ERTS_CRR_ALLOC_MIN = 0,
+
+ ERTS_CRR_ALLOC_MSEG = ERTS_CRR_ALLOC_MIN,
+ ERTS_CRR_ALLOC_SYS = 1,
+
+ ERTS_CRR_ALLOC_MAX,
+ ERTS_CRR_ALLOC_COUNT = ERTS_CRR_ALLOC_MAX + 1
+};
+
+typedef struct {
+ StatValues_t carriers[ERTS_CRR_ALLOC_COUNT];
+
+ StatValues_t max;
+ StatValues_t max_ever;
+
+ BlockStats_t blocks[ERTS_ALC_A_COUNT];
} CarriersStats_t;
#ifdef USE_LTTNG_VM_TRACEPOINTS
@@ -654,8 +663,8 @@ struct Allctr_t_ {
UWord in_pool_limit; /* acnl */
UWord fblk_min_limit; /* acmfl */
struct {
- erts_atomic_t blocks_size[ERTS_ALC_A_MAX + 1];
- erts_atomic_t no_blocks[ERTS_ALC_A_MAX + 1];
+ erts_atomic_t blocks_size[ERTS_ALC_A_COUNT];
+ erts_atomic_t no_blocks[ERTS_ALC_A_COUNT];
erts_atomic_t carriers_size;
erts_atomic_t no_carriers;
CallCounter_t fail_pooled;
diff --git a/erts/emulator/beam/erl_async.c b/erts/emulator/beam/erl_async.c
index 44655ad5df..d160cda4df 100644
--- a/erts/emulator/beam/erl_async.c
+++ b/erts/emulator/beam/erl_async.c
@@ -254,25 +254,6 @@ static ERTS_INLINE void async_add(ErtsAsync *a, ErtsAsyncQ* q)
#endif
erts_thr_q_enqueue(&q->thr_q, a);
-#ifdef USE_LTTNG_VM_TRACEPOINTS
- if (LTTNG_ENABLED(aio_pool_put)) {
- lttng_decl_portbuf(port_str);
- lttng_portid_to_str(a->port, port_str);
- LTTNG2(aio_pool_put, port_str, -1);
- }
-#endif
-#ifdef USE_VM_PROBES
- if (DTRACE_ENABLED(aio_pool_add)) {
- DTRACE_CHARBUF(port_str, 16);
-
- erts_snprintf(port_str, sizeof(DTRACE_CHARBUF_NAME(port_str)),
- "%T", a->port);
- /* DTRACE TODO: Get the queue length from erts_thr_q_enqueue() ? */
- len = -1;
- DTRACE2(aio_pool_add, port_str, len);
- }
- gcc_optimizer_hack++;
-#endif
}
static ERTS_INLINE ErtsAsync *async_get(ErtsThrQ_t *q,
@@ -293,25 +274,6 @@ static ERTS_INLINE ErtsAsync *async_get(ErtsThrQ_t *q,
erts_thr_q_get_finalize_dequeue_data(q, &a->q.fin_deq);
if (saved_fin_deq)
erts_thr_q_append_finalize_dequeue_data(&a->q.fin_deq, &fin_deq);
-#ifdef USE_LTTNG_VM_TRACEPOINTS
- if (LTTNG_ENABLED(aio_pool_get)) {
- lttng_decl_portbuf(port_str);
- int length = erts_thr_q_length_dirty(q);
- lttng_portid_to_str(a->port, port_str);
- LTTNG2(aio_pool_get, port_str, length);
- }
-#endif
-#ifdef USE_VM_PROBES
- if (DTRACE_ENABLED(aio_pool_get)) {
- DTRACE_CHARBUF(port_str, 16);
-
- erts_snprintf(port_str, sizeof(DTRACE_CHARBUF_NAME(port_str)),
- "%T", a->port);
- /* DTRACE TODO: Get the length from erts_thr_q_dequeue() ? */
- len = -1;
- DTRACE2(aio_pool_get, port_str, len);
- }
-#endif
return a;
}
diff --git a/erts/emulator/beam/erl_bif_binary.c b/erts/emulator/beam/erl_bif_binary.c
index b8e56390c1..03b8e0e632 100644
--- a/erts/emulator/beam/erl_bif_binary.c
+++ b/erts/emulator/beam/erl_bif_binary.c
@@ -2413,7 +2413,7 @@ HIPE_WRAPPER_BIF_DISABLE_GC(binary_list_to_bin, 1)
BIF_RETTYPE binary_list_to_bin_1(BIF_ALIST_1)
{
- return erts_list_to_binary_bif(BIF_P, BIF_ARG_1, bif_export[BIF_binary_list_to_bin_1]);
+ return erts_list_to_binary_bif(BIF_P, BIF_ARG_1, &bif_trap_export[BIF_binary_list_to_bin_1]);
}
typedef struct {
diff --git a/erts/emulator/beam/erl_bif_chksum.c b/erts/emulator/beam/erl_bif_chksum.c
index cce8472ccb..4cab9bec1d 100644
--- a/erts/emulator/beam/erl_bif_chksum.c
+++ b/erts/emulator/beam/erl_bif_chksum.c
@@ -327,7 +327,7 @@ crc32_1(BIF_ALIST_1)
res_sum = erts_make_integer(chksum,BIF_P);
if (rest != NIL) {
BUMP_ALL_REDS(BIF_P);
- BIF_TRAP2(bif_export[BIF_crc32_2], BIF_P, res_sum, rest);
+ BIF_TRAP2(&bif_trap_export[BIF_crc32_2], BIF_P, res_sum, rest);
}
BIF_RET(res_sum);
}
@@ -354,7 +354,7 @@ crc32_2(BIF_ALIST_2)
res_sum = erts_make_integer(chksum,BIF_P);
if (rest != NIL) {
BUMP_ALL_REDS(BIF_P);
- BIF_TRAP2(bif_export[BIF_crc32_2], BIF_P, res_sum, rest);
+ BIF_TRAP2(&bif_trap_export[BIF_crc32_2], BIF_P, res_sum, rest);
}
BIF_RET(res_sum);
}
@@ -407,7 +407,7 @@ adler32_1(BIF_ALIST_1)
res_sum = erts_make_integer(chksum,BIF_P);
if (rest != NIL) {
BUMP_ALL_REDS(BIF_P);
- BIF_TRAP2(bif_export[BIF_adler32_2], BIF_P, res_sum, rest);
+ BIF_TRAP2(&bif_trap_export[BIF_adler32_2], BIF_P, res_sum, rest);
}
BIF_RET(res_sum);
}
@@ -434,7 +434,7 @@ adler32_2(BIF_ALIST_2)
res_sum = erts_make_integer(chksum,BIF_P);
if (rest != NIL) {
BUMP_ALL_REDS(BIF_P);
- BIF_TRAP2(bif_export[BIF_adler32_2], BIF_P, res_sum, rest);
+ BIF_TRAP2(&bif_trap_export[BIF_adler32_2], BIF_P, res_sum, rest);
}
BIF_RET(res_sum);
}
@@ -575,7 +575,7 @@ md5_update_2(BIF_ALIST_2)
bin = new_binary(BIF_P, (byte *) &context, sizeof(MD5_CTX));
if (rest != NIL) {
BUMP_ALL_REDS(BIF_P);
- BIF_TRAP2(bif_export[BIF_md5_update_2], BIF_P, bin, rest);
+ BIF_TRAP2(&bif_trap_export[BIF_md5_update_2], BIF_P, bin, rest);
}
BUMP_REDS(BIF_P,res);
BIF_RET(bin);
diff --git a/erts/emulator/beam/erl_bif_guard.c b/erts/emulator/beam/erl_bif_guard.c
index 09757e473b..053797bc89 100644
--- a/erts/emulator/beam/erl_bif_guard.c
+++ b/erts/emulator/beam/erl_bif_guard.c
@@ -239,7 +239,7 @@ static BIF_RETTYPE erlang_length_trap(BIF_ALIST_3)
* Signal an error. The original argument was tucked away in BIF_ARG_3.
*/
ERTS_BIF_ERROR_TRAPPED1(BIF_P, BIF_P->freason,
- bif_export[BIF_length_1], BIF_ARG_3);
+ &bif_trap_export[BIF_length_1], BIF_ARG_3);
}
}
}
diff --git a/erts/emulator/beam/erl_bif_info.c b/erts/emulator/beam/erl_bif_info.c
index 1064c89d84..26020ef5b2 100644
--- a/erts/emulator/beam/erl_bif_info.c
+++ b/erts/emulator/beam/erl_bif_info.c
@@ -158,8 +158,9 @@ static Eterm os_version_tuple;
static Eterm
current_function(Process* p, ErtsHeapFactory *hfact, Process* rp,
int full_info, Uint reserve_size, int flags);
-static Eterm current_stacktrace(ErtsHeapFactory *hfact, Process* rp,
- Uint reserve_size);
+static Eterm
+current_stacktrace(Process* p, ErtsHeapFactory *hfact, Process* rp,
+ Uint reserve_size, int flags);
Eterm
erts_bld_bin_list(Uint **hpp, Uint *szp, ErlOffHeap* oh, Eterm tail)
@@ -548,6 +549,8 @@ static int collect_one_origin_monitor(ErtsMonitor *mon, void *vmicp, Sint reds)
case ERTS_MON_TYPE_PORT:
case ERTS_MON_TYPE_DIST_PROC:
case ERTS_MON_TYPE_TIME_OFFSET:
+ if (mon->flags & ERTS_ML_FLG_SPAWN_PENDING)
+ break; /* Not an active monitor... */
if (!(mon->flags & ERTS_ML_FLG_NAME)) {
micp->mi[micp->mi_i].named = 0;
micp->mi[micp->mi_i].entity.term = mon->other.item;
@@ -1249,9 +1252,9 @@ exited:
yield:
if (pi2)
- ERTS_BIF_PREP_YIELD2(ret, bif_export[BIF_process_info_2], c_p, pid, opt);
+ ERTS_BIF_PREP_YIELD2(ret, &bif_trap_export[BIF_process_info_2], c_p, pid, opt);
else
- ERTS_BIF_PREP_YIELD1(ret, bif_export[BIF_process_info_1], c_p, pid);
+ ERTS_BIF_PREP_YIELD1(ret, &bif_trap_export[BIF_process_info_1], c_p, pid);
goto done;
send_signal: {
@@ -1384,7 +1387,7 @@ process_info_aux(Process *c_p,
break;
case ERTS_PI_IX_CURRENT_STACKTRACE:
- res = current_stacktrace(hfact, rp, reserve_size);
+ res = current_stacktrace(c_p, hfact, rp, reserve_size, flags);
break;
case ERTS_PI_IX_INITIAL_CALL:
@@ -2022,19 +2025,23 @@ current_function(Process *c_p, ErtsHeapFactory *hfact, Process* rp,
}
if (c_p == rp && !(flags & ERTS_PI_FLAG_REQUEST_FOR_OTHER)) {
- FunctionInfo fi2;
+ BeamInstr* return_address;
+ FunctionInfo caller_fi;
- /*
- * The current function is erlang:process_info/{1,2},
- * which is not the answer that the application want.
- * We will use the function pointed into by rp->cp
- * instead if it can be looked up.
- */
- erts_lookup_function_info(&fi2, rp->cp, full_info);
- if (fi2.mfa) {
- fi = fi2;
- rp->current = fi2.mfa;
- }
+ /*
+ * The current function is erlang:process_info/{1,2}, and we've
+ * historically returned the *calling* function in that case. We
+ * therefore use the continuation pointer stored at the top of the
+ * stack instead, which is safe since process_info is a "heavy" BIF
+ * that is only called through its export entry.
+ */
+ return_address = erts_printable_return_address(rp, STACK_TOP(rp));
+
+ erts_lookup_function_info(&caller_fi, return_address, full_info);
+ if (caller_fi.mfa) {
+ fi = caller_fi;
+ rp->current = caller_fi.mfa;
+ }
}
/*
@@ -2055,8 +2062,8 @@ current_function(Process *c_p, ErtsHeapFactory *hfact, Process* rp,
}
static Eterm
-current_stacktrace(ErtsHeapFactory *hfact, Process* rp,
- Uint reserve_size)
+current_stacktrace(Process *p, ErtsHeapFactory *hfact, Process* rp,
+ Uint reserve_size, int flags)
{
Uint sz;
struct StackTrace* s;
@@ -2073,13 +2080,14 @@ current_stacktrace(ErtsHeapFactory *hfact, Process* rp,
sz = offsetof(struct StackTrace, trace) + sizeof(BeamInstr *)*depth;
s = (struct StackTrace *) erts_alloc(ERTS_ALC_T_TMP, sz);
s->depth = 0;
- if (depth > 0 && rp->i) {
- s->trace[s->depth++] = rp->i;
- depth--;
- }
- if (depth > 0 && rp->cp != 0) {
- s->trace[s->depth++] = rp->cp - 1;
- depth--;
+ s->pc = NULL;
+
+ /* We skip current pc when requesting our own stack trace since it will
+ * inevitably point to process_info/1,2 */
+ if ((p != rp || (flags & ERTS_PI_FLAG_REQUEST_FOR_OTHER)) &&
+ depth > 0 && rp->i) {
+ s->trace[s->depth++] = rp->i;
+ depth--;
}
erts_save_stacktrace(rp, s, depth);
@@ -2814,7 +2822,10 @@ BIF_RETTYPE system_info_1(BIF_ALIST_1)
} else if (BIF_ARG_1 == am_threads) {
return am_true;
} else if (BIF_ARG_1 == am_creation) {
- return make_small(erts_this_node->creation);
+ Uint hsz = 0;
+ erts_bld_uint(NULL, &hsz, erts_this_node->creation);
+ hp = hsz ? HAlloc(BIF_P, hsz) : NULL;
+ BIF_RET(erts_bld_uint(&hp, NULL, erts_this_node->creation));
} else if (BIF_ARG_1 == am_break_ignored) {
extern int ignore_break;
if (ignore_break)
@@ -2825,7 +2836,7 @@ BIF_RETTYPE system_info_1(BIF_ALIST_1)
/* Arguments that are unusual follow ... */
else if (ERTS_IS_ATOM_STR("logical_processors", BIF_ARG_1)) {
int no;
- erts_get_logical_processors(&no, NULL, NULL);
+ erts_get_logical_processors(&no, NULL, NULL, NULL);
if (no > 0)
BIF_RET(make_small((Uint) no));
else {
@@ -2835,7 +2846,7 @@ BIF_RETTYPE system_info_1(BIF_ALIST_1)
}
else if (ERTS_IS_ATOM_STR("logical_processors_online", BIF_ARG_1)) {
int no;
- erts_get_logical_processors(NULL, &no, NULL);
+ erts_get_logical_processors(NULL, &no, NULL, NULL);
if (no > 0)
BIF_RET(make_small((Uint) no));
else {
@@ -2845,7 +2856,17 @@ BIF_RETTYPE system_info_1(BIF_ALIST_1)
}
else if (ERTS_IS_ATOM_STR("logical_processors_available", BIF_ARG_1)) {
int no;
- erts_get_logical_processors(NULL, NULL, &no);
+ erts_get_logical_processors(NULL, NULL, &no, NULL);
+ if (no > 0)
+ BIF_RET(make_small((Uint) no));
+ else {
+ DECL_AM(unknown);
+ BIF_RET(AM_unknown);
+ }
+ }
+ else if (ERTS_IS_ATOM_STR("cpu_quota", BIF_ARG_1)) {
+ int no;
+ erts_get_logical_processors(NULL, NULL, NULL, &no);
if (no > 0)
BIF_RET(make_small((Uint) no));
else {
@@ -4043,7 +4064,16 @@ BIF_RETTYPE erts_debug_get_internal_state_1(BIF_ALIST_1)
BIF_RET(am_notsup);
#endif
}
-
+ else if (ERTS_IS_ATOM_STR("flxctr_memory_usage", BIF_ARG_1)) {
+ Sint mem = erts_flxctr_debug_memory_usage();
+ if (mem == -1) {
+ BIF_RET(am_notsup);
+ } else {
+ Uint hsz = BIG_UWORD_HEAP_SIZE((UWord)mem);
+ Eterm *hp = HAlloc(BIF_P, hsz);
+ BIF_RET(uword_to_big((UWord)mem, hp));
+ }
+ }
}
else if (is_tuple(BIF_ARG_1)) {
Eterm* tp = tuple_val(BIF_ARG_1);
@@ -4254,9 +4284,9 @@ BIF_RETTYPE erts_debug_get_internal_state_1(BIF_ALIST_1)
}
}
else if (ERTS_IS_ATOM_STR("term_to_binary_tuple_fallbacks", tp[1])) {
- Uint dflags = (TERM_TO_BINARY_DFLAGS
- & ~DFLAG_EXPORT_PTR_TAG
- & ~DFLAG_BIT_BINARIES);
+ Uint64 dflags = (TERM_TO_BINARY_DFLAGS
+ & ~DFLAG_EXPORT_PTR_TAG
+ & ~DFLAG_BIT_BINARIES);
Eterm res = erts_term_to_binary(BIF_P, tp[2], 0, dflags);
if (is_value(res))
BIF_RET(res);
@@ -4374,6 +4404,9 @@ BIF_RETTYPE erts_debug_get_internal_state_1(BIF_ALIST_1)
break;
BIF_RET(res);
}
+ else if (ERTS_IS_ATOM_STR("term_to_binary", tp[1])) {
+ return erts_debug_term_to_binary(BIF_P, tp[2], tp[3]);
+ }
break;
}
default:
@@ -4593,7 +4626,7 @@ BIF_RETTYPE erts_debug_set_internal_state_2(BIF_ALIST_2)
if (!flag && BIF_ARG_2 != am_false) {
erts_atomic_set_nob(&hipe_test_reschedule_flag, 1);
erts_suspend(BIF_P, ERTS_PROC_LOCK_MAIN, NULL);
- ERTS_BIF_YIELD2(bif_export[BIF_erts_debug_set_internal_state_2],
+ ERTS_BIF_YIELD2(&bif_trap_export[BIF_erts_debug_set_internal_state_2],
BIF_P, BIF_ARG_1, BIF_ARG_2);
}
erts_atomic_set_nob(&hipe_test_reschedule_flag, !flag);
@@ -4639,9 +4672,14 @@ BIF_RETTYPE erts_debug_set_internal_state_2(BIF_ALIST_2)
flag = ERTS_DEBUG_WAIT_COMPLETED_TIMER_CANCELLATIONS;
else if (ERTS_IS_ATOM_STR("aux_work", BIF_ARG_2))
flag = ERTS_DEBUG_WAIT_COMPLETED_AUX_WORK;
+ else if (ERTS_IS_ATOM_STR("thread_progress", BIF_ARG_2))
+ flag = ERTS_DEBUG_WAIT_COMPLETED_THREAD_PROGRESS;
- if (flag && erts_debug_wait_completed(BIF_P, flag)) {
- ERTS_BIF_YIELD_RETURN(BIF_P, am_ok);
+ if (flag) {
+ if (erts_debug_wait_completed(BIF_P, flag))
+ ERTS_BIF_YIELD_RETURN(BIF_P, am_ok);
+ else
+ BIF_ERROR(BIF_P, SYSTEM_LIMIT);
}
}
else if (ERTS_IS_ATOM_STR("broken_halt", BIF_ARG_1)) {
@@ -4720,7 +4758,6 @@ BIF_RETTYPE erts_debug_set_internal_state_2(BIF_ALIST_2)
else if (ERTS_IS_ATOM_STR("ets_debug_random_split_join", BIF_ARG_1)) {
if (is_tuple(BIF_ARG_2)) {
Eterm* tpl = tuple_val(BIF_ARG_2);
-
if (erts_ets_debug_random_split_join(tpl[1], tpl[2] == am_true))
BIF_RET(am_ok);
}
@@ -4735,11 +4772,225 @@ BIF_RETTYPE erts_debug_set_internal_state_2(BIF_ALIST_2)
BIF_P->mbuf_sz += sz;
BIF_RET(copy);
}
+ else if (ERTS_IS_ATOM_STR("remove_hopefull_dflags", BIF_ARG_1)) {
+ int old_val, new_val;
+
+ switch (BIF_ARG_2) {
+ case am_true: new_val = !0; break;
+ case am_false: new_val = 0; break;
+ default: BIF_ERROR(BIF_P, BADARG); break;
+ }
+
+ erts_proc_unlock(BIF_P, ERTS_PROC_LOCK_MAIN);
+ erts_thr_progress_block();
+
+ old_val = erts_dflags_test_remove_hopefull_flags;
+ erts_dflags_test_remove_hopefull_flags = new_val;
+
+ erts_thr_progress_unblock();
+ erts_proc_lock(BIF_P, ERTS_PROC_LOCK_MAIN);
+
+ BIF_RET(old_val ? am_true : am_false);
+ }
}
BIF_ERROR(BIF_P, BADARG);
}
+Eterm
+erts_get_ethread_info(Process *c_p)
+{
+ Uint sz, *szp;
+ Eterm res, *hp, **hpp, *end_hp = NULL;
+
+ sz = 0;
+ szp = &sz;
+ hpp = NULL;
+
+ while (1) {
+ Eterm tup, list, name;
+#if defined(ETHR_NATIVE_ATOMIC32_IMPL) \
+ || defined(ETHR_NATIVE_ATOMIC64_IMPL) \
+ || defined(ETHR_NATIVE_DW_ATOMIC_IMPL)
+ char buf[1024];
+ int i;
+ char **str;
+#endif
+
+ res = NIL;
+
+#ifdef ETHR_X86_MEMBAR_H__
+
+ tup = erts_bld_tuple(hpp, szp, 2,
+ erts_bld_string(hpp, szp, "sse2"),
+#ifdef ETHR_X86_RUNTIME_CONF_HAVE_SSE2__
+ erts_bld_string(hpp, szp,
+ (ETHR_X86_RUNTIME_CONF_HAVE_SSE2__
+ ? "yes" : "no"))
+#else
+ erts_bld_string(hpp, szp, "yes")
+#endif
+ );
+ res = erts_bld_cons(hpp, szp, tup, res);
+
+ tup = erts_bld_tuple(hpp, szp, 2,
+ erts_bld_string(hpp, szp,
+ "x86"
+#ifdef ARCH_64
+ "_64"
+#endif
+ " OOO"),
+ erts_bld_string(hpp, szp,
+#ifdef ETHR_X86_OUT_OF_ORDER
+ "yes"
+#else
+ "no"
+#endif
+ ));
+
+ res = erts_bld_cons(hpp, szp, tup, res);
+#endif
+
+#ifdef ETHR_SPARC_V9_MEMBAR_H__
+
+ tup = erts_bld_tuple(hpp, szp, 2,
+ erts_bld_string(hpp, szp, "Sparc V9"),
+ erts_bld_string(hpp, szp,
+#if defined(ETHR_SPARC_TSO)
+ "TSO"
+#elif defined(ETHR_SPARC_PSO)
+ "PSO"
+#elif defined(ETHR_SPARC_RMO)
+ "RMO"
+#else
+ "undefined"
+#endif
+ ));
+
+ res = erts_bld_cons(hpp, szp, tup, res);
+
+#endif
+
+#ifdef ETHR_PPC_MEMBAR_H__
+
+ tup = erts_bld_tuple(hpp, szp, 2,
+ erts_bld_string(hpp, szp, "lwsync"),
+ erts_bld_string(hpp, szp,
+#if defined(ETHR_PPC_HAVE_LWSYNC)
+ "yes"
+#elif defined(ETHR_PPC_HAVE_NO_LWSYNC)
+ "no"
+#elif defined(ETHR_PPC_RUNTIME_CONF_HAVE_LWSYNC__)
+ ETHR_PPC_RUNTIME_CONF_HAVE_LWSYNC__ ? "yes" : "no"
+#else
+ "undefined"
+#endif
+ ));
+
+ res = erts_bld_cons(hpp, szp, tup, res);
+
+#endif
+
+ tup = erts_bld_tuple(hpp, szp, 2,
+ erts_bld_string(hpp, szp, "Native rw-spinlocks"),
+#ifdef ETHR_NATIVE_RWSPINLOCK_IMPL
+ erts_bld_string(hpp, szp, ETHR_NATIVE_RWSPINLOCK_IMPL)
+#else
+ erts_bld_string(hpp, szp, "no")
+#endif
+ );
+ res = erts_bld_cons(hpp, szp, tup, res);
+
+ tup = erts_bld_tuple(hpp, szp, 2,
+ erts_bld_string(hpp, szp, "Native spinlocks"),
+#ifdef ETHR_NATIVE_SPINLOCK_IMPL
+ erts_bld_string(hpp, szp, ETHR_NATIVE_SPINLOCK_IMPL)
+#else
+ erts_bld_string(hpp, szp, "no")
+#endif
+ );
+ res = erts_bld_cons(hpp, szp, tup, res);
+
+
+ list = NIL;
+#ifdef ETHR_NATIVE_DW_ATOMIC_IMPL
+ if (ethr_have_native_dw_atomic()) {
+ name = erts_bld_string(hpp, szp, ETHR_NATIVE_DW_ATOMIC_IMPL);
+ str = ethr_native_dw_atomic_ops();
+ for (i = 0; str[i]; i++) {
+ erts_snprintf(buf, sizeof(buf), "ethr_native_dw_atomic_%s()", str[i]);
+ list = erts_bld_cons(hpp, szp,
+ erts_bld_string(hpp, szp, buf),
+ list);
+ }
+ str = ethr_native_su_dw_atomic_ops();
+ for (i = 0; str[i]; i++) {
+ erts_snprintf(buf, sizeof(buf), "ethr_native_su_dw_atomic_%s()", str[i]);
+ list = erts_bld_cons(hpp, szp,
+ erts_bld_string(hpp, szp, buf),
+ list);
+ }
+ }
+ else
+#endif
+ name = erts_bld_string(hpp, szp, "no");
+
+ tup = erts_bld_tuple(hpp, szp, 3,
+ erts_bld_string(hpp, szp, "Double word native atomics"),
+ name,
+ list);
+ res = erts_bld_cons(hpp, szp, tup, res);
+
+ list = NIL;
+#ifdef ETHR_NATIVE_ATOMIC64_IMPL
+ name = erts_bld_string(hpp, szp, ETHR_NATIVE_ATOMIC64_IMPL);
+ str = ethr_native_atomic64_ops();
+ for (i = 0; str[i]; i++) {
+ erts_snprintf(buf, sizeof(buf), "ethr_native_atomic64_%s()", str[i]);
+ list = erts_bld_cons(hpp, szp,
+ erts_bld_string(hpp, szp, buf),
+ list);
+ }
+#else
+ name = erts_bld_string(hpp, szp, "no");
+#endif
+ tup = erts_bld_tuple(hpp, szp, 3,
+ erts_bld_string(hpp, szp, "64-bit native atomics"),
+ name,
+ list);
+ res = erts_bld_cons(hpp, szp, tup, res);
+
+ list = NIL;
+#ifdef ETHR_NATIVE_ATOMIC32_IMPL
+ name = erts_bld_string(hpp, szp, ETHR_NATIVE_ATOMIC32_IMPL);
+ str = ethr_native_atomic32_ops();
+ for (i = 0; str[i]; i++) {
+ erts_snprintf(buf, sizeof(buf), "ethr_native_atomic32_%s()", str[i]);
+ list = erts_bld_cons(hpp, szp,
+ erts_bld_string(hpp, szp, buf),
+ list);
+ }
+#else
+ name = erts_bld_string(hpp, szp, "no");
+#endif
+ tup = erts_bld_tuple(hpp, szp, 3,
+ erts_bld_string(hpp, szp, "32-bit native atomics"),
+ name,
+ list);
+ res = erts_bld_cons(hpp, szp, tup, res);
+
+ if (hpp) {
+ HRelease(c_p, end_hp, *hpp)
+ return res;
+ }
+
+ hp = HAlloc(c_p, sz);
+ end_hp = hp + sz;
+ hpp = &hp;
+ szp = NULL;
+ }
+}
+
static BIF_RETTYPE
gather_histograms_helper(Process * c_p, Eterm arg_tuple,
int gather(Process *, int, int, int, UWord, Eterm))
diff --git a/erts/emulator/beam/erl_bif_lists.c b/erts/emulator/beam/erl_bif_lists.c
index fa2edfef1e..40512a117d 100644
--- a/erts/emulator/beam/erl_bif_lists.c
+++ b/erts/emulator/beam/erl_bif_lists.c
@@ -32,8 +32,7 @@
#include "bif.h"
#include "erl_binary.h"
-
-static Eterm keyfind(int Bif, Process* p, Eterm Key, Eterm Pos, Eterm List);
+static Eterm keyfind(Export* Bif, Process* p, Eterm Key, Eterm Pos, Eterm List);
/* erlang:'++'/2
*
@@ -308,12 +307,12 @@ static Eterm append(Export *bif_entry, BIF_ALIST_2) {
Eterm
ebif_plusplus_2(BIF_ALIST_2)
{
- return append(bif_export[BIF_ebif_plusplus_2], BIF_CALL_ARGS);
+ return append(&bif_trap_export[BIF_ebif_plusplus_2], BIF_CALL_ARGS);
}
BIF_RETTYPE append_2(BIF_ALIST_2)
{
- return append(bif_export[BIF_append_2], BIF_CALL_ARGS);
+ return append(&bif_trap_export[BIF_append_2], BIF_CALL_ARGS);
}
/* erlang:'--'/2
@@ -1039,11 +1038,11 @@ static Eterm subtract(Export *bif_entry, BIF_ALIST_2) {
}
BIF_RETTYPE ebif_minusminus_2(BIF_ALIST_2) {
- return subtract(bif_export[BIF_ebif_minusminus_2], BIF_CALL_ARGS);
+ return subtract(&bif_trap_export[BIF_ebif_minusminus_2], BIF_CALL_ARGS);
}
BIF_RETTYPE subtract_2(BIF_ALIST_2) {
- return subtract(bif_export[BIF_subtract_2], BIF_CALL_ARGS);
+ return subtract(&bif_trap_export[BIF_subtract_2], BIF_CALL_ARGS);
}
@@ -1068,7 +1067,7 @@ BIF_RETTYPE lists_member_2(BIF_ALIST_2)
while (is_list(list)) {
if (--max_iter < 0) {
BUMP_ALL_REDS(BIF_P);
- BIF_TRAP2(bif_export[BIF_lists_member_2], BIF_P, term, list);
+ BIF_TRAP2(&bif_trap_export[BIF_lists_member_2], BIF_P, term, list);
}
item = CAR(list_val(list));
if ((item == term) || (non_immed_key && eq(item, term))) {
@@ -1130,7 +1129,7 @@ static BIF_RETTYPE lists_reverse_alloc(Process *c_p,
}
ASSERT(is_list(tail) && cells_left == 0);
- BIF_TRAP2(bif_export[BIF_lists_reverse_2], c_p, list, tail);
+ BIF_TRAP2(&bif_trap_export[BIF_lists_reverse_2], c_p, list, tail);
}
static BIF_RETTYPE lists_reverse_onheap(Process *c_p,
@@ -1179,7 +1178,7 @@ static BIF_RETTYPE lists_reverse_onheap(Process *c_p,
}
BUMP_ALL_REDS(c_p);
- BIF_TRAP2(bif_export[BIF_lists_reverse_2], c_p, list, tail);
+ BIF_TRAP2(&bif_trap_export[BIF_lists_reverse_2], c_p, list, tail);
}
BIF_ERROR(c_p, BADARG);
@@ -1209,7 +1208,7 @@ lists_keymember_3(BIF_ALIST_3)
{
Eterm res;
- res = keyfind(BIF_lists_keymember_3, BIF_P,
+ res = keyfind(&bif_trap_export[BIF_lists_keymember_3], BIF_P,
BIF_ARG_1, BIF_ARG_2, BIF_ARG_3);
if (is_value(res) && is_tuple(res)) {
return am_true;
@@ -1223,7 +1222,7 @@ lists_keysearch_3(BIF_ALIST_3)
{
Eterm res;
- res = keyfind(BIF_lists_keysearch_3, BIF_P,
+ res = keyfind(&bif_trap_export[BIF_lists_keysearch_3], BIF_P,
BIF_ARG_1, BIF_ARG_2, BIF_ARG_3);
if (is_non_value(res) || is_not_tuple(res)) {
return res;
@@ -1236,12 +1235,12 @@ lists_keysearch_3(BIF_ALIST_3)
BIF_RETTYPE
lists_keyfind_3(BIF_ALIST_3)
{
- return keyfind(BIF_lists_keyfind_3, BIF_P,
+ return keyfind(&bif_trap_export[BIF_lists_keyfind_3], BIF_P,
BIF_ARG_1, BIF_ARG_2, BIF_ARG_3);
}
static Eterm
-keyfind(int Bif, Process* p, Eterm Key, Eterm Pos, Eterm List)
+keyfind(Export *Bif, Process* p, Eterm Key, Eterm Pos, Eterm List)
{
int max_iter = 10 * CONTEXT_REDS;
Sint pos;
@@ -1257,7 +1256,7 @@ keyfind(int Bif, Process* p, Eterm Key, Eterm Pos, Eterm List)
while (is_list(List)) {
if (--max_iter < 0) {
BUMP_ALL_REDS(p);
- BIF_TRAP3(bif_export[Bif], p, Key, Pos, List);
+ BIF_TRAP3(Bif, p, Key, Pos, List);
}
term = CAR(list_val(List));
List = CDR(list_val(List));
@@ -1282,7 +1281,7 @@ keyfind(int Bif, Process* p, Eterm Key, Eterm Pos, Eterm List)
while (is_list(List)) {
if (--max_iter < 0) {
BUMP_ALL_REDS(p);
- BIF_TRAP3(bif_export[Bif], p, Key, Pos, List);
+ BIF_TRAP3(Bif, p, Key, Pos, List);
}
term = CAR(list_val(List));
List = CDR(list_val(List));
@@ -1300,7 +1299,7 @@ keyfind(int Bif, Process* p, Eterm Key, Eterm Pos, Eterm List)
while (is_list(List)) {
if (--max_iter < 0) {
BUMP_ALL_REDS(p);
- BIF_TRAP3(bif_export[Bif], p, Key, Pos, List);
+ BIF_TRAP3(Bif, p, Key, Pos, List);
}
term = CAR(list_val(List));
List = CDR(list_val(List));
diff --git a/erts/emulator/beam/erl_bif_persistent.c b/erts/emulator/beam/erl_bif_persistent.c
index 9dc5c66a0a..2610833a33 100644
--- a/erts/emulator/beam/erl_bif_persistent.c
+++ b/erts/emulator/beam/erl_bif_persistent.c
@@ -45,23 +45,45 @@
#define MUST_SHRINK(t) (((Uint)200) * t->num_entries <= LOAD_FACTOR * t->allocated && \
t->allocated > INITIAL_SIZE)
+
+typedef struct delete_op {
+ enum { DELETE_OP_TUPLE, DELETE_OP_TABLE } type;
+ struct delete_op* next;
+ ErtsThrPrgrLaterOp thr_prog_op;
+ int is_scheduled;
+} DeleteOp;
+
typedef struct hash_table {
Uint allocated;
Uint num_entries;
Uint mask;
Uint first_to_delete;
Uint num_to_delete;
- erts_atomic_t refc;
- struct hash_table* delete_next;
- ErtsThrPrgrLaterOp thr_prog_op;
- Eterm term[1];
+ DeleteOp delete_op;
+ erts_atomic_t term[1];
} HashTable;
+static ERTS_INLINE Eterm get_bucket(HashTable* tab, Uint idx)
+{
+ return (Eterm) erts_atomic_read_nob(&tab->term[idx]);
+}
+
+static ERTS_INLINE void set_bucket(HashTable* tab, Uint idx, Eterm term)
+{
+ erts_atomic_set_nob(&tab->term[idx], (erts_aint_t)term);
+}
+
+static ERTS_INLINE Uint sizeof_HashTable(Uint sz)
+{
+ return offsetof(HashTable, term) + (sz * sizeof(erts_atomic_t));
+}
+
typedef struct trap_data {
HashTable* table;
Uint idx;
Uint remaining;
Uint memory; /* Used by info/0 to count used memory */
+ int got_update_permission;
} TrapData;
typedef enum {
@@ -89,8 +111,7 @@ typedef struct {
} ErtsPersistentTermCpyTableCtx;
typedef enum {
- PUT2_TRAP_LOCATION_NEW_KEY,
- PUT2_TRAP_LOCATION_REPLACE_VALUE
+ PUT2_TRAP_LOCATION_NEW_KEY
} ErtsPersistentTermPut2TrapLocation;
typedef struct {
@@ -117,6 +138,7 @@ typedef struct {
Uint entry_index;
Eterm old_term;
HashTable* tmp_table;
+ int must_shrink;
ErtsPersistentTermCpyTableCtx cpy_ctx;
} ErtsPersistentTermErase1Context;
@@ -126,20 +148,21 @@ typedef struct {
static HashTable* create_initial_table(void);
static Uint lookup(HashTable* hash_table, Eterm key);
+static int is_erasable(HashTable* hash_table, Uint idx);
static HashTable* copy_table(ErtsPersistentTermCpyTableCtx* ctx);
static int try_seize_update_permission(Process* c_p);
static void release_update_permission(int release_updater);
static void table_updater(void* table);
-static void table_deleter(void* hash_table);
-static void dec_table_refc(Process* c_p, HashTable* old_table);
-static void delete_table(Process* c_p, HashTable* table);
+static void scheduled_deleter(void* delete_op);
+static void delete_table(HashTable* table);
+static void delete_tuple(Eterm term);
static void mark_for_deletion(HashTable* hash_table, Uint entry_index);
static ErtsLiteralArea* term_to_area(Eterm tuple);
static void suspend_updater(Process* c_p);
static Eterm do_get_all(Process* c_p, TrapData* trap_data, Eterm res);
static Eterm do_info(Process* c_p, TrapData* trap_data);
-static void append_to_delete_queue(HashTable* table);
-static HashTable* next_to_delete(void);
+static void append_to_delete_queue(DeleteOp*);
+static DeleteOp* list_to_delete(DeleteOp*);
static Eterm alloc_trap_data(Process* c_p);
static int cleanup_trap_data(Binary *bp);
@@ -174,13 +197,16 @@ static Process* updater_process = NULL;
/* Protected by update_table_permission_mtx */
static ErtsThrPrgrLaterOp thr_prog_op;
+static Uint fast_update_index;
+static Eterm fast_update_term = THE_NON_VALUE;
+
/*
* Queue of hash tables to be deleted.
*/
static erts_mtx_t delete_queue_mtx;
-static HashTable* delete_queue_head = NULL;
-static HashTable** delete_queue_tail = &delete_queue_head;
+static DeleteOp* delete_queue_head = NULL;
+static DeleteOp** delete_queue_tail = &delete_queue_head;
/*
* The following variables are only used during crash dumping. They
@@ -284,7 +310,7 @@ BIF_RETTYPE persistent_term_put_2(BIF_ALIST_2)
long iterations_until_trap;
long max_iterations;
#define PUT_TRAP_CODE \
- BIF_TRAP2(bif_export[BIF_persistent_term_put_2], BIF_P, state_mref, BIF_ARG_2)
+ BIF_TRAP2(&bif_trap_export[BIF_persistent_term_put_2], BIF_P, state_mref, BIF_ARG_2)
#define TRAPPING_COPY_TABLE_PUT(TABLE_DEST, OLD_TABLE, NEW_SIZE, COPY_TYPE, LOC_NAME) \
TRAPPING_COPY_TABLE(TABLE_DEST, OLD_TABLE, NEW_SIZE, COPY_TYPE, LOC_NAME, PUT_TRAP_CODE)
@@ -306,12 +332,8 @@ BIF_RETTYPE persistent_term_put_2(BIF_ALIST_2)
ctx = ERTS_MAGIC_BIN_DATA(state_bin);
ASSERT(BIF_P->flags & F_DISABLE_GC);
erts_set_gc_state(BIF_P, 1);
- switch (ctx->trap_location) {
- case PUT2_TRAP_LOCATION_NEW_KEY:
- goto L_PUT2_TRAP_LOCATION_NEW_KEY;
- case PUT2_TRAP_LOCATION_REPLACE_VALUE:
- goto L_PUT2_TRAP_LOCATION_REPLACE_VALUE;
- }
+ ASSERT(ctx->trap_location == PUT2_TRAP_LOCATION_NEW_KEY);
+ goto L_PUT2_TRAP_LOCATION_NEW_KEY;
} else {
/* Save state in magic bin in case trapping is necessary */
Eterm* hp;
@@ -329,7 +351,7 @@ BIF_RETTYPE persistent_term_put_2(BIF_ALIST_2)
if (!try_seize_update_permission(BIF_P)) {
- ERTS_BIF_YIELD2(bif_export[BIF_persistent_term_put_2],
+ ERTS_BIF_YIELD2(&bif_trap_export[BIF_persistent_term_put_2],
BIF_P, BIF_ARG_1, BIF_ARG_2);
}
ctx->hash_table = (HashTable *) erts_atomic_read_nob(&the_hash_table);
@@ -344,20 +366,19 @@ BIF_RETTYPE persistent_term_put_2(BIF_ALIST_2)
ctx->heap[2] = ctx->term;
ctx->tuple = make_tuple(ctx->heap);
- if (is_nil(ctx->hash_table->term[ctx->entry_index])) {
- Uint new_size = ctx->hash_table->allocated;
+ if (is_nil(get_bucket(ctx->hash_table, ctx->entry_index))) {
if (MUST_GROW(ctx->hash_table)) {
- new_size *= 2;
+ Uint new_size = ctx->hash_table->allocated * 2;
+ TRAPPING_COPY_TABLE_PUT(ctx->hash_table,
+ ctx->hash_table,
+ new_size,
+ ERTS_PERSISTENT_TERM_CPY_NO_REHASH,
+ PUT2_TRAP_LOCATION_NEW_KEY);
+ ctx->entry_index = lookup(ctx->hash_table, ctx->key);
}
- TRAPPING_COPY_TABLE_PUT(ctx->hash_table,
- ctx->hash_table,
- new_size,
- ERTS_PERSISTENT_TERM_CPY_NO_REHASH,
- PUT2_TRAP_LOCATION_NEW_KEY);
- ctx->entry_index = lookup(ctx->hash_table, ctx->key);
ctx->hash_table->num_entries++;
} else {
- Eterm tuple = ctx->hash_table->term[ctx->entry_index];
+ Eterm tuple = get_bucket(ctx->hash_table, ctx->entry_index);
Eterm old_term;
ASSERT(is_tuple_arity(tuple, 2));
@@ -366,14 +387,6 @@ BIF_RETTYPE persistent_term_put_2(BIF_ALIST_2)
/* Same value. No need to update anything. */
release_update_permission(0);
BIF_RET(am_ok);
- } else {
- /* Mark the old term for deletion. */
- mark_for_deletion(ctx->hash_table, ctx->entry_index);
- TRAPPING_COPY_TABLE_PUT(ctx->hash_table,
- ctx->hash_table,
- ctx->hash_table->allocated,
- ERTS_PERSISTENT_TERM_CPY_NO_REHASH,
- PUT2_TRAP_LOCATION_REPLACE_VALUE);
}
}
@@ -402,8 +415,21 @@ BIF_RETTYPE persistent_term_put_2(BIF_ALIST_2)
literal_area->off_heap = code_off_heap.first;
DESTROY_SHCOPY(info);
erts_set_literal_tag(&ctx->tuple, literal_area->start, term_size);
- ctx->hash_table->term[ctx->entry_index] = ctx->tuple;
+ if (ctx->hash_table == (HashTable *) erts_atomic_read_nob(&the_hash_table)) {
+ /* Schedule fast update in active hash table */
+ fast_update_index = ctx->entry_index;
+ fast_update_term = ctx->tuple;
+ }
+ else {
+ /* Do update in copied table */
+ set_bucket(ctx->hash_table, ctx->entry_index, ctx->tuple);
+ }
+
+ /*
+ * Now wait thread progress before making update visible to guarantee
+ * consistent view of table&term without memory barrier in every get/1.
+ */
erts_schedule_thr_prgr_later_op(table_updater, ctx->hash_table, &thr_prog_op);
suspend_updater(BIF_P);
}
@@ -419,23 +445,25 @@ BIF_RETTYPE persistent_term_get_0(BIF_ALIST_0)
Eterm magic_ref;
Binary* mbp;
+ /* Prevent concurrent updates to get a consistent view */
+ if (!try_seize_update_permission(BIF_P)) {
+ ERTS_BIF_YIELD0(&bif_trap_export[BIF_persistent_term_get_0], BIF_P);
+ }
+
magic_ref = alloc_trap_data(BIF_P);
mbp = erts_magic_ref2bin(magic_ref);
trap_data = ERTS_MAGIC_BIN_DATA(mbp);
trap_data->table = hash_table;
trap_data->idx = 0;
trap_data->remaining = hash_table->num_entries;
+ trap_data->got_update_permission = 1;
res = do_get_all(BIF_P, trap_data, res);
if (trap_data->remaining == 0) {
+ release_update_permission(0);
+ trap_data->got_update_permission = 0;
BUMP_REDS(BIF_P, hash_table->num_entries);
- trap_data->table = NULL; /* Prevent refc decrement */
BIF_RET(res);
} else {
- /*
- * Increment the ref counter to prevent an update operation (by put/2
- * or erase/1) to delete this hash table.
- */
- erts_atomic_inc_nob(&hash_table->refc);
BUMP_ALL_REDS(BIF_P);
BIF_TRAP2(&persistent_term_get_all_export, BIF_P, magic_ref, res);
}
@@ -449,7 +477,7 @@ BIF_RETTYPE persistent_term_get_1(BIF_ALIST_1)
Eterm term;
entry_index = lookup(hash_table, key);
- term = hash_table->term[entry_index];
+ term = get_bucket(hash_table, entry_index);
if (is_boxed(term)) {
ASSERT(is_tuple_arity(term, 2));
BIF_RET(tuple_val(term)[2]);
@@ -466,7 +494,7 @@ BIF_RETTYPE persistent_term_get_2(BIF_ALIST_2)
Eterm term;
entry_index = lookup(hash_table, key);
- term = hash_table->term[entry_index];
+ term = get_bucket(hash_table, entry_index);
if (is_boxed(term)) {
ASSERT(is_tuple_arity(term, 2));
result = tuple_val(term)[2];
@@ -507,7 +535,7 @@ BIF_RETTYPE persistent_term_erase_1(BIF_ALIST_1)
ITERATIONS_PER_RED * ERTS_BIF_REDS_LEFT(BIF_P);
#endif
#define ERASE_TRAP_CODE \
- BIF_TRAP1(bif_export[BIF_persistent_term_erase_1], BIF_P, state_mref);
+ BIF_TRAP1(&bif_trap_export[BIF_persistent_term_erase_1], BIF_P, state_mref);
#define TRAPPING_COPY_TABLE_ERASE(TABLE_DEST, OLD_TABLE, NEW_SIZE, REHASH, LOC_NAME) \
TRAPPING_COPY_TABLE(TABLE_DEST, OLD_TABLE, NEW_SIZE, REHASH, LOC_NAME, ERASE_TRAP_CODE)
if (is_internal_magic_ref(BIF_ARG_1) &&
@@ -542,58 +570,75 @@ BIF_RETTYPE persistent_term_erase_1(BIF_ALIST_1)
ctx->tmp_table = NULL;
}
if (!try_seize_update_permission(BIF_P)) {
- ERTS_BIF_YIELD1(bif_export[BIF_persistent_term_erase_1],
+ ERTS_BIF_YIELD1(&bif_trap_export[BIF_persistent_term_erase_1],
BIF_P, BIF_ARG_1);
}
ctx->key = BIF_ARG_1;
ctx->old_table = (HashTable *) erts_atomic_read_nob(&the_hash_table);
ctx->entry_index = lookup(ctx->old_table, ctx->key);
- ctx->old_term = ctx->old_table->term[ctx->entry_index];
+ ctx->old_term = get_bucket(ctx->old_table, ctx->entry_index);
if (is_boxed(ctx->old_term)) {
- Uint new_size;
- /*
- * Since we don't use any delete markers, we must rehash
- * the table when deleting terms to ensure that all terms
- * can still be reached if there are hash collisions.
- * We can't rehash in place and it would not be safe to modify
- * the old table yet, so we will first need a new
- * temporary table copy of the same size as the old one.
- */
+ ctx->must_shrink = MUST_SHRINK(ctx->old_table);
+ if (!ctx->must_shrink && is_erasable(ctx->old_table, ctx->entry_index)) {
+ /*
+ * Fast erase in active hash table.
+ * We schedule with thread progress even here (see put/2).
+ * It's not needed for read consistenty of the NIL word, BUT it's
+ * needed to guarantee sequential read consistenty of multiple
+ * updates. As we do thread progress between all updates, there is
+ * no risk seeing them out of order.
+ */
+ fast_update_index = ctx->entry_index;
+ fast_update_term = NIL;
+ ctx->old_table->num_entries--;
+ erts_schedule_thr_prgr_later_op(table_updater, ctx->old_table, &thr_prog_op);
+ }
+ else {
+ Uint new_size;
+ /*
+ * Since we don't use any delete markers, we must rehash the table
+ * to ensure that all terms can still be reached if there are
+ * hash collisions.
+ * We can't rehash in place and it would not be safe to modify
+ * the old table yet, so we will first need a new
+ * temporary table copy of the same size as the old one.
+ */
- ASSERT(is_tuple_arity(ctx->old_term, 2));
- TRAPPING_COPY_TABLE_ERASE(ctx->tmp_table,
- ctx->old_table,
- ctx->old_table->allocated,
- ERTS_PERSISTENT_TERM_CPY_TEMP,
- ERASE1_TRAP_LOCATION_TMP_COPY);
+ ASSERT(is_tuple_arity(ctx->old_term, 2));
+ TRAPPING_COPY_TABLE_ERASE(ctx->tmp_table,
+ ctx->old_table,
+ ctx->old_table->allocated,
+ ERTS_PERSISTENT_TERM_CPY_TEMP,
+ ERASE1_TRAP_LOCATION_TMP_COPY);
- /*
- * Delete the term from the temporary table. Then copy the
- * temporary table to a new table, rehashing the entries
- * while copying.
- */
+ /*
+ * Delete the term from the temporary table. Then copy the
+ * temporary table to a new table, rehashing the entries
+ * while copying.
+ */
- ctx->tmp_table->term[ctx->entry_index] = NIL;
- ctx->tmp_table->num_entries--;
- new_size = ctx->tmp_table->allocated;
- if (MUST_SHRINK(ctx->tmp_table)) {
- new_size /= 2;
- }
- TRAPPING_COPY_TABLE_ERASE(ctx->new_table,
- ctx->tmp_table,
- new_size,
- ERTS_PERSISTENT_TERM_CPY_REHASH,
- ERASE1_TRAP_LOCATION_FINAL_COPY);
- erts_free(ERTS_ALC_T_PERSISTENT_TERM_TMP, ctx->tmp_table);
- /*
- * IMPORTANT: Memory management depends on that ctx->tmp_table
- * is set to NULL on the line below
- */
- ctx->tmp_table = NULL;
+ set_bucket(ctx->tmp_table, ctx->entry_index, NIL);
+ ctx->tmp_table->num_entries--;
+ new_size = ctx->tmp_table->allocated;
+ if (ctx->must_shrink) {
+ new_size /= 2;
+ }
+ TRAPPING_COPY_TABLE_ERASE(ctx->new_table,
+ ctx->tmp_table,
+ new_size,
+ ERTS_PERSISTENT_TERM_CPY_REHASH,
+ ERASE1_TRAP_LOCATION_FINAL_COPY);
+ erts_free(ERTS_ALC_T_PERSISTENT_TERM_TMP, ctx->tmp_table);
+ /*
+ * IMPORTANT: Memory management depends on that ctx->tmp_table
+ * is set to NULL on the line below
+ */
+ ctx->tmp_table = NULL;
- mark_for_deletion(ctx->old_table, ctx->entry_index);
- erts_schedule_thr_prgr_later_op(table_updater, ctx->new_table, &thr_prog_op);
+ mark_for_deletion(ctx->old_table, ctx->entry_index);
+ erts_schedule_thr_prgr_later_op(table_updater, ctx->new_table, &thr_prog_op);
+ }
suspend_updater(BIF_P);
BUMP_REDS(BIF_P, (max_iterations - iterations_until_trap) / ITERATIONS_PER_RED);
ERTS_BIF_YIELD_RETURN(BIF_P, am_true);
@@ -614,7 +659,7 @@ BIF_RETTYPE erts_internal_erase_persistent_terms_0(BIF_ALIST_0)
HashTable* new_table;
if (!try_seize_update_permission(BIF_P)) {
- ERTS_BIF_YIELD0(bif_export[BIF_erts_internal_erase_persistent_terms_0],
+ ERTS_BIF_YIELD0(&bif_trap_export[BIF_erts_internal_erase_persistent_terms_0],
BIF_P);
}
old_table = (HashTable *) erts_atomic_read_nob(&the_hash_table);
@@ -634,6 +679,11 @@ BIF_RETTYPE persistent_term_info_0(BIF_ALIST_0)
Eterm magic_ref;
Binary* mbp;
+ /* Prevent concurrent updates to get a consistent view */
+ if (!try_seize_update_permission(BIF_P)) {
+ ERTS_BIF_YIELD0(&bif_trap_export[BIF_persistent_term_info_0], BIF_P);
+ }
+
magic_ref = alloc_trap_data(BIF_P);
mbp = erts_magic_ref2bin(magic_ref);
trap_data = ERTS_MAGIC_BIN_DATA(mbp);
@@ -641,29 +691,19 @@ BIF_RETTYPE persistent_term_info_0(BIF_ALIST_0)
trap_data->idx = 0;
trap_data->remaining = hash_table->num_entries;
trap_data->memory = 0;
+ trap_data->got_update_permission = 0;
res = do_info(BIF_P, trap_data);
if (trap_data->remaining == 0) {
+ release_update_permission(0);
+ trap_data->got_update_permission = 0;
BUMP_REDS(BIF_P, hash_table->num_entries);
- trap_data->table = NULL; /* Prevent refc decrement */
BIF_RET(res);
} else {
- /*
- * Increment the ref counter to prevent an update operation (by put/2
- * or erase/1) to delete this hash table.
- */
- erts_atomic_inc_nob(&hash_table->refc);
BUMP_ALL_REDS(BIF_P);
BIF_TRAP2(&persistent_term_info_export, BIF_P, magic_ref, res);
}
}
-Uint
-erts_persistent_term_count(void)
-{
- HashTable* hash_table = (HashTable *) erts_atomic_read_nob(&the_hash_table);
- return hash_table->num_entries;
-}
-
void
erts_init_persistent_dumping(void)
{
@@ -677,15 +717,15 @@ erts_init_persistent_dumping(void)
*/
erts_persistent_areas = (ErtsLiteralArea **) hash_table->term;
- erts_num_persistent_areas = hash_table->num_entries;
area_p = erts_persistent_areas;
for (i = 0; i < hash_table->allocated; i++) {
- Eterm term = hash_table->term[i];
+ Eterm term = get_bucket(hash_table, i);
if (is_boxed(term)) {
*area_p++ = term_to_area(term);
}
}
+ erts_num_persistent_areas = area_p - erts_persistent_areas;
}
/*
@@ -699,16 +739,14 @@ create_initial_table(void)
int i;
hash_table = (HashTable *) erts_alloc(ERTS_ALC_T_PERSISTENT_TERM,
- sizeof(HashTable)+sizeof(Eterm) *
- (INITIAL_SIZE-1));
+ sizeof_HashTable(INITIAL_SIZE));
hash_table->allocated = INITIAL_SIZE;
hash_table->num_entries = 0;
hash_table->mask = INITIAL_SIZE-1;
hash_table->first_to_delete = 0;
hash_table->num_to_delete = 0;
- erts_atomic_init_nob(&hash_table->refc, (erts_aint_t)1);
for (i = 0; i < INITIAL_SIZE; i++) {
- hash_table->term[i] = NIL;
+ erts_atomic_init_nob(&hash_table->term[i], NIL);
}
return hash_table;
}
@@ -731,12 +769,8 @@ persistent_term_get_all_trap(BIF_ALIST_2)
BUMP_ALL_REDS(BIF_P);
BIF_TRAP2(&persistent_term_get_all_export, BIF_P, BIF_ARG_1, res);
} else {
- /*
- * Decrement ref count (and possibly delete the hash table
- * and associated literal area).
- */
- dec_table_refc(BIF_P, trap_data->table);
- trap_data->table = NULL; /* Prevent refc decrement */
+ release_update_permission(0);
+ trap_data->got_update_permission = 0;
BUMP_REDS(BIF_P, bump_reds);
BIF_RET(res);
}
@@ -774,7 +808,9 @@ do_get_all(Process* c_p, TrapData* trap_data, Eterm res)
i = 0;
heap_size = (2 + 3) * remaining;
while (remaining != 0) {
- Eterm term = hash_table->term[idx];
+ Eterm term;
+ ASSERT(idx < hash_table->allocated);
+ term = get_bucket(hash_table, idx);
if (is_tuple(term)) {
Uint key_size;
Eterm* tup_val;
@@ -829,12 +865,8 @@ persistent_term_info_trap(BIF_ALIST_1)
BUMP_ALL_REDS(BIF_P);
BIF_TRAP1(&persistent_term_info_export, BIF_P, BIF_ARG_1);
} else {
- /*
- * Decrement ref count (and possibly delete the hash table
- * and associated literal area).
- */
- dec_table_refc(BIF_P, trap_data->table);
- trap_data->table = NULL; /* Prevent refc decrement */
+ release_update_permission(0);
+ trap_data->got_update_permission = 0;
BUMP_REDS(BIF_P, bump_reds);
ASSERT(is_map(res));
BIF_RET(res);
@@ -861,9 +893,9 @@ do_info(Process* c_p, TrapData* trap_data)
remaining = trap_data->remaining < max_iter ? trap_data->remaining : max_iter;
trap_data->remaining -= remaining;
while (remaining != 0) {
- if (is_boxed(hash_table->term[idx])) {
+ if (is_boxed(get_bucket(hash_table, idx))) {
ErtsLiteralArea* area;
- area = term_to_area(hash_table->term[idx]);
+ area = term_to_area(get_bucket(hash_table, idx));
trap_data->memory += sizeof(ErtsLiteralArea) +
sizeof(Eterm) * (area->end - area->start - 1);
remaining--;
@@ -911,13 +943,8 @@ cleanup_trap_data(Binary *bp)
{
TrapData* trap_data = ERTS_MAGIC_BIN_DATA(bp);
- if (trap_data->table) {
- /*
- * The process has been killed and is now exiting.
- * Decrement the reference counter for the table.
- */
- dec_table_refc(NULL, trap_data->table);
- }
+ if (trap_data->got_update_permission)
+ release_update_permission(0);
return 1;
}
@@ -925,17 +952,26 @@ static Uint
lookup(HashTable* hash_table, Eterm key)
{
Uint mask = hash_table->mask;
- Eterm* table = hash_table->term;
Uint32 idx = make_internal_hash(key, 0);
Eterm term;
- do {
+ while (1) {
+ term = get_bucket(hash_table, idx & mask);
+ if (is_nil(term) || EQ(key, (tuple_val(term))[1]))
+ break;
idx++;
- term = table[idx & mask];
- } while (is_boxed(term) && !EQ(key, (tuple_val(term))[1]));
+ }
return idx & mask;
}
+static int
+is_erasable(HashTable* hash_table, Uint idx)
+{
+ /* It's ok to erase [idx] if it's not a stepping stone to [idx+1] */
+ return get_bucket(hash_table, (idx + 1) & hash_table->mask) == NIL;
+}
+
+
static HashTable*
copy_table(ErtsPersistentTermCpyTableCtx* ctx)
{
@@ -956,8 +992,7 @@ copy_table(ErtsPersistentTermCpyTableCtx* ctx)
alloc_type = ERTS_ALC_T_PERSISTENT_TERM;
}
ctx->new_table = (HashTable *) erts_alloc(alloc_type,
- sizeof(HashTable) +
- sizeof(Eterm) * (ctx->new_size-1));
+ sizeof_HashTable(ctx->new_size));
if (ctx->old_table->allocated == ctx->new_size &&
(ctx->copy_type == ERTS_PERSISTENT_TERM_CPY_NO_REHASH ||
ctx->copy_type == ERTS_PERSISTENT_TERM_CPY_TEMP)) {
@@ -970,7 +1005,8 @@ copy_table(ErtsPersistentTermCpyTableCtx* ctx)
i < MIN(ctx->iterations_done + ctx->max_iterations,
ctx->new_size);
i++) {
- ctx->new_table->term[i] = ctx->old_table->term[i];
+ erts_atomic_init_nob(&ctx->new_table->term[i],
+ erts_atomic_read_nob(&ctx->old_table->term[i]));
}
ctx->total_iterations_done = (i - ctx->iterations_done);
if (i < ctx->new_size) {
@@ -993,7 +1029,7 @@ copy_table(ErtsPersistentTermCpyTableCtx* ctx)
i < MIN(ctx->iterations_done + ctx->max_iterations,
ctx->new_size);
i++) {
- ctx->new_table->term[i] = NIL;
+ erts_atomic_init_nob(&ctx->new_table->term[i], (erts_aint_t)NIL);
}
ctx->total_iterations_done = (i - ctx->iterations_done);
ctx->max_iterations -= ctx->total_iterations_done;
@@ -1008,11 +1044,12 @@ copy_table(ErtsPersistentTermCpyTableCtx* ctx)
i < MIN(ctx->iterations_done + ctx->max_iterations,
old_size);
i++) {
- if (is_tuple(ctx->old_table->term[i])) {
- Eterm key = tuple_val(ctx->old_table->term[i])[1];
+ Eterm term = get_bucket(ctx->old_table, i);
+ if (is_tuple(term)) {
+ Eterm key = tuple_val(term)[1];
Uint entry_index = lookup(ctx->new_table, key);
- ASSERT(is_nil(ctx->new_table->term[entry_index]));
- ctx->new_table->term[entry_index] = ctx->old_table->term[i];
+ ASSERT(is_nil(get_bucket(ctx->new_table, entry_index)));
+ set_bucket(ctx->new_table, entry_index, term);
}
}
ctx->total_iterations_done += (i - ctx->iterations_done);
@@ -1025,7 +1062,6 @@ copy_table(ErtsPersistentTermCpyTableCtx* ctx)
}
ctx->new_table->first_to_delete = 0;
ctx->new_table->num_to_delete = 0;
- erts_atomic_init_nob(&ctx->new_table->refc, (erts_aint_t)1);
{
HashTable* new_table = ctx->new_table;
/*
@@ -1052,47 +1088,118 @@ term_to_area(Eterm tuple)
offsetof(ErtsLiteralArea, start));
}
+typedef struct {
+ Eterm term;
+ ErtsLiteralArea* area;
+ DeleteOp delete_op;
+} OldLiteral;
+
+static OldLiteral* alloc_old_literal(void)
+{
+ return erts_alloc(ERTS_ALC_T_RELEASE_LAREA, sizeof(OldLiteral));
+}
+
+static void free_old_literal(OldLiteral* olp)
+{
+ return erts_free(ERTS_ALC_T_RELEASE_LAREA, olp);
+}
+
static void
table_updater(void* data)
{
HashTable* old_table;
HashTable* new_table;
+ UWord cleanup_bytes;
old_table = (HashTable *) erts_atomic_read_nob(&the_hash_table);
new_table = (HashTable *) data;
- ASSERT(new_table->num_to_delete == 0);
- erts_atomic_set_nob(&the_hash_table, (erts_aint_t)new_table);
- append_to_delete_queue(old_table);
- erts_schedule_thr_prgr_later_op(table_deleter,
- old_table,
- &old_table->thr_prog_op);
- release_update_permission(1);
-}
-
-static void
-table_deleter(void* data)
-{
- HashTable* old_table = (HashTable *) data;
+ if (new_table == old_table) {
+ Eterm old_term = get_bucket(old_table, fast_update_index);
+ ASSERT(is_value(fast_update_term));
+ ASSERT(fast_update_index < old_table->allocated);
+ set_bucket(old_table, fast_update_index, fast_update_term);
+#ifdef DEBUG
+ fast_update_term = THE_NON_VALUE;
+#endif
- dec_table_refc(NULL, old_table);
+ if (is_not_nil(old_term)) {
+ OldLiteral *olp = alloc_old_literal();
+ ASSERT(is_tuple_arity(old_term,2));
+ olp->term = old_term;
+ olp->area = term_to_area(old_term);
+ olp->delete_op.type = DELETE_OP_TUPLE;
+ olp->delete_op.is_scheduled = 1;
+ append_to_delete_queue(&olp->delete_op);
+ cleanup_bytes = (ERTS_LITERAL_AREA_SIZE(olp->area)
+ + sizeof(OldLiteral));
+ erts_schedule_thr_prgr_later_cleanup_op(scheduled_deleter,
+ &olp->delete_op,
+ &olp->delete_op.thr_prog_op,
+ cleanup_bytes);
+ }
+ }
+ else {
+ ASSERT(is_non_value(fast_update_term));
+ ASSERT(new_table->num_to_delete == 0);
+ erts_atomic_set_nob(&the_hash_table, (erts_aint_t)new_table);
+ old_table->delete_op.type = DELETE_OP_TABLE;
+ old_table->delete_op.is_scheduled = 1;
+ append_to_delete_queue(&old_table->delete_op);
+ cleanup_bytes = sizeof_HashTable(old_table->allocated);
+ if (old_table->num_to_delete <= 1) {
+ if (old_table->num_to_delete == 1) {
+ ErtsLiteralArea* area;
+ area = term_to_area(get_bucket(old_table,
+ old_table->first_to_delete));
+ cleanup_bytes += ERTS_LITERAL_AREA_SIZE(area);
+ }
+ erts_schedule_thr_prgr_later_cleanup_op(scheduled_deleter,
+ &old_table->delete_op,
+ &old_table->delete_op.thr_prog_op,
+ cleanup_bytes);
+ }
+ else {
+ /* Only at init:restart(). Don't bother with total cleanup size. */
+ ASSERT(old_table->num_to_delete == old_table->allocated);
+ erts_schedule_thr_prgr_later_op(scheduled_deleter,
+ &old_table->delete_op,
+ &old_table->delete_op.thr_prog_op);
+ }
+ }
+ release_update_permission(1);
}
static void
-dec_table_refc(Process* c_p, HashTable* old_table)
+scheduled_deleter(void* data)
{
- erts_aint_t refc = erts_atomic_dec_read_nob(&old_table->refc);
-
- if (refc == 0) {
- HashTable* to_delete;
-
- while ((to_delete = next_to_delete()) != NULL) {
- delete_table(c_p, to_delete);
+ DeleteOp* dop = (DeleteOp*)data;
+
+ dop = list_to_delete(dop);
+
+ while (dop) {
+ DeleteOp* next = dop->next;
+ ASSERT(!dop->is_scheduled);
+ switch (dop->type) {
+ case DELETE_OP_TUPLE: {
+ OldLiteral* olp = ErtsContainerStruct(dop, OldLiteral, delete_op);
+ delete_tuple(olp->term);
+ free_old_literal(olp);
+ break;
+ }
+ case DELETE_OP_TABLE: {
+ HashTable* table = ErtsContainerStruct(dop, HashTable, delete_op);
+ delete_table(table);
+ break;
+ }
+ default:
+ ASSERT(!!"Invalid DeleteOp");
}
+ dop = next;
}
}
static void
-delete_table(Process* c_p, HashTable* table)
+delete_table(HashTable* table)
{
Uint idx = table->first_to_delete;
Uint n = table->num_to_delete;
@@ -1106,25 +1213,32 @@ delete_table(Process* c_p, HashTable* table)
#ifdef DEBUG
if (n == 1) {
- ASSERT(is_tuple_arity(table->term[idx], 2));
+ ASSERT(is_tuple_arity(get_bucket(table, idx), 2));
}
#endif
while (n > 0) {
- Eterm term = table->term[idx];
-
- if (is_tuple_arity(term, 2)) {
- if (is_immed(tuple_val(term)[2])) {
- erts_release_literal_area(term_to_area(term));
- } else {
- erts_queue_release_literals(c_p, term_to_area(term));
- }
- }
+ delete_tuple(get_bucket(table, idx));
idx++, n--;
}
erts_free(ERTS_ALC_T_PERSISTENT_TERM, table);
}
+static void
+delete_tuple(Eterm term)
+{
+ if (is_tuple_arity(term, 2)) {
+ if (is_immed(tuple_val(term)[2])) {
+ erts_release_literal_area(term_to_area(term));
+ } else {
+ erts_queue_release_literals(NULL, term_to_area(term));
+ }
+ }
+ else {
+ ASSERT(is_nil(term));
+ }
+}
+
/*
* Caller *must* yield if this function returns 0.
*/
@@ -1199,42 +1313,46 @@ suspend_updater(Process* c_p)
}
static void
-append_to_delete_queue(HashTable* table)
+append_to_delete_queue(DeleteOp* dop)
{
erts_mtx_lock(&delete_queue_mtx);
- table->delete_next = NULL;
- *delete_queue_tail = table;
- delete_queue_tail = &table->delete_next;
+ dop->next = NULL;
+ *delete_queue_tail = dop;
+ delete_queue_tail = &dop->next;
erts_mtx_unlock(&delete_queue_mtx);
}
-static HashTable*
-next_to_delete(void)
+static DeleteOp*
+list_to_delete(DeleteOp* scheduled_dop)
{
- HashTable* table;
+ DeleteOp* dop;
+ DeleteOp* dop_list;
erts_mtx_lock(&delete_queue_mtx);
- table = delete_queue_head;
- if (table) {
- if (erts_atomic_read_nob(&table->refc)) {
- /*
- * This hash table is still referenced. Hash tables
- * must be deleted in order, so we return a NULL
- * pointer.
- */
- table = NULL;
- } else {
- /*
- * Remove the first hash table from the queue.
- */
- delete_queue_head = table->delete_next;
- if (delete_queue_head == NULL) {
- delete_queue_tail = &delete_queue_head;
- }
- }
+ ASSERT(delete_queue_head && delete_queue_head->is_scheduled);
+ ASSERT(scheduled_dop->is_scheduled);
+ scheduled_dop->is_scheduled = 0;
+
+ if (scheduled_dop == delete_queue_head) {
+ dop = delete_queue_head;
+ while (dop->next && !dop->next->is_scheduled)
+ dop = dop->next;
+
+ /*
+ * Remove list of ripe delete ops.
+ */
+ dop_list = delete_queue_head;
+ delete_queue_head = dop->next;
+ dop->next = NULL;
+ if (delete_queue_head == NULL)
+ delete_queue_tail = &delete_queue_head;
+ }
+ else {
+ dop_list = NULL;
}
erts_mtx_unlock(&delete_queue_mtx);
- return table;
+
+ return dop_list;
}
/*
@@ -1269,25 +1387,53 @@ erts_debug_save_accessed_literal_area(ErtsLiteralArea *lap)
accessed_literal_areas[accessed_no_literal_areas++] = lap;
}
-static void debug_foreach_off_heap(HashTable *tbl, void (*func)(ErlOffHeap *, void *), void *arg)
+static void debug_area_off_heap(ErtsLiteralArea* lap,
+ void (*func)(ErlOffHeap *, void *),
+ void *arg)
+{
+ ErlOffHeap oh;
+ if (!erts_debug_have_accessed_literal_area(lap)) {
+ ERTS_INIT_OFF_HEAP(&oh);
+ oh.first = lap->off_heap;
+ (*func)(&oh, arg);
+ erts_debug_save_accessed_literal_area(lap);
+ }
+}
+
+static void debug_table_foreach_off_heap(HashTable *tbl,
+ void (*func)(ErlOffHeap *, void *),
+ void *arg)
{
int i;
for (i = 0; i < tbl->allocated; i++) {
- Eterm term = tbl->term[i];
+ Eterm term = get_bucket(tbl, i);
if (is_tuple_arity(term, 2)) {
- ErtsLiteralArea *lap = term_to_area(term);
- ErlOffHeap oh;
- if (!erts_debug_have_accessed_literal_area(lap)) {
- ERTS_INIT_OFF_HEAP(&oh);
- oh.first = lap->off_heap;
- (*func)(&oh, arg);
- erts_debug_save_accessed_literal_area(lap);
- }
+ debug_area_off_heap(term_to_area(term), func, arg);
}
}
}
+static void debug_delete_op_foreach_off_heap(DeleteOp *dop,
+ void (*func)(ErlOffHeap *, void *),
+ void *arg)
+{
+ switch (dop->type) {
+ case DELETE_OP_TABLE: {
+ HashTable* table = ErtsContainerStruct(dop, HashTable, delete_op);
+ debug_table_foreach_off_heap(table, func, arg);
+ break;
+ }
+ case DELETE_OP_TUPLE: {
+ OldLiteral* olp = ErtsContainerStruct(dop, OldLiteral, delete_op);
+ debug_area_off_heap(olp->area, func, arg);
+ break;
+ }
+ default:
+ ASSERT(!!"Invalid DeleteOp");
+ }
+}
+
struct debug_la_oh {
void (*func)(ErlOffHeap *, void *);
void *arg;
@@ -1299,13 +1445,16 @@ static void debug_handle_table(void *vfap,
{
struct debug_la_oh *fap = vfap;
HashTable *tbl = vtbl;
- debug_foreach_off_heap(tbl, fap->func, fap->arg);
+ debug_table_foreach_off_heap(tbl, fap->func, fap->arg);
}
+
void
-erts_debug_foreach_persistent_term_off_heap(void (*func)(ErlOffHeap *, void *), void *arg)
+erts_debug_foreach_persistent_term_off_heap(void (*func)(ErlOffHeap *, void *),
+ void *arg)
{
HashTable *tbl;
+ DeleteOp *dop;
struct debug_la_oh fa;
accessed_no_literal_areas = 0;
accessed_literal_areas_size = 10;
@@ -1314,19 +1463,16 @@ erts_debug_foreach_persistent_term_off_heap(void (*func)(ErlOffHeap *, void *),
* accessed_literal_areas_size));
tbl = (HashTable *) erts_atomic_read_nob(&the_hash_table);
- debug_foreach_off_heap(tbl, func, arg);
+ debug_table_foreach_off_heap(tbl, func, arg);
erts_mtx_lock(&delete_queue_mtx);
- for (tbl = delete_queue_head; tbl; tbl = tbl->delete_next)
- debug_foreach_off_heap(tbl, func, arg);
+ for (dop = delete_queue_head; dop; dop = dop->next)
+ debug_delete_op_foreach_off_heap(dop, func, arg);
erts_mtx_unlock(&delete_queue_mtx);
fa.func = func;
fa.arg = arg;
erts_debug_later_op_foreach(table_updater,
debug_handle_table,
(void *) &fa);
- erts_debug_later_op_foreach(table_deleter,
- debug_handle_table,
- (void *) &fa);
erts_debug_foreach_release_literal_area_off_heap(func, arg);
erts_free(ERTS_ALC_T_TMP, accessed_literal_areas);
diff --git a/erts/emulator/beam/erl_bif_port.c b/erts/emulator/beam/erl_bif_port.c
index 63bfaf8572..a989ba97ec 100644
--- a/erts/emulator/beam/erl_bif_port.c
+++ b/erts/emulator/beam/erl_bif_port.c
@@ -44,6 +44,7 @@
#include "erl_bif_unique.h"
#include "dtrace-wrapper.h"
#include "erl_proc_sig_queue.h"
+#include "erl_osenv.h"
static Port *open_port(Process* p, Eterm name, Eterm settings, int *err_typep, int *err_nump);
static int merge_global_environment(erts_osenv_t *env, Eterm key_value_pairs);
@@ -210,7 +211,7 @@ BIF_RETTYPE erts_internal_port_command_3(BIF_ALIST_3)
ERTS_BIF_PREP_RET(res, am_false);
else {
erts_suspend(BIF_P, ERTS_PROC_LOCK_MAIN, prt);
- ERTS_BIF_PREP_YIELD3(res, bif_export[BIF_erts_internal_port_command_3],
+ ERTS_BIF_PREP_YIELD3(res, &bif_trap_export[BIF_erts_internal_port_command_3],
BIF_P, BIF_ARG_1, BIF_ARG_2, BIF_ARG_3);
}
break;
@@ -1288,22 +1289,25 @@ static int http_request_erl(void* arg, const http_atom_t* meth,
}
static int
-http_header_erl(void* arg, const http_atom_t* name, const char* name_ptr,
- int name_len, const char* value_ptr, int value_len)
+http_header_erl(void* arg, const http_atom_t* name,
+ const char* name_ptr, int name_len,
+ const char* oname_ptr, int oname_len,
+ const char* value_ptr, int value_len)
{
struct packet_callback_args* pca = (struct packet_callback_args*) arg;
- Eterm bit_term, name_term, val_term;
+ Eterm bit_term, name_term, oname_term, val_term;
Uint sz = 6;
Eterm* hp;
#ifdef DEBUG
Eterm* hend;
#endif
- /* {http_header,Bit,Name,IValue,Value} */
+ /* {http_header,Bit,Name,Oname,Value} */
if (name == NULL) {
http_bld_string(pca, NULL, &sz, name_ptr, name_len);
}
+ http_bld_string(pca, NULL, &sz, oname_ptr, oname_len);
http_bld_string(pca, NULL, &sz, value_ptr, value_len);
hp = HAlloc(pca->p, sz);
@@ -1320,8 +1324,9 @@ http_header_erl(void* arg, const http_atom_t* name, const char* name_ptr,
name_term = http_bld_string(pca, &hp,NULL,name_ptr,name_len);
}
+ oname_term = http_bld_string(pca, &hp, NULL, oname_ptr, oname_len);
val_term = http_bld_string(pca, &hp, NULL, value_ptr, value_len);
- pca->res = TUPLE5(hp, am_http_header, bit_term, name_term, am_undefined, val_term);
+ pca->res = TUPLE5(hp, am_http_header, bit_term, name_term, oname_term, val_term);
ASSERT(hp+6==hend);
return 1;
}
diff --git a/erts/emulator/beam/erl_bif_trace.c b/erts/emulator/beam/erl_bif_trace.c
index 4162a6c591..e0777de298 100644
--- a/erts/emulator/beam/erl_bif_trace.c
+++ b/erts/emulator/beam/erl_bif_trace.c
@@ -80,9 +80,6 @@ static Eterm trace_info_func(Process* p, Eterm pid_spec, Eterm key);
static Eterm trace_info_on_load(Process* p, Eterm key);
static Eterm trace_info_event(Process* p, Eterm event, Eterm key);
-
-static void reset_bif_trace(void);
-static void setup_bif_trace(void);
static void install_exp_breakpoints(BpFunctions* f);
static void uninstall_exp_breakpoints(BpFunctions* f);
static void clean_export_entries(BpFunctions* f);
@@ -133,7 +130,7 @@ trace_pattern(Process* p, Eterm MFA, Eterm Pattern, Eterm flaglist)
ErtsTracer meta_tracer = erts_tracer_nil;
if (!erts_try_seize_code_write_permission(p)) {
- ERTS_BIF_YIELD3(bif_export[BIF_erts_internal_trace_pattern_3], p, MFA, Pattern, flaglist);
+ ERTS_BIF_YIELD3(&bif_trap_export[BIF_erts_internal_trace_pattern_3], p, MFA, Pattern, flaglist);
}
finish_bp.current = -1;
@@ -543,7 +540,7 @@ Eterm erts_internal_trace_3(BIF_ALIST_3)
}
if (!erts_try_seize_code_write_permission(BIF_P)) {
- ERTS_BIF_YIELD3(bif_export[BIF_erts_internal_trace_3],
+ ERTS_BIF_YIELD3(&bif_trap_export[BIF_erts_internal_trace_3],
BIF_P, BIF_ARG_1, BIF_ARG_2, BIF_ARG_3);
}
@@ -793,7 +790,7 @@ Eterm trace_info_2(BIF_ALIST_2)
Eterm res;
if (!erts_try_seize_code_write_permission(p)) {
- ERTS_BIF_YIELD2(bif_export[BIF_trace_info_2], p, What, Key);
+ ERTS_BIF_YIELD2(&bif_trap_export[BIF_trace_info_2], p, What, Key);
}
if (What == am_on_load) {
@@ -1047,14 +1044,13 @@ static int function_is_traced(Process *p,
e.info.mfa.function = mfa[1];
e.info.mfa.arity = mfa[2];
if ((ep = export_get(&e)) != NULL) {
- pc = ep->beam;
+ pc = ep->trampoline.raw;
if (ep->addressv[erts_active_code_ix()] == pc &&
! BeamIsOpCode(*pc, op_call_error_handler)) {
int r = 0;
- ASSERT(BeamIsOpCode(*pc, op_apply_bif) ||
- BeamIsOpCode(*pc, op_i_generic_breakpoint));
+ ASSERT(BeamIsOpCode(*pc, op_i_generic_breakpoint));
if (erts_is_trace_break(&ep->info, ms, 0)) {
return FUNC_TRACE_GLOBAL_TRACE;
@@ -1426,18 +1422,21 @@ erts_set_trace_pattern(Process*p, ErtsCodeMFA *mfa, int specified,
int n;
BpFunction* fp;
- /*
- * First work on normal functions (not real BIFs).
- */
-
erts_bp_match_export(&finish_bp.e, mfa, specified);
fp = finish_bp.e.matching;
n = finish_bp.e.matched;
for (i = 0; i < n; i++) {
ErtsCodeInfo *ci = fp[i].ci;
- BeamInstr* pc = erts_codeinfo_to_code(ci);
- Export* ep = ErtsContainerStruct(ci, Export, info);
+ BeamInstr* pc;
+ Export* ep;
+
+ pc = erts_codeinfo_to_code(ci);
+ ep = ErtsContainerStruct(ci, Export, info);
+
+ if (ep->bif_number != -1) {
+ ep->is_bif_traced = !!on;
+ }
if (on && !flags.breakpoint) {
/* Turn on global call tracing */
@@ -1446,12 +1445,12 @@ erts_set_trace_pattern(Process*p, ErtsCodeMFA *mfa, int specified,
#ifdef DEBUG
ep->info.op = BeamOpCodeAddr(op_i_func_info_IaaI);
#endif
- ep->beam[0] = BeamOpCodeAddr(op_trace_jump_W);
- ep->beam[1] = (BeamInstr) ep->addressv[code_ix];
+ ep->trampoline.op = BeamOpCodeAddr(op_trace_jump_W);
+ ep->trampoline.trace.address = (BeamInstr) ep->addressv[code_ix];
}
- erts_set_call_trace_bif(ci, match_prog_set, 0);
+ erts_set_export_trace(ci, match_prog_set, 0);
if (ep->addressv[code_ix] != pc) {
- ep->beam[0] = BeamOpCodeAddr(op_i_generic_breakpoint);
+ ep->trampoline.op = BeamOpCodeAddr(op_i_generic_breakpoint);
}
} else if (!on && flags.breakpoint) {
/* Turn off breakpoint tracing -- nothing to do here. */
@@ -1460,91 +1459,14 @@ erts_set_trace_pattern(Process*p, ErtsCodeMFA *mfa, int specified,
* Turn off global tracing, either explicitly or implicitly
* before turning on breakpoint tracing.
*/
- erts_clear_call_trace_bif(ci, 0);
- if (BeamIsOpCode(ep->beam[0], op_i_generic_breakpoint)) {
- ep->beam[0] = BeamOpCodeAddr(op_trace_jump_W);
+ erts_clear_export_trace(ci, 0);
+ if (BeamIsOpCode(ep->trampoline.op, op_i_generic_breakpoint)) {
+ ep->trampoline.op = BeamOpCodeAddr(op_trace_jump_W);
}
}
}
/*
- ** OK, now for the bif's
- */
- for (i = 0; i < BIF_SIZE; ++i) {
- Export *ep = bif_export[i];
-
- if (!ExportIsBuiltIn(ep)) {
- continue;
- }
-
- if (bif_table[i].f == bif_table[i].traced) {
- /* Trace wrapper same as regular function - untraceable */
- continue;
- }
-
- switch (specified) {
- case 3:
- if (mfa->arity != ep->info.mfa.arity)
- continue;
- case 2:
- if (mfa->function != ep->info.mfa.function)
- continue;
- case 1:
- if (mfa->module != ep->info.mfa.module)
- continue;
- case 0:
- break;
- default:
- ASSERT(0);
- }
-
- if (! flags.breakpoint) { /* Export entry call trace */
- if (on) {
- erts_clear_call_trace_bif(&ep->info, 1);
- erts_clear_mtrace_bif(&ep->info);
- erts_set_call_trace_bif(&ep->info, match_prog_set, 0);
- } else { /* off */
- erts_clear_call_trace_bif(&ep->info, 0);
- }
- matches++;
- } else { /* Breakpoint call trace */
- int m = 0;
-
- if (on) {
- if (flags.local) {
- erts_clear_call_trace_bif(&ep->info, 0);
- erts_set_call_trace_bif(&ep->info, match_prog_set, 1);
- m = 1;
- }
- if (flags.meta) {
- erts_set_mtrace_bif(&ep->info, meta_match_prog_set,
- meta_tracer);
- m = 1;
- }
- if (flags.call_time) {
- erts_set_time_trace_bif(&ep->info, on);
- /* I don't want to remove any other tracers */
- m = 1;
- }
- } else { /* off */
- if (flags.local) {
- erts_clear_call_trace_bif(&ep->info, 1);
- m = 1;
- }
- if (flags.meta) {
- erts_clear_mtrace_bif(&ep->info);
- m = 1;
- }
- if (flags.call_time) {
- erts_clear_time_trace_bif(&ep->info);
- m = 1;
- }
- }
- matches += m;
- }
- }
-
- /*
** So, now for breakpoint tracing
*/
erts_bp_match_functions(&finish_bp.f, mfa, specified);
@@ -1670,7 +1592,6 @@ erts_finish_breakpointing(void)
install_exp_breakpoints(&finish_bp.e);
}
}
- setup_bif_trace();
return 1;
case 1:
/*
@@ -1699,7 +1620,6 @@ erts_finish_breakpointing(void)
uninstall_exp_breakpoints(&finish_bp.e);
}
}
- reset_bif_trace();
return 1;
case 3:
/*
@@ -1710,7 +1630,6 @@ erts_finish_breakpointing(void)
* updated). If any breakpoints have been totally disabled,
* deallocate the GenericBp structs for them.
*/
- erts_consolidate_bif_bp_data();
clean_export_entries(&finish_bp.e);
erts_consolidate_bp_data(&finish_bp.e, 0);
erts_consolidate_bp_data(&finish_bp.f, 1);
@@ -1736,7 +1655,7 @@ install_exp_breakpoints(BpFunctions* f)
for (i = 0; i < ne; i++) {
Export* ep = ErtsContainerStruct(fp[i].ci, Export, info);
- ep->addressv[code_ix] = ep->beam;
+ ep->addressv[code_ix] = ep->trampoline.raw;
}
}
@@ -1751,11 +1670,12 @@ uninstall_exp_breakpoints(BpFunctions* f)
for (i = 0; i < ne; i++) {
Export* ep = ErtsContainerStruct(fp[i].ci, Export, info);
- if (ep->addressv[code_ix] != ep->beam) {
- continue;
- }
- ASSERT(BeamIsOpCode(ep->beam[0], op_trace_jump_W));
- ep->addressv[code_ix] = (BeamInstr *) ep->beam[1];
+ if (ep->addressv[code_ix] != ep->trampoline.raw) {
+ continue;
+ }
+
+ ASSERT(BeamIsOpCode(ep->trampoline.op, op_trace_jump_W));
+ ep->addressv[code_ix] = (BeamInstr *) ep->trampoline.trace.address;
}
}
@@ -1770,48 +1690,14 @@ clean_export_entries(BpFunctions* f)
for (i = 0; i < ne; i++) {
Export* ep = ErtsContainerStruct(fp[i].ci, Export, info);
- if (ep->addressv[code_ix] == ep->beam) {
- continue;
- }
- if (BeamIsOpCode(ep->beam[0], op_trace_jump_W)) {
- ep->beam[0] = (BeamInstr) 0;
- ep->beam[1] = (BeamInstr) 0;
- }
- }
-}
-
-static void
-setup_bif_trace(void)
-{
- int i;
-
- for (i = 0; i < BIF_SIZE; ++i) {
- Export *ep = bif_export[i];
- GenericBp* g = ep->info.u.gen_bp;
- if (g) {
- if (ExportIsBuiltIn(ep)) {
- ASSERT(ep->beam[1]);
- ep->beam[1] = (BeamInstr) bif_table[i].traced;
- }
- }
- }
-}
+ if (ep->addressv[code_ix] == ep->trampoline.raw) {
+ continue;
+ }
-static void
-reset_bif_trace(void)
-{
- int i;
- ErtsBpIndex active = erts_active_bp_ix();
-
- for (i = 0; i < BIF_SIZE; ++i) {
- Export *ep = bif_export[i];
- GenericBp* g = ep->info.u.gen_bp;
- if (g && g->data[active].flags == 0) {
- if (ExportIsBuiltIn(ep)) {
- ASSERT(ep->beam[1]);
- ep->beam[1] = (BeamInstr) bif_table[i].f;
- }
- }
+ if (BeamIsOpCode(ep->trampoline.op, op_trace_jump_W)) {
+ ep->trampoline.op = (BeamInstr) 0;
+ ep->trampoline.trace.address = (BeamInstr) 0;
+ }
}
}
@@ -1976,8 +1862,9 @@ BIF_RETTYPE erl_seq_trace_info(Process *p, Eterm item)
}
if (have_no_seqtrace(SEQ_TRACE_TOKEN(p))) {
- if ((item == am_send) || (item == am_receive) ||
- (item == am_print) || (item == am_timestamp)
+ if ((item == am_send) || (item == am_spawn) ||
+ (item == am_receive) || (item == am_print)
+ || (item == am_timestamp)
|| (item == am_monotonic_timestamp)
|| (item == am_strict_monotonic_timestamp)) {
hp = HAlloc(p,3);
@@ -2041,7 +1928,7 @@ BIF_RETTYPE seq_trace_print_1(BIF_ALIST_1)
if (have_no_seqtrace(SEQ_TRACE_TOKEN(BIF_P))) {
BIF_RET(am_false);
}
- seq_trace_update_send(BIF_P);
+ seq_trace_update_serial(BIF_P);
seq_trace_output(SEQ_TRACE_TOKEN(BIF_P), BIF_ARG_1,
SEQ_TRACE_PRINT, NIL, BIF_P);
BIF_RET(am_true);
@@ -2062,7 +1949,7 @@ BIF_RETTYPE seq_trace_print_2(BIF_ALIST_2)
}
if (!EQ(BIF_ARG_1, SEQ_TRACE_TOKEN_LABEL(BIF_P)))
BIF_RET(am_false);
- seq_trace_update_send(BIF_P);
+ seq_trace_update_serial(BIF_P);
seq_trace_output(SEQ_TRACE_TOKEN(BIF_P), BIF_ARG_2,
SEQ_TRACE_PRINT, NIL, BIF_P);
BIF_RET(am_true);
diff --git a/erts/emulator/beam/erl_cpu_topology.c b/erts/emulator/beam/erl_cpu_topology.c
index 6a4f43297e..67eebfe8f6 100644
--- a/erts/emulator/beam/erl_cpu_topology.c
+++ b/erts/emulator/beam/erl_cpu_topology.c
@@ -1632,7 +1632,7 @@ erts_get_cpu_topology_term(Process *c_p, Eterm which)
}
static void
-get_logical_processors(int *conf, int *onln, int *avail)
+get_logical_processors(int *conf, int *onln, int *avail, int *quota)
{
if (conf)
*conf = erts_get_cpu_configured(cpuinfo);
@@ -1640,13 +1640,15 @@ get_logical_processors(int *conf, int *onln, int *avail)
*onln = erts_get_cpu_online(cpuinfo);
if (avail)
*avail = erts_get_cpu_available(cpuinfo);
+ if (quota)
+ *quota = erts_get_cpu_quota(cpuinfo);
}
void
-erts_get_logical_processors(int *conf, int *onln, int *avail)
+erts_get_logical_processors(int *conf, int *onln, int *avail, int *quota)
{
erts_rwmtx_rlock(&cpuinfo_rwmtx);
- get_logical_processors(conf, onln, avail);
+ get_logical_processors(conf, onln, avail, quota);
erts_rwmtx_runlock(&cpuinfo_rwmtx);
}
@@ -1655,14 +1657,15 @@ erts_pre_early_init_cpu_topology(int *max_dcg_p,
int *max_rg_p,
int *conf_p,
int *onln_p,
- int *avail_p)
+ int *avail_p,
+ int *quota_p)
{
cpu_groups_maps = NULL;
no_cpu_groups_callbacks = 0;
*max_rg_p = ERTS_MAX_READER_GROUPS;
*max_dcg_p = ERTS_MAX_FLXCTR_GROUPS;
cpuinfo = erts_cpu_info_create();
- get_logical_processors(conf_p, onln_p, avail_p);
+ get_logical_processors(conf_p, onln_p, avail_p, quota_p);
}
void
diff --git a/erts/emulator/beam/erl_cpu_topology.h b/erts/emulator/beam/erl_cpu_topology.h
index 4a428d7972..91e1322504 100644
--- a/erts/emulator/beam/erl_cpu_topology.h
+++ b/erts/emulator/beam/erl_cpu_topology.h
@@ -32,7 +32,8 @@ erts_pre_early_init_cpu_topology(int *max_dcg_p,
int *max_rg_p,
int *conf_p,
int *onln_p,
- int *avail_p);
+ int *avail_p,
+ int *quota_p);
void
erts_early_init_cpu_topology(int no_schedulers,
int *max_main_threads_p,
@@ -81,7 +82,7 @@ Eterm erts_set_cpu_topology(Process *c_p, Eterm term);
Eterm erts_get_cpu_topology_term(Process *c_p, Eterm which);
int erts_update_cpu_info(void);
-void erts_get_logical_processors(int *conf, int *onln, int *avail);
+void erts_get_logical_processors(int *conf, int *onln, int *avail, int *quota);
int erts_sched_bind_atthrcreate_prepare(void);
int erts_sched_bind_atthrcreate_child(int unbind);
diff --git a/erts/emulator/beam/erl_db.c b/erts/emulator/beam/erl_db.c
index b4a97b42c8..91625dd516 100644
--- a/erts/emulator/beam/erl_db.c
+++ b/erts/emulator/beam/erl_db.c
@@ -51,6 +51,15 @@ erts_atomic_t erts_ets_misc_mem_size;
** Utility macros
*/
+#if defined(DEBUG)
+# define DBG_RANDOM_REDS(REDS, SEED) \
+ ((REDS) * 0.1 * erts_sched_local_random_float(SEED))
+#else
+# define DBG_RANDOM_REDS(REDS, SEED) (REDS)
+#endif
+
+
+
#define DB_BIF_GET_TABLE(TB, WHAT, KIND, BIF_IX) \
DB_GET_TABLE(TB, BIF_ARG_1, WHAT, KIND, BIF_IX, NULL, BIF_P)
@@ -75,7 +84,7 @@ static BIF_RETTYPE db_bif_fail(Process* p, Uint freason,
{
if (freason == TRAP) {
if (!bif_exp)
- bif_exp = bif_export[bif_ix];
+ bif_exp = &bif_trap_export[bif_ix];
p->arity = bif_exp->info.mfa.arity;
p->i = (BeamInstr*) bif_exp->addressv[erts_active_code_ix()];
}
@@ -353,7 +362,9 @@ struct meta_name_tab_entry* meta_name_tab_bucket(Eterm name,
typedef enum {
LCK_READ=1, /* read only access */
LCK_WRITE=2, /* exclusive table write access */
- LCK_WRITE_REC=3 /* record write access */
+ LCK_WRITE_REC=3, /* record write access */
+ NOLCK_ACCESS=4 /* Used to access the table structure
+ without acquiring the table lock */
} db_lock_kind_t;
extern DbTableMethod db_hash;
@@ -409,9 +420,11 @@ static void
free_dbtable(void *vtb)
{
DbTable *tb = (DbTable *) vtb;
+ erts_flxctr_add(&tb->common.counters,
+ ERTS_DB_TABLE_MEM_COUNTER_ID,
+ -((Sint)erts_flxctr_nr_of_allocated_bytes(&tb->common.counters)));
ASSERT(erts_flxctr_is_snapshot_ongoing(&tb->common.counters) ||
- sizeof(DbTable) == erts_flxctr_read_approx(&tb->common.counters,
- ERTS_DB_TABLE_MEM_COUNTER_ID));
+ sizeof(DbTable) == DB_GET_APPROX_MEM_CONSUMED(tb));
ASSERT(is_immed(tb->common.heir_data));
@@ -423,7 +436,7 @@ free_dbtable(void *vtb)
if (tb->common.btid)
erts_bin_release(tb->common.btid);
- erts_flxctr_destroy(&tb->common.counters, ERTS_ALC_T_DB_TABLE);
+ erts_flxctr_destroy(&tb->common.counters, ERTS_ALC_T_ETS_CTRS);
erts_free(ERTS_ALC_T_DB_TABLE, tb);
}
@@ -619,7 +632,7 @@ static ERTS_INLINE void db_lock(DbTable* tb, db_lock_kind_t kind)
if (kind == LCK_WRITE) {
erts_rwmtx_rwlock(&tb->common.rwlock);
tb->common.is_thread_safe = 1;
- } else {
+ } else if (kind != NOLCK_ACCESS) {
erts_rwmtx_rlock(&tb->common.rwlock);
ASSERT(!tb->common.is_thread_safe);
}
@@ -631,6 +644,8 @@ static ERTS_INLINE void db_lock(DbTable* tb, db_lock_kind_t kind)
case LCK_WRITE_REC:
erts_rwmtx_rwlock(&tb->common.rwlock);
break;
+ case NOLCK_ACCESS:
+ return;
default:
erts_rwmtx_rlock(&tb->common.rwlock);
}
@@ -640,7 +655,7 @@ static ERTS_INLINE void db_lock(DbTable* tb, db_lock_kind_t kind)
static ERTS_INLINE void db_unlock(DbTable* tb, db_lock_kind_t kind)
{
- if (DB_LOCK_FREE(tb))
+ if (DB_LOCK_FREE(tb) || kind == NOLCK_ACCESS)
return;
if (tb->common.type & DB_FINE_LOCKED) {
if (kind == LCK_WRITE) {
@@ -671,7 +686,10 @@ static ERTS_INLINE int db_is_exclusive(DbTable* tb, db_lock_kind_t kind)
if (DB_LOCK_FREE(tb))
return 1;
- return kind != LCK_READ && tb->common.is_thread_safe;
+ return
+ kind != LCK_READ &&
+ kind != NOLCK_ACCESS &&
+ tb->common.is_thread_safe;
}
static DbTable* handle_lacking_permission(Process* p, DbTable* tb,
@@ -679,11 +697,27 @@ static DbTable* handle_lacking_permission(Process* p, DbTable* tb,
Uint* freason_p)
{
if (tb->common.status & DB_BUSY) {
+ void* continuation_state;
if (!db_is_exclusive(tb, kind)) {
db_unlock(tb, kind);
db_lock(tb, LCK_WRITE);
}
- delete_all_objects_continue(p, tb);
+ continuation_state = (void*)erts_atomic_read_nob(&tb->common.continuation_state);
+ if (continuation_state != NULL) {
+ const long iterations_per_red = 10;
+ const long reds = iterations_per_red * ERTS_BIF_REDS_LEFT(p);
+ long nr_of_reductions = DBG_RANDOM_REDS(reds, (Uint)freason_p);
+ const long init_reds = nr_of_reductions;
+ tb->common.continuation(&nr_of_reductions,
+ &continuation_state,
+ NULL);
+ if (continuation_state == NULL) {
+ erts_atomic_set_relb(&tb->common.continuation_state, (Sint)NULL);
+ }
+ BUMP_REDS(p, (init_reds - nr_of_reductions) / iterations_per_red);
+ } else {
+ delete_all_objects_continue(p, tb);
+ }
db_unlock(tb, LCK_WRITE);
tb = NULL;
*freason_p = TRAP;
@@ -722,9 +756,9 @@ DbTable* db_get_table_aux(Process *p,
if (!meta_already_locked)
erts_rwmtx_rlock(mtl);
else {
- ERTS_LC_ASSERT(erts_lc_rwmtx_is_rlocked(mtl)
- || erts_lc_rwmtx_is_rwlocked(mtl)
- || META_DB_LOCK_FREE());
+ ERTS_LC_ASSERT(META_DB_LOCK_FREE()
+ || erts_lc_rwmtx_is_rlocked(mtl)
+ || erts_lc_rwmtx_is_rwlocked(mtl));
}
tb = NULL;
if (bucket->pu.tb != NULL) {
@@ -752,15 +786,28 @@ DbTable* db_get_table_aux(Process *p,
if (tb) {
db_lock(tb, kind);
#ifdef ETS_DBG_FORCE_TRAP
- if (erts_atomic_read_nob(&tb->common.dbg_force_trap) &&
- erts_atomic_add_read_nob(&tb->common.dbg_force_trap, 2) & 2) {
- db_unlock(tb, kind);
- tb = NULL;
- *freason_p = TRAP;
+ if (erts_atomic_read_nob(&tb->common.dbg_force_trap)) {
+ Uint32 rand = erts_sched_local_random((Uint)&p);
+ if ( !(rand & 7) ) {
+ /* About 7 of 8 threads that are on the line above
+ will get here */
+ if (erts_atomic_add_read_nob(&tb->common.dbg_force_trap, 2) & 2) {
+ db_unlock(tb, kind);
+ tb = NULL;
+ *freason_p = TRAP;
+ return tb;
+ }
+ }
+
+
}
- else
#endif
- if (ERTS_UNLIKELY(!(tb->common.status & what)))
+ if (ERTS_UNLIKELY(what != DB_READ_TBL_STRUCT
+ /* IMPORTANT: the above check is
+ necessary as the status field might
+ be in an intermediate state when
+ kind==NOLCK_ACCESS */ &&
+ !(tb->common.status & what)))
tb = handle_lacking_permission(p, tb, kind, freason_p);
}
else
@@ -779,6 +826,17 @@ DbTable* db_get_table(Process *p,
return db_get_table_aux(p, id, what, kind, 0, freason_p);
}
+static BIF_RETTYPE db_get_table_or_fail_return(DbTable **tb, /* out */
+ Eterm table_id,
+ Uint32 what,
+ db_lock_kind_t kind,
+ Uint bif_ix,
+ Process* p)
+{
+ DB_GET_TABLE(*tb, table_id, what, kind, bif_ix, NULL, p);
+ return THE_NON_VALUE;
+}
+
static int insert_named_tab(Eterm name_atom, DbTable* tb, int have_lock)
{
int ret = 0;
@@ -861,7 +919,7 @@ static int remove_named_tab(DbTable *tb, int have_lock)
db_lock(tb, LCK_WRITE);
}
- ERTS_LC_ASSERT(erts_lc_rwmtx_is_rwlocked(rwlock) || META_DB_LOCK_FREE());
+ ERTS_LC_ASSERT(META_DB_LOCK_FREE() || erts_lc_rwmtx_is_rwlocked(rwlock));
if (bucket->pu.tb == NULL) {
goto done;
@@ -1382,6 +1440,506 @@ BIF_RETTYPE ets_update_counter_4(BIF_ALIST_4)
return do_update_counter(BIF_P, tb, BIF_ARG_2, BIF_ARG_3, BIF_ARG_4);
}
+typedef enum {
+ ETS_INSERT_2_LIST_PROCESS_LOCAL,
+ ETS_INSERT_2_LIST_FAILED_TO_GET_LOCK,
+ ETS_INSERT_2_LIST_FAILED_TO_GET_LOCK_DESTROY,
+ ETS_INSERT_2_LIST_GLOBAL
+} ets_insert_2_list_status;
+
+typedef struct {
+ ets_insert_2_list_status status;
+ BIF_RETTYPE destroy_return_value;
+ DbTable* tb;
+ void* continuation_state;
+ Binary* continuation_res_bin;
+} ets_insert_2_list_info;
+
+
+static ERTS_INLINE BIF_RETTYPE
+ets_cret_to_return_value(Process* p, int cret)
+{
+ switch (cret) {
+ case DB_ERROR_NONE_FALSE:
+ BIF_RET(am_false);
+ case DB_ERROR_NONE:
+ BIF_RET(am_true);
+ case DB_ERROR_SYSRES:
+ BIF_ERROR(p, SYSTEM_LIMIT);
+ default:
+ BIF_ERROR(p, BADARG);
+ }
+}
+
+/*
+ * > > > > > > > > > > > > > > > > > > > > > > > > > > > > > >
+ * > > > > > > > > > > > > > > > > > > > > > > > > > > > > > >
+ *
+ * Start of code section that Yielding C Fun (YCF) transforms
+ *
+ * The functions within #idef YCF_FUNCTIONS below are not called directly.
+ * YCF generates yieldable versions of these functions before "erl_db.c" is
+ * compiled. These generated functions are placed in the file
+ * "erl_db_insert_list.ycf.h" which is included below. The generation of
+ * "erl_db_insert_list.ycf.h" is defined in
+ * "$ERL_TOP/erts/emulator/Makefile.in". See
+ * "$ERL_TOP/erts/emulator/internal_doc/AutomaticYieldingOfCCode.md"
+ * for more information about YCF.
+ *
+ * > > > > > > > > > > > > > > > > > > > > > > > > > > > > > >
+ * > > > > > > > > > > > > > > > > > > > > > > > > > > > > > >
+ */
+
+/*
+ * The LOCAL_VARIABLE macro is a trick to create a local variable that does not
+ * get renamed by YCF.
+ * Such variables will not retain their values over yields. Beware!
+ *
+ * I use this as a workaround for a limitation/bug in YCF. It does not do
+ * proper variable name substitution in expressions passed as argument to
+ * YCF_CONSUME_REDS(Expr).
+ */
+#define LOCAL_VARIABLE(TYPE, NAME) TYPE NAME
+
+#ifdef YCF_FUNCTIONS
+static long ets_insert_2_list_check(int keypos, Eterm list)
+{
+ Eterm lst = THE_NON_VALUE;
+ long i = 0;
+ for (lst = list; is_list(lst); lst = CDR(list_val(lst))) {
+ i++;
+ if (is_not_tuple(CAR(list_val(lst))) ||
+ (arityval(*tuple_val(CAR(list_val(lst)))) < keypos)) {
+ return -1;
+ }
+ }
+ if (lst != NIL) {
+ return -1;
+ }
+ return i;
+}
+
+static int ets_insert_new_2_list_has_member(DbTable* tb, Eterm list)
+{
+ Eterm lst;
+ Eterm lookup_ret;
+ DbTableMethod* meth = tb->common.meth;
+ for (lst = list; is_list(lst); lst = CDR(list_val(lst))) {
+ meth->db_member(tb,
+ TERM_GETKEY(tb,CAR(list_val(lst))),
+ &lookup_ret);
+ if (lookup_ret != am_false) {
+ return 1;
+ }
+ }
+ return 0;
+}
+
+static int ets_insert_2_list_from_p_heap(DbTable* tb, Eterm list)
+{
+ Eterm lst;
+ DbTableMethod* meth = tb->common.meth;
+ int cret = DB_ERROR_NONE;
+ for (lst = list; is_list(lst); lst = CDR(list_val(lst))) {
+ LOCAL_VARIABLE(SWord, consumed_reds);
+ consumed_reds = 1;
+ cret = meth->db_put(tb, CAR(list_val(lst)), 0, &consumed_reds);
+ if (cret != DB_ERROR_NONE)
+ return cret;
+ YCF_CONSUME_REDS(consumed_reds);
+ }
+ return DB_ERROR_NONE;
+}
+#endif /* YCF_FUNCTIONS */
+
+/* This function is called both as is, and as YCF transformed. */
+static void ets_insert_2_list_destroy_copied_dbterms(DbTableMethod* meth,
+ int compressed,
+ void* db_term_list)
+{
+ void* lst = db_term_list;
+ void* term = NULL;
+ while (lst != NULL) {
+ term = meth->db_dbterm_list_remove_first(&lst);
+ meth->db_free_dbterm(compressed, term);
+ }
+}
+
+#ifdef YCF_FUNCTIONS
+static void* ets_insert_2_list_copy_term_list(DbTableMethod* meth,
+ int compress,
+ int keypos,
+ Eterm list)
+{
+ void* db_term_list = NULL;
+ void *term;
+ Eterm lst;
+ for (lst = list; is_list(lst); lst = CDR(list_val(lst))) {
+ term = meth->db_eterm_to_dbterm(compress,
+ keypos,
+ CAR(list_val(lst)));
+ if (db_term_list != NULL) {
+ db_term_list =
+ meth->db_dbterm_list_prepend(db_term_list,
+ term);
+ } else {
+ db_term_list = term;
+ }
+ }
+
+ return db_term_list;
+
+ /* The following code will be executed if the calling process is
+ killed in the middle of the for loop above*/
+ YCF_SPECIAL_CODE_START(ON_DESTROY_STATE); {
+ ets_insert_2_list_destroy_copied_dbterms(meth,
+ compress,
+ db_term_list);
+ } YCF_SPECIAL_CODE_END();
+}
+
+static int ets_insert_new_2_dbterm_list_has_member(DbTable* tb, void* db_term_list)
+{
+ Eterm lookup_ret;
+ DbTableMethod* meth = tb->common.meth;
+ void* lst = db_term_list;
+ void* term = NULL;
+ Eterm key;
+ while (lst != NULL) {
+ term = meth->db_dbterm_list_remove_first(&lst);
+ key = meth->db_get_dbterm_key(tb, term);
+ meth->db_member(tb, key, &lookup_ret);
+ if (lookup_ret != am_false) {
+ return 1;
+ }
+ }
+ return 0;
+}
+
+static void ets_insert_2_list_insert_db_term_list(DbTable* tb,
+ void* list)
+{
+ void* lst = list;
+ void* term = NULL;
+ DbTableMethod* meth = tb->common.meth;
+ do {
+ LOCAL_VARIABLE(SWord, consumed_reds);
+ consumed_reds = 1;
+ term = meth->db_dbterm_list_remove_first(&lst);
+ meth->db_put_dbterm(tb, term, 0, &consumed_reds);
+ YCF_CONSUME_REDS(consumed_reds);
+ } while (lst != NULL);
+ return;
+}
+
+static void ets_insert_2_list_lock_tbl(Eterm table_id,
+ Process* p,
+ Uint bif_ix,
+ ets_insert_2_list_status on_success_status)
+{
+ BIF_RETTYPE fail_ret;
+ DbTable* tb;
+ ets_insert_2_list_info *ctx;
+ do {
+ fail_ret = db_get_table_or_fail_return(&tb,
+ table_id,
+ DB_WRITE,
+ LCK_WRITE,
+ bif_ix,
+ p);
+ ctx = YCF_GET_EXTRA_CONTEXT();
+ if (tb == NULL) {
+ if (p->freason == TRAP) {
+ ctx->status = ETS_INSERT_2_LIST_FAILED_TO_GET_LOCK;
+ } else {
+ ctx->status = ETS_INSERT_2_LIST_FAILED_TO_GET_LOCK_DESTROY;
+ ctx->destroy_return_value = fail_ret;
+ }
+ YCF_YIELD();
+ } else {
+ ctx->status = on_success_status;
+ ASSERT(DB_LOCK_FREE(tb) || erts_lc_rwmtx_is_rwlocked(&tb->common.rwlock));
+ ASSERT(!(tb->common.status & DB_DELETE));
+ }
+ } while (tb == NULL);
+}
+#endif /* YCF_FUNCTIONS */
+
+static ERTS_INLINE int can_insert_without_yield(Uint32 tb_type,
+ long list_len,
+ long reds_left)
+{
+ if (tb_type & DB_BAG) {
+ /* Bag inserts can be really bad and we don't know how much searching
+ * for duplicates we will do */
+ return 0;
+ }
+ else {
+ return list_len <= reds_left;
+ }
+}
+
+#ifdef YCF_FUNCTIONS
+static BIF_RETTYPE ets_insert_2_list(Process* p,
+ Eterm table_id,
+ DbTable *tb,
+ Eterm list,
+ int is_insert_new)
+{
+ int cret = DB_ERROR_NONE;
+ void* db_term_list = NULL; /* OBS: memory managements depends on that
+ db_term_list is initialized to NULL */
+ DbTableMethod* meth = tb->common.meth;
+ int compressed = tb->common.compress;
+ int keypos = tb->common.keypos;
+ Uint32 tb_type = tb->common.type;
+ Uint bif_ix = (is_insert_new ? BIF_ets_insert_new_2 : BIF_ets_insert_2);
+ long list_len;
+ /* tb should not be accessed after this point unless the table
+ lock is held as the table can get deleted while the function is
+ yielding */
+ list_len = ets_insert_2_list_check(keypos, list);
+ if (list_len < 0) {
+ return ets_cret_to_return_value(p, DB_ERROR_BADITEM);
+ }
+ if (can_insert_without_yield(tb_type, list_len, YCF_NR_OF_REDS_LEFT())) {
+ long reds_boost;
+ /* There is enough reductions left to do the inserts directly
+ from the heap without yielding */
+ ets_insert_2_list_lock_tbl(table_id, p, bif_ix, ETS_INSERT_2_LIST_PROCESS_LOCAL);
+ /* Ensure that we will not yield while inserting from heap */
+ reds_boost = YCF_MAX_NR_OF_REDS - YCF_NR_OF_REDS_LEFT();
+ YCF_SET_NR_OF_REDS_LEFT(YCF_MAX_NR_OF_REDS);
+ if (is_insert_new) {
+ if (ets_insert_new_2_list_has_member(tb, list)) {
+ cret = DB_ERROR_NONE_FALSE;
+ } else {
+ cret = ets_insert_2_list_from_p_heap(tb, list);
+ }
+ } else {
+ cret = ets_insert_2_list_from_p_heap(tb, list);
+ }
+ db_unlock(tb, LCK_WRITE);
+ YCF_SET_NR_OF_REDS_LEFT(YCF_NR_OF_REDS_LEFT() - reds_boost);
+ return ets_cret_to_return_value(p, cret);
+ }
+ /* Copy term list from heap so that other processes can help */
+ db_term_list =
+ ets_insert_2_list_copy_term_list(meth, compressed, keypos, list);
+ /* Lock table */
+ ets_insert_2_list_lock_tbl(table_id, p, bif_ix, ETS_INSERT_2_LIST_GLOBAL);
+ /* The operation must complete after this point */
+ if (is_insert_new) {
+ if (ets_insert_new_2_dbterm_list_has_member(tb, db_term_list)) {
+ ets_insert_2_list_destroy_copied_dbterms(meth,
+ compressed,
+ db_term_list);
+ cret = DB_ERROR_NONE_FALSE;
+ } else {
+ ets_insert_2_list_insert_db_term_list(tb, db_term_list);
+ }
+ } else {
+ ets_insert_2_list_insert_db_term_list(tb, db_term_list);
+ }
+ if (tb->common.continuation != NULL) {
+ /* Uninstall the continuation from the table struct */
+ tb->common.continuation = NULL;
+ if (is_insert_new) {
+ int* result_ptr =
+ ERTS_MAGIC_BIN_DATA(tb->common.continuation_res_bin);
+ *result_ptr = cret;
+ erts_bin_release(tb->common.continuation_res_bin);
+ }
+ tb->common.status |= tb->common.type & (DB_PRIVATE|DB_PROTECTED|DB_PUBLIC);
+ tb->common.status &= ~DB_BUSY;
+ erts_atomic_set_relb(&tb->common.continuation_state, (Sint)NULL);
+ }
+
+ return ets_cret_to_return_value(NULL, cret);
+
+ /* The following code will be executed if the initiating process
+ is killed before an ets_insert_2_list_lock_tbl call has
+ succeeded */
+ YCF_SPECIAL_CODE_START(ON_DESTROY_STATE); {
+ ets_insert_2_list_destroy_copied_dbterms(meth,
+ compressed,
+ db_term_list);
+ } YCF_SPECIAL_CODE_END();
+}
+#endif /* YCF_FUNCTIONS */
+
+/*
+ * < < < < < < < < < < < < < < < < < < < < < < < < < < < < < <
+ * < < < < < < < < < < < < < < < < < < < < < < < < < < < < < <
+ *
+ * End of code section that Yielding C Fun (YCF) transforms
+ *
+ * < < < < < < < < < < < < < < < < < < < < < < < < < < < < < <
+ * < < < < < < < < < < < < < < < < < < < < < < < < < < < < < <
+ */
+#include "erl_db_insert_list.ycf.h"
+
+static void* ets_insert_2_yield_alloc(size_t size, void* ctx)
+{
+ (void)ctx;
+ return erts_alloc(ERTS_ALC_T_ETS_I_LST_TRAP, size);
+}
+
+static void ets_insert_2_yield_free(void* data, void* ctx)
+{
+ (void)ctx;
+ erts_free(ERTS_ALC_T_ETS_I_LST_TRAP, data);
+}
+
+static int ets_insert_2_list_yield_dtor(Binary* bin)
+{
+ ets_insert_2_list_info* ctx = ERTS_MAGIC_BIN_DATA(bin);
+ if (ctx->status != ETS_INSERT_2_LIST_GLOBAL &&
+ ctx->continuation_state != NULL) {
+ /* The operation has not been committed to the table and has
+ not completed*/
+ ets_insert_2_list_ycf_gen_destroy(ctx->continuation_state);
+ }
+ return 1;
+}
+
+static void ets_insert_2_list_continuation(long *reds_ptr,
+ void** state,
+ void* extra_context)
+{
+ ets_insert_2_list_ycf_gen_continue(reds_ptr, state, extra_context);
+}
+
+static int db_insert_new_2_res_bin_dtor(Binary *context_bin)
+{
+ (void)context_bin;
+ return 1;
+}
+
+#define ITERATIONS_PER_RED 8
+
+static BIF_RETTYPE ets_insert_2_list_driver(Process* p,
+ Eterm tid,
+ Eterm list,
+ int is_insert_new) {
+ const long reds = ITERATIONS_PER_RED * ERTS_BIF_REDS_LEFT(p);
+ long nr_of_reductions = DBG_RANDOM_REDS(reds, (Uint)&p);
+ const long init_reds = nr_of_reductions;
+ ets_insert_2_list_info* ctx = NULL;
+ ets_insert_2_list_info ictx;
+ BIF_RETTYPE ret = THE_NON_VALUE;
+ Eterm state_mref = list;
+ Uint bix = (is_insert_new ? BIF_ets_insert_new_2 : BIF_ets_insert_2);
+ if (is_internal_magic_ref(state_mref)) {
+ Binary* state_bin = erts_magic_ref2bin(state_mref);
+ if (ERTS_MAGIC_BIN_DESTRUCTOR(state_bin) != ets_insert_2_list_yield_dtor) {
+ BIF_ERROR(p, BADARG);
+ }
+ /* Continue a trapped call */
+ erts_set_gc_state(p, 1);
+ ctx = ERTS_MAGIC_BIN_DATA(state_bin);
+ if (ctx->status == ETS_INSERT_2_LIST_GLOBAL) {
+ /* An operation that can be helped by other operations is
+ handled here */
+ Uint freason__;
+ int cret = DB_ERROR_NONE;
+ DbTable* tb;
+ /* First check if another process has completed the
+ operation without acquiring the lock */
+ if (NULL == (tb = db_get_table(p, tid, DB_READ_TBL_STRUCT, NOLCK_ACCESS, &freason__))) {
+ if (freason__ == TRAP){
+ erts_set_gc_state(p, 0);
+ return db_bif_fail(p, freason__, bix, NULL);
+ }
+ }
+ if (tb != NULL &&
+ (void*)erts_atomic_read_acqb(&tb->common.continuation_state) ==
+ ctx->continuation_state) {
+ /* The lock has to be taken to complete the operation */
+ if (NULL == (tb = db_get_table(p, tid, DB_WRITE, LCK_WRITE, &freason__))) {
+ if (freason__ == TRAP){
+ erts_set_gc_state(p, 0);
+ return db_bif_fail(p, freason__, bix, NULL);
+ }
+ }
+ /* Must be done since the db_get_table call did not trap */
+ if (tb != NULL) {
+ db_unlock(tb, LCK_WRITE);
+ }
+ }
+ if (is_insert_new) {
+ int* res = ERTS_MAGIC_BIN_DATA(ctx->continuation_res_bin);
+ cret = *res;
+ }
+ return ets_cret_to_return_value(NULL, cret);
+ } else {
+ ret = ets_insert_2_list_ycf_gen_continue(&nr_of_reductions,
+ &ctx->continuation_state,
+ ctx);
+ }
+ } else {
+ /* Start call */
+ ictx.continuation_state = NULL;
+ ictx.status = ETS_INSERT_2_LIST_PROCESS_LOCAL;
+ ictx.tb = NULL;
+ ctx = &ictx;
+ DB_GET_TABLE(ctx->tb, tid, DB_READ_TBL_STRUCT, NOLCK_ACCESS, bix, NULL, p);
+ ret = ets_insert_2_list_ycf_gen_yielding(&nr_of_reductions,
+ &ctx->continuation_state,
+ ctx,
+ ets_insert_2_yield_alloc,
+ ets_insert_2_yield_free,
+ NULL,
+ 0,
+ NULL,
+ p,
+ tid,
+ ctx->tb,
+ list,
+ is_insert_new);
+ if (ctx->continuation_state != NULL) {
+ Binary* state_bin = erts_create_magic_binary(sizeof(ets_insert_2_list_info),
+ ets_insert_2_list_yield_dtor);
+ Eterm* hp = HAlloc(p, ERTS_MAGIC_REF_THING_SIZE);
+ state_mref = erts_mk_magic_ref(&hp, &MSO(p), state_bin);
+ ctx = ERTS_MAGIC_BIN_DATA(state_bin);
+ *ctx = ictx;
+ }
+ }
+ BUMP_REDS(p, (init_reds - nr_of_reductions) / ITERATIONS_PER_RED);
+ if (ctx->status == ETS_INSERT_2_LIST_GLOBAL &&
+ ctx->continuation_state != NULL &&
+ ctx->tb->common.continuation == NULL) {
+ /* Install the continuation in the table structure so other
+ threads can help */
+ if (is_insert_new) {
+ Binary* bin =
+ erts_create_magic_binary(sizeof(int),
+ db_insert_new_2_res_bin_dtor);
+ Eterm* hp = HAlloc(p, ERTS_MAGIC_REF_THING_SIZE);
+ erts_mk_magic_ref(&hp, &MSO(p), bin);
+ erts_refc_inctest(&bin->intern.refc, 2);
+ ctx->tb->common.continuation_res_bin = bin;
+ ctx->continuation_res_bin = bin;
+ }
+ ctx->tb->common.continuation = ets_insert_2_list_continuation;
+ ctx->tb->common.status &= ~(DB_PRIVATE|DB_PROTECTED|DB_PUBLIC);
+ ctx->tb->common.status |= DB_BUSY;
+ erts_atomic_set_relb(&ctx->tb->common.continuation_state,
+ (Sint)ctx->continuation_state);
+ }
+ if (ctx->status == ETS_INSERT_2_LIST_FAILED_TO_GET_LOCK_DESTROY) {
+ return ctx->destroy_return_value;
+ }
+ if (ctx->status == ETS_INSERT_2_LIST_GLOBAL) {
+ db_unlock(ctx->tb, LCK_WRITE);
+ }
+ if (ctx->continuation_state != NULL) {
+ erts_set_gc_state(p, 0);
+ BIF_TRAP2(&bif_trap_export[bix], p, tid, state_mref);
+ }
+ return ret;
+}
/*
** The put BIF
@@ -1390,59 +1948,42 @@ BIF_RETTYPE ets_insert_2(BIF_ALIST_2)
{
DbTable* tb;
int cret = DB_ERROR_NONE;
- Eterm lst;
+ Eterm insert_term;
DbTableMethod* meth;
- db_lock_kind_t kind;
-
+ SWord consumed_reds = 0;
CHECK_TABLES();
-
- /* Write lock table if more than one object to keep atomicity */
- kind = ((is_list(BIF_ARG_2) && CDR(list_val(BIF_ARG_2)) != NIL)
- ? LCK_WRITE : LCK_WRITE_REC);
-
- DB_BIF_GET_TABLE(tb, DB_WRITE, kind, BIF_ets_insert_2);
-
if (BIF_ARG_2 == NIL) {
- db_unlock(tb, kind);
+ /* Check that the table exists */
+ DB_BIF_GET_TABLE(tb, DB_WRITE, LCK_WRITE_REC, BIF_ets_insert_2);
+ db_unlock(tb, LCK_WRITE_REC);
BIF_RET(am_true);
- }
- meth = tb->common.meth;
- if (is_list(BIF_ARG_2)) {
- for (lst = BIF_ARG_2; is_list(lst); lst = CDR(list_val(lst))) {
- if (is_not_tuple(CAR(list_val(lst))) ||
- (arityval(*tuple_val(CAR(list_val(lst)))) < tb->common.keypos)) {
- goto badarg;
- }
- }
- if (lst != NIL) {
- goto badarg;
- }
- for (lst = BIF_ARG_2; is_list(lst); lst = CDR(list_val(lst))) {
- cret = meth->db_put(tb, CAR(list_val(lst)), 0);
- if (cret != DB_ERROR_NONE)
- break;
- }
+ } if ((is_list(BIF_ARG_2) && CDR(list_val(BIF_ARG_2)) != NIL) ||
+ is_internal_magic_ref(BIF_ARG_2)) {
+ /* Handle list case */
+ return ets_insert_2_list_driver(BIF_P,
+ BIF_ARG_1,
+ BIF_ARG_2,
+ 0);
+ } else if (is_list(BIF_ARG_2)) {
+ insert_term = CAR(list_val(BIF_ARG_2));
} else {
- if (is_not_tuple(BIF_ARG_2) ||
- (arityval(*tuple_val(BIF_ARG_2)) < tb->common.keypos)) {
- goto badarg;
- }
- cret = meth->db_put(tb, BIF_ARG_2, 0);
+ insert_term = BIF_ARG_2;
}
- db_unlock(tb, kind);
-
- switch (cret) {
- case DB_ERROR_NONE:
- BIF_RET(am_true);
- case DB_ERROR_SYSRES:
- BIF_ERROR(BIF_P, SYSTEM_LIMIT);
- default:
- BIF_ERROR(BIF_P, BADARG);
+ DB_BIF_GET_TABLE(tb, DB_WRITE, LCK_WRITE_REC, BIF_ets_insert_2);
+
+ meth = tb->common.meth;
+ if (is_not_tuple(insert_term) ||
+ (arityval(*tuple_val(insert_term)) < tb->common.keypos)) {
+ db_unlock(tb, LCK_WRITE_REC);
+ BIF_ERROR(BIF_P, BADARG);
}
- badarg:
- db_unlock(tb, kind);
- BIF_ERROR(BIF_P, BADARG);
+ cret = meth->db_put(tb, insert_term, 0, &consumed_reds);
+
+ db_unlock(tb, LCK_WRITE_REC);
+
+ BUMP_REDS(BIF_P, consumed_reds / ITERATIONS_PER_RED);
+ return ets_cret_to_return_value(BIF_P, cret);
}
@@ -1456,69 +1997,40 @@ BIF_RETTYPE ets_insert_new_2(BIF_ALIST_2)
Eterm ret = am_true;
Eterm obj;
db_lock_kind_t kind;
-
+ SWord consumed_reds = 0;
CHECK_TABLES();
- if (is_list(BIF_ARG_2)) {
- if (CDR(list_val(BIF_ARG_2)) != NIL) {
- Eterm lst;
- Eterm lookup_ret;
- DbTableMethod* meth;
-
- /* More than one object, use LCK_WRITE to keep atomicity */
- kind = LCK_WRITE;
- DB_BIF_GET_TABLE(tb, DB_WRITE, kind, BIF_ets_insert_new_2);
-
- meth = tb->common.meth;
- for (lst = BIF_ARG_2; is_list(lst); lst = CDR(list_val(lst))) {
- if (is_not_tuple(CAR(list_val(lst)))
- || (arityval(*tuple_val(CAR(list_val(lst))))
- < tb->common.keypos)) {
- goto badarg;
- }
- }
- if (lst != NIL) {
- goto badarg;
- }
- for (lst = BIF_ARG_2; is_list(lst); lst = CDR(list_val(lst))) {
- cret = meth->db_member(tb, TERM_GETKEY(tb,CAR(list_val(lst))),
- &lookup_ret);
- if ((cret != DB_ERROR_NONE) || (lookup_ret != am_false)) {
- ret = am_false;
- goto done;
- }
- }
-
- for (lst = BIF_ARG_2; is_list(lst); lst = CDR(list_val(lst))) {
- cret = meth->db_put(tb,CAR(list_val(lst)), 0);
- if (cret != DB_ERROR_NONE)
- break;
- }
- goto done;
- }
- obj = CAR(list_val(BIF_ARG_2));
- }
- else {
- obj = BIF_ARG_2;
+ if (BIF_ARG_2 == NIL) {
+ /* Check that the table exists */
+ DB_BIF_GET_TABLE(tb, DB_WRITE, LCK_WRITE_REC, BIF_ets_insert_2);
+ db_unlock(tb, LCK_WRITE_REC);
+ BIF_RET(am_true);
+ } if ((is_list(BIF_ARG_2) && CDR(list_val(BIF_ARG_2)) != NIL) ||
+ is_internal_magic_ref(BIF_ARG_2)) {
+ /* Handle list case */
+ return ets_insert_2_list_driver(BIF_P, BIF_ARG_1, BIF_ARG_2, 1);
+ } else if (is_list(BIF_ARG_2)) {
+ obj = CAR(list_val(BIF_ARG_2));
+ } else {
+ obj = BIF_ARG_2;
}
- /* Only one object (or NIL)
- */
+
+ /* Only one object */
kind = LCK_WRITE_REC;
DB_BIF_GET_TABLE(tb, DB_WRITE, kind, BIF_ets_insert_new_2);
- if (BIF_ARG_2 == NIL) {
- db_unlock(tb, kind);
- BIF_RET(am_true);
- }
if (is_not_tuple(obj)
|| (arityval(*tuple_val(obj)) < tb->common.keypos)) {
- goto badarg;
+ db_unlock(tb, kind);
+ BIF_ERROR(BIF_P, BADARG);
}
cret = tb->common.meth->db_put(tb, obj,
- 1); /* key_clash_fail */
+ 1, /* key_clash_fail */
+ &consumed_reds);
-done:
db_unlock(tb, kind);
+
+ BUMP_REDS(BIF_P, consumed_reds / ITERATIONS_PER_RED);
switch (cret) {
case DB_ERROR_NONE:
BIF_RET(ret);
@@ -1529,9 +2041,6 @@ done:
default:
BIF_ERROR(BIF_P, BADARG);
}
- badarg:
- db_unlock(tb, kind);
- BIF_ERROR(BIF_P, BADARG);
}
/*
@@ -1643,6 +2152,8 @@ BIF_RETTYPE ets_new_2(BIF_ALIST_2)
Sint keypos;
int is_named, is_compressed;
int is_fine_locked, frequent_read;
+ int is_decentralized_counters;
+ int is_decentralized_counters_option;
int cret;
DbTableMethod* meth;
@@ -1658,6 +2169,8 @@ BIF_RETTYPE ets_new_2(BIF_ALIST_2)
is_named = 0;
is_fine_locked = 0;
frequent_read = 0;
+ is_decentralized_counters = 0;
+ is_decentralized_counters_option = -1;
heir = am_none;
heir_data = (UWord) am_undefined;
is_compressed = erts_ets_always_compress;
@@ -1674,6 +2187,7 @@ BIF_RETTYPE ets_new_2(BIF_ALIST_2)
status &= ~(DB_SET | DB_BAG | DB_ORDERED_SET | DB_CA_ORDERED_SET);
}
else if (val == am_ordered_set) {
+ is_decentralized_counters = 1;
status |= DB_ORDERED_SET;
status &= ~(DB_SET | DB_BAG | DB_DUPLICATE_BAG | DB_CA_ORDERED_SET);
}
@@ -1699,12 +2213,18 @@ BIF_RETTYPE ets_new_2(BIF_ALIST_2)
} else if (tp[2] == am_false) {
frequent_read = 0;
} else break;
-
}
else if (tp[1] == am_heir && tp[2] == am_none) {
heir = am_none;
heir_data = am_undefined;
}
+ else if (tp[1] == am_decentralized_counters) {
+ if (tp[2] == am_true) {
+ is_decentralized_counters_option = 1;
+ } else if (tp[2] == am_false) {
+ is_decentralized_counters_option = 0;
+ } else break;
+ }
else break;
}
else if (arityval(tp[0]) == 3 && tp[1] == am_heir
@@ -1738,6 +2258,9 @@ BIF_RETTYPE ets_new_2(BIF_ALIST_2)
if (is_not_nil(list)) { /* bad opt or not a well formed list */
BIF_ERROR(BIF_P, BADARG);
}
+ if (-1 != is_decentralized_counters_option) {
+ is_decentralized_counters = is_decentralized_counters_option;
+ }
if (IS_TREE_TABLE(status) && is_fine_locked && !(status & DB_PRIVATE)) {
meth = &db_catree;
status |= DB_CA_ORDERED_SET;
@@ -1762,27 +2285,30 @@ BIF_RETTYPE ets_new_2(BIF_ALIST_2)
/* we create table outside any table lock
* and take the unusal cost of destroy table if it
- * fails to find a slot
+ * fails to find a slot
*/
{
DbTable init_tb;
- erts_flxctr_init(&init_tb.common.counters, 0, 2, ERTS_ALC_T_DB_TABLE);
+ erts_flxctr_init(&init_tb.common.counters, 0, 2, ERTS_ALC_T_ETS_CTRS);
tb = (DbTable*) erts_db_alloc(ERTS_ALC_T_DB_TABLE,
&init_tb, sizeof(DbTable));
erts_flxctr_init(&tb->common.counters,
- status & DB_CA_ORDERED_SET,
+ (status & DB_FINE_LOCKED) && is_decentralized_counters,
2,
- ERTS_ALC_T_DB_TABLE);
+ ERTS_ALC_T_ETS_CTRS);
erts_flxctr_add(&tb->common.counters,
ERTS_DB_TABLE_MEM_COUNTER_ID,
- DB_GET_APPROX_MEM_CONSUMED(&init_tb));
+ DB_GET_APPROX_MEM_CONSUMED(&init_tb) +
+ erts_flxctr_nr_of_allocated_bytes(&tb->common.counters));
}
tb->common.meth = meth;
tb->common.the_name = BIF_ARG_1;
- tb->common.status = status;
+ tb->common.status = status;
tb->common.type = status;
/* Note, 'type' is *read only* from now on... */
+ tb->common.continuation = NULL;
+ erts_atomic_set_nob(&tb->common.continuation_state, (Sint)NULL);
erts_refc_init(&tb->common.fix_count, 0);
db_init_lock(tb, status & (DB_FINE_LOCKED|DB_FREQ_READ));
tb->common.keypos = keypos;
@@ -1819,7 +2345,7 @@ BIF_RETTYPE ets_new_2(BIF_ALIST_2)
table_dec_refc(tb, 0);
BIF_ERROR(BIF_P, BADARG);
}
-
+
BIF_P->flags |= F_USING_DB; /* So we can remove tb if p dies */
#ifdef HARDDEBUG
@@ -2194,7 +2720,7 @@ BIF_RETTYPE ets_internal_delete_all_2(BIF_ALIST_2)
tb->common.status |= DB_BUSY;
db_unlock(tb, LCK_WRITE);
BUMP_ALL_REDS(BIF_P);
- BIF_TRAP2(bif_export[BIF_ets_internal_delete_all_2], BIF_P,
+ BIF_TRAP2(&bif_trap_export[BIF_ets_internal_delete_all_2], BIF_P,
BIF_ARG_1, nitems_holder);
}
else {
@@ -2225,7 +2751,7 @@ static void delete_all_objects_continue(Process* p, DbTable* tb)
SWord initial_reds = ERTS_BIF_REDS_LEFT(p);
SWord reds = initial_reds;
- ERTS_LC_ASSERT(erts_lc_rwmtx_is_rwlocked(&tb->common.rwlock) || DB_LOCK_FREE(tb));
+ ERTS_LC_ASSERT(DB_LOCK_FREE(tb) || erts_lc_rwmtx_is_rwlocked(&tb->common.rwlock));
if ((tb->common.status & (DB_DELETE|DB_BUSY)) != DB_BUSY)
return;
@@ -3310,6 +3836,7 @@ BIF_RETTYPE ets_info_1(BIF_ALIST_1)
am_node, am_size, am_name, am_heir, am_owner, am_memory, am_compressed,
am_write_concurrency,
am_read_concurrency,
+ am_decentralized_counters,
am_id};
Eterm results[sizeof(fields)/sizeof(Eterm)];
DbTable* tb;
@@ -3358,7 +3885,7 @@ BIF_RETTYPE ets_info_1(BIF_ALIST_1)
BIF_RET(am_undefined);
}
if (rp == ERTS_PROC_LOCK_BUSY) {
- ERTS_BIF_YIELD1(bif_export[BIF_ets_info_1], BIF_P, BIF_ARG_1);
+ ERTS_BIF_YIELD1(&bif_trap_export[BIF_ets_info_1], BIF_P, BIF_ARG_1);
}
if ((tb = db_get_table(BIF_P, BIF_ARG_1, DB_INFO, LCK_READ)) == NULL
|| tb->common.owner != owner) {
@@ -3373,16 +3900,16 @@ BIF_RETTYPE ets_info_1(BIF_ALIST_1)
if (!is_ctrs_read_result_set) {
ErtsFlxCtrSnapshotResult res =
- erts_flxctr_snapshot(&tb->common.counters, ERTS_ALC_T_DB_TABLE, BIF_P);
+ erts_flxctr_snapshot(&tb->common.counters, ERTS_ALC_T_ETS_CTRS, BIF_P);
if (ERTS_FLXCTR_GET_RESULT_AFTER_TRAP == res.type) {
Eterm tuple;
db_unlock(tb, LCK_READ);
hp = HAlloc(BIF_P, 3);
tuple = TUPLE2(hp, res.trap_resume_state, table);
- BIF_TRAP1(bif_export[BIF_ets_info_1], BIF_P, tuple);
+ BIF_TRAP1(&bif_trap_export[BIF_ets_info_1], BIF_P, tuple);
} else if (res.type == ERTS_FLXCTR_TRY_AGAIN_AFTER_TRAP) {
db_unlock(tb, LCK_READ);
- BIF_TRAP1(bif_export[BIF_ets_info_1], BIF_P, table);
+ BIF_TRAP1(&bif_trap_export[BIF_ets_info_1], BIF_P, table);
} else {
size = res.result[ERTS_DB_TABLE_NITEMS_COUNTER_ID];
memory = res.result[ERTS_DB_TABLE_MEM_COUNTER_ID];
@@ -3450,13 +3977,13 @@ BIF_RETTYPE ets_info_2(BIF_ALIST_2)
}
if (BIF_ARG_2 == am_size || BIF_ARG_2 == am_memory) {
ErtsFlxCtrSnapshotResult res =
- erts_flxctr_snapshot(&tb->common.counters, ERTS_ALC_T_DB_TABLE, BIF_P);
+ erts_flxctr_snapshot(&tb->common.counters, ERTS_ALC_T_ETS_CTRS, BIF_P);
if (ERTS_FLXCTR_GET_RESULT_AFTER_TRAP == res.type) {
db_unlock(tb, LCK_READ);
- BIF_TRAP2(bif_export[BIF_ets_info_2], BIF_P, res.trap_resume_state, BIF_ARG_2);
+ BIF_TRAP2(&bif_trap_export[BIF_ets_info_2], BIF_P, res.trap_resume_state, BIF_ARG_2);
} else if (res.type == ERTS_FLXCTR_TRY_AGAIN_AFTER_TRAP) {
db_unlock(tb, LCK_READ);
- BIF_TRAP2(bif_export[BIF_ets_info_2], BIF_P, BIF_ARG_1, BIF_ARG_2);
+ BIF_TRAP2(&bif_trap_export[BIF_ets_info_2], BIF_P, BIF_ARG_1, BIF_ARG_2);
} else if (BIF_ARG_2 == am_size) {
ret = erts_make_integer(res.result[ERTS_DB_TABLE_NITEMS_COUNTER_ID], BIF_P);
} else { /* BIF_ARG_2 == am_memory */
@@ -3522,7 +4049,7 @@ BIF_RETTYPE ets_match_spec_run_r_3(BIF_ALIST_3)
for (lst = BIF_ARG_1; is_list(lst); lst = CDR(list_val(lst))) {
if (++i > CONTEXT_REDS) {
BUMP_ALL_REDS(BIF_P);
- BIF_TRAP3(bif_export[BIF_ets_match_spec_run_r_3],
+ BIF_TRAP3(&bif_trap_export[BIF_ets_match_spec_run_r_3],
BIF_P,lst,BIF_ARG_2,ret);
}
res = db_prog_match(BIF_P, BIF_P,
@@ -4189,7 +4716,7 @@ static SWord free_fixations_locked(Process* p, DbTable *tb)
{
struct free_fixations_ctx ctx;
- ERTS_LC_ASSERT(erts_lc_rwmtx_is_rwlocked(&tb->common.rwlock) || DB_LOCK_FREE(tb));
+ ERTS_LC_ASSERT(DB_LOCK_FREE(tb) || erts_lc_rwmtx_is_rwlocked(&tb->common.rwlock));
ctx.p = p;
ctx.tb = tb;
@@ -4360,7 +4887,7 @@ static Eterm table_info(Process* p, DbTable* tb, Eterm What)
} else if (What == am_heir) {
ret = tb->common.heir;
} else if (What == am_protection) {
- if (tb->common.status & DB_PRIVATE)
+ if (tb->common.status & DB_PRIVATE)
ret = am_private;
else if (tb->common.status & DB_PROTECTED)
ret = am_protected;
@@ -4382,6 +4909,8 @@ static Eterm table_info(Process* p, DbTable* tb, Eterm What)
ret = tb->common.compress ? am_true : am_false;
} else if (What == am_id) {
ret = make_tid(p, tb);
+ } else if (What == am_decentralized_counters) {
+ ret = tb->common.counters.is_decentralized ? am_true : am_false;
}
/*
diff --git a/erts/emulator/beam/erl_db.h b/erts/emulator/beam/erl_db.h
index c604744687..6327c56625 100644
--- a/erts/emulator/beam/erl_db.h
+++ b/erts/emulator/beam/erl_db.h
@@ -159,13 +159,15 @@ extern erts_aint_t erts_ets_dbg_force_trap;
*/
#define ERTS_DB_ALC_MEM_UPDATE_(TAB, FREE_SZ, ALLOC_SZ) \
-do { \
- erts_aint_t sz__ = (((erts_aint_t) (ALLOC_SZ)) \
- - ((erts_aint_t) (FREE_SZ))); \
- ASSERT((TAB)); \
- erts_flxctr_add(&(TAB)->common.counters, \
- ERTS_DB_TABLE_MEM_COUNTER_ID, \
- sz__); \
+do { \
+ if ((TAB) != NULL) { \
+ erts_aint_t sz__ = (((erts_aint_t) (ALLOC_SZ)) \
+ - ((erts_aint_t) (FREE_SZ))); \
+ ASSERT((TAB)); \
+ erts_flxctr_add(&(TAB)->common.counters, \
+ ERTS_DB_TABLE_MEM_COUNTER_ID, \
+ sz__); \
+ } \
} while (0)
#define ERTS_ETS_MISC_MEM_ADD(SZ) \
@@ -310,7 +312,8 @@ erts_db_free(ErtsAlcType_t type, DbTable *tab, void *ptr, Uint size)
ASSERT(ptr != 0);
ASSERT(size == ERTS_ALC_DBG_BLK_SZ(ptr));
ERTS_DB_ALC_MEM_UPDATE_(tab, size, 0);
- ASSERT(((void *) tab) != ptr ||
+ ASSERT(tab == NULL ||
+ ((void *) tab) != ptr ||
tab->common.counters.is_decentralized ||
0 == erts_flxctr_read_centralized(&tab->common.counters,
ERTS_DB_TABLE_MEM_COUNTER_ID));
diff --git a/erts/emulator/beam/erl_db_catree.c b/erts/emulator/beam/erl_db_catree.c
index 4e08f89692..ccf570d3de 100644
--- a/erts/emulator/beam/erl_db_catree.c
+++ b/erts/emulator/beam/erl_db_catree.c
@@ -104,7 +104,8 @@ static int db_last_catree(Process *p, DbTable *tbl,
static int db_prev_catree(Process *p, DbTable *tbl,
Eterm key,
Eterm *ret);
-static int db_put_catree(DbTable *tbl, Eterm obj, int key_clash_fail);
+static int db_put_catree(DbTable *tbl, Eterm obj, int key_clash_fail,
+ SWord *consumed_reds_p);
static int db_get_catree(Process *p, DbTable *tbl,
Eterm key, Eterm *ret);
static int db_member_catree(DbTable *tbl, Eterm key, Eterm *ret);
@@ -160,6 +161,10 @@ db_lookup_dbterm_catree(Process *, DbTable *, Eterm key, Eterm obj,
DbUpdateHandle*);
static void db_finalize_dbterm_catree(int cret, DbUpdateHandle *);
static int db_get_binary_info_catree(Process*, DbTable*, Eterm key, Eterm *ret);
+static int db_put_dbterm_catree(DbTable* tbl,
+ void* obj,
+ int key_clash_fail,
+ SWord *consumed_reds_p);
static void split_catree(DbTableCATree *tb,
DbTableCATreeNode* ERTS_RESTRICT base,
@@ -213,6 +218,12 @@ DbTableMethod db_catree =
db_foreach_offheap_catree,
db_lookup_dbterm_catree,
db_finalize_dbterm_catree,
+ db_eterm_to_dbterm_tree_common,
+ db_dbterm_list_prepend_tree_common,
+ db_dbterm_list_remove_first_tree_common,
+ db_put_dbterm_catree,
+ db_free_dbterm_tree_common,
+ db_get_dbterm_key_tree_common,
db_get_binary_info_catree,
db_first_catree, /* raw_first same as first */
db_next_catree /* raw_next same as next */
@@ -1367,7 +1378,7 @@ static void split_catree(DbTableCATree *tb,
}
}
-/* @brief Free the entire catree and its sub-trees.
+/** @brief Free the entire catree and its sub-trees.
*
* @param reds Reductions to spend.
* @return Reductions left. Negative value if not done.
@@ -1464,7 +1475,7 @@ static SWord db_free_table_continue_catree(DbTable *tbl, SWord reds)
return reds;
}
-/* @brief Free all objects of a base node, but keep the base node.
+/** @brief Free all objects of a base node, but keep the base node.
*
* @param reds Reductions to spend.
* @return Reductions left. Negative value if not done.
@@ -1632,7 +1643,27 @@ static int db_prev_catree(Process *p, DbTable *tbl, Eterm key, Eterm *ret)
return result;
}
-static int db_put_catree(DbTable *tbl, Eterm obj, int key_clash_fail)
+static int db_put_dbterm_catree(DbTable* tbl,
+ void* obj,
+ int key_clash_fail,
+ SWord *consumed_reds_p)
+{
+ TreeDbTerm *value_to_insert = obj;
+ DbTableCATree *tb = &tbl->catree;
+ Eterm key = GETKEY(tb, value_to_insert->dbterm.tpl);
+ FindBaseNode fbn;
+ DbTableCATreeNode* node = find_wlock_valid_base_node(tb, key, &fbn);
+ int result = db_put_dbterm_tree_common(&tb->common,
+ &node->u.base.root,
+ value_to_insert,
+ key_clash_fail,
+ NULL);
+ wunlock_adapt_base_node(tb, node, fbn.parent, fbn.current_level);
+ return result;
+}
+
+static int db_put_catree(DbTable *tbl, Eterm obj, int key_clash_fail,
+ SWord *consumed_reds_p)
{
DbTableCATree *tb = &tbl->catree;
Eterm key = GETKEY(&tb->common, tuple_val(obj));
@@ -1776,7 +1807,7 @@ TreeDbTerm** catree_find_prev_root(CATreeRootIterator *iter, Eterm* keyp)
return catree_find_nextprev_root(iter, 0, keyp);
}
-/* @brief Find root of tree where object with smallest key of all larger than
+/** @brief Find root of tree where object with smallest key of all larger than
* partially bound key may reside. Can be used as a starting point for
* a reverse iteration with pb_key.
*
@@ -1829,7 +1860,7 @@ TreeDbTerm** catree_find_next_from_pb_key_root(Eterm pb_key,
}
}
-/* @brief Find root of tree where object with largest key of all smaller than
+/** @brief Find root of tree where object with largest key of all smaller than
* partially bound key may reside. Can be used as a starting point for
* a forward iteration with pb_key.
*
diff --git a/erts/emulator/beam/erl_db_hash.c b/erts/emulator/beam/erl_db_hash.c
index 5508f5c34e..8076cf33ac 100644
--- a/erts/emulator/beam/erl_db_hash.c
+++ b/erts/emulator/beam/erl_db_hash.c
@@ -85,19 +85,58 @@
#include "erl_db_hash.h"
-#define ADD_NITEMS(DB, TO_ADD) \
- erts_flxctr_add(&(DB)->common.counters, ERTS_DB_TABLE_NITEMS_COUNTER_ID, TO_ADD)
-#define INC_NITEMS(DB) \
- erts_flxctr_inc_read_centralized(&(DB)->common.counters, ERTS_DB_TABLE_NITEMS_COUNTER_ID)
-#define DEC_NITEMS(DB) \
- erts_flxctr_dec_read_centralized(&(DB)->common.counters, ERTS_DB_TABLE_NITEMS_COUNTER_ID)
+#define IS_DECENTRALIZED_CTRS(DB) ((DB)->common.counters.is_decentralized)
+
+#define NITEMS_ESTIMATE_FROM_LCK_CTR(LCK_CTR_P) \
+ (LCK_CTR_P->nitems <= 0 ? 1: LCK_CTR_P->nitems)
+
+#define NITEMS_ESTIMATE(DB, LCK_CTR, HASH) \
+ (IS_DECENTRALIZED_CTRS(DB) ? \
+ (DB_HASH_LOCK_CNT * \
+ (LCK_CTR != NULL ? \
+ NITEMS_ESTIMATE_FROM_LCK_CTR(LCK_CTR) : \
+ NITEMS_ESTIMATE_FROM_LCK_CTR(GET_LOCK_AND_CTR(DB, HASH)))) : \
+ erts_flxctr_read_centralized(&(DB)->common.counters, \
+ ERTS_DB_TABLE_NITEMS_COUNTER_ID))
+
+#define ADD_NITEMS(DB, LCK_CTR, HASH, TO_ADD) \
+ do { \
+ if (IS_DECENTRALIZED_CTRS(DB)) { \
+ if (LCK_CTR != NULL) { \
+ LCK_CTR->nitems += TO_ADD; \
+ } else { \
+ GET_LOCK_AND_CTR(DB,HASH)->nitems += TO_ADD; \
+ } \
+ } \
+ erts_flxctr_add(&(DB)->common.counters, ERTS_DB_TABLE_NITEMS_COUNTER_ID, TO_ADD); \
+ } while(0)
+#define INC_NITEMS(DB, LCK_CTR, HASH) \
+ do { \
+ if (IS_DECENTRALIZED_CTRS(DB)) { \
+ if (LCK_CTR != NULL) { \
+ LCK_CTR->nitems++; \
+ } else { \
+ GET_LOCK_AND_CTR(DB,HASH)->nitems++; \
+ } \
+ } \
+ erts_flxctr_inc(&(DB)->common.counters, ERTS_DB_TABLE_NITEMS_COUNTER_ID); \
+ } while(0)
+#define DEC_NITEMS(DB, LCK_CTR, HASH) \
+ do { \
+ if (IS_DECENTRALIZED_CTRS(DB)) { \
+ if (LCK_CTR != NULL) { \
+ LCK_CTR->nitems--; \
+ } else { \
+ GET_LOCK_AND_CTR(DB,HASH)->nitems--; \
+ } \
+ } \
+ erts_flxctr_dec(&(DB)->common.counters, ERTS_DB_TABLE_NITEMS_COUNTER_ID); \
+ } while(0)
#define RESET_NITEMS(DB) \
erts_flxctr_reset(&(DB)->common.counters, ERTS_DB_TABLE_NITEMS_COUNTER_ID)
-/*
- * The following symbols can be manipulated to "tune" the linear hash array
- */
+
#define GROW_LIMIT(NACTIVE) ((NACTIVE)*1)
-#define SHRINK_LIMIT(NACTIVE) ((NACTIVE) / 2)
+#define SHRINK_LIMIT(TB) erts_atomic_read_nob(&(TB)->shrink_limit)
/*
** We want the first mandatory segment to be small (to reduce minimal footprint)
@@ -129,14 +168,16 @@
: ((struct segment**) erts_atomic_read_nob(&(tb)->segtab)))
#endif
#define NACTIVE(tb) ((int)erts_atomic_read_nob(&(tb)->nactive))
-#define NITEMS(tb) \
- ((Sint)erts_flxctr_read_centralized(&(tb)->common.counters, \
- ERTS_DB_TABLE_NITEMS_COUNTER_ID))
#define SLOT_IX_TO_SEG_IX(i) (((i)+(EXT_SEGSZ-FIRST_SEGSZ)) >> EXT_SEGSZ_EXP)
#define BUCKET(tb, i) SEGTAB(tb)[SLOT_IX_TO_SEG_IX(i)]->buckets[(i) & EXT_SEGSZ_MASK]
+#ifdef DEBUG
+# define DBG_BUCKET_INACTIVE ((HashDbTerm*)0xdead5107)
+#endif
+
+
/*
* When deleting a table, the number of records to delete.
* Approximate number, because we must delete entire buckets.
@@ -224,7 +265,8 @@ static ERTS_INLINE int is_pseudo_deleted(HashDbTerm* p)
make_internal_hash(term, 0)) & MAX_HASH_MASK)
# define DB_HASH_LOCK_MASK (DB_HASH_LOCK_CNT-1)
-# define GET_LOCK(tb,hval) (&(tb)->locks->lck_vec[(hval) & DB_HASH_LOCK_MASK].lck)
+# define GET_LOCK(tb,hval) (&(tb)->locks->lck_vec[(hval) & DB_HASH_LOCK_MASK].lck_ctr.lck)
+# define GET_LOCK_AND_CTR(tb,hval) (&(tb)->locks->lck_vec[(hval) & DB_HASH_LOCK_MASK].lck_ctr)
# define GET_LOCK_MAYBE(tb,hval) ((tb)->common.is_thread_safe ? NULL : GET_LOCK(tb,hval))
/* Fine grained read lock */
@@ -252,6 +294,20 @@ static ERTS_INLINE erts_rwmtx_t* WLOCK_HASH(DbTableHash* tb, HashValue hval)
}
}
+/* Fine grained write lock */
+static ERTS_INLINE
+DbTableHashLockAndCounter* WLOCK_HASH_GET_LCK_AND_CTR(DbTableHash* tb, HashValue hval)
+{
+ if (tb->common.is_thread_safe) {
+ return NULL;
+ } else {
+ DbTableHashLockAndCounter* lck_ctr = GET_LOCK_AND_CTR(tb,hval);
+ ASSERT(tb->common.type & DB_FINE_LOCKED);
+ erts_rwmtx_rwlock(&lck_ctr->lck);
+ return lck_ctr;
+ }
+}
+
static ERTS_INLINE void RUNLOCK_HASH(erts_rwmtx_t* lck)
{
if (lck != NULL) {
@@ -266,6 +322,13 @@ static ERTS_INLINE void WUNLOCK_HASH(erts_rwmtx_t* lck)
}
}
+static ERTS_INLINE void WUNLOCK_HASH_LCK_CTR(DbTableHashLockAndCounter* lck_ctr)
+{
+ if (lck_ctr != NULL) {
+ erts_rwmtx_rwunlock(&lck_ctr->lck);
+ }
+}
+
#ifdef ERTS_ENABLE_LOCK_CHECK
# define IFN_EXCL(tb,cmd) (((tb)->common.is_thread_safe) || (cmd))
@@ -377,7 +440,7 @@ typedef int (*extra_match_validator_t)(int keypos, Eterm match, Eterm guard, Ete
*/
static struct ext_segtab* alloc_ext_segtab(DbTableHash* tb, unsigned seg_ix);
static void alloc_seg(DbTableHash *tb);
-static int free_seg(DbTableHash *tb, int free_records);
+static int free_seg(DbTableHash *tb);
static HashDbTerm* next_live(DbTableHash *tb, Uint *iptr, erts_rwmtx_t** lck_ptr,
HashDbTerm *list);
static HashDbTerm* search_list(DbTableHash* tb, Eterm key,
@@ -468,18 +531,24 @@ db_lookup_dbterm_hash(Process *p, DbTable *tbl, Eterm key, Eterm obj,
DbUpdateHandle* handle);
static void
db_finalize_dbterm_hash(int cret, DbUpdateHandle* handle);
+static void* db_eterm_to_dbterm_hash(int compress, int keypos, Eterm obj);
+static void* db_dbterm_list_prepend_hash(void* list, void* db_term);
+static void* db_dbterm_list_remove_first_hash(void** list);
+static int db_put_dbterm_hash(DbTable* tb,
+ void* obj,
+ int key_clash_fail,
+ SWord *consumed_reds_p);
+static void db_free_dbterm_hash(int compressed, void* obj);
+static Eterm db_get_dbterm_key_hash(DbTable* tb, void* db_term);
static int
db_get_binary_info_hash(Process *p, DbTable *tbl, Eterm key, Eterm *ret);
static int db_raw_first_hash(Process* p, DbTable *tbl, Eterm *ret);
static int db_raw_next_hash(Process* p, DbTable *tbl, Eterm key, Eterm *ret);
-static ERTS_INLINE void try_shrink(DbTableHash* tb)
+static ERTS_INLINE void try_shrink(DbTableHash* tb, Sint nitems)
{
- int nactive = NACTIVE(tb);
- int nitems = NITEMS(tb);
- if (nactive > FIRST_SEGSZ && nitems < SHRINK_LIMIT(nactive)
- && !IS_FIXED(tb)) {
+ if (nitems < SHRINK_LIMIT(tb) && !IS_FIXED(tb)) {
shrink(tb, nitems);
}
}
@@ -512,28 +581,55 @@ static ERTS_INLINE int has_key(DbTableHash* tb, HashDbTerm* b,
}
}
-static ERTS_INLINE HashDbTerm* new_dbterm(DbTableHash* tb, Eterm obj)
+static ERTS_INLINE HashDbTerm* new_dbterm_hash(DbTableCommon* tb, Eterm obj)
{
HashDbTerm* p;
- if (tb->common.compress) {
- p = db_store_term_comp(&tb->common, NULL, offsetof(HashDbTerm,dbterm), obj);
+ if (tb->compress) {
+ p = db_store_term_comp(tb, tb->keypos, NULL, offsetof(HashDbTerm,dbterm), obj);
}
else {
- p = db_store_term(&tb->common, NULL, offsetof(HashDbTerm,dbterm), obj);
+ p = db_store_term(tb, NULL, offsetof(HashDbTerm,dbterm), obj);
+ }
+ return p;
+}
+
+/*
+ * This function only differ from new_dbterm_hash in that it does not
+ * adjust the memory size of a given table.
+ */
+static ERTS_INLINE HashDbTerm* new_dbterm_hash_no_tab(int compress, int keypos, Eterm obj)
+{
+ HashDbTerm* p;
+ if (compress) {
+ p = db_store_term_comp(NULL, keypos, NULL, offsetof(HashDbTerm,dbterm), obj);
+ } else {
+ p = db_store_term(NULL, NULL, offsetof(HashDbTerm,dbterm), obj);
}
return p;
}
+static ERTS_INLINE HashDbTerm* new_dbterm(DbTableHash* tb, Eterm obj)
+{
+ return new_dbterm_hash(&tb->common, obj);
+}
+
static ERTS_INLINE HashDbTerm* replace_dbterm(DbTableHash* tb, HashDbTerm* old,
Eterm obj)
{
HashDbTerm* ret;
ASSERT(old != NULL);
if (tb->common.compress) {
- ret = db_store_term_comp(&tb->common, &(old->dbterm), offsetof(HashDbTerm,dbterm), obj);
+ ret = db_store_term_comp(&tb->common,
+ tb->common.keypos,
+ &(old->dbterm),
+ offsetof(HashDbTerm,dbterm),
+ obj);
}
else {
- ret = db_store_term(&tb->common, &(old->dbterm), offsetof(HashDbTerm,dbterm), obj);
+ ret = db_store_term(&tb->common,
+ &(old->dbterm),
+ offsetof(HashDbTerm,dbterm),
+ obj);
}
return ret;
}
@@ -575,6 +671,12 @@ DbTableMethod db_hash =
db_foreach_offheap_hash,
db_lookup_dbterm_hash,
db_finalize_dbterm_hash,
+ db_eterm_to_dbterm_hash,
+ db_dbterm_list_prepend_hash,
+ db_dbterm_list_remove_first_hash,
+ db_put_dbterm_hash,
+ db_free_dbterm_hash,
+ db_get_dbterm_key_hash,
db_get_binary_info_hash,
db_raw_first_hash,
db_raw_next_hash
@@ -693,6 +795,7 @@ int db_create_hash(Process *p, DbTable *tbl)
erts_atomic_init_nob(&tb->szm, FIRST_SEGSZ_MASK);
erts_atomic_init_nob(&tb->nactive, FIRST_SEGSZ);
+ erts_atomic_init_nob(&tb->shrink_limit, 0);
erts_atomic_init_nob(&tb->fixdel, (erts_aint_t)NULL);
erts_atomic_init_nob(&tb->segtab, (erts_aint_t)NULL);
SET_SEGTAB(tb, tb->first_segtab);
@@ -715,8 +818,9 @@ int db_create_hash(Process *p, DbTable *tbl)
(DbTable *) tb,
sizeof(DbTableHashFineLocks));
for (i=0; i<DB_HASH_LOCK_CNT; ++i) {
- erts_rwmtx_init_opt(&tb->locks->lck_vec[i].lck, &rwmtx_opt,
+ erts_rwmtx_init_opt(&tb->locks->lck_vec[i].lck_ctr.lck, &rwmtx_opt,
"db_hash_slot", tb->common.the_name, ERTS_LOCK_FLAGS_CATEGORY_DB);
+ tb->locks->lck_vec[i].lck_ctr.nitems = 0;
}
/* This important property is needed to guarantee the two buckets
* involved in a grow/shrink operation it protected by the same lock:
@@ -779,7 +883,7 @@ static int db_next_hash(Process *p, DbTable *tbl, Eterm key, Eterm *ret)
b = next_live(tb, &ix, &lck, b->next);
if (tb->common.status & (DB_BAG | DB_DUPLICATE_BAG)) {
while (b != 0) {
- if (!has_key(tb, b, key, hval) && !is_pseudo_deleted(b)) {
+ if (!has_key(tb, b, key, hval)) {
break;
}
b = next_live(tb, &ix, &lck, b->next);
@@ -789,13 +893,56 @@ static int db_next_hash(Process *p, DbTable *tbl, Eterm key, Eterm *ret)
*ret = am_EOT;
}
else {
+ ASSERT(!is_pseudo_deleted(b));
*ret = db_copy_key(p, tbl, &b->dbterm);
RUNLOCK_HASH(lck);
}
return DB_ERROR_NONE;
}
-int db_put_hash(DbTable *tbl, Eterm obj, int key_clash_fail)
+static int db_eq_terms_comp(DbTableCommon* tb, DbTerm* a, DbTerm* b)
+{
+ ErlOffHeap tmp_offheap_a;
+ Eterm* allocp_a;
+ Eterm* hp_a;
+ Eterm tmp_a;
+ ErlOffHeap tmp_offheap_b;
+ Eterm* allocp_b;
+ Eterm* hp_b;
+ Eterm tmp_b;
+ int is_eq;
+
+ ASSERT(tb->compress);
+ hp_a = allocp_a = erts_alloc(ERTS_ALC_T_TMP, b->size*sizeof(Eterm));
+ tmp_offheap_a.first = NULL;
+ tmp_a = db_copy_from_comp(tb, a, &hp_a, &tmp_offheap_a);
+
+ hp_b = allocp_b = erts_alloc(ERTS_ALC_T_TMP, b->size*sizeof(Eterm));
+ tmp_offheap_b.first = NULL;
+ tmp_b = db_copy_from_comp(tb, b, &hp_b, &tmp_offheap_b);
+
+ is_eq = eq(tmp_a,tmp_b);
+ erts_cleanup_offheap(&tmp_offheap_a);
+ erts_free(ERTS_ALC_T_TMP, allocp_a);
+ erts_cleanup_offheap(&tmp_offheap_b);
+ erts_free(ERTS_ALC_T_TMP, allocp_b);
+ return is_eq;
+}
+
+static ERTS_INLINE int db_terms_eq(DbTableCommon* tb, DbTerm* a, DbTerm* b)
+{
+ if (!tb->compress) {
+ return EQ(make_tuple(a->tpl), make_tuple(b->tpl));
+ }
+ else {
+ return db_eq_terms_comp(tb, a, b);
+ }
+}
+
+static int db_put_dbterm_hash(DbTable* tbl,
+ void* ob,
+ int key_clash_fail,
+ SWord *consumed_reds_p)
{
DbTableHash *tb = &tbl->hash;
HashValue hval;
@@ -804,13 +951,126 @@ int db_put_hash(DbTable *tbl, Eterm obj, int key_clash_fail)
HashDbTerm** bp;
HashDbTerm* b;
HashDbTerm* q;
- erts_rwmtx_t* lck;
+ DbTableHashLockAndCounter* lck_ctr;
int nitems;
int ret = DB_ERROR_NONE;
+ HashDbTerm *value_to_insert = ob;
+ Uint size_to_insert = db_term_size(tbl, value_to_insert, offsetof(HashDbTerm, dbterm));
+ ERTS_DB_ALC_MEM_UPDATE_(tbl, 0, size_to_insert);
+ key = GETKEY(tb, value_to_insert->dbterm.tpl);
+ hval = MAKE_HASH(key);
+ value_to_insert->hvalue = hval;
+ lck_ctr = WLOCK_HASH_GET_LCK_AND_CTR(tb, hval);
+ ix = hash_to_ix(tb, hval);
+ bp = &BUCKET(tb, ix);
+ b = *bp;
+
+ for (;;) {
+ if (b == NULL) {
+ goto Lnew;
+ }
+ if (has_key(tb,b,key,hval)) {
+ break;
+ }
+ bp = &b->next;
+ b = b->next;
+ }
+ /* Key found
+ */
+ if (tb->common.status & DB_SET) {
+ HashDbTerm* bnext = b->next;
+ if (is_pseudo_deleted(b)) {
+ INC_NITEMS(tb, lck_ctr, hval);
+ b->pseudo_deleted = 0;
+ }
+ else if (key_clash_fail) {
+ ret = DB_ERROR_BADKEY;
+ goto Ldone;
+ }
+ value_to_insert->pseudo_deleted = b->pseudo_deleted;
+ free_term(tb, b);
+ q = value_to_insert;
+ q->next = bnext;
+ ASSERT(q->hvalue == hval);
+ *bp = q;
+ goto Ldone;
+ }
+ else if (key_clash_fail) { /* && (DB_BAG || DB_DUPLICATE_BAG) */
+ q = b;
+ do {
+ if (!is_pseudo_deleted(q)) {
+ ret = DB_ERROR_BADKEY;
+ goto Ldone;
+ }
+ q = q->next;
+ }while (q != NULL && has_key(tb,q,key,hval));
+ }
+ else if (tb->common.status & DB_BAG) {
+ HashDbTerm** qp = bp;
+ q = b;
+ do {
+ if (db_terms_eq(&tb->common,
+ &value_to_insert->dbterm,
+ &q->dbterm)) {
+ if (is_pseudo_deleted(q)) {
+ INC_NITEMS(tb, lck_ctr, hval);
+ q->pseudo_deleted = 0;
+ ASSERT(q->hvalue == hval);
+ if (q != b) { /* must move to preserve key insertion order */
+ *qp = q->next;
+ q->next = b;
+ *bp = q;
+ }
+ }
+ free_term(tb, value_to_insert);
+ goto Ldone;
+ }
+ qp = &q->next;
+ q = *qp;
+ (*consumed_reds_p)++;
+ }while (q != NULL && has_key(tb,q,key,hval));
+ }
+ /*else DB_DUPLICATE_BAG */
+
+Lnew:
+ q = value_to_insert;
+ q->hvalue = hval;
+ q->pseudo_deleted = 0;
+ q->next = b;
+ *bp = q;
+ INC_NITEMS(tb, lck_ctr, hval);
+ nitems = NITEMS_ESTIMATE(tb, lck_ctr, hval);
+ WUNLOCK_HASH_LCK_CTR(lck_ctr);
+ {
+ int nactive = NACTIVE(tb);
+ if (nitems > GROW_LIMIT(nactive) && !IS_FIXED(tb)) {
+ grow(tb, nitems);
+ }
+ }
+ return DB_ERROR_NONE;
+
+Ldone:
+ WUNLOCK_HASH_LCK_CTR(lck_ctr);
+ return ret;
+}
+
+int db_put_hash(DbTable *tbl, Eterm obj, int key_clash_fail,
+ SWord *consumed_reds_p)
+{
+ DbTableHash *tb = &tbl->hash;
+ HashValue hval;
+ int ix;
+ Eterm key;
+ HashDbTerm** bp;
+ HashDbTerm* b;
+ HashDbTerm* q;
+ DbTableHashLockAndCounter* lck_ctr;
+ Sint nitems;
+ int ret = DB_ERROR_NONE;
key = GETKEY(tb, tuple_val(obj));
hval = MAKE_HASH(key);
- lck = WLOCK_HASH(tb, hval);
+ lck_ctr = WLOCK_HASH_GET_LCK_AND_CTR(tb, hval);
ix = hash_to_ix(tb, hval);
bp = &BUCKET(tb, ix);
b = *bp;
@@ -830,7 +1090,7 @@ int db_put_hash(DbTable *tbl, Eterm obj, int key_clash_fail)
if (tb->common.status & DB_SET) {
HashDbTerm* bnext = b->next;
if (is_pseudo_deleted(b)) {
- INC_NITEMS(tb);
+ INC_NITEMS(tb, lck_ctr, hval);
b->pseudo_deleted = 0;
}
else if (key_clash_fail) {
@@ -859,7 +1119,7 @@ int db_put_hash(DbTable *tbl, Eterm obj, int key_clash_fail)
do {
if (db_eq(&tb->common,obj,&q->dbterm)) {
if (is_pseudo_deleted(q)) {
- INC_NITEMS(tb);
+ INC_NITEMS(tb, lck_ctr, hval);
q->pseudo_deleted = 0;
ASSERT(q->hvalue == hval);
if (q != b) { /* must move to preserve key insertion order */
@@ -871,8 +1131,10 @@ int db_put_hash(DbTable *tbl, Eterm obj, int key_clash_fail)
goto Ldone;
}
qp = &q->next;
- q = *qp;
- }while (q != NULL && has_key(tb,q,key,hval));
+ q = *qp;
+ (*consumed_reds_p)++;
+ }while (q != NULL && has_key(tb,q,key,hval));
+
}
/*else DB_DUPLICATE_BAG */
@@ -882,10 +1144,11 @@ Lnew:
q->pseudo_deleted = 0;
q->next = b;
*bp = q;
- nitems = INC_NITEMS(tb);
- WUNLOCK_HASH(lck);
+ INC_NITEMS(tb, lck_ctr, hval);
+ nitems = NITEMS_ESTIMATE(tb, lck_ctr, hval);
+ WUNLOCK_HASH_LCK_CTR(lck_ctr);
{
- int nactive = NACTIVE(tb);
+ int nactive = NACTIVE(tb);
if (nitems > GROW_LIMIT(nactive) && !IS_FIXED(tb)) {
grow(tb, nitems);
}
@@ -893,7 +1156,7 @@ Lnew:
return DB_ERROR_NONE;
Ldone:
- WUNLOCK_HASH(lck);
+ WUNLOCK_HASH_LCK_CTR(lck_ctr);
return ret;
}
@@ -1047,11 +1310,11 @@ int db_erase_hash(DbTable *tbl, Eterm key, Eterm *ret)
HashDbTerm** bp;
HashDbTerm* b;
HashDbTerm* free_us = NULL;
- erts_rwmtx_t* lck;
+ DbTableHashLockAndCounter* lck_ctr;
int nitems_diff = 0;
-
+ Sint nitems;
hval = MAKE_HASH(key);
- lck = WLOCK_HASH(tb,hval);
+ lck_ctr = WLOCK_HASH_GET_LCK_AND_CTR(tb,hval);
ix = hash_to_ix(tb, hval);
bp = &BUCKET(tb, ix);
b = *bp;
@@ -1078,10 +1341,13 @@ int db_erase_hash(DbTable *tbl, Eterm key, Eterm *ret)
bp = &b->next;
b = b->next;
}
- WUNLOCK_HASH(lck);
if (nitems_diff) {
- ADD_NITEMS(tb, nitems_diff);
- try_shrink(tb);
+ ADD_NITEMS(tb, lck_ctr, hval, nitems_diff);
+ nitems = NITEMS_ESTIMATE(tb, lck_ctr, hval);
+ }
+ WUNLOCK_HASH_LCK_CTR(lck_ctr);
+ if (nitems_diff) {
+ try_shrink(tb, nitems);
}
free_term_list(tb, free_us);
*ret = am_true;
@@ -1099,14 +1365,15 @@ static int db_erase_object_hash(DbTable *tbl, Eterm object, Eterm *ret)
HashDbTerm** bp;
HashDbTerm* b;
HashDbTerm* free_us = NULL;
- erts_rwmtx_t* lck;
+ DbTableHashLockAndCounter* lck_ctr;
int nitems_diff = 0;
+ Sint nitems;
int nkeys = 0;
Eterm key;
key = GETKEY(tb, tuple_val(object));
hval = MAKE_HASH(key);
- lck = WLOCK_HASH(tb,hval);
+ lck_ctr = WLOCK_HASH_GET_LCK_AND_CTR(tb,hval);
ix = hash_to_ix(tb, hval);
bp = &BUCKET(tb, ix);
b = *bp;
@@ -1139,10 +1406,13 @@ static int db_erase_object_hash(DbTable *tbl, Eterm object, Eterm *ret)
bp = &b->next;
b = b->next;
}
- WUNLOCK_HASH(lck);
if (nitems_diff) {
- ADD_NITEMS(tb, nitems_diff);
- try_shrink(tb);
+ ADD_NITEMS(tb, lck_ctr, hval, nitems_diff);
+ nitems = NITEMS_ESTIMATE(tb, lck_ctr, hval);
+ }
+ WUNLOCK_HASH_LCK_CTR(lck_ctr);
+ if (nitems_diff) {
+ try_shrink(tb, nitems);
}
free_term_list(tb, free_us);
*ret = am_true;
@@ -2029,9 +2299,11 @@ static int select_delete_on_match_res(traverse_context_t* ctx_base, Sint slot_ix
HashDbTerm** current_ptr = *current_ptr_ptr;
select_delete_context_t* ctx = (select_delete_context_t*) ctx_base;
HashDbTerm* del;
+ DbTableHashLockAndCounter* lck_ctr;
+ Uint32 hval;
if (match_res != am_true)
return 0;
-
+ hval = (*current_ptr)->hvalue;
if (NFIXED(ctx->base.tb) > ctx->fixated_by_me) { /* fixated by others? */
if (slot_ix != ctx->last_pseudo_delete) {
if (!add_fixed_deletion(ctx->base.tb, slot_ix, ctx->fixated_by_me))
@@ -2047,23 +2319,58 @@ static int select_delete_on_match_res(traverse_context_t* ctx_base, Sint slot_ix
del->next = ctx->free_us;
ctx->free_us = del;
}
- DEC_NITEMS(ctx->base.tb);
+ lck_ctr = GET_LOCK_AND_CTR(ctx->base.tb,slot_ix);
+ DEC_NITEMS(ctx->base.tb, lck_ctr, hval);
return 1;
}
+/* This function is only safe to call while the table lock is held in
+ write mode */
+static Sint get_nitems_from_locks_or_counter(DbTableHash* tb)
+{
+ if (IS_DECENTRALIZED_CTRS(tb)) {
+ int i;
+ Sint total = 0;
+ for (i=0; i < DB_HASH_LOCK_CNT; ++i) {
+ total += tb->locks->lck_vec[i].lck_ctr.nitems;
+ }
+ return total;
+ } else {
+ return erts_flxctr_read_centralized(&tb->common.counters,
+ ERTS_DB_TABLE_NITEMS_COUNTER_ID);
+ }
+}
+
static int select_delete_on_loop_ended(traverse_context_t* ctx_base,
Sint slot_ix, Sint got,
Sint iterations_left, Binary** mpp,
Eterm* ret)
{
select_delete_context_t* ctx = (select_delete_context_t*) ctx_base;
- free_term_list(ctx->base.tb, ctx->free_us);
+ DbTableHash* tb = ctx->base.tb;
+ free_term_list(tb, ctx->free_us);
ctx->free_us = NULL;
ASSERT(iterations_left <= MAX_SELECT_DELETE_ITERATIONS);
BUMP_REDS(ctx->base.p, MAX_SELECT_DELETE_ITERATIONS - iterations_left);
if (got) {
- try_shrink(ctx->base.tb);
+ Sint nitems;
+ if (IS_DECENTRALIZED_CTRS(tb)) {
+ /* Get a random hash value so we can get an nitems
+ estimate from a random lock */
+ HashValue hval =
+ (HashValue)&ctx +
+ (HashValue)iterations_left +
+ (HashValue)erts_get_scheduler_data()->reductions;
+ erts_rwmtx_t* lck = RLOCK_HASH(tb, hval);
+ DbTableHashLockAndCounter* lck_ctr = GET_LOCK_AND_CTR(tb, hval);
+ nitems = NITEMS_ESTIMATE(tb, lck_ctr, hval);
+ RUNLOCK_HASH(lck);
+ } else {
+ nitems = erts_flxctr_read_centralized(&tb->common.counters,
+ ERTS_DB_TABLE_NITEMS_COUNTER_ID);
+ }
+ try_shrink(tb, nitems);
}
*ret = erts_make_integer(got, ctx->base.p);
return DB_ERROR_NONE;
@@ -2294,9 +2601,10 @@ static int db_take_hash(Process *p, DbTable *tbl, Eterm key, Eterm *ret)
HashDbTerm **bp, *b;
HashDbTerm *free_us = NULL;
HashValue hval = MAKE_HASH(key);
- erts_rwmtx_t *lck = WLOCK_HASH(tb, hval);
+ DbTableHashLockAndCounter *lck_ctr = WLOCK_HASH_GET_LCK_AND_CTR(tb, hval);
int ix = hash_to_ix(tb, hval);
int nitems_diff = 0;
+ Sint nitems;
*ret = NIL;
for (bp = &BUCKET(tb, ix), b = *bp; b; bp = &b->next, b = b->next) {
@@ -2322,10 +2630,13 @@ static int db_take_hash(Process *p, DbTable *tbl, Eterm key, Eterm *ret)
break;
}
}
- WUNLOCK_HASH(lck);
if (nitems_diff) {
- ADD_NITEMS(tb, nitems_diff);
- try_shrink(tb);
+ ADD_NITEMS(tb, lck_ctr, hval, nitems_diff);
+ nitems = NITEMS_ESTIMATE(tb, lck_ctr, hval);
+ }
+ WUNLOCK_HASH_LCK_CTR(lck_ctr);
+ if (nitems_diff) {
+ try_shrink(tb, nitems);
}
free_term_list(tb, free_us);
return DB_ERROR_NONE;
@@ -2449,7 +2760,7 @@ static void db_print_hash(fmtfn_t to, void *to_arg, int show, DbTable *tbl)
static int db_free_empty_table_hash(DbTable *tbl)
{
- ASSERT(NITEMS(tbl) == 0);
+ ASSERT(get_nitems_from_locks_or_counter(&tbl->hash) == 0);
while (db_free_table_continue_hash(tbl, ERTS_SWORD_MAX) < 0)
;
return 0;
@@ -2474,7 +2785,7 @@ static SWord db_free_table_continue_hash(DbTable *tbl, SWord reds)
erts_atomic_set_relb(&tb->fixdel, (erts_aint_t)NULL);
while(tb->nslots != 0) {
- reds -= EXT_SEGSZ/64 + free_seg(tb, 1);
+ reds -= EXT_SEGSZ/64 + free_seg(tb);
/*
* If we have done enough work, get out here.
@@ -2492,8 +2803,11 @@ static SWord db_free_table_continue_hash(DbTable *tbl, SWord reds)
(void*)tb->locks, sizeof(DbTableHashFineLocks));
tb->locks = NULL;
}
- ASSERT(sizeof(DbTable) == erts_flxctr_read_approx(&tb->common.counters,
- ERTS_DB_TABLE_MEM_COUNTER_ID));
+ ASSERT(erts_flxctr_is_snapshot_ongoing(&tb->common.counters) ||
+ ((sizeof(DbTable) +
+ erts_flxctr_nr_of_allocated_bytes(&tb->common.counters)) ==
+ erts_flxctr_read_approx(&tb->common.counters,
+ ERTS_DB_TABLE_MEM_COUNTER_ID)));
return reds; /* Done */
}
@@ -2672,6 +2986,63 @@ static struct ext_segtab* alloc_ext_segtab(DbTableHash* tb, unsigned seg_ix)
return est;
}
+static void calc_shrink_limit(DbTableHash* tb)
+{
+ erts_aint_t shrink_limit;
+ int sample_size_is_enough = 1;
+
+ if (IS_DECENTRALIZED_CTRS(tb)) {
+ /*
+ Cochran’s Sample Size Formula indicates that we will get
+ good estimates if we have 100 buckets or more per lock (see
+ calculations below)
+ */
+ /* square of z-score 95% confidence */
+ /* const double z2 = 1.96*1.96; */
+ /* Estimated propotion used buckets */
+ /* const double p = 0.5; */
+ /* margin of error */
+ /* const double moe = 0.1; */
+ /* const double moe2 = moe*moe; */
+ /* Cochran’s Sample Size Formula x=96.040 */
+ /* const double x = (z2 * p * (1-p)) / moe2; */
+ /* Modification for smaller populations */
+ /* for(int n = 10; n < 1000; n = n + 100){ */
+ /* const double d = n*x / (x + n - 1) + 1; */
+ /* printf("Cochran_formula=%f size=%d mod_with_size=%f\n", x, n, d); */
+ /* } */
+ const int needed_slots = 100 * DB_HASH_LOCK_CNT;
+ if (tb->nslots < needed_slots) {
+ sample_size_is_enough = 0;
+ }
+ }
+
+ if (sample_size_is_enough && tb->nslots >= (FIRST_SEGSZ + 2*EXT_SEGSZ)) {
+ /*
+ * Start shrink when the sample size is big enough for
+ * decentralized counters if decentralized counters are used
+ * and when we can remove one extra segment and still remain
+ * below 50% load.
+ */
+ shrink_limit = (tb->nslots - EXT_SEGSZ) / 2;
+ }
+ else {
+ /*
+ * But don't shrink below two segments.
+ * Why? In order to have chance of getting rid of the last extra segment,
+ * and rehash it into the first small segment, we either have to start
+ * early and do speculative joining of buckets or we have to join a lot
+ * of buckets during each delete-op.
+ *
+ * Instead keep segment #2 once allocated. I also think it's a good bet
+ * a shrinking large table will grow large again.
+ */
+ shrink_limit = 0;
+ }
+ erts_atomic_set_nob(&tb->shrink_limit, shrink_limit);
+}
+
+
/* Extend table with one new segment
*/
static void alloc_seg(DbTableHash *tb)
@@ -2690,8 +3061,17 @@ static void alloc_seg(DbTableHash *tb)
segtab[seg_ix] = (struct segment*) erts_db_alloc(ERTS_ALC_T_DB_SEG,
(DbTable *) tb,
SIZEOF_SEGMENT(EXT_SEGSZ));
- sys_memset(segtab[seg_ix], 0, SIZEOF_SEGMENT(EXT_SEGSZ));
+#ifdef DEBUG
+ {
+ int i;
+ for (i = 0; i < EXT_SEGSZ; i++) {
+ segtab[seg_ix]->buckets[i] = DBG_BUCKET_INACTIVE;
+ }
+ }
+#endif
tb->nslots += EXT_SEGSZ;
+
+ calc_shrink_limit(tb);
}
static void dealloc_ext_segtab(void* lop_data)
@@ -2701,10 +3081,19 @@ static void dealloc_ext_segtab(void* lop_data)
erts_free(ERTS_ALC_T_DB_SEG, est);
}
-/* Shrink table by freeing the top segment
+struct dealloc_seg_ops {
+ struct segment* segp;
+ Uint seg_sz;
+
+ struct ext_segtab* est;
+};
+
+/* Shrink table by removing the top segment
** free_records: 1=free any records in segment, 0=assume segment is empty
+** ds_ops: (out) Instructions for dealloc_seg().
*/
-static int free_seg(DbTableHash *tb, int free_records)
+static int remove_seg(DbTableHash *tb, int free_records,
+ struct dealloc_seg_ops *ds_ops)
{
const int seg_ix = SLOT_IX_TO_SEG_IX(tb->nslots) - 1;
struct segment** const segtab = SEGTAB(tb);
@@ -2712,24 +3101,47 @@ static int free_seg(DbTableHash *tb, int free_records)
Uint seg_sz;
int nrecords = 0;
+ ERTS_LC_ASSERT(IS_TAB_WLOCKED(tb) || tb->common.status & DB_DELETE
+ || erts_atomic_read_nob(&tb->is_resizing));
+
ASSERT(segp != NULL);
-#ifndef DEBUG
- if (free_records)
-#endif
- {
- int i = (seg_ix == 0) ? FIRST_SEGSZ : EXT_SEGSZ;
- while (i--) {
- HashDbTerm* p = segp->buckets[i];
+ if (free_records) {
+ int ix, n;
+ if (seg_ix == 0) {
+ /* First segment (always fully active) */
+ n = FIRST_SEGSZ;
+ ix = FIRST_SEGSZ-1;
+ }
+ else if (NACTIVE(tb) < tb->nslots) {
+ /* Last extended segment partially active */
+ n = (NACTIVE(tb) - FIRST_SEGSZ) & EXT_SEGSZ_MASK;
+ ix = (NACTIVE(tb)-1) & EXT_SEGSZ_MASK;
+ }
+ else {
+ /* Full extended segment */
+ n = EXT_SEGSZ;
+ ix = EXT_SEGSZ - 1;
+ }
+ for ( ; n > 0; n--, ix--) {
+ HashDbTerm* p = segp->buckets[ix & EXT_SEGSZ_MASK];
while(p != 0) {
HashDbTerm* nxt = p->next;
- ASSERT(free_records); /* segment not empty as assumed? */
free_term(tb, p);
p = nxt;
++nrecords;
}
}
}
-
+#ifdef DEBUG
+ else {
+ int ix = (seg_ix == 0) ? FIRST_SEGSZ-1 : EXT_SEGSZ-1;
+ for ( ; ix >= 0; ix--) {
+ ASSERT(segp->buckets[ix] == DBG_BUCKET_INACTIVE);
+ }
+ }
+#endif
+
+ ds_ops->est = NULL;
if (seg_ix >= NSEG_1) {
struct ext_segtab* est = ErtsContainerStruct_(segtab,struct ext_segtab,segtab);
@@ -2738,35 +3150,64 @@ static int free_seg(DbTableHash *tb, int free_records)
SET_SEGTAB(tb, est->prev_segtab);
tb->nsegs = est->prev_nsegs;
- if (!tb->common.is_thread_safe) {
- /*
- * Table is doing a graceful shrink operation and we must avoid
- * deallocating this segtab while it may still be read by other
- * threads. Schedule deallocation with thread progress to make
- * sure no lingering threads are still hanging in BUCKET macro
- * with an old segtab pointer.
- */
- erts_schedule_db_free(&tb->common, dealloc_ext_segtab,
- est, &est->lop,
- SIZEOF_EXT_SEGTAB(est->nsegs));
- }
- else
- erts_db_free(ERTS_ALC_T_DB_SEG, (DbTable*)tb, est,
- SIZEOF_EXT_SEGTAB(est->nsegs));
+ ds_ops->est = est;
}
}
+
seg_sz = (seg_ix == 0) ? FIRST_SEGSZ : EXT_SEGSZ;
- erts_db_free(ERTS_ALC_T_DB_SEG, (DbTable *)tb, segp, SIZEOF_SEGMENT(seg_sz));
+ tb->nslots -= seg_sz;
+ ASSERT(tb->nslots >= 0);
+
+ ds_ops->segp = segp;
+ ds_ops->seg_sz = seg_sz;
#ifdef DEBUG
if (seg_ix < tb->nsegs)
SEGTAB(tb)[seg_ix] = NULL;
#endif
- tb->nslots -= seg_sz;
- ASSERT(tb->nslots >= 0);
+ calc_shrink_limit(tb);
return nrecords;
}
+/*
+ * Deallocate segment removed by remove_seg()
+ */
+static void dealloc_seg(DbTableHash *tb, struct dealloc_seg_ops* ds_ops)
+{
+ struct ext_segtab* est = ds_ops->est;
+
+ if (est) {
+ if (!tb->common.is_thread_safe) {
+ /*
+ * Table is doing a graceful shrink operation and we must avoid
+ * deallocating this segtab while it may still be read by other
+ * threads. Schedule deallocation with thread progress to make
+ * sure no lingering threads are still hanging in BUCKET macro
+ * with an old segtab pointer.
+ */
+ erts_schedule_db_free(&tb->common, dealloc_ext_segtab,
+ est, &est->lop,
+ SIZEOF_EXT_SEGTAB(est->nsegs));
+ }
+ else
+ erts_db_free(ERTS_ALC_T_DB_SEG, (DbTable*)tb, est,
+ SIZEOF_EXT_SEGTAB(est->nsegs));
+ }
+
+ erts_db_free(ERTS_ALC_T_DB_SEG, (DbTable *)tb,
+ ds_ops->segp, SIZEOF_SEGMENT(ds_ops->seg_sz));
+}
+
+/* Remove and deallocate top segment and all its contained objects */
+static int free_seg(DbTableHash *tb)
+{
+ struct dealloc_seg_ops ds_ops;
+ int reds;
+
+ reds = remove_seg(tb, 1, &ds_ops);
+ dealloc_seg(tb, &ds_ops);
+ return reds;
+}
/*
** Copy terms from ptr1 until ptr2
@@ -2811,9 +3252,11 @@ static Eterm build_term_list(Process* p, HashDbTerm* ptr1, HashDbTerm* ptr2,
static ERTS_INLINE int
begin_resizing(DbTableHash* tb)
{
- if (DB_USING_FINE_LOCKING(tb))
- return !erts_atomic_xchg_acqb(&tb->is_resizing, 1);
- else
+ if (DB_USING_FINE_LOCKING(tb)) {
+ return
+ !erts_atomic_read_acqb(&tb->is_resizing) &&
+ !erts_atomic_xchg_acqb(&tb->is_resizing, 1);
+ } else
ERTS_LC_ASSERT(IS_TAB_WLOCKED(tb));
return 1;
}
@@ -2888,6 +3331,7 @@ static void grow(DbTableHash* tb, int nitems)
pnext = &BUCKET(tb, from_ix);
p = *pnext;
to_pnext = &BUCKET(tb, to_ix);
+ ASSERT(*to_pnext == DBG_BUCKET_INACTIVE);
while (p != NULL) {
if (is_pseudo_deleted(p)) { /* rare but possible with fine locking */
*pnext = p->next;
@@ -2924,19 +3368,21 @@ abort:
*/
static void shrink(DbTableHash* tb, int nitems)
{
- HashDbTerm** src_bp;
- HashDbTerm** dst_bp;
+ struct dealloc_seg_ops ds_ops;
+ HashDbTerm* src;
+ HashDbTerm* tail;
HashDbTerm** bp;
erts_rwmtx_t* lck;
int src_ix, dst_ix, low_szm;
int nactive;
int loop_limit = 5;
+ ds_ops.segp = NULL;
do {
if (!begin_resizing(tb))
return; /* already in progress */
nactive = NACTIVE(tb);
- if (!(nactive > FIRST_SEGSZ && nitems < SHRINK_LIMIT(nactive))) {
+ if (!(nitems < SHRINK_LIMIT(tb))) {
goto abort; /* already done (race) */
}
src_ix = nactive - 1;
@@ -2953,41 +3399,49 @@ static void shrink(DbTableHash* tb, int nitems)
goto abort;
}
- src_bp = &BUCKET(tb, src_ix);
- dst_bp = &BUCKET(tb, dst_ix);
- bp = src_bp;
-
- /*
- * We join lists by appending "dst" at the end of "src"
- * as we must step through "src" anyway to purge pseudo deleted.
- */
- while(*bp != NULL) {
- if (is_pseudo_deleted(*bp)) {
- HashDbTerm* deleted = *bp;
- *bp = deleted->next;
- free_term(tb, deleted);
- } else {
- bp = &(*bp)->next;
- }
- }
- *bp = *dst_bp;
- *dst_bp = *src_bp;
- *src_bp = NULL;
-
+ src = BUCKET(tb, src_ix);
+#ifdef DEBUG
+ BUCKET(tb, src_ix) = DBG_BUCKET_INACTIVE;
+#endif
nactive = src_ix;
erts_atomic_set_nob(&tb->nactive, nactive);
if (dst_ix == 0) {
erts_atomic_set_relb(&tb->szm, low_szm);
}
- WUNLOCK_HASH(lck);
-
if (tb->nslots - src_ix >= EXT_SEGSZ) {
- free_seg(tb, 0);
+ remove_seg(tb, 0, &ds_ops);
}
done_resizing(tb);
- } while (--loop_limit
- && nactive > FIRST_SEGSZ && nitems < SHRINK_LIMIT(nactive));
+ if (src) {
+ /*
+ * We join buckets by appending "dst" list at the end of "src" list
+ * as we must step through "src" anyway to purge pseudo deleted.
+ */
+ bp = &BUCKET(tb, dst_ix);
+ tail = *bp;
+ *bp = src;
+
+ while(*bp != NULL) {
+ if (is_pseudo_deleted(*bp)) {
+ HashDbTerm* deleted = *bp;
+ *bp = deleted->next;
+ free_term(tb, deleted);
+ } else {
+ bp = &(*bp)->next;
+ }
+ }
+ *bp = tail;
+ }
+
+ WUNLOCK_HASH(lck);
+
+ if (ds_ops.segp) {
+ dealloc_seg(tb, &ds_ops);
+ ds_ops.segp = NULL;
+ }
+
+ } while (--loop_limit && nitems < SHRINK_LIMIT(tb));
return;
abort:
@@ -3047,13 +3501,13 @@ db_lookup_dbterm_hash(Process *p, DbTable *tbl, Eterm key, Eterm obj,
DbTableHash *tb = &tbl->hash;
HashValue hval;
HashDbTerm **bp, *b;
- erts_rwmtx_t* lck;
+ DbTableHashLockAndCounter* lck_ctr;
int flags = 0;
ASSERT(tb->common.status & DB_SET);
hval = MAKE_HASH(key);
- lck = WLOCK_HASH(tb, hval);
+ lck_ctr = WLOCK_HASH_GET_LCK_AND_CTR(tb, hval);
bp = &BUCKET(tb, hash_to_ix(tb, hval));
b = *bp;
@@ -3072,7 +3526,7 @@ db_lookup_dbterm_hash(Process *p, DbTable *tbl, Eterm key, Eterm obj,
}
if (obj == THE_NON_VALUE) {
- WUNLOCK_HASH(lck);
+ WUNLOCK_HASH_LCK_CTR(lck_ctr);
return 0;
}
@@ -3105,7 +3559,7 @@ db_lookup_dbterm_hash(Process *p, DbTable *tbl, Eterm key, Eterm obj,
ASSERT(q->hvalue == hval);
q->pseudo_deleted = 0;
*bp = b = q;
- INC_NITEMS(tb);
+ INC_NITEMS(tb, lck_ctr, hval);
}
HRelease(p, hend, htop);
@@ -3118,7 +3572,7 @@ Ldone:
handle->dbterm = &b->dbterm;
handle->flags = flags;
handle->new_size = b->dbterm.size;
- handle->u.hash.lck = lck;
+ handle->u.hash.lck_ctr = lck_ctr;
return 1;
}
@@ -3131,10 +3585,12 @@ db_finalize_dbterm_hash(int cret, DbUpdateHandle* handle)
DbTableHash *tb = &tbl->hash;
HashDbTerm **bp = (HashDbTerm **) handle->bp;
HashDbTerm *b = *bp;
- erts_rwmtx_t* lck = handle->u.hash.lck;
+ Uint32 hval = b->hvalue;
+ DbTableHashLockAndCounter* lck_ctr = handle->u.hash.lck_ctr;
HashDbTerm* free_me = NULL;
+ Sint nitems;
- ERTS_LC_ASSERT(IS_HASH_WLOCKED(tb, lck)); /* locked by db_lookup_dbterm_hash */
+ ERTS_LC_ASSERT(IS_HASH_WLOCKED(tb, &lck_ctr->lck)); /* locked by db_lookup_dbterm_hash */
ASSERT((&b->dbterm == handle->dbterm) == !(tb->common.compress && handle->flags & DB_MUST_RESIZE));
@@ -3146,11 +3602,11 @@ db_finalize_dbterm_hash(int cret, DbUpdateHandle* handle)
*bp = b->next;
free_me = b;
}
-
- WUNLOCK_HASH(lck);
if (!(handle->flags & DB_INC_TRY_GROW))
- DEC_NITEMS(tb);
- try_shrink(tb);
+ DEC_NITEMS(tb, lck_ctr, hval);
+ nitems = NITEMS_ESTIMATE(tb, lck_ctr, hval);
+ WUNLOCK_HASH_LCK_CTR(lck_ctr);
+ try_shrink(tb, nitems);
} else {
if (handle->flags & DB_MUST_RESIZE) {
ASSERT(cret == DB_ERROR_NONE);
@@ -3159,16 +3615,18 @@ db_finalize_dbterm_hash(int cret, DbUpdateHandle* handle)
}
if (handle->flags & DB_INC_TRY_GROW) {
int nactive;
- int nitems = INC_NITEMS(tb);
+ int nitems;
ASSERT(cret == DB_ERROR_NONE);
- WUNLOCK_HASH(lck);
+ INC_NITEMS(tb, lck_ctr, hval);
+ nitems = NITEMS_ESTIMATE(tb, lck_ctr, hval);
+ WUNLOCK_HASH_LCK_CTR(lck_ctr);
nactive = NACTIVE(tb);
if (nitems > GROW_LIMIT(nactive) && !IS_FIXED(tb)) {
grow(tb, nitems);
}
} else {
- WUNLOCK_HASH(lck);
+ WUNLOCK_HASH_LCK_CTR(lck_ctr);
}
}
@@ -3187,9 +3645,7 @@ static SWord db_delete_all_objects_hash(Process* p,
Eterm* nitems_holder_wb)
{
if (nitems_holder_wb != NULL) {
- Uint nr_of_items =
- erts_flxctr_read_centralized(&tbl->common.counters,
- ERTS_DB_TABLE_NITEMS_COUNTER_ID);
+ Uint nr_of_items = get_nitems_from_locks_or_counter(&tbl->hash);
*nitems_holder_wb = erts_make_integer(nr_of_items, p);
}
if (IS_FIXED(tbl)) {
@@ -3397,6 +3853,49 @@ static int db_raw_next_hash(Process *p, DbTable *tbl, Eterm key, Eterm *ret)
return DB_ERROR_NONE;
}
+static void* db_eterm_to_dbterm_hash(int compress, int keypos, Eterm obj)
+{
+ HashDbTerm* term = new_dbterm_hash_no_tab(compress, keypos, obj);
+ term->next = NULL;
+ return term;
+}
+
+static void* db_dbterm_list_prepend_hash(void* list, void* db_term)
+{
+ HashDbTerm* l = list;
+ HashDbTerm* t = db_term;
+ t->next = l;
+ return t;
+}
+
+static void* db_dbterm_list_remove_first_hash(void** list)
+{
+ if (*list == NULL) {
+ return NULL;
+ } else {
+ HashDbTerm* t = (*list);
+ HashDbTerm* l = t->next;
+ *list = l;
+ return t;
+ }
+}
+
+/*
+ * Frees a HashDbTerm without updating the memory footprint of the
+ * table.
+ */
+static void db_free_dbterm_hash(int compressed, void* obj)
+{
+ HashDbTerm* p = obj;
+ db_free_term_no_tab(compressed, p, offsetof(HashDbTerm, dbterm));
+}
+
+static Eterm db_get_dbterm_key_hash(DbTable* tb, void* db_term)
+{
+ HashDbTerm *value_to_insert = db_term;
+ return GETKEY(tb, value_to_insert->dbterm.tpl);
+}
+
/* For testing only */
Eterm erts_ets_hash_sizeof_ext_segtab(void)
{
@@ -3418,7 +3917,7 @@ void erts_lcnt_enable_db_hash_lock_count(DbTableHash *tb, int enable) {
}
for(i = 0; i < DB_HASH_LOCK_CNT; i++) {
- erts_lcnt_ref_t *ref = &tb->locks->lck_vec[i].lck.lcnt;
+ erts_lcnt_ref_t *ref = &tb->locks->lck_vec[i].lck_ctr.lck.lcnt;
if(enable) {
erts_lcnt_install_new_lock_info(ref, "db_hash_slot", tb->common.the_name,
diff --git a/erts/emulator/beam/erl_db_hash.h b/erts/emulator/beam/erl_db_hash.h
index 9759d8b466..b119129099 100644
--- a/erts/emulator/beam/erl_db_hash.h
+++ b/erts/emulator/beam/erl_db_hash.h
@@ -53,9 +53,14 @@ typedef struct hash_db_term {
#define DB_HASH_LOCK_CNT 64
#endif
+typedef struct DbTableHashLockAndCounter {
+ Sint nitems;
+ erts_rwmtx_t lck;
+} DbTableHashLockAndCounter;
+
typedef struct db_table_hash_fine_locks {
union {
- erts_rwmtx_t lck;
+ DbTableHashLockAndCounter lck_ctr;
byte _cache_line_alignment[ERTS_ALC_CACHE_LINE_ALIGN_SIZE(sizeof(erts_rwmtx_t))];
}lck_vec[DB_HASH_LOCK_CNT];
} DbTableHashFineLocks;
@@ -63,9 +68,10 @@ typedef struct db_table_hash_fine_locks {
typedef struct db_table_hash {
DbTableCommon common;
- /* SMP: szm and nactive are write-protected by is_resizing or table write lock */
+ /* szm, nactive, shrink_limit are write-protected by is_resizing or table write lock */
erts_atomic_t szm; /* current size mask. */
erts_atomic_t nactive; /* Number of "active" slots */
+ erts_atomic_t shrink_limit; /* Shrink table when fewer objects than this */
erts_atomic_t segtab; /* The segment table (struct segment**) */
struct segment* first_segtab[1];
@@ -93,7 +99,7 @@ Uint db_kept_items_hash(DbTableHash *tb);
int db_create_hash(Process *p,
DbTable *tbl /* [in out] */);
-int db_put_hash(DbTable *tbl, Eterm obj, int key_clash_fail);
+int db_put_hash(DbTable *tbl, Eterm obj, int key_clash_fail, SWord* consumed_reds_p);
int db_get_hash(Process *p, DbTable *tbl, Eterm key, Eterm *ret);
diff --git a/erts/emulator/beam/erl_db_tree.c b/erts/emulator/beam/erl_db_tree.c
index 49158108a2..f22a7ae1e9 100644
--- a/erts/emulator/beam/erl_db_tree.c
+++ b/erts/emulator/beam/erl_db_tree.c
@@ -142,20 +142,33 @@ static ERTS_INLINE TreeDbTerm* new_dbterm(DbTableCommon *tb, Eterm obj)
{
TreeDbTerm* p;
if (tb->compress) {
- p = db_store_term_comp(tb, NULL, offsetof(TreeDbTerm,dbterm), obj);
+ p = db_store_term_comp(tb, tb->keypos, NULL, offsetof(TreeDbTerm,dbterm), obj);
}
else {
p = db_store_term(tb, NULL, offsetof(TreeDbTerm,dbterm), obj);
}
return p;
}
+
+static ERTS_INLINE TreeDbTerm* new_dbterm_no_tab(int compress, int keypos, Eterm obj)
+{
+ TreeDbTerm* p;
+ if (compress) {
+ p = db_store_term_comp(NULL, keypos, NULL, offsetof(TreeDbTerm,dbterm), obj);
+ }
+ else {
+ p = db_store_term(NULL, NULL, offsetof(TreeDbTerm,dbterm), obj);
+ }
+ return p;
+}
+
static ERTS_INLINE TreeDbTerm* replace_dbterm(DbTableCommon *tb, TreeDbTerm* old,
Eterm obj)
{
TreeDbTerm* p;
ASSERT(old != NULL);
if (tb->compress) {
- p = db_store_term_comp(tb, &(old->dbterm), offsetof(TreeDbTerm,dbterm), obj);
+ p = db_store_term_comp(tb, tb->keypos, &(old->dbterm), offsetof(TreeDbTerm,dbterm), obj);
}
else {
p = db_store_term(tb, &(old->dbterm), offsetof(TreeDbTerm,dbterm), obj);
@@ -396,7 +409,7 @@ static int db_last_tree(Process *p, DbTable *tbl,
static int db_prev_tree(Process *p, DbTable *tbl,
Eterm key,
Eterm *ret);
-static int db_put_tree(DbTable *tbl, Eterm obj, int key_clash_fail);
+static int db_put_tree(DbTable *tbl, Eterm obj, int key_clash_fail, SWord *consumed_reds_p);
static int db_get_tree(Process *p, DbTable *tbl,
Eterm key, Eterm *ret);
static int db_member_tree(DbTable *tbl, Eterm key, Eterm *ret);
@@ -458,9 +471,11 @@ db_lookup_dbterm_tree(Process *, DbTable *, Eterm key, Eterm obj,
DbUpdateHandle*);
static void
db_finalize_dbterm_tree(int cret, DbUpdateHandle *);
-
static int db_get_binary_info_tree(Process*, DbTable*, Eterm key, Eterm *ret);
-
+static int db_put_dbterm_tree(DbTable* tbl, /* [in out] */
+ void* obj,
+ int key_clash_fail,
+ SWord *consumed_reds_p);
/*
** Static variables
@@ -503,6 +518,12 @@ DbTableMethod db_tree =
db_foreach_offheap_tree,
db_lookup_dbterm_tree,
db_finalize_dbterm_tree,
+ db_eterm_to_dbterm_tree_common,
+ db_dbterm_list_prepend_tree_common,
+ db_dbterm_list_remove_first_tree_common,
+ db_put_dbterm_tree,
+ db_free_dbterm_tree_common,
+ db_get_dbterm_key_tree_common,
db_get_binary_info_tree,
db_first_tree, /* raw_first same as first */
db_next_tree /* raw_next same as next */
@@ -660,6 +681,139 @@ static ERTS_INLINE int cmp_key_eq(DbTableCommon* tb, Eterm key, TreeDbTerm* obj)
return is_same(key, obj_key) || CMP(key, obj_key) == 0;
}
+/*
+ * This function differ to db_put_tree_common in that it inserts a TreeDbTerm
+ * instead of an Eterm.
+ */
+int db_put_dbterm_tree_common(DbTableCommon *tb,
+ TreeDbTerm **root,
+ TreeDbTerm *value_to_insert,
+ int key_clash_fail,
+ DbTableTree *stack_container)
+{
+ /* Non recursive insertion in AVL tree, building our own stack */
+ TreeDbTerm **tstack[STACK_NEED];
+ int tpos = 0;
+ int dstack[STACK_NEED+1];
+ int dpos = 0;
+ int state = 0;
+ TreeDbTerm **this = root;
+ Sint c;
+ Eterm key;
+ int dir;
+ TreeDbTerm *p1, *p2, *p;
+ Uint size_to_insert = db_term_size((DbTable*)tb, value_to_insert, offsetof(TreeDbTerm, dbterm));
+ ERTS_DB_ALC_MEM_UPDATE_((DbTable*)tb, 0, size_to_insert);
+ key = GETKEY(tb, value_to_insert->dbterm.tpl);
+
+ reset_static_stack(stack_container);
+
+ dstack[dpos++] = DIR_END;
+ for (;;)
+ if (!*this) { /* Found our place */
+ state = 1;
+ INC_NITEMS(((DbTable*)tb));
+ *this = value_to_insert;
+ (*this)->balance = 0;
+ (*this)->left = (*this)->right = NULL;
+ break;
+ } else if ((c = cmp_key(tb, key, *this)) < 0) {
+ /* go lefts */
+ dstack[dpos++] = DIR_LEFT;
+ tstack[tpos++] = this;
+ this = &((*this)->left);
+ } else if (c > 0) { /* go right */
+ dstack[dpos++] = DIR_RIGHT;
+ tstack[tpos++] = this;
+ this = &((*this)->right);
+ } else if (!key_clash_fail) { /* Equal key and this is a set, replace. */
+ value_to_insert->balance = (*this)->balance;
+ value_to_insert->left = (*this)->left;
+ value_to_insert->right = (*this)->right;
+ free_term((DbTable*)tb, *this);
+ *this = value_to_insert;
+ break;
+ } else {
+ return DB_ERROR_BADKEY; /* key already exists */
+ }
+
+ while (state && ( dir = dstack[--dpos] ) != DIR_END) {
+ this = tstack[--tpos];
+ p = *this;
+ if (dir == DIR_LEFT) {
+ switch (p->balance) {
+ case 1:
+ p->balance = 0;
+ state = 0;
+ break;
+ case 0:
+ p->balance = -1;
+ break;
+ case -1: /* The icky case */
+ p1 = p->left;
+ if (p1->balance == -1) { /* Single LL rotation */
+ p->left = p1->right;
+ p1->right = p;
+ p->balance = 0;
+ (*this) = p1;
+ } else { /* Double RR rotation */
+ p2 = p1->right;
+ p1->right = p2->left;
+ p2->left = p1;
+ p->left = p2->right;
+ p2->right = p;
+ p->balance = (p2->balance == -1) ? +1 : 0;
+ p1->balance = (p2->balance == 1) ? -1 : 0;
+ (*this) = p2;
+ }
+ (*this)->balance = 0;
+ state = 0;
+ break;
+ }
+ } else { /* dir == DIR_RIGHT */
+ switch (p->balance) {
+ case -1:
+ p->balance = 0;
+ state = 0;
+ break;
+ case 0:
+ p->balance = 1;
+ break;
+ case 1:
+ p1 = p->right;
+ if (p1->balance == 1) { /* Single RR rotation */
+ p->right = p1->left;
+ p1->left = p;
+ p->balance = 0;
+ (*this) = p1;
+ } else { /* Double RL rotation */
+ p2 = p1->left;
+ p1->left = p2->right;
+ p2->right = p1;
+ p->right = p2->left;
+ p2->left = p;
+ p->balance = (p2->balance == 1) ? -1 : 0;
+ p1->balance = (p2->balance == -1) ? 1 : 0;
+ (*this) = p2;
+ }
+ (*this)->balance = 0;
+ state = 0;
+ break;
+ }
+ }
+ }
+ return DB_ERROR_NONE;
+}
+
+static int db_put_dbterm_tree(DbTable* tbl, /* [in out] */
+ void* obj,
+ int key_clash_fail, /* DB_ERROR_BADKEY if key exists */
+ SWord *consumed_reds_p)
+{
+ DbTableTree *tb = &tbl->tree;
+ return db_put_dbterm_tree_common(&tb->common, &tb->root, obj, key_clash_fail, tb);
+}
+
int db_put_tree_common(DbTableCommon *tb, TreeDbTerm **root, Eterm obj,
int key_clash_fail, DbTableTree *stack_container)
{
@@ -772,7 +926,8 @@ int db_put_tree_common(DbTableCommon *tb, TreeDbTerm **root, Eterm obj,
return DB_ERROR_NONE;
}
-static int db_put_tree(DbTable *tbl, Eterm obj, int key_clash_fail)
+static int db_put_tree(DbTable *tbl, Eterm obj, int key_clash_fail,
+ SWord *consumed_reds_p)
{
DbTableTree *tb = &tbl->tree;
return db_put_tree_common(&tb->common, &tb->root, obj, key_clash_fail, tb);
@@ -1175,7 +1330,7 @@ int db_select_continue_tree_common(Process *p,
sc.accum,
tptr[7],
make_small(sc.got));
- RET_TO_BIF(bif_trap1(bif_export[BIF_ets_select_1], p, continuation),
+ RET_TO_BIF(bif_trap1(&bif_trap_export[BIF_ets_select_1], p, continuation),
DB_ERROR_NONE);
#undef RET_TO_BIF
@@ -1320,7 +1475,7 @@ int db_select_tree_common(Process *p, DbTable *tb,
make_small(sc.got));
/* Don't free mpi.mp, so don't use macro */
- *ret = bif_trap1(bif_export[BIF_ets_select_1], p, continuation);
+ *ret = bif_trap1(&bif_trap_export[BIF_ets_select_1], p, continuation);
return DB_ERROR_NONE;
#undef RET_TO_BIF
@@ -1728,7 +1883,7 @@ int db_select_chunk_tree_common(Process *p, DbTable *tb,
make_small(reverse),
make_small(sc.got));
/* Don't let RET_TO_BIF macro free mpi.mp*/
- *ret = bif_trap1(bif_export[BIF_ets_select_1], p, continuation);
+ *ret = bif_trap1(&bif_trap_export[BIF_ets_select_1], p, continuation);
return DB_ERROR_NONE;
#undef RET_TO_BIF
@@ -2307,9 +2462,12 @@ static SWord db_free_table_continue_tree(DbTable *tbl, SWord reds)
sizeof(TreeDbTerm *) * STACK_NEED);
ASSERT(erts_flxctr_is_snapshot_ongoing(&tb->common.counters) ||
((APPROX_MEM_CONSUMED(tb)
- == sizeof(DbTable)) ||
+ == (sizeof(DbTable) +
+ erts_flxctr_nr_of_allocated_bytes(&tb->common.counters))) ||
(APPROX_MEM_CONSUMED(tb)
- == (sizeof(DbTable) + sizeof(DbFixation)))));
+ == (sizeof(DbTable) +
+ sizeof(DbFixation) +
+ erts_flxctr_nr_of_allocated_bytes(&tb->common.counters)))));
}
return reds;
}
@@ -3028,8 +3186,8 @@ found_prev:
}
-/* @brief Find object with smallest key of all larger than partially bound key.
- * Can be used as a starting point for a reverse iteration with pb_key.
+/** @brief Find object with smallest key of all larger than partially bound
+ * key. Can be used as a starting point for a reverse iteration with pb_key.
*
* @param pb_key The partially bound key. Example {42, '$1'}
* @param *rootpp Will return pointer to root pointer of tree with found object.
@@ -3078,8 +3236,8 @@ static TreeDbTerm *find_next_from_pb_key(DbTable *tbl, TreeDbTerm*** rootpp,
}
}
-/* @brief Find object with largest key of all smaller than partially bound key.
- * Can be used as a starting point for a forward iteration with pb_key.
+/** @brief Find object with largest key of all smaller than partially bound
+ * key. Can be used as a starting point for a forward iteration with pb_key.
*
* @param pb_key The partially bound key. Example {42, '$1'}
* @param *rootpp Will return pointer to root pointer of found object.
@@ -3355,6 +3513,50 @@ Eterm db_binary_info_tree_common(Process* p, TreeDbTerm* this)
}
+void* db_eterm_to_dbterm_tree_common(int compress, int keypos, Eterm obj)
+{
+ TreeDbTerm* term = new_dbterm_no_tab(compress, keypos, obj);
+ term->left = NULL;
+ term->right = NULL;
+ return term;
+}
+
+void* db_dbterm_list_prepend_tree_common(void *list, void *db_term)
+{
+ TreeDbTerm* l = list;
+ TreeDbTerm* t = db_term;
+ t->left = l;
+ return t;
+}
+
+void* db_dbterm_list_remove_first_tree_common(void **list)
+{
+ if (*list == NULL) {
+ return NULL;
+ } else {
+ TreeDbTerm* t = (*list);
+ TreeDbTerm* l = t->left;
+ *list = l;
+ return t;
+ }
+}
+
+/*
+ * Frees a TreeDbTerm without updating the memory footprint of the
+ * table.
+ */
+void db_free_dbterm_tree_common(int compressed, void* obj)
+{
+ TreeDbTerm* p = obj;
+ db_free_term_no_tab(compressed, p, offsetof(TreeDbTerm, dbterm));
+}
+
+Eterm db_get_dbterm_key_tree_common(DbTable* tb, void* db_term)
+{
+ TreeDbTerm *term = db_term;
+ return GETKEY(tb, term->dbterm.tpl);
+}
+
/*
* Traverse the tree with a callback function, used by db_match_xxx
*/
diff --git a/erts/emulator/beam/erl_db_tree_util.h b/erts/emulator/beam/erl_db_tree_util.h
index 90ac8c7ba7..4285111810 100644
--- a/erts/emulator/beam/erl_db_tree_util.h
+++ b/erts/emulator/beam/erl_db_tree_util.h
@@ -171,6 +171,13 @@ void db_finalize_dbterm_tree_common(int cret,
DbUpdateHandle *handle,
TreeDbTerm **root,
DbTableTree *stack_container);
+void* db_eterm_to_dbterm_tree_common(int compress, int keypos, Eterm obj);
+void* db_dbterm_list_prepend_tree_common(void* list, void* db_term);
+void* db_dbterm_list_remove_first_tree_common(void **list);
+int db_put_dbterm_tree_common(DbTableCommon *tb, TreeDbTerm **root, TreeDbTerm *value_to_insert,
+ int key_clash_fail, DbTableTree *stack_container);
+void db_free_dbterm_tree_common(int compressed, void* obj);
+Eterm db_get_dbterm_key_tree_common(DbTable* tb, void* db_term);
Sint cmp_partly_bound(Eterm partly_bound_key, Eterm bound_key);
TreeDbTerm *db_find_tree_node_common(DbTableCommon*, TreeDbTerm *root,
diff --git a/erts/emulator/beam/erl_db_util.c b/erts/emulator/beam/erl_db_util.c
index 2a0b28f232..5435868c8c 100644
--- a/erts/emulator/beam/erl_db_util.c
+++ b/erts/emulator/beam/erl_db_util.c
@@ -2612,7 +2612,10 @@ restart:
break;
case matchCaller:
ASSERT(c_p == self);
- if (!(c_p->cp) || !(cp = find_function_from_pc(c_p->cp))) {
+ t = c_p->stop[0];
+ if (is_not_CP(t)) {
+ *esp++ = am_undefined;
+ } else if (!(cp = find_function_from_pc(cp_val(t)))) {
*esp++ = am_undefined;
} else {
ehp = HAllocX(build_proc, 4, HEAP_XTRA);
@@ -2999,6 +3002,34 @@ void db_free_term(DbTable *tb, void* basep, Uint offset)
erts_db_free(ERTS_ALC_T_DB_TERM, tb, basep, size);
}
+Uint db_term_size(DbTable *tb, void* basep, Uint offset)
+{
+ DbTerm* db = (DbTerm*) ((byte*)basep + offset);
+ if (tb->common.compress) {
+ return db_alloced_size_comp(db);
+ }
+ else {
+ return offset + offsetof(DbTerm,tpl) + db->size*sizeof(Eterm);
+ }
+}
+
+void db_free_term_no_tab(int compress, void* basep, Uint offset)
+{
+ DbTerm* db = (DbTerm*) ((byte*)basep + offset);
+ Uint size;
+ if (compress) {
+ db_cleanup_offheap_comp(db);
+ size = db_alloced_size_comp(db);
+ }
+ else {
+ ErlOffHeap tmp_oh;
+ tmp_oh.first = db->first_oh;
+ erts_cleanup_offheap(&tmp_oh);
+ size = offset + offsetof(DbTerm,tpl) + db->size*sizeof(Eterm);
+ }
+ erts_db_free(ERTS_ALC_T_DB_TERM, NULL, basep, size);
+}
+
static ERTS_INLINE Uint align_up(Uint value, Uint pow2)
{
ASSERT((pow2 & (pow2-1)) == 0);
@@ -3007,7 +3038,7 @@ static ERTS_INLINE Uint align_up(Uint value, Uint pow2)
/* Compressed size of an uncompressed term
*/
-static Uint db_size_dbterm_comp(DbTableCommon* tb, Eterm obj)
+static Uint db_size_dbterm_comp(int keypos, Eterm obj)
{
Eterm* tpl = tuple_val(obj);
int i;
@@ -3016,11 +3047,11 @@ static Uint db_size_dbterm_comp(DbTableCommon* tb, Eterm obj)
+ sizeof(Uint); /* "alloc_size" */
for (i = arityval(*tpl); i>0; i--) {
- if (i != tb->keypos && is_not_immed(tpl[i])) {
+ if (i != keypos && is_not_immed(tpl[i])) {
size += erts_encode_ext_size_ets(tpl[i]);
}
}
- size += size_object(tpl[tb->keypos]) * sizeof(Eterm);
+ size += size_object(tpl[keypos]) * sizeof(Eterm);
return align_up(size, sizeof(Uint));
}
@@ -3036,13 +3067,13 @@ static ERTS_INLINE byte* elem2ext(Eterm* tpl, Uint ix)
return (byte*)tpl + (tpl[ix] >> _TAG_PRIMARY_SIZE);
}
-static void* copy_to_comp(DbTableCommon* tb, Eterm obj, DbTerm* dest,
+static void* copy_to_comp(int keypos, Eterm obj, DbTerm* dest,
Uint alloc_size)
{
ErlOffHeap tmp_offheap;
Eterm* src = tuple_val(obj);
Eterm* tpl = dest->tpl;
- Eterm key = src[tb->keypos];
+ Eterm key = src[keypos];
int arity = arityval(src[0]);
union {
Eterm* ep;
@@ -3056,10 +3087,10 @@ static void* copy_to_comp(DbTableCommon* tb, Eterm obj, DbTerm* dest,
tpl[arity + 1] = alloc_size;
tmp_offheap.first = NULL;
- tpl[tb->keypos] = copy_struct(key, size_object(key), &top.ep, &tmp_offheap);
+ tpl[keypos] = copy_struct(key, size_object(key), &top.ep, &tmp_offheap);
dest->first_oh = tmp_offheap.first;
for (i=1; i<=arity; i++) {
- if (i != tb->keypos) {
+ if (i != keypos) {
if (is_immed(src[i])) {
tpl[i] = src[i];
}
@@ -3131,14 +3162,17 @@ void* db_store_term(DbTableCommon *tb, DbTerm* old, Uint offset, Eterm obj)
}
-void* db_store_term_comp(DbTableCommon *tb, DbTerm* old, Uint offset, Eterm obj)
+void* db_store_term_comp(DbTableCommon *tb, /* May be NULL */
+ int keypos,
+ DbTerm* old,
+ Uint offset,Eterm obj)
{
- Uint new_sz = offset + db_size_dbterm_comp(tb, obj);
+ Uint new_sz = offset + db_size_dbterm_comp(keypos, obj);
byte* basep;
DbTerm* newp;
byte* top;
- ASSERT(tb->compress);
+ ASSERT(tb == NULL || tb->compress);
if (old != 0) {
Uint old_sz = db_alloced_size_comp(old);
db_cleanup_offheap_comp(old);
@@ -3158,7 +3192,7 @@ void* db_store_term_comp(DbTableCommon *tb, DbTerm* old, Uint offset, Eterm obj)
}
newp->size = size_object(obj);
- top = copy_to_comp(tb, obj, newp, new_sz);
+ top = copy_to_comp(keypos, obj, newp, new_sz);
ASSERT(top <= basep + new_sz); (void)top;
/* ToDo: Maybe realloc if ((basep+new_sz) - top) > WASTED_SPACE_LIMIT */
@@ -3173,7 +3207,7 @@ void db_finalize_resize(DbUpdateHandle* handle, Uint offset)
DbTerm* newDbTerm;
Uint alloc_sz = offset +
(tbl->common.compress ?
- db_size_dbterm_comp(&tbl->common, make_tuple(handle->dbterm->tpl)) :
+ db_size_dbterm_comp(tbl->common.keypos, make_tuple(handle->dbterm->tpl)) :
sizeof(DbTerm)+sizeof(Eterm)*(handle->new_size-1));
byte* newp = erts_db_alloc(ERTS_ALC_T_DB_TERM, tbl, alloc_sz);
byte* oldp = *(handle->bp);
@@ -3189,7 +3223,7 @@ void db_finalize_resize(DbUpdateHandle* handle, Uint offset)
/* make a flat copy */
if (tbl->common.compress) {
- copy_to_comp(&tbl->common, make_tuple(handle->dbterm->tpl),
+ copy_to_comp(tbl->common.keypos, make_tuple(handle->dbterm->tpl),
newDbTerm, alloc_sz);
db_free_tmp_uncompressed(handle->dbterm);
}
@@ -5215,7 +5249,7 @@ static Eterm match_spec_test(Process *p, Eterm against, Eterm spec, int trace)
Eterm l;
Uint32 ret_flags;
Uint sz;
- BeamInstr *save_cp;
+ Eterm save_cp;
if (trace && !(is_list(against) || against == NIL)) {
return THE_NON_VALUE;
@@ -5259,13 +5293,13 @@ static Eterm match_spec_test(Process *p, Eterm against, Eterm spec, int trace)
++n;
l = CDR(list_val(l));
}
- save_cp = p->cp;
- p->cp = NULL;
+ save_cp = p->stop[0];
+ p->stop[0] = NIL;
res = erts_match_set_run_trace(p, p,
mps, arr, n,
ERTS_PAM_COPY_RESULT|ERTS_PAM_IGNORE_TRACE_SILENT,
&ret_flags);
- p->cp = save_cp;
+ p->stop[0] = save_cp;
} else {
n = 0;
arr = NULL;
diff --git a/erts/emulator/beam/erl_db_util.h b/erts/emulator/beam/erl_db_util.h
index 7846a5c98a..4e384606d1 100644
--- a/erts/emulator/beam/erl_db_util.h
+++ b/erts/emulator/beam/erl_db_util.h
@@ -43,18 +43,19 @@
* checks for negative returns and issues BIF_ERRORS based
* upon these values.
*/
-#define DB_ERROR_NONE 0 /* No error */
-#define DB_ERROR_BADITEM -1 /* The item was malformed ie no
- tuple or to small*/
-#define DB_ERROR_BADTABLE -2 /* The Table is inconsisitent */
-#define DB_ERROR_SYSRES -3 /* Out of system resources */
-#define DB_ERROR_BADKEY -4 /* Returned if a key that should
- exist does not. */
+#define DB_ERROR_NONE_FALSE 1 /* No error am_false reult */
+#define DB_ERROR_NONE 0 /* No error */
+#define DB_ERROR_BADITEM -1 /* The item was malformed ie no
+ tuple or to small*/
+#define DB_ERROR_BADTABLE -2 /* The Table is inconsisitent */
+#define DB_ERROR_SYSRES -3 /* Out of system resources */
+#define DB_ERROR_BADKEY -4 /* Returned if a key that should
+ exist does not. */
#define DB_ERROR_BADPARAM -5 /* Returned if a specified slot does
not exist (hash table only) or
the state parameter in db_match_object
is broken.*/
-#define DB_ERROR_UNSPEC -10 /* Unspecified error */
+#define DB_ERROR_UNSPEC -10 /* Unspecified error */
/*#define DEBUG_CLONE*/
@@ -92,7 +93,7 @@ typedef struct {
int flags;
union {
struct {
- erts_rwmtx_t* lck;
+ struct DbTableHashLockAndCounter* lck_ctr;
} hash;
struct {
struct DbTableCATreeNode* base_node;
@@ -130,7 +131,8 @@ typedef struct db_table_method
Eterm* ret);
int (*db_put)(DbTable* tb, /* [in out] */
Eterm obj,
- int key_clash_fail); /* DB_ERROR_BADKEY if key exists */
+ int key_clash_fail, /* DB_ERROR_BADKEY if key exists */
+ SWord *consumed_reds_p);
int (*db_get)(Process* p,
DbTable* tb, /* [in out] */
Eterm key,
@@ -234,14 +236,20 @@ typedef struct db_table_method
** dbterm was not updated. If the handle was of a new object and cret is
** not DB_ERROR_NONE, the object is removed from the table. */
void (*db_finalize_dbterm)(int cret, DbUpdateHandle* handle);
-
+ void* (*db_eterm_to_dbterm)(int compress, int keypos, Eterm obj);
+ void* (*db_dbterm_list_prepend)(void* list, void* db_term);
+ void* (*db_dbterm_list_remove_first)(void** list);
+ int (*db_put_dbterm)(DbTable* tb, /* [in out] */
+ void* obj,
+ int key_clash_fail, /* DB_ERROR_BADKEY if key exists */
+ SWord *consumed_reds_p);
+ void (*db_free_dbterm)(int compressed, void* obj);
+ Eterm (*db_get_dbterm_key)(DbTable* tb, void* db_term);
int (*db_get_binary_info)(Process*, DbTable* tb, Eterm key, Eterm* ret);
-
/* Raw first/next same as first/next but also return pseudo deleted keys.
Only internal use by ets:info(_,binary) */
int (*db_raw_first)(Process*, DbTable*, Eterm* ret);
int (*db_raw_next)(Process*, DbTable*, Eterm key, Eterm* ret);
-
} DbTableMethod;
typedef struct db_fixation {
@@ -312,6 +320,12 @@ typedef struct db_table_common {
int keypos; /* defaults to 1 */
int compress;
+ /* For unfinished operations that needs to be helped */
+ void (*continuation)(long *reds_ptr,
+ void** state,
+ void* extra_context); /* To help yielded process */
+ erts_atomic_t continuation_state;
+ Binary* continuation_res_bin;
#ifdef ETS_DBG_FORCE_TRAP
erts_atomic_t dbg_force_trap; /* &1 force enabled, &2 trap this call */
#endif
@@ -407,6 +421,7 @@ ERTS_GLB_INLINE int db_eq(DbTableCommon* tb, Eterm a, DbTerm* b)
#define DB_READ (DB_PROTECTED|DB_PUBLIC)
#define DB_WRITE DB_PUBLIC
#define DB_INFO (DB_PROTECTED|DB_PUBLIC|DB_PRIVATE)
+#define DB_READ_TBL_STRUCT (DB_PROTECTED|DB_PUBLIC|DB_PRIVATE|DB_BUSY)
#define ONLY_WRITER(P,T) (((T)->common.status & (DB_PRIVATE|DB_PROTECTED)) \
&& (T)->common.owner == (P)->common.id)
@@ -424,8 +439,13 @@ void db_initialize_util(void);
Eterm db_getkey(int keypos, Eterm obj);
void db_cleanup_offheap_comp(DbTerm* p);
void db_free_term(DbTable *tb, void* basep, Uint offset);
+void db_free_term_no_tab(int compress, void* basep, Uint offset);
+Uint db_term_size(DbTable *tb, void* basep, Uint offset);
void* db_store_term(DbTableCommon *tb, DbTerm* old, Uint offset, Eterm obj);
-void* db_store_term_comp(DbTableCommon *tb, DbTerm* old, Uint offset, Eterm obj);
+void* db_store_term_comp(DbTableCommon *tb, /*May be NULL*/
+ int keypos,
+ DbTerm* old,
+ Uint offset,Eterm obj);
Eterm db_copy_element_from_ets(DbTableCommon* tb, Process* p, DbTerm* obj,
Uint pos, Eterm** hpp, Uint extra);
int db_has_map(Eterm obj);
@@ -546,9 +566,9 @@ ERTS_GLB_INLINE Eterm erts_db_make_match_prog_ref(Process *p, Binary *mp, Eterm
ERTS_GLB_INLINE Binary *erts_db_get_match_prog_binary(Eterm term);
ERTS_GLB_INLINE Binary *erts_db_get_match_prog_binary_unchecked(Eterm term);
-/* @brief Ensure off-heap header is word aligned, make a temporary copy if not.
- * Needed when inspecting ETS off-heap lists that may contain unaligned
- * ProcBins if table is 'compressed'.
+/** @brief Ensure off-heap header is word aligned, make a temporary copy if
+ * not. Needed when inspecting ETS off-heap lists that may contain unaligned
+ * ProcBins if table is 'compressed'.
*/
struct erts_tmp_aligned_offheap
{
diff --git a/erts/emulator/beam/erl_flxctr.c b/erts/emulator/beam/erl_flxctr.c
index 35f4a21508..35c4de1a27 100644
--- a/erts/emulator/beam/erl_flxctr.c
+++ b/erts/emulator/beam/erl_flxctr.c
@@ -46,6 +46,29 @@ typedef enum {
ERTS_FLXCTR_SNAPSHOT_ONGOING_TP_THREAD_DO_FREE = 2
} erts_flxctr_snapshot_status;
+#define ERTS_FLXCTR_DECENTRALIZED_COUNTER_ARRAY_SIZE \
+ (sizeof(ErtsFlxCtrDecentralizedCtrArray) + \
+ (sizeof(ErtsFlxCtrDecentralizedCtrArrayElem) * \
+ ERTS_FLXCTR_DECENTRALIZED_NO_SLOTS) + \
+ ERTS_CACHE_LINE_SIZE)
+
+#ifdef DEBUG
+#define FLXCTR_MEM_DEBUG 1
+#endif
+
+#ifdef FLXCTR_MEM_DEBUG
+static erts_atomic_t debug_mem_usage;
+#endif
+
+#ifdef FLXCTR_MEM_DEBUG
+#define FLXCTR_FREE(ALLOC_TYPE, ADDRESS) do { \
+ erts_free(ALLOC_TYPE, ADDRESS); \
+ erts_atomic_add_mb(&debug_mem_usage, -ERTS_FLXCTR_DECENTRALIZED_COUNTER_ARRAY_SIZE); \
+ } while(0)
+#else
+#define FLXCTR_FREE(ALLOC_TYPE, ADDRESS) erts_free(ALLOC_TYPE, ADDRESS)
+#endif
+
static void
thr_prg_wake_up_and_count(void* bin_p)
{
@@ -72,13 +95,13 @@ thr_prg_wake_up_and_count(void* bin_p)
}
/* Announce that the snapshot is done */
{
- Sint expected = ERTS_FLXCTR_SNAPSHOT_ONGOING;
- if (expected != erts_atomic_cmpxchg_mb(&next->snapshot_status,
- ERTS_FLXCTR_SNAPSHOT_NOT_ONGOING,
- expected)) {
- /* The CAS failed which means that this thread need to free the next array. */
- erts_free(info->alloc_type, next->block_start);
- }
+ Sint expected = ERTS_FLXCTR_SNAPSHOT_ONGOING;
+ if (expected != erts_atomic_cmpxchg_mb(&next->snapshot_status,
+ ERTS_FLXCTR_SNAPSHOT_NOT_ONGOING,
+ expected)) {
+ /* The CAS failed which means that this thread need to free the next array. */
+ FLXCTR_FREE(info->alloc_type, next->block_start);
+ }
}
/* Resume the process that requested the snapshot */
erts_proc_lock(p, ERTS_PROC_LOCK_STATUS);
@@ -86,7 +109,7 @@ thr_prg_wake_up_and_count(void* bin_p)
erts_resume(p, ERTS_PROC_LOCK_STATUS);
}
/* Free the memory that is no longer needed */
- erts_free(info->alloc_type, array->block_start);
+ FLXCTR_FREE(info->alloc_type, array->block_start);
erts_proc_unlock(p, ERTS_PROC_LOCK_STATUS);
erts_proc_dec_refc(p);
erts_bin_release(bin);
@@ -141,6 +164,14 @@ static void suspend_until_thr_prg(Process* p)
erts_schedule_thr_prgr_later_op(thr_prg_wake_up_later, state_bin, &info->later_op);
}
+size_t erts_flxctr_nr_of_allocated_bytes(ErtsFlxCtr* c)
+{
+ if (c->is_decentralized) {
+ return ERTS_FLXCTR_DECENTRALIZED_COUNTER_ARRAY_SIZE;
+ } else {
+ return 0;
+ }
+}
static ErtsFlxCtrDecentralizedCtrArray*
create_decentralized_ctr_array(ErtsAlcType_t alloc_type, Uint nr_of_counters) {
@@ -148,14 +179,14 @@ create_decentralized_ctr_array(ErtsAlcType_t alloc_type, Uint nr_of_counters) {
the array field is located at the start of a cache line */
char* bytes =
erts_alloc(alloc_type,
- sizeof(ErtsFlxCtrDecentralizedCtrArray) +
- (sizeof(ErtsFlxCtrDecentralizedCtrArrayElem) *
- ERTS_FLXCTR_DECENTRALIZED_NO_SLOTS) +
- ERTS_CACHE_LINE_SIZE);
+ ERTS_FLXCTR_DECENTRALIZED_COUNTER_ARRAY_SIZE);
void* block_start = bytes;
int bytes_to_next_cacheline_border;
ErtsFlxCtrDecentralizedCtrArray* array;
int i, sched;
+#ifdef FLXCTR_MEM_DEBUG
+ erts_atomic_add_mb(&debug_mem_usage, ERTS_FLXCTR_DECENTRALIZED_COUNTER_ARRAY_SIZE);
+#endif
bytes = &bytes[offsetof(ErtsFlxCtrDecentralizedCtrArray, array)];
bytes_to_next_cacheline_border =
ERTS_CACHE_LINE_SIZE - (((Uint)bytes) % ERTS_CACHE_LINE_SIZE);
@@ -178,6 +209,9 @@ create_decentralized_ctr_array(ErtsAlcType_t alloc_type, Uint nr_of_counters) {
void erts_flxctr_setup(int decentralized_counter_groups)
{
reader_groups_array_size = decentralized_counter_groups+1;
+#ifdef FLXCTR_MEM_DEBUG
+ erts_atomic_init_mb(&debug_mem_usage, 0);
+#endif
}
void erts_flxctr_init(ErtsFlxCtr* c,
@@ -203,7 +237,7 @@ void erts_flxctr_init(ErtsFlxCtr* c,
}
}
-void erts_flxctr_destroy(ErtsFlxCtr* c, ErtsAlcType_t type)
+void erts_flxctr_destroy(ErtsFlxCtr* c, ErtsAlcType_t alloc_type)
{
if (c->is_decentralized) {
if (erts_flxctr_is_snapshot_ongoing(c)) {
@@ -220,10 +254,10 @@ void erts_flxctr_destroy(ErtsFlxCtr* c, ErtsAlcType_t type)
snapshot is ongoing anymore and the freeing needs
to be done here */
ERTS_ASSERT(!erts_flxctr_is_snapshot_ongoing(c));
- erts_free(type, array->block_start);
+ FLXCTR_FREE(alloc_type, array->block_start);
}
} else {
- erts_free(type, ERTS_FLXCTR_GET_CTR_ARRAY_PTR(c)->block_start);
+ FLXCTR_FREE(alloc_type, ERTS_FLXCTR_GET_CTR_ARRAY_PTR(c)->block_start);
}
}
}
@@ -257,7 +291,7 @@ erts_flxctr_snapshot(ErtsFlxCtr* c,
ErtsFlxCtrSnapshotResult res =
{.type = ERTS_FLXCTR_TRY_AGAIN_AFTER_TRAP};
suspend_until_thr_prg(p);
- erts_free(alloc_type, new_array->block_start);
+ FLXCTR_FREE(alloc_type, new_array->block_start);
return res;
}
/* Create binary with info about the operation that can be
@@ -364,7 +398,19 @@ void erts_flxctr_reset(ErtsFlxCtr* c,
}
-void erts_flxctr_set_slot(int group) {
+void erts_flxctr_set_slot(int group)
+{
ErtsSchedulerData *esdp = erts_get_scheduler_data();
esdp->flxctr_slot_no = group;
}
+
+Sint erts_flxctr_debug_memory_usage(void)
+{
+#ifdef FLXCTR_MEM_DEBUG
+ return erts_atomic_read_mb(&debug_mem_usage);
+#else
+ return -1;
+#endif
+}
+
+
diff --git a/erts/emulator/beam/erl_flxctr.h b/erts/emulator/beam/erl_flxctr.h
index 5cab02b9eb..df60f3651e 100644
--- a/erts/emulator/beam/erl_flxctr.h
+++ b/erts/emulator/beam/erl_flxctr.h
@@ -288,6 +288,24 @@ int erts_flxctr_is_snapshot_ongoing(ErtsFlxCtr* c);
*/
int erts_flxctr_suspend_until_thr_prg_if_snapshot_ongoing(ErtsFlxCtr* c, Process* p);
+/**
+ * @brief This function returns the number of bytes that are allocated
+ * for for the given FlxCtr.
+ *
+ * @return nr of bytes allocated for the FlxCtr
+ */
+size_t erts_flxctr_nr_of_allocated_bytes(ErtsFlxCtr* c);
+
+/**
+ * @brief This debug function returns the amount of memory allocated
+ * for decentralized counter arrays when compiled with the DEBUG
+ * macro. The function returns -1 if the DEBUG macro is undefined.
+ *
+ * @return number of bytes allocated for decentralized counter arrays
+ * if in debug mode and otherwise -1
+ */
+Sint erts_flxctr_debug_memory_usage(void);
+
/* End: Public Interface */
/* Internal Declarations */
diff --git a/erts/emulator/beam/erl_fun.c b/erts/emulator/beam/erl_fun.c
index 9c866250bb..79a1fdb8b9 100644
--- a/erts/emulator/beam/erl_fun.c
+++ b/erts/emulator/beam/erl_fun.c
@@ -100,27 +100,6 @@ int erts_fun_table_sz(void)
}
ErlFunEntry*
-erts_put_fun_entry(Eterm mod, int uniq, int index)
-{
- ErlFunEntry template;
- ErlFunEntry* fe;
- erts_aint_t refc;
- ASSERT(is_atom(mod));
- template.old_uniq = uniq;
- template.old_index = index;
- template.module = mod;
- erts_fun_write_lock();
- fe = (ErlFunEntry *) hash_put(&erts_fun_table, (void*) &template);
- sys_memset(fe->uniq, 0, sizeof(fe->uniq));
- fe->index = 0;
- refc = erts_refc_inctest(&fe->refc, 0);
- if (refc < 2) /* New or pending delete */
- erts_refc_inc(&fe->refc, 1);
- erts_fun_write_unlock();
- return fe;
-}
-
-ErlFunEntry*
erts_put_fun_entry2(Eterm mod, int old_uniq, int old_index,
byte* uniq, int index, int arity)
{
@@ -130,12 +109,12 @@ erts_put_fun_entry2(Eterm mod, int old_uniq, int old_index,
ASSERT(is_atom(mod));
template.old_uniq = old_uniq;
- template.old_index = old_index;
+ template.index = index;
template.module = mod;
erts_fun_write_lock();
fe = (ErlFunEntry *) hash_put(&erts_fun_table, (void*) &template);
sys_memcpy(fe->uniq, uniq, sizeof(fe->uniq));
- fe->index = index;
+ fe->old_index = old_index;
fe->arity = arity;
refc = erts_refc_inctest(&fe->refc, 0);
if (refc < 2) /* New or pending delete */
@@ -144,13 +123,6 @@ erts_put_fun_entry2(Eterm mod, int old_uniq, int old_index,
return fe;
}
-struct my_key {
- Eterm mod;
- byte* uniq;
- int index;
- ErlFunEntry* fe;
-};
-
ErlFunEntry*
erts_get_fun_entry(Eterm mod, int uniq, int index)
{
@@ -159,7 +131,7 @@ erts_get_fun_entry(Eterm mod, int uniq, int index)
ASSERT(is_atom(mod));
template.old_uniq = uniq;
- template.old_index = index;
+ template.index = index;
template.module = mod;
erts_fun_read_lock();
ret = (ErlFunEntry *) hash_get(&erts_fun_table, (void*) &template);
@@ -199,36 +171,33 @@ erts_erase_fun_entry(ErlFunEntry* fe)
erts_fun_write_unlock();
}
+struct fun_purge_foreach_args {
+ BeamInstr *start;
+ BeamInstr *end;
+};
+
+static void fun_purge_foreach(ErlFunEntry *fe, struct fun_purge_foreach_args *arg)
+{
+ BeamInstr* addr = fe->address;
+ if (arg->start <= addr && addr < arg->end) {
+ fe->pend_purge_address = addr;
+ ERTS_THR_WRITE_MEMORY_BARRIER;
+ fe->address = unloaded_fun;
+#ifdef HIPE
+ fe->pend_purge_native_address = fe->native_address;
+ hipe_set_closure_stub(fe);
+#endif
+ erts_purge_state_add_fun(fe);
+ }
+}
+
void
erts_fun_purge_prepare(BeamInstr* start, BeamInstr* end)
{
- int limit;
- HashBucket** bucket;
- int i;
+ struct fun_purge_foreach_args args = {start, end};
erts_fun_read_lock();
- limit = erts_fun_table.size;
- bucket = erts_fun_table.bucket;
- for (i = 0; i < limit; i++) {
- HashBucket* b = bucket[i];
-
- while (b) {
- ErlFunEntry* fe = (ErlFunEntry *) b;
- BeamInstr* addr = fe->address;
-
- if (start <= addr && addr < end) {
- fe->pend_purge_address = addr;
- ERTS_THR_WRITE_MEMORY_BARRIER;
- fe->address = unloaded_fun;
-#ifdef HIPE
- fe->pend_purge_native_address = fe->native_address;
- hipe_set_closure_stub(fe);
-#endif
- erts_purge_state_add_fun(fe);
- }
- b = b->next;
- }
- }
+ hash_foreach(&erts_fun_table, (HFOREACH_FUN)fun_purge_foreach, &args);
erts_fun_read_unlock();
}
@@ -278,36 +247,34 @@ erts_fun_purge_complete(ErlFunEntry **funs, Uint no)
ERTS_THR_WRITE_MEMORY_BARRIER;
}
+struct dump_fun_foreach_args {
+ fmtfn_t to;
+ void *to_arg;
+};
+
+static void
+dump_fun_foreach(ErlFunEntry *fe, struct dump_fun_foreach_args *args)
+{
+ erts_print(args->to, args->to_arg, "=fun\n");
+ erts_print(args->to, args->to_arg, "Module: %T\n", fe->module);
+ erts_print(args->to, args->to_arg, "Uniq: %d\n", fe->old_uniq);
+ erts_print(args->to, args->to_arg, "Index: %d\n",fe->old_index);
+ erts_print(args->to, args->to_arg, "Address: %p\n", fe->address);
+#ifdef HIPE
+ erts_print(args->to, args->to_arg, "Native_address: %p\n", fe->native_address);
+#endif
+ erts_print(args->to, args->to_arg, "Refc: %ld\n", erts_refc_read(&fe->refc, 1));
+}
+
void
erts_dump_fun_entries(fmtfn_t to, void *to_arg)
{
- int limit;
- HashBucket** bucket;
- int i;
+ struct dump_fun_foreach_args args = {to, to_arg};
int lock = !ERTS_IS_CRASH_DUMPING;
-
if (lock)
erts_fun_read_lock();
- limit = erts_fun_table.size;
- bucket = erts_fun_table.bucket;
- for (i = 0; i < limit; i++) {
- HashBucket* b = bucket[i];
-
- while (b) {
- ErlFunEntry* fe = (ErlFunEntry *) b;
- erts_print(to, to_arg, "=fun\n");
- erts_print(to, to_arg, "Module: %T\n", fe->module);
- erts_print(to, to_arg, "Uniq: %d\n", fe->old_uniq);
- erts_print(to, to_arg, "Index: %d\n",fe->old_index);
- erts_print(to, to_arg, "Address: %p\n", fe->address);
-#ifdef HIPE
- erts_print(to, to_arg, "Native_address: %p\n", fe->native_address);
-#endif
- erts_print(to, to_arg, "Refc: %ld\n", erts_refc_read(&fe->refc, 1));
- b = b->next;
- }
- }
+ hash_foreach(&erts_fun_table, (HFOREACH_FUN)dump_fun_foreach, &args);
if (lock)
erts_fun_read_unlock();
}
@@ -315,15 +282,27 @@ erts_dump_fun_entries(fmtfn_t to, void *to_arg)
static HashValue
fun_hash(ErlFunEntry* obj)
{
- return (HashValue) (obj->old_uniq ^ obj->old_index ^ atom_val(obj->module));
+ return (HashValue) (obj->old_uniq ^ obj->index ^ atom_val(obj->module));
}
static int
fun_cmp(ErlFunEntry* obj1, ErlFunEntry* obj2)
{
- return !(obj1->module == obj2->module &&
+ /*
+ * OTP 23: Use 'index' (instead of 'old_index') when comparing fun
+ * entries. In OTP 23, multiple make_fun2 instructions may refer to the
+ * the same 'index' (for the wrapper function generated for the
+ * 'fun F/A' syntax).
+ *
+ * This is safe when loading code compiled with OTP R15 and later,
+ * because since R15 (2011), the 'index' has been reliably equal
+ * to 'old_index'. The loader refuses to load modules compiled before
+ * OTP R15.
+ */
+
+ return !(obj1->module == obj2->module &&
obj1->old_uniq == obj2->old_uniq &&
- obj1->old_index == obj2->old_index);
+ obj1->index == obj2->index);
}
static ErlFunEntry*
@@ -333,7 +312,7 @@ fun_alloc(ErlFunEntry* template)
sizeof(ErlFunEntry));
obj->old_uniq = template->old_uniq;
- obj->old_index = template->old_index;
+ obj->index = template->index;
obj->module = template->module;
erts_refc_init(&obj->refc, -1);
obj->address = unloaded_fun;
diff --git a/erts/emulator/beam/erl_fun.h b/erts/emulator/beam/erl_fun.h
index fb2901d866..eefc7a95bb 100644
--- a/erts/emulator/beam/erl_fun.h
+++ b/erts/emulator/beam/erl_fun.h
@@ -74,7 +74,6 @@ void erts_init_fun_table(void);
void erts_fun_info(fmtfn_t, void *);
int erts_fun_table_sz(void);
-ErlFunEntry* erts_put_fun_entry(Eterm mod, int uniq, int index);
ErlFunEntry* erts_get_fun_entry(Eterm mod, int uniq, int index);
ErlFunEntry* erts_put_fun_entry2(Eterm mod, int old_uniq, int old_index,
diff --git a/erts/emulator/beam/erl_gc.c b/erts/emulator/beam/erl_gc.c
index f4d6ca8eb7..7f51718d1e 100644
--- a/erts/emulator/beam/erl_gc.c
+++ b/erts/emulator/beam/erl_gc.c
@@ -65,6 +65,8 @@
# define HARDDEBUG 1
#endif
+extern BeamInstr beam_apply[2];
+
/*
* Returns number of elements in an array.
*/
@@ -937,13 +939,15 @@ garbage_collect_hibernate(Process* p, int check_long_gc)
*/
erts_atomic32_read_bor_nob(&p->state, ERTS_PSFLG_GC);
ErtsGcQuickSanityCheck(p);
- ASSERT(p->stop == p->hend); /* Stack must be empty. */
+ ASSERT(p->stop == p->hend - 1); /* Only allow one continuation pointer. */
+ ASSERT(p->stop[0] == make_cp(beam_apply+1));
/*
* Do it.
*/
heap_size = p->heap_sz + (p->old_htop - p->old_heap) + p->mbuf_sz;
+ heap_size += 1; /* Reserve place for continuation pointer */
heap = (Eterm*) ERTS_HEAP_ALLOC(ERTS_ALC_T_TMP_HEAP,
sizeof(Eterm)*heap_size);
@@ -969,13 +973,11 @@ garbage_collect_hibernate(Process* p, int check_long_gc)
p->high_water = htop;
p->htop = htop;
p->hend = p->heap + heap_size;
- p->stop = p->hend;
+ p->stop = p->hend - 1;
p->heap_sz = heap_size;
heap_size = actual_size = p->htop - p->heap;
- if (heap_size == 0) {
- heap_size = 1; /* We want a heap... */
- }
+ heap_size += 1; /* Reserve place for continuation pointer */
FLAGS(p) &= ~F_FORCE_GC;
p->live_hf_end = ERTS_INVALID_HFRAG_PTR;
@@ -991,14 +993,15 @@ garbage_collect_hibernate(Process* p, int check_long_gc)
* hibernated.
*/
- ASSERT(p->hend - p->stop == 0); /* Empty stack */
ASSERT(actual_size < p->heap_sz);
heap = ERTS_HEAP_ALLOC(ERTS_ALC_T_HEAP, sizeof(Eterm)*heap_size);
sys_memcpy((void *) heap, (void *) p->heap, actual_size*sizeof(Eterm));
ERTS_HEAP_FREE(ERTS_ALC_T_TMP_HEAP, p->heap, p->heap_sz*sizeof(Eterm));
- p->stop = p->hend = heap + heap_size;
+ p->hend = heap + heap_size;
+ p->stop = p->hend - 1;
+ p->stop[0] = make_cp(beam_apply+1);
offs = heap - p->heap;
area = (char *) p->heap;
@@ -2572,13 +2575,17 @@ setup_rootset(Process *p, Eterm *objv, int nobj, Rootset *rootset)
/*
* The process may be garbage-collected while it is terminating.
- * (fvalue contains the EXIT reason and ftrace the saved stack trace.)
+ * fvalue contains the EXIT reason.
*/
if (is_not_immed(p->fvalue)) {
roots[n].v = &p->fvalue;
roots[n].sz = 1;
n++;
}
+
+ /*
+ * The raise/3 BIF will store the stacktrace in ftrace.
+ */
if (is_not_immed(p->ftrace)) {
roots[n].v = &p->ftrace;
roots[n].sz = 1;
@@ -2588,7 +2595,7 @@ setup_rootset(Process *p, Eterm *objv, int nobj, Rootset *rootset)
/*
* If a NIF or BIF has saved arguments, they need to be added
*/
- if (erts_setup_nif_export_rootset(p, &roots[n].v, &roots[n].sz))
+ if (erts_setup_nfunc_rootset(p, &roots[n].v, &roots[n].sz))
n++;
ASSERT(n <= rootset->size);
@@ -3236,7 +3243,7 @@ offset_one_rootset(Process *p, Sint offs, char* area, Uint area_size,
offset_heap_ptr(objv, nobj, offs, area, area_size);
}
offset_off_heap(p, offs, area, area_size);
- if (erts_setup_nif_export_rootset(p, &v, &sz))
+ if (erts_setup_nfunc_rootset(p, &v, &sz))
offset_heap_ptr(v, sz, offs, area, area_size);
}
diff --git a/erts/emulator/beam/erl_init.c b/erts/emulator/beam/erl_init.c
index 5f0352f5c0..d282eeb12c 100644
--- a/erts/emulator/beam/erl_init.c
+++ b/erts/emulator/beam/erl_init.c
@@ -429,6 +429,7 @@ erl_first_process_otp(char* mod_name, int argc, char** argv)
args = CONS(hp, boot_mod, args);
so.flags = erts_default_spo_flags;
+ so.opts = NIL;
res = erl_spawn_system_process(&parent, am_erl_init, am_start, args, &so);
ASSERT(is_internal_pid(res));
@@ -462,6 +463,7 @@ erl_system_process_otp(Eterm parent_pid, char* modname, int off_heap_msgq, int p
so.priority = prio;
so.max_gen_gcs = (Uint16) erts_atomic32_read_nob(&erts_max_gen_gcs);
so.scheduler = 0;
+ so.opts = NIL;
res = erl_spawn_system_process(parent, mod, am_start, NIL, &so);
ASSERT(is_internal_pid(res));
@@ -484,6 +486,7 @@ Eterm erts_internal_spawn_system_process_3(BIF_ALIST_3) {
ASSERT(erts_list_length(args) >= 0);
so.flags = erts_default_spo_flags;
+ so.opts = NIL;
res = erl_spawn_system_process(BIF_P, mod, func, args, &so);
if (is_non_value(res)) {
@@ -583,7 +586,7 @@ void erts_usage(void)
ERTS_ASYNC_THREAD_MIN_STACK_SIZE,
ERTS_ASYNC_THREAD_MAX_STACK_SIZE);
erts_fprintf(stderr, "-A number set number of threads in async thread pool,\n");
- erts_fprintf(stderr, " valid range is [0-%d]\n",
+ erts_fprintf(stderr, " valid range is [1-%d]\n",
ERTS_MAX_NO_OF_ASYNC_THREADS);
erts_fprintf(stderr, "-B[c|d|i] c to have Ctrl-c interrupt the Erlang shell,\n");
erts_fprintf(stderr, " d (or no extra option) to disable the break\n");
@@ -774,6 +777,7 @@ early_init(int *argc, char **argv) /*
int ncpu;
int ncpuonln;
int ncpuavail;
+ int ncpuquota;
int schdlrs;
int schdlrs_onln;
int schdlrs_percentage = 100;
@@ -811,7 +815,8 @@ early_init(int *argc, char **argv) /*
&max_reader_groups,
&ncpu,
&ncpuonln,
- &ncpuavail);
+ &ncpuavail,
+ &ncpuquota);
ignore_break = 0;
replace_intr = 0;
@@ -838,9 +843,18 @@ early_init(int *argc, char **argv) /*
* can initialize the allocators.
*/
no_schedulers = (Uint) (ncpu > 0 ? ncpu : 1);
- no_schedulers_online = (ncpuavail > 0
- ? ncpuavail
- : (ncpuonln > 0 ? ncpuonln : no_schedulers));
+
+ if (ncpuavail > 0) {
+ if (ncpuquota > 0) {
+ no_schedulers_online = MIN(ncpuquota, ncpuavail);
+ } else {
+ no_schedulers_online = ncpuavail;
+ }
+ } else if (ncpuonln > 0) {
+ no_schedulers_online = ncpuonln;
+ } else {
+ no_schedulers_online = no_schedulers;
+ }
schdlrs = no_schedulers;
schdlrs_onln = no_schedulers_online;
@@ -1129,6 +1143,9 @@ early_init(int *argc, char **argv) /*
i++;
}
+ if (erts_async_max_threads < 1)
+ erts_async_max_threads = 1;
+
/* apply any scheduler percentages */
if (schdlrs_percentage != 100 || schdlrs_onln_percentage != 100) {
schdlrs = schdlrs * schdlrs_percentage / 100;
diff --git a/erts/emulator/beam/erl_io_queue.c b/erts/emulator/beam/erl_io_queue.c
index 2ae5b56b5c..c82d67f893 100644
--- a/erts/emulator/beam/erl_io_queue.c
+++ b/erts/emulator/beam/erl_io_queue.c
@@ -1078,7 +1078,7 @@ static BIF_RETTYPE iol2v_yield(iol2v_state_t *state) {
state = boxed_state;
}
- ERTS_BIF_YIELD1(bif_export[BIF_iolist_to_iovec_1],
+ ERTS_BIF_YIELD1(&bif_trap_export[BIF_iolist_to_iovec_1],
state->process, state->magic_reference);
}
diff --git a/erts/emulator/beam/erl_lock_check.c b/erts/emulator/beam/erl_lock_check.c
index b391c05643..507db504db 100644
--- a/erts/emulator/beam/erl_lock_check.c
+++ b/erts/emulator/beam/erl_lock_check.c
@@ -84,6 +84,7 @@ static erts_lc_lock_order_t erts_lock_order[] = {
{ "reg_tab", NULL },
{ "proc_main", "pid" },
{ "old_code", "address" },
+ { "nif_call_tab", NULL },
#ifdef HIPE
{ "hipe_mfait_lock", NULL },
#endif
@@ -148,6 +149,7 @@ static erts_lc_lock_order_t erts_lock_order[] = {
{ "instr_x", NULL },
{ "instr", NULL },
{ "dyn_lock_check", NULL },
+ { "nif_load", NULL },
{ "alcu_allocator", "index" },
{ "mseg", NULL },
{ "get_time", NULL },
diff --git a/erts/emulator/beam/erl_lock_count.h b/erts/emulator/beam/erl_lock_count.h
index 0d47b16e0b..c061e7894d 100644
--- a/erts/emulator/beam/erl_lock_count.h
+++ b/erts/emulator/beam/erl_lock_count.h
@@ -78,7 +78,7 @@ typedef struct {
} erts_lcnt_time_t;
typedef struct {
- /* @brief log2 array of nano seconds occurences */
+ /** @brief log2 array of nano seconds occurences */
Uint32 ns[ERTS_LCNT_HISTOGRAM_SLOT_SIZE];
} erts_lcnt_hist_t;
@@ -271,7 +271,7 @@ int erts_lcnt_check_ref_installed(erts_lcnt_ref_t *ref);
erts_lcnt_lock_info_carrier_t *erts_lcnt_create_lock_info_carrier(int count);
-/* @brief Initializes the lock info at the given index.
+/** @brief Initializes the lock info at the given index.
* @param id An immediate erlang term with whatever extra data you want to
* identify this lock with.
* @param flags The flags the lock itself was initialized with. Keep in mind
@@ -300,9 +300,10 @@ void erts_lcnt_pre_thr_init(void);
void erts_lcnt_post_thr_init(void);
void erts_lcnt_late_init(void);
-/* @brief Called after everything in the system has been initialized, including
- * the schedulers. This is mainly a backwards compatibility shim for matching
- * the old lcnt behavior where all lock counting was enabled by default. */
+/** @brief Called after everything in the system has been initialized,
+ * including the schedulers. This is mainly a backwards compatibility shim for
+ * matching the old lcnt behavior where all lock counting was enabled by
+ * default. */
void erts_lcnt_post_startup(void);
void erts_lcnt_thread_setup(void);
diff --git a/erts/emulator/beam/erl_lock_flags.h b/erts/emulator/beam/erl_lock_flags.h
index 2db133b598..9d2216eaf6 100644
--- a/erts/emulator/beam/erl_lock_flags.h
+++ b/erts/emulator/beam/erl_lock_flags.h
@@ -71,10 +71,10 @@
typedef unsigned short erts_lock_flags_t;
typedef unsigned short erts_lock_options_t;
-/* @brief Gets the type name of the lock, honoring the RW flag if supplied. */
+/** @brief Gets the type name of the lock, honoring the RW flag if supplied. */
const char *erts_lock_flags_get_type_name(erts_lock_flags_t flags);
-/* @brief Gets a short-form description of the given lock options. (rw/r/w) */
+/** @brief Gets a short-form description of the given lock options. (rw/r/w) */
const char *erts_lock_options_get_short_desc(erts_lock_options_t options);
#endif /* ERTS_LOCK_FLAGS_H__ */
diff --git a/erts/emulator/beam/erl_map.c b/erts/emulator/beam/erl_map.c
index 62dd85e425..9097a36e62 100644
--- a/erts/emulator/beam/erl_map.c
+++ b/erts/emulator/beam/erl_map.c
@@ -973,7 +973,13 @@ BIF_RETTYPE maps_keys_1(BIF_ALIST_1) {
HIPE_WRAPPER_BIF_DISABLE_GC(maps_merge, 2)
BIF_RETTYPE maps_merge_2(BIF_ALIST_2) {
- if (is_flatmap(BIF_ARG_1)) {
+ if (BIF_ARG_1 == BIF_ARG_2) {
+ /* Merging upon itself always returns itself */
+ if (is_map(BIF_ARG_1)) {
+ return BIF_ARG_1;
+ }
+ BIF_P->fvalue = BIF_ARG_1;
+ } else if (is_flatmap(BIF_ARG_1)) {
if (is_flatmap(BIF_ARG_2)) {
BIF_RET(flatmap_merge(BIF_P, BIF_ARG_1, BIF_ARG_2));
} else if (is_hashmap(BIF_ARG_2)) {
@@ -1008,6 +1014,9 @@ static Eterm flatmap_merge(Process *p, Eterm nodeA, Eterm nodeB) {
n1 = flatmap_get_size(mp1);
n2 = flatmap_get_size(mp2);
+ if (n1 == 0) return nodeB;
+ if (n2 == 0) return nodeA;
+
need = MAP_HEADER_FLATMAP_SZ + 1 + 2 * (n1 + n2);
hp = HAlloc(p, need);
@@ -1127,6 +1136,7 @@ static Eterm map_merge_mixed(Process *p, Eterm flat, Eterm tree, int swap_args)
mp = (flatmap_t*)flatmap_val(flat);
n = flatmap_get_size(mp);
+ if (n == 0) return tree;
ks = flatmap_get_keys(mp);
vs = flatmap_get_values(mp);
diff --git a/erts/emulator/beam/erl_message.c b/erts/emulator/beam/erl_message.c
index 7901aa668c..3ac2530068 100644
--- a/erts/emulator/beam/erl_message.c
+++ b/erts/emulator/beam/erl_message.c
@@ -456,6 +456,24 @@ erts_queue_message(Process* receiver, ErtsProcLocks receiver_locks,
}
/**
+ *
+ * @brief Send one message from *NOT* a local process.
+ *
+ * But with a token!
+ */
+void
+erts_queue_message_token(Process* receiver, ErtsProcLocks receiver_locks,
+ ErtsMessage* mp, Eterm msg, Eterm from, Eterm token)
+{
+ ASSERT(is_not_internal_pid(from));
+ ERL_MESSAGE_TERM(mp) = msg;
+ ERL_MESSAGE_FROM(mp) = from;
+ ERL_MESSAGE_TOKEN(mp) = token;
+ queue_messages(receiver, receiver_locks, mp, &mp->next, 1);
+}
+
+
+/**
* @brief Send one message from a local process.
*
* It is up to the caller of this function to set the
@@ -674,7 +692,7 @@ erts_send_message(Process* sender,
* Make sure we don't use the heap between those instances.
*/
if (have_seqtrace(stoken)) {
- seq_trace_update_send(sender);
+ seq_trace_update_serial(sender);
seq_trace_output(stoken, message, SEQ_TRACE_SEND,
receiver->common.id, sender);
diff --git a/erts/emulator/beam/erl_message.h b/erts/emulator/beam/erl_message.h
index 38a2d567d3..6d551a5108 100644
--- a/erts/emulator/beam/erl_message.h
+++ b/erts/emulator/beam/erl_message.h
@@ -447,6 +447,7 @@ void free_message_buffer(ErlHeapFragment *);
void erts_queue_dist_message(Process*, ErtsProcLocks, ErtsDistExternal *,
ErlHeapFragment *, Eterm, Eterm);
void erts_queue_message(Process*, ErtsProcLocks,ErtsMessage*, Eterm, Eterm);
+void erts_queue_message_token(Process*, ErtsProcLocks,ErtsMessage*, Eterm, Eterm, Eterm);
void erts_queue_proc_message(Process* from,Process* to, ErtsProcLocks,ErtsMessage*, Eterm);
void erts_queue_proc_messages(Process* from, Process* to, ErtsProcLocks,
ErtsMessage*, ErtsMessage**, Uint);
diff --git a/erts/emulator/beam/erl_monitor_link.c b/erts/emulator/beam/erl_monitor_link.c
index 43028be39d..7b3010bab2 100644
--- a/erts/emulator/beam/erl_monitor_link.c
+++ b/erts/emulator/beam/erl_monitor_link.c
@@ -540,6 +540,7 @@ erts_mon_link_dist_create(Eterm nodename)
mld->links = NULL;
mld->monitors = NULL;
mld->orig_name_monitors = NULL;
+ mld->dist_pend_spawn_exit = NULL;
return mld;
}
@@ -551,6 +552,7 @@ erts_mon_link_dist_destroy__(ErtsMonLnkDist *mld)
ERTS_ML_ASSERT(!mld->links);
ERTS_ML_ASSERT(!mld->monitors);
ERTS_ML_ASSERT(!mld->orig_name_monitors);
+ ERTS_ML_ASSERT(!mld->dist_pend_spawn_exit);
erts_mtx_destroy(&mld->mtx);
erts_free(ERTS_ALC_T_ML_DIST, mld);
@@ -834,10 +836,29 @@ erts_monitor_create(Uint16 type, Eterm ref, Eterm orgn, Eterm trgt, Eterm name)
Uint rsz, osz, tsz;
Eterm *hp;
ErlOffHeap oh;
- Uint16 name_flag = is_nil(name) ? ((Uint16) 0) : ERTS_ML_FLG_NAME;
+ Uint16 name_flag;
+ Uint16 pending_flag;
rsz = is_immed(ref) ? 0 : size_object(ref);
- tsz = is_immed(trgt) ? 0 : size_object(trgt);
+ if (trgt != am_pending) {
+ if (is_not_immed(trgt))
+ tsz = size_object(trgt);
+ else
+ tsz = 0;
+ pending_flag = (Uint16) 0;
+ name_flag = is_nil(name) ? ((Uint16) 0) : ERTS_ML_FLG_NAME;
+ }
+ else {
+ /* Pending spawn_request() */
+ pending_flag = ERTS_ML_FLG_SPAWN_PENDING;
+ /* Prepare for storage of exteral pid */
+ tsz = EXTERNAL_THING_HEAD_SIZE + 1;
+ /* name contains tag */
+
+ /* Not by name */
+ name_flag = (Uint16) 0;
+
+ }
if (type == ERTS_MON_TYPE_RESOURCE)
osz = 0;
else
@@ -851,6 +872,16 @@ erts_monitor_create(Uint16 type, Eterm ref, Eterm orgn, Eterm trgt, Eterm name)
hp = &mdep->heap[0];
+ if (pending_flag) {
+ /* Make room for the future pid... */
+#ifdef DEBUG
+ int i;
+ for (i = 0; i < EXTERNAL_THING_HEAD_SIZE + 1; i++)
+ hp[i] = THE_NON_VALUE;
+#endif
+ hp += EXTERNAL_THING_HEAD_SIZE + 1;
+ }
+
mdp = &mdep->md;
ERTS_ML_ASSERT(((void *) mdp) == ((void *) mdep));
@@ -858,7 +889,7 @@ erts_monitor_create(Uint16 type, Eterm ref, Eterm orgn, Eterm trgt, Eterm name)
mdp->origin.other.item = tsz ? copy_struct(trgt, tsz, &hp, &oh) : trgt;
mdp->origin.offset = (Uint16) offsetof(ErtsMonitorData, origin);
- mdp->origin.flags = ERTS_ML_FLG_EXTENDED|name_flag;
+ mdp->origin.flags = ERTS_ML_FLG_EXTENDED|name_flag|pending_flag;
mdp->origin.type = type;
if (type == ERTS_MON_TYPE_RESOURCE)
@@ -878,6 +909,25 @@ erts_monitor_create(Uint16 type, Eterm ref, Eterm orgn, Eterm trgt, Eterm name)
}
else {
mdep->u.name = name;
+ if (pending_flag) {
+ /* spawn_request() tag is in 'name' */
+ if (is_not_immed(name)) {
+ /*
+ * Save the tag in its own heap fragment with a
+ * little trick:
+ *
+ * bp->mem[0] = The tag
+ * bp->mem[1] = Beginning of heap
+ * mdep->u.name = Countinuation pointer to
+ * heap fragment...
+ */
+ Uint hsz = size_object(name)+1;
+ ErlHeapFragment *bp = new_message_buffer(hsz);
+ Eterm *hp = &bp->mem[1];
+ bp->mem[0] = copy_struct(name, hsz-1, &hp, &bp->off_heap);
+ mdep->u.name = make_cp((void*)bp);
+ }
+ }
mdp->origin.key_offset = (Uint16) offsetof(ErtsMonitorData, ref);
ERTS_ML_ASSERT(mdp->origin.key_offset >= mdp->origin.offset);
@@ -964,6 +1014,20 @@ erts_monitor_destroy__(ErtsMonitorData *mdp)
}
if (mdep->dist)
erts_mon_link_dist_dec_refc(mdep->dist);
+ if (mdp->origin.flags & ERTS_ML_FLG_SPAWN_PENDING) {
+ /*
+ * We have the spawn_request() tag stored in
+ * mdep->u.name via a little trick
+ * (see pending_flag in erts_monitor_create()).
+ * If non-immediate value make sure to release
+ * this heap fragment as well.
+ */
+ if (is_not_immed(mdep->u.name)) {
+ ErlHeapFragment *bp;
+ bp = (ErlHeapFragment *) cp_val(mdep->u.name);
+ free_message_buffer(bp);
+ }
+ }
erts_free(ERTS_ALC_T_MONITOR_EXT, mdp);
}
}
@@ -1248,11 +1312,26 @@ erts_link_create(Uint16 type, Eterm a, Eterm b)
Eterm *hp;
ErlOffHeap oh;
- if (is_internal_pid(a))
- hsz = NC_HEAP_SIZE(b);
- else
- hsz = NC_HEAP_SIZE(a);
- ERTS_ML_ASSERT(hsz > 0);
+ hsz = EXTERNAL_THING_HEAD_SIZE + 1;
+ if (hsz < ERTS_REF_THING_SIZE
+ && (is_internal_ordinary_ref(a)
+ || is_internal_ordinary_ref(b))) {
+ hsz = ERTS_REF_THING_SIZE;
+ }
+
+#ifdef DEBUG
+ if (is_internal_pid(a)) {
+ ERTS_ML_ASSERT(is_external_pid(b)
+ || is_internal_ordinary_ref(b));
+ ERTS_ML_ASSERT(NC_HEAP_SIZE(b) <= hsz);
+ }
+ else {
+ ERTS_ML_ASSERT(is_internal_pid(b));
+ ERTS_ML_ASSERT(is_external_pid(a)
+ || is_internal_ordinary_ref(a));
+ ERTS_ML_ASSERT(NC_HEAP_SIZE(a) <= hsz);
+ }
+#endif
size = sizeof(ErtsLinkDataExtended) - sizeof(Eterm);
size += hsz*sizeof(Eterm);
diff --git a/erts/emulator/beam/erl_monitor_link.h b/erts/emulator/beam/erl_monitor_link.h
index 86be400c09..e10bb27077 100644
--- a/erts/emulator/beam/erl_monitor_link.h
+++ b/erts/emulator/beam/erl_monitor_link.h
@@ -431,9 +431,22 @@
#define ERTS_ML_FLG_IN_SUBTABLE (((Uint16) 1) << 2)
#define ERTS_ML_FLG_NAME (((Uint16) 1) << 3)
#define ERTS_ML_FLG_EXTENDED (((Uint16) 1) << 4)
+#define ERTS_ML_FLG_SPAWN_PENDING (((Uint16) 1) << 5)
+#define ERTS_ML_FLG_SPAWN_MONITOR (((Uint16) 1) << 6)
+#define ERTS_ML_FLG_SPAWN_LINK (((Uint16) 1) << 7)
+#define ERTS_ML_FLG_SPAWN_ABANDONED (((Uint16) 1) << 8)
+#define ERTS_ML_FLG_SPAWN_NO_SMSG (((Uint16) 1) << 9)
+#define ERTS_ML_FLG_SPAWN_NO_EMSG (((Uint16) 1) << 10)
#define ERTS_ML_FLG_DBG_VISITED (((Uint16) 1) << 15)
+#define ERTS_ML_FLGS_SPAWN (ERTS_ML_FLG_SPAWN_PENDING \
+ | ERTS_ML_FLG_SPAWN_MONITOR \
+ | ERTS_ML_FLG_SPAWN_LINK \
+ | ERTS_ML_FLG_SPAWN_ABANDONED \
+ | ERTS_ML_FLG_SPAWN_NO_SMSG \
+ | ERTS_ML_FLG_SPAWN_NO_EMSG)
+
/* Flags that should be the same on both monitor/link halves */
#define ERTS_ML_FLGS_SAME \
(ERTS_ML_FLG_EXTENDED|ERTS_ML_FLG_NAME)
@@ -478,6 +491,7 @@ typedef struct {
ErtsMonLnkNode *monitors; /* Monitor double linked circular list */
ErtsMonLnkNode *orig_name_monitors; /* Origin named monitors
read-black tree */
+ ErtsMonLnkNode *dist_pend_spawn_exit;
} ErtsMonLnkDist;
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\
@@ -1397,7 +1411,7 @@ erts_monitor_release(ErtsMonitor *mon)
ErtsMonitorData *mdp = erts_monitor_to_data(mon);
ERTS_ML_ASSERT(erts_atomic32_read_nob(&mdp->refc) > 0);
- if (erts_atomic32_dec_read_nob(&mdp->refc) == 0) {
+ if (erts_atomic32_dec_read_mb(&mdp->refc) == 0) {
ERTS_ML_ASSERT(!(mdp->origin.flags & ERTS_ML_FLG_IN_TABLE));
ERTS_ML_ASSERT(!(mdp->target.flags & ERTS_ML_FLG_IN_TABLE));
@@ -1412,7 +1426,7 @@ erts_monitor_release_both(ErtsMonitorData *mdp)
== (mdp->target.flags & ERTS_ML_FLGS_SAME));
ERTS_ML_ASSERT(erts_atomic32_read_nob(&mdp->refc) >= 2);
- if (erts_atomic32_add_read_nob(&mdp->refc, (erts_aint32_t) -2) == 0) {
+ if (erts_atomic32_add_read_mb(&mdp->refc, (erts_aint32_t) -2) == 0) {
ERTS_ML_ASSERT(!(mdp->origin.flags & ERTS_ML_FLG_IN_TABLE));
ERTS_ML_ASSERT(!(mdp->target.flags & ERTS_ML_FLG_IN_TABLE));
diff --git a/erts/emulator/beam/erl_nfunc_sched.c b/erts/emulator/beam/erl_nfunc_sched.c
index b8cf2bee0e..8263a6e9b7 100644
--- a/erts/emulator/beam/erl_nfunc_sched.c
+++ b/erts/emulator/beam/erl_nfunc_sched.c
@@ -30,77 +30,37 @@
#include "erl_nfunc_sched.h"
#include "erl_trace.h"
-NifExport *
-erts_new_proc_nif_export(Process *c_p, int argc)
+ErtsNativeFunc *
+erts_new_proc_nfunc(Process *c_p, int argc)
{
+ ErtsNativeFunc *nep, *old_nep;
size_t size;
- int i;
- NifExport *nep, *old_nep;
-
- size = sizeof(NifExport) + (argc-1)*sizeof(Eterm);
- nep = erts_alloc(ERTS_ALC_T_NIF_TRAP_EXPORT, size);
- for (i = 0; i < ERTS_NUM_CODE_IX; i++)
- nep->exp.addressv[i] = &nep->exp.beam[0];
+ size = sizeof(ErtsNativeFunc) + (argc-1)*sizeof(Eterm);
+ nep = erts_alloc(ERTS_ALC_T_NFUNC_TRAP_WRAPPER, size);
nep->argc = -1; /* unused marker */
nep->argv_size = argc;
- nep->trace = NULL;
- old_nep = ERTS_PROC_SET_NIF_TRAP_EXPORT(c_p, nep);
+ old_nep = ERTS_PROC_SET_NFUNC_TRAP_WRAPPER(c_p, nep);
if (old_nep) {
- ASSERT(!nep->trace);
- erts_free(ERTS_ALC_T_NIF_TRAP_EXPORT, old_nep);
+ erts_free(ERTS_ALC_T_NFUNC_TRAP_WRAPPER, old_nep);
}
return nep;
}
void
-erts_destroy_nif_export(Process *p)
+erts_destroy_nfunc(Process *p)
{
- NifExport *nep = ERTS_PROC_SET_NIF_TRAP_EXPORT(p, NULL);
+ ErtsNativeFunc *nep = ERTS_PROC_SET_NFUNC_TRAP_WRAPPER(p, NULL);
if (nep) {
if (nep->m)
- erts_nif_export_cleanup_nif_mod(nep);
- erts_free(ERTS_ALC_T_NIF_TRAP_EXPORT, nep);
+ erts_nfunc_cleanup_nif_mod(nep);
+ erts_free(ERTS_ALC_T_NFUNC_TRAP_WRAPPER, nep);
}
}
-void
-erts_nif_export_save_trace(Process *c_p, NifExport *nep, int applying,
- Export* ep, BeamInstr *cp, Uint32 flags,
- Uint32 flags_meta, BeamInstr* I,
- ErtsTracer meta_tracer)
-{
- NifExportTrace *netp;
- ASSERT(nep && nep->argc >= 0);
- ASSERT(!nep->trace);
- netp = erts_alloc(ERTS_ALC_T_NIF_EXP_TRACE,
- sizeof(NifExportTrace));
- netp->applying = applying;
- netp->ep = ep;
- netp->cp = cp;
- netp->flags = flags;
- netp->flags_meta = flags_meta;
- netp->I = I;
- netp->meta_tracer = NIL;
- erts_tracer_update(&netp->meta_tracer, meta_tracer);
- nep->trace = netp;
-}
-
-void
-erts_nif_export_restore_trace(Process *c_p, Eterm result, NifExport *nep)
-{
- NifExportTrace *netp = nep->trace;
- nep->trace = NULL;
- erts_bif_trace_epilogue(c_p, result, netp->applying, netp->ep,
- netp->cp, netp->flags, netp->flags_meta,
- netp->I, netp->meta_tracer);
- erts_tracer_update(&netp->meta_tracer, NIL);
- erts_free(ERTS_ALC_T_NIF_EXP_TRACE, netp);
-}
-
-NifExport *
-erts_nif_export_schedule(Process *c_p, Process *dirty_shadow_proc,
+ErtsNativeFunc *
+erts_nfunc_schedule(Process *c_p, Process *dirty_shadow_proc,
ErtsCodeMFA *mfa, BeamInstr *pc,
BeamInstr instr,
void *dfunc, void *ifunc,
@@ -110,7 +70,7 @@ erts_nif_export_schedule(Process *c_p, Process *dirty_shadow_proc,
Process *used_proc;
ErtsSchedulerData *esdp;
Eterm* reg;
- NifExport* nep;
+ ErtsNativeFunc* nep;
int i;
ERTS_LC_ASSERT(erts_proc_lc_my_proc_locks(c_p)
@@ -133,10 +93,10 @@ erts_nif_export_schedule(Process *c_p, Process *dirty_shadow_proc,
reg = esdp->x_reg_array;
if (mfa)
- nep = erts_get_proc_nif_export(c_p, (int) mfa->arity);
+ nep = erts_get_proc_nfunc(c_p, (int) mfa->arity);
else {
/* If no mfa, this is not the first schedule... */
- nep = ERTS_PROC_GET_NIF_TRAP_EXPORT(c_p);
+ nep = ERTS_PROC_GET_NFUNC_TRAP_WRAPPER(c_p);
ASSERT(nep && nep->argc >= 0);
}
@@ -148,16 +108,15 @@ erts_nif_export_schedule(Process *c_p, Process *dirty_shadow_proc,
for (i = 0; i < (int) mfa->arity; i++)
nep->argv[i] = reg[i];
nep->pc = pc;
- nep->cp = c_p->cp;
nep->mfa = mfa;
nep->current = c_p->current;
ASSERT(argc >= 0);
nep->argc = (int) mfa->arity;
nep->m = NULL;
- ASSERT(!erts_check_nif_export_in_area(c_p,
+ ASSERT(!erts_check_nfunc_in_area(c_p,
(char *) nep,
- (sizeof(NifExport)
+ (sizeof(ErtsNativeFunc)
+ (sizeof(Eterm)
*(nep->argc-1)))));
}
@@ -167,14 +126,14 @@ erts_nif_export_schedule(Process *c_p, Process *dirty_shadow_proc,
reg[i] = argv[i];
}
ASSERT(is_atom(mod) && is_atom(func));
- nep->exp.info.mfa.module = mod;
- nep->exp.info.mfa.function = func;
- nep->exp.info.mfa.arity = (Uint) argc;
- nep->exp.beam[0] = (BeamInstr) instr; /* call_nif || apply_bif */
- nep->exp.beam[1] = (BeamInstr) dfunc;
+ nep->trampoline.info.mfa.module = mod;
+ nep->trampoline.info.mfa.function = func;
+ nep->trampoline.info.mfa.arity = (Uint) argc;
+ nep->trampoline.call_op = (BeamInstr) instr; /* call_bif || call_nif */
+ nep->trampoline.dfunc = (BeamInstr) dfunc;
nep->func = ifunc;
used_proc->arity = argc;
used_proc->freason = TRAP;
- used_proc->i = (BeamInstr*) nep->exp.addressv[0];
+ used_proc->i = (BeamInstr*)&nep->trampoline.call_op;
return nep;
}
diff --git a/erts/emulator/beam/erl_nfunc_sched.h b/erts/emulator/beam/erl_nfunc_sched.h
index 1cb252eba5..4dae242d4f 100644
--- a/erts/emulator/beam/erl_nfunc_sched.h
+++ b/erts/emulator/beam/erl_nfunc_sched.h
@@ -25,92 +25,78 @@
#include "bif.h"
#include "error.h"
-typedef struct {
- int applying;
- Export* ep;
- BeamInstr *cp;
- Uint32 flags;
- Uint32 flags_meta;
- BeamInstr* I;
- ErtsTracer meta_tracer;
-} NifExportTrace;
-
/*
- * NIF exports need a few more items than the Export struct provides,
- * including the erl_module_nif* and a NIF function pointer, so the
- * NifExport below adds those. The Export member must be first in the
- * struct. A number of values are stored for error handling purposes
- * only.
+ * Native function wrappers are used to schedule native functions on both
+ * normal and dirty schedulers.
+ *
+ * A number of values are only stored for error handling, and the fields
+ * following `current` can be omitted when a wrapper is statically "scheduled"
+ * through placement in a function stub.
*
- * 'argc' is >= 0 when NifExport is in use, and < 0 when not.
+ * 'argc' is >= 0 when ErtsNativeFunc is in use, and < 0 when not.
*/
typedef struct {
- Export exp;
+ struct {
+ ErtsCodeInfo info;
+ BeamInstr call_op; /* call_bif || call_nif */
+ BeamInstr dfunc;
+ } trampoline;
+
struct erl_module_nif* m; /* NIF module, or NULL if BIF */
void *func; /* Indirect NIF or BIF to execute (may be unused) */
ErtsCodeMFA *current;/* Current as set when originally called */
- NifExportTrace *trace;
/* --- The following is only used on error --- */
BeamInstr *pc; /* Program counter */
- BeamInstr *cp; /* Continuation pointer */
ErtsCodeMFA *mfa; /* MFA of original call */
int argc; /* Number of arguments in original call */
int argv_size; /* Allocated size of argv */
Eterm argv[1]; /* Saved arguments from the original call */
-} NifExport;
-
-NifExport *erts_new_proc_nif_export(Process *c_p, int argc);
-void erts_nif_export_save_trace(Process *c_p, NifExport *nep, int applying,
- Export* ep, BeamInstr *cp, Uint32 flags,
- Uint32 flags_meta, BeamInstr* I,
- ErtsTracer meta_tracer);
-void erts_nif_export_restore_trace(Process *c_p, Eterm result, NifExport *nep);
-void erts_destroy_nif_export(Process *p);
-NifExport *erts_nif_export_schedule(Process *c_p, Process *dirty_shadow_proc,
- ErtsCodeMFA *mfa, BeamInstr *pc,
- BeamInstr instr,
- void *dfunc, void *ifunc,
- Eterm mod, Eterm func,
- int argc, const Eterm *argv);
-void erts_nif_export_cleanup_nif_mod(NifExport *ep); /* erl_nif.c */
-ERTS_GLB_INLINE NifExport *erts_get_proc_nif_export(Process *c_p, int extra);
-ERTS_GLB_INLINE int erts_setup_nif_export_rootset(Process* proc, Eterm** objv,
- Uint* nobj);
-ERTS_GLB_INLINE int erts_check_nif_export_in_area(Process *p,
- char *start, Uint size);
-ERTS_GLB_INLINE void erts_nif_export_restore(Process *c_p, NifExport *ep,
- Eterm result);
-ERTS_GLB_INLINE void erts_nif_export_restore_error(Process* c_p, BeamInstr **pc,
- Eterm *reg, ErtsCodeMFA **nif_mfa);
-ERTS_GLB_INLINE int erts_nif_export_check_save_trace(Process *c_p, Eterm result,
- int applying, Export* ep,
- BeamInstr *cp, Uint32 flags,
- Uint32 flags_meta, BeamInstr* I,
- ErtsTracer meta_tracer);
+} ErtsNativeFunc;
+
+ErtsNativeFunc *erts_new_proc_nfunc(Process *c_p, int argc);
+void erts_destroy_nfunc(Process *p);
+ErtsNativeFunc *erts_nfunc_schedule(Process *c_p, Process *dirty_shadow_proc,
+ ErtsCodeMFA *mfa, BeamInstr *pc,
+ BeamInstr instr,
+ void *dfunc, void *ifunc,
+ Eterm mod, Eterm func,
+ int argc, const Eterm *argv);
+void erts_nfunc_cleanup_nif_mod(ErtsNativeFunc *ep); /* erl_nif.c */
+ERTS_GLB_INLINE ErtsNativeFunc *erts_get_proc_nfunc(Process *c_p, int extra);
+ERTS_GLB_INLINE int erts_setup_nfunc_rootset(Process* proc, Eterm** objv,
+ Uint* nobj);
+ERTS_GLB_INLINE int erts_check_nfunc_in_area(Process *p,
+ char *start, Uint size);
+ERTS_GLB_INLINE void erts_nfunc_restore(Process *c_p, ErtsNativeFunc *ep,
+ Eterm result);
+ERTS_GLB_INLINE void erts_nfunc_restore_error(Process* c_p,
+ BeamInstr **pc,
+ Eterm *reg,
+ ErtsCodeMFA **nif_mfa);
ERTS_GLB_INLINE Process *erts_proc_shadow2real(Process *c_p);
#if ERTS_GLB_INLINE_INCL_FUNC_DEF
-ERTS_GLB_INLINE NifExport *
-erts_get_proc_nif_export(Process *c_p, int argc)
+ERTS_GLB_INLINE ErtsNativeFunc *
+erts_get_proc_nfunc(Process *c_p, int argc)
{
- NifExport *nep = ERTS_PROC_GET_NIF_TRAP_EXPORT(c_p);
+ ErtsNativeFunc *nep = ERTS_PROC_GET_NFUNC_TRAP_WRAPPER(c_p);
if (!nep || (nep->argc < 0 && nep->argv_size < argc))
- return erts_new_proc_nif_export(c_p, argc);
+ return erts_new_proc_nfunc(c_p, argc);
return nep;
}
/*
* If a process has saved arguments, they need to be part of the GC
* rootset. The function below is called from setup_rootset() in
- * erl_gc.c. Any exception term saved in the NifExport is also made
+ * erl_gc.c. Any exception term saved in the ErtsNativeFunc is also made
* part of the GC rootset here; it always resides in rootset[0].
*/
ERTS_GLB_INLINE int
-erts_setup_nif_export_rootset(Process* proc, Eterm** objv, Uint* nobj)
+erts_setup_nfunc_rootset(Process* proc, Eterm** objv, Uint* nobj)
{
- NifExport* ep = (NifExport*) ERTS_PROC_GET_NIF_TRAP_EXPORT(proc);
+ ErtsNativeFunc* ep = (ErtsNativeFunc*) ERTS_PROC_GET_NFUNC_TRAP_WRAPPER(proc);
if (!ep || ep->argc <= 0)
return 0;
@@ -121,18 +107,16 @@ erts_setup_nif_export_rootset(Process* proc, Eterm** objv, Uint* nobj)
}
/*
- * Check if nif export points into code area...
+ * Check if native func wrapper points into code area...
*/
ERTS_GLB_INLINE int
-erts_check_nif_export_in_area(Process *p, char *start, Uint size)
+erts_check_nfunc_in_area(Process *p, char *start, Uint size)
{
- NifExport *nep = ERTS_PROC_GET_NIF_TRAP_EXPORT(p);
+ ErtsNativeFunc *nep = ERTS_PROC_GET_NFUNC_TRAP_WRAPPER(p);
if (!nep || nep->argc < 0)
return 0;
if (ErtsInArea(nep->pc, start, size))
return 1;
- if (ErtsInArea(nep->cp, start, size))
- return 1;
if (ErtsInArea(nep->mfa, start, size))
return 1;
if (ErtsInArea(nep->current, start, size))
@@ -141,7 +125,7 @@ erts_check_nif_export_in_area(Process *p, char *start, Uint size)
}
ERTS_GLB_INLINE void
-erts_nif_export_restore(Process *c_p, NifExport *ep, Eterm result)
+erts_nfunc_restore(Process *c_p, ErtsNativeFunc *ep, Eterm result)
{
ASSERT(!ERTS_SCHEDULER_IS_DIRTY(erts_get_scheduler_data()));
ERTS_LC_ASSERT(!(c_p->static_flags
@@ -151,43 +135,21 @@ erts_nif_export_restore(Process *c_p, NifExport *ep, Eterm result)
c_p->current = ep->current;
ep->argc = -1; /* Unused nif-export marker... */
- if (ep->trace)
- erts_nif_export_restore_trace(c_p, result, ep);
}
ERTS_GLB_INLINE void
-erts_nif_export_restore_error(Process* c_p, BeamInstr **pc,
+erts_nfunc_restore_error(Process* c_p, BeamInstr **pc,
Eterm *reg, ErtsCodeMFA **nif_mfa)
{
- NifExport *nep = (NifExport *) ERTS_PROC_GET_NIF_TRAP_EXPORT(c_p);
+ ErtsNativeFunc *nep = (ErtsNativeFunc *) ERTS_PROC_GET_NFUNC_TRAP_WRAPPER(c_p);
int ix;
ASSERT(nep);
*pc = nep->pc;
- c_p->cp = nep->cp;
*nif_mfa = nep->mfa;
for (ix = 0; ix < nep->argc; ix++)
reg[ix] = nep->argv[ix];
- erts_nif_export_restore(c_p, nep, THE_NON_VALUE);
-}
-
-ERTS_GLB_INLINE int
-erts_nif_export_check_save_trace(Process *c_p, Eterm result,
- int applying, Export* ep,
- BeamInstr *cp, Uint32 flags,
- Uint32 flags_meta, BeamInstr* I,
- ErtsTracer meta_tracer)
-{
- if (is_non_value(result) && c_p->freason == TRAP) {
- NifExport *nep = ERTS_PROC_GET_NIF_TRAP_EXPORT(c_p);
- if (nep && nep->argc >= 0) {
- erts_nif_export_save_trace(c_p, nep, applying, ep,
- cp, flags, flags_meta,
- I, meta_tracer);
- return 1;
- }
- }
- return 0;
+ erts_nfunc_restore(c_p, nep, THE_NON_VALUE);
}
ERTS_GLB_INLINE Process *
@@ -210,10 +172,10 @@ erts_proc_shadow2real(Process *c_p)
#if defined(ERTS_WANT_NFUNC_SCHED_INTERNALS__) && !defined(ERTS_NFUNC_SCHED_INTERNALS__)
#define ERTS_NFUNC_SCHED_INTERNALS__
-#define ERTS_I_BEAM_OP_TO_NIF_EXPORT(I) \
- (ASSERT(BeamIsOpCode(*(I), op_apply_bif) || \
- BeamIsOpCode(*(I), op_call_nif)), \
- ((NifExport *) (((char *) (I)) - offsetof(NifExport, exp.beam[0]))))
+#define ERTS_I_BEAM_OP_TO_NFUNC(I) \
+ (ASSERT(BeamIsOpCode(*(I), op_call_bif_W) || \
+ BeamIsOpCode(*(I), op_call_nif_WWW)), \
+ ((ErtsNativeFunc *) (((char *) (I)) - offsetof(ErtsNativeFunc, trampoline.call_op))))
#include "erl_message.h"
diff --git a/erts/emulator/beam/erl_nif.c b/erts/emulator/beam/erl_nif.c
index 27959ba2bb..71165f5ff8 100644
--- a/erts/emulator/beam/erl_nif.c
+++ b/erts/emulator/beam/erl_nif.c
@@ -74,12 +74,25 @@
* 'handle'.
*/
struct erl_module_nif {
+ erts_refc_t refc; /* References to this struct
+ * +1 erl_module_instance (loaded Erlang code)
+ * +1 "dlopen" (loaded native code)
+ * +1 scheduled load finisher
+ * +1 for each owned resource type
+ */
+ erts_mtx_t load_mtx; /* protects load finish from unload */
+ struct ErtsNifFinish_* finish;
+ ErtsThrPrgrLaterOp lop;
+
void* priv_data;
void* handle; /* "dlopen" */
struct enif_entry_t entry;
- erts_refc_t rt_cnt; /* number of resource types */
- erts_refc_t rt_dtor_cnt; /* number of resource types with destructors */
- Module* mod; /* Can be NULL if orphan with dtor-resources left */
+ erts_refc_t dynlib_refc; /* References to loaded native code
+ +1 erl_module_instance
+ +1 for each owned resource type with callbacks
+ +1 for each ongoing dirty NIF call
+ */
+ Module* mod; /* Can be NULL if purged and dynlib_refc > 0 */
ErlNifFunc _funcs_copy_[1]; /* only used for old libs */
};
@@ -309,10 +322,10 @@ void erts_post_nif(ErlNifEnv* env)
/*
- * Initialize a NifExport struct. Create it if needed and store it in the
+ * Initialize a ErtsNativeFunc struct. Create it if needed and store it in the
* proc. The direct_fp function is what will be invoked by op_call_nif, and
* the indirect_fp function, if not NULL, is what the direct_fp function
- * will call. If the allocated NifExport isn't enough to hold all of argv,
+ * will call. If the allocated ErtsNativeFunc isn't enough to hold all of argv,
* allocate a larger one. Save 'current' and registers if first time this
* call is scheduled.
*/
@@ -321,7 +334,7 @@ static ERTS_INLINE ERL_NIF_TERM
schedule(ErlNifEnv* env, NativeFunPtr direct_fp, NativeFunPtr indirect_fp,
Eterm mod, Eterm func_name, int argc, const ERL_NIF_TERM argv[])
{
- NifExport *ep;
+ ErtsNativeFunc *ep;
Process *c_p, *dirty_shadow_proc;
execution_state(env, &c_p, NULL);
@@ -332,16 +345,16 @@ schedule(ErlNifEnv* env, NativeFunPtr direct_fp, NativeFunPtr indirect_fp,
ERTS_LC_ASSERT(ERTS_PROC_LOCK_MAIN & erts_proc_lc_my_proc_locks(c_p));
- ep = erts_nif_export_schedule(c_p, dirty_shadow_proc,
+ ep = erts_nfunc_schedule(c_p, dirty_shadow_proc,
c_p->current,
- c_p->cp,
- BeamOpCodeAddr(op_call_nif),
+ cp_val(c_p->stop[0]),
+ BeamOpCodeAddr(op_call_nif_WWW),
direct_fp, indirect_fp,
mod, func_name,
argc, (const Eterm *) argv);
if (!ep->m) {
/* First time this call is scheduled... */
- erts_refc_inc(&env->mod_nif->rt_dtor_cnt, 1);
+ erts_refc_inc(&env->mod_nif->dynlib_refc, 2);
ep->m = env->mod_nif;
}
return (ERL_NIF_TERM) THE_NON_VALUE;
@@ -356,7 +369,7 @@ erts_call_dirty_nif(ErtsSchedulerData *esdp, Process *c_p, BeamInstr *I, Eterm *
{
int exiting;
ERL_NIF_TERM *argv = (ERL_NIF_TERM *) reg;
- NifExport *nep = ERTS_I_BEAM_OP_TO_NIF_EXPORT(I);
+ ErtsNativeFunc *nep = ERTS_I_BEAM_OP_TO_NFUNC(I);
ErtsCodeMFA *codemfa = erts_code_to_codemfa(I);
NativeFunPtr dirty_nif = (NativeFunPtr) I[1];
ErlNifEnv env;
@@ -364,7 +377,7 @@ erts_call_dirty_nif(ErtsSchedulerData *esdp, Process *c_p, BeamInstr *I, Eterm *
#ifdef DEBUG
erts_aint32_t state = erts_atomic32_read_nob(&c_p->state);
- ASSERT(nep == ERTS_PROC_GET_NIF_TRAP_EXPORT(c_p));
+ ASSERT(nep == ERTS_PROC_GET_NFUNC_TRAP_WRAPPER(c_p));
ASSERT(!c_p->scheduler_data);
ASSERT((state & ERTS_PSFLG_DIRTY_RUNNING)
@@ -815,7 +828,7 @@ int enif_send(ErlNifEnv* env, const ErlNifPid* to_pid,
}
#endif
if (have_seqtrace(stoken)) {
- seq_trace_update_send(c_p);
+ seq_trace_update_serial(c_p);
seq_trace_output(stoken, msg, SEQ_TRACE_SEND,
rp->common.id, c_p);
}
@@ -2189,7 +2202,10 @@ int enif_vsnprintf(char* buffer, size_t size, const char *format, va_list ap)
** Memory managed (GC'ed) "resource" objects **
***********************************************************/
-/* dummy node in circular list */
+/*
+ * Sentinel node in circular list of all resource types.
+ * List protected by code_write_permission.
+ */
struct enif_resource_type_t resource_type_list;
static ErlNifResourceType* find_resource_type(Eterm module, Eterm name)
@@ -2209,16 +2225,26 @@ static ErlNifResourceType* find_resource_type(Eterm module, Eterm name)
#define in_area(ptr,start,nbytes) \
((UWord)((char*)(ptr) - (char*)(start)) < (nbytes))
-static ERTS_INLINE int rt_have_callbacks(ErlNifResourceType* rt)
+static ERTS_INLINE int rt_have_callbacks(ErlNifResourceTypeInit* rti)
{
- return rt->dtor != NULL;
+ return rti->dtor != NULL;
+}
+
+static void deref_nifmod(struct erl_module_nif* lib)
+{
+ if (erts_refc_dectest(&lib->refc, 0) == 0) {
+ ASSERT(lib->handle == NULL);
+ ASSERT(lib->mod == NULL);
+ erts_free(ERTS_ALC_T_NIF, lib);
+ }
}
-static void close_lib(struct erl_module_nif* lib)
+static void close_dynlib(struct erl_module_nif* lib)
{
ASSERT(lib != NULL);
+ ASSERT(lib->mod == NULL);
ASSERT(lib->handle != NULL);
- ASSERT(erts_refc_read(&lib->rt_dtor_cnt,0) == 0);
+ ASSERT(erts_refc_read(&lib->dynlib_refc,0) == 0);
if (lib->entry.unload != NULL) {
struct enif_msg_environment_t msg_env;
@@ -2228,24 +2254,56 @@ static void close_lib(struct erl_module_nif* lib)
}
if (!erts_is_static_nif(lib->handle))
erts_sys_ddll_close(lib->handle);
+
lib->handle = NULL;
+ deref_nifmod(lib);
}
static void steal_resource_type(ErlNifResourceType* type)
{
struct erl_module_nif* lib = type->owner;
- if (rt_have_callbacks(type)
- && erts_refc_dectest(&lib->rt_dtor_cnt, 0) == 0
- && lib->mod == NULL) {
- /* last type with destructor gone, close orphan lib */
-
- close_lib(lib);
- }
- if (erts_refc_dectest(&lib->rt_cnt, 0) == 0
- && lib->mod == NULL) {
- erts_free(ERTS_ALC_T_NIF, lib);
+ if (rt_have_callbacks(&type->fn_real)
+ && erts_refc_dectest(&lib->dynlib_refc, 0) == 0) {
+ /* last resource type with callbacks gone, close purged lib */
+ close_dynlib(lib);
}
+ deref_nifmod(lib);
+}
+
+static erts_rwmtx_t erts_nif_call_tab_lock;
+
+static void resource_dtor_during_takeover(ErlNifEnv* env, void* obj)
+{
+ ErtsResource* resource = DATA_TO_RESOURCE(obj);
+ ErlNifResourceType* rt = resource->type;
+
+ erts_rwmtx_rlock(&erts_nif_call_tab_lock);
+ if (rt->fn_real.dtor)
+ rt->fn_real.dtor(env, obj);
+ erts_rwmtx_runlock(&erts_nif_call_tab_lock);
+}
+static void resource_stop_during_takeover(ErlNifEnv* env, void* obj,
+ ErlNifEvent e, int is_direct_call)
+{
+ ErtsResource* resource = DATA_TO_RESOURCE(obj);
+ ErlNifResourceType* rt = resource->type;
+
+ erts_rwmtx_rlock(&erts_nif_call_tab_lock);
+ ASSERT(rt->fn_real.stop);
+ rt->fn_real.stop(env, obj, e, is_direct_call);
+ erts_rwmtx_runlock(&erts_nif_call_tab_lock);
+}
+static void resource_down_during_takeover(ErlNifEnv* env, void* obj,
+ ErlNifPid* pid, ErlNifMonitor* mon)
+{
+ ErtsResource* resource = DATA_TO_RESOURCE(obj);
+ ErlNifResourceType* rt = resource->type;
+
+ erts_rwmtx_rlock(&erts_nif_call_tab_lock);
+ ASSERT(rt->fn_real.down);
+ rt->fn_real.down(env, obj, pid, mon);
+ erts_rwmtx_runlock(&erts_nif_call_tab_lock);
}
static void resource_dtor_nop(ErlNifEnv* env, void* obj)
@@ -2278,7 +2336,7 @@ ErlNifResourceType* open_resource_type(ErlNifEnv* env,
ErlNifResourceFlags op = flags;
Eterm module_am, name_am;
- ASSERT(erts_thr_progress_is_blocking());
+ ERTS_LC_ASSERT(erts_has_code_write_permission());
module_am = make_atom(env->mod_nif->mod->module);
name_am = enif_make_atom(env, name_str);
@@ -2292,7 +2350,8 @@ ErlNifResourceType* open_resource_type(ErlNifEnv* env,
erts_refc_init(&type->refc, 1);
op = ERL_NIF_RT_CREATE;
#ifdef DEBUG
- type->dtor = (void*)1;
+ type->fn.dtor = (void*)1;
+ type->fn_real.dtor = (void*)1;
type->owner = (void*)2;
type->prev = (void*)3;
type->next = (void*)4;
@@ -2355,14 +2414,16 @@ enif_open_resource_type_x(ErlNifEnv* env,
env->mod_nif->entry.sizeof_ErlNifResourceTypeInit);
}
-static void commit_opened_resource_types(struct erl_module_nif* lib)
+static void prepare_opened_rt(struct erl_module_nif* lib)
{
- while (opened_rt_list) {
- struct opened_resource_type* ort = opened_rt_list;
+ struct opened_resource_type* ort = opened_rt_list;
+ while (ort) {
ErlNifResourceType* type = ort->type;
if (ort->op == ERL_NIF_RT_CREATE) {
+ type->fn = ort->new_callbacks;
+ type->fn_real = ort->new_callbacks;
type->prev = &resource_type_list;
type->next = resource_type_list.next;
type->next->prev = type;
@@ -2370,20 +2431,55 @@ static void commit_opened_resource_types(struct erl_module_nif* lib)
}
else { /* ERL_NIF_RT_TAKEOVER */
steal_resource_type(type);
+
+ /*
+ * Prepare for atomic change of callbacks with lock-wrappers
+ */
+ type->fn.dtor = resource_dtor_during_takeover;
+ type->fn.stop = resource_stop_during_takeover;
+ type->fn.down = resource_down_during_takeover;
}
+ type->owner = lib;
- type->owner = lib;
- type->dtor = ort->new_callbacks.dtor;
- type->stop = ort->new_callbacks.stop;
- type->down = ort->new_callbacks.down;
+ if (rt_have_callbacks(&ort->new_callbacks))
+ erts_refc_inc(&lib->dynlib_refc, 2);
+ erts_refc_inc(&lib->refc, 2);
- if (rt_have_callbacks(type)) {
- erts_refc_inc(&lib->rt_dtor_cnt, 1);
- }
- erts_refc_inc(&lib->rt_cnt, 1);
+ ort = ort->next;
+ }
+}
- opened_rt_list = ort->next;
- erts_free(ERTS_ALC_T_TMP, ort);
+/*
+ * Do atomic change from old to new callbacks
+ */
+static void commit_opened_rt(void)
+{
+ struct opened_resource_type* ort = opened_rt_list;
+
+ ERTS_LC_ASSERT(erts_lc_rwmtx_is_rwlocked(&erts_nif_call_tab_lock));
+
+ while (ort) {
+ if (ort->op == ERL_NIF_RT_TAKEOVER) {
+ ort->type->fn_real = ort->new_callbacks;
+ }
+ ort = ort->next;
+ }
+}
+
+/*
+ * Cleanup and let callbacks be called directly without locking
+ */
+static void cleanup_opened_rt(void)
+{
+ struct opened_resource_type* ort = opened_rt_list;
+
+ while (opened_rt_list) {
+ ort = opened_rt_list;
+ if (ort->op == ERL_NIF_RT_TAKEOVER) {
+ ort->type->fn = ort->new_callbacks;
+ }
+ opened_rt_list = ort->next;
+ erts_free(ERTS_ALC_T_TMP, ort);
}
}
@@ -2494,7 +2590,7 @@ static void run_resource_dtor(void* vbin)
ErtsMonitor *root;
Uint refc;
- ASSERT(type->down);
+ ASSERT(type->fn.down);
erts_mtx_lock(&rm->lock);
ASSERT(erts_refc_read(&bin->intern.refc, 0) == 0);
kill = !rmon_is_dying(rm);
@@ -2523,10 +2619,10 @@ static void run_resource_dtor(void* vbin)
erts_mtx_destroy(&rm->lock);
}
- if (type->dtor != NULL) {
+ if (type->fn.dtor != NULL) {
struct enif_msg_environment_t msg_env;
pre_nif_noproc(&msg_env, type->owner, NULL);
- type->dtor(&msg_env.env, resource->data);
+ type->fn.dtor(&msg_env.env, resource->data);
post_nif_noproc(&msg_env);
}
if (erts_refc_dectest(&type->refc, 0) == 0) {
@@ -2543,9 +2639,9 @@ void erts_resource_stop(ErtsResource* resource, ErlNifEvent e,
int is_direct_call)
{
struct enif_msg_environment_t msg_env;
- ASSERT(resource->type->stop);
+ ASSERT(resource->type->fn.stop);
pre_nif_noproc(&msg_env, resource->type->owner, NULL);
- resource->type->stop(&msg_env.env, resource->data, e, is_direct_call);
+ resource->type->fn.stop(&msg_env.env, resource->data, e, is_direct_call);
post_nif_noproc(&msg_env);
}
@@ -2556,7 +2652,7 @@ void erts_nif_demonitored(ErtsResource* resource)
int free_me;
ASSERT(rmp);
- ASSERT(resource->type->down);
+ ASSERT(resource->type->fn.down);
erts_mtx_lock(&rmp->lock);
free_me = ((rmon_refc_dec_read(rmp) == 0) & !!rmon_is_dying(rmp));
@@ -2590,7 +2686,7 @@ void erts_fire_nif_monitor(ErtsMonitor *tmon)
omon = &mdp->origin;
ASSERT(rmp);
- ASSERT(resource->type->down);
+ ASSERT(resource->type->fn.down);
erts_mtx_lock(&rmp->lock);
@@ -2617,7 +2713,7 @@ void erts_fire_nif_monitor(ErtsMonitor *tmon)
erts_ref_to_driver_monitor(mdp->ref, &nif_monitor);
nif_pid.pid = omon->other.item;
pre_nif_noproc(&msg_env, resource->type->owner, NULL);
- resource->type->down(&msg_env.env, resource->data, &nif_pid, &nif_monitor);
+ resource->type->fn.down(&msg_env.env, resource->data, &nif_pid, &nif_monitor);
post_nif_noproc(&msg_env);
erts_bin_release(&bin->binary);
@@ -2634,7 +2730,7 @@ void* enif_alloc_resource(ErlNifResourceType* type, size_t data_sz)
ErtsResource* resource;
size_t monitors_offs;
- if (type->down) {
+ if (type->fn.down) {
/* Put ErtsResourceMonitors after user data and properly aligned */
monitors_offs = ((data_sz + ERTS_ALLOC_ALIGN_BYTES - 1)
& ~((size_t)ERTS_ALLOC_ALIGN_BYTES - 1));
@@ -2656,7 +2752,7 @@ void* enif_alloc_resource(ErlNifResourceType* type, size_t data_sz)
erts_refc_init(&resource->nif_refc, 1);
#endif
erts_refc_inc(&resource->type->refc, 2);
- if (type->down) {
+ if (type->fn.down) {
resource->monitors = (ErtsResourceMonitors*) (resource->data + monitors_offs);
erts_mtx_init(&resource->monitors->lock, "resource_monitors", NIL,
ERTS_LOCK_FLAGS_CATEGORY_GENERIC);
@@ -2838,25 +2934,25 @@ int enif_consume_timeslice(ErlNifEnv* env, int percent)
}
static ERTS_INLINE void
-nif_export_cleanup_nif_mod(NifExport *ep)
+nfunc_cleanup_nif_mod(ErtsNativeFunc *ep)
{
- if (erts_refc_dectest(&ep->m->rt_dtor_cnt, 0) == 0 && ep->m->mod == NULL)
- close_lib(ep->m);
+ if (erts_refc_dectest(&ep->m->dynlib_refc, 0) == 0)
+ close_dynlib(ep->m);
ep->m = NULL;
}
void
-erts_nif_export_cleanup_nif_mod(NifExport *ep)
+erts_nfunc_cleanup_nif_mod(ErtsNativeFunc *ep)
{
- nif_export_cleanup_nif_mod(ep);
+ nfunc_cleanup_nif_mod(ep);
}
static ERTS_INLINE void
-nif_export_restore(Process *c_p, NifExport *ep, Eterm res)
+nfunc_restore(Process *c_p, ErtsNativeFunc *ep, Eterm res)
{
- erts_nif_export_restore(c_p, ep, res);
+ erts_nfunc_restore(c_p, ep, res);
ASSERT(ep->m);
- nif_export_cleanup_nif_mod(ep);
+ nfunc_cleanup_nif_mod(ep);
}
@@ -2873,15 +2969,15 @@ static ERL_NIF_TERM
dirty_nif_finalizer(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
Process* proc;
- NifExport* ep;
+ ErtsNativeFunc* ep;
execution_state(env, &proc, NULL);
ASSERT(argc == 1);
ASSERT(!ERTS_SCHEDULER_IS_DIRTY(erts_proc_sched_data(proc)));
- ep = (NifExport*) ERTS_PROC_GET_NIF_TRAP_EXPORT(proc);
+ ep = (ErtsNativeFunc*) ERTS_PROC_GET_NFUNC_TRAP_WRAPPER(proc);
ASSERT(ep);
- nif_export_restore(proc, ep, argv[0]);
+ nfunc_restore(proc, ep, argv[0]);
return argv[0];
}
@@ -2893,21 +2989,22 @@ dirty_nif_exception(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
ERL_NIF_TERM ret;
Process* proc;
- NifExport* ep;
+ ErtsNativeFunc* ep;
Eterm exception;
execution_state(env, &proc, NULL);
ASSERT(argc == 1);
ASSERT(!ERTS_SCHEDULER_IS_DIRTY(erts_proc_sched_data(proc)));
- ep = (NifExport*) ERTS_PROC_GET_NIF_TRAP_EXPORT(proc);
+ ep = (ErtsNativeFunc*) ERTS_PROC_GET_NFUNC_TRAP_WRAPPER(proc);
ASSERT(ep);
exception = argv[0]; /* argv overwritten by restore below... */
- nif_export_cleanup_nif_mod(ep);
+ nfunc_cleanup_nif_mod(ep);
ret = enif_raise_exception(env, exception);
- /* Restore orig info for error and clear nif export in handle_error() */
- proc->freason |= EXF_RESTORE_NIF;
+ /* Restore orig info for error and clear native func wrapper in
+ * handle_error() */
+ proc->freason |= EXF_RESTORE_NFUNC;
return ret;
}
@@ -2944,7 +3041,7 @@ static_schedule_dirty_nif(ErlNifEnv* env, erts_aint32_t dirty_psflg,
int argc, const ERL_NIF_TERM argv[])
{
Process *proc;
- NifExport *ep;
+ ErtsNativeFunc *ep;
Eterm mod, func;
NativeFunPtr fp;
@@ -2954,12 +3051,11 @@ static_schedule_dirty_nif(ErlNifEnv* env, erts_aint32_t dirty_psflg,
* Called in order to schedule statically determined
* dirty NIF calls...
*
- * Note that 'current' does not point into a NifExport
- * structure; only a structure with similar
- * parts (located in code).
+ * Note that 'current' does not point into a ErtsNativeFunc
+ * structure; only a structure with similar parts (located in code).
*/
- ep = ErtsContainerStruct(proc->current, NifExport, exp.info.mfa);
+ ep = ErtsContainerStruct(proc->current, ErtsNativeFunc, trampoline.info.mfa);
mod = proc->current->module;
func = proc->current->function;
fp = (NativeFunPtr) ep->func;
@@ -2987,7 +3083,6 @@ static_schedule_dirty_cpu_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[
return static_schedule_dirty_nif(env, ERTS_PSFLG_DIRTY_CPU_PROC, argc, argv);
}
-
/*
* NIF execution wrapper used by enif_schedule_nif() for regular NIFs. It
* calls the actual NIF, restores original NIF MFA if necessary, and
@@ -2998,12 +3093,12 @@ execute_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
Process* proc;
NativeFunPtr fp;
- NifExport* ep;
+ ErtsNativeFunc* ep;
ERL_NIF_TERM result;
execution_state(env, &proc, NULL);
- ep = ErtsContainerStruct(proc->current, NifExport, exp.info.mfa);
+ ep = ErtsContainerStruct(proc->current, ErtsNativeFunc, trampoline.info.mfa);
fp = ep->func;
ASSERT(ep);
ASSERT(!env->exception_thrown);
@@ -3016,20 +3111,20 @@ execute_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
result = (*fp)(env, argc, argv);
- ASSERT(ep == ERTS_PROC_GET_NIF_TRAP_EXPORT(proc));
+ ASSERT(ep == ERTS_PROC_GET_NFUNC_TRAP_WRAPPER(proc));
if (is_value(result) || proc->freason != TRAP) {
/* Done (not rescheduled)... */
ASSERT(ep->func == ERTS_DBG_NIF_NOT_SCHED_MARKER);
if (!env->exception_thrown)
- nif_export_restore(proc, ep, result);
+ nfunc_restore(proc, ep, result);
else {
- nif_export_cleanup_nif_mod(ep);
+ nfunc_cleanup_nif_mod(ep);
/*
* Restore orig info for error and clear nif
* export in handle_error()
*/
- proc->freason |= EXF_RESTORE_NIF;
+ proc->freason |= EXF_RESTORE_NFUNC;
}
}
@@ -3439,14 +3534,14 @@ int enif_monitor_process(ErlNifEnv* env, void* obj, const ErlNifPid* target_pid,
ASSERT(ERTS_MAGIC_BIN_FROM_UNALIGNED_DATA(rsrc)->magic_binary.destructor
== NIF_RESOURCE_DTOR);
ASSERT(erts_refc_read(&ERTS_MAGIC_BIN_FROM_UNALIGNED_DATA(rsrc)->binary.intern.refc, 0) != 0);
- ASSERT(!rsrc->monitors == !rsrc->type->down);
+ ASSERT(!rsrc->monitors == !rsrc->type->fn.down);
rm = rsrc->monitors;
if (!rm) {
- ASSERT(!rsrc->type->down);
+ ASSERT(!rsrc->type->fn.down);
return -1;
}
- ASSERT(rsrc->type->down);
+ ASSERT(rsrc->type->fn.down);
if (target_pid->pid == am_undefined)
return 1;
@@ -3996,7 +4091,7 @@ void erts_add_taint(Eterm mod_atom)
struct tainted_module_t *first, *t;
ERTS_LC_ASSERT(erts_lc_rwmtx_is_rwlocked(&erts_driver_list_lock)
- || erts_thr_progress_is_blocking());
+ || erts_has_code_write_permission());
first = (struct tainted_module_t*) erts_atomic_read_nob(&first_taint);
for (t=first ; t; t=t->next) {
@@ -4008,7 +4103,7 @@ void erts_add_taint(Eterm mod_atom)
if (t != NULL) {
t->module_atom = mod_atom;
t->next = first;
- erts_atomic_set_nob(&first_taint, (erts_aint_t)t);
+ erts_atomic_set_relb(&first_taint, (erts_aint_t)t);
}
}
@@ -4019,7 +4114,7 @@ Eterm erts_nif_taints(Process* p)
Eterm list = NIL;
Eterm* hp;
- first = (struct tainted_module_t*) erts_atomic_read_nob(&first_taint);
+ first = (struct tainted_module_t*) erts_atomic_read_acqb(&first_taint);
for (t=first ; t!=NULL; t=t->next) {
cnt++;
}
@@ -4091,6 +4186,8 @@ static struct erl_module_nif* create_lib(const ErlNifEntry* src)
bytes += src->num_of_funcs * sizeof(ErlNifFunc);
lib = erts_alloc(ERTS_ALC_T_NIF, bytes);
+ erts_mtx_init(&lib->load_mtx, "nif_load", NIL,
+ ERTS_LOCK_FLAGS_CATEGORY_GENERIC);
dst = &lib->entry;
sys_memcpy(dst, src, offsetof(ErlNifEntry, vm_variant));
@@ -4132,8 +4229,89 @@ static struct erl_module_nif* create_lib(const ErlNifEntry* src)
return lib;
};
+/* load_nif/2 is implemented as an instruction as it needs to know where it
+ * was called from, and it's a pain to get that information in a BIF.
+ *
+ * This is a small stub that rejects apply(erlang, load_nif, [Path, Args]). */
+BIF_RETTYPE load_nif_2(BIF_ALIST_2) {
+ if (BIF_P->flags & F_HIPE_MODE) {
+ BIF_RET(load_nif_error(BIF_P, "notsup",
+ "Calling load_nif from HiPE compiled modules "
+ "not supported"));
+ }
+
+ BIF_RET(load_nif_error(BIF_P, "bad_lib",
+ "load_nif/2 must be explicitly called from the NIF "
+ "module. It cannot be called through apply/3."));
+}
+
+typedef struct {
+ HashBucket bucket;
+ ErtsCodeInfo* code_info;
+ ErtsCodeMFA mfa;
+ BeamInstr beam[4];
+} ErtsNifBeamStub;
+
+typedef struct ErtsNifFinish_ {
+ int nstubs_hashed; /* can be less than 'num_of_funcs' if load failed */
+ ErtsNifBeamStub beam_stubv[1];
+} ErtsNifFinish;
+
+#define sizeof_ErtsNifFinish(N) \
+ (offsetof(ErtsNifFinish, beam_stubv) + (N)*sizeof(ErtsNifBeamStub))
+
+static void load_nif_1st_finisher(void* vlib);
+static void load_nif_2nd_finisher(void* vlib);
+static void load_nif_3rd_finisher(void* vlib);
+static void release_beam_stubs(struct erl_module_nif* lib);
+static void erase_hashed_stubs(ErtsNifFinish*);
+
+struct hash erts_nif_call_tab;
+
+static HashValue nif_call_hash(ErtsNifBeamStub* obj)
+{
+ return ((HashValue)obj->code_info / sizeof(BeamInstr));
+}
+
+static int nif_call_cmp(ErtsNifBeamStub* tmpl, ErtsNifBeamStub* obj)
+{
+ return tmpl->code_info != obj->code_info;
+}
+
+static ErtsNifBeamStub* nif_call_alloc(ErtsNifBeamStub* tmpl)
+{
+ return tmpl;
+}
+
+static void nif_call_free(ErtsNifBeamStub* obj)
+{
+}
+
+static void nif_call_table_init(void)
+{
+ HashFunctions f;
+
+ erts_rwmtx_opt_t rwmtx_opt = ERTS_RWMTX_OPT_DEFAULT_INITER;
+ rwmtx_opt.lived = ERTS_RWMTX_LONG_LIVED;
+
+ erts_rwmtx_init_opt(&erts_nif_call_tab_lock, &rwmtx_opt, "nif_call_tab",
+ NIL, (ERTS_LOCK_FLAGS_PROPERTY_STATIC |
+ ERTS_LOCK_FLAGS_CATEGORY_GENERIC));
+
+ f.hash = (H_FUN) nif_call_hash;
+ f.cmp = (HCMP_FUN) nif_call_cmp;
+ f.alloc = (HALLOC_FUN) nif_call_alloc;
+ f.free = (HFREE_FUN) nif_call_free;
+ f.meta_alloc = (HMALLOC_FUN) erts_alloc;
+ f.meta_free = (HMFREE_FUN) erts_free;
+ f.meta_print = (HMPRINT_FUN) erts_print;
+
+ hash_init(ERTS_ALC_T_NIF, &erts_nif_call_tab, "nif_call_tab", 100, f);
+}
+
+static void patch_call_nif_early(ErlNifEntry*, struct erl_module_instance*);
-BIF_RETTYPE load_nif_2(BIF_ALIST_2)
+Eterm erts_load_nif(Process *c_p, BeamInstr *I, Eterm filename, Eterm args)
{
static const char bad_lib[] = "bad_lib";
static const char upgrade[] = "upgrade";
@@ -4156,42 +4334,21 @@ BIF_RETTYPE load_nif_2(BIF_ALIST_2)
struct erl_module_instance* this_mi;
struct erl_module_instance* prev_mi;
- if (BIF_P->flags & F_HIPE_MODE) {
- ret = load_nif_error(BIF_P, "notsup", "Calling load_nif from HiPE compiled "
- "modules not supported");
- BIF_RET(ret);
- }
-
encoding = erts_get_native_filename_encoding();
if (encoding == ERL_FILENAME_WIN_WCHAR) {
/* Do not convert the lib name to utf-16le yet, do that in win32 specific code */
/* since lib_name is used in error messages */
encoding = ERL_FILENAME_UTF8;
}
- lib_name = erts_convert_filename_to_encoding(BIF_ARG_1, NULL, 0,
+ lib_name = erts_convert_filename_to_encoding(filename, NULL, 0,
ERTS_ALC_T_TMP, 1, 0, encoding,
NULL, 0);
if (!lib_name) {
- BIF_ERROR(BIF_P, BADARG);
- }
-
- if (!erts_try_seize_code_write_permission(BIF_P)) {
- erts_free(ERTS_ALC_T_TMP, lib_name);
- ERTS_BIF_YIELD2(bif_export[BIF_load_nif_2],
- BIF_P, BIF_ARG_1, BIF_ARG_2);
+ return THE_NON_VALUE;
}
- /* Block system (is this the right place to do it?) */
- erts_proc_unlock(BIF_P, ERTS_PROC_LOCK_MAIN);
- erts_thr_progress_block();
- erts_proc_lock(BIF_P, ERTS_PROC_LOCK_MAIN);
-
/* Find calling module */
- ASSERT(BIF_P->current != NULL);
- ASSERT(BIF_P->current->module == am_erlang
- && BIF_P->current->function == am_load_nif
- && BIF_P->current->arity == 2);
- caller = find_function_from_pc(BIF_P->cp);
+ caller = find_function_from_pc(I);
ASSERT(caller != NULL);
mod_atom = caller->module;
ASSERT(is_atom(mod_atom));
@@ -4211,7 +4368,7 @@ BIF_RETTYPE load_nif_2(BIF_ALIST_2)
this_mi = &module_p->curr;
prev_mi = &module_p->old;
if (in_area(caller, module_p->old.code_hdr, module_p->old.code_length)) {
- ret = load_nif_error(BIF_P, "old_code", "Calling load_nif from old "
+ ret = load_nif_error(c_p, "old_code", "Calling load_nif from old "
"module '%T' not allowed", mod_atom);
goto error;
} else if (module_p->on_load) {
@@ -4225,52 +4382,52 @@ BIF_RETTYPE load_nif_2(BIF_ALIST_2)
}
if (this_mi->nif != NULL) {
- ret = load_nif_error(BIF_P,"reload","NIF library already loaded"
+ ret = load_nif_error(c_p,"reload","NIF library already loaded"
" (reload disallowed since OTP 20).");
}
else if (init_func == NULL &&
(err=erts_sys_ddll_open(lib_name, &handle, &errdesc)) != ERL_DE_NO_ERROR) {
const char slogan[] = "Failed to load NIF library";
if (strstr(errdesc.str, lib_name) != NULL) {
- ret = load_nif_error(BIF_P, "load_failed", "%s: '%s'", slogan, errdesc.str);
+ ret = load_nif_error(c_p, "load_failed", "%s: '%s'", slogan, errdesc.str);
}
else {
- ret = load_nif_error(BIF_P, "load_failed", "%s %s: '%s'", slogan, lib_name, errdesc.str);
+ ret = load_nif_error(c_p, "load_failed", "%s %s: '%s'", slogan, lib_name, errdesc.str);
}
}
else if (init_func == NULL &&
erts_sys_ddll_load_nif_init(handle, &init_func, &errdesc) != ERL_DE_NO_ERROR) {
- ret = load_nif_error(BIF_P, bad_lib, "Failed to find library init"
+ ret = load_nif_error(c_p, bad_lib, "Failed to find library init"
" function: '%s'", errdesc.str);
}
else if ((taint ? erts_add_taint(mod_atom) : 0,
(entry = erts_sys_ddll_call_nif_init(init_func)) == NULL)) {
- ret = load_nif_error(BIF_P, bad_lib, "Library init-call unsuccessful");
+ ret = load_nif_error(c_p, bad_lib, "Library init-call unsuccessful");
}
else if (entry->major > ERL_NIF_MAJOR_VERSION
|| (entry->major == ERL_NIF_MAJOR_VERSION
&& entry->minor > ERL_NIF_MINOR_VERSION)) {
char* fmt = "That '%T' NIF library needs %s or newer. Either try to"
" recompile the NIF lib or use a newer erts runtime.";
- ret = load_nif_error(BIF_P, bad_lib, fmt, mod_atom, entry->min_erts);
+ ret = load_nif_error(c_p, bad_lib, fmt, mod_atom, entry->min_erts);
}
else if (entry->major < ERL_NIF_MIN_REQUIRED_MAJOR_VERSION_ON_LOAD
|| (entry->major==2 && entry->minor == 5)) { /* experimental maps */
char* fmt = "That old NIF library (%d.%d) is not compatible with this "
"erts runtime (%d.%d). Try recompile the NIF lib.";
- ret = load_nif_error(BIF_P, bad_lib, fmt, entry->major, entry->minor,
+ ret = load_nif_error(c_p, bad_lib, fmt, entry->major, entry->minor,
ERL_NIF_MAJOR_VERSION, ERL_NIF_MINOR_VERSION);
}
else if (AT_LEAST_VERSION(entry, 2, 1)
&& sys_strcmp(entry->vm_variant, ERL_NIF_VM_VARIANT) != 0) {
- ret = load_nif_error(BIF_P, bad_lib, "Library (%s) not compiled for "
+ ret = load_nif_error(c_p, bad_lib, "Library (%s) not compiled for "
"this vm variant (%s).",
entry->vm_variant, ERL_NIF_VM_VARIANT);
}
else if (!erts_is_atom_str((char*)entry->name, mod_atom, 1)) {
- ret = load_nif_error(BIF_P, bad_lib, "Library module name '%s' does not"
+ ret = load_nif_error(c_p, bad_lib, "Library module name '%s' does not"
" match calling module '%T'", entry->name, mod_atom);
}
else {
@@ -4278,37 +4435,66 @@ BIF_RETTYPE load_nif_2(BIF_ALIST_2)
entry = &lib->entry; /* Use a guaranteed modern lib entry from now on */
lib->handle = handle;
- erts_refc_init(&lib->rt_cnt, 0);
- erts_refc_init(&lib->rt_dtor_cnt, 0);
+ erts_refc_init(&lib->refc, 2); /* Erlang code + NIF code */
+ erts_refc_init(&lib->dynlib_refc, 1);
ASSERT(opened_rt_list == NULL);
lib->mod = module_p;
- for (i=0; i < entry->num_of_funcs && ret==am_ok; i++) {
+ lib->finish = erts_alloc(ERTS_ALC_T_NIF,
+ sizeof_ErtsNifFinish(entry->num_of_funcs));
+ lib->finish->nstubs_hashed = 0;
+
+ erts_rwmtx_rwlock(&erts_nif_call_tab_lock);
+ for (i=0; i < entry->num_of_funcs; i++) {
ErtsCodeInfo** ci_pp;
+ ErtsCodeInfo* ci;
ErlNifFunc* f = &entry->funcs[i];
+ ErtsNifBeamStub* stub = &lib->finish->beam_stubv[i];
+
+ stub->code_info = NULL; /* end marker in case we fail */
if (!erts_atom_get(f->name, sys_strlen(f->name), &f_atom, ERTS_ATOM_ENC_LATIN1)
|| (ci_pp = get_func_pp(this_mi->code_hdr, f_atom, f->arity))==NULL) {
- ret = load_nif_error(BIF_P,bad_lib,"Function not found %T:%s/%u",
+ ret = load_nif_error(c_p,bad_lib,"Function not found %T:%s/%u",
mod_atom, f->name, f->arity);
+ break;
}
- else if (f->flags != 0 &&
- f->flags != ERL_NIF_DIRTY_JOB_IO_BOUND &&
- f->flags != ERL_NIF_DIRTY_JOB_CPU_BOUND) {
- ret = load_nif_error(BIF_P, bad_lib,
- "Illegal flags field value %d for NIF %T:%s/%u",
+ ci = *ci_pp;
+
+ if (f->flags != 0 &&
+ f->flags != ERL_NIF_DIRTY_JOB_IO_BOUND &&
+ f->flags != ERL_NIF_DIRTY_JOB_CPU_BOUND) {
+
+ ret = load_nif_error(c_p, bad_lib, "Illegal flags field value %d for NIF %T:%s/%u",
f->flags, mod_atom, f->name, f->arity);
+ break;
}
- else if (erts_codeinfo_to_code(ci_pp[1]) - erts_codeinfo_to_code(ci_pp[0])
- < BEAM_NIF_MIN_FUNC_SZ)
- {
- ret = load_nif_error(BIF_P,bad_lib,"No explicit call to load_nif"
- " in module (%T:%s/%u too small)",
- mod_atom, f->name, f->arity);
- }
- /*erts_fprintf(stderr, "Found NIF %T:%s/%u\r\n",
- mod_atom, f->name, f->arity);*/
+
+ ASSERT(erts_codeinfo_to_code(ci_pp[1]) - erts_codeinfo_to_code(ci_pp[0])
+ >= BEAM_NATIVE_MIN_FUNC_SZ);
+
+ stub->code_info = ci;
+ stub->mfa = ci->mfa;
+ if (hash_put(&erts_nif_call_tab, stub) != stub) {
+ ret = load_nif_error(c_p, bad_lib, "Duplicate NIF entry for %T:%s/%u",
+ mod_atom, f->name, f->arity);
+ break;
+ }
+ lib->finish->nstubs_hashed++;
+
+ stub->beam[0] = BeamOpCodeAddr(op_call_nif_WWW);
+ stub->beam[2] = (BeamInstr) lib;
+ if (f->flags) {
+ stub->beam[3] = (BeamInstr) f->fptr;
+ stub->beam[1] = (f->flags == ERL_NIF_DIRTY_JOB_IO_BOUND) ?
+ (BeamInstr) static_schedule_dirty_io_nif :
+ (BeamInstr) static_schedule_dirty_cpu_nif;
+ }
+ else
+ stub->beam[1] = (BeamInstr) f->fptr;
}
+ erts_rwmtx_rwunlock(&erts_nif_call_tab_lock);
+ ASSERT(lib->finish->nstubs_hashed == lib->entry.num_of_funcs);
}
if (ret != am_ok) {
@@ -4324,67 +4510,71 @@ BIF_RETTYPE load_nif_2(BIF_ALIST_2)
if (prev_mi->nif != NULL) { /**************** Upgrade ***************/
void* prev_old_data = prev_mi->nif->priv_data;
if (entry->upgrade == NULL) {
- ret = load_nif_error(BIF_P, upgrade, "Upgrade not supported by this NIF library.");
+ ret = load_nif_error(c_p, upgrade, "Upgrade not supported by this NIF library.");
goto error;
}
- erts_pre_nif(&env, BIF_P, lib, NULL);
- veto = entry->upgrade(&env, &lib->priv_data, &prev_mi->nif->priv_data, BIF_ARG_2);
+ /*
+ * Go single scheduler during upgrade callback.
+ * Todo: Fix better solution with suspending callers and waiting for
+ * all calls to return (including dirty).
+ * Note that erts_thr_progress_block() will not block dirty NIFs.
+ */
+ erts_proc_unlock(c_p, ERTS_PROC_LOCK_MAIN);
+ erts_thr_progress_block();
+ erts_proc_lock(c_p, ERTS_PROC_LOCK_MAIN);
+ erts_pre_nif(&env, c_p, lib, NULL);
+ veto = entry->upgrade(&env, &lib->priv_data, &prev_mi->nif->priv_data, args);
erts_post_nif(&env);
+ erts_thr_progress_unblock();
if (veto) {
prev_mi->nif->priv_data = prev_old_data;
- ret = load_nif_error(BIF_P, upgrade, "Library upgrade-call unsuccessful (%d).", veto);
+ ret = load_nif_error(c_p, upgrade, "Library upgrade-call unsuccessful (%d).", veto);
}
}
else if (entry->load != NULL) { /********* Initial load ***********/
- erts_pre_nif(&env, BIF_P, lib, NULL);
- veto = entry->load(&env, &lib->priv_data, BIF_ARG_2);
+ erts_pre_nif(&env, c_p, lib, NULL);
+ veto = entry->load(&env, &lib->priv_data, args);
erts_post_nif(&env);
if (veto) {
- ret = load_nif_error(BIF_P, "load", "Library load-call unsuccessful (%d).", veto);
+ ret = load_nif_error(c_p, "load", "Library load-call unsuccessful (%d).", veto);
}
}
- if (ret == am_ok) {
- commit_opened_resource_types(lib);
+ if (ret == am_ok) {
/*
- ** Everything ok, patch the beam code with op_call_nif
- */
-
+ * Everything ok, make NIF code callable.
+ */
this_mi->nif = lib;
- for (i=0; i < entry->num_of_funcs; i++)
- {
- ErlNifFunc* f = &entry->funcs[i];
- ErtsCodeInfo* ci;
- BeamInstr *code_ptr;
+ prepare_opened_rt(lib);
+ /*
+ * The call table lock will make sure all NIFs and callbacks in module
+ * are made accessible atomically.
+ */
+ erts_rwmtx_rwlock(&erts_nif_call_tab_lock);
+ commit_opened_rt();
+ patch_call_nif_early(entry, this_mi);
+ erts_rwmtx_rwunlock(&erts_nif_call_tab_lock);
- erts_atom_get(f->name, sys_strlen(f->name), &f_atom, ERTS_ATOM_ENC_LATIN1);
- ci = *get_func_pp(this_mi->code_hdr, f_atom, f->arity);
- code_ptr = erts_codeinfo_to_code(ci);
+ cleanup_opened_rt();
- if (ci->u.gen_bp == NULL) {
- code_ptr[0] = BeamOpCodeAddr(op_call_nif);
- }
- else { /* Function traced, patch the original instruction word */
- GenericBp* g = ci->u.gen_bp;
- ASSERT(BeamIsOpCode(code_ptr[0], op_i_generic_breakpoint));
- g->orig_instr = BeamOpCodeAddr(op_call_nif);
- }
- if (f->flags) {
- code_ptr[3] = (BeamInstr) f->fptr;
- code_ptr[1] = (f->flags == ERL_NIF_DIRTY_JOB_IO_BOUND) ?
- (BeamInstr) static_schedule_dirty_io_nif :
- (BeamInstr) static_schedule_dirty_cpu_nif;
- }
- else
- code_ptr[1] = (BeamInstr) f->fptr;
- code_ptr[2] = (BeamInstr) lib;
- }
+ /*
+ * Now we wait thread progress, to make sure no process is still
+ * executing the beam code of the NIFs, before we can patch in the
+ * final fast multi word call_nif_WWW instructions.
+ */
+ erts_refc_inc(&lib->refc, 2);
+ erts_schedule_thr_prgr_later_op(load_nif_1st_finisher, lib,
+ &lib->lop);
}
else {
error:
rollback_opened_resource_types();
ASSERT(ret != am_ok);
if (lib != NULL) {
+ if (lib->finish != NULL) {
+ erase_hashed_stubs(lib->finish);
+ erts_free(ERTS_ALC_T_NIF, lib->finish);
+ }
erts_free(ERTS_ALC_T_NIF, lib);
}
if (handle != NULL && !erts_is_static_nif(handle)) {
@@ -4393,25 +4583,208 @@ BIF_RETTYPE load_nif_2(BIF_ALIST_2)
erts_sys_ddll_free_error(&errdesc);
}
- erts_thr_progress_unblock();
- erts_release_code_write_permission();
erts_free(ERTS_ALC_T_TMP, lib_name);
BIF_RET(ret);
}
+/*
+ * Write 'call_nif_early' as the first beam instruction for all NIFs
+ * which will make them callable.
+ *
+ * The 'call_nif_early' is a one word beam instruction which uses a lock
+ * protected hash lookup to get its "arguments". This guarantees an atomically
+ * safe publication of all NIFs in the module.
+ */
+static void patch_call_nif_early(ErlNifEntry* entry,
+ struct erl_module_instance* this_mi)
+{
+ int i;
+
+ ERTS_LC_ASSERT(erts_has_code_write_permission());
+ ERTS_LC_ASSERT(erts_lc_rwmtx_is_rwlocked(&erts_nif_call_tab_lock));
+
+ for (i=0; i < entry->num_of_funcs; i++)
+ {
+ ErlNifFunc* f = &entry->funcs[i];
+ BeamInstr volatile *code_ptr;
+ ErtsCodeInfo* ci;
+ Eterm f_atom;
+
+ erts_atom_get(f->name, sys_strlen(f->name), &f_atom, ERTS_ATOM_ENC_LATIN1);
+ ci = *get_func_pp(this_mi->code_hdr, f_atom, f->arity);
+ code_ptr = erts_codeinfo_to_code(ci);
+
+ if (ci->u.gen_bp) {
+ /*
+ * Function traced, patch the original instruction word
+ * Code write permission protects against racing breakpoint writes.
+ */
+ GenericBp* g = ci->u.gen_bp;
+ g->orig_instr = BeamOpCodeAddr(op_call_nif_early);
+ if (BeamIsOpCode(code_ptr[0], op_i_generic_breakpoint))
+ continue;
+ }
+ else
+ ASSERT(!BeamIsOpCode(code_ptr[0], op_i_generic_breakpoint));
+ code_ptr[0] = BeamOpCodeAddr(op_call_nif_early);
+ }
+}
+
+BeamInstr* erts_call_nif_early(Process* c_p, ErtsCodeInfo* ci)
+{
+ ErtsNifBeamStub* bs;
+ ErtsNifBeamStub tmpl;
+ tmpl.code_info = ci;
+
+ erts_rwmtx_rlock(&erts_nif_call_tab_lock);
+ bs = (ErtsNifBeamStub*) hash_get(&erts_nif_call_tab, &tmpl);
+ erts_rwmtx_runlock(&erts_nif_call_tab_lock);
+
+ ASSERT(bs);
+ return &bs->beam[0];
+}
+
+static void load_nif_1st_finisher(void* vlib)
+{
+ struct erl_module_nif* lib = (struct erl_module_nif*) vlib;
+ ErtsNifFinish* fin;
+ int i;
+
+ erts_mtx_lock(&lib->load_mtx);
+ fin = lib->finish;
+ if (fin) {
+ for (i=0; i < lib->entry.num_of_funcs; i++) {
+ BeamInstr* code_ptr;
+ code_ptr = erts_codeinfo_to_code(fin->beam_stubv[i].code_info);
+
+ code_ptr[1] = fin->beam_stubv[i].beam[1]; /* called function */
+ code_ptr[2] = fin->beam_stubv[i].beam[2]; /* erl_module_nif */
+ if (lib->entry.funcs[i].flags)
+ code_ptr[3] = fin->beam_stubv[i].beam[3]; /* real NIF */
+ }
+ }
+ erts_mtx_unlock(&lib->load_mtx);
+
+ if (fin) {
+ /*
+ * A second thread progress to get a memory barrier between the
+ * arguments of call_nif_WWW (written above) and the instruction word
+ * itself.
+ */
+ erts_schedule_thr_prgr_later_op(load_nif_2nd_finisher, lib,
+ &lib->lop);
+ }
+ else { /* Unloaded */
+ deref_nifmod(lib);
+ }
+}
+
+static void load_nif_2nd_finisher(void* vlib)
+{
+ struct erl_module_nif* lib = (struct erl_module_nif*) vlib;
+ ErtsNifFinish* fin;
+ int i;
+
+ /*
+ * We seize code write permission only to avoid any trace breakpoints
+ * to change while we patch the op_call_nif_WWW instruction.
+ */
+ if (!erts_try_seize_code_write_permission_aux(load_nif_2nd_finisher, vlib)) {
+ return;
+ }
+
+ erts_mtx_lock(&lib->load_mtx);
+ fin = lib->finish;
+ if (fin) {
+ for (i=0; i < lib->entry.num_of_funcs; i++) {
+ ErtsCodeInfo *ci = fin->beam_stubv[i].code_info;
+ BeamInstr volatile *code_ptr = erts_codeinfo_to_code(ci);
+
+ if (ci->u.gen_bp) {
+ /*
+ * Function traced, patch the original instruction word
+ */
+ GenericBp* g = ci->u.gen_bp;
+ ASSERT(g->orig_instr == BeamOpCodeAddr(op_call_nif_early));
+ g->orig_instr = BeamOpCodeAddr(op_call_nif_WWW);
+ if (BeamIsOpCode(code_ptr[0], op_i_generic_breakpoint))
+ continue;
+ }
+ ASSERT(code_ptr[0] == BeamOpCodeAddr(op_call_nif_early));
+ code_ptr[0] = BeamOpCodeAddr(op_call_nif_WWW);
+ }
+ }
+ erts_mtx_unlock(&lib->load_mtx);
+
+ erts_release_code_write_permission();
+
+ if (fin) {
+ UWord bytes = sizeof_ErtsNifFinish(lib->entry.num_of_funcs);
+ /*
+ * A third and final thread progress, to make sure no one is executing
+ * the call_nif_early instructions anymore, before we can deallocate
+ * the beam stubs.
+ */
+ erts_schedule_thr_prgr_later_cleanup_op(load_nif_3rd_finisher, lib,
+ &lib->lop,
+ bytes);
+ }
+ else { /* Unloaded */
+ deref_nifmod(lib);
+ }
+}
+
+static void load_nif_3rd_finisher(void* vlib)
+{
+ struct erl_module_nif* lib = (struct erl_module_nif*) vlib;
+
+ release_beam_stubs(lib);
+ deref_nifmod(lib);
+}
+
+static void release_beam_stubs(struct erl_module_nif* lib)
+{
+ ErtsNifFinish* fin;
+
+ erts_mtx_lock(&lib->load_mtx);
+ fin = lib->finish;
+ lib->finish = NULL;
+ erts_mtx_unlock(&lib->load_mtx);
+
+ if (fin) {
+ erase_hashed_stubs(fin);
+ erts_free(ERTS_ALC_T_NIF, fin);
+ }
+}
+
+static void erase_hashed_stubs(ErtsNifFinish* fin)
+{
+ int i;
+
+ erts_rwmtx_rwlock(&erts_nif_call_tab_lock);
+ for (i=0; i < fin->nstubs_hashed; i++) {
+ void* erased = hash_erase(&erts_nif_call_tab, &fin->beam_stubv[i]);
+ ASSERT(erased); (void) erased;
+ }
+ erts_rwmtx_rwunlock(&erts_nif_call_tab_lock);
+}
+
void
erts_unload_nif(struct erl_module_nif* lib)
{
ErlNifResourceType* rt;
ErlNifResourceType* next;
- ASSERT(erts_thr_progress_is_blocking());
+
ASSERT(lib != NULL);
ASSERT(lib->mod != NULL);
+ ERTS_LC_ASSERT(erts_has_code_write_permission());
erts_tracer_nif_clear();
+ release_beam_stubs(lib);
+
for (rt = resource_type_list.next;
rt != &resource_type_list;
rt = next) {
@@ -4423,25 +4796,19 @@ erts_unload_nif(struct erl_module_nif* lib)
rt->next = NULL;
rt->prev = NULL;
if (erts_refc_dectest(&rt->refc, 0) == 0) {
- if (rt_have_callbacks(rt)) {
- erts_refc_dec(&lib->rt_dtor_cnt, 0);
- }
- erts_refc_dec(&lib->rt_cnt, 0);
+ if (rt_have_callbacks(&rt->fn_real))
+ erts_refc_dec(&lib->dynlib_refc, 1);
+ erts_refc_dec(&lib->refc, 1);
erts_free(ERTS_ALC_T_NIF, rt);
}
}
}
- if (erts_refc_read(&lib->rt_dtor_cnt, 0) == 0) {
- close_lib(lib);
- if (erts_refc_read(&lib->rt_cnt, 0) == 0) {
- erts_free(ERTS_ALC_T_NIF, lib);
- return;
- }
- }
- else {
- ASSERT(erts_refc_read(&lib->rt_cnt, 1) > 0);
- }
- lib->mod = NULL; /* orphan lib */
+ lib->mod = NULL; /* purged Elang module */
+
+ if (erts_refc_dectest(&lib->dynlib_refc, 0) == 0)
+ close_dynlib(lib);
+
+ deref_nifmod(lib);
}
void erl_nif_init()
@@ -4451,11 +4818,13 @@ void erl_nif_init()
resource_type_list.next = &resource_type_list;
resource_type_list.prev = &resource_type_list;
- resource_type_list.dtor = NULL;
+ resource_type_list.fn.dtor = NULL;
+ resource_type_list.fn_real.dtor = NULL;
resource_type_list.owner = NULL;
resource_type_list.module = THE_NON_VALUE;
resource_type_list.name = THE_NON_VALUE;
+ nif_call_table_init();
}
int erts_nif_get_funcs(struct erl_module_nif* mod,
diff --git a/erts/emulator/beam/erl_node_tables.c b/erts/emulator/beam/erl_node_tables.c
index 51e6a4dc40..abf833f318 100644
--- a/erts/emulator/beam/erl_node_tables.c
+++ b/erts/emulator/beam/erl_node_tables.c
@@ -176,9 +176,10 @@ dist_table_alloc(void *dep_tmpl)
erts_atomic_init_nob(&dep->input_handler, (erts_aint_t) NIL);
dep->connection_id = 0;
dep->state = ERTS_DE_STATE_IDLE;
- dep->flags = 0;
+ dep->pending_nodedown = 0;
+ dep->suspended_nodeup = NULL;
+ dep->dflags = 0;
dep->opts = 0;
- dep->version = 0;
dep->mld = NULL;
@@ -201,7 +202,6 @@ dist_table_alloc(void *dep_tmpl)
erts_port_task_handle_init(&dep->dist_cmd);
dep->send = NULL;
dep->cache = NULL;
- dep->transcode_ctx = NULL;
dep->sequences = NULL;
/* Link in */
@@ -634,7 +634,7 @@ erts_set_dist_entry_not_connected(DistEntry *dep)
else {
ASSERT(dep->state != ERTS_DE_STATE_IDLE);
ASSERT(is_internal_port(dep->cid) || is_internal_pid(dep->cid));
- if (dep->flags & DFLAG_PUBLISHED) {
+ if (dep->dflags & DFLAG_PUBLISHED) {
ASSERT(erts_no_of_visible_dist_entries > 0);
erts_no_of_visible_dist_entries--;
head = &erts_visible_dist_entries;
@@ -658,7 +658,7 @@ erts_set_dist_entry_not_connected(DistEntry *dep)
dep->next->prev = dep->prev;
dep->state = ERTS_DE_STATE_IDLE;
- dep->flags = 0;
+ dep->dflags = 0;
dep->opts = 0;
dep->prev = NULL;
dep->cid = NIL;
@@ -700,7 +700,7 @@ erts_set_dist_entry_pending(DistEntry *dep)
erts_no_of_not_connected_dist_entries--;
dep->state = ERTS_DE_STATE_PENDING;
- dep->flags = (DFLAG_DIST_MANDATORY | DFLAG_DIST_HOPEFULLY | DFLAG_NO_MAGIC);
+ dep->dflags = (DFLAG_DIST_MANDATORY | DFLAG_DIST_HOPEFULLY | DFLAG_PENDING_CONNECT);
dep->connection_id = (dep->connection_id + 1) & ERTS_DIST_CON_ID_MASK;
ASSERT(!dep->mld);
@@ -719,7 +719,7 @@ erts_set_dist_entry_pending(DistEntry *dep)
}
void
-erts_set_dist_entry_connected(DistEntry *dep, Eterm cid, Uint flags)
+erts_set_dist_entry_connected(DistEntry *dep, Eterm cid, Uint64 flags)
{
erts_aint32_t set_qflgs;
@@ -731,6 +731,7 @@ erts_set_dist_entry_connected(DistEntry *dep, Eterm cid, Uint flags)
ASSERT(dep != erts_this_dist_entry);
ASSERT(is_nil(dep->cid));
ASSERT(dep->state == ERTS_DE_STATE_PENDING);
+ ASSERT(!dep->pending_nodedown);
ASSERT(is_internal_port(cid) || is_internal_pid(cid));
if(dep->prev) {
@@ -749,7 +750,7 @@ erts_set_dist_entry_connected(DistEntry *dep, Eterm cid, Uint flags)
erts_no_of_pending_dist_entries--;
dep->state = ERTS_DE_STATE_CONNECTED;
- dep->flags = flags & ~DFLAG_NO_MAGIC;
+ dep->dflags = flags & ~DFLAG_PENDING_CONNECT;
dep->cid = cid;
erts_atomic_set_nob(&dep->input_handler,
(erts_aint_t) cid);
@@ -976,7 +977,7 @@ static void print_node(void *venp, void *vpndp)
if(pndp->sysname == NIL) {
erts_print(pndp->to, pndp->to_arg, "Name: %T ", enp->sysname);
}
- erts_print(pndp->to, pndp->to_arg, " %d", enp->creation);
+ erts_print(pndp->to, pndp->to_arg, " %u", enp->creation);
#ifdef DEBUG
erts_print(pndp->to, pndp->to_arg, " (refc=%ld)",
erts_refc_read(&enp->refc, 0));
@@ -1019,7 +1020,7 @@ void erts_print_node_info(fmtfn_t to,
/* ----------------------------------------------------------------------- */
void
-erts_set_this_node(Eterm sysname, Uint creation)
+erts_set_this_node(Eterm sysname, Uint32 creation)
{
ERTS_LC_ASSERT(erts_thr_progress_is_blocking());
ASSERT(2 <= de_refc_read(erts_this_dist_entry, 2));
diff --git a/erts/emulator/beam/erl_node_tables.h b/erts/emulator/beam/erl_node_tables.h
index ffaafbbbea..f426f46d53 100644
--- a/erts/emulator/beam/erl_node_tables.h
+++ b/erts/emulator/beam/erl_node_tables.h
@@ -23,6 +23,7 @@
typedef struct dist_entry_ DistEntry;
typedef struct ErtsDistOutputBuf_ ErtsDistOutputBuf;
+typedef struct ErtsDistOutputBufsContainer_ ErtsDistOutputBufsContainer;
void erts_ref_dist_entry(DistEntry *dep);
void erts_deref_dist_entry(DistEntry *dep);
@@ -95,26 +96,21 @@ enum dist_entry_state {
struct ErtsDistOutputBuf_ {
#ifdef DEBUG
Uint dbg_pattern;
- byte *ext_startp;
- byte *alloc_endp;
#endif
ErtsDistOutputBuf *next;
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;
- /* 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;
+ /*
+ * iov[0] reserved for driver
+ * iov[1] reserved for distribution header
+ * iov[2 ... vsize-1] data
+ */
+ ErlIOVec *eiov;
+};
+struct ErtsDistOutputBufsContainer_ {
+ Sint fragments;
+ byte *extp;
+ ErtsDistOutputBuf obuf[1]; /* longer if fragmented... */
};
typedef struct {
@@ -147,10 +143,11 @@ struct dist_entry_ {
NIL == free */
Uint32 connection_id; /* Connection id incremented on connect */
enum dist_entry_state state;
- Uint32 flags; /* Distribution flags, like hidden,
+ int pending_nodedown;
+ Process* suspended_nodeup;
+ Uint64 dflags; /* Distribution flags, like hidden,
atom cache etc. */
Uint32 opts;
- unsigned long version; /* Protocol version */
ErtsMonLnkDist *mld; /* Monitors and links */
@@ -173,8 +170,6 @@ struct dist_entry_ {
ErtsThrPrgrLaterOp later_op;
- struct transcode_context* transcode_ctx;
-
struct dist_sequences *sequences; /* Ongoing distribution sequences */
};
@@ -261,10 +256,10 @@ Uint erts_dist_table_size(void);
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);
+void erts_set_dist_entry_connected(DistEntry *, Eterm, Uint64);
ErlNode *erts_find_or_insert_node(Eterm, Uint32, Eterm);
void erts_schedule_delete_node(ErlNode *);
-void erts_set_this_node(Eterm, Uint);
+void erts_set_this_node(Eterm, Uint32);
Uint erts_node_table_size(void);
void erts_init_node_tables(int);
void erts_node_table_info(fmtfn_t, void *);
diff --git a/erts/emulator/beam/erl_proc_sig_queue.c b/erts/emulator/beam/erl_proc_sig_queue.c
index f8d82a8f98..efccb5fcb6 100644
--- a/erts/emulator/beam/erl_proc_sig_queue.c
+++ b/erts/emulator/beam/erl_proc_sig_queue.c
@@ -51,7 +51,7 @@
* Note that not all signal are handled using this functionality!
*/
-#define ERTS_SIG_Q_OP_MAX 13
+#define ERTS_SIG_Q_OP_MAX 14
#define ERTS_SIG_Q_OP_EXIT 0 /* Exit signal due to bif call */
#define ERTS_SIG_Q_OP_EXIT_LINKED 1 /* Exit signal due to link break*/
@@ -66,7 +66,8 @@
#define ERTS_SIG_Q_OP_IS_ALIVE 10
#define ERTS_SIG_Q_OP_PROCESS_INFO 11
#define ERTS_SIG_Q_OP_SYNC_SUSPEND 12
-#define ERTS_SIG_Q_OP_RPC ERTS_SIG_Q_OP_MAX
+#define ERTS_SIG_Q_OP_RPC 13
+#define ERTS_SIG_Q_OP_DIST_SPAWN_REPLY ERTS_SIG_Q_OP_MAX
#define ERTS_SIG_Q_TYPE_MAX (ERTS_MON_LNK_TYPE_MAX + 5)
@@ -137,6 +138,14 @@ typedef struct {
} ErtsSigDistLinkOp;
typedef struct {
+ Eterm message;
+ Eterm ref;
+ Eterm result;
+ ErtsLink *link;
+ Eterm *patch_point;
+} ErtsDistSpawnReplySigData;
+
+typedef struct {
ErtsSignalCommon common;
Uint flags_on;
Uint flags_off;
@@ -215,6 +224,8 @@ static int handle_trace_change_state(Process *c_p,
ErtsMessage ***next_nm_sig);
static void getting_unlinked(Process *c_p, Eterm unlinker);
static void getting_linked(Process *c_p, Eterm linker);
+static void linking(Process *c_p, Eterm to);
+
static void group_leader_reply(Process *c_p, Eterm to,
Eterm ref, int success);
static int stretch_limit(Process *c_p, ErtsSigRecvTracing *tp,
@@ -322,6 +333,17 @@ get_exit_signal_data(ErtsMessage *xsig)
+ xsig->hfrag.used_size);
}
+static ERTS_INLINE ErtsDistSpawnReplySigData *
+get_dist_spawn_reply_data(ErtsMessage *sig)
+{
+ ASSERT(ERTS_SIG_IS_NON_MSG(sig));
+ ASSERT(sig->hfrag.alloc_size > sig->hfrag.used_size);
+ ASSERT((sig->hfrag.alloc_size - sig->hfrag.used_size)*sizeof(UWord)
+ >= sizeof(ErtsDistSpawnReplySigData));
+ return (ErtsDistSpawnReplySigData *) (char *) (&sig->hfrag.mem[0]
+ + sig->hfrag.used_size);
+}
+
static ERTS_INLINE void
destroy_trace_info(ErtsSigTraceInfo *ti)
{
@@ -997,7 +1019,7 @@ send_gen_exit_signal(Process *c_p, Eterm from_tag,
seq_trace = c_p && have_seqtrace(token);
if (seq_trace)
- seq_trace_update_send(c_p);
+ seq_trace_update_serial(c_p);
#ifdef USE_VM_PROBES
utag_sz = 0;
@@ -1751,6 +1773,104 @@ erts_proc_sig_send_sync_suspend(Process *c_p, Eterm to, Eterm tag, Eterm reply)
}
}
+int
+erts_proc_sig_send_dist_spawn_reply(Eterm node,
+ Eterm ref,
+ Eterm to,
+ ErtsLink *lnk,
+ Eterm result,
+ Eterm token)
+{
+ Uint hsz, ref_sz, result_sz, token_sz;
+ ErtsDistSpawnReplySigData *datap;
+ Eterm msg, ref_copy, result_copy, res_type,
+ token_copy, *hp, *hp_start, *patch_point;
+ ErlHeapFragment *hfrag;
+ ErlOffHeap *ohp;
+ ErtsMessage *mp;
+
+ ASSERT(is_atom(node));
+
+ /*
+ * A respons message to a spawn_request() operation
+ * looks like this:
+ * {Tag, Ref, ok|error, Pid|ErrorAtom}
+ *
+ * Tag is stored in its own heap fragment in the
+ * (pending) monitor struct and can be attached
+ * when creating the resulting message on
+ * reception of this signal.
+ */
+
+ hsz = ref_sz = size_object(ref);
+ hsz += 5 /* 4-tuple */;
+ if (is_atom(result)) {
+ res_type = am_error;
+ result_sz = 0;
+ }
+ else {
+ ASSERT(is_external_pid(result));
+ res_type = am_ok;
+ result_sz = size_object(result);
+ hsz += result_sz;
+ }
+
+ token_sz = is_immed(token) ? 0 : size_object(token);
+ hsz += token_sz;
+
+ hsz += sizeof(ErtsDistSpawnReplySigData)/sizeof(Eterm);
+
+ mp = erts_alloc_message(hsz, &hp);
+ hp_start = hp;
+ hfrag = &mp->hfrag;
+ mp->next = NULL;
+ ohp = &hfrag->off_heap;
+
+ ref_copy = copy_struct(ref, ref_sz, &hp, ohp);
+ result_copy = (is_atom(result)
+ ? result
+ : copy_struct(result, result_sz, &hp, ohp));
+ msg = TUPLE4(hp,
+ am_undefined,
+ ref_copy,
+ res_type,
+ result_copy);
+
+ patch_point = &hp[1];
+ ASSERT(*patch_point == am_undefined);
+
+ hp += 5;
+
+ token_copy = (!token_sz
+ ? token
+ : copy_struct(token, token_sz, &hp, ohp));
+
+ hfrag->used_size = hp - hp_start;
+
+ datap = (ErtsDistSpawnReplySigData *) (char *) hp;
+ datap->message = msg;
+ datap->ref = ref_copy;
+ datap->result = result_copy;
+ datap->link = lnk;
+ datap->patch_point = patch_point;
+
+ ERL_MESSAGE_TERM(mp) = ERTS_PROC_SIG_MAKE_TAG(ERTS_SIG_Q_OP_DIST_SPAWN_REPLY,
+ ERTS_SIG_Q_TYPE_UNDEFINED,
+ 0);
+ ERL_MESSAGE_FROM(mp) = node;
+ ERL_MESSAGE_TOKEN(mp) = token_copy;
+ if (!proc_queue_signal(NULL, to, (ErtsSignal *) mp,
+ ERTS_SIG_Q_OP_DIST_SPAWN_REPLY)) {
+ mp->next = NULL;
+ mp->data.attached = ERTS_MSG_COMBINED_HFRAG;
+ ERL_MESSAGE_TERM(mp) = msg;
+ erts_cleanup_messages(mp);
+ return 0;
+ }
+
+ return !0;
+}
+
Eterm
erts_proc_sig_send_rpc_request(Process *c_p,
Eterm to,
@@ -1901,6 +2021,7 @@ is_alive_response(Process *c_p, ErtsMessage *mp, int is_alive)
}
}
+
static ERTS_INLINE void
adjust_tracing_state(Process *c_p, ErtsSigRecvTracing *tracing, int setup)
{
@@ -2303,17 +2424,14 @@ static int
convert_to_down_message(Process *c_p,
ErtsMessage *sig,
ErtsMonitorData *mdp,
+ ErtsMonitor **omon,
Uint16 mon_type,
ErtsMessage ***next_nm_sig)
{
- /*
- * Create a 'DOWN' message and replace the signal
- * with it...
- */
int cnt = 0;
Eterm node = am_undefined;
ErtsMessage *mp;
- ErtsProcLocks locks;
+ ErtsProcLocks locks = ERTS_PROC_LOCK_MAIN;
Uint hsz;
Eterm *hp, ref, from, type, reason;
ErlOffHeap *ohp;
@@ -2322,96 +2440,170 @@ convert_to_down_message(Process *c_p,
ASSERT((mdp->origin.flags & ERTS_ML_FLGS_SAME)
== (mdp->target.flags & ERTS_ML_FLGS_SAME));
- hsz = 6; /* 5-tuple */
+ /* reason is mdp->target.other.item */
+ reason = mdp->target.other.item;
+ ASSERT(is_immed(reason));
+ ASSERT(&mdp->origin == *omon);
+
+ if (mdp->origin.flags & ERTS_ML_FLG_SPAWN_PENDING) {
+ /*
+ * Create a spawn_request() error message and replace
+ * the signal with it...
+ */
+ Eterm tag;
+ ErtsMonitorDataExtended *mdep;
- if (mdp->origin.flags & ERTS_ML_FLG_NAME)
- hsz += 3; /* reg name 2-tuple */
- else {
- ASSERT(is_pid(mdp->origin.other.item)
- || is_internal_port(mdp->origin.other.item));
- hsz += NC_HEAP_SIZE(mdp->origin.other.item);
- }
+ /* Should only happen when connection breaks... */
+ ASSERT(reason == am_noconnection);
- ASSERT(is_ref(mdp->ref));
- hsz += NC_HEAP_SIZE(mdp->ref);
+ if (mdp->origin.flags & (ERTS_ML_FLG_SPAWN_ABANDONED
+ | ERTS_ML_FLG_SPAWN_NO_EMSG)) {
+ /*
+ * Operation has been been abandoned or
+ * error message has been disabled...
+ */
+ erts_monitor_release(*omon);
+ *omon = NULL;
+ return 1;
+ }
- locks = ERTS_PROC_LOCK_MAIN;
+ cnt += 4;
- /* reason is mdp->target.other.item */
- reason = mdp->target.other.item;
- ASSERT(is_immed(reason));
+ mdep = (ErtsMonitorDataExtended *) mdp;
+ hsz = 5; /* 4-tuple */
- mp = erts_alloc_message_heap(c_p, &locks, hsz, &hp, &ohp);
+ ASSERT(is_ref(mdp->ref));
+ hsz += NC_HEAP_SIZE(mdp->ref);
- if (locks != ERTS_PROC_LOCK_MAIN)
- erts_proc_unlock(c_p, locks & ~ERTS_PROC_LOCK_MAIN);
+ mp = erts_alloc_message_heap(c_p, &locks, hsz, &hp, &ohp);
- cnt += 4;
+ if (locks != ERTS_PROC_LOCK_MAIN)
+ erts_proc_unlock(c_p, locks & ~ERTS_PROC_LOCK_MAIN);
+
+ ref = STORE_NC(&hp, ohp, mdp->ref);
+
+ /*
+ * The tag to patch into the resulting message
+ * is stored in mdep->u.name via a little trick
+ * (see pending_flag in erts_monitor_create()).
+ */
+ if (is_immed(mdep->u.name))
+ tag = mdep->u.name;
+ else {
+ ErlHeapFragment *tag_hfrag;
+ tag_hfrag = (ErlHeapFragment *) cp_val(mdep->u.name);
+ tag = tag_hfrag->mem[0];
+ /* Save heap fragment of tag in message... */
+ if (mp->data.attached == ERTS_MSG_COMBINED_HFRAG) {
+ tag_hfrag->next = mp->hfrag.next;
+ mp->hfrag.next = tag_hfrag;
+ }
+ else {
+ tag_hfrag->next = mp->data.heap_frag;
+ mp->data.heap_frag = tag_hfrag;
+ }
+ }
+
+ /* Restore to normal monitor */
+ mdep->u.name = NIL;
+ mdp->origin.flags &= ~ERTS_ML_FLGS_SPAWN;
- ref = STORE_NC(&hp, ohp, mdp->ref);
+ ERL_MESSAGE_FROM(mp) = am_undefined;
+ ERL_MESSAGE_TERM(mp) = TUPLE4(hp, tag, ref, am_error, reason);
- if (!(mdp->origin.flags & ERTS_ML_FLG_NAME)) {
- from = STORE_NC(&hp, ohp, mdp->origin.other.item);
}
else {
- ErtsMonitorDataExtended *mdep;
- ASSERT(mdp->origin.flags & ERTS_ML_FLG_EXTENDED);
- mdep = (ErtsMonitorDataExtended *) mdp;
- ASSERT(is_atom(mdep->u.name));
- if (mdep->dist)
- node = mdep->dist->nodename;
- else
- node = erts_this_dist_entry->sysname;
- from = TUPLE2(hp, mdep->u.name, node);
- hp += 3;
- }
+ /*
+ * Create a 'DOWN' message and replace the signal
+ * with it...
+ */
- ASSERT(mdp->origin.type == mon_type);
- switch (mon_type) {
- case ERTS_MON_TYPE_PORT:
- type = am_port;
- if (mdp->origin.other.item == am_undefined) {
- /* failed by name... */
- ERL_MESSAGE_FROM(mp) = am_system;
- }
+ hsz = 6; /* 5-tuple */
+
+ if (mdp->origin.flags & ERTS_ML_FLG_NAME)
+ hsz += 3; /* reg name 2-tuple */
else {
- ASSERT(is_internal_port(mdp->origin.other.item));
- ERL_MESSAGE_FROM(mp) = mdp->origin.other.item;
+ ASSERT(is_pid(mdp->origin.other.item)
+ || is_internal_port(mdp->origin.other.item));
+ hsz += NC_HEAP_SIZE(mdp->origin.other.item);
}
- break;
- case ERTS_MON_TYPE_PROC:
- type = am_process;
- if (mdp->origin.other.item == am_undefined) {
- /* failed by name... */
- ERL_MESSAGE_FROM(mp) = am_system;
+
+ ASSERT(is_ref(mdp->ref));
+ hsz += NC_HEAP_SIZE(mdp->ref);
+
+ mp = erts_alloc_message_heap(c_p, &locks, hsz, &hp, &ohp);
+
+ if (locks != ERTS_PROC_LOCK_MAIN)
+ erts_proc_unlock(c_p, locks & ~ERTS_PROC_LOCK_MAIN);
+
+ cnt += 4;
+
+ ref = STORE_NC(&hp, ohp, mdp->ref);
+
+ if (!(mdp->origin.flags & ERTS_ML_FLG_NAME)) {
+ from = STORE_NC(&hp, ohp, mdp->origin.other.item);
}
else {
- ASSERT(is_internal_pid(mdp->origin.other.item));
- ERL_MESSAGE_FROM(mp) = mdp->origin.other.item;
- }
- break;
- case ERTS_MON_TYPE_DIST_PROC:
- type = am_process;
- if (node == am_undefined) {
ErtsMonitorDataExtended *mdep;
ASSERT(mdp->origin.flags & ERTS_ML_FLG_EXTENDED);
mdep = (ErtsMonitorDataExtended *) mdp;
- ASSERT(mdep->dist);
- node = mdep->dist->nodename;
+ ASSERT(is_atom(mdep->u.name));
+ if (mdep->dist)
+ node = mdep->dist->nodename;
+ else
+ node = erts_this_dist_entry->sysname;
+ from = TUPLE2(hp, mdep->u.name, node);
+ hp += 3;
+ }
+
+ ASSERT(mdp->origin.type == mon_type);
+ switch (mon_type) {
+ case ERTS_MON_TYPE_PORT:
+ type = am_port;
+ if (mdp->origin.other.item == am_undefined) {
+ /* failed by name... */
+ ERL_MESSAGE_FROM(mp) = am_system;
+ }
+ else {
+ ASSERT(is_internal_port(mdp->origin.other.item));
+ ERL_MESSAGE_FROM(mp) = mdp->origin.other.item;
+ }
+ break;
+ case ERTS_MON_TYPE_PROC:
+ type = am_process;
+ if (mdp->origin.other.item == am_undefined) {
+ /* failed by name... */
+ ERL_MESSAGE_FROM(mp) = am_system;
+ }
+ else {
+ ASSERT(is_internal_pid(mdp->origin.other.item));
+ ERL_MESSAGE_FROM(mp) = mdp->origin.other.item;
+ }
+ break;
+ case ERTS_MON_TYPE_DIST_PROC:
+ type = am_process;
+ if (node == am_undefined) {
+ ErtsMonitorDataExtended *mdep;
+ ASSERT(mdp->origin.flags & ERTS_ML_FLG_EXTENDED);
+ mdep = (ErtsMonitorDataExtended *) mdp;
+ ASSERT(mdep->dist);
+ node = mdep->dist->nodename;
+ }
+ ASSERT(is_atom(node) && node != am_undefined);
+ ERL_MESSAGE_FROM(mp) = node;
+ break;
+ default:
+ ERTS_INTERNAL_ERROR("Unexpected monitor type");
+ type = am_undefined;
+ ERL_MESSAGE_FROM(mp) = am_undefined;
+ break;
}
- ASSERT(is_atom(node) && node != am_undefined);
- ERL_MESSAGE_FROM(mp) = node;
- break;
- default:
- ERTS_INTERNAL_ERROR("Unexpected monitor type");
- type = am_undefined;
- ERL_MESSAGE_FROM(mp) = am_undefined;
- break;
- }
- ERL_MESSAGE_TERM(mp) = TUPLE5(hp, am_DOWN, ref,
- type, from, reason);
- hp += 6;
+ ERL_MESSAGE_TERM(mp) = TUPLE5(hp, am_DOWN, ref,
+ type, from, reason);
+ hp += 6;
+
+ }
ERL_MESSAGE_TOKEN(mp) = am_undefined;
/* Replace original signal with the exit message... */
@@ -3154,6 +3346,308 @@ erts_proc_sig_handle_pending_suspend(Process *c_p)
ERTS_PROC_SET_PENDING_SUSPEND(c_p, NULL);
}
+static int
+handle_dist_spawn_reply(Process *c_p, ErtsSigRecvTracing *tracing,
+ ErtsMessage *sig, ErtsMessage ***next_nm_sig)
+{
+
+ ErtsDistSpawnReplySigData *datap = get_dist_spawn_reply_data(sig);
+ ErtsMonitorDataExtended *mdep;
+ Eterm msg = datap->message;
+ Eterm result = datap->result;
+ ErtsMonitor *omon;
+ int adjust_monitor;
+ ErlHeapFragment *tag_hfrag = NULL;
+ int convert_to_message = !0;
+ int cnt = 1;
+
+ ASSERT(is_atom(result) || is_external_pid(result));
+ ASSERT(is_atom(result) || size_object(result) == EXTERNAL_THING_HEAD_SIZE + 1);
+
+ omon = erts_monitor_tree_lookup(ERTS_P_MONITORS(c_p), datap->ref);
+
+ if (!omon || !(omon->flags & ERTS_ML_FLG_SPAWN_PENDING)) {
+ /* Stale reply; remove link that was setup... */
+ ErtsLink *lnk = datap->link;
+ if (lnk) {
+ ErtsLinkData *ldp;
+ ErtsLink *dlnk = erts_link_to_other(lnk, &ldp);
+ if (erts_link_dist_delete(dlnk))
+ erts_link_release_both(ldp);
+ else
+ erts_link_release(lnk);
+ }
+ remove_nm_sig(c_p, sig, next_nm_sig);
+ sig->data.attached = ERTS_MSG_COMBINED_HFRAG;
+ ERL_MESSAGE_TERM(sig) = msg;
+ sig->next = NULL;;
+ erts_cleanup_messages(sig);
+ return ++cnt;
+ }
+
+ mdep = (ErtsMonitorDataExtended *) erts_monitor_to_data(omon);
+
+#ifdef DEBUG
+ {
+ Eterm *tp;
+ int i;
+ ASSERT(erts_monitor_is_in_table(omon));
+ ASSERT(omon->flags & ERTS_ML_FLG_SPAWN_PENDING);
+ if (is_atom(result)) {
+ ASSERT(!datap->link);
+ }
+ else {
+ ASSERT(!datap->link || (omon->flags & ERTS_ML_FLG_SPAWN_LINK));
+ ASSERT(!(omon->flags & ERTS_ML_FLG_SPAWN_LINK) || datap->link);
+ }
+ ASSERT(omon->other.item == am_pending);
+ ASSERT(is_tuple_arity(datap->message, 4));
+ tp = tuple_val(datap->message);
+ ASSERT(tp[1] == am_undefined); /* patch point */
+ ASSERT(is_internal_ref(tp[2]));
+ ASSERT((tp[3] == am_ok && is_external_pid(tp[4]))
+ || (tp[3] == am_error && is_atom(tp[4])));
+ for (i = 0; i < EXTERNAL_THING_HEAD_SIZE + 1; i++) {
+ ASSERT(is_non_value(mdep->heap[i]));
+ }
+ }
+#endif
+
+ /*
+ * The tag to patch into the resulting message
+ * is stored in mdep->u.name via a little trick
+ * (see pending_flag in erts_monitor_create()).
+ */
+ if (is_immed(mdep->u.name)) {
+ tag_hfrag = NULL;
+ *datap->patch_point = mdep->u.name;
+ }
+ else {
+ tag_hfrag = (ErlHeapFragment *) cp_val(mdep->u.name);
+ *datap->patch_point = tag_hfrag->mem[0];
+ }
+ mdep->u.name = NIL; /* Restore to normal monitor */
+
+ if (is_atom(result)) { /* Spawn error; cleanup... */
+ /* Dist code should not have created a link on failure... */
+
+ ASSERT(is_not_atom(result) || !datap->link);
+ /* delete monitor structure... */
+ adjust_monitor = 0;
+ if (omon->flags & (ERTS_ML_FLG_SPAWN_ABANDONED
+ | ERTS_ML_FLG_SPAWN_NO_EMSG))
+ convert_to_message = 0;
+ }
+ else if (omon->flags & ERTS_ML_FLG_SPAWN_ABANDONED) {
+ /*
+ * Spawn operation has been abandoned and
+ * link option was passed. Send exit signal
+ * with exit reason 'abandoned'...
+ */
+ DistEntry *dep;
+ ErtsMonLnkDist *dist;
+ ErtsMonitorDataExtended *mdep;
+ ErtsLink *lnk;
+
+ mdep = (ErtsMonitorDataExtended *) erts_monitor_to_data(omon);
+ dist = mdep->dist;
+
+ ASSERT(omon->flags & ERTS_ML_FLG_SPAWN_LINK);
+
+ lnk = datap->link;
+ if (lnk) {
+ ErtsLinkData *ldp;
+ ErtsLink *dlnk;
+ dlnk = erts_link_to_other(lnk, &ldp);
+ if (erts_link_dist_delete(dlnk))
+ erts_link_release_both(ldp);
+ else
+ erts_link_release(lnk);
+ }
+
+ ASSERT(is_external_pid(result));
+ dep = external_pid_dist_entry(result);
+
+ if (dep != erts_this_dist_entry && dist->nodename == dep->sysname) {
+ ErtsDSigSendContext ctx;
+ int 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,
+ result,
+ am_abandoned,
+ SEQ_TRACE_TOKEN(c_p));
+ ASSERT(code == ERTS_DSIG_SEND_OK);
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ /* delete monitor structure... */
+ adjust_monitor = 0;
+ /* drop message... */
+ convert_to_message = 0;
+ }
+ else {
+ /* Success... */
+ ASSERT(is_external_pid(result));
+
+ if (omon->flags & ERTS_ML_FLG_SPAWN_NO_SMSG)
+ convert_to_message = 0;
+
+ if (datap->link) {
+ cnt++;
+ erts_link_tree_insert(&ERTS_P_LINKS(c_p), datap->link);
+ if (tracing->procs)
+ linking(c_p, result);
+ }
+
+ adjust_monitor = !!(omon->flags & ERTS_ML_FLG_SPAWN_MONITOR);
+ if (adjust_monitor) {
+ /*
+ * Insert the actual pid of spawned process
+ * in origin part of monitor...
+ */
+ ErlOffHeap oh;
+ ErtsMonitorDataExtended *mdep;
+ Eterm *hp;
+ mdep = (ErtsMonitorDataExtended *) erts_monitor_to_data(omon);
+ hp = &(mdep)->heap[0];
+ omon->flags &= ~ERTS_ML_FLGS_SPAWN;
+ ERTS_INIT_OFF_HEAP(&oh);
+ oh.first = mdep->uptr.ohhp;
+ omon->other.item = copy_struct(result,
+ EXTERNAL_THING_HEAD_SIZE + 1,
+ &hp, &oh);
+ mdep->uptr.ohhp = oh.first;
+ cnt += 2;
+ }
+ }
+
+ if (!adjust_monitor) {
+ /*
+ * Delete monitor; either spawn error
+ * or no monitor requested...
+ */
+ ErtsMonitorData *mdp = erts_monitor_to_data(omon);
+
+ omon->flags &= ~ERTS_ML_FLGS_SPAWN;
+
+ erts_monitor_tree_delete(&ERTS_P_MONITORS(c_p), omon);
+
+ if (erts_monitor_dist_delete(&mdp->target))
+ erts_monitor_release_both(mdp);
+ else
+ erts_monitor_release(omon);
+ cnt += 2;
+ }
+
+ if (convert_to_message) {
+ convert_prepared_sig_to_msg(c_p, sig, msg, next_nm_sig);
+ if (tag_hfrag) {
+ /* Save heap fragment of tag in message... */
+ tag_hfrag->next = sig->hfrag.next;
+ sig->hfrag.next = tag_hfrag;
+ }
+ erts_proc_notify_new_message(c_p, ERTS_PROC_LOCK_MAIN);
+ }
+ else {
+ remove_nm_sig(c_p, sig, next_nm_sig);
+ sig->data.attached = ERTS_MSG_COMBINED_HFRAG;
+ ERL_MESSAGE_TERM(sig) = msg;
+ sig->next = NULL;;
+ erts_cleanup_messages(sig);
+ if (tag_hfrag) {
+ tag_hfrag->next = NULL;
+ free_message_buffer(tag_hfrag);
+ }
+ }
+ return cnt;
+}
+
+static int
+handle_dist_spawn_reply_exiting(Process *c_p,
+ ErtsMessage *sig,
+ ErtsMonitor **pend_spawn_mon_pp,
+ Eterm reason)
+{
+ ErtsDistSpawnReplySigData *datap = get_dist_spawn_reply_data(sig);
+ Eterm result = datap->result;
+ Eterm msg = datap->message;
+ ErtsMonitorData *mdp;
+ ErtsMonitor *omon;
+ int cnt = 1;
+
+ ASSERT(is_atom(result) || is_external_pid(result));
+ ASSERT(is_atom(result) || size_object(result) == EXTERNAL_THING_HEAD_SIZE + 1);
+
+ omon = erts_monitor_tree_lookup(*pend_spawn_mon_pp, datap->ref);
+ if (!omon) {
+ /* May happen when connection concurrently close... */
+ ErtsLink *lnk = datap->link;
+ if (lnk) {
+ ErtsLinkData *ldp;
+ ErtsLink *dlnk = erts_link_to_other(lnk, &ldp);
+ if (erts_link_dist_delete(dlnk))
+ erts_link_release_both(ldp);
+ else
+ erts_link_release(lnk);
+ }
+ cnt++;
+ }
+ else {
+ ASSERT(omon->flags & ERTS_ML_FLG_SPAWN_PENDING);
+ ASSERT(!datap->link || is_external_pid(result));
+
+ erts_monitor_tree_delete(pend_spawn_mon_pp, omon);
+ mdp = erts_monitor_to_data(omon);
+
+ if (!erts_dist_pend_spawn_exit_delete(&mdp->target))
+ mdp = NULL; /* Connection closed/closing... */
+ cnt++;
+
+ if (is_external_pid(result)) {
+ if ((omon->flags & ERTS_ML_FLG_SPAWN_MONITOR) && mdp) {
+ ErtsMonitorDataExtended *mdep = (ErtsMonitorDataExtended *) mdp;
+ erts_proc_exit_dist_demonitor(c_p,
+ external_pid_dist_entry(result),
+ mdep->dist->connection_id,
+ datap->ref,
+ result);
+ cnt++;
+ }
+ ASSERT(!datap->link || (omon->flags & ERTS_ML_FLG_SPAWN_LINK));
+ ASSERT(!(omon->flags & ERTS_ML_FLG_SPAWN_LINK) || datap->link);
+
+ if (datap->link) {
+ /* This link exit *should* have actual reason... */
+ ErtsProcExitContext pectxt = {c_p, reason};
+ /* unless operation has been abandoned... */
+ if (omon->flags & ERTS_ML_FLG_SPAWN_ABANDONED)
+ pectxt.reason = am_abandoned;
+ erts_proc_exit_handle_link(datap->link, (void *) &pectxt, -1);
+ cnt++;
+ }
+ }
+ if (mdp)
+ erts_monitor_release_both(mdp);
+ else
+ erts_monitor_release(omon);
+ cnt++;
+ }
+ sig->data.attached = ERTS_MSG_COMBINED_HFRAG;
+ ERL_MESSAGE_TERM(sig) = msg;
+ sig->next = NULL;
+ erts_cleanup_messages(sig);
+ cnt++;
+ return cnt;
+}
+
/*
* Called in order to handle incoming signals.
*/
@@ -3266,7 +3760,7 @@ erts_proc_sig_handle_incoming(Process *c_p, erts_aint32_t *statep,
omon = &mdp->origin;
erts_monitor_tree_delete(&ERTS_P_MONITORS(c_p),
omon);
- cnt += convert_to_down_message(c_p, sig, mdp,
+ cnt += convert_to_down_message(c_p, sig, mdp, &omon,
type, next_nm_sig);
}
break;
@@ -3282,13 +3776,13 @@ erts_proc_sig_handle_incoming(Process *c_p, erts_aint32_t *statep,
xsigd->u.ref);
if (omon) {
ASSERT(erts_monitor_is_origin(omon));
+ erts_monitor_tree_delete(&ERTS_P_MONITORS(c_p),
+ omon);
if (omon->type == ERTS_MON_TYPE_DIST_PROC) {
mdp = erts_monitor_to_data(omon);
if (erts_monitor_dist_delete(&mdp->target))
tmon = &mdp->target;
}
- erts_monitor_tree_delete(&ERTS_P_MONITORS(c_p),
- omon);
cnt += convert_prepared_down_message(c_p, sig,
xsigd->message,
next_nm_sig);
@@ -3585,6 +4079,13 @@ erts_proc_sig_handle_incoming(Process *c_p, erts_aint32_t *statep,
break;
}
+
+ case ERTS_SIG_Q_OP_DIST_SPAWN_REPLY: {
+ ERTS_PROC_SIG_HDBG_PRIV_CHKQ(c_p, &tracing, next_nm_sig);
+ cnt += handle_dist_spawn_reply(c_p, &tracing, sig, next_nm_sig);
+ ERTS_PROC_SIG_HDBG_PRIV_CHKQ(c_p, &tracing, next_nm_sig);
+ break;
+ }
default:
ERTS_INTERNAL_ERROR("Unknown signal");
@@ -3780,7 +4281,9 @@ stretch_limit(Process *c_p, ErtsSigRecvTracing *tp,
int
-erts_proc_sig_handle_exit(Process *c_p, Sint *redsp)
+erts_proc_sig_handle_exit(Process *c_p, Sint *redsp,
+ ErtsMonitor **pend_spawn_mon_pp,
+ Eterm reason)
{
int cnt;
Sint limit;
@@ -3862,7 +4365,8 @@ erts_proc_sig_handle_exit(Process *c_p, Sint *redsp)
break;
case ERTS_SIG_Q_OP_MONITOR: {
- ErtsProcExitContext pectxt = {c_p, am_noproc, NULL, NULL, NIL};
+ ErtsProcExitContext pectxt = {c_p, am_noproc, NULL, NULL,
+ NULL, NULL, NIL, 0};
erts_proc_exit_handle_monitor((ErtsMonitor *) sig,
(void *) &pectxt, -1);
cnt += 4;
@@ -3914,6 +4418,13 @@ erts_proc_sig_handle_exit(Process *c_p, Sint *redsp)
break;
}
+ case ERTS_SIG_Q_OP_DIST_SPAWN_REPLY: {
+ cnt += handle_dist_spawn_reply_exiting(c_p, sig,
+ pend_spawn_mon_pp,
+ reason);
+ break;
+ }
+
case ERTS_SIG_Q_OP_TRACE_CHANGE_STATE:
destroy_trace_info((ErtsSigTraceInfo *) sig);
break;
@@ -3986,6 +4497,7 @@ clear_seq_trace_token(ErtsMessage *sig)
break;
case ERTS_SIG_Q_OP_PERSISTENT_MON_MSG:
+ case ERTS_SIG_Q_OP_DIST_SPAWN_REPLY:
ERTS_CLEAR_SEQ_TOKEN(sig);
break;
@@ -4060,6 +4572,7 @@ erts_proc_sig_signal_size(ErtsSignal *sig)
case ERTS_SIG_Q_OP_SYNC_SUSPEND:
case ERTS_SIG_Q_OP_PERSISTENT_MON_MSG:
case ERTS_SIG_Q_OP_IS_ALIVE:
+ case ERTS_SIG_Q_OP_DIST_SPAWN_REPLY:
size = ((ErtsMessage *) sig)->hfrag.alloc_size;
size *= sizeof(Eterm);
size += sizeof(ErtsMessage) - sizeof(Eterm);
@@ -4328,6 +4841,13 @@ getting_linked(Process *c_p, Eterm linker)
am_getting_linked, linker);
}
+static void
+linking(Process *c_p, Eterm to)
+{
+ trace_proc(c_p, ERTS_PROC_LOCK_MAIN, c_p,
+ am_link, to);
+}
+
static ERTS_INLINE void
handle_message_enqueued_tracing(Process *c_p,
ErtsSigRecvTracing *tracing,
diff --git a/erts/emulator/beam/erl_proc_sig_queue.h b/erts/emulator/beam/erl_proc_sig_queue.h
index 2789179b34..b0a5d0dac3 100644
--- a/erts/emulator/beam/erl_proc_sig_queue.h
+++ b/erts/emulator/beam/erl_proc_sig_queue.h
@@ -706,6 +706,14 @@ erts_proc_sig_send_rpc_request(Process *c_p,
Eterm (*func)(Process *, void *, int *, ErlHeapFragment **),
void *arg);
+int
+erts_proc_sig_send_dist_spawn_reply(Eterm node,
+ Eterm ref,
+ Eterm to,
+ ErtsLink *lnk,
+ Eterm result,
+ Eterm token);
+
/*
* End of send operations of currently supported process signals.
*/
@@ -787,7 +795,9 @@ erts_proc_sig_handle_incoming(Process *c_p, erts_aint32_t *statep,
* queue.
*/
int
-erts_proc_sig_handle_exit(Process *c_p, Sint *redsp);
+erts_proc_sig_handle_exit(Process *c_p, Sint *redsp,
+ ErtsMonitor **pend_spawn_mon_pp,
+ Eterm reason);
/**
*
diff --git a/erts/emulator/beam/erl_process.c b/erts/emulator/beam/erl_process.c
index e9ed4a7407..16d8230533 100644
--- a/erts/emulator/beam/erl_process.c
+++ b/erts/emulator/beam/erl_process.c
@@ -44,7 +44,6 @@
#include "erl_thr_queue.h"
#include "erl_async.h"
#include "dtrace-wrapper.h"
-#include "lttng-wrapper.h"
#include "erl_ptab.h"
#include "erl_bif_unique.h"
#define ERTS_WANT_TIMER_WHEEL_API
@@ -708,10 +707,10 @@ erts_pre_init_process(void)
erts_psd_required_locks[ERTS_PSD_DELAYED_GC_TASK_QS].set_locks
= ERTS_PSD_DELAYED_GC_TASK_QS_SET_LOCKS;
- erts_psd_required_locks[ERTS_PSD_NIF_TRAP_EXPORT].get_locks
- = ERTS_PSD_NIF_TRAP_EXPORT_GET_LOCKS;
- erts_psd_required_locks[ERTS_PSD_NIF_TRAP_EXPORT].set_locks
- = ERTS_PSD_NIF_TRAP_EXPORT_SET_LOCKS;
+ erts_psd_required_locks[ERTS_PSD_NFUNC_TRAP_WRAPPER].get_locks
+ = ERTS_PSD_NFUNC_TRAP_WRAPPER_GET_LOCKS;
+ erts_psd_required_locks[ERTS_PSD_NFUNC_TRAP_WRAPPER].set_locks
+ = ERTS_PSD_NFUNC_TRAP_WRAPPER_SET_LOCKS;
erts_psd_required_locks[ERTS_PSD_ETS_OWNED_TABLES].get_locks
= ERTS_PSD_ETS_OWNED_TABLES_GET_LOCKS;
@@ -6495,8 +6494,8 @@ schedule_out_process(ErtsRunQueue *c_rq, erts_aint32_t state, Process *p,
ASSERT(!(state & (ERTS_PSFLG_DIRTY_IO_PROC
|ERTS_PSFLG_DIRTY_CPU_PROC))
- || (BeamIsOpCode(*p->i, op_call_nif)
- || BeamIsOpCode(*p->i, op_apply_bif)));
+ || (BeamIsOpCode(*p->i, op_call_nif_WWW)
+ || BeamIsOpCode(*p->i, op_call_bif_W)));
a = state;
@@ -9554,7 +9553,6 @@ Process *erts_schedule(ErtsSchedulerData *esdp, Process *p, int calls)
erts_runq_unlock(rq);
ERTS_MSACC_SET_STATE_CACHED_M(ERTS_MSACC_STATE_CHECK_IO);
- LTTNG2(scheduler_poll, esdp->no, 1);
erts_check_io(esdp->ssi->psi, ERTS_POLL_NO_TIMEOUT);
ERTS_MSACC_POP_STATE_M();
@@ -11100,8 +11098,13 @@ erts_set_gc_state(Process *c_p, int enable)
ERTS_LC_ASSERT(ERTS_PROC_LOCK_MAIN == erts_proc_lc_my_proc_locks(c_p));
if (!enable) {
- c_p->flags |= F_DISABLE_GC;
- return 0;
+ /* Strictly speaking it's not illegal to disable the GC when it's
+ * already disabled, but we risk enabling the GC prematurely if (for
+ * example) a BIF were to blindly disable it when trapping and then
+ * re-enable it before returning its result. */
+ ASSERT(!(c_p->flags & F_DISABLE_GC));
+ c_p->flags |= F_DISABLE_GC;
+ return 0;
}
c_p->flags &= ~F_DISABLE_GC;
@@ -11482,6 +11485,180 @@ alloc_process(ErtsRunQueue *rq, int bound, erts_aint32_t state)
return p;
}
+int
+erts_parse_spawn_opts(ErlSpawnOpts *sop, Eterm opts_list, Eterm *tag,
+ int message_opt)
+{
+ /*
+ * Returns:
+ * - 0 on success
+ * - <0 on badopt
+ * - >0 on badarg (not prober list)
+ */
+ int result = 0;
+ Eterm ap = opts_list;
+
+ if (tag)
+ *tag = am_spawn_reply;
+ /*
+ * Store default values for options.
+ */
+ sop->multi_set = 0;
+ sop->flags = erts_default_spo_flags;
+ sop->min_heap_size = H_MIN_SIZE;
+ sop->min_vheap_size = BIN_VH_MIN_SIZE;
+ sop->max_heap_size = H_MAX_SIZE;
+ sop->max_heap_flags = H_MAX_FLAGS;
+ sop->priority = PRIORITY_NORMAL;
+ sop->max_gen_gcs = (Uint16) erts_atomic32_read_nob(&erts_max_gen_gcs);
+ sop->scheduler = 0;
+
+ /*
+ * Walk through the option list.
+ */
+ while (is_list(ap)) {
+ Eterm arg = CAR(list_val(ap));
+ ap = CDR(list_val(ap));
+ if (arg == am_link) {
+ if (sop->flags & SPO_LINK)
+ sop->multi_set = !0;
+ sop->flags |= SPO_LINK;
+ } else if (arg == am_monitor) {
+ if (sop->flags & SPO_MONITOR)
+ sop->multi_set = !0;
+ sop->flags |= SPO_MONITOR;
+ } else if (is_tuple(arg)) {
+ Eterm* tp2 = tuple_val(arg);
+ Eterm val;
+ if (*tp2 != make_arityval(2)) {
+ result = -1;
+ continue;
+ }
+ arg = tp2[1];
+ val = tp2[2];
+ if (arg == am_priority) {
+ if (sop->flags & SPO_PRIORITY)
+ sop->multi_set = !0;
+ sop->flags |= SPO_PRIORITY;
+ if (val == am_max)
+ sop->priority = PRIORITY_MAX;
+ else if (val == am_high)
+ sop->priority = PRIORITY_HIGH;
+ else if (val == am_normal)
+ sop->priority = PRIORITY_NORMAL;
+ else if (val == am_low)
+ sop->priority = PRIORITY_LOW;
+ else
+ result = -1;
+ } else if (arg == am_message_queue_data) {
+ if (sop->flags & (SPO_OFF_HEAP_MSGQ|SPO_ON_HEAP_MSGQ))
+ sop->multi_set = !0;
+ switch (val) {
+ case am_on_heap:
+ sop->flags &= ~SPO_OFF_HEAP_MSGQ;
+ sop->flags |= SPO_ON_HEAP_MSGQ;
+ break;
+ case am_off_heap:
+ sop->flags &= ~SPO_ON_HEAP_MSGQ;
+ sop->flags |= SPO_OFF_HEAP_MSGQ;
+ break;
+ default:
+ result = -1;
+ break;
+ }
+ } else if (arg == am_min_heap_size && is_small(val)) {
+ Sint min_heap_size = signed_val(val);
+ if (sop->flags & SPO_MIN_HEAP_SIZE)
+ sop->multi_set = !0;
+ sop->flags |= SPO_MIN_HEAP_SIZE;
+ if (min_heap_size < 0) {
+ result = -1;
+ } else if (min_heap_size < H_MIN_SIZE) {
+ sop->min_heap_size = H_MIN_SIZE;
+ } else {
+ sop->min_heap_size = erts_next_heap_size(min_heap_size, 0);
+ }
+ } else if (arg == am_max_heap_size) {
+ if (sop->flags & SPO_MAX_HEAP_SIZE)
+ sop->multi_set = !0;
+ sop->flags |= SPO_MAX_HEAP_SIZE;
+ if (!erts_max_heap_size(val, &sop->max_heap_size, &sop->max_heap_flags))
+ result = -1;
+ } else if (arg == am_min_bin_vheap_size && is_small(val)) {
+ Sint min_vheap_size = signed_val(val);
+ if (sop->flags & SPO_MIN_VHEAP_SIZE)
+ sop->multi_set = !0;
+ sop->flags |= SPO_MIN_VHEAP_SIZE;
+ if (min_vheap_size < 0) {
+ result = -1;
+ } else if (min_vheap_size < BIN_VH_MIN_SIZE) {
+ sop->min_vheap_size = BIN_VH_MIN_SIZE;
+ } else {
+ sop->min_vheap_size = erts_next_heap_size(min_vheap_size, 0);
+ }
+ } else if (arg == am_fullsweep_after && is_small(val)) {
+ Sint max_gen_gcs = signed_val(val);
+ if (sop->flags & SPO_MAX_GEN_GCS)
+ sop->multi_set = !0;
+ sop->flags |= SPO_MAX_GEN_GCS;
+ if (max_gen_gcs < 0) {
+ result = -1;
+ } else {
+ sop->max_gen_gcs = max_gen_gcs;
+ }
+ } else if (arg == am_scheduler && is_small(val)) {
+ Sint scheduler = signed_val(val);
+ if (sop->flags & SPO_SCHEDULER)
+ sop->multi_set = !0;
+ sop->flags |= SPO_SCHEDULER;
+ if (scheduler < 0 || erts_no_schedulers < scheduler)
+ result = -1;
+ else
+ sop->scheduler = (int) scheduler;
+ } else if (arg == am_reply) {
+ if (!message_opt)
+ result = -1;
+ else if (val == am_error_only) {
+ sop->flags |= SPO_NO_SMSG;
+ sop->flags &= ~SPO_NO_EMSG;
+ }
+ else if (val == am_success_only) {
+ sop->flags &= ~SPO_NO_SMSG;
+ sop->flags |= SPO_NO_EMSG;
+ }
+ else if (val == am_no) {
+ sop->flags |= SPO_NO_SMSG;
+ sop->flags |= SPO_NO_EMSG;
+ }
+ else if (val == am_yes) {
+ sop->flags &= ~SPO_NO_SMSG;
+ sop->flags &= ~SPO_NO_EMSG;
+ }
+ else
+ result = -1;
+ } else if (arg == am_reply_tag) {
+ if (!tag)
+ result = -1;
+ else
+ *tag = val;
+ } else {
+ result = -1;
+ }
+ } else {
+ result = -1;
+ }
+ }
+ if (is_not_nil(ap)) {
+ return 1;
+ }
+
+ if (sop->max_heap_size != 0 && sop->max_heap_size < sop->min_heap_size) {
+ result = -1;
+ }
+
+ return result;
+}
+
Eterm
erl_create_process(Process* parent, /* Parent of process (default group leader). */
Eterm mod, /* Tagged atom for module. */
@@ -11501,6 +11678,8 @@ erl_create_process(Process* parent, /* Parent of process (default group leader).
erts_aint32_t state = 0;
erts_aint32_t prio = (erts_aint32_t) PRIORITY_NORMAL;
ErtsProcLocks locks = ERTS_PROC_LOCKS_ALL;
+ Eterm node_token_heap[6];
+ Eterm group_leader, parent_id, spawn_ref, token;
#ifdef SHCOPY_SPAWN
erts_shcopy_t info;
INITIALIZE_SHCOPY(info);
@@ -11508,8 +11687,25 @@ erl_create_process(Process* parent, /* Parent of process (default group leader).
erts_literal_area_t litarea;
INITIALIZE_LITERAL_PURGE_AREA(litarea);
#endif
-
- erts_proc_lock(parent, ERTS_PROC_LOCKS_ALL_MINOR);
+
+ if (!parent) {
+ token = so->token;
+ group_leader = so->group_leader;
+ parent_id = so->parent_id;
+ spawn_ref = so->mref;
+ }
+ else {
+ token = SEQ_TRACE_TOKEN(parent);
+ erts_proc_lock(parent, ERTS_PROC_LOCKS_ALL_MINOR);
+ group_leader = parent->group_leader;
+ parent_id = parent->common.id;
+ if (so->flags & (SPO_MONITOR | SPO_ASYNC))
+ spawn_ref = so->mref = erts_make_ref(parent);
+ else if (have_seqtrace(token))
+ spawn_ref = erts_make_ref(parent);
+ else
+ spawn_ref = THE_NON_VALUE;
+ }
/*
* Check for errors.
@@ -11520,6 +11716,11 @@ erl_create_process(Process* parent, /* Parent of process (default group leader).
goto error;
}
+ if (arity > MAX_SMALL) {
+ so->error_code = SYSTEM_LIMIT;
+ goto error;
+ }
+
if (so->flags & SPO_USE_ARGS) {
if (so->scheduler) {
int ix = so->scheduler-1;
@@ -11544,14 +11745,21 @@ erl_create_process(Process* parent, /* Parent of process (default group leader).
ASSERT((qs_flags & FS_ON_HEAP_MSGQ) || (qs_flags & FS_OFF_HEAP_MSGQ));
- if (!rq)
- rq = erts_get_runq_proc(parent, NULL);
+ if (!rq) {
+ if (parent)
+ rq = erts_get_runq_proc(parent, NULL);
+ else {
+ ErtsSchedulerData *esdp = erts_get_scheduler_data();
+ ASSERT(esdp->type == ERTS_SCHED_NORMAL);
+ rq = esdp->run_queue;
+ }
+ }
+ ASSERT(rq);
p = alloc_process(rq, bound, state); /* All proc locks are locked by this thread
on success */
if (!p) {
- erts_send_error_to_logger_str(parent->group_leader,
- "Too many processes\n");
+ erts_send_error_to_logger_str(group_leader, "Too many processes\n");
so->error_code = SYSTEM_LIMIT;
goto error;
}
@@ -11561,7 +11769,7 @@ erl_create_process(Process* parent, /* Parent of process (default group leader).
#else
arg_size = size_object_litopt(args, &litarea);
#endif
- heap_need = arg_size;
+ heap_need = arg_size + 1; /* Reserve place for continuation pointer */
p->flags = flags;
p->sig_qs.flags = qs_flags;
@@ -11585,18 +11793,14 @@ erl_create_process(Process* parent, /* Parent of process (default group leader).
p->schedule_count = 0;
ASSERT(p->min_heap_size == erts_next_heap_size(p->min_heap_size, 0));
- p->u.initial.module = mod;
- p->u.initial.function = func;
- p->u.initial.arity = (Uint) arity;
-
/*
* Must initialize binary lists here before copying binaries to process.
*/
p->off_heap.first = NULL;
p->off_heap.overhead = 0;
- heap_need +=
- IS_CONST(parent->group_leader) ? 0 : NC_HEAP_SIZE(parent->group_leader);
+ if (is_not_immed(group_leader))
+ heap_need += NC_HEAP_SIZE(group_leader);
if (heap_need < p->min_heap_size) {
sz = heap_need = p->min_heap_size;
@@ -11611,7 +11815,8 @@ erl_create_process(Process* parent, /* Parent of process (default group leader).
p->old_hend = p->old_htop = p->old_heap = NULL;
p->high_water = p->heap;
p->gen_gcs = 0;
- p->stop = p->hend = p->heap + sz;
+ p->hend = p->heap + sz;
+ p->stop = p->hend - 1; /* Reserve place for continuation pointer */
p->htop = p->heap;
p->heap_sz = sz;
p->abandoned_heap = NULL;
@@ -11629,7 +11834,7 @@ erl_create_process(Process* parent, /* Parent of process (default group leader).
p->current = &p->u.initial;
p->i = (BeamInstr *) beam_apply;
- p->cp = (BeamInstr *) beam_apply+1;
+ p->stop[0] = make_cp(beam_apply + 1);
p->arg_reg = p->def_arg_reg;
p->max_arg_reg = sizeof(p->def_arg_reg)/sizeof(p->def_arg_reg[0]);
@@ -11655,16 +11860,16 @@ erl_create_process(Process* parent, /* Parent of process (default group leader).
ERTS_P_MONITORS(p) = NULL;
ERTS_P_LT_MONITORS(p) = NULL;
- ASSERT(is_pid(parent->group_leader));
+ ASSERT(is_pid(group_leader));
- if (parent->group_leader == ERTS_INVALID_PID)
+ if (group_leader == ERTS_INVALID_PID)
p->group_leader = p->common.id;
else {
/* Needs to be done after the heap has been set up */
p->group_leader =
- IS_CONST(parent->group_leader)
- ? parent->group_leader
- : STORE_NC(&p->htop, &p->off_heap, parent->group_leader);
+ IS_CONST(group_leader)
+ ? group_leader
+ : STORE_NC(&p->htop, &p->off_heap, group_leader);
}
erts_get_default_proc_tracing(&ERTS_TRACE_FLAGS(p), &ERTS_TRACER(p));
@@ -11692,14 +11897,11 @@ erl_create_process(Process* parent, /* Parent of process (default group leader).
p->mbuf_sz = 0;
erts_atomic_init_nob(&p->psd, (erts_aint_t) NULL);
p->dictionary = NULL;
- p->seq_trace_lastcnt = 0;
- p->seq_trace_clock = 0;
- SEQ_TRACE_TOKEN(p) = NIL;
#ifdef USE_VM_PROBES
DT_UTAG(p) = NIL;
DT_UTAG_FLAGS(p) = 0;
#endif
- p->parent = (parent->common.id == ERTS_INVALID_PID
+ p->parent = (!parent || parent->common.id == ERTS_INVALID_PID
? NIL
: parent->common.id);
@@ -11715,7 +11917,107 @@ erl_create_process(Process* parent, /* Parent of process (default group leader).
p->fp_exception = 0;
#endif
- if (IS_TRACED(parent)) {
+ /* seq_trace is handled before regular tracing as the latter may touch the
+ * trace token. */
+ if (!have_seqtrace(token)) {
+ SEQ_TRACE_TOKEN(p) = NIL;
+ p->seq_trace_lastcnt = 0;
+ p->seq_trace_clock = 0;
+ }
+ else {
+ Eterm tmp_heap[9]; /* 8-tuple */
+ Eterm seq_msg;
+ Uint token_sz;
+ Eterm *hp;
+
+ if (parent) {
+ seq_trace_update_serial(parent);
+ token = SEQ_TRACE_TOKEN(parent);
+ ASSERT(SEQ_TRACE_T_ARITY(token) == 5);
+ sys_memcpy(&node_token_heap[0],
+ (void *) tuple_val(token),
+ sizeof(Eterm)*6);
+ token = make_tuple(&node_token_heap[0]);
+ }
+
+ ASSERT(SEQ_TRACE_T_ARITY(token) == 5);
+ ASSERT(is_immed(SEQ_TRACE_T_FLAGS(token)));
+ ASSERT(is_immed(SEQ_TRACE_T_SERIAL(token)));
+ ASSERT(is_immed(SEQ_TRACE_T_LASTCNT(token)));
+
+ token_sz = size_object(token);
+
+ hp = HAlloc(p, token_sz);
+ SEQ_TRACE_TOKEN(p) = copy_struct(token, token_sz, &hp, &MSO(p));
+
+ ASSERT((locks & (ERTS_PROC_LOCK_STATUS|ERTS_PROC_LOCK_TRACE)) ==
+ (ERTS_PROC_LOCK_STATUS|ERTS_PROC_LOCK_TRACE));
+
+ locks &= ~(ERTS_PROC_LOCK_STATUS|ERTS_PROC_LOCK_TRACE);
+ erts_proc_unlock(p, ERTS_PROC_LOCK_STATUS|ERTS_PROC_LOCK_TRACE);
+
+ if (parent) {
+ /* Simulate spawn_request message... */
+ Eterm tmp_heap2[4];
+ Eterm mfa;
+ erts_proc_unlock(parent, ERTS_PROC_LOCK_STATUS|ERTS_PROC_LOCK_TRACE);
+ mfa = TUPLE3(&tmp_heap2[0], mod, func, make_small(arity)) ;
+ seq_msg = TUPLE8(&tmp_heap[0], am_spawn_request,
+ spawn_ref, parent_id, group_leader,
+ mfa, so->opts, so->tag, args);
+ seq_trace_output(token, seq_msg, SEQ_TRACE_SEND,
+ p->common.id, parent);
+ seq_trace_output(token, seq_msg, SEQ_TRACE_RECEIVE,
+ p->common.id, parent);
+
+ /* The counters behave the same way on spawning as they do on messages;
+ * we don't inherit our parent's lastcnt. */
+ p->seq_trace_clock = unsigned_val(SEQ_TRACE_T_SERIAL(token));
+ p->seq_trace_lastcnt = p->seq_trace_clock;
+
+ }
+ else {
+ /*
+ * The spawn request is presented as two messages
+ * in dist case. It is sent as one signal over the
+ * distribution with the argument list as payload.
+ * The payload will be delivered as an ordinary
+ * message of its own, as the first message to the
+ * newly created process (in order to decode it in
+ * in the newly created process). For more info see
+ * erts_internal:dist_spawn_init() in erts_interal.erl.
+ * We expose these as two messages when seq-tracing
+ * in order not having to decode the argument list
+ * here. The remote node has passed a token with
+ * serial bumped twice, i.e., the first message should
+ * use a serial of one less than in the actual token;
+ * adjust serial and then restore it for use with
+ * the argument list message...
+ */
+ Eterm serial;
+ Uint serial_num;
+ ASSERT(eq(SEQ_TRACE_T_SENDER(token), parent_id));
+ serial = SEQ_TRACE_T_SERIAL(token);
+ serial_num = unsigned_val(serial);
+ serial_num--;
+ SEQ_TRACE_T_SERIAL(token) = make_small(serial_num);
+
+ seq_msg = TUPLE6(&tmp_heap[0], am_spawn_request,
+ spawn_ref, parent_id, group_leader,
+ so->mfa, so->opts);
+ seq_trace_output(token, seq_msg, SEQ_TRACE_RECEIVE,
+ p->common.id, p);
+
+ /* as on receive... */
+ p->seq_trace_clock = serial_num;
+ p->seq_trace_lastcnt = serial_num;
+
+ /* Restore serial for the argument list message... */
+ SEQ_TRACE_T_SERIAL(token) = serial;
+ }
+ }
+
+ if (parent && IS_TRACED(parent)) {
if (ERTS_TRACE_FLAGS(parent) & F_TRACE_SOS) {
ERTS_TRACE_FLAGS(p) |= (ERTS_TRACE_FLAGS(parent) & TRACEE_FLAGS);
erts_tracer_replace(&p->common, ERTS_TRACER(parent));
@@ -11736,9 +12038,14 @@ erl_create_process(Process* parent, /* Parent of process (default group leader).
}
}
if (ARE_TRACE_FLAGS_ON(parent, F_TRACE_PROCS)) {
- locks &= ~(ERTS_PROC_LOCK_STATUS|ERTS_PROC_LOCK_TRACE);
- erts_proc_unlock(p, ERTS_PROC_LOCK_STATUS|ERTS_PROC_LOCK_TRACE);
- erts_proc_unlock(parent, ERTS_PROC_LOCK_STATUS|ERTS_PROC_LOCK_TRACE);
+ /* The locks may already be released if seq_trace is enabled as
+ * well. */
+ if ((locks & (ERTS_PROC_LOCK_STATUS|ERTS_PROC_LOCK_TRACE))
+ == (ERTS_PROC_LOCK_STATUS|ERTS_PROC_LOCK_TRACE)) {
+ locks &= ~(ERTS_PROC_LOCK_STATUS|ERTS_PROC_LOCK_TRACE);
+ erts_proc_unlock(p, ERTS_PROC_LOCK_STATUS|ERTS_PROC_LOCK_TRACE);
+ erts_proc_unlock(parent, ERTS_PROC_LOCK_STATUS|ERTS_PROC_LOCK_TRACE);
+ }
trace_proc_spawn(parent, am_spawn, p->common.id, mod, func, args);
if (so->flags & SPO_LINK)
trace_proc(parent, locks, parent, am_link, p->common.id);
@@ -11751,49 +12058,187 @@ erl_create_process(Process* parent, /* Parent of process (default group leader).
/* This happens when parent was not traced, but child is */
locks &= ~(ERTS_PROC_LOCK_STATUS|ERTS_PROC_LOCK_TRACE);
erts_proc_unlock(p, ERTS_PROC_LOCK_STATUS|ERTS_PROC_LOCK_TRACE);
- erts_proc_unlock(parent, ERTS_PROC_LOCK_STATUS|ERTS_PROC_LOCK_TRACE);
+ if (parent)
+ erts_proc_unlock(parent, ERTS_PROC_LOCK_STATUS|ERTS_PROC_LOCK_TRACE);
}
- trace_proc_spawn(p, am_spawned, parent->common.id, mod, func, args);
+ trace_proc_spawn(p, am_spawned, parent_id, mod, func, args);
if (so->flags & SPO_LINK)
- trace_proc(p, locks, p, am_getting_linked, parent->common.id);
+ trace_proc(p, locks, p, am_getting_linked, parent_id);
}
/*
* Check if this process should be initially linked to its parent.
*/
- if (so->flags & SPO_LINK) {
- ErtsLink *lnk;
- ErtsLinkData *ldp = erts_link_create(ERTS_LNK_TYPE_PROC,
- parent->common.id,
- p->common.id);
- lnk = erts_link_tree_lookup_insert(&ERTS_P_LINKS(parent), &ldp->a);
- if (lnk) {
- /*
- * This should more or less never happen, but could
- * potentially happen if pid:s wrap...
- */
- erts_link_release(lnk);
+ if (parent) {
+ /* Node local spawn... */
+
+ if (so->flags & SPO_LINK) {
+ ErtsLink *lnk;
+ ErtsLinkData *ldp = erts_link_create(ERTS_LNK_TYPE_PROC,
+ parent->common.id,
+ p->common.id);
+ lnk = erts_link_tree_lookup_insert(&ERTS_P_LINKS(parent), &ldp->a);
+ if (lnk) {
+ /*
+ * This should more or less never happen, but could
+ * potentially happen if pid:s wrap...
+ */
+ erts_link_release(lnk);
+ }
+ erts_link_tree_insert(&ERTS_P_LINKS(p), &ldp->b);
}
- erts_link_tree_insert(&ERTS_P_LINKS(p), &ldp->b);
- }
- /*
- * Test whether this process should be initially monitored by its parent.
- */
- if (so->flags & SPO_MONITOR) {
- Eterm mref = erts_make_ref(parent);
- ErtsMonitorData *mdp = erts_monitor_create(ERTS_MON_TYPE_PROC,
- mref,
- parent->common.id,
- p->common.id,
- NIL);
- erts_monitor_tree_insert(&ERTS_P_MONITORS(parent), &mdp->origin);
- erts_monitor_list_insert(&ERTS_P_LT_MONITORS(p), &mdp->target);
- so->mref = mref;
+ /*
+ * Test whether this process should be initially monitored by its parent.
+ */
+ if (so->flags & SPO_MONITOR) {
+ ErtsMonitorData *mdp = erts_monitor_create(ERTS_MON_TYPE_PROC,
+ spawn_ref,
+ parent->common.id,
+ p->common.id,
+ NIL);
+ erts_monitor_tree_insert(&ERTS_P_MONITORS(parent), &mdp->origin);
+ erts_monitor_list_insert(&ERTS_P_LT_MONITORS(p), &mdp->target);
+ }
+
+ ASSERT(locks & ERTS_PROC_LOCK_MSGQ);
+
+ if (so->flags & SPO_ASYNC) { /* spawn_request() */
+ Eterm *tp;
+
+ ASSERT(is_tuple_arity(so->mfa, 3));
+ tp = tuple_val(so->mfa);
+ ASSERT(is_atom(tp[1]));
+ ASSERT(is_atom(tp[2]));
+ ASSERT(is_small(tp[3]));
+ p->u.initial.module = tp[1];
+ p->u.initial.function = tp[2];
+ p->u.initial.arity = (Uint) unsigned_val(tp[3]);
+
+ ASSERT(is_value(so->tag));
+ if (have_seqtrace(token)) {
+ seq_trace_update_serial(p);
+ token = SEQ_TRACE_TOKEN(p);
+ }
+
+ if (!(so->flags & SPO_NO_SMSG)) {
+ /*
+ * Ensure spawn reply success message reach parent before
+ * any down or exit signals from child...
+ */
+ erts_send_local_spawn_reply(parent, locks, p,
+ so->tag, spawn_ref,
+ p->common.id, token);
+ }
+ }
+ else { /* synchronous spawn */
+
+ p->u.initial.module = mod;
+ p->u.initial.function = func;
+ p->u.initial.arity = (Uint) arity;
+
+ if (have_seqtrace(token)) {
+ /* Simulate spawn reply message... */
+ Eterm tmp_heap[5];
+ Eterm seq_msg;
+ Uint serial;
+
+ seq_trace_update_serial(p);
+ token = SEQ_TRACE_TOKEN(p);
+ serial = SEQ_TRACE_T_SERIAL(token);
+ seq_msg = TUPLE4(&tmp_heap[0], so->tag, spawn_ref,
+ am_ok, p->common.id);
+ seq_trace_output(token, seq_msg, SEQ_TRACE_SEND,
+ parent_id, parent);
+
+ /* Update parent as if receive... */
+ parent->seq_trace_lastcnt = serial;
+ parent->seq_trace_clock = serial;
+ seq_trace_output(token, seq_msg, SEQ_TRACE_RECEIVE,
+ parent_id, parent);
+ }
+
+ }
+
+ erts_proc_unlock(p, locks);
+ erts_proc_unlock(parent, locks & ERTS_PROC_LOCKS_ALL_MINOR);
+
}
+ else {
+ /* Distributed spawn */
+ ErtsDSigSendContext ctx;
+ int code;
+ Eterm *tp;
+
+ ASSERT(is_tuple_arity(so->mfa, 3));
+ tp = tuple_val(so->mfa);
+ ASSERT(is_atom(tp[1]));
+ ASSERT(is_atom(tp[2]));
+ ASSERT(is_small(tp[3]));
+ p->u.initial.module = tp[1];
+ p->u.initial.function = tp[2];
+ p->u.initial.arity = (Uint) unsigned_val(tp[3]);
+
+ ASSERT(locks & ERTS_PROC_LOCK_MSGQ);
+ /*
+ * Pass the (on external format) encoded argument list as
+ * *first* message to the process. Note that this message
+ * *must* be first in the message queue of the newly
+ * spawned process!
+ */
+ erts_queue_dist_message(p, locks, so->edep, so->ede_hfrag,
+ token, parent_id);
- erts_proc_unlock(p, locks);
+ erts_proc_unlock(p, locks & ERTS_PROC_LOCKS_ALL_MINOR);
+
+ if (so->flags & SPO_LINK) {
+ ErtsLinkData *ldp;
+ ldp = erts_link_create(ERTS_LNK_TYPE_DIST_PROC,
+ parent_id, p->common.id);
+ code = erts_link_dist_insert(&ldp->a, so->dist_entry->mld);
+ ASSERT(code);
+ erts_link_tree_insert(&ERTS_P_LINKS(p), &ldp->b);
+ }
+
+ if (so->flags & SPO_MONITOR) {
+ ErtsMonitorData *mdp;
+ mdp = erts_monitor_create(ERTS_MON_TYPE_DIST_PROC,
+ spawn_ref, parent_id,
+ p->common.id, NIL);
+ code = erts_monitor_dist_insert(&mdp->origin, so->dist_entry->mld);
+ ASSERT(code); (void)code;
+ erts_monitor_tree_insert(&ERTS_P_MONITORS(p), &mdp->target);
+ }
+
+ if (have_seqtrace(token)) {
+ Eterm tmp_heap[5];
+ Eterm seq_msg;
+ seq_trace_update_serial(p);
+ seq_msg = TUPLE4(&tmp_heap[0], so->tag,
+ spawn_ref, am_ok, p->common.id);
+ token = SEQ_TRACE_TOKEN(p);
+ seq_trace_output(token, seq_msg, SEQ_TRACE_SEND, parent_id, p);
+ }
+
+ code = erts_dsig_prepare(&ctx, so->dist_entry, NULL, 0,
+ ERTS_DSP_NO_LOCK, 1, 1, 0);
+ if (code == ERTS_DSIG_PREP_CONNECTED) {
+ int dsflags = 0;
+ if (so->flags & SPO_LINK)
+ dsflags |= ERTS_DIST_SPAWN_FLAG_LINK;
+ if (so->flags & SPO_MONITOR)
+ dsflags |= ERTS_DIST_SPAWN_FLAG_MONITOR;
+ code = erts_dsig_send_spawn_reply(&ctx, spawn_ref,
+ parent_id,
+ make_small(dsflags),
+ p->common.id,
+ token);
+ ASSERT(code == ERTS_DSIG_SEND_OK);
+ }
+
+ erts_proc_unlock(p, locks & ERTS_PROC_LOCK_MAIN);
+ }
res = p->common.id;
@@ -11801,8 +12246,6 @@ erl_create_process(Process* parent, /* Parent of process (default group leader).
* Schedule process for execution.
*/
- erts_proc_unlock(parent, locks & ERTS_PROC_LOCKS_ALL_MINOR);
-
schedule_process(p, state, 0);
VERBOSE(DEBUG_PROCESSES, ("Created a new process: %T\n",p->common.id));
@@ -11821,11 +12264,65 @@ erl_create_process(Process* parent, /* Parent of process (default group leader).
error:
- erts_proc_unlock(parent, locks & ERTS_PROC_LOCKS_ALL_MINOR);
+ if (parent)
+ erts_proc_unlock(parent, locks & ERTS_PROC_LOCKS_ALL_MINOR);
return res;
}
+void
+erts_send_local_spawn_reply(Process *parent, ErtsProcLocks parent_locks,
+ Process *child, Eterm tag, Eterm ref,
+ Eterm result, Eterm token)
+{
+ ErtsMessage *mp;
+ ErlOffHeap *ohp;
+ Eterm *hp;
+ Eterm msg, ref_copy, ref_sz, tag_copy, tag_sz,
+ token_copy, token_sz, type;
+ ErtsProcLocks locks = parent_locks;
+
+ type = child ? am_ok : am_error;
+
+ if (have_seqtrace(token) && child)
+ token_sz = size_object(token);
+ else {
+ token_copy = token = NIL;
+ token_sz = 0;
+ }
+
+ ref_sz = size_object(ref);
+ tag_sz = is_immed(tag) ? 0 : size_object(tag);
+ mp = erts_alloc_message_heap(parent, &locks,
+ 5 + tag_sz + ref_sz + token_sz,
+ &hp, &ohp);
+ ref_copy = copy_struct(ref, ref_sz, &hp, ohp);
+ tag_copy = is_immed(tag) ? tag : copy_struct(tag, tag_sz, &hp, ohp);
+ msg = TUPLE4(hp, tag_copy, ref_copy, type, result);
+ hp += 5;
+
+ if (have_seqtrace(token)) {
+ token_copy = copy_struct(token, token_sz, &hp, ohp);
+ seq_trace_output(token_copy, msg, SEQ_TRACE_SEND,
+ parent->common.id, parent);
+ }
+
+ if (!child) { /* error reply */
+ ASSERT(is_atom(result));
+ erts_queue_message(parent, parent_locks, mp, msg,
+ erts_this_dist_entry->sysname);
+ }
+ else { /* success reply */
+ ASSERT(child->common.id == result);
+ ERL_MESSAGE_TOKEN(mp) = token_copy;
+ erts_queue_proc_message(child, parent, parent_locks, mp, msg);
+ }
+
+ ASSERT((parent_locks & locks) == parent_locks);
+ if (locks != parent_locks)
+ erts_proc_unlock(parent, locks & ~parent_locks);
+}
+
/*
* Initiates a pseudo process that can be used
* for arithmetic BIFs.
@@ -11902,7 +12399,6 @@ void erts_init_empty_process(Process *p)
p->u.initial.function = 0;
p->u.initial.arity = 0;
p->catches = 0;
- p->cp = NULL;
p->i = NULL;
p->current = NULL;
@@ -11980,7 +12476,6 @@ erts_debug_verify_clean_empty_process(Process* p)
ASSERT(p->bif_timers == NULL);
ASSERT(p->dictionary == NULL);
ASSERT(p->catches == 0);
- ASSERT(p->cp == NULL);
ASSERT(p->i == NULL);
ASSERT(p->current == NULL);
@@ -12041,7 +12536,7 @@ delete_process(Process* p)
if (pbt)
erts_free(ERTS_ALC_T_BPD, (void *) pbt);
- erts_destroy_nif_export(p);
+ erts_destroy_nfunc(p);
/* Cleanup psd */
@@ -12261,6 +12756,156 @@ erts_proc_exit_handle_dist_monitor(ErtsMonitor *mon, void *vctxt, Sint reds)
return reds_consumed;
}
+static int
+proc_exit_handle_pend_spawn_monitors(ErtsMonitor *mon, void *vctxt, Sint reds)
+{
+ ErtsProcExitContext *ctxt = (ErtsProcExitContext *) vctxt;
+ Process *c_p = ctxt->c_p;
+ Eterm reason = ctxt->reason;
+ Eterm item, *hp;
+ Uint item_sz;
+ int code;
+ ErtsDSigSendContext ctx;
+ ErtsMonitorData *mdp;
+ ErtsMonLnkDist *dist;
+ DistEntry *dep;
+ ErtsHeapFactory factory;
+ Sint reds_consumed = 0;
+
+ ASSERT(c_p->flags & F_DISABLE_GC);
+ ASSERT(erts_monitor_is_origin(mon) && mon->type == ERTS_MON_TYPE_DIST_PROC);
+ ASSERT(ctxt->dist_state == NIL);
+ ASSERT(!ctxt->wait_pend_spawn_monitor);
+ ASSERT(!ctxt->yield);
+
+ ASSERT(mon->flags & ERTS_ML_FLG_SPAWN_PENDING);
+
+ mdp = erts_monitor_to_data(mon);
+ dist = ((ErtsMonitorDataExtended *) mdp)->dist;
+
+ if (!(mon->flags & ERTS_ML_FLG_SPAWN_LINK)) {
+ /* Just cleanup... */
+ if (!erts_dist_pend_spawn_exit_delete(&mdp->target)) {
+ mdp = NULL;
+ }
+ goto done;
+ }
+
+ code = erts_dist_pend_spawn_exit_parent_wait(c_p,
+ ERTS_PROC_LOCK_MAIN,
+ mon);
+ if (code == 0) {
+ /* Connection closing; cleanup... */
+ mdp = NULL;
+ goto done;
+ }
+
+ if (code < 0) {
+ /* We got suspended need to wait for spawn-reply... */
+ ctxt->wait_pend_spawn_monitor = mon;
+ ctxt->yield = !0;
+ return reds;
+ }
+
+ ASSERT(is_external_pid(mon->other.item) || is_atom(mon->other.item));
+
+ /* If other.item is an atom the spawn failed... */
+
+ if (is_not_external_pid(mon->other.item))
+ goto done; /* Cleanup */
+
+ if (mon->flags & ERTS_ML_FLG_SPAWN_ABANDONED)
+ reason = am_abandoned;
+
+ /* Send exit signal... */
+ dep = external_pid_dist_entry(mon->other.item);
+
+ 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;
+ erts_factory_proc_init(&factory, c_p);
+ item_sz = size_object(mon->other.item);
+ hp = erts_produce_heap(&factory, item_sz, 0);
+ item = copy_struct(mon->other.item, item_sz, &hp, factory.off_heap);
+ erts_factory_close(&factory);
+ code = erts_dsig_send_exit_tt(&ctx,
+ c_p->common.id,
+ item,
+ reason,
+ SEQ_TRACE_TOKEN(c_p));
+ reds_consumed = reds - (ctx.reds / TERM_TO_BINARY_LOOP_FACTOR);
+ switch (code) {
+ case ERTS_DSIG_SEND_YIELD:
+ reds_consumed = reds; /* force yield */
+ ctxt->yield = 1;
+ break;
+ case ERTS_DSIG_SEND_CONTINUE:
+ ctxt->dist_state = erts_dsend_export_trap_context(c_p, &ctx);
+ reds_consumed = reds; /* force yield */
+ ctxt->yield = 1;
+ break;
+ case ERTS_DSIG_SEND_OK:
+ break;
+ case ERTS_DSIG_SEND_TOO_LRG:
+ erts_kill_dist_connection(dep, dist->connection_id);
+ break;
+ default:
+ ASSERT(! "Invalid dsig send exit result");
+ break;
+ }
+ break;
+ default:
+ ASSERT(! "Invalid dsig prep exit result");
+ break;
+ }
+
+done:
+
+ if (mdp)
+ erts_monitor_release_both(mdp);
+ else
+ erts_monitor_release(mon);
+
+ return reds_consumed;
+}
+
+void
+erts_proc_exit_dist_demonitor(Process *c_p, DistEntry *dep, Uint32 conn_id,
+ Eterm ref, Eterm watched)
+{
+ ErtsDSigSendContext ctx;
+ int code;
+
+ ASSERT(is_internal_ref(ref));
+ ASSERT(is_atom(watched) || is_external_pid(watched));
+
+ 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 (conn_id == ctx.connection_id) {
+ code = erts_dsig_send_demonitor(&ctx,
+ c_p->common.id,
+ watched,
+ ref);
+ ASSERT(code == ERTS_DSIG_SEND_OK);
+ }
+ default:
+ break;
+ }
+}
+
int
erts_proc_exit_handle_monitor(ErtsMonitor *mon, void *vctxt, Sint reds)
{
@@ -12381,10 +13026,15 @@ erts_proc_exit_handle_monitor(ErtsMonitor *mon, void *vctxt, Sint reds)
case ERTS_MON_TYPE_DIST_PROC: {
ErtsMonLnkDist *dist;
DistEntry *dep;
- ErtsDSigSendContext ctx;
- int code;
Eterm watched;
+ if (mon->flags & ERTS_ML_FLG_SPAWN_PENDING) {
+ if (!erts_dist_pend_spawn_exit_parent_setup(mon))
+ break; /* Drop it... */
+ erts_monitor_tree_insert(&ctxt->pend_spawn_monitors, mon);
+ return 1;
+ }
+
mdp = erts_monitor_to_data(mon);
dist = ((ErtsMonitorDataExtended *) mdp)->dist;
ASSERT(dist);
@@ -12398,21 +13048,8 @@ erts_proc_exit_handle_monitor(ErtsMonitor *mon, void *vctxt, Sint reds)
ASSERT(is_external_pid(watched));
dep = external_pid_dist_entry(watched);
}
- 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_demonitor(&ctx,
- c_p->common.id,
- watched,
- mdp->ref);
- ASSERT(code == ERTS_DSIG_SEND_OK);
- }
- default:
- break;
- }
+ erts_proc_exit_dist_demonitor(c_p, dep, dist->connection_id,
+ mdp->ref, watched);
if (!erts_monitor_dist_delete(&mdp->target))
mdp = NULL;
res = 100;
@@ -12676,6 +13313,7 @@ enum continue_exit_phase {
ERTS_CONTINUE_EXIT_HANDLE_PROC_SIG,
ERTS_CONTINUE_EXIT_DIST_LINKS,
ERTS_CONTINUE_EXIT_DIST_MONITORS,
+ ERTS_CONTINUE_EXIT_DIST_PEND_SPAWN_MONITORS,
ERTS_CONTINUE_EXIT_DONE,
};
@@ -12925,6 +13563,8 @@ restart:
trap_state->pectxt.reason = trap_state->reason;
trap_state->pectxt.dist_links = NULL;
trap_state->pectxt.dist_monitors = NULL;
+ trap_state->pectxt.pend_spawn_monitors = NULL;
+ trap_state->pectxt.wait_pend_spawn_monitor = NULL;
trap_state->pectxt.dist_state = NIL;
trap_state->pectxt.yield = 0;
@@ -12981,8 +13621,11 @@ restart:
case ERTS_CONTINUE_EXIT_HANDLE_PROC_SIG: {
Sint r = reds;
- if (!erts_proc_sig_handle_exit(p, &r))
+ if (!erts_proc_sig_handle_exit(p, &r,
+ &trap_state->pectxt.pend_spawn_monitors,
+ trap_state->reason)) {
goto yield;
+ }
reds -= r;
@@ -13047,6 +13690,31 @@ restart:
if (reds <= 0 || trap_state->pectxt.yield)
goto yield;
+ trap_state->phase = ERTS_CONTINUE_EXIT_DIST_PEND_SPAWN_MONITORS;
+ }
+ case ERTS_CONTINUE_EXIT_DIST_PEND_SPAWN_MONITORS: {
+
+ if (is_not_nil(trap_state->pectxt.dist_state))
+ goto continue_dist_send;
+
+ if (trap_state->pectxt.wait_pend_spawn_monitor) {
+ ErtsMonitor *mon = trap_state->pectxt.wait_pend_spawn_monitor;
+ trap_state->pectxt.wait_pend_spawn_monitor = NULL;
+ reds -= (proc_exit_handle_pend_spawn_monitors(
+ mon, (void *) &trap_state->pectxt, reds));
+ if (reds <= 0 || trap_state->pectxt.yield)
+ goto yield;
+ }
+
+ reds = erts_monitor_tree_foreach_delete_yielding(
+ &trap_state->pectxt.pend_spawn_monitors,
+ proc_exit_handle_pend_spawn_monitors,
+ (void *) &trap_state->pectxt,
+ &trap_state->yield_state,
+ reds);
+ if (reds <= 0 || trap_state->pectxt.yield)
+ goto yield;
+
trap_state->phase = ERTS_CONTINUE_EXIT_DONE;
}
case ERTS_CONTINUE_EXIT_DONE: {
@@ -13266,9 +13934,6 @@ erts_program_counter_info(fmtfn_t to, void *to_arg, Process *p)
erts_print(to, to_arg, "Program counter: %p (", p->i);
print_function_from_pc(to, to_arg, p->i);
erts_print(to, to_arg, ")\n");
- erts_print(to, to_arg, "CP: %p (", p->cp);
- print_function_from_pc(to, to_arg, p->cp);
- erts_print(to, to_arg, ")\n");
state = erts_atomic32_read_acqb(&p->state);
if (!(state & (ERTS_PSFLG_RUNNING
| ERTS_PSFLG_RUNNING_SYS
@@ -13545,9 +14210,6 @@ static void print_current_process_info(fmtfn_t to, void *to_arg,
erts_print(to, to_arg, "Current Process Program counter: %p (", p->i);
print_function_from_pc(to, to_arg, p->i);
erts_print(to, to_arg, ")\n");
- erts_print(to, to_arg, "Current Process CP: %p (", p->cp);
- print_function_from_pc(to, to_arg, p->cp);
- erts_print(to, to_arg, ")\n");
/* Getting this stacktrace can segfault if we are very very
unlucky if called while a process is being garbage collected.
@@ -13579,7 +14241,7 @@ static void print_current_process_info(fmtfn_t to, void *to_arg,
*
* A BIF that calls this should make sure to schedule out to never come back:
* erts_halt(code);
- * ERTS_BIF_YIELD1(bif_export[BIF_erlang_halt_1], BIF_P, NIL);
+ * ERTS_BIF_YIELD1(&bif_trap_export[BIF_erlang_halt_1], BIF_P, NIL);
*/
void erts_halt(int code)
{
diff --git a/erts/emulator/beam/erl_process.h b/erts/emulator/beam/erl_process.h
index d311122381..f38008004f 100644
--- a/erts/emulator/beam/erl_process.h
+++ b/erts/emulator/beam/erl_process.h
@@ -812,7 +812,7 @@ erts_reset_max_len(ErtsRunQueue *rq, ErtsRunQueueInfo *rqi)
#define ERTS_PSD_SCHED_ID 2
#define ERTS_PSD_CALL_TIME_BP 3
#define ERTS_PSD_DELAYED_GC_TASK_QS 4
-#define ERTS_PSD_NIF_TRAP_EXPORT 5
+#define ERTS_PSD_NFUNC_TRAP_WRAPPER 5
#define ERTS_PSD_ETS_OWNED_TABLES 6
#define ERTS_PSD_ETS_FIXED_TABLES 7
#define ERTS_PSD_DIST_ENTRY 8
@@ -849,8 +849,8 @@ typedef struct {
#define ERTS_PSD_DELAYED_GC_TASK_QS_GET_LOCKS ERTS_PROC_LOCK_MAIN
#define ERTS_PSD_DELAYED_GC_TASK_QS_SET_LOCKS ERTS_PROC_LOCK_MAIN
-#define ERTS_PSD_NIF_TRAP_EXPORT_GET_LOCKS ERTS_PROC_LOCK_MAIN
-#define ERTS_PSD_NIF_TRAP_EXPORT_SET_LOCKS ERTS_PROC_LOCK_MAIN
+#define ERTS_PSD_NFUNC_TRAP_WRAPPER_GET_LOCKS ERTS_PROC_LOCK_MAIN
+#define ERTS_PSD_NFUNC_TRAP_WRAPPER_SET_LOCKS ERTS_PROC_LOCK_MAIN
#define ERTS_PSD_ETS_OWNED_TABLES_GET_LOCKS ERTS_PROC_LOCK_STATUS
#define ERTS_PSD_ETS_OWNED_TABLES_SET_LOCKS ERTS_PROC_LOCK_STATUS
@@ -975,7 +975,6 @@ struct process {
unsigned max_arg_reg; /* Maximum number of argument registers available. */
Eterm def_arg_reg[6]; /* Default array for argument registers. */
- BeamInstr* cp; /* (untagged) Continuation pointer (for threaded code). */
BeamInstr* i; /* Program counter for threaded code. */
Sint catches; /* Number of catches on stack */
Sint fcalls; /*
@@ -1326,12 +1325,46 @@ void erts_check_for_holes(Process* p);
* Possible flags for the flags field in ErlSpawnOpts below.
*/
-#define SPO_LINK 1
-#define SPO_USE_ARGS 2
-#define SPO_MONITOR 4
-#define SPO_SYSTEM_PROC 8
-#define SPO_OFF_HEAP_MSGQ 16
-#define SPO_ON_HEAP_MSGQ 32
+#define SPO_IX_LINK 0
+#define SPO_IX_MONITOR 1
+#define SPO_IX_SYSTEM_PROC 2
+#define SPO_IX_OFF_HEAP_MSGQ 3
+#define SPO_IX_ON_HEAP_MSGQ 4
+#define SPO_IX_MIN_HEAP_SIZE 5
+#define SPO_IX_MIN_VHEAP_SIZE 6
+#define SPO_IX_PRIORITY 7
+#define SPO_IX_MAX_GEN_GCS 8
+#define SPO_IX_MAX_HEAP_SIZE 9
+#define SPO_IX_SCHEDULER 10
+#define SPO_IX_ASYNC 11
+#define SPO_IX_NO_SMSG 12
+#define SPO_IX_NO_EMSG 13
+
+#define SPO_NO_INDICES (SPO_IX_ASYNC+1)
+
+#define SPO_LINK (1 << SPO_IX_LINK)
+#define SPO_MONITOR (1 << SPO_IX_MONITOR)
+#define SPO_SYSTEM_PROC (1 << SPO_IX_SYSTEM_PROC)
+#define SPO_OFF_HEAP_MSGQ (1 << SPO_IX_OFF_HEAP_MSGQ)
+#define SPO_ON_HEAP_MSGQ (1 << SPO_IX_ON_HEAP_MSGQ)
+#define SPO_MIN_HEAP_SIZE (1 << SPO_IX_MIN_HEAP_SIZE)
+#define SPO_MIN_VHEAP_SIZE (1 << SPO_IX_MIN_VHEAP_SIZE)
+#define SPO_PRIORITY (1 << SPO_IX_PRIORITY)
+#define SPO_MAX_GEN_GCS (1 << SPO_IX_MAX_GEN_GCS)
+#define SPO_MAX_HEAP_SIZE (1 << SPO_IX_MAX_HEAP_SIZE)
+#define SPO_SCHEDULER (1 << SPO_IX_SCHEDULER)
+#define SPO_ASYNC (1 << SPO_IX_ASYNC)
+#define SPO_NO_SMSG (1 << SPO_IX_NO_SMSG)
+#define SPO_NO_EMSG (1 << SPO_IX_NO_EMSG)
+
+#define SPO_MAX_FLAG SPO_NO_EMSG
+
+#define SPO_USE_ARGS \
+ (SPO_MIN_HEAP_SIZE \
+ | SPO_PRIORITY \
+ | SPO_MAX_GEN_GCS \
+ | SPO_MAX_HEAP_SIZE \
+ | SPO_SCHEDULER)
extern int ERTS_WRITE_UNLIKELY(erts_default_spo_flags);
@@ -1341,7 +1374,22 @@ extern int ERTS_WRITE_UNLIKELY(erts_default_spo_flags);
typedef struct {
int flags;
int error_code; /* Error code returned from create_process(). */
- Eterm mref; /* Monitor ref returned (if SPO_MONITOR was given). */
+ Eterm mref; /* Monitor ref returned (if SPO_MONITOR was given).
+ (output if local; input if distributed) */
+
+ int multi_set;
+
+ Eterm tag; /* If SPO_ASYNC */
+ Eterm opts; /* Option list for seq-trace... */
+
+ /* Input fields used for distributed spawn only */
+ Eterm parent_id;
+ Eterm group_leader;
+ Eterm mfa;
+ DistEntry *dist_entry;
+ ErtsDistExternal *edep;
+ ErlHeapFragment *ede_hfrag;
+ Eterm token;
/*
* The following items are only initialized if the SPO_USE_ARGS flag is set.
@@ -1354,6 +1402,7 @@ typedef struct {
Uint max_heap_size; /* Maximum heap size in words */
Uint max_heap_flags; /* Maximum heap flags (kill | log) */
int scheduler;
+
} ErlSpawnOpts;
/*
@@ -1541,6 +1590,7 @@ extern int erts_system_profile_ts_type;
#define SEQ_TRACE_SEND (1 << 0)
#define SEQ_TRACE_RECEIVE (1 << 1)
#define SEQ_TRACE_PRINT (1 << 2)
+/* (This three-bit gap contains the timestamp.) */
#define ERTS_SEQ_TRACE_FLAGS_TS_TYPE_SHIFT 3
@@ -1852,6 +1902,12 @@ Eterm erts_bind_schedulers(Process *c_p, Eterm how);
ErtsRunQueue *erts_schedid2runq(Uint);
Process *erts_schedule(ErtsSchedulerData *, Process*, int);
void erts_schedule_misc_op(void (*)(void *), void *);
+int erts_parse_spawn_opts(ErlSpawnOpts *sop, Eterm opts_list, Eterm *tag,
+ int success_message_opt);
+void
+erts_send_local_spawn_reply(Process *parent, ErtsProcLocks parent_locks,
+ Process *child, Eterm tag, Eterm ref,
+ Eterm result, Eterm token);
Eterm erl_create_process(Process*, Eterm, Eterm, Eterm, ErlSpawnOpts*);
void erts_set_self_exiting(Process *, Eterm);
void erts_do_exit_process(Process*, Eterm);
@@ -1882,12 +1938,17 @@ typedef struct {
Eterm reason;
ErtsLink *dist_links;
ErtsMonitor *dist_monitors;
+ ErtsMonitor *pend_spawn_monitors;
+ ErtsMonitor *wait_pend_spawn_monitor;
Eterm dist_state;
int yield;
} ErtsProcExitContext;
int erts_proc_exit_handle_monitor(ErtsMonitor *mon, void *vctxt, Sint reds);
int erts_proc_exit_handle_link(ErtsLink *lnk, void *vctxt, Sint reds);
+void erts_proc_exit_dist_demonitor(Process *c_p, DistEntry *dep, Uint32 conn_id,
+ Eterm ref, Eterm watched);
+
Eterm erts_get_process_priority(erts_aint32_t state);
Eterm erts_set_process_priority(Process *p, Eterm prio);
@@ -1917,6 +1978,7 @@ Uint erts_debug_nbalance(void);
#define ERTS_DEBUG_WAIT_COMPLETED_DEALLOCATIONS (1 << 0)
#define ERTS_DEBUG_WAIT_COMPLETED_TIMER_CANCELLATIONS (1 << 1)
#define ERTS_DEBUG_WAIT_COMPLETED_AUX_WORK (1 << 2)
+#define ERTS_DEBUG_WAIT_COMPLETED_THREAD_PROGRESS (1 << 3)
int erts_debug_wait_completed(Process *c_p, int flags);
@@ -2089,10 +2151,10 @@ erts_psd_set(Process *p, int ix, void *data)
#define ERTS_PROC_SET_DELAYED_GC_TASK_QS(P, PBT) \
((ErtsProcSysTaskQs *) erts_psd_set((P), ERTS_PSD_DELAYED_GC_TASK_QS, (void *) (PBT)))
-#define ERTS_PROC_GET_NIF_TRAP_EXPORT(P) \
- erts_psd_get((P), ERTS_PSD_NIF_TRAP_EXPORT)
-#define ERTS_PROC_SET_NIF_TRAP_EXPORT(P, NTE) \
- erts_psd_set((P), ERTS_PSD_NIF_TRAP_EXPORT, (void *) (NTE))
+#define ERTS_PROC_GET_NFUNC_TRAP_WRAPPER(P) \
+ erts_psd_get((P), ERTS_PSD_NFUNC_TRAP_WRAPPER)
+#define ERTS_PROC_SET_NFUNC_TRAP_WRAPPER(P, NTE) \
+ erts_psd_set((P), ERTS_PSD_NFUNC_TRAP_WRAPPER, (void *) (NTE))
#define ERTS_PROC_GET_DIST_ENTRY(P) \
((DistEntry *) erts_psd_get((P), ERTS_PSD_DIST_ENTRY))
@@ -2696,7 +2758,9 @@ ERTS_GLB_INLINE void erts_sched_poke(ErtsSchedulerSleepInfo *ssi);
void erts_aux_thread_poke(void);
ERTS_GLB_INLINE Uint32 erts_sched_local_random_hash_64_to_32_shift(Uint64 key);
ERTS_GLB_INLINE Uint32 erts_sched_local_random(Uint additional_seed);
-
+#ifdef DEBUG
+ERTS_GLB_INLINE float erts_sched_local_random_float(Uint additional_seed);
+#endif
#if ERTS_GLB_INLINE_INCL_FUNC_DEF
@@ -2746,6 +2810,20 @@ Uint32 erts_sched_local_random(Uint additional_seed)
return erts_sched_local_random_hash_64_to_32_shift(seed);
}
+#ifdef DEBUG
+
+/*
+ * This function returns a random float between 0.0 and 1.0.
+ */
+ERTS_GLB_INLINE
+float erts_sched_local_random_float(Uint additional_seed)
+{
+ Uint32 rnd = erts_sched_local_random(additional_seed);
+ return (float)(((double)rnd)/((double)ERTS_UINT32_MAX));
+}
+
+#endif /* #ifdef DEBUG */
+
#endif /* #if ERTS_GLB_INLINE_INCL_FUNC_DEF */
diff --git a/erts/emulator/beam/erl_trace.c b/erts/emulator/beam/erl_trace.c
index cc483a2148..f1f066be46 100644
--- a/erts/emulator/beam/erl_trace.c
+++ b/erts/emulator/beam/erl_trace.c
@@ -830,7 +830,7 @@ trace_receive(Process* receiver,
}
int
-seq_trace_update_send(Process *p)
+seq_trace_update_serial(Process *p)
{
ErtsTracer seq_tracer = erts_get_system_seq_tracer();
ASSERT((is_tuple(SEQ_TRACE_TOKEN(p)) || is_nil(SEQ_TRACE_TOKEN(p))));
@@ -853,6 +853,18 @@ seq_trace_update_send(Process *p)
return 1;
}
+void
+erts_seq_trace_update_node_token(Eterm token)
+{
+ Eterm serial;
+ Uint serial_num;
+ SEQ_TRACE_T_SENDER(token) = erts_this_dist_entry->sysname;
+ serial = SEQ_TRACE_T_SERIAL(token);
+ serial_num = unsigned_val(serial);
+ serial_num++;
+ SEQ_TRACE_T_SERIAL(token) = make_small(serial_num);
+}
+
/* Send a sequential trace message to the sequential tracer.
* p is the caller (which contains the trace token),
@@ -929,6 +941,9 @@ seq_trace_output_generic(Eterm token, Eterm msg, Uint type,
#undef LOCAL_HEAP_SIZE
}
+
+
+
/* Send {trace_ts, Pid, return_to, {Mod, Func, Arity}, Timestamp}
* or {trace, Pid, return_to, {Mod, Func, Arity}}
*/
diff --git a/erts/emulator/beam/erl_trace.h b/erts/emulator/beam/erl_trace.h
index af38ef52db..2c5adf3198 100644
--- a/erts/emulator/beam/erl_trace.h
+++ b/erts/emulator/beam/erl_trace.h
@@ -142,12 +142,6 @@ void monitor_generic(Process *p, Eterm type, Eterm spec);
Uint erts_trace_flag2bit(Eterm flag);
int erts_trace_flags(Eterm List,
Uint *pMask, ErtsTracer *pTracer, int *pCpuTimestamp);
-Eterm erts_bif_trace(int bif_index, Process* p, Eterm* args, BeamInstr *I);
-Eterm
-erts_bif_trace_epilogue(Process *p, Eterm result, int applying,
- Export* ep, BeamInstr *cp, Uint32 flags,
- Uint32 flags_meta, BeamInstr* I,
- ErtsTracer meta_tracer);
void erts_send_pending_trace_msgs(ErtsSchedulerData *esdp);
#define ERTS_CHK_PEND_TRACE_MSGS(ESDP) \
@@ -163,7 +157,10 @@ seq_trace_output_generic((token), (msg), (type), (receiver), NULL, (exitfrom))
void seq_trace_output_generic(Eterm token, Eterm msg, Uint type,
Eterm receiver, Process *process, Eterm exitfrom);
-int seq_trace_update_send(Process *process);
+/* Bump the sequence number if tracing is enabled; must be used before sending
+ * send trace messages. */
+int seq_trace_update_serial(Process *process);
+void erts_seq_trace_update_node_token(Eterm token);
Eterm erts_seq_trace(Process *process,
Eterm atom_type, Eterm atom_true_or_false,
diff --git a/erts/emulator/beam/erl_utils.h b/erts/emulator/beam/erl_utils.h
index 430ac305c5..895f8e5e27 100644
--- a/erts/emulator/beam/erl_utils.h
+++ b/erts/emulator/beam/erl_utils.h
@@ -70,6 +70,7 @@ int erts_fit_in_bits_uint(Uint);
Sint erts_list_length(Eterm);
int erts_is_builtin(Eterm, Eterm, int);
Uint32 make_hash2(Eterm);
+Uint32 trapping_make_hash2(Eterm, Eterm*, struct process*);
Uint32 make_hash(Eterm);
Uint32 make_internal_hash(Eterm, Uint32 salt);
@@ -130,15 +131,28 @@ Sint erts_cmp_compound(Eterm, Eterm, int, int);
#define CMP_GT(a,b) ((a) != (b) && CMP((a),(b)) > 0)
#define CMP_EQ_ACTION(X,Y,Action) \
- if ((X) != (Y)) { CMP_SPEC((X),(Y),!=,Action,1); }
+ if ((X) != (Y)) { EQ_SPEC((X),(Y),!=,Action); }
#define CMP_NE_ACTION(X,Y,Action) \
- if ((X) == (Y)) { Action; } else { CMP_SPEC((X),(Y),==,Action,1); }
-#define CMP_GE_ACTION(X,Y,Action) \
- if ((X) != (Y)) { CMP_SPEC((X),(Y),<,Action,0); }
+ if ((X) == (Y)) { Action; } else { EQ_SPEC((X),(Y),==,Action); }
+
+#define EQ_SPEC(X,Y,Op,Action) \
+ if (is_both_immed(X, Y)) { \
+ if (X Op Y) { Action; }; \
+ } else if (is_float(X) && is_float(Y)) { \
+ FloatDef af, bf; \
+ GET_DOUBLE(X, af); \
+ GET_DOUBLE(Y, bf); \
+ if (af.fd Op bf.fd) { Action; }; \
+ } else { \
+ if (erts_cmp_compound(X,Y,0,1) Op 0) { Action; }; \
+ }
+
+#define CMP_GE_ACTION(X,Y,Action) \
+ if ((X) != (Y)) { CMP_SPEC((X),(Y),<,Action); }
#define CMP_LT_ACTION(X,Y,Action) \
- if ((X) == (Y)) { Action; } else { CMP_SPEC((X),(Y),>=,Action,0); }
+ if ((X) == (Y)) { Action; } else { CMP_SPEC((X),(Y),>=,Action); }
-#define CMP_SPEC(X,Y,Op,Action,EqOnly) \
+#define CMP_SPEC(X,Y,Op,Action) \
if (is_atom(X) && is_atom(Y)) { \
if (erts_cmp_atoms(X, Y) Op 0) { Action; }; \
} else if (is_both_small(X, Y)) { \
@@ -149,7 +163,26 @@ Sint erts_cmp_compound(Eterm, Eterm, int, int);
GET_DOUBLE(Y, bf); \
if (af.fd Op bf.fd) { Action; }; \
} else { \
- if (erts_cmp_compound(X,Y,0,EqOnly) Op 0) { Action; }; \
+ if (erts_cmp_compound(X,Y,0,0) Op 0) { Action; }; \
+ }
+
+/*
+ * When either operand for is_lt or is_ge is a literal, that literal is
+ * almost always an integer and almost never an atom. Therefore, only
+ * special case the comparison of small integers before calling the
+ * general compare function.
+ */
+
+#define CMP_GE_LITERAL_ACTION(X,Y,Action) \
+ if ((X) != (Y)) { CMP_LITERAL_SPEC((X),(Y),<,Action); }
+#define CMP_LT_LITERAL_ACTION(X,Y,Action) \
+ if ((X) == (Y)) { Action; } else { CMP_LITERAL_SPEC((X),(Y),>=,Action); }
+
+#define CMP_LITERAL_SPEC(X,Y,Op,Action) \
+ if (is_both_small(X, Y)) { \
+ if (signed_val(X) Op signed_val(Y)) { Action; }; \
+ } else { \
+ if (erts_cmp_compound(X,Y,0,0) Op 0) { Action; }; \
}
#define erts_float_comp(x,y) (((x)<(y)) ? -1 : (((x)==(y)) ? 0 : 1))
diff --git a/erts/emulator/beam/erlang_dtrace.d b/erts/emulator/beam/erlang_dtrace.d
index 8792138d53..2af2af2b37 100644
--- a/erts/emulator/beam/erlang_dtrace.d
+++ b/erts/emulator/beam/erlang_dtrace.d
@@ -176,7 +176,7 @@ provider erlang {
* Fired whenever a user function returns.
*
* @param p the PID (string form) of the process
- * @param mfa the m:f/a of the function
+ * @param mfa the m:f/a of the function being returned from
* @param depth the stack depth
*/
probe function__return(char *p, char *mfa, int depth);
@@ -193,7 +193,7 @@ provider erlang {
* Fired whenever a Built In Function returns.
*
* @param p the PID (string form) of the process
- * @param mfa the m:f/a of the function
+ * @param mfa the m:f/a of the function being returned from
*/
probe bif__return(char *p, char *mfa);
@@ -209,7 +209,7 @@ provider erlang {
* Fired whenever a Native Function returns.
*
* @param p the PID (string form) of the process
- * @param mfa the m:f/a of the function
+ * @param mfa the m:f/a of the function being returned from
*/
probe nif__return(char *p, char *mfa);
@@ -617,29 +617,6 @@ provider erlang {
probe driver__stop_select(char *name);
- /* Async driver pool */
-
- /**
- * Show the post-add length of the async driver thread pool member's queue.
- *
- * NOTE: The port name is not available: additional lock(s) must
- * be acquired in order to get the port name safely in an SMP
- * environment. The same is true for the aio__pool_get probe.
- *
- * @param port the Port (string form)
- * @param new queue length
- */
- probe aio_pool__add(char *, int);
-
- /**
- * Show the post-get length of the async driver thread pool member's queue.
- *
- * @param port the Port (string form)
- * @param new queue length
- */
- probe aio_pool__get(char *, int);
-
-
/*
* The set of probes called by the erlang tracer nif backend. In order
* to receive events on these you both have to enable tracing in erlang
@@ -678,16 +655,6 @@ provider erlang {
*/
/*
- * NOTE: For file_drv_return + SMP + R14B03 (and perhaps other
- * releases), the sched-thread-id will be the same as the
- * work-thread-id: erl_async.c's async_main() function
- * will call the asynchronous invoke function and then
- * immediately call the drivers ready_async function while
- * inside the same I/O worker pool thread.
- * For R14B03's source, see erl_async.c lines 302-317.
- */
-
-/*
* The set of probes for use by Erlang code ... moved to here from
* lib/runtime_tools/c_src/dtrace_user.d until a more portable solution
* is found. This move pollutes the Erlang VM with functions that are
diff --git a/erts/emulator/beam/erlang_lttng.h b/erts/emulator/beam/erlang_lttng.h
index 9b93d77f6e..213830e6fd 100644
--- a/erts/emulator/beam/erlang_lttng.h
+++ b/erts/emulator/beam/erlang_lttng.h
@@ -30,21 +30,6 @@
#include <lttng/tracepoint.h>
-/* Schedulers */
-
-TRACEPOINT_EVENT(
- org_erlang_otp,
- scheduler_poll,
- TP_ARGS(
- int, id,
- int, runnable
- ),
- TP_FIELDS(
- ctf_integer(int, scheduler, id)
- ctf_integer(int, runnable, runnable)
- )
-)
-
#ifndef LTTNG_CARRIER_STATS
#define LTTNG_CARRIER_STATS
typedef struct {
@@ -292,35 +277,6 @@ TRACEPOINT_EVENT(
)
)
-/* Async pool */
-
-TRACEPOINT_EVENT(
- org_erlang_otp,
- aio_pool_get,
- TP_ARGS(
- char*, port,
- int, length
- ),
- TP_FIELDS(
- ctf_string(port, port)
- ctf_integer(int, length, length)
- )
-)
-
-TRACEPOINT_EVENT(
- org_erlang_otp,
- aio_pool_put,
- TP_ARGS(
- char*, port,
- int, length
- ),
- TP_FIELDS(
- ctf_string(port, port)
- ctf_integer(int, length, length)
- )
-)
-
-
/* Memory Allocator */
TRACEPOINT_EVENT(
diff --git a/erts/emulator/beam/error.h b/erts/emulator/beam/error.h
index 64c08b1570..cc80fd09df 100644
--- a/erts/emulator/beam/error.h
+++ b/erts/emulator/beam/error.h
@@ -66,13 +66,13 @@
#define EXF_OFFSET EXTAG_BITS
#define EXF_BITS 7
-#define EXF_PANIC (1<<(0+EXF_OFFSET)) /* ignore catches */
-#define EXF_THROWN (1<<(1+EXF_OFFSET)) /* nonlocal return */
-#define EXF_LOG (1<<(2+EXF_OFFSET)) /* write to logger on termination */
-#define EXF_NATIVE (1<<(3+EXF_OFFSET)) /* occurred in native code */
-#define EXF_SAVETRACE (1<<(4+EXF_OFFSET)) /* save stack trace in internal form */
-#define EXF_ARGLIST (1<<(5+EXF_OFFSET)) /* has arglist for top of trace */
-#define EXF_RESTORE_NIF (1<<(6+EXF_OFFSET)) /* restore original bif/nif */
+#define EXF_PANIC (1<<(0+EXF_OFFSET)) /* ignore catches */
+#define EXF_THROWN (1<<(1+EXF_OFFSET)) /* nonlocal return */
+#define EXF_LOG (1<<(2+EXF_OFFSET)) /* write to logger on termination */
+#define EXF_NATIVE (1<<(3+EXF_OFFSET)) /* occurred in native code */
+#define EXF_SAVETRACE (1<<(4+EXF_OFFSET)) /* save stack trace in internal form */
+#define EXF_ARGLIST (1<<(5+EXF_OFFSET)) /* has arglist for top of trace */
+#define EXF_RESTORE_NFUNC (1<<(6+EXF_OFFSET)) /* restore original bif/nif */
#define EXC_FLAGBITS (((1<<(EXF_BITS+EXF_OFFSET))-1) \
& ~((1<<(EXF_OFFSET))-1))
@@ -155,10 +155,8 @@
/* No matching try clause */
#define EXC_NOTSUP ((17 << EXC_OFFSET) | EXC_ERROR)
/* Not supported */
-
#define EXC_BADMAP ((18 << EXC_OFFSET) | EXC_ERROR)
/* Bad map */
-
#define EXC_BADKEY ((19 << EXC_OFFSET) | EXC_ERROR)
/* Bad key in map */
diff --git a/erts/emulator/beam/export.c b/erts/emulator/beam/export.c
index 946ffeffb8..af1b1c2892 100644
--- a/erts/emulator/beam/export.c
+++ b/erts/emulator/beam/export.c
@@ -129,14 +129,17 @@ export_alloc(struct export_entry* tmpl_e)
obj->info.mfa.module = tmpl->info.mfa.module;
obj->info.mfa.function = tmpl->info.mfa.function;
obj->info.mfa.arity = tmpl->info.mfa.arity;
- obj->beam[0] = 0;
+ obj->bif_number = -1;
+ obj->is_bif_traced = 0;
+
+ memset(&obj->trampoline, 0, sizeof(obj->trampoline));
+
if (BeamOpsAreInitialized()) {
- obj->beam[0] = BeamOpCodeAddr(op_call_error_handler);
+ obj->trampoline.op = BeamOpCodeAddr(op_call_error_handler);
}
- obj->beam[1] = 0;
for (ix=0; ix<ERTS_NUM_CODE_IX; ix++) {
- obj->addressv[ix] = obj->beam;
+ obj->addressv[ix] = obj->trampoline.raw;
blob->entryv[ix].slot.index = -1;
blob->entryv[ix].ep = &blob->exp;
@@ -196,6 +199,19 @@ init_export_table(void)
}
}
+static struct export_entry* init_template(struct export_templ* templ,
+ Eterm m, Eterm f, unsigned a)
+{
+ templ->entry.ep = &templ->exp;
+ templ->entry.slot.index = -1;
+ templ->exp.info.mfa.module = m;
+ templ->exp.info.mfa.function = f;
+ templ->exp.info.mfa.arity = a;
+ templ->exp.bif_number = -1;
+ templ->exp.is_bif_traced = 0;
+ return &templ->entry;
+}
+
/*
* Return a pointer to the export entry for the given function,
* or NULL otherwise. Notes:
@@ -214,41 +230,15 @@ erts_find_export_entry(Eterm m, Eterm f, unsigned int a,ErtsCodeIndex code_ix);
Export*
erts_find_export_entry(Eterm m, Eterm f, unsigned int a, ErtsCodeIndex code_ix)
{
- HashValue hval = EXPORT_HASH((BeamInstr) m, (BeamInstr) f, (BeamInstr) a);
- int ix;
- HashBucket* b;
-
- ix = hval % export_tables[code_ix].htable.size;
- b = export_tables[code_ix].htable.bucket[ix];
-
- /*
- * Note: We have inlined the code from hash.c for speed.
- */
-
- while (b != (HashBucket*) 0) {
- Export* ep = ((struct export_entry*) b)->ep;
- if (ep->info.mfa.module == m &&
- ep->info.mfa.function == f &&
- ep->info.mfa.arity == a) {
- return ep;
- }
- b = b->next;
- }
+ struct export_templ templ;
+ struct export_entry *ee =
+ hash_fetch(&export_tables[code_ix].htable,
+ init_template(&templ, m, f, a),
+ (H_FUN)export_hash, (HCMP_FUN)export_cmp);
+ if (ee) return ee->ep;
return NULL;
}
-static struct export_entry* init_template(struct export_templ* templ,
- Eterm m, Eterm f, unsigned a)
-{
- templ->entry.ep = &templ->exp;
- templ->entry.slot.index = -1;
- templ->exp.info.mfa.module = m;
- templ->exp.info.mfa.function = f;
- templ->exp.info.mfa.arity = a;
- return &templ->entry;
-}
-
-
/*
* Find the export entry for a loaded function.
* Returns a NULL pointer if the given function is not loaded, or
@@ -268,8 +258,8 @@ erts_find_function(Eterm m, Eterm f, unsigned int a, ErtsCodeIndex code_ix)
ee = hash_get(&export_tables[code_ix].htable, init_template(&templ, m, f, a));
if (ee == NULL ||
- (ee->ep->addressv[code_ix] == ee->ep->beam &&
- ! BeamIsOpCode(ee->ep->beam[0], op_i_generic_breakpoint))) {
+ (ee->ep->addressv[code_ix] == ee->ep->trampoline.raw &&
+ ! BeamIsOpCode(ee->ep->trampoline.op, op_i_generic_breakpoint))) {
return NULL;
}
return ee->ep;
diff --git a/erts/emulator/beam/export.h b/erts/emulator/beam/export.h
index ae8dfa4cf8..91c4844d20 100644
--- a/erts/emulator/beam/export.h
+++ b/erts/emulator/beam/export.h
@@ -31,24 +31,72 @@
typedef struct export
{
- void* addressv[ERTS_NUM_CODE_IX]; /* Pointer to code for function. */
-
- ErtsCodeInfo info; /* MUST be just before beam[] */
-
- /*
- * beam[0]: This entry is 0 unless the 'addressv' field points to it.
- * Threaded code instruction to load function
- * (em_call_error_handler), execute BIF (em_apply_bif),
- * or a breakpoint instruction (op_i_generic_breakpoint).
- * beam[1]: Function pointer to BIF function (for BIFs only),
- * or pointer to threaded code if the module has an
- * on_load function that has not been run yet, or pointer
- * to code if function beam[0] is a breakpoint instruction.
- * Otherwise: 0.
- */
- BeamInstr beam[2];
+ /* Pointer to code for function. */
+ void* addressv[ERTS_NUM_CODE_IX];
+
+ /* Index into bif_table[], or -1 if not a BIF. */
+ int bif_number;
+ /* Non-zero if this is a BIF that's traced. */
+ int is_bif_traced;
+
+ /* This is a small trampoline function that can be used for lazy code
+ * loading, global call tracing, and so on. It's only valid when
+ * addressv points to it and should otherwise be left zeroed.
+ *
+ * Needless to say, the order of the fields below is significant. */
+ ErtsCodeInfo info;
+ union {
+ BeamInstr op; /* Union discriminant. */
+
+ struct {
+ BeamInstr op; /* op_i_generic_breakpoint */
+ BeamInstr address; /* Address of the original function */
+ } breakpoint;
+
+ /* This is used when a module refers to (imports) a function that
+ * hasn't been loaded yet. Upon loading we create an export entry which
+ * redirects to the error_handler so that the appropriate module will
+ * be loaded when called (or crash).
+ *
+ * This is also used when a module has an on_load callback as we need
+ * to defer all calls until the callback returns. `deferred` contains
+ * the address of the original function in this case, and there's an
+ * awkward condiditon where `deferred` may be set while op is zero. See
+ * erlang:finish_after_on_load/2 for details. */
+ struct {
+ BeamInstr op; /* op_call_error_handler, or 0 during the last
+ * phase of code loading when on_load is
+ * present. See above. */
+ BeamInstr deferred;
+ } not_loaded;
+
+ struct {
+ BeamInstr op; /* op_trace_jump_W */
+ BeamInstr address; /* Address of the traced function */
+ } trace;
+
+ BeamInstr raw[2]; /* For use in address comparisons, should not
+ * be tampered directly. */
+ } trampoline;
} Export;
+#ifdef DEBUG
+#define DBG_CHECK_EXPORT(EP, CX) \
+ do { \
+ if((EP)->addressv[CX] == (EP)->trampoline.raw) { \
+ /* The entry currently points at the trampoline, so the
+ * instructions must be valid. */ \
+ ASSERT(((BeamIsOpCode((EP)->trampoline.op, op_i_generic_breakpoint)) && \
+ (EP)->trampoline.breakpoint.address != 0) || \
+ ((BeamIsOpCode((EP)->trampoline.op, op_trace_jump_W)) && \
+ (EP)->trampoline.trace.address != 0) || \
+ /* (EP)->trampoline.not_loaded.deferred may be zero. */ \
+ (BeamIsOpCode((EP)->trampoline.op, op_call_error_handler))); \
+ } \
+ } while(0)
+#else
+#define DBG_CHECK_EXPORT(EP, CX) ((void)(EP), (void)(CX))
+#endif
void init_export_table(void);
void export_info(fmtfn_t, void *);
@@ -71,9 +119,6 @@ extern erts_mtx_t export_staging_lock;
#define export_staging_unlock() erts_mtx_unlock(&export_staging_lock)
#include "beam_load.h" /* For em_* extern declarations */
-#define ExportIsBuiltIn(EntryPtr) \
-(((EntryPtr)->addressv[erts_active_code_ix()] == (EntryPtr)->beam) && \
- (BeamIsOpCode((EntryPtr)->beam[0], op_apply_bif)))
#if ERTS_GLB_INLINE_INCL_FUNC_DEF
diff --git a/erts/emulator/beam/external.c b/erts/emulator/beam/external.c
index 12eda60527..5d91c1b2cb 100644
--- a/erts/emulator/beam/external.c
+++ b/erts/emulator/beam/external.c
@@ -46,23 +46,26 @@
#include "erl_bits.h"
#include "erl_zlib.h"
#include "erl_map.h"
+#include "erl_proc_sig_queue.h"
+#include "erl_trace.h"
+
+#define PASS_THROUGH 'p'
#define in_area(ptr,start,nbytes) ((UWord)((char*)(ptr) - (char*)(start)) < (nbytes))
#define MAX_STRING_LEN 0xffff
-/* MAX value for the creation field in pid, port and reference
- for the local node and for the current external format.
-
- Larger creation values than this are allowed in external pid, port and refs
- encoded with NEW_PID_EXT, NEW_PORT_EXT and NEWER_REFERENCE_EXT.
- The point here is to prepare for future upgrade to 32-bit creation.
- OTP-19 (erts-8.0) can handle big creation values from other (newer) nodes,
- but do not use big creation values for the local node yet,
- as we still may have to communicate with older nodes.
+/*
+ * MAX value for the creation field in pid, port and reference
+ * for the old PID_EXT, PORT_EXT, REFERENCE_EXT and NEW_REFERENCE_EXT.
+ * Older nodes (OTP 19-22) will send us these so we must be able to decode them.
+ *
+ * From OTP 23 DFLAG_BIG_CREATION is mandatory so this node will always
+ * encode with new big 32-bit creations using NEW_PID_EXT, NEW_PORT_EXT
+ * and NEWER_REFERENCE_EXT.
*/
-#define ERTS_MAX_LOCAL_CREATION (3)
-#define is_valid_creation(Cre) ((unsigned)(Cre) <= ERTS_MAX_LOCAL_CREATION)
+#define ERTS_MAX_TINY_CREATION (3)
+#define is_tiny_creation(Cre) ((unsigned)(Cre) <= ERTS_MAX_TINY_CREATION)
#undef ERTS_DEBUG_USE_DIST_SEP
#ifdef DEBUG
@@ -98,13 +101,13 @@
static Export term_to_binary_trap_export;
-static byte* enc_term(ErtsAtomCacheMap *, Eterm, byte*, Uint32, struct erl_off_heap_header** off_heap);
+static byte* enc_term(ErtsAtomCacheMap *, Eterm, byte*, Uint64, struct erl_off_heap_header** off_heap);
struct TTBEncodeContext_;
-static int enc_term_int(struct TTBEncodeContext_*,ErtsAtomCacheMap *acmp, Eterm obj, byte* ep, Uint32 dflags,
+static int enc_term_int(struct TTBEncodeContext_*,ErtsAtomCacheMap *acmp, Eterm obj, byte* ep, Uint64 dflags,
struct erl_off_heap_header** off_heap, Sint *reds, byte **res);
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);
+static byte* enc_atom(ErtsAtomCacheMap *, Eterm, byte*, Uint64);
+static byte* enc_pid(ErtsAtomCacheMap *, Eterm, byte*, Uint64);
struct B2TContext_t;
static byte* dec_term(ErtsDistExternal*, ErtsHeapFactory*, byte*, Eterm*, struct B2TContext_t*, int);
static byte* dec_atom(ErtsDistExternal *, byte*, Eterm*);
@@ -112,19 +115,23 @@ static byte* dec_pid(ErtsDistExternal *, ErtsHeapFactory*, byte*, Eterm*, byte t
static Sint decoded_size(byte *ep, byte* endp, int internal_tags, struct B2TContext_t*);
static BIF_RETTYPE term_to_binary_trap_1(BIF_ALIST_1);
-static Eterm erts_term_to_binary_int(Process* p, Eterm Term, Eterm opts, int level,
- Uint flags, Binary *context_b);
+static Eterm erts_term_to_binary_int(Process* p, Sint bif_ix, Eterm Term, Eterm opts, int level,
+ Uint64 dflags, Binary *context_b, int iovec,
+ Uint fragment_size);
-static Uint encode_size_struct2(ErtsAtomCacheMap *, Eterm, unsigned);
-struct TTBSizeContext_;
-static ErtsExtSzRes encode_size_struct_int(struct TTBSizeContext_*, ErtsAtomCacheMap *acmp,
- Eterm obj, unsigned dflags, Sint *reds, Uint *res);
+static Uint encode_size_struct2(ErtsAtomCacheMap *, Eterm, Uint64);
+static ErtsExtSzRes encode_size_struct_int(TTBSizeContext*, ErtsAtomCacheMap *acmp,
+ Eterm obj, Uint64 dflags, Sint *reds, Uint *res);
static Export binary_to_term_trap_export;
static BIF_RETTYPE binary_to_term_trap_1(BIF_ALIST_1);
-static Sint transcode_dist_obuf(ErtsDistOutputBuf*, DistEntry*, Uint32 dflags, Sint reds);
-
-
+static Sint transcode_dist_obuf(ErtsDistOutputBuf*, DistEntry*, Uint64 dflags, Sint reds);
+static byte *hopefull_bit_binary(TTBEncodeContext* ctx, byte **epp, Binary *pb_val, Eterm pb_term,
+ byte *bytes, byte bitoffs, byte bitsize, Uint sz);
+static void hopefull_export(TTBEncodeContext* ctx, byte **epp, Export* exp, Uint32 dflags,
+ struct erl_off_heap_header** off_heap);
+static void store_in_vec(TTBEncodeContext *ctx, byte *ep, Binary *ohbin, Eterm ohpb,
+ byte *ohp, Uint ohsz);
void erts_init_external(void) {
erts_init_trap_export(&term_to_binary_trap_export,
@@ -221,7 +228,7 @@ erts_destroy_atom_cache_map(ErtsAtomCacheMap *acmp)
}
static ERTS_INLINE void
-insert_acache_map(ErtsAtomCacheMap *acmp, Eterm atom, Uint32 dflags)
+insert_acache_map(ErtsAtomCacheMap *acmp, Eterm atom, Uint64 dflags)
{
if (acmp && acmp->sz < ERTS_MAX_INTERNAL_ATOM_CACHE_ENTRIES) {
int ix;
@@ -237,7 +244,7 @@ insert_acache_map(ErtsAtomCacheMap *acmp, Eterm atom, Uint32 dflags)
}
static ERTS_INLINE int
-get_iix_acache_map(ErtsAtomCacheMap *acmp, Eterm atom, Uint32 dflags)
+get_iix_acache_map(ErtsAtomCacheMap *acmp, Eterm atom, Uint64 dflags)
{
if (!acmp)
return -1;
@@ -257,7 +264,7 @@ get_iix_acache_map(ErtsAtomCacheMap *acmp, Eterm atom, Uint32 dflags)
}
void
-erts_finalize_atom_cache_map(ErtsAtomCacheMap *acmp, Uint32 dflags)
+erts_finalize_atom_cache_map(ErtsAtomCacheMap *acmp, Uint64 dflags)
{
if (acmp) {
int long_atoms = 0; /* !0 if one or more atoms are longer than 255. */
@@ -295,10 +302,16 @@ erts_finalize_atom_cache_map(ErtsAtomCacheMap *acmp, Uint32 dflags)
}
Uint
-erts_encode_ext_dist_header_size(ErtsAtomCacheMap *acmp, Uint fragments)
+erts_encode_ext_dist_header_size(TTBEncodeContext *ctx,
+ ErtsAtomCacheMap *acmp,
+ Uint fragments)
{
- if (!acmp)
- return 0;
+ if (ctx->dflags & DFLAG_PENDING_CONNECT) {
+ /* HOPEFUL_DATA + hopefull flags + hopefull ix + payload ix */
+ return 1 + 8 + 4 + 4;
+ }
+ else if (!acmp && !(ctx->dflags & DFLAG_FRAGMENTS))
+ return 1; /* pass through */
else {
int fix_sz
= 1 /* VERSION_MAGIC */
@@ -306,48 +319,82 @@ erts_encode_ext_dist_header_size(ErtsAtomCacheMap *acmp, Uint fragments)
+ 1 /* dist header flags */
+ 1 /* number of internal cache entries */
;
- ASSERT(acmp->hdr_sz >= 0);
+
if (fragments > 1)
fix_sz += 8 /* sequence id */
+ 8 /* number of fragments */
;
- return fix_sz + acmp->hdr_sz;
+ if (acmp) {
+ ASSERT(acmp->hdr_sz >= 0);
+ fix_sz += acmp->hdr_sz;
+ } else {
+ ASSERT(ctx->dflags & DFLAG_FRAGMENTS);
+ }
+
+ return fix_sz;
}
}
-byte *erts_encode_ext_dist_header_setup(byte *ctl_ext, ErtsAtomCacheMap *acmp,
+byte *erts_encode_ext_dist_header_setup(TTBEncodeContext *ctx,
+ 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. */
- if (!acmp)
- return ctl_ext;
+ if (ctx->dflags & DFLAG_PENDING_CONNECT) {
+ byte *ep = ctl_ext;
+ ep -= 4;
+ ctx->payload_ixp = ep;
+ put_int32(0, ep);
+ ep -= 4;
+ ctx->hopefull_ixp = ep;
+ put_int32(ERTS_NO_HIX, ep);
+ ep -= 8;
+ ctx->hopefull_flagsp = ep;
+ put_int64(0, ep);
+ *--ep = HOPEFUL_DATA;
+ return ep;
+ }
+ else if (!acmp && !(ctx->dflags & DFLAG_FRAGMENTS)) {
+ byte *ep = ctl_ext;
+ *--ep = PASS_THROUGH;
+ return ep;
+ }
else {
int i;
byte *ep = ctl_ext;
- byte dist_hdr_flags = acmp->long_atoms ? ERTS_DIST_HDR_LONG_ATOMS_FLG : 0;
- ASSERT(acmp->hdr_sz >= 0);
- /*
- * Write cache update instructions. Note that this is a purely
- * internal format, never seen on the wire. This section is later
- * rewritten by erts_encode_ext_dist_header_finalize() while updating
- * the cache. We write the header backwards just before the
- * actual term(s).
- */
- for (i = acmp->sz-1; i >= 0; i--) {
- Uint32 aval;
- ASSERT(0 <= acmp->cix[i] && acmp->cix[i] < ERTS_ATOM_CACHE_SIZE);
- ASSERT(i == acmp->cache[acmp->cix[i]].iix);
- ASSERT(is_atom(acmp->cache[acmp->cix[i]].atom));
-
- aval = (Uint32) atom_val(acmp->cache[acmp->cix[i]].atom);
- ep -= 4;
- put_int32(aval, ep);
- ep -= 2;
- put_int16(acmp->cix[i], ep);
- }
- --ep;
- put_int8(acmp->sz, ep);
+ byte dist_hdr_flags = acmp && acmp->long_atoms ? ERTS_DIST_HDR_LONG_ATOMS_FLG : 0;
+ ASSERT(!acmp || acmp->hdr_sz >= 0);
+
+ if (acmp) {
+ /*
+ * Write cache update instructions. Note that this is a purely
+ * internal format, never seen on the wire. This section is later
+ * rewritten by erts_encode_ext_dist_header_finalize() while updating
+ * the cache. We write the header backwards just before the
+ * actual term(s).
+ */
+ for (i = acmp->sz-1; i >= 0; i--) {
+ Uint32 aval;
+ ASSERT(0 <= acmp->cix[i] && acmp->cix[i] < ERTS_ATOM_CACHE_SIZE);
+ ASSERT(i == acmp->cache[acmp->cix[i]].iix);
+ ASSERT(is_atom(acmp->cache[acmp->cix[i]].atom));
+
+ aval = (Uint32) atom_val(acmp->cache[acmp->cix[i]].atom);
+ ep -= 4;
+ put_int32(aval, ep);
+ ep -= 2;
+ put_int16(acmp->cix[i], ep);
+ }
+ --ep;
+ put_int8(acmp->sz, ep);
+ } else {
+ ASSERT(ctx->dflags & DFLAG_FRAGMENTS);
+ /* If we don't have an atom cache but are using a dist header we just put 0
+ in the atom cache size slot */
+ --ep;
+ put_int8(0, ep);
+ }
--ep;
put_int8(dist_hdr_flags, ep);
if (fragments > 1) {
@@ -382,11 +429,9 @@ byte *erts_encode_ext_dist_header_fragment(byte **hdrpp,
}
-#define PASS_THROUGH 'p'
-
Sint erts_encode_ext_dist_header_finalize(ErtsDistOutputBuf* ob,
DistEntry* dep,
- Uint32 dflags,
+ Uint64 dflags,
Sint reds)
{
byte *ip;
@@ -395,69 +440,34 @@ Sint erts_encode_ext_dist_header_finalize(ErtsDistOutputBuf* ob,
byte dist_hdr_flags;
int long_atoms;
Uint64 seq_id = 0, frag_id = 0;
- register byte *ep = ob->hdrp ? ob->hdrp : ob->extp;
+ register byte *ep = ob->eiov->iov[1].iov_base;
ASSERT(dflags & DFLAG_UTF8_ATOMS);
/*
* The buffer can have different layouts at this point depending on
* what was known when encoded:
*
- * Pending connection: CtrlTerm [, MsgTerm]
+ * Pending connection: HOPEFUL_DATA, HFlgs, HIX, PIX, CtrlTerm [, MsgTerm]
* With atom cache : VERSION_MAGIC, DIST_HEADER, ..., CtrlTerm [, MsgTerm]
* No atom cache : VERSION_MAGIC, CtrlTerm [, VERSION_MAGIC, MsgTerm]
*/
- if (ep[0] != VERSION_MAGIC || dep->transcode_ctx) {
- /*
- * Was encoded without atom cache toward pending connection.
- */
- ASSERT(ep[0] == SMALL_TUPLE_EXT || ep[0] == LARGE_TUPLE_EXT);
-
- if (~dflags & (DFLAG_DIST_MONITOR | DFLAG_DIST_MONITOR_NAME)
- && ep[0] == SMALL_TUPLE_EXT
- && ep[1] == 4
- && ep[2] == SMALL_INTEGER_EXT
- && (ep[3] == DOP_MONITOR_P ||
- ep[3] == DOP_MONITOR_P_EXIT ||
- ep[3] == DOP_DEMONITOR_P)) {
- /*
- * Receiver does not support process monitoring.
- * Suppress monitor control msg (see erts_dsig_send_monitor)
- * by converting it to an empty (tick) packet.
- */
- ob->ext_endp = ob->extp;
- return reds;
- }
- if (~dflags & (DFLAG_BIT_BINARIES | DFLAG_EXPORT_PTR_TAG
- | DFLAG_DIST_HDR_ATOM_CACHE)) {
- reds = transcode_dist_obuf(ob, dep, dflags, reds);
- if (reds < 0)
- return reds;
- ep = ob->extp;
- }
- if (dflags & DFLAG_DIST_HDR_ATOM_CACHE) {
- /*
- * Encoding was done without atom caching but receiver expects
- * a dist header, so we prepend an empty one.
- */
- *--ep = 0; /* NumberOfAtomCacheRefs */
- *--ep = DIST_HEADER;
- *--ep = VERSION_MAGIC;
- }
- goto done;
- }
- 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 */
- *--ep = PASS_THROUGH;
- goto done;
+ if (ep[0] == HOPEFUL_DATA)
+ return transcode_dist_obuf(ob, dep, dflags, reds);
+
+ if (ep[0] == PASS_THROUGH) {
+ ASSERT(!(dflags & (DFLAG_DIST_HDR_ATOM_CACHE|DFLAG_FRAGMENTS)));
+ ASSERT(ob->eiov->iov[1].iov_len == 1);
+ return reds;
}
if (ep[1] == DIST_FRAG_CONT) {
- ep = ob->extp;
- goto done;
- } else if (ep[1] == DIST_FRAG_HEADER) {
+ ASSERT(ep[0] == VERSION_MAGIC);
+ ASSERT(ob->eiov->iov[1].iov_len == 18);
+ return reds;
+ }
+
+ if (ep[1] == DIST_FRAG_HEADER) {
/* skip the seq id and frag id */
seq_id = get_int64(&ep[2]);
ep += 8;
@@ -481,10 +491,7 @@ Sint erts_encode_ext_dist_header_finalize(ErtsDistOutputBuf* ob,
ip = &instr_buf[0];
sys_memcpy((void *) ip, (void *) ep, sz);
ep += sz;
- /* ep now points to the beginning of the control message term */
-#ifdef ERTS_DEBUG_USE_DIST_SEP
- ASSERT(*ep == VERSION_MAGIC);
-#endif
+ ASSERT(ep == (byte *) (ob->eiov->iov[1].iov_base + ob->eiov->iov[1].iov_len));
if (ci > 0) {
Uint32 flgs_buf[((ERTS_DIST_HDR_ATOM_CACHE_FLAG_BYTES(
ERTS_MAX_INTERNAL_ATOM_CACHE_ENTRIES)-1)
@@ -597,49 +604,82 @@ Sint erts_encode_ext_dist_header_finalize(ErtsDistOutputBuf* ob,
*--ep = DIST_HEADER;
}
*--ep = VERSION_MAGIC;
-done:
- ob->extp = ep;
- ASSERT((byte*)ob->bin->orig_bytes <= ob->extp && ob->extp < ob->ext_endp);
+
+ sz = ((byte *) ob->eiov->iov[1].iov_base) - ep;
+ ob->eiov->size += sz;
+ ob->eiov->iov[1].iov_len += sz;
+ ob->eiov->iov[1].iov_base = ep;
+
return reds < 0 ? 0 : reds;
}
ErtsExtSzRes
-erts_encode_dist_ext_size(Eterm term, Uint32 flags, ErtsAtomCacheMap *acmp, Uint* szp)
+erts_encode_dist_ext_size(Eterm term,
+ ErtsAtomCacheMap *acmp,
+ TTBSizeContext* ctx,
+ Uint* szp, Sint *redsp,
+ Sint *vlenp, Uint *fragmentsp)
{
Uint sz;
- ErtsExtSzRes res = encode_size_struct_int(NULL, acmp, term, flags, NULL, &sz);
- if (res == ERTS_EXT_SZ_OK) {
+ ErtsExtSzRes res;
+
+ ASSERT(ctx);
+ ASSERT(szp);
+ ASSERT(vlenp);
+ ASSERT(fragmentsp);
+
+ sz = *szp;
+
+ if (!ctx->wstack.wstart) {
+ /*
+ * First call for this 'term'. We might however encode
+ * multiple terms and this might not be the first term
+ * in the sequence. 'ctx' should contain valid info about
+ * about previous terms regarding fragments, and vlen.
+ * 'szp' should contain valid info about the total size
+ * of previous terms.
+ */
+ if (ctx->vlen < 0) {
+ /* First term as well */
+ ctx->vlen = 0;
+ if (ctx->dflags & DFLAG_FRAGMENTS)
+ ctx->fragment_size = ERTS_DIST_FRAGMENT_SIZE;
+ }
+
#ifndef ERTS_DEBUG_USE_DIST_SEP
- if (!(flags & (DFLAG_DIST_HDR_ATOM_CACHE | DFLAG_NO_MAGIC)))
+ if (!(ctx->dflags & (DFLAG_DIST_HDR_ATOM_CACHE|DFLAG_FRAGMENTS)))
#endif
sz++ /* VERSION_MAGIC */;
- *szp += sz;
}
- return res;
-}
-ErtsExtSzRes
-erts_encode_dist_ext_size_ctx(Eterm term, ErtsDSigSendContext *ctx, Uint* szp)
-{
- Uint sz;
- ErtsExtSzRes res = encode_size_struct_int(&ctx->u.sc, ctx->acmp, term,
- ctx->flags, &ctx->reds, &sz);
+ res = encode_size_struct_int(ctx, acmp, term, ctx->dflags, redsp, &sz);
+
if (res == ERTS_EXT_SZ_OK) {
-#ifndef ERTS_DEBUG_USE_DIST_SEP
- if (!(ctx->flags & (DFLAG_DIST_HDR_ATOM_CACHE | DFLAG_NO_MAGIC)))
-#endif
- sz++ /* VERSION_MAGIC */;
+ Uint total_size, fragments;
+
+ /*
+ * Each fragment use
+ * - one element for driver header
+ * - one element for fragment header
+ * - and (at least) one for data
+ */
+ total_size = sz + ctx->extra_size;
+ fragments = (total_size - 1)/ctx->fragment_size + 1;
- *szp += sz;
+ *szp = sz;
+ *fragmentsp = fragments;
+ *vlenp = ctx->vlen + 3*fragments;
}
+
return res;
}
ErtsExtSzRes erts_encode_ext_size_2(Eterm term, unsigned dflags, Uint *szp)
{
- ErtsExtSzRes res = encode_size_struct_int(NULL, NULL, term, dflags,
- NULL, szp);
+ ErtsExtSzRes res;
+ *szp = 0;
+ res = encode_size_struct_int(NULL, NULL, term, dflags, NULL, szp);
(*szp)++ /* VERSION_MAGIC */;
return res;
}
@@ -651,20 +691,44 @@ ErtsExtSzRes erts_encode_ext_size(Eterm term, Uint *szp)
Uint erts_encode_ext_size_ets(Eterm term)
{
- return encode_size_struct2(NULL, term, TERM_TO_BINARY_DFLAGS|DFLAG_INTERNAL_TAGS);
+ return encode_size_struct2(NULL, term,
+ TERM_TO_BINARY_DFLAGS|DFLAG_ETS_COMPRESSED);
}
-int erts_encode_dist_ext(Eterm term, byte **ext, Uint32 flags, ErtsAtomCacheMap *acmp,
- TTBEncodeContext* ctx, Sint* reds)
+int erts_encode_dist_ext(Eterm term, byte **ext, Uint64 flags, ErtsAtomCacheMap *acmp,
+ TTBEncodeContext* ctx, Uint *fragmentsp, Sint* reds)
{
- if (!ctx || !ctx->wstack.wstart) {
- #ifndef ERTS_DEBUG_USE_DIST_SEP
- if (!(flags & (DFLAG_DIST_HDR_ATOM_CACHE | DFLAG_NO_MAGIC)))
- #endif
+ int res;
+ ASSERT(ctx);
+
+ if (!ctx->wstack.wstart) {
+ ctx->cptr = *ext;
+#ifndef ERTS_DEBUG_USE_DIST_SEP
+ if (!(flags & (DFLAG_DIST_HDR_ATOM_CACHE|DFLAG_PENDING_CONNECT|DFLAG_FRAGMENTS)))
+#endif
*(*ext)++ = VERSION_MAGIC;
+#ifndef ERTS_DEBUG_USE_DIST_SEP
+ if (flags & DFLAG_PENDING_CONNECT) {
+ Sint payload_ix = ctx->vlen;
+ ASSERT(ctx->payload_ixp);
+ if (payload_ix) {
+ /* we potentially need a version magic on the payload... */
+ (*ext)++;
+ ctx->cptr = *ext;
+ put_int32(payload_ix, ctx->payload_ixp);
+ }
+ }
+#endif
}
- return enc_term_int(ctx, acmp, term, *ext, flags, NULL, reds, ext);
+ res = enc_term_int(ctx, acmp, term, *ext, flags, NULL, reds, ext);
+ if (fragmentsp)
+ *fragmentsp = res == 0 ? ctx->frag_ix + 1 : ctx->frag_ix;
+ if (flags & DFLAG_PENDING_CONNECT) {
+ ASSERT(ctx->hopefull_flagsp);
+ put_int64(ctx->hopefull_flags, ctx->hopefull_flagsp);
+ }
+ return res;
}
void erts_encode_ext(Eterm term, byte **ext)
@@ -681,7 +745,7 @@ void erts_encode_ext(Eterm term, byte **ext)
byte* erts_encode_ext_ets(Eterm term, byte *ep, struct erl_off_heap_header** off_heap)
{
- return enc_term(NULL, term, ep, TERM_TO_BINARY_DFLAGS|DFLAG_INTERNAL_TAGS,
+ return enc_term(NULL, term, ep, TERM_TO_BINARY_DFLAGS|DFLAG_ETS_COMPRESSED,
off_heap);
}
@@ -786,7 +850,7 @@ erts_prepare_dist_ext(ErtsDistExternal *edep,
ASSERT(dep);
erts_de_rlock(dep);
- ASSERT(dep->flags & DFLAG_UTF8_ATOMS);
+ ASSERT(dep->dflags & DFLAG_UTF8_ATOMS);
if ((dep->state != ERTS_DE_STATE_CONNECTED &&
@@ -796,7 +860,7 @@ erts_prepare_dist_ext(ErtsDistExternal *edep,
return ERTS_PREP_DIST_EXT_CLOSED;
}
- if (!(dep->flags & DFLAG_DIST_HDR_ATOM_CACHE)) {
+ if (!(dep->dflags & (DFLAG_DIST_HDR_ATOM_CACHE|DFLAG_FRAGMENTS))) {
/* Skip PASS_THROUGH */
ext++;
size--;
@@ -826,7 +890,7 @@ erts_prepare_dist_ext(ErtsDistExternal *edep,
edep->data->seq_id = 0;
edep->data->frag_id = 1;
- if (dep->flags & DFLAG_DIST_HDR_ATOM_CACHE)
+ if (dep->dflags & (DFLAG_DIST_HDR_ATOM_CACHE|DFLAG_FRAGMENTS))
edep->flags |= ERTS_DIST_EXT_DFLAG_HDR;
if (ep[1] != DIST_HEADER && ep[1] != DIST_FRAG_HEADER && ep[1] != DIST_FRAG_CONT) {
@@ -836,7 +900,7 @@ erts_prepare_dist_ext(ErtsDistExternal *edep,
edep->data->extp = ext;
}
else if (ep[1] == DIST_FRAG_CONT) {
- if (!(dep->flags & DFLAG_FRAGMENTS))
+ if (!(dep->dflags & DFLAG_FRAGMENTS))
goto bad_hdr;
edep->attab.size = 0;
edep->data->extp = ext + 1 + 1 + 8 + 8;
@@ -853,7 +917,7 @@ erts_prepare_dist_ext(ErtsDistExternal *edep,
goto bad_hdr;
if (ep[1] == DIST_FRAG_HEADER) {
- if (!(dep->flags & DFLAG_FRAGMENTS))
+ if (!(dep->dflags & DFLAG_FRAGMENTS))
goto bad_hdr;
edep->data->seq_id = get_int64(&ep[2]);
edep->data->frag_id = get_int64(&ep[2+8]);
@@ -1286,8 +1350,11 @@ static BIF_RETTYPE term_to_binary_trap_1(BIF_ALIST_1)
Eterm Term = tp[1];
Eterm Opts = tp[2];
Eterm bt = tp[3];
+ Eterm bix = tp[4];
+ Sint bif_ix = signed_val(bix);
Binary *bin = erts_magic_ref2bin(bt);
- Eterm res = erts_term_to_binary_int(BIF_P, Term, Opts, 0, 0,bin);
+ Eterm res = erts_term_to_binary_int(BIF_P, bif_ix, Term, Opts,
+ 0, 0,bin, 0, ~((Uint) 0));
if (is_non_value(res)) {
if (erts_set_gc_state(BIF_P, 1)
|| MSO(BIF_P).overhead > BIN_VHEAP_SZ(BIF_P)) {
@@ -1295,10 +1362,10 @@ static BIF_RETTYPE term_to_binary_trap_1(BIF_ALIST_1)
}
if (Opts == am_undefined)
ERTS_BIF_ERROR_TRAPPED1(BIF_P, SYSTEM_LIMIT,
- bif_export[BIF_term_to_binary_1], Term);
+ &bif_trap_export[bif_ix], Term);
else
ERTS_BIF_ERROR_TRAPPED2(BIF_P, SYSTEM_LIMIT,
- bif_export[BIF_term_to_binary_2], Term, Opts);
+ &bif_trap_export[bif_ix], Term, Opts);
}
if (is_tuple(res)) {
ASSERT(BIF_P->flags & F_DISABLE_GC);
@@ -1316,8 +1383,10 @@ HIPE_WRAPPER_BIF_DISABLE_GC(term_to_binary, 1)
BIF_RETTYPE term_to_binary_1(BIF_ALIST_1)
{
- Eterm res = erts_term_to_binary_int(BIF_P, BIF_ARG_1, am_undefined,
- 0, TERM_TO_BINARY_DFLAGS, NULL);
+ Eterm res = erts_term_to_binary_int(BIF_P, BIF_term_to_binary_1,
+ BIF_ARG_1, am_undefined,
+ 0, TERM_TO_BINARY_DFLAGS, NULL, 0,
+ ~((Uint) 0));
if (is_non_value(res)) {
ASSERT(!(BIF_P->flags & F_DISABLE_GC));
BIF_ERROR(BIF_P, SYSTEM_LIMIT);
@@ -1331,22 +1400,43 @@ BIF_RETTYPE term_to_binary_1(BIF_ALIST_1)
}
}
-HIPE_WRAPPER_BIF_DISABLE_GC(term_to_binary, 2)
+HIPE_WRAPPER_BIF_DISABLE_GC(term_to_iovec, 1)
-BIF_RETTYPE term_to_binary_2(BIF_ALIST_2)
+BIF_RETTYPE term_to_iovec_1(BIF_ALIST_1)
+{
+ Eterm res = erts_term_to_binary_int(BIF_P, BIF_term_to_iovec_1,
+ BIF_ARG_1, am_undefined,
+ 0, TERM_TO_BINARY_DFLAGS, NULL, !0,
+ ~((Uint) 0));
+ if (is_non_value(res)) {
+ ASSERT(!(BIF_P->flags & F_DISABLE_GC));
+ BIF_ERROR(BIF_P, SYSTEM_LIMIT);
+ }
+ if (is_tuple(res)) {
+ erts_set_gc_state(BIF_P, 0);
+ BIF_TRAP1(&term_to_binary_trap_export,BIF_P,res);
+ } else {
+ ASSERT(!(BIF_P->flags & F_DISABLE_GC));
+ BIF_RET(res);
+ }
+}
+
+static ERTS_INLINE int
+parse_t2b_opts(Eterm opts, Uint *flagsp, int *levelp, int *iovecp, Uint *fsizep)
{
- Process* p = BIF_P;
- Eterm Term = BIF_ARG_1;
- Eterm Flags = BIF_ARG_2;
int level = 0;
+ int iovec = 0;
Uint flags = TERM_TO_BINARY_DFLAGS;
- Eterm res;
+ Uint fsize = ~((Uint) 0); /* one fragment */
- while (is_list(Flags)) {
- Eterm arg = CAR(list_val(Flags));
+ while (is_list(opts)) {
+ Eterm arg = CAR(list_val(opts));
Eterm* tp;
if (arg == am_compressed) {
level = Z_DEFAULT_COMPRESSION;
+ }
+ else if (iovecp && arg == am_iovec) {
+ iovec = !0;
} else if (is_tuple(arg) && *(tp = tuple_val(arg)) == make_arityval(2)) {
if (tp[1] == am_minor_version && is_small(tp[2])) {
switch (signed_val(tp[2])) {
@@ -1360,34 +1450,66 @@ BIF_RETTYPE term_to_binary_2(BIF_ALIST_2)
flags = TERM_TO_BINARY_DFLAGS | DFLAG_UTF8_ATOMS;
break;
default:
- goto error;
+ return 0; /* badarg */
}
} else if (tp[1] == am_compressed && is_small(tp[2])) {
level = signed_val(tp[2]);
if (!(0 <= level && level < 10)) {
- goto error;
+ return 0; /* badarg */
}
- } else {
- goto error;
+ } else if (fsizep) {
+ if (ERTS_IS_ATOM_STR("fragment", tp[1])) {
+ if (!term_to_Uint(tp[2], &fsize))
+ return 0; /* badarg */
+ }
+ else {
+ return 0; /* badarg */
+ }
+ }
+ else {
+ return 0; /* badarg */
}
} else {
- error:
- BIF_ERROR(p, BADARG);
+ return 0; /* badarg */
}
- Flags = CDR(list_val(Flags));
+ opts = CDR(list_val(opts));
}
- if (is_not_nil(Flags)) {
- goto error;
+ if (is_not_nil(opts)) {
+ return 0; /* badarg */
}
- res = erts_term_to_binary_int(p, Term, BIF_ARG_2,
- level, flags, NULL);
+ *flagsp = flags;
+ *levelp = level;
+ if (iovecp)
+ *iovecp = iovec;
+ if (fsizep)
+ *fsizep = fsize;
+
+ return !0; /* ok */
+}
+
+HIPE_WRAPPER_BIF_DISABLE_GC(term_to_binary, 2)
+
+BIF_RETTYPE term_to_binary_2(BIF_ALIST_2)
+{
+ int level;
+ Uint flags;
+ Eterm res;
+
+ if (!parse_t2b_opts(BIF_ARG_2, &flags, &level, NULL, NULL)) {
+ BIF_ERROR(BIF_P, BADARG);
+ }
+
+ res = erts_term_to_binary_int(BIF_P, BIF_term_to_binary_2,
+ BIF_ARG_1, BIF_ARG_2,
+ level, flags, NULL, 0,
+ ~((Uint) 0));
if (is_non_value(res)) {
ASSERT(!(BIF_P->flags & F_DISABLE_GC));
BIF_ERROR(BIF_P, SYSTEM_LIMIT);
}
if (is_tuple(res)) {
- erts_set_gc_state(p, 0);
+ erts_set_gc_state(BIF_P, 0);
BIF_TRAP1(&term_to_binary_trap_export,BIF_P,res);
} else {
ASSERT(!(BIF_P->flags & F_DISABLE_GC));
@@ -1395,6 +1517,67 @@ BIF_RETTYPE term_to_binary_2(BIF_ALIST_2)
}
}
+HIPE_WRAPPER_BIF_DISABLE_GC(term_to_iovec, 2)
+
+BIF_RETTYPE term_to_iovec_2(BIF_ALIST_2)
+{
+ int level;
+ Uint flags;
+ Eterm res;
+
+ if (!parse_t2b_opts(BIF_ARG_2, &flags, &level, NULL, NULL)) {
+ BIF_ERROR(BIF_P, BADARG);
+ }
+
+ res = erts_term_to_binary_int(BIF_P, BIF_term_to_iovec_2,
+ BIF_ARG_1, BIF_ARG_2,
+ level, flags, NULL, !0,
+ ~((Uint) 0));
+ if (is_non_value(res)) {
+ ASSERT(!(BIF_P->flags & F_DISABLE_GC));
+ BIF_ERROR(BIF_P, SYSTEM_LIMIT);
+ }
+ if (is_tuple(res)) {
+ erts_set_gc_state(BIF_P, 0);
+ BIF_TRAP1(&term_to_binary_trap_export,BIF_P,res);
+ } else {
+ ASSERT(!(BIF_P->flags & F_DISABLE_GC));
+ BIF_RET(res);
+ }
+}
+
+Eterm
+erts_debug_term_to_binary(Process *p, Eterm term, Eterm opts)
+{
+ Eterm ret;
+ int level, iovec;
+ Uint flags;
+ Uint fsize;
+
+ if (!parse_t2b_opts(opts, &flags, &level, &iovec, &fsize)) {
+ ERTS_BIF_PREP_ERROR(ret, p, BADARG);
+ }
+ else {
+ Eterm res = erts_term_to_binary_int(p, BIF_term_to_binary_2,
+ term, opts, level, flags,
+ NULL, iovec, fsize);
+
+ if (is_non_value(res)) {
+ ASSERT(!(p->flags & F_DISABLE_GC));
+ ERTS_BIF_PREP_ERROR(ret, p, SYSTEM_LIMIT);
+ }
+ else if (is_tuple(res)) {
+ erts_set_gc_state(p, 0);
+ ERTS_BIF_PREP_TRAP1(ret, &term_to_binary_trap_export,p,res);
+ }
+ else {
+ ASSERT(!(p->flags & F_DISABLE_GC));
+ ERTS_BIF_PREP_RET(ret, res);
+ }
+ }
+ return ret;
+}
+
enum B2TState { /* order is somewhat significant */
B2TPrepare,
@@ -1804,8 +1987,8 @@ static BIF_RETTYPE binary_to_term_int(Process* p, Eterm bin, B2TContext *ctx)
case B2TBadArg:
BUMP_REDS(p, (initial_reds - ctx->reds) / B2T_BYTES_PER_REDUCTION);
- ASSERT(ctx->bif == bif_export[BIF_binary_to_term_1]
- || ctx->bif == bif_export[BIF_binary_to_term_2]);
+ ASSERT(ctx->bif == &bif_trap_export[BIF_binary_to_term_1]
+ || ctx->bif == &bif_trap_export[BIF_binary_to_term_2]);
if (is_first_call)
ERTS_BIF_PREP_ERROR(ret_val, p, BADARG);
@@ -1886,7 +2069,7 @@ BIF_RETTYPE binary_to_term_1(BIF_ALIST_1)
ctx.flags = 0;
ctx.used_bytes = 0;
ctx.trap_bin = THE_NON_VALUE;
- ctx.bif = bif_export[BIF_binary_to_term_1];
+ ctx.bif = &bif_trap_export[BIF_binary_to_term_1];
ctx.arg[0] = BIF_ARG_1;
ctx.arg[1] = THE_NON_VALUE;
return binary_to_term_int(BIF_P, BIF_ARG_1, &ctx);
@@ -1921,7 +2104,7 @@ BIF_RETTYPE binary_to_term_2(BIF_ALIST_2)
goto error;
ctx.trap_bin = THE_NON_VALUE;
- ctx.bif = bif_export[BIF_binary_to_term_2];
+ ctx.bif = &bif_trap_export[BIF_binary_to_term_2];
ctx.arg[0] = BIF_ARG_1;
ctx.arg[1] = BIF_ARG_2;
return binary_to_term_int(BIF_P, BIF_ARG_1, &ctx);
@@ -1935,7 +2118,7 @@ external_size_1(BIF_ALIST_1)
{
Process* p = BIF_P;
Eterm Term = BIF_ARG_1;
- Uint size;
+ Uint size = 0;
switch (erts_encode_ext_size(Term, &size)) {
case ERTS_EXT_SZ_SYSTEM_LIMIT:
@@ -1957,7 +2140,7 @@ external_size_1(BIF_ALIST_1)
Eterm
external_size_2(BIF_ALIST_2)
{
- Uint size;
+ Uint size = 0;
Uint flags = TERM_TO_BINARY_DFLAGS;
while (is_list(BIF_ARG_2)) {
@@ -2006,7 +2189,7 @@ external_size_2(BIF_ALIST_2)
}
static Eterm
-erts_term_to_binary_simple(Process* p, Eterm Term, Uint size, int level, Uint flags)
+erts_term_to_binary_simple(Process* p, Eterm Term, Uint size, int level, Uint64 dflags)
{
Eterm bin;
size_t real_size;
@@ -2022,7 +2205,7 @@ erts_term_to_binary_simple(Process* p, Eterm Term, Uint size, int level, Uint fl
bytes = erts_alloc(ERTS_ALC_T_TMP, size);
}
- if ((endp = enc_term(NULL, Term, bytes, flags, NULL))
+ if ((endp = enc_term(NULL, Term, bytes, dflags, NULL))
== NULL) {
erts_exit(ERTS_ERROR_EXIT, "%s, line %d: bad term: %x\n",
__FILE__, __LINE__, Term);
@@ -2067,7 +2250,7 @@ erts_term_to_binary_simple(Process* p, Eterm Term, Uint size, int level, Uint fl
bin = new_binary(p, (byte *)NULL, size);
bytes = binary_bytes(bin);
bytes[0] = VERSION_MAGIC;
- if ((endp = enc_term(NULL, Term, bytes+1, flags, NULL))
+ if ((endp = enc_term(NULL, Term, bytes+1, dflags, NULL))
== NULL) {
erts_exit(ERTS_ERROR_EXIT, "%s, line %d: bad term: %x\n",
__FILE__, __LINE__, Term);
@@ -2082,8 +2265,8 @@ erts_term_to_binary_simple(Process* p, Eterm Term, Uint size, int level, Uint fl
}
Eterm
-erts_term_to_binary(Process* p, Eterm Term, int level, Uint flags) {
- Uint size;
+erts_term_to_binary(Process* p, Eterm Term, int level, Uint64 flags) {
+ Uint size = 0;
switch (encode_size_struct_int(NULL, NULL, Term, flags, NULL, &size)) {
case ERTS_EXT_SZ_SYSTEM_LIMIT:
return THE_NON_VALUE;
@@ -2121,6 +2304,8 @@ static int ttb_context_destructor(Binary *context_bin)
erts_bin_free(context->s.ec.result_bin);
context->s.ec.result_bin = NULL;
}
+ if (context->s.ec.iov)
+ erts_free(ERTS_ALC_T_T2B_VEC, context->s.ec.iov);
break;
case TTBCompress:
erl_zlib_deflate_finish(&(context->s.cc.stream));
@@ -2142,8 +2327,77 @@ static int ttb_context_destructor(Binary *context_bin)
return 1;
}
-static Eterm erts_term_to_binary_int(Process* p, Eterm Term, Eterm opts, int level,
- Uint flags, Binary *context_b)
+Uint
+erts_ttb_iov_size(int use_termv, Sint vlen, Uint fragments)
+{
+ Uint sz;
+ ASSERT(vlen > 0);
+ ASSERT(fragments > 0);
+ sz = sizeof(SysIOVec)*vlen;
+ if (sz % sizeof(void *) != 0)
+ sz += sizeof(void *) - (sz % sizeof(void *));
+ sz += sizeof(ErlDrvBinary *)*vlen;
+ if (use_termv)
+ sz += sizeof(Eterm)*vlen;
+ sz += sizeof(ErlIOVec *)*fragments;
+ sz += sizeof(ErlIOVec)*fragments;
+ if (sz % sizeof(void *) != 0)
+ sz += sizeof(void *) - (sz % sizeof(void *));
+ return sz;
+}
+
+ErlIOVec **
+erts_ttb_iov_init(TTBEncodeContext *ctx, int use_termv, char *ptr,
+ Sint vlen, Uint fragments, Uint fragment_size)
+{
+ int i;
+ ErlIOVec *feiovp;
+ ctx->vlen = 0;
+ ctx->size = 0;
+
+ ctx->iov = (SysIOVec *) ptr;
+ ptr += sizeof(SysIOVec)*vlen;
+ if (((UWord) ptr) % sizeof(void *) != 0)
+ ptr += sizeof(void *) - (((UWord) ptr) % sizeof(void *));
+ ASSERT(((UWord) ptr) % sizeof(void *) == 0);
+
+ ctx->binv = (ErlDrvBinary **) ptr;
+ ptr += sizeof(ErlDrvBinary *)*vlen;
+
+ if (!use_termv)
+ ctx->termv = NULL;
+ else {
+ ctx->termv = (Eterm *) ptr;
+ ptr += sizeof(Eterm)*vlen;
+ }
+
+ ctx->fragment_eiovs = (ErlIOVec **) ptr;
+ ptr += sizeof(ErlIOVec *)*fragments;
+
+ feiovp = (ErlIOVec *) ptr;
+ ptr += sizeof(ErlIOVec)*fragments;
+
+ if (((UWord) ptr) % sizeof(void *) != 0)
+ ptr += sizeof(void *) - (((UWord) ptr) % sizeof(void *));
+ ASSERT(((UWord) ptr) % sizeof(void *) == 0);
+
+ for (i = 0; i < fragments; i++)
+ ctx->fragment_eiovs[i] = &feiovp[i];
+
+ ctx->frag_ix = -1;
+ ctx->fragment_size = fragment_size;
+
+#ifdef DEBUG
+ ctx->cptr = NULL;
+ ctx->debug_fragments = fragments;
+ ctx->debug_vlen = vlen;
+#endif
+ return ctx->fragment_eiovs;
+}
+
+static Eterm erts_term_to_binary_int(Process* p, Sint bif_ix, Eterm Term, Eterm opts,
+ int level, Uint64 dflags, Binary *context_b,
+ int iovec, Uint fragment_size)
{
Eterm *hp;
Eterm res;
@@ -2157,6 +2411,10 @@ static Eterm erts_term_to_binary_int(Process* p, Eterm Term, Eterm opts, int lev
TTBContext c_buff;
TTBContext *context = &c_buff;
+ ASSERT(bif_ix > 0 && IS_USMALL(!0, bif_ix));
+ ASSERT(bif_ix == BIF_term_to_binary_1 || bif_ix == BIF_term_to_binary_2
+ || bif_ix == BIF_term_to_iovec_1 || bif_ix == BIF_term_to_iovec_2);
+
#define EXPORT_CONTEXT() \
do { \
if (context_b == NULL) { \
@@ -2169,36 +2427,47 @@ static Eterm erts_term_to_binary_int(Process* p, Eterm Term, Eterm opts, int lev
#define RETURN_STATE() \
do { \
- hp = HAlloc(p, ERTS_MAGIC_REF_THING_SIZE + 1 + 3); \
+ hp = HAlloc(p, ERTS_MAGIC_REF_THING_SIZE + 1 + 4); \
c_term = erts_mk_magic_ref(&hp, &MSO(p), context_b); \
- res = TUPLE3(hp, Term, opts, c_term); \
+ res = TUPLE4(hp, Term, opts, c_term, make_small(bif_ix)); \
BUMP_ALL_REDS(p); \
return res; \
} while (0);
-
if (context_b == NULL) {
/* Setup enough to get started */
context->state = TTBSize;
context->alive = 1;
- context->s.sc.wstack.wstart = NULL;
- context->s.sc.flags = flags;
+ ERTS_INIT_TTBSizeContext(&context->s.sc, dflags);
context->s.sc.level = level;
+ context->s.sc.fragment_size = fragment_size;
+ if (!level) {
+ context->s.sc.vlen = iovec ? 0 : -1;
+ context->s.sc.iovec = iovec;
+ }
+ else {
+ context->s.sc.vlen = -1;
+ context->s.sc.iovec = 0;
+ }
} else {
context = ERTS_MAGIC_BIN_DATA(context_b);
- }
+ }
+
/* Initialization done, now we will go through the states */
for (;;) {
switch (context->state) {
case TTBSize:
{
- Uint size;
+ Uint size, fragments = 1;
Binary *result_bin;
- int level;
- Uint flags;
- /* Try for fast path */
+ int level = context->s.sc.level;
+ Sint vlen;
+ iovec = context->s.sc.iovec;
+ fragment_size = context->s.sc.fragment_size;
+ size = 1; /* VERSION_MAGIC */
switch (encode_size_struct_int(&context->s.sc, NULL, Term,
- context->s.sc.flags, &reds, &size)) {
+ context->s.sc.dflags, &reds,
+ &size)) {
case ERTS_EXT_SZ_SYSTEM_LIMIT:
BUMP_REDS(p, (initial_reds - reds) / TERM_TO_BINARY_LOOP_FACTOR);
return THE_NON_VALUE;
@@ -2209,14 +2478,23 @@ static Eterm erts_term_to_binary_int(Process* p, Eterm Term, Eterm opts, int lev
case ERTS_EXT_SZ_OK:
break;
}
- ++size; /* VERSION_MAGIC */
/* Move these to next state */
- flags = context->s.sc.flags;
- level = context->s.sc.level;
- if (size <= ERL_ONHEAP_BIN_LIMIT) {
+ dflags = context->s.sc.dflags;
+ vlen = context->s.sc.vlen;
+ if (vlen >= 0) {
+ Uint total_size = size + context->s.sc.extra_size;
+ fragments = (total_size - 1)/fragment_size + 1;
+ vlen += 3*fragments;
+ ASSERT(vlen);
+ }
+ else if (size <= ERL_ONHEAP_BIN_LIMIT) {
/* Finish in one go */
res = erts_term_to_binary_simple(p, Term, size,
- level, flags);
+ level, dflags);
+ if (iovec) {
+ Eterm *hp = HAlloc(p, 2);
+ res = CONS(hp, res, NIL);
+ }
BUMP_REDS(p, 1);
return res;
}
@@ -2225,37 +2503,156 @@ static Eterm erts_term_to_binary_int(Process* p, Eterm Term, Eterm opts, int lev
result_bin->orig_bytes[0] = (byte)VERSION_MAGIC;
/* Next state immediately, no need to export context */
context->state = TTBEncode;
- context->s.ec.flags = flags;
+ ERTS_INIT_TTBEncodeContext(&context->s.ec, dflags);
context->s.ec.level = level;
- context->s.ec.wstack.wstart = NULL;
context->s.ec.result_bin = result_bin;
+ context->s.ec.iovec = iovec;
+ if (vlen >= 0) {
+ Uint sz = erts_ttb_iov_size(!0, vlen, fragments);
+ char *ptr = (char *) erts_alloc(ERTS_ALC_T_T2B_VEC, sz);
+ erts_ttb_iov_init(&context->s.ec, !0, ptr, vlen,
+ fragments, fragment_size);
+ context->s.ec.cptr = (byte *) &result_bin->orig_bytes[0];
+ }
break;
}
case TTBEncode:
{
- byte *endp;
+ byte *endp, *tmp;
byte *bytes = (byte *) context->s.ec.result_bin->orig_bytes;
size_t real_size;
Binary *result_bin;
+ Sint realloc_offset;
+ Uint fragments;
- flags = context->s.ec.flags;
- if (enc_term_int(&context->s.ec, NULL,Term, bytes+1, flags, NULL, &reds, &endp) < 0) {
+ dflags = context->s.ec.dflags;
+ if (enc_term_int(&context->s.ec, NULL,Term, bytes+1, dflags,
+ NULL, &reds, &endp) < 0) {
EXPORT_CONTEXT();
RETURN_STATE();
}
real_size = endp - bytes;
+ tmp = (byte *) &context->s.ec.result_bin->orig_bytes[0];
result_bin = erts_bin_realloc(context->s.ec.result_bin,real_size);
+ realloc_offset = (byte *) &result_bin->orig_bytes[0] - tmp;
level = context->s.ec.level;
BUMP_REDS(p, (initial_reds - reds) / TERM_TO_BINARY_LOOP_FACTOR);
if (level == 0 || real_size < 6) { /* We are done */
+ Sint cbin_refc_diff;
+ Eterm result, rb_term, *hp, *hp_end;
+ Uint hsz;
+ int ix;
+ SysIOVec *iov;
+ Eterm *termv;
return_normal:
+ fragments = context->s.ec.frag_ix + 1;
context->s.ec.result_bin = NULL;
context->alive = 0;
if (context_b && erts_refc_read(&context_b->intern.refc,0) == 0) {
erts_bin_free(context_b);
}
- return erts_build_proc_bin(&MSO(p), HAlloc(p, PROC_BIN_SIZE),
- result_bin);
+ if (!context->s.ec.iov) {
+ hsz = PROC_BIN_SIZE + (iovec ? 2 : 0);
+ hp = HAlloc(p, hsz);
+ result = erts_build_proc_bin(&MSO(p), hp, result_bin);
+ if (iovec) {
+ hp += PROC_BIN_SIZE;
+ result = CONS(hp, result, NIL);
+ }
+ return result;
+ }
+ iovec = context->s.ec.iovec;
+ ASSERT(iovec);
+ iov = context->s.ec.iov;
+ termv = context->s.ec.termv;
+ ASSERT(context->s.ec.vlen <= context->s.ec.debug_vlen);
+ ASSERT(fragments <= context->s.ec.debug_fragments);
+ /* first two elements should be unused */
+ ASSERT(context->s.ec.vlen >= 3*fragments);
+ ASSERT(!iov[0].iov_base && !iov[0].iov_len);
+ ASSERT(!iov[1].iov_base && !iov[1].iov_len);
+
+ hsz = (2 /* cons */
+ + (PROC_BIN_SIZE > ERL_SUB_BIN_SIZE
+ ? PROC_BIN_SIZE
+ : ERL_SUB_BIN_SIZE)); /* max size per vec */
+ hsz *= context->s.ec.vlen - 2*fragments; /* number of vecs */
+ hp = HAlloc(p, hsz);
+ hp_end = hp + hsz;
+ rb_term = THE_NON_VALUE;
+ result = NIL;
+ ASSERT(erts_refc_read(&result_bin->intern.refc, 1) == 1);
+ cbin_refc_diff = -1;
+ for (ix = context->s.ec.vlen - 1; ix > 1; ix--) {
+ Eterm bin_term, pb_term;
+ Uint pb_size;
+ ProcBin *pb;
+ SysIOVec *iovp = &iov[ix];
+ if (!iovp->iov_base)
+ continue; /* empty slot for header */
+ pb_term = termv[ix];
+ if (is_value(pb_term)) {
+ pb_size = binary_size(pb_term);
+ pb = (ProcBin *) binary_val(pb_term);
+ }
+ else {
+ iovp->iov_base = (void *) (((byte *) iovp->iov_base)
+ + realloc_offset);
+ pb_size = result_bin->orig_size;
+ if (is_non_value(rb_term))
+ pb = NULL;
+ else {
+ pb = (ProcBin *) binary_val(rb_term);
+ pb_term = rb_term;
+ }
+ }
+ /*
+ * We intentionally avoid using sub binaries
+ * since the GC might convert those to heap
+ * binaries and by this ruin the nice preparation
+ * for usage of this data as I/O vector in
+ * nifs/drivers.
+ */
+ if (is_value(pb_term) && iovp->iov_len == pb_size)
+ bin_term = pb_term;
+ else {
+ Binary *bin;
+ if (is_value(pb_term)) {
+ bin = ((ProcBin *) binary_val(pb_term))->val;
+ erts_refc_inc(&bin->intern.refc, 2);
+ }
+ else {
+ bin = result_bin;
+ cbin_refc_diff++;
+ }
+ pb = (ProcBin *) (char *) hp;
+ hp += PROC_BIN_SIZE;
+ pb->thing_word = HEADER_PROC_BIN;
+ pb->size = (Uint) iovp->iov_len;
+ pb->next = MSO(p).first;
+ MSO(p).first = (struct erl_off_heap_header*) pb;
+ pb->val = bin;
+ pb->bytes = (byte*) iovp->iov_base;
+ pb->flags = 0;
+ OH_OVERHEAD(&MSO(p), pb->size / sizeof(Eterm));
+ bin_term = make_binary(pb);
+ }
+ result = CONS(hp, bin_term, result);
+ hp += 2;
+ }
+ ASSERT(hp <= hp_end);
+ HRelease(p, hp_end, hp);
+ context->s.ec.iov = NULL;
+ erts_free(ERTS_ALC_T_T2B_VEC, iov);
+ if (cbin_refc_diff) {
+ ASSERT(cbin_refc_diff >= -1);
+ if (cbin_refc_diff > 0)
+ erts_refc_add(&result_bin->intern.refc,
+ cbin_refc_diff, 1);
+ else
+ erts_bin_free(result_bin);
+ }
+ return result;
}
/* Continue with compression... */
/* To make absolutely sure that zlib does not barf on a reallocated context,
@@ -2372,7 +2769,7 @@ static Eterm erts_term_to_binary_int(Process* p, Eterm Term, Eterm opts, int lev
*/
static byte*
-enc_atom(ErtsAtomCacheMap *acmp, Eterm atom, byte *ep, Uint32 dflags)
+enc_atom(ErtsAtomCacheMap *acmp, Eterm atom, byte *ep, Uint64 dflags)
{
int iix;
int len;
@@ -2380,7 +2777,7 @@ enc_atom(ErtsAtomCacheMap *acmp, Eterm atom, byte *ep, Uint32 dflags)
ASSERT(is_atom(atom));
- if (dflags & DFLAG_INTERNAL_TAGS) {
+ if (dflags & DFLAG_ETS_COMPRESSED) {
Uint aval = atom_val(atom);
ASSERT(aval < (1<<24));
if (aval >= (1 << 16)) {
@@ -2457,19 +2854,20 @@ enc_atom(ErtsAtomCacheMap *acmp, Eterm atom, byte *ep, Uint32 dflags)
/*
* We use this atom as sysname in local pid/port/refs
- * for the ETS compressed format (DFLAG_INTERNAL_TAGS).
+ * for the ETS compressed format
*
*/
#define INTERNAL_LOCAL_SYSNAME am_ErtsSecretAtom
static byte*
-enc_pid(ErtsAtomCacheMap *acmp, Eterm pid, byte* ep, Uint32 dflags)
+enc_pid(ErtsAtomCacheMap *acmp, Eterm pid, byte* ep, Uint64 dflags)
{
Uint on, os;
- Eterm sysname = ((is_internal_pid(pid) && (dflags & DFLAG_INTERNAL_TAGS))
+ Eterm sysname = ((is_internal_pid(pid) && (dflags & DFLAG_ETS_COMPRESSED))
? INTERNAL_LOCAL_SYSNAME : pid_node_name(pid));
Uint32 creation = pid_creation(pid);
- byte* tagp = ep++;
+
+ *ep++ = NEW_PID_EXT;
/* insert atom here containing host and sysname */
ep = enc_atom(acmp, sysname, ep, dflags);
@@ -2481,15 +2879,8 @@ enc_pid(ErtsAtomCacheMap *acmp, Eterm pid, byte* ep, Uint32 dflags)
ep += 4;
put_int32(os, ep);
ep += 4;
- if (creation <= ERTS_MAX_LOCAL_CREATION) {
- *tagp = PID_EXT;
- *ep++ = creation;
- } else {
- ASSERT(is_external_pid(pid));
- *tagp = NEW_PID_EXT;
- put_int32(creation, ep);
- ep += 4;
- }
+ put_int32(creation, ep);
+ ep += 4;
return ep;
}
@@ -2609,7 +3000,7 @@ dec_pid(ErtsDistExternal *edep, ErtsHeapFactory* factory, byte* ep,
if (tag == PID_EXT) {
cre = get_int8(ep);
ep += 1;
- if (!is_valid_creation(cre)) {
+ if (!is_tiny_creation(cre)) {
return NULL;
}
} else {
@@ -2653,7 +3044,7 @@ dec_pid(ErtsDistExternal *edep, ErtsHeapFactory* factory, byte* ep,
#define ENC_LAST_ARRAY_ELEMENT ((Eterm) 6)
static byte*
-enc_term(ErtsAtomCacheMap *acmp, Eterm obj, byte* ep, Uint32 dflags,
+enc_term(ErtsAtomCacheMap *acmp, Eterm obj, byte* ep, Uint64 dflags,
struct erl_off_heap_header** off_heap)
{
byte *res;
@@ -2662,7 +3053,8 @@ enc_term(ErtsAtomCacheMap *acmp, Eterm obj, byte* ep, Uint32 dflags,
}
static int
-enc_term_int(TTBEncodeContext* ctx, ErtsAtomCacheMap *acmp, Eterm obj, byte* ep, Uint32 dflags,
+enc_term_int(TTBEncodeContext* ctx, ErtsAtomCacheMap *acmp, Eterm obj, byte* ep,
+ Uint64 dflags,
struct erl_off_heap_header** off_heap, Sint *reds, byte **res)
{
DECLARE_WSTACK(s);
@@ -2673,10 +3065,12 @@ enc_term_int(TTBEncodeContext* ctx, ErtsAtomCacheMap *acmp, Eterm obj, byte* ep,
Eterm val;
FloatDef f;
Sint r = 0;
+ int use_iov = 0;
if (ctx) {
WSTACK_CHANGE_ALLOCATOR(s, ERTS_ALC_T_SAVED_ESTACK);
r = *reds;
+ use_iov = !!ctx->iov;
if (ctx->wstack.wstart) { /* restore saved stacks and byte pointer */
WSTACK_RESTORE(s, &ctx->wstack);
@@ -2867,28 +3261,21 @@ enc_term_int(TTBEncodeContext* ctx, ErtsAtomCacheMap *acmp, Eterm obj, byte* ep,
case REF_DEF:
case EXTERNAL_REF_DEF: {
Uint32 *ref_num;
- Eterm sysname = (((dflags & DFLAG_INTERNAL_TAGS) && is_internal_ref(obj))
+ Eterm sysname = (((dflags & DFLAG_ETS_COMPRESSED) && is_internal_ref(obj))
? INTERNAL_LOCAL_SYSNAME : ref_node_name(obj));
Uint32 creation = ref_creation(obj);
- byte* tagp = ep++;
ASSERT(dflags & DFLAG_EXTENDED_REFERENCES);
erts_magic_ref_save_bin(obj);
+ *ep++ = NEWER_REFERENCE_EXT;
i = ref_no_numbers(obj);
put_int16(i, ep);
ep += 2;
ep = enc_atom(acmp, sysname, ep, dflags);
- if (creation <= ERTS_MAX_LOCAL_CREATION) {
- *tagp = NEW_REFERENCE_EXT;
- *ep++ = creation;
- } else {
- ASSERT(is_external_ref(obj));
- *tagp = NEWER_REFERENCE_EXT;
- put_int32(creation, ep);
- ep += 4;
- }
+ put_int32(creation, ep);
+ ep += 4;
ref_num = ref_numbers(obj);
for (j = 0; j < i; j++) {
put_int32(ref_num[j], ep);
@@ -2898,24 +3285,17 @@ enc_term_int(TTBEncodeContext* ctx, ErtsAtomCacheMap *acmp, Eterm obj, byte* ep,
}
case PORT_DEF:
case EXTERNAL_PORT_DEF: {
- Eterm sysname = (((dflags & DFLAG_INTERNAL_TAGS) && is_internal_port(obj))
+ Eterm sysname = (((dflags & DFLAG_ETS_COMPRESSED) && is_internal_port(obj))
? INTERNAL_LOCAL_SYSNAME : port_node_name(obj));
Uint32 creation = port_creation(obj);
- byte* tagp = ep++;
+ *ep++ = NEW_PORT_EXT;
ep = enc_atom(acmp, sysname, ep, dflags);
j = port_number(obj);
put_int32(j, ep);
ep += 4;
- if (creation <= ERTS_MAX_LOCAL_CREATION) {
- *tagp = PORT_EXT;
- *ep++ = creation;
- } else {
- ASSERT(is_external_port(obj));
- *tagp = NEW_PORT_EXT;
- put_int32(creation, ep);
- ep += 4;
- }
+ put_int32(creation, ep);
+ ep += 4;
break;
}
case LIST_DEF:
@@ -3042,9 +3422,44 @@ enc_term_int(TTBEncodeContext* ctx, ErtsAtomCacheMap *acmp, Eterm obj, byte* ep,
Uint bitsize;
byte* bytes;
byte* data_dst;
+ Uint off_heap_bytesize = 0;
+ Uint off_heap_tail;
+ Eterm pb_term;
+ Binary *pb_val;
+ ASSERT(!(dflags & DFLAG_PENDING_CONNECT) || (ctx && ctx->iov));
+
ERTS_GET_BINARY_BYTES(obj, bytes, bitoffs, bitsize);
- if (dflags & DFLAG_INTERNAL_TAGS) {
+ if (use_iov) {
+ if (bitoffs == 0) {
+ ProcBin* pb = (ProcBin*) binary_val(obj);
+ off_heap_bytesize = pb->size;
+ if (off_heap_bytesize <= ERL_ONHEAP_BIN_LIMIT)
+ off_heap_bytesize = 0;
+ else {
+ pb_term = obj;
+ if (pb->thing_word == HEADER_SUB_BIN) {
+ ErlSubBin* sub = (ErlSubBin*)pb;
+ pb_term = sub->orig;
+ pb = (ProcBin*) binary_val(pb_term);
+ }
+ if (pb->thing_word != HEADER_PROC_BIN)
+ off_heap_bytesize = 0;
+ else {
+ if (pb->flags) {
+ char* before_realloc = pb->val->orig_bytes;
+ erts_emasculate_writable_binary(pb);
+ bytes += (pb->val->orig_bytes - before_realloc);
+ ASSERT((byte *) &pb->val->orig_bytes[0] <= bytes
+ && bytes < ((byte *) &pb->val->orig_bytes[0]
+ + pb->val->orig_size));
+ }
+ pb_val = pb->val;
+ }
+ }
+ }
+ }
+ else if (dflags & DFLAG_ETS_COMPRESSED) {
ProcBin* pb = (ProcBin*) binary_val(obj);
Uint bytesize = pb->size;
if (pb->thing_word == HEADER_SUB_BIN) {
@@ -3087,20 +3502,51 @@ enc_term_int(TTBEncodeContext* ctx, ErtsAtomCacheMap *acmp, Eterm obj, byte* ep,
j = binary_size(obj);
put_int32(j, ep);
ep += 4;
- data_dst = ep;
- ep += j;
+ if (off_heap_bytesize)
+ off_heap_tail = 0;
+ else {
+ data_dst = ep;
+ ep += j;
+ }
} else if (dflags & DFLAG_BIT_BINARIES) {
/* Bit-level binary. */
- *ep++ = BIT_BINARY_EXT;
- j = binary_size(obj);
- put_int32((j+1), ep);
- ep += 4;
- *ep++ = bitsize;
- ep[j] = 0; /* Zero unused bits at end of binary */
- data_dst = ep;
- ep += j + 1;
- if (ctx)
- ctx->hopefull_flags |= DFLAG_BIT_BINARIES;
+ if (dflags & DFLAG_PENDING_CONNECT) {
+ j = off_heap_bytesize;
+ if (!j) {
+ pb_val = NULL;
+ pb_term = THE_NON_VALUE;
+ j = binary_size(obj);
+ }
+ data_dst = hopefull_bit_binary(ctx, &ep, pb_val, pb_term,
+ bytes, bitoffs, bitsize, j);
+ if (!data_dst)
+ break; /* off heap binary referred... */
+ ASSERT(!off_heap_bytesize);
+ off_heap_tail = 0;
+ /*
+ * Trailing bits already written by hopefull_bit_binary();
+ * now go copy all whole octets...
+ */
+ bitsize = 0;
+ }
+ else {
+ *ep++ = BIT_BINARY_EXT;
+ j = binary_size(obj);
+ put_int32((j+1), ep);
+ ep += 4;
+ *ep++ = bitsize;
+ if (off_heap_bytesize) {
+ /* trailing bits */
+ ep[0] = 0;
+ copy_binary_to_buffer(ep, 0, bytes + j, 0, bitsize);
+ off_heap_tail = 1;
+ }
+ else {
+ ep[j] = 0; /* Zero unused bits at end of binary */
+ data_dst = ep;
+ ep += j + 1;
+ }
+ }
} else {
/*
* Bit-level binary, but the receiver doesn't support it.
@@ -3112,13 +3558,30 @@ enc_term_int(TTBEncodeContext* ctx, ErtsAtomCacheMap *acmp, Eterm obj, byte* ep,
j = binary_size(obj);
put_int32((j+1), ep);
ep += 4;
- ep[j] = 0; /* Zero unused bits at end of binary */
- data_dst = ep;
- ep += j+1;
- *ep++ = SMALL_INTEGER_EXT;
- *ep++ = bitsize;
+
+ if (off_heap_bytesize) {
+ /* trailing bits */
+ ep[0] = 0;
+ copy_binary_to_buffer(ep, 0, bytes + j, 0, bitsize);
+ ep[1] = SMALL_INTEGER_EXT;
+ ep[2] = bitsize;
+ off_heap_tail = 3;
+ }
+ else {
+ ep[j] = 0; /* Zero unused bits at end of binary */
+ data_dst = ep;
+ ep += j+1;
+ *ep++ = SMALL_INTEGER_EXT;
+ *ep++ = bitsize;
+ }
}
- if (ctx && j > r * TERM_TO_BINARY_MEMCPY_FACTOR) {
+ if (off_heap_bytesize) {
+ ASSERT(pb_val);
+ store_in_vec(ctx, ep, pb_val, pb_term,
+ bytes, off_heap_bytesize);
+ ep += off_heap_tail;
+ }
+ else if (ctx && j > r * TERM_TO_BINARY_MEMCPY_FACTOR) {
WSTACK_PUSH5(s, (UWord)data_dst, (UWord)bytes, bitoffs,
ENC_BIN_COPY, 8*j + bitsize);
} else {
@@ -3130,14 +3593,15 @@ enc_term_int(TTBEncodeContext* ctx, ErtsAtomCacheMap *acmp, Eterm obj, byte* ep,
case EXPORT_DEF:
{
Export* exp = *((Export **) (export_val(obj) + 1));
- if ((dflags & DFLAG_EXPORT_PTR_TAG) != 0) {
+ ASSERT(!(dflags & DFLAG_PENDING_CONNECT) || (ctx && ctx->iov));
+ if (dflags & DFLAG_PENDING_CONNECT)
+ hopefull_export(ctx, &ep, exp, dflags, off_heap);
+ else if ((dflags & DFLAG_EXPORT_PTR_TAG) != 0) {
*ep++ = EXPORT_EXT;
ep = enc_atom(acmp, exp->info.mfa.module, ep, dflags);
ep = enc_atom(acmp, exp->info.mfa.function, ep, dflags);
ep = enc_term(acmp, make_small(exp->info.mfa.arity),
ep, dflags, off_heap);
- if (ctx)
- ctx->hopefull_flags |= DFLAG_EXPORT_PTR_TAG;
} else {
/* Tag, arity */
*ep++ = SMALL_TUPLE_EXT;
@@ -3187,11 +3651,276 @@ enc_term_int(TTBEncodeContext* ctx, ErtsAtomCacheMap *acmp, Eterm obj, byte* ep,
if (ctx) {
ASSERT(ctx->wstack.wstart == NULL);
*reds = r;
+ if (use_iov)
+ store_in_vec(ctx, ep, NULL, THE_NON_VALUE, NULL, 0);
}
*res = ep;
return 0;
}
+static ERTS_INLINE void
+store_in_vec_aux(Uint *szp,
+ Sint *vlenp,
+ SysIOVec *iov,
+ ErlDrvBinary **binv,
+ Eterm *termv,
+ Binary *bin,
+ Eterm term,
+ byte *ptr,
+ Uint len,
+ Sint *fixp,
+ ErlIOVec **frag_eiovpp,
+ Uint frag_size)
+{
+ ErlDrvBinary *dbin = Binary2ErlDrvBinary(bin);
+ Uint size = 0;
+ int vlen = *vlenp;
+ Uint iov_len;
+ ErlIOVec *feiovp;
+ Sint fix = *fixp;
+
+ ASSERT(((byte *) &bin->orig_bytes[0]) <= ptr);
+ ASSERT(ptr + len <= ((byte *) &bin->orig_bytes[0]) + bin->orig_size);
+
+ if (fix >= 0) {
+ feiovp = frag_eiovpp[fix];
+ ASSERT(0 < feiovp->size);
+ ASSERT(feiovp->size <= frag_size);
+ if (feiovp->size != frag_size) {
+ /* current fragment not full yet... */
+ iov_len = frag_size - feiovp->size;
+ if (len < iov_len)
+ iov_len = len;
+ goto store_iov_data;
+ }
+ }
+
+ while (len) {
+ /* Start new fragment... */
+
+ feiovp = frag_eiovpp[++fix];
+ ASSERT(fix >= 0);
+
+ if (termv) {
+ termv[vlen] = THE_NON_VALUE;
+ termv[vlen+1] = THE_NON_VALUE;
+ }
+
+ feiovp->vsize = 2;
+ feiovp->size = 0;
+ feiovp->iov = &iov[vlen];
+ feiovp->binv = &binv[vlen];
+
+ /* entry for driver header */
+ iov[vlen].iov_base = NULL;
+ iov[vlen].iov_len = 0;
+ binv[vlen] = NULL;
+ vlen++;
+
+ /* entry for dist header */
+ iov[vlen].iov_base = NULL;
+ iov[vlen].iov_len = 0;
+ binv[vlen] = NULL;
+ vlen++;
+
+ iov_len = len < frag_size ? len : frag_size;
+
+ store_iov_data:
+
+ ASSERT(iov_len);
+
+ do {
+ Uint iov_len_left;
+
+ if (iov_len <= MAX_SYSIOVEC_IOVLEN)
+ iov_len_left = 0;
+ else {
+ iov_len_left = iov_len - MAX_SYSIOVEC_IOVLEN;
+ iov_len = MAX_SYSIOVEC_IOVLEN;
+ }
+
+ iov[vlen].iov_base = ptr;
+ iov[vlen].iov_len = iov_len;
+ binv[vlen] = dbin;
+ if (termv)
+ termv[vlen] = term;
+ else
+ erts_refc_inc(&bin->intern.refc, 2);
+ size += iov_len;
+ len -= iov_len;
+ ptr += iov_len;
+ vlen++;
+ feiovp->size += iov_len;
+ feiovp->vsize++;
+
+ iov_len = iov_len_left;
+ } while (iov_len);
+ }
+
+ *fixp = fix;
+ *vlenp = vlen;
+ *szp += size;
+}
+
+static void
+store_in_vec(TTBEncodeContext *ctx,
+ byte *ep,
+ Binary *ohbin,
+ Eterm ohpb,
+ byte *ohp,
+ Uint ohsz)
+{
+ byte *cp = ctx->cptr;
+ if (cp != ep) {
+ /* save data in common binary... */
+ store_in_vec_aux(&ctx->size,
+ &ctx->vlen,
+ ctx->iov,
+ ctx->binv,
+ ctx->termv,
+ ctx->result_bin,
+ THE_NON_VALUE,
+ cp, ep - cp,
+ &ctx->frag_ix,
+ ctx->fragment_eiovs,
+ ctx->fragment_size);
+ ASSERT(ctx->vlen <= ctx->debug_vlen);
+ ASSERT(ctx->frag_ix <= ctx->debug_fragments);
+ ctx->cptr = ep;
+ }
+ if (ohbin) {
+ /* save off-heap binary... */
+ store_in_vec_aux(&ctx->size,
+ &ctx->vlen,
+ ctx->iov,
+ ctx->binv,
+ ctx->termv,
+ ohbin, ohpb, ohp, ohsz,
+ &ctx->frag_ix,
+ ctx->fragment_eiovs,
+ ctx->fragment_size);
+ ASSERT(ctx->vlen <= ctx->debug_vlen);
+ ASSERT(ctx->frag_ix <= ctx->debug_fragments);
+ }
+}
+
+static byte *
+begin_hopefull_data(TTBEncodeContext *ctx, byte *ep)
+{
+ store_in_vec(ctx, ep, NULL, THE_NON_VALUE, NULL, 0);
+ ASSERT(ERTS_NO_HIX == (Uint32) get_int32(ctx->hopefull_ixp));
+ put_int32(ctx->vlen, ctx->hopefull_ixp);
+ ctx->hopefull_ixp = ep;
+ put_int32(ERTS_NO_HIX, ep);
+ ep += 4;
+ ctx->cptr = ep;
+ return ep;
+}
+
+static byte *
+end_hopefull_data(TTBEncodeContext *ctx, byte *ep, Uint fallback_size)
+{
+ Uint sz;
+ store_in_vec(ctx, ep, NULL, THE_NON_VALUE, NULL, 0);
+ /*
+ * Reserve extra room for fallback if needed. The four
+ * bytes used for hopefull index can be used for
+ * fallback encoding...
+ */
+ sz = ep - ctx->hopefull_ixp;
+ if (fallback_size > sz) {
+ ep += fallback_size - sz;
+ ctx->cptr = ep;
+ }
+ return ep;
+}
+
+static byte *
+hopefull_bit_binary(TTBEncodeContext* ctx, byte **epp, Binary *pb_val, Eterm pb_term,
+ byte *bytes, byte bitoffs, byte bitsize, Uint sz)
+{
+ byte *octets, *ep = *epp;
+
+ ctx->hopefull_flags |= DFLAG_BIT_BINARIES;
+
+ /*
+ * The fallback:
+ *
+ * SMALL_TUPLE_EXT - 1 byte
+ * 2 - 1 byte
+ * BINARY_EXT - 1 byte
+ * whole octet size ('sz') - 4 byte
+ * whole octets - 'sz' bytes
+ * trailing bits - 1 byte
+ * SMALL_INTEGER_EXT - 1 byte
+ * bitsize - 1 byte
+ */
+
+ /* bit binary prelude in one hopefull data element */
+ ep = begin_hopefull_data(ctx, ep);
+ *ep++ = BIT_BINARY_EXT;
+ put_int32((sz+1), ep);
+ ep += 4;
+ *ep++ = bitsize;
+ ep = end_hopefull_data(ctx, ep, 1+1+1+4);
+
+ /* All whole octets... */
+ if (pb_val) {
+ octets = NULL;
+ store_in_vec(ctx, ep, pb_val, pb_term, bytes, sz);
+ }
+ else {
+ /* ... will be copied here afterwards */
+ octets = ep;
+ ep += sz;
+ }
+
+ /* copy trailing bits into new hopefull data element */
+ ep = begin_hopefull_data(ctx, ep);
+ *ep = 0;
+
+ copy_binary_to_buffer(ep, 0, bytes + sz, bitoffs, bitsize);
+ ep++;
+
+ ep = end_hopefull_data(ctx, ep, 1+1+1);
+ *epp = ep;
+
+ return octets;
+}
+
+static void
+hopefull_export(TTBEncodeContext* ctx, byte **epp, Export* exp, Uint32 dflags,
+ struct erl_off_heap_header** off_heap)
+{
+ Uint fallback_sz;
+ byte *ep = *epp, *mod_start;
+
+ /*
+ * The fallback:
+ *
+ * SMALL_TUPLE_EXT - 1 byte
+ * 2 - 1 byte
+ * module atom... - M bytes
+ * function atom... - F bytes
+ */
+
+ ctx->hopefull_flags |= DFLAG_EXPORT_PTR_TAG;
+
+ ep = begin_hopefull_data(ctx, ep);
+
+ *ep++ = EXPORT_EXT;
+ mod_start = ep;
+ ep = enc_atom(NULL, exp->info.mfa.module, ep, dflags);
+ ep = enc_atom(NULL, exp->info.mfa.function, ep, dflags);
+ fallback_sz = 2 + (ep - mod_start);
+ ep = enc_term(NULL, make_small(exp->info.mfa.arity),
+ ep, dflags, off_heap);
+
+ ep = end_hopefull_data(ctx, ep, fallback_sz);
+
+ *epp = ep;
+}
+
/** @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
@@ -3611,7 +4340,7 @@ dec_term_atom_common:
if (tag == PORT_EXT) {
cre = get_int8(ep);
ep++;
- if (!is_valid_creation(cre)) {
+ if (!is_tiny_creation(cre)) {
goto error;
}
}
@@ -3658,7 +4387,7 @@ dec_term_atom_common:
cre = get_int8(ep);
ep += 1;
- if (!is_valid_creation(cre)) {
+ if (!is_tiny_creation(cre)) {
goto error;
}
goto ref_ext_common;
@@ -3672,7 +4401,7 @@ dec_term_atom_common:
cre = get_int8(ep);
ep += 1;
- if (!is_valid_creation(cre)) {
+ if (!is_tiny_creation(cre)) {
goto error;
}
r0 = get_int32(ep);
@@ -4067,73 +4796,6 @@ dec_term_atom_common:
next = &(funp->creator);
break;
}
- case FUN_EXT:
- {
- ErlFunThing* funp = (ErlFunThing *) hp;
- Eterm module;
- Sint old_uniq;
- Sint old_index;
- unsigned num_free;
- int i;
- Eterm temp;
-
- num_free = get_int32(ep);
- ep += 4;
- hp += ERL_FUN_SIZE;
- hp += num_free;
- factory->hp = hp;
- funp->thing_word = HEADER_FUN;
- funp->num_free = num_free;
- *objp = make_fun(funp);
-
- /* Creator pid */
- if ((*ep != PID_EXT && *ep != NEW_PID_EXT)
- || (ep = dec_pid(edep, factory, ep+1,
- &funp->creator, *ep))==NULL) {
- goto error;
- }
-
- /* Module */
- if ((ep = dec_atom(edep, ep, &module)) == NULL) {
- goto error;
- }
-
- /* Index */
- if ((ep = dec_term(edep, factory, ep, &temp, NULL, 0)) == NULL) {
- goto error;
- }
- if (!is_small(temp)) {
- goto error;
- }
- old_index = unsigned_val(temp);
-
- /* Uniq */
- if ((ep = dec_term(edep, factory, ep, &temp, NULL, 0)) == NULL) {
- goto error;
- }
- if (!is_small(temp)) {
- goto error;
- }
-
- /*
- * It is safe to link the fun into the fun list only when
- * no more validity tests can fail.
- */
- funp->next = factory->off_heap->first;
- factory->off_heap->first = (struct erl_off_heap_header*)funp;
- old_uniq = unsigned_val(temp);
-
- funp->fe = erts_put_fun_entry(module, old_uniq, old_index);
- funp->arity = funp->fe->address[-1] - num_free;
- hp = factory->hp;
-
- /* Environment */
- for (i = num_free-1; i >= 0; i--) {
- funp->env[i] = (Eterm) next;
- next = funp->env + i;
- }
- break;
- }
case ATOM_INTERNAL_REF2:
n = get_int16(ep);
ep += 2;
@@ -4302,10 +4964,11 @@ error_hamt:
(except for cached atoms) */
static Uint encode_size_struct2(ErtsAtomCacheMap *acmp,
Eterm obj,
- unsigned dflags) {
- Uint size;
+ Uint64 dflags) {
+ Uint size = 0;
ErtsExtSzRes res = encode_size_struct_int(NULL, acmp, obj,
- dflags, NULL, &size);
+ dflags, NULL,
+ &size);
/*
* encode_size_struct2() only allowed when
* we know the result will always be OK!
@@ -4316,18 +4979,23 @@ static Uint encode_size_struct2(ErtsAtomCacheMap *acmp,
static ErtsExtSzRes
encode_size_struct_int(TTBSizeContext* ctx, ErtsAtomCacheMap *acmp, Eterm obj,
- unsigned dflags, Sint *reds, Uint *res)
+ Uint64 dflags, Sint *reds, Uint *res)
{
DECLARE_WSTACK(s);
Uint m, i, arity;
- Uint result = 0;
+ Uint result = *res;
Sint r = 0;
+ int vlen = -1;
if (ctx) {
WSTACK_CHANGE_ALLOCATOR(s, ERTS_ALC_T_SAVED_ESTACK);
r = *reds;
- if (ctx->wstack.wstart) { /* restore saved stack */
+ vlen = ctx->vlen;
+
+ if (!ctx->wstack.wstart)
+ ctx->last_result = result;
+ else { /* restore saved stack */
WSTACK_RESTORE(s, &ctx->wstack);
result = ctx->result;
obj = ctx->obj;
@@ -4346,6 +5014,7 @@ encode_size_struct_int(TTBSizeContext* ctx, ErtsAtomCacheMap *acmp, Eterm obj,
*reds = 0;
ctx->obj = obj;
ctx->result = result;
+ ctx->vlen = vlen;
WSTACK_SAVE(s, &ctx->wstack);
return ERTS_EXT_SZ_YIELD;
}
@@ -4354,7 +5023,7 @@ encode_size_struct_int(TTBSizeContext* ctx, ErtsAtomCacheMap *acmp, Eterm obj,
result++;
break;
case ATOM_DEF:
- if (dflags & DFLAG_INTERNAL_TAGS) {
+ if (dflags & DFLAG_ETS_COMPRESSED) {
if (atom_val(obj) >= (1<<16)) {
result += 1 + 3;
}
@@ -4408,30 +5077,21 @@ encode_size_struct_int(TTBSizeContext* ctx, ErtsAtomCacheMap *acmp, Eterm obj,
result += 1 + 4 + 1 + i; /* tag,size,sign,digits */
break;
case EXTERNAL_PID_DEF:
- if (external_pid_creation(obj) > ERTS_MAX_LOCAL_CREATION)
- result += 3;
- /*fall through*/
case PID_DEF:
result += (1 + encode_size_struct2(acmp, pid_node_name(obj), dflags) +
- 4 + 4 + 1);
+ 4 + 4 + 4);
break;
case EXTERNAL_REF_DEF:
- if (external_ref_creation(obj) > ERTS_MAX_LOCAL_CREATION)
- result += 3;
- /*fall through*/
case REF_DEF:
ASSERT(dflags & DFLAG_EXTENDED_REFERENCES);
i = ref_no_numbers(obj);
result += (1 + 2 + encode_size_struct2(acmp, ref_node_name(obj), dflags) +
- 1 + 4*i);
+ 4 + 4*i);
break;
case EXTERNAL_PORT_DEF:
- if (external_port_creation(obj) > ERTS_MAX_LOCAL_CREATION)
- result += 3;
- /*fall through*/
case PORT_DEF:
result += (1 + encode_size_struct2(acmp, port_node_name(obj), dflags) +
- 4 + 1);
+ 4 + 4);
break;
case LIST_DEF: {
int is_str = is_external_string(obj, &m);
@@ -4525,39 +5185,131 @@ encode_size_struct_int(TTBSizeContext* ctx, ErtsAtomCacheMap *acmp, Eterm obj,
break;
case BINARY_DEF: {
ProcBin* pb = (ProcBin*) binary_val(obj);
- Uint tot_bytes = pb->size;
- if (!(dflags & DFLAG_INTERNAL_TAGS)) {
-#ifdef ARCH_64
- if (tot_bytes >= (Uint) 0xffffffff) {
- if (pb->thing_word == HEADER_SUB_BIN) {
- ErlSubBin* sub = (ErlSubBin*) pb;
- tot_bytes += (sub->bitoffs + sub->bitsize+ 7) / 8;
- }
- if (tot_bytes > (Uint) 0xffffffff) {
- WSTACK_DESTROY(s);
- return ERTS_EXT_SZ_SYSTEM_LIMIT;
- }
- }
-#endif
- }
- else {
+ Uint bin_size = pb->size;
+ byte bitoffs = 0;
+ byte bitsize = 0;
+ if (dflags & DFLAG_ETS_COMPRESSED) {
ProcBin* pb = (ProcBin*) binary_val(obj);
Uint sub_extra = 0;
if (pb->thing_word == HEADER_SUB_BIN) {
ErlSubBin* sub = (ErlSubBin*) pb;
+ bitoffs = sub->bitoffs;
+ bitsize = sub->bitsize;
pb = (ProcBin*) binary_val(sub->orig);
sub_extra = 2; /* bitoffs and bitsize */
- tot_bytes += (sub->bitoffs + sub->bitsize+ 7) / 8;
+ bin_size += (bitoffs + bitsize + 7) / 8;
}
if (pb->thing_word == HEADER_PROC_BIN
- && heap_bin_size(tot_bytes) > PROC_BIN_SIZE) {
+ && heap_bin_size(bin_size) > PROC_BIN_SIZE) {
result += 1 + sub_extra + sizeof(ProcBin);
break;
}
+ }
+ else {
+#ifdef ARCH_64
+ if (bin_size >= (Uint) 0xffffffff) {
+ if (pb->thing_word == HEADER_SUB_BIN) {
+ ErlSubBin* sub = (ErlSubBin*) pb;
+ bin_size += (sub->bitoffs + sub->bitsize+ 7) / 8;
+ }
+ if (bin_size > (Uint) 0xffffffff) {
+ WSTACK_DESTROY(s);
+ return ERTS_EXT_SZ_SYSTEM_LIMIT;
+ }
+ }
+#endif
+ if (pb->thing_word == HEADER_SUB_BIN) {
+ ErlSubBin* sub = (ErlSubBin*) pb;
+ bitoffs = sub->bitoffs;
+ bitsize = sub->bitsize;
+ pb = (ProcBin*) binary_val(sub->orig);
+ }
+ if (vlen >= 0) {
+ Uint csz;
+ if (pb->thing_word == HEADER_PROC_BIN
+ && bitoffs == 0
+ && bin_size > ERL_ONHEAP_BIN_LIMIT) {
+ Uint trailing_result;
+ if (bitsize == 0) {
+ result += (1 /* BIT_BINARY_EXT */
+ + 4 /* size */);
+ trailing_result = 0;
+ }
+ else if (dflags & DFLAG_BIT_BINARIES) {
+ result += (1 /* BIT_BINARY_EXT */
+ + 4 /* size */
+ + 1 /* trailing bitsize */);
+ trailing_result = 1 /* trailing bits */;
+ }
+ else {
+ /* sigh... */
+ result += (1 /* SMALL_TUPLE_EXT */
+ + 1 /* 2 tuple size */
+ + 1 /* BINARY_EXT */
+ + 4 /* binary size */);
+ trailing_result = (1 /* SMALL_INTEGER_EXT */
+ + 1 /* bitsize */);
+ }
+ csz = result - ctx->last_result;
+ ctx->last_result = result;
+ result += trailing_result;
+ vlen += 2; /* data leading up to binary and binary */
+
+ /* potentially multiple elements leading up to binary */
+ vlen += csz/MAX_SYSIOVEC_IOVLEN;
+ /* potentially multiple elements for binary */
+ vlen += bin_size/MAX_SYSIOVEC_IOVLEN;
+ ctx->extra_size += bin_size;
+
+ if (dflags & DFLAG_PENDING_CONNECT) {
+ ASSERT(dflags & DFLAG_BIT_BINARIES);
+ vlen += 2; /* for hopefull prolog and epilog */
+ result += (4 /* for hopefull prolog (see below) */
+ + 4); /* for hopefull epilog (see below) */
+ ctx->last_result = result;
+ }
+ break;
+ }
+ }
}
- result += 1 + 4 + binary_size(obj) +
- 5; /* For unaligned binary */
+
+ if (bitsize == 0) {
+ result += (1 /* BIT_BINARY_EXT */
+ + 4 /* size */
+ + bin_size);
+ }
+ else if (dflags & DFLAG_PENDING_CONNECT) {
+ Uint csz = result - ctx->last_result;
+ ASSERT(dflags & DFLAG_BIT_BINARIES);
+ /* potentially multiple elements leading up to binary */
+ vlen += csz/MAX_SYSIOVEC_IOVLEN;
+ vlen++; /* hopefull prolog */
+ /*
+ * Size for hopefull prolog is max of
+ * - fallback: 1 + 1 + 1 + 4
+ * - hopfull index + bit binary prolog: 4 + 1 + 4 + 1
+ */
+ result += 4 + 1 + 4 + 1;
+ /* potentially multiple elements for binary */
+ vlen += bin_size/MAX_SYSIOVEC_IOVLEN + 1;
+ result += bin_size;
+ vlen++; /* hopefull epiolog */
+ /*
+ * Size for hopefull epiolog is max of
+ * - fallback: 1 + 1 + 1
+ * - hopfull index + bit binary epilog: 4 + 1
+ */
+ result += 4 + 1;
+ ctx->last_result = result;
+ }
+ else if (dflags & DFLAG_BIT_BINARIES) {
+ result += 1 + 4 + 1 + bin_size + 1;
+ }
+ else {
+ /* Sigh... */
+ result += 1 + 1 + 1 + 4 + bin_size + 1 + 1 + 1;
+ }
break;
}
case FUN_DEF:
@@ -4584,10 +5336,25 @@ encode_size_struct_int(TTBSizeContext* ctx, ErtsAtomCacheMap *acmp, Eterm obj,
case EXPORT_DEF:
{
Export* ep = *((Export **) (export_val(obj) + 1));
+ Uint tmp_result = result;
result += 1;
result += encode_size_struct2(acmp, ep->info.mfa.module, dflags);
result += encode_size_struct2(acmp, ep->info.mfa.function, dflags);
result += encode_size_struct2(acmp, make_small(ep->info.mfa.arity), dflags);
+ if (dflags & DFLAG_PENDING_CONNECT) {
+ Uint csz;
+ /*
+ * Fallback is 1 + 1 + Module size + Function size, that is,
+ * the hopefull index + hopefull encoding is larger...
+ */
+ ASSERT(dflags & DFLAG_EXPORT_PTR_TAG);
+ csz = tmp_result - ctx->last_result;
+ /* potentially multiple elements leading up to hopefull entry */
+ vlen += csz/MAX_SYSIOVEC_IOVLEN;
+ vlen++; /* hopefull entry */
+ result += 4; /* hopefull index */
+ ctx->last_result = result;
+ }
}
break;
@@ -4630,6 +5397,14 @@ encode_size_struct_int(TTBSizeContext* ctx, ErtsAtomCacheMap *acmp, Eterm obj,
if (ctx) {
ASSERT(ctx->wstack.wstart == NULL);
*reds = r < 0 ? 0 : r;
+
+ if (vlen >= 0) {
+ Uint csz;
+ csz = result - ctx->last_result;
+ if (csz)
+ vlen += csz/MAX_SYSIOVEC_IOVLEN + 1;
+ ctx->vlen = vlen;
+ }
}
*res = result;
return ERTS_EXT_SZ_OK;
@@ -4898,9 +5673,6 @@ init_done:
total_size = get_int32(ep);
CHKSIZE(total_size);
ep += 1+16+4+4;
- /*FALLTHROUGH*/
-
- case FUN_EXT:
CHKSIZE(4);
num_free = get_int32(ep);
ep += 4;
@@ -4911,6 +5683,12 @@ init_done:
heap_size += ERL_FUN_SIZE + num_free;
break;
}
+ case FUN_EXT:
+ /*
+ * OTP 23: No longer support decoding the old fun
+ * representation.
+ */
+ goto error;
case ATOM_INTERNAL_REF2:
SKIP(2+atom_extra_skip);
atom_extra_skip = 0;
@@ -4968,181 +5746,374 @@ error:
#undef CHKSIZE
}
+#define ERTS_TRANSCODE_REDS_FACT 4
-struct transcode_context {
- enum {
- TRANSCODE_DEC_MSG_SIZE,
- TRANSCODE_DEC_MSG,
- TRANSCODE_ENC_CTL,
- TRANSCODE_ENC_MSG
- }state;
- Eterm ctl_term;
- Eterm* ctl_heap;
- ErtsHeapFactory ctl_factory;
- Eterm* msg_heap;
- B2TContext b2t;
- TTBEncodeContext ttb;
-#ifdef DEBUG
- ErtsDistOutputBuf* dbg_ob;
-#endif
-};
-
-void transcode_free_ctx(DistEntry* dep)
+Sint transcode_dist_obuf(ErtsDistOutputBuf* ob,
+ DistEntry* dep,
+ Uint64 dflags,
+ Sint reds)
{
- struct transcode_context* ctx = dep->transcode_ctx;
+ ErlIOVec* eiov = ob->eiov;
+ SysIOVec* iov = eiov->iov;
+ byte *hdr;
+ Uint64 hopefull_flags;
+ Uint32 hopefull_ix, payload_ix;
+ Sint start_r, r;
+ Uint new_len;
+ byte *ep;
- erts_factory_close(&ctx->ctl_factory);
- erts_free(ERTS_ALC_T_DIST_TRANSCODE, ctx->ctl_heap);
+ if (reds < 0)
+ return reds;
- if (ctx->msg_heap) {
- erts_factory_close(&ctx->b2t.u.dc.factory);
- erts_free(ERTS_ALC_T_DIST_TRANSCODE, ctx->msg_heap);
+ /*
+ * HOPEFUL_DATA header always present in io vector
+ * element 1:
+ *
+ * +---+--------------+-----------+----------+
+ * |'H'|Hopefull Flags|Hopefull IX|Payload IX|
+ * +---+--------------+-----------+----------+
+ * 1 8 4 4
+ *
+ * Hopefull flags: Flags corresponding to actual
+ * hopefull encodings in this
+ * buffer.
+ * Hopefull IX: Vector index of first hopefull
+ * encoding. Each hopefull encoding
+ * is preceeded by 4 bytes containing
+ * next vector index of hopefull
+ * encoding. ERTS_NO_HIX marks the
+ * end.
+ * Payload IX: Vector index of the beginning
+ * of the payload if there is
+ * one; otherwise, zero.
+ */
+ hdr = (byte *) iov[1].iov_base;
+
+ ASSERT(HOPEFUL_DATA == *((byte *)iov[1].iov_base));
+ ASSERT(iov[1].iov_len == 1+8+4+4);
+
+ /* Control message always begin in vector element 2 */
+ ep = iov[2].iov_base;
+ ASSERT(ep[0] == SMALL_TUPLE_EXT || ep[0] == LARGE_TUPLE_EXT);
+
+ if (~dflags & (DFLAG_DIST_MONITOR | DFLAG_DIST_MONITOR_NAME)
+ && ep[0] == SMALL_TUPLE_EXT
+ && ep[1] == 4
+ && ep[2] == SMALL_INTEGER_EXT
+ && (ep[3] == DOP_MONITOR_P ||
+ ep[3] == DOP_MONITOR_P_EXIT ||
+ ep[3] == DOP_DEMONITOR_P)) {
+ /*
+ * Receiver does not support process monitoring.
+ * Suppress monitor control msg (see erts_dsig_send_monitor)
+ * by converting it to an empty (tick) packet.
+ */
+ ob->eiov->vsize = 1;
+ ob->eiov->size = 0;
+ return reds;
}
- erts_free(ERTS_ALC_T_DIST_TRANSCODE, ctx);
- dep->transcode_ctx = NULL;
-}
-Sint transcode_dist_obuf(ErtsDistOutputBuf* ob,
- DistEntry* dep,
- Uint32 dflags,
- Sint reds)
-{
- Sint hsz;
- byte* decp;
- const int have_msg = !!ob->msg_start;
- int i;
- struct transcode_context* ctx = dep->transcode_ctx;
+ hdr++;
+ hopefull_flags = get_int64(hdr);
- if (!ctx) { /* first call for 'ob' */
- ASSERT(!(ob->hopefull_flags & ~(Uint)(DFLAG_BIT_BINARIES |
- DFLAG_EXPORT_PTR_TAG)));
- if (~dflags & ob->hopefull_flags) {
- /*
- * Receiver does not support bitstrings and/or export funs
- * and output buffer contains such message tags (hopefull_flags).
- * Must transcode control and message terms to use tuple fallbacks.
- */
- ctx = erts_alloc(ERTS_ALC_T_DIST_TRANSCODE, sizeof(struct transcode_context));
- dep->transcode_ctx = ctx;
- #ifdef DEBUG
- ctx->dbg_ob = ob;
- #endif
-
- hsz = decoded_size(ob->extp, ob->ext_endp, 0, NULL);
- ctx->ctl_heap = erts_alloc(ERTS_ALC_T_DIST_TRANSCODE, hsz*sizeof(Eterm));
- erts_factory_tmp_init(&ctx->ctl_factory, ctx->ctl_heap, hsz, ERTS_ALC_T_DIST_TRANSCODE);
- ctx->msg_heap = NULL;
-
- decp = dec_term(NULL, &ctx->ctl_factory, ob->extp, &ctx->ctl_term, NULL, 0);
- if (have_msg) {
- ASSERT(decp == ob->msg_start); (void)decp;
- ctx->b2t.u.sc.ep = NULL;
- ctx->b2t.state = B2TSize;
- ctx->b2t.aligned_alloc = NULL;
- ctx->b2t.b2ts.exttmp = 0;
- ctx->state = TRANSCODE_DEC_MSG_SIZE;
- }
- else {
- ASSERT(decp == ob->ext_endp);
- ctx->state = TRANSCODE_ENC_CTL;
- }
+ hdr += 8;
+ hopefull_ix = get_int32(hdr);
+
+ if ((~dflags & DFLAG_SPAWN)
+ && ep[0] == SMALL_TUPLE_EXT
+ && ((ep[1] == 6
+ && ep[2] == SMALL_INTEGER_EXT
+ && ep[3] == DOP_SPAWN_REQUEST)
+ || (ep[1] == 8
+ && ep[2] == SMALL_INTEGER_EXT
+ && ep[3] == DOP_SPAWN_REQUEST_TT))) {
+ /*
+ * Receiver does not support distributed spawn. Convert
+ * this packet to an empty (tick) packet, and inform
+ * spawning process that this is not supported...
+ */
+ ErtsHeapFactory factory;
+ Eterm ctl_msg, ref, pid, token, *tp, *hp;
+ Uint buf_sz;
+ byte *buf_start, *buf_end;
+ byte *ptr;
+ Uint hsz;
+
+ hdr += 4;
+ payload_ix = get_int32(hdr);
+ ASSERT(payload_ix >= 3);
+
+ if (payload_ix == 3) {
+ /* The whole control message is in iov[2].iov_base */
+ buf_sz = (Uint) iov[2].iov_len;
+ buf_start = (byte *) iov[2].iov_base;
+ buf_end = buf_start + buf_sz;
}
- else if (!(dflags & DFLAG_DIST_HDR_ATOM_CACHE)) {
- /*
- * No need for full transcoding, but primitive receiver (erl_/jinterface)
- * expects VERSION_MAGIC before both control and message terms.
- */
- if (ob->msg_start) {
- Sint ctl_bytes = ob->msg_start - ob->extp;
- ASSERT(ob->extp < ob->msg_start && ob->msg_start < ob->ext_endp);
- /* Move control term back 1 byte to make room */
- sys_memmove(ob->extp-1, ob->extp, ctl_bytes);
- *--(ob->msg_start) = VERSION_MAGIC;
- --(ob->extp);
- reds -= ctl_bytes / (B2T_BYTES_PER_REDUCTION * B2T_MEMCPY_FACTOR);
+ else {
+ /* Control message over multiple buffers... */
+ int ix;
+ buf_sz = 0;
+ for (ix = 2; ix < payload_ix; ix++)
+ buf_sz += iov[ix].iov_len;
+ ptr = buf_start = erts_alloc(ERTS_ALC_T_TMP, buf_sz);
+ buf_end = buf_start + buf_sz;
+ for (ix = 2; ix < payload_ix; ix++) {
+ sys_memcpy((void *) ptr,
+ (void *) iov[ix].iov_base,
+ iov[ix].iov_len);
+ ptr += iov[ix].iov_len;
}
- *--(ob->extp) = VERSION_MAGIC;
- goto done;
}
- else
- goto done;
- }
- else { /* continue after yield */
- ASSERT(ctx->dbg_ob == ob);
- }
- ctx->b2t.reds = reds * B2T_BYTES_PER_REDUCTION;
- switch (ctx->state) {
- case TRANSCODE_DEC_MSG_SIZE:
- hsz = decoded_size(ob->msg_start, ob->ext_endp, 0, &ctx->b2t);
- if (ctx->b2t.state == B2TSize) {
- return -1;
+ hsz = decoded_size(buf_start, buf_end, 0, NULL);
+ hp = erts_alloc(ERTS_ALC_T_TMP, hsz*sizeof(Eterm));
+ erts_factory_tmp_init(&factory, hp, hsz, ERTS_ALC_T_TMP);
+
+ ptr = dec_term(NULL, &factory, buf_start, &ctl_msg, NULL, 0);
+ ASSERT(ptr); (void)ptr;
+
+ ASSERT(is_tuple_arity(ctl_msg, 6)
+ || is_tuple_arity(ctl_msg, 8));
+ tp = tuple_val(ctl_msg);
+ ASSERT(tp[1] == make_small(DOP_SPAWN_REQUEST)
+ || tp[1] == make_small(DOP_SPAWN_REQUEST_TT));
+
+ ref = tp[2];
+ pid = tp[3];
+ if (tp[1] == make_small(DOP_SPAWN_REQUEST))
+ token = NIL;
+ else {
+ token = tp[8];
+ erts_seq_trace_update_node_token(token);
}
- ASSERT(ctx->b2t.state == B2TDecodeInit);
- ctx->msg_heap = erts_alloc(ERTS_ALC_T_DIST_TRANSCODE, hsz*sizeof(Eterm));
- ctx->b2t.u.dc.ep = ob->msg_start;
- ctx->b2t.u.dc.res = (Eterm) NULL;
- ctx->b2t.u.dc.next = &ctx->b2t.u.dc.res;
- erts_factory_tmp_init(&ctx->b2t.u.dc.factory,
- ctx->msg_heap, hsz, ERTS_ALC_T_DIST_TRANSCODE);
- ctx->b2t.u.dc.flat_maps.wstart = NULL;
- ctx->b2t.u.dc.hamt_array.pstart = NULL;
- ctx->b2t.state = B2TDecode;
-
- ctx->state = TRANSCODE_DEC_MSG;
- case TRANSCODE_DEC_MSG:
- if (ctx->b2t.reds <= 0)
- ctx->b2t.reds = 1;
- decp = dec_term(NULL, NULL, NULL, NULL, &ctx->b2t, 0);
- if (ctx->b2t.state < B2TDone) {
- return -1;
- }
- ASSERT(ctx->b2t.state == B2TDone);
- ASSERT(decp && decp <= ob->ext_endp);
- reds = ctx->b2t.reds / B2T_BYTES_PER_REDUCTION;
- b2t_destroy_context(&ctx->b2t);
-
- ctx->state = TRANSCODE_ENC_CTL;
- case TRANSCODE_ENC_CTL:
- if (!(dflags & DFLAG_DIST_HDR_ATOM_CACHE)) {
- ASSERT(!(dflags & DFLAG_NO_MAGIC));
- ob->extp -= 2; /* VERSION_MAGIC x 2 */
- }
- ob->ext_endp = ob->extp;
- i = erts_encode_dist_ext(ctx->ctl_term, &ob->ext_endp, dflags,
- NULL, NULL, NULL);
- ASSERT(i == 0); (void)i;
- ASSERT(ob->ext_endp <= ob->alloc_endp);
+ ASSERT(is_internal_ordinary_ref(tp[2]));
+ ASSERT(is_internal_pid(tp[3]));
+
+ (void) erts_proc_sig_send_dist_spawn_reply(dep->sysname,
+ ref, pid,
+ NULL, am_notsup,
+ token);
+
+ erts_factory_close(&factory);
+ erts_free(ERTS_ALC_T_TMP, hp);
+ if (buf_start != (byte *) iov[2].iov_base)
+ erts_free(ERTS_ALC_T_TMP, buf_start);
+
+ ob->eiov->vsize = 1;
+ ob->eiov->size = 0;
+
+ reds -= 4;
+
+ if (reds < 0)
+ return 0;
+ return reds;
+ }
+
+ start_r = r = reds*ERTS_TRANSCODE_REDS_FACT;
- if (!have_msg) {
- break;
+ if (~dflags & hopefull_flags) {
+
+ while (hopefull_ix != ERTS_NO_HIX) {
+ Uint32 new_hopefull_ix;
+
+ if (r <= 0) { /* yield... */
+ /* save current hopefull_ix... */
+ ep = (byte *) iov[1].iov_base;
+ ep += 5;
+ put_int32(hopefull_ix, ep);
+ return -1;
+ }
+
+ /* Read next hopefull index */
+ ep = (byte *) iov[hopefull_ix].iov_base;
+ ep -= 4;
+ new_hopefull_ix = get_int32(ep);
+ ASSERT(new_hopefull_ix == ERTS_NO_HIX
+ || (hopefull_ix < new_hopefull_ix
+ && new_hopefull_ix < eiov->vsize));
+
+ ep = (byte *) iov[hopefull_ix].iov_base;
+ switch (*ep) {
+
+ case EXPORT_EXT: {
+ byte *start_ep, *end_ep;
+ Eterm module, function;
+ if (!(hopefull_flags & DFLAG_EXPORT_PTR_TAG))
+ break;
+ /* Read original encoding... */
+ ep++;
+ start_ep = ep;
+ ep = dec_atom(NULL, ep, &module);
+ ASSERT(ep && is_atom(module));
+ ep = dec_atom(NULL, ep, &function);
+ ASSERT(ep && is_atom(function));
+ end_ep = ep;
+ ASSERT(*ep == SMALL_INTEGER_EXT
+ || *ep == INTEGER_EXT
+ || *ep == SMALL_BIG_EXT
+ || *ep == LARGE_BIG_EXT);
+
+ /*
+ * module and function atoms are encoded
+ * between start_ep and end_ep. Prepend a
+ * 2-tuple tag before the atoms and
+ * remove arity at end.
+ */
+
+ /* write fallback */
+
+ ep = start_ep;
+ ep--;
+ put_int8(2, ep);
+ ep--;
+ *ep = SMALL_TUPLE_EXT;
+
+ iov[hopefull_ix].iov_base = ep;
+
+ /* Update iov sizes... */
+ new_len = end_ep - ep;
+ eiov->size -= iov[hopefull_ix].iov_len;
+ eiov->size += new_len;
+ iov[hopefull_ix].iov_len = new_len;
+ r--;
+ break;
+ }
+
+ case BIT_BINARY_EXT: {
+ Uint bin_sz;
+ byte bitsize, epilog_byte;
+ ASSERT(hopefull_ix != ERTS_NO_HIX);
+ if (!(hopefull_flags & DFLAG_BIT_BINARIES)) {
+ /* skip to epilog... */
+ hopefull_ix = new_hopefull_ix;
+ ep = (byte *) iov[hopefull_ix].iov_base;
+ ep -= 4;
+ new_hopefull_ix = get_int32(ep);
+ ASSERT(new_hopefull_ix == ERTS_NO_HIX
+ || (hopefull_ix < new_hopefull_ix
+ && new_hopefull_ix < eiov->vsize));
+ break;
+ }
+
+ /* read original encoded prolog... */
+ ep++;
+ bin_sz = get_int32(ep);
+ ep += 4;
+ bitsize = *ep++;
+
+ /* write fallback prolog... */
+ iov[hopefull_ix].iov_base -= 4;
+ ep = (byte *) iov[hopefull_ix].iov_base;
+
+ *ep++ = SMALL_TUPLE_EXT;
+ *ep++ = 2;
+ *ep++ = BINARY_EXT;
+ put_int32(bin_sz, ep);
+ ep += 4;
+
+ /* Update iov sizes... */
+ new_len = ep - (byte *) iov[hopefull_ix].iov_base;
+ eiov->size -= iov[hopefull_ix].iov_len;
+ eiov->size += new_len;
+ iov[hopefull_ix].iov_len = new_len;
+ r--;
+#ifdef DEBUG
+ /*
+ * The binary data between the prolog and the
+ * epilog should be of size 'bin_sz - 1' and
+ * exists in the iov elements between prolog
+ * and epilog...
+ */
+ {
+ Uint ix, debug_bin_sz = 0;
+ for (ix = hopefull_ix+1; ix < new_hopefull_ix; ix++)
+ debug_bin_sz += iov[ix].iov_len;
+ ASSERT(debug_bin_sz == bin_sz - 1);
+ }
+#endif
+ /* jump to epilog... */
+ hopefull_ix = new_hopefull_ix;
+ ep = (byte *) iov[hopefull_ix].iov_base;
+
+ /* read original encoded epilog... */
+ epilog_byte = *ep;
+
+ ASSERT(1 == iov[hopefull_ix].iov_len);
+
+ iov[hopefull_ix].iov_base -= 4;
+ ep = (byte *) iov[hopefull_ix].iov_base;
+ new_hopefull_ix = get_int32(ep);
+ ASSERT(new_hopefull_ix == ERTS_NO_HIX
+ || (hopefull_ix < new_hopefull_ix
+ && new_hopefull_ix < eiov->vsize));
+
+ /* write fallback epilog... */
+
+ *ep++ = epilog_byte;
+ *ep++ = SMALL_INTEGER_EXT;
+ *ep++ = bitsize;
+
+ /* Update iov sizes... */
+ new_len = ep - (byte *) iov[hopefull_ix].iov_base;
+ eiov->size -= iov[hopefull_ix].iov_len;
+ eiov->size += new_len;
+ iov[hopefull_ix].iov_len = new_len;
+ r--;
+ break;
+ }
+
+ default:
+ ERTS_INTERNAL_ERROR("Unexpected external tag");
+ break;
+ }
+
+ hopefull_ix = new_hopefull_ix;
+ r--;
}
- ob->msg_start = ob->ext_endp;
- ctx->ttb.wstack.wstart = NULL;
- ctx->ttb.flags = dflags;
- ctx->ttb.hopefull_flags = 0;
- ctx->ttb.level = 0;
-
- ctx->state = TRANSCODE_ENC_MSG;
- case TRANSCODE_ENC_MSG:
- reds *= TERM_TO_BINARY_LOOP_FACTOR;
- if (erts_encode_dist_ext(ctx->b2t.u.dc.res, &ob->ext_endp, dflags, NULL,
- &ctx->ttb, &reds)) {
- return -1;
+ }
+
+ /*
+ * Replace hopefull data header with actual header...
+ */
+ ep = (byte *) iov[1].iov_base;
+ eiov->size -= iov[1].iov_len;
+
+ if (dflags & (DFLAG_DIST_HDR_ATOM_CACHE|DFLAG_FRAGMENTS)) {
+ /*
+ * Encoding was done without atom caching but receiver expects
+ * a dist header, so we prepend an empty one.
+ */
+ *ep++ = VERSION_MAGIC;
+ *ep++ = DIST_HEADER;
+ *ep++ = 0; /* NumberOfAtomCacheRefs */
+ }
+ else {
+ hdr += 4;
+ payload_ix = get_int32(hdr);
+
+ if (payload_ix) {
+ ASSERT(0 < payload_ix && payload_ix < eiov->vsize);
+ /* Prepend version magic on payload. */
+ iov[payload_ix].iov_base--;
+ *((byte *) iov[payload_ix].iov_base) = VERSION_MAGIC;
+ iov[payload_ix].iov_len++;
+ eiov->size++;
+ r--;
}
- reds /= TERM_TO_BINARY_LOOP_FACTOR;
- ASSERT(ob->ext_endp <= ob->alloc_endp);
- ASSERT(!ctx->ttb.hopefull_flags);
+ *ep++ = PASS_THROUGH;
+ *ep++ = VERSION_MAGIC;
}
- transcode_free_ctx(dep);
-done:
- if (!(dflags & DFLAG_DIST_HDR_ATOM_CACHE))
- *--(ob->extp) = PASS_THROUGH;
+ iov[1].iov_len = ep - (byte *) iov[1].iov_base;
+ eiov->size += iov[1].iov_len;
- if (reds < 0)
- reds = 0;
+ r--;
+
+ /* done... */
+ reds -= (start_r - r)/ERTS_TRANSCODE_REDS_FACT + 1;
+ if (reds < 0)
+ return 0;
return reds;
}
diff --git a/erts/emulator/beam/external.h b/erts/emulator/beam/external.h
index e362a6c81f..bc006f83e2 100644
--- a/erts/emulator/beam/external.h
+++ b/erts/emulator/beam/external.h
@@ -60,6 +60,7 @@
#define DIST_HEADER 'D'
#define DIST_FRAG_HEADER 'E'
#define DIST_FRAG_CONT 'F'
+#define HOPEFUL_DATA 'H'
#define ATOM_CACHE_REF 'R'
#define ATOM_INTERNAL_REF2 'I'
#define ATOM_INTERNAL_REF3 'K'
@@ -90,6 +91,8 @@
#undef ERL_NODE_TABLES_BASIC_ONLY
#include "erl_alloc.h"
+#define ERTS_NO_HIX (~((Uint32) 0))
+
#define ERTS_ATOM_CACHE_SIZE 2048
typedef struct cache {
@@ -153,16 +156,19 @@ typedef struct {
/* -------------------------------------------------------------------------- */
+struct TTBSizeContext_;
+struct TTBEncodeContext_;
void erts_init_atom_cache_map(ErtsAtomCacheMap *);
void erts_reset_atom_cache_map(ErtsAtomCacheMap *);
void erts_destroy_atom_cache_map(ErtsAtomCacheMap *);
-void erts_finalize_atom_cache_map(ErtsAtomCacheMap *, Uint32);
+void erts_finalize_atom_cache_map(ErtsAtomCacheMap *, Uint64);
-Uint erts_encode_ext_dist_header_size(ErtsAtomCacheMap *, Uint);
-byte *erts_encode_ext_dist_header_setup(byte *, ErtsAtomCacheMap *, Uint, Eterm);
+Uint erts_encode_ext_dist_header_size(struct TTBEncodeContext_ *ctx, ErtsAtomCacheMap *, Uint);
+byte *erts_encode_ext_dist_header_setup(struct TTBEncodeContext_ *ctx, 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);
+Sint erts_encode_ext_dist_header_finalize(ErtsDistOutputBuf*, DistEntry *, Uint64 dflags, Sint reds);
struct erts_dsig_send_context;
typedef enum {
@@ -171,12 +177,13 @@ typedef enum {
ERTS_EXT_SZ_SYSTEM_LIMIT
} ErtsExtSzRes;
-ErtsExtSzRes erts_encode_dist_ext_size(Eterm, Uint32, ErtsAtomCacheMap*, Uint* szp);
-ErtsExtSzRes erts_encode_dist_ext_size_ctx(Eterm term, struct erts_dsig_send_context* ctx, Uint* szp);
-struct TTBEncodeContext_;
-int erts_encode_dist_ext(Eterm, byte **, Uint32, ErtsAtomCacheMap *,
- struct TTBEncodeContext_ *, Sint* reds);
-
+ErtsExtSzRes erts_encode_dist_ext_size(Eterm term, ErtsAtomCacheMap *acmp,
+ struct TTBSizeContext_ *ctx,
+ Uint* szp, Sint *redsp,
+ Sint *vlenp, Uint *fragments);
+int erts_encode_dist_ext(Eterm, byte **, Uint64, ErtsAtomCacheMap *,
+ struct TTBEncodeContext_ *, Uint *,
+ Sint *);
ErtsExtSzRes erts_encode_ext_size(Eterm, Uint *szp);
ErtsExtSzRes erts_encode_ext_size_2(Eterm, unsigned, Uint *szp);
Uint erts_encode_ext_size_ets(Eterm);
@@ -207,7 +214,8 @@ Sint erts_decode_ext_size_ets(byte*, Uint);
Eterm erts_decode_ext(ErtsHeapFactory*, byte**, Uint32 flags);
Eterm erts_decode_ext_ets(ErtsHeapFactory*, byte*);
-Eterm erts_term_to_binary(Process* p, Eterm Term, int level, Uint flags);
+Eterm erts_term_to_binary(Process* p, Eterm Term, int level, Uint64 flags);
+Eterm erts_debug_term_to_binary(Process *p, Eterm term, Eterm opts);
Sint erts_binary2term_prepare(ErtsBinary2TermState *, byte *, Sint);
void erts_binary2term_abort(ErtsBinary2TermState *);
diff --git a/erts/emulator/beam/global.h b/erts/emulator/beam/global.h
index 8a04a4c7f6..b9af01ee57 100644
--- a/erts/emulator/beam/global.h
+++ b/erts/emulator/beam/global.h
@@ -74,9 +74,8 @@ struct enif_resource_type_t
struct enif_resource_type_t* next; /* list of all resource types */
struct enif_resource_type_t* prev;
struct erl_module_nif* owner; /* that created this type and thus implements the destructor*/
- ErlNifResourceDtor* dtor; /* user destructor function */
- ErlNifResourceStop* stop;
- ErlNifResourceDown* down;
+ ErlNifResourceTypeInit fn;
+ ErlNifResourceTypeInit fn_real;
erts_refc_t refc; /* num of resources of this type (HOTSPOT warning)
+1 for active erl_module_nif */
Eterm module;
@@ -110,6 +109,7 @@ typedef struct ErtsResource_
extern Eterm erts_bld_resource_ref(Eterm** hp, ErlOffHeap*, ErtsResource*);
+extern BeamInstr* erts_call_nif_early(Process* c_p, ErtsCodeInfo* ci);
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);
@@ -122,6 +122,10 @@ void erts_nif_demonitored(ErtsResource* resource);
extern void erts_add_taint(Eterm mod_atom);
extern Eterm erts_nif_taints(Process* p);
extern void erts_print_nif_taints(fmtfn_t to, void* to_arg);
+
+/* Loads the specified NIF. The caller must have code write permission. */
+Eterm erts_load_nif(Process *c_p, BeamInstr *I, Eterm filename, Eterm args);
+
void erts_unload_nif(struct erl_module_nif* nif);
extern void erl_nif_init(void);
extern int erts_nif_get_funcs(struct erl_module_nif*,
@@ -885,6 +889,8 @@ void erts_bif_info_init(void);
/* bif.c */
+void erts_write_bif_wrapper(Export *export, BeamInstr *address);
+
void erts_queue_monitor_message(Process *,
ErtsProcLocks*,
Eterm,
@@ -926,6 +932,8 @@ void erts_queue_release_literals(Process *c_p, ErtsLiteralArea* literals);
#define ERTS_LITERAL_AREA_ALLOC_SIZE(N) \
(sizeof(ErtsLiteralArea) + sizeof(Eterm)*((N) - 1))
+#define ERTS_LITERAL_AREA_SIZE(AP) \
+ (ERTS_LITERAL_AREA_ALLOC_SIZE((AP)->end - (AP)->start))
extern erts_atomic_t erts_copy_literal_area__;
#define ERTS_COPY_LITERAL_AREA() \
@@ -1135,6 +1143,12 @@ extern int is_node_name_atom(Eterm a);
extern int erts_net_message(Port *, DistEntry *, Uint32 conn_id,
byte *, ErlDrvSizeT, Binary *, byte *, ErlDrvSizeT);
+int erts_dist_pend_spawn_exit_delete(ErtsMonitor *mon);
+int erts_dist_pend_spawn_exit_parent_setup(ErtsMonitor *mon);
+int erts_dist_pend_spawn_exit_parent_wait(Process *c_p,
+ ErtsProcLocks locks,
+ ErtsMonitor *mon);
+
extern void init_dist(void);
extern int stop_dist(void);
@@ -1152,6 +1166,7 @@ void erts_dirty_process_main(ErtsSchedulerData *);
Eterm build_stacktrace(Process* c_p, Eterm exc);
Eterm expand_error_value(Process* c_p, Uint freason, Eterm Value);
void erts_save_stacktrace(Process* p, struct StackTrace* s, int depth);
+BeamInstr *erts_printable_return_address(Process* p, Eterm *E) ERTS_NOINLINE;
/* erl_init.c */
@@ -1291,7 +1306,6 @@ Sint erts_binary_set_loop_limit(Sint limit);
/* erl_bif_persistent.c */
void erts_init_bif_persistent_term(void);
-Uint erts_persistent_term_count(void);
void erts_init_persistent_dumping(void);
extern ErtsLiteralArea** erts_persistent_areas;
extern Uint erts_num_persistent_areas;
diff --git a/erts/emulator/beam/hash.c b/erts/emulator/beam/hash.c
index 8954dbb06c..177b7cc3d1 100644
--- a/erts/emulator/beam/hash.c
+++ b/erts/emulator/beam/hash.c
@@ -30,37 +30,19 @@
#include "hash.h"
/*
-** List of sizes (all are primes)
-*/
-static const int h_size_table[] = {
- 2, 5, 11, 23, 47, 97, 197, 397, 797, /* double upto here */
- 1201, 1597,
- 2411, 3203,
- 4813, 6421,
- 9643, 12853,
- 19289, 25717,
- 51437,
- 102877,
- 205759,
- 411527,
- 823117,
- 1646237,
- 3292489,
- 6584983,
- 13169977,
- 26339969,
- 52679969,
- -1
-};
-
-/*
** Get info about hash
**
*/
+#define MAX_SHIFT (ERTS_SIZEOF_TERM * 8)
+
+static int hash_get_slots(Hash *h) {
+ return UWORD_CONSTANT(1) << (MAX_SHIFT - h->shift);
+}
+
void hash_get_info(HashInfo *hi, Hash *h)
{
- int size = h->size;
+ int size = hash_get_slots(h);
int i;
int max_depth = 0;
int objects = 0;
@@ -84,7 +66,7 @@ void hash_get_info(HashInfo *hi, Hash *h)
ASSERT(objects == h->nobjs);
hi->name = h->name;
- hi->size = h->size;
+ hi->size = hash_get_slots(h);
hi->used = used;
hi->objs = h->nobjs;
hi->depth = max_depth;
@@ -118,15 +100,15 @@ hash_table_sz(Hash *h)
int i;
for(i=0;h->name[i];i++);
i++;
- return sizeof(Hash) + h->size*sizeof(HashBucket*) + i;
+ return sizeof(Hash) + hash_get_slots(h)*sizeof(HashBucket*) + i;
}
static ERTS_INLINE void set_thresholds(Hash* h)
{
- h->grow_threshold = (8*h->size)/5; /* grow at 160% load */
- if (h->size_ix > h->min_size_ix)
- h->shrink_threshold = h->size / 5; /* shrink at 20% load */
+ h->grow_threshold = (8*hash_get_slots(h))/5; /* grow at 160% load */
+ if (h->shift < h->max_shift)
+ h->shrink_threshold = hash_get_slots(h) / 5; /* shrink at 20% load */
else
h->shrink_threshold = -1; /* never shrink below inital size */
}
@@ -138,29 +120,27 @@ static ERTS_INLINE void set_thresholds(Hash* h)
Hash* hash_init(int type, Hash* h, char* name, int size, HashFunctions fun)
{
int sz;
- int ix = 0;
+ int shift = 1;
h->meta_alloc_type = type;
- while (h_size_table[ix] != -1 && h_size_table[ix] < size)
- ix++;
- if (h_size_table[ix] == -1)
- return NULL;
-
- size = h_size_table[ix];
- sz = size*sizeof(HashBucket*);
+ while ((UWORD_CONSTANT(1) << shift) < size)
+ shift++;
- h->bucket = (HashBucket**) fun.meta_alloc(h->meta_alloc_type, sz);
-
- memzero(h->bucket, sz);
h->is_allocated = 0;
h->name = name;
h->fun = fun;
- h->size = size;
- h->size_ix = ix;
- h->min_size_ix = ix;
+ h->shift = MAX_SHIFT - shift;
+ h->max_shift = h->shift;
h->nobjs = 0;
set_thresholds(h);
+
+ sz = hash_get_slots(h) * sizeof(HashBucket*);
+ h->bucket = (HashBucket**) fun.meta_alloc(h->meta_alloc_type, sz);
+ memzero(h->bucket, sz);
+
+ ASSERT(h->shift > 0 && h->shift < 64);
+
return h;
}
@@ -183,7 +163,7 @@ Hash* hash_new(int type, char* name, int size, HashFunctions fun)
*/
void hash_delete(Hash* h)
{
- int old_size = h->size;
+ int old_size = hash_get_slots(h);
int i;
for (i = 0; i < old_size; i++) {
@@ -206,22 +186,20 @@ void hash_delete(Hash* h)
static void rehash(Hash* h, int grow)
{
int sz;
- int old_size = h->size;
+ int old_size = hash_get_slots(h);
HashBucket** new_bucket;
int i;
if (grow) {
- if ((h_size_table[h->size_ix+1]) == -1)
- return;
- h->size_ix++;
+ h->shift--;
}
else {
- if (h->size_ix == 0)
+ if (h->shift == h->max_shift)
return;
- h->size_ix--;
+ h->shift++;
}
- h->size = h_size_table[h->size_ix];
- sz = h->size*sizeof(HashBucket*);
+
+ sz = hash_get_slots(h)*sizeof(HashBucket*);
new_bucket = (HashBucket **) h->fun.meta_alloc(h->meta_alloc_type, sz);
memzero(new_bucket, sz);
@@ -230,7 +208,7 @@ static void rehash(Hash* h, int grow)
HashBucket* b = h->bucket[i];
while (b != (HashBucket*) 0) {
HashBucket* b_next = b->next;
- int ix = b->hvalue % h->size;
+ Uint ix = hash_get_slot(h, b->hvalue);
b->next = new_bucket[ix];
new_bucket[ix] = b;
b = b_next;
@@ -247,16 +225,7 @@ static void rehash(Hash* h, int grow)
*/
void* hash_get(Hash* h, void* tmpl)
{
- HashValue hval = h->fun.hash(tmpl);
- int ix = hval % h->size;
- HashBucket* b = h->bucket[ix];
-
- while(b != (HashBucket*) 0) {
- if ((b->hvalue == hval) && (h->fun.cmp(tmpl, (void*)b) == 0))
- return (void*) b;
- b = b->next;
- }
- return (void*) 0;
+ return hash_fetch(h, tmpl, h->fun.hash, h->fun.cmp);
}
/*
@@ -265,7 +234,7 @@ void* hash_get(Hash* h, void* tmpl)
void* hash_put(Hash* h, void* tmpl)
{
HashValue hval = h->fun.hash(tmpl);
- int ix = hval % h->size;
+ Uint ix = hash_get_slot(h, hval);
HashBucket* b = h->bucket[ix];
while(b != (HashBucket*) 0) {
@@ -291,7 +260,7 @@ void* hash_put(Hash* h, void* tmpl)
void* hash_erase(Hash* h, void* tmpl)
{
HashValue hval = h->fun.hash(tmpl);
- int ix = hval % h->size;
+ Uint ix = hash_get_slot(h, hval);
HashBucket* b = h->bucket[ix];
HashBucket* prev = 0;
@@ -323,7 +292,7 @@ void *
hash_remove(Hash *h, void *tmpl)
{
HashValue hval = h->fun.hash(tmpl);
- int ix = hval % h->size;
+ Uint ix = hash_get_slot(h, hval);
HashBucket *b = h->bucket[ix];
HashBucket *prev = NULL;
@@ -343,11 +312,11 @@ hash_remove(Hash *h, void *tmpl)
return NULL;
}
-void hash_foreach(Hash* h, void (*func)(void *, void *), void *func_arg2)
+void hash_foreach(Hash* h, HFOREACH_FUN func, void *func_arg2)
{
int i;
- for (i = 0; i < h->size; i++) {
+ for (i = 0; i < hash_get_slots(h); i++) {
HashBucket* b = h->bucket[i];
while(b != (HashBucket*) 0) {
(*func)((void *) b, func_arg2);
diff --git a/erts/emulator/beam/hash.h b/erts/emulator/beam/hash.h
index d319aaca83..4e8eb6594b 100644
--- a/erts/emulator/beam/hash.h
+++ b/erts/emulator/beam/hash.h
@@ -18,16 +18,16 @@
* %CopyrightEnd%
*/
-/*
-** General hash functions
-**
-*/
+/**
+ * General hash functions
+ *
+ **/
#ifndef __HASH_H__
#define __HASH_H__
#include "sys.h"
-typedef unsigned long HashValue;
+typedef UWord HashValue;
typedef struct hash Hash;
typedef int (*HCMP_FUN)(void*, void*);
@@ -38,6 +38,7 @@ typedef void (*HFREE_FUN)(void*);
typedef void* (*HMALLOC_FUN)(int,size_t);
typedef void (*HMFREE_FUN)(int,void*);
typedef int (*HMPRINT_FUN)(fmtfn_t,void*,char*, ...);
+typedef void (*HFOREACH_FUN)(void *, void *);
/*
** This bucket must be placed in top of
@@ -75,11 +76,10 @@ struct hash
int is_allocated; /* 0 iff hash structure is on stack or is static */
int meta_alloc_type; /* argument to pass to meta_alloc and meta_free */
char* name; /* Table name (static string, for debugging) */
- int size; /* Number of slots */
+ int shift; /* How much to shift the hash value */
+ int max_shift; /* Never shift more than this value */
int shrink_threshold;
int grow_threshold;
- int size_ix; /* Size index in size table */
- int min_size_ix; /* Never shrink table smaller than this */
int nobjs; /* Number of objects in table */
HashBucket** bucket; /* Vector of bucket pointers (objects) */
};
@@ -96,6 +96,54 @@ void* hash_get(Hash*, void*);
void* hash_put(Hash*, void*);
void* hash_erase(Hash*, void*);
void* hash_remove(Hash*, void*);
-void hash_foreach(Hash*, void (*func)(void *, void *), void *);
+void hash_foreach(Hash*, HFOREACH_FUN, void *);
+
+ERTS_GLB_INLINE Uint hash_get_slot(Hash *h, HashValue hv);
+ERTS_GLB_INLINE void* hash_fetch(Hash *, void*, H_FUN, HCMP_FUN);
+
+#if ERTS_GLB_INLINE_INCL_FUNC_DEF
+
+ERTS_GLB_INLINE Uint
+hash_get_slot(Hash *h, HashValue hv)
+{
+ /* This slot mapping function uses fibonacci hashing in order to
+ * protect itself against a very bad hash function. This is not
+ * a hash function, so the user of hash.h should still spend time
+ * to figure out a good hash function for its data.
+ *
+ * See https://probablydance.com/2018/06/16/fibonacci-hashing-the-optimization-that-the-world-forgot-or-a-better-alternative-to-integer-modulo/
+ * for some thoughts and ideas about fibonacci hashing.
+ */
+
+ /* This is not strictly part of the fibonacci hashing algorithm
+ * but it does help to spread the values of the mapping function better.
+ */
+ hv ^= hv >> h->shift;
+#ifdef ARCH_64
+ /* 2^64 / 1.61803398875 = 11400714819323198485.... */
+ return (UWORD_CONSTANT(11400714819323198485) * hv) >> h->shift;
+#else
+ /* 2^32 / 1.61803398875 = 2654435769.... */
+ return (UWORD_CONSTANT(2654435769) * hv) >> h->shift;
+#endif
+}
+
+ERTS_GLB_INLINE void* hash_fetch(Hash *h, void* tmpl, H_FUN hash, HCMP_FUN cmp)
+{
+ HashValue hval = hash(tmpl);
+ Uint ix = hash_get_slot(h, hval);
+ HashBucket* b = h->bucket[ix];
+ ASSERT(h->fun.hash == hash);
+ ASSERT(h->fun.cmp == cmp);
+
+ while(b != (HashBucket*) 0) {
+ if ((b->hvalue == hval) && (cmp(tmpl, (void*)b) == 0))
+ return (void*) b;
+ b = b->next;
+ }
+ return (void*) 0;
+}
+
+#endif /* ERTS_GLB_INLINE_INCL_FUNC_DEF */
#endif
diff --git a/erts/emulator/beam/index.c b/erts/emulator/beam/index.c
index be1771b037..09d3c24424 100644
--- a/erts/emulator/beam/index.c
+++ b/erts/emulator/beam/index.c
@@ -114,35 +114,26 @@ int index_get(IndexTable* t, void* tmpl)
return -1;
}
-void erts_index_merge(Hash* src, IndexTable* dst)
+static void index_merge_foreach(IndexSlot *p, IndexTable *dst)
{
- int limit = src->size;
- HashBucket** bucket = src->bucket;
- int i;
-
- for (i = 0; i < limit; i++) {
- HashBucket* b = bucket[i];
- IndexSlot* p;
- int ix;
-
- while (b) {
- Uint sz;
- ix = dst->entries++;
- if (ix >= dst->size) {
- if (ix >= dst->limit) {
- erts_exit(ERTS_ERROR_EXIT, "no more index entries in %s (max=%d)\n",
- dst->htable.name, dst->limit);
- }
- sz = INDEX_PAGE_SIZE*sizeof(IndexSlot*);
- dst->seg_table[ix>>INDEX_PAGE_SHIFT] = erts_alloc(dst->type, sz);
- dst->size += INDEX_PAGE_SIZE;
- }
- p = (IndexSlot*) b;
- p->index = ix;
- dst->seg_table[ix>>INDEX_PAGE_SHIFT][ix&INDEX_PAGE_MASK] = p;
- b = b->next;
- }
+ Uint sz;
+ int ix = dst->entries++;
+ if (ix >= dst->size) {
+ if (ix >= dst->limit) {
+ erts_exit(ERTS_ERROR_EXIT, "no more index entries in %s (max=%d)\n",
+ dst->htable.name, dst->limit);
+ }
+ sz = INDEX_PAGE_SIZE*sizeof(IndexSlot*);
+ dst->seg_table[ix>>INDEX_PAGE_SHIFT] = erts_alloc(dst->type, sz);
+ dst->size += INDEX_PAGE_SIZE;
}
+ p->index = ix;
+ dst->seg_table[ix>>INDEX_PAGE_SHIFT][ix&INDEX_PAGE_MASK] = p;
+}
+
+void erts_index_merge(Hash* src, IndexTable* dst)
+{
+ hash_foreach(src, (HFOREACH_FUN)index_merge_foreach, dst);
}
void index_erase_latest_from(IndexTable* t, Uint from_ix)
diff --git a/erts/emulator/beam/instrs.tab b/erts/emulator/beam/instrs.tab
index 7cffe7fb5c..c28b5f3443 100644
--- a/erts/emulator/beam/instrs.tab
+++ b/erts/emulator/beam/instrs.tab
@@ -19,7 +19,12 @@
// %CopyrightEnd%
//
-// Stack manipulation instructions
+//
+// Stack manipulation instructions follow.
+//
+// See the comment for AH() in macros.tab for information about
+// the layout of stack frames.
+//
allocate(NeedStack, Live) {
$AH($NeedStack, 0, $Live);
@@ -58,105 +63,170 @@ allocate_heap_zero(NeedStack, NeedHeap, Live) {
deallocate(Deallocate) {
//| -no_prefetch
- SET_CP(c_p, (BeamInstr *) cp_val(*E));
E = ADD_BYTE_OFFSET(E, $Deallocate);
}
-deallocate_return(Deallocate) {
- //| -no_next
- int words_to_pop = $Deallocate;
- SET_I((BeamInstr *) cp_val(*E));
- E = ADD_BYTE_OFFSET(E, words_to_pop);
- CHECK_TERM(x(0));
- DispatchReturn;
+//
+// Micro-benchmarks showed that the deallocate_return instruction
+// became slower when the continuation pointer was moved from
+// the process struct to the stack. The reason seems to be read
+// dependencies, i.e. that the CPU cannot figure out beforehand
+// from which position on the stack the continuation pointer
+// should be fetched.
+//
+// Initializing num_bytes with a constant value seems to restore
+// the lost speed, so we've specialized the instruction for the
+// most common values.
+//
+
+deallocate_return0 := dealloc_ret.n0.execute;
+deallocate_return1 := dealloc_ret.n1.execute;
+deallocate_return2 := dealloc_ret.n2.execute;
+deallocate_return3 := dealloc_ret.n3.execute;
+deallocate_return4 := dealloc_ret.n4.execute;
+deallocate_return := dealloc_ret.var.execute;
+
+dealloc_ret.head() {
+ Uint num_bytes;
}
-move_deallocate_return(Src, Deallocate) {
- x(0) = $Src;
- $deallocate_return($Deallocate);
+dealloc_ret.n0() {
+ num_bytes = (0+1) * sizeof(Eterm);
}
-// Call instructions
+dealloc_ret.n1() {
+ num_bytes = (1+1) * sizeof(Eterm);
+}
+
+dealloc_ret.n2() {
+ num_bytes = (2+1) * sizeof(Eterm);
+}
+
+dealloc_ret.n3() {
+ num_bytes = (3+1) * sizeof(Eterm);
+}
+
+dealloc_ret.n4() {
+ num_bytes = (4+1) * sizeof(Eterm);
+}
+
+dealloc_ret.var(Deallocate) {
+ num_bytes = $Deallocate;
+}
-DISPATCH_REL(CallDest) {
+dealloc_ret.execute() {
//| -no_next
- $SET_I_REL($CallDest);
- DTRACE_LOCAL_CALL(c_p, erts_code_to_codemfa(I));
- Dispatch();
+
+ E = ADD_BYTE_OFFSET(E, num_bytes);
+ $RETURN();
+ CHECK_TERM(x(0));
+ $DISPATCH_RETURN();
}
-DISPATCH_ABS(CallDest) {
+move_deallocate_return(Src, Deallocate) {
//| -no_next
- SET_I((BeamInstr *) $CallDest);
- DTRACE_LOCAL_CALL(c_p, erts_code_to_codemfa(I));
- Dispatch();
+
+ /*
+ * Explicitly do reads first to mitigate the impact of read
+ * dependencies.
+ */
+
+ Uint bytes_to_pop = $Deallocate;
+ Eterm src = $Src;
+ E = ADD_BYTE_OFFSET(E, bytes_to_pop);
+ x(0) = src;
+ DTRACE_RETURN_FROM_PC(c_p, I);
+ $RETURN();
+ CHECK_TERM(x(0));
+ $DISPATCH_RETURN();
}
+// Call instructions
+
i_call(CallDest) {
- SET_CP(c_p, $NEXT_INSTRUCTION);
+ //| -no_next
+ $SAVE_CONTINUATION_POINTER($NEXT_INSTRUCTION);
$DISPATCH_REL($CallDest);
}
move_call(Src, CallDest) {
- x(0) = $Src;
- SET_CP(c_p, $NEXT_INSTRUCTION);
- $DISPATCH_REL($CallDest);
+ //| -no_next
+ Eterm call_dest = $CallDest;
+ Eterm src = $Src;
+ $SAVE_CONTINUATION_POINTER($NEXT_INSTRUCTION);
+ x(0) = src;
+ $DISPATCH_REL(call_dest);
}
i_call_last(CallDest, Deallocate) {
+ //| -no_next
$deallocate($Deallocate);
$DISPATCH_REL($CallDest);
}
move_call_last(Src, CallDest, Deallocate) {
- x(0) = $Src;
- $i_call_last($CallDest, $Deallocate);
+ //| -no_next
+ Eterm call_dest = $CallDest;
+ Eterm src = $Src;
+ $deallocate($Deallocate);
+ x(0) = src;
+ $DISPATCH_REL(call_dest);
}
i_call_only(CallDest) {
+ //| -no_next
$DISPATCH_REL($CallDest);
}
move_call_only(Src, CallDest) {
- x(0) = $Src;
- $i_call_only($CallDest);
-}
-
-DISPATCHX(Dest) {
//| -no_next
- DTRACE_GLOBAL_CALL_FROM_EXPORT(c_p, $Dest);
- // Dispatchx assumes the Export* is in Arg(0)
- I = (&$Dest) - 1;
- Dispatchx();
+ Eterm call_dest = $CallDest;
+ Eterm src = $Src;
+ x(0) = src;
+ $DISPATCH_REL(call_dest);
}
i_call_ext(Dest) {
- SET_CP(c_p, $NEXT_INSTRUCTION);
- $DISPATCHX($Dest);
+ //| -no_next
+ $SAVE_CONTINUATION_POINTER($NEXT_INSTRUCTION);
+ $DISPATCH_EXPORT($Dest);
}
-i_move_call_ext(Src, Dest) {
- x(0) = $Src;
- $i_call_ext($Dest);
+i_move_call_ext(Src, CallDest) {
+ //| -no_next
+ Eterm call_dest = $CallDest;
+ Eterm src = $Src;
+ $SAVE_CONTINUATION_POINTER($NEXT_INSTRUCTION);
+ x(0) = src;
+ $DISPATCH_EXPORT(call_dest);
}
i_call_ext_only(Dest) {
- $DISPATCHX($Dest);
+ //| -no_next
+ $DISPATCH_EXPORT($Dest);
}
-i_move_call_ext_only(Dest, Src) {
- x(0) = $Src;
- $i_call_ext_only($Dest);
+i_move_call_ext_only(CallDest, Src) {
+ //| -no_next
+ Eterm call_dest = $CallDest;
+ Eterm src = $Src;
+ x(0) = src;
+ $DISPATCH_EXPORT(call_dest);
}
i_call_ext_last(Dest, Deallocate) {
+ //| -no_next
$deallocate($Deallocate);
- $DISPATCHX($Dest);
+ $DISPATCH_EXPORT($Dest);
}
-i_move_call_ext_last(Dest, StackOffset, Src) {
- x(0) = $Src;
- $i_call_ext_last($Dest, $StackOffset);
+i_move_call_ext_last(CallDest, Deallocate, Src) {
+ //| -no_next
+ Eterm call_dest = $CallDest;
+ Eterm src = $Src;
+ $deallocate($Deallocate);
+ x(0) = src;
+ $DISPATCH_EXPORT(call_dest);
}
APPLY(I, Deallocate, Next) {
@@ -167,21 +237,23 @@ APPLY(I, Deallocate, Next) {
}
HANDLE_APPLY_ERROR() {
- I = handle_error(c_p, I, reg, &bif_export[BIF_apply_3]->info.mfa);
+ I = handle_error(c_p, I, reg, &bif_trap_export[BIF_apply_3].info.mfa);
goto post_error_handling;
}
i_apply() {
+ //| -no_next
BeamInstr *next;
$APPLY(NULL, 0, next);
if (ERTS_LIKELY(next != NULL)) {
- SET_CP(c_p, $NEXT_INSTRUCTION);
+ $SAVE_CONTINUATION_POINTER($NEXT_INSTRUCTION);
$DISPATCH_ABS(next);
}
$HANDLE_APPLY_ERROR();
}
i_apply_last(Deallocate) {
+ //| -no_next
BeamInstr *next;
$APPLY(I, $Deallocate, next);
if (ERTS_LIKELY(next != NULL)) {
@@ -192,6 +264,7 @@ i_apply_last(Deallocate) {
}
i_apply_only() {
+ //| -no_next
BeamInstr *next;
$APPLY(I, 0, next);
if (ERTS_LIKELY(next != NULL)) {
@@ -208,16 +281,18 @@ FIXED_APPLY(Arity, I, Deallocate, Next) {
}
apply(Arity) {
+ //| -no_next
BeamInstr *next;
$FIXED_APPLY($Arity, NULL, 0, next);
if (ERTS_LIKELY(next != NULL)) {
- SET_CP(c_p, $NEXT_INSTRUCTION);
+ $SAVE_CONTINUATION_POINTER($NEXT_INSTRUCTION);
$DISPATCH_ABS(next);
}
$HANDLE_APPLY_ERROR();
}
apply_last(Arity, Deallocate) {
+ //| -no_next
BeamInstr *next;
$FIXED_APPLY($Arity, I, $Deallocate, next);
if (ERTS_LIKELY(next != NULL)) {
@@ -237,23 +312,19 @@ HANDLE_APPLY_FUN_ERROR() {
goto find_func_info;
}
-DISPATCH_FUN(I) {
- //| -no_next
- SET_I($I);
- Dispatchfun();
-}
-
i_apply_fun() {
+ //| -no_next
BeamInstr *next;
$APPLY_FUN(next);
if (ERTS_LIKELY(next != NULL)) {
- SET_CP(c_p, $NEXT_INSTRUCTION);
+ $SAVE_CONTINUATION_POINTER($NEXT_INSTRUCTION);
$DISPATCH_FUN(next);
}
$HANDLE_APPLY_FUN_ERROR();
}
i_apply_fun_last(Deallocate) {
+ //| -no_next
BeamInstr *next;
$APPLY_FUN(next);
if (ERTS_LIKELY(next != NULL)) {
@@ -264,6 +335,7 @@ i_apply_fun_last(Deallocate) {
}
i_apply_fun_only() {
+ //| -no_next
BeamInstr *next;
$APPLY_FUN(next);
if (ERTS_LIKELY(next != NULL)) {
@@ -280,16 +352,18 @@ CALL_FUN(Fun, Next) {
}
i_call_fun(Fun) {
+ //| -no_next
BeamInstr *next;
$CALL_FUN($Fun, next);
if (ERTS_LIKELY(next != NULL)) {
- SET_CP(c_p, $NEXT_INSTRUCTION);
+ $SAVE_CONTINUATION_POINTER($NEXT_INSTRUCTION);
$DISPATCH_FUN(next);
}
$HANDLE_APPLY_FUN_ERROR();
}
i_call_fun_last(Fun, Deallocate) {
+ //| -no_next
BeamInstr *next;
$CALL_FUN($Fun, next);
if (ERTS_LIKELY(next != NULL)) {
@@ -301,18 +375,12 @@ i_call_fun_last(Fun, Deallocate) {
return() {
//| -no_next
- SET_I(c_p->cp);
- DTRACE_RETURN_FROM_PC(c_p);
-
- /*
- * We must clear the CP to make sure that a stale value do not
- * create a false module dependcy preventing code upgrading.
- * It also means that we can use the CP in stack backtraces.
- */
- c_p->cp = 0;
+ DTRACE_RETURN_FROM_PC(c_p, I);
+ $RETURN();
CHECK_TERM(r(0));
HEAP_SPACE_VERIFIED(0);
- DispatchReturn;
+
+ $DISPATCH_RETURN();
}
get_list(Src, Hd, Tl) {
@@ -478,16 +546,21 @@ i_make_fun(FunP, NumFree) {
}
move_trim(Src, Dst, Words) {
- Uint cp = E[0];
$Dst = $Src;
- E += $Words;
- E[0] = cp;
+ $i_trim($Words);
}
i_trim(Words) {
- Uint cp = E[0];
E += $Words;
- E[0] = cp;
+
+ /*
+ * Clear the reserved location for the continuation pointer at
+ * E[0]. This is not strictly necessary for correctness, but if a
+ * GC is triggered before E[0] is overwritten by another
+ * continuation pointer the now dead term at E[0] would be
+ * retained by the GC.
+ */
+ E[0] = NIL;
}
move(Src, Dst) {
@@ -599,9 +672,9 @@ move_window5(S1, S2, S3, S4, S5, D) {
move_return(Src) {
//| -no_next
x(0) = $Src;
- SET_I(c_p->cp);
- c_p->cp = 0;
- DispatchReturn;
+ DTRACE_RETURN_FROM_PC(c_p, I);
+ $RETURN();
+ $DISPATCH_RETURN();
}
move_x1(Src) {
@@ -683,10 +756,11 @@ swap(R1, R2) {
$R2 = V;
}
-swap_temp(R1, R2, Tmp) {
- Eterm V = $R1;
- $R1 = $R2;
- $R2 = $Tmp = V;
+swap2(R1, R2, R3) {
+ Eterm V = $R2;
+ $R2 = $R1;
+ $R1 = $R3;
+ $R3 = V;
}
test_heap(Nh, Live) {
@@ -952,6 +1026,14 @@ is_ge(Fail, X, Y) {
CMP_GE_ACTION($X, $Y, $FAIL($Fail));
}
+is_lt_literal(Fail, X, Y) {
+ CMP_LT_LITERAL_ACTION($X, $Y, $FAIL($Fail));
+}
+
+is_ge_literal(Fail, X, Y) {
+ CMP_GE_LITERAL_ACTION($X, $Y, $FAIL($Fail));
+}
+
badarg(Fail) {
$BADARG($Fail);
//| -no_next;
@@ -991,6 +1073,7 @@ catch_end(Y) {
$try_end($Y);
if (is_non_value(r(0))) {
c_p->fvalue = NIL;
+ c_p->ftrace = NIL;
if (x(1) == am_throw) {
r(0) = x(2);
} else {
@@ -1024,6 +1107,7 @@ try_case(Y) {
$try_end($Y);
ASSERT(is_non_value(r(0)));
c_p->fvalue = NIL;
+ c_p->ftrace = NIL;
r(0) = x(1);
x(1) = x(2);
x(2) = x(3);
diff --git a/erts/emulator/beam/macros.tab b/erts/emulator/beam/macros.tab
index 1b5e5f66b0..802c8aec9a 100644
--- a/erts/emulator/beam/macros.tab
+++ b/erts/emulator/beam/macros.tab
@@ -104,14 +104,136 @@ GC_TEST_PRESERVE(NeedHeap, Live, PreserveTerm) {
// Make sure that there are NeedStack + NeedHeap + 1 words available
-// on the combined heap/stack segment, then allocates NeedHeap + 1
-// words on the stack and saves CP.
+// on the combined heap/stack segment, then decrement the stack
+// pointer by (NeedStack + 1) words. Finally clear the word reserved
+// for the continuation pointer at the top of the stack.
+//
+// Stack frame layout:
+//
+// +-----------+
+// y(N) | Term |
+// +-----------+
+// .
+// .
+// .
+// +-----------+
+// y(0) | Term |
+// +-----------+
+// E ==> | NIL or CP |
+// +-----------+
+//
+// When the function owning the stack frame is the currently executing
+// function, the word at the top of the stack is NIL. When calling
+// another function, the continuation pointer will be stored in the
+// word at the top of the stack. When returning to the function
+// owning the stack frame, the word at the stack top will again be set
+// to NIL.
+
AH(NeedStack, NeedHeap, Live) {
unsigned needed = $NeedStack + 1;
$GC_TEST(needed, $NeedHeap, $Live);
E -= needed;
- *E = make_cp(c_p->cp);
- c_p->cp = 0;
+ *E = NIL;
+}
+
+
+//
+// Helpers for call instructions
+//
+
+DISPATCH() {
+ BeamInstr dis_next;
+
+ dis_next = *I;
+ CHECK_ARGS(I);
+
+ if (FCALLS > 0 || FCALLS > neg_o_reds) {
+ FCALLS--;
+ Goto(dis_next);
+ } else {
+ goto context_switch;
+ }
+}
+
+DISPATCH_ABS(CallDest) {
+ SET_I((BeamInstr *) $CallDest);
+ DTRACE_LOCAL_CALL(c_p, erts_code_to_codemfa(I));
+
+ $DISPATCH();
+}
+
+DISPATCH_EXPORT(Export) {
+ BeamInstr dis_next;
+ Export *ep;
+
+ ep = (Export*)($Export);
+
+ DTRACE_GLOBAL_CALL_FROM_EXPORT(c_p, ep);
+
+ SET_I(ep->addressv[erts_active_code_ix()]);
+ CHECK_ARGS(I);
+ dis_next = *I;
+
+ if (ERTS_UNLIKELY(FCALLS <= 0)) {
+ if (ERTS_PROC_GET_SAVED_CALLS_BUF(c_p) && FCALLS > neg_o_reds) {
+ save_calls(c_p, ep);
+ } else {
+ goto context_switch;
+ }
+ }
+
+ FCALLS--;
+ Goto(dis_next);
+}
+
+DISPATCH_FUN(I) {
+ BeamInstr dis_next;
+
+ SET_I($I);
+
+ dis_next = *I;
+ CHECK_ARGS(I);
+
+ if (FCALLS > 0 || FCALLS > neg_o_reds) {
+ FCALLS--;
+ Goto(dis_next);
+ } else {
+ goto context_switch_fun;
+ }
+}
+
+DISPATCH_REL(CallDest) {
+ $SET_I_REL($CallDest);
+ DTRACE_LOCAL_CALL(c_p, erts_code_to_codemfa(I));
+
+ $DISPATCH();
+}
+
+DISPATCH_RETURN() {
+ if (FCALLS > 0 || FCALLS > neg_o_reds) {
+ FCALLS--;
+ Goto(*I);
+ } else {
+ c_p->current = NULL;
+ c_p->arity = 1;
+ goto context_switch3;
+ }
+}
+
+// Save the continuation pointer in the reserved slot at the
+// top of the stack as preparation for doing a function call.
+
+SAVE_CONTINUATION_POINTER(IP) {
+ ASSERT(VALID_INSTR(*($IP)));
+ *E = (BeamInstr) ($IP);
+}
+
+// Return to the function whose continuation pointer is stored
+// at the top of the stack and set that word to NIL.
+
+RETURN() {
+ SET_I(cp_val(*E));
+ *E = NIL;
}
NEXT0() {
@@ -167,7 +289,7 @@ BIF_ERROR_ARITY_1(Fail, BIF, Op1) {
}
reg[0] = $Op1;
SWAPOUT;
- I = handle_error(c_p, I, reg, &bif_export[$BIF]->info.mfa);
+ I = handle_error(c_p, I, reg, &bif_trap_export[$BIF].info.mfa);
goto post_error_handling;
}
@@ -179,6 +301,6 @@ BIF_ERROR_ARITY_2(Fail, BIF, Op1, Op2) {
reg[0] = $Op1;
reg[1] = $Op2;
SWAPOUT;
- I = handle_error(c_p, I, reg, &bif_export[$BIF]->info.mfa);
+ I = handle_error(c_p, I, reg, &bif_trap_export[$BIF].info.mfa);
goto post_error_handling;
}
diff --git a/erts/emulator/beam/msg_instrs.tab b/erts/emulator/beam/msg_instrs.tab
index b08466c830..3c989b6e60 100644
--- a/erts/emulator/beam/msg_instrs.tab
+++ b/erts/emulator/beam/msg_instrs.tab
@@ -210,7 +210,8 @@ remove_message() {
ASSERT(is_small(SEQ_TRACE_TOKEN_SERIAL(c_p)));
ASSERT(is_small(SEQ_TRACE_TOKEN_LASTCNT(c_p)));
ASSERT(is_small(SEQ_TRACE_TOKEN_FLAGS(c_p)));
- ASSERT(is_pid(SEQ_TRACE_TOKEN_SENDER(c_p)));
+ ASSERT(is_pid(SEQ_TRACE_TOKEN_SENDER(c_p))
+ || is_atom(SEQ_TRACE_TOKEN_SENDER(c_p)));
c_p->seq_trace_lastcnt = unsigned_val(SEQ_TRACE_TOKEN_SERIAL(c_p));
if (c_p->seq_trace_clock < unsigned_val(SEQ_TRACE_TOKEN_SERIAL(c_p))) {
c_p->seq_trace_clock = unsigned_val(SEQ_TRACE_TOKEN_SERIAL(c_p));
diff --git a/erts/emulator/beam/ops.tab b/erts/emulator/beam/ops.tab
index b9d4f6afcc..8c9361c99b 100644
--- a/erts/emulator/beam/ops.tab
+++ b/erts/emulator/beam/ops.tab
@@ -74,22 +74,29 @@ trace_jump W
return
-# To ensure that a "move Src x(0)" instruction can be combined with
-# the following call instruction, we need to make sure that there is
-# no line/1 instruction between the move and the call.
#
-# A tail-recursive call to an external function (BIF or non-BIF) will
-# never be saved on the stack, so there is no reason to keep the line
-# instruction.
+# A tail call will not refer to the current function on error unless it's a
+# BIF, so we can omit the line instruction for non-BIFs.
+#
-move S X0=x==0 | line Loc | call_ext Ar Func => \
- line Loc | move S X0 | call_ext Ar Func
-move S X0=x==0 | line Loc | call_ext_last Ar Func D => \
+move S X0=x==0 | line Loc | call_ext_last Ar Func=u$is_not_bif D => \
move S X0 | call_ext_last Ar Func D
-move S X0=x==0 | line Loc | call_ext_only Ar Func => \
+move S X0=x==0 | line Loc | call_ext_only Ar Func=u$is_not_bif => \
move S X0 | call_ext_only Ar Func
-move S X0=x==0 | line Loc | call Ar Func => \
- line Loc | move S X0 | call Ar Func
+
+move S X0=x==0 | line Loc | call_last Ar Func D => \
+ move S X0 | call_last Ar Func D
+move S X0=x==0 | line Loc | call_only Ar Func => \
+ move S X0 | call_only Ar Func
+
+# To ensure that a "move Src x(0)" instruction can be combined with
+# the following call instruction, we need to make sure that there is
+# no line/1 instruction between the move and the call. (We don't
+# need to match the call instruction, because reordering the move
+# and line instructions would be harmless even if no call instruction
+# follows.)
+
+move S X0=x==0 | line Loc => line Loc | move S X0
line Loc | func_info M F A => func_info M F A | line Loc
@@ -273,26 +280,19 @@ move_jump f c r
# Movement to and from the stack is common.
# Try to pack as much as we can into one instruction.
-# Window move
-move_window/5
-move_window/6
-
# x -> y
-move X1=x Y1=y | move X2=x Y2=y | move X3=x Y3=y | succ(Y1,Y2) | succ(Y2,Y3) => \
- move_window X1 X2 X3 Y1 Y3
-
-move X1=x Y1=y | move X2=x Y2=y | succ(Y1,Y2) => \
+move X1=x Y1=y | move X2=x Y2=y | succ(Y1, Y2) => \
move_window2 X1 X2 Y1
-move_window X1=x X2=x X3=x Y1=y Y3=y | move X4=x Y4=y | succ(Y3,Y4) => \
- move_window X1 X2 X3 X4 Y1 Y4
+move_window2 X1 X2 Y1 | move X3=x Y3=y | offset(Y1, Y3, 2) => \
+ move_window3 X1 X2 X3 Y1
-move_window X1=x X2=x X3=x X4=x Y1=y Y4=y | move X5=x Y5=y | succ(Y4,Y5) => \
- move_window5 X1 X2 X3 X4 X5 Y1
+move_window3 X1 X2 X3 Y1 | move X4=x Y4=y | offset(Y1, Y4, 3) => \
+ move_window4 X1 X2 X3 X4 Y1
-move_window X1=x X2=x X3=x Y1=y Y3=y => move_window3 X1 X2 X3 Y1
-move_window X1=x X2=x X3=x X4=x Y1=y Y4=y => move_window4 X1 X2 X3 X4 Y1
+move_window4 X1 X2 X3 X4 Y1=y | move X5=x Y5=y | offset(Y1, Y5, 4) => \
+ move_window5 X1 X2 X3 X4 X5 Y1
move_window2 x x y
move_window3 x x x y
@@ -324,76 +324,15 @@ move_src_window2 y x x
move_src_window3 y x x x
move_src_window4 y x x x x
-# Swap registers.
-move R1=xy Tmp=x | move R2=xy R1 | move Tmp R2 => swap_temp R1 R2 Tmp
-
-# The compiler uses x(1022) when swapping registers. It will definitely
-# not be used again.
-swap_temp R1 R2 Tmp=x==1022 => swap R1 R2
-
-swap_temp R1 R2 Tmp | move Src Tmp => swap R1 R2 | move Src Tmp
-
-swap_temp R1 R2 Tmp | line Loc | apply Live | is_killed_apply(Tmp, Live) => \
- swap R1 R2 | line Loc | apply Live
-swap_temp R1 R2 Tmp | line Loc | apply_last Live D | is_killed_apply(Tmp, Live) => \
- swap R1 R2 | line Loc | apply_last Live D
-
-swap_temp R1 R2 Tmp | line Loc | call_fun Live | is_killed_by_call_fun(Tmp, Live) => \
- swap R1 R2 | line Loc | call_fun Live
-swap_temp R1 R2 Tmp | make_fun2 OldIndex=u | is_killed_by_make_fun(Tmp, OldIndex) => \
- swap R1 R2 | make_fun2 OldIndex
-
-swap_temp R1 R2 Tmp | line Loc | call Live Addr | is_killed(Tmp, Live) => \
- swap R1 R2 | line Loc | call Live Addr
-swap_temp R1 R2 Tmp | call_only Live Addr | \
- is_killed(Tmp, Live) => swap R1 R2 | call_only Live Addr
-swap_temp R1 R2 Tmp | call_last Live Addr D | \
- is_killed(Tmp, Live) => swap R1 R2 | call_last Live Addr D
-
-swap_temp R1 R2 Tmp | line Loc | call_ext Live Addr | is_killed(Tmp, Live) => \
- swap R1 R2 | line Loc | call_ext Live Addr
-swap_temp R1 R2 Tmp | line Loc | call_ext_only Live Addr | \
- is_killed(Tmp, Live) => swap R1 R2 | line Loc | call_ext_only Live Addr
-swap_temp R1 R2 Tmp | line Loc | call_ext_last Live Addr D | \
- is_killed(Tmp, Live) => swap R1 R2 | line Loc | call_ext_last Live Addr D
-
-swap_temp R1 R2 Tmp | call_ext Live Addr | is_killed(Tmp, Live) => \
- swap R1 R2 | call_ext Live Addr
-swap_temp R1 R2 Tmp | call_ext_only Live Addr | is_killed(Tmp, Live) => \
- swap R1 R2 | call_ext_only Live Addr
-swap_temp R1 R2 Tmp | call_ext_last Live Addr D | is_killed(Tmp, Live) => \
- swap R1 R2 | call_ext_last Live Addr D
-
-swap_temp R1 R2 Tmp | move Src Any | line Loc | call Live Addr | \
- is_killed(Tmp, Live) | distinct(Tmp, Src) => \
- swap R1 R2 | move Src Any | line Loc | call Live Addr
-swap_temp R1 R2 Tmp | move Src Any | line Loc | call_ext Live Addr | \
- is_killed(Tmp, Live) | distinct(Tmp, Src) => \
- swap R1 R2 | move Src Any | line Loc | call_ext Live Addr
-swap_temp R1 R2 Tmp | move Src Any | call_only Live Addr | \
- is_killed(Tmp, Live) | distinct(Tmp, Src) => \
- swap R1 R2 | move Src Any | call_only Live Addr
-swap_temp R1 R2 Tmp | move Src Any | line Loc | call_ext_only Live Addr | \
- is_killed(Tmp, Live) | distinct(Tmp, Src) => \
- swap R1 R2 | move Src Any | line Loc | call_ext_only Live Addr
-swap_temp R1 R2 Tmp | move Src Any | line Loc | call_fun Live | \
- is_killed(Tmp, Live) | distinct(Tmp, Src) => \
- swap R1 R2 | move Src Any | line Loc | call_fun Live
-
-swap_temp R1 R2 Tmp | line Loc | send | is_killed_by_send(Tmp) => \
- swap R1 R2 | line Loc | send
-
-# swap_temp/3 with Y register operands are rare.
-swap_temp R1 R2=y Tmp => swap R1 R2 | move R2 Tmp
-swap_temp R1=y R2 Tmp => swap R1 R2 | move R2 Tmp
-
swap R1=x R2=y => swap R2 R1
-swap_temp x x x
-
swap xy x
swap y y
+swap R1=x R2=x | swap R3=x R1 => swap2 R1 R2 R3
+
+swap2 x x x
+
# move_shift
move SD=x D=x | move Src=cxy SD=x | distinct(D, Src) => move_shift Src SD D
@@ -450,16 +389,6 @@ move2_par X1=x X2=x X3=x X4=x | move X5=x X6=x => move3 X1 X2 X3 X4 X5 X6
move3 y x y x y x
move3 x x x x x x
-# move_x1, move_x2
-
-move C=aiq X=x==1 => move_x1 C
-move C=aiq X=x==2 => move_x2 C
-
-move n D=y => init D
-
-move_x1 c
-move_x2 c
-
move xy xy
move c xy
move n x
@@ -542,16 +471,27 @@ is_eq_exact f? y y
is_ne_exact f? S S
+# When either operand for is_lt or is_ge is a literal,
+# that literal is almost always an integer and almost never
+# an atom. Therefore we use a specialized instruction when
+# one of the operands is a literal.
+
+is_lt Fail Src=x Lit=c => is_lt_literal Fail Src Lit
+is_lt Fail Lit=c Src=x => is_lt_literal Fail Lit Src
+
is_lt f? x x
-is_lt f? x c
-is_lt f? c x
+is_lt_literal f? x c
+is_lt_literal f? c x
%cold
is_lt f? s s
%hot
+is_ge Fail Src=x Lit=c => is_ge_literal Fail Src Lit
+is_ge Fail Lit=c Src=x => is_ge_literal Fail Lit Src
+
is_ge f? x x
-is_ge f? x c
-is_ge f? c x
+is_ge_literal f? x c
+is_ge_literal f? c x
%cold
is_ge f? s s
%hot
@@ -635,8 +575,9 @@ put_list s s d
%cold
normal_exit
continue_exit
-apply_bif
-call_nif
+call_bif W
+call_nif W W W
+call_nif_early
call_error_handler
error_action_code
return_trace
@@ -657,8 +598,20 @@ move S x==0 | deallocate D | return => move_deallocate_return S D
move_deallocate_return xycn Q
+deallocate u==0 | return => deallocate_return0
+deallocate u==1 | return => deallocate_return1
+deallocate u==2 | return => deallocate_return2
+deallocate u==3 | return => deallocate_return3
+deallocate u==4 | return => deallocate_return4
+
deallocate D | return => deallocate_return D
+deallocate_return0
+deallocate_return1
+deallocate_return2
+deallocate_return3
+deallocate_return4
+
deallocate_return Q
test_heap Need u==1 | put_list Y=y x==0 x==0 => test_heap_1_put_list Need Y
@@ -836,62 +789,22 @@ allocate_init t t? y
# External function and bif calls.
#################################################################
-#
-# The BIFs erts_internal:check_process_code/1 must be called like a function,
-# to ensure that c_p->i (program counter) is set correctly (an ordinary
-# BIF call doesn't set it).
-#
-
-call_ext u==1 Bif=u$bif:erts_internal:check_process_code/1 => i_call_ext Bif
-call_ext_last u==1 Bif=u$bif:erts_internal:check_process_code/1 D => i_call_ext_last Bif D
-call_ext_only u==1 Bif=u$bif:erts_internal:check_process_code/1 => i_call_ext_only Bif
+# Expands into call_light_bif(_only)/2
+call_light_bif/1
+call_light_bif_only/1
+call_light_bif_last/2
#
-# The BIFs erts_internal:garbage_collect/1 must be called like a function,
-# to allow them to invoke the garbage collector. (The stack pointer must
-# be saved and p->arity must be zeroed, which is not done on ordinary BIF calls.)
+# The load_nif/2 BIF is an instruction.
#
-call_ext u==1 Bif=u$bif:erts_internal:garbage_collect/1 => i_call_ext Bif
-call_ext_last u==1 Bif=u$bif:erts_internal:garbage_collect/1 D => i_call_ext_last Bif D
-call_ext_only u==1 Bif=u$bif:erts_internal:garbage_collect/1 => i_call_ext_only Bif
-#
-# put/2 and erase/1 must be able to do garbage collection, so we must call
-# them like functions.
-#
-
-call_ext u==2 Bif=u$bif:erlang:put/2 => i_call_ext Bif
-call_ext_last u==2 Bif=u$bif:erlang:put/2 D => i_call_ext_last Bif D
-call_ext_only u==2 Bif=u$bif:erlang:put/2 => i_call_ext_only Bif
-
-call_ext u==1 Bif=u$bif:erlang:erase/1 => i_call_ext Bif
-call_ext_last u==1 Bif=u$bif:erlang:erase/1 D => i_call_ext_last Bif D
-call_ext_only u==1 Bif=u$bif:erlang:erase/1 => i_call_ext_only Bif
-
-#
-# The process_info/1,2 BIF should be called like a function, to force
-# the emulator to set c_p->current before calling it (a BIF call doesn't
-# set it).
-#
-# In addition, we force the use of a non-tail-recursive call. This will ensure
-# that c_p->cp points into the function making the call.
-#
-
-call_ext u==1 Bif=u$bif:erlang:process_info/1 => i_call_ext Bif
-call_ext_last u==1 Bif=u$bif:erlang:process_info/1 D => i_call_ext Bif | deallocate_return D
-call_ext_only Ar=u==1 Bif=u$bif:erlang:process_info/1 => allocate u Ar | i_call_ext Bif | deallocate_return u
-
-call_ext u==2 Bif=u$bif:erlang:process_info/2 => i_call_ext Bif
-call_ext_last u==2 Bif=u$bif:erlang:process_info/2 D => i_call_ext Bif | deallocate_return D
-call_ext_only Ar=u==2 Bif=u$bif:erlang:process_info/2 => allocate u Ar | i_call_ext Bif | deallocate_return u
-
-#
-# load_nif/2 also needs to know calling function like process_info
-#
-call_ext u==2 Bif=u$bif:erlang:load_nif/2 => i_call_ext Bif
-call_ext_last u==2 Bif=u$bif:erlang:load_nif/2 D => i_call_ext Bif | deallocate_return D
-call_ext_only Ar=u==2 Bif=u$bif:erlang:load_nif/2 => allocate u Ar | i_call_ext Bif | deallocate_return u
+call_ext u==2 u$func:erlang:load_nif/2 => i_load_nif
+call_ext_last u==2 u$func:erlang:load_nif/2 D => i_load_nif | deallocate_return D
+call_ext_only u==2 u$func:erlang:load_nif/2 => i_load_nif | return
+%cold
+i_load_nif
+%hot
#
# apply/2 is an instruction, not a BIF.
@@ -910,33 +823,6 @@ call_ext_last u==3 u$bif:erlang:apply/3 D => i_apply_last D
call_ext_only u==3 u$bif:erlang:apply/3 => i_apply_only
#
-# The exit/1 and throw/1 BIFs never execute the instruction following them;
-# thus there is no need to generate any return instruction.
-#
-
-call_ext_last u==1 Bif=u$bif:erlang:exit/1 D => call_bif Bif
-call_ext_last u==1 Bif=u$bif:erlang:throw/1 D => call_bif Bif
-
-call_ext_only u==1 Bif=u$bif:erlang:exit/1 => call_bif Bif
-call_ext_only u==1 Bif=u$bif:erlang:throw/1 => call_bif Bif
-
-#
-# The error/1 and error/2 BIFs never execute the instruction following them;
-# thus there is no need to generate any return instruction.
-# However, they generate stack backtraces, so if the call instruction
-# is call_ext_only/2 instruction, we explicitly do an allocate/2 to store
-# the continuation pointer on the stack.
-#
-
-call_ext_last u==1 Bif=u$bif:erlang:error/1 D => call_bif Bif
-call_ext_last u==2 Bif=u$bif:erlang:error/2 D => call_bif Bif
-
-call_ext_only Ar=u==1 Bif=u$bif:erlang:error/1 => \
- allocate u Ar | call_bif Bif
-call_ext_only Ar=u==2 Bif=u$bif:erlang:error/2 => \
- allocate u Ar | call_bif Bif
-
-#
# The yield/0 BIF is an instruction
#
@@ -1048,17 +934,24 @@ call_ext_only u==0 u$func:os:perf_counter/0 => \
i_perf_counter | return
#
-# The general case for BIFs that have no special instructions.
-# A BIF used in the tail must be followed by a return instruction.
+# BIFs like process_info/1,2 require up-to-date information about the current
+# emulator state, which the ordinary call_light_bif instruction doesn't save.
#
-# To make trapping and stack backtraces work correctly, we make sure that
-# the continuation pointer is always stored on the stack.
-call_ext u Bif=u$is_bif => call_bif Bif
+call_ext u Bif=u$is_bif | is_heavy_bif(Bif) => \
+ i_call_ext Bif
+call_ext_last u Bif=u$is_bif D | is_heavy_bif(Bif) => \
+ i_call_ext Bif | deallocate_return D
+call_ext_only Ar=u Bif=u$is_bif | is_heavy_bif(Bif) => \
+ allocate u Ar | i_call_ext Bif | deallocate_return u
-call_ext_last u Bif=u$is_bif D => deallocate D | call_bif_only Bif
+#
+# The general case for BIFs that have no special requirements.
+#
-call_ext_only Ar=u Bif=u$is_bif => call_bif_only Bif
+call_ext u Bif=u$is_bif => call_light_bif Bif
+call_ext_last u Bif=u$is_bif D => call_light_bif_last Bif D
+call_ext_only Ar=u Bif=u$is_bif => call_light_bif_only Bif
#
# Any remaining calls are calls to Erlang functions, not BIFs.
@@ -1083,14 +976,32 @@ i_apply_fun
i_apply_fun_last Q
i_apply_fun_only
+#
+# When a BIF is traced, these instructions make a body call through the export
+# entry instead of calling the BIF directly (setting up a temporary stack frame
+# if needed). We therefore retain the stack frame in call_light_bif_last, and
+# add a deallocate_return after call_light_bif_only to remove the temporary
+# stack frame before returning.
+#
+
+call_light_bif Bif=u$is_bif => \
+ call_light_bif Bif Bif
+
+call_light_bif_last Bif=u$is_bif D => \
+ call_light_bif Bif Bif | deallocate_return D
+
+call_light_bif_only Bif=u$is_bif => \
+ call_light_bif_only Bif Bif | deallocate_return u
+
+call_light_bif b e
+call_light_bif_only b e
+
%cold
-i_hibernate
+i_hibernate
i_perf_counter
-%hot
-call_bif e
-call_bif_only e
+%hot
#
# Calls to non-building and guard BIFs.
@@ -1252,7 +1163,7 @@ i_bs_get_integer_imm Ms Bits Live Fail Flags Y=y => \
i_bs_get_integer_small_imm xy W f? t x
i_bs_get_integer_imm xy W t f? t x
-i_bs_get_integer xy f? t t s d
+i_bs_get_integer xy f? t t S d
i_bs_get_integer_8 xy f? d
i_bs_get_integer_16 xy f? d
@@ -1265,7 +1176,7 @@ bs_get_binary2 Fail=f Ms=xy Live=u Sz=sq Unit=u Flags=u Dst=d => \
gen_get_binary2(Fail, Ms, Live, Sz, Unit, Flags, Dst)
i_bs_get_binary_imm2 xy f? t W t d
-i_bs_get_binary2 xy f t? s t d
+i_bs_get_binary2 xy f t? S t d
i_bs_get_binary_all2 xy f? t t d
# Fetching float from binaries.
@@ -1302,16 +1213,32 @@ bs_get_tail xy d t
# "slots" in the context itself, which lets us continue matching even after
# we've passed it off to another function.
+bs_start_match4 a==am_no_fail Live=u Src=xy Ctx=d => \
+ bs_start_match3 p Src Live Ctx
+bs_start_match4 Fail=f Live=u Src=xy Ctx=d => \
+ bs_start_match3 Fail Src Live Ctx
+
%if ARCH_64
+
+# This instruction nops on 64-bit platforms
+bs_start_match4 a==am_resume Live Same Same =>
+bs_start_match4 a==am_resume Live Ctx Dst => move Ctx Dst
+
bs_start_match3 Fail Bin Live Ctx | bs_get_position Ctx Pos=x Ignored => \
i_bs_start_match3_gp Bin Live Fail Ctx Pos
-i_bs_start_match3_gp xy t f d x
+i_bs_start_match3_gp xy t j d x
+
+%else
+
+bs_start_match4 a==am_resume Live Ctx Dst => \
+ bs_start_match4 a=am_no_fail Live Ctx Dst
+
%endif
-bs_start_match3 Fail=f ica Live Dst => jump Fail
+bs_start_match3 Fail=j ica Live Dst => jump Fail
bs_start_match3 Fail Bin Live Dst => i_bs_start_match3 Bin Live Fail Dst
-i_bs_start_match3 xy t f d
+i_bs_start_match3 xy t j d
# Match context position instructions. 64-bit assumes that all positions can
# fit into an unsigned small.
@@ -1793,3 +1720,21 @@ i_recv_set
build_stacktrace
raw_raise
+
+#
+# Specialized move instructions. Since they don't require a second
+# instruction, we have intentionally placed them after any other
+# transformation rules that starts with a move instruction in order to
+# produce better code for the transformation engine.
+#
+
+# move_x1, move_x2
+
+move C=aiq X=x==1 => move_x1 C
+move C=aiq X=x==2 => move_x2 C
+
+move n D=y => init D
+
+move_x1 c
+move_x2 c
+
diff --git a/erts/emulator/beam/packet_parser.c b/erts/emulator/beam/packet_parser.c
index 4b526887b5..8f24725326 100644
--- a/erts/emulator/beam/packet_parser.c
+++ b/erts/emulator/beam/packet_parser.c
@@ -816,9 +816,6 @@ int packet_parse_http(const char* buf, int len, int* statep,
ptr++;
if (--n == 0) return -1;
}
- while (n && SP(ptr)) { /* Skip white space before ':' */
- ptr++; n--;
- }
if (*ptr != ':') {
return -1;
}
@@ -837,7 +834,9 @@ int packet_parse_http(const char* buf, int len, int* statep,
while (n && SP(ptr)) {
ptr++; n--;
}
- return pcb->http_header(arg, name, name_ptr, name_len,
+ return pcb->http_header(arg, name,
+ name_ptr, name_len,
+ buf, name_len,
ptr, n);
}
return -1;
diff --git a/erts/emulator/beam/packet_parser.h b/erts/emulator/beam/packet_parser.h
index 358d650804..b05623efec 100644
--- a/erts/emulator/beam/packet_parser.h
+++ b/erts/emulator/beam/packet_parser.h
@@ -77,8 +77,10 @@ typedef int HttpResponseMessageFn(void* arg, int major, int minor, int status,
typedef int HttpRequestMessageFn(void* arg, const http_atom_t* meth, const char* meth_ptr,
int meth_len, const PacketHttpURI*, int major, int minor);
typedef int HttpEohMessageFn(void *arg);
-typedef int HttpHeaderMessageFn(void* arg, const http_atom_t* name, const char* name_ptr,
- int name_len, const char* value_ptr, int value_len);
+typedef int HttpHeaderMessageFn(void* arg, const http_atom_t* name,
+ const char* name_ptr, int name_len,
+ const char* oname_ptr, int oname_len,
+ const char* value_ptr, int value_len);
typedef int HttpErrorMessageFn(void* arg, const char* buf, int len);
typedef int SslTlsFn(void* arg, unsigned type, unsigned major, unsigned minor,
const char* data, int len, const char* prefix, int plen);
diff --git a/erts/emulator/beam/register.c b/erts/emulator/beam/register.c
index c7e02c6d48..8e44b527a2 100644
--- a/erts/emulator/beam/register.c
+++ b/erts/emulator/beam/register.c
@@ -265,10 +265,8 @@ Eterm
erts_whereis_name_to_id(Process *c_p, Eterm name)
{
Eterm res = am_undefined;
- HashValue hval;
- int ix;
- HashBucket* b;
ErtsProcLocks c_p_locks = 0;
+ RegProc *rp, tmpl;
if (c_p) {
c_p_locks = ERTS_PROC_LOCK_MAIN;
ERTS_CHK_HAVE_ONLY_MAIN_PROC_LOCK(c_p);
@@ -278,29 +276,14 @@ erts_whereis_name_to_id(Process *c_p, Eterm name)
if (c_p && !c_p_locks)
erts_proc_lock(c_p, ERTS_PROC_LOCK_MAIN);
- hval = REG_HASH(name);
- ix = hval % process_reg.size;
- b = process_reg.bucket[ix];
+ tmpl.name = name;
+ rp = hash_fetch(&process_reg, &tmpl, (H_FUN)reg_hash, (HCMP_FUN)reg_cmp);
- /*
- * Note: We have inlined the code from hash.c for speed.
- */
-
- while (b) {
- RegProc* rp = (RegProc *) b;
- if (rp->name == name) {
- /*
- * SMP NOTE: No need to lock registered entity since it cannot
- * be removed without acquiring write reg lock and id on entity
- * is read only.
- */
- if (rp->p)
- res = rp->p->common.id;
- else if (rp->pt)
- res = rp->pt->common.id;
- break;
- }
- b = b->next;
+ if (rp) {
+ if (rp->p)
+ res = rp->p->common.id;
+ else if (rp->pt)
+ res = rp->pt->common.id;
}
reg_read_unlock();
@@ -321,10 +304,7 @@ erts_whereis_name(Process *c_p,
Port** port,
int lock_port)
{
- RegProc* rp = NULL;
- HashValue hval;
- int ix;
- HashBucket* b;
+ RegProc* rp = NULL, tmpl;
ErtsProcLocks current_c_p_locks;
Port *pending_port = NULL;
@@ -342,21 +322,8 @@ erts_whereis_name(Process *c_p,
* - current_c_p_locks (either c_p_locks or 0) on c_p
*/
- hval = REG_HASH(name);
- ix = hval % process_reg.size;
- b = process_reg.bucket[ix];
-
- /*
- * Note: We have inlined the code from hash.c for speed.
- */
-
- while (b) {
- if (((RegProc *) b)->name == name) {
- rp = (RegProc *) b;
- break;
- }
- b = b->next;
- }
+ tmpl.name = name;
+ rp = hash_fetch(&process_reg, &tmpl, (H_FUN)reg_hash, (HCMP_FUN)reg_cmp);
if (proc) {
if (!rp)
@@ -564,18 +531,6 @@ int erts_unregister_name(Process *c_p,
return res;
}
-int process_reg_size(void)
-{
- int size;
- int lock = !ERTS_IS_CRASH_DUMPING;
- if (lock)
- reg_read_lock();
- size = process_reg.size;
- if (lock)
- reg_read_unlock();
- return size;
-}
-
int process_reg_sz(void)
{
int sz;
@@ -592,15 +547,24 @@ int process_reg_sz(void)
#include "bif.h"
+struct registered_foreach_arg {
+ Eterm res;
+ Eterm *hp;
+};
+
+static void
+registered_foreach(RegProc *reg, struct registered_foreach_arg *arg)
+{
+ arg->res = CONS(arg->hp, reg->name, arg->res);
+ arg->hp += 2;
+}
+
/* return a list of the registered processes */
BIF_RETTYPE registered_0(BIF_ALIST_0)
{
- int i;
- Eterm res;
+ struct registered_foreach_arg arg;
Uint need;
- Eterm* hp;
- HashBucket **bucket;
ErtsProcLocks proc_locks = ERTS_PROC_LOCK_MAIN;
ERTS_CHK_HAVE_ONLY_MAIN_PROC_LOCK(BIF_P);
@@ -608,41 +572,21 @@ BIF_RETTYPE registered_0(BIF_ALIST_0)
if (!proc_locks)
erts_proc_lock(BIF_P, ERTS_PROC_LOCK_MAIN);
- bucket = process_reg.bucket;
-
- /* work out how much heap we need & maybe garb, by scanning through
- the registered process table */
- need = 0;
- for (i = 0; i < process_reg.size; i++) {
- HashBucket *b = bucket[i];
- while (b != NULL) {
- need += 2;
- b = b->next;
- }
- }
+ /* work out how much heap we need */
+ need = process_reg.nobjs * 2;
if (need == 0) {
reg_read_unlock();
BIF_RET(NIL);
}
- hp = HAlloc(BIF_P, need);
-
- /* scan through again and make the list */
- res = NIL;
+ /* scan through again and make the list */
+ arg.hp = HAlloc(BIF_P, need);
+ arg.res = NIL;
- for (i = 0; i < process_reg.size; i++) {
- HashBucket *b = bucket[i];
- while (b != NULL) {
- RegProc *reg = (RegProc *) b;
-
- res = CONS(hp, reg->name, res);
- hp += 2;
- b = b->next;
- }
- }
+ hash_foreach(&process_reg, (HFOREACH_FUN)registered_foreach, &arg);
reg_read_unlock();
- BIF_RET(res);
+ BIF_RET(arg.res);
}
diff --git a/erts/emulator/beam/register.h b/erts/emulator/beam/register.h
index 27a314ca78..c77bd03653 100644
--- a/erts/emulator/beam/register.h
+++ b/erts/emulator/beam/register.h
@@ -41,7 +41,6 @@ typedef struct reg_proc
Eterm name; /* Atom name */
} RegProc;
-int process_reg_size(void);
int process_reg_sz(void);
void init_register_table(void);
void register_info(fmtfn_t, void *);
diff --git a/erts/emulator/beam/sys.h b/erts/emulator/beam/sys.h
index 0d85211be8..3316d9dfde 100644
--- a/erts/emulator/beam/sys.h
+++ b/erts/emulator/beam/sys.h
@@ -92,6 +92,12 @@
# define ERTS_GLB_INLINE_INCL_FUNC_DEF 0
#endif
+#ifdef __GNUC__
+# define ERTS_NOINLINE __attribute__((__noinline__))
+#else
+# define ERTS_NOINLINE
+#endif
+
#if defined(VALGRIND) && !defined(NO_FPE_SIGNALS)
# define NO_FPE_SIGNALS
#endif
@@ -172,7 +178,8 @@ typedef ERTS_SYS_FD_TYPE ErtsSysFdType;
# define ERTS_UNLIKELY(BOOL) (BOOL)
#endif
-#if ERTS_AT_LEAST_GCC_VSN__(2, 96, 0)
+/* AIX doesn't like this and claims section conflicts */
+#if ERTS_AT_LEAST_GCC_VSN__(2, 96, 0) && !defined(_AIX)
#if (defined(__APPLE__) && defined(__MACH__)) || defined(__DARWIN__)
# define ERTS_WRITE_UNLIKELY(X) X __attribute__ ((section ("__DATA,ERTS_LOW_WRITE") ))
#else
@@ -211,7 +218,7 @@ typedef ERTS_SYS_FD_TYPE ErtsSysFdType;
#endif
/* In VC++, noreturn is a declspec that has to be before the types,
- * but in GNUC it is an att ribute to be placed between return type
+ * but in GNUC it is an attribute to be placed between return type
* and function name, hence __decl_noreturn <types> __noreturn <function name>
*
* at some platforms (e.g. Android) __noreturn is defined at sys/cdef.h
@@ -254,6 +261,39 @@ __decl_noreturn void __noreturn erl_assert_error(const char* expr, const char *f
#endif
/*
+ * ERTS_GCC_DIAG_ON and ERTS_GCC_DIAG_OFF can be used to temporarly
+ * disable a gcc or clang warning in a file.
+ *
+ * Example:
+ * GCC_DIAG_OFF(unused-function)
+ * static int test(){ return 0;}
+ * GCC_DIAG_ON(unused-function)
+ *
+ * These macros were orginally authored by Jonathan Wakely and has
+ * been modified by Patrick Horgan.
+ *
+ * Source: http://dbp-consulting.com/tutorials/SuppressingGCCWarnings.html
+ *
+ */
+#if ((__GNUC__ * 100) + __GNUC_MINOR__) >= 402
+#define ERTS_GCC_DIAG_STR(s) #s
+#define ERTS_GCC_DIAG_JOINSTR(x,y) ERTS_GCC_DIAG_STR(x ## y)
+# define ERTS_GCC_DIAG_DO_PRAGMA(x) _Pragma (#x)
+# define ERTS_GCC_DIAG_PRAGMA(x) ERTS_GCC_DIAG_DO_PRAGMA(GCC diagnostic x)
+# if ((__GNUC__ * 100) + __GNUC_MINOR__) >= 406
+# define ERTS_GCC_DIAG_OFF(x) ERTS_GCC_DIAG_PRAGMA(push) \
+ ERTS_GCC_DIAG_PRAGMA(ignored ERTS_GCC_DIAG_JOINSTR(-W,x))
+# define ERTS_GCC_DIAG_ON(x) ERTS_GCC_DIAG_PRAGMA(pop)
+# else
+# define ERTS_GCC_DIAG_OFF(x) ERTS_GCC_DIAG_PRAGMA(ignored ERTS_GCC_DIAG_JOINSTR(-W,x))
+# define ERTS_GCC_DIAG_ON(x) ERTS_GCC_DIAG_PRAGMA(warning ERTS_GCC_DIAG_JOINSTR(-W,x))
+# endif
+#else
+# define ERTS_GCC_DIAG_OFF(x)
+# define ERTS_GCC_DIAG_ON(x)
+#endif
+
+/*
* Compile time assert
* (the actual compiler error msg can be a bit confusing)
*/
@@ -666,7 +706,16 @@ typedef struct preload {
*/
typedef Eterm ErtsTracer;
-#include "erl_osenv.h"
+
+/*
+ * This structure contains the rb tree for the erlang osenv copy
+ * see erl_osenv.h for more details.
+ */
+typedef struct __erts_osenv_t {
+ struct __env_rbtnode_t *tree;
+ int variable_count;
+ int content_size;
+} erts_osenv_t;
extern char *erts_default_arg0;
diff --git a/erts/emulator/beam/trace_instrs.tab b/erts/emulator/beam/trace_instrs.tab
index 6bcd12e22e..a6cdbcbc20 100644
--- a/erts/emulator/beam/trace_instrs.tab
+++ b/erts/emulator/beam/trace_instrs.tab
@@ -20,16 +20,15 @@
//
return_trace() {
- ErtsCodeMFA* mfa = (ErtsCodeMFA *)(E[0]);
+ ErtsCodeMFA* mfa = (ErtsCodeMFA *)(E[1]);
SWAPOUT; /* Needed for shared heap */
ERTS_UNREQ_PROC_MAIN_LOCK(c_p);
- erts_trace_return(c_p, mfa, r(0), ERTS_TRACER_FROM_ETERM(E+1)/* tracer */);
+ erts_trace_return(c_p, mfa, r(0), ERTS_TRACER_FROM_ETERM(E+2)/* tracer */);
ERTS_REQ_PROC_MAIN_LOCK(c_p);
SWAPIN;
- c_p->cp = NULL;
- SET_I((BeamInstr *) cp_val(E[2]));
E += 3;
+ $RETURN();
Goto(*I);
//| -no_next
}
@@ -45,14 +44,12 @@ i_generic_breakpoint() {
}
i_return_time_trace() {
- ErtsCodeInfo *cinfo = (!is_CP(E[0]) ? NULL
- : erts_code_to_codeinfo((BeamInstr*)E[0]));
+ ErtsCodeInfo *cinfo = (!is_CP(E[1]) ? NULL : erts_code_to_codeinfo((BeamInstr*)E[1]));
SWAPOUT;
erts_trace_time_return(c_p, cinfo);
SWAPIN;
- c_p->cp = NULL;
- SET_I((BeamInstr *) cp_val(E[1]));
E += 2;
+ $RETURN();
Goto(*I);
//| -no_next
}
@@ -60,8 +57,10 @@ i_return_time_trace() {
i_return_to_trace() {
if (IS_TRACED_FL(c_p, F_TRACE_RETURN_TO)) {
Uint *cpp = (Uint*) E;
+ while (is_not_CP(*cpp)) {
+ cpp++;
+ }
for(;;) {
- ASSERT(is_CP(*cpp));
if (IsOpCode(*cp_val(*cpp), return_trace)) {
do
++cpp;
@@ -81,9 +80,8 @@ i_return_to_trace() {
ERTS_REQ_PROC_MAIN_LOCK(c_p);
SWAPIN;
}
- c_p->cp = NULL;
- SET_I((BeamInstr *) cp_val(E[0]));
E += 1;
+ $RETURN();
Goto(*I);
//| -no_next
}
@@ -109,7 +107,7 @@ i_hibernate() {
goto do_schedule;
} else {
HEAVY_SWAPIN;
- I = handle_error(c_p, I, reg, &bif_export[BIF_hibernate_3]->info.mfa);
+ I = handle_error(c_p, I, reg, &bif_trap_export[BIF_hibernate_3].info.mfa);
goto post_error_handling;
}
//| -no_next
diff --git a/erts/emulator/beam/utils.c b/erts/emulator/beam/utils.c
index 9fb37f77fc..f06a4a3d60 100644
--- a/erts/emulator/beam/utils.c
+++ b/erts/emulator/beam/utils.c
@@ -66,7 +66,7 @@
#undef M_MMAP_THRESHOLD
#undef M_MMAP_MAX
-#if defined(__GLIBC__) && defined(HAVE_MALLOC_H)
+#if (defined(__GLIBC__) || defined(_AIX)) && defined(HAVE_MALLOC_H)
#include <malloc.h>
#endif
@@ -907,7 +907,7 @@ tail_recur:
hash = hash * FUNNY_NUMBER10 + num_free;
hash = hash*FUNNY_NUMBER1 +
(atom_tab(atom_val(funp->fe->module))->slot.bucket.hvalue);
- hash = hash*FUNNY_NUMBER2 + funp->fe->old_index;
+ hash = hash*FUNNY_NUMBER2 + funp->fe->index;
hash = hash*FUNNY_NUMBER2 + funp->fe->old_uniq;
if (num_free > 0) {
if (num_free > 1) {
@@ -1069,54 +1069,237 @@ do { \
#define HCONST 0x9e3779b9UL /* the golden ratio; an arbitrary value */
-static Uint32
-block_hash(byte *k, Uint length, Uint32 initval)
+typedef struct {
+ Uint32 a,b,c;
+} ErtsBlockHashHelperCtx;
+
+#define BLOCK_HASH_BYTES_PER_ITER 12
+
+/* The three functions below are separated into different functions even
+ though they are always used together to make trapping and handling
+ of unaligned binaries easier. Examples of how they are used can be
+ found in block_hash and make_hash2_helper.*/
+static ERTS_INLINE
+void block_hash_setup(Uint32 initval,
+ ErtsBlockHashHelperCtx* ctx /* out parameter */)
+{
+ ctx->a = ctx->b = HCONST;
+ ctx->c = initval; /* the previous hash value */
+}
+
+static ERTS_INLINE
+void block_hash_buffer(byte *buf,
+ Uint buf_length,
+ ErtsBlockHashHelperCtx* ctx /* out parameter */)
{
- Uint32 a,b,c;
- Uint len;
-
- /* Set up the internal state */
- len = length;
- a = b = HCONST;
- c = initval; /* the previous hash value */
-
- while (len >= 12)
- {
- a += (k[0] +((Uint32)k[1]<<8) +((Uint32)k[2]<<16) +((Uint32)k[3]<<24));
- b += (k[4] +((Uint32)k[5]<<8) +((Uint32)k[6]<<16) +((Uint32)k[7]<<24));
- c += (k[8] +((Uint32)k[9]<<8) +((Uint32)k[10]<<16)+((Uint32)k[11]<<24));
- MIX(a,b,c);
- k += 12; len -= 12;
- }
-
- c += length;
- switch(len) /* all the case statements fall through */
- {
- case 11: c+=((Uint32)k[10]<<24);
- case 10: c+=((Uint32)k[9]<<16);
- case 9 : c+=((Uint32)k[8]<<8);
- /* the first byte of c is reserved for the length */
- case 8 : b+=((Uint32)k[7]<<24);
- case 7 : b+=((Uint32)k[6]<<16);
- case 6 : b+=((Uint32)k[5]<<8);
- case 5 : b+=k[4];
- case 4 : a+=((Uint32)k[3]<<24);
- case 3 : a+=((Uint32)k[2]<<16);
- case 2 : a+=((Uint32)k[1]<<8);
- case 1 : a+=k[0];
- /* case 0: nothing left to add */
- }
- MIX(a,b,c);
- return c;
+ Uint len = buf_length;
+ byte *k = buf;
+ ASSERT(buf_length % BLOCK_HASH_BYTES_PER_ITER == 0);
+ while (len >= BLOCK_HASH_BYTES_PER_ITER) {
+ ctx->a += (k[0] +((Uint32)k[1]<<8) +((Uint32)k[2]<<16) +((Uint32)k[3]<<24));
+ ctx->b += (k[4] +((Uint32)k[5]<<8) +((Uint32)k[6]<<16) +((Uint32)k[7]<<24));
+ ctx->c += (k[8] +((Uint32)k[9]<<8) +((Uint32)k[10]<<16)+((Uint32)k[11]<<24));
+ MIX(ctx->a,ctx->b,ctx->c);
+ k += BLOCK_HASH_BYTES_PER_ITER; len -= BLOCK_HASH_BYTES_PER_ITER;
+ }
}
+static ERTS_INLINE
+Uint32 block_hash_final_bytes(byte *buf,
+ Uint buf_length,
+ Uint full_length,
+ ErtsBlockHashHelperCtx* ctx)
+{
+ Uint len = buf_length;
+ byte *k = buf;
+ ctx->c += full_length;
+ switch(len)
+ { /* all the case statements fall through */
+ case 11: ctx->c+=((Uint32)k[10]<<24);
+ case 10: ctx->c+=((Uint32)k[9]<<16);
+ case 9 : ctx->c+=((Uint32)k[8]<<8);
+ /* the first byte of c is reserved for the length */
+ case 8 : ctx->b+=((Uint32)k[7]<<24);
+ case 7 : ctx->b+=((Uint32)k[6]<<16);
+ case 6 : ctx->b+=((Uint32)k[5]<<8);
+ case 5 : ctx->b+=k[4];
+ case 4 : ctx->a+=((Uint32)k[3]<<24);
+ case 3 : ctx->a+=((Uint32)k[2]<<16);
+ case 2 : ctx->a+=((Uint32)k[1]<<8);
+ case 1 : ctx->a+=k[0];
+ /* case 0: nothing left to add */
+ }
+ MIX(ctx->a,ctx->b,ctx->c);
+ return ctx->c;
+}
+
+static
Uint32
-make_hash2(Eterm term)
+block_hash(byte *block, Uint block_length, Uint32 initval)
{
+ ErtsBlockHashHelperCtx ctx;
+ Uint no_bytes_not_in_loop =
+ (block_length % BLOCK_HASH_BYTES_PER_ITER);
+ Uint no_bytes_to_process_in_loop =
+ block_length - no_bytes_not_in_loop;
+ byte *final_bytes = block + no_bytes_to_process_in_loop;
+ block_hash_setup(initval, &ctx);
+ block_hash_buffer(block,
+ no_bytes_to_process_in_loop,
+ &ctx);
+ return block_hash_final_bytes(final_bytes,
+ no_bytes_not_in_loop,
+ block_length,
+ &ctx);
+}
+
+typedef enum {
+ tag_primary_list,
+ arityval_subtag,
+ hamt_subtag_head_flatmap,
+ map_subtag,
+ fun_subtag,
+ neg_big_subtag,
+ sub_binary_subtag_1,
+ sub_binary_subtag_2,
+ hash2_common_1,
+ hash2_common_2,
+ hash2_common_3,
+} ErtsMakeHash2TrapLocation;
+
+typedef struct {
+ int c;
+ Uint32 sh;
+ Eterm* ptr;
+} ErtsMakeHash2Context_TAG_PRIMARY_LIST;
+
+typedef struct {
+ int i;
+ int arity;
+ Eterm* elem;
+} ErtsMakeHash2Context_ARITYVAL_SUBTAG;
+
+typedef struct {
+ Eterm *ks;
+ Eterm *vs;
+ int i;
+ Uint size;
+} ErtsMakeHash2Context_HAMT_SUBTAG_HEAD_FLATMAP;
+
+typedef struct {
+ Eterm* ptr;
+ int i;
+} ErtsMakeHash2Context_MAP_SUBTAG;
+
+typedef struct {
+ Uint num_free;
+ Eterm* bptr;
+} ErtsMakeHash2Context_FUN_SUBTAG;
+
+typedef struct {
+ Eterm* ptr;
+ Uint i;
+ Uint n;
+ Uint32 con;
+} ErtsMakeHash2Context_NEG_BIG_SUBTAG;
+
+typedef struct {
+ byte* bptr;
+ Uint sz;
+ Uint bitsize;
+ Uint bitoffs;
+ Uint no_bytes_processed;
+ ErtsBlockHashHelperCtx block_hash_ctx;
+ /* The following fields are only used when bitoffs != 0 */
+ byte* buf;
+ int done;
+
+} ErtsMakeHash2Context_SUB_BINARY_SUBTAG;
+
+typedef struct {
+ int dummy__; /* Empty structs are not supported on all platforms */
+} ErtsMakeHash2Context_EMPTY;
+
+typedef struct {
+ ErtsMakeHash2TrapLocation trap_location;
+ /* specific to the trap location: */
+ union {
+ ErtsMakeHash2Context_TAG_PRIMARY_LIST tag_primary_list;
+ ErtsMakeHash2Context_ARITYVAL_SUBTAG arityval_subtag;
+ ErtsMakeHash2Context_HAMT_SUBTAG_HEAD_FLATMAP hamt_subtag_head_flatmap;
+ ErtsMakeHash2Context_MAP_SUBTAG map_subtag;
+ ErtsMakeHash2Context_FUN_SUBTAG fun_subtag;
+ ErtsMakeHash2Context_NEG_BIG_SUBTAG neg_big_subtag;
+ ErtsMakeHash2Context_SUB_BINARY_SUBTAG sub_binary_subtag_1;
+ ErtsMakeHash2Context_SUB_BINARY_SUBTAG sub_binary_subtag_2;
+ ErtsMakeHash2Context_EMPTY hash2_common_1;
+ ErtsMakeHash2Context_EMPTY hash2_common_2;
+ ErtsMakeHash2Context_EMPTY hash2_common_3;
+ } trap_location_state;
+ /* same for all trap locations: */
+ Eterm term;
Uint32 hash;
Uint32 hash_xor_pairs;
- DeclareTmpHeapNoproc(tmp_big,2);
+ ErtsEStack stack;
+} ErtsMakeHash2Context;
+
+static int make_hash2_ctx_bin_dtor(Binary *context_bin) {
+ ErtsMakeHash2Context* context = ERTS_MAGIC_BIN_DATA(context_bin);
+ DESTROY_SAVED_ESTACK(&context->stack);
+ if (context->trap_location == sub_binary_subtag_2 &&
+ context->trap_location_state.sub_binary_subtag_2.buf != NULL) {
+ erts_free(ERTS_ALC_T_PHASH2_TRAP, context->trap_location_state.sub_binary_subtag_2.buf);
+ }
+ return 1;
+}
+/* hash2_save_trap_state is called seldom so we want to avoid inlining */
+static ERTS_NOINLINE
+Eterm hash2_save_trap_state(Eterm state_mref,
+ Uint32 hash_xor_pairs,
+ Uint32 hash,
+ Process* p,
+ Eterm term,
+ Eterm* ESTK_DEF_STACK(s),
+ ErtsEStack s,
+ ErtsMakeHash2TrapLocation trap_location,
+ void* trap_location_state_ptr,
+ size_t trap_location_state_size) {
+ Binary* state_bin;
+ ErtsMakeHash2Context* context;
+ if (state_mref == THE_NON_VALUE) {
+ Eterm* hp;
+ state_bin = erts_create_magic_binary(sizeof(ErtsMakeHash2Context),
+ make_hash2_ctx_bin_dtor);
+ hp = HAlloc(p, ERTS_MAGIC_REF_THING_SIZE);
+ state_mref = erts_mk_magic_ref(&hp, &MSO(p), state_bin);
+ } else {
+ state_bin = erts_magic_ref2bin(state_mref);
+ }
+ context = ERTS_MAGIC_BIN_DATA(state_bin);
+ context->term = term;
+ context->hash = hash;
+ context->hash_xor_pairs = hash_xor_pairs;
+ ESTACK_SAVE(s, &context->stack);
+ context->trap_location = trap_location;
+ sys_memcpy(&context->trap_location_state,
+ trap_location_state_ptr,
+ trap_location_state_size);
+ erts_set_gc_state(p, 0);
+ BUMP_ALL_REDS(p);
+ return state_mref;
+}
+#undef NOINLINE_HASH2_SAVE_TRAP_STATE
+
+/* Writes back a magic reference to *state_mref_write_back when the
+ function traps */
+static ERTS_INLINE Uint32
+make_hash2_helper(Eterm term_param, const int can_trap, Eterm* state_mref_write_back, Process* p)
+{
+ static const Uint ITERATIONS_PER_RED = 64;
+ Uint32 hash;
+ Uint32 hash_xor_pairs;
+ Eterm term = term_param;
ERTS_UNDEF(hash_xor_pairs, 0);
/* (HCONST * {2, ..., 22}) mod 2^32 */
@@ -1168,12 +1351,63 @@ make_hash2(Eterm term)
#define IS_SSMALL28(x) (((Uint) (((x) >> (28-1)) + 1)) < 2)
+#define NOT_SSMALL28_HASH(SMALL) \
+ do { \
+ Uint64 t; \
+ Uint32 x, y; \
+ Uint32 con; \
+ if (SMALL < 0) { \
+ con = HCONST_10; \
+ t = (Uint64)(SMALL * (-1)); \
+ } else { \
+ con = HCONST_11; \
+ t = SMALL; \
+ } \
+ x = t & 0xffffffff; \
+ y = t >> 32; \
+ UINT32_HASH_2(x, y, con); \
+ } while(0)
+
#ifdef ARCH_64
# define POINTER_HASH(Ptr, AConst) UINT32_HASH_2((Uint32)(UWord)(Ptr), (((UWord)(Ptr)) >> 32), AConst)
#else
# define POINTER_HASH(Ptr, AConst) UINT32_HASH(Ptr, AConst)
#endif
+#define TRAP_LOCATION_NO_RED(location_name) \
+ do { \
+ if(can_trap && iterations_until_trap <= 0) { \
+ *state_mref_write_back = \
+ hash2_save_trap_state(state_mref, \
+ hash_xor_pairs, \
+ hash, \
+ p, \
+ term, \
+ ESTK_DEF_STACK(s), \
+ s, \
+ location_name, \
+ &ctx, \
+ sizeof(ctx)); \
+ return 0; \
+ L_##location_name: \
+ ctx = context->trap_location_state. location_name; \
+ } \
+ } while(0)
+
+#define TRAP_LOCATION(location_name) \
+ do { \
+ if (can_trap) { \
+ iterations_until_trap--; \
+ TRAP_LOCATION_NO_RED(location_name); \
+ } \
+ } while(0)
+
+#define TRAP_LOCATION_NO_CTX(location_name) \
+ do { \
+ ErtsMakeHash2Context_EMPTY ctx; \
+ TRAP_LOCATION(location_name); \
+ } while(0)
+
/* Optimization. Simple cases before declaration of estack. */
if (primary_tag(term) == TAG_PRIMARY_IMMED1) {
switch (term & _TAG_IMMED1_MASK) {
@@ -1186,51 +1420,94 @@ make_hash2(Eterm term)
break;
case _TAG_IMMED1_SMALL:
{
- Sint x = signed_val(term);
-
- if (SMALL_BITS > 28 && !IS_SSMALL28(x)) {
- term = small_to_big(x, tmp_big);
- break;
+ Sint small = signed_val(term);
+ if (SMALL_BITS > 28 && !IS_SSMALL28(small)) {
+ hash = 0;
+ NOT_SSMALL28_HASH(small);
+ return hash;
}
hash = 0;
- SINT32_HASH(x, HCONST);
+ SINT32_HASH(small, HCONST);
return hash;
}
}
};
{
Eterm tmp;
+ long max_iterations = 0;
+ long iterations_until_trap = 0;
+ Eterm state_mref = THE_NON_VALUE;
+ ErtsMakeHash2Context* context = NULL;
DECLARE_ESTACK(s);
-
- UseTmpHeapNoproc(2);
+ ESTACK_CHANGE_ALLOCATOR(s, ERTS_ALC_T_SAVED_ESTACK);
+ if(can_trap){
+#ifdef DEBUG
+ (void)ITERATIONS_PER_RED;
+ iterations_until_trap = max_iterations =
+ (1103515245 * (ERTS_BIF_REDS_LEFT(p)) + 12345) % 227;
+#else
+ iterations_until_trap = max_iterations =
+ ITERATIONS_PER_RED * ERTS_BIF_REDS_LEFT(p);
+#endif
+ }
+ if (can_trap && is_internal_magic_ref(term)) {
+ Binary* state_bin;
+ state_mref = term;
+ state_bin = erts_magic_ref2bin(state_mref);
+ if (ERTS_MAGIC_BIN_DESTRUCTOR(state_bin) == make_hash2_ctx_bin_dtor) {
+ /* Restore state after a trap */
+ context = ERTS_MAGIC_BIN_DATA(state_bin);
+ term = context->term;
+ hash = context->hash;
+ hash_xor_pairs = context->hash_xor_pairs;
+ ESTACK_RESTORE(s, &context->stack);
+ ASSERT(p->flags & F_DISABLE_GC);
+ erts_set_gc_state(p, 1);
+ switch (context->trap_location) {
+ case hash2_common_3: goto L_hash2_common_3;
+ case tag_primary_list: goto L_tag_primary_list;
+ case arityval_subtag: goto L_arityval_subtag;
+ case hamt_subtag_head_flatmap: goto L_hamt_subtag_head_flatmap;
+ case map_subtag: goto L_map_subtag;
+ case fun_subtag: goto L_fun_subtag;
+ case neg_big_subtag: goto L_neg_big_subtag;
+ case sub_binary_subtag_1: goto L_sub_binary_subtag_1;
+ case sub_binary_subtag_2: goto L_sub_binary_subtag_2;
+ case hash2_common_1: goto L_hash2_common_1;
+ case hash2_common_2: goto L_hash2_common_2;
+ }
+ }
+ }
hash = 0;
for (;;) {
switch (primary_tag(term)) {
case TAG_PRIMARY_LIST:
{
- int c = 0;
- Uint32 sh = 0;
- Eterm* ptr = list_val(term);
- while (is_byte(*ptr)) {
+ ErtsMakeHash2Context_TAG_PRIMARY_LIST ctx = {
+ .c = 0,
+ .sh = 0,
+ .ptr = list_val(term)};
+ while (is_byte(*ctx.ptr)) {
/* Optimization for strings. */
- sh = (sh << 8) + unsigned_val(*ptr);
- if (c == 3) {
- UINT32_HASH(sh, HCONST_4);
- c = sh = 0;
+ ctx.sh = (ctx.sh << 8) + unsigned_val(*ctx.ptr);
+ if (ctx.c == 3) {
+ UINT32_HASH(ctx.sh, HCONST_4);
+ ctx.c = ctx.sh = 0;
} else {
- c++;
+ ctx.c++;
}
- term = CDR(ptr);
+ term = CDR(ctx.ptr);
if (is_not_list(term))
break;
- ptr = list_val(term);
+ ctx.ptr = list_val(term);
+ TRAP_LOCATION(tag_primary_list);
}
- if (c > 0)
- UINT32_HASH(sh, HCONST_4);
+ if (ctx.c > 0)
+ UINT32_HASH(ctx.sh, HCONST_4);
if (is_list(term)) {
- tmp = CDR(ptr);
+ tmp = CDR(ctx.ptr);
ESTACK_PUSH(s, tmp);
- term = CAR(ptr);
+ term = CAR(ctx.ptr);
}
}
break;
@@ -1241,34 +1518,39 @@ make_hash2(Eterm term)
switch (hdr & _TAG_HEADER_MASK) {
case ARITYVAL_SUBTAG:
{
- int i;
- int arity = header_arity(hdr);
- Eterm* elem = tuple_val(term);
- UINT32_HASH(arity, HCONST_9);
- if (arity == 0) /* Empty tuple */
+ ErtsMakeHash2Context_ARITYVAL_SUBTAG ctx = {
+ .i = 0,
+ .arity = header_arity(hdr),
+ .elem = tuple_val(term)};
+ UINT32_HASH(ctx.arity, HCONST_9);
+ if (ctx.arity == 0) /* Empty tuple */
goto hash2_common;
- for (i = arity; ; i--) {
- term = elem[i];
- if (i == 1)
+ for (ctx.i = ctx.arity; ; ctx.i--) {
+ term = ctx.elem[ctx.i];
+ if (ctx.i == 1)
break;
ESTACK_PUSH(s, term);
+ TRAP_LOCATION(arityval_subtag);
}
}
break;
case MAP_SUBTAG:
{
- Eterm* ptr = boxed_val(term) + 1;
Uint size;
- int i;
+ ErtsMakeHash2Context_MAP_SUBTAG ctx = {
+ .ptr = boxed_val(term) + 1,
+ .i = 0};
switch (hdr & _HEADER_MAP_SUBTAG_MASK) {
case HAMT_SUBTAG_HEAD_FLATMAP:
{
flatmap_t *mp = (flatmap_t *)flatmap_val(term);
- Eterm *ks = flatmap_get_keys(mp);
- Eterm *vs = flatmap_get_values(mp);
- size = flatmap_get_size(mp);
- UINT32_HASH(size, HCONST_16);
- if (size == 0)
+ ErtsMakeHash2Context_HAMT_SUBTAG_HEAD_FLATMAP ctx = {
+ .ks = flatmap_get_keys(mp),
+ .vs = flatmap_get_values(mp),
+ .i = 0,
+ .size = flatmap_get_size(mp)};
+ UINT32_HASH(ctx.size, HCONST_16);
+ if (ctx.size == 0)
goto hash2_common;
/* We want a portable hash function that is *independent* of
@@ -1281,17 +1563,18 @@ make_hash2(Eterm term)
ESTACK_PUSH(s, HASH_MAP_TAIL);
hash = 0;
hash_xor_pairs = 0;
- for (i = size - 1; i >= 0; i--) {
+ for (ctx.i = ctx.size - 1; ctx.i >= 0; ctx.i--) {
ESTACK_PUSH(s, HASH_MAP_PAIR);
- ESTACK_PUSH(s, vs[i]);
- ESTACK_PUSH(s, ks[i]);
+ ESTACK_PUSH(s, ctx.vs[ctx.i]);
+ ESTACK_PUSH(s, ctx.ks[ctx.i]);
+ TRAP_LOCATION(hamt_subtag_head_flatmap);
}
goto hash2_common;
}
case HAMT_SUBTAG_HEAD_ARRAY:
case HAMT_SUBTAG_HEAD_BITMAP:
- size = *ptr++;
+ size = *ctx.ptr++;
UINT32_HASH(size, HCONST_16);
if (size == 0)
goto hash2_common;
@@ -1303,27 +1586,28 @@ make_hash2(Eterm term)
}
switch (hdr & _HEADER_MAP_SUBTAG_MASK) {
case HAMT_SUBTAG_HEAD_ARRAY:
- i = 16;
+ ctx.i = 16;
break;
case HAMT_SUBTAG_HEAD_BITMAP:
case HAMT_SUBTAG_NODE_BITMAP:
- i = hashmap_bitcount(MAP_HEADER_VAL(hdr));
+ ctx.i = hashmap_bitcount(MAP_HEADER_VAL(hdr));
break;
default:
erts_exit(ERTS_ERROR_EXIT, "bad header");
}
- while (i) {
- if (is_list(*ptr)) {
- Eterm* cons = list_val(*ptr);
+ while (ctx.i) {
+ if (is_list(*ctx.ptr)) {
+ Eterm* cons = list_val(*ctx.ptr);
ESTACK_PUSH(s, HASH_MAP_PAIR);
ESTACK_PUSH(s, CDR(cons));
ESTACK_PUSH(s, CAR(cons));
}
else {
- ASSERT(is_boxed(*ptr));
- ESTACK_PUSH(s, *ptr);
+ ASSERT(is_boxed(*ctx.ptr));
+ ESTACK_PUSH(s, *ctx.ptr);
}
- i--; ptr++;
+ ctx.i--; ctx.ptr++;
+ TRAP_LOCATION(map_subtag);
}
goto hash2_common;
}
@@ -1344,22 +1628,25 @@ make_hash2(Eterm term)
case FUN_SUBTAG:
{
ErlFunThing* funp = (ErlFunThing *) fun_val(term);
- Uint num_free = funp->num_free;
+ ErtsMakeHash2Context_FUN_SUBTAG ctx = {
+ .num_free = funp->num_free,
+ .bptr = NULL};
UINT32_HASH_2
- (num_free,
+ (ctx.num_free,
atom_tab(atom_val(funp->fe->module))->slot.bucket.hvalue,
HCONST);
UINT32_HASH_2
- (funp->fe->old_index, funp->fe->old_uniq, HCONST);
- if (num_free == 0) {
+ (funp->fe->index, funp->fe->old_uniq, HCONST);
+ if (ctx.num_free == 0) {
goto hash2_common;
} else {
- Eterm* bptr = funp->env + num_free - 1;
- while (num_free-- > 1) {
- term = *bptr--;
+ ctx.bptr = funp->env + ctx.num_free - 1;
+ while (ctx.num_free-- > 1) {
+ term = *ctx.bptr--;
ESTACK_PUSH(s, term);
+ TRAP_LOCATION(fun_subtag);
}
- term = *bptr;
+ term = *ctx.bptr;
}
}
break;
@@ -1367,70 +1654,190 @@ make_hash2(Eterm term)
case HEAP_BINARY_SUBTAG:
case SUB_BINARY_SUBTAG:
{
- byte* bptr;
- unsigned sz = binary_size(term);
+#define BYTE_BITS 8
+ ErtsMakeHash2Context_SUB_BINARY_SUBTAG ctx = {
+ .bptr = 0,
+ /* !!!!!!!!!!!!!!!!!!!! OBS !!!!!!!!!!!!!!!!!!!!
+ *
+ * The size is truncated to 32 bits on the line
+ * below so that the code is compatible with old
+ * versions of the code. This means that hash
+ * values for binaries with a size greater than
+ * 4GB do not take all bytes in consideration.
+ *
+ * !!!!!!!!!!!!!!!!!!!! OBS !!!!!!!!!!!!!!!!!!!!
+ */
+ .sz = (0xFFFFFFFF & binary_size(term)),
+ .bitsize = 0,
+ .bitoffs = 0,
+ .no_bytes_processed = 0
+ };
Uint32 con = HCONST_13 + hash;
- Uint bitoffs;
- Uint bitsize;
-
- ERTS_GET_BINARY_BYTES(term, bptr, bitoffs, bitsize);
- if (sz == 0 && bitsize == 0) {
+ Uint iters_for_bin = MAX(1, ctx.sz / BLOCK_HASH_BYTES_PER_ITER);
+ ERTS_GET_BINARY_BYTES(term, ctx.bptr, ctx.bitoffs, ctx.bitsize);
+ if (ctx.sz == 0 && ctx.bitsize == 0) {
hash = con;
- } else {
- if (bitoffs == 0) {
- hash = block_hash(bptr, sz, con);
- if (bitsize > 0) {
- UINT32_HASH_2(bitsize, (bptr[sz] >> (8 - bitsize)),
- HCONST_15);
- }
- } else {
- byte* buf = (byte *) erts_alloc(ERTS_ALC_T_TMP,
- sz + (bitsize != 0));
- erts_copy_bits(bptr, bitoffs, 1, buf, 0, 1, sz*8+bitsize);
- hash = block_hash(buf, sz, con);
- if (bitsize > 0) {
- UINT32_HASH_2(bitsize, (buf[sz] >> (8 - bitsize)),
- HCONST_15);
- }
- erts_free(ERTS_ALC_T_TMP, (void *) buf);
- }
+ } else if (ctx.bitoffs == 0 &&
+ (!can_trap ||
+ (iterations_until_trap - iters_for_bin) > 0)) {
+ /* No need to trap while hashing binary */
+ if (can_trap) iterations_until_trap -= iters_for_bin;
+ hash = block_hash(ctx.bptr, ctx.sz, con);
+ if (ctx.bitsize > 0) {
+ UINT32_HASH_2(ctx.bitsize,
+ (ctx.bptr[ctx.sz] >> (BYTE_BITS - ctx.bitsize)),
+ HCONST_15);
+ }
+ } else if (ctx.bitoffs == 0) {
+ /* Need to trap while hashing binary */
+ ErtsBlockHashHelperCtx* block_hash_ctx = &ctx.block_hash_ctx;
+ block_hash_setup(con, block_hash_ctx);
+ do {
+ Uint max_bytes_to_process =
+ iterations_until_trap <= 0 ? BLOCK_HASH_BYTES_PER_ITER :
+ iterations_until_trap * BLOCK_HASH_BYTES_PER_ITER;
+ Uint bytes_left = ctx.sz - ctx.no_bytes_processed;
+ Uint even_bytes_left =
+ bytes_left - (bytes_left % BLOCK_HASH_BYTES_PER_ITER);
+ Uint bytes_to_process =
+ MIN(max_bytes_to_process, even_bytes_left);
+ block_hash_buffer(&ctx.bptr[ctx.no_bytes_processed],
+ bytes_to_process,
+ block_hash_ctx);
+ ctx.no_bytes_processed += bytes_to_process;
+ iterations_until_trap -=
+ MAX(1, bytes_to_process / BLOCK_HASH_BYTES_PER_ITER);
+ TRAP_LOCATION_NO_RED(sub_binary_subtag_1);
+ block_hash_ctx = &ctx.block_hash_ctx; /* Restore after trap */
+ } while ((ctx.sz - ctx.no_bytes_processed) >=
+ BLOCK_HASH_BYTES_PER_ITER);
+ hash = block_hash_final_bytes(ctx.bptr +
+ ctx.no_bytes_processed,
+ ctx.sz - ctx.no_bytes_processed,
+ ctx.sz,
+ block_hash_ctx);
+ if (ctx.bitsize > 0) {
+ UINT32_HASH_2(ctx.bitsize,
+ (ctx.bptr[ctx.sz] >> (BYTE_BITS - ctx.bitsize)),
+ HCONST_15);
+ }
+ } else if (/* ctx.bitoffs != 0 && */
+ (!can_trap ||
+ (iterations_until_trap - iters_for_bin) > 0)) {
+ /* No need to trap while hashing binary */
+ Uint nr_of_bytes = ctx.sz + (ctx.bitsize != 0);
+ byte *buf = erts_alloc(ERTS_ALC_T_TMP, nr_of_bytes);
+ Uint nr_of_bits_to_copy = ctx.sz*BYTE_BITS+ctx.bitsize;
+ if (can_trap) iterations_until_trap -= iters_for_bin;
+ erts_copy_bits(ctx.bptr,
+ ctx.bitoffs, 1, buf, 0, 1, nr_of_bits_to_copy);
+ hash = block_hash(buf, ctx.sz, con);
+ if (ctx.bitsize > 0) {
+ UINT32_HASH_2(ctx.bitsize,
+ (buf[ctx.sz] >> (BYTE_BITS - ctx.bitsize)),
+ HCONST_15);
+ }
+ erts_free(ERTS_ALC_T_TMP, buf);
+ } else /* ctx.bitoffs != 0 && */ {
+#ifdef DEBUG
+#define BINARY_BUF_SIZE (BLOCK_HASH_BYTES_PER_ITER * 3)
+#else
+#define BINARY_BUF_SIZE (BLOCK_HASH_BYTES_PER_ITER * 256)
+#endif
+#define BINARY_BUF_SIZE_BITS (BINARY_BUF_SIZE*BYTE_BITS)
+ /* Need to trap while hashing binary */
+ ErtsBlockHashHelperCtx* block_hash_ctx = &ctx.block_hash_ctx;
+ Uint nr_of_bytes = ctx.sz + (ctx.bitsize != 0);
+ ERTS_CT_ASSERT(BINARY_BUF_SIZE % BLOCK_HASH_BYTES_PER_ITER == 0);
+ ctx.buf = erts_alloc(ERTS_ALC_T_PHASH2_TRAP,
+ MIN(nr_of_bytes, BINARY_BUF_SIZE));
+ block_hash_setup(con, block_hash_ctx);
+ do {
+ Uint bytes_left =
+ ctx.sz - ctx.no_bytes_processed;
+ Uint even_bytes_left =
+ bytes_left - (bytes_left % BLOCK_HASH_BYTES_PER_ITER);
+ Uint bytes_to_process =
+ MIN(BINARY_BUF_SIZE, even_bytes_left);
+ Uint nr_of_bits_left =
+ (ctx.sz*BYTE_BITS+ctx.bitsize) -
+ ctx.no_bytes_processed*BYTE_BITS;
+ Uint nr_of_bits_to_copy =
+ MIN(nr_of_bits_left, BINARY_BUF_SIZE_BITS);
+ ctx.done = nr_of_bits_left == nr_of_bits_to_copy;
+ erts_copy_bits(ctx.bptr + ctx.no_bytes_processed,
+ ctx.bitoffs, 1, ctx.buf, 0, 1,
+ nr_of_bits_to_copy);
+ block_hash_buffer(ctx.buf,
+ bytes_to_process,
+ block_hash_ctx);
+ ctx.no_bytes_processed += bytes_to_process;
+ iterations_until_trap -=
+ MAX(1, bytes_to_process / BLOCK_HASH_BYTES_PER_ITER);
+ TRAP_LOCATION_NO_RED(sub_binary_subtag_2);
+ block_hash_ctx = &ctx.block_hash_ctx; /* Restore after trap */
+ } while (!ctx.done);
+ nr_of_bytes = ctx.sz + (ctx.bitsize != 0);
+ hash = block_hash_final_bytes(ctx.buf +
+ (ctx.no_bytes_processed -
+ ((nr_of_bytes-1) / BINARY_BUF_SIZE) * BINARY_BUF_SIZE),
+ ctx.sz - ctx.no_bytes_processed,
+ ctx.sz,
+ block_hash_ctx);
+ if (ctx.bitsize > 0) {
+ Uint last_byte_index =
+ nr_of_bytes - (((nr_of_bytes-1) / BINARY_BUF_SIZE) * BINARY_BUF_SIZE) -1;
+ UINT32_HASH_2(ctx.bitsize,
+ (ctx.buf[last_byte_index] >> (BYTE_BITS - ctx.bitsize)),
+ HCONST_15);
+ }
+ erts_free(ERTS_ALC_T_PHASH2_TRAP, ctx.buf);
+ context->trap_location_state.sub_binary_subtag_2.buf = NULL;
}
goto hash2_common;
+#undef BYTE_BITS
+#undef BINARY_BUF_SIZE
+#undef BINARY_BUF_SIZE_BITS
}
break;
case POS_BIG_SUBTAG:
case NEG_BIG_SUBTAG:
{
- Eterm* ptr = big_val(term);
- Uint i = 0;
- Uint n = BIG_SIZE(ptr);
- Uint32 con = BIG_SIGN(ptr) ? HCONST_10 : HCONST_11;
+ Eterm* big_val_ptr = big_val(term);
+ ErtsMakeHash2Context_NEG_BIG_SUBTAG ctx = {
+ .ptr = big_val_ptr,
+ .i = 0,
+ .n = BIG_SIZE(big_val_ptr),
+ .con = BIG_SIGN(big_val_ptr) ? HCONST_10 : HCONST_11};
#if D_EXP == 16
do {
Uint32 x, y;
- x = i < n ? BIG_DIGIT(ptr, i++) : 0;
- x += (Uint32)(i < n ? BIG_DIGIT(ptr, i++) : 0) << 16;
- y = i < n ? BIG_DIGIT(ptr, i++) : 0;
- y += (Uint32)(i < n ? BIG_DIGIT(ptr, i++) : 0) << 16;
- UINT32_HASH_2(x, y, con);
- } while (i < n);
+ x = ctx.i < ctx.n ? BIG_DIGIT(ctx.ptr, ctx.i++) : 0;
+ x += (Uint32)(ctx.i < ctx.n ? BIG_DIGIT(ctx.ptr, ctx.i++) : 0) << 16;
+ y = ctx.i < ctx.n ? BIG_DIGIT(ctx.ptr, ctx.i++) : 0;
+ y += (Uint32)(ctx.i < ctx.n ? BIG_DIGIT(ctx.ptr, ctx.i++) : 0) << 16;
+ UINT32_HASH_2(x, y, ctx.con);
+ TRAP_LOCATION(neg_big_subtag);
+ } while (ctx.i < ctx.n);
#elif D_EXP == 32
do {
Uint32 x, y;
- x = i < n ? BIG_DIGIT(ptr, i++) : 0;
- y = i < n ? BIG_DIGIT(ptr, i++) : 0;
- UINT32_HASH_2(x, y, con);
- } while (i < n);
+ x = ctx.i < ctx.n ? BIG_DIGIT(ctx.ptr, ctx.i++) : 0;
+ y = ctx.i < ctx.n ? BIG_DIGIT(ctx.ptr, ctx.i++) : 0;
+ UINT32_HASH_2(x, y, ctx.con);
+ TRAP_LOCATION(neg_big_subtag);
+ } while (ctx.i < ctx.n);
#elif D_EXP == 64
do {
Uint t;
Uint32 x, y;
- ASSERT(i < n);
- t = BIG_DIGIT(ptr, i++);
+ ASSERT(ctx.i < ctx.n);
+ t = BIG_DIGIT(ctx.ptr, ctx.i++);
x = t & 0xffffffff;
y = t >> 32;
- UINT32_HASH_2(x, y, con);
- } while (i < n);
+ UINT32_HASH_2(x, y, ctx.con);
+ TRAP_LOCATION(neg_big_subtag);
+ } while (ctx.i < ctx.n);
#else
#error "unsupported D_EXP size"
#endif
@@ -1508,13 +1915,13 @@ make_hash2(Eterm term)
}
case _TAG_IMMED1_SMALL:
{
- Sint x = signed_val(term);
+ Sint small = signed_val(term);
+ if (SMALL_BITS > 28 && !IS_SSMALL28(small)) {
+ NOT_SSMALL28_HASH(small);
+ } else {
+ SINT32_HASH(small, HCONST);
+ }
- if (SMALL_BITS > 28 && !IS_SSMALL28(x)) {
- term = small_to_big(x, tmp_big);
- break;
- }
- SINT32_HASH(x, HCONST);
goto hash2_common;
}
}
@@ -1529,7 +1936,10 @@ make_hash2(Eterm term)
if (ESTACK_ISEMPTY(s)) {
DESTROY_ESTACK(s);
- UnUseTmpHeapNoproc(2);
+ if (can_trap) {
+ BUMP_REDS(p, (max_iterations - iterations_until_trap) / ITERATIONS_PER_RED);
+ ASSERT(!(p->flags & F_DISABLE_GC));
+ }
return hash;
}
@@ -1540,18 +1950,37 @@ make_hash2(Eterm term)
hash = (Uint32) ESTACK_POP(s);
UINT32_HASH(hash_xor_pairs, HCONST_19);
hash_xor_pairs = (Uint32) ESTACK_POP(s);
+ TRAP_LOCATION_NO_CTX(hash2_common_1);
goto hash2_common;
}
case HASH_MAP_PAIR:
hash_xor_pairs ^= hash;
hash = 0;
+ TRAP_LOCATION_NO_CTX(hash2_common_2);
goto hash2_common;
default:
break;
}
+
}
+ TRAP_LOCATION_NO_CTX(hash2_common_3);
}
}
+#undef TRAP_LOCATION_NO_RED
+#undef TRAP_LOCATION
+#undef TRAP_LOCATION_NO_CTX
+}
+
+Uint32
+make_hash2(Eterm term)
+{
+ return make_hash2_helper(term, 0, NULL, NULL);
+}
+
+Uint32
+trapping_make_hash2(Eterm term, Eterm* state_mref_write_back, Process* p)
+{
+ return make_hash2_helper(term, 1, state_mref_write_back, p);
}
/* Term hash function for internal use.
@@ -1731,7 +2160,7 @@ make_internal_hash(Eterm term, Uint32 salt)
ErlFunThing* funp = (ErlFunThing *) fun_val(term);
Uint num_free = funp->num_free;
UINT32_HASH_2(num_free, funp->fe->module, HCONST_20);
- UINT32_HASH_2(funp->fe->old_index, funp->fe->old_uniq, HCONST_21);
+ UINT32_HASH_2(funp->fe->index, funp->fe->old_uniq, HCONST_21);
if (num_free == 0) {
goto pop_next;
} else {
@@ -2381,7 +2810,7 @@ tailrecur_ne:
f1 = (ErlFunThing *) fun_val(a);
f2 = (ErlFunThing *) fun_val(b);
if (f1->fe->module != f2->fe->module ||
- f1->fe->old_index != f2->fe->old_index ||
+ f1->fe->index != f2->fe->index ||
f1->fe->old_uniq != f2->fe->old_uniq ||
f1->num_free != f2->num_free) {
goto not_equal;
@@ -2544,10 +2973,14 @@ tailrecur_ne:
bb = hashmap_val(b) + 1;
switch (hdr & _HEADER_MAP_SUBTAG_MASK) {
case HAMT_SUBTAG_HEAD_ARRAY:
+ if (aa[0] != bb[0])
+ goto not_equal;
aa++; bb++;
sz = 16;
break;
case HAMT_SUBTAG_HEAD_BITMAP:
+ if (aa[0] != bb[0])
+ goto not_equal;
aa++; bb++;
case HAMT_SUBTAG_NODE_BITMAP:
sz = hashmap_bitcount(MAP_HEADER_VAL(hdr));
@@ -2701,8 +3134,7 @@ 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); \
- if ((AN)->creation != 0 && (BN)->creation != 0) \
- RETURN_NEQ(((AN)->creation < (BN)->creation) ? -1 : 1); \
+ RETURN_NEQ(((AN)->creation < (BN)->creation) ? -1 : 1); \
} \
} while (0)
@@ -2976,7 +3408,7 @@ tailrecur_ne:
if (diff != 0) {
RETURN_NEQ(diff);
}
- diff = f1->fe->old_index - f2->fe->old_index;
+ diff = f1->fe->index - f2->fe->index;
if (diff != 0) {
RETURN_NEQ(diff);
}
@@ -4454,201 +4886,6 @@ erts_get_emu_args(Process *c_p)
return res;
}
-
-Eterm
-erts_get_ethread_info(Process *c_p)
-{
- Uint sz, *szp;
- Eterm res, *hp, **hpp, *end_hp = NULL;
-
- sz = 0;
- szp = &sz;
- hpp = NULL;
-
- while (1) {
- Eterm tup, list, name;
-#if defined(ETHR_NATIVE_ATOMIC32_IMPL) \
- || defined(ETHR_NATIVE_ATOMIC64_IMPL) \
- || defined(ETHR_NATIVE_DW_ATOMIC_IMPL)
- char buf[1024];
- int i;
- char **str;
-#endif
-
- res = NIL;
-
-#ifdef ETHR_X86_MEMBAR_H__
-
- tup = erts_bld_tuple(hpp, szp, 2,
- erts_bld_string(hpp, szp, "sse2"),
-#ifdef ETHR_X86_RUNTIME_CONF_HAVE_SSE2__
- erts_bld_string(hpp, szp,
- (ETHR_X86_RUNTIME_CONF_HAVE_SSE2__
- ? "yes" : "no"))
-#else
- erts_bld_string(hpp, szp, "yes")
-#endif
- );
- res = erts_bld_cons(hpp, szp, tup, res);
-
- tup = erts_bld_tuple(hpp, szp, 2,
- erts_bld_string(hpp, szp,
- "x86"
-#ifdef ARCH_64
- "_64"
-#endif
- " OOO"),
- erts_bld_string(hpp, szp,
-#ifdef ETHR_X86_OUT_OF_ORDER
- "yes"
-#else
- "no"
-#endif
- ));
-
- res = erts_bld_cons(hpp, szp, tup, res);
-#endif
-
-#ifdef ETHR_SPARC_V9_MEMBAR_H__
-
- tup = erts_bld_tuple(hpp, szp, 2,
- erts_bld_string(hpp, szp, "Sparc V9"),
- erts_bld_string(hpp, szp,
-#if defined(ETHR_SPARC_TSO)
- "TSO"
-#elif defined(ETHR_SPARC_PSO)
- "PSO"
-#elif defined(ETHR_SPARC_RMO)
- "RMO"
-#else
- "undefined"
-#endif
- ));
-
- res = erts_bld_cons(hpp, szp, tup, res);
-
-#endif
-
-#ifdef ETHR_PPC_MEMBAR_H__
-
- tup = erts_bld_tuple(hpp, szp, 2,
- erts_bld_string(hpp, szp, "lwsync"),
- erts_bld_string(hpp, szp,
-#if defined(ETHR_PPC_HAVE_LWSYNC)
- "yes"
-#elif defined(ETHR_PPC_HAVE_NO_LWSYNC)
- "no"
-#elif defined(ETHR_PPC_RUNTIME_CONF_HAVE_LWSYNC__)
- ETHR_PPC_RUNTIME_CONF_HAVE_LWSYNC__ ? "yes" : "no"
-#else
- "undefined"
-#endif
- ));
-
- res = erts_bld_cons(hpp, szp, tup, res);
-
-#endif
-
- tup = erts_bld_tuple(hpp, szp, 2,
- erts_bld_string(hpp, szp, "Native rw-spinlocks"),
-#ifdef ETHR_NATIVE_RWSPINLOCK_IMPL
- erts_bld_string(hpp, szp, ETHR_NATIVE_RWSPINLOCK_IMPL)
-#else
- erts_bld_string(hpp, szp, "no")
-#endif
- );
- res = erts_bld_cons(hpp, szp, tup, res);
-
- tup = erts_bld_tuple(hpp, szp, 2,
- erts_bld_string(hpp, szp, "Native spinlocks"),
-#ifdef ETHR_NATIVE_SPINLOCK_IMPL
- erts_bld_string(hpp, szp, ETHR_NATIVE_SPINLOCK_IMPL)
-#else
- erts_bld_string(hpp, szp, "no")
-#endif
- );
- res = erts_bld_cons(hpp, szp, tup, res);
-
-
- list = NIL;
-#ifdef ETHR_NATIVE_DW_ATOMIC_IMPL
- if (ethr_have_native_dw_atomic()) {
- name = erts_bld_string(hpp, szp, ETHR_NATIVE_DW_ATOMIC_IMPL);
- str = ethr_native_dw_atomic_ops();
- for (i = 0; str[i]; i++) {
- erts_snprintf(buf, sizeof(buf), "ethr_native_dw_atomic_%s()", str[i]);
- list = erts_bld_cons(hpp, szp,
- erts_bld_string(hpp, szp, buf),
- list);
- }
- str = ethr_native_su_dw_atomic_ops();
- for (i = 0; str[i]; i++) {
- erts_snprintf(buf, sizeof(buf), "ethr_native_su_dw_atomic_%s()", str[i]);
- list = erts_bld_cons(hpp, szp,
- erts_bld_string(hpp, szp, buf),
- list);
- }
- }
- else
-#endif
- name = erts_bld_string(hpp, szp, "no");
-
- tup = erts_bld_tuple(hpp, szp, 3,
- erts_bld_string(hpp, szp, "Double word native atomics"),
- name,
- list);
- res = erts_bld_cons(hpp, szp, tup, res);
-
- list = NIL;
-#ifdef ETHR_NATIVE_ATOMIC64_IMPL
- name = erts_bld_string(hpp, szp, ETHR_NATIVE_ATOMIC64_IMPL);
- str = ethr_native_atomic64_ops();
- for (i = 0; str[i]; i++) {
- erts_snprintf(buf, sizeof(buf), "ethr_native_atomic64_%s()", str[i]);
- list = erts_bld_cons(hpp, szp,
- erts_bld_string(hpp, szp, buf),
- list);
- }
-#else
- name = erts_bld_string(hpp, szp, "no");
-#endif
- tup = erts_bld_tuple(hpp, szp, 3,
- erts_bld_string(hpp, szp, "64-bit native atomics"),
- name,
- list);
- res = erts_bld_cons(hpp, szp, tup, res);
-
- list = NIL;
-#ifdef ETHR_NATIVE_ATOMIC32_IMPL
- name = erts_bld_string(hpp, szp, ETHR_NATIVE_ATOMIC32_IMPL);
- str = ethr_native_atomic32_ops();
- for (i = 0; str[i]; i++) {
- erts_snprintf(buf, sizeof(buf), "ethr_native_atomic32_%s()", str[i]);
- list = erts_bld_cons(hpp, szp,
- erts_bld_string(hpp, szp, buf),
- list);
- }
-#else
- name = erts_bld_string(hpp, szp, "no");
-#endif
- tup = erts_bld_tuple(hpp, szp, 3,
- erts_bld_string(hpp, szp, "32-bit native atomics"),
- name,
- list);
- res = erts_bld_cons(hpp, szp, tup, res);
-
- if (hpp) {
- HRelease(c_p, end_hp, *hpp)
- return res;
- }
-
- hp = HAlloc(c_p, sz);
- end_hp = hp + sz;
- hpp = &hp;
- szp = NULL;
- }
-}
-
/*
* To be used to silence unused result warnings, but do not abuse it.
*/
diff --git a/erts/emulator/drivers/common/inet_drv.c b/erts/emulator/drivers/common/inet_drv.c
index 2ef452fa01..c3d2df6008 100644
--- a/erts/emulator/drivers/common/inet_drv.c
+++ b/erts/emulator/drivers/common/inet_drv.c
@@ -2574,16 +2574,18 @@ http_request_inetdrv(void* arg, const http_atom_t* meth, const char* meth_ptr,
}
static int
-http_header_inetdrv(void* arg, const http_atom_t* name, const char* name_ptr,
- int name_len, const char* value_ptr, int value_len)
+http_header_inetdrv(void* arg, const http_atom_t* name,
+ const char* name_ptr, int name_len,
+ const char* oname_ptr, int oname_len,
+ const char* value_ptr, int value_len)
{
tcp_descriptor* desc = (tcp_descriptor*) arg;
int i = 0;
- ErlDrvTermData spec[26];
+ ErlDrvTermData spec[27];
ErlDrvTermData caller = ERL_DRV_NIL;
if (desc->inet.active == INET_PASSIVE) {
- /* {inet_async,S,Ref,{ok,{http_header,Bit,Name,IValue,Value}} */
+ /* {inet_async,S,Ref,{ok,{http_header,Bit,Name,Oname,Value}} */
int req;
int aid;
@@ -2596,7 +2598,7 @@ http_header_inetdrv(void* arg, const http_atom_t* name, const char* name_ptr,
i = LOAD_ATOM(spec, i, am_ok);
}
else {
- /* {http, S, {http_header,Bit,Name,IValue,Value}} */
+ /* {http, S, {http_header,Bit,Name,Oname,Value}} */
i = LOAD_ATOM(spec, i, am_http);
i = LOAD_PORT(spec, i, desc->inet.dport);
}
@@ -2610,19 +2612,19 @@ http_header_inetdrv(void* arg, const http_atom_t* name, const char* name_ptr,
i = LOAD_INT(spec, i, 0);
i = http_load_string(desc, spec, i, name_ptr, name_len);
}
- i = LOAD_ATOM(spec, i, am_undefined);
+ i = http_load_string(desc, spec, i, oname_ptr, oname_len);
i = http_load_string(desc, spec, i, value_ptr, value_len);
i = LOAD_TUPLE(spec, i, 5);
if (desc->inet.active == INET_PASSIVE) {
i = LOAD_TUPLE(spec, i, 2);
i = LOAD_TUPLE(spec, i, 4);
- ASSERT(i <= 26);
+ ASSERT(i <= 27);
return erl_drv_send_term(desc->inet.dport, caller, spec, i);
}
else {
i = LOAD_TUPLE(spec, i, 3);
- ASSERT(i <= 26);
+ ASSERT(i <= 27);
return erl_drv_output_term(desc->inet.dport, spec, i);
}
}
diff --git a/erts/emulator/drivers/win32/win_con.c b/erts/emulator/drivers/win32/win_con.c
index 0bcccfe405..de5701d2f3 100644
--- a/erts/emulator/drivers/win32/win_con.c
+++ b/erts/emulator/drivers/win32/win_con.c
@@ -157,7 +157,7 @@ static RECT winPos;
static BOOL toolbarVisible;
static BOOL destroyed = FALSE;
-static int lines_to_save = 1000; /* Maximum number of screen lines to save. */
+static int lines_to_save = 10000; /* Maximum number of screen lines to save. */
#define TITLE_BUF_SZ 256
@@ -205,6 +205,18 @@ static void window_title(struct title_buf *);
static void free_window_title(struct title_buf *);
static void Client_OnMouseMove(HWND hwnd, int x, int y, UINT keyFlags);
+#ifdef HARDDEBUG
+/* For really hard GUI startup debugging, place DEBUGBOX() macros in code
+ and get modal message boxes with the line number. */
+static void debug_box(int line) {
+ TCHAR buff[1024];
+ swprintf(buff,1024,TEXT("DBG:%d"),line);
+ MessageBox(NULL,buff,TEXT("DBG"),MB_OK|MB_APPLMODAL);
+}
+
+#define DEBUGBOX() debug_box(__LINE__)
+#endif
+
#define CON_VPRINTF_BUF_INC_SIZE 1024
static erts_dsprintf_buf_t *
@@ -430,6 +442,13 @@ ConThreadInit(LPVOID param)
struct title_buf title;
/*DebugBreak();*/
+#ifdef HARDDEBUG
+ if(AttachConsole(ATTACH_PARENT_PROCESS) || AllocConsole()) {
+ freopen("CONOUT$", "w", stdout);
+ freopen("CONOUT$", "w", stderr);
+ }
+#endif
+
hInstance = GetModuleHandle(NULL);
StartupInfo.dwFlags = 0;
GetStartupInfo(&StartupInfo);
@@ -549,7 +568,7 @@ FrameWndProc(HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam)
case WM_CREATE:
/* client window creation */
window_title(&title);
- hClientWnd = CreateWindowEx(WS_EX_CLIENTEDGE, szClientClass, title.name,
+ hClientWnd = CreateWindowEx(0, szClientClass, title.name,
WS_CHILD|WS_VISIBLE|WS_VSCROLL|WS_HSCROLL,
CW_USEDEFAULT, CW_USEDEFAULT,
CW_USEDEFAULT, CW_USEDEFAULT,
@@ -610,7 +629,7 @@ FrameWndProc(HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam)
lpttt->hinst = hInstance;
/* check for combobox handle */
if (lpttt->uFlags&TTF_IDISHWND) {
- if ((lpttt->hdr.idFrom == (UINT) hComboWnd)) {
+ if ((lpttt->hdr.idFrom == (UINT_PTR) hComboWnd)) {
lstrcpy(lpttt->lpszText,TEXT("Command History"));
break;
}
@@ -1313,13 +1332,21 @@ LoadUserPreferences(void)
DWORD size;
DWORD res;
DWORD type;
-
+ HFONT hfont;
/* default prefs */
- GetObject(GetStockObject(SYSTEM_FIXED_FONT),sizeof(LOGFONT),(PSTR)&logfont);
+ hfont = CreateFont(0,0, 0,0, 0, FALSE,FALSE,FALSE,
+ ANSI_CHARSET, OUT_TT_ONLY_PRECIS, CLIP_DEFAULT_PRECIS,
+ CLEARTYPE_QUALITY, FIXED_PITCH, TEXT("Consolas"));
+ if(hfont) {
+ GetObject(hfont, sizeof(LOGFONT), (PSTR)&logfont);
+ DeleteObject(hfont);
+ } else {
+ GetObject(GetStockObject(SYSTEM_FIXED_FONT),sizeof(LOGFONT),(PSTR)&logfont);
+ }
fgColor = GetSysColor(COLOR_WINDOWTEXT);
bkgColor = GetSysColor(COLOR_WINDOW);
winPos.left = -1;
- toolbarVisible = TRUE;
+ toolbarVisible = FALSE;
if (RegCreateKeyEx(HKEY_CURRENT_USER, USER_KEY, 0, 0,
REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL,
@@ -1774,7 +1801,7 @@ void ConChooseColor(HWND hwnd)
SetBkColor(hdc,bkgColor);
ReleaseDC(hwnd,hdc);
hbrush = CreateSolidBrush(bkgColor);
- DeleteObject((HBRUSH)SetClassLong(hClientWnd,GCL_HBRBACKGROUND,(LONG)hbrush));
+ DeleteObject((HBRUSH)SetClassLongPtr(hClientWnd,GCL_HBRBACKGROUND,(LONG_PTR)hbrush));
InvalidateRect(hwnd,NULL,TRUE);
}
}
@@ -2012,7 +2039,7 @@ ConDrawText(HWND hwnd)
TCHAR *bu = (TCHAR *) ALLOC((num_chars+1) * sizeof(TCHAR));
memcpy(bu,buf,num_chars * sizeof(TCHAR));
bu[num_chars]='\0';
- fprintf(stderr,TEXT("ConDrawText\"%s\"\n"),bu);
+ fprintf(stderr,"ConDrawText\"%S\"\n",bu);
FREE(bu);
fflush(stderr);
}
@@ -2211,17 +2238,6 @@ static TBADDBITMAP tbbitmap =
HINST_COMMCTRL, IDB_STD_SMALL_COLOR,
};
-#ifdef HARDDEBUG
-/* For really hard GUI startup debugging, place DEBUGBOX() macros in code
- and get modal message boxes with the line number. */
-static void debug_box(int line) {
- TCHAR buff[1024];
- swprintf(buff,1024,TEXT("DBG:%d"),line);
- MessageBox(NULL,buff,TEXT("DBG"),MB_OK|MB_APPLMODAL);
-}
-
-#define DEBUGBOX() debug_box(__LINE__)
-#endif
static HWND
InitToolBar(HWND hwndParent)
@@ -2243,7 +2259,7 @@ InitToolBar(HWND hwndParent)
SendMessage(hwndTB,TB_BUTTONSTRUCTSIZE,
(WPARAM) sizeof(TBBUTTON),0);
tbbitmap.hInst = NULL;
- tbbitmap.nID = (UINT) CreateMappedBitmap(beam_module, 1,0, &colorMap, 1);
+ tbbitmap.nID = (UINT_PTR) CreateMappedBitmap(beam_module, 1,0, &colorMap, 1);
SendMessage(hwndTB, TB_ADDBITMAP, (WPARAM) 4,
(LPARAM) &tbbitmap);
@@ -2269,7 +2285,7 @@ InitToolBar(HWND hwndParent)
ti.cbSize = sizeof(TOOLINFO);
ti.uFlags = TTF_IDISHWND|TTF_CENTERTIP|TTF_SUBCLASS;
ti.hwnd = hwndTB;;
- ti.uId = (UINT)hComboWnd;
+ ti.uId = (UINT_PTR)hComboWnd;
ti.lpszText = LPSTR_TEXTCALLBACK;
hwndTT = (HWND)SendMessage(hwndTB,TB_GETTOOLTIPS,0,0);
SendMessage(hwndTT,TTM_ADDTOOL,0,(LPARAM)&ti);
diff --git a/erts/emulator/hipe/hipe_bif2.c b/erts/emulator/hipe/hipe_bif2.c
index 7e04f7d9c0..13acf2e6b3 100644
--- a/erts/emulator/hipe/hipe_bif2.c
+++ b/erts/emulator/hipe/hipe_bif2.c
@@ -193,5 +193,6 @@ BIF_RETTYPE hipe_bifs_llvm_fix_pinned_regs_0(BIF_ALIST_0)
BIF_RETTYPE hipe_bifs_build_stacktrace_1(BIF_ALIST_1)
{
+ BIF_P->ftrace = NIL;
BIF_RET(build_stacktrace(BIF_P, BIF_ARG_1));
}
diff --git a/erts/emulator/hipe/hipe_bif_list.m4 b/erts/emulator/hipe/hipe_bif_list.m4
index 7468860c37..77b5b2c275 100644
--- a/erts/emulator/hipe/hipe_bif_list.m4
+++ b/erts/emulator/hipe/hipe_bif_list.m4
@@ -290,6 +290,8 @@ noproc_primop_interface_1(nbif_atomic_inc, hipe_atomic_inc)
*/
gc_bif_interface_1(nbif_term_to_binary_1, hipe_wrapper_term_to_binary_1)
gc_bif_interface_2(nbif_term_to_binary_2, hipe_wrapper_term_to_binary_2)
+gc_bif_interface_1(nbif_term_to_iovec_1, hipe_wrapper_term_to_iovec_1)
+gc_bif_interface_2(nbif_term_to_iovec_2, hipe_wrapper_term_to_iovec_2)
gc_bif_interface_1(nbif_binary_to_term_1, hipe_wrapper_binary_to_term_1)
gc_bif_interface_2(nbif_binary_to_term_2, hipe_wrapper_binary_to_term_2)
gc_bif_interface_1(nbif_binary_to_list_1, hipe_wrapper_binary_to_list_1)
diff --git a/erts/emulator/hipe/hipe_debug.c b/erts/emulator/hipe/hipe_debug.c
index 138e4f7da3..2e34cfac59 100644
--- a/erts/emulator/hipe/hipe_debug.c
+++ b/erts/emulator/hipe/hipe_debug.c
@@ -232,7 +232,6 @@ void hipe_print_pcb(Process *p)
U("intial.fun ", u.initial.function);
U("intial.ari ", u.initial.arity);
U("current ", current);
- P("cp ", cp);
P("i ", i);
U("catches ", catches);
U("arity ", arity);
diff --git a/erts/emulator/hipe/hipe_instrs.tab b/erts/emulator/hipe/hipe_instrs.tab
index a01baebddf..8aa8544b2a 100644
--- a/erts/emulator/hipe/hipe_instrs.tab
+++ b/erts/emulator/hipe/hipe_instrs.tab
@@ -86,15 +86,14 @@ hipe_trap.post() {
switch( c_p->def_arg_reg[3] ) {
case HIPE_MODE_SWITCH_RES_RETURN:
ASSERT(is_value(reg[0]));
- SET_I(c_p->cp);
- c_p->cp = 0;
+ $RETURN();
Goto(*I);
case HIPE_MODE_SWITCH_RES_CALL_EXPORTED:
c_p->i = c_p->hipe.u.callee_exp->addressv[erts_active_code_ix()];
/*fall through*/
case HIPE_MODE_SWITCH_RES_CALL_BEAM:
SET_I(c_p->i);
- Dispatch();
+ $DISPATCH();
case HIPE_MODE_SWITCH_RES_CALL_CLOSURE:
/* This can be used to call any function value, but currently
it's only used to call closures referring to unloaded
@@ -105,13 +104,11 @@ hipe_trap.post() {
next = call_fun(c_p, c_p->arity - 1, reg, THE_NON_VALUE);
HEAVY_SWAPIN;
if (next != NULL) {
- SET_I(next);
- Dispatchfun();
+ $DISPATCH_FUN(next);
}
goto find_func_info;
}
case HIPE_MODE_SWITCH_RES_THROW:
- c_p->cp = NULL;
I = handle_error(c_p, I, reg, NULL);
goto post_error_handling;
default:
diff --git a/erts/emulator/hipe/hipe_mode_switch.c b/erts/emulator/hipe/hipe_mode_switch.c
index 1cc25a3cf0..6795c526fa 100644
--- a/erts/emulator/hipe/hipe_mode_switch.c
+++ b/erts/emulator/hipe/hipe_mode_switch.c
@@ -202,15 +202,13 @@ hipe_push_beam_trap_frame(Process *p, Eterm reg[], unsigned arity)
p->stop -= 2;
p->stop[1] = hipe_beam_catch_throw;
}
- p->stop[0] = make_cp(p->cp);
+ p->stop[0] = (BeamInstr) hipe_beam_pc_return;
++p->catches;
- p->cp = hipe_beam_pc_return;
}
static __inline__ void hipe_pop_beam_trap_frame(Process *p)
{
ASSERT(p->stop[1] == hipe_beam_catch_throw);
- p->cp = cp_val(p->stop[0]);
--p->catches;
p->stop += 2;
}
@@ -263,7 +261,7 @@ Process *hipe_mode_switch(Process *p, unsigned cmd, Eterm reg[])
unsigned arity = cmd >> 8;
/* p->hipe.u.ncallee set in beam_emu */
- if (p->cp == hipe_beam_pc_return) {
+ if (cp_val(p->stop[0]) == hipe_beam_pc_return) {
/* Native called BEAM, which now tailcalls native. */
hipe_pop_beam_trap_frame(p);
result = hipe_tailcall_to_native(p, arity, reg);
@@ -292,7 +290,7 @@ Process *hipe_mode_switch(Process *p, unsigned cmd, Eterm reg[])
/* just like a normal call from now on */
/* p->hipe.u.ncallee set in beam_emu */
- if (p->cp == hipe_beam_pc_return) {
+ if (cp_val(p->stop[0]) == hipe_beam_pc_return) {
/* Native called BEAM, which now tailcalls native. */
hipe_pop_beam_trap_frame(p);
result = hipe_tailcall_to_native(p, arity, reg);
diff --git a/erts/emulator/internal_doc/AutomaticYieldingOfCCode.md b/erts/emulator/internal_doc/AutomaticYieldingOfCCode.md
new file mode 100644
index 0000000000..e68a57a7ab
--- /dev/null
+++ b/erts/emulator/internal_doc/AutomaticYieldingOfCCode.md
@@ -0,0 +1,62 @@
+Automatic Yielding of C Code
+============================
+
+Introduction
+------------
+
+Erlang [NIFs](http://erlang.org/doc/tutorial/nif.html) and
+[BIFs](http://erlang.org/pipermail/erlang-questions/2009-October/046899.html)
+should not run for a too long time without yielding (often referred to
+as trapping in the source code of ERTS). The Erlang/OTP system gets
+unresponsive, and some task may get prioritized unfairly if NIFs and
+BIFs occupy scheduler threads for a too long time. Therefore, the most
+commonly used NIFs and BIFs that may run for a long time can yield.
+
+Problems
+--------
+
+Erlang NIFs and BIFs are typically implemented in the C programming
+language. The C programming language does not have built-in support
+for automatic yielding in the middle of a routine (referred to as
+[coroutine support](https://en.wikipedia.org/wiki/Coroutine) in other
+programming languages). Therefore, most NIFs and BIFs implement
+yielding manually. Manual implementation of yielding has the advantage
+of giving the programmer control over what should be saved and when
+yielding should happen. Unfortunately, manual implementation of
+yielding also leads to code with a lot of boilerplate that is more
+difficult to read than corresponding code that does not
+yield. Furthermore, manual implementation of yielding can be
+time-consuming and error-prone, especially if the NIF or BIF is
+complicated.
+
+Solution
+--------
+
+A source-to-source transformer, called Yielding C Fun (YCF), has been
+created to make it easier to implement yielding NIFs and BIFs. YCF is
+a tool that takes a set of function names and a C source code file and
+transforms the functions with the given names in the source code file
+into yieldable versions that can be used as coroutines. YCF has been
+created with yielding NIFs and BIFs in mind and has several features
+that can be handy when implementing yielding NIFs and BIFs. The reader
+is recommended to look at YCF's documentation for a detailed
+description of YCF.
+
+Yielding C Fun's Source Code and Documentation
+----------------------------------------------
+
+The source code of YCF is included in the folder
+`"$ERL_TOP"/erts/lib_src/yielding_c_fun/` inside the source tree of
+the Erlang/OTP system. The documentation of YCF can be found in
+`"$ERL_TOP"/erts/lib_src/yielding_c_fun/README.md`. A rendered version
+of YCF documentation can be found
+[here](https://github.com/erlang/otp/erts/lib_src/yielding_c_fun/README.md).
+
+Yielding C Fun in the Erlang Run-time System
+-------------------------------------------
+
+YCF is used to implement yielding in the BIFs for the `ets:insert/2`
+and `ets:insert_new/2` functions when these two functions get a list
+as their second parameter. The implementation of these two functions
+is a good starting point if one wants to use YCF to implement
+something else inside ERTS.
diff --git a/erts/emulator/internal_doc/beam_makeops.md b/erts/emulator/internal_doc/beam_makeops.md
index 2880099b70..1ec94d4ff9 100644
--- a/erts/emulator/internal_doc/beam_makeops.md
+++ b/erts/emulator/internal_doc/beam_makeops.md
@@ -1087,12 +1087,14 @@ use as a temporary X register.
* `y` - Y register. The default value is 0.
-* `l` - Foating point register number. The default value is 0.
+* `l` - Floating point register number. The default value is 0.
* `i` - Tagged literal integer. The default value is 0.
* `a` - Tagged atom. The default value is the empty atom (`am_Empty`).
+* `p` - Zero failure label.
+
* `n` - NIL (`[]`, the empty list).
#### Function call on the right side ####
@@ -1457,26 +1459,26 @@ all instructions. It expands to the address of the next instruction.
Here is an example:
i_call(CallDest) {
- SET_CP(c_p, $NEXT_INSTRUCTION);
+ //| -no_next
+ $SAVE_CONTINUATION_POINTER($NEXT_INSTRUCTION);
$DISPATCH_REL($CallDest);
}
-When calling a function, the return address is first stored in `c_p->cp`
-(using the `SET_CP()` macro defined in `beam_emu.c`), and then control is
+When calling a function, the return address is first stored in `E[0]`
+(using the `$SAVE_CONTINUATION_POINTER()` macro), and then control is
transferred to the callee. Here is the generated code:
OpCase(i_call_f):
{
- SET_CP(c_p, I+1);
- ASSERT(VALID_INSTR(*(I + (fb(BeamExtraData(I[0]))) + 0)));
- I += fb(BeamExtraData(I[0])) + 0;;
- DTRACE_LOCAL_CALL(c_p, erts_code_to_codemfa(I));
- Dispatch();;
+ ASSERT(VALID_INSTR(*(I+2)));
+ *E = (BeamInstr) (I+2);;
+
+ /* ... dispatch code intentionally left out ... */
}
-We can see that that `$NEXT_INSTRUCTION` has been expanded to `I+1`.
+We can see that that `$NEXT_INSTRUCTION` has been expanded to `I+2`.
That makes sense since the size of the `i_call_f/1` instruction is
-one word.
+two words.
##### The IP_ADJUSTMENT pre-bound variable #####
diff --git a/erts/emulator/nifs/common/prim_file_nif.c b/erts/emulator/nifs/common/prim_file_nif.c
index 3df04e42e2..5c5e9a2d30 100644
--- a/erts/emulator/nifs/common/prim_file_nif.c
+++ b/erts/emulator/nifs/common/prim_file_nif.c
@@ -162,6 +162,7 @@ WRAP_FILE_HANDLE_EXPORT(allocate_nif)
WRAP_FILE_HANDLE_EXPORT(advise_nif)
WRAP_FILE_HANDLE_EXPORT(get_handle_nif)
WRAP_FILE_HANDLE_EXPORT(ipread_s32bu_p32bu_nif)
+WRAP_FILE_HANDLE_EXPORT(read_handle_info_nif)
static ErlNifFunc nif_funcs[] = {
/* File handle ops */
@@ -176,6 +177,7 @@ static ErlNifFunc nif_funcs[] = {
{"truncate_nif", 1, truncate_nif, ERL_NIF_DIRTY_JOB_IO_BOUND},
{"allocate_nif", 3, allocate_nif, ERL_NIF_DIRTY_JOB_IO_BOUND},
{"advise_nif", 4, advise_nif, ERL_NIF_DIRTY_JOB_IO_BOUND},
+ {"read_handle_info_nif", 1, read_handle_info_nif, ERL_NIF_DIRTY_JOB_IO_BOUND},
/* Filesystem ops */
{"make_hard_link_nif", 2, make_hard_link_nif, ERL_NIF_DIRTY_JOB_IO_BOUND},
@@ -231,6 +233,7 @@ static int load(ErlNifEnv *env, void** priv_data, ERL_NIF_TERM prim_file_pid)
am_append = enif_make_atom(env, "append");
am_sync = enif_make_atom(env, "sync");
am_skip_type_check = enif_make_atom(env, "skip_type_check");
+ am_directory = enif_make_atom(env, "directory");
am_read_write = enif_make_atom(env, "read_write");
am_none = enif_make_atom(env, "none");
@@ -447,6 +450,8 @@ static enum efile_modes_t efile_translate_modelist(ErlNifEnv *env, ERL_NIF_TERM
modes |= EFILE_MODE_SYNC;
} else if(enif_is_identical(head, am_skip_type_check)) {
modes |= EFILE_MODE_SKIP_TYPE_CHECK;
+ } else if (enif_is_identical(head, am_directory)) {
+ modes |= EFILE_MODE_DIRECTORY;
} else {
/* Modes like 'raw', 'ram', 'delayed_writes' etc are handled
* further up the chain. */
@@ -893,6 +898,26 @@ static ERL_NIF_TERM get_handle_nif_impl(efile_data_t *d, ErlNifEnv *env, int arg
return efile_get_handle(env, d);
}
+static ERL_NIF_TERM build_file_info(ErlNifEnv *env, efile_fileinfo_t *info) {
+ /* #file_info as declared in file.hrl */
+ return enif_make_tuple(env, 14,
+ am_file_info,
+ enif_make_uint64(env, info->size),
+ efile_filetype_to_atom(info->type),
+ efile_access_to_atom(info->access),
+ enif_make_int64(env, MAX(EFILE_MIN_FILETIME, info->a_time)),
+ enif_make_int64(env, MAX(EFILE_MIN_FILETIME, info->m_time)),
+ enif_make_int64(env, MAX(EFILE_MIN_FILETIME, info->c_time)),
+ enif_make_uint(env, info->mode),
+ enif_make_uint(env, info->links),
+ enif_make_uint(env, info->major_device),
+ enif_make_uint(env, info->minor_device),
+ enif_make_uint(env, info->inode),
+ enif_make_uint(env, info->uid),
+ enif_make_uint(env, info->gid)
+ );
+}
+
static ERL_NIF_TERM read_info_nif(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) {
posix_errno_t posix_errno;
@@ -911,23 +936,20 @@ static ERL_NIF_TERM read_info_nif(ErlNifEnv *env, int argc, const ERL_NIF_TERM a
return posix_error_to_tuple(env, posix_errno);
}
- /* #file_info as declared in file.hrl */
- return enif_make_tuple(env, 14,
- am_file_info,
- enif_make_uint64(env, info.size),
- efile_filetype_to_atom(info.type),
- efile_access_to_atom(info.access),
- enif_make_int64(env, MAX(EFILE_MIN_FILETIME, info.a_time)),
- enif_make_int64(env, MAX(EFILE_MIN_FILETIME, info.m_time)),
- enif_make_int64(env, MAX(EFILE_MIN_FILETIME, info.c_time)),
- enif_make_uint(env, info.mode),
- enif_make_uint(env, info.links),
- enif_make_uint(env, info.major_device),
- enif_make_uint(env, info.minor_device),
- enif_make_uint(env, info.inode),
- enif_make_uint(env, info.uid),
- enif_make_uint(env, info.gid)
- );
+ return build_file_info(env, &info);
+}
+
+static ERL_NIF_TERM read_handle_info_nif_impl(efile_data_t *d, ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) {
+ posix_errno_t posix_errno;
+ efile_fileinfo_t info = {0};
+
+ ASSERT(argc == 0);
+
+ if((posix_errno = efile_read_handle_info(d, &info))) {
+ return posix_error_to_tuple(env, posix_errno);
+ }
+
+ return build_file_info(env, &info);
}
static ERL_NIF_TERM set_permissions_nif(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) {
diff --git a/erts/emulator/nifs/common/prim_file_nif.h b/erts/emulator/nifs/common/prim_file_nif.h
index b2e30c59dd..1cf5d52192 100644
--- a/erts/emulator/nifs/common/prim_file_nif.h
+++ b/erts/emulator/nifs/common/prim_file_nif.h
@@ -30,6 +30,8 @@ enum efile_modes_t {
EFILE_MODE_SKIP_TYPE_CHECK = (1 << 5), /* Special for device files on Unix. */
EFILE_MODE_NO_TRUNCATE = (1 << 6), /* Special for reopening on VxWorks. */
+ EFILE_MODE_DIRECTORY = (1 << 7),
+
EFILE_MODE_READ_WRITE = EFILE_MODE_READ | EFILE_MODE_WRITE
};
@@ -110,7 +112,7 @@ typedef struct {
typedef ErlNifBinary efile_path_t;
-/* @brief Translates the given "raw name" into the format expected by the APIs
+/** @brief Translates the given "raw name" into the format expected by the APIs
* used by the underlying implementation. The result is transient and does not
* need to be released.
*
@@ -121,30 +123,30 @@ typedef ErlNifBinary efile_path_t;
* prim_file:internal_native2name for compatibility reasons. */
posix_errno_t efile_marshal_path(ErlNifEnv *env, ERL_NIF_TERM path, efile_path_t *result);
-/* @brief Returns the underlying handle as an implementation-defined term.
+/** @brief Returns the underlying handle as an implementation-defined term.
*
* This is an internal function intended to support tests and tricky
* operations like sendfile(2). */
ERL_NIF_TERM efile_get_handle(ErlNifEnv *env, efile_data_t *d);
-/* @brief Read until EOF or the given iovec has been filled.
+/** @brief Read until EOF or the given iovec has been filled.
*
* @return -1 on failure, or the number of bytes read on success. The return
* value will be 0 if no bytes could be read before EOF or the end of the
* iovec. */
Sint64 efile_readv(efile_data_t *d, SysIOVec *iov, int iovlen);
-/* @brief Write the entirety of the given iovec.
+/** @brief Write the entirety of the given iovec.
*
* @return -1 on failure, or the number of bytes written on success. "Partial"
* failures will be reported with -1 and not the number of bytes we managed to
* write to disk before the failure. */
Sint64 efile_writev(efile_data_t *d, SysIOVec *iov, int iovlen);
-/* @brief As \c efile_readv, but starting from a file offset. */
+/** @brief As \c efile_readv, but starting from a file offset. */
Sint64 efile_preadv(efile_data_t *d, Sint64 offset, SysIOVec *iov, int iovlen);
-/* @brief As \c efile_writev, but starting from a file offset. */
+/** @brief As \c efile_writev, but starting from a file offset. */
Sint64 efile_pwritev(efile_data_t *d, Sint64 offset, SysIOVec *iov, int iovlen);
int efile_seek(efile_data_t *d, enum efile_seek_t seek, Sint64 offset, Sint64 *new_position);
@@ -168,6 +170,7 @@ int efile_close(efile_data_t *d, posix_errno_t *error);
/* **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** */
posix_errno_t efile_read_info(const efile_path_t *path, int follow_link, efile_fileinfo_t *result);
+posix_errno_t efile_read_handle_info(efile_data_t *d, efile_fileinfo_t *result);
/** @brief Sets the file times to the given values. Refer to efile_fileinfo_t
* for a description of each. */
diff --git a/erts/emulator/nifs/common/prim_net_nif.c b/erts/emulator/nifs/common/prim_net_nif.c
index 1fbd3e2366..28ece878c2 100644
--- a/erts/emulator/nifs/common/prim_net_nif.c
+++ b/erts/emulator/nifs/common/prim_net_nif.c
@@ -65,6 +65,14 @@
#ifdef HAVE_IFADDRS_H
#include <ifaddrs.h>
+#elif defined(__PASE__)
+/* PASE has this, but under a different name because AIX doesn't have it. */
+#include <as400_protos.h>
+/*
+ * We don't redefine the function names because they're used in other
+ * contexts, but the struct is safe to rename.
+ */
+#define ifaddrs ifaddrs_pase
#endif
#ifdef HAVE_NETPACKET_PACKET_H
@@ -1030,7 +1038,7 @@ ERL_NIF_TERM nif_getifaddrs(ErlNifEnv* env,
{
#if defined(__WIN32__)
return enif_raise_exception(env, MKA(env, "notsup"));
-#elif defined(HAVE_GETIFADDRS)
+#elif defined(HAVE_GETIFADDRS) || defined(__PASE__)
ERL_NIF_TERM extra;
char* netns;
ERL_NIF_TERM result;
@@ -1066,7 +1074,7 @@ ERL_NIF_TERM nif_getifaddrs(ErlNifEnv* env,
}
-#ifdef HAVE_GETIFADDRS
+#if defined(HAVE_GETIFADDRS) || defined(__PASE__)
#ifdef HAVE_SETNS
/* enet_getifaddrs_netns - extract the netns field from the 'extra' map
*
@@ -1146,9 +1154,17 @@ ERL_NIF_TERM enet_getifaddrs(ErlNifEnv* env, char* netns)
return esock_make_error_errno(env, save_errno);
#endif
+#ifdef __PASE__
+ if (0 == Qp2getifaddrs(&ifap)) {
+#else
if (0 == getifaddrs(&ifap)) {
+#endif
result = enet_getifaddrs_process(env, ifap);
+#ifdef __PASE__
+ Qp2freeifaddrs(ifap);
+#else
freeifaddrs(ifap);
+#endif
} else {
save_errno = get_errno();
@@ -1676,7 +1692,7 @@ ERL_NIF_TERM nif_if_names(ErlNifEnv* env,
int argc,
const ERL_NIF_TERM argv[])
{
-#if defined(__WIN32__)
+#if defined(__WIN32__) || (defined(__ANDROID__) && (__ANDROID_API__ < 24))
return enif_raise_exception(env, MKA(env, "notsup"));
#else
ERL_NIF_TERM result;
@@ -1697,7 +1713,10 @@ ERL_NIF_TERM nif_if_names(ErlNifEnv* env,
-#if !defined(__WIN32__)
+/* if_nameindex and if_freenameindex were added in Android 7.0 Nougat. With
+the Android NDK Unified Headers, check that the build is targeting at least
+the corresponding API level 24. */
+#if !defined(__WIN32__) && !(defined(__ANDROID__) && (__ANDROID_API__ < 24))
static
ERL_NIF_TERM enet_if_names(ErlNifEnv* env)
{
diff --git a/erts/emulator/nifs/common/socket_nif.c b/erts/emulator/nifs/common/socket_nif.c
index ab95748458..a775889f26 100644
--- a/erts/emulator/nifs/common/socket_nif.c
+++ b/erts/emulator/nifs/common/socket_nif.c
@@ -690,9 +690,14 @@ typedef union {
#define ESOCK_SUPPORTS_LOCAL 0x0004
#define ESOCK_SUPPORTS_SEND_FLAGS 0x0005
#define ESOCK_SUPPORTS_RECV_FLAGS 0x0006
+#define ESOCK_SUPPORTS_NETNS 0x0007
-#define ESOCK_WHICH_PROTO_ERROR -1
-#define ESOCK_WHICH_PROTO_UNSUP -2
+#define ESOCK_WHICH_DOMAIN_ERROR -1
+#define ESOCK_WHICH_DOMAIN_UNSUP -2
+#define ESOCK_WHICH_TYPE_ERROR -1
+#define ESOCK_WHICH_TYPE_UNSUP -2
+#define ESOCK_WHICH_PROTO_ERROR -1
+#define ESOCK_WHICH_PROTO_UNSUP -2
/* =================================================================== *
@@ -702,9 +707,10 @@ typedef union {
* =================================================================== */
/* Global socket debug */
-#define SGDBG( proto ) ESOCK_DBG_PRINTF( data.dbg , proto )
+#define SGDBG( proto ) ESOCK_DBG_PRINTF( data.dbg , proto )
+#define SGDBG2( __DBG__ , proto ) ESOCK_DBG_PRINTF( __DBG__ || data.dbg , proto )
/* Socket specific debug */
-#define SSDBG( __D__ , proto ) ESOCK_DBG_PRINTF( (__D__)->dbg , proto )
+#define SSDBG( __D__ , proto ) ESOCK_DBG_PRINTF( (__D__)->dbg , proto )
#define ESOCK_CNT_INC( __E__, __D__, SF, ACNT, CNT, INC) \
{ \
@@ -902,6 +908,8 @@ typedef struct {
/* +++ The actual socket +++ */
SOCKET sock;
HANDLE event;
+ SOCKET origFD; // A 'socket' created from this FD
+ BOOLEAN_T closeOnClose; // Have we dup'ed or not
/* +++ Stuff "about" the socket +++ */
int domain;
@@ -1134,6 +1142,8 @@ static ERL_NIF_TERM esock_socket_info_protocol(ErlNifEnv* env,
ESockDescriptor* descP);
static ERL_NIF_TERM esock_socket_info_counters(ErlNifEnv* env,
ESockDescriptor* descP);
+static ERL_NIF_TERM esock_socket_info_ctype(ErlNifEnv* env,
+ ESockDescriptor* descP);
#define ESOCK_SOCKET_INFO_REQ_FUNCS \
ESOCK_SOCKET_INFO_REQ_FUNC_DEF(readers); \
ESOCK_SOCKET_INFO_REQ_FUNC_DEF(writers); \
@@ -1162,14 +1172,33 @@ static ERL_NIF_TERM esock_supports_options_sctp(ErlNifEnv* env);
static ERL_NIF_TERM esock_supports_sctp(ErlNifEnv* env);
static ERL_NIF_TERM esock_supports_ipv6(ErlNifEnv* env);
static ERL_NIF_TERM esock_supports_local(ErlNifEnv* env);
+static ERL_NIF_TERM esock_supports_netns(ErlNifEnv* env);
static ERL_NIF_TERM esock_supports_send_flags(ErlNifEnv* env);
static ERL_NIF_TERM esock_supports_recv_flags(ErlNifEnv* env);
-static ERL_NIF_TERM esock_open(ErlNifEnv* env,
- int domain,
- int type,
- int protocol,
- char* netns);
+static ERL_NIF_TERM esock_open2(ErlNifEnv* env,
+ int fd,
+ ERL_NIF_TERM eextra);
+static BOOLEAN_T esock_open2_is_debug(ErlNifEnv* env,
+ ERL_NIF_TERM eextra);
+static BOOLEAN_T esock_open2_todup(ErlNifEnv* env,
+ ERL_NIF_TERM eextra);
+static BOOLEAN_T esock_open2_get_domain(ErlNifEnv* env,
+ ERL_NIF_TERM eopts,
+ int* domain);
+static BOOLEAN_T esock_open2_get_type(ErlNifEnv* env,
+ ERL_NIF_TERM eopt,
+ int* type);
+static BOOLEAN_T esock_open2_get_protocol(ErlNifEnv* env,
+ ERL_NIF_TERM eopts,
+ int* protocol);
+static ERL_NIF_TERM esock_open4(ErlNifEnv* env,
+ int domain,
+ int type,
+ int protocol,
+ ERL_NIF_TERM eopts);
+static BOOLEAN_T esock_open_which_domain(SOCKET sock, int* domain);
+static BOOLEAN_T esock_open_which_type(SOCKET sock, int* type);
static BOOLEAN_T esock_open_which_protocol(SOCKET sock, int* proto);
static ERL_NIF_TERM esock_bind(ErlNifEnv* env,
@@ -2618,7 +2647,9 @@ static BOOLEAN_T elevel2level(BOOLEAN_T isEncoded,
BOOLEAN_T* isOTP,
int* level);
#ifdef HAVE_SETNS
-static BOOLEAN_T emap2netns(ErlNifEnv* env, ERL_NIF_TERM map, char** netns);
+static BOOLEAN_T esock_open4_get_netns(ErlNifEnv* env,
+ ERL_NIF_TERM opts,
+ char** netns);
static BOOLEAN_T change_network_namespace(char* netns, int* cns, int* err);
static BOOLEAN_T restore_network_namespace(int ns, SOCKET sock, int* err);
#endif
@@ -3123,6 +3154,7 @@ ERL_NIF_TERM esock_atom_socket_tag; // This has a "special" name ('$socket')
LOCAL_ATOM_DECL(cookie_life); \
LOCAL_ATOM_DECL(counter_wrap); \
LOCAL_ATOM_DECL(counters); \
+ LOCAL_ATOM_DECL(ctype); \
LOCAL_ATOM_DECL(data_in); \
LOCAL_ATOM_DECL(del); \
LOCAL_ATOM_DECL(dest_unreach); \
@@ -3147,6 +3179,7 @@ ERL_NIF_TERM esock_atom_socket_tag; // This has a "special" name ('$socket')
LOCAL_ATOM_DECL(max_instreams); \
LOCAL_ATOM_DECL(max_rxt); \
LOCAL_ATOM_DECL(min); \
+ LOCAL_ATOM_DECL(missing); \
LOCAL_ATOM_DECL(mode); \
LOCAL_ATOM_DECL(multiaddr); \
LOCAL_ATOM_DECL(net_unknown); \
@@ -3277,6 +3310,7 @@ static ESOCK_INLINE ErlNifEnv* esock_alloc_env(const char* slogan)
*
* The "proper" socket functions:
* ------------------------------
+ * nif_open(FD, Extra)
* nif_open(Domain, Type, Protocol, Extra)
* nif_bind(Sock, LocalAddr)
* nif_connect(Sock, SockAddr)
@@ -3436,6 +3470,7 @@ ERL_NIF_TERM esock_socket_info(ErlNifEnv* env,
ERL_NIF_TERM type = esock_socket_info_type(env, descP);
ERL_NIF_TERM protocol = esock_socket_info_protocol(env, descP);
ERL_NIF_TERM ctrlPid = MKPID(env, &descP->ctrlPid);
+ ERL_NIF_TERM ctype = esock_socket_info_ctype(env, descP);
ERL_NIF_TERM readable = BOOL2ATOM(descP->isReadable);
ERL_NIF_TERM writable = BOOL2ATOM(descP->isWritable);
// ERL_NIF_TERM connected = BOOL2ATOM(descP->isConnected);
@@ -3447,6 +3482,7 @@ ERL_NIF_TERM esock_socket_info(ErlNifEnv* env,
esock_atom_type,
esock_atom_protocol,
esock_atom_ctrl,
+ atom_ctype,
atom_readable,
atom_writable,
atom_counters,
@@ -3457,6 +3493,7 @@ ERL_NIF_TERM esock_socket_info(ErlNifEnv* env,
type,
protocol,
ctrlPid,
+ ctype,
readable,
writable,
counters,
@@ -3541,6 +3578,45 @@ ERL_NIF_TERM esock_socket_info_protocol(ErlNifEnv* env,
/*
+ * Encode the socket "create type"
+ * That is "show" how this socket was created:
+ *
+ * normal | fromfd | {fromfd, integer()}
+ */
+static
+ERL_NIF_TERM esock_socket_info_ctype(ErlNifEnv* env,
+ ESockDescriptor* descP)
+{
+ ERL_NIF_TERM ctype;
+
+ SSDBG( descP, ("SOCKET", "esock_socket_info_ctype -> entry with"
+ "\r\n origFD: %d"
+ "\r\n closeOnClose: %s"
+ "\r\n", descP->origFD, B2S(descP->closeOnClose)) );
+
+ if (descP->origFD > 0) {
+ /* Created from other FD */
+ if (descP->closeOnClose) {
+ /* We *have* dup'ed: {fromfd, integer()} */
+ ctype = MKT2(env, MKA(env, "fromfd"), MKI(env, descP->origFD));
+ } else {
+ /* We have *not* dup'ed: fromfd */
+ ctype = MKA(env, "fromfd");
+ }
+ } else {
+ /* Normal socket */
+ ctype = MKA(env, "normal");
+ }
+
+ SSDBG( descP, ("SOCKET", "esock_socket_info_ctype -> done:"
+ "\r\n ctype: %T"
+ "\r\n", ctype) );
+
+ return ctype;
+}
+
+
+/*
* Collect all counters for a socket.
*/
static
@@ -3789,6 +3865,9 @@ ERL_NIF_TERM socket_info_reqs(ErlNifEnv* env,
* sctp boolean()
* ipv6 boolean()
* local boolean()
+ * netns boolean()
+ * send_flags [{SendFlag, boolean()}]
+ * recv_flags [{RecvFlag, boolean()}]
*/
static
@@ -3846,6 +3925,10 @@ ERL_NIF_TERM esock_supports(ErlNifEnv* env, int key)
result = esock_supports_local(env);
break;
+ case ESOCK_SUPPORTS_NETNS:
+ result = esock_supports_netns(env);
+ break;
+
case ESOCK_SUPPORTS_SEND_FLAGS:
result = esock_supports_send_flags(env);
break;
@@ -5127,6 +5210,24 @@ ERL_NIF_TERM esock_supports_local(ErlNifEnv* env)
#if !defined(__WIN32__)
static
+ERL_NIF_TERM esock_supports_netns(ErlNifEnv* env)
+{
+ ERL_NIF_TERM supports;
+
+#if defined(HAVE_SETNS)
+ supports = esock_atom_true;
+#else
+ supports = esock_atom_false;
+#endif
+
+ return supports;
+}
+#endif
+
+
+
+#if !defined(__WIN32__)
+static
ERL_NIF_TERM esock_supports_send_flags(ErlNifEnv* env)
{
SocketTArray sflags = TARRAY_CREATE(8);
@@ -5265,16 +5366,35 @@ ERL_NIF_TERM esock_supports_recv_flags(ErlNifEnv* env)
*
* Description:
* Create an endpoint for communication.
- *
- * Arguments:
+ * This function "exist" in two variants.
+ * One with two args and onewith four.
+ *
+ * Arguments (2):
+ * FD - File Descriptor (of an already open socket).
+ * Extra - A map with extra options.
+ * The options are:
+ * [M] dup - boolean() - Shall the fd be dup'ed or not.
+ * [O] bound - boolean() - Is the fd already bound.
+ * [O] domain - domain() - We may not be able to retrieve
+ * this on all platforms, and in
+ * *those* cases this *must* be
+ * provided.
+ * [O] type - type() - We may not be able to retrieve
+ * this on all platforms, and in
+ * *those* cases this *must* be
+ * provided.
+ * [O] proto - protocol() - We may not be able to retrieve
+ * this on all platforms, and in
+ * *those* cases this *must* be
+ * provided.
+ * Arguments (4):
* Domain - The domain, for example 'inet'
* Type - Type of socket, for example 'stream'
* Protocol - The protocol, for example 'tcp'
* Extra - A map with "obscure" options.
* Currently the only allowed option is netns (network namespace).
* This is *only* allowed on linux!
- * We should also use this for the fd value, in case we should use
- * an already existing (file) descriptor.
+ *
*/
static
ERL_NIF_TERM nif_open(ErlNifEnv* env,
@@ -5284,72 +5404,363 @@ ERL_NIF_TERM nif_open(ErlNifEnv* env,
#if defined(__WIN32__)
return enif_raise_exception(env, MKA(env, "notsup"));
#else
- int edomain, etype;
- ERL_NIF_TERM eproto; // This is normally an int, but can also be '{raw, int}'
- ERL_NIF_TERM emap;
- int domain, type, proto;
- char* netns;
ERL_NIF_TERM result;
- SGDBG( ("SOCKET", "nif_open -> entry with %d args\r\n", argc) );
-
- /* Extract arguments and perform preliminary validation */
+ SGDBG( ("SOCKET", "nif_open -> "
+ "\r\n argc: %d"
+ "\r\n", argc) );
- if ((argc != 4) ||
- !GET_INT(env, argv[0], &edomain) ||
- !GET_INT(env, argv[1], &etype) ||
- !IS_MAP(env, argv[3])) {
- return enif_make_badarg(env);
- }
- eproto = argv[2];
- emap = argv[3];
+ switch (argc) {
+ case 2:
+ /* The FD-version */
+ {
+ int fd;
+ ERL_NIF_TERM eopts;
- SGDBG( ("SOCKET", "nif_open -> "
- "\r\n edomain: %T"
- "\r\n etype: %T"
- "\r\n eproto: %T"
- "\r\n extra: %T"
- "\r\n", argv[0], argv[1], eproto, emap) );
-
- if (!edomain2domain(edomain, &domain)) {
- SGDBG( ("SOCKET", "nif_open -> invalid domain: %d\r\n", edomain) );
- return esock_make_error(env, esock_atom_einval);
+ if (!GET_INT(env, argv[0], &fd) ||
+ !IS_MAP(env, argv[1])) {
+ return enif_make_badarg(env);
+ }
+ eopts = argv[1];
+
+ SGDBG( ("SOCKET", "nif_open -> "
+ "\r\n FD: %d"
+ "\r\n eopts: %T"
+ "\r\n", fd, eopts) );
+
+ result = esock_open2(env, fd, eopts);
+ }
+ break;
+
+ case 4:
+ /* The normal version */
+ {
+ int edomain, etype;
+ ERL_NIF_TERM eproto; // integer() (normal case) | {raw, integer()}
+ ERL_NIF_TERM eopts;
+ int domain, type, proto;
+
+ /* Extract arguments and perform preliminary validation */
+
+ if (!GET_INT(env, argv[0], &edomain) ||
+ !GET_INT(env, argv[1], &etype) ||
+ !IS_MAP(env, argv[3])) {
+ return enif_make_badarg(env);
+ }
+ eproto = argv[2];
+ eopts = argv[3];
+
+ SGDBG( ("SOCKET", "nif_open -> "
+ "\r\n edomain: %T"
+ "\r\n etype: %T"
+ "\r\n eproto: %T"
+ "\r\n eopts: %T"
+ "\r\n", argv[0], argv[1], eproto, eopts) );
+
+ if (!edomain2domain(edomain, &domain)) {
+ SGDBG( ("SOCKET", "nif_open -> invalid domain: %d\r\n", edomain) );
+ return esock_make_error(env, esock_atom_einval);
+ }
+
+ if (!etype2type(etype, &type)) {
+ SGDBG( ("SOCKET", "nif_open -> invalid type: %d\r\n", etype) );
+ return esock_make_error(env, esock_atom_einval);
+ }
+
+ if (!eproto2proto(env, eproto, &proto)) {
+ SGDBG( ("SOCKET", "nif_open -> invalid protocol: %d\r\n", eproto) );
+ return esock_make_error(env, esock_atom_einval);
+ }
+
+ result = esock_open4(env, domain, type, proto, eopts);
+
+ }
+ break;
+
+ default:
+ SGDBG( ("SOCKET", "nif_open -> invalid number of arguments: %d"
+ "\r\n", argc) );
+ result = enif_make_badarg(env);
+ break;
}
- if (!etype2type(etype, &type)) {
- SGDBG( ("SOCKET", "nif_open -> invalid type: %d\r\n", etype) );
- return esock_make_error(env, esock_atom_einval);
+ SGDBG( ("SOCKET", "nif_open -> done with result: "
+ "\r\n %T"
+ "\r\n", result) );
+
+ return result;
+
+#endif // if defined(__WIN32__)
+}
+
+
+/* esock_open - create an endpoint (from an existing fd) for communication
+ *
+ * Assumes the input has been validated.
+ *
+ * Normally we want debugging on (individual) sockets to be controlled
+ * by the sockets own debug flag. But since we don't even have a socket
+ * yet, we must use the global debug flag.
+ */
+#if !defined(__WIN32__)
+static
+ERL_NIF_TERM esock_open2(ErlNifEnv* env,
+ int fd,
+ ERL_NIF_TERM eopts)
+{
+ BOOLEAN_T dbg = esock_open2_is_debug(env, eopts);
+ ESockDescriptor* descP;
+ ERL_NIF_TERM res, reason;
+ int domain, type, protocol;
+ int save_errno = 0;
+ BOOLEAN_T closeOnClose;
+ SOCKET sock;
+ HANDLE event;
+
+ SGDBG2( dbg,
+ ("SOCKET", "esock_open2 -> entry with"
+ "\r\n fd: %d"
+ "\r\n eopts: %T"
+ "\r\n", fd, eopts) );
+
+ /*
+ * Before we do anything else, we try to retrieve domain, type and protocol
+ * This information is either present in the eextra map or if not we need
+ * to "get" it from the system (getsockopt).
+ * Note that its not possible to get all of these on all platoforms,
+ * and in those cases the user *must* provide us with them (eextra).
+ *
+ * We try the system first (since its more reliable) and if that fails
+ * we check the eextra map. If neither one works, we *give up*!
+ */
+
+ if (!esock_open_which_domain(fd, &domain)) {
+ SGDBG2( dbg,
+ ("SOCKET",
+ "esock_open2 -> failed get domain from system\r\n") );
+ if (!esock_open2_get_domain(env, eopts, &domain)) {
+ reason = MKT2(env, atom_missing, esock_atom_domain);
+ return esock_make_error(env, reason);
+ }
+ }
+
+ if (!esock_open_which_type(fd, &type)) {
+ SGDBG2( dbg,
+ ("SOCKET", "esock_open2 -> failed get type from system\r\n") );
+ if (!esock_open2_get_type(env, eopts, &type)) {
+ reason = MKT2(env, atom_missing, esock_atom_type);
+ return esock_make_error(env, reason);
+
+ }
+ }
+
+ if (!esock_open_which_protocol(fd, &protocol)) {
+ SGDBG2( dbg,
+ ("SOCKET", "esock_open2 -> failed get protocol from system\r\n") );
+ if (!esock_open2_get_protocol(env, eopts, &protocol)) {
+ reason = MKT2(env, atom_missing, esock_atom_protocol);
+ return esock_make_error(env, reason);
+ }
}
- if (!eproto2proto(env, eproto, &proto)) {
- SGDBG( ("SOCKET", "nif_open -> invalid protocol: %d\r\n", eproto) );
- return esock_make_error(env, esock_atom_einval);
+
+ SGDBG2( dbg,
+ ("SOCKET", "esock_open2 -> "
+ "\r\n domain: %d"
+ "\r\n type: %d"
+ "\r\n protocol: %d"
+ "\r\n", domain, type, protocol) );
+
+
+ if (esock_open2_todup(env, eopts)) {
+ /* We shall dup the socket */
+ if ((sock = dup(fd)) == INVALID_SOCKET) {
+ save_errno = sock_errno();
+
+ SGDBG2( dbg,
+ ("SOCKET", "esock_open2 -> dup failed: %d\r\n", save_errno) );
+
+ /* reason = {dup, 'errno atom'} */
+ reason = MKT2(env, MKA(env, "dup"), MKA(env, erl_errno_id(save_errno)));
+ return esock_make_error(env, reason);
+ }
+ closeOnClose = TRUE;
+ } else {
+ sock = fd;
+ closeOnClose = FALSE;
}
-#ifdef HAVE_SETNS
- /* We *currently* only support one extra option: netns */
- if (!emap2netns(env, emap, &netns)) {
- SGDBG( ("SOCKET", "nif_open -> namespace: %s\r\n", netns) );
+ event = sock;
+
+ SET_NONBLOCKING(sock);
+
+ /* Create and initiate the socket "descriptor" */
+ if ((descP = alloc_descriptor(sock, event)) == NULL) {
+ if (closeOnClose) sock_close(sock);
+ // Not sure if this is really the proper error, but...
return enif_make_badarg(env);
}
-#else
- netns = NULL;
-#endif
+ descP->state = ESOCK_STATE_OPEN;
+ descP->domain = domain;
+ descP->type = type;
+ descP->protocol = protocol;
+ descP->closeOnClose = closeOnClose;
+ descP->origFD = fd;
- result = esock_open(env, domain, type, proto, netns);
+ /* Does this apply to other types? Such as RAW?
+ * Also, is this really correct? Should we not wait for bind?
+ */
+ if ((type == SOCK_DGRAM) ||
+ (type == SOCK_SEQPACKET)) {
+ descP->isReadable = TRUE;
+ descP->isWritable = TRUE;
+ } else if (type == SOCK_STREAM) {
+ /* Check if we are already connected, if so change state */
+ unsigned int sz = sizeof(descP->remote);
+ int code = sock_peer(descP->sock,
+ (struct sockaddr*) &descP->remote, &sz);
+ if (!IS_SOCKET_ERROR(code)) {
+ SGDBG2( dbg, ("SOCKET", "esock_open2 -> connected\r\n") );
+ descP->state = ESOCK_STATE_CONNECTED;
+ descP->isReadable = TRUE;
+ descP->isWritable = TRUE;
+ enif_set_pid_undefined(&descP->connPid);
+ } else {
+ SGDBG2( dbg, ("SOCKET", "esock_open2 -> not connected\r\n") );
+ }
+ }
- SGDBG( ("SOCKET", "nif_open -> done with result: "
- "\r\n %T"
- "\r\n", result) );
+ /* And create the 'socket' resource */
+ res = enif_make_resource(env, descP);
+ enif_release_resource(descP);
- return result;
+ /* Keep track of the creator
+ * This should not be a problem, but just in case
+ * the *open* function is used with the wrong kind
+ * of environment...
+ */
+ if (enif_self(env, &descP->ctrlPid) == NULL)
+ return esock_make_error(env, atom_exself);
-#endif // if defined(__WIN32__)
+ if (MONP("esock_open2 -> ctrl",
+ env, descP,
+ &descP->ctrlPid,
+ &descP->ctrlMon) != 0)
+ return esock_make_error(env, atom_exmon);
+
+
+ inc_socket(domain, type, protocol);
+
+ /* And finally update the registry.
+ * Shall we keep track of the fact that this socket is created elsewhere?
+ */
+ esock_send_reg_add_msg(env, res);
+
+ SGDBG2( dbg,
+ ("SOCKET", "esock_open2 -> done: %T\r\n", res) );
+
+ return esock_make_ok2(env, res);
+}
+
+
+/* The eextra map "may" contain a boolean 'debug' key, if not we assume
+ * the default of FALSE.
+ */
+static
+BOOLEAN_T esock_open2_is_debug(ErlNifEnv* env, ERL_NIF_TERM eextra)
+{
+ return esock_get_bool_from_map(env, eextra, MKA(env, "debug"), FALSE);
}
-/* esock_open - create an endpoint for communication
+/* The eextra contains a boolean 'dup' key. Defaults to TRUE.
+ */
+static
+BOOLEAN_T esock_open2_todup(ErlNifEnv* env, ERL_NIF_TERM eextra)
+{
+ return esock_get_bool_from_map(env, eextra, MKA(env, "dup"), TRUE);
+}
+
+/* The eextra contains an integer 'domain' key.
+ */
+static
+BOOLEAN_T esock_open2_get_domain(ErlNifEnv* env,
+ ERL_NIF_TERM eopts, int* domain)
+{
+ ERL_NIF_TERM key = MKA(env, "domain");
+ int edomain;
+
+ SGDBG( ("SOCKET", "esock_open2_get_domain -> entry with"
+ "\r\n eopts: %T"
+ "\r\n", eopts) );
+
+ if (esock_get_int_from_map(env, eopts, key, &edomain)) {
+ /* decode */
+ if (edomain2domain(edomain, domain))
+ return TRUE;
+ else
+ return FALSE;
+ } else {
+ *domain = edomain; // Contains an "error code"
+ return FALSE;
+ }
+}
+
+/* The eextra contains an integer 'type' key.
+ */
+static
+BOOLEAN_T esock_open2_get_type(ErlNifEnv* env,
+ ERL_NIF_TERM eopts, int* type)
+{
+ ERL_NIF_TERM key = MKA(env, "type");
+ int etype;
+
+ SGDBG( ("SOCKET", "esock_open2_get_type -> entry with"
+ "\r\n eopts: %T"
+ "\r\n", eopts) );
+
+ if (esock_get_int_from_map(env, eopts, key, &etype)) {
+ /* decode */
+ if (etype2type(etype, type))
+ return TRUE;
+ else
+ return FALSE;
+ } else {
+ *type = etype; // Contains an "error code"
+ return FALSE;
+ }
+}
+
+/* The eextra contains an integer 'protocol' key.
+ */
+static
+BOOLEAN_T esock_open2_get_protocol(ErlNifEnv* env,
+ ERL_NIF_TERM eopts, int* protocol)
+{
+ ERL_NIF_TERM key = MKA(env, "protocol");
+ int eproto;
+
+ SGDBG( ("SOCKET", "esock_open2_get_protocol -> entry with"
+ "\r\n eopts: %T"
+ "\r\n", eopts) );
+
+ if (esock_get_int_from_map(env, eopts, key, &eproto)) {
+ /* decode */
+ if (eproto2proto(env, eproto, protocol))
+ return TRUE;
+ else
+ return FALSE;
+ } else {
+ *protocol = eproto; // Contains an "error code"
+ return FALSE;
+ }
+}
+
+#endif // if defined(__WIN32__)
+
+
+/* esock_open4 - create an endpoint for communication
*
* Assumes the input has been validated.
*
@@ -5358,38 +5769,56 @@ ERL_NIF_TERM nif_open(ErlNifEnv* env,
* yet, we must use the global debug flag.
*/
#if !defined(__WIN32__)
-
static
-ERL_NIF_TERM esock_open(ErlNifEnv* env,
- int domain, int type, int protocol,
- char* netns)
+ERL_NIF_TERM esock_open4(ErlNifEnv* env,
+ int domain,
+ int type,
+ int protocol,
+ ERL_NIF_TERM eopts)
{
+ BOOLEAN_T dbg = esock_open2_is_debug(env, eopts);
ESockDescriptor* descP;
ERL_NIF_TERM res;
int proto = protocol, save_errno = 0;
SOCKET sock;
HANDLE event;
+ char* netns;
#ifdef HAVE_SETNS
int current_ns = 0;
#endif
- SGDBG( ("SOCKET", "esock_open -> entry with"
- "\r\n domain: %d"
- "\r\n type: %d"
- "\r\n protocol: %d"
- "\r\n netns: %s"
- "\r\n", domain, type, protocol, ((netns == NULL) ? "NULL" : netns)) );
+ SGDBG2( dbg,
+ ("SOCKET", "esock_open4 -> entry with"
+ "\r\n domain: %d"
+ "\r\n type: %d"
+ "\r\n protocol: %d"
+ "\r\n eopts: %T"
+ "\r\n", domain, type, protocol, eopts) );
+
+
+#ifdef HAVE_SETNS
+ if (esock_open4_get_netns(env, eopts, &netns)) {
+ SGDBG( ("SOCKET", "nif_open -> namespace: %s\r\n", netns) );
+ }
+#else
+ netns = NULL;
+#endif
+
#ifdef HAVE_SETNS
if ((netns != NULL) &&
- !change_network_namespace(netns, &current_ns, &save_errno))
+ !change_network_namespace(netns, &current_ns, &save_errno)) {
+ FREE(netns);
return esock_make_error_errno(env, save_errno);
+ }
#endif
- if ((sock = sock_open(domain, type, proto)) == INVALID_SOCKET)
+ if ((sock = sock_open(domain, type, proto)) == INVALID_SOCKET) {
+ if (netns != NULL) FREE(netns);
return esock_make_error_errno(env, sock_errno());
+ }
- SGDBG( ("SOCKET", "esock_open -> open success: %d\r\n", sock) );
+ SGDBG2( dbg, ("SOCKET", "esock_open -> open success: %d\r\n", sock) );
/* NOTE that if the protocol = 0 (default) and the domain is not
@@ -5406,10 +5835,12 @@ ERL_NIF_TERM esock_open(ErlNifEnv* env,
save_errno = sock_errno();
while ((sock_close(sock) == INVALID_SOCKET) &&
(sock_errno() == EINTR));
+ if (netns != NULL) FREE(netns);
return esock_make_error_errno(env, save_errno);
} else {
while ((sock_close(sock) == INVALID_SOCKET) &&
(sock_errno() == EINTR));
+ if (netns != NULL) FREE(netns);
return esock_make_error(env, esock_atom_eafnosupport);
}
}
@@ -5417,11 +5848,13 @@ ERL_NIF_TERM esock_open(ErlNifEnv* env,
#ifdef HAVE_SETNS
if ((netns != NULL) &&
- !restore_network_namespace(current_ns, sock, &save_errno))
+ !restore_network_namespace(current_ns, sock, &save_errno)) {
+ FREE(netns);
return esock_make_error_errno(env, save_errno);
+ }
+
+ if (netns != NULL) FREE(netns);
- if (netns != NULL)
- FREE(netns);
#endif
@@ -5431,7 +5864,7 @@ ERL_NIF_TERM esock_open(ErlNifEnv* env,
return esock_make_error_errno(env, save_errno);
}
- SGDBG( ("SOCKET", "esock_open -> event success: %d\r\n", event) );
+ SGDBG2( dbg, ("SOCKET", "esock_open4 -> event success: %d\r\n", event) );
SET_NONBLOCKING(sock);
@@ -5490,6 +5923,54 @@ ERL_NIF_TERM esock_open(ErlNifEnv* env,
static
+BOOLEAN_T esock_open_which_domain(SOCKET sock, int* domain)
+{
+#if defined(SO_DOMAIN)
+ int val;
+ SOCKOPTLEN_T valSz = sizeof(val);
+ int res;
+
+ res = sock_getopt(sock, SOL_SOCKET, SO_DOMAIN, &val, &valSz);
+
+ if (res != 0) {
+ *domain = ESOCK_WHICH_DOMAIN_ERROR;
+ return FALSE;
+ } else {
+ *domain = val;
+ return TRUE;
+ }
+#else
+ *domain = ESOCK_WHICH_DOMAIN_UNSUP;
+ return FALSE;
+#endif
+}
+
+
+static
+BOOLEAN_T esock_open_which_type(SOCKET sock, int* type)
+{
+#if defined(SO_TYPE)
+ int val;
+ SOCKOPTLEN_T valSz = sizeof(val);
+ int res;
+
+ res = sock_getopt(sock, SOL_SOCKET, SO_TYPE, &val, &valSz);
+
+ if (res != 0) {
+ *type = ESOCK_WHICH_TYPE_ERROR;
+ return FALSE;
+ } else {
+ *type = val;
+ return TRUE;
+ }
+#else
+ *type = ESOCK_WHICH_TYPE_UNSUP;
+ return FALSE;
+#endif
+}
+
+
+static
BOOLEAN_T esock_open_which_protocol(SOCKET sock, int* proto)
{
#if defined(SO_PROTOCOL)
@@ -8108,7 +8589,7 @@ ERL_NIF_TERM esock_finalize_close(ErlNifEnv* env,
*/
SET_BLOCKING(descP->sock);
- if (sock_close(descP->sock) != 0) {
+ if (descP->closeOnClose && (sock_close(descP->sock) != 0)) {
int save_errno = sock_errno();
if (save_errno != ERRNO_BLOCK) {
@@ -10411,8 +10892,12 @@ ERL_NIF_TERM esock_setopt_lvl_ipv6_addrform(ErlNifEnv* env,
domain) );
res = socket_setopt(descP->sock,
- SOL_IPV6, IPV6_ADDRFORM,
- &domain, sizeof(domain));
+#if defined(SOL_IPV6)
+ SOL_IPV6,
+#else
+ IPPROTO_IPV6,
+#endif
+ IPV6_ADDRFORM, &domain, sizeof(domain));
if (res != 0)
result = esock_make_error_errno(env, sock_errno());
@@ -10442,7 +10927,14 @@ ERL_NIF_TERM esock_setopt_lvl_ipv6_authhdr(ErlNifEnv* env,
ESockDescriptor* descP,
ERL_NIF_TERM eVal)
{
- return esock_setopt_bool_opt(env, descP, SOL_IPV6, IPV6_AUTHHDR, eVal);
+#if defined(SOL_IPV6)
+ int level = SOL_IPV6;
+#else
+ int level = IPPROTO_IPV6;
+#endif
+
+
+ return esock_setopt_bool_opt(env, descP, level, IPV6_AUTHHDR, eVal);
}
#endif
@@ -19040,6 +19532,8 @@ ESockDescriptor* alloc_descriptor(SOCKET sock, HANDLE event)
descP->sock = sock;
descP->event = event;
+ descP->origFD = -1;
+ descP->closeOnClose = TRUE;
enif_set_pid_undefined(&descP->ctrlPid);
MON_INIT(&descP->ctrlMon);
@@ -19304,58 +19798,12 @@ BOOLEAN_T eproto2proto(ErlNifEnv* env,
#ifdef HAVE_SETNS
- /* emap2netns - extract the netns field from the extra map
- *
- * Note that currently we only support one extra option, the netns.
+/* esock_open4_get_netns - extract the netns field from the opts map
*/
static
-BOOLEAN_T emap2netns(ErlNifEnv* env, ERL_NIF_TERM map, char** netns)
+BOOLEAN_T esock_open4_get_netns(ErlNifEnv* env, ERL_NIF_TERM opts, char** netns)
{
- size_t sz;
- ERL_NIF_TERM key;
- ERL_NIF_TERM value;
- unsigned int len;
- char* buf;
- int written;
-
- /* Note that its acceptable that the extra map is empty */
- if (!enif_get_map_size(env, map, &sz) ||
- (sz != 1)) {
- *netns = NULL;
- return TRUE;
- }
-
- /* The currently only supported extra option is: netns */
- key = enif_make_atom(env, "netns");
- if (!GET_MAP_VAL(env, map, key, &value)) {
- *netns = NULL; // Just in case...
- return FALSE;
- }
-
- /* So far so good. The value should be a string, check. */
- if (!enif_is_list(env, value)) {
- *netns = NULL; // Just in case...
- return FALSE;
- }
-
- if (!enif_get_list_length(env, value, &len)) {
- *netns = NULL; // Just in case...
- return FALSE;
- }
-
- if ((buf = MALLOC(len+1)) == NULL) {
- *netns = NULL; // Just in case...
- return FALSE;
- }
-
- written = enif_get_string(env, value, buf, len+1, ERL_NIF_LATIN1);
- if (written == (len+1)) {
- *netns = buf;
- return TRUE;
- } else {
- *netns = NULL; // Just in case...
- return FALSE;
- }
+ return esock_get_string_from_map(env, opts, MKA(env, "netns"), netns);
}
#endif
@@ -20995,7 +21443,7 @@ void esock_down(ErlNifEnv* env,
* If the owner wish to ensure the buffer are written,
* it should have closed the socket explicitly...
*/
- if (sock_close(descP->sock) != 0) {
+ if (descP->closeOnClose && (sock_close(descP->sock) != 0)) {
int save_errno = sock_errno();
esock_warning_msg("Failed closing socket for terminating "
@@ -21034,7 +21482,7 @@ void esock_down(ErlNifEnv* env,
* for later (when the stop function actually gets called...
*/
- if (sock_close(descP->sock) != 0) {
+ if (descP->closeOnClose && (sock_close(descP->sock) != 0)) {
int save_errno = sock_errno();
esock_warning_msg("Failed closing socket for terminating "
@@ -21249,8 +21697,7 @@ ErlNifFunc esock_funcs[] =
{"nif_command", 1, nif_command, 0},
// The proper "socket" interface
- // nif_open/1 is (supposed to be) used when we already have a file descriptor
- // {"nif_open", 1, nif_open, 0},
+ {"nif_open", 2, nif_open, 0},
{"nif_open", 4, nif_open, 0},
{"nif_bind", 2, nif_bind, 0},
{"nif_connect", 2, nif_connect, 0},
diff --git a/erts/emulator/nifs/common/socket_util.c b/erts/emulator/nifs/common/socket_util.c
index a30c2e36ae..c12899b134 100644
--- a/erts/emulator/nifs/common/socket_util.c
+++ b/erts/emulator/nifs/common/socket_util.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2018-2019. All Rights Reserved.
+ * Copyright Ericsson AB 2018-2020. 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.
@@ -111,6 +111,119 @@ static BOOLEAN_T esock_extract_from_map(ErlNifEnv* env,
ERL_NIF_TERM key,
ERL_NIF_TERM* val);
+
+
+/* *** esock_get_bool_from_map ***
+ *
+ * Simple utility function used to extract a boolean value from a map.
+ * If it fails to extract the value (for whatever reason) the default
+ * value is returned.
+ */
+
+extern
+BOOLEAN_T esock_get_bool_from_map(ErlNifEnv* env,
+ ERL_NIF_TERM map,
+ ERL_NIF_TERM key,
+ BOOLEAN_T def)
+{
+ ERL_NIF_TERM val;
+
+ if (!GET_MAP_VAL(env, map, key, &val)) {
+ return def;
+ } else {
+ if (COMPARE(val, esock_atom_true) == 0)
+ return TRUE;
+ else
+ return FALSE;
+ }
+}
+
+
+/* *** esock_get_bool_from_map ***
+ *
+ * Simple utility function used to extract a integer value from a map.
+ */
+
+extern
+BOOLEAN_T esock_get_int_from_map(ErlNifEnv* env,
+ ERL_NIF_TERM map,
+ ERL_NIF_TERM key,
+ int* val)
+{
+ ERL_NIF_TERM eval;
+
+ if (!GET_MAP_VAL(env, map, key, &eval)) {
+ *val = -1;
+ return FALSE;
+ }
+
+ if (!IS_NUM(env, eval)) {
+ *val = -2;
+ return FALSE;
+ }
+
+ if (!GET_INT(env, eval, val)) {
+ *val = -3;
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+
+
+/* *** esock_get_string_from_map ***
+ *
+ * Simple utility function used to extract a (latin1) string value from a map.
+ * This function will allocate a buffer to put the string!
+ */
+
+extern
+BOOLEAN_T esock_get_string_from_map(ErlNifEnv* env,
+ ERL_NIF_TERM map,
+ ERL_NIF_TERM key,
+ char** str)
+{
+ ERL_NIF_TERM value;
+ unsigned int len;
+ char* buf;
+ int written;
+
+ /* The currently only supported extra option is: netns */
+ if (!GET_MAP_VAL(env, map, key, &value)) {
+ *str = NULL;
+ return FALSE;
+ }
+
+ /* So far so good. The value should be a string, check. */
+ if (!enif_is_list(env, value)) {
+ *str = NULL;
+ return FALSE;
+ }
+
+ if (!enif_get_list_length(env, value, &len)) {
+ *str = NULL;
+ return FALSE;
+ }
+
+ if ((buf = MALLOC(len+1)) == NULL) {
+ *str = NULL;
+ return FALSE;
+ }
+
+ written = enif_get_string(env, value, buf, len+1, ERL_NIF_LATIN1);
+ if (written == (len+1)) {
+ *str = buf;
+ return TRUE;
+ } else {
+ *str = NULL;
+ return FALSE;
+ }
+}
+
+
+
+
/* +++ esock_encode_iov +++
*
* Encode an IO Vector. In erlang we represented this as a list of binaries.
diff --git a/erts/emulator/nifs/common/socket_util.h b/erts/emulator/nifs/common/socket_util.h
index 398fb40a5a..0a1eb92339 100644
--- a/erts/emulator/nifs/common/socket_util.h
+++ b/erts/emulator/nifs/common/socket_util.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2018-2019. All Rights Reserved.
+ * Copyright Ericsson AB 2018-2020. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -18,7 +18,7 @@
* %CopyrightEnd%
*
* ----------------------------------------------------------------------
- * Purpose : Utility "stuff" for socket and net.
+ * Purpose : Utility "stuff" for socket and prim_net (nifs).
* ----------------------------------------------------------------------
*
*/
@@ -43,6 +43,22 @@
#define ESOCK_ASSERT(e) ((void) ((e) ? 1 : (ESOCK_ABORT(#e), 0)))
extern
+BOOLEAN_T esock_get_bool_from_map(ErlNifEnv* env,
+ ERL_NIF_TERM map,
+ ERL_NIF_TERM key,
+ BOOLEAN_T def);
+extern
+BOOLEAN_T esock_get_int_from_map(ErlNifEnv* env,
+ ERL_NIF_TERM map,
+ ERL_NIF_TERM key,
+ int* val);
+extern
+BOOLEAN_T esock_get_string_from_map(ErlNifEnv* env,
+ ERL_NIF_TERM map,
+ ERL_NIF_TERM key,
+ char** str);
+
+extern
char* esock_encode_iov(ErlNifEnv* env,
int read,
struct iovec* iov,
diff --git a/erts/emulator/nifs/unix/unix_prim_file.c b/erts/emulator/nifs/unix/unix_prim_file.c
index fbf312a3b8..e6ddaa3479 100644
--- a/erts/emulator/nifs/unix/unix_prim_file.c
+++ b/erts/emulator/nifs/unix/unix_prim_file.c
@@ -107,7 +107,7 @@ ERL_NIF_TERM efile_get_handle(ErlNifEnv *env, efile_data_t *d) {
return result;
}
-static int open_file_type_check(const efile_path_t *path, int fd) {
+static int open_file_is_dir(const efile_path_t *path, int fd) {
struct stat file_info;
int error;
@@ -119,27 +119,14 @@ static int open_file_type_check(const efile_path_t *path, int fd) {
(void)path;
#endif
- if(error < 0) {
- /* If we failed to stat assume success and let the next call handle the
- * error. The old driver checked whether the file was to be used
- * immediately in a read within the call, but the new implementation
- * never does that. */
- return 1;
- }
-
- /* Allow everything that isn't a directory, and error out on the next call
- * if it's unsupported. */
- if(S_ISDIR(file_info.st_mode)) {
- return 0;
- }
-
- return 1;
+ /* Assume not a directory on error. */
+ return error == 0 && S_ISDIR(file_info.st_mode);
}
posix_errno_t efile_open(const efile_path_t *path, enum efile_modes_t modes,
ErlNifResourceType *nif_type, efile_data_t **d) {
- int flags, fd;
+ int mode, flags, fd;
flags = 0;
@@ -174,18 +161,38 @@ posix_errno_t efile_open(const efile_path_t *path, enum efile_modes_t modes,
#endif
}
+ if(modes & EFILE_MODE_DIRECTORY) {
+ mode = DIR_MODE;
+#ifdef O_DIRECTORY
+ flags |= O_DIRECTORY;
+#endif
+ } else {
+ mode = FILE_MODE;
+ }
+
do {
- fd = open((const char*)path->data, flags, FILE_MODE);
+ fd = open((const char*)path->data, flags, mode);
} while(fd == -1 && errno == EINTR);
if(fd != -1) {
efile_unix_t *u;
- if(!(modes & EFILE_MODE_SKIP_TYPE_CHECK) && !open_file_type_check(path, fd)) {
+#ifndef O_DIRECTORY
+ /* On platforms without O_DIRECTORY support, ensure that using the
+ * directory flag to open a file fails. */
+ if(!(modes & EFILE_MODE_SKIP_TYPE_CHECK) &&
+ (modes & EFILE_MODE_DIRECTORY) && !open_file_is_dir(path, fd)) {
close(fd);
+ return ENOTDIR;
+ }
+#endif
- /* This is blatantly incorrect, but we're documented as returning
- * this for everything that isn't a file. */
+ /* open() works on directories without the O_DIRECTORY flag but for
+ * consistency across platforms we require that the user has requested
+ * directory mode. */
+ if(!(modes & EFILE_MODE_SKIP_TYPE_CHECK) &&
+ !(modes & EFILE_MODE_DIRECTORY) && open_file_is_dir(path, fd)) {
+ close(fd);
return EISDIR;
}
@@ -555,7 +562,7 @@ int efile_allocate(efile_data_t *d, Sint64 offset, Sint64 length) {
#if defined(HAVE_FALLOCATE)
/* Linux-specific */
do {
- ret = fallocate(u->fd, FALLOC_FL_KEEP_SIZE, offset, length);
+ ret = fallocate(u->fd, 0, offset, length);
} while(ret < 0 && errno == EINTR);
#elif defined(F_PREALLOCATE)
/* Mac-specific */
@@ -656,6 +663,33 @@ int efile_truncate(efile_data_t *d) {
return 1;
}
+static void build_file_info(struct stat *data, efile_fileinfo_t *result) {
+ if(S_ISCHR(data->st_mode) || S_ISBLK(data->st_mode)) {
+ result->type = EFILE_FILETYPE_DEVICE;
+ } else if(S_ISDIR(data->st_mode)) {
+ result->type = EFILE_FILETYPE_DIRECTORY;
+ } else if(S_ISREG(data->st_mode)) {
+ result->type = EFILE_FILETYPE_REGULAR;
+ } else if(S_ISLNK(data->st_mode)) {
+ result->type = EFILE_FILETYPE_SYMLINK;
+ } else {
+ result->type = EFILE_FILETYPE_OTHER;
+ }
+
+ result->a_time = (Sint64)data->st_atime;
+ result->m_time = (Sint64)data->st_mtime;
+ result->c_time = (Sint64)data->st_ctime;
+ result->size = data->st_size;
+
+ result->major_device = data->st_dev;
+ result->minor_device = data->st_rdev;
+ result->links = data->st_nlink;
+ result->inode = data->st_ino;
+ result->mode = data->st_mode;
+ result->uid = data->st_uid;
+ result->gid = data->st_gid;
+}
+
posix_errno_t efile_read_info(const efile_path_t *path, int follow_links, efile_fileinfo_t *result) {
struct stat data;
@@ -669,30 +703,7 @@ posix_errno_t efile_read_info(const efile_path_t *path, int follow_links, efile_
}
}
- if(S_ISCHR(data.st_mode) || S_ISBLK(data.st_mode)) {
- result->type = EFILE_FILETYPE_DEVICE;
- } else if(S_ISDIR(data.st_mode)) {
- result->type = EFILE_FILETYPE_DIRECTORY;
- } else if(S_ISREG(data.st_mode)) {
- result->type = EFILE_FILETYPE_REGULAR;
- } else if(S_ISLNK(data.st_mode)) {
- result->type = EFILE_FILETYPE_SYMLINK;
- } else {
- result->type = EFILE_FILETYPE_OTHER;
- }
-
- result->a_time = (Sint64)data.st_atime;
- result->m_time = (Sint64)data.st_mtime;
- result->c_time = (Sint64)data.st_ctime;
- result->size = data.st_size;
-
- result->major_device = data.st_dev;
- result->minor_device = data.st_rdev;
- result->links = data.st_nlink;
- result->inode = data.st_ino;
- result->mode = data.st_mode;
- result->uid = data.st_uid;
- result->gid = data.st_gid;
+ build_file_info(&data, result);
#ifndef NO_ACCESS
result->access = EFILE_ACCESS_NONE;
@@ -711,6 +722,56 @@ posix_errno_t efile_read_info(const efile_path_t *path, int follow_links, efile_
return 0;
}
+static int check_access(struct stat *st) {
+ int ret = EFILE_ACCESS_NONE;
+
+ if(st->st_uid == getuid()) {
+ if(st->st_mode & S_IRUSR) {
+ ret |= EFILE_ACCESS_READ;
+ }
+ if(st->st_mode & S_IWUSR) {
+ ret |= EFILE_ACCESS_WRITE;
+ }
+ return ret;
+ }
+
+ if(st->st_gid == getgid()) {
+ if(st->st_mode & S_IRGRP) {
+ ret |= EFILE_ACCESS_READ;
+ }
+ if(st->st_mode & S_IWGRP) {
+ ret |= EFILE_ACCESS_WRITE;
+ }
+ return ret;
+ }
+
+ if(st->st_mode & S_IROTH) {
+ ret |= EFILE_ACCESS_READ;
+ }
+ if(st->st_mode & S_IWOTH) {
+ ret |= EFILE_ACCESS_WRITE;
+ }
+ return ret;
+}
+
+posix_errno_t efile_read_handle_info(efile_data_t *d, efile_fileinfo_t *result) {
+ struct stat data;
+ efile_unix_t *u = (efile_unix_t*)d;
+
+#ifdef HAVE_FSTAT
+ if(fstat(u->fd, &data) < 0) {
+ return errno;
+ }
+
+ build_file_info(&data, result);
+ result->access = check_access(&data);
+
+ return 0;
+#else
+ return ENOTSUP;
+#endif
+}
+
posix_errno_t efile_set_permissions(const efile_path_t *path, Uint32 permissions) {
const mode_t MUTABLE_MODES = (S_ISUID | S_ISGID | S_IRWXU | S_IRWXG | S_IRWXO);
mode_t new_modes = permissions & MUTABLE_MODES;
diff --git a/erts/emulator/nifs/win32/win_prim_file.c b/erts/emulator/nifs/win32/win_prim_file.c
index e7d3924240..fca7385809 100644
--- a/erts/emulator/nifs/win32/win_prim_file.c
+++ b/erts/emulator/nifs/win32/win_prim_file.c
@@ -269,7 +269,18 @@ static int normalize_path_result(ErlNifBinary *path) {
return enif_realloc_binary(path, length * sizeof(WCHAR));
}
-/* @brief Checks whether all the given attributes are set on the object at the
+/** @brief Checks whether all the given attributes are set on the object at the
+ * given handle. Note that it assumes false on errors. */
+static int handle_has_file_attributes(HANDLE handle, DWORD mask) {
+ BY_HANDLE_FILE_INFORMATION native_file_info;
+ if(!GetFileInformationByHandle(handle, &native_file_info)) {
+ return 0;
+ }
+
+ return !!((native_file_info.dwFileAttributes & mask) == mask);
+}
+
+/** @brief Checks whether all the given attributes are set on the object at the
* given path. Note that it assumes false on errors. */
static int has_file_attributes(const efile_path_t *path, DWORD mask) {
DWORD attributes = GetFileAttributesW((WCHAR*)path->data);
@@ -313,7 +324,7 @@ static int get_drive_number(const efile_path_t *path) {
return -1;
}
-/* @brief Checks whether two *paths* are on the same mount point; they don't
+/** @brief Checks whether two *paths* are on the same mount point; they don't
* have to refer to existing or accessible files/directories. */
static int has_same_mount_point(const efile_path_t *path_a, const efile_path_t *path_b) {
WCHAR *mount_a, *mount_b;
@@ -412,10 +423,15 @@ posix_errno_t efile_open(const efile_path_t *path, enum efile_modes_t modes,
ASSERT_PATH_FORMAT(path);
+ attributes = 0;
access_flags = 0;
open_mode = 0;
- if(modes & EFILE_MODE_READ && !(modes & EFILE_MODE_WRITE)) {
+ if(modes & EFILE_MODE_DIRECTORY) {
+ attributes = FILE_FLAG_BACKUP_SEMANTICS;
+ access_flags = GENERIC_READ;
+ open_mode = OPEN_EXISTING;
+ } else if(modes & EFILE_MODE_READ && !(modes & EFILE_MODE_WRITE)) {
access_flags = GENERIC_READ;
open_mode = OPEN_EXISTING;
} else if(modes & EFILE_MODE_WRITE && !(modes & EFILE_MODE_READ)) {
@@ -438,9 +454,9 @@ posix_errno_t efile_open(const efile_path_t *path, enum efile_modes_t modes,
}
if(modes & EFILE_MODE_SYNC) {
- attributes = FILE_FLAG_WRITE_THROUGH;
+ attributes |= FILE_FLAG_WRITE_THROUGH;
} else {
- attributes = FILE_ATTRIBUTE_NORMAL;
+ attributes |= FILE_ATTRIBUTE_NORMAL;
}
handle = CreateFileW((WCHAR*)path->data, access_flags,
@@ -449,6 +465,12 @@ posix_errno_t efile_open(const efile_path_t *path, enum efile_modes_t modes,
if(handle != INVALID_HANDLE_VALUE) {
efile_win_t *w;
+ /* Directory mode specified, but path is not a directory. */
+ if((modes & EFILE_MODE_DIRECTORY) && !handle_has_file_attributes(handle, FILE_ATTRIBUTE_DIRECTORY)) {
+ CloseHandle(handle);
+ return ENOTDIR;
+ }
+
w = (efile_win_t*)enif_alloc_resource(nif_type, sizeof(efile_win_t));
w->handle = handle;
@@ -461,7 +483,7 @@ posix_errno_t efile_open(const efile_path_t *path, enum efile_modes_t modes,
/* Rewrite all failures on directories to EISDIR to match the old
* driver. */
- if(has_file_attributes(path, FILE_ATTRIBUTE_DIRECTORY)) {
+ if(!(modes & EFILE_MODE_DIRECTORY) && has_file_attributes(path, FILE_ATTRIBUTE_DIRECTORY)) {
return EISDIR;
}
@@ -751,6 +773,71 @@ static int is_name_surrogate(const efile_path_t *path) {
return result;
}
+static void build_file_info(BY_HANDLE_FILE_INFORMATION *native_file_info, const efile_path_t *path, int is_link, efile_fileinfo_t *result) {
+ DWORD attributes;
+
+ attributes = native_file_info->dwFileAttributes;
+
+ if(is_link) {
+ result->type = EFILE_FILETYPE_SYMLINK;
+ /* This should be _S_IFLNK, but the old driver always set
+ * non-directories to _S_IFREG. */
+ result->mode |= _S_IFREG;
+ } else if(attributes & FILE_ATTRIBUTE_DIRECTORY) {
+ result->type = EFILE_FILETYPE_DIRECTORY;
+ result->mode |= _S_IFDIR | _S_IEXEC;
+ } else {
+ if(is_executable_file(path)) {
+ result->mode |= _S_IEXEC;
+ }
+
+ result->type = EFILE_FILETYPE_REGULAR;
+ result->mode |= _S_IFREG;
+ }
+
+ if(!(attributes & FILE_ATTRIBUTE_READONLY)) {
+ result->access = EFILE_ACCESS_READ | EFILE_ACCESS_WRITE;
+ result->mode |= _S_IREAD | _S_IWRITE;
+ } else {
+ result->access = EFILE_ACCESS_READ;
+ result->mode |= _S_IREAD;
+ }
+
+ /* Propagate user mode-bits to group/other fields */
+ result->mode |= (result->mode & 0700) >> 3;
+ result->mode |= (result->mode & 0700) >> 6;
+
+ result->size =
+ ((Uint64)native_file_info->nFileSizeHigh << 32ull) |
+ (Uint64)native_file_info->nFileSizeLow;
+ result->links = MAX(1, native_file_info->nNumberOfLinks);
+
+ result->major_device = get_drive_number(path);
+ result->minor_device = 0;
+ result->inode = 0;
+ result->uid = 0;
+ result->gid = 0;
+}
+
+static void build_file_info_times(BY_HANDLE_FILE_INFORMATION *native_file_info, efile_fileinfo_t *result) {
+ FILETIME_TO_EPOCH(result->m_time, native_file_info->ftLastWriteTime);
+ FILETIME_TO_EPOCH(result->a_time, native_file_info->ftLastAccessTime);
+ FILETIME_TO_EPOCH(result->c_time, native_file_info->ftCreationTime);
+
+ if(result->m_time == -EPOCH_DIFFERENCE) {
+ /* Default to 1970 just like the old driver. */
+ result->m_time = 0;
+ }
+
+ if(result->a_time == -EPOCH_DIFFERENCE) {
+ result->a_time = result->m_time;
+ }
+
+ if(result->c_time == -EPOCH_DIFFERENCE) {
+ result->c_time = result->m_time;
+ }
+}
+
posix_errno_t efile_read_info(const efile_path_t *path, int follow_links, efile_fileinfo_t *result) {
BY_HANDLE_FILE_INFORMATION native_file_info;
DWORD attributes;
@@ -770,23 +857,41 @@ posix_errno_t efile_read_info(const efile_path_t *path, int follow_links, efile_
return windows_to_posix_errno(last_error);
}
- attributes = FILE_ATTRIBUTE_DIRECTORY;
+ native_file_info.dwFileAttributes = FILE_ATTRIBUTE_DIRECTORY;
} else if(is_path_root(path)) {
/* Local (or mounted) roots can be queried with GetFileAttributesW but
* lack support for GetFileInformationByHandle, so we'll skip that
* part. */
+ native_file_info.dwFileAttributes = attributes;
} else {
HANDLE handle;
+ DWORD last_error;
+ DWORD flags;
if(attributes & FILE_ATTRIBUTE_REPARSE_POINT) {
is_link = is_name_surrogate(path);
}
+ flags = FILE_FLAG_BACKUP_SEMANTICS;
+ if(!follow_links && is_link) {
+ flags |= FILE_FLAG_OPEN_REPARSE_POINT;
+ }
+
+ handle = CreateFileW((const WCHAR*)path->data, GENERIC_READ,
+ FILE_SHARE_FLAGS, NULL, OPEN_EXISTING, flags, NULL);
+ last_error = GetLastError();
+
+ if(handle == INVALID_HANDLE_VALUE) {
+ return windows_to_posix_errno(last_error);
+ }
+
if(follow_links && is_link) {
posix_errno_t posix_errno;
efile_path_t resolved_path;
- posix_errno = internal_read_link(path, &resolved_path);
+ posix_errno = internal_read_link(handle, &resolved_path);
+
+ CloseHandle(handle);
if(posix_errno != 0) {
return posix_errno;
@@ -795,74 +900,43 @@ posix_errno_t efile_read_info(const efile_path_t *path, int follow_links, efile_
return efile_read_info(&resolved_path, 0, result);
}
- handle = CreateFileW((const WCHAR*)path->data, GENERIC_READ,
- FILE_SHARE_FLAGS, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS,
- NULL);
-
- /* The old driver never cared whether this succeeded. */
- if(handle != INVALID_HANDLE_VALUE) {
- GetFileInformationByHandle(handle, &native_file_info);
- CloseHandle(handle);
- }
+ GetFileInformationByHandle(handle, &native_file_info);
+ CloseHandle(handle);
- FILETIME_TO_EPOCH(result->m_time, native_file_info.ftLastWriteTime);
- FILETIME_TO_EPOCH(result->a_time, native_file_info.ftLastAccessTime);
- FILETIME_TO_EPOCH(result->c_time, native_file_info.ftCreationTime);
+ build_file_info_times(&native_file_info, result);
+ }
- if(result->m_time == -EPOCH_DIFFERENCE) {
- /* Default to 1970 just like the old driver. */
- result->m_time = 0;
- }
+ build_file_info(&native_file_info, path, is_link, result);
- if(result->a_time == -EPOCH_DIFFERENCE) {
- result->a_time = result->m_time;
- }
+ return 0;
+}
- if(result->c_time == -EPOCH_DIFFERENCE) {
- result->c_time = result->m_time;
- }
- }
+posix_errno_t efile_read_handle_info(efile_data_t *d, efile_fileinfo_t *result) {
+ efile_win_t *w = (efile_win_t*)d;
+ HANDLE handle;
+ BY_HANDLE_FILE_INFORMATION native_file_info;
+ posix_errno_t posix_errno;
+ efile_path_t path;
+ int length;
- if(is_link) {
- result->type = EFILE_FILETYPE_SYMLINK;
- /* This should be _S_IFLNK, but the old driver always set
- * non-directories to _S_IFREG. */
- result->mode |= _S_IFREG;
- } else if(attributes & FILE_ATTRIBUTE_DIRECTORY) {
- result->type = EFILE_FILETYPE_DIRECTORY;
- result->mode |= _S_IFDIR | _S_IEXEC;
- } else {
- if(is_executable_file(path)) {
- result->mode |= _S_IEXEC;
- }
+ sys_memset(&native_file_info, 0, sizeof(native_file_info));
- result->type = EFILE_FILETYPE_REGULAR;
- result->mode |= _S_IFREG;
+ posix_errno = internal_read_link(w->handle, &path);
+ if(posix_errno != 0) {
+ return posix_errno;
}
- if(!(attributes & FILE_ATTRIBUTE_READONLY)) {
- result->access = EFILE_ACCESS_READ | EFILE_ACCESS_WRITE;
- result->mode |= _S_IREAD | _S_IWRITE;
+ if(GetFileInformationByHandle(w->handle, &native_file_info)) {
+ build_file_info_times(&native_file_info, result);
+ } else if(is_path_root(&path)) {
+ /* GetFileInformationByHandle is not supported on path roots, so
+ * fall back to efile_read_info. */
+ return efile_read_info(&path, 0, result);
} else {
- result->access = EFILE_ACCESS_READ;
- result->mode |= _S_IREAD;
+ return windows_to_posix_errno(GetLastError());
}
- /* Propagate user mode-bits to group/other fields */
- result->mode |= (result->mode & 0700) >> 3;
- result->mode |= (result->mode & 0700) >> 6;
-
- result->size =
- ((Uint64)native_file_info.nFileSizeHigh << 32ull) |
- (Uint64)native_file_info.nFileSizeLow;
-
- result->links = MAX(1, native_file_info.nNumberOfLinks);
-
- result->major_device = get_drive_number(path);
- result->minor_device = 0;
- result->inode = 0;
- result->uid = 0;
- result->gid = 0;
+ build_file_info(&native_file_info, &path, 0, result);
return 0;
}
@@ -941,31 +1015,20 @@ posix_errno_t efile_set_time(const efile_path_t *path, Sint64 a_time, Sint64 m_t
return windows_to_posix_errno(last_error);
}
-static posix_errno_t internal_read_link(const efile_path_t *path, efile_path_t *result) {
+static posix_errno_t internal_read_link(HANDLE link_handle, efile_path_t *result) {
DWORD required_length, actual_length;
- HANDLE link_handle;
DWORD last_error;
- link_handle = CreateFileW((WCHAR*)path->data, GENERIC_READ,
- FILE_SHARE_FLAGS, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
- last_error = GetLastError();
-
- if(link_handle == INVALID_HANDLE_VALUE) {
- return windows_to_posix_errno(last_error);
- }
-
required_length = GetFinalPathNameByHandleW(link_handle, NULL, 0, 0);
last_error = GetLastError();
if(required_length <= 0) {
- CloseHandle(link_handle);
return windows_to_posix_errno(last_error);
}
/* Unlike many other path functions (eg. GetFullPathNameW), this one
* includes the NUL terminator in its required length. */
if(!enif_alloc_binary(required_length * sizeof(WCHAR), result)) {
- CloseHandle(link_handle);
return ENOMEM;
}
@@ -973,8 +1036,6 @@ static posix_errno_t internal_read_link(const efile_path_t *path, efile_path_t *
(WCHAR*)result->data, required_length, 0);
last_error = GetLastError();
- CloseHandle(link_handle);
-
if(actual_length == 0 || actual_length >= required_length) {
enif_release_binary(result);
return windows_to_posix_errno(last_error);
@@ -992,6 +1053,8 @@ posix_errno_t efile_read_link(ErlNifEnv *env, const efile_path_t *path, ERL_NIF_
posix_errno_t posix_errno;
ErlNifBinary result_bin;
DWORD attributes;
+ HANDLE handle;
+ DWORD last_error;
ASSERT_PATH_FORMAT(path);
@@ -1007,7 +1070,17 @@ posix_errno_t efile_read_link(ErlNifEnv *env, const efile_path_t *path, ERL_NIF_
return EINVAL;
}
- posix_errno = internal_read_link(path, &result_bin);
+ handle = CreateFileW((WCHAR*)path->data, GENERIC_READ,
+ FILE_SHARE_FLAGS, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS,
+ NULL);
+ last_error = GetLastError();
+
+ if(handle == INVALID_HANDLE_VALUE) {
+ return windows_to_posix_errno(last_error);
+ }
+ posix_errno = internal_read_link(handle, &result_bin);
+
+ CloseHandle(handle);
if(posix_errno == 0) {
if(!normalize_path_result(&result_bin)) {
diff --git a/erts/emulator/sys/common/erl_check_io.c b/erts/emulator/sys/common/erl_check_io.c
index 308d829c09..35b6d99ba2 100644
--- a/erts/emulator/sys/common/erl_check_io.c
+++ b/erts/emulator/sys/common/erl_check_io.c
@@ -1082,7 +1082,7 @@ enif_select_x(ErlNifEnv* env,
pid ? pid->pid : THE_NON_VALUE, THE_NON_VALUE);
if (mode & ERL_NIF_SELECT_STOP) {
- ASSERT(resource->type->stop);
+ ASSERT(resource->type->fn.stop);
if (IS_FD_UNKNOWN(state)) {
/* fast track to stop callback */
call_stop = CALL_STOP;
diff --git a/erts/emulator/sys/common/erl_osenv.h b/erts/emulator/sys/common/erl_osenv.h
index 4777f2148a..d0902e0ba1 100644
--- a/erts/emulator/sys/common/erl_osenv.h
+++ b/erts/emulator/sys/common/erl_osenv.h
@@ -35,16 +35,10 @@
# include "config.h"
#endif
-typedef struct __erts_osenv_data_t erts_osenv_data_t;
-
-typedef struct __erts_osenv_t {
- struct __env_rbtnode_t *tree;
- int variable_count;
- int content_size;
-} erts_osenv_t;
-
#include "sys.h"
+typedef struct __erts_osenv_data_t erts_osenv_data_t;
+
struct __erts_osenv_data_t {
Sint length;
void *data;
@@ -53,7 +47,7 @@ struct __erts_osenv_data_t {
void erts_osenv_init(erts_osenv_t *env);
void erts_osenv_clear(erts_osenv_t *env);
-/* @brief Merges \c with into \c env
+/** @brief Merges \c with into \c env
*
* @param overwrite Whether to overwrite existing entries or keep them as they
* are. */
@@ -61,25 +55,25 @@ void erts_osenv_merge(erts_osenv_t *env, const erts_osenv_t *with, int overwrite
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
-/* @brief Copies env[key] into \c value
+/** @brief Copies env[key] into \c value
*
* @return 1 on success, 0 if the key couldn't be found, and -1 if the input
* was invalid. */
int erts_osenv_get_term(const erts_osenv_t *env, struct process *process,
Eterm key, Eterm *value);
-/* @brief Copies \c value into \c env[key]
+/** @brief Copies \c value into \c env[key]
*
* @return 1 on success, -1 if the input was invalid. */
int erts_osenv_put_term(erts_osenv_t *env, Eterm key, Eterm value);
-/* @brief Removes \c env[key]
+/** @brief Removes \c env[key]
*
* @return 1 on success, 0 if the key couldn't be found, and -1 if the input
* was invalid. */
int erts_osenv_unset_term(erts_osenv_t *env, Eterm key);
-/* @brief Copies env[key] into \c value
+/** @brief Copies env[key] into \c value
*
* @param value [in,out] The buffer to copy the value into, may be NULL if you
* only wish to query presence.
@@ -89,13 +83,13 @@ int erts_osenv_unset_term(erts_osenv_t *env, Eterm key);
int erts_osenv_get_native(const erts_osenv_t *env, const erts_osenv_data_t *key,
erts_osenv_data_t *value);
-/* @brief Copies \c value into \c env[key]
+/** @brief Copies \c value into \c env[key]
*
* @return 1 on success, -1 on failure. */
int erts_osenv_put_native(erts_osenv_t *env, const erts_osenv_data_t *key,
const erts_osenv_data_t *value);
-/* @brief Removes \c key from the env.
+/** @brief Removes \c key from the env.
*
* @return 1 on success, 0 if the key couldn't be found. */
int erts_osenv_unset_native(erts_osenv_t *env, const erts_osenv_data_t *key);
@@ -109,8 +103,8 @@ typedef void (*erts_osenv_foreach_native_cb_t)(void *state,
const erts_osenv_data_t *key,
const erts_osenv_data_t *value);
-/* @brief Walks through all environment variables, calling \c callback for each
- * one. It's unsafe to modify \c env within the callback. */
+/** @brief Walks through all environment variables, calling \c callback for
+ * each one. It's unsafe to modify \c env within the callback. */
void erts_osenv_foreach_term(const erts_osenv_t *env, struct process *process,
void *state, erts_osenv_foreach_term_cb_t callback);
diff --git a/erts/emulator/sys/common/erl_poll.h b/erts/emulator/sys/common/erl_poll.h
index d40dabc529..096c14816e 100644
--- a/erts/emulator/sys/common/erl_poll.h
+++ b/erts/emulator/sys/common/erl_poll.h
@@ -1,8 +1,8 @@
/*
* %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.
* You may obtain a copy of the License at
@@ -161,7 +161,10 @@ typedef enum {
#include <sys/epoll.h>
#if ERTS_POLL_USE_EPOLL
-#ifdef HAVE_SYS_TIMERFD_H
+/* sys/timerfd.h was added in Android 4.4 KitKat. With the Android NDK
+Unified Headers, check that the build is targeting at least the
+corresponding API level 19. */
+#if defined(HAVE_SYS_TIMERFD_H) && !(defined(__ANDROID__) && (__ANDROID_API__ < 19))
#include <sys/timerfd.h>
#undef ERTS_POLL_USE_TIMERFD
#define ERTS_POLL_USE_TIMERFD 1
diff --git a/erts/emulator/sys/unix/erl_child_setup.c b/erts/emulator/sys/unix/erl_child_setup.c
index 129861ebd5..103eb40288 100644
--- a/erts/emulator/sys/unix/erl_child_setup.c
+++ b/erts/emulator/sys/unix/erl_child_setup.c
@@ -63,10 +63,13 @@
#include "erl_driver.h"
#include "sys_uds.h"
-#include "hash.h"
#include "erl_term.h"
#include "erl_child_setup.h"
+#undef ERTS_GLB_INLINE_INCL_FUNC_DEF
+#define ERTS_GLB_INLINE_INCL_FUNC_DEF 1
+#include "hash.h"
+
#define SET_CLOEXEC(fd) fcntl(fd, F_SETFD, fcntl(fd, F_GETFD) | FD_CLOEXEC)
#if defined(__ANDROID__)
@@ -75,6 +78,10 @@
#define SHELL "/bin/sh"
#endif /* __ANDROID__ */
+#if !defined(MSG_DONTWAIT) && defined(MSG_NONBLOCK)
+#define MSG_DONTWAIT MSG_NONBLOCK
+#endif
+
//#define HARD_DEBUG
#ifdef HARD_DEBUG
#define DEBUG_PRINT(fmt, ...) fprintf(stderr, "%d:" fmt "\r\n", getpid(), ##__VA_ARGS__)
@@ -411,6 +418,7 @@ main(int argc, char *argv[])
int uds_fd = 3, max_fd = 3;
#ifndef HAVE_CLOSEFROM
int i;
+ DIR *dir;
#endif
struct sigaction sa;
@@ -426,11 +434,29 @@ main(int argc, char *argv[])
#if defined(HAVE_CLOSEFROM)
closefrom(4);
#else
- for (i = 4; i < max_files; i++)
+ dir = opendir("/dev/fd");
+ if (dir == NULL) { /* /dev/fd not available */
+ for (i = 4; i < max_files; i++)
+#if defined(__ANDROID__)
+ if (i != system_properties_fd())
+#endif
+ (void) close(i);
+ } else {
+ /* Iterate over fds obtained from /dev/fd */
+ struct dirent *entry;
+ int dir_fd = dirfd(dir);
+
+ while ((entry = readdir(dir)) != NULL) {
+ i = atoi(entry->d_name);
#if defined(__ANDROID__)
- if (i != system_properties_fd())
+ if (i != system_properties_fd())
#endif
- (void) close(i);
+ if (i >= 4 && i != dir_fd)
+ (void) close(i);
+ }
+
+ closedir(dir);
+ }
#endif
if (pipe(sigchld_pipe) < 0) {
diff --git a/erts/emulator/sys/unix/sys.c b/erts/emulator/sys/unix/sys.c
index 6475a70fbf..a85426092e 100644
--- a/erts/emulator/sys/unix/sys.c
+++ b/erts/emulator/sys/unix/sys.c
@@ -692,6 +692,10 @@ void
erts_sys_unix_later_init(void)
{
sys_signal(SIGTERM, generic_signal_handler);
+
+ /* Ignore SIGCHLD to ensure orphaned processes don't turn into zombies on
+ * death when we're pid 1. */
+ sys_signal(SIGCHLD, SIG_IGN);
}
int sys_max_files(void)
@@ -743,10 +747,17 @@ void os_version(int *pMajor, int *pMinor, int *pBuild) {
* X.Y or X.Y.Z. */
(void) uname(&uts);
+#ifdef _AIX
+ /* AIX stores the major in version and minor in release */
+ *pMajor = atoi(uts.version);
+ *pMinor = atoi(uts.release);
+ *pBuild = 0; /* XXX: get oslevel for AIX or TR on i */
+#else
release = uts.release;
*pMajor = get_number(&release); /* Pointer to major version. */
*pMinor = get_number(&release); /* Pointer to minor version. */
*pBuild = get_number(&release); /* Pointer to build number. */
+#endif
}
void erts_do_break_handling(void)
diff --git a/erts/emulator/sys/unix/sys_drivers.c b/erts/emulator/sys/unix/sys_drivers.c
index 6883519dc3..b754f90004 100644
--- a/erts/emulator/sys/unix/sys_drivers.c
+++ b/erts/emulator/sys/unix/sys_drivers.c
@@ -55,6 +55,7 @@
#define WANT_NONBLOCKING /* must define this to pull in defs from sys.h */
#include "sys.h"
+#include "erl_osenv.h"
#include "erl_threads.h"
diff --git a/erts/emulator/sys/unix/sys_uds.c b/erts/emulator/sys/unix/sys_uds.c
index c9f73622ba..c3094d20d4 100644
--- a/erts/emulator/sys/unix/sys_uds.c
+++ b/erts/emulator/sys/unix/sys_uds.c
@@ -23,7 +23,7 @@
#endif
#if defined(__sun__) && !defined(_XOPEN_SOURCE)
-#define _XOPEN_SOURCE 500
+#define _XOPEN_SOURCE 600
#endif
#include <limits.h>
diff --git a/erts/emulator/sys/win32/sys.c b/erts/emulator/sys/win32/sys.c
index 53411a4cc2..6c3fe7ab0a 100644
--- a/erts/emulator/sys/win32/sys.c
+++ b/erts/emulator/sys/win32/sys.c
@@ -27,6 +27,7 @@
#endif
#include "sys.h"
+#include "erl_osenv.h"
#include "erl_alloc.h"
#include "erl_sys_driver.h"
#include "global.h"
diff --git a/erts/emulator/sys/win32/sys_env.c b/erts/emulator/sys/win32/sys_env.c
index c78161b344..36223c2c14 100644
--- a/erts/emulator/sys/win32/sys_env.c
+++ b/erts/emulator/sys/win32/sys_env.c
@@ -23,6 +23,7 @@
#endif
#include "sys.h"
+#include "erl_osenv.h"
#include "erl_sys_driver.h"
#include "erl_alloc.h"
diff --git a/erts/emulator/test/Makefile b/erts/emulator/test/Makefile
index 478555a902..fb56d72691 100644
--- a/erts/emulator/test/Makefile
+++ b/erts/emulator/test/Makefile
@@ -87,6 +87,7 @@ MODULES= \
gc_SUITE \
guard_SUITE \
hash_SUITE \
+ hash_property_test_SUITE \
hibernate_SUITE \
hipe_SUITE \
iovec_SUITE \
@@ -148,6 +149,7 @@ MODULES= \
dgawd_handler \
random_iolist \
erts_test_utils \
+ erts_test_destructor \
crypto_reference \
literal_area_collector_test
@@ -247,7 +249,7 @@ release_tests_spec: make_emakefile
$(INSTALL_DATA) $(NO_OPT_ERL_FILES) "$(RELSYSDIR)"
$(INSTALL_DATA) $(NATIVE_ERL_FILES) "$(RELSYSDIR)"
chmod -R u+w "$(RELSYSDIR)"
- tar cf - *_SUITE_data | (cd "$(RELSYSDIR)"; tar xf -)
+ tar cf - *_SUITE_data property_test | (cd "$(RELSYSDIR)"; tar xf -)
release_docs_spec:
diff --git a/erts/emulator/test/alloc_SUITE.erl b/erts/emulator/test/alloc_SUITE.erl
index 4e0243c1cd..24677247a7 100644
--- a/erts/emulator/test/alloc_SUITE.erl
+++ b/erts/emulator/test/alloc_SUITE.erl
@@ -302,45 +302,52 @@ wait_for_memory_deallocations() ->
end.
print_stats(migration) ->
- IFun = fun({instance,Inr,Istats}, {Bacc,Cacc,Pacc}) ->
- {mbcs,MBCS} = lists:keyfind(mbcs, 1, Istats),
- Btup = lists:keyfind(blocks, 1, MBCS),
- Ctup = lists:keyfind(carriers, 1, MBCS),
-
- Ptup = case lists:keyfind(mbcs_pool, 1, Istats) of
- {mbcs_pool,POOL} ->
- {blocks, Bpool} = lists:keyfind(blocks, 1, POOL),
- {carriers, Cpool} = lists:keyfind(carriers, 1, POOL),
- {pool, Bpool, Cpool};
- false ->
- {pool, 0, 0}
- end,
- io:format("{instance,~p,~p,~p,~p}}\n",
- [Inr, Btup, Ctup, Ptup]),
- {tuple_add(Bacc,Btup),tuple_add(Cacc,Ctup),
- tuple_add(Pacc,Ptup)};
- (_, Acc) -> Acc
+ IFun = fun({instance,_,Stats}, {Regular0, Pooled0}) ->
+ {mbcs,MBCS} = lists:keyfind(mbcs, 1, Stats),
+ {sbcs,SBCS} = lists:keyfind(sbcs, 1, Stats),
+
+ Regular = MBCS ++ SBCS ++ Regular0,
+ case lists:keyfind(mbcs_pool, 1, Stats) of
+ {mbcs_pool,Pool} -> {Regular, Pool ++ Pooled0};
+ false -> {Regular, Pooled0}
+ end;
+ (_, Acc) ->
+ Acc
end,
- {Btot,Ctot,Ptot} = lists:foldl(IFun,
- {{blocks,0,0,0},{carriers,0,0,0},{pool,0,0}},
- erlang:system_info({allocator,test_alloc})),
-
- {pool, PBtot, PCtot} = Ptot,
- io:format("Number of blocks : ~p\n", [Btot]),
- io:format("Number of carriers: ~p\n", [Ctot]),
- io:format("Number of pooled blocks : ~p\n", [PBtot]),
- io:format("Number of pooled carriers: ~p\n", [PCtot]);
-print_stats(_) -> ok.
-
-tuple_add(T1, T2) ->
- list_to_tuple(lists:zipwith(fun(E1,E2) when is_number(E1), is_number(E2) ->
- E1 + E2;
- (A,A) ->
- A
- end,
- tuple_to_list(T1), tuple_to_list(T2))).
+ Stats = erlang:system_info({allocator,test_alloc}),
+ {Regular, Pooled} = lists:foldl(IFun, {[], []}, Stats),
+ {RegBlocks, RegCarriers} = summarize_alloc_stats(Regular, {0, 0}),
+ {PooledBlocks, PooledCarriers} = summarize_alloc_stats(Pooled, {0, 0}),
+
+ io:format("Number of blocks : ~p\n", [RegBlocks]),
+ io:format("Number of carriers: ~p\n", [RegCarriers]),
+ io:format("Number of pooled blocks : ~p\n", [PooledBlocks]),
+ io:format("Number of pooled carriers: ~p\n", [PooledCarriers]);
+print_stats(_) ->
+ ok.
+
+summarize_alloc_stats([{blocks,L} | Rest], {Blocks0, Carriers}) ->
+ Blocks = count_blocks([S || {_Type, S} <- L], Blocks0),
+ summarize_alloc_stats(Rest, {Blocks, Carriers});
+summarize_alloc_stats([{carriers, Count, _, _} | Rest], {Blocks, Carriers0}) ->
+ summarize_alloc_stats(Rest, {Blocks, Carriers0 + Count});
+summarize_alloc_stats([{carriers, Count} | Rest], {Blocks, Carriers0}) ->
+ summarize_alloc_stats(Rest, {Blocks, Carriers0 + Count});
+summarize_alloc_stats([_ | Rest], Acc) ->
+ summarize_alloc_stats(Rest, Acc);
+summarize_alloc_stats([], Acc) ->
+ Acc.
+
+count_blocks([{count, Count, _, _} | Rest], Acc) ->
+ count_blocks(Rest, Acc + Count);
+count_blocks([{count, Count} | Rest], Acc) ->
+ count_blocks(Rest, Acc + Count);
+count_blocks([_ | Rest], Acc) ->
+ count_blocks(Rest, Acc);
+count_blocks([], Acc) ->
+ Acc.
one_shot(CaseName) ->
State = CaseName:start({1, 0, erlang:system_info(build_type)}),
diff --git a/erts/emulator/test/beam_SUITE.erl b/erts/emulator/test/beam_SUITE.erl
index d3b3b96b14..b1f1e115ac 100644
--- a/erts/emulator/test/beam_SUITE.erl
+++ b/erts/emulator/test/beam_SUITE.erl
@@ -132,7 +132,8 @@ packed_registers(Config) when is_list(Config) ->
"_ = id(2),\n"
"id([_@Vars,_@NewVars,_@MoreNewVars]).\n"
"id(I) -> I.\n"]),
- merl:compile_and_load(Code),
+
+ merl:compile_and_load(Code, []),
%% Optionally print the generated code.
PrintCode = false, %Change to true to print code.
diff --git a/erts/emulator/test/bif_SUITE.erl b/erts/emulator/test/bif_SUITE.erl
index c5abd04e07..1904ffdf93 100644
--- a/erts/emulator/test/bif_SUITE.erl
+++ b/erts/emulator/test/bif_SUITE.erl
@@ -29,8 +29,8 @@
shadow_comments/1,list_to_utf8_atom/1,
specs/1,improper_bif_stubs/1,auto_imports/1,
t_list_to_existing_atom/1,os_env/1,otp_7526/1,
- binary_to_atom/1,binary_to_existing_atom/1,
- atom_to_binary/1,min_max/1, erlang_halt/1,
+ t_binary_to_atom/1,t_binary_to_existing_atom/1,
+ t_atom_to_binary/1,min_max/1, erlang_halt/1,
erl_crash_dump_bytes/1,
is_builtin/1, error_stacktrace/1,
error_stacktrace_during_call_trace/1,
@@ -50,7 +50,7 @@ all() ->
specs, improper_bif_stubs, auto_imports,
t_list_to_existing_atom, os_env, otp_7526,
display, display_string, list_to_utf8_atom,
- atom_to_binary, binary_to_atom, binary_to_existing_atom,
+ t_atom_to_binary, t_binary_to_atom, t_binary_to_existing_atom,
erl_crash_dump_bytes, min_max, erlang_halt, is_builtin,
error_stacktrace, error_stacktrace_during_call_trace,
group_leader_prio, group_leader_prio_dirty,
@@ -496,7 +496,7 @@ test_7526(N) ->
-define(BADARG(E), {'EXIT',{badarg,_}} = (catch E)).
-define(SYS_LIMIT(E), {'EXIT',{system_limit,_}} = (catch E)).
-binary_to_atom(Config) when is_list(Config) ->
+t_binary_to_atom(Config) when is_list(Config) ->
HalfLong = lists:seq(0, 127),
HalfLongAtom = list_to_atom(HalfLong),
HalfLongBin = list_to_binary(HalfLong),
@@ -524,8 +524,10 @@ binary_to_atom(Config) when is_list(Config) ->
test_binary_to_atom(<<C/utf8>>, utf8)
end],
- <<"ã“ã‚“ã«ã¡ã¯"/utf8>> =
- atom_to_binary(test_binary_to_atom(<<"ã“ã‚“ã«ã¡ã¯"/utf8>>, utf8), utf8),
+ ExoticBin = <<"ã“ã‚“ã«ã¡ã¯"/utf8>>,
+ ExoticAtom = test_binary_to_atom(ExoticBin, utf8),
+ ExoticBin = atom_to_binary(ExoticAtom, utf8),
+ ExoticBin = atom_to_binary(ExoticAtom),
%% badarg failures.
fail_binary_to_atom(atom),
@@ -543,6 +545,7 @@ binary_to_atom(Config) when is_list(Config) ->
%% Bad UTF8 sequences.
?BADARG(binary_to_atom(id(<<255>>), utf8)),
+ ?BADARG(binary_to_atom(id(<<255>>))),
?BADARG(binary_to_atom(id(<<255,0>>), utf8)),
?BADARG(binary_to_atom(id(<<16#C0,16#80>>), utf8)), %Overlong 0.
<<B:1/binary, _/binary>> = id(<<194, 163>>), %Truncated character ERL-474
@@ -550,6 +553,7 @@ binary_to_atom(Config) when is_list(Config) ->
%% system_limit failures.
?SYS_LIMIT(binary_to_atom(id(<<0:512/unit:8,255>>), utf8)),
+ ?SYS_LIMIT(binary_to_atom(id(<<0:512/unit:8,255>>))),
?SYS_LIMIT(binary_to_atom(id(<<0:512/unit:8,255,0>>), utf8)),
?SYS_LIMIT(binary_to_atom(<<0:256/unit:8>>, latin1)),
?SYS_LIMIT(binary_to_atom(<<0:257/unit:8>>, latin1)),
@@ -562,6 +566,14 @@ binary_to_atom(Config) when is_list(Config) ->
test_binary_to_atom(Bin0, Encoding) ->
Res = binary_to_atom(Bin0, Encoding),
Res = binary_to_existing_atom(Bin0, Encoding),
+ if
+ Encoding =:= utf8;
+ Encoding =:= unicode ->
+ Res = binary_to_atom(Bin0),
+ Res = binary_to_existing_atom(Bin0);
+ true ->
+ ok
+ end,
Bin1 = id(<<7:3,Bin0/binary,32:5>>),
Sz = byte_size(Bin0),
<<_:3,UnalignedBin:Sz/binary,_:5>> = Bin1,
@@ -581,6 +593,12 @@ fail_binary_to_atom(Bin) ->
ok
end,
try
+ binary_to_atom(Bin)
+ catch
+ error:badarg ->
+ ok
+ end,
+ try
binary_to_existing_atom(Bin, latin1)
catch
error:badarg ->
@@ -591,10 +609,16 @@ fail_binary_to_atom(Bin) ->
catch
error:badarg ->
ok
+ end,
+ try
+ binary_to_existing_atom(Bin)
+ catch
+ error:badarg ->
+ ok
end.
-binary_to_existing_atom(Config) when is_list(Config) ->
+t_binary_to_existing_atom(Config) when is_list(Config) ->
UnlikelyBin = <<"ou0897979655678dsfj923874390867er869fds973qerueoru">>,
try
binary_to_existing_atom(UnlikelyBin, latin1),
@@ -609,6 +633,12 @@ binary_to_existing_atom(Config) when is_list(Config) ->
catch
error:badarg -> ok
end,
+ try
+ binary_to_existing_atom(UnlikelyBin),
+ ct:fail(atom_exists)
+ catch
+ error:badarg -> ok
+ end,
UnlikelyAtom = binary_to_atom(id(UnlikelyBin), latin1),
UnlikelyAtom = binary_to_existing_atom(UnlikelyBin, latin1),
@@ -625,7 +655,7 @@ binary_to_existing_atom(Config) when is_list(Config) ->
ok.
-atom_to_binary(Config) when is_list(Config) ->
+t_atom_to_binary(Config) when is_list(Config) ->
HalfLong = lists:seq(0, 127),
HalfLongAtom = list_to_atom(HalfLong),
HalfLongBin = list_to_binary(HalfLong),
@@ -641,12 +671,15 @@ atom_to_binary(Config) when is_list(Config) ->
LongBin = atom_to_binary(LongAtom, latin1),
%% utf8.
+ <<>> = atom_to_binary(''),
<<>> = atom_to_binary('', utf8),
<<>> = atom_to_binary('', unicode),
<<127>> = atom_to_binary('\177', utf8),
<<"abcdef">> = atom_to_binary(abcdef, utf8),
HalfLongBin = atom_to_binary(HalfLongAtom, utf8),
+ HalfLongBin = atom_to_binary(HalfLongAtom),
LongAtomBin = atom_to_binary(LongAtom, utf8),
+ LongAtomBin = atom_to_binary(LongAtom),
verify_long_atom_bin(LongAtomBin, 0),
%% Failing cases.
@@ -678,8 +711,15 @@ fail_atom_to_binary(Term) ->
catch
error:badarg ->
ok
+ end,
+ try
+ atom_to_binary(Term)
+ catch
+ error:badarg ->
+ ok
end.
+
min_max(Config) when is_list(Config) ->
a = erlang:min(id(a), a),
a = erlang:min(id(a), b),
@@ -860,6 +900,7 @@ error_stacktrace_test() ->
Types = [apply_const_last, apply_const, apply_last,
apply, double_apply_const_last, double_apply_const,
double_apply_last, double_apply, multi_apply_const_last,
+ apply_const_only, apply_only,
multi_apply_const, multi_apply_last, multi_apply,
call_const_last, call_last, call_const, call],
lists:foreach(fun (Type) ->
@@ -896,12 +937,18 @@ error_stacktrace_test() ->
Types),
ok.
-stk([], Type, Func) ->
- tail(Type, Func, jump),
- ok;
stk([_|L], Type, Func) ->
stk(L, Type, Func),
- ok.
+ %% Force the compiler to keep this body-recursive. We want the stack trace
+ %% to have one entry here and another in the base case to test that
+ %% multiple frames in the same function aren't removed unless they're
+ %% identical.
+ id(ok);
+stk([], Type, Func) ->
+ put(erlang, erlang),
+ put(tail, []),
+ tail(Type, Func, jump),
+ id(ok).
tail(Type, Func, jump) ->
tail(Type, Func, do);
@@ -910,6 +957,12 @@ tail(Type, error_1, do) ->
tail(Type, error_2, do) ->
do_error_2(Type).
+do_error_2(apply_const_only) ->
+ apply(erlang, error, [oops, [apply_const_only]]);
+do_error_2(apply_only) ->
+ Erlang = get(erlang),
+ Tail = get(tail),
+ apply(Erlang, error, [oops, [apply_only|Tail]]);
do_error_2(apply_const_last) ->
erlang:apply(erlang, error, [oops, [apply_const_last]]);
do_error_2(apply_const) ->
@@ -951,6 +1004,12 @@ do_error_2(call) ->
erlang:error(id(oops), id([call])).
+do_error_1(apply_const_only) ->
+ apply(erlang, error, [oops]);
+do_error_1(apply_only) ->
+ Erlang = get(erlang),
+ Tail = get(tail),
+ apply(Erlang, error, [oops|Tail]);
do_error_1(apply_const_last) ->
erlang:apply(erlang, error, [oops]);
do_error_1(apply_const) ->
diff --git a/erts/emulator/test/binary_SUITE.erl b/erts/emulator/test/binary_SUITE.erl
index cc4cbaad0e..6227148614 100644
--- a/erts/emulator/test/binary_SUITE.erl
+++ b/erts/emulator/test/binary_SUITE.erl
@@ -73,7 +73,8 @@
robustness/1,otp_8117/1,
otp_8180/1, trapping/1, large/1,
error_after_yield/1, cmp_old_impl/1,
- t2b_system_limit/1]).
+ t2b_system_limit/1,
+ term_to_iovec/1]).
%% Internal exports.
-export([sleeper/0,trapping_loop/4]).
@@ -92,8 +93,8 @@ all() ->
b2t_used_big,
bad_binary_to_term_2, safe_binary_to_term2,
bad_binary_to_term, bad_terms, t_hash, bad_size,
- sub_bin_copy,
- bad_term_to_binary, t2b_system_limit, more_bad_terms,
+ sub_bin_copy, bad_term_to_binary, t2b_system_limit,
+ term_to_iovec, more_bad_terms,
otp_5484, otp_5933,
ordering, unaligned_order, gc_test,
bit_sized_binary_sizes, otp_6817, otp_8117, deep,
@@ -456,6 +457,7 @@ bad_term_to_binary(Config) when is_list(Config) ->
T = id({a,b,c}),
{'EXIT',{badarg,_}} = (catch term_to_binary(T, not_a_list)),
{'EXIT',{badarg,_}} = (catch term_to_binary(T, [blurf])),
+ {'EXIT',{badarg,_}} = (catch term_to_binary(T, [iovec])),
{'EXIT',{badarg,_}} = (catch term_to_binary(T, [{compressed,-1}])),
{'EXIT',{badarg,_}} = (catch term_to_binary(T, [{compressed,10}])),
{'EXIT',{badarg,_}} = (catch term_to_binary(T, [{compressed,cucumber}])),
@@ -473,7 +475,8 @@ t2b_system_limit(Config) when is_list(Config) ->
memsup:get_system_memory_data()) of
Memory when is_integer(Memory),
Memory > 6*1024*1024*1024 ->
- test_t2b_system_limit(),
+ test_t2b_system_limit(term_to_binary, fun erlang:term_to_binary/1, fun erlang:term_to_binary/2),
+ test_t2b_system_limit(term_to_iovec, fun erlang:term_to_iovec/1, fun erlang:term_to_iovec/2),
garbage_collect(),
ok;
_ ->
@@ -483,37 +486,81 @@ t2b_system_limit(Config) when is_list(Config) ->
{skipped, "Only interesting on 64-bit builds"}
end.
-test_t2b_system_limit() ->
+test_t2b_system_limit(Name, F1, F2) ->
io:format("Creating HugeBin~n", []),
Bits = ((1 bsl 32)+1)*8,
HugeBin = <<0:Bits>>,
- io:format("Testing term_to_binary(HugeBin)~n", []),
- {'EXIT',{system_limit,[{erlang,term_to_binary,
+ io:format("Testing ~p(HugeBin)~n", [Name]),
+ {'EXIT',{system_limit,[{erlang,Name,
[HugeBin],
- _} |_]}} = (catch term_to_binary(HugeBin)),
+ _} |_]}} = (catch F1(HugeBin)),
- io:format("Testing term_to_binary(HugeBin, [compressed])~n", []),
- {'EXIT',{system_limit,[{erlang,term_to_binary,
+ io:format("Testing ~p(HugeBin, [compressed])~n", [Name]),
+ {'EXIT',{system_limit,[{erlang,Name,
[HugeBin, [compressed]],
- _} |_]}} = (catch term_to_binary(HugeBin, [compressed])),
+ _} |_]}} = (catch F2(HugeBin, [compressed])),
%% Check that it works also after we have trapped...
io:format("Creating HugeListBin~n", []),
HugeListBin = [lists:duplicate(2000000,2000000), HugeBin],
- io:format("Testing term_to_binary(HugeListBin)~n", []),
- {'EXIT',{system_limit,[{erlang,term_to_binary,
+ io:format("Testing ~p(HugeListBin)~n", [Name]),
+ {'EXIT',{system_limit,[{erlang,Name,
[HugeListBin],
- _} |_]}} = (catch term_to_binary(HugeListBin)),
+ _} |_]}} = (catch F1(HugeListBin)),
- io:format("Testing term_to_binary(HugeListBin, [compressed])~n", []),
- {'EXIT',{system_limit,[{erlang,term_to_binary,
+ io:format("Testing ~p(HugeListBin, [compressed])~n", [Name]),
+ {'EXIT',{system_limit,[{erlang,Name,
[HugeListBin, [compressed]],
- _} |_]}} = (catch term_to_binary(HugeListBin, [compressed])),
+ _} |_]}} = (catch F2(HugeListBin, [compressed])),
ok.
+term_to_iovec(Config) when is_list(Config) ->
+ Bin = list_to_binary(lists:duplicate(1000,100)),
+ Bin2 = list_to_binary(lists:duplicate(65,100)),
+ check_term_to_iovec({[Bin, atom, Bin, 1244, make_ref(), Bin]}),
+ check_term_to_iovec(Bin),
+ check_term_to_iovec([Bin,Bin,Bin,Bin]),
+ check_term_to_iovec(blipp),
+ check_term_to_iovec(lists:duplicate(1000,100)),
+ check_term_to_iovec([[Bin2]]),
+ check_term_to_iovec([erlang:ports(), Bin, erlang:processes()]),
+ ok.
+
+check_term_to_iovec(Term) ->
+ IoVec1 = erlang:term_to_iovec(Term),
+ ok = check_is_iovec(IoVec1),
+ IoVec2 = erlang:term_to_iovec(Term, []),
+ ok = check_is_iovec(IoVec2),
+ B = erlang:term_to_binary(Term),
+ IoVec1Bin = erlang:iolist_to_binary(IoVec1),
+ IoVec2Bin = erlang:iolist_to_binary(IoVec2),
+ try
+ B = IoVec1Bin
+ catch
+ _:_ ->
+ io:format("Binary: ~p~n", [B]),
+ io:format("I/O vec1 binary: ~p~n", [IoVec1Bin]),
+ io:format("I/O vec1: ~p~n", [IoVec1]),
+ ct:fail(not_same_result)
+ end,
+ try
+ B = IoVec2Bin
+ catch
+ _:_ ->
+ io:format("Binary: ~p~n", [B]),
+ io:format("I/O vec2 binary: ~p~n", [IoVec2Bin]),
+ io:format("I/O vec2: ~p~n", [IoVec2]),
+ ct:fail(not_same_result)
+ end.
+
+check_is_iovec([]) ->
+ ok;
+check_is_iovec([B|Bs]) when is_binary(B) ->
+ check_is_iovec(Bs).
+
%% Tests binary_to_term/1 and term_to_binary/1.
terms(Config) when is_list(Config) ->
@@ -1862,9 +1909,16 @@ huge_iolist(X, Sz, Lim) ->
huge_iolist([X, X], Sz*2, Lim).
cmp_node(Node, {M, F, A}) ->
- Res = rpc:call(Node, M, F, A),
+ ResN = rpc:call(Node, M, F, A),
Res = apply(M, F, A),
- ok.
+ case ResN =:= Res of
+ true ->
+ ok;
+ false ->
+ io:format("~p: ~p~n~p: ~p~n",
+ [Node, ResN, node(), Res]),
+ ct:fail(different_results)
+ end.
make_sub_binary(Bin) when is_binary(Bin) ->
{_,B} = split_binary(list_to_binary([0,1,3,Bin]), 3),
diff --git a/erts/emulator/test/call_trace_SUITE.erl b/erts/emulator/test/call_trace_SUITE.erl
index 742592f88e..477b0f5bb3 100644
--- a/erts/emulator/test/call_trace_SUITE.erl
+++ b/erts/emulator/test/call_trace_SUITE.erl
@@ -832,21 +832,27 @@ deep_exception() ->
R1 -> ct:fail({returned,abbr(R1)})
catch error:badarg -> ok
end,
- expect(fun ({trace,S,call,{lists,reverse,[L1,L2]}})
+ expect(fun ({trace,S,call,{lists,reverse,[L1,L2]}}, Traps)
when is_list(L1), is_list(L2), S == Self ->
- next;
+ %% Each trapping call to reverse/2 must have a corresponding
+ %% exception_from
+ {next, Traps + 1};
({trace,S,exception_from,
- {lists,reverse,2},{error,badarg}})
+ {lists,reverse,2},{error,badarg}}, Traps)
+ when S == Self, Traps > 1 ->
+ {next, Traps - 1};
+ ({trace,S,exception_from,
+ {lists,reverse,2},{error,badarg}}, 1)
when S == Self ->
expected;
- ('_') ->
+ ('_', _Traps) ->
{trace,Self,exception_from,
{lists,reverse,2},{error,badarg}};
- (_) ->
+ (_, _Traps) ->
{unexpected,
{trace,Self,exception_from,
{lists,reverse,2},{error,badarg}}}
- end),
+ end, 0),
deep_exception(?LINE, deep_5, [1,2], 7,
[{trace,Self,call,{erlang,error,[undef]}},
{trace,Self,exception_from,{erlang,error,1},
@@ -896,21 +902,27 @@ deep_exception() ->
R2 -> ct:fail({returned,abbr(R2)})
catch error:badarg -> ok
end,
- expect(fun ({trace,S,call,{lists,reverse,[L1,L2]}})
+ expect(fun ({trace,S,call,{lists,reverse,[L1,L2]}}, Traps)
when is_list(L1), is_list(L2), S == Self ->
- next;
+ %% Each trapping call to reverse/2 must have a corresponding
+ %% exception_from
+ {next, Traps + 1};
+ ({trace,S,exception_from,
+ {lists,reverse,2},{error,badarg}}, Traps)
+ when S == Self, Traps > 1 ->
+ {next, Traps - 1};
({trace,S,exception_from,
- {lists,reverse,2},{error,badarg}})
+ {lists,reverse,2},{error,badarg}}, 1)
when S == Self ->
expected;
- ('_') ->
+ ('_', _Traps) ->
{trace,Self,exception_from,
{lists,reverse,2},{error,badarg}};
- (_) ->
+ (_, _Traps) ->
{unexpected,
{trace,Self,exception_from,
{lists,reverse,2},{error,badarg}}}
- end),
+ end, 0),
deep_exception(?LINE, apply, [?MODULE,deep_5,[1,2]], 7,
[{trace,Self,call,{erlang,error,[undef]}},
{trace,Self,exception_from,{erlang,error,1},
@@ -975,21 +987,27 @@ deep_exception() ->
R3 -> ct:fail({returned,abbr(R3)})
catch error:badarg -> ok
end,
- expect(fun ({trace,S,call,{lists,reverse,[L1,L2]}})
+ expect(fun ({trace,S,call,{lists,reverse,[L1,L2]}}, Traps)
when is_list(L1), is_list(L2), S == Self ->
- next;
+ %% Each trapping call to reverse/2 must have a corresponding
+ %% exception_from
+ {next, Traps + 1};
+ ({trace,S,exception_from,
+ {lists,reverse,2},{error,badarg}}, Traps)
+ when S == Self, Traps > 1 ->
+ {next, Traps - 1};
({trace,S,exception_from,
- {lists,reverse,2},{error,badarg}})
+ {lists,reverse,2},{error,badarg}}, 1)
when S == Self ->
expected;
- ('_') ->
+ ('_', _Traps) ->
{trace,Self,exception_from,
{lists,reverse,2},{error,badarg}};
- (_) ->
+ (_, _Traps) ->
{unexpected,
{trace,Self,exception_from,
{lists,reverse,2},{error,badarg}}}
- end),
+ end, 0),
deep_exception(?LINE, apply,
[fun () -> ?MODULE:deep_5(1,2) end, []], 7,
[{trace,Self,call,{erlang,error,[undef]}},
@@ -1249,6 +1267,24 @@ expect(Message) ->
ct:fail(no_trace_message)
end.
+expect(Validator, State0) when is_function(Validator) ->
+ receive
+ M ->
+ case Validator(M, State0) of
+ expected ->
+ ok = io:format("Expected and got ~p", [abbr(M)]);
+ {next, State} ->
+ ok = io:format("Expected and got ~p", [abbr(M)]),
+ expect(Validator, State);
+ {unexpected,Message} ->
+ io:format("Expected ~p; got ~p", [abbr(Message),abbr(M)]),
+ ct:fail({unexpected,abbr([M|flush()])})
+ end
+ after 5000 ->
+ io:format("Expected ~p; got nothing", [abbr(Validator('_'))]),
+ ct:fail(no_trace_message)
+ end.
+
trace_info(What, Key) ->
get(tracer) ! {apply,self(),{erlang,trace_info,[What,Key]}},
Res = receive
diff --git a/erts/emulator/test/code_SUITE_data/call_purged_fun_tester.erl b/erts/emulator/test/code_SUITE_data/call_purged_fun_tester.erl
index 699f0c1161..b08cd5e654 100644
--- a/erts/emulator/test/code_SUITE_data/call_purged_fun_tester.erl
+++ b/erts/emulator/test/code_SUITE_data/call_purged_fun_tester.erl
@@ -8,8 +8,7 @@
do(Priv, Data, Type, Opts) ->
try do_it(Priv, Data, Type, Opts)
catch
- C:E ->
- ST = erlang:get_stacktrace(),
+ C:E:ST ->
io:format("Caught exception from line ~p:\n~p\n",
[get(the_line), ST]),
io:format("Message queue: ~p\n", [process_info(self(), messages)]),
diff --git a/erts/emulator/test/decode_packet_SUITE.erl b/erts/emulator/test/decode_packet_SUITE.erl
index ef13b515fb..099dfabcb6 100644
--- a/erts/emulator/test/decode_packet_SUITE.erl
+++ b/erts/emulator/test/decode_packet_SUITE.erl
@@ -301,8 +301,8 @@ http(Config) when is_list(Config) ->
StrA = list_to_atom(Str),
StrB = list_to_binary(Str),
Bin = <<StrB/binary,": ",ValB/binary,"\r\n",Rest/binary>>,
- {ok, {http_header,N,StrA,undefined,Val}, Rest} = decode_pkt(httph,Bin),
- {ok, {http_header,N,StrA,undefined,ValB}, Rest} = decode_pkt(httph_bin,Bin),
+ {ok, {http_header,N,StrA,Str,Val}, Rest} = decode_pkt(httph,Bin),
+ {ok, {http_header,N,StrA,StrB,ValB}, Rest} = decode_pkt(httph_bin,Bin),
N + 1
end,
lists:foldl(HdrF, 1, http_hdr_strings()),
@@ -330,6 +330,15 @@ http(Config) when is_list(Config) ->
%% Response with empty phrase
{ok,{http_response,{1,1},200,[]},<<>>} = decode_pkt(http, <<"HTTP/1.1 200\r\n">>, []),
{ok,{http_response,{1,1},200,<<>>},<<>>} = decode_pkt(http_bin, <<"HTTP/1.1 200\r\n">>, []),
+
+
+ %% Test error cases
+ {ok,{http_error,"Host\t: localhost:8000\r\n"},<<"a">>} =
+ decode_pkt(httph, <<"Host\t: localhost:8000\r\na">>, []),
+ {ok,{http_error,"Host : localhost:8000\r\n"},<<"a">>} =
+ decode_pkt(httph, <<"Host : localhost:8000\r\na">>, []),
+ {ok,{http_error," : localhost:8000\r\n"},<<"a">>} =
+ decode_pkt(httph, <<" : localhost:8000\r\na">>, []),
ok.
http_with_bin(http) ->
@@ -364,31 +373,40 @@ http_request(Msg) ->
{http_request, 'POST', {abs_path, "/invalid/url" }, {1,1}},
{http_request, 'POST', {abs_path,<<"/invalid/url">>}, {1,1}}},
{"Connection: close\r\n",
- {http_header,2,'Connection',undefined, "close"},
- {http_header,2,'Connection',undefined,<<"close">>}},
- {"Host\t : localhost:8000\r\n", % white space before :
- {http_header,14,'Host',undefined, "localhost:8000"},
- {http_header,14,'Host',undefined,<<"localhost:8000">>}},
+ {http_header,2,'Connection', "Connection" , "close"},
+ {http_header,2,'Connection',<<"Connection">>,<<"close">>}},
{"User-Agent: perl post\r\n",
- {http_header,24,'User-Agent',undefined, "perl post"},
- {http_header,24,'User-Agent',undefined,<<"perl post">>}},
+ {http_header,24,'User-Agent', "User-Agent" , "perl post"},
+ {http_header,24,'User-Agent',<<"User-Agent">>,<<"perl post">>}},
{"Content-Length: 4\r\n",
- {http_header,38,'Content-Length',undefined, "4"},
- {http_header,38,'Content-Length',undefined,<<"4">>}},
+ {http_header,38,'Content-Length', "Content-Length" , "4"},
+ {http_header,38,'Content-Length',<<"Content-Length">>,<<"4">>}},
{"Content-Type: text/xml; charset=utf-8\r\n",
- {http_header,42,'Content-Type',undefined, "text/xml; charset=utf-8"},
- {http_header,42,'Content-Type',undefined,<<"text/xml; charset=utf-8">>}},
+ {http_header,42,'Content-Type', "Content-Type" , "text/xml; charset=utf-8"},
+ {http_header,42,'Content-Type',<<"Content-Type">>,<<"text/xml; charset=utf-8">>}},
{"Other-Field: with some text\r\n",
- {http_header,0, "Other-Field" ,undefined, "with some text"},
- {http_header,0,<<"Other-Field">>,undefined,<<"with some text">>}},
+ {http_header,0, "Other-Field" , "Other-Field" , "with some text"},
+ {http_header,0,<<"Other-Field">>,<<"Other-Field">>,<<"with some text">>}},
+ {"Content--Type: text/xml; charset=utf-8\r\n",
+ {http_header,0, "Content--type" , "Content--Type" , "text/xml; charset=utf-8"},
+ {http_header,0,<<"Content--type">>,<<"Content--Type">>,<<"text/xml; charset=utf-8">>}},
+ {"Content---Type: text/xml; charset=utf-8\r\n",
+ {http_header,0, "Content---Type" , "Content---Type" , "text/xml; charset=utf-8"},
+ {http_header,0,<<"Content---Type">>,<<"Content---Type">>,<<"text/xml; charset=utf-8">>}},
+ {"CONTENT-type: text/xml; charset=utf-8\r\n",
+ {http_header,42,'Content-Type', "CONTENT-type" , "text/xml; charset=utf-8"},
+ {http_header,42,'Content-Type',<<"CONTENT-type">>,<<"text/xml; charset=utf-8">>}},
+ {"OTHER-field: with some text\r\n",
+ {http_header,0, "Other-Field" , "OTHER-field" , "with some text"},
+ {http_header,0,<<"Other-Field">>,<<"OTHER-field">>,<<"with some text">>}},
{"Make-sure-a-LONG-HEaDer-fIeLd-is-fORMATTED-NicelY: with some text\r\n",
- {http_header,0, "Make-Sure-A-Long-Header-Field-Is-Formatted-Nicely" ,undefined, "with some text"},
- {http_header,0,<<"Make-Sure-A-Long-Header-Field-Is-Formatted-Nicely">>,undefined,<<"with some text">>}},
+ {http_header,0, "Make-Sure-A-Long-Header-Field-Is-Formatted-Nicely" , "Make-sure-a-LONG-HEaDer-fIeLd-is-fORMATTED-NicelY" , "with some text"},
+ {http_header,0,<<"Make-Sure-A-Long-Header-Field-Is-Formatted-Nicely">>,<<"Make-sure-a-LONG-HEaDer-fIeLd-is-fORMATTED-NicelY">>,<<"with some text">>}},
{"Multi-Line: Once upon a time in a land far far away,\r\n"
" there lived a princess imprisoned in the highest tower\r\n"
" of the most haunted castle.\r\n",
- {http_header,0, "Multi-Line" ,undefined, "Once upon a time in a land far far away,\r\n there lived a princess imprisoned in the highest tower\r\n of the most haunted castle."},
- {http_header,0,<<"Multi-Line">>,undefined,<<"Once upon a time in a land far far away,\r\n there lived a princess imprisoned in the highest tower\r\n of the most haunted castle.">>}},
+ {http_header,0, "Multi-Line" , "Multi-Line" , "Once upon a time in a land far far away,\r\n there lived a princess imprisoned in the highest tower\r\n of the most haunted castle."},
+ {http_header,0,<<"Multi-Line">>,<<"Multi-Line">>,<<"Once upon a time in a land far far away,\r\n there lived a princess imprisoned in the highest tower\r\n of the most haunted castle.">>}},
{"\r\n",
http_eoh,
http_eoh}],
@@ -404,17 +422,17 @@ http_response(Msg) ->
{http_response, {1,0}, 404, "Object Not Found"},
{http_response, {1,0}, 404, <<"Object Not Found">>}},
{"Server: inets/4.7.16\r\n",
- {http_header, 30, 'Server', undefined, "inets/4.7.16"},
- {http_header, 30, 'Server', undefined, <<"inets/4.7.16">>}},
+ {http_header, 30, 'Server', "Server" , "inets/4.7.16"},
+ {http_header, 30, 'Server', <<"Server">>, <<"inets/4.7.16">>}},
{"Date: Fri, 04 Jul 2008 17:16:22 GMT\r\n",
- {http_header, 3, 'Date', undefined, "Fri, 04 Jul 2008 17:16:22 GMT"},
- {http_header, 3, 'Date', undefined, <<"Fri, 04 Jul 2008 17:16:22 GMT">>}},
+ {http_header, 3, 'Date', "Date" , "Fri, 04 Jul 2008 17:16:22 GMT"},
+ {http_header, 3, 'Date', <<"Date">>, <<"Fri, 04 Jul 2008 17:16:22 GMT">>}},
{"Content-Type: text/html\r\n",
- {http_header, 42, 'Content-Type', undefined, "text/html"},
- {http_header, 42, 'Content-Type', undefined, <<"text/html">>}},
+ {http_header, 42, 'Content-Type', "Content-Type" , "text/html"},
+ {http_header, 42, 'Content-Type', <<"Content-Type">>, <<"text/html">>}},
{"Content-Length: 207\r\n",
- {http_header, 38, 'Content-Length', undefined, "207"},
- {http_header, 38, 'Content-Length', undefined, <<"207">>}},
+ {http_header, 38, 'Content-Length', "Content-Length" , "207"},
+ {http_header, 38, 'Content-Length', <<"Content-Length">>, <<"207">>}},
{"\r\n",
http_eoh,
http_eoh}],
@@ -542,7 +560,7 @@ otp_8536_do(N) ->
Bin = <<Hdr/binary, ": ", Data/binary, "\r\n\r\n">>,
io:format("Bin='~p'\n",[Bin]),
- {ok,{http_header,0,Hdr2,undefined,Data2},<<"\r\n">>} = decode_pkt(httph_bin, Bin, []),
+ {ok,{http_header,0,Hdr2,Hdr2,Data2},<<"\r\n">>} = decode_pkt(httph_bin, Bin, []),
%% Do something to trash the C-stack, how about another decode_packet:
decode_pkt(httph_bin,<<Letters/binary, ": ", Data/binary, "\r\n\r\n">>, []),
diff --git a/erts/emulator/test/dirty_bif_SUITE.erl b/erts/emulator/test/dirty_bif_SUITE.erl
index 4f5ad0295a..2ded862b8a 100644
--- a/erts/emulator/test/dirty_bif_SUITE.erl
+++ b/erts/emulator/test/dirty_bif_SUITE.erl
@@ -397,7 +397,9 @@ dirty_process_trace(Config) when is_list(Config) ->
access_dirty_process(
Config,
fun() ->
- erlang:trace_pattern({erts_debug,dirty_io,2},
+ %% BIFs can only be traced when their modules are loaded.
+ code:ensure_loaded(erts_debug),
+ 1 = erlang:trace_pattern({erts_debug,dirty_io,2},
[{'_',[],[{return_trace}]}],
[local,meta]),
ok
diff --git a/erts/emulator/test/distribution_SUITE.erl b/erts/emulator/test/distribution_SUITE.erl
index 9dcdd60060..da246d8157 100644
--- a/erts/emulator/test/distribution_SUITE.erl
+++ b/erts/emulator/test/distribution_SUITE.erl
@@ -68,7 +68,9 @@
message_latency_large_link_exit/1,
message_latency_large_monitor_exit/1,
message_latency_large_exit2/1,
- system_limit/1]).
+ system_limit/1,
+ hopefull_data_encoding/1,
+ mk_hopefull_data/0]).
%% Internal exports.
-export([sender/3, receiver2/2, dummy_waiter/0, dead_process/0,
@@ -97,7 +99,8 @@ all() ->
contended_atom_cache_entry, contended_unicode_atom_cache_entry,
{group, message_latency},
{group, bad_dist}, {group, bad_dist_ext},
- start_epmd_false, epmd_module, system_limit].
+ start_epmd_false, epmd_module, system_limit,
+ hopefull_data_encoding].
groups() ->
[{bulk_send, [], [bulk_send_small, bulk_send_big, bulk_send_bigbig]},
@@ -2567,6 +2570,134 @@ address_please(_Name, _Address, _AddressFamily) ->
IP = {127,0,0,1},
{ok, IP}.
+hopefull_data_encoding(Config) when is_list(Config) ->
+ test_hopefull_data_encoding(Config, true),
+ test_hopefull_data_encoding(Config, false).
+
+test_hopefull_data_encoding(Config, Fallback) when is_list(Config) ->
+ {ok, ProxyNode} = start_node(hopefull_data_normal),
+ {ok, BouncerNode} = start_node(hopefull_data_bouncer, "-hidden"),
+ case Fallback of
+ false ->
+ ok;
+ true ->
+ rpc:call(BouncerNode, erts_debug, set_internal_state,
+ [available_internal_state, true]),
+ false = rpc:call(BouncerNode, erts_debug, set_internal_state,
+ [remove_hopefull_dflags, true])
+ end,
+ HData = mk_hopefull_data(),
+ Tester = self(),
+ R1 = make_ref(),
+ R2 = make_ref(),
+ R3 = make_ref(),
+ Bouncer = spawn_link(BouncerNode, fun () -> bounce_loop() end),
+ Proxy = spawn_link(ProxyNode,
+ fun () ->
+ register(bouncer, self()),
+ %% Verify same result between this node and tester
+ Tester ! [R1, HData],
+ %% Test when connection has not been setup yet
+ Bouncer ! {Tester, [R2, HData]},
+ Sync = make_ref(),
+ Bouncer ! {self(), Sync},
+ receive Sync -> ok end,
+ %% Test when connection is already up
+ Bouncer ! {Tester, [R3, HData]},
+ receive after infinity -> ok end
+ end),
+ receive
+ [R1, HData1] ->
+ Hdata = HData1
+ end,
+ receive
+ [R2, HData2] ->
+ case Fallback of
+ false ->
+ HData = HData2;
+ true ->
+ check_hopefull_fallback_data(Hdata, HData2)
+ end
+ end,
+ receive
+ [R3, HData3] ->
+ case Fallback of
+ false ->
+ HData = HData3;
+ true ->
+ check_hopefull_fallback_data(Hdata, HData3)
+ end
+ end,
+ unlink(Proxy),
+ exit(Proxy, bye),
+ unlink(Bouncer),
+ exit(Bouncer, bye),
+ stop_node(ProxyNode),
+ stop_node(BouncerNode),
+ ok.
+
+bounce_loop() ->
+ receive
+ {SendTo, Data} ->
+ SendTo ! Data
+ end,
+ bounce_loop().
+
+mk_hopefull_data() ->
+ HugeBs = list_to_bitstring([lists:duplicate(12*1024*1024, 85), <<6:6>>]),
+ <<_:1/bitstring,HugeBs2/bitstring>> = HugeBs,
+ lists:flatten([mk_hopefull_data(list_to_binary(lists:seq(1,255))),
+ 1234567890, HugeBs, fun gurka:banan/3, fun erlang:node/1,
+ self(), fun erlang:self/0,
+ mk_hopefull_data(list_to_binary(lists:seq(1,32))), an_atom,
+ fun lists:reverse/1, make_ref(), HugeBs2,
+ fun blipp:blapp/7]).
+
+mk_hopefull_data(BS) ->
+ BSsz = bit_size(BS),
+ [lists:map(fun (Offset) ->
+ <<_:Offset/bitstring, NewBs/bitstring>> = BS,
+ NewBs
+ end, lists:seq(1, 16)),
+ lists:map(fun (Offset) ->
+ <<NewBs:Offset/bitstring, _/bitstring>> = BS,
+ NewBs
+ end, lists:seq(BSsz-16, BSsz-1)),
+ lists:map(fun (Offset) ->
+ PreOffset = Offset rem 16,
+ <<_:PreOffset/bitstring, NewBs:Offset/bitstring, _/bitstring>> = BS,
+ NewBs
+ end, lists:seq(BSsz-32, BSsz-17))].
+
+
+check_hopefull_fallback_data([], []) ->
+ ok;
+check_hopefull_fallback_data([X|Xs],[Y|Ys]) ->
+ chk_hopefull_fallback(X, Y),
+ check_hopefull_fallback_data(Xs,Ys).
+
+chk_hopefull_fallback(Binary, FallbackBinary) when is_binary(Binary) ->
+ Binary = FallbackBinary;
+chk_hopefull_fallback(BitStr, {Bin, BitSize}) when is_bitstring(BitStr) ->
+ true = is_binary(Bin),
+ true = is_integer(BitSize),
+ true = BitSize > 0,
+ true = BitSize < 8,
+ Hsz = size(Bin) - 1,
+ <<Head:Hsz/binary, I/integer>> = Bin,
+ IBits = I bsr (8 - BitSize),
+ FallbackBitStr = list_to_bitstring([Head,<<IBits:BitSize>>]),
+ BitStr = FallbackBitStr,
+ ok;
+chk_hopefull_fallback(Func, {ModName, FuncName}) when is_function(Func) ->
+ {M, F, _} = erlang:fun_info_mfa(Func),
+ M = ModName,
+ F = FuncName,
+ ok;
+chk_hopefull_fallback(Other, SameOther) ->
+ Other = SameOther,
+ ok.
+
%%% Utilities
timestamp() ->
@@ -3034,3 +3165,5 @@ free_memory() ->
error : undef ->
ct:fail({"os_mon not built"})
end.
+
+
diff --git a/erts/emulator/test/emulator.spec b/erts/emulator/test/emulator.spec
index 7a6dd83020..087bd8880d 100644
--- a/erts/emulator/test/emulator.spec
+++ b/erts/emulator/test/emulator.spec
@@ -1,2 +1,3 @@
{enable_builtin_hooks, false}.
{suites,"../emulator_test",all}.
+{skip_groups,"../emulator_test",hash_SUITE,[phash2_benchmark],"Benchmark only"}.
diff --git a/erts/emulator/test/emulator_bench.spec b/erts/emulator/test/emulator_bench.spec
index 03638bfa23..8b1bb71a40 100644
--- a/erts/emulator/test/emulator_bench.spec
+++ b/erts/emulator/test/emulator_bench.spec
@@ -1,3 +1,4 @@
{groups,"../emulator_test",estone_SUITE,[estone_bench]}.
{groups,"../emulator_test",binary_SUITE,[iolist_size_benchmarks]}.
{groups,"../emulator_test",erts_debug_SUITE,[interpreter_size_bench]}.
+{groups,"../emulator_test",hash_SUITE,[phash2_benchmark]}.
diff --git a/erts/emulator/test/erts_test_destructor.erl b/erts/emulator/test/erts_test_destructor.erl
new file mode 100644
index 0000000000..311bb0aaf9
--- /dev/null
+++ b/erts/emulator/test/erts_test_destructor.erl
@@ -0,0 +1,41 @@
+%%
+%% %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%
+%%
+
+%% A NIF resource that sends a message in the destructor.
+
+-module(erts_test_destructor).
+
+-export([init/1, send/2]).
+
+init(Config) ->
+ case is_loaded() of
+ false ->
+ Path = proplists:get_value(data_dir, Config),
+ erlang:load_nif(filename:join(Path,?MODULE), []);
+ true ->
+ ok
+ end.
+
+is_loaded() ->
+ false.
+
+%% Create a resource which sends Msg to Pid when destructed.
+send(_Pid, _Msg) ->
+ erlang:nif_error("NIF not loaded").
diff --git a/erts/emulator/test/exception_SUITE.erl b/erts/emulator/test/exception_SUITE.erl
index 154bce3c35..e94a8d701b 100644
--- a/erts/emulator/test/exception_SUITE.erl
+++ b/erts/emulator/test/exception_SUITE.erl
@@ -303,57 +303,42 @@ maxbig_gc() ->
Maxbig.
stacktrace(Conf) when is_list(Conf) ->
- Tag = make_ref(),
- {_,Mref} = spawn_monitor(fun() -> exit({Tag,erlang:get_stacktrace()}) end),
- {Tag,[]} = receive {'DOWN',Mref,_,_,Info} -> Info end,
V = [make_ref()|self()],
- {value2,{caught1,badarg,[{erlang,abs,[V],_}|_]=St1}} =
- stacktrace_1({'abs',V}, error, {value,V}),
- St1 = erase(stacktrace1),
- St1 = erase(stacktrace2),
- St1 = erlang:get_stacktrace(),
- {caught2,{error,badarith},[{erlang,'+',[0,a],_},{?MODULE,my_add,2,_}|_]=St2} =
- stacktrace_1({'div',{1,0}}, error, {'add',{0,a}}),
- [{erlang,'div',[1,0],_},{?MODULE,my_div,2,_}|_] = erase(stacktrace1),
- St2 = erase(stacktrace2),
- St2 = erlang:get_stacktrace(),
- {caught2,{error,{try_clause,V}},[{?MODULE,stacktrace_1,3,_}|_]=St3} =
- stacktrace_1({value,V}, error, {value,V}),
- St3 = erase(stacktrace1),
- St3 = erase(stacktrace2),
- St3 = erlang:get_stacktrace(),
- {caught2,{throw,V},[{?MODULE,foo,1,_}|_]=St4} =
- stacktrace_1({value,V}, error, {throw,V}),
- [{?MODULE,stacktrace_1,3,_}|_] = erase(stacktrace1),
- St4 = erase(stacktrace2),
- St4 = erlang:get_stacktrace(),
+ {value2,{caught1,badarg,[{erlang,abs,[V],_}|_]}} =
+ stacktrace_1({'abs',V}, error, {value,V}),
+ {caught2,{error,badarith},[{erlang,'+',[0,a],_},{?MODULE,my_add,2,_}|_]} =
+ stacktrace_1({'div',{1,0}}, error, {'add',{0,a}}),
+ {caught2,{error,{try_clause,V}},[{?MODULE,stacktrace_1,3,_}|_]} =
+ stacktrace_1({value,V}, error, {value,V}),
+ {caught2,{throw,V},[{?MODULE,foo,1,_}|_]} =
+ stacktrace_1({value,V}, error, {throw,V}),
try
stacktrace_2()
catch
- error:{badmatch,_} ->
+ error:{badmatch,_}:Stk ->
[{?MODULE,stacktrace_2,0,_},
- {?MODULE,stacktrace,1,_}|_] =
- erlang:get_stacktrace(),
+ {?MODULE,stacktrace,1,_}|_] = Stk,
ok
end.
stacktrace_1(X, C1, Y) ->
- erase(stacktrace1),
- erase(stacktrace2),
try try foo(X) of
C1 -> value1
catch
- C1:D1 -> {caught1,D1,erlang:get_stacktrace()}
+ C1:D1:Stk1 ->
+ [] = erlang:get_stacktrace(),
+ {caught1,D1,Stk1}
after
- put(stacktrace1, erlang:get_stacktrace()),
foo(Y)
end of
V2 -> {value2,V2}
catch
- C2:D2 -> {caught2,{C2,D2},erlang:get_stacktrace()}
+ C2:D2:Stk2 ->
+ [] = erlang:get_stacktrace(),
+ {caught2,{C2,D2},Stk2}
after
- put(stacktrace2, erlang:get_stacktrace())
+ ok
end.
stacktrace_2() ->
@@ -364,76 +349,71 @@ stacktrace_2() ->
nested_stacktrace(Conf) when is_list(Conf) ->
V = [{make_ref()}|[self()]],
value1 =
- nested_stacktrace_1({{value,{V,x1}},void,{V,x1}},
- {void,void,void}),
+ nested_stacktrace_1({{value,{V,x1}},void,{V,x1}},
+ {void,void,void}),
{caught1,
[{erlang,'+',[V,x1],_},{?MODULE,my_add,2,_}|_],
- value2,
- [{erlang,'+',[V,x1],_},{?MODULE,my_add,2,_}|_]} =
- nested_stacktrace_1({{'add',{V,x1}},error,badarith},
- {{value,{V,x2}},void,{V,x2}}),
+ value2} =
+ nested_stacktrace_1({{'add',{V,x1}},error,badarith},
+ {{value,{V,x2}},void,{V,x2}}),
{caught1,
[{erlang,'+',[V,x1],_},{?MODULE,my_add,2,_}|_],
- {caught2,[{erlang,abs,[V],_}|_]},
- [{erlang,abs,[V],_}|_]} =
- nested_stacktrace_1({{'add',{V,x1}},error,badarith},
- {{'abs',V},error,badarg}),
+ {caught2,[{erlang,abs,[V],_}|_]}} =
+ nested_stacktrace_1({{'add',{V,x1}},error,badarith},
+ {{'abs',V},error,badarg}),
ok.
nested_stacktrace_1({X1,C1,V1}, {X2,C2,V2}) ->
try foo(X1) of
V1 -> value1
catch
- C1:V1 ->
- S1 = erlang:get_stacktrace(),
- T2 =
- try foo(X2) of
- V2 -> value2
- catch
- C2:V2 -> {caught2,erlang:get_stacktrace()}
+ C1:V1:S1 ->
+ T2 = try foo(X2) of
+ V2 -> value2
+ catch
+ C2:V2:S2 -> {caught2,S2}
end,
- {caught1,S1,T2,erlang:get_stacktrace()}
+ {caught1,S1,T2}
end.
raise(Conf) when is_list(Conf) ->
erase(raise),
- A =
- try
- try foo({'div',{1,0}})
+ A =
+ try
+ try foo({'div',{1,0}})
+ catch
+ error:badarith:A0 ->
+ put(raise, A0),
+ erlang:raise(error, badarith, A0)
+ end
catch
- error:badarith:A0 ->
- put(raise, A0 = erlang:get_stacktrace()),
- erlang:raise(error, badarith, A0)
- end
- catch
- error:badarith:A1 ->
- A1 = erlang:get_stacktrace(),
- A1 = get(raise)
- end,
- A = erlang:get_stacktrace(),
+ error:badarith:A1 ->
+ A1 = get(raise)
+ end,
A = get(raise),
[{erlang,'div',[1, 0], _},{?MODULE,my_div,2,_}|_] = A,
%%
N = 8, % Must be even
N = erlang:system_flag(backtrace_depth, N),
- B = odd_even(N, []),
- try even(N)
- catch error:function_clause -> ok
+ try
+ even(N)
+ catch
+ error:function_clause -> ok
end,
- B = erlang:get_stacktrace(),
%%
- C0 = odd_even(N+1, []),
- C = lists:sublist(C0, N),
- try odd(N+1)
- catch error:function_clause -> ok
+ C = odd_even(N+1, []),
+ try
+ odd(N+1)
+ catch
+ error:function_clause -> ok
end,
- C = erlang:get_stacktrace(),
- try erlang:raise(error, function_clause, C0)
- catch error:function_clause -> ok
+ try
+ erlang:raise(error, function_clause, C)
+ catch
+ error:function_clause -> ok
end,
- C = erlang:get_stacktrace(),
ok.
odd_even(N, R) when is_integer(N), N > 1 ->
@@ -601,11 +581,11 @@ do_exception_with_heap_frag(Bin, [Sz|Sizes]) ->
try
binary_to_term(Bin)
catch
- _:_ ->
+ _:_:Stk ->
%% term_to_binary/1 is an easy way to traverse the
%% entire stacktrace term to make sure that every part
%% of it is OK.
- term_to_binary(erlang:get_stacktrace())
+ term_to_binary(Stk)
end,
id(Filler)
end),
@@ -811,8 +791,8 @@ close_calls(Where) -> %Line 2
call2(), %Line 6
call3(), %Line 7
no_crash %Line 8
- catch error:crash ->
- erlang:get_stacktrace() %Line 10
+ catch error:crash:Stk ->
+ Stk %Line 10
end. %Line 11
call1() -> %Line 13
diff --git a/erts/emulator/test/hash_SUITE.erl b/erts/emulator/test/hash_SUITE.erl
index 3cbb3c7d5f..dd71c3da58 100644
--- a/erts/emulator/test/hash_SUITE.erl
+++ b/erts/emulator/test/hash_SUITE.erl
@@ -33,7 +33,25 @@
-module(hash_SUITE).
-export([basic_test/0,cmp_test/1,range_test/0,spread_test/1,
phash2_test/0, otp_5292_test/0,
- otp_7127_test/0]).
+ otp_7127_test/0,
+ run_phash2_benchmarks/0,
+ test_phash2_binary_aligned_and_unaligned_equal/1,
+ test_phash2_4GB_plus_bin/1,
+ test_phash2_10MB_plus_bin/1,
+ test_phash2_large_map/1,
+ test_phash2_shallow_long_list/1,
+ test_phash2_deep_list/1,
+ test_phash2_deep_tuple/1,
+ test_phash2_deep_tiny/1,
+ test_phash2_with_42/1,
+ test_phash2_with_short_tuple/1,
+ test_phash2_with_short_list/1,
+ test_phash2_with_tiny_bin/1,
+ test_phash2_with_tiny_unaligned_sub_binary/1,
+ test_phash2_with_small_unaligned_sub_binary/1,
+ test_phash2_with_large_bin/1,
+ test_phash2_with_large_unaligned_sub_binary/1,
+ test_phash2_with_super_large_unaligned_sub_binary/1]).
%%
%% Define to run outside of test server
@@ -43,13 +61,15 @@
%%
%% Define for debug output
%%
-%-define(debug,1).
+-define(debug,1).
-ifdef(STANDALONE).
-define(config(A,B),config(A,B)).
+-record(event, {name, data}).
-export([config/2]).
-else.
-include_lib("common_test/include/ct.hrl").
+-include_lib("common_test/include/ct_event.hrl").
-endif.
-ifdef(debug).
@@ -67,12 +87,15 @@
-ifdef(STANDALONE).
config(priv_dir,_) ->
".".
+notify(X) ->
+ erlang:display(X).
-else.
%% When run in test server.
--export([all/0, suite/0,
+-export([groups/0, all/0, suite/0,
test_basic/1,test_cmp/1,test_range/1,test_spread/1,
test_phash2/1,otp_5292/1,bit_level_binaries/1,otp_7127/1,
- test_hash_zero/1]).
+ test_hash_zero/1, init_per_suite/1, end_per_suite/1,
+ init_per_group/2, end_per_group/2]).
suite() ->
[{ct_hooks,[ts_install_cth]},
@@ -81,7 +104,71 @@ suite() ->
all() ->
[test_basic, test_cmp, test_range, test_spread,
test_phash2, otp_5292, bit_level_binaries, otp_7127,
- test_hash_zero].
+ test_hash_zero, test_phash2_binary_aligned_and_unaligned_equal,
+ test_phash2_4GB_plus_bin,
+ test_phash2_10MB_plus_bin,
+ {group, phash2_benchmark_tests},
+ {group, phash2_benchmark}].
+
+get_phash2_benchmarks() ->
+ [
+ test_phash2_large_map,
+ test_phash2_shallow_long_list,
+ test_phash2_deep_list,
+ test_phash2_deep_tuple,
+ test_phash2_deep_tiny,
+ test_phash2_with_42,
+ test_phash2_with_short_tuple,
+ test_phash2_with_short_list,
+ test_phash2_with_tiny_bin,
+ test_phash2_with_tiny_unaligned_sub_binary,
+ test_phash2_with_small_unaligned_sub_binary,
+ test_phash2_with_large_bin,
+ test_phash2_with_large_unaligned_sub_binary,
+ test_phash2_with_super_large_unaligned_sub_binary
+ ].
+
+groups() ->
+ [
+ {
+ phash2_benchmark_tests,
+ [],
+ get_phash2_benchmarks()
+ },
+ {
+ phash2_benchmark,
+ [],
+ get_phash2_benchmarks()
+ }
+ ].
+
+
+init_per_suite(Config) ->
+ io:format("START APPS~n"),
+ A0 = case application:start(sasl) of
+ ok -> [sasl];
+ _ -> []
+ end,
+ A = case application:start(os_mon) of
+ ok -> [os_mon|A0];
+ _ -> A0
+ end,
+ io:format("APPS STARTED~n"),
+ [{started_apps, A}|Config].
+
+end_per_suite(Config) ->
+ As = proplists:get_value(started_apps, Config),
+ lists:foreach(fun (A) -> application:stop(A) end, As),
+ Config.
+
+init_per_group(phash2_benchmark_tests, Config) ->
+ [phash2_benchmark_tests |Config];
+init_per_group(_, Config) ->
+ Config.
+
+end_per_group(_, Config) ->
+ Config.
+
%% Tests basic functionality of erlang:phash and that the
%% hashes has not changed (neither hash nor phash)
@@ -119,6 +206,9 @@ otp_7127(Config) when is_list(Config) ->
test_hash_zero(Config) when is_list(Config) ->
hash_zero_test().
+
+notify(X) ->
+ ct_event:notify(X).
-endif.
@@ -133,26 +223,17 @@ basic_test() ->
16#77777777777777],16#FFFFFFFF),
ExternalReference = <<131,114,0,3,100,0,13,110,111,110,111,100,101,64,
110,111,104,111,115,116,0,0,0,0,122,0,0,0,0,0,0,0,0>>,
- 1113403635 = erlang:phash(binary_to_term(ExternalReference),
- 16#FFFFFFFF),
- ExternalFun = <<131,117,0,0,0,3,103,100,0,13,110,111,110,111,100,101,64,
- 110,111,104,111,115,116,0,0,0,38,0,0,0,0,0,100,0,8,101,
- 114,108,95,101,118,97,108,97,20,98,5,182,139,98,108,0,0,
- 0,3,104,2,100,0,1,66,109,0,0,0,33,131,114,0,3,100,0,13,
- 110,111,110,111,100,101,64,110,111,104,111,115,116,0,0,
- 0,0,122,0,0,0,0,0,0,0,0,104,2,100,0,1,76,107,0,33,131,
- 114,0,3,100,0,13,110,111,110,111,100,101,64,110,111,104,
- 111,115,116,0,0,0,0,122,0,0,0,0,0,0,0,0,104,2,100,0,1,82,
- 114,0,3,100,0,13,110,111,110,111,100,101,64,110,111,104,
- 111,115,116,0,0,0,0,122,0,0,0,0,0,0,0,0,106,108,0,0,0,1,
- 104,5,100,0,6,99,108,97,117,115,101,97,1,106,106,108,0,0,
- 0,1,104,3,100,0,7,105,110,116,101,103,101,114,97,1,97,1,
- 106,106,104,3,100,0,4,101,118,97,108,104,2,100,0,5,115,
- 104,101,108,108,100,0,10,108,111,99,97,108,95,102,117,
- 110,99,108,0,0,0,1,103,100,0,13,110,111,110,111,100,101,
- 64,110,111,104,111,115,116,0,0,0,22,0,0,0,0,0,106>>,
- 170987488 = erlang:phash(binary_to_term(ExternalFun),
- 16#FFFFFFFF),
+ ExternalReference = <<131,114,0,3,100,0,13,110,111,110,111,100,101,64,
+ 110,111,104,111,115,116,0,0,0,0,122,0,0,0,0,0,0,0,0>>,
+ 1113403635 = phash_from_external(ExternalReference),
+
+ ExternalFun = <<131,112,0,0,0,70,1,212,190,220,28,179,144,194,131,
+ 19,215,105,97,77,251,125,93,0,0,0,0,0,0,0,2,100,0,1,
+ 116,97,0,98,6,165,246,224,103,100,0,13,110,111,
+ 110,111,100,101,64,110,111,104,111,115,116,0,0,0,91,
+ 0,0,0,0,0,97,2,97,1>>,
+ 25769064 = phash_from_external(ExternalFun),
+
case (catch erlang:phash(1,0)) of
{'EXIT',{badarg, _}} ->
ok;
@@ -160,6 +241,8 @@ basic_test() ->
exit(phash_accepted_zero_as_range)
end.
+phash_from_external(Ext) ->
+ erlang:phash(binary_to_term(Ext), 16#FFFFFFFF).
range_test() ->
F = fun(From,From,_FF) ->
@@ -354,6 +437,7 @@ phash2_test() ->
%% bit-level binaries
{<<0:7>>, 1055790816},
+ {(fun()-> B = <<255,7:3>>, <<_:4,D/bitstring>> = B, D end)(), 911751529},
{<<"abc",13:4>>, 670412287},
{<<5:3,"12345678901234567890">>, 289973273},
@@ -424,6 +508,159 @@ phash2_test() ->
[] = [{E,H,H2} || {E,H} <- L, (H2 = erlang:phash2(E, Max)) =/= H],
ok.
+test_phash2_binary_aligned_and_unaligned_equal(Config) when is_list(Config) ->
+ erts_debug:set_internal_state(available_internal_state, true),
+ test_aligned_and_unaligned_equal_up_to(256*12+255),
+ erts_debug:set_internal_state(available_internal_state, false).
+
+test_aligned_and_unaligned_equal_up_to(BinSize) ->
+ Results =
+ lists:map(fun(Size) ->
+ test_aligned_and_unaligned_equal(Size)
+ end, lists:seq(1, BinSize)),
+ %% DataDir = filename:join(filename:dirname(code:which(?MODULE)), "hash_SUITE_data"),
+ %% ExpResFile = filename:join(DataDir, "phash2_bin_expected_results.txt"),
+ %% {ok, [ExpRes]} = file:consult(ExpResFile),
+ %% %% ok = file:write_file(ExpResFile, io_lib:format("~w.~n", [Results])),
+ %% Results = ExpRes,
+ 110469206 = erlang:phash2(Results).
+
+test_aligned_and_unaligned_equal(BinSize) ->
+ Bin = make_random_bin(BinSize),
+ LastByte = last_byte(Bin),
+ LastInBitstring = LastByte rem 11,
+ Bitstring = << Bin/binary, <<LastInBitstring:5>>/bitstring >>,
+ UnalignedBin = make_unaligned_sub_bitstring(Bin),
+ UnalignedBitstring = make_unaligned_sub_bitstring(Bitstring),
+ case erts_debug:get_internal_state(available_internal_state) of
+ false -> erts_debug:set_internal_state(available_internal_state, true);
+ _ -> ok
+ end,
+ erts_debug:set_internal_state(reds_left, 3),
+ BinHash = erlang:phash2(Bin),
+ BinHash = erlang:phash2(Bin),
+ erts_debug:set_internal_state(reds_left, 3),
+ UnalignedBinHash = erlang:phash2(UnalignedBin),
+ UnalignedBinHash = erlang:phash2(UnalignedBin),
+ BinHash = UnalignedBinHash,
+ erts_debug:set_internal_state(reds_left, 3),
+ BitstringHash = erlang:phash2(Bitstring),
+ BitstringHash = erlang:phash2(Bitstring),
+ erts_debug:set_internal_state(reds_left, 3),
+ UnalignedBitstringHash = erlang:phash2(UnalignedBitstring),
+ UnalignedBitstringHash = erlang:phash2(UnalignedBitstring),
+ BitstringHash = UnalignedBitstringHash,
+ {BinHash, BitstringHash}.
+
+last_byte(Bin) ->
+ NotLastByteSize = (erlang:bit_size(Bin)) - 8,
+ <<_:NotLastByteSize/bitstring, LastByte:8>> = Bin,
+ LastByte.
+
+test_phash2_4GB_plus_bin(Config) when is_list(Config) ->
+ run_when_enough_resources(
+ fun() ->
+ erts_debug:set_internal_state(available_internal_state, true),
+ %% Created Bin4GB here so it only needs to be created once
+ erts_debug:set_internal_state(force_gc, self()),
+ Bin4GB = get_4GB_bin(),
+ test_phash2_plus_bin_helper1(Bin4GB, <<>>, <<>>, 13708901),
+ erts_debug:set_internal_state(force_gc, self()),
+ test_phash2_plus_bin_helper1(Bin4GB, <<>>, <<3:5>>, 66617678),
+ erts_debug:set_internal_state(force_gc, self()),
+ test_phash2_plus_bin_helper1(Bin4GB, <<13>>, <<>>, 31308392),
+ erts_debug:set_internal_state(force_gc, self()),
+ erts_debug:set_internal_state(available_internal_state, false)
+ end).
+
+
+test_phash2_10MB_plus_bin(Config) when is_list(Config) ->
+ erts_debug:set_internal_state(available_internal_state, true),
+ erts_debug:set_internal_state(force_gc, self()),
+ Bin10MB = get_10MB_bin(),
+ test_phash2_plus_bin_helper1(Bin10MB, <<>>, <<>>, 22776267),
+ erts_debug:set_internal_state(force_gc, self()),
+ test_phash2_plus_bin_helper1(Bin10MB, <<>>, <<3:5>>, 124488972),
+ erts_debug:set_internal_state(force_gc, self()),
+ test_phash2_plus_bin_helper1(Bin10MB, <<13>>, <<>>, 72958346),
+ erts_debug:set_internal_state(force_gc, self()),
+ erts_debug:set_internal_state(available_internal_state, false).
+
+get_10MB_bin() ->
+ TmpBin = make_random_bin(10239),
+ Bin = erlang:iolist_to_binary([0, TmpBin]),
+ IOList10MB = duplicate_iolist(Bin, 10),
+ Bin10MB = erlang:iolist_to_binary(IOList10MB),
+ 10485760 = size(Bin10MB),
+ Bin10MB.
+
+get_4GB_bin() ->
+ TmpBin = make_random_bin(65535),
+ Bin = erlang:iolist_to_binary([0, TmpBin]),
+ IOList4GB = duplicate_iolist(Bin, 16),
+ Bin4GB = erlang:iolist_to_binary(IOList4GB),
+ 4294967296 = size(Bin4GB),
+ Bin4GB.
+
+duplicate_iolist(IOList, 0) ->
+ IOList;
+duplicate_iolist(IOList, NrOfTimes) ->
+ duplicate_iolist([IOList, IOList], NrOfTimes - 1).
+
+test_phash2_plus_bin_helper1(Bin4GB, ExtraBytes, ExtraBits, ExpectedHash) ->
+ test_phash2_plus_bin_helper2(Bin4GB, fun id/1, ExtraBytes, ExtraBits, ExpectedHash),
+ test_phash2_plus_bin_helper2(Bin4GB, fun make_unaligned_sub_bitstring/1, ExtraBytes, ExtraBits, ExpectedHash).
+
+test_phash2_plus_bin_helper2(Bin, TransformerFun, ExtraBytes, ExtraBits, ExpectedHash) ->
+ ExtraBitstring = << ExtraBytes/binary, ExtraBits/bitstring >>,
+ LargerBitstring = << ExtraBytes/binary,
+ ExtraBits/bitstring,
+ Bin/bitstring >>,
+ LargerTransformedBitstring = TransformerFun(LargerBitstring),
+ ExtraBitstringHash = erlang:phash2(ExtraBitstring),
+ ExpectedHash =
+ case size(LargerTransformedBitstring) < 4294967296 of
+ true ->
+ erts_debug:set_internal_state(force_gc, self()),
+ erts_debug:set_internal_state(reds_left, 1),
+ Hash = erlang:phash2(LargerTransformedBitstring),
+ Hash = erlang:phash2(LargerTransformedBitstring),
+ Hash;
+ false ->
+ erts_debug:set_internal_state(force_gc, self()),
+ erts_debug:set_internal_state(reds_left, 1),
+ ExtraBitstringHash = erlang:phash2(LargerTransformedBitstring),
+ ExtraBitstringHash = erlang:phash2(LargerTransformedBitstring),
+ ExtraBitstringHash
+ end.
+
+run_when_enough_resources(Fun) ->
+ case {total_memory(), erlang:system_info(wordsize)} of
+ {Mem, 8} when is_integer(Mem) andalso Mem >= 31 ->
+ Fun();
+ {Mem, WordSize} ->
+ {skipped,
+ io_lib:format("Not enough resources (System Memory >= ~p, Word Size = ~p)",
+ [Mem, WordSize])}
+ end.
+
+%% Total memory in GB
+total_memory() ->
+ try
+ MemoryData = memsup:get_system_memory_data(),
+ case lists:keysearch(total_memory, 1, MemoryData) of
+ {value, {total_memory, TM}} ->
+ TM div (1024*1024*1024);
+ false ->
+ {value, {system_total_memory, STM}} =
+ lists:keysearch(system_total_memory, 1, MemoryData),
+ STM div (1024*1024*1024)
+ end
+ catch
+ _ : _ ->
+ undefined
+ end.
+
-ifdef(FALSE).
f1() ->
abc.
@@ -436,14 +673,23 @@ f3(X, Y) ->
-endif.
otp_5292_test() ->
- PH = fun(E) -> [erlang:phash(E, 1 bsl 32),
- erlang:phash(-E, 1 bsl 32),
- erlang:phash2(E, 1 bsl 32),
- erlang:phash2(-E, 1 bsl 32)]
- end,
+ PH = fun(E) ->
+ EInList = [1, 2, 3, E],
+ EInList2 = [E, 1, 2, 3],
+ NegEInList = [1, 2, 3, -E],
+ NegEInList2 = [-E, 1, 2, 3],
+ [erlang:phash(E, 1 bsl 32),
+ erlang:phash(-E, 1 bsl 32),
+ erlang:phash2(E, 1 bsl 32),
+ erlang:phash2(-E, 1 bsl 32),
+ erlang:phash2(EInList, 1 bsl 32),
+ erlang:phash2(EInList2, 1 bsl 32),
+ erlang:phash2(NegEInList, 1 bsl 32),
+ erlang:phash2(NegEInList2, 1 bsl 32)]
+ end,
S2 = md5([md5(hash_int(S, E, PH)) || {Start, N, Sz} <- d(),
{S, E} <- int(Start, N, Sz)]),
- <<124,81,198,121,174,233,19,137,10,83,33,80,226,111,238,99>> = S2,
+ <<234,63,192,76,253,57,250,32,44,11,73,1,161,102,14,238>> = S2,
ok.
d() ->
@@ -684,3 +930,313 @@ unaligned_sub_bitstr(Bin0) when is_bitstring(Bin0) ->
id(I) -> I.
+
+%% Benchmarks for phash2
+
+run_phash2_benchmarks() ->
+ Benchmarks = [
+ test_phash2_large_map,
+ test_phash2_shallow_long_list,
+ test_phash2_deep_list,
+ test_phash2_deep_tuple,
+ test_phash2_deep_tiny,
+ test_phash2_with_42,
+ test_phash2_with_short_tuple,
+ test_phash2_with_short_list,
+ test_phash2_with_tiny_bin,
+ test_phash2_with_tiny_unaligned_sub_binary,
+ test_phash2_with_small_unaligned_sub_binary,
+ test_phash2_with_large_bin,
+ test_phash2_with_large_unaligned_sub_binary,
+ test_phash2_with_super_large_unaligned_sub_binary
+ ],
+ [print_comment(B) || B <- Benchmarks].
+
+
+print_comment(FunctionName) ->
+ io:format("~p~n", [FunctionName]),
+ io:format("~s~n", [element(2, erlang:apply(?MODULE, FunctionName, [[]]))]).
+
+nr_of_iters(BenchmarkNumberOfIterations, Config) ->
+ case lists:member(phash2_benchmark_tests, Config) of
+ true -> 1;
+ false -> BenchmarkNumberOfIterations
+ end.
+
+
+test_phash2_large_map(Config) when is_list(Config) ->
+ {Size, ExpectedHash} =
+ case {total_memory(), erlang:system_info(wordsize)} of
+ {Mem, 8} when is_integer(Mem) andalso Mem > 2 ->
+ {1000000, 121857429};
+ _ ->
+ {1000, 66609305}
+ end,
+ run_phash2_test_and_benchmark(nr_of_iters(45, Config),
+ get_map(Size),
+ ExpectedHash).
+
+test_phash2_shallow_long_list(Config) when is_list(Config) ->
+ {Size, ExpectedHash} =
+ case {total_memory(), erlang:system_info(wordsize)} of
+ {Mem, 8} when is_integer(Mem) andalso Mem > 2 ->
+ {1000000, 78700388};
+ _ ->
+ {1000, 54749638}
+ end,
+ run_phash2_test_and_benchmark(nr_of_iters(1, Config),
+ lists:duplicate(Size, get_complex_tuple()),
+ ExpectedHash).
+
+test_phash2_deep_list(Config) when is_list(Config) ->
+ {Size, ExpectedHash} =
+ case {total_memory(), erlang:system_info(wordsize)} of
+ {Mem, 8} when is_integer(Mem) andalso Mem > 2 ->
+ {500000, 17986444};
+ _ ->
+ {1000, 81794308}
+ end,
+ run_phash2_test_and_benchmark(nr_of_iters(1, Config),
+ make_deep_list(Size, get_complex_tuple()),
+ ExpectedHash).
+
+test_phash2_deep_tuple(Config) when is_list(Config) ->
+ {Size, ExpectedHash} =
+ case {total_memory(), erlang:system_info(wordsize)} of
+ {Mem, 8} when is_integer(Mem) andalso Mem > 2 ->
+ {500000, 116594715};
+ _ ->
+ {500, 109057352}
+ end,
+ run_phash2_test_and_benchmark(nr_of_iters(1, Config),
+ make_deep_tuple(Size, get_complex_tuple()),
+ ExpectedHash).
+
+test_phash2_deep_tiny(Config) when is_list(Config) ->
+ run_phash2_test_and_benchmark(nr_of_iters(1000000, Config),
+ make_deep_list(19, 42),
+ 111589624).
+
+test_phash2_with_42(Config) when is_list(Config) ->
+ run_phash2_test_and_benchmark(nr_of_iters(20000000, Config),
+ 42,
+ 30328728).
+
+test_phash2_with_short_tuple(Config) when is_list(Config) ->
+ run_phash2_test_and_benchmark(nr_of_iters(10000000, Config),
+ {a,b,<<"hej">>, "hej"},
+ 50727199).
+
+test_phash2_with_short_list(Config) when is_list(Config) ->
+ run_phash2_test_and_benchmark(nr_of_iters(10000000, Config),
+ [a,b,"hej", "hello"],
+ 117108642).
+
+test_phash2_with_tiny_bin(Config) when is_list(Config) ->
+ run_phash2_test_and_benchmark(nr_of_iters(20000000, Config),
+ make_random_bin(10),
+ 129616602).
+
+test_phash2_with_tiny_unaligned_sub_binary(Config) when is_list(Config) ->
+ run_phash2_test_and_benchmark(nr_of_iters(10000000, Config),
+ make_unaligned_sub_binary(make_random_bin(11)),
+ 59364725).
+
+test_phash2_with_small_unaligned_sub_binary(Config) when is_list(Config) ->
+ run_phash2_test_and_benchmark(nr_of_iters(400000, Config),
+ make_unaligned_sub_binary(make_random_bin(1001)),
+ 130388119).
+
+test_phash2_with_large_bin(Config) when is_list(Config) ->
+ {Size, ExpectedHash} =
+ case {total_memory(), erlang:system_info(wordsize)} of
+ {Mem, 8} when is_integer(Mem) andalso Mem > 2 ->
+ {10000000, 48249379};
+ _ ->
+ {1042, 14679520}
+ end,
+ run_phash2_test_and_benchmark(nr_of_iters(150, Config),
+ make_random_bin(Size),
+ ExpectedHash).
+
+test_phash2_with_large_unaligned_sub_binary(Config) when is_list(Config) ->
+ {Size, ExpectedHash} =
+ case {total_memory(), erlang:system_info(wordsize)} of
+ {Mem, 8} when is_integer(Mem) andalso Mem > 2 ->
+ {10000001, 122836437};
+ _ ->
+ {10042, 127144287}
+ end,
+ run_phash2_test_and_benchmark(nr_of_iters(50, Config),
+ make_unaligned_sub_binary(make_random_bin(Size)),
+ ExpectedHash).
+
+test_phash2_with_super_large_unaligned_sub_binary(Config) when is_list(Config) ->
+ {Size, ExpectedHash} =
+ case {total_memory(), erlang:system_info(wordsize)} of
+ {Mem, 8} when is_integer(Mem) andalso Mem > 2 ->
+ {20000001, 112086727};
+ _ ->
+ {20042, 91996619}
+ end,
+ run_phash2_test_and_benchmark(nr_of_iters(20, Config),
+ make_unaligned_sub_binary(make_random_bin(Size)),
+ ExpectedHash).
+
+make_deep_list(1, Item) ->
+ {Item, Item};
+make_deep_list(Depth, Item) ->
+ [{Item, Item}, make_deep_list(Depth - 1, Item)].
+
+make_deep_tuple(1, Item) ->
+ [Item, Item];
+make_deep_tuple(Depth, Item) ->
+ {[Item, Item], make_deep_tuple(Depth - 1, Item)}.
+
+% Helper functions for benchmarking
+
+loop(0, _) -> ok;
+loop(Iterations, Fun) ->
+ Fun(),
+ loop(Iterations - 1, Fun).
+
+run_phash2_test_and_benchmark(Iterations, Term, ExpectedHash) ->
+ Parent = self(),
+ Test =
+ fun() ->
+ Hash = erlang:phash2(Term),
+ case ExpectedHash =:= Hash of
+ false ->
+ Parent ! {got_bad_hash, Hash},
+ ExpectedHash = Hash;
+ _ -> ok
+ end
+ end,
+ Benchmark =
+ fun() ->
+ garbage_collect(),
+ {Time, _} =timer:tc(fun() -> loop(Iterations, Test) end),
+ Parent ! Time
+ end,
+ spawn(Benchmark),
+ receive
+ {got_bad_hash, Hash} ->
+ ExpectedHash = Hash;
+ Time ->
+ TimeInS = case (Time/1000000) of
+ 0.0 -> 0.0000000001;
+ T -> T
+ end,
+ IterationsPerSecond = Iterations / TimeInS,
+ notify(#event{ name = benchmark_data, data = [{value, IterationsPerSecond}]}),
+ {comment, io_lib:format("Iterations per second: ~p, Iterations ~p, Benchmark time: ~p seconds)",
+ [IterationsPerSecond, Iterations, Time/1000000])}
+ end.
+
+get_complex_tuple() ->
+ BPort = <<131,102,100,0,13,110,111,110,111,100,101,64,110,111,104,
+ 111,115,116,0,0,0,1,0>>,
+ Port = binary_to_term(BPort),
+
+ BXPort = <<131,102,100,0,11,97,112,97,64,108,101,103,111,108,97,115,
+ 0,0,0,24,3>>,
+ XPort = binary_to_term(BXPort),
+
+ BRef = <<131,114,0,3,100,0,13,110,111,110,111,100,101,64,110,111,104,
+ 111,115,116,0,0,0,1,255,0,0,0,0,0,0,0,0>>,
+ Ref = binary_to_term(BRef),
+
+ BXRef = <<131,114,0,3,100,0,11,97,112,97,64,108,101,103,111,108,97,115,
+ 2,0,0,0,155,0,0,0,0,0,0,0,0>>,
+ XRef = binary_to_term(BXRef),
+
+ BXPid = <<131,103,100,0,11,97,112,97,64,108,101,103,111,108,97,115,
+ 0,0,0,36,0,0,0,0,1>>,
+ XPid = binary_to_term(BXPid),
+
+
+ %% X = f1(), Y = f2(), Z = f3(X, Y),
+
+ %% F1 = fun f1/0, % -> abc
+ B1 = <<131,112,0,0,0,66,0,215,206,77,69,249,50,170,17,129,47,21,98,
+ 13,196,76,242,0,0,0,1,0,0,0,0,100,0,1,116,97,1,98,2,195,126,
+ 58,103,100,0,13,110,111,110,111,100,101,64,110,111,104,111,
+ 115,116,0,0,0,112,0,0,0,0,0>>,
+ F1 = binary_to_term(B1),
+
+ %% F2 = fun f2/0, % -> abd
+ B2 = <<131,112,0,0,0,66,0,215,206,77,69,249,50,170,17,129,47,21,98,
+ 13,196,76,242,0,0,0,2,0,0,0,0,100,0,1,116,97,2,98,3,130,152,
+ 185,103,100,0,13,110,111,110,111,100,101,64,110,111,104,111,
+ 115,116,0,0,0,112,0,0,0,0,0>>,
+ F2 = binary_to_term(B2),
+
+ %% F3 = fun f3/2, % -> {abc, abd}
+ B3 = <<131,112,0,0,0,66,2,215,206,77,69,249,50,170,17,129,47,21,98,
+ 13,196,76,242,0,0,0,3,0,0,0,0,100,0,1,116,97,3,98,7,168,160,
+ 93,103,100,0,13,110,111,110,111,100,101,64,110,111,104,111,
+ 115,116,0,0,0,112,0,0,0,0,0>>,
+ F3 = binary_to_term(B3),
+
+ %% F4 = fun () -> 123456789012345678901234567 end,
+ B4 = <<131,112,0,0,0,66,0,215,206,77,69,249,50,170,17,129,47,21,98,
+ 13,196,76,242,0,0,0,4,0,0,0,0,100,0,1,116,97,4,98,2,230,21,
+ 171,103,100,0,13,110,111,110,111,100,101,64,110,111,104,111,
+ 115,116,0,0,0,112,0,0,0,0,0>>,
+ F4 = binary_to_term(B4),
+
+ %% F5 = fun() -> {X,Y,Z} end,
+ B5 = <<131,112,0,0,0,92,0,215,206,77,69,249,50,170,17,129,47,21,98,
+ 13,196,76,242,0,0,0,5,0,0,0,3,100,0,1,116,97,5,98,0,99,101,
+ 130,103,100,0,13,110,111,110,111,100,101,64,110,111,104,111,
+ 115,116,0,0,0,112,0,0,0,0,0,100,0,3,97,98,99,100,0,3,97,98,
+ 100,104,2,100,0,3,97,98,99,100,0,3,97,98,100>>,
+ F5 = binary_to_term(B5),
+ {{1,{2}},an_atom, 1, 3434.923942394,<<"this is a binary">>,
+ make_unaligned_sub_binary(<<"this is also a binary">>),c,d,e,f,g,h,i,j,k,l,[f],
+ 999999999999999999666666662123123123123324234999999999999999, 234234234,
+ BPort, Port, BXPort, XPort, BRef, Ref, BXRef, XRef, BXPid, XPid, F1, F2, F3, F4, F5,
+ #{a => 1, b => 2, c => 3, d => 4, e => 5, f => 6, g => 7, h => 8, i => 9,
+ j => 1, k => 1, l => 123123123123213, m => [1,2,3,4,5,6,7,8], o => 5, p => 6,
+ q => 7, r => 8, s => 9}}.
+
+get_map_helper(MapSoFar, 0) ->
+ MapSoFar;
+get_map_helper(MapSoFar, NumOfItemsToAdd) ->
+ NewMapSoFar = maps:put(NumOfItemsToAdd, NumOfItemsToAdd, MapSoFar),
+ get_map_helper(NewMapSoFar, NumOfItemsToAdd -1).
+
+get_map(Size) ->
+ get_map_helper(#{}, Size).
+
+
+%% Copied from binary_SUITE
+make_unaligned_sub_binary(Bin0) when is_binary(Bin0) ->
+ Bin1 = <<0:3,Bin0/binary,31:5>>,
+ Sz = size(Bin0),
+ <<0:3,Bin:Sz/binary,31:5>> = id(Bin1),
+ Bin.
+
+make_unaligned_sub_bitstring(Bin0) ->
+ Bin1 = <<0:3,Bin0/bitstring,31:5>>,
+ Sz = erlang:bit_size(Bin0),
+ <<0:3,Bin:Sz/bitstring,31:5>> = id(Bin1),
+ Bin.
+
+make_random_bin(Size) ->
+ make_random_bin(Size, []).
+
+make_random_bin(0, Acc) ->
+ iolist_to_binary(Acc);
+make_random_bin(Size, []) ->
+ make_random_bin(Size - 1, [simple_rand() rem 256]);
+make_random_bin(Size, [N | Tail]) ->
+ make_random_bin(Size - 1, [simple_rand(N) rem 256, N |Tail]).
+
+simple_rand() ->
+ 123456789.
+simple_rand(Seed) ->
+ A = 1103515245,
+ C = 12345,
+ M = (1 bsl 31),
+ (A * Seed + C) rem M.
diff --git a/erts/emulator/test/hash_property_test_SUITE.erl b/erts/emulator/test/hash_property_test_SUITE.erl
new file mode 100644
index 0000000000..b4c7810a52
--- /dev/null
+++ b/erts/emulator/test/hash_property_test_SUITE.erl
@@ -0,0 +1,103 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2004-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%
+%%
+%%
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%%% %%%
+%%% WARNING %%%
+%%% %%%
+%%% This is experimental code which may be changed or removed %%%
+%%% anytime without any warning. %%%
+%%% %%%
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+-module(hash_property_test_SUITE).
+
+-export([suite/0,all/0,groups/0,init_per_suite/1,
+ end_per_suite/1,init_per_group/2,end_per_group/2]).
+
+-export([test_phash2_no_diff/1,
+ test_phash2_no_diff_long/1,
+ test_phash2_no_diff_between_versions/1]).
+
+-include_lib("common_test/include/ct.hrl").
+
+suite() ->
+ [{ct_hooks,[ts_install_cth]}].
+
+all() -> [{group, proper}].
+
+groups() ->
+ [{proper, [], [test_phash2_no_diff,
+ test_phash2_no_diff_long,
+ test_phash2_no_diff_between_versions]}].
+
+
+%%% First prepare Config and compile the property tests for the found tool:
+init_per_suite(Config) ->
+ ct_property_test:init_per_suite(Config).
+
+end_per_suite(Config) ->
+ Config.
+
+%%% Only proper is supported
+init_per_group(proper, Config) ->
+ case proplists:get_value(property_test_tool,Config) of
+ proper -> Config;
+ X -> {skip, lists:concat([X," is not supported"])}
+ end;
+init_per_group(_, Config) ->
+ Config.
+
+end_per_group(_, Config) ->
+ Config.
+
+test_phash2_no_diff(Config) when is_list(Config) ->
+ true = ct_property_test:quickcheck(
+ phash2_properties:prop_phash2_same_with_same_input(),
+ Config).
+
+test_phash2_no_diff_long(Config) when is_list(Config) ->
+ true = ct_property_test:quickcheck(
+ phash2_properties:prop_phash2_same_with_same_long_input(),
+ Config).
+
+test_phash2_no_diff_between_versions(Config) when is_list(Config) ->
+ R = "21",
+ case test_server:is_release_available(R) of
+ true ->
+ Rel = {release,R},
+ case test_server:start_node(rel21,peer,[{erl,[Rel]}]) of
+ {error, Reason} -> {skip, io_lib:format("Could not start node: ~p~n", [Reason])};
+ {ok, Node} ->
+ try
+ true = ct_property_test:quickcheck(
+ phash2_properties:prop_phash2_same_in_different_versions(Node),
+ Config),
+ true = ct_property_test:quickcheck(
+ phash2_properties:prop_phash2_same_in_different_versions_with_long_input(Node),
+ Config)
+ after
+ test_server:stop_node(Node)
+ end
+ end;
+ false ->
+ {skip, io_lib:format("Release ~s not available~n", [R])}
+ end.
diff --git a/erts/emulator/test/hibernate_SUITE.erl b/erts/emulator/test/hibernate_SUITE.erl
index a20f306e04..d65d0ff2fd 100644
--- a/erts/emulator/test/hibernate_SUITE.erl
+++ b/erts/emulator/test/hibernate_SUITE.erl
@@ -46,12 +46,17 @@ all() ->
basic(Config) when is_list(Config) ->
Ref = make_ref(),
Info = {self(),Ref},
- ExpectedHeapSz = erts_debug:size([Info]),
+ ExpectedHeapSz = expected_heap_size([Info]),
Child = spawn_link(fun() -> basic_hibernator(Info) end),
hibernate_wake_up(100, ExpectedHeapSz, Child),
Child ! please_quit_now,
ok.
+expected_heap_size(Term) ->
+ %% When hibernating, an extra word will be allocated on the stack
+ %% for a continuation pointer.
+ erts_debug:size(Term) + 1.
+
hibernate_wake_up(0, _, _) -> ok;
hibernate_wake_up(N, ExpectedHeapSz, Child) ->
{heap_size,Before} = process_info(Child, heap_size),
@@ -142,7 +147,7 @@ whats_up_calc(A1, A2, A3, A4, A5, A6, A7, A8, A9, Acc) ->
dynamic_call(Config) when is_list(Config) ->
Ref = make_ref(),
Info = {self(),Ref},
- ExpectedHeapSz = erts_debug:size([Info]),
+ ExpectedHeapSz = expected_heap_size([Info]),
Child = spawn_link(fun() -> ?MODULE:dynamic_call_hibernator(Info, hibernate) end),
hibernate_wake_up(100, ExpectedHeapSz, Child),
Child ! please_quit_now,
diff --git a/erts/emulator/test/hipe_SUITE.erl b/erts/emulator/test/hipe_SUITE.erl
index e62d4260f6..9741872fd8 100644
--- a/erts/emulator/test/hipe_SUITE.erl
+++ b/erts/emulator/test/hipe_SUITE.erl
@@ -131,8 +131,8 @@ t_trycatch(Config) ->
t_trycatch_1([S|Ss]) ->
io:format("~p", [S]),
compile_and_load(S),
- call_trycatch(try_catch),
- call_trycatch(plain_catch),
+ call_trycatch(),
+ call_catch(),
io:nl(),
t_trycatch_1(Ss);
t_trycatch_1([]) ->
@@ -144,38 +144,49 @@ trycatch_combine([N|Ns]) ->
trycatch_combine([]) ->
[[]].
-call_trycatch(Func) ->
- case do_call_trycatch(error, Func, {error,whatever}) of
+call_trycatch() ->
+ case trycatch_1:one_try_catch({error,whatever}) of
{error,whatever,[{trycatch_3,three,1,_}|_]} ->
ok
end,
- case do_call_trycatch(error, Func, fc) of
+ case trycatch_1:one_try_catch(fc) of
{error,function_clause,[{trycatch_3,three,[fc],_}|_]} ->
ok;
{error,function_clause,[{trycatch_3,three,1,_}|_]} ->
+ true = trycatch_3:module_info(native),
ok
end,
- case do_call_trycatch(throw, Func, {throw,{a,b}}) of
+ case trycatch_1:one_try_catch({throw,{a,b}}) of
{throw,{a,b},[{trycatch_3,three,1,_}|_]} ->
ok
end,
- case do_call_trycatch(exit, Func, {exit,{a,b,c}}) of
+ case trycatch_1:one_try_catch({exit,{a,b,c}}) of
{exit,{a,b,c},[{trycatch_3,three,1,_}|_]} ->
ok
end,
ok.
-do_call_trycatch(_Class, try_catch, Argument) ->
- trycatch_1:one_try_catch(Argument);
-do_call_trycatch(error, plain_catch, Argument) ->
- {{'EXIT',{Reason,Stk}},Stk} = trycatch_1:one_plain_catch(Argument),
- {error,Reason,Stk};
-do_call_trycatch(throw, plain_catch, Argument) ->
- {Reason,Stk} = trycatch_1:one_plain_catch(Argument),
- {throw,Reason,Stk};
-do_call_trycatch(exit, plain_catch, Argument) ->
- {{'EXIT',Reason},Stk} = trycatch_1:one_plain_catch(Argument),
- {exit,Reason,Stk}.
+call_catch() ->
+ case trycatch_1:one_plain_catch({error,whatever}) of
+ {'EXIT',{whatever,[{trycatch_3,three,1,_}|_]}} ->
+ ok
+ end,
+
+ case trycatch_1:one_plain_catch(fc) of
+ {'EXIT',{function_clause,[{trycatch_3,three,[fc],_}|_]}} ->
+ ok;
+ {'EXIT',{function_clause,[{trycatch_3,three,1,_}|_]}} ->
+ true = trycatch_3:module_info(native)
+ end,
+ case trycatch_1:one_plain_catch({throw,{a,b}}) of
+ {a,b} ->
+ ok
+ end,
+ case trycatch_1:one_plain_catch({exit,{a,b,c}}) of
+ {'EXIT',{a,b,c}} ->
+ ok
+ end,
+ ok.
compile_and_load(Sources) ->
_ = [begin
diff --git a/erts/emulator/test/hipe_SUITE_data/trycatch_1.erl b/erts/emulator/test/hipe_SUITE_data/trycatch_1.erl
index 702b14b5b9..f7d0e3bd1e 100644
--- a/erts/emulator/test/hipe_SUITE_data/trycatch_1.erl
+++ b/erts/emulator/test/hipe_SUITE_data/trycatch_1.erl
@@ -5,10 +5,9 @@ one_try_catch(Term) ->
try
trycatch_2:two(Term)
catch
- C:R ->
- Stk = erlang:get_stacktrace(),
+ C:R:Stk ->
{C,R,Stk}
end.
one_plain_catch(Term) ->
- {catch trycatch_2:two(Term),erlang:get_stacktrace()}.
+ catch trycatch_2:two(Term).
diff --git a/erts/emulator/test/list_bif_SUITE.erl b/erts/emulator/test/list_bif_SUITE.erl
index 4ddcd0f60b..68773e8611 100644
--- a/erts/emulator/test/list_bif_SUITE.erl
+++ b/erts/emulator/test/list_bif_SUITE.erl
@@ -156,22 +156,18 @@ t_list_to_ext_pidportref(Config) when is_list(Config) ->
Port2 = list_to_port(PortStr),
Ref2 = list_to_ref(RefStr),
- %% The local roundtrips of externals does not work
- %% as 'creation' is missing in the string formats and we don't know
- %% the 'creation' of the connected node.
- false = (Pid =:= Pid2),
- false = (Port =:= Port2),
- false = (Ref =:= Ref2),
-
- %% Local roundtrip kind of "works" for '==' since OTP-22.0 (bf7c722bd3b)
- %% Operator '==' treats 0-creations as wildcards
- %% which breaks term transitivity (A==B and B==C => B==C).
+ %% Local roundtrips of externals work from OTP-23
+ %% as even though 'creation' is missing in the string formats
+ %% we know the 'creation' of the connected node and list_to_* use that.
+ true = (Pid =:= Pid2),
+ true = (Port =:= Port2),
+ true = (Ref =:= Ref2),
true = (Pid == Pid2),
true = (Port == Port2),
true = (Ref == Ref2),
- %% It works when sent back to node with matching name, as 0-creations
- %% will be converted to the local node creation.
+ %% And it works when sent back to the same node instance,
+ %% which was connected when list_to_* were called.
true = rpc:call(Node, erlang, '=:=', [Pid, Pid2]),
true = rpc:call(Node, erlang, '==', [Pid, Pid2]),
true = rpc:call(Node, erlang, '=:=', [Port, Port2]),
@@ -179,9 +175,57 @@ t_list_to_ext_pidportref(Config) when is_list(Config) ->
true = rpc:call(Node, erlang, '=:=', [Ref, Ref2]),
true = rpc:call(Node, erlang, '==', [Ref, Ref2]),
+ %% Make sure no ugly comparison with 0-creation as wildcard is done.
+ Pid0 = make_0_creation(Pid),
+ Port0 = make_0_creation(Port),
+ Ref0 = make_0_creation(Ref),
+ false = (Pid =:= Pid0),
+ false = (Port =:= Port0),
+ false = (Ref =:= Ref0),
+ false = (Pid == Pid0),
+ false = (Port == Port0),
+ false = (Ref == Ref0),
+
+ %% Check 0-creations are converted to local node creations
+ %% when sent to matching node name.
+ true = rpc:call(Node, erlang, '=:=', [Pid, Pid0]),
+ true = rpc:call(Node, erlang, '==', [Pid, Pid0]),
+ true = rpc:call(Node, erlang, '=:=', [Port, Port0]),
+ true = rpc:call(Node, erlang, '==', [Port, Port0]),
+ true = rpc:call(Node, erlang, '=:=', [Ref, Ref0]),
+ true = rpc:call(Node, erlang, '==', [Ref, Ref0]),
+
slave:stop(Node),
ok.
+-define(NEW_PID_EXT, 88).
+-define(NEW_PORT_EXT, 89).
+-define(NEWER_REFERENCE_EXT, 90).
+
+%% Copy pid/port/ref but set creation=0
+make_0_creation(X) when is_pid(X); is_port(X); is_reference(X) ->
+ B = term_to_binary(X),
+ Sz = byte_size(B),
+ B2 = case B of
+ <<131, ?NEW_PID_EXT, _/binary>> ->
+ PreSz = Sz - 4,
+ <<_:PreSz/binary, Cr:32>> = B,
+ true = (Cr =/= 0),
+ <<B:PreSz/binary, 0:32>>;
+ <<131, ?NEW_PORT_EXT, _/binary>> ->
+ PreSz = Sz - 4,
+ <<_:PreSz/binary, Cr:32>> = B,
+ true = (Cr =/= 0),
+ <<B:PreSz/binary, 0:32>>;
+ <<131, ?NEWER_REFERENCE_EXT, Len:16, _/binary>> ->
+ PostSz = Len*4,
+ PreSz = Sz - (4 + PostSz),
+ <<_:PreSz/binary, Cr:32, PostFix:PostSz/binary>> = B,
+ true = (Cr =/= 0),
+ <<B:PreSz/binary, 0:32, PostFix/binary>>
+ end,
+ binary_to_term(B2).
+
%% Test list_to_float/1 with correct and incorrect arguments.
diff --git a/erts/emulator/test/lttng_SUITE.erl b/erts/emulator/test/lttng_SUITE.erl
index f19047ba71..18d72cc16a 100644
--- a/erts/emulator/test/lttng_SUITE.erl
+++ b/erts/emulator/test/lttng_SUITE.erl
@@ -32,8 +32,7 @@
t_driver_ready_input_output/1,
t_driver_timeout/1,
t_driver_caller/1,
- t_driver_flush/1,
- t_scheduler_poll/1]).
+ t_driver_flush/1]).
-export([ets_load/0]).
@@ -43,7 +42,7 @@ suite() ->
[{ct_hooks,[ts_install_cth]},
{timetrap, {minutes, 1}}].
-all() ->
+all() ->
[t_lttng_list,
t_memory_carrier,
t_carrier_pool,
@@ -52,9 +51,7 @@ all() ->
t_driver_control,
t_driver_timeout,
t_driver_caller,
- t_driver_flush,
- t_scheduler_poll].
-
+ t_driver_flush].
init_per_suite(Config) ->
case erlang:system_info(dynamic_trace) of
@@ -88,8 +85,6 @@ end_per_testcase(Case, _Config) ->
%% org_erlang_otp:carrier_pool_put
%% org_erlang_otp:carrier_destroy
%% org_erlang_otp:carrier_create
-%% org_erlang_otp:aio_pool_put
-%% org_erlang_otp:aio_pool_get
%% org_erlang_otp:driver_control
%% org_erlang_otp:driver_call
%% org_erlang_otp:driver_finish
@@ -105,7 +100,6 @@ end_per_testcase(Case, _Config) ->
%% org_erlang_otp:driver_outputv
%% org_erlang_otp:driver_init
%% org_erlang_otp:driver_start
-%% org_erlang_otp:scheduler_poll
%%
%% Testcases
@@ -264,35 +258,6 @@ t_driver_caller(Config) ->
ok = check_tracepoint("org_erlang_otp:driver_init", Res),
ok = check_tracepoint("org_erlang_otp:driver_finish", Res),
ok.
-
-%% org_erlang_otp:scheduler_poll
-t_scheduler_poll(Config) ->
- ok = lttng_start_event("org_erlang_otp:scheduler_poll", Config),
-
- N = 100,
-
- Me = self(),
- Pid = spawn_link(fun() -> tcp_server(Me, {active, N*2}) end),
- receive {Pid, accept} -> ok end,
-
- %% We want to create a scenario where the fd is moved into a scheduler
- %% pollset, this means we have to send many small packages to the
- %% same socket, but not fast enough for them to all arrive at the
- %% same time.
- {ok, Sock} = gen_tcp:connect("localhost", 5679, [binary, {packet, 2}]),
- [begin gen_tcp:send(Sock,txt()), receive ok -> ok end end || _ <- lists:seq(1,N)],
-
- ok = memory_load(),
-
- [begin gen_tcp:send(Sock,txt()), receive ok -> ok end end || _ <- lists:seq(1,N)],
-
- ok = gen_tcp:close(Sock),
- Pid ! die,
- receive {Pid, done} -> ok end,
-
- Res = lttng_stop_and_view(Config),
- ok = check_tracepoint("org_erlang_otp:scheduler_poll", Res),
- ok.
%% org_erlang_otp:driver_flush
t_driver_flush(Config) ->
@@ -333,24 +298,6 @@ chk_caller(Port, Callback, ExpectedCaller) ->
ExpectedCaller = Caller
end.
-memory_load() ->
- Me = self(),
- Pids0 = [spawn_link(fun() -> memory_loop(Me, 20, <<42>>) end) || _ <- lists:seq(1,30)],
- timer:sleep(50),
- Pids1 = [spawn_link(fun() -> memory_loop(Me, 20, <<42>>) end) || _ <- lists:seq(1,30)],
- [receive {Pid, done} -> ok end || Pid <- Pids0 ++ Pids1],
- timer:sleep(500),
- ok.
-
-memory_loop(Parent, N, Bin) ->
- memory_loop(Parent, N, Bin, []).
-
-memory_loop(Parent, 0, _Bin, _) ->
- Parent ! {self(), done};
-memory_loop(Parent, N, Bin0, Ls) ->
- Bin = binary:copy(<<Bin0/binary, Bin0/binary>>),
- memory_loop(Parent, N - 1, Bin, [a,b,c|Ls]).
-
ets_load(Config) ->
%% Have to do on a fresh node to guarantee that carriers are created
@@ -429,8 +376,6 @@ txt() ->
"%% org_erlang_otp:carrier_pool_put\n"
"%% org_erlang_otp:carrier_destroy\n"
"%% org_erlang_otp:carrier_create\n"
- "%% org_erlang_otp:aio_pool_put\n"
- "%% org_erlang_otp:aio_pool_get\n"
"%% org_erlang_otp:driver_control\n"
"%% org_erlang_otp:driver_call\n"
"%% org_erlang_otp:driver_finish\n"
@@ -445,8 +390,7 @@ txt() ->
"%% org_erlang_otp:driver_output\n"
"%% org_erlang_otp:driver_outputv\n"
"%% org_erlang_otp:driver_init\n"
- "%% org_erlang_otp:driver_start\n"
- "%% org_erlang_otp:scheduler_poll">>.
+ "%% org_erlang_otp:driver_start">>.
load_driver(Dir, Driver) ->
case erl_ddll:load_driver(Dir, Driver) of
@@ -464,12 +408,6 @@ have_carriers(Alloc) ->
_ -> true
end.
-have_async_threads() ->
- Tps = erlang:system_info(thread_pool_size),
- if Tps =:= 0 -> false;
- true -> true
- end.
-
%% lttng
lttng_stop_and_view(Config) ->
Path = proplists:get_value(priv_dir, Config),
diff --git a/erts/emulator/test/map_SUITE.erl b/erts/emulator/test/map_SUITE.erl
index 4b638b9082..dbf6fa58ed 100644
--- a/erts/emulator/test/map_SUITE.erl
+++ b/erts/emulator/test/map_SUITE.erl
@@ -1872,15 +1872,18 @@ t_bif_map_get(Config) when is_list(Config) ->
"v3" = maps:get(<<"k2">>, M1),
%% error cases
+ %%
+ %% Note that the stack trace is ignored because the compiler may have
+ %% rewritten maps:get/2 to map_get.
do_badmap(fun(T) ->
- {'EXIT',{{badmap,T},[{maps,get,_,_}|_]}} =
+ {'EXIT',{{badmap,T},_}} =
(catch maps:get(a, T))
end),
- {'EXIT',{{badkey,{1,1}},[{maps,get,_,_}|_]}} =
+ {'EXIT',{{badkey,{1,1}},_}} =
(catch maps:get({1,1}, #{{1,1.0} => "tuple"})),
- {'EXIT',{{badkey,a},[{maps,get,_,_}|_]}} = (catch maps:get(a, #{})),
- {'EXIT',{{badkey,a},[{maps,get,_,_}|_]}} =
+ {'EXIT',{{badkey,a},_}} = (catch maps:get(a, #{})),
+ {'EXIT',{{badkey,a},_}} =
(catch maps:get(a, #{b=>1, c=>2})),
ok.
@@ -1942,8 +1945,11 @@ t_bif_map_is_key(Config) when is_list(Config) ->
false = maps:is_key(1.0, maps:put(1, "number", M1)),
%% error case
+ %%
+ %% Note that the stack trace is ignored because the compiler may have
+ %% rewritten maps:is_key/2 to is_map_key.
do_badmap(fun(T) ->
- {'EXIT',{{badmap,T},[{maps,is_key,_,_}|_]}} =
+ {'EXIT',{{badmap,T},_}} =
(catch maps:is_key(a, T))
end),
ok.
diff --git a/erts/emulator/test/match_spec_SUITE.erl b/erts/emulator/test/match_spec_SUITE.erl
index 21de6b1002..686b431876 100644
--- a/erts/emulator/test/match_spec_SUITE.erl
+++ b/erts/emulator/test/match_spec_SUITE.erl
@@ -198,7 +198,8 @@ caller_and_return_to(Config) ->
{trace,Tracee,call,{?MODULE,do_the_put,[test]},{?MODULE,do_put,1}},
{trace,Tracee,call,{erlang,integer_to_list,[1]},{?MODULE,do_the_put,1}},
{trace,Tracee,return_to,{?MODULE,do_the_put,1}},
- {trace,Tracee,call,{erlang,put,[test,"1"]},{?MODULE,do_put,1}},
+ {trace,Tracee,call,{erlang,put,[test,"1"]},{?MODULE,do_the_put,1}},
+ {trace,Tracee,return_to,{?MODULE,do_the_put,1}},
{trace,Tracee,return_to,{?MODULE,do_put,1}},
%% These last trace messages are a bit strange...
diff --git a/erts/emulator/test/mtx_SUITE_data/Makefile.src b/erts/emulator/test/mtx_SUITE_data/Makefile.src
index 1816dc6798..c736cfa7dd 100644
--- a/erts/emulator/test/mtx_SUITE_data/Makefile.src
+++ b/erts/emulator/test/mtx_SUITE_data/Makefile.src
@@ -28,8 +28,10 @@ LIBS = @ERTS_LIBS@
all: $(NIF_LIBS)
+WSL=@WSL@
+
mtx_SUITE.c: force_rebuild
- touch mtx_SUITE.c
+ $(WSL) touch mtx_SUITE.c
force_rebuild:
echo "Force rebuild to compensate for emulator type dependencies"
diff --git a/erts/emulator/test/nif_SUITE.erl b/erts/emulator/test/nif_SUITE.erl
index 6a8f7607cd..009c3dc7ac 100644
--- a/erts/emulator/test/nif_SUITE.erl
+++ b/erts/emulator/test/nif_SUITE.erl
@@ -33,6 +33,7 @@
init_per_testcase/2, end_per_testcase/2,
basic/1, reload_error/1, upgrade/1, heap_frag/1,
t_on_load/1,
+ load_traced_nif/1,
select/1, select_steal/1,
monitor_process_a/1,
monitor_process_b/1,
@@ -93,6 +94,7 @@ all() ->
{group, monitor},
monitor_frenzy,
hipe,
+ load_traced_nif,
binaries, get_string, get_atom, maps, api_macros, from_array,
iolist_as_binary, resource, resource_binary,
threading, send, send2, send3,
@@ -500,6 +502,47 @@ t_on_load(Config) when is_list(Config) ->
verify_tmpmem(TmpMem),
ok.
+%% Test load of module where a NIF stub is already traced.
+load_traced_nif(Config) when is_list(Config) ->
+ TmpMem = tmpmem(),
+
+ Data = proplists:get_value(data_dir, Config),
+ File = filename:join(Data, "nif_mod"),
+ {ok,nif_mod,Bin} = compile:file(File, [binary,return_errors]),
+ {module,nif_mod} = erlang:load_module(nif_mod,Bin),
+
+ Tracee = spawn_link(fun Loop() -> receive {lib_version,ExpRet} ->
+ ExpRet = nif_mod:lib_version()
+ end,
+ Loop()
+ end),
+ 1 = erlang:trace_pattern({nif_mod,lib_version,0}, true, [local]),
+ 1 = erlang:trace(Tracee, true, [call]),
+
+ Tracee ! {lib_version, undefined},
+ {trace, Tracee, call, {nif_mod,lib_version,[]}} = receive_any(1000),
+
+ ok = nif_mod:load_nif_lib(Config, 1),
+
+ Tracee ! {lib_version, 1},
+ {trace, Tracee, call, {nif_mod,lib_version,[]}} = receive_any(1000),
+
+ %% Wait for NIF loading to finish and write final call_nif instruction
+ timer:sleep(500),
+
+ Tracee ! {lib_version, 1},
+ {trace, Tracee, call, {nif_mod,lib_version,[]}} = receive_any(1000),
+
+ true = erlang:delete_module(nif_mod),
+ true = erlang:purge_module(nif_mod),
+
+ unlink(Tracee),
+ exit(Tracee, kill),
+
+ verify_tmpmem(TmpMem),
+ ok.
+
+
-define(ERL_NIF_SELECT_READ, (1 bsl 0)).
-define(ERL_NIF_SELECT_WRITE, (1 bsl 1)).
-define(ERL_NIF_SELECT_STOP, (1 bsl 2)).
@@ -2520,26 +2563,7 @@ dummy_call(_) ->
ok.
tmpmem() ->
- case erlang:system_info({allocator,temp_alloc}) of
- false -> undefined;
- MemInfo ->
- MSBCS = lists:foldl(
- fun ({instance, 0, _}, Acc) ->
- Acc; % Ignore instance 0
- ({instance, _, L}, Acc) ->
- {value,{_,MBCS}} = lists:keysearch(mbcs, 1, L),
- {value,{_,SBCS}} = lists:keysearch(sbcs, 1, L),
- [MBCS,SBCS | Acc]
- end,
- [],
- MemInfo),
- lists:foldl(
- fun(L, {Bl0,BlSz0}) ->
- {value,{_,Bl,_,_}} = lists:keysearch(blocks, 1, L),
- {value,{_,BlSz,_,_}} = lists:keysearch(blocks_size, 1, L),
- {Bl0+Bl,BlSz0+BlSz}
- end, {0,0}, MSBCS)
- end.
+ erts_debug:alloc_blocks_size(temp_alloc).
verify_tmpmem(MemInfo) ->
%%wait_for_test_procs(),
diff --git a/erts/emulator/test/nif_SUITE_data/Makefile.src b/erts/emulator/test/nif_SUITE_data/Makefile.src
index de06026780..69dd2d6757 100644
--- a/erts/emulator/test/nif_SUITE_data/Makefile.src
+++ b/erts/emulator/test/nif_SUITE_data/Makefile.src
@@ -24,7 +24,8 @@ tsd@dll@: tester.c testcase_driver.h
DRIVER_DIR = ../erl_drv_thread_SUITE_data
+WSL=@WSL@
+
basic.c rwlock.c tsd.c: $(DRIVER_DIR)/$@
- cat head.txt > $@
- cat $(DRIVER_DIR)/$@ | sed -e 's/erl_drv_/enif_/g' -e 's/driver_/enif_/g' -e 's/ErlDrv/ErlNif/g' >> $@
- cat tail.txt >> $@
+ $(WSL) sed -e 's/erl_drv_/enif_/g' -e 's/driver_/enif_/g' -e 's/ErlDrv/ErlNif/g' $(DRIVER_DIR)/$@ > $@.tmp
+ $(WSL) cat head.txt $@.tmp tail.txt > $@
diff --git a/erts/emulator/test/nif_SUITE_data/nif_SUITE.c b/erts/emulator/test/nif_SUITE_data/nif_SUITE.c
index ff47cfe500..f4bb1504b8 100644
--- a/erts/emulator/test/nif_SUITE_data/nif_SUITE.c
+++ b/erts/emulator/test/nif_SUITE_data/nif_SUITE.c
@@ -3634,7 +3634,6 @@ static ErlNifFunc nif_funcs[] =
{"release_resource", 1, release_resource},
{"release_resource_from_thread", 1, release_resource_from_thread},
{"last_resource_dtor_call_nif", 0, last_resource_dtor_call_nif},
- {"make_new_resource", 2, make_new_resource},
{"check_is", 11, check_is},
{"check_is_exception", 0, check_is_exception},
{"length_test", 6, length_test},
diff --git a/erts/emulator/test/nif_SUITE_data/nif_api_2_0/erl_nif.h b/erts/emulator/test/nif_SUITE_data/nif_api_2_0/erl_nif.h
index 4b2b7550e5..90ed8da0b6 100644
--- a/erts/emulator/test/nif_SUITE_data/nif_api_2_0/erl_nif.h
+++ b/erts/emulator/test/nif_SUITE_data/nif_api_2_0/erl_nif.h
@@ -164,11 +164,7 @@ extern TWinDynNifCallbacks WinDynNifCallbacks;
#else
# define ERL_NIF_INIT_GLOB
# define ERL_NIF_INIT_BODY
-# if defined(VXWORKS)
-# define ERL_NIF_INIT_DECL(MODNAME) ErlNifEntry* MODNAME ## _init(void)
-# else
# define ERL_NIF_INIT_DECL(MODNAME) ErlNifEntry* nif_init(void)
-# endif
#endif
diff --git a/erts/emulator/test/nofrag_SUITE.erl b/erts/emulator/test/nofrag_SUITE.erl
index 8b1519ae36..d4c74579e2 100644
--- a/erts/emulator/test/nofrag_SUITE.erl
+++ b/erts/emulator/test/nofrag_SUITE.erl
@@ -22,6 +22,11 @@
-include_lib("common_test/include/ct.hrl").
+%% This suite alters the return values of functions which breaks certain
+%% assumptions made by the compiler, so we have to turn off module-level type
+%% optimization to be safe.
+-compile(no_module_opt).
+
-export([all/0, suite/0,
error_handler/1,error_handler_apply/1,
error_handler_fixed_apply/1,error_handler_fun/1,
diff --git a/erts/emulator/test/persistent_term_SUITE.erl b/erts/emulator/test/persistent_term_SUITE.erl
index c9874e5679..6682af489a 100644
--- a/erts/emulator/test/persistent_term_SUITE.erl
+++ b/erts/emulator/test/persistent_term_SUITE.erl
@@ -23,6 +23,7 @@
-export([all/0,suite/0,init_per_suite/1,end_per_suite/1,
basic/1,purging/1,sharing/1,get_trapping/1,
+ destruction/1,
info/1,info_trapping/1,killed_while_trapping/1,
off_heap_values/1,keys/1,collisions/1,
init_restart/1, put_erase_trapping/1,
@@ -38,11 +39,13 @@ suite() ->
all() ->
[basic,purging,sharing,get_trapping,info,info_trapping,
+ destruction,
killed_while_trapping,off_heap_values,keys,collisions,
init_restart, put_erase_trapping, killed_while_trapping_put,
killed_while_trapping_erase].
init_per_suite(Config) ->
+ erts_debug:set_internal_state(available_internal_state, true),
%% Put a term in the dict so that we know that the testcases handle
%% stray terms left by stdlib or other test suites.
persistent_term:put(init_per_suite, {?MODULE}),
@@ -50,6 +53,7 @@ init_per_suite(Config) ->
end_per_suite(Config) ->
persistent_term:erase(init_per_suite),
+ erts_debug:set_internal_state(available_internal_state, false),
Config.
basic(_Config) ->
@@ -152,19 +156,25 @@ purging_tester(Parent, Key) ->
receive
{Parent,erased} ->
{'EXIT',{badarg,_}} = (catch persistent_term:get(Key)),
- purging_tester_1(Term);
+ purging_tester_1(Term, 1);
{Parent,replaced} ->
{?MODULE,new} = persistent_term:get(Key),
- purging_tester_1(Term)
+ purging_tester_1(Term, 1)
end.
%% Wait for the term to be copied into this process.
-purging_tester_1(Term) ->
+purging_tester_1(Term, Timeout) ->
purging_check_term(Term),
- receive after 1 -> ok end,
+ receive after Timeout -> ok end,
case erts_debug:size_shared(Term) of
0 ->
- purging_tester_1(Term);
+ case Timeout of
+ 1000 ->
+ flush_later_ops(),
+ purging_tester_1(Term, 1);
+ _ ->
+ purging_tester_1(Term, Timeout*10)
+ end;
Size ->
%% The term has been copied into this process.
purging_check_term(Term),
@@ -174,6 +184,83 @@ purging_tester_1(Term) ->
purging_check_term({term,[<<"abc",0:777/unit:8>>]}) ->
ok.
+%% Make sure terms are really deallocated when overwritten or erased.
+destruction(Config) ->
+ ok = erts_test_destructor:init(Config),
+
+ NKeys = 100,
+ Keys = lists:seq(0,NKeys-1),
+ [begin
+ V = erts_test_destructor:send(self(), K),
+ persistent_term:put({?MODULE,K}, V)
+ end
+ || K <- Keys],
+
+ %% Erase or overwrite all keys in "random" order.
+ lists:foldl(fun(_, K) ->
+ case erlang:phash2(K) band 1 of
+ 0 ->
+ %%io:format("erase key ~p\n", [K]),
+ persistent_term:erase({?MODULE,K});
+ 1 ->
+ %%io:format("replace key ~p\n", [K]),
+ persistent_term:put({?MODULE,K}, value)
+ end,
+ (K + 13) rem NKeys
+ end,
+ 17, Keys),
+
+ destruction_1(Keys).
+
+destruction_1(Keys) ->
+ erlang:garbage_collect(),
+
+ %% Receive all destruction messages
+ MsgLst = destruction_recv(length(Keys), [], 2),
+ ok = case lists:sort(MsgLst) of
+ Keys ->
+ ok;
+ _ ->
+ io:format("GOT ~p\n", [MsgLst]),
+ io:format("MISSING ~p\n", [Keys -- MsgLst]),
+ error
+ end,
+
+ %% Cleanup all remaining
+ [persistent_term:erase({?MODULE,K}) || K <- Keys],
+ ok.
+
+destruction_recv(0, Acc, _) ->
+ Acc;
+destruction_recv(N, Acc, Flush) ->
+ receive M ->
+ destruction_recv(N-1, [M | Acc], Flush)
+ after 1000 ->
+ io:format("TIMEOUT. Missing ~p destruction messages.\n", [N]),
+ case Flush of
+ 0 ->
+ Acc;
+ _ ->
+ io:format("Try flush last literal area cleanup...\n"),
+ flush_later_ops(),
+ destruction_recv(N, Acc, Flush-1)
+ end
+ end.
+
+%% Both persistent_term itself and erts_literal_are_collector use
+%% erts_schedule_thr_prgr_later_cleanup_op() to schedule purge and deallocation
+%% of literals. To avoid waiting forever on sleeping schedulers we flush
+%% all later ops to make these cleanup jobs go through.
+flush_later_ops() ->
+ try
+ erts_debug:set_internal_state(wait, thread_progress)
+ catch
+ error:system_limit ->
+ ok % already ongoing; called by other process
+ end,
+ ok.
+
+
%% Test that sharing is preserved when storing terms.
sharing(_Config) ->
@@ -517,17 +604,12 @@ colliding_keys() ->
%% Verify that the keys still collide (this will fail if the
%% internal hash function has been changed).
- erts_debug:set_internal_state(available_internal_state, true),
- try
- case erlang:system_info(wordsize) of
- 8 ->
- verify_colliding_keys(L);
- 4 ->
- %% Not guaranteed to collide on a 32-bit system.
- ok
- end
- after
- erts_debug:set_internal_state(available_internal_state, false)
+ case erlang:system_info(wordsize) of
+ 8 ->
+ verify_colliding_keys(L);
+ 4 ->
+ %% Not guaranteed to collide on a 32-bit system.
+ ok
end,
L.
@@ -611,19 +693,25 @@ chk({Info, _Initial} = Chk) ->
ok = persistent_term:put(Key, {term,Info}),
Term = persistent_term:get(Key),
true = persistent_term:erase(Key),
- chk_not_stuck(Term),
+ chk_not_stuck(Term, 1),
[persistent_term:erase(K) || {K, _} <- pget(Chk)],
ok.
-chk_not_stuck(Term) ->
+chk_not_stuck(Term, Timeout) ->
%% Hash tables to be deleted are put onto a queue.
%% Make sure that the queue isn't stuck by a table with
%% a non-zero ref count.
case erts_debug:size_shared(Term) of
0 ->
- erlang:yield(),
- chk_not_stuck(Term);
+ receive after Timeout -> ok end,
+ case Timeout of
+ 1000 ->
+ flush_later_ops(),
+ chk_not_stuck(Term, 1);
+ _ ->
+ chk_not_stuck(Term, Timeout*10)
+ end;
_ ->
ok
end.
@@ -633,7 +721,6 @@ pget({_, Initial}) ->
killed_while_trapping_put(_Config) ->
- erts_debug:set_internal_state(available_internal_state, true),
repeat(
fun() ->
NrOfPutsInChild = 10000,
@@ -647,10 +734,9 @@ killed_while_trapping_put(_Config) ->
do_erases(NrOfPutsInChild)
end,
10),
- erts_debug:set_internal_state(available_internal_state, false).
+ ok.
killed_while_trapping_erase(_Config) ->
- erts_debug:set_internal_state(available_internal_state, true),
repeat(
fun() ->
NrOfErases = 2500,
@@ -664,15 +750,14 @@ killed_while_trapping_erase(_Config) ->
do_erases(NrOfErases)
end,
10),
- erts_debug:set_internal_state(available_internal_state, false).
+ ok.
put_erase_trapping(_Config) ->
NrOfItems = 5000,
- erts_debug:set_internal_state(available_internal_state, true),
do_puts(NrOfItems, first),
do_puts(NrOfItems, second),
do_erases(NrOfItems),
- erts_debug:set_internal_state(available_internal_state, false).
+ ok.
do_puts(0, _) -> ok;
do_puts(NrOfPuts, ValuePrefix) ->
diff --git a/erts/emulator/test/persistent_term_SUITE_data/Makefile.src b/erts/emulator/test/persistent_term_SUITE_data/Makefile.src
new file mode 100644
index 0000000000..29b7fcb647
--- /dev/null
+++ b/erts/emulator/test/persistent_term_SUITE_data/Makefile.src
@@ -0,0 +1,8 @@
+
+NIF_LIBS = erts_test_destructor@dll@
+
+all: $(NIF_LIBS)
+
+@SHLIB_RULES@
+
+$(NIF_LIBS): erts_test_destructor.c
diff --git a/erts/emulator/test/persistent_term_SUITE_data/erts_test_destructor.c b/erts/emulator/test/persistent_term_SUITE_data/erts_test_destructor.c
new file mode 100644
index 0000000000..808334f1c4
--- /dev/null
+++ b/erts/emulator/test/persistent_term_SUITE_data/erts_test_destructor.c
@@ -0,0 +1,83 @@
+/*
+ * %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 <erl_nif.h>
+
+#include <stdio.h>
+
+
+static ErlNifResourceType* resource_type;
+static void resource_dtor(ErlNifEnv* env, void* obj);
+
+typedef struct {
+ ErlNifPid to;
+ ERL_NIF_TERM msg;
+ ErlNifEnv* msg_env;
+} DtorSender;
+
+static int load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info)
+{
+ resource_type = enif_open_resource_type(env,NULL,"DtorSender",resource_dtor,
+ ERL_NIF_RT_CREATE, NULL);
+ return 0;
+}
+
+static ERL_NIF_TERM is_loaded_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
+{
+ return enif_make_atom(env, "true");
+}
+
+static ERL_NIF_TERM send_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
+{
+ DtorSender *p;
+ ErlNifPid pid;
+ ERL_NIF_TERM res;
+
+ p = enif_alloc_resource(resource_type, sizeof(DtorSender));
+
+ if (!enif_get_local_pid(env, argv[0], &p->to)) {
+ p->msg_env = NULL;
+ enif_release_resource(p);
+ return enif_make_badarg(env);
+ }
+ p->msg_env = enif_alloc_env();
+ p->msg = enif_make_copy(p->msg_env, argv[1]);
+ res = enif_make_resource(env, p);
+ enif_release_resource(p);
+ return res;
+}
+
+static void resource_dtor(ErlNifEnv* env, void* obj)
+{
+ DtorSender *p = (DtorSender*)obj;
+
+ if (p->msg_env) {
+ enif_send(env, &p->to, p->msg_env, p->msg);
+ enif_free(p->msg_env);
+ }
+}
+
+
+static ErlNifFunc nif_funcs[] =
+{
+ {"is_loaded", 0, is_loaded_nif},
+ {"send", 2, send_nif}
+};
+
+ERL_NIF_INIT(erts_test_destructor,nif_funcs,load,NULL,NULL,NULL)
diff --git a/erts/emulator/test/port_SUITE.erl b/erts/emulator/test/port_SUITE.erl
index eb9b94a316..8a67bf7512 100644
--- a/erts/emulator/test/port_SUITE.erl
+++ b/erts/emulator/test/port_SUITE.erl
@@ -1052,7 +1052,9 @@ huge_env(Config) when is_list(Config) ->
%% Test to spawn program with command payload buffer
%% just around pipe capacity (9f779819f6bda734c5953468f7798)
pipe_limit_env(Config) when is_list(Config) ->
+ WSL = os:getenv("WSLENV") =/= false,
Cmd = case os:type() of
+ {win32,_} when WSL -> "cmd.exe /q /c wsl true";
{win32,_} -> "cmd /q /c true";
_ -> "true"
end,
@@ -1706,7 +1708,11 @@ spawn_executable(Config) when is_list(Config) ->
ok.
unregister_name(Config) when is_list(Config) ->
- true = register(crash, open_port({spawn, "sleep 100"}, [])),
+ Cmd = case os:getenv("WSLENV") of
+ false -> "sleep 5";
+ _ -> "wsl.exe sleep 5"
+ end,
+ true = register(crash, open_port({spawn, Cmd}, [])),
true = unregister(crash).
test_bat_file(Dir) ->
diff --git a/erts/emulator/test/process_SUITE.erl b/erts/emulator/test/process_SUITE.erl
index 0e1c15e160..a6210a3ca2 100644
--- a/erts/emulator/test/process_SUITE.erl
+++ b/erts/emulator/test/process_SUITE.erl
@@ -63,13 +63,24 @@
system_task_failed_enqueue/1,
gc_request_when_gc_disabled/1,
gc_request_blast_when_gc_disabled/1,
- otp_16436/1]).
+ otp_16436/1,
+ spawn_huge_arglist/1,
+ spawn_request_bif/1,
+ spawn_request_monitor_demonitor/1,
+ spawn_request_monitor_child_exit/1,
+ spawn_request_link_child_exit/1,
+ spawn_request_link_parent_exit/1,
+ spawn_request_abandon_bif/1,
+ dist_spawn_monitor/1,
+ spawn_old_node/1,
+ spawn_new_node/1,
+ spawn_request_reply_option/1]).
-export([prio_server/2, prio_client/2, init/1, handle_event/2]).
-export([init_per_testcase/2, end_per_testcase/2]).
-export([hangaround/2, processes_bif_test/0, do_processes/1,
- processes_term_proc_list_test/1]).
+ processes_term_proc_list_test/1, huge_arglist_child/255]).
suite() ->
[{ct_hooks,[ts_install_cth]},
@@ -90,7 +101,19 @@ all() ->
bump_reductions, low_prio, yield, yield2, otp_4725,
bad_register, garbage_collect, process_info_messages,
process_flag_badarg, process_flag_heap_size,
- spawn_opt_heap_size, spawn_opt_max_heap_size, otp_6237,
+ spawn_opt_heap_size, spawn_opt_max_heap_size,
+ spawn_huge_arglist,
+ spawn_request_bif,
+ spawn_request_monitor_demonitor,
+ spawn_request_monitor_child_exit,
+ spawn_request_link_child_exit,
+ spawn_request_link_parent_exit,
+ spawn_request_abandon_bif,
+ dist_spawn_monitor,
+ spawn_old_node,
+ spawn_new_node,
+ spawn_request_reply_option,
+ otp_6237,
{group, processes_bif},
{group, otp_7738}, garb_other_running,
{group, system_task}].
@@ -2250,7 +2273,7 @@ max_heap_size_test(Option, Size, Kill, ErrorLogger)
when is_map(Option); is_integer(Option) ->
max_heap_size_test([{max_heap_size, Option}], Size, Kill, ErrorLogger);
max_heap_size_test(Option, Size, Kill, ErrorLogger) ->
- OomFun = fun F() -> timer:sleep(5),[lists:seq(1,1000)|F()] end,
+ OomFun = fun () -> oom_fun([]) end,
Pid = spawn_opt(OomFun, Option),
{max_heap_size, MHSz} = erlang:process_info(Pid, max_heap_size),
ct:log("Default: ~p~nOption: ~p~nProc: ~p~n",
@@ -2293,6 +2316,13 @@ max_heap_size_test(Option, Size, Kill, ErrorLogger) ->
%% Make sure that there are no unexpected messages.
receive_unexpected().
+oom_fun(Acc0) ->
+ %% This is tail-recursive since the compiler is smart enough to figure
+ %% out that a body-recursive variant never returns, and loops forever
+ %% without keeping the list alive.
+ timer:sleep(5),
+ oom_fun([lists:seq(1, 1000) | Acc0]).
+
receive_error_messages(Pid) ->
receive
{error, _, {emulator, _, [Pid|_]}} ->
@@ -2327,6 +2357,959 @@ handle_event(Event, Pid) ->
Pid ! Event,
{ok, Pid}.
+huge_arglist_child(A0, A1, A2, A3, A4, A5, A6, A7, A8, A9,
+ A10, A11, A12, A13, A14, A15, A16, A17, A18, A19,
+ A20, A21, A22, A23, A24, A25, A26, A27, A28, A29,
+ A30, A31, A32, A33, A34, A35, A36, A37, A38, A39,
+ A40, A41, A42, A43, A44, A45, A46, A47, A48, A49,
+ A50, A51, A52, A53, A54, A55, A56, A57, A58, A59,
+ A60, A61, A62, A63, A64, A65, A66, A67, A68, A69,
+ A70, A71, A72, A73, A74, A75, A76, A77, A78, A79,
+ A80, A81, A82, A83, A84, A85, A86, A87, A88, A89,
+ A90, A91, A92, A93, A94, A95, A96, A97, A98, A99,
+ A100, A101, A102, A103, A104, A105, A106, A107, A108, A109,
+ A110, A111, A112, A113, A114, A115, A116, A117, A118, A119,
+ A120, A121, A122, A123, A124, A125, A126, A127, A128, A129,
+ A130, A131, A132, A133, A134, A135, A136, A137, A138, A139,
+ A140, A141, A142, A143, A144, A145, A146, A147, A148, A149,
+ A150, A151, A152, A153, A154, A155, A156, A157, A158, A159,
+ A160, A161, A162, A163, A164, A165, A166, A167, A168, A169,
+ A170, A171, A172, A173, A174, A175, A176, A177, A178, A179,
+ A180, A181, A182, A183, A184, A185, A186, A187, A188, A189,
+ A190, A191, A192, A193, A194, A195, A196, A197, A198, A199,
+ A200, A201, A202, A203, A204, A205, A206, A207, A208, A209,
+ A210, A211, A212, A213, A214, A215, A216, A217, A218, A219,
+ A220, A221, A222, A223, A224, A225, A226, A227, A228, A229,
+ A230, A231, A232, A233, A234, A235, A236, A237, A238, A239,
+ A240, A241, A242, A243, A244, A245, A246, A247, A248, A249,
+ A250, A251, A252, A253, A254) ->
+ receive go -> ok end,
+ exit([A0, A1, A2, A3, A4, A5, A6, A7, A8, A9,
+ A10, A11, A12, A13, A14, A15, A16, A17, A18, A19,
+ A20, A21, A22, A23, A24, A25, A26, A27, A28, A29,
+ A30, A31, A32, A33, A34, A35, A36, A37, A38, A39,
+ A40, A41, A42, A43, A44, A45, A46, A47, A48, A49,
+ A50, A51, A52, A53, A54, A55, A56, A57, A58, A59,
+ A60, A61, A62, A63, A64, A65, A66, A67, A68, A69,
+ A70, A71, A72, A73, A74, A75, A76, A77, A78, A79,
+ A80, A81, A82, A83, A84, A85, A86, A87, A88, A89,
+ A90, A91, A92, A93, A94, A95, A96, A97, A98, A99,
+ A100, A101, A102, A103, A104, A105, A106, A107, A108, A109,
+ A110, A111, A112, A113, A114, A115, A116, A117, A118, A119,
+ A120, A121, A122, A123, A124, A125, A126, A127, A128, A129,
+ A130, A131, A132, A133, A134, A135, A136, A137, A138, A139,
+ A140, A141, A142, A143, A144, A145, A146, A147, A148, A149,
+ A150, A151, A152, A153, A154, A155, A156, A157, A158, A159,
+ A160, A161, A162, A163, A164, A165, A166, A167, A168, A169,
+ A170, A171, A172, A173, A174, A175, A176, A177, A178, A179,
+ A180, A181, A182, A183, A184, A185, A186, A187, A188, A189,
+ A190, A191, A192, A193, A194, A195, A196, A197, A198, A199,
+ A200, A201, A202, A203, A204, A205, A206, A207, A208, A209,
+ A210, A211, A212, A213, A214, A215, A216, A217, A218, A219,
+ A220, A221, A222, A223, A224, A225, A226, A227, A228, A229,
+ A230, A231, A232, A233, A234, A235, A236, A237, A238, A239,
+ A240, A241, A242, A243, A244, A245, A246, A247, A248, A249,
+ A250, A251, A252, A253, A254]).
+
+spawn_huge_arglist(Config) when is_list(Config) ->
+ %% Huge in two different ways; encoded size and
+ %% length...
+ ArgListHead = [make_ref(),
+ lists:duplicate(1000000, $a),
+ <<1:8388608>>,
+ processes(),
+ erlang:ports(),
+ {hej, hopp},
+ <<17:8388608>>,
+ lists:duplicate(3000000, $x),
+ #{ a => 1, b => 2, c => 3, d => 4, e => 5}],
+ ArgList = ArgListHead ++ lists:seq(1, 255 - length(ArgListHead)),
+
+ io:format("size(term_to_binary(ArgList)) = ~p~n",
+ [size(term_to_binary(ArgList))]),
+
+ io:format("Testing spawn with huge argument list on local node...~n", []),
+ spawn_huge_arglist_test(true, node(), ArgList),
+ io:format("Testing spawn with huge argument list on local node with Node...~n", []),
+ spawn_huge_arglist_test(false, node(), ArgList),
+ {ok, Node} = start_node(Config),
+ _ = rpc:call(Node, ?MODULE, module_info, []),
+ io:format("Testing spawn with huge argument list on remote node ~p...~n", [Node]),
+ spawn_huge_arglist_test(false, Node, ArgList),
+ stop_node(Node),
+ ok.
+
+spawn_huge_arglist_test(Local, Node, ArgList) ->
+
+ R1 = case Local of
+ true ->
+ spawn_request(?MODULE, huge_arglist_child, ArgList, [monitor]);
+ false ->
+ spawn_request(Node, ?MODULE, huge_arglist_child, ArgList, [monitor])
+ end,
+ receive
+ {spawn_reply, R1, ok, Pid1} ->
+ Pid1 ! go,
+ receive
+ {'DOWN', R1, process, Pid1, Reason1} ->
+ ArgList = Reason1
+ end
+ end,
+
+ {Pid2, R2} = case Local of
+ true ->
+ spawn_monitor(?MODULE, huge_arglist_child, ArgList);
+ false ->
+ spawn_monitor(Node, ?MODULE, huge_arglist_child, ArgList)
+ end,
+ Node = node(Pid2),
+ Pid2 ! go,
+ receive
+ {'DOWN', R2, process, Pid2, Reason2} ->
+ ArgList = Reason2
+ end,
+
+ {Pid3, R3} = case Local of
+ true ->
+ spawn_opt(?MODULE, huge_arglist_child, ArgList, [monitor]);
+ false ->
+ spawn_opt(Node, ?MODULE, huge_arglist_child, ArgList, [monitor])
+ end,
+ Node = node(Pid3),
+ Pid3 ! go,
+ receive
+ {'DOWN', R3, process, Pid3, Reason3} ->
+ ArgList = Reason3
+ end,
+
+ OldTA = process_flag(trap_exit, true),
+ Pid4 = case Local of
+ true ->
+ spawn_link(?MODULE, huge_arglist_child, ArgList);
+ false ->
+ spawn_link(Node, ?MODULE, huge_arglist_child, ArgList)
+ end,
+ Node = node(Pid4),
+ Pid4 ! go,
+ receive
+ {'EXIT', Pid4, Reason4} ->
+ ArgList = Reason4
+ end,
+
+ true = process_flag(trap_exit, OldTA),
+
+ Pid5 = case Local of
+ true ->
+ spawn(?MODULE, huge_arglist_child, ArgList);
+ false ->
+ spawn(Node, ?MODULE, huge_arglist_child, ArgList)
+ end,
+ Node = node(Pid5),
+ R5 = erlang:monitor(process, Pid5),
+ Pid5 ! go,
+ receive
+ {'DOWN', R5, process, Pid5, Reason5} ->
+ ArgList = Reason5
+ end,
+ ok.
+
+spawn_request_bif(Config) when is_list(Config) ->
+ io:format("Testing spawn_request() on local node...~n", []),
+ spawn_request_bif_test(true, node()),
+ io:format("Testing spawn_request() on local node with Node...~n", []),
+ spawn_request_bif_test(false, node()),
+ {ok, Node} = start_node(Config),
+ io:format("Testing spawn_request() on remote node ~p...~n", [Node]),
+ spawn_request_bif_test(false, Node),
+ stop_node(Node),
+ ok.
+
+spawn_request_bif_test(Local, Node) ->
+
+ Me = self(),
+
+ process_flag(trap_exit, true),
+
+ T1 = {test, 1},
+ F1 = fun () -> exit({exit, T1}) end,
+ R1 = if Local ->
+ spawn_request(F1, [{reply_tag, T1}, monitor, link]);
+ true ->
+ spawn_request(Node, F1, [{reply_tag, T1}, monitor, link])
+ end,
+ receive
+ {T1, R1, ok, P1} ->
+ receive
+ {'DOWN', R1, process, P1, {exit, T1}} ->
+ ok
+ end,
+ receive
+ {'EXIT', P1, {exit, T1}} ->
+ ok
+ end
+ end,
+
+ R1b = if Local ->
+ spawn_request(F1, [monitor, link]);
+ true ->
+ spawn_request(Node, F1, [monitor, link])
+ end,
+ receive
+ {spawn_reply, R1b, ok, P1b} ->
+ receive
+ {'DOWN', R1b, process, P1b, {exit, T1}} ->
+ ok
+ end,
+ receive
+ {'EXIT', P1b, {exit, T1}} ->
+ ok
+ end
+ end,
+
+ Ref1c = make_ref(),
+ F1c = fun () -> Me ! Ref1c end,
+ R1c = if Local ->
+ spawn_request(F1c);
+ true ->
+ spawn_request(Node, F1c)
+ end,
+ receive
+ {spawn_reply, R1c, ok, _P1c} ->
+ receive Ref1c -> ok end
+ end,
+
+ R1e = if Local ->
+ spawn_request(F1, [monitors, links, {reply_tag, T1}]);
+ true ->
+ spawn_request(Node, F1, [monitors, links, {reply_tag, T1}])
+ end,
+ receive
+ {T1, R1e, error, BadOpt1} ->
+ badopt = BadOpt1,
+ ok
+ end,
+ ok = try
+ BadF = fun (X) -> exit({X,T1}) end,
+ if Local ->
+ spawn_request(BadF, [monitor, {reply_tag, T1}, link]);
+ true ->
+ spawn_request(Node, BadF, [monitor, {reply_tag, T1}, link])
+ end,
+ nok
+ catch
+ error:badarg -> ok
+ end,
+ ok = try
+ spawn_request(<<"node">>, F1, [monitor, link], T1),
+ nok
+ catch
+ error:badarg -> ok
+ end,
+
+ T2 = {test, 2},
+ M2 = erlang,
+ F2 = exit,
+ Reason2 = {exit, T2},
+ Args2 = [Reason2],
+ R2 = if Local ->
+ spawn_request(M2, F2, Args2, [monitor, link, {reply_tag, T2}]);
+ true ->
+ spawn_request(Node, M2, F2, Args2, [monitor, link, {reply_tag, T2}])
+ end,
+ receive
+ {T2, R2, ok, P2} ->
+ receive
+ {'DOWN', R2, process, P2, Reason2} ->
+ ok
+ end,
+ receive
+ {'EXIT', P2, Reason2} ->
+ ok
+ end
+ end,
+
+ R2b = if Local ->
+ spawn_request(M2, F2, Args2, [monitor, link]);
+ true ->
+ spawn_request(Node, M2, F2, Args2, [monitor, link])
+ end,
+ receive
+ {spawn_reply, R2b, ok, P2b} ->
+ receive
+ {'DOWN', R2b, process, P2b, Reason2} ->
+ ok
+ end,
+ receive
+ {'EXIT', P2b, Reason2} ->
+ ok
+ end
+ end,
+
+ Ref2c = make_ref(),
+ R2c = if Local ->
+ spawn_request(erlang, send, [Me, Ref2c]);
+ true ->
+ spawn_request(Node, erlang, send, [Me, Ref2c])
+ end,
+ receive
+ {spawn_reply, R2c, ok, _P2c} ->
+ receive Ref2c -> ok end
+ end,
+
+ R2e = if Local ->
+ spawn_request(M2, F2, Args2, [monitors, {reply_tag, T2}, links]);
+ true ->
+ spawn_request(Node, M2, F2, Args2, [monitors, {reply_tag, T2}, links])
+ end,
+ receive
+ {T2, R2e, error, BadOpt2} ->
+ badopt = BadOpt2,
+ ok
+ end,
+
+ R2eb = if Local ->
+ spawn_request(M2, F2, Args2, [monitors, links]);
+ true ->
+ spawn_request(Node, M2, F2, Args2, [monitors, links])
+ end,
+ receive
+ {spawn_reply, R2eb, error, BadOpt2b} ->
+ badopt = BadOpt2b,
+ ok
+ end,
+
+ ok = try
+ if Local ->
+ spawn_request(M2, F2, [Args2|oops], [monitor, link, {reply_tag, T2}]);
+ true ->
+ spawn_request(Node, M2, F2, [Args2|oops], [monitor, link, {reply_tag, T2}])
+ end,
+ nok
+ catch
+ error:badarg -> ok
+ end,
+ ok = try
+ if Local ->
+ spawn_request(M2, F2, [Args2|oops], [monitor, {reply_tag, blupp}, link]);
+ true ->
+ spawn_request(Node, M2, F2, [Args2|oops], [monitor, {reply_tag, blupp}, link])
+ end,
+ nok
+ catch
+ error:badarg -> ok
+ end,
+ ok = try
+ if Local ->
+ spawn_request(M2, F2, [Args2|oops]);
+ true ->
+ spawn_request(Node, M2, F2, [Args2|oops])
+ end,
+ nok
+ catch
+ error:badarg -> ok
+ end,
+ ok = try
+ if Local ->
+ spawn_request(M2, <<"exit">>, Args2, [monitor, {reply_tag, T2}, link]);
+ true ->
+ spawn_request(Node, M2, <<"exit">>, Args2, [monitor, {reply_tag, T2}, link])
+ end,
+ nok
+ catch
+ error:badarg -> ok
+ end,
+ ok = try
+ if Local ->
+ spawn_request(M2, <<"exit">>, Args2, [monitor, link]);
+ true ->
+ spawn_request(Node, M2, <<"exit">>, Args2, [monitor, link])
+ end,
+ nok
+ catch
+ error:badarg -> ok
+ end,
+ ok = try
+ if Local ->
+ spawn_request(M2, <<"exit">>, Args2);
+ true ->
+ spawn_request(Node, M2, <<"exit">>, Args2)
+ end,
+ nok
+ catch
+ error:badarg -> ok
+ end,
+ ok = try
+ if Local ->
+ spawn_request(<<"erlang">>, F2, Args2, [{reply_tag, T2}, monitor, link]);
+ true ->
+ spawn_request(Node, <<"erlang">>, F2, Args2, [{reply_tag, T2}, monitor, link])
+ end,
+ nok
+ catch
+ error:badarg -> ok
+ end,
+ ok = try
+ if Local ->
+ spawn_request(<<"erlang">>, F2, Args2, [monitor, link]);
+ true ->
+ spawn_request(Node, <<"erlang">>, F2, Args2, [monitor, link])
+ end,
+ nok
+ catch
+ error:badarg -> ok
+ end,
+ ok = try
+ if Local ->
+ spawn_request(<<"erlang">>, F2, Args2);
+ true ->
+ spawn_request(Node, <<"erlang">>, F2, Args2)
+ end,
+ nok
+ catch
+ error:badarg -> ok
+ end,
+ ok = try
+ spawn_request(<<"node">>, M2, F2, Args2, [{reply_tag, T2}, monitor, link]),
+ nok
+ catch
+ error:badarg -> ok
+ end,
+ ok = try
+ spawn_request(<<"node">>, M2, F2, Args2, [monitor, link]),
+ nok
+ catch
+ error:badarg -> ok
+ end,
+ ok = try
+ spawn_request(<<"node">>, M2, F2, Args2),
+ nok
+ catch
+ error:badarg -> ok
+ end,
+ ok.
+
+
+spawn_request_monitor_demonitor(Config) when is_list(Config) ->
+ {ok, Node} = start_node(Config),
+ BlockFun = fun () ->
+ erts_debug:set_internal_state(available_internal_state, true),
+ erts_debug:set_internal_state(block, 1000),
+ ok
+ end,
+
+ %% Block receiver node...
+ spawn_request(Node, BlockFun, [{priority,max}, link]),
+ receive after 100 -> ok end,
+
+ erlang:display(spawning),
+ erlang:yield(),
+ R = spawn_request(Node, timer, sleep, [10000], [monitor]),
+ %% Should not be possible to demonitor
+ %% before operation has succeeded...
+ erlang:display(premature_demonitor),
+ {monitors, []} = process_info(self(), monitors),
+ false = erlang:demonitor(R, [info]), %% Should be ignored by VM...
+ erlang:display(wait_success),
+ receive
+ {spawn_reply, R, ok, P} ->
+ erlang:display(demonitor),
+ {monitors, [{process,P}]} = process_info(self(), monitors),
+ true = erlang:demonitor(R, [info]),
+ {monitors, []} = process_info(self(), monitors),
+ exit(P, kill)
+ end,
+ erlang:display(done),
+ stop_node(Node),
+ ok.
+
+spawn_request_monitor_child_exit(Config) when is_list(Config) ->
+ %% Early child exit...
+ Tag = {a, tag},
+ R1 = spawn_request(nonexisting_module, nonexisting_function, [], [monitor, {reply_tag, Tag}]),
+ receive
+ {Tag, R1, ok, P1} ->
+ receive
+ {'DOWN', R1, process, P1, Reason1} ->
+ {undef, _} = Reason1
+ end
+ end,
+ {ok, Node} = start_node(Config),
+ R2 = spawn_request(Node, nonexisting_module, nonexisting_function, [], [{reply_tag, Tag}, monitor]),
+ receive
+ {Tag, R2, ok, P2} ->
+ receive
+ {'DOWN', R2, process, P2, Reason2} ->
+ {undef, _} = Reason2
+ end
+ end,
+ stop_node(Node),
+ ok.
+
+spawn_request_link_child_exit(Config) when is_list(Config) ->
+ %% Early child exit...
+ process_flag(trap_exit, true),
+ Tag = {a, tag},
+ R1 = spawn_request(nonexisting_module, nonexisting_function, [], [{reply_tag, Tag}, link]),
+ receive
+ {Tag, R1, ok, P1} ->
+ receive
+ {'EXIT', P1, Reason1} ->
+ {undef, _} = Reason1
+ end
+ end,
+ {ok, Node} = start_node(Config),
+ R2 = spawn_request(Node, nonexisting_module, nonexisting_function, [], [link, {reply_tag, Tag}]),
+ receive
+ {Tag, R2, ok, P2} ->
+ receive
+ {'EXIT', P2, Reason2} ->
+ {undef, _} = Reason2
+ end
+ end,
+ stop_node(Node),
+ ok.
+
+spawn_request_link_parent_exit(Config) when is_list(Config) ->
+ C1 = spawn_request_link_parent_exit_test(node()),
+ {ok, Node} = start_node(Config),
+ C2 = spawn_request_link_parent_exit_test(Node),
+ stop_node(Node),
+ {comment, C1 ++ " " ++ C2}.
+
+spawn_request_link_parent_exit_test(Node) ->
+ %% Early parent exit...
+ Tester = self(),
+
+ verify_nc(node()),
+
+ %% Ensure code loaded on other node...
+ _ = rpc:call(Node, ?MODULE, module_info, []),
+
+ ChildFun = fun () ->
+ Child = self(),
+ spawn_opt(fun () ->
+ process_flag(trap_exit, true),
+ receive
+ {'EXIT', Child, Reason} ->
+ Tester ! {parent_exit, Reason}
+ end
+ end, [link,{priority,max}]),
+ receive after infinity -> ok end
+ end,
+ ParentFun = case node() == Node of
+ true ->
+ fun (Wait) ->
+ spawn_request(ChildFun, [link,{priority,max}]),
+ receive after Wait -> ok end,
+ exit(kaboom)
+ end;
+ false ->
+ fun (Wait) ->
+ spawn_request(Node, ChildFun, [link,{priority,max}]),
+ receive after Wait -> ok end,
+ exit(kaboom)
+ end
+ end,
+ lists:foreach(fun (N) ->
+ spawn(fun () -> ParentFun(N rem 10) end)
+ end,
+ lists:seq(1, 1000)),
+ N = gather_parent_exits(kaboom, false),
+ Comment = case node() == Node of
+ true ->
+ C = "Got " ++ integer_to_list(N) ++ " node local kabooms!",
+ erlang:display(C),
+ C;
+ false ->
+ C = "Got " ++ integer_to_list(N) ++ " node remote kabooms!",
+ erlang:display(C),
+ true = N /= 0,
+ C
+ end,
+ Comment.
+
+spawn_request_abandon_bif(Config) when is_list(Config) ->
+ {ok, Node} = start_node(Config),
+ false = spawn_request_abandon(make_ref()),
+ false = spawn_request_abandon(spawn_request(fun () -> ok end)),
+ false = spawn_request_abandon(rpc:call(Node, erlang, make_ref, [])),
+ try
+ noreturn = spawn_request_abandon(self())
+ catch
+ error:badarg ->
+ ok
+ end,
+ try
+ noreturn = spawn_request_abandon(4711)
+ catch
+ error:badarg ->
+ ok
+ end,
+
+ verify_nc(node()),
+
+ %% Ensure code loaded on other node...
+ _ = rpc:call(Node, ?MODULE, module_info, []),
+
+
+ TotOps = 1000,
+ Tester = self(),
+
+ ChildFun = fun () ->
+ Child = self(),
+ spawn_opt(fun () ->
+ process_flag(trap_exit, true),
+ receive
+ {'EXIT', Child, Reason} ->
+ Tester ! {parent_exit, Reason}
+ end
+ end, [link,{priority,max}]),
+ receive after infinity -> ok end
+ end,
+ ParentFun = fun (Wait, Opts) ->
+ ReqId = spawn_request(Node, ChildFun, Opts),
+ receive after Wait -> ok end,
+ case spawn_request_abandon(ReqId) of
+ true ->
+ ok;
+ false ->
+ receive
+ {spawn_reply, ReqId, error, _} ->
+ exit(spawn_failed);
+ {spawn_reply, ReqId, ok, Pid} ->
+ unlink(Pid),
+ exit(Pid, bye)
+ after
+ 0 ->
+ exit(missing_spawn_reply)
+ end
+ end
+ end,
+ %% Parent exit early...
+ lists:foreach(fun (N) ->
+ spawn_opt(fun () ->
+ ParentFun(N rem 50, [link])
+ end, [link,{priority,max}])
+ end,
+ lists:seq(1, TotOps)),
+ NoA1 = gather_parent_exits(abandoned, true),
+ %% Parent exit late...
+ lists:foreach(fun (N) ->
+ spawn_opt(fun () ->
+ ParentFun(N rem 50, [link]),
+ receive
+ {spawn_reply, _, _, _} ->
+ exit(unexpected_spawn_reply)
+ after
+ 1000 -> ok
+ end
+ end, [link,{priority,max}])
+ end,
+ lists:seq(1, TotOps)),
+ NoA2 = gather_parent_exits(abandoned, true),
+ %% Parent exit early...
+ lists:foreach(fun (N) ->
+ spawn_opt(fun () ->
+ ParentFun(N rem 50, [])
+ end, [link,{priority,max}])
+ end,
+ lists:seq(1, TotOps)),
+ 0 = gather_parent_exits(abandoned, true),
+ %% Parent exit late...
+ lists:foreach(fun (N) ->
+ spawn_opt(fun () ->
+ ParentFun(N rem 50, []),
+ receive
+ {spawn_reply, _, _, _} ->
+ exit(unexpected_spawn_reply)
+ after
+ 1000 -> ok
+ end
+ end, [link,{priority,max}])
+ end,
+ lists:seq(1, TotOps)),
+ 0 = gather_parent_exits(abandoned, true),
+ stop_node(Node),
+ C = "Got " ++ integer_to_list(NoA1) ++ " and "
+ ++ integer_to_list(NoA2) ++ " abandoneds of 2*"
+ ++ integer_to_list(TotOps) ++ " ops!",
+ erlang:display(C),
+ true = NoA1 /= 0,
+ true = NoA1 /= TotOps,
+ true = NoA2 /= 0,
+ true = NoA2 /= TotOps,
+ {comment, C}.
+
+gather_parent_exits(Reason, AllowOther) ->
+ receive after 2000 -> ok end,
+ gather_parent_exits(Reason, AllowOther, 0).
+
+gather_parent_exits(Reason, AllowOther, N) ->
+ receive
+ {parent_exit, Reason} ->
+ gather_parent_exits(Reason, AllowOther, N+1);
+ {parent_exit, _} = ParentExit ->
+ case AllowOther of
+ false ->
+ ct:fail(ParentExit);
+ true ->
+ gather_parent_exits(Reason, AllowOther, N)
+ end
+ after 0 ->
+ N
+ end.
+dist_spawn_monitor(Config) when is_list(Config) ->
+ {ok, Node} = start_node(Config),
+ R1 = spawn_request(Node, erlang, exit, [hej], [monitor]),
+ receive
+ {spawn_reply, R1, ok, P1} ->
+ receive
+ {'DOWN', R1, process, P1, Reason1} ->
+ hej = Reason1
+ end
+ end,
+ {P2, Mon2} = spawn_monitor(Node, erlang, exit, [hej]),
+ receive
+ {'DOWN', Mon2, process, P2, Reason2} ->
+ hej = Reason2
+ end,
+ {P3, Mon3} = spawn_opt(Node, erlang, exit, [hej], [monitor]),
+ receive
+ {'DOWN', Mon3, process, P3, Reason3} ->
+ hej = Reason3
+ end,
+ stop_node(Node),
+ ok.
+
+spawn_old_node(Config) when is_list(Config) ->
+ Cookie = atom_to_list(erlang:get_cookie()),
+ Rel = "22_latest",
+ case test_server:is_release_available(Rel) of
+ false ->
+ {skipped, "No OTP 22 available"};
+ true ->
+ {ok, OldNode} = test_server:start_node(make_nodename(Config),
+ peer,
+ [{args, " -setcookie "++Cookie},
+ {erl, [{release, Rel}]}]),
+ try
+ %% Spawns triggering a new connection; which
+ %% will trigger hopeful data transcoding
+ %% of spawn requests...
+ io:format("~n~nDoing initial connect tests...~n", []),
+ spawn_old_node_test(OldNode, true),
+ %% Spawns on an already existing connection...
+ io:format("~n~nDoing already connected tests...~n", []),
+ spawn_old_node_test(OldNode, false)
+ after
+ test_server:stop_node(OldNode)
+ end,
+ ok
+ end.
+
+spawn_new_node(Config) when is_list(Config) ->
+ Cookie = atom_to_list(erlang:get_cookie()),
+ %% Test that the same operations as in spawn_old_node test
+ %% works as expected on current OTP...
+ {ok, CurrNode} = test_server:start_node(make_nodename(Config),
+ peer,
+ [{args, " -setcookie "++Cookie}]),
+ try
+ %% Spawns triggering a new connection; which
+ %% will trigger hopeful data transcoding
+ %% of spawn requests...
+ io:format("~n~nDoing initial connect tests...~n", []),
+ spawn_current_node_test(CurrNode, true),
+ io:format("~n~nDoing already connected tests...~n", []),
+ %% Spawns on an already existing connection...
+ spawn_current_node_test(CurrNode, false)
+ after
+ test_server:stop_node(CurrNode)
+ end.
+
+disconnect_node(Node, Disconnect) ->
+ case Disconnect of
+ false ->
+ ok;
+ true ->
+ monitor_node(Node, true),
+ erlang:disconnect_node(Node),
+ receive {nodedown, Node} -> ok end
+ end.
+
+spawn_old_node_test(Node, Disconnect) ->
+ io:format("Testing spawn_request() on old node...", []),
+ disconnect_node(Node, Disconnect),
+ R1 = spawn_request(Node, erlang, exit, [hej], [monitor, {reply_tag, a_tag}]),
+ receive
+ {a_tag, R1, Err, Notsup} ->
+ error = Err,
+ notsup = Notsup,
+ ok
+ end,
+ io:format("Testing spawn_monitor() on old node...", []),
+ disconnect_node(Node, Disconnect),
+ try
+ spawn_monitor(Node, erlang, exit, [hej])
+ catch
+ error:notsup ->
+ ok
+ end,
+ io:format("Testing spawn_opt() with monitor on old node...", []),
+ disconnect_node(Node, Disconnect),
+ try
+ spawn_opt(Node, erlang, exit, [hej], [monitor])
+ catch
+ error:badarg ->
+ ok
+ end,
+ io:format("Testing spawn_opt() with link on old node...", []),
+ disconnect_node(Node, Disconnect),
+ process_flag(trap_exit, true),
+ P1 = spawn_opt(Node, erlang, exit, [hej], [link]),
+ Node = node(P1),
+ receive
+ {'EXIT', P1, hej} ->
+ ok
+ end,
+ io:format("Testing spawn_link() on old node...", []),
+ disconnect_node(Node, Disconnect),
+ P2 = spawn_link(Node, erlang, exit, [hej]),
+ Node = node(P2),
+ receive
+ {'EXIT', P2, hej} ->
+ ok
+ end.
+
+spawn_current_node_test(Node, Disconnect) ->
+ io:format("Testing spawn_request() on new node...", []),
+ disconnect_node(Node, Disconnect),
+ R1 = spawn_request(Node, erlang, exit, [hej], [monitor, {reply_tag, a_tag}]),
+ receive
+ {a_tag, R1, ok, P1} ->
+ Node = node(P1),
+ receive
+ {'DOWN', R1, process, P1, hej} -> ok
+ end
+ end,
+ io:format("Testing spawn_monitor() on new node...", []),
+ disconnect_node(Node, Disconnect),
+ {P2, M2} = spawn_monitor(Node, erlang, exit, [hej]),
+ receive
+ {'DOWN', M2, process, P2, hej} -> ok
+ end,
+ Node = node(P2),
+ io:format("Testing spawn_opt() with monitor on new node...", []),
+ disconnect_node(Node, Disconnect),
+ {P3, M3} = spawn_opt(Node, erlang, exit, [hej], [monitor]),
+ receive
+ {'DOWN', M3, process, P3, hej} -> ok
+ end,
+ Node = node(P3),
+ io:format("Testing spawn_opt() with link on new node...", []),
+ disconnect_node(Node, Disconnect),
+ process_flag(trap_exit, true),
+ P4 = spawn_opt(Node, erlang, exit, [hej], [link]),
+ Node = node(P4),
+ receive
+ {'EXIT', P4, hej} ->
+ ok
+ end,
+ io:format("Testing spawn_link() on new node...", []),
+ disconnect_node(Node, Disconnect),
+ P5 = spawn_link(Node, erlang, exit, [hej]),
+ Node = node(P5),
+ receive
+ {'EXIT', P5, hej} ->
+ ok
+ end.
+
+spawn_request_reply_option(Config) when is_list(Config) ->
+ spawn_request_reply_option_test(node()),
+ {ok, Node} = start_node(Config),
+ spawn_request_reply_option_test(Node).
+
+spawn_request_reply_option_test(Node) ->
+ io:format("Testing on node: ~p~n", [Node]),
+ Parent = self(),
+ Done1 = make_ref(),
+ RID1 = spawn_request(Node, fun () -> Parent ! Done1 end, [{reply, yes}]),
+ receive Done1 -> ok end,
+ receive
+ {spawn_reply, RID1, ok, _} -> ok
+ after 0 ->
+ ct:fail(missing_spawn_reply)
+ end,
+ Done2 = make_ref(),
+ RID2 = spawn_request(Node, fun () -> Parent ! Done2 end, [{reply, success_only}]),
+ receive Done2 -> ok end,
+ receive
+ {spawn_reply, RID2, ok, _} -> ok
+ after 0 ->
+ ct:fail(missing_spawn_reply)
+ end,
+ Done3 = make_ref(),
+ RID3 = spawn_request(Node, fun () -> Parent ! Done3 end, [{reply, error_only}]),
+ receive Done3 -> ok end,
+ receive
+ {spawn_reply, RID3, _, _} ->
+ ct:fail(unexpected_spawn_reply)
+ after 0 ->
+ ok
+ end,
+ Done4 = make_ref(),
+ RID4 = spawn_request(Node, fun () -> Parent ! Done4 end, [{reply, no}]),
+ receive Done4 -> ok end,
+ receive
+ {spawn_reply, RID4, _, _} ->
+ ct:fail(unexpected_spawn_reply)
+ after 0 ->
+ ok
+ end,
+ RID5 = spawn_request(Node, fun () -> ok end, [{reply, yes}, bad_option]),
+ receive
+ {spawn_reply, RID5, error, badopt} -> ok
+ end,
+ RID6 = spawn_request(Node, fun () -> ok end, [{reply, success_only}, bad_option]),
+ receive
+ {spawn_reply, RID6, error, badopt} -> ct:fail(unexpected_spawn_reply)
+ after 1000 -> ok
+ end,
+ RID7 = spawn_request(Node, fun () -> ok end, [{reply, error_only}, bad_option]),
+ receive
+ {spawn_reply, RID7, error, badopt} -> ok
+ end,
+ RID8 = spawn_request(Node, fun () -> ok end, [{reply, no}, bad_option]),
+ receive
+ {spawn_reply, RID8, error, badopt} -> ct:fail(unexpected_spawn_reply)
+ after 1000 -> ok
+ end,
+ case Node == node() of
+ true ->
+ ok;
+ false ->
+ stop_node(Node),
+ RID9 = spawn_request(Node, fun () -> ok end, [{reply, yes}]),
+ receive
+ {spawn_reply, RID9, error, noconnection} -> ok
+ end,
+ RID10 = spawn_request(Node, fun () -> ok end, [{reply, success_only}]),
+ receive
+ {spawn_reply, RID10, error, noconnection} -> ct:fail(unexpected_spawn_reply)
+ after 1000 -> ok
+ end,
+ RID11 = spawn_request(Node, fun () -> ok end, [{reply, error_only}]),
+ receive
+ {spawn_reply, RID11, error, noconnection} -> ok
+ end,
+ RID12 = spawn_request(Node, fun () -> ok end, [{reply, no}]),
+ receive
+ {spawn_reply, RID12, error, noconnection} -> ct:fail(unexpected_spawn_reply)
+ after 1000 -> ok
+ end,
+ ok
+ end.
+
processes_term_proc_list(Config) when is_list(Config) ->
Tester = self(),
@@ -2897,24 +3880,50 @@ tok_loop(hopp) ->
tok_loop(hej).
id(I) -> I.
+
+make_nodename(Config) when is_list(Config) ->
+ list_to_atom(atom_to_list(?MODULE)
+ ++ "-"
+ ++ atom_to_list(proplists:get_value(testcase, Config))
+ ++ "-"
+ ++ integer_to_list(erlang:system_time(second))
+ ++ "-"
+ ++ integer_to_list(erlang:unique_integer([positive]))).
start_node(Config) ->
start_node(Config, "").
start_node(Config, Args) when is_list(Config) ->
Pa = filename:dirname(code:which(?MODULE)),
- Name = list_to_atom(atom_to_list(?MODULE)
- ++ "-"
- ++ atom_to_list(proplists:get_value(testcase, Config))
- ++ "-"
- ++ integer_to_list(erlang:system_time(second))
- ++ "-"
- ++ integer_to_list(erlang:unique_integer([positive]))),
+ Name = make_nodename(Config),
test_server:start_node(Name, slave, [{args, "-pa "++Pa++" "++Args}]).
stop_node(Node) ->
+ verify_nc(node()),
+ verify_nc(Node),
test_server:stop_node(Node).
+verify_nc(Node) ->
+ P = self(),
+ Ref = make_ref(),
+ Pid = spawn(Node,
+ fun() ->
+ R = erts_test_utils:check_node_dist(fun(E) -> E end),
+ P ! {Ref, R}
+ end),
+ MonRef = monitor(process, Pid),
+ receive
+ {Ref, ok} ->
+ demonitor(MonRef,[flush]),
+ ok;
+ {Ref, Error} ->
+ ct:log("~s",[Error]),
+ ct:fail(failed_nc_refc_check);
+ {'DOWN', MonRef, _, _, _} = Down ->
+ ct:log("~p",[Down]),
+ ct:fail(crashed_nc_refc_check)
+ end.
+
enable_internal_state() ->
case catch erts_debug:get_internal_state(available_internal_state) of
true -> true;
diff --git a/erts/emulator/test/property_test/phash2_properties.erl b/erts/emulator/test/property_test/phash2_properties.erl
new file mode 100644
index 0000000000..b1f3207c56
--- /dev/null
+++ b/erts/emulator/test/property_test/phash2_properties.erl
@@ -0,0 +1,63 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2019-2019. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+%%
+
+-module(phash2_properties).
+
+-ifdef(PROPER).
+
+-include_lib("proper/include/proper.hrl").
+-export([prop_phash2_same_with_same_input/0,
+ prop_phash2_same_with_same_long_input/0,
+ prop_phash2_same_in_different_versions/1,
+ prop_phash2_same_in_different_versions_with_long_input/1]).
+-proptest([proper]).
+
+%%--------------------------------------------------------------------
+%% Properties --------------------------------------------------------
+%%--------------------------------------------------------------------
+
+prop_phash2_same_with_same_input() ->
+ ?FORALL(T, any(), erlang:phash2(T) =:= erlang:phash2(T)).
+
+prop_phash2_same_with_same_long_input() ->
+ ?FORALL(T, any(),
+ begin
+ BigTerm = lists:duplicate(10000, T),
+ erlang:phash2(BigTerm) =:= erlang:phash2(BigTerm)
+ end).
+
+prop_phash2_same_in_different_versions(DifferntVersionNode) ->
+ ?FORALL(T, any(),
+ erlang:phash2(T) =:= rpc:call(DifferntVersionNode,erlang,phash2,[T])).
+
+prop_phash2_same_in_different_versions_with_long_input(DifferntVersionNode) ->
+ ?FORALL(T, any(),
+ begin
+ BigTerm = lists:duplicate(10000, T),
+ RpcRes = rpc:call(DifferntVersionNode,erlang,phash2,[BigTerm]),
+ LocalRes = erlang:phash2(BigTerm),
+ RpcRes =:= LocalRes
+ end).
+
+%%--------------------------------------------------------------------
+%% Generators -------------------------------------------------------
+%%--------------------------------------------------------------------
+
+-endif.
diff --git a/erts/emulator/test/scheduler_SUITE.erl b/erts/emulator/test/scheduler_SUITE.erl
index 59cf66d277..f477af1b5b 100644
--- a/erts/emulator/test/scheduler_SUITE.erl
+++ b/erts/emulator/test/scheduler_SUITE.erl
@@ -995,62 +995,81 @@ sct_cmd(Config) when is_list(Config) ->
{"db", thread_no_node_processor_spread}]).
sbt_cmd(Config) when is_list(Config) ->
- Bind = try
- OldVal = erlang:system_flag(scheduler_bind_type, default_bind),
- erlang:system_flag(scheduler_bind_type, OldVal),
- go_for_it
- catch
- error:notsup -> notsup;
- error:_ -> go_for_it
- end,
- case Bind of
- notsup ->
- {skipped, "Binding of schedulers not supported"};
- go_for_it ->
- CpuTCmd = case erlang:system_info({cpu_topology,detected}) of
- undefined ->
- case os:type() of
- linux ->
- case erlang:system_info(logical_processors) of
- 1 ->
- "+sctL0";
- N when is_integer(N) ->
- NS = integer_to_list(N-1),
- "+sctL0-"++NS++"p0-"++NS;
- _ ->
- false
- end;
- _ ->
- false
- end;
- _ ->
- ""
- end,
- case CpuTCmd of
- false ->
- {skipped, "Don't know how to create cpu topology"};
- _ ->
- case erlang:system_info(logical_processors) of
- LP when is_integer(LP) ->
- OldRelFlags = clear_erl_rel_flags(),
- try
- lists:foreach(fun ({ClBt, Bt}) ->
- sbt_test(Config,
- CpuTCmd,
- ClBt,
- Bt,
- LP)
- end,
- ?BIND_TYPES)
- after
- restore_erl_rel_flags(OldRelFlags)
- end,
- ok;
- _ ->
- {skipped,
- "Don't know the amount of logical processors"}
- end
- end
+ case sbt_check_prereqs() of
+ {skipped, _Reason}=Skipped ->
+ Skipped;
+ ok ->
+ case sbt_make_topology_args() of
+ false ->
+ {skipped, "Don't know how to create cpu topology"};
+ CpuTCmd ->
+ LP = erlang:system_info(logical_processors),
+ OldRelFlags = clear_erl_rel_flags(),
+ try
+ lists:foreach(fun ({ClBt, Bt}) ->
+ sbt_test(Config, CpuTCmd,
+ ClBt, Bt, LP)
+ end,
+ ?BIND_TYPES)
+ after
+ restore_erl_rel_flags(OldRelFlags)
+ end,
+ ok
+ end
+ end.
+
+sbt_make_topology_args() ->
+ case erlang:system_info({cpu_topology,detected}) of
+ undefined ->
+ case os:type() of
+ linux ->
+ case erlang:system_info(logical_processors) of
+ 1 ->
+ "+sctL0";
+ N ->
+ NS = integer_to_list(N - 1),
+ "+sctL0-"++NS++"p0-"++NS
+ end;
+ _ ->
+ false
+ end;
+ _ ->
+ ""
+ end.
+
+sbt_check_prereqs() ->
+ try
+ Available = erlang:system_info(logical_processors_available),
+ Quota = erlang:system_info(cpu_quota),
+ if
+ Quota =:= unknown; Quota >= Available ->
+ ok;
+ Quota < Available ->
+ throw({skipped, "Test requires that CPU quota is greater than "
+ "the number of available processors."})
+ end,
+
+ try
+ OldVal = erlang:system_flag(scheduler_bind_type, default_bind),
+ erlang:system_flag(scheduler_bind_type, OldVal)
+ catch
+ error:notsup ->
+ throw({skipped, "Scheduler binding not supported."});
+ error:_ ->
+ %% ?!
+ ok
+ end,
+
+ case erlang:system_info(logical_processors) of
+ Count when is_integer(Count) ->
+ ok;
+ unknown ->
+ throw({skipped, "Can't detect number of logical processors."})
+ end,
+
+ ok
+ catch
+ throw:{skip,_Reason}=Skip -> Skip
end.
sbt_test(Config, CpuTCmd, ClBt, Bt, LP) ->
@@ -1112,28 +1131,48 @@ scheduler_threads(Config) when is_list(Config) ->
{Sched, HalfSchedOnln, _} = get_sstate(Config, "+SP:50"),
%% Configure 2x scheduler threads only
{TwiceSched, SchedOnln, _} = get_sstate(Config, "+SP 200"),
- case {erlang:system_info(logical_processors),
- erlang:system_info(logical_processors_available)} of
- {LProc, LProcAvail} when is_integer(LProc), is_integer(LProcAvail) ->
- %% Test resetting the scheduler counts
- ResetCmd = "+S "++FourSched++":"++FourSchedOnln++" +S 0:0",
- {LProc, LProcAvail, _} = get_sstate(Config, ResetCmd),
- %% Test negative +S settings, but only for SMP-enabled emulators
- case {LProc > 1, LProcAvail > 1} of
- {true, true} ->
- SchedMinus1 = LProc-1,
- SchedOnlnMinus1 = LProcAvail-1,
- {SchedMinus1, SchedOnlnMinus1, _} = get_sstate(Config, "+S -1"),
- {LProc, SchedOnlnMinus1, _} = get_sstate(Config, "+S :-1"),
- {SchedMinus1, SchedOnlnMinus1, _} = get_sstate(Config, "+S -1:-1"),
- ok;
- _ ->
- {comment, "Skipped reduced amount of schedulers test due to too few logical processors"}
- end;
- _ -> %% Skipped when missing info about logical processors...
- {comment, "Skipped reset amount of schedulers test, and reduced amount of schedulers test due to too unknown amount of logical processors"}
+
+ LProc = erlang:system_info(logical_processors),
+ LProcAvail = erlang:system_info(logical_processors_available),
+ Quota = erlang:system_info(cpu_quota),
+
+ if
+ not is_integer(LProc); not is_integer(LProcAvail) ->
+ {comment, "Skipped reset amount of schedulers test, and reduced "
+ "amount of schedulers test due to too unknown amount of "
+ "logical processors"};
+ is_integer(LProc); is_integer(LProcAvail) ->
+ ExpectedOnln = st_expected_onln(LProcAvail, Quota),
+
+ st_reset(Config, LProc, ExpectedOnln, FourSched, FourSchedOnln),
+
+ if
+ LProc =:= 1; LProcAvail =:= 1 ->
+ {comment, "Skipped reduced amount of schedulers test due "
+ "to too few logical processors"};
+ LProc > 1, LProcAvail > 1 ->
+ st_reduced(Config, LProc, ExpectedOnln)
+ end
end.
+st_reset(Config, LProc, ExpectedOnln, FourSched, FourSchedOnln) ->
+ %% Test resetting # of schedulers.
+ ResetCmd = "+S "++FourSched++":"++FourSchedOnln++" +S 0:0",
+ {LProc, ExpectedOnln, _} = get_sstate(Config, ResetCmd),
+ ok.
+
+st_reduced(Config, LProc, ExpectedOnln) ->
+ %% Test negative +S settings
+ SchedMinus1 = LProc-1,
+ SchedOnlnMinus1 = ExpectedOnln-1,
+ {SchedMinus1, SchedOnlnMinus1, _} = get_sstate(Config, "+S -1"),
+ {LProc, SchedOnlnMinus1, _} = get_sstate(Config, "+S :-1"),
+ {SchedMinus1, SchedOnlnMinus1, _} = get_sstate(Config, "+S -1:-1"),
+ ok.
+
+st_expected_onln(LProcAvail, unknown) -> LProcAvail;
+st_expected_onln(LProcAvail, Quota) -> min(LProcAvail, Quota).
+
dirty_scheduler_threads(Config) when is_list(Config) ->
case erlang:system_info(dirty_cpu_schedulers) of
0 -> {skipped, "No dirty scheduler support"};
diff --git a/erts/emulator/test/send_term_SUITE.erl b/erts/emulator/test/send_term_SUITE.erl
index 8afe4e4ac1..6fa19c47d4 100644
--- a/erts/emulator/test/send_term_SUITE.erl
+++ b/erts/emulator/test/send_term_SUITE.erl
@@ -156,27 +156,11 @@ receive_any() ->
end.
chk_temp_alloc() ->
- case erlang:system_info({allocator,temp_alloc}) of
- false ->
- %% Temp alloc is not enabled
- ok;
- TIL ->
- %% Verify that we havn't got anything allocated by temp_alloc
- lists:foreach(
- fun ({instance, _, TI}) ->
- {value, {mbcs, MBCInfo}}
- = lists:keysearch(mbcs, 1, TI),
- {value, {blocks, 0, _, _}}
- = lists:keysearch(blocks, 1, MBCInfo),
- {value, {sbcs, SBCInfo}}
- = lists:keysearch(sbcs, 1, TI),
- {value, {blocks, 0, _, _}}
- = lists:keysearch(blocks, 1, SBCInfo)
- end,
- TIL),
- ok
+ %% Verify that we haven't got any outstanding temp_alloc allocations.
+ case erts_debug:alloc_blocks_size(temp_alloc) of
+ undefined -> ok;
+ 0 -> ok
end.
-
%% Start/stop drivers.
start_driver(Config, Name) ->
diff --git a/erts/emulator/test/small_SUITE.erl b/erts/emulator/test/small_SUITE.erl
index 00a02e5560..7dbe1fb4f4 100644
--- a/erts/emulator/test/small_SUITE.erl
+++ b/erts/emulator/test/small_SUITE.erl
@@ -78,6 +78,15 @@ sp2_1(N, MinS, MaxS) when N > 0 ->
[N | sp2_1(N bsl 1, MinS, MaxS)].
arith_test(A, B, MinS, MaxS) ->
+ try arith_test_1(A, B, MinS, MaxS) of
+ ok -> ok
+ catch
+ error:Reason:Stk ->
+ ct:fail("arith_test failed with ~p~n\tA = ~p~n\tB = ~p\n\t~p",
+ [Reason, A, B, Stk])
+ end.
+
+arith_test_1(A, B, MinS, MaxS) ->
verify_kind(A + B, MinS, MaxS),
verify_kind(B + A, MinS, MaxS),
verify_kind(A - B, MinS, MaxS),
@@ -97,6 +106,9 @@ arith_test(A, B, MinS, MaxS) ->
true = B =:= 0 orelse ((A * B) div id(B) =:= A),
true = A =:= 0 orelse ((B * A) div id(A) =:= B),
+ true = B =:= 0 orelse (((A div id(B)) * id(B) + A rem id(B)) =:= A),
+ true = A =:= 0 orelse (((B div id(A)) * id(A) + B rem id(A)) =:= B),
+
ok.
%% Verifies that N is a small when it should be
diff --git a/erts/emulator/test/socket_SUITE.erl b/erts/emulator/test/socket_SUITE.erl
index dd1f3b1086..b074ff4bea 100644
--- a/erts/emulator/test/socket_SUITE.erl
+++ b/erts/emulator/test/socket_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2018-2019. All Rights Reserved.
+%% Copyright Ericsson AB 2018-2020. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -95,6 +95,25 @@
api_b_sendmsg_and_recvmsg_tcpL/1,
api_b_sendmsg_and_recvmsg_sctp4/1,
+ %% *** API socket from FD ***
+ api_ffd_open_wod_and_info_udp4/1,
+ api_ffd_open_wod_and_info_udp6/1,
+ api_ffd_open_wod_and_info_tcp4/1,
+ api_ffd_open_wod_and_info_tcp6/1,
+ api_ffd_open_wd_and_info_udp4/1,
+ api_ffd_open_wd_and_info_udp6/1,
+ api_ffd_open_wd_and_info_tcp4/1,
+ api_ffd_open_wd_and_info_tcp6/1,
+ api_ffd_open_and_open_wod_and_send_udp4/1,
+ api_ffd_open_and_open_wod_and_send_udp6/1,
+ api_ffd_open_and_open_wd_and_send_udp4/1,
+ api_ffd_open_and_open_wd_and_send_udp6/1,
+ api_ffd_open_connect_and_open_wod_and_send_tcp4/1,
+ api_ffd_open_connect_and_open_wod_and_send_tcp6/1,
+ api_ffd_open_connect_and_open_wd_and_send_tcp4/1,
+ api_ffd_open_connect_and_open_wd_and_send_tcp6/1,
+
+
%% *** API async ***
api_a_connect_tcp4/1,
api_a_connect_tcp6/1,
@@ -682,6 +701,7 @@ groups() ->
[{api, [], api_cases()},
{api_misc, [], api_misc_cases()},
{api_basic, [], api_basic_cases()},
+ {api_from_fd, [], api_from_fd_cases()},
{api_async, [], api_async_cases()},
{api_options, [], api_options_cases()},
{api_options_otp, [], api_options_otp_cases()},
@@ -813,6 +833,26 @@ api_basic_cases() ->
api_b_sendmsg_and_recvmsg_sctp4
].
+api_from_fd_cases() ->
+ [
+ api_ffd_open_wod_and_info_udp4,
+ api_ffd_open_wod_and_info_udp6,
+ api_ffd_open_wod_and_info_tcp4,
+ api_ffd_open_wod_and_info_tcp6,
+ api_ffd_open_wd_and_info_udp4,
+ api_ffd_open_wd_and_info_udp6,
+ api_ffd_open_wd_and_info_tcp4,
+ api_ffd_open_wd_and_info_tcp6,
+ api_ffd_open_and_open_wod_and_send_udp4,
+ api_ffd_open_and_open_wod_and_send_udp6,
+ api_ffd_open_and_open_wd_and_send_udp4,
+ api_ffd_open_and_open_wd_and_send_udp6,
+ api_ffd_open_connect_and_open_wod_and_send_tcp4,
+ api_ffd_open_connect_and_open_wod_and_send_tcp6,
+ api_ffd_open_connect_and_open_wd_and_send_tcp4,
+ api_ffd_open_connect_and_open_wd_and_send_tcp6
+ ].
+
api_async_cases() ->
[
api_a_connect_tcp4,
@@ -4237,6 +4277,1890 @@ api_b_send_and_recv_sctp(_InitState) ->
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% %%
+%% API FROM FD %%
+%% %%
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+%% Basically open (create) a socket from an already existing
+%% file descriptor (FD) and info of an IPv4 UDP (dgram) socket.
+%% With some extra checks...
+%% IPv4
+%% Without dup
+api_ffd_open_wod_and_info_udp4(suite) ->
+ [];
+api_ffd_open_wod_and_info_udp4(doc) ->
+ [];
+api_ffd_open_wod_and_info_udp4(_Config) when is_list(_Config) ->
+ ?TT(?SECS(5)),
+ tc_try(api_ffd_open_wod_and_info_udp4,
+ fun() ->
+ InitState = #{domain => inet,
+ type => dgram,
+ protocol => udp,
+ dup => false},
+ ok = api_ffd_open_and_info(InitState)
+ end).
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+%% Basically open (create) a socket from an already existing
+%% file descriptor (FD) and info of an IPv6 UDP (dgram) socket.
+%% With some extra checks...
+%% IPv6
+%% Without dup
+api_ffd_open_wod_and_info_udp6(suite) ->
+ [];
+api_ffd_open_wod_and_info_udp6(doc) ->
+ [];
+api_ffd_open_wod_and_info_udp6(_Config) when is_list(_Config) ->
+ ?TT(?SECS(5)),
+ tc_try(api_ffd_open_wod_and_info_udp6,
+ fun() -> has_support_ipv6() end,
+ fun() ->
+ InitState = #{domain => inet6,
+ type => dgram,
+ protocol => udp,
+ dup => false},
+ ok = api_ffd_open_and_info(InitState)
+ end).
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+%% Basically open (create) a socket from an already existing
+%% file descriptor (FD) and info of an IPv4 UDP (dgram) socket.
+%% With some extra checks...
+%% IPv4
+%% With dup
+api_ffd_open_wd_and_info_udp4(suite) ->
+ [];
+api_ffd_open_wd_and_info_udp4(doc) ->
+ [];
+api_ffd_open_wd_and_info_udp4(_Config) when is_list(_Config) ->
+ ?TT(?SECS(5)),
+ tc_try(api_ffd_wd_open_and_info_udp4,
+ fun() ->
+ InitState = #{domain => inet,
+ type => dgram,
+ protocol => udp,
+ dup => true},
+ ok = api_ffd_open_and_info(InitState)
+ end).
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+%% Basically open (create) a socket from an already existing
+%% file descriptor (FD) and info of an IPv4 UDP (dgram) socket.
+%% With some extra checks...
+%% IPv6
+%% With dup
+api_ffd_open_wd_and_info_udp6(suite) ->
+ [];
+api_ffd_open_wd_and_info_udp6(doc) ->
+ [];
+api_ffd_open_wd_and_info_udp6(_Config) when is_list(_Config) ->
+ ?TT(?SECS(5)),
+ tc_try(api_ffd_wd_open_and_info_udp6,
+ fun() -> has_support_ipv6() end,
+ fun() ->
+ InitState = #{domain => inet,
+ type => dgram,
+ protocol => udp,
+ dup => true},
+ ok = api_ffd_open_and_info(InitState)
+ end).
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+%% Basically open (create) a socket from an already existing
+%% file descriptor (FD) and info of an IPv4 TCP (stream) socket.
+%% With some extra checks...
+%% IPv6
+%% Without dup
+api_ffd_open_wod_and_info_tcp4(suite) ->
+ [];
+api_ffd_open_wod_and_info_tcp4(doc) ->
+ [];
+api_ffd_open_wod_and_info_tcp4(_Config) when is_list(_Config) ->
+ ?TT(?SECS(5)),
+ tc_try(api_ffd_open_wod_and_info_tcp4,
+ fun() ->
+ InitState = #{domain => inet,
+ type => stream,
+ protocol => tcp,
+ dup => false},
+ ok = api_ffd_open_and_info(InitState)
+ end).
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+%% Basically open (create) a socket from an already existing
+%% file descriptor (FD) and info of an IPv6 TCP (stream) socket.
+%% With some extra checks...
+%% IPv6
+%% Without dup
+api_ffd_open_wod_and_info_tcp6(suite) ->
+ [];
+api_ffd_open_wod_and_info_tcp6(doc) ->
+ [];
+api_ffd_open_wod_and_info_tcp6(_Config) when is_list(_Config) ->
+ ?TT(?SECS(5)),
+ tc_try(api_ffd_open_wod_and_info_tcp6,
+ fun() -> has_support_ipv6() end,
+ fun() ->
+ InitState = #{domain => inet6,
+ type => stream,
+ protocol => tcp,
+ dup => false},
+ ok = api_ffd_open_and_info(InitState)
+ end).
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+%% Basically open (create) a socket from an already existing
+%% file descriptor (FD) and info of an IPv4 TCP (stream) socket.
+%% With some extra checks...
+%% IPv6
+%% With dup
+api_ffd_open_wd_and_info_tcp4(suite) ->
+ [];
+api_ffd_open_wd_and_info_tcp4(doc) ->
+ [];
+api_ffd_open_wd_and_info_tcp4(_Config) when is_list(_Config) ->
+ ?TT(?SECS(5)),
+ tc_try(api_ffd_open_wd_and_info_tcp4,
+ fun() ->
+ InitState = #{domain => inet,
+ type => stream,
+ protocol => tcp,
+ dup => true},
+ ok = api_ffd_open_and_info(InitState)
+ end).
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+%% Basically open (create) a socket from an already existing
+%% file descriptor (FD) and info of an IPv6 TCP (stream) socket.
+%% With some extra checks...
+%% IPv6
+%% With dup
+api_ffd_open_wd_and_info_tcp6(suite) ->
+ [];
+api_ffd_open_wd_and_info_tcp6(doc) ->
+ [];
+api_ffd_open_wd_and_info_tcp6(_Config) when is_list(_Config) ->
+ ?TT(?SECS(5)),
+ tc_try(api_ffd_open_wd_and_info_tcp6,
+ fun() -> has_support_ipv6() end,
+ fun() ->
+ InitState = #{domain => inet6,
+ type => stream,
+ protocol => tcp,
+ dup => true},
+ ok = api_ffd_open_and_info(InitState)
+ end).
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+api_ffd_open_and_info(InitState) ->
+ Seq =
+ [
+ #{desc => "open",
+ cmd => fun(#{domain := Domain,
+ type := Type,
+ protocol := Protocol} = State) ->
+ case socket:open(Domain, Type, Protocol) of
+ {ok, Sock1} ->
+ {ok, State#{sock1 => Sock1}};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+ #{desc => "get socket (1) FD",
+ cmd => fun(#{sock1 := Sock1} = State) ->
+ case socket:getopt(Sock1, otp, fd) of
+ {ok, FD} ->
+ ?SEV_IPRINT("FD: ~w", [FD]),
+ {ok, State#{fd => FD}};
+ {error, Reason} = ERROR ->
+ ?SEV_EPRINT("failed get FD: "
+ "~n ~p", [Reason]),
+ ERROR
+ end
+ end},
+ #{desc => "check if we need to provide protocol or not",
+ cmd => fun(#{sock1 := Sock1} = State) ->
+ case socket:getopt(Sock1, socket, protocol) of
+ {ok, _} ->
+ ?SEV_IPRINT("protocol accessible"),
+ {ok, State#{provide_protocol => false}};
+ {error, Reason} ->
+ ?SEV_IPRINT("failed get protocol: "
+ "~n ~p", [Reason]),
+ {ok, State#{provide_protocol => true}}
+ end
+ end},
+ #{desc => "open with FD",
+ cmd => fun(#{fd := FD,
+ dup := DUP,
+ provide_protocol := true,
+ protocol := Protocol} = State) ->
+ case socket:open(FD, #{dup => DUP,
+ protocol => Protocol}) of
+ {ok, Sock2} ->
+ ?SEV_IPRINT("socket 2 open"),
+ {ok, State#{sock2 => Sock2}};
+ {error, Reason} = ERROR ->
+ ?SEV_EPRINT("failed open socket with FD (~w): "
+ "~n ~p", [FD, Reason]),
+ ERROR
+ end;
+ (#{fd := FD,
+ dup := DUP,
+ provide_protocol := false} = State) ->
+ case socket:open(FD, #{dup => DUP}) of
+ {ok, Sock2} ->
+ ?SEV_IPRINT("socket 2 open"),
+ {ok, State#{sock2 => Sock2}};
+ {error, Reason} = ERROR ->
+ ?SEV_EPRINT("failed open socket with FD (~w): "
+ "~n ~p", [FD, Reason]),
+ ERROR
+ end
+ end},
+ #{desc => "get socket (1) info",
+ cmd => fun(#{sock1 := Sock} = State) ->
+ %% socket:setopt(Sock, otp, debug, true),
+ Info = socket:info(Sock),
+ %% socket:setopt(Sock, otp, debug, false),
+ ?SEV_IPRINT("Got Info: "
+ "~n ~p", [Info]),
+ {ok, State#{info1 => Info}}
+ end},
+ #{desc => "get socket (2) info",
+ cmd => fun(#{sock2 := Sock} = State) ->
+ %% socket:setopt(Sock, otp, debug, true),
+ Info = socket:info(Sock),
+ %% socket:setopt(Sock, otp, debug, false),
+ ?SEV_IPRINT("Got Info: "
+ "~n ~p", [Info]),
+ {ok, State#{info2 => Info}}
+ end},
+ #{desc => "validate socket (1) info",
+ cmd => fun(#{domain := Domain,
+ type := Type,
+ protocol := Protocol,
+ info1 := #{domain := Domain,
+ type := Type,
+ protocol := Protocol,
+ ctype := normal,
+ counters := _,
+ num_readers := 0,
+ num_writers := 0,
+ num_acceptors := 0}}) ->
+ ok;
+ (#{domain := Domain,
+ type := Type,
+ protocol := Protocol,
+ info := Info}) ->
+ ?SEV_EPRINT("Unexpected Info for socket 1: "
+ "~n (expected) Domain: ~p"
+ "~n (expected) Type: ~p"
+ "~n (expected) Protocol: ~p"
+ "~n (expected) Create Type: ~p"
+ "~n ~p",
+ [Domain, Type, Protocol, normal, Info]),
+ {error, unexpected_infio}
+ end},
+ #{desc => "validate socket (2) info",
+ cmd => fun(#{domain := Domain,
+ type := Type,
+ protocol := Protocol,
+ fd := _FD,
+ dup := false,
+ info2 := #{domain := Domain,
+ type := Type,
+ protocol := Protocol,
+ ctype := fromfd,
+ counters := _,
+ num_readers := 0,
+ num_writers := 0,
+ num_acceptors := 0}}) ->
+ ok;
+ (#{domain := Domain,
+ type := Type,
+ protocol := Protocol,
+ fd := _FD,
+ dup := false,
+ info := Info}) ->
+ ?SEV_EPRINT("Unexpected Info for socket 2: "
+ "~n (expected) Domain: ~p"
+ "~n (expected) Type: ~p"
+ "~n (expected) Protocol: ~p"
+ "~n (expected) Create Type: ~p"
+ "~n ~p",
+ [Domain, Type, Protocol,
+ fromfd, Info]),
+ {error, unexpected_info};
+ (#{domain := Domain,
+ type := Type,
+ protocol := Protocol,
+ fd := FD,
+ dup := true,
+ info2 := #{domain := Domain,
+ type := Type,
+ protocol := Protocol,
+ ctype := {fromfd, FD},
+ counters := _,
+ num_readers := 0,
+ num_writers := 0,
+ num_acceptors := 0}}) ->
+ ok;
+ (#{domain := Domain,
+ type := Type,
+ protocol := Protocol,
+ fd := FD,
+ dup := true,
+ info := Info}) ->
+ ?SEV_EPRINT("Unexpected Info for socket 2: "
+ "~n (expected) Domain: ~p"
+ "~n (expected) Type: ~p"
+ "~n (expected) Protocol: ~p"
+ "~n (expected) Create Type: ~p"
+ "~n ~p",
+ [Domain, Type, Protocol,
+ {fromfd, FD}, Info]),
+ {error, unexpected_info}
+ end},
+ #{desc => "close socket (1)",
+ cmd => fun(#{sock1 := Sock} = _State) ->
+ socket:close(Sock)
+ end},
+ #{desc => "close socket (2)",
+ cmd => fun(#{sock2 := Sock} = _State) ->
+ socket:close(Sock)
+ end},
+
+ %% *** We are done ***
+ ?SEV_FINISH_NORMAL
+ ],
+ Evaluator = ?SEV_START("tester", Seq, InitState),
+ ok = ?SEV_AWAIT_FINISH([Evaluator]).
+
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+%% Basically open a socket (1) and then create another socket (2) from
+%% its file descriptor *without* dup.
+%% Exchange som data from via both "client" sockets.
+%% Finally close the second socket. Ensure that the original socket
+%% has not been closed (test by sending some data).
+%% IPv4 UDP (dgram) socket.
+%%
+%% <WARNING>
+%%
+%% This is *not* how its intended to be used.
+%% That an erlang process creating a socket and then handing over the
+%% file descriptor to another erlang process. *But* its a convient way
+%% to test it!
+%%
+%% </WARNING>
+%%
+api_ffd_open_and_open_wod_and_send_udp4(suite) ->
+ [];
+api_ffd_open_and_open_wod_and_send_udp4(doc) ->
+ [];
+api_ffd_open_and_open_wod_and_send_udp4(_Config) when is_list(_Config) ->
+ ?TT(?SECS(5)),
+ tc_try(api_ffd_open_and_open_wod_and_send_udp4,
+ fun() ->
+ InitState = #{domain => inet,
+ type => dgram,
+ protocol => udp,
+ dup => false},
+ ok = api_ffd_open_and_open_and_send_udp(InitState)
+ end).
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+%% Basically open a socket (1) and then create another socket (2) from
+%% its file descriptor *without* dup.
+%% Exchange som data from via both "client" sockets.
+%% Finally close the second socket. Ensure that the original socket
+%% has not been closed (test by sending some data).
+%% IPv6 UDP (dgram) socket.
+%%
+%% <WARNING>
+%%
+%% This is *not* how its intended to be used.
+%% That an erlang process creating a socket and then handing over the
+%% file descriptor to another erlang process. *But* its a convient way
+%% to test it!
+%%
+%% </WARNING>
+%%
+api_ffd_open_and_open_wod_and_send_udp6(suite) ->
+ [];
+api_ffd_open_and_open_wod_and_send_udp6(doc) ->
+ [];
+api_ffd_open_and_open_wod_and_send_udp6(_Config) when is_list(_Config) ->
+ ?TT(?SECS(5)),
+ tc_try(api_ffd_open_and_open_wod_and_send_udp6,
+ fun() -> has_support_ipv6() end,
+ fun() ->
+ InitState = #{domain => inet6,
+ type => dgram,
+ protocol => udp,
+ dup => false},
+ ok = api_ffd_open_and_open_and_send_udp(InitState)
+ end).
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+%% Basically open a socket (1) and then create another socket (2) from
+%% its file descriptor *with* dup.
+%% Exchange som data from via both "client" sockets.
+%% Finally close the second socket. Ensure that the original socket
+%% has not been closed (test by sending some data).
+%% IPv4 UDP (dgram) socket.
+%%
+api_ffd_open_and_open_wd_and_send_udp4(suite) ->
+ [];
+api_ffd_open_and_open_wd_and_send_udp4(doc) ->
+ [];
+api_ffd_open_and_open_wd_and_send_udp4(_Config) when is_list(_Config) ->
+ ?TT(?SECS(5)),
+ tc_try(api_ffd_open_and_open_wd_and_send_udp4,
+ fun() ->
+ InitState = #{domain => inet,
+ type => dgram,
+ protocol => udp,
+ dup => true},
+ ok = api_ffd_open_and_open_and_send_udp(InitState)
+ end).
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+%% Basically open a socket (1) and then create another socket (2) from
+%% its file descriptor *with* dup.
+%% Exchange som data from via both "client" sockets.
+%% Finally close the second socket. Ensure that the original socket
+%% has not been closed (test by sending some data).
+%% IPv6 UDP (dgram) socket.
+%%
+api_ffd_open_and_open_wd_and_send_udp6(suite) ->
+ [];
+api_ffd_open_and_open_wd_and_send_udp6(doc) ->
+ [];
+api_ffd_open_and_open_wd_and_send_udp6(_Config) when is_list(_Config) ->
+ ?TT(?SECS(5)),
+ tc_try(api_ffd_open_and_open_wd_and_send_udp6,
+ fun() -> has_support_ipv6() end,
+ fun() ->
+ InitState = #{domain => inet6,
+ type => dgram,
+ protocol => udp,
+ dup => true},
+ ok = api_ffd_open_and_open_and_send_udp(InitState)
+ end).
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+api_ffd_open_and_open_and_send_udp(InitState) ->
+ Send = fun(Sock, Data, Dest) ->
+ socket:sendto(Sock, Data, Dest)
+ end,
+ Recv = fun(Sock) ->
+ socket:recvfrom(Sock)
+ end,
+ api_ffd_open_and_open_and_send_udp2(InitState#{send => Send,
+ recv => Recv}).
+
+api_ffd_open_and_open_and_send_udp2(InitState) ->
+ process_flag(trap_exit, true),
+ ServerSeq =
+ [
+ %% *** Wait for start order ***
+ #{desc => "await start (from tester)",
+ cmd => fun(State) ->
+ Tester = ?SEV_AWAIT_START(),
+ {ok, State#{tester => Tester}}
+ end},
+ #{desc => "monitor tester",
+ cmd => fun(#{tester := Tester}) ->
+ _MRef = erlang:monitor(process, Tester),
+ ok
+ end},
+
+ %% *** Init part ***
+ #{desc => "which local address",
+ cmd => fun(#{domain := Domain} = State) ->
+ LSA = which_local_socket_addr(Domain),
+ {ok, State#{lsa => LSA}}
+ end},
+ #{desc => "create socket",
+ cmd => fun(#{domain := Domain,
+ protocol := Proto} = State) ->
+ case socket:open(Domain, dgram, Proto) of
+ {ok, Sock} ->
+ {ok, State#{sock => Sock}};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+ #{desc => "bind to local address",
+ cmd => fun(#{sock := Sock, lsa := LSA} = State) ->
+ case sock_bind(Sock, LSA) of
+ {ok, Port} ->
+ ?SEV_IPRINT("bound to port: ~w", [Port]),
+ {ok, State#{port => Port}};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+ #{desc => "announce ready (init)",
+ cmd => fun(#{tester := Tester, port := Port}) ->
+ ?SEV_ANNOUNCE_READY(Tester, init, Port),
+ ok
+ end},
+
+ #{desc => "await request 1 (recv)",
+ cmd => fun(#{sock := Sock, recv := Recv} = State) ->
+ case Recv(Sock) of
+ {ok, {Source, ?BASIC_REQ}} ->
+ ?SEV_IPRINT("received request (1) from: "
+ "~n ~p", [Source]),
+ {ok, State#{source => Source}};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+ #{desc => "announce ready 1 (recv request)",
+ cmd => fun(#{tester := Tester}) ->
+ ?SEV_ANNOUNCE_READY(Tester, recv_req),
+ ok
+ end},
+ #{desc => "await continue 1 (with send reply)",
+ cmd => fun(#{tester := Tester}) ->
+ ?SEV_AWAIT_CONTINUE(Tester, tester, send_reply)
+ end},
+ #{desc => "send reply 1",
+ cmd => fun(#{sock := Sock, send := Send, source := Source}) ->
+ Send(Sock, ?BASIC_REP, Source)
+ end},
+ #{desc => "announce ready 1 (send reply)",
+ cmd => fun(#{tester := Tester} = State) ->
+ ?SEV_ANNOUNCE_READY(Tester, send_reply),
+ {ok, maps:remove(source, State)}
+ end},
+
+ #{desc => "await request 2 (recv)",
+ cmd => fun(#{sock := Sock, recv := Recv} = State) ->
+ case Recv(Sock) of
+ {ok, {Source, ?BASIC_REQ}} ->
+ ?SEV_IPRINT("received request (2) from: "
+ "~n ~p", [Source]),
+ {ok, State#{source => Source}};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+ #{desc => "announce ready 2 (recv request)",
+ cmd => fun(#{tester := Tester}) ->
+ ?SEV_ANNOUNCE_READY(Tester, recv_req),
+ ok
+ end},
+ #{desc => "await continue 2 (with send reply)",
+ cmd => fun(#{tester := Tester}) ->
+ ?SEV_AWAIT_CONTINUE(Tester, tester, send_reply)
+ end},
+ #{desc => "send reply 2",
+ cmd => fun(#{sock := Sock, send := Send, source := Source}) ->
+ Send(Sock, ?BASIC_REP, Source)
+ end},
+ #{desc => "announce ready 2 (send reply)",
+ cmd => fun(#{tester := Tester} = State) ->
+ ?SEV_ANNOUNCE_READY(Tester, send_reply),
+ {ok, maps:remove(source, State)}
+ end},
+
+ #{desc => "await request 3 (recv)",
+ cmd => fun(#{sock := Sock, recv := Recv} = State) ->
+ case Recv(Sock) of
+ {ok, {Source, ?BASIC_REQ}} ->
+ ?SEV_IPRINT("received request (2) from: "
+ "~n ~p", [Source]),
+ {ok, State#{source => Source}};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+ #{desc => "announce ready 3 (recv request)",
+ cmd => fun(#{tester := Tester}) ->
+ ?SEV_ANNOUNCE_READY(Tester, recv_req),
+ ok
+ end},
+ #{desc => "await continue 3 (with send reply)",
+ cmd => fun(#{tester := Tester}) ->
+ ?SEV_AWAIT_CONTINUE(Tester, tester, send_reply)
+ end},
+ #{desc => "send reply 3",
+ cmd => fun(#{sock := Sock, send := Send, source := Source}) ->
+ Send(Sock, ?BASIC_REP, Source)
+ end},
+ #{desc => "announce ready 3 (send reply)",
+ cmd => fun(#{tester := Tester} = State) ->
+ ?SEV_ANNOUNCE_READY(Tester, send_reply),
+ {ok, maps:remove(source, State)}
+ end},
+
+ %% *** Termination ***
+ #{desc => "await terminate",
+ cmd => fun(#{tester := Tester} = State) ->
+ case ?SEV_AWAIT_TERMINATE(Tester, tester) of
+ ok ->
+ {ok, maps:remove(tester, State)};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+ #{desc => "close socket",
+ cmd => fun(#{sock := Sock} = State) ->
+ ok = socket:close(Sock),
+ {ok, maps:remove(sock, State)}
+ end},
+
+ %% *** We are done ***
+ ?SEV_FINISH_NORMAL
+ ],
+
+
+ Client1Seq =
+ [
+ %% *** Wait for start order ***
+ #{desc => "await start (from tester)",
+ cmd => fun(State) ->
+ {Tester, Port} = ?SEV_AWAIT_START(),
+ {ok, State#{tester => Tester, server_port => Port}}
+ end},
+ #{desc => "monitor tester",
+ cmd => fun(#{tester := Tester}) ->
+ _MRef = erlang:monitor(process, Tester),
+ ok
+ end},
+
+ %% *** The init part ***
+ #{desc => "which server (local) address",
+ cmd => fun(#{domain := Domain, server_port := Port} = State) ->
+ LSA = which_local_socket_addr(Domain),
+ SSA = LSA#{port => Port},
+ {ok, State#{local_sa => LSA, server_sa => SSA}}
+ end},
+ #{desc => "create socket",
+ cmd => fun(#{domain := Domain,
+ protocol := Proto} = State) ->
+ case socket:open(Domain, dgram, Proto) of
+ {ok, Sock} ->
+ {ok, State#{sock => Sock}};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+ #{desc => "bind to local address",
+ cmd => fun(#{sock := Sock, local_sa := LSA} = _State) ->
+ case sock_bind(Sock, LSA) of
+ {ok, _Port} ->
+ ok;
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+ #{desc => "get socket FD",
+ cmd => fun(#{sock := Sock} = State) ->
+ case socket:getopt(Sock, otp, fd) of
+ {ok, FD} ->
+ ?SEV_IPRINT("FD: ~w", [FD]),
+ {ok, State#{fd => FD}};
+ {error, Reason} = ERROR ->
+ ?SEV_EPRINT("failed get FD: "
+ "~n ~p", [Reason]),
+ ERROR
+ end
+ end},
+ #{desc => "announce ready (init)",
+ cmd => fun(#{tester := Tester,
+ fd := FD}) ->
+ ?SEV_ANNOUNCE_READY(Tester, init, FD),
+ ok
+ end},
+
+ %% *** The actual test ***
+ #{desc => "await continue (send request 1)",
+ cmd => fun(#{tester := Tester} = _State) ->
+ ?SEV_AWAIT_CONTINUE(Tester, tester, send_req)
+ end},
+ #{desc => "send request 1 (to server)",
+ cmd => fun(#{sock := Sock, send := Send, server_sa := SSA}) ->
+ Send(Sock, ?BASIC_REQ, SSA)
+ end},
+ #{desc => "announce ready (send request 1)",
+ cmd => fun(#{tester := Tester}) ->
+ ?SEV_ANNOUNCE_READY(Tester, send_req),
+ ok
+ end},
+ #{desc => "await recv reply 1 (from server)",
+ cmd => fun(#{sock := Sock, recv := Recv}) ->
+ {ok, {_, ?BASIC_REP}} = Recv(Sock),
+ ok
+ end},
+ #{desc => "announce ready (recv reply 1)",
+ cmd => fun(#{tester := Tester}) ->
+ ?SEV_ANNOUNCE_READY(Tester, recv_reply),
+ ok
+ end},
+
+ #{desc => "await continue (send request 3)",
+ cmd => fun(#{tester := Tester} = _State) ->
+ ?SEV_AWAIT_CONTINUE(Tester, tester, send_req)
+ end},
+ #{desc => "send request 3 (to server)",
+ cmd => fun(#{sock := Sock, send := Send, server_sa := SSA}) ->
+ Send(Sock, ?BASIC_REQ, SSA)
+ end},
+ #{desc => "announce ready (send request 3)",
+ cmd => fun(#{tester := Tester}) ->
+ ?SEV_ANNOUNCE_READY(Tester, send_req),
+ ok
+ end},
+ #{desc => "await recv reply 3 (from server)",
+ cmd => fun(#{sock := Sock, recv := Recv}) ->
+ {ok, {_, ?BASIC_REP}} = Recv(Sock),
+ ok
+ end},
+ #{desc => "announce ready (recv reply 3)",
+ cmd => fun(#{tester := Tester}) ->
+ ?SEV_ANNOUNCE_READY(Tester, recv_reply),
+ ok
+ end},
+
+ %% *** Termination ***
+ #{desc => "await terminate",
+ cmd => fun(#{tester := Tester} = State) ->
+ case ?SEV_AWAIT_TERMINATE(Tester, tester) of
+ ok ->
+ {ok, maps:remove(tester, State)};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+ #{desc => "close socket",
+ cmd => fun(#{sock := Sock} = State) ->
+ ok = socket:close(Sock),
+ {ok, maps:remove(sock, State)}
+ end},
+
+ %% *** We are done ***
+ ?SEV_FINISH_NORMAL
+ ],
+
+
+ Client2Seq =
+ [
+ %% *** Wait for start order ***
+ #{desc => "await start (from tester)",
+ cmd => fun(State) ->
+ {Tester, {Port, FD}} = ?SEV_AWAIT_START(),
+ {ok, State#{tester => Tester,
+ server_port => Port,
+ fd => FD}}
+ end},
+ #{desc => "monitor tester",
+ cmd => fun(#{tester := Tester}) ->
+ _MRef = erlang:monitor(process, Tester),
+ ok
+ end},
+
+ %% *** The init part ***
+ #{desc => "which server (local) address",
+ cmd => fun(#{domain := Domain, server_port := Port} = State) ->
+ LSA = which_local_socket_addr(Domain),
+ SSA = LSA#{port => Port},
+ {ok, State#{server_sa => SSA}}
+ end},
+ #{desc => "create socket",
+ cmd => fun(#{fd := FD,
+ dup := DUP} = State) ->
+ case socket:open(FD, #{dup => DUP}) of
+ {ok, Sock} ->
+ {ok, State#{sock => Sock}};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+ #{desc => "announce ready (init)",
+ cmd => fun(#{tester := Tester}) ->
+ ?SEV_ANNOUNCE_READY(Tester, init),
+ ok
+ end},
+
+ %% *** The actual test ***
+ #{desc => "await continue (send request 2)",
+ cmd => fun(#{tester := Tester} = _State) ->
+ ?SEV_AWAIT_CONTINUE(Tester, tester, send_req)
+ end},
+ #{desc => "send request 2 (to server)",
+ cmd => fun(#{sock := Sock, send := Send, server_sa := SSA}) ->
+ Send(Sock, ?BASIC_REQ, SSA)
+ end},
+ #{desc => "announce ready (send request 2)",
+ cmd => fun(#{tester := Tester}) ->
+ ?SEV_ANNOUNCE_READY(Tester, send_req),
+ ok
+ end},
+ #{desc => "await recv reply 2 (from server)",
+ cmd => fun(#{sock := Sock, recv := Recv}) ->
+ {ok, {_, ?BASIC_REP}} = Recv(Sock),
+ ok
+ end},
+ #{desc => "announce ready (recv reply 2)",
+ cmd => fun(#{tester := Tester}) ->
+ ?SEV_ANNOUNCE_READY(Tester, recv_reply),
+ ok
+ end},
+
+ %% *** Termination ***
+ #{desc => "await terminate",
+ cmd => fun(#{tester := Tester} = State) ->
+ case ?SEV_AWAIT_TERMINATE(Tester, tester) of
+ ok ->
+ {ok, maps:remove(tester, State)};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+ #{desc => "close socket",
+ cmd => fun(#{sock := Sock} = State) ->
+ ok = socket:close(Sock),
+ {ok, maps:remove(sock, State)}
+ end},
+
+ %% *** We are done ***
+ ?SEV_FINISH_NORMAL
+ ],
+
+
+ TesterSeq =
+ [
+ %% *** Init part ***
+ #{desc => "monitor server",
+ cmd => fun(#{server := Pid} = _State) ->
+ _MRef = erlang:monitor(process, Pid),
+ ok
+ end},
+ #{desc => "monitor client 1",
+ cmd => fun(#{client1 := Pid} = _State) ->
+ _MRef = erlang:monitor(process, Pid),
+ ok
+ end},
+ #{desc => "monitor client 2",
+ cmd => fun(#{client2 := Pid} = _State) ->
+ _MRef = erlang:monitor(process, Pid),
+ ok
+ end},
+
+ %% Start the server
+ #{desc => "order server start",
+ cmd => fun(#{server := Pid} = _State) ->
+ ?SEV_ANNOUNCE_START(Pid),
+ ok
+ end},
+ #{desc => "await server ready (init)",
+ cmd => fun(#{server := Pid} = State) ->
+ {ok, Port} = ?SEV_AWAIT_READY(Pid, server, init),
+ {ok, State#{server_port => Port}}
+ end},
+
+ %% Start the client 1
+ #{desc => "order client 1 start",
+ cmd => fun(#{client1 := Pid, server_port := Port} = _State) ->
+ ?SEV_ANNOUNCE_START(Pid, Port),
+ ok
+ end},
+ #{desc => "await client 1 ready (init)",
+ cmd => fun(#{client1 := Pid} = State) ->
+ case ?SEV_AWAIT_READY(Pid, client1, init) of
+ {ok, FD} ->
+ {ok, State#{fd => FD}};
+ {error, Reason} = ERROR ->
+ ?SEV_EPRINT("Client 1 init error: "
+ "~n ~p", [Reason]),
+ ERROR
+ end
+ end},
+
+ ?SEV_SLEEP(?SECS(1)),
+
+ %% Start the client 2
+ #{desc => "order client 2 start",
+ cmd => fun(#{client2 := Pid,
+ server_port := Port,
+ fd := FD} = _State) ->
+ ?SEV_ANNOUNCE_START(Pid, {Port, FD}),
+ ok
+ end},
+ #{desc => "await client 2 ready (init)",
+ cmd => fun(#{client2 := Pid} = _State) ->
+ ok = ?SEV_AWAIT_READY(Pid, client2, init)
+ end},
+
+ %% *** The actual test ***
+
+ #{desc => "order client 1 to continue (with send request 1)",
+ cmd => fun(#{client1 := Client} = _State) ->
+ ?SEV_ANNOUNCE_CONTINUE(Client, send_req),
+ ok
+ end},
+ #{desc => "await client 1 ready (with send request 1)",
+ cmd => fun(#{client1 := Client} = _State) ->
+ ?SEV_AWAIT_READY(Client, client1, send_req)
+ end},
+ #{desc => "await server ready (request recv 1)",
+ cmd => fun(#{server := Server} = _State) ->
+ ?SEV_AWAIT_READY(Server, server, recv_req)
+ end},
+ #{desc => "order server to continue (with send reply 1)",
+ cmd => fun(#{server := Server} = _State) ->
+ ?SEV_ANNOUNCE_CONTINUE(Server, send_reply),
+ ok
+ end},
+ #{desc => "await server ready (with reply 1 sent)",
+ cmd => fun(#{server := Server} = _State) ->
+ ?SEV_AWAIT_READY(Server, server, send_reply)
+ end},
+ #{desc => "await client 1 ready (reply recv 1)",
+ cmd => fun(#{client1 := Client} = _State) ->
+ ?SEV_AWAIT_READY(Client, client1, recv_reply)
+ end},
+
+
+ #{desc => "order client 2 to continue (with send request 2)",
+ cmd => fun(#{client2 := Client} = _State) ->
+ ?SEV_ANNOUNCE_CONTINUE(Client, send_req),
+ ok
+ end},
+ #{desc => "await client 2 ready (with send request 2)",
+ cmd => fun(#{client2 := Client} = _State) ->
+ ?SEV_AWAIT_READY(Client, client2, send_req)
+ end},
+ #{desc => "await server ready (request recv 2)",
+ cmd => fun(#{server := Server} = _State) ->
+ ?SEV_AWAIT_READY(Server, server, recv_req)
+ end},
+ #{desc => "order server to continue (with send reply 2)",
+ cmd => fun(#{server := Server} = _State) ->
+ ?SEV_ANNOUNCE_CONTINUE(Server, send_reply),
+ ok
+ end},
+ #{desc => "await server ready (with reply 2 sent)",
+ cmd => fun(#{server := Server} = _State) ->
+ ?SEV_AWAIT_READY(Server, server, send_reply)
+ end},
+ #{desc => "await client 2 ready (reply recv 2)",
+ cmd => fun(#{client2 := Client} = _State) ->
+ ?SEV_AWAIT_READY(Client, client2, recv_reply)
+ end},
+
+
+ #{desc => "order client 2 to terminate",
+ cmd => fun(#{client2 := Client} = _State) ->
+ ?SEV_ANNOUNCE_TERMINATE(Client),
+ ok
+ end},
+ #{desc => "await client 2 termination",
+ cmd => fun(#{client2 := Client} = State) ->
+ ?SEV_AWAIT_TERMINATION(Client),
+ State1 = maps:remove(client2, State),
+ {ok, State1}
+ end},
+
+
+ #{desc => "order client 1 to continue (with send request 3)",
+ cmd => fun(#{client1 := Client} = _State) ->
+ ?SEV_ANNOUNCE_CONTINUE(Client, send_req),
+ ok
+ end},
+ #{desc => "await client 1 ready (with send request 3)",
+ cmd => fun(#{client1 := Client} = _State) ->
+ ?SEV_AWAIT_READY(Client, client1, send_req)
+ end},
+ #{desc => "await server ready (request recv 3)",
+ cmd => fun(#{server := Server} = _State) ->
+ ?SEV_AWAIT_READY(Server, server, recv_req)
+ end},
+ #{desc => "order server to continue (with send reply 3)",
+ cmd => fun(#{server := Server} = _State) ->
+ ?SEV_ANNOUNCE_CONTINUE(Server, send_reply),
+ ok
+ end},
+ #{desc => "await server ready (with reply 3 sent)",
+ cmd => fun(#{server := Server} = _State) ->
+ ?SEV_AWAIT_READY(Server, server, send_reply)
+ end},
+ #{desc => "await client 1 ready (reply recv 3)",
+ cmd => fun(#{client1 := Client} = _State) ->
+ ?SEV_AWAIT_READY(Client, client1, recv_reply)
+ end},
+
+
+ %% *** Termination ***
+ #{desc => "order client 1 to terminate",
+ cmd => fun(#{client1 := Client} = _State) ->
+ ?SEV_ANNOUNCE_TERMINATE(Client),
+ ok
+ end},
+ #{desc => "await client 1 termination",
+ cmd => fun(#{client1 := Client} = State) ->
+ ?SEV_AWAIT_TERMINATION(Client),
+ State1 = maps:remove(client1, State),
+ {ok, State1}
+ end},
+ #{desc => "order server to terminate",
+ cmd => fun(#{server := Server} = _State) ->
+ ?SEV_ANNOUNCE_TERMINATE(Server),
+ ok
+ end},
+ #{desc => "await server termination",
+ cmd => fun(#{server := Server} = State) ->
+ ?SEV_AWAIT_TERMINATION(Server),
+ State1 = maps:remove(server, State),
+ {ok, State1}
+ end},
+
+ %% *** We are done ***
+ ?SEV_FINISH_NORMAL
+ ],
+
+ i("start server evaluator"),
+ Server = ?SEV_START("server", ServerSeq, maps:remove(dup, InitState)),
+
+ i("start (socket origin) client 1 evaluator"),
+ Client1 = ?SEV_START("client-1", Client1Seq, maps:remove(dup, InitState)),
+ i("await evaluator(s)"),
+
+ i("start client 2 evaluator"),
+ Client2 = ?SEV_START("client-2", Client2Seq, InitState),
+ i("await evaluator(s)"),
+
+ i("start tester evaluator"),
+ TesterInitState = #{server => Server#ev.pid,
+ client1 => Client1#ev.pid,
+ client2 => Client2#ev.pid},
+ Tester = ?SEV_START("tester", TesterSeq, TesterInitState),
+
+ ok = ?SEV_AWAIT_FINISH([Server, Client1, Client2, Tester]).
+
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+%% Basically open a socket (1), connect to a server and then create
+%% another socket (2) from its file descriptor *without* dup.
+%% Exchange som data from via both "client" sockets.
+%% Finally close the second socket. Ensure that the original socket
+%% has not been closed (test by sending some data).
+%% IPv4 TCP (stream) socket.
+%%
+%% <WARNING>
+%%
+%% This is *not* how its intended to be used.
+%% That an erlang process creating a socket and then handing over the
+%% file descriptor to another erlang process. *But* its a convient way
+%% to test it!
+%%
+%% </WARNING>
+%%
+api_ffd_open_connect_and_open_wod_and_send_tcp4(suite) ->
+ [];
+api_ffd_open_connect_and_open_wod_and_send_tcp4(doc) ->
+ [];
+api_ffd_open_connect_and_open_wod_and_send_tcp4(_Config) when is_list(_Config) ->
+ ?TT(?SECS(5)),
+ tc_try(api_ffd_open_connect_and_open_wod_and_send_tcp4,
+ fun() ->
+ InitState = #{domain => inet,
+ type => stream,
+ protocol => tcp,
+ dup => false},
+ ok = api_ffd_open_connect_and_open_and_send_tcp(InitState)
+ end).
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+%% Basically open a socket (1), connect to a server and then create
+%% another socket (2) from its file descriptor *without* dup.
+%% Exchange som data from via both "client" sockets.
+%% Finally close the second socket. Ensure that the original socket
+%% has not been closed (test by sending some data).
+%% IPv6 TCP (stream) socket.
+%%
+%% <WARNING>
+%%
+%% This is *not* how its intended to be used.
+%% That an erlang process creating a socket and then handing over the
+%% file descriptor to another erlang process. *But* its a convient way
+%% to test it!
+%%
+%% </WARNING>
+%%
+api_ffd_open_connect_and_open_wod_and_send_tcp6(suite) ->
+ [];
+api_ffd_open_connect_and_open_wod_and_send_tcp6(doc) ->
+ [];
+api_ffd_open_connect_and_open_wod_and_send_tcp6(_Config) when is_list(_Config) ->
+ ?TT(?SECS(5)),
+ tc_try(api_ffd_open_connect_and_open_wod_and_send_tcp6,
+ fun() -> has_support_ipv6() end,
+ fun() ->
+ InitState = #{domain => inet6,
+ type => stream,
+ protocol => tcp,
+ dup => false},
+ ok = api_ffd_open_connect_and_open_and_send_tcp(InitState)
+ end).
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+%% Basically open a socket (1), connect to a server and then create
+%% another socket (2) from its file descriptor *with* dup.
+%% Exchange som data from via both "client" sockets.
+%% Finally close the second socket. Ensure that the original socket
+%% has not been closed (test by sending some data).
+%% IPv4 TCP (stream) socket.
+api_ffd_open_connect_and_open_wd_and_send_tcp4(suite) ->
+ [];
+api_ffd_open_connect_and_open_wd_and_send_tcp4(doc) ->
+ [];
+api_ffd_open_connect_and_open_wd_and_send_tcp4(_Config) when is_list(_Config) ->
+ ?TT(?SECS(5)),
+ tc_try(api_ffd_open_connect_and_open_wd_and_send_tcp4,
+ fun() ->
+ InitState = #{domain => inet,
+ type => stream,
+ protocol => tcp,
+ dup => true},
+ ok = api_ffd_open_connect_and_open_and_send_tcp(InitState)
+ end).
+
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+%% Basically open a socket (1), connect to a server and then create
+%% another socket (2) from its file descriptor *with* dup.
+%% Exchange som data from via both "client" sockets.
+%% Finally close the second socket. Ensure that the original socket
+%% has not been closed (test by sending some data).
+%% IPv6 TCP (stream) socket.
+api_ffd_open_connect_and_open_wd_and_send_tcp6(suite) ->
+ [];
+api_ffd_open_connect_and_open_wd_and_send_tcp6(doc) ->
+ [];
+api_ffd_open_connect_and_open_wd_and_send_tcp6(_Config) when is_list(_Config) ->
+ ?TT(?SECS(5)),
+ tc_try(api_ffd_open_connect_and_open_wd_and_send_tcp6,
+ fun() -> has_support_ipv6() end,
+ fun() ->
+ InitState = #{domain => inet6,
+ type => stream,
+ protocol => tcp,
+ dup => true},
+ ok = api_ffd_open_connect_and_open_and_send_tcp(InitState)
+ end).
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+api_ffd_open_connect_and_open_and_send_tcp(InitState) ->
+ Send = fun(Sock, Data) ->
+ socket:send(Sock, Data)
+ end,
+ Recv = fun(Sock) ->
+ socket:recv(Sock)
+ end,
+ api_ffd_open_connect_and_open_and_send_tcp2(InitState#{send => Send,
+ recv => Recv}).
+
+api_ffd_open_connect_and_open_and_send_tcp2(InitState) ->
+ process_flag(trap_exit, true),
+ ServerSeq =
+ [
+ %% *** Wait for start order ***
+ #{desc => "await start (from tester)",
+ cmd => fun(State) ->
+ Tester = ?SEV_AWAIT_START(),
+ {ok, State#{tester => Tester}}
+ end},
+ #{desc => "monitor tester",
+ cmd => fun(#{tester := Tester}) ->
+ _MRef = erlang:monitor(process, Tester),
+ ok
+ end},
+
+ %% *** Init part ***
+ #{desc => "which local address",
+ cmd => fun(#{domain := Domain} = State) ->
+ LSA = which_local_socket_addr(Domain),
+ {ok, State#{lsa => LSA}}
+ end},
+ #{desc => "create listen socket",
+ cmd => fun(#{domain := Domain,
+ protocol := Proto} = State) ->
+ case socket:open(Domain, stream, Proto) of
+ {ok, Sock} ->
+ {ok, State#{lsock => Sock}};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+ #{desc => "bind to local address",
+ cmd => fun(#{lsock := LSock, lsa := LSA} = State) ->
+ case sock_bind(LSock, LSA) of
+ {ok, Port} ->
+ ?SEV_IPRINT("bound to port: ~w", [Port]),
+ {ok, State#{lport => Port}};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+ #{desc => "make listen socket",
+ cmd => fun(#{lsock := LSock}) ->
+ socket:listen(LSock)
+ end},
+ #{desc => "announce ready (init)",
+ cmd => fun(#{tester := Tester, lport := Port}) ->
+ %% This is actually not used for unix domain socket
+ ?SEV_ANNOUNCE_READY(Tester, init, Port),
+ ok
+ end},
+
+ %% The actual test
+ #{desc => "await continue (accept)",
+ cmd => fun(#{tester := Tester}) ->
+ ?SEV_AWAIT_CONTINUE(Tester, tester, accept)
+ end},
+ #{desc => "await connection",
+ cmd => fun(#{lsock := LSock} = State) ->
+ case socket:accept(LSock) of
+ {ok, Sock} ->
+ ?SEV_IPRINT("accepted: ~n ~p", [Sock]),
+ {ok, State#{csock => Sock}};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+ #{desc => "announce ready (accept)",
+ cmd => fun(#{tester := Tester}) ->
+ ?SEV_ANNOUNCE_READY(Tester, accept),
+ ok
+ end},
+
+ #{desc => "await request 1 (recv)",
+ cmd => fun(#{csock := Sock, recv := Recv}) ->
+ case Recv(Sock) of
+ {ok, ?BASIC_REQ} ->
+ ok;
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+ #{desc => "announce ready 1 (recv request)",
+ cmd => fun(#{tester := Tester}) ->
+ ?SEV_ANNOUNCE_READY(Tester, recv_req),
+ ok
+ end},
+ #{desc => "await continue 1 (with send reply)",
+ cmd => fun(#{tester := Tester}) ->
+ ?SEV_AWAIT_CONTINUE(Tester, tester, send_reply)
+ end},
+ #{desc => "send reply 1",
+ cmd => fun(#{csock := Sock, send := Send}) ->
+ Send(Sock, ?BASIC_REP)
+ end},
+ #{desc => "announce ready 1 (send reply)",
+ cmd => fun(#{tester := Tester}) ->
+ ?SEV_ANNOUNCE_READY(Tester, send_reply),
+ ok
+ end},
+
+ #{desc => "await request 2 (recv)",
+ cmd => fun(#{csock := Sock, recv := Recv}) ->
+ case Recv(Sock) of
+ {ok, ?BASIC_REQ} ->
+ ok;
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+ #{desc => "announce ready 2 (recv request)",
+ cmd => fun(#{tester := Tester}) ->
+ ?SEV_ANNOUNCE_READY(Tester, recv_req),
+ ok
+ end},
+ #{desc => "await continue 2 (with send reply)",
+ cmd => fun(#{tester := Tester}) ->
+ ?SEV_AWAIT_CONTINUE(Tester, tester, send_reply)
+ end},
+ #{desc => "send reply 2",
+ cmd => fun(#{csock := Sock, send := Send}) ->
+ Send(Sock, ?BASIC_REP)
+ end},
+ #{desc => "announce ready 2 (send reply)",
+ cmd => fun(#{tester := Tester}) ->
+ ?SEV_ANNOUNCE_READY(Tester, send_reply),
+ ok
+ end},
+
+ #{desc => "await request 3 (recv)",
+ cmd => fun(#{csock := Sock, recv := Recv}) ->
+ case Recv(Sock) of
+ {ok, ?BASIC_REQ} ->
+ ok;
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+ #{desc => "announce ready 3 (recv request)",
+ cmd => fun(#{tester := Tester}) ->
+ ?SEV_ANNOUNCE_READY(Tester, recv_req),
+ ok
+ end},
+ #{desc => "await continue 3 (with send reply)",
+ cmd => fun(#{tester := Tester}) ->
+ ?SEV_AWAIT_CONTINUE(Tester, tester, send_reply)
+ end},
+ #{desc => "send reply 3",
+ cmd => fun(#{csock := Sock, send := Send}) ->
+ Send(Sock, ?BASIC_REP)
+ end},
+ #{desc => "announce ready 3 (send reply)",
+ cmd => fun(#{tester := Tester}) ->
+ ?SEV_ANNOUNCE_READY(Tester, send_reply),
+ ok
+ end},
+
+ %% *** Termination ***
+ #{desc => "await terminate",
+ cmd => fun(#{tester := Tester} = State) ->
+ case ?SEV_AWAIT_TERMINATE(Tester, tester) of
+ ok ->
+ {ok, maps:remove(tester, State)};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+ #{desc => "close connection socket",
+ cmd => fun(#{csock := Sock} = State) ->
+ ok = socket:close(Sock),
+ {ok, maps:remove(csock, State)}
+ end},
+ #{desc => "close listen socket",
+ cmd => fun(#{lsock := LSock} = State) ->
+ case socket:close(LSock) of
+ ok ->
+ {ok, maps:remove(lsock, State)};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+
+ %% *** We are done ***
+ ?SEV_FINISH_NORMAL
+ ],
+
+
+ Client1Seq =
+ [
+ %% *** Wait for start order ***
+ #{desc => "await start (from tester)",
+ cmd => fun(State) ->
+ {Tester, Port} = ?SEV_AWAIT_START(),
+ {ok, State#{tester => Tester, server_port => Port}}
+ end},
+ #{desc => "monitor tester",
+ cmd => fun(#{tester := Tester}) ->
+ _MRef = erlang:monitor(process, Tester),
+ ok
+ end},
+
+ %% *** The init part ***
+ #{desc => "which server (local) address",
+ cmd => fun(#{domain := Domain, server_port := Port} = State) ->
+ LSA = which_local_socket_addr(Domain),
+ SSA = LSA#{port => Port},
+ {ok, State#{local_sa => LSA, server_sa => SSA}}
+ end},
+ #{desc => "create socket",
+ cmd => fun(#{domain := Domain,
+ protocol := Proto} = State) ->
+ case socket:open(Domain, stream, Proto) of
+ {ok, Sock} ->
+ {ok, State#{sock => Sock}};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+ #{desc => "bind to local address",
+ cmd => fun(#{sock := Sock, local_sa := LSA} = _State) ->
+ case sock_bind(Sock, LSA) of
+ {ok, _Port} ->
+ ok;
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+ #{desc => "announce ready (init)",
+ cmd => fun(#{tester := Tester}) ->
+ ?SEV_ANNOUNCE_READY(Tester, init),
+ ok
+ end},
+
+
+ #{desc => "await continue (connect)",
+ cmd => fun(#{tester := Tester} = _State) ->
+ ?SEV_AWAIT_CONTINUE(Tester, tester, connect)
+ end},
+ #{desc => "connect to server",
+ cmd => fun(#{sock := Sock, server_sa := SSA}) ->
+ socket:connect(Sock, SSA)
+ end},
+ #{desc => "get socket FD",
+ cmd => fun(#{sock := Sock} = State) ->
+ case socket:getopt(Sock, otp, fd) of
+ {ok, FD} ->
+ ?SEV_IPRINT("FD: ~w", [FD]),
+ {ok, State#{fd => FD}};
+ {error, Reason} = ERROR ->
+ ?SEV_EPRINT("failed get FD: "
+ "~n ~p", [Reason]),
+ ERROR
+ end
+ end},
+ #{desc => "announce ready (connect)",
+ cmd => fun(#{tester := Tester,
+ fd := FD}) ->
+ ?SEV_ANNOUNCE_READY(Tester, connect, FD),
+ ok
+ end},
+
+
+ %% *** The actual test ***
+ #{desc => "await continue (send request 1)",
+ cmd => fun(#{tester := Tester} = _State) ->
+ ?SEV_AWAIT_CONTINUE(Tester, tester, send_req)
+ end},
+ #{desc => "send request 1 (to server)",
+ cmd => fun(#{sock := Sock, send := Send}) ->
+ Send(Sock, ?BASIC_REQ)
+ end},
+ #{desc => "announce ready (send request 1)",
+ cmd => fun(#{tester := Tester}) ->
+ ?SEV_ANNOUNCE_READY(Tester, send_req),
+ ok
+ end},
+ #{desc => "await recv reply 1 (from server)",
+ cmd => fun(#{sock := Sock, recv := Recv}) ->
+ {ok, ?BASIC_REP} = Recv(Sock),
+ ok
+ end},
+ #{desc => "announce ready (recv reply 1)",
+ cmd => fun(#{tester := Tester}) ->
+ ?SEV_ANNOUNCE_READY(Tester, recv_reply),
+ ok
+ end},
+
+ #{desc => "await continue (send request 3)",
+ cmd => fun(#{tester := Tester} = _State) ->
+ ?SEV_AWAIT_CONTINUE(Tester, tester, send_req)
+ end},
+ #{desc => "send request 3 (to server)",
+ cmd => fun(#{sock := Sock, send := Send}) ->
+ Send(Sock, ?BASIC_REQ)
+ end},
+ #{desc => "announce ready (send request 3)",
+ cmd => fun(#{tester := Tester}) ->
+ ?SEV_ANNOUNCE_READY(Tester, send_req),
+ ok
+ end},
+ #{desc => "await recv reply 3 (from server)",
+ cmd => fun(#{sock := Sock, recv := Recv}) ->
+ {ok, ?BASIC_REP} = Recv(Sock),
+ ok
+ end},
+ #{desc => "announce ready (recv reply 3)",
+ cmd => fun(#{tester := Tester}) ->
+ ?SEV_ANNOUNCE_READY(Tester, recv_reply),
+ ok
+ end},
+
+ %% *** Termination ***
+ #{desc => "await terminate",
+ cmd => fun(#{tester := Tester} = State) ->
+ case ?SEV_AWAIT_TERMINATE(Tester, tester) of
+ ok ->
+ {ok, maps:remove(tester, State)};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+ #{desc => "close socket",
+ cmd => fun(#{sock := Sock} = State) ->
+ ok = socket:close(Sock),
+ {ok, maps:remove(sock, State)}
+ end},
+
+ %% *** We are done ***
+ ?SEV_FINISH_NORMAL
+ ],
+
+
+ Client2Seq =
+ [
+ %% *** Wait for start order ***
+ #{desc => "await start (from tester)",
+ cmd => fun(State) ->
+ {Tester, FD} = ?SEV_AWAIT_START(),
+ {ok, State#{tester => Tester, fd => FD}}
+ end},
+ #{desc => "monitor tester",
+ cmd => fun(#{tester := Tester}) ->
+ _MRef = erlang:monitor(process, Tester),
+ ok
+ end},
+
+ %% *** The init part ***
+ #{desc => "create socket",
+ cmd => fun(#{fd := FD,
+ dup := DUP} = State) ->
+ case socket:open(FD, #{dup => DUP}) of
+ {ok, Sock} ->
+ {ok, State#{sock => Sock}};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+ #{desc => "announce ready (init)",
+ cmd => fun(#{tester := Tester}) ->
+ ?SEV_ANNOUNCE_READY(Tester, init),
+ ok
+ end},
+
+ %% *** The actual test ***
+ #{desc => "await continue (send request 2)",
+ cmd => fun(#{tester := Tester} = _State) ->
+ ?SEV_AWAIT_CONTINUE(Tester, tester, send_req)
+ end},
+ #{desc => "send request 2 (to server)",
+ cmd => fun(#{sock := Sock, send := Send}) ->
+ Send(Sock, ?BASIC_REQ)
+ end},
+ #{desc => "announce ready (send request 2)",
+ cmd => fun(#{tester := Tester}) ->
+ ?SEV_ANNOUNCE_READY(Tester, send_req),
+ ok
+ end},
+ #{desc => "await recv reply 2 (from server)",
+ cmd => fun(#{sock := Sock, recv := Recv}) ->
+ {ok, ?BASIC_REP} = Recv(Sock),
+ ok
+ end},
+ #{desc => "announce ready (recv reply 2)",
+ cmd => fun(#{tester := Tester}) ->
+ ?SEV_ANNOUNCE_READY(Tester, recv_reply),
+ ok
+ end},
+
+ %% *** Termination ***
+ #{desc => "await terminate",
+ cmd => fun(#{tester := Tester} = State) ->
+ case ?SEV_AWAIT_TERMINATE(Tester, tester) of
+ ok ->
+ {ok, maps:remove(tester, State)};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+ #{desc => "close socket",
+ cmd => fun(#{sock := Sock} = State) ->
+ ok = socket:close(Sock),
+ {ok, maps:remove(sock, State)}
+ end},
+
+ %% *** We are done ***
+ ?SEV_FINISH_NORMAL
+ ],
+
+
+ TesterSeq =
+ [
+ %% *** Init part ***
+ #{desc => "monitor server",
+ cmd => fun(#{server := Pid} = _State) ->
+ _MRef = erlang:monitor(process, Pid),
+ ok
+ end},
+ #{desc => "monitor client 1",
+ cmd => fun(#{client1 := Pid} = _State) ->
+ _MRef = erlang:monitor(process, Pid),
+ ok
+ end},
+ #{desc => "monitor client 2",
+ cmd => fun(#{client2 := Pid} = _State) ->
+ _MRef = erlang:monitor(process, Pid),
+ ok
+ end},
+
+ %% Start the server
+ #{desc => "order server start",
+ cmd => fun(#{server := Pid} = _State) ->
+ ?SEV_ANNOUNCE_START(Pid),
+ ok
+ end},
+ #{desc => "await server ready (init)",
+ cmd => fun(#{server := Pid} = State) ->
+ {ok, Port} = ?SEV_AWAIT_READY(Pid, server, init),
+ {ok, State#{server_port => Port}}
+ end},
+
+ %% Start the client 1
+ #{desc => "order client 1 start",
+ cmd => fun(#{client1 := Pid, server_port := Port} = _State) ->
+ ?SEV_ANNOUNCE_START(Pid, Port),
+ ok
+ end},
+ #{desc => "await client 1 ready (init)",
+ cmd => fun(#{client1 := Pid} = _State) ->
+ ok = ?SEV_AWAIT_READY(Pid, client1, init)
+ end},
+
+ #{desc => "order server to continue (with accept)",
+ cmd => fun(#{server := Server} = _State) ->
+ ?SEV_ANNOUNCE_CONTINUE(Server, accept),
+ ok
+ end},
+ ?SEV_SLEEP(?SECS(1)),
+ #{desc => "order client 1 to continue (with connect)",
+ cmd => fun(#{client1 := Client} = _State) ->
+ ?SEV_ANNOUNCE_CONTINUE(Client, connect),
+ ok
+ end},
+ #{desc => "await client 1 ready (connect)",
+ cmd => fun(#{client1 := Pid} = State) ->
+ {ok, FD} = ?SEV_AWAIT_READY(Pid, client1, connect),
+ {ok, State#{fd => FD}}
+ end},
+ #{desc => "await server ready (accept)",
+ cmd => fun(#{server := Server} = _State) ->
+ ?SEV_AWAIT_READY(Server, server, accept)
+ end},
+
+ %% Start the client 2
+ #{desc => "order client 2 start",
+ cmd => fun(#{client2 := Pid, fd := FD} = _State) ->
+ ?SEV_ANNOUNCE_START(Pid, FD),
+ ok
+ end},
+ #{desc => "await client 2 ready (init)",
+ cmd => fun(#{client2 := Pid} = _State) ->
+ ok = ?SEV_AWAIT_READY(Pid, client2, init)
+ end},
+
+ %% *** The actual test ***
+
+ #{desc => "order client 1 to continue (with send request 1)",
+ cmd => fun(#{client1 := Client} = _State) ->
+ ?SEV_ANNOUNCE_CONTINUE(Client, send_req),
+ ok
+ end},
+ #{desc => "await client 1 ready (with send request 1)",
+ cmd => fun(#{client1 := Client} = _State) ->
+ ?SEV_AWAIT_READY(Client, client1, send_req)
+ end},
+ #{desc => "await server ready (request recv 1)",
+ cmd => fun(#{server := Server} = _State) ->
+ ?SEV_AWAIT_READY(Server, server, recv_req)
+ end},
+ #{desc => "order server to continue (with send reply 1)",
+ cmd => fun(#{server := Server} = _State) ->
+ ?SEV_ANNOUNCE_CONTINUE(Server, send_reply),
+ ok
+ end},
+ #{desc => "await server ready (with reply 1 sent)",
+ cmd => fun(#{server := Server} = _State) ->
+ ?SEV_AWAIT_READY(Server, server, send_reply)
+ end},
+ #{desc => "await client 1 ready (reply recv 1)",
+ cmd => fun(#{client1 := Client} = _State) ->
+ ?SEV_AWAIT_READY(Client, client1, recv_reply)
+ end},
+
+
+ #{desc => "order client 2 to continue (with send request 2)",
+ cmd => fun(#{client2 := Client} = _State) ->
+ ?SEV_ANNOUNCE_CONTINUE(Client, send_req),
+ ok
+ end},
+ #{desc => "await client 2 ready (with send request 2)",
+ cmd => fun(#{client2 := Client} = _State) ->
+ ?SEV_AWAIT_READY(Client, client2, send_req)
+ end},
+ #{desc => "await server ready (request recv 2)",
+ cmd => fun(#{server := Server} = _State) ->
+ ?SEV_AWAIT_READY(Server, server, recv_req)
+ end},
+ #{desc => "order server to continue (with send reply 2)",
+ cmd => fun(#{server := Server} = _State) ->
+ ?SEV_ANNOUNCE_CONTINUE(Server, send_reply),
+ ok
+ end},
+ #{desc => "await server ready (with reply 2 sent)",
+ cmd => fun(#{server := Server} = _State) ->
+ ?SEV_AWAIT_READY(Server, server, send_reply)
+ end},
+ #{desc => "await client 2 ready (reply recv 2)",
+ cmd => fun(#{client2 := Client} = _State) ->
+ ?SEV_AWAIT_READY(Client, client2, recv_reply)
+ end},
+
+
+ #{desc => "order client 2 to terminate",
+ cmd => fun(#{client2 := Client} = _State) ->
+ ?SEV_ANNOUNCE_TERMINATE(Client),
+ ok
+ end},
+ #{desc => "await client 2 termination",
+ cmd => fun(#{client2 := Client} = State) ->
+ ?SEV_AWAIT_TERMINATION(Client),
+ State1 = maps:remove(client2, State),
+ {ok, State1}
+ end},
+
+
+ #{desc => "order client 1 to continue (with send request 3)",
+ cmd => fun(#{client1 := Client} = _State) ->
+ ?SEV_ANNOUNCE_CONTINUE(Client, send_req),
+ ok
+ end},
+ #{desc => "await client 1 ready (with send request 3)",
+ cmd => fun(#{client1 := Client} = _State) ->
+ ?SEV_AWAIT_READY(Client, client1, send_req)
+ end},
+ #{desc => "await server ready (request recv 3)",
+ cmd => fun(#{server := Server} = _State) ->
+ ?SEV_AWAIT_READY(Server, server, recv_req)
+ end},
+ #{desc => "order server to continue (with send reply 3)",
+ cmd => fun(#{server := Server} = _State) ->
+ ?SEV_ANNOUNCE_CONTINUE(Server, send_reply),
+ ok
+ end},
+ #{desc => "await server ready (with reply 3 sent)",
+ cmd => fun(#{server := Server} = _State) ->
+ ?SEV_AWAIT_READY(Server, server, send_reply)
+ end},
+ #{desc => "await client 1 ready (reply recv 3)",
+ cmd => fun(#{client1 := Client} = _State) ->
+ ?SEV_AWAIT_READY(Client, client1, recv_reply)
+ end},
+
+
+ %% *** Termination ***
+ #{desc => "order client 1 to terminate",
+ cmd => fun(#{client1 := Client} = _State) ->
+ ?SEV_ANNOUNCE_TERMINATE(Client),
+ ok
+ end},
+ #{desc => "await client 1 termination",
+ cmd => fun(#{client1 := Client} = State) ->
+ ?SEV_AWAIT_TERMINATION(Client),
+ State1 = maps:remove(client1, State),
+ {ok, State1}
+ end},
+ #{desc => "order server to terminate",
+ cmd => fun(#{server := Server} = _State) ->
+ ?SEV_ANNOUNCE_TERMINATE(Server),
+ ok
+ end},
+ #{desc => "await server termination",
+ cmd => fun(#{server := Server} = State) ->
+ ?SEV_AWAIT_TERMINATION(Server),
+ State1 = maps:remove(server, State),
+ {ok, State1}
+ end},
+
+ %% *** We are done ***
+ ?SEV_FINISH_NORMAL
+ ],
+
+ i("start server evaluator"),
+ Server = ?SEV_START("server", ServerSeq, maps:remove(dup, InitState)),
+
+ i("start (socket origin) client 1 evaluator"),
+ Client1 = ?SEV_START("client-1", Client1Seq, maps:remove(dup, InitState)),
+ i("await evaluator(s)"),
+
+ i("start client 2 evaluator"),
+ Client2 = ?SEV_START("client-2", Client2Seq, InitState),
+ i("await evaluator(s)"),
+
+ i("start tester evaluator"),
+ TesterInitState = #{server => Server#ev.pid,
+ client1 => Client1#ev.pid,
+ client2 => Client2#ev.pid},
+ Tester = ?SEV_START("tester", TesterSeq, TesterInitState),
+
+ ok = ?SEV_AWAIT_FINISH([Server, Client1, Client2, Tester]).
+
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% %%
+%% API ASYNC %%
+%% %%
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Basically establish a TCP connection via an async connect. IPv4.
diff --git a/erts/emulator/test/trace_SUITE.erl b/erts/emulator/test/trace_SUITE.erl
index c2d5cd7023..cd60187809 100644
--- a/erts/emulator/test/trace_SUITE.erl
+++ b/erts/emulator/test/trace_SUITE.erl
@@ -664,7 +664,7 @@ dist_procs_trace(Config) when is_list(Config) ->
Proc1 ! {trap_exit_please, true},
Proc3 = receive {spawned, Proc1, P3} -> P3 end,
io:format("Proc3 = ~p ~n", [Proc3]),
- {trace, Proc1, getting_linked, Proc3} = receive_first_trace(),
+ {trace, Proc1, link, Proc3} = receive_first_trace(),
Reason3 = make_ref(),
Proc1 ! {send_please, Proc3, {exit_please, Reason3}},
receive {Proc1, {'EXIT', Proc3, Reason3}} -> ok end,
@@ -958,15 +958,14 @@ do_system_monitor_long_schedule() ->
{Self,L} when is_list(L) ->
ok
after 1000 ->
- ct:fail(no_trace_of_pid)
+ ct:fail(no_trace_of_pid)
end,
"ok" = erlang:port_control(Port,1,[]),
- "ok" = erlang:port_control(Port,2,[]),
receive
{Port,LL} when is_list(LL) ->
ok
after 1000 ->
- ct:fail(no_trace_of_port)
+ ct:fail(no_trace_of_port)
end,
port_close(Port),
erlang:system_monitor(undefined),
diff --git a/erts/emulator/test/trace_SUITE_data/Makefile.src b/erts/emulator/test/trace_SUITE_data/Makefile.src
new file mode 100644
index 0000000000..645107d7b0
--- /dev/null
+++ b/erts/emulator/test/trace_SUITE_data/Makefile.src
@@ -0,0 +1,3 @@
+all: slow_drv@dll@
+
+@SHLIB_RULES@
diff --git a/erts/emulator/test/trace_SUITE_data/slow_drv.c b/erts/emulator/test/trace_SUITE_data/slow_drv.c
new file mode 100644
index 0000000000..4f7c93a69e
--- /dev/null
+++ b/erts/emulator/test/trace_SUITE_data/slow_drv.c
@@ -0,0 +1,102 @@
+#ifdef __WIN32__
+#include <windows.h>
+#endif
+
+#include <stdio.h>
+#include "erl_driver.h"
+
+typedef struct _erl_drv_data {
+ ErlDrvPort erlang_port;
+} EchoDrvData;
+
+static EchoDrvData slow_drv_data, *slow_drv_data_p;
+
+static EchoDrvData *slow_drv_start(ErlDrvPort port, char *command);
+static void slow_drv_stop(EchoDrvData *data_p);
+static void slow_drv_output(ErlDrvData drv_data, char *buf,
+ ErlDrvSizeT len);
+static ErlDrvSSizeT slow_drv_control(ErlDrvData drv_data, unsigned int command,
+ char *buf, ErlDrvSizeT len,
+ char **rbuf, ErlDrvSizeT rlen);
+static void slow_drv_timeout(ErlDrvData drv_data);
+static void slow_drv_finish(void);
+
+static ErlDrvEntry slow_drv_entry = {
+ NULL, /* init */
+ slow_drv_start,
+ slow_drv_stop,
+ slow_drv_output,
+ NULL, /* ready_input */
+ NULL, /* ready_output */
+ "slow_drv",
+ slow_drv_finish,
+ NULL, /* handle */
+ slow_drv_control, /* control */
+ slow_drv_timeout, /* timeout */
+ NULL, /* outputv */
+ NULL, /* ready_async */
+ NULL,
+ NULL,
+ NULL,
+ ERL_DRV_EXTENDED_MARKER,
+ ERL_DRV_EXTENDED_MAJOR_VERSION,
+ ERL_DRV_EXTENDED_MINOR_VERSION,
+ 0,
+ NULL,
+ NULL,
+ NULL
+
+};
+
+DRIVER_INIT(slow_drv)
+{
+ slow_drv_data_p = NULL;
+ return &slow_drv_entry;
+}
+
+static EchoDrvData *slow_drv_start(ErlDrvPort port, char *command)
+{
+ if (slow_drv_data_p != NULL) {
+ return ERL_DRV_ERROR_GENERAL;
+ }
+ slow_drv_data_p = &slow_drv_data;
+ slow_drv_data_p->erlang_port = port;
+ return slow_drv_data_p;
+}
+
+static void slow_drv_stop(EchoDrvData *data_p) {
+ slow_drv_data_p = NULL;
+}
+
+static void slow_drv_output(ErlDrvData drv_data, char *buf, ErlDrvSizeT len) {
+ EchoDrvData* data_p = (EchoDrvData *) drv_data;
+ driver_output(data_p->erlang_port, buf, len);
+}
+
+static ErlDrvSSizeT slow_drv_control(ErlDrvData drv_data, unsigned int command,
+ char *buf, ErlDrvSizeT len,
+ char **rbuf, ErlDrvSizeT rlen)
+{
+ EchoDrvData* data_p = (EchoDrvData *) drv_data;
+ memcpy(*rbuf,"ok",2);
+ if (command == 1) {
+ driver_set_timer(data_p->erlang_port, 0);
+ } else {
+ slow_drv_timeout(drv_data);
+ }
+ return 2;
+}
+
+static void slow_drv_timeout(ErlDrvData drv_data)
+{
+ /* Sleep for 150 msec */
+#ifdef __WIN32__
+ Sleep(150);
+#else
+ usleep(150000);
+#endif
+}
+
+static void slow_drv_finish() {
+ slow_drv_data_p = NULL;
+}
diff --git a/erts/emulator/test/trace_call_time_SUITE.erl b/erts/emulator/test/trace_call_time_SUITE.erl
index 4732d42296..03efb92e2e 100644
--- a/erts/emulator/test/trace_call_time_SUITE.erl
+++ b/erts/emulator/test/trace_call_time_SUITE.erl
@@ -307,8 +307,7 @@ combo(Config) when is_list(Config) ->
2 = erlang:trace_pattern({erlang, term_to_binary, '_'}, [], [local]),
2 = erlang:trace_pattern({erlang, term_to_binary, '_'}, true, [call_time]),
2 = erlang:trace_pattern({erlang, term_to_binary, '_'}, MetaMs, [{meta,MetaTracer}]),
- %% not implemented
- %2 = erlang:trace_pattern({erlang, term_to_binary, '_'}, true, [call_count]),
+ 2 = erlang:trace_pattern({erlang, term_to_binary, '_'}, true, [call_count]),
1 = erlang:trace(Self, true, [{tracer,LocalTracer} | Flags]),
%%
@@ -337,9 +336,7 @@ combo(Config) when is_list(Config) ->
{value,{match_spec,[]}} = lists:keysearch(match_spec, 1, TraceInfoBif),
{value,{meta, MetaTracer}} = lists:keysearch(meta, 1, TraceInfoBif),
{value,{meta_match_spec,MetaMs}} = lists:keysearch(meta_match_spec, 1, TraceInfoBif),
- %% not implemented
- {value,{call_count,false}} = lists:keysearch(call_count, 1, TraceInfoBif),
- %{value,{call_count,0}} = lists:keysearch(call_count, 1, TraceInfoBif),
+ {value,{call_count,0}} = lists:keysearch(call_count, 1, TraceInfoBif),
{value,{call_time,[]}} = lists:keysearch(call_time, 1, TraceInfoBif),
%%
@@ -408,8 +405,12 @@ bif(Config) when is_list(Config) ->
%%
2 = erlang:trace_pattern({erlang, binary_to_term, '_'}, true, [call_time]),
2 = erlang:trace_pattern({erlang, term_to_binary, '_'}, true, [call_time]),
+ 1 = erlang:trace_pattern({?MODULE, with_bif, 1}, true, [call_time]),
Pid = setup(),
- {L, T1} = execute(Pid, fun() -> with_bif(M) end),
+ {L, Tot1} = execute(Pid, fun() -> with_bif(M) end),
+
+ {call_time,[{Pid,_,S,Us}]} = erlang:trace_info({?MODULE,with_bif,1}, call_time),
+ T1 = Tot1 - (S*1000000 + Us),
ok = check_trace_info({erlang, binary_to_term, 1}, [{Pid, M - 1, 0, 0}], T1/2),
ok = check_trace_info({erlang, term_to_binary, 1}, [{Pid, M - 1, 0, 0}], T1/2),
@@ -418,9 +419,9 @@ bif(Config) when is_list(Config) ->
2 = erlang:trace_pattern({erlang, term_to_binary, '_'}, false, [call_time]),
- {L, T2} = execute(Pid, fun() -> with_bif(M) end),
+ {L, _T2} = execute(Pid, fun() -> with_bif(M) end),
- ok = check_trace_info({erlang, binary_to_term, 1}, [{Pid, M*2 - 2, 0, 0}], T1/2 + T2),
+ ok = check_trace_info({erlang, binary_to_term, 1}, [{Pid, M*2 - 2, 0, 0}], T1),
ok = check_trace_info({erlang, term_to_binary, 1}, false, none),
%%
@@ -439,12 +440,14 @@ nif(Config) when is_list(Config) ->
1 = erlang:trace_pattern({?MODULE, nif_dec, '_'}, true, [call_time]),
1 = erlang:trace_pattern({?MODULE, with_nif, '_'}, true, [call_time]),
Pid = setup(),
- {_, T1} = execute(Pid, fun() -> with_nif(M) end),
+ {_, Tot1} = execute(Pid, fun() -> with_nif(M) end),
+
+ {call_time,[{Pid,_,S,Us}]} = erlang:trace_info({?MODULE,with_nif,1}, call_time),
+ T1 = Tot1 - (S*1000000 + Us),
% the nif is called M - 1 times, the last time the function with 'with_nif'
% returns ok and does not call the nif.
- ok = check_trace_info({?MODULE, nif_dec, 1}, [{Pid, M-1, 0, 0}], T1/2),
- ok = check_trace_info({?MODULE, with_nif, 1}, [{Pid, M, 0, 0}], T1/2),
+ ok = check_trace_info({?MODULE, nif_dec, 1}, [{Pid, M-1, 0, 0}], T1),
%%
P = erlang:trace_pattern({'_','_','_'}, false, [call_time]),
@@ -482,6 +485,8 @@ called_function(Config) when is_list(Config) ->
%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
dead_tracer(Config) when is_list(Config) ->
+ TracedMFAs = dead_tracer_mfas(),
+
Self = self(),
FirstTracer = tracer(),
StartTracing = fun() -> turn_on_tracing(Self) end,
@@ -496,14 +501,14 @@ dead_tracer(Config) when is_list(Config) ->
erlang:yield(),
%% Collect and check that we only get call_time info for the current process.
- Info1 = collect_all_info(),
+ Info1 = collect_all_info(TracedMFAs),
[] = other_than_self(Info1),
io:format("~p\n", [Info1]),
%% Note that we have not turned off tracing for the current process,
%% but that the tracer has terminated. No more call_time information should be recorded.
[1,2,3] = seq(1, 3, fun(I) -> I + 1 end),
- [] = collect_all_info(),
+ [] = collect_all_info(TracedMFAs),
%% When we start a second tracer process, that tracer process must
%% not inherit the tracing flags and the dead tracer (even though
@@ -512,7 +517,7 @@ dead_tracer(Config) when is_list(Config) ->
tell_tracer(SecondTracer, StartTracing),
Seq20 = lists:seq(1, 20),
Seq20 = seq(1, 20, fun(I) -> I + 1 end),
- Info2 = collect_all_info(),
+ Info2 = collect_all_info(TracedMFAs),
io:format("~p\n", [Info2]),
[] = other_than_self(Info2),
SecondTracer ! quit,
@@ -548,9 +553,21 @@ turn_on_tracing(Pid) ->
_ = now(),
ok.
-collect_all_info() ->
- collect_all_info([{?MODULE,F,A} || {F,A} <- module_info(functions)] ++
- erlang:system_info(snifs)).
+%% We want to trace functions local to this module as well as all BIFs, and for
+%% the latter we need to ensure that their modules are loaded.
+dead_tracer_mfas() ->
+ Modules = [M || {M,_F,_A} <- erlang:system_info(snifs)],
+ Whitelist0 = gb_sets:from_list(Modules),
+ Whitelist = case code:ensure_modules_loaded(Modules) of
+ {error, Reasons} ->
+ Blacklist = gb_sets:from_list([M || {M, _} <- Reasons]),
+ gb_sets:subtract(Whitelist0, Blacklist);
+ ok ->
+ Whitelist0
+ end,
+ EligibleSNIFs = [MFA || {M,_F,_A}=MFA <- erlang:system_info(snifs),
+ gb_sets:is_element(M, Whitelist)],
+ [{?MODULE,F,A} || {F,A} <- module_info(functions)] ++ EligibleSNIFs.
collect_all_info([MFA|T]) ->
CallTime = erlang:trace_info(MFA, call_time),
@@ -669,21 +686,29 @@ seq_r(Start, Stop, Succ, R) ->
seq_r(Succ(Start), Stop, Succ, [Start | R]).
% Check call time tracing data and print mismatches
-check_trace_info(Mfa, [{Pid, C,_,_}] = Expect, Time) ->
- case erlang:trace_info(Mfa, call_time) of
- % Time tests are somewhat problematic. We want to know if Time (EXPECTED_TIME) and S*1000000 + Us (ACTUAL_TIME)
- % is the same.
- % If the ratio EXPECTED_TIME/ACTUAL_TIME is ~ 1 or if EXPECTED_TIME - ACTUAL_TIME is near zero, the test is ok.
- {call_time,[{Pid,C,S,Us}]} when S >= 0, Us >= 0, abs(1 - Time/(S*1000000 + Us)) < ?R_ERROR; abs(Time - S*1000000 - Us) < ?US_ERROR ->
+check_trace_info(Mfa, [{Pid, ExpectedC,_,_}] = Expect, Time) ->
+ {call_time,[{Pid,C,S,Us}]} = erlang:trace_info(Mfa, call_time),
+ {Mod, Name, Arity} = Mfa,
+ IsBuiltin = erlang:is_builtin(Mod, Name, Arity),
+ if
+ %% Call count on BIFs may exceed number of calls as they often trap to
+ %% themselves.
+ IsBuiltin, C >= ExpectedC, S >= 0, Us >= 0,
+ abs(1 - Time/(S*1000000 + Us)) < ?R_ERROR;
+ abs(Time - S*1000000 - Us) < ?US_ERROR ->
+ ok;
+ not IsBuiltin, C =:= ExpectedC, S >= 0, Us >= 0,
+ abs(1 - Time/(S*1000000 + Us)) < ?R_ERROR;
+ abs(Time - S*1000000 - Us) < ?US_ERROR ->
ok;
- {call_time,[{Pid,C,S,Us}]} ->
+ true ->
Sum = S*1000000 + Us,
- io:format("Expected ~p -> {call_time, ~p (Time ~p us)}~n - got ~w s. ~w us. = ~w us. - ~w -> delta ~w (ratio ~.2f, should be 1.0)~n",
- [Mfa, Expect, Time, S, Us, Sum, Time, Sum - Time, Time/Sum]),
- time_error;
- Other ->
- io:format("Expected ~p -> {call_time, ~p (Time ~p us)}~n - got ~p~n", [ Mfa, Expect, Time, Other]),
- time_count_error
+ io:format("Expected ~p -> {call_time, ~p (Time ~p us)}~n - got ~w "
+ "s. ~w us. = ~w us. - ~w -> delta ~w (ratio ~.2f, "
+ "should be 1.0)~n",
+ [Mfa, Expect, Time,
+ S, Us, Sum, Time, Sum - Time, Time/Sum]),
+ time_error
end;
check_trace_info(Mfa, Expect, _) ->
case erlang:trace_info(Mfa, call_time) of
@@ -748,7 +773,8 @@ setup() ->
setup([]).
setup(Opts) ->
- Pid = spawn_link(fun() -> loop() end),
+ Pid = spawn_opt(fun() -> loop() end,
+ [link, {max_heap_size, 10000}]),
1 = erlang:trace(Pid, true, [call|Opts]),
Pid.
@@ -772,9 +798,12 @@ loop() ->
quit ->
ok;
{Pid, execute, Fun } when is_function(Fun) ->
+ %% Make sure we always run with the same amount of reductions.
+ erlang:yield(),
Pid ! {self(), answer, erlang:apply(Fun, [])},
loop();
{Pid, execute, {M, F, A}} ->
+ erlang:yield(),
Pid ! {self(), answer, erlang:apply(M, F, A)},
loop()
end.
diff --git a/erts/emulator/test/trace_local_SUITE.erl b/erts/emulator/test/trace_local_SUITE.erl
index ad802352b9..699964b4fb 100644
--- a/erts/emulator/test/trace_local_SUITE.erl
+++ b/erts/emulator/test/trace_local_SUITE.erl
@@ -1111,12 +1111,14 @@ x_exc_body(ExcOpts, {M,F}=Func, Args, Apply) ->
end,
{value,Value}
catch
- Thrown when Nocatch ->
+ throw:Thrown:Stk when Nocatch ->
+ put(get_stacktrace, Stk),
CR = {error,{nocatch,Thrown}},
x_exc_exception(Rtt, M, F, Args, Arity, CR),
expect({eft,{?MODULE,exc,2},CR}),
CR;
- Class:Reason ->
+ Class:Reason:Stk ->
+ put(get_stacktrace, Stk),
CR = {Class,Reason},
x_exc_exception(Rtt, M, F, Args, Arity, CR),
expect({eft,{?MODULE,exc,2},CR}),
@@ -1157,7 +1159,8 @@ x_exc_exception(_Rtt, M, F, _, Arity, CR) ->
expect({eft,{M,F,Arity},CR}).
x_exc_stacktrace() ->
- x_exc_stacktrace(erlang:get_stacktrace()).
+ x_exc_stacktrace(get(get_stacktrace)).
+
%% Truncate stacktrace to below exc/2
x_exc_stacktrace([{?MODULE,x_exc,4,_}|_]) -> [];
x_exc_stacktrace([{?MODULE,x_exc_func,4,_}|_]) -> [];
diff --git a/erts/emulator/utils/beam_makeops b/erts/emulator/utils/beam_makeops
index 605a402f2a..005a6e6d45 100755
--- a/erts/emulator/utils/beam_makeops
+++ b/erts/emulator/utils/beam_makeops
@@ -186,7 +186,7 @@ foreach my $type (keys %pattern_type) {
$construction_type{$type} = 1
if index($genop_types, $type) >= 0;
}
-foreach my $makes_no_sense ('f', 'j', 'o', 'p', 'q') {
+foreach my $makes_no_sense ('f', 'j', 'o', 'q') {
delete $construction_type{$makes_no_sense};
}
@@ -719,7 +719,7 @@ sub emulator_output {
print "const GenOpEntry gen_opc[] = {\n";
for ($i = 0; $i < @gen_opname; $i++) {
if ($i == $num_file_opcodes) {
- print "\n/*\n * Internal generic instructions.\n */\n\n";
+ print starred_comment("Internal generic instructions.");
}
my($name) = $gen_opname[$i];
my($arity) = $gen_arity[$i];
@@ -1315,7 +1315,7 @@ sub combine_instruction_group {
my $inc = 0;
unless ($i == $#slots) {
- $flags = "-no_next";
+ $flags = "-micro_instruction";
my $next_offset = $label_to_offset{$next};
$inc = ($offset + $size) - $next_offset;
$transfer_to_next = "I += $inc;\n" if $inc;
@@ -1553,8 +1553,10 @@ sub code_gen {
my $dispatch_next;
my $instr_offset = $group_size + $offset + 1;
- if ($flags =~ /-no_next/) {
+ if ($flags =~ /-micro_instruction/) {
$dispatch_next = "";
+ } elsif ($flags =~ /-no_next/) {
+ $dispatch_next = "ASSERT(!\"Fell through '$name' (-no_next)\");";
} elsif ($flags =~ /-no_prefetch/) {
$dispatch_next = "\nI += $instr_offset;\n" .
"ASSERT(VALID_INSTR(*I));\n" .
@@ -2238,6 +2240,7 @@ sub parse_transformation {
(undef,$_) = compile_transform(0, $rest_var, @op);
}
}
+ $orig =~ tr/ \t/ /s;
push(@transformations, [$., $orig, [@from], [reverse @to]]);
}
@@ -2396,6 +2399,13 @@ sub tr_gen {
}
#
+ # Group instructions.
+ #
+ foreach $key (sort keys %gen_transform) {
+ $gen_transform{$key} = group_tr($gen_transform{$key});
+ }
+
+ #
# Print the generated transformation engine.
#
my($offset) = 0;
@@ -2404,29 +2414,18 @@ sub tr_gen {
$gen_transform_offset{$key} = $offset;
my @instr = @{$gen_transform{$key}};
- #
- # If the last instruction is 'fail', remove it and
- # convert the previous 'try_me_else' to 'try_me_else_fail'.
- #
- if (is_instr($instr[$#instr], 'fail')) {
- pop(@instr);
- my $i = $#instr;
- $i-- while !is_instr($instr[$i], 'try_me_else');
- $instr[$i] = make_op('', 'try_me_else_fail');
- }
-
foreach $instr (@instr) {
my($size, $instr_ref, $comment) = @$instr;
my($op, @args) = @$instr_ref;
- print " ";
if (!defined $op) {
$comment =~ s/\n(.)/\n $1/g;
- print "\n", $comment;
+ print $comment;
} else {
+ print " ";
$op = "TOP_$op";
$match_engine_ops{$op} = 1;
if ($comment ne '') {
- printf "%-24s /* %s */\n", (join(", ", ($op, @args)) . ","),
+ printf "%-30s /* %s */\n", (join(", ", ($op, @args)) . ","),
$comment;
} else {
print join(", ", ($op, @args)), ",\n";
@@ -2436,9 +2435,7 @@ sub tr_gen {
}
print "\n";
}
- print "/*\n";
- print " * Total number of words: $offset\n";
- print " */\n";
+ print starred_comment("Total number of words: $offset");
print "};\n\n";
}
@@ -2452,6 +2449,7 @@ sub tr_gen_from {
my $where = "left side of transformation in line $line: ";
my $may_fail = 0;
my $is_first = 1;
+ my @instrs;
foreach $ref (@tr) {
my($name, $arity, @ops) = @$ref;
@@ -2469,20 +2467,26 @@ sub tr_gen_from {
$name = $1;
$may_fail = 1;
my $var;
- my(@args);
+ my @args;
+ my @vars;
foreach $var (@ops) {
- error($where, "variable '$var' unbound")
+ if ($var =~ /^-?\d+$/) {
+ push @args, $var;
+ next;
+ }
+ error($where, "'$var' unbound")
unless defined $var{$var};
+ push @vars, $var;
if ($var_type{$var} eq 'scalar') {
push(@args, "var[$var{$var}]");
} else {
push(@args, "rest_args");
}
}
- my $pi = tr_next_index(\@pred_table, \%pred_table, $name, @args);
- my $op = make_op("$name()", 'pred', $pi);
- my @slots = grep(/^\d+/, map { $var{$_} } @ops);
+ my $pi = tr_next_index(\@{pred_table}, \%pred_table, $name, @args);
+ my $op = make_op("$name()", 'pred', $pi);
+ my @slots = grep(/^\d+/, map { $var{$_} } @vars);
op_slot_usage($op, @slots);
push(@code, $op);
next;
@@ -2497,6 +2501,7 @@ sub tr_gen_from {
$opnum = $gen_opnum{$name,$arity};
push(@code, make_op("$name/$arity", 'next_instr', $opnum));
+ push @instrs, "$name/$arity";
foreach $op (@ops) {
my($var, $type, $type_val, $cond, $val) = @$op;
my $ignored_var = "$var (ignored)";
@@ -2587,14 +2592,47 @@ sub tr_gen_from {
#
push(@code, make_op($may_fail ? '' : 'always reached', 'commit'));
+ #
+ # Peephole optimization: combine instructions.
+ #
+ for (my $i = 0; $i < @code; $i++) {
+ if (is_instr($code[$i], 'is_type')) {
+ my(undef, $is_type_ref, $type_comment) = @{$code[$i]};
+ if (is_instr($code[$i+1], 'set_var_next_arg')) {
+ my(undef, $next_ref, $next_comment) = @{$code[$i+1]};
+ my $comment = "$type_comment $next_comment";
+ my $op = make_op($comment, 'is_type_set_var_next_arg',
+ $is_type_ref->[1], $next_ref->[1]);
+ splice @code, $i, 2, ($op);
+ } elsif (is_instr($code[$i+1], 'next_arg')) {
+ my $op = make_op($type_comment, 'is_type_next_arg', $is_type_ref->[1]);
+ splice @code, $i, 2, ($op);
+ }
+ } elsif (is_instr($code[$i], 'is_type_eq')) {
+ my(undef, $is_type_ref, $type_comment) = @{$code[$i]};
+ if (is_instr($code[$i+1], 'set_var_next_arg')) {
+ my(undef, $next_ref, $next_comment) = @{$code[$i+1]};
+ my $comment = "$type_comment $next_comment";
+ my $op = make_op($comment, 'is_type_eq_set_var_next_arg',
+ $is_type_ref->[1], $is_type_ref->[2],
+ $next_ref->[1]);
+ splice @code, $i, 2, ($op);
+ } elsif (is_instr($code[$i+1], 'next_arg')) {
+ my $op = make_op($type_comment, 'is_type_eq_next_arg',
+ $is_type_ref->[1], $is_type_ref->[2]);
+ splice @code, $i, 2, ($op);
+ }
+ }
+ }
+
$te_max_vars = $var_num
if $te_max_vars < $var_num;
- [\%var, \%var_type, \@code];
+ [\%var, \%var_type, \@instrs, \@code];
}
sub tr_gen_to {
my($line, $orig_transform, $so_far, @tr) = @_;
- my($var_ref, $var_type_ref, $code_ref) = @$so_far;
+ my($var_ref, $var_type_ref, $instrs_ref, $code_ref) = @$so_far;
my(%var) = %$var_ref;
my(%var_type) = %$var_type_ref;
my(@code) = @$code_ref;
@@ -2662,11 +2700,10 @@ sub tr_gen_to {
op_slot_usage($op, $var{$var});
push(@code, $op);
} elsif ($type ne '') {
- push(@code, make_op('', 'store_type', "TAG_$type"));
- if ($type_val) {
- push(@code, make_op('', 'store_val', $type_val));
- }
- push(@code, make_op('', 'next_arg'));
+ my $val = $type_val || 0;
+ my $comment = "$type=$val";
+ my $op = make_op($comment, 'store_val_next_arg', "TAG_$type", $val);
+ push @code, $op;
}
}
pop(@code) if is_instr($code[$#code], 'next_arg');
@@ -2677,31 +2714,133 @@ sub tr_gen_to {
tr_maybe_keep(\@code);
tr_maybe_rename(\@code);
+ combine_commit(\@code);
tr_remove_unused(\@code);
+ chain_instructions($instrs_ref, $line, $orig_transform,
+ $cannot_fail, @code);
+}
+
+#
+# Chain together all codes segments having the same first instruction.
+#
+sub chain_instructions() {
+ my($instrs_ref, $line, $orig_transform, $cannot_fail, @code) = @_;
+ my($first,$second) = @$instrs_ref;
+
+ my $tr_ref = $gen_transform{$first};
+
+ if ($tr_ref) {
+ my (undef, $cant_fail) = $$tr_ref[$#{$tr_ref}];
+ if ($cant_fail) {
+ error("Line $line: A previous transformation shadows '$orig_transform'");
+ }
+ }
+ shift @code;
+ my $comment = starred_comment("Line $line:", " $orig_transform");
+ push @$tr_ref, [$first,$second,$cannot_fail,$comment,@code];
+
+ $gen_transform{$first} = $tr_ref;
+}
+
+#
+# Optimize the code for transformations matching the same first
+# instruction.
+#
+
+sub group_tr {
+ my($lref) = @_;
+
#
- # Chain together all codes segments having the same first operation.
+ # Group tranformations (while keeping the order) that all match the same
+ # second instruction.
#
- my($first_ref) = shift(@code);
- my($size, $first, $key) = @$first_ref;
- my($dummy, $arity);
- ($dummy, $op, $arity) = @$first;
- my($comment) = "\n/*\n * Line $line:\n * $orig_transform\n */\n\n";
+ for (my $i = 0; $i < @$lref; $i++) {
+ my(undef,$current) = @{${$lref}[$i]};
+ next unless defined $current;
+
+ # Find the next instruction that as the same second instruction.
+ for (my $j = $i + 1; $j < @$lref; $j++) {
+ my(undef,$other) = @{${$lref}[$j]};
- my $prev_last;
- $prev_last = pop(@{$gen_transform{$key}})
- if defined $gen_transform{$key}; # Fail
+ # If this instruction does not match a second instruction,
+ # we must not continue the search.
+ last unless defined $other;
- if ($prev_last && !is_instr($prev_last, 'fail')) {
- error("Line $line: A previous transformation shadows '$orig_transform'");
+ if ($other eq $current) {
+ # Found an instruction. If it is already the very next
+ # instruction, place it directly after the current instruction.
+ if ($j > $i + 1) {
+ my($el) = splice @$lref, $j, 1;
+ splice @$lref, $i, 0, ($el);
+ }
+ last;
+ }
+ }
}
- unless ($cannot_fail) {
- unshift(@code, make_op('', 'try_me_else',
- tr_code_len(@code)));
- push(@code, make_op("$key", 'fail'));
+
+ #
+ # Add 'try_me_else' instructions to try the next transformation
+ # when the current transformation fails.
+ #
+ for (my $i = 0; $i < @$lref; $i++) {
+ my($first,$second,$cannot_fail,$comment,@c) = @{${$lref}[$i]};
+ unless ($cannot_fail) {
+ if ($i == $#{$lref}) {
+ unshift @c, make_op('', 'try_me_else_fail');
+ } else {
+ unshift @c, make_op('', 'try_me_else', code_len(@c));
+ }
+ }
+ ${$lref}[$i] = [$first,$second,$cannot_fail,$comment,@c];
+ }
+
+ #
+ # Find consecutive runs of at least two transformation matching
+ # the same second instruction. When a run is found, add a
+ # 'skip_unless' instruction that will skip all of the instructions in the run
+ # when the second instruction is wrong.
+ #
+ for (my $i = 0; $i < @$lref; $i++) {
+ my(undef,$current) = @{${$lref}[$i]};
+ next unless defined $current;
+ my $j;
+ my $skip_len = 0;
+
+ for ($j = $i; $j < @$lref; $j++) {
+ my(undef,$other,undef,undef,@c) = @{${$lref}[$j]};
+ last unless defined $other and $other eq $current;
+ $skip_len += code_len(@c);
+ }
+
+ if ($j > $i + 1) {
+ my $num_rules_skipped = $j - $i;
+ my $comment = "Skip $num_rules_skipped rules" .
+ " unless the second instruction is $current.";
+ $comment = starred_comment($comment);
+ my($name, $arity) = split('/', $current);
+ my $op = make_op('', 'skip_unless',
+ $gen_opnum{$name,$arity}, $skip_len);
+ splice @$lref, $i, 0, (['','',1,$comment,$op]);
+ $i = $j + 1;
+ if ($j == $#{$lref}) {
+ my($first,$second,$cannot_fail,$comment,@c) = @{${$lref}[$j]};
+ push @c, make_op('wrong second instruction', 'fail');
+ ${$lref}[$j] = [$first,$second,$cannot_fail,$comment,@c];
+ }
+ }
}
- unshift(@code, make_op($comment));
- push(@{$gen_transform{$key}}, @code),
+
+ #
+ # Flatten the code to a one-dimensional sequence of instructions.
+ #
+ my @code;
+ for (my $i = 0; $i < @$lref; $i++) {
+ my($first,$second,$cannot_fail,$comment,@c) = @{${$lref}[$i]};
+ push @code, make_op($comment);
+ push @code, @c;
+ }
+ \@code;
}
sub tr_maybe_keep {
@@ -2718,6 +2857,10 @@ sub tr_maybe_keep {
@last_instr = ($args[0]);
} elsif ($op eq 'set_var_next_arg') {
push @last_instr, $args[0];
+ } elsif ($op eq 'is_type_set_var_next_arg') {
+ push @last_instr, $args[1];
+ } elsif ($op eq 'is_type_eq_set_var_next_arg') {
+ push @last_instr, $args[2];
} elsif ($op eq 'next_arg') {
push @last_instr, 'ignored';
} elsif ($op eq 'new_instr') {
@@ -2744,6 +2887,22 @@ sub tr_maybe_keep {
}
}
+sub combine_commit {
+ my($ref) = @_;
+
+ for (my $i = 1; $i < @$ref; $i++) {
+ my $instr = $$ref[$i];
+ my($size, $instr_ref, $comment) = @$instr;
+ my($op, @args) = @$instr_ref;
+ if ($op eq 'rest_args') {
+ return;
+ } elsif ($op eq 'new_instr' and is_instr($$ref[$i-1], 'commit')) {
+ my $op = make_op($comment, 'commit_new_instr', @args);
+ splice @$ref, $i - 1, 2, ($op);
+ }
+ }
+}
+
sub tr_maybe_rename {
my($ref) = @_;
my $s = 'left';
@@ -2764,8 +2923,22 @@ sub tr_maybe_rename {
$num_args++;
}
$a++;
+ } elsif ($op eq 'is_type_set_var_next_arg') {
+ if ($num_args == $a and $args[1] == $a) {
+ $num_args++;
+ }
+ $a++;
+ } elsif ($op eq 'is_type_eq_set_var_next_arg') {
+ if ($num_args == $a and $args[2] == $a) {
+ $num_args++;
+ }
+ $a++;
} elsif ($op eq 'next_arg') {
$a++;
+ } elsif ($op eq 'is_type_next_arg') {
+ $a++;
+ } elsif ($op eq 'is_type_eq_next_arg') {
+ $a++;
} elsif ($op eq 'commit') {
$a = 0;
$first = $i;
@@ -2809,8 +2982,7 @@ sub tr_remove_unused {
}
}
- # Replace 'set_var_next_arg' with 'next_arg' if the variable
- # is never used.
+ # If a variable is not used, don't store the variable.
for my $instr (@$ref) {
my($size, $instr_ref, $comment) = @$instr;
my($op, @args) = @$instr_ref;
@@ -2818,7 +2990,16 @@ sub tr_remove_unused {
my $var = $args[0];
next if $used{$var};
$instr = make_op("$comment (ignored)", 'next_arg');
- }
+ } elsif ($op eq 'is_type_set_var_next_arg') {
+ my($type,$var) = @args;
+ next if $used{$var};
+ $instr = make_op("$comment (ignored)", 'is_type_next_arg', $type);
+ } elsif ($op eq 'is_type_eq_set_var_next_arg') {
+ my($type,$val,$var) = @args;
+ next if $used{$var};
+ $instr = make_op("$comment (ignored)", 'is_type_eq_next_arg',
+ $type, $val);
+ }
}
# Delete a sequence of 'next_arg' instructions when they are
@@ -2846,7 +3027,7 @@ sub tr_remove_unused {
}
}
-sub tr_code_len {
+sub code_len {
my($sum) = 0;
my($ref);
@@ -2878,6 +3059,10 @@ sub get_comment {
$ref->[2];
}
+sub starred_comment {
+ "\n/*" . join("\n * ", '', @_) . "\n */\n\n";
+}
+
sub tr_next_index {
my($lref,$href,$name,@args) = @_;
my $code = "RVAL = $name(" . join(', ', 'st', @args) . "); break;\n";
diff --git a/erts/emulator/utils/make_alloc_types b/erts/emulator/utils/make_alloc_types
index 33afe139a2..ddb8713a72 100755
--- a/erts/emulator/utils/make_alloc_types
+++ b/erts/emulator/utils/make_alloc_types
@@ -233,6 +233,7 @@ foreach my $a (@a_order) {
$a_no--;
print DST "\n#define ERTS_ALC_A_MAX ($a_no)\n";
+print DST "\n#define ERTS_ALC_A_COUNT (ERTS_ALC_A_MAX - ERTS_ALC_A_MIN + 1)\n";
# Print class numbers -----------------------------------------------------
@@ -254,6 +255,7 @@ foreach my $c (sort keys(%c_tab)) {
}
$c_no--;
print DST "\n#define ERTS_ALC_C_MAX ($c_no)\n";
+print DST "\n#define ERTS_ALC_C_COUNT (ERTS_ALC_C_MAX - ERTS_ALC_C_MIN + 1)\n";
# Print type number intervals ---------------------------------------------
@@ -291,6 +293,7 @@ foreach my $a (@a_order) {
}
$t_no--;
print DST "#define ERTS_ALC_N_MAX ($t_no)\n";
+print DST "\n#define ERTS_ALC_N_COUNT (ERTS_ALC_N_MAX - ERTS_ALC_N_MIN + 1)\n";
# Print multi thread use of allocators -------------------------------------
diff --git a/erts/emulator/utils/make_preload b/erts/emulator/utils/make_preload
index 0cd3509b62..04163bc63b 100755
--- a/erts/emulator/utils/make_preload
+++ b/erts/emulator/utils/make_preload
@@ -40,7 +40,6 @@ use File::Basename;
my $gen_rc = 0;
my $gen_old = 0;
my $windres = 0;
-my $msys = 0;
my $file;
my $progname = basename($0);
@@ -51,8 +50,6 @@ while (@ARGV && $ARGV[0] =~ /^-(\w+)/) {
$gen_rc = 1;
} elsif ($opt eq '-windres') {
$windres = 1;
- } elsif ($opt eq '-msys') {
- $msys = 1;
} elsif ($opt eq '-old') {
$gen_old = 1;
} else {
@@ -73,12 +70,7 @@ foreach $file (@ARGV) {
my $module = basename($file, ".beam");
if ($gen_rc) {
my $win_file;
- if ($msys) {
- ($win_file) = split("\n", `(msys2win_path.sh $file)`);
- } else {
- ($win_file) = split("\n", `(cygpath -d $file 2>/dev/null || cygpath -w $file)`);
- }
- $win_file =~ s&\\&\\\\&g;
+ ($win_file) = split("\n", `(w32_path.sh -d $file)`);
print "$num ERLANG_CODE \"$win_file\"\n";
push(@modules, " ", -s $file, "L, $num, ",
length($module), ",\"$module\",\n");
diff --git a/erts/emulator/utils/make_tables b/erts/emulator/utils/make_tables
index deee5c2344..f87472111f 100755
--- a/erts/emulator/utils/make_tables
+++ b/erts/emulator/utils/make_tables
@@ -35,7 +35,6 @@ use File::Basename;
# Output:
# <-src>/erl_am.c
# <-src>/erl_bif_table.c
-# <-src>/erl_bif_wrap.c
# <-src>/erl_dirty_bif_wrap.c
# <-src>/erl_guard_bifs.c
# <-src>/hipe_nbif_impl.c
@@ -92,7 +91,7 @@ while (<>) {
my($type, @args) = split;
if ($type eq 'atom') {
save_atoms(@args);
- } elsif ($type eq 'bif' or $type eq 'ubif' or $type eq 'gcbif') {
+ } elsif ($type eq 'bif' or $type eq 'ubif' or $type eq 'hbif') {
if (@args > 2) {
error("$type only allows two arguments");
}
@@ -124,14 +123,22 @@ while (<>) {
error("invalid sched_type: $sched_type");
}
- my $wrapper;
- if ($type eq 'bif') {
- $wrapper = "wrap_$alias";
- } else {
- $wrapper = $alias;
- }
+ my $kind;
+ if ($type eq 'bif') {
+ $kind = 'BIF_KIND_REGULAR';
+ }
+ elsif ($type eq 'hbif') {
+ $kind = 'BIF_KIND_HEAVY';
+ }
+ elsif ($type eq 'ubif') {
+ $kind = 'BIF_KIND_GUARD';
+ }
+ else {
+ error("invalid bif_type: $type");
+ }
+
push(@bif, ["am_$atom_alias{$mod}","am_$atom_alias{$name}",$arity,
- $alias3,$wrapper,$alias]);
+ $alias3,$alias,$kind]);
push(@bif_info, [$type, $sched_type, $alias3, $alias]);
} elsif ($type eq 'dirty-cpu' or $type eq 'dirty-io'
or $type eq 'dirty-cpu-test' or $type eq 'dirty-io-test') {
@@ -196,7 +203,7 @@ open_file("$include/erl_bif_list.h");
my $i;
for ($i = 0; $i < @bif; $i++) {
# module atom, function atom, arity, C function, table index
- print "BIF_LIST($bif[$i]->[0],$bif[$i]->[1],$bif[$i]->[2],$bif[$i]->[3],$bif[$i]->[5],$i)\n";
+ print "BIF_LIST($bif[$i]->[0],$bif[$i]->[1],$bif[$i]->[2],$bif[$i]->[3],$bif[$i]->[4],$i)\n";
}
#
@@ -208,15 +215,24 @@ my $bif_size = @bif;
print <<EOF;
#ifndef __ERL_BIF_TABLE_H__
#define __ERL_BIF_TABLE_H__
+
+#include "sys.h"
+
typedef void *BifFunction;
+typedef enum {
+ BIF_KIND_REGULAR,
+ BIF_KIND_HEAVY,
+ BIF_KIND_GUARD
+} BifKind;
+
typedef struct bif_entry {
Eterm module;
Eterm name;
int arity;
BifFunction f;
- BifFunction traced;
BifFunction impl;
+ BifKind kind;
} BifEntry;
typedef struct erts_gc_bif {
@@ -231,8 +247,7 @@ typedef struct erts_u_bif {
} ErtsUBif;
extern BifEntry bif_table[];
-extern Export* bif_export[];
-extern const ErtsGcBif erts_gc_bifs[];
+extern Export bif_trap_export[];
extern const ErtsUBif erts_u_bifs[];
#define BIF_SIZE $bif_size
@@ -250,7 +265,6 @@ for ($i = 0; $i < @bif; $i++) {
my $args = join(', ', 'Process*', 'Eterm*', 'UWord*');
my $name = $bif_info[$i]->[3];
print "Eterm $name($args);\n";
- print "Eterm wrap_$name($args);\n";
print "Eterm erts_gc_$name(Process* p, Eterm* reg, Uint live);\n"
if $bif_info[$i]->[0] eq 'gcbif';
print "Eterm $bif_info[$i]->[2]($args);\n"
@@ -273,7 +287,7 @@ my $i;
includes("export.h", "sys.h", "erl_vm.h", "erl_process.h", "bif.h",
"erl_bif_table.h", "erl_atom_table.h");
-print "\nExport* bif_export[BIF_SIZE];\n";
+print "\nExport bif_trap_export[BIF_SIZE];\n";
print "BifEntry bif_table[] = {\n";
for ($i = 0; $i < @bif; $i++) {
@@ -283,25 +297,6 @@ for ($i = 0; $i < @bif; $i++) {
print "};\n\n";
#
-# Generate the bif wrappers file.
-#
-
-open_file("$src/erl_bif_wrap.c");
-my $i;
-includes("export.h", "sys.h", "erl_vm.h", "global.h", "erl_process.h", "bif.h",
- "erl_bif_table.h", "erl_atom_table.h");
-for ($i = 0; $i < @bif; $i++) {
- next if $bif[$i]->[3] eq $bif[$i]->[4]; # Skip unwrapped bifs
- my $arity = $bif[$i]->[2];
- my $func = $bif_info[$i]->[3];
- print "Eterm\n";
- print "wrap_$func(Process* p, Eterm* args, UWord* I)\n";
- print "{\n";
- print " return erts_bif_trace($i, p, args, I);\n";
- print "}\n\n";
-}
-
-#
# Generate erl_gc_bifs.c.
#
@@ -309,18 +304,11 @@ open_file("$src/erl_guard_bifs.c");
my $i;
includes("export.h", "sys.h", "erl_vm.h", "global.h", "erl_process.h", "bif.h",
"erl_bif_table.h");
-print "const ErtsGcBif erts_gc_bifs[] = {\n";
-for ($i = 0; $i < @bif; $i++) {
- next unless $bif_info[$i]->[0] eq 'gcbif';
- print " {$bif[$i]->[3], erts_gc_$bif[$i]->[3], BIF_$bif[$i]->[5]},\n";
-}
-print " {NULL, NULL, -1}\n";
-print "};\n";
print "const ErtsUBif erts_u_bifs[] = {\n";
for ($i = 0; $i < @bif; $i++) {
next unless $bif_info[$i]->[0] eq 'ubif';
- print " {$bif[$i]->[3], BIF_$bif[$i]->[5]},\n";
+ print " {$bif[$i]->[3], BIF_$bif[$i]->[4]},\n";
}
print " {NULL, -1}\n";
print "};\n";
@@ -368,7 +356,7 @@ EOF
my $i;
for ($i = 0; $i < @bif; $i++) {
print <<EOF;
-Eterm nbif_impl_$bif[$i]->[5](Process *c_p, Eterm *regs);
+Eterm nbif_impl_$bif[$i]->[4](Process *c_p, Eterm *regs);
EOF
}
@@ -388,9 +376,9 @@ EOF
for ($i = 0; $i < @bif; $i++) {
print <<EOF;
-Eterm nbif_impl_$bif[$i]->[5](Process *c_p, Eterm *regs)
+Eterm nbif_impl_$bif[$i]->[4](Process *c_p, Eterm *regs)
{
- return $bif[$i]->[3](c_p, regs, (UWord *) bif_export\[BIF_$bif[$i]->[5]\]);
+ return $bif[$i]->[3](c_p, regs, (UWord *)&bif_trap_export\[BIF_$bif[$i]->[4]\]);
}
EOF
diff --git a/erts/epmd/Makefile b/erts/epmd/Makefile
index d3308ddedc..e4b201bd88 100644
--- a/erts/epmd/Makefile
+++ b/erts/epmd/Makefile
@@ -31,3 +31,5 @@ SPECIAL_TARGETS =
# Default Subdir Targets
# ----------------------------------------------------
include $(ERL_TOP)/make/otp_subdir.mk
+
+include $(ERL_TOP)/make/app_targets.mk
diff --git a/erts/epmd/epmd.mk b/erts/epmd/epmd.mk
index b1fd04dc04..f6889a7ff1 100644
--- a/erts/epmd/epmd.mk
+++ b/erts/epmd/epmd.mk
@@ -67,5 +67,5 @@ EPMD_NODE_TYPE=110
# Distribution format 5 contains the new md5 based handshake.
EPMD_DIST_LOW=5
-EPMD_DIST_HIGH=5
+EPMD_DIST_HIGH=6
diff --git a/erts/epmd/src/Makefile.in b/erts/epmd/src/Makefile.in
index da4370d5f9..f9f65cec5e 100644
--- a/erts/epmd/src/Makefile.in
+++ b/erts/epmd/src/Makefile.in
@@ -53,12 +53,8 @@ ERTS_INCL = -I$(ERL_TOP)/erts/include \
ifeq ($(TARGET),win32)
ERTS_INTERNAL_LIBS=-L../../lib/internal/$(TARGET) -lerts_internal_r$(ERTS_LIB_TYPEMARKER) @ERTS_INTERNAL_X_LIBS@
else
-ifeq ($(findstring vxworks,$(TARGET)),vxworks)
-ERTS_INTERNAL_LIBS=-L../../lib/internal/$(TARGET) -lerts_internal$(ERTS_LIB_TYPEMARKER) @ERTS_INTERNAL_X_LIBS@
-else
ERTS_INTERNAL_LIBS=-L../../lib/internal/$(TARGET) -lerts_internal$(ERTS_LIB_TYPEMARKER) @ERTS_INTERNAL_X_LIBS@ -lm
endif
-endif
ERTS_LIB = $(ERL_TOP)/erts/lib_src/obj/$(TARGET)/$(TYPE)/MADE
diff --git a/erts/epmd/src/epmd.c b/erts/epmd/src/epmd.c
index 8313678b5d..1abc3d2fca 100644
--- a/erts/epmd/src/epmd.c
+++ b/erts/epmd/src/epmd.c
@@ -81,48 +81,6 @@ static char *mystrdup(char *s)
return r;
}
-#ifdef VXWORKS
-int start_epmd(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9)
-char *a0, *a1, *a2, *a3, *a4, *a5, *a6, *a7, *a8, *a9;
-{
- char* argarr[] = {a0,a1,a2,a3,a4,a5,a6,a7,a8,a9};
- int i;
- char** argv = malloc(sizeof(char *)*10);
- int argvsiz = 10;
- int argc = 1;
- char* tmp = malloc(100);
- int tmpsiz = 100;
- char* pplast;
- char* token;
-
- argv[0] = mystrdup("epmd");
- argv[1] = NULL;
-
- for(i=0;i<10;++i)
- {
- if(argarr[i] == NULL || *argarr[i] == '\0')
- continue;
- if(strlen(argarr[i]) >= tmpsiz)
- tmp = realloc(tmp, tmpsiz = (strlen(argarr[i])+1));
- strcpy(tmp,argarr[i]);
- for(token = strtok_r(tmp," ",&pplast);
- token != NULL;
- token = strtok_r(NULL," ",&pplast))
- {
- if(argc >= argvsiz - 1)
- argv = realloc(argv,sizeof(char *) * (argvsiz += 10));
- argv[argc++] = mystrdup(token);
- argv[argc] = NULL;
- }
- }
- free(tmp);
- return taskSpawn("epmd",100,VX_FP_TASK,20000,epmd_main,
- argc,(int) argv,1,
- 0,0,0,0,0,0,0);
-}
-#endif /* WxWorks */
-
-
int epmd(int argc, char **argv)
{
return epmd_main(argc,argv,0);
@@ -397,13 +355,6 @@ static void run_daemon(EpmdVars *g)
}
#endif
-#if defined(VXWORKS)
-static void run_daemon(EpmdVars *g)
-{
- run(g);
-}
-#endif
-
/***************************************************************************
* Misc support routines
@@ -474,7 +425,7 @@ static void usage(EpmdVars *g)
* Error reporting - dbg_printf() & dbg_tty_printf & dbg_perror()
*
* The first form will print out on tty or syslog depending on
- * if it runs as deamon or not. The second form will never print
+ * if it runs as daemon or not. The second form will never print
* out on syslog.
*
* The arguments are
diff --git a/erts/epmd/src/epmd.h b/erts/epmd/src/epmd.h
index cffcd4ae7a..7332294d3d 100644
--- a/erts/epmd/src/epmd.h
+++ b/erts/epmd/src/epmd.h
@@ -26,6 +26,7 @@
#define EPMD_ALIVE2_REQ 'x'
#define EPMD_PORT2_REQ 'z'
#define EPMD_ALIVE2_RESP 'y'
+#define EPMD_ALIVE2_X_RESP 'v'
#define EPMD_PORT2_RESP 'w'
#define EPMD_NAMES_REQ 'n'
diff --git a/erts/epmd/src/epmd_int.h b/erts/epmd/src/epmd_int.h
index ed9bbdb8cd..c51ff22d82 100644
--- a/erts/epmd/src/epmd_int.h
+++ b/erts/epmd/src/epmd_int.h
@@ -30,13 +30,6 @@
#define NO_DAEMON
#endif
-#ifdef VXWORKS
-#define NO_SYSCONF
-#define NO_DAEMON
-#define NO_FCNTL
-#define DONT_USE_MAIN
-#endif
-
/* ************************************************************************ */
/* Standard includes */
@@ -56,16 +49,6 @@
#include <sys/types.h>
#include <fcntl.h>
-#ifdef VXWORKS
-# include <sys/times.h>
-# include <time.h>
-# include <selectLib.h>
-# include <sysLib.h>
-# include <sockLib.h>
-# include <ioLib.h>
-# include <taskLib.h>
-# include <rpc/rpc.h>
-#else /* ! VXWORKS */
#ifndef __WIN32__
# ifdef TIME_WITH_SYS_TIME
# include <sys/time.h>
@@ -78,7 +61,6 @@
# endif
# endif
#endif
-#endif /* ! VXWORKS */
#if !defined(__WIN32__)
# include <netinet/in.h>
@@ -130,10 +112,6 @@
# define ioctl(s,r,o) ioctlsocket((s),(r),(o))
#endif /* WIN32 */
-#ifdef VXWORKS
-#define sleep(n) taskDelay((n) * sysClkRateGet())
-#endif /* VXWORKS */
-
#ifdef USE_BCOPY
# define memcpy(a, b, c) bcopy((b), (a), (c))
# define memcmp(a, b, c) bcmp((a), (b), (c))
@@ -277,6 +255,12 @@ static const struct in6_addr in6addr_loopback =
#define put_int16(i, s) {((unsigned char*)(s))[0] = ((i) >> 8) & 0xff; \
((unsigned char*)(s))[1] = (i) & 0xff;}
+#define put_int32(i, s) do {((char*)(s))[0] = (char)((i) >> 24) & 0xff; \
+ ((char*)(s))[1] = (char)((i) >> 16) & 0xff; \
+ ((char*)(s))[2] = (char)((i) >> 8) & 0xff; \
+ ((char*)(s))[3] = (char)(i) & 0xff;} \
+ while (0)
+
#if defined(__GNUC__)
# define EPMD_INLINE __inline__
#elif defined(__WIN32__)
@@ -287,7 +271,7 @@ static const struct in6_addr in6addr_loopback =
/* ************************************************************************ */
-/* Stuctures used by server */
+/* Structures used by server */
typedef struct {
int fd; /* File descriptor */
@@ -307,10 +291,10 @@ struct enode {
int fd; /* The socket in use */
unsigned short port; /* Port number of Erlang node */
char symname[MAXSYMLEN+1]; /* Name of the Erlang node */
- short creation; /* Started as a random number 1..3 */
+ unsigned int cr_counter; /* Used to generate 'creation' numbers */
char nodetype; /* 77 = normal erlang node 72 = hidden (c-node */
char protocol; /* 0 = tcp/ipv4 */
- unsigned short highvsn; /* 0 = OTP-R3 erts-4.6.x, 1 = OTP-R4 erts-4.7.x*/
+ unsigned short highvsn; /* 5: creation=1..3, 6: creation=1..(2^32-1)*/
unsigned short lowvsn;
int extralen;
char extra[MAXSYMLEN+1];
diff --git a/erts/epmd/src/epmd_srv.c b/erts/epmd/src/epmd_srv.c
index 3c6f1fbdf4..b04a58bcad 100644
--- a/erts/epmd/src/epmd_srv.c
+++ b/erts/epmd/src/epmd_srv.c
@@ -38,7 +38,7 @@
* register their names with this server.
*
* To be accessible to all Erlang nodes this server listens to a well
- * known port EPMD_PORT_NO (curently port 4369) where requests
+ * known port EPMD_PORT_NO (currently port 4369) where requests
* for connections can be sent.
*
* To keep track of when registered Erlang nodes are terminated this
@@ -59,7 +59,7 @@
/* We use separate data structures for node names and connections
so that a request will not use a slot with a name that we
- want to resuse later incrementing the "creation" */
+ want to reuse later incrementing the "creation" */
/* forward declarations */
@@ -410,12 +410,11 @@ void run(EpmdVars *g)
in accept() waiting for the next request. */
#if (defined(__WIN32__) || defined(NO_FCNTL))
opt = 1;
- /* Gives warning in VxWorks */
if (ioctl(listensock[i], FIONBIO, &opt) != 0)
#else
opt = fcntl(listensock[i], F_GETFL, 0);
if (fcntl(listensock[i], F_SETFL, opt | O_NONBLOCK) == -1)
-#endif /* __WIN32__ || VXWORKS */
+#endif /* __WIN32__ */
dbg_perror(g,"failed to set non-blocking mode of listening socket %d",
listensock[i]);
@@ -525,7 +524,7 @@ void run(EpmdVars *g)
/*
* This routine read as much of the packet as possible and
- * if completed calls "do_request()" to fullfill the request.
+ * if completed calls "do_request()" to fulfill the request.
*
*/
@@ -665,6 +664,21 @@ static int do_accept(EpmdVars *g,int listensock)
return conn_open(g,msgsock);
}
+static void bump_creation(Node* node)
+{
+ if (++node->cr_counter < 4)
+ node->cr_counter = 4;
+}
+static unsigned int get_creation(Node* node)
+{
+ if (node->highvsn >= 6) {
+ return node->cr_counter; /* 4..(2^32-1)*/
+ }
+ else {
+ return node->cr_counter % 3 + 1; /* 1..3 */
+ }
+}
+
/* buf is actually one byte larger than bsize,
giving place for null termination */
static void do_request(g, fd, s, buf, bsize)
@@ -706,8 +720,10 @@ static void do_request(g, fd, s, buf, bsize)
unsigned char protocol;
unsigned short highvsn;
unsigned short lowvsn;
+ unsigned int creation;
int namelen;
int extralen;
+ int replylen;
char *name;
char *extra;
eport = get_int16(&buf[1]);
@@ -737,17 +753,22 @@ static void do_request(g, fd, s, buf, bsize)
extra = &buf[11+namelen+2];
extra[extralen]='\000';
- wbuf[0] = EPMD_ALIVE2_RESP;
- if ((node = node_reg2(g, namelen, name, fd, eport, nodetype, protocol,
- highvsn, lowvsn, extralen, extra)) == NULL) {
- wbuf[1] = 1; /* error */
- put_int16(99, wbuf+2);
- } else {
- wbuf[1] = 0; /* ok */
- put_int16(node->creation, wbuf+2);
- }
+ node = node_reg2(g, namelen, name, fd, eport, nodetype, protocol,
+ highvsn, lowvsn, extralen, extra);
+ creation = node ? get_creation(node) : 99;
+ wbuf[1] = node ? 0 : 1; /* ok | error */
+ if (highvsn >= 6) {
+ wbuf[0] = EPMD_ALIVE2_X_RESP;
+ put_int32(creation, wbuf+2);
+ replylen = 6;
+ }
+ else {
+ wbuf[0] = EPMD_ALIVE2_RESP;
+ put_int16(creation, wbuf+2);
+ replylen = 4;
+ }
- if (!reply(g, fd, wbuf, 4))
+ if (!reply(g, fd, wbuf, replylen))
{
node_unreg(g, name);
dbg_tty_printf(g,1,"** failed to send ALIVE2_RESP for \"%s\"",
@@ -1033,23 +1054,6 @@ static int conn_open(EpmdVars *g,int fd)
int i;
Connection *s;
-#ifdef VXWORKS
- /*
- * Since file descriptors are global on VxWorks, we might get an fd that
- * does not fit in the FD_SET.
- *
- * Note: This test would be harmless on Unix, but would fail on Windows
- * because socket are numbered differently and FD_SETs are implemented
- * differently.
- */
- if (fd >= FD_SETSIZE) {
- dbg_tty_printf(g,0,"file descriptor %d: too high for FD_SETSIZE=%d",
- fd,FD_SETSIZE);
- close(fd);
- return EPMD_FALSE;
- }
-#endif
-
for (i = 0; i < g->max_conn; i++) {
if (g->conn[i].open == EPMD_FALSE) {
g->active_conn++;
@@ -1200,8 +1204,8 @@ static int node_unreg(EpmdVars *g,char *name)
for (; node; prev = &node->next, node = node->next)
if (is_same_str(node->symname, name))
{
- dbg_tty_printf(g,1,"unregistering '%s:%d', port %d",
- node->symname, node->creation, node->port);
+ dbg_tty_printf(g,1,"unregistering '%s:%u', port %d",
+ node->symname, get_creation(node), node->port);
*prev = node->next; /* Link out from "reg" list */
@@ -1235,8 +1239,8 @@ static int node_unreg_sock(EpmdVars *g,int fd)
for (; node; prev = &node->next, node = node->next)
if (node->fd == fd)
{
- dbg_tty_printf(g,1,"unregistering '%s:%d', port %d",
- node->symname, node->creation, node->port);
+ dbg_tty_printf(g,1,"unregistering '%s:%u', port %d",
+ node->symname, get_creation(node), node->port);
*prev = node->next; /* Link out from "reg" list */
@@ -1264,19 +1268,8 @@ static int node_unreg_sock(EpmdVars *g,int fd)
}
/*
- * Finding a node slot and a (name,creation) name is a bit tricky.
- * We try in order
- *
- * 1. If the name was used before and we can reuse that slot but use
- * a new "creation" digit in the range 1..3.
- *
- * 2. We try to find a new unused slot.
- *
- * 3. We try to use an used slot this isn't used any longer.
- * FIXME: The criteria for *what* slot to steal should be improved.
- * Perhaps use the oldest or something.
+ * Register a new node
*/
-
static Node *node_reg2(EpmdVars *g,
int namelen,
char* name,
@@ -1346,7 +1339,7 @@ static Node *node_reg2(EpmdVars *g,
}
/* Try to find the name in the used queue so that we
- can change "creation" number 1..3 */
+ can change "creation" number */
prev = NULL;
@@ -1375,9 +1368,8 @@ static Node *node_reg2(EpmdVars *g,
g->nodes.unreg_count--;
- /* When reusing we change the "creation" number 1..3 */
-
- node->creation = node->creation % 3 + 1;
+ /* When reusing we change the "creation" number */
+ bump_creation(node);
break;
}
@@ -1404,7 +1396,8 @@ static Node *node_reg2(EpmdVars *g,
exit(1);
}
- node->creation = (current_time(g) % 3) + 1; /* "random" 1-3 */
+ node->cr_counter = current_time(g); /* "random" */
+ bump_creation(node);
}
}
@@ -1423,11 +1416,11 @@ static Node *node_reg2(EpmdVars *g,
select_fd_set(g, fd);
if (highvsn == 0) {
- dbg_tty_printf(g,1,"registering '%s:%d', port %d",
- node->symname, node->creation, node->port);
+ dbg_tty_printf(g,1,"registering '%s:%u', port %d",
+ node->symname, get_creation(node), node->port);
} else {
- dbg_tty_printf(g,1,"registering '%s:%d', port %d",
- node->symname, node->creation, node->port);
+ dbg_tty_printf(g,1,"registering '%s:%u', port %d",
+ node->symname, get_creation(node), node->port);
dbg_tty_printf(g,1,"type %d proto %d highvsn %d lowvsn %d",
nodetype, protocol, highvsn, lowvsn);
}
@@ -1561,8 +1554,8 @@ static void print_names(EpmdVars *g)
for (node = g->nodes.reg; node; node = node->next)
{
- fprintf(stderr,"***** active name \"%s#%d\" at port %d, fd = %d\r\n",
- node->symname, node->creation, node->port, node->fd);
+ fprintf(stderr,"***** active name \"%s#%u\" at port %d, fd = %d\r\n",
+ node->symname, get_creation(node), node->port, node->fd);
count ++;
}
@@ -1572,8 +1565,8 @@ static void print_names(EpmdVars *g)
for (node = g->nodes.unreg; node; node = node->next)
{
- fprintf(stderr,"***** old/unused name \"%s#%d\"\r\n",
- node->symname, node->creation);
+ fprintf(stderr,"***** old/unused name \"%s#%u\"\r\n",
+ node->symname, get_creation(node));
count ++;
}
diff --git a/erts/epmd/test/Makefile b/erts/epmd/test/Makefile
index ad1315440f..d55ccabfb2 100644
--- a/erts/epmd/test/Makefile
+++ b/erts/epmd/test/Makefile
@@ -73,7 +73,7 @@ release_spec:
release_tests_spec: opt
$(INSTALL_DIR) "$(RELEPMDDIR)"
- $(INSTALL_DATA) epmd.spec epmd.spec.vxworks $(ERL_FILES) \
+ $(INSTALL_DATA) epmd.spec $(ERL_FILES) \
$(EMAKEFILE) "$(RELEPMDDIR)"
chmod -R u+w "$(RELEPMDDIR)"
diff --git a/erts/etc/common/Makefile.in b/erts/etc/common/Makefile.in
index 3a03374fbf..4df00377b7 100644
--- a/erts/etc/common/Makefile.in
+++ b/erts/etc/common/Makefile.in
@@ -23,7 +23,6 @@ include $(ERL_TOP)/make/target.mk
ERTS_LIB_TYPEMARKER=.$(TYPE)
-USING_MINGW=@MIXED_CYGWIN_MINGW@
USING_VC=@MIXED_VC@
ifeq ($(TYPE),debug)
@@ -491,10 +490,8 @@ ifneq ($(INSTALL_OBJS),)
endif
$(INSTALL_DIR) "$(RELEASE_PATH)/erts-$(VSN)/bin"
ifneq ($(TARGET), win32)
-ifneq ($(findstring vxworks,$(TARGET)), vxworks)
$(INSTALL_SCRIPT) erl.src "$(RELEASE_PATH)/erts-$(VSN)/bin"
endif
-endif
ifneq ($(INSTALL_PROGS),)
$(INSTALL_PROGRAM) $(INSTALL_PROGS) "$(RELEASE_PATH)/erts-$(VSN)/bin"
endif
diff --git a/erts/etc/common/erlexec.c b/erts/etc/common/erlexec.c
index f18ed0c983..8f6095ef17 100644
--- a/erts/etc/common/erlexec.c
+++ b/erts/etc/common/erlexec.c
@@ -199,7 +199,7 @@ void error(char* format, ...);
static void usage_notsup(const char *switchname, const char *alt);
static char **build_args_from_env(char *env_var);
-static char **build_args_from_string(char *env_var);
+static char **build_args_from_string(char *env_var, int allow_comments);
static void initial_argv_massage(int *argc, char ***argv);
static void get_parameters(int argc, char** argv);
static void add_arg(char *new_arg);
@@ -648,11 +648,13 @@ int main(int argc, char **argv)
error("Conflicting -start_erl and -config options");
if (i+1 >= argc)
usage("-config");
- config_script_cnt++;
- config_scripts = erealloc(config_scripts,
- config_script_cnt * sizeof(char*));
- config_scripts[config_script_cnt-1] = strsave(argv[i+1]);
- i++;
+ do {
+ config_script_cnt++;
+ config_scripts = erealloc(config_scripts,
+ config_script_cnt * sizeof(char*));
+ config_scripts[config_script_cnt-1] = strsave(argv[i+1]);
+ i++;
+ } while ((i+1) < argc && argv[i+1][0] != '-' && argv[i+1][0] != '+');
}
#endif
else {
@@ -1238,7 +1240,7 @@ start_epmd(char *epmd)
if (!epmd) {
epmd = epmd_cmd;
#ifdef __WIN32__
- erts_snprintf(epmd_cmd, sizeof(epmd_cmd), "%s" DIRSEP "epmd", bindir);
+ erts_snprintf(epmd_cmd, sizeof(epmd_cmd), "\"%s" DIRSEP "epmd\"", bindir);
arg1 = "-daemon";
#else
erts_snprintf(epmd_cmd, sizeof(epmd_cmd), "\"%s" DIRSEP "epmd\" -daemon", bindir);
@@ -1657,12 +1659,12 @@ static void add_epmd_port(void)
static char **build_args_from_env(char *env_var)
{
char *value = get_env(env_var);
- char **res = build_args_from_string(value);
+ char **res = build_args_from_string(value, 0);
free_env_val(value);
return res;
}
-static char **build_args_from_string(char *string)
+static char **build_args_from_string(char *string, int allow_comments)
{
int argc = 0;
char **argv = NULL;
@@ -1671,7 +1673,7 @@ static char **build_args_from_string(char *string)
int s_alloced = 0;
int s_pos = 0;
char *p = string;
- enum {Start, Build, Build0, BuildSQuoted, BuildDQuoted, AcceptNext} state;
+ enum {Start, Build, Build0, BuildSQuoted, BuildDQuoted, AcceptNext, BuildComment} state;
#define ENSURE() \
if (s_pos >= s_alloced) { \
@@ -1703,12 +1705,24 @@ static char **build_args_from_string(char *string)
break;
case Build0:
switch (*p) {
+ case '\n':
+ case '\f':
+ case '\r':
+ case '\t':
+ case '\v':
case ' ':
++p;
break;
case '\0':
state = Start;
break;
+ case '#':
+ if (allow_comments) {
+ ++p;
+ state = BuildComment;
+ break;
+ }
+ /* fall-through */
default:
state = Build;
break;
@@ -1716,6 +1730,15 @@ static char **build_args_from_string(char *string)
break;
case Build:
switch (*p) {
+ case '#':
+ if (!allow_comments)
+ goto build_default;
+ /* fall-through */
+ case '\n':
+ case '\f':
+ case '\r':
+ case '\t':
+ case '\v':
case ' ':
case '\0':
ENSURE();
@@ -1736,6 +1759,7 @@ static char **build_args_from_string(char *string)
state = AcceptNext;
break;
default:
+ build_default:
ENSURE();
(*cur_s)[s_pos++] = *p++;
break;
@@ -1770,14 +1794,19 @@ static char **build_args_from_string(char *string)
}
break;
case AcceptNext:
- if (!*p) {
- state = Build;
- } else {
+ if (*p) {
ENSURE();
(*cur_s)[s_pos++] = *p++;
}
state = Build;
break;
+ case BuildComment:
+ if (*p == '\n' || *p == '\0') {
+ state = Build0;
+ } else {
+ p++;
+ }
+ break;
}
}
done:
@@ -1786,8 +1815,8 @@ done:
return NULL;
}
argv[argc++] = "--"; /* Add a -- separator in order
- for different from different environments
- to effect each other */
+ for flags from different environments
+ to not effect each other */
argv[argc++] = NULL; /* Sure to be large enough */
return argv;
#undef ENSURE
@@ -1802,30 +1831,16 @@ errno_string(void)
return str;
}
+#define FILE_BUFF_SIZE 1024
+
static char **
read_args_file(char *filename)
{
- int c, aix = 0, quote = 0, cmnt = 0, asize = 0;
- char **res, *astr = NULL;
FILE *file;
-
-#undef EAF_CMNT
-#undef EAF_QUOTE
-#undef SAVE_CHAR
-
-#define EAF_CMNT (1 << 8)
-#define EAF_QUOTE (1 << 9)
-#define SAVE_CHAR(C) \
- do { \
- if (!astr) \
- astr = emalloc(sizeof(char)*(asize = 20)); \
- if (aix == asize) \
- astr = erealloc(astr, sizeof(char)*(asize += 20)); \
- if (' ' != (char) (C)) \
- astr[aix++] = (char) (C); \
- else if (aix > 0 && astr[aix-1] != ' ') \
- astr[aix++] = ' '; \
- } while (0)
+ char buff[FILE_BUFF_SIZE+1];
+ size_t astr_sz = 0, sz;
+ char *astr = buff;
+ char **res;
do {
errno = 0;
@@ -1837,63 +1852,41 @@ read_args_file(char *filename)
errno_string());
}
- while (1) {
- c = getc(file);
- if (c == EOF) {
- if (ferror(file)) {
- if (errno == EINTR) {
- clearerr(file);
- continue;
- }
- usage_format("Failed to read arguments file \"%s\": %s\n",
- filename,
- errno_string());
- }
- break;
- }
+ sz = fread(astr, 1, FILE_BUFF_SIZE, file);
- switch (quote | cmnt | c) {
- case '\\':
- quote = EAF_QUOTE;
- break;
- case '#':
- cmnt = EAF_CMNT;
- break;
- case EAF_CMNT|'\n':
- cmnt = 0;
- /* Fall through... */
- case '\n':
- case '\f':
- case '\r':
- case '\t':
- case '\v':
- if (!quote)
- c = ' ';
- /* Fall through... */
- default:
- if (!cmnt)
- SAVE_CHAR(c);
- quote = 0;
- break;
- }
+ while (!feof(file) && sz == FILE_BUFF_SIZE) {
+ if (astr == buff) {
+ astr = emalloc(FILE_BUFF_SIZE*2+1);
+ astr_sz = FILE_BUFF_SIZE;
+ memcpy(astr, buff, sizeof(buff));
+ } else {
+ astr_sz += FILE_BUFF_SIZE;
+ astr = erealloc(astr,astr_sz+FILE_BUFF_SIZE+1);
+ }
+ sz = fread(astr+astr_sz, 1, FILE_BUFF_SIZE, file);
+ }
+
+ if (ferror(file)) {
+ usage_format("Failed to read arguments file \"%s\": %s\n",
+ filename,
+ errno_string());
}
- SAVE_CHAR('\0');
+ astr[astr_sz + sz] = '\0';
fclose(file);
if (astr[0] == '\0')
res = NULL;
else
- res = build_args_from_string(astr);
+ res = build_args_from_string(astr, !0);
- efree(astr);
+ if (astr != buff)
+ efree(astr);
return res;
-#undef EAF_CMNT
-#undef EAF_QUOTE
-#undef SAVE_CHAR
+#undef FILE_BUFF_SIZE
}
@@ -2077,7 +2070,7 @@ initial_argv_massage(int *argc, char ***argv)
if (av)
avv[vix++].argv = av;
- if (vix == (*argc > 1 ? 1 : 0)) {
+ if (vix == (*argc > 1 ? 2 : 0)) {
/* Only command line argv; check if we can use argv as it is... */
ac = *argc;
av = *argv;
diff --git a/erts/etc/unix/cerl.src b/erts/etc/unix/cerl.src
index c841b49303..386ed4086a 100644
--- a/erts/etc/unix/cerl.src
+++ b/erts/etc/unix/cerl.src
@@ -220,6 +220,12 @@ while [ $# -gt 0 ]; do
run_valgrind=yes
skip_erlexec=yes
;;
+ "-emu_type")
+ shift
+ cargs="$cargs -$1"
+ TYPE=.$1
+ shift
+ ;;
"-rr")
shift
cargs="$cargs -rr"
diff --git a/erts/etc/unix/etp-commands.in b/erts/etc/unix/etp-commands.in
index 4cc59cdd5f..9c56d92115 100644
--- a/erts/etc/unix/etp-commands.in
+++ b/erts/etc/unix/etp-commands.in
@@ -1187,12 +1187,14 @@ document etp-cp
%---------------------------------------------------------------------------
% etp-cp Eterm
%
-% Take a code continuation pointer and print
+% Take a code continuation pointer or instruction pointer and print
% module, function, arity and offset.
%
-% Code continuation pointers can be found in the process structure e.g
-% process_tab[i]->cp and process_tab[i]->i, the second is the
-% program counter, which is the same thing as a continuation pointer.
+% Code continuation pointers can be found on the stack. The instruction
+% pointer is found in the process struct. For example:
+%
+% c_p->i
+% process_tab[i]->i
%---------------------------------------------------------------------------
end
@@ -1509,17 +1511,13 @@ define etp-stack-preamble
set $etp_stack_end = ($arg0)->hend
if ($arg0)->state.counter & 0x8000
printf "%%%%%% WARNING: The process is currently running, so c_p->stop will not be correct\r\n"
- printf "%%%%%% Consider using %s-emu instead\r\n", $arg1
+ printf "%%%%%% Consider using -emu variant instead\r\n"
end
printf "%% Stacktrace (%u)\n", $etp_stack_end-$etp_stack_p
if ($arg0)->i != 0
printf "I: "
etp ((Eterm)($arg0)->i)
end
- if ($arg0)->cp != 0
- printf "cp:"
- etp ((Eterm)($arg0)->cp)
- end
end
define etp-stack-preamble-emu
@@ -1528,10 +1526,6 @@ define etp-stack-preamble-emu
printf "%% Stacktrace (%u)\n", $etp_stack_end-$etp_stack_p
printf "I: "
etp ((BeamInstr)I)
- if ($arg0)->cp != 0
- printf "cp: "
- etp ((Eterm)($arg0)->cp)
- end
end
define etp-stacktrace-1
@@ -2224,13 +2218,6 @@ define etp-process-info-int
else
printf "unknown\n"
end
- printf " CP: "
- if ($etp_proc->cp)
- etp-cp-1 $etp_proc->cp
- printf "\n"
- else
- printf "unknown\n"
- end
printf " I: "
if ($etp_proc->i)
etp-cp-1 $etp_proc->i
@@ -2456,11 +2443,6 @@ define etp-process-memory-info
end
end
- if ($etp_pmem_proc->cp)
- printf " CP: "
- etp-cp-1 $etp_pmem_proc->cp
- printf " "
- end
printf "\n"
end
end
diff --git a/erts/etc/unix/run_erl.c b/erts/etc/unix/run_erl.c
index bfb3e1bd2c..e974630695 100644
--- a/erts/etc/unix/run_erl.c
+++ b/erts/etc/unix/run_erl.c
@@ -1201,7 +1201,19 @@ static void error_logf(int priority, int line, const char *format, ...)
#ifdef HAVE_SYSLOG_H
if (run_daemon) {
+#ifdef HAVE_VSYSLOG
vsyslog(priority,format,args);
+#else
+ /* Some OSes like AIX lack vsyslog. */
+ va_list ap;
+ char message[900]; /* AIX man page says truncation past this */
+
+ va_start (ap, format);
+ vsnprintf(message, 900, format, ap);
+ va_end(ap);
+
+ syslog(priority, message);
+#endif
}
else
#endif
diff --git a/erts/etc/win32/cygwin_tools/reg_query.sh b/erts/etc/win32/cygwin_tools/reg_query.sh
new file mode 100644
index 0000000000..c5c2566b7a
--- /dev/null
+++ b/erts/etc/win32/cygwin_tools/reg_query.sh
@@ -0,0 +1,24 @@
+#! /bin/sh
+BAT_FILE=/tmp/w$$.bat
+if [ -z "$1" -o -z "$2" ]; then
+ echo "Usage:" "$0" '<key> <valuename>'
+ exit 1
+fi
+BACKED=`echo "$1" | sed 's,/,\\\\,g'`
+# We need to get the 64bit part of the registry, hence we need to execute
+# a 64bit reg.exe, but c:\windows\system32 is redirected to 32bit versions
+# if we are in the 32bit virtual environment, so we need to use the
+# sysnative trick to get to the 64bit executable of reg.exe (ouch!)
+if [ -d $WINDIR/sysnative ]; then
+ REG_CMD="$WINDIR\\sysnative\\reg.exe"
+else
+ REG_CMD="reg.exe"
+fi
+cat > $BAT_FILE <<EOF
+@echo off
+$REG_CMD query "$BACKED" /v "$2"
+EOF
+#echo $BAT_FILE
+#cat $BAT_FILE
+RESULT=`cmd.exe //C $BAT_FILE`
+echo $RESULT | sed "s,.*$2 REG_[^ ]* ,,"
diff --git a/erts/etc/win32/cygwin_tools/w32_path.sh b/erts/etc/win32/cygwin_tools/w32_path.sh
new file mode 100755
index 0000000000..3b67ec853a
--- /dev/null
+++ b/erts/etc/win32/cygwin_tools/w32_path.sh
@@ -0,0 +1,59 @@
+#!/bin/bash
+
+WIN32=false
+SEPARATOR=""
+ABSOLUTE=""
+UNIX=false
+done=false
+while [ $done = false ]; do
+ case "$1" in
+ -w)
+ WIN32=true;
+ SEPARATOR=backslash;
+ shift;;
+ -d)
+ WIN32=true;
+ SEPARATOR=double;
+ shift;;
+ -m)
+ WIN32=true;
+ SEPARATOR=slash;
+ shift;;
+ -u)
+ UNIX=true;
+ shift;;
+ -a)
+ ABSOLUTE="-a";
+ shift;;
+
+ *)
+ done=true;;
+ esac
+done
+
+if [ $WIN32 = false -a $UNIX = false ]; then
+ echo "Usage: $0 -m|-w|-d|-u [-a] <path>" >&2
+ exit 1;
+fi
+
+if [ -z "$1" ]; then
+ echo "Usage: $0 -m|-w|-u [-a] <path>" >&2
+ exit 1;
+fi
+
+if [ $UNIX = true ]; then
+ echo `cygpath $ABSOLUTE -u $1`
+else
+ case "$SEPARATOR" in
+ slash)
+ echo `cygpath $ABSOLUTE -m $1`;
+ ;;
+ backslash)
+ echo `cygpath $ABSOLUTE -w $1`;
+ ;;
+ double)
+ DOUBLE=`cygpath $ABSOLUTE -w $1 | sed 's,\\\\,\\\\\\\\,g'`;
+ echo $DOUBLE
+ ;;
+ esac
+fi
diff --git a/erts/etc/win32/erl.c b/erts/etc/win32/erl.c
index b230aa6a40..7cbd0d027c 100644
--- a/erts/etc/win32/erl.c
+++ b/erts/etc/win32/erl.c
@@ -53,8 +53,9 @@ int wmain(int argc, wchar_t **argv)
HANDLE erlexec_handle; /* Instance */
ErlexecFunction *win_erlexec;
wchar_t *path = malloc(100*sizeof(wchar_t));
+ wchar_t *wslpath = malloc(100*sizeof(wchar_t));
wchar_t *npath;
- int pathlen;
+ int pathlen, wslpathlen;
char ** utf8argv;
int i, len;
@@ -66,9 +67,23 @@ int wmain(int argc, wchar_t **argv)
path = realloc(path,pathlen*sizeof(wchar_t));
GetEnvironmentVariableW(L"PATH",path,pathlen);
}
- pathlen = (wcslen(path) + wcslen(erlexec_dir) + 2);
+
+ if ((wslpathlen = GetEnvironmentVariableW(L"WSLENV",wslpath,100)) > 0) {
+ if ((wslpathlen = GetEnvironmentVariableW(L"WSLPATH",wslpath,100)) > 0) {
+ if (wslpathlen > 100) {
+ wslpath = realloc(wslpath,wslpathlen*sizeof(wchar_t));
+ GetEnvironmentVariableW(L"WSLPATH",wslpath,wslpathlen);
+ }
+ wslpathlen = wcslen(wslpath);
+ }
+ }
+ pathlen = (wcslen(path) + wslpathlen + wcslen(erlexec_dir) + 2);
npath = (wchar_t *) malloc(pathlen*sizeof(wchar_t));
- swprintf(npath,pathlen,L"%s;%s",erlexec_dir,path);
+ if(wslpathlen > 0) {
+ swprintf(npath,pathlen,L"%s;%s;%s",erlexec_dir,path,wslpath);
+ } else {
+ swprintf(npath,pathlen,L"%s;%s",erlexec_dir,path);
+ }
SetEnvironmentVariableW(L"PATH",npath);
if ((erlexec_handle = LoadLibraryW(erlexec_name)) == NULL) {
diff --git a/erts/etc/win32/msys_tools/reg_query.sh b/erts/etc/win32/msys_tools/reg_query.sh
index ae6d5c3218..70a7bf0d17 100644
--- a/erts/etc/win32/msys_tools/reg_query.sh
+++ b/erts/etc/win32/msys_tools/reg_query.sh
@@ -7,7 +7,7 @@ fi
BACKED=`echo "$1" | sed 's,/,\\\\,g'`
# We need to get the 64bit part of the registry, hence we need to execute
# a 64bit reg.exe, but c:\windows\system32 is redirected to 32bit versions
-# if we ate in the 32bit virtual environment, why we need to use the
+# if we are in the 32bit virtual environment, so we need to use the
# sysnative trick to get to the 64bit executable of reg.exe (ouch!)
if [ -d $WINDIR/sysnative ]; then
REG_CMD="$WINDIR\\sysnative\\reg.exe"
diff --git a/erts/etc/win32/msys_tools/w32_path.sh b/erts/etc/win32/msys_tools/w32_path.sh
new file mode 100755
index 0000000000..9a5089391d
--- /dev/null
+++ b/erts/etc/win32/msys_tools/w32_path.sh
@@ -0,0 +1,59 @@
+#!/bin/bash
+
+WIN32=false
+SEPARATOR=""
+ABSOLUTE=""
+UNIX=false
+done=false
+while [ $done = false ]; do
+ case "$1" in
+ -w)
+ WIN32=true;
+ SEPARATOR=backslash;
+ shift;;
+ -d)
+ WIN32=true;
+ SEPARATOR=double;
+ shift;;
+ -m)
+ WIN32=true;
+ SEPARATOR=slash;
+ shift;;
+ -u)
+ UNIX=true;
+ shift;;
+ -a)
+ ABSOLUTE="-a";
+ shift;;
+
+ *)
+ done=true;;
+ esac
+done
+
+if [ $WIN32 = false -a $UNIX = false ]; then
+ echo "Usage: $0 -m|-w|-d|-u [-a] <path>" >&2
+ exit 1;
+fi
+
+if [ -z "$1" ]; then
+ echo "Usage: $0 -m|-w|-u [-a] <path>" >&2
+ exit 1;
+fi
+
+if [ $UNIX = true ]; then
+ echo `win2msys_path.sh $ABSOLUTE $1`
+else
+ case "$SEPARATOR" in
+ slash)
+ echo `msys2win_path.sh -m $ABSOLUTE -m $1`;
+ ;;
+ backslash)
+ echo `msys2win_path.sh $ABSOLUTE $1`;
+ ;;
+ double)
+ DOUBLE=`msys2win_path.sh $ABSOLUTE $1 | sed 's,\\\\,\\\\\\\\,g'`;
+ echo $DOUBLE
+ ;;
+ esac
+fi
diff --git a/erts/etc/win32/nsis/Makefile b/erts/etc/win32/nsis/Makefile
index 0b4e0d0359..4c5e5325b3 100644
--- a/erts/etc/win32/nsis/Makefile
+++ b/erts/etc/win32/nsis/Makefile
@@ -23,11 +23,11 @@ include $(ERL_TOP)/make/$(TARGET)/otp.mk
include $(ERL_TOP)/erts/vsn.mk
VERSION_HEADER = erlang.nsh
-MAKENSIS = makensis
+MAKENSIS = makensis.exe
MAKENSISFLAGS = /V2
CUSTOM_MODERN=custom_modern.exe
-# This is not the way we usually do in our makefiles,
+# This is not the way we usually do in our makefiles,
# but making release is the ONLY thing we do with this one,
# Its not called during ordinary recursive make.
all: release
@@ -44,43 +44,32 @@ TARGET_DIR = $(RELEASE_PATH)
ifdef MSYSTEM
ifeq ($(MSYSTEM),$(filter $(MSYSTEM),MSYS MINGW32 MINGW64))
- USEMSYS := true
+ MAKENSISFLAGS = //V2
endif
endif
-ifeq ($(USEMSYS),true)
-
- MAKENSISFLAGS = //V2
- WTESTROOT=$(shell (msys2win_path.sh "$(RELEASE_PATH)"))
- WTARGET_DIR=$(shell (msys2win_path.sh "$(TARGET_DIR)"))
-
-else
-
- MAKENSISFLAGS = /V2
- WTESTROOT=$(shell (cygpath -d "$(RELEASE_PATH)" 2>/dev/null || cygpath -w "$(RELEASE_PATH)"))
- WTARGET_DIR=$(shell (cygpath -d "$(TARGET_DIR)" 2>/dev/null || cygpath -d "$(TARGET_DIR)"))
-
-endif
+WTESTROOT=$(shell (w32_path.sh -d "$(RELEASE_PATH)"))
+WTARGET_DIR=$(shell (w32_path.sh -d "$(TARGET_DIR)"))
ifeq ($(CONFIG_SUBTYPE),win64)
WINTYPE=win64
+ REDIST_TARGET=vcredist_x64.exe
else
WINTYPE=win32
+ REDIST_TARGET=vcredist_x86.exe
endif
-REDIST_FILE=$(shell (sh ./find_redist.sh || echo ""))
-ifeq ($(USEMSYS),true)
- NICEREDISTFILE=$(shell (msys2win_path.sh -m "$(REDIST_FILE)" 2>/dev/null || echo ""))
-else
- NICEREDISTFILE=$(shell (cygpath -d -m "$(REDIST_FILE)" 2>/dev/null || echo ""))
-endif
-
-REDIST_TARGET=$(shell (sh ./find_redist.sh -n || echo ""))
-REDIST_DLL_VERSION=$(shell (sh ./dll_version_helper.sh $(NICEREDISTFILE) || echo ""))
-REDIST_DLL_NAME=$(shell (sh ./dll_version_helper.sh -n $(NICEREDISTFILE) || echo ""))
+REDIST_FILE=$(shell (sh ./find_redist.sh $(WINTYPE) || echo ""))
+NICEREDISTFILE=$(shell (w32_path.sh -m "$(REDIST_FILE)" 2>/dev/null || echo "NOTFOUND"))
+# $(info $$NICEREDISTFILE = [${NICEREDISTFILE}])
+REDIST_DLL_VERSION=$(shell (sh ./dll_version_helper.sh "$(NICEREDISTFILE)" || echo ""))
+REDIST_DLL_NAME=$(shell (sh ./dll_version_helper.sh -n "$(NICEREDISTFILE)" || echo ""))
+# $(info $$REDIST_DLL_VERSION = [${REDIST_DLL_VERSION}])
+# $(info $$REDIST_DLL_NAME = [${REDIST_DLL_NAME}])
+# $(info $$REDIST_FILE = [${REDIST_FILE}])
release_spec:
- @NSIS_VER=`makensis /hdrinfo | head -1 | awk '{print $$2}'`; \
+ @NSIS_VER=`makensis.exe -version`; \
case $$NSIS_VER in \
v2.0b*) \
echo '!define MUI_VERSION "$(SYSTEM_VSN)"' > $(VERSION_HEADER);\
@@ -92,7 +81,7 @@ release_spec:
echo '!define HAVE_DOCS 1' >> $(VERSION_HEADER); \
fi;\
$(MAKENSIS) erlang.nsi;;\
- v2.*) \
+ v2.* | v3.*) \
echo '!define OTP_VERSION "$(SYSTEM_VSN)"' > $(VERSION_HEADER);\
echo '!define ERTS_VERSION "$(VSN)"' >> $(VERSION_HEADER);\
echo '!define TESTROOT "$(WTESTROOT)"' >> $(VERSION_HEADER);\
@@ -104,7 +93,7 @@ release_spec:
fi;\
if [ '!' -z "$(REDIST_FILE)" -a '!' -z "$(REDIST_DLL_VERSION)" ];\
then \
- cp $(REDIST_FILE) "$(RELEASE_PATH)/$(REDIST_TARGET)";\
+ cp "$(REDIST_FILE)" "$(RELEASE_PATH)/$(REDIST_TARGET)";\
echo '!define HAVE_REDIST_FILE 1' >> $(VERSION_HEADER); \
echo '!define REDIST_DLL_VERSION "$(REDIST_DLL_VERSION)"' >> $(VERSION_HEADER);\
echo '!define REDIST_DLL_NAME "$(REDIST_DLL_NAME)"' >> $(VERSION_HEADER);\
@@ -117,7 +106,7 @@ release_spec:
echo "Running $(MAKENSIS) $(MAKENSISFLAGS) erlang20.nsi";\
$(MAKENSIS) $(MAKENSISFLAGS) erlang20.nsi;;\
*) \
- echo 'Unsupported NSIS version';;\
+ echo "Unsupported NSIS version: $$NSIS_VER";;\
esac
release_docs release_docs_spec docs:
diff --git a/erts/etc/win32/nsis/dll_version_helper.sh b/erts/etc/win32/nsis/dll_version_helper.sh
index 9eafb6ce0e..1b295aa89a 100755
--- a/erts/etc/win32/nsis/dll_version_helper.sh
+++ b/erts/etc/win32/nsis/dll_version_helper.sh
@@ -44,12 +44,16 @@ int main(void)
}
EOF
-cl -MD hello.c > /dev/null 2>&1
+cl.exe -MD hello.c > /dev/null 2>&1
if [ '!' -f hello.exe.manifest ]; then
# Gah - VC 2010 changes the way it handles DLL's and manifests... Again...
# need another way of getting the version
DLLNAME=`dumpbin.exe -imports hello.exe | egrep MSVCR.*dll`
DLLNAME=`echo $DLLNAME`
+ if [ -z "$DLLNAME" ]; then
+ DLLNAME=`dumpbin.exe -imports hello.exe | egrep VCRUNTIME.*dll`
+ DLLNAME=`echo $DLLNAME`
+ fi
if [ '!' -z "$1" ]; then
FILETOLOOKIN=$1
else
@@ -92,22 +96,25 @@ int main(void)
for(i=0; i < n; ++i) {
sprintf(buff,"\\\\StringFileInfo\\\\%04x%04x\\\\FileVersion",
translate[i*2],translate[i*2+1]);
- if (VerQueryValue(versinfo,buff,&vs_verinfo,&vs_ver_size)) {
- printf("%s\n",(char *) vs_verinfo);
- return 0;
+ if (VerQueryValue(versinfo,buff,&vs_verinfo,&vs_ver_size) && vs_ver_size > 2) {
+ if(vs_verinfo[1] == 0) // Wide char (depends on compiler version!!)
+ printf("%S\n",(unsigned short *) vs_verinfo);
+ else
+ printf("%s\n",(char *) vs_verinfo);
+ return 0;
}
}
fprintf(stderr,"Failed to find file version of %s\n",REQ_MODULE);
return 0;
}
EOF
- cl -MD helper.c version.lib > /dev/null 2>&1
+ cl.exe -MD helper.c version.lib > /dev/null 2>&1
if [ '!' -f helper.exe ]; then
echo "Failed to build helper program." >&2
exit 1
fi
NAME=$DLLNAME
- VERSION=`./helper`
+ VERSION=`./helper.exe`
else
VERSION=`grep '<assemblyIdentity' hello.exe.manifest | sed 's,.*version=.\([0-9\.]*\).*,\1,g' | grep -v '<'`
NAME=`grep '<assemblyIdentity' hello.exe.manifest | sed 's,.*name=.[A-Za-z\.]*\([0-9]*\).*,msvcr\1.dll,g' | grep -v '<'`
diff --git a/erts/etc/win32/nsis/find_redist.sh b/erts/etc/win32/nsis/find_redist.sh
index c070ad469a..deb5480e1a 100755
--- a/erts/etc/win32/nsis/find_redist.sh
+++ b/erts/etc/win32/nsis/find_redist.sh
@@ -26,17 +26,9 @@ lookup_prog_in_path ()
save_ifs=$IFS
IFS=:
for p in $PATH; do
- # In cygwin the programs are not always executable and have .exe suffix...
- if [ "X$TARGET" = "Xwin32" ]; then
- if [ -f $p/$PROG.exe ]; then
- echo $p/$PROG
- break;
- fi
- else
- if [ -x $p/$PROG ]; then
- echo $p/$PROG
- break;
- fi
+ if [ -f $p/$PROG.exe ]; then
+ echo $p/$PROG
+ break;
fi
done
IFS=$save_ifs
@@ -89,36 +81,38 @@ add_path_element()
echo "$PA"
}
-
-CLPATH=`lookup_prog_in_path cl`
-
-if [ -z "$CLPATH" ]; then
- echo "Can not locate cl.exe and vcredist_x86/x64.exe - OK if using mingw" >&2
- exit 1
-fi
-
# Look to see if it's 64bit
-XX=`remove_path_element cl "$CLPATH"`
-YY=`remove_path_element amd64 "$XX"`
-if [ "$YY" != "$XX" ]; then
+if [ "$1" = "win64" ]; then
AMD64DIR=true
VCREDIST=vcredist_x64
COMPONENTS="cl amd64 bin vc"
-else
+elif [ "$1" = "win32" ]; then
AMD64DIR=false
VCREDIST=vcredist_x86
COMPONENTS="cl bin vc"
+else
+ echo "TARGET argument should win32 or win64"
+ exit 2
fi
-if [ X"$1" = X"-n" ]; then
- echo $VCREDIST.exe
- exit 0
+if [ x"$VCToolsRedistDir" != x"" ]; then
+ File="$VCToolsRedistDir/$VCREDIST.exe"
+ if [ -r "$File" ]; then
+ echo "$File"
+ exit 0
+ fi
+fi
+
+CLPATH=`lookup_prog_in_path cl`
+if [ -z "$CLPATH" ]; then
+ echo "Can not locate cl.exe and vcredist_x86/x64.exe - OK if using mingw" >&2
+ exit 1
fi
-# echo $CLPATH
+echo $CLPATH
BPATH=$CLPATH
for x in $COMPONENTS; do
- # echo $x
+ #echo $x
NBPATH=`remove_path_element $x "$BPATH"`
if [ "$NBPATH" = "$BPATH" ]; then
echo "Failed to locate $VCREDIST.exe because cl.exe was in an unexpected location" >&2
diff --git a/erts/etc/win32/wsl_tools/SetupWSLcross.bat b/erts/etc/win32/wsl_tools/SetupWSLcross.bat
new file mode 100644
index 0000000000..860a7d5e60
--- /dev/null
+++ b/erts/etc/win32/wsl_tools/SetupWSLcross.bat
@@ -0,0 +1,66 @@
+@echo off
+rem Setup MCL and echo the environment
+rem Usage: eval `cmd.exe /c SetupWSLcross.bat x64`
+
+IF "%~1"=="x86" GOTO search
+IF "%~1"=="x64" GOTO search
+
+GOTO badarg
+
+:search
+IF EXIST "C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\vcvarsall.bat". (
+ call "C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\vcvarsall.bat" %~1 > nul
+ goto continue
+)
+
+IF EXIST "C:\Program Files (x86)\Microsoft Visual Studio\2019\BuildTools\VC\Auxiliary\Build\vcvarsall.bat". (
+ call "C:\Program Files (x86)\Microsoft Visual Studio\2019\BuildTools\VC\Auxiliary\Build\vcvarsall.bat" %~1 > nul
+ goto continue
+)
+
+IF EXIST "C:\Program Files (x86)\Microsoft Visual Studio\2019\Professional\VC\Auxiliary\Build\vcvarsall.bat". (
+ call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Professional\VC\Auxiliary\Build\vcvarsall.bat" %~1 > nul
+ goto continue
+)
+
+IF EXIST "C:\Program Files (x86)\Microsoft Visual Studio\2017\BuildTools\VC\Auxiliary\Build\vcvarsall.bat". (
+ call "C:\Program Files (x86)\Microsoft Visual Studio\2017\BuildTools\VC\Auxiliary\Build\vcvarsall.bat" %~1 > nul
+ goto continue
+)
+
+IF EXIST "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat". (
+ call "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat" %~1 > nul
+ goto continue
+)
+
+GOTO no_vcvars
+
+:continue
+
+FOR /F "delims==" %%F IN ('where cl.exe') DO SET _cl_exec_=%%F
+FOR %%F IN ("%_cl_exec_%") DO SET CL_PATH=%%~dpF
+
+FOR /F "delims==" %%F IN ('where rc.exe') DO SET _rc_exec_=%%F
+FOR %%F IN ("%_rc_exec_%") DO SET RC_PATH=%%~dpF
+
+rem Order is important for some unknown reason
+set WSLENV=VCToolsRedistDir/up:CL_PATH/up:RC_PATH/up:LIBPATH/ul:LIB/ul:INCLUDE/ul
+wsl.exe echo INCLUDE=\"$INCLUDE\";
+wsl.exe echo LIB=\"$LIB\";
+wsl.exe echo LIBPATH=\"$LIBPATH\";
+wsl.exe echo VCToolsRedistDir=\"$VCToolsRedistDir\";
+wsl.exe echo PATH=\"$CL_PATH\":\"$RC_PATH\":'$PATH';
+wsl.exe echo WSLENV='$WSLENV:LIBPATH/l:LIB/l:INCLUDE/l';
+rem wsl.exe echo export 'INCLUDE LIB LIBPATH VCToolsRedistDir WSLENV PATH';
+wsl.exe echo "# Eval this file eval \`cmd.exe /c SetupWSLcross.bat\`"
+
+exit
+
+:badarg
+echo "Bad TARGET or not specified: %~1 expected x86 or x64"
+exit
+
+:no_vcvars
+echo "Error: SetupWSLcross.bat: Could not find vcvarsall.bat"
+echo " edit erts/etc/win32/wsl_tools/SetupWSLcross.bat"
+exit
diff --git a/erts/etc/win32/wsl_tools/erl b/erts/etc/win32/wsl_tools/erl
new file mode 100755
index 0000000000..db24f6b4fe
--- /dev/null
+++ b/erts/etc/win32/wsl_tools/erl
@@ -0,0 +1,45 @@
+#! /bin/sh
+#
+# %CopyrightBegin%
+#
+# Copyright Ericsson AB 2002-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%
+#
+# Note! This shellscript expects to be run in a wsl environment,
+# it converts erlc command lines to native windows erlc commands, which
+# basically means running the command wslpath on whatever is a path...
+
+CMD=""
+for x in "$@"; do
+ case "$x" in
+ -I/*|-o/*)
+ y=`echo $x | sed 's,^-[Io]\(/.*\),\1,g'`;
+ z=`echo $x | sed 's,^-\([Io]\)\(/.*\),\1,g'`;
+ MPATH=`wslpath -m $y`;
+ CMD="$CMD -$z\"$MPATH\"";;
+ /*)
+ MPATH=`wslpath -m $x`;
+ CMD="$CMD \"$MPATH\"";;
+ *)
+ y=`echo $x | sed 's,",\\\",g'`;
+ CMD="$CMD \"$y\"";;
+ esac
+done
+ERL_TOP=`wslpath -m $ERL_TOP`
+WSLENV="ERL_TOP/w:$WSLENV"
+export WSLENV
+export ERL_TOP
+eval erl.exe $CMD
diff --git a/erts/etc/win32/wsl_tools/erlc b/erts/etc/win32/wsl_tools/erlc
new file mode 100755
index 0000000000..956ac19abd
--- /dev/null
+++ b/erts/etc/win32/wsl_tools/erlc
@@ -0,0 +1,60 @@
+#! /bin/sh
+#
+# %CopyrightBegin%
+#
+# Copyright Ericsson AB 2002-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%
+#
+
+
+CMD=""
+ECHO_ONLY=false
+for x in "$@"; do
+ case "$x" in
+ --echo_only)
+ ECHO_ONLY=true;;
+ -I/*|-o/*)
+ y=`echo $x | sed 's,^-[Io]\(/.*\),\1,g'`;
+ z=`echo $x | sed 's,^-\([Io]\)\(/.*\),\1,g'`;
+ MPATH=`wslpath -m $y`;
+ CMD="$CMD -$z$MPATH";;
+ -pa/*)
+ y=`echo $x | sed 's,^-pa\(/.*\),\1,g'`;
+ MPATH=`wslpath -m $y`;
+ CMD="$CMD -pa $MPATH";;
+ /*)
+ MPATH=`wslpath -m $x`;
+ CMD="$CMD \"$MPATH\"";;
+# Needed for +'{preproc_flags,whatever}'
+ +{preproc_flags,*})
+ y=`echo $x | sed 's,^+{preproc_flags\,"\(.*\)"},\1,g'`;
+ z=`eval $0 --echo_only $y`;
+ case "$z" in # Dont "doubledoublequote"
+ \"*\")
+ CMD="$CMD +'{preproc_flags,$z}'";;
+ *)
+ CMD="$CMD +'{preproc_flags,\"$z\"}'";;
+ esac;;
+ *)
+ y=`echo $x | sed 's,",\\\",g'`;
+ CMD="$CMD \"$y\"";;
+ esac
+done
+if [ $ECHO_ONLY = true ]; then
+ echo $CMD
+else
+ eval erlc.exe $CMD
+fi
diff --git a/erts/etc/win32/wsl_tools/javac.sh b/erts/etc/win32/wsl_tools/javac.sh
new file mode 100755
index 0000000000..b1f142adfd
--- /dev/null
+++ b/erts/etc/win32/wsl_tools/javac.sh
@@ -0,0 +1,53 @@
+#! /bin/sh
+#
+# %CopyrightBegin%
+#
+# Copyright Ericsson AB 2002-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%
+#
+# Note! This shellscript expects to be run in a WSL environment,
+# it converts erlc command lines to native windows erlc commands, which
+# basically means running the command wslpath on whatever is a path...
+
+CMD=""
+
+SAVE="$@"
+while test -n "$1" ; do
+ x="$1"
+ case "$x" in
+ -I/*|-o/*|-d/*)
+ y=`echo $x | sed 's,^-[Iod]\(/.*\),\1,g'`;
+ z=`echo $x | sed 's,^-\([Iod]\)\(/.*\),\1,g'`;
+ #echo "Foooo:$z"
+ MPATH=`wslpath -m $y`;
+ CMD="$CMD -$z\"$MPATH\"";;
+ -d|-I|-o)
+ shift;
+ MPATH=`wslpath -m $1`;
+ CMD="$CMD $x $MPATH";;
+ /*)
+ #echo "absolute:"$x;
+ MPATH=`wslpath -m $x`;
+ CMD="$CMD \"$MPATH\"";;
+ *)
+ y=`echo $x | sed 's,",\\\",g'`;
+ CMD="$CMD \"$y\"";;
+ esac
+ shift
+done
+#echo javac.exe "$CMD"
+export WSLENV=CLASSPATH/l
+eval javac.exe "$CMD"
diff --git a/erts/etc/win32/wsl_tools/make_bootstrap_ini.sh b/erts/etc/win32/wsl_tools/make_bootstrap_ini.sh
new file mode 100755
index 0000000000..c33d328ea0
--- /dev/null
+++ b/erts/etc/win32/wsl_tools/make_bootstrap_ini.sh
@@ -0,0 +1,43 @@
+#! /bin/bash
+#
+# %CopyrightBegin%
+#
+# Copyright Ericsson AB 2003-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%
+#
+# Create a local init-file for erlang in the build environment.
+if [ -z "$1" ]; then
+ echo "error: $0: No rootdir given"
+ exit 1
+else
+ RDIR=$1
+fi
+if [ -z "$2" ]; then
+ echo "error: $0: No bindir given"
+ exit 1
+else
+ BDIR=$2
+fi
+
+DRDIR=`w32_path.sh -d $RDIR`
+DBDIR=`w32_path.sh -d $BDIR`
+
+cat > $RDIR/bin/erl.ini <<EOF
+[erlang]
+Bindir=$DBDIR
+Progname=erl
+Rootdir=$DRDIR
+EOF
diff --git a/erts/doc/Makefile b/erts/etc/win32/wsl_tools/make_local_ini.sh
index f26a43592e..85ec4e40aa 100644..100755
--- a/erts/doc/Makefile
+++ b/erts/etc/win32/wsl_tools/make_local_ini.sh
@@ -1,8 +1,9 @@
-#
+#! /bin/bash
+#
# %CopyrightBegin%
-#
-# Copyright Ericsson AB 1996-2016. All Rights Reserved.
-#
+#
+# Copyright Ericsson AB 2003-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
@@ -14,24 +15,26 @@
# 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%
-#
-
-#
-# Default Rules
#
-OTP_MAKE_ROOT=/home/super/otp/otp_make
-include $(OTP_MAKE_ROOT)/otp.mk
-
-#
-# Macros
+# %CopyrightEnd%
#
-SUB_DIRECTORIES = src
+# Create a local init-file for erlang in the build environment.
+if [ -z "$1" ]; then
+ if [ -z $ERL_TOP ]; then
+ echo "error: $0: No rootdir available"
+ exit 1
+ else
+ RDIR=$ERL_TOP
+ fi
+else
+ RDIR=$1
+fi
-SPECIAL_TARGETS =
+DDIR=`w32_path.sh -d $RDIR`
-#
-# Default Subdir Targets
-#
-include $(OTP_MAKE_ROOT)/otp_subdir.mk
+cat > $RDIR/bin/erl.ini <<EOF
+[erlang]
+Bindir=$DDIR\\\\bin\\\\win32
+Progname=erl
+Rootdir=$DDIR
+EOF
diff --git a/erts/etc/win32/wsl_tools/reg_query.sh b/erts/etc/win32/wsl_tools/reg_query.sh
new file mode 100755
index 0000000000..c05f00dfa1
--- /dev/null
+++ b/erts/etc/win32/wsl_tools/reg_query.sh
@@ -0,0 +1,19 @@
+#! /bin/sh
+
+mkdir -p $ERL_TOP/tmp
+BAT_FILE=$ERL_TOP/tmp/w$$.bat
+if [ -z "$1" -o -z "$2" ]; then
+ echo "Usage:" "$0" '<key> <valuename>'
+ exit 1
+fi
+BACKED=`echo "$1" | sed 's,/,\\\\,g'`
+
+if [ $CONFIG_SUBTYPE = "win64" ]; then
+ REG_OPT=" /reg:64"
+else
+ REG_OPT=" /reg:32"
+fi
+
+WIN_BAT_FILE=`w32_path.sh -w $BAT_FILE`
+RESULT=`reg.exe query "$BACKED" /v "$2" $REG_OPT | sed 's@\\\@/@g' | tr -d '\r\n'`
+echo "$RESULT" | sed "s,.*REG_[^ ]* *,,g"
diff --git a/erts/etc/win32/wsl_tools/vc/ar.sh b/erts/etc/win32/wsl_tools/vc/ar.sh
new file mode 100755
index 0000000000..4d3b8ffdb5
--- /dev/null
+++ b/erts/etc/win32/wsl_tools/vc/ar.sh
@@ -0,0 +1,48 @@
+#! /bin/sh
+#
+# %CopyrightBegin%
+#
+# Copyright Ericsson AB 2002-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%
+#
+CMD=""
+while test -n "$1" ; do
+ x="$1"
+ case "$x" in
+ -out:)
+ shift
+ case "$1" in
+ /*)
+ MPATH=`w32_path.sh -d $1`;;
+ *)
+ MPATH=$1;;
+ esac
+ CMD="$CMD -out:\"$MPATH\"";;
+ -out:/*)
+ y=`echo $x | sed 's,^-out:\(/.*\),\1,g'`;
+ MPATH=`w32_path.sh -d $y`;
+ CMD="$CMD -out:\"$MPATH\"";;
+ /*)
+ MPATH=`w32_path.sh -d $x`;
+ CMD="$CMD \"$MPATH\"";;
+ *)
+ y=`echo $x | sed 's,",\\\",g'`;
+ CMD="$CMD \"$y\"";;
+ esac
+ shift
+done
+
+eval lib.exe /nologo $CMD
diff --git a/erts/etc/win32/wsl_tools/vc/cc.sh b/erts/etc/win32/wsl_tools/vc/cc.sh
new file mode 100755
index 0000000000..036e00681c
--- /dev/null
+++ b/erts/etc/win32/wsl_tools/vc/cc.sh
@@ -0,0 +1,382 @@
+#! /bin/sh
+#
+# %CopyrightBegin%
+#
+# Copyright Ericsson AB 2002-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%
+#
+# Icky cl wrapper that does it's best to behave like a Unixish cc.
+# Made to work for Erlang builds and to make configure happy, not really
+# general I suspect.
+# set -x
+# Save the command line for debug outputs
+
+SAVE="$@"
+
+# Constants
+COMMON_CFLAGS="-nologo -D__WIN32__ -DWIN32 -DWINDOWS -D_WIN32 -DNT -D_CRT_SECURE_NO_DEPRECATE"
+
+# Variables
+# The stdout and stderr for the compiler
+MSG_FILE=/tmp/cl.exe.$$.1
+ERR_FILE=/tmp/cl.exe.$$.2
+
+# "Booleans" determined during "command line parsing"
+# If the stdlib option is explicitly passed to this program
+MD_FORCED=false
+# If we're preprocession (only) i.e. -E
+PREPROCESSING=false
+# If we're generating dependencies (implies preprocesing)
+DEPENDENCIES=false
+# If this is supposed to be a debug build
+DEBUG_BUILD=false
+# If this is supposed to be an optimized build (there can only be one...)
+OPTIMIZED_BUILD=false
+# If we're linking or only compiling
+LINKING=true
+
+# This data is accumulated during command line "parsing"
+# The stdlibrary option, default multithreaded dynamic
+MD=-MD
+# Flags for debug compilation
+DEBUG_FLAGS=""
+# Flags for optimization
+OPTIMIZE_FLAGS=""
+# The specified output filename (if any), may be either object or exe.
+OUTFILE=""
+# Unspecified command line options for the compiler
+CMD=""
+# All the c source files, in unix style
+SOURCES=""
+# All the options to pass to the linker, kept in Unix style
+LINKCMD=""
+
+
+# Loop through the parameters and set the above variables accordingly
+# Also convert some filenames to "windows style"
+# except for anything passed to the linker, that script
+# handles those and the sources, which are also kept unixish for now
+
+# If we are in "unix" directory we can't use relative paths
+# since cl.exe can't find that path
+WINCHECK=`w32_path.sh -m $PWD`
+case $WINCHECK in
+ //wsl$/*)
+ USEABSPATH=true
+ ;;
+ *)
+ USEABSPATH=false
+ ;;
+esac
+
+while test -n "$1" ; do
+ x="$1"
+ case "$x" in
+ -Wall)
+ ;;
+ -c)
+ LINKING=false;;
+ #CMD="$CMD -c";;
+ -MM)
+ PREPROCESSING=true;
+ LINKING=false;
+ DEPENDENCIES=true;;
+ -E)
+ PREPROCESSING=true;
+ LINKING=false;; # Obviously...
+ #CMD="$CMD -E";;
+ -Owx)
+ # Optimization hardcoded of wxErlang
+ OPTIMIZE_FLAGS="-Ob2ity -Gs -Z7";
+ DEBUG_FLAGS="";
+ DEBUG_BUILD=false;
+ if [ $MD_FORCED = false ]; then
+ MD=-MD;
+ fi
+ OPTIMIZED_BUILD=true;;
+ -O*)
+ # Optimization hardcoded
+ OPTIMIZE_FLAGS="-Ox -Z7";
+ DEBUG_FLAGS="";
+ DEBUG_BUILD=false;
+ if [ $MD_FORCED = false ]; then
+ MD=-MD;
+ fi
+ OPTIMIZED_BUILD=true;;
+ -g|-ggdb)
+ if [ $OPTIMIZED_BUILD = false ];then
+ # Hardcoded windows debug flags
+ DEBUG_FLAGS="-Z7";
+ if [ $MD_FORCED = false ]; then
+ MD=-MDd;
+ fi
+ LINKCMD="$LINKCMD -g";
+ DEBUG_BUILD=true;
+ fi;;
+ # Allow forcing of stdlib
+ -mt|-MT)
+ MD="-MT";
+ MD_FORCED=true;;
+ -md|-MD)
+ MD="-MD";
+ MD_FORCED=true;;
+ -ml|-ML)
+ MD="-ML";
+ MD_FORCED=true;;
+ -mdd|-MDD|-MDd)
+ MD="-MDd";
+ MD_FORCED=true;;
+ -mtd|-MTD|-MTd)
+ MD="-MTd";
+ MD_FORCED=true;;
+ -mld|-MLD|-MLd)
+ MD="-MLd";
+ MD_FORCED=true;;
+ -o)
+ shift;
+ OUTFILE="$1";;
+ -o*)
+ y=`echo $x | sed 's,^-[Io]\(.*\),\1,g'`;
+ OUTFILE="$y";;
+ -I/*)
+ y=`echo $x | sed 's,^-[Io]\(/.*\),\1,g'`;
+ z=`echo $x | sed 's,^-\([Io]\)\(/.*\),\1,g'`;
+ MPATH=`w32_path.sh -d "$y"`;
+ CMD="$CMD -$z\"$MPATH\"";;
+ -I\"/*)
+ y=`echo $x | sed 's,^\"-[Io]\(/.*\)\",\1,g'`;
+ z=`echo $x | sed 's,^\"-\([Io]\)\(/.*\)\",\1,g'`;
+ MPATH=`w32_path.sh -d "$y"`;
+ CMD="$CMD -$z\"$MPATH\"";;
+ -I*)
+ if [ $USEABSPATH = true ]; then
+ y=`echo $x | sed 's,^-[Io]\(.*\),\1,g'`;
+ z=`echo $x | sed 's,^-\([Io]\)\(.*\),\1,g'`;
+ MPATH=`w32_path.sh -a -d "$y"`;
+ CMD="$CMD -$z$MPATH";
+ else
+ y=`echo $x | sed 's,",\\\",g'`;
+ CMD="$CMD $y"
+ fi;;
+ -D*)
+ y=`echo $x | sed 's,",\\\",g'`;
+ CMD="$CMD $y";;
+ -EH*)
+ y=`echo $x | sed 's,",\\\",g'`;
+ CMD="$CMD $y";;
+ -TP|-Tp)
+ y=`echo $x | sed 's,",\\\",g'`;
+ CMD="$CMD $y";;
+ -l*)
+ y=`echo $x | sed 's,^-l\(.*\),\1,g'`;
+ LINKCMD="$LINKCMD $x";;
+ /*.c)
+ SOURCES="$SOURCES $x";;
+ *.c)
+ SOURCES="$SOURCES $x";;
+ /*.cc)
+ SOURCES="$SOURCES $x";;
+ *.cc)
+ SOURCES="$SOURCES $x";;
+ /*.cpp)
+ SOURCES="$SOURCES $x";;
+ *.cpp)
+ SOURCES="$SOURCES $x";;
+ /*.o)
+ LINKCMD="$LINKCMD $x";;
+ *.o)
+ LINKCMD="$LINKCMD $x";;
+ *)
+ # Try to quote uninterpreted options
+ y=`echo $x | sed 's,",\\\",g'`;
+ LINKCMD="$LINKCMD $y";;
+ esac
+ shift
+done
+
+#Return code from compiler, linker.sh and finally this script...
+RES=0
+
+# Accumulated object names
+ACCUM_OBJECTS=""
+
+# A temporary object file location
+TMPOBJDIR=$ERL_TOP/tmpobj$$
+mkdir $TMPOBJDIR
+
+WINTMPDIR=`w32_path.sh -w $TMPOBJDIR`
+
+# Sometimes the file server doesn't keep up (paralell file creation)
+while true ; do
+ DIR_EXISTS=$(cd /mnt/c; cmd.exe /C "IF EXIST $WINTMPDIR (echo yes) ELSE (echo no)")
+ case $DIR_EXISTS in # Contains trash in the end of string
+ yes*)
+ break
+ ;;
+ *)
+ if [ "X$CC_SH_DEBUG_LOG" != "X" ]; then
+ echo "sleep 1" >> $CC_SH_DEBUG_LOG
+ fi;
+ echo sleep $WINTMPDIR does not exist >&2
+ sleep 1
+ esac
+done
+
+# Compile
+for x in $SOURCES; do
+ # Compile each source
+ if [ $LINKING = false ]; then
+ # We should have an output defined, which is a directory
+ # or an object file
+ case $OUTFILE in
+ /*.o)
+ # Simple output, SOURCES should be one single
+ n=`echo $SOURCES | wc -w`;
+ if [ $n -gt 1 ]; then
+ echo "cc.sh:Error, multiple sources, one object output.";
+ exit 1;
+ else
+ output_filename=`echo $OUTFILE`;
+ fi;;
+ *.o)
+ # Relative path needs no translation
+ n=`echo $SOURCES | wc -w`
+ if [ $n -gt 1 ]; then
+ echo "cc.sh:Error, multiple sources, one object output."
+ exit 1
+ else
+ output_filename=$OUTFILE
+ fi;;
+ /*)
+ # Absolute directory
+ o=`echo $x | sed 's,.*/,,' | sed 's,\.c$,.o,'`
+ output_filename=`echo $OUTFILE`
+ output_filename="$output_filename/${o}";;
+ *)
+ # Relative_directory or empty string (.//x.o is valid)
+ o=`echo $x | sed 's,.*/,,' | sed 's,\.cp*$,.o,'`
+ output_filename="./${OUTFILE}/${o}";;
+ esac
+ else
+ # We are linking, which means we build objects in a temporary
+ # directory and link from there. We should retain the basename
+ # of each source to make examining the exe easier...
+ o=`echo $x | sed 's,.*/,,' | sed 's,\.c$,.o,'`
+ output_filename=$TMPOBJDIR/$o
+ ACCUM_OBJECTS="$ACCUM_OBJECTS $output_filename"
+ fi
+ # Now we know enough, lets try a compilation...
+ if [ $USEABSPATH = true ]; then
+ MPATH=`w32_path.sh -a -d $x`
+ else
+ MPATH=`w32_path.sh -d $x`
+ fi
+ if [ $PREPROCESSING = true ]; then
+ output_flag="-E"
+ else
+ output_flag="/FS -c -Fo`w32_path.sh -a -d ${output_filename}`"
+ fi
+ params="$COMMON_CFLAGS $MD $DEBUG_FLAGS $OPTIMIZE_FLAGS \
+ $CMD ${output_flag} $MPATH"
+ if [ "X$CC_SH_DEBUG_LOG" != "X" ]; then
+ echo cc.sh "$SAVE" >>$CC_SH_DEBUG_LOG
+ echo cl.exe $params >>$CC_SH_DEBUG_LOG
+ fi
+ eval cl.exe $params >$MSG_FILE 2>$ERR_FILE
+ RES=$?
+ if test $PREPROCESSING = false; then
+ cat $ERR_FILE >&2
+ tail -n +2 $MSG_FILE
+ else
+ tail -n +2 $ERR_FILE >&2
+ if test $DEPENDENCIES = true; then
+ perl -e '
+my $file = "'$x'";
+while (<>) {
+ next unless /^#line/;
+ next if /$file/o;
+ (undef,$_) = split(/\"/);
+ next if / /;
+ $all{$_} = 1;
+}
+foreach (sort keys %all) {
+ my $w_file;
+ ($w_file) = split("\n",`(w32_path.sh -u $_)`);
+ push @f, "\\\n $w_file ";
+}
+if (@f) {
+ my $oname = $file;
+ $oname =~ s@.*/@@;
+ $oname =~ s@[.]cp*@.o@;
+ print $oname, ":", @f;
+ print "\n\n";
+ print STDERR "Made dependencies for $file\n";
+}' $MSG_FILE
+ else
+ cat $MSG_FILE
+ fi
+ fi
+ rm -f $ERR_FILE $MSG_FILE
+ if [ $RES != 0 ]; then
+ echo Failed: cl.exe $params
+ rm -rf $TMPOBJDIR
+ exit $RES
+ fi
+done
+
+# If we got here, we succeeded in compiling (if there were anything to compile)
+# The output filename should name an executable if we're linking
+if [ $LINKING = true ]; then
+ case $OUTFILE in
+ "")
+ # Use the first source name to name the executable
+ first_source=""
+ for x in $SOURCES; do first_source=$x; break; done;
+ if [ -n "$first_source" ]; then
+ e=`echo $x | sed 's,.*/,,' | sed 's,\.c$,.exe,'`;
+ out_spec="-o $e";
+ else
+ out_spec="";
+ fi;;
+ *)
+ out_spec="-o $OUTFILE";;
+ esac
+ # Descide which standard library to link against
+ case $MD in
+ -ML)
+ stdlib="-lLIBC";;
+ -MLd)
+ stdlib="-lLIBCD";;
+ -MD)
+ stdlib="-lMSVCRT";;
+ -MDd)
+ stdlib="-lMSVCRTD";;
+ -MT)
+ stdlib="-lLIBCMT";;
+ -MTd)
+ stdlib="-lLIBMTD";;
+ esac
+ # And finally call the next script to do the linking...
+ params="$out_spec $LINKCMD $stdlib"
+ if [ "X$CC_SH_DEBUG_LOG" != "X" ]; then
+ echo ld.sh $ACCUM_OBJECTS $params >>$CC_SH_DEBUG_LOG
+ fi
+ eval ld.sh $ACCUM_OBJECTS $params
+ RES=$?
+fi
+rm -rf $TMPOBJDIR
+
+exit $RES
diff --git a/erts/etc/win32/wsl_tools/vc/coffix.c b/erts/etc/win32/wsl_tools/vc/coffix.c
new file mode 100755
index 0000000000..7428f9cd41
--- /dev/null
+++ b/erts/etc/win32/wsl_tools/vc/coffix.c
@@ -0,0 +1,161 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB 1999-2016. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * %CopyrightEnd%
+ */
+/*
+** This mini tool fixes an incompatibility between
+** Microsoft's tools, who dont like the virtual size being put in
+** the physical address field, but rely on the raw size field for
+** sizing the ".bss" section.
+** This fixes some of the problems with linking gcc compiled objects
+** together with MSVC dito.
+**
+** Courtesy DJ Delorie for describing the COFF file format on
+** http://www.delorie.com/djgpp/doc/coff/
+** The coff structures are fetched from Microsofts headers though.
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+
+#include <windows.h>
+#include <winnt.h> /* Structure definitions for PE (COFF) */
+
+static int dump_edit(char *filename, int edit);
+static int v_printf(char *format, ...);
+
+
+char *progname;
+int verbouse = 0;
+
+int main(int argc, char **argv)
+{
+ int findex = 1;
+ int edit = 0;
+ int ret;
+
+ progname = argv[0];
+ if (argc == 1) {
+ fprintf(stderr,"Format : %s [-e] [-v] <filename>\n", progname);
+ return 1;
+ }
+ for (findex = 1;
+ findex < argc && (*argv[findex] == '-' || *argv[findex] == '/');
+ ++findex)
+ switch (argv[findex][1]) {
+ case 'e':
+ case 'E':
+ edit = 1;
+ break;
+ case 'v':
+ case 'V':
+ verbouse = 1;
+ default:
+ fprintf(stderr, "%s: unknown option %s\n", progname, argv[findex]);
+ break;
+ }
+ if (findex == argc) {
+ fprintf(stderr,"%s: No filenames given.\n", progname);
+ return 1;
+ }
+ for(; findex < argc; ++findex)
+ if ((ret = dump_edit(argv[findex],edit)) != 0)
+ return ret;
+ return 0;
+}
+
+int dump_edit(char *filename, int edit)
+{
+ FILE *f = fopen(filename, (edit) ? "r+b" : "rb");
+ IMAGE_FILE_HEADER filhdr;
+ IMAGE_SECTION_HEADER scnhdr;
+ int i;
+
+ if (f == NULL) {
+ fprintf(stderr, "%s: cannot open %s.\n", progname, filename);
+ return 1;
+ }
+
+ if (fread(&filhdr, sizeof(filhdr), 1, f) == 0) {
+ fprintf(stderr,"%s: Could not read COFF header from %s,"
+ " is this a PE (COFF) file?\n", progname, filename);
+ fclose(f);
+ return 1;
+ }
+ v_printf("File: %s\n", filename);
+ v_printf("Magic number: 0x%08x\n", filhdr.Machine);
+ v_printf("Number of sections: %d\n",filhdr.NumberOfSections);
+
+ if (fseek(f, (long) filhdr.SizeOfOptionalHeader, SEEK_CUR) != 0) {
+ fprintf(stderr,"%s: Could not read COFF optional header from %s,"
+ " is this a PE (COFF) file?\n", progname, filename);
+ fclose(f);
+ return 1;
+ }
+
+ for (i = 0; i < filhdr.NumberOfSections; ++i) {
+ if (fread(&scnhdr, sizeof(scnhdr), 1, f) == 0) {
+ fprintf(stderr,"%s: Could not read section header from %s,"
+ " is this a PE (COFF) file?\n", progname, filename);
+ fclose(f);
+ return 1;
+ }
+ v_printf("Section %s:\n", scnhdr.Name);
+ v_printf("Physical address: 0x%08x\n", scnhdr.Misc.PhysicalAddress);
+ v_printf("Size: 0x%08x\n", scnhdr.SizeOfRawData);
+ if (scnhdr.Misc.PhysicalAddress != 0 &&
+ scnhdr.SizeOfRawData == 0) {
+ printf("Section header %s in file %s will confuse MSC linker, "
+ "virtual size is 0x%08x and raw size is 0\n",
+ scnhdr.Name, filename, scnhdr.Misc.PhysicalAddress,
+ scnhdr.SizeOfRawData);
+ if (edit) {
+ scnhdr.SizeOfRawData = scnhdr.Misc.PhysicalAddress;
+ scnhdr.Misc.PhysicalAddress = 0;
+ if (fseek(f, (long) -((long)sizeof(scnhdr)), SEEK_CUR) != 0 ||
+ fwrite(&scnhdr, sizeof(scnhdr), 1, f) == 0) {
+ fprintf(stderr,"%s: could not edit file %s.\n",
+ progname, filename);
+ fclose(f);
+ return 1;
+ }
+ printf("Edited object, virtual size is now 0, and "
+ "raw size is 0x%08x.\n", scnhdr.SizeOfRawData);
+ } else {
+ printf("Specify option '-e' to correct the problem.\n");
+ }
+ }
+ }
+ fclose(f);
+ return 0;
+}
+
+
+static int v_printf(char *format, ...)
+{
+ va_list ap;
+ int ret = 0;
+ if (verbouse) {
+ va_start(ap, format);
+ ret = vfprintf(stdout, format, ap);
+ va_end(ap);
+ }
+ return ret;
+}
diff --git a/erts/etc/win32/wsl_tools/vc/emu_cc.sh b/erts/etc/win32/wsl_tools/vc/emu_cc.sh
new file mode 100755
index 0000000000..00b8555d2b
--- /dev/null
+++ b/erts/etc/win32/wsl_tools/vc/emu_cc.sh
@@ -0,0 +1,100 @@
+#! /bin/sh
+#
+# %CopyrightBegin%
+#
+# Copyright Ericsson AB 2002-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%
+#
+
+# sudo apt-get install gcc-mingw-w64 on (wsl ubuntu)
+
+if [ X"$CONFIG_SUBTYPE" = X"win64" ]; then
+ GCC="x86_64-w64-mingw32-gcc -m64"
+else
+ GCC="x86_64-w64-mingw32-gcc -m32"
+fi
+TOOLDIR=$ERL_TOP/erts/etc/win32/wsl_tools/vc
+COFFIX=$TOOLDIR/coffix
+WTOOLDIR=`w32_path.sh -d "$TOOLDIR"`
+# Do primitive 'make'
+newer_exe=`find $TOOLDIR -newer $COFFIX.c -name coffix.exe -print`
+
+if [ -z $newer_exe ]; then
+ echo recompiling $COFFIX.exe
+ cl.exe -Fe${WTOOLDIR}/coffix.exe ${WTOOLDIR}/coffix.c
+ rm -f $COFFIX.obj coffix.obj $COFFIX.pdb coffix.pdb
+fi
+
+# Try to find out the output filename and remove it from command line
+CMD=""
+OUTFILE=""
+INFILE=""
+SKIP_COFFIX=false
+while test -n "$1" ; do
+ x="$1"
+ case "$x" in
+ -o/*)
+ OUTFILE=`echo $x | sed 's,^-[Io]\(/.*\),\1,g'`;;
+ -o)
+ shift
+ OUTFILE=$1;;
+ -MM)
+ SKIP_COFFIX=true
+ CMD="$CMD \"$x\"";;
+ *.c)
+ INFILE="$INFILE $x";
+ CMD="$CMD \"$x\"";;
+ *)
+ CMD="$CMD \"$x\"";;
+ esac
+ shift
+done
+if [ -z "$INFILE" ]; then
+ echo 'emu_cc.sh: please give an input filename for the compiler' >&2
+ exit 1
+fi
+if [ -z "$OUTFILE" ]; then
+ OUTFILE=`echo $INFILE | sed 's,\.c$,.o,'`
+fi
+
+if [ $SKIP_COFFIX = false ]; then
+ n=`echo $INFILE | wc -w`;
+ if [ $n -gt 1 ]; then
+ echo "emu_cc.sh:Error, multiple sources, one object output.";
+ exit 1;
+ fi
+ mkdir -p $ERL_TOP/tmp
+ TEMPFILE=$ERL_TOP/tmp/tmp_emu_cc$$.o
+ if [ "X$EMU_CC_SH_DEBUG_LOG" != "X" ]; then
+ echo "$GCC -o $TEMPFILE -D__WIN32__ -DWIN32 -DWINDOWS -fomit-frame-pointer $CMD" >> $EMU_CC_SH_DEBUG_LOG 2>&1
+ fi
+ eval $GCC -o $TEMPFILE -D__WIN32__ -DWIN32 -DWINDOWS -fomit-frame-pointer $CMD
+ RES=$?
+ if [ $RES = 0 ]; then
+ $COFFIX.exe -e `w32_path.sh -w $TEMPFILE`
+ RES=$?
+ if [ $RES = 0 ]; then
+ cp $TEMPFILE $OUTFILE
+ else
+ echo "emu_cc.sh: fatal: coffix failed!" >&2
+ fi
+ fi
+ rm -f $TEMPFILE
+ exit $RES
+else
+ eval $GCC -D__WIN32__ -DWIN32 -DWINDOWS -fomit-frame-pointer -fno-tree-copyrename $CMD 2>/dev/null
+ exit $?
+fi
diff --git a/erts/etc/win32/wsl_tools/vc/ld.sh b/erts/etc/win32/wsl_tools/vc/ld.sh
new file mode 100755
index 0000000000..fc115bec8c
--- /dev/null
+++ b/erts/etc/win32/wsl_tools/vc/ld.sh
@@ -0,0 +1,210 @@
+#! /bin/sh
+# set -x
+#
+# %CopyrightBegin%
+#
+# Copyright Ericsson AB 2002-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%
+#
+# Save the command line for debug outputs
+
+SAVE="$@"
+kernel_libs="kernel32.lib advapi32.lib"
+gdi_libs="gdi32.lib user32.lib comctl32.lib comdlg32.lib shell32.lib"
+DEFAULT_LIBRARIES="$kernel_libs $gdi_libs"
+
+CMD=""
+STDLIB=MSVCRT.LIB
+DEBUG_BUILD=false
+STDLIB_FORCED=false
+BUILD_DLL=false
+OUTPUT_FILENAME=""
+
+while test -n "$1" ; do
+ x="$1"
+ case "$x" in
+ -dll| -DLL)
+ BUILD_DLL=true;;
+ -L/*|-L.*)
+ y=`echo $x | sed 's,^-L\(.*\),\1,g'`;
+ MPATH=`w32_path.sh -d $y`;
+ CMD="$CMD -libpath:\"$MPATH\"";;
+ -lMSVCRT|-lmsvcrt)
+ STDLIB_FORCED=true;
+ STDLIB=MSVCRT.LIB;;
+ -lMSVCRTD|-lmsvcrtd)
+ STDLIB_FORCED=true;
+ STDLIB=MSVCRTD.LIB;;
+ -lLIBCMT|-llibcmt)
+ STDLIB_FORCED=true;
+ STDLIB=LIBCMT.LIB;;
+ -lLIBCMTD|-llibcmtd)
+ STDLIB_FORCED=true;
+ STDLIB=LIBCMTD.LIB;;
+ -lsocket)
+ DEFAULT_LIBRARIES="$DEFAULT_LIBRARIES WS2_32.LIB IPHLPAPI.LIB";;
+ -l*)
+ y=`echo $x | sed 's,^-l\(.*\),\1,g'`;
+ MPATH=`w32_path.sh -d $y`;
+ CMD="$CMD \"${MPATH}.lib\"";;
+ -g)
+ DEBUG_BUILD=true;;
+ -pdb:none|-incremental:no)
+ ;;
+ -implib:*)
+ y=`echo $x | sed 's,^-implib:\(.*\),\1,g'`;
+ MPATH=`w32_path.sh -d $y`;
+ CMD="$CMD -implib:\"${MPATH}\"";;
+ -def:*)
+ y=`echo $x | sed 's,^-def:\(.*\),\1,g'`;
+ MPATH=`w32_path.sh -d $y`;
+ CMD="$CMD -def:\"${MPATH}\"";;
+ -o)
+ shift
+ MPATH=`w32_path.sh -a -d $1`;
+ OUTPUT_FILENAME="$MPATH";;
+ -o/*)
+ y=`echo $x | sed 's,^-[Io]\(/.*\),\1,g'`;
+ MPATH=`w32_path.sh -a -d $y`;
+ OUTPUT_FILENAME="$MPATH";;
+ /*)
+ MPATH=`w32_path.sh -d $x`;
+ CMD="$CMD \"$MPATH\"";;
+ *)
+ y=`echo $x | sed 's,",\\\",g'`;
+ CMD="$CMD \"$y\"";;
+ esac
+ shift
+done
+if [ $DEBUG_BUILD = true ]; then
+ linktype="-debug -pdb:none"
+ if [ $STDLIB_FORCED = false ]; then
+ STDLIB=MSVCRTD.LIB
+ fi
+fi
+# Generate a PDB
+linkadd_pdb=""
+case "$OUTPUT_FILENAME" in
+ *.exe|*.EXE)
+ fn=`echo "$OUTPUT_FILENAME" | sed 's,[eE][xX][eE]$,,g'`;
+ # fn=`w32_path.sh -a -d $fn0`
+ # echo EXE "$OUTPUT_FILENAME" $fn
+ linkadd_pdb="-pdb:\"${fn}pdb\"";;
+ *.dll|*.DLL)
+ fn=`echo "$OUTPUT_FILENAME" | sed 's,[dD][lL][lL]$,,g'`;
+ # fn=`w32_path.sh -a -d $fn0`
+ # echo DLL "$OUTPUT_FILENAME" $fn
+ linkadd_pdb="-pdb:\"${fn}pdb\"";;
+ "")
+ linkadd_pdb="-pdb:\"a.pdb\"";;
+ *)
+ fn="$OUTPUT_FILENAME"
+ # fn=`w32_path.sh -a -d $OUTPUT_FILENAME`
+ # echo * "$OUTPUT_FILENAME" $fn
+ linkadd_pdb="-pdb:\"${fn}.pdb\"";;
+esac
+
+linktype="-debug $linkadd_pdb"
+
+CHMOD_FILE=""
+
+if [ $BUILD_DLL = true ];then
+ case "$OUTPUT_FILENAME" in
+ *.exe|*.EXE)
+ echo "Warning, output set to .exe when building DLL" >&2
+ CHMOD_FILE="$OUTPUT_FILENAME";
+ CMD="-dll -out:\"$OUTPUT_FILENAME\" $CMD";
+ OUTPUTRES="${OUTPUT_FILENAME}\;2";
+ MANIFEST="${OUTPUT_FILENAME}.manifest";;
+ *.dll|*.DLL)
+ CMD="-dll -out:\"$OUTPUT_FILENAME\" $CMD";
+ OUTPUTRES="${OUTPUT_FILENAME}\;2";
+ MANIFEST="${OUTPUT_FILENAME}.manifest";;
+ "")
+ CMD="-dll -out:\"a.dll\" $CMD";
+ OUTPUTRES="a.dll\;2";
+ MANIFEST="a.dll.manifest";;
+ *)
+ CMD="-dll -out:\"${OUTPUT_FILENAME}.dll\" $CMD";
+ OUTPUTRES="${OUTPUT_FILENAME}.dll\;2";
+ MANIFEST="${OUTPUT_FILENAME}.dll.manifest";;
+ esac
+else
+ case "$OUTPUT_FILENAME" in
+ *.exe|*.EXE)
+ CHMOD_FILE="$OUTPUT_FILENAME";
+ CMD="-out:\"$OUTPUT_FILENAME\" $CMD";
+ OUTPUTRES="${OUTPUT_FILENAME}\;1"
+ MANIFEST="${OUTPUT_FILENAME}.manifest";;
+ *.dll|*.DLL)
+ echo "Warning, output set to .dll when building EXE" >&2
+ CMD="-out:\"$OUTPUT_FILENAME\" $CMD";
+ OUTPUTRES="${OUTPUT_FILENAME}\;1";
+ MANIFEST="${OUTPUT_FILENAME}.manifest";;
+ "")
+ CHMOD_FILE="a.exe";
+ CMD="-out:\"a.exe\" $CMD";
+ OUTPUTRES="a.exe\;1";
+ MANIFEST="a.exe.manifest";;
+ *)
+ CMD="-out:\"${OUTPUT_FILENAME}.exe\" $CMD";
+ OUTPUTRES="${OUTPUT_FILENAME}.exe\;1";
+ MANIFEST="${OUTPUT_FILENAME}.exe.manifest";;
+ esac
+fi
+
+p=$$
+CMD="$linktype -nologo -incremental:no $CMD $STDLIB $DEFAULT_LIBRARIES"
+if [ "X$LD_SH_DEBUG_LOG" != "X" ]; then
+ echo ld.sh "$SAVE" >>$LD_SH_DEBUG_LOG
+ echo link.exe $CMD >>$LD_SH_DEBUG_LOG
+fi
+eval link.exe "$CMD" >/tmp/link.exe.${p}.1 2>/tmp/link.exe.${p}.2
+RES=$?
+
+CMANIFEST=`w32_path.sh -u $MANIFEST`
+
+if [ "$RES" = "0" -a -f "$CMANIFEST" ]; then
+ # Add stuff to manifest to turn off "virtualization"
+ sed -n -i '1h;1!H;${;g;s,<trustInfo.*</trustInfo>.,,g;p;}' $CMANIFEST 2>/dev/null
+ sed -i "s/<\/assembly>/ <ms_asmv2:trustInfo xmlns:ms_asmv2=\"urn:schemas-microsoft-com:asm.v2\">\n <ms_asmv2:security>\n <ms_asmv2:requestedPrivileges>\n <ms_asmv2:requestedExecutionLevel level=\"AsInvoker\" uiAccess=\"false\"\/>\n <\/ms_asmv2:requestedPrivileges>\n <\/ms_asmv2:security>\n <\/ms_asmv2:trustInfo>\n<\/assembly>/" $CMANIFEST 2>/dev/null
+
+ eval mt.exe -nologo -manifest "$MANIFEST" -outputresource:"$OUTPUTRES" >>/tmp/link.exe.${p}.1 2>>/tmp/link.exe.${p}.2
+ RES=$?
+ if [ "$RES" != "0" ]; then
+ REMOVE=`echo "$OUTPUTRES" | sed 's,\\\;[12]$,,g'`
+ CREMOVE=`wslpath -u $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"
+fi
+
+# This works around some strange behaviour
+# in cygwin 1.7 Beta on Windows 7 with samba drive.
+# Configure will think the compiler failed if test -x fails,
+# which it might do as we might not be the owner of the
+# file.
+if [ '!' -z "$CHMOD_FILE" -a -s "$CHMOD_FILE" -a '!' -x "$CHMOD_FILE" ]; then
+ chmod +x $CHMOD_FILE
+fi
+
+tail -n +2 /tmp/link.exe.${p}.2 >&2
+cat /tmp/link.exe.${p}.1
+rm -f /tmp/link.exe.${p}.2 /tmp/link.exe.${p}.1
+exit $RES
diff --git a/erts/etc/win32/wsl_tools/vc/mc.sh b/erts/etc/win32/wsl_tools/vc/mc.sh
new file mode 100755
index 0000000000..4ed7e7e2a3
--- /dev/null
+++ b/erts/etc/win32/wsl_tools/vc/mc.sh
@@ -0,0 +1,96 @@
+#! /bin/sh
+# set -x
+#
+# %CopyrightBegin%
+#
+# Copyright Ericsson AB 2002-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%
+#
+# Save the command line for debug outputs
+SAVE="$@"
+CMD=""
+OUTPUT_DIRNAME=""
+
+# Find the correct mc.exe. This could be done by the configure script,
+# But as we seldom use the message compiler, it might as well be done here...
+MCC=""
+save_ifs=$IFS
+IFS=:
+for p in $PATH; do
+ if [ -f $p/mc.exe ]; then
+ if [ -n "`$p/mc.exe -? 2>&1 >/dev/null </dev/null \
+ | grep -i \"message compiler\"`" ]; then
+ MCC=`echo "mc.exe" | sed 's/ /\\\\ /g'`
+ break
+ else
+ echo "Bad mc.exe in path" >&2
+ exit 1
+ fi
+ fi
+done
+IFS=$save_ifs
+
+if [ -z "$MCC" ]; then
+ echo 'mc.exe not found!' >&2
+ exit 1
+fi
+
+while test -n "$1" ; do
+ x="$1"
+ case "$x" in
+ -o)
+ shift
+ OUTPUT_DIRNAME="$1";;
+ -o/*)
+ y=`echo $x | sed 's,^-[Io]\(/.*\),\1,g'`;
+ OUTPUT_DIRNAME="$y";;
+ -I)
+ shift
+ MPATH=`w32_path.sh -d $1`;
+ CMD="$CMD -I\"$MPATH\"";;
+ -I/*)
+ y=`echo $x | sed 's,^-[Io]\(/.*\),\1,g'`;
+ MPATH=`w32_path.sh -d $y`;
+ CMD="$CMD -I\"$MPATH\"";;
+ *)
+ MPATH=`w32_path.sh -d -a $x`;
+ CMD="$CMD \"$MPATH\"";;
+ esac
+ shift
+done
+p=$$
+if [ "X$MC_SH_DEBUG_LOG" != "X" ]; then
+ echo mc.sh "$SAVE" >>$MC_SH_DEBUG_LOG
+ echo mc.exe $CMD >>$MC_SH_DEBUG_LOG
+fi
+if [ -n "$OUTPUT_DIRNAME" ]; then
+ cd $OUTPUT_DIRNAME
+ RES=$?
+ if [ "$RES" != "0" ]; then
+ echo "mc.sh: Error: could not cd to $OUTPUT_DIRNAME">&2
+ exit $RES
+ fi
+fi
+
+eval $MCC "$CMD" >/tmp/mc.exe.${p}.1 2>/tmp/mc.exe.${p}.2
+RES=$?
+if [ $RES != 0 ]; then
+ echo Failed: $MCC "$CMD"
+fi
+tail -n +2 /tmp/mc.exe.${p}.2 >&2
+cat /tmp/mc.exe.${p}.1
+rm -f /tmp/mc.exe.${p}.2 /tmp/mc.exe.${p}.1
+exit $RES
diff --git a/erts/etc/win32/wsl_tools/vc/rc.sh b/erts/etc/win32/wsl_tools/vc/rc.sh
new file mode 100755
index 0000000000..bbd5c9a773
--- /dev/null
+++ b/erts/etc/win32/wsl_tools/vc/rc.sh
@@ -0,0 +1,94 @@
+#! /bin/sh
+# set -x
+#
+# %CopyrightBegin%
+#
+# Copyright Ericsson AB 2002-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%
+#
+# Save the command line for debug outputs
+SAVE="$@"
+CMD=""
+OUTPUT_FILENAME=""
+
+# Find the correct rc.exe. This could be done by the configure script,
+# But as we seldom use the resource compiler, it might as well be done here...
+RCC=""
+save_ifs=$IFS
+IFS=:
+for p in $PATH; do
+ if [ -f $p/rc.exe ]; then
+ if [ -n "`$p/rc.exe -? 2>&1 | grep -i "resource compiler"`" ]; then
+ RCC="rc.exe /nologo"
+ break
+ else
+ echo "Bad rc.exe in path" >&2
+ exit 1
+ fi
+ fi
+done
+IFS=$save_ifs
+
+if [ -z "$RCC" ]; then
+ echo 'rc.exe not found!' >&2
+ exit 1
+fi
+
+while test -n "$1" ; do
+ x="$1"
+ case "$x" in
+ -o)
+ shift
+ MPATH=`w32_path.sh -d $1`;
+ OUTPUT_FILENAME="$MPATH";;
+ -o/*)
+ y=`echo $x | sed 's,^-[Io]\(/.*\),\1,g'`;
+ MPATH=`w32_path.sh -d $y`;
+ OUTPUT_FILENAME="$MPATH";;
+ -I)
+ shift
+ MPATH=`w32_path.sh -d $1`;
+ CMD="$CMD -I\"$MPATH\"";;
+ -I/*)
+ y=`echo $x | sed 's,^-[Io]\(/.*\),\1,g'`;
+ MPATH=`w32_path.sh -d $y`;
+ CMD="$CMD -I\"$MPATH\"";;
+ /*)
+ MPATH=`w32_path.sh -d $x`;
+ CMD="$CMD \"$MPATH\"";;
+ *)
+ y=`echo $x | sed 's,",\\\",g'`;
+ CMD="$CMD \"$y\"";;
+ esac
+ shift
+done
+p=$$
+if [ -n "$OUTPUT_FILENAME" ]; then
+ CMD="-Fo$OUTPUT_FILENAME $CMD"
+fi
+if [ "X$RC_SH_DEBUG_LOG" != "X" ]; then
+ echo rc.sh "$SAVE" >>$RC_SH_DEBUG_LOG
+ echo rc.exe /nologo $CMD >>$RC_SH_DEBUG_LOG
+fi
+eval $RCC "$CMD" >/tmp/rc.exe.${p}.1 2>/tmp/rc.exe.${p}.2
+RES=$?
+if [ $RES != 0 ]; then
+ echo Failed: $RCC "$CMD"
+fi
+tail -n +2 /tmp/rc.exe.${p}.2 >&2
+cat /tmp/rc.exe.${p}.1
+rm -f /tmp/rc.exe.${p}.2 /tmp/rc.exe.${p}.1
+exit $RES
diff --git a/erts/etc/win32/wsl_tools/w32_path.sh b/erts/etc/win32/wsl_tools/w32_path.sh
new file mode 100755
index 0000000000..55fbd76174
--- /dev/null
+++ b/erts/etc/win32/wsl_tools/w32_path.sh
@@ -0,0 +1,70 @@
+#!/bin/bash
+
+WIN32=false
+SEPARATOR=""
+ABSOLUTE=""
+UNIX=false
+done=false
+while [ $done = false ]; do
+ case "$1" in
+ -w)
+ WIN32=true;
+ SEPARATOR=backslash;
+ shift;;
+ -d)
+ WIN32=true;
+ SEPARATOR=double;
+ shift;;
+ -m)
+ WIN32=true;
+ SEPARATOR=slash;
+ shift;;
+ -u)
+ UNIX=true;
+ shift;;
+ -a)
+ ABSOLUTE="-a";
+ shift;;
+
+ *)
+ done=true;;
+ esac
+done
+
+if [ $WIN32 = false -a $UNIX = false ]; then
+ echo "Usage: $0 -m|-w|-d|-u [-a] <path>" >&2
+ exit 1;
+fi
+
+if [ -z "$1" ]; then
+ echo "Usage: $0 -m|-w|-u [-a] <path>" >&2
+ exit 1;
+fi
+
+if [ $UNIX = true ]; then
+ # cl.exe loses //// in the beginning which make dependencies fail
+ # and sometimes lowercases the path
+ case $1 in
+ \\*wsl$\\*)
+ y=`echo $1 | sed 's,\\\\\+,/,g'`;
+ z=`echo $y | sed 's,^/wsl$/[^/]*\(.*\),\1,g' | sed 's, ,\\ ,g'`;
+ echo "$z";
+ ;;
+ *)
+ echo `wslpath -u $ABSOLUTE "$1" | sed 's, ,\\ ,g'`
+ ;;
+ esac
+else
+ case "$SEPARATOR" in
+ slash)
+ echo `wslpath -m $ABSOLUTE "$1"`;
+ ;;
+ backslash)
+ echo `wslpath -w $ABSOLUTE "$1"`;
+ ;;
+ double)
+ DOUBLE=`wslpath -w $ABSOLUTE "$1" | sed 's,\\\\,\\\\\\\\,g'`;
+ echo $DOUBLE
+ ;;
+ esac
+fi
diff --git a/erts/include/internal/erl_misc_utils.h b/erts/include/internal/erl_misc_utils.h
index 55566ddf74..59933ade4b 100644
--- a/erts/include/internal/erl_misc_utils.h
+++ b/erts/include/internal/erl_misc_utils.h
@@ -39,6 +39,7 @@ int erts_cpu_info_update(erts_cpu_info_t *cpuinfo);
int erts_get_cpu_configured(erts_cpu_info_t *cpuinfo);
int erts_get_cpu_online(erts_cpu_info_t *cpuinfo);
int erts_get_cpu_available(erts_cpu_info_t *cpuinfo);
+int erts_get_cpu_quota(erts_cpu_info_t *cpuinfo);
char *erts_get_unbind_from_cpu_str(erts_cpu_info_t *cpuinfo);
int erts_get_available_cpu(erts_cpu_info_t *cpuinfo, int no);
int erts_get_cpu_topology_size(erts_cpu_info_t *cpuinfo);
diff --git a/erts/include/internal/erl_printf_format.h b/erts/include/internal/erl_printf_format.h
index 56ec032bd1..27fe914caa 100644
--- a/erts/include/internal/erl_printf_format.h
+++ b/erts/include/internal/erl_printf_format.h
@@ -21,10 +21,6 @@
#ifndef ERL_PRINTF_FORMAT_H__
#define ERL_PRINTF_FORMAT_H__
-#ifdef VXWORKS
-#include <vxWorks.h>
-#endif
-
#include <sys/types.h>
#include <stdarg.h>
#include <stdlib.h>
diff --git a/erts/include/internal/ethread.h b/erts/include/internal/ethread.h
index fa35bf3d0b..2fe826226c 100644
--- a/erts/include/internal/ethread.h
+++ b/erts/include/internal/ethread.h
@@ -54,8 +54,7 @@
#endif
#if defined(ETHR_DEBUG) || !defined(ETHR_INLINE) || ETHR_XCHK \
- || (defined(__GNUC__) && defined(ERTS_MIXED_CYGWIN_VC)) \
- || (defined(__GNUC__) && defined(ERTS_MIXED_MSYS_VC))
+ || (defined(__GNUC__) && defined(ERTS_MIXED_VC))
# undef ETHR_INLINE
# define ETHR_INLINE
# undef ETHR_FORCE_INLINE
diff --git a/erts/lib_src/Makefile.in b/erts/lib_src/Makefile.in
index 1da11c2d0a..85250c4358 100644
--- a/erts/lib_src/Makefile.in
+++ b/erts/lib_src/Makefile.in
@@ -152,7 +152,6 @@ ERTS_INCL_INT=../include/internal
INCLUDES=-I$(ERTS_INCL) -I$(ERTS_INCL)/$(TARGET) -I$(ERTS_INCL_INT) -I$(ERTS_INCL_INT)/$(TARGET)
INCLUDES += -I../emulator/beam -I../emulator/sys/$(ERLANG_OSTYPE)
-USING_MINGW=@MIXED_CYGWIN_MINGW@
USING_VC=@MIXED_VC@
ifeq ($(USING_VC),yes)
@@ -657,8 +656,10 @@ endif
endif
@echo "# EOF" >> $(DEPEND_MK);
+ifneq ($(ERTS_SKIP_DEPEND),true)
ifneq ($(MAKECMDGOALS),clean)
-include $(DEPEND_MK)
endif
+endif
# eof
diff --git a/erts/lib_src/common/erl_misc_utils.c b/erts/lib_src/common/erl_misc_utils.c
index c75c492c9d..e047c0eec7 100644
--- a/erts/lib_src/common/erl_misc_utils.c
+++ b/erts/lib_src/common/erl_misc_utils.c
@@ -29,10 +29,7 @@
#include "ethread_inline.h"
#include "erl_misc_utils.h"
-#if defined(__WIN32__)
-#elif defined(VXWORKS)
-# include <selectLib.h>
-#else /* UNIX */
+#if !defined(__WIN32__) /* UNIX */
# include <stdio.h>
# include <sys/types.h>
# include <sys/param.h>
@@ -54,6 +51,7 @@
# endif
# endif
# include <string.h>
+# include <stdio.h>
# ifdef HAVE_UNISTD_H
# include <unistd.h>
# endif
@@ -133,6 +131,7 @@
#endif
static int read_topology(erts_cpu_info_t *cpuinfo);
+static int read_cpu_quota(int limit);
#if defined(ERTS_HAVE_MISC_UTIL_AFFINITY_MASK__)
static int
@@ -176,6 +175,7 @@ struct erts_cpu_info_t_ {
int online;
int available;
int topology_size;
+ int quota;
erts_cpu_topology_t *topology;
#if defined(ERTS_HAVE_MISC_UTIL_AFFINITY_MASK__)
char *affinity_str;
@@ -238,6 +238,7 @@ erts_cpu_info_create(void)
cpuinfo->configured = -1;
cpuinfo->online = -1;
cpuinfo->available = -1;
+ cpuinfo->quota = -1;
erts_cpu_info_update(cpuinfo);
return cpuinfo;
}
@@ -269,6 +270,7 @@ erts_cpu_info_update(erts_cpu_info_t *cpuinfo)
int configured = 0;
int online = 0;
int available = 0;
+ int quota = 0;
erts_cpu_topology_t *old_topology;
int old_topology_size;
#if defined(ERTS_HAVE_MISC_UTIL_AFFINITY_MASK__)
@@ -412,9 +414,14 @@ erts_cpu_info_update(erts_cpu_info_t *cpuinfo)
if (cpuinfo->available != available)
changed = 1;
+ quota = read_cpu_quota(online);
+ if (cpuinfo->quota != quota)
+ changed = 1;
+
cpuinfo->configured = configured;
cpuinfo->online = online;
cpuinfo->available = available;
+ cpuinfo->quota = quota;
old_topology = cpuinfo->topology;
old_topology_size = cpuinfo->topology_size;
@@ -471,6 +478,16 @@ erts_get_cpu_available(erts_cpu_info_t *cpuinfo)
return cpuinfo->available;
}
+int
+erts_get_cpu_quota(erts_cpu_info_t *cpuinfo)
+{
+ if (!cpuinfo)
+ return -EINVAL;
+ if (cpuinfo->quota <= 0)
+ return -ENOTSUP;
+ return cpuinfo->quota;
+}
+
char *
erts_get_unbind_from_cpu_str(erts_cpu_info_t *cpuinfo)
{
@@ -775,7 +792,7 @@ adjust_processor_nodes(erts_cpu_info_t *cpuinfo, int no_nodes)
#ifdef __linux__
static int
-read_file(char *path, char *buf, int size)
+read_file(const char *path, char *buf, int size)
{
int ix = 0;
ssize_t sz = size-1;
@@ -1004,6 +1021,211 @@ read_topology(erts_cpu_info_t *cpuinfo)
return res;
}
+static int
+csv_contains(const char *haystack,
+ const char *element,
+ char separator) {
+ size_t element_len;
+ const char *ptr;
+
+ element_len = strlen(element);
+ ptr = strstr(haystack, element);
+
+ while (ptr) {
+ if (!ptr[element_len] || ptr[element_len] == separator) {
+ if (ptr == haystack || ptr[-1] == separator) {
+ return 1;
+ }
+ }
+
+ ptr = strstr(&ptr[1], element);
+ }
+
+ return 0;
+}
+
+static const char*
+str_combine(const char *a, const char *b) {
+ size_t a_len, b_len;
+ char *result;
+
+ a_len = strlen(a);
+ b_len = strlen(b);
+
+ result = malloc(a_len + b_len + 1);
+
+ memcpy(&result[0], a, a_len);
+ memcpy(&result[a_len], b, b_len + 1);
+
+ return result;
+}
+
+static const char*
+get_cgroup_v1_base_dir(const char *controller) {
+ char line_buf[5 << 10];
+ FILE *var_file;
+
+ var_file = fopen("/proc/self/cgroup", "r");
+
+ if (var_file == NULL) {
+ return NULL;
+ }
+
+ while (fgets(line_buf, sizeof(line_buf), var_file)) {
+ /* sscanf_s requires C11, so we use hardcoded sizes (rather than rely
+ * on macros like MAXPATHLEN) so we can specify them directly in the
+ * format string. */
+ char base_dir[4 << 10];
+ char controllers[256];
+
+ if (sscanf(line_buf, "%*d:%255[^:]:%4095s\n",
+ controllers, base_dir) != 2) {
+ continue;
+ }
+
+ if (csv_contains(controllers, controller, ',')) {
+ fclose(var_file);
+ return strdup(base_dir);
+ }
+ }
+
+ fclose(var_file);
+ return NULL;
+}
+
+static const char*
+get_cgroup_path(const char *controller) {
+ char line_buf[10 << 10];
+ FILE *var_file;
+
+ var_file = fopen("/proc/self/mountinfo", "r");
+
+ if (var_file == NULL) {
+ return NULL;
+ }
+
+ while (fgets(line_buf, sizeof(line_buf), var_file)) {
+ char mount_path[4 << 10];
+ char root_path[4 << 10];
+ char fs_flags[512];
+ char fs_type[64];
+
+ /* Format:
+ * [Mount id] [Parent id] [Major] [Minor] [Root] [Mounted at] \
+ * [Mount flags] ... (options terminated by a single hyphen) ... \
+ * [FS type] [Mount source] [Flags]
+ *
+ * (See proc(5) for a more complete description.)
+ *
+ * This fails if any of the fs options contain a hyphen, but this is
+ * not likely to happen on a cgroup, so we just skip such lines. */
+ if (sscanf(line_buf,
+ "%*d %*d %*d:%*d %4095s %4095s %*s %*[^-]- "
+ "%63s %*s %511[^\n]\n",
+ root_path, mount_path,
+ fs_type, fs_flags) != 4) {
+ continue;
+ }
+
+ if (!strcmp(fs_type, "cgroup2")) {
+ char controllers[256];
+ const char *cgc_path;
+
+ cgc_path = str_combine(mount_path, "/cgroup.controllers");
+ if (read_file(cgc_path, controllers, sizeof(controllers)) > 0) {
+ if (csv_contains(controllers, controller, ' ')) {
+ free((void*)cgc_path);
+ fclose(var_file);
+ return strdup(mount_path);
+ }
+ }
+ free((void*)cgc_path);
+ } else if (!strcmp(fs_type, "cgroup")) {
+ if (csv_contains(fs_flags, controller, ',')) {
+ const char *base_dir = get_cgroup_v1_base_dir(controller);
+
+ if (base_dir) {
+ const char *result;
+
+ if (strcmp(root_path, base_dir)) {
+ result = str_combine(mount_path, base_dir);
+ } else {
+ result = strdup(mount_path);
+ }
+
+ free((void*)base_dir);
+ fclose(var_file);
+ return result;
+ }
+ }
+ }
+ }
+
+ fclose(var_file);
+ return NULL;
+}
+
+static int read_cgroup_var(const char *group_path, const char *var_name,
+ ssize_t *out) {
+ const char *var_path;
+ int res;
+
+ var_path = str_combine(group_path, var_name);
+ res = 0;
+
+ if (var_path) {
+ FILE *var_file = fopen(var_path, "r");
+ free((void*)var_path);
+
+ if (var_file) {
+ if (fscanf(var_file, "%zi", out) == 1) {
+ res = 1;
+ }
+ fclose(var_file);
+ }
+ }
+
+ return res;
+}
+
+/* CPU quotas are read from the cgroup configuration, which can be pretty hairy
+ * as we need to support both v1 and v2, and it's possible for both versions to
+ * be active at the same time. */
+
+static int
+read_cpu_quota(int limit)
+{
+ const char *cgroup_path = get_cgroup_path("cpu");
+
+ if (cgroup_path) {
+ ssize_t cfs_period_us, cfs_quota_us;
+ int succeeded;
+
+ cfs_period_us = -1;
+ cfs_quota_us = -1;
+
+ succeeded =
+ read_cgroup_var(cgroup_path, "/cpu.cfs_quota_us", &cfs_quota_us) &&
+ read_cgroup_var(cgroup_path, "/cpu.cfs_period_us", &cfs_period_us);
+
+ free((void*)cgroup_path);
+
+ if (succeeded) {
+ if (cfs_period_us > 0 && cfs_quota_us > 0) {
+ size_t quota = cfs_quota_us / cfs_period_us;
+
+ if (quota > 0 && quota <= (size_t)limit) {
+ return quota;
+ }
+ }
+
+ return limit;
+ }
+ }
+
+ return 0;
+}
+
#elif defined(HAVE_KSTAT) /* SunOS kstat */
#include <kstat.h>
@@ -1157,6 +1379,13 @@ read_topology(erts_cpu_info_t *cpuinfo)
}
+static int
+read_cpu_quota(int limit)
+{
+ (void)limit;
+ return 0;
+}
+
#elif defined(__WIN32__)
/*
@@ -1431,6 +1660,13 @@ read_topology(erts_cpu_info_t *cpuinfo)
return res;
}
+static int
+read_cpu_quota(int limit)
+{
+ (void)limit;
+ return 0;
+}
+
#elif defined(__FreeBSD__)
/**
@@ -1670,9 +1906,23 @@ error:
return res;
}
+static int
+read_cpu_quota(int limit)
+{
+ (void)limit;
+ return 0;
+}
+
#else
static int
+read_cpu_quota(int limit)
+{
+ (void)limit;
+ return 0;
+}
+
+static int
read_topology(erts_cpu_info_t *cpuinfo)
{
return -ENOTSUP;
diff --git a/erts/lib_src/common/erl_printf.c b/erts/lib_src/common/erl_printf.c
index dd808eb158..9c33a107d1 100644
--- a/erts/lib_src/common/erl_printf.c
+++ b/erts/lib_src/common/erl_printf.c
@@ -18,11 +18,6 @@
* %CopyrightEnd%
*/
-/* Without this, variable argument lists break on VxWorks */
-#ifdef VXWORKS
-#include <vxWorks.h>
-#endif
-
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
diff --git a/erts/lib_src/common/erl_printf_format.c b/erts/lib_src/common/erl_printf_format.c
index 8f9e0b4a90..c8256532b5 100644
--- a/erts/lib_src/common/erl_printf_format.c
+++ b/erts/lib_src/common/erl_printf_format.c
@@ -31,11 +31,6 @@
* sz: 8 | 16 | 32 | 64 | p | e
*/
-/* Without this, variable argument lists break on VxWorks */
-#ifdef VXWORKS
-#include <vxWorks.h>
-#endif
-
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
diff --git a/erts/lib_src/common/ethr_aux.c b/erts/lib_src/common/ethr_aux.c
index 7b156fe01a..931469b386 100644
--- a/erts/lib_src/common/ethr_aux.c
+++ b/erts/lib_src/common/ethr_aux.c
@@ -109,7 +109,8 @@ x86_init(void)
if (eax > 0
&& (ETHR_IS_X86_VENDOR("GenuineIntel", ebx, ecx, edx)
- || ETHR_IS_X86_VENDOR("AuthenticAMD", ebx, ecx, edx))) {
+ || ETHR_IS_X86_VENDOR("AuthenticAMD", ebx, ecx, edx)
+ || ETHR_IS_X86_VENDOR("HygonGenuine", ebx, ecx, edx))) {
eax = 1;
ethr_x86_cpuid__(&eax, &ebx, &ecx, &edx);
}
diff --git a/erts/lib_src/yielding_c_fun/.gitignore b/erts/lib_src/yielding_c_fun/.gitignore
new file mode 100644
index 0000000000..eb74315e49
--- /dev/null
+++ b/erts/lib_src/yielding_c_fun/.gitignore
@@ -0,0 +1,16 @@
+*.o
+*~
+bin/yielding_c_fun.bin
+core
+test/test_trap_out.c
+GPATH
+GRTAGS
+GTAGS
+test/tmp_dir
+compile_commands.json
+compile_commands_old.json
+CMakeLists.txt
+cmake_mkdir/
+.idea/
+cmake-build-debug/
+ycf_malloc_log.txt \ No newline at end of file
diff --git a/erts/lib_src/yielding_c_fun/GNUmakefile b/erts/lib_src/yielding_c_fun/GNUmakefile
new file mode 100644
index 0000000000..1a18214c21
--- /dev/null
+++ b/erts/lib_src/yielding_c_fun/GNUmakefile
@@ -0,0 +1,212 @@
+# %CopyrightBegin%
+#
+# Copyright Ericsson AB and Kjell Winblad 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%
+
+#
+# Author: Kjell Winblad
+#
+
+#
+# Note: The main target of this makefile generates another makefile
+# which is named "Makefile". The generated file "Makefile" should be
+# compatible with other make implementations than GNU make while this
+# file is only compatible with GNU make. GNU make will automatically
+# use this file as makefile when executed in the directory where this
+# file is located.
+#
+# Erlang/OTP note: The build system for Erlang/OTP does not use this
+# file to build Yielding C Fun (YCF). The file "main_target.mk"
+# contains the necessary rules to build YCF and is included both by
+# this file and the Erlang/OTP file
+# $(ERL_TOP)/erts/emulator/Makefile.in. This file is intended for
+# building YCF independently of Erlang/OTP. This file also contains
+# several special make targets (e.g., to invoke tests).
+#
+
+
+ifdef MODERN_CC
+ EXTRA_C_FLAGS = -g -O02 -std=c99 -pedantic -Wall
+endif
+
+ifdef CC_32_BIT
+ EXTRA_C_FLAGS = -m32 -g -O03 -std=c99 -pedantic -Wall
+endif
+
+ifdef USE_GC
+ USE_GC_STRING = -use_gc
+endif
+
+ifdef ADD_SAN
+ V_CC = clang
+ EXTRA_C_FLAGS = -std=c99 -Wall -pedantic -g -O00 -fsanitize-blacklist=lib/simple_c_gc/.misc/clang_blacklist.txt -fsanitize=address -fno-omit-frame-pointer
+ USE_GC_STRING = -use_gc
+endif
+
+ifdef MEM_SAN
+ V_CC = clang
+ EXTRA_C_FLAGS = -std=c99 -Wall -pedantic -g -O00 -fsanitize-blacklist=lib/simple_c_gc/.misc/clang_blacklist.txt -fsanitize=memory -fno-omit-frame-pointer
+endif
+
+ifdef UB_SAN
+ V_CC = clang
+ EXTRA_C_FLAGS = -std=c99 -Wall -pedantic -g -O00 -fsanitize-blacklist=lib/simple_c_gc/.misc/clang_blacklist.txt -fsanitize=undefined -fno-omit-frame-pointer
+endif
+
+
+YCF_SOURCE_DIR = .
+
+ifndef V_CC
+ V_CC = cc
+endif
+
+ifndef V_LD
+ V_LD = $(CC)
+endif
+
+CFLAGS = $(EXTRA_C_FLAGS)
+
+all: ./bin/yielding_c_fun.bin Makefile
+
+include main_target.mk
+
+##############################################################
+# Generate a simple Makefile intended to work with other make
+# implementations than GNU make
+##############################################################
+
+Makefile: $(YCF_SOURCES)
+ echo '#' > Makefile
+ echo '# !!!!!!!!!!! OBS Generated by GNUmakefile !!!!!!!!!!!!' >> Makefile
+ echo '# This file is supposed to be compatible with as many make implementations as possible' >> Makefile
+ echo '# Do not modify this file manually unless you cannot use GNU make.' >> Makefile
+ echo '# Instead, just run GNU make in the directory where this file is located' >> Makefile
+ echo '# and a new version of this file will be generated when needed.' >> Makefile
+ echo '# GNU make will use the file GNUmakefile which is located in the same' >> Makefile
+ echo '# directory.' >> Makefile
+ echo '#' >> Makefile
+ echo '#' >> Makefile
+ echo '# Erlang/OTP note: The build system for Erlang/OTP does not use this' >> Makefile
+ echo '# file to build Yielding C Fun (YCF). The file "main_target.mk"' >> Makefile
+ echo '# contains the necessary rules to build YCF and is included both by' >> Makefile
+ echo '# this file and the Erlang/OTP file' >> Makefile
+ echo '# $$(ERL_TOP)/erts/emulator/Makefile.in. This file is intended for' >> Makefile
+ echo '# building YCF independently of Erlang/OTP.' >> Makefile
+ echo '#' >> Makefile
+ echo >> Makefile
+ O_FILES="";\
+ for FILE in $(YCF_SOURCES);\
+ do\
+ OFILE_FULL=`echo $$FILE | sed 's/\.c/.o/'` ; \
+ OFILE=`basename $$OFILE_FULL` ; \
+ O_FILES="$$O_FILES $$OFILE" ; \
+ done ; \
+ echo $(YCF_EXECUTABLE)':' $$O_FILES >> Makefile ; \
+ echo ' $$(CC) $(YCF_INCLUDE_DIRS) $$(CFLAGS) -o '$(YCF_EXECUTABLE)' ' $$O_FILES >> Makefile
+ for FILE in $(YCF_SOURCES);\
+ do\
+ echo >> Makefile ; \
+ OFILE_FULL=`echo $$FILE | sed 's/\.c/.o/'` ; \
+ OFILE=`basename $$OFILE_FULL` ; \
+ echo "$$OFILE: $$FILE" >> Makefile ;\
+ echo ' $$(CC) $(YCF_INCLUDE_DIRS) $$(CFLAGS) ' -c -o $$OFILE $$FILE >> Makefile ;\
+ done
+ echo >> Makefile
+
+##############################################
+# Special targets for testing etc:
+##############################################
+
+.PHONY: all clean test run_test_continusly CMakeLists.txt cmake_compile clang_format test_add_san test_mem_san test_modern_cc test_sanitizers test_gcc_clang_tcc clang_tidy test_bmake test_all
+
+test: $(YCF_EXECUTABLE)
+ ./test/test.sh $(USE_GC_STRING) ;\
+ RESULT=$$? &&\
+ (exit $$RESULT) &&\
+ printf "\n\n\033[0;32mALL TESTS PASSED!\033[0m\n\n\n" ||\
+ printf "\n\n\033[0;31mTEST FAILED!\033[0m\n\n\n" &&\
+ exit $$RESULT
+
+test_add_san:
+ make clean && \
+ make V_CC=clang V_LD=clang ADD_SAN=1 test
+
+test_mem_san:
+ make clean && \
+ make V_CC=clang V_LD=clang MEM_SAN=1 test
+
+test_ub_san:
+ make clean && \
+ make V_CC=clang V_LD=clang UB_SAN=1 test
+
+test_modern_cc:
+ make clean && \
+ make V_CC=clang V_LD=clang MODERN_CC=1 test
+
+test_sanitizers:
+ make test_add_san && \
+ make test_mem_san && \
+ make test_ub_san
+
+test_gcc_clang_tcc:
+ make V_CC=gcc V_LD=gcc EXTRA_C_FLAGS="-g -O01 -std=c99 -pedantic -Wall -Werror" clean $(YCF_EXECUTABLE) && \
+ make V_CC=clang V_LD=clang EXTRA_C_FLAGS="-g -O01 -std=c99 -pedantic -Wall -Werror" clean $(YCF_EXECUTABLE) && \
+ make V_CC=tcc V_LD=tcc EXTRA_C_FLAGS="-g -O01 -std=c99 -pedantic -Wall -Werror" clean $(YCF_EXECUTABLE)
+
+test_32_bit:
+ make clean && \
+ make CC_32_BIT=1 test && \
+ make clean
+
+# sudo apt-get install bmake
+# test that something else than GNU make can compile the tool
+test_bmake:
+ make clean && \
+ make Makefile && \
+ bmake
+
+test_all:
+ make test_gcc_clang_tcc && \
+ make clang_tidy && \
+ make test_sanitizers && \
+ make test_modern_cc && \
+ make test_32_bit && \
+ make test_bmake
+
+run_test_continusly:
+ inotifywait -e close_write,moved_to,create -m ./*.c ./*.h -m test -m test/examples | while read -r directory events filename; do gtags ; make test_all ; done
+
+cmake_compile: CMakeLists.txt
+ mkdir cmake_mkdir || true
+ cd cmake_mkdir && cmake ..
+
+clang_tidy:
+ (ls *.c ; echo lib/tiny_regex_c/re.c ; echo lib/simple_c_gc/simple_c_gc.c) | xargs -I{} -n1 clang-tidy -warnings-as-errors=* {} -- $(YCF_INCLUDE_DIRS) $(YCF_CFLAGS)
+
+clang_format:
+ clang-format -style="{BasedOnStyle: LLVM}" -i *.c *.h
+
+clean:
+ rm -f lib/simple_c_gc/*.o lib/tiny_regex_c/*.o ./*.o ./*~ core trap parse $(YCF_EXECUTABLE) CMakeLists.txt
+
+# Produce a CMakeLists.txt to build with cmake
+CMakeLists.txt: $(YCF_SOURCES)
+ echo "cmake_minimum_required (VERSION 2.6)" > CMakeLists.txt
+ echo "project (YIELDING_C_FUN C)" >> CMakeLists.txt
+ echo "add_executable(cmake.out " >> CMakeLists.txt
+ echo $(YCF_SOURCES) >> CMakeLists.txt
+ echo ")" >> CMakeLists.txt
+
diff --git a/erts/lib_src/yielding_c_fun/Makefile b/erts/lib_src/yielding_c_fun/Makefile
new file mode 100644
index 0000000000..35de0a86ac
--- /dev/null
+++ b/erts/lib_src/yielding_c_fun/Makefile
@@ -0,0 +1,54 @@
+#
+# !!!!!!!!!!! OBS Generated by GNUmakefile !!!!!!!!!!!!
+# This file is supposed to be compatible with as many make implementations as possible
+# Do not modify this file manually unless you cannot use GNU make.
+# Instead, just run GNU make in the directory where this file is located
+# and a new version of this file will be generated when needed.
+# GNU make will use the file GNUmakefile which is located in the same
+# directory.
+#
+#
+# Erlang/OTP note: The build system for Erlang/OTP does not use this
+# file to build Yielding C Fun (YCF). The file "main_target.mk"
+# contains the necessary rules to build YCF and is included both by
+# this file and the Erlang/OTP file
+# $(ERL_TOP)/erts/emulator/Makefile.in. This file is intended for
+# building YCF independently of Erlang/OTP.
+#
+
+./bin/yielding_c_fun.bin: simple_c_gc.o re.o ycf_lexer.o ycf_main.o ycf_node.o ycf_parser.o ycf_printers.o ycf_string.o ycf_symbol.o ycf_utils.o ycf_yield_fun.o
+ $(CC) -I. -I./lib/tiny_regex_c -I./lib/simple_c_gc $(CFLAGS) -o ./bin/yielding_c_fun.bin simple_c_gc.o re.o ycf_lexer.o ycf_main.o ycf_node.o ycf_parser.o ycf_printers.o ycf_string.o ycf_symbol.o ycf_utils.o ycf_yield_fun.o
+
+simple_c_gc.o: ./lib/simple_c_gc/simple_c_gc.c
+ $(CC) -I. -I./lib/tiny_regex_c -I./lib/simple_c_gc $(CFLAGS) -c -o simple_c_gc.o ./lib/simple_c_gc/simple_c_gc.c
+
+re.o: ./lib/tiny_regex_c/re.c
+ $(CC) -I. -I./lib/tiny_regex_c -I./lib/simple_c_gc $(CFLAGS) -c -o re.o ./lib/tiny_regex_c/re.c
+
+ycf_lexer.o: ./ycf_lexer.c
+ $(CC) -I. -I./lib/tiny_regex_c -I./lib/simple_c_gc $(CFLAGS) -c -o ycf_lexer.o ./ycf_lexer.c
+
+ycf_main.o: ./ycf_main.c
+ $(CC) -I. -I./lib/tiny_regex_c -I./lib/simple_c_gc $(CFLAGS) -c -o ycf_main.o ./ycf_main.c
+
+ycf_node.o: ./ycf_node.c
+ $(CC) -I. -I./lib/tiny_regex_c -I./lib/simple_c_gc $(CFLAGS) -c -o ycf_node.o ./ycf_node.c
+
+ycf_parser.o: ./ycf_parser.c
+ $(CC) -I. -I./lib/tiny_regex_c -I./lib/simple_c_gc $(CFLAGS) -c -o ycf_parser.o ./ycf_parser.c
+
+ycf_printers.o: ./ycf_printers.c
+ $(CC) -I. -I./lib/tiny_regex_c -I./lib/simple_c_gc $(CFLAGS) -c -o ycf_printers.o ./ycf_printers.c
+
+ycf_string.o: ./ycf_string.c
+ $(CC) -I. -I./lib/tiny_regex_c -I./lib/simple_c_gc $(CFLAGS) -c -o ycf_string.o ./ycf_string.c
+
+ycf_symbol.o: ./ycf_symbol.c
+ $(CC) -I. -I./lib/tiny_regex_c -I./lib/simple_c_gc $(CFLAGS) -c -o ycf_symbol.o ./ycf_symbol.c
+
+ycf_utils.o: ./ycf_utils.c
+ $(CC) -I. -I./lib/tiny_regex_c -I./lib/simple_c_gc $(CFLAGS) -c -o ycf_utils.o ./ycf_utils.c
+
+ycf_yield_fun.o: ./ycf_yield_fun.c
+ $(CC) -I. -I./lib/tiny_regex_c -I./lib/simple_c_gc $(CFLAGS) -c -o ycf_yield_fun.o ./ycf_yield_fun.c
+
diff --git a/erts/lib_src/yielding_c_fun/README.md b/erts/lib_src/yielding_c_fun/README.md
new file mode 100644
index 0000000000..f63475e6a8
--- /dev/null
+++ b/erts/lib_src/yielding_c_fun/README.md
@@ -0,0 +1,610 @@
+Yielding C Fun
+==============
+
+Introduction
+------------
+
+Yielding C Fun (YCF) is a tool that transforms functions written in a
+subset of the C programming language so that they become yieldable. A
+yieldable function can be suspended/yielded/paused/trapped (either
+automatically or where the user has inserted a particular statement)
+and then be resumed at a later point. Yileldable functions are also
+called [coroutines](https://en.wikipedia.org/wiki/Coroutine).
+
+Difference Between Yielding C Fun and Coroutine Libraries
+---------------------------------------------------------
+
+Several libraries implement [coroutine support for the C programming
+language](https://en.wikipedia.org/wiki/Coroutine#Implementations_for_C)
+(e.g., \[[1], [2], [3], [4], [5], [6], [7], [8], [9], [10], [11],
+[12], [13]\]). These libraries either rely on platform-specific code
+or do not save call stack variables. Yielding C Fun (YCF) does not
+have any of these two limitations. YCF can accomplish this as it is a
+source-to-source transformation tool and not only a library.
+
+YCF has been created to make it easier to implement yielding Erlang
+[NIFs](http://erlang.org/doc/tutorial/nif.html) and
+[BIFs](http://erlang.org/pipermail/erlang-questions/2009-October/046899.html)
+(i.e., Erlang functions that are written in C). Below are examples of
+YCF features that are useful when implementing yielding Erlang NIFs
+and BIFs:
+
+ * YCF automatically generates a destroy function for each yieldable
+ function. The destroy function frees resources that are used by a
+ suspended function. The destroy function is useful when a suspended
+ function needs to abort (e.g., when the Erlang process that invoked
+ the function has died).
+
+ * YCF can automatically insert code that yields functions after a
+ user specifiable number of loop iterations and goto statements.
+
+ * YCF has a hook system that lets the user insert code that is
+ triggered when certain events happen (e.g., when a function
+ yields).
+
+The main limitations of YCF are that it cannot handle all valid C code
+and that it cannot make library functions without source code
+yieldable. Pointers to stack-allocated data are also not allowed (YCF
+has a memory allocation function called `YCF_STACK_ALLOC` to work
+around this issue).
+
+Requirements
+------------
+
+* A C99 compatible C compiler
+* make (optional but useful for building)
+
+Compile and Test
+----------------
+
+Build the executable `$YCF_ROOT/bin/yielding_c_fun.bin`:
+
+ make
+
+Build the executable and run all tests:
+
+ make test
+
+Getting Started
+---------------
+
+A brief introduction tutorial can be found
+[here](doc/thread_tutorial.md). This tutorial is a perfect place to
+start!
+
+The "[test/examples/](test/examples/)" folder in this repository
+contains many small examples that are useful when learning about
+YCF. YCF's automatic tests use these examples as well. The driver for
+these tests is located in `test/test.sh`.
+
+[This Erlang NIF example](test/examples/sha256_erlang_nif/) shows how
+one can use YCF to write a yielding Erlang NIF library.
+
+
+Command Line Parameters
+-----------------------
+
+```
+Usage: yielding_c_fun [-h]
+ yielding_c_fun [-use_gc [-print_gc_info]]
+ [-log_max_mem_usage log_file]
+ [(( -f | -frec | -fnoauto ) function_name)...
+ [-output_file_name output_file]
+ [-header_file_name header_file]
+ [-debug]
+ [-only_yielding_funs]
+ [-static_aux_funs]
+ input_c_file]]
+```
+
+* `-h`
+
+ Print help text
+
+* `-use_gc`
+
+ Use garbage collection. The garbage collection system assumes that
+ the C call stack consists of a continuous memory block and is
+ therefore not enabled by default even though this assumption is
+ valid on all major platforms. YCF does not reclaim any allocated
+ memory if the `-use_gc` flag is not set.
+
+* `-print_gc_info`
+
+ (For debugging) Print garbage collection information to `stderr`
+
+* `-log_max_mem_usage log_file`
+
+ (For debugging) Print the peak memory usage of the tool to the file
+ `log_file`
+
+* `-fnoauto function_name`
+
+ Generate a yieldable version of the function named
+ function_name. The user can use `YCF_YIELD()`,
+ `YCF_YIELD_NO_REDS()`, and `YCF_CONSUME_REDS(N)` to control
+ when and where the function should yield. See the section titled
+ "Special Statements and Macros" for more information.
+
+* `-f function_name`
+
+ Generate a yieldable version of the function named
+ `function_name`. The generated function automatically decrements the
+ reduction counter by one at the beginning of loop bodies and before
+ goto statements. The function yields automatically if the reduction
+ counter reaches a value that is zero or smaller after it has been
+ decremented.
+
+* `-frec function_name`
+
+ Same as the -f option with the exception that the generated function
+ also decrements one reduction before calls to other yieldable
+ functions and before returning. The function yields automatically if
+ the reduction counter reaches a value that is zero or smaller after
+ it has been decremented.
+
+* `-output_file_name output_file`
+
+ Output the generated code to a file named output_file. The output
+ is printed to standard output if this parameter is unspecified.
+
+* `-header_file_name header_file`
+
+ Generate a header file containing only declarations for the generated
+ functions and write the header file to the file named header_file.
+
+* `-debug`
+
+ Generate debug code that executes when a function yields. The debug
+ code searches the call stack of the yielding functions for pointers
+ to data that is allocated on the call stack. The program crashes
+ with an error message if any such pointer is found.
+
+* `-only_yielding_funs`
+
+ Print only the generated functions and struct declarations. The
+ default behavior is to insert the generated functions into a copy of
+ the input source file.
+
+* `-static_aux_funs`
+
+ Make the generated auxiliary functions static (i.e., local to the C
+ compilation unit)
+
+* `input_c_file`
+
+ The source file containing the functions that YCF shall create
+ yieldable versions of. YCF does not do any macro expansions. There
+ are several restrictions on the code that YCF can handle that are
+ described in the "Code Restrictions" section below.
+
+Generated Functions
+-------------------
+
+YCF generates three functions for each function name that it is
+given. These functions have the original function name as prefix and
+different suffixes. Descriptions of the functions that YCF generates
+follows below:
+
+
+```c
+
+/* Shall return a pointer to a memory block of size size bytes. */
+typedef void* (*ycf_yield_alloc_type) (size_t size ,void* ctx);
+/* Shall free the memory block which block points to. */
+typedef void (*ycf_yield_free_type) (void* block,void* ctx);
+
+return_type_of_orginal_fun
+original_fun_name_ycf_gen_yielding(
+ long * ycf_nr_of_reductions,
+ void ** ycf_yield_state,
+ void * ycf_extra_context,
+ ycf_yield_alloc_type ycf_yield_alloc,
+ ycf_yield_free_type ycf_yield_free,
+ void * ycf_yield_alloc_free_context,
+ size_t ycf_stack_alloc_size_or_max_size,
+ void* ycf_stack_alloc_data
+ paremeters_of_orginal_function);
+```
+
+The generated function with suffix `_ycf_gen_yielding` initiates the
+call of a yieldable function. Its parameters and return types are
+described below:
+
+* `return_type_of_orginal_fun`
+
+ The return type is the same as the return type of the original
+ function. The return value is the return value of the function if
+ the `_ycf_gen_yielding` function returns without yielding and is
+ uninitialized otherwise.
+
+* `long * ycf_nr_of_reductions`
+
+ (input/output parameter) Gives the yieldable function the number of
+ reductions it can consume before yielding and is also used to write
+ back the number of reductions that are left when the function
+ returns or yields.
+
+* `void ** ycf_yield_state`
+
+ (input/output parameter) Should be a pointer to a pointer to NULL
+ when the `_ycf_gen_yielding` function is called. The value pointed
+ to by ycf_yield_state is NULL when the `_ycf_gen_yielding` function
+ has returned if it did not yield and points to the yield state
+ otherwise.
+
+* `void * ycf_extra_context`
+
+ This parameter is useful if the yieldable function needs to access
+ data that may change when it resumes after having been yielded. The
+ extra context can be accessed from within the yieldable function
+ with the `YCF_GET_EXTRA_CONTEXT()` function.
+
+* `ycf_yield_alloc_type ycf_yield_alloc`
+
+ A memory allocator function that is used by the yieldable function
+ to allocate memory (e.g., to save the state when the function
+ yields).
+
+* `ycf_yield_free ycf_yield_free`
+
+ A memory free function that should free a memory block that has been
+ allocated with ycf_yield_alloc.
+
+* `void * ycf_yield_alloc_free_context`
+
+ A context that is passed as the second argument to `ycf_yield_alloc`
+ and `ycf_yield_free`.
+
+* `size_t ycf_stack_alloc_size_or_max_size`
+
+ The max number of total bytes that can be allocated with the special
+ allocator `YCF_STACK_ALLOC(n)`. This can be set to 0 if
+ `YCF_STACK_ALLOC(n)` is unused. See the documentation of
+ `YCF_STACK_ALLOC(n)` below for more information.
+
+* `void* ycf_stack_alloc_data`
+
+ A pointer to a data block that will be used by
+ `YCF_STACK_ALLOC(n)`. The value of `ycf_stack_alloc_data` should be
+ `NULL` or a pointer to a data block that is least
+ `ycf_stack_alloc_size_or_max_size` bytes large if
+ `YCF_STACK_ALLOC(n)` is used within the yieldable function or any
+ yieldable function that is called by the yieldable function. The
+ `ycf_yield_alloc` and `ycf_yield_free` functions will be used to
+ automatically alloc and free a data block when needed, if
+ `ycf_stack_alloc_data` is set to `NULL`. The value of
+ `ycf_stack_alloc_data` does not matter if `YCF_STACK_ALLOC(n)` is
+ unused.
+
+* `paremeters_of_orginal_function`
+
+ Parameters that the original function takes will be placed in the
+ end of the parameter list of the `ycf_gen_yielding` function.
+
+
+```c
+return_type_of_orginal_fun
+original_fun_name_ycf_gen_continue(
+ long * ycf_nr_of_reduction,
+ void ** ycf_yield_state,
+ void * ycf_extra_context);
+```
+
+The generated function with the suffix `_ycf_gen_continue` is used to
+resume a yielded function. The descriptions of the parameters and
+return type for the `_ycf_gen_yielding` function above are valid for
+the `_ycf_gen_continue` function as well, with the exception that the
+parameter `ycf_yield_state` should point to a pointer to a yield state
+(created in the previous call to `_ycf_gen_yielding` or
+`_ycf_gen_continue`).
+
+```c
+void original_fun_name_ycf_gen_destroy(void * ycf_yield_state);
+```
+
+The `_gen_destroy` function frees the state of a yieldable function
+that has been suspended. Note that the parameter `ycf_yield_state`
+points directly to the yield state, unlike the parameter of the
+`_ycf_gen_yielding` and `_ycf_gen_continue` functions with the same
+name.
+
+
+
+The `YCF_YIELD_CODE_GENERATED` Macro
+------------------------------------
+
+YCF also generates code that defines the macro
+`YCF_YIELD_CODE_GENERATED`. This macro may be useful if one wants to
+compile one version of a program with yieldable functions and another
+without yieldable functions.
+
+Special Statements and Macros
+-----------------------------
+
+Some special statements and macros can be used from within a yieldable
+function. Descriptions of those follow below:
+
+* `YCF_YIELD();`
+
+ The `YCF_YIELD();` statement sets the reduction counter to zero
+ and yields the function when it is executed.
+
+* `YCF_YIELD_NO_REDS();`
+
+ The `YCF_YIELD_NO_REDS();` statement yields the function
+ without changing the reduction counter when it is executed.
+
+* `YCF_CONSUME_REDS(N);`
+
+ The `YCF_CONSUME_REDS(N);` statement decrements the
+ reductions counter by N and yields if the reduction counter is less
+ than or equal to zero after the decrement.
+
+* `YCF_STACK_ALLOC(N)`
+
+ The `YCF_STACK_ALLOC(N)` macro uses an allocator that is included in
+ the code generated by YCF to allocate a block with `N` bytes and
+ return a pointer to these bytes. A block that has been allocated
+ with `YCF_STACK_ALLOC(N)` is automatically freed when the function
+ that allocated the block returns. Memory blocks that are allocated
+ with `YCF_STACK_ALLOC(N)` do not move when a yieldable function
+ yields and then resumes again. In contrast, data that is allocated
+ directly on the call stack may move when a function yields and
+ resumes. `YCF_STACK_ALLOC(N)` can thus be useful if one wants to
+ port C code that has variables that point to data that is allocated
+ on the call stack. The parameters `ycf_stack_alloc_size_or_max_size`
+ and `ycf_stack_alloc_data` of the `_ycf_gen_yielding` function need
+ to be set correctly if `YCF_STACK_ALLOC(N)` is used. Please see the
+ description of the `_ycf_gen_yielding` function in the "Generated
+ Functions" section above for details about those parameters. Notice
+ also that the `-debug` flag that is described in the "Command Line
+ Parameters" section above can be useful when one wants to find out
+ if a function points to data that is allocated on the call stack of
+ a yieldable function.
+
+* `YCF_GET_EXTRA_CONTEXT()`
+
+ The `YCF_GET_EXTRA_CONTEXT()` macro returns the value of the
+ `ycf_extra_context` parameter that was passed to the latest call of
+ one of the corresponding `_ycf_gen_yielding` or `_ycf_gen_continue`
+ functions. See the "Generated Functions" section above for
+ information about the parameters of `_ycf_gen_yielding` and
+ `_ycf_gen_continue` functions.
+
+* `YCF_NR_OF_REDS_LEFT()`
+
+ The `YCF_NR_OF_REDS_LEFT()` macro returns the current value of
+ the reduction counter (a value of type `long`).
+
+* `YCF_SET_NR_OF_REDS_LEFT(NEW_NR_OF_REDS_LEFT)`
+
+ The `YCF_SET_NR_OF_REDS_LEFT(NEW_NR_OF_REDS_LEFT)` macro sets
+ the value that the reduction counter (which stores a value of type
+ `long`) to `NEW_NR_OF_REDS_LEFT`.
+
+* `YCF_MAX_NR_OF_REDS`
+
+ The `YCF_MAX_NR_OF_REDS` macro returns the maximum value that the
+ reduction counter may have.
+
+Code Restrictions
+-----------------
+
+YCF cannot parse all valid C code. The code restrictions that
+yieldable functions need to follow are described below. It is
+recommended to check that the generated code is correct.
+
+* **Declarations**
+
+ Variable declarations and parameters of yieldable functions need to
+ be in the following form:
+
+ ```
+ "(optional) type descriptor (i.e., struct, union or enum)"
+
+ "type name"
+
+ "(optional) one or more star characters (i.e., *)"
+
+ "variable name"
+
+ "(optional) one or more square brackets with a number inside (e.g, [3])"
+
+ "(optional) one or more empty square brackets (e.g, [])"
+
+ "(optional) equal sign followed by an expression (automatic array
+ initialization and struct initialization of the form
+ {.filed_name=value...} are not allowed)"
+
+ "semicolon"
+ ```
+
+ Here are some examples of declarations that are **correct**:
+
+ ```c
+ int var1;
+ int var2 = 1;
+ int var3 = var2 + 1;
+ int var4 = function(var3);
+ int * var5 = malloc(sizeof(int*));
+ int ** var6 = malloc(sizeof(int*)*10);
+ int ***** var7;
+ struct struct_name var8;
+ struct struct_name var9 = function2();
+ double var10[128];
+ double var11[128][];
+ double * var12[128];
+ ```
+
+ Here are examples of declarations that are **incorrect**:
+
+ ```c
+ int var1, var2;
+ int var1 = 1, var2 = 10;
+ void (*printer_t)(int);
+ ```
+
+ Note that one has to use a `typedef` to be able to declare a
+ function pointer variable.
+
+* **Pointers**
+
+ Pointers to call-stack-allocated data are not allowed. The
+ `YCF_YIELD_ALLOC(N)` function, which is described in the "Special
+ Statements and Macros" section above, can be used to work around
+ this limitation. The `-debug` flag that is described in the "Command
+ Line Parameters" section above, can be useful when one wants to find
+ out if a yieldable function points to call-stack-allocated data.
+
+
+* **Macros**
+
+ YCF does not expand macros so macros in functions that YCF
+ transforms should not "hide" variables or any other code that is
+ relevant for yielding.
+
+* **Calls to a Yieldable Function from Another Yieldable Function**
+
+ Calls to a yieldable function from another yieldable function need
+ to be in a form that YCF recognizes. Such calls need to be in one of
+ the following forms:
+
+ * As a separate statement:
+
+ Examples:
+ ```c
+ my_fun(my_param_expression + 1, 10);
+ my_fun2();
+ ```
+
+ * A separate assignment statement to a variable. The function call
+ expression may be negated but is not allowed to be nested in other
+ types of expressions.
+
+ Examples of **correct** ways of calling yieldable functions:
+ ```c
+ int var_name_1 = my_fun();
+ int var_name_2 = !my_fun();
+ var_name_3 = my_fun();
+ var_name_4 = !my_fun();
+ ```
+
+ Examples of **incorrect** ways of calling yieldable functions:
+ ```c
+ int var_name_1 = (my_fun());
+ var_name_2 = 1 + my_fun();
+ t->name = my_fun();
+ *ptr = my_fun();
+ ```
+
+ * As the expression of `while`-statements, `do-while`-statements or
+ 'if`-statements:
+
+ Examples of **correct** ways of calling yieldable functions:
+ ```c
+ if(my_fun()) printf("hej\n");
+ if(0) else if(my_fun()) printf("hej\n");
+ while(!my_fun()) printf("hej\n");
+ do { printf("hej\n"); } while(my_fun());
+ var_name_3 = my_fun();
+ var_name_4 = !my_fun();
+ ```
+
+ Examples of **incorrect** ways of calling yieldable functions:
+ ```
+ if(3+my_fun()) printf("hej\n");
+ if(hej=my_fun()) printf("hej\n");
+ ```
+
+ YCF ignores calls to yieldable functions that are not in any of
+ the forms described above, so it is important to check the
+ generated source code.
+
+Hooks
+-----
+
+It is possible to insert special hooks in yieldable functions. Hooks
+execute when certain events are happening. Hooks may read and write to
+variables (changes to variables are visible after the code block has
+executed). Hooks can be placed anywhere one can place a normal
+statement within the function body. There are two ways to write hooks:
+
+**Hook Style 1:**
+
+```c
+ YCF_SPECIAL_CODE_START(ON_EVENT_NAME);
+ printf("This will be printed when EVENT_NAME is happening\n");
+ YCF_SPECIAL_CODE_END();
+```
+
+**Hook Style 2:**
+
+```c
+ /*special_code_start:ON_EVENT_NAME*/
+ if(0){
+ printf("This will be printed when EVENT_NAME is happening\n");
+ }
+ /*special_code_end*/
+```
+
+The following hook events are currently available:
+
+* `ON_SAVE_YIELD_STATE`
+
+ Triggered when the function yields.
+
+* `ON_RESTORE_YIELD_STATE`
+
+ Triggered before a function resumes after a yield.
+
+* `ON_DESTROY_STATE`
+
+ Triggered if and when the corresponding `_ycf_gen_destroy` function
+ is executing.
+
+* `ON_DESTROY_STATE_OR_RETURN`
+
+ Triggered if and when the corresponding `_ycf_gen_destroy` function
+ for the function is executing or when the function is returning.
+
+* `ON_RETURN`
+
+ Triggered when the function is returning.
+
+License
+-------
+
+Yielding C Fun is released under the [Apache License
+2.0](http://www.apache.org/licenses/LICENSE-2.0).
+
+
+> Copyright Ericsson AB and Kjell Winblad 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.
+
+
+
+[1]: http://swtch.com/libtask/ "libtask"
+[2]: http://xmailserver.org/libpcl.html
+[3]: https://web.archive.org/web/20060110123338/http://www.goron.de/~froese/coro/
+[4]: https://github.com/halayli/lthread
+[5]: http://dekorte.com/projects/opensource/libcoroutine/
+[6]: http://code.google.com/p/libconcurrency/libconcurrency
+[7]: http://software.schmorp.de/pkg/libcoro.html
+[8]: https://github.com/Adaptv/ribs2
+[9]: http://libdill.org/
+[10]: https://github.com/hnes/libaco
+[11]: https://byuu.org/library/libco/
+[12]: http://www.chiark.greenend.org.uk/~sgtatham/coroutines.html
+[13]: https://github.com/jsseldenthuis/coroutine
diff --git a/erts/lib_src/yielding_c_fun/TODO b/erts/lib_src/yielding_c_fun/TODO
new file mode 100644
index 0000000000..f3ded2dd22
--- /dev/null
+++ b/erts/lib_src/yielding_c_fun/TODO
@@ -0,0 +1,4 @@
+* Refactor and beautify the code so it does not look like spaghetti
+* Handle "automatic" array size initialization
+* Handle struct initialization (e.g., struct t my_struct = {.field = hej});
+* Print warning or error message when calling yieldable function from a position that YCF can't handle
diff --git a/erts/lib_src/yielding_c_fun/bin/yielding_c_fun b/erts/lib_src/yielding_c_fun/bin/yielding_c_fun
new file mode 100755
index 0000000000..87745e7aad
--- /dev/null
+++ b/erts/lib_src/yielding_c_fun/bin/yielding_c_fun
@@ -0,0 +1,42 @@
+#!/bin/bash
+
+
+#Code to find directory of this file from https://stackoverflow.com/questions/59895/get-the-source-directory-of-a-bash-script-from-within-the-script-itself
+SOURCE="${BASH_SOURCE[0]}"
+while [ -h "$SOURCE" ]; do # resolve $SOURCE until the file is no longer a symlink
+ DIR="$( cd -P "$( dirname "$SOURCE" )" >/dev/null 2>&1 && pwd )"
+ SOURCE="$(readlink "$SOURCE")"
+ [[ $SOURCE != /* ]] && SOURCE="$DIR/$SOURCE" # if $SOURCE was a relative symlink, we need to resolve it relative to the path where the symlink file was located
+done
+DIR="$( cd -P "$( dirname "$SOURCE" )" >/dev/null 2>&1 && pwd )"
+
+
+FOUND="no"
+
+for FILE in "$@"
+do
+ if [ "${FILE: -2}" == ".c" ]
+ then
+ if ! cc -c $FILE -o $DIR/a.out ; then
+ echo "$0: error: Could not compile file with cc"
+ exit 1
+ fi
+ rm $DIR/a.out
+ if [ $1 == "-rr" ]
+ then
+ rr record $DIR/yielding_c_fun.bin "${@:2}" > $DIR/TMP_OUT_WITH_RR
+ # rr does not print when stdout is redirected
+ cat $DIR/TMP_OUT_WITH_RR
+ rm $DIR/TMP_OUT_WITH_RR
+ else
+ $DIR/yielding_c_fun.bin $@
+ fi
+ fi
+ FOUND="yes"
+done
+
+if [ $FOUND == "no" ]
+then
+ echo "$0: error: Expected a file name with .c ending"
+ exit 1
+fi
diff --git a/erts/lib_src/yielding_c_fun/doc/thread_tutorial.md b/erts/lib_src/yielding_c_fun/doc/thread_tutorial.md
new file mode 100644
index 0000000000..c818032bc5
--- /dev/null
+++ b/erts/lib_src/yielding_c_fun/doc/thread_tutorial.md
@@ -0,0 +1,222 @@
+Yielding C Fun Thread Example Tutorial
+======================================
+
+This tutorial goes through how Yielding C Fun can be used to simulate
+multi-threading in a single thread. You can find the source code that
+we use in the tutorial below or in
+[../test/examples/thread_example.c](../test/examples/thread_example.c).
+
+```c
+#include <stdio.h>
+#include <stdlib.h>
+
+#define YCF_YIELD_NO_REDS()
+
+static int f_2(char* name, int n) {
+ for(int y = 0; y < 2; y++) {
+ printf("%s f_2: y=%d\n", name, y);
+ }
+ return n*2;
+}
+
+static void f_1(char* name, int x) {
+ YCF_YIELD_NO_REDS();
+ while (x > 0) {
+ int f_2_ret = f_2(name, x);
+ printf("%s f_1: x=%d f_2_ret=%d\n", name, x, f_2_ret);
+ x--;
+ }
+ printf("%s f_1: DONE\n", name);
+}
+
+static void* ycf_alloc(size_t size, void* context){
+ (void)context;
+ return malloc(size);
+}
+
+static void ycf_free(void* data, void* context){
+ (void)context;
+ free(data);
+}
+
+int main( int argc, const char* argv[] ) {
+#ifdef YCF_YIELD_CODE_GENERATED
+ long t1_nr_of_reds = 1;
+ void* t1_state = NULL;
+ long t2_nr_of_reds = 2;
+ void* t2_state = NULL;
+ long t3_nr_of_reds = 1000;
+ void* t3_state = NULL;
+ /* Start t1, t2 and t3*/
+ f_1_ycf_gen_yielding(&t1_nr_of_reds, &t1_state, NULL,
+ ycf_alloc, ycf_free, NULL, 0, NULL,
+ "t1", 2);
+ f_1_ycf_gen_yielding(&t2_nr_of_reds, &t2_state, NULL,
+ ycf_alloc, ycf_free, NULL, 0, NULL,
+ "t2", 4);
+ f_1_ycf_gen_yielding(&t3_nr_of_reds, &t3_state, NULL,
+ ycf_alloc, ycf_free, NULL, 0, NULL,
+ "t3", 2);
+ printf("THREADS STARTED\n");
+ /* Execute t1, t2 and t3*/
+ while (t1_state != NULL ||
+ t2_state != NULL ||
+ t3_state != NULL) {
+ t1_nr_of_reds = 1;
+ t2_nr_of_reds = 2;
+ if (t1_state != NULL) {
+ printf("SCHEDULING THREAD: t1\n");
+ f_1_ycf_gen_continue(&t1_nr_of_reds, &t1_state, NULL);
+ if (t1_state == NULL) {
+ printf("THREAD t1 DONE (number of reductions left = %ld)\n",
+ t1_nr_of_reds);
+ }
+ }
+ if (t2_state != NULL) {
+ printf("SCHEDULING THREAD: t2\n");
+ f_1_ycf_gen_continue(&t2_nr_of_reds, &t2_state, NULL);
+ if (t2_state == NULL) {
+ printf("THREAD t2 DONE (number of reductions left = %ld)\n",
+ t2_nr_of_reds);
+ }
+ }
+ if (t3_state != NULL) {
+ printf("SCHEDULING THREAD: t3\n");
+ f_1_ycf_gen_continue(&t3_nr_of_reds, &t3_state, NULL);
+ if (t3_state == NULL) {
+ printf("THREAD t3 DONE (number of reductions left = %ld)\n",
+ t3_nr_of_reds);
+ }
+ }
+ }
+#endif
+ (void)f_1;
+ printf("DONE\n");
+ return 0;
+}
+```
+
+To run this example, you need to compile YCF itself, if you have not
+done so already:
+
+ cd directory_where_the_ycf_source_code_is_located
+ YCF_ROOT=`pwd`
+ make
+
+Now, you can transform `test/examples/thread_example.c` by executing
+the following command:
+
+ "$YCF_ROOT"/bin/yielding_c_fun.bin \
+ -f f_1 -f f_2 \
+ -output_file_name modified_thread_example.c \
+ "$YCF_ROOT"/test/examples/thread_example.c
+
+A new file should now exist in your current directory called
+`modified_thread_example.c`. This file contains a transformed
+version of `test/examples/thread_example.c`. The parameters `-f
+f_1` and `-f f_2` tells YCF to generate yieldable versions of the
+functions named `f_1` and `f_2`.
+
+Before you inspect the generated file to see what the tool has done,
+it is smart to format the generated source code with `clang-format` or
+some other tool to make the code more readable:
+
+ clang-format -i modified_thread_example.c
+
+You can now open the generated source code in your favorite editor:
+
+ emacs modified_thread_example.c &
+
+The yieldable functions get the suffix `_ycf_gen_yielding`. For
+example, the yielding version of `f_1` is called
+`f_1_ycf_gen_yielding`. As you can see, several parameters have been
+added to the yieldable versions of `f_1`. We explain the first two of
+these parameters here. [The main documentation of YCF](../README.md)
+explains the rest of the parameters. The first parameter is an
+input/output parameter that tells the yieldable function how many
+reductions that can be "consumed" before it yields. The first
+parameter can also be used by the caller to get the number of
+reductions that have been consumed. The second parameter is also an
+input/output parameter that should be a pointer to a `NULL` pointer
+when the yieldable function is first started. The pointer that is
+pointed to by the second parameter is set to a value which is
+different from `NULL` when the function yields.
+
+Let us now compile and run the generated source code so we can see
+what is happening:
+
+
+ cc -g modified_thread_example.c
+
+```
+$ ./a.out
+THREADS STARTED
+SCHEDULING THREAD: t1
+SCHEDULING THREAD: t2
+SCHEDULING THREAD: t3
+t3 f_2: y=0
+t3 f_2: y=1
+t3 f_1: x=2 f_2_ret=4
+t3 f_2: y=0
+t3 f_2: y=1
+t3 f_1: x=1 f_2_ret=2
+t3 f_1: DONE
+THREAD t3 DONE (number of reductions left = 994)
+SCHEDULING THREAD: t1
+SCHEDULING THREAD: t2
+t2 f_2: y=0
+t2 f_2: y=1
+t2 f_1: x=4 f_2_ret=8
+SCHEDULING THREAD: t1
+t1 f_2: y=0
+SCHEDULING THREAD: t2
+t2 f_2: y=0
+SCHEDULING THREAD: t1
+t1 f_2: y=1
+t1 f_1: x=2 f_2_ret=4
+SCHEDULING THREAD: t2
+t2 f_2: y=1
+t2 f_1: x=3 f_2_ret=6
+SCHEDULING THREAD: t1
+SCHEDULING THREAD: t2
+t2 f_2: y=0
+t2 f_2: y=1
+t2 f_1: x=2 f_2_ret=4
+SCHEDULING THREAD: t1
+t1 f_2: y=0
+SCHEDULING THREAD: t2
+t2 f_2: y=0
+SCHEDULING THREAD: t1
+t1 f_2: y=1
+t1 f_1: x=1 f_2_ret=2
+t1 f_1: DONE
+THREAD t1 DONE (number of reductions left = 1)
+SCHEDULING THREAD: t2
+t2 f_2: y=1
+t2 f_1: x=1 f_2_ret=2
+t2 f_1: DONE
+THREAD t2 DONE (number of reductions left = 2)
+DONE
+$
+```
+
+We can see that all three calls to the yieldable version of `f_1`
+yield before printing anything. This is due to the
+`YCF_YIELD_NO_REDS()` statement at the beginning of `f_1`'s
+body. We can also see that the "thread" named `t3` runs to completion
+without yielding when we call `f_1_ycf_gen_continue` with `t3_state`
+in the while loop and that `t3` consumed (1000-994=6) reductions. This
+is expected as we gave `t3` 1000 reductions when we started it with
+the `f_1_ycf_gen_yielding` function. The "threads" `t1` and `t2` get
+much fewer reductions (i.e., 1 and 2) each time they get to execute
+and they are therefore interleaved.
+
+You can now experiment with other transformation options (e.g.,
+`-frec` and `-fnoauto`) and the special statements that you can use
+inside functions that can yield. All these options and statements are
+documented [here](../README.md).
+
+The best way to figure out how YCF works under the hood is probably to
+step through the transformed program in a debugger.
+
+Good Luck! \ No newline at end of file
diff --git a/erts/lib_src/yielding_c_fun/lib/simple_c_gc/.gitignore b/erts/lib_src/yielding_c_fun/lib/simple_c_gc/.gitignore
new file mode 100644
index 0000000000..e72b2fe033
--- /dev/null
+++ b/erts/lib_src/yielding_c_fun/lib/simple_c_gc/.gitignore
@@ -0,0 +1,11 @@
+# Object files
+*.o
+*.ko
+*.obj
+*.elf
+*~
+GPATH
+GRTAGS
+GTAGS
+test.bin
+.tmp_out \ No newline at end of file
diff --git a/erts/lib_src/yielding_c_fun/lib/simple_c_gc/.misc/clang_blacklist.txt b/erts/lib_src/yielding_c_fun/lib/simple_c_gc/.misc/clang_blacklist.txt
new file mode 100644
index 0000000000..d212057d10
--- /dev/null
+++ b/erts/lib_src/yielding_c_fun/lib/simple_c_gc/.misc/clang_blacklist.txt
@@ -0,0 +1,10 @@
+
+[memory]
+
+fun:scgc_mark_reachable_objects_in_region
+fun:reverse_bits
+
+[address]
+
+fun:scgc_mark_reachable_objects_in_region
+fun:reverse_bits
diff --git a/erts/lib_src/yielding_c_fun/lib/simple_c_gc/GIT_VERSION b/erts/lib_src/yielding_c_fun/lib/simple_c_gc/GIT_VERSION
new file mode 100644
index 0000000000..8cd6ceac63
--- /dev/null
+++ b/erts/lib_src/yielding_c_fun/lib/simple_c_gc/GIT_VERSION
@@ -0,0 +1,73 @@
+origin https://github.com/kjellwinblad/simple_c_gc.git (fetch)
+origin https://github.com/kjellwinblad/simple_c_gc.git (push)
+commit 76577516b6e9bd8e8f647d869fb19361f42f9f9f
+Author: Kjell Winblad <kjellwinblad@gmail.com>
+Date: Wed Sep 25 16:04:40 2019 +0200
+
+ Fix more Microsoft VS C/C++ compiler errors
+
+commit 4b2734f051ca1386bb970af6b0308e5f2d338403
+Author: Kjell Winblad <kjellwinblad@gmail.com>
+Date: Tue Sep 24 16:36:48 2019 +0200
+
+ Make compatible with Microsoft VS C/C++ compiler and fix make test
+
+commit 41e6f3558756d9b6da44ca5247049c23bceadad9
+Author: Kjell Winblad <kjellwinblad@gmail.com>
+Date: Mon Sep 23 16:25:55 2019 +0200
+
+ Small fix
+
+commit 8fbd03f880a1eb741462c395e689d844f8d58e06
+Author: Kjell Winblad <kjellwinblad@gmail.com>
+Date: Mon Sep 23 16:13:56 2019 +0200
+
+ Fix compile warnings when compiling on a 32-bit system
+
+commit fc051d92bbef9ad1a22f855bc24b46d4efadda93
+Author: Kjell Winblad <kjellwinblad@gmail.com>
+Date: Tue Sep 10 15:14:20 2019 +0200
+
+ Add clang-tidy Makefile target
+
+commit c6c3c1adc24f500472c22aad7f467d0a925108f0
+Author: Kjell Winblad <kjellwinblad@gmail.com>
+Date: Tue Sep 10 15:07:39 2019 +0200
+
+ Add undefined behavior sanitizer and fix undefined behavior
+
+commit c734e684030a24ae694ed1fe7477c0eb5719bc12
+Author: Kjell Winblad <kjellwinblad@gmail.com>
+Date: Mon Sep 9 09:06:19 2019 +0200
+
+ Improve Makefile and add info support
+
+commit 286c6ace1a71f2b021b76b602d5bfe4813f0b519
+Author: Kjell Winblad <kjellwinblad@gmail.com>
+Date: Thu Sep 5 12:08:19 2019 +0200
+
+ Make clang_format target .PHONY
+
+commit eacdda4f9c71b5d917111bb9285671739d1cb671
+Author: Kjell Winblad <kjellwinblad@gmail.com>
+Date: Thu Sep 5 11:33:03 2019 +0200
+
+ Add clang-format make target and reformat files
+
+commit c617afa556535620633117bdd6cc232ecc8c62f2
+Author: Kjell Winblad <kjellwinblad@gmail.com>
+Date: Thu Sep 5 11:07:17 2019 +0200
+
+ Small fixes
+
+commit fb043e17d6e63100ca89ec41c34f667d35da7fb8
+Author: Kjell Winblad <kjellwinblad@gmail.com>
+Date: Thu Sep 5 10:55:18 2019 +0200
+
+ Fix compiler warning and make c99 compatible
+
+commit d4b8333a49e62d8ffdbf60ab8382de39209891e2
+Author: Kjell Winblad <kjellwinblad@gmail.com>
+Date: Sat Aug 17 05:59:27 2019 +0200
+
+ First version of Simple C GC
diff --git a/erts/lib_src/yielding_c_fun/lib/simple_c_gc/LICENSE b/erts/lib_src/yielding_c_fun/lib/simple_c_gc/LICENSE
new file mode 100644
index 0000000000..261eeb9e9f
--- /dev/null
+++ b/erts/lib_src/yielding_c_fun/lib/simple_c_gc/LICENSE
@@ -0,0 +1,201 @@
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ 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.
diff --git a/erts/lib_src/yielding_c_fun/lib/simple_c_gc/Makefile b/erts/lib_src/yielding_c_fun/lib/simple_c_gc/Makefile
new file mode 100644
index 0000000000..1b326a9988
--- /dev/null
+++ b/erts/lib_src/yielding_c_fun/lib/simple_c_gc/Makefile
@@ -0,0 +1,121 @@
+IDIR = .
+CC = cc
+
+ifdef MODERN_CC
+ EXTRA_C_FLAGS = -g -O03 -std=c99 -pedantic -Wall
+endif
+
+ifdef CC_32_BIT
+ EXTRA_C_FLAGS = -m32 -g -O03 -std=c99 -pedantic -Wall
+endif
+
+ifdef ADD_SAN
+ CC = clang
+ EXTRA_C_FLAGS = -std=c99 -Wall -pedantic -g -O00 -fsanitize-blacklist=.misc/clang_blacklist.txt -fsanitize=address -fno-omit-frame-pointer
+ USE_GC_STRING = -use_gc
+endif
+
+ifdef MEM_SAN
+ CC = clang
+ EXTRA_C_FLAGS = -std=c99 -Wall -pedantic -g -O00 -fsanitize-blacklist=.misc/clang_blacklist.txt -fsanitize=memory -fno-omit-frame-pointer
+endif
+
+ifdef UB_SAN
+ CC = clang
+ EXTRA_C_FLAGS = -std=c99 -Wall -pedantic -g -O00 -fsanitize-blacklist=.misc/clang_blacklist.txt -fsanitize=undefined -fno-omit-frame-pointer
+endif
+
+CFLAGS = -I$(IDIR) $(EXTRA_C_FLAGS)
+
+ODIR = .
+LDIR =
+
+_DEPS = bitreversal.h simple_c_gc.h chained_hash_set.h sorted_list_set.h
+DEPS = $(patsubst %,$(IDIR)/%,$(_DEPS))
+
+_OBJ = simple_c_gc.o test.o
+OBJ = $(patsubst %,$(ODIR)/%,$(_OBJ))
+C_FILES = $(patsubst %.o,%.c,$(_OBJ))
+
+$(ODIR)/%.o: %.c $(DEPS)
+ $(CC) -c -o $@ $< $(CFLAGS)
+
+test.bin: $(OBJ)
+ $(CC) -o $@ $^ $(CFLAGS)
+
+
+.PHONY: clean test run_test_continusly CMakeLists.txt cmake_compile clang_format test_add_san test_ub_san test_mem_san test_sanitizers test_modern_cc test_valgrind
+
+test: test.bin
+ ./test.bin ;\
+ RESULT=$$? &&\
+ (exit $$RESULT) &&\
+ printf "\n\n\033[0;32mALL TESTS PASSED!\033[0m\n\n\n" ||\
+ printf "\n\n\033[0;31mTEST FAILED!\033[0m\n\n\n" &&\
+ exit $$RESULT
+
+test_valgrind:
+ make clean && \
+ make EXTRA_C_FLAGS="-g -O01" && \
+ valgrind --undef-value-errors=no ./test.bin ;\
+ RESULT=$$? &&\
+ (exit $$RESULT) &&\
+ printf "\n\n\033[0;32mALL TESTS PASSED!\033[0m\n\n\n" ||\
+ printf "\n\n\033[0;31mTEST FAILED!\033[0m\n\n\n" &&\
+ exit $$RESULT
+
+test_add_san:
+ make clean && \
+ make ADD_SAN=1 test
+
+test_mem_san:
+ make clean && \
+ make MEM_SAN=1 test
+
+test_ub_san:
+ make clean && \
+ make UB_SAN=1 test
+
+test_sanitizers:
+ make test_add_san && \
+ make test_mem_san && \
+ make test_ub_san
+
+test_modern_cc:
+ make clean && \
+ make MODERN_CC=1 test
+
+test_32_bit:
+ make clean && \
+ make CC_32_BIT=1 test && \
+ make clean
+
+test_all:
+ make test_valgrind && \
+ make test_sanitizers && \
+ make test_modern_cc && \
+ make test_32_bit
+
+run_test_continusly:
+ inotifywait -e close_write,moved_to,create -m ./*.c ./*.h | while read -r directory events filename; do gtags ; make test ; done
+
+CMakeLists.txt: $(C_FILES)
+ echo "cmake_minimum_required (VERSION 2.6)" > CMakeLists.txt
+ echo "project (SIMPLE_C_GC)" >> CMakeLists.txt
+ echo "add_executable(cmake.out" >> CMakeLists.txt
+ echo $(C_FILES) >> CMakeLists.txt
+ echo ")" >> CMakeLists.txt
+
+
+cmake_compile: CMakeLists.txt
+ mkdir cmake_mkdir || true
+ cd cmake_mkdir && cmake ..
+
+clang_tidy:
+ ls *.c | xargs -I{} -n1 clang-tidy -warnings-as-errors=* {} -- $(CFLAGS)
+
+clang_format:
+ clang-format -style="{BasedOnStyle: LLVM}" -i *.c *.h
+
+clean:
+ rm -f $(ODIR)/*.o *~ core $(IDIR)/*~ test.bin CMakeLists.txt
diff --git a/erts/lib_src/yielding_c_fun/lib/simple_c_gc/README.md b/erts/lib_src/yielding_c_fun/lib/simple_c_gc/README.md
new file mode 100644
index 0000000000..c1f6834505
--- /dev/null
+++ b/erts/lib_src/yielding_c_fun/lib/simple_c_gc/README.md
@@ -0,0 +1,69 @@
+Simple C GC
+===========
+
+This is the readme file for Simple C GC. Simple C GC is a simple
+garbage collection system for the C programming language inspired by
+[Daniel Holden's article about Cello's garbage collection
+system](http://libcello.org/learn/garbage-collection).
+
+Usage
+-----
+
+The interface of Simple C GC consists of only two functions:
+
+```C
+/**
+ * This function starts code that will be garbage collected
+ *
+ * @param main The function that will be started and garbage collected
+ * @param argc Passed to main
+ * @param argv Passed to main
+ * @param my_malloc Simple C GC will use this to allocated memory
+ * @param my_free Simple C GC will use this to free memory
+ */
+int scgc_start_gced_code(int (*main)(int, char *[]),
+ int argc,
+ char *argv[],
+ void* (*my_malloc)( size_t ),
+ void (*my_free)( void* ));
+
+/**
+ * Allocate a new memory block that will be garbage collected
+ *
+ * @param size The size of the new memory block
+ */
+void* scgc_new(size_t size);
+```
+
+An example that illustrates how these functions can be used in
+practice is located in the `test.c` file.
+
+
+Notes
+-----
+
+The garbage collector assumes that all data that should be garbage
+collected is pointed to directly or indirectly from the "C stack" and
+that the "C stack" is implemented as a continuous block of memory.
+
+Compile and Test
+----------------
+
+ make test
+
+License
+-------
+
+ Copyright 2019 Kjell Winblad (kjellwinblad@gmail.com, http://winsh.me)
+
+ 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. \ No newline at end of file
diff --git a/erts/lib_src/yielding_c_fun/lib/simple_c_gc/chained_hash_set.h b/erts/lib_src/yielding_c_fun/lib/simple_c_gc/chained_hash_set.h
new file mode 100644
index 0000000000..ee508e5ccc
--- /dev/null
+++ b/erts/lib_src/yielding_c_fun/lib/simple_c_gc/chained_hash_set.h
@@ -0,0 +1,307 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright 2019 Kjell Winblad (kjellwinblad@gmail.com, http://winsh.me).
+ * 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%
+ */
+
+/*
+ *
+ * Author: Kjell Winblad
+ *
+ */
+
+#ifndef CHAINED_HASH_SET_H
+#define CHAINED_HASH_SET_H
+
+
+#if defined(_MSC_VER)
+#define inline __inline
+#endif
+
+#include "sorted_list_set.h"
+#include <stdint.h>
+
+#define CHAIN_LENGHT_EXPAND_THRESHOLD 2
+#define CHAIN_LENGHT_SHRINK_THRESHOLD 0.5
+/*Must be power of two*/
+#define INITIAL_NUMBER_OF_BUCKETS 4
+
+typedef struct {
+ unsigned int keyPosition;
+ void *(*extract_key)(void *v, int keyPos);
+ unsigned int (*hash_key)(void *key);
+ bool (*are_equal)(void *v1, void *v2);
+ char *(*to_string)(void *v1);
+ void *(*malloc)(size_t size);
+ void (*free)(void *ptr);
+ unsigned int numberOfBuckets;
+ unsigned int expandTreshold;
+ unsigned int shrinkTreshold;
+ unsigned int size;
+ SortedListSetNode **buckets;
+} ChainedHashSet;
+
+/*
+ * The reverse_bits function is inspired by:
+ * https://stackoverflow.com/questions/746171/efficient-algorithm-for-bit-reversal-from-msb-lsb-to-lsb-msb-in-c
+ */
+static const unsigned char BitReverseTable256[] = {
+ 0x00, 0x80, 0x40, 0xC0, 0x20, 0xA0, 0x60, 0xE0, 0x10, 0x90, 0x50, 0xD0,
+ 0x30, 0xB0, 0x70, 0xF0, 0x08, 0x88, 0x48, 0xC8, 0x28, 0xA8, 0x68, 0xE8,
+ 0x18, 0x98, 0x58, 0xD8, 0x38, 0xB8, 0x78, 0xF8, 0x04, 0x84, 0x44, 0xC4,
+ 0x24, 0xA4, 0x64, 0xE4, 0x14, 0x94, 0x54, 0xD4, 0x34, 0xB4, 0x74, 0xF4,
+ 0x0C, 0x8C, 0x4C, 0xCC, 0x2C, 0xAC, 0x6C, 0xEC, 0x1C, 0x9C, 0x5C, 0xDC,
+ 0x3C, 0xBC, 0x7C, 0xFC, 0x02, 0x82, 0x42, 0xC2, 0x22, 0xA2, 0x62, 0xE2,
+ 0x12, 0x92, 0x52, 0xD2, 0x32, 0xB2, 0x72, 0xF2, 0x0A, 0x8A, 0x4A, 0xCA,
+ 0x2A, 0xAA, 0x6A, 0xEA, 0x1A, 0x9A, 0x5A, 0xDA, 0x3A, 0xBA, 0x7A, 0xFA,
+ 0x06, 0x86, 0x46, 0xC6, 0x26, 0xA6, 0x66, 0xE6, 0x16, 0x96, 0x56, 0xD6,
+ 0x36, 0xB6, 0x76, 0xF6, 0x0E, 0x8E, 0x4E, 0xCE, 0x2E, 0xAE, 0x6E, 0xEE,
+ 0x1E, 0x9E, 0x5E, 0xDE, 0x3E, 0xBE, 0x7E, 0xFE, 0x01, 0x81, 0x41, 0xC1,
+ 0x21, 0xA1, 0x61, 0xE1, 0x11, 0x91, 0x51, 0xD1, 0x31, 0xB1, 0x71, 0xF1,
+ 0x09, 0x89, 0x49, 0xC9, 0x29, 0xA9, 0x69, 0xE9, 0x19, 0x99, 0x59, 0xD9,
+ 0x39, 0xB9, 0x79, 0xF9, 0x05, 0x85, 0x45, 0xC5, 0x25, 0xA5, 0x65, 0xE5,
+ 0x15, 0x95, 0x55, 0xD5, 0x35, 0xB5, 0x75, 0xF5, 0x0D, 0x8D, 0x4D, 0xCD,
+ 0x2D, 0xAD, 0x6D, 0xED, 0x1D, 0x9D, 0x5D, 0xDD, 0x3D, 0xBD, 0x7D, 0xFD,
+ 0x03, 0x83, 0x43, 0xC3, 0x23, 0xA3, 0x63, 0xE3, 0x13, 0x93, 0x53, 0xD3,
+ 0x33, 0xB3, 0x73, 0xF3, 0x0B, 0x8B, 0x4B, 0xCB, 0x2B, 0xAB, 0x6B, 0xEB,
+ 0x1B, 0x9B, 0x5B, 0xDB, 0x3B, 0xBB, 0x7B, 0xFB, 0x07, 0x87, 0x47, 0xC7,
+ 0x27, 0xA7, 0x67, 0xE7, 0x17, 0x97, 0x57, 0xD7, 0x37, 0xB7, 0x77, 0xF7,
+ 0x0F, 0x8F, 0x4F, 0xCF, 0x2F, 0xAF, 0x6F, 0xEF, 0x1F, 0x9F, 0x5F, 0xDF,
+ 0x3F, 0xBF, 0x7F, 0xFF};
+
+static inline uint32_t reverse_bits(uint32_t v) {
+ return (((uint32_t)BitReverseTable256[v & 0xff]) << 24) |
+ (((uint32_t)BitReverseTable256[(v >> 8) & 0xff]) << 16) |
+ (((uint32_t)BitReverseTable256[(v >> 16) & 0xff]) << 8) |
+ (((uint32_t)BitReverseTable256[(v >> 24) & 0xff]));
+}
+
+static inline void ch_set_increase_size(ChainedHashSet *set) {
+ set->size = set->size + 1;
+ if (set->size > set->expandTreshold) {
+ unsigned int oldNumberOfBuckets = set->numberOfBuckets;
+ unsigned int newNumberOfBuckets = oldNumberOfBuckets * 2;
+ unsigned int splitUpMask = reverse_bits(newNumberOfBuckets - 1) ^
+ reverse_bits(oldNumberOfBuckets - 1);
+ SortedListSetNode **newBuckets =
+ set->malloc(sizeof(SortedListSetNode *) * newNumberOfBuckets);
+ SortedListSetNode **oldBuckets = set->buckets;
+ SortedListSetNode *moveTemp;
+ for (unsigned int i = 0; i < oldNumberOfBuckets; i++) {
+ moveTemp = sl_set_split_opt(&oldBuckets[i], splitUpMask);
+ newBuckets[i] = oldBuckets[i];
+ newBuckets[i + oldNumberOfBuckets] = moveTemp;
+ }
+ set->free(oldBuckets);
+ set->buckets = newBuckets;
+ set->numberOfBuckets = newNumberOfBuckets;
+ set->expandTreshold = newNumberOfBuckets * CHAIN_LENGHT_EXPAND_THRESHOLD;
+ set->shrinkTreshold = newNumberOfBuckets * CHAIN_LENGHT_SHRINK_THRESHOLD;
+ }
+}
+
+static inline void ch_set_decrease_size(ChainedHashSet *set) {
+ set->size = set->size - 1;
+ if (set->size < set->shrinkTreshold) {
+ unsigned int oldNumberOfBuckets = set->numberOfBuckets;
+ unsigned int newNumberOfBuckets = oldNumberOfBuckets / 2;
+ SortedListSetNode **newBuckets =
+ set->malloc(sizeof(SortedListSetNode *) * newNumberOfBuckets);
+ SortedListSetNode **oldBuckets = set->buckets;
+ for (unsigned int i = 0; i < newNumberOfBuckets; i++) {
+ newBuckets[i] = oldBuckets[i];
+ sl_set_concat_opt(&newBuckets[i], oldBuckets[i + newNumberOfBuckets]);
+ }
+ set->free(oldBuckets);
+ set->buckets = newBuckets;
+ set->numberOfBuckets = newNumberOfBuckets;
+ set->expandTreshold = newNumberOfBuckets * CHAIN_LENGHT_EXPAND_THRESHOLD;
+ if (set->numberOfBuckets == INITIAL_NUMBER_OF_BUCKETS) {
+ set->shrinkTreshold = 0;
+ } else {
+ set->shrinkTreshold = newNumberOfBuckets * CHAIN_LENGHT_SHRINK_THRESHOLD;
+ }
+ }
+}
+
+static inline void ch_set_initialize(ChainedHashSet *set,
+ unsigned int keyPosition,
+ void *(*extract_key)(void *v, int keyPos),
+ unsigned int (*hash_key)(void *k),
+ bool (*are_equal)(void *v1, void *v2),
+ char *(*to_string)(void *v1),
+ void *(*my_malloc)(size_t size),
+ void (*my_free)(void *ptr)) {
+ set->keyPosition = keyPosition;
+ set->extract_key = extract_key;
+ set->hash_key = hash_key;
+ set->are_equal = are_equal;
+ set->to_string = to_string;
+ set->malloc = my_malloc;
+ set->free = my_free;
+ set->numberOfBuckets = INITIAL_NUMBER_OF_BUCKETS;
+ set->expandTreshold = set->numberOfBuckets * CHAIN_LENGHT_EXPAND_THRESHOLD;
+ set->shrinkTreshold = set->numberOfBuckets * CHAIN_LENGHT_EXPAND_THRESHOLD;
+ set->size = 0;
+ set->buckets =
+ set->malloc(sizeof(SortedListSetNode *) * INITIAL_NUMBER_OF_BUCKETS);
+ for (int i = 0; i < INITIAL_NUMBER_OF_BUCKETS; i++) {
+ set->buckets[i] = NULL;
+ }
+}
+
+static inline ChainedHashSet *ch_set_create(
+ unsigned int keyPosition, void *(*extract_key)(void *v, int keyPos),
+ unsigned int (*hash_key)(void *k), bool (*are_equal)(void *v1, void *v2),
+ char *(*to_string)(void *v1), void *(*my_malloc)(size_t size),
+ void (*my_free)(void *ptr)) {
+ ChainedHashSet *set = my_malloc(sizeof(ChainedHashSet));
+ ch_set_initialize(set, keyPosition, extract_key, hash_key, are_equal,
+ to_string, my_malloc, my_free);
+ return set;
+}
+
+static inline bool ch_set_insert_opt(ChainedHashSet *set, void *value,
+ unsigned int valueSize,
+ unsigned int hashValue, bool overwrite) {
+ unsigned int bucketIndex = hashValue & (set->numberOfBuckets - 1);
+ SortedListSetNode **bucket = &set->buckets[bucketIndex];
+ bool oneAdded = sl_set_insert_opt(
+ bucket, value, valueSize, reverse_bits(hashValue), set->keyPosition,
+ overwrite, set->extract_key, set->are_equal, set->malloc, set->free);
+ if (oneAdded) {
+ ch_set_increase_size(set);
+ }
+ return oneAdded;
+}
+
+static inline void ch_set_insert(void *setParam, void *value,
+ unsigned int valueSize) {
+ ChainedHashSet *set = (ChainedHashSet *)setParam;
+ void *key = set->extract_key(value, set->keyPosition);
+ unsigned int hashValue = set->hash_key(key);
+ ch_set_insert_opt(set, value, valueSize, hashValue, true);
+}
+
+static inline bool ch_set_insert_new(void *setParam, void *value,
+ unsigned int valueSize) {
+ ChainedHashSet *set = (ChainedHashSet *)setParam;
+ void *key = set->extract_key(value, set->keyPosition);
+ unsigned int hashValue = set->hash_key(key);
+ return ch_set_insert_opt(set, value, valueSize, hashValue, false);
+}
+
+static inline void *ch_set_lookup(void *setParam, void *key) {
+ ChainedHashSet *set = (ChainedHashSet *)setParam;
+ unsigned int hashValue = set->hash_key(key);
+ unsigned int bucketIndex = hashValue & (set->numberOfBuckets - 1);
+ SortedListSetNode **bucket = &set->buckets[bucketIndex];
+ return sl_set_lookup_opt(bucket, key, reverse_bits(hashValue),
+ set->keyPosition, set->extract_key, set->are_equal,
+ false, set->malloc);
+}
+
+static inline void ch_set_delete(void *setParam, void *key,
+ unsigned int keySize) {
+ (void)keySize;
+ ChainedHashSet *set = (ChainedHashSet *)setParam;
+ unsigned int hashValue = set->hash_key(key);
+ unsigned int bucketIndex = hashValue & (set->numberOfBuckets - 1);
+ SortedListSetNode **bucket = &set->buckets[bucketIndex];
+ bool oneRemoved =
+ sl_set_delete_opt(bucket, key, reverse_bits(hashValue), set->keyPosition,
+ set->extract_key, set->are_equal, set->free);
+ if (oneRemoved) {
+ ch_set_decrease_size(set);
+ }
+}
+
+static inline void ch_set_traverse(void *setParam,
+ void (*traverser)(size_t index, void *v,
+ void *context),
+ void *context) {
+ ChainedHashSet *set = (ChainedHashSet *)setParam;
+ unsigned int numberOfBuckets = set->numberOfBuckets;
+ SortedListSetNode *itemNode;
+ size_t index = 0;
+ for (unsigned int i = 0; i < numberOfBuckets; i++) {
+ itemNode = set->buckets[i];
+ while (itemNode != NULL) {
+ traverser(index, (void *)itemNode->value, context);
+ index++;
+ itemNode = itemNode->next;
+ }
+ }
+}
+
+static inline void ch_set_free(void *setParam) {
+ ChainedHashSet *set = (ChainedHashSet *)setParam;
+ unsigned int numberOfBuckets = set->numberOfBuckets;
+ for (unsigned int i = 0; i < numberOfBuckets; i++) {
+ sl_set_free_opt(&set->buckets[i], set->free);
+ }
+ set->free(set->buckets);
+ set->free(set);
+}
+
+static inline char *ch_set_to_string(void *setParam) {
+ ChainedHashSet *set = (ChainedHashSet *)setParam;
+ unsigned int numberOfBuckets = set->numberOfBuckets;
+ char **bucketStrings = malloc(sizeof(char*)*numberOfBuckets);
+ unsigned int *bucketStringSizes = malloc(sizeof(unsigned int)*numberOfBuckets);
+ unsigned int totalBucketsStringSize = 0;
+ SortedListSet *tempListSet = plain_sl_set_create(
+ set->keyPosition, set->extract_key, set->hash_key, set->are_equal,
+ set->to_string, set->malloc, set->free);
+ for (unsigned int i = 0; i < numberOfBuckets; i++) {
+ tempListSet->head = set->buckets[i];
+ bucketStrings[i] = sl_set_to_string(tempListSet);
+ bucketStringSizes[i] = strlen(bucketStrings[i]);
+ totalBucketsStringSize = totalBucketsStringSize + bucketStringSizes[i];
+ }
+ tempListSet->head = NULL;
+ sl_set_free(tempListSet);
+ char *resultString =
+ set->malloc(totalBucketsStringSize + numberOfBuckets * 3 - 3 + 3);
+ resultString[0] = '[';
+ unsigned int currentPosition = 1;
+ for (unsigned int i = 0; i < numberOfBuckets; i++) {
+ sprintf(&resultString[currentPosition], "%s", bucketStrings[i]);
+ set->free(bucketStrings[i]);
+ currentPosition = currentPosition + bucketStringSizes[i];
+ if (i != (numberOfBuckets - 1)) {
+ sprintf(&resultString[currentPosition], ",\n ");
+ currentPosition = currentPosition + 3;
+ }
+ }
+ sprintf(&resultString[currentPosition], "]");
+ free(bucketStrings);
+ free(bucketStringSizes);
+ return resultString;
+}
+
+static inline void ch_set_print(void *setParam) {
+ ChainedHashSet *set = (ChainedHashSet *)setParam;
+ char *str = ch_set_to_string(set);
+ printf("%s\n", str);
+ set->free(str);
+}
+
+static inline bool ch_set_is_concurrent() { return false; }
+
+#endif
diff --git a/erts/lib_src/yielding_c_fun/lib/simple_c_gc/simple_c_gc.c b/erts/lib_src/yielding_c_fun/lib/simple_c_gc/simple_c_gc.c
new file mode 100644
index 0000000000..a6932ab99b
--- /dev/null
+++ b/erts/lib_src/yielding_c_fun/lib/simple_c_gc/simple_c_gc.c
@@ -0,0 +1,310 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright 2019 Kjell Winblad (kjellwinblad@gmail.com, http://winsh.me).
+ * 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%
+ */
+
+/*
+ *
+ * Author: Kjell Winblad
+ *
+ */
+
+#include "simple_c_gc.h"
+#include "chained_hash_set.h"
+
+#include <setjmp.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+
+static bool scgc_print_gc_info = false;
+static ChainedHashSet *scgc_objects;
+static void *scgc_stack_top;
+
+#define SCGC_MIN_ALLOCS_UNTIL_FREE 100
+static int scgc_allocs_until_gc = SCGC_MIN_ALLOCS_UNTIL_FREE;
+static void *(*scgc_user_malloc)(size_t size);
+static void (*scgc_user_free)(void *ptr);
+
+typedef enum { scgc_blank_state, scgc_marked } scgc_object_state;
+
+typedef struct {
+ unsigned long magic_number;
+ size_t size;
+ scgc_object_state state;
+ void *data[1];
+} scgc_object;
+
+typedef struct {
+ void *address;
+ scgc_object *base;
+ unsigned long magic_number;
+} scgc_object_ref;
+
+static void *scgc_malloc(size_t size) {
+ void *(*my_malloc)(size_t size);
+ if (scgc_user_malloc == NULL) {
+ my_malloc = malloc;
+ } else {
+ my_malloc = scgc_user_malloc;
+ }
+ void *res = my_malloc(size);
+ if (res == NULL) {
+ printf("GCGC: Allocator returned NULL.\n");
+ exit(1);
+ }
+ return res;
+}
+
+static void scgc_free(void *ptr) {
+ void (*my_free)(void *ptr);
+ if (scgc_user_free == NULL) {
+ my_free = free;
+ } else {
+ my_free = scgc_user_free;
+ }
+ my_free(ptr);
+}
+
+static void *scgc_extract_key(void *v, int keyPos) {
+ (void)keyPos;
+ return v;
+}
+
+static unsigned int scgc_hash_key(void *keyPtr) {
+ /* From
+ * https://lemire.me/blog/2018/08/15/fast-strongly-universal-64-bit-hashing-everywhere*/
+ int64_t x = (long)*((void **)keyPtr);
+ int64_t a = 2348923;
+ int64_t b = 3292;
+ int64_t c = 9893487421;
+ int32_t low = (int)x;
+ int32_t high = (int)(x >> 32);
+ return (unsigned int)((a * low + b * high + c) >> 32);
+}
+
+static bool scgc_are_equal(void *v1p, void *v2p) {
+ scgc_object_ref *v1 = ((scgc_object_ref *)v1p);
+ scgc_object_ref *v2 = ((scgc_object_ref *)v2p);
+ return v1->address == v2->address;
+}
+
+static char *scgc_to_string(void *vp) {
+ scgc_object_ref *v = ((scgc_object_ref *)vp);
+ char *str = scgc_malloc(200);
+ sprintf(str, "{.address=%p, .base_address=%p, magic_number=%lu}", v->address,
+ (void *)v->base, v->magic_number);
+ return str;
+}
+
+static void scgc_initialize_global_state() {
+ scgc_objects =
+ ch_set_create(0, scgc_extract_key, scgc_hash_key, scgc_are_equal,
+ scgc_to_string, scgc_malloc, scgc_free);
+ srand((int)(long)scgc_objects * 2654435761);
+}
+
+static void scgc_do_gc(bool no_stack);
+
+static void scgc_destroy_global_state() {
+ scgc_do_gc(true);
+ ch_set_free(scgc_objects);
+}
+
+static int scgc_start_gced_code_2(int (*main)(int, char *[]), int argc,
+ char **argv[]) {
+ volatile int noinline = 1;
+ volatile char **my_argv = (volatile char **)*argv;
+ scgc_stack_top = (void *)my_argv;
+ int (*next)(int, char *[]) = noinline ? main : (int (*)(int, char *[]))(NULL);
+ {
+ int to_return;
+ scgc_initialize_global_state();
+ to_return = next(argc, (char **)my_argv);
+ scgc_destroy_global_state();
+ return to_return;
+ }
+}
+
+static void scgc_global_set_put(scgc_object_ref ref) {
+ ch_set_insert(scgc_objects, &ref, sizeof(scgc_object_ref));
+}
+
+static void scgc_global_set_del(void *key) {
+ ch_set_delete(scgc_objects, &key, sizeof(void *));
+}
+
+static scgc_object_ref *scgc_global_set_get(void *key) {
+ scgc_object_ref *ret = ch_set_lookup(scgc_objects, &key);
+ return ret;
+}
+
+static void scgc_mark_reachable_objects_in_region(void *start, void *end);
+
+static void *scgc_min(void *a, void *b) { return a <= b ? a : b; }
+
+static void *scgc_max(void *a, void *b) { return a > b ? a : b; }
+
+static void ycf_find_stack_bottom_and_mark_conservative_helper(void) {
+ volatile void *p = NULL;
+ volatile intptr_t stack_bottom = (intptr_t)&p;
+ scgc_mark_reachable_objects_in_region(
+ scgc_min(scgc_stack_top, (void *)&stack_bottom),
+ scgc_max((void *)&stack_bottom, scgc_stack_top));
+}
+
+static void scgc_get_stack_bottom_and_mark() {
+ jmp_buf env;
+ setjmp(env);
+
+ volatile int noinline = 1;
+
+ void (*bottom)(void) =
+ noinline ? ycf_find_stack_bottom_and_mark_conservative_helper
+ : (void (*)(void))(NULL);
+
+ bottom();
+}
+
+static void scgc_mark_reachable_objects_in_region(void *start, void *end) {
+ void **iter = start;
+ void **iter_end = end;
+ scgc_object *object;
+ while (iter <= iter_end) {
+ scgc_object_ref *ref = scgc_global_set_get(*iter);
+ if (ref != NULL && ref->base->data == *iter &&
+ ref->magic_number == ref->base->magic_number &&
+ ref->base->state == scgc_blank_state) {
+ object = ref->base;
+ object->state = scgc_marked;
+ scgc_mark_reachable_objects_in_region(
+ &object->data[0], &((char *)object->data)[object->size - 1]);
+ }
+ iter = iter + 1;
+ }
+}
+
+static void scgc_mark_reachable_objects(bool no_stack) {
+ void *tmp = NULL;
+ if (no_stack) {
+ scgc_mark_reachable_objects_in_region(&tmp, &tmp);
+ } else {
+ scgc_get_stack_bottom_and_mark();
+ }
+}
+
+static void scgc_collect_unmarked_traverser(size_t index, void *v,
+ void *context) {
+ void **objects_to_remove = context;
+ scgc_object_ref *ref = v;
+ if (ref->base->state == scgc_blank_state) {
+ scgc_free(ref->base);
+ objects_to_remove[index] = ref->address;
+ } else {
+ objects_to_remove[index] = NULL;
+ }
+}
+
+static void scgc_remove_unmarked_objects() {
+ size_t nr_of_candidates = scgc_objects->size;
+ void **objects_to_remove = scgc_malloc(sizeof(void *) * nr_of_candidates);
+ for (size_t i = 0; i < nr_of_candidates; i++) {
+ objects_to_remove[i] = NULL;
+ }
+ ch_set_traverse(scgc_objects, scgc_collect_unmarked_traverser,
+ objects_to_remove);
+ for (size_t i = 0; i < nr_of_candidates; i++) {
+ if (objects_to_remove[i] != NULL) {
+ scgc_global_set_del(objects_to_remove[i]);
+ }
+ }
+ scgc_free(objects_to_remove);
+}
+
+static void scgc_unmark_traverser(size_t index, void *v, void *context) {
+ scgc_object_ref *ref = v;
+ ref->base->state = scgc_blank_state;
+}
+
+static void scgc_unmark_objects() {
+ ch_set_traverse(scgc_objects, scgc_unmark_traverser, NULL);
+}
+
+static void scgc_do_gc(bool no_stack) {
+ unsigned int objects_before = scgc_objects->size;
+ scgc_mark_reachable_objects(no_stack);
+ scgc_remove_unmarked_objects();
+ scgc_unmark_objects();
+ if (scgc_print_gc_info) {
+ unsigned int objects_after = scgc_objects->size;
+ unsigned int objects_removed = objects_before - objects_after;
+ fprintf(stderr, "GC: before=%u, after=%u, removed=%u\n", objects_before,
+ objects_after, objects_removed);
+ }
+}
+
+static void scgc_gc() {
+ scgc_allocs_until_gc--;
+ if (scgc_allocs_until_gc <= 0) {
+ scgc_do_gc(false);
+ unsigned int objects_after = scgc_objects->size;
+ scgc_allocs_until_gc = objects_after * 2;
+ if (scgc_allocs_until_gc < SCGC_MIN_ALLOCS_UNTIL_FREE) {
+ scgc_allocs_until_gc = SCGC_MIN_ALLOCS_UNTIL_FREE;
+ }
+ }
+}
+
+/* Public interface */
+
+int scgc_start_gced_code(int (*main)(int, char *[]), int argc, char *argv[],
+ void *(*my_malloc)(size_t), void (*my_free)(void *)) {
+ volatile int noinline = 1;
+ int (*next)(int (*)(int, char *[]), int, char **[]) =
+ (noinline ? scgc_start_gced_code_2
+ : (int (*)(int (*)(int, char *[]), int, char **[]))(NULL));
+ volatile char **my_argv = (volatile char **)argv;
+ int res;
+ scgc_user_malloc = my_malloc;
+ scgc_user_free = my_free;
+ if (my_argv == NULL) {
+ fprintf(stderr,
+ "scgc_start_gced_code: the argv parameter should not be NULL!");
+ exit(1);
+ }
+ res = next(main, argc, (char ***)&my_argv);
+ return res;
+}
+
+void *scgc_new(size_t size) {
+ scgc_gc();
+ scgc_object *new = scgc_malloc(size + sizeof(scgc_object));
+ scgc_object_ref new_ref;
+ unsigned long magic_number = (unsigned long)rand();
+ new->state = scgc_blank_state;
+ new->magic_number = magic_number;
+ new->size = size;
+ new_ref.address = new->data;
+ new_ref.base = new;
+ new_ref.magic_number = magic_number;
+ scgc_global_set_put(new_ref);
+ return new->data;
+}
+
+void scgc_enable_print_gc_info() { scgc_print_gc_info = true; }
diff --git a/erts/lib_src/yielding_c_fun/lib/simple_c_gc/simple_c_gc.h b/erts/lib_src/yielding_c_fun/lib/simple_c_gc/simple_c_gc.h
new file mode 100644
index 0000000000..0e9aff84b8
--- /dev/null
+++ b/erts/lib_src/yielding_c_fun/lib/simple_c_gc/simple_c_gc.h
@@ -0,0 +1,57 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright 2019 Kjell Winblad (kjellwinblad@gmail.com, http://winsh.me).
+ * 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%
+ */
+
+/**
+ * Pulic interface for Simple C GC.
+ *
+ * @author Kjell Winblad
+ *
+ */
+
+#ifndef SIMPLE_C_GC_H
+#define SIMPLE_C_GC_H
+
+#include <stddef.h>
+
+/**
+ * This function starts code that will be garbage collected
+ *
+ * @param main The function that will be started and garbage collected
+ * @param argc Passed to main
+ * @param argv Passed to main
+ * @param my_malloc Simple C GC will use this to allocated memory
+ * @param my_free Simple C GC will use this to free memory
+ */
+int scgc_start_gced_code(int (*main)(int, char *[]), int argc, char *argv[],
+ void *(*my_malloc)(size_t), void (*my_free)(void *));
+
+/**
+ * Allocate a new memory block that will be garbage collected
+ *
+ * @param size The size of the new memory block
+ */
+void *scgc_new(size_t size);
+
+/**
+ * Enables printing of garbage collection information
+ */
+void scgc_enable_print_gc_info(void);
+#endif
diff --git a/erts/lib_src/yielding_c_fun/lib/simple_c_gc/sorted_list_set.h b/erts/lib_src/yielding_c_fun/lib/simple_c_gc/sorted_list_set.h
new file mode 100644
index 0000000000..1c216d2ed9
--- /dev/null
+++ b/erts/lib_src/yielding_c_fun/lib/simple_c_gc/sorted_list_set.h
@@ -0,0 +1,370 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright 2019 Kjell Winblad (kjellwinblad@gmail.com, http://winsh.me).
+ * 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%
+ */
+
+/*
+ *
+ * Author: Kjell Winblad
+ *
+ */
+
+#ifndef SORTED_LIST_SET_H
+#define SORTED_LIST_SET_H
+
+#include <limits.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+typedef struct SortedListSetNodeImpl {
+ struct SortedListSetNodeImpl *next;
+ unsigned int key_hash_value;
+ unsigned int valueSize;
+ char value[]; /* Flexible size array member */
+} SortedListSetNode;
+
+typedef struct {
+ SortedListSetNode *head;
+ unsigned int keyPosition;
+ void *(*extract_key)(void *v, int keyPos);
+ unsigned int (*hash_key)(void *k);
+ bool (*are_equal)(void *v1, void *v2);
+ char *(*to_string)(void *v1);
+ void *(*malloc)(size_t size);
+ void (*free)(void *ptr);
+} SortedListSet;
+
+static inline SortedListSet *plain_sl_set_create(
+ unsigned int keyPosition, void *(*extract_key)(void *v, int keyPos),
+ unsigned int (*hash_key)(void *k), bool (*are_equal)(void *v1, void *v2),
+ char *(*to_string)(void *v1), void *(*my_malloc)(size_t size),
+ void (*my_free)(void *ptr)) {
+ SortedListSet *set = my_malloc(sizeof(SortedListSet));
+ set->head = NULL;
+ set->keyPosition = keyPosition;
+ set->extract_key = extract_key;
+ set->hash_key = hash_key;
+ set->are_equal = are_equal;
+ set->to_string = to_string;
+ set->malloc = my_malloc;
+ set->free = my_free;
+ return set;
+}
+
+static inline int compare_hash_codes(unsigned int code1, unsigned int code2) {
+ return code1 - code2;
+}
+
+static inline bool sl_set_insert_opt(SortedListSetNode **root, void *valuePtr,
+ unsigned int valueSize,
+ unsigned int keyHashValue, int keyPosition,
+ bool overwrite,
+ void *(*extract_key)(void *v, int keyPos),
+ bool (*are_equal)(void *v1, void *v2),
+ void *(*my_malloc)(size_t size),
+ void (*my_free)(void *ptr)) {
+ void *key = extract_key(valuePtr, keyPosition);
+ SortedListSetNode *previous = (SortedListSetNode *)root;
+ SortedListSetNode *current = previous->next;
+ bool oneMore = true;
+ int compareResult;
+ while (current != NULL) {
+ compareResult = compare_hash_codes(current->key_hash_value, keyHashValue);
+ if (compareResult < 0) {
+ previous = current;
+ current = previous->next;
+ } else if (compareResult > 0) {
+ break;
+ } else {
+ if (are_equal(extract_key(current->value, keyPosition), key)) {
+ if (overwrite) {
+ SortedListSetNode *oldCurrent = current;
+ current = current->next;
+ previous->next = current;
+ my_free(oldCurrent);
+ oneMore = false;
+ break;
+ } else {
+ return false;
+ }
+ } else {
+ previous = current;
+ current = previous->next;
+ }
+ }
+ }
+ SortedListSetNode *newNode = my_malloc(sizeof(SortedListSetNode) + valueSize);
+ previous->next = newNode;
+ newNode->next = current;
+ newNode->key_hash_value = keyHashValue;
+ memcpy(newNode->value, valuePtr, valueSize);
+ newNode->valueSize = valueSize;
+ return oneMore;
+}
+
+static inline void sl_set_insert(void *setParam, void *valuePtr,
+ unsigned int valueSize) {
+ SortedListSet *set = (SortedListSet *)setParam;
+ sl_set_insert_opt(&set->head, valuePtr, valueSize,
+ set->hash_key(set->extract_key(valuePtr, set->keyPosition)),
+ set->keyPosition, true, set->extract_key, set->are_equal,
+ set->malloc, set->free);
+}
+
+static inline bool sl_set_insert_new(void *setParam, void *valuePtr,
+ unsigned int valueSize) {
+ SortedListSet *set = (SortedListSet *)setParam;
+ return sl_set_insert_opt(
+ &set->head, valuePtr, valueSize,
+ set->hash_key(set->extract_key(valuePtr, set->keyPosition)),
+ set->keyPosition, false, set->extract_key, set->are_equal, set->malloc,
+ set->free);
+}
+
+static inline void *sl_set_lookup_opt(SortedListSetNode **root, void *key,
+ unsigned int keyHashValue,
+ int keyPosition,
+ void *(*extract_key)(void *v, int keyPos),
+ bool (*are_equal)(void *v1, void *v2),
+ bool copyOut,
+ void *(*my_malloc)(size_t size)) {
+ SortedListSetNode *previous = (SortedListSetNode *)root;
+ SortedListSetNode *current = previous->next;
+ int compareResult;
+ while (current != NULL) {
+ compareResult = compare_hash_codes(current->key_hash_value, keyHashValue);
+ if (compareResult < 0) {
+ previous = current;
+ current = previous->next;
+ } else if (compareResult > 0) {
+ return NULL;
+ } else if (are_equal(extract_key(current->value, keyPosition), key)) {
+ if (copyOut) {
+ void *toReturn = my_malloc(current->valueSize);
+ memcpy(toReturn, current->value, current->valueSize);
+ return toReturn;
+ } else {
+ return current->value;
+ }
+ } else {
+ previous = current;
+ current = previous->next;
+ }
+ }
+ return NULL;
+}
+
+static inline void *sl_set_lookup(void *setParam, void *key) {
+ SortedListSet *set = (SortedListSet *)setParam;
+ return sl_set_lookup_opt(&set->head, key, set->hash_key(key),
+ set->keyPosition, set->extract_key, set->are_equal,
+ false, set->malloc);
+}
+
+static inline bool sl_set_delete_opt(SortedListSetNode **root, void *key,
+ unsigned int keyHashValue, int keyPosition,
+ void *(*extract_key)(void *v, int keyPos),
+ bool (*are_equal)(void *v1, void *v2),
+ void (*my_free)(void *ptr)) {
+ SortedListSetNode *previous = (SortedListSetNode *)root;
+ SortedListSetNode *current = previous->next;
+ int compareResult;
+ while (current != NULL) {
+ compareResult = compare_hash_codes(current->key_hash_value, keyHashValue);
+ if (compareResult < 0) {
+ previous = current;
+ current = previous->next;
+ } else if (compareResult > 0) {
+ return false;
+ } else if (are_equal(extract_key(current->value, keyPosition), key)) {
+ previous->next = current->next;
+ my_free(current);
+ return true;
+ } else {
+ previous = current;
+ current = previous->next;
+ }
+ }
+ return false;
+}
+
+static inline void sl_set_delete(void *setParam, void *key,
+ unsigned int keySize) {
+ (void)keySize;
+ SortedListSet *set = (SortedListSet *)setParam;
+ sl_set_delete_opt(&set->head, key, set->hash_key(key), set->keyPosition,
+ set->extract_key, set->are_equal, set->free);
+}
+
+static inline void sl_set_free_opt(SortedListSetNode **root,
+ void (*my_free)(void *ptr)) {
+ SortedListSetNode *previous = (SortedListSetNode *)root;
+ SortedListSetNode *current = previous->next;
+ while (current != NULL) {
+ previous = current;
+ current = current->next;
+ my_free(previous);
+ }
+}
+
+static inline void sl_set_free(void *setParam) {
+ SortedListSet *set = (SortedListSet *)setParam;
+ sl_set_free_opt(&set->head, set->free);
+ set->free(set);
+}
+
+static inline SortedListSetNode *sl_set_split_opt(SortedListSetNode **root,
+ unsigned int splitPattern) {
+ SortedListSetNode *previous = (SortedListSetNode *)root;
+ SortedListSetNode *current = previous->next;
+ while (current != NULL) {
+ if (current->key_hash_value & splitPattern) {
+ previous->next = NULL;
+ return current;
+ }
+ previous = current;
+ current = previous->next;
+ }
+ return NULL;
+}
+
+static inline void sl_set_concat_opt(SortedListSetNode **root,
+ SortedListSetNode *list) {
+ SortedListSetNode *previous = (SortedListSetNode *)root;
+ SortedListSetNode *current = previous->next;
+ while (current != NULL) {
+ previous = current;
+ current = previous->next;
+ }
+ previous->next = list;
+}
+
+static inline void sl_set_append_opt(SortedListSetNode **root,
+ SortedListSetNode *list) {
+ if (list == NULL) {
+ return;
+ }
+ SortedListSetNode *previous = (SortedListSetNode *)root;
+ SortedListSetNode *current = previous->next;
+ while (current != NULL) {
+ previous = current;
+ current = previous->next;
+ }
+ previous->next = list;
+}
+
+static inline void *
+sl_set_fold_opt(SortedListSetNode **root, void *initialValue,
+ void *(*f)(void *soFar, void *currentValue)) {
+ SortedListSetNode *previous = (SortedListSetNode *)root;
+ SortedListSetNode *current = previous->next;
+ void *soFar = initialValue;
+ while (current != NULL) {
+ soFar = f(soFar, current->value);
+ current = current->next;
+ }
+ return soFar;
+}
+
+static inline void *sl_set_fold(SortedListSet *set, void *initialValue,
+ void *(*f)(void *soFar, void *currentValue)) {
+ return sl_set_fold_opt(&set->head, initialValue, f);
+}
+
+static inline void *_______size_helper(void *soFarParam, void *currentValue) {
+ unsigned int *soFar = (unsigned int *)soFarParam;
+ (void)currentValue;
+ unsigned int prev = *soFar;
+ *soFar = prev + 1;
+ return soFar;
+}
+
+static inline unsigned int sl_set_size_opt(SortedListSetNode **root) {
+ unsigned int size = 0;
+ sl_set_fold_opt(root, &size, _______size_helper);
+ return size;
+}
+
+static inline unsigned int sl_set_size(SortedListSet *set) {
+ return sl_set_size_opt(&set->head);
+}
+
+static inline void sl_set_print(void *setParam) {
+ SortedListSet *set = (SortedListSet *)setParam;
+ SortedListSetNode *previous = (SortedListSetNode *)set;
+ SortedListSetNode *current = previous->next;
+ printf("[");
+ while (current != NULL) {
+ char *string = set->to_string(current->value);
+ printf("%s", string);
+ set->free(string);
+ previous = current;
+ current = previous->next;
+ if (current != NULL) {
+ printf(",");
+ }
+ }
+ printf("]\n");
+}
+
+static inline char *sl_set_to_string(void *setParam) {
+ SortedListSet *set = (SortedListSet *)setParam;
+ unsigned int numberOfElements = sl_set_size(set);
+ char **stringArray = malloc(sizeof(char*)*numberOfElements);
+ unsigned int *stringLengths = malloc(sizeof(unsigned int)*numberOfElements);
+ SortedListSetNode *previous = (SortedListSetNode *)set;
+ SortedListSetNode *current = previous->next;
+ unsigned int elementNumber = 0;
+ unsigned int totalCharCount = 2;
+ if (numberOfElements == 0) {
+ char *buffer = (char *)set->malloc(3);
+ sprintf(buffer, "[]");
+ free(stringArray);
+ free(stringLengths);
+ return buffer;
+ }
+ while (current != NULL) {
+ stringArray[elementNumber] = set->to_string(current->value);
+ stringLengths[elementNumber] = strlen(stringArray[elementNumber]);
+ totalCharCount = totalCharCount + stringLengths[elementNumber];
+ current = current->next;
+ elementNumber++;
+ }
+ totalCharCount = totalCharCount + numberOfElements;
+ char *stringBuffer = set->malloc(totalCharCount);
+ stringBuffer[0] = '[';
+ unsigned int currentPosition = 1;
+ for (unsigned int i = 0; i < numberOfElements; i++) {
+ sprintf(&stringBuffer[currentPosition], "%s", stringArray[i]);
+ set->free(stringArray[i]);
+ currentPosition = currentPosition + stringLengths[i];
+ if (i != (numberOfElements - 1)) {
+ sprintf(&stringBuffer[currentPosition], ",");
+ currentPosition = currentPosition + 1;
+ }
+ }
+ sprintf(&stringBuffer[currentPosition], "]");
+ free(stringArray);
+ free(stringLengths);
+ return stringBuffer;
+}
+
+#endif
diff --git a/erts/lib_src/yielding_c_fun/lib/simple_c_gc/test.c b/erts/lib_src/yielding_c_fun/lib/simple_c_gc/test.c
new file mode 100644
index 0000000000..f118db4361
--- /dev/null
+++ b/erts/lib_src/yielding_c_fun/lib/simple_c_gc/test.c
@@ -0,0 +1,53 @@
+#include "simple_c_gc.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+static size_t nr_of_objects_created = 0;
+static size_t nr_of_objects = 0;
+static size_t peek_nr_of_objects = 0;
+
+static void *my_malloc(size_t size) {
+ nr_of_objects++;
+ nr_of_objects_created++;
+ if (nr_of_objects > peek_nr_of_objects) {
+ peek_nr_of_objects = nr_of_objects;
+ }
+ return calloc(1, size);
+}
+
+static void my_free(void *ptr) {
+ nr_of_objects--;
+ free(ptr);
+}
+
+int my_main(int argc, char *argv[]) {
+ if (argc == 2 && strcmp(argv[1], "-enable_gc_info") == 0) {
+ scgc_enable_print_gc_info();
+ }
+ void **my = scgc_new(100);
+ FILE *fp = fopen(".tmp_out", "w");
+ fprintf(fp, "my %p %p\n", (void *)my, (void *)&my);
+ for (int i = 0; i < 100000; i++) {
+ my[0] = scgc_new(32);
+ my[1] = scgc_new(32);
+ my[2] = scgc_new(32);
+ ((void **)my[2])[0] = scgc_new(32);
+ ((void **)my[2])[1] = scgc_new(32);
+ fprintf(fp, "test1 %p\n", ((void **)my[0])[0]);
+ fprintf(fp, "test2 %p\n", ((void **)my[1])[0]);
+ fprintf(fp, "test3 %p\n", ((void ***)my[2])[0][0]);
+ fprintf(fp, "test4 %p\n", ((void ***)my[2])[1][0]);
+ }
+ fclose(fp);
+ return 0;
+}
+
+int main(int argc, char *argv[]) {
+ /*Test the gc*/
+ scgc_start_gced_code(my_main, argc, argv, my_malloc, my_free);
+ fprintf(stderr, "Peek nr of live objects: %zu\n", peek_nr_of_objects);
+ fprintf(stderr, "Nr of objects after: %zu\n", nr_of_objects);
+ fprintf(stderr, "Nr of objects created: %zu\n", nr_of_objects_created);
+ return 0;
+}
diff --git a/erts/lib_src/yielding_c_fun/lib/tiny_regex_c/GIT_VERSION b/erts/lib_src/yielding_c_fun/lib/tiny_regex_c/GIT_VERSION
new file mode 100644
index 0000000000..0bbde9c80d
--- /dev/null
+++ b/erts/lib_src/yielding_c_fun/lib/tiny_regex_c/GIT_VERSION
@@ -0,0 +1,468 @@
+origin https://github.com/kokke/tiny-regex-c.git (fetch)
+origin https://github.com/kokke/tiny-regex-c.git (push)
+commit d3058f271f7a06ff298dff0a6a9a1e0753a5fa17
+Merge: 28882c4 c2ed772
+Author: kokke <spam@rowdy.dk>
+Date: Fri Oct 26 23:20:07 2018 +0200
+
+ Merge pull request #22 from monolifed/master
+
+ Update re.c for #20
+
+commit c2ed77267c86e30aa342f48528e5ffce6ab4d103
+Author: monolifed <6624464+monolifed@users.noreply.github.com>
+Date: Thu Oct 25 19:19:24 2018 +0300
+
+ Update re.c
+
+commit 28882c4a39fbc9ddd44e69caa22aac4c4a208934
+Author: kokke <spam@rowdy.dk>
+Date: Tue Oct 23 11:27:23 2018 +0200
+
+ Update README.md
+
+commit 4583018febd2d28277b512f09427e2ba01b4cbd5
+Author: kokke <spam@rowdy.dk>
+Date: Tue Oct 23 11:26:47 2018 +0200
+
+ Update README.md
+
+commit 2211111107da75f5574d5f0140e4396ae701d947
+Author: kokke <spam@rowdy.dk>
+Date: Mon Oct 22 16:04:02 2018 +0200
+
+ Update test1.c
+
+ Adding failing test-case for question-mark '?', brought to my attention by @tobermory in https://github.com/kokke/tiny-regex-c/issues/20
+
+commit 679aebd38a245afb9f9d107d066b68765b94865b
+Author: kokke <spam@rowdy.dk>
+Date: Mon Oct 22 15:41:33 2018 +0200
+
+ Update re.c
+
+ fixing typo, noticed by @tobermory -> https://github.com/kokke/tiny-regex-c/issues/19
+
+commit 2f225fa5e355ad3a99cdd5e953768399fe0b6607
+Author: kokke <spam@rowdy.dk>
+Date: Wed Jun 6 18:15:48 2018 +0200
+
+ Update test1.c
+
+commit b587a65abf0f1347a3b7c7050b73c5dbb94d9cb7
+Merge: 89a479f 96a8f77
+Author: kokke <spam@rowdy.dk>
+Date: Wed Jun 6 18:10:57 2018 +0200
+
+ Merge pull request #17 from monolifed/patch-1
+
+ Update re.c
+
+commit 96a8f770c2922505699c9a4d6ba9b9584be5ee29
+Author: monolifed <6624464+monolifed@users.noreply.github.com>
+Date: Thu May 31 02:06:49 2018 +0300
+
+ Update re.c
+
+ hopefully fixes #12
+
+commit 89a479f985cb25284c4e11870c1531c299255790
+Merge: bf9b2f0 e5f3564
+Author: kokke <spam@rowdy.dk>
+Date: Tue May 15 11:25:53 2018 +0200
+
+ Merge pull request #16 from TermoSINteZ/master
+
+ Fix pattern ".?" issues
+
+commit e5f3564a1de7230cec207cb6aec3866b0b7931e0
+Author: TermoSINteZ <termo.sintez@gmail.com>
+Date: Tue May 15 10:41:17 2018 +0300
+
+ Remove tabs
+
+commit acb0a441470808c99a08ecc8d8716d258866835c
+Author: TermoSINteZ <termo.sintez@gmail.com>
+Date: Tue May 15 00:10:55 2018 +0300
+
+ Fix pattern ".?" issues
+
+commit bf9b2f0c5e91dd12e1fea8cbc7ae7a6193e7b4ed
+Merge: cb80dee 84af23d
+Author: kokke <spam@rowdy.dk>
+Date: Tue Apr 17 14:09:03 2018 +0200
+
+ Merge pull request #14 from roflcopter4/master
+
+ Check for correct python2 binary in Makefile
+
+commit 84af23dde1c6785ca680d5aced93e20e484efa8d
+Author: roflcopter4 <brendan.leason.4@gmail.com>
+Date: Mon Apr 16 14:45:34 2018 -0600
+
+ Fix dumb typos
+
+commit 0cb0b1348392b795971be9472d8dd2854403a2cb
+Author: roflcopter4 <brendan.leason.4@gmail.com>
+Date: Mon Apr 16 14:42:13 2018 -0600
+
+ Add back '@' signs I accidentally removed
+
+commit 81d12dfd3de805d0969e66650731e4df9158169c
+Author: roflcopter4 <brendan.leason.4@gmail.com>
+Date: Mon Apr 16 14:26:28 2018 -0600
+
+ Check for correct python2 binry in Makefile
+
+commit cb80dee0644f41df67b1740fefd8573d18d84a53
+Author: kokke <spam@rowdy.dk>
+Date: Fri Mar 23 15:39:17 2018 +0100
+
+ Update README.md
+
+commit 005de160fa2d8796eb2bce75b52eeaac3ac13d8d
+Author: kokke <spam@rowdy.dk>
+Date: Fri Mar 23 15:32:37 2018 +0100
+
+ Update Makefile
+
+commit 9ec0029e83e7cba718f6b6f8b107a0133d22b4a7
+Author: kokke <spam@rowdy.dk>
+Date: Fri Mar 23 15:31:40 2018 +0100
+
+ Create regex_test_neg.py
+
+commit 960dd3ebec78d2ac61969840d4e23c97d443bdc9
+Author: kokke <spam@rowdy.dk>
+Date: Fri Mar 23 15:31:11 2018 +0100
+
+ Create test_rand_neg.c
+
+commit 3d472f3d78d9702ffcdf4439ac842e3250da7c49
+Author: kokke <spam@rowdy.dk>
+Date: Fri Mar 23 14:02:23 2018 +0100
+
+ Update README.md
+
+commit cdf61829adc1c94a9f2d019b2683c34c7732ca60
+Author: kokke <spam@rowdy.dk>
+Date: Fri Mar 23 13:06:49 2018 +0100
+
+ Update Makefile
+
+commit 98812bdcafa58c7f35fc40fae9ba64d6d2a9eac1
+Author: kokke <spam@rowdy.dk>
+Date: Fri Mar 23 12:52:34 2018 +0100
+
+ Update README.md
+
+commit 9c192d4199e4e1e6a764f1b1699deb5b159b161e
+Author: kokke <spam@rowdy.dk>
+Date: Fri Mar 23 12:47:27 2018 +0100
+
+ Update re.c
+
+commit fb677f315fd159e13c2c2fc5b40f199148c0fb0f
+Author: kokke <spam@rowdy.dk>
+Date: Fri Mar 23 12:45:20 2018 +0100
+
+ Update test1.c
+
+commit dc1b3ee8fc4354e5dfa1846ee6b778452609d50e
+Author: kokke <spam@rowdy.dk>
+Date: Fri Mar 23 12:08:34 2018 +0100
+
+ Update README.md
+
+commit eac0cef080a32c4b606bd10994ddcf8a72249d78
+Author: kokke <spam@rowdy.dk>
+Date: Mon Dec 11 23:26:53 2017 +0100
+
+ Update README.md
+
+commit dc9f34d74b6ac80cd0bed17fca76ef35fca3b101
+Author: kokke <spam@rowdy.dk>
+Date: Mon Dec 11 23:23:40 2017 +0100
+
+ Update re.c
+
+commit d76301fa18f3575cca94816cff291a01fda58ad7
+Author: kokke <spam@rowdy.dk>
+Date: Mon Dec 11 23:23:22 2017 +0100
+
+ Update test1.c
+
+commit b72898ef7a67a0650c7d00b7a09f36abc1fa57a1
+Author: kokke <spam@rowdy.dk>
+Date: Mon Dec 11 21:35:55 2017 +0100
+
+ Update re.c
+
+commit 3cd275c9c55ec51a01ad01c317232247aeec9bab
+Merge: baf3a15 881f634
+Author: kokke <spam@rowdy.dk>
+Date: Mon Dec 11 21:32:26 2017 +0100
+
+ Merge pull request #9 from mrigger/out-of-bounds-fixes
+
+ Out of bounds fixes.
+
+commit 881f634e9a905933d1889a4e0b9b09920337478a
+Author: Manuel Rigger <manuel.rigger@jku.at>
+Date: Sun Dec 10 11:42:17 2017 +0100
+
+ Fix out-of-bunds access found by AFL (input: [00000000000000000000000000000000000000][).
+
+commit e6c91ab986f1ed7d7e39d58e6fdfae44d3110c99
+Author: Manuel Rigger <manuel.rigger@jku.at>
+Date: Sun Dec 10 10:30:06 2017 +0100
+
+ Fix out-of-bounds accesses found by AFL (input: [00000000000000000000000000000000000000).
+
+commit 619a9c654df6a471d7e043081b3fb4bf0e1cd642
+Author: Manuel Rigger <manuel.rigger@jku.at>
+Date: Sat Dec 9 22:56:12 2017 +0100
+
+ Fix out-of-bounds access found by AFL.
+
+commit 43051e257141740e99c32f1cffea78d2a72fe10e
+Author: Manuel Rigger <manuel.rigger@jku.at>
+Date: Fri Dec 8 21:25:01 2017 +0100
+
+ Fix out-of-bounds access found by AFL.
+
+commit baf3a15d7b99b856e82d3dd69555d3bac6750049
+Author: kokke <spam@rowdy.dk>
+Date: Thu Oct 12 00:54:16 2017 +0200
+
+ Update re.h
+
+ To make the C++ crowd and their compilers happy ;)
+
+commit 107352174172b05f25d153c651b327890ab3b574
+Author: kokke <spam@rowdy.dk>
+Date: Sat Jul 8 03:32:29 2017 +0200
+
+ Update re.c
+
+commit ef6b2416b17388da413d3b9cce95ef6897255970
+Author: kokke <spam@rowdy.dk>
+Date: Sat Jul 8 03:26:59 2017 +0200
+
+ Update test1.c
+
+commit b8446ecba1c59b7a9f29e7d8174661deb38a74a5
+Author: kokke <spam@rowdy.dk>
+Date: Wed May 3 22:55:11 2017 +0200
+
+ Update README.md
+
+commit 1600de0a66b11610183c02c4f778866dce6927af
+Author: kokke <spam@rowdy.dk>
+Date: Mon May 1 21:43:47 2017 +0200
+
+ Update regex_test.py
+
+commit e5dafc83fe1672de45ab1bc40e7f34876e2d1d15
+Author: kokke <spam@rowdy.dk>
+Date: Mon May 1 21:34:22 2017 +0200
+
+ Update test_rand.c
+
+commit a39262a534a628d7c07bbb753a66d031a4b4a43a
+Author: kokke <spam@rowdy.dk>
+Date: Mon May 1 21:27:17 2017 +0200
+
+ Update test_print.c
+
+commit 96b9af356129a263ef60355426de0c339308cd26
+Author: kokke <spam@rowdy.dk>
+Date: Mon May 1 21:25:11 2017 +0200
+
+ Update re.c
+
+commit 529889acae614c91697af87feba5abe5334a4274
+Author: kokke <spam@rowdy.dk>
+Date: Mon May 1 21:18:40 2017 +0200
+
+ Update README.md
+
+commit 1afc07dc2936f17dc0df5178ef61caeb19d2c5b1
+Author: kokke <spam@rowdy.dk>
+Date: Mon May 1 21:17:57 2017 +0200
+
+ Update README.md
+
+commit 407f4fe08fb219d68026817d47d1a453a0b6c1ba
+Author: kokke <spam@rowdy.dk>
+Date: Fri Apr 28 17:27:12 2017 +0200
+
+ Update README.md
+
+commit 2d2ebe66d55349ba6add2a4335b24b0a13fb26d2
+Author: kokke <spam@rowdy.dk>
+Date: Mon Apr 24 23:48:18 2017 +0200
+
+ Update README.md
+
+commit 5c76b8cc4e77af9c4a3cb1bbd1cfda3e73e1be56
+Author: kokke <spam@rowdy.dk>
+Date: Mon Apr 24 23:33:34 2017 +0200
+
+ Update README.md
+
+commit e4c7cb9d63d1b284f92053f535402738ee2c3a6a
+Author: kokke <spam@rowdy.dk>
+Date: Mon Apr 24 09:28:24 2017 +0200
+
+ Update README.md
+
+commit 1e694fe184be1261e9f684ea3556103e45649c3b
+Author: kokke <spam@rowdy.dk>
+Date: Thu Apr 20 17:58:33 2017 +0200
+
+ Update README.md
+
+commit 52810460dd877f337e23d58e627b5ae3fbbee430
+Author: kokke <spam@rowdy.dk>
+Date: Wed Apr 19 02:38:00 2017 +0200
+
+ Update Makefile
+
+commit d44fb891007d56e0344f7372521f3ff583630d61
+Author: kokke <spam@rowdy.dk>
+Date: Wed Apr 19 01:19:38 2017 +0200
+
+ Update README.md
+
+commit af6e7b642b75255bc2a608383a1b0969816bbd83
+Author: kokke <spam@rowdy.dk>
+Date: Wed Apr 19 01:16:23 2017 +0200
+
+ Update README.md
+
+commit 858d217db95390b6e7bd1fd1ba7be950142852c9
+Author: kokke <spam@rowdy.dk>
+Date: Wed Apr 19 00:32:20 2017 +0200
+
+ Update README.md
+
+commit 28fffd4d32b56cd9b318091cacc8a5c71fbfffed
+Author: kokke <spam@rowdy.dk>
+Date: Wed Apr 19 00:31:44 2017 +0200
+
+ Update README.md
+
+commit bd874728041ad0444744a9e739dd5d078f249150
+Author: kokke <spam@rowdy.dk>
+Date: Wed Apr 19 00:28:26 2017 +0200
+
+ Update README.md
+
+commit 02110e89252e76c1a2b2a44430f0f402e523f50a
+Author: kokke <spam@rowdy.dk>
+Date: Wed Apr 19 00:26:41 2017 +0200
+
+ Update test1.c
+
+commit 939bc7572148c77d398b22892857b769ec63ac54
+Author: kokke <spam@rowdy.dk>
+Date: Tue Apr 18 23:31:14 2017 +0200
+
+ Update README.md
+
+commit 5fef3901aabb6100b28460dccf3154d3e1cc18e7
+Author: kokke <spam@rowdy.dk>
+Date: Tue Apr 18 23:09:12 2017 +0200
+
+ Update README.md
+
+commit cae4b96ced1679ea703890eff00deefb0b80cc16
+Author: kokke <spam@rowdy.dk>
+Date: Tue Apr 18 23:08:34 2017 +0200
+
+ Update README.md
+
+commit 72398075ee66c49e3e804be94146956dc8bafff6
+Author: kokke <spam@rowdy.dk>
+Date: Tue Apr 18 22:58:10 2017 +0200
+
+ Update Makefile
+
+commit 2994559b99506b35e71c8e18aa99dd706f3b7e38
+Author: kokke <spam@rowdy.dk>
+Date: Tue Apr 18 22:53:05 2017 +0200
+
+ Update re.c
+
+commit 124052b32f302c5a570e79089f951a7d1d56cb38
+Author: kokke <spam@rowdy.dk>
+Date: Tue Apr 18 22:52:01 2017 +0200
+
+ Update Makefile
+
+commit ba8caa931b36b6c274c82214b636770857f7872d
+Author: kokke <spam@rowdy.dk>
+Date: Tue Apr 18 22:50:11 2017 +0200
+
+ Create regex_test.py
+
+commit 9630b56d1faffea799575bb9a81f9c0278906a16
+Author: kokke <spam@rowdy.dk>
+Date: Tue Apr 18 22:49:39 2017 +0200
+
+ Create exrex.py
+
+commit 7da49b443622deaaa472d233a80c58a378fb8646
+Author: kokke <spam@rowdy.dk>
+Date: Tue Apr 18 22:47:37 2017 +0200
+
+ Create test_rand.c
+
+commit 29384927296062ecf0fec81a1c7e35d315b5d84a
+Author: kokke <spam@rowdy.dk>
+Date: Tue Apr 18 22:47:09 2017 +0200
+
+ Create test_print.c
+
+commit 475c199a1b1af4fdd8422b8639117ec97f53b350
+Author: kokke <spam@rowdy.dk>
+Date: Tue Apr 18 22:46:34 2017 +0200
+
+ Create test2.c
+
+commit 2e6e69622061daa740fa03c726f4cc66c5fbc38c
+Author: kokke <spam@rowdy.dk>
+Date: Tue Apr 18 22:45:08 2017 +0200
+
+ Create test1.c
+
+commit e9e4ce1b91609d839a2996bb918397b502f908b7
+Author: kokke <spam@rowdy.dk>
+Date: Tue Apr 18 22:44:33 2017 +0200
+
+ Create Makefile
+
+commit 5a8f0e60718fbfdca7c1fc079a77051af68bdc87
+Author: kokke <spam@rowdy.dk>
+Date: Tue Apr 18 22:41:01 2017 +0200
+
+ Create re.c
+
+commit 52b0aeb459cf1682b636fabe01a97de17579b036
+Author: kokke <spam@rowdy.dk>
+Date: Tue Apr 18 22:38:55 2017 +0200
+
+ Create re.h
+
+commit d061985f504096a59e320a4760784a4090eaf1e4
+Author: kokke <spam@rowdy.dk>
+Date: Tue Apr 18 22:38:01 2017 +0200
+
+ Update README.md
+
+commit 32cbf08728415efb96a3f562f31b780c024502ff
+Author: kokke <spam@rowdy.dk>
+Date: Tue Apr 18 22:37:02 2017 +0200
+
+ Initial commit
diff --git a/erts/lib_src/yielding_c_fun/lib/tiny_regex_c/LICENSE b/erts/lib_src/yielding_c_fun/lib/tiny_regex_c/LICENSE
new file mode 100644
index 0000000000..cf1ab25da0
--- /dev/null
+++ b/erts/lib_src/yielding_c_fun/lib/tiny_regex_c/LICENSE
@@ -0,0 +1,24 @@
+This is free and unencumbered software released into the public domain.
+
+Anyone is free to copy, modify, publish, use, compile, sell, or
+distribute this software, either in source code form or as a compiled
+binary, for any purpose, commercial or non-commercial, and by any
+means.
+
+In jurisdictions that recognize copyright laws, the author or authors
+of this software dedicate any and all copyright interest in the
+software to the public domain. We make this dedication for the benefit
+of the public at large and to the detriment of our heirs and
+successors. We intend this dedication to be an overt act of
+relinquishment in perpetuity of all present and future rights to this
+software under copyright law.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
+
+For more information, please refer to <http://unlicense.org>
diff --git a/erts/lib_src/yielding_c_fun/lib/tiny_regex_c/Makefile b/erts/lib_src/yielding_c_fun/lib/tiny_regex_c/Makefile
new file mode 100644
index 0000000000..deb9fea33a
--- /dev/null
+++ b/erts/lib_src/yielding_c_fun/lib/tiny_regex_c/Makefile
@@ -0,0 +1,106 @@
+# Compiler to use - can be replaced by clang for instance
+CC := gcc
+
+# Number of random text expressions to generate, for random testing
+NRAND_TESTS := 1000
+
+PYTHON != if (python --version 2>&1 | grep -q 'Python 2\..*'); then \
+ echo 'python'; \
+ elif command -v python2 >/dev/null 2>&1; then \
+ echo 'python2'; \
+ else \
+ echo 'Error: no compatible python version found.' >&2; \
+ exit 1; \
+ fi
+
+# Flags to pass to compiler
+CFLAGS := -O3 -Wall -Wextra -std=c99 -I.
+
+all:
+ @$(CC) $(CFLAGS) re.c tests/test1.c -o tests/test1
+ @$(CC) $(CFLAGS) re.c tests/test2.c -o tests/test2
+ @$(CC) $(CFLAGS) re.c tests/test_rand.c -o tests/test_rand
+ @$(CC) $(CFLAGS) re.c tests/test_rand_neg.c -o tests/test_rand_neg
+
+clean:
+ @rm -f tests/test1 tests/test2 tests/test_rand
+ @#@$(foreach test_bin,$(TEST_BINS), rm -f $(test_bin) ; )
+ @rm -f a.out
+ @rm -f *.o
+
+
+test: all
+ @$(test $(PYTHON))
+ @echo
+ @echo Testing hand-picked regex\'s:
+ @./tests/test1
+ @echo Testing patterns against $(NRAND_TESTS) random strings matching the Python implementation and comparing:
+ @echo
+ @$(PYTHON) ./scripts/regex_test.py \\d+\\w?\\D\\d $(NRAND_TESTS)
+ @$(PYTHON) ./scripts/regex_test.py \\s+[a-zA-Z0-9?]* $(NRAND_TESTS)
+ @$(PYTHON) ./scripts/regex_test.py \\w*\\d?\\w\\? $(NRAND_TESTS)
+ @$(PYTHON) ./scripts/regex_test.py [^\\d]+\\\\?\\s $(NRAND_TESTS)
+ @$(PYTHON) ./scripts/regex_test.py [^\\w][^-1-4] $(NRAND_TESTS)
+ @$(PYTHON) ./scripts/regex_test.py [^\\w] $(NRAND_TESTS)
+ @$(PYTHON) ./scripts/regex_test.py [^1-4] $(NRAND_TESTS)
+ @$(PYTHON) ./scripts/regex_test.py [^-1-4] $(NRAND_TESTS)
+ @$(PYTHON) ./scripts/regex_test.py [^\\d]+\\s?[\\w]* $(NRAND_TESTS)
+ @$(PYTHON) ./scripts/regex_test.py a+b*[ac]*.+.*.[\\.]. $(NRAND_TESTS)
+ @$(PYTHON) ./scripts/regex_test.py a?b[ac*]*.?[\\]+[?]? $(NRAND_TESTS)
+ @#python ./scripts/regex_test.py [1-5-]+[-1-2]-[-] $(NRAND_TESTS)
+ @$(PYTHON) ./scripts/regex_test.py [-1-3]-[-]+ $(NRAND_TESTS)
+ @$(PYTHON) ./scripts/regex_test.py [1-5]+[-1-2]-[\\-] $(NRAND_TESTS)
+ @$(PYTHON) ./scripts/regex_test.py [-1-2]* $(NRAND_TESTS)
+ @$(PYTHON) ./scripts/regex_test.py \\s?[a-fKL098]+-? $(NRAND_TESTS)
+ @$(PYTHON) ./scripts/regex_test.py [\\-]* $(NRAND_TESTS)
+ @$(PYTHON) ./scripts/regex_test.py [\\\\]+ $(NRAND_TESTS)
+ @$(PYTHON) ./scripts/regex_test.py [0-9a-fA-F]+ $(NRAND_TESTS)
+ @$(PYTHON) ./scripts/regex_test.py [1379][2468][abcdef] $(NRAND_TESTS)
+ @$(PYTHON) ./scripts/regex_test.py [012345-9]?[0123-789] $(NRAND_TESTS)
+ @$(PYTHON) ./scripts/regex_test.py [012345-9] $(NRAND_TESTS)
+ @$(PYTHON) ./scripts/regex_test.py [0-56789] $(NRAND_TESTS)
+ @$(PYTHON) ./scripts/regex_test.py [abc-zABC-Z] $(NRAND_TESTS)
+ @$(PYTHON) ./scripts/regex_test.py [a\d]?1234 $(NRAND_TESTS)
+ @$(PYTHON) ./scripts/regex_test.py .*123faerdig $(NRAND_TESTS)
+ @$(PYTHON) ./scripts/regex_test.py .?\\w+jsj$ $(NRAND_TESTS)
+ @$(PYTHON) ./scripts/regex_test.py [?to][+to][?ta][*ta] $(NRAND_TESTS)
+ @$(PYTHON) ./scripts/regex_test.py \\d+ $(NRAND_TESTS)
+ @$(PYTHON) ./scripts/regex_test.py [a-z]+ $(NRAND_TESTS)
+ @$(PYTHON) ./scripts/regex_test.py \\s+[a-zA-Z0-9?]* $(NRAND_TESTS)
+ @$(PYTHON) ./scripts/regex_test.py \\w $(NRAND_TESTS)
+ @$(PYTHON) ./scripts/regex_test.py \\d $(NRAND_TESTS)
+ @$(PYTHON) ./scripts/regex_test.py [\\d] $(NRAND_TESTS)
+ @$(PYTHON) ./scripts/regex_test.py [^\\d] $(NRAND_TESTS)
+ @#python ./scripts/regex_test.py [^-1-4] $(NRAND_TESTS)
+ @echo
+ @echo
+ @echo
+ @echo Testing rejection of patterns against $(NRAND_TESTS) random strings also rejected by the Python implementation:
+ @echo
+ @$(PYTHON) ./scripts/regex_test_neg.py \\d+ $(NRAND_TESTS)
+ @$(PYTHON) ./scripts/regex_test_neg.py [a-z]+ $(NRAND_TESTS)
+ @$(PYTHON) ./scripts/regex_test_neg.py \\s+[a-zA-Z0-9?]* $(NRAND_TESTS)
+ @$(PYTHON) ./scripts/regex_test_neg.py ^\\w $(NRAND_TESTS)
+ @$(PYTHON) ./scripts/regex_test_neg.py ^\\d $(NRAND_TESTS)
+ @$(PYTHON) ./scripts/regex_test_neg.py [\\d] $(NRAND_TESTS)
+ @$(PYTHON) ./scripts/regex_test_neg.py ^[^\\d] $(NRAND_TESTS)
+ @$(PYTHON) ./scripts/regex_test_neg.py [^\\w]+ $(NRAND_TESTS)
+ @$(PYTHON) ./scripts/regex_test_neg.py ^[\\w]+ $(NRAND_TESTS)
+ @$(PYTHON) ./scripts/regex_test_neg.py ^[^0-9] $(NRAND_TESTS)
+ @$(PYTHON) ./scripts/regex_test_neg.py [a-z].[A-Z] $(NRAND_TESTS)
+ @$(PYTHON) ./scripts/regex_test_neg.py [-1-3]-[-]+ $(NRAND_TESTS)
+ @$(PYTHON) ./scripts/regex_test_neg.py [1-5]+[-1-2]-[\\-] $(NRAND_TESTS)
+ @$(PYTHON) ./scripts/regex_test_neg.py [-0-9]+ $(NRAND_TESTS)
+ @$(PYTHON) ./scripts/regex_test_neg.py [\\-]+ $(NRAND_TESTS)
+ @$(PYTHON) ./scripts/regex_test_neg.py [\\\\]+ $(NRAND_TESTS)
+ @$(PYTHON) ./scripts/regex_test_neg.py [0-9a-fA-F]+ $(NRAND_TESTS)
+ @$(PYTHON) ./scripts/regex_test_neg.py [1379][2468][abcdef] $(NRAND_TESTS)
+ @$(PYTHON) ./scripts/regex_test_neg.py [012345-9] $(NRAND_TESTS)
+ @$(PYTHON) ./scripts/regex_test_neg.py [0-56789] $(NRAND_TESTS)
+ @$(PYTHON) ./scripts/regex_test_neg.py .*123faerdig $(NRAND_TESTS)
+ @echo
+ @echo
+ @./tests/test2
+ @echo
+ @echo
+
diff --git a/erts/lib_src/yielding_c_fun/lib/tiny_regex_c/README.md b/erts/lib_src/yielding_c_fun/lib/tiny_regex_c/README.md
new file mode 100644
index 0000000000..ab89576f08
--- /dev/null
+++ b/erts/lib_src/yielding_c_fun/lib/tiny_regex_c/README.md
@@ -0,0 +1,138 @@
+# tiny-regex-c
+# A small regex implementation in C
+### Description
+Small and portable [Regular Expression](https://en.wikipedia.org/wiki/Regular_expression) (regex) library written in C.
+
+Design is inspired by Rob Pike's regex-code for the book *"Beautiful Code"* [available online here](http://www.cs.princeton.edu/courses/archive/spr09/cos333/beautiful.html).
+
+Supports a subset of the syntax and semantics of the Python standard library implementation (the `re`-module).
+
+### Current status
+All supported regex-operators seem to work properly according to the test-set, with the following exception:
+
+There is a problem with ranges (e.g. `[0-9]` for a digit 0-9) combined with inverted character-cases, e.g. `[^ab]` for anything but 'a' or 'b' - like `[^-0-9]` for anything not '-' or a digit 0-9. I think the code mathces too broadly in that case.
+
+I think you should test the patterns you are going to use. You can easily modify the test-harness to generate tests for your intended patterns to check for compliance.
+
+**I will gladly accept patches correcting bugs.**
+
+### Design goals
+The main design goal of this library is to be small, correct, self contained and use few resources while retaining acceptable performance and feature completeness. Clarity of the code is also highly valued.
+
+### Notable features and omissions
+- Small code and binary size: <500 SLOC, ~3kb binary for x86. Statically #define'd memory usage / allocation.
+- No use of dynamic memory allocation (i.e. no calls to `malloc` / `free`).
+- To avoid call-stack exhaustion, iterative searching is preferred over recursive by default (can be changed with a pre-processor flag).
+- No support for capturing groups or named capture: `(^P<name>group)` etc.
+- Thorough testing : [exrex](https://github.com/asciimoo/exrex) is used to randomly generate test-cases from regex patterns, which are fed into the regex code for verification. Try `make test` to generate a few thousand tests cases yourself.
+- Compiled for x86 using GCC 4.7.4 and optimizing for size, the binary takes up ~2-3kb code space and allocates ~0.5kb RAM :
+ ```
+ > gcc -Os -c re.c
+ > size re.o
+ text data bss dec hex filename
+ 2319 0 544 2863 b2f re.o
+
+ ```
+ For ARM/Thumb using GCC 4.8.1 it's around 1.5kb code and less RAM :
+ ```
+ > arm-none-eabi-gcc -Os -mthumb -c re.c
+ > size re.o
+ text data bss dec hex filename
+ 1418 0 280 1698 6a2 re.o
+
+ ```
+ For 8-bit AVR using AVR-GCC 4.8.1 it's around 2kb code and less RAM :
+ ```
+ > avr-gcc -Os -c re.c
+ > size re.o
+ text data bss dec hex filename
+ 2128 0 130 2258 8d2 re.o
+ ```
+
+
+
+### API
+This is the public / exported API:
+```C
+/* Typedef'd pointer to hide implementation details. */
+typedef struct regex_t* re_t;
+
+/* Compiles regex string pattern to a regex_t-array. */
+re_t re_compile(const char* pattern);
+
+/* Finds matches of the compiled pattern inside text. */
+int re_matchp(re_t pattern, const char* text);
+
+/* Finds matches of pattern inside text (compiles first automatically). */
+int re_match(const char* pattern, const char* text);
+```
+
+### Supported regex-operators
+The following features / regex-operators are supported by this library.
+
+NOTE: inverted character classes are buggy - see the test harness for concrete examples.
+
+
+ - `.` Dot, matches any character
+ - `^` Start anchor, matches beginning of string
+ - `$` End anchor, matches end of string
+ - `*` Asterisk, match zero or more (greedy)
+ - `+` Plus, match one or more (greedy)
+ - `?` Question, match zero or one (non-greedy)
+ - `[abc]` Character class, match if one of {'a', 'b', 'c'}
+ - `[^abc]` Inverted class, match if NOT one of {'a', 'b', 'c'}
+ **`NOTE: This feature is currently broken for some usage of character ranges!`**
+ - `[a-zA-Z]` Character ranges, the character set of the ranges { a-z | A-Z }
+ - `\s` Whitespace, \t \f \r \n \v and spaces
+ - `\S` Non-whitespace
+ - `\w` Alphanumeric, [a-zA-Z0-9_]
+ - `\W` Non-alphanumeric
+ - `\d` Digits, [0-9]
+ - `\D` Non-digits
+
+### Usage
+Compile a regex from ASCII-string (char-array) to a custom pattern structure using `re_compile()`.
+
+Search a text-string for a regex and get an index into the string, using `re_match()` or `re_matchp()`.
+
+The returned index points to the first place in the string, where the regex pattern matches.
+
+If the regular expression doesn't match, the matching function returns an index of -1 to indicate failure.
+
+### Examples
+Example of usage:
+```C
+/* Standard null-terminated C-string to search: */
+const char* string_to_search = "ahem.. 'hello world !' ..";
+
+/* Compile a simple regular expression using character classes, meta-char and greedy + non-greedy quantifiers: */
+re_t pattern = re_compile("[Hh]ello [Ww]orld\\s*[!]?");
+
+/* Check if the regex matches the text: */
+int match_idx = re_matchp(pattern, string_to_search);
+if (match_idx != -1)
+{
+ printf("match at idx %d.\n", match_idx);
+}
+```
+
+For more usage examples I encourage you to look at the code in the `tests`-folder.
+
+### TODO
+- Fix the implementation of inverted character classes.
+- Fix implementation of branches (`|`), and see if that can lead us closer to groups as well, e.g. `(a|b)+`.
+- Add `example.c` that demonstrates usage.
+- Add `tests/test_perf.c` for performance and time measurements.
+- Testing: Improve pattern rejection testing.
+
+### FAQ
+- *Q: What differentiates this library from other C regex implementations?*
+
+ A: Well, the small size for one. <500 lines of C-code compiling to 2-3kb ROM, using very little RAM.
+
+### License
+All material in this repository is in the public domain.
+
+
+
+
diff --git a/erts/lib_src/yielding_c_fun/lib/tiny_regex_c/re.c b/erts/lib_src/yielding_c_fun/lib/tiny_regex_c/re.c
new file mode 100644
index 0000000000..76d4066247
--- /dev/null
+++ b/erts/lib_src/yielding_c_fun/lib/tiny_regex_c/re.c
@@ -0,0 +1,470 @@
+/*
+ *
+ * Mini regex-module inspired by Rob Pike's regex code described in:
+ *
+ * http://www.cs.princeton.edu/courses/archive/spr09/cos333/beautiful.html
+ *
+ *
+ *
+ * Supports:
+ * ---------
+ * '.' Dot, matches any character
+ * '^' Start anchor, matches beginning of string
+ * '$' End anchor, matches end of string
+ * '*' Asterisk, match zero or more (greedy)
+ * '+' Plus, match one or more (greedy)
+ * '?' Question, match zero or one (non-greedy)
+ * '[abc]' Character class, match if one of {'a', 'b', 'c'}
+ * '[^abc]' Inverted class, match if NOT one of {'a', 'b', 'c'} -- NOTE: feature is currently broken!
+ * '[a-zA-Z]' Character ranges, the character set of the ranges { a-z | A-Z }
+ * '\s' Whitespace, \t \f \r \n \v and spaces
+ * '\S' Non-whitespace
+ * '\w' Alphanumeric, [a-zA-Z0-9_]
+ * '\W' Non-alphanumeric
+ * '\d' Digits, [0-9]
+ * '\D' Non-digits
+ *
+ *
+ */
+
+
+
+#include "re.h"
+#include <stdio.h>
+
+/* Definitions: */
+
+#define MAX_REGEXP_OBJECTS 30 /* Max number of regex symbols in expression. */
+#define MAX_CHAR_CLASS_LEN 40 /* Max length of character-class buffer in. */
+
+
+enum { UNUSED, DOT, BEGIN, END, QUESTIONMARK, STAR, PLUS, CHAR, CHAR_CLASS, INV_CHAR_CLASS, DIGIT, NOT_DIGIT, ALPHA, NOT_ALPHA, WHITESPACE, NOT_WHITESPACE, /* BRANCH */ };
+
+typedef struct regex_t
+{
+ unsigned char type; /* CHAR, STAR, etc. */
+ union
+ {
+ unsigned char ch; /* the character itself */
+ unsigned char* ccl; /* OR a pointer to characters in class */
+ } u;
+} regex_t;
+
+
+
+/* Private function declarations: */
+static int matchpattern(regex_t* pattern, const char* text);
+static int matchcharclass(char c, const char* str);
+static int matchstar(regex_t p, regex_t* pattern, const char* text);
+static int matchplus(regex_t p, regex_t* pattern, const char* text);
+static int matchone(regex_t p, char c);
+static int matchdigit(char c);
+static int matchalpha(char c);
+static int matchwhitespace(char c);
+static int matchmetachar(char c, const char* str);
+static int matchrange(char c, const char* str);
+static int ismetachar(char c);
+
+
+
+/* Public functions: */
+int re_match(const char* pattern, const char* text)
+{
+ return re_matchp(re_compile(pattern), text);
+}
+
+int re_matchp(re_t pattern, const char* text)
+{
+ if (pattern != 0)
+ {
+ if (pattern[0].type == BEGIN)
+ {
+ return ((matchpattern(&pattern[1], text)) ? 0 : -1);
+ }
+ else
+ {
+ int idx = -1;
+
+ do
+ {
+ idx += 1;
+
+ if (matchpattern(pattern, text))
+ {
+ if (text[0] == '\0')
+ return -1;
+
+ return idx;
+ }
+ }
+ while (*text++ != '\0');
+ }
+ }
+ return -1;
+}
+
+re_t re_compile(const char* pattern)
+{
+ /* The sizes of the two static arrays below substantiates the static RAM usage of this module.
+ MAX_REGEXP_OBJECTS is the max number of symbols in the expression.
+ MAX_CHAR_CLASS_LEN determines the size of buffer for chars in all char-classes in the expression. */
+ static regex_t re_compiled[MAX_REGEXP_OBJECTS];
+ static unsigned char ccl_buf[MAX_CHAR_CLASS_LEN];
+ int ccl_bufidx = 1;
+
+ char c; /* current char in pattern */
+ int i = 0; /* index into pattern */
+ int j = 0; /* index into re_compiled */
+
+ while (pattern[i] != '\0' && (j+1 < MAX_REGEXP_OBJECTS))
+ {
+ c = pattern[i];
+
+ switch (c)
+ {
+ /* Meta-characters: */
+ case '^': { re_compiled[j].type = BEGIN; } break;
+ case '$': { re_compiled[j].type = END; } break;
+ case '.': { re_compiled[j].type = DOT; } break;
+ case '*': { re_compiled[j].type = STAR; } break;
+ case '+': { re_compiled[j].type = PLUS; } break;
+ case '?': { re_compiled[j].type = QUESTIONMARK; } break;
+/* case '|': { re_compiled[j].type = BRANCH; } break; <-- not working properly */
+
+ /* Escaped character-classes (\s \w ...): */
+ case '\\':
+ {
+ if (pattern[i+1] != '\0')
+ {
+ /* Skip the escape-char '\\' */
+ i += 1;
+ /* ... and check the next */
+ switch (pattern[i])
+ {
+ /* Meta-character: */
+ case 'd': { re_compiled[j].type = DIGIT; } break;
+ case 'D': { re_compiled[j].type = NOT_DIGIT; } break;
+ case 'w': { re_compiled[j].type = ALPHA; } break;
+ case 'W': { re_compiled[j].type = NOT_ALPHA; } break;
+ case 's': { re_compiled[j].type = WHITESPACE; } break;
+ case 'S': { re_compiled[j].type = NOT_WHITESPACE; } break;
+
+ /* Escaped character, e.g. '.' or '$' */
+ default:
+ {
+ re_compiled[j].type = CHAR;
+ re_compiled[j].u.ch = pattern[i];
+ } break;
+ }
+ }
+ /* '\\' as last char in pattern -> invalid regular expression. */
+/*
+ else
+ {
+ re_compiled[j].type = CHAR;
+ re_compiled[j].ch = pattern[i];
+ }
+*/
+ } break;
+
+ /* Character class: */
+ case '[':
+ {
+ /* Remember where the char-buffer starts. */
+ int buf_begin = ccl_bufidx;
+
+ /* Look-ahead to determine if negated */
+ if (pattern[i+1] == '^')
+ {
+ re_compiled[j].type = INV_CHAR_CLASS;
+ i += 1; /* Increment i to avoid including '^' in the char-buffer */
+ }
+ else
+ {
+ re_compiled[j].type = CHAR_CLASS;
+ }
+
+ /* Copy characters inside [..] to buffer */
+ while ( (pattern[++i] != ']')
+ && (pattern[i] != '\0')) /* Missing ] */
+ {
+ if (pattern[i] == '\\')
+ {
+ if (ccl_bufidx >= MAX_CHAR_CLASS_LEN - 1)
+ {
+ //fputs("exceeded internal buffer!\n", stderr);
+ return 0;
+ }
+ ccl_buf[ccl_bufidx++] = pattern[i++];
+ }
+ else if (ccl_bufidx >= MAX_CHAR_CLASS_LEN)
+ {
+ //fputs("exceeded internal buffer!\n", stderr);
+ return 0;
+ }
+ ccl_buf[ccl_bufidx++] = pattern[i];
+ }
+ if (ccl_bufidx >= MAX_CHAR_CLASS_LEN)
+ {
+ /* Catches cases such as [00000000000000000000000000000000000000][ */
+ //fputs("exceeded internal buffer!\n", stderr);
+ return 0;
+ }
+ /* Null-terminate string end */
+ ccl_buf[ccl_bufidx++] = 0;
+ re_compiled[j].u.ccl = &ccl_buf[buf_begin];
+ } break;
+
+ /* Other characters: */
+ default:
+ {
+ re_compiled[j].type = CHAR;
+ re_compiled[j].u.ch = c;
+ } break;
+ }
+ i += 1;
+ j += 1;
+ }
+ /* 'UNUSED' is a sentinel used to indicate end-of-pattern */
+ re_compiled[j].type = UNUSED;
+
+ return (re_t) re_compiled;
+}
+
+void re_print(regex_t* pattern)
+{
+ const char* types[] = { "UNUSED", "DOT", "BEGIN", "END", "QUESTIONMARK", "STAR", "PLUS", "CHAR", "CHAR_CLASS", "INV_CHAR_CLASS", "DIGIT", "NOT_DIGIT", "ALPHA", "NOT_ALPHA", "WHITESPACE", "NOT_WHITESPACE", "BRANCH" };
+
+ int i;
+ for (i = 0; i < MAX_REGEXP_OBJECTS; ++i)
+ {
+ if (pattern[i].type == UNUSED)
+ {
+ break;
+ }
+
+ printf("type: %s", types[pattern[i].type]);
+ if (pattern[i].type == CHAR_CLASS || pattern[i].type == INV_CHAR_CLASS)
+ {
+ printf(" [");
+ int j;
+ char c;
+ for (j = 0; j < MAX_CHAR_CLASS_LEN; ++j)
+ {
+ c = pattern[i].u.ccl[j];
+ if ((c == '\0') || (c == ']'))
+ {
+ break;
+ }
+ printf("%c", c);
+ }
+ printf("]");
+ }
+ else if (pattern[i].type == CHAR)
+ {
+ printf(" '%c'", pattern[i].u.ch);
+ }
+ printf("\n");
+ }
+}
+
+
+
+/* Private functions: */
+static int matchdigit(char c)
+{
+ return ((c >= '0') && (c <= '9'));
+}
+static int matchalpha(char c)
+{
+ return ((c >= 'a') && (c <= 'z')) || ((c >= 'A') && (c <= 'Z'));
+}
+static int matchwhitespace(char c)
+{
+ return ((c == ' ') || (c == '\t') || (c == '\n') || (c == '\r') || (c == '\f') || (c == '\v'));
+}
+static int matchalphanum(char c)
+{
+ return ((c == '_') || matchalpha(c) || matchdigit(c));
+}
+static int matchrange(char c, const char* str)
+{
+ return ((c != '-') && (str[0] != '\0') && (str[0] != '-') &&
+ (str[1] == '-') && (str[1] != '\0') &&
+ (str[2] != '\0') && ((c >= str[0]) && (c <= str[2])));
+}
+static int ismetachar(char c)
+{
+ return ((c == 's') || (c == 'S') || (c == 'w') || (c == 'W') || (c == 'd') || (c == 'D'));
+}
+
+static int matchmetachar(char c, const char* str)
+{
+ switch (str[0])
+ {
+ case 'd': return matchdigit(c);
+ case 'D': return !matchdigit(c);
+ case 'w': return matchalphanum(c);
+ case 'W': return !matchalphanum(c);
+ case 's': return matchwhitespace(c);
+ case 'S': return !matchwhitespace(c);
+ default: return (c == str[0]);
+ }
+}
+
+static int matchcharclass(char c, const char* str)
+{
+ do
+ {
+ if (matchrange(c, str))
+ {
+ return 1;
+ }
+ else if (str[0] == '\\')
+ {
+ /* Escape-char: increment str-ptr and match on next char */
+ str += 1;
+ if (matchmetachar(c, str))
+ {
+ return 1;
+ }
+ else if ((c == str[0]) && !ismetachar(c))
+ {
+ return 1;
+ }
+ }
+ else if (c == str[0])
+ {
+ if (c == '-')
+ {
+ return ((str[-1] == '\0') || (str[1] == '\0'));
+ }
+ else
+ {
+ return 1;
+ }
+ }
+ }
+ while (*str++ != '\0');
+
+ return 0;
+}
+
+static int matchone(regex_t p, char c)
+{
+ switch (p.type)
+ {
+ case DOT: return 1;
+ case CHAR_CLASS: return matchcharclass(c, (const char*)p.u.ccl);
+ case INV_CHAR_CLASS: return !matchcharclass(c, (const char*)p.u.ccl);
+ case DIGIT: return matchdigit(c);
+ case NOT_DIGIT: return !matchdigit(c);
+ case ALPHA: return matchalphanum(c);
+ case NOT_ALPHA: return !matchalphanum(c);
+ case WHITESPACE: return matchwhitespace(c);
+ case NOT_WHITESPACE: return !matchwhitespace(c);
+ default: return (p.u.ch == c);
+ }
+}
+
+static int matchstar(regex_t p, regex_t* pattern, const char* text)
+{
+ do
+ {
+ if (matchpattern(pattern, text))
+ return 1;
+ }
+ while ((text[0] != '\0') && matchone(p, *text++));
+
+ return 0;
+}
+
+static int matchplus(regex_t p, regex_t* pattern, const char* text)
+{
+ while ((text[0] != '\0') && matchone(p, *text++))
+ {
+ if (matchpattern(pattern, text))
+ return 1;
+ }
+ return 0;
+}
+
+static int matchquestion(regex_t p, regex_t* pattern, const char* text)
+{
+ if (p.type == UNUSED)
+ return 1;
+ if (matchpattern(pattern, text))
+ return 1;
+ if (*text && matchone(p, *text++))
+ return matchpattern(pattern, text);
+ return 0;
+}
+
+
+#if 0
+
+/* Recursive matching */
+static int matchpattern(regex_t* pattern, const char* text)
+{
+ if ((pattern[0].type == UNUSED) || (pattern[1].type == QUESTIONMARK))
+ {
+ return matchquestion(pattern[1], &pattern[2], text);
+ }
+ else if (pattern[1].type == STAR)
+ {
+ return matchstar(pattern[0], &pattern[2], text);
+ }
+ else if (pattern[1].type == PLUS)
+ {
+ return matchplus(pattern[0], &pattern[2], text);
+ }
+ else if ((pattern[0].type == END) && pattern[1].type == UNUSED)
+ {
+ return text[0] == '\0';
+ }
+ else if ((text[0] != '\0') && matchone(pattern[0], text[0]))
+ {
+ return matchpattern(&pattern[1], text+1);
+ }
+ else
+ {
+ return 0;
+ }
+}
+
+#else
+
+/* Iterative matching */
+static int matchpattern(regex_t* pattern, const char* text)
+{
+ do
+ {
+ if ((pattern[0].type == UNUSED) || (pattern[1].type == QUESTIONMARK))
+ {
+ return matchquestion(pattern[0], &pattern[2], text);
+ }
+ else if (pattern[1].type == STAR)
+ {
+ return matchstar(pattern[0], &pattern[2], text);
+ }
+ else if (pattern[1].type == PLUS)
+ {
+ return matchplus(pattern[0], &pattern[2], text);
+ }
+ else if ((pattern[0].type == END) && pattern[1].type == UNUSED)
+ {
+ return (text[0] == '\0');
+ }
+/* Branching is not working properly
+ else if (pattern[1].type == BRANCH)
+ {
+ return (matchpattern(pattern, text) || matchpattern(&pattern[2], text));
+ }
+*/
+ }
+ while ((text[0] != '\0') && matchone(*pattern++, *text++));
+
+ return 0;
+}
+
+#endif
diff --git a/erts/lib_src/yielding_c_fun/lib/tiny_regex_c/re.h b/erts/lib_src/yielding_c_fun/lib/tiny_regex_c/re.h
new file mode 100644
index 0000000000..fd36412100
--- /dev/null
+++ b/erts/lib_src/yielding_c_fun/lib/tiny_regex_c/re.h
@@ -0,0 +1,54 @@
+/*
+ *
+ * Mini regex-module inspired by Rob Pike's regex code described in:
+ *
+ * http://www.cs.princeton.edu/courses/archive/spr09/cos333/beautiful.html
+ *
+ *
+ *
+ * Supports:
+ * ---------
+ * '.' Dot, matches any character
+ * '^' Start anchor, matches beginning of string
+ * '$' End anchor, matches end of string
+ * '*' Asterisk, match zero or more (greedy)
+ * '+' Plus, match one or more (greedy)
+ * '?' Question, match zero or one (non-greedy)
+ * '[abc]' Character class, match if one of {'a', 'b', 'c'}
+ * '[^abc]' Inverted class, match if NOT one of {'a', 'b', 'c'} -- NOTE: feature is currently broken!
+ * '[a-zA-Z]' Character ranges, the character set of the ranges { a-z | A-Z }
+ * '\s' Whitespace, \t \f \r \n \v and spaces
+ * '\S' Non-whitespace
+ * '\w' Alphanumeric, [a-zA-Z0-9_]
+ * '\W' Non-alphanumeric
+ * '\d' Digits, [0-9]
+ * '\D' Non-digits
+ *
+ *
+ */
+
+#ifdef __cplusplus
+extern "C"{
+#endif
+
+
+
+/* Typedef'd pointer to get abstract datatype. */
+typedef struct regex_t* re_t;
+
+
+/* Compile regex string pattern to a regex_t-array. */
+re_t re_compile(const char* pattern);
+
+
+/* Find matches of the compiled pattern inside text. */
+int re_matchp(re_t pattern, const char* text);
+
+
+/* Find matches of the txt pattern inside text (will compile automatically first). */
+int re_match(const char* pattern, const char* text);
+
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/erts/lib_src/yielding_c_fun/lib/tiny_regex_c/scripts/exrex.py b/erts/lib_src/yielding_c_fun/lib/tiny_regex_c/scripts/exrex.py
new file mode 100644
index 0000000000..fc6fa7d5cc
--- /dev/null
+++ b/erts/lib_src/yielding_c_fun/lib/tiny_regex_c/scripts/exrex.py
@@ -0,0 +1,307 @@
+#!/usr/bin/env python
+
+# This file is part of exrex.
+#
+# exrex is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# exrex is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with exrex. If not, see < http://www.gnu.org/licenses/ >.
+#
+# (C) 2012- by Adam Tauber, <asciimoo@gmail.com>
+
+try:
+ from future_builtins import map, range
+except:
+ pass
+from re import sre_parse
+from itertools import product, chain, tee
+from random import choice,randint
+import string
+
+__all__ = ('generate', 'CATEGORIES', 'count', 'parse', 'getone')
+
+CATEGORIES = {'category_space' : sorted(sre_parse.WHITESPACE)
+ ,'category_digit' : sorted(sre_parse.DIGITS)
+ ,'category_any' : [chr(x) for x in range(32, 123)]
+ ,'category_word' : sorted( frozenset(string.ascii_letters + string.digits + "_") )
+ }
+
+def comb(g, i):
+ for c in g:
+ g2,i = tee(i)
+ for c2 in g2:
+ yield c+c2
+
+def mappend(g, c):
+ for cc in g:
+ yield cc+c
+
+def _in(d):
+ ret = []
+ neg = False
+ for i in d:
+ if i[0] == 'range':
+ subs = map(chr, range(i[1][0], i[1][1]+1))
+ if neg:
+ for char in subs:
+ try:
+ ret.remove(char)
+ except:
+ pass
+ else:
+ ret.extend(subs)
+ elif i[0] == 'literal':
+ if neg:
+ try:
+ ret.remove(chr(i[1]))
+ except:
+ pass
+ else:
+ ret.append(chr(i[1]))
+ elif i[0] == 'category':
+ subs = CATEGORIES.get(i[1], [''])
+ if neg:
+ for char in subs:
+ try:
+ ret.remove(char)
+ except:
+ pass
+ else:
+ ret.extend(subs)
+ elif i[0] == 'negate':
+ ret = list(CATEGORIES['category_any'])
+ neg = True
+ return ret
+
+
+def prods(orig, ran, items):
+ for o in orig:
+ for r in ran:
+ for s in product(items, repeat=r):
+ yield o+''.join(s)
+
+def ggen(g1, f, *args, **kwargs):
+ for a in g1:
+ g2 = f(*args, **kwargs)
+ if isinstance(g2, int):
+ yield g2
+ else:
+ for b in g2:
+ yield a+b
+
+def _gen(d, limit=20, count=False):
+ """docstring for _gen"""
+ ret = ['']
+ strings = 0
+ for i in d:
+ if i[0] == 'in':
+ subs = _in(i[1])
+ if count:
+ strings = (strings or 1) * len(subs)
+ ret = comb(ret, subs)
+ elif i[0] == 'literal':
+ ret = mappend(ret, chr(i[1]))
+ elif i[0] == 'category':
+ subs = CATEGORIES.get(i[1], [''])
+ if count:
+ strings = (strings or 1) * len(subs)
+ ret = comb(ret, subs)
+ elif i[0] == 'any':
+ subs = CATEGORIES['category_any']
+ if count:
+ strings = (strings or 1) * len(subs)
+ ret = comb(ret, subs)
+ elif i[0] == 'max_repeat':
+ chars = filter(None, _gen(list(i[1][2]), limit))
+ if i[1][1]+1 - i[1][0] >= limit:
+ ran = range(i[1][0], i[1][0]+limit)
+ else:
+ ran = range(i[1][0], i[1][1]+1)
+ if count:
+ for i in ran:
+ strings += pow(len(chars), i)
+ ret = prods(ret, ran, chars)
+ elif i[0] == 'branch':
+ subs = list(chain.from_iterable(_gen(list(x), limit) for x in i[1][1]))
+ if count:
+ strings = (strings or 1) * (len(subs) or 1)
+ ret = comb(ret, subs)
+ elif i[0] == 'subpattern':
+ if count:
+ strings = (strings or 1) * (sum(ggen([0], _gen, i[1][1], limit=limit, count=True)) or 1)
+ ret = ggen(ret, _gen, i[1][1], limit=limit, count=False)
+ # ignore ^ and $
+ elif i[0] == 'at':
+ continue
+ elif i[0] == 'not_literal':
+ subs = list(CATEGORIES['category_any'])
+ subs.remove(chr(i[1]))
+ if count:
+ strings = (strings or 1) * len(subs)
+ ret = comb(ret, subs)
+ elif i[0] == 'assert':
+ print i[1][1]
+ continue
+ else:
+ #print('[!] cannot handle expression ' + repr(i))
+ raise Exception('[!] cannot handle expression ' + repr(i))
+
+ if count:
+ return strings
+
+ return ret
+
+def _randone(d, limit=20):
+ """docstring for _randone"""
+ ret = ''
+ for i in d:
+ if i[0] == 'in':
+ ret += choice(_in(i[1]))
+ elif i[0] == 'literal':
+ ret += chr(i[1])
+ elif i[0] == 'category':
+ ret += choice(CATEGORIES.get(i[1], ['']))
+ elif i[0] == 'any':
+ ret += choice(CATEGORIES['category_any'])
+ elif i[0] == 'max_repeat':
+ chars = filter(None, _gen(list(i[1][2]), limit))
+ if i[1][1]+1 - i[1][0] >= limit:
+ min,max = i[1][0], i[1][0]+limit
+ else:
+ min,max = i[1][0], i[1][1]
+ for _ in range(randint(min, max)):
+ ret += choice(chars)
+ elif i[0] == 'branch':
+ ret += choice(list(chain.from_iterable(_gen(list(x), limit) for x in i[1][1])))
+ elif i[0] == 'subpattern':
+ ret += _randone(i[1][1], limit)
+ elif i[0] == 'at':
+ continue
+ elif i[0] == 'not_literal':
+ c=list(CATEGORIES['category_any'])
+ c.remove(chr(i[1]))
+ ret += choice(c)
+ else:
+ #print('[!] cannot handle expression "%s"' % str(i))
+ raise Exception('[!] cannot handle expression "%s"' % str(i))
+
+ return ret
+
+
+def parse(s):
+ """Regular expression parser
+ :param s: Regular expression
+ :type s: str
+ :rtype: list
+ """
+ r = sre_parse.parse(s)
+ return list(r)
+
+def generate(s, limit=20):
+ """Creates a generator that generates all matching strings to a given regular expression
+ :param s: Regular expression
+ :type s: str
+ :param limit: Range limit
+ :type limit: int
+ :returns: string generator object
+ """
+ return _gen(parse(s), limit)
+
+def count(s, limit=20):
+ """Counts all matching strings to a given regular expression
+ :param s: Regular expression
+ :type s: str
+ :param limit: Range limit
+ :type limit: int
+ :rtype: int
+ :returns: number of matching strings
+ """
+ return _gen(parse(s), limit, count=True)
+
+def getone(regex_string, limit=20):
+ """Returns a random matching string to a given regular expression
+ """
+ return _randone(parse(regex_string), limit)
+
+def argparser():
+ import argparse
+ from sys import stdout
+ argp = argparse.ArgumentParser(description='exrex - regular expression string generator')
+ argp.add_argument('-o', '--output'
+ ,help = 'Output file - default is STDOUT'
+ ,metavar = 'FILE'
+ ,default = stdout
+ ,type = argparse.FileType('w')
+ )
+ argp.add_argument('-l', '--limit'
+ ,help = 'Max limit for range size - default is 20'
+ ,default = 20
+ ,action = 'store'
+ ,type = int
+ ,metavar = 'N'
+ )
+ argp.add_argument('-c', '--count'
+ ,help = 'Count matching strings'
+ ,default = False
+ ,action = 'store_true'
+ )
+ argp.add_argument('-r', '--random'
+ ,help = 'Returns a random string that matches to the regex'
+ ,default = False
+ ,action = 'store_true'
+ )
+ argp.add_argument('-d', '--delimiter'
+ ,help = 'Delimiter - default is \\n'
+ ,default = '\n'
+ )
+ argp.add_argument('-v', '--verbose'
+ ,action = 'store_true'
+ ,help = 'Verbose mode'
+ ,default = False
+ )
+ argp.add_argument('regex'
+ ,metavar = 'REGEX'
+ ,help = 'REGEX string'
+ )
+ return vars(argp.parse_args())
+
+def __main__():
+ from sys import exit, stderr
+ # 'as(d|f)qw(e|r|s)[a-zA-Z]{2,3}'
+ # 'as(QWE|Z([XC]|Y|U)V){2,3}asdf'
+ # '.?'
+ # '.+'
+ # 'asdf.{1,4}qwer{2,5}'
+ # 'a(b)?(c)?(d)?'
+ # 'a[b][c][d]?[e]?
+ args = argparser()
+ if args['verbose']:
+ args['output'].write('%r%s' % (parse(args['regex'], limit=args['limit']), args['delimiter']))
+ if args['count']:
+ args['output'].write('%d%s' % (count(args['regex'], limit=args['limit']), args['delimiter']))
+ exit(0)
+ if args['random']:
+ args['output'].write('%s%s' % (getone(args['regex'], limit=args['limit']), args['delimiter']))
+ exit(0)
+ try:
+ g = generate(args['regex'], args['limit'])
+ except Exception, e:
+ print >> stderr, '[!] Error: ', e
+ exit(1)
+ for s in g:
+ try:
+ args['output'].write(s+args['delimiter'])
+ except:
+ break
+
+if __name__ == '__main__':
+ __main__()
+
diff --git a/erts/lib_src/yielding_c_fun/lib/tiny_regex_c/scripts/regex_test.py b/erts/lib_src/yielding_c_fun/lib/tiny_regex_c/scripts/regex_test.py
new file mode 100644
index 0000000000..0d3d00702c
--- /dev/null
+++ b/erts/lib_src/yielding_c_fun/lib/tiny_regex_c/scripts/regex_test.py
@@ -0,0 +1,77 @@
+#!/usr/bin/env python
+
+"""
+ This program generates random text that matches a given regex-pattern.
+ The pattern is given via sys.argv and the generated text is passed to
+ the binary 'tests/test_rand' to check if the generated text also matches
+ the regex-pattern in the C implementation.
+ The exit-code of the testing program, is used to determine test success.
+
+ This script is called by the Makefile when doing 'make test'
+"""
+
+
+import re
+import sys
+import exrex
+from subprocess import call
+
+
+prog = "./tests/test_rand"
+
+if len(sys.argv) < 2:
+ print("")
+ print("usage: %s pattern [nrepeat]" % sys.argv[0])
+ print(" where [nrepeat] is optional")
+ print("")
+ sys.exit(-1)
+
+own_prog = sys.argv[0]
+pattern = sys.argv[1]
+if len(sys.argv) > 2:
+ ntests = int(sys.argv[2])
+else:
+ ntests = 10
+nfails = 0
+repeats = ntests
+
+
+try:
+ repeats = int(sys.argv[2])
+except:
+ pass
+
+r = 50
+while r < 0:
+ try:
+ g = exrex.generate(pattern)
+ break
+ except:
+ pass
+
+
+sys.stdout.write("%-35s" % (" pattern '%s': " % pattern))
+
+
+while repeats >= 0:
+ try:
+ repeats -= 1
+ example = exrex.getone(pattern)
+ #print("%s %s %s" % (prog, pattern, example))
+ ret = call([prog, "\"%s\"" % pattern, "\"%s\"" % example])
+ if ret != 0:
+ escaped = repr(example) # escapes special chars for better printing
+ print(" FAIL : doesn't match %s as expected [%s]." % (escaped, ", ".join([("0x%02x" % ord(e)) for e in example]) ))
+ nfails += 1
+
+ except:
+ #import traceback
+ #print("EXCEPTION!")
+ #raw_input(traceback.format_exc())
+ ntests -= 1
+ repeats += 1
+ #nfails += 1
+
+sys.stdout.write("%4d/%d tests succeeded \n" % (ntests - nfails, ntests))
+#print("")
+
diff --git a/erts/lib_src/yielding_c_fun/lib/tiny_regex_c/scripts/regex_test_neg.py b/erts/lib_src/yielding_c_fun/lib/tiny_regex_c/scripts/regex_test_neg.py
new file mode 100644
index 0000000000..c3daad62c0
--- /dev/null
+++ b/erts/lib_src/yielding_c_fun/lib/tiny_regex_c/scripts/regex_test_neg.py
@@ -0,0 +1,82 @@
+#!/usr/bin/env python
+
+"""
+ This program generates random text that matches a given regex-pattern.
+ The pattern is given via sys.argv and the generated text is passed to
+ the binary 'tests/test_rand' to check if the generated text also matches
+ the regex-pattern in the C implementation.
+ The exit-code of the testing program, is used to determine test success.
+
+ This script is called by the Makefile when doing 'make test'
+"""
+
+
+import re
+import sys
+import string
+import random
+from subprocess import call
+
+
+prog = "./tests/test_rand_neg"
+
+if len(sys.argv) < 2:
+ print("")
+ print("usage: %s pattern [nrepeat]" % sys.argv[0])
+ print(" where [nrepeat] is optional")
+ print("")
+ sys.exit(-1)
+
+own_prog = sys.argv[0]
+pattern = sys.argv[1]
+if len(sys.argv) > 2:
+ ntests = int(sys.argv[2])
+else:
+ ntests = 10
+nfails = 0
+repeats = ntests
+
+
+try:
+ repeats = int(sys.argv[2])
+except:
+ pass
+
+sys.stdout.write("%-35s" % (" pattern '%s': " % pattern))
+
+
+
+
+def gen_no_match(pattern, minlen=1, maxlen=50, maxattempts=500):
+ nattempts = 0
+ while True:
+ nattempts += 1
+ ret = "".join([random.choice(string.printable) for i in range(random.Random().randint(minlen, maxlen))])
+ if re.findall(pattern, ret) == []:
+ return ret
+ if nattempts >= maxattempts:
+ raise Exception("Could not generate string that did not match the regex pattern '%s' after %d attempts" % (pattern, nattempts))
+
+
+
+while repeats >= 0:
+ try:
+ repeats -= 1
+ example = gen_no_match(pattern)
+ #print("%s %s %s" % (prog, pattern, example))
+ ret = call([prog, "\"%s\"" % pattern, "\"%s\"" % example])
+ if ret != 0:
+ escaped = repr(example) # escapes special chars for better printing
+ print(" FAIL : matches %s unexpectedly [%s]." % (escaped, ", ".join([("0x%02x" % ord(e)) for e in example]) ))
+ nfails += 1
+
+ except:
+ #import traceback
+ #print("EXCEPTION!")
+ #raw_input(traceback.format_exc())
+ ntests -= 1
+ repeats += 1
+ #nfails += 1
+
+sys.stdout.write("%4d/%d tests succeeded \n" % (ntests - nfails, ntests))
+#print("")
diff --git a/erts/lib_src/yielding_c_fun/lib/tiny_regex_c/tests/test1.c b/erts/lib_src/yielding_c_fun/lib/tiny_regex_c/tests/test1.c
new file mode 100644
index 0000000000..ab54cc2ebe
--- /dev/null
+++ b/erts/lib_src/yielding_c_fun/lib/tiny_regex_c/tests/test1.c
@@ -0,0 +1,141 @@
+/*
+ * Testing various regex-patterns
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include "re.h"
+
+
+#define OK ((char*) 1)
+#define NOK ((char*) 0)
+
+
+char* test_vector[][3] =
+{
+ { OK, "\\d", "5" },
+ { OK, "\\w+", "hej" },
+ { OK, "\\s", "\t \n" },
+ { NOK, "\\S", "\t \n" },
+ { OK, "[\\s]", "\t \n" },
+ { NOK, "[\\S]", "\t \n" },
+ { NOK, "\\D", "5" },
+ { NOK, "\\W+", "hej" },
+ { OK, "[0-9]+", "12345" },
+ { OK, "\\D", "hej" },
+ { NOK, "\\d", "hej" },
+ { OK, "[^\\w]", "\\" },
+ { OK, "[\\W]", "\\" },
+ { NOK, "[\\w]", "\\" },
+ { OK, "[^\\d]", "d" },
+ { NOK, "[\\d]", "d" },
+ { NOK, "[^\\D]", "d" },
+ { OK, "[\\D]", "d" },
+ { OK, "^.*\\\\.*$", "c:\\Tools" },
+ { OK, "^[\\+-]*[\\d]+$", "+27" },
+ { OK, "[abc]", "1c2" },
+ { NOK, "[abc]", "1C2" },
+ { OK, "[1-5]+", "0123456789" },
+ { OK, "[.2]", "1C2" },
+ { OK, "a*$", "Xaa" },
+ { OK, "a*$", "Xaa" },
+ { OK, "[a-h]+", "abcdefghxxx" },
+ { NOK, "[a-h]+", "ABCDEFGH" },
+ { OK, "[A-H]+", "ABCDEFGH" },
+ { NOK, "[A-H]+", "abcdefgh" },
+ { OK, "[^\\s]+", "abc def" },
+ { OK, "[^fc]+", "abc def" },
+ { OK, "[^d\\sf]+", "abc def" },
+ { OK, "\n", "abc\ndef" },
+ { OK, "b.\\s*\n", "aa\r\nbb\r\ncc\r\n\r\n" },
+ { OK, ".*c", "abcabc" },
+ { OK, ".+c", "abcabc" },
+ { OK, "[b-z].*", "ab" },
+ { OK, "b[k-z]*", "ab" },
+ { NOK, "[0-9]", " - " },
+ { OK, "[^0-9]", " - " },
+ { OK, "0|", "0|" },
+ { NOK, "\\d\\d:\\d\\d:\\d\\d", "0s:00:00" },
+ { NOK, "\\d\\d:\\d\\d:\\d\\d", "000:00" },
+ { NOK, "\\d\\d:\\d\\d:\\d\\d", "00:0000" },
+ { NOK, "\\d\\d:\\d\\d:\\d\\d", "100:0:00" },
+ { NOK, "\\d\\d:\\d\\d:\\d\\d", "00:100:00" },
+ { NOK, "\\d\\d:\\d\\d:\\d\\d", "0:00:100" },
+ { OK, "\\d\\d?:\\d\\d?:\\d\\d?", "0:0:0" },
+ { OK, "\\d\\d?:\\d\\d?:\\d\\d?", "0:00:0" },
+ { OK, "\\d\\d?:\\d\\d?:\\d\\d?", "0:0:00" },
+ { OK, "\\d\\d?:\\d\\d?:\\d\\d?", "00:0:0" },
+ { OK, "\\d\\d?:\\d\\d?:\\d\\d?", "00:00:0" },
+ { OK, "\\d\\d?:\\d\\d?:\\d\\d?", "00:0:00" },
+ { OK, "\\d\\d?:\\d\\d?:\\d\\d?", "0:00:00" },
+ { OK, "\\d\\d?:\\d\\d?:\\d\\d?", "00:00:00" },
+ { OK, "[Hh]ello [Ww]orld\\s*[!]?", "Hello world !" },
+ { OK, "[Hh]ello [Ww]orld\\s*[!]?", "hello world !" },
+ { OK, "[Hh]ello [Ww]orld\\s*[!]?", "Hello World !" },
+ { OK, "[Hh]ello [Ww]orld\\s*[!]?", "Hello world! " },
+ { OK, "[Hh]ello [Ww]orld\\s*[!]?", "Hello world !" },
+ { OK, "[Hh]ello [Ww]orld\\s*[!]?", "hello World !" },
+ { NOK, "\\d\\d?:\\d\\d?:\\d\\d?", "a:0" }, /* Failing test case reported in https://github.com/kokke/tiny-regex-c/issues/12 */
+/*
+ { OK, "[^\\w][^-1-4]", ")T" },
+ { OK, "[^\\w][^-1-4]", ")^" },
+ { OK, "[^\\w][^-1-4]", "*)" },
+ { OK, "[^\\w][^-1-4]", "!." },
+ { OK, "[^\\w][^-1-4]", " x" },
+ { OK, "[^\\w][^-1-4]", "$b" },
+*/
+ { OK, ".?bar", "real_bar" },
+ { NOK, ".?bar", "real_foo" },
+ { NOK, "X?Y", "Z" },
+};
+
+
+void re_print(re_t);
+
+int main()
+{
+ char* text;
+ char* pattern;
+ int should_fail;
+ size_t ntests = sizeof(test_vector) / sizeof(*test_vector);
+ size_t nfailed = 0;
+ size_t i;
+
+ for (i = 0; i < ntests; ++i)
+ {
+ pattern = test_vector[i][1];
+ text = test_vector[i][2];
+ should_fail = (test_vector[i][0] == NOK);
+
+ int m = re_match(pattern, text);
+
+ if (should_fail)
+ {
+ if (m != (-1))
+ {
+ printf("\n");
+ re_print(re_compile(pattern));
+ fprintf(stderr, "[%lu/%lu]: pattern '%s' matched '%s' unexpectedly. \n", (i+1), ntests, pattern, text);
+ nfailed += 1;
+ }
+ }
+ else
+ {
+ if (m == (-1))
+ {
+ printf("\n");
+ re_print(re_compile(pattern));
+ fprintf(stderr, "[%lu/%lu]: pattern '%s' didn't match '%s' as expected. \n", (i+1), ntests, pattern, text);
+ nfailed += 1;
+ }
+ }
+ }
+
+ // printf("\n");
+ printf("%lu/%lu tests succeeded.\n", ntests - nfailed, ntests);
+ printf("\n");
+ printf("\n");
+ printf("\n");
+
+ return 0;
+}
diff --git a/erts/lib_src/yielding_c_fun/lib/tiny_regex_c/tests/test2.c b/erts/lib_src/yielding_c_fun/lib/tiny_regex_c/tests/test2.c
new file mode 100644
index 0000000000..f234f2d49a
--- /dev/null
+++ b/erts/lib_src/yielding_c_fun/lib/tiny_regex_c/tests/test2.c
@@ -0,0 +1,2097 @@
+/*
+ * A small performance test, to see how the library performs on a few MBs of text.
+ *
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include "re.h"
+
+
+char buf[] = {
+ 0x79, 0x66, 0x75, 0x66, 0x63, 0x6f, 0x75, 0x62, 0x74, 0x77, 0x66, 0x6f, 0x69, 0x72, 0x71, 0x78,
+ 0x66, 0x69, 0x63, 0x72, 0x6c, 0x61, 0x6a, 0x79, 0x68, 0x6d, 0x67, 0x66, 0x67, 0x75, 0x69, 0x65,
+ 0x62, 0x6d, 0x72, 0x63, 0x67, 0x74, 0x64, 0x62, 0x67, 0x64, 0x6c, 0x66, 0x78, 0x68, 0x75, 0x6f,
+ 0x73, 0x6b, 0x68, 0x68, 0x64, 0x68, 0x71, 0x66, 0x79, 0x6a, 0x6a, 0x67, 0x70, 0x6e, 0x6f, 0x77,
+ 0x6e, 0x68, 0x79, 0x65, 0x76, 0x61, 0x73, 0x6e, 0x77, 0x71, 0x74, 0x64, 0x77, 0x76, 0x74, 0x6d,
+ 0x6e, 0x76, 0x79, 0x71, 0x79, 0x79, 0x6a, 0x63, 0x74, 0x75, 0x6f, 0x74, 0x6d, 0x66, 0x67, 0x64,
+ 0x77, 0x61, 0x75, 0x79, 0x72, 0x79, 0x69, 0x73, 0x6f, 0x69, 0x67, 0x65, 0x6a, 0x6b, 0x66, 0x69,
+ 0x63, 0x79, 0x64, 0x71, 0x68, 0x6c, 0x67, 0x66, 0x6c, 0x6e, 0x79, 0x64, 0x67, 0x76, 0x64, 0x6a,
+ 0x73, 0x64, 0x6a, 0x61, 0x79, 0x6d, 0x71, 0x72, 0x68, 0x67, 0x68, 0x67, 0x66, 0x64, 0x63, 0x77,
+ 0x6f, 0x73, 0x67, 0x6d, 0x63, 0x74, 0x77, 0x72, 0x6b, 0x6f, 0x6f, 0x63, 0x68, 0x6f, 0x66, 0x67,
+ 0x73, 0x66, 0x6f, 0x75, 0x77, 0x62, 0x71, 0x6b, 0x77, 0x72, 0x68, 0x6d, 0x66, 0x71, 0x78, 0x75,
+ 0x6a, 0x74, 0x75, 0x6c, 0x69, 0x62, 0x64, 0x61, 0x78, 0x78, 0x66, 0x71, 0x61, 0x6d, 0x76, 0x76,
+ 0x6f, 0x65, 0x68, 0x78, 0x70, 0x79, 0x65, 0x68, 0x76, 0x62, 0x73, 0x6f, 0x64, 0x76, 0x63, 0x64,
+ 0x74, 0x73, 0x6e, 0x6a, 0x6a, 0x61, 0x72, 0x62, 0x66, 0x76, 0x63, 0x71, 0x6d, 0x74, 0x67, 0x75,
+ 0x6a, 0x6b, 0x6a, 0x6e, 0x75, 0x72, 0x73, 0x6d, 0x65, 0x69, 0x79, 0x62, 0x6d, 0x67, 0x63, 0x63,
+ 0x6c, 0x6c, 0x67, 0x6e, 0x6b, 0x76, 0x6e, 0x61, 0x79, 0x6a, 0x62, 0x6d, 0x70, 0x69, 0x67, 0x68,
+ 0x70, 0x74, 0x6b, 0x70, 0x74, 0x6e, 0x62, 0x6f, 0x6e, 0x6e, 0x61, 0x76, 0x68, 0x78, 0x77, 0x6f,
+ 0x77, 0x76, 0x6d, 0x6d, 0x75, 0x6b, 0x73, 0x6e, 0x79, 0x69, 0x6f, 0x72, 0x6a, 0x78, 0x67, 0x65,
+ 0x6b, 0x71, 0x61, 0x6b, 0x72, 0x75, 0x69, 0x65, 0x6b, 0x70, 0x69, 0x72, 0x70, 0x76, 0x79, 0x76,
+ 0x72, 0x76, 0x69, 0x76, 0x67, 0x66, 0x61, 0x72, 0x6c, 0x71, 0x66, 0x79, 0x74, 0x78, 0x6c, 0x6e,
+ 0x64, 0x68, 0x61, 0x66, 0x6f, 0x76, 0x6e, 0x67, 0x63, 0x78, 0x61, 0x75, 0x73, 0x65, 0x69, 0x77,
+ 0x67, 0x75, 0x77, 0x62, 0x75, 0x6b, 0x70, 0x77, 0x75, 0x69, 0x70, 0x62, 0x76, 0x73, 0x71, 0x70,
+ 0x78, 0x71, 0x77, 0x78, 0x66, 0x6e, 0x75, 0x65, 0x76, 0x6b, 0x79, 0x6d, 0x74, 0x61, 0x68, 0x61,
+ 0x78, 0x6e, 0x74, 0x77, 0x63, 0x79, 0x6e, 0x6d, 0x77, 0x71, 0x6f, 0x66, 0x6f, 0x65, 0x65, 0x72,
+ 0x61, 0x76, 0x64, 0x64, 0x67, 0x77, 0x69, 0x78, 0x6a, 0x75, 0x67, 0x66, 0x63, 0x79, 0x65, 0x6d,
+ 0x62, 0x68, 0x72, 0x6e, 0x78, 0x6d, 0x71, 0x6c, 0x6e, 0x6a, 0x62, 0x62, 0x64, 0x75, 0x6c, 0x6d,
+ 0x61, 0x71, 0x67, 0x6d, 0x68, 0x6f, 0x67, 0x71, 0x69, 0x74, 0x75, 0x78, 0x69, 0x6c, 0x65, 0x79,
+ 0x6e, 0x65, 0x76, 0x61, 0x75, 0x6a, 0x65, 0x73, 0x68, 0x63, 0x62, 0x6d, 0x6b, 0x6d, 0x6f, 0x6d,
+ 0x6c, 0x69, 0x79, 0x69, 0x6e, 0x76, 0x74, 0x66, 0x6f, 0x63, 0x6c, 0x6e, 0x6a, 0x75, 0x70, 0x70,
+ 0x62, 0x78, 0x71, 0x63, 0x6d, 0x6e, 0x67, 0x6e, 0x66, 0x65, 0x72, 0x62, 0x62, 0x74, 0x78, 0x77,
+ 0x6f, 0x69, 0x66, 0x75, 0x76, 0x79, 0x6c, 0x73, 0x62, 0x6e, 0x6c, 0x70, 0x6a, 0x76, 0x6c, 0x78,
+ 0x6e, 0x66, 0x6f, 0x68, 0x65, 0x6c, 0x75, 0x70, 0x62, 0x62, 0x78, 0x61, 0x62, 0x6b, 0x61, 0x6e,
+ 0x61, 0x78, 0x71, 0x71, 0x73, 0x6e, 0x6c, 0x6f, 0x6a, 0x67, 0x61, 0x65, 0x61, 0x6b, 0x74, 0x78,
+ 0x70, 0x67, 0x73, 0x65, 0x78, 0x79, 0x74, 0x76, 0x6f, 0x74, 0x73, 0x6f, 0x63, 0x76, 0x63, 0x70,
+ 0x66, 0x6d, 0x6a, 0x71, 0x62, 0x78, 0x68, 0x74, 0x71, 0x70, 0x69, 0x65, 0x77, 0x65, 0x77, 0x65,
+ 0x78, 0x6a, 0x72, 0x71, 0x6e, 0x65, 0x79, 0x64, 0x6b, 0x73, 0x79, 0x63, 0x64, 0x6b, 0x78, 0x64,
+ 0x66, 0x69, 0x77, 0x62, 0x6f, 0x71, 0x6c, 0x73, 0x6c, 0x68, 0x78, 0x70, 0x71, 0x79, 0x72, 0x79,
+ 0x6f, 0x6b, 0x6c, 0x77, 0x63, 0x6a, 0x68, 0x67, 0x69, 0x70, 0x68, 0x6e, 0x61, 0x71, 0x77, 0x61,
+ 0x70, 0x65, 0x76, 0x6c, 0x72, 0x71, 0x66, 0x66, 0x6e, 0x67, 0x71, 0x6c, 0x77, 0x63, 0x6c, 0x65,
+ 0x6b, 0x72, 0x66, 0x63, 0x73, 0x61, 0x70, 0x71, 0x64, 0x69, 0x6e, 0x71, 0x78, 0x6c, 0x74, 0x61,
+ 0x77, 0x65, 0x71, 0x61, 0x64, 0x66, 0x68, 0x67, 0x6f, 0x75, 0x66, 0x69, 0x73, 0x6e, 0x76, 0x6e,
+ 0x66, 0x63, 0x66, 0x74, 0x6c, 0x6b, 0x68, 0x62, 0x6e, 0x6c, 0x78, 0x65, 0x73, 0x70, 0x76, 0x78,
+ 0x61, 0x70, 0x66, 0x70, 0x79, 0x67, 0x69, 0x67, 0x68, 0x78, 0x70, 0x76, 0x73, 0x63, 0x6b, 0x68,
+ 0x71, 0x67, 0x66, 0x6c, 0x78, 0x77, 0x6c, 0x72, 0x76, 0x6c, 0x73, 0x68, 0x68, 0x61, 0x68, 0x6c,
+ 0x6c, 0x79, 0x6a, 0x66, 0x74, 0x77, 0x65, 0x62, 0x6e, 0x71, 0x6b, 0x61, 0x71, 0x75, 0x67, 0x62,
+ 0x76, 0x62, 0x6b, 0x71, 0x71, 0x6b, 0x65, 0x69, 0x74, 0x75, 0x79, 0x77, 0x6c, 0x65, 0x64, 0x67,
+ 0x70, 0x6a, 0x71, 0x73, 0x6c, 0x73, 0x61, 0x69, 0x70, 0x68, 0x73, 0x76, 0x74, 0x69, 0x77, 0x69,
+ 0x6b, 0x6b, 0x70, 0x68, 0x78, 0x79, 0x67, 0x79, 0x62, 0x63, 0x74, 0x68, 0x69, 0x63, 0x77, 0x70,
+ 0x6b, 0x6a, 0x74, 0x6a, 0x66, 0x75, 0x75, 0x73, 0x65, 0x68, 0x63, 0x6a, 0x64, 0x76, 0x64, 0x72,
+ 0x79, 0x77, 0x6d, 0x74, 0x69, 0x6a, 0x66, 0x65, 0x67, 0x77, 0x67, 0x73, 0x73, 0x74, 0x6a, 0x75,
+ 0x65, 0x6e, 0x72, 0x61, 0x63, 0x6e, 0x67, 0x74, 0x6d, 0x6d, 0x65, 0x76, 0x76, 0x65, 0x61, 0x64,
+ 0x70, 0x77, 0x78, 0x72, 0x77, 0x70, 0x6f, 0x69, 0x66, 0x71, 0x6e, 0x6c, 0x75, 0x68, 0x68, 0x6f,
+ 0x63, 0x6f, 0x6d, 0x62, 0x61, 0x70, 0x72, 0x77, 0x6c, 0x76, 0x6b, 0x70, 0x68, 0x6e, 0x6d, 0x78,
+ 0x68, 0x65, 0x6c, 0x6f, 0x65, 0x73, 0x63, 0x69, 0x65, 0x74, 0x77, 0x66, 0x64, 0x74, 0x68, 0x66,
+ 0x71, 0x62, 0x67, 0x72, 0x6a, 0x64, 0x68, 0x77, 0x6d, 0x72, 0x6b, 0x78, 0x72, 0x6b, 0x72, 0x78,
+ 0x70, 0x65, 0x69, 0x65, 0x72, 0x63, 0x6a, 0x69, 0x62, 0x75, 0x72, 0x6b, 0x69, 0x6a, 0x78, 0x65,
+ 0x68, 0x69, 0x6d, 0x6a, 0x75, 0x72, 0x66, 0x76, 0x79, 0x66, 0x67, 0x74, 0x79, 0x69, 0x76, 0x6d,
+ 0x77, 0x77, 0x65, 0x6b, 0x73, 0x61, 0x73, 0x6c, 0x70, 0x74, 0x6f, 0x76, 0x79, 0x6a, 0x66, 0x64,
+ 0x67, 0x6d, 0x72, 0x66, 0x66, 0x6e, 0x68, 0x63, 0x64, 0x73, 0x6c, 0x63, 0x6c, 0x71, 0x6d, 0x6c,
+ 0x63, 0x69, 0x65, 0x6b, 0x73, 0x64, 0x6b, 0x76, 0x68, 0x73, 0x71, 0x61, 0x72, 0x67, 0x6a, 0x79,
+ 0x78, 0x6a, 0x6f, 0x65, 0x71, 0x65, 0x72, 0x66, 0x62, 0x6d, 0x63, 0x73, 0x64, 0x77, 0x78, 0x77,
+ 0x6f, 0x67, 0x70, 0x6d, 0x6f, 0x66, 0x79, 0x79, 0x74, 0x75, 0x65, 0x75, 0x6e, 0x72, 0x76, 0x72,
+ 0x6a, 0x77, 0x78, 0x6e, 0x6e, 0x67, 0x74, 0x6d, 0x6f, 0x66, 0x6f, 0x6a, 0x67, 0x65, 0x6c, 0x75,
+ 0x63, 0x78, 0x6c, 0x67, 0x78, 0x79, 0x72, 0x61, 0x6b, 0x71, 0x76, 0x77, 0x6d, 0x6d, 0x69, 0x76,
+ 0x6f, 0x6e, 0x66, 0x61, 0x61, 0x62, 0x62, 0x79, 0x61, 0x66, 0x6d, 0x78, 0x74, 0x78, 0x69, 0x6f,
+ 0x63, 0x79, 0x70, 0x74, 0x75, 0x79, 0x63, 0x63, 0x70, 0x6d, 0x70, 0x66, 0x67, 0x65, 0x61, 0x70,
+ 0x79, 0x79, 0x6d, 0x6a, 0x6d, 0x77, 0x6a, 0x75, 0x62, 0x6b, 0x69, 0x64, 0x71, 0x67, 0x64, 0x63,
+ 0x62, 0x68, 0x6c, 0x68, 0x72, 0x6a, 0x67, 0x6d, 0x72, 0x73, 0x78, 0x72, 0x64, 0x65, 0x77, 0x72,
+ 0x72, 0x71, 0x6a, 0x64, 0x70, 0x71, 0x76, 0x73, 0x65, 0x75, 0x6d, 0x76, 0x6f, 0x6b, 0x66, 0x73,
+ 0x6b, 0x69, 0x6b, 0x66, 0x68, 0x79, 0x64, 0x78, 0x6b, 0x67, 0x76, 0x6e, 0x74, 0x70, 0x67, 0x69,
+ 0x62, 0x64, 0x72, 0x6a, 0x6e, 0x72, 0x72, 0x6b, 0x73, 0x6f, 0x6d, 0x6c, 0x6c, 0x66, 0x78, 0x75,
+ 0x66, 0x66, 0x6e, 0x6b, 0x69, 0x74, 0x6c, 0x79, 0x69, 0x70, 0x6c, 0x72, 0x6e, 0x74, 0x6e, 0x6e,
+ 0x67, 0x6f, 0x62, 0x70, 0x65, 0x62, 0x62, 0x6a, 0x68, 0x66, 0x76, 0x6a, 0x74, 0x6a, 0x69, 0x71,
+ 0x6c, 0x76, 0x77, 0x6b, 0x6b, 0x73, 0x6f, 0x64, 0x62, 0x67, 0x6a, 0x6b, 0x68, 0x65, 0x61, 0x69,
+ 0x77, 0x78, 0x74, 0x62, 0x70, 0x73, 0x70, 0x68, 0x6b, 0x75, 0x72, 0x70, 0x68, 0x6b, 0x75, 0x6d,
+ 0x62, 0x74, 0x77, 0x65, 0x71, 0x77, 0x6d, 0x65, 0x70, 0x66, 0x70, 0x6c, 0x69, 0x68, 0x75, 0x75,
+ 0x62, 0x62, 0x75, 0x69, 0x6d, 0x68, 0x63, 0x6e, 0x6d, 0x6b, 0x78, 0x70, 0x77, 0x61, 0x77, 0x69,
+ 0x78, 0x69, 0x6a, 0x68, 0x64, 0x74, 0x6e, 0x72, 0x6c, 0x6d, 0x72, 0x75, 0x6e, 0x66, 0x66, 0x67,
+ 0x6f, 0x73, 0x6e, 0x6f, 0x78, 0x72, 0x72, 0x6c, 0x66, 0x70, 0x66, 0x69, 0x6a, 0x71, 0x6e, 0x67,
+ 0x6c, 0x74, 0x76, 0x78, 0x74, 0x77, 0x73, 0x66, 0x73, 0x75, 0x70, 0x67, 0x67, 0x62, 0x75, 0x77,
+ 0x67, 0x78, 0x76, 0x6d, 0x6a, 0x78, 0x69, 0x75, 0x63, 0x6a, 0x62, 0x6e, 0x6d, 0x66, 0x61, 0x6b,
+ 0x73, 0x68, 0x72, 0x78, 0x79, 0x6d, 0x69, 0x68, 0x6f, 0x73, 0x6d, 0x62, 0x68, 0x70, 0x78, 0x6f,
+ 0x6e, 0x63, 0x63, 0x6f, 0x78, 0x65, 0x74, 0x6a, 0x77, 0x78, 0x79, 0x6a, 0x62, 0x78, 0x70, 0x69,
+ 0x65, 0x79, 0x77, 0x6d, 0x6b, 0x6c, 0x6b, 0x62, 0x6e, 0x75, 0x73, 0x6e, 0x63, 0x67, 0x6c, 0x63,
+ 0x63, 0x6a, 0x71, 0x62, 0x61, 0x78, 0x6c, 0x6d, 0x69, 0x64, 0x6f, 0x71, 0x62, 0x6f, 0x62, 0x72,
+ 0x6b, 0x61, 0x65, 0x77, 0x62, 0x75, 0x79, 0x6e, 0x73, 0x79, 0x67, 0x76, 0x63, 0x70, 0x69, 0x6a,
+ 0x6c, 0x62, 0x70, 0x62, 0x61, 0x77, 0x69, 0x6e, 0x62, 0x71, 0x6e, 0x6b, 0x72, 0x76, 0x76, 0x64,
+ 0x77, 0x68, 0x66, 0x78, 0x77, 0x64, 0x6b, 0x6a, 0x74, 0x66, 0x67, 0x69, 0x70, 0x65, 0x6a, 0x68,
+ 0x71, 0x76, 0x71, 0x69, 0x75, 0x64, 0x6b, 0x71, 0x71, 0x61, 0x67, 0x6d, 0x67, 0x62, 0x69, 0x6b,
+ 0x61, 0x66, 0x62, 0x63, 0x76, 0x64, 0x66, 0x73, 0x63, 0x78, 0x74, 0x77, 0x6e, 0x63, 0x68, 0x61,
+ 0x6c, 0x71, 0x70, 0x69, 0x65, 0x77, 0x6d, 0x69, 0x72, 0x6b, 0x6e, 0x6f, 0x75, 0x78, 0x6d, 0x74,
+ 0x6a, 0x68, 0x72, 0x6b, 0x69, 0x72, 0x6b, 0x78, 0x72, 0x68, 0x65, 0x68, 0x76, 0x6d, 0x67, 0x71,
+ 0x65, 0x6f, 0x61, 0x72, 0x6f, 0x63, 0x70, 0x67, 0x78, 0x73, 0x6f, 0x62, 0x6b, 0x65, 0x64, 0x6b,
+ 0x62, 0x76, 0x76, 0x77, 0x63, 0x6a, 0x77, 0x78, 0x6e, 0x79, 0x63, 0x75, 0x62, 0x75, 0x67, 0x67,
+ 0x62, 0x6a, 0x6a, 0x74, 0x6f, 0x6a, 0x6c, 0x75, 0x70, 0x65, 0x61, 0x6f, 0x68, 0x6b, 0x67, 0x6e,
+ 0x6a, 0x6c, 0x69, 0x71, 0x63, 0x75, 0x6a, 0x72, 0x71, 0x6d, 0x75, 0x73, 0x78, 0x62, 0x78, 0x66,
+ 0x79, 0x79, 0x70, 0x6e, 0x75, 0x6c, 0x66, 0x6e, 0x6b, 0x6b, 0x73, 0x71, 0x6d, 0x77, 0x68, 0x6d,
+ 0x76, 0x75, 0x6c, 0x77, 0x6f, 0x71, 0x6c, 0x67, 0x70, 0x6f, 0x76, 0x70, 0x72, 0x64, 0x74, 0x77,
+ 0x63, 0x78, 0x69, 0x73, 0x6f, 0x77, 0x6f, 0x6a, 0x64, 0x64, 0x77, 0x6b, 0x79, 0x72, 0x65, 0x6f,
+ 0x74, 0x61, 0x71, 0x71, 0x6d, 0x6d, 0x68, 0x66, 0x63, 0x6d, 0x73, 0x76, 0x6f, 0x76, 0x68, 0x6c,
+ 0x61, 0x6b, 0x76, 0x6a, 0x69, 0x73, 0x76, 0x72, 0x6c, 0x63, 0x78, 0x74, 0x73, 0x78, 0x62, 0x74,
+ 0x70, 0x68, 0x62, 0x6a, 0x70, 0x66, 0x74, 0x66, 0x61, 0x66, 0x72, 0x74, 0x6f, 0x67, 0x76, 0x67,
+ 0x6c, 0x64, 0x73, 0x69, 0x6d, 0x69, 0x6a, 0x74, 0x67, 0x73, 0x78, 0x69, 0x75, 0x72, 0x6e, 0x61,
+ 0x76, 0x6a, 0x69, 0x73, 0x6a, 0x77, 0x71, 0x67, 0x66, 0x6e, 0x66, 0x74, 0x71, 0x6a, 0x6f, 0x64,
+ 0x63, 0x6c, 0x67, 0x70, 0x77, 0x78, 0x64, 0x6f, 0x6b, 0x69, 0x76, 0x75, 0x66, 0x6c, 0x6d, 0x6d,
+ 0x65, 0x75, 0x65, 0x75, 0x77, 0x6a, 0x72, 0x6b, 0x77, 0x67, 0x65, 0x63, 0x6f, 0x6e, 0x6d, 0x6a,
+ 0x6d, 0x66, 0x78, 0x66, 0x66, 0x76, 0x61, 0x6f, 0x74, 0x6f, 0x63, 0x75, 0x79, 0x74, 0x61, 0x6a,
+ 0x64, 0x62, 0x75, 0x79, 0x77, 0x6a, 0x68, 0x76, 0x67, 0x61, 0x74, 0x64, 0x6d, 0x64, 0x75, 0x65,
+ 0x64, 0x6c, 0x70, 0x75, 0x68, 0x6c, 0x70, 0x74, 0x70, 0x71, 0x6a, 0x6d, 0x61, 0x79, 0x67, 0x76,
+ 0x69, 0x6c, 0x6a, 0x63, 0x6b, 0x69, 0x77, 0x63, 0x77, 0x63, 0x61, 0x72, 0x66, 0x76, 0x69, 0x78,
+ 0x72, 0x68, 0x72, 0x75, 0x74, 0x63, 0x64, 0x6a, 0x73, 0x79, 0x70, 0x6f, 0x69, 0x65, 0x6b, 0x73,
+ 0x71, 0x70, 0x6b, 0x68, 0x61, 0x68, 0x79, 0x62, 0x75, 0x65, 0x79, 0x68, 0x65, 0x6f, 0x67, 0x70,
+ 0x77, 0x78, 0x6f, 0x77, 0x72, 0x65, 0x75, 0x73, 0x6a, 0x64, 0x6c, 0x75, 0x6e, 0x65, 0x76, 0x78,
+ 0x73, 0x6a, 0x69, 0x6d, 0x61, 0x6d, 0x6a, 0x6d, 0x71, 0x6d, 0x71, 0x6b, 0x64, 0x6c, 0x65, 0x61,
+ 0x67, 0x6a, 0x6c, 0x64, 0x78, 0x64, 0x64, 0x78, 0x75, 0x73, 0x70, 0x76, 0x69, 0x66, 0x6e, 0x74,
+ 0x72, 0x73, 0x79, 0x78, 0x72, 0x74, 0x6b, 0x78, 0x71, 0x63, 0x62, 0x72, 0x75, 0x6d, 0x68, 0x63,
+ 0x69, 0x78, 0x6d, 0x71, 0x68, 0x6d, 0x64, 0x65, 0x75, 0x70, 0x6b, 0x73, 0x63, 0x72, 0x69, 0x75,
+ 0x77, 0x76, 0x67, 0x72, 0x75, 0x6c, 0x76, 0x65, 0x6e, 0x70, 0x63, 0x6f, 0x66, 0x71, 0x65, 0x62,
+ 0x73, 0x64, 0x70, 0x63, 0x78, 0x74, 0x6f, 0x73, 0x75, 0x6f, 0x78, 0x73, 0x71, 0x6e, 0x77, 0x75,
+ 0x70, 0x76, 0x74, 0x75, 0x61, 0x70, 0x68, 0x72, 0x71, 0x79, 0x6d, 0x72, 0x69, 0x79, 0x69, 0x74,
+ 0x61, 0x72, 0x75, 0x68, 0x72, 0x79, 0x62, 0x65, 0x6d, 0x61, 0x71, 0x6c, 0x77, 0x67, 0x73, 0x66,
+ 0x67, 0x69, 0x6e, 0x62, 0x6c, 0x74, 0x76, 0x70, 0x71, 0x64, 0x71, 0x79, 0x70, 0x73, 0x63, 0x6c,
+ 0x67, 0x63, 0x73, 0x6f, 0x68, 0x78, 0x73, 0x66, 0x6c, 0x6b, 0x62, 0x78, 0x70, 0x68, 0x62, 0x6c,
+ 0x66, 0x67, 0x74, 0x77, 0x61, 0x6c, 0x71, 0x70, 0x6a, 0x6f, 0x6c, 0x75, 0x6e, 0x61, 0x79, 0x68,
+ 0x73, 0x61, 0x79, 0x69, 0x71, 0x6e, 0x6d, 0x66, 0x6c, 0x68, 0x74, 0x72, 0x64, 0x64, 0x6a, 0x75,
+ 0x70, 0x6c, 0x69, 0x69, 0x68, 0x72, 0x66, 0x75, 0x61, 0x61, 0x69, 0x64, 0x66, 0x69, 0x68, 0x63,
+ 0x67, 0x6d, 0x76, 0x71, 0x6a, 0x6e, 0x6a, 0x78, 0x61, 0x77, 0x67, 0x77, 0x69, 0x77, 0x71, 0x6d,
+ 0x6b, 0x6a, 0x72, 0x71, 0x72, 0x6e, 0x61, 0x65, 0x6f, 0x71, 0x6f, 0x64, 0x6d, 0x79, 0x61, 0x66,
+ 0x6b, 0x77, 0x76, 0x64, 0x67, 0x6f, 0x66, 0x6e, 0x79, 0x64, 0x77, 0x75, 0x6d, 0x76, 0x64, 0x69,
+ 0x78, 0x6b, 0x72, 0x6f, 0x75, 0x74, 0x71, 0x6c, 0x77, 0x61, 0x6d, 0x69, 0x61, 0x75, 0x72, 0x61,
+ 0x6d, 0x78, 0x6c, 0x71, 0x6e, 0x74, 0x6c, 0x6e, 0x71, 0x78, 0x77, 0x6f, 0x64, 0x76, 0x66, 0x62,
+ 0x62, 0x75, 0x6b, 0x75, 0x66, 0x75, 0x73, 0x6a, 0x76, 0x75, 0x70, 0x73, 0x68, 0x6c, 0x77, 0x6d,
+ 0x75, 0x71, 0x78, 0x73, 0x64, 0x73, 0x78, 0x69, 0x76, 0x64, 0x61, 0x6e, 0x62, 0x6a, 0x79, 0x6f,
+ 0x70, 0x6a, 0x6d, 0x75, 0x6e, 0x75, 0x63, 0x66, 0x65, 0x75, 0x6a, 0x64, 0x6b, 0x65, 0x66, 0x64,
+ 0x62, 0x66, 0x78, 0x67, 0x77, 0x75, 0x74, 0x62, 0x67, 0x61, 0x6e, 0x67, 0x68, 0x6b, 0x6c, 0x73,
+ 0x6d, 0x69, 0x69, 0x75, 0x6e, 0x6c, 0x69, 0x73, 0x77, 0x69, 0x63, 0x64, 0x75, 0x6c, 0x6b, 0x78,
+ 0x69, 0x78, 0x67, 0x73, 0x72, 0x6a, 0x64, 0x78, 0x6a, 0x72, 0x78, 0x6f, 0x61, 0x76, 0x71, 0x66,
+ 0x76, 0x63, 0x77, 0x67, 0x75, 0x65, 0x73, 0x64, 0x64, 0x6a, 0x77, 0x68, 0x6f, 0x61, 0x6f, 0x78,
+ 0x6e, 0x6d, 0x78, 0x70, 0x76, 0x6b, 0x67, 0x67, 0x75, 0x6d, 0x6e, 0x62, 0x70, 0x74, 0x66, 0x6a,
+ 0x70, 0x68, 0x70, 0x6c, 0x74, 0x6d, 0x62, 0x66, 0x69, 0x73, 0x6a, 0x70, 0x66, 0x69, 0x6b, 0x73,
+ 0x62, 0x70, 0x6f, 0x61, 0x63, 0x63, 0x71, 0x6f, 0x70, 0x6a, 0x75, 0x64, 0x6d, 0x74, 0x68, 0x76,
+ 0x61, 0x6e, 0x72, 0x71, 0x62, 0x70, 0x64, 0x66, 0x70, 0x71, 0x61, 0x69, 0x68, 0x64, 0x61, 0x65,
+ 0x79, 0x78, 0x6b, 0x72, 0x64, 0x63, 0x6d, 0x6c, 0x67, 0x61, 0x6a, 0x67, 0x64, 0x73, 0x70, 0x76,
+ 0x74, 0x70, 0x6c, 0x66, 0x77, 0x71, 0x66, 0x72, 0x6c, 0x67, 0x69, 0x66, 0x70, 0x68, 0x67, 0x6e,
+ 0x70, 0x69, 0x67, 0x71, 0x73, 0x72, 0x6d, 0x71, 0x74, 0x64, 0x6b, 0x71, 0x73, 0x70, 0x78, 0x70,
+ 0x75, 0x76, 0x74, 0x6e, 0x73, 0x63, 0x6d, 0x64, 0x6e, 0x73, 0x79, 0x68, 0x6b, 0x6e, 0x6a, 0x77,
+ 0x6e, 0x62, 0x67, 0x6d, 0x72, 0x68, 0x6c, 0x76, 0x64, 0x6f, 0x73, 0x6d, 0x62, 0x67, 0x69, 0x68,
+ 0x64, 0x65, 0x72, 0x67, 0x75, 0x66, 0x68, 0x64, 0x61, 0x67, 0x6a, 0x75, 0x75, 0x6c, 0x72, 0x71,
+ 0x75, 0x74, 0x73, 0x78, 0x74, 0x6d, 0x62, 0x79, 0x6f, 0x63, 0x6f, 0x62, 0x67, 0x73, 0x70, 0x70,
+ 0x72, 0x6e, 0x68, 0x79, 0x76, 0x77, 0x76, 0x6e, 0x6e, 0x6e, 0x78, 0x65, 0x65, 0x63, 0x64, 0x61,
+ 0x62, 0x78, 0x71, 0x72, 0x63, 0x68, 0x71, 0x63, 0x73, 0x70, 0x70, 0x61, 0x71, 0x76, 0x64, 0x67,
+ 0x74, 0x71, 0x74, 0x67, 0x74, 0x77, 0x6e, 0x79, 0x73, 0x67, 0x79, 0x70, 0x77, 0x6f, 0x6b, 0x79,
+ 0x74, 0x79, 0x67, 0x6a, 0x6e, 0x6a, 0x70, 0x76, 0x62, 0x77, 0x74, 0x70, 0x72, 0x63, 0x6c, 0x74,
+ 0x65, 0x64, 0x63, 0x78, 0x64, 0x6d, 0x73, 0x69, 0x65, 0x70, 0x6d, 0x71, 0x6e, 0x6c, 0x62, 0x64,
+ 0x67, 0x6c, 0x72, 0x65, 0x6b, 0x63, 0x77, 0x6d, 0x63, 0x70, 0x72, 0x65, 0x6d, 0x74, 0x6d, 0x63,
+ 0x78, 0x75, 0x68, 0x70, 0x73, 0x63, 0x6e, 0x77, 0x71, 0x61, 0x73, 0x6b, 0x75, 0x6e, 0x76, 0x74,
+ 0x63, 0x70, 0x72, 0x65, 0x62, 0x75, 0x61, 0x6a, 0x76, 0x66, 0x75, 0x73, 0x64, 0x67, 0x6d, 0x77,
+ 0x64, 0x6a, 0x66, 0x6b, 0x6b, 0x6c, 0x66, 0x64, 0x69, 0x72, 0x70, 0x76, 0x67, 0x74, 0x6c, 0x6a,
+ 0x72, 0x67, 0x74, 0x66, 0x6a, 0x62, 0x66, 0x73, 0x79, 0x77, 0x6b, 0x62, 0x77, 0x6a, 0x75, 0x73,
+ 0x61, 0x69, 0x78, 0x6b, 0x66, 0x67, 0x62, 0x70, 0x65, 0x64, 0x76, 0x79, 0x77, 0x6c, 0x75, 0x74,
+ 0x77, 0x63, 0x73, 0x62, 0x63, 0x73, 0x6f, 0x6c, 0x64, 0x65, 0x6e, 0x6c, 0x6a, 0x72, 0x77, 0x6b,
+ 0x61, 0x78, 0x75, 0x77, 0x6c, 0x61, 0x64, 0x63, 0x64, 0x6a, 0x61, 0x68, 0x6e, 0x65, 0x68, 0x66,
+ 0x71, 0x6b, 0x73, 0x6f, 0x62, 0x6e, 0x69, 0x72, 0x79, 0x63, 0x6f, 0x79, 0x79, 0x75, 0x70, 0x77,
+ 0x75, 0x76, 0x63, 0x77, 0x76, 0x75, 0x70, 0x68, 0x65, 0x68, 0x63, 0x6c, 0x6d, 0x62, 0x73, 0x68,
+ 0x70, 0x68, 0x6b, 0x70, 0x63, 0x70, 0x79, 0x66, 0x68, 0x70, 0x72, 0x77, 0x65, 0x6e, 0x65, 0x74,
+ 0x62, 0x64, 0x74, 0x78, 0x6d, 0x70, 0x79, 0x74, 0x74, 0x6f, 0x6e, 0x6d, 0x78, 0x73, 0x6d, 0x6a,
+ 0x74, 0x6d, 0x75, 0x6f, 0x79, 0x66, 0x6c, 0x6b, 0x62, 0x68, 0x77, 0x67, 0x75, 0x71, 0x6e, 0x6d,
+ 0x6b, 0x63, 0x6a, 0x6a, 0x78, 0x78, 0x70, 0x68, 0x74, 0x72, 0x74, 0x65, 0x6f, 0x77, 0x6c, 0x72,
+ 0x74, 0x6c, 0x66, 0x78, 0x77, 0x78, 0x72, 0x73, 0x6f, 0x78, 0x6d, 0x66, 0x63, 0x61, 0x63, 0x6e,
+ 0x69, 0x73, 0x79, 0x72, 0x6f, 0x72, 0x62, 0x76, 0x75, 0x71, 0x70, 0x61, 0x78, 0x77, 0x76, 0x70,
+ 0x78, 0x69, 0x77, 0x65, 0x75, 0x63, 0x71, 0x6f, 0x6b, 0x6a, 0x74, 0x61, 0x63, 0x61, 0x64, 0x72,
+ 0x64, 0x62, 0x67, 0x72, 0x79, 0x6b, 0x74, 0x70, 0x6e, 0x6b, 0x63, 0x79, 0x66, 0x74, 0x6c, 0x6e,
+ 0x78, 0x6c, 0x70, 0x73, 0x6c, 0x69, 0x66, 0x6d, 0x71, 0x77, 0x79, 0x76, 0x71, 0x6e, 0x67, 0x78,
+ 0x74, 0x6b, 0x6b, 0x64, 0x6f, 0x63, 0x77, 0x61, 0x75, 0x6c, 0x6a, 0x6d, 0x6e, 0x72, 0x66, 0x6a,
+ 0x76, 0x75, 0x71, 0x6f, 0x6d, 0x71, 0x77, 0x75, 0x70, 0x77, 0x76, 0x74, 0x70, 0x71, 0x6b, 0x63,
+ 0x6c, 0x64, 0x69, 0x6e, 0x67, 0x68, 0x61, 0x71, 0x6a, 0x71, 0x75, 0x65, 0x69, 0x6b, 0x63, 0x76,
+ 0x79, 0x6b, 0x76, 0x62, 0x77, 0x76, 0x73, 0x78, 0x6d, 0x76, 0x66, 0x6f, 0x69, 0x64, 0x72, 0x6d,
+ 0x6c, 0x62, 0x71, 0x61, 0x72, 0x6c, 0x6c, 0x70, 0x64, 0x69, 0x76, 0x6e, 0x62, 0x62, 0x69, 0x75,
+ 0x64, 0x73, 0x6b, 0x76, 0x76, 0x67, 0x6b, 0x78, 0x76, 0x70, 0x78, 0x70, 0x68, 0x67, 0x72, 0x6e,
+ 0x6b, 0x68, 0x6f, 0x73, 0x69, 0x6d, 0x71, 0x6b, 0x74, 0x6f, 0x79, 0x73, 0x65, 0x78, 0x6c, 0x67,
+ 0x74, 0x73, 0x6f, 0x62, 0x61, 0x73, 0x61, 0x63, 0x62, 0x74, 0x74, 0x6c, 0x68, 0x70, 0x76, 0x77,
+ 0x6a, 0x6a, 0x6a, 0x6f, 0x77, 0x6f, 0x66, 0x68, 0x78, 0x67, 0x64, 0x73, 0x75, 0x69, 0x6d, 0x69,
+ 0x62, 0x6b, 0x77, 0x63, 0x69, 0x6f, 0x61, 0x72, 0x61, 0x6f, 0x61, 0x77, 0x6a, 0x63, 0x69, 0x70,
+ 0x62, 0x79, 0x61, 0x61, 0x66, 0x71, 0x75, 0x72, 0x75, 0x61, 0x69, 0x6c, 0x78, 0x63, 0x78, 0x6c,
+ 0x74, 0x78, 0x6f, 0x78, 0x6a, 0x76, 0x72, 0x61, 0x69, 0x68, 0x6c, 0x67, 0x65, 0x79, 0x75, 0x6d,
+ 0x76, 0x65, 0x62, 0x67, 0x6b, 0x74, 0x6a, 0x62, 0x74, 0x6a, 0x63, 0x66, 0x62, 0x6c, 0x75, 0x6a,
+ 0x78, 0x61, 0x6f, 0x79, 0x62, 0x64, 0x61, 0x64, 0x77, 0x69, 0x77, 0x77, 0x75, 0x78, 0x77, 0x69,
+ 0x72, 0x69, 0x67, 0x65, 0x73, 0x65, 0x71, 0x6d, 0x6c, 0x65, 0x73, 0x68, 0x6e, 0x68, 0x69, 0x6b,
+ 0x72, 0x74, 0x76, 0x78, 0x62, 0x6a, 0x78, 0x72, 0x77, 0x76, 0x78, 0x68, 0x74, 0x74, 0x62, 0x6a,
+ 0x6c, 0x73, 0x76, 0x73, 0x79, 0x71, 0x72, 0x77, 0x65, 0x6f, 0x6a, 0x6e, 0x63, 0x64, 0x73, 0x65,
+ 0x69, 0x74, 0x72, 0x76, 0x68, 0x79, 0x67, 0x67, 0x78, 0x77, 0x75, 0x78, 0x72, 0x72, 0x68, 0x64,
+ 0x76, 0x73, 0x64, 0x61, 0x64, 0x78, 0x6f, 0x67, 0x6c, 0x6e, 0x61, 0x6a, 0x6b, 0x79, 0x71, 0x6b,
+ 0x62, 0x74, 0x71, 0x6b, 0x76, 0x6e, 0x6e, 0x6e, 0x6e, 0x74, 0x6b, 0x76, 0x61, 0x6e, 0x67, 0x65,
+ 0x73, 0x72, 0x79, 0x66, 0x61, 0x65, 0x65, 0x74, 0x6e, 0x61, 0x66, 0x74, 0x70, 0x69, 0x65, 0x67,
+ 0x67, 0x76, 0x62, 0x61, 0x6c, 0x66, 0x67, 0x76, 0x65, 0x76, 0x6a, 0x62, 0x64, 0x64, 0x6b, 0x6d,
+ 0x70, 0x70, 0x63, 0x74, 0x63, 0x67, 0x78, 0x73, 0x72, 0x6d, 0x63, 0x78, 0x6d, 0x70, 0x6e, 0x75,
+ 0x76, 0x74, 0x79, 0x6b, 0x61, 0x69, 0x6d, 0x73, 0x73, 0x69, 0x64, 0x66, 0x6e, 0x76, 0x67, 0x67,
+ 0x62, 0x77, 0x74, 0x68, 0x74, 0x68, 0x65, 0x64, 0x75, 0x68, 0x70, 0x69, 0x73, 0x6f, 0x67, 0x6b,
+ 0x74, 0x79, 0x68, 0x6c, 0x77, 0x73, 0x68, 0x61, 0x64, 0x68, 0x76, 0x61, 0x62, 0x74, 0x71, 0x6f,
+ 0x79, 0x6b, 0x6d, 0x61, 0x63, 0x65, 0x64, 0x74, 0x67, 0x75, 0x73, 0x6d, 0x65, 0x6a, 0x76, 0x69,
+ 0x75, 0x63, 0x72, 0x6c, 0x69, 0x70, 0x73, 0x70, 0x62, 0x77, 0x61, 0x76, 0x65, 0x69, 0x62, 0x70,
+ 0x6f, 0x6d, 0x70, 0x77, 0x78, 0x64, 0x78, 0x62, 0x77, 0x6e, 0x65, 0x71, 0x63, 0x6d, 0x79, 0x74,
+ 0x72, 0x78, 0x6e, 0x6b, 0x74, 0x62, 0x6f, 0x68, 0x74, 0x71, 0x66, 0x6e, 0x6d, 0x73, 0x76, 0x76,
+ 0x6f, 0x76, 0x73, 0x66, 0x66, 0x62, 0x62, 0x68, 0x66, 0x6e, 0x72, 0x72, 0x73, 0x6a, 0x73, 0x72,
+ 0x69, 0x63, 0x73, 0x79, 0x69, 0x6b, 0x66, 0x6e, 0x6c, 0x71, 0x66, 0x66, 0x74, 0x78, 0x62, 0x6e,
+ 0x75, 0x61, 0x74, 0x62, 0x68, 0x6f, 0x76, 0x6c, 0x75, 0x6c, 0x6a, 0x69, 0x67, 0x77, 0x61, 0x6b,
+ 0x76, 0x76, 0x6f, 0x66, 0x62, 0x6f, 0x66, 0x62, 0x67, 0x62, 0x75, 0x6e, 0x72, 0x6d, 0x71, 0x6d,
+ 0x6f, 0x73, 0x69, 0x6c, 0x70, 0x69, 0x69, 0x6a, 0x6d, 0x6e, 0x68, 0x70, 0x63, 0x63, 0x69, 0x68,
+ 0x68, 0x65, 0x73, 0x6a, 0x67, 0x65, 0x74, 0x79, 0x77, 0x67, 0x6d, 0x76, 0x75, 0x69, 0x71, 0x68,
+ 0x62, 0x6c, 0x72, 0x66, 0x76, 0x65, 0x76, 0x71, 0x78, 0x74, 0x68, 0x62, 0x6c, 0x68, 0x68, 0x66,
+ 0x64, 0x62, 0x6d, 0x6f, 0x77, 0x68, 0x69, 0x78, 0x65, 0x71, 0x68, 0x79, 0x6d, 0x67, 0x77, 0x6b,
+ 0x64, 0x6a, 0x79, 0x6b, 0x68, 0x71, 0x65, 0x68, 0x65, 0x6d, 0x74, 0x63, 0x72, 0x67, 0x66, 0x76,
+ 0x6c, 0x61, 0x78, 0x74, 0x6c, 0x67, 0x62, 0x73, 0x75, 0x73, 0x70, 0x66, 0x69, 0x64, 0x61, 0x72,
+ 0x6b, 0x6e, 0x66, 0x73, 0x6d, 0x71, 0x76, 0x75, 0x6a, 0x61, 0x6a, 0x70, 0x78, 0x61, 0x6c, 0x63,
+ 0x75, 0x6d, 0x79, 0x65, 0x66, 0x61, 0x79, 0x68, 0x74, 0x72, 0x6b, 0x77, 0x74, 0x63, 0x77, 0x67,
+ 0x61, 0x73, 0x6b, 0x79, 0x67, 0x63, 0x73, 0x6d, 0x67, 0x79, 0x65, 0x73, 0x64, 0x72, 0x71, 0x65,
+ 0x68, 0x66, 0x6b, 0x64, 0x73, 0x76, 0x63, 0x70, 0x78, 0x70, 0x76, 0x78, 0x6d, 0x66, 0x79, 0x63,
+ 0x6a, 0x6a, 0x6c, 0x75, 0x77, 0x64, 0x62, 0x67, 0x77, 0x64, 0x74, 0x76, 0x69, 0x6d, 0x72, 0x64,
+ 0x73, 0x75, 0x71, 0x6e, 0x74, 0x66, 0x65, 0x6e, 0x76, 0x6b, 0x6c, 0x70, 0x70, 0x6e, 0x68, 0x78,
+ 0x75, 0x77, 0x65, 0x62, 0x68, 0x6f, 0x76, 0x63, 0x72, 0x70, 0x71, 0x67, 0x67, 0x70, 0x64, 0x66,
+ 0x67, 0x68, 0x79, 0x70, 0x63, 0x67, 0x6f, 0x68, 0x70, 0x6f, 0x68, 0x6f, 0x6b, 0x71, 0x79, 0x62,
+ 0x76, 0x6a, 0x75, 0x6e, 0x77, 0x65, 0x70, 0x6f, 0x72, 0x6f, 0x61, 0x66, 0x65, 0x6b, 0x73, 0x63,
+ 0x61, 0x62, 0x66, 0x65, 0x67, 0x77, 0x77, 0x79, 0x72, 0x72, 0x6b, 0x6e, 0x6d, 0x75, 0x6b, 0x65,
+ 0x68, 0x6e, 0x6a, 0x6e, 0x65, 0x65, 0x61, 0x78, 0x66, 0x68, 0x64, 0x62, 0x79, 0x66, 0x63, 0x72,
+ 0x64, 0x74, 0x71, 0x6a, 0x72, 0x78, 0x76, 0x64, 0x6c, 0x66, 0x6d, 0x75, 0x6b, 0x64, 0x6d, 0x78,
+ 0x68, 0x74, 0x63, 0x69, 0x6c, 0x6d, 0x65, 0x63, 0x63, 0x69, 0x70, 0x76, 0x6d, 0x6e, 0x70, 0x67,
+ 0x6d, 0x64, 0x6d, 0x6f, 0x69, 0x6d, 0x76, 0x75, 0x65, 0x6b, 0x6f, 0x73, 0x78, 0x70, 0x63, 0x6c,
+ 0x73, 0x6e, 0x77, 0x6f, 0x6d, 0x79, 0x70, 0x77, 0x63, 0x77, 0x69, 0x65, 0x71, 0x6d, 0x74, 0x6d,
+ 0x78, 0x78, 0x61, 0x70, 0x65, 0x65, 0x64, 0x65, 0x75, 0x71, 0x67, 0x63, 0x64, 0x74, 0x6c, 0x68,
+ 0x78, 0x65, 0x6d, 0x6f, 0x68, 0x72, 0x65, 0x72, 0x68, 0x6b, 0x65, 0x74, 0x68, 0x78, 0x66, 0x64,
+ 0x69, 0x77, 0x79, 0x72, 0x77, 0x73, 0x71, 0x78, 0x71, 0x76, 0x67, 0x74, 0x79, 0x74, 0x6b, 0x77,
+ 0x63, 0x6d, 0x75, 0x76, 0x6f, 0x67, 0x62, 0x76, 0x67, 0x72, 0x72, 0x72, 0x61, 0x6d, 0x73, 0x74,
+ 0x78, 0x65, 0x64, 0x64, 0x67, 0x77, 0x67, 0x6f, 0x78, 0x74, 0x76, 0x6b, 0x75, 0x69, 0x75, 0x68,
+ 0x62, 0x72, 0x67, 0x73, 0x6f, 0x75, 0x73, 0x79, 0x6a, 0x6b, 0x70, 0x79, 0x76, 0x64, 0x6b, 0x71,
+ 0x64, 0x79, 0x68, 0x76, 0x6a, 0x71, 0x63, 0x65, 0x70, 0x78, 0x61, 0x74, 0x72, 0x65, 0x6f, 0x68,
+ 0x76, 0x75, 0x6a, 0x75, 0x6a, 0x62, 0x65, 0x61, 0x62, 0x6b, 0x74, 0x64, 0x70, 0x6d, 0x6e, 0x74,
+ 0x74, 0x72, 0x6b, 0x72, 0x71, 0x66, 0x72, 0x72, 0x75, 0x6b, 0x6b, 0x6a, 0x63, 0x73, 0x6c, 0x76,
+ 0x77, 0x71, 0x6e, 0x70, 0x65, 0x71, 0x61, 0x72, 0x68, 0x79, 0x79, 0x74, 0x78, 0x62, 0x6a, 0x63,
+ 0x72, 0x78, 0x6d, 0x61, 0x72, 0x73, 0x69, 0x61, 0x61, 0x78, 0x69, 0x63, 0x6b, 0x64, 0x79, 0x75,
+ 0x66, 0x72, 0x63, 0x67, 0x77, 0x6f, 0x77, 0x65, 0x75, 0x6f, 0x6a, 0x72, 0x61, 0x6a, 0x6c, 0x66,
+ 0x72, 0x6e, 0x70, 0x69, 0x68, 0x6a, 0x62, 0x6c, 0x6c, 0x70, 0x71, 0x6e, 0x75, 0x69, 0x76, 0x76,
+ 0x66, 0x6e, 0x77, 0x76, 0x66, 0x69, 0x78, 0x74, 0x68, 0x78, 0x64, 0x79, 0x66, 0x75, 0x74, 0x68,
+ 0x6f, 0x67, 0x79, 0x78, 0x73, 0x6d, 0x78, 0x79, 0x66, 0x63, 0x74, 0x75, 0x6f, 0x62, 0x62, 0x62,
+ 0x68, 0x78, 0x6c, 0x64, 0x76, 0x64, 0x72, 0x6e, 0x65, 0x71, 0x66, 0x69, 0x77, 0x76, 0x62, 0x66,
+ 0x71, 0x63, 0x74, 0x61, 0x78, 0x62, 0x74, 0x72, 0x6b, 0x67, 0x69, 0x78, 0x63, 0x6a, 0x6e, 0x76,
+ 0x62, 0x61, 0x6f, 0x68, 0x6a, 0x67, 0x72, 0x72, 0x79, 0x74, 0x78, 0x67, 0x65, 0x72, 0x6a, 0x68,
+ 0x6d, 0x6f, 0x61, 0x71, 0x65, 0x72, 0x64, 0x61, 0x79, 0x73, 0x6a, 0x64, 0x76, 0x77, 0x6a, 0x6b,
+ 0x71, 0x6c, 0x75, 0x75, 0x6c, 0x78, 0x67, 0x77, 0x76, 0x78, 0x69, 0x74, 0x62, 0x74, 0x75, 0x71,
+ 0x77, 0x77, 0x70, 0x69, 0x6c, 0x61, 0x71, 0x66, 0x6c, 0x78, 0x6a, 0x67, 0x77, 0x68, 0x65, 0x63,
+ 0x77, 0x72, 0x79, 0x6c, 0x6f, 0x64, 0x78, 0x77, 0x68, 0x78, 0x63, 0x68, 0x66, 0x70, 0x78, 0x6f,
+ 0x70, 0x78, 0x66, 0x79, 0x76, 0x63, 0x6a, 0x65, 0x79, 0x66, 0x78, 0x63, 0x6e, 0x6b, 0x75, 0x61,
+ 0x70, 0x6c, 0x62, 0x68, 0x79, 0x63, 0x6b, 0x6d, 0x62, 0x77, 0x68, 0x76, 0x62, 0x6f, 0x68, 0x62,
+ 0x78, 0x78, 0x72, 0x72, 0x6c, 0x74, 0x79, 0x74, 0x6b, 0x68, 0x6e, 0x74, 0x74, 0x70, 0x73, 0x67,
+ 0x73, 0x71, 0x6b, 0x6f, 0x69, 0x6a, 0x71, 0x78, 0x6a, 0x77, 0x70, 0x76, 0x72, 0x64, 0x6a, 0x68,
+ 0x6e, 0x6a, 0x69, 0x79, 0x73, 0x78, 0x71, 0x77, 0x71, 0x68, 0x6d, 0x6b, 0x68, 0x79, 0x63, 0x62,
+ 0x63, 0x6b, 0x67, 0x6f, 0x69, 0x68, 0x6e, 0x6d, 0x67, 0x65, 0x6a, 0x74, 0x67, 0x66, 0x73, 0x62,
+ 0x67, 0x78, 0x6d, 0x69, 0x62, 0x73, 0x67, 0x76, 0x71, 0x73, 0x79, 0x6d, 0x6a, 0x77, 0x65, 0x67,
+ 0x70, 0x6e, 0x64, 0x76, 0x66, 0x6b, 0x70, 0x73, 0x6e, 0x61, 0x69, 0x71, 0x71, 0x62, 0x75, 0x70,
+ 0x72, 0x63, 0x69, 0x68, 0x6e, 0x6d, 0x65, 0x6e, 0x6a, 0x73, 0x62, 0x70, 0x6a, 0x69, 0x67, 0x69,
+ 0x6f, 0x76, 0x6f, 0x70, 0x74, 0x6f, 0x66, 0x6f, 0x64, 0x64, 0x76, 0x66, 0x6b, 0x62, 0x73, 0x6c,
+ 0x6a, 0x6e, 0x73, 0x6b, 0x75, 0x6f, 0x66, 0x6e, 0x78, 0x6b, 0x64, 0x6c, 0x6d, 0x73, 0x6e, 0x69,
+ 0x6a, 0x6f, 0x78, 0x62, 0x6b, 0x78, 0x74, 0x65, 0x67, 0x72, 0x66, 0x66, 0x69, 0x69, 0x63, 0x73,
+ 0x75, 0x79, 0x75, 0x73, 0x77, 0x72, 0x77, 0x77, 0x74, 0x74, 0x68, 0x78, 0x76, 0x71, 0x79, 0x78,
+ 0x61, 0x6c, 0x6b, 0x6e, 0x61, 0x76, 0x6b, 0x6d, 0x76, 0x63, 0x70, 0x79, 0x6e, 0x78, 0x72, 0x61,
+ 0x75, 0x6c, 0x64, 0x69, 0x6f, 0x78, 0x61, 0x71, 0x68, 0x6c, 0x61, 0x77, 0x69, 0x69, 0x74, 0x75,
+ 0x6f, 0x66, 0x75, 0x6e, 0x62, 0x6c, 0x6d, 0x67, 0x79, 0x6f, 0x69, 0x71, 0x76, 0x71, 0x78, 0x67,
+ 0x75, 0x66, 0x6d, 0x69, 0x67, 0x75, 0x6f, 0x6b, 0x63, 0x61, 0x6f, 0x63, 0x70, 0x63, 0x76, 0x75,
+ 0x63, 0x74, 0x70, 0x78, 0x64, 0x77, 0x6e, 0x69, 0x78, 0x6c, 0x63, 0x70, 0x66, 0x6d, 0x68, 0x64,
+ 0x6a, 0x75, 0x64, 0x71, 0x6b, 0x64, 0x67, 0x78, 0x73, 0x63, 0x6e, 0x75, 0x74, 0x77, 0x76, 0x6a,
+ 0x62, 0x74, 0x73, 0x76, 0x67, 0x70, 0x6f, 0x78, 0x6f, 0x63, 0x75, 0x64, 0x61, 0x79, 0x6b, 0x70,
+ 0x73, 0x75, 0x65, 0x64, 0x74, 0x70, 0x61, 0x6f, 0x77, 0x6d, 0x74, 0x6e, 0x78, 0x65, 0x72, 0x75,
+ 0x63, 0x71, 0x69, 0x66, 0x79, 0x65, 0x74, 0x77, 0x6f, 0x6d, 0x66, 0x6a, 0x77, 0x6f, 0x78, 0x76,
+ 0x66, 0x67, 0x65, 0x64, 0x66, 0x61, 0x62, 0x61, 0x71, 0x79, 0x64, 0x6c, 0x79, 0x6b, 0x64, 0x75,
+ 0x79, 0x6c, 0x67, 0x6d, 0x76, 0x67, 0x77, 0x71, 0x69, 0x65, 0x66, 0x71, 0x6b, 0x70, 0x62, 0x64,
+ 0x73, 0x6e, 0x6d, 0x67, 0x67, 0x6f, 0x75, 0x70, 0x65, 0x79, 0x63, 0x62, 0x61, 0x6b, 0x62, 0x6a,
+ 0x66, 0x68, 0x67, 0x75, 0x78, 0x68, 0x6b, 0x77, 0x66, 0x79, 0x67, 0x6c, 0x6f, 0x6a, 0x6a, 0x73,
+ 0x6f, 0x73, 0x63, 0x78, 0x6a, 0x67, 0x74, 0x6d, 0x6f, 0x68, 0x6b, 0x62, 0x70, 0x66, 0x6a, 0x6e,
+ 0x6e, 0x67, 0x6c, 0x65, 0x74, 0x6f, 0x6c, 0x67, 0x68, 0x6d, 0x67, 0x79, 0x61, 0x6a, 0x6e, 0x70,
+ 0x64, 0x6f, 0x68, 0x6b, 0x6a, 0x64, 0x64, 0x6f, 0x6a, 0x64, 0x66, 0x67, 0x73, 0x6a, 0x76, 0x78,
+ 0x6c, 0x67, 0x66, 0x75, 0x6c, 0x79, 0x69, 0x65, 0x71, 0x6c, 0x6b, 0x63, 0x61, 0x6d, 0x61, 0x78,
+ 0x6a, 0x74, 0x6a, 0x78, 0x65, 0x75, 0x62, 0x6b, 0x6b, 0x75, 0x77, 0x78, 0x73, 0x77, 0x64, 0x61,
+ 0x61, 0x6b, 0x71, 0x6f, 0x6f, 0x6c, 0x67, 0x6e, 0x6d, 0x77, 0x64, 0x6d, 0x64, 0x6d, 0x64, 0x76,
+ 0x77, 0x6b, 0x69, 0x76, 0x72, 0x70, 0x77, 0x62, 0x75, 0x69, 0x73, 0x68, 0x65, 0x76, 0x70, 0x76,
+ 0x76, 0x6c, 0x6a, 0x69, 0x77, 0x73, 0x69, 0x66, 0x67, 0x6a, 0x74, 0x6a, 0x70, 0x6f, 0x6c, 0x77,
+ 0x70, 0x6a, 0x76, 0x6a, 0x61, 0x68, 0x78, 0x73, 0x70, 0x72, 0x78, 0x62, 0x76, 0x6b, 0x66, 0x6b,
+ 0x64, 0x71, 0x6d, 0x62, 0x6d, 0x70, 0x79, 0x6a, 0x66, 0x6c, 0x79, 0x76, 0x66, 0x75, 0x69, 0x71,
+ 0x66, 0x79, 0x78, 0x77, 0x75, 0x75, 0x6b, 0x77, 0x6d, 0x67, 0x67, 0x6b, 0x75, 0x75, 0x64, 0x75,
+ 0x62, 0x73, 0x6c, 0x6a, 0x6c, 0x64, 0x69, 0x74, 0x6e, 0x65, 0x63, 0x79, 0x6e, 0x6d, 0x6c, 0x77,
+ 0x6a, 0x69, 0x6d, 0x78, 0x62, 0x74, 0x69, 0x6a, 0x66, 0x75, 0x6e, 0x6f, 0x6e, 0x78, 0x73, 0x6d,
+ 0x74, 0x74, 0x62, 0x6c, 0x68, 0x6b, 0x70, 0x61, 0x6c, 0x6c, 0x63, 0x64, 0x67, 0x65, 0x68, 0x73,
+ 0x74, 0x6d, 0x64, 0x78, 0x68, 0x71, 0x61, 0x66, 0x77, 0x66, 0x6a, 0x66, 0x6f, 0x61, 0x65, 0x6f,
+ 0x6f, 0x74, 0x6a, 0x69, 0x6a, 0x75, 0x68, 0x6c, 0x73, 0x6b, 0x72, 0x75, 0x69, 0x63, 0x63, 0x62,
+ 0x6e, 0x64, 0x76, 0x75, 0x79, 0x71, 0x64, 0x61, 0x6a, 0x65, 0x65, 0x61, 0x61, 0x6e, 0x66, 0x65,
+ 0x74, 0x77, 0x61, 0x62, 0x68, 0x68, 0x67, 0x71, 0x69, 0x68, 0x6b, 0x73, 0x6b, 0x76, 0x68, 0x73,
+ 0x62, 0x63, 0x65, 0x65, 0x73, 0x72, 0x6a, 0x71, 0x6e, 0x61, 0x6a, 0x63, 0x61, 0x62, 0x73, 0x6e,
+ 0x6e, 0x70, 0x67, 0x6f, 0x63, 0x70, 0x64, 0x66, 0x62, 0x61, 0x64, 0x6a, 0x78, 0x70, 0x61, 0x6f,
+ 0x69, 0x79, 0x66, 0x77, 0x74, 0x69, 0x62, 0x6a, 0x73, 0x75, 0x6d, 0x76, 0x74, 0x6d, 0x61, 0x77,
+ 0x67, 0x67, 0x72, 0x6f, 0x79, 0x75, 0x61, 0x72, 0x65, 0x6c, 0x67, 0x69, 0x63, 0x77, 0x71, 0x61,
+ 0x62, 0x63, 0x61, 0x71, 0x6d, 0x6d, 0x64, 0x6a, 0x6a, 0x70, 0x63, 0x71, 0x72, 0x6b, 0x6a, 0x66,
+ 0x62, 0x76, 0x6c, 0x61, 0x72, 0x6f, 0x75, 0x72, 0x75, 0x65, 0x6b, 0x75, 0x73, 0x64, 0x76, 0x6b,
+ 0x6c, 0x6d, 0x73, 0x74, 0x6a, 0x70, 0x61, 0x64, 0x74, 0x71, 0x6c, 0x63, 0x6f, 0x6c, 0x68, 0x75,
+ 0x69, 0x70, 0x73, 0x73, 0x76, 0x78, 0x6b, 0x67, 0x73, 0x74, 0x6f, 0x70, 0x72, 0x61, 0x75, 0x6c,
+ 0x66, 0x66, 0x69, 0x6f, 0x75, 0x6b, 0x6d, 0x64, 0x74, 0x79, 0x6b, 0x66, 0x6b, 0x65, 0x65, 0x6c,
+ 0x6b, 0x61, 0x6c, 0x73, 0x63, 0x66, 0x67, 0x76, 0x6d, 0x74, 0x61, 0x65, 0x6e, 0x62, 0x61, 0x72,
+ 0x63, 0x6d, 0x6c, 0x6a, 0x75, 0x67, 0x6d, 0x62, 0x6e, 0x70, 0x76, 0x6b, 0x64, 0x77, 0x6b, 0x68,
+ 0x75, 0x74, 0x77, 0x61, 0x73, 0x62, 0x6c, 0x79, 0x79, 0x77, 0x6f, 0x66, 0x63, 0x73, 0x67, 0x64,
+ 0x63, 0x62, 0x66, 0x71, 0x73, 0x79, 0x66, 0x73, 0x69, 0x72, 0x71, 0x70, 0x72, 0x67, 0x68, 0x72,
+ 0x6d, 0x6d, 0x79, 0x68, 0x77, 0x6f, 0x68, 0x62, 0x64, 0x70, 0x64, 0x70, 0x6e, 0x72, 0x69, 0x66,
+ 0x6b, 0x72, 0x64, 0x65, 0x63, 0x77, 0x61, 0x61, 0x6b, 0x73, 0x73, 0x77, 0x78, 0x77, 0x6c, 0x64,
+ 0x61, 0x69, 0x62, 0x72, 0x72, 0x6d, 0x6e, 0x76, 0x70, 0x75, 0x72, 0x63, 0x77, 0x69, 0x65, 0x62,
+ 0x73, 0x73, 0x6b, 0x70, 0x70, 0x64, 0x67, 0x71, 0x72, 0x6a, 0x6f, 0x79, 0x75, 0x75, 0x64, 0x75,
+ 0x6f, 0x73, 0x78, 0x67, 0x79, 0x79, 0x78, 0x6e, 0x76, 0x63, 0x6c, 0x73, 0x6a, 0x6c, 0x65, 0x6e,
+ 0x6c, 0x70, 0x69, 0x76, 0x63, 0x6a, 0x6f, 0x6a, 0x6a, 0x6f, 0x6b, 0x66, 0x75, 0x73, 0x66, 0x71,
+ 0x61, 0x75, 0x78, 0x68, 0x6b, 0x67, 0x71, 0x72, 0x6b, 0x65, 0x67, 0x6d, 0x68, 0x74, 0x62, 0x78,
+ 0x65, 0x6f, 0x69, 0x74, 0x71, 0x79, 0x63, 0x67, 0x79, 0x77, 0x6b, 0x69, 0x61, 0x6e, 0x6b, 0x69,
+ 0x70, 0x78, 0x74, 0x75, 0x72, 0x66, 0x6f, 0x64, 0x77, 0x62, 0x79, 0x73, 0x79, 0x73, 0x78, 0x65,
+ 0x62, 0x6b, 0x6f, 0x6b, 0x76, 0x62, 0x6e, 0x68, 0x6a, 0x72, 0x61, 0x61, 0x73, 0x6f, 0x78, 0x61,
+ 0x68, 0x76, 0x63, 0x6c, 0x77, 0x6c, 0x63, 0x6e, 0x6d, 0x6c, 0x71, 0x6f, 0x70, 0x72, 0x63, 0x75,
+ 0x68, 0x61, 0x67, 0x69, 0x6f, 0x6a, 0x6e, 0x61, 0x71, 0x72, 0x6b, 0x66, 0x73, 0x6c, 0x64, 0x79,
+ 0x79, 0x75, 0x79, 0x6d, 0x6e, 0x6c, 0x67, 0x65, 0x6a, 0x76, 0x66, 0x76, 0x77, 0x6c, 0x6b, 0x77,
+ 0x75, 0x64, 0x72, 0x73, 0x77, 0x69, 0x64, 0x74, 0x72, 0x69, 0x66, 0x6b, 0x6f, 0x6d, 0x76, 0x74,
+ 0x64, 0x64, 0x73, 0x74, 0x69, 0x6f, 0x71, 0x79, 0x65, 0x64, 0x71, 0x69, 0x6d, 0x62, 0x6d, 0x70,
+ 0x6c, 0x76, 0x61, 0x72, 0x6e, 0x65, 0x64, 0x77, 0x6a, 0x67, 0x72, 0x78, 0x74, 0x72, 0x69, 0x62,
+ 0x6f, 0x71, 0x71, 0x6a, 0x69, 0x70, 0x73, 0x79, 0x6a, 0x70, 0x70, 0x6d, 0x6f, 0x6f, 0x6d, 0x79,
+ 0x6f, 0x6e, 0x6a, 0x6e, 0x75, 0x67, 0x76, 0x78, 0x65, 0x72, 0x77, 0x64, 0x69, 0x6f, 0x63, 0x69,
+ 0x73, 0x68, 0x72, 0x79, 0x6c, 0x6a, 0x61, 0x77, 0x6b, 0x72, 0x78, 0x63, 0x72, 0x63, 0x79, 0x69,
+ 0x6a, 0x72, 0x6e, 0x79, 0x62, 0x67, 0x6f, 0x72, 0x63, 0x6b, 0x6d, 0x61, 0x76, 0x6e, 0x73, 0x64,
+ 0x62, 0x6a, 0x78, 0x74, 0x72, 0x74, 0x64, 0x79, 0x6b, 0x71, 0x72, 0x68, 0x67, 0x63, 0x63, 0x76,
+ 0x6f, 0x77, 0x76, 0x61, 0x64, 0x65, 0x70, 0x71, 0x72, 0x61, 0x63, 0x63, 0x66, 0x75, 0x73, 0x62,
+ 0x70, 0x70, 0x67, 0x68, 0x70, 0x63, 0x6c, 0x64, 0x6d, 0x76, 0x6d, 0x67, 0x73, 0x72, 0x6a, 0x6a,
+ 0x78, 0x6b, 0x64, 0x78, 0x71, 0x71, 0x66, 0x68, 0x79, 0x6b, 0x6b, 0x69, 0x67, 0x70, 0x6e, 0x66,
+ 0x73, 0x73, 0x78, 0x72, 0x63, 0x76, 0x79, 0x65, 0x63, 0x67, 0x79, 0x64, 0x69, 0x71, 0x75, 0x68,
+ 0x79, 0x6c, 0x6b, 0x61, 0x78, 0x6a, 0x6c, 0x76, 0x63, 0x61, 0x70, 0x79, 0x71, 0x6c, 0x77, 0x6c,
+ 0x6f, 0x66, 0x63, 0x6a, 0x69, 0x64, 0x6f, 0x6f, 0x75, 0x64, 0x76, 0x70, 0x65, 0x63, 0x72, 0x70,
+ 0x62, 0x71, 0x6c, 0x73, 0x74, 0x74, 0x6b, 0x6f, 0x6e, 0x79, 0x72, 0x64, 0x66, 0x6d, 0x79, 0x73,
+ 0x6d, 0x64, 0x67, 0x6b, 0x69, 0x6d, 0x76, 0x65, 0x63, 0x73, 0x6c, 0x6a, 0x66, 0x64, 0x64, 0x70,
+ 0x72, 0x67, 0x69, 0x6c, 0x63, 0x63, 0x71, 0x63, 0x69, 0x73, 0x78, 0x67, 0x77, 0x6a, 0x6d, 0x74,
+ 0x63, 0x63, 0x68, 0x78, 0x61, 0x73, 0x6b, 0x6b, 0x73, 0x69, 0x73, 0x72, 0x73, 0x71, 0x67, 0x68,
+ 0x65, 0x76, 0x77, 0x65, 0x6e, 0x68, 0x72, 0x6a, 0x63, 0x6d, 0x67, 0x6e, 0x65, 0x73, 0x78, 0x73,
+ 0x6a, 0x6d, 0x74, 0x61, 0x61, 0x73, 0x74, 0x62, 0x6a, 0x6a, 0x62, 0x63, 0x65, 0x63, 0x70, 0x69,
+ 0x66, 0x71, 0x70, 0x68, 0x6a, 0x66, 0x67, 0x78, 0x61, 0x63, 0x6c, 0x75, 0x79, 0x6a, 0x63, 0x6b,
+ 0x72, 0x78, 0x65, 0x65, 0x77, 0x77, 0x73, 0x71, 0x68, 0x6f, 0x6b, 0x64, 0x6e, 0x66, 0x64, 0x6e,
+ 0x6b, 0x78, 0x67, 0x73, 0x6c, 0x75, 0x64, 0x6d, 0x66, 0x6b, 0x77, 0x73, 0x74, 0x70, 0x68, 0x66,
+ 0x6c, 0x70, 0x6a, 0x62, 0x6b, 0x78, 0x6e, 0x65, 0x61, 0x6a, 0x68, 0x63, 0x6a, 0x64, 0x75, 0x69,
+ 0x71, 0x77, 0x6e, 0x67, 0x72, 0x70, 0x70, 0x76, 0x70, 0x6e, 0x6b, 0x74, 0x75, 0x76, 0x6b, 0x62,
+ 0x79, 0x63, 0x78, 0x6c, 0x63, 0x6f, 0x69, 0x68, 0x76, 0x6f, 0x62, 0x6a, 0x61, 0x66, 0x6b, 0x72,
+ 0x75, 0x76, 0x67, 0x74, 0x78, 0x73, 0x69, 0x63, 0x63, 0x72, 0x75, 0x61, 0x64, 0x75, 0x69, 0x70,
+ 0x63, 0x68, 0x72, 0x67, 0x69, 0x6f, 0x6d, 0x62, 0x61, 0x68, 0x62, 0x76, 0x6e, 0x6f, 0x62, 0x76,
+ 0x70, 0x6a, 0x67, 0x6b, 0x74, 0x75, 0x75, 0x70, 0x70, 0x6c, 0x76, 0x6d, 0x71, 0x6b, 0x6f, 0x72,
+ 0x70, 0x67, 0x63, 0x79, 0x72, 0x64, 0x67, 0x6f, 0x6d, 0x77, 0x64, 0x61, 0x6f, 0x68, 0x63, 0x61,
+ 0x64, 0x61, 0x67, 0x62, 0x6e, 0x65, 0x6a, 0x6e, 0x77, 0x66, 0x6e, 0x75, 0x79, 0x64, 0x75, 0x79,
+ 0x79, 0x61, 0x63, 0x6f, 0x63, 0x79, 0x77, 0x64, 0x73, 0x74, 0x6a, 0x71, 0x63, 0x61, 0x6b, 0x76,
+ 0x63, 0x6e, 0x73, 0x72, 0x75, 0x62, 0x75, 0x75, 0x67, 0x68, 0x71, 0x6f, 0x6e, 0x61, 0x65, 0x75,
+ 0x6e, 0x6a, 0x76, 0x71, 0x68, 0x74, 0x79, 0x68, 0x65, 0x70, 0x65, 0x6e, 0x6c, 0x62, 0x78, 0x6e,
+ 0x66, 0x64, 0x63, 0x77, 0x68, 0x75, 0x78, 0x6e, 0x71, 0x6d, 0x79, 0x71, 0x68, 0x68, 0x75, 0x73,
+ 0x6c, 0x67, 0x73, 0x75, 0x6d, 0x74, 0x72, 0x6d, 0x77, 0x65, 0x67, 0x67, 0x6f, 0x67, 0x66, 0x75,
+ 0x6d, 0x63, 0x75, 0x6d, 0x6d, 0x74, 0x72, 0x6f, 0x64, 0x74, 0x6f, 0x76, 0x73, 0x75, 0x67, 0x65,
+ 0x73, 0x78, 0x77, 0x67, 0x66, 0x77, 0x66, 0x69, 0x73, 0x75, 0x61, 0x65, 0x6a, 0x61, 0x6b, 0x72,
+ 0x6c, 0x6a, 0x71, 0x71, 0x73, 0x6e, 0x6f, 0x70, 0x65, 0x6e, 0x72, 0x6a, 0x74, 0x6f, 0x72, 0x76,
+ 0x61, 0x69, 0x74, 0x67, 0x72, 0x6f, 0x6d, 0x74, 0x75, 0x74, 0x72, 0x63, 0x65, 0x78, 0x71, 0x6b,
+ 0x79, 0x73, 0x63, 0x6c, 0x6f, 0x75, 0x73, 0x62, 0x70, 0x70, 0x73, 0x65, 0x79, 0x6d, 0x6f, 0x76,
+ 0x64, 0x76, 0x67, 0x69, 0x64, 0x6a, 0x72, 0x69, 0x75, 0x69, 0x79, 0x6e, 0x6d, 0x78, 0x69, 0x62,
+ 0x71, 0x6e, 0x6b, 0x66, 0x75, 0x73, 0x6b, 0x74, 0x6e, 0x61, 0x6c, 0x69, 0x76, 0x63, 0x66, 0x6d,
+ 0x69, 0x61, 0x62, 0x73, 0x74, 0x6a, 0x79, 0x64, 0x6f, 0x67, 0x77, 0x68, 0x6b, 0x63, 0x75, 0x63,
+ 0x74, 0x6d, 0x76, 0x68, 0x72, 0x6e, 0x69, 0x6d, 0x74, 0x6f, 0x79, 0x78, 0x62, 0x76, 0x62, 0x6f,
+ 0x70, 0x6c, 0x6f, 0x62, 0x65, 0x78, 0x63, 0x65, 0x72, 0x77, 0x64, 0x76, 0x65, 0x66, 0x74, 0x63,
+ 0x63, 0x6a, 0x6d, 0x6b, 0x6e, 0x64, 0x73, 0x62, 0x66, 0x6d, 0x62, 0x66, 0x6b, 0x76, 0x63, 0x68,
+ 0x6c, 0x65, 0x69, 0x72, 0x75, 0x62, 0x6e, 0x64, 0x6a, 0x6a, 0x6d, 0x75, 0x74, 0x79, 0x6d, 0x67,
+ 0x65, 0x6e, 0x6b, 0x67, 0x6a, 0x6e, 0x63, 0x63, 0x63, 0x6d, 0x6b, 0x64, 0x6f, 0x6e, 0x6a, 0x75,
+ 0x77, 0x71, 0x74, 0x65, 0x73, 0x71, 0x62, 0x61, 0x76, 0x67, 0x6d, 0x6b, 0x74, 0x78, 0x6c, 0x66,
+ 0x65, 0x74, 0x6f, 0x68, 0x61, 0x73, 0x66, 0x63, 0x79, 0x64, 0x6a, 0x79, 0x74, 0x67, 0x67, 0x6b,
+ 0x78, 0x70, 0x6f, 0x6c, 0x64, 0x68, 0x69, 0x76, 0x6b, 0x77, 0x67, 0x6d, 0x62, 0x72, 0x71, 0x76,
+ 0x69, 0x76, 0x6e, 0x76, 0x71, 0x6c, 0x74, 0x74, 0x71, 0x75, 0x6b, 0x6c, 0x70, 0x6e, 0x69, 0x78,
+ 0x71, 0x6d, 0x62, 0x6d, 0x69, 0x65, 0x6c, 0x77, 0x68, 0x68, 0x6d, 0x6d, 0x62, 0x70, 0x75, 0x74,
+ 0x77, 0x6f, 0x65, 0x69, 0x78, 0x62, 0x78, 0x74, 0x6b, 0x76, 0x72, 0x76, 0x65, 0x78, 0x77, 0x74,
+ 0x63, 0x75, 0x71, 0x66, 0x6e, 0x6c, 0x6d, 0x62, 0x76, 0x6b, 0x77, 0x70, 0x76, 0x79, 0x6b, 0x78,
+ 0x76, 0x6e, 0x6d, 0x66, 0x6d, 0x6b, 0x69, 0x6b, 0x64, 0x79, 0x77, 0x62, 0x65, 0x61, 0x67, 0x6c,
+ 0x66, 0x6f, 0x70, 0x73, 0x73, 0x66, 0x68, 0x71, 0x68, 0x70, 0x65, 0x65, 0x74, 0x69, 0x65, 0x6e,
+ 0x65, 0x68, 0x76, 0x79, 0x66, 0x76, 0x67, 0x66, 0x68, 0x68, 0x6d, 0x73, 0x78, 0x6d, 0x69, 0x79,
+ 0x63, 0x6f, 0x64, 0x64, 0x79, 0x68, 0x71, 0x75, 0x73, 0x6d, 0x63, 0x67, 0x75, 0x64, 0x6b, 0x65,
+ 0x69, 0x76, 0x70, 0x65, 0x6a, 0x63, 0x70, 0x68, 0x61, 0x66, 0x73, 0x73, 0x68, 0x74, 0x71, 0x6c,
+ 0x63, 0x61, 0x62, 0x73, 0x75, 0x73, 0x78, 0x6d, 0x66, 0x74, 0x74, 0x73, 0x63, 0x74, 0x64, 0x75,
+ 0x6b, 0x6c, 0x6a, 0x72, 0x72, 0x61, 0x64, 0x74, 0x71, 0x70, 0x6b, 0x69, 0x70, 0x75, 0x61, 0x6e,
+ 0x71, 0x77, 0x73, 0x6f, 0x72, 0x77, 0x67, 0x67, 0x67, 0x61, 0x73, 0x73, 0x61, 0x6e, 0x63, 0x70,
+ 0x6c, 0x69, 0x68, 0x67, 0x62, 0x76, 0x71, 0x72, 0x72, 0x72, 0x72, 0x65, 0x65, 0x6d, 0x70, 0x6d,
+ 0x73, 0x73, 0x69, 0x68, 0x74, 0x76, 0x71, 0x66, 0x67, 0x67, 0x70, 0x66, 0x69, 0x78, 0x62, 0x78,
+ 0x72, 0x6d, 0x78, 0x76, 0x79, 0x6a, 0x6f, 0x79, 0x76, 0x65, 0x61, 0x61, 0x6f, 0x6c, 0x6c, 0x79,
+ 0x6e, 0x62, 0x63, 0x76, 0x69, 0x69, 0x77, 0x76, 0x6d, 0x66, 0x69, 0x79, 0x79, 0x6f, 0x75, 0x71,
+ 0x64, 0x75, 0x6b, 0x6c, 0x6d, 0x73, 0x6c, 0x71, 0x78, 0x6e, 0x6d, 0x74, 0x6e, 0x71, 0x6b, 0x66,
+ 0x6d, 0x67, 0x69, 0x6b, 0x77, 0x66, 0x69, 0x6d, 0x6d, 0x6d, 0x6a, 0x74, 0x77, 0x6f, 0x63, 0x76,
+ 0x6d, 0x67, 0x65, 0x65, 0x64, 0x76, 0x68, 0x67, 0x68, 0x66, 0x68, 0x6f, 0x61, 0x63, 0x78, 0x6d,
+ 0x76, 0x64, 0x74, 0x6e, 0x6d, 0x69, 0x66, 0x71, 0x62, 0x70, 0x71, 0x66, 0x77, 0x77, 0x63, 0x6b,
+ 0x76, 0x78, 0x72, 0x66, 0x70, 0x76, 0x78, 0x65, 0x72, 0x6f, 0x62, 0x62, 0x67, 0x65, 0x62, 0x76,
+ 0x66, 0x6e, 0x79, 0x69, 0x64, 0x69, 0x68, 0x67, 0x66, 0x70, 0x77, 0x6e, 0x76, 0x64, 0x75, 0x6c,
+ 0x71, 0x68, 0x6a, 0x71, 0x71, 0x70, 0x65, 0x68, 0x61, 0x71, 0x78, 0x75, 0x6c, 0x76, 0x68, 0x6d,
+ 0x64, 0x65, 0x65, 0x79, 0x6f, 0x78, 0x66, 0x77, 0x70, 0x66, 0x66, 0x6f, 0x74, 0x73, 0x71, 0x6e,
+ 0x65, 0x65, 0x75, 0x68, 0x6e, 0x64, 0x6e, 0x67, 0x72, 0x76, 0x71, 0x6b, 0x65, 0x71, 0x6d, 0x6f,
+ 0x63, 0x73, 0x63, 0x6e, 0x66, 0x73, 0x68, 0x6d, 0x6b, 0x72, 0x67, 0x6d, 0x6f, 0x6a, 0x77, 0x70,
+ 0x64, 0x77, 0x62, 0x79, 0x6e, 0x65, 0x6c, 0x62, 0x66, 0x77, 0x6e, 0x73, 0x77, 0x64, 0x74, 0x74,
+ 0x77, 0x64, 0x65, 0x66, 0x6e, 0x73, 0x78, 0x6d, 0x79, 0x76, 0x73, 0x71, 0x76, 0x61, 0x66, 0x6e,
+ 0x76, 0x77, 0x6f, 0x73, 0x6c, 0x6e, 0x6d, 0x78, 0x73, 0x71, 0x64, 0x66, 0x78, 0x65, 0x73, 0x63,
+ 0x69, 0x73, 0x69, 0x62, 0x64, 0x63, 0x74, 0x6f, 0x6e, 0x69, 0x79, 0x66, 0x75, 0x71, 0x70, 0x79,
+ 0x6c, 0x6d, 0x67, 0x72, 0x67, 0x65, 0x72, 0x75, 0x78, 0x68, 0x73, 0x6f, 0x76, 0x62, 0x62, 0x78,
+ 0x68, 0x78, 0x79, 0x72, 0x65, 0x76, 0x61, 0x74, 0x69, 0x6f, 0x67, 0x79, 0x6d, 0x78, 0x73, 0x78,
+ 0x68, 0x67, 0x77, 0x71, 0x76, 0x78, 0x74, 0x61, 0x6c, 0x76, 0x78, 0x65, 0x78, 0x6a, 0x76, 0x64,
+ 0x6f, 0x6d, 0x71, 0x6f, 0x6a, 0x6f, 0x77, 0x79, 0x69, 0x6e, 0x66, 0x71, 0x6e, 0x62, 0x66, 0x6f,
+ 0x61, 0x68, 0x73, 0x72, 0x69, 0x68, 0x68, 0x73, 0x63, 0x67, 0x69, 0x74, 0x6c, 0x76, 0x79, 0x66,
+ 0x6f, 0x75, 0x6c, 0x69, 0x73, 0x72, 0x74, 0x6c, 0x6f, 0x62, 0x78, 0x68, 0x6b, 0x6d, 0x77, 0x63,
+ 0x6d, 0x67, 0x64, 0x77, 0x6f, 0x64, 0x72, 0x77, 0x6b, 0x6f, 0x6c, 0x6c, 0x6e, 0x6c, 0x63, 0x77,
+ 0x73, 0x77, 0x6c, 0x71, 0x73, 0x73, 0x6f, 0x61, 0x64, 0x65, 0x69, 0x78, 0x63, 0x66, 0x6f, 0x66,
+ 0x72, 0x77, 0x69, 0x64, 0x6c, 0x72, 0x6b, 0x6d, 0x72, 0x6c, 0x6d, 0x64, 0x71, 0x6b, 0x69, 0x61,
+ 0x75, 0x66, 0x65, 0x62, 0x6e, 0x6f, 0x68, 0x75, 0x6a, 0x6d, 0x71, 0x74, 0x78, 0x71, 0x75, 0x6a,
+ 0x75, 0x6f, 0x68, 0x6d, 0x68, 0x68, 0x78, 0x61, 0x75, 0x71, 0x75, 0x62, 0x79, 0x6c, 0x75, 0x73,
+ 0x72, 0x6b, 0x66, 0x62, 0x71, 0x71, 0x79, 0x69, 0x6f, 0x6d, 0x69, 0x6d, 0x79, 0x6a, 0x66, 0x69,
+ 0x6a, 0x66, 0x79, 0x61, 0x63, 0x65, 0x69, 0x67, 0x61, 0x73, 0x71, 0x65, 0x72, 0x73, 0x6b, 0x6b,
+ 0x70, 0x6f, 0x78, 0x69, 0x78, 0x6c, 0x78, 0x61, 0x66, 0x6b, 0x69, 0x69, 0x64, 0x6c, 0x74, 0x70,
+ 0x78, 0x75, 0x6f, 0x73, 0x79, 0x6f, 0x6a, 0x6e, 0x74, 0x70, 0x70, 0x66, 0x6c, 0x64, 0x79, 0x67,
+ 0x78, 0x6e, 0x6c, 0x78, 0x71, 0x6c, 0x74, 0x62, 0x6e, 0x77, 0x6c, 0x66, 0x71, 0x6f, 0x76, 0x79,
+ 0x70, 0x71, 0x76, 0x67, 0x73, 0x76, 0x76, 0x77, 0x64, 0x61, 0x6c, 0x68, 0x61, 0x61, 0x78, 0x74,
+ 0x72, 0x78, 0x6d, 0x6d, 0x64, 0x6d, 0x72, 0x65, 0x6a, 0x6a, 0x79, 0x63, 0x6c, 0x62, 0x66, 0x75,
+ 0x79, 0x72, 0x68, 0x71, 0x71, 0x64, 0x6f, 0x79, 0x63, 0x72, 0x6c, 0x6b, 0x64, 0x63, 0x75, 0x68,
+ 0x71, 0x72, 0x77, 0x72, 0x6c, 0x6b, 0x75, 0x6d, 0x6e, 0x6c, 0x67, 0x75, 0x66, 0x65, 0x68, 0x6d,
+ 0x79, 0x70, 0x6c, 0x73, 0x79, 0x6b, 0x6c, 0x6d, 0x72, 0x6f, 0x70, 0x76, 0x78, 0x6d, 0x6f, 0x61,
+ 0x68, 0x76, 0x6f, 0x6a, 0x66, 0x77, 0x72, 0x6e, 0x62, 0x71, 0x6b, 0x71, 0x63, 0x65, 0x71, 0x65,
+ 0x6d, 0x70, 0x65, 0x6e, 0x6d, 0x77, 0x61, 0x66, 0x76, 0x6e, 0x79, 0x71, 0x76, 0x63, 0x70, 0x76,
+ 0x75, 0x6b, 0x74, 0x65, 0x6f, 0x67, 0x6e, 0x79, 0x6f, 0x61, 0x72, 0x6e, 0x6a, 0x61, 0x6d, 0x63,
+ 0x66, 0x6c, 0x63, 0x6a, 0x6e, 0x62, 0x62, 0x77, 0x63, 0x75, 0x71, 0x78, 0x65, 0x79, 0x6e, 0x79,
+ 0x6a, 0x65, 0x78, 0x73, 0x73, 0x63, 0x69, 0x74, 0x79, 0x66, 0x73, 0x71, 0x74, 0x63, 0x62, 0x76,
+ 0x73, 0x6a, 0x67, 0x78, 0x64, 0x77, 0x69, 0x78, 0x6b, 0x62, 0x79, 0x78, 0x67, 0x67, 0x76, 0x72,
+ 0x75, 0x70, 0x66, 0x68, 0x76, 0x6e, 0x6e, 0x6e, 0x75, 0x64, 0x77, 0x6a, 0x65, 0x6a, 0x70, 0x77,
+ 0x65, 0x79, 0x67, 0x6e, 0x61, 0x79, 0x6b, 0x77, 0x77, 0x78, 0x6e, 0x63, 0x72, 0x68, 0x79, 0x67,
+ 0x76, 0x77, 0x68, 0x6c, 0x78, 0x72, 0x65, 0x73, 0x71, 0x6f, 0x78, 0x78, 0x77, 0x71, 0x70, 0x64,
+ 0x6a, 0x63, 0x76, 0x6f, 0x68, 0x70, 0x72, 0x6f, 0x63, 0x72, 0x67, 0x67, 0x76, 0x77, 0x65, 0x72,
+ 0x6d, 0x75, 0x74, 0x63, 0x6b, 0x73, 0x6b, 0x73, 0x6c, 0x6b, 0x6e, 0x70, 0x75, 0x76, 0x67, 0x6e,
+ 0x68, 0x66, 0x75, 0x70, 0x78, 0x69, 0x69, 0x6c, 0x6b, 0x66, 0x77, 0x67, 0x78, 0x66, 0x69, 0x6b,
+ 0x72, 0x61, 0x64, 0x64, 0x78, 0x62, 0x68, 0x72, 0x76, 0x73, 0x78, 0x71, 0x65, 0x6f, 0x63, 0x79,
+ 0x70, 0x74, 0x77, 0x78, 0x75, 0x79, 0x75, 0x66, 0x65, 0x72, 0x70, 0x78, 0x6c, 0x79, 0x70, 0x76,
+ 0x61, 0x73, 0x64, 0x79, 0x76, 0x74, 0x79, 0x77, 0x66, 0x65, 0x61, 0x63, 0x76, 0x75, 0x64, 0x75,
+ 0x6e, 0x70, 0x63, 0x6b, 0x6c, 0x79, 0x62, 0x67, 0x67, 0x74, 0x73, 0x72, 0x68, 0x6f, 0x67, 0x62,
+ 0x6e, 0x6b, 0x68, 0x62, 0x64, 0x62, 0x70, 0x71, 0x61, 0x74, 0x6d, 0x6c, 0x78, 0x6b, 0x6b, 0x75,
+ 0x68, 0x69, 0x6e, 0x6d, 0x66, 0x76, 0x6a, 0x63, 0x68, 0x72, 0x6d, 0x63, 0x6d, 0x6b, 0x6c, 0x78,
+ 0x73, 0x72, 0x6b, 0x6c, 0x64, 0x73, 0x71, 0x6e, 0x76, 0x73, 0x69, 0x6c, 0x77, 0x72, 0x73, 0x72,
+ 0x74, 0x78, 0x61, 0x65, 0x6b, 0x6e, 0x6f, 0x62, 0x75, 0x6c, 0x69, 0x63, 0x77, 0x6d, 0x78, 0x76,
+ 0x72, 0x65, 0x75, 0x6d, 0x6f, 0x70, 0x63, 0x6b, 0x62, 0x64, 0x75, 0x6d, 0x62, 0x64, 0x77, 0x6d,
+ 0x6b, 0x70, 0x70, 0x72, 0x77, 0x64, 0x65, 0x74, 0x62, 0x69, 0x63, 0x65, 0x69, 0x71, 0x71, 0x6f,
+ 0x62, 0x67, 0x67, 0x6a, 0x77, 0x79, 0x75, 0x69, 0x77, 0x6e, 0x62, 0x77, 0x61, 0x67, 0x62, 0x74,
+ 0x79, 0x76, 0x78, 0x71, 0x78, 0x70, 0x65, 0x61, 0x65, 0x76, 0x76, 0x70, 0x69, 0x73, 0x6d, 0x75,
+ 0x6a, 0x76, 0x6a, 0x72, 0x6a, 0x73, 0x74, 0x6a, 0x76, 0x74, 0x6d, 0x70, 0x79, 0x74, 0x6b, 0x76,
+ 0x64, 0x68, 0x62, 0x76, 0x6c, 0x79, 0x77, 0x71, 0x64, 0x75, 0x6a, 0x77, 0x76, 0x6e, 0x6d, 0x74,
+ 0x65, 0x71, 0x76, 0x6a, 0x67, 0x62, 0x70, 0x71, 0x64, 0x6b, 0x71, 0x63, 0x6b, 0x72, 0x71, 0x70,
+ 0x68, 0x64, 0x72, 0x70, 0x70, 0x6d, 0x6e, 0x61, 0x77, 0x64, 0x78, 0x67, 0x70, 0x6b, 0x71, 0x63,
+ 0x66, 0x74, 0x64, 0x75, 0x72, 0x72, 0x67, 0x66, 0x75, 0x74, 0x63, 0x62, 0x71, 0x76, 0x6f, 0x69,
+ 0x65, 0x6e, 0x6f, 0x62, 0x70, 0x72, 0x74, 0x6a, 0x6c, 0x72, 0x78, 0x72, 0x66, 0x78, 0x6d, 0x75,
+ 0x79, 0x63, 0x67, 0x6c, 0x74, 0x66, 0x79, 0x71, 0x67, 0x68, 0x75, 0x67, 0x78, 0x78, 0x71, 0x6a,
+ 0x74, 0x6f, 0x62, 0x76, 0x63, 0x63, 0x65, 0x76, 0x64, 0x68, 0x71, 0x6f, 0x65, 0x61, 0x6b, 0x72,
+ 0x67, 0x64, 0x66, 0x63, 0x6e, 0x68, 0x74, 0x66, 0x6d, 0x67, 0x70, 0x6d, 0x6c, 0x65, 0x68, 0x6f,
+ 0x62, 0x65, 0x63, 0x68, 0x62, 0x66, 0x76, 0x61, 0x67, 0x61, 0x62, 0x6f, 0x74, 0x71, 0x78, 0x65,
+ 0x68, 0x6a, 0x6c, 0x66, 0x79, 0x61, 0x6b, 0x6c, 0x6c, 0x6d, 0x64, 0x62, 0x72, 0x67, 0x77, 0x75,
+ 0x6d, 0x69, 0x70, 0x6b, 0x62, 0x6b, 0x70, 0x62, 0x75, 0x79, 0x6e, 0x78, 0x6f, 0x75, 0x6a, 0x66,
+ 0x73, 0x68, 0x71, 0x6b, 0x77, 0x76, 0x66, 0x72, 0x6e, 0x6c, 0x72, 0x6d, 0x68, 0x6e, 0x6b, 0x6c,
+ 0x72, 0x78, 0x70, 0x62, 0x6b, 0x67, 0x65, 0x6b, 0x6a, 0x61, 0x68, 0x6a, 0x61, 0x67, 0x63, 0x73,
+ 0x62, 0x73, 0x6d, 0x62, 0x70, 0x72, 0x77, 0x67, 0x6c, 0x68, 0x67, 0x6b, 0x74, 0x79, 0x65, 0x79,
+ 0x78, 0x72, 0x6a, 0x75, 0x76, 0x6b, 0x66, 0x6e, 0x6e, 0x70, 0x71, 0x61, 0x6b, 0x64, 0x61, 0x77,
+ 0x72, 0x63, 0x63, 0x61, 0x64, 0x68, 0x65, 0x75, 0x6c, 0x61, 0x70, 0x75, 0x76, 0x68, 0x69, 0x64,
+ 0x71, 0x68, 0x73, 0x78, 0x71, 0x71, 0x6a, 0x6d, 0x79, 0x74, 0x71, 0x63, 0x78, 0x74, 0x6e, 0x6f,
+ 0x70, 0x6f, 0x74, 0x73, 0x70, 0x6e, 0x74, 0x73, 0x76, 0x75, 0x69, 0x76, 0x63, 0x6b, 0x69, 0x67,
+ 0x70, 0x78, 0x63, 0x67, 0x67, 0x6f, 0x6f, 0x74, 0x65, 0x72, 0x68, 0x69, 0x71, 0x6f, 0x6d, 0x6c,
+ 0x72, 0x68, 0x76, 0x78, 0x76, 0x73, 0x6d, 0x65, 0x6a, 0x69, 0x76, 0x66, 0x62, 0x63, 0x71, 0x73,
+ 0x73, 0x62, 0x6d, 0x66, 0x67, 0x68, 0x61, 0x75, 0x67, 0x6b, 0x70, 0x6e, 0x6d, 0x66, 0x61, 0x74,
+ 0x76, 0x67, 0x6a, 0x6d, 0x69, 0x6b, 0x75, 0x68, 0x6a, 0x65, 0x6f, 0x6f, 0x64, 0x70, 0x76, 0x67,
+ 0x69, 0x63, 0x70, 0x63, 0x64, 0x75, 0x6c, 0x67, 0x6d, 0x76, 0x67, 0x6a, 0x64, 0x62, 0x6b, 0x65,
+ 0x72, 0x71, 0x6c, 0x68, 0x6d, 0x78, 0x66, 0x73, 0x75, 0x6f, 0x72, 0x63, 0x77, 0x6b, 0x77, 0x71,
+ 0x68, 0x62, 0x6f, 0x6c, 0x77, 0x6c, 0x69, 0x6b, 0x6b, 0x6b, 0x76, 0x66, 0x76, 0x72, 0x6f, 0x67,
+ 0x63, 0x70, 0x66, 0x78, 0x6b, 0x76, 0x6a, 0x74, 0x62, 0x67, 0x61, 0x6c, 0x75, 0x63, 0x68, 0x76,
+ 0x6c, 0x6e, 0x75, 0x76, 0x63, 0x6a, 0x6f, 0x64, 0x75, 0x65, 0x61, 0x6d, 0x72, 0x63, 0x62, 0x69,
+ 0x76, 0x66, 0x6e, 0x6a, 0x6c, 0x63, 0x68, 0x68, 0x71, 0x69, 0x6b, 0x62, 0x66, 0x69, 0x63, 0x6f,
+ 0x6d, 0x6f, 0x70, 0x76, 0x77, 0x63, 0x69, 0x62, 0x76, 0x70, 0x6e, 0x76, 0x78, 0x69, 0x71, 0x73,
+ 0x75, 0x6b, 0x6d, 0x78, 0x61, 0x64, 0x78, 0x64, 0x6e, 0x76, 0x72, 0x66, 0x64, 0x72, 0x66, 0x67,
+ 0x6e, 0x66, 0x67, 0x78, 0x69, 0x6d, 0x62, 0x6e, 0x72, 0x63, 0x71, 0x6c, 0x67, 0x62, 0x6a, 0x63,
+ 0x70, 0x71, 0x6c, 0x71, 0x66, 0x66, 0x67, 0x76, 0x69, 0x6a, 0x65, 0x72, 0x64, 0x6e, 0x67, 0x6c,
+ 0x68, 0x6b, 0x6c, 0x65, 0x6b, 0x70, 0x6b, 0x6a, 0x66, 0x73, 0x70, 0x77, 0x71, 0x61, 0x78, 0x74,
+ 0x63, 0x6c, 0x75, 0x6a, 0x62, 0x77, 0x69, 0x6d, 0x6d, 0x6e, 0x65, 0x75, 0x71, 0x76, 0x66, 0x77,
+ 0x78, 0x78, 0x72, 0x72, 0x68, 0x79, 0x66, 0x68, 0x65, 0x6e, 0x6d, 0x68, 0x6b, 0x65, 0x70, 0x66,
+ 0x72, 0x65, 0x79, 0x65, 0x79, 0x73, 0x78, 0x62, 0x62, 0x69, 0x75, 0x63, 0x74, 0x6c, 0x72, 0x74,
+ 0x73, 0x71, 0x64, 0x6c, 0x75, 0x77, 0x6a, 0x6a, 0x68, 0x64, 0x68, 0x76, 0x71, 0x6d, 0x74, 0x6f,
+ 0x66, 0x75, 0x70, 0x71, 0x62, 0x64, 0x64, 0x74, 0x6b, 0x72, 0x70, 0x71, 0x6d, 0x79, 0x68, 0x61,
+ 0x64, 0x6c, 0x61, 0x75, 0x72, 0x79, 0x77, 0x69, 0x6d, 0x67, 0x64, 0x78, 0x6c, 0x6b, 0x63, 0x78,
+ 0x6a, 0x71, 0x63, 0x67, 0x6d, 0x68, 0x64, 0x61, 0x73, 0x6a, 0x73, 0x68, 0x74, 0x69, 0x6a, 0x72,
+ 0x64, 0x6c, 0x77, 0x70, 0x72, 0x6b, 0x68, 0x6c, 0x6d, 0x6d, 0x63, 0x63, 0x6c, 0x6f, 0x78, 0x63,
+ 0x61, 0x79, 0x6a, 0x64, 0x6a, 0x70, 0x72, 0x71, 0x70, 0x74, 0x66, 0x73, 0x6d, 0x79, 0x6d, 0x6a,
+ 0x62, 0x71, 0x6f, 0x79, 0x66, 0x69, 0x65, 0x78, 0x61, 0x73, 0x61, 0x6b, 0x78, 0x70, 0x78, 0x64,
+ 0x6e, 0x6d, 0x79, 0x6b, 0x77, 0x75, 0x78, 0x78, 0x68, 0x61, 0x6f, 0x6d, 0x78, 0x79, 0x6a, 0x77,
+ 0x6b, 0x77, 0x69, 0x64, 0x74, 0x6e, 0x63, 0x76, 0x67, 0x65, 0x73, 0x61, 0x72, 0x73, 0x78, 0x66,
+ 0x6b, 0x6c, 0x64, 0x6e, 0x78, 0x73, 0x73, 0x68, 0x6e, 0x73, 0x76, 0x62, 0x6e, 0x6d, 0x66, 0x6d,
+ 0x64, 0x79, 0x62, 0x6a, 0x6e, 0x72, 0x71, 0x64, 0x70, 0x79, 0x6a, 0x62, 0x64, 0x65, 0x61, 0x73,
+ 0x74, 0x6e, 0x63, 0x6b, 0x6a, 0x6f, 0x66, 0x68, 0x6e, 0x67, 0x68, 0x77, 0x73, 0x68, 0x61, 0x63,
+ 0x74, 0x73, 0x62, 0x6a, 0x69, 0x75, 0x72, 0x6d, 0x77, 0x6c, 0x6e, 0x72, 0x6f, 0x70, 0x6d, 0x76,
+ 0x71, 0x6e, 0x76, 0x66, 0x6e, 0x6e, 0x75, 0x70, 0x69, 0x72, 0x66, 0x61, 0x6f, 0x6d, 0x66, 0x75,
+ 0x65, 0x68, 0x69, 0x64, 0x78, 0x6c, 0x6f, 0x6b, 0x66, 0x72, 0x6a, 0x77, 0x64, 0x72, 0x72, 0x61,
+ 0x6f, 0x67, 0x61, 0x64, 0x6c, 0x76, 0x70, 0x6c, 0x66, 0x76, 0x79, 0x77, 0x6b, 0x6e, 0x6e, 0x67,
+ 0x76, 0x76, 0x75, 0x6a, 0x64, 0x70, 0x71, 0x63, 0x67, 0x6a, 0x6e, 0x63, 0x73, 0x76, 0x6e, 0x78,
+ 0x62, 0x6e, 0x63, 0x6f, 0x74, 0x65, 0x64, 0x6f, 0x6f, 0x62, 0x79, 0x75, 0x72, 0x69, 0x61, 0x78,
+ 0x72, 0x75, 0x6e, 0x6b, 0x64, 0x67, 0x6e, 0x72, 0x6f, 0x73, 0x68, 0x79, 0x68, 0x61, 0x6b, 0x6d,
+ 0x72, 0x64, 0x6f, 0x71, 0x72, 0x64, 0x6b, 0x71, 0x6c, 0x62, 0x68, 0x6b, 0x6a, 0x6d, 0x64, 0x6b,
+ 0x71, 0x76, 0x6c, 0x63, 0x72, 0x61, 0x73, 0x62, 0x6f, 0x6f, 0x6c, 0x69, 0x75, 0x64, 0x63, 0x70,
+ 0x6b, 0x6e, 0x6f, 0x6f, 0x6c, 0x65, 0x6f, 0x78, 0x6a, 0x74, 0x64, 0x79, 0x62, 0x64, 0x69, 0x79,
+ 0x63, 0x6c, 0x77, 0x6f, 0x74, 0x6a, 0x68, 0x61, 0x6a, 0x68, 0x74, 0x6a, 0x70, 0x68, 0x72, 0x68,
+ 0x63, 0x6f, 0x71, 0x77, 0x78, 0x76, 0x6a, 0x6e, 0x76, 0x73, 0x67, 0x69, 0x6f, 0x73, 0x68, 0x71,
+ 0x69, 0x6c, 0x73, 0x73, 0x79, 0x67, 0x79, 0x61, 0x64, 0x70, 0x61, 0x71, 0x68, 0x73, 0x6b, 0x65,
+ 0x64, 0x75, 0x76, 0x69, 0x73, 0x6e, 0x67, 0x77, 0x6a, 0x72, 0x62, 0x74, 0x76, 0x63, 0x6f, 0x73,
+ 0x64, 0x64, 0x62, 0x69, 0x72, 0x6b, 0x71, 0x63, 0x73, 0x73, 0x64, 0x69, 0x6e, 0x73, 0x64, 0x6d,
+ 0x63, 0x70, 0x67, 0x76, 0x61, 0x79, 0x6d, 0x73, 0x74, 0x6c, 0x79, 0x6d, 0x74, 0x68, 0x65, 0x77,
+ 0x63, 0x66, 0x77, 0x6e, 0x64, 0x69, 0x6a, 0x62, 0x65, 0x6e, 0x79, 0x6b, 0x6c, 0x6e, 0x79, 0x6e,
+ 0x65, 0x76, 0x6b, 0x6e, 0x62, 0x6a, 0x72, 0x73, 0x72, 0x76, 0x69, 0x71, 0x62, 0x76, 0x63, 0x69,
+ 0x6c, 0x63, 0x64, 0x70, 0x6d, 0x6b, 0x63, 0x6c, 0x61, 0x77, 0x71, 0x74, 0x61, 0x71, 0x6c, 0x65,
+ 0x65, 0x70, 0x70, 0x67, 0x6c, 0x77, 0x6e, 0x74, 0x64, 0x79, 0x62, 0x64, 0x63, 0x77, 0x79, 0x73,
+ 0x73, 0x66, 0x68, 0x70, 0x65, 0x61, 0x67, 0x72, 0x61, 0x6a, 0x68, 0x66, 0x62, 0x72, 0x6d, 0x6e,
+ 0x65, 0x78, 0x6b, 0x6b, 0x78, 0x74, 0x72, 0x6b, 0x71, 0x67, 0x6a, 0x69, 0x71, 0x65, 0x6d, 0x76,
+ 0x79, 0x69, 0x61, 0x65, 0x70, 0x68, 0x6e, 0x64, 0x6a, 0x77, 0x69, 0x6c, 0x6b, 0x6b, 0x63, 0x62,
+ 0x6d, 0x63, 0x76, 0x77, 0x67, 0x6e, 0x6b, 0x6d, 0x67, 0x71, 0x69, 0x78, 0x6e, 0x6d, 0x6b, 0x78,
+ 0x65, 0x6c, 0x61, 0x77, 0x6f, 0x64, 0x79, 0x62, 0x63, 0x61, 0x6b, 0x66, 0x72, 0x79, 0x69, 0x65,
+ 0x76, 0x77, 0x65, 0x6d, 0x6c, 0x69, 0x62, 0x6f, 0x79, 0x77, 0x6d, 0x78, 0x6d, 0x62, 0x6f, 0x78,
+ 0x68, 0x77, 0x6d, 0x70, 0x72, 0x6d, 0x63, 0x71, 0x79, 0x65, 0x61, 0x66, 0x78, 0x66, 0x63, 0x6e,
+ 0x69, 0x68, 0x73, 0x74, 0x6d, 0x6f, 0x76, 0x71, 0x64, 0x75, 0x77, 0x63, 0x6c, 0x63, 0x70, 0x61,
+ 0x6d, 0x77, 0x76, 0x78, 0x72, 0x71, 0x6f, 0x61, 0x74, 0x62, 0x64, 0x68, 0x64, 0x6a, 0x67, 0x75,
+ 0x71, 0x79, 0x66, 0x70, 0x64, 0x72, 0x62, 0x6b, 0x65, 0x65, 0x66, 0x79, 0x6d, 0x75, 0x70, 0x6a,
+ 0x61, 0x75, 0x67, 0x65, 0x70, 0x6c, 0x65, 0x61, 0x77, 0x72, 0x67, 0x73, 0x74, 0x68, 0x77, 0x70,
+ 0x65, 0x6c, 0x74, 0x75, 0x73, 0x74, 0x6d, 0x61, 0x64, 0x6f, 0x79, 0x78, 0x79, 0x62, 0x75, 0x6b,
+ 0x66, 0x79, 0x78, 0x75, 0x73, 0x71, 0x75, 0x76, 0x6a, 0x63, 0x73, 0x68, 0x65, 0x71, 0x6e, 0x62,
+ 0x75, 0x77, 0x74, 0x68, 0x70, 0x77, 0x6c, 0x66, 0x6d, 0x79, 0x65, 0x64, 0x66, 0x69, 0x70, 0x66,
+ 0x74, 0x66, 0x6c, 0x77, 0x71, 0x6f, 0x6d, 0x78, 0x6d, 0x6d, 0x70, 0x73, 0x77, 0x75, 0x79, 0x65,
+ 0x63, 0x6e, 0x76, 0x66, 0x63, 0x6d, 0x71, 0x65, 0x67, 0x74, 0x73, 0x6b, 0x6f, 0x70, 0x62, 0x74,
+ 0x78, 0x68, 0x6d, 0x67, 0x72, 0x66, 0x75, 0x77, 0x69, 0x68, 0x62, 0x70, 0x74, 0x75, 0x6f, 0x63,
+ 0x68, 0x70, 0x79, 0x6e, 0x77, 0x61, 0x68, 0x6d, 0x6d, 0x68, 0x74, 0x78, 0x76, 0x78, 0x76, 0x74,
+ 0x65, 0x75, 0x6f, 0x70, 0x65, 0x79, 0x6f, 0x73, 0x63, 0x61, 0x6b, 0x76, 0x6c, 0x74, 0x75, 0x75,
+ 0x68, 0x6f, 0x65, 0x6d, 0x74, 0x68, 0x63, 0x71, 0x67, 0x64, 0x72, 0x63, 0x67, 0x6b, 0x68, 0x74,
+ 0x69, 0x65, 0x6c, 0x73, 0x74, 0x63, 0x75, 0x69, 0x71, 0x65, 0x75, 0x67, 0x6b, 0x79, 0x79, 0x6f,
+ 0x75, 0x63, 0x6d, 0x66, 0x75, 0x6f, 0x6d, 0x6d, 0x73, 0x63, 0x6d, 0x73, 0x70, 0x71, 0x76, 0x74,
+ 0x71, 0x72, 0x77, 0x71, 0x70, 0x76, 0x65, 0x79, 0x72, 0x6d, 0x6d, 0x76, 0x78, 0x65, 0x77, 0x72,
+ 0x78, 0x72, 0x6c, 0x75, 0x76, 0x76, 0x72, 0x72, 0x77, 0x6b, 0x68, 0x6a, 0x73, 0x6c, 0x6f, 0x6d,
+ 0x79, 0x68, 0x75, 0x62, 0x6c, 0x6d, 0x69, 0x68, 0x78, 0x62, 0x65, 0x79, 0x72, 0x78, 0x6d, 0x6e,
+ 0x68, 0x69, 0x6d, 0x74, 0x63, 0x65, 0x66, 0x70, 0x77, 0x62, 0x6b, 0x64, 0x75, 0x6e, 0x72, 0x6a,
+ 0x76, 0x6e, 0x75, 0x6f, 0x6e, 0x61, 0x71, 0x72, 0x74, 0x76, 0x61, 0x61, 0x74, 0x66, 0x66, 0x61,
+ 0x78, 0x72, 0x70, 0x62, 0x65, 0x73, 0x79, 0x64, 0x75, 0x67, 0x75, 0x66, 0x71, 0x73, 0x73, 0x70,
+ 0x69, 0x73, 0x6d, 0x6c, 0x70, 0x64, 0x69, 0x6f, 0x74, 0x78, 0x70, 0x72, 0x78, 0x71, 0x73, 0x63,
+ 0x62, 0x70, 0x76, 0x6c, 0x6b, 0x62, 0x61, 0x76, 0x73, 0x67, 0x77, 0x75, 0x6a, 0x72, 0x65, 0x64,
+ 0x62, 0x79, 0x6e, 0x76, 0x6d, 0x65, 0x6c, 0x62, 0x67, 0x63, 0x62, 0x77, 0x72, 0x78, 0x65, 0x77,
+ 0x71, 0x72, 0x64, 0x75, 0x69, 0x77, 0x6c, 0x6f, 0x77, 0x61, 0x69, 0x6d, 0x69, 0x76, 0x63, 0x72,
+ 0x79, 0x79, 0x6a, 0x6f, 0x63, 0x72, 0x74, 0x62, 0x78, 0x77, 0x6d, 0x75, 0x62, 0x6c, 0x6b, 0x6f,
+ 0x74, 0x63, 0x73, 0x70, 0x74, 0x6d, 0x6a, 0x70, 0x6f, 0x6a, 0x6f, 0x66, 0x61, 0x66, 0x71, 0x75,
+ 0x74, 0x73, 0x6a, 0x6c, 0x6d, 0x79, 0x68, 0x71, 0x78, 0x75, 0x6d, 0x6f, 0x70, 0x74, 0x70, 0x76,
+ 0x71, 0x6a, 0x61, 0x62, 0x67, 0x69, 0x6d, 0x62, 0x74, 0x6b, 0x67, 0x73, 0x66, 0x6f, 0x6d, 0x6c,
+ 0x79, 0x69, 0x69, 0x6d, 0x67, 0x6d, 0x71, 0x66, 0x73, 0x66, 0x6e, 0x65, 0x6c, 0x75, 0x79, 0x68,
+ 0x6a, 0x74, 0x79, 0x70, 0x72, 0x73, 0x6b, 0x65, 0x6d, 0x6a, 0x6e, 0x6a, 0x76, 0x6d, 0x6d, 0x6a,
+ 0x6b, 0x68, 0x65, 0x6a, 0x68, 0x71, 0x6b, 0x63, 0x73, 0x6a, 0x78, 0x68, 0x6f, 0x76, 0x70, 0x75,
+ 0x73, 0x72, 0x78, 0x6f, 0x74, 0x6c, 0x6f, 0x6d, 0x6c, 0x74, 0x6b, 0x73, 0x71, 0x66, 0x6a, 0x64,
+ 0x6a, 0x68, 0x6b, 0x67, 0x72, 0x78, 0x76, 0x68, 0x70, 0x69, 0x6e, 0x75, 0x6d, 0x76, 0x6b, 0x62,
+ 0x6b, 0x61, 0x78, 0x6f, 0x77, 0x72, 0x75, 0x76, 0x6e, 0x69, 0x6f, 0x6c, 0x6d, 0x68, 0x6e, 0x6b,
+ 0x6d, 0x75, 0x6c, 0x75, 0x72, 0x75, 0x78, 0x6b, 0x73, 0x71, 0x6b, 0x76, 0x63, 0x63, 0x6f, 0x6b,
+ 0x75, 0x6a, 0x6a, 0x74, 0x6c, 0x78, 0x73, 0x74, 0x75, 0x71, 0x6e, 0x76, 0x77, 0x66, 0x6e, 0x61,
+ 0x66, 0x62, 0x63, 0x65, 0x72, 0x63, 0x67, 0x77, 0x62, 0x6a, 0x71, 0x75, 0x6a, 0x6f, 0x63, 0x71,
+ 0x77, 0x72, 0x74, 0x6e, 0x71, 0x6f, 0x72, 0x69, 0x74, 0x65, 0x66, 0x77, 0x61, 0x68, 0x74, 0x70,
+ 0x64, 0x76, 0x62, 0x65, 0x64, 0x68, 0x63, 0x68, 0x77, 0x75, 0x6b, 0x79, 0x77, 0x70, 0x61, 0x63,
+ 0x6b, 0x64, 0x6a, 0x79, 0x72, 0x6f, 0x73, 0x62, 0x65, 0x69, 0x71, 0x6d, 0x66, 0x61, 0x73, 0x6e,
+ 0x6b, 0x63, 0x65, 0x77, 0x73, 0x72, 0x77, 0x62, 0x63, 0x75, 0x67, 0x62, 0x65, 0x62, 0x6b, 0x76,
+ 0x61, 0x71, 0x78, 0x76, 0x61, 0x6d, 0x70, 0x65, 0x6f, 0x68, 0x77, 0x64, 0x61, 0x6a, 0x6f, 0x67,
+ 0x6b, 0x67, 0x68, 0x75, 0x71, 0x66, 0x73, 0x69, 0x75, 0x62, 0x6a, 0x6c, 0x72, 0x71, 0x68, 0x69,
+ 0x61, 0x6a, 0x6a, 0x79, 0x71, 0x6c, 0x6d, 0x76, 0x6f, 0x68, 0x74, 0x77, 0x6a, 0x63, 0x62, 0x6d,
+ 0x6a, 0x6f, 0x74, 0x61, 0x6f, 0x6b, 0x69, 0x64, 0x72, 0x6b, 0x67, 0x75, 0x76, 0x77, 0x78, 0x61,
+ 0x6c, 0x70, 0x69, 0x6f, 0x69, 0x62, 0x61, 0x67, 0x72, 0x62, 0x67, 0x65, 0x67, 0x6e, 0x62, 0x6c,
+ 0x66, 0x69, 0x69, 0x6b, 0x6a, 0x6d, 0x6a, 0x70, 0x6d, 0x73, 0x6f, 0x77, 0x63, 0x75, 0x6c, 0x69,
+ 0x65, 0x6c, 0x79, 0x6d, 0x79, 0x71, 0x6d, 0x61, 0x74, 0x6b, 0x78, 0x75, 0x6b, 0x6c, 0x73, 0x63,
+ 0x6d, 0x6c, 0x6c, 0x78, 0x74, 0x6f, 0x6f, 0x67, 0x71, 0x67, 0x78, 0x71, 0x64, 0x65, 0x63, 0x6b,
+ 0x69, 0x74, 0x6d, 0x74, 0x6b, 0x6b, 0x78, 0x61, 0x74, 0x66, 0x75, 0x77, 0x66, 0x68, 0x69, 0x6a,
+ 0x66, 0x65, 0x79, 0x78, 0x65, 0x6a, 0x6e, 0x71, 0x76, 0x79, 0x69, 0x71, 0x64, 0x76, 0x77, 0x67,
+ 0x61, 0x6d, 0x6c, 0x71, 0x66, 0x6c, 0x67, 0x64, 0x65, 0x62, 0x66, 0x77, 0x6c, 0x65, 0x74, 0x72,
+ 0x62, 0x64, 0x61, 0x6c, 0x6e, 0x6c, 0x75, 0x6e, 0x64, 0x74, 0x78, 0x63, 0x62, 0x62, 0x68, 0x73,
+ 0x61, 0x67, 0x79, 0x6f, 0x67, 0x6c, 0x66, 0x62, 0x75, 0x72, 0x76, 0x6f, 0x62, 0x61, 0x66, 0x73,
+ 0x74, 0x75, 0x6c, 0x62, 0x69, 0x6a, 0x68, 0x6b, 0x75, 0x77, 0x6d, 0x72, 0x6e, 0x62, 0x77, 0x6a,
+ 0x64, 0x72, 0x74, 0x67, 0x76, 0x79, 0x6c, 0x65, 0x79, 0x78, 0x79, 0x68, 0x76, 0x75, 0x78, 0x69,
+ 0x61, 0x78, 0x6e, 0x77, 0x64, 0x69, 0x61, 0x69, 0x74, 0x70, 0x66, 0x78, 0x75, 0x64, 0x6a, 0x68,
+ 0x6f, 0x67, 0x6c, 0x6e, 0x74, 0x70, 0x75, 0x74, 0x75, 0x6a, 0x61, 0x6d, 0x78, 0x76, 0x63, 0x67,
+ 0x6f, 0x66, 0x6b, 0x71, 0x6d, 0x74, 0x6c, 0x66, 0x63, 0x62, 0x73, 0x6c, 0x74, 0x63, 0x6c, 0x6d,
+ 0x72, 0x73, 0x6c, 0x77, 0x75, 0x6a, 0x77, 0x66, 0x75, 0x75, 0x73, 0x71, 0x68, 0x6d, 0x6d, 0x61,
+ 0x6d, 0x6b, 0x63, 0x63, 0x6c, 0x6d, 0x77, 0x74, 0x6c, 0x69, 0x65, 0x68, 0x6d, 0x67, 0x63, 0x70,
+ 0x63, 0x61, 0x6a, 0x71, 0x72, 0x67, 0x6f, 0x78, 0x73, 0x62, 0x70, 0x6f, 0x6f, 0x65, 0x6f, 0x6f,
+ 0x73, 0x75, 0x6d, 0x6a, 0x75, 0x70, 0x71, 0x61, 0x61, 0x72, 0x6a, 0x66, 0x72, 0x72, 0x62, 0x61,
+ 0x67, 0x63, 0x73, 0x79, 0x6e, 0x71, 0x62, 0x6a, 0x63, 0x66, 0x76, 0x6c, 0x64, 0x64, 0x68, 0x77,
+ 0x66, 0x6b, 0x6c, 0x6e, 0x6b, 0x61, 0x73, 0x6e, 0x73, 0x63, 0x78, 0x72, 0x62, 0x75, 0x6b, 0x63,
+ 0x6e, 0x6b, 0x6e, 0x65, 0x6a, 0x62, 0x62, 0x77, 0x6a, 0x77, 0x6c, 0x62, 0x70, 0x67, 0x68, 0x72,
+ 0x73, 0x6d, 0x71, 0x63, 0x69, 0x74, 0x68, 0x6c, 0x78, 0x69, 0x78, 0x65, 0x75, 0x6d, 0x63, 0x6b,
+ 0x64, 0x75, 0x65, 0x76, 0x6c, 0x72, 0x6e, 0x63, 0x79, 0x6d, 0x63, 0x6d, 0x67, 0x70, 0x66, 0x6b,
+ 0x76, 0x6a, 0x6b, 0x62, 0x75, 0x69, 0x76, 0x6c, 0x62, 0x76, 0x66, 0x72, 0x61, 0x6a, 0x67, 0x71,
+ 0x64, 0x74, 0x74, 0x70, 0x6c, 0x63, 0x73, 0x6e, 0x71, 0x74, 0x6b, 0x6a, 0x64, 0x62, 0x69, 0x68,
+ 0x65, 0x71, 0x6a, 0x6b, 0x62, 0x73, 0x6c, 0x67, 0x6d, 0x68, 0x78, 0x6d, 0x73, 0x69, 0x65, 0x74,
+ 0x6c, 0x61, 0x66, 0x74, 0x65, 0x65, 0x62, 0x6a, 0x6a, 0x6d, 0x71, 0x61, 0x69, 0x74, 0x70, 0x6c,
+ 0x68, 0x65, 0x65, 0x6b, 0x69, 0x79, 0x72, 0x71, 0x71, 0x74, 0x6f, 0x6e, 0x6c, 0x62, 0x74, 0x63,
+ 0x64, 0x69, 0x66, 0x70, 0x76, 0x6d, 0x6b, 0x72, 0x6c, 0x6a, 0x77, 0x61, 0x74, 0x72, 0x62, 0x65,
+ 0x68, 0x6e, 0x75, 0x62, 0x71, 0x6e, 0x63, 0x63, 0x64, 0x64, 0x62, 0x69, 0x74, 0x6c, 0x6e, 0x70,
+ 0x78, 0x69, 0x63, 0x65, 0x6a, 0x6f, 0x78, 0x6c, 0x6a, 0x65, 0x6e, 0x6c, 0x6c, 0x65, 0x67, 0x6e,
+ 0x68, 0x62, 0x63, 0x65, 0x6c, 0x62, 0x6f, 0x6e, 0x6d, 0x61, 0x6c, 0x61, 0x76, 0x69, 0x71, 0x77,
+ 0x74, 0x6a, 0x74, 0x74, 0x6d, 0x6a, 0x6a, 0x77, 0x70, 0x76, 0x70, 0x75, 0x6c, 0x6f, 0x6c, 0x75,
+ 0x69, 0x61, 0x6c, 0x75, 0x62, 0x6f, 0x68, 0x67, 0x69, 0x67, 0x61, 0x6d, 0x72, 0x72, 0x63, 0x6b,
+ 0x63, 0x70, 0x6c, 0x70, 0x69, 0x71, 0x6e, 0x76, 0x6f, 0x77, 0x74, 0x64, 0x76, 0x67, 0x6f, 0x77,
+ 0x76, 0x77, 0x6e, 0x61, 0x72, 0x76, 0x74, 0x65, 0x63, 0x63, 0x73, 0x65, 0x6d, 0x67, 0x69, 0x65,
+ 0x74, 0x6e, 0x77, 0x63, 0x78, 0x77, 0x63, 0x76, 0x68, 0x65, 0x76, 0x73, 0x64, 0x6a, 0x68, 0x6a,
+ 0x65, 0x72, 0x75, 0x70, 0x61, 0x6b, 0x71, 0x68, 0x74, 0x6f, 0x78, 0x6d, 0x74, 0x62, 0x61, 0x71,
+ 0x64, 0x6b, 0x6a, 0x76, 0x79, 0x61, 0x6c, 0x77, 0x6a, 0x68, 0x78, 0x62, 0x70, 0x78, 0x78, 0x65,
+ 0x64, 0x74, 0x6f, 0x71, 0x69, 0x75, 0x70, 0x78, 0x71, 0x78, 0x66, 0x65, 0x74, 0x6d, 0x6a, 0x75,
+ 0x66, 0x76, 0x70, 0x75, 0x69, 0x6c, 0x73, 0x76, 0x79, 0x63, 0x62, 0x68, 0x74, 0x6e, 0x69, 0x72,
+ 0x76, 0x6b, 0x62, 0x6d, 0x67, 0x65, 0x79, 0x70, 0x79, 0x6d, 0x65, 0x70, 0x79, 0x6a, 0x62, 0x61,
+ 0x76, 0x64, 0x65, 0x75, 0x69, 0x6a, 0x76, 0x70, 0x6c, 0x69, 0x74, 0x6b, 0x65, 0x6d, 0x69, 0x75,
+ 0x77, 0x68, 0x6b, 0x6f, 0x72, 0x66, 0x68, 0x75, 0x73, 0x63, 0x77, 0x69, 0x69, 0x6b, 0x68, 0x67,
+ 0x66, 0x6f, 0x71, 0x69, 0x63, 0x77, 0x74, 0x61, 0x6b, 0x70, 0x62, 0x6b, 0x6e, 0x6f, 0x77, 0x6e,
+ 0x78, 0x6e, 0x66, 0x69, 0x78, 0x6f, 0x64, 0x77, 0x61, 0x6c, 0x70, 0x75, 0x62, 0x70, 0x6d, 0x76,
+ 0x79, 0x72, 0x66, 0x64, 0x76, 0x75, 0x6f, 0x6f, 0x71, 0x73, 0x75, 0x73, 0x77, 0x79, 0x71, 0x63,
+ 0x76, 0x78, 0x73, 0x6e, 0x6d, 0x70, 0x69, 0x78, 0x75, 0x6c, 0x6e, 0x76, 0x76, 0x79, 0x6d, 0x68,
+ 0x71, 0x76, 0x63, 0x77, 0x67, 0x6d, 0x6a, 0x64, 0x6f, 0x73, 0x64, 0x70, 0x68, 0x67, 0x6f, 0x61,
+ 0x67, 0x6d, 0x76, 0x6f, 0x6c, 0x74, 0x72, 0x74, 0x76, 0x62, 0x70, 0x61, 0x64, 0x6f, 0x73, 0x63,
+ 0x64, 0x74, 0x68, 0x68, 0x76, 0x79, 0x6a, 0x66, 0x78, 0x6a, 0x63, 0x68, 0x6a, 0x61, 0x73, 0x65,
+ 0x65, 0x6c, 0x6f, 0x61, 0x61, 0x71, 0x6b, 0x72, 0x6e, 0x71, 0x62, 0x6f, 0x66, 0x77, 0x72, 0x66,
+ 0x63, 0x75, 0x73, 0x65, 0x62, 0x66, 0x74, 0x69, 0x71, 0x66, 0x72, 0x67, 0x61, 0x6c, 0x68, 0x64,
+ 0x63, 0x66, 0x73, 0x78, 0x74, 0x63, 0x64, 0x71, 0x71, 0x75, 0x77, 0x6a, 0x71, 0x6a, 0x6e, 0x6b,
+ 0x77, 0x64, 0x66, 0x68, 0x69, 0x77, 0x77, 0x71, 0x62, 0x73, 0x64, 0x70, 0x71, 0x65, 0x63, 0x71,
+ 0x71, 0x70, 0x6e, 0x6a, 0x75, 0x6f, 0x70, 0x63, 0x67, 0x65, 0x72, 0x76, 0x77, 0x79, 0x72, 0x6b,
+ 0x68, 0x77, 0x75, 0x68, 0x6a, 0x66, 0x6f, 0x6c, 0x79, 0x76, 0x66, 0x65, 0x62, 0x72, 0x75, 0x62,
+ 0x69, 0x65, 0x77, 0x72, 0x77, 0x74, 0x69, 0x6f, 0x76, 0x6e, 0x76, 0x6a, 0x68, 0x76, 0x6f, 0x75,
+ 0x77, 0x6f, 0x75, 0x6c, 0x67, 0x74, 0x6d, 0x75, 0x79, 0x62, 0x6c, 0x6c, 0x66, 0x6e, 0x76, 0x62,
+ 0x6a, 0x71, 0x65, 0x66, 0x77, 0x62, 0x6b, 0x71, 0x61, 0x66, 0x71, 0x76, 0x6b, 0x6c, 0x72, 0x6c,
+ 0x63, 0x63, 0x68, 0x68, 0x6a, 0x69, 0x6a, 0x63, 0x75, 0x71, 0x68, 0x6b, 0x6d, 0x61, 0x76, 0x76,
+ 0x6e, 0x6a, 0x6a, 0x67, 0x68, 0x6f, 0x65, 0x67, 0x72, 0x6f, 0x70, 0x70, 0x6b, 0x63, 0x64, 0x75,
+ 0x63, 0x67, 0x65, 0x71, 0x69, 0x64, 0x66, 0x65, 0x6c, 0x72, 0x72, 0x66, 0x72, 0x74, 0x66, 0x75,
+ 0x65, 0x77, 0x6d, 0x69, 0x68, 0x6e, 0x72, 0x77, 0x6a, 0x61, 0x72, 0x67, 0x73, 0x76, 0x6b, 0x67,
+ 0x67, 0x6c, 0x75, 0x67, 0x61, 0x73, 0x78, 0x68, 0x72, 0x6b, 0x64, 0x69, 0x6d, 0x6c, 0x6e, 0x68,
+ 0x78, 0x76, 0x70, 0x67, 0x6c, 0x6f, 0x68, 0x63, 0x72, 0x76, 0x61, 0x69, 0x68, 0x71, 0x69, 0x6f,
+ 0x67, 0x6b, 0x61, 0x69, 0x6d, 0x73, 0x66, 0x6c, 0x64, 0x76, 0x71, 0x78, 0x79, 0x74, 0x62, 0x71,
+ 0x6b, 0x62, 0x71, 0x63, 0x69, 0x74, 0x61, 0x6a, 0x63, 0x75, 0x6f, 0x6a, 0x6b, 0x64, 0x61, 0x77,
+ 0x66, 0x61, 0x6c, 0x74, 0x71, 0x75, 0x61, 0x69, 0x6e, 0x69, 0x6a, 0x6b, 0x61, 0x65, 0x78, 0x68,
+ 0x62, 0x78, 0x75, 0x6c, 0x6b, 0x68, 0x79, 0x77, 0x6a, 0x76, 0x6f, 0x61, 0x63, 0x71, 0x65, 0x66,
+ 0x6e, 0x67, 0x73, 0x74, 0x62, 0x73, 0x73, 0x76, 0x78, 0x6b, 0x63, 0x78, 0x72, 0x6c, 0x6a, 0x67,
+ 0x69, 0x63, 0x78, 0x64, 0x63, 0x6d, 0x6a, 0x6f, 0x6d, 0x6d, 0x63, 0x74, 0x70, 0x64, 0x6d, 0x74,
+ 0x72, 0x77, 0x6a, 0x75, 0x73, 0x6c, 0x63, 0x68, 0x71, 0x71, 0x62, 0x75, 0x6d, 0x79, 0x70, 0x6f,
+ 0x77, 0x72, 0x62, 0x6a, 0x69, 0x6e, 0x76, 0x77, 0x61, 0x73, 0x71, 0x79, 0x78, 0x70, 0x78, 0x6e,
+ 0x64, 0x79, 0x78, 0x6e, 0x6e, 0x65, 0x78, 0x73, 0x68, 0x76, 0x79, 0x75, 0x65, 0x73, 0x65, 0x6c,
+ 0x72, 0x6d, 0x64, 0x76, 0x73, 0x76, 0x6e, 0x67, 0x73, 0x74, 0x6a, 0x62, 0x79, 0x79, 0x70, 0x64,
+ 0x71, 0x73, 0x6d, 0x64, 0x6a, 0x62, 0x66, 0x67, 0x70, 0x67, 0x74, 0x66, 0x6e, 0x78, 0x78, 0x6e,
+ 0x64, 0x6e, 0x64, 0x6f, 0x64, 0x6f, 0x73, 0x6d, 0x68, 0x79, 0x68, 0x67, 0x6f, 0x61, 0x66, 0x68,
+ 0x78, 0x73, 0x62, 0x6b, 0x61, 0x63, 0x6b, 0x6a, 0x76, 0x62, 0x66, 0x78, 0x79, 0x61, 0x76, 0x6c,
+ 0x64, 0x71, 0x72, 0x75, 0x66, 0x69, 0x6c, 0x64, 0x62, 0x6b, 0x63, 0x6d, 0x73, 0x72, 0x61, 0x77,
+ 0x69, 0x71, 0x73, 0x78, 0x63, 0x74, 0x74, 0x66, 0x71, 0x73, 0x63, 0x6c, 0x70, 0x66, 0x65, 0x68,
+ 0x6a, 0x72, 0x62, 0x70, 0x75, 0x65, 0x66, 0x70, 0x73, 0x72, 0x62, 0x69, 0x70, 0x6f, 0x78, 0x78,
+ 0x66, 0x78, 0x71, 0x63, 0x73, 0x6a, 0x70, 0x70, 0x6a, 0x74, 0x67, 0x6e, 0x79, 0x67, 0x75, 0x72,
+ 0x69, 0x74, 0x61, 0x73, 0x74, 0x68, 0x75, 0x70, 0x6f, 0x72, 0x63, 0x75, 0x70, 0x73, 0x64, 0x69,
+ 0x72, 0x75, 0x74, 0x63, 0x71, 0x75, 0x6f, 0x64, 0x74, 0x66, 0x63, 0x65, 0x68, 0x76, 0x74, 0x77,
+ 0x75, 0x69, 0x75, 0x63, 0x65, 0x61, 0x79, 0x77, 0x67, 0x77, 0x69, 0x6f, 0x6e, 0x74, 0x75, 0x75,
+ 0x74, 0x6c, 0x62, 0x6f, 0x6b, 0x78, 0x71, 0x66, 0x78, 0x62, 0x64, 0x64, 0x6b, 0x69, 0x77, 0x64,
+ 0x66, 0x70, 0x74, 0x73, 0x6a, 0x68, 0x74, 0x75, 0x65, 0x6e, 0x73, 0x70, 0x78, 0x6f, 0x74, 0x69,
+ 0x72, 0x76, 0x70, 0x72, 0x69, 0x68, 0x70, 0x6d, 0x66, 0x67, 0x74, 0x73, 0x6d, 0x66, 0x68, 0x73,
+ 0x6b, 0x6c, 0x66, 0x62, 0x66, 0x76, 0x66, 0x6a, 0x70, 0x76, 0x67, 0x72, 0x65, 0x78, 0x61, 0x6f,
+ 0x78, 0x68, 0x62, 0x61, 0x73, 0x64, 0x65, 0x6b, 0x73, 0x6a, 0x64, 0x61, 0x68, 0x67, 0x64, 0x78,
+ 0x6f, 0x6f, 0x61, 0x65, 0x78, 0x68, 0x64, 0x6f, 0x71, 0x72, 0x69, 0x66, 0x64, 0x71, 0x73, 0x61,
+ 0x78, 0x74, 0x69, 0x77, 0x6a, 0x74, 0x74, 0x79, 0x6f, 0x70, 0x78, 0x6b, 0x76, 0x6d, 0x79, 0x72,
+ 0x6e, 0x65, 0x74, 0x62, 0x6f, 0x67, 0x67, 0x6e, 0x6b, 0x74, 0x77, 0x73, 0x69, 0x75, 0x61, 0x64,
+ 0x6a, 0x79, 0x70, 0x70, 0x62, 0x78, 0x6c, 0x71, 0x61, 0x72, 0x6c, 0x65, 0x79, 0x61, 0x77, 0x6a,
+ 0x79, 0x6b, 0x6f, 0x66, 0x66, 0x79, 0x6a, 0x79, 0x6c, 0x62, 0x67, 0x6f, 0x64, 0x6a, 0x67, 0x61,
+ 0x72, 0x6a, 0x64, 0x72, 0x72, 0x77, 0x79, 0x61, 0x78, 0x77, 0x71, 0x70, 0x76, 0x67, 0x78, 0x78,
+ 0x63, 0x6b, 0x78, 0x64, 0x77, 0x6c, 0x73, 0x66, 0x6c, 0x63, 0x74, 0x6e, 0x6c, 0x65, 0x63, 0x74,
+ 0x65, 0x68, 0x64, 0x6c, 0x65, 0x6d, 0x64, 0x76, 0x71, 0x72, 0x6b, 0x66, 0x74, 0x71, 0x6d, 0x6b,
+ 0x74, 0x69, 0x75, 0x73, 0x67, 0x65, 0x6c, 0x73, 0x63, 0x6c, 0x72, 0x66, 0x78, 0x69, 0x67, 0x73,
+ 0x75, 0x6f, 0x6a, 0x75, 0x6b, 0x6e, 0x6f, 0x64, 0x6a, 0x66, 0x73, 0x69, 0x63, 0x66, 0x6e, 0x6b,
+ 0x68, 0x71, 0x69, 0x61, 0x6a, 0x63, 0x78, 0x6e, 0x6c, 0x6a, 0x78, 0x66, 0x71, 0x69, 0x6e, 0x62,
+ 0x78, 0x75, 0x72, 0x64, 0x73, 0x6f, 0x65, 0x78, 0x70, 0x73, 0x72, 0x72, 0x70, 0x78, 0x76, 0x6a,
+ 0x6b, 0x63, 0x75, 0x78, 0x76, 0x67, 0x6c, 0x66, 0x61, 0x6a, 0x71, 0x71, 0x6d, 0x6a, 0x6a, 0x62,
+ 0x65, 0x76, 0x73, 0x71, 0x77, 0x69, 0x63, 0x65, 0x74, 0x64, 0x79, 0x66, 0x6d, 0x66, 0x74, 0x68,
+ 0x62, 0x6d, 0x6d, 0x64, 0x78, 0x74, 0x76, 0x75, 0x78, 0x79, 0x66, 0x71, 0x6d, 0x6d, 0x6f, 0x78,
+ 0x61, 0x63, 0x64, 0x74, 0x71, 0x63, 0x65, 0x63, 0x68, 0x64, 0x78, 0x6b, 0x79, 0x6d, 0x71, 0x6f,
+ 0x61, 0x79, 0x72, 0x6e, 0x64, 0x71, 0x63, 0x63, 0x6f, 0x71, 0x73, 0x6d, 0x74, 0x71, 0x63, 0x64,
+ 0x74, 0x6f, 0x71, 0x78, 0x78, 0x6b, 0x68, 0x74, 0x6f, 0x63, 0x62, 0x6f, 0x78, 0x6e, 0x6f, 0x76,
+ 0x6a, 0x6b, 0x6c, 0x68, 0x68, 0x62, 0x62, 0x6a, 0x71, 0x67, 0x6f, 0x63, 0x66, 0x72, 0x63, 0x63,
+ 0x6f, 0x74, 0x6d, 0x72, 0x77, 0x71, 0x72, 0x6d, 0x65, 0x6c, 0x6b, 0x76, 0x70, 0x74, 0x72, 0x61,
+ 0x74, 0x72, 0x71, 0x76, 0x79, 0x6a, 0x77, 0x72, 0x65, 0x61, 0x6b, 0x74, 0x6d, 0x61, 0x67, 0x75,
+ 0x73, 0x73, 0x69, 0x67, 0x77, 0x66, 0x73, 0x72, 0x63, 0x6b, 0x66, 0x76, 0x6a, 0x69, 0x67, 0x61,
+ 0x74, 0x64, 0x66, 0x78, 0x64, 0x72, 0x62, 0x6a, 0x64, 0x65, 0x61, 0x62, 0x63, 0x72, 0x61, 0x66,
+ 0x68, 0x72, 0x78, 0x77, 0x73, 0x6a, 0x72, 0x66, 0x79, 0x6d, 0x70, 0x77, 0x78, 0x64, 0x66, 0x63,
+ 0x73, 0x68, 0x61, 0x75, 0x73, 0x77, 0x63, 0x79, 0x79, 0x78, 0x66, 0x6f, 0x6d, 0x79, 0x67, 0x6f,
+ 0x74, 0x61, 0x66, 0x78, 0x6f, 0x77, 0x6b, 0x77, 0x66, 0x6a, 0x63, 0x61, 0x75, 0x69, 0x77, 0x62,
+ 0x72, 0x77, 0x74, 0x66, 0x67, 0x6b, 0x63, 0x61, 0x71, 0x77, 0x63, 0x75, 0x67, 0x72, 0x67, 0x67,
+ 0x76, 0x73, 0x61, 0x72, 0x72, 0x61, 0x75, 0x6a, 0x68, 0x67, 0x75, 0x64, 0x79, 0x78, 0x6a, 0x61,
+ 0x73, 0x6c, 0x6f, 0x62, 0x64, 0x73, 0x69, 0x79, 0x71, 0x79, 0x65, 0x72, 0x79, 0x6c, 0x6c, 0x6f,
+ 0x71, 0x70, 0x66, 0x72, 0x77, 0x73, 0x62, 0x66, 0x66, 0x78, 0x79, 0x72, 0x71, 0x73, 0x64, 0x68,
+ 0x6b, 0x6e, 0x6f, 0x6e, 0x67, 0x73, 0x6c, 0x6a, 0x74, 0x74, 0x6a, 0x6a, 0x74, 0x64, 0x70, 0x62,
+ 0x79, 0x75, 0x61, 0x6d, 0x6b, 0x70, 0x70, 0x6c, 0x6a, 0x73, 0x62, 0x72, 0x72, 0x76, 0x72, 0x66,
+ 0x78, 0x66, 0x78, 0x78, 0x75, 0x70, 0x6d, 0x6a, 0x70, 0x66, 0x65, 0x6f, 0x72, 0x76, 0x6c, 0x67,
+ 0x76, 0x69, 0x73, 0x74, 0x76, 0x63, 0x76, 0x75, 0x70, 0x76, 0x69, 0x6f, 0x65, 0x70, 0x72, 0x6a,
+ 0x6b, 0x68, 0x6d, 0x6a, 0x71, 0x79, 0x72, 0x6a, 0x74, 0x68, 0x64, 0x72, 0x76, 0x65, 0x6e, 0x63,
+ 0x66, 0x6a, 0x61, 0x66, 0x71, 0x69, 0x62, 0x69, 0x76, 0x66, 0x71, 0x67, 0x73, 0x6f, 0x6c, 0x61,
+ 0x62, 0x63, 0x69, 0x6f, 0x64, 0x70, 0x6e, 0x66, 0x70, 0x68, 0x70, 0x6d, 0x77, 0x66, 0x74, 0x73,
+ 0x64, 0x64, 0x6f, 0x64, 0x68, 0x63, 0x79, 0x72, 0x70, 0x6c, 0x76, 0x64, 0x66, 0x74, 0x70, 0x6b,
+ 0x78, 0x6a, 0x63, 0x6a, 0x61, 0x69, 0x74, 0x61, 0x6b, 0x6a, 0x62, 0x63, 0x71, 0x6b, 0x6e, 0x6f,
+ 0x67, 0x77, 0x61, 0x64, 0x79, 0x73, 0x6c, 0x64, 0x64, 0x64, 0x74, 0x71, 0x6a, 0x73, 0x64, 0x6b,
+ 0x65, 0x6a, 0x79, 0x78, 0x70, 0x6e, 0x72, 0x71, 0x66, 0x79, 0x78, 0x77, 0x76, 0x61, 0x78, 0x69,
+ 0x6f, 0x76, 0x71, 0x70, 0x6b, 0x75, 0x69, 0x75, 0x72, 0x6f, 0x66, 0x65, 0x66, 0x69, 0x79, 0x67,
+ 0x71, 0x76, 0x76, 0x79, 0x71, 0x6b, 0x74, 0x6f, 0x77, 0x61, 0x77, 0x63, 0x71, 0x75, 0x72, 0x73,
+ 0x63, 0x6d, 0x72, 0x6e, 0x73, 0x66, 0x62, 0x70, 0x66, 0x79, 0x77, 0x73, 0x61, 0x69, 0x6a, 0x6a,
+ 0x6c, 0x69, 0x69, 0x69, 0x62, 0x63, 0x63, 0x76, 0x74, 0x6d, 0x6e, 0x72, 0x69, 0x6d, 0x67, 0x76,
+ 0x6a, 0x72, 0x73, 0x61, 0x61, 0x6f, 0x6f, 0x67, 0x78, 0x74, 0x76, 0x77, 0x62, 0x73, 0x64, 0x74,
+ 0x69, 0x76, 0x61, 0x69, 0x65, 0x70, 0x76, 0x73, 0x6f, 0x70, 0x77, 0x73, 0x75, 0x72, 0x68, 0x76,
+ 0x73, 0x62, 0x78, 0x74, 0x78, 0x74, 0x69, 0x71, 0x69, 0x77, 0x6c, 0x68, 0x6d, 0x6a, 0x65, 0x75,
+ 0x70, 0x79, 0x6c, 0x76, 0x62, 0x64, 0x68, 0x62, 0x70, 0x72, 0x6d, 0x65, 0x70, 0x67, 0x65, 0x73,
+ 0x6f, 0x77, 0x66, 0x63, 0x74, 0x77, 0x68, 0x6e, 0x74, 0x6a, 0x76, 0x70, 0x79, 0x77, 0x72, 0x67,
+ 0x65, 0x68, 0x71, 0x66, 0x70, 0x76, 0x67, 0x6e, 0x65, 0x68, 0x77, 0x61, 0x6c, 0x76, 0x76, 0x75,
+ 0x6f, 0x78, 0x77, 0x77, 0x6c, 0x65, 0x73, 0x76, 0x6b, 0x64, 0x70, 0x6f, 0x6a, 0x79, 0x6d, 0x73,
+ 0x6e, 0x64, 0x62, 0x6d, 0x79, 0x63, 0x72, 0x71, 0x68, 0x61, 0x74, 0x62, 0x64, 0x63, 0x72, 0x73,
+ 0x62, 0x79, 0x73, 0x66, 0x66, 0x6e, 0x79, 0x61, 0x70, 0x6b, 0x6a, 0x6e, 0x62, 0x79, 0x6b, 0x64,
+ 0x68, 0x65, 0x66, 0x62, 0x74, 0x63, 0x76, 0x71, 0x73, 0x77, 0x6d, 0x73, 0x71, 0x66, 0x64, 0x6d,
+ 0x71, 0x68, 0x68, 0x77, 0x74, 0x61, 0x79, 0x70, 0x74, 0x66, 0x65, 0x71, 0x74, 0x66, 0x63, 0x73,
+ 0x6c, 0x68, 0x64, 0x75, 0x72, 0x62, 0x72, 0x72, 0x6e, 0x71, 0x6e, 0x69, 0x75, 0x72, 0x71, 0x63,
+ 0x75, 0x65, 0x79, 0x73, 0x65, 0x62, 0x67, 0x62, 0x70, 0x61, 0x62, 0x74, 0x69, 0x6b, 0x6d, 0x6c,
+ 0x65, 0x6a, 0x78, 0x63, 0x72, 0x6f, 0x66, 0x62, 0x77, 0x62, 0x75, 0x69, 0x6f, 0x70, 0x6c, 0x68,
+ 0x6a, 0x78, 0x73, 0x61, 0x66, 0x79, 0x6f, 0x6e, 0x73, 0x67, 0x76, 0x79, 0x64, 0x62, 0x6f, 0x66,
+ 0x63, 0x6b, 0x6c, 0x75, 0x74, 0x77, 0x6a, 0x66, 0x75, 0x63, 0x77, 0x76, 0x6b, 0x6d, 0x69, 0x77,
+ 0x71, 0x61, 0x65, 0x72, 0x66, 0x6d, 0x70, 0x73, 0x68, 0x73, 0x74, 0x77, 0x6e, 0x73, 0x74, 0x76,
+ 0x63, 0x75, 0x72, 0x63, 0x66, 0x6d, 0x67, 0x72, 0x61, 0x69, 0x61, 0x6b, 0x69, 0x74, 0x77, 0x68,
+ 0x74, 0x6e, 0x73, 0x77, 0x66, 0x65, 0x62, 0x6a, 0x6d, 0x66, 0x71, 0x6c, 0x78, 0x79, 0x65, 0x66,
+ 0x62, 0x68, 0x72, 0x61, 0x6e, 0x6a, 0x79, 0x6e, 0x70, 0x79, 0x70, 0x68, 0x71, 0x77, 0x69, 0x6f,
+ 0x63, 0x66, 0x67, 0x77, 0x71, 0x6b, 0x6b, 0x62, 0x71, 0x67, 0x66, 0x6b, 0x65, 0x65, 0x78, 0x75,
+ 0x75, 0x74, 0x75, 0x71, 0x68, 0x72, 0x64, 0x6b, 0x6e, 0x6d, 0x69, 0x62, 0x64, 0x68, 0x73, 0x63,
+ 0x61, 0x74, 0x6e, 0x77, 0x6a, 0x6f, 0x77, 0x74, 0x63, 0x75, 0x6f, 0x70, 0x75, 0x61, 0x67, 0x68,
+ 0x68, 0x72, 0x6b, 0x73, 0x73, 0x66, 0x6f, 0x79, 0x64, 0x77, 0x68, 0x74, 0x71, 0x6d, 0x69, 0x73,
+ 0x71, 0x62, 0x76, 0x77, 0x68, 0x68, 0x79, 0x75, 0x6c, 0x75, 0x79, 0x6e, 0x78, 0x6f, 0x66, 0x71,
+ 0x65, 0x6a, 0x62, 0x67, 0x75, 0x74, 0x6f, 0x64, 0x78, 0x6a, 0x63, 0x73, 0x72, 0x63, 0x68, 0x6b,
+ 0x69, 0x70, 0x6c, 0x73, 0x65, 0x6d, 0x69, 0x63, 0x74, 0x77, 0x6c, 0x6b, 0x78, 0x65, 0x75, 0x6b,
+ 0x6a, 0x77, 0x73, 0x77, 0x77, 0x64, 0x61, 0x69, 0x75, 0x76, 0x77, 0x78, 0x61, 0x6e, 0x71, 0x78,
+ 0x65, 0x77, 0x76, 0x71, 0x70, 0x6d, 0x6d, 0x65, 0x69, 0x71, 0x65, 0x66, 0x74, 0x6c, 0x61, 0x66,
+ 0x6c, 0x62, 0x76, 0x73, 0x77, 0x66, 0x6a, 0x65, 0x79, 0x71, 0x6b, 0x67, 0x68, 0x6e, 0x72, 0x71,
+ 0x73, 0x71, 0x62, 0x71, 0x74, 0x6d, 0x75, 0x6e, 0x6e, 0x6f, 0x75, 0x73, 0x76, 0x6c, 0x68, 0x6e,
+ 0x79, 0x6e, 0x79, 0x74, 0x79, 0x79, 0x79, 0x74, 0x66, 0x77, 0x6d, 0x75, 0x6e, 0x67, 0x68, 0x68,
+ 0x6c, 0x76, 0x68, 0x65, 0x68, 0x6f, 0x76, 0x71, 0x69, 0x68, 0x6f, 0x6c, 0x66, 0x74, 0x6d, 0x73,
+ 0x70, 0x76, 0x61, 0x65, 0x73, 0x62, 0x79, 0x76, 0x6c, 0x69, 0x76, 0x76, 0x79, 0x6a, 0x77, 0x65,
+ 0x68, 0x6d, 0x61, 0x75, 0x6f, 0x6c, 0x66, 0x69, 0x70, 0x73, 0x62, 0x73, 0x6e, 0x72, 0x77, 0x6d,
+ 0x68, 0x67, 0x6d, 0x75, 0x74, 0x67, 0x69, 0x6e, 0x69, 0x65, 0x72, 0x63, 0x64, 0x75, 0x63, 0x63,
+ 0x6a, 0x62, 0x67, 0x74, 0x6b, 0x74, 0x77, 0x73, 0x77, 0x73, 0x65, 0x69, 0x71, 0x63, 0x63, 0x6f,
+ 0x64, 0x63, 0x79, 0x6c, 0x75, 0x74, 0x6b, 0x70, 0x78, 0x62, 0x69, 0x69, 0x6a, 0x72, 0x63, 0x64,
+ 0x62, 0x78, 0x6e, 0x74, 0x6d, 0x66, 0x73, 0x75, 0x77, 0x6c, 0x6a, 0x77, 0x78, 0x62, 0x67, 0x78,
+ 0x70, 0x64, 0x77, 0x6b, 0x6b, 0x77, 0x68, 0x75, 0x62, 0x67, 0x73, 0x76, 0x61, 0x61, 0x71, 0x63,
+ 0x75, 0x70, 0x6a, 0x78, 0x6d, 0x6a, 0x76, 0x72, 0x76, 0x63, 0x66, 0x6c, 0x6e, 0x77, 0x79, 0x6f,
+ 0x69, 0x65, 0x65, 0x65, 0x69, 0x6e, 0x71, 0x79, 0x64, 0x71, 0x72, 0x75, 0x6f, 0x70, 0x61, 0x65,
+ 0x75, 0x6f, 0x70, 0x6b, 0x78, 0x79, 0x6a, 0x6c, 0x78, 0x62, 0x6d, 0x6d, 0x64, 0x62, 0x6e, 0x68,
+ 0x79, 0x64, 0x79, 0x74, 0x61, 0x65, 0x6a, 0x68, 0x6f, 0x62, 0x62, 0x65, 0x73, 0x71, 0x6e, 0x77,
+ 0x76, 0x63, 0x62, 0x74, 0x74, 0x74, 0x6b, 0x6d, 0x79, 0x78, 0x69, 0x75, 0x69, 0x6f, 0x62, 0x6b,
+ 0x6a, 0x75, 0x77, 0x67, 0x71, 0x6d, 0x67, 0x70, 0x6d, 0x65, 0x6b, 0x70, 0x6d, 0x70, 0x74, 0x78,
+ 0x61, 0x68, 0x72, 0x6d, 0x78, 0x79, 0x6d, 0x77, 0x66, 0x68, 0x73, 0x73, 0x64, 0x71, 0x64, 0x64,
+ 0x6e, 0x72, 0x66, 0x74, 0x72, 0x65, 0x75, 0x79, 0x6c, 0x6c, 0x68, 0x72, 0x6c, 0x71, 0x6b, 0x6c,
+ 0x70, 0x61, 0x79, 0x76, 0x72, 0x6a, 0x65, 0x71, 0x72, 0x76, 0x71, 0x6e, 0x74, 0x75, 0x62, 0x64,
+ 0x67, 0x6c, 0x65, 0x78, 0x75, 0x67, 0x6f, 0x79, 0x64, 0x69, 0x66, 0x67, 0x6f, 0x64, 0x62, 0x72,
+ 0x73, 0x79, 0x70, 0x68, 0x63, 0x61, 0x64, 0x6a, 0x6f, 0x6d, 0x75, 0x6e, 0x63, 0x61, 0x74, 0x6e,
+ 0x6f, 0x74, 0x61, 0x6e, 0x6c, 0x62, 0x6f, 0x64, 0x62, 0x76, 0x6c, 0x74, 0x68, 0x79, 0x70, 0x6a,
+ 0x68, 0x71, 0x71, 0x6b, 0x6c, 0x66, 0x67, 0x6e, 0x61, 0x68, 0x6a, 0x6f, 0x65, 0x77, 0x64, 0x70,
+ 0x6c, 0x65, 0x63, 0x66, 0x6b, 0x65, 0x63, 0x62, 0x79, 0x6f, 0x79, 0x63, 0x77, 0x70, 0x61, 0x67,
+ 0x65, 0x73, 0x67, 0x6e, 0x74, 0x6c, 0x67, 0x6e, 0x64, 0x71, 0x75, 0x61, 0x70, 0x70, 0x6c, 0x68,
+ 0x64, 0x65, 0x63, 0x6c, 0x6e, 0x68, 0x6c, 0x67, 0x70, 0x64, 0x63, 0x65, 0x76, 0x78, 0x78, 0x78,
+ 0x68, 0x65, 0x61, 0x79, 0x77, 0x6d, 0x72, 0x72, 0x6f, 0x79, 0x67, 0x78, 0x6d, 0x6d, 0x67, 0x71,
+ 0x75, 0x70, 0x72, 0x69, 0x68, 0x75, 0x6f, 0x70, 0x63, 0x66, 0x65, 0x69, 0x75, 0x64, 0x75, 0x72,
+ 0x6b, 0x67, 0x75, 0x6b, 0x67, 0x6f, 0x6d, 0x65, 0x6d, 0x63, 0x73, 0x79, 0x61, 0x6e, 0x65, 0x69,
+ 0x64, 0x6c, 0x73, 0x75, 0x69, 0x66, 0x79, 0x65, 0x61, 0x68, 0x76, 0x70, 0x62, 0x76, 0x78, 0x72,
+ 0x6b, 0x74, 0x73, 0x6b, 0x66, 0x63, 0x6c, 0x79, 0x68, 0x72, 0x78, 0x6a, 0x62, 0x70, 0x73, 0x6a,
+ 0x68, 0x73, 0x67, 0x67, 0x64, 0x70, 0x77, 0x69, 0x71, 0x65, 0x6f, 0x6c, 0x68, 0x61, 0x6f, 0x64,
+ 0x6c, 0x68, 0x6d, 0x64, 0x79, 0x74, 0x78, 0x63, 0x68, 0x62, 0x64, 0x61, 0x70, 0x78, 0x6e, 0x74,
+ 0x6b, 0x71, 0x67, 0x73, 0x71, 0x73, 0x62, 0x61, 0x64, 0x6a, 0x6c, 0x6e, 0x77, 0x70, 0x66, 0x65,
+ 0x6b, 0x64, 0x66, 0x6d, 0x6d, 0x6a, 0x69, 0x65, 0x65, 0x71, 0x66, 0x62, 0x6f, 0x74, 0x6c, 0x6e,
+ 0x65, 0x73, 0x75, 0x6f, 0x78, 0x62, 0x68, 0x72, 0x65, 0x6c, 0x6f, 0x6a, 0x70, 0x77, 0x64, 0x6b,
+ 0x6d, 0x73, 0x71, 0x68, 0x6b, 0x61, 0x6b, 0x69, 0x73, 0x65, 0x6d, 0x61, 0x70, 0x79, 0x6f, 0x76,
+ 0x62, 0x67, 0x78, 0x61, 0x6d, 0x63, 0x6f, 0x70, 0x66, 0x6e, 0x65, 0x65, 0x62, 0x62, 0x6b, 0x73,
+ 0x64, 0x6c, 0x78, 0x71, 0x62, 0x76, 0x65, 0x6e, 0x71, 0x73, 0x6e, 0x75, 0x6d, 0x71, 0x6e, 0x75,
+ 0x73, 0x6a, 0x74, 0x75, 0x6d, 0x68, 0x6f, 0x69, 0x6a, 0x73, 0x6b, 0x75, 0x70, 0x75, 0x69, 0x6a,
+ 0x71, 0x6a, 0x73, 0x71, 0x6c, 0x66, 0x70, 0x63, 0x75, 0x6d, 0x6f, 0x65, 0x71, 0x75, 0x77, 0x61,
+ 0x63, 0x78, 0x6f, 0x78, 0x6c, 0x69, 0x76, 0x6b, 0x65, 0x79, 0x67, 0x74, 0x77, 0x65, 0x64, 0x6f,
+ 0x74, 0x79, 0x6f, 0x74, 0x6e, 0x74, 0x66, 0x6e, 0x69, 0x70, 0x77, 0x68, 0x78, 0x73, 0x66, 0x6a,
+ 0x65, 0x6f, 0x6b, 0x64, 0x6c, 0x73, 0x75, 0x64, 0x64, 0x6b, 0x68, 0x62, 0x77, 0x72, 0x75, 0x76,
+ 0x75, 0x69, 0x63, 0x6b, 0x65, 0x74, 0x64, 0x77, 0x62, 0x65, 0x66, 0x65, 0x6a, 0x71, 0x69, 0x71,
+ 0x78, 0x65, 0x77, 0x77, 0x79, 0x66, 0x67, 0x72, 0x6d, 0x6c, 0x68, 0x6a, 0x63, 0x79, 0x79, 0x72,
+ 0x73, 0x62, 0x62, 0x77, 0x6e, 0x6c, 0x79, 0x75, 0x6f, 0x78, 0x68, 0x6a, 0x64, 0x6c, 0x62, 0x67,
+ 0x65, 0x6c, 0x65, 0x64, 0x65, 0x70, 0x75, 0x68, 0x64, 0x72, 0x71, 0x62, 0x66, 0x74, 0x70, 0x77,
+ 0x63, 0x6d, 0x76, 0x63, 0x78, 0x64, 0x76, 0x75, 0x72, 0x6e, 0x65, 0x68, 0x71, 0x78, 0x73, 0x62,
+ 0x77, 0x6c, 0x6f, 0x63, 0x74, 0x64, 0x71, 0x62, 0x79, 0x78, 0x70, 0x6a, 0x69, 0x6c, 0x73, 0x68,
+ 0x6c, 0x6a, 0x6b, 0x6f, 0x73, 0x6a, 0x6e, 0x62, 0x73, 0x66, 0x6f, 0x67, 0x76, 0x74, 0x6c, 0x78,
+ 0x63, 0x6c, 0x76, 0x77, 0x6c, 0x6c, 0x74, 0x71, 0x6b, 0x66, 0x66, 0x63, 0x61, 0x68, 0x6c, 0x76,
+ 0x68, 0x72, 0x72, 0x6c, 0x6b, 0x61, 0x6f, 0x6c, 0x72, 0x6b, 0x6b, 0x78, 0x77, 0x78, 0x61, 0x68,
+ 0x63, 0x66, 0x6a, 0x68, 0x78, 0x74, 0x71, 0x68, 0x64, 0x69, 0x6d, 0x75, 0x72, 0x6d, 0x78, 0x6c,
+ 0x6a, 0x68, 0x61, 0x74, 0x69, 0x64, 0x67, 0x71, 0x62, 0x67, 0x6b, 0x6f, 0x6e, 0x63, 0x64, 0x73,
+ 0x72, 0x6c, 0x6c, 0x78, 0x64, 0x70, 0x76, 0x79, 0x69, 0x61, 0x76, 0x6d, 0x72, 0x65, 0x64, 0x75,
+ 0x64, 0x77, 0x70, 0x64, 0x71, 0x73, 0x77, 0x75, 0x61, 0x6b, 0x76, 0x6e, 0x62, 0x70, 0x75, 0x69,
+ 0x6e, 0x73, 0x74, 0x66, 0x6d, 0x6c, 0x6b, 0x70, 0x67, 0x66, 0x6f, 0x75, 0x65, 0x69, 0x78, 0x67,
+ 0x68, 0x78, 0x67, 0x6b, 0x6a, 0x68, 0x72, 0x71, 0x72, 0x71, 0x77, 0x6c, 0x74, 0x64, 0x79, 0x74,
+ 0x73, 0x6d, 0x73, 0x6f, 0x79, 0x62, 0x79, 0x71, 0x6c, 0x73, 0x73, 0x6a, 0x67, 0x6e, 0x72, 0x69,
+ 0x75, 0x71, 0x62, 0x72, 0x65, 0x63, 0x6b, 0x63, 0x79, 0x76, 0x74, 0x79, 0x79, 0x67, 0x77, 0x79,
+ 0x66, 0x64, 0x72, 0x73, 0x61, 0x6f, 0x62, 0x72, 0x76, 0x66, 0x62, 0x76, 0x73, 0x68, 0x67, 0x63,
+ 0x72, 0x64, 0x6b, 0x74, 0x6c, 0x6b, 0x78, 0x6f, 0x64, 0x6a, 0x69, 0x76, 0x67, 0x75, 0x73, 0x70,
+ 0x78, 0x73, 0x75, 0x67, 0x71, 0x71, 0x74, 0x65, 0x65, 0x6d, 0x70, 0x6e, 0x74, 0x64, 0x77, 0x68,
+ 0x78, 0x72, 0x6e, 0x71, 0x73, 0x70, 0x6f, 0x6a, 0x6a, 0x6f, 0x69, 0x70, 0x63, 0x6b, 0x71, 0x6e,
+ 0x77, 0x79, 0x6c, 0x6e, 0x73, 0x6f, 0x63, 0x71, 0x67, 0x65, 0x77, 0x6d, 0x75, 0x6d, 0x64, 0x67,
+ 0x64, 0x65, 0x73, 0x73, 0x64, 0x74, 0x61, 0x66, 0x73, 0x69, 0x69, 0x65, 0x68, 0x66, 0x6e, 0x67,
+ 0x69, 0x6d, 0x66, 0x65, 0x78, 0x79, 0x72, 0x61, 0x6b, 0x68, 0x64, 0x75, 0x67, 0x67, 0x71, 0x76,
+ 0x76, 0x75, 0x79, 0x71, 0x62, 0x76, 0x61, 0x77, 0x71, 0x76, 0x76, 0x67, 0x70, 0x69, 0x62, 0x6e,
+ 0x6e, 0x65, 0x65, 0x67, 0x75, 0x6a, 0x70, 0x70, 0x71, 0x68, 0x71, 0x6b, 0x61, 0x6c, 0x74, 0x6a,
+ 0x6b, 0x63, 0x70, 0x72, 0x6c, 0x73, 0x79, 0x77, 0x74, 0x67, 0x71, 0x72, 0x65, 0x61, 0x72, 0x67,
+ 0x66, 0x6d, 0x69, 0x65, 0x75, 0x61, 0x6d, 0x69, 0x77, 0x6c, 0x76, 0x71, 0x64, 0x6f, 0x79, 0x69,
+ 0x77, 0x6a, 0x68, 0x73, 0x65, 0x6a, 0x75, 0x6f, 0x75, 0x6a, 0x63, 0x67, 0x6c, 0x6d, 0x78, 0x74,
+ 0x74, 0x75, 0x72, 0x6a, 0x64, 0x74, 0x72, 0x75, 0x63, 0x6f, 0x72, 0x6f, 0x6b, 0x65, 0x64, 0x76,
+ 0x79, 0x6d, 0x78, 0x71, 0x61, 0x61, 0x79, 0x6d, 0x77, 0x79, 0x68, 0x74, 0x69, 0x6e, 0x67, 0x72,
+ 0x74, 0x70, 0x64, 0x72, 0x6a, 0x78, 0x6c, 0x61, 0x6d, 0x71, 0x6b, 0x6a, 0x64, 0x70, 0x62, 0x6a,
+ 0x67, 0x69, 0x64, 0x6e, 0x6b, 0x62, 0x61, 0x6a, 0x74, 0x61, 0x69, 0x78, 0x75, 0x78, 0x72, 0x69,
+ 0x6d, 0x6e, 0x79, 0x63, 0x70, 0x68, 0x78, 0x6d, 0x79, 0x64, 0x63, 0x6e, 0x62, 0x6e, 0x65, 0x6f,
+ 0x6c, 0x79, 0x71, 0x70, 0x6a, 0x68, 0x70, 0x75, 0x6e, 0x64, 0x72, 0x79, 0x67, 0x77, 0x63, 0x68,
+ 0x72, 0x79, 0x65, 0x78, 0x64, 0x77, 0x6e, 0x6d, 0x62, 0x71, 0x79, 0x75, 0x6b, 0x63, 0x72, 0x71,
+ 0x65, 0x69, 0x6c, 0x68, 0x66, 0x62, 0x61, 0x79, 0x6c, 0x67, 0x61, 0x78, 0x76, 0x6c, 0x6d, 0x75,
+ 0x73, 0x6d, 0x6a, 0x6c, 0x6e, 0x66, 0x70, 0x61, 0x6f, 0x6e, 0x6b, 0x70, 0x69, 0x6b, 0x61, 0x75,
+ 0x6d, 0x73, 0x6b, 0x61, 0x63, 0x72, 0x73, 0x65, 0x6a, 0x77, 0x6b, 0x78, 0x68, 0x6e, 0x6f, 0x78,
+ 0x69, 0x6b, 0x67, 0x63, 0x73, 0x76, 0x6f, 0x67, 0x63, 0x71, 0x6d, 0x67, 0x62, 0x6e, 0x76, 0x71,
+ 0x71, 0x78, 0x78, 0x68, 0x78, 0x63, 0x65, 0x72, 0x6f, 0x6b, 0x75, 0x6a, 0x76, 0x6d, 0x79, 0x68,
+ 0x75, 0x78, 0x77, 0x75, 0x6d, 0x70, 0x6e, 0x71, 0x76, 0x77, 0x76, 0x76, 0x70, 0x77, 0x72, 0x61,
+ 0x6a, 0x62, 0x71, 0x67, 0x67, 0x73, 0x71, 0x75, 0x6a, 0x73, 0x66, 0x6e, 0x64, 0x76, 0x6a, 0x65,
+ 0x76, 0x66, 0x6e, 0x68, 0x67, 0x78, 0x79, 0x66, 0x65, 0x6d, 0x6d, 0x71, 0x73, 0x66, 0x63, 0x79,
+ 0x6c, 0x72, 0x6a, 0x6f, 0x78, 0x76, 0x6d, 0x78, 0x63, 0x6b, 0x67, 0x70, 0x78, 0x76, 0x61, 0x62,
+ 0x6e, 0x76, 0x75, 0x76, 0x74, 0x70, 0x75, 0x61, 0x77, 0x65, 0x75, 0x6b, 0x65, 0x74, 0x76, 0x78,
+ 0x66, 0x6f, 0x6a, 0x72, 0x72, 0x70, 0x66, 0x70, 0x63, 0x73, 0x78, 0x6e, 0x74, 0x77, 0x78, 0x68,
+ 0x61, 0x6a, 0x61, 0x75, 0x74, 0x75, 0x6b, 0x62, 0x6c, 0x6a, 0x72, 0x79, 0x73, 0x79, 0x6b, 0x6f,
+ 0x77, 0x6b, 0x75, 0x74, 0x76, 0x74, 0x64, 0x79, 0x64, 0x74, 0x64, 0x77, 0x71, 0x6b, 0x75, 0x72,
+ 0x66, 0x75, 0x61, 0x79, 0x78, 0x6a, 0x69, 0x6a, 0x68, 0x6a, 0x6c, 0x77, 0x68, 0x67, 0x77, 0x73,
+ 0x76, 0x6e, 0x65, 0x6d, 0x6f, 0x6a, 0x6e, 0x61, 0x6e, 0x6c, 0x74, 0x6b, 0x70, 0x70, 0x75, 0x66,
+ 0x70, 0x72, 0x76, 0x69, 0x77, 0x6b, 0x76, 0x68, 0x6e, 0x6b, 0x77, 0x64, 0x76, 0x61, 0x6b, 0x6d,
+ 0x77, 0x6f, 0x6a, 0x6a, 0x62, 0x6b, 0x79, 0x73, 0x6a, 0x6a, 0x76, 0x62, 0x64, 0x69, 0x6e, 0x6a,
+ 0x6e, 0x6a, 0x6b, 0x75, 0x6f, 0x61, 0x72, 0x6d, 0x79, 0x66, 0x71, 0x77, 0x6e, 0x72, 0x68, 0x74,
+ 0x69, 0x6d, 0x6b, 0x70, 0x65, 0x77, 0x65, 0x64, 0x66, 0x77, 0x6b, 0x71, 0x6c, 0x66, 0x70, 0x75,
+ 0x63, 0x63, 0x62, 0x65, 0x64, 0x65, 0x75, 0x6a, 0x6b, 0x67, 0x69, 0x64, 0x77, 0x71, 0x65, 0x70,
+ 0x78, 0x67, 0x63, 0x62, 0x79, 0x74, 0x75, 0x73, 0x72, 0x70, 0x63, 0x72, 0x77, 0x66, 0x6f, 0x73,
+ 0x66, 0x6e, 0x61, 0x69, 0x62, 0x75, 0x79, 0x71, 0x68, 0x77, 0x6f, 0x79, 0x67, 0x76, 0x62, 0x67,
+ 0x78, 0x63, 0x70, 0x67, 0x62, 0x69, 0x6b, 0x75, 0x78, 0x77, 0x6d, 0x71, 0x78, 0x63, 0x77, 0x78,
+ 0x61, 0x61, 0x75, 0x74, 0x6b, 0x77, 0x62, 0x71, 0x70, 0x74, 0x6d, 0x6e, 0x62, 0x71, 0x73, 0x71,
+ 0x78, 0x76, 0x79, 0x68, 0x61, 0x73, 0x78, 0x61, 0x77, 0x77, 0x62, 0x67, 0x77, 0x6b, 0x6d, 0x69,
+ 0x77, 0x6a, 0x6a, 0x74, 0x69, 0x64, 0x62, 0x70, 0x6c, 0x6e, 0x75, 0x71, 0x6e, 0x66, 0x72, 0x76,
+ 0x63, 0x63, 0x61, 0x6b, 0x74, 0x62, 0x68, 0x61, 0x70, 0x69, 0x75, 0x64, 0x6a, 0x6b, 0x68, 0x73,
+ 0x6f, 0x6b, 0x67, 0x63, 0x6a, 0x79, 0x79, 0x65, 0x78, 0x78, 0x78, 0x67, 0x73, 0x6d, 0x63, 0x79,
+ 0x6c, 0x75, 0x6e, 0x65, 0x79, 0x66, 0x6d, 0x63, 0x76, 0x6a, 0x6b, 0x67, 0x71, 0x67, 0x69, 0x61,
+ 0x77, 0x71, 0x75, 0x76, 0x76, 0x70, 0x74, 0x6d, 0x69, 0x67, 0x61, 0x66, 0x64, 0x6b, 0x73, 0x66,
+ 0x66, 0x70, 0x65, 0x77, 0x6d, 0x70, 0x6f, 0x61, 0x71, 0x6e, 0x76, 0x69, 0x6a, 0x68, 0x70, 0x71,
+ 0x70, 0x66, 0x6e, 0x6f, 0x70, 0x72, 0x64, 0x78, 0x6f, 0x75, 0x66, 0x69, 0x73, 0x68, 0x71, 0x77,
+ 0x6b, 0x69, 0x73, 0x66, 0x79, 0x6d, 0x64, 0x69, 0x74, 0x66, 0x73, 0x64, 0x6e, 0x63, 0x6b, 0x75,
+ 0x70, 0x70, 0x64, 0x72, 0x71, 0x79, 0x74, 0x71, 0x73, 0x6f, 0x6a, 0x72, 0x6d, 0x72, 0x78, 0x63,
+ 0x73, 0x61, 0x70, 0x6d, 0x69, 0x76, 0x73, 0x64, 0x6c, 0x73, 0x69, 0x61, 0x78, 0x61, 0x78, 0x78,
+ 0x67, 0x75, 0x6a, 0x78, 0x75, 0x6e, 0x6f, 0x61, 0x6e, 0x62, 0x75, 0x74, 0x77, 0x6a, 0x68, 0x70,
+ 0x67, 0x65, 0x72, 0x6c, 0x6e, 0x75, 0x79, 0x6a, 0x72, 0x6b, 0x6b, 0x63, 0x79, 0x77, 0x74, 0x73,
+ 0x6a, 0x6c, 0x6a, 0x69, 0x6b, 0x66, 0x6b, 0x61, 0x6a, 0x72, 0x66, 0x67, 0x73, 0x6c, 0x67, 0x6d,
+ 0x6c, 0x69, 0x6a, 0x6d, 0x6f, 0x64, 0x78, 0x6f, 0x6c, 0x75, 0x74, 0x6c, 0x74, 0x74, 0x64, 0x72,
+ 0x76, 0x6f, 0x62, 0x63, 0x77, 0x66, 0x69, 0x74, 0x70, 0x6e, 0x72, 0x75, 0x6c, 0x77, 0x61, 0x79,
+ 0x78, 0x72, 0x62, 0x75, 0x75, 0x6c, 0x6a, 0x6c, 0x69, 0x70, 0x76, 0x63, 0x6a, 0x74, 0x75, 0x6c,
+ 0x77, 0x6c, 0x75, 0x72, 0x68, 0x79, 0x6b, 0x62, 0x6a, 0x64, 0x63, 0x6b, 0x70, 0x6e, 0x6d, 0x6b,
+ 0x78, 0x6e, 0x77, 0x6c, 0x61, 0x6d, 0x66, 0x6e, 0x62, 0x77, 0x75, 0x76, 0x6c, 0x72, 0x73, 0x6c,
+ 0x61, 0x63, 0x77, 0x75, 0x64, 0x70, 0x76, 0x72, 0x6a, 0x63, 0x62, 0x6d, 0x72, 0x71, 0x62, 0x6e,
+ 0x76, 0x70, 0x66, 0x68, 0x63, 0x68, 0x69, 0x66, 0x6c, 0x72, 0x74, 0x6c, 0x78, 0x74, 0x64, 0x64,
+ 0x74, 0x63, 0x6a, 0x6d, 0x69, 0x6a, 0x74, 0x78, 0x66, 0x68, 0x63, 0x66, 0x76, 0x63, 0x74, 0x67,
+ 0x6e, 0x74, 0x69, 0x66, 0x69, 0x75, 0x63, 0x65, 0x64, 0x62, 0x6f, 0x71, 0x6c, 0x62, 0x79, 0x61,
+ 0x71, 0x6d, 0x76, 0x6e, 0x63, 0x62, 0x66, 0x61, 0x6d, 0x65, 0x74, 0x6e, 0x6a, 0x78, 0x61, 0x73,
+ 0x6e, 0x62, 0x78, 0x72, 0x65, 0x6b, 0x6a, 0x76, 0x6b, 0x6f, 0x70, 0x6a, 0x65, 0x72, 0x6c, 0x61,
+ 0x64, 0x61, 0x64, 0x69, 0x61, 0x6f, 0x6f, 0x66, 0x6a, 0x70, 0x63, 0x6e, 0x77, 0x6b, 0x77, 0x63,
+ 0x74, 0x62, 0x79, 0x6e, 0x77, 0x6b, 0x70, 0x6d, 0x67, 0x72, 0x66, 0x71, 0x72, 0x6a, 0x6f, 0x73,
+ 0x77, 0x71, 0x77, 0x62, 0x75, 0x78, 0x6b, 0x6a, 0x63, 0x6b, 0x78, 0x67, 0x66, 0x66, 0x61, 0x77,
+ 0x79, 0x65, 0x66, 0x64, 0x73, 0x71, 0x72, 0x77, 0x73, 0x77, 0x77, 0x75, 0x79, 0x73, 0x75, 0x65,
+ 0x75, 0x65, 0x6d, 0x74, 0x75, 0x65, 0x74, 0x67, 0x69, 0x70, 0x6c, 0x6d, 0x74, 0x73, 0x68, 0x6f,
+ 0x61, 0x6c, 0x76, 0x65, 0x6d, 0x6c, 0x6d, 0x64, 0x64, 0x61, 0x73, 0x6f, 0x68, 0x77, 0x6e, 0x6c,
+ 0x6a, 0x77, 0x73, 0x77, 0x64, 0x69, 0x79, 0x6d, 0x75, 0x65, 0x6f, 0x6a, 0x72, 0x68, 0x75, 0x6f,
+ 0x6a, 0x77, 0x69, 0x6c, 0x61, 0x70, 0x69, 0x64, 0x78, 0x73, 0x6e, 0x70, 0x74, 0x75, 0x63, 0x71,
+ 0x77, 0x78, 0x73, 0x69, 0x66, 0x73, 0x73, 0x6a, 0x6e, 0x66, 0x65, 0x67, 0x70, 0x63, 0x6b, 0x6f,
+ 0x76, 0x77, 0x75, 0x74, 0x6d, 0x77, 0x74, 0x6c, 0x74, 0x69, 0x78, 0x6d, 0x6c, 0x75, 0x75, 0x66,
+ 0x76, 0x69, 0x71, 0x69, 0x73, 0x66, 0x78, 0x6e, 0x69, 0x78, 0x69, 0x69, 0x62, 0x71, 0x70, 0x69,
+ 0x70, 0x78, 0x64, 0x6d, 0x67, 0x70, 0x62, 0x67, 0x64, 0x65, 0x71, 0x77, 0x6b, 0x72, 0x6a, 0x71,
+ 0x70, 0x68, 0x70, 0x62, 0x71, 0x71, 0x74, 0x70, 0x73, 0x6d, 0x70, 0x64, 0x67, 0x76, 0x69, 0x74,
+ 0x70, 0x64, 0x72, 0x6a, 0x70, 0x6d, 0x66, 0x69, 0x74, 0x62, 0x66, 0x6a, 0x72, 0x69, 0x62, 0x74,
+ 0x71, 0x77, 0x6e, 0x72, 0x6b, 0x73, 0x66, 0x71, 0x74, 0x6a, 0x6c, 0x68, 0x6b, 0x6a, 0x68, 0x6c,
+ 0x6c, 0x69, 0x6d, 0x67, 0x76, 0x78, 0x61, 0x78, 0x6b, 0x6a, 0x65, 0x74, 0x75, 0x6d, 0x74, 0x71,
+ 0x79, 0x61, 0x6a, 0x75, 0x75, 0x6c, 0x74, 0x6f, 0x72, 0x6f, 0x74, 0x65, 0x75, 0x76, 0x62, 0x74,
+ 0x6f, 0x64, 0x62, 0x6a, 0x67, 0x62, 0x68, 0x6f, 0x6b, 0x75, 0x6a, 0x6c, 0x77, 0x69, 0x78, 0x66,
+ 0x6e, 0x68, 0x70, 0x66, 0x68, 0x77, 0x74, 0x67, 0x6b, 0x68, 0x63, 0x69, 0x76, 0x62, 0x6e, 0x69,
+ 0x64, 0x6d, 0x70, 0x63, 0x6a, 0x78, 0x67, 0x64, 0x69, 0x70, 0x65, 0x6c, 0x61, 0x67, 0x6a, 0x64,
+ 0x67, 0x61, 0x62, 0x76, 0x69, 0x77, 0x70, 0x70, 0x6a, 0x78, 0x70, 0x68, 0x78, 0x65, 0x64, 0x61,
+ 0x69, 0x6e, 0x76, 0x62, 0x64, 0x66, 0x6a, 0x79, 0x63, 0x78, 0x6b, 0x71, 0x78, 0x77, 0x6d, 0x6b,
+ 0x75, 0x72, 0x6f, 0x67, 0x75, 0x69, 0x72, 0x76, 0x66, 0x79, 0x63, 0x77, 0x74, 0x6d, 0x67, 0x6c,
+ 0x70, 0x75, 0x71, 0x78, 0x6f, 0x6d, 0x73, 0x75, 0x64, 0x78, 0x6b, 0x77, 0x6b, 0x77, 0x6f, 0x79,
+ 0x78, 0x6f, 0x73, 0x65, 0x62, 0x61, 0x79, 0x75, 0x74, 0x66, 0x71, 0x74, 0x6e, 0x6b, 0x68, 0x61,
+ 0x74, 0x62, 0x75, 0x65, 0x76, 0x78, 0x6b, 0x75, 0x6b, 0x69, 0x70, 0x74, 0x75, 0x74, 0x6e, 0x6b,
+ 0x68, 0x62, 0x61, 0x65, 0x6b, 0x77, 0x63, 0x66, 0x61, 0x62, 0x6a, 0x73, 0x68, 0x63, 0x61, 0x79,
+ 0x68, 0x70, 0x74, 0x70, 0x73, 0x6d, 0x67, 0x71, 0x6b, 0x73, 0x63, 0x6d, 0x61, 0x6b, 0x69, 0x69,
+ 0x72, 0x65, 0x61, 0x6e, 0x61, 0x78, 0x67, 0x6a, 0x73, 0x67, 0x6a, 0x6c, 0x66, 0x74, 0x6b, 0x75,
+ 0x6a, 0x6b, 0x66, 0x69, 0x68, 0x63, 0x73, 0x65, 0x6b, 0x62, 0x77, 0x68, 0x73, 0x72, 0x6a, 0x67,
+ 0x6a, 0x63, 0x6a, 0x70, 0x77, 0x72, 0x69, 0x69, 0x6d, 0x6b, 0x70, 0x62, 0x79, 0x74, 0x63, 0x74,
+ 0x6d, 0x6c, 0x79, 0x79, 0x76, 0x6e, 0x74, 0x79, 0x6b, 0x6a, 0x70, 0x6b, 0x6f, 0x78, 0x6f, 0x72,
+ 0x77, 0x69, 0x71, 0x62, 0x61, 0x6a, 0x72, 0x6b, 0x6e, 0x78, 0x6e, 0x65, 0x75, 0x61, 0x70, 0x70,
+ 0x71, 0x61, 0x66, 0x6d, 0x65, 0x6c, 0x75, 0x71, 0x70, 0x6e, 0x63, 0x6a, 0x69, 0x61, 0x62, 0x72,
+ 0x77, 0x79, 0x6c, 0x65, 0x70, 0x6d, 0x75, 0x78, 0x6f, 0x78, 0x6f, 0x73, 0x6c, 0x78, 0x61, 0x75,
+ 0x73, 0x63, 0x65, 0x6b, 0x78, 0x61, 0x6d, 0x67, 0x69, 0x71, 0x6e, 0x76, 0x6a, 0x70, 0x70, 0x6b,
+ 0x68, 0x78, 0x73, 0x75, 0x74, 0x70, 0x72, 0x70, 0x61, 0x67, 0x63, 0x78, 0x78, 0x61, 0x68, 0x76,
+ 0x77, 0x64, 0x6d, 0x66, 0x71, 0x69, 0x71, 0x61, 0x6a, 0x6f, 0x6c, 0x6c, 0x62, 0x6a, 0x77, 0x77,
+ 0x77, 0x70, 0x6b, 0x78, 0x68, 0x61, 0x67, 0x6d, 0x72, 0x6b, 0x73, 0x75, 0x70, 0x67, 0x70, 0x73,
+ 0x76, 0x6a, 0x74, 0x76, 0x79, 0x67, 0x6d, 0x61, 0x67, 0x73, 0x6c, 0x6a, 0x79, 0x64, 0x61, 0x76,
+ 0x68, 0x75, 0x76, 0x69, 0x75, 0x72, 0x6e, 0x63, 0x62, 0x63, 0x6c, 0x76, 0x78, 0x6b, 0x79, 0x78,
+ 0x79, 0x79, 0x68, 0x74, 0x6a, 0x71, 0x6c, 0x73, 0x69, 0x69, 0x6c, 0x61, 0x70, 0x74, 0x66, 0x65,
+ 0x71, 0x67, 0x75, 0x6b, 0x66, 0x69, 0x61, 0x72, 0x72, 0x6e, 0x6e, 0x6c, 0x64, 0x65, 0x79, 0x68,
+ 0x6d, 0x66, 0x63, 0x71, 0x6b, 0x73, 0x62, 0x6d, 0x68, 0x72, 0x70, 0x6c, 0x68, 0x64, 0x76, 0x61,
+ 0x77, 0x6a, 0x61, 0x6c, 0x6b, 0x69, 0x63, 0x77, 0x6f, 0x78, 0x63, 0x64, 0x62, 0x63, 0x74, 0x62,
+ 0x69, 0x79, 0x6f, 0x75, 0x6a, 0x6f, 0x73, 0x6a, 0x66, 0x62, 0x6a, 0x6a, 0x76, 0x65, 0x69, 0x6d,
+ 0x78, 0x68, 0x6d, 0x67, 0x78, 0x76, 0x78, 0x73, 0x6e, 0x6f, 0x72, 0x73, 0x72, 0x77, 0x6c, 0x64,
+ 0x6e, 0x74, 0x62, 0x65, 0x61, 0x64, 0x72, 0x75, 0x6a, 0x64, 0x63, 0x67, 0x61, 0x6f, 0x76, 0x76,
+ 0x68, 0x69, 0x73, 0x75, 0x6b, 0x6d, 0x65, 0x69, 0x6d, 0x6a, 0x77, 0x6e, 0x77, 0x67, 0x6a, 0x6b,
+ 0x6d, 0x77, 0x73, 0x62, 0x63, 0x73, 0x6c, 0x62, 0x71, 0x6c, 0x6f, 0x73, 0x6d, 0x61, 0x77, 0x76,
+ 0x64, 0x66, 0x79, 0x6a, 0x6f, 0x76, 0x67, 0x6c, 0x6c, 0x6f, 0x76, 0x72, 0x77, 0x72, 0x6d, 0x64,
+ 0x6b, 0x76, 0x74, 0x69, 0x62, 0x79, 0x63, 0x6b, 0x64, 0x6a, 0x63, 0x75, 0x6d, 0x76, 0x76, 0x62,
+ 0x69, 0x6b, 0x71, 0x65, 0x6c, 0x79, 0x6c, 0x76, 0x64, 0x70, 0x68, 0x6a, 0x6d, 0x75, 0x6e, 0x73,
+ 0x68, 0x77, 0x66, 0x6a, 0x73, 0x61, 0x72, 0x67, 0x6d, 0x63, 0x6a, 0x77, 0x64, 0x61, 0x6b, 0x68,
+ 0x6c, 0x6a, 0x64, 0x73, 0x68, 0x77, 0x67, 0x71, 0x74, 0x72, 0x72, 0x6a, 0x67, 0x66, 0x6d, 0x69,
+ 0x77, 0x72, 0x67, 0x75, 0x76, 0x65, 0x6e, 0x79, 0x6b, 0x71, 0x6c, 0x79, 0x6a, 0x64, 0x66, 0x6a,
+ 0x6f, 0x6c, 0x75, 0x71, 0x71, 0x6e, 0x75, 0x79, 0x68, 0x79, 0x63, 0x71, 0x69, 0x67, 0x69, 0x70,
+ 0x70, 0x67, 0x68, 0x71, 0x68, 0x76, 0x68, 0x78, 0x62, 0x6e, 0x79, 0x73, 0x77, 0x73, 0x66, 0x6c,
+ 0x63, 0x74, 0x71, 0x66, 0x70, 0x6b, 0x75, 0x6d, 0x79, 0x64, 0x77, 0x64, 0x6c, 0x68, 0x62, 0x79,
+ 0x78, 0x62, 0x6f, 0x76, 0x64, 0x62, 0x69, 0x62, 0x77, 0x61, 0x70, 0x67, 0x79, 0x73, 0x66, 0x6d,
+ 0x6d, 0x62, 0x77, 0x70, 0x66, 0x61, 0x79, 0x66, 0x67, 0x65, 0x78, 0x73, 0x65, 0x70, 0x79, 0x71,
+ 0x75, 0x71, 0x77, 0x77, 0x68, 0x6e, 0x6a, 0x6b, 0x72, 0x64, 0x68, 0x67, 0x64, 0x66, 0x6c, 0x62,
+ 0x75, 0x68, 0x64, 0x6f, 0x68, 0x73, 0x76, 0x61, 0x6e, 0x61, 0x77, 0x6f, 0x71, 0x62, 0x62, 0x65,
+ 0x6f, 0x68, 0x6b, 0x76, 0x6c, 0x67, 0x65, 0x6c, 0x74, 0x65, 0x79, 0x66, 0x72, 0x76, 0x74, 0x6f,
+ 0x63, 0x69, 0x6d, 0x6d, 0x78, 0x6c, 0x6d, 0x65, 0x6a, 0x63, 0x72, 0x65, 0x62, 0x6b, 0x66, 0x64,
+ 0x6d, 0x79, 0x72, 0x76, 0x74, 0x6d, 0x6a, 0x66, 0x67, 0x67, 0x61, 0x67, 0x6f, 0x64, 0x76, 0x79,
+ 0x76, 0x69, 0x6e, 0x66, 0x79, 0x73, 0x69, 0x65, 0x76, 0x6a, 0x6f, 0x66, 0x68, 0x64, 0x72, 0x6c,
+ 0x67, 0x6c, 0x79, 0x70, 0x6e, 0x72, 0x61, 0x69, 0x73, 0x6e, 0x65, 0x79, 0x67, 0x63, 0x6d, 0x74,
+ 0x67, 0x75, 0x6c, 0x6c, 0x71, 0x70, 0x62, 0x74, 0x61, 0x61, 0x64, 0x76, 0x69, 0x74, 0x6b, 0x76,
+ 0x76, 0x68, 0x62, 0x62, 0x66, 0x71, 0x6a, 0x72, 0x6a, 0x6b, 0x6c, 0x72, 0x6e, 0x74, 0x74, 0x77,
+ 0x61, 0x77, 0x69, 0x63, 0x6e, 0x6e, 0x68, 0x6c, 0x68, 0x61, 0x76, 0x61, 0x61, 0x75, 0x75, 0x6e,
+ 0x69, 0x70, 0x6d, 0x6c, 0x68, 0x73, 0x71, 0x63, 0x73, 0x61, 0x72, 0x75, 0x6f, 0x75, 0x67, 0x70,
+ 0x65, 0x69, 0x65, 0x77, 0x72, 0x79, 0x77, 0x73, 0x76, 0x6f, 0x77, 0x6a, 0x69, 0x72, 0x69, 0x71,
+ 0x79, 0x63, 0x63, 0x75, 0x6b, 0x69, 0x75, 0x62, 0x63, 0x75, 0x6d, 0x61, 0x6b, 0x6a, 0x63, 0x6f,
+ 0x77, 0x71, 0x69, 0x6b, 0x6e, 0x77, 0x75, 0x68, 0x78, 0x74, 0x70, 0x77, 0x68, 0x67, 0x73, 0x72,
+ 0x76, 0x61, 0x66, 0x68, 0x62, 0x61, 0x6a, 0x6e, 0x73, 0x77, 0x79, 0x61, 0x66, 0x74, 0x6a, 0x61,
+ 0x70, 0x6f, 0x66, 0x75, 0x65, 0x6a, 0x76, 0x66, 0x67, 0x75, 0x6c, 0x79, 0x6d, 0x79, 0x73, 0x79,
+ 0x66, 0x64, 0x61, 0x75, 0x79, 0x6d, 0x66, 0x6a, 0x78, 0x6a, 0x73, 0x76, 0x6a, 0x6a, 0x6d, 0x6f,
+ 0x67, 0x63, 0x66, 0x71, 0x67, 0x69, 0x73, 0x68, 0x6b, 0x6e, 0x6e, 0x6a, 0x70, 0x61, 0x68, 0x6a,
+ 0x77, 0x6d, 0x65, 0x69, 0x6b, 0x67, 0x6e, 0x6f, 0x63, 0x72, 0x79, 0x70, 0x73, 0x6d, 0x75, 0x76,
+ 0x77, 0x6b, 0x6b, 0x72, 0x70, 0x61, 0x74, 0x6c, 0x76, 0x76, 0x76, 0x75, 0x70, 0x72, 0x77, 0x6c,
+ 0x6b, 0x64, 0x66, 0x6f, 0x64, 0x6e, 0x6f, 0x75, 0x64, 0x67, 0x63, 0x71, 0x64, 0x70, 0x79, 0x67,
+ 0x61, 0x70, 0x62, 0x72, 0x71, 0x6d, 0x66, 0x65, 0x6f, 0x66, 0x63, 0x77, 0x6b, 0x78, 0x70, 0x77,
+ 0x65, 0x71, 0x78, 0x66, 0x70, 0x64, 0x76, 0x63, 0x74, 0x6d, 0x62, 0x77, 0x6b, 0x70, 0x6d, 0x6a,
+ 0x64, 0x72, 0x65, 0x61, 0x6e, 0x75, 0x62, 0x6a, 0x66, 0x6d, 0x6f, 0x6e, 0x74, 0x6a, 0x79, 0x67,
+ 0x73, 0x6e, 0x64, 0x74, 0x72, 0x79, 0x64, 0x6a, 0x79, 0x61, 0x61, 0x64, 0x62, 0x66, 0x6f, 0x72,
+ 0x70, 0x72, 0x75, 0x66, 0x77, 0x6e, 0x62, 0x6c, 0x73, 0x64, 0x66, 0x71, 0x69, 0x79, 0x66, 0x74,
+ 0x64, 0x61, 0x70, 0x67, 0x62, 0x69, 0x72, 0x71, 0x61, 0x78, 0x74, 0x77, 0x6b, 0x6f, 0x62, 0x77,
+ 0x6c, 0x75, 0x67, 0x78, 0x67, 0x61, 0x64, 0x76, 0x68, 0x72, 0x68, 0x71, 0x64, 0x63, 0x70, 0x69,
+ 0x6f, 0x75, 0x6e, 0x76, 0x78, 0x6d, 0x69, 0x68, 0x78, 0x62, 0x63, 0x72, 0x65, 0x69, 0x63, 0x72,
+ 0x71, 0x75, 0x71, 0x69, 0x74, 0x65, 0x6b, 0x71, 0x79, 0x6e, 0x6e, 0x74, 0x63, 0x70, 0x71, 0x6a,
+ 0x6a, 0x6e, 0x73, 0x6a, 0x6e, 0x66, 0x63, 0x66, 0x66, 0x63, 0x6f, 0x76, 0x69, 0x78, 0x75, 0x68,
+ 0x65, 0x74, 0x65, 0x6b, 0x68, 0x68, 0x6c, 0x66, 0x79, 0x6d, 0x64, 0x78, 0x6a, 0x69, 0x71, 0x6b,
+ 0x6b, 0x61, 0x69, 0x73, 0x67, 0x78, 0x6d, 0x73, 0x78, 0x6b, 0x62, 0x75, 0x71, 0x76, 0x79, 0x6b,
+ 0x61, 0x73, 0x6d, 0x75, 0x66, 0x63, 0x6f, 0x6c, 0x65, 0x6a, 0x6f, 0x78, 0x63, 0x78, 0x75, 0x6b,
+ 0x78, 0x6e, 0x6c, 0x71, 0x70, 0x75, 0x68, 0x68, 0x6b, 0x78, 0x77, 0x69, 0x73, 0x6c, 0x67, 0x6b,
+ 0x6d, 0x6f, 0x6b, 0x72, 0x77, 0x78, 0x73, 0x68, 0x63, 0x68, 0x69, 0x62, 0x77, 0x72, 0x77, 0x77,
+ 0x73, 0x66, 0x77, 0x6e, 0x68, 0x6a, 0x68, 0x73, 0x6c, 0x6c, 0x6d, 0x73, 0x67, 0x63, 0x64, 0x6d,
+ 0x68, 0x73, 0x69, 0x66, 0x78, 0x64, 0x6e, 0x74, 0x69, 0x71, 0x6f, 0x62, 0x68, 0x6f, 0x76, 0x71,
+ 0x65, 0x63, 0x70, 0x61, 0x68, 0x67, 0x72, 0x79, 0x71, 0x6e, 0x69, 0x68, 0x74, 0x61, 0x6c, 0x6b,
+ 0x6c, 0x6f, 0x63, 0x70, 0x69, 0x71, 0x6d, 0x63, 0x64, 0x6b, 0x79, 0x67, 0x72, 0x75, 0x65, 0x6b,
+ 0x75, 0x61, 0x76, 0x63, 0x61, 0x6d, 0x6e, 0x6d, 0x70, 0x6a, 0x77, 0x66, 0x75, 0x66, 0x68, 0x6d,
+ 0x71, 0x66, 0x6d, 0x6e, 0x64, 0x63, 0x65, 0x6c, 0x72, 0x6f, 0x6f, 0x62, 0x78, 0x6d, 0x67, 0x62,
+ 0x6a, 0x6d, 0x75, 0x67, 0x63, 0x63, 0x61, 0x78, 0x66, 0x78, 0x66, 0x78, 0x6d, 0x66, 0x79, 0x78,
+ 0x72, 0x65, 0x65, 0x79, 0x61, 0x79, 0x70, 0x73, 0x72, 0x73, 0x77, 0x69, 0x62, 0x62, 0x71, 0x61,
+ 0x76, 0x66, 0x73, 0x66, 0x66, 0x62, 0x63, 0x62, 0x6f, 0x63, 0x64, 0x63, 0x6d, 0x6d, 0x75, 0x6d,
+ 0x6f, 0x68, 0x62, 0x62, 0x6e, 0x76, 0x65, 0x6b, 0x6a, 0x69, 0x70, 0x63, 0x75, 0x68, 0x6f, 0x6e,
+ 0x6d, 0x71, 0x66, 0x75, 0x64, 0x79, 0x74, 0x69, 0x6d, 0x6e, 0x78, 0x6c, 0x73, 0x69, 0x73, 0x79,
+ 0x67, 0x6b, 0x6a, 0x6e, 0x63, 0x6f, 0x79, 0x66, 0x62, 0x69, 0x63, 0x71, 0x6c, 0x73, 0x73, 0x63,
+ 0x6c, 0x77, 0x64, 0x6d, 0x67, 0x72, 0x64, 0x6b, 0x75, 0x72, 0x69, 0x70, 0x68, 0x70, 0x62, 0x62,
+ 0x64, 0x78, 0x69, 0x78, 0x6f, 0x67, 0x6e, 0x6c, 0x77, 0x6f, 0x78, 0x65, 0x70, 0x70, 0x6a, 0x68,
+ 0x6e, 0x71, 0x64, 0x61, 0x6d, 0x63, 0x75, 0x75, 0x6e, 0x79, 0x66, 0x63, 0x6e, 0x70, 0x6f, 0x74,
+ 0x77, 0x75, 0x63, 0x6e, 0x71, 0x78, 0x67, 0x74, 0x79, 0x66, 0x71, 0x6b, 0x64, 0x74, 0x6c, 0x79,
+ 0x62, 0x78, 0x6d, 0x67, 0x6b, 0x6a, 0x67, 0x61, 0x63, 0x61, 0x6e, 0x73, 0x61, 0x6b, 0x66, 0x69,
+ 0x74, 0x6e, 0x70, 0x66, 0x6a, 0x66, 0x64, 0x73, 0x66, 0x74, 0x6a, 0x74, 0x63, 0x72, 0x63, 0x73,
+ 0x71, 0x76, 0x6c, 0x64, 0x69, 0x74, 0x6b, 0x73, 0x6c, 0x6d, 0x75, 0x66, 0x62, 0x79, 0x6d, 0x62,
+ 0x70, 0x77, 0x6f, 0x63, 0x6b, 0x69, 0x6c, 0x78, 0x69, 0x78, 0x66, 0x65, 0x68, 0x62, 0x6f, 0x67,
+ 0x65, 0x61, 0x71, 0x6f, 0x69, 0x71, 0x75, 0x74, 0x75, 0x62, 0x69, 0x79, 0x76, 0x78, 0x6a, 0x66,
+ 0x79, 0x6e, 0x6e, 0x73, 0x62, 0x63, 0x77, 0x74, 0x75, 0x71, 0x6d, 0x6d, 0x6d, 0x74, 0x73, 0x6a,
+ 0x78, 0x74, 0x64, 0x6d, 0x79, 0x6c, 0x6f, 0x66, 0x6d, 0x61, 0x6f, 0x70, 0x71, 0x76, 0x79, 0x70,
+ 0x67, 0x71, 0x6d, 0x69, 0x62, 0x66, 0x75, 0x79, 0x71, 0x61, 0x69, 0x6b, 0x68, 0x66, 0x62, 0x66,
+ 0x6c, 0x73, 0x6e, 0x71, 0x62, 0x6c, 0x6e, 0x6d, 0x63, 0x66, 0x73, 0x6d, 0x61, 0x69, 0x75, 0x6a,
+ 0x64, 0x61, 0x72, 0x71, 0x75, 0x73, 0x6d, 0x71, 0x79, 0x68, 0x61, 0x77, 0x74, 0x64, 0x69, 0x6d,
+ 0x6d, 0x61, 0x72, 0x72, 0x76, 0x64, 0x66, 0x70, 0x64, 0x6c, 0x73, 0x6c, 0x75, 0x74, 0x73, 0x66,
+ 0x75, 0x67, 0x77, 0x6d, 0x6f, 0x77, 0x6e, 0x68, 0x67, 0x66, 0x68, 0x6f, 0x6b, 0x68, 0x62, 0x73,
+ 0x64, 0x79, 0x73, 0x75, 0x69, 0x6f, 0x77, 0x71, 0x68, 0x6e, 0x66, 0x78, 0x6f, 0x66, 0x73, 0x68,
+ 0x74, 0x76, 0x6d, 0x6d, 0x67, 0x6d, 0x6d, 0x73, 0x65, 0x66, 0x6b, 0x6d, 0x69, 0x70, 0x72, 0x70,
+ 0x70, 0x70, 0x64, 0x71, 0x75, 0x74, 0x61, 0x78, 0x77, 0x64, 0x6f, 0x79, 0x63, 0x69, 0x6b, 0x64,
+ 0x67, 0x78, 0x6d, 0x65, 0x63, 0x66, 0x73, 0x6d, 0x70, 0x64, 0x77, 0x75, 0x66, 0x61, 0x77, 0x70,
+ 0x63, 0x66, 0x75, 0x65, 0x78, 0x6c, 0x77, 0x73, 0x62, 0x77, 0x75, 0x67, 0x64, 0x72, 0x61, 0x6b,
+ 0x6c, 0x6a, 0x68, 0x79, 0x6a, 0x6b, 0x6b, 0x70, 0x67, 0x74, 0x6b, 0x71, 0x72, 0x65, 0x78, 0x71,
+ 0x74, 0x64, 0x79, 0x79, 0x61, 0x69, 0x68, 0x6c, 0x68, 0x64, 0x67, 0x74, 0x64, 0x67, 0x64, 0x73,
+ 0x6d, 0x6a, 0x6c, 0x61, 0x70, 0x6e, 0x72, 0x62, 0x6d, 0x66, 0x62, 0x77, 0x6e, 0x6e, 0x74, 0x79,
+ 0x62, 0x6d, 0x77, 0x76, 0x65, 0x6c, 0x72, 0x76, 0x79, 0x75, 0x76, 0x78, 0x6f, 0x77, 0x61, 0x75,
+ 0x6c, 0x75, 0x63, 0x65, 0x79, 0x77, 0x69, 0x73, 0x62, 0x71, 0x6a, 0x6e, 0x6b, 0x72, 0x6c, 0x76,
+ 0x6e, 0x62, 0x64, 0x75, 0x62, 0x6e, 0x75, 0x6c, 0x65, 0x78, 0x72, 0x6c, 0x6a, 0x66, 0x6f, 0x6e,
+ 0x74, 0x68, 0x77, 0x64, 0x68, 0x6d, 0x6b, 0x6b, 0x6b, 0x6e, 0x71, 0x76, 0x6f, 0x71, 0x66, 0x75,
+ 0x68, 0x70, 0x73, 0x75, 0x6d, 0x6c, 0x6e, 0x6b, 0x74, 0x75, 0x79, 0x66, 0x69, 0x61, 0x79, 0x61,
+ 0x6d, 0x66, 0x71, 0x67, 0x63, 0x73, 0x63, 0x75, 0x76, 0x68, 0x70, 0x67, 0x78, 0x6f, 0x6c, 0x68,
+ 0x6e, 0x75, 0x69, 0x69, 0x75, 0x78, 0x6f, 0x6b, 0x67, 0x76, 0x79, 0x73, 0x61, 0x72, 0x74, 0x62,
+ 0x64, 0x61, 0x63, 0x64, 0x63, 0x77, 0x6e, 0x6b, 0x78, 0x65, 0x70, 0x73, 0x6b, 0x64, 0x6a, 0x64,
+ 0x76, 0x6b, 0x74, 0x6e, 0x6b, 0x67, 0x64, 0x78, 0x65, 0x71, 0x68, 0x68, 0x74, 0x62, 0x79, 0x75,
+ 0x73, 0x6a, 0x69, 0x72, 0x64, 0x75, 0x61, 0x66, 0x70, 0x66, 0x71, 0x6d, 0x6b, 0x63, 0x62, 0x71,
+ 0x66, 0x65, 0x68, 0x78, 0x77, 0x65, 0x62, 0x78, 0x66, 0x72, 0x67, 0x78, 0x6d, 0x75, 0x6d, 0x75,
+ 0x6a, 0x72, 0x68, 0x6d, 0x71, 0x68, 0x63, 0x6e, 0x76, 0x63, 0x79, 0x66, 0x73, 0x69, 0x6e, 0x6f,
+ 0x70, 0x66, 0x70, 0x62, 0x66, 0x63, 0x6a, 0x61, 0x72, 0x69, 0x79, 0x69, 0x69, 0x74, 0x76, 0x61,
+ 0x61, 0x67, 0x68, 0x72, 0x62, 0x61, 0x64, 0x6b, 0x66, 0x6d, 0x6e, 0x79, 0x79, 0x67, 0x68, 0x79,
+ 0x76, 0x64, 0x65, 0x65, 0x61, 0x6b, 0x6a, 0x6f, 0x6d, 0x6e, 0x63, 0x68, 0x6e, 0x76, 0x71, 0x67,
+ 0x6b, 0x74, 0x70, 0x68, 0x73, 0x77, 0x6c, 0x76, 0x67, 0x6a, 0x77, 0x79, 0x77, 0x74, 0x68, 0x6a,
+ 0x70, 0x6e, 0x6f, 0x62, 0x6f, 0x62, 0x6c, 0x6d, 0x63, 0x6f, 0x77, 0x6e, 0x75, 0x6c, 0x6a, 0x61,
+ 0x68, 0x71, 0x76, 0x64, 0x78, 0x66, 0x66, 0x67, 0x65, 0x6e, 0x74, 0x6e, 0x77, 0x65, 0x6c, 0x67,
+ 0x62, 0x6e, 0x62, 0x6d, 0x6b, 0x69, 0x6c, 0x67, 0x74, 0x6e, 0x63, 0x65, 0x70, 0x74, 0x6d, 0x71,
+ 0x6c, 0x65, 0x6d, 0x74, 0x62, 0x62, 0x6f, 0x64, 0x61, 0x61, 0x70, 0x6a, 0x79, 0x62, 0x62, 0x77,
+ 0x6d, 0x70, 0x79, 0x70, 0x67, 0x69, 0x76, 0x75, 0x66, 0x70, 0x6a, 0x6c, 0x62, 0x71, 0x61, 0x6b,
+ 0x77, 0x68, 0x6d, 0x6b, 0x73, 0x73, 0x68, 0x62, 0x70, 0x71, 0x71, 0x6b, 0x70, 0x68, 0x75, 0x63,
+ 0x77, 0x63, 0x76, 0x62, 0x6a, 0x66, 0x61, 0x77, 0x6f, 0x76, 0x64, 0x75, 0x72, 0x64, 0x6a, 0x70,
+ 0x75, 0x76, 0x65, 0x68, 0x68, 0x63, 0x6c, 0x6f, 0x78, 0x6b, 0x66, 0x68, 0x68, 0x71, 0x79, 0x74,
+ 0x76, 0x68, 0x77, 0x76, 0x79, 0x75, 0x6b, 0x79, 0x61, 0x64, 0x69, 0x6f, 0x6e, 0x68, 0x6d, 0x75,
+ 0x63, 0x71, 0x6b, 0x77, 0x65, 0x68, 0x72, 0x63, 0x64, 0x64, 0x6a, 0x62, 0x6e, 0x6d, 0x72, 0x77,
+ 0x74, 0x71, 0x67, 0x6d, 0x68, 0x61, 0x63, 0x65, 0x6c, 0x64, 0x64, 0x71, 0x75, 0x62, 0x67, 0x69,
+ 0x6d, 0x76, 0x68, 0x62, 0x71, 0x74, 0x65, 0x62, 0x61, 0x6f, 0x70, 0x78, 0x6f, 0x65, 0x78, 0x71,
+ 0x67, 0x77, 0x70, 0x66, 0x65, 0x72, 0x69, 0x79, 0x72, 0x66, 0x62, 0x79, 0x66, 0x62, 0x6d, 0x6b,
+ 0x6b, 0x6b, 0x78, 0x6a, 0x68, 0x66, 0x6f, 0x77, 0x70, 0x75, 0x70, 0x75, 0x78, 0x6d, 0x63, 0x74,
+ 0x61, 0x78, 0x77, 0x65, 0x63, 0x68, 0x6f, 0x6f, 0x74, 0x79, 0x73, 0x6a, 0x6b, 0x6b, 0x67, 0x70,
+ 0x74, 0x77, 0x76, 0x73, 0x75, 0x6a, 0x74, 0x66, 0x6a, 0x73, 0x64, 0x6f, 0x6c, 0x73, 0x6e, 0x75,
+ 0x77, 0x6d, 0x71, 0x71, 0x63, 0x6f, 0x62, 0x75, 0x62, 0x72, 0x72, 0x66, 0x71, 0x63, 0x71, 0x6f,
+ 0x67, 0x71, 0x65, 0x76, 0x71, 0x70, 0x6f, 0x79, 0x73, 0x69, 0x67, 0x64, 0x71, 0x68, 0x73, 0x79,
+ 0x74, 0x68, 0x70, 0x6d, 0x6e, 0x74, 0x6e, 0x79, 0x79, 0x62, 0x65, 0x6f, 0x74, 0x75, 0x61, 0x6b,
+ 0x69, 0x6e, 0x70, 0x6e, 0x70, 0x6b, 0x70, 0x72, 0x78, 0x63, 0x6c, 0x66, 0x67, 0x71, 0x6e, 0x72,
+ 0x65, 0x69, 0x72, 0x6e, 0x75, 0x71, 0x61, 0x6e, 0x64, 0x6b, 0x6a, 0x69, 0x63, 0x62, 0x76, 0x62,
+ 0x6f, 0x61, 0x74, 0x72, 0x66, 0x6b, 0x67, 0x63, 0x6d, 0x66, 0x73, 0x64, 0x65, 0x72, 0x65, 0x73,
+ 0x76, 0x70, 0x62, 0x62, 0x70, 0x71, 0x70, 0x66, 0x78, 0x62, 0x6b, 0x6f, 0x63, 0x6e, 0x74, 0x63,
+ 0x61, 0x63, 0x78, 0x74, 0x79, 0x6e, 0x77, 0x64, 0x6a, 0x73, 0x64, 0x72, 0x6a, 0x68, 0x6a, 0x68,
+ 0x64, 0x62, 0x67, 0x68, 0x6a, 0x75, 0x78, 0x74, 0x73, 0x70, 0x6d, 0x65, 0x75, 0x71, 0x61, 0x6f,
+ 0x6c, 0x78, 0x67, 0x79, 0x66, 0x71, 0x64, 0x75, 0x79, 0x75, 0x77, 0x67, 0x78, 0x69, 0x70, 0x70,
+ 0x74, 0x6d, 0x62, 0x70, 0x68, 0x71, 0x75, 0x6d, 0x67, 0x73, 0x79, 0x64, 0x74, 0x72, 0x67, 0x74,
+ 0x70, 0x6b, 0x67, 0x78, 0x66, 0x6c, 0x77, 0x71, 0x78, 0x6c, 0x75, 0x66, 0x69, 0x67, 0x69, 0x67,
+ 0x79, 0x6c, 0x70, 0x71, 0x6b, 0x6c, 0x67, 0x74, 0x79, 0x79, 0x78, 0x79, 0x63, 0x73, 0x79, 0x6c,
+ 0x62, 0x62, 0x78, 0x62, 0x74, 0x75, 0x61, 0x6d, 0x73, 0x6d, 0x61, 0x64, 0x78, 0x6d, 0x61, 0x6b,
+ 0x70, 0x77, 0x67, 0x63, 0x65, 0x67, 0x62, 0x78, 0x70, 0x6c, 0x6b, 0x63, 0x78, 0x63, 0x66, 0x79,
+ 0x62, 0x6c, 0x73, 0x65, 0x6c, 0x6d, 0x70, 0x67, 0x69, 0x67, 0x6d, 0x61, 0x79, 0x6b, 0x76, 0x66,
+ 0x66, 0x76, 0x6e, 0x62, 0x66, 0x61, 0x77, 0x67, 0x6c, 0x61, 0x6f, 0x61, 0x62, 0x75, 0x64, 0x72,
+ 0x77, 0x76, 0x6d, 0x65, 0x77, 0x65, 0x67, 0x73, 0x64, 0x73, 0x77, 0x68, 0x73, 0x69, 0x74, 0x6a,
+ 0x65, 0x62, 0x77, 0x73, 0x6a, 0x61, 0x64, 0x73, 0x6f, 0x65, 0x75, 0x76, 0x61, 0x63, 0x78, 0x64,
+ 0x69, 0x79, 0x6d, 0x68, 0x69, 0x65, 0x6b, 0x69, 0x6b, 0x62, 0x6d, 0x6d, 0x73, 0x6f, 0x61, 0x70,
+ 0x79, 0x67, 0x6d, 0x6c, 0x61, 0x72, 0x70, 0x79, 0x66, 0x78, 0x61, 0x66, 0x71, 0x74, 0x6c, 0x73,
+ 0x62, 0x70, 0x61, 0x72, 0x6e, 0x78, 0x77, 0x69, 0x77, 0x6a, 0x62, 0x75, 0x6f, 0x6d, 0x67, 0x79,
+ 0x71, 0x63, 0x61, 0x76, 0x77, 0x75, 0x6b, 0x65, 0x72, 0x72, 0x63, 0x65, 0x65, 0x64, 0x71, 0x74,
+ 0x78, 0x77, 0x6f, 0x70, 0x71, 0x63, 0x77, 0x63, 0x75, 0x66, 0x66, 0x6d, 0x67, 0x61, 0x76, 0x77,
+ 0x69, 0x64, 0x72, 0x6f, 0x73, 0x69, 0x6d, 0x69, 0x71, 0x74, 0x64, 0x71, 0x63, 0x72, 0x6d, 0x75,
+ 0x77, 0x63, 0x77, 0x67, 0x77, 0x64, 0x6d, 0x6f, 0x72, 0x6f, 0x6d, 0x6b, 0x62, 0x63, 0x70, 0x6f,
+ 0x72, 0x62, 0x61, 0x71, 0x65, 0x67, 0x64, 0x76, 0x78, 0x63, 0x66, 0x66, 0x71, 0x6f, 0x69, 0x73,
+ 0x6b, 0x72, 0x67, 0x6e, 0x77, 0x77, 0x79, 0x73, 0x69, 0x79, 0x6a, 0x6d, 0x72, 0x74, 0x79, 0x68,
+ 0x72, 0x6e, 0x70, 0x61, 0x6d, 0x67, 0x66, 0x74, 0x67, 0x6d, 0x73, 0x72, 0x79, 0x62, 0x6d, 0x6f,
+ 0x64, 0x63, 0x6f, 0x67, 0x70, 0x69, 0x79, 0x71, 0x68, 0x72, 0x79, 0x63, 0x6e, 0x77, 0x6b, 0x66,
+ 0x67, 0x73, 0x65, 0x71, 0x72, 0x68, 0x64, 0x73, 0x78, 0x6f, 0x64, 0x66, 0x78, 0x68, 0x66, 0x65,
+ 0x64, 0x78, 0x72, 0x61, 0x68, 0x74, 0x79, 0x6f, 0x6f, 0x6a, 0x63, 0x6a, 0x66, 0x6b, 0x64, 0x71,
+ 0x6f, 0x66, 0x6b, 0x67, 0x6c, 0x6c, 0x6c, 0x73, 0x61, 0x6f, 0x78, 0x73, 0x71, 0x62, 0x6e, 0x65,
+ 0x74, 0x67, 0x66, 0x6f, 0x6c, 0x77, 0x6a, 0x67, 0x6d, 0x61, 0x77, 0x77, 0x73, 0x73, 0x61, 0x6c,
+ 0x79, 0x6a, 0x78, 0x62, 0x74, 0x62, 0x62, 0x62, 0x70, 0x64, 0x6e, 0x6e, 0x62, 0x63, 0x79, 0x62,
+ 0x61, 0x6c, 0x74, 0x6e, 0x67, 0x77, 0x63, 0x75, 0x71, 0x61, 0x63, 0x67, 0x6f, 0x71, 0x63, 0x67,
+ 0x76, 0x73, 0x63, 0x66, 0x74, 0x68, 0x63, 0x6c, 0x66, 0x6a, 0x71, 0x69, 0x76, 0x6c, 0x72, 0x62,
+ 0x72, 0x67, 0x61, 0x6f, 0x72, 0x71, 0x64, 0x74, 0x64, 0x71, 0x6b, 0x68, 0x63, 0x6b, 0x67, 0x62,
+ 0x68, 0x6b, 0x79, 0x76, 0x70, 0x71, 0x76, 0x62, 0x72, 0x72, 0x74, 0x61, 0x6f, 0x67, 0x6f, 0x79,
+ 0x74, 0x69, 0x62, 0x72, 0x65, 0x61, 0x73, 0x61, 0x6b, 0x72, 0x6d, 0x73, 0x79, 0x78, 0x63, 0x62,
+ 0x70, 0x68, 0x63, 0x73, 0x77, 0x65, 0x61, 0x71, 0x79, 0x70, 0x71, 0x6a, 0x67, 0x70, 0x6c, 0x62,
+ 0x6f, 0x79, 0x62, 0x72, 0x73, 0x61, 0x75, 0x77, 0x74, 0x79, 0x70, 0x76, 0x64, 0x6c, 0x71, 0x61,
+ 0x67, 0x68, 0x76, 0x66, 0x71, 0x75, 0x72, 0x69, 0x71, 0x66, 0x6a, 0x78, 0x66, 0x6d, 0x6f, 0x75,
+ 0x6d, 0x6a, 0x61, 0x75, 0x6c, 0x6f, 0x71, 0x75, 0x6d, 0x70, 0x67, 0x79, 0x69, 0x61, 0x65, 0x72,
+ 0x6c, 0x6c, 0x71, 0x68, 0x72, 0x75, 0x65, 0x61, 0x77, 0x69, 0x73, 0x63, 0x6d, 0x75, 0x67, 0x69,
+ 0x76, 0x6b, 0x6e, 0x78, 0x70, 0x64, 0x6a, 0x6c, 0x67, 0x6e, 0x6e, 0x69, 0x77, 0x67, 0x67, 0x6e,
+ 0x66, 0x76, 0x61, 0x72, 0x6b, 0x61, 0x78, 0x74, 0x66, 0x76, 0x6e, 0x68, 0x6b, 0x66, 0x64, 0x66,
+ 0x72, 0x6b, 0x68, 0x74, 0x6f, 0x6b, 0x77, 0x73, 0x77, 0x6f, 0x6d, 0x70, 0x61, 0x76, 0x78, 0x76,
+ 0x6c, 0x71, 0x6f, 0x75, 0x79, 0x67, 0x68, 0x68, 0x68, 0x77, 0x68, 0x68, 0x62, 0x6d, 0x6d, 0x74,
+ 0x6f, 0x78, 0x6e, 0x62, 0x6e, 0x65, 0x77, 0x74, 0x79, 0x62, 0x67, 0x79, 0x65, 0x61, 0x72, 0x78,
+ 0x61, 0x73, 0x6e, 0x6e, 0x77, 0x6c, 0x6a, 0x77, 0x62, 0x70, 0x68, 0x64, 0x62, 0x79, 0x65, 0x73,
+ 0x6d, 0x77, 0x77, 0x78, 0x78, 0x69, 0x65, 0x6e, 0x75, 0x71, 0x79, 0x6b, 0x73, 0x6e, 0x75, 0x69,
+ 0x77, 0x69, 0x65, 0x75, 0x6f, 0x79, 0x79, 0x6c, 0x6b, 0x74, 0x74, 0x63, 0x67, 0x67, 0x75, 0x71,
+ 0x74, 0x73, 0x76, 0x69, 0x68, 0x6d, 0x67, 0x67, 0x74, 0x62, 0x69, 0x6d, 0x6b, 0x78, 0x77, 0x68,
+ 0x66, 0x61, 0x6a, 0x67, 0x6f, 0x65, 0x62, 0x69, 0x67, 0x64, 0x78, 0x6b, 0x75, 0x6a, 0x67, 0x72,
+ 0x76, 0x64, 0x6f, 0x72, 0x63, 0x63, 0x76, 0x66, 0x6b, 0x6c, 0x62, 0x64, 0x70, 0x63, 0x68, 0x70,
+ 0x74, 0x71, 0x61, 0x77, 0x6b, 0x78, 0x68, 0x6c, 0x77, 0x63, 0x76, 0x71, 0x6c, 0x69, 0x6a, 0x6c,
+ 0x70, 0x75, 0x78, 0x68, 0x62, 0x77, 0x61, 0x63, 0x77, 0x6c, 0x66, 0x77, 0x79, 0x64, 0x79, 0x79,
+ 0x76, 0x75, 0x77, 0x6e, 0x6d, 0x72, 0x66, 0x6b, 0x74, 0x6b, 0x62, 0x67, 0x76, 0x66, 0x74, 0x74,
+ 0x6d, 0x68, 0x6e, 0x6a, 0x77, 0x6c, 0x6c, 0x75, 0x6b, 0x65, 0x74, 0x6d, 0x62, 0x6c, 0x61, 0x70,
+ 0x75, 0x67, 0x6f, 0x67, 0x6d, 0x64, 0x76, 0x72, 0x6b, 0x6a, 0x65, 0x6e, 0x6c, 0x63, 0x71, 0x6b,
+ 0x6a, 0x64, 0x6f, 0x74, 0x67, 0x70, 0x69, 0x6e, 0x62, 0x72, 0x66, 0x65, 0x66, 0x65, 0x6e, 0x74,
+ 0x64, 0x6a, 0x78, 0x71, 0x61, 0x62, 0x61, 0x66, 0x6a, 0x63, 0x73, 0x66, 0x66, 0x79, 0x69, 0x6c,
+ 0x74, 0x6e, 0x78, 0x69, 0x6b, 0x6c, 0x62, 0x6f, 0x6c, 0x65, 0x61, 0x74, 0x65, 0x65, 0x66, 0x70,
+ 0x74, 0x71, 0x64, 0x67, 0x76, 0x69, 0x79, 0x6f, 0x6a, 0x74, 0x63, 0x62, 0x76, 0x71, 0x6a, 0x6e,
+ 0x78, 0x6b, 0x75, 0x67, 0x6c, 0x68, 0x63, 0x76, 0x63, 0x73, 0x6b, 0x6d, 0x69, 0x62, 0x72, 0x72,
+ 0x70, 0x64, 0x79, 0x79, 0x63, 0x6c, 0x70, 0x72, 0x6a, 0x79, 0x76, 0x74, 0x67, 0x6f, 0x6a, 0x67,
+ 0x68, 0x63, 0x69, 0x69, 0x78, 0x65, 0x77, 0x64, 0x6e, 0x6f, 0x78, 0x72, 0x68, 0x79, 0x68, 0x6f,
+ 0x6a, 0x62, 0x72, 0x75, 0x70, 0x6b, 0x75, 0x76, 0x6c, 0x69, 0x6b, 0x6d, 0x6d, 0x65, 0x73, 0x62,
+ 0x69, 0x68, 0x76, 0x78, 0x61, 0x6c, 0x6e, 0x61, 0x72, 0x72, 0x75, 0x75, 0x70, 0x78, 0x71, 0x79,
+ 0x74, 0x6e, 0x73, 0x6a, 0x75, 0x73, 0x76, 0x67, 0x6d, 0x6b, 0x68, 0x75, 0x77, 0x72, 0x79, 0x62,
+ 0x68, 0x6d, 0x77, 0x6b, 0x6b, 0x61, 0x73, 0x75, 0x6a, 0x74, 0x72, 0x66, 0x68, 0x63, 0x70, 0x79,
+ 0x69, 0x66, 0x6c, 0x67, 0x71, 0x72, 0x67, 0x76, 0x73, 0x63, 0x67, 0x76, 0x68, 0x6e, 0x63, 0x6e,
+ 0x75, 0x63, 0x69, 0x70, 0x6e, 0x78, 0x66, 0x66, 0x75, 0x6b, 0x67, 0x79, 0x6d, 0x6d, 0x70, 0x6e,
+ 0x6e, 0x6e, 0x6a, 0x69, 0x76, 0x63, 0x6c, 0x76, 0x75, 0x79, 0x61, 0x79, 0x74, 0x65, 0x71, 0x67,
+ 0x62, 0x64, 0x62, 0x67, 0x6f, 0x6b, 0x6c, 0x68, 0x61, 0x6d, 0x73, 0x74, 0x70, 0x6b, 0x62, 0x63,
+ 0x75, 0x66, 0x6a, 0x6a, 0x6a, 0x6a, 0x64, 0x76, 0x6b, 0x67, 0x73, 0x75, 0x69, 0x73, 0x6a, 0x6e,
+ 0x6a, 0x68, 0x6d, 0x72, 0x73, 0x71, 0x73, 0x75, 0x6a, 0x6d, 0x69, 0x75, 0x66, 0x71, 0x67, 0x75,
+ 0x73, 0x70, 0x71, 0x61, 0x69, 0x71, 0x74, 0x71, 0x65, 0x6b, 0x71, 0x6f, 0x77, 0x68, 0x6a, 0x77,
+ 0x74, 0x79, 0x6f, 0x74, 0x69, 0x62, 0x70, 0x70, 0x65, 0x6d, 0x6d, 0x77, 0x6d, 0x72, 0x62, 0x67,
+ 0x62, 0x74, 0x61, 0x76, 0x79, 0x6b, 0x79, 0x6d, 0x75, 0x71, 0x6e, 0x6e, 0x76, 0x78, 0x72, 0x70,
+ 0x62, 0x64, 0x70, 0x6c, 0x6e, 0x79, 0x6c, 0x68, 0x70, 0x65, 0x67, 0x6d, 0x65, 0x75, 0x76, 0x62,
+ 0x63, 0x70, 0x6b, 0x75, 0x66, 0x68, 0x68, 0x6f, 0x70, 0x66, 0x68, 0x73, 0x6e, 0x78, 0x75, 0x68,
+ 0x6a, 0x6e, 0x61, 0x78, 0x6e, 0x6b, 0x78, 0x6e, 0x71, 0x66, 0x75, 0x68, 0x62, 0x79, 0x6f, 0x6c,
+ 0x66, 0x67, 0x67, 0x6c, 0x6c, 0x75, 0x62, 0x79, 0x6b, 0x69, 0x71, 0x69, 0x75, 0x72, 0x71, 0x6a,
+ 0x70, 0x76, 0x70, 0x6d, 0x73, 0x70, 0x72, 0x61, 0x6b, 0x77, 0x78, 0x78, 0x6e, 0x73, 0x75, 0x75,
+ 0x6f, 0x68, 0x63, 0x6c, 0x75, 0x75, 0x6d, 0x72, 0x70, 0x75, 0x63, 0x63, 0x63, 0x6b, 0x6a, 0x79,
+ 0x6d, 0x61, 0x73, 0x62, 0x79, 0x75, 0x6b, 0x6d, 0x6a, 0x6f, 0x67, 0x61, 0x62, 0x6a, 0x6e, 0x71,
+ 0x69, 0x71, 0x6a, 0x6a, 0x61, 0x68, 0x68, 0x76, 0x61, 0x6a, 0x72, 0x6b, 0x76, 0x74, 0x6f, 0x61,
+ 0x70, 0x77, 0x6b, 0x67, 0x73, 0x75, 0x70, 0x62, 0x6d, 0x65, 0x67, 0x6e, 0x62, 0x79, 0x74, 0x74,
+ 0x72, 0x61, 0x6b, 0x76, 0x64, 0x69, 0x69, 0x69, 0x6c, 0x68, 0x78, 0x65, 0x70, 0x68, 0x79, 0x70,
+ 0x65, 0x72, 0x64, 0x68, 0x68, 0x6c, 0x61, 0x6f, 0x74, 0x62, 0x78, 0x6c, 0x72, 0x6c, 0x62, 0x75,
+ 0x6c, 0x64, 0x72, 0x64, 0x79, 0x6b, 0x72, 0x72, 0x62, 0x6d, 0x74, 0x72, 0x6b, 0x78, 0x6c, 0x75,
+ 0x78, 0x6a, 0x71, 0x67, 0x72, 0x79, 0x76, 0x71, 0x75, 0x6d, 0x75, 0x67, 0x6a, 0x63, 0x66, 0x6f,
+ 0x63, 0x66, 0x79, 0x74, 0x74, 0x70, 0x75, 0x6a, 0x6e, 0x64, 0x64, 0x6e, 0x68, 0x6e, 0x6e, 0x78,
+ 0x6b, 0x68, 0x72, 0x74, 0x6c, 0x6d, 0x73, 0x74, 0x65, 0x72, 0x6c, 0x68, 0x78, 0x72, 0x66, 0x70,
+ 0x79, 0x6d, 0x61, 0x61, 0x76, 0x77, 0x64, 0x6e, 0x77, 0x74, 0x67, 0x6f, 0x74, 0x6f, 0x6b, 0x77,
+ 0x69, 0x79, 0x79, 0x73, 0x72, 0x71, 0x63, 0x62, 0x69, 0x78, 0x75, 0x72, 0x62, 0x71, 0x6f, 0x6e,
+ 0x66, 0x67, 0x6c, 0x72, 0x66, 0x6b, 0x61, 0x6e, 0x70, 0x70, 0x71, 0x65, 0x68, 0x71, 0x6e, 0x79,
+ 0x75, 0x75, 0x75, 0x67, 0x64, 0x67, 0x75, 0x74, 0x6a, 0x6b, 0x6a, 0x65, 0x71, 0x75, 0x73, 0x6d,
+ 0x77, 0x78, 0x72, 0x6e, 0x71, 0x61, 0x73, 0x63, 0x75, 0x78, 0x6c, 0x6c, 0x78, 0x6a, 0x75, 0x68,
+ 0x65, 0x71, 0x77, 0x6a, 0x76, 0x6d, 0x6d, 0x6e, 0x6a, 0x6e, 0x61, 0x65, 0x64, 0x72, 0x6c, 0x74,
+ 0x76, 0x63, 0x69, 0x65, 0x77, 0x70, 0x61, 0x69, 0x65, 0x74, 0x6d, 0x74, 0x68, 0x67, 0x6f, 0x65,
+ 0x62, 0x69, 0x63, 0x77, 0x77, 0x61, 0x75, 0x76, 0x69, 0x69, 0x77, 0x6f, 0x66, 0x6b, 0x77, 0x65,
+ 0x77, 0x6c, 0x6b, 0x69, 0x6a, 0x6a, 0x6c, 0x67, 0x76, 0x66, 0x74, 0x75, 0x72, 0x6c, 0x66, 0x6a,
+ 0x64, 0x67, 0x62, 0x73, 0x65, 0x75, 0x6e, 0x75, 0x6a, 0x6a, 0x69, 0x6a, 0x70, 0x63, 0x6f, 0x6c,
+ 0x69, 0x6b, 0x69, 0x6e, 0x79, 0x69, 0x70, 0x71, 0x66, 0x6b, 0x71, 0x71, 0x6e, 0x73, 0x66, 0x77,
+ 0x66, 0x73, 0x72, 0x77, 0x70, 0x74, 0x66, 0x71, 0x69, 0x6b, 0x74, 0x64, 0x61, 0x79, 0x65, 0x6e,
+ 0x6d, 0x6f, 0x6b, 0x78, 0x61, 0x70, 0x76, 0x74, 0x63, 0x78, 0x76, 0x63, 0x75, 0x67, 0x77, 0x62,
+ 0x63, 0x6a, 0x62, 0x79, 0x70, 0x61, 0x6e, 0x6c, 0x72, 0x6f, 0x6c, 0x6a, 0x63, 0x79, 0x75, 0x65,
+ 0x72, 0x72, 0x6e, 0x76, 0x64, 0x73, 0x77, 0x6b, 0x6d, 0x75, 0x67, 0x74, 0x75, 0x6f, 0x73, 0x67,
+ 0x6b, 0x61, 0x6a, 0x68, 0x69, 0x66, 0x6f, 0x64, 0x6f, 0x6e, 0x6a, 0x68, 0x61, 0x75, 0x79, 0x76,
+ 0x69, 0x70, 0x69, 0x6b, 0x6f, 0x71, 0x76, 0x6a, 0x77, 0x63, 0x68, 0x6f, 0x6e, 0x64, 0x71, 0x62,
+ 0x6d, 0x67, 0x76, 0x78, 0x70, 0x67, 0x6f, 0x67, 0x79, 0x78, 0x68, 0x73, 0x6a, 0x6f, 0x6b, 0x67,
+ 0x74, 0x6a, 0x77, 0x71, 0x77, 0x72, 0x70, 0x6c, 0x69, 0x78, 0x73, 0x72, 0x72, 0x71, 0x67, 0x6d,
+ 0x63, 0x6c, 0x6a, 0x72, 0x77, 0x61, 0x6b, 0x66, 0x63, 0x6c, 0x71, 0x78, 0x70, 0x6f, 0x70, 0x76,
+ 0x79, 0x61, 0x79, 0x6f, 0x6d, 0x61, 0x6b, 0x73, 0x6f, 0x6a, 0x64, 0x78, 0x6d, 0x6e, 0x67, 0x6e,
+ 0x74, 0x67, 0x74, 0x6a, 0x74, 0x6e, 0x79, 0x79, 0x6c, 0x63, 0x79, 0x6b, 0x6f, 0x6c, 0x69, 0x71,
+ 0x67, 0x75, 0x61, 0x62, 0x6b, 0x6d, 0x66, 0x71, 0x65, 0x79, 0x63, 0x6b, 0x79, 0x6e, 0x76, 0x6c,
+ 0x77, 0x75, 0x61, 0x66, 0x63, 0x65, 0x77, 0x75, 0x6a, 0x78, 0x75, 0x6f, 0x6a, 0x61, 0x61, 0x6d,
+ 0x69, 0x68, 0x77, 0x73, 0x79, 0x64, 0x77, 0x6f, 0x77, 0x76, 0x76, 0x6d, 0x69, 0x67, 0x78, 0x6f,
+ 0x76, 0x6a, 0x73, 0x65, 0x77, 0x6e, 0x70, 0x6a, 0x78, 0x75, 0x77, 0x79, 0x62, 0x6e, 0x76, 0x63,
+ 0x61, 0x76, 0x69, 0x6a, 0x68, 0x74, 0x78, 0x69, 0x78, 0x71, 0x6f, 0x6d, 0x76, 0x6f, 0x6d, 0x64,
+ 0x6d, 0x73, 0x6c, 0x65, 0x68, 0x6c, 0x71, 0x6d, 0x61, 0x66, 0x69, 0x78, 0x6e, 0x6b, 0x64, 0x79,
+ 0x62, 0x70, 0x6d, 0x66, 0x6d, 0x74, 0x6e, 0x70, 0x67, 0x6c, 0x6e, 0x68, 0x6c, 0x6a, 0x71, 0x79,
+ 0x66, 0x67, 0x68, 0x73, 0x6f, 0x72, 0x73, 0x6a, 0x77, 0x73, 0x65, 0x67, 0x62, 0x75, 0x69, 0x6d,
+ 0x71, 0x67, 0x73, 0x73, 0x72, 0x6b, 0x6e, 0x70, 0x6f, 0x68, 0x62, 0x72, 0x6c, 0x74, 0x66, 0x72,
+ 0x77, 0x72, 0x6d, 0x67, 0x75, 0x74, 0x77, 0x62, 0x72, 0x73, 0x68, 0x6e, 0x70, 0x65, 0x64, 0x6b,
+ 0x6f, 0x65, 0x76, 0x6c, 0x6f, 0x69, 0x72, 0x72, 0x77, 0x62, 0x63, 0x61, 0x6e, 0x6e, 0x72, 0x73,
+ 0x62, 0x6d, 0x6b, 0x69, 0x76, 0x6d, 0x71, 0x62, 0x61, 0x6b, 0x6a, 0x62, 0x6d, 0x79, 0x6d, 0x78,
+ 0x6a, 0x70, 0x68, 0x61, 0x65, 0x76, 0x66, 0x71, 0x64, 0x6c, 0x65, 0x77, 0x72, 0x66, 0x71, 0x6f,
+ 0x69, 0x71, 0x79, 0x70, 0x72, 0x6d, 0x6c, 0x69, 0x70, 0x69, 0x72, 0x64, 0x61, 0x79, 0x74, 0x6a,
+ 0x61, 0x79, 0x72, 0x6c, 0x74, 0x68, 0x66, 0x72, 0x72, 0x67, 0x63, 0x68, 0x6d, 0x77, 0x64, 0x71,
+ 0x71, 0x75, 0x69, 0x6d, 0x6e, 0x69, 0x72, 0x76, 0x6e, 0x6d, 0x72, 0x78, 0x75, 0x73, 0x66, 0x72,
+ 0x6b, 0x77, 0x6b, 0x77, 0x77, 0x72, 0x64, 0x77, 0x74, 0x76, 0x6e, 0x66, 0x62, 0x6e, 0x69, 0x77,
+ 0x62, 0x6f, 0x62, 0x73, 0x67, 0x79, 0x69, 0x63, 0x79, 0x63, 0x73, 0x64, 0x78, 0x76, 0x70, 0x6b,
+ 0x6f, 0x77, 0x72, 0x61, 0x6f, 0x78, 0x79, 0x67, 0x68, 0x76, 0x61, 0x6f, 0x63, 0x78, 0x6e, 0x6c,
+ 0x69, 0x73, 0x6c, 0x79, 0x74, 0x79, 0x67, 0x79, 0x79, 0x77, 0x77, 0x78, 0x65, 0x75, 0x6b, 0x65,
+ 0x65, 0x69, 0x77, 0x6c, 0x66, 0x65, 0x75, 0x68, 0x78, 0x66, 0x62, 0x6c, 0x75, 0x67, 0x67, 0x70,
+ 0x69, 0x6c, 0x79, 0x71, 0x77, 0x69, 0x6f, 0x79, 0x6d, 0x69, 0x6d, 0x79, 0x71, 0x66, 0x63, 0x75,
+ 0x69, 0x69, 0x76, 0x72, 0x75, 0x69, 0x6b, 0x64, 0x62, 0x70, 0x64, 0x78, 0x79, 0x69, 0x64, 0x72,
+ 0x68, 0x73, 0x6d, 0x68, 0x6c, 0x72, 0x66, 0x6a, 0x6a, 0x6f, 0x66, 0x67, 0x6d, 0x6f, 0x66, 0x74,
+ 0x62, 0x74, 0x70, 0x61, 0x71, 0x70, 0x70, 0x68, 0x77, 0x73, 0x78, 0x65, 0x61, 0x6f, 0x6e, 0x6c,
+ 0x61, 0x71, 0x6f, 0x66, 0x68, 0x76, 0x76, 0x66, 0x79, 0x70, 0x6c, 0x6d, 0x64, 0x6d, 0x76, 0x75,
+ 0x6d, 0x6e, 0x73, 0x6d, 0x67, 0x62, 0x78, 0x75, 0x6c, 0x6a, 0x6b, 0x74, 0x78, 0x63, 0x67, 0x65,
+ 0x62, 0x63, 0x66, 0x67, 0x6d, 0x77, 0x79, 0x66, 0x77, 0x72, 0x79, 0x6a, 0x64, 0x76, 0x78, 0x78,
+ 0x6b, 0x66, 0x6f, 0x6c, 0x6e, 0x6d, 0x6b, 0x73, 0x71, 0x6d, 0x68, 0x68, 0x74, 0x6a, 0x75, 0x6b,
+ 0x63, 0x77, 0x66, 0x6b, 0x6e, 0x69, 0x66, 0x75, 0x6e, 0x75, 0x6c, 0x67, 0x66, 0x6e, 0x6f, 0x73,
+ 0x6e, 0x73, 0x73, 0x77, 0x75, 0x71, 0x72, 0x76, 0x68, 0x64, 0x6a, 0x6c, 0x61, 0x66, 0x6b, 0x6d,
+ 0x6e, 0x63, 0x79, 0x69, 0x73, 0x6f, 0x64, 0x72, 0x61, 0x65, 0x6a, 0x67, 0x79, 0x6c, 0x6d, 0x61,
+ 0x62, 0x6e, 0x67, 0x6d, 0x77, 0x6a, 0x77, 0x72, 0x68, 0x68, 0x6f, 0x76, 0x77, 0x73, 0x76, 0x6f,
+ 0x6c, 0x66, 0x69, 0x75, 0x63, 0x74, 0x70, 0x74, 0x75, 0x6e, 0x66, 0x6d, 0x66, 0x69, 0x6b, 0x70,
+ 0x70, 0x74, 0x66, 0x6a, 0x6c, 0x6e, 0x65, 0x6b, 0x67, 0x71, 0x79, 0x76, 0x65, 0x64, 0x6a, 0x70,
+ 0x63, 0x71, 0x6f, 0x68, 0x76, 0x77, 0x65, 0x77, 0x78, 0x78, 0x65, 0x68, 0x69, 0x63, 0x66, 0x74,
+ 0x70, 0x65, 0x6e, 0x6a, 0x68, 0x6c, 0x70, 0x67, 0x6f, 0x62, 0x66, 0x79, 0x75, 0x71, 0x63, 0x6b,
+ 0x64, 0x6e, 0x74, 0x65, 0x72, 0x63, 0x63, 0x6b, 0x78, 0x66, 0x63, 0x6f, 0x6f, 0x73, 0x67, 0x73,
+ 0x72, 0x67, 0x61, 0x66, 0x77, 0x6f, 0x78, 0x63, 0x6f, 0x64, 0x6d, 0x68, 0x70, 0x74, 0x70, 0x70,
+ 0x6c, 0x61, 0x63, 0x63, 0x78, 0x72, 0x68, 0x69, 0x76, 0x79, 0x74, 0x6c, 0x6c, 0x64, 0x72, 0x62,
+ 0x65, 0x73, 0x6e, 0x63, 0x70, 0x74, 0x6a, 0x74, 0x68, 0x6d, 0x62, 0x63, 0x74, 0x6b, 0x62, 0x6f,
+ 0x61, 0x72, 0x68, 0x62, 0x6c, 0x6e, 0x72, 0x71, 0x79, 0x76, 0x74, 0x67, 0x74, 0x70, 0x74, 0x67,
+ 0x77, 0x6e, 0x79, 0x70, 0x6f, 0x66, 0x72, 0x6b, 0x75, 0x6f, 0x64, 0x63, 0x65, 0x6f, 0x67, 0x66,
+ 0x68, 0x65, 0x75, 0x77, 0x64, 0x76, 0x61, 0x66, 0x69, 0x6a, 0x67, 0x62, 0x72, 0x71, 0x64, 0x79,
+ 0x72, 0x61, 0x73, 0x72, 0x75, 0x61, 0x65, 0x63, 0x77, 0x77, 0x77, 0x70, 0x65, 0x65, 0x62, 0x76,
+ 0x65, 0x68, 0x70, 0x77, 0x6c, 0x63, 0x63, 0x62, 0x6a, 0x68, 0x79, 0x64, 0x63, 0x73, 0x6f, 0x66,
+ 0x78, 0x66, 0x78, 0x63, 0x6f, 0x67, 0x74, 0x75, 0x69, 0x73, 0x77, 0x69, 0x6b, 0x78, 0x65, 0x70,
+ 0x6c, 0x66, 0x70, 0x77, 0x62, 0x69, 0x66, 0x74, 0x76, 0x75, 0x6e, 0x64, 0x71, 0x6f, 0x79, 0x62,
+ 0x67, 0x78, 0x77, 0x79, 0x74, 0x62, 0x73, 0x61, 0x6a, 0x67, 0x76, 0x6a, 0x6f, 0x62, 0x65, 0x75,
+ 0x75, 0x74, 0x68, 0x6a, 0x68, 0x6b, 0x65, 0x79, 0x73, 0x75, 0x6c, 0x69, 0x66, 0x68, 0x6c, 0x79,
+ 0x6f, 0x78, 0x75, 0x72, 0x72, 0x61, 0x6c, 0x68, 0x76, 0x64, 0x79, 0x6a, 0x6f, 0x69, 0x62, 0x75,
+ 0x6b, 0x73, 0x71, 0x74, 0x74, 0x79, 0x6d, 0x6d, 0x61, 0x6d, 0x72, 0x78, 0x72, 0x6e, 0x65, 0x61,
+ 0x75, 0x65, 0x65, 0x77, 0x62, 0x62, 0x69, 0x6c, 0x6d, 0x6a, 0x76, 0x78, 0x64, 0x76, 0x61, 0x63,
+ 0x69, 0x71, 0x6b, 0x6d, 0x62, 0x78, 0x6a, 0x6d, 0x6a, 0x78, 0x67, 0x68, 0x79, 0x79, 0x66, 0x71,
+ 0x6b, 0x73, 0x72, 0x76, 0x78, 0x74, 0x63, 0x72, 0x74, 0x76, 0x70, 0x79, 0x68, 0x79, 0x69, 0x62,
+ 0x61, 0x74, 0x6b, 0x79, 0x79, 0x6c, 0x78, 0x62, 0x65, 0x78, 0x71, 0x64, 0x75, 0x73, 0x68, 0x62,
+ 0x68, 0x74, 0x71, 0x70, 0x77, 0x61, 0x6a, 0x65, 0x79, 0x6d, 0x61, 0x68, 0x61, 0x75, 0x6e, 0x6e,
+ 0x6d, 0x73, 0x73, 0x65, 0x77, 0x66, 0x70, 0x78, 0x69, 0x65, 0x64, 0x71, 0x6f, 0x65, 0x62, 0x6e,
+ 0x72, 0x69, 0x61, 0x6e, 0x75, 0x76, 0x79, 0x68, 0x69, 0x64, 0x6e, 0x69, 0x63, 0x6d, 0x68, 0x65,
+ 0x62, 0x63, 0x68, 0x67, 0x79, 0x67, 0x67, 0x66, 0x63, 0x71, 0x69, 0x79, 0x69, 0x6a, 0x77, 0x76,
+ 0x64, 0x6c, 0x73, 0x71, 0x6b, 0x6b, 0x77, 0x63, 0x79, 0x78, 0x62, 0x76, 0x75, 0x73, 0x68, 0x65,
+ 0x64, 0x6b, 0x70, 0x78, 0x75, 0x71, 0x6e, 0x64, 0x70, 0x64, 0x6b, 0x6f, 0x77, 0x63, 0x77, 0x67,
+ 0x74, 0x66, 0x78, 0x73, 0x6c, 0x77, 0x62, 0x68, 0x76, 0x66, 0x70, 0x6e, 0x71, 0x66, 0x73, 0x6f,
+ 0x70, 0x65, 0x77, 0x63, 0x61, 0x71, 0x64, 0x6c, 0x72, 0x6a, 0x71, 0x68, 0x73, 0x66, 0x67, 0x62,
+ 0x70, 0x77, 0x69, 0x67, 0x6e, 0x78, 0x63, 0x75, 0x6a, 0x6b, 0x70, 0x69, 0x6c, 0x76, 0x64, 0x6f,
+ 0x76, 0x63, 0x75, 0x62, 0x77, 0x67, 0x6e, 0x67, 0x67, 0x6d, 0x63, 0x66, 0x6d, 0x67, 0x65, 0x6b,
+ 0x66, 0x73, 0x66, 0x75, 0x66, 0x73, 0x73, 0x6b, 0x6b, 0x78, 0x75, 0x65, 0x75, 0x76, 0x70, 0x76,
+ 0x61, 0x6e, 0x78, 0x66, 0x6c, 0x71, 0x66, 0x62, 0x71, 0x75, 0x79, 0x70, 0x74, 0x79, 0x65, 0x70,
+ 0x66, 0x79, 0x6f, 0x75, 0x70, 0x63, 0x76, 0x75, 0x62, 0x6c, 0x78, 0x68, 0x6f, 0x77, 0x75, 0x6e,
+ 0x79, 0x6d, 0x70, 0x64, 0x71, 0x63, 0x69, 0x75, 0x76, 0x68, 0x71, 0x75, 0x67, 0x79, 0x69, 0x69,
+ 0x77, 0x6d, 0x69, 0x6a, 0x68, 0x6a, 0x6d, 0x6a, 0x70, 0x6c, 0x73, 0x6d, 0x6c, 0x6c, 0x6f, 0x6c,
+ 0x69, 0x71, 0x73, 0x78, 0x6d, 0x61, 0x61, 0x73, 0x6f, 0x66, 0x66, 0x63, 0x71, 0x6b, 0x6d, 0x6f,
+ 0x75, 0x67, 0x71, 0x77, 0x67, 0x73, 0x76, 0x76, 0x6a, 0x76, 0x6c, 0x61, 0x67, 0x66, 0x6a, 0x69,
+ 0x73, 0x71, 0x69, 0x72, 0x72, 0x78, 0x63, 0x6b, 0x76, 0x6f, 0x6b, 0x78, 0x6b, 0x72, 0x6f, 0x6a,
+ 0x64, 0x64, 0x73, 0x74, 0x75, 0x64, 0x6f, 0x75, 0x77, 0x62, 0x64, 0x6a, 0x67, 0x6a, 0x73, 0x69,
+ 0x6a, 0x79, 0x63, 0x77, 0x75, 0x72, 0x75, 0x62, 0x68, 0x73, 0x6c, 0x69, 0x74, 0x64, 0x6d, 0x71,
+ 0x76, 0x76, 0x6e, 0x67, 0x67, 0x65, 0x71, 0x70, 0x6a, 0x79, 0x74, 0x75, 0x76, 0x74, 0x67, 0x79,
+ 0x62, 0x63, 0x68, 0x6c, 0x6c, 0x70, 0x67, 0x65, 0x64, 0x76, 0x72, 0x65, 0x76, 0x6b, 0x69, 0x71,
+ 0x73, 0x79, 0x78, 0x64, 0x70, 0x79, 0x61, 0x76, 0x61, 0x66, 0x74, 0x79, 0x6a, 0x6a, 0x6a, 0x78,
+ 0x6b, 0x6e, 0x74, 0x6f, 0x66, 0x67, 0x62, 0x76, 0x71, 0x65, 0x62, 0x64, 0x73, 0x6f, 0x63, 0x66,
+ 0x66, 0x6c, 0x63, 0x77, 0x6e, 0x72, 0x66, 0x72, 0x74, 0x70, 0x70, 0x6c, 0x6f, 0x72, 0x6b, 0x79,
+ 0x68, 0x67, 0x63, 0x62, 0x79, 0x6f, 0x73, 0x65, 0x62, 0x76, 0x67, 0x65, 0x77, 0x64, 0x6c, 0x61,
+ 0x62, 0x61, 0x64, 0x68, 0x79, 0x72, 0x70, 0x61, 0x77, 0x6d, 0x6a, 0x69, 0x70, 0x6f, 0x77, 0x77,
+ 0x77, 0x6f, 0x6d, 0x78, 0x76, 0x69, 0x69, 0x78, 0x74, 0x68, 0x68, 0x74, 0x6c, 0x6e, 0x66, 0x67,
+ 0x67, 0x64, 0x62, 0x6d, 0x63, 0x71, 0x75, 0x65, 0x75, 0x71, 0x6c, 0x75, 0x62, 0x66, 0x66, 0x61,
+ 0x69, 0x67, 0x79, 0x70, 0x6e, 0x6b, 0x6f, 0x71, 0x62, 0x77, 0x6c, 0x75, 0x67, 0x77, 0x78, 0x79,
+ 0x6e, 0x77, 0x6a, 0x71, 0x71, 0x77, 0x68, 0x72, 0x73, 0x77, 0x69, 0x75, 0x61, 0x62, 0x65, 0x71,
+ 0x6a, 0x78, 0x6e, 0x6c, 0x6c, 0x70, 0x76, 0x74, 0x69, 0x76, 0x61, 0x78, 0x66, 0x76, 0x6e, 0x62,
+ 0x64, 0x71, 0x71, 0x6c, 0x79, 0x65, 0x65, 0x6b, 0x71, 0x6a, 0x73, 0x6e, 0x6b, 0x76, 0x70, 0x79,
+ 0x77, 0x63, 0x63, 0x67, 0x75, 0x6e, 0x66, 0x74, 0x63, 0x76, 0x75, 0x6d, 0x66, 0x6c, 0x63, 0x6b,
+ 0x79, 0x6d, 0x72, 0x69, 0x67, 0x75, 0x65, 0x78, 0x70, 0x6c, 0x71, 0x69, 0x69, 0x76, 0x70, 0x75,
+ 0x6b, 0x6a, 0x64, 0x66, 0x71, 0x71, 0x6b, 0x6a, 0x66, 0x64, 0x79, 0x69, 0x6f, 0x74, 0x62, 0x6c,
+ 0x78, 0x61, 0x77, 0x65, 0x6e, 0x6f, 0x62, 0x70, 0x76, 0x6e, 0x73, 0x73, 0x74, 0x6d, 0x6d, 0x6a,
+ 0x68, 0x66, 0x6f, 0x78, 0x70, 0x76, 0x66, 0x6f, 0x6a, 0x76, 0x6a, 0x6b, 0x6c, 0x6f, 0x64, 0x77,
+ 0x78, 0x61, 0x68, 0x63, 0x72, 0x61, 0x69, 0x78, 0x69, 0x74, 0x78, 0x6c, 0x79, 0x61, 0x61, 0x67,
+ 0x72, 0x6a, 0x6f, 0x76, 0x6d, 0x6d, 0x6c, 0x78, 0x63, 0x63, 0x65, 0x69, 0x67, 0x78, 0x73, 0x76,
+ 0x78, 0x69, 0x6d, 0x70, 0x73, 0x76, 0x78, 0x71, 0x75, 0x65, 0x77, 0x69, 0x73, 0x77, 0x6f, 0x66,
+ 0x65, 0x6b, 0x61, 0x6a, 0x78, 0x70, 0x70, 0x71, 0x71, 0x79, 0x67, 0x79, 0x79, 0x6e, 0x64, 0x6c,
+ 0x6c, 0x6a, 0x6d, 0x6a, 0x79, 0x73, 0x63, 0x72, 0x6a, 0x72, 0x77, 0x6d, 0x74, 0x70, 0x77, 0x6e,
+ 0x77, 0x63, 0x6c, 0x71, 0x74, 0x75, 0x61, 0x79, 0x69, 0x71, 0x6a, 0x64, 0x62, 0x64, 0x68, 0x74,
+ 0x75, 0x75, 0x64, 0x6d, 0x6f, 0x66, 0x64, 0x69, 0x74, 0x73, 0x6c, 0x66, 0x77, 0x64, 0x73, 0x6e,
+ 0x63, 0x6e, 0x73, 0x6d, 0x67, 0x73, 0x76, 0x6b, 0x73, 0x6b, 0x77, 0x6c, 0x6f, 0x70, 0x69, 0x6f,
+ 0x6f, 0x62, 0x63, 0x6c, 0x70, 0x70, 0x6e, 0x61, 0x78, 0x6d, 0x63, 0x74, 0x67, 0x66, 0x79, 0x69,
+ 0x6c, 0x68, 0x71, 0x68, 0x68, 0x75, 0x79, 0x61, 0x62, 0x6b, 0x6b, 0x70, 0x6a, 0x69, 0x75, 0x6a,
+ 0x78, 0x65, 0x74, 0x62, 0x6d, 0x72, 0x6c, 0x74, 0x79, 0x74, 0x66, 0x63, 0x6d, 0x71, 0x68, 0x70,
+ 0x6c, 0x74, 0x79, 0x6e, 0x69, 0x75, 0x66, 0x6d, 0x69, 0x6f, 0x74, 0x68, 0x6c, 0x6b, 0x65, 0x72,
+ 0x69, 0x70, 0x68, 0x72, 0x65, 0x66, 0x6e, 0x72, 0x71, 0x66, 0x6f, 0x6e, 0x68, 0x62, 0x70, 0x6d,
+ 0x6e, 0x75, 0x77, 0x68, 0x74, 0x77, 0x74, 0x61, 0x76, 0x74, 0x79, 0x63, 0x6f, 0x63, 0x6c, 0x64,
+ 0x78, 0x69, 0x6f, 0x6d, 0x66, 0x78, 0x6c, 0x6c, 0x6f, 0x6a, 0x77, 0x6d, 0x74, 0x67, 0x63, 0x64,
+ 0x79, 0x64, 0x6d, 0x61, 0x66, 0x64, 0x79, 0x6a, 0x61, 0x68, 0x63, 0x6d, 0x61, 0x69, 0x70, 0x6e,
+ 0x61, 0x77, 0x6a, 0x64, 0x63, 0x64, 0x70, 0x6d, 0x75, 0x62, 0x6d, 0x74, 0x64, 0x6d, 0x74, 0x67,
+ 0x6d, 0x72, 0x77, 0x79, 0x6f, 0x76, 0x6f, 0x75, 0x62, 0x6b, 0x72, 0x77, 0x66, 0x6b, 0x6c, 0x73,
+ 0x6a, 0x76, 0x69, 0x68, 0x6f, 0x77, 0x62, 0x74, 0x71, 0x66, 0x74, 0x64, 0x63, 0x79, 0x6d, 0x72,
+ 0x76, 0x65, 0x69, 0x6f, 0x74, 0x73, 0x73, 0x6b, 0x68, 0x66, 0x64, 0x72, 0x70, 0x62, 0x73, 0x76,
+ 0x64, 0x6d, 0x70, 0x6c, 0x6d, 0x63, 0x73, 0x73, 0x72, 0x76, 0x69, 0x76, 0x62, 0x64, 0x72, 0x75,
+ 0x76, 0x6a, 0x62, 0x78, 0x69, 0x61, 0x73, 0x68, 0x6a, 0x67, 0x62, 0x6a, 0x64, 0x67, 0x77, 0x76,
+ 0x6c, 0x69, 0x77, 0x74, 0x72, 0x6f, 0x77, 0x66, 0x67, 0x62, 0x6e, 0x6e, 0x66, 0x70, 0x72, 0x64,
+ 0x71, 0x79, 0x66, 0x6f, 0x72, 0x65, 0x75, 0x64, 0x6b, 0x6f, 0x77, 0x73, 0x79, 0x75, 0x78, 0x6a,
+ 0x64, 0x6d, 0x79, 0x77, 0x6b, 0x62, 0x77, 0x62, 0x66, 0x67, 0x73, 0x68, 0x72, 0x62, 0x62, 0x67,
+ 0x6d, 0x6d, 0x6c, 0x78, 0x69, 0x6c, 0x75, 0x77, 0x6d, 0x69, 0x6c, 0x6b, 0x71, 0x64, 0x67, 0x65,
+ 0x73, 0x63, 0x6d, 0x6d, 0x63, 0x65, 0x6a, 0x71, 0x79, 0x6d, 0x63, 0x6e, 0x77, 0x64, 0x63, 0x71,
+ 0x67, 0x77, 0x68, 0x6d, 0x63, 0x61, 0x67, 0x6e, 0x63, 0x71, 0x6a, 0x72, 0x75, 0x61, 0x74, 0x69,
+ 0x6c, 0x76, 0x62, 0x65, 0x78, 0x74, 0x63, 0x67, 0x75, 0x6e, 0x71, 0x69, 0x68, 0x77, 0x69, 0x6b,
+ 0x69, 0x67, 0x70, 0x6c, 0x77, 0x6d, 0x6d, 0x6c, 0x77, 0x77, 0x6d, 0x66, 0x61, 0x6d, 0x66, 0x76,
+ 0x69, 0x62, 0x66, 0x6b, 0x64, 0x68, 0x75, 0x78, 0x67, 0x64, 0x61, 0x6b, 0x78, 0x6c, 0x78, 0x70,
+ 0x77, 0x61, 0x68, 0x65, 0x6d, 0x66, 0x6f, 0x77, 0x78, 0x6f, 0x61, 0x6e, 0x6e, 0x77, 0x67, 0x6b,
+ 0x62, 0x70, 0x6b, 0x69, 0x61, 0x67, 0x6a, 0x76, 0x68, 0x74, 0x61, 0x6d, 0x70, 0x69, 0x70, 0x6a,
+ 0x68, 0x71, 0x69, 0x63, 0x74, 0x6a, 0x6a, 0x73, 0x69, 0x64, 0x64, 0x75, 0x79, 0x6a, 0x65, 0x6d,
+ 0x71, 0x62, 0x68, 0x77, 0x74, 0x65, 0x68, 0x6b, 0x78, 0x69, 0x6e, 0x73, 0x65, 0x66, 0x6e, 0x64,
+ 0x6b, 0x79, 0x66, 0x61, 0x78, 0x63, 0x6a, 0x6b, 0x68, 0x76, 0x74, 0x6f, 0x72, 0x6d, 0x73, 0x6c,
+ 0x67, 0x6a, 0x72, 0x78, 0x75, 0x6f, 0x6d, 0x6e, 0x61, 0x6d, 0x66, 0x6b, 0x65, 0x72, 0x70, 0x62,
+ 0x67, 0x78, 0x64, 0x74, 0x77, 0x6c, 0x67, 0x78, 0x71, 0x75, 0x71, 0x62, 0x61, 0x6d, 0x79, 0x70,
+ 0x74, 0x71, 0x6e, 0x6a, 0x70, 0x61, 0x74, 0x6a, 0x6f, 0x6a, 0x78, 0x6d, 0x72, 0x61, 0x67, 0x76,
+ 0x71, 0x71, 0x75, 0x61, 0x62, 0x6d, 0x62, 0x67, 0x6a, 0x61, 0x73, 0x73, 0x70, 0x64, 0x69, 0x74,
+ 0x6e, 0x62, 0x72, 0x72, 0x6d, 0x78, 0x72, 0x76, 0x6a, 0x6b, 0x70, 0x6d, 0x72, 0x77, 0x61, 0x63,
+ 0x79, 0x64, 0x6b, 0x6e, 0x72, 0x6b, 0x61, 0x6e, 0x77, 0x70, 0x6f, 0x6e, 0x68, 0x6c, 0x71, 0x62,
+ 0x74, 0x64, 0x63, 0x67, 0x65, 0x67, 0x67, 0x6e, 0x6d, 0x66, 0x62, 0x72, 0x71, 0x74, 0x76, 0x79,
+ 0x70, 0x72, 0x6f, 0x6f, 0x63, 0x79, 0x72, 0x63, 0x73, 0x64, 0x68, 0x63, 0x73, 0x6c, 0x6f, 0x6c,
+ 0x66, 0x71, 0x6a, 0x75, 0x69, 0x6e, 0x67, 0x72, 0x74, 0x6a, 0x61, 0x73, 0x68, 0x69, 0x6d, 0x71,
+ 0x76, 0x73, 0x65, 0x6f, 0x63, 0x62, 0x71, 0x6d, 0x66, 0x71, 0x69, 0x61, 0x76, 0x6d, 0x76, 0x6f,
+ 0x64, 0x6c, 0x6c, 0x74, 0x6c, 0x74, 0x76, 0x6b, 0x63, 0x62, 0x79, 0x70, 0x6e, 0x64, 0x64, 0x6e,
+ 0x71, 0x73, 0x71, 0x6d, 0x71, 0x67, 0x6a, 0x69, 0x66, 0x76, 0x6e, 0x73, 0x6f, 0x77, 0x67, 0x6f,
+ 0x65, 0x71, 0x73, 0x70, 0x61, 0x6b, 0x67, 0x69, 0x6b, 0x75, 0x68, 0x6d, 0x6a, 0x6c, 0x61, 0x70,
+ 0x71, 0x74, 0x66, 0x63, 0x61, 0x67, 0x77, 0x77, 0x68, 0x61, 0x61, 0x6a, 0x74, 0x72, 0x6d, 0x75,
+ 0x75, 0x64, 0x69, 0x70, 0x64, 0x62, 0x6b, 0x75, 0x6c, 0x73, 0x68, 0x65, 0x6b, 0x74, 0x74, 0x6e,
+ 0x6c, 0x61, 0x6c, 0x74, 0x71, 0x6c, 0x79, 0x69, 0x65, 0x76, 0x74, 0x77, 0x76, 0x69, 0x66, 0x78,
+ 0x68, 0x79, 0x64, 0x79, 0x61, 0x76, 0x68, 0x70, 0x78, 0x73, 0x68, 0x6d, 0x6a, 0x67, 0x65, 0x76,
+ 0x73, 0x66, 0x6b, 0x6b, 0x70, 0x68, 0x64, 0x79, 0x76, 0x71, 0x77, 0x63, 0x65, 0x6a, 0x6e, 0x6f,
+ 0x6f, 0x75, 0x6e, 0x65, 0x6a, 0x64, 0x6c, 0x69, 0x6e, 0x70, 0x6f, 0x6d, 0x66, 0x76, 0x76, 0x66,
+ 0x62, 0x6c, 0x78, 0x71, 0x71, 0x73, 0x63, 0x6e, 0x72, 0x6d, 0x6a, 0x74, 0x67, 0x77, 0x78, 0x74,
+ 0x65, 0x66, 0x6d, 0x72, 0x73, 0x6f, 0x64, 0x6e, 0x66, 0x71, 0x61, 0x6c, 0x72, 0x77, 0x67, 0x71,
+ 0x6d, 0x74, 0x69, 0x72, 0x61, 0x6b, 0x78, 0x67, 0x78, 0x73, 0x70, 0x6b, 0x63, 0x76, 0x6f, 0x65,
+ 0x6b, 0x70, 0x74, 0x71, 0x75, 0x67, 0x78, 0x6c, 0x6b, 0x79, 0x76, 0x71, 0x63, 0x6b, 0x6a, 0x6e,
+ 0x61, 0x66, 0x6f, 0x69, 0x6f, 0x64, 0x6d, 0x6e, 0x61, 0x76, 0x69, 0x6f, 0x78, 0x68, 0x61, 0x74,
+ 0x6d, 0x68, 0x72, 0x64, 0x63, 0x79, 0x78, 0x76, 0x6c, 0x6e, 0x6f, 0x6a, 0x72, 0x6b, 0x6b, 0x70,
+ 0x65, 0x65, 0x6c, 0x63, 0x66, 0x73, 0x66, 0x68, 0x6e, 0x74, 0x6d, 0x64, 0x62, 0x6f, 0x6f, 0x67,
+ 0x79, 0x6d, 0x69, 0x61, 0x78, 0x6b, 0x6b, 0x61, 0x6e, 0x76, 0x6b, 0x70, 0x6e, 0x6d, 0x68, 0x67,
+ 0x72, 0x72, 0x69, 0x74, 0x78, 0x6a, 0x78, 0x75, 0x68, 0x71, 0x70, 0x6b, 0x6b, 0x64, 0x71, 0x63,
+ 0x65, 0x6c, 0x6c, 0x69, 0x72, 0x76, 0x70, 0x6f, 0x70, 0x6e, 0x73, 0x74, 0x6a, 0x61, 0x75, 0x63,
+ 0x61, 0x78, 0x6f, 0x79, 0x67, 0x65, 0x61, 0x69, 0x6d, 0x68, 0x6c, 0x62, 0x75, 0x79, 0x68, 0x79,
+ 0x65, 0x6a, 0x67, 0x6f, 0x68, 0x78, 0x70, 0x69, 0x62, 0x6b, 0x6b, 0x64, 0x77, 0x78, 0x6b, 0x76,
+ 0x69, 0x64, 0x75, 0x70, 0x72, 0x6e, 0x67, 0x73, 0x65, 0x73, 0x71, 0x73, 0x70, 0x78, 0x66, 0x6b,
+ 0x6e, 0x6b, 0x6a, 0x6f, 0x73, 0x6b, 0x70, 0x71, 0x69, 0x63, 0x6f, 0x73, 0x69, 0x70, 0x6f, 0x6b,
+ 0x69, 0x75, 0x61, 0x70, 0x72, 0x65, 0x6a, 0x75, 0x61, 0x67, 0x75, 0x76, 0x65, 0x6d, 0x6c, 0x73,
+ 0x71, 0x6b, 0x73, 0x65, 0x79, 0x68, 0x70, 0x69, 0x6f, 0x69, 0x6c, 0x6b, 0x72, 0x64, 0x6c, 0x79,
+ 0x6b, 0x6a, 0x61, 0x6e, 0x6b, 0x6b, 0x74, 0x6f, 0x79, 0x64, 0x68, 0x68, 0x6c, 0x6f, 0x62, 0x70,
+ 0x6e, 0x79, 0x62, 0x62, 0x6b, 0x66, 0x6e, 0x6f, 0x70, 0x64, 0x66, 0x75, 0x73, 0x79, 0x62, 0x75,
+ 0x6d, 0x67, 0x73, 0x73, 0x6b, 0x71, 0x6e, 0x6d, 0x75, 0x62, 0x72, 0x6a, 0x6b, 0x63, 0x6d, 0x6c,
+ 0x6f, 0x6c, 0x74, 0x65, 0x71, 0x65, 0x6b, 0x77, 0x61, 0x66, 0x78, 0x6c, 0x78, 0x6d, 0x6e, 0x6d,
+ 0x6a, 0x71, 0x62, 0x64, 0x68, 0x67, 0x61, 0x6a, 0x68, 0x72, 0x72, 0x62, 0x6e, 0x6b, 0x71, 0x6d,
+ 0x66, 0x75, 0x6a, 0x78, 0x64, 0x66, 0x75, 0x74, 0x6f, 0x6a, 0x74, 0x73, 0x75, 0x62, 0x71, 0x6b,
+ 0x6d, 0x66, 0x65, 0x62, 0x64, 0x68, 0x79, 0x74, 0x6f, 0x75, 0x76, 0x66, 0x79, 0x70, 0x77, 0x65,
+ 0x78, 0x72, 0x65, 0x6e, 0x6a, 0x62, 0x61, 0x74, 0x65, 0x73, 0x68, 0x6e, 0x72, 0x75, 0x62, 0x64,
+ 0x69, 0x73, 0x65, 0x6b, 0x61, 0x6f, 0x6e, 0x71, 0x61, 0x6e, 0x64, 0x6e, 0x76, 0x73, 0x65, 0x63,
+ 0x6c, 0x6b, 0x6d, 0x77, 0x66, 0x6b, 0x72, 0x72, 0x78, 0x72, 0x6b, 0x6c, 0x69, 0x69, 0x79, 0x76,
+ 0x6c, 0x78, 0x75, 0x73, 0x65, 0x6d, 0x6e, 0x74, 0x68, 0x65, 0x76, 0x70, 0x64, 0x6b, 0x77, 0x6c,
+ 0x77, 0x6d, 0x66, 0x79, 0x64, 0x64, 0x6f, 0x74, 0x62, 0x6d, 0x6b, 0x61, 0x78, 0x6a, 0x73, 0x63,
+ 0x6e, 0x6a, 0x62, 0x73, 0x72, 0x6e, 0x6c, 0x62, 0x75, 0x79, 0x62, 0x66, 0x69, 0x72, 0x6c, 0x67,
+ 0x78, 0x61, 0x76, 0x62, 0x6e, 0x66, 0x63, 0x69, 0x68, 0x73, 0x78, 0x78, 0x74, 0x61, 0x78, 0x71,
+ 0x6e, 0x70, 0x75, 0x77, 0x63, 0x64, 0x61, 0x6f, 0x75, 0x68, 0x76, 0x71, 0x64, 0x72, 0x74, 0x75,
+ 0x6c, 0x73, 0x72, 0x75, 0x63, 0x61, 0x74, 0x61, 0x77, 0x74, 0x64, 0x65, 0x74, 0x68, 0x74, 0x78,
+ 0x63, 0x65, 0x79, 0x75, 0x6e, 0x6d, 0x78, 0x77, 0x67, 0x65, 0x6e, 0x74, 0x66, 0x76, 0x6c, 0x78,
+ 0x66, 0x76, 0x62, 0x76, 0x68, 0x65, 0x6f, 0x73, 0x78, 0x76, 0x69, 0x6c, 0x63, 0x76, 0x70, 0x79,
+ 0x6d, 0x75, 0x6e, 0x69, 0x61, 0x66, 0x65, 0x78, 0x6d, 0x68, 0x62, 0x73, 0x66, 0x73, 0x6b, 0x6c,
+ 0x77, 0x64, 0x6c, 0x72, 0x78, 0x75, 0x6c, 0x63, 0x6a, 0x64, 0x68, 0x79, 0x6f, 0x74, 0x65, 0x6d,
+ 0x65, 0x70, 0x6d, 0x70, 0x68, 0x65, 0x65, 0x65, 0x6c, 0x74, 0x6b, 0x69, 0x74, 0x6a, 0x71, 0x64,
+ 0x72, 0x74, 0x6e, 0x74, 0x75, 0x6a, 0x72, 0x73, 0x6b, 0x69, 0x6c, 0x71, 0x78, 0x77, 0x75, 0x6c,
+ 0x69, 0x62, 0x6e, 0x6a, 0x68, 0x73, 0x77, 0x77, 0x63, 0x66, 0x6a, 0x78, 0x6d, 0x77, 0x6e, 0x62,
+ 0x6c, 0x76, 0x66, 0x66, 0x75, 0x65, 0x6e, 0x69, 0x79, 0x75, 0x72, 0x6d, 0x70, 0x71, 0x65, 0x67,
+ 0x74, 0x75, 0x6a, 0x6d, 0x66, 0x64, 0x72, 0x66, 0x77, 0x70, 0x76, 0x75, 0x73, 0x75, 0x6c, 0x73,
+ 0x75, 0x6b, 0x71, 0x6f, 0x79, 0x6a, 0x62, 0x78, 0x69, 0x62, 0x72, 0x61, 0x6a, 0x69, 0x67, 0x71,
+ 0x66, 0x71, 0x6a, 0x63, 0x62, 0x79, 0x78, 0x69, 0x61, 0x63, 0x6a, 0x62, 0x6a, 0x63, 0x68, 0x79,
+ 0x68, 0x69, 0x63, 0x73, 0x65, 0x67, 0x70, 0x74, 0x72, 0x6f, 0x79, 0x75, 0x65, 0x73, 0x6a, 0x76,
+ 0x6f, 0x64, 0x66, 0x72, 0x6d, 0x71, 0x66, 0x6d, 0x62, 0x6a, 0x68, 0x74, 0x67, 0x68, 0x6e, 0x6b,
+ 0x67, 0x6c, 0x6e, 0x72, 0x79, 0x6e, 0x6b, 0x6a, 0x66, 0x6a, 0x65, 0x69, 0x75, 0x6a, 0x6e, 0x6a,
+ 0x61, 0x62, 0x79, 0x6c, 0x65, 0x74, 0x63, 0x76, 0x63, 0x6a, 0x68, 0x76, 0x6d, 0x69, 0x66, 0x68,
+ 0x67, 0x71, 0x66, 0x68, 0x66, 0x75, 0x76, 0x6e, 0x74, 0x70, 0x63, 0x67, 0x73, 0x68, 0x67, 0x61,
+ 0x74, 0x66, 0x65, 0x6e, 0x6b, 0x6c, 0x64, 0x6b, 0x70, 0x70, 0x75, 0x78, 0x76, 0x68, 0x74, 0x69,
+ 0x76, 0x77, 0x6e, 0x6c, 0x77, 0x76, 0x66, 0x6a, 0x61, 0x6a, 0x63, 0x6a, 0x6e, 0x73, 0x67, 0x73,
+ 0x6b, 0x6e, 0x75, 0x6a, 0x6a, 0x70, 0x73, 0x66, 0x78, 0x79, 0x61, 0x79, 0x6a, 0x6e, 0x76, 0x74,
+ 0x65, 0x74, 0x79, 0x6d, 0x6e, 0x79, 0x75, 0x76, 0x72, 0x6f, 0x6a, 0x67, 0x61, 0x66, 0x6a, 0x72,
+ 0x65, 0x79, 0x61, 0x6b, 0x65, 0x6b, 0x67, 0x77, 0x6a, 0x69, 0x73, 0x64, 0x6e, 0x6a, 0x68, 0x68,
+ 0x73, 0x76, 0x70, 0x78, 0x79, 0x6c, 0x71, 0x78, 0x6b, 0x77, 0x6b, 0x72, 0x70, 0x64, 0x75, 0x64,
+ 0x6a, 0x63, 0x69, 0x79, 0x70, 0x64, 0x72, 0x78, 0x72, 0x70, 0x79, 0x6a, 0x61, 0x6f, 0x73, 0x64,
+ 0x76, 0x79, 0x77, 0x71, 0x69, 0x75, 0x69, 0x6a, 0x77, 0x65, 0x77, 0x72, 0x6b, 0x61, 0x65, 0x78,
+ 0x65, 0x64, 0x66, 0x62, 0x6f, 0x72, 0x74, 0x72, 0x73, 0x71, 0x64, 0x77, 0x76, 0x70, 0x6c, 0x73,
+ 0x6e, 0x68, 0x6a, 0x62, 0x6d, 0x6f, 0x70, 0x78, 0x64, 0x6e, 0x65, 0x6f, 0x6d, 0x65, 0x64, 0x71,
+ 0x70, 0x6c, 0x6c, 0x6d, 0x6e, 0x71, 0x65, 0x66, 0x63, 0x74, 0x62, 0x71, 0x6a, 0x6c, 0x6b, 0x70,
+ 0x6a, 0x65, 0x6e, 0x6c, 0x6c, 0x69, 0x6e, 0x61, 0x79, 0x73, 0x78, 0x72, 0x65, 0x69, 0x78, 0x62,
+ 0x6f, 0x79, 0x6e, 0x63, 0x74, 0x6c, 0x65, 0x66, 0x72, 0x6d, 0x6a, 0x6c, 0x6b, 0x62, 0x6b, 0x6c,
+ 0x70, 0x65, 0x64, 0x70, 0x67, 0x71, 0x6c, 0x77, 0x6b, 0x66, 0x62, 0x77, 0x72, 0x6d, 0x6a, 0x62,
+ 0x61, 0x62, 0x65, 0x71, 0x78, 0x67, 0x62, 0x66, 0x69, 0x70, 0x79, 0x6b, 0x66, 0x6f, 0x62, 0x6c,
+ 0x61, 0x73, 0x61, 0x6c, 0x64, 0x62, 0x61, 0x70, 0x61, 0x63, 0x73, 0x6d, 0x76, 0x79, 0x68, 0x66,
+ 0x66, 0x6f, 0x66, 0x6f, 0x6a, 0x63, 0x61, 0x66, 0x75, 0x75, 0x61, 0x72, 0x61, 0x6a, 0x71, 0x6b,
+ 0x62, 0x65, 0x6c, 0x62, 0x62, 0x74, 0x69, 0x75, 0x69, 0x6f, 0x77, 0x65, 0x73, 0x72, 0x63, 0x72,
+ 0x62, 0x78, 0x75, 0x75, 0x6d, 0x70, 0x6c, 0x75, 0x6e, 0x72, 0x6f, 0x6c, 0x61, 0x61, 0x6d, 0x79,
+ 0x65, 0x71, 0x68, 0x6a, 0x6e, 0x6e, 0x6d, 0x69, 0x68, 0x75, 0x65, 0x6a, 0x72, 0x61, 0x77, 0x75,
+ 0x69, 0x73, 0x76, 0x6b, 0x64, 0x77, 0x75, 0x61, 0x68, 0x74, 0x65, 0x63, 0x69, 0x6e, 0x62, 0x6f,
+ 0x63, 0x79, 0x65, 0x65, 0x6e, 0x66, 0x6f, 0x6c, 0x61, 0x78, 0x69, 0x68, 0x73, 0x62, 0x61, 0x6f,
+ 0x65, 0x75, 0x70, 0x6a, 0x68, 0x72, 0x6e, 0x66, 0x62, 0x6a, 0x74, 0x61, 0x63, 0x72, 0x6e, 0x65,
+ 0x65, 0x67, 0x71, 0x79, 0x61, 0x63, 0x72, 0x6f, 0x77, 0x77, 0x77, 0x74, 0x67, 0x77, 0x68, 0x6c,
+ 0x72, 0x64, 0x61, 0x77, 0x64, 0x62, 0x6f, 0x63, 0x67, 0x77, 0x6a, 0x6b, 0x71, 0x78, 0x75, 0x6b,
+ 0x6a, 0x77, 0x76, 0x75, 0x6e, 0x6d, 0x61, 0x67, 0x74, 0x6c, 0x62, 0x75, 0x78, 0x63, 0x6a, 0x71,
+ 0x6f, 0x6f, 0x77, 0x6f, 0x74, 0x6c, 0x66, 0x73, 0x78, 0x75, 0x78, 0x62, 0x66, 0x62, 0x70, 0x78,
+ 0x65, 0x6a, 0x77, 0x71, 0x6c, 0x71, 0x65, 0x79, 0x79, 0x70, 0x6b, 0x70, 0x79, 0x6b, 0x78, 0x69,
+ 0x6d, 0x65, 0x6c, 0x66, 0x78, 0x73, 0x6e, 0x6a, 0x72, 0x77, 0x69, 0x76, 0x68, 0x78, 0x6b, 0x71,
+ 0x70, 0x76, 0x63, 0x61, 0x62, 0x79, 0x6f, 0x64, 0x6e, 0x72, 0x65, 0x72, 0x69, 0x69, 0x64, 0x65,
+ 0x6b, 0x71, 0x68, 0x6b, 0x6d, 0x6c, 0x6c, 0x62, 0x72, 0x71, 0x72, 0x65, 0x61, 0x69, 0x65, 0x71,
+ 0x6c, 0x6d, 0x66, 0x69, 0x67, 0x71, 0x6d, 0x6d, 0x71, 0x69, 0x6f, 0x69, 0x68, 0x6d, 0x61, 0x74,
+ 0x6f, 0x65, 0x6c, 0x68, 0x67, 0x79, 0x6b, 0x63, 0x69, 0x78, 0x61, 0x6f, 0x65, 0x6c, 0x79, 0x64,
+ 0x68, 0x66, 0x72, 0x6a, 0x6d, 0x64, 0x6d, 0x71, 0x6c, 0x64, 0x6b, 0x6d, 0x64, 0x74, 0x64, 0x62,
+ 0x72, 0x64, 0x67, 0x71, 0x68, 0x61, 0x76, 0x70, 0x69, 0x61, 0x73, 0x6a, 0x72, 0x62, 0x66, 0x75,
+ 0x64, 0x77, 0x70, 0x6e, 0x72, 0x6f, 0x76, 0x6f, 0x68, 0x73, 0x6c, 0x6a, 0x61, 0x78, 0x72, 0x73,
+ 0x70, 0x76, 0x76, 0x6b, 0x75, 0x71, 0x65, 0x6d, 0x6a, 0x67, 0x73, 0x64, 0x62, 0x68, 0x79, 0x74,
+ 0x71, 0x70, 0x66, 0x77, 0x6e, 0x72, 0x65, 0x75, 0x72, 0x74, 0x76, 0x72, 0x78, 0x64, 0x73, 0x68,
+ 0x71, 0x65, 0x74, 0x67, 0x6b, 0x67, 0x64, 0x61, 0x64, 0x63, 0x6e, 0x71, 0x6e, 0x74, 0x76, 0x67,
+ 0x6b, 0x6c, 0x6c, 0x63, 0x6e, 0x70, 0x72, 0x76, 0x65, 0x72, 0x6c, 0x6f, 0x64, 0x71, 0x66, 0x78,
+ 0x66, 0x6c, 0x62, 0x76, 0x75, 0x69, 0x65, 0x70, 0x75, 0x76, 0x77, 0x6e, 0x78, 0x6a, 0x6f, 0x6e,
+ 0x67, 0x73, 0x6b, 0x61, 0x6b, 0x6a, 0x6a, 0x72, 0x6f, 0x69, 0x77, 0x61, 0x61, 0x75, 0x6f, 0x71,
+ 0x76, 0x76, 0x70, 0x71, 0x75, 0x63, 0x75, 0x6d, 0x6e, 0x74, 0x68, 0x63, 0x64, 0x6c, 0x65, 0x74,
+ 0x77, 0x64, 0x72, 0x67, 0x73, 0x72, 0x65, 0x61, 0x64, 0x70, 0x71, 0x76, 0x6a, 0x79, 0x6f, 0x6e,
+ 0x61, 0x71, 0x63, 0x76, 0x78, 0x71, 0x63, 0x77, 0x66, 0x75, 0x78, 0x67, 0x6e, 0x66, 0x6c, 0x61,
+ 0x6d, 0x78, 0x79, 0x61, 0x67, 0x6c, 0x79, 0x6e, 0x68, 0x6d, 0x66, 0x67, 0x61, 0x75, 0x76, 0x73,
+ 0x6c, 0x6c, 0x76, 0x6c, 0x6f, 0x61, 0x71, 0x61, 0x73, 0x72, 0x69, 0x6e, 0x64, 0x78, 0x73, 0x79,
+ 0x64, 0x63, 0x69, 0x61, 0x71, 0x65, 0x74, 0x65, 0x6b, 0x6d, 0x75, 0x74, 0x61, 0x64, 0x77, 0x65,
+ 0x78, 0x71, 0x64, 0x65, 0x6b, 0x71, 0x6d, 0x6b, 0x70, 0x6c, 0x62, 0x68, 0x64, 0x62, 0x63, 0x62,
+ 0x6c, 0x70, 0x6b, 0x72, 0x78, 0x62, 0x79, 0x68, 0x6e, 0x79, 0x6a, 0x65, 0x6d, 0x69, 0x64, 0x79,
+ 0x6d, 0x6a, 0x69, 0x6d, 0x79, 0x69, 0x6e, 0x76, 0x6d, 0x74, 0x71, 0x79, 0x74, 0x6a, 0x70, 0x6f,
+ 0x79, 0x6c, 0x6e, 0x71, 0x63, 0x77, 0x76, 0x64, 0x66, 0x6c, 0x74, 0x64, 0x68, 0x65, 0x75, 0x72,
+ 0x73, 0x6a, 0x65, 0x67, 0x73, 0x69, 0x6b, 0x6f, 0x64, 0x62, 0x61, 0x74, 0x70, 0x73, 0x6f, 0x6a,
+ 0x6a, 0x6b, 0x6a, 0x67, 0x6a, 0x66, 0x72, 0x6e, 0x73, 0x62, 0x65, 0x77, 0x6a, 0x70, 0x65, 0x69,
+ 0x6a, 0x70, 0x61, 0x62, 0x6f, 0x63, 0x62, 0x77, 0x77, 0x6b, 0x65, 0x75, 0x63, 0x65, 0x61, 0x65,
+ 0x68, 0x66, 0x67, 0x75, 0x61, 0x67, 0x79, 0x71, 0x6a, 0x66, 0x65, 0x73, 0x6c, 0x64, 0x70, 0x65,
+ 0x68, 0x6e, 0x71, 0x6b, 0x74, 0x67, 0x77, 0x75, 0x6a, 0x79, 0x74, 0x70, 0x79, 0x6d, 0x62, 0x6e,
+ 0x71, 0x75, 0x72, 0x62, 0x6b, 0x6d, 0x68, 0x71, 0x78, 0x66, 0x67, 0x75, 0x6a, 0x6f, 0x6f, 0x67,
+ 0x6b, 0x62, 0x67, 0x63, 0x63, 0x71, 0x62, 0x79, 0x68, 0x6e, 0x77, 0x70, 0x68, 0x73, 0x64, 0x76,
+ 0x65, 0x75, 0x6e, 0x66, 0x6a, 0x75, 0x61, 0x63, 0x6b, 0x73, 0x62, 0x61, 0x71, 0x69, 0x6b, 0x70,
+ 0x6c, 0x76, 0x79, 0x77, 0x6c, 0x6c, 0x64, 0x6e, 0x6d, 0x79, 0x71, 0x62, 0x70, 0x6f, 0x6e, 0x6d,
+ 0x73, 0x74, 0x69, 0x76, 0x6f, 0x74, 0x71, 0x73, 0x78, 0x6c, 0x61, 0x70, 0x78, 0x78, 0x6f, 0x67,
+ 0x68, 0x70, 0x72, 0x64, 0x71, 0x6d, 0x6a, 0x67, 0x75, 0x61, 0x78, 0x71, 0x74, 0x73, 0x63, 0x6c,
+ 0x65, 0x66, 0x6c, 0x72, 0x6d, 0x77, 0x6f, 0x62, 0x77, 0x70, 0x68, 0x68, 0x72, 0x72, 0x66, 0x73,
+ 0x74, 0x79, 0x69, 0x79, 0x65, 0x79, 0x68, 0x67, 0x69, 0x71, 0x74, 0x75, 0x65, 0x6c, 0x62, 0x61,
+ 0x63, 0x64, 0x74, 0x63, 0x76, 0x77, 0x79, 0x65, 0x73, 0x63, 0x73, 0x6c, 0x6d, 0x6b, 0x6a, 0x78,
+ 0x6c, 0x67, 0x76, 0x64, 0x68, 0x73, 0x63, 0x78, 0x66, 0x73, 0x78, 0x67, 0x75, 0x70, 0x63, 0x63,
+ 0x61, 0x77, 0x6f, 0x68, 0x6d, 0x6d, 0x74, 0x6a, 0x66, 0x62, 0x61, 0x76, 0x62, 0x63, 0x6c, 0x6f,
+ 0x68, 0x6f, 0x74, 0x69, 0x6a, 0x6b, 0x6c, 0x70, 0x78, 0x6a, 0x75, 0x63, 0x67, 0x6d, 0x6e, 0x6e,
+ 0x6f, 0x63, 0x69, 0x64, 0x66, 0x72, 0x69, 0x6d, 0x6d, 0x6a, 0x74, 0x74, 0x62, 0x6a, 0x61, 0x67,
+ 0x79, 0x6f, 0x78, 0x62, 0x6e, 0x63, 0x64, 0x74, 0x71, 0x75, 0x6d, 0x73, 0x72, 0x67, 0x6f, 0x72,
+ 0x6d, 0x70, 0x6f, 0x6c, 0x6b, 0x66, 0x69, 0x79, 0x63, 0x79, 0x79, 0x63, 0x66, 0x70, 0x6c, 0x61,
+ 0x6a, 0x61, 0x62, 0x76, 0x70, 0x71, 0x73, 0x71, 0x72, 0x63, 0x77, 0x78, 0x62, 0x6b, 0x75, 0x68,
+ 0x69, 0x75, 0x6b, 0x77, 0x71, 0x6e, 0x61, 0x6e, 0x62, 0x79, 0x6d, 0x61, 0x6e, 0x63, 0x6a, 0x74,
+ 0x77, 0x6d, 0x78, 0x64, 0x75, 0x6b, 0x79, 0x79, 0x73, 0x62, 0x67, 0x62, 0x75, 0x6f, 0x72, 0x67,
+ 0x6f, 0x76, 0x73, 0x68, 0x66, 0x6f, 0x78, 0x72, 0x69, 0x64, 0x61, 0x66, 0x69, 0x6c, 0x6a, 0x71,
+ 0x73, 0x75, 0x6a, 0x69, 0x71, 0x64, 0x6c, 0x61, 0x72, 0x67, 0x71, 0x76, 0x6d, 0x64, 0x74, 0x72,
+ 0x78, 0x67, 0x6d, 0x70, 0x65, 0x79, 0x61, 0x6d, 0x79, 0x6a, 0x68, 0x67, 0x6c, 0x64, 0x70, 0x74,
+ 0x62, 0x70, 0x6c, 0x6d, 0x72, 0x6e, 0x72, 0x68, 0x70, 0x78, 0x6f, 0x6e, 0x6b, 0x72, 0x66, 0x77,
+ 0x72, 0x61, 0x64, 0x68, 0x6b, 0x61, 0x6a, 0x6d, 0x77, 0x6c, 0x63, 0x78, 0x6c, 0x65, 0x63, 0x74,
+ 0x78, 0x76, 0x68, 0x61, 0x6f, 0x6b, 0x64, 0x66, 0x73, 0x72, 0x73, 0x66, 0x65, 0x67, 0x6d, 0x70,
+ 0x69, 0x63, 0x75, 0x6a, 0x79, 0x6e, 0x6d, 0x79, 0x63, 0x71, 0x61, 0x75, 0x76, 0x79, 0x73, 0x66,
+ 0x76, 0x61, 0x6c, 0x71, 0x69, 0x71, 0x6b, 0x66, 0x65, 0x78, 0x78, 0x63, 0x6d, 0x78, 0x6c, 0x6e,
+ 0x6e, 0x79, 0x69, 0x6a, 0x6f, 0x75, 0x64, 0x6c, 0x65, 0x71, 0x68, 0x78, 0x67, 0x73, 0x76, 0x75,
+ 0x70, 0x6c, 0x6d, 0x6f, 0x6c, 0x64, 0x67, 0x73, 0x74, 0x73, 0x6b, 0x6b, 0x78, 0x68, 0x77, 0x62,
+ 0x74, 0x79, 0x72, 0x6f, 0x6f, 0x76, 0x73, 0x74, 0x76, 0x67, 0x6d, 0x6c, 0x71, 0x75, 0x61, 0x6a,
+ 0x69, 0x77, 0x6d, 0x68, 0x72, 0x72, 0x6b, 0x6b, 0x69, 0x6c, 0x6c, 0x63, 0x68, 0x62, 0x76, 0x6c,
+ 0x78, 0x69, 0x79, 0x62, 0x79, 0x68, 0x71, 0x62, 0x70, 0x72, 0x6e, 0x66, 0x71, 0x71, 0x67, 0x63,
+ 0x69, 0x67, 0x70, 0x6b, 0x76, 0x69, 0x79, 0x78, 0x73, 0x74, 0x69, 0x67, 0x6a, 0x78, 0x63, 0x66,
+ 0x65, 0x65, 0x64, 0x65, 0x72, 0x6d, 0x67, 0x72, 0x64, 0x70, 0x73, 0x66, 0x6e, 0x73, 0x70, 0x65,
+ 0x6e, 0x61, 0x68, 0x66, 0x66, 0x6a, 0x75, 0x6a, 0x65, 0x66, 0x70, 0x65, 0x66, 0x6b, 0x77, 0x63,
+ 0x62, 0x6c, 0x61, 0x73, 0x77, 0x70, 0x65, 0x72, 0x61, 0x69, 0x76, 0x62, 0x6b, 0x72, 0x64, 0x69,
+ 0x61, 0x62, 0x6d, 0x79, 0x64, 0x76, 0x61, 0x76, 0x69, 0x72, 0x64, 0x73, 0x76, 0x66, 0x67, 0x6b,
+ 0x71, 0x75, 0x6b, 0x75, 0x76, 0x68, 0x77, 0x63, 0x79, 0x76, 0x6c, 0x69, 0x67, 0x69, 0x76, 0x6d,
+ 0x79, 0x6c, 0x62, 0x65, 0x66, 0x76, 0x73, 0x78, 0x73, 0x75, 0x6e, 0x68, 0x6e, 0x61, 0x79, 0x75,
+ 0x6f, 0x77, 0x71, 0x6d, 0x62, 0x74, 0x63, 0x67, 0x66, 0x68, 0x63, 0x79, 0x72, 0x75, 0x6a, 0x78,
+ 0x79, 0x75, 0x66, 0x75, 0x6e, 0x61, 0x6a, 0x6b, 0x72, 0x75, 0x6f, 0x6f, 0x6f, 0x75, 0x78, 0x70,
+ 0x71, 0x68, 0x76, 0x67, 0x65, 0x63, 0x78, 0x78, 0x6d, 0x73, 0x63, 0x63, 0x74, 0x67, 0x71, 0x6c,
+ 0x75, 0x78, 0x74, 0x6e, 0x66, 0x62, 0x6e, 0x63, 0x78, 0x75, 0x64, 0x78, 0x69, 0x67, 0x6b, 0x64,
+ 0x6d, 0x6f, 0x79, 0x74, 0x61, 0x6f, 0x64, 0x67, 0x71, 0x77, 0x62, 0x63, 0x6d, 0x64, 0x76, 0x67,
+ 0x63, 0x79, 0x75, 0x74, 0x6f, 0x68, 0x73, 0x77, 0x69, 0x76, 0x6e, 0x68, 0x6a, 0x6a, 0x79, 0x75,
+ 0x65, 0x68, 0x69, 0x6e, 0x6d, 0x6a, 0x69, 0x68, 0x65, 0x6c, 0x77, 0x62, 0x61, 0x6b, 0x6d, 0x77,
+ 0x78, 0x70, 0x6a, 0x73, 0x79, 0x6b, 0x63, 0x6e, 0x79, 0x6d, 0x65, 0x72, 0x75, 0x66, 0x6c, 0x79,
+ 0x6f, 0x6e, 0x77, 0x69, 0x6d, 0x72, 0x70, 0x74, 0x75, 0x62, 0x75, 0x72, 0x6c, 0x65, 0x77, 0x66,
+ 0x78, 0x62, 0x70, 0x70, 0x6c, 0x6e, 0x69, 0x78, 0x73, 0x79, 0x71, 0x6b, 0x6d, 0x75, 0x62, 0x76,
+ 0x63, 0x65, 0x6d, 0x67, 0x6c, 0x66, 0x74, 0x6d, 0x6b, 0x72, 0x66, 0x6e, 0x61, 0x6f, 0x6d, 0x61,
+ 0x67, 0x6b, 0x66, 0x71, 0x71, 0x6a, 0x6e, 0x72, 0x78, 0x64, 0x71, 0x73, 0x63, 0x79, 0x74, 0x72,
+ 0x74, 0x70, 0x77, 0x66, 0x73, 0x79, 0x71, 0x65, 0x6a, 0x73, 0x66, 0x66, 0x67, 0x63, 0x65, 0x6f,
+ 0x71, 0x6c, 0x73, 0x79, 0x73, 0x67, 0x78, 0x74, 0x61, 0x6f, 0x6a, 0x75, 0x74, 0x70, 0x76, 0x78,
+ 0x69, 0x6b, 0x75, 0x78, 0x6f, 0x68, 0x6c, 0x65, 0x65, 0x61, 0x71, 0x65, 0x66, 0x67, 0x66, 0x74,
+ 0x79, 0x6c, 0x62, 0x74, 0x69, 0x66, 0x79, 0x61, 0x79, 0x77, 0x64, 0x69, 0x70, 0x67, 0x73, 0x6b,
+ 0x75, 0x74, 0x6c, 0x68, 0x74, 0x62, 0x64, 0x66, 0x76, 0x6a, 0x69, 0x72, 0x68, 0x73, 0x65, 0x63,
+ 0x74, 0x61, 0x6e, 0x6c, 0x63, 0x64, 0x67, 0x6d, 0x68, 0x75, 0x63, 0x6a, 0x68, 0x64, 0x79, 0x61,
+ 0x6b, 0x77, 0x6a, 0x66, 0x75, 0x79, 0x6b, 0x65, 0x6f, 0x77, 0x75, 0x62, 0x72, 0x77, 0x75, 0x73,
+ 0x64, 0x69, 0x79, 0x61, 0x73, 0x67, 0x6b, 0x68, 0x62, 0x75, 0x69, 0x6f, 0x6a, 0x64, 0x77, 0x68,
+ 0x65, 0x65, 0x66, 0x61, 0x61, 0x68, 0x72, 0x6a, 0x69, 0x6b, 0x77, 0x71, 0x78, 0x6c, 0x6b, 0x74,
+ 0x76, 0x6c, 0x6a, 0x70, 0x63, 0x6f, 0x71, 0x65, 0x65, 0x6f, 0x69, 0x71, 0x62, 0x73, 0x6c, 0x75,
+ 0x6a, 0x74, 0x61, 0x78, 0x6d, 0x62, 0x6e, 0x6e, 0x79, 0x69, 0x68, 0x69, 0x77, 0x6c, 0x6a, 0x6c,
+ 0x75, 0x62, 0x6e, 0x66, 0x67, 0x66, 0x62, 0x71, 0x75, 0x69, 0x66, 0x6c, 0x68, 0x75, 0x71, 0x61,
+ 0x78, 0x62, 0x69, 0x64, 0x6c, 0x66, 0x70, 0x61, 0x64, 0x61, 0x70, 0x63, 0x6b, 0x6d, 0x78, 0x62,
+ 0x6f, 0x65, 0x71, 0x73, 0x75, 0x66, 0x6a, 0x69, 0x6b, 0x64, 0x63, 0x6f, 0x63, 0x6d, 0x69, 0x73,
+ 0x79, 0x77, 0x69, 0x68, 0x66, 0x70, 0x76, 0x6e, 0x6f, 0x64, 0x72, 0x79, 0x70, 0x66, 0x6f, 0x63,
+ 0x79, 0x64, 0x76, 0x69, 0x70, 0x6e, 0x65, 0x67, 0x73, 0x6c, 0x63, 0x6f, 0x77, 0x67, 0x63, 0x6a,
+ 0x69, 0x6a, 0x6f, 0x74, 0x61, 0x68, 0x6b, 0x63, 0x6c, 0x63, 0x71, 0x68, 0x61, 0x70, 0x70, 0x64,
+ 0x62, 0x6b, 0x68, 0x75, 0x63, 0x6e, 0x78, 0x68, 0x61, 0x71, 0x6e, 0x77, 0x6f, 0x6b, 0x78, 0x78,
+ 0x74, 0x73, 0x70, 0x77, 0x66, 0x61, 0x6b, 0x77, 0x6c, 0x63, 0x76, 0x65, 0x79, 0x73, 0x76, 0x79,
+ 0x68, 0x78, 0x6b, 0x73, 0x61, 0x71, 0x77, 0x70, 0x61, 0x6b, 0x79, 0x73, 0x6b, 0x6c, 0x77, 0x63,
+ 0x69, 0x62, 0x77, 0x64, 0x68, 0x6c, 0x68, 0x79, 0x62, 0x72, 0x6a, 0x62, 0x72, 0x62, 0x61, 0x6b,
+ 0x71, 0x63, 0x63, 0x66, 0x62, 0x6a, 0x75, 0x65, 0x79, 0x6e, 0x65, 0x63, 0x6a, 0x75, 0x63, 0x79,
+ 0x74, 0x69, 0x69, 0x76, 0x78, 0x66, 0x68, 0x6c, 0x67, 0x77, 0x65, 0x6f, 0x76, 0x75, 0x61, 0x63,
+ 0x71, 0x73, 0x76, 0x64, 0x69, 0x64, 0x75, 0x64, 0x71, 0x62, 0x74, 0x6f, 0x74, 0x63, 0x66, 0x63,
+ 0x74, 0x70, 0x6d, 0x66, 0x6f, 0x6f, 0x6b, 0x79, 0x74, 0x61, 0x70, 0x76, 0x6d, 0x6f, 0x70, 0x64,
+ 0x74, 0x68, 0x6a, 0x6e, 0x6b, 0x70, 0x68, 0x64, 0x72, 0x72, 0x75, 0x63, 0x6e, 0x69, 0x69, 0x61,
+ 0x6f, 0x64, 0x76, 0x6a, 0x69, 0x6e, 0x79, 0x70, 0x61, 0x64, 0x76, 0x69, 0x6d, 0x6b, 0x67, 0x6a,
+ 0x6e, 0x67, 0x63, 0x6f, 0x78, 0x79, 0x73, 0x64, 0x77, 0x77, 0x72, 0x73, 0x61, 0x65, 0x6a, 0x65,
+ 0x6d, 0x62, 0x6b, 0x69, 0x78, 0x6d, 0x78, 0x74, 0x62, 0x75, 0x79, 0x6b, 0x66, 0x65, 0x73, 0x64,
+ 0x67, 0x72, 0x61, 0x64, 0x6c, 0x64, 0x61, 0x6f, 0x74, 0x64, 0x68, 0x73, 0x69, 0x6d, 0x69, 0x6f,
+ 0x6f, 0x71, 0x70, 0x77, 0x75, 0x77, 0x71, 0x73, 0x75, 0x63, 0x79, 0x74, 0x6f, 0x79, 0x6f, 0x63,
+ 0x64, 0x72, 0x78, 0x70, 0x63, 0x65, 0x62, 0x6e, 0x65, 0x64, 0x70, 0x6a, 0x6f, 0x63, 0x70, 0x6b,
+ 0x67, 0x73, 0x78, 0x61, 0x77, 0x70, 0x6e, 0x70, 0x78, 0x71, 0x61, 0x63, 0x6d, 0x68, 0x6a, 0x63,
+ 0x74, 0x73, 0x62, 0x75, 0x74, 0x75, 0x6c, 0x6b, 0x6c, 0x76, 0x6b, 0x65, 0x6a, 0x77, 0x77, 0x64,
+ 0x77, 0x6f, 0x6f, 0x70, 0x61, 0x74, 0x65, 0x74, 0x6d, 0x6f, 0x68, 0x77, 0x6b, 0x63, 0x65, 0x62,
+ 0x6a, 0x74, 0x6b, 0x78, 0x6d, 0x76, 0x63, 0x73, 0x6c, 0x68, 0x6f, 0x6d, 0x6c, 0x78, 0x79, 0x6b,
+ 0x74, 0x74, 0x66, 0x69, 0x76, 0x70, 0x6f, 0x6f, 0x78, 0x61, 0x6d, 0x68, 0x77, 0x75, 0x74, 0x79,
+ 0x68, 0x6f, 0x63, 0x64, 0x6b, 0x72, 0x65, 0x68, 0x70, 0x6c, 0x61, 0x79, 0x73, 0x6d, 0x72, 0x75,
+ 0x74, 0x75, 0x6d, 0x6f, 0x6f, 0x65, 0x68, 0x64, 0x6a, 0x74, 0x64, 0x68, 0x62, 0x70, 0x6d, 0x68,
+ 0x73, 0x6d, 0x65, 0x76, 0x75, 0x64, 0x65, 0x66, 0x77, 0x69, 0x71, 0x6a, 0x64, 0x6b, 0x73, 0x6f,
+ 0x70, 0x6c, 0x6e, 0x6c, 0x6a, 0x66, 0x67, 0x78, 0x61, 0x79, 0x68, 0x63, 0x70, 0x79, 0x68, 0x6c,
+ 0x70, 0x6c, 0x76, 0x64, 0x68, 0x76, 0x67, 0x73, 0x65, 0x62, 0x65, 0x71, 0x71, 0x79, 0x72, 0x77,
+ 0x65, 0x77, 0x79, 0x78, 0x72, 0x75, 0x6e, 0x71, 0x68, 0x72, 0x64, 0x66, 0x64, 0x63, 0x68, 0x79,
+ 0x66, 0x70, 0x67, 0x64, 0x75, 0x62, 0x79, 0x66, 0x62, 0x74, 0x6b, 0x72, 0x6a, 0x70, 0x78, 0x61,
+ 0x70, 0x6e, 0x76, 0x63, 0x6a, 0x62, 0x76, 0x69, 0x68, 0x77, 0x6c, 0x73, 0x62, 0x68, 0x65, 0x6f,
+ 0x6f, 0x6c, 0x65, 0x6e, 0x66, 0x6a, 0x71, 0x69, 0x71, 0x66, 0x63, 0x6e, 0x63, 0x6d, 0x67, 0x78,
+ 0x72, 0x72, 0x66, 0x79, 0x66, 0x75, 0x72, 0x73, 0x69, 0x76, 0x75, 0x75, 0x69, 0x64, 0x6f, 0x6e,
+ 0x67, 0x67, 0x78, 0x72, 0x72, 0x71, 0x71, 0x69, 0x61, 0x66, 0x6f, 0x6d, 0x67, 0x68, 0x63, 0x62,
+ 0x6b, 0x6b, 0x6c, 0x61, 0x63, 0x65, 0x62, 0x76, 0x72, 0x74, 0x6c, 0x78, 0x68, 0x64, 0x71, 0x78,
+ 0x76, 0x6c, 0x72, 0x71, 0x73, 0x79, 0x66, 0x67, 0x72, 0x76, 0x6c, 0x75, 0x68, 0x6b, 0x70, 0x6e,
+ 0x63, 0x62, 0x6d, 0x67, 0x64, 0x70, 0x68, 0x68, 0x6c, 0x65, 0x77, 0x61, 0x75, 0x71, 0x61, 0x6d,
+ 0x6d, 0x67, 0x6c, 0x69, 0x63, 0x6a, 0x6d, 0x62, 0x6a, 0x77, 0x73, 0x6d, 0x65, 0x6f, 0x73, 0x76,
+ 0x69, 0x6f, 0x70, 0x66, 0x64, 0x73, 0x69, 0x72, 0x71, 0x6a, 0x79, 0x74, 0x6e, 0x65, 0x6a, 0x6b,
+ 0x63, 0x76, 0x66, 0x66, 0x72, 0x6e, 0x79, 0x6d, 0x6b, 0x6d, 0x75, 0x76, 0x68, 0x70, 0x6a, 0x78,
+ 0x6a, 0x6c, 0x78, 0x68, 0x77, 0x66, 0x75, 0x62, 0x6c, 0x75, 0x79, 0x69, 0x6a, 0x6e, 0x78, 0x74,
+ 0x6c, 0x6e, 0x65, 0x78, 0x64, 0x6f, 0x78, 0x68, 0x76, 0x6c, 0x75, 0x73, 0x62, 0x62, 0x79, 0x68,
+ 0x6d, 0x77, 0x75, 0x6d, 0x67, 0x6c, 0x68, 0x72, 0x72, 0x65, 0x62, 0x68, 0x67, 0x61, 0x74, 0x63,
+ 0x72, 0x6c, 0x63, 0x65, 0x74, 0x78, 0x6a, 0x68, 0x71, 0x71, 0x6b, 0x64, 0x79, 0x77, 0x73, 0x68,
+ 0x69, 0x76, 0x64, 0x62, 0x77, 0x75, 0x78, 0x76, 0x67, 0x65, 0x75, 0x67, 0x75, 0x68, 0x6e, 0x64,
+ 0x79, 0x67, 0x77, 0x6f, 0x68, 0x70, 0x69, 0x64, 0x61, 0x62, 0x69, 0x76, 0x64, 0x78, 0x71, 0x6e,
+ 0x6e, 0x69, 0x61, 0x68, 0x75, 0x76, 0x6c, 0x74, 0x68, 0x63, 0x6d, 0x79, 0x73, 0x69, 0x61, 0x64,
+ 0x6c, 0x69, 0x74, 0x75, 0x69, 0x70, 0x69, 0x73, 0x74, 0x6c, 0x62, 0x6c, 0x6e, 0x6c, 0x6f, 0x73,
+ 0x61, 0x63, 0x6e, 0x66, 0x72, 0x63, 0x65, 0x71, 0x62, 0x68, 0x77, 0x74, 0x6d, 0x62, 0x62, 0x75,
+ 0x69, 0x75, 0x63, 0x63, 0x6b, 0x68, 0x70, 0x71, 0x63, 0x6f, 0x65, 0x70, 0x6a, 0x78, 0x73, 0x61,
+ 0x68, 0x75, 0x71, 0x66, 0x6a, 0x77, 0x6b, 0x6a, 0x76, 0x79, 0x61, 0x64, 0x6a, 0x6f, 0x68, 0x72,
+ 0x78, 0x64, 0x75, 0x6f, 0x64, 0x64, 0x6d, 0x65, 0x75, 0x6e, 0x73, 0x65, 0x63, 0x75, 0x65, 0x6d,
+ 0x6d, 0x72, 0x65, 0x62, 0x70, 0x64, 0x64, 0x69, 0x6b, 0x6a, 0x63, 0x78, 0x6c, 0x74, 0x61, 0x68,
+ 0x61, 0x6e, 0x6c, 0x75, 0x73, 0x62, 0x62, 0x6e, 0x71, 0x62, 0x6a, 0x78, 0x65, 0x63, 0x61, 0x65,
+ 0x75, 0x73, 0x66, 0x61, 0x6b, 0x74, 0x6b, 0x65, 0x6f, 0x69, 0x63, 0x6e, 0x6f, 0x69, 0x6b, 0x62,
+ 0x65, 0x73, 0x64, 0x64, 0x6d, 0x72, 0x76, 0x66, 0x76, 0x76, 0x72, 0x6d, 0x67, 0x6a, 0x65, 0x6d,
+ 0x73, 0x65, 0x67, 0x79, 0x79, 0x63, 0x62, 0x6a, 0x6e, 0x68, 0x71, 0x65, 0x69, 0x6a, 0x6e, 0x63,
+ 0x76, 0x72, 0x6c, 0x67, 0x6f, 0x6d, 0x64, 0x74, 0x61, 0x76, 0x6d, 0x76, 0x6d, 0x75, 0x64, 0x6a,
+ 0x64, 0x63, 0x75, 0x70, 0x64, 0x6a, 0x68, 0x72, 0x65, 0x64, 0x79, 0x79, 0x72, 0x6a, 0x6d, 0x77,
+ 0x75, 0x64, 0x6f, 0x72, 0x63, 0x65, 0x74, 0x72, 0x6e, 0x67, 0x66, 0x70, 0x63, 0x6d, 0x64, 0x65,
+ 0x63, 0x72, 0x69, 0x6a, 0x74, 0x68, 0x77, 0x74, 0x72, 0x79, 0x77, 0x71, 0x6c, 0x6d, 0x79, 0x6f,
+ 0x62, 0x79, 0x72, 0x61, 0x62, 0x6a, 0x77, 0x76, 0x61, 0x6d, 0x74, 0x65, 0x73, 0x69, 0x6f, 0x74,
+ 0x76, 0x68, 0x71, 0x76, 0x67, 0x76, 0x62, 0x6f, 0x63, 0x6f, 0x6c, 0x77, 0x69, 0x6a, 0x62, 0x64,
+ 0x62, 0x74, 0x6a, 0x69, 0x71, 0x6a, 0x6c, 0x78, 0x67, 0x78, 0x67, 0x65, 0x65, 0x70, 0x79, 0x65,
+ 0x71, 0x75, 0x6c, 0x6e, 0x6b, 0x68, 0x6e, 0x62, 0x78, 0x65, 0x76, 0x69, 0x6d, 0x66, 0x6a, 0x70,
+ 0x6c, 0x63, 0x68, 0x75, 0x6a, 0x6c, 0x73, 0x75, 0x68, 0x6d, 0x63, 0x73, 0x68, 0x6e, 0x64, 0x6f,
+ 0x61, 0x65, 0x75, 0x6a, 0x75, 0x62, 0x76, 0x79, 0x6d, 0x64, 0x71, 0x76, 0x78, 0x62, 0x6a, 0x75,
+ 0x6c, 0x6b, 0x77, 0x79, 0x6a, 0x6a, 0x77, 0x74, 0x6a, 0x72, 0x73, 0x66, 0x72, 0x63, 0x69, 0x75,
+ 0x62, 0x70, 0x77, 0x6e, 0x62, 0x65, 0x71, 0x71, 0x6c, 0x76, 0x6b, 0x73, 0x6e, 0x64, 0x74, 0x75,
+ 0x62, 0x79, 0x71, 0x77, 0x75, 0x73, 0x6f, 0x76, 0x6d, 0x6a, 0x73, 0x72, 0x62, 0x6d, 0x69, 0x73,
+ 0x6a, 0x6a, 0x6d, 0x6f, 0x73, 0x63, 0x72, 0x75, 0x6c, 0x6c, 0x68, 0x67, 0x70, 0x74, 0x61, 0x73,
+ 0x78, 0x6d, 0x63, 0x6c, 0x6d, 0x74, 0x71, 0x65, 0x67, 0x74, 0x75, 0x6a, 0x6f, 0x6a, 0x6e, 0x64,
+ 0x6d, 0x68, 0x63, 0x77, 0x79, 0x6a, 0x65, 0x6b, 0x62, 0x6a, 0x66, 0x6b, 0x76, 0x73, 0x74, 0x75,
+ 0x6b, 0x6a, 0x79, 0x69, 0x6d, 0x6c, 0x79, 0x6a, 0x79, 0x79, 0x79, 0x78, 0x71, 0x75, 0x76, 0x67,
+ 0x70, 0x64, 0x69, 0x74, 0x70, 0x61, 0x62, 0x79, 0x65, 0x6e, 0x65, 0x6e, 0x77, 0x63, 0x67, 0x73,
+ 0x79, 0x77, 0x6a, 0x73, 0x78, 0x61, 0x62, 0x76, 0x74, 0x6f, 0x6b, 0x6f, 0x75, 0x6b, 0x6f, 0x67,
+ 0x6b, 0x67, 0x76, 0x65, 0x6b, 0x70, 0x6d, 0x6a, 0x61, 0x70, 0x6c, 0x67, 0x61, 0x64, 0x74, 0x63,
+ 0x63, 0x6b, 0x73, 0x74, 0x68, 0x6d, 0x6d, 0x69, 0x79, 0x65, 0x67, 0x71, 0x6e, 0x66, 0x6b, 0x72,
+ 0x69, 0x75, 0x6f, 0x65, 0x61, 0x75, 0x71, 0x67, 0x63, 0x70, 0x6b, 0x63, 0x76, 0x73, 0x64, 0x6a,
+ 0x6e, 0x61, 0x75, 0x72, 0x68, 0x6d, 0x62, 0x68, 0x65, 0x67, 0x79, 0x63, 0x6f, 0x66, 0x68, 0x68,
+ 0x66, 0x61, 0x79, 0x68, 0x62, 0x74, 0x70, 0x69, 0x6e, 0x6a, 0x78, 0x65, 0x6c, 0x77, 0x79, 0x73,
+ 0x69, 0x62, 0x71, 0x63, 0x62, 0x73, 0x65, 0x65, 0x6b, 0x6f, 0x6f, 0x71, 0x6b, 0x62, 0x6f, 0x68,
+ 0x75, 0x66, 0x78, 0x62, 0x73, 0x6d, 0x62, 0x71, 0x64, 0x6a, 0x6c, 0x63, 0x76, 0x66, 0x66, 0x69,
+ 0x62, 0x6c, 0x6a, 0x69, 0x78, 0x78, 0x65, 0x68, 0x73, 0x70, 0x65, 0x6f, 0x67, 0x73, 0x79, 0x69,
+ 0x67, 0x71, 0x70, 0x62, 0x6e, 0x65, 0x63, 0x62, 0x69, 0x70, 0x6e, 0x6f, 0x70, 0x77, 0x64, 0x62,
+ 0x6e, 0x6e, 0x69, 0x66, 0x61, 0x61, 0x79, 0x62, 0x68, 0x72, 0x6a, 0x75, 0x6c, 0x6f, 0x73, 0x75,
+ 0x6a, 0x73, 0x62, 0x6e, 0x6f, 0x65, 0x65, 0x62, 0x6d, 0x6a, 0x71, 0x6c, 0x6c, 0x77, 0x71, 0x6c,
+ 0x69, 0x6a, 0x6c, 0x74, 0x6a, 0x6b, 0x73, 0x78, 0x72, 0x6e, 0x68, 0x64, 0x69, 0x63, 0x73, 0x78,
+ 0x77, 0x6b, 0x6a, 0x6b, 0x78, 0x72, 0x61, 0x6d, 0x6b, 0x64, 0x71, 0x75, 0x6d, 0x65, 0x71, 0x71,
+ 0x6b, 0x61, 0x72, 0x71, 0x6e, 0x76, 0x77, 0x6e, 0x61, 0x66, 0x6d, 0x66, 0x68, 0x6a, 0x64, 0x6a,
+ 0x66, 0x75, 0x66, 0x6c, 0x76, 0x64, 0x73, 0x6a, 0x6c, 0x63, 0x6d, 0x71, 0x67, 0x73, 0x74, 0x76,
+ 0x74, 0x66, 0x70, 0x75, 0x6c, 0x6c, 0x63, 0x74, 0x70, 0x68, 0x6e, 0x78, 0x61, 0x73, 0x6a, 0x6f,
+ 0x67, 0x78, 0x6d, 0x79, 0x62, 0x68, 0x6f, 0x66, 0x64, 0x78, 0x61, 0x64, 0x6d, 0x61, 0x74, 0x72,
+ 0x61, 0x77, 0x6a, 0x79, 0x75, 0x66, 0x6f, 0x6c, 0x79, 0x78, 0x76, 0x76, 0x72, 0x69, 0x6f, 0x79,
+ 0x65, 0x62, 0x65, 0x73, 0x6e, 0x61, 0x6e, 0x71, 0x63, 0x68, 0x6e, 0x76, 0x64, 0x6f, 0x69, 0x62,
+ 0x6e, 0x6c, 0x6e, 0x79, 0x71, 0x79, 0x73, 0x72, 0x71, 0x65, 0x6b, 0x65, 0x65, 0x76, 0x65, 0x6d,
+ 0x72, 0x69, 0x77, 0x71, 0x70, 0x78, 0x78, 0x6b, 0x64, 0x70, 0x77, 0x79, 0x73, 0x75, 0x74, 0x62,
+ 0x79, 0x6b, 0x79, 0x70, 0x71, 0x70, 0x76, 0x6f, 0x71, 0x71, 0x66, 0x62, 0x6d, 0x6b, 0x74, 0x73,
+ 0x6c, 0x78, 0x77, 0x68, 0x6d, 0x6d, 0x65, 0x75, 0x74, 0x64, 0x71, 0x72, 0x6c, 0x64, 0x6f, 0x68,
+ 0x63, 0x75, 0x78, 0x68, 0x6f, 0x71, 0x61, 0x68, 0x78, 0x78, 0x73, 0x62, 0x74, 0x70, 0x61, 0x6e,
+ 0x68, 0x72, 0x71, 0x6c, 0x72, 0x71, 0x70, 0x77, 0x68, 0x6b, 0x64, 0x63, 0x6c, 0x76, 0x79, 0x70,
+ 0x76, 0x6f, 0x6a, 0x65, 0x67, 0x74, 0x73, 0x6a, 0x72, 0x64, 0x6a, 0x77, 0x6b, 0x61, 0x62, 0x69,
+ 0x70, 0x75, 0x6c, 0x6f, 0x6e, 0x6e, 0x6b, 0x76, 0x66, 0x71, 0x74, 0x62, 0x71, 0x75, 0x6c, 0x70,
+ 0x71, 0x70, 0x73, 0x61, 0x6a, 0x61, 0x79, 0x78, 0x74, 0x77, 0x77, 0x61, 0x76, 0x6f, 0x6c, 0x75,
+ 0x68, 0x63, 0x72, 0x77, 0x64, 0x64, 0x77, 0x6e, 0x67, 0x6a, 0x6c, 0x74, 0x73, 0x78, 0x72, 0x6a,
+ 0x6e, 0x69, 0x70, 0x79, 0x64, 0x62, 0x63, 0x76, 0x65, 0x76, 0x6d, 0x6f, 0x6b, 0x79, 0x63, 0x78,
+ 0x68, 0x70, 0x6e, 0x75, 0x64, 0x77, 0x64, 0x6e, 0x6c, 0x6e, 0x64, 0x78, 0x72, 0x73, 0x70, 0x74,
+ 0x72, 0x69, 0x76, 0x74, 0x61, 0x77, 0x64, 0x72, 0x65, 0x67, 0x6e, 0x68, 0x68, 0x67, 0x64, 0x6c,
+ 0x6f, 0x6e, 0x6f, 0x76, 0x71, 0x6c, 0x6f, 0x67, 0x75, 0x77, 0x6a, 0x72, 0x6d, 0x72, 0x70, 0x79,
+ 0x75, 0x72, 0x70, 0x78, 0x6f, 0x6e, 0x65, 0x6d, 0x74, 0x72, 0x6a, 0x62, 0x63, 0x76, 0x69, 0x68,
+ 0x77, 0x78, 0x6c, 0x72, 0x77, 0x63, 0x75, 0x6a, 0x6b, 0x74, 0x73, 0x64, 0x64, 0x70, 0x74, 0x61,
+ 0x6e, 0x6e, 0x62, 0x77, 0x76, 0x71, 0x78, 0x6a, 0x71, 0x76, 0x74, 0x61, 0x64, 0x75, 0x75, 0x63,
+ 0x61, 0x71, 0x78, 0x67, 0x77, 0x71, 0x66, 0x6e, 0x6c, 0x73, 0x61, 0x6e, 0x77, 0x62, 0x71, 0x63,
+ 0x6b, 0x6f, 0x6c, 0x6c, 0x6d, 0x77, 0x6a, 0x78, 0x77, 0x64, 0x6d, 0x70, 0x6a, 0x6c, 0x73, 0x78,
+ 0x68, 0x76, 0x6e, 0x6d, 0x6c, 0x70, 0x6f, 0x65, 0x64, 0x74, 0x61, 0x61, 0x73, 0x66, 0x76, 0x6a,
+ 0x62, 0x71, 0x6f, 0x6f, 0x73, 0x68, 0x79, 0x6b, 0x6b, 0x70, 0x73, 0x62, 0x75, 0x6a, 0x74, 0x73,
+ 0x75, 0x65, 0x6b, 0x6b, 0x6e, 0x74, 0x75, 0x75, 0x70, 0x6b, 0x70, 0x78, 0x77, 0x64, 0x65, 0x75,
+ 0x6f, 0x75, 0x73, 0x64, 0x66, 0x72, 0x67, 0x65, 0x77, 0x68, 0x66, 0x6b, 0x62, 0x6d, 0x62, 0x67,
+ 0x6d, 0x70, 0x66, 0x67, 0x62, 0x71, 0x68, 0x6f, 0x78, 0x68, 0x76, 0x67, 0x6d, 0x61, 0x63, 0x67,
+ 0x6b, 0x75, 0x65, 0x65, 0x67, 0x63, 0x78, 0x61, 0x6e, 0x78, 0x66, 0x71, 0x75, 0x6d, 0x66, 0x73,
+ 0x69, 0x75, 0x62, 0x78, 0x70, 0x70, 0x73, 0x75, 0x67, 0x67, 0x79, 0x63, 0x63, 0x62, 0x67, 0x72,
+ 0x69, 0x62, 0x6e, 0x78, 0x73, 0x77, 0x78, 0x79, 0x64, 0x70, 0x64, 0x66, 0x72, 0x66, 0x63, 0x6e,
+ 0x74, 0x70, 0x62, 0x77, 0x6e, 0x61, 0x77, 0x64, 0x79, 0x69, 0x74, 0x70, 0x63, 0x62, 0x6e, 0x6d,
+ 0x72, 0x71, 0x6c, 0x6b, 0x6f, 0x6d, 0x67, 0x69, 0x68, 0x6f, 0x79, 0x6a, 0x73, 0x67, 0x70, 0x65,
+ 0x62, 0x6a, 0x75, 0x6f, 0x67, 0x67, 0x68, 0x6d, 0x6f, 0x64, 0x64, 0x70, 0x69, 0x70, 0x64, 0x62,
+ 0x67, 0x76, 0x64, 0x62, 0x6e, 0x71, 0x63, 0x66, 0x65, 0x70, 0x68, 0x78, 0x6a, 0x66, 0x6f, 0x68,
+ 0x70, 0x78, 0x67, 0x70, 0x6e, 0x63, 0x6c, 0x67, 0x61, 0x62, 0x67, 0x69, 0x77, 0x73, 0x61, 0x77,
+ 0x6f, 0x6d, 0x6b, 0x6f, 0x67, 0x68, 0x66, 0x77, 0x6c, 0x71, 0x63, 0x72, 0x74, 0x79, 0x77, 0x65,
+ 0x66, 0x6d, 0x77, 0x77, 0x74, 0x71, 0x63, 0x78, 0x64, 0x69, 0x79, 0x74, 0x71, 0x74, 0x74, 0x71,
+ 0x75, 0x78, 0x62, 0x6b, 0x78, 0x6d, 0x6c, 0x70, 0x71, 0x71, 0x78, 0x70, 0x69, 0x75, 0x6b, 0x6b,
+ 0x63, 0x6e, 0x72, 0x68, 0x67, 0x6c, 0x71, 0x69, 0x6c, 0x75, 0x70, 0x6a, 0x61, 0x6d, 0x66, 0x76,
+ 0x73, 0x6a, 0x74, 0x65, 0x67, 0x66, 0x71, 0x6f, 0x68, 0x6f, 0x6e, 0x71, 0x78, 0x72, 0x6b, 0x75,
+ 0x79, 0x77, 0x6c, 0x66, 0x61, 0x6b, 0x75, 0x65, 0x6d, 0x62, 0x68, 0x6c, 0x70, 0x72, 0x73, 0x72,
+ 0x63, 0x64, 0x67, 0x69, 0x67, 0x72, 0x64, 0x6b, 0x61, 0x77, 0x62, 0x6f, 0x79, 0x6a, 0x68, 0x64,
+ 0x65, 0x6e, 0x76, 0x72, 0x68, 0x6e, 0x6e, 0x77, 0x62, 0x68, 0x71, 0x62, 0x66, 0x69, 0x73, 0x64,
+ 0x6f, 0x78, 0x6c, 0x6a, 0x77, 0x6b, 0x62, 0x76, 0x71, 0x70, 0x71, 0x70, 0x6e, 0x6e, 0x76, 0x68,
+ 0x6a, 0x6c, 0x6b, 0x63, 0x71, 0x79, 0x68, 0x6a, 0x78, 0x6e, 0x74, 0x66, 0x73, 0x65, 0x6d, 0x77,
+ 0x6a, 0x67, 0x79, 0x67, 0x64, 0x71, 0x79, 0x62, 0x75, 0x68, 0x65, 0x74, 0x6c, 0x6a, 0x72, 0x6b,
+ 0x79, 0x73, 0x71, 0x74, 0x6b, 0x63, 0x70, 0x70, 0x69, 0x65, 0x69, 0x76, 0x79, 0x69, 0x6b, 0x70,
+ 0x6a, 0x76, 0x6a, 0x65, 0x63, 0x73, 0x70, 0x69, 0x6a, 0x6c, 0x71, 0x63, 0x72, 0x6a, 0x73, 0x79,
+ 0x62, 0x65, 0x69, 0x79, 0x63, 0x6c, 0x75, 0x65, 0x6d, 0x6e, 0x73, 0x63, 0x66, 0x71, 0x75, 0x61,
+ 0x72, 0x79, 0x75, 0x6f, 0x61, 0x61, 0x6c, 0x68, 0x6e, 0x77, 0x66, 0x74, 0x6f, 0x70, 0x75, 0x65,
+ 0x66, 0x66, 0x65, 0x78, 0x72, 0x62, 0x67, 0x6d, 0x70, 0x69, 0x64, 0x65, 0x68, 0x63, 0x67, 0x72,
+ 0x79, 0x6c, 0x67, 0x64, 0x6d, 0x68, 0x70, 0x6a, 0x73, 0x6b, 0x62, 0x6e, 0x62, 0x66, 0x76, 0x77,
+ 0x69, 0x6a, 0x6e, 0x61, 0x74, 0x6d, 0x73, 0x72, 0x6f, 0x79, 0x6b, 0x68, 0x71, 0x62, 0x79, 0x6e,
+ 0x62, 0x6b, 0x67, 0x79, 0x77, 0x6e, 0x64, 0x6d, 0x66, 0x64, 0x73, 0x6f, 0x77, 0x70, 0x65, 0x6b,
+ 0x65, 0x71, 0x65, 0x6d, 0x69, 0x67, 0x6c, 0x75, 0x72, 0x66, 0x77, 0x61, 0x76, 0x6e, 0x69, 0x74,
+ 0x61, 0x72, 0x74, 0x62, 0x68, 0x67, 0x72, 0x6a, 0x65, 0x77, 0x6e, 0x75, 0x75, 0x79, 0x6a, 0x6d,
+ 0x6f, 0x75, 0x65, 0x6c, 0x6f, 0x6a, 0x71, 0x74, 0x74, 0x6f, 0x6e, 0x71, 0x71, 0x79, 0x62, 0x74,
+ 0x77, 0x6a, 0x61, 0x75, 0x74, 0x74, 0x6d, 0x61, 0x6f, 0x77, 0x77, 0x76, 0x71, 0x64, 0x79, 0x73,
+ 0x61, 0x69, 0x66, 0x6a, 0x61, 0x61, 0x64, 0x62, 0x74, 0x77, 0x6a, 0x6d, 0x6e, 0x6f, 0x68, 0x78,
+ 0x75, 0x6d, 0x6c, 0x70, 0x77, 0x74, 0x71, 0x78, 0x77, 0x75, 0x6c, 0x62, 0x72, 0x73, 0x63, 0x6c,
+ 0x74, 0x63, 0x64, 0x64, 0x73, 0x68, 0x74, 0x64, 0x6b, 0x63, 0x72, 0x76, 0x6d, 0x68, 0x74, 0x6c,
+ 0x6a, 0x79, 0x70, 0x68, 0x6b, 0x6c, 0x68, 0x61, 0x77, 0x69, 0x71, 0x64, 0x66, 0x64, 0x61, 0x76,
+ 0x61, 0x6a, 0x68, 0x75, 0x73, 0x6e, 0x70, 0x68, 0x77, 0x69, 0x67, 0x6d, 0x67, 0x6e, 0x73, 0x79,
+ 0x6a, 0x70, 0x77, 0x65, 0x6f, 0x68, 0x67, 0x6a, 0x66, 0x64, 0x6c, 0x6e, 0x6e, 0x72, 0x67, 0x70,
+ 0x76, 0x78, 0x68, 0x6d, 0x69, 0x66, 0x76, 0x77, 0x77, 0x66, 0x63, 0x74, 0x74, 0x68, 0x62, 0x65,
+ 0x74, 0x65, 0x74, 0x65, 0x6f, 0x73, 0x73, 0x69, 0x62, 0x64, 0x6a, 0x72, 0x75, 0x72, 0x6c, 0x79,
+ 0x61, 0x77, 0x66, 0x62, 0x71, 0x63, 0x71, 0x66, 0x6f, 0x65, 0x6d, 0x62, 0x61, 0x66, 0x73, 0x71,
+ 0x77, 0x75, 0x62, 0x75, 0x63, 0x75, 0x68, 0x6a, 0x79, 0x68, 0x68, 0x77, 0x64, 0x79, 0x67, 0x67,
+ 0x64, 0x76, 0x69, 0x78, 0x6b, 0x6b, 0x64, 0x61, 0x64, 0x61, 0x76, 0x64, 0x6f, 0x75, 0x71, 0x6b,
+ 0x72, 0x70, 0x72, 0x6b, 0x66, 0x6c, 0x68, 0x73, 0x66, 0x62, 0x73, 0x75, 0x66, 0x79, 0x79, 0x71,
+ 0x69, 0x73, 0x6b, 0x70, 0x65, 0x67, 0x79, 0x66, 0x65, 0x69, 0x65, 0x6a, 0x64, 0x73, 0x79, 0x74,
+ 0x65, 0x64, 0x76, 0x77, 0x68, 0x74, 0x72, 0x79, 0x68, 0x62, 0x66, 0x74, 0x67, 0x78, 0x70, 0x6d,
+ 0x68, 0x70, 0x6d, 0x6b, 0x73, 0x74, 0x75, 0x76, 0x64, 0x6f, 0x6f, 0x79, 0x76, 0x68, 0x67, 0x6c,
+ 0x78, 0x72, 0x64, 0x67, 0x63, 0x65, 0x70, 0x6f, 0x61, 0x77, 0x62, 0x6b, 0x61, 0x64, 0x77, 0x6c,
+ 0x70, 0x74, 0x64, 0x70, 0x6d, 0x6e, 0x71, 0x68, 0x79, 0x73, 0x72, 0x6c, 0x72, 0x70, 0x77, 0x62,
+ 0x68, 0x61, 0x6d, 0x68, 0x6a, 0x73, 0x75, 0x6e, 0x74, 0x61, 0x61, 0x6f, 0x75, 0x67, 0x79, 0x77,
+ 0x67, 0x6d, 0x61, 0x70, 0x64, 0x76, 0x69, 0x67, 0x70, 0x79, 0x6f, 0x78, 0x70, 0x66, 0x76, 0x6a,
+ 0x6e, 0x69, 0x63, 0x66, 0x73, 0x69, 0x78, 0x6e, 0x66, 0x64, 0x62, 0x62, 0x66, 0x75, 0x61, 0x61,
+ 0x6c, 0x63, 0x79, 0x6b, 0x63, 0x6c, 0x6e, 0x64, 0x6e, 0x6a, 0x68, 0x66, 0x64, 0x63, 0x61, 0x75,
+ 0x79, 0x65, 0x67, 0x6b, 0x6a, 0x76, 0x75, 0x74, 0x6c, 0x63, 0x78, 0x63, 0x78, 0x61, 0x70, 0x66,
+ 0x64, 0x76, 0x71, 0x73, 0x73, 0x6f, 0x65, 0x64, 0x6f, 0x73, 0x75, 0x79, 0x72, 0x70, 0x65, 0x6f,
+ 0x65, 0x64, 0x70, 0x62, 0x6f, 0x73, 0x69, 0x72, 0x62, 0x76, 0x67, 0x67, 0x62, 0x77, 0x70, 0x67,
+ 0x6a, 0x78, 0x65, 0x70, 0x64, 0x73, 0x6d, 0x79, 0x77, 0x71, 0x68, 0x6b, 0x79, 0x76, 0x69, 0x65,
+ 0x75, 0x66, 0x75, 0x76, 0x6b, 0x6b, 0x71, 0x6d, 0x6b, 0x6e, 0x67, 0x6b, 0x6f, 0x71, 0x77, 0x6f,
+ 0x61, 0x70, 0x61, 0x62, 0x64, 0x75, 0x63, 0x6c, 0x77, 0x65, 0x6a, 0x77, 0x62, 0x69, 0x64, 0x6a,
+ 0x71, 0x77, 0x65, 0x66, 0x6c, 0x6f, 0x67, 0x6c, 0x62, 0x74, 0x79, 0x69, 0x62, 0x79, 0x6c, 0x6f,
+ 0x71, 0x6e, 0x6c, 0x68, 0x66, 0x6e, 0x66, 0x76, 0x62, 0x6a, 0x75, 0x73, 0x6c, 0x67, 0x68, 0x73,
+ 0x77, 0x73, 0x76, 0x77, 0x6f, 0x79, 0x74, 0x6c, 0x75, 0x71, 0x78, 0x6e, 0x63, 0x62, 0x72, 0x64,
+ 0x74, 0x74, 0x73, 0x70, 0x68, 0x66, 0x76, 0x75, 0x77, 0x6e, 0x6b, 0x76, 0x73, 0x73, 0x70, 0x63,
+ 0x6a, 0x78, 0x77, 0x77, 0x70, 0x6e, 0x63, 0x6c, 0x77, 0x78, 0x6e, 0x72, 0x6a, 0x6d, 0x62, 0x6e,
+ 0x6d, 0x75, 0x72, 0x63, 0x64, 0x78, 0x62, 0x66, 0x71, 0x67, 0x72, 0x6b, 0x62, 0x73, 0x75, 0x70,
+ 0x74, 0x6d, 0x67, 0x70, 0x63, 0x69, 0x69, 0x76, 0x68, 0x74, 0x70, 0x62, 0x75, 0x6a, 0x72, 0x75,
+ 0x79, 0x6e, 0x77, 0x68, 0x63, 0x65, 0x71, 0x68, 0x66, 0x66, 0x79, 0x6b, 0x6b, 0x6b, 0x62, 0x74,
+ 0x68, 0x63, 0x69, 0x6d, 0x6f, 0x71, 0x78, 0x66, 0x6e, 0x64, 0x70, 0x78, 0x6e, 0x67, 0x66, 0x62,
+ 0x66, 0x6f, 0x79, 0x6d, 0x6a, 0x77, 0x6e, 0x6b, 0x78, 0x64, 0x65, 0x64, 0x6e, 0x64, 0x68, 0x73,
+ 0x75, 0x72, 0x70, 0x71, 0x67, 0x63, 0x76, 0x61, 0x6a, 0x61, 0x66, 0x6f, 0x79, 0x74, 0x68, 0x6a,
+ 0x70, 0x77, 0x75, 0x70, 0x69, 0x76, 0x70, 0x72, 0x76, 0x61, 0x78, 0x6b, 0x62, 0x69, 0x68, 0x77,
+ 0x6c, 0x6a, 0x78, 0x65, 0x71, 0x66, 0x61, 0x6a, 0x74, 0x72, 0x66, 0x72, 0x6e, 0x6c, 0x74, 0x73,
+ 0x69, 0x68, 0x6f, 0x75, 0x71, 0x6b, 0x6b, 0x69, 0x70, 0x71, 0x73, 0x74, 0x61, 0x71, 0x69, 0x77,
+ 0x64, 0x6d, 0x6c, 0x77, 0x6f, 0x76, 0x76, 0x6f, 0x62, 0x63, 0x74, 0x73, 0x73, 0x74, 0x62, 0x67,
+ 0x6f, 0x71, 0x6f, 0x70, 0x73, 0x65, 0x6a, 0x71, 0x75, 0x62, 0x63, 0x79, 0x65, 0x61, 0x64, 0x75,
+ 0x79, 0x69, 0x74, 0x64, 0x6a, 0x61, 0x6a, 0x77, 0x63, 0x6c, 0x67, 0x69, 0x6e, 0x62, 0x73, 0x68,
+ 0x65, 0x64, 0x72, 0x62, 0x6f, 0x68, 0x73, 0x66, 0x6b, 0x76, 0x79, 0x6a, 0x73, 0x6b, 0x70, 0x69,
+ 0x62, 0x62, 0x71, 0x76, 0x6f, 0x6d, 0x6e, 0x6f, 0x71, 0x66, 0x6f, 0x70, 0x75, 0x73, 0x67, 0x72,
+ 0x64, 0x78, 0x6a, 0x79, 0x76, 0x68, 0x73, 0x65, 0x65, 0x71, 0x6f, 0x66, 0x6e, 0x61, 0x62, 0x72,
+ 0x70, 0x62, 0x65, 0x6b, 0x66, 0x73, 0x73, 0x76, 0x6e, 0x69, 0x79, 0x66, 0x69, 0x6f, 0x73, 0x6e,
+ 0x77, 0x72, 0x79, 0x65, 0x69, 0x73, 0x78, 0x61, 0x68, 0x6e, 0x6c, 0x79, 0x70, 0x64, 0x75, 0x6b,
+ 0x70, 0x73, 0x77, 0x75, 0x65, 0x79, 0x77, 0x73, 0x61, 0x72, 0x6b, 0x69, 0x79, 0x74, 0x63, 0x6a,
+ 0x64, 0x68, 0x63, 0x69, 0x61, 0x6a, 0x76, 0x74, 0x63, 0x6a, 0x79, 0x75, 0x6d, 0x70, 0x67, 0x6d,
+ 0x64, 0x64, 0x78, 0x61, 0x6d, 0x6d, 0x72, 0x78, 0x74, 0x6e, 0x6e, 0x65, 0x77, 0x65, 0x63, 0x72,
+ 0x74, 0x70, 0x70, 0x71, 0x6a, 0x72, 0x67, 0x69, 0x75, 0x6e, 0x6c, 0x61, 0x74, 0x66, 0x79, 0x63,
+ 0x66, 0x67, 0x75, 0x75, 0x79, 0x75, 0x79, 0x63, 0x72, 0x6c, 0x6f, 0x73, 0x77, 0x71, 0x78, 0x74,
+ 0x72, 0x75, 0x6f, 0x6b, 0x71, 0x67, 0x65, 0x63, 0x77, 0x78, 0x6b, 0x6c, 0x69, 0x6d, 0x6a, 0x78,
+ 0x63, 0x6c, 0x69, 0x6d, 0x6a, 0x6f, 0x61, 0x62, 0x64, 0x67, 0x61, 0x6b, 0x6b, 0x6d, 0x71, 0x61,
+ 0x64, 0x6b, 0x6f, 0x6d, 0x72, 0x65, 0x6d, 0x66, 0x65, 0x77, 0x78, 0x78, 0x6c, 0x63, 0x65, 0x76,
+ 0x71, 0x67, 0x73, 0x61, 0x69, 0x68, 0x71, 0x76, 0x6c, 0x72, 0x72, 0x63, 0x69, 0x6b, 0x78, 0x6a,
+ 0x6e, 0x79, 0x62, 0x64, 0x6c, 0x63, 0x6c, 0x72, 0x62, 0x6f, 0x72, 0x68, 0x79, 0x62, 0x70, 0x67,
+ 0x64, 0x73, 0x6e, 0x67, 0x6c, 0x69, 0x77, 0x76, 0x68, 0x70, 0x6f, 0x66, 0x63, 0x67, 0x72, 0x72,
+ 0x6c, 0x69, 0x61, 0x75, 0x61, 0x62, 0x6e, 0x75, 0x69, 0x61, 0x77, 0x77, 0x65, 0x6a, 0x6b, 0x74,
+ 0x61, 0x61, 0x6d, 0x6f, 0x77, 0x79, 0x72, 0x78, 0x73, 0x77, 0x6b, 0x63, 0x6d, 0x6b, 0x66, 0x71,
+ 0x78, 0x64, 0x64, 0x76, 0x79, 0x6f, 0x63, 0x62, 0x74, 0x69, 0x65, 0x6f, 0x65, 0x62, 0x74, 0x6f,
+ 0x74, 0x77, 0x78, 0x64, 0x64, 0x63, 0x61, 0x65, 0x76, 0x70, 0x63, 0x71, 0x62, 0x66, 0x66, 0x75,
+ 0x76, 0x78, 0x63, 0x65, 0x68, 0x6d, 0x6e, 0x61, 0x72, 0x74, 0x67, 0x77, 0x71, 0x71, 0x67, 0x66,
+ 0x6c, 0x67, 0x73, 0x65, 0x76, 0x72, 0x74, 0x67, 0x68, 0x75, 0x76, 0x70, 0x66, 0x68, 0x78, 0x6e,
+ 0x6d, 0x62, 0x6b, 0x77, 0x6c, 0x61, 0x65, 0x6a, 0x6d, 0x6c, 0x6f, 0x6c, 0x66, 0x6f, 0x76, 0x73,
+ 0x76, 0x6e, 0x72, 0x63, 0x62, 0x63, 0x72, 0x74, 0x6f, 0x63, 0x6a, 0x72, 0x6b, 0x63, 0x6d, 0x6d,
+ 0x62, 0x78, 0x72, 0x71, 0x71, 0x70, 0x6a, 0x6d, 0x65, 0x6e, 0x6e, 0x79, 0x74, 0x66, 0x74, 0x64,
+ 0x73, 0x64, 0x73, 0x74, 0x79, 0x66, 0x77, 0x61, 0x6a, 0x71, 0x68, 0x72, 0x73, 0x73, 0x77, 0x71,
+ 0x67, 0x61, 0x6e, 0x68, 0x75, 0x69, 0x61, 0x66, 0x67, 0x73, 0x73, 0x6e, 0x6b, 0x70, 0x61, 0x69,
+ 0x61, 0x63, 0x6e, 0x67, 0x62, 0x78, 0x6e, 0x6f, 0x74, 0x67, 0x61, 0x74, 0x68, 0x74, 0x63, 0x68,
+ 0x64, 0x68, 0x6a, 0x63, 0x79, 0x65, 0x78, 0x6c, 0x78, 0x6a, 0x62, 0x73, 0x71, 0x65, 0x65, 0x66,
+ 0x6a, 0x62, 0x75, 0x6b, 0x70, 0x66, 0x68, 0x73, 0x72, 0x6d, 0x71, 0x79, 0x6e, 0x71, 0x6f, 0x65,
+ 0x66, 0x6b, 0x72, 0x78, 0x75, 0x6c, 0x74, 0x6a, 0x62, 0x62, 0x74, 0x78, 0x61, 0x62, 0x75, 0x65,
+ 0x6d, 0x67, 0x64, 0x67, 0x78, 0x65, 0x68, 0x6e, 0x75, 0x6d, 0x78, 0x77, 0x61, 0x6a, 0x79, 0x72,
+ 0x63, 0x6a, 0x6b, 0x66, 0x6b, 0x75, 0x69, 0x73, 0x64, 0x73, 0x65, 0x64, 0x6a, 0x6c, 0x62, 0x6e,
+ 0x66, 0x6f, 0x63, 0x78, 0x67, 0x63, 0x76, 0x76, 0x6f, 0x78, 0x61, 0x67, 0x77, 0x73, 0x72, 0x77,
+ 0x63, 0x6e, 0x62, 0x79, 0x79, 0x73, 0x6c, 0x65, 0x66, 0x75, 0x6d, 0x68, 0x72, 0x61, 0x70, 0x6f,
+ 0x76, 0x6f, 0x68, 0x62, 0x65, 0x6f, 0x70, 0x6b, 0x6c, 0x65, 0x69, 0x66, 0x73, 0x71, 0x66, 0x71,
+ 0x74, 0x78, 0x76, 0x65, 0x75, 0x66, 0x78, 0x6b, 0x63, 0x78, 0x70, 0x6b, 0x74, 0x67, 0x78, 0x75,
+ 0x65, 0x6c, 0x6c, 0x75, 0x75, 0x75, 0x6b, 0x68, 0x6f, 0x63, 0x64, 0x64, 0x67, 0x61, 0x63, 0x61,
+ 0x6d, 0x65, 0x79, 0x76, 0x69, 0x70, 0x74, 0x71, 0x62, 0x68, 0x67, 0x65, 0x6f, 0x77, 0x74, 0x63,
+ 0x64, 0x65, 0x67, 0x78, 0x62, 0x66, 0x65, 0x6d, 0x6a, 0x6c, 0x62, 0x6c, 0x6a, 0x75, 0x61, 0x67,
+ 0x71, 0x6e, 0x6e, 0x70, 0x72, 0x6c, 0x78, 0x77, 0x69, 0x6d, 0x66, 0x69, 0x6e, 0x6b, 0x63, 0x76,
+ 0x72, 0x70, 0x67, 0x79, 0x64, 0x6e, 0x68, 0x73, 0x75, 0x73, 0x6d, 0x62, 0x6b, 0x6d, 0x69, 0x70,
+ 0x6b, 0x6f, 0x65, 0x6f, 0x78, 0x6d, 0x69, 0x79, 0x63, 0x71, 0x68, 0x72, 0x75, 0x6d, 0x67, 0x71,
+ 0x6d, 0x76, 0x6f, 0x6f, 0x69, 0x62, 0x78, 0x64, 0x69, 0x6f, 0x65, 0x66, 0x66, 0x63, 0x62, 0x6c,
+ 0x6b, 0x74, 0x79, 0x6f, 0x6f, 0x79, 0x70, 0x73, 0x79, 0x75, 0x67, 0x72, 0x79, 0x72, 0x63, 0x73,
+ 0x6c, 0x64, 0x72, 0x6f, 0x62, 0x6b, 0x62, 0x6f, 0x77, 0x76, 0x6c, 0x62, 0x6c, 0x70, 0x66, 0x67,
+ 0x6f, 0x63, 0x79, 0x66, 0x69, 0x67, 0x65, 0x79, 0x6e, 0x64, 0x6e, 0x69, 0x6d, 0x71, 0x67, 0x6f,
+ 0x69, 0x74, 0x67, 0x65, 0x72, 0x63, 0x6f, 0x64, 0x68, 0x73, 0x79, 0x79, 0x6d, 0x6d, 0x69, 0x67,
+ 0x77, 0x6d, 0x64, 0x70, 0x74, 0x6c, 0x75, 0x79, 0x74, 0x62, 0x62, 0x6a, 0x63, 0x65, 0x61, 0x79,
+ 0x62, 0x65, 0x68, 0x65, 0x6b, 0x73, 0x78, 0x73, 0x68, 0x6a, 0x79, 0x71, 0x71, 0x6e, 0x62, 0x61,
+ 0x6b, 0x73, 0x6a, 0x65, 0x6b, 0x74, 0x68, 0x74, 0x66, 0x6b, 0x6f, 0x76, 0x73, 0x6e, 0x65, 0x6d,
+ 0x6e, 0x78, 0x72, 0x69, 0x69, 0x76, 0x6e, 0x72, 0x71, 0x61, 0x61, 0x6c, 0x73, 0x63, 0x74, 0x6e,
+ 0x64, 0x71, 0x6d, 0x6b, 0x62, 0x62, 0x63, 0x6c, 0x61, 0x61, 0x65, 0x78, 0x64, 0x71, 0x78, 0x68,
+ 0x6b, 0x76, 0x72, 0x79, 0x70, 0x65, 0x6e, 0x6a, 0x65, 0x77, 0x63, 0x61, 0x64, 0x66, 0x71, 0x61,
+ 0x76, 0x72, 0x75, 0x68, 0x69, 0x63, 0x75, 0x71, 0x71, 0x6f, 0x75, 0x77, 0x76, 0x61, 0x6b, 0x73,
+ 0x6e, 0x77, 0x70, 0x71, 0x64, 0x62, 0x61, 0x76, 0x65, 0x65, 0x6f, 0x71, 0x76, 0x77, 0x72, 0x73,
+ 0x64, 0x73, 0x61, 0x74, 0x75, 0x75, 0x6c, 0x6f, 0x74, 0x64, 0x6d, 0x6c, 0x6a, 0x72, 0x66, 0x68,
+ 0x6f, 0x75, 0x63, 0x71, 0x73, 0x73, 0x6e, 0x6b, 0x75, 0x67, 0x74, 0x6e, 0x6b, 0x6f, 0x73, 0x73,
+ 0x6a, 0x61, 0x70, 0x66, 0x66, 0x61, 0x68, 0x70, 0x76, 0x6c, 0x70, 0x64, 0x72, 0x63, 0x61, 0x6e,
+ 0x77, 0x74, 0x73, 0x6d, 0x73, 0x67, 0x6e, 0x63, 0x61, 0x73, 0x63, 0x73, 0x6d, 0x76, 0x6a, 0x6d,
+ 0x6e, 0x6d, 0x6a, 0x66, 0x71, 0x6e, 0x66, 0x6c, 0x66, 0x72, 0x77, 0x6a, 0x79, 0x67, 0x67, 0x62,
+ 0x69, 0x72, 0x71, 0x66, 0x66, 0x69, 0x61, 0x6d, 0x75, 0x77, 0x66, 0x74, 0x6e, 0x6d, 0x67, 0x75,
+ 0x74, 0x63, 0x65, 0x75, 0x6d, 0x6f, 0x73, 0x61, 0x74, 0x6a, 0x66, 0x64, 0x70, 0x69, 0x61, 0x6e,
+ 0x61, 0x64, 0x77, 0x75, 0x63, 0x76, 0x6c, 0x66, 0x79, 0x77, 0x74, 0x79, 0x64, 0x6f, 0x65, 0x66,
+ 0x62, 0x71, 0x6a, 0x6c, 0x6a, 0x67, 0x69, 0x68, 0x61, 0x66, 0x75, 0x78, 0x73, 0x65, 0x6c, 0x61,
+ 0x6b, 0x74, 0x68, 0x75, 0x70, 0x70, 0x6a, 0x67, 0x73, 0x71, 0x63, 0x68, 0x65, 0x64, 0x77, 0x66,
+ 0x66, 0x6a, 0x71, 0x77, 0x72, 0x62, 0x73, 0x6d, 0x62, 0x73, 0x65, 0x6c, 0x75, 0x6f, 0x67, 0x6a,
+ 0x61, 0x79, 0x6b, 0x70, 0x61, 0x71, 0x61, 0x64, 0x6f, 0x63, 0x6a, 0x75, 0x63, 0x6e, 0x79, 0x78,
+ 0x71, 0x69, 0x73, 0x68, 0x6a, 0x6b, 0x66, 0x6f, 0x68, 0x66, 0x62, 0x65, 0x75, 0x70, 0x70, 0x68,
+ 0x61, 0x68, 0x76, 0x78, 0x6d, 0x64, 0x6c, 0x64, 0x63, 0x73, 0x72, 0x76, 0x65, 0x62, 0x66, 0x6b,
+ 0x66, 0x78, 0x62, 0x75, 0x6b, 0x69, 0x77, 0x76, 0x64, 0x77, 0x71, 0x72, 0x71, 0x6d, 0x78, 0x6a,
+ 0x6b, 0x73, 0x79, 0x64, 0x6b, 0x6a, 0x76, 0x73, 0x76, 0x6c, 0x78, 0x6d, 0x6c, 0x6a, 0x78, 0x79,
+ 0x75, 0x76, 0x75, 0x79, 0x67, 0x77, 0x6d, 0x76, 0x72, 0x79, 0x6a, 0x62, 0x6f, 0x78, 0x69, 0x73,
+ 0x73, 0x61, 0x70, 0x66, 0x67, 0x79, 0x61, 0x67, 0x62, 0x6d, 0x65, 0x6a, 0x72, 0x77, 0x66, 0x65,
+ 0x62, 0x63, 0x71, 0x6d, 0x6d, 0x62, 0x62, 0x6d, 0x75, 0x6a, 0x6e, 0x64, 0x79, 0x70, 0x67, 0x68,
+ 0x70, 0x65, 0x71, 0x77, 0x75, 0x77, 0x6e, 0x67, 0x6e, 0x61, 0x6c, 0x6e, 0x64, 0x64, 0x70, 0x70,
+ 0x6f, 0x6a, 0x62, 0x70, 0x62, 0x69, 0x6d, 0x62, 0x70, 0x6b, 0x63, 0x6c, 0x68, 0x79, 0x6b, 0x68,
+ 0x71, 0x6e, 0x71, 0x63, 0x6e, 0x62, 0x6c, 0x71, 0x76, 0x6a, 0x6b, 0x67, 0x66, 0x6a, 0x73, 0x79,
+ 0x6a, 0x73, 0x67, 0x77, 0x6c, 0x61, 0x6c, 0x6c, 0x6a, 0x63, 0x76, 0x6b, 0x64, 0x78, 0x61, 0x70,
+ 0x63, 0x6d, 0x6a, 0x75, 0x6f, 0x74, 0x65, 0x63, 0x6b, 0x68, 0x67, 0x73, 0x73, 0x63, 0x71, 0x63,
+ 0x73, 0x68, 0x68, 0x74, 0x76, 0x6e, 0x6e, 0x6c, 0x66, 0x61, 0x68, 0x71, 0x6c, 0x6e, 0x76, 0x79,
+ 0x77, 0x76, 0x61, 0x66, 0x69, 0x6d, 0x65, 0x6a, 0x70, 0x6a, 0x79, 0x79, 0x6a, 0x74, 0x65, 0x65,
+ 0x66, 0x6b, 0x77, 0x77, 0x68, 0x72, 0x62, 0x79, 0x6d, 0x78, 0x71, 0x67, 0x78, 0x74, 0x63, 0x79,
+ 0x74, 0x65, 0x69, 0x64, 0x64, 0x71, 0x68, 0x74, 0x65, 0x6d, 0x6a, 0x64, 0x78, 0x6c, 0x61, 0x76,
+ 0x73, 0x6c, 0x75, 0x73, 0x63, 0x64, 0x68, 0x76, 0x67, 0x6e, 0x78, 0x78, 0x68, 0x75, 0x69, 0x65,
+ 0x6a, 0x65, 0x75, 0x78, 0x77, 0x69, 0x71, 0x6a, 0x70, 0x6a, 0x70, 0x6b, 0x70, 0x72, 0x72, 0x63,
+ 0x64, 0x74, 0x79, 0x65, 0x63, 0x68, 0x79, 0x62, 0x63, 0x61, 0x79, 0x6e, 0x77, 0x79, 0x61, 0x65,
+ 0x66, 0x71, 0x6e, 0x79, 0x78, 0x69, 0x73, 0x6c, 0x67, 0x6f, 0x66, 0x75, 0x63, 0x63, 0x65, 0x78,
+ 0x66, 0x6a, 0x70, 0x67, 0x62, 0x63, 0x69, 0x77, 0x63, 0x76, 0x6d, 0x79, 0x68, 0x71, 0x77, 0x74,
+ 0x6f, 0x6c, 0x6b, 0x71, 0x6c, 0x67, 0x6e, 0x70, 0x71, 0x78, 0x66, 0x69, 0x73, 0x6e, 0x72, 0x69,
+ 0x77, 0x71, 0x65, 0x6f, 0x6f, 0x6b, 0x69, 0x66, 0x6e, 0x61, 0x6d, 0x73, 0x61, 0x70, 0x70, 0x6b,
+ 0x67, 0x76, 0x72, 0x66, 0x64, 0x71, 0x65, 0x71, 0x64, 0x61, 0x79, 0x64, 0x6b, 0x6a, 0x6c, 0x6c,
+ 0x69, 0x65, 0x62, 0x69, 0x61, 0x6e, 0x6e, 0x74, 0x79, 0x63, 0x63, 0x6d, 0x65, 0x6b, 0x68, 0x6c,
+ 0x71, 0x76, 0x62, 0x78, 0x63, 0x77, 0x75, 0x69, 0x79, 0x6f, 0x76, 0x66, 0x77, 0x6e, 0x6c, 0x78,
+ 0x6e, 0x78, 0x73, 0x6a, 0x74, 0x62, 0x68, 0x66, 0x65, 0x68, 0x61, 0x6d, 0x77, 0x72, 0x69, 0x6f,
+ 0x6b, 0x6f, 0x65, 0x71, 0x6b, 0x79, 0x74, 0x77, 0x61, 0x74, 0x69, 0x74, 0x6a, 0x75, 0x70, 0x76,
+ 0x6a, 0x77, 0x70, 0x73, 0x77, 0x72, 0x75, 0x71, 0x76, 0x68, 0x73, 0x73, 0x6b, 0x6c, 0x63, 0x69,
+ 0x6b, 0x75, 0x67, 0x74, 0x6e, 0x79, 0x76, 0x67, 0x71, 0x78, 0x64, 0x6a, 0x79, 0x68, 0x6e, 0x65,
+ 0x65, 0x62, 0x76, 0x6f, 0x6d, 0x75, 0x74, 0x73, 0x6a, 0x68, 0x71, 0x67, 0x6b, 0x64, 0x62, 0x74,
+ 0x64, 0x62, 0x77, 0x65, 0x69, 0x61, 0x78, 0x68, 0x72, 0x70, 0x6d, 0x69, 0x6b, 0x70, 0x6e, 0x62,
+ 0x69, 0x74, 0x62, 0x6b, 0x6d, 0x64, 0x78, 0x70, 0x76, 0x74, 0x6a, 0x6a, 0x63, 0x65, 0x77, 0x78,
+ 0x72, 0x71, 0x63, 0x6d, 0x6c, 0x69, 0x63, 0x68, 0x71, 0x62, 0x62, 0x6f, 0x79, 0x75, 0x75, 0x73,
+ 0x71, 0x6c, 0x77, 0x6d, 0x62, 0x75, 0x67, 0x66, 0x61, 0x6e, 0x73, 0x73, 0x62, 0x67, 0x63, 0x72,
+ 0x66, 0x6e, 0x63, 0x71, 0x78, 0x6d, 0x68, 0x67, 0x79, 0x79, 0x76, 0x74, 0x6d, 0x74, 0x74, 0x73,
+ 0x79, 0x76, 0x6d, 0x79, 0x71, 0x72, 0x70, 0x65, 0x69, 0x66, 0x73, 0x74, 0x77, 0x6d, 0x74, 0x6e,
+ 0x6f, 0x72, 0x71, 0x62, 0x67, 0x74, 0x62, 0x77, 0x6f, 0x6b, 0x6c, 0x66, 0x65, 0x63, 0x75, 0x6e,
+ 0x66, 0x6b, 0x75, 0x69, 0x61, 0x79, 0x6c, 0x74, 0x77, 0x6b, 0x73, 0x78, 0x70, 0x6f, 0x69, 0x72,
+ 0x72, 0x74, 0x69, 0x6f, 0x62, 0x66, 0x61, 0x6c, 0x72, 0x6b, 0x63, 0x6c, 0x73, 0x67, 0x6e, 0x6b,
+ 0x74, 0x70, 0x70, 0x73, 0x6d, 0x6a, 0x73, 0x67, 0x71, 0x70, 0x77, 0x71, 0x69, 0x6a, 0x79, 0x71,
+ 0x75, 0x70, 0x67, 0x62, 0x72, 0x61, 0x79, 0x64, 0x73, 0x73, 0x66, 0x78, 0x64, 0x76, 0x69, 0x79,
+ 0x6f, 0x62, 0x6a, 0x6d, 0x76, 0x69, 0x66, 0x6b, 0x69, 0x6c, 0x6a, 0x69, 0x75, 0x71, 0x66, 0x77,
+ 0x69, 0x75, 0x76, 0x6b, 0x73, 0x69, 0x6d, 0x66, 0x61, 0x6b, 0x79, 0x74, 0x61, 0x66, 0x64, 0x64,
+ 0x65, 0x66, 0x73, 0x64, 0x76, 0x74, 0x6f, 0x63, 0x6c, 0x6c, 0x66, 0x75, 0x69, 0x6a, 0x79, 0x72,
+ 0x70, 0x64, 0x61, 0x74, 0x75, 0x74, 0x62, 0x61, 0x72, 0x6a, 0x73, 0x6f, 0x77, 0x65, 0x67, 0x71,
+ 0x64, 0x66, 0x69, 0x69, 0x63, 0x6a, 0x66, 0x73, 0x76, 0x70, 0x63, 0x69, 0x75, 0x64, 0x78, 0x62,
+ 0x79, 0x67, 0x79, 0x6b, 0x78, 0x6f, 0x63, 0x6d, 0x69, 0x61, 0x67, 0x6f, 0x77, 0x64, 0x70, 0x66,
+ 0x6b, 0x73, 0x76, 0x6c, 0x6a, 0x64, 0x69, 0x6f, 0x68, 0x75, 0x72, 0x67, 0x6c, 0x64, 0x73, 0x71,
+ 0x67, 0x6f, 0x69, 0x6c, 0x63, 0x6a, 0x62, 0x71, 0x70, 0x78, 0x73, 0x66, 0x66, 0x6e, 0x79, 0x62,
+ 0x6c, 0x77, 0x65, 0x6f, 0x6e, 0x70, 0x61, 0x76, 0x67, 0x77, 0x6d, 0x71, 0x73, 0x70, 0x75, 0x62,
+ 0x62, 0x68, 0x78, 0x6f, 0x62, 0x76, 0x61, 0x65, 0x61, 0x6a, 0x70, 0x65, 0x70, 0x70, 0x73, 0x75,
+ 0x62, 0x73, 0x61, 0x72, 0x64, 0x6c, 0x76, 0x6e, 0x6b, 0x74, 0x67, 0x6a, 0x6b, 0x66, 0x65, 0x6c,
+ 0x62, 0x65, 0x79, 0x6b, 0x69, 0x78, 0x61, 0x72, 0x73, 0x62, 0x6c, 0x70, 0x64, 0x78, 0x71, 0x78,
+ 0x68, 0x6c, 0x63, 0x72, 0x66, 0x78, 0x73, 0x79, 0x68, 0x73, 0x62, 0x68, 0x68, 0x6f, 0x67, 0x63,
+ 0x73, 0x6d, 0x6d, 0x75, 0x64, 0x63, 0x79, 0x74, 0x68, 0x74, 0x79, 0x69, 0x6b, 0x63, 0x63, 0x67,
+ 0x6a, 0x76, 0x75, 0x6b, 0x63, 0x77, 0x79, 0x64, 0x76, 0x74, 0x68, 0x6c, 0x76, 0x77, 0x68, 0x70,
+ 0x69, 0x6f, 0x73, 0x66, 0x76, 0x62, 0x76, 0x64, 0x73, 0x65, 0x64, 0x61, 0x70, 0x67, 0x6e, 0x6b,
+ 0x72, 0x70, 0x6e, 0x70, 0x79, 0x75, 0x6e, 0x6f, 0x6c, 0x67, 0x6d, 0x78, 0x61, 0x62, 0x6c, 0x6e,
+ 0x79, 0x75, 0x75, 0x66, 0x74, 0x65, 0x61, 0x74, 0x69, 0x6d, 0x78, 0x64, 0x63, 0x68, 0x70, 0x61,
+ 0x73, 0x67, 0x65, 0x6e, 0x70, 0x74, 0x67, 0x66, 0x69, 0x62, 0x68, 0x6d, 0x73, 0x64, 0x6f, 0x67,
+ 0x6c, 0x6a, 0x67, 0x71, 0x6c, 0x72, 0x6e, 0x6a, 0x6b, 0x6f, 0x73, 0x73, 0x70, 0x77, 0x66, 0x61,
+ 0x6b, 0x70, 0x65, 0x6e, 0x6e, 0x76, 0x6a, 0x6e, 0x63, 0x75, 0x77, 0x6f, 0x72, 0x62, 0x78, 0x6a,
+ 0x6a, 0x6e, 0x6e, 0x66, 0x69, 0x66, 0x68, 0x76, 0x61, 0x74, 0x6f, 0x76, 0x6c, 0x63, 0x68, 0x66,
+ 0x65, 0x72, 0x71, 0x65, 0x63, 0x69, 0x75, 0x73, 0x6b, 0x64, 0x6b, 0x79, 0x68, 0x62, 0x79, 0x65,
+ 0x74, 0x61, 0x70, 0x76, 0x79, 0x70, 0x74, 0x75, 0x79, 0x62, 0x6e, 0x6c, 0x70, 0x6d, 0x69, 0x72,
+ 0x77, 0x71, 0x6b, 0x63, 0x71, 0x61, 0x6c, 0x72, 0x67, 0x6d, 0x6b, 0x74, 0x6e, 0x71, 0x75, 0x66,
+ 0x6b, 0x6b, 0x65, 0x77, 0x65, 0x73, 0x72, 0x63, 0x67, 0x6b, 0x6a, 0x61, 0x69, 0x73, 0x61, 0x64,
+ 0x6d, 0x77, 0x74, 0x76, 0x65, 0x63, 0x61, 0x6e, 0x62, 0x68, 0x6e, 0x70, 0x62, 0x64, 0x6e, 0x79,
+ 0x69, 0x79, 0x68, 0x75, 0x6c, 0x79, 0x71, 0x78, 0x62, 0x64, 0x62, 0x78, 0x79, 0x66, 0x70, 0x65,
+ 0x70, 0x68, 0x67, 0x6d, 0x67, 0x6b, 0x62, 0x65, 0x63, 0x68, 0x67, 0x63, 0x6b, 0x78, 0x6a, 0x63,
+ 0x73, 0x63, 0x62, 0x6c, 0x61, 0x6b, 0x68, 0x68, 0x6e, 0x61, 0x70, 0x73, 0x69, 0x74, 0x63, 0x68,
+ 0x64, 0x66, 0x69, 0x62, 0x6e, 0x6c, 0x63, 0x72, 0x63, 0x62, 0x62, 0x74, 0x67, 0x67, 0x61, 0x62,
+ 0x67, 0x72, 0x75, 0x6a, 0x74, 0x78, 0x62, 0x66, 0x67, 0x63, 0x69, 0x73, 0x75, 0x68, 0x76, 0x6c,
+ 0x66, 0x65, 0x69, 0x66, 0x6e, 0x6e, 0x68, 0x72, 0x72, 0x65, 0x62, 0x71, 0x63, 0x72, 0x71, 0x69,
+ 0x63, 0x66, 0x74, 0x77, 0x65, 0x67, 0x6b, 0x70, 0x6c, 0x63, 0x6d, 0x68, 0x68, 0x71, 0x70, 0x72,
+ 0x76, 0x72, 0x77, 0x61, 0x72, 0x73, 0x75, 0x68, 0x6c, 0x73, 0x6f, 0x61, 0x6e, 0x76, 0x6e, 0x66,
+ 0x70, 0x61, 0x67, 0x6c, 0x6b, 0x61, 0x75, 0x67, 0x64, 0x76, 0x6d, 0x6f, 0x77, 0x78, 0x72, 0x70,
+ 0x6e, 0x6e, 0x6d, 0x77, 0x67, 0x71, 0x75, 0x78, 0x74, 0x71, 0x73, 0x76, 0x67, 0x61, 0x79, 0x70,
+ 0x6d, 0x71, 0x73, 0x76, 0x6c, 0x6f, 0x6d, 0x77, 0x65, 0x65, 0x76, 0x68, 0x66, 0x6a, 0x76, 0x72,
+ 0x76, 0x71, 0x64, 0x67, 0x74, 0x6d, 0x62, 0x6b, 0x64, 0x6d, 0x74, 0x78, 0x6f, 0x73, 0x62, 0x76,
+ 0x78, 0x79, 0x74, 0x6b, 0x75, 0x77, 0x6a, 0x77, 0x66, 0x65, 0x74, 0x74, 0x79, 0x6e, 0x64, 0x6d,
+ 0x6e, 0x67, 0x64, 0x62, 0x6b, 0x77, 0x72, 0x69, 0x61, 0x70, 0x67, 0x64, 0x64, 0x77, 0x6b, 0x63,
+ 0x70, 0x74, 0x67, 0x73, 0x69, 0x72, 0x79, 0x65, 0x64, 0x6e, 0x71, 0x68, 0x71, 0x79, 0x6e, 0x6e,
+ 0x63, 0x64, 0x6a, 0x70, 0x62, 0x73, 0x76, 0x72, 0x71, 0x71, 0x74, 0x67, 0x6a, 0x62, 0x70, 0x63,
+ 0x69, 0x61, 0x6f, 0x6d, 0x63, 0x67, 0x74, 0x74, 0x70, 0x72, 0x6c, 0x6d, 0x6a, 0x69, 0x64, 0x79,
+ 0x77, 0x6d, 0x77, 0x75, 0x6b, 0x68, 0x6e, 0x70, 0x71, 0x64, 0x66, 0x6f, 0x72, 0x78, 0x78, 0x69,
+ 0x6e, 0x75, 0x6b, 0x74, 0x65, 0x6c, 0x65, 0x79, 0x73, 0x73, 0x65, 0x66, 0x6f, 0x72, 0x75, 0x69,
+ 0x77, 0x61, 0x75, 0x74, 0x63, 0x68, 0x73, 0x6c, 0x73, 0x78, 0x6d, 0x6b, 0x66, 0x64, 0x75, 0x76,
+ 0x6f, 0x6f, 0x79, 0x78, 0x68, 0x6d, 0x6b, 0x6f, 0x6a, 0x72, 0x6e, 0x62, 0x63, 0x67, 0x79, 0x78,
+ 0x6b, 0x77, 0x75, 0x71, 0x69, 0x6e, 0x70, 0x73, 0x61, 0x68, 0x66, 0x63, 0x6a, 0x66, 0x75, 0x74,
+ 0x63, 0x6d, 0x72, 0x71, 0x64, 0x77, 0x77, 0x69, 0x68, 0x70, 0x79, 0x6c, 0x72, 0x62, 0x78, 0x69,
+ 0x6a, 0x61, 0x6d, 0x75, 0x6d, 0x67, 0x61, 0x78, 0x61, 0x6e, 0x73, 0x6b, 0x6c, 0x74, 0x67, 0x61,
+ 0x71, 0x68, 0x62, 0x6c, 0x62, 0x77, 0x75, 0x65, 0x6c, 0x76, 0x73, 0x64, 0x68, 0x73, 0x68, 0x79,
+ 0x78, 0x6a, 0x73, 0x6a, 0x72, 0x65, 0x77, 0x68, 0x64, 0x76, 0x75, 0x6d, 0x6d, 0x64, 0x75, 0x78,
+ 0x78, 0x76, 0x61, 0x63, 0x6f, 0x78, 0x69, 0x76, 0x61, 0x66, 0x62, 0x64, 0x72, 0x6c, 0x70, 0x79,
+ 0x6e, 0x63, 0x6a, 0x70, 0x6f, 0x73, 0x63, 0x76, 0x6d, 0x76, 0x77, 0x6f, 0x78, 0x63, 0x67, 0x6c,
+ 0x6b, 0x77, 0x71, 0x79, 0x70, 0x6a, 0x78, 0x71, 0x69, 0x69, 0x76, 0x6c, 0x6d, 0x66, 0x6f, 0x66,
+ 0x68, 0x64, 0x61, 0x71, 0x61, 0x74, 0x68, 0x72, 0x75, 0x6e, 0x6d, 0x73, 0x69, 0x71, 0x71, 0x6b,
+ 0x79, 0x76, 0x74, 0x67, 0x63, 0x64, 0x70, 0x69, 0x65, 0x75, 0x72, 0x6e, 0x6c, 0x76, 0x63, 0x72,
+ 0x6d, 0x6e, 0x73, 0x6d, 0x75, 0x6c, 0x73, 0x75, 0x61, 0x64, 0x69, 0x72, 0x75, 0x66, 0x71, 0x6b,
+ 0x77, 0x6b, 0x64, 0x62, 0x68, 0x61, 0x6e, 0x67, 0x6d, 0x68, 0x66, 0x72, 0x69, 0x6b, 0x61, 0x78,
+ 0x70, 0x6d, 0x74, 0x6d, 0x6c, 0x75, 0x63, 0x72, 0x71, 0x71, 0x70, 0x78, 0x62, 0x6a, 0x69, 0x70,
+ 0x70, 0x68, 0x6a, 0x6e, 0x6e, 0x74, 0x69, 0x62, 0x64, 0x69, 0x66, 0x62, 0x78, 0x6b, 0x6c, 0x64,
+ 0x66, 0x79, 0x6a, 0x77, 0x77, 0x79, 0x70, 0x6e, 0x76, 0x78, 0x6a, 0x70, 0x69, 0x63, 0x62, 0x72,
+ 0x6f, 0x64, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x67, 0x74, 0x65, 0x77, 0x6c, 0x6e, 0x72, 0x64, 0x6d,
+ 0x62, 0x68, 0x72, 0x66, 0x67, 0x63, 0x69, 0x74, 0x69, 0x6c, 0x75, 0x72, 0x6c, 0x63, 0x73, 0x71,
+ 0x66, 0x6f, 0x79, 0x67, 0x73, 0x6b, 0x6f, 0x6c, 0x65, 0x72, 0x78, 0x72, 0x76, 0x6a, 0x6f, 0x6e,
+ 0x66, 0x6e, 0x75, 0x6c, 0x6b, 0x75, 0x72, 0x74, 0x6a, 0x6d, 0x67, 0x67, 0x67, 0x74, 0x61, 0x6c,
+ 0x64, 0x64, 0x71, 0x79, 0x62, 0x72, 0x65, 0x6d, 0x62, 0x6c, 0x6f, 0x76, 0x63, 0x71, 0x68, 0x79,
+ 0x6e, 0x78, 0x6c, 0x63, 0x79, 0x61, 0x69, 0x65, 0x68, 0x68, 0x64, 0x66, 0x66, 0x6d, 0x6a, 0x79,
+ 0x62, 0x71, 0x78, 0x70, 0x77, 0x78, 0x73, 0x69, 0x61, 0x6e, 0x66, 0x6e, 0x77, 0x72, 0x63, 0x68,
+ 0x75, 0x6e, 0x75, 0x64, 0x67, 0x6e, 0x69, 0x6a, 0x73, 0x71, 0x6f, 0x71, 0x62, 0x73, 0x6b, 0x65,
+ 0x74, 0x73, 0x73, 0x70, 0x66, 0x66, 0x75, 0x6e, 0x70, 0x76, 0x77, 0x71, 0x78, 0x6d, 0x71, 0x78,
+ 0x6b, 0x6e, 0x62, 0x6d, 0x6f, 0x69, 0x66, 0x61, 0x6f, 0x63, 0x6e, 0x6d, 0x79, 0x77, 0x65, 0x73,
+ 0x62, 0x73, 0x77, 0x61, 0x65, 0x61, 0x79, 0x76, 0x61, 0x78, 0x62, 0x77, 0x77, 0x70, 0x77, 0x77,
+ 0x77, 0x6b, 0x63, 0x6e, 0x70, 0x76, 0x6e, 0x79, 0x72, 0x68, 0x70, 0x6f, 0x62, 0x74, 0x65, 0x78,
+ 0x6d, 0x76, 0x6a, 0x63, 0x6a, 0x66, 0x6d, 0x77, 0x6c, 0x73, 0x66, 0x6c, 0x78, 0x6e, 0x6d, 0x6b,
+ 0x6f, 0x67, 0x6a, 0x67, 0x71, 0x79, 0x75, 0x77, 0x76, 0x74, 0x6d, 0x6e, 0x68, 0x66, 0x79, 0x69,
+ 0x68, 0x75, 0x64, 0x70, 0x6c, 0x74, 0x77, 0x70, 0x6f, 0x74, 0x71, 0x6f, 0x67, 0x6e, 0x6d, 0x6f,
+ 0x76, 0x62, 0x64, 0x74, 0x65, 0x74, 0x63, 0x66, 0x67, 0x6d, 0x75, 0x63, 0x79, 0x69, 0x65, 0x73,
+ 0x6c, 0x73, 0x65, 0x62, 0x6d, 0x78, 0x68, 0x66, 0x77, 0x66, 0x74, 0x71, 0x6b, 0x65, 0x6d, 0x72,
+ 0x63, 0x73, 0x61, 0x66, 0x6e, 0x71, 0x67, 0x64, 0x73, 0x64, 0x77, 0x6a, 0x78, 0x6f, 0x66, 0x63,
+ 0x61, 0x65, 0x63, 0x76, 0x63, 0x6d, 0x71, 0x6d, 0x79, 0x79, 0x65, 0x61, 0x79, 0x66, 0x6f, 0x64,
+ 0x6b, 0x74, 0x6c, 0x75, 0x70, 0x63, 0x66, 0x77, 0x74, 0x65, 0x70, 0x75, 0x78, 0x63, 0x66, 0x6f,
+ 0x6e, 0x62, 0x6f, 0x62, 0x79, 0x73, 0x65, 0x72, 0x66, 0x6a, 0x63, 0x79, 0x66, 0x73, 0x73, 0x63,
+ 0x62, 0x69, 0x76, 0x63, 0x6e, 0x6b, 0x74, 0x76, 0x75, 0x73, 0x71, 0x74, 0x6e, 0x6e, 0x73, 0x68,
+ 0x65, 0x61, 0x6c, 0x68, 0x67, 0x65, 0x79, 0x6c, 0x79, 0x63, 0x6b, 0x77, 0x62, 0x75, 0x6d, 0x73,
+ 0x6f, 0x70, 0x69, 0x68, 0x68, 0x77, 0x6c, 0x63, 0x63, 0x6e, 0x78, 0x76, 0x69, 0x76, 0x75, 0x74,
+ 0x70, 0x64, 0x6a, 0x70, 0x6c, 0x67, 0x6f, 0x62, 0x6c, 0x73, 0x6e, 0x6e, 0x78, 0x66, 0x74, 0x65,
+ 0x6b, 0x70, 0x79, 0x72, 0x73, 0x69, 0x6c, 0x72, 0x61, 0x6c, 0x78, 0x6e, 0x63, 0x72, 0x72, 0x76,
+ 0x64, 0x68, 0x78, 0x78, 0x70, 0x79, 0x61, 0x76, 0x70, 0x6f, 0x73, 0x6c, 0x69, 0x72, 0x63, 0x72,
+ 0x72, 0x78, 0x62, 0x69, 0x61, 0x73, 0x67, 0x66, 0x64, 0x6b, 0x67, 0x66, 0x79, 0x6c, 0x61, 0x6b,
+ 0x72, 0x6d, 0x6a, 0x74, 0x70, 0x72, 0x69, 0x6b, 0x71, 0x74, 0x6e, 0x6a, 0x79, 0x69, 0x78, 0x73,
+ 0x73, 0x77, 0x68, 0x68, 0x73, 0x6b, 0x78, 0x72, 0x66, 0x6a, 0x68, 0x61, 0x67, 0x67, 0x68, 0x76,
+ 0x6a, 0x71, 0x62, 0x62, 0x68, 0x6b, 0x68, 0x66, 0x76, 0x6b, 0x6e, 0x78, 0x65, 0x77, 0x62, 0x70,
+ 0x72, 0x76, 0x76, 0x6b, 0x76, 0x64, 0x6c, 0x68, 0x79, 0x6f, 0x68, 0x69, 0x6a, 0x66, 0x67, 0x73,
+ 0x63, 0x61, 0x6b, 0x69, 0x74, 0x64, 0x62, 0x6a, 0x76, 0x74, 0x6b, 0x79, 0x77, 0x65, 0x69, 0x65,
+ 0x6b, 0x77, 0x72, 0x74, 0x6f, 0x74, 0x62, 0x64, 0x78, 0x6f, 0x62, 0x73, 0x6b, 0x69, 0x79, 0x64,
+ 0x76, 0x78, 0x71, 0x6a, 0x79, 0x61, 0x77, 0x64, 0x66, 0x77, 0x63, 0x77, 0x77, 0x6b, 0x69, 0x68,
+ 0x6e, 0x6d, 0x67, 0x6a, 0x74, 0x6c, 0x76, 0x68, 0x68, 0x72, 0x61, 0x77, 0x71, 0x64, 0x61, 0x65,
+ 0x6b, 0x72, 0x65, 0x61, 0x6f, 0x64, 0x6d, 0x71, 0x76, 0x68, 0x66, 0x78, 0x71, 0x6c, 0x6e, 0x68,
+ 0x75, 0x74, 0x75, 0x6f, 0x73, 0x70, 0x70, 0x66, 0x76, 0x6d, 0x73, 0x66, 0x6a, 0x75, 0x63, 0x77,
+ 0x69, 0x66, 0x67, 0x76, 0x6b, 0x68, 0x65, 0x63, 0x68, 0x73, 0x6f, 0x75, 0x64, 0x79, 0x6d, 0x6f,
+ 0x6e, 0x79, 0x6a, 0x78, 0x64, 0x69, 0x71, 0x70, 0x6d, 0x78, 0x70, 0x61, 0x63, 0x77, 0x66, 0x79,
+ 0x6c, 0x79, 0x66, 0x70, 0x73, 0x77, 0x65, 0x6b, 0x72, 0x6d, 0x70, 0x65, 0x74, 0x61, 0x68, 0x66,
+ 0x63, 0x79, 0x79, 0x6b, 0x76, 0x76, 0x79, 0x78, 0x6f, 0x76, 0x67, 0x79, 0x77, 0x77, 0x62, 0x77,
+ 0x69, 0x78, 0x74, 0x65, 0x65, 0x6f, 0x6a, 0x6d, 0x76, 0x6d, 0x75, 0x66, 0x6d, 0x6e, 0x70, 0x79,
+ 0x6e, 0x6c, 0x79, 0x67, 0x72, 0x6b, 0x6c, 0x64, 0x76, 0x6c, 0x6e, 0x65, 0x69, 0x64, 0x76, 0x6d,
+ 0x68, 0x79, 0x6b, 0x74, 0x63, 0x6a, 0x63, 0x66, 0x65, 0x74, 0x69, 0x74, 0x71, 0x6e, 0x62, 0x66,
+ 0x78, 0x75, 0x62, 0x74, 0x73, 0x66, 0x61, 0x77, 0x6d, 0x6c, 0x62, 0x77, 0x61, 0x68, 0x6e, 0x76,
+ 0x66, 0x6a, 0x68, 0x73, 0x63, 0x6e, 0x74, 0x64, 0x6a, 0x75, 0x68, 0x63, 0x6f, 0x73, 0x77, 0x6a,
+ 0x68, 0x75, 0x72, 0x69, 0x79, 0x69, 0x65, 0x64, 0x6d, 0x6a, 0x67, 0x70, 0x79, 0x68, 0x6f, 0x6c,
+ 0x6d, 0x75, 0x6f, 0x73, 0x68, 0x68, 0x6f, 0x73, 0x70, 0x6c, 0x65, 0x67, 0x6b, 0x6e, 0x74, 0x69,
+ 0x63, 0x72, 0x6f, 0x63, 0x68, 0x6b, 0x79, 0x74, 0x77, 0x79, 0x76, 0x63, 0x71, 0x6b, 0x6d, 0x6d,
+ 0x6f, 0x69, 0x6c, 0x77, 0x6c, 0x6c, 0x62, 0x6b, 0x71, 0x6c, 0x74, 0x67, 0x6c, 0x64, 0x6b, 0x69,
+ 0x62, 0x73, 0x78, 0x66, 0x68, 0x77, 0x64, 0x74, 0x73, 0x73, 0x71, 0x74, 0x64, 0x6b, 0x6a, 0x69,
+ 0x74, 0x6c, 0x61, 0x72, 0x74, 0x6e, 0x6a, 0x6c, 0x68, 0x78, 0x77, 0x6b, 0x67, 0x6f, 0x6f, 0x6a,
+ 0x61, 0x65, 0x6a, 0x78, 0x67, 0x61, 0x77, 0x72, 0x6e, 0x62, 0x6d, 0x6c, 0x6e, 0x67, 0x69, 0x66,
+ 0x6e, 0x6a, 0x76, 0x69, 0x77, 0x72, 0x79, 0x6f, 0x63, 0x69, 0x6e, 0x67, 0x72, 0x6a, 0x76, 0x69,
+ 0x75, 0x78, 0x6d, 0x6e, 0x65, 0x64, 0x67, 0x70, 0x6a, 0x6d, 0x74, 0x78, 0x79, 0x73, 0x72, 0x70,
+ 0x72, 0x61, 0x6f, 0x77, 0x61, 0x79, 0x6f, 0x72, 0x6b, 0x61, 0x61, 0x75, 0x77, 0x69, 0x63, 0x6d,
+ 0x68, 0x78, 0x74, 0x65, 0x74, 0x67, 0x63, 0x77, 0x63, 0x71, 0x72, 0x72, 0x71, 0x6c, 0x64, 0x79,
+ 0x78, 0x6b, 0x63, 0x75, 0x70, 0x64, 0x6f, 0x6f, 0x6c, 0x76, 0x6f, 0x71, 0x75, 0x6d, 0x63, 0x74,
+ 0x71, 0x63, 0x76, 0x6e, 0x75, 0x63, 0x65, 0x69, 0x67, 0x76, 0x64, 0x61, 0x6d, 0x72, 0x6a, 0x6b,
+ 0x64, 0x70, 0x68, 0x75, 0x67, 0x6c, 0x77, 0x71, 0x61, 0x70, 0x6e, 0x67, 0x73, 0x70, 0x6e, 0x68,
+ 0x73, 0x73, 0x69, 0x65, 0x6a, 0x66, 0x68, 0x67, 0x72, 0x73, 0x72, 0x69, 0x6a, 0x77, 0x6b, 0x78,
+ 0x6a, 0x61, 0x73, 0x66, 0x66, 0x65, 0x6b, 0x65, 0x6b, 0x61, 0x68, 0x6b, 0x6a, 0x73, 0x79, 0x6f,
+ 0x64, 0x78, 0x72, 0x76, 0x6c, 0x67, 0x61, 0x70, 0x79, 0x77, 0x77, 0x6c, 0x69, 0x67, 0x71, 0x76,
+ 0x79, 0x61, 0x72, 0x62, 0x74, 0x64, 0x69, 0x64, 0x6f, 0x78, 0x6d, 0x6f, 0x67, 0x67, 0x6c, 0x78,
+ 0x74, 0x62, 0x64, 0x72, 0x63, 0x70, 0x64, 0x71, 0x79, 0x6f, 0x63, 0x74, 0x6c, 0x69, 0x6c, 0x6d,
+ 0x77, 0x6b, 0x69, 0x63, 0x72, 0x70, 0x70, 0x71, 0x75, 0x73, 0x68, 0x68, 0x6c, 0x79, 0x6c, 0x67,
+ 0x69, 0x65, 0x79, 0x76, 0x6b, 0x74, 0x6f, 0x6d, 0x61, 0x79, 0x79, 0x6a, 0x78, 0x6e, 0x78, 0x62,
+ 0x78, 0x65, 0x61, 0x70, 0x68, 0x75, 0x78, 0x6d, 0x77, 0x77, 0x70, 0x6e, 0x75, 0x67, 0x65, 0x66,
+ 0x6f, 0x6c, 0x65, 0x77, 0x61, 0x6b, 0x69, 0x68, 0x73, 0x61, 0x74, 0x62, 0x6c, 0x6a, 0x65, 0x6d,
+ 0x74, 0x77, 0x77, 0x64, 0x67, 0x6f, 0x76, 0x78, 0x62, 0x6b, 0x71, 0x6d, 0x67, 0x62, 0x75, 0x61,
+ 0x79, 0x78, 0x64, 0x65, 0x65, 0x72, 0x62, 0x64, 0x75, 0x61, 0x66, 0x6e, 0x66, 0x6c, 0x70, 0x73,
+ 0x70, 0x68, 0x6d, 0x79, 0x63, 0x71, 0x64, 0x70, 0x6d, 0x75, 0x78, 0x61, 0x6b, 0x61, 0x62, 0x67,
+ 0x65, 0x62, 0x64, 0x66, 0x64, 0x6a, 0x76, 0x67, 0x68, 0x78, 0x6e, 0x6d, 0x65, 0x79, 0x73, 0x64,
+ 0x63, 0x68, 0x72, 0x71, 0x6a, 0x6f, 0x71, 0x61, 0x77, 0x6a, 0x6b, 0x69, 0x62, 0x73, 0x63, 0x69,
+ 0x73, 0x63, 0x64, 0x6c, 0x67, 0x71, 0x69, 0x75, 0x71, 0x63, 0x64, 0x6b, 0x61, 0x6e, 0x73, 0x6c,
+ 0x65, 0x66, 0x64, 0x6e, 0x6a, 0x74, 0x6b, 0x78, 0x69, 0x6a, 0x64, 0x6a, 0x77, 0x70, 0x70, 0x67,
+ 0x6d, 0x72, 0x6b, 0x65, 0x79, 0x64, 0x61, 0x6f, 0x64, 0x70, 0x73, 0x63, 0x72, 0x6d, 0x68, 0x73,
+ 0x74, 0x6e, 0x6f, 0x72, 0x61, 0x73, 0x61, 0x72, 0x71, 0x72, 0x61, 0x75, 0x77, 0x67, 0x70, 0x68,
+ 0x70, 0x66, 0x61, 0x72, 0x67, 0x73, 0x69, 0x67, 0x6b, 0x71, 0x73, 0x64, 0x77, 0x61, 0x67, 0x70,
+ 0x69, 0x71, 0x72, 0x77, 0x77, 0x68, 0x71, 0x63, 0x6a, 0x61, 0x70, 0x63, 0x70, 0x62, 0x71, 0x68,
+ 0x63, 0x78, 0x71, 0x62, 0x70, 0x79, 0x74, 0x76, 0x76, 0x62, 0x74, 0x63, 0x76, 0x68, 0x62, 0x73,
+ 0x72, 0x64, 0x64, 0x66, 0x71, 0x65, 0x6d, 0x72, 0x63, 0x77, 0x6a, 0x77, 0x6a, 0x76, 0x62, 0x6c,
+ 0x77, 0x65, 0x61, 0x63, 0x6b, 0x62, 0x79, 0x71, 0x75, 0x6b, 0x68, 0x74, 0x6f, 0x66, 0x74, 0x65,
+ 0x72, 0x76, 0x74, 0x6f, 0x71, 0x71, 0x6e, 0x65, 0x63, 0x78, 0x6d, 0x74, 0x74, 0x66, 0x71, 0x78,
+ 0x77, 0x68, 0x75, 0x74, 0x72, 0x62, 0x73, 0x6c, 0x65, 0x71, 0x66, 0x6c, 0x6d, 0x75, 0x66, 0x62,
+ 0x71, 0x78, 0x68, 0x63, 0x65, 0x6f, 0x72, 0x63, 0x69, 0x74, 0x68, 0x74, 0x72, 0x64, 0x62, 0x68,
+ 0x63, 0x65, 0x74, 0x78, 0x67, 0x74, 0x61, 0x74, 0x75, 0x69, 0x6f, 0x6b, 0x78, 0x6c, 0x72, 0x79,
+ 0x77, 0x69, 0x63, 0x6a, 0x6c, 0x75, 0x62, 0x62, 0x77, 0x77, 0x64, 0x77, 0x72, 0x62, 0x79, 0x74,
+ 0x73, 0x6e, 0x75, 0x69, 0x68, 0x64, 0x71, 0x68, 0x6f, 0x65, 0x64, 0x77, 0x63, 0x6f, 0x75, 0x74,
+ 0x6a, 0x74, 0x75, 0x64, 0x62, 0x68, 0x62, 0x76, 0x75, 0x75, 0x66, 0x63, 0x69, 0x62, 0x79, 0x62,
+ 0x6d, 0x70, 0x75, 0x71, 0x74, 0x76, 0x71, 0x63, 0x68, 0x6f, 0x69, 0x77, 0x6b, 0x6b, 0x61, 0x6b,
+ 0x79, 0x74, 0x77, 0x6c, 0x65, 0x67, 0x6b, 0x71, 0x61, 0x76, 0x66, 0x77, 0x69, 0x72, 0x79, 0x66,
+ 0x6e, 0x63, 0x75, 0x70, 0x73, 0x6f, 0x6c, 0x72, 0x70, 0x69, 0x6e, 0x71, 0x61, 0x77, 0x6b, 0x61,
+ 0x6c, 0x70, 0x69, 0x74, 0x72, 0x6a, 0x75, 0x74, 0x78, 0x6e, 0x6a, 0x71, 0x6b, 0x6a, 0x67, 0x71,
+ 0x78, 0x61, 0x6d, 0x77, 0x6b, 0x6d, 0x68, 0x77, 0x79, 0x66, 0x77, 0x6a, 0x75, 0x76, 0x70, 0x62,
+ 0x67, 0x64, 0x64, 0x73, 0x6e, 0x72, 0x71, 0x69, 0x65, 0x6e, 0x66, 0x64, 0x70, 0x65, 0x73, 0x67,
+ 0x6e, 0x74, 0x6e, 0x6e, 0x62, 0x78, 0x68, 0x71, 0x64, 0x62, 0x68, 0x6e, 0x77, 0x6c, 0x6c, 0x74,
+ 0x66, 0x76, 0x72, 0x65, 0x62, 0x79, 0x6d, 0x72, 0x79, 0x78, 0x63, 0x62, 0x64, 0x77, 0x6a, 0x67,
+ 0x6b, 0x6b, 0x79, 0x65, 0x6d, 0x67, 0x6a, 0x72, 0x79, 0x78, 0x63, 0x64, 0x68, 0x66, 0x75, 0x6e,
+ 0x63, 0x6d, 0x70, 0x66, 0x69, 0x64, 0x64, 0x76, 0x74, 0x62, 0x69, 0x66, 0x6f, 0x75, 0x6d, 0x67,
+ 0x63, 0x79, 0x74, 0x61, 0x72, 0x70, 0x6a, 0x73, 0x65, 0x71, 0x71, 0x62, 0x75, 0x75, 0x65, 0x69,
+ 0x6d, 0x6d, 0x72, 0x64, 0x62, 0x64, 0x72, 0x63, 0x74, 0x6e, 0x74, 0x72, 0x6f, 0x79, 0x67, 0x79,
+ 0x78, 0x6c, 0x64, 0x63, 0x6b, 0x74, 0x76, 0x78, 0x70, 0x64, 0x6c, 0x64, 0x72, 0x66, 0x62, 0x71,
+ 0x69, 0x62, 0x61, 0x6a, 0x6f, 0x73, 0x6d, 0x77, 0x6d, 0x65, 0x6b, 0x71, 0x64, 0x6c, 0x63, 0x61,
+ 0x6c, 0x71, 0x65, 0x6c, 0x78, 0x74, 0x76, 0x67, 0x74, 0x69, 0x64, 0x76, 0x72, 0x74, 0x75, 0x68,
+ 0x68, 0x63, 0x77, 0x65, 0x64, 0x67, 0x70, 0x70, 0x70, 0x61, 0x6b, 0x65, 0x68, 0x67, 0x6d, 0x72,
+ 0x6e, 0x6a, 0x64, 0x68, 0x62, 0x69, 0x61, 0x76, 0x6b, 0x71, 0x64, 0x74, 0x66, 0x67, 0x6c, 0x71,
+ 0x6b, 0x76, 0x61, 0x77, 0x79, 0x75, 0x76, 0x6c, 0x71, 0x62, 0x6e, 0x61, 0x6b, 0x6d, 0x6e, 0x6d,
+ 0x77, 0x72, 0x75, 0x66, 0x72, 0x6b, 0x62, 0x6b, 0x62, 0x73, 0x75, 0x75, 0x71, 0x61, 0x67, 0x66,
+ 0x79, 0x70, 0x6c, 0x6c, 0x6b, 0x62, 0x63, 0x66, 0x6a, 0x74, 0x76, 0x6d, 0x70, 0x71, 0x6a, 0x62,
+ 0x68, 0x75, 0x72, 0x75, 0x62, 0x74, 0x70, 0x6b, 0x66, 0x61, 0x67, 0x6e, 0x61, 0x6f, 0x6f, 0x68,
+ 0x77, 0x6a, 0x72, 0x78, 0x79, 0x73, 0x71, 0x78, 0x75, 0x61, 0x78, 0x63, 0x6a, 0x75, 0x73, 0x73,
+ 0x62, 0x70, 0x71, 0x75, 0x63, 0x6e, 0x66, 0x72, 0x74, 0x79, 0x6f, 0x6f, 0x73, 0x6a, 0x75, 0x62,
+ 0x65, 0x79, 0x79, 0x79, 0x75, 0x6f, 0x79, 0x78, 0x6e, 0x77, 0x75, 0x62, 0x66, 0x6e, 0x67, 0x73,
+ 0x63, 0x68, 0x65, 0x74, 0x68, 0x75, 0x6a, 0x78, 0x69, 0x6f, 0x70, 0x62, 0x78, 0x65, 0x68, 0x63,
+ 0x75, 0x78, 0x62, 0x6d, 0x73, 0x6a, 0x68, 0x6a, 0x76, 0x6f, 0x76, 0x6a, 0x6c, 0x73, 0x62, 0x6e,
+ 0x64, 0x68, 0x6f, 0x63, 0x63, 0x67, 0x72, 0x78, 0x64, 0x71, 0x74, 0x76, 0x71, 0x62, 0x66, 0x79,
+ 0x70, 0x61, 0x78, 0x61, 0x77, 0x64, 0x69, 0x79, 0x6e, 0x64, 0x78, 0x66, 0x68, 0x75, 0x65, 0x76,
+ 0x70, 0x6a, 0x67, 0x6a, 0x63, 0x67, 0x61, 0x74, 0x67, 0x77, 0x6c, 0x78, 0x78, 0x75, 0x73, 0x64,
+ 0x69, 0x64, 0x69, 0x6f, 0x72, 0x62, 0x70, 0x65, 0x77, 0x74, 0x74, 0x66, 0x76, 0x6e, 0x74, 0x6b,
+ 0x74, 0x77, 0x76, 0x67, 0x62, 0x75, 0x68, 0x62, 0x66, 0x78, 0x77, 0x72, 0x6a, 0x66, 0x6a, 0x64,
+ 0x6b, 0x6c, 0x70, 0x78, 0x63, 0x77, 0x6b, 0x63, 0x62, 0x77, 0x77, 0x77, 0x65, 0x66, 0x73, 0x64,
+ 0x62, 0x76, 0x6b, 0x73, 0x75, 0x6a, 0x70, 0x63, 0x72, 0x6b, 0x71, 0x73, 0x66, 0x61, 0x70, 0x6c,
+ 0x76, 0x64, 0x61, 0x74, 0x69, 0x76, 0x79, 0x6a, 0x76, 0x75, 0x65, 0x6e, 0x67, 0x63, 0x6e, 0x75,
+ 0x65, 0x74, 0x6c, 0x75, 0x78, 0x6a, 0x6b, 0x78, 0x66, 0x68, 0x71, 0x6e, 0x76, 0x68, 0x68, 0x6c,
+ 0x70, 0x6c, 0x66, 0x64, 0x65, 0x72, 0x6f, 0x73, 0x74, 0x75, 0x6f, 0x6b, 0x6c, 0x6b, 0x6b, 0x6a,
+ 0x74, 0x71, 0x66, 0x63, 0x70, 0x69, 0x6e, 0x61, 0x6d, 0x64, 0x6b, 0x73, 0x74, 0x75, 0x6f, 0x6f,
+ 0x61, 0x61, 0x65, 0x6d, 0x73, 0x67, 0x71, 0x77, 0x65, 0x70, 0x73, 0x67, 0x6d, 0x6d, 0x6d, 0x62,
+ 0x74, 0x6c, 0x68, 0x65, 0x68, 0x74, 0x65, 0x76, 0x62, 0x6f, 0x70, 0x6c, 0x74, 0x68, 0x76, 0x6e,
+ 0x6c, 0x68, 0x6b, 0x76, 0x68, 0x68, 0x78, 0x72, 0x79, 0x6a, 0x67, 0x64, 0x71, 0x71, 0x6a, 0x77,
+ 0x6a, 0x74, 0x74, 0x64, 0x72, 0x69, 0x74, 0x74, 0x66, 0x61, 0x71, 0x70, 0x64, 0x68, 0x69, 0x6b,
+ 0x6b, 0x6e, 0x6b, 0x6a, 0x61, 0x70, 0x73, 0x68, 0x70, 0x62, 0x65, 0x77, 0x77, 0x62, 0x64, 0x73,
+ 0x6a, 0x6e, 0x79, 0x76, 0x78, 0x68, 0x6d, 0x6a, 0x6c, 0x76, 0x61, 0x71, 0x73, 0x73, 0x69, 0x78,
+ 0x69, 0x6c, 0x65, 0x73, 0x6f, 0x6d, 0x67, 0x6a, 0x64, 0x6b, 0x6e, 0x73, 0x69, 0x72, 0x64, 0x65,
+ 0x77, 0x71, 0x79, 0x72, 0x67, 0x71, 0x68, 0x6a, 0x63, 0x74, 0x75, 0x70, 0x79, 0x75, 0x73, 0x78,
+ 0x6d, 0x64, 0x76, 0x63, 0x67, 0x64, 0x69, 0x6b, 0x77, 0x73, 0x6a, 0x6d, 0x6b, 0x6d, 0x77, 0x68,
+ 0x62, 0x6c, 0x67, 0x76, 0x6a, 0x6f, 0x64, 0x6a, 0x76, 0x64, 0x61, 0x6a, 0x74, 0x77, 0x68, 0x69,
+ 0x6a, 0x6d, 0x6d, 0x6b, 0x67, 0x6f, 0x76, 0x73, 0x6e, 0x6f, 0x70, 0x67, 0x65, 0x77, 0x68, 0x63,
+ 0x64, 0x75, 0x64, 0x63, 0x79, 0x62, 0x77, 0x78, 0x64, 0x70, 0x6b, 0x70, 0x70, 0x69, 0x75, 0x62,
+ 0x73, 0x76, 0x76, 0x72, 0x67, 0x6f, 0x73, 0x6e, 0x70, 0x74, 0x70, 0x6d, 0x78, 0x79, 0x77, 0x6e,
+ 0x75, 0x77, 0x70, 0x76, 0x6f, 0x62, 0x73, 0x64, 0x78, 0x6f, 0x66, 0x64, 0x67, 0x69, 0x68, 0x79,
+ 0x70, 0x71, 0x72, 0x72, 0x6a, 0x73, 0x6d, 0x78, 0x61, 0x64, 0x67, 0x70, 0x72, 0x77, 0x79, 0x65,
+ 0x6e, 0x72, 0x62, 0x64, 0x74, 0x62, 0x72, 0x72, 0x78, 0x76, 0x79, 0x76, 0x68, 0x63, 0x64, 0x62,
+ 0x67, 0x70, 0x6e, 0x76, 0x6d, 0x6f, 0x74, 0x72, 0x76, 0x61, 0x64, 0x72, 0x74, 0x79, 0x67, 0x64,
+ 0x6f, 0x74, 0x69, 0x68, 0x78, 0x79, 0x65, 0x69, 0x69, 0x64, 0x6c, 0x63, 0x69, 0x62, 0x63, 0x61,
+ 0x64, 0x68, 0x78, 0x65, 0x64, 0x67, 0x6d, 0x6e, 0x6e, 0x67, 0x71, 0x6e, 0x65, 0x76, 0x76, 0x64,
+ 0x6a, 0x66, 0x65, 0x70, 0x77, 0x79, 0x67, 0x74, 0x74, 0x67, 0x6c, 0x69, 0x65, 0x72, 0x6e, 0x70,
+ 0x62, 0x6b, 0x78, 0x65, 0x68, 0x74, 0x70, 0x6a, 0x64, 0x6d, 0x6e, 0x79, 0x76, 0x73, 0x70, 0x72,
+ 0x6f, 0x67, 0x62, 0x75, 0x63, 0x64, 0x74, 0x74, 0x71, 0x78, 0x73, 0x79, 0x69, 0x79, 0x62, 0x66,
+ 0x64, 0x75, 0x6f, 0x76, 0x68, 0x75, 0x72, 0x75, 0x66, 0x71, 0x6d, 0x61, 0x63, 0x68, 0x69, 0x67,
+ 0x61, 0x69, 0x6b, 0x69, 0x77, 0x68, 0x72, 0x69, 0x6b, 0x70, 0x72, 0x6b, 0x75, 0x66, 0x64, 0x79,
+ 0x6b, 0x65, 0x71, 0x69, 0x61, 0x67, 0x6c, 0x75, 0x64, 0x70, 0x75, 0x72, 0x68, 0x65, 0x73, 0x69,
+ 0x67, 0x65, 0x6d, 0x71, 0x66, 0x79, 0x61, 0x64, 0x70, 0x65, 0x73, 0x6a, 0x76, 0x76, 0x79, 0x71,
+ 0x71, 0x6e, 0x76, 0x64, 0x63, 0x6b, 0x73, 0x70, 0x6a, 0x6d, 0x65, 0x73, 0x74, 0x63, 0x6a, 0x6f,
+ 0x73, 0x75, 0x76, 0x72, 0x67, 0x67, 0x61, 0x72, 0x66, 0x70, 0x77, 0x79, 0x78, 0x6a, 0x76, 0x64,
+ 0x61, 0x6c, 0x6f, 0x79, 0x73, 0x62, 0x69, 0x62, 0x64, 0x6e, 0x62, 0x69, 0x67, 0x6d, 0x6e, 0x6b,
+ 0x78, 0x66, 0x64, 0x61, 0x6d, 0x79, 0x63, 0x77, 0x66, 0x61, 0x64, 0x72, 0x6a, 0x6e, 0x74, 0x78,
+ 0x62, 0x63, 0x6f, 0x6f, 0x66, 0x6d, 0x6b, 0x63, 0x64, 0x76, 0x6d, 0x67, 0x75, 0x65, 0x68, 0x6b,
+ 0x68, 0x63, 0x77, 0x6a, 0x70, 0x77, 0x6b, 0x67, 0x61, 0x79, 0x6b, 0x62, 0x79, 0x74, 0x68, 0x73,
+ 0x71, 0x78, 0x75, 0x75, 0x63, 0x64, 0x6f, 0x6a, 0x6b, 0x61, 0x73, 0x75, 0x6e, 0x71, 0x63, 0x68,
+ 0x71, 0x6a, 0x78, 0x6b, 0x68, 0x70, 0x68, 0x73, 0x72, 0x65, 0x73, 0x64, 0x70, 0x75, 0x64, 0x78,
+ 0x78, 0x78, 0x66, 0x76, 0x6f, 0x71, 0x65, 0x63, 0x61, 0x76, 0x74, 0x64, 0x78, 0x6b, 0x72, 0x61,
+ 0x6e, 0x64, 0x6e, 0x65, 0x78, 0x77, 0x63, 0x65, 0x6c, 0x6d, 0x77, 0x6c, 0x79, 0x6b, 0x65, 0x74,
+ 0x78, 0x62, 0x73, 0x66, 0x76, 0x62, 0x79, 0x76, 0x61, 0x79, 0x70, 0x69, 0x64, 0x66, 0x6b, 0x68,
+ 0x72, 0x62, 0x74, 0x6d, 0x6b, 0x75, 0x73, 0x67, 0x68, 0x78, 0x61, 0x6e, 0x62, 0x6f, 0x75, 0x65,
+ 0x72, 0x65, 0x78, 0x75, 0x69, 0x67, 0x70, 0x6e, 0x73, 0x61, 0x71, 0x61, 0x65, 0x77, 0x69, 0x6c,
+ 0x6a, 0x73, 0x62, 0x73, 0x66, 0x6b, 0x6f, 0x71, 0x73, 0x71, 0x62, 0x73, 0x78, 0x6d, 0x77, 0x67,
+ 0x78, 0x75, 0x66, 0x6c, 0x77, 0x6e, 0x65, 0x6c, 0x63, 0x79, 0x77, 0x6f, 0x70, 0x6f, 0x65, 0x75,
+ 0x72, 0x6f, 0x67, 0x63, 0x6c, 0x69, 0x75, 0x70, 0x69, 0x70, 0x6e, 0x67, 0x74, 0x72, 0x72, 0x72,
+ 0x6e, 0x69, 0x70, 0x74, 0x71, 0x68, 0x70, 0x76, 0x6b, 0x62, 0x68, 0x6f, 0x77, 0x66, 0x6b, 0x64,
+ 0x75, 0x66, 0x67, 0x79, 0x65, 0x65, 0x78, 0x61, 0x70, 0x67, 0x77, 0x76, 0x67, 0x6d, 0x64, 0x65,
+ 0x69, 0x62, 0x73, 0x65, 0x67, 0x70, 0x74, 0x6e, 0x67, 0x78, 0x75, 0x69, 0x6a, 0x68, 0x6d, 0x65,
+ 0x78, 0x62, 0x6d, 0x6d, 0x62, 0x68, 0x65, 0x6c, 0x6e, 0x67, 0x6b, 0x61, 0x78, 0x6f, 0x62, 0x79,
+ 0x64, 0x78, 0x6f, 0x65, 0x74, 0x72, 0x73, 0x6d, 0x6c, 0x61, 0x76, 0x77, 0x68, 0x64, 0x71, 0x77,
+ 0x71, 0x79, 0x77, 0x69, 0x6c, 0x66, 0x75, 0x65, 0x79, 0x72, 0x70, 0x75, 0x6b, 0x61, 0x64, 0x6c,
+ 0x67, 0x6e, 0x6e, 0x72, 0x75, 0x68, 0x68, 0x71, 0x62, 0x65, 0x72, 0x72, 0x73, 0x68, 0x71, 0x6a,
+ 0x78, 0x74, 0x74, 0x6a, 0x6a, 0x6f, 0x6d, 0x67, 0x62, 0x61, 0x6b, 0x6a, 0x66, 0x6a, 0x66, 0x6e,
+ 0x75, 0x64, 0x64, 0x74, 0x6e, 0x64, 0x62, 0x6f, 0x71, 0x6d, 0x74, 0x6c, 0x6b, 0x72, 0x6e, 0x68,
+ 0x6a, 0x70, 0x6d, 0x72, 0x66, 0x64, 0x6d, 0x71, 0x6b, 0x66, 0x65, 0x6e, 0x69, 0x74, 0x69, 0x68,
+ 0x78, 0x6c, 0x61, 0x66, 0x79, 0x6e, 0x71, 0x62, 0x75, 0x63, 0x6f, 0x79, 0x6b, 0x78, 0x65, 0x6f,
+ 0x73, 0x70, 0x64, 0x69, 0x74, 0x6d, 0x6d, 0x67, 0x6a, 0x75, 0x79, 0x71, 0x72, 0x67, 0x6f, 0x72,
+ 0x61, 0x6c, 0x79, 0x77, 0x72, 0x67, 0x66, 0x76, 0x77, 0x66, 0x71, 0x79, 0x79, 0x77, 0x74, 0x73,
+ 0x61, 0x63, 0x77, 0x79, 0x6f, 0x6b, 0x70, 0x78, 0x79, 0x62, 0x72, 0x63, 0x78, 0x66, 0x74, 0x65,
+ 0x79, 0x66, 0x63, 0x6a, 0x69, 0x6c, 0x64, 0x62, 0x62, 0x78, 0x64, 0x68, 0x72, 0x73, 0x64, 0x62,
+ 0x67, 0x76, 0x6a, 0x75, 0x74, 0x70, 0x79, 0x6e, 0x6e, 0x64, 0x63, 0x79, 0x63, 0x75, 0x73, 0x67,
+ 0x77, 0x77, 0x6a, 0x69, 0x76, 0x74, 0x75, 0x69, 0x76, 0x71, 0x62, 0x6f, 0x68, 0x70, 0x78, 0x69,
+ 0x75, 0x74, 0x76, 0x66, 0x6c, 0x77, 0x69, 0x66, 0x6b, 0x6b, 0x70, 0x6b, 0x6d, 0x6b, 0x79, 0x76,
+ 0x69, 0x73, 0x64, 0x69, 0x74, 0x64, 0x6b, 0x72, 0x6c, 0x78, 0x64, 0x68, 0x66, 0x63, 0x75, 0x72,
+ 0x6d, 0x6f, 0x68, 0x6e, 0x61, 0x76, 0x72, 0x79, 0x69, 0x67, 0x76, 0x69, 0x70, 0x6e, 0x6a, 0x6c,
+ 0x64, 0x79, 0x68, 0x65, 0x79, 0x63, 0x74, 0x6b, 0x64, 0x72, 0x62, 0x6e, 0x77, 0x6e, 0x6d, 0x62,
+ 0x78, 0x72, 0x77, 0x6c, 0x67, 0x74, 0x6a, 0x75, 0x78, 0x69, 0x65, 0x63, 0x6e, 0x72, 0x70, 0x70,
+ 0x69, 0x69, 0x62, 0x77, 0x61, 0x63, 0x77, 0x62, 0x6e, 0x61, 0x79, 0x66, 0x77, 0x73, 0x75, 0x71,
+ 0x6b, 0x6d, 0x68, 0x74, 0x69, 0x65, 0x6a, 0x67, 0x6c, 0x71, 0x77, 0x6d, 0x68, 0x78, 0x66, 0x68,
+ 0x68, 0x75, 0x6c, 0x65, 0x6e, 0x6e, 0x77, 0x6d, 0x79, 0x62, 0x6c, 0x76, 0x62, 0x6c, 0x69, 0x61,
+ 0x76, 0x68, 0x68, 0x78, 0x6d, 0x68, 0x6d, 0x75, 0x67, 0x6d, 0x68, 0x64, 0x63, 0x68, 0x6e, 0x6d,
+ 0x68, 0x63, 0x76, 0x74, 0x78, 0x78, 0x66, 0x61, 0x64, 0x6a, 0x69, 0x6e, 0x74, 0x66, 0x63, 0x62,
+ 0x68, 0x78, 0x74, 0x73, 0x72, 0x78, 0x6b, 0x76, 0x79, 0x61, 0x65, 0x61, 0x68, 0x6d, 0x73, 0x76,
+ 0x74, 0x69, 0x6c, 0x70, 0x6a, 0x6b, 0x77, 0x72, 0x67, 0x79, 0x65, 0x6c, 0x6a, 0x77, 0x66, 0x73,
+ 0x66, 0x67, 0x71, 0x6f, 0x67, 0x61, 0x68, 0x6c, 0x73, 0x6e, 0x72, 0x67, 0x75, 0x6e, 0x78, 0x79,
+ 0x72, 0x62, 0x78, 0x63, 0x6d, 0x69, 0x79, 0x62, 0x63, 0x70, 0x64, 0x70, 0x67, 0x78, 0x79, 0x6d,
+ 0x76, 0x65, 0x6e, 0x68, 0x70, 0x65, 0x6c, 0x73, 0x71, 0x63, 0x6a, 0x75, 0x75, 0x6f, 0x66, 0x77,
+ 0x72, 0x6d, 0x6b, 0x71, 0x72, 0x6a, 0x6b, 0x71, 0x74, 0x75, 0x64, 0x64, 0x6e, 0x6e, 0x6f, 0x63,
+ 0x74, 0x72, 0x76, 0x76, 0x77, 0x67, 0x65, 0x6a, 0x69, 0x78, 0x63, 0x6a, 0x68, 0x68, 0x63, 0x74,
+ 0x6f, 0x70, 0x61, 0x73, 0x6e, 0x61, 0x64, 0x62, 0x6b, 0x65, 0x61, 0x74, 0x77, 0x62, 0x74, 0x6d,
+ 0x66, 0x62, 0x6e, 0x76, 0x6e, 0x6c, 0x67, 0x78, 0x67, 0x6b, 0x77, 0x64, 0x67, 0x69, 0x73, 0x79,
+ 0x67, 0x77, 0x6e, 0x6f, 0x68, 0x73, 0x6c, 0x69, 0x68, 0x75, 0x65, 0x6c, 0x77, 0x74, 0x77, 0x78,
+ 0x62, 0x62, 0x68, 0x6d, 0x6f, 0x77, 0x74, 0x68, 0x78, 0x67, 0x71, 0x79, 0x62, 0x74, 0x6d, 0x66,
+ 0x78, 0x64, 0x69, 0x6f, 0x65, 0x70, 0x6b, 0x62, 0x68, 0x6c, 0x62, 0x69, 0x63, 0x75, 0x6e, 0x68,
+ 0x79, 0x73, 0x78, 0x6f, 0x6b, 0x78, 0x65, 0x79, 0x71, 0x6e, 0x6a, 0x67, 0x73, 0x62, 0x73, 0x6b,
+ 0x6f, 0x68, 0x6e, 0x65, 0x6e, 0x78, 0x62, 0x61, 0x62, 0x78, 0x6e, 0x71, 0x72, 0x6a, 0x71, 0x67,
+ 0x70, 0x79, 0x67, 0x6e, 0x69, 0x75, 0x74, 0x71, 0x70, 0x78, 0x66, 0x70, 0x61, 0x6f, 0x6a, 0x70,
+ 0x61, 0x76, 0x74, 0x76, 0x67, 0x74, 0x79, 0x65, 0x67, 0x6b, 0x64, 0x78, 0x70, 0x63, 0x66, 0x79,
+ 0x74, 0x65, 0x74, 0x6f, 0x62, 0x79, 0x77, 0x74, 0x77, 0x71, 0x63, 0x61, 0x73, 0x78, 0x6c, 0x77,
+ 0x6c, 0x71, 0x67, 0x62, 0x79, 0x69, 0x67, 0x6a, 0x6f, 0x6c, 0x68, 0x67, 0x64, 0x76, 0x66, 0x73,
+ 0x77, 0x6d, 0x76, 0x65, 0x62, 0x68, 0x68, 0x76, 0x61, 0x68, 0x6d, 0x75, 0x6e, 0x61, 0x76, 0x00, 0x00
+};
+
+
+int main()
+{
+ const int ntests = 10;
+ size_t bufsize = sizeof(buf) - 1;
+ int i;
+ size_t bufsizes[ntests];
+ char old;
+
+ for (i = ntests-1; i >= 0; --i)
+ {
+ bufsizes[i] = bufsize;
+ bufsize /= 2;
+ }
+
+ printf("\n\n");
+ printf("Testing pathological pattern '.+nonexisting.+' to force worst-case asymptotic performance: \n");
+
+ for (i = 0; i < ntests; ++i)
+ {
+ old = buf[bufsizes[i]];
+ buf[bufsizes[i]] = 0;
+
+ printf(" matching on %lu bytes of test input: ", bufsizes[i]);
+ fflush(stdout);
+ printf("%d \n", re_match(".+nonexisting.+", buf));
+
+ buf[bufsizes[i]] = old;
+ }
+
+ printf("\n\n");
+
+ return 0;
+}
+
+
diff --git a/erts/lib_src/yielding_c_fun/lib/tiny_regex_c/tests/test_print.c b/erts/lib_src/yielding_c_fun/lib/tiny_regex_c/tests/test_print.c
new file mode 100644
index 0000000000..42eddbe704
--- /dev/null
+++ b/erts/lib_src/yielding_c_fun/lib/tiny_regex_c/tests/test_print.c
@@ -0,0 +1,21 @@
+/*
+ This program prints out a verbose explanation of a given regular expression.
+*/
+
+#include <stdio.h>
+#include "re.h"
+
+
+int main(int argc, char** argv)
+{
+ if (argc == 2)
+ {
+ re_print(re_compile(argv[1]));
+ }
+ else
+ {
+ printf("\nUsage: %s <PATTERN> \n", argv[0]);
+ }
+ return -2;
+}
+
diff --git a/erts/lib_src/yielding_c_fun/lib/tiny_regex_c/tests/test_rand.c b/erts/lib_src/yielding_c_fun/lib/tiny_regex_c/tests/test_rand.c
new file mode 100644
index 0000000000..5a8baede6c
--- /dev/null
+++ b/erts/lib_src/yielding_c_fun/lib/tiny_regex_c/tests/test_rand.c
@@ -0,0 +1,28 @@
+/*
+ This program tries to match a given regular expression with text given as input to stdin.
+ If the text is a match for the pattern, the program returns 0.
+ If the text doesn't match the pattern, the program returns -2.
+
+ This program is used in random testing to test a lot of random text and regex together.
+ See ./scripts/regex_test.py and the Makefile for this project for the gritty details.
+*/
+
+#include <stdio.h>
+#include "re.h"
+
+
+int main(int argc, char** argv)
+{
+ if (argc == 3)
+ {
+ int m = re_match(argv[1], argv[2]);
+ if (m != -1)
+ return 0;
+ }
+ else
+ {
+ printf("\nUsage: %s <PATTERN> <TEXT> \n", argv[0]);
+ }
+ return -2;
+}
+
diff --git a/erts/lib_src/yielding_c_fun/lib/tiny_regex_c/tests/test_rand_neg.c b/erts/lib_src/yielding_c_fun/lib/tiny_regex_c/tests/test_rand_neg.c
new file mode 100644
index 0000000000..192ce5c608
--- /dev/null
+++ b/erts/lib_src/yielding_c_fun/lib/tiny_regex_c/tests/test_rand_neg.c
@@ -0,0 +1,29 @@
+/*
+ Negative version of test_rand.c -- returns true if no match
+
+ This program tries to match a given regular expression with text given as input to stdin.
+ If the text is NOT a match for the pattern, the program returns 0.
+ If the text does match the pattern, the program returns -2.
+
+ This program is used in random testing to test a lot of random text and regex together.
+ See ./scripts/regex_test_neg.py and the Makefile for this project for the gritty details.
+*/
+
+#include <stdio.h>
+#include "re.h"
+
+
+int main(int argc, char** argv)
+{
+ if (argc == 3)
+ {
+ int m = re_match(argv[1], argv[2]);
+ if (m == -1)
+ return 0;
+ }
+ else
+ {
+ printf("\nUsage: %s <PATTERN> <TEXT> \n", argv[0]);
+ }
+ return -2;
+}
diff --git a/erts/lib_src/yielding_c_fun/main_target.mk b/erts/lib_src/yielding_c_fun/main_target.mk
new file mode 100644
index 0000000000..de37c19897
--- /dev/null
+++ b/erts/lib_src/yielding_c_fun/main_target.mk
@@ -0,0 +1,26 @@
+# This file is included by GNUmakefile
+
+YCF_INCLUDE_DIRS = \
+ -I$(YCF_SOURCE_DIR) \
+ -I$(YCF_SOURCE_DIR)/lib/tiny_regex_c \
+ -I$(YCF_SOURCE_DIR)/lib/simple_c_gc
+
+YCF_HEADERS = $(sort $(shell find $(YCF_SOURCE_DIR) -name '*.h'))
+
+YCF_EXTRA_SOURCES = \
+ $(YCF_SOURCE_DIR)/lib/tiny_regex_c/re.c \
+ $(YCF_SOURCE_DIR)/lib/simple_c_gc/simple_c_gc.c
+
+YCF_SOURCES = $(sort $(wildcard $(YCF_SOURCE_DIR)/*.c) $(YCF_EXTRA_SOURCES))
+
+YCF_OBJECTS = $(patsubst $(YCF_SOURCE_DIR)/%.c, $(YCF_SOURCE_DIR)/%.o, $(YCF_SOURCES))
+
+YCF_CFLAGS = $(filter-out -Wstrict-prototypes -Wdeclaration-after-statement -Wmissing-prototypes,$(CFLAGS))
+
+YCF_EXECUTABLE = $(YCF_SOURCE_DIR)/bin/yielding_c_fun.bin$(EXE_SUFFIX)
+
+$(YCF_EXECUTABLE): $(YCF_OBJECTS)
+ $(V_LD) $(YCF_CFLAGS) $(LDFLAGS) $(YCF_OBJECTS) -o $@
+
+$(YCF_SOURCE_DIR)/%.o: $(YCF_SOURCE_DIR)/%.c $(YCF_HEADERS)
+ $(V_CC) $(YCF_CFLAGS) $(LDFLAGS) $(YCF_INCLUDE_DIRS) -c $< -o $@
diff --git a/erts/lib_src/yielding_c_fun/test/examples/auto_yield.c b/erts/lib_src/yielding_c_fun/test/examples/auto_yield.c
new file mode 100644
index 0000000000..3fa85ba061
--- /dev/null
+++ b/erts/lib_src/yielding_c_fun/test/examples/auto_yield.c
@@ -0,0 +1,124 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB and Kjell Winblad 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%
+ */
+
+/*
+ * Description:
+ *
+ * Author: Kjell Winblad
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#define YCF_YIELD()
+
+
+int rec_inc(int to_inc, int nr_of_incs){
+ int res;
+ printf("nr_of_incs %d\n", nr_of_incs);
+ if(nr_of_incs == 0) {
+ return to_inc;
+ } else {
+ res = rec_inc(to_inc + 1, nr_of_incs -1);
+ return res;
+ }
+}
+
+
+int fun(){
+ int i;
+ int v1;
+ int v2 = 13;
+ int v3 = 7;
+ int v4 = 5;
+ int v5 = 2;
+ printf("REC CALL START\n");
+ v1 = rec_inc(42, 100); /* v1 == 142 */
+ printf("REC CALL END\n");
+ printf("FOR LOOP START\n");
+ for(i = 0; i < 100; i++){
+ v2 = v2 + 1;
+ } /* v2 == 113 */
+ printf("FOR LOOP END\n");
+ printf("WHILE LOOP START\n");
+ i = 0;
+ while(i < 100){
+ v3 = v3 + 1;
+ i++;
+ } /* v3 == 107 */
+ printf("WHILE LOOP END\n");
+ printf("DO WHILE LOOP START\n");
+ i = 0;
+ do {
+ v4 = v4 + 1;
+ i++;
+ } while (i < 100); /* v4 == 105 */
+ printf("DO WHILE LOOP END\n");
+ printf("GOTO START\n");
+ i = 0;
+ my_label:
+ v5 = v5 + 1;
+ i++;
+ if (i < 100) goto my_label; /* v5 == 102 */
+ printf("GOTO END\n");
+ return v1 + v2 + v3 + v4 + v5; /* 142 + 113 + 107 + 105 + 102 == 569 */
+}
+
+void* allocator(size_t size, void* context){
+ (void)context;
+ return malloc(size);
+}
+
+void freer(void* data, void* context){
+ (void)context;
+ free(data);
+}
+
+int main( int argc, const char* argv[] )
+{
+#ifdef YCF_YIELD_CODE_GENERATED
+ void* wb = NULL;
+#endif
+ int ret = 0;
+ long nr_of_reductions;
+#ifdef YCF_YIELD_CODE_GENERATED
+ do{
+ nr_of_reductions = 10;
+ ret = fun_ycf_gen_yielding(&nr_of_reductions,&wb,NULL,allocator,freer,NULL,0,NULL);
+ if(wb != NULL){
+ printf("TRAPPED %ld\n", nr_of_reductions);
+ }
+ }while(wb != NULL);
+ if(wb != NULL){
+ free(wb);
+ }
+ printf("REDS LEFT WHEN READY %ld\n", nr_of_reductions);
+#else
+ ret = fun();
+#endif
+ printf("RETURNED %d\n", ret);
+ if(ret != 569){
+ return 1;
+ }else{
+ return 0;
+ }
+}
+
diff --git a/erts/lib_src/yielding_c_fun/test/examples/auto_yield.c.out b/erts/lib_src/yielding_c_fun/test/examples/auto_yield.c.out
new file mode 100644
index 0000000000..d6962791d0
--- /dev/null
+++ b/erts/lib_src/yielding_c_fun/test/examples/auto_yield.c.out
@@ -0,0 +1,173 @@
+REC CALL START
+nr_of_incs 100
+nr_of_incs 99
+nr_of_incs 98
+nr_of_incs 97
+nr_of_incs 96
+nr_of_incs 95
+nr_of_incs 94
+nr_of_incs 93
+nr_of_incs 92
+TRAPPED 0
+nr_of_incs 91
+nr_of_incs 90
+nr_of_incs 89
+nr_of_incs 88
+nr_of_incs 87
+nr_of_incs 86
+nr_of_incs 85
+nr_of_incs 84
+nr_of_incs 83
+nr_of_incs 82
+TRAPPED 0
+nr_of_incs 81
+nr_of_incs 80
+nr_of_incs 79
+nr_of_incs 78
+nr_of_incs 77
+nr_of_incs 76
+nr_of_incs 75
+nr_of_incs 74
+nr_of_incs 73
+nr_of_incs 72
+TRAPPED 0
+nr_of_incs 71
+nr_of_incs 70
+nr_of_incs 69
+nr_of_incs 68
+nr_of_incs 67
+nr_of_incs 66
+nr_of_incs 65
+nr_of_incs 64
+nr_of_incs 63
+nr_of_incs 62
+TRAPPED 0
+nr_of_incs 61
+nr_of_incs 60
+nr_of_incs 59
+nr_of_incs 58
+nr_of_incs 57
+nr_of_incs 56
+nr_of_incs 55
+nr_of_incs 54
+nr_of_incs 53
+nr_of_incs 52
+TRAPPED 0
+nr_of_incs 51
+nr_of_incs 50
+nr_of_incs 49
+nr_of_incs 48
+nr_of_incs 47
+nr_of_incs 46
+nr_of_incs 45
+nr_of_incs 44
+nr_of_incs 43
+nr_of_incs 42
+TRAPPED 0
+nr_of_incs 41
+nr_of_incs 40
+nr_of_incs 39
+nr_of_incs 38
+nr_of_incs 37
+nr_of_incs 36
+nr_of_incs 35
+nr_of_incs 34
+nr_of_incs 33
+nr_of_incs 32
+TRAPPED 0
+nr_of_incs 31
+nr_of_incs 30
+nr_of_incs 29
+nr_of_incs 28
+nr_of_incs 27
+nr_of_incs 26
+nr_of_incs 25
+nr_of_incs 24
+nr_of_incs 23
+nr_of_incs 22
+TRAPPED 0
+nr_of_incs 21
+nr_of_incs 20
+nr_of_incs 19
+nr_of_incs 18
+nr_of_incs 17
+nr_of_incs 16
+nr_of_incs 15
+nr_of_incs 14
+nr_of_incs 13
+nr_of_incs 12
+TRAPPED 0
+nr_of_incs 11
+nr_of_incs 10
+nr_of_incs 9
+nr_of_incs 8
+nr_of_incs 7
+nr_of_incs 6
+nr_of_incs 5
+nr_of_incs 4
+nr_of_incs 3
+nr_of_incs 2
+TRAPPED 0
+nr_of_incs 1
+nr_of_incs 0
+TRAPPED 0
+TRAPPED 0
+TRAPPED 0
+TRAPPED 0
+TRAPPED 0
+TRAPPED 0
+TRAPPED 0
+TRAPPED 0
+TRAPPED 0
+TRAPPED 0
+REC CALL END
+FOR LOOP START
+TRAPPED 0
+TRAPPED 0
+TRAPPED 0
+TRAPPED 0
+TRAPPED 0
+TRAPPED 0
+TRAPPED 0
+TRAPPED 0
+TRAPPED 0
+TRAPPED 0
+FOR LOOP END
+WHILE LOOP START
+TRAPPED 0
+TRAPPED 0
+TRAPPED 0
+TRAPPED 0
+TRAPPED 0
+TRAPPED 0
+TRAPPED 0
+TRAPPED 0
+TRAPPED 0
+TRAPPED 0
+WHILE LOOP END
+DO WHILE LOOP START
+TRAPPED 0
+TRAPPED 0
+TRAPPED 0
+TRAPPED 0
+TRAPPED 0
+TRAPPED 0
+TRAPPED 0
+TRAPPED 0
+TRAPPED 0
+TRAPPED 0
+DO WHILE LOOP END
+GOTO START
+TRAPPED 0
+TRAPPED 0
+TRAPPED 0
+TRAPPED 0
+TRAPPED 0
+TRAPPED 0
+TRAPPED 0
+TRAPPED 0
+TRAPPED 0
+TRAPPED 0
+GOTO END
+REDS LEFT WHEN READY 8
+RETURNED 569
diff --git a/erts/lib_src/yielding_c_fun/test/examples/const_defenition.c b/erts/lib_src/yielding_c_fun/test/examples/const_defenition.c
new file mode 100644
index 0000000000..683acda063
--- /dev/null
+++ b/erts/lib_src/yielding_c_fun/test/examples/const_defenition.c
@@ -0,0 +1,54 @@
+#include <stdio.h>
+#include <stdlib.h>
+
+#define YCF_YIELD()
+
+
+int fun(const char x){
+ const int y = x + 1; /* y == 2*/
+ int const *ptr = NULL;
+ YCF_YIELD();
+ if(ptr != NULL){
+ printf("ERROR\n");
+ }
+ return y;
+}
+
+void* allocator(size_t size, void* context){
+ (void)context;
+ return malloc(size);
+}
+
+void freer(void* data, void* context){
+ (void)context;
+ free(data);
+}
+
+int main( int argc, const char* argv[] )
+{
+#ifdef YCF_YIELD_CODE_GENERATED
+ void* wb = NULL;
+ long nr_of_reductions = 1;
+#endif
+ int ret = 0;
+#ifdef YCF_YIELD_CODE_GENERATED
+ do{
+ ret = fun_ycf_gen_yielding(&nr_of_reductions,&wb,NULL,allocator,freer,NULL,0,NULL,1);
+ if(wb != NULL){
+ printf("TRAPPED\n");
+ }
+ }while(wb != NULL);
+ if(wb != NULL){
+ free(wb);
+ }
+#else
+ ret = fun(1);
+#endif
+ printf("RETURNED %d\n", ret);
+ if(ret != 2){
+ return 1;
+ }else{
+ return 0;
+ }
+}
+
diff --git a/erts/lib_src/yielding_c_fun/test/examples/const_defenition.c.out b/erts/lib_src/yielding_c_fun/test/examples/const_defenition.c.out
new file mode 100644
index 0000000000..37d51cb983
--- /dev/null
+++ b/erts/lib_src/yielding_c_fun/test/examples/const_defenition.c.out
@@ -0,0 +1,2 @@
+TRAPPED
+RETURNED 2
diff --git a/erts/lib_src/yielding_c_fun/test/examples/consume_reds.c b/erts/lib_src/yielding_c_fun/test/examples/consume_reds.c
new file mode 100644
index 0000000000..fcab3dbe5e
--- /dev/null
+++ b/erts/lib_src/yielding_c_fun/test/examples/consume_reds.c
@@ -0,0 +1,85 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB and Kjell Winblad 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%
+ */
+
+/*
+ * Description:
+ *
+ * Author: Kjell Winblad
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#define YCF_YIELD()
+#define YCF_CONSUME_REDS(X)
+
+int fun(int x){
+ int i;
+ int count = 0;
+ for(i = 0; i < 100; i++){
+ count = count + 2;
+ YCF_CONSUME_REDS(10);
+ }
+ return count + x;
+}
+
+void* allocator(size_t size, void* context){
+ (void)context;
+ return malloc(size);
+}
+
+void freer(void* data, void* context){
+ (void)context;
+ free(data);
+}
+
+int main( int argc, const char* argv[] )
+{
+#ifdef YCF_YIELD_CODE_GENERATED
+ void* wb = NULL;
+#endif
+ int ret = 0;
+ int nr_of_yields = 0;
+ long nr_of_reductions;
+#ifdef YCF_YIELD_CODE_GENERATED
+ do{
+ nr_of_reductions = 101;
+ ret = fun_ycf_gen_yielding(&nr_of_reductions,&wb,NULL,allocator,freer,NULL,0,NULL,1);
+ if(wb != NULL){
+ printf("TRAPPED %ld\n", nr_of_reductions);
+ nr_of_yields++;
+ }
+ }while(wb != NULL);
+ if(wb != NULL){
+ free(wb);
+ }
+#else
+ ret = fun(1);
+#endif
+ printf("Number of yields %d\n", nr_of_yields);
+ printf("RETURNED %d\n", ret);
+ if(ret != 201){
+ return 1;
+ }else{
+ return 0;
+ }
+}
+
diff --git a/erts/lib_src/yielding_c_fun/test/examples/consume_reds.c.out b/erts/lib_src/yielding_c_fun/test/examples/consume_reds.c.out
new file mode 100644
index 0000000000..32b93f33f6
--- /dev/null
+++ b/erts/lib_src/yielding_c_fun/test/examples/consume_reds.c.out
@@ -0,0 +1,11 @@
+TRAPPED -9
+TRAPPED -9
+TRAPPED -9
+TRAPPED -9
+TRAPPED -9
+TRAPPED -9
+TRAPPED -9
+TRAPPED -9
+TRAPPED -9
+Number of yields 9
+RETURNED 201
diff --git a/erts/lib_src/yielding_c_fun/test/examples/control_statements.c b/erts/lib_src/yielding_c_fun/test/examples/control_statements.c
new file mode 100644
index 0000000000..3abac5e57d
--- /dev/null
+++ b/erts/lib_src/yielding_c_fun/test/examples/control_statements.c
@@ -0,0 +1,289 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB and Kjell Winblad 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%
+ */
+
+/*
+ * Description:
+ *
+ * Author: Kjell Winblad
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#define YCF_YIELD()
+
+
+int fun(char x){
+ YCF_YIELD();
+ if(x == 1){
+ /*empty control statement*/
+ } else {
+ /* another empty*/
+ }
+ if(x == 1){
+
+ } else {
+
+ }
+ if(x == 1){
+ int y = 4;
+ YCF_YIELD();
+ printf("s_1 %d %d\n", x, y);
+ }
+ YCF_YIELD();
+ if(x == 1){
+ int y = 4;
+ YCF_YIELD();
+ printf("s_2 %d %d\n", x, y);
+ }else printf("Hej\n");
+ YCF_YIELD();
+ if(x == 1){
+ int y = 4;
+ YCF_YIELD();
+ printf("s_3 %d %d\n", x, y);
+ }else if(x == 1){
+ int y = 4;
+ printf("Hej %d %d\n", x, y);
+ }
+ YCF_YIELD();
+ if(x == 2){
+ int y = 4;
+ printf("Hej %d %d\n", x, y);
+ }else if(x == 1){
+ int y = 4;
+ YCF_YIELD();
+ printf("s_4 %d %d\n", x, y);
+ }
+ YCF_YIELD();
+ if(x == 2){
+ int y = 4;
+ printf("Hej %d %d\n", x, y);
+ }else{
+ int y = 4;
+ YCF_YIELD();
+ printf("s_5 %d %d\n", x, y);
+ }
+ YCF_YIELD();
+ while(x == 1) {
+ int y = 4;
+ x = 2;
+ YCF_YIELD();
+ printf("s_6 %d %d\n", x, y);
+ }
+ YCF_YIELD();
+ x = 1;
+ YCF_YIELD();
+ do x++; while(x == 1);
+ YCF_YIELD();
+ printf("s_7 %d\n", x);
+ YCF_YIELD();
+ x = 1;
+ do {
+ int y = 4;
+ YCF_YIELD();
+ printf("s_8 %d %d\n", x, y);
+ YCF_YIELD();
+ x++;
+ } while(x == 2);
+ YCF_YIELD();
+ x = 1;
+ YCF_YIELD();
+ while(x==1){
+ int y = 4;
+ YCF_YIELD();
+ printf("s_9 %d %d\n", x, y);
+ YCF_YIELD();
+ x++;
+ }
+ YCF_YIELD();
+ x = 2;
+ for(;;){
+ int y = 4;
+ YCF_YIELD();
+ printf("s_10 %d %d\n", x, y);
+ YCF_YIELD();
+ break;
+ }
+ YCF_YIELD();
+ for(x = 0;x < 10;x++){
+ int y = 4;
+ YCF_YIELD();
+ printf("s_11 %d %d\n", x, y);
+ }
+ YCF_YIELD();
+ x = 42;
+ YCF_YIELD();
+ switch(x){
+ int y;
+ case 42:
+ y = 4;
+ YCF_YIELD();
+ printf("s_12 %d %d\n", x, y);
+ YCF_YIELD();
+ }
+ YCF_YIELD();
+ {
+ x = 1;
+ YCF_YIELD();
+ if(x == 1) if(1){
+ int y = 4;
+ YCF_YIELD();
+ printf("s_1 %d %d\n", x, y);
+ }
+ YCF_YIELD();
+ if(x == 1) switch(1){
+ int y;
+ case 1:;
+ y = 4;
+ YCF_YIELD();
+ printf("s_2 %d %d\n", x, y);
+ }else printf("Hej\n");
+ YCF_YIELD();
+ if(x == 1) switch(1){
+ int y;
+ case 1:;
+ y = 4;
+ YCF_YIELD();
+ printf("s_3 %d %d\n", x, y);
+ }else if(x == 1){
+ int y = 4;
+ printf("Hej %d %d\n", x, y);
+ }
+ YCF_YIELD();
+ if(x == 2){
+ int y = 4;
+ printf("Hej %d %d\n", x, y);
+ }else if(x == 1) if(1){
+ int y = 4;
+ YCF_YIELD();
+ printf("s_4 %d %d\n", x, y);
+ }
+ YCF_YIELD();
+ if(x == 2){
+ int y = 4;
+ printf("Hej %d %d\n", x, y);
+ }else if(1){
+ int y = 4;
+ YCF_YIELD();
+ printf("s_5 %d %d\n", x, y);
+ }
+ YCF_YIELD();
+ while(x == 1) if(1) {
+ int y = 4;
+ x = 2;
+ YCF_YIELD();
+ printf("s_6 %d %d\n", x, y);
+ }
+ YCF_YIELD();
+ x = 1;
+ YCF_YIELD();
+ do if(1) x++; while(x == 1);
+ YCF_YIELD();
+ printf("s_7 %d\n", x);
+ YCF_YIELD();
+ x = 1;
+ do if(1){
+ int y = 4;
+ YCF_YIELD();
+ printf("s_8 %d %d\n", x, y);
+ YCF_YIELD();
+ x++;
+ } while(x == 2);
+ YCF_YIELD();
+ x = 1;
+ YCF_YIELD();
+ while(x==1) if(1){
+ int y = 4;
+ YCF_YIELD();
+ printf("s_9 %d %d\n", x, y);
+ YCF_YIELD();
+ x++;
+ }
+ YCF_YIELD();
+ x = 2;
+ for(;;) if(1){
+ int y = 4;
+ YCF_YIELD();
+ printf("s_10 %d %d\n", x, y);
+ YCF_YIELD();
+ break;
+ }
+ YCF_YIELD();
+ for(x = 0;x < 10;x++) if(1){
+ int y = 4;
+ YCF_YIELD();
+ printf("s_11 %d %d\n", x, y);
+ }
+ YCF_YIELD();
+ x = 42;
+ YCF_YIELD();
+ switch(x){
+ int y;
+ case 42:
+ y = 4;
+ YCF_YIELD();
+ printf("s_12 %d %d\n", x, y);
+ YCF_YIELD();
+ }
+ YCF_YIELD();
+ }
+ return x;
+}
+
+void* allocator(size_t size, void* context){
+ (void)context;
+ return malloc(size);
+}
+
+void freer(void* data, void* context){
+ (void)context;
+ free(data);
+}
+
+int main( int argc, const char* argv[] )
+{
+#ifdef YCF_YIELD_CODE_GENERATED
+ void* wb = NULL;
+#endif
+ int ret = 0;
+ int nr_of_trapps = 0;
+ long nr_of_reductions = 1;
+#ifdef YCF_YIELD_CODE_GENERATED
+ do{
+ ret = fun_ycf_gen_yielding(&nr_of_reductions,&wb,NULL,allocator,freer,NULL,0,NULL,1);
+ if(wb != NULL){
+ nr_of_trapps++;
+ }
+ }while(wb != NULL);
+ if(wb != NULL){
+ free(wb);
+ }
+#else
+ ret = fun(1);
+ nr_of_trapps = 86;
+#endif
+ printf("RETURNED %d %d\n", ret, nr_of_trapps);
+ if(ret != 42){
+ return 1;
+ }else{
+ return 0;
+ }
+}
diff --git a/erts/lib_src/yielding_c_fun/test/examples/control_statements.c.out b/erts/lib_src/yielding_c_fun/test/examples/control_statements.c.out
new file mode 100644
index 0000000000..1beeae592e
--- /dev/null
+++ b/erts/lib_src/yielding_c_fun/test/examples/control_statements.c.out
@@ -0,0 +1,45 @@
+s_1 1 4
+s_2 1 4
+s_3 1 4
+s_4 1 4
+s_5 1 4
+s_6 2 4
+s_7 2
+s_8 1 4
+s_8 2 4
+s_9 1 4
+s_10 2 4
+s_11 0 4
+s_11 1 4
+s_11 2 4
+s_11 3 4
+s_11 4 4
+s_11 5 4
+s_11 6 4
+s_11 7 4
+s_11 8 4
+s_11 9 4
+s_12 42 4
+s_1 1 4
+s_2 1 4
+s_3 1 4
+s_4 1 4
+s_5 1 4
+s_6 2 4
+s_7 2
+s_8 1 4
+s_8 2 4
+s_9 1 4
+s_10 2 4
+s_11 0 4
+s_11 1 4
+s_11 2 4
+s_11 3 4
+s_11 4 4
+s_11 5 4
+s_11 6 4
+s_11 7 4
+s_11 8 4
+s_11 9 4
+s_12 42 4
+RETURNED 42 86
diff --git a/erts/lib_src/yielding_c_fun/test/examples/custom_code_save_restore_yield_state.c b/erts/lib_src/yielding_c_fun/test/examples/custom_code_save_restore_yield_state.c
new file mode 100644
index 0000000000..739cd9702a
--- /dev/null
+++ b/erts/lib_src/yielding_c_fun/test/examples/custom_code_save_restore_yield_state.c
@@ -0,0 +1,129 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB and Kjell Winblad 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%
+ */
+
+/*
+ * Description:
+ *
+ * Author: Kjell Winblad
+ *
+ */
+
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#define YCF_YIELD()
+
+int fun(char x){
+ int y = 10;
+ /*special_code_start:ON_SAVE_YIELD_STATE*/
+ if(0){
+ printf("y=%d\n", y);
+ y = 42;
+ }
+ /*special_code_end*/
+ /*special_code_start:ON_RETURN*/
+ if(0){
+ printf("I returned y=%d\n", y);
+ }
+ /*special_code_end*/
+ /*special_code_start:ON_DESTROY_STATE_OR_RETURN*/
+ if(0){
+ printf("I got destroyed or returned y=%d\n", y);
+ }
+ /*special_code_end*/
+ /*special_code_start:ON_RESTORE_YIELD_STATE*/
+ if(0){
+ x = 9;
+ }
+ /*special_code_end*/
+ if(0){
+ /*special_code_start:ON_SAVE_YIELD_STATE*/
+ if(0){
+ int z = 10;
+ printf("y=%d z=%d\n", y, z);
+ }
+ /*special_code_end*/
+ }
+ if(y != 10 || x != 1){
+ /*special_code_start:ON_RESTORE_YIELD_STATE*/
+ if(0){
+ printf("y=%d x=%d\n", y, x);
+ x = x*2;
+ printf("y=%d x=%d\n", y, x);
+ }
+ /*special_code_end*/
+ /*special_code_start:ON_RESTORE_YIELD_STATE*/
+ if(0){
+ printf("y=%d x=%d\n", y, x);
+ x = x/2;
+ printf("y=%d x=%d\n", y, x);
+ }
+ /*special_code_end*/
+ printf("ERROR BEFORE YIELD\n");
+ exit(1);
+ }
+ YCF_YIELD();
+ if(y != 42 || x != 9){
+ printf("ERROR AFTER YIELD\n");
+ exit(1);
+ }
+ printf("SUCCESS\n");
+ return x;
+}
+
+void* allocator(size_t size, void* context){
+ (void)context;
+ return malloc(size);
+}
+
+void freer(void* data, void* context){
+ (void)context;
+ free(data);
+}
+
+int main( int argc, const char* argv[] )
+{
+#ifdef YCF_YIELD_CODE_GENERATED
+ void* wb = NULL;
+#endif
+ int ret = 0;
+ long nr_of_reductions = 1;
+#ifdef YCF_YIELD_CODE_GENERATED
+ do{
+ ret = fun_ycf_gen_yielding(&nr_of_reductions,&wb,NULL,allocator,freer,NULL,0,NULL,1);
+ if(wb != NULL){
+ printf("TRAPPED\n");
+ }
+ }while(wb != NULL);
+ if(wb != NULL){
+ free(wb);
+ }
+#else
+ ret = fun(1);
+#endif
+ printf("RETURNED %d\n", ret);
+ if(ret != 9){
+ return 1;
+ }else{
+ return 0;
+ }
+}
+
diff --git a/erts/lib_src/yielding_c_fun/test/examples/custom_code_save_restore_yield_state.c.out b/erts/lib_src/yielding_c_fun/test/examples/custom_code_save_restore_yield_state.c.out
new file mode 100644
index 0000000000..639fd64b46
--- /dev/null
+++ b/erts/lib_src/yielding_c_fun/test/examples/custom_code_save_restore_yield_state.c.out
@@ -0,0 +1,11 @@
+y=10
+y=42 z=10
+TRAPPED
+y=42 x=9
+y=42 x=18
+y=42 x=18
+y=42 x=9
+SUCCESS
+I returned y=42
+I got destroyed or returned y=42
+RETURNED 9
diff --git a/erts/lib_src/yielding_c_fun/test/examples/custom_code_save_restore_yield_state_alt_syntax.c b/erts/lib_src/yielding_c_fun/test/examples/custom_code_save_restore_yield_state_alt_syntax.c
new file mode 100644
index 0000000000..28e1512ece
--- /dev/null
+++ b/erts/lib_src/yielding_c_fun/test/examples/custom_code_save_restore_yield_state_alt_syntax.c
@@ -0,0 +1,131 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB and Kjell Winblad 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%
+ */
+
+/*
+ * Description:
+ *
+ * Author: Kjell Winblad
+ *
+ */
+
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#define YCF_YIELD()
+
+#ifndef YCF_YIELDING_C_FUN_HELPERS
+
+#define ON_SAVE_YIELD_STATE
+#define ON_RESTORE_YIELD_STATE
+#define ON_DESTROY_STATE
+#define ON_RETURN
+#define ON_DESTROY_STATE_OR_RETURN
+#define YCF_SPECIAL_CODE_START(PARAM) \
+ /*special_code_start:PARAM*/ \
+ if(0){
+#define YCF_SPECIAL_CODE_END() \
+ } \
+ /*special_code_end*/
+
+#endif
+
+int fun(char x){
+ int y = 10;
+ YCF_SPECIAL_CODE_START(ON_SAVE_YIELD_STATE);
+ printf("y=%d\n", y);
+ y = 42;
+ YCF_SPECIAL_CODE_END();
+ YCF_SPECIAL_CODE_START(ON_RETURN);
+ printf("I returned y=%d\n", y);
+ YCF_SPECIAL_CODE_END();
+ YCF_SPECIAL_CODE_START(ON_DESTROY_STATE_OR_RETURN);
+ printf("I got destroyed or returned y=%d\n", y);
+ YCF_SPECIAL_CODE_END();
+ YCF_SPECIAL_CODE_START(ON_RESTORE_YIELD_STATE);
+ x = 9;
+ YCF_SPECIAL_CODE_END();
+ if(0){
+ YCF_SPECIAL_CODE_START(ON_SAVE_YIELD_STATE); {
+ int z = 10;
+ printf("y=%d z=%d\n", y, z);
+ } YCF_SPECIAL_CODE_END();
+ }
+ if(y != 10 || x != 1){
+ YCF_SPECIAL_CODE_START(ON_RESTORE_YIELD_STATE);
+ printf("y=%d x=%d\n", y, x);
+ x = x*2;
+ printf("y=%d x=%d\n", y, x);
+ YCF_SPECIAL_CODE_END();
+ YCF_SPECIAL_CODE_START(ON_RESTORE_YIELD_STATE);
+ printf("y=%d x=%d\n", y, x);
+ x = x/2;
+ printf("y=%d x=%d\n", y, x);
+ YCF_SPECIAL_CODE_END();
+ printf("ERROR BEFORE YIELD\n");
+ exit(1);
+ }
+ YCF_YIELD();
+ if(y != 42 || x != 9){
+ printf("ERROR AFTER YIELD\n");
+ exit(1);
+ }
+ printf("SUCCESS\n");
+ return x;
+}
+
+void* allocator(size_t size, void* context){
+ (void)context;
+ return malloc(size);
+}
+
+void freer(void* data, void* context){
+ (void)context;
+ free(data);
+}
+
+int main( int argc, const char* argv[] )
+{
+#ifdef YCF_YIELD_CODE_GENERATED
+ void* wb = NULL;
+#endif
+ int ret = 0;
+ long nr_of_reductions = 1;
+#ifdef YCF_YIELD_CODE_GENERATED
+ do{
+ ret = fun_ycf_gen_yielding(&nr_of_reductions,&wb,NULL,allocator,freer,NULL,0,NULL,1);
+ if(wb != NULL){
+ printf("TRAPPED\n");
+ }
+ }while(wb != NULL);
+ if(wb != NULL){
+ free(wb);
+ }
+#else
+ ret = fun(1);
+#endif
+ printf("RETURNED %d\n", ret);
+ if(ret != 9){
+ return 1;
+ }else{
+ return 0;
+ }
+}
+
diff --git a/erts/lib_src/yielding_c_fun/test/examples/custom_code_save_restore_yield_state_alt_syntax.c.out b/erts/lib_src/yielding_c_fun/test/examples/custom_code_save_restore_yield_state_alt_syntax.c.out
new file mode 100644
index 0000000000..639fd64b46
--- /dev/null
+++ b/erts/lib_src/yielding_c_fun/test/examples/custom_code_save_restore_yield_state_alt_syntax.c.out
@@ -0,0 +1,11 @@
+y=10
+y=42 z=10
+TRAPPED
+y=42 x=9
+y=42 x=18
+y=42 x=18
+y=42 x=9
+SUCCESS
+I returned y=42
+I got destroyed or returned y=42
+RETURNED 9
diff --git a/erts/lib_src/yielding_c_fun/test/examples/debug_ptr_to_stack.c b/erts/lib_src/yielding_c_fun/test/examples/debug_ptr_to_stack.c
new file mode 100644
index 0000000000..76d500de24
--- /dev/null
+++ b/erts/lib_src/yielding_c_fun/test/examples/debug_ptr_to_stack.c
@@ -0,0 +1,54 @@
+#include <stdio.h>
+#include <stdlib.h>
+
+#define YCF_YIELD()
+
+int fun(){
+ int array[1];
+ int* ptr;
+ array[0] = 1;
+ ptr = &array[0];
+ (void)ptr;
+ YCF_YIELD();
+ array[0] = 1;
+ return array[0];
+}
+
+void* allocator(size_t size, void* context){
+ (void)context;
+ return malloc(size);
+}
+
+void freer(void* data, void* context){
+ (void)context;
+ free(data);
+}
+
+int main( int argc, const char* argv[] )
+{
+#ifdef YCF_YIELD_CODE_GENERATED
+ void* wb = NULL;
+ long nr_of_reductions = 1;
+#endif
+ int ret = 0;
+#ifdef YCF_YIELD_CODE_GENERATED
+ do{
+ ret = fun_ycf_gen_yielding(&nr_of_reductions,&wb,NULL,allocator,freer,NULL,0,NULL);
+ if(wb != NULL){
+ printf("TRAPPED\n");
+ }
+ }while(wb != NULL);
+ if(wb != NULL){
+ free(wb);
+ }
+#else
+ fun();
+#endif
+ printf("RETURNED %d\n", ret);
+ if(ret != 1){
+ return 1;
+ }else{
+ return 0;
+ }
+}
+
diff --git a/erts/lib_src/yielding_c_fun/test/examples/debug_ptr_to_stack.c.out b/erts/lib_src/yielding_c_fun/test/examples/debug_ptr_to_stack.c.out
new file mode 100644
index 0000000000..7c3cfb5078
--- /dev/null
+++ b/erts/lib_src/yielding_c_fun/test/examples/debug_ptr_to_stack.c.out
@@ -0,0 +1,2 @@
+TRAPPED
+RETURNED 1
diff --git a/erts/lib_src/yielding_c_fun/test/examples/declarations.c b/erts/lib_src/yielding_c_fun/test/examples/declarations.c
new file mode 100644
index 0000000000..718471c73f
--- /dev/null
+++ b/erts/lib_src/yielding_c_fun/test/examples/declarations.c
@@ -0,0 +1,91 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB and Kjell Winblad 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%
+ */
+
+/*
+ * Description:
+ *
+ * Author: Kjell Winblad
+ *
+ */
+
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#define YCF_YIELD()
+
+
+unsigned long fun(char x, char* y, char **z, unsigned long a, unsigned long* b, unsigned long** c){
+ unsigned int f = 3 + 3;
+ x = x + 1;
+ y = y + 1;
+ z = z + 1;
+ a = a + 1;
+ b = b + 1;
+ c = c + 1;
+ YCF_YIELD();
+ f = f - 6;
+ x = x + 1;
+ y = y + 1;
+ z = z + 1;
+ a = a + 1;
+ b = b + 1;
+ c = c + 1;
+ return (unsigned long)((long)(x + y) + ((z + a) + ((long)b + (long)c))) + f;
+}
+
+void* allocator(size_t size, void* context){
+ (void)context;
+ return malloc(size);
+}
+
+void freer(void* data, void* context){
+ (void)context;
+ free(data);
+}
+
+int main( int argc, const char* argv[] )
+{
+#ifdef YCF_YIELD_CODE_GENERATED
+ void* wb = NULL;
+#endif
+ int ret = 0;
+ long nr_of_reductions = 1;
+#ifdef YCF_YIELD_CODE_GENERATED
+ do{
+ ret = fun_ycf_gen_yielding(&nr_of_reductions,&wb,NULL,allocator,freer,NULL,0,NULL,1,(void*)1,(void*)1,1,(void*)1,(void*)1);
+ if(wb != NULL){
+ printf("TRAPPED\n");
+ }
+ }while(wb != NULL);
+ if(wb != NULL){
+ free(wb);
+ }
+#else
+ ret = fun(1,(void*)1,(void*)1,1,(void*)1,(void*)1);
+#endif
+ printf("RETURNED %d\n", ret);
+ if(ret != fun(1,(void*)1,(void*)1,1,(void*)1,(void*)1)){
+ return 1;
+ }else{
+ return 0;
+ }
+}
+
diff --git a/erts/lib_src/yielding_c_fun/test/examples/declarations.c.out b/erts/lib_src/yielding_c_fun/test/examples/declarations.c.out
new file mode 100644
index 0000000000..d96b566da5
--- /dev/null
+++ b/erts/lib_src/yielding_c_fun/test/examples/declarations.c.out
@@ -0,0 +1,2 @@
+TRAPPED
+RETURNED 361
diff --git a/erts/lib_src/yielding_c_fun/test/examples/declarations_in_for_loops.c b/erts/lib_src/yielding_c_fun/test/examples/declarations_in_for_loops.c
new file mode 100644
index 0000000000..a8d2b02f02
--- /dev/null
+++ b/erts/lib_src/yielding_c_fun/test/examples/declarations_in_for_loops.c
@@ -0,0 +1,83 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB and Kjell Winblad 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%
+ */
+
+/*
+ * Description:
+ *
+ * Author: Kjell Winblad
+ *
+ */
+
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#define YCF_YIELD()
+
+
+int fun(){
+ int y = 0;
+ for(int x = 0; x < 10; x++){
+ y++;
+ for(int x = 0; x < 10; x++){
+ y++;
+ }
+ }
+ return y;
+}
+
+void* allocator(size_t size, void* context){
+ (void)context;
+ return malloc(size);
+}
+
+void freer(void* data, void* context){
+ (void)context;
+ free(data);
+}
+
+int main( int argc, const char* argv[] )
+{
+#ifdef YCF_YIELD_CODE_GENERATED
+ void* wb = NULL;
+#endif
+ int ret = 0;
+ long nr_of_reductions = 1;
+#ifdef YCF_YIELD_CODE_GENERATED
+ do{
+ nr_of_reductions = 1;
+ ret = fun_ycf_gen_yielding(&nr_of_reductions,&wb,NULL,allocator,freer,NULL,0,NULL);
+ if(wb != NULL){
+ printf("TRAPPED\n");
+ }
+ }while(wb != NULL);
+ if(wb != NULL){
+ free(wb);
+ }
+#else
+ ret = fun(1);
+#endif
+ printf("RETURNED %d\n", ret);
+ if(ret != 110){
+ return 1;
+ }else{
+ return 0;
+ }
+}
diff --git a/erts/lib_src/yielding_c_fun/test/examples/declarations_in_for_loops.c.out b/erts/lib_src/yielding_c_fun/test/examples/declarations_in_for_loops.c.out
new file mode 100644
index 0000000000..88a9c3bbd1
--- /dev/null
+++ b/erts/lib_src/yielding_c_fun/test/examples/declarations_in_for_loops.c.out
@@ -0,0 +1,211 @@
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+RETURNED 110
diff --git a/erts/lib_src/yielding_c_fun/test/examples/destroy_while_yielded.c b/erts/lib_src/yielding_c_fun/test/examples/destroy_while_yielded.c
new file mode 100644
index 0000000000..aedaf74fb0
--- /dev/null
+++ b/erts/lib_src/yielding_c_fun/test/examples/destroy_while_yielded.c
@@ -0,0 +1,92 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB and Kjell Winblad 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%
+ */
+
+/*
+ * Description:
+ *
+ * Author: Kjell Winblad
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#define YCF_YIELD()
+
+
+void fun(int level){
+ void* my_mem = malloc(1000);
+ /*special_code_start:ON_DESTROY_STATE*/
+ if(0){
+ printf("FREE AT LEVEL %d\n", level);
+ free(my_mem);
+ }
+ /*special_code_end*/
+ /*special_code_start:ON_DESTROY_STATE_OR_RETURN*/
+ if(0){
+ printf("I got destroyed or returned %d\n", level);
+ }
+ /*special_code_end*/
+ printf("LEVEL %d\n", level);
+ if (level == 10) {
+ YCF_YIELD();
+ printf("SHOULD NOT BE PRINTED 1\n");
+ return;
+ }else {
+ fun(level + 1);
+ printf("SHOULD NOT BE PRINTED 2\n");
+ }
+}
+
+void* allocator(size_t size, void* context){
+ (void)context;
+ return malloc(size);
+}
+
+void freer(void* data, void* context){
+ (void)context;
+ free(data);
+}
+
+int main( int argc, const char* argv[] )
+{
+#ifdef YCF_YIELD_CODE_GENERATED
+ void* wb = NULL;
+ long nr_of_reductions = 1;
+ do {
+ fun_ycf_gen_yielding(&nr_of_reductions,&wb,NULL,allocator,freer,NULL,0,NULL,1);
+ if(wb != NULL){
+ printf("TRAPPED\n");
+ fun_ycf_gen_destroy(wb);
+ printf("DESTROYED\n");
+ wb = NULL;
+ break;
+ }
+ } while(wb != NULL);
+ if(wb != NULL){
+ free(wb);
+ }
+#else
+ fun(1);
+#endif
+ printf("RETURNED\n");
+ return 0;
+}
+
diff --git a/erts/lib_src/yielding_c_fun/test/examples/destroy_while_yielded.c.out b/erts/lib_src/yielding_c_fun/test/examples/destroy_while_yielded.c.out
new file mode 100644
index 0000000000..bdf7893e32
--- /dev/null
+++ b/erts/lib_src/yielding_c_fun/test/examples/destroy_while_yielded.c.out
@@ -0,0 +1,33 @@
+LEVEL 1
+LEVEL 2
+LEVEL 3
+LEVEL 4
+LEVEL 5
+LEVEL 6
+LEVEL 7
+LEVEL 8
+LEVEL 9
+LEVEL 10
+TRAPPED
+I got destroyed or returned 1
+FREE AT LEVEL 1
+I got destroyed or returned 2
+FREE AT LEVEL 2
+I got destroyed or returned 3
+FREE AT LEVEL 3
+I got destroyed or returned 4
+FREE AT LEVEL 4
+I got destroyed or returned 5
+FREE AT LEVEL 5
+I got destroyed or returned 6
+FREE AT LEVEL 6
+I got destroyed or returned 7
+FREE AT LEVEL 7
+I got destroyed or returned 8
+FREE AT LEVEL 8
+I got destroyed or returned 9
+FREE AT LEVEL 9
+I got destroyed or returned 10
+FREE AT LEVEL 10
+DESTROYED
+RETURNED
diff --git a/erts/lib_src/yielding_c_fun/test/examples/in_code_var_declaration.c b/erts/lib_src/yielding_c_fun/test/examples/in_code_var_declaration.c
new file mode 100644
index 0000000000..a99fed5753
--- /dev/null
+++ b/erts/lib_src/yielding_c_fun/test/examples/in_code_var_declaration.c
@@ -0,0 +1,54 @@
+#include <stdio.h>
+#include <stdlib.h>
+
+#define YCF_YIELD()
+
+
+int fun(char x){
+ x = x + 1; /* x == 2*/
+ int y;
+ y = 1;
+ int z = 1;
+ YCF_YIELD();
+ x = x + y + z; /* x == 4*/
+ return x;
+}
+
+void* allocator(size_t size, void* context){
+ (void)context;
+ return malloc(size);
+}
+
+void freer(void* data, void* context){
+ (void)context;
+ free(data);
+}
+
+int main( int argc, const char* argv[] )
+{
+#ifdef YCF_YIELD_CODE_GENERATED
+ void* wb = NULL;
+ long nr_of_reductions = 1;
+#endif
+ int ret = 0;
+#ifdef YCF_YIELD_CODE_GENERATED
+ do{
+ ret = fun_ycf_gen_yielding(&nr_of_reductions,&wb,NULL,allocator,freer,NULL,0,NULL,1);
+ if(wb != NULL){
+ printf("TRAPPED\n");
+ }
+ }while(wb != NULL);
+ if(wb != NULL){
+ free(wb);
+ }
+#else
+ fun(1);
+#endif
+ printf("RETURNED %d\n", ret);
+ if(ret != 4){
+ return 1;
+ }else{
+ return 0;
+ }
+}
+
diff --git a/erts/lib_src/yielding_c_fun/test/examples/in_code_var_declaration.c.out b/erts/lib_src/yielding_c_fun/test/examples/in_code_var_declaration.c.out
new file mode 100644
index 0000000000..cf0b7ff2c6
--- /dev/null
+++ b/erts/lib_src/yielding_c_fun/test/examples/in_code_var_declaration.c.out
@@ -0,0 +1,2 @@
+TRAPPED
+RETURNED 4
diff --git a/erts/lib_src/yielding_c_fun/test/examples/multi_scope_yield.c b/erts/lib_src/yielding_c_fun/test/examples/multi_scope_yield.c
new file mode 100644
index 0000000000..87024abe39
--- /dev/null
+++ b/erts/lib_src/yielding_c_fun/test/examples/multi_scope_yield.c
@@ -0,0 +1,93 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB and Kjell Winblad 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%
+ */
+
+/*
+ * Description:
+ *
+ * Author: Kjell Winblad
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#define YCF_YIELD()
+
+
+int fun(char x){
+ long y = x + 1; /* y == 2*/
+ YCF_YIELD();
+ {
+ int x = 10;
+ YCF_YIELD();
+ y = y + x; /* y == 12*/
+ YCF_YIELD();
+ {
+ long x = 30;
+ YCF_YIELD();
+ y = y + x; /* y == 42*/
+ YCF_YIELD();
+ }
+ YCF_YIELD();
+ y = y + x; /* y == 52*/
+ }
+ YCF_YIELD();
+ y = y + x; /* y == 53*/
+ YCF_YIELD();
+ return y;
+}
+
+void* allocator(size_t size, void* context){
+ (void)context;
+ return malloc(size);
+}
+
+void freer(void* data, void* context){
+ (void)context;
+ free(data);
+}
+
+int main( int argc, const char* argv[] )
+{
+#ifdef YCF_YIELD_CODE_GENERATED
+ void* wb = NULL;
+#endif
+ int ret = 0;
+ long nr_of_reductions = 1;
+#ifdef YCF_YIELD_CODE_GENERATED
+ do{
+ ret = fun_ycf_gen_yielding(&nr_of_reductions,&wb,NULL,allocator,freer,NULL,0,NULL,1);
+ if(wb != NULL){
+ printf("TRAPPED\n");
+ }
+ }while(wb != NULL);
+ if(wb != NULL){
+ free(wb);
+ }
+#else
+ fun(1);
+#endif
+ printf("RETURNED %d\n", ret);
+ if(ret != 53){
+ return 1;
+ }else{
+ return 0;
+ }
+}
diff --git a/erts/lib_src/yielding_c_fun/test/examples/multi_scope_yield.c.out b/erts/lib_src/yielding_c_fun/test/examples/multi_scope_yield.c.out
new file mode 100644
index 0000000000..bf653730af
--- /dev/null
+++ b/erts/lib_src/yielding_c_fun/test/examples/multi_scope_yield.c.out
@@ -0,0 +1,9 @@
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+RETURNED 53
diff --git a/erts/lib_src/yielding_c_fun/test/examples/nested_call_consume_reds.c b/erts/lib_src/yielding_c_fun/test/examples/nested_call_consume_reds.c
new file mode 100644
index 0000000000..239de1a334
--- /dev/null
+++ b/erts/lib_src/yielding_c_fun/test/examples/nested_call_consume_reds.c
@@ -0,0 +1,91 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB and Kjell Winblad 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%
+ */
+
+/*
+ * Description:
+ *
+ * Author: Kjell Winblad
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#define YCF_YIELD()
+#define YCF_CONSUME_REDS(X)
+
+int sub_fun(int x){
+ int i;
+ int count = 0;
+ for(i = 0; i < 100; i++){
+ count = count + 2;
+ YCF_CONSUME_REDS(10);
+ }
+ return count + x;
+}
+
+int fun(int x){
+ int ret;
+ ret = sub_fun(x);
+ return ret;
+}
+
+void* allocator(size_t size, void* context){
+ (void)context;
+ return malloc(size);
+}
+
+void freer(void* data, void* context){
+ (void)context;
+ free(data);
+}
+
+int main( int argc, const char* argv[] )
+{
+#ifdef YCF_YIELD_CODE_GENERATED
+ void* wb = NULL;
+#endif
+ int ret = 0;
+ int nr_of_yields = 0;
+ long nr_of_reductions;
+#ifdef YCF_YIELD_CODE_GENERATED
+ do{
+ nr_of_reductions = 101;
+ ret = fun_ycf_gen_yielding(&nr_of_reductions,&wb,NULL,allocator,freer,NULL,0,NULL,1);
+ if(wb != NULL){
+ printf("TRAPPED %ld\n", nr_of_reductions);
+ nr_of_yields++;
+ }
+ }while(wb != NULL);
+ if(wb != NULL){
+ free(wb);
+ }
+#else
+ ret = fun(1);
+#endif
+ printf("Number of yields %d\n", nr_of_yields);
+ printf("RETURNED %d\n", ret);
+ if(ret != 201){
+ return 1;
+ }else{
+ return 0;
+ }
+}
+
diff --git a/erts/lib_src/yielding_c_fun/test/examples/nested_call_consume_reds.c.out b/erts/lib_src/yielding_c_fun/test/examples/nested_call_consume_reds.c.out
new file mode 100644
index 0000000000..32b93f33f6
--- /dev/null
+++ b/erts/lib_src/yielding_c_fun/test/examples/nested_call_consume_reds.c.out
@@ -0,0 +1,11 @@
+TRAPPED -9
+TRAPPED -9
+TRAPPED -9
+TRAPPED -9
+TRAPPED -9
+TRAPPED -9
+TRAPPED -9
+TRAPPED -9
+TRAPPED -9
+Number of yields 9
+RETURNED 201
diff --git a/erts/lib_src/yielding_c_fun/test/examples/nested_loop_yield.c b/erts/lib_src/yielding_c_fun/test/examples/nested_loop_yield.c
new file mode 100644
index 0000000000..b87685e23d
--- /dev/null
+++ b/erts/lib_src/yielding_c_fun/test/examples/nested_loop_yield.c
@@ -0,0 +1,104 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB and Kjell Winblad 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%
+ */
+
+/*
+ * Description:
+ *
+ * Author: Kjell Winblad
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#define YCF_YIELD()
+
+
+int fun(){
+ int x;
+ int y;
+ int z;
+ int outer = 0;
+ int inner = 0;
+ for(x = 0; x < 2; x++){
+ for(y = 0; y < 2; y++){ /* 2 times */
+ for(z = 0; z < 2; z++){ /* 4 times */
+ YCF_YIELD(); /* 8 times */
+ outer++;
+ printf("outer %d: x=%d y=%d z=%d\n", outer, x, y, z);
+ {
+ int x;
+ int y;
+ int z;
+ for(x = 0; x < 2; x++){ /* 8 times */
+ for(y = 0; y < 2; y++){ /* 16 times */
+ for(z = 0; z < 2; z++){ /* 32 times */
+ YCF_YIELD(); /* 64 times */
+ inner++;
+ printf("inner %d: x=%d y=%d z=%d\n", inner, x, y, z);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ return inner + outer;
+}
+
+void* allocator(size_t size, void* context){
+ (void)context;
+ return malloc(size);
+}
+
+void freer(void* data, void* context){
+ (void)context;
+ free(data);
+}
+
+int main( int argc, const char* argv[] )
+{
+#ifdef YCF_YIELD_CODE_GENERATED
+ void* wb = NULL;
+#endif
+ int ret = 0;
+ int nr_of_traps = 0;
+ long nr_of_reductions = 1;
+#ifdef YCF_YIELD_CODE_GENERATED
+ do{
+ ret = fun_ycf_gen_yielding(&nr_of_reductions,&wb,NULL,allocator,freer,NULL,0,NULL);
+ if(wb != NULL){
+ nr_of_traps++;
+ }
+ }while(wb != NULL);
+ if(wb != NULL){
+ free(wb);
+ }
+#else
+ ret = fun();
+#endif
+ printf("NR OF TRAPS %d\n", nr_of_traps);
+ printf("RETURNED %d\n", ret);
+ if(ret != 72 || nr_of_traps != ret){
+ return 1;
+ }else{
+ return 0;
+ }
+}
diff --git a/erts/lib_src/yielding_c_fun/test/examples/nested_loop_yield.c.out b/erts/lib_src/yielding_c_fun/test/examples/nested_loop_yield.c.out
new file mode 100644
index 0000000000..b48664e112
--- /dev/null
+++ b/erts/lib_src/yielding_c_fun/test/examples/nested_loop_yield.c.out
@@ -0,0 +1,74 @@
+outer 1: x=0 y=0 z=0
+inner 1: x=0 y=0 z=0
+inner 2: x=0 y=0 z=1
+inner 3: x=0 y=1 z=0
+inner 4: x=0 y=1 z=1
+inner 5: x=1 y=0 z=0
+inner 6: x=1 y=0 z=1
+inner 7: x=1 y=1 z=0
+inner 8: x=1 y=1 z=1
+outer 2: x=0 y=0 z=1
+inner 9: x=0 y=0 z=0
+inner 10: x=0 y=0 z=1
+inner 11: x=0 y=1 z=0
+inner 12: x=0 y=1 z=1
+inner 13: x=1 y=0 z=0
+inner 14: x=1 y=0 z=1
+inner 15: x=1 y=1 z=0
+inner 16: x=1 y=1 z=1
+outer 3: x=0 y=1 z=0
+inner 17: x=0 y=0 z=0
+inner 18: x=0 y=0 z=1
+inner 19: x=0 y=1 z=0
+inner 20: x=0 y=1 z=1
+inner 21: x=1 y=0 z=0
+inner 22: x=1 y=0 z=1
+inner 23: x=1 y=1 z=0
+inner 24: x=1 y=1 z=1
+outer 4: x=0 y=1 z=1
+inner 25: x=0 y=0 z=0
+inner 26: x=0 y=0 z=1
+inner 27: x=0 y=1 z=0
+inner 28: x=0 y=1 z=1
+inner 29: x=1 y=0 z=0
+inner 30: x=1 y=0 z=1
+inner 31: x=1 y=1 z=0
+inner 32: x=1 y=1 z=1
+outer 5: x=1 y=0 z=0
+inner 33: x=0 y=0 z=0
+inner 34: x=0 y=0 z=1
+inner 35: x=0 y=1 z=0
+inner 36: x=0 y=1 z=1
+inner 37: x=1 y=0 z=0
+inner 38: x=1 y=0 z=1
+inner 39: x=1 y=1 z=0
+inner 40: x=1 y=1 z=1
+outer 6: x=1 y=0 z=1
+inner 41: x=0 y=0 z=0
+inner 42: x=0 y=0 z=1
+inner 43: x=0 y=1 z=0
+inner 44: x=0 y=1 z=1
+inner 45: x=1 y=0 z=0
+inner 46: x=1 y=0 z=1
+inner 47: x=1 y=1 z=0
+inner 48: x=1 y=1 z=1
+outer 7: x=1 y=1 z=0
+inner 49: x=0 y=0 z=0
+inner 50: x=0 y=0 z=1
+inner 51: x=0 y=1 z=0
+inner 52: x=0 y=1 z=1
+inner 53: x=1 y=0 z=0
+inner 54: x=1 y=0 z=1
+inner 55: x=1 y=1 z=0
+inner 56: x=1 y=1 z=1
+outer 8: x=1 y=1 z=1
+inner 57: x=0 y=0 z=0
+inner 58: x=0 y=0 z=1
+inner 59: x=0 y=1 z=0
+inner 60: x=0 y=1 z=1
+inner 61: x=1 y=0 z=0
+inner 62: x=1 y=0 z=1
+inner 63: x=1 y=1 z=0
+inner 64: x=1 y=1 z=1
+NR OF TRAPS 72
+RETURNED 72
diff --git a/erts/lib_src/yielding_c_fun/test/examples/sha256_erlang_nif/.gitignore b/erts/lib_src/yielding_c_fun/test/examples/sha256_erlang_nif/.gitignore
new file mode 100644
index 0000000000..b5e8b97bac
--- /dev/null
+++ b/erts/lib_src/yielding_c_fun/test/examples/sha256_erlang_nif/.gitignore
@@ -0,0 +1,25 @@
+.rebar3
+_*
+.eunit
+*.o
+*.beam
+*.plt
+*.swp
+*.swo
+.erlang.cookie
+ebin
+log
+erl_crash.dump
+.rebar
+logs
+_build
+.idea
+*.iml
+rebar3.crashdump
+*~
+c_src/compile_commands.json
+c_src/compile_commands_old.json
+c_src/gen_yielding_sha_256.c
+c_src/gen_yielding_sha_256.h
+priv/
+rebar.lock
diff --git a/erts/lib_src/yielding_c_fun/test/examples/sha256_erlang_nif/LICENSE b/erts/lib_src/yielding_c_fun/test/examples/sha256_erlang_nif/LICENSE
new file mode 100644
index 0000000000..66232832b9
--- /dev/null
+++ b/erts/lib_src/yielding_c_fun/test/examples/sha256_erlang_nif/LICENSE
@@ -0,0 +1,191 @@
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ Copyright 2019, Kjell Winblad <kjellwinblad@gmail.com>.
+
+ 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.
+
diff --git a/erts/lib_src/yielding_c_fun/test/examples/sha256_erlang_nif/Makefile b/erts/lib_src/yielding_c_fun/test/examples/sha256_erlang_nif/Makefile
new file mode 100644
index 0000000000..8bae3dc26a
--- /dev/null
+++ b/erts/lib_src/yielding_c_fun/test/examples/sha256_erlang_nif/Makefile
@@ -0,0 +1,34 @@
+
+ifeq ($(wildcard rebar3),rebar3)
+REBAR3 = $(CURDIR)/rebar3
+endif
+
+REBAR3 ?= $(shell test -e `which rebar3` 2>/dev/null && which rebar3 || echo "./rebar3")
+
+
+.PHONY: deps test build
+
+all: build test docs
+
+build: $(REBAR3)
+ @$(REBAR3) compile
+
+deps:
+ @$(REBAR3) get-deps
+
+clean:
+ @$(REBAR3) clean
+
+distclean: clean
+ @$(REBAR3) delete-deps
+
+docs:
+ @$(REBAR3) edoc
+
+
+test:
+ @$(REBAR3) ct
+
+
+release: test
+ @$(REBAR3) release
diff --git a/erts/lib_src/yielding_c_fun/test/examples/sha256_erlang_nif/README.md b/erts/lib_src/yielding_c_fun/test/examples/sha256_erlang_nif/README.md
new file mode 100644
index 0000000000..ba579880b3
--- /dev/null
+++ b/erts/lib_src/yielding_c_fun/test/examples/sha256_erlang_nif/README.md
@@ -0,0 +1,23 @@
+sha256_nif
+=====
+
+An OTP library that illustrates how one can implement a yielding
+Erlang NIF API with the help of Yielding C Fun.
+
+Build
+-----
+
+ $ rebar3 compile
+
+or
+
+ $ make
+
+Test
+----
+
+ $ rebar3 ct
+
+or
+
+ $ make test \ No newline at end of file
diff --git a/erts/lib_src/yielding_c_fun/test/examples/sha256_erlang_nif/c_src/Makefile b/erts/lib_src/yielding_c_fun/test/examples/sha256_erlang_nif/c_src/Makefile
new file mode 100644
index 0000000000..dde621c591
--- /dev/null
+++ b/erts/lib_src/yielding_c_fun/test/examples/sha256_erlang_nif/c_src/Makefile
@@ -0,0 +1,83 @@
+# Based on c_src.mk from erlang.mk by Loic Hoguin <essen@ninenines.eu>
+
+CURDIR := $(shell pwd)
+BASEDIR := $(abspath $(CURDIR)/..)
+
+PROJECT ?= $(notdir $(BASEDIR))
+PROJECT := $(strip $(PROJECT))
+
+ERTS_INCLUDE_DIR ?= $(shell erl -noshell -s init stop -eval "io:format(\"~ts/erts-~ts/include/\", [code:root_dir(), erlang:system_info(version)]).")
+ERL_INTERFACE_INCLUDE_DIR ?= $(shell erl -noshell -s init stop -eval "io:format(\"~ts\", [code:lib_dir(erl_interface, include)]).")
+ERL_INTERFACE_LIB_DIR ?= $(shell erl -noshell -s init stop -eval "io:format(\"~ts\", [code:lib_dir(erl_interface, lib)]).")
+
+C_SRC_DIR = $(CURDIR)
+C_SRC_OUTPUT ?= $(CURDIR)/../priv/$(PROJECT).so
+
+# System type and C compiler/flags.
+
+UNAME_SYS := $(shell uname -s)
+ifeq ($(UNAME_SYS), Darwin)
+ CC ?= cc
+ CFLAGS ?= -O3 -std=c99 -arch x86_64 -finline-functions -Wall
+ CXXFLAGS ?= -O3 -arch x86_64 -finline-functions -Wall
+ LDFLAGS ?= -arch x86_64 -flat_namespace -undefined suppress
+else ifeq ($(UNAME_SYS), FreeBSD)
+ CC ?= cc
+ CFLAGS ?= -O3 -std=c99 -finline-functions -Wall -Wmissing-prototypes
+ CXXFLAGS ?= -O3 -finline-functions -Wall
+else ifeq ($(UNAME_SYS), Linux)
+ CC ?= gcc
+ CFLAGS ?= -O3 -std=c99 -g -Wall -Wno-missing-prototypes -Wno-unused-function
+#-O3 -std=c99 -finline-functions -Wall -Wmissing-prototypes
+ CXXFLAGS ?= -O3 -finline-functions -Wall
+endif
+
+EXTRACFLAGSYCFCODE=-Wno-unused-function
+
+CFLAGS += -fPIC -I $(ERTS_INCLUDE_DIR) -I $(ERL_INTERFACE_INCLUDE_DIR)
+CXXFLAGS += -fPIC -I $(ERTS_INCLUDE_DIR) -I $(ERL_INTERFACE_INCLUDE_DIR)
+
+LDLIBS += -L $(ERL_INTERFACE_LIB_DIR) -lerl_interface -lei
+LDFLAGS += -shared
+
+# Verbosity.
+
+c_verbose_0 = @echo " C " $(?F);
+c_verbose = $(c_verbose_$(V))
+
+cpp_verbose_0 = @echo " CPP " $(?F);
+cpp_verbose = $(cpp_verbose_$(V))
+
+link_verbose_0 = @echo " LD " $(@F);
+link_verbose = $(link_verbose_$(V))
+
+SOURCES := $(shell find $(C_SRC_DIR) -type f \( -name "*.c" -o -name "*.C" -o -name "*.cc" -o -name "*.cpp" \) | grep -v "./sha-2/sha-256_orginal.c" | grep -v "./sha-2/sha-256.c" | grep -v "./gen_yielding_sha_256.c" | grep -v "./sha-2/test.c")
+OBJECTS = gen_yielding_sha_256.o $(addsuffix .o, $(basename $(SOURCES)))
+
+COMPILE_C = $(c_verbose) $(CC) $(CFLAGS) $(CPPFLAGS) -c
+COMPILE_CPP = $(cpp_verbose) $(CXX) $(CXXFLAGS) $(CPPFLAGS) -c
+
+$(C_SRC_OUTPUT): $(OBJECTS)
+ @mkdir -p $(BASEDIR)/priv/
+ $(link_verbose) $(CC) $(OBJECTS) $(LDFLAGS) $(LDLIBS) -o $(C_SRC_OUTPUT)
+
+gen_yielding_sha_256.c: sha-2/sha-256.c
+ ../../../../bin/yielding_c_fun -use_gc -yield -debug -f calc_sha_256 -f calc_chunk -header_file_name gen_yielding_sha_256.h "sha-2/sha-256.c" > gen_yielding_sha_256.c
+
+gen_yielding_sha_256.o: gen_yielding_sha_256.c
+ $(CC) $(CFLAGS) $(EXTRACFLAGSYCFCODE) $(CPPFLAGS) -c gen_yielding_sha_256.c
+
+%.o: %.c
+ $(COMPILE_C) $(OUTPUT_OPTION) $<
+
+%.o: %.cc
+ $(COMPILE_CPP) $(OUTPUT_OPTION) $<
+
+%.o: %.C
+ $(COMPILE_CPP) $(OUTPUT_OPTION) $<
+
+%.o: %.cpp
+ $(COMPILE_CPP) $(OUTPUT_OPTION) $<
+
+clean:
+ @rm -f gen_yielding_sha_256.c gen_yielding_sha_256.h $(C_SRC_OUTPUT) $(OBJECTS)
diff --git a/erts/lib_src/yielding_c_fun/test/examples/sha256_erlang_nif/c_src/sha-2/.gitignore b/erts/lib_src/yielding_c_fun/test/examples/sha256_erlang_nif/c_src/sha-2/.gitignore
new file mode 100644
index 0000000000..6587b2f0fc
--- /dev/null
+++ b/erts/lib_src/yielding_c_fun/test/examples/sha256_erlang_nif/c_src/sha-2/.gitignore
@@ -0,0 +1,3 @@
+*~
+*.o
+test
diff --git a/erts/lib_src/yielding_c_fun/test/examples/sha256_erlang_nif/c_src/sha-2/.travis.yml b/erts/lib_src/yielding_c_fun/test/examples/sha256_erlang_nif/c_src/sha-2/.travis.yml
new file mode 100644
index 0000000000..ffec9e605d
--- /dev/null
+++ b/erts/lib_src/yielding_c_fun/test/examples/sha256_erlang_nif/c_src/sha-2/.travis.yml
@@ -0,0 +1,2 @@
+language: c
+script: make
diff --git a/erts/lib_src/yielding_c_fun/test/examples/sha256_erlang_nif/c_src/sha-2/GIT_VERSION b/erts/lib_src/yielding_c_fun/test/examples/sha256_erlang_nif/c_src/sha-2/GIT_VERSION
new file mode 100644
index 0000000000..28e48c7b6f
--- /dev/null
+++ b/erts/lib_src/yielding_c_fun/test/examples/sha256_erlang_nif/c_src/sha-2/GIT_VERSION
@@ -0,0 +1,265 @@
+origin https://github.com/amosnier/sha-2.git (fetch)
+origin https://github.com/amosnier/sha-2.git (push)
+commit ff76937294694ec347819d81a1f580187fb34246
+Author: Alain Mosnier <alain@wanamoon.net>
+Date: Sat Jul 28 23:24:58 2018 +0200
+
+ Update README.md
+
+commit ad933ac6959f8507c349b2c60f02b9336f5ade10
+Author: Alain Mosnier <alain@wanamoon.net>
+Date: Sat Jul 28 23:24:18 2018 +0200
+
+ Update README.md
+
+commit 92e7db3c188a2c7a971564b2fdac2d38c7b329e3
+Author: Alain Mosnier <alain@wanamoon.net>
+Date: Sat Jul 28 23:22:45 2018 +0200
+
+ Update README.md
+
+commit 6d500e221aa29f9a3701833f1f31ccabba9c58f7
+Author: Alain Mosnier <alain@wanamoon.net>
+Date: Sat Jul 28 23:22:00 2018 +0200
+
+ Update README.md
+
+ Added section about reference implementation.
+
+commit 924fba5c17abac8e6b90eef99b509f2cf759d223
+Author: Alain Mosnier <alain@wanamoon.net>
+Date: Sat Jul 28 22:02:09 2018 +0200
+
+ Replaced some for loops by memset
+
+commit 9583456b329abe8bfbaaedd0aca76fc3eb31e0b8
+Author: Alain Mosnier <alain@wanamoon.net>
+Date: Sat Jul 28 19:16:41 2018 +0200
+
+ Implemented workaround for ld issue with large BSS
+
+ The large arrays are now allocated on heap instead of in BSS. This
+ actually feels like a pretty stupid linker limitation, and the
+ workaround leads to code that is less readable. But it only impacts
+ the test program.
+
+ The issue was reported with GCC 7.3.0 and compiling for 64-bit arch on
+ MSYS2's MinGW compiler. GCC 7.3.0 for 64-bit on Linux (my own machine
+ and Travis) seems perfectly OK with a large BSS segment (around 3 GB
+ is what the test program uses for test data).
+
+ Reported issue:
+
+ $ make
+ cc -Wall -Wextra -Wpedantic -c -o test.o test.c
+ cc -Wall -Wextra -Wpedantic -c -o sha-256.o sha-256.c
+ cc test.o sha-256.o -o test
+ C:/msys64/mingw64/bin/../lib/gcc/x86_64-w64-mingw32/7.3.0/../../../../x86_64-w64-mingw32/bin/ld.exe: C:/msys64/mingw64/bin/../lib/gcc/x86_64-w64-mingw32/7.3.0/../../../../x86_64-w64-mingw32/lib/../lib/crt2.o: in function `__tmainCRTStartup':
+ C:/repo/mingw-w64-crt-git/src/mingw-w64/mingw-w64-crt/crt/crtexe.c:251:(.text+0x1cd): relocation truncated to fit: R_X86_64_PC32 against symbol `__imp_Sleep' defined in .idata$5 section in C:/msys64/mingw64/bin/../lib/gcc/x86_64-w64-mingw32/7.3.0/../../../../x86_64-w64-mingw32/lib/../lib/libkernel32.a(dkhchs01360.o)
+ C:/msys64/mingw64/bin/../lib/gcc/x86_64-w64-mingw32/7.3.0/../../../../x86_64-w64-mingw32/bin/ld.exe: C:/repo/mingw-w64-crt-git/src/mingw-w64/mingw-w64-crt/crt/crtexe.c:278:(.text+0x255): relocation truncated to fit: R_X86_64_PC32 against symbol `__imp_SetUnhandledExceptionFilter' defined in .idata$5 section in C:/msys64/mingw64/bin/../lib/gcc/x86_64-w64-mingw32/7.3.0/../../../../x86_64-w64-mingw32/lib/../lib/libkernel32.a(dkhchs01346.o)
+ C:/msys64/mingw64/bin/../lib/gcc/x86_64-w64-mingw32/7.3.0/../../../../x86_64-w64-mingw32/bin/ld.exe: C:/repo/mingw-w64-crt-git/src/mingw-w64/mingw-w64-crt/crt/crtexe.c:286:(.text+0x283): relocation truncated to fit: R_X86_64_PC32 against symbol `__mingw_winmain_hInstance' defined in COMMON section in C:/msys64/mingw64/bin/../lib/gcc/x86_64-w64-mingw32/7.3.0/../../../../x86_64-w64-mingw32/lib/../lib/crt2.o
+ C:/msys64/mingw64/bin/../lib/gcc/x86_64-w64-mingw32/7.3.0/../../../../x86_64-w64-mingw32/bin/ld.exe: C:/repo/mingw-w64-crt-git/src/mingw-w64/mingw-w64-crt/crt/crtexe.c:312:(.text+0x2f3): relocation truncated to fit: R_X86_64_PC32 against symbol `__mingw_winmain_lpCmdLine' defined in COMMON section in C:/msys64/mingw64/bin/../lib/gcc/x86_64-w64-mingw32/7.3.0/../../../../x86_64-w64-mingw32/lib/../lib/crt2.o
+ C:/msys64/mingw64/bin/../lib/gcc/x86_64-w64-mingw32/7.3.0/../../../../x86_64-w64-mingw32/bin/ld.exe: C:/repo/mingw-w64-crt-git/src/mingw-w64/mingw-w64-crt/crt/crtexe.c:238:(.text+0x475): relocation truncated to fit: R_X86_64_PC32 against symbol `__imp_GetStartupInfoA' defined in .idata$5 section in C:/msys64/mingw64/bin/../lib/gcc/x86_64-w64-mingw32/7.3.0/../../../../x86_64-w64-mingw32/lib/../lib/libkernel32.a(dkhchs00721.o)
+ C:/msys64/mingw64/bin/../lib/gcc/x86_64-w64-mingw32/7.3.0/../../../../x86_64-w64-mingw32/bin/ld.exe: C:/msys64/mingw64/bin/../lib/gcc/x86_64-w64-mingw32/7.3.0/../../../../x86_64-w64-mingw32/lib/../lib/libmingw32.a(lib64_libmingw32_a-gccmain.o): in function `__main':
+ C:/repo/mingw-w64-crt-git/src/mingw-w64/mingw-w64-crt/crt/gccmain.c:74:(.text+0xb2): relocation truncated to fit: R_X86_64_PC32 against `.bss'
+ C:/msys64/mingw64/bin/../lib/gcc/x86_64-w64-mingw32/7.3.0/../../../../x86_64-w64-mingw32/bin/ld.exe: C:/repo/mingw-w64-crt-git/src/mingw-w64/mingw-w64-crt/crt/gccmain.c:76:(.text+0xc2): relocation truncated to fit: R_X86_64_PC32 against `.bss'
+ C:/msys64/mingw64/bin/../lib/gcc/x86_64-w64-mingw32/7.3.0/../../../../x86_64-w64-mingw32/bin/ld.exe: C:/msys64/mingw64/bin/../lib/gcc/x86_64-w64-mingw32/7.3.0/../../../../x86_64-w64-mingw32/lib/../lib/libmingw32.a(lib64_libmingw32_a-charmax.o): in function `my_lconv_init':
+ C:/repo/mingw-w64-crt-git/src/mingw-w64/mingw-w64-crt/crt/charmax.c:19:(.text+0x3): relocation truncated to fit: R_X86_64_PC32 against symbol `__imp___lconv_init' defined in .idata$5 section in C:/msys64/mingw64/bin/../lib/gcc/x86_64-w64-mingw32/7.3.0/../../../../x86_64-w64-mingw32/lib/../lib/libmsvcrt.a(dkxcbs00090.o)
+ C:/msys64/mingw64/bin/../lib/gcc/x86_64-w64-mingw32/7.3.0/../../../../x86_64-w64-mingw32/bin/ld.exe: C:/msys64/mingw64/bin/../lib/gcc/x86_64-w64-mingw32/7.3.0/../../../../x86_64-w64-mingw32/lib/../lib/libmingw32.a(lib64_libmingw32_a-gs_support.o): in function `__security_init_cookie':
+ C:/repo/mingw-w64-crt-git/src/mingw-w64/mingw-w64-crt/crt/gs_support.c:62:(.text+0x47): relocation truncated to fit: R_X86_64_PC32 against symbol `__imp_GetSystemTimeAsFileTime' defined in .idata$5 section in C:/msys64/mingw64/bin/../lib/gcc/x86_64-w64-mingw32/7.3.0/../../../../x86_64-w64-mingw32/lib/../lib/libkernel32.a(dkhchs00746.o)
+ C:/msys64/mingw64/bin/../lib/gcc/x86_64-w64-mingw32/7.3.0/../../../../x86_64-w64-mingw32/bin/ld.exe: C:/repo/mingw-w64-crt-git/src/mingw-w64/mingw-w64-crt/crt/gs_support.c:70:(.text+0x52): relocation truncated to fit: R_X86_64_PC32 against symbol `__imp_GetCurrentProcessId' defined in .idata$5 section in C:/msys64/mingw64/bin/../lib/gcc/x86_64-w64-mingw32/7.3.0/../../../../x86_64-w64-mingw32/lib/../lib/libkernel32.a(dkhchs00536.o)
+ C:/msys64/mingw64/bin/../lib/gcc/x86_64-w64-mingw32/7.3.0/../../../../x86_64-w64-mingw32/bin/ld.exe: C:/repo/mingw-w64-crt-git/src/mingw-w64/mingw-w64-crt/crt/gs_support.c:71:(.text+0x5b): additional relocation overflows omitted from the output
+ collect2.exe: error: ld returned 1 exit status
+ make: *** [<builtin>: test] Error 1```
+
+commit 23b53726f3c5c283112110468ff1c9b4981260e9
+Author: Alain Mosnier <alain@wanamoon.net>
+Date: Sat Jul 28 16:53:38 2018 +0200
+
+ Added explicit cast to correct printf() related bug
+
+commit f03474231fc0e86c66cbe2a74a9d9f6589dd96f9
+Author: Alain Mosnier <alain@wanamoon.net>
+Date: Fri Dec 15 23:41:17 2017 +0100
+
+ Activated testing of long messages
+
+commit a222d72880c8b9b2b22155d34adfc72e49e511c5
+Author: Alain Mosnier <alain@wanamoon.net>
+Date: Fri Dec 15 23:11:41 2017 +0100
+
+ Minor README change
+
+commit 98a0bf165c558dde77211ff09cc6b0e1c30badc7
+Author: Alain Mosnier <alain@wanamoon.net>
+Date: Fri Dec 15 23:08:48 2017 +0100
+
+ Minor README change
+
+commit 688b14926c42b61dbfb61f3046594afb058dad31
+Author: Alain Mosnier <alain@wanamoon.net>
+Date: Fri Dec 15 23:07:54 2017 +0100
+
+ Added Travis comment
+
+commit d6b49c65bdaa54120d20401e15368cb462f8a122
+Author: Alain Mosnier <alain@wanamoon.net>
+Date: Fri Dec 15 23:00:29 2017 +0100
+
+ Fixed minor warnings
+
+commit a6c25e2cdf96eeb522199c67e895f0791e297fc9
+Author: Alain Mosnier <alain@wanamoon.net>
+Date: Fri Dec 15 22:53:19 2017 +0100
+
+ Included test in make
+
+commit 538cfe97fa7ad217875196b4e5ce8ddfc1d88f7a
+Author: Alain Mosnier <alain@wanamoon.net>
+Date: Fri Dec 15 22:34:57 2017 +0100
+
+ Travis icon again
+
+commit 903e46163b6d6258b3d5e5b5de60f134749e2f10
+Author: Alain Mosnier <alain@wanamoon.net>
+Date: Fri Dec 15 22:33:50 2017 +0100
+
+ Work on Travis icon
+
+commit d87b78db4bfa08ddceecb3837943b7886d2f4f88
+Author: Alain Mosnier <alain@wanamoon.net>
+Date: Fri Dec 15 22:31:02 2017 +0100
+
+ Added Travis link
+
+commit 19d1e8a568af53c9788ed6fc2ecc9fcb34b17037
+Author: Alain Mosnier <alain@wanamoon.net>
+Date: Fri Dec 15 22:24:03 2017 +0100
+
+ Started adding Travis
+
+commit 8f83e8dedf169858efd366fe2237ec31e8f3c3ec
+Author: Alain Mosnier <alain@wanamoon.net>
+Date: Fri Dec 15 21:54:31 2017 +0100
+
+ Minor README change
+
+commit 369c47af630d6f3dc6cf817e402ef070a588d5a9
+Author: Alain Mosnier <alain@wanamoon.net>
+Date: Fri Dec 15 21:52:36 2017 +0100
+
+ Minor README change
+
+commit b177afbe06965bec656f0cf437f449f747f6575e
+Author: Alain Mosnier <alain@wanamoon.net>
+Date: Fri Dec 15 21:45:25 2017 +0100
+
+ Improved formatting
+
+commit 8fd2b04fed6b5bcaad04e66cb2a3158b61b5ce1e
+Author: Alain Mosnier <alain@wanamoon.net>
+Date: Fri Dec 15 21:44:04 2017 +0100
+
+ Improved formatting
+
+commit 96988951cc63d2305ce7612ebad5456bfca444c9
+Author: Alain Mosnier <alain@wanamoon.net>
+Date: Fri Dec 15 21:40:56 2017 +0100
+
+ Improved formatting
+
+commit 37500f461fbc5e0553ec4ea2f5ec449126ac63c0
+Author: Alain Mosnier <alain@wanamoon.net>
+Date: Fri Dec 15 21:37:39 2017 +0100
+
+ Formatting improvement
+
+commit 6f35ae7007f0f9b54e5798d6a63ffbd1f9f9d59c
+Author: Alain Mosnier <alain@wanamoon.net>
+Date: Fri Dec 15 21:34:20 2017 +0100
+
+ Added extensive testing
+
+commit cba84edcb52079236c0c32c591a74ec89155fc62
+Author: Alain Mosnier <alain@wanamoon.net>
+Date: Fri Dec 15 19:37:47 2017 +0100
+
+ Made changes according to review comments from StackExchange CODE REVIEW
+
+commit d7214be72713f020f2dffbac01a13442921cabce
+Author: Alain Mosnier <alain@wanamoon.net>
+Date: Thu Dec 14 22:45:59 2017 +0100
+
+ Added comment about code review
+
+commit da12ae4e94725e87052da5c0191103de0d817ed5
+Author: Alain Mosnier <alain@wanamoon.net>
+Date: Thu Dec 14 20:11:09 2017 +0100
+
+ Tests at compile time instead of runtime
+
+commit b21a7b552969cbf8a2c702f617e15750fe1b2b2b
+Author: Alain Mosnier <alain@wanamoon.net>
+Date: Wed Dec 13 12:03:01 2017 +0100
+
+ Added comment
+
+commit 85e93b4a79b7ed9f20077021b489c3a1cbd6a251
+Author: Alain Mosnier <alain@wanamoon.net>
+Date: Wed Dec 13 12:01:19 2017 +0100
+
+ Typo
+
+commit ccc3d23b29eeb4c1ace83ae93f415d7a3d57ed45
+Author: Alain Mosnier <alain@wanamoon.net>
+Date: Wed Dec 13 12:00:03 2017 +0100
+
+ Added comment.
+
+commit 047f309134372f826027ac383667c18ce7a2062f
+Author: Alain Mosnier <alain@wanamoon.net>
+Date: Wed Dec 13 11:29:58 2017 +0100
+
+ Added comment about testing
+
+commit 5833df68d9de2f1cb1cc3db57608eba25559a9bf
+Author: Alain Mosnier <alain@wanamoon.net>
+Date: Wed Dec 13 11:27:39 2017 +0100
+
+ Removed temporary code
+
+commit f3f5e1b4532a40e37fc9b880f348a525c4a4165a
+Author: Alain Mosnier <alain@wanamoon.net>
+Date: Wed Dec 13 11:23:44 2017 +0100
+
+ Added some notes
+
+commit 097f4b536db64d29fbc5f54d052ba795db68f00f
+Author: Alain Mosnier <alain@wanamoon.net>
+Date: Wed Dec 13 11:18:17 2017 +0100
+
+ Typo
+
+commit 73684be1627a05d4dbd585d2cc926e6b91275dc4
+Author: Alain Mosnier <alain@wanamoon.net>
+Date: Wed Dec 13 11:14:32 2017 +0100
+
+ Corrected typo
+
+commit 489f5e66b63ed90ed3de197237e608e101253efb
+Author: Alain Mosnier <alain@wanamoon.net>
+Date: Wed Dec 13 11:12:47 2017 +0100
+
+ Completed the implementation, including testing, and fixed bugs
+
+commit d676207600a94c5d94ef6f6188cc1c907f06b42a
+Author: Alain Mosnier <alain@wanamoon.net>
+Date: Wed Dec 13 00:52:28 2017 +0100
+
+ Started a SHA-256 implementation. Buggy so far.
+
+commit 3433d4ef5ccb136f0b889610cac3f752776ea525
+Author: Alain Mosnier <alain@wanamoon.net>
+Date: Tue Dec 12 20:48:09 2017 +0100
+
+ Initial commit
diff --git a/erts/lib_src/yielding_c_fun/test/examples/sha256_erlang_nif/c_src/sha-2/LICENSE b/erts/lib_src/yielding_c_fun/test/examples/sha256_erlang_nif/c_src/sha-2/LICENSE
new file mode 100644
index 0000000000..cf1ab25da0
--- /dev/null
+++ b/erts/lib_src/yielding_c_fun/test/examples/sha256_erlang_nif/c_src/sha-2/LICENSE
@@ -0,0 +1,24 @@
+This is free and unencumbered software released into the public domain.
+
+Anyone is free to copy, modify, publish, use, compile, sell, or
+distribute this software, either in source code form or as a compiled
+binary, for any purpose, commercial or non-commercial, and by any
+means.
+
+In jurisdictions that recognize copyright laws, the author or authors
+of this software dedicate any and all copyright interest in the
+software to the public domain. We make this dedication for the benefit
+of the public at large and to the detriment of our heirs and
+successors. We intend this dedication to be an overt act of
+relinquishment in perpetuity of all present and future rights to this
+software under copyright law.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
+
+For more information, please refer to <http://unlicense.org>
diff --git a/erts/lib_src/yielding_c_fun/test/examples/sha256_erlang_nif/c_src/sha-2/Makefile b/erts/lib_src/yielding_c_fun/test/examples/sha256_erlang_nif/c_src/sha-2/Makefile
new file mode 100644
index 0000000000..53bcbab93e
--- /dev/null
+++ b/erts/lib_src/yielding_c_fun/test/examples/sha256_erlang_nif/c_src/sha-2/Makefile
@@ -0,0 +1,11 @@
+CFLAGS = -Wall -Wextra -Wpedantic
+
+.PHONY: all
+all: test
+ ./test
+
+test: test.o sha-256.o
+
+.PHONY: clean
+clean:
+ rm test *.o
diff --git a/erts/lib_src/yielding_c_fun/test/examples/sha256_erlang_nif/c_src/sha-2/README.md b/erts/lib_src/yielding_c_fun/test/examples/sha256_erlang_nif/c_src/sha-2/README.md
new file mode 100644
index 0000000000..167a69d08c
--- /dev/null
+++ b/erts/lib_src/yielding_c_fun/test/examples/sha256_erlang_nif/c_src/sha-2/README.md
@@ -0,0 +1,97 @@
+# sha-2 [![Build Status](https://travis-ci.org/amosnier/sha-2.svg?branch=master)](https://travis-ci.org/amosnier/sha-2)
+
+## Contents
+
+SHA-2 algorithm implementations.
+
+At the moment, only SHA-256 is implemented.
+
+## Design criteria
+
+- Easy to test, include in any project, compile and link.
+
+- ANSI C with as little specific C99 as possible (e.g. extended
+ integer types are used, but not bool).
+
+- Portable. Makes no assumptions on the target system's endianess or
+ word size.
+
+- The SHA-256 implementation is a straightforward implementation of
+ the algorithm specified on
+ [Wikipedia](https://en.wikipedia.org/wiki/SHA-2).
+
+## Notes
+
+The Makefile is as minimal as possible. No effort was put into making
+it general. Its purpose is mainly to ease testing for the developer's
+host machine. The actual implementation is however extremely easy to
+include in any project, may it use GNU make or any other build tool.
+
+## Code review
+
+This code has been reviewed at [Stack Exchange CODE
+REVIEW](https://codereview.stackexchange.com/questions/182812/self-contained-sha-256-implementation-in-c),
+and the implementation has been improved accordingly.
+
+## Testing
+
+Testing is continuously performed on Travis CI (see above).
+
+Apart from that, the implementation has been successfully tested on an x86-64 machine
+under Linux as well as on a 16-bit DSP. On the x86-64 machine, all the
+available NIST test vectors where successfully tested ([SHA-256
+examples](https://csrc.nist.gov/CSRC/media/Projects/Cryptographic-Standards-and-Guidelines/documents/examples/SHA256.pdf)
+and [SHA-2 Additional
+examples](https://csrc.nist.gov/CSRC/media/Projects/Cryptographic-Standards-and-Guidelines/documents/examples/SHA2_Additional.pdf),
+plus a few others).
+
+In particular:
+
+```
+Input Message: "abc"
+Message Digest is BA7816BF 8F01CFEA 414140DE 5DAE2223 B00361A3 96177A9C B410FF61 F20015AD
+```
+
+```
+Input Message: "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"
+Message Digest is 248D6A61 D20638B8 E5C02693 0C3E6039 A33CE459 64FF2167 F6ECEDD4 19DB06C1
+```
+
+```
+SHA-256 Test Data
+#1) 1 byte 0xbd
+68325720 aabd7c82 f30f554b 313d0570 c95accbb 7dc4b5aa e11204c0 8ffe732b
+#2) 4 bytes 0xc98c8e55
+7abc22c0 ae5af26c e93dbb94 433a0e0b 2e119d01 4f8e7f65 bd56c61c cccd9504
+#3) 55 bytes of zeros
+02779466 cdec1638 11d07881 5c633f21 90141308 1449002f 24aa3e80 f0b88ef7
+#4) 56 bytes of zeros
+d4817aa5 497628e7 c77e6b60 6107042b bba31308 88c5f47a 375e6179 be789fbb
+#5) 57 bytes of zeros
+65a16cb7 861335d5 ace3c607 18b5052e 44660726 da4cd13b b745381b 235a1785
+#6) 64 bytes of zeros
+f5a5fd42 d16a2030 2798ef6e d309979b 43003d23 20d9f0e8 ea9831a9 2759fb4b
+#7) 1000 bytes of zeros
+541b3e9d aa09b20b f85fa273 e5cbd3e8 0185aa4e c298e765 db87742b 70138a53
+#8) 1000 bytes of 0x41 ‘A’
+c2e68682 3489ced2 017f6059 b8b23931 8b6364f6 dcd835d0 a519105a 1eadd6e4
+#9) 1005 bytes of 0x55 ‘U’
+f4d62dde c0f3dd90 ea1380fa 16a5ff8d c4c54b21 740650f2 4afc4120 903552b0
+#10) 1000000 bytes of zeros
+d29751f2 649b32ff 572b5e0a 9f541ea6 60a50f94 ff0beedf b0b692b9 24cc8025
+#11) 0x20000000 (536870912) bytes of 0x5a ‘Z’
+15a1868c 12cc5395 1e182344 277447cd 0979536b adcc512a d24c67e9 b2d4f3dd
+#12) 0x41000000 (1090519040) bytes of zeros
+461c19a9 3bd4344f 9215f5ec 64357090 342bc66b 15a14831 7d276e31 cbc20b53
+#13) 0x6000003e (1610612798) bytes of 0x42 ‘B’
+c23ce8a7 895f4b21 ec0daf37 920ac0a2 62a22004 5a03eb2d fed48ef9 b05aabea
+```
+
+## License
+
+This repository is made available in the public domain. See [LICENSE
+FILE](LICENSE).
+
+## Reference implementation
+
+I had missed that when I made this implementation but [RFC 6234, chapter 8](https://tools.ietf.org/html/rfc6234#section-8) actually includes a reference implementation in C that is (at least in ambition) broader in scope than this one. I have however neither compiled nor tested it.
diff --git a/erts/lib_src/yielding_c_fun/test/examples/sha256_erlang_nif/c_src/sha-2/sha-256.c b/erts/lib_src/yielding_c_fun/test/examples/sha256_erlang_nif/c_src/sha-2/sha-256.c
new file mode 100644
index 0000000000..4338531d7e
--- /dev/null
+++ b/erts/lib_src/yielding_c_fun/test/examples/sha256_erlang_nif/c_src/sha-2/sha-256.c
@@ -0,0 +1,224 @@
+#include <stdint.h>
+#include <string.h>
+
+#ifdef YCF_YIELD_CODE_GENERATED
+#include "sha-2/sha-256.h"
+#else
+#include "sha-256.h"
+#endif
+
+#define CHUNK_SIZE 64
+#define TOTAL_LEN_LEN 8
+
+/*
+ * ABOUT bool: this file does not use bool in order to be as pre-C99 compatible as possible.
+ */
+
+/*
+ * Comments from pseudo-code at https://en.wikipedia.org/wiki/SHA-2 are reproduced here.
+ * When useful for clarification, portions of the pseudo-code are reproduced here too.
+ */
+
+/*
+ * Initialize array of round constants:
+ * (first 32 bits of the fractional parts of the cube roots of the first 64 primes 2..311):
+ */
+static const uint32_t k[] = {
+ 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
+ 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
+ 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
+ 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
+ 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
+ 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
+ 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
+ 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2
+};
+
+struct buffer_state {
+ const uint8_t * p;
+ size_t len;
+ size_t total_len;
+ int single_one_delivered; /* bool */
+ int total_len_delivered; /* bool */
+};
+
+static inline uint32_t right_rot(uint32_t value, unsigned int count)
+{
+ /*
+ * Defined behaviour in standard C for all count where 0 < count < 32,
+ * which is what we need here.
+ */
+ return value >> count | value << (32 - count);
+}
+
+static void init_buf_state(struct buffer_state * state, const void * input, size_t len)
+{
+ state->p = input;
+ state->len = len;
+ state->total_len = len;
+ state->single_one_delivered = 0;
+ state->total_len_delivered = 0;
+}
+
+/* Return value: bool */
+static int calc_chunk(uint8_t chunk[CHUNK_SIZE], struct buffer_state * state)
+{
+ size_t space_in_chunk;
+
+ if (state->total_len_delivered) {
+ return 0;
+ }
+
+ if (state->len >= CHUNK_SIZE) {
+ memcpy(chunk, state->p, CHUNK_SIZE);
+ state->p += CHUNK_SIZE;
+ state->len -= CHUNK_SIZE;
+ return 1;
+ }
+
+ memcpy(chunk, state->p, state->len);
+ chunk += state->len;
+ space_in_chunk = CHUNK_SIZE - state->len;
+ state->p += state->len;
+ state->len = 0;
+
+ /* If we are here, space_in_chunk is one at minimum. */
+ if (!state->single_one_delivered) {
+ *chunk++ = 0x80;
+ space_in_chunk -= 1;
+ state->single_one_delivered = 1;
+ }
+
+ /*
+ * Now:
+ * - either there is enough space left for the total length, and we can conclude,
+ * - or there is too little space left, and we have to pad the rest of this chunk with zeroes.
+ * In the latter case, we will conclude at the next invokation of this function.
+ */
+ if (space_in_chunk >= TOTAL_LEN_LEN) {
+ const size_t left = space_in_chunk - TOTAL_LEN_LEN;
+ size_t len = state->total_len;
+ int i;
+ memset(chunk, 0x00, left);
+ chunk += left;
+
+ /* Storing of len * 8 as a big endian 64-bit without overflow. */
+ chunk[7] = (uint8_t) (len << 3);
+ len >>= 5;
+ for (i = 6; i >= 0; i--) {
+ chunk[i] = (uint8_t) len;
+ len >>= 8;
+ }
+ state->total_len_delivered = 1;
+ } else {
+ memset(chunk, 0x00, space_in_chunk);
+ }
+
+ return 1;
+}
+
+#define YCF_STACK_ALLOC(X) malloc(X)
+
+
+/*
+ * Limitations:
+ * - Since input is a pointer in RAM, the data to hash should be in RAM, which could be a problem
+ * for large data sizes.
+ * - SHA algorithms theoretically operate on bit strings. However, this implementation has no support
+ * for bit string lengths that are not multiples of eight, and it really operates on arrays of bytes.
+ * In particular, the len parameter is a number of bytes.
+ */
+void calc_sha_256(uint8_t hash[32], const void * input, size_t len)
+{
+ /*
+ * Note 1: All integers (expect indexes) are 32-bit unsigned integers and addition is calculated modulo 2^32.
+ * Note 2: For each round, there is one round constant k[i] and one entry in the message schedule array w[i], 0 = i = 63
+ * Note 3: The compression function uses 8 working variables, a through h
+ * Note 4: Big-endian convention is used when expressing the constants in this pseudocode,
+ * and when parsing message block data from bytes to words, for example,
+ * the first word of the input message "abc" after padding is 0x61626380
+ */
+
+ /*
+ * Initialize hash values:
+ * (first 32 bits of the fractional parts of the square roots of the first 8 primes 2..19):
+ */
+ uint32_t h[8];
+ int i;
+ int j;
+
+ /* 512-bit chunks is what we will operate on. */
+ uint8_t* chunk = YCF_STACK_ALLOC(64);
+ struct buffer_state state;
+ h[0] = 0x6a09e667;
+ h[1] = 0xbb67ae85;
+ h[2] = 0x3c6ef372;
+ h[3] = 0xa54ff53a;
+ h[4] = 0x510e527f;
+ h[5] = 0x9b05688c;
+ h[6] = 0x1f83d9ab;
+ h[7] = 0x5be0cd19;
+ init_buf_state(&state, input, len);
+
+ while (calc_chunk(chunk, &state)) {
+ uint32_t ah[8];
+
+ /*
+ * create a 64-entry message schedule array w[0..63] of 32-bit words
+ * (The initial values in w[0..63] don't matter, so many implementations zero them here)
+ * copy chunk into first 16 words w[0..15] of the message schedule array
+ */
+ uint32_t w[64];
+ const uint8_t *p = chunk;
+
+ memset(w, 0x00, sizeof w);
+ for (i = 0; i < 16; i++) {
+ w[i] = (uint32_t) p[0] << 24 | (uint32_t) p[1] << 16 |
+ (uint32_t) p[2] << 8 | (uint32_t) p[3];
+ p += 4;
+ }
+
+ /* Extend the first 16 words into the remaining 48 words w[16..63] of the message schedule array: */
+ for (i = 16; i < 64; i++) {
+ const uint32_t s0 = right_rot(w[i - 15], 7) ^ right_rot(w[i - 15], 18) ^ (w[i - 15] >> 3);
+ const uint32_t s1 = right_rot(w[i - 2], 17) ^ right_rot(w[i - 2], 19) ^ (w[i - 2] >> 10);
+ w[i] = w[i - 16] + s0 + w[i - 7] + s1;
+ }
+
+ /* Initialize working variables to current hash value: */
+ for (i = 0; i < 8; i++)
+ ah[i] = h[i];
+
+ /* Compression function main loop: */
+ for (i = 0; i < 64; i++) {
+ const uint32_t s1 = right_rot(ah[4], 6) ^ right_rot(ah[4], 11) ^ right_rot(ah[4], 25);
+ const uint32_t ch = (ah[4] & ah[5]) ^ (~ah[4] & ah[6]);
+ const uint32_t temp1 = ah[7] + s1 + ch + k[i] + w[i];
+ const uint32_t s0 = right_rot(ah[0], 2) ^ right_rot(ah[0], 13) ^ right_rot(ah[0], 22);
+ const uint32_t maj = (ah[0] & ah[1]) ^ (ah[0] & ah[2]) ^ (ah[1] & ah[2]);
+ const uint32_t temp2 = s0 + maj;
+
+ ah[7] = ah[6];
+ ah[6] = ah[5];
+ ah[5] = ah[4];
+ ah[4] = ah[3] + temp1;
+ ah[3] = ah[2];
+ ah[2] = ah[1];
+ ah[1] = ah[0];
+ ah[0] = temp1 + temp2;
+ }
+
+ /* Add the compressed chunk to the current hash value: */
+ for (i = 0; i < 8; i++)
+ h[i] += ah[i];
+ }
+
+ /* Produce the final hash value (big-endian): */
+ for (i = 0, j = 0; i < 8; i++)
+ {
+ hash[j++] = (uint8_t) (h[i] >> 24);
+ hash[j++] = (uint8_t) (h[i] >> 16);
+ hash[j++] = (uint8_t) (h[i] >> 8);
+ hash[j++] = (uint8_t) h[i];
+ }
+}
diff --git a/erts/lib_src/yielding_c_fun/test/examples/sha256_erlang_nif/c_src/sha-2/sha-256.h b/erts/lib_src/yielding_c_fun/test/examples/sha256_erlang_nif/c_src/sha-2/sha-256.h
new file mode 100644
index 0000000000..2cce60bb7b
--- /dev/null
+++ b/erts/lib_src/yielding_c_fun/test/examples/sha256_erlang_nif/c_src/sha-2/sha-256.h
@@ -0,0 +1,4 @@
+#include <stdint.h>
+#include <stdlib.h>
+
+void calc_sha_256(uint8_t hash[], const void *input, size_t len);
diff --git a/erts/lib_src/yielding_c_fun/test/examples/sha256_erlang_nif/c_src/sha-2/sha-256_orginal.c b/erts/lib_src/yielding_c_fun/test/examples/sha256_erlang_nif/c_src/sha-2/sha-256_orginal.c
new file mode 100644
index 0000000000..53d6ff2e2a
--- /dev/null
+++ b/erts/lib_src/yielding_c_fun/test/examples/sha256_erlang_nif/c_src/sha-2/sha-256_orginal.c
@@ -0,0 +1,210 @@
+#include <stdint.h>
+#include <string.h>
+
+#include "sha-256.h"
+
+#define CHUNK_SIZE 64
+#define TOTAL_LEN_LEN 8
+
+/*
+ * ABOUT bool: this file does not use bool in order to be as pre-C99 compatible as possible.
+ */
+
+/*
+ * Comments from pseudo-code at https://en.wikipedia.org/wiki/SHA-2 are reproduced here.
+ * When useful for clarification, portions of the pseudo-code are reproduced here too.
+ */
+
+/*
+ * Initialize array of round constants:
+ * (first 32 bits of the fractional parts of the cube roots of the first 64 primes 2..311):
+ */
+static const uint32_t k[] = {
+ 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
+ 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
+ 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
+ 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
+ 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
+ 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
+ 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
+ 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2
+};
+
+struct buffer_state {
+ const uint8_t * p;
+ size_t len;
+ size_t total_len;
+ int single_one_delivered; /* bool */
+ int total_len_delivered; /* bool */
+};
+
+static inline uint32_t right_rot(uint32_t value, unsigned int count)
+{
+ /*
+ * Defined behaviour in standard C for all count where 0 < count < 32,
+ * which is what we need here.
+ */
+ return value >> count | value << (32 - count);
+}
+
+static void init_buf_state(struct buffer_state * state, const void * input, size_t len)
+{
+ state->p = input;
+ state->len = len;
+ state->total_len = len;
+ state->single_one_delivered = 0;
+ state->total_len_delivered = 0;
+}
+
+/* Return value: bool */
+static int calc_chunk(uint8_t chunk[CHUNK_SIZE], struct buffer_state * state)
+{
+ size_t space_in_chunk;
+
+ if (state->total_len_delivered) {
+ return 0;
+ }
+
+ if (state->len >= CHUNK_SIZE) {
+ memcpy(chunk, state->p, CHUNK_SIZE);
+ state->p += CHUNK_SIZE;
+ state->len -= CHUNK_SIZE;
+ return 1;
+ }
+
+ memcpy(chunk, state->p, state->len);
+ chunk += state->len;
+ space_in_chunk = CHUNK_SIZE - state->len;
+ state->p += state->len;
+ state->len = 0;
+
+ /* If we are here, space_in_chunk is one at minimum. */
+ if (!state->single_one_delivered) {
+ *chunk++ = 0x80;
+ space_in_chunk -= 1;
+ state->single_one_delivered = 1;
+ }
+
+ /*
+ * Now:
+ * - either there is enough space left for the total length, and we can conclude,
+ * - or there is too little space left, and we have to pad the rest of this chunk with zeroes.
+ * In the latter case, we will conclude at the next invokation of this function.
+ */
+ if (space_in_chunk >= TOTAL_LEN_LEN) {
+ const size_t left = space_in_chunk - TOTAL_LEN_LEN;
+ size_t len = state->total_len;
+ int i;
+ memset(chunk, 0x00, left);
+ chunk += left;
+
+ /* Storing of len * 8 as a big endian 64-bit without overflow. */
+ chunk[7] = (uint8_t) (len << 3);
+ len >>= 5;
+ for (i = 6; i >= 0; i--) {
+ chunk[i] = (uint8_t) len;
+ len >>= 8;
+ }
+ state->total_len_delivered = 1;
+ } else {
+ memset(chunk, 0x00, space_in_chunk);
+ }
+
+ return 1;
+}
+
+/*
+ * Limitations:
+ * - Since input is a pointer in RAM, the data to hash should be in RAM, which could be a problem
+ * for large data sizes.
+ * - SHA algorithms theoretically operate on bit strings. However, this implementation has no support
+ * for bit string lengths that are not multiples of eight, and it really operates on arrays of bytes.
+ * In particular, the len parameter is a number of bytes.
+ */
+void calc_sha_256(uint8_t hash[32], const void * input, size_t len)
+{
+ /*
+ * Note 1: All integers (expect indexes) are 32-bit unsigned integers and addition is calculated modulo 2^32.
+ * Note 2: For each round, there is one round constant k[i] and one entry in the message schedule array w[i], 0 = i = 63
+ * Note 3: The compression function uses 8 working variables, a through h
+ * Note 4: Big-endian convention is used when expressing the constants in this pseudocode,
+ * and when parsing message block data from bytes to words, for example,
+ * the first word of the input message "abc" after padding is 0x61626380
+ */
+
+ /*
+ * Initialize hash values:
+ * (first 32 bits of the fractional parts of the square roots of the first 8 primes 2..19):
+ */
+ uint32_t h[] = { 0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19 };
+ int i, j;
+
+ /* 512-bit chunks is what we will operate on. */
+ uint8_t chunk[64];
+
+ struct buffer_state state;
+
+ init_buf_state(&state, input, len);
+
+ while (calc_chunk(chunk, &state)) {
+ uint32_t ah[8];
+
+ /*
+ * create a 64-entry message schedule array w[0..63] of 32-bit words
+ * (The initial values in w[0..63] don't matter, so many implementations zero them here)
+ * copy chunk into first 16 words w[0..15] of the message schedule array
+ */
+ uint32_t w[64];
+ const uint8_t *p = chunk;
+
+ memset(w, 0x00, sizeof w);
+ for (i = 0; i < 16; i++) {
+ w[i] = (uint32_t) p[0] << 24 | (uint32_t) p[1] << 16 |
+ (uint32_t) p[2] << 8 | (uint32_t) p[3];
+ p += 4;
+ }
+
+ /* Extend the first 16 words into the remaining 48 words w[16..63] of the message schedule array: */
+ for (i = 16; i < 64; i++) {
+ const uint32_t s0 = right_rot(w[i - 15], 7) ^ right_rot(w[i - 15], 18) ^ (w[i - 15] >> 3);
+ const uint32_t s1 = right_rot(w[i - 2], 17) ^ right_rot(w[i - 2], 19) ^ (w[i - 2] >> 10);
+ w[i] = w[i - 16] + s0 + w[i - 7] + s1;
+ }
+
+ /* Initialize working variables to current hash value: */
+ for (i = 0; i < 8; i++)
+ ah[i] = h[i];
+
+ /* Compression function main loop: */
+ for (i = 0; i < 64; i++) {
+ const uint32_t s1 = right_rot(ah[4], 6) ^ right_rot(ah[4], 11) ^ right_rot(ah[4], 25);
+ const uint32_t ch = (ah[4] & ah[5]) ^ (~ah[4] & ah[6]);
+ const uint32_t temp1 = ah[7] + s1 + ch + k[i] + w[i];
+ const uint32_t s0 = right_rot(ah[0], 2) ^ right_rot(ah[0], 13) ^ right_rot(ah[0], 22);
+ const uint32_t maj = (ah[0] & ah[1]) ^ (ah[0] & ah[2]) ^ (ah[1] & ah[2]);
+ const uint32_t temp2 = s0 + maj;
+
+ ah[7] = ah[6];
+ ah[6] = ah[5];
+ ah[5] = ah[4];
+ ah[4] = ah[3] + temp1;
+ ah[3] = ah[2];
+ ah[2] = ah[1];
+ ah[1] = ah[0];
+ ah[0] = temp1 + temp2;
+ }
+
+ /* Add the compressed chunk to the current hash value: */
+ for (i = 0; i < 8; i++)
+ h[i] += ah[i];
+ }
+
+ /* Produce the final hash value (big-endian): */
+ for (i = 0, j = 0; i < 8; i++)
+ {
+ hash[j++] = (uint8_t) (h[i] >> 24);
+ hash[j++] = (uint8_t) (h[i] >> 16);
+ hash[j++] = (uint8_t) (h[i] >> 8);
+ hash[j++] = (uint8_t) h[i];
+ }
+}
diff --git a/erts/lib_src/yielding_c_fun/test/examples/sha256_erlang_nif/c_src/sha-2/test.c b/erts/lib_src/yielding_c_fun/test/examples/sha256_erlang_nif/c_src/sha-2/test.c
new file mode 100644
index 0000000000..5b5031c374
--- /dev/null
+++ b/erts/lib_src/yielding_c_fun/test/examples/sha256_erlang_nif/c_src/sha-2/test.c
@@ -0,0 +1,238 @@
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include "sha-256.h"
+
+struct string_vector {
+ const char *input;
+ const char *output;
+};
+
+static const struct string_vector STRING_VECTORS[] = {
+ {
+ "",
+ "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
+ },
+ {
+ "abc",
+ "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad"
+ },
+ {
+ "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef",
+ "a8ae6e6ee929abea3afcfc5258c8ccd6f85273e0d4626d26c7279f3250f77c8e"
+ },
+ {
+ "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcde",
+ "057ee79ece0b9a849552ab8d3c335fe9a5f1c46ef5f1d9b190c295728628299c"
+ },
+ {
+ "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0",
+ "2a6ad82f3620d3ebe9d678c812ae12312699d673240d5be8fac0910a70000d93"
+ },
+ {
+ "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq",
+ "248d6a61d20638b8e5c026930c3e6039a33ce45964ff2167f6ecedd419db06c1"
+ },
+ {
+ "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmno"
+ "ijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu",
+ "cf5b16a778af8380036ce59e7b0492370b249b11e8f07a51afac45037afee9d1"
+ }
+};
+
+#define LARGE_MESSAGES 1
+
+static uint8_t data1[] = { 0xbd };
+static uint8_t data2[] = { 0xc9, 0x8c, 0x8e, 0x55 };
+static uint8_t data7[1000];
+static uint8_t data8[1000];
+static uint8_t data9[1005];
+#if LARGE_MESSAGES
+#define SIZEOF_DATA11 536870912
+#define SIZEOF_DATA12 1090519040
+#define SIZEOF_DATA13 1610612798
+static uint8_t * data11;
+static uint8_t * data12;
+static uint8_t * data13;
+#endif
+
+struct vector {
+ const uint8_t *input;
+ size_t input_len;
+ const char *output;
+};
+
+static struct vector vectors[] = {
+ {
+ data1,
+ sizeof data1,
+ "68325720aabd7c82f30f554b313d0570c95accbb7dc4b5aae11204c08ffe732b"
+ },
+ {
+ data2,
+ sizeof data2,
+ "7abc22c0ae5af26ce93dbb94433a0e0b2e119d014f8e7f65bd56c61ccccd9504"
+ },
+ {
+ data7,
+ 55,
+ "02779466cdec163811d078815c633f21901413081449002f24aa3e80f0b88ef7"
+ },
+ {
+ data7,
+ 56,
+ "d4817aa5497628e7c77e6b606107042bbba3130888c5f47a375e6179be789fbb"
+ },
+ {
+ data7,
+ 57,
+ "65a16cb7861335d5ace3c60718b5052e44660726da4cd13bb745381b235a1785"
+ },
+ {
+ data7,
+ 64,
+ "f5a5fd42d16a20302798ef6ed309979b43003d2320d9f0e8ea9831a92759fb4b"
+ },
+ {
+ data7,
+ sizeof data7,
+ "541b3e9daa09b20bf85fa273e5cbd3e80185aa4ec298e765db87742b70138a53"
+ },
+ {
+ data8,
+ sizeof data8,
+ "c2e686823489ced2017f6059b8b239318b6364f6dcd835d0a519105a1eadd6e4"
+ },
+ {
+ data9,
+ sizeof data9,
+ "f4d62ddec0f3dd90ea1380fa16a5ff8dc4c54b21740650f24afc4120903552b0"
+ }
+#if LARGE_MESSAGES
+ ,
+ {
+ NULL,
+ 1000000,
+ "d29751f2649b32ff572b5e0a9f541ea660a50f94ff0beedfb0b692b924cc8025"
+ },
+ {
+ NULL,
+ SIZEOF_DATA11,
+ "15a1868c12cc53951e182344277447cd0979536badcc512ad24c67e9b2d4f3dd"
+ },
+ {
+ NULL,
+ SIZEOF_DATA12,
+ "461c19a93bd4344f9215f5ec64357090342bc66b15a148317d276e31cbc20b53"
+ },
+ {
+ NULL,
+ SIZEOF_DATA13,
+ "c23ce8a7895f4b21ec0daf37920ac0a262a220045a03eb2dfed48ef9b05aabea"
+ }
+#endif
+};
+
+static void construct_binary_messages(void)
+{
+ memset(data7, 0x00, sizeof data7);
+ memset(data8, 0x41, sizeof data8);
+ memset(data9, 0x55, sizeof data9);
+#if LARGE_MESSAGES
+ /*
+ * Heap allocation as a workaround for some linkers not liking
+ * large BSS segments.
+ */
+ data11 = malloc(SIZEOF_DATA11);
+ data12 = malloc(SIZEOF_DATA12);
+ data13 = malloc(SIZEOF_DATA13);
+ memset(data11, 0x5a, SIZEOF_DATA11);
+ memset(data12, 0x00, SIZEOF_DATA12);
+ memset(data13, 0x42, SIZEOF_DATA13);
+ vectors[9].input = data12;
+ vectors[10].input = data11;
+ vectors[11].input = data12;
+ vectors[12].input = data13;
+#endif
+}
+
+static void destruct_binary_messages(void)
+{
+#if LARGE_MESSAGES
+ free(data11);
+ free(data12);
+ free(data13);
+#endif
+}
+
+static void hash_to_string(char string[65], const uint8_t hash[32])
+{
+ size_t i;
+ for (i = 0; i < 32; i++) {
+ string += sprintf(string, "%02x", hash[i]);
+ }
+}
+
+static int string_test(const char input[], const char output[])
+{
+ uint8_t hash[32];
+ char hash_string[65];
+ calc_sha_256(hash, input, strlen(input));
+ hash_to_string(hash_string, hash);
+ printf("input: %s\n", input);
+ printf("hash : %s\n", hash_string);
+ if (strcmp(output, hash_string)) {
+ printf("FAILURE!\n\n");
+ return 1;
+ } else {
+ printf("SUCCESS!\n\n");
+ return 0;
+ }
+}
+
+/*
+ * Limitation:
+ * - The variable input_len will be truncated to its LONG_BIT least
+ * significant bits in the print output. This will never be a problem
+ * for values that in practice are less than 2^32 - 1. Rationale: ANSI
+ * C-compatibility and keeping it simple.
+ */
+static int test(const uint8_t * input, size_t input_len, const char output[])
+{
+ uint8_t hash[32];
+ char hash_string[65];
+ calc_sha_256(hash, input, input_len);
+ hash_to_string(hash_string, hash);
+ printf("input starts with 0x%02x, length %lu\n", *input, (unsigned long) input_len);
+ printf("hash : %s\n", hash_string);
+ if (strcmp(output, hash_string)) {
+ printf("FAILURE!\n\n");
+ return 1;
+ } else {
+ printf("SUCCESS!\n\n");
+ return 0;
+ }
+}
+
+int main(void)
+{
+ size_t i;
+ for (i = 0; i < (sizeof STRING_VECTORS / sizeof (struct string_vector)); i++) {
+ const struct string_vector *vector = &STRING_VECTORS[i];
+ if (string_test(vector->input, vector->output))
+ return 1;
+ }
+ construct_binary_messages();
+ for (i = 0; i < (sizeof vectors / sizeof (struct vector)); i++) {
+ const struct vector *vector = &vectors[i];
+ if (test(vector->input, vector->input_len, vector->output))
+ {
+ destruct_binary_messages();
+ return 1;
+ }
+ }
+ destruct_binary_messages();
+ return 0;
+}
diff --git a/erts/lib_src/yielding_c_fun/test/examples/sha256_erlang_nif/c_src/sha256_nif.c b/erts/lib_src/yielding_c_fun/test/examples/sha256_erlang_nif/c_src/sha256_nif.c
new file mode 100644
index 0000000000..6ad22c1dcd
--- /dev/null
+++ b/erts/lib_src/yielding_c_fun/test/examples/sha256_erlang_nif/c_src/sha256_nif.c
@@ -0,0 +1,173 @@
+
+#include "erl_nif.h"
+
+#include "sha-2/sha-256.h"
+struct buffer_state;
+#include "gen_yielding_sha_256.h"
+
+
+
+
+ERL_NIF_TERM
+mk_atom(ErlNifEnv* env, const char* atom)
+{
+ ERL_NIF_TERM ret;
+
+ if(!enif_make_existing_atom(env, atom, &ret, ERL_NIF_LATIN1))
+ {
+ return enif_make_atom(env, atom);
+ }
+
+ return ret;
+}
+
+ERL_NIF_TERM
+mk_error(ErlNifEnv* env, const char* mesg)
+{
+ return enif_make_tuple2(env, mk_atom(env, "error"), mk_atom(env, mesg));
+}
+
+
+
+void* allocator(size_t size, void* context){
+ (void)context;
+ return enif_alloc(size);
+}
+
+void freer(void* data, void* context){
+ (void)context;
+ enif_free(data);
+}
+
+typedef struct {
+ void* continuation;
+ ErlNifEnv* work_env;
+ ErlNifBinary out_bin;
+} sha256continuation;
+
+/* #define DEBUG */
+#ifdef DEBUG
+# define DEBUG_PRINT(x) printf x
+long nr_of_yields = 0;
+#else
+# define DEBUG_PRINT(x) do {} while (0)
+#endif
+
+void sha256continuation_ErlNifResourceDtor(ErlNifEnv* caller_env, void* obj){
+ sha256continuation* continuation = obj;
+ if(continuation->continuation != NULL){
+ calc_sha_256_ycf_gen_destroy(continuation->continuation);
+ enif_release_binary(&continuation->out_bin);
+ enif_free_env(continuation->work_env);
+ continuation->continuation = NULL;
+ DEBUG_PRINT(("DESTOY\n"));
+ }
+ DEBUG_PRINT(("DEALLOC\n"));
+}
+
+#define REDUCTIONS_UNTIL_YCF_YIELD() (200*300*8)
+
+static ERL_NIF_TERM
+sha256_nif_cont_after_yield(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
+{
+ ErlNifResourceType* res_type = (ErlNifResourceType*)enif_priv_data(env);
+ long nr_of_reductions = REDUCTIONS_UNTIL_YCF_YIELD();
+ sha256continuation* continuation;
+ enif_get_resource(env,
+ argv[0],
+ res_type,
+ (void**)&continuation);
+ calc_sha_256_ycf_gen_continue(&nr_of_reductions,
+ &continuation->continuation,
+ NULL);
+ if(YCF_IS_YIELDED(continuation->continuation)){
+#ifdef DEBUG
+ nr_of_yields++;
+#endif
+ return enif_schedule_nif(env, "sha256_nif_cont_after_yield", 0, sha256_nif_cont_after_yield, 1, argv);
+ }
+ DEBUG_PRINT(("NUMBER OF YCF_YIELD()S %ld \n", nr_of_yields));
+ enif_free_env(continuation->work_env);
+ ErlNifBinary out_bin = continuation->out_bin;
+ return enif_make_binary(env, &out_bin);
+}
+
+static ERL_NIF_TERM
+sha256_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
+{
+ /* ErlNifEnv* msg_env; */
+ ErlNifResourceType* res_type = (ErlNifResourceType*)enif_priv_data(env);
+ ErlNifEnv *work_env = enif_alloc_env();
+ void* wb = NULL;
+ long nr_of_reductions;
+ ERL_NIF_TERM newargv[1];
+ if(argc != 1 || !enif_is_binary(env, argv[0]))
+ {
+ return enif_make_badarg(env);
+ }
+ /* Copy the input binary to the work environemnt so it will be kept when we are yielding */
+ ERL_NIF_TERM input_bin_term = enif_make_copy(work_env, argv[0]);
+ ErlNifBinary input_bin;
+ enif_inspect_binary(work_env, input_bin_term, &input_bin);
+ ErlNifBinary out_bin;
+ enif_alloc_binary(256/8, &out_bin);
+ nr_of_reductions = REDUCTIONS_UNTIL_YCF_YIELD();
+#ifdef DEBUG
+ nr_of_yields = 0;
+#endif
+ calc_sha_256_ycf_gen_yielding(&nr_of_reductions,
+ &wb,
+ NULL,
+ allocator,
+ freer,
+ NULL,
+ 64,
+ NULL,
+ out_bin.data,
+ input_bin.data,
+ input_bin.size);
+ if(YCF_IS_YIELDED(wb)){
+ DEBUG_PRINT(("TRAPPED FIRST CALL %ld \n", nr_of_reductions));
+ sha256continuation* continuation =
+ enif_alloc_resource(res_type,
+ sizeof(sha256continuation));
+ continuation->work_env = work_env;
+ continuation->continuation = wb;
+ continuation->out_bin = out_bin;
+ newargv[0] = enif_make_resource(env, continuation);
+ enif_release_resource(continuation);
+ return enif_schedule_nif(env, "sha256_nif_cont_after_yield", 0, sha256_nif_cont_after_yield, 1, newargv);
+ }
+ enif_free_env(work_env);
+ return enif_make_binary(env, &out_bin);
+}
+
+static ErlNifFunc nif_funcs[] = {
+ {"sha256", 1, sha256_nif}
+};
+
+static int
+nifload(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info)
+{
+ *priv_data = enif_open_resource_type(env,
+ NULL,
+ "sha256_nif",
+ sha256continuation_ErlNifResourceDtor,
+ ERL_NIF_RT_CREATE|ERL_NIF_RT_TAKEOVER,
+ NULL);
+ return 0;
+}
+
+static int
+nifupgrade(ErlNifEnv* env, void** priv_data, void** old_priv_data, ERL_NIF_TERM load_info)
+{
+ *priv_data = enif_open_resource_type(env,
+ NULL,
+ "sha256_nif",
+ sha256continuation_ErlNifResourceDtor,
+ ERL_NIF_RT_TAKEOVER,
+ NULL);
+ return 0;
+}
+
+ERL_NIF_INIT(sha256_nif, nif_funcs, nifload, NULL, nifupgrade, NULL);
diff --git a/erts/lib_src/yielding_c_fun/test/examples/sha256_erlang_nif/rebar.config b/erts/lib_src/yielding_c_fun/test/examples/sha256_erlang_nif/rebar.config
new file mode 100644
index 0000000000..d20b1db59d
--- /dev/null
+++ b/erts/lib_src/yielding_c_fun/test/examples/sha256_erlang_nif/rebar.config
@@ -0,0 +1,12 @@
+{erl_opts, [debug_info]}.
+{deps, []}.
+
+{erl_opts, [debug_info]}.
+{deps, []}.
+
+{pre_hooks,
+ [{"(linux|darwin|solaris)", compile, "make -C c_src"},
+ {"(freebsd)", compile, "gmake -C c_src"}]}.
+{post_hooks,
+ [{"(linux|darwin|solaris)", clean, "make -C c_src clean"},
+ {"(freebsd)", clean, "gmake -C c_src clean"}]}. \ No newline at end of file
diff --git a/erts/lib_src/yielding_c_fun/test/examples/sha256_erlang_nif/src/sha256_nif.app.src b/erts/lib_src/yielding_c_fun/test/examples/sha256_erlang_nif/src/sha256_nif.app.src
new file mode 100644
index 0000000000..603a5092bb
--- /dev/null
+++ b/erts/lib_src/yielding_c_fun/test/examples/sha256_erlang_nif/src/sha256_nif.app.src
@@ -0,0 +1,14 @@
+{application, sha256_nif,
+ [{description, "An OTP library"},
+ {vsn, "0.1.0"},
+ {registered, []},
+ {applications,
+ [kernel,
+ stdlib
+ ]},
+ {env,[]},
+ {modules, []},
+
+ {licenses, ["Apache 2.0"]},
+ {links, []}
+ ]}.
diff --git a/erts/lib_src/yielding_c_fun/test/examples/sha256_erlang_nif/src/sha256_nif.erl b/erts/lib_src/yielding_c_fun/test/examples/sha256_erlang_nif/src/sha256_nif.erl
new file mode 100644
index 0000000000..d613f2c5aa
--- /dev/null
+++ b/erts/lib_src/yielding_c_fun/test/examples/sha256_erlang_nif/src/sha256_nif.erl
@@ -0,0 +1,27 @@
+-module(sha256_nif).
+
+-export([sha256/1]).
+-on_load(init/0).
+
+-define(APPNAME, sha256_nif).
+-define(LIBNAME, sha256_erlang_nif).
+
+sha256(_) ->
+ not_loaded(?LINE).
+
+init() ->
+ SoName = case code:priv_dir(?APPNAME) of
+ {error, bad_name} ->
+ case filelib:is_dir(filename:join(["..", priv])) of
+ true ->
+ filename:join(["..", priv, ?LIBNAME]);
+ _ ->
+ filename:join([priv, ?LIBNAME])
+ end;
+ Dir ->
+ filename:join(Dir, ?LIBNAME)
+ end,
+ erlang:load_nif(SoName, 0).
+
+not_loaded(Line) ->
+ exit({not_loaded, [{module, ?MODULE}, {line, Line}]}).
diff --git a/erts/lib_src/yielding_c_fun/test/examples/sha256_erlang_nif/test/basic_SUITE.erl b/erts/lib_src/yielding_c_fun/test/examples/sha256_erlang_nif/test/basic_SUITE.erl
new file mode 100644
index 0000000000..7d0a42ce84
--- /dev/null
+++ b/erts/lib_src/yielding_c_fun/test/examples/sha256_erlang_nif/test/basic_SUITE.erl
@@ -0,0 +1,32 @@
+-module(basic_SUITE).
+-include_lib("common_test/include/ct.hrl").
+-export([all/0]).
+-export([test1/1, test2/1]).
+
+all() ->
+ [test1, test2].
+
+bin_to_hex_string(Bin)->
+ lists:flatten(io_lib:format("~s", [lists:flatten([io_lib:format("~2.16.0B",[X]) || <<X:8>> <= Bin ])])).
+
+foreach_up_to_helper(UpTo, UpTo, Fun) ->
+ ok;
+foreach_up_to_helper(UpTo, Current, Fun) ->
+ Fun(Current),
+ foreach_up_to_helper(UpTo, Current + 1, Fun).
+
+foreach_up_to(UpTo, Fun) ->
+ foreach_up_to_helper(UpTo, 1, Fun).
+
+test1(_Config) ->
+ foreach_up_to(15, fun(V) ->
+ In = erlang:list_to_binary(lists:flatten(lists:duplicate(1024 bsl V, "h"))),
+ {CryptoHashTime, Expect} = timer:tc(crypto, hash, [sha256,In]),
+ {MyTime, Expect} = timer:tc(sha256_nif, sha256, [In]),
+ io:format("Size: ~p My Time: ~p Crypto Hash Time: ~p", [1024 bsl V, MyTime/1000000, CryptoHashTime/1000000])
+ end),
+ ok.
+
+test2(_Config) ->
+ Expect = crypto:hash(sha256, "hej"),
+ Expect = sha256_nif:sha256(<<"hej">>).
diff --git a/erts/lib_src/yielding_c_fun/test/examples/simple_fun_call.c b/erts/lib_src/yielding_c_fun/test/examples/simple_fun_call.c
new file mode 100644
index 0000000000..0ac86d0b7d
--- /dev/null
+++ b/erts/lib_src/yielding_c_fun/test/examples/simple_fun_call.c
@@ -0,0 +1,92 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB and Kjell Winblad 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%
+ */
+
+/*
+ * Description:
+ *
+ * Author: Kjell Winblad
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#define YCF_YIELD()
+
+
+void sub_fun(char* x){
+ *x = *x + 1; /* x == 3*/
+ *x = *x + 1; /* x == 4*/
+}
+
+int sub_fun2(int x, int y){
+ return x+y;
+}
+
+int fun(char x){
+ int y;
+ (void)y;
+ x = x + 1; /* x == 2*/
+ sub_fun(((&x)));
+ sub_fun2(10, 20);
+ y = sub_fun2(10, 20);
+ YCF_YIELD();
+ x = x + 1; /* x == 5*/
+ return x;
+}
+
+void* allocator(size_t size, void* context){
+ (void)context;
+ return malloc(size);
+}
+
+void freer(void* data, void* context){
+ (void)context;
+ free(data);
+}
+
+int main( int argc, const char* argv[] )
+{
+#ifdef YCF_YIELD_CODE_GENERATED
+ void* wb = NULL;
+#endif
+ int ret = 0;
+ long nr_of_reductions = 1;
+#ifdef YCF_YIELD_CODE_GENERATED
+ do{
+ ret = fun_ycf_gen_yielding(&nr_of_reductions,&wb,NULL,allocator,freer,NULL,0,NULL,1);
+ if(wb != NULL){
+ printf("TRAPPED\n");
+ }
+ }while(wb != NULL);
+ if(wb != NULL){
+ free(wb);
+ }
+#else
+ fun(1);
+#endif
+ printf("RETURNED %d\n", ret);
+ if(ret != 5){
+ return 1;
+ }else{
+ return 0;
+ }
+}
+
diff --git a/erts/lib_src/yielding_c_fun/test/examples/simple_fun_call.c.out b/erts/lib_src/yielding_c_fun/test/examples/simple_fun_call.c.out
new file mode 100644
index 0000000000..20db8bc359
--- /dev/null
+++ b/erts/lib_src/yielding_c_fun/test/examples/simple_fun_call.c.out
@@ -0,0 +1,2 @@
+TRAPPED
+RETURNED 5
diff --git a/erts/lib_src/yielding_c_fun/test/examples/simple_yield.c b/erts/lib_src/yielding_c_fun/test/examples/simple_yield.c
new file mode 100644
index 0000000000..49316af598
--- /dev/null
+++ b/erts/lib_src/yielding_c_fun/test/examples/simple_yield.c
@@ -0,0 +1,58 @@
+#include <stdio.h>
+#include <stdlib.h>
+
+#define YCF_YIELD()
+
+int empty_fun(){
+ return 1;
+}
+
+int fun(char x){
+ x = x + 1; /* x == 2*/
+ YCF_YIELD();
+ x = x + 1; /* x == 3*/
+ {
+ int hej = !!empty_fun();
+ (void)hej;
+ }
+ return x;
+}
+
+void* allocator(size_t size, void* context){
+ (void)context;
+ return malloc(size);
+}
+
+void freer(void* data, void* context){
+ (void)context;
+ free(data);
+}
+
+int main( int argc, const char* argv[] )
+{
+#ifdef YCF_YIELD_CODE_GENERATED
+ void* wb = NULL;
+ long nr_of_reductions = 1;
+#endif
+ int ret = 0;
+#ifdef YCF_YIELD_CODE_GENERATED
+ do{
+ ret = fun_ycf_gen_yielding(&nr_of_reductions,&wb,NULL,allocator,freer,NULL,0,NULL,1);
+ if(wb != NULL){
+ printf("TRAPPED\n");
+ }
+ }while(wb != NULL);
+ if(wb != NULL){
+ free(wb);
+ }
+#else
+ fun(1);
+#endif
+ printf("RETURNED %d\n", ret);
+ if(ret != 3){
+ return 1;
+ }else{
+ return 0;
+ }
+}
+
diff --git a/erts/lib_src/yielding_c_fun/test/examples/simple_yield.c.out b/erts/lib_src/yielding_c_fun/test/examples/simple_yield.c.out
new file mode 100644
index 0000000000..96f574d048
--- /dev/null
+++ b/erts/lib_src/yielding_c_fun/test/examples/simple_yield.c.out
@@ -0,0 +1,2 @@
+TRAPPED
+RETURNED 3
diff --git a/erts/lib_src/yielding_c_fun/test/examples/simple_yield_no_reds.c b/erts/lib_src/yielding_c_fun/test/examples/simple_yield_no_reds.c
new file mode 100644
index 0000000000..5c3778e203
--- /dev/null
+++ b/erts/lib_src/yielding_c_fun/test/examples/simple_yield_no_reds.c
@@ -0,0 +1,61 @@
+#include <stdio.h>
+#include <stdlib.h>
+
+#define YCF_YIELD_NO_REDS()
+
+int empty_fun(){
+ return 1;
+}
+
+int fun(char x){
+ x = x + 1; /* x == 2*/
+ YCF_YIELD_NO_REDS();
+ x = x + 1; /* x == 3*/
+ {
+ int hej = !!empty_fun();
+ (void)hej;
+ return x;
+ }
+}
+
+void* allocator(size_t size, void* context){
+ (void)context;
+ return malloc(size);
+}
+
+void freer(void* data, void* context){
+ (void)context;
+ free(data);
+}
+
+int main( int argc, const char* argv[] )
+{
+#ifdef YCF_YIELD_CODE_GENERATED
+ void* wb = NULL;
+ long nr_of_reductions = 777;
+#endif
+ int ret = 0;
+#ifdef YCF_YIELD_CODE_GENERATED
+ do{
+ ret = fun_ycf_gen_yielding(&nr_of_reductions,&wb,NULL,allocator,freer,NULL,0,NULL,1);
+ if(wb != NULL){
+ if(nr_of_reductions != 777){
+ printf("SHOULD NOT HAPPEN\n");
+ }
+ printf("TRAPPED\n");
+ }
+ }while(wb != NULL);
+ if(wb != NULL){
+ free(wb);
+ }
+#else
+ fun(1);
+#endif
+ printf("RETURNED %d\n", ret);
+ if(ret != 3){
+ return 1;
+ }else{
+ return 0;
+ }
+}
+
diff --git a/erts/lib_src/yielding_c_fun/test/examples/simple_yield_no_reds.c.out b/erts/lib_src/yielding_c_fun/test/examples/simple_yield_no_reds.c.out
new file mode 100644
index 0000000000..96f574d048
--- /dev/null
+++ b/erts/lib_src/yielding_c_fun/test/examples/simple_yield_no_reds.c.out
@@ -0,0 +1,2 @@
+TRAPPED
+RETURNED 3
diff --git a/erts/lib_src/yielding_c_fun/test/examples/simple_yielding_fun_call.c b/erts/lib_src/yielding_c_fun/test/examples/simple_yielding_fun_call.c
new file mode 100644
index 0000000000..99fcabaf99
--- /dev/null
+++ b/erts/lib_src/yielding_c_fun/test/examples/simple_yielding_fun_call.c
@@ -0,0 +1,111 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB and Kjell Winblad 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%
+ */
+
+/*
+ * Description:
+ *
+ * Author: Kjell Winblad
+ *
+ */
+
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#define YCF_YIELD()
+
+
+void sub_fun_1(char* res, char x, char y){
+ printf("sub_fun_1_1\n");
+ YCF_YIELD();
+ printf("sub_fun_1_2\n");
+ *res = x + y;
+ YCF_YIELD();
+ printf("sub_fun_1_3\n");
+ YCF_YIELD();
+ printf("sub_fun_1_4\n");
+}
+
+void sub_fun_2(){
+ printf("sub_fun_2_1\n");
+ YCF_YIELD();
+ printf("sub_fun_2_2\n");
+}
+
+char sub_fun_3(char x, char y){
+ char res;
+ printf("sub_fun_2_1\n");
+ YCF_YIELD();
+ res = x + y;
+ YCF_YIELD();
+ printf("sub_fun_2_2\n");
+ return res;
+}
+
+int fun(char x){
+ char y;
+ char z = 10
+ YCF_YIELD();
+ sub_fun_1(&y,x,1); /* y == 2 */
+ YCF_YIELD();
+ sub_fun_2();
+ YCF_YIELD();
+ y = sub_fun_3(y, 1); /* y == 3 */
+ YCF_YIELD();
+ return y + z;
+}
+
+void* allocator(size_t size, void* context){
+ (void)context;
+ return malloc(size);
+}
+
+void freer(void* data, void* context){
+ (void)context;
+ free(data);
+}
+
+int main( int argc, const char* argv[] )
+{
+#ifdef YCF_YIELD_CODE_GENERATED
+ void* wb = NULL;
+#endif
+ int ret = 0;
+ long nr_of_reductions = 1;
+#ifdef YCF_YIELD_CODE_GENERATED
+ do{
+ ret = fun_ycf_gen_yielding(&nr_of_reductions,&wb,NULL,allocator,freer,NULL,0,NULL,1);
+ if(wb != NULL){
+ printf("TRAPPED\n");
+ }
+ }while(wb != NULL);
+ if(wb != NULL){
+ free(wb);
+ }
+#else
+ fun(1);
+#endif
+ printf("RETURNED %d\n", ret);
+ if(ret != 13){
+ return 1;
+ }else{
+ return 0;
+ }
+}
diff --git a/erts/lib_src/yielding_c_fun/test/examples/simple_yielding_fun_call.c.out b/erts/lib_src/yielding_c_fun/test/examples/simple_yielding_fun_call.c.out
new file mode 100644
index 0000000000..cc807db6c3
--- /dev/null
+++ b/erts/lib_src/yielding_c_fun/test/examples/simple_yielding_fun_call.c.out
@@ -0,0 +1,18 @@
+sub_fun_1_1
+TRAPPED
+sub_fun_1_2
+TRAPPED
+sub_fun_1_3
+TRAPPED
+sub_fun_1_4
+TRAPPED
+sub_fun_2_1
+TRAPPED
+sub_fun_2_2
+TRAPPED
+sub_fun_2_1
+TRAPPED
+TRAPPED
+sub_fun_2_2
+TRAPPED
+RETURNED 13
diff --git a/erts/lib_src/yielding_c_fun/test/examples/stack_array.c b/erts/lib_src/yielding_c_fun/test/examples/stack_array.c
new file mode 100644
index 0000000000..9f251749d6
--- /dev/null
+++ b/erts/lib_src/yielding_c_fun/test/examples/stack_array.c
@@ -0,0 +1,135 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB and Kjell Winblad 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%
+ */
+
+/*
+ * Description:
+ *
+ * Author: Kjell Winblad
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+
+#define YCF_YIELD()
+
+
+void fun(int x3content[]){
+ int x1[2];
+ int x2[2][2];
+ int *x3[2];
+ int z = 42;
+ (void)z;
+ x1[0] = 1;
+ x1[1] = 2;
+ x2[0][0] = 3;
+ x2[0][1] = 4;
+ x2[1][0] = 5;
+ x2[1][1] = 6;
+ x3[0] = x3content;
+ x3[1] = x3content;
+ x3[0][0] = 7;
+ x3[0][1] = 8;
+ x3[1][0] = 9;
+ x3[1][1] = 10;
+ printf("%d %d %d %d %d %d %d %d %d %d\n",
+ x1[0],
+ x1[1],
+ x2[0][0],
+ x2[0][1],
+ x2[1][0],
+ x2[1][1],
+ x3[0][0],
+ x3[0][1],
+ x3[1][0],
+ x3[1][1]);
+ YCF_YIELD();
+ printf("%d %d %d %d %d %d %d %d %d %d\n",
+ x1[0],
+ x1[1],
+ x2[0][0],
+ x2[0][1],
+ x2[1][0],
+ x2[1][1],
+ x3[0][0],
+ x3[0][1],
+ x3[1][0],
+ x3[1][1]);
+ return;
+}
+
+void fun_reset(int x3content[]){
+ int x1[2];
+ int x2[2][2];
+ int *x3[2];
+ (void)x2;
+ (void)x1;
+ x1[0] = 42;
+ x1[1] = 42;
+ x2[0][0] = 42;
+ x2[0][1] = 42;
+ x2[1][0] = 42;
+ x2[1][1] = 42;
+ x3[0] = x3content;
+ x3[1] = x3content;
+ x3[0][0] = 42;
+ x3[0][1] = 42;
+ x3[1][0] = 42;
+ x3[1][1] = 42;
+ return;
+}
+
+void* allocator(size_t size, void* context){
+ (void)context;
+ return malloc(size);
+}
+
+void freer(void* data, void* context){
+ (void)context;
+ free(data);
+}
+
+int main( int argc, const char* argv[] )
+{
+#ifdef YCF_YIELD_CODE_GENERATED
+ void* wb = NULL;
+#endif
+ int x3content[2];
+ long nr_of_reductions = 1;
+#ifdef YCF_YIELD_CODE_GENERATED
+ do{
+ fun_ycf_gen_yielding(&nr_of_reductions,&wb,NULL,allocator,freer,NULL,0,NULL, x3content);
+ fun_reset(x3content);
+ if(wb != NULL){
+ printf("TRAPPED\n");
+ }
+ }while(wb != NULL);
+ if(wb != NULL){
+ free(wb);
+ }
+#else
+ fun(x3content);
+#endif
+ printf("RETURNED\n");
+ return 0;
+}
+
diff --git a/erts/lib_src/yielding_c_fun/test/examples/stack_array.c.out b/erts/lib_src/yielding_c_fun/test/examples/stack_array.c.out
new file mode 100644
index 0000000000..c5cd129186
--- /dev/null
+++ b/erts/lib_src/yielding_c_fun/test/examples/stack_array.c.out
@@ -0,0 +1,4 @@
+1 2 3 4 5 6 9 10 9 10
+TRAPPED
+1 2 3 4 5 6 42 42 42 42
+RETURNED
diff --git a/erts/lib_src/yielding_c_fun/test/examples/static_inline_function.c b/erts/lib_src/yielding_c_fun/test/examples/static_inline_function.c
new file mode 100644
index 0000000000..146364d936
--- /dev/null
+++ b/erts/lib_src/yielding_c_fun/test/examples/static_inline_function.c
@@ -0,0 +1,71 @@
+#include <stdio.h>
+#include <stdlib.h>
+
+#if __STDC_VERSION__ >= 199901L
+
+#else
+#define inline
+#endif
+
+#define YCF_YIELD()
+
+static
+inline
+int fun(char x);
+
+
+static
+inline
+int fun(char x){
+ x = x + 1; /* x == 2*/
+ {
+ int y;
+ y = 1;
+ {
+ int z = 1;
+ YCF_YIELD();
+ x = x + y + z; /* x == 4*/
+ return x;
+ }
+ }
+}
+
+void* allocator(size_t size, void* context){
+ (void)context;
+ return malloc(size);
+}
+
+void freer(void* data, void* context){
+ (void)context;
+ free(data);
+}
+
+int main( int argc, const char* argv[] )
+{
+#ifdef YCF_YIELD_CODE_GENERATED
+ void* wb = NULL;
+ long nr_of_reductions = 1;
+#endif
+ int ret = 0;
+ (void)fun;
+#ifdef YCF_YIELD_CODE_GENERATED
+ do{
+ ret = fun_ycf_gen_yielding(&nr_of_reductions,&wb,NULL,allocator,freer,NULL,0,NULL,1);
+ if(wb != NULL){
+ printf("TRAPPED\n");
+ }
+ }while(wb != NULL);
+ if(wb != NULL){
+ free(wb);
+ }
+#else
+ fun(1);
+#endif
+ printf("RETURNED %d\n", ret);
+ if(ret != 4){
+ return 1;
+ }else{
+ return 0;
+ }
+}
+
diff --git a/erts/lib_src/yielding_c_fun/test/examples/static_inline_function.c.out b/erts/lib_src/yielding_c_fun/test/examples/static_inline_function.c.out
new file mode 100644
index 0000000000..cf0b7ff2c6
--- /dev/null
+++ b/erts/lib_src/yielding_c_fun/test/examples/static_inline_function.c.out
@@ -0,0 +1,2 @@
+TRAPPED
+RETURNED 4
diff --git a/erts/lib_src/yielding_c_fun/test/examples/test_generated_header_file_code.c b/erts/lib_src/yielding_c_fun/test/examples/test_generated_header_file_code.c
new file mode 100644
index 0000000000..fd1240c180
--- /dev/null
+++ b/erts/lib_src/yielding_c_fun/test/examples/test_generated_header_file_code.c
@@ -0,0 +1,66 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB and Kjell Winblad 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%
+ */
+
+/*
+ * Description:
+ *
+ * Author: Kjell Winblad
+ *
+ */
+
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#define YCF_YIELD()
+
+int A(int depth);
+int B(int depth);
+
+int A(int depth){
+ int b;
+ YCF_YIELD();
+ depth++;
+ printf("A ");
+ YCF_YIELD();
+ if(depth == 100){
+ return 1;
+ } else {
+ b = B(depth);
+ }
+ YCF_YIELD();
+ return b + 1;
+}
+
+int B(int depth){
+ int a;
+ YCF_YIELD();
+ depth++;
+ printf("B ");
+ YCF_YIELD();
+ if(depth == 100){
+ YCF_YIELD();
+ return 1;
+ } else {
+ a = A(depth);
+ }
+ YCF_YIELD();
+ return a + 1;
+}
diff --git a/erts/lib_src/yielding_c_fun/test/examples/test_generated_header_file_main.c b/erts/lib_src/yielding_c_fun/test/examples/test_generated_header_file_main.c
new file mode 100644
index 0000000000..b8d377f724
--- /dev/null
+++ b/erts/lib_src/yielding_c_fun/test/examples/test_generated_header_file_main.c
@@ -0,0 +1,71 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB and Kjell Winblad 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%
+ */
+
+/*
+ * Description:
+ *
+ * Author: Kjell Winblad
+ *
+ */
+
+#ifdef YCF_YIELD_CODE_GENERATED
+#include "tmp_dir/tmp.h"
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+
+int A(int depth);
+int B(int depth);
+
+void* allocator(size_t size, void* context){
+ (void)context;
+ return malloc(size);
+}
+
+void freer(void* data, void* context){
+ (void)context;
+ free(data);
+}
+
+int main( int argc, const char* argv[] )
+{
+#ifdef YCF_YIELD_CODE_GENERATED
+ void* wb = NULL;
+#endif
+ int ret = 0;
+ long nr_of_reductions = 1;
+#ifdef YCF_YIELD_CODE_GENERATED
+ do{
+ ret = A_ycf_gen_yielding(&nr_of_reductions,&wb,NULL,allocator,freer,NULL,0,NULL,0);
+ if(wb != NULL){
+ printf("TRAPPED\n");
+ }
+ }while(wb != NULL);
+#else
+ ret = A(0);
+#endif
+ printf("RETURNED %d\n", ret);
+ if(ret != A(0)){
+ return 1;
+ }else{
+ return 0;
+ }
+}
diff --git a/erts/lib_src/yielding_c_fun/test/examples/test_generated_header_file_main.c.out b/erts/lib_src/yielding_c_fun/test/examples/test_generated_header_file_main.c.out
new file mode 100644
index 0000000000..c41fe372ae
--- /dev/null
+++ b/erts/lib_src/yielding_c_fun/test/examples/test_generated_header_file_main.c.out
@@ -0,0 +1,302 @@
+TRAPPED
+A TRAPPED
+TRAPPED
+B TRAPPED
+TRAPPED
+A TRAPPED
+TRAPPED
+B TRAPPED
+TRAPPED
+A TRAPPED
+TRAPPED
+B TRAPPED
+TRAPPED
+A TRAPPED
+TRAPPED
+B TRAPPED
+TRAPPED
+A TRAPPED
+TRAPPED
+B TRAPPED
+TRAPPED
+A TRAPPED
+TRAPPED
+B TRAPPED
+TRAPPED
+A TRAPPED
+TRAPPED
+B TRAPPED
+TRAPPED
+A TRAPPED
+TRAPPED
+B TRAPPED
+TRAPPED
+A TRAPPED
+TRAPPED
+B TRAPPED
+TRAPPED
+A TRAPPED
+TRAPPED
+B TRAPPED
+TRAPPED
+A TRAPPED
+TRAPPED
+B TRAPPED
+TRAPPED
+A TRAPPED
+TRAPPED
+B TRAPPED
+TRAPPED
+A TRAPPED
+TRAPPED
+B TRAPPED
+TRAPPED
+A TRAPPED
+TRAPPED
+B TRAPPED
+TRAPPED
+A TRAPPED
+TRAPPED
+B TRAPPED
+TRAPPED
+A TRAPPED
+TRAPPED
+B TRAPPED
+TRAPPED
+A TRAPPED
+TRAPPED
+B TRAPPED
+TRAPPED
+A TRAPPED
+TRAPPED
+B TRAPPED
+TRAPPED
+A TRAPPED
+TRAPPED
+B TRAPPED
+TRAPPED
+A TRAPPED
+TRAPPED
+B TRAPPED
+TRAPPED
+A TRAPPED
+TRAPPED
+B TRAPPED
+TRAPPED
+A TRAPPED
+TRAPPED
+B TRAPPED
+TRAPPED
+A TRAPPED
+TRAPPED
+B TRAPPED
+TRAPPED
+A TRAPPED
+TRAPPED
+B TRAPPED
+TRAPPED
+A TRAPPED
+TRAPPED
+B TRAPPED
+TRAPPED
+A TRAPPED
+TRAPPED
+B TRAPPED
+TRAPPED
+A TRAPPED
+TRAPPED
+B TRAPPED
+TRAPPED
+A TRAPPED
+TRAPPED
+B TRAPPED
+TRAPPED
+A TRAPPED
+TRAPPED
+B TRAPPED
+TRAPPED
+A TRAPPED
+TRAPPED
+B TRAPPED
+TRAPPED
+A TRAPPED
+TRAPPED
+B TRAPPED
+TRAPPED
+A TRAPPED
+TRAPPED
+B TRAPPED
+TRAPPED
+A TRAPPED
+TRAPPED
+B TRAPPED
+TRAPPED
+A TRAPPED
+TRAPPED
+B TRAPPED
+TRAPPED
+A TRAPPED
+TRAPPED
+B TRAPPED
+TRAPPED
+A TRAPPED
+TRAPPED
+B TRAPPED
+TRAPPED
+A TRAPPED
+TRAPPED
+B TRAPPED
+TRAPPED
+A TRAPPED
+TRAPPED
+B TRAPPED
+TRAPPED
+A TRAPPED
+TRAPPED
+B TRAPPED
+TRAPPED
+A TRAPPED
+TRAPPED
+B TRAPPED
+TRAPPED
+A TRAPPED
+TRAPPED
+B TRAPPED
+TRAPPED
+A TRAPPED
+TRAPPED
+B TRAPPED
+TRAPPED
+A TRAPPED
+TRAPPED
+B TRAPPED
+TRAPPED
+A TRAPPED
+TRAPPED
+B TRAPPED
+TRAPPED
+A TRAPPED
+TRAPPED
+B TRAPPED
+TRAPPED
+A TRAPPED
+TRAPPED
+B TRAPPED
+TRAPPED
+A TRAPPED
+TRAPPED
+B TRAPPED
+TRAPPED
+A TRAPPED
+TRAPPED
+B TRAPPED
+TRAPPED
+A TRAPPED
+TRAPPED
+B TRAPPED
+TRAPPED
+A TRAPPED
+TRAPPED
+B TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+RETURNED 100
+A B A B A B A B A B A B A B A B A B A B A B A B A B A B A B A B A B A B A B A B A B A B A B A B A B A B A B A B A B A B A B A B A B A B A B A B A B A B A B A B A B A B A B A B A B A B A B A B A B A B \ No newline at end of file
diff --git a/erts/lib_src/yielding_c_fun/test/examples/test_only_output_yielding_funs.c b/erts/lib_src/yielding_c_fun/test/examples/test_only_output_yielding_funs.c
new file mode 100644
index 0000000000..04e071e94b
--- /dev/null
+++ b/erts/lib_src/yielding_c_fun/test/examples/test_only_output_yielding_funs.c
@@ -0,0 +1,60 @@
+#include <stdio.h>
+#include <stdlib.h>
+
+#define YCF_YIELD()
+
+int empty_fun(){
+ return 1;
+}
+
+#include "tmp.inc"
+
+static int fun(char x){
+ x = x + 1; /* x == 2*/
+ YCF_YIELD();
+ x = x + 1; /* x == 3*/
+ {
+ int hej = !!empty_fun();
+ return x + hej;
+ }
+}
+
+void* allocator(size_t size, void* context){
+ (void)context;
+ return malloc(size);
+}
+
+void freer(void* data, void* context){
+ (void)context;
+ free(data);
+}
+
+int main( int argc, const char* argv[] )
+{
+#ifdef YCF_YIELD_CODE_GENERATED
+ void* wb = NULL;
+ long nr_of_reductions = 1;
+#endif
+ int ret = 0;
+ (void)fun;
+#ifdef YCF_YIELD_CODE_GENERATED
+ do{
+ ret = fun_ycf_gen_yielding(&nr_of_reductions,&wb,NULL,allocator,freer,NULL,0,NULL,1);
+ if(wb != NULL){
+ printf("TRAPPED\n");
+ }
+ }while(wb != NULL);
+ if(wb != NULL){
+ free(wb);
+ }
+#else
+ fun(1);
+#endif
+ printf("RETURNED %d\n", ret);
+ if(ret != 4){
+ return 1;
+ }else{
+ return 0;
+ }
+}
+
diff --git a/erts/lib_src/yielding_c_fun/test/examples/test_only_output_yielding_funs.c.out b/erts/lib_src/yielding_c_fun/test/examples/test_only_output_yielding_funs.c.out
new file mode 100644
index 0000000000..cf0b7ff2c6
--- /dev/null
+++ b/erts/lib_src/yielding_c_fun/test/examples/test_only_output_yielding_funs.c.out
@@ -0,0 +1,2 @@
+TRAPPED
+RETURNED 4
diff --git a/erts/lib_src/yielding_c_fun/test/examples/test_trap.c b/erts/lib_src/yielding_c_fun/test/examples/test_trap.c
new file mode 100644
index 0000000000..7799de618e
--- /dev/null
+++ b/erts/lib_src/yielding_c_fun/test/examples/test_trap.c
@@ -0,0 +1,42 @@
+#include <stdio.h>
+#include <stdlib.h>
+
+#define YCF_YIELD()
+
+
+int fun(char x){
+ int y = 10;
+ int i = 0;
+ printf("BEFORE YCF_YIELD()\n");
+ y = y + x;/*y == 11*/
+ for (i = 0; i < 100; i++){
+ printf("ITER %d\n", i);
+ y = y + x;
+ YCF_YIELD();
+ }
+ printf("AFTER YCF_YIELD()\n");/*y == 111*/
+ y = y*3;
+ return y;/*y == 333*/
+}
+
+int main( int argc, const char* argv[] )
+{
+#ifdef YCF_YIELD_CODE_GENERATED
+ void* wb = NULL;
+#endif
+ int ret = 0;
+#ifdef YCF_YIELD_CODE_GENERATED
+ do{
+ ret = fun(1,&wb,1);
+ if(wb != NULL){
+ printf("TRAPPED\n");
+ }
+ }while(wb != NULL);
+#else
+ fun(1);
+#endif
+ printf("RETURNED %d\n", ret);
+ return 0;
+}
+
+
diff --git a/erts/lib_src/yielding_c_fun/test/examples/thread_example.c b/erts/lib_src/yielding_c_fun/test/examples/thread_example.c
new file mode 100644
index 0000000000..875e9fe990
--- /dev/null
+++ b/erts/lib_src/yielding_c_fun/test/examples/thread_example.c
@@ -0,0 +1,88 @@
+#include <stdio.h>
+#include <stdlib.h>
+
+#define YCF_YIELD_NO_REDS()
+
+static int f_2(char* name, int n) {
+ for(int y = 0; y < 2; y++) {
+ printf("%s f_2: y=%d\n", name, y);
+ }
+ return n*2;
+}
+
+static void f_1(char* name, int x) {
+ YCF_YIELD_NO_REDS();
+ while (x > 0) {
+ int f_2_ret = f_2(name, x);
+ printf("%s f_1: x=%d f_2_ret=%d\n", name, x, f_2_ret);
+ x--;
+ }
+ printf("%s f_1: DONE\n", name);
+}
+
+static void* ycf_alloc(size_t size, void* context){
+ (void)context;
+ return malloc(size);
+}
+
+static void ycf_free(void* data, void* context){
+ (void)context;
+ free(data);
+}
+
+int main( int argc, const char* argv[] ) {
+#ifdef YCF_YIELD_CODE_GENERATED
+ long t1_nr_of_reds = 1;
+ void* t1_state = NULL;
+ long t2_nr_of_reds = 2;
+ void* t2_state = NULL;
+ long t3_nr_of_reds = 1000;
+ void* t3_state = NULL;
+ /* Start t1, t2 and t3*/
+ f_1_ycf_gen_yielding(&t1_nr_of_reds, &t1_state, NULL,
+ ycf_alloc, ycf_free, NULL, 0, NULL,
+ "t1", 2);
+ f_1_ycf_gen_yielding(&t2_nr_of_reds, &t2_state, NULL,
+ ycf_alloc, ycf_free, NULL, 0, NULL,
+ "t2", 4);
+ f_1_ycf_gen_yielding(&t3_nr_of_reds, &t3_state, NULL,
+ ycf_alloc, ycf_free, NULL, 0, NULL,
+ "t3", 2);
+ printf("THREADS STARTED\n");
+ /* Execute t1, t2 and t3*/
+ while (t1_state != NULL ||
+ t2_state != NULL ||
+ t3_state != NULL) {
+ t1_nr_of_reds = 1;
+ t2_nr_of_reds = 2;
+ if (t1_state != NULL) {
+ printf("SCHEDULING THREAD: t1\n");
+ f_1_ycf_gen_continue(&t1_nr_of_reds, &t1_state, NULL);
+ if (t1_state == NULL) {
+ printf("THREAD t1 DONE (number of reductions left = %ld)\n",
+ t1_nr_of_reds);
+ }
+ }
+ if (t2_state != NULL) {
+ printf("SCHEDULING THREAD: t2\n");
+ f_1_ycf_gen_continue(&t2_nr_of_reds, &t2_state, NULL);
+ if (t2_state == NULL) {
+ printf("THREAD t2 DONE (number of reductions left = %ld)\n",
+ t2_nr_of_reds);
+ }
+ }
+ if (t3_state != NULL) {
+ printf("SCHEDULING THREAD: t3\n");
+ f_1_ycf_gen_continue(&t3_nr_of_reds, &t3_state, NULL);
+ if (t3_state == NULL) {
+ printf("THREAD t3 DONE (number of reductions left = %ld)\n",
+ t3_nr_of_reds);
+ }
+ }
+ }
+#endif
+ (void)f_1;
+ printf("DONE\n");
+ return 0;
+}
+
diff --git a/erts/lib_src/yielding_c_fun/test/examples/thread_example.c.out b/erts/lib_src/yielding_c_fun/test/examples/thread_example.c.out
new file mode 100644
index 0000000000..015f2204ea
--- /dev/null
+++ b/erts/lib_src/yielding_c_fun/test/examples/thread_example.c.out
@@ -0,0 +1,47 @@
+THREADS STARTED
+SCHEDULING THREAD: t1
+SCHEDULING THREAD: t2
+SCHEDULING THREAD: t3
+t3 f_2: y=0
+t3 f_2: y=1
+t3 f_1: x=2 f_2_ret=4
+t3 f_2: y=0
+t3 f_2: y=1
+t3 f_1: x=1 f_2_ret=2
+t3 f_1: DONE
+THREAD t3 DONE (number of reductions left = 994)
+SCHEDULING THREAD: t1
+SCHEDULING THREAD: t2
+t2 f_2: y=0
+t2 f_2: y=1
+t2 f_1: x=4 f_2_ret=8
+SCHEDULING THREAD: t1
+t1 f_2: y=0
+SCHEDULING THREAD: t2
+t2 f_2: y=0
+SCHEDULING THREAD: t1
+t1 f_2: y=1
+t1 f_1: x=2 f_2_ret=4
+SCHEDULING THREAD: t2
+t2 f_2: y=1
+t2 f_1: x=3 f_2_ret=6
+SCHEDULING THREAD: t1
+SCHEDULING THREAD: t2
+t2 f_2: y=0
+t2 f_2: y=1
+t2 f_1: x=2 f_2_ret=4
+SCHEDULING THREAD: t1
+t1 f_2: y=0
+SCHEDULING THREAD: t2
+t2 f_2: y=0
+SCHEDULING THREAD: t1
+t1 f_2: y=1
+t1 f_1: x=1 f_2_ret=2
+t1 f_1: DONE
+THREAD t1 DONE (number of reductions left = 1)
+SCHEDULING THREAD: t2
+t2 f_2: y=1
+t2 f_1: x=1 f_2_ret=2
+t2 f_1: DONE
+THREAD t2 DONE (number of reductions left = 2)
+DONE
diff --git a/erts/lib_src/yielding_c_fun/test/examples/void_param.c b/erts/lib_src/yielding_c_fun/test/examples/void_param.c
new file mode 100644
index 0000000000..f5e089b27a
--- /dev/null
+++ b/erts/lib_src/yielding_c_fun/test/examples/void_param.c
@@ -0,0 +1,79 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB and Kjell Winblad 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%
+ */
+
+/*
+ * Description:
+ *
+ * Author: Kjell Winblad
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#define YCF_YIELD()
+
+
+int fun(void){
+ int z = 1;
+ char x = z + 1; /* x == 2*/
+ YCF_YIELD();
+ x = x + 1; /* x == 3*/
+ return x;
+}
+
+void* allocator(size_t size, void* context){
+ (void)context;
+ return malloc(size);
+}
+
+void freer(void* data, void* context){
+ (void)context;
+ free(data);
+}
+
+int main( int argc, const char* argv[] )
+{
+#ifdef YCF_YIELD_CODE_GENERATED
+ void* wb = NULL;
+#endif
+ int ret = 0;
+ long nr_of_reductions = 1;
+#ifdef YCF_YIELD_CODE_GENERATED
+ do{
+ ret = fun_ycf_gen_yielding(&nr_of_reductions,&wb,NULL,allocator,freer,NULL,0,NULL);
+ if(wb != NULL){
+ printf("TRAPPED\n");
+ }
+ }while(wb != NULL);
+ if(wb != NULL){
+ free(wb);
+ }
+#else
+ fun();
+#endif
+ printf("RETURNED %d\n", ret);
+ if(ret != 3){
+ return 1;
+ }else{
+ return 0;
+ }
+}
+
diff --git a/erts/lib_src/yielding_c_fun/test/examples/void_param.c.out b/erts/lib_src/yielding_c_fun/test/examples/void_param.c.out
new file mode 100644
index 0000000000..96f574d048
--- /dev/null
+++ b/erts/lib_src/yielding_c_fun/test/examples/void_param.c.out
@@ -0,0 +1,2 @@
+TRAPPED
+RETURNED 3
diff --git a/erts/lib_src/yielding_c_fun/test/examples/void_ret_fun.c b/erts/lib_src/yielding_c_fun/test/examples/void_ret_fun.c
new file mode 100644
index 0000000000..10dd51bfae
--- /dev/null
+++ b/erts/lib_src/yielding_c_fun/test/examples/void_ret_fun.c
@@ -0,0 +1,85 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB and Kjell Winblad 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%
+ */
+
+/*
+ * Description:
+ *
+ * Author: Kjell Winblad
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#define YCF_YIELD()
+
+
+void fun(int* y){
+ int x = 1;
+ x = x + 1; /* x == 2*/
+ YCF_YIELD();
+ x = x + 1; /* x == 3*/
+ *y = x;
+}
+
+void* allocator(size_t size, void* context){
+ (void)context;
+ return malloc(size);
+}
+
+void freer(void* data, void* context){
+ (void)context;
+ free(data);
+}
+
+int main( int argc, const char* argv[] )
+{
+#ifdef YCF_YIELD_CODE_GENERATED
+ void* wb = NULL;
+#endif
+ int nr_of_trapps = 0;
+ int ret = 0;
+ long nr_of_reductions = 1;
+#ifdef YCF_YIELD_CODE_GENERATED
+ do{
+ fun_ycf_gen_yielding(&nr_of_reductions,&wb,NULL,allocator,freer,NULL,0,NULL,&ret);
+ if(wb != NULL){
+ printf("TRAP\n");
+ nr_of_trapps++;
+ }
+ }while(wb != NULL);
+ if(wb != NULL){
+ free(wb);
+ }
+ if(nr_of_trapps != 1){
+ printf("ERROR\n");
+ exit(1);
+ }
+#else
+ fun(&ret);
+#endif
+ printf("RETURNED %d\n", ret);
+ if(ret != 3){
+ printf("ERROR\n");
+ exit(1);
+ }
+ return 0;
+}
+
diff --git a/erts/lib_src/yielding_c_fun/test/examples/void_ret_fun.c.out b/erts/lib_src/yielding_c_fun/test/examples/void_ret_fun.c.out
new file mode 100644
index 0000000000..4b85bbb30e
--- /dev/null
+++ b/erts/lib_src/yielding_c_fun/test/examples/void_ret_fun.c.out
@@ -0,0 +1,2 @@
+TRAP
+RETURNED 3
diff --git a/erts/lib_src/yielding_c_fun/test/examples/ycf_stack_alloc.c b/erts/lib_src/yielding_c_fun/test/examples/ycf_stack_alloc.c
new file mode 100644
index 0000000000..20fe2a444d
--- /dev/null
+++ b/erts/lib_src/yielding_c_fun/test/examples/ycf_stack_alloc.c
@@ -0,0 +1,126 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB and Kjell Winblad 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%
+ */
+
+/*
+ * Description:
+ *
+ * Author: Kjell Winblad
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#define YCF_YIELD()
+#define YCF_CONSUME_REDS(X)
+#ifndef YCF_YIELD_CODE_GENERATED
+#define YCF_STACK_ALLOC(X) malloc(1)
+#endif
+
+int sub_fun(int x){
+ int i;
+ int count = 0;
+ char* my_data = YCF_STACK_ALLOC(5);
+ my_data[0] = 's';
+ my_data[1] = 'u';
+ my_data[2] = 'b';
+ my_data[3] = '\n';
+ my_data[4] = '\0';
+ for(i = 0; i < 2; i++){
+ count = count + 2;
+ YCF_YIELD();
+ printf("%s", my_data);
+ }
+ return count + x;
+}
+
+int fun(int x){
+ int ret;
+ char* my_data = YCF_STACK_ALLOC(5);
+ char* my_data2 = YCF_STACK_ALLOC(5);
+ char* my_data3;
+ my_data[0] = 'h';
+ my_data[1] = 'e';
+ my_data[2] = 'j';
+ my_data[3] = '\n';
+ my_data[4] = '\0';
+ my_data2[0] = 's';
+ my_data2[1] = 'o';
+ my_data2[2] = 's';
+ my_data2[3] = '\n';
+ my_data2[4] = '\0';
+ YCF_YIELD();
+ printf("%s", my_data);
+ printf("%s", my_data2);
+ ret = sub_fun(x);
+ ret = sub_fun(x);
+ my_data3 = YCF_STACK_ALLOC(5);
+ printf("BEFORE OVERWRITE %s", my_data3);
+ my_data3[0] = 'e';
+ my_data3[1] = 'r';
+ my_data3[2] = 'l';
+ my_data3[3] = '\n';
+ my_data3[4] = '\0';
+ printf("AFTER OVERWRITE %s", my_data3);
+ return ret;
+}
+
+void* allocator(size_t size, void* context){
+ (void)context;
+ return malloc(size);
+}
+
+void freer(void* data, void* context){
+ (void)context;
+ free(data);
+}
+
+int main( int argc, const char* argv[] )
+{
+#ifdef YCF_YIELD_CODE_GENERATED
+ void* wb = NULL;
+#endif
+ int ret = 0;
+ int nr_of_yields = 0;
+ long nr_of_reductions;
+#ifdef YCF_YIELD_CODE_GENERATED
+ do{
+ nr_of_reductions = 101;
+ ret = fun_ycf_gen_yielding(&nr_of_reductions,&wb,NULL,allocator,freer,NULL,15,NULL,1);
+ if(wb != NULL){
+ printf("TRAPPED %ld\n", nr_of_reductions);
+ nr_of_yields++;
+ }
+ }while(wb != NULL);
+ if(wb != NULL){
+ free(wb);
+ }
+#else
+ ret = fun(1);
+#endif
+ printf("Number of yields %d\n", nr_of_yields);
+ printf("RETURNED %d\n", ret);
+ if(ret != 5){
+ return 1;
+ }else{
+ return 0;
+ }
+}
+
diff --git a/erts/lib_src/yielding_c_fun/test/examples/ycf_stack_alloc.c.out b/erts/lib_src/yielding_c_fun/test/examples/ycf_stack_alloc.c.out
new file mode 100644
index 0000000000..8b3c31c9e5
--- /dev/null
+++ b/erts/lib_src/yielding_c_fun/test/examples/ycf_stack_alloc.c.out
@@ -0,0 +1,15 @@
+TRAPPED 0
+hej
+sos
+TRAPPED 0
+sub
+TRAPPED 0
+sub
+TRAPPED 0
+sub
+TRAPPED 0
+sub
+BEFORE OVERWRITE sub
+AFTER OVERWRITE erl
+Number of yields 5
+RETURNED 5
diff --git a/erts/lib_src/yielding_c_fun/test/examples/yield_with_struct.c b/erts/lib_src/yielding_c_fun/test/examples/yield_with_struct.c
new file mode 100644
index 0000000000..3506d83499
--- /dev/null
+++ b/erts/lib_src/yielding_c_fun/test/examples/yield_with_struct.c
@@ -0,0 +1,67 @@
+#include <stdio.h>
+#include <stdlib.h>
+
+typedef struct my_struct {
+ struct my_struct* my_struct;
+ int len;
+} my_struct;
+
+
+#define YCF_YIELD()
+
+int fun(char x){
+ my_struct s;
+ my_struct* sp;
+ int len = 55;
+ s.len = 10;
+ s.my_struct = NULL;
+ sp = malloc(sizeof(my_struct));
+ sp->len = 10;
+ sp->my_struct = NULL;
+ YCF_YIELD();
+ return
+ s.len == 10 &&
+ s.my_struct == NULL &&
+ sp->len == 10 &&
+ sp->my_struct == NULL &&
+ len == 55;
+}
+
+void* allocator(size_t size, void* context){
+ (void)context;
+ return malloc(size);
+}
+
+void freer(void* data, void* context){
+ (void)context;
+ free(data);
+}
+
+int main( int argc, const char* argv[] )
+{
+#ifdef YCF_YIELD_CODE_GENERATED
+ void* wb = NULL;
+ long nr_of_reductions = 1;
+#endif
+ int ret = 0;
+#ifdef YCF_YIELD_CODE_GENERATED
+ do{
+ ret = fun_ycf_gen_yielding(&nr_of_reductions,&wb,NULL,allocator,freer,NULL,0,NULL,1);
+ if(wb != NULL){
+ printf("TRAPPED\n");
+ }
+ }while(wb != NULL);
+ if(wb != NULL){
+ free(wb);
+ }
+#else
+ fun(1);
+#endif
+ printf("RETURNED %d\n", ret);
+ if(ret != 1){
+ return 1;
+ }else{
+ return 0;
+ }
+}
+
diff --git a/erts/lib_src/yielding_c_fun/test/examples/yield_with_struct.c.out b/erts/lib_src/yielding_c_fun/test/examples/yield_with_struct.c.out
new file mode 100644
index 0000000000..7c3cfb5078
--- /dev/null
+++ b/erts/lib_src/yielding_c_fun/test/examples/yield_with_struct.c.out
@@ -0,0 +1,2 @@
+TRAPPED
+RETURNED 1
diff --git a/erts/lib_src/yielding_c_fun/test/examples/yielding_fun_in_control.c b/erts/lib_src/yielding_c_fun/test/examples/yielding_fun_in_control.c
new file mode 100644
index 0000000000..c0d7ac6c13
--- /dev/null
+++ b/erts/lib_src/yielding_c_fun/test/examples/yielding_fun_in_control.c
@@ -0,0 +1,68 @@
+#include <stdio.h>
+#include <stdlib.h>
+
+#define YCF_YIELD()
+
+int sub_fun(char x){
+ YCF_YIELD();
+ x = x + 1; /* x == 1*/
+ return (x == 10 ? 0 : 1);
+}
+
+
+int fun(char x){
+ if(sub_fun(0)){
+ printf("hej\n");
+ }
+ if(0) printf("NO");
+ else if(sub_fun(0)){
+ printf("hej 2\n");
+ }
+ if(sub_fun(10)){
+ printf("hej 3\n");
+ } else {
+ printf("HEJ\n");
+ }
+ while(sub_fun(9)){
+ printf("hoo\n");
+ }
+ do{
+ printf("haa\n");
+ }while(sub_fun(9));
+ return x;
+}
+
+void* allocator(size_t size, void* context){
+ (void)context;
+ return malloc(size);
+}
+
+void freer(void* data, void* context){
+ (void)context;
+ free(data);
+}
+
+int main( int argc, const char* argv[] )
+{
+#ifdef YCF_YIELD_CODE_GENERATED
+ void* wb = NULL;
+ long nr_of_reductions = 1;
+#endif
+ int ret = 0;
+#ifdef YCF_YIELD_CODE_GENERATED
+ do{
+ ret = fun_ycf_gen_yielding(&nr_of_reductions,&wb,NULL,allocator,freer,NULL,0,NULL,1);
+ if(wb != NULL){
+ printf("TRAPPED\n");
+ }
+ }while(wb != NULL);
+ if(wb != NULL){
+ free(wb);
+ }
+#else
+ fun(1);
+#endif
+ printf("RETURNED %d\n", ret);
+ return 0;
+}
+
diff --git a/erts/lib_src/yielding_c_fun/test/examples/yielding_fun_in_control.c.out b/erts/lib_src/yielding_c_fun/test/examples/yielding_fun_in_control.c.out
new file mode 100644
index 0000000000..11b01b7d63
--- /dev/null
+++ b/erts/lib_src/yielding_c_fun/test/examples/yielding_fun_in_control.c.out
@@ -0,0 +1,10 @@
+TRAPPED
+hej
+TRAPPED
+hej 2
+TRAPPED
+hej 3
+TRAPPED
+haa
+TRAPPED
+RETURNED 1
diff --git a/erts/lib_src/yielding_c_fun/test/examples/yielding_mutual_recursion.c b/erts/lib_src/yielding_c_fun/test/examples/yielding_mutual_recursion.c
new file mode 100644
index 0000000000..655c94e956
--- /dev/null
+++ b/erts/lib_src/yielding_c_fun/test/examples/yielding_mutual_recursion.c
@@ -0,0 +1,104 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB and Kjell Winblad 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%
+ */
+
+/*
+ * Description:
+ *
+ * Author: Kjell Winblad
+ *
+ */
+
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#define YCF_YIELD()
+
+int A(int depth);
+int B(int depth);
+
+int A(int depth){
+ int b;
+ YCF_YIELD();
+ depth++;
+ printf("A ");
+ YCF_YIELD();
+ if(depth == 100){
+ return 1;
+ } else {
+ b = B(depth);
+ }
+ YCF_YIELD();
+ return b + 1;
+}
+
+int B(int depth){
+ int a;
+ YCF_YIELD();
+ depth++;
+ printf("B ");
+ YCF_YIELD();
+ if(depth == 100){
+ YCF_YIELD();
+ return 1;
+ } else {
+ a = A(depth);
+ }
+ YCF_YIELD();
+ return a + 1;
+}
+
+void* allocator(size_t size, void* context){
+ (void)context;
+ return malloc(size);
+}
+
+void freer(void* data, void* context){
+ (void)context;
+ free(data);
+}
+
+int main( int argc, const char* argv[] )
+{
+#ifdef YCF_YIELD_CODE_GENERATED
+ void* wb = NULL;
+#endif
+ int ret = 0;
+ long nr_of_reductions = 1;
+#ifdef YCF_YIELD_CODE_GENERATED
+ do{
+ ret = A_ycf_gen_yielding(&nr_of_reductions,&wb,NULL,allocator,freer,NULL,0,NULL,0);
+ if(wb != NULL){
+ printf("TRAPPED\n");
+ }
+ }while(wb != NULL);
+ if(wb != NULL){
+ free(wb);
+ }
+#else
+ ret = A(0);
+#endif
+ printf("RETURNED %d\n", ret);
+ if(ret != A(0)){
+ return 1;
+ }else{
+ return 0;
+ }
+}
diff --git a/erts/lib_src/yielding_c_fun/test/examples/yielding_mutual_recursion.c.out b/erts/lib_src/yielding_c_fun/test/examples/yielding_mutual_recursion.c.out
new file mode 100644
index 0000000000..c41fe372ae
--- /dev/null
+++ b/erts/lib_src/yielding_c_fun/test/examples/yielding_mutual_recursion.c.out
@@ -0,0 +1,302 @@
+TRAPPED
+A TRAPPED
+TRAPPED
+B TRAPPED
+TRAPPED
+A TRAPPED
+TRAPPED
+B TRAPPED
+TRAPPED
+A TRAPPED
+TRAPPED
+B TRAPPED
+TRAPPED
+A TRAPPED
+TRAPPED
+B TRAPPED
+TRAPPED
+A TRAPPED
+TRAPPED
+B TRAPPED
+TRAPPED
+A TRAPPED
+TRAPPED
+B TRAPPED
+TRAPPED
+A TRAPPED
+TRAPPED
+B TRAPPED
+TRAPPED
+A TRAPPED
+TRAPPED
+B TRAPPED
+TRAPPED
+A TRAPPED
+TRAPPED
+B TRAPPED
+TRAPPED
+A TRAPPED
+TRAPPED
+B TRAPPED
+TRAPPED
+A TRAPPED
+TRAPPED
+B TRAPPED
+TRAPPED
+A TRAPPED
+TRAPPED
+B TRAPPED
+TRAPPED
+A TRAPPED
+TRAPPED
+B TRAPPED
+TRAPPED
+A TRAPPED
+TRAPPED
+B TRAPPED
+TRAPPED
+A TRAPPED
+TRAPPED
+B TRAPPED
+TRAPPED
+A TRAPPED
+TRAPPED
+B TRAPPED
+TRAPPED
+A TRAPPED
+TRAPPED
+B TRAPPED
+TRAPPED
+A TRAPPED
+TRAPPED
+B TRAPPED
+TRAPPED
+A TRAPPED
+TRAPPED
+B TRAPPED
+TRAPPED
+A TRAPPED
+TRAPPED
+B TRAPPED
+TRAPPED
+A TRAPPED
+TRAPPED
+B TRAPPED
+TRAPPED
+A TRAPPED
+TRAPPED
+B TRAPPED
+TRAPPED
+A TRAPPED
+TRAPPED
+B TRAPPED
+TRAPPED
+A TRAPPED
+TRAPPED
+B TRAPPED
+TRAPPED
+A TRAPPED
+TRAPPED
+B TRAPPED
+TRAPPED
+A TRAPPED
+TRAPPED
+B TRAPPED
+TRAPPED
+A TRAPPED
+TRAPPED
+B TRAPPED
+TRAPPED
+A TRAPPED
+TRAPPED
+B TRAPPED
+TRAPPED
+A TRAPPED
+TRAPPED
+B TRAPPED
+TRAPPED
+A TRAPPED
+TRAPPED
+B TRAPPED
+TRAPPED
+A TRAPPED
+TRAPPED
+B TRAPPED
+TRAPPED
+A TRAPPED
+TRAPPED
+B TRAPPED
+TRAPPED
+A TRAPPED
+TRAPPED
+B TRAPPED
+TRAPPED
+A TRAPPED
+TRAPPED
+B TRAPPED
+TRAPPED
+A TRAPPED
+TRAPPED
+B TRAPPED
+TRAPPED
+A TRAPPED
+TRAPPED
+B TRAPPED
+TRAPPED
+A TRAPPED
+TRAPPED
+B TRAPPED
+TRAPPED
+A TRAPPED
+TRAPPED
+B TRAPPED
+TRAPPED
+A TRAPPED
+TRAPPED
+B TRAPPED
+TRAPPED
+A TRAPPED
+TRAPPED
+B TRAPPED
+TRAPPED
+A TRAPPED
+TRAPPED
+B TRAPPED
+TRAPPED
+A TRAPPED
+TRAPPED
+B TRAPPED
+TRAPPED
+A TRAPPED
+TRAPPED
+B TRAPPED
+TRAPPED
+A TRAPPED
+TRAPPED
+B TRAPPED
+TRAPPED
+A TRAPPED
+TRAPPED
+B TRAPPED
+TRAPPED
+A TRAPPED
+TRAPPED
+B TRAPPED
+TRAPPED
+A TRAPPED
+TRAPPED
+B TRAPPED
+TRAPPED
+A TRAPPED
+TRAPPED
+B TRAPPED
+TRAPPED
+A TRAPPED
+TRAPPED
+B TRAPPED
+TRAPPED
+A TRAPPED
+TRAPPED
+B TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+RETURNED 100
+A B A B A B A B A B A B A B A B A B A B A B A B A B A B A B A B A B A B A B A B A B A B A B A B A B A B A B A B A B A B A B A B A B A B A B A B A B A B A B A B A B A B A B A B A B A B A B A B A B A B \ No newline at end of file
diff --git a/erts/lib_src/yielding_c_fun/test/test.sh b/erts/lib_src/yielding_c_fun/test/test.sh
new file mode 100755
index 0000000000..a0270475fb
--- /dev/null
+++ b/erts/lib_src/yielding_c_fun/test/test.sh
@@ -0,0 +1,176 @@
+#!/bin/bash
+
+# %CopyrightBegin%
+#
+# Copyright Ericsson AB and Kjell Winblad 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%
+
+
+#
+# Description:
+#
+# Author: Kjell Winblad
+#
+
+#Code to find directory of this file from https://stackoverflow.com/questions/59895/get-the-source-directory-of-a-bash-script-from-within-the-script-itself
+SOURCE="${BASH_SOURCE[0]}"
+while [ -h "$SOURCE" ]; do # resolve $SOURCE until the file is no longer a symlink
+ DIR="$( cd -P "$( dirname "$SOURCE" )" >/dev/null 2>&1 && pwd )"
+ SOURCE="$(readlink "$SOURCE")"
+ [[ $SOURCE != /* ]] && SOURCE="$DIR/$SOURCE" # if $SOURCE was a relative symlink, we need to resolve it relative to the path where the symlink file was located
+done
+DIR="$( cd -P "$( dirname "$SOURCE" )" >/dev/null 2>&1 && pwd )"
+
+GC=$1
+
+RR=-rr
+RR=""
+
+PATH=$PATH:$DIR/../bin
+
+set -e
+set -x
+
+TMP_DIR=$DIR/tmp_dir
+
+mkdir -p $TMP_DIR
+
+TMP_FILE=$TMP_DIR/tmp
+
+TMP_C_FILE=$TMP_DIR/tmp.c
+TMP_INC_FILE=$TMP_DIR/tmp.inc
+TMP_C_FILE2=$TMP_DIR/tmp2.c
+TMP_O_FILE1=$TMP_DIR/tmp1.o
+TMP_O_FILE2=$TMP_DIR/tmp2.o
+TMP_H_FILE=$TMP_DIR/tmp.h
+TMP_CC_OUT=$TMP_DIR/a.out
+
+CC_ARGS="-std=c99 -pedantic -Wall"
+
+CC=clang
+
+SIMPLE_TEST_FILES=("$DIR/examples/simple_yield.c"
+ "$DIR/examples/multi_scope_yield.c"
+ "$DIR/examples/nested_loop_yield.c"
+ "$DIR/examples/void_ret_fun.c"
+ "$DIR/examples/declarations.c"
+ "$DIR/examples/void_param.c"
+ "$DIR/examples/simple_fun_call.c"
+ "$DIR/examples/control_statements.c"
+ "$DIR/examples/simple_yielding_fun_call.c"
+ "$DIR/examples/yielding_mutual_recursion.c"
+ "$DIR/examples/stack_array.c"
+ "$DIR/examples/custom_code_save_restore_yield_state.c"
+ "$DIR/examples/custom_code_save_restore_yield_state_alt_syntax.c"
+ "$DIR/examples/destroy_while_yielded.c"
+ "$DIR/examples/consume_reds.c"
+ "$DIR/examples/nested_call_consume_reds.c"
+ "$DIR/examples/auto_yield.c"
+ "$DIR/examples/yield_with_struct.c"
+ "$DIR/examples/const_defenition.c"
+ "$DIR/examples/ycf_stack_alloc.c"
+ "$DIR/examples/declarations_in_for_loops.c"
+ "$DIR/examples/in_code_var_declaration.c"
+ "$DIR/examples/static_inline_function.c"
+ "$DIR/examples/yielding_fun_in_control.c"
+ "$DIR/examples/debug_ptr_to_stack.c"
+ "$DIR/examples/simple_yield_no_reds.c"
+ "$DIR/examples/thread_example.c")
+
+SIMPLE_TEST_FILES_YIELD_FUNS=("-fnoauto fun"
+ "-fnoauto fun"
+ "-fnoauto fun"
+ "-fnoauto fun"
+ "-fnoauto fun"
+ "-fnoauto fun"
+ "-fnoauto fun"
+ "-fnoauto fun"
+ "-fnoauto fun -fnoauto sub_fun_1 -fnoauto sub_fun_2 -fnoauto sub_fun_3"
+ "-fnoauto A -fnoauto B"
+ "-fnoauto fun"
+ "-fnoauto fun"
+ "-fnoauto fun"
+ "-fnoauto fun"
+ "-fnoauto fun"
+ "-fnoauto fun -fnoauto sub_fun"
+ "-frec fun -frec rec_inc"
+ "-fnoauto fun"
+ "-fnoauto fun"
+ "-fnoauto fun -fnoauto sub_fun"
+ "-f fun"
+ "-fnoauto fun"
+ "-fnoauto fun"
+ "-fnoauto fun -fnoauto sub_fun"
+ "-fnoauto fun"
+ "-fnoauto fun"
+ "-f f_1 -f f_2")
+
+# Check that yielding_c_fun can repeat files correctly
+for C_FILE in $SIMPLE_TEST_FILES
+do
+ yielding_c_fun $GC $RR -repeat $C_FILE > $TMP_C_FILE
+ cmp $C_FILE $TMP_C_FILE
+done
+
+# Check that the default action is to generate correct yield code
+yielding_c_fun $GC -yield $DIR/examples/simple_yield.c > $TMP_C_FILE
+yielding_c_fun $GC $DIR/examples/simple_yield.c > $TMP_C_FILE2
+cmp $TMP_C_FILE $TMP_C_FILE2
+
+# Check yielding
+for ((i = 0; i < ${#SIMPLE_TEST_FILES[@]}; i++))
+do
+ yielding_c_fun $GC $RR -yield ${SIMPLE_TEST_FILES_YIELD_FUNS[$i]} "${SIMPLE_TEST_FILES[$i]}" > $TMP_C_FILE
+ $CC $CC_ARGS -g $TMP_C_FILE -o $TMP_CC_OUT
+ $TMP_CC_OUT > $TMP_FILE
+ cmp $TMP_FILE "${SIMPLE_TEST_FILES[$i]}.out"
+done
+
+
+# Check generated header file and output file
+yielding_c_fun $GC $RR -yield -fnoauto A -fnoauto B -header_file_name $TMP_H_FILE -output_file_name $TMP_C_FILE "$DIR/examples/test_generated_header_file_code.c"
+$CC $CC_ARGS -c $TMP_C_FILE -o $TMP_O_FILE1
+$CC $CC_ARGS -I$DIR -DYCF_YIELD_CODE_GENERATED=1 -c "$DIR/examples/test_generated_header_file_main.c" -o $TMP_O_FILE2
+$CC $CC_ARGS $TMP_O_FILE1 $TMP_O_FILE2 -o $TMP_CC_OUT
+$TMP_CC_OUT > $TMP_FILE
+cmp $TMP_FILE "$DIR/examples/test_generated_header_file_main.c.out"
+
+
+# Check generation of only yielding fun
+echo $TMP_INC_FILE
+yielding_c_fun.bin $GC $RR -yield -static_aux_funs -only_yielding_funs -fnoauto fun -output_file_name $TMP_INC_FILE "$DIR/examples/test_only_output_yielding_funs.c"
+$CC $CC_ARGS -I "$TMP_DIR" "$DIR/examples/test_only_output_yielding_funs.c" -o $TMP_CC_OUT
+$TMP_CC_OUT > $TMP_FILE
+cmp $TMP_FILE "$DIR/examples/test_only_output_yielding_funs.c.out"
+
+# Check that debug mode can detect pointers to stack
+yielding_c_fun $GC $RR -yield -debug -fnoauto fun "$DIR/examples/debug_ptr_to_stack.c" > $TMP_C_FILE
+$CC $CC_ARGS $TMP_C_FILE -o $TMP_CC_OUT
+(set +e ; $TMP_CC_OUT ; [ $? -ne 0 ])
+
+# Check that memory usage of the tool can be logged
+MEM_LOG_FILE="$TMP_DIR/my_mem_log_file.txt"
+yielding_c_fun $GC $RR -log_max_mem_usage "$MEM_LOG_FILE" -yield -debug -fnoauto fun "$DIR/examples/multi_scope_yield.c" > $TMP_C_FILE
+test -f "$MEM_LOG_FILE"
+#rm "$MEM_LOG_FILE"
+
+# Uncomment to the test the Erlang NIF example
+# if [ `rebar3 > /dev/null 2>&1 ; echo $?` = 0 ]
+# then
+# (cd "$DIR/examples/sha256_erlang_nif/" && make clean && make test)
+# fi
+
+
diff --git a/erts/lib_src/yielding_c_fun/ycf_helpers.h b/erts/lib_src/yielding_c_fun/ycf_helpers.h
new file mode 100644
index 0000000000..ae32f60f10
--- /dev/null
+++ b/erts/lib_src/yielding_c_fun/ycf_helpers.h
@@ -0,0 +1,40 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB and Kjell Winblad 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%
+ */
+
+/*
+ * Author: Kjell Winblad
+ */
+
+#ifndef YIELDING_C_FUN_HELPERS_H
+#define YIELDING_C_FUN_HELPERS_H
+
+#include <stdlib.h>
+
+static void* ycf_alloc(size_t size, void* context){
+ (void)context;
+ return malloc(size);
+}
+
+void ycf_free(void* data, void* context){
+ (void)context;
+ free(data);
+}
+
+#endif
diff --git a/erts/lib_src/yielding_c_fun/ycf_lexer.c b/erts/lib_src/yielding_c_fun/ycf_lexer.c
new file mode 100644
index 0000000000..8fcebcc6b0
--- /dev/null
+++ b/erts/lib_src/yielding_c_fun/ycf_lexer.c
@@ -0,0 +1,483 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB and Kjell Winblad 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%
+ */
+
+/*
+ * Author: Kjell Winblad
+ */
+
+
+#include "lib/tiny_regex_c/re.h"
+#include "ycf_yield_fun.h"
+#include "ycf_utils.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+
+
+int ycf_symbol_is_text_eq(ycf_symbol* symbol, char* str){
+ unsigned long symbol_length = symbol->stop - symbol->start;
+ return
+ symbol_length == strlen(str) &&
+ strncmp(str, &symbol->source[symbol->start], symbol_length) == 0;
+}
+
+char* ycf_symbol_text_between(ycf_symbol* s1, ycf_symbol* s2){
+ int size = s2->stop - s1->start;
+ char* str = ycf_malloc(size+1);
+ strncpy(str, &s1->source[s1->start], size);
+ str[size] = 0;
+ return str;
+}
+
+char* get_symbol_type_text(ycf_symbol_type type){
+ switch(type) {
+ case ycf_symbol_type_comment: return "ycf_symbol_type_comment";
+ case ycf_symbol_type_string_literal: return "ycf_symbol_type_string_literal";
+ case ycf_symbol_type_macro_define: return "ycf_symbol_type_macro_define";
+ case ycf_symbol_type_macro_command: return "ycf_symbol_type_macro_command";
+ case ycf_symbol_type_whitespace: return "ycf_symbol_type_whitespace";
+ case ycf_symbol_type_identifier: return "ycf_symbol_type_identifier";
+ case ycf_symbol_type_number: return "ycf_symbol_type_number";
+ case ycf_symbol_type_star: return "ycf_symbol_type_star";
+ case ycf_symbol_type_neg: return "ycf_symbol_type_neg";
+ case ycf_symbol_type_equal_equal_sign: return "ycf_symbol_type_equal_equal_sign";
+ case ycf_symbol_type_not_equal_sign: return "ycf_symbol_type_not_equal_sign";
+ case ycf_symbol_type_open_parenthesis: return "ycf_symbol_type_open_parenthesis";
+ case ycf_symbol_type_end_parenthesis: return "ycf_symbol_type_end_parenthesis";
+ case ycf_symbol_type_open_curly_brace: return "ycf_symbol_type_open_curly_brace";
+ case ycf_symbol_type_end_curly_brace: return "ycf_symbol_type_end_curly_brace";
+ case ycf_symbol_type_open_square_bracket: return "ycf_symbol_type_open_square_bracket";
+ case ycf_symbol_type_end_square_bracket: return "ycf_symbol_type_end_square_bracket";
+ case ycf_symbol_type_equal_sign: return "ycf_symbol_type_equal_sign";
+ case ycf_symbol_type_semicolon: return "ycf_symbol_type_semicolon";
+ case ycf_symbol_type_comma: return "ycf_symbol_type_comma";
+ case ycf_symbol_type_consume_reds: return "ycf_symbol_type_consume_reds";
+ case ycf_symbol_type_pointer_field_access: return "ycf_symbol_type_pointer_field_access";
+ case ycf_symbol_type_period: return "ycf_symbol_type_period";
+ case ycf_symbol_type_const: return "ycf_symbol_type_const";
+ case ycf_symbol_type_void: return "ycf_symbol_type_void";
+ case ycf_symbol_type_volatile: return "ycf_symbol_type_volatile";
+ case ycf_symbol_type_static: return "ycf_symbol_type_static";
+ case ycf_symbol_type_inline: return "ycf_symbol_type_inline";
+ case ycf_symbol_type_return: return "ycf_symbol_type_return";
+ case ycf_symbol_type_if: return "ycf_symbol_type_if";
+ case ycf_symbol_type_else: return "ycf_symbol_type_else";
+ case ycf_symbol_type_goto: return "ycf_symbol_type_goto";
+ case ycf_symbol_type_break: return "ycf_symbol_type_break";
+ case ycf_symbol_type_while: return "ycf_symbol_type_while";
+ case ycf_symbol_type_do: return "ycf_symbol_type_do";
+ case ycf_symbol_type_for: return "ycf_symbol_type_for";
+ case ycf_symbol_type_switch: return "ycf_symbol_type_switch";
+ case ycf_symbol_type_continue: return "ycf_symbol_type_continue";
+ case ycf_symbol_type_something_else: return "ycf_symbol_type_something_else";
+ case ycf_symbol_type_special_code_start: return "ycf_symbol_type_special_code_start";
+ case ycf_symbol_type_special_code_end: return "ycf_symbol_type_special_code_end";
+ }
+ return "non_existing_symbol?";
+}
+
+typedef struct symbol_finder {
+ int (*finder)(struct symbol_finder*,char*);
+ ycf_symbol_type type;
+ int length;
+ char *str_1;
+ char *str_2;
+} symbol_finder;
+
+int starts_with(char *str, char *prefix)
+{
+ return strncmp(str, prefix, strlen(prefix)) == 0;
+}
+
+int until_no_match(symbol_finder* f, char* text){
+ int pos = 0;
+ while(re_match(f->str_1, &(text[pos])) == 0){
+ pos++;
+ }
+ return pos;
+}
+
+int string_litteral_finder(symbol_finder* f, char* text){
+ int pos = 0;
+ if (starts_with(text, "\"")){
+ pos++;
+ //\"(\\.|[^"\\])*\"
+ while(re_match("\\.", &(text[pos])) == 0 ||
+ re_match("[^\"]", &(text[pos])) == 0){
+ pos++;
+ }
+ if(starts_with(&(text[pos]), "\"")){
+ return pos + 1;
+ }else {
+ printf("Broken string litteral\n");
+ exit(1);
+ }
+ }
+ return pos;
+}
+
+int macro_define_finder(symbol_finder* f, char* text){
+ int pos = 0;
+ if (starts_with(text, "#define")){
+ pos = pos + strlen("#define");
+ while(1){
+ if(starts_with(&(text[pos]), "\\\n")){
+ pos = pos + 2;
+ } else if (starts_with(&(text[pos]), "\n")){
+ break;
+ } else {
+ pos++;
+ }
+ }
+ }
+ return pos;
+}
+
+
+int starts_with_until_no_match(symbol_finder* f, char* text){
+ int pos = 0;
+ if(re_match(f->str_1, text) == 0){
+ while(re_match(f->str_2, &(text[pos])) == 0){
+ pos++;
+ }
+ }
+ return pos;
+}
+
+int starts_with_ends_with(symbol_finder* f, char* text){
+ if(starts_with(text, f->str_1)){
+ int pos = 1;
+ while(!starts_with(&(text[pos]), f->str_2)){
+ pos++;
+ }
+ return pos+strlen(f->str_2);
+ }
+ return 0;
+}
+
+int fixed_string(symbol_finder* f, char* text){
+ if(starts_with(text, f->str_1)){
+ return strlen(f->str_1);
+ }
+ return 0;
+}
+
+int fixed_alpha_string(symbol_finder* f, char* text){
+ if(starts_with(text, f->str_1) &&
+ re_match("[^\\W]", &text[strlen(f->str_1)])){
+ return strlen(f->str_1);
+ }
+ return 0;
+}
+
+int regex_char(symbol_finder* f, char* text){
+ if(re_match(f->str_1, text) == 0){
+ return 1;
+ }
+ return 0;
+}
+
+void fold_whitespace_and_comments(ycf_symbol_list* symbols){
+ ycf_symbol* prev = NULL;
+ ycf_symbol* current = symbols->head;
+ ycf_symbol* dummy = ycf_malloc(sizeof(ycf_symbol));
+ while(current != NULL){
+ current->whitespace_or_comment_before = NULL;
+ if(prev != NULL && (prev->type == ycf_symbol_type_whitespace ||
+ prev->type == ycf_symbol_type_comment)){
+ current->whitespace_or_comment_before = prev;
+ }
+ prev = current;
+ current = current->next;
+ }
+ // remove ycf_symbol_type_whitespace and comments from list
+ dummy->type = ycf_symbol_type_void;
+ dummy->next = symbols->head;
+ prev = dummy;
+ current = prev->next;
+ while(current != NULL &&
+ current != symbols->last){
+ if(current->type == ycf_symbol_type_whitespace ||
+ current->type == ycf_symbol_type_comment){
+ prev->next = current->next;
+ current = current->next;
+ }else {
+ prev = current;
+ current = current->next;
+ }
+ }
+ symbols->head = dummy->next;
+}
+
+ycf_symbol_list ycf_symbol_list_from_text(char* text){
+ int pos = 0;
+ int nr_of_finders = 41;
+ int i;
+ ycf_symbol_list ret = ycf_symbol_list_empty();
+ symbol_finder symbol_finders[] =
+ {
+ {
+ .type = ycf_symbol_type_special_code_start,
+ .str_1 = "/*special_code_start:",
+ .str_2 = "*/",
+ .finder = starts_with_ends_with
+ },
+ {
+ .type = ycf_symbol_type_special_code_end,
+ .str_1 = "/*special_code_end*/",
+ .finder = fixed_string
+ },
+ {
+ .type = ycf_symbol_type_comment,
+ .str_1 = "/*",
+ .str_2 = "*/",
+ .finder = starts_with_ends_with
+ },
+ {
+ .type = ycf_symbol_type_string_literal,
+ .finder = string_litteral_finder
+ },
+ {
+ .type = ycf_symbol_type_macro_define,
+ .finder = macro_define_finder
+ },
+ {
+ .type = ycf_symbol_type_macro_command,
+ .str_1 = "#",
+ .str_2 = "\n",
+ .finder = starts_with_ends_with
+ },
+ {
+ .type = ycf_symbol_type_whitespace,
+ .str_1 = "\\s",
+ .finder = until_no_match
+ },
+ {
+ .type = ycf_symbol_type_void,
+ .str_1 = "void",
+ .finder = fixed_alpha_string
+ },
+ {
+ .type = ycf_symbol_type_static,
+ .str_1 = "static",
+ .finder = fixed_alpha_string
+ },
+ {
+ .type = ycf_symbol_type_inline,
+ .str_1 = "inline",
+ .finder = fixed_alpha_string
+ },
+ {
+ .type = ycf_symbol_type_const,
+ .str_1 = "const",
+ .finder = fixed_alpha_string
+ },
+ {
+ .type = ycf_symbol_type_volatile,
+ .str_1 = "volatile",
+ .finder = fixed_alpha_string
+ },
+ {
+ .type = ycf_symbol_type_consume_reds,
+ .str_1 = "YCF_CONSUME_REDS",
+ .finder = fixed_alpha_string
+ },
+ {
+ .type = ycf_symbol_type_return,
+ .str_1 = "return",
+ .finder = fixed_alpha_string
+ },
+ {
+ .type = ycf_symbol_type_if,
+ .str_1 = "if",
+ .finder = fixed_alpha_string
+ },
+ {
+ .type = ycf_symbol_type_else,
+ .str_1 = "else",
+ .finder = fixed_alpha_string
+ },
+ {
+ .type = ycf_symbol_type_goto,
+ .str_1 = "goto",
+ .finder = fixed_alpha_string
+ },
+ {
+ .type = ycf_symbol_type_break,
+ .str_1 = "break",
+ .finder = fixed_alpha_string
+ },
+ {
+ .type = ycf_symbol_type_continue,
+ .str_1 = "continue",
+ .finder = fixed_alpha_string
+ },
+ {
+ .type = ycf_symbol_type_while,
+ .str_1 = "while",
+ .finder = fixed_alpha_string
+ },
+ {
+ .type = ycf_symbol_type_do,
+ .str_1 = "do",
+ .finder = fixed_alpha_string
+ },
+ {
+ .type = ycf_symbol_type_for,
+ .str_1 = "for",
+ .finder = fixed_alpha_string
+ },
+ {
+ .type = ycf_symbol_type_switch,
+ .str_1 = "switch",
+ .finder = fixed_alpha_string
+ },
+ {
+ .type = ycf_symbol_type_identifier,
+ .str_1 = "[a-zA-Z]",
+ .str_2 = "\\w",
+ .finder = starts_with_until_no_match
+ },
+ {
+ .type = ycf_symbol_type_number,
+ .str_1 = "\\d",
+ .finder = until_no_match
+ },
+ {
+ .type = ycf_symbol_type_open_parenthesis,
+ .str_1 = "(",
+ .finder = fixed_string
+ },
+ {
+ .type = ycf_symbol_type_end_parenthesis,
+ .str_1 = ")",
+ .finder = fixed_string
+ },
+ {
+ .type = ycf_symbol_type_open_curly_brace,
+ .str_1 = "{",
+ .finder = fixed_string
+ },
+ {
+ .type = ycf_symbol_type_end_curly_brace,
+ .str_1 = "}",
+ .finder = fixed_string
+ },
+ {
+ .type = ycf_symbol_type_open_square_bracket,
+ .str_1 = "[",
+ .finder = fixed_string
+ },
+ {
+ .type = ycf_symbol_type_end_square_bracket,
+ .str_1 = "]",
+ .finder = fixed_string
+ },
+ {
+ .type = ycf_symbol_type_equal_sign,
+ .str_1 = "=",
+ .finder = fixed_string
+ },
+ {
+ .type = ycf_symbol_type_not_equal_sign,
+ .str_1 = "!=",
+ .finder = fixed_string
+ },
+ {
+ .type = ycf_symbol_type_equal_sign,
+ .str_1 = "==",
+ .finder = fixed_string
+ },
+ {
+ .type = ycf_symbol_type_star,
+ .str_1 = "*",
+ .finder = fixed_string
+ },
+ {
+ .type = ycf_symbol_type_neg,
+ .str_1 = "!",
+ .finder = fixed_string
+ },
+ {
+ .type = ycf_symbol_type_semicolon,
+ .str_1 = ";",
+ .finder = fixed_string
+ },
+ {
+ .type = ycf_symbol_type_comma,
+ .str_1 = ",",
+ .finder = fixed_string
+ },
+ {
+ .type = ycf_symbol_type_period,
+ .str_1 = ".",
+ .finder = fixed_string
+ },
+ {
+ .type = ycf_symbol_type_pointer_field_access,
+ .str_1 = "->",
+ .finder = fixed_string
+ },
+ {
+ .type = ycf_symbol_type_something_else,
+ .str_1 = ".",
+ .finder = regex_char
+ }
+ };
+ while(text[pos] != 0){
+ int last_pos = pos;
+ for(i = 0; i < nr_of_finders; i++) {
+ symbol_finder f = symbol_finders[i];
+ int stop = f.finder(&f, &text[pos]);
+ if(stop){
+ ycf_symbol* s = ycf_malloc(sizeof(ycf_symbol));
+ s->type = f.type;
+ s->source = text;
+ s->start = pos;
+ s->stop = pos + stop;
+ s->next = NULL;
+ ycf_symbol_list_append(&ret, s);
+ pos = s->stop;
+ break;
+ }
+ }
+ if (last_pos == pos){
+ printf("Lexer: NOTHING MATCH Stuck at: \n%s\n", &text[pos]);
+ exit(1);
+ }
+ }
+ fold_whitespace_and_comments(&ret);
+ return ret;
+}
+
+void ycf_symbol_list_print(char* text){
+ ycf_symbol_list symbols = ycf_symbol_list_from_text(text);
+ ycf_symbol* s = symbols.head;
+ while(s != NULL){
+ printf("TYPE %s, START=%d, STOP=%d\n",
+ get_symbol_type_text(s->type),
+ s->start,
+ s->stop);
+ s = s->next;
+ }
+ printf("||||| END OF SYMBOLS\n");
+}
diff --git a/erts/lib_src/yielding_c_fun/ycf_lists.h b/erts/lib_src/yielding_c_fun/ycf_lists.h
new file mode 100644
index 0000000000..7bb3487ecf
--- /dev/null
+++ b/erts/lib_src/yielding_c_fun/ycf_lists.h
@@ -0,0 +1,316 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB and Kjell Winblad 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%
+ */
+
+/*
+ * Author: Kjell Winblad
+ */
+
+#ifndef YIELDING_C_FUN_LISTS_H
+#define YIELDING_C_FUN_LISTS_H
+
+#include <stdlib.h>
+#include <stdio.h>
+
+
+#define INIT_LIST(L) \
+ do{ \
+ (L)->head = NULL; \
+ (L)->last = NULL; \
+ }while(0)
+
+#define APPEND_LIST(L,E) \
+ do{ \
+ void* e = E; \
+ if((L)->head == NULL){ \
+ (L)->head = e; \
+ } else { \
+ (L)->last->next = e; \
+ } \
+ (L)->last = e; \
+ }while(0)
+
+#define PREPEND_LIST(T,L,E) \
+ do{ \
+ T* e = E; \
+ if((L)->head == NULL){ \
+ (L)->last = e; \
+ } else { \
+ e->next = (L)->head; \
+ } \
+ (L)->head = e; \
+ }while(0)
+
+#define CONCAT_LIST(T,L1,L2) \
+ do{ \
+ T* current = (L2)->head; \
+ while(current != NULL){ \
+ APPEND_LIST(L1, current); \
+ current = current->next; \
+ } \
+ }while(0)
+
+#define PRINT_LIST(T,L, NAME) \
+ do{ \
+ printf("NAME %s\n", NAME); \
+ printf("HEAD %p\n", (L)->head); \
+ printf("LAST %p\n", (L)->last); \
+ printf("ELEMS:\n"); \
+ T* current = (L)->head; \
+ while(current != NULL){ \
+ printf("E: %p\n", current); \
+ current = current->next; \
+ } \
+ }while(0)
+
+#define REMOVE_LIST(T,L,E) \
+ do{ \
+ T* prev = NULL; \
+ T* rl_current = (L)->head; \
+ T* e = E; \
+ while(rl_current != e && rl_current != NULL){ \
+ prev = rl_current ; \
+ rl_current = rl_current ->next; \
+ } \
+ if(rl_current == NULL){ \
+ exit(1); \
+ } \
+ if(prev == NULL){ \
+ if((L)->head == (L)->last){ \
+ (L)->last = NULL; \
+ } \
+ (L)->head = (L)->head->next; \
+ }else{ \
+ if(rl_current == (L)->last){ \
+ (L)->last = prev; \
+ } \
+ prev->next = rl_current ->next; \
+ } \
+ }while(0)
+
+#define INSERT_AFTER_LIST(T,L,E,TO_INSERT) \
+ do{ \
+ T* current_y = (L)->head; \
+ T* elem_x = E; \
+ T* to_insert2 = TO_INSERT; \
+ if(elem_x == NULL){ \
+ PREPEND_LIST(T,L,to_insert2); \
+ break; \
+ }else if(elem_x == (L)->last){ \
+ APPEND_LIST(L,to_insert2); \
+ break; \
+ } \
+ while(current_y != elem_x && current_y != NULL){ \
+ current_y = current_y->next; \
+ } \
+ if(current_y == NULL){ \
+ printf("CANNOT INSERT AFTER NONEXISTING\n"); \
+ exit(1); \
+ } \
+ to_insert2->next = current_y->next; \
+ current_y->next = to_insert2; \
+ }while(0)
+
+#define INSERT_BEFORE_LIST(T,L,E_BEFORE,TO_INSERT) \
+ do{ \
+ T* prev_x = NULL; \
+ T* current_x = (L)->head; \
+ T* to_insert_x = TO_INSERT; \
+ T* e_before_x = E_BEFORE; \
+ while(current_x != e_before_x && current_x != NULL){ \
+ prev_x = current_x; \
+ current_x = current_x->next; \
+ } \
+ if(current_x == NULL){ \
+ printf("CANNOT INSERT AFTER NONEXISTING\n"); \
+ exit(1); \
+ } \
+ INSERT_AFTER_LIST(T,L,prev_x,to_insert_x); \
+ }while(0)
+
+
+#define REPLACE_LIST(T,L,OLD,NEW) \
+ do{ \
+ T* old = OLD; \
+ T* new = NEW; \
+ T* prev_old_next = old->next; \
+ INSERT_AFTER_LIST(T,L,old,new); \
+ REMOVE_LIST(T,L,old); \
+ old->next = prev_old_next; \
+ }while(0)
+
+
+/* list functions */
+
+#define GENERATE_LIST_FUNCTIONS(NODE_TYPE) \
+ \
+ NODE_TYPE##_list NODE_TYPE##_list_empty(){ \
+ NODE_TYPE##_list list; \
+ INIT_LIST(&list); \
+ return list; \
+ } \
+ \
+ NODE_TYPE* NODE_TYPE##_shallow_copy(NODE_TYPE* n){ \
+ NODE_TYPE* new = ycf_malloc(sizeof(NODE_TYPE)); \
+ *new = *n; \
+ new->next = NULL; \
+ return new; \
+ } \
+ \
+ NODE_TYPE##_list NODE_TYPE##_list_shallow_copy(NODE_TYPE##_list n){ \
+ NODE_TYPE##_list new; \
+ NODE_TYPE* current = n.head; \
+ INIT_LIST(&new); \
+ while(current != NULL){ \
+ APPEND_LIST(&new, NODE_TYPE##_shallow_copy(current)); \
+ current = current->next; \
+ } \
+ return new; \
+ } \
+ \
+ int NODE_TYPE##_list_get_item_position(NODE_TYPE##_list* list, NODE_TYPE* node){ \
+ NODE_TYPE* current = list->head; \
+ int pos = 0; \
+ while(current != NULL){ \
+ if(current == node){ \
+ return pos; \
+ } \
+ pos = pos + 1; \
+ current = current->next; \
+ } \
+ return -1; \
+ } \
+ \
+ NODE_TYPE* NODE_TYPE##_list_get_item_at_position(NODE_TYPE##_list* list, int pos){ \
+ NODE_TYPE* current = list->head; \
+ int current_pos = 0; \
+ while(current != NULL){ \
+ if(current_pos == pos){ \
+ return current; \
+ } \
+ current_pos = current_pos + 1; \
+ current = current->next; \
+ } \
+ return NULL; \
+ } \
+ \
+ void NODE_TYPE##_list_append(NODE_TYPE##_list* list, NODE_TYPE* node){ \
+ APPEND_LIST(list, node); \
+ } \
+ \
+ NODE_TYPE##_list NODE_TYPE##_list_copy_append(NODE_TYPE##_list list, NODE_TYPE* node){ \
+ NODE_TYPE##_list list_copy = NODE_TYPE##_list_shallow_copy(list); \
+ NODE_TYPE* node_copy = NODE_TYPE##_shallow_copy(node); \
+ NODE_TYPE##_list_append(&list_copy, node_copy); \
+ return list_copy; \
+ } \
+ \
+ void NODE_TYPE##_list_prepend(NODE_TYPE##_list* list, NODE_TYPE* node){ \
+ PREPEND_LIST(NODE_TYPE, list, node); \
+ } \
+ \
+ NODE_TYPE##_list NODE_TYPE##_list_copy_prepend(NODE_TYPE##_list list, NODE_TYPE* node){ \
+ NODE_TYPE##_list list_copy = NODE_TYPE##_list_shallow_copy(list); \
+ NODE_TYPE* node_copy = NODE_TYPE##_shallow_copy(node); \
+ NODE_TYPE##_list_prepend(&list_copy, node_copy); \
+ return list_copy; \
+ } \
+ \
+ void NODE_TYPE##_list_insert_before(NODE_TYPE##_list* list, NODE_TYPE* before_this, NODE_TYPE* to_insert_z){ \
+ INSERT_BEFORE_LIST(NODE_TYPE, list, before_this, to_insert_z); \
+ } \
+ \
+ NODE_TYPE##_list NODE_TYPE##_list_copy_insert_before(NODE_TYPE##_list list, NODE_TYPE* before_this, NODE_TYPE* to_insert){ \
+ int pos = NODE_TYPE##_list_get_item_position(&list, before_this); \
+ NODE_TYPE##_list list_copy = NODE_TYPE##_list_shallow_copy(list);; \
+ NODE_TYPE* actual_this = NODE_TYPE##_list_get_item_at_position(&list_copy, pos); \
+ NODE_TYPE* to_insert_copy = NODE_TYPE##_shallow_copy(to_insert); \
+ NODE_TYPE##_list_insert_before(&list_copy, actual_this, to_insert_copy); \
+ return list_copy; \
+ } \
+ \
+ void NODE_TYPE##_list_insert_after(NODE_TYPE##_list* list, NODE_TYPE* after_this, NODE_TYPE* to_insert_z){ \
+ INSERT_AFTER_LIST(NODE_TYPE, list, after_this, to_insert_z); \
+ } \
+ \
+ NODE_TYPE##_list NODE_TYPE##_list_copy_insert_after(NODE_TYPE##_list list, NODE_TYPE* after_this, NODE_TYPE* to_insert){ \
+ int pos = NODE_TYPE##_list_get_item_position(&list, after_this); \
+ NODE_TYPE##_list list_copy = NODE_TYPE##_list_shallow_copy(list); \
+ NODE_TYPE* actual_this = NODE_TYPE##_list_get_item_at_position(&list_copy, pos); \
+ NODE_TYPE* to_insert_copy = NODE_TYPE##_shallow_copy(to_insert); \
+ NODE_TYPE##_list_insert_after(&list_copy, actual_this, to_insert_copy); \
+ return list_copy; \
+ } \
+ \
+ void NODE_TYPE##_list_remove(NODE_TYPE##_list* list, NODE_TYPE* to_remove){ \
+ REMOVE_LIST(NODE_TYPE, list, to_remove); \
+ } \
+ \
+ NODE_TYPE##_list NODE_TYPE##_list_copy_remove(NODE_TYPE##_list list, NODE_TYPE* to_remove){ \
+ int pos = NODE_TYPE##_list_get_item_position(&list, to_remove); \
+ NODE_TYPE##_list list_copy = NODE_TYPE##_list_shallow_copy(list); \
+ NODE_TYPE* actual_this = NODE_TYPE##_list_get_item_at_position(&list_copy, pos); \
+ NODE_TYPE##_list_remove(&list_copy, actual_this); \
+ return list_copy; \
+ } \
+ \
+ void NODE_TYPE##_list_replace(NODE_TYPE##_list* list, NODE_TYPE* to_replace, NODE_TYPE* replace_with){ \
+ REPLACE_LIST(NODE_TYPE, list, to_replace, replace_with); \
+ } \
+ \
+ NODE_TYPE##_list NODE_TYPE##_list_copy_replace(NODE_TYPE##_list list, NODE_TYPE* to_replace, NODE_TYPE* replace_with){ \
+ int pos = NODE_TYPE##_list_get_item_position(&list, to_replace); \
+ NODE_TYPE##_list list_copy = NODE_TYPE##_list_shallow_copy(list); \
+ NODE_TYPE* actual_this = NODE_TYPE##_list_get_item_at_position(&list_copy, pos); \
+ NODE_TYPE* replace_with_copy = NODE_TYPE##_shallow_copy(replace_with); \
+ NODE_TYPE##_list_replace(&list_copy, actual_this, replace_with_copy); \
+ return list_copy; \
+ } \
+ \
+ void NODE_TYPE##_list_concat(NODE_TYPE##_list* list1, NODE_TYPE##_list* list2){ \
+ CONCAT_LIST(NODE_TYPE, list1, list2); \
+ } \
+ NODE_TYPE##_list NODE_TYPE##_list_copy_concat(NODE_TYPE##_list list1, NODE_TYPE##_list list2){ \
+ NODE_TYPE##_list list1_copy = NODE_TYPE##_list_shallow_copy(list1); \
+ NODE_TYPE##_list list2_copy = NODE_TYPE##_list_shallow_copy(list2); \
+ CONCAT_LIST(NODE_TYPE, &list1_copy, &list2_copy); \
+ return list1_copy; \
+ } \
+ \
+ size_t NODE_TYPE##_list_length(NODE_TYPE##_list list){ \
+ NODE_TYPE* current = list.head; \
+ size_t count = 0; \
+ while(current != NULL){ \
+ count = count + 1; \
+ current = current->next; \
+ } \
+ return count; \
+ }
+
+/* void print_string_list(string_list n){ */
+/* string_list_item* current = n.head; */
+/* printf("START\n"); */
+/* while(current != NULL){ */
+/* printf("%s\n", current->str); */
+/* current = current->next; */
+/* } */
+/* printf("END\n"); */
+/* } */
+
+#endif //YIELDING_C_FUN_LISTS_H
diff --git a/erts/lib_src/yielding_c_fun/ycf_main.c b/erts/lib_src/yielding_c_fun/ycf_main.c
new file mode 100644
index 0000000000..2287db8161
--- /dev/null
+++ b/erts/lib_src/yielding_c_fun/ycf_main.c
@@ -0,0 +1,330 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB and Kjell Winblad 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%
+ */
+
+/*
+ * Author: Kjell Winblad
+ */
+
+
+#include "ycf_utils.h"
+#include "ycf_yield_fun.h"
+#include "ycf_node.h"
+#include "lib/simple_c_gc/simple_c_gc.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+
+
+char* file_to_str(const char* filename) {
+ char * buffer;
+ long length;
+ FILE * f = fopen (filename, "rb");
+
+ if (f) {
+ fseek (f, 0, SEEK_END);
+ length = ftell (f);
+ fseek (f, 0, SEEK_SET);
+ buffer = ycf_malloc (length+1);
+ size_t nr_of_read_bytes =
+ fread (buffer, 1, length, f);
+ if (nr_of_read_bytes != length) {
+ printf("error: while reading file %s\n", filename);
+ exit (1);
+ }
+ fclose (f);
+ buffer[length] = 0;
+ } else {
+ printf("error: could not open file %s\n", filename);
+ exit(1);
+ }
+
+ return buffer;
+}
+
+void str_to_file(const char *filepath, const char *data)
+{
+ FILE *fp = fopen(filepath, "w");
+ if (fp != NULL)
+ {
+ fputs(data, fp);
+ fclose(fp);
+ }
+}
+
+void parse_and_repeat_src_from_ast(const char* file_name){
+ char* content = file_to_str(file_name);
+ ycf_node* tree = ycf_node_from_string(content);
+ ycf_node_print(tree, NULL);
+}
+
+void parse_and_print_symbols(const char* file_name){
+ char* content = file_to_str(file_name);
+ ycf_symbol_list_print(content);
+}
+
+void parse_and_print_ast(const char* file_name){
+ char* content = file_to_str(file_name);
+ ycf_node* tree = ycf_node_from_string(content);
+ print_abstract_syntax_tree(tree);
+}
+
+void print_help_text_and_exit(char* program_name, int error_code) {
+ printf("Usage: %s [-h]\n"
+ " %s [-use_gc [-print_gc_info]]\n"
+ " [-log_max_mem_usage log_file]\n"
+ " [(( -f | -frec | -fnoauto ) function_name)...\n"
+ " [-output_file_name output_file]\n"
+ " [-header_file_name header_file]\n"
+ " [-debug]\n"
+ " [-only_yielding_funs]\n"
+ " [-static_aux_funs]\n"
+ " input_c_file]]\n"
+ "\n"
+ "Please see the README.md file for more details.\n",
+ program_name,
+ program_name);
+ exit(0);
+}
+
+int ycf_main( int argc, char* argv[] )
+{
+ int i = 1;
+ for(i = 1; i < argc; i++ ){
+ bool repeat = ycf_string_is_equal("-repeat", argv[i]);
+ bool print_symbols = ycf_string_is_equal("-print_symbols", argv[i]);
+ bool print_ast = ycf_string_is_equal("-print_ast", argv[i]);;
+ bool yield = ycf_string_is_equal("-yield", argv[i]);
+ if(repeat || print_symbols || print_ast || yield){
+ i++;
+ if(i >= argc){
+ printf("ERROR: Expected at least one more argument\n\n");
+ print_help_text_and_exit(argv[0], 1);
+ }
+ }
+ if (print_ast) {
+ parse_and_print_ast(argv[i]);
+ } else if (repeat) {
+ parse_and_repeat_src_from_ast(argv[i]);
+ } else if (print_symbols) {
+ parse_and_print_symbols(argv[i]);
+ } else /* Default is yield */ {
+ ycf_string_item_list funs_to_yield = ycf_string_item_list_empty();
+ ycf_string_item_list funs_to_yield_no_auto = ycf_string_item_list_empty();
+ ycf_string_item_list funs_to_yield_frec = ycf_string_item_list_empty();
+ ycf_string_item_list all_yield_funs = ycf_string_item_list_empty();
+ char* header_file_name = NULL;
+ char* output_file_name = NULL;
+ bool debug = false;
+ bool only_yielding_funs = false;
+ bool static_aux_funs = false;
+ while(i < argc && (ycf_string_is_equal(argv[i], "-f") ||
+ ycf_string_is_equal(argv[i], "-frec") ||
+ ycf_string_is_equal(argv[i], "-fnoauto") ||
+ ycf_string_is_equal(argv[i], "-header_file_name") ||
+ ycf_string_is_equal(argv[i], "-output_file_name") ||
+ ycf_string_is_equal(argv[i], "-debug") ||
+ ycf_string_is_equal(argv[i], "-only_yielding_funs") ||
+ ycf_string_is_equal(argv[i], "-static_aux_funs"))){
+ bool frec = ycf_string_is_equal(argv[i], "-frec");
+ bool noauto = ycf_string_is_equal(argv[i], "-fnoauto");
+ bool header = ycf_string_is_equal(argv[i], "-header_file_name");
+ bool output = ycf_string_is_equal(argv[i], "-output_file_name");
+ i++;
+ if(ycf_string_is_equal(argv[i-1], "-debug")){
+ debug = true;
+ continue;
+ }
+ if(ycf_string_is_equal(argv[i-1], "-only_yielding_funs")){
+ only_yielding_funs = true;
+ continue;
+ }
+ if(ycf_string_is_equal(argv[i-1], "-static_aux_funs")){
+ static_aux_funs = true;
+ continue;
+ }
+ if(i >= argc){
+ fprintf(stderr, "ERROR: Expected a name after %s\n\n", argv[i-1]);
+ print_help_text_and_exit(argv[0], 1);
+ }
+ if(header){
+ if(header_file_name != NULL){
+ fprintf(stderr, "ERROR: Can only print a single header file\n\n");
+ print_help_text_and_exit(argv[0], 1);
+ }
+ header_file_name = (char*)argv[i];
+ } else if(output){
+ if(output_file_name != NULL){
+ fprintf(stderr, "ERROR: Can only print a single output file\n\n");
+ print_help_text_and_exit(argv[0], 1);
+ }
+ output_file_name = (char*)argv[i];
+ } else if(frec){
+ ycf_string_item_list_append(&funs_to_yield_frec, ycf_string_item_new((char *) argv[i]));
+ } else if(noauto){
+ ycf_string_item_list_append(&funs_to_yield_no_auto, ycf_string_item_new((char *) argv[i]));
+ } else{
+ ycf_string_item_list_append(&funs_to_yield, ycf_string_item_new((char *) argv[i]));
+ }
+ i++;
+ }
+ ycf_string_item_list funs_to_yield_copy = ycf_string_item_list_shallow_copy(funs_to_yield);
+ ycf_string_item_list funs_to_yield_no_auto_copy = ycf_string_item_list_shallow_copy(funs_to_yield_no_auto);
+ ycf_string_item_list funs_to_yield_frec_copy = ycf_string_item_list_shallow_copy(funs_to_yield_frec);
+ ycf_string_item_list_concat(&all_yield_funs, &funs_to_yield_copy);
+ ycf_string_item_list_concat(&all_yield_funs, &funs_to_yield_no_auto_copy);
+ ycf_string_item_list_concat(&all_yield_funs, &funs_to_yield_frec_copy);
+ if(funs_to_yield.head == NULL && funs_to_yield_no_auto.head == NULL && funs_to_yield_frec.head == NULL){
+ fprintf(stderr, "ERROR: Expected at least one \"(-f|-frec|-fnoauto) function_name\" argument\n\n");
+ print_help_text_and_exit(argv[0], 1);
+ }
+ if(i >= argc){
+ fprintf(stderr, "ERROR: Expected an input file name as the last parameter\n\n");
+ print_help_text_and_exit(argv[0], 1);
+ }
+ {
+ char* file_name = (char*)argv[i];
+ char* src = file_to_str(file_name);
+ ycf_string_item* current_fun = funs_to_yield.head;
+ ycf_node* header_file = ycf_node_from_string("");
+ ycf_node* tree = ycf_node_from_string(src);
+ ycf_node* only_yielding_funs_tree = NULL;
+ while(current_fun != NULL){
+ tree = ast_get_ast_with_yieldified_function(tree,
+ header_file,
+ current_fun->str,
+ &all_yield_funs,
+ true,
+ false,
+ debug,
+ only_yielding_funs,
+ &only_yielding_funs_tree,
+ static_aux_funs);
+ current_fun = current_fun->next;
+ }
+ current_fun = funs_to_yield_no_auto.head;
+ while(current_fun != NULL){
+ tree = ast_get_ast_with_yieldified_function(tree,
+ header_file,
+ current_fun->str,
+ &all_yield_funs,
+ false,
+ false,
+ debug,
+ only_yielding_funs,
+ &only_yielding_funs_tree,
+ static_aux_funs);
+ current_fun = current_fun->next;
+ }
+ current_fun = funs_to_yield_frec.head;
+ while(current_fun != NULL){
+ tree = ast_get_ast_with_yieldified_function(tree,
+ header_file,
+ current_fun->str,
+ &all_yield_funs,
+ true,
+ true,
+ debug,
+ only_yielding_funs,
+ &only_yielding_funs_tree,
+ static_aux_funs);
+ current_fun = current_fun->next;
+ }
+ ast_add_yield_code_generated_define(tree, debug);
+ ast_add_yield_code_generated_define(header_file, debug);
+ if(only_yielding_funs){
+ ast_add_yield_code_generated_define(only_yielding_funs_tree, debug);
+ }
+ if(header_file_name != NULL){
+ ycf_string_printable_buffer* b = ycf_string_printable_buffer_new();
+ ycf_node_print(header_file, b);
+ str_to_file(header_file_name, b->buffer);
+ }
+ if(only_yielding_funs){
+ tree = only_yielding_funs_tree;
+ }
+ if(output_file_name != NULL){
+ ycf_string_printable_buffer* b = ycf_string_printable_buffer_new();
+ ycf_node_print(tree, b);
+ str_to_file(output_file_name, b->buffer);
+ } else {
+ ycf_node_print(tree, NULL);
+ }
+ }
+ }
+ }
+ return 0;
+}
+
+bool is_help_option(char* string) {
+return
+ ycf_string_is_equal("-h", string) ||
+ ycf_string_is_equal("--h", string) ||
+ ycf_string_is_equal("-help", string) ||
+ ycf_string_is_equal("--help", string);
+}
+
+int main( int argc, char* argv[] )
+{
+ bool log_max_mem_usage = false;
+ bool use_gc = false;
+ char * log_max_mem_usage_file = NULL;
+ int i = 1;
+ if (argc == 1) {
+ print_help_text_and_exit(argv[0], 0);
+ }
+ while(i < argc &&
+ (ycf_string_is_equal("-use_gc", argv[i]) ||
+ ycf_string_is_equal("-log_max_mem_usage", argv[i]) ||
+ ycf_string_is_equal("-print_gc_info", argv[i]) ||
+ is_help_option(argv[i]))) {
+ if (is_help_option(argv[i])) {
+ print_help_text_and_exit(argv[0], 0);
+ } else if (ycf_string_is_equal("-use_gc", argv[i])) {
+ use_gc = true;
+ i++;
+ } else if(ycf_string_is_equal("-print_gc_info", argv[i])) {
+ scgc_enable_print_gc_info();
+ i++;
+ } else if(ycf_string_is_equal(argv[i], "-log_max_mem_usage")) {
+ ycf_enable_memory_tracking();
+ log_max_mem_usage = true;
+ i++;
+ log_max_mem_usage_file = (char*)argv[i];
+ i++;
+ }
+ }
+ int nr_of_params_to_remove = i - 1;
+ int ret;
+ if (!use_gc) {
+ ret = ycf_main(argc - nr_of_params_to_remove ,
+ argv + nr_of_params_to_remove);
+ } else {
+ ycf_enable_gc();
+ ret = scgc_start_gced_code(ycf_main,
+ argc - nr_of_params_to_remove,
+ argv + nr_of_params_to_remove,
+ ycf_raw_malloc,
+ ycf_free);
+ }
+ if(log_max_mem_usage){
+ ycf_malloc_log(log_max_mem_usage_file, "all");
+ }
+ return ret;
+}
diff --git a/erts/lib_src/yielding_c_fun/ycf_node.c b/erts/lib_src/yielding_c_fun/ycf_node.c
new file mode 100644
index 0000000000..517c7e991d
--- /dev/null
+++ b/erts/lib_src/yielding_c_fun/ycf_node.c
@@ -0,0 +1,915 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB and Kjell Winblad 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%
+ */
+
+/*
+ * Author: Kjell Winblad
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdint.h>
+#include "ycf_symbol.h"
+#include "ycf_node.h"
+#include "ycf_string.h"
+#include "ycf_lists.h"
+#include "ycf_yield_fun.h"
+#include "ycf_parser.h"
+
+GENERATE_LIST_FUNCTIONS(ycf_node)
+
+#define YCF_UNIQ_VARIABLE_NAME_NUMBER_PREFIX "_N"
+
+static int ycf_uniq_variable_name_number_counter = 0;
+
+static void uniqify_local_vars_in_node_rename_var_helper(ycf_node* n,
+ char* old_name,
+ char* new_name);
+static void uniqify_local_vars_in_scope_rename_var_helper(ycf_node_code_scope* s,
+ char* old_name,
+ char* new_name);
+
+
+static void uniqify_local_vars_in_expression_rename_var_helper(ycf_node_expression* n,
+ char* old_name,
+ char* new_name){
+ ycf_node* current = n->content.head;
+ while(current != NULL){
+ uniqify_local_vars_in_node_rename_var_helper(current,
+ old_name,
+ new_name);
+ current = current->next;
+ }
+}
+
+static void uniqify_local_vars_in_paran_expression_rename_var_helper(ycf_node_parentheses_expression* n,
+ char* old_name,
+ char* new_name){
+ uniqify_local_vars_in_expression_rename_var_helper(&n->content,
+ old_name,
+ new_name);
+}
+
+static void uniqify_local_vars_in_fun_call_rename_var_helper(ycf_node_function_call* n,
+ char* old_name,
+ char* new_name)
+{
+ ycf_node* e = n->parameter_expressions.head;
+ while(e != NULL){
+ uniqify_local_vars_in_node_rename_var_helper(e, old_name, new_name);
+ e = e->next;
+ }
+ if(ycf_symbol_is_text_eq(n->identifier, old_name)){
+ n->identifier = ycf_symbol_copy_change_text(n->identifier, new_name);
+ }
+}
+
+static void uniqify_local_vars_in_assignment_fun_call_rename_var_helper(ycf_node_function_call_assignment* n,
+ char* old_name,
+ char* new_name)
+{
+ uniqify_local_vars_in_expression_rename_var_helper(&n->left_side, old_name, new_name);
+ uniqify_local_vars_in_fun_call_rename_var_helper(&n->fun_call, old_name, new_name);
+}
+
+static void uniqify_local_vars_in_node_rename_var_helper(ycf_node* n,
+ char* old_name,
+ char* new_name){
+ if(n == NULL){
+ return;
+ } else if(n->type == ycf_node_type_code_scope){
+ uniqify_local_vars_in_scope_rename_var_helper(&n->u.code_scope, old_name, new_name);
+ } else if(n->type == ycf_node_type_on_restore_yield_state_code ||
+ n->type == ycf_node_type_on_save_yield_state_code ||
+ n->type == ycf_node_type_on_destroy_state_code ||
+ n->type == ycf_node_type_on_return_code ||
+ n->type == ycf_node_type_on_destroy_state_or_return_code){
+ uniqify_local_vars_in_scope_rename_var_helper(&n->u.special_code_block.code.if_statement->u.code_scope, old_name, new_name);
+ } else if(n->type == ycf_node_type_other &&
+ n->u.other.what->type == ycf_symbol_type_identifier &&
+ ycf_symbol_is_text_eq(n->u.other.what, old_name)){
+ n->u.other.what =
+ ycf_symbol_copy_change_text(n->u.other.what, new_name);
+ } else if(n->type == ycf_node_type_assignment){
+ uniqify_local_vars_in_expression_rename_var_helper(&n->u.a.left_side, old_name, new_name);
+ uniqify_local_vars_in_expression_rename_var_helper(&n->u.a.right_side, old_name, new_name);
+ } else if (n->type == ycf_node_type_function_call){
+ uniqify_local_vars_in_fun_call_rename_var_helper(&n->u.function_call, old_name, new_name);
+ } else if (n->type == ycf_node_type_statement){
+ ycf_node* e = n->u.statement.expression;
+ uniqify_local_vars_in_node_rename_var_helper(e, old_name, new_name);
+ } else if (n->type == ycf_node_type_return_statement && n->u.return_n.return_expression != NULL){
+ ycf_node* e = n->u.return_n.return_expression;
+ uniqify_local_vars_in_node_rename_var_helper(e, old_name, new_name);
+ } else if (n->type == ycf_node_type_expression){
+ uniqify_local_vars_in_expression_rename_var_helper(&n->u.expression, old_name, new_name);
+ } else if (n->type == ycf_node_type_assignment_function_call){
+ uniqify_local_vars_in_assignment_fun_call_rename_var_helper(&n->u.function_call_assignment, old_name, new_name);
+ }else if (n->type == ycf_node_type_parentheses_expression){
+ uniqify_local_vars_in_paran_expression_rename_var_helper(&n->u.parentheses_expression, old_name, new_name);
+ } else if (n->type == ycf_node_type_if){
+ uniqify_local_vars_in_paran_expression_rename_var_helper(&n->u.if_n.expression, old_name, new_name);
+ uniqify_local_vars_in_node_rename_var_helper(n->u.if_n.if_statement, old_name, new_name);
+ } else if (n->type == ycf_node_type_if_else){
+ uniqify_local_vars_in_paran_expression_rename_var_helper(&n->u.if_else.if_part.expression, old_name, new_name);
+ uniqify_local_vars_in_node_rename_var_helper(n->u.if_else.if_part.if_statement, old_name, new_name);
+ uniqify_local_vars_in_node_rename_var_helper(n->u.if_else.else_statement, old_name, new_name);
+ } else if (n->type == ycf_node_type_while){
+ uniqify_local_vars_in_paran_expression_rename_var_helper(&n->u.while_n.expression, old_name, new_name);
+ uniqify_local_vars_in_node_rename_var_helper(n->u.while_n.statement, old_name, new_name);
+ } else if (n->type == ycf_node_type_do_while){
+ uniqify_local_vars_in_paran_expression_rename_var_helper(&n->u.do_while.expression, old_name, new_name);
+ uniqify_local_vars_in_node_rename_var_helper(n->u.do_while.statement, old_name, new_name);
+ } else if (n->type == ycf_node_type_switch){
+ uniqify_local_vars_in_paran_expression_rename_var_helper(&n->u.switch_n.expression, old_name, new_name);
+ uniqify_local_vars_in_scope_rename_var_helper(&n->u.switch_n.scope, old_name, new_name);
+ } else if (n->type == ycf_node_type_for){
+ uniqify_local_vars_in_node_rename_var_helper(n->u.for_n.init, old_name, new_name);
+ uniqify_local_vars_in_node_rename_var_helper(n->u.for_n.stop_cond, old_name, new_name);
+ uniqify_local_vars_in_node_rename_var_helper(n->u.for_n.end_exp, old_name, new_name);
+ uniqify_local_vars_in_node_rename_var_helper(n->u.for_n.statement, old_name, new_name);
+ }
+}
+
+static void uniqify_local_vars_in_scope_rename_var_helper(ycf_node_code_scope* s,
+ char* old_name,
+ char* new_name){
+ ycf_node* current = s->other_nodes.head;
+ while(current != NULL){
+ uniqify_local_vars_in_node_rename_var_helper(current, old_name, new_name);
+ current = current->next;
+ }
+}
+
+
+
+static void uniqify_definitions_in_scope(ycf_node_list* definition_nodes, ycf_node_code_scope* s){
+ ycf_node* current = definition_nodes->head;
+ while(current != NULL){
+ ycf_symbol * old_symbol = NULL;
+ if(current->type == ycf_node_type_variable_definition){
+ old_symbol = current->u.definition.identifier;
+ }else if(current->type == ycf_node_type_variable_definition_init){
+ old_symbol = current->u.definition_init.definition.identifier;
+ }
+ ycf_uniq_variable_name_number_counter++;
+ {
+ char* old_name = ycf_symbol_get_text(old_symbol);
+ char* new_name = ycf_string_new("%s%s%lu", old_name, YCF_UNIQ_VARIABLE_NAME_NUMBER_PREFIX,
+ ycf_uniq_variable_name_number_counter);
+ if(current->type == ycf_node_type_variable_definition){
+ current->u.definition.identifier = ycf_symbol_copy_change_text(old_symbol, new_name);
+ }else if(current->type == ycf_node_type_variable_definition_init){
+ current->u.definition_init.definition.identifier = ycf_symbol_copy_change_text(old_symbol, new_name);
+ }
+ uniqify_local_vars_in_scope_rename_var_helper(s, old_name, new_name);
+ current = current->next;
+ }
+ }
+}
+
+static void uniqify_local_vars_in_scope(ycf_node_code_scope* s);
+
+static void uniqify_local_vars_in_node(ycf_node* n){
+ if(n->type == ycf_node_type_code_scope){
+ uniqify_local_vars_in_scope(&n->u.code_scope);
+ } else if(n->type == ycf_node_type_on_restore_yield_state_code){
+ uniqify_local_vars_in_scope(&n->u.special_code_block.code.if_statement->u.code_scope);
+ } else if(n->type == ycf_node_type_on_save_yield_state_code ||
+ n->type == ycf_node_type_on_save_yield_state_code ||
+ n->type == ycf_node_type_on_destroy_state_code ||
+ n->type == ycf_node_type_on_return_code ||
+ n->type == ycf_node_type_on_destroy_state_or_return_code){
+ uniqify_local_vars_in_scope(&n->u.special_code_block.code.if_statement->u.code_scope);
+ } else if (n->type == ycf_node_type_if){
+ uniqify_local_vars_in_node(n->u.if_n.if_statement);
+ } else if (n->type == ycf_node_type_if_else){
+ uniqify_local_vars_in_node(n->u.if_else.if_part.if_statement);
+ uniqify_local_vars_in_node(n->u.if_else.else_statement);
+ } else if (n->type == ycf_node_type_while){
+ uniqify_local_vars_in_node(n->u.while_n.statement);
+ } else if (n->type == ycf_node_type_do_while){
+ uniqify_local_vars_in_node(n->u.do_while.statement);
+ } else if (n->type == ycf_node_type_switch){
+ uniqify_local_vars_in_scope(&n->u.switch_n.scope);
+ } else if (n->type == ycf_node_type_for){
+ uniqify_local_vars_in_node(n->u.for_n.statement);
+ }
+}
+
+static void uniqify_local_vars_in_scope(ycf_node_code_scope* s){
+ ycf_node* current = s->other_nodes.head;
+ while(current != NULL){
+ uniqify_local_vars_in_node(current);
+ current = current->next;
+ }
+ uniqify_definitions_in_scope(&s->definition_nodes, s);
+}
+
+void ycf_uniqify_local_vars_in_function(ycf_node* function){
+ uniqify_local_vars_in_scope(&function->u.function.body);
+ uniqify_definitions_in_scope(&function->u.function.definition.parameters,
+ &function->u.function.body);
+}
+
+
+ycf_node* ycf_node_new_assignment_from_definition_init(ycf_node_definition_init* n){
+ ycf_node* res = ycf_malloc(sizeof(ycf_node));
+ res->type = ycf_node_type_assignment;
+ res->next = NULL;
+ res->u.a.assignment_symbol = n->definition.end;
+ {
+ ycf_symbol* ident = ycf_symbol_copy(n->definition.identifier);
+ ident->whitespace_or_comment_before =
+ n->definition.type_specifiers.head->whitespace_or_comment_before;
+ res->u.a.left_side = parse_expression(ident).result->u.expression;
+ }
+ res->u.a.right_side = parse_expression(n->initializer_expression.head).result->u.expression;
+ res->u.a.end = ycf_symbol_new_semicolon();
+ return res;
+}
+
+ycf_node* ycf_node_new_definition_from_definition_init(ycf_node_definition_init* n){
+ ycf_node* res = ycf_malloc(sizeof(ycf_node));
+ res->type = ycf_node_type_variable_definition;
+ res->next = NULL;
+ res->u.definition = n->definition;
+ res->u.definition.end = ycf_symbol_new_semicolon();
+ return res;
+}
+
+ycf_node* ycf_node_code_scope_shallow_copy(ycf_node* n){
+ ycf_node* new_scope = ycf_node_shallow_copy(n);
+ new_scope->u.code_scope.definition_nodes = ycf_node_list_shallow_copy(new_scope->u.code_scope.definition_nodes);
+ new_scope->u.code_scope.other_nodes = ycf_node_list_shallow_copy(new_scope->u.code_scope.other_nodes);
+ return new_scope;
+}
+
+
+void ycf_node_search_and_replace_statements_in_node(ycf_node* n, ycf_node* (*replacer) (ycf_node*,
+ ycf_node_code_scope*,
+ void*), void* context,
+ ycf_node_code_scope* s){
+ if(n->type == ycf_node_type_code_scope){
+ ycf_node_search_and_replace_statements_in_scope(&n->u.code_scope, replacer, context);
+ } else if(n->type == ycf_node_type_on_destroy_state_code ||
+ n->type == ycf_node_type_on_restore_yield_state_code ||
+ n->type == ycf_node_type_on_save_yield_state_code ||
+ n->type == ycf_node_type_on_destroy_state_or_return_code ||
+ n->type == ycf_node_type_on_return_code) {
+ ycf_node* replace_candidate = n->u.special_code_block.code.if_statement;
+ ycf_node* possible_replacement = replacer(replace_candidate, s, context);
+ if(replace_candidate != possible_replacement){
+ n->u.special_code_block.code.if_statement = possible_replacement;
+ } else {
+ ycf_node_search_and_replace_statements_in_node(n->u.special_code_block.code.if_statement,
+ replacer,
+ context,
+ s);
+ }
+ } else if (n->type == ycf_node_type_if){
+ ycf_node_search_and_replace_statements_in_node(n->u.if_n.if_statement, replacer, context, s);
+ } else if (n->type == ycf_node_type_if_else){
+ ycf_node_search_and_replace_statements_in_node(n->u.if_else.if_part.if_statement, replacer, context, s);
+ ycf_node_search_and_replace_statements_in_node(n->u.if_else.else_statement, replacer, context, s);
+ } else if (n->type == ycf_node_type_while){
+ ycf_node_search_and_replace_statements_in_node(n->u.while_n.statement, replacer, context, s);
+ } else if (n->type == ycf_node_type_do_while){
+ ycf_node_search_and_replace_statements_in_node(n->u.do_while.statement, replacer, context, s);
+ } else if (n->type == ycf_node_type_switch){
+ ycf_node_search_and_replace_statements_in_scope(&n->u.switch_n.scope, replacer, context);
+ } else if (n->type == ycf_node_type_for){
+ ycf_node_search_and_replace_statements_in_node(n->u.for_n.statement, replacer, context, s);
+ }
+}
+
+void ycf_node_search_and_replace_statements_in_scope(ycf_node_code_scope* s,
+ ycf_node* (*replacer) (ycf_node* canditate,
+ ycf_node_code_scope* candidates_scope,
+ void* context),
+ void* context){
+ ycf_node* n = s->other_nodes.head;
+ while(n != NULL){
+ ycf_node* replace_candidate = n;
+ ycf_node* possible_replacement = replacer(replace_candidate, s, context);
+ if(replace_candidate != possible_replacement){
+ ycf_node_list_replace(&s->other_nodes, replace_candidate, possible_replacement);
+ } else {
+ ycf_node_search_and_replace_statements_in_node(n, replacer, context, s);
+ }
+ n = n->next;
+ }
+ return;
+}
+
+ycf_node_code_scope ycf_node_copy_search_and_replace_statements_in_scope(ycf_node_code_scope s,
+ ycf_node* (*replacer) (ycf_node*,
+ ycf_node_code_scope*,
+ void*),
+ void* context){
+ ycf_node_search_and_replace_statements_in_scope(&s, replacer, context);
+ return s;
+}
+
+char* ycf_node_to_string(ycf_node* n){
+ ycf_string_printable_buffer* b = ycf_string_printable_buffer_new();
+ ycf_node_print(n, b);
+ return b->buffer;
+}
+
+char* ycf_node_list_to_string(ycf_node_list* l){
+ ycf_string_printable_buffer* b = ycf_string_printable_buffer_new();
+ ycf_node* current = l->head;
+ while(current != NULL){
+ ycf_node_print(current, b);
+ current = current->next;
+ }
+ return b->buffer;
+}
+
+ycf_node* ycf_node_new_text_node(char* text){
+ ycf_node* n = ycf_node_new();
+ n->type = ycf_node_type_other;
+ n->u.other.what = ycf_symbol_new_something_else(text);
+ return n;
+}
+
+ycf_node* ycf_node_new(){
+ ycf_node* new = ycf_malloc(sizeof(ycf_node));
+ new->next = NULL;
+ return new;
+}
+
+ycf_node* ycf_node_find_function(ycf_node* c_file_node, char* fun_name){
+ ycf_node* current = c_file_node->u.c_file.content.head;
+ while(current != NULL){
+ if(current->type == ycf_node_type_function_definition &&
+ ycf_symbol_is_text_eq(current->u.function.definition.definition.identifier,
+ fun_name)){
+ return current;
+ }
+ current = current->next;
+ }
+ return NULL;
+}
+
+ycf_node* ycf_node_find_function_declaration(ycf_node* c_file_node, char* fun_name){
+ ycf_node* current = c_file_node->u.c_file.content.head;
+ while(current != NULL){
+ if(current->type == ycf_node_type_function_declaration &&
+ ycf_symbol_is_text_eq(current->u.function_definition.definition.identifier,
+ fun_name)){
+ return current;
+ }
+ current = current->next;
+ }
+ return NULL;
+}
+
+ycf_node* ycf_node_find_define_node(ycf_node* c_file_node, char* define_name){
+ ycf_node* current = c_file_node->u.c_file.content.head;
+ while(current != NULL){
+ if(current->type == ycf_node_type_other &&
+ ycf_symbol_is_text_eq(current->u.other.what,
+ ycf_string_new("#define %s", define_name))){
+ return current;
+ }
+ current = current->next;
+ }
+ return NULL;
+}
+
+bool ycf_node_is_void_ret_fun(ycf_node* f_node){
+ ycf_symbol_list type = f_node->u.function.definition.definition.type_specifiers;
+ return type.last->type == ycf_symbol_type_void;
+}
+
+ycf_node* ycf_node_get_from_code_scope_text(char* code){
+ char* f_code = ycf_string_new("void f(){\n"
+ "{\n"
+ "%s\n"
+ "}\n"
+ "}\n",
+ code);
+ ycf_symbol_list symbols = ycf_symbol_list_from_text(f_code);
+ ycf_node* tree = get_abstract_syntax_tree_root(&symbols);
+ if(tree->u.c_file.content.head->type != ycf_node_type_function_definition){
+ printf("NOT A FUNCTION\n");
+ exit(1);
+ }
+ return tree->u.c_file.content.head->u.function.body.other_nodes.head;
+}
+
+ycf_node* ycf_node_get_function_from_text(char* code){
+ char* f_code = ycf_string_new("%s",
+ code);
+ ycf_symbol_list symbols = ycf_symbol_list_from_text(f_code);
+ ycf_node* tree = get_abstract_syntax_tree_root(&symbols);
+ if(tree->u.c_file.content.head->type != ycf_node_type_function_definition){
+ printf("ycf_get_function_from_text: NOT A FUNCTION\n");
+ exit(1);
+ }
+ return tree->u.c_file.content.head;
+}
+
+ycf_node* ycf_node_from_string(char* src){
+ ycf_symbol_list symbols = ycf_symbol_list_from_text(src);
+ ycf_node* tree = get_abstract_syntax_tree_root(&symbols);
+ return tree;
+}
+
+ycf_node* mk_scope_wrapper(ycf_node* statement){
+ return ycf_node_get_from_code_scope_text(ycf_string_new("{\n"
+ " %s\n"
+ "}\n",
+ ycf_node_to_string(statement)));
+}
+
+static ycf_node* scope_inserter(ycf_node* candidate, ycf_node_code_scope* s, void* context){
+ (void)context;
+ (void)s;
+ if (candidate->type == ycf_node_type_while){
+ if(candidate->u.while_n.statement->type == ycf_node_type_code_scope){
+ ycf_node_insert_scopes_in_complex_statements(&candidate->u.while_n.statement->u.code_scope);
+ }
+ candidate->u.while_n.statement = mk_scope_wrapper(candidate->u.while_n.statement);
+ return candidate;
+ } else if (candidate->type == ycf_node_type_do_while){
+ if(candidate->u.do_while.statement->type == ycf_node_type_code_scope){
+ ycf_node_insert_scopes_in_complex_statements(&candidate->u.do_while.statement->u.code_scope);
+ }
+ candidate->u.do_while.statement = mk_scope_wrapper(candidate->u.do_while.statement);
+ return candidate;
+ } else if (candidate->type == ycf_node_type_for){
+ if(candidate->u.for_n.statement->type == ycf_node_type_code_scope){
+ ycf_node_insert_scopes_in_complex_statements(&candidate->u.for_n.statement->u.code_scope);
+ }
+ candidate->u.for_n.statement = mk_scope_wrapper(candidate->u.for_n.statement);
+ return candidate;
+ } else if (candidate->type == ycf_node_type_if){
+ if(candidate->u.if_n.if_statement->type == ycf_node_type_code_scope){
+ ycf_node_insert_scopes_in_complex_statements(&candidate->u.if_n.if_statement->u.code_scope);
+ }
+ candidate->u.if_n.if_statement = mk_scope_wrapper(candidate->u.if_n.if_statement);
+ return candidate;
+ } else if (candidate->type == ycf_node_type_if_else){
+ if(candidate->u.if_else.if_part.if_statement->type == ycf_node_type_code_scope){
+ ycf_node_insert_scopes_in_complex_statements(&candidate->u.if_else.if_part.if_statement->u.code_scope);
+ }
+ candidate->u.if_else.if_part.if_statement = mk_scope_wrapper(candidate->u.if_else.if_part.if_statement);
+ if(candidate->u.if_else.else_statement->type == ycf_node_type_code_scope){
+ ycf_node_insert_scopes_in_complex_statements(&candidate->u.if_else.else_statement->u.code_scope);
+ }
+ candidate->u.if_else.else_statement = mk_scope_wrapper(candidate->u.if_else.else_statement);
+ return candidate;
+ } else {
+ return candidate;
+ }
+}
+
+void ycf_node_insert_scopes_in_complex_statements(ycf_node_code_scope* s){
+ ycf_node_search_and_replace_statements_in_scope(s,
+ scope_inserter,
+ NULL);
+}
+
+
+static ycf_node* scope_remover(ycf_node* candidate, ycf_node_code_scope* s, void* context){
+ uintptr_t* removed_scopes = context;
+ if(candidate->type == ycf_node_type_code_scope &&
+ ycf_node_list_length(candidate->u.code_scope.definition_nodes) == 0 &&
+ ycf_node_list_length(candidate->u.code_scope.other_nodes) == 1 &&
+ candidate->u.code_scope.other_nodes.head->type == ycf_node_type_code_scope) {
+ *removed_scopes = *removed_scopes + 1;
+ return candidate->u.code_scope.other_nodes.head;
+ }
+ return candidate;
+}
+
+void ycf_node_remove_unecessary_scopes(ycf_node_code_scope* s){
+ uintptr_t removed_scopes;
+ do{
+ removed_scopes = 0;
+ ycf_node_search_and_replace_statements_in_scope(s,
+ scope_remover,
+ &removed_scopes);
+ }while(removed_scopes > 0);
+}
+
+static ycf_node* for_declaration_normalizer(ycf_node* candidate,
+ ycf_node_code_scope* s,
+ void* context){
+ (void)context;
+ (void)s;
+ if (candidate->type == ycf_node_type_for){
+ if(candidate->u.for_n.init != NULL &&
+ (candidate->u.for_n.init->type == ycf_node_type_variable_definition ||
+ candidate->u.for_n.init->type == ycf_node_type_variable_definition_init)){
+ if(candidate->u.for_n.statement->type == ycf_node_type_code_scope){
+ ycf_node_normalize_for_var_declarations(&candidate->u.for_n.statement->u.code_scope);
+ }
+ ycf_string_printable_buffer * b = ycf_string_printable_buffer_new();
+ ycf_node_print(candidate->u.for_n.init, b);
+ candidate->u.for_n.init = ycf_node_statement_new(ycf_node_expression_new(ycf_node_list_empty()),
+ ycf_symbol_new_semicolon());
+ ycf_node_print(candidate, b);
+ return ycf_node_get_from_code_scope_text(b->buffer);
+ }else {
+ return candidate;
+ }
+
+ } else {
+ return candidate;
+ }
+}
+
+void ycf_node_normalize_for_var_declarations(ycf_node_code_scope* s){
+ ycf_node_search_and_replace_statements_in_scope(s,
+ for_declaration_normalizer,
+ NULL);
+}
+
+
+
+static ycf_node* ycf_node_move_in_code_var_declarations_to_top_mover(ycf_node* candidate,
+ ycf_node_code_scope* candidates_scope,
+ void* context){
+ (void)context;
+ if (candidate->type == ycf_node_type_variable_definition) {
+ ycf_node_list_append(&candidates_scope->definition_nodes,
+ ycf_node_shallow_copy(candidate));
+ return ycf_node_get_from_code_scope_text(ycf_string_new("\n/* moved declaration (%s)*/\n",
+ ycf_symbol_get_text(candidate->u.definition.identifier)));
+ } else if (candidate->type == ycf_node_type_variable_definition_init) {
+ ycf_node_list_append(&candidates_scope->definition_nodes,
+ ycf_node_new_definition_from_definition_init(&candidate->u.definition_init));
+ return ycf_node_new_assignment_from_definition_init(&candidate->u.definition_init);
+ } else {
+ return candidate;
+ }
+}
+
+void ycf_node_move_in_code_var_declarations_to_top(ycf_node_code_scope* s){
+ ycf_node_search_and_replace_statements_in_scope(s,
+ ycf_node_move_in_code_var_declarations_to_top_mover,
+ NULL);
+}
+
+static ycf_node* ycf_node_remove_declarations_in_scope_helper(ycf_node* candidate,
+ ycf_node_code_scope* candidates_scope,
+ void* context){
+ (void)context;
+ candidates_scope->definition_nodes = ycf_node_list_empty();
+ return candidate;
+}
+
+void ycf_node_remove_declarations_in_scope(ycf_node_code_scope* s){
+ ycf_node_search_and_replace_statements_in_scope(s,
+ ycf_node_remove_declarations_in_scope_helper,
+ NULL);
+}
+
+static void normalize_init_definitions_in_scope_helper_insert_assignments(ycf_node* current_def,
+ ycf_node_list* other_nodes){
+ if(current_def == NULL){
+ return;
+ }
+ normalize_init_definitions_in_scope_helper_insert_assignments(current_def->next,
+ other_nodes);
+ if(current_def->type == ycf_node_type_variable_definition_init){
+ ycf_node* assignment = ycf_node_new_assignment_from_definition_init(&current_def->u.definition_init);
+ ycf_node_list_prepend(other_nodes, assignment);
+ return;
+ }else{
+ return;
+ }
+}
+
+static ycf_node* ycf_node_normalize_init_definitions_in_scope_helper(ycf_node* candidate,
+ ycf_node_code_scope* candidates_scope,
+ void* context){
+ (void)context;
+ (void)candidates_scope;
+ ycf_node_code_scope* s = NULL;
+ if(candidate == NULL){
+ s = context;
+ } else if(candidate->type == ycf_node_type_code_scope){
+ s = &candidate->u.code_scope;
+
+ } else if(candidate->type == ycf_node_type_switch){
+ s = &candidate->u.switch_n.scope;
+ }
+ if(s != NULL) {
+ ycf_node * current = s->definition_nodes.head;
+ normalize_init_definitions_in_scope_helper_insert_assignments(s->definition_nodes.head,
+ &s->other_nodes);
+ while (current != NULL) {
+ if (current->type == ycf_node_type_variable_definition_init) {
+ ycf_node *def_node = ycf_node_new_definition_from_definition_init(&current->u.definition_init);
+ ycf_node_list_replace(&s->definition_nodes, current, def_node);
+ }
+ current = current->next;
+ }
+ }
+ return candidate;
+}
+
+void ycf_node_normalize_init_definitions_in_scope(ycf_node_code_scope* s){
+ ycf_node_normalize_init_definitions_in_scope_helper(NULL,
+ NULL,
+ s);
+ ycf_node_search_and_replace_statements_in_scope(s,
+ ycf_node_normalize_init_definitions_in_scope_helper,
+ NULL);
+}
+
+
+static ycf_node* ycf_node_get_declarations_in_scope_helper(ycf_node* candidate,
+ ycf_node_code_scope* candidates_scope,
+ void* context){
+ ycf_node_list* list = (ycf_node_list*)context;
+ if(candidate->type == ycf_node_type_code_scope){
+ ycf_node_list to_append = ycf_node_list_shallow_copy(candidate->u.code_scope.definition_nodes);
+ ycf_node_list_concat(list, &to_append);
+ } else if(candidate->type == ycf_node_type_switch){
+ ycf_node_list to_append = ycf_node_list_shallow_copy(candidate->u.switch_n.scope.definition_nodes);
+ ycf_node_list_concat(list, &to_append);
+ }
+ return candidate;
+}
+
+ycf_node_list ycf_node_get_declarations_in_scope(ycf_node_code_scope* s){
+ ycf_node_list res = s->definition_nodes;
+ ycf_node_search_and_replace_statements_in_scope(s,
+ ycf_node_get_declarations_in_scope_helper,
+ &res);
+ return res;
+}
+
+ycf_node_list ycf_node_definition_list_from_string(char* str){
+ return ycf_node_get_from_code_scope_text(str)->u.code_scope.definition_nodes;
+}
+
+void ycf_node_rename_function(ycf_node_function* f, char* new_name){
+ f->definition.definition.identifier =
+ ycf_symbol_copy_change_text(f->definition.definition.identifier,
+ new_name);
+}
+
+static bool is_scope_ends_with_void_ret(ycf_node_code_scope* scope_node){
+ if(scope_node->other_nodes.last->type == ycf_node_type_code_scope){
+ return is_scope_ends_with_void_ret(&scope_node->other_nodes.last->u.code_scope);
+ }else{
+ ycf_node* prev = NULL;
+ ycf_node* current = scope_node->other_nodes.head;
+ while(current != NULL){
+ prev = current;
+ current = current->next;
+ }
+ return
+ prev != NULL &&
+ current != NULL &&
+ prev->type == ycf_node_type_other &&
+ prev->u.other.what->type == ycf_symbol_type_return &&
+ current->type == ycf_node_type_other &&
+ current->u.other.what->type == ycf_symbol_type_semicolon;
+ }
+}
+
+bool ycf_node_is_void_ret_ending_fun(ycf_node* f_node){
+ return is_scope_ends_with_void_ret(&f_node->u.function.body);
+}
+
+ycf_node_list ycf_node_get_all_definitions_in_function(ycf_node_function* f){
+ ycf_node_list all = ycf_node_get_declarations_in_scope(&f->body);
+ ycf_node_list all_copy = ycf_node_list_shallow_copy(all);
+ ycf_node_list params = f->definition.parameters;
+ ycf_node_list_concat(&all_copy, &params);
+ return all_copy;
+}
+
+void ycf_node_remove_const_specifiers_from_declaration(ycf_node* declaration){
+ ycf_symbol* current = declaration->u.definition.type_specifiers.head;
+ while(current != NULL) {
+ if(current->type == ycf_symbol_type_const){
+ ycf_symbol_list_remove(&declaration->u.definition.type_specifiers,
+ current);
+ }
+ current = current->next;
+ }
+}
+
+void ycf_node_remove_static_specifiers_from_declaration(ycf_node* declaration){
+ ycf_symbol* current = declaration->u.definition.type_specifiers.head;
+ while(current != NULL) {
+ if(current->type == ycf_symbol_type_static){
+ ycf_symbol_list_remove(&declaration->u.definition.type_specifiers,
+ current);
+ }
+ current = current->next;
+ }
+}
+
+void ycf_node_remove_inline_specifiers_from_declaration(ycf_node* declaration){
+ ycf_symbol* current = declaration->u.definition.type_specifiers.head;
+ while(current != NULL) {
+ if(current->type == ycf_symbol_type_inline){
+ ycf_symbol_list_remove(&declaration->u.definition.type_specifiers,
+ current);
+ }
+ current = current->next;
+ }
+}
+
+void ycf_node_remove_array_size_info_from_declaration(ycf_node* declaration){
+ ycf_node* current = declaration->u.definition.array_brackets.head;
+ while(current != NULL) {
+ current->u.array_bracket.empty = true;
+ current = current->next;
+ }
+}
+
+void ycf_node_modify_declarations(ycf_node_list declarations, void (*modifer)(ycf_node*)){
+ ycf_node* current = declarations.head;
+ while(current != NULL) {
+ modifer(current);
+ current = current->next;
+ }
+}
+
+ycf_symbol_list ycf_node_get_return_type(ycf_node* f_node_or_f_dec_node){
+ ycf_node_function_definition def;
+ ycf_node * tmp;
+ if (f_node_or_f_dec_node->type == ycf_node_type_function_declaration) {
+ def = f_node_or_f_dec_node->u.function_definition;
+ } else {
+ def = f_node_or_f_dec_node->u.function.definition;
+ }
+ tmp =
+ ycf_node_defenition_new(ycf_symbol_list_shallow_copy(def.definition.type_specifiers),
+ ycf_symbol_new_identifier(ycf_string_new("tmp")),
+ ycf_node_list_empty(),
+ ycf_symbol_new_semicolon());
+ ycf_node_remove_inline_specifiers_from_declaration(tmp);
+ ycf_node_remove_static_specifiers_from_declaration(tmp);
+ return tmp->u.definition.type_specifiers;
+}
+
+ycf_symbol_list ycf_node_find_function_return_type(ycf_node* c_file_node, char* fun_name)
+{
+ ycf_node* f_node_or_f_dec_node = ycf_node_find_function(c_file_node, fun_name);
+ if (f_node_or_f_dec_node == NULL) {
+ f_node_or_f_dec_node = ycf_node_find_function_declaration(c_file_node, fun_name);
+ }
+ if (f_node_or_f_dec_node == NULL) {
+ fprintf(stderr, "ycf_node_find_function_return_type: Could not find function declaration or definition: %s\n", fun_name);
+ exit(1);
+ }
+ return ycf_node_get_return_type(f_node_or_f_dec_node);
+}
+
+char* ycf_node_get_node_type_string(ycf_node_type t){
+ switch (t){
+ case ycf_node_type_c_file:
+ return "ycf_node_type_c_file";
+ case ycf_node_type_variable_definition:
+ return "ycf_node_type_variable_definition";
+ case ycf_node_type_variable_definition_init:
+ return "ycf_node_type_variable_definition_init";
+ case ycf_node_type_function_definition:
+ return "ycf_node_type_function_definition";
+ case ycf_node_type_function_declaration:
+ return "ycf_node_type_function_declaration";
+ case ycf_node_type_code_scope:
+ return "ycf_node_type_code_scope";
+ case ycf_node_type_other:
+ return "ycf_node_type_other";
+ case ycf_node_type_gen_typedef_struct:
+ return "ycf_node_type_gen_typedef_struct";
+ case ycf_node_type_expression:
+ return "ycf_node_type_expression";
+ case ycf_node_type_statement:
+ return "ycf_node_type_statement";
+ case ycf_node_type_return_statement:
+ return "ycf_node_type_return_statement";
+ case ycf_node_type_assignment:
+ return "ycf_node_type_assignment";
+ case ycf_node_type_yield:
+ return "ycf_node_type_yield";
+ case ycf_node_type_consume_reds:
+ return "ycf_node_type_consume_reds";
+ case ycf_node_type_goto:
+ return "ycf_node_type_goto";
+ case ycf_node_type_parentheses_expression:
+ return "ycf_node_type_parentheses_expression";
+ case ycf_node_type_function_call:
+ return "ycf_node_type_function_call";
+ case ycf_node_type_assignment_function_call:
+ return "ycf_node_type_assignment_function_call";
+ case ycf_node_type_while:
+ return "ycf_node_type_while";
+ case ycf_node_type_do_while:
+ return "ycf_node_type_do_while";
+ case ycf_node_type_for:
+ return "ycf_node_type_for";
+ case ycf_node_type_switch:
+ return "ycf_node_type_switch";
+ case ycf_node_type_if:
+ return "ycf_node_type_if";
+ case ycf_node_type_if_else:
+ return "ycf_node_type_if_else";
+ case ycf_node_type_comma:
+ return "ycf_node_type_comma";
+ case ycf_node_type_array_bracket:
+ return "ycf_node_type_array_bracket";
+ case ycf_node_type_macro_cmd:
+ return "ycf_node_type_macro_cmd";
+ case ycf_node_type_period_field_access:
+ return "ycf_node_type_period_field_access";
+ case ycf_node_type_pointer_field_access:
+ return "ycf_node_type_pointer_field_access";
+ case ycf_node_type_on_save_yield_state_code:
+ return "ycf_node_type_on_save_yield_state_code";
+ case ycf_node_type_on_restore_yield_state_code:
+ return "ycf_node_type_on_restore_yield_state_code";
+ case ycf_node_type_on_destroy_state_code:
+ return "ycf_node_type_on_destroy_state_code";
+ case ycf_node_type_on_return_code:
+ return "ycf_node_type_on_return_code";
+ case ycf_node_type_on_destroy_state_or_return_code:
+ return "ycf_node_type_on_destroy_state_or_return_code";
+ }
+ return "unrecognized type";
+}
+
+void ycf_node_print_node_type(ycf_node_type t){
+ printf("%s\n",ycf_node_get_node_type_string(t));
+}
+
+ycf_node_assignment* ycf_node_get_assignment(ycf_node* n) {
+ if(n->type == ycf_node_type_assignment){
+ return &n->u.a;
+ } else if (n->type == ycf_node_type_statement &&
+ n->u.statement.expression->u.expression.content.head->type == ycf_node_type_assignment){
+ return &n->u.statement.expression->u.expression.content.head->u.a;
+ } else {
+ fprintf(stderr,
+ "Trying to get %s from a node of type %s\n",
+ "ycf_node_type_assignment",
+ ycf_node_get_node_type_string(n->type));
+ exit(1);
+ }
+}
+
+void ycf_node_normalize_function(ycf_node* fun){
+ /* Remove array size info from parameters */
+ ycf_node_modify_declarations(fun->u.function.definition.parameters,
+ ycf_node_remove_array_size_info_from_declaration);
+ /* Insert scope in nest statements (e.g., if(1) print(); -> if(1) {print();}) */
+ ycf_node_insert_scopes_in_complex_statements(&fun->u.function.body);
+ /* Move out declarations from for loops */
+ ycf_node_normalize_for_var_declarations(&fun->u.function.body);
+ /* Move in code declations to top of scope */
+ ycf_node_move_in_code_var_declarations_to_top(&fun->u.function.body);
+ /* Normalize declarations */
+ ycf_node_normalize_init_definitions_in_scope(&fun->u.function.body);
+ /* Uniqify local variables */
+ ycf_uniqify_local_vars_in_function(fun);
+ /* Save scope declarations and function declarations (includes
+ parameters) */
+ ycf_node_list scope_defs = ycf_node_get_declarations_in_scope(&fun->u.function.body);
+ scope_defs = ycf_node_list_shallow_copy(scope_defs);
+ ycf_node_list defs = ycf_node_get_all_definitions_in_function(&fun->u.function);
+ defs = ycf_node_list_shallow_copy(defs);
+ /* Remove const from const declarations */
+ ycf_node_modify_declarations(defs, ycf_node_remove_const_specifiers_from_declaration);
+ ycf_node_modify_declarations(scope_defs, ycf_node_remove_const_specifiers_from_declaration);
+ ycf_node_modify_declarations(fun->u.function.definition.parameters,
+ ycf_node_remove_const_specifiers_from_declaration);
+ /* Move all declarations to top scope */
+ ycf_node_remove_declarations_in_scope(&fun->u.function.body);
+ fun->u.function.body.definition_nodes = scope_defs;
+ /* Add return statement if function is missing return statement */
+ if(ycf_node_is_void_ret_fun(fun) &&
+ !ycf_node_is_void_ret_ending_fun(fun)){
+ ycf_node_list_append(&fun->u.function.body.other_nodes,
+ ycf_node_get_from_code_scope_text("return;"));
+ }
+ ycf_node_remove_unecessary_scopes(&fun->u.function.body);
+}
diff --git a/erts/lib_src/yielding_c_fun/ycf_node.h b/erts/lib_src/yielding_c_fun/ycf_node.h
new file mode 100644
index 0000000000..d230a1a2c2
--- /dev/null
+++ b/erts/lib_src/yielding_c_fun/ycf_node.h
@@ -0,0 +1,455 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB and Kjell Winblad 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%
+ */
+
+/*
+ * Author: Kjell Winblad
+ */
+
+#ifndef YIELDING_C_FUN_YCF_NODE_FUNS_H
+#define YIELDING_C_FUN_YCF_NODE_FUNS_H
+
+#include "ycf_utils.h"
+#include "ycf_string.h"
+#include "ycf_symbol.h"
+
+/* Types for nodes */
+typedef enum {
+ ycf_node_type_c_file,
+ ycf_node_type_variable_definition,
+ ycf_node_type_variable_definition_init,
+ ycf_node_type_function_definition,
+ ycf_node_type_function_declaration,
+ ycf_node_type_code_scope,
+ ycf_node_type_other,
+ ycf_node_type_gen_typedef_struct,
+ ycf_node_type_expression,
+ ycf_node_type_statement,
+ ycf_node_type_return_statement,
+ ycf_node_type_assignment,
+ ycf_node_type_yield,
+ ycf_node_type_consume_reds,
+ ycf_node_type_goto,
+ ycf_node_type_parentheses_expression,
+ ycf_node_type_function_call,
+ ycf_node_type_assignment_function_call,
+ ycf_node_type_while,
+ ycf_node_type_do_while,
+ ycf_node_type_for,
+ ycf_node_type_switch,
+ ycf_node_type_if,
+ ycf_node_type_if_else,
+ ycf_node_type_comma,
+ ycf_node_type_array_bracket,
+ ycf_node_type_macro_cmd,
+ ycf_node_type_period_field_access,
+ ycf_node_type_pointer_field_access,
+ ycf_node_type_on_save_yield_state_code,
+ ycf_node_type_on_restore_yield_state_code,
+ ycf_node_type_on_destroy_state_code,
+ ycf_node_type_on_return_code,
+ ycf_node_type_on_destroy_state_or_return_code
+} ycf_node_type;
+
+struct ycf_node;
+
+typedef struct {
+ struct ycf_node* head;
+ struct ycf_node* last;
+} ycf_node_list;
+
+typedef struct {
+ ycf_node_list content;
+} ycf_node_c_file;
+
+typedef struct {
+ ycf_node_list content;
+ ycf_symbol* end_symbol;
+} ycf_node_expression;
+
+typedef struct {
+ ycf_symbol* start_symbol;
+ ycf_node_expression content;
+ ycf_symbol* end_symbol;
+} ycf_node_parentheses_expression;
+
+typedef struct {
+ ycf_symbol_list neg_symbols;
+ ycf_symbol* identifier;
+ ycf_symbol* start_symbol;
+ ycf_node_list parameter_expressions;
+ ycf_symbol* end_symbol;
+} ycf_node_function_call;
+
+typedef struct array_bracket {
+ ycf_symbol* start;
+ bool empty;
+ ycf_node_expression content;
+ ycf_symbol* end;
+} ycf_node_array_bracket;
+
+
+typedef struct {
+ ycf_symbol_list type_specifiers;
+ ycf_symbol* identifier;
+ ycf_node_list array_brackets;
+ ycf_symbol* end;
+} ycf_node_definition;
+
+typedef struct {
+ ycf_node_definition definition;
+ ycf_symbol_list initializer_expression;
+ ycf_symbol* end;
+} ycf_node_definition_init;
+
+typedef struct {
+ struct ycf_node* expression;
+ ycf_symbol* end_symbol;
+} ycf_node_statement;
+
+typedef struct {
+ ycf_node_list definition_nodes;
+ ycf_node_list other_nodes;
+ ycf_symbol* start;
+ ycf_symbol* end;
+} ycf_node_code_scope;
+
+typedef struct {
+ ycf_symbol* while_word;
+ ycf_node_parentheses_expression expression;
+ struct ycf_node* statement;
+} ycf_node_while;
+
+typedef struct {
+ ycf_symbol* do_word;
+ struct ycf_node* statement;
+ ycf_symbol* while_word;
+ ycf_node_parentheses_expression expression;
+ ycf_symbol* end;
+} ycf_node_do_while;
+
+typedef struct {
+ ycf_symbol* for_word;
+ ycf_symbol* start_parentheses;
+ struct ycf_node* init;
+ struct ycf_node* stop_cond;
+ ycf_symbol* stop_cond_end;
+ struct ycf_node* end_exp;
+ ycf_symbol* end_parentheses;
+ struct ycf_node* statement;
+} ycf_node_for;
+
+typedef struct {
+ ycf_symbol* switch_word;
+ ycf_node_parentheses_expression expression;
+ ycf_node_code_scope scope;
+} ycf_node_switch;
+
+typedef struct {
+ ycf_symbol* if_word;
+ ycf_node_parentheses_expression expression;
+ struct ycf_node* if_statement;
+} ycf_node_if;
+
+typedef struct {
+ ycf_node_if if_part;
+ ycf_symbol* else_word;
+ struct ycf_node* else_statement;
+} ycf_node_if_else;
+
+typedef struct {
+ ycf_node_expression left_side;
+ ycf_symbol* assignment_symbol;
+ ycf_node_expression right_side;
+ ycf_symbol* end;
+} ycf_node_assignment;
+
+/*typedef struct {
+ ycf_symbol* id;
+} ycf_node_identifier;*/
+
+typedef struct {
+ ycf_symbol* yield_symbol;
+ ycf_symbol* end_symbol;
+} ycf_node_yield;
+
+typedef struct {
+ ycf_symbol* consume_reds_symbol;
+ ycf_node_parentheses_expression nr_of_reds_expression;
+ ycf_symbol* end_symbol;
+} ycf_node_consume_reds;
+
+typedef struct {
+ ycf_symbol* what;
+} ycf_node_other;
+
+typedef struct {
+ ycf_node_definition definition;
+ int ignore_param_ending;
+ ycf_node_list parameters;
+ ycf_symbol_list end;
+} ycf_node_function_definition;
+
+typedef struct {
+ ycf_node_function_definition definition;
+ ycf_node_code_scope body;
+} ycf_node_function;
+
+typedef struct {
+ ycf_node_expression left_side;
+ ycf_symbol* assignment_symbol;
+ ycf_node_function_call fun_call;
+} ycf_node_function_call_assignment;
+
+typedef struct {
+ ycf_node_list definition_nodes;
+ char* name;
+} ycf_node_gen_typedef_struct;
+
+typedef struct {
+ ycf_symbol* comma_symbol;
+} ycf_node_comma;
+
+typedef struct {
+ ycf_symbol* symbol;
+} ycf_node_macro_cmd;
+
+typedef struct {
+ struct ycf_symbol* start;
+ ycf_node_if code;
+ struct ycf_symbol* end;
+} ycf_node_special_code_block;
+
+
+typedef struct {
+ struct ycf_symbol* goto_symbol;
+ struct ycf_symbol* label_symbol;
+ struct ycf_symbol* end_symbol;
+} ycf_node_goto;
+
+typedef struct {
+ struct ycf_symbol* return_symbol;
+ struct ycf_node* return_expression;
+ struct ycf_symbol* end_symbol;
+} ycf_node_return;
+
+typedef struct {
+ struct ycf_symbol* period;
+ struct ycf_symbol* field_name;
+} ycf_node_period_field_access;
+
+typedef struct {
+ struct ycf_symbol* pointer;
+ struct ycf_symbol* field_name;
+} ycf_pointer_field_access;
+
+typedef struct ycf_node {
+ ycf_node_type type;
+ struct ycf_node* next;
+ union {
+ ycf_node_c_file c_file;
+ ycf_node_definition definition;
+ ycf_node_definition_init definition_init;
+ ycf_node_code_scope code_scope;
+ ycf_node_other other;
+ ycf_node_function function;
+ ycf_node_function_definition function_definition;
+ ycf_node_assignment a;
+ ycf_node_gen_typedef_struct gen_typedef_struct;
+ ycf_node_yield yield;
+ ycf_node_consume_reds consume_reds;
+ ycf_node_expression expression;
+ ycf_node_parentheses_expression parentheses_expression;
+ ycf_node_function_call function_call;
+ ycf_node_statement statement;
+ ycf_node_while while_n;
+ ycf_node_do_while do_while;
+ ycf_node_for for_n;
+ ycf_node_switch switch_n;
+ ycf_node_if if_n;
+ ycf_node_if_else if_else;
+ ycf_node_function_call_assignment function_call_assignment;
+ ycf_node_comma comma;
+ ycf_node_array_bracket array_bracket;
+ ycf_node_macro_cmd macro_cmd;
+ ycf_node_special_code_block special_code_block;
+ ycf_node_goto goto_n;
+ ycf_node_return return_n;
+ ycf_node_period_field_access period_field_access;
+ ycf_pointer_field_access pointer_field_access;
+ } u;
+} ycf_node;
+
+void ycf_node_search_and_replace_statements_in_scope(ycf_node_code_scope* s,
+ ycf_node* (*replacer) (ycf_node* canditate,
+ ycf_node_code_scope* candidates_scope,
+ void* context),
+ void* context);
+
+char* ycf_node_to_string(ycf_node* n);
+ycf_node* ycf_node_new_text_node(char* text);
+ycf_node* ycf_node_new();
+ycf_node* ycf_node_find_function(ycf_node* c_file_node, char* fun_name);
+ycf_node* ycf_node_find_function_declaration(ycf_node* c_file_node, char* fun_name);
+ycf_node* ycf_node_find_define_node(ycf_node* c_file_node, char* define_name);
+bool ycf_node_is_void_ret_fun(ycf_node* f_node);
+ycf_node* ycf_node_get_from_code_scope_text(char* code);
+ycf_node* ycf_node_get_function_from_text(char* code);
+void ycf_node_print(ycf_node* node, ycf_string_printable_buffer* b);
+ycf_node* ycf_node_deep_copy(ycf_node *n);
+ycf_node* ycf_node_from_string(char* src);
+void ycf_node_insert_scopes_in_complex_statements(ycf_node_code_scope* s);
+void ycf_node_normalize_for_var_declarations(ycf_node_code_scope* s);
+void ycf_node_move_in_code_var_declarations_to_top(ycf_node_code_scope* s);
+void ycf_node_remove_declarations_in_scope(ycf_node_code_scope* s);
+ycf_node_list ycf_node_get_declarations_in_scope(ycf_node_code_scope* s);
+void ycf_node_normalize_init_definitions_in_scope(ycf_node_code_scope* s);
+ycf_node_list ycf_node_get_all_definitions_in_function(ycf_node_function* f);
+void ycf_uniqify_local_vars_in_function(ycf_node* function);
+
+ycf_node* ycf_node_new_assignment_from_definition_init(ycf_node_definition_init* n);
+ycf_node* ycf_node_new_definition_from_definition_init(ycf_node_definition_init* n);
+ycf_node* ycf_node_defenition_new(ycf_symbol_list type_spec,
+ ycf_symbol* id,
+ ycf_node_list array_brackets,
+ ycf_symbol* end);
+ycf_node* ycf_node_defenition_with_init_new(ycf_node_definition def,
+ ycf_symbol_list expression,
+ ycf_symbol* end);
+ycf_node* ycf_node_c_file_new(ycf_node_list content);
+ycf_node* ycf_node_function_def_new(ycf_node_definition def_node,
+ ycf_node_list parameters,
+ bool ignore_param_ending,
+ ycf_symbol_list end);
+ycf_node* ycf_node_yield_new(ycf_symbol* yield_symbol,
+ ycf_symbol* end);
+ycf_node* ycf_node_consume_reds_new(ycf_symbol* consume_reds_symbol,
+ ycf_node_parentheses_expression nr_of_reds_expression,
+ ycf_symbol* end_symbol);
+ycf_node* ycf_node_other_new(ycf_symbol* other_symbol);
+ycf_node* ycf_node_scope_new(ycf_symbol* start,
+ ycf_node_list declaration_nodes,
+ ycf_node_list other_nodes,
+ ycf_symbol* end);
+ycf_node* ycf_node_function_new(ycf_node_function_definition fun_def,
+ ycf_node_code_scope body);
+ycf_node* ycf_node_function_call_new(ycf_symbol_list neg_symbols,
+ ycf_symbol* ident,
+ ycf_symbol* paran_start,
+ ycf_node_list parameters,
+ ycf_symbol* paran_end);
+ycf_node* ycf_node_paran_expression_new(ycf_symbol* start, ycf_node_expression expr, ycf_symbol* end);
+ycf_node* ycf_node_expression_new(ycf_node_list expr);
+ycf_node* ycf_node_statement_new(ycf_node* expression,
+ ycf_symbol* end_symbol);
+ycf_node* ycf_node_while_new(ycf_symbol* while_word,
+ ycf_node_parentheses_expression expression,
+ ycf_node* statement);
+ycf_node* ycf_node_do_while_new(ycf_symbol* do_word,
+ ycf_node* statm,
+ ycf_symbol* while_word,
+ ycf_node_parentheses_expression expression,
+ ycf_symbol* end);
+ycf_node* ycf_node_for_new(ycf_symbol* for_word,
+ ycf_symbol* start_paran,
+ struct ycf_node* init,
+ ycf_node* stop_cond,
+ ycf_symbol* stop_cond_end,
+ ycf_node* end_exp,
+ ycf_symbol* end_paran,
+ ycf_node* statem);
+ycf_node* ycf_node_switch_new(ycf_symbol* switch_word,
+ ycf_node_parentheses_expression expression,
+ ycf_node_code_scope scope);
+ycf_node* ycf_node_if_new(ycf_symbol* if_word,
+ ycf_node_parentheses_expression expression,
+ struct ycf_node* if_statem);
+ycf_node* ycf_node_if_else_new(ycf_node_if if_n,
+ ycf_symbol* else_word,
+ struct ycf_node* else_statement);
+ycf_node* ycf_node_fun_call_assignment_new(ycf_node_expression left_side,
+ ycf_symbol* assignment_symbol,
+ ycf_node_function_call fun_call);
+ycf_node* ycf_node_comma_new(ycf_symbol* comma_symbol);
+ycf_node* ycf_node_array_bracket_new(ycf_symbol* start,
+ bool empty,
+ ycf_node_expression content,
+ ycf_symbol* end);
+ycf_node* ycf_node_macro_cmd_new(ycf_symbol* macro_symbol);
+ycf_node* ycf_node_special_code_block_new(ycf_node_type type,
+ ycf_symbol* start,
+ ycf_node_if code,
+ ycf_symbol* end);
+ycf_node* ycf_node_goto_new(ycf_symbol* goto_symbol,
+ ycf_symbol* label_symbol,
+ ycf_symbol* end_symbol);
+ycf_node* ycf_node_return_new(ycf_symbol* return_symbol,
+ ycf_node* return_expression,
+ ycf_symbol* end_symbol);
+ycf_node* ycf_node_period_field_access_new(ycf_symbol* period,
+ ycf_symbol* field_name);
+ycf_node* ycf_pointer_field_access_new(ycf_symbol* pointer,
+ ycf_symbol* field_name);
+
+ycf_node_list ycf_node_definition_list_from_string(char* str);
+void ycf_node_rename_function(ycf_node_function* f, char* new_name);
+bool ycf_node_is_void_ret_ending_fun(ycf_node* f_node);
+void ycf_node_remove_const_specifiers_from_declaration(ycf_node* declaration);
+void ycf_node_remove_static_specifiers_from_declaration(ycf_node* declaration);
+void ycf_node_remove_inline_specifiers_from_declaration(ycf_node* declaration);
+void ycf_node_remove_array_size_info_from_declaration(ycf_node* declaration);
+void ycf_node_modify_declarations(ycf_node_list declarations, void (*modifer)(ycf_node*));
+ycf_symbol_list ycf_node_get_return_type(ycf_node* f_node);
+ycf_symbol_list ycf_node_find_function_return_type(ycf_node* c_file_node, char* fun_name);
+
+
+/* Functions for node lists */
+
+int ycf_node_list_get_item_position(ycf_node_list* list, ycf_node* node);
+
+ycf_node* ycf_node_shallow_copy(ycf_node* n);
+ycf_node* ycf_node_list_get_item_at_position(ycf_node_list* list, int pos);
+
+void ycf_node_list_append(ycf_node_list* list, ycf_node* node);
+void ycf_node_list_prepend(ycf_node_list* list, ycf_node* node);
+void ycf_node_list_insert_before(ycf_node_list* list, ycf_node* before_this, ycf_node* to_insert);
+void ycf_node_list_insert_after(ycf_node_list* list, ycf_node* after_this, ycf_node* to_insert);
+void ycf_node_list_remove(ycf_node_list* list, ycf_node* to_remove);
+void ycf_node_list_replace(ycf_node_list* list, ycf_node* to_replace, ycf_node* replace_with);
+void ycf_node_list_concat(ycf_node_list* list1, ycf_node_list* list2);
+
+ycf_node_list ycf_node_list_empty();
+ycf_node_list ycf_node_list_shallow_copy(ycf_node_list n);
+ycf_node_list ycf_node_list_copy_append(ycf_node_list list, ycf_node* node);
+ycf_node_list ycf_node_list_copy_prepend(ycf_node_list list, ycf_node* node);
+ycf_node_list ycf_node_list_copy_insert_before(ycf_node_list list, ycf_node* before_this, ycf_node* to_insert);
+ycf_node_list ycf_node_list_copy_insert_after(ycf_node_list list, ycf_node* after_this, ycf_node* to_insert);
+ycf_node_list ycf_node_list_copy_remove(ycf_node_list list, ycf_node* to_remove);
+ycf_node_list ycf_node_list_copy_replace(ycf_node_list list, ycf_node* to_replace, ycf_node* replace_with);
+ycf_node_list ycf_node_list_copy_concat(ycf_node_list list1, ycf_node_list list2);
+size_t ycf_node_list_length(ycf_node_list list);
+char* ycf_node_list_to_string(ycf_node_list* l);
+char* ycf_node_get_node_type_string(ycf_node_type t);
+
+ycf_node_assignment* ycf_node_get_assignment(ycf_node* n);
+void ycf_node_print_node_type(ycf_node_type t);
+void ycf_node_normalize_function(ycf_node* fun);
+void ycf_node_remove_unecessary_scopes(ycf_node_code_scope* s);
+
+#endif //YIELDING_C_FUN_YCF_NODE_FUNS_H
diff --git a/erts/lib_src/yielding_c_fun/ycf_parser.c b/erts/lib_src/yielding_c_fun/ycf_parser.c
new file mode 100644
index 0000000000..ed2747c955
--- /dev/null
+++ b/erts/lib_src/yielding_c_fun/ycf_parser.c
@@ -0,0 +1,1498 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB and Kjell Winblad 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%
+ */
+
+/*
+ * Author: Kjell Winblad
+ */
+
+#include "ycf_yield_fun.h"
+#include "ycf_utils.h"
+#include "ycf_node.h"
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+void print_symbol_code(ycf_symbol* s);
+ycf_parse_result parse_exp_statement(ycf_symbol* symbols);
+
+ycf_parse_result fail_parse_result(){
+ ycf_parse_result r;
+ r.success = false;
+ r.next_symbol = NULL;
+ r.result = NULL;
+ return r;
+}
+
+ycf_parse_result success_parse_result(ycf_symbol* next,
+ ycf_node* result){
+ ycf_parse_result r;
+ r.success = true;
+ r.next_symbol = next;
+ r.result = result;
+ return r;
+}
+
+typedef struct {
+ ycf_node_list list;
+ ycf_symbol* next_symbol;
+} parse_list_res;
+
+typedef struct {
+ ycf_symbol_list list;
+ ycf_symbol* next_symbol;
+} parse_symbol_list_res;
+
+
+parse_list_res parse_list_generic(int number_of_parsers,
+ ycf_parse_result (*parsers[])(ycf_symbol*),
+ ycf_symbol* symbols,
+ bool use_break_symbol,
+ int nr_of_break_symbols,
+ ycf_symbol_type break_symbols[]){
+ parse_list_res r;
+ ycf_symbol* current = symbols;
+ int i;
+ r.list = ycf_node_list_empty();
+ while(current != NULL){
+ if(use_break_symbol){
+ bool is_break = false;
+ for(int i = 0; i < nr_of_break_symbols; i++){
+ if(break_symbols[i] == current->type){
+ is_break = true;
+ }
+ }
+ if(is_break){
+ break;
+ }
+ }
+ ycf_symbol* prev_current_symbol = current;
+ for(i = 0; i < number_of_parsers; i++){
+ ycf_parse_result res = parsers[i](current);
+ if(res.success){
+ ycf_node_list_append(&r.list, res.result);
+ current = res.next_symbol;
+ break;
+ }
+ }
+ if(prev_current_symbol == current){
+ break;
+ }
+ }
+ r.next_symbol = current;
+ return r;
+}
+
+parse_list_res parse_list(int number_of_parsers,
+ ycf_parse_result (*parsers[])(ycf_symbol*),
+ ycf_symbol* symbols){
+ return parse_list_generic(number_of_parsers,
+ parsers,
+ symbols,
+ false,
+ 0,
+ NULL);
+
+}
+
+parse_list_res parse_list_break_symbol(int number_of_parsers,
+ ycf_parse_result (*parsers[])(ycf_symbol*),
+ ycf_symbol* symbols,
+ ycf_symbol_type break_symbol){
+ ycf_symbol_type break_s[1] = {break_symbol};
+ return parse_list_generic(number_of_parsers,
+ parsers,
+ symbols,
+ true,
+ 1,
+ break_s);
+
+}
+
+parse_list_res parse_list_break_symbols(int number_of_parsers,
+ ycf_parse_result (*parsers[])(ycf_symbol*),
+ ycf_symbol* symbols,
+ int nr_of_break_symbols,
+ ycf_symbol_type break_symbols[]){
+ return parse_list_generic(number_of_parsers,
+ parsers,
+ symbols,
+ true,
+ nr_of_break_symbols,
+ break_symbols);
+
+}
+
+
+ycf_node* ycf_node_defenition_new(ycf_symbol_list type_spec,
+ ycf_symbol* id,
+ ycf_node_list array_brackets,
+ ycf_symbol* end){
+ ycf_node* n = ycf_malloc(sizeof(ycf_node));
+ n->type = ycf_node_type_variable_definition;
+ n->next = NULL;
+ n->u.definition.identifier = ycf_symbol_copy(id);
+ n->u.definition.type_specifiers = type_spec;
+ n->u.definition.array_brackets = array_brackets;
+ n->u.definition.end = ycf_symbol_copy(end);
+ return n;
+}
+
+ycf_node* ycf_node_defenition_with_init_new(ycf_node_definition def,
+ ycf_symbol_list expression,
+ ycf_symbol* end){
+ ycf_node* n = ycf_malloc(sizeof(ycf_node));
+ n->type = ycf_node_type_variable_definition_init;
+ n->next = NULL;
+ n->u.definition_init.definition = def;
+ n->u.definition_init.initializer_expression = expression;
+ n->u.definition_init.end = ycf_symbol_copy(end);
+ return n;
+}
+
+ycf_node* ycf_node_c_file_new(ycf_node_list content){
+ ycf_node* n = ycf_malloc(sizeof(ycf_node));
+ n->type = ycf_node_type_c_file;
+ n->next = NULL;
+ n->u.c_file.content = content;
+ return n;
+}
+
+
+ycf_node* ycf_node_function_def_new(ycf_node_definition def_node,
+ ycf_node_list parameters,
+ bool ignore_param_ending,
+ ycf_symbol_list end){
+ ycf_node* n = ycf_malloc(sizeof(ycf_node));
+ n->type = ycf_node_type_function_declaration;
+ n->next = NULL;
+ n->u.function_definition.definition = def_node;
+ n->u.function_definition.parameters = parameters;
+ n->u.function_definition.ignore_param_ending = ignore_param_ending;
+ n->u.function_definition.end = end;
+ return n;
+}
+
+ycf_node* ycf_node_yield_new(ycf_symbol* yield_symbol,
+ ycf_symbol* end){
+ ycf_node* n = ycf_malloc(sizeof(ycf_node));
+ n->type = ycf_node_type_yield;
+ n->next = NULL;
+ n->u.yield.yield_symbol = ycf_symbol_copy(yield_symbol);
+ n->u.yield.end_symbol = ycf_symbol_copy(end);
+ return n;
+}
+
+
+
+ycf_node* ycf_node_consume_reds_new(ycf_symbol* consume_reds_symbol,
+ ycf_node_parentheses_expression nr_of_reds_expression,
+ ycf_symbol* end_symbol){
+ ycf_node* n = ycf_malloc(sizeof(ycf_node));
+ n->type = ycf_node_type_consume_reds;
+ n->next = NULL;
+ n->u.consume_reds.consume_reds_symbol = ycf_symbol_copy(consume_reds_symbol);
+ n->u.consume_reds.nr_of_reds_expression = nr_of_reds_expression;
+ n->u.consume_reds.end_symbol = ycf_symbol_copy(end_symbol);
+ return n;
+}
+
+
+ycf_node* ycf_node_other_new(ycf_symbol* other_symbol){
+ ycf_node* n = ycf_malloc(sizeof(ycf_node));
+ n->type = ycf_node_type_other;
+ n->next = NULL;
+ n->u.other.what = ycf_symbol_copy(other_symbol);
+ return n;
+}
+
+ycf_node* ycf_node_scope_new(ycf_symbol* start,
+ ycf_node_list declaration_nodes,
+ ycf_node_list other_nodes,
+ ycf_symbol* end){
+ ycf_node* n = ycf_malloc(sizeof(ycf_node));
+ n->type = ycf_node_type_code_scope;
+ n->next = NULL;
+ n->u.code_scope.start = ycf_symbol_copy(start);
+ n->u.code_scope.definition_nodes = declaration_nodes;
+ n->u.code_scope.other_nodes = other_nodes;
+ n->u.code_scope.end = ycf_symbol_copy(end);
+ return n;
+}
+
+ycf_node* ycf_node_function_new(ycf_node_function_definition fun_def,
+ ycf_node_code_scope body){
+ ycf_node* n = ycf_malloc(sizeof(ycf_node));
+ n->type = ycf_node_type_function_definition;
+ n->next = NULL;
+ n->u.function.definition = fun_def;
+ n->u.function.body = body;
+ return n;
+}
+
+ycf_node* ycf_node_function_call_new(ycf_symbol_list neg_symbols,
+ ycf_symbol* ident,
+ ycf_symbol* paran_start,
+ ycf_node_list parameters,
+ ycf_symbol* paran_end){
+ ycf_node* n = ycf_malloc(sizeof(ycf_node));
+ n->type = ycf_node_type_function_call;
+ n->next = NULL;
+ n->u.function_call.neg_symbols = neg_symbols;
+ n->u.function_call.identifier = ident;
+ n->u.function_call.start_symbol = paran_start;
+ n->u.function_call.parameter_expressions = parameters;
+ n->u.function_call.end_symbol = paran_end;
+ return n;
+}
+
+ycf_node* ycf_node_paran_expression_new(ycf_symbol* start, ycf_node_expression expr, ycf_symbol* end){
+ ycf_node* n = ycf_malloc(sizeof(ycf_node));
+ n->type = ycf_node_type_parentheses_expression;
+ n->next = NULL;
+ n->u.parentheses_expression.start_symbol = start;
+ n->u.parentheses_expression.content = expr;
+ n->u.parentheses_expression.end_symbol = end;
+ return n;
+}
+
+ycf_node* ycf_node_expression_new(ycf_node_list expr){
+ ycf_node* n = ycf_malloc(sizeof(ycf_node));
+ n->type = ycf_node_type_expression;
+ n->next = NULL;
+ n->u.expression.content = expr;
+ n->u.expression.end_symbol = NULL;
+ return n;
+}
+
+ycf_node* ycf_node_statement_new(ycf_node* expression,
+ ycf_symbol* end_symbol){
+ ycf_node* n = ycf_malloc(sizeof(ycf_node));
+ n->type = ycf_node_type_statement;
+ n->next = NULL;
+ n->u.statement.expression = expression;
+ n->u.statement.end_symbol = end_symbol;
+ return n;
+}
+
+ycf_node* ycf_node_while_new(ycf_symbol* while_word,
+ ycf_node_parentheses_expression expression,
+ ycf_node* statement){
+ ycf_node* n = ycf_malloc(sizeof(ycf_node));
+ n->next = NULL;
+ n->type = ycf_node_type_while;
+ n->u.while_n.while_word = while_word;
+ n->u.while_n.expression = expression;
+ n->u.while_n.statement = statement;
+ return n;
+}
+
+
+
+ycf_node* ycf_node_do_while_new(ycf_symbol* do_word,
+ ycf_node* statm,
+ ycf_symbol* while_word,
+ ycf_node_parentheses_expression expression,
+ ycf_symbol* end){
+ ycf_node* n = ycf_malloc(sizeof(ycf_node));
+ n->next = NULL;
+ n->type = ycf_node_type_do_while;
+ n->u.do_while.do_word = do_word;
+ n->u.do_while.statement = statm;
+ n->u.do_while.while_word = while_word;
+ n->u.do_while.expression = expression;
+ n->u.do_while.end = end;
+ return n;
+}
+
+ycf_node* ycf_node_for_new(ycf_symbol* for_word,
+ ycf_symbol* start_paran,
+ struct ycf_node* init,
+ ycf_node* stop_cond,
+ ycf_symbol* stop_cond_end,
+ ycf_node* end_exp,
+ ycf_symbol* end_paran,
+ ycf_node* statem){
+ ycf_node* n = ycf_malloc(sizeof(ycf_node));
+ n->next = NULL;
+ n->type = ycf_node_type_for;
+ n->u.for_n.for_word = for_word;
+ n->u.for_n.start_parentheses = start_paran;
+ n->u.for_n.init = init;
+ n->u.for_n.stop_cond = stop_cond;
+ n->u.for_n.stop_cond_end = stop_cond_end;
+ n->u.for_n.end_exp = end_exp;
+ n->u.for_n.end_parentheses = end_paran;
+ n->u.for_n.statement = statem;
+ return n;
+}
+
+ycf_node* ycf_node_switch_new(ycf_symbol* switch_word,
+ ycf_node_parentheses_expression expression,
+ ycf_node_code_scope scope){
+ ycf_node* n = ycf_malloc(sizeof(ycf_node));
+ n->next = NULL;
+ n->type = ycf_node_type_switch;
+ n->u.switch_n.switch_word = switch_word;
+ n->u.switch_n.expression = expression;
+ n->u.switch_n.scope = scope;
+ return n;
+}
+
+ycf_node* ycf_node_if_new(ycf_symbol* if_word,
+ ycf_node_parentheses_expression expression,
+ struct ycf_node* if_statem){
+ ycf_node* n = ycf_malloc(sizeof(ycf_node));
+ n->next = NULL;
+ n->type = ycf_node_type_if;
+ n->u.if_n.if_word = if_word;
+ n->u.if_n.expression = expression;
+ n->u.if_n.if_statement = if_statem;
+ return n;
+}
+
+ycf_node* ycf_node_if_else_new(ycf_node_if if_n,
+ ycf_symbol* else_word,
+ struct ycf_node* else_statement){
+ ycf_node* n = ycf_malloc(sizeof(ycf_node));
+ n->next = NULL;
+ n->type = ycf_node_type_if_else;
+ n->u.if_else.if_part = if_n;
+ n->u.if_else.else_word = else_word;
+ n->u.if_else.else_statement = else_statement;
+ return n;
+}
+
+
+ycf_node* ycf_node_fun_call_assignment_new(ycf_node_expression left_side,
+ ycf_symbol* assignment_symbol,
+ ycf_node_function_call fun_call){
+ ycf_node* n = ycf_malloc(sizeof(ycf_node));
+ n->next = NULL;
+ n->type = ycf_node_type_assignment_function_call;
+ n->u.function_call_assignment.left_side = left_side;
+ n->u.function_call_assignment.assignment_symbol = assignment_symbol;
+ n->u.function_call_assignment.fun_call = fun_call;
+ return n;
+}
+
+ycf_node* ycf_node_assignment_new(ycf_node_expression left_side,
+ ycf_symbol* assignment_symbol,
+ ycf_node_expression right_side,
+ ycf_symbol* end){
+ ycf_node* n = ycf_malloc(sizeof(ycf_node));
+ n->next = NULL;
+ n->type = ycf_node_type_assignment;
+ n->u.a.left_side = left_side;
+ n->u.a.right_side = right_side;
+ n->u.a.assignment_symbol = assignment_symbol;
+ n->u.a.end = end;
+ return n;
+}
+
+ycf_node* ycf_node_comma_new(ycf_symbol* comma_symbol){
+ ycf_node* n = ycf_malloc(sizeof(ycf_node));
+ n->next = NULL;
+ n->type = ycf_node_type_comma;
+ n->u.comma.comma_symbol = comma_symbol;
+ return n;
+}
+
+ycf_node* ycf_node_array_bracket_new(ycf_symbol* start,
+ bool empty,
+ ycf_node_expression content,
+ ycf_symbol* end){
+ ycf_node* n = ycf_malloc(sizeof(ycf_node));
+ n->next = NULL;
+ n->type = ycf_node_type_array_bracket;
+ n->u.array_bracket.start = start;
+ n->u.array_bracket.empty = empty;
+ n->u.array_bracket.content = content;
+ n->u.array_bracket.end = end;
+ return n;
+}
+
+ycf_node* ycf_node_macro_cmd_new(ycf_symbol* macro_symbol){
+ ycf_node* n = ycf_malloc(sizeof(ycf_node));
+ n->next = NULL;
+ n->type = ycf_node_type_macro_cmd;
+ n->u.macro_cmd.symbol = macro_symbol;
+ return n;
+}
+
+ycf_node* ycf_node_special_code_block_new(ycf_node_type type,
+ ycf_symbol* start,
+ ycf_node_if code,
+ ycf_symbol* end){
+ ycf_node* n = ycf_malloc(sizeof(ycf_node));
+ n->next = NULL;
+ n->type = type;
+ n->u.special_code_block.start = start;
+ n->u.special_code_block.code = code;
+ n->u.special_code_block.end = end;
+ return n;
+}
+
+ycf_node* ycf_node_goto_new(ycf_symbol* goto_symbol,
+ ycf_symbol* label_symbol,
+ ycf_symbol* end_symbol){
+ ycf_node* n = ycf_malloc(sizeof(ycf_node));
+ n->next = NULL;
+ n->type = ycf_node_type_goto;
+ n->u.goto_n.goto_symbol = goto_symbol;
+ n->u.goto_n.label_symbol = label_symbol;
+ n->u.goto_n.end_symbol = end_symbol;
+ return n;
+}
+
+ycf_node* ycf_node_return_new(ycf_symbol* return_symbol,
+ ycf_node* return_expression,
+ ycf_symbol* end_symbol){
+ ycf_node* n = ycf_malloc(sizeof(ycf_node));
+ n->next = NULL;
+ n->type = ycf_node_type_return_statement;
+ n->u.return_n.return_symbol = return_symbol;
+ n->u.return_n.return_expression = return_expression;
+ n->u.return_n.end_symbol = end_symbol;
+ return n;
+}
+
+
+ycf_node* ycf_node_period_field_access_new(ycf_symbol* period,
+ ycf_symbol* field_name){
+ ycf_node* n = ycf_malloc(sizeof(ycf_node));
+ n->next = NULL;
+ n->type = ycf_node_type_period_field_access;
+ n->u.period_field_access.period = period;
+ n->u.period_field_access.field_name = field_name;
+ return n;
+}
+
+ycf_node* ycf_pointer_field_access_new(ycf_symbol* pointer,
+ ycf_symbol* field_name){
+ ycf_node* n = ycf_malloc(sizeof(ycf_node));
+ n->next = NULL;
+ n->type = ycf_node_type_period_field_access;
+ n->u.pointer_field_access.pointer = pointer;
+ n->u.pointer_field_access.field_name = field_name;
+ return n;
+}
+
+parse_symbol_list_res parse_symbol_list(int nr_of_aloved_symbols,
+ ycf_symbol_type accept_symbols[],
+ ycf_symbol* symbols){
+ int i;
+ parse_symbol_list_res r;
+ ycf_symbol* current = symbols;
+ r.list = ycf_symbol_list_empty();
+ while (current != NULL){
+ ycf_symbol* prev_current = current;
+ for(i = 0; i < nr_of_aloved_symbols; i++){
+ if(current->type == accept_symbols[i]){
+ ycf_symbol_list_append(&r.list, ycf_symbol_copy(current));
+ current = current->next;
+ }
+ }
+ if(prev_current == current){
+ break;
+ }
+ }
+ r.next_symbol = current;
+ return r;
+}
+
+parse_symbol_list_res
+parse_symbol_list_until(ycf_symbol* symbols, ycf_symbol_type end_symbol){
+ parse_symbol_list_res r;
+ ycf_symbol* current = symbols;
+ r.list = ycf_symbol_list_empty();
+ while (current->type != end_symbol){
+ ycf_symbol_list_append(&r.list, ycf_symbol_copy(current));
+ current = current->next;
+ }
+ r.next_symbol = current;
+ return r;
+}
+
+ycf_parse_result parse_expression_end_end_squarebracket(ycf_symbol* symbols);
+
+ycf_parse_result parse_array_bracket(ycf_symbol* symbols){
+ if(symbols->type != ycf_symbol_type_open_square_bracket){
+ return fail_parse_result();
+ }
+ if(symbols->next->type == ycf_symbol_type_end_square_bracket){
+ ycf_node_expression empty = {.content = ycf_node_list_empty(), .end_symbol = NULL};
+ return success_parse_result(symbols->next->next,
+ ycf_node_array_bracket_new(symbols, true, empty, symbols->next));
+ }
+ ycf_parse_result square_bracket_content =
+ parse_expression_end_end_squarebracket(symbols->next);
+ if(!square_bracket_content.success){
+ return fail_parse_result();
+ }
+ return success_parse_result(square_bracket_content.next_symbol->next,
+ ycf_node_array_bracket_new(symbols,
+ false,
+ square_bracket_content.result->u.expression,
+ square_bracket_content.next_symbol));
+}
+
+ycf_parse_result parse_defenition_until_identifier(ycf_symbol* symbols,
+ ycf_symbol_type end_symbol_type){
+ ycf_symbol* current = symbols;
+ ycf_symbol_list type_specifier = ycf_symbol_list_empty();
+ ycf_symbol* ident = NULL;
+ ycf_node_list array_brackets;
+ /* Parse modifiers */
+ {
+ int nr_of_aloved_symbols = 4;
+ ycf_symbol_type accept_symbols[4];
+ accept_symbols[0] = ycf_symbol_type_static;
+ accept_symbols[1] = ycf_symbol_type_const;
+ accept_symbols[2] = ycf_symbol_type_inline;
+ accept_symbols[3] = ycf_symbol_type_volatile;
+ parse_symbol_list_res more =
+ parse_symbol_list(nr_of_aloved_symbols,
+ accept_symbols,
+ current);
+ ycf_symbol_list_concat(&type_specifier, &more.list);
+ current = more.next_symbol;
+ }
+ /* Parse first symbol */
+ if(current->type == ycf_symbol_type_identifier ||
+ current->type == ycf_symbol_type_void){
+ ycf_symbol_list_append(&type_specifier, ycf_symbol_copy(current));
+ current = current->next;
+ } else {
+ return fail_parse_result();
+ }
+ if(type_specifier.last->type == ycf_symbol_type_identifier){
+ /* Parse remaining identifiers if first symbol is ycf_symbol_type_identifier */
+ int nr_of_aloved_symbols = 2;
+ ycf_symbol_type accept_symbols[2];
+ accept_symbols[0] = ycf_symbol_type_identifier;
+ accept_symbols[1] = ycf_symbol_type_const;
+ parse_symbol_list_res more =
+ parse_symbol_list(nr_of_aloved_symbols,
+ accept_symbols,
+ current);
+ ycf_symbol_list_concat(&type_specifier, &more.list);
+ current = more.next_symbol;
+ }
+ {
+ /* Parse stars */
+ int nr_of_aloved_symbols = 1;
+ ycf_symbol_type accept_symbols[1];
+ accept_symbols[0] = ycf_symbol_type_star;
+ parse_symbol_list_res more =
+ parse_symbol_list(nr_of_aloved_symbols,
+ accept_symbols,
+ current);
+ ycf_symbol_list_concat(&type_specifier, &more.list);
+ current = more.next_symbol;
+ }
+ /* Handle ycf_symbol_type_identifier */
+ if(type_specifier.last->type == ycf_symbol_type_identifier){
+ ident = type_specifier.last;
+ ycf_symbol_list_remove(&type_specifier, type_specifier.last);
+ if (type_specifier.head == NULL) {
+ return fail_parse_result();
+ }
+ } else if (current->type != ycf_symbol_type_identifier){
+ return fail_parse_result();
+ } else {
+ ident = current;
+ current = current->next;
+ }
+ /* Handle array brackets */
+ {
+ ycf_parse_result (*parsers[])(ycf_symbol *) = {
+ parse_array_bracket
+ };
+ parse_list_res res = parse_list(1, parsers, current);
+ array_brackets = res.list;
+ current = res.next_symbol;
+ }
+ /* Parse end symbol */
+ if (current == NULL ||
+ current->type != end_symbol_type){
+ return fail_parse_result();
+ } else {
+ return success_parse_result(current->next,
+ ycf_node_defenition_new(type_specifier,
+ ident,
+ array_brackets,
+ current));
+ }
+}
+
+ycf_parse_result parse_defenition_no_init(ycf_symbol* symbols){
+ ycf_parse_result res = parse_defenition_until_identifier(symbols, ycf_symbol_type_semicolon);
+ return res;
+}
+
+ycf_parse_result parse_defenition_with_init(ycf_symbol* symbols){
+ ycf_parse_result dec = parse_defenition_until_identifier(symbols, ycf_symbol_type_equal_sign);
+ parse_symbol_list_res expression_list_res;
+ if(!dec.success){
+ return fail_parse_result();
+ }
+ expression_list_res =
+ parse_symbol_list_until(dec.next_symbol, ycf_symbol_type_semicolon);
+ return success_parse_result(expression_list_res.next_symbol->next,
+ ycf_node_defenition_with_init_new(dec.result->u.definition,
+ expression_list_res.list,
+ expression_list_res.next_symbol));
+}
+
+ycf_parse_result parse_defenition_comma(ycf_symbol* symbols){
+ ycf_parse_result res = parse_defenition_until_identifier(symbols, ycf_symbol_type_comma);
+ return res;
+}
+
+ycf_parse_result parse_defenition_end_paran(ycf_symbol* symbols){
+ ycf_parse_result res = parse_defenition_until_identifier(symbols, ycf_symbol_type_end_parenthesis);
+ return res;
+}
+
+ycf_parse_result parse_function_head(ycf_symbol* symbols,
+ ycf_symbol_type end_symbol,
+ bool exclude_end_symbol){
+ ycf_symbol* current = symbols;
+ ycf_parse_result def_res =
+ parse_defenition_until_identifier(current,
+ ycf_symbol_type_open_parenthesis);
+ if(!def_res.success){
+ return fail_parse_result();
+ }
+ current = def_res.next_symbol;
+
+ /* Parse function without parameters */
+
+ if(current->type == ycf_symbol_type_end_parenthesis &&
+ current->next->type == end_symbol){
+ ycf_symbol_list end = ycf_symbol_list_empty();
+ ycf_symbol* next_symbol;
+ ycf_node_list parameter_list = ycf_node_list_empty();
+ ycf_symbol_list_append(&end, ycf_symbol_copy(current));
+ if(!exclude_end_symbol){
+ next_symbol = current->next->next;
+ ycf_symbol_list_append(&end, ycf_symbol_copy(current->next));
+ } else {
+ next_symbol = current->next;
+ }
+ return success_parse_result(next_symbol,
+ ycf_node_function_def_new(def_res.result->u.definition,
+ parameter_list,
+ false,
+ end));
+ } else if(current->type == ycf_symbol_type_void &&
+ current->next->type == ycf_symbol_type_end_parenthesis &&
+ current->next->next->type == end_symbol){
+ ycf_symbol_list end = ycf_symbol_list_empty();
+ ycf_symbol* next_symbol;
+ ycf_node_list parameter_list = ycf_node_list_empty();
+ ycf_symbol_list_append(&end, ycf_symbol_copy(current));
+ ycf_symbol_list_append(&end, ycf_symbol_copy(current->next));
+ if(!exclude_end_symbol){
+ next_symbol = current->next->next->next;
+ ycf_symbol_list_append(&end, ycf_symbol_copy(current->next->next));
+ }else{
+ next_symbol = current->next->next;
+ }
+ return success_parse_result(next_symbol,
+ ycf_node_function_def_new(def_res.result->u.definition,
+ parameter_list,
+ false,
+ end));
+ }
+
+ /* Parse parameters */
+
+ int number_of_parsers = 2;
+ ycf_parse_result (*parsers[])(ycf_symbol *) = {
+ parse_defenition_comma,
+ parse_defenition_end_paran
+ };
+ parse_list_res param_list =
+ parse_list(number_of_parsers, parsers, current);
+ if(param_list.next_symbol == NULL ||
+ param_list.next_symbol->type != end_symbol){
+ return fail_parse_result();
+ }
+
+ /* Parse ending */
+
+ {
+ ycf_symbol_list end = ycf_symbol_list_empty();
+ ycf_symbol* next_symbol;
+ if(!exclude_end_symbol){
+ next_symbol = param_list.next_symbol->next;
+ ycf_symbol_list_append(&end, ycf_symbol_copy(param_list.next_symbol));
+ }else{
+ next_symbol = param_list.next_symbol;
+ }
+ return success_parse_result(next_symbol,
+ ycf_node_function_def_new(def_res.result->u.definition,
+ param_list.list,
+ false,
+ end));
+ }
+}
+
+ycf_parse_result parse_function_def(ycf_symbol* symbols){
+ return parse_function_head(symbols, ycf_symbol_type_semicolon, false);
+}
+
+ycf_parse_result parse_other(ycf_symbol* symbols){
+ ycf_symbol* what = symbols;
+ return success_parse_result(symbols->next,
+ ycf_node_other_new(what));
+}
+
+
+ycf_parse_result parse_goto(ycf_symbol* symbols){
+ ycf_symbol* goto_symbol = symbols;
+ if(goto_symbol->type != ycf_symbol_type_goto){
+ return fail_parse_result();
+ }
+ ycf_symbol* label_symbol = goto_symbol->next;
+ if(label_symbol->type != ycf_symbol_type_identifier){
+ return fail_parse_result();
+ }
+ ycf_symbol* end_symbol = label_symbol->next;
+ if(end_symbol->type != ycf_symbol_type_semicolon){
+ return fail_parse_result();
+ }
+ return success_parse_result(end_symbol->next,
+ ycf_node_goto_new(goto_symbol, label_symbol, end_symbol));
+}
+
+
+ycf_parse_result parse_comma(ycf_symbol* symbols){
+ if(symbols->type == ycf_symbol_type_comma){
+ return success_parse_result(symbols->next, ycf_node_comma_new(symbols));
+ } else {
+ return fail_parse_result();
+ }
+}
+
+
+ycf_parse_result parse_pointer_field_access_node(ycf_symbol* symbols){
+ ycf_symbol* pointer = symbols;
+ if(pointer->type != ycf_symbol_type_pointer_field_access){
+ return fail_parse_result();
+ }
+ ycf_symbol* field_name = pointer->next;
+ if(field_name->type != ycf_symbol_type_identifier){
+ return fail_parse_result();
+ }
+ return success_parse_result(field_name->next,
+ ycf_pointer_field_access_new(pointer, field_name));
+}
+
+ycf_parse_result parse_period_field_access_node(ycf_symbol* symbols){
+ ycf_symbol* p = symbols;
+ if(p->type != ycf_symbol_type_period){
+ return fail_parse_result();
+ }
+ ycf_symbol* field_name = p->next;
+ if(field_name->type != ycf_symbol_type_identifier){
+ return fail_parse_result();
+ }
+ return success_parse_result(field_name->next,
+ ycf_node_period_field_access_new(p, field_name));
+}
+
+ycf_parse_result parse_function_call(ycf_symbol* symbols);
+ycf_parse_result parse_expression_generic(ycf_symbol* symbols,
+ bool use_end_symbol,
+ bool no_function_call_assignment,
+ int nr_of_end_symbols,
+ ycf_symbol_type end_symbols[]);
+
+ycf_parse_result parse_paran_expression(ycf_symbol* symbols){
+ if(symbols->type != ycf_symbol_type_open_parenthesis){
+ return fail_parse_result();
+ }
+ ycf_symbol* start = symbols;
+ ycf_parse_result exp = parse_expression_generic(start->next,
+ true,
+ false,
+ 1,
+ (ycf_symbol_type[]){ycf_symbol_type_end_parenthesis});
+ if(!exp.success || exp.next_symbol->type != ycf_symbol_type_end_parenthesis ){
+ return fail_parse_result();
+ }
+ return success_parse_result(exp.next_symbol->next,
+ ycf_node_paran_expression_new(start,
+ exp.result->u.expression,
+ exp.next_symbol));
+}
+
+ycf_parse_result parse_function_call_assignment(ycf_symbol* symbols);
+ycf_parse_result parse_assignment(ycf_symbol* symbols);
+
+ycf_parse_result parse_expression_generic(ycf_symbol* symbols,
+ bool use_end_symbol,
+ bool no_function_call_assignment,
+ int nr_of_end_symbols,
+ ycf_symbol_type end_symbols[]){
+ int number_of_parsers = (!no_function_call_assignment ? 1 : 0) + 5;
+ ycf_parse_result (*parsers[7])(ycf_symbol *);
+ if(!no_function_call_assignment){
+ parsers[0] = parse_function_call;
+ parsers[1] = parse_function_call_assignment;
+ parsers[2] = parse_paran_expression;
+ parsers[3] = parse_period_field_access_node;
+ parsers[4] = parse_pointer_field_access_node;
+ parsers[5] = parse_other;
+ }else{
+ parsers[0] = parse_function_call;
+ parsers[1] = parse_paran_expression;
+ parsers[2] = parse_period_field_access_node;
+ parsers[3] = parse_pointer_field_access_node;
+ parsers[4] = parse_other;
+ }
+ parse_list_res res = parse_list_break_symbols(number_of_parsers, parsers, symbols, nr_of_end_symbols,end_symbols);
+ if(res.list.head == NULL || (use_end_symbol &&
+ (res.next_symbol == NULL ||res.next_symbol->type != end_symbols[0]))){
+ return fail_parse_result();
+ }
+ return success_parse_result(res.next_symbol,
+ ycf_node_expression_new(res.list));
+}
+
+ycf_parse_result parse_expression(ycf_symbol* symbols){
+ return parse_expression_generic(symbols,
+ false,
+ false,
+ 0 /* ignored */,
+ NULL);
+}
+
+ycf_parse_result parse_expression_end_comma(ycf_symbol* symbols){
+ return parse_expression_generic(symbols,
+ true,
+ false,
+ 2,
+ (ycf_symbol_type[]){ycf_symbol_type_comma, ycf_symbol_type_end_parenthesis});
+}
+
+ycf_parse_result parse_expression_end_end_paren(ycf_symbol* symbols){
+ return parse_expression_generic(symbols,
+ true,
+ false,
+ 1,
+ (ycf_symbol_type[]){ycf_symbol_type_end_parenthesis});
+}
+
+ycf_parse_result parse_expression_end_end_semicolon(ycf_symbol* symbols){
+ return parse_expression_generic(symbols,
+ true,
+ false,
+ 1,
+ (ycf_symbol_type[]){ycf_symbol_type_semicolon});
+}
+
+ycf_parse_result parse_expression_end_end_squarebracket(ycf_symbol* symbols){
+ return parse_expression_generic(symbols,
+ true,
+ false,
+ 1,
+ (ycf_symbol_type[]){ycf_symbol_type_end_square_bracket});
+}
+
+
+ycf_parse_result parse_function_call(ycf_symbol* symbols){
+ parse_symbol_list_res negs;
+ {
+ /* Parse neg symbols */
+ int nr_of_aloved_symbols = 1;
+ ycf_symbol_type accept_symbols[1];
+ accept_symbols[0] = ycf_symbol_type_neg;
+ negs =
+ parse_symbol_list(nr_of_aloved_symbols,
+ accept_symbols,
+ symbols);
+ }
+ ycf_symbol* ident = negs.next_symbol;
+ if(ident == NULL ||
+ ident->type != ycf_symbol_type_identifier){
+ return fail_parse_result();
+ }
+ ycf_symbol* paran_start = ident->next;
+ if(paran_start == NULL || paran_start->type != ycf_symbol_type_open_parenthesis){
+ return fail_parse_result();
+ }
+ int number_of_parsers = 3;
+ ycf_parse_result (*parsers[])(ycf_symbol *) = {
+ parse_comma,
+ parse_expression_end_comma,
+ parse_expression_end_end_paren
+ };
+ parse_list_res param_list_res =
+ parse_list_break_symbol(number_of_parsers,
+ parsers,
+ paran_start->next,
+ ycf_symbol_type_end_parenthesis);
+ if(param_list_res.next_symbol == NULL ||
+ param_list_res.next_symbol->type != ycf_symbol_type_end_parenthesis){
+ return fail_parse_result();
+ }
+ return success_parse_result(param_list_res.next_symbol->next,
+ ycf_node_function_call_new(negs.list,
+ ident,
+ paran_start,
+ param_list_res.list,
+ param_list_res.next_symbol));
+}
+
+ycf_parse_result parse_assignment(ycf_symbol* symbols) {
+ if(symbols->type != ycf_symbol_type_identifier){
+ return fail_parse_result();
+ }
+ if(symbols->next == NULL || symbols->next->type != ycf_symbol_type_equal_sign){
+ return fail_parse_result();
+ }
+ ycf_parse_result left_expression = parse_expression_generic(symbols,
+ false,
+ true,
+ 3,
+ (ycf_symbol_type[]){
+ ycf_symbol_type_equal_sign,
+ ycf_symbol_type_semicolon,
+ ycf_symbol_type_end_parenthesis});
+ if(!left_expression.success){
+ return fail_parse_result();
+ }
+ ycf_symbol* equal_sign = left_expression.next_symbol;
+ if(equal_sign == NULL || equal_sign->type != ycf_symbol_type_equal_sign) {
+ return fail_parse_result();
+ }
+ if(equal_sign->next == NULL || equal_sign->next->type != ycf_symbol_type_identifier){
+ return fail_parse_result();
+ }
+ if(equal_sign->next->next == NULL || equal_sign->next->next->type != ycf_symbol_type_semicolon){
+ return fail_parse_result();
+ }
+ ycf_parse_result right_expression = parse_expression_generic(equal_sign->next,
+ false,
+ true,
+ 3,
+ (ycf_symbol_type[]){
+ ycf_symbol_type_semicolon,
+ ycf_symbol_type_equal_sign,
+ ycf_symbol_type_end_parenthesis});
+ if(!right_expression.success ||
+ right_expression.next_symbol == NULL ||
+ right_expression.next_symbol->type != ycf_symbol_type_semicolon){
+ return fail_parse_result();
+ }
+ ycf_node* node = ycf_node_assignment_new(left_expression.result->u.expression,
+ ycf_symbol_copy(equal_sign),
+ right_expression.result->u.expression,
+ ycf_symbol_copy(right_expression.next_symbol));
+ return success_parse_result(right_expression.next_symbol->next,
+ node);
+}
+
+ycf_parse_result parse_function_call_assignment(ycf_symbol* symbols){
+ ycf_parse_result expression = parse_expression_generic(symbols,
+ true,
+ true,
+ 3,
+ (ycf_symbol_type[]){ycf_symbol_type_equal_sign,
+ ycf_symbol_type_semicolon,
+ ycf_symbol_type_end_parenthesis});
+ if(!expression.success){
+ return fail_parse_result();
+ }
+ ycf_symbol* equal_sign = expression.next_symbol;
+ ycf_parse_result fun_call = parse_function_call(equal_sign->next);
+ if(!fun_call.success){
+ return fail_parse_result();
+ }
+ return success_parse_result(fun_call.next_symbol,
+ ycf_node_fun_call_assignment_new(expression.result->u.expression,
+ equal_sign,
+ fun_call.result->u.function_call));
+}
+
+
+ycf_parse_result parse_function_call_assignment_statement(ycf_symbol* symbols){
+ ycf_parse_result fun_call_ass = parse_function_call_assignment(symbols);
+ if(!fun_call_ass.success || fun_call_ass.next_symbol->type != ycf_symbol_type_semicolon){
+ return fail_parse_result();
+ }
+ return success_parse_result(fun_call_ass.next_symbol->next,
+ ycf_node_statement_new(fun_call_ass.result,
+ fun_call_ass.next_symbol));
+}
+
+
+ycf_parse_result parse_function_call_statement(ycf_symbol* symbols){
+ ycf_parse_result fun_call = parse_function_call(symbols);
+ if(!fun_call.success || fun_call.next_symbol->type != ycf_symbol_type_semicolon){
+ return fail_parse_result();
+ }
+ return success_parse_result(fun_call.next_symbol->next,
+ ycf_node_statement_new(fun_call.result,
+ fun_call.next_symbol));
+}
+
+ycf_parse_result parse_statement(ycf_symbol* symbols);
+
+
+ycf_parse_result parse_cond_statement(ycf_symbol* symbols, ycf_symbol_type cond_keyword){
+ if(symbols->type != cond_keyword){
+ return fail_parse_result();
+ }
+ ycf_symbol* cond_word = symbols;
+ ycf_parse_result cond_exp = parse_paran_expression(cond_word->next);
+ if(!cond_exp.success){
+ return fail_parse_result();
+ }
+ ycf_parse_result cond_statement = parse_statement(cond_exp.next_symbol);
+ if(!cond_statement.success){
+ return fail_parse_result();
+ }
+ /* Return if node even though it might be something else */
+ return success_parse_result(cond_statement.next_symbol,
+ ycf_node_if_new(cond_word,
+ cond_exp.result->u.parentheses_expression,
+ cond_statement.result));
+}
+
+ycf_parse_result parse_if_statement(ycf_symbol* symbols){
+ return parse_cond_statement(symbols, ycf_symbol_type_if);
+}
+
+ycf_parse_result parse_if_else_statement(ycf_symbol* symbols){
+ ycf_parse_result if_statem = parse_if_statement(symbols);
+ if(!if_statem.success){
+ return fail_parse_result();
+ }
+ if(if_statem.next_symbol->type != ycf_symbol_type_else){
+ return fail_parse_result();
+ }
+ ycf_symbol* else_w = if_statem.next_symbol;
+ ycf_parse_result else_statement = parse_statement(else_w->next);
+ if(!else_statement.success){
+ return fail_parse_result();
+ }
+ return success_parse_result(else_statement.next_symbol,
+ ycf_node_if_else_new(if_statem.result->u.if_n,
+ else_w,
+ else_statement.result));
+}
+
+ycf_parse_result parse_while_statement(ycf_symbol* symbols){
+ ycf_parse_result cond_statem = parse_cond_statement(symbols, ycf_symbol_type_while);
+ if(!cond_statem.success){
+ return fail_parse_result();
+ }
+ return success_parse_result(cond_statem.next_symbol,
+ ycf_node_while_new(cond_statem.result->u.if_n.if_word,
+ cond_statem.result->u.if_n.expression,
+ cond_statem.result->u.if_n.if_statement));
+}
+
+ycf_parse_result parse_do_while_statement(ycf_symbol* symbols){
+ if(symbols->type != ycf_symbol_type_do){
+ return fail_parse_result();
+ }
+ ycf_symbol* do_word = symbols;
+ ycf_parse_result statement = parse_statement(do_word->next);
+ if(!statement.success || statement.next_symbol->type != ycf_symbol_type_while){
+ return fail_parse_result();
+ }
+ ycf_symbol* while_word = statement.next_symbol;
+ ycf_parse_result cond = parse_paran_expression(while_word->next);
+ if(!cond.success){
+ return fail_parse_result();
+ }
+ if(cond.next_symbol->type != ycf_symbol_type_semicolon){
+ return fail_parse_result();
+ }
+ return success_parse_result(cond.next_symbol->next,
+ ycf_node_do_while_new(do_word, statement.result, while_word,
+ cond.result->u.parentheses_expression, cond.next_symbol));
+}
+
+ycf_parse_result parse_for_statement(ycf_symbol* symbols){
+ if(symbols->type != ycf_symbol_type_for){
+ return fail_parse_result();
+ }
+ ycf_symbol* for_word = symbols;
+ if(for_word->next->type != ycf_symbol_type_open_parenthesis){
+ return fail_parse_result();
+ }
+ ycf_symbol* start_paran = for_word->next;
+ ycf_node* init = NULL;
+ ycf_parse_result init_res = parse_defenition_with_init(start_paran->next);
+ if(!init_res.success){
+ init_res = parse_defenition_no_init(start_paran->next);
+ }
+ if(!init_res.success){
+ init_res = parse_exp_statement(start_paran->next);
+ }
+ if(!init_res.success){
+ return fail_parse_result();
+ }
+ init = init_res.result;
+ ycf_node* stop_cond = NULL;
+ ycf_symbol* stop_cond_end = NULL;
+ if(init_res.next_symbol->type == ycf_symbol_type_semicolon){
+ stop_cond_end = init_res.next_symbol;
+ }else{
+ ycf_parse_result stop_cond_res = parse_expression_end_end_semicolon(init_res.next_symbol);
+ if(!stop_cond_res.success || stop_cond_res.next_symbol->type != ycf_symbol_type_semicolon){
+ return fail_parse_result();
+ }
+ stop_cond = stop_cond_res.result;
+ stop_cond_end = stop_cond_res.next_symbol;
+ }
+ ycf_node* end_exp = NULL;
+ ycf_symbol* end_exp_end = NULL;
+ if(stop_cond_end->next->type == ycf_symbol_type_end_parenthesis){
+ end_exp_end = stop_cond_end->next;
+ }else{
+ ycf_parse_result end_exp_res = parse_expression_end_end_paren(stop_cond_end->next);
+ if(!end_exp_res.success || end_exp_res.next_symbol->type != ycf_symbol_type_end_parenthesis){
+ return fail_parse_result();
+ }
+ end_exp = end_exp_res.result;
+ end_exp_end = end_exp_res.next_symbol;
+ }
+ ycf_parse_result for_statement = parse_statement(end_exp_end->next);
+ if(!for_statement.success){
+ return fail_parse_result();
+ }
+ return success_parse_result(for_statement.next_symbol,
+ ycf_node_for_new(for_word,
+ start_paran,
+ init,
+ stop_cond,
+ stop_cond_end,
+ end_exp,
+ end_exp_end,
+ for_statement.result));
+}
+
+
+ycf_parse_result parse_switch_statement(ycf_symbol* symbols){
+ ycf_parse_result cond_statem = parse_cond_statement(symbols, ycf_symbol_type_switch);
+ if(!cond_statem.success){
+ return fail_parse_result();
+ }
+ if(cond_statem.result->u.if_n.if_statement->type != ycf_node_type_code_scope){
+ return fail_parse_result();
+ }
+ return success_parse_result(cond_statem.next_symbol,
+ ycf_node_switch_new(cond_statem.result->u.if_n.if_word,
+ cond_statem.result->u.if_n.expression,
+ cond_statem.result->u.if_n.if_statement->u.code_scope));
+}
+
+ycf_parse_result parse_exp_statement(ycf_symbol* symbols){
+ if(symbols->type == ycf_symbol_type_semicolon){
+ /*Empty statement*/
+ return success_parse_result(symbols->next,
+ ycf_node_statement_new(ycf_node_expression_new(ycf_node_list_empty()),
+ symbols));
+ }
+ ycf_parse_result expression = parse_expression_end_end_semicolon(symbols);
+ if(!expression.success || expression.next_symbol->type != ycf_symbol_type_semicolon){
+ return fail_parse_result();
+ }
+ return success_parse_result(expression.next_symbol->next,
+ ycf_node_statement_new(expression.result,
+ expression.next_symbol));
+}
+
+ycf_parse_result parse_return_statement(ycf_symbol* symbols){
+ ycf_symbol* return_symbol = symbols;
+ if(return_symbol->type != ycf_symbol_type_return){
+ return fail_parse_result();
+ }
+ if(return_symbol->next->type == ycf_symbol_type_semicolon){
+ return success_parse_result(return_symbol->next->next,
+ ycf_node_return_new(return_symbol,
+ NULL,
+ return_symbol->next));
+ }
+ ycf_parse_result expression = parse_expression_end_end_semicolon(return_symbol->next);
+ if(!expression.success || expression.next_symbol->type != ycf_symbol_type_semicolon){
+ return fail_parse_result();
+ }
+ return success_parse_result(expression.next_symbol->next,
+ ycf_node_return_new(return_symbol,
+ expression.result,
+ expression.next_symbol));
+}
+
+ycf_parse_result parse_macro_command(ycf_symbol* symbols){
+ if(symbols->type == ycf_symbol_type_macro_command || symbols->type == ycf_symbol_type_macro_define){
+ return success_parse_result(symbols->next, ycf_node_macro_cmd_new(symbols));
+ } else {
+ return fail_parse_result();
+ }
+}
+
+ycf_parse_result parse_scope(ycf_symbol* symbols);
+
+ycf_parse_result parse_special_code_block(ycf_symbol* symbols, char* code_block_name, ycf_node_type type){
+ ycf_symbol* start = symbols;
+ if(symbols->type != ycf_symbol_type_special_code_start ||
+ !ycf_symbol_is_text_eq(start,
+ ycf_string_new("/*special_code_start:%s*/", code_block_name))){
+ return fail_parse_result();
+ }
+ ycf_parse_result code = parse_if_statement(start->next);
+ if(!code.success){
+ return fail_parse_result();
+ }
+ ycf_symbol* end = code.next_symbol;
+ if(end->type != ycf_symbol_type_special_code_end){
+ return fail_parse_result();
+ }
+ return success_parse_result(end->next,
+ ycf_node_special_code_block_new(type,
+ start,
+ code.result->u.if_n,
+ end));
+}
+
+ycf_parse_result parse_on_save_yield_state(ycf_symbol* symbols){
+ return parse_special_code_block(symbols, "ON_SAVE_YIELD_STATE", ycf_node_type_on_save_yield_state_code);
+}
+
+ycf_parse_result parse_on_restore_yield_state(ycf_symbol* symbols){
+ return parse_special_code_block(symbols, "ON_RESTORE_YIELD_STATE", ycf_node_type_on_restore_yield_state_code);
+}
+
+ycf_parse_result parse_destroy_state_code(ycf_symbol* symbols){
+ ycf_parse_result res = parse_special_code_block(symbols, "ON_DESTROY_STATE", ycf_node_type_on_destroy_state_code);
+ return res;
+}
+
+ycf_parse_result parse_on_return_code(ycf_symbol* symbols){
+ ycf_parse_result res = parse_special_code_block(symbols, "ON_RETURN", ycf_node_type_on_return_code);
+ return res;
+}
+
+ycf_parse_result parse_on_destroy_state_or_return_code(ycf_symbol* symbols){
+ ycf_parse_result res = parse_special_code_block(symbols, "ON_DESTROY_STATE_OR_RETURN", ycf_node_type_on_destroy_state_or_return_code);
+ return res;
+}
+
+ycf_parse_result parse_consume_reds(ycf_symbol* symbols){
+ ycf_symbol* consume_reds_symbol = symbols;
+ if(consume_reds_symbol->type != ycf_symbol_type_consume_reds){
+ return fail_parse_result();
+ }
+ ycf_parse_result paran_expression =
+ parse_paran_expression(consume_reds_symbol->next);
+ if(!paran_expression.success){
+ return fail_parse_result();
+ }
+ if(paran_expression.next_symbol->type != ycf_symbol_type_semicolon){
+ return fail_parse_result();
+ }
+ return success_parse_result(paran_expression.next_symbol->next,
+ ycf_node_consume_reds_new(consume_reds_symbol,
+ paran_expression.result->u.parentheses_expression,
+ paran_expression.next_symbol));
+}
+
+ycf_parse_result parse_statement(ycf_symbol* symbols){
+ int number_of_parsers = 22;
+ ycf_parse_result (*parsers[])(ycf_symbol *) = {
+ parse_defenition_no_init,
+ parse_defenition_with_init,
+ parse_if_else_statement,
+ parse_if_statement,
+ parse_while_statement,
+ parse_do_while_statement,
+ parse_for_statement,
+ parse_switch_statement,
+ parse_scope,
+ parse_goto,
+ parse_return_statement,
+ parse_consume_reds,
+ parse_function_call_statement,
+ parse_function_call_assignment_statement,
+ parse_assignment,
+ parse_on_save_yield_state,
+ parse_on_restore_yield_state,
+ parse_on_return_code,
+ parse_on_destroy_state_or_return_code,
+ parse_destroy_state_code,
+ parse_macro_command,
+ parse_exp_statement
+ };
+ for(int i = 0; i < number_of_parsers; i++){
+ ycf_parse_result res = parsers[i](symbols);
+ if(res.success){
+ return res;
+ }
+ }
+ return fail_parse_result();
+}
+
+ycf_parse_result parse_scope(ycf_symbol* symbols){
+ ycf_symbol* current = symbols;
+ ycf_symbol* start;
+ if(current->type != ycf_symbol_type_open_curly_brace){
+ return fail_parse_result();
+ }
+ start = current;
+ current = current->next;
+ int number_of_parsers = 2;
+ ycf_parse_result (*parsers[])(ycf_symbol *) = {
+ parse_defenition_no_init,
+ parse_defenition_with_init
+ };
+ parse_list_res decs = parse_list(number_of_parsers, parsers, current);
+ current = decs.next_symbol;
+ /* parse rest */
+ number_of_parsers = 1;
+ ycf_parse_result (*rest_parsers[])(ycf_symbol *) = {
+ parse_statement
+ };
+ parse_list_res others =
+ parse_list_break_symbol(number_of_parsers,
+ rest_parsers,
+ current,
+ ycf_symbol_type_end_curly_brace);
+ current = others.next_symbol;
+ if (current == NULL) {
+ return fail_parse_result();
+ }
+ return success_parse_result(current->next, ycf_node_scope_new(start,
+ decs.list,
+ others.list,
+ current));
+}
+
+ycf_parse_result parse_function(ycf_symbol* symbols){
+ ycf_parse_result fun_head = parse_function_head(symbols, ycf_symbol_type_open_curly_brace, true);
+ if(!fun_head.success){
+ return fail_parse_result();
+ }
+ ycf_parse_result scope = parse_scope(fun_head.next_symbol);
+ if(!scope.success){
+ return fail_parse_result();
+ }
+ return success_parse_result(scope.next_symbol,
+ ycf_node_function_new(fun_head.result->u.function_definition,
+ scope.result->u.code_scope));
+}
+
+ycf_parse_result parse_c_file(ycf_symbol* symbols){
+ int number_of_parsers = 5;
+ ycf_parse_result (*parsers[])(ycf_symbol *) = {
+ parse_defenition_no_init,
+ parse_defenition_with_init,
+ parse_function_def,
+ parse_function,
+ parse_other
+ };
+ parse_list_res program_list =
+ parse_list(number_of_parsers, parsers, symbols);
+ if(program_list.next_symbol != NULL){
+ return fail_parse_result();
+ } else {
+ return success_parse_result(NULL,
+ ycf_node_c_file_new(program_list.list));
+ }
+}
+
+
+ycf_node* get_abstract_syntax_tree_root(ycf_symbol_list* symbols){
+ ycf_parse_result res = parse_c_file(symbols->head);
+ if(!res.success){
+ printf("ERROR: Could not parse file\n");
+ exit(1);
+ }
+ return res.result;
+}
+
+ycf_node* ycf_node_deep_copy(ycf_node *n) {
+ ycf_string_printable_buffer *b = ycf_string_printable_buffer_new();
+ ycf_node_print(n, b);
+ ycf_symbol_list symbols = ycf_symbol_list_from_text(b->buffer);
+ ycf_parse_result res;
+ if (n->type == ycf_node_type_c_file) {
+ res = parse_c_file(symbols.head);
+ } else if (n->type == ycf_node_type_variable_definition ||
+ n->type == ycf_node_type_variable_definition_init ||
+ n->type == ycf_node_type_other ||
+ n->type == ycf_node_type_code_scope ||
+ n->type == ycf_node_type_assignment ||
+ n->type == ycf_node_type_yield ||
+ n->type == ycf_node_type_statement ||
+ n->type == ycf_node_type_if ||
+ n->type == ycf_node_type_if_else ||
+ n->type == ycf_node_type_while ||
+ n->type == ycf_node_type_do_while ||
+ n->type == ycf_node_type_switch ||
+ n->type == ycf_node_type_for ||
+ n->type == ycf_node_type_assignment_function_call ||
+ n->type == ycf_node_type_on_save_yield_state_code ||
+ n->type == ycf_node_type_on_restore_yield_state_code ||
+ n->type == ycf_node_type_on_destroy_state_code ||
+ n->type == ycf_node_type_on_return_code ||
+ n->type == ycf_node_type_on_destroy_state_or_return_code ||
+ n->type == ycf_node_type_goto ||
+ n->type == ycf_node_type_return_statement ||
+ n->type == ycf_node_type_consume_reds) {
+ res = parse_statement(symbols.head);
+ } else if (n->type == ycf_node_type_function_declaration) {
+ res = parse_function_def(symbols.head);
+ } else if (n->type == ycf_node_type_function_definition) {
+ res = parse_function(symbols.head);
+ } else if (n->type == ycf_node_type_function_call) {
+ res = parse_function_call(symbols.head);
+ } else if (n->type == ycf_node_type_expression) {
+ res = parse_expression(symbols.head);
+ } else if (n->type == ycf_node_type_parentheses_expression) {
+ res = parse_paran_expression(symbols.head);
+ } else if (n->type == ycf_node_type_comma) {
+ res = parse_comma(symbols.head);
+ } else if (n->type == ycf_node_type_array_bracket) {
+ res = parse_array_bracket(symbols.head);
+ } else if (n->type == ycf_node_type_macro_cmd) {
+ res = parse_macro_command(symbols.head);
+ } else if (n->type == ycf_node_type_period_field_access) {
+ res = parse_period_field_access_node(symbols.head);
+ } else if (n->type == ycf_node_type_pointer_field_access) {
+ res = parse_pointer_field_access_node(symbols.head);
+ } else {
+ fprintf(stderr, "Unknown node type %d\n", n->type);
+ exit(1);
+ }
+ if(!res.success) {
+ fprintf(stderr, "An error has been detected in the function ycf_node_deep_copy\n");
+ exit(1);
+ }
+ return res.result;
+}
+
diff --git a/erts/lib_src/yielding_c_fun/ycf_parser.h b/erts/lib_src/yielding_c_fun/ycf_parser.h
new file mode 100644
index 0000000000..566c7ec503
--- /dev/null
+++ b/erts/lib_src/yielding_c_fun/ycf_parser.h
@@ -0,0 +1,27 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB and Kjell Winblad 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%
+ */
+
+/*
+ * Author: Kjell Winblad
+ */
+
+#include "ycf_node.h"
+
+ycf_node* get_abstract_syntax_tree_root(ycf_symbol_list* symbols);
diff --git a/erts/lib_src/yielding_c_fun/ycf_printers.c b/erts/lib_src/yielding_c_fun/ycf_printers.c
new file mode 100644
index 0000000000..0cebfc5ebc
--- /dev/null
+++ b/erts/lib_src/yielding_c_fun/ycf_printers.c
@@ -0,0 +1,493 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB and Kjell Winblad 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%
+ */
+
+/*
+ * Author: Kjell Winblad
+ */
+
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "ycf_yield_fun.h"
+#include "ycf_node.h"
+
+void print_node_code_expression(ycf_node_expression e, ycf_string_printable_buffer* b);
+
+void print_symbol_code(ycf_symbol* s, ycf_string_printable_buffer* b){
+ if(s == NULL){
+ return;
+ }
+ print_symbol_code(s->whitespace_or_comment_before, b);
+ ycf_string_printable_buffer_printf(b, "%s", ycf_symbol_get_text(s));
+}
+
+void print_symbol_list(ycf_symbol_list* l, ycf_string_printable_buffer* b){
+ ycf_symbol* s = l->head;
+ while(s != NULL){
+ print_symbol_code(s, b);
+ s = s->next;
+ }
+}
+
+void print_node_code_array_bracket(ycf_node_array_bracket n, ycf_string_printable_buffer* b){
+ print_symbol_code(n.start, b);
+ if(!n.empty){
+ print_node_code_expression(n.content, b);
+ }
+ print_symbol_code(n.end, b);
+}
+
+void print_node_code_definition_custom_end(ycf_node_definition d, bool dyn_array_to_ptr, ycf_symbol* end, ycf_string_printable_buffer* b){
+ int nr_of_empty_brackets = 0;
+ {
+ ycf_node* current = d.array_brackets.head;
+ while(current != NULL){
+ if(current->u.array_bracket.empty){
+ nr_of_empty_brackets++;
+ }
+ current = current->next;
+ }
+ }
+ print_symbol_list(&d.type_specifiers, b);
+ if(dyn_array_to_ptr){
+ for(int i = 0; i < nr_of_empty_brackets; i++){
+ ycf_string_printable_buffer_printf(b, "*");
+ }
+ }
+ print_symbol_code(d.identifier, b);
+ if (!dyn_array_to_ptr || nr_of_empty_brackets == 0){
+ print_node_list_code(d.array_brackets.head, b);
+ }
+ print_symbol_code(end, b);
+}
+
+void print_node_code_definition(ycf_node_definition d, ycf_string_printable_buffer* b){
+ print_node_code_definition_custom_end(d, false, d.end, b);
+}
+
+void print_node_code_definition_init(ycf_node_definition_init d, ycf_string_printable_buffer* b){
+ print_node_code_definition(d.definition, b);
+ print_symbol_list(&d.initializer_expression, b);
+ print_symbol_code(d.end, b);
+}
+
+void print_node_list_code(ycf_node* n, ycf_string_printable_buffer* b){
+ while(n != NULL){
+ ycf_node_print(n, b);
+ n = n->next;
+ }
+}
+
+void print_node_code_function_def_parameters(ycf_node_function_definition f_def, ycf_string_printable_buffer* b){
+ if(f_def.ignore_param_ending){
+ ycf_node* n = f_def.parameters.head;
+ while(n != NULL){
+ if(n->next == NULL){
+ print_node_code_definition_custom_end(n->u.definition, false, ycf_symbol_new_parenthesis(), b);
+ }else{
+ print_node_code_definition_custom_end(n->u.definition, false, ycf_symbol_new_comma(), b);
+ }
+ n = n->next;
+ }
+ if(f_def.end.head != NULL && f_def.end.head->type == ycf_symbol_type_end_parenthesis){
+ ycf_symbol_list end = f_def.end;
+ end.head = end.head->next;
+ print_symbol_list(&end, b);
+ }else if(f_def.end.head != NULL && f_def.end.head->type != ycf_symbol_type_void){
+ print_symbol_list(&f_def.end, b);
+ }
+ }else {
+ print_node_list_code(f_def.parameters.head, b);
+ print_symbol_list(&f_def.end, b);
+ }
+}
+void print_node_code_function_def(ycf_node_function_definition f_def, ycf_string_printable_buffer* b){
+ print_node_code_definition(f_def.definition, b);
+ print_node_code_function_def_parameters(f_def, b);
+}
+
+void print_node_code_scope(ycf_node_code_scope s, ycf_string_printable_buffer* b){
+ print_symbol_code(s.start, b);
+ print_node_list_code(s.definition_nodes.head, b);
+ print_node_list_code(s.other_nodes.head, b);
+ print_symbol_code(s.end, b);
+}
+
+void print_node_code_function(ycf_node_function f, ycf_string_printable_buffer* b){
+ print_node_code_function_def(f.definition, b);
+ print_node_code_scope(f.body, b);
+}
+
+void print_node_code_assignment(ycf_node_assignment a, ycf_string_printable_buffer* b){
+ print_node_code_expression(a.left_side, b);
+ print_symbol_code(a.assignment_symbol, b);
+ print_node_code_expression(a.right_side, b);
+ print_symbol_code(a.end, b);
+}
+
+void print_node_code_gen_struct(ycf_node_gen_typedef_struct n, ycf_string_printable_buffer* b){
+ ycf_string_printable_buffer_printf(b, "\n\n\nstruct %s {", n.name);
+ ycf_node* current = n.definition_nodes.head;
+ while(current != NULL){
+ print_node_code_definition_custom_end(current->u.definition, true, ycf_symbol_new_semicolon(), b);
+ current = current->next;
+ }
+ ycf_string_printable_buffer_printf(b, "\n};\n");
+}
+
+void print_node_code_yield(ycf_node_yield n, ycf_string_printable_buffer* b){
+ print_symbol_code(n.yield_symbol,b);
+ print_symbol_code(n.end_symbol,b);
+
+}
+
+void print_node_code_function_call(ycf_node_function_call n, ycf_string_printable_buffer* b){
+ print_symbol_list(&n.neg_symbols, b);
+ print_symbol_code(n.identifier, b);
+ print_symbol_code(n.start_symbol, b);
+ print_node_list_code(n.parameter_expressions.head, b);
+ print_symbol_code(n.end_symbol, b);
+}
+
+void print_node_code_expression(ycf_node_expression e, ycf_string_printable_buffer* b){
+ print_node_list_code(e.content.head, b);
+}
+
+
+void print_node_code_paran_expression(ycf_node_parentheses_expression e, ycf_string_printable_buffer* b){
+ print_symbol_code(e.start_symbol, b);
+ print_node_code_expression(e.content, b);
+ print_symbol_code(e.end_symbol, b);
+}
+
+void print_node_code_consume_reds(ycf_node_consume_reds e, ycf_string_printable_buffer* b){
+ print_symbol_code(e.consume_reds_symbol, b);
+ print_node_code_paran_expression(e.nr_of_reds_expression, b);
+ print_symbol_code(e.end_symbol, b);
+}
+
+void print_node_code_if_statement(ycf_node_if e, ycf_string_printable_buffer* b){
+ print_symbol_code(e.if_word, b);
+ print_node_code_paran_expression(e.expression, b);
+ ycf_node_print(e.if_statement, b);
+}
+
+void print_node_code_while_statement(ycf_node_while e, ycf_string_printable_buffer* b){
+ print_symbol_code(e.while_word, b);
+ print_node_code_paran_expression(e.expression, b);
+ ycf_node_print(e.statement, b);
+}
+
+void print_node_code_do_while_statement(ycf_node_do_while e, ycf_string_printable_buffer* b){
+ print_symbol_code(e.do_word, b);
+ ycf_node_print(e.statement, b);
+ print_symbol_code(e.while_word, b);
+ print_node_code_paran_expression(e.expression, b);
+ print_symbol_code(e.end, b);
+}
+
+void print_node_code_switch_statement(ycf_node_switch e, ycf_string_printable_buffer* b){
+ print_symbol_code(e.switch_word, b);
+ print_node_code_paran_expression(e.expression, b);
+ print_node_code_scope(e.scope, b);
+}
+
+void print_node_code_for_statement(ycf_node_for e, ycf_string_printable_buffer* b){
+ print_symbol_code(e.for_word, b);
+ print_symbol_code(e.start_parentheses, b);
+ ycf_node_print(e.init, b);
+ ycf_node_print(e.stop_cond, b);
+ print_symbol_code(e.stop_cond_end, b);
+ ycf_node_print(e.end_exp, b);
+ print_symbol_code(e.end_parentheses, b);
+ ycf_node_print(e.statement, b);
+}
+
+void print_node_code_if_else_statement(ycf_node_if_else e, ycf_string_printable_buffer* b){
+ print_node_code_if_statement(e.if_part, b);
+ print_symbol_code(e.else_word, b);
+ ycf_node_print(e.else_statement, b);
+}
+
+void print_node_code_for_assignment_fun_call(ycf_node_function_call_assignment e, ycf_string_printable_buffer* b){
+ print_node_code_expression(e.left_side, b);
+ print_symbol_code(e.assignment_symbol, b);
+ print_node_code_function_call(e.fun_call, b);
+}
+
+void print_node_code_special_code_block(ycf_node_special_code_block block, ycf_string_printable_buffer* b){
+ print_symbol_code(block.start, b);
+ print_node_code_if_statement(block.code, b);
+ print_symbol_code(block.end, b);
+
+}
+
+void print_node_code_goto(ycf_node_goto n, ycf_string_printable_buffer* b){
+ print_symbol_code(n.goto_symbol, b);
+ print_symbol_code(n.label_symbol, b);
+ print_symbol_code(n.end_symbol, b);
+}
+
+void print_node_code_period_field_access(ycf_node_period_field_access n, ycf_string_printable_buffer* b){
+ print_symbol_code(n.period, b);
+ print_symbol_code(n.field_name, b);
+}
+
+void print_node_code_pointer_field_access(ycf_pointer_field_access n, ycf_string_printable_buffer* b){
+ print_symbol_code(n.pointer, b);
+ print_symbol_code(n.field_name, b);
+}
+
+void print_node_code_return(ycf_node_return n, ycf_string_printable_buffer* b){
+ print_symbol_code(n.return_symbol, b);
+ if (n.return_expression != NULL) {
+ ycf_node_print(n.return_expression, b);
+ }
+ print_symbol_code(n.end_symbol, b);
+}
+
+void ycf_node_print(ycf_node* node, ycf_string_printable_buffer* b){
+ if(node == NULL){
+ return;
+ } else if(node->type == ycf_node_type_c_file){
+ print_node_list_code(node->u.c_file.content.head, b);
+ } else if(node->type == ycf_node_type_variable_definition){
+ print_node_code_definition(node->u.definition, b);
+ } else if(node->type == ycf_node_type_variable_definition_init){
+ print_node_code_definition_init(node->u.definition_init, b);
+ } else if(node->type == ycf_node_type_function_declaration){
+ print_node_code_function_def(node->u.function_definition, b);
+ } else if(node->type == ycf_node_type_other){
+ print_symbol_code(node->u.other.what, b);
+ } else if(node->type == ycf_node_type_code_scope){
+ print_node_code_scope(node->u.code_scope, b);
+ }else if(node->type == ycf_node_type_function_definition){
+ print_node_code_function(node->u.function, b);
+ }else if(node->type == ycf_node_type_assignment){
+ print_node_code_assignment(node->u.a, b);
+ }else if(node->type == ycf_node_type_gen_typedef_struct){
+ print_node_code_gen_struct(node->u.gen_typedef_struct, b);
+ }else if(node->type == ycf_node_type_yield){
+ print_node_code_yield(node->u.yield, b);
+ } else if(node->type == ycf_node_type_statement){
+ ycf_node_print(node->u.statement.expression, b);
+ print_symbol_code(node->u.statement.end_symbol, b);
+ } else if(node->type == ycf_node_type_function_call){
+ print_node_code_function_call(node->u.function_call, b);
+ } else if(node->type == ycf_node_type_expression){
+ print_node_code_expression(node->u.expression, b);
+ } else if(node->type == ycf_node_type_parentheses_expression){
+ print_node_code_paran_expression(node->u.parentheses_expression, b);
+ } else if(node->type == ycf_node_type_if){
+ print_node_code_if_statement(node->u.if_n, b);
+ }else if(node->type == ycf_node_type_if_else){
+ print_node_code_if_else_statement(node->u.if_else, b);
+ }else if(node->type == ycf_node_type_while){
+ print_node_code_while_statement(node->u.while_n, b);
+ }else if(node->type == ycf_node_type_do_while){
+ print_node_code_do_while_statement(node->u.do_while, b);
+ }else if(node->type == ycf_node_type_switch){
+ print_node_code_switch_statement(node->u.switch_n, b);
+ }else if(node->type == ycf_node_type_for){
+ print_node_code_for_statement(node->u.for_n, b);
+ }else if(node->type == ycf_node_type_assignment_function_call){
+ print_node_code_for_assignment_fun_call(node->u.function_call_assignment, b);
+ } else if(node->type == ycf_node_type_comma){
+ print_symbol_code(node->u.comma.comma_symbol, b);
+ } else if(node->type == ycf_node_type_array_bracket){
+ print_node_code_array_bracket(node->u.array_bracket, b);
+ } else if(node->type == ycf_node_type_macro_cmd){
+ print_symbol_code(node->u.macro_cmd.symbol, b);
+ } else if(node->type == ycf_node_type_on_save_yield_state_code ||
+ node->type == ycf_node_type_on_restore_yield_state_code ||
+ node->type == ycf_node_type_on_destroy_state_code ||
+ node->type == ycf_node_type_on_return_code ||
+ node->type == ycf_node_type_on_destroy_state_or_return_code){
+ print_node_code_special_code_block(node->u.special_code_block, b);
+ } else if(node->type == ycf_node_type_consume_reds){
+ print_node_code_consume_reds(node->u.consume_reds, b);
+ } else if(node->type == ycf_node_type_goto){
+ print_node_code_goto(node->u.goto_n, b);
+ } else if(node->type == ycf_node_type_return_statement){
+ print_node_code_return(node->u.return_n, b);
+ } else if(node->type == ycf_node_type_period_field_access){
+ print_node_code_period_field_access(node->u.period_field_access, b);
+ } else if(node->type == ycf_node_type_pointer_field_access){
+ print_node_code_pointer_field_access(node->u.pointer_field_access, b);
+ } else {
+ fprintf(stderr, "Unknown node type %d\n", node->type);
+ exit(1);
+ }
+}
+
+
+void print_definition(ycf_node_definition d){
+ printf("NODE: definition (type=%s,%s=%s)\n",
+ ycf_symbol_text_between(d.type_specifiers.head,
+ d.type_specifiers.last),
+ get_symbol_type_text(d.identifier->type),
+ ycf_symbol_get_text(d.identifier) );
+}
+
+void print_scope(ycf_node_code_scope node){
+ printf("NODE: scope\n");
+ printf(">>>>>>>>>>>>>>>>>>>>>>>\n");
+ printf("Defenition Nodes:\n");
+ struct ycf_node* n = node.definition_nodes.head;
+ while(n != NULL){
+ print_abstract_syntax_tree(n);
+ n = n->next;
+ }
+ printf("Other Nodes:\n");
+ n = node.other_nodes.head;
+ while(n != NULL){
+ print_abstract_syntax_tree(n);
+ n = n->next;
+ }
+ printf("END SCOPE\n");
+ printf("<<<<<<<<<<<<<<<<<<<<<<<\n");
+}
+
+void print_function_def(ycf_node_function_definition node){
+ printf("NODE: function def:\n");
+ printf(">>>>>>>>>>>>>>>>>>>>>>>\n");
+ printf("definition:\n");
+ print_definition(node.definition);
+ printf("parameters:\n");
+ struct ycf_node* n = node.parameters.head;
+ while(n != NULL){
+ print_abstract_syntax_tree(n);
+ n = n->next;
+ }
+ printf("<<<<<<<<<<<<<<<<<<<<<<<\n");
+}
+
+void print_fun_call(ycf_node_function_call f_call){
+ printf("NODE: fun_call %s parameters:\n", ycf_symbol_get_text(f_call.identifier));
+ printf(">>>>>>>>>>>>>>>>>>>>>>>\n");
+ struct ycf_node* current = f_call.parameter_expressions.head;
+ int i = 1;
+ while(current != NULL){
+ printf("param %d: \n", i);
+ ycf_node_print(current, NULL);
+ current = current->next;
+ i++;
+ }
+ printf("<<<<<<<<<<<<<<<<<<<<<<<\n");
+}
+
+void print_abstract_syntax_tree(ycf_node* node){
+ printf("%p\n",(void*)node);
+ if(node == NULL){
+ return;
+ } else if(node->type == ycf_node_type_c_file){
+ printf("NODE: c_file\n");
+ printf(">>>>>>>>>>>>>>>>>>>>>>>\n");
+ struct ycf_node* n = node->u.c_file.content.head;
+ while(n != NULL){
+ print_abstract_syntax_tree(n);
+ n = n->next;
+ }
+ printf("<<<<<<<<<<<<<<<<<<<<<<<\n");
+ } else if(node->type == ycf_node_type_variable_definition){
+ print_definition(node->u.definition);
+ } else if(node->type == ycf_node_type_variable_definition_init){
+ printf("NODE: definition_init (type=%s,%s=%s,init=%s)\n",
+ ycf_symbol_text_between(node->u.definition_init.definition.type_specifiers.head,
+ node->u.definition_init.definition.type_specifiers.last),
+ get_symbol_type_text(node->u.definition_init.definition.identifier->type),
+ ycf_symbol_get_text(node->u.definition_init.definition.identifier),
+ ycf_symbol_text_between(node->u.definition_init.initializer_expression.head,
+ node->u.definition_init.initializer_expression.last));
+ } else if(node->type == ycf_node_type_function_declaration){
+ print_function_def(node->u.function_definition);
+ } else if(node->type == ycf_node_type_other){
+ printf("NODE: other (%s)\n", get_symbol_type_text(node->u.other.what->type));
+ } else if(node->type == ycf_node_type_code_scope){
+ print_scope(node->u.code_scope);
+ }else if(node->type == ycf_node_type_function_definition){
+ printf("NODE: function\n");
+ printf(">>>>>>>>>>>>>>>>>>>>>>>\n");
+ print_function_def(node->u.function.definition);
+ print_scope(node->u.function.body);
+ printf("<<<<<<<<<<<<<<<<<<<<<<<\n");
+ }else if(node->type == ycf_node_type_assignment){
+ printf("NODE: assignment\n");
+ printf(">>>>>>>>>>>>>>>>>>>>>>>\n");
+ print_node_code_assignment(node->u.a, NULL);
+ printf("<<<<<<<<<<<<<<<<<<<<<<<\n");
+ }else if(node->type == ycf_node_type_yield){
+ printf("NODE: yield\n");
+ }else if(node->type == ycf_node_type_statement){
+ printf("NODE: statement\n");
+ printf(">>>>>>>>>>>>>>>>>>>>>>>\n");
+ print_abstract_syntax_tree(node->u.statement.expression);
+ printf("<<<<<<<<<<<<<<<<<<<<<<<\n");
+ }else if(node->type == ycf_node_type_if){
+ printf("NODE: if_statement\n");
+ printf(">>>>>>>>>>>>>>>>>>>>>>>\n");
+ ycf_node_print(node, NULL);
+ printf("<<<<<<<<<<<<<<<<<<<<<<<\n");
+ }else if(node->type == ycf_node_type_if_else){
+ printf("NODE: if_else_statement\n");
+ printf(">>>>>>>>>>>>>>>>>>>>>>>\n");
+ ycf_node_print(node, NULL);
+ printf("<<<<<<<<<<<<<<<<<<<<<<<\n");
+ }else if(node->type == ycf_node_type_while){
+ printf("NODE: while_statement\n");
+ printf(">>>>>>>>>>>>>>>>>>>>>>>\n");
+ ycf_node_print(node, NULL);
+ printf("<<<<<<<<<<<<<<<<<<<<<<<\n");
+ }else if(node->type == ycf_node_type_do_while){
+ printf("NODE: do_while_statement\n");
+ printf(">>>>>>>>>>>>>>>>>>>>>>>\n");
+ ycf_node_print(node, NULL);
+ printf("<<<<<<<<<<<<<<<<<<<<<<<\n");
+ }else if(node->type == ycf_node_type_for){
+ printf("NODE: for_statement\n");
+ printf(">>>>>>>>>>>>>>>>>>>>>>>\n");
+ ycf_node_print(node, NULL);
+ printf("<<<<<<<<<<<<<<<<<<<<<<<\n");
+ }else if(node->type == ycf_node_type_switch){
+ printf("NODE: switch_statement\n");
+ printf(">>>>>>>>>>>>>>>>>>>>>>>\n");
+ ycf_node_print(node, NULL);
+ printf("<<<<<<<<<<<<<<<<<<<<<<<\n");
+ } else if(node->type == ycf_node_type_function_call) {
+ print_fun_call(node->u.function_call);
+ } else if(node->type == ycf_node_type_assignment_function_call) {
+ printf("NODE: assignment fun call\n");
+ printf(">>>>>>>>>>>>>>>>>>>>>>>\n");
+ printf("Assignment expression:\n");
+ print_node_code_expression(node->u.function_call_assignment.left_side, NULL);
+ print_fun_call(node->u.function_call_assignment.fun_call);
+ printf("<<<<<<<<<<<<<<<<<<<<<<<\n");
+
+
+ } else {
+ printf("NODE: OTHER???\n");
+ printf(">>>>>>>>>>>>>>>>>>>>>>>\n");
+ ycf_node_print(node, NULL);
+ printf("<<<<<<<<<<<<<<<<<<<<<<<\n");
+ }
+
+}
+
diff --git a/erts/lib_src/yielding_c_fun/ycf_printers.h b/erts/lib_src/yielding_c_fun/ycf_printers.h
new file mode 100644
index 0000000000..1cc23165c8
--- /dev/null
+++ b/erts/lib_src/yielding_c_fun/ycf_printers.h
@@ -0,0 +1,30 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB and Kjell Winblad 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%
+ */
+
+/*
+ * Author: Kjell Winblad
+ */
+
+#include "ycf_node.h"
+
+void print_node_code_function_def_parameters(ycf_node_function_definition f_def, ycf_string_printable_buffer* b);
+void print_node_code_function_def(ycf_node_function_definition f_def, ycf_string_printable_buffer* b);
+void print_node_list_code(ycf_node* n, ycf_string_printable_buffer* b);
+
diff --git a/erts/lib_src/yielding_c_fun/ycf_string.c b/erts/lib_src/yielding_c_fun/ycf_string.c
new file mode 100644
index 0000000000..aabb0c3c61
--- /dev/null
+++ b/erts/lib_src/yielding_c_fun/ycf_string.c
@@ -0,0 +1,111 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB and Kjell Winblad 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%
+ */
+
+/*
+ * Author: Kjell Winblad
+ */
+
+
+#include <string.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <stdio.h>
+
+#include "ycf_string.h"
+#include "ycf_lists.h"
+#include "ycf_utils.h"
+
+bool ycf_string_is_equal(const char* str1, const char* str2)
+{
+ size_t str1_length = strlen(str1);
+ size_t str2_length = strlen(str2);
+ return
+ str1_length == str2_length &&
+ strncmp(str1, str2, str1_length) == 0;
+}
+
+
+ycf_string_item* ycf_string_item_new(char* str){
+ ycf_string_item* item = ycf_malloc(sizeof(ycf_string_item));
+ item->str = str;
+ item->next = NULL;
+ return item;
+}
+
+char* ycf_string_new(char* format, ...){
+ va_list args;
+ va_start (args, format);
+ int n = vsnprintf(NULL, 0, format, args);
+ va_end (args);
+ char* new = ycf_malloc(n +1);
+ va_start (args, format);
+ vsnprintf(new, n +1, format, args);
+ va_end (args);
+ return new;
+}
+
+
+ycf_string_printable_buffer* ycf_string_printable_buffer_new(){
+ const size_t init_size = 128;
+ ycf_string_printable_buffer* b = ycf_malloc(sizeof(ycf_string_printable_buffer));
+ b->current_pos = 0;
+ b->buffer = ycf_malloc(init_size);
+ b->size = init_size;
+ return b;
+}
+
+void ycf_string_printable_buffer_printf(ycf_string_printable_buffer* buf, char* format, ...){
+ va_list args;
+ if(buf == NULL){
+ va_start (args, format);
+ vprintf(format, args);
+ va_end (args);
+ return;
+ }
+ va_start (args, format);
+ int n = vsnprintf(NULL, 0, format, args) + 1;
+ va_end (args);
+ while(buf->current_pos + n + 64 > buf->size){
+ char* new_buf = ycf_malloc(buf->size * 2);
+ for(int i = 0; i < buf->size; i++){
+ new_buf[i] = buf->buffer[i];
+ }
+ buf->buffer = new_buf;
+ buf->size = buf->size * 2;
+ }
+ va_start (args, format);
+ vsnprintf(&buf->buffer[buf->current_pos], n, format, args);
+ va_end (args);
+ buf->current_pos = buf->current_pos + n - 1;
+}
+
+bool ycf_string_item_list_contains(ycf_string_item_list* l, char* str){
+ ycf_string_item* current = l->head;
+ while(current != NULL){
+ if(strcmp(current->str, str) == 0){
+ return true;
+ }
+ current = current->next;
+ }
+ return false;
+}
+
+
+GENERATE_LIST_FUNCTIONS(ycf_string_item)
diff --git a/erts/lib_src/yielding_c_fun/ycf_string.h b/erts/lib_src/yielding_c_fun/ycf_string.h
new file mode 100644
index 0000000000..0c19e08572
--- /dev/null
+++ b/erts/lib_src/yielding_c_fun/ycf_string.h
@@ -0,0 +1,89 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB and Kjell Winblad 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%
+ */
+
+/*
+ * Author: Kjell Winblad
+ */
+
+#ifndef YIELDING_C_FUN_YCF_STRING_H
+#define YIELDING_C_FUN_YCF_STRING_H
+
+#include <stdlib.h>
+#include "ycf_utils.h"
+
+/* Types for printable buffers */
+
+typedef struct {
+ char* buffer;
+ size_t current_pos;
+ size_t size;
+} ycf_string_printable_buffer;
+
+/* Types for string lists */
+
+typedef struct ycf_string_item {
+ char* str;
+ struct ycf_string_item* next;
+} ycf_string_item;
+
+typedef struct string_item_list {
+ ycf_string_item* head;
+ ycf_string_item* last;
+} ycf_string_item_list;
+
+
+bool ycf_string_is_equal(const char* str1, const char* str2);
+ycf_string_item* ycf_string_item_new(char* str);
+char* ycf_string_new(char* format, ...);
+
+/* Functions for printable buffers */
+
+ycf_string_printable_buffer* ycf_string_printable_buffer_new(void);
+void ycf_string_printable_buffer_printf(ycf_string_printable_buffer* buf, char* format, ...);
+
+/* Functions for string lists */
+
+int ycf_string_item_list_get_item_position(ycf_string_item_list* list, ycf_string_item* node);
+
+bool ycf_string_item_list_contains(ycf_string_item_list* l, char* str);
+
+ycf_string_item* ycf_string_item_shallow_copy(ycf_string_item* n);
+ycf_string_item* ycf_string_item_list_get_item_at_position(ycf_string_item_list* list, int pos);
+
+void ycf_string_item_list_append(ycf_string_item_list* list, ycf_string_item* node);
+void ycf_string_item_list_prepend(ycf_string_item_list* list, ycf_string_item* node);
+void ycf_string_item_list_insert_before(ycf_string_item_list* list, ycf_string_item* before_this, ycf_string_item* to_insert);
+void ycf_string_item_list_insert_after(ycf_string_item_list* list, ycf_string_item* after_this, ycf_string_item* to_insert);
+void ycf_string_item_list_remove(ycf_string_item_list* list, ycf_string_item* to_remove);
+void ycf_string_item_list_replace(ycf_string_item_list* list, ycf_string_item* to_replace, ycf_string_item* replace_with);
+void ycf_string_item_list_concat(ycf_string_item_list* list1, ycf_string_item_list* list2);
+
+ycf_string_item_list ycf_string_item_list_empty();
+ycf_string_item_list ycf_string_item_list_shallow_copy(ycf_string_item_list n);
+ycf_string_item_list ycf_string_item_list_copy_append(ycf_string_item_list list, ycf_string_item* node);
+ycf_string_item_list ycf_string_item_list_copy_prepend(ycf_string_item_list list, ycf_string_item* node);
+ycf_string_item_list ycf_string_item_list_copy_insert_before(ycf_string_item_list list, ycf_string_item* before_this, ycf_string_item* to_insert);
+ycf_string_item_list ycf_string_item_list_copy_insert_after(ycf_string_item_list list, ycf_string_item* after_this, ycf_string_item* to_insert);
+ycf_string_item_list ycf_string_item_list_copy_remove(ycf_string_item_list list, ycf_string_item* to_remove);
+ycf_string_item_list ycf_string_item_list_copy_replace(ycf_string_item_list list, ycf_string_item* to_replace, ycf_string_item* replace_with);
+ycf_string_item_list ycf_string_item_list_copy_concat(ycf_string_item_list list1, ycf_string_item_list list2);
+
+
+#endif //YIELDING_C_FUN_YCF_STRING_H
diff --git a/erts/lib_src/yielding_c_fun/ycf_symbol.c b/erts/lib_src/yielding_c_fun/ycf_symbol.c
new file mode 100644
index 0000000000..826aac3afd
--- /dev/null
+++ b/erts/lib_src/yielding_c_fun/ycf_symbol.c
@@ -0,0 +1,166 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB and Kjell Winblad 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%
+ */
+
+/*
+ * Author: Kjell Winblad
+ */
+
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "ycf_symbol.h"
+#include "ycf_string.h"
+#include "ycf_utils.h"
+#include "ycf_lists.h"
+
+
+
+ycf_symbol* ycf_symbol_copy(ycf_symbol* symbol){
+ ycf_symbol* new_symbol = ycf_malloc(sizeof(ycf_symbol));
+ new_symbol->next = NULL;
+ new_symbol->source = symbol->source;
+ new_symbol->start = symbol->start;
+ new_symbol->stop = symbol->stop;
+ new_symbol->type = symbol->type;
+ new_symbol->whitespace_or_comment_before = symbol->whitespace_or_comment_before;
+ return new_symbol;
+}
+
+ycf_symbol* ycf_symbol_copy_change_text(ycf_symbol* to_copy, char* new_text){
+ ycf_symbol* new_symbol = ycf_malloc(sizeof(ycf_symbol));
+ new_symbol->next = NULL;
+ new_symbol->source = new_text;
+ new_symbol->start = 0;
+ new_symbol->stop = strlen(new_text);
+ new_symbol->type = to_copy->type;
+ new_symbol->whitespace_or_comment_before = to_copy->whitespace_or_comment_before;
+ return new_symbol;
+}
+
+ycf_symbol* ycf_symbol_new_something_else(char* text){
+ ycf_symbol* new = ycf_malloc(sizeof(ycf_symbol));
+ new->type = ycf_symbol_type_something_else;
+ new->next = NULL;
+ new->source = text;
+ new->whitespace_or_comment_before = NULL;
+ new->start = 0;
+ new->stop = strlen(text);
+ return new;
+}
+
+ycf_symbol* ycf_symbol_new_semicolon(){
+ ycf_symbol* new_symbol = ycf_malloc(sizeof(ycf_symbol));
+ new_symbol->next = NULL;
+ new_symbol->source = ";";
+ new_symbol->start = 0;
+ new_symbol->stop = strlen(";");
+ new_symbol->type = ycf_symbol_type_semicolon;
+ new_symbol->whitespace_or_comment_before = NULL;
+ return new_symbol;
+}
+
+ycf_symbol* ycf_symbol_new_identifier(char* name){
+ ycf_symbol* new_symbol = ycf_malloc(sizeof(ycf_symbol));
+ new_symbol->next = NULL;
+ new_symbol->source = name;
+ new_symbol->start = 0;
+ new_symbol->stop = strlen(new_symbol->source);
+ new_symbol->type = ycf_symbol_type_identifier;
+ new_symbol->whitespace_or_comment_before = NULL;
+ return new_symbol;
+}
+
+ycf_symbol* ycf_symbol_new_star(){
+ ycf_symbol* new_symbol = ycf_malloc(sizeof(ycf_symbol));
+ new_symbol->next = NULL;
+ new_symbol->source = "*";
+ new_symbol->start = 0;
+ new_symbol->stop = strlen("*");
+ new_symbol->type = ycf_symbol_type_star;
+ new_symbol->whitespace_or_comment_before = NULL;
+ return new_symbol;
+}
+
+ycf_symbol* ycf_symbol_new_parenthesis(){
+ ycf_symbol* new_symbol = ycf_malloc(sizeof(ycf_symbol));
+ new_symbol->next = NULL;
+ new_symbol->source = ")";
+ new_symbol->start = 0;
+ new_symbol->stop = strlen(")");
+ new_symbol->type = ycf_symbol_type_semicolon;
+ new_symbol->whitespace_or_comment_before = NULL;
+ return new_symbol;
+}
+
+ycf_symbol* ycf_symbol_new_open_curly_brace(){
+ ycf_symbol* new_symbol = ycf_malloc(sizeof(ycf_symbol));
+ new_symbol->next = NULL;
+ new_symbol->source = "\n{\n";
+ new_symbol->start = 0;
+ new_symbol->stop = strlen(new_symbol->source);
+ new_symbol->type = ycf_symbol_type_open_curly_brace;
+ new_symbol->whitespace_or_comment_before = NULL;
+ return new_symbol;
+}
+
+ycf_symbol* ycf_symbol_new_end_curly_brace(){
+ ycf_symbol* new_symbol = ycf_malloc(sizeof(ycf_symbol));
+ new_symbol->next = NULL;
+ new_symbol->source = "\n}\n";
+ new_symbol->start = 0;
+ new_symbol->stop = strlen(new_symbol->source);
+ new_symbol->type = ycf_symbol_type_end_curly_brace;
+ new_symbol->whitespace_or_comment_before = NULL;
+ return new_symbol;
+}
+
+
+ycf_symbol* ycf_symbol_new_comma(){
+ ycf_symbol* new_symbol = ycf_malloc(sizeof(ycf_symbol));
+ new_symbol->next = NULL;
+ new_symbol->source = ",";
+ new_symbol->start = 0;
+ new_symbol->stop = strlen(",");
+ new_symbol->type = ycf_symbol_type_semicolon;
+ new_symbol->whitespace_or_comment_before = NULL;
+ return new_symbol;
+}
+
+
+char* ycf_symbol_get_text(ycf_symbol* symbol){
+ int size = symbol->stop - symbol->start;
+ char* str = ycf_malloc(size+1);
+ strncpy(str, &symbol->source[symbol->start], size);
+ str[size] = 0;
+ return str;
+}
+
+char* ycf_symbol_list_to_str(ycf_symbol_list* l){
+ ycf_symbol* s = l->head;
+ char* str = "";
+ while(s != NULL){
+ str = ycf_string_new(" %s %s ", str, ycf_symbol_get_text(s));
+ s = s->next;
+ }
+ return str;
+}
+
+GENERATE_LIST_FUNCTIONS(ycf_symbol)
diff --git a/erts/lib_src/yielding_c_fun/ycf_symbol.h b/erts/lib_src/yielding_c_fun/ycf_symbol.h
new file mode 100644
index 0000000000..b6d69cc5cb
--- /dev/null
+++ b/erts/lib_src/yielding_c_fun/ycf_symbol.h
@@ -0,0 +1,110 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB and Kjell Winblad 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%
+ */
+
+/*
+ * Author: Kjell Winblad
+ */
+
+#ifndef YIELDING_C_FUN_YCF_SYMBOL_H
+#define YIELDING_C_FUN_YCF_SYMBOL_H
+
+/* Types for symbols */
+
+typedef enum {
+ ycf_symbol_type_comment,
+ ycf_symbol_type_string_literal,
+ ycf_symbol_type_macro_define,
+ ycf_symbol_type_macro_command,
+ ycf_symbol_type_whitespace,
+ ycf_symbol_type_identifier,
+ ycf_symbol_type_number,
+ ycf_symbol_type_open_parenthesis,
+ ycf_symbol_type_end_parenthesis,
+ ycf_symbol_type_open_curly_brace,
+ ycf_symbol_type_end_curly_brace,
+ ycf_symbol_type_open_square_bracket,
+ ycf_symbol_type_end_square_bracket,
+ ycf_symbol_type_not_equal_sign,
+ ycf_symbol_type_equal_sign,
+ ycf_symbol_type_equal_equal_sign,
+ ycf_symbol_type_star,
+ ycf_symbol_type_neg,
+ ycf_symbol_type_semicolon,
+ ycf_symbol_type_comma,
+ ycf_symbol_type_pointer_field_access,
+ ycf_symbol_type_period,
+ ycf_symbol_type_special_code_start,
+ ycf_symbol_type_special_code_end,
+ ycf_symbol_type_const,
+ ycf_symbol_type_void,
+ ycf_symbol_type_static,
+ ycf_symbol_type_inline,
+ ycf_symbol_type_volatile,
+ ycf_symbol_type_consume_reds,
+ ycf_symbol_type_return,
+ ycf_symbol_type_if,
+ ycf_symbol_type_else,
+ ycf_symbol_type_goto,
+ ycf_symbol_type_while,
+ ycf_symbol_type_do,
+ ycf_symbol_type_for,
+ ycf_symbol_type_switch,
+ ycf_symbol_type_break,
+ ycf_symbol_type_continue,
+ ycf_symbol_type_something_else
+} ycf_symbol_type;
+
+
+typedef struct ycf_symbol {
+ ycf_symbol_type type;
+ int start;
+ int stop;
+ char* source;
+ struct ycf_symbol* whitespace_or_comment_before;
+ struct ycf_symbol* next;
+} ycf_symbol;
+
+typedef struct symbol_list {
+ struct ycf_symbol* head;
+ struct ycf_symbol* last;
+} ycf_symbol_list;
+
+/* Functions for symbols */
+
+ycf_symbol_list ycf_symbol_list_shallow_copy(ycf_symbol_list n);
+
+ycf_symbol* ycf_symbol_new_something_else(char* text);
+ycf_symbol* ycf_symbol_copy_change_text(ycf_symbol* to_copy, char* new_text);
+ycf_symbol* ycf_symbol_new_semicolon(void);
+ycf_symbol* ycf_symbol_new_star();
+ycf_symbol* ycf_symbol_new_parenthesis(void);
+ycf_symbol* ycf_symbol_new_comma(void);
+ycf_symbol* ycf_symbol_new_open_curly_brace(void);
+ycf_symbol* ycf_symbol_new_end_curly_brace(void);
+ycf_symbol* ycf_symbol_new_identifier(char* name);
+char* ycf_symbol_get_text(ycf_symbol* symbol);
+ycf_symbol_list ycf_symbol_list_from_text(char* text);
+void ycf_symbol_list_print(char* text);
+char* get_symbol_type_text(ycf_symbol_type type);
+ycf_symbol* ycf_symbol_copy(ycf_symbol* symbol);
+char* ycf_symbol_text_between(ycf_symbol* s1, ycf_symbol* s2);
+int ycf_symbol_is_text_eq(ycf_symbol* symbol, char* str);
+char* ycf_symbol_list_to_str(ycf_symbol_list* l);
+#endif //YIELDING_C_FUN_YCF_SYMBOL_H
diff --git a/erts/lib_src/yielding_c_fun/ycf_utils.c b/erts/lib_src/yielding_c_fun/ycf_utils.c
new file mode 100644
index 0000000000..1c51043b41
--- /dev/null
+++ b/erts/lib_src/yielding_c_fun/ycf_utils.c
@@ -0,0 +1,106 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB and Kjell Winblad 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%
+ */
+
+/*
+ * Author: Kjell Winblad
+ */
+
+#include <string.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <stdio.h>
+
+#include "ycf_utils.h"
+#include "lib/simple_c_gc/simple_c_gc.h"
+#include <stdint.h>
+
+bool ycf_use_gc = false;
+bool ycf_track_memory = false;
+
+size_t ycf_memory_usage = 0;
+size_t ycf_max_memory_usage = 0;
+
+void ycf_enable_gc(){
+ ycf_use_gc = true;
+}
+
+void ycf_enable_memory_tracking(){
+ ycf_track_memory = true;
+}
+
+void ycf_malloc_log(char* log_file, char* log_entry_id) {
+ FILE* out = fopen(log_file, "a");
+ fprintf(out,
+ "(%s)\nMax memory consumption %zu bytes ~ %zu kilo bytes ~ %zu mega bytes (after=%zu)\n",
+ log_entry_id,
+ ycf_max_memory_usage,
+ ycf_max_memory_usage / 1000,
+ ycf_max_memory_usage / 1000000,
+ ycf_memory_usage);
+ fclose(out);
+}
+
+void* ycf_raw_malloc(size_t size) {
+ if (ycf_track_memory) {
+ void* block = malloc(size + sizeof(intptr_t));
+ intptr_t* size_ptr = block;
+ *size_ptr = size + sizeof(intptr_t);
+ ycf_memory_usage = ycf_memory_usage + size + sizeof(intptr_t);
+ if (ycf_memory_usage > ycf_max_memory_usage) {
+ ycf_max_memory_usage = ycf_memory_usage;
+ }
+ if(block == NULL) {
+ fprintf(stderr, "ycf_malloc failed: is there enough memory in the machine?\n");
+ exit(1);
+ }
+ return (void*)(((char*)block) + sizeof(intptr_t));
+ } else {
+ void* block = malloc(size);
+ if(block == NULL) {
+ fprintf(stderr, "ycf_malloc failed: is there enough memory in the machine?\n");
+ exit(1);
+ }
+ return block;
+ }
+}
+
+void* ycf_malloc(size_t size) {
+ if (ycf_use_gc) {
+ return scgc_new(size);
+ } else {
+ return ycf_raw_malloc(size);
+ }
+}
+
+
+void ycf_free(void* to_free) {
+ if (ycf_track_memory) {
+ char* to_free_cp = to_free;
+ char* start = to_free_cp - sizeof(intptr_t);
+ intptr_t* size_ptr = (intptr_t*)start;
+ ycf_memory_usage = ycf_memory_usage - *size_ptr;
+ free(start);
+ } else {
+ free(to_free);
+ }
+}
+
+
+
diff --git a/erts/lib_src/yielding_c_fun/ycf_utils.h b/erts/lib_src/yielding_c_fun/ycf_utils.h
new file mode 100644
index 0000000000..d713fafc09
--- /dev/null
+++ b/erts/lib_src/yielding_c_fun/ycf_utils.h
@@ -0,0 +1,40 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB and Kjell Winblad 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%
+ */
+
+/*
+ * Author: Kjell Winblad
+ */
+
+#ifndef UTILS_H
+#define UTILS_H
+
+#include <stddef.h>
+#include <stdbool.h>
+
+
+void ycf_enable_gc(void);
+void ycf_enable_memory_tracking(void);
+void* ycf_malloc(size_t size);
+void ycf_malloc_log(char* log_file, char* log_entry_id);
+void* ycf_raw_malloc(size_t size);
+void ycf_free(void* to_free);
+
+
+#endif
diff --git a/erts/lib_src/yielding_c_fun/ycf_yield_fun.c b/erts/lib_src/yielding_c_fun/ycf_yield_fun.c
new file mode 100644
index 0000000000..6260e5402e
--- /dev/null
+++ b/erts/lib_src/yielding_c_fun/ycf_yield_fun.c
@@ -0,0 +1,1625 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB and Kjell Winblad 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%
+ */
+
+/*
+ * Author: Kjell Winblad
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+
+#include "ycf_utils.h"
+#include "ycf_yield_fun.h"
+#include "ycf_node.h"
+#include "ycf_symbol.h"
+#include "ycf_string.h"
+#include "ycf_printers.h"
+#include "ycf_parser.h"
+
+static int ycf_yield_location_id_counter = 0;
+
+static
+ycf_node* mk_typedef_struct_node(ycf_node_list definitions, char* name){
+ ycf_node* res = ycf_malloc(sizeof(ycf_node));
+ res->next = NULL;
+ res->type = ycf_node_type_gen_typedef_struct;
+ res->u.gen_typedef_struct.definition_nodes = definitions;
+ res->u.gen_typedef_struct.name = name;
+ return res;
+}
+
+static
+ycf_node_list mk_ycf_trap_state_params(){
+ return ycf_node_definition_list_from_string(ycf_string_new(
+ "long* ycf_nr_of_reductions_param;\n"
+ "void** ycf_trap_state;\n"
+ "void* ycf_extra_context;\n"));
+}
+
+static
+ycf_node_list mk_saved_ycf_trap_state_params(){
+ return ycf_node_definition_list_from_string(
+ "ycf_yield_alloc_type ycf_yield_alloc;\n"
+ "ycf_yield_free_type ycf_yield_free;\n"
+ "void* ycf_yield_alloc_free_context;\n"
+ "size_t ycf_stack_alloc_size_or_max_size;\n"
+ "void* ycf_stack_alloc_data;\n");
+}
+
+static
+ycf_node_list mk_trap_extra_state(){
+ return ycf_node_definition_list_from_string(
+ "int ycf_trap_location;\n"
+ "long ycf_nr_of_reductions;\n"
+ "struct ycf_alloc_data ycf_frame_alloc_data;\n");;
+}
+
+static
+char* get_ycf_trap_state_assignments(ycf_node_list ycf_trap_state_defines){
+ ycf_node* current = ycf_trap_state_defines.head;
+ char* str = "";
+ while(current != NULL){
+ char* ident = ycf_symbol_get_text(current->u.definition.identifier);
+ if(current->u.definition.array_brackets.head != NULL && !current->u.definition.array_brackets.head->u.array_bracket.empty){
+ ycf_string_printable_buffer* array_size_exp = ycf_string_printable_buffer_new();
+ ycf_node* bracket = current->u.definition.array_brackets.head;
+ while(bracket != NULL){
+ print_node_code_expression(bracket->u.array_bracket.content,array_size_exp);
+ ycf_string_printable_buffer_printf(array_size_exp, " * ");
+ bracket = bracket->next;
+ }
+ ycf_string_printable_buffer_printf(array_size_exp, " sizeof(");
+ print_symbol_list(&current->u.definition.type_specifiers, array_size_exp);
+ ycf_string_printable_buffer_printf(array_size_exp, ")");
+ str = ycf_string_new("%s"
+ " memcpy(ycf_my_trap_state->%s, %s, %s);\n",
+ str,
+ ident,
+ ident,
+ array_size_exp->buffer);
+ }else{
+ str = ycf_string_new("%s"
+ " ycf_my_trap_state->%s = %s;\n",
+ str,
+ ident,
+ ident);
+ }
+ current = current->next;
+ }
+ return str;
+}
+
+static
+char* mk_code_from_special_code_list(ycf_node_list special_code_list){
+ ycf_string_printable_buffer* b = ycf_string_printable_buffer_new();
+ ycf_node* current = special_code_list.head;
+ ycf_string_printable_buffer_printf(b, "\n/* YCF SPECIAL CUSTOM CODE START */\n");
+ while(current != NULL) {
+ ycf_node_print(current->u.special_code_block.code.if_statement, b);
+ ycf_string_printable_buffer_printf(b, "\n");
+ current = current->next;
+ }
+ ycf_string_printable_buffer_printf(b, "\n/* YCF SPECIAL CUSTOM CODE END */\n");
+ return b->buffer;
+}
+
+static
+ycf_node* mk_yield_code(ycf_node* f_node,
+ char* ycf_trap_state_name,
+ ycf_node_list ycf_trap_state_defines,
+ ycf_node_list on_save_yield_state_code_list,
+ bool debug){
+ char* ret_code;
+ if(ycf_node_is_void_ret_fun(f_node)){
+ ret_code = "return;\n";
+ } else{
+ ycf_symbol_list ret_type = ycf_node_get_return_type(f_node);
+ ret_code =
+ ycf_string_new(" {\n"
+ " %s ycf_ret_value;\n"
+ " return ycf_ret_value;\n"
+ " }\n",
+ ycf_symbol_list_to_str(&ret_type));
+ }
+ char *debug_check_for_stack_ptrs = "";
+ if(debug){
+ debug_check_for_stack_ptrs =
+ ycf_string_new("ycf_debug_check_block(\"%s\",\n"
+ " ycf_find_stack_bottom_conservative(),\n"
+ " ycf_trap_state,\n"
+ " ycf_my_trap_state,\n"
+ " sizeof(struct %s));\n",
+ ycf_trap_state_name,
+ ycf_trap_state_name);
+ }
+ char* code = ycf_string_new("\n"
+ "{\n"
+ " struct %s* ycf_my_trap_state;\n"
+ " ycf_do_yield_label_%s:;\n"
+ " %s"
+ " if (*ycf_trap_state == NULL) {\n"
+ " ycf_my_trap_state = ycf_yield_alloc(sizeof(struct %s), ycf_yield_alloc_free_context);\n"
+ " } else {\n"
+ " ycf_my_trap_state = *ycf_trap_state;\n"
+ " }\n"
+ " %s\n"
+ " *ycf_nr_of_reductions_param = ycf_nr_of_reductions;\n"
+ " *ycf_trap_state = ycf_my_trap_state;\n"
+ " %s"
+ " %s\n"
+ "}\n"
+ "\n",
+ ycf_trap_state_name,
+ ycf_symbol_get_text(f_node->u.function.definition.definition.identifier),
+ mk_code_from_special_code_list(on_save_yield_state_code_list),
+ ycf_trap_state_name,
+ get_ycf_trap_state_assignments(ycf_trap_state_defines),
+ debug_check_for_stack_ptrs,
+ ret_code
+ );
+ return ycf_node_get_from_code_scope_text(code);
+}
+
+ycf_node* mk_goto_yield_code(ycf_node* f_node,
+ char* ycf_trap_state_name,
+ ycf_node_list ycf_trap_state_defines){
+ ycf_yield_location_id_counter++;
+ char* code = ycf_string_new("\n"
+ "{\n"
+ " ycf_nr_of_reductions = 0;\n"
+ " ycf_trap_location = %d;\n"
+ " goto ycf_do_yield_label_%s;\n"
+ " ycf_yield_location_label_%d:;\n"
+ "}\n",
+ ycf_yield_location_id_counter,
+ ycf_symbol_get_text(f_node->u.function.definition.definition.identifier),
+ ycf_yield_location_id_counter
+ );
+ return ycf_node_get_from_code_scope_text(code);
+}
+
+static
+ycf_node* mk_goto_yield_no_reds_code(ycf_node* f_node,
+ char* ycf_trap_state_name,
+ ycf_node_list ycf_trap_state_defines){
+ ycf_yield_location_id_counter++;
+ char* code = ycf_string_new("\n"
+ "{\n"
+ " ycf_trap_location = %d;\n"
+ " goto ycf_do_yield_label_%s;\n"
+ " ycf_yield_location_label_%d:;\n"
+ "}\n",
+ ycf_yield_location_id_counter,
+ ycf_symbol_get_text(f_node->u.function.definition.definition.identifier),
+ ycf_yield_location_id_counter
+ );
+ return ycf_node_get_from_code_scope_text(code);
+}
+
+
+static
+ycf_node* mk_yield_fun_call_state_var(ycf_node* f){
+ ycf_node_list code = ycf_node_list_empty();
+ char* fun_name = NULL;
+ if(f->u.statement.expression->type == ycf_node_type_function_call){
+ fun_name = ycf_symbol_get_text(f->u.statement.expression->u.function_call.identifier);
+ }else{
+ fun_name = ycf_symbol_get_text(f->u.statement.expression->u.function_call_assignment.fun_call.identifier);
+ }
+ ycf_node * scope_with_dec =
+ ycf_node_get_from_code_scope_text(ycf_string_new("void* ycf_sub_fun_trap_state_wb = NULL;\n"
+ "/*special_code_start:ON_DESTROY_STATE*/\n"
+ "if(0){\n"
+ " if(ycf_sub_fun_trap_state_wb != NULL)\n{"
+ " %s_ycf_gen_destroy(ycf_sub_fun_trap_state_wb);\n"
+ " }\n"
+ "}\n"
+ "/*special_code_end*/\n", fun_name));
+ ycf_node_list_append(&code, ycf_node_shallow_copy(f));
+ ycf_node_list_append(&code, scope_with_dec->u.code_scope.other_nodes.head);
+ ycf_node* ret = ycf_node_scope_new(ycf_symbol_new_open_curly_brace(),
+ scope_with_dec->u.code_scope.definition_nodes,
+ code,
+ ycf_symbol_new_end_curly_brace());
+ return ret;
+}
+
+static
+ycf_node* mk_yield_fun_call(ycf_node* c_file_node,
+ ycf_node* f,
+ char* ycf_trap_state_var_name,
+ char* calling_fun_name,
+ bool auto_yield){
+ ycf_yield_location_id_counter++;
+ ycf_node_function_call f_node;
+ char* assignment_code = "";
+ char* tmp_assignment_var_name = "ycf_tmp_fun_call_tmp_var";
+ char* tmp_assignment_var_declaration = "";
+ char* parameters = "";
+ char* finalize_call_code = "";
+ if(f->u.statement.expression->type == ycf_node_type_function_call){
+ f_node = f->u.statement.expression->u.function_call;
+ }else{
+ ycf_symbol_list called_fun_ret_type;
+ ycf_string_printable_buffer* assign_to_b = ycf_string_printable_buffer_new();
+ f_node = f->u.statement.expression->u.function_call_assignment.fun_call;
+ called_fun_ret_type =
+ ycf_node_find_function_return_type(c_file_node,
+ ycf_symbol_get_text(f_node.identifier));
+ tmp_assignment_var_declaration =
+ ycf_string_new("%s %s;\n",
+ ycf_symbol_list_to_str(&called_fun_ret_type),
+ tmp_assignment_var_name);
+ print_node_code_expression(f->u.statement.expression->u.function_call_assignment.left_side, assign_to_b);
+ assignment_code = ycf_string_new("%s = ", tmp_assignment_var_name);
+ finalize_call_code = ycf_string_new("%s = %s;\n",
+ assign_to_b->buffer,
+ tmp_assignment_var_name);
+ }
+ if (f_node.parameter_expressions.head != NULL){
+ ycf_string_printable_buffer* b = ycf_string_printable_buffer_new();
+ print_node_list_code(f_node.parameter_expressions.head, b);
+ parameters = ycf_string_new(",%s", b->buffer);
+ }
+
+ char* code = ycf_string_new("%s"
+ "%s"
+ "%s %s%s_ycf_gen_yielding(&ycf_nr_of_reductions,\n"
+ " &%s,ycf_extra_context,\n"
+ " ycf_yield_alloc,ycf_yield_free,\n"
+ " ycf_yield_alloc_free_context,\n"
+ " YCF_ALLOC_NEXT_MAX_SIZE(),\n"
+ " YCF_ALLOC_NEXT_BLOCK()\n"
+ " %s);\n"
+ "while(YCF_IS_YIELDED(%s)){\n"
+ " ycf_trap_location = %d;\n"
+ " goto ycf_do_yield_label_%s;\n"
+ " ycf_yield_location_label_%d:;\n"
+ " %s %s%s_ycf_gen_continue(&ycf_nr_of_reductions,\n"
+ " &%s,\n"
+ " ycf_extra_context);\n"
+ "}\n"
+ "%s\n",
+ tmp_assignment_var_declaration,
+ auto_yield ? "YCF_CONSUME_REDS(1);" : "",
+ assignment_code,
+ ycf_symbol_list_to_str(&f_node.neg_symbols),
+ ycf_symbol_get_text(f_node.identifier),
+ ycf_trap_state_var_name,
+ parameters,
+ ycf_trap_state_var_name,
+ ycf_yield_location_id_counter,
+ calling_fun_name,
+ ycf_yield_location_id_counter,
+ assignment_code,
+ ycf_symbol_list_to_str(&f_node.neg_symbols),
+ ycf_symbol_get_text(f_node.identifier),
+ ycf_trap_state_var_name,
+ finalize_call_code
+ );
+
+ return ycf_node_get_from_code_scope_text(code);
+}
+
+
+static
+char* get_trap_restore_assignments(ycf_node_list ycf_trap_state_defines){
+ ycf_node* current = ycf_trap_state_defines.head;
+ char* str = "\n";
+ while(current != NULL){
+ char* ident = ycf_symbol_get_text(current->u.definition.identifier);
+ if(current->u.definition.array_brackets.head != NULL && !current->u.definition.array_brackets.head->u.array_bracket.empty){
+ ycf_string_printable_buffer* array_size_exp = ycf_string_printable_buffer_new();
+ ycf_node* bracket = current->u.definition.array_brackets.head;
+ while(bracket != NULL){
+ print_node_code_expression(bracket->u.array_bracket.content,array_size_exp);
+ ycf_string_printable_buffer_printf(array_size_exp, " * ");
+ bracket = bracket->next;
+ }
+ ycf_string_printable_buffer_printf(array_size_exp, " sizeof(");
+ print_symbol_list(&current->u.definition.type_specifiers, array_size_exp);
+ ycf_string_printable_buffer_printf(array_size_exp, ")");
+ str = ycf_string_new("%s"
+ " memcpy(%s, ycf_my_trap_state->%s, %s);\n",
+ str,
+ ident,
+ ident,
+ array_size_exp->buffer);
+ }else{
+ str = ycf_string_new("%s"
+ " %s = ycf_my_trap_state->%s;\n",
+ str,
+ ident,
+ ident);
+ }
+ current = current->next;
+ }
+ return str;
+}
+
+static
+char* get_goto_ycf_trap_location_switch(int nr_of_ycf_trap_locations){
+ int i;
+ char* str = "switch(ycf_trap_location){\n";
+ for(i = 1; i <= nr_of_ycf_trap_locations; i++){
+ str = ycf_string_new("%s"
+ "case %d: goto ycf_yield_location_label_%d;\n", str, i, i);
+ }
+ str = ycf_string_new("%s\n}", str, i, i);
+ return str;
+}
+
+typedef struct {
+ ycf_node* f_node;
+ char* ycf_trap_state_name;
+ ycf_node_list ycf_trap_state_defines;
+} yield_code_replacer_context;
+
+ycf_node* yield_code_replacer(ycf_node* candidate, ycf_node_code_scope* s,void* context){
+ yield_code_replacer_context* c = context;
+ (void)s;
+ if(candidate->type == ycf_node_type_statement &&
+ candidate->u.statement.expression != NULL &&
+ candidate->u.statement.expression->type == ycf_node_type_function_call &&
+ ycf_symbol_is_text_eq(candidate->u.statement.expression->u.function_call.identifier,
+ "YCF_YIELD") &&
+ candidate->u.statement.expression->u.function_call.parameter_expressions.head == NULL){
+ ycf_node* yield_code = mk_goto_yield_code(c->f_node, c->ycf_trap_state_name, c->ycf_trap_state_defines);
+ return yield_code;
+ } else {
+ return candidate;
+ }
+}
+
+static
+void insert_yield_code(ycf_node* f_node, char* ycf_trap_state_name, ycf_node_list ycf_trap_state_defines, ycf_node_code_scope* s){
+ ycf_node_search_and_replace_statements_in_scope(s,
+ yield_code_replacer,
+ &(yield_code_replacer_context) {f_node, ycf_trap_state_name,
+ ycf_trap_state_defines});
+}
+
+static
+ycf_node* special_code_section_replacer(ycf_node* candidate, ycf_node_code_scope* s,void* context){
+ (void)context;
+ (void)s;
+ if(candidate->type == ycf_node_type_statement &&
+ candidate->u.statement.expression != NULL &&
+ candidate->u.statement.expression->type == ycf_node_type_function_call) {
+ ycf_node_function_call fun_call = candidate->u.statement.expression->u.function_call;
+ ycf_symbol* fun_name = fun_call.identifier;
+ if(ycf_symbol_is_text_eq(fun_name, "YCF_SPECIAL_CODE_START") &&
+ ycf_node_list_length(fun_call.parameter_expressions) == 1) {
+ char* parameter = ycf_node_to_string(fun_call.parameter_expressions.head);
+ ycf_node* special_code_start =
+ ycf_node_new_text_node(ycf_string_new("/*special_code_start:%s*/\n"
+ "if(0){\n",
+ parameter));
+ return special_code_start;
+ } else if (ycf_symbol_is_text_eq(candidate->u.statement.expression->u.function_call.identifier,
+ "YCF_SPECIAL_CODE_END") &&
+ ycf_node_list_length(fun_call.parameter_expressions) == 0) {
+ return ycf_node_new_text_node("}/*special_code_end*/\n");
+ }
+ }
+ return candidate;
+}
+
+static
+ycf_node* replace_alt_syntax_special_code_section_code(ycf_node* f_node){
+ ycf_node_search_and_replace_statements_in_scope(&f_node->u.function.body,
+ special_code_section_replacer,
+ NULL);
+ return ycf_node_get_function_from_text(ycf_node_to_string(f_node));
+}
+
+
+static
+ycf_node* yield_no_reds_code_replacer(ycf_node* candidate, ycf_node_code_scope* s,void* context){
+ yield_code_replacer_context* c = context;
+ (void)s;
+ if(candidate->type == ycf_node_type_statement &&
+ candidate->u.statement.expression != NULL &&
+ candidate->u.statement.expression->type == ycf_node_type_function_call &&
+ ycf_symbol_is_text_eq(candidate->u.statement.expression->u.function_call.identifier,
+ "YCF_YIELD_NO_REDS") &&
+ candidate->u.statement.expression->u.function_call.parameter_expressions.head == NULL){
+ ycf_node* yield_code = mk_goto_yield_no_reds_code(c->f_node, c->ycf_trap_state_name, c->ycf_trap_state_defines);
+ return yield_code;
+ } else {
+ return candidate;
+ }
+}
+
+static
+void insert_yield_no_reds_code(ycf_node* f_node, char* ycf_trap_state_name, ycf_node_list ycf_trap_state_defines, ycf_node_code_scope* s){
+ ycf_node_search_and_replace_statements_in_scope(s,
+ yield_no_reds_code_replacer,
+ &(yield_code_replacer_context){f_node, ycf_trap_state_name,
+ ycf_trap_state_defines});
+}
+
+typedef struct {
+ ycf_node_list on_return_code_list;
+ ycf_node_list on_return_or_destroy_code_list;
+ bool auto_yield;
+} replace_return_ctx;
+
+static
+ycf_node* save_nr_of_reductions_before_return_replacer(ycf_node* candidate,
+ ycf_node_code_scope* s,
+ void* context){
+ replace_return_ctx* ctx = context;
+ (void)s;
+ char* consume_reds_code = ctx->auto_yield ? "YCF_CONSUME_REDS(1);\n" : "";
+ if(candidate->type == ycf_node_type_return_statement){
+ char* code =
+ ycf_string_new("\n"
+ "%s"
+ "if (*ycf_trap_state != NULL) {\n"
+ " ycf_yield_free(*ycf_trap_state, ycf_yield_alloc_free_context);\n"
+ " *ycf_trap_state = NULL;\n"
+ "}\n"
+ "ycf_destroy_stack_allocator(&ycf_frame_alloc_data,\n"
+ " ycf_yield_free,\n"
+ " ycf_yield_alloc_free_context);\n"
+ "*ycf_nr_of_reductions_param = ycf_nr_of_reductions;"
+ "%s"
+ "%s"
+ "%s",
+ consume_reds_code,
+ mk_code_from_special_code_list(
+ ctx->on_return_or_destroy_code_list),
+ mk_code_from_special_code_list(ctx->on_return_code_list),
+ ycf_node_to_string(candidate));
+ return ycf_node_get_from_code_scope_text(code);
+ } else {
+ return candidate;
+ }
+}
+
+static
+void save_nr_of_reductions_before_return(ycf_node_code_scope* s,
+ ycf_node_list on_return_code_list,
+ ycf_node_list on_return_or_destroy_code_list,
+ bool auto_yield){
+ replace_return_ctx ctx;
+ ctx.on_return_code_list = on_return_code_list;
+ ctx.on_return_or_destroy_code_list = on_return_or_destroy_code_list;
+ ctx.auto_yield = auto_yield;
+ ycf_node_search_and_replace_statements_in_scope(s,
+ save_nr_of_reductions_before_return_replacer,
+ &ctx);
+}
+
+ycf_node* mk_consume_reds_code(char* function_name, ycf_node* consume_reds_node){
+ ycf_string_printable_buffer* b = ycf_string_printable_buffer_new();
+ ycf_yield_location_id_counter++;
+ print_node_code_paran_expression(consume_reds_node->u.consume_reds.nr_of_reds_expression, b);
+ return ycf_node_get_from_code_scope_text(ycf_string_new("ycf_nr_of_reductions = ycf_nr_of_reductions - %s;\n"
+ "if (ycf_nr_of_reductions <= 0) {\n"
+ " ycf_trap_location = %d;\n"
+ " goto ycf_do_yield_label_%s;\n"
+ " ycf_yield_location_label_%d:;\n"
+ "}\n",
+ b->buffer,
+ ycf_yield_location_id_counter,
+ function_name,
+ ycf_yield_location_id_counter));
+}
+
+static
+ycf_node* consume_reds_replacer(ycf_node* candidate, ycf_node_code_scope* s, void* context){
+ char* function_name = context;
+ (void)s;
+ if(candidate->type == ycf_node_type_consume_reds){
+ return mk_consume_reds_code(function_name, candidate);
+ } else {
+ return candidate;
+ }
+}
+
+static
+void insert_consume_reds_code(char* function_name, ycf_node_code_scope* s){
+ ycf_node_search_and_replace_statements_in_scope(s,
+ consume_reds_replacer,
+ function_name);
+}
+
+static
+ycf_node* mk_consume_reds_wrapped_statement(ycf_node* statement){
+ return ycf_node_get_from_code_scope_text(ycf_string_new("YCF_CONSUME_REDS(1);\n"
+ "%s\n", ycf_node_to_string(statement)));
+}
+
+static
+void insert_consume_reds_calls(ycf_node_code_scope* s);
+
+static
+ycf_node* consume_reds_calls_inserter(ycf_node* candidate, ycf_node_code_scope* s, void* context){
+ if(candidate->type == ycf_node_type_goto){
+ return mk_consume_reds_wrapped_statement(candidate);
+ } else if (candidate->type == ycf_node_type_while){
+ if(candidate->u.while_n.statement->type == ycf_node_type_code_scope){
+ insert_consume_reds_calls(&candidate->u.while_n.statement->u.code_scope);
+ }
+ candidate->u.while_n.statement = mk_consume_reds_wrapped_statement(candidate->u.while_n.statement);
+ return candidate;
+ } else if (candidate->type == ycf_node_type_do_while){
+ if(candidate->u.do_while.statement->type == ycf_node_type_code_scope){
+ insert_consume_reds_calls(&candidate->u.do_while.statement->u.code_scope);
+ }
+ candidate->u.do_while.statement = mk_consume_reds_wrapped_statement(candidate->u.do_while.statement);
+ return candidate;
+ } else if (candidate->type == ycf_node_type_for){
+ if(candidate->u.for_n.statement->type == ycf_node_type_code_scope){
+ insert_consume_reds_calls(&candidate->u.for_n.statement->u.code_scope);
+ }
+ candidate->u.for_n.statement = mk_consume_reds_wrapped_statement(candidate->u.for_n.statement);
+ return candidate;
+ } else {
+ return candidate;
+ }
+}
+
+static void insert_consume_reds_calls(ycf_node_code_scope* s){
+ ycf_node_search_and_replace_statements_in_scope(s,
+ consume_reds_calls_inserter,
+ NULL);
+}
+
+typedef struct {
+ ycf_node_list code;
+ ycf_node_type code_type;
+} special_code_context;
+
+ycf_node* do_special_code_replace(ycf_node* candidate, ycf_node_code_scope* s, void* context){
+ special_code_context* c = context;
+ (void)s;
+ if(candidate->type == c->code_type){
+ ycf_node_list_append(&c->code, ycf_node_shallow_copy(candidate));
+ return ycf_node_new_text_node("\n/* YCF Replaced special code */\n");
+ } else {
+ return candidate;
+ }
+}
+
+static
+ycf_node_list save_and_replace_special_code(ycf_node_code_scope* s, ycf_node_type code_type){
+ special_code_context context;
+ context.code = ycf_node_list_empty();
+ context.code_type = code_type;
+ ycf_node_search_and_replace_statements_in_scope(s,
+ do_special_code_replace,
+ &context);
+ return context.code;
+}
+
+static
+ycf_node* insert_fun_call_state_var_replacer(ycf_node* candidate, ycf_node_code_scope* s, void* context){
+ ycf_string_item_list* yielding_funs = context;
+ (void)s;
+ if(candidate->type == ycf_node_type_statement &&
+ (candidate->u.statement.expression->type == ycf_node_type_function_call ||
+ candidate->u.statement.expression->type == ycf_node_type_assignment_function_call)){
+ ycf_string_item* current = yielding_funs->head;
+ while(current != NULL){
+ char* fun_name = NULL;
+ if(candidate->u.statement.expression->type == ycf_node_type_function_call){
+ fun_name = ycf_symbol_get_text(candidate->u.statement.expression->u.function_call.identifier);
+ } else if(candidate->u.statement.expression->type == ycf_node_type_assignment_function_call){
+ fun_name = ycf_symbol_get_text(
+ candidate->u.statement.expression->u.function_call_assignment.fun_call.identifier);
+ } else {
+ current = current->next;
+ continue;
+ }
+ if(ycf_string_is_equal(current->str, fun_name)){
+ return mk_yield_fun_call_state_var(candidate);
+ }
+ current = current->next;
+ }
+ }
+ return candidate;
+}
+
+static
+void insert_fun_call_state_var(ycf_node_code_scope* s, ycf_string_item_list* yielding_funs){
+ ycf_node_search_and_replace_statements_in_scope(s,
+ insert_fun_call_state_var_replacer,
+ yielding_funs);
+}
+
+typedef struct {
+ ycf_node* c_file_node;
+ char* calling_fun_name;
+ ycf_string_item_list* yielding_funs;
+ ycf_string_item_list* starte_vars_wb;
+ bool auto_yield;
+} yielding_fun_call_code_context;
+
+static
+ycf_node* insert_yielding_fun_call_code_replacer(ycf_node* candidate,
+ ycf_node_code_scope* s,
+ void* context){
+ yielding_fun_call_code_context* c = context;
+ ycf_node* c_file_node = c->c_file_node;
+ ycf_string_item_list* yielding_funs = c->yielding_funs;
+ (void)s;
+ if (candidate->type == ycf_node_type_code_scope &&
+ candidate->u.code_scope.other_nodes.head != candidate->u.code_scope.other_nodes.last){
+ ycf_node* call_candidate = candidate->u.code_scope.other_nodes.head->next;
+ if (call_candidate->type == ycf_node_type_statement &&
+ (call_candidate->u.statement.expression->type == ycf_node_type_function_call ||
+ call_candidate->u.statement.expression->type == ycf_node_type_assignment_function_call)) {
+ ycf_string_item* current = yielding_funs->head;
+ while(current != NULL){
+ char* fun_name = NULL;
+ if (call_candidate->u.statement.expression->type == ycf_node_type_function_call) {
+ fun_name = ycf_symbol_get_text(call_candidate->u.statement.expression->u.function_call.identifier);
+ } else if (call_candidate->u.statement.expression->type == ycf_node_type_assignment_function_call) {
+ fun_name = ycf_symbol_get_text(
+ call_candidate->u.statement.expression->u.function_call_assignment.fun_call.identifier);
+ } else {
+ current = current->next;
+ continue;
+ }
+ if(ycf_string_is_equal(current->str, fun_name)){
+ char* ycf_trap_state_var_name =
+ ycf_symbol_get_text(ycf_node_get_assignment(candidate->u.code_scope.other_nodes.head)->left_side.content.head->u.other.what);
+ ycf_string_item_list_append(c->starte_vars_wb, ycf_string_item_new(ycf_trap_state_var_name));
+ ycf_node* new_call_code = mk_yield_fun_call(c_file_node,
+ call_candidate,
+ ycf_trap_state_var_name,
+ c->calling_fun_name,
+ c->auto_yield);
+ ycf_node_list_replace(&candidate->u.code_scope.other_nodes, call_candidate, new_call_code);
+ return ycf_node_scope_new(ycf_symbol_new_open_curly_brace(),
+ candidate->u.code_scope.definition_nodes,
+ candidate->u.code_scope.other_nodes,
+ ycf_symbol_new_end_curly_brace());;
+ }
+ current = current->next;
+ }
+ }
+ }
+ return candidate;
+}
+
+ycf_string_item_list insert_yielding_fun_call_code(ycf_node* c_file_node,
+ ycf_node_code_scope* s,
+ ycf_string_item_list* yielding_funs,
+ char* calling_fun_name,
+ bool auto_yield){
+ yielding_fun_call_code_context context;
+ ycf_string_item_list state_vars = ycf_string_item_list_empty();
+ context.c_file_node = c_file_node;
+ context.calling_fun_name = calling_fun_name;
+ context.yielding_funs = yielding_funs;
+ context.starte_vars_wb = &state_vars;
+ context.auto_yield = auto_yield;
+ ycf_node_search_and_replace_statements_in_scope(s,
+ insert_yielding_fun_call_code_replacer,
+ &context);
+ return state_vars;
+}
+
+static
+ycf_node* mk_yield_init_code(char* ycf_trap_state_name, ycf_node_list ycf_trap_state_defines, ycf_node_list on_restore_yield_state_code_list, bool auto_yield, char *function_name){
+ char* code = ycf_string_new("\n"
+ "{\n"
+ " ycf_nr_of_reductions = *ycf_nr_of_reductions_param;\n"
+ " ycf_frame_alloc_data.size = 0;\n"
+ " ycf_frame_alloc_data.max_size = ycf_stack_alloc_size_or_max_size;\n"
+ " ycf_frame_alloc_data.data = ycf_stack_alloc_data;\n"
+ " ycf_frame_alloc_data.needs_freeing = 0;\n"
+ " if(*ycf_trap_state != NULL){\n"
+ " struct %s* ycf_my_trap_state = *ycf_trap_state;\n"
+ " %s\n"
+ " %s\n"
+ " ycf_nr_of_reductions = *ycf_nr_of_reductions_param;\n"
+ " %s\n"
+ " }\n"
+ "}\n",
+ ycf_trap_state_name,
+ get_trap_restore_assignments(ycf_trap_state_defines),
+ mk_code_from_special_code_list(on_restore_yield_state_code_list),
+ get_goto_ycf_trap_location_switch(ycf_yield_location_id_counter));
+ return ycf_node_get_from_code_scope_text(code);
+}
+
+static
+ycf_node* mk_destroy_state_function_node(char* yielding_function_name,
+ ycf_node_list defs,
+ char* ycf_trap_state_struct_name,
+ ycf_node_list destroy_code,
+ ycf_node_list on_destroy_state_or_return_code_list,
+ bool static_aux_funs){
+ ycf_node_list my_defs = ycf_node_list_shallow_copy(defs);
+ ycf_node* current = my_defs.head;
+ while(current != NULL){
+ current->u.definition.end = ycf_symbol_new_semicolon();
+ int empty_array_brackets = 0;
+ {
+ current->u.definition.array_brackets = ycf_node_list_shallow_copy(current->u.definition.array_brackets);
+ ycf_node* b = current->u.definition.array_brackets.head;
+ while(b != NULL && b->u.array_bracket.empty){
+ empty_array_brackets++;
+ ycf_node_list_remove(&current->u.definition.array_brackets, b);
+ b = b->next;
+ }
+ }
+ current->u.definition.type_specifiers = ycf_symbol_list_shallow_copy(current->u.definition.type_specifiers);
+ if(empty_array_brackets > 0){
+ for(int i = 0; i < empty_array_brackets; i++){
+ ycf_symbol_list_append(&current->u.definition.type_specifiers, ycf_symbol_new_star());
+ }
+ }
+ current = current->next;
+ }
+ char* code =
+ ycf_string_new("\n"
+ "%s void %s_ycf_gen_destroy(struct %s* ycf_my_trap_state){\n"
+ " {\n"
+ " %s\n"
+ " %s\n"
+ " %s\n"
+ " %s\n"
+ " ycf_destroy_stack_allocator(&ycf_frame_alloc_data, ycf_yield_free, ycf_yield_alloc_free_context);\n"
+ " ycf_yield_free(ycf_my_trap_state, ycf_yield_alloc_free_context);\n"
+ " }\n"
+ "}\n",
+ static_aux_funs ? "static" : "",
+ yielding_function_name,
+ ycf_trap_state_struct_name,
+ ycf_node_list_to_string(&my_defs),
+ get_trap_restore_assignments(my_defs),
+ mk_code_from_special_code_list(on_destroy_state_or_return_code_list),
+ mk_code_from_special_code_list(destroy_code));
+ return ycf_node_get_function_from_text(code);
+}
+
+void ast_add_yield_code_generated_define(ycf_node* source_out_tree/*Will be changed*/, bool debug_mode)
+{
+ char *debug_mode_code =
+ (debug_mode ?
+ "\n"
+ "#include <setjmp.h>\n"
+ "#include <stdint.h>\n"
+ "#include <string.h>\n"
+ "static void* ycf_find_stack_bottom_conservative_helper() {\n"
+ " void* p = NULL;\n"
+ " volatile intptr_t tmp = (intptr_t)&p;\n"
+ " return (void*)tmp;\n"
+ "}\n"
+ "static void* ycf_find_stack_bottom_conservative() {\n"
+ " jmp_buf env;\n"
+ " setjmp(env);\n"
+ "\n"
+ " {\n"
+ " volatile int noinline = 1;\n"
+ " void* (*bottom)(void) = noinline\n"
+ " ? ycf_find_stack_bottom_conservative_helper\n"
+ " : (void*(*)(void))(NULL);\n"
+ "\n"
+ " return bottom();\n"
+ " }\n"
+ "}\n"
+ "static void ycf_debug_check_block(char* struct_name, void* stack_start, void* stack_end, void* block, size_t block_size) {\n"
+ " char* p;\n"
+ " for (p = block; p < (((char*)block) + block_size); p += sizeof(void*)) {\n"
+ " if(*((char**)p) > (char*)stack_start && *((char**)p) <= (char*)stack_end){\n"
+ " fprintf(stderr, \"Pointer to stack in yielded functions state!!!!! (pointer_address=%p, struct %s,offset=%lu)\\n\", (void*)p, struct_name, (unsigned long)(p-(size_t)block));\n"
+ " exit(1);\n"
+ " }\n"
+ " }\n"
+ "}\n"
+ "\n"
+ :
+ "");
+ char* ycf_yielding_c_fun_helpers_code =
+ ycf_string_new("#ifndef YCF_YIELDING_C_FUN_HELPERS\n"
+ "#define YCF_YIELDING_C_FUN_HELPERS 1\n"
+ "#include <string.h>\n"
+ "#include <stdio.h>\n"
+ "#include <stdlib.h>\n"
+ "\n"
+ "/*\n"
+ " * YCF_GCC_DIAG_ON and YCF_GCC_DIAG_OFF can be used to temporarly\n"
+ " * disable a gcc or clang warning in a file.\n"
+ " *\n"
+ " * Example:\n"
+ " * YCF_GCC_DIAG_OFF(unused-function)\n"
+ " * static int test(){ return 0;}\n"
+ " * YCF_GCC_DIAG_ON(unused-function)\n"
+ " *\n"
+ " * These macros were orginally authored by Jonathan Wakely and has\n"
+ " * been modified by Patrick Horgan.\n"
+ " *\n"
+ " * Source: http://dbp-consulting.com/tutorials/SuppressingGCCWarnings.html\n"
+ " *\n"
+ " */\n"
+ "#if defined(_MSC_VER)\n"
+ "# define YCF_GCC_DIAG_OFF(x) __pragma(warning(push, 0))\n"
+ "# define YCF_GCC_DIAG_ON(x) __pragma(warning(pop))\n"
+ "#elif ((__GNUC__ * 100) + __GNUC_MINOR__) >= 402\n"
+ "#define YCF_GCC_DIAG_STR(s) #s\n"
+ "#define YCF_GCC_DIAG_JOINSTR(x,y) YCF_GCC_DIAG_STR(x ## y)\n"
+ "# define YCF_GCC_DIAG_DO_PRAGMA(x) _Pragma (#x)\n"
+ "# define YCF_GCC_DIAG_PRAGMA(x) YCF_GCC_DIAG_DO_PRAGMA(GCC diagnostic x)\n"
+ "# if ((__GNUC__ * 100) + __GNUC_MINOR__) >= 406\n"
+ "# define YCF_GCC_DIAG_OFF(x) YCF_GCC_DIAG_PRAGMA(push) \\\n"
+ " YCF_GCC_DIAG_PRAGMA(ignored YCF_GCC_DIAG_JOINSTR(-W,x))\n"
+ "# define YCF_GCC_DIAG_ON(x) YCF_GCC_DIAG_PRAGMA(pop)\n"
+ "# else\n"
+ "# define YCF_GCC_DIAG_OFF(x) YCF_GCC_DIAG_PRAGMA(ignored YCF_GCC_DIAG_JOINSTR(-W,x))\n"
+ "# define YCF_GCC_DIAG_ON(x) YCF_GCC_DIAG_PRAGMA(warning YCF_GCC_DIAG_JOINSTR(-W,x))\n"
+ "# endif\n"
+ "#else\n"
+ "# define YCF_GCC_DIAG_OFF(x)\n"
+ "# define YCF_GCC_DIAG_ON(x)\n"
+ "#endif\n"
+ "#ifdef __GNUC__\n"
+ "# if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ > 5) || defined(__clang__)\n"
+ "# define YCF_GCC_ATTRIBUTE_UNUSED __attribute__ ((unused))\n"
+ "# else\n"
+ "# define YCF_GCC_ATTRIBUTE_UNUSED\n"
+ "# endif\n"
+ "#else\n"
+ "# define YCF_GCC_ATTRIBUTE_UNUSED\n"
+ "#endif\n"
+ "\n"
+ "typedef void* (*ycf_yield_alloc_type) (size_t,void*);\n"
+ "typedef void (*ycf_yield_free_type) (void*,void*);\n"
+ "\n"
+ "struct ycf_alloc_data {\n"
+ " size_t size;\n"
+ " size_t max_size;\n"
+ " int needs_freeing;\n"
+ " char* data;\n"
+ "};\n"
+ "\n"
+ "#define YCF_ALLOC_NEXT_BLOCK() (\\\n"
+ " ycf_frame_alloc_data.data == NULL \\\n"
+ " ? NULL \\\n"
+ " : ((void*)(&ycf_frame_alloc_data.data[ycf_frame_alloc_data.size]))\\\n"
+ ")\n"
+ "#define YCF_ALLOC_NEXT_MAX_SIZE() (\\\n"
+ " ycf_frame_alloc_data.data == NULL \\\n"
+ " ? 0 \\\n"
+ " : (ycf_frame_alloc_data.max_size - ycf_frame_alloc_data.size)\\\n"
+ ")\n"
+ "\n"
+ "/* Macros for special code sections */\n"
+ "#define ON_SAVE_YIELD_STATE ON_SAVE_YIELD_STATE\n"
+ "#define ON_RESTORE_YIELD_STATE ON_RESTORE_YIELD_STATE\n"
+ "#define ON_DESTROY_STATE ON_DESTROY_STATE\n"
+ "#define ON_RETURN ON_RETURN\n"
+ "#define ON_DESTROY_STATE_OR_RETURN ON_DESTROY_STATE_OR_RETURN\n"
+ "#define YCF_SPECIAL_CODE_START(PARAM) \\\n"
+ " /*special_code_start:PARAM*/ \\\n"
+ " if(0){\n"
+ "#define YCF_SPECIAL_CODE_END() \\\n"
+ " }\\\n"
+ " /*special_code_end*/\n"
+ "\n"
+ "YCF_GCC_ATTRIBUTE_UNUSED\n"
+ "static void* ycf_stack_alloc(size_t size,\n"
+ " struct ycf_alloc_data* data,\n"
+ " ycf_yield_alloc_type allocator,\n"
+ " void* ycf_yield_alloc_free_context,\n"
+ " size_t default_stack_size){\n"
+ " void * ret = NULL;"
+ " if (data->data == NULL) {\n"
+ " if (default_stack_size == 0) {\n"
+ " fprintf(stderr, \"ycf_alloc: not enough stack!!\\n\");\n"
+ " exit(1);\n"
+ " }\n"
+ " data->data = allocator(default_stack_size, ycf_yield_alloc_free_context);\n"
+ " data->needs_freeing = 1;"
+ " data->max_size = default_stack_size;"
+ " data->size = 0;"
+ " }\n"
+ " if (data->size + size > data->max_size) {\n"
+ " fprintf(stderr, \"ycf_alloc: not enough stack!\\n\");\n"
+ " exit(1);\n"
+ " }\n"
+ " ret = &data->data[data->size];\n"
+ " data->size = data->size + size;\n"
+ " return ret;\n"
+ "}\n"
+ "static void ycf_destroy_stack_allocator(struct ycf_alloc_data* data,\n"
+ " ycf_yield_free_type freer,\n"
+ " void* ycf_yield_alloc_free_context){\n"
+ " if(data->needs_freeing){\n"
+ " freer(data->data, ycf_yield_alloc_free_context);\n"
+ " }\n"
+ "}\n"
+ "\n"
+ "#include <limits.h>\n"
+ "#define YCF_MAX_NR_OF_REDS LONG_MAX\n"
+ "#define YCF_NR_OF_REDS_LEFT() ycf_nr_of_reductions\n"
+ "#define YCF_SET_NR_OF_REDS_LEFT(NEW_NR_OF_REDS_LEFT) \\\n"
+ " do{ycf_nr_of_reductions = (NEW_NR_OF_REDS_LEFT);}while(0)\n"
+ "\n"
+ "#define YCF_GET_EXTRA_CONTEXT() ycf_extra_context\n"
+ "\n"
+ "#define YCF_IS_YIELDED(CTX) (CTX != NULL)\n"
+ "\n"
+ "#define YCF_YIELD_CODE_GENERATED 1\n"
+ "\n"
+ "%s"
+ "\n"
+ "/* end of YCF_YIELDING_C_FUN_HELPERS guard */\n"
+ "#endif\n", debug_mode_code);
+ ycf_node_list_prepend(&source_out_tree->u.c_file.content,
+ ycf_node_new_text_node(ycf_yielding_c_fun_helpers_code));
+}
+
+static
+ycf_node* mk_fun_def(ycf_node* fun_node){
+ ycf_node* fun_def = ycf_malloc(sizeof(ycf_node));
+ fun_def->next = NULL;
+ fun_def->type = ycf_node_type_function_declaration;
+ fun_def->u.function_definition = fun_node->u.function.definition;
+ ycf_symbol_list fun_node_def_end = ycf_symbol_list_empty();
+ ycf_symbol_list_append(&fun_node_def_end, ycf_symbol_new_semicolon());
+ fun_def->u.function_definition.end = fun_node_def_end;
+ return fun_def;
+}
+
+
+static
+ycf_node* mk_null_vars_code(ycf_string_item_list fun_call_state_vars){
+ ycf_string_item* current = fun_call_state_vars.head;
+ char* assignments = "";
+ while(current != NULL){
+ assignments = ycf_string_new("%s\n"
+ "%s = NULL;\n",
+ assignments,
+ current->str);
+ current = current->next;
+ }
+ ycf_node* code = ycf_node_get_from_code_scope_text(assignments);
+ return code;
+}
+
+static
+ycf_node* mk_continue_function_node(char* yielding_function_name,
+ char* ycf_trap_state_struct_name,
+ ycf_node* yielding_fun,
+ ycf_node_list uniqified_parameters){
+ char* parameters = "";
+ ycf_node* current = uniqified_parameters.head;
+ while(current != NULL){
+ parameters = ycf_string_new("%s,ycf_my_trap_state->%s", parameters, ycf_symbol_get_text(current->u.definition.identifier));
+ current = current->next;
+ }
+ char* fun_call_str =
+ ycf_string_new("%s_ycf_gen_yielding(ycf_number_of_reduction_param,\n"
+ " ycf_trap_state,\n"
+ " ycf_extra_context,\n"
+ " ycf_my_trap_state->ycf_yield_alloc,\n"
+ " ycf_my_trap_state->ycf_yield_free,\n"
+ " ycf_my_trap_state->ycf_yield_alloc_free_context,\n"
+ " ycf_my_trap_state->ycf_stack_alloc_size_or_max_size,\n"
+ " ycf_my_trap_state->ycf_stack_alloc_data\n"
+ " %s)\n",
+ yielding_function_name,
+ parameters);
+ char* code = ycf_string_new("\n"
+ "%s %s_ycf_gen_continue(long* ycf_number_of_reduction_param,\n"
+ " void** ycf_trap_state,\n"
+ " void* ycf_extra_context){\n"
+ " struct %s* ycf_my_trap_state = *ycf_trap_state;\n"
+ "%s"
+ "}\n",
+ ycf_symbol_list_to_str(&yielding_fun->u.function.definition.definition.type_specifiers),
+ yielding_function_name,
+ ycf_trap_state_struct_name,
+ (ycf_node_is_void_ret_fun(yielding_fun) ?
+ ycf_string_new("%s;\n"
+ "return;\n",
+ fun_call_str):
+ ycf_string_new("return %s;\n",
+ fun_call_str)));
+ ycf_symbol_list symbols = ycf_symbol_list_from_text(code);
+ ycf_node* tree = get_abstract_syntax_tree_root(&symbols);
+ if(tree->u.c_file.content.head->type != ycf_node_type_function_definition){
+ printf("NOT A FUNCTION\n");
+ exit(1);
+ }
+ return tree->u.c_file.content.head;
+}
+
+static
+ycf_node* mk_debug_yielding_fun_call_wrapper(char* yielding_function_name,
+ char* ycf_trap_state_struct_name,
+ ycf_node* yielding_fun){
+ char* parameters_2 = "";
+ {
+ ycf_node* current = yielding_fun->u.function.definition.parameters.head;
+ bool first = true;
+ char* identifier;
+ while(current != NULL){
+ identifier = ycf_symbol_get_text(current->u.definition.identifier);
+ if(strcmp(identifier, "ycf_trap_state") == 0){
+ identifier = ycf_string_new("(void**)(&ycf_my_trap_state)");
+ }
+ parameters_2 = ycf_string_new(first ? "%s%s" :"%s,%s",
+ parameters_2,
+ identifier);
+ current = current->next;
+ first = false;
+ }
+ }
+ char* parameters = "";
+ {
+ ycf_node* current = yielding_fun->u.function.definition.parameters.head;
+ bool first = true;
+ while(current != NULL){
+ parameters = ycf_string_new(first ? "%s%s" :"%s,%s",
+ parameters,
+ ycf_symbol_get_text(current->u.definition.identifier));
+ current = current->next;
+ first = false;
+ }
+ }
+ ycf_string_printable_buffer* header_2 = ycf_string_printable_buffer_new();
+ ycf_node_function_definition def_2 = yielding_fun->u.function.definition;
+ def_2.definition.identifier =
+ ycf_symbol_copy_change_text(def_2.definition.identifier,
+ ycf_string_new("%s_2",
+ ycf_symbol_get_text(def_2.definition.identifier)));
+ print_node_code_function_def(def_2, header_2);
+ ycf_string_printable_buffer* header = ycf_string_printable_buffer_new();
+ print_node_code_function_def(yielding_fun->u.function.definition, header);
+ ycf_string_printable_buffer* parameter_types = ycf_string_printable_buffer_new();
+ print_node_code_function_def_parameters(yielding_fun->u.function.definition, parameter_types);
+ ycf_symbol_list ret_type = ycf_node_get_return_type(yielding_fun);
+ char* wrapper_assignment = NULL;
+ char* wrapper_return = NULL;
+ if(ycf_node_is_void_ret_fun(yielding_fun)){
+ wrapper_assignment = ycf_string_new("");
+ wrapper_return = ycf_string_new("return;");
+ }else{
+ wrapper_assignment = ycf_string_new("%s ycf_to_return = ", ycf_symbol_list_to_str(&ret_type));
+ wrapper_return = ycf_string_new("return ycf_to_return;");
+ }
+ char* code = ycf_string_new("\n"
+ "%s{\n"
+ " volatile int noinline = 1;\n"
+ " volatile void* ycf_my_trap_state = *ycf_trap_state;\n"
+ " %s (*next)(%s = noinline\n"
+ " ? %s_ycf_gen_yielding_3\n"
+ " : (%s(*)(%s)(NULL);\n"
+ " {\n"
+ " %s next(%s);\n"
+ " *ycf_trap_state = (struct %s*)ycf_my_trap_state;\n"
+ " %s\n"
+ " }\n"
+ "}\n"
+ "\n"
+ "\n"
+ "%s{\n"
+ " volatile int noinline = 1;\n"
+ " %s (*next)(%s = noinline\n"
+ " ? %s_ycf_gen_yielding_2\n"
+ " : (%s(*)(%s)(NULL);\n"
+ "\n"
+ " {\n"
+ " %s next(%s);\n"
+ " %s;\n"
+ " }\n"
+ "}\n",
+ header_2->buffer,
+ ycf_symbol_list_to_str(&ret_type),
+ parameter_types->buffer,
+ yielding_function_name,
+ ycf_symbol_list_to_str(&ret_type),
+ parameter_types->buffer,
+ wrapper_assignment,
+ parameters_2,
+ ycf_trap_state_struct_name,
+ wrapper_return,
+ /* Second function*/
+ header->buffer,
+ ycf_symbol_list_to_str(&ret_type),
+ parameter_types->buffer,
+ yielding_function_name,
+ ycf_symbol_list_to_str(&ret_type),
+ parameter_types->buffer,
+ wrapper_assignment,
+ parameters,
+ wrapper_return);
+
+ return ycf_node_new_text_node(code);
+}
+
+
+static
+void break_up_control_expressions(ycf_node_code_scope* s,
+ ycf_string_item_list* yielding_function_names);
+
+
+static ycf_node* break_up_control_expressions_helper(ycf_node* candidate,
+ ycf_node_code_scope* candidates_scope,
+ void* context){
+ ycf_string_item_list* list = (ycf_string_item_list*)context;
+ if(candidate->type == ycf_node_type_if &&
+ ycf_node_list_length(candidate->u.if_n.expression.content.content) == 1 &&
+ candidate->u.if_n.expression.content.content.head->type == ycf_node_type_function_call &&
+ ycf_string_item_list_contains(list,
+ ycf_symbol_get_text(candidate->u.if_n.expression.content.content.head->u.function_call.identifier))){
+ break_up_control_expressions(&candidate->u.if_n.if_statement->u.code_scope, list);
+ ycf_node * wrapper =
+ ycf_node_get_from_code_scope_text(ycf_string_new("int ycf_gen_control_tmp;\n"
+ "ycf_gen_control_tmp = !!%s;\n"
+ "if(ycf_gen_control_tmp)\n"
+ "%s",
+ ycf_node_to_string(candidate->u.if_n.expression.content.content.head),
+ ycf_node_to_string(candidate->u.if_n.if_statement)));
+ return wrapper;
+ } else if(candidate->type == ycf_node_type_if_else &&
+ ycf_node_list_length(candidate->u.if_else.if_part.expression.content.content) == 1 &&
+ candidate->u.if_else.if_part.expression.content.content.head->type == ycf_node_type_function_call &&
+ ycf_string_item_list_contains(list,
+ ycf_symbol_get_text(candidate->u.if_else.if_part.expression.content.content.head->u.function_call.identifier))){
+ break_up_control_expressions(&candidate->u.if_else.if_part.if_statement->u.code_scope, list);
+ ycf_node * wrapper =
+ ycf_node_get_from_code_scope_text(ycf_string_new("int ycf_gen_control_tmp;\n"
+ "ycf_gen_control_tmp = !!%s;\n"
+ "if(ycf_gen_control_tmp)\n"
+ "%s\n"
+ "%s\n"
+ "%s\n",
+ ycf_node_to_string(candidate->u.if_else.if_part.expression.content.content.head),
+ ycf_node_to_string(candidate->u.if_else.if_part.if_statement),
+ ycf_symbol_get_text(candidate->u.if_else.else_word),
+ ycf_node_to_string(candidate->u.if_else.else_statement)));
+ return wrapper;
+ } else if(candidate->type == ycf_node_type_do_while &&
+ ycf_node_list_length(candidate->u.do_while.expression.content.content) == 1 &&
+ candidate->u.do_while.expression.content.content.head->type == ycf_node_type_function_call &&
+ ycf_string_item_list_contains(list,
+ ycf_symbol_get_text(candidate->u.do_while.expression.content.content.head->u.function_call.identifier))){
+ break_up_control_expressions(&candidate->u.do_while.statement->u.code_scope, list);
+ ycf_node * wrapper =
+ ycf_node_get_from_code_scope_text(ycf_string_new("int ycf_gen_control_tmp;\n"
+ "do {\n"
+ " %s\n"
+ " ycf_gen_control_tmp = !!%s;\n"
+ "}while(ycf_gen_control_tmp);\n",
+ ycf_node_to_string(candidate->u.do_while.statement),
+ ycf_node_to_string(candidate->u.do_while.expression.content.content.head)));
+ return wrapper;
+ } else if(candidate->type == ycf_node_type_while &&
+ ycf_node_list_length(candidate->u.while_n.expression.content.content) == 1 &&
+ candidate->u.while_n.expression.content.content.head->type == ycf_node_type_function_call &&
+ ycf_string_item_list_contains(list,
+ ycf_symbol_get_text(candidate->u.while_n.expression.content.content.head->u.function_call.identifier))){
+ break_up_control_expressions(&candidate->u.while_n.statement->u.code_scope, list);
+ ycf_node * wrapper =
+ ycf_node_get_from_code_scope_text(ycf_string_new("int ycf_gen_control_tmp;\n"
+ "ycf_gen_control_tmp = !!%s;\n"
+ "while(ycf_gen_control_tmp){\n"
+ " %s\n"
+ " ycf_gen_control_tmp = !!%s;\n"
+ "}\n",
+ ycf_node_to_string(candidate->u.while_n.expression.content.content.head),
+ ycf_node_to_string(candidate->u.while_n.statement),
+ ycf_node_to_string(candidate->u.while_n.expression.content.content.head)));
+ return wrapper;
+ }
+ return candidate;
+}
+
+static
+void break_up_control_expressions(ycf_node_code_scope* s,
+ ycf_string_item_list* yielding_function_names){
+ ycf_node_insert_scopes_in_complex_statements(s);
+ ycf_node_search_and_replace_statements_in_scope(s,
+ break_up_control_expressions_helper,
+ yielding_function_names);
+}
+
+static
+ycf_node* mk_wrap_in_surpress_warn(char* warning, ycf_node* to_wrap) {
+ ycf_string_printable_buffer* buf = ycf_string_printable_buffer_new();
+ ycf_string_printable_buffer_printf(buf,
+ "\n"
+ "/* clang-format off */\n"
+ "YCF_GCC_DIAG_OFF(%s)\n"
+ "/* clang-format on */\n", warning);
+ ycf_node_print(to_wrap, buf);
+ ycf_string_printable_buffer_printf(buf,
+ "\n"
+ "/* clang-format off */\n"
+ "YCF_GCC_DIAG_ON(%s)\n"
+ "/* clang-format on */\n", warning);
+ return ycf_node_new_text_node(buf->buffer);
+
+}
+
+static
+ycf_node* supress_warnings_wrap_yielding_fun(ycf_node* yielding_fun){
+ ycf_node* ret = yielding_fun;
+ ret = mk_wrap_in_surpress_warn("uninitialized", ret);
+ ret = mk_wrap_in_surpress_warn("maybe-uninitialized", ret);
+ ret = mk_wrap_in_surpress_warn("sometimes-uninitialized", ret);
+ ret = mk_wrap_in_surpress_warn("unknown-warning-option", ret);
+ ret = mk_wrap_in_surpress_warn("pragmas", ret);
+ return ret;
+}
+
+static
+ycf_node* supress_warnings_wrap_destroy_fun(ycf_node* fun){
+ ycf_node* ret = fun;
+ ret = mk_wrap_in_surpress_warn("unused-function", ret);
+ ret = mk_wrap_in_surpress_warn("unused-but-set-variable", ret);
+ ret = mk_wrap_in_surpress_warn("unknown-warning-option", ret);
+ ret = mk_wrap_in_surpress_warn("pragmas", ret);
+ return ret;
+}
+
+ycf_node* insert_yielding_function_with_prefix_suffix(ycf_node* tree_to_insert_to,
+ ycf_node* insert_before,
+ ycf_node* yielding_fun,
+ bool debug_mode,
+ char* yielding_function_name,
+ char* ycf_trap_state_struct_name){
+ ycf_node* prefix = ycf_node_new_text_node("\n"
+ "#define YCF_IN_YIELDING_FUN 1\n"
+ "#undef YCF_STACK_ALLOC\n"
+ "#define YCF_STACK_ALLOC(SIZE) \\\n"
+ " ycf_stack_alloc(SIZE,\\\n"
+ " &ycf_frame_alloc_data,\\\n"
+ " ycf_yield_alloc, ycf_yield_alloc_free_context,\\\n"
+ " ycf_stack_alloc_size_or_max_size)\n");
+ if(insert_before == NULL){
+ ycf_node_list_append(&tree_to_insert_to->u.c_file.content, prefix);
+ }else{
+ ycf_node_list_insert_before(&tree_to_insert_to->u.c_file.content,
+ insert_before,
+ prefix);
+ }
+ if(debug_mode){
+ ycf_node* wrapper_funs =
+ mk_debug_yielding_fun_call_wrapper(yielding_function_name,
+ ycf_trap_state_struct_name,
+ yielding_fun);
+ ycf_node_rename_function(&yielding_fun->u.function,
+ ycf_string_new("%s_ycf_gen_yielding_3",
+ yielding_function_name));
+
+ if(insert_before == NULL){
+ yielding_fun = supress_warnings_wrap_yielding_fun(yielding_fun);
+ ycf_node_list_append(&tree_to_insert_to->u.c_file.content,
+ yielding_fun);
+ ycf_node_list_append(&tree_to_insert_to->u.c_file.content, wrapper_funs);
+ } else {
+ yielding_fun = supress_warnings_wrap_yielding_fun(yielding_fun);
+ ycf_node_list_insert_before(&tree_to_insert_to->u.c_file.content,
+ insert_before,
+ yielding_fun);
+ ycf_node_list_insert_before(&tree_to_insert_to->u.c_file.content,
+ insert_before,
+ wrapper_funs);
+ }
+ }else {
+ if(insert_before == NULL){
+ yielding_fun = supress_warnings_wrap_yielding_fun(yielding_fun);
+ ycf_node_list_append(&tree_to_insert_to->u.c_file.content,
+ yielding_fun);
+ }else{
+ yielding_fun = supress_warnings_wrap_yielding_fun(yielding_fun);
+ ycf_node_list_insert_before(&tree_to_insert_to->u.c_file.content,
+ insert_before,
+ yielding_fun);
+ }
+ }
+ if(insert_before == NULL){
+ ycf_node_list_append(&tree_to_insert_to->u.c_file.content,
+ ycf_node_new_text_node("\n"
+ "#undef YCF_STACK_ALLOC\n"
+ "#undef YCF_IN_YIELDING_FUN\n"));
+ }else{
+ ycf_node_list_insert_after(&tree_to_insert_to->u.c_file.content,
+ yielding_fun,
+ ycf_node_new_text_node("\n"
+ "#undef YCF_STACK_ALLOC\n"
+ "#undef YCF_IN_YIELDING_FUN\n"
+ "#define YCF_STACK_ALLOC(SIZE) malloc(SIZE)\n"));
+ ycf_node_list_insert_after(&tree_to_insert_to->u.c_file.content,
+ insert_before,
+ ycf_node_new_text_node("\n#undef YCF_STACK_ALLOC\n"));
+ }
+ return yielding_fun;
+}
+
+ycf_node* ast_get_ast_with_yieldified_function(ycf_node* source_tree,
+ ycf_node* header_tree,
+ char* yielding_function_name,
+ ycf_string_item_list* all_yielding_function_names,
+ bool auto_yield,
+ bool recusive_auto_yield,
+ bool debug_mode,
+ bool only_yielding_funs,
+ ycf_node** only_yielding_funs_tree,
+ bool static_aux_funs)
+{
+ ycf_yield_location_id_counter = 0;
+ ycf_node* tree_ret = ycf_node_deep_copy(source_tree);
+ if (*only_yielding_funs_tree == NULL){
+ *only_yielding_funs_tree = ycf_node_c_file_new(ycf_node_list_empty());
+ }
+ /* Find function */
+ ycf_node* fun = ycf_node_find_function(tree_ret, yielding_function_name);
+ if(fun == NULL){
+ fprintf(stderr, "Could not find function %s\n", yielding_function_name);
+ exit(1);
+ }
+ ycf_node* fun_change = ycf_node_deep_copy(fun);
+ /* Replace alternative syntax for special code sections */
+ fun_change = replace_alt_syntax_special_code_section_code(fun_change);
+ ycf_node_normalize_function(fun_change);
+ /* Brake up control expressions with simple calls to yielding functions */
+ break_up_control_expressions(&fun_change->u.function.body, all_yielding_function_names);
+ /* Insert trap state var for calls to yielding functions */
+ insert_fun_call_state_var(&fun_change->u.function.body, all_yielding_function_names);
+ /* Normalize the function to make transformation easier (move all declarations to the top etc) */
+ ycf_node_normalize_function(fun_change);
+ /* Insert YCF_CONSUME_REDS(1) code if auto yielding is on */
+ if(auto_yield){
+ insert_consume_reds_calls(&fun_change->u.function.body);
+ }
+ /* Save variable declaraions */
+ ycf_node_list uniqified_parameters =
+ ycf_node_list_shallow_copy(fun_change->u.function.definition.parameters);
+ ycf_node_list scope_defs =
+ ycf_node_list_shallow_copy(ycf_node_get_declarations_in_scope(&fun_change->u.function.body));
+ ycf_node_list defs =
+ ycf_node_list_shallow_copy(ycf_node_get_all_definitions_in_function(&fun_change->u.function));
+ /* Add extra vaiables that are needed for yielding */
+ ycf_node_list extra_ycf_trap_state = mk_trap_extra_state();
+ ycf_node_list_concat(&scope_defs, &extra_ycf_trap_state);
+ fun_change->u.function.body.definition_nodes = scope_defs;
+ /* Generate trap state struct */
+ ycf_node_list trap_state_struct_var_declarations =
+ ycf_node_list_shallow_copy(extra_ycf_trap_state);
+ ycf_node_list_concat(&trap_state_struct_var_declarations, &defs);
+ char* ycf_trap_state_struct_name =
+ ycf_string_new("gen_ycf_trap_state_for_%s",
+ yielding_function_name);
+ trap_state_struct_var_declarations =
+ ycf_node_list_copy_concat(mk_saved_ycf_trap_state_params(),
+ trap_state_struct_var_declarations);
+ ycf_node* ycf_trap_state_struct =
+ mk_typedef_struct_node(trap_state_struct_var_declarations,
+ ycf_trap_state_struct_name);
+ /* Add extra parameters for trapping */
+ {
+ ycf_node_list trap_params = mk_ycf_trap_state_params();
+ ycf_node_list saved_params = mk_saved_ycf_trap_state_params();
+ ycf_node_list_concat(&trap_params, &saved_params);
+ ycf_node_list_concat(&trap_params, &fun_change->u.function.definition.parameters);
+ fun_change->u.function.definition.parameters = trap_params;
+ fun_change->u.function.definition.ignore_param_ending = 1;
+ }
+ /* Collect and replace special code */
+ ycf_node_list on_save_yield_state_code_list =
+ save_and_replace_special_code(&fun_change->u.function.body,
+ ycf_node_type_on_save_yield_state_code);
+ ycf_node_list on_restore_yield_state_code_list =
+ save_and_replace_special_code(&fun_change->u.function.body,
+ ycf_node_type_on_restore_yield_state_code);
+ ycf_node_list on_destroy_state_code_list =
+ save_and_replace_special_code(&fun_change->u.function.body,
+ ycf_node_type_on_destroy_state_code);
+ ycf_node_list on_destroy_state_or_return_code_list =
+ save_and_replace_special_code(&fun_change->u.function.body,
+ ycf_node_type_on_destroy_state_or_return_code);
+ ycf_node_list on_return_code_list =
+ save_and_replace_special_code(&fun_change->u.function.body, ycf_node_type_on_return_code);
+ save_nr_of_reductions_before_return(&fun_change->u.function.body,
+ on_destroy_state_or_return_code_list,
+ on_return_code_list,
+ recusive_auto_yield);
+ /* Insert goto yield code in function */
+ insert_yield_code(fun_change,
+ ycf_trap_state_struct_name,
+ trap_state_struct_var_declarations,
+ &fun_change->u.function.body);
+ insert_yield_no_reds_code(fun_change,
+ ycf_trap_state_struct_name,
+ trap_state_struct_var_declarations,
+ &fun_change->u.function.body);
+ {
+ ycf_string_item_list fun_call_state_vars =
+ insert_yielding_fun_call_code(source_tree,
+ &fun_change->u.function.body,
+ all_yielding_function_names,
+ yielding_function_name,
+ recusive_auto_yield);
+ /* Null all fun_call_state_vars */
+ ycf_node_list_prepend(&fun_change->u.function.body.other_nodes, mk_null_vars_code(fun_call_state_vars));
+ }
+ /* Replace YCF_CONSUME_REDS calls */
+ insert_consume_reds_code(yielding_function_name, &fun_change->u.function.body);
+ /* Insert yield initialization in the beginning of the function */
+ {
+ ycf_node* trap_init = mk_yield_init_code(ycf_trap_state_struct_name,
+ trap_state_struct_var_declarations,
+ on_restore_yield_state_code_list,
+ auto_yield,
+ yielding_function_name);
+ ycf_node_list_prepend(&fun_change->u.function.body.other_nodes, trap_init);
+ }
+ /* Add code that saves the state and yields to the end of the function */
+ ycf_node_list_append(&fun_change->u.function.body.other_nodes,
+ mk_yield_code(fun_change,
+ ycf_trap_state_struct_name,
+ trap_state_struct_var_declarations,
+ on_save_yield_state_code_list,
+ debug_mode));
+ /* Change name of function */
+ ycf_node_rename_function(&fun_change->u.function,
+ ycf_string_new("%s_ycf_gen_yielding",
+ yielding_function_name));
+ /* Remove unecessary scopes */
+ ycf_node_remove_unecessary_scopes(&fun_change->u.function.body);
+ /* Make continue function */
+ ycf_node* continue_function =
+ mk_continue_function_node(yielding_function_name,
+ ycf_trap_state_struct_name,
+ fun,
+ uniqified_parameters);
+ /* Make destroy state function */
+ ycf_node* destroy_state_function =
+ mk_destroy_state_function_node(yielding_function_name,
+ trap_state_struct_var_declarations,
+ ycf_trap_state_struct_name,
+ on_destroy_state_code_list,
+ on_destroy_state_or_return_code_list,
+ static_aux_funs);
+ ycf_node* fun_change_dec = mk_fun_def(fun_change);
+ /****************************************************************
+ *
+ * The following code inserts the changed function and its helper
+ * function into the output AST(s)
+ *
+ ****************************************************************/
+ /*>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/
+ /* Insert changed function into tree that will be printed */
+ /*>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/
+ ycf_node* fun_ret = ycf_node_find_function(tree_ret, yielding_function_name);
+ ycf_node* fun_change_wrapper =
+ insert_yielding_function_with_prefix_suffix(tree_ret,
+ fun_ret,
+ fun_change,
+ debug_mode,
+ yielding_function_name,
+ ycf_trap_state_struct_name);
+ /*<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/
+ /* END: Insert changed function into tree that will be printed */
+ /*<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/
+ /*>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/
+ /* Insert declarations in the top */
+ /*>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/
+ ycf_node* fun_dec_node = ycf_node_find_function_declaration(tree_ret, yielding_function_name);
+ ycf_node_list_prepend(&tree_ret->u.c_file.content, ycf_node_new_text_node("\n"));
+ if(fun_dec_node == NULL){
+ ycf_node_list_prepend(&tree_ret->u.c_file.content,
+ mk_wrap_in_surpress_warn("unused-function",
+ mk_fun_def(destroy_state_function)));
+ ycf_node_list_prepend(&tree_ret->u.c_file.content,
+ mk_wrap_in_surpress_warn("unused-function",
+ mk_fun_def(continue_function)));
+ } else {
+ ycf_node_list_insert_before(&tree_ret->u.c_file.content,
+ fun_dec_node,
+ mk_wrap_in_surpress_warn("unused-function",
+ mk_fun_def(destroy_state_function)));
+ ycf_node_list_insert_before(&tree_ret->u.c_file.content,
+ fun_dec_node,
+ mk_wrap_in_surpress_warn("unused-function",
+ mk_fun_def(continue_function)));
+
+ }
+ ycf_node_list_prepend(&header_tree->u.c_file.content, ycf_node_shallow_copy(fun_change_dec));
+ ycf_node_list_prepend(&header_tree->u.c_file.content, ycf_node_new_text_node("\n"));
+ ycf_node_list_prepend(&header_tree->u.c_file.content,
+ mk_wrap_in_surpress_warn("unused-function",
+ mk_fun_def(destroy_state_function)));
+ ycf_node_list_prepend(&header_tree->u.c_file.content,
+ mk_wrap_in_surpress_warn("unused-function",
+ mk_fun_def(continue_function)));
+ ycf_node* ycf_trap_state_struct_dec =
+ ycf_node_new_text_node(ycf_string_new("\n\nstruct %s;",
+ ycf_trap_state_struct->u.gen_typedef_struct.name));
+ ycf_node_list_prepend(&tree_ret->u.c_file.content,
+ ycf_trap_state_struct_dec);
+ ycf_node_list_prepend(&header_tree->u.c_file.content,
+ ycf_node_shallow_copy(ycf_trap_state_struct_dec));
+ ycf_node_list_append(&header_tree->u.c_file.content,
+ ycf_node_new_text_node("\n"));
+ /* Insert definition of changed function */
+ if(fun_dec_node != NULL){
+ ycf_node_list_insert_before(&tree_ret->u.c_file.content,
+ fun_dec_node,
+ ycf_node_shallow_copy(ycf_node_shallow_copy(fun_change_dec)));
+ }else{
+ ycf_node_list_insert_before(&tree_ret->u.c_file.content,
+ fun_change_wrapper,
+ ycf_node_shallow_copy(ycf_node_shallow_copy(fun_change_dec)));
+ }
+ ycf_node_list_insert_before(&tree_ret->u.c_file.content, fun_change_wrapper, ycf_trap_state_struct);
+ ycf_node_list_insert_after(&tree_ret->u.c_file.content,
+ ycf_trap_state_struct,
+ supress_warnings_wrap_destroy_fun(destroy_state_function));
+ ycf_node_list_insert_after(&tree_ret->u.c_file.content,
+ ycf_trap_state_struct,
+ mk_wrap_in_surpress_warn("unused-function",
+ continue_function));
+ /*<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/
+ /* END: Insert declarations in the top */
+ /*<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/
+ if(only_yielding_funs){
+ ycf_node_list_prepend(&(*only_yielding_funs_tree)->u.c_file.content,
+ ycf_node_new_text_node("\n"));
+ ycf_node_list_prepend(&(*only_yielding_funs_tree)->u.c_file.content,
+ ycf_node_shallow_copy(fun_change_dec));
+ ycf_node_list_prepend(&(*only_yielding_funs_tree)->u.c_file.content,
+ ycf_node_new_text_node("\n"));
+ ycf_node_list_prepend(&(*only_yielding_funs_tree)->u.c_file.content,
+ mk_wrap_in_surpress_warn("unused-function",mk_fun_def(destroy_state_function)));
+ ycf_node_list_prepend(&(*only_yielding_funs_tree)->u.c_file.content,
+ ycf_node_new_text_node("\n"));
+ ycf_node_list_prepend(&(*only_yielding_funs_tree)->u.c_file.content,
+ mk_wrap_in_surpress_warn("unused-function",
+ mk_fun_def(continue_function)));
+ ycf_node_list_prepend(&(*only_yielding_funs_tree)->u.c_file.content,
+ ycf_node_new_text_node("\n"));
+ ycf_node_list_prepend(&(*only_yielding_funs_tree)->u.c_file.content,
+ ycf_node_shallow_copy(ycf_trap_state_struct_dec));
+ ycf_node_list_prepend(&(*only_yielding_funs_tree)->u.c_file.content,
+ ycf_node_new_text_node("\n"));
+
+ ycf_node_list_append(&(*only_yielding_funs_tree)->u.c_file.content,
+ ycf_node_shallow_copy(ycf_trap_state_struct));
+ ycf_node_list_append(&(*only_yielding_funs_tree)->u.c_file.content,
+ ycf_node_new_text_node("\n"));
+ ycf_node_list_append(&(*only_yielding_funs_tree)->u.c_file.content,
+ supress_warnings_wrap_destroy_fun(destroy_state_function));
+ ycf_node_list_append(&(*only_yielding_funs_tree)->u.c_file.content,
+ ycf_node_new_text_node("\n"));
+ ycf_node_list_append(&(*only_yielding_funs_tree)->u.c_file.content,
+ mk_wrap_in_surpress_warn("unused-function",continue_function));
+ insert_yielding_function_with_prefix_suffix((*only_yielding_funs_tree),
+ NULL,
+ fun_change,
+ debug_mode,
+ yielding_function_name,
+ ycf_trap_state_struct_name);
+ }
+ return tree_ret;
+}
diff --git a/erts/lib_src/yielding_c_fun/ycf_yield_fun.h b/erts/lib_src/yielding_c_fun/ycf_yield_fun.h
new file mode 100644
index 0000000000..410256e71c
--- /dev/null
+++ b/erts/lib_src/yielding_c_fun/ycf_yield_fun.h
@@ -0,0 +1,116 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB and Kjell Winblad 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%
+ */
+
+/*
+ * Author: Kjell Winblad
+ */
+
+
+#ifndef YCF_AST_H
+#define YCF_AST_H
+
+#include "ycf_utils.h"
+#include "ycf_symbol.h"
+#include "ycf_string.h"
+#include "ycf_node.h"
+
+
+typedef struct {
+ bool success;
+ ycf_symbol* next_symbol;
+ ycf_node* result;
+} ycf_parse_result;
+
+
+/* Functions for strings */
+
+void string_item_list_print(ycf_string_item_list n);
+
+
+/* Functions for symbol lists */
+
+int ycf_symbol_list_get_item_position(ycf_symbol_list* list, ycf_symbol* node);
+
+ycf_symbol* ycf_symbol_shallow_copy(ycf_symbol* n);
+ycf_symbol* ycf_symbol_list_get_item_at_position(ycf_symbol_list* list, int pos);
+
+void ycf_symbol_list_append(ycf_symbol_list* list, ycf_symbol* node);
+void ycf_symbol_list_prepend(ycf_symbol_list* list, ycf_symbol* node);
+void ycf_symbol_list_insert_before(ycf_symbol_list* list, ycf_symbol* before_this, ycf_symbol* to_insert);
+void ycf_symbol_list_insert_after(ycf_symbol_list* list, ycf_symbol* after_this, ycf_symbol* to_insert);
+void ycf_symbol_list_remove(ycf_symbol_list* list, ycf_symbol* to_remove);
+void ycf_symbol_list_replace(ycf_symbol_list* list, ycf_symbol* to_replace, ycf_symbol* replace_with);
+void ycf_symbol_list_concat(ycf_symbol_list* list1, ycf_symbol_list* list2);
+
+ycf_symbol_list ycf_symbol_list_empty();
+ycf_symbol_list ycf_symbol_list_shallow_copy(ycf_symbol_list n);
+ycf_symbol_list ycf_symbol_list_copy_append(ycf_symbol_list list, ycf_symbol* node);
+ycf_symbol_list ycf_symbol_list_copy_prepend(ycf_symbol_list list, ycf_symbol* node);
+ycf_symbol_list ycf_symbol_list_copy_insert_before(ycf_symbol_list list, ycf_symbol* before_this, ycf_symbol* to_insert);
+ycf_symbol_list ycf_symbol_list_copy_insert_after(ycf_symbol_list list, ycf_symbol* after_this, ycf_symbol* to_insert);
+ycf_symbol_list ycf_symbol_list_copy_remove(ycf_symbol_list list, ycf_symbol* to_remove);
+ycf_symbol_list ycf_symbol_list_copy_replace(ycf_symbol_list list, ycf_symbol* to_replace, ycf_symbol* replace_with);
+ycf_symbol_list ycf_symbol_list_copy_concat(ycf_symbol_list list1, ycf_symbol_list list2);
+
+
+
+
+/* Functions for parsing text to AST */
+
+ycf_node* ycf_node_scope_new(ycf_symbol* start,
+ ycf_node_list declaration_nodes,
+ ycf_node_list other_nodes,
+ ycf_symbol* end);
+ycf_parse_result parse_expression(ycf_symbol* symbols);
+
+
+void print_abstract_syntax_tree(ycf_node* node);
+void print_node_code_paran_expression(ycf_node_parentheses_expression e, ycf_string_printable_buffer* b);
+void print_node_code_expression(ycf_node_expression e, ycf_string_printable_buffer* b);
+
+void ast_add_yield_code_generated_define(ycf_node* source_out_tree/*Will be changed*/, bool debug_mode);
+void print_symbol_list(ycf_symbol_list* l, ycf_string_printable_buffer* b);
+
+
+/* Abstract syntax tree functions */
+
+
+
+ycf_node* ycf_node_find_function(ycf_node* c_file_node, char* fun_name);
+ycf_node_list ycf_node_get_all_definitions_in_function(ycf_node_function* f);
+
+
+ycf_node* ast_get_ast_with_yieldified_function(ycf_node* source_tree,
+ ycf_node* header_tree, /*Will be changed*/
+ char* yielding_function_name,
+ ycf_string_item_list* all_yielding_function_names,
+ bool auto_yield,
+ bool recusive_auto_yield,
+ bool debug_mode,
+ bool only_yielding_funs,
+ ycf_node** only_yielding_funs_tree,
+ bool static_aux_funs);
+
+void print_abstract_syntax_tree(ycf_node* node);
+void print_node_code_expression(ycf_node_expression e, ycf_string_printable_buffer* b);
+void print_node_list_code(ycf_node* n, ycf_string_printable_buffer* b);
+void ast_add_yield_code_generated_define(ycf_node* source_out_tree, bool debug_mode);
+
+#endif
diff --git a/erts/preloaded/ebin/atomics.beam b/erts/preloaded/ebin/atomics.beam
index aa246f8808..903ec90218 100644
--- a/erts/preloaded/ebin/atomics.beam
+++ b/erts/preloaded/ebin/atomics.beam
Binary files differ
diff --git a/erts/preloaded/ebin/counters.beam b/erts/preloaded/ebin/counters.beam
index 1fb85276f9..53a839ce71 100644
--- a/erts/preloaded/ebin/counters.beam
+++ b/erts/preloaded/ebin/counters.beam
Binary files differ
diff --git a/erts/preloaded/ebin/erl_init.beam b/erts/preloaded/ebin/erl_init.beam
index 04559ff767..feef7ae343 100644
--- a/erts/preloaded/ebin/erl_init.beam
+++ b/erts/preloaded/ebin/erl_init.beam
Binary files differ
diff --git a/erts/preloaded/ebin/erl_prim_loader.beam b/erts/preloaded/ebin/erl_prim_loader.beam
index b8adf71297..9490303fb8 100644
--- a/erts/preloaded/ebin/erl_prim_loader.beam
+++ b/erts/preloaded/ebin/erl_prim_loader.beam
Binary files differ
diff --git a/erts/preloaded/ebin/erl_tracer.beam b/erts/preloaded/ebin/erl_tracer.beam
index 4430489710..3df4d13400 100644
--- a/erts/preloaded/ebin/erl_tracer.beam
+++ b/erts/preloaded/ebin/erl_tracer.beam
Binary files differ
diff --git a/erts/preloaded/ebin/erlang.beam b/erts/preloaded/ebin/erlang.beam
index b744d97a41..4c69aa5a74 100644
--- a/erts/preloaded/ebin/erlang.beam
+++ b/erts/preloaded/ebin/erlang.beam
Binary files differ
diff --git a/erts/preloaded/ebin/erts_code_purger.beam b/erts/preloaded/ebin/erts_code_purger.beam
index a94b37854e..816d198ce8 100644
--- a/erts/preloaded/ebin/erts_code_purger.beam
+++ b/erts/preloaded/ebin/erts_code_purger.beam
Binary files differ
diff --git a/erts/preloaded/ebin/erts_dirty_process_signal_handler.beam b/erts/preloaded/ebin/erts_dirty_process_signal_handler.beam
index dd92fb9582..1e211e9662 100644
--- a/erts/preloaded/ebin/erts_dirty_process_signal_handler.beam
+++ b/erts/preloaded/ebin/erts_dirty_process_signal_handler.beam
Binary files differ
diff --git a/erts/preloaded/ebin/erts_internal.beam b/erts/preloaded/ebin/erts_internal.beam
index d52ae9a2dc..a5583cdbf5 100644
--- a/erts/preloaded/ebin/erts_internal.beam
+++ b/erts/preloaded/ebin/erts_internal.beam
Binary files differ
diff --git a/erts/preloaded/ebin/erts_literal_area_collector.beam b/erts/preloaded/ebin/erts_literal_area_collector.beam
index 44103e5d56..37c3c988b6 100644
--- a/erts/preloaded/ebin/erts_literal_area_collector.beam
+++ b/erts/preloaded/ebin/erts_literal_area_collector.beam
Binary files differ
diff --git a/erts/preloaded/ebin/init.beam b/erts/preloaded/ebin/init.beam
index 06074d1717..b29a8031d0 100644
--- a/erts/preloaded/ebin/init.beam
+++ b/erts/preloaded/ebin/init.beam
Binary files differ
diff --git a/erts/preloaded/ebin/persistent_term.beam b/erts/preloaded/ebin/persistent_term.beam
index dc4a1aceb5..752c5dc65f 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_buffer.beam b/erts/preloaded/ebin/prim_buffer.beam
index a54a956fac..cac7cd0abb 100644
--- a/erts/preloaded/ebin/prim_buffer.beam
+++ b/erts/preloaded/ebin/prim_buffer.beam
Binary files differ
diff --git a/erts/preloaded/ebin/prim_eval.beam b/erts/preloaded/ebin/prim_eval.beam
index 7b39f3e5ba..86c88f30b4 100644
--- a/erts/preloaded/ebin/prim_eval.beam
+++ b/erts/preloaded/ebin/prim_eval.beam
Binary files differ
diff --git a/erts/preloaded/ebin/prim_file.beam b/erts/preloaded/ebin/prim_file.beam
index d0ef080a30..927eb70558 100644
--- a/erts/preloaded/ebin/prim_file.beam
+++ b/erts/preloaded/ebin/prim_file.beam
Binary files differ
diff --git a/erts/preloaded/ebin/prim_inet.beam b/erts/preloaded/ebin/prim_inet.beam
index 197ff59503..f77df9795f 100644
--- a/erts/preloaded/ebin/prim_inet.beam
+++ b/erts/preloaded/ebin/prim_inet.beam
Binary files differ
diff --git a/erts/preloaded/ebin/prim_net.beam b/erts/preloaded/ebin/prim_net.beam
index 76d4f0fc69..88e0237262 100644
--- a/erts/preloaded/ebin/prim_net.beam
+++ b/erts/preloaded/ebin/prim_net.beam
Binary files differ
diff --git a/erts/preloaded/ebin/prim_zip.beam b/erts/preloaded/ebin/prim_zip.beam
index 75413bb065..69b14fa353 100644
--- a/erts/preloaded/ebin/prim_zip.beam
+++ b/erts/preloaded/ebin/prim_zip.beam
Binary files differ
diff --git a/erts/preloaded/ebin/socket.beam b/erts/preloaded/ebin/socket.beam
index 1a5f34f3ee..34168521e5 100644
--- a/erts/preloaded/ebin/socket.beam
+++ b/erts/preloaded/ebin/socket.beam
Binary files differ
diff --git a/erts/preloaded/ebin/socket_registry.beam b/erts/preloaded/ebin/socket_registry.beam
index a5eb2c190f..ed205ead81 100644
--- a/erts/preloaded/ebin/socket_registry.beam
+++ b/erts/preloaded/ebin/socket_registry.beam
Binary files differ
diff --git a/erts/preloaded/ebin/zlib.beam b/erts/preloaded/ebin/zlib.beam
index 14558a7424..d2d178a281 100644
--- a/erts/preloaded/ebin/zlib.beam
+++ b/erts/preloaded/ebin/zlib.beam
Binary files differ
diff --git a/erts/preloaded/src/erl_prim_loader.erl b/erts/preloaded/src/erl_prim_loader.erl
index 89c535c43a..658a138694 100644
--- a/erts/preloaded/src/erl_prim_loader.erl
+++ b/erts/preloaded/src/erl_prim_loader.erl
@@ -300,18 +300,18 @@ check_file_result(Func, Target, {error,Reason}) ->
%% This is equal to calling logger:error/2 which
%% we don't want to do from code_server during system boot.
%% We don't want to call logger:timestamp() either.
- try
- logger ! {log,error,#{label=>{?MODULE,file_error},report=>Report},
- #{pid=>self(),
- gl=>group_leader(),
- time=>os:system_time(microsecond),
- error_logger=>#{tag=>error_report,
- type=>std_error}}}
- catch _:_ ->
- %% If logger has not been started yet we just display it
- erlang:display({?MODULE,file_error}),
- erlang:display(Report)
- end,
+ _ = try
+ logger ! {log,error,#{label=>{?MODULE,file_error},report=>Report},
+ #{pid=>self(),
+ gl=>group_leader(),
+ time=>os:system_time(microsecond),
+ error_logger=>#{tag=>error_report,
+ type=>std_error}}}
+ catch _:_ ->
+ %% If logger has not been started yet we just display it
+ erlang:display({?MODULE,file_error}),
+ erlang:display(Report)
+ end,
error
end;
check_file_result(_, _, Other) ->
diff --git a/erts/preloaded/src/erlang.erl b/erts/preloaded/src/erlang.erl
index 82d0aa91f8..600818f6c6 100644
--- a/erts/preloaded/src/erlang.erl
+++ b/erts/preloaded/src/erlang.erl
@@ -20,12 +20,14 @@
-module(erlang).
-export([apply/2,apply/3,spawn/4,spawn_link/4,
- spawn_monitor/1,spawn_monitor/3,
+ spawn_monitor/1,spawn_monitor/2,
+ spawn_monitor/3,spawn_monitor/4,
spawn_opt/2,spawn_opt/3,spawn_opt/4,spawn_opt/5,
- disconnect_node/1]).
+ spawn_request/1, spawn_request/2,
+ spawn_request/3, spawn_request/4, spawn_request/5,
+ spawn_request_abandon/1, disconnect_node/1]).
-export([spawn/1, spawn_link/1, spawn/2, spawn_link/2]).
-export([yield/0]).
--export([crasher/6]).
-export([fun_info/1]).
-export([send_nosuspend/2, send_nosuspend/3]).
-export([localtime_to_universaltime/1]).
@@ -52,7 +54,13 @@
dist_ctrl_set_opt/3,
dist_get_stat/1]).
--deprecated([get_stacktrace/0,now/0]).
+-deprecated([{get_stacktrace,0,
+ "use the new try/catch syntax for retrieving the "
+ "stack backtrace"}]).
+-deprecated([{now,0,
+ "see the \"Time and Time Correction in Erlang\" "
+ "chapter of the ERTS User's Guide for more information"}]).
+-removed([{hash,2,"use erlang:phash2/2 instead"}]).
%% Get rid of autoimports of spawn to avoid clashes with ourselves.
-compile({no_auto_import,[spawn_link/1]}).
@@ -64,8 +72,13 @@
-export_type([timestamp/0]).
-export_type([time_unit/0]).
-export_type([deprecated_time_unit/0]).
+-export_type([spawn_opt_option/0]).
+-export_type([priority_level/0]).
+-export_type([max_heap_size/0]).
+-export_type([message_queue_data/0]).
-type ext_binary() :: binary().
+-type ext_iovec() :: iovec().
-type timestamp() :: {MegaSecs :: non_neg_integer(),
Secs :: non_neg_integer(),
MicroSecs :: non_neg_integer()}.
@@ -109,8 +122,11 @@
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-export([adler32/1, adler32/2, adler32_combine/3, append_element/2]).
--export([atom_to_binary/2, atom_to_list/1, binary_part/2, binary_part/3]).
--export([binary_to_atom/2, binary_to_existing_atom/2, binary_to_float/1]).
+-export([atom_to_binary/1, atom_to_binary/2]).
+-export([atom_to_list/1, binary_part/2, binary_part/3]).
+-export([binary_to_atom/1, binary_to_atom/2]).
+-export([binary_to_existing_atom/1, binary_to_existing_atom/2]).
+-export([binary_to_float/1]).
-export([binary_to_integer/1,binary_to_integer/2]).
-export([binary_to_list/1]).
-export([binary_to_list/3, binary_to_term/1, binary_to_term/2]).
@@ -178,14 +194,37 @@
make_tuple/2, make_tuple/3, nodes/1, open_port/2,
port_call/2, port_call/3, port_info/1, port_info/2, process_flag/2,
process_info/2, send/2, send/3, seq_trace_info/1,
- setelement/3, spawn_opt/1,
+ setelement/3,
statistics/1, subtract/2, system_flag/2,
- term_to_binary/1, term_to_binary/2, tl/1, trace_pattern/2,
+ term_to_binary/1, term_to_binary/2,
+ term_to_iovec/1, term_to_iovec/2,
+ tl/1, trace_pattern/2,
trace_pattern/3, tuple_to_list/1, system_info/1,
universaltime_to_localtime/1]).
-export([dt_get_tag/0, dt_get_tag_data/0, dt_prepend_vm_tag_data/1, dt_append_vm_tag_data/1,
dt_put_tag/1, dt_restore_tag/1, dt_spread_tag/1]).
+%% Operators
+
+-export(['=='/2, '=:='/2,
+ '/='/2, '=/='/2,
+ '=<'/2, '>='/2,
+ '<'/2, '>'/2]).
+
+-export(['-'/1, '+'/1,
+ '-'/2, '+'/2,
+ '/'/2, '*'/2,
+ 'div'/2, 'rem'/2,
+ 'bsl'/2, 'bsr'/2,
+ 'bor'/2, 'band'/2,
+ 'bxor'/2, 'bnot'/1]).
+
+-export(['and'/2, 'or'/2,
+ 'xor'/2, 'not'/1]).
+
+-export(['--'/2, '++'/2]).
+
+-export(['!'/2]).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%% Simple native code BIFs
@@ -341,6 +380,12 @@ adler32_combine(_FirstAdler, _SecondAdler, _SecondSize) ->
append_element(_Tuple1, _Term) ->
erlang:nif_error(undefined).
+%% atom_to_binary/1
+-spec atom_to_binary(Atom) -> binary() when
+ Atom :: atom().
+atom_to_binary(Atom) ->
+ erlang:atom_to_binary(Atom, utf8).
+
%% atom_to_binary/2
-spec atom_to_binary(Atom, Encoding) -> binary() when
Atom :: atom(),
@@ -371,6 +416,12 @@ binary_part(_Subject, _PosLen) ->
binary_part(_Subject, _Start, _Length) ->
erlang:nif_error(undefined).
+%% binary_to_atom/1
+-spec binary_to_atom(Binary) -> atom() when
+ Binary :: binary().
+binary_to_atom(Binary) ->
+ erlang:binary_to_atom(Binary, utf8).
+
%% binary_to_atom/2
-spec binary_to_atom(Binary, Encoding) -> atom() when
Binary :: binary(),
@@ -378,6 +429,12 @@ binary_part(_Subject, _Start, _Length) ->
binary_to_atom(_Binary, _Encoding) ->
erlang:nif_error(undefined).
+%% binary_to_existing_atom/1
+-spec binary_to_existing_atom(Binary) -> atom() when
+ Binary :: binary().
+binary_to_existing_atom(Binary) ->
+ erlang:binary_to_existing_atom(Binary, utf8).
+
%% binary_to_existing_atom/2
-spec binary_to_existing_atom(Binary, Encoding) -> atom() when
Binary :: binary(),
@@ -585,7 +642,7 @@ date() ->
HttpHeader :: {'http_header',
integer(),
HttpField,
- Reserved :: term(),
+ UnmodifiedField :: HttpString,
Value :: HttpString},
HttpError :: {'http_error', HttpString},
HttpMethod :: 'OPTIONS' | 'GET' | 'HEAD' | 'POST' | 'PUT' | 'DELETE'
@@ -1710,11 +1767,9 @@ setnode(_P1, _P2) ->
-spec erlang:setnode(Node, DistCtrlr, Opts) -> dist_handle() when
Node :: atom(),
DistCtrlr :: port() | pid(),
- Opts :: {integer(), integer(), atom(), atom()}.
-setnode(Node, DistCtrlr, {Flags, Ver, IC, OC} = Opts) when erlang:is_atom(IC),
- erlang:is_atom(OC) ->
- case case erts_internal:create_dist_channel(Node, DistCtrlr,
- Flags, Ver) of
+ Opts :: {integer(), integer(), pos_integer()}.
+setnode(Node, DistCtrlr, {_Flags, _Ver, _Creation} = Opts) ->
+ case case erts_internal:create_dist_channel(Node, DistCtrlr, Opts) of
{ok, DH} -> DH;
{message, Ref} -> receive {Ref, Res} -> Res end;
Err -> Err
@@ -2186,7 +2241,7 @@ nodes(_Arg) ->
-spec open_port(PortName, PortSettings) -> port() when
PortName :: {spawn, Command :: string() | binary()} |
{spawn_driver, Command :: string() | binary()} |
- {spawn_executable, FileName :: file:name() } |
+ {spawn_executable, FileName :: file:name_all() } |
{fd, In :: non_neg_integer(), Out :: non_neg_integer()},
PortSettings :: [Opt],
Opt :: {packet, N :: 1 | 2 | 4}
@@ -2395,20 +2450,6 @@ seq_trace_info(_What) ->
setelement(_Index, _Tuple1, _Value) ->
erlang:nif_error(undefined).
--spec erlang:spawn_opt({Module, Function, Args, Options}) -> pid() | {pid(), reference()} when
- Module :: module(),
- Function :: atom(),
- Args :: [term()],
- Options :: [Option],
- Option :: link | monitor
- | {priority, Level :: priority_level()}
- | {fullsweep_after, Number :: non_neg_integer()}
- | {min_heap_size, Size :: non_neg_integer()}
- | {max_heap_size, Size :: max_heap_size()}
- | {min_bin_vheap_size, VSize :: non_neg_integer()}.
-spawn_opt(_Tuple) ->
- erlang:nif_error(undefined).
-
-spec statistics(active_tasks) -> [ActiveTasks] when
ActiveTasks :: non_neg_integer();
(active_tasks_all) -> [ActiveTasks] when
@@ -2556,11 +2597,24 @@ term_to_binary(_Term) ->
-spec term_to_binary(Term, Options) -> ext_binary() when
Term :: term(),
Options :: [compressed |
- {compressed, Level :: 0..9} |
- {minor_version, Version :: 0..2} ].
+ {compressed, Level :: 0..9} |
+ {minor_version, Version :: 0..2} ].
term_to_binary(_Term, _Options) ->
erlang:nif_error(undefined).
+-spec term_to_iovec(Term) -> ext_iovec() when
+ Term :: term().
+term_to_iovec(_Term) ->
+ erlang:nif_error(undefined).
+
+-spec term_to_iovec(Term, Options) -> ext_iovec() when
+ Term :: term(),
+ Options :: [compressed |
+ {compressed, Level :: 0..9} |
+ {minor_version, Version :: 0..2} ].
+term_to_iovec(_Term, _Options) ->
+ erlang:nif_error(undefined).
+
%% Shadowed by erl_bif_types: erlang:tl/1
-spec tl(List) -> term() when
List :: [term(), ...].
@@ -2836,10 +2890,24 @@ spawn_link(N, F) ->
-spec spawn_monitor(Fun) -> {pid(), reference()} when
Fun :: function().
spawn_monitor(F) when erlang:is_function(F, 0) ->
- erlang:spawn_opt({erlang,apply,[F,[]],[monitor]});
+ erlang:spawn_opt(erlang,apply,[F,[]],[monitor]);
spawn_monitor(F) ->
erlang:error(badarg, [F]).
+-spec spawn_monitor(Node, Fun) -> {pid(), reference()} when
+ Node :: node(),
+ Fun :: function().
+
+spawn_monitor(Node, F) when erlang:is_atom(Node), erlang:is_function(F, 0) ->
+ try
+ erlang:spawn_monitor(Node,erlang,apply,[F,[]])
+ catch
+ error:Err ->
+ erlang:error(Err, [Node, F])
+ end;
+spawn_monitor(Node, F) ->
+ erlang:error(badarg, [Node, F]).
+
-spec spawn_monitor(Module, Function, Args) -> {pid(), reference()} when
Module :: module(),
Function :: atom(),
@@ -2847,7 +2915,7 @@ spawn_monitor(F) ->
spawn_monitor(M, F, A) when erlang:is_atom(M),
erlang:is_atom(F),
erlang:is_list(A) ->
- erlang:spawn_opt({M,F,A,[monitor]});
+ erlang:spawn_opt(M,F,A,[monitor]);
spawn_monitor(M, F, A) ->
erlang:error(badarg, [M,F,A]).
@@ -2873,24 +2941,23 @@ spawn_monitor(M, F, A) ->
Fun :: function(),
Options :: [spawn_opt_option()].
spawn_opt(F, O) when erlang:is_function(F) ->
- spawn_opt(erlang, apply, [F, []], O);
+ erlang:spawn_opt(erlang, apply, [F, []], O);
spawn_opt({M,F}=MF, O) when erlang:is_atom(M), erlang:is_atom(F) ->
- spawn_opt(erlang, apply, [MF, []], O);
-spawn_opt({M,F,A}, O) -> % For (undocumented) backward compatibility
- spawn_opt(M, F, A, O);
+ erlang:spawn_opt(erlang, apply, [MF, []], O);
spawn_opt(F, O) ->
erlang:error(badarg, [F, O]).
-spec spawn_opt(Node, Fun, Options) -> pid() | {pid(), reference()} when
Node :: node(),
Fun :: function(),
- Options :: [spawn_opt_option()].
+ Options :: [monitor | link | OtherOption],
+ OtherOption :: term().
spawn_opt(N, F, O) when N =:= erlang:node() ->
- spawn_opt(F, O);
-spawn_opt(N, F, O) when erlang:is_function(F) ->
- spawn_opt(N, erlang, apply, [F, []], O);
+ erlang:spawn_opt(F, O);
+spawn_opt(N, F, O) when erlang:is_function(F, 0) ->
+ erlang:spawn_opt(N, erlang, apply, [F, []], O);
spawn_opt(N, {M,F}=MF, O) when erlang:is_atom(M), erlang:is_atom(F) ->
- spawn_opt(N, erlang, apply, [MF, []], O);
+ erlang:spawn_opt(N, erlang, apply, [MF, []], O);
spawn_opt(N, F, O) ->
erlang:error(badarg, [N, F, O]).
@@ -2909,24 +2976,11 @@ spawn(N,M,F,A) when N =:= erlang:node(),
spawn(N,M,F,A) when erlang:is_atom(N),
erlang:is_atom(M),
erlang:is_atom(F) ->
- case is_well_formed_list(A) of
- true ->
- ok;
- false ->
- erlang:error(badarg, [N, M, F, A])
- end,
- case catch gen_server:call({net_kernel,N},
- {spawn,M,F,A,erlang:group_leader()},
- infinity) of
- Pid when erlang:is_pid(Pid) ->
- Pid;
- Error ->
- case remote_spawn_error(Error, {no_link, N, M, F, A, []}) of
- {fault, Fault} ->
- erlang:error(Fault, [N, M, F, A]);
- Pid ->
- Pid
- end
+ try
+ erlang:spawn_opt(N, M, F, A, [])
+ catch
+ _:Reason ->
+ erlang:error(Reason, [N, M, F, A])
end;
spawn(N,M,F,A) ->
erlang:error(badarg, [N, M, F, A]).
@@ -2944,41 +2998,69 @@ spawn_link(N,M,F,A) when N =:= erlang:node(),
spawn_link(N,M,F,A) when erlang:is_atom(N),
erlang:is_atom(M),
erlang:is_atom(F) ->
- case is_well_formed_list(A) of
- true ->
- ok;
- _ ->
- erlang:error(badarg, [N, M, F, A])
- end,
- case catch gen_server:call({net_kernel,N},
- {spawn_link,M,F,A,erlang:group_leader()},
- infinity) of
- Pid when erlang:is_pid(Pid) ->
- Pid;
- Error ->
- case remote_spawn_error(Error, {link, N, M, F, A, []}) of
- {fault, Fault} ->
- erlang:error(Fault, [N, M, F, A]);
- Pid ->
- Pid
- end
+ try
+ erlang:spawn_opt(N, M, F, A, [link])
+ catch
+ _:Reason ->
+ erlang:error(Reason, [N, M, F, A])
end;
spawn_link(N,M,F,A) ->
erlang:error(badarg, [N, M, F, A]).
+-spec spawn_monitor(Node, Module, Function, Args) -> {pid(), reference()} when
+ Node :: node(),
+ Module :: module(),
+ Function :: atom(),
+ Args :: [term()].
+spawn_monitor(N,M,F,A) when N =:= erlang:node(),
+ erlang:is_atom(M),
+ erlang:is_atom(F),
+ erlang:is_list(A) ->
+ try
+ erlang:spawn_monitor(M,F,A)
+ catch
+ error:Err ->
+ erlang:error(Err, [N, M, F, A])
+ end;
+spawn_monitor(N,M,F,A) when erlang:is_atom(N),
+ erlang:is_atom(M),
+ erlang:is_atom(F) ->
+ Ref = try
+ erlang:spawn_request(N, M, F, A, [monitor])
+ catch
+ error:Err0 ->
+ erlang:error(Err0, [N, M, F, A])
+ end,
+ receive
+ {spawn_reply, Ref, ok, Pid} when erlang:is_pid(Pid) ->
+ {Pid, Ref};
+ {spawn_reply, Ref, error, badopt} ->
+ erlang:error(badarg, [N, M, F, A]);
+ {spawn_reply, Ref, error, noconnection} ->
+ try
+ erlang:spawn_opt(erts_internal,crasher,
+ [N,M,F,A,[monitor],
+ noconnection],
+ [monitor])
+ catch
+ _:Err1 ->
+ erlang:error(Err1, [N, M, F, A])
+ end;
+ {spawn_reply, Ref, error, Err2} ->
+ erlang:error(Err2, [N, M, F, A])
+ end;
+spawn_monitor(N,M,F,A) ->
+ erlang:error(badarg, [N, M, F, A]).
+
-spec spawn_opt(Module, Function, Args, Options) ->
pid() | {pid(), reference()} when
Module :: module(),
Function :: atom(),
Args :: [term()],
Options :: [spawn_opt_option()].
-spawn_opt(M, F, A, Opts) ->
- case catch erlang:spawn_opt({M,F,A,Opts}) of
- {'EXIT',{Reason,_}} ->
- erlang:error(Reason, [M,F,A,Opts]);
- Res ->
- Res
- end.
+spawn_opt(_Module, _Function, _Args, _Options) ->
+ erlang:nif_error(undefined).
+
-spec spawn_opt(Node, Module, Function, Args, Options) ->
pid() | {pid(), reference()} when
@@ -2986,47 +3068,76 @@ spawn_opt(M, F, A, Opts) ->
Module :: module(),
Function :: atom(),
Args :: [term()],
- Options :: [spawn_opt_option()].
+ Options :: [monitor | link | OtherOption],
+ OtherOption :: term().
+
spawn_opt(N, M, F, A, O) when N =:= erlang:node(),
erlang:is_atom(M), erlang:is_atom(F),
erlang:is_list(A), erlang:is_list(O) ->
- spawn_opt(M, F, A, O);
+ erlang:spawn_opt(M, F, A, O);
spawn_opt(N, M, F, A, O) when erlang:is_atom(N),
erlang:is_atom(M),
erlang:is_atom(F) ->
- case {is_well_formed_list(A), is_well_formed_list(O)} of
- {true, true} ->
- ok;
- _ ->
- erlang:error(badarg, [N, M, F, A, O])
- end,
- case lists:member(monitor, O) of
- false -> ok;
- true -> erlang:error(badarg, [N, M, F, A, O])
- end,
- {L,NO} = lists:foldl(fun (link, {_, NewOpts}) ->
- {link, NewOpts};
- (Opt, {LO, NewOpts}) ->
- {LO, [Opt|NewOpts]}
- end,
- {no_link,[]},
- O),
- case catch gen_server:call({net_kernel,N},
- {spawn_opt,M,F,A,NO,L,erlang:group_leader()},
- infinity) of
- Pid when erlang:is_pid(Pid) ->
- Pid;
- Error ->
- case remote_spawn_error(Error, {L, N, M, F, A, NO}) of
- {fault, Fault} ->
- erlang:error(Fault, [N, M, F, A, O]);
- Pid ->
- Pid
- end
+ {Ref, MonOpt} = case erts_internal:dist_spawn_request(N, {M, F, A}, O, spawn_opt) of
+ {R, MO} when erlang:is_reference(R) -> {R, MO};
+ badarg -> erlang:error(badarg, [N, M, F, A, O])
+ end,
+ receive
+ {spawn_reply, Ref, ok, Pid} when erlang:is_pid(Pid) ->
+ case MonOpt of
+ true -> {Pid, Ref};
+ false -> Pid
+ end;
+ {spawn_reply, Ref, error, badopt} ->
+ erlang:error(badarg, [N, M, F, A, O]);
+ {spawn_reply, Ref, error, noconnection} ->
+ try
+ erlang:spawn_opt(erts_internal,crasher,
+ [N,M,F,A,O,noconnection], O)
+ catch
+ _:Err1 ->
+ erlang:error(Err1, [N, M, F, A, O])
+ end;
+ {spawn_reply, Ref, error, notsup} ->
+ case old_remote_spawn_opt(N, M, F, A, O) of
+ Pid when erlang:is_pid(Pid) ->
+ Pid;
+ Err2 ->
+ erlang:error(Err2, [N, M, F, A, O])
+ end;
+ {spawn_reply, Ref, error, Err3} ->
+ erlang:error(Err3, [N, M, F, A, O])
end;
spawn_opt(N,M,F,A,O) ->
erlang:error(badarg, [N,M,F,A,O]).
+old_remote_spawn_opt(N, M, F, A, O) ->
+ case lists:member(monitor, O) of
+ true ->
+ badarg;
+ _ ->
+ {L,NO} = lists:foldl(fun (link, {_, NewOpts}) ->
+ {link, NewOpts};
+ (Opt, {LO, NewOpts}) ->
+ {LO, [Opt|NewOpts]}
+ end,
+ {no_link,[]},
+ O),
+ case catch gen_server:call({net_kernel,N},
+ {spawn_opt,M,F,A,NO,L,erlang:group_leader()},
+ infinity) of
+ Pid when erlang:is_pid(Pid) ->
+ Pid;
+ Error ->
+ case remote_spawn_error(Error, {L, N, M, F, A, NO}) of
+ {fault, Fault} ->
+ Fault;
+ Pid ->
+ Pid
+ end
+ end
+ end.
+
remote_spawn_error({'EXIT', {{nodedown,N}, _}}, {L, N, M, F, A, O}) ->
{Opts, LL} = case L =:= link of
true ->
@@ -3034,7 +3145,7 @@ remote_spawn_error({'EXIT', {{nodedown,N}, _}}, {L, N, M, F, A, O}) ->
false ->
{O, []}
end,
- spawn_opt(erlang,crasher,[N,M,F,A,Opts,noconnection], LL);
+ erlang:spawn_opt(erts_internal,crasher,[N,M,F,A,Opts,noconnection], LL);
remote_spawn_error({'EXIT', {Reason, _}}, _) ->
{fault, Reason};
remote_spawn_error({'EXIT', Reason}, _) ->
@@ -3042,21 +3153,177 @@ remote_spawn_error({'EXIT', Reason}, _) ->
remote_spawn_error(Other, _) ->
{fault, Other}.
-is_well_formed_list([]) ->
- true;
-is_well_formed_list([_|Rest]) ->
- is_well_formed_list(Rest);
-is_well_formed_list(_) ->
- false.
-
-crasher(Node,Mod,Fun,Args,[],Reason) ->
- error_logger:warning_msg("** Can not start ~w:~w,~w on ~w **~n",
- [Mod,Fun,Args,Node]),
- erlang:exit(Reason);
-crasher(Node,Mod,Fun,Args,Opts,Reason) ->
- error_logger:warning_msg("** Can not start ~w:~w,~w (~w) on ~w **~n",
- [Mod,Fun,Args,Opts,Node]),
- erlang:exit(Reason).
+%%
+%% spawn_request/1
+%%
+
+-spec spawn_request(Fun) -> ReqId when
+ Fun :: function(),
+ ReqId :: reference().
+
+spawn_request(F) when erlang:is_function(F, 0) ->
+ try
+ erlang:spawn_request(erlang, apply, [F, []], [])
+ catch
+ error:Err ->
+ erlang:error(Err, [F])
+ end;
+spawn_request(F) ->
+ erlang:error(badarg, [F]).
+
+%%
+%% spawn_request/2
+%%
+
+-spec spawn_request(Fun, Options) -> ReqId when
+ Fun :: function(),
+ Option :: {reply_tag, ReplyTag}
+ | {reply, Reply}
+ | spawn_opt_option(),
+ ReplyTag :: term(),
+ Reply :: yes | no | error_only | success_only,
+ Options :: [Option],
+ ReqId :: reference();
+ (Node, Fun) -> ReqId when
+ Node :: node(),
+ Fun :: function(),
+ ReqId :: reference().
+
+spawn_request(F, O) when erlang:is_function(F, 0) ->
+ try
+ erlang:spawn_request(erlang, apply, [F, []], O)
+ catch
+ error:Err ->
+ erlang:error(Err, [F, O])
+ end;
+spawn_request(N, F) when erlang:is_function(F, 0) ->
+ try
+ erlang:spawn_request(N, erlang, apply, [F, []], [])
+ catch
+ error:Err ->
+ erlang:error(Err, [N, F])
+ end;
+spawn_request(A1, A2) ->
+ erlang:error(badarg, [A1, A2]).
+
+%%
+%% spawn_request/3
+%%
+
+-spec spawn_request(Node, Fun, Options) -> ReqId when
+ Node :: node(),
+ Fun :: function(),
+ Options :: [Option],
+ Option :: monitor
+ | link
+ | {reply_tag, ReplyTag}
+ | {reply, Reply}
+ | OtherOption,
+ ReplyTag :: term(),
+ Reply :: yes | no | error_only | success_only,
+ OtherOption :: term(),
+ ReqId :: reference();
+ (Module, Function, Args) ->
+ ReqId when
+ Module :: module(),
+ Function :: atom(),
+ Args :: [term()],
+ ReqId :: reference().
+
+spawn_request(N, F, O) when erlang:is_function(F, 0) ->
+ try
+ erlang:spawn_request(N, erlang, apply, [F, []], O)
+ catch
+ error:Err ->
+ erlang:error(Err, [N, F, O])
+ end;
+spawn_request(M, F, A) ->
+ try
+ erlang:spawn_request(M, F, A, [])
+ catch
+ error:Err ->
+ erlang:error(Err, [M, F, A])
+ end.
+
+%%
+%% spawn_request/4
+%%
+
+-spec spawn_request(Node, Module, Function, Args) ->
+ ReqId when
+ Node :: node(),
+ Module :: module(),
+ Function :: atom(),
+ Args :: [term()],
+ ReqId :: reference();
+ (Module, Function, Args, Options) ->
+ ReqId when
+ Module :: module(),
+ Function :: atom(),
+ Args :: [term()],
+ Option :: {reply_tag, ReplyTag}
+ | {reply, Reply}
+ | spawn_opt_option(),
+ ReplyTag :: term(),
+ Reply :: yes | no | error_only | success_only,
+ Options :: [Option],
+ ReqId :: reference().
+
+spawn_request(N, M, F, A) when erlang:is_atom(F) ->
+ try
+ erlang:spawn_request(N, M, F, A, [])
+ catch
+ error:Err ->
+ erlang:error(Err, [N, M, F, A])
+ end;
+spawn_request(M, F, A, O) ->
+ case erts_internal:spawn_request(M, F, A, O) of
+ Ref when erlang:is_reference(Ref) ->
+ Ref;
+ badarg ->
+ erlang:error(badarg, [M, F, A, O])
+ end.
+
+%%
+%% spawn_request/5
+%%
+
+-spec spawn_request(Node, Module, Function, Args, Options) ->
+ ReqId when
+ Node :: node(),
+ Module :: module(),
+ Function :: atom(),
+ Args :: [term()],
+ Options :: [Option],
+ Option :: monitor
+ | link
+ | {reply_tag, ReplyTag}
+ | {reply, Reply}
+ | OtherOption,
+ ReplyTag :: term(),
+ Reply :: yes | no | error_only | success_only,
+ OtherOption :: term(),
+ ReqId :: reference().
+
+spawn_request(N, M, F, A, O) when N =:= erlang:node() ->
+ try
+ erlang:spawn_request(M, F, A, O)
+ catch
+ error:Err ->
+ erlang:error(Err, [N, M, F, A, O])
+ end;
+spawn_request(N, M, F, A, O) ->
+ case erts_internal:dist_spawn_request(N, {M, F, A}, O, spawn_request) of
+ Ref when erlang:is_reference(Ref) ->
+ Ref;
+ badarg ->
+ erlang:error(badarg, [N, M, F, A, O])
+ end.
+
+-spec spawn_request_abandon(ReqId :: reference()) -> boolean().
+
+spawn_request_abandon(_ReqId) ->
+ erlang:nif_error(undefined).
-spec erlang:yield() -> 'true'.
yield() ->
@@ -3337,7 +3604,7 @@ dist_ctrl_put_data(_DHandle, _Data) ->
-spec erlang:dist_ctrl_get_data(DHandle) -> {Size, Data} | Data | 'none' when
Size :: non_neg_integer(),
DHandle :: dist_handle(),
- Data :: iodata().
+ Data :: iovec().
dist_ctrl_get_data(_DHandle) ->
erlang:nif_error(undefined).
@@ -3671,15 +3938,6 @@ get_memval(code, #memory{code = V}) -> V;
get_memval(ets, #memory{ets = V}) -> V;
get_memval(_, #memory{}) -> erlang:error(badarg).
-get_blocks_size([{blocks_size, Sz, _, _} | Rest], Acc) ->
- get_blocks_size(Rest, Acc+Sz);
-get_blocks_size([{blocks_size, Sz} | Rest], Acc) ->
- get_blocks_size(Rest, Acc+Sz);
-get_blocks_size([_ | Rest], Acc) ->
- get_blocks_size(Rest, Acc);
-get_blocks_size([], Acc) ->
- Acc.
-
get_fix_proc([{ProcType, A1, U1}| Rest], {A0, U0}) when ProcType == proc;
ProcType == monitor;
ProcType == link;
@@ -3722,14 +3980,15 @@ au_mem_acc(#memory{ total = Tot,
processes = Proc,
processes_used = ProcU } = Mem,
eheap_alloc, Data) ->
- Sz = get_blocks_size(Data, 0),
+ Sz = acc_blocks_size(Data, 0),
Mem#memory{ total = Tot+Sz,
processes = Proc+Sz,
processes_used = ProcU+Sz};
au_mem_acc(#memory{ total = Tot,
system = Sys,
- ets = Ets } = Mem, ets_alloc, Data) ->
- Sz = get_blocks_size(Data, 0),
+ ets = Ets } = Mem,
+ ets_alloc, Data) ->
+ Sz = acc_blocks_size(Data, 0),
Mem#memory{ total = Tot+Sz,
system = Sys+Sz,
ets = Ets+Sz };
@@ -3737,31 +3996,45 @@ au_mem_acc(#memory{total = Tot,
system = Sys,
binary = Bin } = Mem,
binary_alloc, Data) ->
- Sz = get_blocks_size(Data, 0),
+ Sz = acc_blocks_size(Data, 0),
Mem#memory{ total = Tot+Sz,
system = Sys+Sz,
binary = Bin+Sz};
au_mem_acc(#memory{ total = Tot,
system = Sys } = Mem,
_Type, Data) ->
- Sz = get_blocks_size(Data, 0),
+ Sz = acc_blocks_size(Data, 0),
Mem#memory{ total = Tot+Sz,
system = Sys+Sz }.
-au_mem_foreign(Mem, [{Type, SizeList} | Rest]) ->
- au_mem_foreign(au_mem_acc(Mem, Type, SizeList), Rest);
-au_mem_foreign(Mem, []) ->
+acc_blocks_size([{size, Sz, _, _} | Rest], Acc) ->
+ acc_blocks_size(Rest, Acc+Sz);
+acc_blocks_size([{size, Sz} | Rest], Acc) ->
+ acc_blocks_size(Rest, Acc+Sz);
+acc_blocks_size([_ | Rest], Acc) ->
+ acc_blocks_size(Rest, Acc);
+acc_blocks_size([], Acc) ->
+ Acc.
+
+au_mem_blocks([{blocks, L} | Rest], Mem0) ->
+ Mem = au_mem_blocks_1(L, Mem0),
+ au_mem_blocks(Rest, Mem);
+au_mem_blocks([_ | Rest], Mem) ->
+ au_mem_blocks(Rest, Mem);
+au_mem_blocks([], Mem) ->
+ Mem.
+
+au_mem_blocks_1([{Type, SizeList} | Rest], Mem) ->
+ au_mem_blocks_1(Rest, au_mem_acc(Mem, Type, SizeList));
+au_mem_blocks_1([], Mem) ->
Mem.
-au_mem_current(Mem0, Type, [{mbcs_pool, MBCS} | Rest]) ->
- [Foreign] = [Foreign || {foreign_blocks, Foreign} <- MBCS],
- SizeList = MBCS -- [Foreign],
- Mem = au_mem_foreign(Mem0, Foreign),
- au_mem_current(au_mem_acc(Mem, Type, SizeList), Type, Rest);
-au_mem_current(Mem, Type, [{mbcs, SizeList} | Rest]) ->
- au_mem_current(au_mem_acc(Mem, Type, SizeList), Type, Rest);
-au_mem_current(Mem, Type, [{sbcs, SizeList} | Rest]) ->
- au_mem_current(au_mem_acc(Mem, Type, SizeList), Type, Rest);
+au_mem_current(Mem, Type, [{mbcs_pool, Stats} | Rest]) ->
+ au_mem_current(au_mem_blocks(Stats, Mem), Type, Rest);
+au_mem_current(Mem, Type, [{mbcs, Stats} | Rest]) ->
+ au_mem_current(au_mem_blocks(Stats, Mem), Type, Rest);
+au_mem_current(Mem, Type, [{sbcs, Stats} | Rest]) ->
+ au_mem_current(au_mem_blocks(Stats, Mem), Type, Rest);
au_mem_current(Mem, Type, [_ | Rest]) ->
au_mem_current(Mem, Type, Rest);
au_mem_current(Mem, _Type, []) ->
@@ -3917,3 +4190,98 @@ gc_info(Ref, N, {OrigColls,OrigRecl}) ->
{Ref, {_,Colls, Recl}} ->
gc_info(Ref, N-1, {Colls+OrigColls,Recl+OrigRecl})
end.
+
+%% Operators
+
+-spec erlang:'=='(term(), term()) -> boolean().
+'=='(_A, _B) ->
+ erlang:nif_error(undefined).
+-spec erlang:'=:='(term(), term()) -> boolean().
+'=:='(_A, _B) ->
+ erlang:nif_error(undefined).
+-spec erlang:'/='(term(), term()) -> boolean().
+'/='(_A, _B) ->
+ erlang:nif_error(undefined).
+-spec erlang:'=/='(term(), term()) -> boolean().
+'=/='(_A, _B) ->
+ erlang:nif_error(undefined).
+-spec erlang:'=<'(term(), term()) -> boolean().
+'=<'(_A, _B) ->
+ erlang:nif_error(undefined).
+-spec erlang:'>='(term(), term()) -> boolean().
+'>='(_A, _B) ->
+ erlang:nif_error(undefined).
+-spec erlang:'<'(term(), term()) -> boolean().
+'<'(_A, _B) ->
+ erlang:nif_error(undefined).
+-spec erlang:'>'(term(), term()) -> boolean().
+'>'(_A, _B) ->
+ erlang:nif_error(undefined).
+
+-spec erlang:'-'(number()) -> number().
+'-'(_A) ->
+ erlang:nif_error(undefined).
+-spec erlang:'+'(number()) -> number().
+'+'(_A) ->
+ erlang:nif_error(undefined).
+-spec erlang:'-'(number(), number()) -> number().
+'-'(_A, _B) ->
+ erlang:nif_error(undefined).
+-spec erlang:'+'(number(), number()) -> number().
+'+'(_A, _B) ->
+ erlang:nif_error(undefined).
+-spec erlang:'/'(number(), number()) -> float().
+'/'(_A, _B) ->
+ erlang:nif_error(undefined).
+-spec erlang:'*'(number(), number()) -> number().
+'*'(_A, _B) ->
+ erlang:nif_error(undefined).
+-spec erlang:'div'(integer(), integer()) -> integer().
+'div'(_A, _B) ->
+ erlang:nif_error(undefined).
+-spec erlang:'rem'(integer(), integer()) -> integer().
+'rem'(_A, _B) ->
+ erlang:nif_error(undefined).
+-spec erlang:'bsl'(integer(), integer()) -> integer().
+'bsl'(_A, _B) ->
+ erlang:nif_error(undefined).
+-spec erlang:'bsr'(integer(), integer()) -> integer().
+'bsr'(_A, _B) ->
+ erlang:nif_error(undefined).
+-spec erlang:'bor'(integer(), integer()) -> integer().
+'bor'(_A, _B) ->
+ erlang:nif_error(undefined).
+-spec erlang:'band'(integer(), integer()) -> integer().
+'band'(_A, _B) ->
+ erlang:nif_error(undefined).
+-spec erlang:'bxor'(integer(), integer()) -> integer().
+'bxor'(_A, _B) ->
+ erlang:nif_error(undefined).
+-spec erlang:'bnot'(integer()) -> integer().
+'bnot'(_A) ->
+ erlang:nif_error(undefined).
+
+-spec erlang:'--'(list(), list()) -> list().
+'--'(_A, _B) ->
+ erlang:nif_error(undefined).
+-spec erlang:'++'(list(), term()) -> term().
+'++'(_A, _B) ->
+ erlang:nif_error(undefined).
+
+-spec erlang:'and'(boolean(), boolean()) -> boolean().
+'and'(_A, _B) ->
+ erlang:nif_error(undefined).
+-spec erlang:'or'(boolean(), boolean()) -> boolean().
+'or'(_A, _B) ->
+ erlang:nif_error(undefined).
+
+-spec erlang:'xor'(boolean(), boolean()) -> boolean().
+'xor'(_A, _B) ->
+ erlang:nif_error(undefined).
+-spec erlang:'not'(boolean()) -> boolean().
+'not'(_A) ->
+ erlang:nif_error(undefined).
+
+-spec erlang:'!'(dst(), term()) -> term().
+'!'(_Dst, _Msg) ->
+ erlang:nif_error(undefined).
diff --git a/erts/preloaded/src/erts.app.src b/erts/preloaded/src/erts.app.src
index d3dbe0f2d1..f8b2338c44 100644
--- a/erts/preloaded/src/erts.app.src
+++ b/erts/preloaded/src/erts.app.src
@@ -42,7 +42,7 @@
{registered, []},
{applications, []},
{env, []},
- {runtime_dependencies, ["stdlib-3.5", "kernel-6.5.1", "sasl-3.3"]}
+ {runtime_dependencies, ["stdlib-@OTP-15251@", "kernel-@OTP-15251@", "sasl-3.3"]}
]}.
%% vim: ft=erlang
diff --git a/erts/preloaded/src/erts_internal.erl b/erts/preloaded/src/erts_internal.erl
index 0dd65d3e27..6c23b47895 100644
--- a/erts/preloaded/src/erts_internal.erl
+++ b/erts/preloaded/src/erts_internal.erl
@@ -67,6 +67,7 @@
-export([dist_ctrl_put_data/2]).
-export([get_dflags/0]).
+-export([get_creation/0]).
-export([new_connection/1]).
-export([abort_pending_connection/2]).
@@ -89,7 +90,7 @@
-export([process_flag/3]).
--export([create_dist_channel/4]).
+-export([create_dist_channel/3]).
-export([erase_persistent_terms/0]).
@@ -105,6 +106,10 @@
-export([get_internal_state_blocked/1]).
+-export([spawn_request/4, spawn_init/1, dist_spawn_request/4, dist_spawn_init/1]).
+
+-export([crasher/6]).
+
%%
%% Await result of send to port
%%
@@ -564,6 +569,10 @@ dist_ctrl_put_data(DHandle, IoList) ->
get_dflags() ->
erlang:nif_error(undefined).
+-spec erts_internal:get_creation() -> pos_integer().
+get_creation() ->
+ erlang:nif_error(undefined).
+
-spec erts_internal:new_connection(Node) -> ConnId when
Node :: atom(),
ConnId :: {integer(), erlang:dist_handle()}.
@@ -703,17 +712,18 @@ process_display(_Pid, _Type) ->
process_flag(_Pid, _Flag, _Value) ->
erlang:nif_error(undefined).
--spec create_dist_channel(Node, DistCtrlr, Flags, Ver) -> Result when
+-spec create_dist_channel(Node, DistCtrlr, {Flags, Ver, Cr}) -> Result when
Node :: atom(),
DistCtrlr :: port() | pid(),
Flags :: integer(),
Ver :: integer(),
+ Cr :: pos_integer(),
Result :: {'ok', erlang:dist_handle()}
| {'message', reference()}
| 'badarg'
| 'system_limit'.
-create_dist_channel(_Node, _DistCtrlr, _Flags, _Ver) ->
+create_dist_channel(_Node, _DistCtrlr, _Tpl) ->
erlang:nif_error(undefined).
-spec erase_persistent_terms() -> 'ok'.
@@ -830,3 +840,85 @@ get_internal_state_blocked(Arg) ->
erlang:system_flag(multi_scheduling, unblock)
end,
Result.
+
+-spec spawn_request(Module, Function, Args, Opts) -> Res when
+ Module :: module(),
+ Function :: atom(),
+ Args :: [term()],
+ Opts :: [term()],
+ Res :: reference() | 'badarg'.
+
+spawn_request(_Module, _Function, _Args, _Opts) ->
+ erlang:nif_error(undef).
+
+-spec spawn_init({Module, Function, Args}) -> Res when
+ Module :: module(),
+ Function :: atom(),
+ Args :: [term()],
+ Res :: term().
+
+spawn_init({M, F, A}) ->
+ apply(M, F, A).
+
+-spec dist_spawn_request(Node, MFA, Opts, spawn_request) -> Res when
+ Node :: node(),
+ MFA :: {Module, Function, Args},
+ Module :: module(),
+ Function :: atom(),
+ Args :: [term()],
+ Opts :: [term()],
+ Res :: reference() | 'badarg';
+ (Node, MFA, Opts, spawn_opt) -> Res when
+ Node :: node(),
+ MFA :: {Module, Function, Args},
+ Module :: module(),
+ Function :: atom(),
+ Args :: [term()],
+ Opts :: [term()],
+ Res :: {reference(), boolean()} | 'badarg'.
+
+dist_spawn_request(_Node, _MFA, _Opts, _Type) ->
+ erlang:nif_error(undef).
+
+-spec dist_spawn_init(MFA) -> Res when
+ MFA :: {Module, Function, non_neg_integer()},
+ Module :: module(),
+ Function :: atom(),
+ Res :: term().
+
+dist_spawn_init(MFA) ->
+ %%
+ %% The argument list is passed as a message
+ %% to the newly created process. This since
+ %% it might be large and require a substantial
+ %% amount of work to decode. This way we put
+ %% this work on the newly created process
+ %% (which can execute in parallel with all
+ %% other tasks) instead of on the distribution
+ %% channel code which is a bottleneck in the
+ %% system.
+ %%
+ %% erl_create_process() ensures that the
+ %% argument list to use in apply is
+ %% guaranteed to be the first message in the
+ %% message queue.
+ %%
+ {M, F, _NoA} = MFA,
+ receive
+ A ->
+ erlang:apply(M, F, A)
+ end.
+
+%%
+%% Failed distributed spawn(), spawn_link(), spawn_monitor(), spawn_opt()
+%% spawns a dummy process executing the crasher/6 function...
+%%
+
+crasher(Node,Mod,Fun,Args,[],Reason) ->
+ error_logger:warning_msg("** Can not start ~w:~w,~w on ~w **~n",
+ [Mod,Fun,Args,Node]),
+ erlang:exit(Reason);
+crasher(Node,Mod,Fun,Args,Opts,Reason) ->
+ error_logger:warning_msg("** Can not start ~w:~w,~w (~w) on ~w **~n",
+ [Mod,Fun,Args,Opts,Node]),
+ erlang:exit(Reason).
diff --git a/erts/preloaded/src/init.erl b/erts/preloaded/src/init.erl
index 5ea67347ec..76077880b6 100644
--- a/erts/preloaded/src/init.erl
+++ b/erts/preloaded/src/init.erl
@@ -48,7 +48,7 @@
-module(init).
--export([restart/0,reboot/0,stop/0,stop/1,
+-export([restart/1,restart/0,reboot/0,stop/0,stop/1,
get_status/0,boot/1,get_arguments/0,get_plain_arguments/0,
get_argument/1,script_id/0]).
@@ -63,6 +63,7 @@
-include_lib("kernel/include/file.hrl").
-type internal_status() :: 'starting' | 'started' | 'stopping'.
+-type mode() :: 'embedded' | 'interactive'.
-record(state, {flags = [],
args = [],
@@ -164,7 +165,15 @@ request(Req) ->
end.
-spec restart() -> 'ok'.
-restart() -> init ! {stop,restart}, ok.
+restart() -> restart([]).
+
+-spec restart([{mode, mode()}]) -> 'ok'.
+restart([]) ->
+ init ! {stop,restart}, ok;
+restart([{mode, Mode}]) when Mode =:= embedded; Mode =:= interactive ->
+ init ! {stop,{restart,Mode}}, ok;
+restart(Opts) when is_list(Opts) ->
+ erlang:error(badarg, [Opts]).
-spec reboot() -> 'ok'.
reboot() -> init ! {stop,reboot}, ok.
@@ -546,11 +555,11 @@ stop(Reason,State) ->
clear_system(BootPid,State1),
do_stop(Reason,State1).
-do_stop(restart,#state{start = Start, flags = Flags, args = Args}) ->
- %% Make sure we don't have any outstanding messages before doing the restart.
- flush(),
- erts_internal:erase_persistent_terms(),
- boot(Start,Flags,Args);
+do_stop({restart,Mode},#state{start=Start, flags=Flags0, args=Args}) ->
+ Flags = update_flag(mode, Flags0, atom_to_binary(Mode)),
+ do_restart(Start,Flags,Args);
+do_stop(restart,#state{start=Start, flags=Flags, args=Args}) ->
+ do_restart(Start,Flags,Args);
do_stop(reboot,_) ->
halt();
do_stop(stop,State) ->
@@ -560,6 +569,11 @@ do_stop({stop,Status},State) ->
stop_heart(State),
halt(Status).
+do_restart(Start,Flags,Args) ->
+ flush(),
+ erts_internal:erase_persistent_terms(),
+ boot(Start,Flags,Args).
+
clear_system(BootPid,State) ->
Heart = get_heart(State#state.kernel),
Logger = get_logger(State#state.kernel),
@@ -798,7 +812,7 @@ do_boot(Init,Flags,Start) ->
start_prim_loader(Init, bs2ss(Path), PathFls),
BootFile = bootfile(Flags,Root),
BootList = get_boot(BootFile,Root),
- LoadMode = b2a(get_flag(mode, Flags, false)),
+ LoadMode = b2a(get_flag(mode, Flags, interactive)),
Deb = b2a(get_flag(init_debug, Flags, false)),
catch ?ON_LOAD_HANDLER ! {init_debug_flag,Deb},
BootVars = get_boot_vars(Root, Flags),
@@ -1233,6 +1247,13 @@ get_args([B|Bs], As) ->
end;
get_args([], As) -> {reverse(As),[]}.
+update_flag(Flag, [{Flag, _} | Flags], Value) ->
+ [{Flag, [Value]} | Flags];
+update_flag(Flag, [Head | Flags], Value) ->
+ [Head | update_flag(Flag, Flags, Value)];
+update_flag(Flag, [], Value) ->
+ [{Flag, [Value]}].
+
%%
%% Internal get_flag function, with default value.
%% Return: true if flag given without args
diff --git a/erts/preloaded/src/prim_file.erl b/erts/preloaded/src/prim_file.erl
index f241be8569..ae3980022a 100644
--- a/erts/preloaded/src/prim_file.erl
+++ b/erts/preloaded/src/prim_file.erl
@@ -34,6 +34,7 @@
-export([read_link/1, read_link_all/1,
read_link_info/1, read_link_info/2,
read_file_info/1, read_file_info/2,
+ read_handle_info/1, read_handle_info/2,
write_file_info/2, write_file_info/3]).
-export([list_dir/1, list_dir_all/1]).
@@ -497,6 +498,8 @@ get_handle_nif(_FileRef) ->
erlang:nif_error(undef).
delayed_close_nif(_FileRef) ->
erlang:nif_error(undef).
+read_handle_info_nif(_FileRef) ->
+ erlang:nif_error(undef).
%%
%% Quality-of-life helpers
@@ -598,20 +601,37 @@ read_link_info(Name, Opts) ->
read_info_1(Name, FollowLinks, TimeType) ->
try
case read_info_nif(encode_path(Name), FollowLinks) of
- {error, Reason} ->
- {error, Reason};
- FileInfo ->
- CTime = from_posix_seconds(FileInfo#file_info.ctime, TimeType),
- MTime = from_posix_seconds(FileInfo#file_info.mtime, TimeType),
- ATime = from_posix_seconds(FileInfo#file_info.atime, TimeType),
- {ok, FileInfo#file_info{ ctime = CTime,
- mtime = MTime,
- atime = ATime }}
+ {error, Reason} -> {error, Reason};
+ FileInfo -> {ok, adjust_times(FileInfo, TimeType)}
+ end
+ catch
+ error:_ -> {error, badarg}
+ end.
+
+read_handle_info(Fd) ->
+ read_handle_info_1(Fd, local).
+read_handle_info(Fd, Opts) ->
+ read_handle_info_1(Fd, proplist_get_value(time, Opts, local)).
+
+read_handle_info_1(Fd, TimeType) ->
+ try
+ #{ handle := FRef } = get_fd_data(Fd),
+ case read_handle_info_nif(FRef) of
+ {error, Reason} -> {error, Reason};
+ FileInfo -> {ok, adjust_times(FileInfo, TimeType)}
end
catch
error:_ -> {error, badarg}
end.
+adjust_times(FileInfo, TimeType) ->
+ CTime = from_posix_seconds(FileInfo#file_info.ctime, TimeType),
+ MTime = from_posix_seconds(FileInfo#file_info.mtime, TimeType),
+ ATime = from_posix_seconds(FileInfo#file_info.atime, TimeType),
+ FileInfo#file_info{ ctime = CTime,
+ mtime = MTime,
+ atime = ATime }.
+
write_file_info(Filename, Info) ->
write_file_info_1(Filename, Info, local).
write_file_info(Filename, Info, Opts) ->
diff --git a/erts/preloaded/src/socket.erl b/erts/preloaded/src/socket.erl
index eb1bcdce66..ec447d3b3e 100644
--- a/erts/preloaded/src/socket.erl
+++ b/erts/preloaded/src/socket.erl
@@ -39,7 +39,7 @@
]).
-export([
- open/2, open/3, open/4,
+ open/1, open/2, open/3, open/4,
bind/2, bind/3,
connect/2, connect/3,
listen/1, listen/2,
@@ -176,6 +176,7 @@
type := type(),
protocol := protocol(),
ctrl := pid(),
+ ctype := normal | fromfd | {fromfd, integer()},
counters := socket_counters(),
num_readers := non_neg_integer(),
num_writers := non_neg_integer(),
@@ -351,6 +352,9 @@
sockaddr_un() |
sockaddr_ll().
+-define(OPEN2_OPTS_DEFAULTS, #{debug => false, dup => true}).
+-define(OPEN4_OPTS_DEFAULTS, #{debug => false}).
+
-define(SOCKADDR_IN4_DEFAULTS(A), #{port => 0,
addr => A}).
-define(SOCKADDR_IN4_DEFAULTS, ?SOCKADDR_IN4_DEFAULTS(any)).
@@ -947,6 +951,7 @@
-define(ESOCK_SUPPORTS_LOCAL, 16#0004).
-define(ESOCK_SUPPORTS_SEND_FLAGS, 16#0005).
-define(ESOCK_SUPPORTS_RECV_FLAGS, 16#0006).
+-define(ESOCK_SUPPORTS_NETNS, 16#0007).
%% ===========================================================================
@@ -1094,15 +1099,19 @@ info(#socket{ref = SockRef}) ->
{ipv6, boolean()} |
{local, boolean()} |
{send_flags, supports_send_flags()} |
- {recv_flags, supports_recv_flags()}].
+ {recv_flags, supports_recv_flags()} |
+ {netns, boolean()}].
supports() ->
- [{options, supports(options)},
+ [
+ {options, supports(options)},
{sctp, supports(sctp)},
{ipv6, supports(ipv6)},
{local, supports(local)},
{send_flags, supports(send_flags)},
- {recv_flags, supports(recv_flags)}].
+ {recv_flags, supports(recv_flags)},
+ {netns, supports(netns)}
+ ].
-dialyzer({no_contracts, supports/1}).
@@ -1112,7 +1121,8 @@ supports() ->
(local) -> boolean();
(send_flags) -> supports_send_flags();
(recv_flags) -> supports_recv_flags();
- (Key1) -> false when
+ (netns) -> boolean();
+ (Key1) -> false when
Key1 :: term().
supports(options) ->
@@ -1127,6 +1137,8 @@ supports(send_flags) ->
nif_supports(?ESOCK_SUPPORTS_SEND_FLAGS);
supports(recv_flags) ->
nif_supports(?ESOCK_SUPPORTS_RECV_FLAGS);
+supports(netns) ->
+ nif_supports(?ESOCK_SUPPORTS_NETNS);
supports(_Key1) ->
false.
@@ -1187,29 +1199,6 @@ supports(_Key1, _Key2, _Key3) ->
%%
%% <KOLLA>
%%
-%% How do we handle the case when an fd has been created (somehow)
-%% and we shall create a socket "from it".
-%% Can we figure out Domain, Type and Protocol from fd?
-%% No we can't: For instance, its not possible to 'get' domain on FreeBSD.
-%%
-%% Instead, require: open(Domain, Stream, Proto, #{fd => FD}).
-%% The last argument, Extra, is used to provide the fd.
-%%
-%% </KOLLA>
-%%
-%%
-%% <KOLLA>
-%%
-%% Possibly add a "registry" in the nif, allowing the user processes to
-%% "register" themselves.
-%% The point of this would be to ensure that these processes are
-%% informed if the socket "terminates". Could possibly be used for
-%% other things? If gen_tcp implements the active feature using
-%% a reader process, the nif may need to know about this process,
-%% since its probably "hidden" from the socket "owner" (someone
-%% needs to handle it if it dies).
-%% Register under a name?
-%%
%% The nif sets up a monitor to this process, and if it dies the socket
%% is closed. It is also used if someone wants to monitor the socket.
%%
@@ -1230,12 +1219,33 @@ supports(_Key1, _Key2, _Key3) ->
%% Extra: Currently only used for netns
%%
--spec open(Domain, Type) -> {ok, Socket} | {error, Reason} when
+-spec open(FD) -> {ok, Socket} | {error, Reason} when
+ FD :: integer(),
+ Socket :: socket(),
+ Reason :: term().
+
+open(FD) ->
+ open(FD, ?OPEN2_OPTS_DEFAULTS).
+
+-spec open(FD, Opts) -> {ok, Socket} | {error, Reason} when
+ FD :: integer(),
+ Opts :: map(),
+ Socket :: socket(),
+ Reason :: term();
+ (Domain, Type) -> {ok, Socket} | {error, Reason} when
Domain :: domain(),
Type :: type(),
Socket :: socket(),
Reason :: term().
+open(FD, Opts) when is_integer(FD) andalso is_map(Opts) ->
+ case nif_open(FD, ensure_open2_opts(Opts)) of
+ {ok, SockRef} ->
+ Socket = #socket{ref = SockRef},
+ {ok, Socket};
+ {error, _} = ERROR ->
+ ERROR
+ end;
open(Domain, Type) ->
open(Domain, Type, default).
@@ -1247,23 +1257,23 @@ open(Domain, Type) ->
Reason :: term().
open(Domain, Type, Protocol) ->
- open(Domain, Type, Protocol, #{}).
+ open(Domain, Type, Protocol, ?OPEN4_OPTS_DEFAULTS).
--spec open(Domain, Type, Protocol, Extra) -> {ok, Socket} | {error, Reason} when
+-spec open(Domain, Type, Protocol, Opts) -> {ok, Socket} | {error, Reason} when
Domain :: domain(),
Type :: type(),
Protocol :: default | protocol(),
- Extra :: map(),
+ Opts :: map(),
Socket :: socket(),
Reason :: term().
-open(Domain, Type, Protocol, Extra) when is_map(Extra) ->
+open(Domain, Type, Protocol, Opts) when is_map(Opts) ->
try
begin
EDomain = enc_domain(Domain),
EType = enc_type(Type),
EProtocol = enc_protocol(Protocol),
- nif_open(EDomain, EType, EProtocol, Extra)
+ nif_open(EDomain, EType, EProtocol, Opts)
end
of
{ok, SockRef} ->
@@ -3908,6 +3918,25 @@ ensure_sockaddr(SockAddr) ->
invalid_address(SockAddr).
+ensure_open2_opts(M) when is_map(M) ->
+ ensure_open2_opts(maps:to_list(M), ?OPEN2_OPTS_DEFAULTS).
+
+ensure_open2_opts([], Acc) ->
+ Acc;
+ensure_open2_opts([{domain, D}|Opts], Acc) ->
+ ensure_open2_opts(Opts, Acc#{domain => enc_domain(D)});
+ensure_open2_opts([{type, T}|Opts], Acc) ->
+ ensure_open2_opts(Opts, Acc#{type => enc_type(T)});
+ensure_open2_opts([{protocol, P}|Opts], Acc) ->
+ ensure_open2_opts(Opts, Acc#{protocol => enc_protocol(P)});
+ensure_open2_opts([{dup, D}|Opts], Acc) when is_boolean(D) ->
+ ensure_open2_opts(Opts, Acc#{dup => D});
+ensure_open2_opts([{debug, D}|Opts], Acc) when is_boolean(D) ->
+ ensure_open2_opts(Opts, Acc#{debug => D});
+ensure_open2_opts([_|Opts], Acc) ->
+ ensure_open2_opts(Opts, Acc).
+
+
cancel(SockRef, Op, OpRef) ->
case nif_cancel(SockRef, Op, OpRef) of
@@ -4080,7 +4109,10 @@ nif_command(_Command) ->
nif_supports(_Key) ->
erlang:nif_error(undef).
-nif_open(_Domain, _Type, _Protocol, _Extra) ->
+nif_open(_FD, _Opts) ->
+ erlang:nif_error(undef).
+
+nif_open(_Domain, _Type, _Protocol, _Opts) ->
erlang:nif_error(undef).
nif_bind(_SRef, _SockAddr) ->
diff --git a/erts/test/erl_print_SUITE.erl b/erts/test/erl_print_SUITE.erl
index 463d890688..0a5987df88 100644
--- a/erts/test/erl_print_SUITE.erl
+++ b/erts/test/erl_print_SUITE.erl
@@ -324,6 +324,9 @@ run_case(Config, TestArgs, Fun) ->
-define(PORT_EXT, 102).
-define(PID_EXT, 103).
-define(NEW_REFERENCE_EXT, 114).
+-define(NEW_PID_EXT, $X).
+-define(NEW_PORT_EXT, $Y).
+-define(NEWER_REFERENCE_EXT, $Z).
uint32_be(Uint) when is_integer(Uint), 0 =< Uint, Uint < 1 bsl 32 ->
[(Uint bsr 24) band 16#ff,
@@ -351,13 +354,13 @@ mk_pid({NodeName, Creation}, Number, Serial) when is_atom(NodeName) ->
mk_pid({atom_to_list(NodeName), Creation}, Number, Serial);
mk_pid({NodeName, Creation}, Number, Serial) ->
case catch binary_to_term(list_to_binary([?VERSION_MAGIC,
- ?PID_EXT,
+ ?NEW_PID_EXT,
?ATOM_EXT,
uint16_be(length(NodeName)),
NodeName,
uint32_be(Number),
uint32_be(Serial),
- uint8(Creation)])) of
+ uint32_be(Creation)])) of
Pid when is_pid(Pid) ->
Pid;
{'EXIT', {badarg, _}} ->
@@ -370,12 +373,12 @@ mk_port({NodeName, Creation}, Number) when is_atom(NodeName) ->
mk_port({atom_to_list(NodeName), Creation}, Number);
mk_port({NodeName, Creation}, Number) ->
case catch binary_to_term(list_to_binary([?VERSION_MAGIC,
- ?PORT_EXT,
+ ?NEW_PORT_EXT,
?ATOM_EXT,
uint16_be(length(NodeName)),
NodeName,
uint32_be(Number),
- uint8(Creation)])) of
+ uint32_be(Creation)])) of
Port when is_port(Port) ->
Port;
{'EXIT', {badarg, _}} ->
@@ -388,33 +391,16 @@ mk_ref({NodeName, Creation}, Numbers) when is_atom(NodeName),
is_integer(Creation),
is_list(Numbers) ->
mk_ref({atom_to_list(NodeName), Creation}, Numbers);
-mk_ref({NodeName, Creation}, [Number]) when is_list(NodeName),
- is_integer(Creation),
- is_integer(Number) ->
- case catch binary_to_term(list_to_binary([?VERSION_MAGIC,
- ?REFERENCE_EXT,
- ?ATOM_EXT,
- uint16_be(length(NodeName)),
- NodeName,
- uint32_be(Number),
- uint8(Creation)])) of
- Ref when is_reference(Ref) ->
- Ref;
- {'EXIT', {badarg, _}} ->
- exit({badarg, mk_ref, [{NodeName, Creation}, [Number]]});
- Other ->
- exit({unexpected_binary_to_term_result, Other})
- end;
mk_ref({NodeName, Creation}, Numbers) when is_list(NodeName),
is_integer(Creation),
is_list(Numbers) ->
case catch binary_to_term(list_to_binary([?VERSION_MAGIC,
- ?NEW_REFERENCE_EXT,
+ ?NEWER_REFERENCE_EXT,
uint16_be(length(Numbers)),
?ATOM_EXT,
uint16_be(length(NodeName)),
NodeName,
- uint8(Creation),
+ uint32_be(Creation),
lists:map(fun (N) ->
uint32_be(N)
end,
@@ -429,11 +415,10 @@ mk_ref({NodeName, Creation}, Numbers) when is_list(NodeName),
my_cre() -> erlang:system_info(creation).
-oth_cre(0) -> 1;
-oth_cre(1) -> 2;
-oth_cre(2) -> 3;
-oth_cre(3) -> 1;
-oth_cre(N) -> exit({invalid_creation, N}).
+oth_cre(N) when N >= 0, N < (1 bsl 32) ->
+ (N rem ((1 bsl 32) - 1)) + 1;
+oth_cre(N) ->
+ exit({invalid_creation, N}).
str_1_bsl_10000() ->
"19950631168807583848837421626835850838234968318861924548520089498529438830221946631919961684036194597899331129423209124271556491349413781117593785932096323957855730046793794526765246551266059895520550086918193311542508608460618104685509074866089624888090489894838009253941633257850621568309473902556912388065225096643874441046759871626985453222868538161694315775629640762836880760732228535091641476183956381458969463899410840960536267821064621427333394036525565649530603142680234969400335934316651459297773279665775606172582031407994198179607378245683762280037302885487251900834464581454650557929601414833921615734588139257095379769119277800826957735674444123062018757836325502728323789270710373802866393031428133241401624195671690574061419654342324638801248856147305207431992259611796250130992860241708340807605932320161268492288496255841312844061536738951487114256315111089745514203313820202931640957596464756010405845841566072044962867016515061920631004186422275908670900574606417856951911456055068251250406007519842261898059237118054444788072906395242548339221982707404473162376760846613033778706039803413197133493654622700563169937455508241780972810983291314403571877524768509857276937926433221599399876886660808368837838027643282775172273657572744784112294389733810861607423253291974813120197604178281965697475898164531258434135959862784130128185406283476649088690521047580882615823961985770122407044330583075869039319604603404973156583208672105913300903752823415539745394397715257455290510212310947321610753474825740775273986348298498340756937955646638621874569499279016572103701364433135817214311791398222983845847334440270964182851005072927748364550578634501100852987812389473928699540834346158807043959118985815145779177143619698728131459483783202081474982171858011389071228250905826817436220577475921417653715687725614904582904992461028630081535583308130101987675856234343538955409175623400844887526162643568648833519463720377293240094456246923254350400678027273837755376406726898636241037491410966718557050759098100246789880178271925953381282421954028302759408448955014676668389697996886241636313376393903373455801407636741877711055384225739499110186468219696581651485130494222369947714763069155468217682876200362777257723781365331611196811280792669481887201298643660768551639860534602297871557517947385246369446923087894265948217008051120322365496288169035739121368338393591756418733850510970271613915439590991598154654417336311656936031122249937969999226781732358023111862644575299135758175008199839236284615249881088960232244362173771618086357015468484058622329792853875623486556440536962622018963571028812361567512543338303270029097668650568557157505516727518899194129711337690149916181315171544007728650573189557450920330185304847113818315407324053319038462084036421763703911550639789000742853672196280903477974533320468368795868580237952218629120080742819551317948157624448298518461509704888027274721574688131594750409732115080498190455803416826949787141316063210686391511681774304792596709376".
diff --git a/erts/test/erlexec_SUITE.erl b/erts/test/erlexec_SUITE.erl
index 952e6da4dc..f8c9f7ae5a 100644
--- a/erts/test/erlexec_SUITE.erl
+++ b/erts/test/erlexec_SUITE.erl
@@ -129,21 +129,26 @@ args_file(Config) when is_list(Config) ->
" -args_file ~s#acomment~n"
"~n"
"-MiscArg7~n"
+ "-MiscArg8 'val with double space'~n"
+ "-MiscArg9 '~n'~n"
+ "-MiscArg10 \\~n\\\t~n"
"#~n"
"+\\#700~n"
+ "#~s~n"
"-extra +XtraArg6~n",
- [AFN2]),
+ [AFN2,lists:duplicate(1024*1024, $a)]),
write_file(AFN2,
- "-MiscArg3~n"
+ "-MiscArg3 \t\v\f\r\n ~n"
"+\\#300~n"
"-args_file ~s~n"
- "-MiscArg5~n"
- "+\\#500#anothercomment -MiscArg10~n"
+ "-MiscArg5 ' '~n"
+ "+\\#500#anothercomment -MiscArg11~n"
"-args_file ~s~n"
"-args_file ~s~n"
"-args_file ~s~n"
+ "# ~s~n"
"-extra +XtraArg5~n",
- [AFN3, AFN4, AFN5, AFN6]),
+ [AFN3, AFN4, AFN5, AFN6,lists:duplicate(1758, $a)]),
write_file(AFN3,
"# comment again~n"
" -MiscArg4 +\\#400 -extra +XtraArg1"),
@@ -156,33 +161,37 @@ args_file(Config) when is_list(Config) ->
write_file(AFN6, "-extra # +XtraArg10~n"),
CmdLine = "+#100 -MiscArg1 "
++ "-args_file " ++ AFN1
- ++ " +#800 -MiscArg8 -extra +XtraArg7 +XtraArg8",
+ ++ " +#800 -MiscArgCLI \\\t -extra +XtraArg7 +XtraArg8",
{Emu, Misc, Extra} = emu_args(CmdLine),
verify_args(["-#100", "-#200", "-#300", "-#400",
"-#500", "-#600", "-#700", "-#800"], Emu),
verify_args(["-MiscArg1", "-MiscArg2", "-MiscArg3", "-MiscArg4",
- "-MiscArg5", "-MiscArg6", "-MiscArg7", "-MiscArg8"],
+ "-MiscArg5", " ", "-MiscArg6", "-MiscArg7", "-MiscArg8",
+ "val with double space",
+ "-MiscArg9","\n","-MiscArg10","\n\t","-MiscArgCLI"],
Misc),
verify_args(["+XtraArg1", "+XtraArg2", "+XtraArg3", "+XtraArg4",
"+XtraArg5", "+XtraArg6", "+XtraArg7", "+XtraArg8"],
Extra),
- verify_not_args(["-MiscArg10", "-#1000", "+XtraArg10",
+ verify_not_args(["","-MiscArg11", "-#1000", "+XtraArg10",
"-MiscArg1", "-MiscArg2", "-MiscArg3", "-MiscArg4",
"-MiscArg5", "-MiscArg6", "-MiscArg7", "-MiscArg8",
+ "-MiscArg9", "-MiscArg10","-MiscArgCLI",
"+XtraArg1", "+XtraArg2", "+XtraArg3", "+XtraArg4",
"+XtraArg5", "+XtraArg6", "+XtraArg7", "+XtraArg8"],
Emu),
- verify_not_args(["-MiscArg10", "-#1000", "+XtraArg10",
+ verify_not_args(["","-MiscArg11", "-#1000", "+XtraArg10",
"-#100", "-#200", "-#300", "-#400",
"-#500", "-#600", "-#700", "-#800",
"+XtraArg1", "+XtraArg2", "+XtraArg3", "+XtraArg4",
"+XtraArg5", "+XtraArg6", "+XtraArg7", "+XtraArg8"],
Misc),
- verify_not_args(["-MiscArg10", "-#1000", "+XtraArg10",
+ verify_not_args(["","-MiscArg11", "-#1000", "+XtraArg10",
"-#100", "-#200", "-#300", "-#400",
"-#500", "-#600", "-#700", "-#800",
"-MiscArg1", "-MiscArg2", "-MiscArg3", "-MiscArg4",
- "-MiscArg5", "-MiscArg6", "-MiscArg7", "-MiscArg8"],
+ "-MiscArg5", "-MiscArg6", "-MiscArg7", "-MiscArg8",
+ "-MiscArg9","-MiscArg10","-MiscArgCLI"],
Extra),
ok.
@@ -220,8 +229,7 @@ evil_args_file(Config) when is_list(Config) ->
ANums),
Misc),
ok.
-
-
+
env(Config) when is_list(Config) ->
os:putenv("ERL_AFLAGS", "-MiscArg1 +#100 -extra +XtraArg1 +XtraArg2"),
@@ -414,9 +422,9 @@ verify_not_args(Xs, Ys) ->
emu_args(CmdLineArgs) ->
io:format("CmdLineArgs = ~ts~n", [CmdLineArgs]),
{ok,[[Erl]]} = init:get_argument(progname),
- EmuCL = os:cmd(Erl ++ " -emu_args_exit " ++ CmdLineArgs),
- io:format("EmuCL = ~ts", [EmuCL]),
- split_emu_clt(string:lexemes(EmuCL, [$ ,$\t,$\n,[$\r,$\n]])).
+ EmuCL = os:cmd(Erl ++ " -emu_qouted_cmd_exit " ++ CmdLineArgs),
+ ct:pal("EmuCL = ~ts", [EmuCL]),
+ split_emu_clt(string:split(string:trim(EmuCL,both,"\n \""), "\" \"", all)).
split_emu_clt(EmuCLT) ->
split_emu_clt(EmuCLT, [], [], [], emu).
diff --git a/erts/test/erlexec_SUITE_data/erlexec_tests.c b/erts/test/erlexec_SUITE_data/erlexec_tests.c
index bd28d2900c..057d674a8f 100644
--- a/erts/test/erlexec_SUITE_data/erlexec_tests.c
+++ b/erts/test/erlexec_SUITE_data/erlexec_tests.c
@@ -23,7 +23,7 @@
* Author: Sverker Eriksson
*/
-#if defined (__WIN32__) || defined(VXWORKS)
+#if defined (__WIN32__)
int main() {return 0;}
#else /* UNIX only */
diff --git a/erts/test/nt_SUITE_data/nt_info.c b/erts/test/nt_SUITE_data/nt_info.c
index 8ef52cad2d..87917a1559 100644
--- a/erts/test/nt_SUITE_data/nt_info.c
+++ b/erts/test/nt_SUITE_data/nt_info.c
@@ -27,12 +27,7 @@
#include <stdlib.h>
#include <string.h>
-#if defined(VXWORKS)
-int nt_info(int argc, char **argv){
- printf("Hello Älvsjö!\n");
- return 0;
-}
-#elif !defined(__WIN32__)
+#if !defined(__WIN32__)
int main(int argc, char **argv){
printf("Hello Älvsjö!\n");
return 0;
diff --git a/erts/test/otp_SUITE.erl b/erts/test/otp_SUITE.erl
index 2372e8b9ac..3f4f3f9574 100644
--- a/erts/test/otp_SUITE.erl
+++ b/erts/test/otp_SUITE.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.
@@ -57,8 +57,14 @@ init_per_suite(Config) ->
{error,bad_name} ->
Erts = filename:join([code:root_dir(),"erts","preloaded","ebin"]),
{ok,_} = xref:add_directory(Server, Erts, []);
- _ ->
- ok
+ LibDir ->
+ case file:read_file_info(filename:join([LibDir,"ebin"])) of
+ {error,enoent} ->
+ Erts = filename:join([LibDir, "preloaded","ebin"]),
+ {ok,_} = xref:add_directory(Server, Erts, []);
+ _ ->
+ ok
+ end
end,
[{xref_server,Server}|Config].
@@ -79,8 +85,7 @@ undefined_functions(Config) when is_list(Config) ->
[UndefS,ExcludeFrom]),
{ok,Undef0} = xref:q(Server, lists:flatten(Q)),
Undef1 = hipe_filter(Undef0),
- Undef2 = ssl_crypto_filter(Undef1),
- Undef3 = edoc_filter(Undef2),
+ Undef3 = ssl_crypto_filter(Undef1),
Undef4 = eunit_filter(Undef3),
Undef5 = dialyzer_filter(Undef4),
Undef6 = wx_filter(Undef5),
@@ -92,9 +97,9 @@ undefined_functions(Config) when is_list(Config) ->
_ ->
Fd = open_log(Config, "undefined_functions"),
foreach(fun ({MFA1,MFA2}) ->
- io:format("~s calls undefined ~s",
- [format_mfa(Server, MFA1),
- format_mfa(MFA2)]),
+ ct:pal("~s calls undefined ~s",
+ [format_mfa(Server, MFA1),
+ format_mfa(MFA2)]),
io:format(Fd, "~s ~s\n",
[format_mfa(Server, MFA1),
format_mfa(MFA2)])
@@ -157,12 +162,6 @@ ssl_crypto_filter(Undef) ->
{_,_} -> Undef
end.
-edoc_filter(Undef) ->
- %% Filter away function call that is catched.
- filter(fun({{edoc_lib,uri_get_http,1},{http,request_sync,2}}) -> false;
- (_) -> true
- end, Undef).
-
eunit_filter(Undef) ->
filter(fun({{eunit_test,wrapper_test_exported_,0},
{eunit_test,nonexisting_function,0}}) -> false;
diff --git a/erts/test/upgrade_SUITE.erl b/erts/test/upgrade_SUITE.erl
index f92c25bdb4..f4c8aae810 100644
--- a/erts/test/upgrade_SUITE.erl
+++ b/erts/test/upgrade_SUITE.erl
@@ -20,6 +20,8 @@
-compile(export_all).
+-compile(r21).
+
-include_lib("common_test/include/ct.hrl").
-include_lib("kernel/include/file.hrl").
@@ -80,7 +82,7 @@ end_per_testcase(_Case,Config) ->
ok.
all() ->
- [minor,major].
+ [minor,major,ancient_major].
%% If this is major release X, then this test performs an upgrade from
%% major release X-1 to the current release.
@@ -89,6 +91,13 @@ major(Config) ->
PreviousMajor = previous_major(Current),
upgrade_test(PreviousMajor,Current,Config).
+%% If this is major release X, then this test performs an upgrade from
+%% major release X-2 to the current release.
+ancient_major(Config) ->
+ Current = erlang:system_info(otp_release),
+ PreviousPreviousMajor = previous_major(previous_major(Current)),
+ upgrade_test(PreviousPreviousMajor,Current,Config).
+
%% If this is a patched version of major release X, then this test
%% performs an upgrade from major release X to the current release.
minor(Config) ->
diff --git a/erts/test/z_SUITE.erl b/erts/test/z_SUITE.erl
index 536212af2e..38b43d50f0 100644
--- a/erts/test/z_SUITE.erl
+++ b/erts/test/z_SUITE.erl
@@ -48,7 +48,7 @@ all() ->
core_files(Config) when is_list(Config) ->
case os:type() of
{win32, _} ->
- {skipped, "No idea searching for core-files on windows"};
+ win32_search(true, os:getenv("OTP_DAILY_BUILD_TOP_DIR"));
{unix, darwin} ->
core_file_search(
core_search_conf(true,
@@ -63,7 +63,7 @@ core_files(Config) when is_list(Config) ->
search_for_core_files(Dir) ->
case os:type() of
{win32, _} ->
- io:format("No idea searching for core-files on windows");
+ win32_search(false, Dir);
{unix, darwin} ->
core_file_search(core_search_conf(false, Dir, "/cores"));
_ ->
@@ -103,18 +103,7 @@ core_search_conf(RunByTS, DBTop) ->
core_search_conf(RunByTS, DBTop, false).
core_search_conf(RunByTS, DBTop, XDir) ->
- SearchDir = case is_dir(DBTop) of
- false ->
- case code:which(test_server) of
- non_existing ->
- {ok, CWD} = file:get_cwd(),
- CWD;
- TS ->
- filename:dirname(filename:dirname(TS))
- end;
- true ->
- DBTop
- end,
+ SearchDir = search_dir(DBTop),
XSearchDir = case is_dir(XDir) of
false ->
false;
@@ -130,6 +119,20 @@ core_search_conf(RunByTS, DBTop, XDir) ->
file = os:find_executable("file"),
run_by_ts = RunByTS}.
+search_dir(DBTop) ->
+ case is_dir(DBTop) of
+ false ->
+ case code:which(test_server) of
+ non_existing ->
+ {ok, CWD} = file:get_cwd(),
+ CWD;
+ TS ->
+ filename:dirname(filename:dirname(TS))
+ end;
+ true ->
+ DBTop
+ end.
+
file_inspect(#core_search_conf{file = File}, Core) ->
FRes0 = os:cmd(File ++ " " ++ Core),
FRes = case string:split(FRes0, Core) of
@@ -186,15 +189,14 @@ dump_core(#core_search_conf{ cerl = false }, _) ->
dump_core(_, {ignore, _Core}) ->
ok;
dump_core(#core_search_conf{ cerl = Cerl }, Core) ->
- Dump = case test_server:is_debug() of
- true ->
- os:cmd(Cerl ++ " -debug -dump " ++ Core);
- _ ->
- os:cmd(Cerl ++ " -dump " ++ Core)
- end,
+ Dump = case erlang:system_info(build_type) of
+ opt ->
+ os:cmd(Cerl ++ " -dump " ++ Core);
+ Type ->
+ os:cmd(lists:concat([Cerl," -",Type," -dump ",Core]))
+ end,
ct:log("~ts~n~n~ts",[Core,Dump]).
-
format_core(Conf, {ignore, Core}) ->
format_core(Conf, Core, "[ignored] ");
format_core(Conf, Core) ->
@@ -230,17 +232,24 @@ core_file_search(#core_search_conf{search_dir = Base,
extra_search_dir = XBase,
cerl = Cerl,
run_by_ts = RunByTS} = Conf) ->
- case {Cerl,test_server:is_debug()} of
+ case {Cerl,erlang:system_info(build_type)} of
{false,_} -> ok;
- {_,true} ->
+ {_,opt} ->
catch io:format("A cerl script that probably can be used for "
- "inspection of emulator cores:~n ~s -debug~n",
+ "inspection of emulator cores:~n ~s~n",
[Cerl]);
- _ ->
+ {_,Type} ->
catch io:format("A cerl script that probably can be used for "
- "inspection of emulator cores:~n ~s~n",
- [Cerl])
+ "inspection of emulator cores:~n ~s -emu_type ~p~n",
+ [Cerl,Type])
end,
+
+ case os:getenv("DOCKER_BUILD_INFO") of
+ false -> ok;
+ Info ->
+ io:format(Info)
+ end,
+
io:format("Searching for core-files in: ~s~s~n",
[case XBase of
false -> "";
@@ -321,3 +330,39 @@ core_file_search(#core_search_conf{search_dir = Base,
_ -> Res
end
end.
+
+win32_search(RunByTS, DBTop) ->
+ case os:getenv("WSLENV") of
+ false when RunByTS ->
+ {skipped, "No idea searching for core-files on old windows"};
+ false ->
+ io:format("No idea searching for core-files on old windows");
+ _ ->
+ win32_search_2(RunByTS, DBTop)
+ end.
+
+win32_search_2(true, DBTop0) ->
+ DBTop = search_dir(DBTop0),
+ Dir = "c:/ldisk/daily_build",
+ io:format("Find and move 'dmp' files in: ~s to ~s~n",[Dir, DBTop]),
+ case filelib:wildcard("*.dmp", Dir) of
+ [] -> ok;
+ Dumps ->
+ %% We move the "daily" dmp files to this test-run
+ Str = lists:flatten(["Core-files found:", lists:join($\s, lists:reverse(Dumps))]),
+ Rename = fun(File) ->
+ FP = filename:join(Dir, File),
+ _ = file:rename(FP, filename:join(DBTop, File))
+ end,
+ [Rename(File) || File <- Dumps],
+ ct:fail(Str)
+ end;
+win32_search_2(false, _DBTop0) ->
+ DBTop = search_dir("c:/ldisk/daily_build"),
+ io:format("Search for 'dmp' files in: ~s~n",[DBTop]),
+ case filelib:wildcard("*.dmp", DBTop) of
+ [] -> "Core-files found: Ignored core-files found:";
+ Dumps ->
+ io:format("The dmp files must be removed manually\n", []),
+ lists:flatten(["Core-files found:", lists:join($\s, lists:reverse(Dumps))])
+ end.